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 NE2000 NIC.
|
||||||
- [x] Create a network driver for the 3C509B NIC. It should only support RJ45 and 10base-T.
|
- [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`.
|
- [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 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 DHCP subsystem. Create the `dhcp` command to show current DHCP status information.
|
||||||
- [ ] Create a UDP and TCP stack.
|
- [ ] 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
|
[ 2%] Building user-mode applications
|
||||||
Building app: cat
|
Building app: cat
|
||||||
Built: /workspaces/claude-os/build/apps_bin/cat (310 bytes)
|
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)
|
Built: /workspaces/claude-os/build/apps_bin/fork-test (132 bytes)
|
||||||
Building app: hello-world
|
Building app: hello-world
|
||||||
Built: /workspaces/claude-os/build/apps_bin/hello-world (49 bytes)
|
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
|
Building app: ls
|
||||||
Built: /workspaces/claude-os/build/apps_bin/ls (250 bytes)
|
Built: /workspaces/claude-os/build/apps_bin/ls (250 bytes)
|
||||||
Building app: mkfs.fat32
|
Building app: mkfs.fat32
|
||||||
@@ -29,7 +34,12 @@ Building app: sh
|
|||||||
1 warning generated.
|
1 warning generated.
|
||||||
Built: /workspaces/claude-os/build/apps_bin/sh (3428 bytes)
|
Built: /workspaces/claude-os/build/apps_bin/sh (3428 bytes)
|
||||||
[ 2%] Built target apps
|
[ 2%] Built target apps
|
||||||
|
[ 5%] Generating CPIO initial ramdisk
|
||||||
|
Generated initrd: 24100 bytes
|
||||||
[ 5%] Built target initrd
|
[ 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
|
[ 97%] Built target kernel
|
||||||
[100%] Generating bootable ISO image
|
[100%] Generating bootable ISO image
|
||||||
xorriso 1.5.6 : RockRidge filesystem manipulator, libburnia project.
|
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 current: stdio file, overwriteable
|
||||||
Media status : is blank
|
Media status : is blank
|
||||||
Media summary: 0 sessions, 0 data blocks, 0 data, 126g free
|
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
|
xorriso : UPDATE : 581 files added in 1 seconds
|
||||||
Added to ISO image: directory '/'='/workspaces/claude-os/build/isodir'
|
Added to ISO image: directory '/'='/workspaces/claude-os/build/isodir'
|
||||||
xorriso : UPDATE : 586 files added in 1 seconds
|
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 : 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.
|
xorriso : UPDATE : 0.27% done
|
||||||
ISO image produced: 5967 sectors
|
ISO image produced: 5974 sectors
|
||||||
Written to medium : 5967 sectors at LBA 0
|
Written to medium : 5974 sectors at LBA 0
|
||||||
Writing to 'stdio:/workspaces/claude-os/release/claude-os.iso' completed successfully.
|
Writing to 'stdio:/workspaces/claude-os/release/claude-os.iso' completed successfully.
|
||||||
|
|
||||||
[100%] Built target iso
|
[100%] Built target iso
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ add_executable(kernel
|
|||||||
ne2000.c
|
ne2000.c
|
||||||
e3c509.c
|
e3c509.c
|
||||||
ethernet.c
|
ethernet.c
|
||||||
|
ipv4.c
|
||||||
env.c
|
env.c
|
||||||
keyboard.c
|
keyboard.c
|
||||||
interrupts.S
|
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 "fat32.h"
|
||||||
#include "keyboard.h"
|
#include "keyboard.h"
|
||||||
#include "ethernet.h"
|
#include "ethernet.h"
|
||||||
|
#include "ipv4.h"
|
||||||
#include "framebuffer.h"
|
#include "framebuffer.h"
|
||||||
|
|
||||||
/* Global framebuffer info, parsed from multiboot2 tags. */
|
/* Global framebuffer info, parsed from multiboot2 tags. */
|
||||||
@@ -419,6 +420,9 @@ void kernel_main(uint32_t magic, uint32_t addr) {
|
|||||||
ethernet_init();
|
ethernet_init();
|
||||||
offset_print("Ethernet subsystem initialized\n");
|
offset_print("Ethernet subsystem initialized\n");
|
||||||
|
|
||||||
|
ipv4_init();
|
||||||
|
offset_print("IPv4 stack initialized\n");
|
||||||
|
|
||||||
init_drivers();
|
init_drivers();
|
||||||
EARLY_PRINT("DRV ");
|
EARLY_PRINT("DRV ");
|
||||||
offset_print("Drivers initialized\n");
|
offset_print("Drivers initialized\n");
|
||||||
|
|||||||
Reference in New Issue
Block a user