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