xref: /netbsd-src/sys/nfs/nfs_bootdhcp.c (revision dc306354b0b29af51801a7632f1e95265a68cd81)
1 /*	$NetBSD: nfs_bootdhcp.c,v 1.9 1998/09/13 13:49:29 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1995, 1997 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Adam Glass and Gordon W. Ross.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the NetBSD
21  *	Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * Support for NFS diskless booting with BOOTP (RFC951, RFC1048)
41  *
42  * History:
43  *
44  * Tor Egge developed the initial version of this code based on
45  * the Sun RPC/bootparam sources nfs_boot.c and krpc_subr.c and
46  * submitted that work to NetBSD as bugreport "kern/2351" on
47  * 29 Apr 1996.
48  *
49  * Gordon Ross reorganized Tor's version into this form and
50  * integrated it into the NetBSD sources during Aug 1997.
51  */
52 
53 #include "opt_nfs_boot.h"
54 
55 #include <sys/param.h>
56 #include <sys/systm.h>
57 #include <sys/kernel.h>
58 #include <sys/conf.h>
59 #include <sys/device.h>
60 #include <sys/ioctl.h>
61 #include <sys/proc.h>
62 #include <sys/mount.h>
63 #include <sys/mbuf.h>
64 #include <sys/reboot.h>
65 #include <sys/socket.h>
66 #include <sys/socketvar.h>
67 
68 #include <net/if.h>
69 #include <net/if_types.h>
70 #include <net/if_arp.h> 	/* ARPHRD_ETHER, etc. */
71 #include <net/if_dl.h>
72 #include <net/if_ether.h>
73 #include <net/route.h>
74 
75 #include <netinet/in.h>
76 #include <netinet/if_inarp.h>
77 
78 #include <nfs/rpcv2.h>
79 
80 #include <nfs/nfsproto.h>
81 #include <nfs/nfs.h>
82 #include <nfs/nfsmount.h>
83 #include <nfs/nfsdiskless.h>
84 
85 /*
86  * There are two implementations of NFS diskless boot.
87  * This implementation uses BOOTP (RFC951, RFC1048), and
88  * the other uses Sun RPC/bootparams (nfs_bootparam.c).
89  *
90  * This method gets everything it needs with one BOOTP
91  * request and reply.  Note that this actually uses only
92  * the old BOOTP functionality subset of DHCP.  It is not
93  * clear that DHCP provides any advantage over BOOTP for
94  * diskless boot.  DHCP allows the server to assign an IP
95  * address without any a-priori knowledge of the client,
96  * but we require that the server has a-priori knowledge
97  * of the client so it can export our (unique) NFS root.
98  * Given that the server needs a-priori knowledge about
99  * the client anyway, it might as well assign a fixed IP
100  * address for the client and support BOOTP.
101  *
102  * On the other hand, disk-FULL clients may use DHCP, but
103  * in that case the DHCP client should be user-mode code,
104  * and has no bearing on the code below. -gwr
105  */
106 
107 /* Begin stuff from bootp.h */
108 /* Definitions from RFC951 */
109 #define BP_CHADDR_LEN	 16
110 #define BP_SNAME_LEN	 64
111 #define BP_FILE_LEN	128
112 #define BP_VEND_LEN	 64
113 struct bootp {
114 	u_int8_t	bp_op;		/* packet opcode type */
115 	u_int8_t	bp_htype;	/* hardware addr type */
116 	u_int8_t	bp_hlen;	/* hardware addr length */
117 	u_int8_t	bp_hops;	/* gateway hops */
118 	u_int32_t	bp_xid;		/* transaction ID */
119 	u_int16_t	bp_secs;	/* seconds since boot began */
120 	u_int16_t	bp_flags;	/* RFC1532 broadcast, etc. */
121 	struct in_addr	bp_ciaddr;	/* client IP address */
122 	struct in_addr	bp_yiaddr;	/* 'your' IP address */
123 	struct in_addr	bp_siaddr;	/* server IP address */
124 	struct in_addr	bp_giaddr;	/* gateway IP address */
125 	u_int8_t bp_chaddr[BP_CHADDR_LEN]; /* client hardware address */
126 	char	bp_sname[BP_SNAME_LEN]; /* server host name */
127 	char	bp_file[BP_FILE_LEN];	/* boot file name */
128 	u_int8_t bp_vend[BP_VEND_LEN];	/* RFC1048 options */
129 	/*
130 	 * Note that BOOTP packets are allowed to be longer
131 	 * (see RFC 1532 sect. 2.1) and common practice is to
132 	 * allow the option data in bp_vend to extend into the
133 	 * additional space provided in longer packets.
134 	 */
135 };
136 
137 #define IPPORT_BOOTPS 67
138 #define IPPORT_BOOTPC 68
139 
140 #define BOOTREQUEST		1
141 #define BOOTREPLY		2
142 
143 /*
144  * Is this available from the sockaddr_dl somehow?
145  * Perhaps (struct arphdr)->ar_hrd = ARPHRD_ETHER?
146  * The interface has ->if_type but not the ARP fmt.
147  */
148 #define HTYPE_ETHERNET		  1
149 
150 /*
151  * Vendor magic cookie (v_magic) for RFC1048
152  */
153 static const u_int8_t vm_rfc1048[4] = { 99, 130, 83, 99 };
154 
155 /*
156  * Tag values used to specify what information is being supplied in
157  * the vendor (options) data area of the packet.
158  */
159 /* RFC 1048 */
160 #define TAG_END			((unsigned char) 255)
161 #define TAG_PAD			((unsigned char)   0)
162 #define TAG_SUBNET_MASK		((unsigned char)   1)
163 #define TAG_TIME_OFFSET		((unsigned char)   2)
164 #define TAG_GATEWAY		((unsigned char)   3)
165 #define TAG_TIME_SERVER		((unsigned char)   4)
166 #define TAG_NAME_SERVER		((unsigned char)   5)
167 #define TAG_DOMAIN_SERVER	((unsigned char)   6)
168 #define TAG_LOG_SERVER		((unsigned char)   7)
169 #define TAG_COOKIE_SERVER	((unsigned char)   8)
170 #define TAG_LPR_SERVER		((unsigned char)   9)
171 #define TAG_IMPRESS_SERVER	((unsigned char)  10)
172 #define TAG_RLP_SERVER		((unsigned char)  11)
173 #define TAG_HOST_NAME		((unsigned char)  12)
174 #define TAG_BOOT_SIZE		((unsigned char)  13)
175 /* RFC 1395 */
176 #define TAG_DUMP_FILE		((unsigned char)  14)
177 #define TAG_DOMAIN_NAME		((unsigned char)  15)
178 #define TAG_SWAP_SERVER		((unsigned char)  16)
179 #define TAG_ROOT_PATH		((unsigned char)  17)
180 /* End of stuff from bootp.h */
181 
182 #ifdef NFS_BOOT_DHCP
183 #define TAG_REQ_ADDR		((unsigned char)  50)
184 #define TAG_LEASETIME		((unsigned char)  51)
185 #define TAG_OVERLOAD		((unsigned char)  52)
186 #define TAG_DHCP_MSGTYPE	((unsigned char)  53)
187 #define TAG_SERVERID		((unsigned char)  54)
188 #define TAG_PARAM_REQ		((unsigned char)  55)
189 #define TAG_MSG			((unsigned char)  56)
190 #define TAG_MAXSIZE		((unsigned char)  57)
191 #define TAG_T1			((unsigned char)  58)
192 #define TAG_T2			((unsigned char)  59)
193 #define TAG_CLASSID		((unsigned char)  60)
194 #define TAG_CLIENTID		((unsigned char)  61)
195 #endif
196 
197 #ifdef NFS_BOOT_DHCP
198 #define DHCPDISCOVER 1
199 #define DHCPOFFER 2
200 #define DHCPREQUEST 3
201 #define DHCPDECLINE 4
202 #define DHCPACK 5
203 #define DHCPNAK 6
204 #define DHCPRELEASE 7
205 #endif
206 
207 #ifdef NFS_BOOT_DHCP
208 #define BOOTP_SIZE_MAX	(sizeof(struct bootp)+312-64)
209 #else
210 /*
211  * The "extended" size is somewhat arbitrary, but is
212  * constrained by the maximum message size specified
213  * by RFC1533 (567 total).  This value increases the
214  * space for options from 64 bytes to 256 bytes.
215  */
216 #define BOOTP_SIZE_MAX	(sizeof(struct bootp)+256-64)
217 #endif
218 #define BOOTP_SIZE_MIN	(sizeof(struct bootp))
219 
220 /* Convenience macro */
221 #define INTOHL(ina) ((u_int32_t)ntohl((ina).s_addr))
222 
223 static int bootpc_call __P((struct socket *, struct ifnet *,
224 			struct nfs_diskless *, struct proc *));
225 static void bootp_extract __P((struct bootp *, int, struct nfs_diskless *));
226 
227 /* #define DEBUG	XXX */
228 
229 #ifdef	DEBUG
230 #define DPRINT(s) printf("nfs_boot: %s\n", s)
231 #else
232 #define DPRINT(s) (void)0
233 #endif
234 
235 
236 /*
237  * Get our boot parameters using BOOTP.
238  */
239 int
240 nfs_bootdhcp(ifp, nd, procp)
241 	struct ifnet *ifp;
242 	struct nfs_diskless *nd;
243 	struct proc *procp;
244 {
245 	struct ifaliasreq iareq;
246 	struct socket *so;
247 	struct sockaddr_in *sin;
248 	int error;
249 
250 	/*
251 	 * Get a socket to use for various things in here.
252 	 * After this, use "goto out" to cleanup and return.
253 	 */
254 	error = socreate(AF_INET, &so, SOCK_DGRAM, 0);
255 	if (error) {
256 		printf("nfs_boot: socreate, error=%d\n", error);
257 		return (error);
258 	}
259 
260 	/*
261 	 * Do enough of ifconfig(8) so that the chosen interface
262 	 * can talk to the servers.  Use address zero for now.
263 	 */
264 	memset(&iareq, 0, sizeof(iareq));
265 	memcpy(iareq.ifra_name, ifp->if_xname, IFNAMSIZ);
266 	/* Set the I/F address */
267 	sin = (struct sockaddr_in *)&iareq.ifra_addr;
268 	sin->sin_len = sizeof(*sin);
269 	sin->sin_family = AF_INET;
270 	sin->sin_addr.s_addr = INADDR_ANY;
271 	/* Leave subnetmask unspecified (len=0) */
272 	/* Set the broadcast addr. */
273 	sin = (struct sockaddr_in *)&iareq.ifra_broadaddr;
274 	sin->sin_len = sizeof(*sin);
275 	sin->sin_family = AF_INET;
276 	sin->sin_addr.s_addr = INADDR_BROADCAST;
277 	error = ifioctl(so, SIOCAIFADDR, (caddr_t)&iareq, procp);
278 	if (error) {
279 		printf("nfs_boot: set ifaddr zero, error=%d\n", error);
280 		goto out;
281 	}
282 
283 	/* This function call does the real send/recv work. */
284 	error = bootpc_call(so, ifp, nd, procp);
285 	/* Get rid of the temporary (zero) IP address. */
286 	/*
287 	 * XXX SIOCDIFADDR takes a "struct ifreq", which is
288 	 * an exact subset of "struct ifaliasreq".
289 	 */
290 	(void) ifioctl(so, SIOCDIFADDR, (caddr_t)&iareq, procp);
291 	/* NOW we can test the error from bootpc_call. */
292 	if (error)
293 		goto out;
294 
295 	/*
296 	 * Do ifconfig with our real IP address and mask.
297 	 */
298 	/* I/F address */
299 	sin = (struct sockaddr_in *)&iareq.ifra_addr;
300 	sin->sin_addr = nd->nd_myip;
301 	/* subnetmask */
302 	if (nd->nd_mask.s_addr) {
303 		sin = (struct sockaddr_in *)&iareq.ifra_mask;
304 		sin->sin_len = sizeof(*sin);
305 		sin->sin_family = AF_INET;
306 		sin->sin_addr = nd->nd_mask;
307 	}
308 	/* Let ifioctl() default the broadcast address. */
309 	sin = (struct sockaddr_in *)&iareq.ifra_broadaddr;
310 	sin->sin_len = 0;
311 	sin->sin_family = 0;
312 	sin->sin_addr.s_addr = 0;
313 	error = ifioctl(so, SIOCAIFADDR, (caddr_t)&iareq, procp);
314 	if (error) {
315 		printf("nfs_boot: set ifaddr real, error=%d\n", error);
316 		goto out;
317 	}
318 
319 out:
320 	soclose(so);
321 	return (error);
322 }
323 
324 struct bootpcontext {
325 	int xid;
326 	u_char *haddr;
327 	u_char halen;
328 	struct bootp *replybuf;
329 	int replylen;
330 #ifdef NFS_BOOT_DHCP
331 	char expected_dhcpmsgtype, dhcp_ok;
332 	struct in_addr dhcp_serverip;
333 #endif
334 };
335 
336 static int bootpset __P((struct mbuf*, void*, int));
337 static int bootpcheck __P((struct mbuf*, void*));
338 
339 static int
340 bootpset(m, context, waited)
341 	struct mbuf *m;
342 	void *context;
343 	int waited;
344 {
345 	struct bootp *bootp;
346 
347 	/* we know it's contigous (in 1 mbuf cluster) */
348 	bootp = mtod(m, struct bootp*);
349 
350 	bootp->bp_secs = htons(waited);
351 
352 	return (0);
353 }
354 
355 static int
356 bootpcheck(m, context)
357 	struct mbuf *m;
358 	void *context;
359 {
360 	struct bootp *bootp;
361 	struct bootpcontext *bpc = context;
362 	u_int tag, len;
363 	u_char *p, *limit;
364 
365 	/*
366 	 * Is this a valid reply?
367 	 */
368 	if (m->m_pkthdr.len < BOOTP_SIZE_MIN) {
369 		DPRINT("short packet");
370 		return (-1);
371 	}
372 	if (m->m_pkthdr.len > BOOTP_SIZE_MAX) {
373 		DPRINT("long packet");
374 		return (-1);
375 	}
376 
377 	/*
378 	 * don't make first checks more expensive than necessary
379 	 */
380 #define ofs(what, elem) ((int)&(((what *)0)->elem))
381 	if (m->m_len < ofs(struct bootp, bp_secs)) {
382 		m = m_pullup(m, ofs(struct bootp, bp_secs));
383 		if (m == NULL)
384 			return (-1);
385 	}
386 #undef ofs
387 	bootp = mtod(m, struct bootp*);
388 
389 	if (bootp->bp_op != BOOTREPLY) {
390 		DPRINT("not reply");
391 		return (-1);
392 	}
393 	if (bootp->bp_hlen != bpc->halen) {
394 		DPRINT("bad hwa_len");
395 		return (-1);
396 	}
397 	if (memcmp(bootp->bp_chaddr, bpc->haddr, bpc->halen)) {
398 		DPRINT("wrong hwaddr");
399 		return (-1);
400 	}
401 	if (bootp->bp_xid != bpc->xid) {
402 		DPRINT("wrong xid");
403 		return (-1);
404 	}
405 
406 	/*
407 	 * OK, it's worth to look deeper.
408 	 * We copy the mbuf into a flat buffer here because
409 	 * m_pullup() is a bit limited for this purpose
410 	 * (doesn't allocate a cluster if necessary).
411 	 */
412 	bpc->replylen = m->m_pkthdr.len;
413 	m_copydata(m, 0, bpc->replylen, (caddr_t)bpc->replybuf);
414 	bootp = bpc->replybuf;
415 
416 	/*
417 	 * Check if the IP address we get looks correct.
418 	 * (DHCP servers can send junk to unknown clients.)
419 	 * XXX more checks might be needed
420 	 */
421 	if (bootp->bp_yiaddr.s_addr == INADDR_ANY ||
422 	    bootp->bp_yiaddr.s_addr == INADDR_BROADCAST) {
423 		printf("nfs_boot: wrong IP addr 0x%x",
424 		       INTOHL(bootp->bp_yiaddr));
425 		goto warn;
426 	}
427 
428 	/*
429 	 * Check the vendor data.
430 	 */
431 	if (memcmp(bootp->bp_vend, vm_rfc1048, 4)) {
432 		printf("nfs_boot: reply missing options");
433 		goto warn;
434 	}
435 	p = &bootp->bp_vend[4];
436 	limit = ((char*)bootp) + bpc->replylen;
437 	while (p < limit) {
438 		tag = *p++;
439 		if (tag == TAG_END)
440 			break;
441 		if (tag == TAG_PAD)
442 			continue;
443 		len = *p++;
444 		if ((p + len) > limit) {
445 			printf("nfs_boot: option %d too long", tag);
446 			goto warn;
447 		}
448 		switch (tag) {
449 #ifdef NFS_BOOT_DHCP
450 		    case TAG_DHCP_MSGTYPE:
451 			if (*p != bpc->expected_dhcpmsgtype)
452 				return (-1);
453 			bpc->dhcp_ok = 1;
454 			break;
455 		    case TAG_SERVERID:
456 			memcpy(&bpc->dhcp_serverip.s_addr, p,
457 			      sizeof(bpc->dhcp_serverip.s_addr));
458 			break;
459 #endif
460 		    default:
461 			break;
462 		}
463 		p += len;
464 	}
465 	return (0);
466 
467 warn:
468 	printf(" (bad reply from 0x%x)\n", INTOHL(bootp->bp_siaddr));
469 	return (-1);
470 }
471 
472 static int
473 bootpc_call(so, ifp, nd, procp)
474 	struct socket *so;
475 	struct ifnet *ifp;
476 	struct nfs_diskless *nd;
477 	struct proc *procp;
478 {
479 	static u_int32_t xid = ~0xFF;
480 	struct bootp *bootp;	/* request */
481 	struct mbuf *m, *nam;
482 	struct sockaddr_in *sin;
483 	int error;
484 	u_char *haddr;
485 	u_char hafmt, halen;
486 	struct bootpcontext bpc;
487 
488 	/*
489 	 * Initialize to NULL anything that will hold an allocation,
490 	 * and free each at the end if not null.
491 	 */
492 	bpc.replybuf = NULL;
493 	m = nam = NULL;
494 
495 	/* Record our H/W (Ethernet) address. */
496 	{	struct sockaddr_dl *sdl = ifp->if_sadl;
497 		switch (sdl->sdl_type) {
498 		    case IFT_ETHER:
499 		    case IFT_FDDI:
500 			hafmt = HTYPE_ETHERNET;
501 			break;
502 		    default:
503 			printf("bootp: unsupported interface type %d\n",
504 			       sdl->sdl_type);
505 			error = EINVAL;
506 			goto out;
507 		}
508 		halen = sdl->sdl_alen;
509 		haddr = (unsigned char *)LLADDR(sdl);
510 	}
511 
512 	/*
513 	 * Skip the route table when sending on this socket.
514 	 * If this is not done, ip_output finds the loopback
515 	 * interface (why?) and then fails because broadcast
516 	 * is not supported on that interface...
517 	 */
518 	{	int32_t *opt;
519 		m = m_get(M_WAIT, MT_SOOPTS);
520 		opt = mtod(m, int32_t *);
521 		m->m_len = sizeof(*opt);
522 		*opt = 1;
523 		error = sosetopt(so, SOL_SOCKET, SO_DONTROUTE, m);
524 		m = NULL;	/* was consumed */
525 	}
526 	if (error) {
527 		DPRINT("SO_DONTROUTE");
528 		goto out;
529 	}
530 
531 	/* Enable broadcast. */
532 	if ((error = nfs_boot_enbroadcast(so))) {
533 		DPRINT("SO_BROADCAST");
534 		goto out;
535 	}
536 
537 	/* Set the receive timeout for the socket. */
538 	if ((error = nfs_boot_setrecvtimo(so))) {
539 		DPRINT("SO_RCVTIMEO");
540 		goto out;
541 	}
542 
543 	/*
544 	 * Bind the local endpoint to a bootp client port.
545 	 */
546 	if ((error = nfs_boot_sobind_ipport(so, IPPORT_BOOTPC))) {
547 		DPRINT("bind failed\n");
548 		goto out;
549 	}
550 
551 	/*
552 	 * Setup socket address for the server.
553 	 */
554 	nam = m_get(M_WAIT, MT_SONAME);
555 	sin = mtod(nam, struct sockaddr_in *);
556 	sin->sin_len = nam->m_len = sizeof(*sin);
557 	sin->sin_family = AF_INET;
558 	sin->sin_addr.s_addr = INADDR_BROADCAST;
559 	sin->sin_port = htons(IPPORT_BOOTPS);
560 
561 	/*
562 	 * Allocate buffer used for request
563 	 */
564 	m = m_gethdr(M_WAIT, MT_DATA);
565 	MCLGET(m, M_WAIT);
566 	bootp = mtod(m, struct bootp*);
567 	m->m_pkthdr.len = m->m_len = BOOTP_SIZE_MAX;
568 	m->m_pkthdr.rcvif = NULL;
569 
570 	/*
571 	 * Build the BOOTP reqest message.
572 	 * Note: xid is host order! (opaque to server)
573 	 */
574 	memset((caddr_t)bootp, 0, BOOTP_SIZE_MAX);
575 	bootp->bp_op    = BOOTREQUEST;
576 	bootp->bp_htype = hafmt;
577 	bootp->bp_hlen  = halen;	/* Hardware address length */
578 	bootp->bp_xid = ++xid;
579 	memcpy(bootp->bp_chaddr, haddr, halen);
580 	/* Fill-in the vendor data. */
581 	memcpy(bootp->bp_vend, vm_rfc1048, 4);
582 #ifdef NFS_BOOT_DHCP
583 	bootp->bp_vend[4] = TAG_DHCP_MSGTYPE;
584 	bootp->bp_vend[5] = 1;
585 	bootp->bp_vend[6] = DHCPDISCOVER;
586 	bootp->bp_vend[7] = TAG_END;
587 #else
588 	bootp->bp_vend[4] = TAG_END;
589 #endif
590 
591 	bpc.xid = xid;
592 	bpc.haddr = haddr;
593 	bpc.halen = halen;
594 	bpc.replybuf = malloc(BOOTP_SIZE_MAX, M_DEVBUF, M_WAITOK);
595 	if (bpc.replybuf == NULL)
596 		panic("nfs_boot: malloc reply buf");
597 #ifdef NFS_BOOT_DHCP
598 	bpc.expected_dhcpmsgtype = DHCPOFFER;
599 	bpc.dhcp_ok = 0;
600 #endif
601 
602 	error = nfs_boot_sendrecv(so, nam, bootpset, m,
603 				  bootpcheck, 0, 0, &bpc);
604 	if (error)
605 		goto out;
606 
607 #ifdef NFS_BOOT_DHCP
608 	if (bpc.dhcp_ok) {
609 		u_int32_t leasetime;
610 		bootp->bp_vend[6] = DHCPREQUEST;
611 		bootp->bp_vend[7] = TAG_REQ_ADDR;
612 		bootp->bp_vend[8] = 4;
613 		memcpy(&bootp->bp_vend[9], &bpc.replybuf->bp_yiaddr, 4);
614 		bootp->bp_vend[13] = TAG_SERVERID;
615 		bootp->bp_vend[14] = 4;
616 		memcpy(&bootp->bp_vend[15], &bpc.dhcp_serverip.s_addr, 4);
617 		bootp->bp_vend[19] = TAG_LEASETIME;
618 		bootp->bp_vend[20] = 4;
619 		leasetime = htonl(300);
620 		memcpy(&bootp->bp_vend[21], &leasetime, 4);
621 		bootp->bp_vend[25] = TAG_END;
622 
623 		bpc.expected_dhcpmsgtype = DHCPACK;
624 
625 		error = nfs_boot_sendrecv(so, nam, bootpset, m,
626 					  bootpcheck, 0, 0, &bpc);
627 		if (error)
628 			goto out;
629 	}
630 #endif
631 
632 	/*
633 	 * bootpcheck() has copied the receive mbuf into
634 	 * the buffer at bpc.replybuf.
635 	 */
636 #ifdef NFS_BOOT_DHCP
637 	printf("nfs_boot: %s server: 0x%x\n",
638 	       (bpc.dhcp_ok ? "DHCP" : "BOOTP"),
639 #else
640 	printf("nfs_boot: BOOTP server: 0x%x\n",
641 #endif
642 	       INTOHL(bpc.replybuf->bp_siaddr));
643 
644 	bootp_extract(bpc.replybuf, bpc.replylen, nd);
645 
646 out:
647 	if (bpc.replybuf)
648 		free(bpc.replybuf, M_DEVBUF);
649 	if (m)
650 		m_freem(m);
651 	if (nam)
652 		m_freem(nam);
653 	return (error);
654 }
655 
656 static void
657 bootp_extract(bootp, replylen, nd)
658 	struct bootp *bootp;
659 	int replylen;
660 	struct nfs_diskless *nd;
661 {
662 	struct sockaddr_in *sin;
663 	struct in_addr netmask;
664 	struct in_addr gateway;
665 	struct in_addr rootserver;
666 	char *myname;	/* my hostname */
667 	char *mydomain;	/* my domainname */
668 	char *rootpath;
669 	int mynamelen;
670 	int mydomainlen;
671 	int rootpathlen;
672 	u_int tag, len;
673 	u_char *p, *limit;
674 
675 	/* Default these to "unspecified". */
676 	netmask.s_addr = 0;
677 	gateway.s_addr = 0;
678 	mydomain    = myname    = rootpath = NULL;
679 	mydomainlen = mynamelen = rootpathlen = 0;
680 	/* default root server to bootp next-server */
681 	rootserver = bootp->bp_siaddr;
682 
683 	p = &bootp->bp_vend[4];
684 	limit = ((char*)bootp) + replylen;
685 	while (p < limit) {
686 		tag = *p++;
687 		if (tag == TAG_END)
688 			break;
689 		if (tag == TAG_PAD)
690 			continue;
691 		len = *p++;
692 #if 0 /* already done in bootpcheck() */
693 		if ((p + len) > limit) {
694 			printf("nfs_boot: option %d too long\n", tag);
695 			break;
696 		}
697 #endif
698 		switch (tag) {
699 		    case TAG_SUBNET_MASK:
700 			memcpy(&netmask, p, 4);
701 			break;
702 		    case TAG_GATEWAY:
703 			/* Routers */
704 			memcpy(&gateway, p, 4);
705 			break;
706 		    case TAG_HOST_NAME:
707 			if (len >= sizeof(hostname)) {
708 				printf("nfs_boot: host name >=%d bytes",
709 				       sizeof(hostname));
710 				break;
711 			}
712 			myname = p;
713 			mynamelen = len;
714 			break;
715 		    case TAG_DOMAIN_NAME:
716 			if (len >= sizeof(domainname)) {
717 				printf("nfs_boot: domain name >=%d bytes",
718 				       sizeof(domainname));
719 				break;
720 			}
721 			mydomain = p;
722 			mydomainlen = len;
723 			break;
724 		    case TAG_ROOT_PATH:
725 			/* Leave some room for the server name. */
726 			if (len >= (MNAMELEN-10)) {
727 				printf("nfs_boot: rootpath >=%d bytes",
728 				       (MNAMELEN-10));
729 				break;
730 			}
731 			rootpath = p;
732 			rootpathlen = len;
733 			break;
734 		    case TAG_SWAP_SERVER:
735 			/* override NFS server address */
736 			memcpy(&rootserver, p, 4);
737 			break;
738 		    default:
739 			break;
740 		}
741 		p += len;
742 	}
743 
744 	/*
745 	 * Store and print network config info.
746 	 */
747 	if (myname) {
748 		myname[mynamelen] = '\0';
749 		strncpy(hostname, myname, sizeof(hostname));
750 		hostnamelen = mynamelen;
751 		printf("nfs_boot: my_name=%s\n", hostname);
752 	}
753 	if (mydomain) {
754 		mydomain[mydomainlen] = '\0';
755 		strncpy(domainname, mydomain, sizeof(domainname));
756 		domainnamelen = mydomainlen;
757 		printf("nfs_boot: my_domain=%s\n", domainname);
758 	}
759 	nd->nd_myip = bootp->bp_yiaddr;
760 	if (nd->nd_myip.s_addr)
761 		printf("nfs_boot: my_addr=0x%x\n", INTOHL(nd->nd_myip));
762 	nd->nd_mask = netmask;
763 	if (nd->nd_mask.s_addr)
764 		printf("nfs_boot: my_mask=0x%x\n", INTOHL(nd->nd_mask));
765 	nd->nd_gwip = gateway;
766 	if (nd->nd_gwip.s_addr)
767 		printf("nfs_boot: gateway=0x%x\n", INTOHL(nd->nd_gwip));
768 
769 	/*
770 	 * Store the information about our NFS root mount.
771 	 * The caller will print it, so be silent here.
772 	 */
773 	{
774 		struct nfs_dlmount *ndm = &nd->nd_root;
775 
776 		/* Server IP address. */
777 		sin = (struct sockaddr_in *) &ndm->ndm_saddr;
778 		memset((caddr_t)sin, 0, sizeof(*sin));
779 		sin->sin_len = sizeof(*sin);
780 		sin->sin_family = AF_INET;
781 		sin->sin_addr = rootserver;
782 		/* Server name. */
783 		if (!memcmp(&rootserver, &bootp->bp_siaddr,
784 			  sizeof(struct in_addr))) {
785 			/* standard root server, we have the name */
786 			strncpy(ndm->ndm_host, bootp->bp_sname, BP_SNAME_LEN-1);
787 		} else {
788 			/* Show the server IP address numerically. */
789 			sprintf(ndm->ndm_host, "0x%8x",
790 				INTOHL(rootserver));
791 		}
792 		len = strlen(ndm->ndm_host);
793 		if (rootpath &&
794 		    len + 1 + rootpathlen + 1 <= sizeof(ndm->ndm_host)) {
795 			ndm->ndm_host[len++] = ':';
796 			strncpy(ndm->ndm_host + len,
797 				rootpath, rootpathlen);
798 			ndm->ndm_host[len + rootpathlen] = '\0';
799 		} /* else: upper layer will handle error */
800 	}
801 }
802