xref: /netbsd-src/sys/nfs/nfs_boot.c (revision ae9172d6cd9432a6a1a56760d86b32c57a66c39c)
1 /*    $NetBSD: nfs_boot.c,v 1.12 1994/10/29 06:38:11 cgd Exp $ */
2 
3 /*
4  * Copyright (c) 1994 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/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 #include <nfs/krpc.h>
52 
53 #include "ether.h"
54 #if NETHER == 0
55 
56 int nfs_boot_init(nd, procp)
57 	struct nfs_diskless *nd;
58 	struct proc *procp;
59 {
60 	panic("nfs_boot_init: no ether");
61 }
62 
63 #else /* NETHER */
64 
65 /*
66  * Support for NFS diskless booting, specifically getting information
67  * about where to boot from, what pathnames, etc.
68  *
69  * This implememtation uses RARP and the bootparam RPC.
70  * We are forced to implement RPC anyway (to get file handles)
71  * so we might as well take advantage of it for bootparam too.
72  *
73  * The diskless boot sequence goes as follows:
74  * (1) Get our interface address using RARP
75  *     (also save the address of the RARP server)
76  * (2) Get our hostname using RPC/bootparam/whoami
77  *     (all boopararms RPCs to the RARP server)
78  * (3) Get the root path using RPC/bootparam/getfile
79  * (4) Get the root file handle using RPC/mountd
80  * (5) Get the swap path using RPC/bootparam/getfile
81  * (6) Get the swap file handle using RPC/mountd
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 /*
101  * Called with an empty nfs_diskless struct to be filled in.
102  */
103 int
104 nfs_boot_init(nd, procp)
105 	struct nfs_diskless *nd;
106 	struct proc *procp;
107 {
108 	struct ifreq ireq;
109 	struct in_addr my_ip, gw_ip;
110 	struct sockaddr_in bp_sin;
111 	struct sockaddr_in *sin;
112 	struct ifnet *ifp;
113 	struct socket *so;
114 	int error, len;
115 	u_short port;
116 
117 #if 0
118 	/*
119 	 * XXX time must be non-zero when we init the interface or else
120 	 * the arp code will wedge... (Fixed in if_ether.c -gwr)
121 	 */
122 	if (time.tv_sec == 0)
123 		time.tv_sec = 1;
124 #endif
125 
126 	/*
127 	 * Find an interface, rarp for its ip address, stuff it, the
128 	 * implied broadcast addr, and netmask into a nfs_diskless struct.
129 	 *
130 	 * This was moved here from nfs_vfsops.c because this procedure
131 	 * would be quite different if someone decides to write (i.e.) a
132 	 * BOOTP version of this file (might not use RARP, etc.) -gwr
133 	 */
134 
135 	/*
136 	 * Find a network interface.
137 	 * XXX - This should use the specified boot device.
138 	 */
139 	for (ifp = ifnet; ifp; ifp = ifp->if_next)
140 		if ((ifp->if_flags & (IFF_LOOPBACK|IFF_POINTOPOINT)) == 0)
141 			break;
142 	if (ifp == NULL)
143 		panic("nfs_boot: no suitable interface");
144 	sprintf(ireq.ifr_name, "%s%d", ifp->if_name, ifp->if_unit);
145 	printf("nfs_boot: using network interface '%s'\n",
146 	    ireq.ifr_name);
147 
148 	/*
149 	 * Bring up the interface.
150 	 */
151 	if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)) != 0)
152 		panic("nfs_boot: socreate, error=%d", error);
153 	ireq.ifr_flags = IFF_UP;
154 	error = ifioctl(so, SIOCSIFFLAGS, (caddr_t)&ireq, procp);
155 	if (error)
156 		panic("nfs_boot: SIFFLAGS, error=%d", error);
157 
158 	/*
159 	 * Do RARP for the interface address.  Also
160 	 * save the server address for bootparam RPC.
161 	 */
162 	if ((error = revarpwhoami(&my_ip, ifp)) != 0)
163 		panic("revarp failed, error=%d", error);
164 	printf("nfs_boot: client_addr=0x%x\n", ntohl(my_ip.s_addr));
165 
166 	/*
167 	 * Do enough of ifconfig(8) so that the chosen interface can
168 	 * talk to the server(s).  (also get brcast addr and netmask)
169 	 */
170 	/* Set interface address. */
171 	sin = (struct sockaddr_in *)&ireq.ifr_addr;
172 	bzero((caddr_t)sin, sizeof(*sin));
173 	sin->sin_len = sizeof(*sin);
174 	sin->sin_family = AF_INET;
175 	sin->sin_addr.s_addr = my_ip.s_addr;
176 	error = ifioctl(so, SIOCSIFADDR, (caddr_t)&ireq, procp);
177 	if (error)
178 		panic("nfs_boot: set if addr, error=%d", error);
179 
180 	soclose(so);
181 
182 	/*
183 	 * Get client name and gateway address.
184 	 * RPC: bootparam/whoami
185 	 * XXX - Using old broadcast addr. for WHOAMI,
186 	 * then it is replaced with the BP server addr.
187 	 */
188 	bzero((caddr_t)&bp_sin, sizeof(bp_sin));
189 	bp_sin.sin_len = sizeof(bp_sin);
190 	bp_sin.sin_family = AF_INET;
191 	bp_sin.sin_addr.s_addr = ~0;	/* XXX */
192 	hostnamelen = MAXHOSTNAMELEN;
193 
194 	/* this returns gateway IP address */
195 	error = bp_whoami(&bp_sin, &my_ip, &gw_ip);
196 	if (error)
197 		panic("nfs_boot: bootparam whoami, error=%d", error);
198 	printf("nfs_boot: server_addr=0x%x\n",
199 		   ntohl(bp_sin.sin_addr.s_addr));
200 	printf("nfs_boot: hostname=%s\n", hostname);
201 
202 #ifdef	NFS_BOOT_GATEWAY
203 	/*
204 	 * XXX - This code is conditionally compiled only because
205 	 * many bootparam servers (in particular, SunOS 4.1.3)
206 	 * always set the gateway address to their own address.
207 	 * The bootparam server is not necessarily the gateway.
208 	 * We could just believe the server, and at worst you would
209 	 * need to delete the incorrect default route before adding
210 	 * the correct one, but for simplicity, ignore the gateway.
211 	 * If your server is OK, you can turn on this option.
212 	 *
213 	 * If the gateway address is set, add a default route.
214 	 * (The mountd RPCs may go across a gateway.)
215 	 */
216 	if (gw_ip.s_addr) {
217 		struct sockaddr dst, gw, mask;
218 		/* Destination: (default) */
219 		bzero((caddr_t)&dst, sizeof(dst));
220 		dst.sa_len = sizeof(dst);
221 		dst.sa_family = AF_INET;
222 		/* Gateway: */
223 		bzero((caddr_t)&gw, sizeof(gw));
224 		sin = (struct sockaddr_in *)&gw;
225 		sin->sin_len = sizeof(gw);
226 		sin->sin_family = AF_INET;
227 		sin->sin_addr.s_addr = gw_ip.s_addr;
228 		/* Mask: (zero length) */
229 		bzero(&mask, sizeof(mask));
230 
231 		printf("nfs_boot: gateway=0x%x\n", ntohl(gw_ip.s_addr));
232 		/* add, dest, gw, mask, flags, 0 */
233 		error = rtrequest(RTM_ADD, &dst, (struct sockaddr *)&gw,
234 		    &mask, (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL);
235 		if (error)
236 			printf("nfs_boot: add route, error=%d\n", error);
237 	}
238 #endif
239 
240 	get_path_and_handle(&bp_sin, "root", &nd->nd_root);
241 	get_path_and_handle(&bp_sin, "swap", &nd->nd_swap);
242 
243 	return (0);
244 }
245 
246 static void
247 get_path_and_handle(bpsin, key, ndmntp)
248 	struct sockaddr_in *bpsin;	/* bootparam server */
249 	char *key;			/* root or swap */
250 	struct nfs_dlmount *ndmntp;	/* output */
251 {
252 	char pathname[MAXPATHLEN];
253 	char *sp, *dp, *endp;
254 	int error;
255 
256 	/*
257 	 * Get server:pathname for "key" (root or swap)
258 	 * using RPC to bootparam/getfile
259 	 */
260 	error = bp_getfile(bpsin, key, &ndmntp->ndm_saddr,
261 	    ndmntp->ndm_host, pathname);
262 	if (error)
263 		panic("nfs_boot: bootparam get %s: %d", key, error);
264 	printf("%s on %s:%s\n", key, ndmntp->ndm_host, pathname);
265 
266 	/*
267 	 * Get file handle for "key" (root or swap)
268 	 * using RPC to mountd/mount
269 	 */
270 	error = md_mount(&ndmntp->ndm_saddr, pathname, ndmntp->ndm_fh);
271 	if (error)
272 		panic("nfs_boot: mountd %s, error=%d", key, error);
273 
274 	/* Construct remote path (for getmntinfo(3)) */
275 	dp = ndmntp->ndm_host;
276 	endp = dp + MNAMELEN - 1;
277 	dp += strlen(dp);
278 	*dp++ = ':';
279 	for (sp = pathname; *sp && dp < endp;)
280 		*dp++ = *sp++;
281 	*dp = '\0';
282 
283 }
284 
285 
286 /*
287  * Get an mbuf with the given length, and
288  * initialize the pkthdr length field.
289  */
290 static struct mbuf *
291 m_get_len(int msg_len)
292 {
293 	struct mbuf *m;
294 	m = m_gethdr(M_WAIT, MT_DATA);
295 	if (m == NULL)
296 		return NULL;
297 	if (msg_len > MHLEN) {
298 		if (msg_len > MCLBYTES)
299 			panic("nfs_boot: msg_len > MCLBYTES");
300 		MCLGET(m, M_WAIT);
301 		if (m == NULL)
302 			return NULL;
303 	}
304 	m->m_len = msg_len;
305 	m->m_pkthdr.len = m->m_len;
306 	return (m);
307 }
308 
309 
310 /*
311  * String representation for RPC.
312  */
313 struct rpc_string {
314 	u_long len;		/* length without null or padding */
315 	u_char data[4];	/* data (longer, of course) */
316     /* data is padded to a long-word boundary */
317 };
318 /* Compute space used given string length. */
319 #define	RPC_STR_SIZE(slen) (4 + ((slen + 3) & ~3))
320 
321 /*
322  * Inet address in RPC messages
323  * (Note, really four longs, NOT chars.  Blech.)
324  */
325 struct bp_inaddr {
326 	u_long  atype;
327 	long	addr[4];
328 };
329 
330 
331 /*
332  * RPC: bootparam/whoami
333  * Given client IP address, get:
334  *	client name	(hostname)
335  *	domain name (domainname)
336  *	gateway address
337  *
338  * Setting the hostname and domainname here may be somewhat
339  * controvercial, but it is so easy to do it here. -gwr
340  *
341  * Note - bpsin is initialized to the broadcast address,
342  * and will be replaced with the bootparam server address
343  * after this call is complete.  Have to use PMAP_PROC_CALL
344  * to make sure we get responses only from a servers that
345  * know about us (don't want to broadcast a getport call).
346  */
347 static int
348 bp_whoami(bpsin, my_ip, gw_ip)
349 	struct sockaddr_in *bpsin;
350 	struct in_addr *my_ip;
351 	struct in_addr *gw_ip;
352 {
353 	/* RPC structures for PMAPPROC_CALLIT */
354 	struct whoami_call {
355 		u_long call_prog;
356 		u_long call_vers;
357 		u_long call_proc;
358 		u_long call_arglen;
359 		struct bp_inaddr call_ia;
360 	} *call;
361 
362 	struct rpc_string *str;
363 	struct bp_inaddr *bia;
364 	struct mbuf *m, *from;
365 	struct sockaddr_in *sin;
366 	int error, msg_len;
367 	int cn_len, dn_len;
368 	u_char *p;
369 	long *lp;
370 
371 	/*
372 	 * Get message buffer of sufficient size.
373 	 */
374 	msg_len = sizeof(*call);
375 	m = m_get_len(msg_len);
376 	if (m == NULL)
377 		return ENOBUFS;
378 
379 	/*
380 	 * Build request message for PMAPPROC_CALLIT.
381 	 */
382 	call = mtod(m, struct whoami_call *);
383 	call->call_prog = htonl(BOOTPARAM_PROG);
384 	call->call_vers = htonl(BOOTPARAM_VERS);
385 	call->call_proc = htonl(BOOTPARAM_WHOAMI);
386 	call->call_arglen = htonl(sizeof(struct bp_inaddr));
387 
388 	/* client IP address */
389 	call->call_ia.atype = htonl(1);
390 	p = (u_char*)my_ip;
391 	lp = call->call_ia.addr;
392 	*lp++ = htonl(*p);	p++;
393 	*lp++ = htonl(*p);	p++;
394 	*lp++ = htonl(*p);	p++;
395 	*lp++ = htonl(*p);	p++;
396 
397 	/* RPC: portmap/callit */
398 	bpsin->sin_port = htons(PMAPPORT);
399 	from = NULL;
400 	error = krpc_call(bpsin, PMAPPROG, PMAPVERS,
401 			PMAPPROC_CALLIT, &m, &from);
402 	if (error)
403 		return error;
404 
405 	/*
406 	 * Parse result message.
407 	 */
408 	msg_len = m->m_len;
409 	lp = mtod(m, long *);
410 
411 	/* bootparam server port (also grab from address). */
412 	if (msg_len < sizeof(*lp))
413 		goto bad;
414 	msg_len -= sizeof(*lp);
415 	bpsin->sin_port = htons((short)ntohl(*lp++));
416 	sin = mtod(from, struct sockaddr_in *);
417 	bpsin->sin_addr.s_addr = sin->sin_addr.s_addr;
418 
419 	/* length of encapsulated results */
420 	if (msg_len < (ntohl(*lp) + sizeof(*lp)))
421 		goto bad;
422 	msg_len = ntohl(*lp++);
423 	p = (char*)lp;
424 
425 	/* client name */
426 	if (msg_len < sizeof(*str))
427 		goto bad;
428 	str = (struct rpc_string *)p;
429 	cn_len = ntohl(str->len);
430 	if (msg_len < cn_len)
431 		goto bad;
432 	if (cn_len >= MAXHOSTNAMELEN)
433 		goto bad;
434 	bcopy(str->data, hostname, cn_len);
435 	hostname[cn_len] = '\0';
436 	hostnamelen = cn_len;
437 	p += RPC_STR_SIZE(cn_len);
438 	msg_len -= RPC_STR_SIZE(cn_len);
439 
440 	/* domain name */
441 	if (msg_len < sizeof(*str))
442 		goto bad;
443 	str = (struct rpc_string *)p;
444 	dn_len = ntohl(str->len);
445 	if (msg_len < dn_len)
446 		goto bad;
447 	if (dn_len >= MAXHOSTNAMELEN)
448 		goto bad;
449 	bcopy(str->data, domainname, dn_len);
450 	domainname[dn_len] = '\0';
451 	domainnamelen = dn_len;
452 	p += RPC_STR_SIZE(dn_len);
453 	msg_len -= RPC_STR_SIZE(dn_len);
454 
455 	/* gateway address */
456 	if (msg_len < sizeof(*bia))
457 		goto bad;
458 	bia = (struct bp_inaddr *)p;
459 	if (bia->atype != htonl(1))
460 		goto bad;
461 	p = (u_char*)gw_ip;
462 	*p++ = ntohl(bia->addr[0]);
463 	*p++ = ntohl(bia->addr[1]);
464 	*p++ = ntohl(bia->addr[2]);
465 	*p++ = ntohl(bia->addr[3]);
466 	goto out;
467 
468 bad:
469 	printf("nfs_boot: bootparam_whoami: bad reply\n");
470 	error = EBADRPC;
471 
472 out:
473 	if (from)
474 		m_freem(from);
475 	m_freem(m);
476 	return(error);
477 }
478 
479 
480 /*
481  * RPC: bootparam/getfile
482  * Given client name and file "key", get:
483  *	server name
484  *	server IP address
485  *	server pathname
486  */
487 static int
488 bp_getfile(bpsin, key, md_sin, serv_name, pathname)
489 	struct sockaddr_in *bpsin;
490 	char *key;
491 	struct sockaddr_in *md_sin;
492 	char *serv_name;
493 	char *pathname;
494 {
495 	struct rpc_string *str;
496 	struct mbuf *m;
497 	struct bp_inaddr *bia;
498 	struct sockaddr_in *sin;
499 	u_char *p, *q;
500 	int error, msg_len;
501 	int cn_len, key_len, sn_len, path_len;
502 
503 	/*
504 	 * Get message buffer of sufficient size.
505 	 */
506 	cn_len = hostnamelen;
507 	key_len = strlen(key);
508 	msg_len = 0;
509 	msg_len += RPC_STR_SIZE(cn_len);
510 	msg_len += RPC_STR_SIZE(key_len);
511 	m = m_get_len(msg_len);
512 	if (m == NULL)
513 		return ENOBUFS;
514 
515 	/*
516 	 * Build request message.
517 	 */
518 	p = mtod(m, u_char *);
519 	bzero(p, msg_len);
520 	/* client name (hostname) */
521 	str = (struct rpc_string *)p;
522 	str->len = htonl(cn_len);
523 	bcopy(hostname, str->data, cn_len);
524 	p += RPC_STR_SIZE(cn_len);
525 	/* key name (root or swap) */
526 	str = (struct rpc_string *)p;
527 	str->len = htonl(key_len);
528 	bcopy(key, str->data, key_len);
529 
530 	/* RPC: bootparam/getfile */
531 	error = krpc_call(bpsin, BOOTPARAM_PROG, BOOTPARAM_VERS,
532 			BOOTPARAM_GETFILE, &m, NULL);
533 	if (error)
534 		return error;
535 
536 	/*
537 	 * Parse result message.
538 	 */
539 	p = mtod(m, u_char *);
540 	msg_len = m->m_len;
541 
542 	/* server name */
543 	if (msg_len < sizeof(*str))
544 		goto bad;
545 	str = (struct rpc_string *)p;
546 	sn_len = ntohl(str->len);
547 	if (msg_len < sn_len)
548 		goto bad;
549 	if (sn_len >= MNAMELEN)
550 		goto bad;
551 	bcopy(str->data, serv_name, sn_len);
552 	serv_name[sn_len] = '\0';
553 	p += RPC_STR_SIZE(sn_len);
554 	msg_len -= RPC_STR_SIZE(sn_len);
555 
556 	/* server IP address (mountd) */
557 	if (msg_len < sizeof(*bia))
558 		goto bad;
559 	bia = (struct bp_inaddr *)p;
560 	if (bia->atype != htonl(1))
561 		goto bad;
562 	sin = md_sin;
563 	bzero((caddr_t)sin, sizeof(*sin));
564 	sin->sin_len = sizeof(*sin);
565 	sin->sin_family = AF_INET;
566 	q = (u_char*) &sin->sin_addr;
567 	*q++ = ntohl(bia->addr[0]);
568 	*q++ = ntohl(bia->addr[1]);
569 	*q++ = ntohl(bia->addr[2]);
570 	*q++ = ntohl(bia->addr[3]);
571 	p += sizeof(*bia);
572 	msg_len -= sizeof(*bia);
573 
574 	/* server pathname */
575 	if (msg_len < sizeof(*str))
576 		goto bad;
577 	str = (struct rpc_string *)p;
578 	path_len = ntohl(str->len);
579 	if (msg_len < path_len)
580 		goto bad;
581 	if (path_len >= MAXPATHLEN)
582 		goto bad;
583 	bcopy(str->data, pathname, path_len);
584 	pathname[path_len] = '\0';
585 	goto out;
586 
587 bad:
588 	printf("nfs_boot: bootparam_getfile: bad reply\n");
589 	error = EBADRPC;
590 
591 out:
592 	m_freem(m);
593 	return(0);
594 }
595 
596 
597 /*
598  * RPC: mountd/mount
599  * Given a server pathname, get an NFS file handle.
600  * Also, sets sin->sin_port to the NFS service port.
601  */
602 static int
603 md_mount(mdsin, path, fhp)
604 	struct sockaddr_in *mdsin;		/* mountd server address */
605 	char *path;
606 	u_char *fhp;
607 {
608 	/* The RPC structures */
609 	struct rpc_string *str;
610 	struct rdata {
611 		u_long	errno;
612 		u_char	fh[NFS_FHSIZE];
613 	} *rdata;
614 	struct mbuf *m;
615 	int error, mlen, slen;
616 
617 	/* Get port number for MOUNTD. */
618 	error = krpc_portmap(mdsin, RPCPROG_MNT, RPCMNT_VER1,
619 						 &mdsin->sin_port);
620 	if (error) return error;
621 
622 	slen = strlen(path);
623 	mlen = RPC_STR_SIZE(slen);
624 
625 	m = m_get_len(mlen);
626 	if (m == NULL)
627 		return ENOBUFS;
628 	str = mtod(m, struct rpc_string *);
629 	str->len = htonl(slen);
630 	bcopy(path, str->data, slen);
631 
632 	/* Do RPC to mountd. */
633 	error = krpc_call(mdsin, RPCPROG_MNT, RPCMNT_VER1,
634 			RPCMNT_MOUNT, &m, NULL);
635 	if (error)
636 		return error;	/* message already freed */
637 
638 	mlen = m->m_len;
639 	if (mlen < sizeof(*rdata))
640 		goto bad;
641 	rdata = mtod(m, struct rdata *);
642 	error = ntohl(rdata->errno);
643 	if (error)
644 		goto bad;
645 	bcopy(rdata->fh, fhp, NFS_FHSIZE);
646 
647 	/* Set port number for NFS use. */
648 	error = krpc_portmap(mdsin, NFS_PROG, NFS_VER2,
649 						 &mdsin->sin_port);
650 	goto out;
651 
652 bad:
653 	error = EBADRPC;
654 
655 out:
656 	m_freem(m);
657 	return error;
658 }
659 
660 #endif /* NETHER */
661