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