/** * @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 - 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// */ 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 \n"); return 1; } if (getenv("ARG3", ip, sizeof(ip)) < 0) { puts("Usage: ip set \n"); return 1; } if (getenv("ARG4", netmask, sizeof(netmask)) < 0) { puts("Usage: ip set \n"); return 1; } if (getenv("ARG5", gateway, sizeof(gateway)) < 0) { puts("Usage: ip set \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 ]\n"); return 1; }