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