Uthernet II: add partial support for IPRAW.

Signed-off-by: Andrea Odetti <mariofutire@gmail.com>
This commit is contained in:
Andrea Odetti 2022-03-11 16:19:33 +00:00
parent 0e24e443f7
commit 69036f6601
5 changed files with 340 additions and 33 deletions

View file

@ -35,6 +35,7 @@ set(SOURCE_FILES
Tfe/tfesupp.cpp
Tfe/NetworkBackend.cpp
Tfe/PCapBackend.cpp
Tfe/IPRaw.cpp
Debugger/Debug.cpp
Debugger/Debugger_Help.cpp
@ -124,6 +125,7 @@ set(HEADER_FILES
Tfe/tfesupp.h
Tfe/NetworkBackend.h
Tfe/PCapBackend.h
Tfe/IPRaw.h
Uthernet1.h
Uthernet2.h

138
source/Tfe/IPRaw.cpp Normal file
View file

@ -0,0 +1,138 @@
#include "IPRaw.h"
#include <cstring>
#include <arpa/inet.h>
#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<const uint16_t *>(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<const uint8_t *>(ptr);
return sum;
}
uint16_t checksum(const void *addr, int count, int start_sum)
{
/* Compute Internet Checksum for "count" bytes
* beginning at location "addr".
* Taken from https://tools.ietf.org/html/rfc1071
*/
uint32_t sum = start_sum;
sum += sum_every_16bits(addr, count);
/* Fold 32-bit sum to 16 bits */
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
return ~sum;
}
}
std::vector<uint8_t> createETH2Frame(const std::vector<uint8_t> &data,
const uint8_t *sourceMac, const uint8_t *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<uint8_t> frame(total);
ETH2Frame *eth2frame = reinterpret_cast<ETH2Frame *>(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<IP4Header *>(frame.data() + sizeof(ETH2Frame));
ip4header->version = IPV4;
ip4header->ihl = 0x05;
ip4header->tos = tos;
ip4header->len = htons(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), 0);
memcpy(frame.data() + sizeof(ETH2Frame) + sizeof(IP4Header), data.data(), data.size());
return frame;
}
size_t getIPMinimumSize()
{
const size_t minimumSize = sizeof(ETH2Frame) + sizeof(IP4Header) + 0; // 0 len
return minimumSize;
}
void getIPPayload(const size_t lengthOfFrame, const uint8_t *frame,
size_t &lengthOfPayload, const uint8_t *&payload, uint32_t &destination, uint8_t &protocol)
{
const size_t minimumSize = getIPMinimumSize();
if (lengthOfFrame > minimumSize)
{
const ETH2Frame *eth2Frame = reinterpret_cast<const ETH2Frame *>(frame);
const IP4Header *ip4header = reinterpret_cast<const IP4Header *>(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 size_t 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;
}

17
source/Tfe/IPRaw.h Normal file
View file

@ -0,0 +1,17 @@
#pragma once
#include <vector>
#include <cstdint>
#include <cstddef>
std::vector<uint8_t> createETH2Frame(const std::vector<uint8_t> &data,
const uint8_t *sourceMac, const uint8_t *destinationMac,
const uint8_t ttl, const uint8_t tos, const uint8_t protocol,
const uint32_t sourceAddress, const uint32_t destinationAddress);
// get the minimum size of a ETH Frame that contains a IP payload
// 34 = 14 bytes for ETH2 + 20 byes IPv4 (minimum)
size_t getIPMinimumSize();
void getIPPayload(const size_t lengthOfFrame, const uint8_t *frame,
size_t &lengthOfPayload, const uint8_t *&payload, uint32_t &destination, uint8_t &protocol);

View file

@ -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
@ -95,6 +96,12 @@ namespace
return host;
}
uint32_t readAddress(const uint8_t *ptr)
{
const uint32_t address = *reinterpret_cast<const uint32_t *>(ptr);
return address;
}
uint8_t getIByte(const uint16_t value, const size_t shift)
{
return (value >> shift) & 0xFF;
@ -141,6 +148,13 @@ namespace
writeData(socket, memory, data, len);
}
void writeDataIPRaw(Socket &socket, std::vector<uint8_t> &memory, const uint8_t *data, const size_t len, const uint32_t destination)
{
writeAny(socket, memory, destination);
write16(socket, memory, static_cast<uint16_t>(len));
writeData(socket, memory, data, len);
}
void writeDataForProtocol(Socket &socket, std::vector<uint8_t> &memory, const uint8_t *data, const size_t len, const sockaddr_in &source)
{
if (socket.sn_sr == W5100_SN_SR_SOCK_UDP)
@ -283,7 +297,7 @@ const std::string& Uthernet2::GetSnapshotCardName()
return name;
}
Uthernet2::Uthernet2(UINT slot) : Card(CT_Uthernet2, slot)
Uthernet2::Uthernet2(UINT slot) : Card(CT_Uthernet2, slot), myIPMinimumSize(getIPMinimumSize())
{
Reset(true);
}
@ -434,7 +448,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 +456,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 >= myIPMinimumSize)
{
if (acceptAll)
{
return len;
}
if (data[0] == mac[0] &&
data[1] == mac[1] &&
data[2] == mac[2] &&
@ -458,6 +466,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 +477,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 +494,134 @@ 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;
// see if any raw socket is looking for all packets
// otherwise we drop them immediately (which is the standard)
for (const Socket & socket : mySockets)
{
switch (socket.sn_sr)
{
case W5100_SN_SR_SOCK_IPRAW:
case W5100_SN_SR_SOCK_MACRAW:
{
const uint8_t mr = myMemory[socket.registerAddress + W5100_SN_MR];
const bool filterMAC = mr & W5100_SN_MR_MF;
if (!filterMAC)
{
acceptAll = true;
}
break;
}
}
}
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);
int macRawSocket = -1;
int ipRawSocket = -1;
// find the destination socket
// match IPRAW by protocol, otherwise send to MACRAW (if open)
for (size_t i = 0; i < mySockets.size(); ++ i)
{
Socket & socket = mySockets[i];
if (packetDestination == OTHER) // some sockets are accepting all packets
{
// This is a packet not for this host
// see if this sockets is listening to all
const uint8_t mr = myMemory[socket.registerAddress + W5100_SN_MR];
const bool filterMAC = mr & W5100_SN_MR_MF;
if (filterMAC)
{
// socket is filtering, packet not acceptable, try next socket
continue;
}
}
switch (socket.sn_sr)
{
case W5100_SN_SR_SOCK_MACRAW:
{
// MAC always accepts
macRawSocket = i;
break;
}
case W5100_SN_SR_SOCK_IPRAW:
{
// IP only accepts by protocol
const uint8_t socketProtocol = myMemory[socket.registerAddress + W5100_SN_PROTO];
if (payload && packetProtocol == socketProtocol)
{
ipRawSocket = i;
}
break;
}
}
}
// 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);
}
}
}
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 +674,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 +690,28 @@ void Uthernet2::receiveOnePacket(const size_t i)
};
}
void Uthernet2::sendDataIPRaw(const size_t i, std::vector<uint8_t> &payload) const
{
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 uint8_t * sourceMac = myMemory.data() + W5100_SHAR0;
const uint8_t destinationMac[6] = {0x52, 0x55, 0x0A, 0x00, 0x00, 0x01};
std::vector<uint8_t> packet = createETH2Frame(payload, sourceMac, destinationMac, ttl, tos, protocol, source, destination);
#ifdef U2_LOG_TRAFFIC
LogFileOutput("U2: Send IPRAW[%" SIZE_T_FMT "]: %" SIZE_T_FMT " bytes (%" SIZE_T_FMT ")\n", i, payload.size(), packet.size());
#endif
myNetworkBackend->transmit(packet.size(), packet.data());
}
void Uthernet2::sendDataMacRaw(const size_t i, std::vector<uint8_t> &packet) const
{
#ifdef U2_LOG_TRAFFIC
@ -656,6 +796,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);

View file

@ -54,6 +54,8 @@ class Uthernet2 : public Card
public:
static const std::string& GetSnapshotCardName();
enum PacketDestination { HOST, BROADCAST, OTHER };
Uthernet2(UINT slot);
virtual void Destroy(void) {}
@ -66,6 +68,8 @@ public:
BYTE IO_C0(WORD programcounter, WORD address, BYTE write, BYTE value, ULONG nCycles);
private:
const size_t myIPMinimumSize;
std::vector<uint8_t> myMemory;
std::vector<Socket> mySockets;
uint8_t myModeRegister;
@ -79,11 +83,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<uint8_t> &data) const;
void sendDataMacRaw(const size_t i, std::vector<uint8_t> &data) const;
void sendDataToSocket(const size_t i, std::vector<uint8_t> &data);
void sendData(const size_t i);