1/*
2 * Copyright (c) 2000-2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/* Copyright (c) 1995, 1997 NeXT Computer, Inc. All Rights Reserved */
29/*
30 * Copyright (c) 1994 Adam Glass, Gordon Ross
31 * All rights reserved.
32 *
33 * This software was developed by the Computer Systems Engineering group
34 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
35 * contributed to Berkeley.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 *    notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 *    notice, this list of conditions and the following disclaimer in the
44 *    documentation and/or other materials provided with the distribution.
45 * 3. All advertising materials mentioning features or use of this software
46 *    must display the following acknowledgement:
47 *      This product includes software developed by the University of
48 *      California, Lawrence Berkeley Laboratory and its contributors.
49 * 4. Neither the name of the University nor the names of its contributors
50 *    may be used to endorse or promote products derived from this software
51 *    without specific prior written permission.
52 *
53 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 * SUCH DAMAGE.
64 *
65 *  History:
66 *  14-March-97	Dieter Siegmund (dieter@next.com)
67 *	- Use BOOTP instead of RARP to get the IP address at boot time
68 *
69 *  23-May-97  Umesh Vaishampayan  (umeshv@apple.com)
70 *	- Added the ability to mount "/private" separately.
71 *
72 *  30-May-97	Dieter Siegmund	(dieter@next.com)
73 *	- Clear out the ireq structure before using it to prevent
74 *	  our sending using a bogus source IP address, we should use
75 *	  an IP address of all zeroes
76 *	- Right after BOOTP, get the correct netmask using AUTONETMASK
77 *  18-Jul-97	Dieter Siegmund	(dieter@apple.com)
78 *	- we can't restrict the netmask until we have a default route,
79 *	  removed AUTONETMASK call (ifdef'd out)
80 *  5-Aug-97	Dieter Siegmund (dieter@apple.com)
81 *	- use the default route from the bpwhoami call, enabled autonetmask
82 *	  again
83 *  19-Feb-1999	Dieter Siegmund (dieter@apple.com)
84 *	- use new BOOTP routine to get the subnet mask and router
85 *        and stop using SIOCAUTOADDR
86 *      - don't bother mounting private separately if it's not
87 *        specified or not required because they are substrings of
88 *        one another ie. root=host:/A and private=host:/A/private
89 *      - allow the root path to be specified in the boot variable
90 *	  "rp" (AKA "rootpath")
91 *  19-Jul-1999 Dieter Siegmund (dieter@apple.com)
92 *	- replaced big automatic arrays with MALLOC'd data
93 */
94
95#include <sys/param.h>
96#include <sys/systm.h>
97#include <sys/kernel.h>
98#include <sys/conf.h>
99#include <sys/ioctl.h>
100#include <sys/proc.h>
101#include <sys/mount_internal.h>
102#include <sys/kpi_mbuf.h>
103
104#include <sys/malloc.h>
105#include <sys/socket.h>
106
107#include <net/if.h>
108#include <net/if_dl.h>
109#include <net/if_types.h>
110#include <net/route.h>
111
112#include <netinet/in.h>
113#include <netinet/if_ether.h>
114
115#include <nfs/rpcv2.h>
116#include <nfs/nfsproto.h>
117#include <nfs/nfs.h>
118#include <nfs/nfsdiskless.h>
119#include <nfs/krpc.h>
120
121#include <pexpert/pexpert.h>
122
123#include "ether.h"
124
125#include <libkern/libkern.h>
126
127
128#if NETHER == 0
129
130int nfs_boot_init(__unused struct nfs_diskless *nd)
131{
132	panic("nfs_boot_init: no ether");
133}
134
135int nfs_boot_getfh(__unused struct nfs_diskless *nd, __unused int v3, __unused int sotype)
136{
137	panic("nfs_boot_getfh: no ether");
138}
139
140#else /* NETHER */
141
142/*
143 * Support for NFS diskless booting, specifically getting information
144 * about where to boot from, what pathnames, etc.
145 *
146 * This implememtation uses RARP and the bootparam RPC.
147 * We are forced to implement RPC anyway (to get file handles)
148 * so we might as well take advantage of it for bootparam too.
149 *
150 * The diskless boot sequence goes as follows:
151 * (1) Use RARP to get our interface address
152 * (2) Use RPC/bootparam/whoami to get our hostname,
153 *     our IP address, and the server's IP address.
154 * (3) Use RPC/bootparam/getfile to get the root path
155 * (4) Use RPC/mountd to get the root file handle
156 * (5) Use RPC/bootparam/getfile to get the swap path
157 * (6) Use RPC/mountd to get the swap file handle
158 *
159 * (This happens to be the way Sun does it too.)
160 */
161
162/* bootparam RPC */
163static int bp_whoami(struct sockaddr_in *bpsin,
164	struct in_addr *my_ip, struct in_addr *gw_ip);
165static int bp_getfile(struct sockaddr_in *bpsin, const char *key,
166	struct sockaddr_in *mdsin, char *servname, char *path);
167
168/* mountd RPC */
169static int md_mount(struct sockaddr_in *mdsin, char *path, int v3, int sotype,
170	u_char *fhp, u_int32_t *fhlenp);
171
172/* other helpers */
173static int get_file_handle(struct nfs_dlmount *ndmntp);
174
175
176#define IP_FORMAT	"%d.%d.%d.%d"
177#define IP_CH(ip)	((u_char *)ip)
178#define IP_LIST(ip)	IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
179
180#include <sys/netboot.h>
181
182/*
183 * Called with an empty nfs_diskless struct to be filled in.
184 */
185int
186nfs_boot_init(struct nfs_diskless *nd)
187{
188	struct sockaddr_in 	bp_sin;
189	boolean_t		do_bpwhoami = TRUE;
190	boolean_t		do_bpgetfile = TRUE;
191	int 			error = 0;
192	struct in_addr 		my_ip;
193	struct sockaddr_in *	sin_p;
194
195	/* make sure mbuf constants are set up */
196	if (!nfs_mbuf_mhlen)
197		nfs_mbuf_init();
198
199	/* by this point, networking must already have been configured */
200	if (netboot_iaddr(&my_ip) == FALSE) {
201	    printf("nfs_boot: networking is not initialized\n");
202	    error = ENXIO;
203	    goto failed;
204	}
205
206	/* get the root path information */
207	MALLOC_ZONE(nd->nd_root.ndm_path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
208	if (!nd->nd_root.ndm_path) {
209	    printf("nfs_boot: can't allocate root path buffer\n");
210	    error = ENOMEM;
211	    goto failed;
212	}
213	MALLOC_ZONE(nd->nd_root.ndm_mntfrom, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
214	if (!nd->nd_root.ndm_mntfrom) {
215	    printf("nfs_boot: can't allocate root mntfrom buffer\n");
216	    error = ENOMEM;
217	    goto failed;
218	}
219	sin_p = &nd->nd_root.ndm_saddr;
220	bzero((caddr_t)sin_p, sizeof(*sin_p));
221	sin_p->sin_len = sizeof(*sin_p);
222	sin_p->sin_family = AF_INET;
223	if (netboot_rootpath(&sin_p->sin_addr, nd->nd_root.ndm_host,
224			     sizeof(nd->nd_root.ndm_host),
225			     nd->nd_root.ndm_path, MAXPATHLEN) == TRUE) {
226	    do_bpgetfile = FALSE;
227	    do_bpwhoami = FALSE;
228	}
229	nd->nd_private.ndm_saddr.sin_addr.s_addr = 0;
230
231	if (do_bpwhoami) {
232		struct in_addr router;
233		/*
234		 * Get client name and gateway address.
235		 * RPC: bootparam/whoami
236		 * Use the old broadcast address for the WHOAMI
237		 * call because we do not yet know our netmask.
238		 * The server address returned by the WHOAMI call
239		 * is used for all subsequent booptaram RPCs.
240		 */
241		bzero((caddr_t)&bp_sin, sizeof(bp_sin));
242		bp_sin.sin_len = sizeof(bp_sin);
243		bp_sin.sin_family = AF_INET;
244		bp_sin.sin_addr.s_addr = INADDR_BROADCAST;
245		hostnamelen = MAXHOSTNAMELEN;
246		router.s_addr = 0;
247		error = bp_whoami(&bp_sin, &my_ip, &router);
248		if (error) {
249			printf("nfs_boot: bootparam whoami, error=%d", error);
250			goto failed;
251		}
252		printf("nfs_boot: BOOTPARAMS server " IP_FORMAT "\n",
253		       IP_LIST(&bp_sin.sin_addr));
254		printf("nfs_boot: hostname %s\n", hostname);
255	}
256	if (do_bpgetfile) {
257		error = bp_getfile(&bp_sin, "root", &nd->nd_root.ndm_saddr,
258				   nd->nd_root.ndm_host, nd->nd_root.ndm_path);
259		if (error) {
260			printf("nfs_boot: bootparam get root: %d\n", error);
261			goto failed;
262		}
263	}
264
265#if !defined(NO_MOUNT_PRIVATE)
266	if (do_bpgetfile) { /* get private path */
267		MALLOC_ZONE(nd->nd_private.ndm_path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
268		if (!nd->nd_private.ndm_path) {
269			printf("nfs_boot: can't allocate private path buffer\n");
270			error = ENOMEM;
271			goto failed;
272		}
273		MALLOC_ZONE(nd->nd_private.ndm_mntfrom, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
274		if (!nd->nd_private.ndm_mntfrom) {
275			printf("nfs_boot: can't allocate private host buffer\n");
276			error = ENOMEM;
277			goto failed;
278		}
279		error = bp_getfile(&bp_sin, "private",
280				   &nd->nd_private.ndm_saddr,
281				   nd->nd_private.ndm_host,
282				   nd->nd_private.ndm_path);
283		if (!error) {
284			char * check_path = NULL;
285
286			MALLOC_ZONE(check_path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
287			if (!check_path) {
288				printf("nfs_boot: can't allocate check_path buffer\n");
289				error = ENOMEM;
290				goto failed;
291			}
292			snprintf(check_path, MAXPATHLEN, "%s/private", nd->nd_root.ndm_path);
293			if ((nd->nd_root.ndm_saddr.sin_addr.s_addr
294			     == nd->nd_private.ndm_saddr.sin_addr.s_addr)
295			    && (strncmp(check_path, nd->nd_private.ndm_path, MAXPATHLEN) == 0)) {
296				/* private path is prefix of root path, don't mount */
297				nd->nd_private.ndm_saddr.sin_addr.s_addr = 0;
298			}
299			FREE_ZONE(check_path, MAXPATHLEN, M_NAMEI);
300		}
301		else {
302			/* private key not defined, don't mount */
303			nd->nd_private.ndm_saddr.sin_addr.s_addr = 0;
304		}
305	}
306	else {
307		error = 0;
308	}
309#endif /* NO_MOUNT_PRIVATE */
310failed:
311	return (error);
312}
313
314/*
315 * Called with a partially initialized nfs_diskless struct
316 * with file handles to be filled in.
317 */
318int
319nfs_boot_getfh(struct nfs_diskless *nd, int v3, int sotype)
320{
321	int error = 0;
322
323	nd->nd_root.ndm_nfsv3 = v3;
324	nd->nd_root.ndm_sotype = sotype;
325	error = get_file_handle(&nd->nd_root);
326	if (error) {
327		printf("nfs_boot: get_file_handle(v%d) root failed, %d\n",
328			v3 ? 3 : 2, error);
329		goto failed;
330	}
331
332#if !defined(NO_MOUNT_PRIVATE)
333	if (nd->nd_private.ndm_saddr.sin_addr.s_addr) {
334		/* get private file handle */
335		nd->nd_private.ndm_nfsv3 = v3;
336		nd->nd_private.ndm_sotype = sotype;
337		error = get_file_handle(&nd->nd_private);
338		if (error) {
339			printf("nfs_boot: get_file_handle(v%d) private failed, %d\n",
340				v3 ? 3 : 2, error);
341			goto failed;
342		}
343	}
344#endif /* NO_MOUNT_PRIVATE */
345failed:
346	return (error);
347}
348
349static int
350get_file_handle(ndmntp)
351	struct nfs_dlmount *ndmntp;
352{
353	char *sp, *dp, *endp;
354	int error;
355
356	/*
357	 * Get file handle for "key" (root or swap)
358	 * using RPC to mountd/mount
359	 */
360	error = md_mount(&ndmntp->ndm_saddr, ndmntp->ndm_path, ndmntp->ndm_nfsv3,
361			ndmntp->ndm_sotype, ndmntp->ndm_fh, &ndmntp->ndm_fhlen);
362	if (error)
363		return (error);
364
365	/* Construct remote path (for getmntinfo(3)) */
366	dp = ndmntp->ndm_mntfrom;
367	endp = dp + MAXPATHLEN - 1;
368	for (sp = ndmntp->ndm_host; *sp && dp < endp;)
369		*dp++ = *sp++;
370	if (dp < endp)
371		*dp++ = ':';
372	for (sp = ndmntp->ndm_path; *sp && dp < endp;)
373		*dp++ = *sp++;
374	*dp = '\0';
375	return (0);
376
377}
378
379
380/*
381 * Get an mbuf with the given length, and
382 * initialize the pkthdr length field.
383 */
384static int
385mbuf_get_with_len(size_t msg_len, mbuf_t *m)
386{
387	int error;
388	error = mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, m);
389	if (error)
390		return (error);
391	if (msg_len > mbuf_maxlen(*m)) {
392		error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, m);
393		if (error) {
394			mbuf_freem(*m);
395			return (error);
396		}
397		if (msg_len > mbuf_maxlen(*m))
398			panic("nfs_boot: msg_len > MCLBYTES");
399	}
400	mbuf_setlen(*m, msg_len);
401	mbuf_pkthdr_setlen(*m, msg_len);
402	return (0);
403}
404
405
406/*
407 * String representation for RPC.
408 */
409struct rpc_string {
410	u_int32_t len;		/* length without null or padding */
411	u_char data[4];	/* data (longer, of course) */
412    /* data is padded to a long-word boundary */
413};
414/* Compute space used given string length. */
415#define	RPC_STR_SIZE(slen) (4 + ((slen + 3) & ~3))
416
417/*
418 * Inet address in RPC messages
419 * (Note, really four 32-bit ints, NOT chars.  Blech.)
420 */
421struct bp_inaddr {
422	u_int32_t  atype;
423	int32_t	addr[4];
424};
425
426
427/*
428 * RPC: bootparam/whoami
429 * Given client IP address, get:
430 *	client name	(hostname)
431 *	domain name (domainname)
432 *	gateway address
433 *
434 * The hostname and domainname are set here for convenience.
435 *
436 * Note - bpsin is initialized to the broadcast address,
437 * and will be replaced with the bootparam server address
438 * after this call is complete.  Have to use PMAP_PROC_CALL
439 * to make sure we get responses only from a servers that
440 * know about us (don't want to broadcast a getport call).
441 */
442static int
443bp_whoami(bpsin, my_ip, gw_ip)
444	struct sockaddr_in *bpsin;
445	struct in_addr *my_ip;
446	struct in_addr *gw_ip;
447{
448	/* RPC structures for PMAPPROC_CALLIT */
449	struct whoami_call {
450		u_int32_t call_prog;
451		u_int32_t call_vers;
452		u_int32_t call_proc;
453		u_int32_t call_arglen;
454		struct bp_inaddr call_ia;
455	} *call;
456
457	struct rpc_string *str;
458	struct bp_inaddr *bia;
459	mbuf_t m;
460	struct sockaddr_in sin;
461	int error;
462	size_t msg_len, cn_len, dn_len;
463	u_char *p;
464	int32_t *lp;
465
466	/*
467	 * Get message buffer of sufficient size.
468	 */
469	msg_len = sizeof(*call);
470	error = mbuf_get_with_len(msg_len, &m);
471	if (error)
472		return error;
473
474	/*
475	 * Build request message for PMAPPROC_CALLIT.
476	 */
477	call = mbuf_data(m);
478	call->call_prog = htonl(BOOTPARAM_PROG);
479	call->call_vers = htonl(BOOTPARAM_VERS);
480	call->call_proc = htonl(BOOTPARAM_WHOAMI);
481	call->call_arglen = htonl(sizeof(struct bp_inaddr));
482
483	/* client IP address */
484	call->call_ia.atype = htonl(1);
485	p = (u_char*)my_ip;
486	lp = call->call_ia.addr;
487	*lp++ = htonl(*p);	p++;
488	*lp++ = htonl(*p);	p++;
489	*lp++ = htonl(*p);	p++;
490	*lp++ = htonl(*p);	p++;
491
492	/* RPC: portmap/callit */
493	bpsin->sin_port = htons(PMAPPORT);
494
495	error = krpc_call(bpsin, SOCK_DGRAM, PMAPPROG, PMAPVERS, PMAPPROC_CALLIT, &m, &sin);
496	if (error)
497		return error;
498
499	/*
500	 * Parse result message.
501	 */
502	msg_len = mbuf_len(m);
503	lp = mbuf_data(m);
504
505	/* bootparam server port (also grab from address). */
506	if (msg_len < sizeof(*lp))
507		goto bad;
508	msg_len -= sizeof(*lp);
509	bpsin->sin_port = htons((short)ntohl(*lp++));
510	bpsin->sin_addr.s_addr = sin.sin_addr.s_addr;
511
512	/* length of encapsulated results */
513	if (msg_len < (ntohl(*lp) + sizeof(*lp)))
514		goto bad;
515	msg_len = ntohl(*lp++);
516	p = (u_char*)lp;
517
518	/* client name */
519	if (msg_len < sizeof(*str))
520		goto bad;
521	str = (struct rpc_string *)p;
522	cn_len = ntohl(str->len);
523	if (msg_len < cn_len)
524		goto bad;
525	if (cn_len >= MAXHOSTNAMELEN)
526		goto bad;
527	bcopy(str->data, hostname, cn_len);
528	hostname[cn_len] = '\0';
529	hostnamelen = cn_len;
530	p += RPC_STR_SIZE(cn_len);
531	msg_len -= RPC_STR_SIZE(cn_len);
532
533	/* domain name */
534	if (msg_len < sizeof(*str))
535		goto bad;
536	str = (struct rpc_string *)p;
537	dn_len = ntohl(str->len);
538	if (msg_len < dn_len)
539		goto bad;
540	if (dn_len >= MAXHOSTNAMELEN)
541		goto bad;
542	bcopy(str->data, domainname, dn_len);
543	domainname[dn_len] = '\0';
544	domainnamelen = dn_len;
545	p += RPC_STR_SIZE(dn_len);
546	msg_len -= RPC_STR_SIZE(dn_len);
547
548	/* gateway address */
549	if (msg_len < sizeof(*bia))
550		goto bad;
551	bia = (struct bp_inaddr *)p;
552	if (bia->atype != htonl(1))
553		goto bad;
554	p = (u_char*)gw_ip;
555	*p++ = ntohl(bia->addr[0]);
556	*p++ = ntohl(bia->addr[1]);
557	*p++ = ntohl(bia->addr[2]);
558	*p++ = ntohl(bia->addr[3]);
559	goto out;
560
561bad:
562	printf("nfs_boot: bootparam_whoami: bad reply\n");
563	error = EBADRPC;
564
565out:
566	mbuf_freem(m);
567	return(error);
568}
569
570
571/*
572 * RPC: bootparam/getfile
573 * Given client name and file "key", get:
574 *	server name
575 *	server IP address
576 *	server pathname
577 */
578static int
579bp_getfile(bpsin, key, md_sin, serv_name, pathname)
580	struct sockaddr_in *bpsin;
581	const char *key;
582	struct sockaddr_in *md_sin;
583	char *serv_name;
584	char *pathname;
585{
586	struct rpc_string *str;
587	mbuf_t m;
588	struct bp_inaddr *bia;
589	struct sockaddr_in *sin;
590	u_char *p, *q;
591	int error, msg_len;
592	int cn_len, key_len, sn_len, path_len;
593
594	/*
595	 * Get message buffer of sufficient size.
596	 */
597	cn_len = hostnamelen;
598	key_len = strlen(key);
599	msg_len = 0;
600	msg_len += RPC_STR_SIZE(cn_len);
601	msg_len += RPC_STR_SIZE(key_len);
602	error = mbuf_get_with_len(msg_len, &m);
603	if (error)
604		return error;
605
606	/*
607	 * Build request message.
608	 */
609	p = mbuf_data(m);
610	bzero(p, msg_len);
611	/* client name (hostname) */
612	str = (struct rpc_string *)p;
613	str->len = htonl(cn_len);
614	bcopy(hostname, str->data, cn_len);
615	p += RPC_STR_SIZE(cn_len);
616	/* key name (root or swap) */
617	str = (struct rpc_string *)p;
618	str->len = htonl(key_len);
619	bcopy(key, str->data, key_len);
620
621	/* RPC: bootparam/getfile */
622	error = krpc_call(bpsin, SOCK_DGRAM, BOOTPARAM_PROG, BOOTPARAM_VERS,
623			BOOTPARAM_GETFILE, &m, NULL);
624	if (error)
625		return error;
626
627	/*
628	 * Parse result message.
629	 */
630	p = mbuf_data(m);
631	msg_len = mbuf_len(m);
632
633	/* server name */
634	if (msg_len < (int)sizeof(*str))
635		goto bad;
636	str = (struct rpc_string *)p;
637	sn_len = ntohl(str->len);
638	if (msg_len < sn_len)
639		goto bad;
640	if (sn_len >= MAXHOSTNAMELEN)
641		goto bad;
642	bcopy(str->data, serv_name, sn_len);
643	serv_name[sn_len] = '\0';
644	p += RPC_STR_SIZE(sn_len);
645	msg_len -= RPC_STR_SIZE(sn_len);
646
647	/* server IP address (mountd) */
648	if (msg_len < (int)sizeof(*bia))
649		goto bad;
650	bia = (struct bp_inaddr *)p;
651	if (bia->atype != htonl(1))
652		goto bad;
653	sin = md_sin;
654	bzero((caddr_t)sin, sizeof(*sin));
655	sin->sin_len = sizeof(*sin);
656	sin->sin_family = AF_INET;
657	q = (u_char*) &sin->sin_addr;
658	*q++ = ntohl(bia->addr[0]);
659	*q++ = ntohl(bia->addr[1]);
660	*q++ = ntohl(bia->addr[2]);
661	*q++ = ntohl(bia->addr[3]);
662	p += sizeof(*bia);
663	msg_len -= sizeof(*bia);
664
665	/* server pathname */
666	if (msg_len < (int)sizeof(*str))
667		goto bad;
668	str = (struct rpc_string *)p;
669	path_len = ntohl(str->len);
670	if (msg_len < path_len)
671		goto bad;
672	if (path_len >= MAXPATHLEN)
673		goto bad;
674	bcopy(str->data, pathname, path_len);
675	pathname[path_len] = '\0';
676	goto out;
677
678bad:
679	printf("nfs_boot: bootparam_getfile: bad reply\n");
680	error = EBADRPC;
681
682out:
683	mbuf_freem(m);
684	return(0);
685}
686
687
688/*
689 * RPC: mountd/mount
690 * Given a server pathname, get an NFS file handle.
691 * Also, sets sin->sin_port to the NFS service port.
692 */
693static int
694md_mount(mdsin, path, v3, sotype, fhp, fhlenp)
695	struct sockaddr_in *mdsin;		/* mountd server address */
696	char *path;
697	int v3;
698	int sotype;
699	u_char *fhp;
700	u_int32_t *fhlenp;
701{
702	/* The RPC structures */
703	struct rpc_string *str;
704	struct rdata {
705		u_int32_t	errno;
706		u_char	data[NFSX_V3FHMAX + sizeof(u_int32_t)];
707	} *rdata;
708	mbuf_t m;
709	int error, mlen, slen;
710	int mntversion = v3 ? RPCMNT_VER3 : RPCMNT_VER1;
711	int proto = (sotype == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP;
712	in_port_t mntport, nfsport;
713
714	/* Get port number for MOUNTD. */
715	error = krpc_portmap(mdsin, RPCPROG_MNT, mntversion, proto, &mntport);
716	if (error)
717		return error;
718
719	/* Get port number for NFS use. */
720	/* (If NFS/proto unavailable, don't bother with the mount call) */
721	error = krpc_portmap(mdsin, NFS_PROG, v3 ? NFS_VER3 : NFS_VER2, proto, &nfsport);
722	if (error)
723		return error;
724
725	/* Set port number for MOUNTD */
726	mdsin->sin_port = mntport;
727
728	slen = strlen(path);
729	mlen = RPC_STR_SIZE(slen);
730
731	error = mbuf_get_with_len(mlen, &m);
732	if (error)
733		return error;
734	str = mbuf_data(m);
735	str->len = htonl(slen);
736	bcopy(path, str->data, slen);
737
738	/* Do RPC to mountd. */
739	error = krpc_call(mdsin, sotype, RPCPROG_MNT, mntversion, RPCMNT_MOUNT, &m, NULL);
740	if (error)
741		return error;	/* message already freed */
742
743	/*
744	 * the reply must be long enough to hold the errno plus either of:
745	 * + a v2 filehandle
746	 * + a v3 filehandle length + a v3 filehandle
747	 */
748	mlen = mbuf_len(m);
749	if (mlen < (int)sizeof(u_int32_t))
750		goto bad;
751	rdata = mbuf_data(m);
752	error = ntohl(rdata->errno);
753	if (error)
754		goto out;
755	if (v3) {
756		u_int32_t fhlen;
757		u_char *fh;
758		if (mlen < (int)sizeof(u_int32_t)*2)
759			goto bad;
760		fhlen = ntohl(*(u_int32_t*)rdata->data);
761		fh = rdata->data + sizeof(u_int32_t);
762		if (mlen < (int)(sizeof(u_int32_t)*2 + fhlen))
763			goto bad;
764		bcopy(fh, fhp, fhlen);
765		*fhlenp = fhlen;
766	} else {
767		if (mlen < ((int)sizeof(u_int32_t) + NFSX_V2FH))
768			goto bad;
769		bcopy(rdata->data, fhp, NFSX_V2FH);
770		*fhlenp = NFSX_V2FH;
771	}
772
773	/* Set port number for NFS use. */
774	mdsin->sin_port = nfsport;
775	goto out;
776
777bad:
778	error = EBADRPC;
779
780out:
781	mbuf_freem(m);
782	return error;
783}
784
785#endif /* NETHER */
786