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