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 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 96 xdr_fhandle(XDR *xdrs, fhandle fhp) 97 { 98 return (xdr_opaque(xdrs, (char *)fhp, NFS_FHSIZE)); 99 } 100 101 bool_t 102 xdr_path(XDR *xdrs, char **pathp) 103 { 104 return (xdr_string(xdrs, pathp, MNTPATHLEN)); 105 } 106 107 bool_t 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 115 xdr_mountstat3(XDR *xdrs, mountstat3 *objp) 116 { 117 return (xdr_enum(xdrs, (enum_t *)objp)); 118 } 119 120 bool_t 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 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 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 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 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 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 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 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 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 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