xref: /netbsd-src/sys/nfs/nfs_boot.c (revision d9158b13b5dfe46201430699a3f7a235ecf28df3)
1 /*
2  * Copyright (c) 1994 Adam Glass, Gordon Ross
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the authors may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  *
27  * $Id: nfs_boot.c,v 1.3 1994/06/13 15:28:59 gwr Exp $
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/socket.h>
39 #include <sys/reboot.h>
40 
41 #include <net/if.h>
42 #include <net/route.h>
43 
44 #include <netinet/in.h>
45 #include <netinet/if_ether.h>
46 
47 #include <nfs/rpcv2.h>
48 #include <nfs/nfsv2.h>
49 #include <nfs/nfs.h>
50 #include <nfs/nfsdiskless.h>
51 
52 /*
53  * Support for NFS diskless booting, specifically getting information
54  * about where to boot from, what pathnames, etc.
55  *
56  * This implememtation uses RARP and the bootparam RPC.
57  * We are forced to implement RPC anyway (to get file handles)
58  * so we might as well take advantage of it for bootparam too.
59  *
60  * The diskless boot sequence goes as follows:
61  * (1) Get our interface address using RARP
62  *     (also save the address of the RARP server)
63  * (2) Get our hostname using RPC/bootparam/whoami
64  *     (all boopararms RPCs to the RARP server)
65  * (3) Get the root path using RPC/bootparam/getfile
66  * (4) Get the root file handle using RPC/mountd
67  * (5) Get the swap path using RPC/bootparam/getfile
68  * (6) Get the swap file handle using RPC/mountd
69  *
70  * (This happens to be the way Sun does it too.)
71  */
72 
73 /* bootparam RPC */
74 static int bp_whoami(struct sockaddr_in *bpsin,
75 					 struct in_addr *my_ip,
76 					 struct in_addr *gw_ip);
77 static int bp_getfile(struct sockaddr_in *bpsin, char *key,
78 					  struct sockaddr_in *mdsin,
79 					  char *servname, char *path);
80 
81 /* mountd RPC */
82 static int md_mount(struct sockaddr_in *mdsin, char *path, u_char *fh);
83 
84 /* other helpers */
85 static void get_path_and_handle(struct sockaddr_in *bpsin, char *key,
86 								struct nfs_dlmount *ndmntp);
87 
88 
89 /*
90  * Called with an empty nfs_diskless struct to be filled in.
91  */
92 int nfs_boot_init(nd, procp)
93 	struct nfs_diskless *nd;
94 	struct proc *procp;
95 {
96 	struct ifreq ireq;
97 	struct in_addr my_ip, srv_ip, gw_ip;
98 	struct sockaddr_in bp_sin;
99 	struct sockaddr_in *sin;
100 	struct ifnet *ifp;
101 	struct socket *so;
102 	int error, len;
103 	u_short port;
104 
105 #if 0
106 	/*
107 	 * XXX time must be non-zero when we init the interface or else
108 	 * the arp code will wedge... (Fixed in if_ether.c -gwr)
109 	 */
110 	if (time.tv_sec == 0)
111 		time.tv_sec = 1;
112 #endif
113 
114 	/*
115 	 * Find an interface, rarp for its ip address, stuff it, the
116 	 * implied broadcast addr, and netmask into a nfs_diskless struct.
117 	 *
118 	 * This was moved here from nfs_vfsops.c because this procedure
119 	 * would be quite different if someone decides to write (i.e.) a
120 	 * BOOTP version of this file (might not use RARP, etc.) -gwr
121 	 */
122 
123 	/*
124 	 * Find a network interface.
125 	 * XXX - This should use the specified boot device.
126 	 */
127 	for (ifp = ifnet; ifp; ifp = ifp->if_next)
128 		if ((ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
129 			break;
130 	if (ifp == NULL)
131 		panic("nfs_boot: no suitable interface");
132 	sprintf(ireq.ifr_name, "%s%d", ifp->if_name, ifp->if_unit);
133 	printf("nfs_boot: using network interface '%s'\n",
134 		   ireq.ifr_name);
135 
136 	/*
137 	 * Bring up the interface.
138 	 */
139 	if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0)
140 		panic("nfs_boot: socreate, error=%d", error);
141 	ireq.ifr_flags = IFF_UP;
142 	error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp);
143 	if (error) panic("nfs_boot: SIFFLAGS, error=%d", error);
144 
145 	/*
146 	 * Do RARP for the interface address.  Also
147 	 * save the server address for bootparam RPC.
148 	 */
149 	if ((error = revarpwhoarewe(ifp, &srv_ip, &my_ip)) != 0)
150 		panic("revarp failed, error=%d", error);
151 	printf("nfs_boot: client=0x%x, server=0x%x\n",
152 		   my_ip.s_addr, srv_ip.s_addr);
153 
154 	/*
155 	 * Do enough of ifconfig(8) so that the chosen interface can
156 	 * talk to the server(s).  (also get brcast addr and netmask)
157 	 */
158 	/* Set interface address. */
159 	sin = (struct sockaddr_in *)&ireq.ifr_addr;
160 	sin->sin_len = sizeof(*sin);
161 	sin->sin_family = AF_INET;
162 	sin->sin_addr.s_addr = my_ip.s_addr;
163 	error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp);
164 	if (error) panic("nfs_boot: set if addr, error=%d", error);
165 
166 	soclose(so);
167 
168 	/*
169 	 * Get client name and gateway address.
170 	 * RPC: bootparam/whoami
171 	 */
172 	bp_sin.sin_len = sizeof(bp_sin);
173 	bp_sin.sin_family = AF_INET;
174 	bp_sin.sin_addr.s_addr = srv_ip.s_addr;
175 	hostnamelen = MAXHOSTNAMELEN;
176 	error = bp_whoami(&bp_sin,	/*  input: where to send RPC */
177 					  &my_ip, 	/*  input: client IP */
178 					  &gw_ip);	/* ouptut: gateway IP */
179 	if (error)
180 		panic("nfs_boot: bootparam whoami, error=%d", error);
181 	printf("nfs_boot: hostname=%s\n", hostname);
182 
183 #ifdef	NFS_BOOT_GATEWAY
184 	/*
185 	 * XXX - Server supplied gateway is usually bogus...
186 	 * (At least for SunOS 4.1.3 servers it is.)
187 	 * If your server is OK, you can turn on this option.
188 	 *
189 	 * If the gateway address is set, add a default route.
190 	 * (The mountd RPCs may go across a gateway.)
191 	 */
192 	if (gw_ip.s_addr) {
193 		/* Destination: (default) */
194 		struct sockaddr_in dst, gw;
195 		bzero(&dst, sizeof(dst));
196 		dst.sin_len = sizeof(dst);
197 		dst.sin_family = AF_INET;
198 		/* Gateway: */
199 		bzero(&gw, sizeof(gw));
200 		gw.sin_len = sizeof(gw);
201 		gw.sin_family = AF_INET;
202 		gw.sin_addr.s_addr = gw_ip.s_addr;
203 		/* Netmask: */
204 		error = ifioctl(so, SIOCGIFNETMASK, (caddr_t)&ireq, procp);
205 		if (error) panic("nfs_boot: get netmask, error=%d", error);
206 
207 		/* add, dest, gw, mask, flags, 0 */
208 		error = rtrequest(RTM_ADD, &dst, &gw, &ifr.ifr_addr,
209 						  (RTF_UP | RTF_GATEWAY), NULL);
210 		if (error)
211 			printf("nfs_boot: add route, error=%d\n", error);
212 	}
213 #endif
214 
215 	get_path_and_handle(&bp_sin, "root", &nd->nd_root);
216 	get_path_and_handle(&bp_sin, "swap", &nd->nd_swap);
217 
218 	return (0);
219 }
220 
221 static void
222 get_path_and_handle(bpsin, key, ndmntp)
223 	struct sockaddr_in *bpsin;	/* bootparam server */
224 	char *key;					/* root or swap */
225 	struct nfs_dlmount *ndmntp; /* output */
226 {
227 	char pathname[MAXPATHLEN];
228 	int error;
229 
230 	/*
231 	 * Get server:pathname for "key" (root or swap)
232 	 * using RPC to bootparam/getfile
233 	 */
234 	error = bp_getfile(bpsin, key,
235 					   &ndmntp->ndm_saddr,
236 					   ndmntp->ndm_host,
237 					   pathname);
238 	if (error)
239 		panic("nfs_boot: bootparam get %s: %d", key, error);
240 	printf("%s on %s:%s\n", key, ndmntp->ndm_host, pathname);
241 
242 	/*
243 	 * Get file handle for "key" (root or swap)
244 	 * using RPC to mountd/mount
245 	 */
246 	error = md_mount(&ndmntp->ndm_saddr,
247 					 pathname,
248 					 ndmntp->ndm_fh);
249 	if (error)
250 		panic("nfs_boot: mountd %s, error=%d", key, error);
251 }
252 
253 
254 /*
255  * Get an mbuf with the given length, and
256  * initialize the pkthdr length field.
257  */
258 static struct mbuf *
259 m_get_len(int msg_len)
260 {
261 	struct mbuf *m;
262 	m = m_gethdr(M_WAIT, MT_DATA);
263 	if (m == NULL)
264 		return NULL;
265 	if (msg_len > MHLEN) {
266 		if (msg_len > MCLBYTES)
267 			panic("nfs_boot: msg_len > MCLBYTES");
268 		MCLGET(m, M_WAIT);
269 		if (m == NULL)
270 			return NULL;
271 	}
272 	m->m_len = msg_len;
273 	m->m_pkthdr.len = m->m_len;
274 	return (m);
275 }
276 
277 
278 /*
279  * String representation for RPC.
280  */
281 struct rpc_string {
282 	u_long len;		/* length without null or padding */
283 	u_char data[4];	/* data (longer, of course) */
284     /* data is padded to a long-word boundary */
285 };
286 /* Compute space used given string length. */
287 #define	RPC_STR_SIZE(slen) (4 + ((slen + 3) & ~3))
288 
289 /*
290  * Inet address in RPC messages
291  * (Note, really four longs, NOT chars.  Blech.)
292  */
293 struct bp_inaddr {
294 	u_long  atype;
295 	long	addr[4];
296 };
297 
298 
299 /*
300  * RPC definitions for bootparamd
301  * (XXX - move to a header file?)
302  */
303 #define	BOOTPARAM_PROG		100026
304 #define	BOOTPARAM_VERS		1
305 #define BOOTPARAM_WHOAMI	1
306 #define BOOTPARAM_GETFILE	2
307 
308 
309 /*
310  * RPC: bootparam/whoami
311  * Given client IP address, get:
312  *	client name	(hostname)
313  *	domain name (domainname)
314  *	gateway address
315  *
316  * Setting the hostname and domainname here may be somewhat
317  * controvercial, but it is so easy to do it here. -gwr
318  */
319 static int
320 bp_whoami(struct sockaddr_in *bpsin,
321 		  struct in_addr *my_ip,
322 		  struct in_addr *gw_ip)
323 {
324 	/* The RPC structures */
325 	struct bp_inaddr *bia;
326 	struct rpc_string *str;
327 	struct mbuf *m;
328 	struct sockaddr_in *sin;
329 	int error, msg_len;
330 	int cn_len, dn_len;
331 	u_char *p;
332 
333 	/*
334 	 * Get message buffer of sufficient size.
335 	 */
336 	msg_len = sizeof(*bia);
337 	m = m_get_len(msg_len);
338 	if (m == NULL)
339 		return ENOBUFS;
340 
341 	/*
342 	 * Build request message.
343 	 */
344 	/* client IP address */
345 	bia = mtod(m, struct bp_inaddr *);
346 	bia->atype = htonl(1);
347 	p = (u_char*)my_ip;	/* ugh! */
348 	bia->addr[0] = htonl(*p); p++;
349 	bia->addr[1] = htonl(*p); p++;
350 	bia->addr[2] = htonl(*p); p++;
351 	bia->addr[3] = htonl(*p); p++;
352 
353 	/* RPC: bootparam/whoami */
354 	error = krpc_call((struct sockaddr *)bpsin,
355 					  BOOTPARAM_PROG, BOOTPARAM_VERS,
356 					  BOOTPARAM_WHOAMI, &m);
357 	if (error)
358 		return error;
359 
360 	/*
361 	 * Parse result message.
362 	 */
363 	msg_len = m->m_len;
364 	p = mtod(m, char *);
365 
366 	/* client name */
367 	if (msg_len < sizeof(*str))
368 		goto bad;
369 	str = (struct rpc_string *)p;
370 	cn_len = ntohl(str->len);
371 	if (msg_len < cn_len)
372 		goto bad;
373 	if (cn_len >= MAXHOSTNAMELEN)
374 		goto bad;
375 	bcopy(str->data, hostname, cn_len);
376 	hostname[cn_len] = '\0';
377 	hostnamelen = cn_len;
378 	p += RPC_STR_SIZE(cn_len);
379 	msg_len -= RPC_STR_SIZE(cn_len);
380 
381 	/* domain name */
382 	if (msg_len < sizeof(*str))
383 		goto bad;
384 	str = (struct rpc_string *)p;
385 	dn_len = ntohl(str->len);
386 	if (msg_len < dn_len)
387 		goto bad;
388 	if (dn_len >= MAXHOSTNAMELEN)
389 		goto bad;
390 	bcopy(str->data, domainname, dn_len);
391 	domainname[dn_len] = '\0';
392 	domainnamelen = dn_len;
393 	p += RPC_STR_SIZE(dn_len);
394 	msg_len -= RPC_STR_SIZE(dn_len);
395 
396 	/* gateway address */
397 	if (msg_len < sizeof(*bia))
398 		goto bad;
399 	bia = (struct bp_inaddr *)p;
400 	if (bia->atype != htonl(1))
401 		goto bad;
402 	p = (u_char*)gw_ip;
403 	*p++ = ntohl(bia->addr[0]);
404 	*p++ = ntohl(bia->addr[1]);
405 	*p++ = ntohl(bia->addr[2]);
406 	*p++ = ntohl(bia->addr[3]);
407 	goto out;
408 
409  bad:
410 	printf("nfs_boot: bootparam_whoami: bad reply\n");
411 	error = EBADRPC;
412 
413  out:
414 	m_freem(m);
415 	return(error);
416 }
417 
418 
419 /*
420  * RPC: bootparam/getfile
421  * Given client name and file "key", get:
422  *	server name
423  *	server IP address
424  *	server pathname
425  */
426 static int
427 bp_getfile(struct sockaddr_in *bpsin, char *key,
428 	struct sockaddr_in *md_sin, char *serv_name, char *pathname)
429 {
430 	struct rpc_string *str;
431 	struct mbuf *m;
432 	struct bp_inaddr *bia;
433 	struct sockaddr_in *sin;
434 	u_char *p, *q;
435 	int error, msg_len;
436 	int cn_len, key_len, sn_len, path_len;
437 
438 	/*
439 	 * Get message buffer of sufficient size.
440 	 */
441 	cn_len = hostnamelen;
442 	key_len = strlen(key);
443 	msg_len = 0;
444 	msg_len += RPC_STR_SIZE(cn_len);
445 	msg_len += RPC_STR_SIZE(key_len);
446 	m = m_get_len(msg_len);
447 	if (m == NULL)
448 		return ENOBUFS;
449 
450 	/*
451 	 * Build request message.
452 	 */
453 	p = mtod(m, u_char *);
454 	bzero(p, msg_len);
455 	/* client name (hostname) */
456 	str = (struct rpc_string *)p;
457 	str->len = htonl(cn_len);
458 	bcopy(hostname, str->data, cn_len);
459 	p += RPC_STR_SIZE(cn_len);
460 	/* key name (root or swap) */
461 	str = (struct rpc_string *)p;
462 	str->len = htonl(key_len);
463 	bcopy(key, str->data, key_len);
464 
465 	/* RPC: bootparam/getfile */
466 	error = krpc_call((struct sockaddr *)bpsin,
467 					  BOOTPARAM_PROG, BOOTPARAM_VERS,
468 					  BOOTPARAM_GETFILE, &m);
469 	if (error)
470 		return error;
471 
472 	/*
473 	 * Parse result message.
474 	 */
475 	p = mtod(m, u_char *);
476 	msg_len = m->m_len;
477 
478 	/* server name */
479 	if (msg_len < sizeof(*str))
480 		goto bad;
481 	str = (struct rpc_string *)p;
482 	sn_len = ntohl(str->len);
483 	if (msg_len < sn_len)
484 		goto bad;
485 	if (sn_len >= MNAMELEN)
486 		goto bad;
487 	bcopy(str->data, serv_name, sn_len);
488 	serv_name[sn_len] = '\0';
489 	p += RPC_STR_SIZE(sn_len);
490 	msg_len -= RPC_STR_SIZE(sn_len);
491 
492 	/* server IP address (mountd) */
493 	if (msg_len < sizeof(*bia))
494 		goto bad;
495 	bia = (struct bp_inaddr *)p;
496 	if (bia->atype != htonl(1))
497 		goto bad;
498 	sin = md_sin;
499 	sin->sin_len = sizeof(*sin);
500 	sin->sin_family = AF_INET;
501 	q = (u_char*) &sin->sin_addr;
502 	*q++ = ntohl(bia->addr[0]);
503 	*q++ = ntohl(bia->addr[1]);
504 	*q++ = ntohl(bia->addr[2]);
505 	*q++ = ntohl(bia->addr[3]);
506 	p += sizeof(*bia);
507 	msg_len -= sizeof(*bia);
508 
509 	/* server pathname */
510 	if (msg_len < sizeof(*str))
511 		goto bad;
512 	str = (struct rpc_string *)p;
513 	path_len = ntohl(str->len);
514 	if (msg_len < path_len)
515 		goto bad;
516 	if (path_len >= MAXPATHLEN)
517 		goto bad;
518 	bcopy(str->data, pathname, path_len);
519 	pathname[path_len] = '\0';
520 	goto out;
521 
522  bad:
523 	printf("nfs_boot: bootparam_getfile: bad reply\n");
524 	error = EBADRPC;
525 
526  out:
527 	m_freem(m);
528 	return(0);
529 }
530 
531 
532 /*
533  * RPC: mountd/mount
534  * Given a server pathname, get an NFS file handle.
535  * Also, sets sin->sin_port to the NFS service port.
536  */
537 static int
538 md_mount(mdsin, path, fhp)
539 	struct sockaddr_in *mdsin;		/* mountd server address */
540 	char *path;
541 	u_char *fhp;
542 {
543 	/* The RPC structures */
544 	struct rpc_string *str;
545 	struct rdata {
546 		u_long	errno;
547 		u_char	fh[NFS_FHSIZE];
548 	} *rdata;
549 	struct mbuf *m;
550 	int error, mlen, slen;
551 
552 	slen = strlen(path);
553 	mlen = RPC_STR_SIZE(slen);
554 
555 	m = m_get_len(mlen);
556 	if (m == NULL)
557 		return ENOBUFS;
558 	str = mtod(m, struct rpc_string *);
559 	str->len = htonl(slen);
560 	bcopy(path, str->data, slen);
561 
562 	/* Do RPC to mountd. */
563 	error = krpc_call((struct sockaddr *)mdsin,
564 					  RPCPROG_MNT, RPCMNT_VER1,
565 					  RPCMNT_MOUNT, &m);
566 	if (error)
567 		return error;	/* message already freed */
568 
569 	mlen = m->m_len;
570 	if (mlen < sizeof(*rdata))
571 		goto bad;
572 	rdata = mtod(m, struct rdata *);
573 	error = ntohl(rdata->errno);
574 	if (error)
575 		goto bad;
576 	bcopy(rdata->fh, fhp, NFS_FHSIZE);
577 
578 	/* Set port number for NFS use. */
579 	error = krpc_portmap((struct sockaddr *)mdsin,
580 						 NFS_PROG, NFS_VER2,
581 						 &mdsin->sin_port);
582 	goto out;
583 
584  bad:
585 	error = EBADRPC;
586 
587  out:
588 	m_freem(m);
589 	return error;
590 }
591