feat: add ethernet & TCP/IP stack

This commit is contained in:
2026-02-17 13:19:11 +01:00
parent f7e6ce70a0
commit 20fa8a9be2
23 changed files with 2409 additions and 0 deletions
+188
View File
@@ -0,0 +1,188 @@
/*
* Ipv4.cpp
* Internet Protocol version 4
* Copyright (c) 2025 Daniel Hammer
*/
#include "Ipv4.hpp"
#include <Net/ByteOrder.hpp>
#include <Net/Ethernet.hpp>
#include <Net/Arp.hpp>
#include <Net/Icmp.hpp>
#include <Net/Udp.hpp>
#include <Net/Tcp.hpp>
#include <Net/NetConfig.hpp>
#include <Libraries/Memory.hpp>
#include <Terminal/Terminal.hpp>
#include <CppLib/Stream.hpp>
#include <Timekeeping/ApicTimer.hpp>
using namespace Kt;
namespace Net::Ipv4 {
static uint16_t g_identification = 0;
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 > 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;
}
}
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)) {
// ARP request sent, wait briefly and retry
for (int attempt = 0; attempt < 3; attempt++) {
Timekeeping::Sleep(50);
if (Arp::Resolve(nextHop, destMac)) {
break;
}
}
// Final check
if (!Arp::Resolve(nextHop, destMac)) {
return false;
}
}
// Build IP packet
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;
// Calculate header checksum
hdr->Checksum = Checksum(hdr, HEADER_SIZE);
// Copy payload
memcpy(packet + HEADER_SIZE, payload, payloadLen);
return Ethernet::Send(destMac, Ethernet::ETHERTYPE_IPV4, packet, HEADER_SIZE + payloadLen);
}
}