From 2f0649f6fe411d7e07c8d56cf8ea56db53536da8 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:06:04 +0200 Subject: Adding upstream version 2.0.13. Signed-off-by: Daniel Baumann --- usr/kinit/ipconfig/dhcp_proto.c | 301 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 usr/kinit/ipconfig/dhcp_proto.c (limited to 'usr/kinit/ipconfig/dhcp_proto.c') 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 +#include +#include +#include +#include +#include + +#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); +} -- cgit v1.2.3