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