- 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
203 lines
5.1 KiB
C
203 lines
5.1 KiB
C
/**
|
|
* @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;
|
|
}
|