summaryrefslogtreecommitdiffstats
path: root/usr/kinit/ipconfig/main.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:06:04 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:06:04 +0000
commit2f0649f6fe411d7e07c8d56cf8ea56db53536da8 (patch)
tree778611fb52176dce1ad06c68e87b2cb348ca0f7b /usr/kinit/ipconfig/main.c
parentInitial commit. (diff)
downloadklibc-upstream/2.0.13.tar.xz
klibc-upstream/2.0.13.zip
Adding upstream version 2.0.13.upstream/2.0.13upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'usr/kinit/ipconfig/main.c')
-rw-r--r--usr/kinit/ipconfig/main.c924
1 files changed, 924 insertions, 0 deletions
diff --git a/usr/kinit/ipconfig/main.c b/usr/kinit/ipconfig/main.c
new file mode 100644
index 0000000..64c5398
--- /dev/null
+++ b/usr/kinit/ipconfig/main.c
@@ -0,0 +1,924 @@
+#include <poll.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/sysinfo.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h> /* for getopts */
+
+#include <net/if_arp.h>
+
+#include "ipconfig.h"
+#include "netdev.h"
+#include "bootp_packet.h"
+#include "bootp_proto.h"
+#include "dhcp_proto.h"
+#include "packet.h"
+
+static const char sysfs_class_net[] = "/sys/class/net";
+static const char *progname;
+static jmp_buf abort_buf;
+static char do_not_config;
+static unsigned int default_caps = CAP_DHCP | CAP_BOOTP | CAP_RARP;
+static int loop_timeout = -1;
+static int configured;
+static int bringup_first = 0;
+static int n_devices = 0;
+
+/* DHCP vendor class identifier */
+char vendor_class_identifier[260];
+int vendor_class_identifier_len;
+
+struct state {
+ int state;
+ int restart_state;
+ time_t expire;
+ int retry_period;
+
+ struct netdev *dev;
+ struct state *next;
+};
+
+/* #define PROTO_x : for uint8_t proto of struct netdev */
+struct protoinfo {
+ char *name;
+} protoinfos[] = {
+#define PROTO_NONE 0
+ {"none"},
+#define PROTO_BOOTP 1
+ {"bootp"},
+#define PROTO_DHCP 2
+ {"dhcp"},
+#define PROTO_RARP 3
+ {"rarp"}
+};
+
+static inline const char *my_inet_ntoa(uint32_t addr)
+{
+ struct in_addr a;
+
+ a.s_addr = addr;
+
+ return inet_ntoa(a);
+}
+
+static void print_device_config(struct netdev *dev)
+{
+ int dns0_spaces;
+ int dns1_spaces;
+ printf("IP-Config: %s complete", dev->name);
+ if (dev->proto == PROTO_BOOTP || dev->proto == PROTO_DHCP)
+ printf(" (%s from %s)", protoinfos[dev->proto].name,
+ my_inet_ntoa(dev->serverid ?
+ dev->serverid : dev->ip_server));
+
+ printf(":\n address: %-16s ", my_inet_ntoa(dev->ip_addr));
+ printf("broadcast: %-16s ", my_inet_ntoa(dev->ip_broadcast));
+ printf("netmask: %-16s\n", my_inet_ntoa(dev->ip_netmask));
+ if (dev->routes != NULL) {
+ struct route *cur;
+ char *delim = "";
+ printf(" routes :");
+ for (cur = dev->routes; cur != NULL; cur = cur->next) {
+ printf("%s %s/%u", delim, my_inet_ntoa(cur->subnet), cur->netmask_width);
+ if (cur->gateway != 0) {
+ printf(" via %s", my_inet_ntoa(cur->gateway));
+ }
+ delim = ",";
+ }
+ printf("\n");
+ dns0_spaces = 3;
+ dns1_spaces = 5;
+ } else {
+ printf(" gateway: %-16s", my_inet_ntoa(dev->ip_gateway));
+ dns0_spaces = 5;
+ dns1_spaces = 3;
+ }
+ printf(" dns0%*c: %-16s", dns0_spaces, ' ', my_inet_ntoa(dev->ip_nameserver[0]));
+ printf(" dns1%*c: %-16s\n", dns1_spaces, ' ', my_inet_ntoa(dev->ip_nameserver[1]));
+ if (dev->hostname[0])
+ printf(" host : %-64s\n", dev->hostname);
+ if (dev->dnsdomainname[0])
+ printf(" domain : %-64s\n", dev->dnsdomainname);
+ if (dev->nisdomainname[0])
+ printf(" nisdomain: %-64s\n", dev->nisdomainname);
+ printf(" rootserver: %s ", my_inet_ntoa(dev->ip_server));
+ printf("rootpath: %s\n", dev->bootpath);
+ printf(" filename : %s\n", dev->filename);
+}
+
+static void configure_device(struct netdev *dev)
+{
+ if (do_not_config)
+ return;
+
+ if (netdev_setmtu(dev))
+ printf("IP-Config: failed to set MTU on %s to %u\n",
+ dev->name, dev->mtu);
+
+ if (netdev_setaddress(dev))
+ printf("IP-Config: failed to set addresses on %s\n",
+ dev->name);
+ if (netdev_setroutes(dev))
+ printf("IP-Config: failed to set routes on %s\n",
+ dev->name);
+ if (dev->hostname[0] &&
+ sethostname(dev->hostname, strlen(dev->hostname)))
+ printf("IP-Config: failed to set hostname '%s' from %s\n",
+ dev->hostname, dev->name);
+}
+
+/*
+ * Escape shell varialbes in git style:
+ * Always start with a single quote ('), then leave all characters
+ * except ' and ! unchanged.
+ */
+static void write_option(FILE *f, const char *name, const char *chr)
+{
+
+ fprintf(f, "%s='", name);
+ while (*chr) {
+ switch (*chr) {
+ case '!':
+ case '\'':
+ fprintf(f, "'\\%c'", *chr);
+ break;
+ default:
+ fprintf(f, "%c", *chr);
+ break;
+ }
+ ++chr;
+ }
+ fprintf(f, "'\n");
+}
+
+static void dump_device_config(struct netdev *dev)
+{
+ char fn[40];
+ FILE *f;
+ /*
+ * char UINT64_MAX[] = "18446744073709551615";
+ * sizeof(UINT64_MAX)==21
+ */
+ char buf21[21];
+ const char path[] = "/run/";
+
+ snprintf(fn, sizeof(fn), "%snet-%s.conf", path, dev->name);
+ f = fopen(fn, "w");
+ if (f) {
+ write_option(f, "DEVICE", dev->name);
+ write_option(f, "PROTO", protoinfos[dev->proto].name);
+ write_option(f, "IPV4ADDR",
+ my_inet_ntoa(dev->ip_addr));
+ write_option(f, "IPV4BROADCAST",
+ my_inet_ntoa(dev->ip_broadcast));
+ write_option(f, "IPV4NETMASK",
+ my_inet_ntoa(dev->ip_netmask));
+ if (dev->routes != NULL) {
+ /* Use 6 digits to encode the index */
+ char key[23];
+ char value[19];
+ int i = 0;
+ struct route *cur;
+ for (cur = dev->routes; cur != NULL; cur = cur->next) {
+ snprintf(key, sizeof(key), "IPV4ROUTE%iSUBNET", i);
+ snprintf(value, sizeof(value), "%s/%u", my_inet_ntoa(cur->subnet), cur->netmask_width);
+ write_option(f, key, value);
+ snprintf(key, sizeof(key), "IPV4ROUTE%iGATEWAY", i);
+ write_option(f, key, my_inet_ntoa(cur->gateway));
+ i++;
+ }
+ } else {
+ write_option(f, "IPV4GATEWAY",
+ my_inet_ntoa(dev->ip_gateway));
+ }
+ write_option(f, "IPV4DNS0",
+ my_inet_ntoa(dev->ip_nameserver[0]));
+ write_option(f, "IPV4DNS1",
+ my_inet_ntoa(dev->ip_nameserver[1]));
+ write_option(f, "HOSTNAME", dev->hostname);
+ write_option(f, "DNSDOMAIN", dev->dnsdomainname);
+ write_option(f, "NISDOMAIN", dev->nisdomainname);
+ write_option(f, "ROOTSERVER",
+ my_inet_ntoa(dev->ip_server));
+ write_option(f, "ROOTPATH", dev->bootpath);
+ write_option(f, "filename", dev->filename);
+ sprintf(buf21, "%ld", (long)dev->uptime);
+ write_option(f, "UPTIME", buf21);
+ sprintf(buf21, "%u", (unsigned int)dev->dhcpleasetime);
+ write_option(f, "DHCPLEASETIME", buf21);
+ write_option(f, "DOMAINSEARCH", dev->domainsearch == NULL ?
+ "" : dev->domainsearch);
+ fclose(f);
+ }
+}
+
+static uint32_t inet_class_netmask(uint32_t ip)
+{
+ ip = ntohl(ip);
+ if (IN_CLASSA(ip))
+ return htonl(IN_CLASSA_NET);
+ if (IN_CLASSB(ip))
+ return htonl(IN_CLASSB_NET);
+ if (IN_CLASSC(ip))
+ return htonl(IN_CLASSC_NET);
+ return INADDR_ANY;
+}
+
+static void postprocess_device(struct netdev *dev)
+{
+ if (dev->ip_netmask == INADDR_ANY) {
+ dev->ip_netmask = inet_class_netmask(dev->ip_addr);
+ printf("IP-Config: %s guessed netmask %s\n",
+ dev->name, my_inet_ntoa(dev->ip_netmask));
+ }
+ if (dev->ip_broadcast == INADDR_ANY) {
+ dev->ip_broadcast =
+ (dev->ip_addr & dev->ip_netmask) | ~dev->ip_netmask;
+ printf("IP-Config: %s guessed broadcast address %s\n",
+ dev->name, my_inet_ntoa(dev->ip_broadcast));
+ }
+}
+
+static void complete_device(struct netdev *dev)
+{
+ struct sysinfo info;
+
+ if (!sysinfo(&info))
+ dev->uptime = info.uptime;
+ postprocess_device(dev);
+ configure_device(dev);
+ dump_device_config(dev);
+ print_device_config(dev);
+ packet_close(dev);
+
+ ++configured;
+
+ dev->next = ifaces;
+ ifaces = dev;
+}
+
+/*
+ * Returns:
+ * 0 = Not handled, try again later
+ * 1 = Handled
+ */
+static int process_receive_event(struct state *s, time_t now)
+{
+ int handled = 1;
+
+ switch (s->state) {
+ case DEVST_ERROR:
+ return 0; /* Not handled */
+ case DEVST_COMPLETE:
+ return 0; /* Not handled as already configured */
+
+ case DEVST_BOOTP:
+ s->restart_state = DEVST_BOOTP;
+ switch (bootp_recv_reply(s->dev)) {
+ case -1:
+ s->state = DEVST_ERROR;
+ break;
+ case 0:
+ handled = 0;
+ break;
+ case 1:
+ s->state = DEVST_COMPLETE;
+ s->dev->proto = PROTO_BOOTP;
+ dprintf("\n bootp reply\n");
+ break;
+ }
+ break;
+
+ case DEVST_DHCPDISC:
+ s->restart_state = DEVST_DHCPDISC;
+ switch (dhcp_recv_offer(s->dev)) {
+ case -1:
+ s->state = DEVST_ERROR;
+ break;
+ case 0:
+ handled = 0;
+ break;
+ case DHCPOFFER: /* Offer received */
+ s->state = DEVST_DHCPREQ;
+ dhcp_send_request(s->dev);
+ break;
+ }
+ break;
+
+ case DEVST_DHCPREQ:
+ s->restart_state = DEVST_DHCPDISC;
+ switch (dhcp_recv_ack(s->dev)) {
+ case -1: /* error */
+ s->state = DEVST_ERROR;
+ break;
+ case 0:
+ handled = 0;
+ break;
+ case DHCPACK: /* ACK received */
+ s->state = DEVST_COMPLETE;
+ s->dev->proto = PROTO_DHCP;
+ break;
+ case DHCPNAK: /* NAK received */
+ s->state = DEVST_DHCPDISC;
+ break;
+ }
+ break;
+
+ default:
+ dprintf("\n");
+ handled = 0;
+ break;
+ }
+
+ switch (s->state) {
+ case DEVST_COMPLETE:
+ complete_device(s->dev);
+ break;
+
+ case DEVST_ERROR:
+ /* error occurred, try again in 10 seconds */
+ s->expire = now + 10;
+ break;
+ }
+
+ return handled;
+}
+
+static void process_timeout_event(struct state *s, time_t now)
+{
+ int ret = 0;
+
+ /*
+ * If we had an error, restore a sane state to
+ * restart from.
+ */
+ if (s->state == DEVST_ERROR)
+ s->state = s->restart_state;
+
+ /*
+ * Now send a packet depending on our state.
+ */
+ switch (s->state) {
+ case DEVST_BOOTP:
+ ret = bootp_send_request(s->dev);
+ s->restart_state = DEVST_BOOTP;
+ break;
+
+ case DEVST_DHCPDISC:
+ ret = dhcp_send_discover(s->dev);
+ s->restart_state = DEVST_DHCPDISC;
+ break;
+
+ case DEVST_DHCPREQ:
+ ret = dhcp_send_request(s->dev);
+ s->restart_state = DEVST_DHCPDISC;
+ break;
+ }
+
+ if (ret == -1) {
+ s->state = DEVST_ERROR;
+ s->expire = now + 1;
+ } else {
+ s->expire = now + s->retry_period;
+
+ s->retry_period *= 2;
+ if (s->retry_period > 60)
+ s->retry_period = 60;
+ }
+}
+
+static void process_error_event(struct state *s, time_t now)
+{
+ s->state = DEVST_ERROR;
+ s->expire = now + 1;
+}
+
+static struct state *slist;
+struct netdev *ifaces;
+
+/*
+ * Returns:
+ * 0 = No dhcp/bootp packet was received
+ * 1 = A packet was received and handled
+ */
+static int do_pkt_recv(int nr, struct pollfd *fds, time_t now)
+{
+ int i, ret = 0;
+ struct state *s;
+
+ for (i = 0, s = slist; s && nr; s = s->next) {
+ if (s->dev->pkt_fd != fds[i].fd)
+ continue;
+ if (fds[i].revents) {
+ if (fds[i].revents & POLLRDNORM)
+ ret |= process_receive_event(s, now);
+ else
+ process_error_event(s, now);
+ nr--;
+ }
+ i++;
+ }
+ return ret;
+}
+
+static int loop(void)
+{
+ struct pollfd *fds;
+ struct state *s;
+ int i, nr = 0, rc = 0;
+ struct timeval now, prev;
+ time_t start;
+
+ fds = malloc(sizeof(struct pollfd) * n_devices);
+ if (!fds) {
+ fprintf(stderr, "malloc failed\n");
+ rc = -1;
+ goto bail;
+ }
+
+ memset(fds, 0, sizeof(*fds));
+
+ gettimeofday(&now, NULL);
+ start = now.tv_sec;
+ while (1) {
+ int timeout = 60;
+ int pending = 0;
+ int done = 0;
+ int timeout_ms;
+ int x;
+
+ for (i = 0, s = slist; s; s = s->next) {
+ dprintf("%s: state = %d\n", s->dev->name, s->state);
+
+ if (s->state == DEVST_COMPLETE) {
+ done++;
+ continue;
+ }
+
+ pending++;
+
+ if (s->expire - now.tv_sec <= 0) {
+ dprintf("timeout\n");
+ process_timeout_event(s, now.tv_sec);
+ }
+
+ if (s->state != DEVST_ERROR) {
+ fds[i].fd = s->dev->pkt_fd;
+ fds[i].events = POLLRDNORM;
+ i++;
+ }
+
+ if (timeout > s->expire - now.tv_sec)
+ timeout = s->expire - now.tv_sec;
+ }
+
+ if (pending == 0 || (bringup_first && done))
+ break;
+
+ timeout_ms = timeout * 1000;
+
+ for (x = 0; x < 2; x++) {
+ int delta_ms;
+
+ if (timeout_ms <= 0)
+ timeout_ms = 100;
+
+ nr = poll(fds, i, timeout_ms);
+ prev = now;
+ gettimeofday(&now, NULL);
+
+ if ((nr > 0) && do_pkt_recv(nr, fds, now.tv_sec))
+ break;
+
+ if (loop_timeout >= 0 &&
+ now.tv_sec - start >= loop_timeout) {
+ printf("IP-Config: no response after %d "
+ "secs - giving up\n", loop_timeout);
+ rc = -1;
+ goto bail;
+ }
+
+ delta_ms = (now.tv_sec - prev.tv_sec) * 1000;
+ delta_ms += (now.tv_usec - prev.tv_usec) / 1000;
+
+ dprintf("Delta: %d ms\n", delta_ms);
+
+ timeout_ms -= delta_ms;
+ }
+ }
+bail:
+ if (fds)
+ free(fds);
+ return rc;
+}
+
+static int add_one_dev(struct netdev *dev)
+{
+ struct state *state;
+
+ state = malloc(sizeof(struct state));
+ if (!state)
+ return -1;
+
+ state->dev = dev;
+ state->expire = time(NULL);
+ state->retry_period = 1;
+
+ /*
+ * Select the state that we start from.
+ */
+ if (dev->caps & CAP_DHCP && dev->ip_addr == INADDR_ANY)
+ state->restart_state = state->state = DEVST_DHCPDISC;
+ else if (dev->caps & CAP_DHCP)
+ state->restart_state = state->state = DEVST_DHCPREQ;
+ else if (dev->caps & CAP_BOOTP)
+ state->restart_state = state->state = DEVST_BOOTP;
+
+ state->next = slist;
+ slist = state;
+
+ n_devices++;
+
+ return 0;
+}
+
+static void parse_addr(uint32_t *addr, const char *ip)
+{
+ struct in_addr in;
+ if (inet_aton(ip, &in) == 0) {
+ fprintf(stderr, "%s: can't parse IP address '%s'\n",
+ progname, ip);
+ longjmp(abort_buf, 1);
+ }
+ *addr = in.s_addr;
+}
+
+static unsigned int parse_proto(const char *ip)
+{
+ unsigned int caps = 0;
+
+ if (*ip == '\0' || strcmp(ip, "on") == 0 || strcmp(ip, "any") == 0)
+ caps = CAP_BOOTP | CAP_DHCP | CAP_RARP;
+ else if (strcmp(ip, "both") == 0)
+ caps = CAP_BOOTP | CAP_RARP;
+ else if (strcmp(ip, "dhcp") == 0)
+ caps = CAP_BOOTP | CAP_DHCP;
+ else if (strcmp(ip, "bootp") == 0)
+ caps = CAP_BOOTP;
+ else if (strcmp(ip, "rarp") == 0)
+ caps = CAP_RARP;
+ else if (strcmp(ip, "none") == 0 || strcmp(ip, "static") == 0
+ || strcmp(ip, "off") == 0)
+ goto bail;
+ else {
+ fprintf(stderr, "%s: invalid protocol '%s'\n", progname, ip);
+ longjmp(abort_buf, 1);
+ }
+bail:
+ return caps;
+}
+
+static int add_all_devices(struct netdev *template);
+
+static int parse_device(struct netdev *dev, char *ip)
+{
+ char *cp;
+ int opt;
+ int is_ip = 0;
+
+ dprintf("IP-Config: parse_device: \"%s\"\n", ip);
+
+ if (strncmp(ip, "ip=", 3) == 0) {
+ ip += 3;
+ is_ip = 1;
+ } else if (strncmp(ip, "nfsaddrs=", 9) == 0) {
+ ip += 9;
+ is_ip = 1; /* Not sure about this...? */
+ }
+
+ if (!strchr(ip, ':')) {
+ /* Only one option, e.g. "ip=dhcp", or an interface name */
+ if (is_ip) {
+ dev->caps = parse_proto(ip);
+ bringup_first = 1;
+ } else {
+ dev->name = ip;
+ }
+ } else {
+ for (opt = 0; ip && *ip; ip = cp, opt++) {
+ if ((cp = strchr(ip, ':'))) {
+ *cp++ = '\0';
+ }
+ if (*ip == '\0')
+ continue;
+ dprintf("IP-Config: opt #%d: '%s'\n", opt, ip);
+ switch (opt) {
+ case 0:
+ parse_addr(&dev->ip_addr, ip);
+ dev->caps = 0;
+ break;
+ case 1:
+ parse_addr(&dev->ip_server, ip);
+ break;
+ case 2:
+ parse_addr(&dev->ip_gateway, ip);
+ break;
+ case 3:
+ parse_addr(&dev->ip_netmask, ip);
+ break;
+ case 4:
+ strncpy(dev->hostname, ip, SYS_NMLN - 1);
+ dev->hostname[SYS_NMLN - 1] = '\0';
+ memcpy(dev->reqhostname, dev->hostname,
+ SYS_NMLN);
+ break;
+ case 5:
+ dev->name = ip;
+ break;
+ case 6:
+ dev->caps = parse_proto(ip);
+ break;
+ case 7:
+ parse_addr(&dev->ip_nameserver[0], ip);
+ break;
+ case 8:
+ parse_addr(&dev->ip_nameserver[1], ip);
+ break;
+ case 9:
+ /* NTP server - ignore */
+ break;
+ }
+ }
+ }
+
+ if (dev->name == NULL ||
+ dev->name[0] == '\0' || strcmp(dev->name, "all") == 0) {
+ add_all_devices(dev);
+ bringup_first = 1;
+ return 0;
+ }
+ return 1;
+}
+
+static void bringup_device(struct netdev *dev)
+{
+ if (netdev_up(dev) == 0) {
+ if (dev->caps)
+ add_one_dev(dev);
+ else {
+ dev->proto = PROTO_NONE;
+ complete_device(dev);
+ }
+ }
+}
+
+static void bringup_one_dev(struct netdev *template, struct netdev *dev)
+{
+ if (template->ip_addr != INADDR_NONE)
+ dev->ip_addr = template->ip_addr;
+ if (template->ip_server != INADDR_NONE)
+ dev->ip_server = template->ip_server;
+ if (template->ip_gateway != INADDR_NONE)
+ dev->ip_gateway = template->ip_gateway;
+ if (template->ip_netmask != INADDR_NONE)
+ dev->ip_netmask = template->ip_netmask;
+ if (template->ip_nameserver[0] != INADDR_NONE)
+ dev->ip_nameserver[0] = template->ip_nameserver[0];
+ if (template->ip_nameserver[1] != INADDR_NONE)
+ dev->ip_nameserver[1] = template->ip_nameserver[1];
+ if (template->hostname[0] != '\0')
+ strcpy(dev->hostname, template->hostname);
+ if (template->reqhostname[0] != '\0')
+ strcpy(dev->reqhostname, template->reqhostname);
+ dev->caps &= template->caps;
+
+ bringup_device(dev);
+}
+
+static struct netdev *add_device(char *info)
+{
+ struct netdev *dev;
+ int i;
+
+ dev = malloc(sizeof(struct netdev));
+ if (dev == NULL) {
+ fprintf(stderr, "%s: out of memory\n", progname);
+ longjmp(abort_buf, 1);
+ }
+
+ memset(dev, 0, sizeof(struct netdev));
+ dev->caps = default_caps;
+
+ if (parse_device(dev, info) == 0)
+ goto bail;
+
+ if (netdev_init_if(dev) == -1)
+ goto bail;
+
+ if (bootp_init_if(dev) == -1)
+ goto bail;
+
+ if (packet_open(dev) == -1)
+ goto bail;
+
+ printf("IP-Config: %s hardware address", dev->name);
+ for (i = 0; i < dev->hwlen; i++)
+ printf("%c%02x", i == 0 ? ' ' : ':', dev->hwaddr[i]);
+ printf(" mtu %d%s%s\n", dev->mtu,
+ dev->caps & CAP_DHCP ? " DHCP" :
+ dev->caps & CAP_BOOTP ? " BOOTP" : "",
+ dev->caps & CAP_RARP ? " RARP" : "");
+ return dev;
+bail:
+ free(dev);
+ return NULL;
+}
+
+static int add_all_devices(struct netdev *template)
+{
+ DIR *d;
+ struct dirent *de;
+ struct netdev *dev;
+ char t[PATH_MAX], p[255];
+ int i, fd;
+ unsigned long flags;
+
+ d = opendir(sysfs_class_net);
+ if (!d)
+ return 0;
+
+ while ((de = readdir(d)) != NULL) {
+ /* This excludes devices beginning with dots or "dummy",
+ as well as . or .. */
+ if (de->d_name[0] == '.' || !strcmp(de->d_name, ".."))
+ continue;
+ i = snprintf(t, PATH_MAX - 1, "%s/%s/flags", sysfs_class_net,
+ de->d_name);
+ if (i < 0 || i >= PATH_MAX - 1)
+ continue;
+ t[i] = '\0';
+ fd = open(t, O_RDONLY);
+ if (fd < 0) {
+ perror(t);
+ continue;
+ }
+ i = read(fd, &p, sizeof(p) - 1);
+ close(fd);
+ if (i < 0) {
+ perror(t);
+ continue;
+ }
+ p[i] = '\0';
+ flags = strtoul(p, NULL, 0);
+ /* Heuristic for if this is a reasonable boot interface.
+ This is the same
+ logic the in-kernel ipconfig uses... */
+ if (!(flags & IFF_LOOPBACK) &&
+ (flags & (IFF_BROADCAST | IFF_POINTOPOINT))) {
+ dprintf("Trying to bring up %s\n", de->d_name);
+
+ dev = add_device(de->d_name);
+ if (!dev)
+ continue;
+ bringup_one_dev(template, dev);
+ }
+ }
+ closedir(d);
+ return 1;
+}
+
+static int check_autoconfig(void)
+{
+ int ndev = 0, nauto = 0;
+ struct state *s;
+
+ for (s = slist; s; s = s->next) {
+ ndev++;
+ if (s->dev->caps)
+ nauto++;
+ }
+
+ if (ndev == 0) {
+ if (configured == 0) {
+ fprintf(stderr, "%s: no devices to configure\n",
+ progname);
+ longjmp(abort_buf, 1);
+ }
+ }
+
+ return nauto;
+}
+
+static void set_vendor_identifier(const char *id)
+{
+ int len = strlen(id);
+ if (len >= 255) {
+ fprintf(stderr,
+ "%s: invalid vendor class identifier: "
+ "%s\n", progname, id);
+ longjmp(abort_buf, 1);
+ }
+ memcpy(vendor_class_identifier+2, id, len);
+ vendor_class_identifier[0] = 60;
+ vendor_class_identifier[1] = len;
+ vendor_class_identifier_len = len+2;
+}
+
+int main(int argc, char *argv[])
+ __attribute__ ((weak, alias("ipconfig_main")));
+
+int ipconfig_main(int argc, char *argv[])
+{
+ struct netdev *dev;
+ int c, port;
+ int err = 0;
+
+ /* If progname is set we're invoked from another program */
+ if (!progname) {
+ struct timeval now;
+ progname = argv[0];
+ gettimeofday(&now, NULL);
+ srand48(now.tv_usec ^ (now.tv_sec << 24));
+ }
+
+ if ((err = setjmp(abort_buf)))
+ return err;
+
+ /* Default vendor identifier */
+ set_vendor_identifier("Linux ipconfig");
+
+ do {
+ c = getopt(argc, argv, "c:d:i:onp:t:");
+ if (c == EOF)
+ break;
+
+ switch (c) {
+ case 'c':
+ default_caps = parse_proto(optarg);
+ break;
+ case 'p':
+ port = atoi(optarg);
+ if (port <= 0 || port > USHRT_MAX) {
+ fprintf(stderr,
+ "%s: invalid port number %d\n",
+ progname, port);
+ longjmp(abort_buf, 1);
+ }
+ cfg_local_port = port;
+ cfg_remote_port = cfg_local_port - 1;
+ break;
+ case 't':
+ loop_timeout = atoi(optarg);
+ if (loop_timeout < 0) {
+ fprintf(stderr,
+ "%s: invalid timeout %d\n",
+ progname, loop_timeout);
+ longjmp(abort_buf, 1);
+ }
+ break;
+ case 'i':
+ set_vendor_identifier(optarg);
+ break;
+ case 'o':
+ bringup_first = 1;
+ break;
+ case 'n':
+ do_not_config = 1;
+ break;
+ case 'd':
+ dev = add_device(optarg);
+ if (dev)
+ bringup_device(dev);
+ break;
+ case '?':
+ fprintf(stderr, "%s: invalid option -%c\n",
+ progname, optopt);
+ longjmp(abort_buf, 1);
+ }
+ } while (1);
+
+ for (c = optind; c < argc; c++) {
+ dev = add_device(argv[c]);
+ if (dev)
+ bringup_device(dev);
+ }
+
+ if (check_autoconfig()) {
+ if (cfg_local_port != LOCAL_PORT) {
+ printf("IP-Config: binding source port to %d, "
+ "dest to %d\n",
+ cfg_local_port, cfg_remote_port);
+ }
+ err = loop();
+ }
+
+ return err;
+}