xref: /netbsd-src/sys/nfs/nfs_boot.c (revision 6ea46cb5e46c49111a6ecf3bcbe3c7e2730fe9f6)
1 /*    $NetBSD: nfs_boot.c,v 1.10 1994/08/11 23:47:51 mycroft Exp $ */
2 
3 /*
4  * Copyright (c) 1994 Adam Glass, Gordon Ross
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the authors may not be used to endorse or promote products
16  *    derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/conf.h>
34 #include <sys/ioctl.h>
35 #include <sys/proc.h>
36 #include <sys/mount.h>
37 #include <sys/mbuf.h>
38 #include <sys/socket.h>
39 #include <sys/reboot.h>
40 
41 #include <net/if.h>
42 #include <net/route.h>
43 
44 #include <netinet/in.h>
45 #include <netinet/if_ether.h>
46 
47 #include <nfs/rpcv2.h>
48 #include <nfs/nfsv2.h>
49 #include <nfs/nfs.h>
50 #include <nfs/nfsdiskless.h>
51 
52 #include "ether.h"
53 #if NETHER > 0
54 
55 /*
56  * Support for NFS diskless booting, specifically getting information
57  * about where to boot from, what pathnames, etc.
58  *
59  * This implememtation uses RARP and the bootparam RPC.
60  * We are forced to implement RPC anyway (to get file handles)
61  * so we might as well take advantage of it for bootparam too.
62  *
63  * The diskless boot sequence goes as follows:
64  * (1) Get our interface address using RARP
65  *     (also save the address of the RARP server)
66  * (2) Get our hostname using RPC/bootparam/whoami
67  *     (all boopararms RPCs to the RARP server)
68  * (3) Get the root path using RPC/bootparam/getfile
69  * (4) Get the root file handle using RPC/mountd
70  * (5) Get the swap path using RPC/bootparam/getfile
71  * (6) Get the swap file handle using RPC/mountd
72  *
73  * (This happens to be the way Sun does it too.)
74  */
75 
76 /* bootparam RPC */
77 static int bp_whoami __P((struct sockaddr_in *bpsin,
78 	struct in_addr *my_ip, struct in_addr *gw_ip));
79 static int bp_getfile __P((struct sockaddr_in *bpsin, char *key,
80 	struct sockaddr_in *mdsin, char *servname, char *path));
81 
82 /* mountd RPC */
83 static int md_mount __P((struct sockaddr_in *mdsin, char *path,
84 	u_char *fh));
85 
86 /* other helpers */
87 static void get_path_and_handle __P((struct sockaddr_in *bpsin,
88 	char *key, struct nfs_dlmount *ndmntp));
89 
90 /*
91  * Called with an empty nfs_diskless struct to be filled in.
92  */
93 int
94 nfs_boot_init(nd, procp)
95 	struct nfs_diskless *nd;
96 	struct proc *procp;
97 {
98 	struct ifreq ireq;
99 	struct in_addr my_ip, srv_ip, gw_ip;
100 	struct sockaddr_in bp_sin;
101 	struct sockaddr_in *sin;
102 	struct ifnet *ifp;
103 	struct socket *so;
104 	int error, len;
105 	u_short port;
106 
107 #if 0
108 	/*
109 	 * XXX time must be non-zero when we init the interface or else
110 	 * the arp code will wedge... (Fixed in if_ether.c -gwr)
111 	 */
112 	if (time.tv_sec == 0)
113 		time.tv_sec = 1;
114 #endif
115 
116 	/*
117 	 * Find an interface, rarp for its ip address, stuff it, the
118 	 * implied broadcast addr, and netmask into a nfs_diskless struct.
119 	 *
120 	 * This was moved here from nfs_vfsops.c because this procedure
121 	 * would be quite different if someone decides to write (i.e.) a
122 	 * BOOTP version of this file (might not use RARP, etc.) -gwr
123 	 */
124 
125 	/*
126 	 * Find a network interface.
127 	 * XXX - This should use the specified boot device.
128 	 */
129 	for (ifp = ifnet; ifp; ifp = ifp->if_next)
130 		if ((ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
131 			break;
132 	if (ifp == NULL)
133 		panic("nfs_boot: no suitable interface");
134 	sprintf(ireq.ifr_name, "%s%d", ifp->if_name, ifp->if_unit);
135 	printf("nfs_boot: using network interface '%s'\n",
136 	    ireq.ifr_name);
137 
138 	/*
139 	 * Bring up the interface.
140 	 */
141 	if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0)
142 		panic("nfs_boot: socreate, error=%d", error);
143 	ireq.ifr_flags = IFF_UP;
144 	error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp);
145 	if (error)
146 		panic("nfs_boot: SIFFLAGS, error=%d", error);
147 
148 	/*
149 	 * Do RARP for the interface address.  Also
150 	 * save the server address for bootparam RPC.
151 	 */
152 	if ((error = revarpwhoarewe(ifp, &srv_ip, &my_ip)) != 0)
153 		panic("revarp failed, error=%d", error);
154 	printf("nfs_boot: client=0x%x, server=0x%x\n",
155 	    ntohl(my_ip.s_addr), ntohl(srv_ip.s_addr));
156 
157 	/*
158 	 * Do enough of ifconfig(8) so that the chosen interface can
159 	 * talk to the server(s).  (also get brcast addr and netmask)
160 	 */
161 	/* Set interface address. */
162 	sin = (struct sockaddr_in *)&ireq.ifr_addr;
163 	bzero((caddr_t)sin, sizeof(*sin));
164 	sin->sin_len = sizeof(*sin);
165 	sin->sin_family = AF_INET;
166 	sin->sin_addr.s_addr = my_ip.s_addr;
167 	error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp);
168 	if (error)
169 		panic("nfs_boot: set if addr, error=%d", error);
170 
171 	soclose(so);
172 
173 	/*
174 	 * Get client name and gateway address.
175 	 * RPC: bootparam/whoami
176 	 */
177 	bzero((caddr_t)&bp_sin, sizeof(bp_sin));
178 	bp_sin.sin_len = sizeof(bp_sin);
179 	bp_sin.sin_family = AF_INET;
180 	bp_sin.sin_addr.s_addr = srv_ip.s_addr;
181 	hostnamelen = MAXHOSTNAMELEN;
182 
183 	/* this returns gateway IP address */
184 	error = bp_whoami(&bp_sin, &my_ip, &gw_ip);
185 	if (error)
186 		panic("nfs_boot: bootparam whoami, error=%d", error);
187 	printf("nfs_boot: hostname=%s\n", hostname);
188 
189 #ifdef	NFS_BOOT_GATEWAY
190 	/*
191 	 * XXX - This code is conditionally compiled only because
192 	 * many bootparam servers (in particular, SunOS 4.1.3)
193 	 * always set the gateway address to their own address.
194 	 * The bootparam server is not necessarily the gateway.
195 	 * We could just believe the server, and at worst you would
196 	 * need to delete the incorrect default route before adding
197 	 * the correct one, but for simplicity, ignore the gateway.
198 	 * If your server is OK, you can turn on this option.
199 	 *
200 	 * If the gateway address is set, add a default route.
201 	 * (The mountd RPCs may go across a gateway.)
202 	 */
203 	if (gw_ip.s_addr) {
204 		struct sockaddr dst, gw, mask;
205 		/* Destination: (default) */
206 		bzero((caddr_t)&dst, sizeof(dst));
207 		dst.sa_len = sizeof(dst);
208 		dst.sa_family = AF_INET;
209 		/* Gateway: */
210 		bzero((caddr_t)&gw, sizeof(gw));
211 		sin = (struct sockaddr_in *)&gw;
212 		sin->sin_len = sizeof(gw);
213 		sin->sin_family = AF_INET;
214 		sin->sin_addr.s_addr = gw_ip.s_addr;
215 		/* Mask: (zero length) */
216 		bzero(&mask, sizeof(mask));
217 
218 		printf("nfs_boot: gateway=0x%x\n", ntohl(gw_ip.s_addr));
219 		/* add, dest, gw, mask, flags, 0 */
220 		error = rtrequest(RTM_ADD, &dst, (struct sockaddr *)&gw,
221 		    &mask, (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL);
222 		if (error)
223 			printf("nfs_boot: add route, error=%d\n", error);
224 	}
225 #endif
226 
227 	get_path_and_handle(&bp_sin, "root", &nd->nd_root);
228 	get_path_and_handle(&bp_sin, "swap", &nd->nd_swap);
229 
230 	return (0);
231 }
232 
233 static void
234 get_path_and_handle(bpsin, key, ndmntp)
235 	struct sockaddr_in *bpsin;	/* bootparam server */
236 	char *key;			/* root or swap */
237 	struct nfs_dlmount *ndmntp;	/* output */
238 {
239 	char pathname[MAXPATHLEN];
240 	char *sp, *dp, *endp;
241 	int error;
242 
243 	/*
244 	 * Get server:pathname for "key" (root or swap)
245 	 * using RPC to bootparam/getfile
246 	 */
247 	error = bp_getfile(bpsin, key, &ndmntp->ndm_saddr,
248 	    ndmntp->ndm_host, pathname);
249 	if (error)
250 		panic("nfs_boot: bootparam get %s: %d", key, error);
251 	printf("%s on %s:%s\n", key, ndmntp->ndm_host, pathname);
252 
253 	/*
254 	 * Get file handle for "key" (root or swap)
255 	 * using RPC to mountd/mount
256 	 */
257 	error = md_mount(&ndmntp->ndm_saddr, pathname, ndmntp->ndm_fh);
258 	if (error)
259 		panic("nfs_boot: mountd %s, error=%d", key, error);
260 
261 	/* Construct remote path (for getmntinfo(3)) */
262 	dp = ndmntp->ndm_host;
263 	endp = dp + MNAMELEN - 1;
264 	dp += strlen(dp);
265 	*dp++ = ':';
266 	for (sp = pathname; *sp && dp < endp;)
267 		*dp++ = *sp++;
268 	*dp = '\0';
269 
270 }
271 
272 
273 /*
274  * Get an mbuf with the given length, and
275  * initialize the pkthdr length field.
276  */
277 static struct mbuf *
278 m_get_len(int msg_len)
279 {
280 	struct mbuf *m;
281 	m = m_gethdr(M_WAIT, MT_DATA);
282 	if (m == NULL)
283 		return NULL;
284 	if (msg_len > MHLEN) {
285 		if (msg_len > MCLBYTES)
286 			panic("nfs_boot: msg_len > MCLBYTES");
287 		MCLGET(m, M_WAIT);
288 		if (m == NULL)
289 			return NULL;
290 	}
291 	m->m_len = msg_len;
292 	m->m_pkthdr.len = m->m_len;
293 	return (m);
294 }
295 
296 
297 /*
298  * String representation for RPC.
299  */
300 struct rpc_string {
301 	u_long len;		/* length without null or padding */
302 	u_char data[4];	/* data (longer, of course) */
303     /* data is padded to a long-word boundary */
304 };
305 /* Compute space used given string length. */
306 #define	RPC_STR_SIZE(slen) (4 + ((slen + 3) & ~3))
307 
308 /*
309  * Inet address in RPC messages
310  * (Note, really four longs, NOT chars.  Blech.)
311  */
312 struct bp_inaddr {
313 	u_long  atype;
314 	long	addr[4];
315 };
316 
317 
318 /*
319  * RPC definitions for bootparamd
320  * (XXX - move to a header file?)
321  */
322 #define	BOOTPARAM_PROG		100026
323 #define	BOOTPARAM_VERS		1
324 #define BOOTPARAM_WHOAMI	1
325 #define BOOTPARAM_GETFILE	2
326 
327 
328 /*
329  * RPC: bootparam/whoami
330  * Given client IP address, get:
331  *	client name	(hostname)
332  *	domain name (domainname)
333  *	gateway address
334  *
335  * Setting the hostname and domainname here may be somewhat
336  * controvercial, but it is so easy to do it here. -gwr
337  */
338 static int
339 bp_whoami(bpsin, my_ip, gw_ip)
340 	struct sockaddr_in *bpsin;
341 	struct in_addr *my_ip;
342 	struct in_addr *gw_ip;
343 {
344 	/* The RPC structures */
345 	struct bp_inaddr *bia;
346 	struct rpc_string *str;
347 	struct mbuf *m;
348 	struct sockaddr_in *sin;
349 	int error, msg_len;
350 	int cn_len, dn_len;
351 	u_char *p;
352 
353 	/*
354 	 * Get message buffer of sufficient size.
355 	 */
356 	msg_len = sizeof(*bia);
357 	m = m_get_len(msg_len);
358 	if (m == NULL)
359 		return ENOBUFS;
360 
361 	/*
362 	 * Build request message.
363 	 */
364 	/* client IP address */
365 	bia = mtod(m, struct bp_inaddr *);
366 	bia->atype = htonl(1);
367 	p = (u_char*)my_ip;	/* ugh! */
368 	bia->addr[0] = htonl(*p);
369 	p++;
370 	bia->addr[1] = htonl(*p);
371 	p++;
372 	bia->addr[2] = htonl(*p);
373 	p++;
374 	bia->addr[3] = htonl(*p);
375 	p++;
376 
377 	/* RPC: bootparam/whoami */
378 	error = krpc_call((struct sockaddr *)bpsin,
379 	    BOOTPARAM_PROG, BOOTPARAM_VERS, BOOTPARAM_WHOAMI, &m);
380 	if (error)
381 		return error;
382 
383 	/*
384 	 * Parse result message.
385 	 */
386 	msg_len = m->m_len;
387 	p = mtod(m, char *);
388 
389 	/* client name */
390 	if (msg_len < sizeof(*str))
391 		goto bad;
392 	str = (struct rpc_string *)p;
393 	cn_len = ntohl(str->len);
394 	if (msg_len < cn_len)
395 		goto bad;
396 	if (cn_len >= MAXHOSTNAMELEN)
397 		goto bad;
398 	bcopy(str->data, hostname, cn_len);
399 	hostname[cn_len] = '\0';
400 	hostnamelen = cn_len;
401 	p += RPC_STR_SIZE(cn_len);
402 	msg_len -= RPC_STR_SIZE(cn_len);
403 
404 	/* domain name */
405 	if (msg_len < sizeof(*str))
406 		goto bad;
407 	str = (struct rpc_string *)p;
408 	dn_len = ntohl(str->len);
409 	if (msg_len < dn_len)
410 		goto bad;
411 	if (dn_len >= MAXHOSTNAMELEN)
412 		goto bad;
413 	bcopy(str->data, domainname, dn_len);
414 	domainname[dn_len] = '\0';
415 	domainnamelen = dn_len;
416 	p += RPC_STR_SIZE(dn_len);
417 	msg_len -= RPC_STR_SIZE(dn_len);
418 
419 	/* gateway address */
420 	if (msg_len < sizeof(*bia))
421 		goto bad;
422 	bia = (struct bp_inaddr *)p;
423 	if (bia->atype != htonl(1))
424 		goto bad;
425 	p = (u_char*)gw_ip;
426 	*p++ = ntohl(bia->addr[0]);
427 	*p++ = ntohl(bia->addr[1]);
428 	*p++ = ntohl(bia->addr[2]);
429 	*p++ = ntohl(bia->addr[3]);
430 	goto out;
431 
432 bad:
433 	printf("nfs_boot: bootparam_whoami: bad reply\n");
434 	error = EBADRPC;
435 
436 out:
437 	m_freem(m);
438 	return(error);
439 }
440 
441 
442 /*
443  * RPC: bootparam/getfile
444  * Given client name and file "key", get:
445  *	server name
446  *	server IP address
447  *	server pathname
448  */
449 static int
450 bp_getfile(bpsin, key, md_sin, serv_name, pathname)
451 	struct sockaddr_in *bpsin;
452 	char *key;
453 	struct sockaddr_in *md_sin;
454 	char *serv_name;
455 	char *pathname;
456 {
457 	struct rpc_string *str;
458 	struct mbuf *m;
459 	struct bp_inaddr *bia;
460 	struct sockaddr_in *sin;
461 	u_char *p, *q;
462 	int error, msg_len;
463 	int cn_len, key_len, sn_len, path_len;
464 
465 	/*
466 	 * Get message buffer of sufficient size.
467 	 */
468 	cn_len = hostnamelen;
469 	key_len = strlen(key);
470 	msg_len = 0;
471 	msg_len += RPC_STR_SIZE(cn_len);
472 	msg_len += RPC_STR_SIZE(key_len);
473 	m = m_get_len(msg_len);
474 	if (m == NULL)
475 		return ENOBUFS;
476 
477 	/*
478 	 * Build request message.
479 	 */
480 	p = mtod(m, u_char *);
481 	bzero(p, msg_len);
482 	/* client name (hostname) */
483 	str = (struct rpc_string *)p;
484 	str->len = htonl(cn_len);
485 	bcopy(hostname, str->data, cn_len);
486 	p += RPC_STR_SIZE(cn_len);
487 	/* key name (root or swap) */
488 	str = (struct rpc_string *)p;
489 	str->len = htonl(key_len);
490 	bcopy(key, str->data, key_len);
491 
492 	/* RPC: bootparam/getfile */
493 	error = krpc_call((struct sockaddr *)bpsin,
494 	    BOOTPARAM_PROG, BOOTPARAM_VERS, BOOTPARAM_GETFILE, &m);
495 	if (error)
496 		return error;
497 
498 	/*
499 	 * Parse result message.
500 	 */
501 	p = mtod(m, u_char *);
502 	msg_len = m->m_len;
503 
504 	/* server name */
505 	if (msg_len < sizeof(*str))
506 		goto bad;
507 	str = (struct rpc_string *)p;
508 	sn_len = ntohl(str->len);
509 	if (msg_len < sn_len)
510 		goto bad;
511 	if (sn_len >= MNAMELEN)
512 		goto bad;
513 	bcopy(str->data, serv_name, sn_len);
514 	serv_name[sn_len] = '\0';
515 	p += RPC_STR_SIZE(sn_len);
516 	msg_len -= RPC_STR_SIZE(sn_len);
517 
518 	/* server IP address (mountd) */
519 	if (msg_len < sizeof(*bia))
520 		goto bad;
521 	bia = (struct bp_inaddr *)p;
522 	if (bia->atype != htonl(1))
523 		goto bad;
524 	sin = md_sin;
525 	bzero((caddr_t)sin, sizeof(*sin));
526 	sin->sin_len = sizeof(*sin);
527 	sin->sin_family = AF_INET;
528 	q = (u_char*) &sin->sin_addr;
529 	*q++ = ntohl(bia->addr[0]);
530 	*q++ = ntohl(bia->addr[1]);
531 	*q++ = ntohl(bia->addr[2]);
532 	*q++ = ntohl(bia->addr[3]);
533 	p += sizeof(*bia);
534 	msg_len -= sizeof(*bia);
535 
536 	/* server pathname */
537 	if (msg_len < sizeof(*str))
538 		goto bad;
539 	str = (struct rpc_string *)p;
540 	path_len = ntohl(str->len);
541 	if (msg_len < path_len)
542 		goto bad;
543 	if (path_len >= MAXPATHLEN)
544 		goto bad;
545 	bcopy(str->data, pathname, path_len);
546 	pathname[path_len] = '\0';
547 	goto out;
548 
549 bad:
550 	printf("nfs_boot: bootparam_getfile: bad reply\n");
551 	error = EBADRPC;
552 
553 out:
554 	m_freem(m);
555 	return(0);
556 }
557 
558 
559 /*
560  * RPC: mountd/mount
561  * Given a server pathname, get an NFS file handle.
562  * Also, sets sin->sin_port to the NFS service port.
563  */
564 static int
565 md_mount(mdsin, path, fhp)
566 	struct sockaddr_in *mdsin;		/* mountd server address */
567 	char *path;
568 	u_char *fhp;
569 {
570 	/* The RPC structures */
571 	struct rpc_string *str;
572 	struct rdata {
573 		u_long	errno;
574 		u_char	fh[NFS_FHSIZE];
575 	} *rdata;
576 	struct mbuf *m;
577 	int error, mlen, slen;
578 
579 	slen = strlen(path);
580 	mlen = RPC_STR_SIZE(slen);
581 
582 	m = m_get_len(mlen);
583 	if (m == NULL)
584 		return ENOBUFS;
585 	str = mtod(m, struct rpc_string *);
586 	str->len = htonl(slen);
587 	bcopy(path, str->data, slen);
588 
589 	/* Do RPC to mountd. */
590 	error = krpc_call((struct sockaddr *)mdsin,
591 	    RPCPROG_MNT, RPCMNT_VER1, RPCMNT_MOUNT, &m);
592 	if (error)
593 		return error;	/* message already freed */
594 
595 	mlen = m->m_len;
596 	if (mlen < sizeof(*rdata))
597 		goto bad;
598 	rdata = mtod(m, struct rdata *);
599 	error = ntohl(rdata->errno);
600 	if (error)
601 		goto bad;
602 	bcopy(rdata->fh, fhp, NFS_FHSIZE);
603 
604 	/* Set port number for NFS use. */
605 	error = krpc_portmap((struct sockaddr *)mdsin,
606 	    NFS_PROG, NFS_VER2, &mdsin->sin_port);
607 	goto out;
608 
609 bad:
610 	error = EBADRPC;
611 
612 out:
613 	m_freem(m);
614 	return error;
615 }
616 
617 #else /* NETHER */
618 
619 int nfs_boot_init(nd, procp)
620 	struct nfs_diskless *nd;
621 	struct proc *procp;
622 {
623 	panic("nfs_boot_init: no ether");
624 }
625 
626 #endif /* NETHER */
627