/* * Ipv4.cpp * Internet Protocol version 4 * Copyright (c) 2025 Daniel Hammer */ #include "Ipv4.hpp" #include #include #include #include #include #include #include #include #include #include using namespace Kt; namespace Net::Ipv4 { static uint16_t g_identification = 0; // Deferred packet queue for packets awaiting ARP resolution struct PendingPacket { uint32_t DestIp; uint8_t Protocol; uint8_t Data[Ethernet::MAX_PAYLOAD_SIZE - HEADER_SIZE]; uint16_t Length; bool Active; }; static constexpr uint32_t PENDING_QUEUE_SIZE = 8; static PendingPacket g_pendingQueue[PENDING_QUEUE_SIZE] = {}; void Initialize() { g_identification = 0; KernelLogStream(OK, "Net") << "IPv4 initialized, IP: " << base::dec << (uint64_t)(GetIpAddress() & 0xFF) << "." << (uint64_t)((GetIpAddress() >> 8) & 0xFF) << "." << (uint64_t)((GetIpAddress() >> 16) & 0xFF) << "." << (uint64_t)((GetIpAddress() >> 24) & 0xFF); } uint16_t Checksum(const void* data, uint16_t length) { const uint16_t* ptr = (const uint16_t*)data; uint32_t sum = 0; while (length > 1) { sum += *ptr++; length -= 2; } // Handle odd byte if (length == 1) { sum += *(const uint8_t*)ptr; } // Fold 32-bit sum into 16 bits while (sum >> 16) { sum = (sum & 0xFFFF) + (sum >> 16); } return (uint16_t)(~sum); } uint16_t PseudoHeaderChecksum(uint32_t srcIp, uint32_t dstIp, uint8_t protocol, uint16_t length, const void* data, uint16_t dataLen) { uint32_t sum = 0; // Pseudo-header fields (already in network byte order) sum += (srcIp & 0xFFFF); sum += (srcIp >> 16); sum += (dstIp & 0xFFFF); sum += (dstIp >> 16); sum += Htons(protocol); sum += Htons(length); // Data const uint16_t* ptr = (const uint16_t*)data; uint16_t remaining = dataLen; while (remaining > 1) { sum += *ptr++; remaining -= 2; } if (remaining == 1) { sum += *(const uint8_t*)ptr; } while (sum >> 16) { sum = (sum & 0xFFFF) + (sum >> 16); } return (uint16_t)(~sum); } void OnPacketReceived(const uint8_t* data, uint16_t length) { if (length < HEADER_SIZE) { return; } const Header* hdr = (const Header*)data; // Verify version uint8_t version = (hdr->VersionIhl >> 4) & 0xF; if (version != 4) { return; } // Get header length uint8_t ihl = (hdr->VersionIhl & 0xF) * 4; if (ihl < HEADER_SIZE || ihl > length) { return; } // Verify checksum if (Checksum(data, ihl) != 0) { return; } uint16_t totalLen = Ntohs(hdr->TotalLength); if (totalLen < ihl || totalLen > length) { return; } // Check destination: accept packets addressed to us or broadcast uint32_t ourIp = GetIpAddress(); if (hdr->DstIp != ourIp && hdr->DstIp != 0xFFFFFFFF) { return; } const uint8_t* payload = data + ihl; uint16_t payloadLen = totalLen - ihl; switch (hdr->Protocol) { case PROTO_ICMP: Icmp::OnPacketReceived(hdr->SrcIp, payload, payloadLen); break; case PROTO_UDP: Udp::OnPacketReceived(hdr->SrcIp, hdr->DstIp, payload, payloadLen); break; case PROTO_TCP: Tcp::OnPacketReceived(hdr->SrcIp, hdr->DstIp, payload, payloadLen); break; default: break; } } // Build and send an IP packet over Ethernet (MAC already resolved) static bool SendDirect(uint32_t destIp, uint8_t protocol, const uint8_t* destMac, const uint8_t* payload, uint16_t payloadLen) { uint8_t packet[Ethernet::MAX_PAYLOAD_SIZE]; Header* hdr = (Header*)packet; hdr->VersionIhl = (4 << 4) | 5; // IPv4, 5 dwords (20 bytes) hdr->Tos = 0; hdr->TotalLength = Htons(HEADER_SIZE + payloadLen); hdr->Identification = Htons(g_identification++); hdr->FlagsFragment = 0; hdr->Ttl = DEFAULT_TTL; hdr->Protocol = protocol; hdr->Checksum = 0; hdr->SrcIp = GetIpAddress(); hdr->DstIp = destIp; hdr->Checksum = Checksum(hdr, HEADER_SIZE); memcpy(packet + HEADER_SIZE, payload, payloadLen); return Ethernet::Send(destMac, Ethernet::ETHERTYPE_IPV4, packet, HEADER_SIZE + payloadLen); } bool Send(uint32_t destIp, uint8_t protocol, const uint8_t* payload, uint16_t payloadLen) { if (payloadLen > (Ethernet::MAX_PAYLOAD_SIZE - HEADER_SIZE)) { return false; } // Determine next-hop IP and resolve MAC uint32_t nextHop = GetNextHop(destIp); uint8_t destMac[6]; if (Arp::Resolve(nextHop, destMac)) { return SendDirect(destIp, protocol, destMac, payload, payloadLen); } // ARP request already sent by Resolve(), queue the packet for later for (uint32_t i = 0; i < PENDING_QUEUE_SIZE; i++) { if (!g_pendingQueue[i].Active) { g_pendingQueue[i].DestIp = destIp; g_pendingQueue[i].Protocol = protocol; g_pendingQueue[i].Length = payloadLen; memcpy(g_pendingQueue[i].Data, payload, payloadLen); g_pendingQueue[i].Active = true; return true; } } // Queue full, drop the packet return false; } void FlushPending() { for (uint32_t i = 0; i < PENDING_QUEUE_SIZE; i++) { if (!g_pendingQueue[i].Active) { continue; } uint32_t nextHop = GetNextHop(g_pendingQueue[i].DestIp); uint8_t destMac[6]; if (Arp::Resolve(nextHop, destMac)) { SendDirect(g_pendingQueue[i].DestIp, g_pendingQueue[i].Protocol, destMac, g_pendingQueue[i].Data, g_pendingQueue[i].Length); g_pendingQueue[i].Active = false; } } } }