summaryrefslogtreecommitdiff
path: root/usr/src/boot/lib/libstand/bootp.c
diff options
context:
space:
mode:
authorJerry Jelinek <jerry.jelinek@joyent.com>2016-09-26 13:27:55 +0000
committerJerry Jelinek <jerry.jelinek@joyent.com>2016-09-26 13:27:55 +0000
commit0264fb65b613e5b2c44f273fa48b26bebe491074 (patch)
treea375b79dd7543bcf0571578b2189d37168c37c57 /usr/src/boot/lib/libstand/bootp.c
parentd21e83058c8edeb1becd9202380d088cb056f0c4 (diff)
parentf76886de6cd6914424d9f6c25bd9d93d87889269 (diff)
downloadillumos-joyent-0264fb65b613e5b2c44f273fa48b26bebe491074.tar.gz
[illumos-gate merge]
commit f76886de6cd6914424d9f6c25bd9d93d87889269 7402 Create tunable to ignore hole_birth feature commit 5bdf86e2a288d6c81a0bcc50a98699f52557bab6 7401 loader.4th is missing newline commit ed4e7a6a5cbc5e8986dc649ad54435210487b102 7340 receive manual origin should override automatic origin commit b021ac0b78f8df3d9c421783d9a323723df84925 7337 inherit_001_pos occasionally times out commit c166b69d29138aed7a415fe7cef698e54c6ae945 7254 ztest failed assertion in ztest_dataset_dirobj_verify: dirobjs + 1 == usedobjs commit 754998c8d410b7b7ddefbfa4de310a030e0c7ce1 7253 ztest failure: dsl_destroy_head(name) == 0 (0x10 == 0x0), file ../ztest.c, line 3235 commit 4220fdc152e5dfec9a1dd51452175295f3684689 7398 zfs test zfs_get_005_neg does not work as expected commit d5f26ad8122c3762fb16413a17bfb497db86a782 5142 libzfs support raidz root pool (loader project) commit c8811bd3e2427dddbac6c05a59cfe117d8fea370 5120 zfs should allow large block/gzip/raidz boot pool (loader project) commit 12e3fba22ec759e9dd8f9564fad79541275b2aa5 6709 manual pages need to be updated for loader (loader project) commit fa0c327afe484fa5ff164fb81ff93715dd6573f8 6706 disable grub menu management in bootadm (loader project) 6707 disable grub menu management in libbe (loader project) commit 9abc7a578aecf9064f46563361e8f856b4bdc35e 6705 halt: replace grub_get_boot_args with be_get_boot_args (loader project) commit a6424c753d6e2f0f04fb65b11e9f9c04445ccbae 6704 svc.startd: replace grub_get_boot_args with be_get_boot_args (loader project) commit c262cbbc8301f7c884fd4800056ee51ba75d931c 6703 update bootadm to support loader configuration (loader project) 6708 update eeprom for loader (loader project) commit ce3cb817f67103796b730fd322174dddefd9a9a8 6702 libbe should support x86 installboot command (loader project) commit 0c946d80993858b7b1314e0b31773e48500e03fb 6701 add installboot to i386 platform (loader project) commit 1386b601c0c7f5c89a9325b8a1e34037304e8119 6700 remove installboot script from i386 platform (loader project) commit f5e5a2c4965aa1013184568ca3140cdcba93e44b 6699 be_get_boot_args interface implementation in libbe (loader project) commit 199767f8919635c4928607450d9e0abb932109ce 5061 freebsd boot loader integration (loader project) commit 0cc5983c8a077e6396dc7c492ee928b40bf0fed1 6698 freebsd btxld port for illumos (loader project) commit afc2ba1deb75b323afde536f2dd18bcafdaa308d 6185 want ficl scripting engine in illumos (loader project) Conflicts: exception_lists/cstyle exception_lists/hdrchk exception_lists/copyright
Diffstat (limited to 'usr/src/boot/lib/libstand/bootp.c')
-rw-r--r--usr/src/boot/lib/libstand/bootp.c749
1 files changed, 749 insertions, 0 deletions
diff --git a/usr/src/boot/lib/libstand/bootp.c b/usr/src/boot/lib/libstand/bootp.c
new file mode 100644
index 0000000000..1af7bd5da1
--- /dev/null
+++ b/usr/src/boot/lib/libstand/bootp.c
@@ -0,0 +1,749 @@
+/* $NetBSD: bootp.c,v 1.14 1998/02/16 11:10:54 drochner Exp $ */
+
+/*
+ * Copyright (c) 1992 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
+ * contributed to Berkeley.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#) Header: bootp.c,v 1.4 93/09/11 03:13:51 leres Exp (LBL)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include <string.h>
+
+#define BOOTP_DEBUGxx
+#define SUPPORT_DHCP
+
+#define DHCP_ENV_NOVENDOR 1 /* do not parse vendor options */
+#define DHCP_ENV_PXE 10 /* assume pxe vendor options */
+#define DHCP_ENV_FREEBSD 11 /* assume freebsd vendor options */
+/* set DHCP_ENV to one of the values above to export dhcp options to kenv */
+#define DHCP_ENV DHCP_ENV_NO_VENDOR
+
+#include "stand.h"
+#include "net.h"
+#include "netif.h"
+#include "bootp.h"
+
+
+struct in_addr servip;
+
+static n_long nmask, smask;
+
+static time_t bot;
+
+static char vm_rfc1048[4] = VM_RFC1048;
+#ifdef BOOTP_VEND_CMU
+static char vm_cmu[4] = VM_CMU;
+#endif
+
+/* Local forwards */
+static ssize_t bootpsend(struct iodesc *, void *, size_t);
+static ssize_t bootprecv(struct iodesc *, void *, size_t, time_t);
+static int vend_rfc1048(u_char *, u_int);
+#ifdef BOOTP_VEND_CMU
+static void vend_cmu(u_char *);
+#endif
+
+#ifdef DHCP_ENV /* export the dhcp response to kenv */
+struct dhcp_opt;
+static void setenv_(u_char *cp, u_char *ep, struct dhcp_opt *opts);
+#else
+#define setenv_(a, b, c)
+#endif
+
+#ifdef SUPPORT_DHCP
+static char expected_dhcpmsgtype = -1, dhcp_ok;
+struct in_addr dhcp_serverip;
+#endif
+
+/* Fetch required bootp infomation */
+void
+bootp(sock, flag)
+ int sock;
+ int flag;
+{
+ struct iodesc *d;
+ struct bootp *bp;
+ struct {
+ u_char header[HEADER_SIZE];
+ struct bootp wbootp;
+ } wbuf;
+ struct {
+ u_char header[HEADER_SIZE];
+ struct bootp rbootp;
+ } rbuf;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootp: socket=%d\n", sock);
+#endif
+ if (!bot)
+ bot = getsecs();
+
+ if (!(d = socktodesc(sock))) {
+ printf("bootp: bad socket. %d\n", sock);
+ return;
+ }
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootp: d=%lx\n", (long)d);
+#endif
+
+ bp = &wbuf.wbootp;
+ bzero(bp, sizeof(*bp));
+
+ bp->bp_op = BOOTREQUEST;
+ bp->bp_htype = 1; /* 10Mb Ethernet (48 bits) */
+ bp->bp_hlen = 6;
+ bp->bp_xid = htonl(d->xid);
+ MACPY(d->myea, bp->bp_chaddr);
+ strncpy(bp->bp_file, bootfile, sizeof(bp->bp_file));
+ bcopy(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048));
+#ifdef SUPPORT_DHCP
+ bp->bp_vend[4] = TAG_DHCP_MSGTYPE;
+ bp->bp_vend[5] = 1;
+ bp->bp_vend[6] = DHCPDISCOVER;
+
+ /*
+ * If we are booting from PXE, we want to send the string
+ * 'PXEClient' to the DHCP server so you have the option of
+ * only responding to PXE aware dhcp requests.
+ */
+ if (flag & BOOTP_PXE) {
+ bp->bp_vend[7] = TAG_CLASSID;
+ bp->bp_vend[8] = 9;
+ bcopy("PXEClient", &bp->bp_vend[9], 9);
+ bp->bp_vend[18] = TAG_END;
+ } else
+ bp->bp_vend[7] = TAG_END;
+#else
+ bp->bp_vend[4] = TAG_END;
+#endif
+
+ d->myip.s_addr = INADDR_ANY;
+ d->myport = htons(IPPORT_BOOTPC);
+ d->destip.s_addr = INADDR_BROADCAST;
+ d->destport = htons(IPPORT_BOOTPS);
+
+#ifdef SUPPORT_DHCP
+ expected_dhcpmsgtype = DHCPOFFER;
+ dhcp_ok = 0;
+#endif
+
+ if(sendrecv(d,
+ bootpsend, bp, sizeof(*bp),
+ bootprecv, &rbuf.rbootp, sizeof(rbuf.rbootp))
+ == -1) {
+ printf("bootp: no reply\n");
+ return;
+ }
+
+#ifdef SUPPORT_DHCP
+ if(dhcp_ok) {
+ u_int32_t leasetime;
+ bp->bp_vend[6] = DHCPREQUEST;
+ bp->bp_vend[7] = TAG_REQ_ADDR;
+ bp->bp_vend[8] = 4;
+ bcopy(&rbuf.rbootp.bp_yiaddr, &bp->bp_vend[9], 4);
+ bp->bp_vend[13] = TAG_SERVERID;
+ bp->bp_vend[14] = 4;
+ bcopy(&dhcp_serverip.s_addr, &bp->bp_vend[15], 4);
+ bp->bp_vend[19] = TAG_LEASETIME;
+ bp->bp_vend[20] = 4;
+ leasetime = htonl(300);
+ bcopy(&leasetime, &bp->bp_vend[21], 4);
+ if (flag & BOOTP_PXE) {
+ bp->bp_vend[25] = TAG_CLASSID;
+ bp->bp_vend[26] = 9;
+ bcopy("PXEClient", &bp->bp_vend[27], 9);
+ bp->bp_vend[36] = TAG_END;
+ } else
+ bp->bp_vend[25] = TAG_END;
+
+ expected_dhcpmsgtype = DHCPACK;
+
+ if(sendrecv(d,
+ bootpsend, bp, sizeof(*bp),
+ bootprecv, &rbuf.rbootp, sizeof(rbuf.rbootp))
+ == -1) {
+ printf("DHCPREQUEST failed\n");
+ return;
+ }
+ }
+#endif
+
+ myip = d->myip = rbuf.rbootp.bp_yiaddr;
+ servip = rbuf.rbootp.bp_siaddr;
+ if(rootip.s_addr == INADDR_ANY) rootip = servip;
+ bcopy(rbuf.rbootp.bp_file, bootfile, sizeof(bootfile));
+ bootfile[sizeof(bootfile) - 1] = '\0';
+
+ if (IN_CLASSA(ntohl(myip.s_addr)))
+ nmask = htonl(IN_CLASSA_NET);
+ else if (IN_CLASSB(ntohl(myip.s_addr)))
+ nmask = htonl(IN_CLASSB_NET);
+ else
+ nmask = htonl(IN_CLASSC_NET);
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("'native netmask' is %s\n", intoa(nmask));
+#endif
+
+ /* Check subnet mask against net mask; toss if bogus */
+ if ((nmask & smask) != nmask) {
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("subnet mask (%s) bad\n", intoa(smask));
+#endif
+ smask = 0;
+ }
+
+ /* Get subnet (or natural net) mask */
+ netmask = nmask;
+ if (smask)
+ netmask = smask;
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("mask: %s\n", intoa(netmask));
+#endif
+
+ /* We need a gateway if root is on a different net */
+ if (!SAMENET(myip, rootip, netmask)) {
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("need gateway for root ip\n");
+#endif
+ }
+
+ /* Toss gateway if on a different net */
+ if (!SAMENET(myip, gateip, netmask)) {
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("gateway ip (%s) bad\n", inet_ntoa(gateip));
+#endif
+ gateip.s_addr = 0;
+ }
+
+ /* Bump xid so next request will be unique. */
+ ++d->xid;
+}
+
+/* Transmit a bootp request */
+static ssize_t
+bootpsend(d, pkt, len)
+ struct iodesc *d;
+ void *pkt;
+ size_t len;
+{
+ struct bootp *bp;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootpsend: d=%lx called.\n", (long)d);
+#endif
+
+ bp = pkt;
+ bp->bp_secs = htons((u_short)(getsecs() - bot));
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootpsend: calling sendudp\n");
+#endif
+
+ return (sendudp(d, pkt, len));
+}
+
+static ssize_t
+bootprecv(d, pkt, len, tleft)
+struct iodesc *d;
+void *pkt;
+size_t len;
+time_t tleft;
+{
+ ssize_t n;
+ struct bootp *bp;
+
+#ifdef BOOTP_DEBUGx
+ if (debug)
+ printf("bootp_recvoffer: called\n");
+#endif
+
+ n = readudp(d, pkt, len, tleft);
+ if (n == -1 || n < sizeof(struct bootp) - BOOTP_VENDSIZE)
+ goto bad;
+
+ bp = (struct bootp *)pkt;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootprecv: checked. bp = 0x%lx, n = %d\n",
+ (long)bp, (int)n);
+#endif
+ if (bp->bp_xid != htonl(d->xid)) {
+#ifdef BOOTP_DEBUG
+ if (debug) {
+ printf("bootprecv: expected xid 0x%lx, got 0x%x\n",
+ d->xid, ntohl(bp->bp_xid));
+ }
+#endif
+ goto bad;
+ }
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("bootprecv: got one!\n");
+#endif
+
+ /* Suck out vendor info */
+ if (bcmp(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)) == 0) {
+ if(vend_rfc1048(bp->bp_vend, sizeof(bp->bp_vend)) != 0)
+ goto bad;
+ }
+#ifdef BOOTP_VEND_CMU
+ else if (bcmp(vm_cmu, bp->bp_vend, sizeof(vm_cmu)) == 0)
+ vend_cmu(bp->bp_vend);
+#endif
+ else
+ printf("bootprecv: unknown vendor 0x%lx\n", (long)bp->bp_vend);
+
+ return(n);
+bad:
+ errno = 0;
+ return (-1);
+}
+
+static int
+vend_rfc1048(cp, len)
+ u_char *cp;
+ u_int len;
+{
+ u_char *ep;
+ int size;
+ u_char tag;
+ const char *val;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("vend_rfc1048 bootp info. len=%d\n", len);
+#endif
+ ep = cp + len;
+
+ /* Step over magic cookie */
+ cp += sizeof(int);
+
+ setenv_(cp, ep, NULL);
+
+ while (cp < ep) {
+ tag = *cp++;
+ size = *cp++;
+ if (tag == TAG_END)
+ break;
+
+ if (tag == TAG_SUBNET_MASK) {
+ bcopy(cp, &smask, sizeof(smask));
+ }
+ if (tag == TAG_GATEWAY) {
+ bcopy(cp, &gateip.s_addr, sizeof(gateip.s_addr));
+ }
+ if (tag == TAG_SWAPSERVER) {
+ /* let it override bp_siaddr */
+ bcopy(cp, &rootip.s_addr, sizeof(rootip.s_addr));
+ }
+ if (tag == TAG_ROOTPATH) {
+ if ((val = getenv("dhcp.root-path")) == NULL)
+ val = (const char *)cp;
+ strlcpy(rootpath, val, sizeof(rootpath));
+ }
+ if (tag == TAG_HOSTNAME) {
+ if ((val = getenv("dhcp.host-name")) == NULL)
+ val = (const char *)cp;
+ strlcpy(hostname, val, sizeof(hostname));
+ }
+#ifdef SUPPORT_DHCP
+ if (tag == TAG_DHCP_MSGTYPE) {
+ if(*cp != expected_dhcpmsgtype)
+ return(-1);
+ dhcp_ok = 1;
+ }
+ if (tag == TAG_SERVERID) {
+ bcopy(cp, &dhcp_serverip.s_addr,
+ sizeof(dhcp_serverip.s_addr));
+ }
+#endif
+ cp += size;
+ }
+ return(0);
+}
+
+#ifdef BOOTP_VEND_CMU
+static void
+vend_cmu(cp)
+ u_char *cp;
+{
+ struct cmu_vend *vp;
+
+#ifdef BOOTP_DEBUG
+ if (debug)
+ printf("vend_cmu bootp info.\n");
+#endif
+ vp = (struct cmu_vend *)cp;
+
+ if (vp->v_smask.s_addr != 0) {
+ smask = vp->v_smask.s_addr;
+ }
+ if (vp->v_dgate.s_addr != 0) {
+ gateip = vp->v_dgate;
+ }
+}
+#endif
+
+#ifdef DHCP_ENV
+/*
+ * Parse DHCP options and store them into kenv variables.
+ * Original code from Danny Braniss, modifications by Luigi Rizzo.
+ *
+ * The parser is driven by tables which specify the type and name of
+ * each dhcp option and how it appears in kenv.
+ * The first entry in the list contains the prefix used to set the kenv
+ * name (including the . if needed), the last entry must have a 0 tag.
+ * Entries do not need to be sorted though it helps for readability.
+ *
+ * Certain vendor-specific tables can be enabled according to DHCP_ENV.
+ * Set it to 0 if you don't want any.
+ */
+enum opt_fmt { __NONE = 0,
+ __8 = 1, __16 = 2, __32 = 4, /* Unsigned fields, value=size */
+ __IP, /* IPv4 address */
+ __TXT, /* C string */
+ __BYTES, /* byte sequence, printed %02x */
+ __INDIR, /* name=value */
+ __ILIST, /* name=value;name=value ... */
+ __VE, /* vendor specific, recurse */
+};
+
+struct dhcp_opt {
+ uint8_t tag;
+ uint8_t fmt;
+ const char *desc;
+};
+
+static struct dhcp_opt vndr_opt[] = { /* Vendor Specific Options */
+#if DHCP_ENV == DHCP_ENV_FREEBSD /* FreeBSD table in the original code */
+ {0, 0, "FreeBSD"}, /* prefix */
+ {1, __TXT, "kernel"},
+ {2, __TXT, "kernelname"},
+ {3, __TXT, "kernel_options"},
+ {4, __IP, "usr-ip"},
+ {5, __TXT, "conf-path"},
+ {6, __TXT, "rc.conf0"},
+ {7, __TXT, "rc.conf1"},
+ {8, __TXT, "rc.conf2"},
+ {9, __TXT, "rc.conf3"},
+ {10, __TXT, "rc.conf4"},
+ {11, __TXT, "rc.conf5"},
+ {12, __TXT, "rc.conf6"},
+ {13, __TXT, "rc.conf7"},
+ {14, __TXT, "rc.conf8"},
+ {15, __TXT, "rc.conf9"},
+
+ {20, __TXT, "boot.nfsroot.options"},
+
+ {245, __INDIR, ""},
+ {246, __INDIR, ""},
+ {247, __INDIR, ""},
+ {248, __INDIR, ""},
+ {249, __INDIR, ""},
+ {250, __INDIR, ""},
+ {251, __INDIR, ""},
+ {252, __INDIR, ""},
+ {253, __INDIR, ""},
+ {254, __INDIR, ""},
+
+#elif DHCP_ENV == DHCP_ENV_PXE /* some pxe options, RFC4578 */
+ {0, 0, "pxe"}, /* prefix */
+ {93, __16, "system-architecture"},
+ {94, __BYTES, "network-interface"},
+ {97, __BYTES, "machine-identifier"},
+#else /* default (empty) table */
+ {0, 0, "dhcp.vendor."}, /* prefix */
+#endif
+ {0, __TXT, "%soption-%d"}
+};
+
+static struct dhcp_opt dhcp_opt[] = {
+ /* DHCP Option names, formats and codes, from RFC2132. */
+ {0, 0, "dhcp."}, // prefix
+ {1, __IP, "subnet-mask"},
+ {2, __32, "time-offset"}, /* this is signed */
+ {3, __IP, "routers"},
+ {4, __IP, "time-servers"},
+ {5, __IP, "ien116-name-servers"},
+ {6, __IP, "domain-name-servers"},
+ {7, __IP, "log-servers"},
+ {8, __IP, "cookie-servers"},
+ {9, __IP, "lpr-servers"},
+ {10, __IP, "impress-servers"},
+ {11, __IP, "resource-location-servers"},
+ {12, __TXT, "host-name"},
+ {13, __16, "boot-size"},
+ {14, __TXT, "merit-dump"},
+ {15, __TXT, "domain-name"},
+ {16, __IP, "swap-server"},
+ {17, __TXT, "root-path"},
+ {18, __TXT, "extensions-path"},
+ {19, __8, "ip-forwarding"},
+ {20, __8, "non-local-source-routing"},
+ {21, __IP, "policy-filter"},
+ {22, __16, "max-dgram-reassembly"},
+ {23, __8, "default-ip-ttl"},
+ {24, __32, "path-mtu-aging-timeout"},
+ {25, __16, "path-mtu-plateau-table"},
+ {26, __16, "interface-mtu"},
+ {27, __8, "all-subnets-local"},
+ {28, __IP, "broadcast-address"},
+ {29, __8, "perform-mask-discovery"},
+ {30, __8, "mask-supplier"},
+ {31, __8, "perform-router-discovery"},
+ {32, __IP, "router-solicitation-address"},
+ {33, __IP, "static-routes"},
+ {34, __8, "trailer-encapsulation"},
+ {35, __32, "arp-cache-timeout"},
+ {36, __8, "ieee802-3-encapsulation"},
+ {37, __8, "default-tcp-ttl"},
+ {38, __32, "tcp-keepalive-interval"},
+ {39, __8, "tcp-keepalive-garbage"},
+ {40, __TXT, "nis-domain"},
+ {41, __IP, "nis-servers"},
+ {42, __IP, "ntp-servers"},
+ {43, __VE, "vendor-encapsulated-options"},
+ {44, __IP, "netbios-name-servers"},
+ {45, __IP, "netbios-dd-server"},
+ {46, __8, "netbios-node-type"},
+ {47, __TXT, "netbios-scope"},
+ {48, __IP, "x-font-servers"},
+ {49, __IP, "x-display-managers"},
+ {50, __IP, "dhcp-requested-address"},
+ {51, __32, "dhcp-lease-time"},
+ {52, __8, "dhcp-option-overload"},
+ {53, __8, "dhcp-message-type"},
+ {54, __IP, "dhcp-server-identifier"},
+ {55, __8, "dhcp-parameter-request-list"},
+ {56, __TXT, "dhcp-message"},
+ {57, __16, "dhcp-max-message-size"},
+ {58, __32, "dhcp-renewal-time"},
+ {59, __32, "dhcp-rebinding-time"},
+ {60, __TXT, "vendor-class-identifier"},
+ {61, __TXT, "dhcp-client-identifier"},
+ {64, __TXT, "nisplus-domain"},
+ {65, __IP, "nisplus-servers"},
+ {66, __TXT, "tftp-server-name"},
+ {67, __TXT, "bootfile-name"},
+ {68, __IP, "mobile-ip-home-agent"},
+ {69, __IP, "smtp-server"},
+ {70, __IP, "pop-server"},
+ {71, __IP, "nntp-server"},
+ {72, __IP, "www-server"},
+ {73, __IP, "finger-server"},
+ {74, __IP, "irc-server"},
+ {75, __IP, "streettalk-server"},
+ {76, __IP, "streettalk-directory-assistance-server"},
+ {77, __TXT, "user-class"},
+ {85, __IP, "nds-servers"},
+ {86, __TXT, "nds-tree-name"},
+ {87, __TXT, "nds-context"},
+ {210, __TXT, "authenticate"},
+
+ /* use the following entries for arbitrary variables */
+ {246, __ILIST, ""},
+ {247, __ILIST, ""},
+ {248, __ILIST, ""},
+ {249, __ILIST, ""},
+ {250, __INDIR, ""},
+ {251, __INDIR, ""},
+ {252, __INDIR, ""},
+ {253, __INDIR, ""},
+ {254, __INDIR, ""},
+ {0, __TXT, "%soption-%d"}
+};
+
+/*
+ * parse a dhcp response, set environment variables translating options
+ * names and values according to the tables above. Also set dhcp.tags
+ * to the list of selected tags.
+ */
+static void
+setenv_(u_char *cp, u_char *ep, struct dhcp_opt *opts)
+{
+ u_char *ncp;
+ u_char tag;
+ char tags[512], *tp; /* the list of tags */
+
+#define FLD_SEP ',' /* separator in list of elements */
+ ncp = cp;
+ tp = tags;
+ if (opts == NULL)
+ opts = dhcp_opt;
+
+ while (ncp < ep) {
+ unsigned int size; /* option size */
+ char *vp, *endv, buf[256]; /* the value buffer */
+ struct dhcp_opt *op;
+
+ tag = *ncp++; /* extract tag and size */
+ size = *ncp++;
+ cp = ncp; /* current payload */
+ ncp += size; /* point to the next option */
+
+ if (tag == TAG_END)
+ break;
+ if (tag == 0)
+ continue;
+
+ for (op = opts+1; op->tag && op->tag != tag; op++)
+ ;
+ /* if not found we end up on the default entry */
+
+ /*
+ * Copy data into the buffer. libstand does not have snprintf so we
+ * need to be careful with sprintf(). With strings, the source is
+ * always <256 char so shorter than the buffer so we are safe; with
+ * other arguments, the longest string is inet_ntoa which is 16 bytes
+ * so we make sure to have always enough room in the string before
+ * trying an sprint.
+ */
+ vp = buf;
+ *vp = '\0';
+ endv = buf + sizeof(buf) - 1 - 16; /* last valid write position */
+
+ switch(op->fmt) {
+ case __NONE:
+ break; /* should not happen */
+
+ case __VE: /* recurse, vendor specific */
+ setenv_(cp, cp+size, vndr_opt);
+ break;
+
+ case __IP: /* ip address */
+ for (; size > 0 && vp < endv; size -= 4, cp += 4) {
+ struct in_addr in_ip; /* ip addresses */
+ if (vp != buf)
+ *vp++ = FLD_SEP;
+ bcopy(cp, &in_ip.s_addr, sizeof(in_ip.s_addr));
+ sprintf(vp, "%s", inet_ntoa(in_ip));
+ vp += strlen(vp);
+ }
+ break;
+
+ case __BYTES: /* opaque byte string */
+ for (; size > 0 && vp < endv; size -= 1, cp += 1) {
+ sprintf(vp, "%02x", *cp);
+ vp += strlen(vp);
+ }
+ break;
+
+ case __TXT:
+ bcopy(cp, buf, size); /* cannot overflow */
+ buf[size] = 0;
+ break;
+
+ case __32:
+ case __16:
+ case __8: /* op->fmt is also the length of each field */
+ for (; size > 0 && vp < endv; size -= op->fmt, cp += op->fmt) {
+ uint32_t v;
+ if (op->fmt == __32)
+ v = (cp[0]<<24) + (cp[1]<<16) + (cp[2]<<8) + cp[3];
+ else if (op->fmt == __16)
+ v = (cp[0]<<8) + cp[1];
+ else
+ v = cp[0];
+ if (vp != buf)
+ *vp++ = FLD_SEP;
+ sprintf(vp, "%u", v);
+ vp += strlen(vp);
+ }
+ break;
+
+ case __INDIR: /* name=value */
+ case __ILIST: /* name=value;name=value... */
+ bcopy(cp, buf, size); /* cannot overflow */
+ buf[size] = '\0';
+ for (endv = buf; endv; endv = vp) {
+ u_char *s = NULL; /* semicolon ? */
+
+ /* skip leading whitespace */
+ while (*endv && strchr(" \t\n\r", *endv))
+ endv++;
+ vp = strchr(endv, '='); /* find name=value separator */
+ if (!vp)
+ break;
+ *vp++ = 0;
+ if (op->fmt == __ILIST && (s = strchr(vp, ';')))
+ *s++ = '\0';
+ setenv(endv, vp, 1);
+ vp = s; /* prepare for next round */
+ }
+ buf[0] = '\0'; /* option already done */
+ }
+
+ if (tp - tags < sizeof(tags) - 5) { /* add tag to the list */
+ if (tp != tags)
+ *tp++ = FLD_SEP;
+ sprintf(tp, "%d", tag);
+ tp += strlen(tp);
+ }
+ if (buf[0]) {
+ char env[128]; /* the string name */
+
+ if (op->tag == 0)
+ sprintf(env, op->desc, opts[0].desc, tag);
+ else
+ sprintf(env, "%s%s", opts[0].desc, op->desc);
+ /*
+ * Do not replace existing values in the environment, so that
+ * locally-obtained values can override server-provided values.
+ */
+ setenv(env, buf, 0);
+ }
+ }
+ if (tp != tags) {
+ char env[128]; /* the string name */
+ sprintf(env, "%stags", opts[0].desc);
+ setenv(env, tags, 1);
+ }
+}
+#endif /* additional dhcp */