summaryrefslogtreecommitdiffstats
path: root/usr/kinit/ipconfig
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
parentInitial commit. (diff)
downloadklibc-2f0649f6fe411d7e07c8d56cf8ea56db53536da8.tar.xz
klibc-2f0649f6fe411d7e07c8d56cf8ea56db53536da8.zip
Adding upstream version 2.0.13.upstream/2.0.13upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--usr/kinit/ipconfig/Kbuild35
-rw-r--r--usr/kinit/ipconfig/README.ipconfig120
-rw-r--r--usr/kinit/ipconfig/bootp_packet.h44
-rw-r--r--usr/kinit/ipconfig/bootp_proto.c565
-rw-r--r--usr/kinit/ipconfig/bootp_proto.h10
-rw-r--r--usr/kinit/ipconfig/dhcp_proto.c301
-rw-r--r--usr/kinit/ipconfig/dhcp_proto.h19
-rw-r--r--usr/kinit/ipconfig/ipconfig.h25
-rw-r--r--usr/kinit/ipconfig/main.c924
-rw-r--r--usr/kinit/ipconfig/netdev.c279
-rw-r--r--usr/kinit/ipconfig/netdev.h107
-rw-r--r--usr/kinit/ipconfig/packet.c278
-rw-r--r--usr/kinit/ipconfig/packet.h12
13 files changed, 2719 insertions, 0 deletions
diff --git a/usr/kinit/ipconfig/Kbuild b/usr/kinit/ipconfig/Kbuild
new file mode 100644
index 0000000..686b03b
--- /dev/null
+++ b/usr/kinit/ipconfig/Kbuild
@@ -0,0 +1,35 @@
+#
+# Kbuild file for ipconfig
+#
+
+static-y := static/ipconfig
+shared-y := shared/ipconfig
+
+# common .o files
+objs := main.o netdev.o packet.o
+# dhcp
+objs += dhcp_proto.o
+# bootp
+objs += bootp_proto.o
+
+
+# TODO - do we want a stripped version
+# TODO - do we want the static.g + shared.g directories?
+
+
+# Create built-in.o with all object files (used by kinit)
+lib-y := $(objs)
+
+# .o files used to built executables
+static/ipconfig-y := $(objs)
+shared/ipconfig-y := $(objs)
+
+# Cleaning
+clean-dirs := static shared
+
+# install binary
+ifdef KLIBCSHAREDFLAGS
+install-y := $(shared-y)
+else
+install-y := $(static-y)
+endif
diff --git a/usr/kinit/ipconfig/README.ipconfig b/usr/kinit/ipconfig/README.ipconfig
new file mode 100644
index 0000000..5ee87e5
--- /dev/null
+++ b/usr/kinit/ipconfig/README.ipconfig
@@ -0,0 +1,120 @@
+BOOTP/DHCP client for klibc
+---------------------------
+
+Usage:
+
+ipconfig [-c proto] [-d interface] [-i identifier]
+ [-n] [-p port] [-t timeout] [interface ...]
+
+-c proto Use PROTO as the configuration protocol for all
+ interfaces, unless overridden by specific interfaces.
+-d interface Either the name of an interface, or a long spec.
+-i identifier DHCP vendor class identifier. The default is
+ "Linux ipconfig".
+-n Do nothing - just print the configuration that would
+ be performed.
+-p port Send bootp/dhcp broadcasts from PORT, to PORT - 1.
+-t timeout Give up on all unconfigured interfaces after TIMEOUT secs.
+
+You can configure multiple interfaces by passing multiple interface
+specs on the command line, or by using the special interface name
+"all". If you're autoconfiguring any interfaces, ipconfig will wait
+until either all such interfaces have been configured, or the timeout
+passes.
+
+PROTO can be one of the following, which selects the autoconfiguration
+protocol to use:
+
+not specified use all protocols (the default)
+dhcp use bootp and dhcp
+bootp use bootp only
+rarp use rarp (not currently supported)
+none no autoconfiguration - either static config, or none at all
+
+An interface spec can be either short form, which is just the name of
+an interface (eth0 or whatever), or long form. The long form consists
+of two or more fields, separated by colons:
+
+<client-ip>:<server-ip>:<gw-ip>:<netmask>:<hostname>:<device>:<autoconf>:
+ <dns0-ip>:<dns1-ip>:<ntp0-ip>:...
+
+ <client-ip> IP address of the client. If empty, the address will
+ either be determined by RARP/BOOTP/DHCP. What protocol
+ is used de- pends on the <autoconf> parameter. If this
+ parameter is not empty, autoconf will be used.
+
+ <server-ip> IP address of the NFS server. If RARP is used to
+ determine the client address and this parameter is NOT
+ empty only replies from the specified server are
+ accepted. To use different RARP and NFS server,
+ specify your RARP server here (or leave it blank), and
+ specify your NFS server in the `nfsroot' parameter
+ (see above). If this entry is blank the address of the
+ server is used which answered the RARP/BOOTP/DHCP
+ request.
+
+ <gw-ip> IP address of a gateway if the server is on a different
+ subnet. If this entry is empty no gateway is used and the
+ server is assumed to be on the local network, unless a
+ value has been received by BOOTP/DHCP.
+
+ <netmask> Netmask for local network interface. If this is empty,
+ the netmask is derived from the client IP address assuming
+ classful addressing, unless overridden in BOOTP/DHCP reply.
+
+ <hostname> Name of the client. If empty, the client IP address is
+ used in ASCII notation, or the value received by
+ BOOTP/DHCP.
+
+ <device> Name of network device to use. If this is empty, all
+ devices are used for RARP/BOOTP/DHCP requests, and the
+ first one we receive a reply on is configured. If you
+ have only one device, you can safely leave this blank.
+
+ <autoconf> Method to use for autoconfiguration. If this is either
+ 'rarp', 'bootp', or 'dhcp' the specified protocol is
+ used. If the value is 'both', 'all' or empty, all
+ protocols are used. 'off', 'static' or 'none' means
+ no autoconfiguration.
+
+ <dns0-ip> IP address of primary nameserver.
+
+ Default: None if not using autoconfiguration; determined
+ automatically if using autoconfiguration.
+
+ <dns1-ip> IP address of secondary nameserver.
+ See <dns0-ip>.
+
+ <ntp0-ip> IP address of a Network Time Protocol (NTP) server.
+ Currently ignored.
+
+ ... Additional fields will be ignored.
+
+IP addresses and netmasks must be either absent (defaulting to zero)
+or presented in dotted-quad notation.
+
+An interface spec can be prefixed with either "ip=", "nfsaddrs=", both
+of which are ignored. These (along with the ugliness of the long
+form) are present for compatibility with the in-kernel ipconfig code
+from 2.4 and earlier kernels.
+
+Here are a few examples of valid ipconfig command lines.
+
+Enable the loopback interface:
+ ipconfig 127.0.0.1:::::lo:none
+
+Try to configure eth0 using bootp for up to 30 seconds:
+ ipconfig -t 30 -c bootp eth0
+
+Configure eth0 and eth1 using dhcp or bootp, and eth2 statically:
+ ipconfig -c any eth0 eth1 192.168.1.1:::::eth2:none
+
+--
+
+From Russell's original README, and still true:
+
+The code in main.c is yucky imho. Needs cleaning.
+
+--
+Russell King (2002/10/22)
+Bryan O'Sullivan (2003/04/29)
diff --git a/usr/kinit/ipconfig/bootp_packet.h b/usr/kinit/ipconfig/bootp_packet.h
new file mode 100644
index 0000000..1d5bd0d
--- /dev/null
+++ b/usr/kinit/ipconfig/bootp_packet.h
@@ -0,0 +1,44 @@
+#ifndef BOOTP_PACKET_H
+#define BOOTP_PACKET_H
+
+#include <sys/uio.h>
+
+struct netdev;
+
+/* packet ops */
+#define BOOTP_REQUEST 1
+#define BOOTP_REPLY 2
+
+/* your basic bootp packet */
+struct bootp_hdr {
+ uint8_t op;
+ uint8_t htype;
+ uint8_t hlen;
+ uint8_t hops;
+ uint32_t xid;
+ uint16_t secs;
+ uint16_t flags;
+ uint32_t ciaddr;
+ uint32_t yiaddr;
+ uint32_t siaddr;
+ uint32_t giaddr;
+ uint8_t chaddr[16];
+ char server_name[64];
+ char boot_file[128];
+ /* 312 bytes of extensions */
+};
+
+/*
+ * memory size of BOOTP Vendor Extensions/DHCP Options for receiving
+ *
+ * generic_ether_mtu:1500, min_sizeof(ip_hdr):20, sizeof(udp_hdr):8
+ *
+ * #define BOOTP_EXTS_SIZE (1500 - 20 - 8 - sizeof(struct bootp_hdr))
+ */
+/* larger size for backward compatibility of ipconfig */
+#define BOOTP_EXTS_SIZE 1500
+
+/* minimum length of BOOTP/DHCP packet on sending */
+#define BOOTP_MIN_LEN 300
+
+#endif /* BOOTP_PACKET_H */
diff --git a/usr/kinit/ipconfig/bootp_proto.c b/usr/kinit/ipconfig/bootp_proto.c
new file mode 100644
index 0000000..f6f9dd4
--- /dev/null
+++ b/usr/kinit/ipconfig/bootp_proto.c
@@ -0,0 +1,565 @@
+/*
+ * BOOTP packet protocol handling.
+ */
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+#include "ipconfig.h"
+#include "netdev.h"
+#include "bootp_packet.h"
+#include "bootp_proto.h"
+#include "packet.h"
+
+static uint8_t bootp_options[312] = {
+ [ 0] = 99, 130, 83, 99,/* RFC1048 magic cookie */
+ [ 4] = 1, 4, /* 4- 9 subnet mask */
+ [ 10] = 3, 4, /* 10- 15 default gateway */
+ [ 16] = 5, 8, /* 16- 25 nameserver */
+ [ 26] = 12, 32, /* 26- 59 host name */
+ [ 60] = 40, 32, /* 60- 95 nis domain name */
+ [ 96] = 17, 40, /* 96-137 boot path */
+ [138] = 57, 2, 1, 150, /* 138-141 extension buffer */
+ [142] = 255, /* end of list */
+};
+
+/*
+ * Send a plain bootp request packet with options
+ */
+int bootp_send_request(struct netdev *dev)
+{
+ struct bootp_hdr bootp;
+ struct iovec iov[] = {
+ /* [0] = ip + udp headers */
+ [1] = {&bootp, sizeof(bootp)},
+ [2] = {bootp_options, 312}
+ };
+
+ memset(&bootp, 0, sizeof(struct bootp_hdr));
+
+ bootp.op = BOOTP_REQUEST, bootp.htype = dev->hwtype;
+ bootp.hlen = dev->hwlen;
+ bootp.xid = dev->bootp.xid;
+ bootp.ciaddr = dev->ip_addr;
+ bootp.secs = htons(time(NULL) - dev->open_time);
+ memcpy(bootp.chaddr, dev->hwaddr, 16);
+
+ dprintf("-> bootp xid 0x%08x secs 0x%08x ",
+ bootp.xid, ntohs(bootp.secs));
+
+ return packet_send(dev, iov, 2);
+}
+
+/*
+ * DESCRIPTION
+ * bootp_ext119_decode() decodes Domain Search Option data.
+ * The decoded string is separated with ' '.
+ * For example, it is either "foo.bar.baz. bar.baz.", "foo.bar.", or "foo.".
+ *
+ * ARGUMENTS
+ * const uint8_t *ext
+ * *ext is a pointer to a DHCP Domain Search Option data. *ext does not
+ * include a tag(code) octet and a length octet in DHCP options.
+ * For example, if *ext is {3, 'f', 'o', 'o', 0}, this function returns
+ * a pointer to a "foo." string.
+ *
+ * int16_t ext_size
+ * ext_size is the memory size of *ext. For example,
+ * if *ext is {3, 'f', 'o', 'o', 0}, ext_size must be 5.
+ *
+ * uint8_t *tmp
+ * *tmp is a pointer to a temporary memory space for decoding.
+ * The memory size must be equal to or more than ext_size.
+ * 'memset(tmp, 0, sizeof(tmp));' is not required, but values in *tmp
+ * are changed in decoding process.
+ *
+ * RETURN VALUE
+ * if OK, a pointer to a decoded string malloc-ed
+ * else , NULL
+ *
+ * SEE ALSO RFC3397
+ */
+static char *bootp_ext119_decode(const void *ext, int16_t ext_size, void *tmp)
+{
+ uint8_t *u8ext;
+ int_fast32_t i;
+ int_fast32_t decoded_size;
+ int_fast8_t currentdomain_is_singledot;
+
+ /* only for validating *ext */
+ uint8_t *is_pointee;
+ int_fast32_t is_pointee_size;
+
+ /* only for structing a decoded string */
+ char *decoded_str;
+ int_fast32_t dst_i;
+
+ if (ext == NULL || ext_size <= 0 || tmp == NULL)
+ return NULL;
+
+ u8ext = (uint8_t *)ext;
+ is_pointee = tmp;
+ memset(is_pointee, 0, (size_t)ext_size);
+ is_pointee_size = 0;
+
+ /*
+ * validate the format of *ext and
+ * calculate the memory size for a decoded string
+ */
+ i = 0;
+ decoded_size = 0;
+ currentdomain_is_singledot = 1;
+ while (1) {
+ if (i >= ext_size)
+ return NULL;
+
+ if (u8ext[i] == 0) {
+ /* Zero-ending */
+ if (currentdomain_is_singledot)
+ decoded_size++; /* for '.' */
+ decoded_size++; /* for ' ' or '\0' */
+ currentdomain_is_singledot = 1;
+ i++;
+ if (i == ext_size)
+ break;
+ is_pointee_size = i;
+ } else if (u8ext[i] < 0x40) {
+ /* Label(sub-domain string) */
+ int j;
+
+ /* loosely validate characters for domain names */
+ if (i + u8ext[i] >= ext_size)
+ return NULL;
+ for (j = i + 1; j <= i + u8ext[i]; j++)
+ if (!(u8ext[j] == '-' ||
+ ('0' <= u8ext[j] && u8ext[j] <= '9') ||
+ ('A' <= u8ext[j] && u8ext[j] <= 'Z') ||
+ ('a' <= u8ext[j] && u8ext[j] <= 'z')))
+ return NULL;
+
+ is_pointee[i] = 1;
+ decoded_size += u8ext[i] + 1; /* for Label + '.' */
+ currentdomain_is_singledot = 0;
+ i += u8ext[i] + 1;
+ } else if (u8ext[i] < 0xc0)
+ return NULL;
+
+ else {
+ /* Compression-pointer (to a prior Label) */
+ int_fast32_t p;
+
+ if (i + 1 >= ext_size)
+ return NULL;
+
+ p = ((0x3f & u8ext[i]) << 8) + u8ext[i + 1];
+ if (!(p < is_pointee_size && is_pointee[p]))
+ return NULL;
+
+ while (1) {
+ /* u8ext[p] was validated */
+ if (u8ext[p] == 0) {
+ /* Zero-ending */
+ decoded_size++;
+ break;
+ } else if (u8ext[p] < 0x40) {
+ /* Label(sub-domain string) */
+ decoded_size += u8ext[p] + 1;
+ p += u8ext[p] + 1;
+ } else {
+ /* Compression-pointer */
+ p = ((0x3f & u8ext[p]) << 8)
+ + u8ext[p + 1];
+ }
+ }
+
+ currentdomain_is_singledot = 1;
+ i += 2;
+ if (i == ext_size)
+ break;
+ is_pointee_size = i;
+ }
+ }
+
+
+ /*
+ * construct a decoded string
+ */
+ decoded_str = malloc(decoded_size);
+ if (decoded_str == NULL)
+ return NULL;
+
+ i = 0;
+ dst_i = 0;
+ currentdomain_is_singledot = 1;
+ while (1) {
+ if (u8ext[i] == 0) {
+ /* Zero-ending */
+ if (currentdomain_is_singledot) {
+ if (dst_i != 0)
+ dst_i++;
+ decoded_str[dst_i] = '.';
+ }
+ dst_i++;
+ decoded_str[dst_i] = ' ';
+
+ currentdomain_is_singledot = 1;
+ i++;
+ if (i == ext_size)
+ break;
+ } else if (u8ext[i] < 0x40) {
+ /* Label(sub-domain string) */
+ if (dst_i != 0)
+ dst_i++;
+ memcpy(&decoded_str[dst_i], &u8ext[i + 1],
+ (size_t)u8ext[i]);
+ dst_i += u8ext[i];
+ decoded_str[dst_i] = '.';
+
+ currentdomain_is_singledot = 0;
+ i += u8ext[i] + 1;
+ } else {
+ /* Compression-pointer (to a prior Label) */
+ int_fast32_t p;
+
+ p = ((0x3f & u8ext[i]) << 8) + u8ext[i + 1];
+ while (1) {
+ if (u8ext[p] == 0) {
+ /* Zero-ending */
+ decoded_str[dst_i++] = '.';
+ decoded_str[dst_i] = ' ';
+ break;
+ } else if (u8ext[p] < 0x40) {
+ /* Label(sub-domain string) */
+ dst_i++;
+ memcpy(&decoded_str[dst_i],
+ &u8ext[p + 1],
+ (size_t)u8ext[p]);
+ dst_i += u8ext[p];
+ decoded_str[dst_i] = '.';
+
+ p += u8ext[p] + 1;
+ } else {
+ /* Compression-pointer */
+ p = ((0x3f & u8ext[p]) << 8)
+ + u8ext[p + 1];
+ }
+ }
+
+ currentdomain_is_singledot = 1;
+ i += 2;
+ if (i == ext_size)
+ break;
+ }
+ }
+ decoded_str[dst_i] = '\0';
+#ifdef DEBUG
+ if (dst_i + 1 != decoded_size) {
+ dprintf("bug:%s():bottom: malloc(%ld), write(%ld)\n",
+ __func__, (long)decoded_size, (long)(dst_i + 1));
+ exit(1);
+ }
+#endif
+ return decoded_str;
+}
+
+/*
+ * DESCRIPTION
+ * bootp_ext121_decode() decodes Classless Route Option data.
+ *
+ * ARGUMENTS
+ * const uint8_t *ext
+ * *ext is a pointer to a DHCP Classless Route Option data.
+ * For example, if *ext is {16, 192, 168, 192, 168, 42, 1},
+ * this function returns a pointer to
+ * {
+ * subnet = 192.168.0.0;
+ * netmask_width = 16;
+ * gateway = 192.168.42.1;
+ * next = NULL;
+ * }
+ *
+ * int16_t ext_size
+ * ext_size is the memory size of *ext. For example,
+ * if *ext is {16, 192, 168, 192, 168, 42, 1}, ext_size must be 7.
+ *
+ * RETURN VALUE
+ * if OK, a pointer to a decoded struct route malloc-ed
+ * else , NULL
+ *
+ * SEE ALSO RFC3442
+ */
+struct route *bootp_ext121_decode(const uint8_t *ext, int16_t ext_size)
+{
+ int16_t index = 0;
+ uint8_t netmask_width;
+ uint8_t significant_octets;
+ struct route *routes = NULL;
+ struct route *prev_route = NULL;
+
+ while (index < ext_size) {
+ netmask_width = ext[index];
+ index++;
+ if (netmask_width > 32) {
+ printf("IP-Config: Given Classless Route Option subnet mask width '%u' "
+ "exceeds IPv4 limit of 32. Ignoring remaining option.\n",
+ netmask_width);
+ return routes;
+ }
+ significant_octets = netmask_width / 8 + (netmask_width % 8 > 0);
+ if (ext_size - index < significant_octets + 4) {
+ printf("IP-Config: Given Classless Route Option remaining lengths (%u octets) "
+ "is shorter than the expected %u octets. Ignoring remaining options.\n",
+ ext_size - index, significant_octets + 4);
+ return routes;
+ }
+
+ struct route *route = malloc(sizeof(struct route));
+ if (route == NULL)
+ return routes;
+
+ /* convert only significant octets from byte array into integer in network byte order */
+ route->subnet = 0;
+ memcpy(&route->subnet, &ext[index], significant_octets);
+ index += significant_octets;
+ /* RFC3442 demands: After deriving a subnet number and subnet mask from
+ each destination descriptor, the DHCP client MUST zero any bits in
+ the subnet number where the corresponding bit in the mask is zero. */
+ route->subnet &= netdev_genmask(netmask_width);
+
+ /* convert octet array into network byte order */
+ memcpy(&route->gateway, &ext[index], 4);
+ index += 4;
+
+ route->netmask_width = netmask_width;
+ route->next = NULL;
+
+ if (prev_route == NULL) {
+ routes = route;
+ } else {
+ prev_route->next = route;
+ }
+ prev_route = route;
+ }
+ return routes;
+}
+
+/*
+ * Parse a bootp reply packet
+ */
+int bootp_parse(struct netdev *dev, struct bootp_hdr *hdr,
+ uint8_t *exts, int extlen)
+{
+ uint8_t ext119_buf[BOOTP_EXTS_SIZE];
+ int16_t ext119_len = 0;
+ uint8_t ext121_buf[BOOTP_EXTS_SIZE];
+ int16_t ext121_len = 0;
+
+ dev->bootp.gateway = hdr->giaddr;
+ dev->ip_addr = hdr->yiaddr;
+ dev->ip_server = hdr->siaddr;
+ dev->ip_netmask = INADDR_ANY;
+ dev->ip_broadcast = INADDR_ANY;
+ dev->ip_gateway = hdr->giaddr;
+ dev->ip_nameserver[0] = INADDR_ANY;
+ dev->ip_nameserver[1] = INADDR_ANY;
+ dev->hostname[0] = '\0';
+ dev->nisdomainname[0] = '\0';
+ dev->bootpath[0] = '\0';
+ memcpy(&dev->filename, &hdr->boot_file, FNLEN);
+
+ if (extlen >= 4 && exts[0] == 99 && exts[1] == 130 &&
+ exts[2] == 83 && exts[3] == 99) {
+ uint8_t *ext;
+
+ for (ext = exts + 4; ext - exts < extlen;) {
+ int len;
+ uint8_t opt = *ext++;
+
+ if (opt == 0)
+ continue;
+ else if (opt == 255)
+ break;
+
+ if (ext - exts >= extlen)
+ break;
+ len = *ext++;
+
+ if (ext - exts + len > extlen)
+ break;
+ switch (opt) {
+ case 1: /* subnet mask */
+ if (len == 4)
+ memcpy(&dev->ip_netmask, ext, 4);
+ break;
+ case 3: /* default gateway */
+ if (len >= 4)
+ memcpy(&dev->ip_gateway, ext, 4);
+ break;
+ case 6: /* DNS server */
+ if (len >= 4)
+ memcpy(&dev->ip_nameserver, ext,
+ len >= 8 ? 8 : 4);
+ break;
+ case 12: /* host name */
+ if (len > sizeof(dev->hostname) - 1)
+ len = sizeof(dev->hostname) - 1;
+ memcpy(&dev->hostname, ext, len);
+ dev->hostname[len] = '\0';
+ break;
+ case 15: /* domain name */
+ if (len > sizeof(dev->dnsdomainname) - 1)
+ len = sizeof(dev->dnsdomainname) - 1;
+ memcpy(&dev->dnsdomainname, ext, len);
+ dev->dnsdomainname[len] = '\0';
+ break;
+ case 17: /* root path */
+ if (len > sizeof(dev->bootpath) - 1)
+ len = sizeof(dev->bootpath) - 1;
+ memcpy(&dev->bootpath, ext, len);
+ dev->bootpath[len] = '\0';
+ break;
+ case 26: /* interface MTU */
+ if (len == 2)
+ dev->mtu = (ext[0] << 8) + ext[1];
+ break;
+ case 28: /* broadcast addr */
+ if (len == 4)
+ memcpy(&dev->ip_broadcast, ext, 4);
+ break;
+ case 40: /* NIS domain name */
+ if (len > sizeof(dev->nisdomainname) - 1)
+ len = sizeof(dev->nisdomainname) - 1;
+ memcpy(&dev->nisdomainname, ext, len);
+ dev->nisdomainname[len] = '\0';
+ break;
+ case 54: /* server identifier */
+ if (len == 4 && !dev->ip_server)
+ memcpy(&dev->ip_server, ext, 4);
+ break;
+ case 119: /* Domain Search Option */
+ if (ext119_len >= 0 &&
+ ext119_len + len <= sizeof(ext119_buf)) {
+ memcpy(ext119_buf + ext119_len,
+ ext, len);
+ ext119_len += len;
+ } else
+ ext119_len = -1;
+
+ break;
+ case 121: /* Classless Static Route Option (RFC3442) */
+ if (ext121_len >= 0 &&
+ ext121_len + len <= sizeof(ext121_buf)) {
+ memcpy(ext121_buf + ext121_len,
+ ext, len);
+ ext121_len += len;
+ } else
+ ext121_len = -1;
+
+ break;
+ }
+
+ ext += len;
+ }
+ }
+ if (ext119_len > 0) {
+ char *ret;
+ uint8_t ext119_tmp[BOOTP_EXTS_SIZE];
+
+ ret = bootp_ext119_decode(ext119_buf, ext119_len, ext119_tmp);
+ if (ret != NULL) {
+ if (dev->domainsearch != NULL)
+ free(dev->domainsearch);
+ dev->domainsearch = ret;
+ }
+ }
+
+ if (ext121_len > 0) {
+ struct route *ret;
+
+ ret = bootp_ext121_decode(ext121_buf, ext121_len);
+ if (ret != NULL) {
+ struct route *cur = dev->routes;
+ struct route *next;
+ while (cur != NULL) {
+ next = cur->next;
+ free(cur);
+ cur = next;
+ }
+ dev->routes = ret;
+ }
+ }
+
+ /*
+ * Got packet.
+ */
+ return 1;
+}
+
+/*
+ * Receive a bootp reply and parse packet
+ * Returns:
+ *-1 = Error in packet_recv, try again later
+ * 0 = Unexpected packet, discarded
+ * 1 = Correctly received and parsed packet
+ */
+int bootp_recv_reply(struct netdev *dev)
+{
+ struct bootp_hdr bootp;
+ uint8_t bootp_options[BOOTP_EXTS_SIZE];
+ struct iovec iov[] = {
+ /* [0] = ip + udp headers */
+ [1] = {&bootp, sizeof(struct bootp_hdr)},
+ [2] = {bootp_options, sizeof(bootp_options)}
+ };
+ int ret;
+
+ ret = packet_recv(dev, iov, 3);
+ if (ret <= 0)
+ return ret;
+
+ if (ret < sizeof(struct bootp_hdr) ||
+ bootp.op != BOOTP_REPLY || /* RFC951 7.5 */
+ bootp.xid != dev->bootp.xid ||
+ memcmp(bootp.chaddr, dev->hwaddr, 16))
+ return 0;
+
+ ret -= sizeof(struct bootp_hdr);
+
+ return bootp_parse(dev, &bootp, bootp_options, ret);
+}
+
+/*
+ * Initialise interface for bootp.
+ */
+int bootp_init_if(struct netdev *dev)
+{
+ short flags;
+
+ /*
+ * Get the device flags
+ */
+ if (netdev_getflags(dev, &flags))
+ return -1;
+
+ /*
+ * We can't do DHCP nor BOOTP if this device
+ * doesn't support broadcast.
+ */
+ if (dev->mtu < 364 || (flags & IFF_BROADCAST) == 0) {
+ dev->caps &= ~(CAP_BOOTP | CAP_DHCP);
+ return 0;
+ }
+
+ /*
+ * Get a random XID
+ */
+ dev->bootp.xid = (uint32_t) lrand48();
+ dev->open_time = time(NULL);
+
+ return 0;
+}
diff --git a/usr/kinit/ipconfig/bootp_proto.h b/usr/kinit/ipconfig/bootp_proto.h
new file mode 100644
index 0000000..60873ce
--- /dev/null
+++ b/usr/kinit/ipconfig/bootp_proto.h
@@ -0,0 +1,10 @@
+#ifndef IPCONFIG_BOOTP_PROTO_H
+#define IPCONFIG_BOOTP_PROTO_H
+
+int bootp_send_request(struct netdev *dev);
+int bootp_recv_reply(struct netdev *dev);
+int bootp_parse(struct netdev *dev, struct bootp_hdr *hdr, uint8_t * exts,
+ int extlen);
+int bootp_init_if(struct netdev *dev);
+
+#endif /* IPCONFIG_BOOTP_PROTO_H */
diff --git a/usr/kinit/ipconfig/dhcp_proto.c b/usr/kinit/ipconfig/dhcp_proto.c
new file mode 100644
index 0000000..4e560b8
--- /dev/null
+++ b/usr/kinit/ipconfig/dhcp_proto.c
@@ -0,0 +1,301 @@
+/*
+ * DHCP RFC 2131 and 2132
+ */
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#include "ipconfig.h"
+#include "netdev.h"
+#include "bootp_packet.h"
+#include "bootp_proto.h"
+#include "dhcp_proto.h"
+#include "packet.h"
+
+static uint8_t dhcp_params[] = {
+ 1, /* subnet mask */
+ 3, /* default gateway */
+ 6, /* DNS server */
+ 12, /* host name */
+ 15, /* domain name */
+ 17, /* root path */
+ 26, /* interface mtu */
+ 28, /* broadcast addr */
+ 40, /* NIS domain name (why?) */
+ 119, /* Domain Search Option */
+ 121, /* Classless Static Route Option (RFC3442) */
+};
+
+static uint8_t dhcp_discover_hdr[] = {
+ 99, 130, 83, 99, /* bootp cookie */
+ 53, 1, DHCPDISCOVER, /* dhcp message type */
+ 55, sizeof(dhcp_params), /* parameter list */
+};
+
+static uint8_t dhcp_request_hdr[] = {
+ 99, 130, 83, 99, /* boot cookie */
+ 53, 1, DHCPREQUEST, /* dhcp message type */
+#define SERVER_IP_OFF 9
+ 54, 4, 0, 0, 0, 0, /* server IP */
+#define REQ_IP_OFF 15
+ 50, 4, 0, 0, 0, 0, /* requested IP address */
+ 55, sizeof(dhcp_params), /* parameter list */
+};
+
+static uint8_t dhcp_end[] = {
+ 255,
+};
+
+/* Both iovecs below have to have the same structure, since dhcp_send()
+ pokes at the internals */
+#define DHCP_IOV_LEN 8
+
+static struct iovec dhcp_discover_iov[DHCP_IOV_LEN] = {
+ /* [0] = ip + udp header */
+ /* [1] = bootp header */
+ [2] = {dhcp_discover_hdr, sizeof(dhcp_discover_hdr)},
+ [3] = {dhcp_params, sizeof(dhcp_params)},
+ /* [4] = optional vendor class */
+ /* [5] = optional hostname */
+ /* [6] = {dhcp_end, sizeof(dhcp_end)} */
+ /* [7] = optional padding */
+};
+
+static struct iovec dhcp_request_iov[DHCP_IOV_LEN] = {
+ /* [0] = ip + udp header */
+ /* [1] = bootp header */
+ [2] = {dhcp_request_hdr, sizeof(dhcp_request_hdr)},
+ [3] = {dhcp_params, sizeof(dhcp_params)},
+ /* [4] = optional vendor class */
+ /* [5] = optional hostname */
+ /* [6] = {dhcp_end, sizeof(dhcp_end)} */
+ /* [7] = optional padding */
+};
+
+/*
+ * Parse a DHCP response packet
+ * Returns:
+ * 0 = Unexpected packet, not parsed
+ * 2 = DHCPOFFER (from dhcp_proto.h)
+ * 5 = DHCPACK
+ * 6 = DHCPNACK
+ */
+static int dhcp_parse(struct netdev *dev, struct bootp_hdr *hdr,
+ uint8_t *exts, int extlen)
+{
+ uint8_t type = 0;
+ uint32_t serverid = INADDR_NONE;
+ uint32_t leasetime = 0;
+ int ret = 0;
+
+ if (extlen >= 4 && exts[0] == 99 && exts[1] == 130 &&
+ exts[2] == 83 && exts[3] == 99) {
+ uint8_t *ext;
+
+ for (ext = exts + 4; ext - exts < extlen;) {
+ int len;
+ uint8_t opt = *ext++;
+
+ if (opt == 0)
+ continue;
+ else if (opt == 255)
+ break;
+
+ if (ext - exts >= extlen)
+ break;
+ len = *ext++;
+
+ if (ext - exts + len > extlen)
+ break;
+ switch (opt) {
+ case 51: /* IP Address Lease Time */
+ if (len == 4)
+ leasetime = ntohl(*(uint32_t *)ext);
+ break;
+ case 53: /* DHCP Message Type */
+ if (len == 1)
+ type = *ext;
+ break;
+ case 54: /* Server Identifier */
+ if (len == 4)
+ memcpy(&serverid, ext, 4);
+ break;
+ }
+ ext += len;
+ }
+ }
+
+ switch (type) {
+ case DHCPOFFER:
+ ret = bootp_parse(dev, hdr, exts, extlen) ? DHCPOFFER : 0;
+ if (ret == DHCPOFFER && serverid != INADDR_NONE)
+ dev->serverid = serverid;
+ dprintf("\n dhcp offer\n");
+ break;
+
+ case DHCPACK:
+ dev->dhcpleasetime = leasetime;
+ ret = bootp_parse(dev, hdr, exts, extlen) ? DHCPACK : 0;
+ dprintf("\n dhcp ack\n");
+ break;
+
+ case DHCPNAK:
+ ret = DHCPNAK;
+ dprintf("\n dhcp nak\n");
+ break;
+ }
+ return ret;
+}
+
+/*
+ * Receive and parse a DHCP packet
+ * Returns:
+ *-1 = Error in packet_recv, try again later
+ * 0 = Unexpected packet, discarded
+ * 2 = DHCPOFFER (from dhcp_proto.h)
+ * 5 = DHCPACK
+ * 6 = DHCPNACK
+ */
+static int dhcp_recv(struct netdev *dev)
+{
+ struct bootp_hdr bootp;
+ uint8_t dhcp_options[BOOTP_EXTS_SIZE];
+ struct iovec iov[] = {
+ /* [0] = ip + udp header */
+ [1] = {&bootp, sizeof(struct bootp_hdr)},
+ [2] = {dhcp_options, sizeof(dhcp_options)}
+ };
+ int ret;
+
+ ret = packet_recv(dev, iov, 3);
+ if (ret <= 0)
+ return ret;
+
+ dprintf("\n dhcp xid %08x ", dev->bootp.xid);
+
+ if (ret < sizeof(struct bootp_hdr) || bootp.op != BOOTP_REPLY ||
+ /* RFC951 7.5 */ bootp.xid != dev->bootp.xid ||
+ memcmp(bootp.chaddr, dev->hwaddr, 16))
+ return 0;
+
+ ret -= sizeof(struct bootp_hdr);
+
+ return dhcp_parse(dev, &bootp, dhcp_options, ret);
+}
+
+static int dhcp_send(struct netdev *dev, struct iovec *vec)
+{
+ struct bootp_hdr bootp;
+ char dhcp_hostname[SYS_NMLN+2];
+ uint8_t padding[BOOTP_MIN_LEN - sizeof(struct bootp_hdr)];
+ int padding_len;
+ int i = 4;
+ int j;
+
+ memset(&bootp, 0, sizeof(struct bootp_hdr));
+
+ bootp.op = BOOTP_REQUEST;
+ bootp.htype = dev->hwtype;
+ bootp.hlen = dev->hwlen;
+ bootp.xid = dev->bootp.xid;
+ bootp.ciaddr = INADDR_ANY;
+ /* yiaddr should always be set to 0 for the messages we're likely
+ * to send as a DHCP client: DHCPDISCOVER, DHCPREQUEST, DHCPDECLINE,
+ * DHCPINFORM, DHCPRELEASE
+ * cf. RFC2131 section 4.1.1, table 5.
+ */
+ bootp.yiaddr = INADDR_ANY;
+ bootp.giaddr = INADDR_ANY;
+ bootp.flags = htons(0x8000);
+ bootp.secs = htons(time(NULL) - dev->open_time);
+ memcpy(bootp.chaddr, dev->hwaddr, 16);
+
+ vec[1].iov_base = &bootp;
+ vec[1].iov_len = sizeof(struct bootp_hdr);
+
+ dprintf("xid %08x secs %d ", bootp.xid, ntohs(bootp.secs));
+
+ if (vendor_class_identifier_len > 2) {
+ vec[i].iov_base = vendor_class_identifier;
+ vec[i].iov_len = vendor_class_identifier_len;
+ i++;
+
+ dprintf("vendor_class_identifier \"%.*s\" ",
+ vendor_class_identifier_len-2,
+ vendor_class_identifier+2);
+ }
+
+ if (dev->reqhostname[0] != '\0') {
+ int len = strlen(dev->reqhostname);
+ dhcp_hostname[0] = 12;
+ dhcp_hostname[1] = len;
+ memcpy(dhcp_hostname+2, dev->reqhostname, len);
+
+ vec[i].iov_base = dhcp_hostname;
+ vec[i].iov_len = len+2;
+ i++;
+
+ printf("hostname %.*s ", len, dhcp_hostname+2);
+ }
+
+ vec[i].iov_base = dhcp_end;
+ vec[i].iov_len = sizeof(dhcp_end);
+
+ /* Append padding if DHCP packet length is shorter than BOOTP_MIN_LEN */
+ padding_len = sizeof(padding);
+ for (j = 2; j <= i; j++)
+ padding_len -= vec[j].iov_len;
+ if (padding_len > 0) {
+ memset(padding, 0, padding_len);
+ i++;
+ vec[i].iov_base = padding;
+ vec[i].iov_len = padding_len;
+ }
+
+ return packet_send(dev, vec, i + 1);
+}
+
+/*
+ * Send a DHCP discover packet
+ */
+int dhcp_send_discover(struct netdev *dev)
+{
+ dev->ip_addr = INADDR_ANY;
+ dev->ip_gateway = INADDR_ANY;
+
+ dprintf("-> dhcp discover ");
+
+ return dhcp_send(dev, dhcp_discover_iov);
+}
+
+/*
+ * Receive a DHCP offer packet
+ */
+int dhcp_recv_offer(struct netdev *dev)
+{
+ return dhcp_recv(dev);
+}
+
+/*
+ * Send a DHCP request packet
+ */
+int dhcp_send_request(struct netdev *dev)
+{
+ memcpy(&dhcp_request_hdr[SERVER_IP_OFF], &dev->serverid, 4);
+ memcpy(&dhcp_request_hdr[REQ_IP_OFF], &dev->ip_addr, 4);
+
+ dprintf("-> dhcp request ");
+
+ return dhcp_send(dev, dhcp_request_iov);
+}
+
+/*
+ * Receive a DHCP ack packet
+ */
+int dhcp_recv_ack(struct netdev *dev)
+{
+ return dhcp_recv(dev);
+}
diff --git a/usr/kinit/ipconfig/dhcp_proto.h b/usr/kinit/ipconfig/dhcp_proto.h
new file mode 100644
index 0000000..0fba92f
--- /dev/null
+++ b/usr/kinit/ipconfig/dhcp_proto.h
@@ -0,0 +1,19 @@
+#ifndef IPCONFIG_DHCP_PROTO_H
+#define IPCONFIG_DHCP_PROTO_H
+
+/* DHCP message types */
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+
+int dhcp_send_discover(struct netdev *dev);
+int dhcp_recv_offer(struct netdev *dev);
+int dhcp_send_request(struct netdev *dev);
+int dhcp_recv_ack(struct netdev *dev);
+
+#endif /* IPCONFIG_DHCP_PROTO_H */
diff --git a/usr/kinit/ipconfig/ipconfig.h b/usr/kinit/ipconfig/ipconfig.h
new file mode 100644
index 0000000..d1d7e42
--- /dev/null
+++ b/usr/kinit/ipconfig/ipconfig.h
@@ -0,0 +1,25 @@
+#ifndef IPCONFIG_IPCONFIG_H
+#define IPCONFIG_IPCONFIG_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#define LOCAL_PORT 68
+#define REMOTE_PORT (LOCAL_PORT - 1)
+
+extern uint16_t cfg_local_port;
+extern uint16_t cfg_remote_port;
+
+extern char vendor_class_identifier[];
+extern int vendor_class_identifier_len;
+
+int ipconfig_main(int argc, char *argv[]);
+uint32_t ipconfig_server_address(void *next);
+
+#ifdef DEBUG
+# define dprintf printf
+#else
+# define dprintf(...) ((void)0)
+#endif
+
+#endif /* IPCONFIG_IPCONFIG_H */
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;
+}
diff --git a/usr/kinit/ipconfig/netdev.c b/usr/kinit/ipconfig/netdev.c
new file mode 100644
index 0000000..de87f96
--- /dev/null
+++ b/usr/kinit/ipconfig/netdev.c
@@ -0,0 +1,279 @@
+/*
+ * ioctl-based device configuration
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <netinet/in.h>
+#include <linux/route.h>
+#include <linux/sockios.h>
+
+#include "netdev.h"
+
+static int cfd = -1;
+
+static void copy_name(struct netdev *dev, struct ifreq *ifr)
+{
+ strncpy(ifr->ifr_name, dev->name, sizeof(ifr->ifr_name));
+ ifr->ifr_name[sizeof(ifr->ifr_name) - 1] = '\0';
+}
+
+int netdev_getflags(struct netdev *dev, short *flags)
+{
+ struct ifreq ifr;
+
+ copy_name(dev, &ifr);
+
+ if (ioctl(cfd, SIOCGIFFLAGS, &ifr) == -1) {
+ perror("SIOCGIFFLAGS");
+ return -1;
+ }
+
+ *flags = ifr.ifr_flags;
+ return 0;
+}
+
+static int netdev_sif_addr(struct ifreq *ifr, int cmd, uint32_t addr)
+{
+ struct sockaddr_in sin;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = addr;
+
+ memcpy(&ifr->ifr_addr, &sin, sizeof sin);
+
+ return ioctl(cfd, cmd, ifr);
+}
+
+int netdev_setaddress(struct netdev *dev)
+{
+ struct ifreq ifr;
+
+ copy_name(dev, &ifr);
+
+ if (dev->ip_addr != INADDR_ANY &&
+ netdev_sif_addr(&ifr, SIOCSIFADDR, dev->ip_addr) == -1) {
+ perror("SIOCSIFADDR");
+ return -1;
+ }
+
+ if (dev->ip_broadcast != INADDR_ANY &&
+ netdev_sif_addr(&ifr, SIOCSIFBRDADDR, dev->ip_broadcast) == -1) {
+ perror("SIOCSIFBRDADDR");
+ return -1;
+ }
+
+ if (dev->ip_netmask != INADDR_ANY &&
+ netdev_sif_addr(&ifr, SIOCSIFNETMASK, dev->ip_netmask) == -1) {
+ perror("SIOCSIFNETMASK");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void set_s_addr(struct sockaddr *saddr, uint32_t ipaddr)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = ipaddr,
+ };
+ memcpy(saddr, &sin, sizeof sin);
+}
+
+int netdev_setroutes(struct netdev *dev)
+{
+ struct rtentry r;
+
+ /* RFC3442 demands:
+ If the DHCP server returns both a Classless Static Routes option and
+ a Router option, the DHCP client MUST ignore the Router option. */
+ if (dev->routes != NULL) {
+ struct route *cur;
+ for (cur = dev->routes; cur != NULL; cur = cur->next) {
+ memset(&r, 0, sizeof(r));
+
+ r.rt_dev = dev->name;
+ set_s_addr(&r.rt_dst, cur->subnet);
+ set_s_addr(&r.rt_gateway, cur->gateway);
+ set_s_addr(&r.rt_genmask, netdev_genmask(cur->netmask_width));
+ r.rt_flags = RTF_UP;
+ if (cur->gateway != 0) {
+ r.rt_flags |= RTF_GATEWAY;
+ }
+
+ if (ioctl(cfd, SIOCADDRT, &r) == -1 && errno != EEXIST) {
+ perror("SIOCADDRT");
+ return -1;
+ }
+ }
+ } else if (dev->ip_gateway != INADDR_ANY) {
+ memset(&r, 0, sizeof(r));
+
+ set_s_addr(&r.rt_dst, INADDR_ANY);
+ set_s_addr(&r.rt_gateway, dev->ip_gateway);
+ set_s_addr(&r.rt_genmask, INADDR_ANY);
+ r.rt_flags = RTF_UP | RTF_GATEWAY;
+
+ if (ioctl(cfd, SIOCADDRT, &r) == -1 && errno != EEXIST) {
+ perror("SIOCADDRT");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int netdev_setmtu(struct netdev *dev)
+{
+ struct ifreq ifr;
+
+ copy_name(dev, &ifr);
+ ifr.ifr_mtu = dev->mtu;
+
+ return ioctl(cfd, SIOCSIFMTU, &ifr);
+}
+
+static int netdev_gif_addr(struct ifreq *ifr, int cmd, uint32_t * ptr)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr;
+
+ if (ioctl(cfd, cmd, ifr) == -1)
+ return -1;
+
+ *ptr = sin->sin_addr.s_addr;
+
+ return 0;
+}
+
+int netdev_up(struct netdev *dev)
+{
+ struct ifreq ifr;
+
+ copy_name(dev, &ifr);
+
+ if (ioctl(cfd, SIOCGIFFLAGS, &ifr) == -1) {
+ perror("SIOCGIFFLAGS");
+ return -1;
+ }
+
+ ifr.ifr_flags |= IFF_UP;
+
+ if (ioctl(cfd, SIOCSIFFLAGS, &ifr) == -1) {
+ perror("SIOCSIFFLAGS");
+ return -1;
+ }
+ return 0;
+}
+
+int netdev_down(struct netdev *dev)
+{
+ struct ifreq ifr;
+
+ copy_name(dev, &ifr);
+
+ if (ioctl(cfd, SIOCGIFFLAGS, &ifr) == -1) {
+ perror("SIOCGIFFLAGS");
+ return -1;
+ }
+
+ ifr.ifr_flags &= ~IFF_UP;
+
+ if (ioctl(cfd, SIOCSIFFLAGS, &ifr) == -1) {
+ perror("SIOCSIFFLAGS");
+ return -1;
+ }
+ return 0;
+}
+
+int netdev_init_if(struct netdev *dev)
+{
+ struct ifreq ifr;
+
+ if (cfd == -1)
+ cfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (cfd == -1) {
+ fprintf(stderr, "ipconfig: %s: socket(AF_INET): %s\n",
+ dev->name, strerror(errno));
+ return -1;
+ }
+
+ copy_name(dev, &ifr);
+
+ if (ioctl(cfd, SIOCGIFINDEX, &ifr) == -1) {
+ fprintf(stderr, "ipconfig: %s: SIOCGIFINDEX: %s\n",
+ dev->name, strerror(errno));
+ return -1;
+ }
+
+ dev->ifindex = ifr.ifr_ifindex;
+
+ if (ioctl(cfd, SIOCGIFMTU, &ifr) == -1) {
+ fprintf(stderr, "ipconfig: %s: SIOCGIFMTU: %s\n",
+ dev->name, strerror(errno));
+ return -1;
+ }
+
+ dev->mtu = ifr.ifr_mtu;
+
+ if (ioctl(cfd, SIOCGIFHWADDR, &ifr) == -1) {
+ fprintf(stderr, "ipconfig: %s: SIOCGIFHWADDR: %s\n",
+ dev->name, strerror(errno));
+ return -1;
+ }
+
+ dev->hwtype = ifr.ifr_hwaddr.sa_family;
+ dev->hwlen = 0;
+
+ switch (dev->hwtype) {
+ case ARPHRD_ETHER:
+ dev->hwlen = 6;
+ break;
+ case ARPHRD_EUI64:
+ dev->hwlen = 8;
+ break;
+ case ARPHRD_LOOPBACK:
+ dev->hwlen = 0;
+ break;
+ default:
+ return -1;
+ }
+
+ memcpy(dev->hwaddr, ifr.ifr_hwaddr.sa_data, dev->hwlen);
+ memset(dev->hwbrd, 0xff, dev->hwlen);
+
+ /*
+ * Try to get the current interface information.
+ */
+ if (dev->ip_addr == INADDR_NONE &&
+ netdev_gif_addr(&ifr, SIOCGIFADDR, &dev->ip_addr) == -1) {
+ fprintf(stderr, "ipconfig: %s: SIOCGIFADDR: %s\n",
+ dev->name, strerror(errno));
+ dev->ip_addr = 0;
+ dev->ip_broadcast = 0;
+ dev->ip_netmask = 0;
+ return 0;
+ }
+
+ if (dev->ip_broadcast == INADDR_NONE &&
+ netdev_gif_addr(&ifr, SIOCGIFBRDADDR, &dev->ip_broadcast) == -1) {
+ fprintf(stderr, "ipconfig: %s: SIOCGIFBRDADDR: %s\n",
+ dev->name, strerror(errno));
+ dev->ip_broadcast = 0;
+ }
+
+ if (dev->ip_netmask == INADDR_NONE &&
+ netdev_gif_addr(&ifr, SIOCGIFNETMASK, &dev->ip_netmask) == -1) {
+ fprintf(stderr, "ipconfig: %s: SIOCGIFNETMASK: %s\n",
+ dev->name, strerror(errno));
+ dev->ip_netmask = 0;
+ }
+
+ return 0;
+}
diff --git a/usr/kinit/ipconfig/netdev.h b/usr/kinit/ipconfig/netdev.h
new file mode 100644
index 0000000..dbc80cd
--- /dev/null
+++ b/usr/kinit/ipconfig/netdev.h
@@ -0,0 +1,107 @@
+#ifndef IPCONFIG_NETDEV_H
+#define IPCONFIG_NETDEV_H
+
+#include <arpa/inet.h>
+#include <sys/utsname.h>
+#include <net/if.h>
+
+#define BPLEN 256
+#define FNLEN 128 /* from DHCP RFC 2131 */
+
+struct route {
+ uint32_t subnet; /* subnet */
+ uint32_t netmask_width; /* subnet mask width */
+ uint32_t gateway; /* gateway */
+ struct route *next;
+};
+
+struct netdev {
+ char *name; /* Device name */
+ unsigned int ifindex; /* interface index */
+ unsigned int hwtype; /* ARPHRD_xxx */
+ unsigned int hwlen; /* HW address length */
+ uint8_t hwaddr[16]; /* HW address */
+ uint8_t hwbrd[16]; /* Broadcast HW address */
+ unsigned int mtu; /* Device mtu */
+ unsigned int caps; /* Capabilities */
+ time_t open_time;
+
+ struct { /* BOOTP/DHCP info */
+ int fd;
+ uint32_t xid;
+ uint32_t gateway; /* BOOTP/DHCP gateway */
+ } bootp;
+
+ struct { /* RARP information */
+ int fd;
+ } rarp;
+
+ uint8_t proto; /* a protocol used (e.g. PROTO_DHCP) */
+ uint32_t ip_addr; /* my address */
+ uint32_t ip_broadcast; /* broadcast address */
+ uint32_t ip_server; /* server address */
+ uint32_t ip_netmask; /* my subnet mask */
+ uint32_t ip_gateway; /* my gateway */
+ uint32_t ip_nameserver[2]; /* two nameservers */
+ uint32_t serverid; /* dhcp serverid */
+ uint32_t dhcpleasetime; /* duration in seconds */
+ char reqhostname[SYS_NMLN]; /* requested hostname */
+ char hostname[SYS_NMLN]; /* hostname */
+ char dnsdomainname[SYS_NMLN]; /* dns domain name */
+ char nisdomainname[SYS_NMLN]; /* nis domain name */
+ char bootpath[BPLEN]; /* boot path */
+ char filename[FNLEN]; /* filename */
+ char *domainsearch; /* decoded, NULL or malloc-ed */
+ struct route *routes; /* decoded, NULL or malloc-ed list */
+ long uptime; /* when complete configuration */
+ int pkt_fd; /* packet socket for this interface */
+ struct netdev *next; /* next configured i/f */
+};
+
+extern struct netdev *ifaces;
+
+/*
+ * Device capabilities
+ */
+#define CAP_BOOTP (1<<0)
+#define CAP_DHCP (1<<1)
+#define CAP_RARP (1<<2)
+
+/*
+ * Device states
+ */
+#define DEVST_UP 0
+#define DEVST_BOOTP 1
+#define DEVST_DHCPDISC 2
+#define DEVST_DHCPREQ 3
+#define DEVST_COMPLETE 4
+#define DEVST_ERROR 5
+
+int netdev_getflags(struct netdev *dev, short *flags);
+int netdev_setaddress(struct netdev *dev);
+int netdev_setroutes(struct netdev *dev);
+int netdev_up(struct netdev *dev);
+int netdev_down(struct netdev *dev);
+int netdev_init_if(struct netdev *dev);
+int netdev_setmtu(struct netdev *dev);
+
+static inline int netdev_running(struct netdev *dev)
+{
+ short flags;
+ int ret = netdev_getflags(dev, &flags);
+
+ return ret ? 0 : !!(flags & IFF_RUNNING);
+}
+
+static inline uint32_t netdev_genmask(uint32_t netmask_width)
+{
+ /* Map netmask width to network mask in network byte order.
+ Example: 24 -> "255.255.255.0" -> htonl(0xFFFFFF00) */
+ if (netmask_width == 0) {
+ return 0;
+ } else {
+ return htonl(~((1u << (32 - netmask_width)) - 1));
+ }
+}
+
+#endif /* IPCONFIG_NETDEV_H */
diff --git a/usr/kinit/ipconfig/packet.c b/usr/kinit/ipconfig/packet.c
new file mode 100644
index 0000000..2e1487d
--- /dev/null
+++ b/usr/kinit/ipconfig/packet.c
@@ -0,0 +1,278 @@
+#include <errno.h>/*XXX*/
+/*
+ * Packet socket handling glue.
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <net/if_packet.h>
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#include <netpacket/packet.h>
+#include <asm/byteorder.h>
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include "ipconfig.h"
+#include "netdev.h"
+#include "packet.h"
+
+uint16_t cfg_local_port = LOCAL_PORT;
+uint16_t cfg_remote_port = REMOTE_PORT;
+
+int packet_open(struct netdev *dev)
+{
+ struct sockaddr_ll sll;
+ int fd, rv, one = 1;
+
+ /*
+ * Get a PACKET socket for IP traffic.
+ */
+ fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
+ if (fd == -1) {
+ perror("socket");
+ return -1;
+ }
+
+ /*
+ * We want to broadcast
+ */
+ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &one,
+ sizeof(one)) == -1) {
+ perror("SO_BROADCAST");
+ close(fd);
+ return -1;
+ }
+
+ memset(&sll, 0, sizeof(sll));
+ sll.sll_family = AF_PACKET;
+ sll.sll_ifindex = dev->ifindex;
+
+ rv = bind(fd, (struct sockaddr *)&sll, sizeof(sll));
+ if (-1 == rv) {
+ perror("bind");
+ close(fd);
+ return -1;
+ }
+
+ dev->pkt_fd = fd;
+ return fd;
+}
+
+void packet_close(struct netdev *dev)
+{
+ close(dev->pkt_fd);
+ dev->pkt_fd = -1;
+}
+
+static unsigned int ip_checksum(uint16_t *hdr, int len)
+{
+ unsigned int chksum = 0;
+
+ while (len) {
+ chksum += *hdr++;
+ chksum += *hdr++;
+ len--;
+ }
+ chksum = (chksum & 0xffff) + (chksum >> 16);
+ chksum = (chksum & 0xffff) + (chksum >> 16);
+ return (~chksum) & 0xffff;
+}
+
+struct header {
+ struct iphdr ip;
+ struct udphdr udp;
+} __attribute__ ((packed, aligned(4)));
+
+static struct header ipudp_hdrs = {
+ .ip = {
+ .ihl = 5,
+ .version = IPVERSION,
+ .frag_off = __constant_htons(IP_DF),
+ .ttl = 64,
+ .protocol = IPPROTO_UDP,
+ .saddr = INADDR_ANY,
+ .daddr = INADDR_BROADCAST,
+ },
+ .udp = {
+ .source = __constant_htons(LOCAL_PORT),
+ .dest = __constant_htons(REMOTE_PORT),
+ .len = 0,
+ .check = 0,
+ },
+};
+
+#ifdef DEBUG /* Only used with dprintf() */
+static char *ntoa(uint32_t addr)
+{
+ struct in_addr in = { addr };
+ return inet_ntoa(in);
+}
+#endif /* DEBUG */
+
+/*
+ * Send a packet. The options are listed in iov[1...iov_len-1].
+ * iov[0] is reserved for the bootp packet header.
+ */
+int packet_send(struct netdev *dev, struct iovec *iov, int iov_len)
+{
+ struct sockaddr_ll sll;
+ struct msghdr msg;
+ int i, len = 0;
+
+ memset(&sll, 0, sizeof(sll));
+ msg.msg_name = &sll;
+ msg.msg_namelen = sizeof(sll);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = iov_len;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+
+ if (cfg_local_port != LOCAL_PORT) {
+ ipudp_hdrs.udp.source = htons(cfg_local_port);
+ ipudp_hdrs.udp.dest = htons(cfg_remote_port);
+ }
+
+ dprintf("\n udp src %d dst %d", ntohs(ipudp_hdrs.udp.source),
+ ntohs(ipudp_hdrs.udp.dest));
+
+ dprintf("\n ip src %s ", ntoa(ipudp_hdrs.ip.saddr));
+ dprintf("dst %s ", ntoa(ipudp_hdrs.ip.daddr));
+
+ /*
+ * Glue in the ip+udp header iovec
+ */
+ iov[0].iov_base = &ipudp_hdrs;
+ iov[0].iov_len = sizeof(struct header);
+
+ for (i = 0; i < iov_len; i++)
+ len += iov[i].iov_len;
+
+ sll.sll_family = AF_PACKET;
+ sll.sll_protocol = htons(ETH_P_IP);
+ sll.sll_ifindex = dev->ifindex;
+ sll.sll_hatype = dev->hwtype;
+ sll.sll_pkttype = PACKET_BROADCAST;
+ sll.sll_halen = dev->hwlen;
+ memcpy(sll.sll_addr, dev->hwbrd, dev->hwlen);
+
+ ipudp_hdrs.ip.tot_len = htons(len);
+ ipudp_hdrs.ip.check = 0;
+ ipudp_hdrs.ip.check = ip_checksum((uint16_t *) &ipudp_hdrs.ip,
+ ipudp_hdrs.ip.ihl);
+
+ ipudp_hdrs.udp.len = htons(len - sizeof(struct iphdr));
+
+ dprintf("\n bytes %d\n", len);
+
+ return sendmsg(dev->pkt_fd, &msg, 0);
+}
+
+void packet_discard(struct netdev *dev)
+{
+ struct iphdr iph;
+ struct sockaddr_ll sll;
+ socklen_t sllen = sizeof(sll);
+
+ sll.sll_ifindex = dev->ifindex;
+
+ recvfrom(dev->pkt_fd, &iph, sizeof(iph), 0,
+ (struct sockaddr *)&sll, &sllen);
+}
+
+/*
+ * Receive a bootp packet. The options are listed in iov[1...iov_len].
+ * iov[0] must point to the bootp packet header.
+ * Returns:
+ * -1 = Error, try again later
+* 0 = Discarded packet (non-DHCP/BOOTP traffic)
+ * >0 = Size of packet
+ */
+int packet_recv(struct netdev *dev, struct iovec *iov, int iov_len)
+{
+ struct iphdr *ip, iph;
+ struct udphdr *udp;
+ struct msghdr msg = {
+ .msg_name = NULL,
+ .msg_namelen = 0,
+ .msg_iov = iov,
+ .msg_iovlen = iov_len,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_flags = 0
+ };
+ int ret, iphl;
+ struct sockaddr_ll sll;
+ socklen_t sllen = sizeof(sll);
+
+ sll.sll_ifindex = dev->ifindex;
+ msg.msg_name = &sll;
+ msg.msg_namelen = sllen;
+
+ ret = recvfrom(dev->pkt_fd, &iph, sizeof(struct iphdr),
+ MSG_PEEK, (struct sockaddr *)&sll, &sllen);
+ if (ret == -1)
+ return -1;
+
+ if (iph.ihl < 5 || iph.version != IPVERSION)
+ goto discard_pkt;
+
+ iphl = iph.ihl * 4;
+
+ ip = malloc(iphl + sizeof(struct udphdr));
+ if (!ip)
+ goto discard_pkt;
+
+ udp = (struct udphdr *)((char *)ip + iphl);
+
+ iov[0].iov_base = ip;
+ iov[0].iov_len = iphl + sizeof(struct udphdr);
+
+ ret = recvmsg(dev->pkt_fd, &msg, 0);
+ if (ret == -1)
+ goto free_pkt;
+
+ dprintf("<- bytes %d ", ret);
+
+ if (ip_checksum((uint16_t *) ip, ip->ihl) != 0)
+ goto free_pkt;
+
+ dprintf("\n ip src %s ", ntoa(ip->saddr));
+ dprintf("dst %s ", ntoa(ip->daddr));
+
+ if (ntohs(ip->tot_len) > ret || ip->protocol != IPPROTO_UDP)
+ goto free_pkt;
+
+ ret -= 4 * ip->ihl;
+
+ dprintf("\n udp src %d dst %d ", ntohs(udp->source),
+ ntohs(udp->dest));
+
+ if (udp->source != htons(cfg_remote_port) ||
+ udp->dest != htons(cfg_local_port))
+ goto free_pkt;
+
+ if (ntohs(udp->len) > ret)
+ goto free_pkt;
+
+ ret -= sizeof(struct udphdr);
+
+ free(ip);
+
+ return ret;
+
+free_pkt:
+ dprintf("freed\n");
+ free(ip);
+ return 0;
+
+discard_pkt:
+ dprintf("discarded\n");
+ packet_discard(dev);
+ return 0;
+}
diff --git a/usr/kinit/ipconfig/packet.h b/usr/kinit/ipconfig/packet.h
new file mode 100644
index 0000000..4367efe
--- /dev/null
+++ b/usr/kinit/ipconfig/packet.h
@@ -0,0 +1,12 @@
+#ifndef IPCONFIG_PACKET_H
+#define IPCONFIG_PACKET_H
+
+struct iovec;
+
+int packet_open(struct netdev *dev);
+void packet_close(struct netdev *dev);
+int packet_send(struct netdev *dev, struct iovec *iov, int iov_len);
+void packet_discard(struct netdev *dev);
+int packet_recv(struct netdev *dev, struct iovec *iov, int iov_len);
+
+#endif /* IPCONFIG_PACKET_H */