Implement ARP subsystem and arp app (AI)

- Created src/arp.h: ARP packet struct, cache entry struct, operation codes,
  lookup/request/resolve/receive API, sysfs registration
- Created src/arp.c: ARP cache with 32 entries, request/reply handling,
  ARP response to incoming requests for our IP, sysfs /sys/arp/table
  with formatted IP/MAC/interface/state columns
- Created apps/arp/arp.c: reads and displays /sys/arp/table
- Kernel calls arp_init() at boot, registered sysfs 'arp' namespace
- Tested: clean boot, ARP initialized, arp app in CPIO
This commit is contained in:
AI
2026-02-24 07:31:45 +00:00
parent 1825448528
commit d7d7e8e58e
7 changed files with 571 additions and 7 deletions

View File

@@ -72,7 +72,7 @@ Once a task is completed, it should be checked off.
- [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 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`
- [x] 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.
- [ ] Implement a simple version of `ftp` and `wget`.

30
apps/arp/arp.c Normal file
View File

@@ -0,0 +1,30 @@
/**
* @file arp.c
* @brief Display the ARP table.
*
* Reads the ARP cache from /sys/arp/table and displays it.
*
* Usage:
* arp - Show the ARP table
*/
#include "syscalls.h"
int main(void) {
/* Open the ARP table sysfs file */
int32_t fd = open("/sys/arp/table", 0);
if (fd < 0) {
puts("arp: failed to open /sys/arp/table\n");
return 1;
}
/* Read and display contents */
char buf[512];
int32_t n;
while ((n = read(fd, buf, sizeof(buf))) > 0) {
write(1, buf, (uint32_t)n);
}
close(fd);
return 0;
}

View File

@@ -2,6 +2,8 @@
-- Generating done (0.2s)
-- Build files have been written to: /workspaces/claude-os/build
[ 2%] Building user-mode applications
Building app: arp
Built: /workspaces/claude-os/build/apps_bin/arp (214 bytes)
Building app: cat
Built: /workspaces/claude-os/build/apps_bin/cat (310 bytes)
Building app: diskpart
@@ -35,10 +37,10 @@ Building app: sh
Built: /workspaces/claude-os/build/apps_bin/sh (3428 bytes)
[ 2%] Built target apps
[ 5%] Generating CPIO initial ramdisk
Generated initrd: 24100 bytes
Generated initrd: 24432 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
[ 10%] Building C object src/CMakeFiles/kernel.dir/arp.c.o
[ 13%] Linking C executable ../bin/kernel
[ 97%] Built target kernel
[100%] Generating bootable ISO image
@@ -48,14 +50,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.JjepBC'
Added to ISO image: directory '/'='/tmp/grub.OdkOfh'
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 : 0.27% done
ISO image produced: 5974 sectors
Written to medium : 5974 sectors at LBA 0
xorriso : UPDATE : 63.88% done
ISO image produced: 5986 sectors
Written to medium : 5986 sectors at LBA 0
Writing to 'stdio:/workspaces/claude-os/release/claude-os.iso' completed successfully.
[100%] Built target iso

View File

@@ -29,6 +29,7 @@ add_executable(kernel
e3c509.c
ethernet.c
ipv4.c
arp.c
env.c
keyboard.c
interrupts.S

382
src/arp.c Normal file
View File

@@ -0,0 +1,382 @@
/**
* @file arp.c
* @brief Address Resolution Protocol (ARP) implementation.
*
* Maintains an ARP cache mapping IPv4 addresses to Ethernet MAC
* addresses. Sends ARP requests and processes ARP replies.
* Also responds to incoming ARP requests for our own IP addresses.
*
* The ARP table is exposed via sysfs at /sys/arp.
*/
#include "arp.h"
#include "ethernet.h"
#include "ipv4.h"
#include "sysfs.h"
#include <string.h>
/* Debug print helpers */
extern void offset_print(const char *str);
extern void print_hex(uint32_t val);
/* ================================================================
* Global state
* ================================================================ */
/** ARP cache table. */
static arp_entry_t arp_table[ARP_TABLE_SIZE];
/** Number of active entries. */
static uint32_t arp_count = 0;
/* ================================================================
* ARP cache management
* ================================================================ */
/**
* Find an ARP entry by IP address.
* @return Pointer to entry, or NULL if not found.
*/
static arp_entry_t *arp_find(uint32_t ip) {
for (uint32_t i = 0; i < ARP_TABLE_SIZE; i++) {
if (arp_table[i].state != ARP_STATE_FREE &&
arp_table[i].ip_addr == ip) {
return &arp_table[i];
}
}
return NULL;
}
/**
* Allocate a new ARP entry.
* Reuses a free slot, or evicts the oldest entry.
*/
static arp_entry_t *arp_alloc(void) {
/* Look for a free slot */
for (uint32_t i = 0; i < ARP_TABLE_SIZE; i++) {
if (arp_table[i].state == ARP_STATE_FREE) {
return &arp_table[i];
}
}
/* Evict the oldest entry (lowest timestamp) */
arp_entry_t *oldest = &arp_table[0];
for (uint32_t i = 1; i < ARP_TABLE_SIZE; i++) {
if (arp_table[i].timestamp < oldest->timestamp) {
oldest = &arp_table[i];
}
}
return oldest;
}
/* ================================================================
* ARP send
* ================================================================ */
/**
* Send an ARP packet.
*/
static int arp_send_packet(uint32_t iface_idx, uint16_t operation,
const uint8_t *target_mac, uint32_t target_ip) {
eth_iface_t *iface = ethernet_get_iface(iface_idx);
if (!iface || !iface->active) return -1;
arp_packet_t pkt;
pkt.hw_type = htons(ARP_HW_ETHER);
pkt.proto_type = htons(ETHERTYPE_IPV4);
pkt.hw_len = 6;
pkt.proto_len = 4;
pkt.operation = htons(operation);
/* Sender: our MAC and IP */
memcpy(pkt.sender_mac, iface->mac, 6);
pkt.sender_ip = htonl(iface->ip_addr);
/* Target */
memcpy(pkt.target_mac, target_mac, 6);
pkt.target_ip = htonl(target_ip);
/* For ARP requests, broadcast; for replies, send directly */
const uint8_t *dst_mac;
if (operation == ARP_OP_REQUEST) {
static const uint8_t bcast[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
dst_mac = bcast;
} else {
dst_mac = target_mac;
}
return ethernet_send(iface, dst_mac, ETHERTYPE_ARP,
&pkt, ARP_PACKET_SIZE);
}
/* ================================================================
* Public API
* ================================================================ */
int arp_lookup(uint32_t ip, uint8_t *mac) {
arp_entry_t *entry = arp_find(ip);
if (entry && entry->state == ARP_STATE_RESOLVED) {
memcpy(mac, entry->mac, 6);
return 0;
}
return -1;
}
int arp_request(uint32_t iface_idx, uint32_t target_ip) {
static const uint8_t zero_mac[6] = {0, 0, 0, 0, 0, 0};
/* Create an incomplete entry if we don't have one */
arp_entry_t *entry = arp_find(target_ip);
if (!entry) {
entry = arp_alloc();
memset(entry, 0, sizeof(arp_entry_t));
entry->ip_addr = target_ip;
entry->state = ARP_STATE_INCOMPLETE;
entry->iface_idx = (uint8_t)iface_idx;
entry->timestamp = 0; /* will be updated on reply */
arp_count++;
}
return arp_send_packet(iface_idx, ARP_OP_REQUEST, zero_mac, target_ip);
}
int arp_resolve(uint32_t iface_idx, uint32_t ip, uint8_t *mac) {
/* Broadcast address — no ARP needed */
if (ip == 0xFFFFFFFF) {
memset(mac, 0xFF, 6);
return 0;
}
/* Check cache */
if (arp_lookup(ip, mac) == 0) return 0;
/* Send ARP request */
arp_request(iface_idx, ip);
return -1;
}
void arp_receive(const void *data, uint32_t len, uint32_t iface_idx) {
if (len < ARP_PACKET_SIZE) return;
const arp_packet_t *pkt = (const arp_packet_t *)data;
/* Validate: must be Ethernet/IPv4 */
if (ntohs(pkt->hw_type) != ARP_HW_ETHER) return;
if (ntohs(pkt->proto_type) != ETHERTYPE_IPV4) return;
if (pkt->hw_len != 6 || pkt->proto_len != 4) return;
uint32_t sender_ip = ntohl(pkt->sender_ip);
uint32_t target_ip = ntohl(pkt->target_ip);
uint16_t operation = ntohs(pkt->operation);
/* Update or create ARP cache entry for sender */
arp_entry_t *entry = arp_find(sender_ip);
if (entry) {
memcpy(entry->mac, pkt->sender_mac, 6);
entry->state = ARP_STATE_RESOLVED;
entry->iface_idx = (uint8_t)iface_idx;
} else {
entry = arp_alloc();
entry->ip_addr = sender_ip;
memcpy(entry->mac, pkt->sender_mac, 6);
entry->state = ARP_STATE_RESOLVED;
entry->iface_idx = (uint8_t)iface_idx;
entry->timestamp = 0;
arp_count++;
}
/* Check if this ARP is targeted at us */
eth_iface_t *iface = ethernet_get_iface(iface_idx);
if (!iface || iface->ip_addr == 0) return;
if (target_ip == iface->ip_addr && operation == ARP_OP_REQUEST) {
/* Send ARP reply */
arp_send_packet(iface_idx, ARP_OP_REPLY,
pkt->sender_mac, sender_ip);
}
}
int arp_add_static(uint32_t ip, const uint8_t *mac, uint32_t iface_idx) {
arp_entry_t *entry = arp_find(ip);
if (!entry) {
entry = arp_alloc();
if (!entry) return -1;
arp_count++;
}
entry->ip_addr = ip;
memcpy(entry->mac, mac, 6);
entry->state = ARP_STATE_RESOLVED;
entry->iface_idx = (uint8_t)iface_idx;
entry->timestamp = 0;
return 0;
}
const arp_entry_t *arp_get_entry(uint32_t index) {
uint32_t count = 0;
for (uint32_t i = 0; i < ARP_TABLE_SIZE; i++) {
if (arp_table[i].state == ARP_STATE_FREE) continue;
if (count == index) return &arp_table[i];
count++;
}
return NULL;
}
uint32_t arp_get_count(void) {
return arp_count;
}
/* ================================================================
* Sysfs interface: /sys/arp
*
* Layout:
* /sys/arp/
* table - ARP table in human-readable format
* ================================================================ */
/**
* Format a MAC address as "XX:XX:XX:XX:XX:XX".
*/
static void mac_to_str(const uint8_t *mac, char *buf) {
static const char hex[] = "0123456789ABCDEF";
int pos = 0;
for (int i = 0; i < 6; i++) {
if (i > 0) buf[pos++] = ':';
buf[pos++] = hex[(mac[i] >> 4) & 0xF];
buf[pos++] = hex[mac[i] & 0xF];
}
buf[pos] = '\0';
}
/**
* Format an IPv4 address (host byte order) as dotted decimal.
*/
static int ip_to_str(uint32_t ip, char *buf, uint32_t size) {
int pos = 0;
for (int i = 0; i < 4; i++) {
if (i > 0 && pos < (int)size - 1) buf[pos++] = '.';
uint8_t octet = (uint8_t)((ip >> (24 - i * 8)) & 0xFF);
if (octet >= 100 && pos < (int)size - 3) {
buf[pos++] = (char)('0' + octet / 100);
buf[pos++] = (char)('0' + (octet % 100) / 10);
buf[pos++] = (char)('0' + octet % 10);
} else if (octet >= 10 && pos < (int)size - 2) {
buf[pos++] = (char)('0' + octet / 10);
buf[pos++] = (char)('0' + octet % 10);
} else if (pos < (int)size - 1) {
buf[pos++] = (char)('0' + octet);
}
}
if (pos < (int)size) buf[pos] = '\0';
return pos;
}
/**
* Append a string to buf at position *pos.
*/
static void append_str(char *buf, uint32_t size, int *pos, const char *s) {
while (*s && *pos < (int)size - 1) buf[(*pos)++] = *s++;
}
static int arp_sysfs_list(void *ctx, const char *path, uint32_t idx,
sysfs_entry_t *out) {
(void)ctx;
if (path[0] == '\0') {
if (idx == 0) {
memset(out, 0, sizeof(sysfs_entry_t));
strncpy(out->name, "table", SYSFS_MAX_NAME - 1);
out->is_dir = 0;
return 0;
}
return -1;
}
return -1;
}
static int arp_sysfs_read(void *ctx, const char *path, char *buf,
uint32_t buf_size) {
(void)ctx;
if (strcmp(path, "table") != 0) return -1;
int pos = 0;
/* Header */
append_str(buf, buf_size, &pos, "IP Address MAC Address Iface State\n");
for (uint32_t i = 0; i < ARP_TABLE_SIZE; i++) {
if (arp_table[i].state == ARP_STATE_FREE) continue;
/* IP address */
char ip_buf[16];
ip_to_str(arp_table[i].ip_addr, ip_buf, sizeof(ip_buf));
append_str(buf, buf_size, &pos, ip_buf);
/* Pad to column 17 */
int ip_len = (int)strlen(ip_buf);
for (int p = ip_len; p < 17; p++) {
if (pos < (int)buf_size - 1) buf[pos++] = ' ';
}
/* MAC */
char mac_buf[18];
mac_to_str(arp_table[i].mac, mac_buf);
append_str(buf, buf_size, &pos, mac_buf);
if (pos < (int)buf_size - 1) buf[pos++] = ' ';
if (pos < (int)buf_size - 1) buf[pos++] = ' ';
/* Interface */
eth_iface_t *iface = ethernet_get_iface(arp_table[i].iface_idx);
if (iface) {
append_str(buf, buf_size, &pos, iface->name);
} else {
append_str(buf, buf_size, &pos, "?");
}
/* Pad and state */
int name_len = iface ? (int)strlen(iface->name) : 1;
for (int p = name_len; p < 7; p++) {
if (pos < (int)buf_size - 1) buf[pos++] = ' ';
}
if (arp_table[i].state == ARP_STATE_RESOLVED) {
append_str(buf, buf_size, &pos, "resolved");
} else {
append_str(buf, buf_size, &pos, "incomplete");
}
if (pos < (int)buf_size - 1) buf[pos++] = '\n';
}
buf[pos] = '\0';
return pos;
}
static int arp_sysfs_write(void *ctx, const char *path, const char *buf,
uint32_t size) {
(void)ctx;
(void)path;
(void)buf;
(void)size;
return -1; /* Read-only for now */
}
static sysfs_ops_t arp_sysfs_ops = {
.list = arp_sysfs_list,
.read = arp_sysfs_read,
.write = arp_sysfs_write,
};
/* ================================================================
* Initialization
* ================================================================ */
void arp_init(void) {
memset(arp_table, 0, sizeof(arp_table));
arp_count = 0;
sysfs_register("arp", &arp_sysfs_ops, NULL);
offset_print(" ARP: initialized\n");
}

145
src/arp.h Normal file
View File

@@ -0,0 +1,145 @@
/**
* @file arp.h
* @brief Address Resolution Protocol (ARP) subsystem.
*
* Implements ARP (RFC 826) for mapping IPv4 addresses to Ethernet
* MAC addresses. Maintains an ARP cache and handles ARP requests
* and replies.
*
* The ARP table is exposed via sysfs at /sys/arp for userspace tools.
*/
#ifndef ARP_H
#define ARP_H
#include <stdint.h>
/** Maximum number of ARP cache entries. */
#define ARP_TABLE_SIZE 32
/** ARP hardware type: Ethernet */
#define ARP_HW_ETHER 1
/** ARP operation codes */
#define ARP_OP_REQUEST 1
#define ARP_OP_REPLY 2
/** ARP cache entry states */
#define ARP_STATE_FREE 0 /**< Unused slot. */
#define ARP_STATE_INCOMPLETE 1 /**< Request sent, awaiting reply. */
#define ARP_STATE_RESOLVED 2 /**< MAC address known. */
/* ================================================================
* ARP packet (28 bytes for IPv4-over-Ethernet)
* ================================================================ */
/**
* ARP packet structure for IPv4 over Ethernet.
* All multi-byte fields are in network byte order.
*/
typedef struct __attribute__((packed)) arp_packet {
uint16_t hw_type; /**< Hardware type (1 = Ethernet). */
uint16_t proto_type; /**< Protocol type (0x0800 = IPv4). */
uint8_t hw_len; /**< Hardware address length (6). */
uint8_t proto_len; /**< Protocol address length (4). */
uint16_t operation; /**< Operation (1=request, 2=reply). */
uint8_t sender_mac[6]; /**< Sender hardware address. */
uint32_t sender_ip; /**< Sender protocol address. */
uint8_t target_mac[6]; /**< Target hardware address. */
uint32_t target_ip; /**< Target protocol address. */
} arp_packet_t;
/** ARP packet size. */
#define ARP_PACKET_SIZE 28
/* ================================================================
* ARP cache entry
* ================================================================ */
/**
* ARP cache entry.
*/
typedef struct arp_entry {
uint32_t ip_addr; /**< IPv4 address (host byte order). */
uint8_t mac[6]; /**< Resolved MAC address. */
uint8_t state; /**< ARP_STATE_*. */
uint8_t iface_idx; /**< Ethernet interface index. */
uint32_t timestamp; /**< Time when entry was created (tick count). */
} arp_entry_t;
/* ================================================================
* Public API
* ================================================================ */
/**
* Initialize the ARP subsystem.
* Registers sysfs namespace "arp".
*/
void arp_init(void);
/**
* Look up an IP address in the ARP cache.
*
* @param ip IPv4 address (host byte order).
* @param mac Output: 6-byte MAC address if found.
* @return 0 if found and resolved, -1 if not in cache or incomplete.
*/
int arp_lookup(uint32_t ip, uint8_t *mac);
/**
* Send an ARP request for the given IP address.
*
* @param iface_idx Ethernet interface to send on.
* @param target_ip IP address to resolve (host byte order).
* @return 0 on success, -1 on failure.
*/
int arp_request(uint32_t iface_idx, uint32_t target_ip);
/**
* Resolve an IP address to a MAC address.
*
* Checks the ARP cache first. If not found, sends an ARP request
* and returns -1 (caller should retry later).
*
* @param iface_idx Ethernet interface index.
* @param ip IPv4 address (host byte order).
* @param mac Output: 6-byte MAC address.
* @return 0 if resolved, -1 if pending.
*/
int arp_resolve(uint32_t iface_idx, uint32_t ip, uint8_t *mac);
/**
* Process an incoming ARP packet.
*
* Called by the Ethernet subsystem when an ARP frame is received.
*
* @param data Raw ARP packet.
* @param len Packet length.
* @param iface_idx Interface the packet arrived on.
*/
void arp_receive(const void *data, uint32_t len, uint32_t iface_idx);
/**
* Add a static ARP entry.
*
* @param ip IPv4 address (host byte order).
* @param mac 6-byte MAC address.
* @param iface_idx Ethernet interface index.
* @return 0 on success, -1 if table full.
*/
int arp_add_static(uint32_t ip, const uint8_t *mac, uint32_t iface_idx);
/**
* Get an ARP table entry by index (for enumeration).
*
* @param index 0-based index.
* @return Pointer to entry, or NULL if out of range.
*/
const arp_entry_t *arp_get_entry(uint32_t index);
/**
* Get the number of active ARP entries.
*/
uint32_t arp_get_count(void);
#endif /* ARP_H */

View File

@@ -24,6 +24,7 @@
#include "keyboard.h"
#include "ethernet.h"
#include "ipv4.h"
#include "arp.h"
#include "framebuffer.h"
/* Global framebuffer info, parsed from multiboot2 tags. */
@@ -423,6 +424,9 @@ void kernel_main(uint32_t magic, uint32_t addr) {
ipv4_init();
offset_print("IPv4 stack initialized\n");
arp_init();
offset_print("ARP subsystem initialized\n");
init_drivers();
EARLY_PRINT("DRV ");
offset_print("Drivers initialized\n");