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