Implement IPv4 stack and ip app (AI)
- Created src/ipv4.h: IPv4 header struct, protocol numbers, checksum, address conversion (ipv4_aton/ntoa), send/receive/routing API, protocol handler registration - Created src/ipv4.c: packet construction with header checksum, simple routing (direct subnet + gateway), incoming packet validation and dispatch to registered protocol handlers - Created apps/ip/ip.c: displays network interface config from /sys/net (MAC, link, IP, netmask, gateway); supports 'ip set' to configure interface via sysfs writes - Added ipv4.c to kernel build, kernel calls ipv4_init() at boot - Tested: clean boot, IPv4 initialized, ip app in CPIO
This commit is contained in:
@@ -71,7 +71,7 @@ Once a task is completed, it should be checked off.
|
||||
- [x] Create a network driver for the NE2000 NIC.
|
||||
- [x] Create a network driver for the 3C509B NIC. It should only support RJ45 and 10base-T.
|
||||
- [x] Create an ethernet subsytsem. Each ethernet device should be shown as a character device with the name `ethN`.
|
||||
- [ ] Create a IPv4 stack. Create the `ip` app that shows curernt IPv4 configuration. It should read this information from `/sys`
|
||||
- [x] Create a IPv4 stack. Create the `ip` app that shows curernt IPv4 configuration. It should read this information from `/sys`
|
||||
- [ ] Create a ARP subsystem. Create the `arp` command that shows current ARP tables. Again, this info should be found in `/sys`
|
||||
- [ ] Create a DHCP subsystem. Create the `dhcp` command to show current DHCP status information.
|
||||
- [ ] Create a UDP and TCP stack.
|
||||
|
||||
202
apps/ip/ip.c
Normal file
202
apps/ip/ip.c
Normal file
@@ -0,0 +1,202 @@
|
||||
/**
|
||||
* @file ip.c
|
||||
* @brief Display and configure IPv4 network configuration.
|
||||
*
|
||||
* Reads network interface information from /sys/net and displays
|
||||
* the current IPv4 configuration for all interfaces.
|
||||
*
|
||||
* Usage:
|
||||
* ip - Show all interfaces
|
||||
* ip set <iface> <ip> <netmask> <gateway> - Configure an interface
|
||||
*
|
||||
* Examples:
|
||||
* ip
|
||||
* ip set eth1 192.168.1.100 255.255.255.0 192.168.1.1
|
||||
*/
|
||||
|
||||
#include "syscalls.h"
|
||||
|
||||
/**
|
||||
* Read the contents of a sysfs file into buf.
|
||||
* Returns number of bytes read, or -1 on failure.
|
||||
*/
|
||||
static int32_t read_sysfs(const char *path, char *buf, uint32_t size) {
|
||||
int32_t fd = open(path, 0);
|
||||
if (fd < 0) return -1;
|
||||
|
||||
int32_t n = read(fd, buf, size - 1);
|
||||
close(fd);
|
||||
|
||||
if (n > 0) {
|
||||
buf[n] = '\0';
|
||||
/* Strip trailing newline */
|
||||
for (int32_t i = n - 1; i >= 0; i--) {
|
||||
if (buf[i] == '\n' || buf[i] == '\r') buf[i] = '\0';
|
||||
else break;
|
||||
}
|
||||
} else {
|
||||
buf[0] = '\0';
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a sysfs path: /sys/net/<iface>/<file>
|
||||
*/
|
||||
static void build_path(char *out, uint32_t out_size,
|
||||
const char *iface, const char *file) {
|
||||
/* Manual string concatenation */
|
||||
uint32_t pos = 0;
|
||||
const char *prefix = "/sys/net/";
|
||||
while (*prefix && pos < out_size - 1) out[pos++] = *prefix++;
|
||||
while (*iface && pos < out_size - 1) out[pos++] = *iface++;
|
||||
if (pos < out_size - 1) out[pos++] = '/';
|
||||
while (*file && pos < out_size - 1) out[pos++] = *file++;
|
||||
out[pos] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Show info for one interface.
|
||||
*/
|
||||
static void show_iface(const char *name) {
|
||||
char path[128];
|
||||
char val[64];
|
||||
|
||||
puts(name);
|
||||
puts(":\n");
|
||||
|
||||
/* MAC address */
|
||||
build_path(path, sizeof(path), name, "mac");
|
||||
if (read_sysfs(path, val, sizeof(val)) > 0) {
|
||||
puts(" MAC: ");
|
||||
puts(val);
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
/* Link status */
|
||||
build_path(path, sizeof(path), name, "link");
|
||||
if (read_sysfs(path, val, sizeof(val)) > 0) {
|
||||
puts(" Link: ");
|
||||
puts(val);
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
/* IP address */
|
||||
build_path(path, sizeof(path), name, "ip");
|
||||
if (read_sysfs(path, val, sizeof(val)) > 0) {
|
||||
puts(" IP: ");
|
||||
puts(val);
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
/* Netmask */
|
||||
build_path(path, sizeof(path), name, "netmask");
|
||||
if (read_sysfs(path, val, sizeof(val)) > 0) {
|
||||
puts(" Netmask: ");
|
||||
puts(val);
|
||||
puts("\n");
|
||||
}
|
||||
|
||||
/* Gateway */
|
||||
build_path(path, sizeof(path), name, "gateway");
|
||||
if (read_sysfs(path, val, sizeof(val)) > 0) {
|
||||
puts(" Gateway: ");
|
||||
puts(val);
|
||||
puts("\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a value to a sysfs file.
|
||||
*/
|
||||
static int32_t write_sysfs(const char *path, const char *value) {
|
||||
int32_t fd = open(path, 0);
|
||||
if (fd < 0) return -1;
|
||||
|
||||
int32_t n = write(fd, value, strlen(value));
|
||||
close(fd);
|
||||
return n;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
char arg1[64];
|
||||
|
||||
/* Check if we have a subcommand */
|
||||
if (getenv("ARG1", arg1, sizeof(arg1)) < 0 || arg1[0] == '\0') {
|
||||
/* No arguments — show all interfaces */
|
||||
char name[128];
|
||||
uint32_t idx = 0;
|
||||
int found = 0;
|
||||
|
||||
while (readdir("/sys/net", idx, name) >= 0) {
|
||||
show_iface(name);
|
||||
found = 1;
|
||||
idx++;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
puts("No network interfaces found.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check for "set" subcommand */
|
||||
if (strcmp(arg1, "set") == 0) {
|
||||
char iface_name[32], ip[32], netmask[32], gateway[32];
|
||||
char path[128];
|
||||
|
||||
if (getenv("ARG2", iface_name, sizeof(iface_name)) < 0) {
|
||||
puts("Usage: ip set <iface> <ip> <netmask> <gateway>\n");
|
||||
return 1;
|
||||
}
|
||||
if (getenv("ARG3", ip, sizeof(ip)) < 0) {
|
||||
puts("Usage: ip set <iface> <ip> <netmask> <gateway>\n");
|
||||
return 1;
|
||||
}
|
||||
if (getenv("ARG4", netmask, sizeof(netmask)) < 0) {
|
||||
puts("Usage: ip set <iface> <ip> <netmask> <gateway>\n");
|
||||
return 1;
|
||||
}
|
||||
if (getenv("ARG5", gateway, sizeof(gateway)) < 0) {
|
||||
puts("Usage: ip set <iface> <ip> <netmask> <gateway>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Write IP */
|
||||
build_path(path, sizeof(path), iface_name, "ip");
|
||||
if (write_sysfs(path, ip) < 0) {
|
||||
puts("Failed to set IP address\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Write netmask */
|
||||
build_path(path, sizeof(path), iface_name, "netmask");
|
||||
if (write_sysfs(path, netmask) < 0) {
|
||||
puts("Failed to set netmask\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Write gateway */
|
||||
build_path(path, sizeof(path), iface_name, "gateway");
|
||||
if (write_sysfs(path, gateway) < 0) {
|
||||
puts("Failed to set gateway\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
puts("Configured ");
|
||||
puts(iface_name);
|
||||
puts(": ");
|
||||
puts(ip);
|
||||
puts(" / ");
|
||||
puts(netmask);
|
||||
puts(" gw ");
|
||||
puts(gateway);
|
||||
puts("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
puts("Unknown command: ");
|
||||
puts(arg1);
|
||||
puts("\nUsage: ip [set <iface> <ip> <netmask> <gateway>]\n");
|
||||
return 1;
|
||||
}
|
||||
18
build.log
18
build.log
@@ -1,3 +1,6 @@
|
||||
-- Configuring done (0.1s)
|
||||
-- Generating done (0.2s)
|
||||
-- Build files have been written to: /workspaces/claude-os/build
|
||||
[ 2%] Building user-mode applications
|
||||
Building app: cat
|
||||
Built: /workspaces/claude-os/build/apps_bin/cat (310 bytes)
|
||||
@@ -11,6 +14,8 @@ Building app: fork-test
|
||||
Built: /workspaces/claude-os/build/apps_bin/fork-test (132 bytes)
|
||||
Building app: hello-world
|
||||
Built: /workspaces/claude-os/build/apps_bin/hello-world (49 bytes)
|
||||
Building app: ip
|
||||
Built: /workspaces/claude-os/build/apps_bin/ip (3695 bytes)
|
||||
Building app: ls
|
||||
Built: /workspaces/claude-os/build/apps_bin/ls (250 bytes)
|
||||
Building app: mkfs.fat32
|
||||
@@ -29,7 +34,12 @@ Building app: sh
|
||||
1 warning generated.
|
||||
Built: /workspaces/claude-os/build/apps_bin/sh (3428 bytes)
|
||||
[ 2%] Built target apps
|
||||
[ 5%] Generating CPIO initial ramdisk
|
||||
Generated initrd: 24100 bytes
|
||||
[ 5%] Built target initrd
|
||||
[ 8%] Building C object src/CMakeFiles/kernel.dir/kernel.c.o
|
||||
[ 11%] Building C object src/CMakeFiles/kernel.dir/ipv4.c.o
|
||||
[ 13%] Linking C executable ../bin/kernel
|
||||
[ 97%] Built target kernel
|
||||
[100%] Generating bootable ISO image
|
||||
xorriso 1.5.6 : RockRidge filesystem manipulator, libburnia project.
|
||||
@@ -38,14 +48,14 @@ Drive current: -outdev 'stdio:/workspaces/claude-os/release/claude-os.iso'
|
||||
Media current: stdio file, overwriteable
|
||||
Media status : is blank
|
||||
Media summary: 0 sessions, 0 data blocks, 0 data, 126g free
|
||||
Added to ISO image: directory '/'='/tmp/grub.KhfgHk'
|
||||
Added to ISO image: directory '/'='/tmp/grub.JjepBC'
|
||||
xorriso : UPDATE : 581 files added in 1 seconds
|
||||
Added to ISO image: directory '/'='/workspaces/claude-os/build/isodir'
|
||||
xorriso : UPDATE : 586 files added in 1 seconds
|
||||
xorriso : NOTE : Copying to System Area: 512 bytes from file '/usr/lib/grub/i386-pc/boot_hybrid.img'
|
||||
xorriso : UPDATE : Thank you for being patient. Working since 0 seconds.
|
||||
ISO image produced: 5967 sectors
|
||||
Written to medium : 5967 sectors at LBA 0
|
||||
xorriso : UPDATE : 0.27% done
|
||||
ISO image produced: 5974 sectors
|
||||
Written to medium : 5974 sectors at LBA 0
|
||||
Writing to 'stdio:/workspaces/claude-os/release/claude-os.iso' completed successfully.
|
||||
|
||||
[100%] Built target iso
|
||||
|
||||
@@ -28,6 +28,7 @@ add_executable(kernel
|
||||
ne2000.c
|
||||
e3c509.c
|
||||
ethernet.c
|
||||
ipv4.c
|
||||
env.c
|
||||
keyboard.c
|
||||
interrupts.S
|
||||
|
||||
279
src/ipv4.c
Normal file
279
src/ipv4.c
Normal file
@@ -0,0 +1,279 @@
|
||||
/**
|
||||
* @file ipv4.c
|
||||
* @brief IPv4 network layer implementation.
|
||||
*
|
||||
* Handles IPv4 packet construction, parsing, checksum calculation,
|
||||
* and routing to the appropriate Ethernet interface.
|
||||
*
|
||||
* Incoming Ethernet frames with EtherType 0x0800 are passed to
|
||||
* ipv4_receive(), which validates the header and dispatches to
|
||||
* registered protocol handlers (ICMP, UDP, TCP, etc.).
|
||||
*/
|
||||
|
||||
#include "ipv4.h"
|
||||
#include "ethernet.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Debug print helpers */
|
||||
extern void offset_print(const char *str);
|
||||
extern void print_hex(uint32_t val);
|
||||
|
||||
/* ================================================================
|
||||
* Global state
|
||||
* ================================================================ */
|
||||
|
||||
/** Maximum number of registered protocol handlers. */
|
||||
#define MAX_PROTO_HANDLERS 16
|
||||
|
||||
/** Protocol handler entry. */
|
||||
typedef struct {
|
||||
uint8_t protocol;
|
||||
ipv4_proto_handler_t handler;
|
||||
} proto_handler_t;
|
||||
|
||||
/** Registered protocol handlers. */
|
||||
static proto_handler_t proto_handlers[MAX_PROTO_HANDLERS];
|
||||
static uint32_t proto_handler_count = 0;
|
||||
|
||||
/** Global IP identification counter. */
|
||||
static uint16_t ip_id_counter = 1;
|
||||
|
||||
/* ================================================================
|
||||
* Checksum
|
||||
* ================================================================ */
|
||||
|
||||
uint16_t ipv4_checksum(const void *data, uint32_t len) {
|
||||
const uint16_t *words = (const uint16_t *)data;
|
||||
uint32_t sum = 0;
|
||||
|
||||
while (len > 1) {
|
||||
sum += *words++;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
/* Add left-over byte, if any */
|
||||
if (len == 1) {
|
||||
sum += *(const uint8_t *)words;
|
||||
}
|
||||
|
||||
/* Fold 32-bit sum into 16 bits */
|
||||
while (sum >> 16) {
|
||||
sum = (sum & 0xFFFF) + (sum >> 16);
|
||||
}
|
||||
|
||||
return (uint16_t)(~sum);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Address conversion helpers
|
||||
* ================================================================ */
|
||||
|
||||
uint32_t ipv4_aton(const char *str) {
|
||||
uint32_t octets[4] = {0};
|
||||
int idx = 0;
|
||||
|
||||
for (const char *p = str; *p && idx < 4; p++) {
|
||||
if (*p == '.') {
|
||||
idx++;
|
||||
} else if (*p >= '0' && *p <= '9') {
|
||||
octets[idx] = octets[idx] * 10 + (uint32_t)(*p - '0');
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (octets[0] << 24) | (octets[1] << 16) |
|
||||
(octets[2] << 8) | octets[3];
|
||||
}
|
||||
|
||||
char *ipv4_ntoa(uint32_t ip, char *buf, uint32_t size) {
|
||||
if (size < 16) { buf[0] = '\0'; return buf; }
|
||||
|
||||
int pos = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (i > 0) buf[pos++] = '.';
|
||||
uint8_t octet = (uint8_t)((ip >> (24 - i * 8)) & 0xFF);
|
||||
if (octet >= 100) {
|
||||
buf[pos++] = (char)('0' + octet / 100);
|
||||
buf[pos++] = (char)('0' + (octet % 100) / 10);
|
||||
buf[pos++] = (char)('0' + octet % 10);
|
||||
} else if (octet >= 10) {
|
||||
buf[pos++] = (char)('0' + octet / 10);
|
||||
buf[pos++] = (char)('0' + octet % 10);
|
||||
} else {
|
||||
buf[pos++] = (char)('0' + octet);
|
||||
}
|
||||
}
|
||||
buf[pos] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Routing
|
||||
* ================================================================ */
|
||||
|
||||
/**
|
||||
* Find the Ethernet interface for a given destination IP.
|
||||
*
|
||||
* Simple routing: check if dst is on a directly-connected subnet.
|
||||
* If not, use the first interface with a configured gateway.
|
||||
* Fallback: first interface.
|
||||
*
|
||||
* @param dst_ip Destination IP (host byte order).
|
||||
* @return Interface index, or -1 if no interfaces.
|
||||
*/
|
||||
static int ipv4_route(uint32_t dst_ip) {
|
||||
uint32_t count = ethernet_get_iface_count();
|
||||
if (count == 0) return -1;
|
||||
|
||||
/* Check for directly-connected subnet */
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
eth_iface_t *iface = ethernet_get_iface(i);
|
||||
if (!iface || !iface->active) continue;
|
||||
if (iface->ip_addr == 0 || iface->netmask == 0) continue;
|
||||
|
||||
if ((dst_ip & iface->netmask) == (iface->ip_addr & iface->netmask)) {
|
||||
return (int)i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Use first interface with a gateway */
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
eth_iface_t *iface = ethernet_get_iface(i);
|
||||
if (!iface || !iface->active) continue;
|
||||
if (iface->gateway != 0) return (int)i;
|
||||
}
|
||||
|
||||
/* Fallback: first active interface */
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
eth_iface_t *iface = ethernet_get_iface(i);
|
||||
if (iface && iface->active) return (int)i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Send
|
||||
* ================================================================ */
|
||||
|
||||
int ipv4_send_iface(uint32_t iface_idx, uint32_t dst_ip, uint8_t protocol,
|
||||
const void *payload, uint32_t len) {
|
||||
if (len > IPV4_MTU) return -1;
|
||||
|
||||
eth_iface_t *iface = ethernet_get_iface(iface_idx);
|
||||
if (!iface || !iface->active) return -1;
|
||||
|
||||
/* Build IPv4 packet on stack */
|
||||
uint8_t packet[ETH_MTU];
|
||||
ipv4_header_t *hdr = (ipv4_header_t *)packet;
|
||||
|
||||
hdr->ihl_version = 0x45; /* IPv4, IHL=5 (20 bytes) */
|
||||
hdr->tos = 0;
|
||||
hdr->total_length = htons((uint16_t)(IPV4_HLEN + len));
|
||||
hdr->identification = htons(ip_id_counter++);
|
||||
hdr->flags_fragoff = htons(IPV4_FLAG_DF); /* Don't fragment */
|
||||
hdr->ttl = 64;
|
||||
hdr->protocol = protocol;
|
||||
hdr->checksum = 0;
|
||||
hdr->src_ip = htonl(iface->ip_addr);
|
||||
hdr->dst_ip = htonl(dst_ip);
|
||||
|
||||
/* Compute header checksum */
|
||||
hdr->checksum = ipv4_checksum(hdr, IPV4_HLEN);
|
||||
|
||||
/* Copy payload */
|
||||
memcpy(packet + IPV4_HLEN, payload, len);
|
||||
|
||||
/* Determine destination MAC.
|
||||
* For now, use broadcast (FF:FF:FF:FF:FF:FF) — the ARP subsystem
|
||||
* will override this once implemented. */
|
||||
uint8_t dst_mac[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
|
||||
/* TODO: ARP lookup for dst_ip (or gateway if not on-link) */
|
||||
|
||||
return ethernet_send(iface, dst_mac, ETHERTYPE_IPV4, packet,
|
||||
IPV4_HLEN + len);
|
||||
}
|
||||
|
||||
int ipv4_send(uint32_t dst_ip, uint8_t protocol,
|
||||
const void *payload, uint32_t len) {
|
||||
int iface_idx = ipv4_route(dst_ip);
|
||||
if (iface_idx < 0) return -1;
|
||||
return ipv4_send_iface((uint32_t)iface_idx, dst_ip, protocol, payload, len);
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Receive
|
||||
* ================================================================ */
|
||||
|
||||
void ipv4_receive(const void *data, uint32_t len, uint32_t iface_idx) {
|
||||
if (len < IPV4_HLEN) return;
|
||||
|
||||
const ipv4_header_t *hdr = (const ipv4_header_t *)data;
|
||||
|
||||
/* Verify version */
|
||||
if ((hdr->ihl_version >> 4) != 4) return;
|
||||
|
||||
/* Verify header length */
|
||||
uint32_t ihl = (uint32_t)(hdr->ihl_version & 0x0F) * 4;
|
||||
if (ihl < IPV4_HLEN || ihl > len) return;
|
||||
|
||||
/* Verify checksum */
|
||||
if (ipv4_checksum(data, ihl) != 0) return;
|
||||
|
||||
uint16_t total_len = ntohs(hdr->total_length);
|
||||
if (total_len > len) return;
|
||||
|
||||
uint32_t src_ip = ntohl(hdr->src_ip);
|
||||
uint32_t dst_ip = ntohl(hdr->dst_ip);
|
||||
const uint8_t *payload = (const uint8_t *)data + ihl;
|
||||
uint32_t payload_len = total_len - ihl;
|
||||
|
||||
/* Check if this packet is for us */
|
||||
eth_iface_t *iface = ethernet_get_iface(iface_idx);
|
||||
if (iface && iface->ip_addr != 0) {
|
||||
if (dst_ip != iface->ip_addr &&
|
||||
dst_ip != 0xFFFFFFFF && /* broadcast */
|
||||
(dst_ip & ~iface->netmask) != ~iface->netmask) {
|
||||
return; /* Not for us */
|
||||
}
|
||||
}
|
||||
|
||||
/* Dispatch to registered protocol handler */
|
||||
for (uint32_t i = 0; i < proto_handler_count; i++) {
|
||||
if (proto_handlers[i].protocol == hdr->protocol) {
|
||||
proto_handlers[i].handler(src_ip, dst_ip, payload,
|
||||
payload_len, iface_idx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* No handler registered for this protocol */
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Protocol registration
|
||||
* ================================================================ */
|
||||
|
||||
int ipv4_register_proto(uint8_t protocol, ipv4_proto_handler_t handler) {
|
||||
if (proto_handler_count >= MAX_PROTO_HANDLERS) return -1;
|
||||
|
||||
proto_handlers[proto_handler_count].protocol = protocol;
|
||||
proto_handlers[proto_handler_count].handler = handler;
|
||||
proto_handler_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Initialization
|
||||
* ================================================================ */
|
||||
|
||||
void ipv4_init(void) {
|
||||
memset(proto_handlers, 0, sizeof(proto_handlers));
|
||||
proto_handler_count = 0;
|
||||
ip_id_counter = 1;
|
||||
|
||||
offset_print(" IPv4: initialized\n");
|
||||
}
|
||||
158
src/ipv4.h
Normal file
158
src/ipv4.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* @file ipv4.h
|
||||
* @brief IPv4 network layer.
|
||||
*
|
||||
* Provides IPv4 packet construction, parsing, and routing.
|
||||
* Sits on top of the Ethernet subsystem and provides the foundation
|
||||
* for higher-level protocols (ICMP, UDP, TCP).
|
||||
*
|
||||
* The IPv4 stack maintains a simple routing table: each Ethernet
|
||||
* interface has an IP address, netmask, and default gateway.
|
||||
* Outbound packets are routed to the appropriate interface.
|
||||
*
|
||||
* Inbound packets are dispatched to registered protocol handlers
|
||||
* based on the IP protocol field.
|
||||
*/
|
||||
|
||||
#ifndef IPV4_H
|
||||
#define IPV4_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* ================================================================
|
||||
* IPv4 header (20 bytes minimum, no options)
|
||||
* ================================================================ */
|
||||
|
||||
/** IPv4 header structure (network byte order in memory). */
|
||||
typedef struct __attribute__((packed)) ipv4_header {
|
||||
uint8_t ihl_version; /**< Version (4) and IHL (5 for no options). */
|
||||
uint8_t tos; /**< Type of Service. */
|
||||
uint16_t total_length; /**< Total datagram length (header + data). */
|
||||
uint16_t identification; /**< Fragment identification. */
|
||||
uint16_t flags_fragoff; /**< Flags (3 bits) + Fragment Offset (13 bits). */
|
||||
uint8_t ttl; /**< Time to Live. */
|
||||
uint8_t protocol; /**< Upper-layer protocol number. */
|
||||
uint16_t checksum; /**< Header checksum. */
|
||||
uint32_t src_ip; /**< Source IP address (network byte order). */
|
||||
uint32_t dst_ip; /**< Destination IP address (network byte order). */
|
||||
} ipv4_header_t;
|
||||
|
||||
/** IPv4 header size (no options). */
|
||||
#define IPV4_HLEN 20
|
||||
|
||||
/** Maximum IPv4 payload over Ethernet (1500 - 20). */
|
||||
#define IPV4_MTU 1480
|
||||
|
||||
/* ================================================================
|
||||
* Protocol numbers
|
||||
* ================================================================ */
|
||||
#define IP_PROTO_ICMP 1
|
||||
#define IP_PROTO_TCP 6
|
||||
#define IP_PROTO_UDP 17
|
||||
|
||||
/* ================================================================
|
||||
* IPv4 flags
|
||||
* ================================================================ */
|
||||
#define IPV4_FLAG_DF 0x4000 /**< Don't Fragment. */
|
||||
#define IPV4_FLAG_MF 0x2000 /**< More Fragments. */
|
||||
|
||||
/* ================================================================
|
||||
* Protocol handler callback
|
||||
* ================================================================ */
|
||||
|
||||
/**
|
||||
* Callback for handling incoming IPv4 packets of a specific protocol.
|
||||
*
|
||||
* @param src_ip Source IP address (host byte order).
|
||||
* @param dst_ip Destination IP address (host byte order).
|
||||
* @param payload Protocol payload (past IPv4 header).
|
||||
* @param len Payload length.
|
||||
* @param iface_idx Ethernet interface index the packet arrived on.
|
||||
*/
|
||||
typedef void (*ipv4_proto_handler_t)(uint32_t src_ip, uint32_t dst_ip,
|
||||
const void *payload, uint32_t len,
|
||||
uint32_t iface_idx);
|
||||
|
||||
/* ================================================================
|
||||
* Public API
|
||||
* ================================================================ */
|
||||
|
||||
/**
|
||||
* Initialize the IPv4 subsystem.
|
||||
*/
|
||||
void ipv4_init(void);
|
||||
|
||||
/**
|
||||
* Send an IPv4 packet.
|
||||
*
|
||||
* Automatically routes to the correct Ethernet interface based on
|
||||
* the destination IP and configured routes.
|
||||
*
|
||||
* @param dst_ip Destination IP (host byte order).
|
||||
* @param protocol IP protocol number (e.g., IP_PROTO_UDP).
|
||||
* @param payload Packet payload.
|
||||
* @param len Payload length (max IPV4_MTU).
|
||||
* @return 0 on success, -1 on failure.
|
||||
*/
|
||||
int ipv4_send(uint32_t dst_ip, uint8_t protocol,
|
||||
const void *payload, uint32_t len);
|
||||
|
||||
/**
|
||||
* Send an IPv4 packet through a specific interface.
|
||||
*
|
||||
* @param iface_idx Ethernet interface index.
|
||||
* @param dst_ip Destination IP (host byte order).
|
||||
* @param protocol IP protocol number.
|
||||
* @param payload Packet payload.
|
||||
* @param len Payload length.
|
||||
* @return 0 on success, -1 on failure.
|
||||
*/
|
||||
int ipv4_send_iface(uint32_t iface_idx, uint32_t dst_ip, uint8_t protocol,
|
||||
const void *payload, uint32_t len);
|
||||
|
||||
/**
|
||||
* Process an incoming IPv4 packet (called from Ethernet layer).
|
||||
*
|
||||
* @param data Raw IPv4 packet (header + payload).
|
||||
* @param len Total packet length.
|
||||
* @param iface_idx Interface index the packet arrived on.
|
||||
*/
|
||||
void ipv4_receive(const void *data, uint32_t len, uint32_t iface_idx);
|
||||
|
||||
/**
|
||||
* Register a protocol handler.
|
||||
*
|
||||
* @param protocol IP protocol number.
|
||||
* @param handler Callback function.
|
||||
* @return 0 on success, -1 if table is full.
|
||||
*/
|
||||
int ipv4_register_proto(uint8_t protocol, ipv4_proto_handler_t handler);
|
||||
|
||||
/**
|
||||
* Compute the Internet checksum over a buffer.
|
||||
*
|
||||
* @param data Buffer.
|
||||
* @param len Length in bytes.
|
||||
* @return Checksum in network byte order.
|
||||
*/
|
||||
uint16_t ipv4_checksum(const void *data, uint32_t len);
|
||||
|
||||
/**
|
||||
* Convert an IPv4 address string "A.B.C.D" to a uint32_t (host byte order).
|
||||
*
|
||||
* @param str Dotted-decimal string.
|
||||
* @return IPv4 address, or 0 on failure.
|
||||
*/
|
||||
uint32_t ipv4_aton(const char *str);
|
||||
|
||||
/**
|
||||
* Format an IPv4 address (host byte order) into a dotted-decimal string.
|
||||
*
|
||||
* @param ip IPv4 address.
|
||||
* @param buf Output buffer (at least 16 bytes).
|
||||
* @param size Buffer size.
|
||||
* @return Pointer to buf.
|
||||
*/
|
||||
char *ipv4_ntoa(uint32_t ip, char *buf, uint32_t size);
|
||||
|
||||
#endif /* IPV4_H */
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "fat32.h"
|
||||
#include "keyboard.h"
|
||||
#include "ethernet.h"
|
||||
#include "ipv4.h"
|
||||
#include "framebuffer.h"
|
||||
|
||||
/* Global framebuffer info, parsed from multiboot2 tags. */
|
||||
@@ -419,6 +420,9 @@ void kernel_main(uint32_t magic, uint32_t addr) {
|
||||
ethernet_init();
|
||||
offset_print("Ethernet subsystem initialized\n");
|
||||
|
||||
ipv4_init();
|
||||
offset_print("IPv4 stack initialized\n");
|
||||
|
||||
init_drivers();
|
||||
EARLY_PRINT("DRV ");
|
||||
offset_print("Drivers initialized\n");
|
||||
|
||||
Reference in New Issue
Block a user