xref: /netbsd-src/sys/nfs/nfs_boot.c (revision 76dfffe33547c37f8bdd446e3e4ab0f3c16cea4b)
1 /*	$NetBSD: nfs_boot.c,v 1.29 1996/10/20 13:13:25 fvdl Exp $	*/
2 
3 /*
4  * Copyright (c) 1995 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/reboot.h>
39 #include <sys/socket.h>
40 #include <sys/socketvar.h>
41 
42 #include <net/if.h>
43 #include <net/route.h>
44 
45 #include <netinet/in.h>
46 #include <netinet/if_ether.h>
47 
48 #include <nfs/rpcv2.h>
49 #include <nfs/nfsproto.h>
50 #include <nfs/nfs.h>
51 #include <nfs/nfsdiskless.h>
52 #include <nfs/krpc.h>
53 #include <nfs/xdr_subs.h>
54 #include <nfs/nfs_var.h>
55 
56 #include "ether.h"
57 #if NETHER == 0
58 
59 int nfs_boot_init(nd, procp)
60 	struct nfs_diskless *nd;
61 	struct proc *procp;
62 {
63 	panic("nfs_boot_init: no ether");
64 }
65 
66 void
67 nfs_boot_getfh(bpsin, key, ndmntp)
68 	struct sockaddr_in *bpsin;
69 	char *key;
70 	struct nfs_dlmount *ndmntp;
71 {
72 	/* can not get here */
73 }
74 
75 #else /* NETHER */
76 
77 /*
78  * Support for NFS diskless booting, specifically getting information
79  * about where to boot from, what pathnames, etc.
80  *
81  * This implememtation uses RARP and the bootparam RPC.
82  * We are forced to implement RPC anyway (to get file handles)
83  * so we might as well take advantage of it for bootparam too.
84  *
85  * The diskless boot sequence goes as follows:
86  * (1) Use RARP to get our interface address
87  * (2) Use RPC/bootparam/whoami to get our hostname,
88  *     our IP address, and the server's IP address.
89  * (3) Use RPC/bootparam/getfile to get the root path
90  * (4) Use RPC/mountd to get the root file handle
91  * (5) Use RPC/bootparam/getfile to get the swap path
92  * (6) Use RPC/mountd to get the swap file handle
93  *
94  * (This happens to be the way Sun does it too.)
95  */
96 
97 /* bootparam RPC */
98 static int bp_whoami __P((struct sockaddr_in *bpsin,
99 	struct in_addr *my_ip, struct in_addr *gw_ip));
100 static int bp_getfile __P((struct sockaddr_in *bpsin, char *key,
101 	struct sockaddr_in *mdsin, char *servname, char *path));
102 
103 /* mountd RPC */
104 static int md_mount __P((struct sockaddr_in *mdsin, char *path,
105 	struct nfs_args *argp));
106 
107 char	*nfsbootdevname;
108 
109 /*
110  * Called with an empty nfs_diskless struct to be filled in.
111  */
112 int
113 nfs_boot_init(nd, procp)
114 	struct nfs_diskless *nd;
115 	struct proc *procp;
116 {
117 	struct ifreq ireq;
118 	struct in_addr my_ip, gw_ip;
119 	struct sockaddr_in bp_sin;
120 	struct sockaddr_in *sin;
121 	struct ifnet *ifp;
122 	struct socket *so;
123 	int error;
124 
125 	/*
126 	 * Find an interface, rarp for its ip address, stuff it, the
127 	 * implied broadcast addr, and netmask into a nfs_diskless struct.
128 	 *
129 	 * This was moved here from nfs_vfsops.c because this procedure
130 	 * would be quite different if someone decides to write (i.e.) a
131 	 * BOOTP version of this file (might not use RARP, etc.)
132 	 */
133 
134 	/*
135 	 * Find a network interface.
136 	 */
137 	if (nfsbootdevname)
138 		ifp = ifunit(nfsbootdevname);
139 	else
140 		for (ifp = ifnet.tqh_first; ifp != 0; ifp = ifp->if_list.tqe_next)
141 			if ((ifp->if_flags &
142 			     (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
143 				break;
144 	if (ifp == NULL)
145 		panic("nfs_boot: no suitable interface");
146 	bcopy(ifp->if_xname, ireq.ifr_name, IFNAMSIZ);
147 	printf("nfs_boot: using network interface '%s'\n",
148 	    ireq.ifr_name);
149 
150 	/*
151 	 * Bring up the interface.
152 	 *
153 	 * Get the old interface flags and or IFF_UP into them; if
154 	 * IFF_UP set blindly, interface selection can be clobbered.
155 	 */
156 	if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0)
157 		panic("nfs_boot: socreate, error=%d", error);
158 	error = ifioctl(so, SIOCGIFFLAGS, (caddr_t)&ireq, procp);
159 	if (error)
160 		panic("nfs_boot: GIFFLAGS, error=%d", error);
161 	ireq.ifr_flags |= IFF_UP;
162 	error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp);
163 	if (error)
164 		panic("nfs_boot: SIFFLAGS, error=%d", error);
165 
166 	/*
167 	 * Do RARP for the interface address.
168 	 */
169 	if ((error = revarpwhoami(&my_ip, ifp)) != 0)
170 		panic("revarp failed, error=%d", error);
171 	printf("nfs_boot: client_addr=0x%x\n", (u_int32_t)ntohl(my_ip.s_addr));
172 
173 	/*
174 	 * Do enough of ifconfig(8) so that the chosen interface
175 	 * can talk to the servers.  (just set the address)
176 	 */
177 	sin = (struct sockaddr_in *)&ireq.ifr_addr;
178 	bzero((caddr_t)sin, sizeof(*sin));
179 	sin->sin_len = sizeof(*sin);
180 	sin->sin_family = AF_INET;
181 	sin->sin_addr.s_addr = my_ip.s_addr;
182 	error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp);
183 	if (error)
184 		panic("nfs_boot: set if addr, error=%d", error);
185 
186 	soclose(so);
187 
188 	/*
189 	 * Get client name and gateway address.
190 	 * RPC: bootparam/whoami
191 	 * Use the old broadcast address for the WHOAMI
192 	 * call because we do not yet know our netmask.
193 	 * The server address returned by the WHOAMI call
194 	 * is used for all subsequent booptaram RPCs.
195 	 */
196 	bzero((caddr_t)&bp_sin, sizeof(bp_sin));
197 	bp_sin.sin_len = sizeof(bp_sin);
198 	bp_sin.sin_family = AF_INET;
199 	bp_sin.sin_addr.s_addr = INADDR_BROADCAST;
200 	hostnamelen = MAXHOSTNAMELEN;
201 
202 	/* this returns gateway IP address */
203 	error = bp_whoami(&bp_sin, &my_ip, &gw_ip);
204 	if (error)
205 		panic("nfs_boot: bootparam whoami, error=%d", error);
206 	printf("nfs_boot: server_addr=0x%x\n",
207 		   (u_int32_t)ntohl(bp_sin.sin_addr.s_addr));
208 	printf("nfs_boot: hostname=%s\n", hostname);
209 
210 #ifdef	NFS_BOOT_GATEWAY
211 	/*
212 	 * XXX - This code is conditionally compiled only because
213 	 * many bootparam servers (in particular, SunOS 4.1.3)
214 	 * always set the gateway address to their own address.
215 	 * The bootparam server is not necessarily the gateway.
216 	 * We could just believe the server, and at worst you would
217 	 * need to delete the incorrect default route before adding
218 	 * the correct one, but for simplicity, ignore the gateway.
219 	 * If your server is OK, you can turn on this option.
220 	 *
221 	 * If the gateway address is set, add a default route.
222 	 * (The mountd RPCs may go across a gateway.)
223 	 */
224 	if (gw_ip.s_addr) {
225 		struct sockaddr dst, gw, mask;
226 		/* Destination: (default) */
227 		bzero((caddr_t)&dst, sizeof(dst));
228 		dst.sa_len = sizeof(dst);
229 		dst.sa_family = AF_INET;
230 		/* Gateway: */
231 		bzero((caddr_t)&gw, sizeof(gw));
232 		sin = (struct sockaddr_in *)&gw;
233 		sin->sin_len = sizeof(gw);
234 		sin->sin_family = AF_INET;
235 		sin->sin_addr.s_addr = gw_ip.s_addr;
236 		/* Mask: (zero length) */
237 		bzero(&mask, sizeof(mask));
238 
239 		printf("nfs_boot: gateway=0x%x\n", ntohl(gw_ip.s_addr));
240 		/* add, dest, gw, mask, flags, 0 */
241 		error = rtrequest(RTM_ADD, &dst, (struct sockaddr *)&gw,
242 		    &mask, (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL);
243 		if (error)
244 			printf("nfs_boot: add route, error=%d\n", error);
245 	}
246 #endif
247 
248 	bcopy(&bp_sin, &nd->nd_boot, sizeof(bp_sin));
249 
250 	return (0);
251 }
252 
253 void
254 nfs_boot_getfh(bpsin, key, ndmntp)
255 	struct sockaddr_in *bpsin;	/* bootparam server */
256 	char *key;			/* root or swap */
257 	struct nfs_dlmount *ndmntp;	/* output */
258 {
259 	struct nfs_args *args;
260 	char pathname[MAXPATHLEN];
261 	char *sp, *dp, *endp;
262 	struct sockaddr_in *sin;
263 	int error;
264 
265 	args = &ndmntp->ndm_args;
266 
267 	/* Initialize mount args. */
268 	bzero((caddr_t) args, sizeof(*args));
269 	args->addr     = &ndmntp->ndm_saddr;
270 	args->addrlen  = args->addr->sa_len;
271 #ifdef NFS_BOOT_TCP
272 	args->sotype   = SOCK_STREAM;
273 #else
274 	args->sotype   = SOCK_DGRAM;
275 #endif
276 	args->fh       = ndmntp->ndm_fh;
277 	args->hostname = ndmntp->ndm_host;
278 	args->flags    = NFSMNT_RESVPORT | NFSMNT_NFSV3;
279 
280 #ifdef	NFS_BOOT_OPTIONS
281 	args->flags    |= NFS_BOOT_OPTIONS;
282 #endif
283 #ifdef	NFS_BOOT_RWSIZE
284 	/*
285 	 * Reduce rsize,wsize for interfaces that consistently
286 	 * drop fragments of long UDP messages.  (i.e. wd8003).
287 	 * You can always change these later via remount.
288 	 */
289 	args->flags   |= NFSMNT_WSIZE | NFSMNT_RSIZE;
290 	args->wsize    = NFS_BOOT_RWSIZE;
291 	args->rsize    = NFS_BOOT_RWSIZE;
292 #endif
293 
294 	sin = (void*)&ndmntp->ndm_saddr;
295 
296 	/*
297 	 * Get server:pathname for "key" (root or swap)
298 	 * using RPC to bootparam/getfile
299 	 */
300 	error = bp_getfile(bpsin, key, sin, ndmntp->ndm_host, pathname);
301 	if (error)
302 		panic("nfs_boot: bootparam get %s: %d", key, error);
303 
304 	/*
305 	 * Get file handle for "key" (root or swap)
306 	 * using RPC to mountd/mount
307 	 */
308 	error = md_mount(sin, pathname, args);
309 	if (error)
310 		panic("nfs_boot: mountd %s, error=%d", key, error);
311 
312 	/* Set port number for NFS use. */
313 	/* XXX: NFS port is always 2049, right? */
314 #ifdef NFS_BOOT_TCP
315 retry:
316 #endif
317 	error = krpc_portmap(sin, NFS_PROG,
318 		    (args->flags & NFSMNT_NFSV3) ? NFS_VER3 : NFS_VER2,
319 		    (args->sotype == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP,
320 		    &sin->sin_port);
321 
322 	if (error || (sin->sin_port == htons(0))) {
323 #ifdef NFS_BOOT_TCP
324 		if (args->sotype == SOCK_STREAM) {
325 			args->sotype = SOCK_DGRAM;
326 			goto retry;
327 		}
328 #endif
329 		panic("nfs_boot: portmap NFS, error=%d", error);
330 	}
331 
332 	/* Construct remote path (for getmntinfo(3)) */
333 	dp = ndmntp->ndm_host;
334 	endp = dp + MNAMELEN - 1;
335 	dp += strlen(dp);
336 	*dp++ = ':';
337 	for (sp = pathname; *sp && dp < endp;)
338 		*dp++ = *sp++;
339 	*dp = '\0';
340 
341 }
342 
343 
344 /*
345  * RPC: bootparam/whoami
346  * Given client IP address, get:
347  *	client name	(hostname)
348  *	domain name (domainname)
349  *	gateway address
350  *
351  * The hostname and domainname are set here for convenience.
352  *
353  * Note - bpsin is initialized to the broadcast address,
354  * and will be replaced with the bootparam server address
355  * after this call is complete.  Have to use PMAP_PROC_CALL
356  * to make sure we get responses only from a servers that
357  * know about us (don't want to broadcast a getport call).
358  */
359 static int
360 bp_whoami(bpsin, my_ip, gw_ip)
361 	struct sockaddr_in *bpsin;
362 	struct in_addr *my_ip;
363 	struct in_addr *gw_ip;
364 {
365 	/* RPC structures for PMAPPROC_CALLIT */
366 	struct whoami_call {
367 		u_int32_t call_prog;
368 		u_int32_t call_vers;
369 		u_int32_t call_proc;
370 		u_int32_t call_arglen;
371 	} *call;
372 	struct callit_reply {
373 		u_int32_t port;
374 		u_int32_t encap_len;
375 		/* encapsulated data here */
376 	} *reply;
377 
378 	struct mbuf *m, *from;
379 	struct sockaddr_in *sin;
380 	int error, msg_len;
381 	int16_t port;
382 
383 	/*
384 	 * Build request message for PMAPPROC_CALLIT.
385 	 */
386 	m = m_get(M_WAIT, MT_DATA);
387 	call = mtod(m, struct whoami_call *);
388 	m->m_len = sizeof(*call);
389 	call->call_prog = txdr_unsigned(BOOTPARAM_PROG);
390 	call->call_vers = txdr_unsigned(BOOTPARAM_VERS);
391 	call->call_proc = txdr_unsigned(BOOTPARAM_WHOAMI);
392 
393 	/*
394 	 * append encapsulated data (client IP address)
395 	 */
396 	m->m_next = xdr_inaddr_encode(my_ip);
397 	call->call_arglen = txdr_unsigned(m->m_next->m_len);
398 
399 	/* RPC: portmap/callit */
400 	bpsin->sin_port = htons(PMAPPORT);
401 	from = NULL;
402 	error = krpc_call(bpsin, PMAPPROG, PMAPVERS,
403 			PMAPPROC_CALLIT, &m, &from);
404 	if (error)
405 		return error;
406 
407 	/*
408 	 * Parse result message.
409 	 */
410 	if (m->m_len < sizeof(*reply)) {
411 		m = m_pullup(m, sizeof(*reply));
412 		if (m == NULL)
413 			goto bad;
414 	}
415 	reply = mtod(m, struct callit_reply *);
416 	port = fxdr_unsigned(u_int32_t, reply->port);
417 	msg_len = fxdr_unsigned(u_int32_t, reply->encap_len);
418 	m_adj(m, sizeof(*reply));
419 
420 	/*
421 	 * Save bootparam server address
422 	 */
423 	sin = mtod(from, struct sockaddr_in *);
424 	bpsin->sin_port = htons(port);
425 	bpsin->sin_addr.s_addr = sin->sin_addr.s_addr;
426 
427 	/* client name */
428 	hostnamelen = MAXHOSTNAMELEN-1;
429 	m = xdr_string_decode(m, hostname, &hostnamelen);
430 	if (m == NULL)
431 		goto bad;
432 
433 	/* domain name */
434 	domainnamelen = MAXHOSTNAMELEN-1;
435 	m = xdr_string_decode(m, domainname, &domainnamelen);
436 	if (m == NULL)
437 		goto bad;
438 
439 	/* gateway address */
440 	m = xdr_inaddr_decode(m, gw_ip);
441 	if (m == NULL)
442 		goto bad;
443 
444 	/* success */
445 	goto out;
446 
447 bad:
448 	printf("nfs_boot: bootparam_whoami: bad reply\n");
449 	error = EBADRPC;
450 
451 out:
452 	if (from)
453 		m_freem(from);
454 	if (m)
455 		m_freem(m);
456 	return(error);
457 }
458 
459 
460 /*
461  * RPC: bootparam/getfile
462  * Given client name and file "key", get:
463  *	server name
464  *	server IP address
465  *	server pathname
466  */
467 static int
468 bp_getfile(bpsin, key, md_sin, serv_name, pathname)
469 	struct sockaddr_in *bpsin;
470 	char *key;
471 	struct sockaddr_in *md_sin;
472 	char *serv_name;
473 	char *pathname;
474 {
475 	struct mbuf *m;
476 	struct sockaddr_in *sin;
477 	struct in_addr inaddr;
478 	int error, sn_len, path_len;
479 
480 	/*
481 	 * Build request message.
482 	 */
483 
484 	/* client name (hostname) */
485 	m  = xdr_string_encode(hostname, hostnamelen);
486 	if (m == NULL)
487 		return (ENOMEM);
488 
489 	/* key name (root or swap) */
490 	m->m_next = xdr_string_encode(key, strlen(key));
491 	if (m->m_next == NULL)
492 		return (ENOMEM);
493 
494 	/* RPC: bootparam/getfile */
495 	error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS,
496 			BOOTPARAM_GETFILE, &m, NULL);
497 	if (error)
498 		return error;
499 
500 	/*
501 	 * Parse result message.
502 	 */
503 
504 	/* server name */
505 	sn_len = MNAMELEN-1;
506 	m = xdr_string_decode(m, serv_name, &sn_len);
507 	if (m == NULL)
508 		goto bad;
509 
510 	/* server IP address (mountd/NFS) */
511 	m = xdr_inaddr_decode(m, &inaddr);
512 	if (m == NULL)
513 		goto bad;
514 
515 	/* server pathname */
516 	path_len = MAXPATHLEN-1;
517 	m = xdr_string_decode(m, pathname, &path_len);
518 	if (m == NULL)
519 		goto bad;
520 
521 	/* setup server socket address */
522 	sin = md_sin;
523 	bzero((caddr_t)sin, sizeof(*sin));
524 	sin->sin_len = sizeof(*sin);
525 	sin->sin_family = AF_INET;
526 	sin->sin_addr = inaddr;
527 
528 	/* success */
529 	goto out;
530 
531 bad:
532 	printf("nfs_boot: bootparam_getfile: bad reply\n");
533 	error = EBADRPC;
534 
535 out:
536 	m_freem(m);
537 	return(0);
538 }
539 
540 
541 /*
542  * RPC: mountd/mount
543  * Given a server pathname, get an NFS file handle.
544  * Also, sets sin->sin_port to the NFS service port.
545  */
546 static int
547 md_mount(mdsin, path, argp)
548 	struct sockaddr_in *mdsin;		/* mountd server address */
549 	char *path;
550 	struct nfs_args *argp;
551 {
552 	/* The RPC structures */
553 	struct rdata {
554 		u_int32_t errno;
555 		union {
556 			u_int8_t  v2fh[NFSX_V2FH];
557 			struct {
558 				u_int32_t fhlen;
559 				u_int8_t  fh[1];
560 			} v3fh;
561 		} fh;
562 	} *rdata;
563 	struct mbuf *m;
564 	u_int8_t *fh;
565 	int minlen, error;
566 
567 retry:
568 	/* Get port number for MOUNTD. */
569 	error = krpc_portmap(mdsin, RPCPROG_MNT,
570 		     (argp->flags & NFSMNT_NFSV3) ? RPCMNT_VER3 : RPCMNT_VER1,
571 		     IPPROTO_UDP, &mdsin->sin_port);
572 	if (error)
573 		return error;
574 
575 	m = xdr_string_encode(path, strlen(path));
576 	if (m == NULL)
577 		return ENOMEM;
578 
579 	/* Do RPC to mountd. */
580 	error = krpc_call(mdsin, RPCPROG_MNT, (argp->flags & NFSMNT_NFSV3) ?
581 			  RPCMNT_VER3 : RPCMNT_VER1, RPCMNT_MOUNT, &m, NULL);
582 	if (error) {
583 		if ((error==RPC_PROGMISMATCH) && (argp->flags & NFSMNT_NFSV3)) {
584 			argp->flags &= ~NFSMNT_NFSV3;
585 			goto retry;
586 		}
587 		return error;	/* message already freed */
588 	}
589 
590 	/* The reply might have only the errno. */
591 	if (m->m_len < 4)
592 		goto bad;
593 	/* Have at least errno, so check that. */
594 	rdata = mtod(m, struct rdata *);
595 	error = fxdr_unsigned(u_int32_t, rdata->errno);
596 	if (error)
597 		goto out;
598 
599 	 /* Have errno==0, so the fh must be there. */
600 	if (argp->flags & NFSMNT_NFSV3){
601 		argp->fhsize   = fxdr_unsigned(u_int32_t, rdata->fh.v3fh.fhlen);
602 		if (argp->fhsize > NFSX_V3FHMAX)
603 			goto bad;
604 		minlen = 2 * sizeof(u_int32_t) + argp->fhsize;
605 	} else {
606 		argp->fhsize   = NFSX_V2FH;
607 		minlen = sizeof(u_int32_t) + argp->fhsize;
608 	}
609 
610 	if (m->m_len < minlen) {
611 		m = m_pullup(m, minlen);
612 		if (m == NULL)
613 			return(EBADRPC);
614 		rdata = mtod(m, struct rdata *);
615 	}
616 
617 	fh = (argp->flags & NFSMNT_NFSV3) ? rdata->fh.v3fh.fh : rdata->fh.v2fh;
618 	bcopy(fh, argp->fh, argp->fhsize);
619 
620 	goto out;
621 
622 bad:
623 	error = EBADRPC;
624 
625 out:
626 	m_freem(m);
627 	return error;
628 }
629 
630 #endif /* NETHER */
631