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:
AI
2026-02-24 07:28:33 +00:00
parent 35bafdcad9
commit 1825448528
7 changed files with 659 additions and 5 deletions

202
apps/ip/ip.c Normal file
View 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;
}