138451Smsmith/*	$NetBSD: bootp.c,v 1.14 1998/02/16 11:10:54 drochner Exp $	*/
238451Smsmith
338451Smsmith/*
438451Smsmith * Copyright (c) 1992 Regents of the University of California.
538451Smsmith * All rights reserved.
638451Smsmith *
738451Smsmith * This software was developed by the Computer Systems Engineering group
838451Smsmith * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
938451Smsmith * contributed to Berkeley.
1038451Smsmith *
1138451Smsmith * Redistribution and use in source and binary forms, with or without
1238451Smsmith * modification, are permitted provided that the following conditions
1338451Smsmith * are met:
1438451Smsmith * 1. Redistributions of source code must retain the above copyright
1538451Smsmith *    notice, this list of conditions and the following disclaimer.
1638451Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1738451Smsmith *    notice, this list of conditions and the following disclaimer in the
1838451Smsmith *    documentation and/or other materials provided with the distribution.
1938451Smsmith * 4. Neither the name of the University nor the names of its contributors
2038451Smsmith *    may be used to endorse or promote products derived from this software
2138451Smsmith *    without specific prior written permission.
2238451Smsmith *
2338451Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2438451Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2538451Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2638451Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2738451Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2838451Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2938451Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3038451Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3138451Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3238451Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3338451Smsmith * SUCH DAMAGE.
3438451Smsmith *
3538451Smsmith * @(#) Header: bootp.c,v 1.4 93/09/11 03:13:51 leres Exp  (LBL)
3638451Smsmith */
3738451Smsmith
3884221Sdillon#include <sys/cdefs.h>
3984221Sdillon__FBSDID("$FreeBSD$");
4084221Sdillon
4138451Smsmith#include <sys/types.h>
4238451Smsmith#include <netinet/in.h>
4338451Smsmith#include <netinet/in_systm.h>
4438451Smsmith
4538451Smsmith#include <string.h>
4638451Smsmith
4738451Smsmith#define BOOTP_DEBUGxx
4838451Smsmith#define SUPPORT_DHCP
4938451Smsmith
50185643Sluigi#define	DHCP_ENV_NOVENDOR	1	/* do not parse vendor options */
51185643Sluigi#define	DHCP_ENV_PXE		10	/* assume pxe vendor options */
52193189Sed#define	DHCP_ENV_FREEBSD	11	/* assume freebsd vendor options */
53185643Sluigi/* set DHCP_ENV to one of the values above to export dhcp options to kenv */
54185643Sluigi#define DHCP_ENV		DHCP_ENV_NO_VENDOR
55185643Sluigi
5638451Smsmith#include "stand.h"
5738451Smsmith#include "net.h"
5838451Smsmith#include "netif.h"
5938451Smsmith#include "bootp.h"
6038451Smsmith
6138451Smsmith
6238451Smsmithstruct in_addr servip;
6338451Smsmith
6438451Smsmithstatic n_long	nmask, smask;
6538451Smsmith
6638451Smsmithstatic time_t	bot;
6738451Smsmith
6838451Smsmithstatic	char vm_rfc1048[4] = VM_RFC1048;
6938451Smsmith#ifdef BOOTP_VEND_CMU
7038451Smsmithstatic	char vm_cmu[4] = VM_CMU;
7138451Smsmith#endif
7238451Smsmith
7338451Smsmith/* Local forwards */
7438451Smsmithstatic	ssize_t bootpsend(struct iodesc *, void *, size_t);
7538451Smsmithstatic	ssize_t bootprecv(struct iodesc *, void *, size_t, time_t);
7638451Smsmithstatic	int vend_rfc1048(u_char *, u_int);
7738451Smsmith#ifdef BOOTP_VEND_CMU
7838451Smsmithstatic	void vend_cmu(u_char *);
7938451Smsmith#endif
8038451Smsmith
81185643Sluigi#ifdef DHCP_ENV		/* export the dhcp response to kenv */
82185643Sluigistruct dhcp_opt;
83185643Sluigistatic void setenv_(u_char *cp,  u_char *ep, struct dhcp_opt *opts);
84185643Sluigi#else
85185643Sluigi#define setenv_(a, b, c)
86185643Sluigi#endif
87185643Sluigi
8838451Smsmith#ifdef SUPPORT_DHCP
8938451Smsmithstatic char expected_dhcpmsgtype = -1, dhcp_ok;
9038451Smsmithstruct in_addr dhcp_serverip;
9138451Smsmith#endif
9238451Smsmith
9338451Smsmith/* Fetch required bootp infomation */
9438451Smsmithvoid
9564527Spsbootp(sock, flag)
9638451Smsmith	int sock;
9764527Sps	int flag;
9838451Smsmith{
9938451Smsmith	struct iodesc *d;
10092913Sobrien	struct bootp *bp;
10138451Smsmith	struct {
10238451Smsmith		u_char header[HEADER_SIZE];
10338451Smsmith		struct bootp wbootp;
10438451Smsmith	} wbuf;
10538451Smsmith	struct {
10638451Smsmith		u_char header[HEADER_SIZE];
10738451Smsmith		struct bootp rbootp;
10838451Smsmith	} rbuf;
10938451Smsmith
11038451Smsmith#ifdef BOOTP_DEBUG
11138451Smsmith 	if (debug)
11238451Smsmith		printf("bootp: socket=%d\n", sock);
11338451Smsmith#endif
11438451Smsmith	if (!bot)
11538451Smsmith		bot = getsecs();
11638451Smsmith
11738451Smsmith	if (!(d = socktodesc(sock))) {
11838451Smsmith		printf("bootp: bad socket. %d\n", sock);
11938451Smsmith		return;
12038451Smsmith	}
12138451Smsmith#ifdef BOOTP_DEBUG
12238451Smsmith 	if (debug)
12338451Smsmith		printf("bootp: d=%lx\n", (long)d);
12438451Smsmith#endif
12538451Smsmith
12638451Smsmith	bp = &wbuf.wbootp;
12738451Smsmith	bzero(bp, sizeof(*bp));
12838451Smsmith
12938451Smsmith	bp->bp_op = BOOTREQUEST;
13038451Smsmith	bp->bp_htype = 1;		/* 10Mb Ethernet (48 bits) */
13138451Smsmith	bp->bp_hlen = 6;
13238451Smsmith	bp->bp_xid = htonl(d->xid);
13338451Smsmith	MACPY(d->myea, bp->bp_chaddr);
13438451Smsmith	strncpy(bp->bp_file, bootfile, sizeof(bp->bp_file));
13538451Smsmith	bcopy(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048));
13638451Smsmith#ifdef SUPPORT_DHCP
13738451Smsmith	bp->bp_vend[4] = TAG_DHCP_MSGTYPE;
13838451Smsmith	bp->bp_vend[5] = 1;
13938451Smsmith	bp->bp_vend[6] = DHCPDISCOVER;
14064527Sps
14164527Sps	/*
14264527Sps	 * If we are booting from PXE, we want to send the string
14364527Sps	 * 'PXEClient' to the DHCP server so you have the option of
14464527Sps	 * only responding to PXE aware dhcp requests.
14564527Sps	 */
14664527Sps	if (flag & BOOTP_PXE) {
14764527Sps		bp->bp_vend[7] = TAG_CLASSID;
14864527Sps		bp->bp_vend[8] = 9;
14964527Sps		bcopy("PXEClient", &bp->bp_vend[9], 9);
15064527Sps		bp->bp_vend[18] = TAG_END;
15164527Sps	} else
15264527Sps		bp->bp_vend[7] = TAG_END;
15338451Smsmith#else
15438451Smsmith	bp->bp_vend[4] = TAG_END;
15538451Smsmith#endif
15638451Smsmith
15738451Smsmith	d->myip.s_addr = INADDR_ANY;
15838451Smsmith	d->myport = htons(IPPORT_BOOTPC);
15938451Smsmith	d->destip.s_addr = INADDR_BROADCAST;
16038451Smsmith	d->destport = htons(IPPORT_BOOTPS);
16138451Smsmith
16238451Smsmith#ifdef SUPPORT_DHCP
16338451Smsmith	expected_dhcpmsgtype = DHCPOFFER;
16438451Smsmith	dhcp_ok = 0;
16538451Smsmith#endif
16638451Smsmith
16738451Smsmith	if(sendrecv(d,
16838451Smsmith		    bootpsend, bp, sizeof(*bp),
16938451Smsmith		    bootprecv, &rbuf.rbootp, sizeof(rbuf.rbootp))
17038451Smsmith	   == -1) {
17138451Smsmith	    printf("bootp: no reply\n");
17238451Smsmith	    return;
17338451Smsmith	}
17438451Smsmith
17538451Smsmith#ifdef SUPPORT_DHCP
17638451Smsmith	if(dhcp_ok) {
17738451Smsmith		u_int32_t leasetime;
17838451Smsmith		bp->bp_vend[6] = DHCPREQUEST;
17938451Smsmith		bp->bp_vend[7] = TAG_REQ_ADDR;
18038451Smsmith		bp->bp_vend[8] = 4;
18138451Smsmith		bcopy(&rbuf.rbootp.bp_yiaddr, &bp->bp_vend[9], 4);
18238451Smsmith		bp->bp_vend[13] = TAG_SERVERID;
18338451Smsmith		bp->bp_vend[14] = 4;
18438451Smsmith		bcopy(&dhcp_serverip.s_addr, &bp->bp_vend[15], 4);
18538451Smsmith		bp->bp_vend[19] = TAG_LEASETIME;
18638451Smsmith		bp->bp_vend[20] = 4;
18738451Smsmith		leasetime = htonl(300);
18838451Smsmith		bcopy(&leasetime, &bp->bp_vend[21], 4);
18964527Sps		if (flag & BOOTP_PXE) {
19064527Sps			bp->bp_vend[25] = TAG_CLASSID;
19164527Sps			bp->bp_vend[26] = 9;
19264527Sps			bcopy("PXEClient", &bp->bp_vend[27], 9);
19364527Sps			bp->bp_vend[36] = TAG_END;
19464527Sps		} else
19564527Sps			bp->bp_vend[25] = TAG_END;
19638451Smsmith
19738451Smsmith		expected_dhcpmsgtype = DHCPACK;
19838451Smsmith
19938451Smsmith		if(sendrecv(d,
20038451Smsmith			    bootpsend, bp, sizeof(*bp),
20138451Smsmith			    bootprecv, &rbuf.rbootp, sizeof(rbuf.rbootp))
20238451Smsmith		   == -1) {
20338451Smsmith			printf("DHCPREQUEST failed\n");
20438451Smsmith			return;
20538451Smsmith		}
20638451Smsmith	}
20738451Smsmith#endif
20838451Smsmith
20938451Smsmith	myip = d->myip = rbuf.rbootp.bp_yiaddr;
21038451Smsmith	servip = rbuf.rbootp.bp_siaddr;
21138451Smsmith	if(rootip.s_addr == INADDR_ANY) rootip = servip;
21238451Smsmith	bcopy(rbuf.rbootp.bp_file, bootfile, sizeof(bootfile));
21338451Smsmith	bootfile[sizeof(bootfile) - 1] = '\0';
21438451Smsmith
21566134Sps	if (IN_CLASSA(ntohl(myip.s_addr)))
21638451Smsmith		nmask = htonl(IN_CLASSA_NET);
21766134Sps	else if (IN_CLASSB(ntohl(myip.s_addr)))
21838451Smsmith		nmask = htonl(IN_CLASSB_NET);
21938451Smsmith	else
22038451Smsmith		nmask = htonl(IN_CLASSC_NET);
22138451Smsmith#ifdef BOOTP_DEBUG
22238451Smsmith	if (debug)
22338451Smsmith		printf("'native netmask' is %s\n", intoa(nmask));
22438451Smsmith#endif
22538451Smsmith
22638451Smsmith	/* Check subnet mask against net mask; toss if bogus */
22738451Smsmith	if ((nmask & smask) != nmask) {
22838451Smsmith#ifdef BOOTP_DEBUG
22938451Smsmith		if (debug)
23038451Smsmith			printf("subnet mask (%s) bad\n", intoa(smask));
23138451Smsmith#endif
23238451Smsmith		smask = 0;
23338451Smsmith	}
23438451Smsmith
23538451Smsmith	/* Get subnet (or natural net) mask */
23638451Smsmith	netmask = nmask;
23738451Smsmith	if (smask)
23838451Smsmith		netmask = smask;
23938451Smsmith#ifdef BOOTP_DEBUG
24038451Smsmith	if (debug)
24138451Smsmith		printf("mask: %s\n", intoa(netmask));
24238451Smsmith#endif
24338451Smsmith
24438451Smsmith	/* We need a gateway if root is on a different net */
24538451Smsmith	if (!SAMENET(myip, rootip, netmask)) {
24638451Smsmith#ifdef BOOTP_DEBUG
24738451Smsmith		if (debug)
24838451Smsmith			printf("need gateway for root ip\n");
24938451Smsmith#endif
25038451Smsmith	}
25138451Smsmith
25238451Smsmith	/* Toss gateway if on a different net */
25338451Smsmith	if (!SAMENET(myip, gateip, netmask)) {
25438451Smsmith#ifdef BOOTP_DEBUG
25538451Smsmith		if (debug)
25638451Smsmith			printf("gateway ip (%s) bad\n", inet_ntoa(gateip));
25738451Smsmith#endif
25838451Smsmith		gateip.s_addr = 0;
25938451Smsmith	}
26038451Smsmith
26138451Smsmith	/* Bump xid so next request will be unique. */
26238451Smsmith	++d->xid;
26338451Smsmith}
26438451Smsmith
26538451Smsmith/* Transmit a bootp request */
26638451Smsmithstatic ssize_t
26738451Smsmithbootpsend(d, pkt, len)
26892913Sobrien	struct iodesc *d;
26992913Sobrien	void *pkt;
27092913Sobrien	size_t len;
27138451Smsmith{
27292913Sobrien	struct bootp *bp;
27338451Smsmith
27438451Smsmith#ifdef BOOTP_DEBUG
27538451Smsmith	if (debug)
27638451Smsmith		printf("bootpsend: d=%lx called.\n", (long)d);
27738451Smsmith#endif
27838451Smsmith
27938451Smsmith	bp = pkt;
28038451Smsmith	bp->bp_secs = htons((u_short)(getsecs() - bot));
28138451Smsmith
28238451Smsmith#ifdef BOOTP_DEBUG
28338451Smsmith	if (debug)
28438451Smsmith		printf("bootpsend: calling sendudp\n");
28538451Smsmith#endif
28638451Smsmith
28738451Smsmith	return (sendudp(d, pkt, len));
28838451Smsmith}
28938451Smsmith
29038451Smsmithstatic ssize_t
29138451Smsmithbootprecv(d, pkt, len, tleft)
29292913Sobrienstruct iodesc *d;
29392913Sobrienvoid *pkt;
29492913Sobriensize_t len;
29538451Smsmithtime_t tleft;
29638451Smsmith{
29792913Sobrien	ssize_t n;
29892913Sobrien	struct bootp *bp;
29938451Smsmith
30038451Smsmith#ifdef BOOTP_DEBUGx
30138451Smsmith	if (debug)
30238451Smsmith		printf("bootp_recvoffer: called\n");
30338451Smsmith#endif
30438451Smsmith
30538451Smsmith	n = readudp(d, pkt, len, tleft);
30638451Smsmith	if (n == -1 || n < sizeof(struct bootp) - BOOTP_VENDSIZE)
30738451Smsmith		goto bad;
30838451Smsmith
30938451Smsmith	bp = (struct bootp *)pkt;
31038451Smsmith
31138451Smsmith#ifdef BOOTP_DEBUG
31238451Smsmith	if (debug)
31338451Smsmith		printf("bootprecv: checked.  bp = 0x%lx, n = %d\n",
31438451Smsmith		    (long)bp, (int)n);
31538451Smsmith#endif
31638451Smsmith	if (bp->bp_xid != htonl(d->xid)) {
31738451Smsmith#ifdef BOOTP_DEBUG
31838451Smsmith		if (debug) {
31938451Smsmith			printf("bootprecv: expected xid 0x%lx, got 0x%x\n",
32038451Smsmith			    d->xid, ntohl(bp->bp_xid));
32138451Smsmith		}
32238451Smsmith#endif
32338451Smsmith		goto bad;
32438451Smsmith	}
32538451Smsmith
32638451Smsmith#ifdef BOOTP_DEBUG
32738451Smsmith	if (debug)
32838451Smsmith		printf("bootprecv: got one!\n");
32938451Smsmith#endif
33038451Smsmith
33138451Smsmith	/* Suck out vendor info */
33238451Smsmith	if (bcmp(vm_rfc1048, bp->bp_vend, sizeof(vm_rfc1048)) == 0) {
33338451Smsmith		if(vend_rfc1048(bp->bp_vend, sizeof(bp->bp_vend)) != 0)
33438451Smsmith		    goto bad;
33538451Smsmith	}
33638451Smsmith#ifdef BOOTP_VEND_CMU
33738451Smsmith	else if (bcmp(vm_cmu, bp->bp_vend, sizeof(vm_cmu)) == 0)
33838451Smsmith		vend_cmu(bp->bp_vend);
33938451Smsmith#endif
34038451Smsmith	else
34138451Smsmith		printf("bootprecv: unknown vendor 0x%lx\n", (long)bp->bp_vend);
34238451Smsmith
34338451Smsmith	return(n);
34438451Smsmithbad:
34538451Smsmith	errno = 0;
34638451Smsmith	return (-1);
34738451Smsmith}
34838451Smsmith
34938451Smsmithstatic int
35038451Smsmithvend_rfc1048(cp, len)
35192913Sobrien	u_char *cp;
35238451Smsmith	u_int len;
35338451Smsmith{
35492913Sobrien	u_char *ep;
35592913Sobrien	int size;
35692913Sobrien	u_char tag;
35738451Smsmith
35838451Smsmith#ifdef BOOTP_DEBUG
35938451Smsmith	if (debug)
36038451Smsmith		printf("vend_rfc1048 bootp info. len=%d\n", len);
36138451Smsmith#endif
36238451Smsmith	ep = cp + len;
36338451Smsmith
36438451Smsmith	/* Step over magic cookie */
36538451Smsmith	cp += sizeof(int);
36638451Smsmith
367185643Sluigi	setenv_(cp, ep, NULL);
368185643Sluigi
36938451Smsmith	while (cp < ep) {
37038451Smsmith		tag = *cp++;
37138451Smsmith		size = *cp++;
37238451Smsmith		if (tag == TAG_END)
37338451Smsmith			break;
37438451Smsmith
37538451Smsmith		if (tag == TAG_SUBNET_MASK) {
37638451Smsmith			bcopy(cp, &smask, sizeof(smask));
37738451Smsmith		}
37838451Smsmith		if (tag == TAG_GATEWAY) {
37938451Smsmith			bcopy(cp, &gateip.s_addr, sizeof(gateip.s_addr));
38038451Smsmith		}
38138451Smsmith		if (tag == TAG_SWAPSERVER) {
38238451Smsmith			/* let it override bp_siaddr */
38338451Smsmith			bcopy(cp, &rootip.s_addr, sizeof(swapip.s_addr));
38438451Smsmith		}
38538451Smsmith		if (tag == TAG_ROOTPATH) {
38638451Smsmith			strncpy(rootpath, (char *)cp, sizeof(rootpath));
38738451Smsmith			rootpath[size] = '\0';
38838451Smsmith		}
38938451Smsmith		if (tag == TAG_HOSTNAME) {
39038451Smsmith			strncpy(hostname, (char *)cp, sizeof(hostname));
39138451Smsmith			hostname[size] = '\0';
39238451Smsmith		}
39338451Smsmith#ifdef SUPPORT_DHCP
39438451Smsmith		if (tag == TAG_DHCP_MSGTYPE) {
39538451Smsmith			if(*cp != expected_dhcpmsgtype)
39638451Smsmith			    return(-1);
39738451Smsmith			dhcp_ok = 1;
39838451Smsmith		}
39938451Smsmith		if (tag == TAG_SERVERID) {
40038451Smsmith			bcopy(cp, &dhcp_serverip.s_addr,
40138451Smsmith			      sizeof(dhcp_serverip.s_addr));
40238451Smsmith		}
40338451Smsmith#endif
40438451Smsmith		cp += size;
40538451Smsmith	}
40638451Smsmith	return(0);
40738451Smsmith}
40838451Smsmith
40938451Smsmith#ifdef BOOTP_VEND_CMU
41038451Smsmithstatic void
41138451Smsmithvend_cmu(cp)
41238451Smsmith	u_char *cp;
41338451Smsmith{
41492913Sobrien	struct cmu_vend *vp;
41538451Smsmith
41638451Smsmith#ifdef BOOTP_DEBUG
41738451Smsmith	if (debug)
41838451Smsmith		printf("vend_cmu bootp info.\n");
41938451Smsmith#endif
42038451Smsmith	vp = (struct cmu_vend *)cp;
42138451Smsmith
42238451Smsmith	if (vp->v_smask.s_addr != 0) {
42338451Smsmith		smask = vp->v_smask.s_addr;
42438451Smsmith	}
42538451Smsmith	if (vp->v_dgate.s_addr != 0) {
42638451Smsmith		gateip = vp->v_dgate;
42738451Smsmith	}
42838451Smsmith}
42938451Smsmith#endif
430185643Sluigi
431185643Sluigi#ifdef DHCP_ENV
432185643Sluigi/*
433185643Sluigi * Parse DHCP options and store them into kenv variables.
434185643Sluigi * Original code from Danny Braniss, modifications by Luigi Rizzo.
435185643Sluigi *
436185643Sluigi * The parser is driven by tables which specify the type and name of
437185643Sluigi * each dhcp option and how it appears in kenv.
438185643Sluigi * The first entry in the list contains the prefix used to set the kenv
439185643Sluigi * name (including the . if needed), the last entry must have a 0 tag.
440185643Sluigi * Entries do not need to be sorted though it helps for readability.
441185643Sluigi *
442185643Sluigi * Certain vendor-specific tables can be enabled according to DHCP_ENV.
443185643Sluigi * Set it to 0 if you don't want any.
444185643Sluigi */
445185643Sluigienum opt_fmt { __NONE = 0,
446185643Sluigi	__8 = 1, __16 = 2, __32 = 4,	/* Unsigned fields, value=size	*/
447185643Sluigi	__IP,				/* IPv4 address			*/
448185643Sluigi	__TXT,				/* C string			*/
449185643Sluigi	__BYTES,			/* byte sequence, printed %02x	*/
450185643Sluigi	__INDIR,			/* name=value			*/
451185643Sluigi	__ILIST,			/* name=value;name=value ... */
452185643Sluigi	__VE,				/* vendor specific, recurse	*/
453185643Sluigi};
454185643Sluigi
455185643Sluigistruct dhcp_opt {
456185643Sluigi	uint8_t	tag;
457185643Sluigi	uint8_t	fmt;
458185643Sluigi	const char	*desc;
459185643Sluigi};
460185643Sluigi
461185643Sluigistatic struct dhcp_opt vndr_opt[] = { /* Vendor Specific Options */
462185643Sluigi#if DHCP_ENV == DHCP_ENV_FREEBSD /* FreeBSD table in the original code */
463185643Sluigi	{0,	0,	"FreeBSD"},		/* prefix */
464185643Sluigi	{1,	__TXT,	"kernel"},
465185643Sluigi	{2,	__TXT,	"kernelname"},
466185643Sluigi	{3,	__TXT,	"kernel_options"},
467185643Sluigi	{4,	__IP,	"usr-ip"},
468185643Sluigi	{5,	__TXT,	"conf-path"},
469185643Sluigi	{6,	__TXT,	"rc.conf0"},
470185643Sluigi	{7,	__TXT,	"rc.conf1"},
471185643Sluigi	{8,	__TXT,	"rc.conf2"},
472185643Sluigi	{9,	__TXT,	"rc.conf3"},
473185643Sluigi	{10,	__TXT,	"rc.conf4"},
474185643Sluigi	{11,	__TXT,	"rc.conf5"},
475185643Sluigi	{12,	__TXT,	"rc.conf6"},
476185643Sluigi	{13,	__TXT,	"rc.conf7"},
477185643Sluigi	{14,	__TXT,	"rc.conf8"},
478185643Sluigi	{15,	__TXT,	"rc.conf9"},
479185643Sluigi
480185643Sluigi	{20,	__TXT,  "boot.nfsroot.options"},
481185643Sluigi
482185643Sluigi	{245,	__INDIR, ""},
483185643Sluigi	{246,	__INDIR, ""},
484185643Sluigi	{247,	__INDIR, ""},
485185643Sluigi	{248,	__INDIR, ""},
486185643Sluigi	{249,	__INDIR, ""},
487185643Sluigi	{250,	__INDIR, ""},
488185643Sluigi	{251,	__INDIR, ""},
489185643Sluigi	{252,	__INDIR, ""},
490185643Sluigi	{253,	__INDIR, ""},
491185643Sluigi	{254,	__INDIR, ""},
492185643Sluigi
493185643Sluigi#elif DHCP_ENV == DHCP_ENV_PXE		/* some pxe options, RFC4578 */
494185643Sluigi	{0,	0,	"pxe"},		/* prefix */
495185643Sluigi	{93,	__16,	"system-architecture"},
496185643Sluigi	{94,	__BYTES,	"network-interface"},
497185643Sluigi	{97,	__BYTES,	"machine-identifier"},
498185643Sluigi#else					/* default (empty) table */
499186799Sluigi	{0,	0,	"dhcp.vendor."},		/* prefix */
500185643Sluigi#endif
501185643Sluigi	{0,	__TXT,	"%soption-%d"}
502185643Sluigi};
503185643Sluigi
504185643Sluigistatic struct dhcp_opt dhcp_opt[] = {
505185643Sluigi	/* DHCP Option names, formats and codes, from RFC2132. */
506185643Sluigi	{0,	0,	"dhcp."},	// prefix
507185643Sluigi	{1,	__IP,	"subnet-mask"},
508185643Sluigi	{2,	__32,	"time-offset"}, /* this is signed */
509185643Sluigi	{3,	__IP,	"routers"},
510185643Sluigi	{4,	__IP,	"time-servers"},
511185643Sluigi	{5,	__IP,	"ien116-name-servers"},
512185643Sluigi	{6,	__IP,	"domain-name-servers"},
513185643Sluigi	{7,	__IP,	"log-servers"},
514185643Sluigi	{8,	__IP,	"cookie-servers"},
515185643Sluigi	{9,	__IP,	"lpr-servers"},
516185643Sluigi	{10,	__IP,	"impress-servers"},
517185643Sluigi	{11,	__IP,	"resource-location-servers"},
518185643Sluigi	{12,	__TXT,	"host-name"},
519185643Sluigi	{13,	__16,	"boot-size"},
520185643Sluigi	{14,	__TXT,	"merit-dump"},
521185643Sluigi	{15,	__TXT,	"domain-name"},
522185643Sluigi	{16,	__IP,	"swap-server"},
523185643Sluigi	{17,	__TXT,	"root-path"},
524185643Sluigi	{18,	__TXT,	"extensions-path"},
525185643Sluigi	{19,	__8,	"ip-forwarding"},
526185643Sluigi	{20,	__8,	"non-local-source-routing"},
527185643Sluigi	{21,	__IP,	"policy-filter"},
528185643Sluigi	{22,	__16,	"max-dgram-reassembly"},
529185643Sluigi	{23,	__8,	"default-ip-ttl"},
530185643Sluigi	{24,	__32,	"path-mtu-aging-timeout"},
531185643Sluigi	{25,	__16,	"path-mtu-plateau-table"},
532185643Sluigi	{26,	__16,	"interface-mtu"},
533185643Sluigi	{27,	__8,	"all-subnets-local"},
534185643Sluigi	{28,	__IP,	"broadcast-address"},
535185643Sluigi	{29,	__8,	"perform-mask-discovery"},
536185643Sluigi	{30,	__8,	"mask-supplier"},
537185643Sluigi	{31,	__8,	"perform-router-discovery"},
538185643Sluigi	{32,	__IP,	"router-solicitation-address"},
539185643Sluigi	{33,	__IP,	"static-routes"},
540185643Sluigi	{34,	__8,	"trailer-encapsulation"},
541185643Sluigi	{35,	__32,	"arp-cache-timeout"},
542185643Sluigi	{36,	__8,	"ieee802-3-encapsulation"},
543185643Sluigi	{37,	__8,	"default-tcp-ttl"},
544185643Sluigi	{38,	__32,	"tcp-keepalive-interval"},
545185643Sluigi	{39,	__8,	"tcp-keepalive-garbage"},
546185643Sluigi	{40,	__TXT,	"nis-domain"},
547185643Sluigi	{41,	__IP,	"nis-servers"},
548185643Sluigi	{42,	__IP,	"ntp-servers"},
549185643Sluigi	{43,	__VE,	"vendor-encapsulated-options"},
550185643Sluigi	{44,	__IP,	"netbios-name-servers"},
551185643Sluigi	{45,	__IP,	"netbios-dd-server"},
552185643Sluigi	{46,	__8,	"netbios-node-type"},
553185643Sluigi	{47,	__TXT,	"netbios-scope"},
554185643Sluigi	{48,	__IP,	"x-font-servers"},
555185643Sluigi	{49,	__IP,	"x-display-managers"},
556185643Sluigi	{50,	__IP,	"dhcp-requested-address"},
557185643Sluigi	{51,	__32,	"dhcp-lease-time"},
558185643Sluigi	{52,	__8,	"dhcp-option-overload"},
559185643Sluigi	{53,	__8,	"dhcp-message-type"},
560185643Sluigi	{54,	__IP,	"dhcp-server-identifier"},
561185643Sluigi	{55,	__8,	"dhcp-parameter-request-list"},
562185643Sluigi	{56,	__TXT,	"dhcp-message"},
563185643Sluigi	{57,	__16,	"dhcp-max-message-size"},
564185643Sluigi	{58,	__32,	"dhcp-renewal-time"},
565185643Sluigi	{59,	__32,	"dhcp-rebinding-time"},
566185643Sluigi	{60,	__TXT,	"vendor-class-identifier"},
567185643Sluigi	{61,	__TXT,	"dhcp-client-identifier"},
568185643Sluigi	{64,	__TXT,	"nisplus-domain"},
569185643Sluigi	{65,	__IP,	"nisplus-servers"},
570185643Sluigi	{66,	__TXT,	"tftp-server-name"},
571185643Sluigi	{67,	__TXT,	"bootfile-name"},
572185643Sluigi	{68,	__IP,	"mobile-ip-home-agent"},
573185643Sluigi	{69,	__IP,	"smtp-server"},
574185643Sluigi	{70,	__IP,	"pop-server"},
575185643Sluigi	{71,	__IP,	"nntp-server"},
576185643Sluigi	{72,	__IP,	"www-server"},
577185643Sluigi	{73,	__IP,	"finger-server"},
578185643Sluigi	{74,	__IP,	"irc-server"},
579185643Sluigi	{75,	__IP,	"streettalk-server"},
580185643Sluigi	{76,	__IP,	"streettalk-directory-assistance-server"},
581185643Sluigi	{77,	__TXT,	"user-class"},
582185643Sluigi	{85,	__IP,	"nds-servers"},
583185643Sluigi	{86,	__TXT,	"nds-tree-name"},
584185643Sluigi	{87,	__TXT,	"nds-context"},
585185643Sluigi	{210,	__TXT,	"authenticate"},
586185643Sluigi
587185643Sluigi	/* use the following entries for arbitrary variables */
588185643Sluigi	{246,	__ILIST, ""},
589185643Sluigi	{247,	__ILIST, ""},
590185643Sluigi	{248,	__ILIST, ""},
591185643Sluigi	{249,	__ILIST, ""},
592185643Sluigi	{250,	__INDIR, ""},
593185643Sluigi	{251,	__INDIR, ""},
594185643Sluigi	{252,	__INDIR, ""},
595185643Sluigi	{253,	__INDIR, ""},
596185643Sluigi	{254,	__INDIR, ""},
597185643Sluigi	{0,	__TXT,	"%soption-%d"}
598185643Sluigi};
599185643Sluigi
600185643Sluigi/*
601185643Sluigi * parse a dhcp response, set environment variables translating options
602185643Sluigi * names and values according to the tables above. Also set dhcp.tags
603185643Sluigi * to the list of selected tags.
604185643Sluigi */
605185643Sluigistatic void
606185643Sluigisetenv_(u_char *cp,  u_char *ep, struct dhcp_opt *opts)
607185643Sluigi{
608185643Sluigi    u_char	*ncp;
609185643Sluigi    u_char	tag;
610185643Sluigi    char	tags[512], *tp;	/* the list of tags */
611185643Sluigi
612185643Sluigi#define FLD_SEP	','	/* separator in list of elements */
613185643Sluigi    ncp = cp;
614185643Sluigi    tp = tags;
615185643Sluigi    if (opts == NULL)
616185643Sluigi	opts = dhcp_opt;
617185643Sluigi
618185643Sluigi    while (ncp < ep) {
619185643Sluigi	unsigned int	size;		/* option size */
620185643Sluigi	char *vp, *endv, buf[256];	/* the value buffer */
621185643Sluigi	struct dhcp_opt *op;
622185643Sluigi
623185643Sluigi	tag = *ncp++;			/* extract tag and size */
624185643Sluigi	size = *ncp++;
625185643Sluigi	cp = ncp;			/* current payload */
626185643Sluigi	ncp += size;			/* point to the next option */
627185643Sluigi
628185643Sluigi	if (tag == TAG_END)
629185643Sluigi	    break;
630185643Sluigi	if (tag == 0)
631185643Sluigi	    continue;
632185643Sluigi
633185643Sluigi	for (op = opts+1; op->tag && op->tag != tag; op++)
634185643Sluigi		;
635185643Sluigi	/* if not found we end up on the default entry */
636185643Sluigi
637185643Sluigi	/*
638185643Sluigi	 * Copy data into the buffer. libstand does not have snprintf so we
639185643Sluigi	 * need to be careful with sprintf(). With strings, the source is
640185643Sluigi	 * always <256 char so shorter than the buffer so we are safe; with
641185643Sluigi	 * other arguments, the longest string is inet_ntoa which is 16 bytes
642185643Sluigi	 * so we make sure to have always enough room in the string before
643185643Sluigi	 * trying an sprint.
644185643Sluigi	 */
645185643Sluigi	vp = buf;
646185643Sluigi	*vp = '\0';
647185643Sluigi	endv = buf + sizeof(buf) - 1 - 16;	/* last valid write position */
648185643Sluigi
649185643Sluigi	switch(op->fmt) {
650185643Sluigi	case __NONE:
651185643Sluigi	    break;	/* should not happen */
652185643Sluigi
653185643Sluigi	case __VE: /* recurse, vendor specific */
654185643Sluigi	    setenv_(cp, cp+size, vndr_opt);
655185643Sluigi	    break;
656185643Sluigi
657185643Sluigi	case __IP:	/* ip address */
658185643Sluigi	    for (; size > 0 && vp < endv; size -= 4, cp += 4) {
659185643Sluigi		struct	in_addr in_ip;		/* ip addresses */
660185643Sluigi		if (vp != buf)
661185643Sluigi		    *vp++ = FLD_SEP;
662185643Sluigi		bcopy(cp, &in_ip.s_addr, sizeof(in_ip.s_addr));
663185643Sluigi		sprintf(vp, "%s", inet_ntoa(in_ip));
664185643Sluigi		vp += strlen(vp);
665185643Sluigi	    }
666185643Sluigi	    break;
667185643Sluigi
668185643Sluigi	case __BYTES:	/* opaque byte string */
669185643Sluigi	    for (; size > 0 && vp < endv; size -= 1, cp += 1) {
670185643Sluigi		sprintf(vp, "%02x", *cp);
671185643Sluigi		vp += strlen(vp);
672185643Sluigi	    }
673185643Sluigi	    break;
674185643Sluigi
675185643Sluigi	case __TXT:
676185643Sluigi	    bcopy(cp, buf, size);	/* cannot overflow */
677185643Sluigi	    buf[size] = 0;
678185643Sluigi	    break;
679185643Sluigi
680185643Sluigi	case __32:
681185643Sluigi	case __16:
682185643Sluigi	case __8:	/* op->fmt is also the length of each field */
683185643Sluigi	    for (; size > 0 && vp < endv; size -= op->fmt, cp += op->fmt) {
684185643Sluigi		uint32_t v;
685185643Sluigi		if (op->fmt == __32)
686185643Sluigi			v = (cp[0]<<24) + (cp[1]<<16) + (cp[2]<<8) + cp[3];
687185643Sluigi		else if (op->fmt == __16)
688185643Sluigi			v = (cp[0]<<8) + cp[1];
689185643Sluigi		else
690185643Sluigi			v = cp[0];
691185643Sluigi		if (vp != buf)
692185643Sluigi		    *vp++ = FLD_SEP;
693185643Sluigi		sprintf(vp, "%u", v);
694185643Sluigi		vp += strlen(vp);
695185643Sluigi	    }
696185643Sluigi	    break;
697185643Sluigi
698185643Sluigi	case __INDIR:	/* name=value */
699185643Sluigi	case __ILIST:	/* name=value;name=value... */
700185643Sluigi	    bcopy(cp, buf, size);	/* cannot overflow */
701185643Sluigi	    buf[size] = '\0';
702185643Sluigi	    for (endv = buf; endv; endv = vp) {
703185643Sluigi		u_char *s = NULL;	/* semicolon ? */
704185643Sluigi
705185643Sluigi		/* skip leading whitespace */
706229403Sed		while (*endv && strchr(" \t\n\r", *endv))
707185643Sluigi		    endv++;
708229403Sed		vp = strchr(endv, '=');	/* find name=value separator */
709185643Sluigi		if (!vp)
710185643Sluigi		    break;
711185643Sluigi		*vp++ = 0;
712229403Sed		if (op->fmt == __ILIST && (s = strchr(vp, ';')))
713185643Sluigi		    *s++ = '\0';
714185643Sluigi		setenv(endv, vp, 1);
715185643Sluigi		vp = s;	/* prepare for next round */
716185643Sluigi	    }
717185643Sluigi	    buf[0] = '\0';	/* option already done */
718185643Sluigi	}
719185643Sluigi
720185643Sluigi	if (tp - tags < sizeof(tags) - 5) {	/* add tag to the list */
721185643Sluigi	    if (tp != tags)
722185643Sluigi		*tp++ = FLD_SEP;
723185643Sluigi	    sprintf(tp, "%d", tag);
724185643Sluigi	    tp += strlen(tp);
725185643Sluigi	}
726185643Sluigi	if (buf[0]) {
727185643Sluigi	    char	env[128];	/* the string name */
728185643Sluigi
729185643Sluigi	    if (op->tag == 0)
730185643Sluigi		sprintf(env, op->desc, opts[0].desc, tag);
731185643Sluigi	    else
732185643Sluigi		sprintf(env, "%s%s", opts[0].desc, op->desc);
733185643Sluigi	    setenv(env, buf, 1);
734185643Sluigi	}
735185643Sluigi    }
736185643Sluigi    if (tp != tags) {
737185643Sluigi	char	env[128];	/* the string name */
738185643Sluigi	sprintf(env, "%stags", opts[0].desc);
739185643Sluigi	setenv(env, tags, 1);
740185643Sluigi    }
741185643Sluigi}
742185643Sluigi#endif /* additional dhcp */
743