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