summaryrefslogtreecommitdiffstats
path: root/usr/kinit/ipconfig/dhcp_proto.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/kinit/ipconfig/dhcp_proto.c')
-rw-r--r--usr/kinit/ipconfig/dhcp_proto.c301
1 files changed, 301 insertions, 0 deletions
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);
+}