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 *
21199767f8SToomas Soome * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22199767f8SToomas Soome * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23199767f8SToomas Soome * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24199767f8SToomas Soome * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25199767f8SToomas Soome * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26199767f8SToomas Soome * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27199767f8SToomas Soome * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28199767f8SToomas Soome * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29199767f8SToomas Soome * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30199767f8SToomas Soome * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
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,
2828eef2ab6SToomas 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 */
780