/* 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 &source, 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; source = ip4header->sourceAddress; return; } } } // not a good packet protocol = 0xFF; // reserved protocol payload = nullptr; lengthOfPayload = 0; source = 0; }