xref: /illumos-gate/usr/src/boot/lib/libstand/bootp.c (revision 8eef2ab6)
1199767f8SToomas Soome /*
2199767f8SToomas Soome  * Copyright (c) 1992 Regents of the University of California.
3199767f8SToomas Soome  * All rights reserved.
4199767f8SToomas Soome  *
5199767f8SToomas Soome  * This software was developed by the Computer Systems Engineering group
6199767f8SToomas Soome  * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
7199767f8SToomas Soome  * contributed to Berkeley.
8199767f8SToomas Soome  *
9199767f8SToomas Soome  * Redistribution and use in source and binary forms, with or without
10199767f8SToomas Soome  * modification, are permitted provided that the following conditions
11199767f8SToomas Soome  * are met:
12199767f8SToomas Soome  * 1. Redistributions of source code must retain the above copyright
13199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer.
14199767f8SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
15199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
16199767f8SToomas Soome  *    documentation and/or other materials provided with the distribution.
1765c41f67SToomas Soome  * 3. Neither the name of the University nor the names of its contributors
18199767f8SToomas Soome  *    may be used to endorse or promote products derived from this software
19199767f8SToomas Soome  *    without specific prior written permission.
20199767f8SToomas Soome  *
31199767f8SToomas Soome  * SUCH DAMAGE.
32199767f8SToomas Soome  */
33199767f8SToomas Soome 
34199767f8SToomas Soome #include <sys/cdefs.h>
35199767f8SToomas Soome 
36859472daSToomas Soome #include <stddef.h>
37199767f8SToomas Soome #include <sys/types.h>
38c21cf554SToomas Soome #include <sys/limits.h>
39c21cf554SToomas Soome #include <sys/endian.h>
40199767f8SToomas Soome #include <netinet/in.h>
41199767f8SToomas Soome #include <netinet/in_systm.h>
42199767f8SToomas Soome 
43199767f8SToomas Soome #include <string.h>
44199767f8SToomas Soome 
45734b3a42SToomas Soome #define	BOOTP_DEBUGxx
46734b3a42SToomas Soome #define	SUPPORT_DHCP
47199767f8SToomas Soome 
48199767f8SToomas Soome #define	DHCP_ENV_NOVENDOR	1	/* do not parse vendor options */
49199767f8SToomas Soome #define	DHCP_ENV_PXE		10	/* assume pxe vendor options */
50199767f8SToomas Soome #define	DHCP_ENV_FREEBSD	11	/* assume freebsd vendor options */
51199767f8SToomas Soome /* set DHCP_ENV to one of the values above to export dhcp options to kenv */
52734b3a42SToomas Soome #define	DHCP_ENV		DHCP_ENV_NO_VENDOR
53199767f8SToomas Soome 
54199767f8SToomas Soome #include "stand.h"
55199767f8SToomas Soome #include "net.h"
56199767f8SToomas Soome #include "netif.h"
57199767f8SToomas Soome #include "bootp.h"
58199767f8SToomas Soome 
59199767f8SToomas Soome 
60199767f8SToomas Soome struct in_addr servip;
61199767f8SToomas Soome 
62199767f8SToomas Soome static time_t	bot;
63199767f8SToomas Soome 
64199767f8SToomas Soome static	char vm_rfc1048[4] = VM_RFC1048;
65199767f8SToomas Soome #ifdef BOOTP_VEND_CMU
66199767f8SToomas Soome static	char vm_cmu[4] = VM_CMU;
67199767f8SToomas Soome #endif
68199767f8SToomas Soome 
69199767f8SToomas Soome /* Local forwards */
70199767f8SToomas Soome static	ssize_t bootpsend(struct iodesc *, void *, size_t);
71890c8671SToomas Soome static	ssize_t bootprecv(struct iodesc *, void **, void **, time_t, void *);
72734b3a42SToomas Soome static	int vend_rfc1048(uchar_t *, uint_t);
73199767f8SToomas Soome #ifdef BOOTP_VEND_CMU
74734b3a42SToomas Soome static	void vend_cmu(uchar_t *);
75199767f8SToomas Soome #endif
76199767f8SToomas Soome 
77199767f8SToomas Soome #ifdef DHCP_ENV		/* export the dhcp response to kenv */
78199767f8SToomas Soome struct dhcp_opt;
79734b3a42SToomas Soome static void setenv_(uchar_t *cp,  uchar_t *ep, struct dhcp_opt *opts);
80199767f8SToomas Soome #else
81734b3a42SToomas Soome #define	setenv_(a, b, c)
82199767f8SToomas Soome #endif
83199767f8SToomas Soome 
84199767f8SToomas Soome #ifdef SUPPORT_DHCP
85199767f8SToomas Soome static char expected_dhcpmsgtype = -1, dhcp_ok;
86199767f8SToomas Soome struct in_addr dhcp_serverip;
87199767f8SToomas Soome #endif
88f9feecc1SToomas Soome struct bootp *bootp_response;
89859472daSToomas Soome size_t bootp_response_size;
90199767f8SToomas Soome 
916dfcdabdSToomas Soome static void
bootp_fill_request(unsigned char * bp_vend)926dfcdabdSToomas Soome bootp_fill_request(unsigned char *bp_vend)
936dfcdabdSToomas Soome {
946dfcdabdSToomas Soome 	/*
956dfcdabdSToomas Soome 	 * We are booting from PXE, we want to send the string
966dfcdabdSToomas Soome 	 * 'PXEClient' to the DHCP server so you have the option of
976dfcdabdSToomas Soome 	 * only responding to PXE aware dhcp requests.
986dfcdabdSToomas Soome 	 */
996dfcdabdSToomas Soome 	bp_vend[0] = TAG_CLASSID;
1006dfcdabdSToomas Soome 	bp_vend[1] = 9;
1016dfcdabdSToomas Soome 	bcopy("PXEClient", &bp_vend[2], 9);
1026dfcdabdSToomas Soome 	bp_vend[11] = TAG_USER_CLASS;
1036dfcdabdSToomas Soome 	/* len of each user class + number of user class */
1046dfcdabdSToomas Soome 	bp_vend[12] = 8;
1056dfcdabdSToomas Soome 	/* len of the first user class */
1066dfcdabdSToomas Soome 	bp_vend[13] = 7;
1076dfcdabdSToomas Soome 	bcopy("illumos", &bp_vend[14], 7);
1086dfcdabdSToomas Soome 	bp_vend[21] = TAG_PARAM_REQ;
1096dfcdabdSToomas Soome 	bp_vend[22] = 7;
1106dfcdabdSToomas Soome 	bp_vend[23] = TAG_SUBNET_MASK;
1116dfcdabdSToomas Soome 	bp_vend[24] = TAG_GATEWAY;
1126dfcdabdSToomas Soome 	bp_vend[25] = TAG_HOSTNAME;
1136dfcdabdSToomas Soome 	bp_vend[26] = TAG_SWAPSERVER;
1146dfcdabdSToomas Soome 	bp_vend[27] = TAG_ROOTPATH;
1156dfcdabdSToomas Soome 	bp_vend[28] = TAG_INTF_MTU;
1166dfcdabdSToomas Soome 	bp_vend[29] = TAG_SERVERID;
1176dfcdabdSToomas Soome 	bp_vend[30] = TAG_END;
1186dfcdabdSToomas Soome }
1196dfcdabdSToomas Soome 
120199767f8SToomas Soome /* Fetch required bootp infomation */
121199767f8SToomas Soome void
bootp(int sock)122dfbc6f2dSToomas Soome bootp(int sock)
123199767f8SToomas Soome {
124859472daSToomas Soome 	void *pkt;
125199767f8SToomas Soome 	struct iodesc *d;
126199767f8SToomas Soome 	struct bootp *bp;
127199767f8SToomas Soome 	struct {
128734b3a42SToomas Soome 		uchar_t header[HEADER_SIZE];
129199767f8SToomas Soome 		struct bootp wbootp;
130199767f8SToomas Soome 	} wbuf;
131859472daSToomas Soome 	struct bootp *rbootp;
132199767f8SToomas Soome 
133199767f8SToomas Soome #ifdef BOOTP_DEBUG
134734b3a42SToomas Soome 	if (debug)
135199767f8SToomas Soome 		printf("bootp: socket=%d\n", sock);
136199767f8SToomas Soome #endif
137199767f8SToomas Soome 	if (!bot)
138199767f8SToomas Soome 		bot = getsecs();
139734b3a42SToomas Soome 
140199767f8SToomas Soome 	if (!(d = socktodesc(sock))) {
141199767f8SToomas Soome 		printf("bootp: bad socket. %d\n", sock);
142199767f8SToomas Soome 		return;
143199767f8SToomas Soome 	}
144199767f8SToomas Soome #ifdef BOOTP_DEBUG
145734b3a42SToomas Soome 	if (debug)
146199767f8SToomas Soome 		printf("bootp: d=%lx\n", (long)d);
147199767f8SToomas Soome #endif
148199767f8SToomas Soome 
149199767f8SToomas Soome 	bp = &wbuf.wbootp;
150734b3a42SToomas Soome 	bzero(bp, sizeof (*bp));
151199767f8SToomas Soome 
152199767f8SToomas Soome 	bp->bp_op = BOOTREQUEST;
153199767f8SToomas Soome 	bp->bp_htype = 1;		/* 10Mb Ethernet (48 bits) */
154199767f8SToomas Soome 	bp->bp_hlen = 6;
155199767f8SToomas Soome 	bp->bp_xid = htonl(d->xid);
156199767f8SToomas Soome 	MACPY(d->myea, bp->bp_chaddr);
157734b3a42SToomas Soome 	strncpy(bp->bp_file, bootfile, sizeof (bp->bp_file));
158734b3a42SToomas Soome 	bcopy(vm_rfc1048, bp->bp_vend, sizeof (vm_rfc1048));
159199767f8SToomas Soome #ifdef SUPPORT_DHCP
160199767f8SToomas Soome 	bp->bp_vend[4] = TAG_DHCP_MSGTYPE;
161199767f8SToomas Soome 	bp->bp_vend[5] = 1;
162199767f8SToomas Soome 	bp->bp_vend[6] = DHCPDISCOVER;
1636dfcdabdSToomas Soome 	bootp_fill_request(&bp->bp_vend[7]);
164199767f8SToomas Soome #else
165199767f8SToomas Soome 	bp->bp_vend[4] = TAG_END;
166199767f8SToomas Soome #endif
167199767f8SToomas Soome 
168199767f8SToomas Soome 	d->myip.s_addr = INADDR_ANY;
169199767f8SToomas Soome 	d->myport = htons(IPPORT_BOOTPC);
170199767f8SToomas Soome 	d->destip.s_addr = INADDR_BROADCAST;
171199767f8SToomas Soome 	d->destport = htons(IPPORT_BOOTPS);
172199767f8SToomas Soome 
173199767f8SToomas Soome #ifdef SUPPORT_DHCP
174199767f8SToomas Soome 	expected_dhcpmsgtype = DHCPOFFER;
175199767f8SToomas Soome 	dhcp_ok = 0;
176199767f8SToomas Soome #endif
177199767f8SToomas Soome 
178734b3a42SToomas Soome 	if (sendrecv(d, bootpsend, bp, sizeof (*bp),
179734b3a42SToomas Soome 	    bootprecv, &pkt, (void **)&rbootp, NULL) == -1) {
180734b3a42SToomas Soome 		printf("bootp: no reply\n");
181734b3a42SToomas Soome 		return;
182199767f8SToomas Soome 	}
183199767f8SToomas Soome 
184199767f8SToomas Soome #ifdef SUPPORT_DHCP
185734b3a42SToomas Soome 	if (dhcp_ok) {
186734b3a42SToomas Soome 		uint32_t leasetime;
187199767f8SToomas Soome 		bp->bp_vend[6] = DHCPREQUEST;
188199767f8SToomas Soome 		bp->bp_vend[7] = TAG_REQ_ADDR;
189199767f8SToomas Soome 		bp->bp_vend[8] = 4;
190859472daSToomas Soome 		bcopy(&rbootp->bp_yiaddr, &bp->bp_vend[9], 4);
191199767f8SToomas Soome 		bp->bp_vend[13] = TAG_SERVERID;
192199767f8SToomas Soome 		bp->bp_vend[14] = 4;
193199767f8SToomas Soome 		bcopy(&dhcp_serverip.s_addr, &bp->bp_vend[15], 4);
194199767f8SToomas Soome 		bp->bp_vend[19] = TAG_LEASETIME;
195199767f8SToomas Soome 		bp->bp_vend[20] = 4;
196199767f8SToomas Soome 		leasetime = htonl(300);
197199767f8SToomas Soome 		bcopy(&leasetime, &bp->bp_vend[21], 4);
1986dfcdabdSToomas Soome 		bootp_fill_request(&bp->bp_vend[25]);
199199767f8SToomas Soome 
200199767f8SToomas Soome 		expected_dhcpmsgtype = DHCPACK;
201199767f8SToomas Soome 
202859472daSToomas Soome 		free(pkt);
203734b3a42SToomas Soome 		if (sendrecv(d, bootpsend, bp, sizeof (*bp),
204734b3a42SToomas Soome 		    bootprecv, &pkt, (void **)&rbootp, NULL) == -1) {
205199767f8SToomas Soome 			printf("DHCPREQUEST failed\n");
206199767f8SToomas Soome 			return;
207199767f8SToomas Soome 		}
208199767f8SToomas Soome 	}
209199767f8SToomas Soome #endif
210199767f8SToomas Soome 
211859472daSToomas Soome 	myip = d->myip = rbootp->bp_yiaddr;
212859472daSToomas Soome 	servip = rbootp->bp_siaddr;
213859472daSToomas Soome 	if (rootip.s_addr == INADDR_ANY)
214859472daSToomas Soome 		rootip = servip;
215734b3a42SToomas Soome 	bcopy(rbootp->bp_file, bootfile, sizeof (bootfile));
216734b3a42SToomas Soome 	bootfile[sizeof (bootfile) - 1] = '\0';
217199767f8SToomas Soome 
218714901a9SToomas Soome 	if (!netmask) {
219714901a9SToomas Soome 		if (IN_CLASSA(ntohl(myip.s_addr)))
220714901a9SToomas Soome 			netmask = htonl(IN_CLASSA_NET);
221714901a9SToomas Soome 		else if (IN_CLASSB(ntohl(myip.s_addr)))
222714901a9SToomas Soome 			netmask = htonl(IN_CLASSB_NET);
223714901a9SToomas Soome 		else
224714901a9SToomas Soome 			netmask = htonl(IN_CLASSC_NET);
225199767f8SToomas Soome #ifdef BOOTP_DEBUG
226199767f8SToomas Soome 		if (debug)
227714901a9SToomas Soome 			printf("'native netmask' is %s\n", intoa(netmask));
228199767f8SToomas Soome #endif
229199767f8SToomas Soome 	}
230199767f8SToomas Soome 
231199767f8SToomas Soome #ifdef BOOTP_DEBUG
232199767f8SToomas Soome 	if (debug)
233199767f8SToomas Soome 		printf("mask: %s\n", intoa(netmask));
234199767f8SToomas Soome #endif
235199767f8SToomas Soome 
236199767f8SToomas Soome 	/* We need a gateway if root is on a different net */
237199767f8SToomas Soome 	if (!SAMENET(myip, rootip, netmask)) {
238199767f8SToomas Soome #ifdef BOOTP_DEBUG
239199767f8SToomas Soome 		if (debug)
240199767f8SToomas Soome 			printf("need gateway for root ip\n");
241199767f8SToomas Soome #endif
242199767f8SToomas Soome 	}
243199767f8SToomas Soome 
244199767f8SToomas Soome 	/* Toss gateway if on a different net */
245199767f8SToomas Soome 	if (!SAMENET(myip, gateip, netmask)) {
246199767f8SToomas Soome #ifdef BOOTP_DEBUG
247199767f8SToomas Soome 		if (debug)
248199767f8SToomas Soome 			printf("gateway ip (%s) bad\n", inet_ntoa(gateip));
249199767f8SToomas Soome #endif
250199767f8SToomas Soome 		gateip.s_addr = 0;
251199767f8SToomas Soome 	}
252199767f8SToomas Soome 
253199767f8SToomas Soome 	/* Bump xid so next request will be unique. */
254199767f8SToomas Soome 	++d->xid;
255859472daSToomas Soome 	free(pkt);
256199767f8SToomas Soome }
257199767f8SToomas Soome 
258199767f8SToomas Soome /* Transmit a bootp request */
259199767f8SToomas Soome static ssize_t
bootpsend(struct iodesc * d,void * pkt,size_t len)260c21cf554SToomas Soome bootpsend(struct iodesc *d, void *pkt, size_t len)
261199767f8SToomas Soome {
262199767f8SToomas Soome 	struct bootp *bp;
263199767f8SToomas Soome 
264199767f8SToomas Soome #ifdef BOOTP_DEBUG
265199767f8SToomas Soome 	if (debug)
266199767f8SToomas Soome 		printf("bootpsend: d=%lx called.\n", (long)d);
267199767f8SToomas Soome #endif
268199767f8SToomas Soome 
269199767f8SToomas Soome 	bp = pkt;
270734b3a42SToomas Soome 	bp->bp_secs = htons((ushort_t)(getsecs() - bot));
271199767f8SToomas Soome 
272199767f8SToomas Soome #ifdef BOOTP_DEBUG
273199767f8SToomas Soome 	if (debug)
274199767f8SToomas Soome 		printf("bootpsend: calling sendudp\n");
275199767f8SToomas Soome #endif
276199767f8SToomas Soome 
277199767f8SToomas Soome 	return (sendudp(d, pkt, len));
278199767f8SToomas Soome }
279199767f8SToomas Soome 
280199767f8SToomas Soome static ssize_t
bootprecv(struct iodesc * d,void ** pkt,void ** payload,time_t tleft,void * extra __unused)281890c8671SToomas Soome bootprecv(struct iodesc *d, void **pkt, void **payload, time_t tleft,
282*8eef2ab6SToomas Soome     void *extra __unused)
283199767f8SToomas Soome {
284199767f8SToomas Soome 	ssize_t n;
285199767f8SToomas Soome 	struct bootp *bp;
286859472daSToomas Soome 	void *ptr;
287199767f8SToomas Soome 
288859472daSToomas Soome #ifdef BOOTP_DEBUG
289199767f8SToomas Soome 	if (debug)
290199767f8SToomas Soome 		printf("bootp_recvoffer: called\n");
291199767f8SToomas Soome #endif
292199767f8SToomas Soome 
293859472daSToomas Soome 	ptr = NULL;
294859472daSToomas Soome 	n = readudp(d, &ptr, (void **)&bp, tleft);
295734b3a42SToomas Soome 	if (n == -1 || n < sizeof (struct bootp) - BOOTP_VENDSIZE)
296199767f8SToomas Soome 		goto bad;
297199767f8SToomas Soome 
298199767f8SToomas Soome #ifdef BOOTP_DEBUG
299199767f8SToomas Soome 	if (debug)
300859472daSToomas Soome 		printf("bootprecv: checked.  bp = %p, n = %zd\n", bp, n);
301199767f8SToomas Soome #endif
302199767f8SToomas Soome 	if (bp->bp_xid != htonl(d->xid)) {
303199767f8SToomas Soome #ifdef BOOTP_DEBUG
304199767f8SToomas Soome 		if (debug) {
305199767f8SToomas Soome 			printf("bootprecv: expected xid 0x%lx, got 0x%x\n",
306199767f8SToomas Soome 			    d->xid, ntohl(bp->bp_xid));
307199767f8SToomas Soome 		}
308199767f8SToomas Soome #endif
309199767f8SToomas Soome 		goto bad;
310199767f8SToomas Soome 	}
311199767f8SToomas Soome 
312199767f8SToomas Soome #ifdef BOOTP_DEBUG
313199767f8SToomas Soome 	if (debug)
314199767f8SToomas Soome 		printf("bootprecv: got one!\n");
315199767f8SToomas Soome #endif
316199767f8SToomas Soome 
317199767f8SToomas Soome 	/* Suck out vendor info */
318734b3a42SToomas Soome 	if (bcmp(vm_rfc1048, bp->bp_vend, sizeof (vm_rfc1048)) == 0) {
319859472daSToomas Soome 		int vsize = n - offsetof(struct bootp, bp_vend);
320734b3a42SToomas Soome 		if (vend_rfc1048(bp->bp_vend, vsize) != 0)
321734b3a42SToomas Soome 			goto bad;
322f9feecc1SToomas Soome 
323f9feecc1SToomas Soome 		/* Save copy of bootp reply or DHCP ACK message */
324f9feecc1SToomas Soome 		if (bp->bp_op == BOOTREPLY &&
325f9feecc1SToomas Soome 		    ((dhcp_ok == 1 && expected_dhcpmsgtype == DHCPACK) ||
326f9feecc1SToomas Soome 		    dhcp_ok == 0)) {
327f9feecc1SToomas Soome 			free(bootp_response);
328859472daSToomas Soome 			bootp_response = malloc(n);
329f9feecc1SToomas Soome 			if (bootp_response != NULL) {
330859472daSToomas Soome 				bootp_response_size = n;
331859472daSToomas Soome 				bcopy(bp, bootp_response, bootp_response_size);
332f9feecc1SToomas Soome 			}
333f9feecc1SToomas Soome 		}
334199767f8SToomas Soome 	}
335199767f8SToomas Soome #ifdef BOOTP_VEND_CMU
336734b3a42SToomas Soome 	else if (bcmp(vm_cmu, bp->bp_vend, sizeof (vm_cmu)) == 0)
337199767f8SToomas Soome 		vend_cmu(bp->bp_vend);
338199767f8SToomas Soome #endif
339199767f8SToomas Soome 	else
340199767f8SToomas Soome 		printf("bootprecv: unknown vendor 0x%lx\n", (long)bp->bp_vend);
341199767f8SToomas Soome 
342859472daSToomas Soome 	*pkt = ptr;
343859472daSToomas Soome 	*payload = bp;
344734b3a42SToomas Soome 	return (n);
345199767f8SToomas Soome bad:
346859472daSToomas Soome 	free(ptr);
347199767f8SToomas Soome 	errno = 0;
348199767f8SToomas Soome 	return (-1);
349199767f8SToomas Soome }
350199767f8SToomas Soome 
351199767f8SToomas Soome static int
vend_rfc1048(uchar_t * cp,uint_t len)352734b3a42SToomas Soome vend_rfc1048(uchar_t *cp, uint_t len)
353199767f8SToomas Soome {
354734b3a42SToomas Soome 	uchar_t *ep;
355199767f8SToomas Soome 	int size;
356734b3a42SToomas Soome 	uchar_t tag;
357199767f8SToomas Soome 	const char *val;
358199767f8SToomas Soome 
359199767f8SToomas Soome #ifdef BOOTP_DEBUG
360199767f8SToomas Soome 	if (debug)
361199767f8SToomas Soome 		printf("vend_rfc1048 bootp info. len=%d\n", len);
362199767f8SToomas Soome #endif
363199767f8SToomas Soome 	ep = cp + len;
364199767f8SToomas Soome 
365199767f8SToomas Soome 	/* Step over magic cookie */
366734b3a42SToomas Soome 	cp += sizeof (int);
367199767f8SToomas Soome 
368199767f8SToomas Soome 	setenv_(cp, ep, NULL);
369199767f8SToomas Soome 
370199767f8SToomas Soome 	while (cp < ep) {
371199767f8SToomas Soome 		tag = *cp++;
372199767f8SToomas Soome 		size = *cp++;
373199767f8SToomas Soome 		if (tag == TAG_END)
374199767f8SToomas Soome 			break;
375199767f8SToomas Soome 
376199767f8SToomas Soome 		if (tag == TAG_SUBNET_MASK) {
377734b3a42SToomas Soome 			bcopy(cp, &netmask, sizeof (netmask));
378199767f8SToomas Soome 		}
379199767f8SToomas Soome 		if (tag == TAG_GATEWAY) {
380734b3a42SToomas Soome 			bcopy(cp, &gateip.s_addr, sizeof (gateip.s_addr));
381199767f8SToomas Soome 		}
382199767f8SToomas Soome 		if (tag == TAG_SWAPSERVER) {
383199767f8SToomas Soome 			/* let it override bp_siaddr */
384734b3a42SToomas Soome 			bcopy(cp, &rootip.s_addr, sizeof (rootip.s_addr));
385199767f8SToomas Soome 		}
386199767f8SToomas Soome 		if (tag == TAG_ROOTPATH) {
387199767f8SToomas Soome 			if ((val = getenv("dhcp.root-path")) == NULL)
388199767f8SToomas Soome 				val = (const char *)cp;
389734b3a42SToomas Soome 			strlcpy(rootpath, val, sizeof (rootpath));
390199767f8SToomas Soome 		}
391199767f8SToomas Soome 		if (tag == TAG_HOSTNAME) {
392199767f8SToomas Soome 			if ((val = getenv("dhcp.host-name")) == NULL)
393199767f8SToomas Soome 				val = (const char *)cp;
394734b3a42SToomas Soome 			strlcpy(hostname, val, sizeof (hostname));
395199767f8SToomas Soome 		}
396c21cf554SToomas Soome 		if (tag == TAG_INTF_MTU) {
397c21cf554SToomas Soome 			intf_mtu = 0;
398c21cf554SToomas Soome 			if ((val = getenv("dhcp.interface-mtu")) != NULL) {
399c21cf554SToomas Soome 				unsigned long tmp;
400c21cf554SToomas Soome 				char *end;
401c21cf554SToomas Soome 
402c21cf554SToomas Soome 				errno = 0;
403c21cf554SToomas Soome 				/*
404c21cf554SToomas Soome 				 * Do not allow MTU to exceed max IPv4 packet
405c21cf554SToomas Soome 				 * size, max value of 16-bit word.
406c21cf554SToomas Soome 				 */
407c21cf554SToomas Soome 				tmp = strtoul(val, &end, 0);
408c21cf554SToomas Soome 				if (errno != 0 ||
409c21cf554SToomas Soome 				    *val == '\0' || *end != '\0' ||
410c21cf554SToomas Soome 				    tmp > USHRT_MAX) {
411c21cf554SToomas Soome 					printf("%s: bad value: \"%s\", "
412c21cf554SToomas Soome 					    "ignoring\n",
413c21cf554SToomas Soome 					    "dhcp.interface-mtu", val);
414c21cf554SToomas Soome 				} else {
415734b3a42SToomas Soome 					intf_mtu = (uint_t)tmp;
416c21cf554SToomas Soome 				}
417c21cf554SToomas Soome 			}
418c21cf554SToomas Soome 			if (intf_mtu == 0)
419c21cf554SToomas Soome 				intf_mtu = be16dec(cp);
420c21cf554SToomas Soome 		}
421199767f8SToomas Soome #ifdef SUPPORT_DHCP
422199767f8SToomas Soome 		if (tag == TAG_DHCP_MSGTYPE) {
423734b3a42SToomas Soome 			if (*cp != expected_dhcpmsgtype)
424734b3a42SToomas Soome 				return (-1);
425199767f8SToomas Soome 			dhcp_ok = 1;
426199767f8SToomas Soome 		}
427199767f8SToomas Soome 		if (tag == TAG_SERVERID) {
428199767f8SToomas Soome 			bcopy(cp, &dhcp_serverip.s_addr,
429734b3a42SToomas Soome 			    sizeof (dhcp_serverip.s_addr));
430199767f8SToomas Soome 		}
431199767f8SToomas Soome #endif
432199767f8SToomas Soome 		cp += size;
433199767f8SToomas Soome 	}
434734b3a42SToomas Soome 	return (0);
435199767f8SToomas Soome }
436199767f8SToomas Soome 
437199767f8SToomas Soome #ifdef BOOTP_VEND_CMU
438199767f8SToomas Soome static void
vend_cmu(uchar_t * cp)439734b3a42SToomas Soome vend_cmu(uchar_t *cp)
440199767f8SToomas Soome {
441199767f8SToomas Soome 	struct cmu_vend *vp;
442199767f8SToomas Soome 
443199767f8SToomas Soome #ifdef BOOTP_DEBUG
444199767f8SToomas Soome 	if (debug)
445199767f8SToomas Soome 		printf("vend_cmu bootp info.\n");
446199767f8SToomas Soome #endif
447199767f8SToomas Soome 	vp = (struct cmu_vend *)cp;
448199767f8SToomas Soome 
449199767f8SToomas Soome 	if (vp->v_smask.s_addr != 0) {
450714901a9SToomas Soome 		netmask = vp->v_smask.s_addr;
451199767f8SToomas Soome 	}
452199767f8SToomas Soome 	if (vp->v_dgate.s_addr != 0) {
453199767f8SToomas Soome 		gateip = vp->v_dgate;
454199767f8SToomas Soome 	}
455199767f8SToomas Soome }
456199767f8SToomas Soome #endif
457199767f8SToomas Soome 
458199767f8SToomas Soome #ifdef DHCP_ENV
459199767f8SToomas Soome /*
460199767f8SToomas Soome  * Parse DHCP options and store them into kenv variables.
461199767f8SToomas Soome  * Original code from Danny Braniss, modifications by Luigi Rizzo.
462199767f8SToomas Soome  *
463199767f8SToomas Soome  * The parser is driven by tables which specify the type and name of
464199767f8SToomas Soome  * each dhcp option and how it appears in kenv.
465199767f8SToomas Soome  * The first entry in the list contains the prefix used to set the kenv
466199767f8SToomas Soome  * name (including the . if needed), the last entry must have a 0 tag.
467199767f8SToomas Soome  * Entries do not need to be sorted though it helps for readability.
468199767f8SToomas Soome  *
469199767f8SToomas Soome  * Certain vendor-specific tables can be enabled according to DHCP_ENV.
470199767f8SToomas Soome  * Set it to 0 if you don't want any.
471199767f8SToomas Soome  */
472199767f8SToomas Soome enum opt_fmt { __NONE = 0,
473199767f8SToomas Soome 	__8 = 1, __16 = 2, __32 = 4,	/* Unsigned fields, value=size	*/
474199767f8SToomas Soome 	__IP,				/* IPv4 address			*/
475199767f8SToomas Soome 	__TXT,				/* C string			*/
476199767f8SToomas Soome 	__BYTES,			/* byte sequence, printed %02x	*/
477199767f8SToomas Soome 	__INDIR,			/* name=value			*/
478199767f8SToomas Soome 	__ILIST,			/* name=value;name=value ... */
479199767f8SToomas Soome 	__VE,				/* vendor specific, recurse	*/
480199767f8SToomas Soome };
481199767f8SToomas Soome 
482199767f8SToomas Soome struct dhcp_opt {
483199767f8SToomas Soome 	uint8_t	tag;
484199767f8SToomas Soome 	uint8_t	fmt;
485199767f8SToomas Soome 	const char	*desc;
486199767f8SToomas Soome };
487199767f8SToomas Soome 
488199767f8SToomas Soome static struct dhcp_opt vndr_opt[] = { /* Vendor Specific Options */
489199767f8SToomas Soome #if DHCP_ENV == DHCP_ENV_FREEBSD /* FreeBSD table in the original code */
490199767f8SToomas Soome 	{0,	0,	"FreeBSD"},		/* prefix */
491199767f8SToomas Soome 	{1,	__TXT,	"kernel"},
492199767f8SToomas Soome 	{2,	__TXT,	"kernelname"},
493199767f8SToomas Soome 	{3,	__TXT,	"kernel_options"},
494199767f8SToomas Soome 	{4,	__IP,	"usr-ip"},
495199767f8SToomas Soome 	{5,	__TXT,	"conf-path"},
496199767f8SToomas Soome 	{6,	__TXT,	"rc.conf0"},
497199767f8SToomas Soome 	{7,	__TXT,	"rc.conf1"},
498199767f8SToomas Soome 	{8,	__TXT,	"rc.conf2"},
499199767f8SToomas Soome 	{9,	__TXT,	"rc.conf3"},
500199767f8SToomas Soome 	{10,	__TXT,	"rc.conf4"},
501199767f8SToomas Soome 	{11,	__TXT,	"rc.conf5"},
502199767f8SToomas Soome 	{12,	__TXT,	"rc.conf6"},
503199767f8SToomas Soome 	{13,	__TXT,	"rc.conf7"},
504199767f8SToomas Soome 	{14,	__TXT,	"rc.conf8"},
505199767f8SToomas Soome 	{15,	__TXT,	"rc.conf9"},
506199767f8SToomas Soome 
507199767f8SToomas Soome 	{20,	__TXT,  "boot.nfsroot.options"},
508199767f8SToomas Soome 
509199767f8SToomas Soome 	{245,	__INDIR, ""},
510199767f8SToomas Soome 	{246,	__INDIR, ""},
511199767f8SToomas Soome 	{247,	__INDIR, ""},
512199767f8SToomas Soome 	{248,	__INDIR, ""},
513199767f8SToomas Soome 	{249,	__INDIR, ""},
514199767f8SToomas Soome 	{250,	__INDIR, ""},
515199767f8SToomas Soome 	{251,	__INDIR, ""},
516199767f8SToomas Soome 	{252,	__INDIR, ""},
517199767f8SToomas Soome 	{253,	__INDIR, ""},
518199767f8SToomas Soome 	{254,	__INDIR, ""},
519199767f8SToomas Soome 
520199767f8SToomas Soome #elif DHCP_ENV == DHCP_ENV_PXE		/* some pxe options, RFC4578 */
521199767f8SToomas Soome 	{0,	0,	"pxe"},		/* prefix */
522199767f8SToomas Soome 	{93,	__16,	"system-architecture"},
523199767f8SToomas Soome 	{94,	__BYTES,	"network-interface"},
524199767f8SToomas Soome 	{97,	__BYTES,	"machine-identifier"},
525199767f8SToomas Soome #else					/* default (empty) table */
526199767f8SToomas Soome 	{0,	0,	"dhcp.vendor."},		/* prefix */
527199767f8SToomas Soome #endif
528199767f8SToomas Soome 	{0,	__TXT,	"%soption-%d"}
529199767f8SToomas Soome };
530199767f8SToomas Soome 
531199767f8SToomas Soome static struct dhcp_opt dhcp_opt[] = {
532199767f8SToomas Soome 	/* DHCP Option names, formats and codes, from RFC2132. */
533199767f8SToomas Soome 	{0,	0,	"dhcp."},	// prefix
534199767f8SToomas Soome 	{1,	__IP,	"subnet-mask"},
535199767f8SToomas Soome 	{2,	__32,	"time-offset"}, /* this is signed */
536199767f8SToomas Soome 	{3,	__IP,	"routers"},
537199767f8SToomas Soome 	{4,	__IP,	"time-servers"},
538199767f8SToomas Soome 	{5,	__IP,	"ien116-name-servers"},
539199767f8SToomas Soome 	{6,	__IP,	"domain-name-servers"},
540199767f8SToomas Soome 	{7,	__IP,	"log-servers"},
541199767f8SToomas Soome 	{8,	__IP,	"cookie-servers"},
542199767f8SToomas Soome 	{9,	__IP,	"lpr-servers"},
543199767f8SToomas Soome 	{10,	__IP,	"impress-servers"},
544199767f8SToomas Soome 	{11,	__IP,	"resource-location-servers"},
545199767f8SToomas Soome 	{12,	__TXT,	"host-name"},
546199767f8SToomas Soome 	{13,	__16,	"boot-size"},
547199767f8SToomas Soome 	{14,	__TXT,	"merit-dump"},
548199767f8SToomas Soome 	{15,	__TXT,	"domain-name"},
549199767f8SToomas Soome 	{16,	__IP,	"swap-server"},
550199767f8SToomas Soome 	{17,	__TXT,	"root-path"},
551199767f8SToomas Soome 	{18,	__TXT,	"extensions-path"},
552199767f8SToomas Soome 	{19,	__8,	"ip-forwarding"},
553199767f8SToomas Soome 	{20,	__8,	"non-local-source-routing"},
554199767f8SToomas Soome 	{21,	__IP,	"policy-filter"},
555199767f8SToomas Soome 	{22,	__16,	"max-dgram-reassembly"},
556199767f8SToomas Soome 	{23,	__8,	"default-ip-ttl"},
557199767f8SToomas Soome 	{24,	__32,	"path-mtu-aging-timeout"},
558199767f8SToomas Soome 	{25,	__16,	"path-mtu-plateau-table"},
559199767f8SToomas Soome 	{26,	__16,	"interface-mtu"},
560199767f8SToomas Soome 	{27,	__8,	"all-subnets-local"},
561199767f8SToomas Soome 	{28,	__IP,	"broadcast-address"},
562199767f8SToomas Soome 	{29,	__8,	"perform-mask-discovery"},
563199767f8SToomas Soome 	{30,	__8,	"mask-supplier"},
564199767f8SToomas Soome 	{31,	__8,	"perform-router-discovery"},
565199767f8SToomas Soome 	{32,	__IP,	"router-solicitation-address"},
566199767f8SToomas Soome 	{33,	__IP,	"static-routes"},
567199767f8SToomas Soome 	{34,	__8,	"trailer-encapsulation"},
568199767f8SToomas Soome 	{35,	__32,	"arp-cache-timeout"},
569199767f8SToomas Soome 	{36,	__8,	"ieee802-3-encapsulation"},
570199767f8SToomas Soome 	{37,	__8,	"default-tcp-ttl"},
571199767f8SToomas Soome 	{38,	__32,	"tcp-keepalive-interval"},
572199767f8SToomas Soome 	{39,	__8,	"tcp-keepalive-garbage"},
573199767f8SToomas Soome 	{40,	__TXT,	"nis-domain"},
574199767f8SToomas Soome 	{41,	__IP,	"nis-servers"},
575199767f8SToomas Soome 	{42,	__IP,	"ntp-servers"},
576199767f8SToomas Soome 	{43,	__VE,	"vendor-encapsulated-options"},
577199767f8SToomas Soome 	{44,	__IP,	"netbios-name-servers"},
578199767f8SToomas Soome 	{45,	__IP,	"netbios-dd-server"},
579199767f8SToomas Soome 	{46,	__8,	"netbios-node-type"},
580199767f8SToomas Soome 	{47,	__TXT,	"netbios-scope"},
581199767f8SToomas Soome 	{48,	__IP,	"x-font-servers"},
582199767f8SToomas Soome 	{49,	__IP,	"x-display-managers"},
583199767f8SToomas Soome 	{50,	__IP,	"dhcp-requested-address"},
584199767f8SToomas Soome 	{51,	__32,	"dhcp-lease-time"},
585199767f8SToomas Soome 	{52,	__8,	"dhcp-option-overload"},
586199767f8SToomas Soome 	{53,	__8,	"dhcp-message-type"},
587199767f8SToomas Soome 	{54,	__IP,	"dhcp-server-identifier"},
588199767f8SToomas Soome 	{55,	__8,	"dhcp-parameter-request-list"},
589199767f8SToomas Soome 	{56,	__TXT,	"dhcp-message"},
590199767f8SToomas Soome 	{57,	__16,	"dhcp-max-message-size"},
591199767f8SToomas Soome 	{58,	__32,	"dhcp-renewal-time"},
592199767f8SToomas Soome 	{59,	__32,	"dhcp-rebinding-time"},
593199767f8SToomas Soome 	{60,	__TXT,	"vendor-class-identifier"},
594199767f8SToomas Soome 	{61,	__TXT,	"dhcp-client-identifier"},
595199767f8SToomas Soome 	{64,	__TXT,	"nisplus-domain"},
596199767f8SToomas Soome 	{65,	__IP,	"nisplus-servers"},
597199767f8SToomas Soome 	{66,	__TXT,	"tftp-server-name"},
598199767f8SToomas Soome 	{67,	__TXT,	"bootfile-name"},
599199767f8SToomas Soome 	{68,	__IP,	"mobile-ip-home-agent"},
600199767f8SToomas Soome 	{69,	__IP,	"smtp-server"},
601199767f8SToomas Soome 	{70,	__IP,	"pop-server"},
602199767f8SToomas Soome 	{71,	__IP,	"nntp-server"},
603199767f8SToomas Soome 	{72,	__IP,	"www-server"},
604199767f8SToomas Soome 	{73,	__IP,	"finger-server"},
605199767f8SToomas Soome 	{74,	__IP,	"irc-server"},
606199767f8SToomas Soome 	{75,	__IP,	"streettalk-server"},
607199767f8SToomas Soome 	{76,	__IP,	"streettalk-directory-assistance-server"},
608199767f8SToomas Soome 	{77,	__TXT,	"user-class"},
609199767f8SToomas Soome 	{85,	__IP,	"nds-servers"},
610199767f8SToomas Soome 	{86,	__TXT,	"nds-tree-name"},
611199767f8SToomas Soome 	{87,	__TXT,	"nds-context"},
612199767f8SToomas Soome 	{210,	__TXT,	"authenticate"},
613199767f8SToomas Soome 
614199767f8SToomas Soome 	/* use the following entries for arbitrary variables */
615199767f8SToomas Soome 	{246,	__ILIST, ""},
616199767f8SToomas Soome 	{247,	__ILIST, ""},
617199767f8SToomas Soome 	{248,	__ILIST, ""},
618199767f8SToomas Soome 	{249,	__ILIST, ""},
619199767f8SToomas Soome 	{250,	__INDIR, ""},
620199767f8SToomas Soome 	{251,	__INDIR, ""},
621199767f8SToomas Soome 	{252,	__INDIR, ""},
622199767f8SToomas Soome 	{253,	__INDIR, ""},
623199767f8SToomas Soome 	{254,	__INDIR, ""},
624199767f8SToomas Soome 	{0,	__TXT,	"%soption-%d"}
625199767f8SToomas Soome };
626199767f8SToomas Soome 
627199767f8SToomas Soome /*
628199767f8SToomas Soome  * parse a dhcp response, set environment variables translating options
629199767f8SToomas Soome  * names and values according to the tables above. Also set dhcp.tags
630199767f8SToomas Soome  * to the list of selected tags.
631199767f8SToomas Soome  */
632199767f8SToomas Soome static void
setenv_(uchar_t * cp,uchar_t * ep,struct dhcp_opt * opts)633734b3a42SToomas Soome setenv_(uchar_t *cp,  uchar_t *ep, struct dhcp_opt *opts)
634199767f8SToomas Soome {
635734b3a42SToomas Soome 	uchar_t	*ncp;
636734b3a42SToomas Soome 	uchar_t	tag;
637734b3a42SToomas Soome 	char	tags[512], *tp;	/* the list of tags */
638734b3a42SToomas Soome 
639734b3a42SToomas Soome #define	FLD_SEP	','	/* separator in list of elements */
640734b3a42SToomas Soome 	ncp = cp;
641734b3a42SToomas Soome 	tp = tags;
642734b3a42SToomas Soome 	if (opts == NULL)
643734b3a42SToomas Soome 		opts = dhcp_opt;
644734b3a42SToomas Soome 
645734b3a42SToomas Soome 	while (ncp < ep) {
646734b3a42SToomas Soome 		unsigned int	size;		/* option size */
647734b3a42SToomas Soome 		char *vp, *endv, buf[256];	/* the value buffer */
648734b3a42SToomas Soome 		struct dhcp_opt *op;
649734b3a42SToomas Soome 
650734b3a42SToomas Soome 		tag = *ncp++;			/* extract tag and size */
651734b3a42SToomas Soome 		size = *ncp++;
652734b3a42SToomas Soome 		cp = ncp;			/* current payload */
653734b3a42SToomas Soome 		ncp += size;			/* point to the next option */
654199767f8SToomas Soome 
655734b3a42SToomas Soome 		if (tag == TAG_END)
656734b3a42SToomas Soome 			break;
657734b3a42SToomas Soome 		if (tag == 0)
658734b3a42SToomas Soome 			continue;
659734b3a42SToomas Soome 
660734b3a42SToomas Soome 		for (op = opts+1; op->tag && op->tag != tag; op++)
661734b3a42SToomas Soome 			;
662734b3a42SToomas Soome 		/* if not found we end up on the default entry */
663734b3a42SToomas Soome 
664734b3a42SToomas Soome 		/*
665734b3a42SToomas Soome 		 * Copy data into the buffer. libstand does not have snprintf
666734b3a42SToomas Soome 		 * so we need to be careful with sprintf(). With strings, the
667734b3a42SToomas Soome 		 * source is always <256 char so shorter than the buffer so we
668734b3a42SToomas Soome 		 * are safe; with other arguments, the longest string is
669734b3a42SToomas Soome 		 * inet_ntoa which is 16 bytes so we make sure to have always
670734b3a42SToomas Soome 		 * enough room in the string before trying an sprint.
671734b3a42SToomas Soome 		 */
672734b3a42SToomas Soome 		vp = buf;
673734b3a42SToomas Soome 		*vp = '\0';
674734b3a42SToomas Soome 		/* last valid write position */
675734b3a42SToomas Soome 		endv = buf + sizeof (buf) - 1 - 16;
676734b3a42SToomas Soome 
677734b3a42SToomas Soome 		switch (op->fmt) {
678734b3a42SToomas Soome 		case __NONE:
679734b3a42SToomas Soome 			break;	/* should not happen */
680734b3a42SToomas Soome 
681734b3a42SToomas Soome 		case __VE: /* recurse, vendor specific */
682734b3a42SToomas Soome 			setenv_(cp, cp+size, vndr_opt);
683734b3a42SToomas Soome 			break;
684199767f8SToomas Soome 
685734b3a42SToomas Soome 		case __IP:	/* ip address */
686734b3a42SToomas Soome 			for (; size > 0 && vp < endv; size -= 4, cp += 4) {
687734b3a42SToomas Soome 				struct	in_addr in_ip;	/* ip addresses */
688734b3a42SToomas Soome 				if (vp != buf)
689734b3a42SToomas Soome 					*vp++ = FLD_SEP;
690734b3a42SToomas Soome 				bcopy(cp, &in_ip.s_addr, sizeof (in_ip.s_addr));
691734b3a42SToomas Soome 				sprintf(vp, "%s", inet_ntoa(in_ip));
692734b3a42SToomas Soome 				vp += strlen(vp);
693734b3a42SToomas Soome 			}
694734b3a42SToomas Soome 			break;
695734b3a42SToomas Soome 
696734b3a42SToomas Soome 		case __BYTES:	/* opaque byte string */
697734b3a42SToomas Soome 			for (; size > 0 && vp < endv; size -= 1, cp += 1) {
698734b3a42SToomas Soome 				sprintf(vp, "%02x", *cp);
699734b3a42SToomas Soome 				vp += strlen(vp);
700734b3a42SToomas Soome 			}
701734b3a42SToomas Soome 			break;
702734b3a42SToomas Soome 
703734b3a42SToomas Soome 		case __TXT:
704734b3a42SToomas Soome 			bcopy(cp, buf, size);	/* cannot overflow */
705734b3a42SToomas Soome 			buf[size] = 0;
706734b3a42SToomas Soome 			break;
707734b3a42SToomas Soome 
708734b3a42SToomas Soome 		case __32:
709734b3a42SToomas Soome 		case __16:
710734b3a42SToomas Soome 		case __8:	/* op->fmt is also the length of each field */
711734b3a42SToomas Soome 			for (; size > 0 && vp < endv;
712734b3a42SToomas Soome 			    size -= op->fmt, cp += op->fmt) {
713734b3a42SToomas Soome 				uint32_t v;
714734b3a42SToomas Soome 				if (op->fmt == __32)
715734b3a42SToomas Soome 					v = (cp[0]<<24) + (cp[1]<<16) +
716734b3a42SToomas Soome 					    (cp[2]<<8) + cp[3];
717734b3a42SToomas Soome 				else if (op->fmt == __16)
718734b3a42SToomas Soome 					v = (cp[0]<<8) + cp[1];
719734b3a42SToomas Soome 				else
720734b3a42SToomas Soome 					v = cp[0];
721734b3a42SToomas Soome 				if (vp != buf)
722734b3a42SToomas Soome 					*vp++ = FLD_SEP;
723734b3a42SToomas Soome 				sprintf(vp, "%u", v);
724734b3a42SToomas Soome 				vp += strlen(vp);
725734b3a42SToomas Soome 			}
726734b3a42SToomas Soome 			break;
727734b3a42SToomas Soome 
728734b3a42SToomas Soome 		case __INDIR:	/* name=value */
729734b3a42SToomas Soome 		case __ILIST:	/* name=value;name=value... */
730734b3a42SToomas Soome 			bcopy(cp, buf, size);	/* cannot overflow */
731734b3a42SToomas Soome 			buf[size] = '\0';
732734b3a42SToomas Soome 			for (endv = buf; endv; endv = vp) {
733734b3a42SToomas Soome 				char *s = NULL;	/* semicolon ? */
734734b3a42SToomas Soome 
735734b3a42SToomas Soome 				/* skip leading whitespace */
736734b3a42SToomas Soome 				while (*endv && strchr(" \t\n\r", *endv))
737734b3a42SToomas Soome 					endv++;
738734b3a42SToomas Soome 				/* find name=value separator */
739734b3a42SToomas Soome 				vp = strchr(endv, '=');
740734b3a42SToomas Soome 				if (!vp)
741734b3a42SToomas Soome 					break;
742734b3a42SToomas Soome 				*vp++ = 0;
743734b3a42SToomas Soome 				if (op->fmt == __ILIST && (s = strchr(vp, ';')))
744734b3a42SToomas Soome 					*s++ = '\0';
745734b3a42SToomas Soome 				setenv(endv, vp, 1);
746734b3a42SToomas Soome 				vp = s;	/* prepare for next round */
747734b3a42SToomas Soome 			}
748734b3a42SToomas Soome 			buf[0] = '\0';	/* option already done */
749734b3a42SToomas Soome 		}
750734b3a42SToomas Soome 
751734b3a42SToomas Soome 		if (tp - tags < sizeof (tags) - 5) {
752734b3a42SToomas Soome 			/* add tag to the list */
753734b3a42SToomas Soome 			if (tp != tags)
754734b3a42SToomas Soome 				*tp++ = FLD_SEP;
755734b3a42SToomas Soome 			sprintf(tp, "%d", tag);
756734b3a42SToomas Soome 			tp += strlen(tp);
757734b3a42SToomas Soome 		}
758734b3a42SToomas Soome 		if (buf[0]) {
759734b3a42SToomas Soome 			char	env[128];	/* the string name */
760734b3a42SToomas Soome 
761734b3a42SToomas Soome 			if (op->tag == 0)
762734b3a42SToomas Soome 				sprintf(env, op->desc, opts[0].desc, tag);
763734b3a42SToomas Soome 			else
764734b3a42SToomas Soome 				sprintf(env, "%s%s", opts[0].desc, op->desc);
765734b3a42SToomas Soome 			/*
766734b3a42SToomas Soome 			 * Do not replace existing values in the environment,
767734b3a42SToomas Soome 			 * so that locally-obtained values can override
768734b3a42SToomas Soome 			 * server-provided values.
769734b3a42SToomas Soome 			 */
770734b3a42SToomas Soome 			setenv(env, buf, 0);
771734b3a42SToomas Soome 		}
772199767f8SToomas Soome 	}
773734b3a42SToomas Soome 	if (tp != tags) {
774734b3a42SToomas Soome 		char	env[128];	/* the string name */
775734b3a42SToomas Soome 		sprintf(env, "%stags", opts[0].desc);
776734b3a42SToomas Soome 		setenv(env, tags, 1);
777199767f8SToomas Soome 	}
778199767f8SToomas Soome }
779199767f8SToomas Soome #endif /* additional dhcp */