1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /* Copyright (c) 1988 AT&T */
28 /* All Rights Reserved */
29
30 #pragma ident "%Z%%M% %I% %E% SMI"
31
32 #include <sys/utsname.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <rpc/types.h>
37 #include <rpc/auth.h>
38 #include <sys/t_lock.h>
39 #include <netdb.h>
40 #include "clnt.h"
41 #include <rpc/xdr.h>
42 #include <rpc/rpc_msg.h>
43 #include <rpc/rpc.h>
44 #include "brpc.h"
45 #include "auth_inet.h"
46 #include "pmap.h"
47 #include <rpcsvc/nfs_prot.h>
48 #include <rpcsvc/nfs4_prot.h>
49 #include "nfs_inet.h"
50 #include <rpcsvc/bootparam.h>
51 #include <dhcp_impl.h>
52 #include <rpcsvc/mount.h>
53 #include <sys/promif.h>
54 #include <sys/salib.h>
55 #include "socket_inet.h"
56 #include "ipv4.h"
57 #include "mac.h"
58 #include <sys/bootdebug.h>
59 #include <errno.h>
60 #include "dhcpv4.h"
61 #include <sys/mntent.h>
62
63 /* ARP timeout in milliseconds for BOOTP/RARP */
64 #define ARP_INETBOOT_TIMEOUT 1000
65
66 struct nfs_file roothandle; /* root file handle */
67 static char root_hostname[SYS_NMLN]; /* server hostname */
68 static char my_hostname[MAXHOSTNAMELEN];
69 static char root_pathbuf[NFS_MAXPATHLEN]; /* the root's path */
70 static char root_boot_file[NFS_MAXPATHLEN]; /* optional boot file */
71 static struct sockaddr_in root_to; /* server sock ip */
72 /* in network order */
73 CLIENT *root_CLIENT = NULL; /* CLIENT handle */
74 int dontroute = FALSE; /* In case rarp/bootparams was selected */
75 char rootopts[MAX_PATH_LEN];
76 static gid_t fake_gids = 1; /* fake gids list for auth_unix */
77
78 extern void set_default_filename(char *); /* boot.c */
79
80 /*
81 * xdr routines used by mount.
82 */
83
84 bool_t
xdr_fhstatus(XDR * xdrs,struct fhstatus * fhsp)85 xdr_fhstatus(XDR *xdrs, struct fhstatus *fhsp)
86 {
87 if (!xdr_int(xdrs, (int *)&fhsp->fhs_status))
88 return (FALSE);
89 if (fhsp->fhs_status == 0) {
90 return (xdr_fhandle(xdrs, fhsp->fhstatus_u.fhs_fhandle));
91 }
92 return (TRUE);
93 }
94
95 bool_t
xdr_fhandle(XDR * xdrs,fhandle fhp)96 xdr_fhandle(XDR *xdrs, fhandle fhp)
97 {
98 return (xdr_opaque(xdrs, (char *)fhp, NFS_FHSIZE));
99 }
100
101 bool_t
xdr_path(XDR * xdrs,char ** pathp)102 xdr_path(XDR *xdrs, char **pathp)
103 {
104 return (xdr_string(xdrs, pathp, MNTPATHLEN));
105 }
106
107 bool_t
xdr_fhandle3(XDR * xdrs,fhandle3 * objp)108 xdr_fhandle3(XDR *xdrs, fhandle3 *objp)
109 {
110 return (xdr_bytes(xdrs, (char **)&objp->fhandle3_val,
111 (uint_t *)&objp->fhandle3_len, FHSIZE3));
112 }
113
114 bool_t
xdr_mountstat3(XDR * xdrs,mountstat3 * objp)115 xdr_mountstat3(XDR *xdrs, mountstat3 *objp)
116 {
117 return (xdr_enum(xdrs, (enum_t *)objp));
118 }
119
120 bool_t
xdr_mountres3_ok(XDR * xdrs,mountres3_ok * objp)121 xdr_mountres3_ok(XDR *xdrs, mountres3_ok *objp)
122 {
123 if (!xdr_fhandle3(xdrs, &objp->fhandle))
124 return (FALSE);
125 return (xdr_array(xdrs, (char **)&objp->auth_flavors.auth_flavors_val,
126 (uint_t *)&objp->auth_flavors.auth_flavors_len, ~0,
127 sizeof (int), (xdrproc_t)xdr_int));
128 }
129
130 bool_t
xdr_mountres3(XDR * xdrs,mountres3 * objp)131 xdr_mountres3(XDR *xdrs, mountres3 *objp)
132 {
133 if (!xdr_mountstat3(xdrs, &objp->fhs_status))
134 return (FALSE);
135 if (objp->fhs_status == MNT_OK)
136 return (xdr_mountres3_ok(xdrs, &objp->mountres3_u.mountinfo));
137 return (TRUE);
138 }
139
140 static int
nfsmountroot(char * path,struct nfs_file * filep)141 nfsmountroot(char *path, struct nfs_file *filep)
142 {
143 int rexmit;
144 int resp_wait;
145 enum clnt_stat status;
146 struct fhstatus root_tmp; /* to pass to rpc/xdr */
147
148 /*
149 * Wait up to 16 secs for first response, retransmitting expon.
150 */
151 rexmit = 0; /* default retransmission interval */
152 resp_wait = 16;
153
154 do {
155 status = brpc_call((rpcprog_t)MOUNTPROG, (rpcvers_t)MOUNTVERS,
156 (rpcproc_t)MOUNTPROC_MNT, xdr_path, (caddr_t)&path,
157 xdr_fhstatus, (caddr_t)&(root_tmp), rexmit, resp_wait,
158 &root_to, NULL, AUTH_UNIX);
159 if (status == RPC_TIMEDOUT) {
160 dprintf("boot: %s:%s mount server not responding.\n",
161 root_hostname, path);
162 }
163 rexmit = resp_wait;
164 resp_wait = 0; /* use default wait time. */
165 } while (status == RPC_TIMEDOUT);
166
167 if ((status != RPC_SUCCESS) || (root_tmp.fhs_status != 0)) {
168 nfs_error(root_tmp.fhs_status);
169 root_to.sin_port = 0;
170 return (-1);
171 }
172
173 /*
174 * Since the mount succeeded, we'll mark the filep's
175 * status as NFS_OK, and its type as NFDIR. If these
176 * points aren't the case, then we wouldn't be here.
177 */
178 bcopy(&root_tmp.fhstatus_u.fhs_fhandle, &filep->fh.fh2, FHSIZE);
179 filep->ftype.type2 = NFDIR;
180 filep->version = NFS_VERSION;
181 nfs_readsize = nfs_readsize < NFS_MAXDATA ? nfs_readsize : NFS_MAXDATA;
182 /*
183 * Set a reasonable lower limit on readsize
184 */
185 nfs_readsize = (nfs_readsize != 0 && nfs_readsize < 512) ?
186 512 : nfs_readsize;
187 return (0);
188 }
189
190 int
setup_root_vars(void)191 setup_root_vars(void)
192 {
193 size_t buflen;
194 uint16_t readsize;
195
196 /*
197 * Root server name. Required.
198 */
199 buflen = sizeof (root_hostname);
200 if (dhcp_getinfo(DSYM_VENDOR, VS_NFSMNT_ROOTSRVR_NAME, 0,
201 root_hostname, &buflen)) {
202 root_hostname[buflen] = '\0';
203 } else {
204 dprintf("BOUND: Missing Root Server Name Option\n");
205 errno = EINVAL;
206 return (-1);
207 }
208
209 /*
210 * Root server IP. Required.
211 */
212 buflen = sizeof (root_to.sin_addr);
213 if (!dhcp_getinfo(DSYM_VENDOR, VS_NFSMNT_ROOTSRVR_IP, 0,
214 &root_to.sin_addr, &buflen)) {
215 dprintf("BOUND: Missing Root Server IP Option\n");
216 errno = EINVAL;
217 return (-1);
218 }
219
220 /*
221 * Root path Required.
222 */
223 buflen = sizeof (root_pathbuf);
224 if (dhcp_getinfo(DSYM_VENDOR, VS_NFSMNT_ROOTPATH, 0,
225 root_pathbuf, &buflen)) {
226 root_pathbuf[buflen] = '\0';
227 } else {
228 dprintf("BOUND: Missing Root Path Option\n");
229 errno = EINVAL;
230 return (-1);
231 }
232
233 /*
234 * Optional Bootfile path.
235 */
236 buflen = sizeof (root_boot_file);
237 if (dhcp_getinfo(DSYM_VENDOR, VS_NFSMNT_BOOTFILE, 0,
238 root_boot_file, &buflen)) {
239 root_boot_file[buflen] = '\0';
240 dprintf("BOUND: Optional Boot File is: %s\n", root_boot_file);
241 }
242
243 /* if we got a boot file name, use it as the default */
244 if (root_boot_file[0] != '\0')
245 set_default_filename(root_boot_file);
246
247 /*
248 * Set the NFS read size. The mount code will adjust it to
249 * the maximum size.
250 */
251 buflen = sizeof (readsize);
252 if (dhcp_getinfo(DSYM_VENDOR, VS_BOOT_NFS_READSIZE, 0,
253 &readsize, &buflen)) {
254 nfs_readsize = ntohs(readsize);
255 if (boothowto & RB_VERBOSE) {
256 printf("Boot NFS read size: %d\n", nfs_readsize);
257 }
258 }
259
260 /*
261 * Optional rootopts.
262 */
263 buflen = sizeof (rootopts);
264 if (dhcp_getinfo(DSYM_VENDOR, VS_NFSMNT_ROOTOPTS, 0,
265 rootopts, &buflen)) {
266 rootopts[buflen] = '\0';
267 dprintf("BOUND: Optional Rootopts is: %s\n", rootopts);
268 }
269
270 return (0);
271 }
272
273 static void
mnt3_error(enum mountstat3 status)274 mnt3_error(enum mountstat3 status)
275 {
276 if (!(boothowto & RB_DEBUG))
277 return;
278
279 switch (status) {
280 case MNT_OK:
281 printf("Mount: No error.\n");
282 break;
283 case MNT3ERR_PERM:
284 printf("Mount: Not owner.\n");
285 break;
286 case MNT3ERR_NOENT:
287 printf("Mount: No such file or directory.\n");
288 break;
289 case MNT3ERR_IO:
290 printf("Mount: I/O error.\n");
291 break;
292 case MNT3ERR_ACCES:
293 printf("Mount: Permission denied.\n");
294 break;
295 case MNT3ERR_NOTDIR:
296 printf("Mount: Not a directory.\n");
297 break;
298 case MNT3ERR_INVAL:
299 printf("Mount: Invalid argument.\n");
300 break;
301 case MNT3ERR_NAMETOOLONG:
302 printf("Mount: File name too long.\n");
303 break;
304 case MNT3ERR_NOTSUPP:
305 printf("Mount: Operation not supported.\n");
306 break;
307 case MNT3ERR_SERVERFAULT:
308 printf("Mount: Server fault.\n");
309 break;
310 default:
311 printf("Mount: unknown error.\n");
312 break;
313 }
314 }
315
316 static int
nfs3mountroot(char * path,struct nfs_file * filep)317 nfs3mountroot(char *path, struct nfs_file *filep)
318 {
319 int rexmit;
320 int resp_wait;
321 struct mountres3 res3;
322 enum clnt_stat status;
323
324 /*
325 * Wait up to 16 secs for first response, retransmitting expon.
326 */
327 rexmit = 0; /* default retransmission interval */
328 resp_wait = 16;
329
330 /*
331 * Try to mount using V3
332 */
333 do {
334 bzero(&res3, sizeof (struct mountres3));
335
336 status = brpc_call((rpcprog_t)MOUNTPROG, (rpcvers_t)MOUNTVERS3,
337 (rpcproc_t)MOUNTPROC_MNT, xdr_path, (caddr_t)&path,
338 xdr_mountres3, (caddr_t)&res3, rexmit, resp_wait,
339 &root_to, NULL, AUTH_UNIX);
340
341 if (status != RPC_TIMEDOUT)
342 break;
343
344 dprintf("boot: %s:%s mount server not responding.\n",
345 root_hostname, path);
346
347 rexmit = resp_wait;
348 resp_wait = 0; /* use default wait time. */
349
350 xdr_free(xdr_mountres3, (caddr_t)&res3);
351 } while (status == RPC_TIMEDOUT);
352
353 if ((status != RPC_SUCCESS) || (res3.fhs_status != MNT_OK)) {
354 mnt3_error(res3.fhs_status);
355 root_to.sin_port = 0;
356 return (-1);
357 }
358
359 /*
360 * Since the mount succeeded, we'll mark the filep's
361 * status as NFS_OK, and its type as NF3DIR. If these
362 * points aren't the case, then we wouldn't be here.
363 */
364 filep->fh.fh3.len = res3.mountres3_u.mountinfo.fhandle.fhandle3_len;
365 bcopy(res3.mountres3_u.mountinfo.fhandle.fhandle3_val,
366 filep->fh.fh3.data,
367 filep->fh.fh3.len);
368 filep->ftype.type3 = NF3DIR;
369 filep->version = NFS_V3;
370 /*
371 * Hardwire in a known reasonable upper limit of 32K
372 */
373 nfs_readsize = nfs_readsize < 32 * 1024 ? nfs_readsize : 32 * 1024;
374 /*
375 * Set a reasonable lower limit on readsize
376 */
377 nfs_readsize = (nfs_readsize != 0 && nfs_readsize < 512) ?
378 512 : nfs_readsize;
379 xdr_free(xdr_mountres3, (caddr_t)&res3);
380 return (0);
381 }
382
383 /*
384 * Setup v4 client for inetboot
385 */
386 static int
nfs4init(char * path,uint16_t nfs_port)387 nfs4init(char *path, uint16_t nfs_port)
388 {
389 struct timeval wait;
390 int fd = -1;
391 int error = 0;
392 enum clnt_stat rpc_stat;
393 struct nfs_file rootpath;
394
395 wait.tv_sec = RPC_RCVWAIT_MSEC / 1000;
396 wait.tv_usec = 0;
397
398 /*
399 * If we haven't explicitly set the port number, set to the standard
400 * 2049 and don't cause a rpcbind request.
401 */
402 if (nfs_port == 0)
403 nfs_port = 2049;
404
405 root_to.sin_port = htons(nfs_port);
406
407 /*
408 * Support TCP only
409 */
410 root_CLIENT = clntbtcp_create(&root_to, NFS_PROGRAM,
411 NFS_V4, wait, &fd,
412 NFS4BUF_SIZE, NFS4BUF_SIZE);
413
414 if (root_CLIENT == NULL) {
415 root_to.sin_port = 0;
416 return (-1);
417 }
418
419 root_CLIENT->cl_auth =
420 authunix_create(my_hostname, 0, 1, 1, &fake_gids);
421
422 /*
423 * Send NULL proc the server first to see if V4 exists
424 */
425 rpc_stat = CLNT_CALL(root_CLIENT, NFSPROC4_NULL, xdr_void, NULL,
426 xdr_void, NULL, wait);
427
428 if (rpc_stat != RPC_SUCCESS) {
429 dprintf("boot: NULL proc failed NFSv4 service not available\n");
430 AUTH_DESTROY(root_CLIENT->cl_auth);
431 CLNT_DESTROY(root_CLIENT);
432 root_to.sin_port = 0;
433 return (-1);
434 }
435
436 /*
437 * Do a lookup to get to the root_path. This is nice since it can
438 * handle multicomponent lookups.
439 */
440 roothandle.version = NFS_V4;
441 roothandle.ftype.type4 = NF4DIR;
442 roothandle.fh.fh4.len = 0; /* Force a PUTROOTFH */
443 roothandle.offset = (uint_t)0; /* it's a directory! */
444 error = lookup(path, &rootpath, TRUE);
445
446 if (error) {
447 printf("boot: lookup %s failed\n", path);
448 return (-1);
449 }
450 roothandle = rootpath; /* structure copy */
451
452 /*
453 * Hardwire in a known reasonable upper limit of 32K
454 */
455 nfs_readsize = nfs_readsize < 32 * 1024 ? nfs_readsize : 32 * 1024;
456 /*
457 * Set a reasonable lower limit on readsize
458 */
459 nfs_readsize = (nfs_readsize != 0 && nfs_readsize < 512) ?
460 512 : nfs_readsize;
461
462 return (0);
463 }
464
465 static int
atoi(const char * p)466 atoi(const char *p)
467 {
468 int n;
469 int c, neg = 0;
470
471 if (!isdigit(c = *p)) {
472 while (c == ' ' || c == '\t' || c == '\n')
473 c = *++p;
474 switch (c) {
475 case '-':
476 neg++;
477 /* FALLTHROUGH */
478 case '+':
479 c = *++p;
480 }
481 if (!isdigit(c))
482 return (0);
483 }
484 for (n = '0' - c; isdigit(c = *++p); ) {
485 n *= 10; /* two steps to avoid unnecessary overflow */
486 n += '0' - c; /* accum neg to avoid surprises at MAX */
487 }
488 return (neg ? n : -n);
489 }
490
491 /*
492 * Parse suboptions from a string.
493 * Same as getsubopt(3C).
494 */
495 static int
getsubopt(char ** optionsp,char * const * tokens,char ** valuep)496 getsubopt(char **optionsp, char * const *tokens, char **valuep)
497 {
498 char *s = *optionsp, *p;
499 int i;
500 size_t optlen;
501
502 *valuep = NULL;
503 if (*s == '\0')
504 return (-1);
505 p = strchr(s, ','); /* find next option */
506 if (p == NULL) {
507 p = s + strlen(s);
508 } else {
509 *p++ = '\0'; /* mark end and point to next */
510 }
511 *optionsp = p; /* point to next option */
512 p = strchr(s, '='); /* find value */
513 if (p == NULL) {
514 optlen = strlen(s);
515 *valuep = NULL;
516 } else {
517 optlen = p - s;
518 *valuep = ++p;
519 }
520 for (i = 0; tokens[i] != NULL; i++) {
521 if ((optlen == strlen(tokens[i])) &&
522 (strncmp(s, tokens[i], optlen) == 0))
523 return (i);
524 }
525 /* no match, point value at option and return error */
526 *valuep = s;
527 return (-1);
528 }
529
530 /*
531 * The only interesting NFS mount options for initiating the kernel
532 * all others are ignored.
533 */
534 static char *optlist[] = {
535 #define OPT_RSIZE 0
536 MNTOPT_RSIZE,
537 #define OPT_TIMEO 1
538 MNTOPT_TIMEO,
539 #define OPT_VERS 2
540 MNTOPT_VERS,
541 #define OPT_PROTO 3
542 MNTOPT_PROTO,
543 #define OPT_PORT 4
544 MNTOPT_PORT,
545 NULL
546 };
547
548 /*
549 * This routine will open a device as it is known by the V2 OBP. It
550 * then goes thru the stuff necessary to initialize the network device,
551 * get our network parameters, (using DHCP or rarp/bootparams), and
552 * finally actually go and get the root filehandle. Sound like fun?
553 * Suuurrrree. Take a look.
554 *
555 * Returns 0 if things worked. -1 if we crashed and burned.
556 */
557 int
boot_nfs_mountroot(char * str)558 boot_nfs_mountroot(char *str)
559 {
560 int status;
561 enum clnt_stat rpc_stat;
562 char *root_path = &root_pathbuf[0]; /* to make XDR happy */
563 struct timeval wait;
564 int fd;
565 int bufsize;
566 char *opts, *val;
567 int nfs_version = 0;
568 int istcp = 1;
569 int nfs_port = 0; /* Cause pmap to get port */
570 struct sockaddr_in tmp_addr; /* throw away */
571
572 if (root_CLIENT != NULL) {
573 AUTH_DESTROY(root_CLIENT->cl_auth);
574 CLNT_DESTROY(root_CLIENT);
575 root_CLIENT = NULL;
576 }
577
578 root_to.sin_family = AF_INET;
579 root_to.sin_addr.s_addr = htonl(INADDR_ANY);
580 root_to.sin_port = htons(0);
581
582 mac_init(str);
583
584 (void) ipv4_setpromiscuous(TRUE);
585
586 if (get_netconfig_strategy() == NCT_BOOTP_DHCP) {
587 if (boothowto & RB_VERBOSE)
588 printf("Using BOOTP/DHCP...\n");
589 if (dhcp() != 0 || setup_root_vars() != 0) {
590 (void) ipv4_setpromiscuous(FALSE);
591 if (boothowto & RB_VERBOSE)
592 printf("BOOTP/DHCP configuration failed!\n");
593 return (-1);
594 }
595
596 /* now that we have an IP address, turn off promiscuous mode */
597 (void) ipv4_setpromiscuous(FALSE);
598 } else {
599 /* Use RARP/BOOTPARAMS. RARP will try forever... */
600 if (boothowto & RB_VERBOSE)
601 printf("Using RARP/BOOTPARAMS...\n");
602 mac_call_rarp();
603
604 /*
605 * Since there is no way to determine our netmask, and therefore
606 * figure out if the router we got is useful, we assume all
607 * services are local. Use DHCP if this bothers you.
608 */
609 dontroute = TRUE;
610 /*
611 * We are trying to keep the ARP response
612 * timeout on the lower side with BOOTP/RARP.
613 * We are doing this for BOOTP/RARP where policy
614 * doesn't allow to route the packets outside
615 * the subnet as it has no idea about the
616 * netmask. By doing so, we are reducing
617 * ARP response timeout for any packet destined
618 * for outside booting clients subnet. Client can
619 * not expect such ARP replies and will finally
620 * timeout after a long delay. This would cause
621 * booting client to get stalled for a longer
622 * time. We can not avoid accepting any outside
623 * subnet packets accidentally destined for the
624 * booting client.
625 */
626 mac_set_arp_timeout(ARP_INETBOOT_TIMEOUT);
627
628 /* now that we have an IP address, turn off promiscuous mode */
629 (void) ipv4_setpromiscuous(FALSE);
630
631 /* get our hostname */
632 if (whoami() == FALSE)
633 return (-1);
634
635 /* get our bootparams. */
636 if (getfile("root", root_hostname, &root_to.sin_addr,
637 root_pathbuf) == FALSE)
638 return (-1);
639
640 /* get our rootopts. */
641 (void) getfile("rootopts", root_hostname, &tmp_addr.sin_addr,
642 rootopts);
643 }
644
645 /* mount root */
646 if (boothowto & RB_VERBOSE) {
647 printf("root server: %s (%s)\n", root_hostname,
648 inet_ntoa(root_to.sin_addr));
649 printf("root directory: %s\n", root_pathbuf);
650 }
651
652 /*
653 * Assumes we've configured the stack and thus know our
654 * IP address/hostname, either by using DHCP or rarp/bootparams.
655 */
656 gethostname(my_hostname, sizeof (my_hostname));
657
658 wait.tv_sec = RPC_RCVWAIT_MSEC / 1000;
659 wait.tv_usec = 0;
660
661 /*
662 * Parse out the interesting root options, if an invalid
663 * or unknown option is provided, silently ignore it and
664 * use the defaults.
665 */
666 opts = rootopts;
667 while (*opts) {
668 int ival;
669 switch (getsubopt(&opts, optlist, &val)) {
670 case OPT_RSIZE:
671 if (val == NULL || !isdigit(*val))
672 break;
673 nfs_readsize = atoi(val);
674 break;
675 case OPT_TIMEO:
676 if (val == NULL || !isdigit(*val))
677 break;
678 ival = atoi(val);
679 wait.tv_sec = ival / 10;
680 wait.tv_usec = (ival % 10) * 100000;
681 break;
682 case OPT_VERS:
683 if (val == NULL || !isdigit(*val))
684 break;
685 nfs_version = atoi(val);
686 break;
687 case OPT_PROTO:
688 if (val == NULL || isdigit(*val))
689 break;
690 if ((strncmp(val, "udp", 3) == 0))
691 istcp = 0;
692 else
693 istcp = 1; /* must be tcp */
694 break;
695 case OPT_PORT:
696 if (val == NULL || !isdigit(*val))
697 break;
698 nfs_port = atoi(val);
699
700 /*
701 * Currently nfs_dlinet.c doesn't support setting
702 * the root NFS port. Delete this when it does.
703 */
704 nfs_port = 0;
705 break;
706 default:
707 /*
708 * Unknown options are silently ignored
709 */
710 break;
711 }
712 }
713
714 /*
715 * If version is set, then try that version first.
716 */
717 switch (nfs_version) {
718 case NFS_VERSION:
719 if (nfsmountroot(root_path, &roothandle) == 0)
720 goto domount;
721 break;
722 case NFS_V3:
723 if (nfs3mountroot(root_path, &roothandle) == 0)
724 goto domount;
725 break;
726 case NFS_V4:
727 /*
728 * With v4 we skip the mount and go straight to
729 * setting the root filehandle. Because of this we
730 * do things slightly differently and obtain our
731 * client handle first.
732 */
733 if (istcp && nfs4init(root_path, nfs_port) == 0) {
734 /*
735 * If v4 init succeeded then we are done. Just return.
736 */
737 return (0);
738 }
739 }
740
741 /*
742 * If there was no chosen version or the chosen version failed
743 * try all versions in order, this may still fail to boot
744 * at the kernel level if the options are not right, but be
745 * generous at this early stage.
746 */
747 if (istcp && nfs4init(root_path, nfs_port) == 0) {
748 /*
749 * If v4 init succeeded then we are done. Just return.
750 */
751 return (0);
752 }
753
754 if (nfs3mountroot(root_path, &roothandle) == 0)
755 goto domount;
756
757 if ((status = nfsmountroot(root_path, &roothandle)) != 0)
758 return (status);
759
760 domount:
761 /*
762 * Only v2 and v3 go on from here.
763 */
764 roothandle.offset = (uint_t)0; /* it's a directory! */
765 root_to.sin_port = htons(nfs_port); /* NFS is next after mount */
766
767 /*
768 * Create the CLIENT handle for NFS operations
769 */
770 if (roothandle.version == NFS_VERSION)
771 bufsize = NFSBUF_SIZE;
772 else
773 bufsize = NFS3BUF_SIZE;
774
775 /*
776 * First try TCP then UDP (unless UDP asked for explicitly), if mountd
777 * alows this version but neither transport is available we are stuck.
778 */
779 if (istcp) {
780 fd = -1;
781 root_CLIENT = clntbtcp_create(&root_to, NFS_PROGRAM,
782 roothandle.version, wait, &fd, bufsize, bufsize);
783 if (root_CLIENT != NULL) {
784 root_CLIENT->cl_auth =
785 authunix_create(my_hostname, 0, 1, 1, &fake_gids);
786 /*
787 * Send NULL proc, check if the server really exists
788 */
789 rpc_stat = CLNT_CALL(root_CLIENT, 0,
790 xdr_void, NULL, xdr_void, NULL, wait);
791
792 if (rpc_stat == RPC_SUCCESS)
793 return (0);
794
795 AUTH_DESTROY(root_CLIENT->cl_auth);
796 CLNT_DESTROY(root_CLIENT);
797 root_CLIENT = NULL;
798 }
799 /* Fall through to UDP case */
800 }
801
802 fd = -1;
803 root_CLIENT = clntbudp_bufcreate(&root_to, NFS_PROGRAM,
804 roothandle.version, wait, &fd, bufsize, bufsize);
805 if (root_CLIENT == NULL)
806 return (-1);
807
808 root_CLIENT->cl_auth =
809 authunix_create(my_hostname, 0, 1, 1, &fake_gids);
810 /*
811 * Send NULL proc, check if the server really exists
812 */
813 rpc_stat = CLNT_CALL(root_CLIENT, 0,
814 xdr_void, NULL, xdr_void, NULL, wait);
815
816 if (rpc_stat == RPC_SUCCESS)
817 return (0);
818
819 AUTH_DESTROY(root_CLIENT->cl_auth);
820 CLNT_DESTROY(root_CLIENT);
821 root_CLIENT = NULL;
822 return (-1);
823 }
824