]> git.saurik.com Git - apple/xnu.git/blob - bsd/nfs/nfs_boot.c
67d3d5ef4420158bcea5f6a24e62da75bcf7f55f
[apple/xnu.git] / bsd / nfs / nfs_boot.c
1 /*
2 * Copyright (c) 2000-2016 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
130 int
131 nfs_boot_init(__unused struct nfs_diskless *nd)
132 {
133 panic("nfs_boot_init: no ether");
134 }
135
136 int
137 nfs_boot_getfh(__unused struct nfs_diskless *nd, __unused int v3, __unused int sotype)
138 {
139 panic("nfs_boot_getfh: no ether");
140 }
141
142 #else /* NETHER */
143
144 /*
145 * Support for NFS diskless booting, specifically getting information
146 * about where to boot from, what pathnames, etc.
147 *
148 * This implememtation uses RARP and the bootparam RPC.
149 * We are forced to implement RPC anyway (to get file handles)
150 * so we might as well take advantage of it for bootparam too.
151 *
152 * The diskless boot sequence goes as follows:
153 * (1) Use RARP to get our interface address
154 * (2) Use RPC/bootparam/whoami to get our hostname,
155 * our IP address, and the server's IP address.
156 * (3) Use RPC/bootparam/getfile to get the root path
157 * (4) Use RPC/mountd to get the root file handle
158 * (5) Use RPC/bootparam/getfile to get the swap path
159 * (6) Use RPC/mountd to get the swap file handle
160 *
161 * (This happens to be the way Sun does it too.)
162 */
163
164 /* bootparam RPC */
165 static int bp_whoami(struct sockaddr_in *bpsin,
166 struct in_addr *my_ip, struct in_addr *gw_ip);
167 static int bp_getfile(struct sockaddr_in *bpsin, const char *key,
168 struct sockaddr_in *mdsin, char *servname, char *path);
169
170 /* mountd RPC */
171 static int md_mount(struct sockaddr_in *mdsin, char *path, int v3, int sotype,
172 u_char *fhp, u_int32_t *fhlenp);
173
174 /* other helpers */
175 static int get_file_handle(struct nfs_dlmount *ndmntp);
176
177
178 #define IP_FORMAT "%d.%d.%d.%d"
179 #define IP_CH(ip) ((u_char *)ip)
180 #define IP_LIST(ip) IP_CH(ip)[0],IP_CH(ip)[1],IP_CH(ip)[2],IP_CH(ip)[3]
181
182 #include <sys/netboot.h>
183
184 /*
185 * Called with an empty nfs_diskless struct to be filled in.
186 */
187 int
188 nfs_boot_init(struct nfs_diskless *nd)
189 {
190 struct sockaddr_in bp_sin;
191 boolean_t do_bpwhoami = TRUE;
192 boolean_t do_bpgetfile = TRUE;
193 int error = 0;
194 struct in_addr my_ip;
195 struct sockaddr_in * sin_p;
196
197 /* make sure mbuf constants are set up */
198 if (!nfs_mbuf_mhlen) {
199 nfs_mbuf_init();
200 }
201
202 /* by this point, networking must already have been configured */
203 if (netboot_iaddr(&my_ip) == FALSE) {
204 printf("nfs_boot: networking is not initialized\n");
205 error = ENXIO;
206 goto failed;
207 }
208
209 /* get the root path information */
210 MALLOC_ZONE(nd->nd_root.ndm_path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
211 if (!nd->nd_root.ndm_path) {
212 printf("nfs_boot: can't allocate root path buffer\n");
213 error = ENOMEM;
214 goto failed;
215 }
216 MALLOC_ZONE(nd->nd_root.ndm_mntfrom, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
217 if (!nd->nd_root.ndm_mntfrom) {
218 printf("nfs_boot: can't allocate root mntfrom buffer\n");
219 error = ENOMEM;
220 goto failed;
221 }
222 sin_p = &nd->nd_root.ndm_saddr;
223 bzero((caddr_t)sin_p, sizeof(*sin_p));
224 sin_p->sin_len = sizeof(*sin_p);
225 sin_p->sin_family = AF_INET;
226 if (netboot_rootpath(&sin_p->sin_addr, nd->nd_root.ndm_host,
227 sizeof(nd->nd_root.ndm_host),
228 nd->nd_root.ndm_path, MAXPATHLEN) == TRUE) {
229 do_bpgetfile = FALSE;
230 do_bpwhoami = FALSE;
231 }
232 nd->nd_private.ndm_saddr.sin_addr.s_addr = 0;
233
234 if (do_bpwhoami) {
235 struct in_addr router;
236 /*
237 * Get client name and gateway address.
238 * RPC: bootparam/whoami
239 * Use the old broadcast address for the WHOAMI
240 * call because we do not yet know our netmask.
241 * The server address returned by the WHOAMI call
242 * is used for all subsequent booptaram RPCs.
243 */
244 bzero((caddr_t)&bp_sin, sizeof(bp_sin));
245 bp_sin.sin_len = sizeof(bp_sin);
246 bp_sin.sin_family = AF_INET;
247 bp_sin.sin_addr.s_addr = INADDR_BROADCAST;
248 hostnamelen = MAXHOSTNAMELEN;
249 router.s_addr = 0;
250 error = bp_whoami(&bp_sin, &my_ip, &router);
251 if (error) {
252 printf("nfs_boot: bootparam whoami, error=%d", error);
253 goto failed;
254 }
255 printf("nfs_boot: BOOTPARAMS server " IP_FORMAT "\n",
256 IP_LIST(&bp_sin.sin_addr));
257 printf("nfs_boot: hostname %s\n", hostname);
258 }
259 if (do_bpgetfile) {
260 error = bp_getfile(&bp_sin, "root", &nd->nd_root.ndm_saddr,
261 nd->nd_root.ndm_host, nd->nd_root.ndm_path);
262 if (error) {
263 printf("nfs_boot: bootparam get root: %d\n", error);
264 goto failed;
265 }
266 }
267
268 #if !defined(NO_MOUNT_PRIVATE)
269 if (do_bpgetfile) { /* get private path */
270 MALLOC_ZONE(nd->nd_private.ndm_path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
271 if (!nd->nd_private.ndm_path) {
272 printf("nfs_boot: can't allocate private path buffer\n");
273 error = ENOMEM;
274 goto failed;
275 }
276 MALLOC_ZONE(nd->nd_private.ndm_mntfrom, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
277 if (!nd->nd_private.ndm_mntfrom) {
278 printf("nfs_boot: can't allocate private host buffer\n");
279 error = ENOMEM;
280 goto failed;
281 }
282 error = bp_getfile(&bp_sin, "private",
283 &nd->nd_private.ndm_saddr,
284 nd->nd_private.ndm_host,
285 nd->nd_private.ndm_path);
286 if (!error) {
287 char * check_path = NULL;
288
289 MALLOC_ZONE(check_path, char *, MAXPATHLEN, M_NAMEI, M_WAITOK);
290 if (!check_path) {
291 printf("nfs_boot: can't allocate check_path buffer\n");
292 error = ENOMEM;
293 goto failed;
294 }
295 snprintf(check_path, MAXPATHLEN, "%s/private", nd->nd_root.ndm_path);
296 if ((nd->nd_root.ndm_saddr.sin_addr.s_addr
297 == nd->nd_private.ndm_saddr.sin_addr.s_addr)
298 && (strncmp(check_path, nd->nd_private.ndm_path, MAXPATHLEN) == 0)) {
299 /* private path is prefix of root path, don't mount */
300 nd->nd_private.ndm_saddr.sin_addr.s_addr = 0;
301 }
302 FREE_ZONE(check_path, MAXPATHLEN, M_NAMEI);
303 } else {
304 /* private key not defined, don't mount */
305 nd->nd_private.ndm_saddr.sin_addr.s_addr = 0;
306 }
307 } else {
308 error = 0;
309 }
310 #endif /* NO_MOUNT_PRIVATE */
311 failed:
312 return error;
313 }
314
315 /*
316 * Called with a partially initialized nfs_diskless struct
317 * with file handles to be filled in.
318 */
319 int
320 nfs_boot_getfh(struct nfs_diskless *nd, int v3, int sotype)
321 {
322 int error = 0;
323
324 nd->nd_root.ndm_nfsv3 = v3;
325 nd->nd_root.ndm_sotype = sotype;
326 error = get_file_handle(&nd->nd_root);
327 if (error) {
328 printf("nfs_boot: get_file_handle(v%d) root failed, %d\n",
329 v3 ? 3 : 2, error);
330 goto failed;
331 }
332
333 #if !defined(NO_MOUNT_PRIVATE)
334 if (nd->nd_private.ndm_saddr.sin_addr.s_addr) {
335 /* get private file handle */
336 nd->nd_private.ndm_nfsv3 = v3;
337 nd->nd_private.ndm_sotype = sotype;
338 error = get_file_handle(&nd->nd_private);
339 if (error) {
340 printf("nfs_boot: get_file_handle(v%d) private failed, %d\n",
341 v3 ? 3 : 2, error);
342 goto failed;
343 }
344 }
345 #endif /* NO_MOUNT_PRIVATE */
346 failed:
347 return error;
348 }
349
350 static int
351 get_file_handle(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
366 /* Construct remote path (for getmntinfo(3)) */
367 dp = ndmntp->ndm_mntfrom;
368 endp = dp + MAXPATHLEN - 1;
369 for (sp = ndmntp->ndm_host; *sp && dp < endp;) {
370 *dp++ = *sp++;
371 }
372 if (dp < endp) {
373 *dp++ = ':';
374 }
375 for (sp = ndmntp->ndm_path; *sp && dp < endp;) {
376 *dp++ = *sp++;
377 }
378 *dp = '\0';
379 return 0;
380 }
381
382
383 /*
384 * Get an mbuf with the given length, and
385 * initialize the pkthdr length field.
386 */
387 static int
388 mbuf_get_with_len(size_t msg_len, mbuf_t *m)
389 {
390 int error;
391 error = mbuf_gethdr(MBUF_WAITOK, MBUF_TYPE_DATA, m);
392 if (error) {
393 return error;
394 }
395 if (msg_len > mbuf_maxlen(*m)) {
396 error = mbuf_mclget(MBUF_WAITOK, MBUF_TYPE_DATA, m);
397 if (error) {
398 mbuf_freem(*m);
399 return error;
400 }
401 if (msg_len > mbuf_maxlen(*m)) {
402 panic("nfs_boot: msg_len > MCLBYTES");
403 }
404 }
405 mbuf_setlen(*m, msg_len);
406 mbuf_pkthdr_setlen(*m, msg_len);
407 return 0;
408 }
409
410
411 /*
412 * String representation for RPC.
413 */
414 struct rpc_string {
415 u_int32_t len; /* length without null or padding */
416 u_char data[4]; /* data (longer, of course) */
417 /* data is padded to a long-word boundary */
418 };
419 /* Compute space used given string length. */
420 #define RPC_STR_SIZE(slen) (4 + ((slen + 3) & ~3))
421
422 /*
423 * Inet address in RPC messages
424 * (Note, really four 32-bit ints, NOT chars. Blech.)
425 */
426 struct bp_inaddr {
427 u_int32_t atype;
428 int32_t addr[4];
429 };
430
431
432 /*
433 * RPC: bootparam/whoami
434 * Given client IP address, get:
435 * client name (hostname)
436 * domain name (domainname)
437 * gateway address
438 *
439 * The hostname and domainname are set here for convenience.
440 *
441 * Note - bpsin is initialized to the broadcast address,
442 * and will be replaced with the bootparam server address
443 * after this call is complete. Have to use PMAP_PROC_CALL
444 * to make sure we get responses only from a servers that
445 * know about us (don't want to broadcast a getport call).
446 */
447 static int
448 bp_whoami(struct sockaddr_in *bpsin,
449 struct in_addr *my_ip,
450 struct in_addr *gw_ip)
451 {
452 /* RPC structures for PMAPPROC_CALLIT */
453 struct whoami_call {
454 u_int32_t call_prog;
455 u_int32_t call_vers;
456 u_int32_t call_proc;
457 u_int32_t call_arglen;
458 struct bp_inaddr call_ia;
459 } *call;
460
461 struct rpc_string *str;
462 struct bp_inaddr *bia;
463 mbuf_t m;
464 struct sockaddr_in sin;
465 int error;
466 size_t msg_len, cn_len, dn_len;
467 u_char *p;
468 int32_t *lp;
469 size_t encapsulated_size;
470
471 /*
472 * Get message buffer of sufficient size.
473 */
474 msg_len = sizeof(*call);
475 error = mbuf_get_with_len(msg_len, &m);
476 if (error) {
477 return error;
478 }
479
480 /*
481 * Build request message for PMAPPROC_CALLIT.
482 */
483 call = mbuf_data(m);
484 call->call_prog = htonl(BOOTPARAM_PROG);
485 call->call_vers = htonl(BOOTPARAM_VERS);
486 call->call_proc = htonl(BOOTPARAM_WHOAMI);
487 call->call_arglen = htonl(sizeof(struct bp_inaddr));
488
489 /* client IP address */
490 call->call_ia.atype = htonl(1);
491 p = (u_char*)my_ip;
492 lp = call->call_ia.addr;
493 *lp++ = htonl(*p); p++;
494 *lp++ = htonl(*p); p++;
495 *lp++ = htonl(*p); p++;
496 *lp++ = htonl(*p); p++;
497
498 /* RPC: portmap/callit */
499 bpsin->sin_port = htons(PMAPPORT);
500
501 error = krpc_call(bpsin, SOCK_DGRAM, PMAPPROG, PMAPVERS, PMAPPROC_CALLIT, &m, &sin);
502 if (error) {
503 return error;
504 }
505
506 /*
507 * Parse result message.
508 */
509 msg_len = mbuf_len(m);
510 lp = mbuf_data(m);
511
512 /* bootparam server port (also grab from address). */
513 if (msg_len < sizeof(*lp)) {
514 goto bad;
515 }
516 msg_len -= sizeof(*lp);
517 bpsin->sin_port = htons((short)ntohl(*lp++));
518 bpsin->sin_addr.s_addr = sin.sin_addr.s_addr;
519
520 /* length of encapsulated results */
521 if (os_add_overflow((size_t) ntohl(*lp), sizeof(*lp), &encapsulated_size)
522 || msg_len < encapsulated_size) {
523 goto bad;
524 }
525 msg_len = ntohl(*lp++);
526 p = (u_char*)lp;
527
528 /* client name */
529 if (msg_len < sizeof(*str)) {
530 goto bad;
531 }
532 str = (struct rpc_string *)p;
533 cn_len = ntohl(str->len);
534 if ((msg_len - 4) < cn_len) {
535 goto bad;
536 }
537 if (cn_len >= MAXHOSTNAMELEN) {
538 goto bad;
539 }
540 bcopy(str->data, hostname, cn_len);
541 hostname[cn_len] = '\0';
542 hostnamelen = cn_len;
543 p += RPC_STR_SIZE(cn_len);
544 msg_len -= RPC_STR_SIZE(cn_len);
545
546 /* domain name */
547 if (msg_len < sizeof(*str)) {
548 goto bad;
549 }
550 str = (struct rpc_string *)p;
551 dn_len = ntohl(str->len);
552 if ((msg_len - 4) < dn_len) {
553 goto bad;
554 }
555 if (dn_len >= MAXHOSTNAMELEN) {
556 goto bad;
557 }
558 bcopy(str->data, domainname, dn_len);
559 domainname[dn_len] = '\0';
560 domainnamelen = dn_len;
561 p += RPC_STR_SIZE(dn_len);
562 msg_len -= RPC_STR_SIZE(dn_len);
563
564 /* gateway address */
565 if (msg_len < sizeof(*bia)) {
566 goto bad;
567 }
568 bia = (struct bp_inaddr *)p;
569 if (bia->atype != htonl(1)) {
570 goto bad;
571 }
572 p = (u_char*)gw_ip;
573 *p++ = ntohl(bia->addr[0]);
574 *p++ = ntohl(bia->addr[1]);
575 *p++ = ntohl(bia->addr[2]);
576 *p++ = ntohl(bia->addr[3]);
577 goto out;
578
579 bad:
580 printf("nfs_boot: bootparam_whoami: bad reply\n");
581 error = EBADRPC;
582
583 out:
584 mbuf_freem(m);
585 return error;
586 }
587
588
589 /*
590 * RPC: bootparam/getfile
591 * Given client name and file "key", get:
592 * server name
593 * server IP address
594 * server pathname
595 */
596 static int
597 bp_getfile(struct sockaddr_in *bpsin,
598 const char *key,
599 struct sockaddr_in *md_sin,
600 char *serv_name,
601 char *pathname)
602 {
603 struct rpc_string *str;
604 mbuf_t m;
605 struct bp_inaddr *bia;
606 struct sockaddr_in *sin;
607 u_char *p, *q;
608 int error;
609 size_t msg_len, cn_len, key_len, sn_len, path_len;
610
611 /*
612 * Get message buffer of sufficient size.
613 */
614 cn_len = hostnamelen;
615 key_len = strlen(key);
616 msg_len = 0;
617 msg_len += RPC_STR_SIZE(cn_len);
618 msg_len += RPC_STR_SIZE(key_len);
619 error = mbuf_get_with_len(msg_len, &m);
620 if (error) {
621 return error;
622 }
623
624 /*
625 * Build request message.
626 */
627 p = mbuf_data(m);
628 bzero(p, msg_len);
629 /* client name (hostname) */
630 str = (struct rpc_string *)p;
631 str->len = htonl(cn_len);
632 bcopy(hostname, str->data, cn_len);
633 p += RPC_STR_SIZE(cn_len);
634 /* key name (root or swap) */
635 str = (struct rpc_string *)p;
636 str->len = htonl(key_len);
637 bcopy(key, str->data, key_len);
638
639 /* RPC: bootparam/getfile */
640 error = krpc_call(bpsin, SOCK_DGRAM, BOOTPARAM_PROG, BOOTPARAM_VERS,
641 BOOTPARAM_GETFILE, &m, NULL);
642 if (error) {
643 return error;
644 }
645
646 /*
647 * Parse result message.
648 */
649 p = mbuf_data(m);
650 msg_len = mbuf_len(m);
651
652 /* server name */
653 if (msg_len < sizeof(*str)) {
654 goto bad;
655 }
656 str = (struct rpc_string *)p;
657 sn_len = ntohl(str->len);
658 if ((msg_len - 4) < sn_len) {
659 goto bad;
660 }
661 if (sn_len >= MAXHOSTNAMELEN) {
662 goto bad;
663 }
664 bcopy(str->data, serv_name, sn_len);
665 serv_name[sn_len] = '\0';
666 p += RPC_STR_SIZE(sn_len);
667 msg_len -= RPC_STR_SIZE(sn_len);
668
669 /* server IP address (mountd) */
670 if (msg_len < sizeof(*bia)) {
671 goto bad;
672 }
673 bia = (struct bp_inaddr *)p;
674 if (bia->atype != htonl(1)) {
675 goto bad;
676 }
677 sin = md_sin;
678 bzero((caddr_t)sin, sizeof(*sin));
679 sin->sin_len = sizeof(*sin);
680 sin->sin_family = AF_INET;
681 q = (u_char*) &sin->sin_addr;
682 *q++ = ntohl(bia->addr[0]);
683 *q++ = ntohl(bia->addr[1]);
684 *q++ = ntohl(bia->addr[2]);
685 *q++ = ntohl(bia->addr[3]);
686 p += sizeof(*bia);
687 msg_len -= sizeof(*bia);
688
689 /* server pathname */
690 if (msg_len < sizeof(*str)) {
691 goto bad;
692 }
693 str = (struct rpc_string *)p;
694 path_len = ntohl(str->len);
695 if ((msg_len - 4) < path_len) {
696 goto bad;
697 }
698 if (path_len >= MAXPATHLEN) {
699 goto bad;
700 }
701 bcopy(str->data, pathname, path_len);
702 pathname[path_len] = '\0';
703 goto out;
704
705 bad:
706 printf("nfs_boot: bootparam_getfile: bad reply\n");
707 error = EBADRPC;
708
709 out:
710 mbuf_freem(m);
711 return 0;
712 }
713
714
715 /*
716 * RPC: mountd/mount
717 * Given a server pathname, get an NFS file handle.
718 * Also, sets sin->sin_port to the NFS service port.
719 */
720 static int
721 md_mount(struct sockaddr_in *mdsin, /* mountd server address */
722 char *path,
723 int v3,
724 int sotype,
725 u_char *fhp,
726 u_int32_t *fhlenp)
727 {
728 /* The RPC structures */
729 struct rpc_string *str;
730 struct rdata {
731 u_int32_t errno;
732 u_char data[NFSX_V3FHMAX + sizeof(u_int32_t)];
733 } *rdata;
734 mbuf_t m;
735 size_t mlen;
736 int error, slen;
737 int mntversion = v3 ? RPCMNT_VER3 : RPCMNT_VER1;
738 int proto = (sotype == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP;
739 in_port_t mntport, nfsport;
740
741 /* Get port number for MOUNTD. */
742 error = krpc_portmap(mdsin, RPCPROG_MNT, mntversion, proto, &mntport);
743 if (error) {
744 return error;
745 }
746
747 /* Get port number for NFS use. */
748 /* (If NFS/proto unavailable, don't bother with the mount call) */
749 error = krpc_portmap(mdsin, NFS_PROG, v3 ? NFS_VER3 : NFS_VER2, proto, &nfsport);
750 if (error) {
751 return error;
752 }
753
754 /* Set port number for MOUNTD */
755 mdsin->sin_port = mntport;
756
757 slen = strlen(path);
758 mlen = RPC_STR_SIZE(slen);
759
760 error = mbuf_get_with_len(mlen, &m);
761 if (error) {
762 return error;
763 }
764 str = mbuf_data(m);
765 str->len = htonl(slen);
766 bcopy(path, str->data, slen);
767
768 /* Do RPC to mountd. */
769 error = krpc_call(mdsin, sotype, RPCPROG_MNT, mntversion, RPCMNT_MOUNT, &m, NULL);
770 if (error) {
771 return error; /* message already freed */
772 }
773 /*
774 * the reply must be long enough to hold the errno plus either of:
775 * + a v2 filehandle
776 * + a v3 filehandle length + a v3 filehandle
777 */
778 mlen = mbuf_len(m);
779 if (mlen < sizeof(u_int32_t)) {
780 goto bad;
781 }
782 rdata = mbuf_data(m);
783 error = ntohl(rdata->errno);
784 if (error) {
785 goto out;
786 }
787 if (v3) {
788 u_int32_t fhlen;
789 u_char *fh;
790 if (mlen < sizeof(u_int32_t) * 2) {
791 goto bad;
792 }
793 fhlen = ntohl(*(u_int32_t*)rdata->data);
794 fh = rdata->data + sizeof(u_int32_t);
795 if (mlen < (sizeof(u_int32_t) * 2 + fhlen)
796 || fhlen >= (NFSX_V3FHMAX + sizeof(u_int32_t))) {
797 goto bad;
798 }
799 bcopy(fh, fhp, fhlen);
800 *fhlenp = fhlen;
801 } else {
802 if (mlen < (sizeof(u_int32_t) + NFSX_V2FH)) {
803 goto bad;
804 }
805 bcopy(rdata->data, fhp, NFSX_V2FH);
806 *fhlenp = NFSX_V2FH;
807 }
808
809 /* Set port number for NFS use. */
810 mdsin->sin_port = nfsport;
811 goto out;
812
813 bad:
814 error = EBADRPC;
815
816 out:
817 mbuf_freem(m);
818 return error;
819 }
820
821 #endif /* NETHER */