1 /* $NetBSD: srvr_nfs.c,v 1.1.1.2 2009/03/20 20:26:50 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2009 Erez Zadok 5 * Copyright (c) 1990 Jan-Simon Pendry 6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 7 * Copyright (c) 1990 The Regents of the University of California. 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * Jan-Simon Pendry at Imperial College, London. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. All advertising materials mentioning features or use of this software 22 * must display the following acknowledgment: 23 * This product includes software developed by the University of 24 * California, Berkeley and its contributors. 25 * 4. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * 42 * File: am-utils/amd/srvr_nfs.c 43 * 44 */ 45 46 /* 47 * NFS server modeling 48 */ 49 50 #ifdef HAVE_CONFIG_H 51 # include <config.h> 52 #endif /* HAVE_CONFIG_H */ 53 #include <am_defs.h> 54 #include <amd.h> 55 56 /* 57 * Number of pings allowed to fail before host is declared down 58 * - three-fifths of the allowed mount time... 59 */ 60 #define MAX_ALLOWED_PINGS (3 + /* for luck ... */ 1) 61 62 /* 63 * How often to ping when starting a new server 64 */ 65 #define FAST_NFS_PING 3 66 67 #if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME 68 # error: sanity check failed in srvr_nfs.c 69 /* 70 * you cannot do things this way... 71 * sufficient fast pings must be given the chance to fail 72 * within the allowed mount time 73 */ 74 #endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */ 75 76 /* structures and typedefs */ 77 typedef struct nfs_private { 78 u_short np_mountd; /* Mount daemon port number */ 79 char np_mountd_inval; /* Port *may* be invalid */ 80 int np_ping; /* Number of failed ping attempts */ 81 time_t np_ttl; /* Time when server is thought dead */ 82 int np_xid; /* RPC transaction id for pings */ 83 int np_error; /* Error during portmap request */ 84 } nfs_private; 85 86 /* globals */ 87 qelem nfs_srvr_list = {&nfs_srvr_list, &nfs_srvr_list}; 88 89 /* statics */ 90 static int global_xid; /* For NFS pings */ 91 #define XID_ALLOC() (++global_xid) 92 93 #ifdef HAVE_FS_NFS3 94 # define NUM_NFS_VERS 2 95 #else /* not HAVE_FS_NFS3 */ 96 # define NUM_NFS_VERS 1 97 #endif /* not HAVE_FS_NFS3 */ 98 static int ping_len[NUM_NFS_VERS]; 99 static char ping_buf[NUM_NFS_VERS][sizeof(struct rpc_msg) + 32]; 100 101 #if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) 102 /* 103 * Protocols we know about, in order of preference. 104 * 105 * Note that Solaris 8 and newer NetBSD systems are switching to UDP first, 106 * so this order may have to be adjusted for Amd in the future once more 107 * vendors make that change. -Erez 11/24/2000 108 * 109 * Or we might simply make this is a platform-specific order. -Ion 09/13/2003 110 */ 111 static char *protocols[] = { "tcp", "udp", NULL }; 112 #endif /* defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */ 113 114 /* forward definitions */ 115 static void nfs_keepalive(voidp); 116 117 118 /* 119 * Flush cached data for an fserver (or for all, if fs==NULL) 120 */ 121 void 122 flush_srvr_nfs_cache(fserver *fs) 123 { 124 fserver *fs2 = NULL; 125 126 ITER(fs2, fserver, &nfs_srvr_list) { 127 if (fs == NULL || fs == fs2) { 128 nfs_private *np = (nfs_private *) fs2->fs_private; 129 if (np) { 130 np->np_mountd_inval = TRUE; 131 np->np_error = -1; 132 } 133 } 134 } 135 } 136 137 138 /* 139 * Startup the NFS ping for a particular version. 140 */ 141 static void 142 create_ping_payload(u_long nfs_version) 143 { 144 XDR ping_xdr; 145 struct rpc_msg ping_msg; 146 147 /* 148 * Non nfs mounts like /afs/glue.umd.edu have ended up here. 149 */ 150 if (nfs_version == 0) { 151 nfs_version = NFS_VERSION; 152 plog(XLOG_WARNING, "create_ping_payload: nfs_version = 0, changed to 2"); 153 } else 154 plog(XLOG_INFO, "create_ping_payload: nfs_version: %d", (int) nfs_version); 155 156 rpc_msg_init(&ping_msg, NFS_PROGRAM, nfs_version, NFSPROC_NULL); 157 158 /* 159 * Create an XDR endpoint 160 */ 161 xdrmem_create(&ping_xdr, ping_buf[nfs_version - NFS_VERSION], sizeof(ping_buf[0]), XDR_ENCODE); 162 163 /* 164 * Create the NFS ping message 165 */ 166 if (!xdr_callmsg(&ping_xdr, &ping_msg)) { 167 plog(XLOG_ERROR, "Couldn't create ping RPC message"); 168 going_down(3); 169 } 170 /* 171 * Find out how long it is 172 */ 173 ping_len[nfs_version - NFS_VERSION] = xdr_getpos(&ping_xdr); 174 175 /* 176 * Destroy the XDR endpoint - we don't need it anymore 177 */ 178 xdr_destroy(&ping_xdr); 179 } 180 181 182 /* 183 * Called when a portmap reply arrives 184 */ 185 static void 186 got_portmap(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, voidp idv, int done) 187 { 188 fserver *fs2 = (fserver *) idv; 189 fserver *fs = NULL; 190 191 /* 192 * Find which fileserver we are talking about 193 */ 194 ITER(fs, fserver, &nfs_srvr_list) 195 if (fs == fs2) 196 break; 197 198 if (fs == fs2) { 199 u_long port = 0; /* XXX - should be short but protocol is naff */ 200 int error = done ? pickup_rpc_reply(pkt, len, (voidp) &port, (XDRPROC_T_TYPE) xdr_u_long) : -1; 201 nfs_private *np = (nfs_private *) fs->fs_private; 202 203 if (!error && port) { 204 dlog("got port (%d) for mountd on %s", (int) port, fs->fs_host); 205 /* 206 * Grab the port number. Portmap sends back 207 * an u_long in native ordering, so it 208 * needs converting to a u_short in 209 * network ordering. 210 */ 211 np->np_mountd = htons((u_short) port); 212 np->np_mountd_inval = FALSE; 213 np->np_error = 0; 214 } else { 215 dlog("Error fetching port for mountd on %s", fs->fs_host); 216 dlog("\t error=%d, port=%d", error, (int) port); 217 /* 218 * Almost certainly no mountd running on remote host 219 */ 220 np->np_error = error ? error : ETIMEDOUT; 221 } 222 223 if (fs->fs_flags & FSF_WANT) 224 wakeup_srvr(fs); 225 } else if (done) { 226 dlog("Got portmap for old port request"); 227 } else { 228 dlog("portmap request timed out"); 229 } 230 } 231 232 233 /* 234 * Obtain portmap information 235 */ 236 static int 237 call_portmap(fserver *fs, AUTH *auth, u_long prog, u_long vers, u_long prot) 238 { 239 struct rpc_msg pmap_msg; 240 int len; 241 char iobuf[UDPMSGSIZE]; 242 int error; 243 struct pmap pmap; 244 245 rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, PMAPPROC_NULL); 246 pmap.pm_prog = prog; 247 pmap.pm_vers = vers; 248 pmap.pm_prot = prot; 249 pmap.pm_port = 0; 250 len = make_rpc_packet(iobuf, 251 sizeof(iobuf), 252 PMAPPROC_GETPORT, 253 &pmap_msg, 254 (voidp) &pmap, 255 (XDRPROC_T_TYPE) xdr_pmap, 256 auth); 257 if (len > 0) { 258 struct sockaddr_in sin; 259 memset((voidp) &sin, 0, sizeof(sin)); 260 sin = *fs->fs_ip; 261 sin.sin_port = htons(PMAPPORT); 262 error = fwd_packet(RPC_XID_PORTMAP, iobuf, len, 263 &sin, &sin, (voidp) fs, got_portmap); 264 } else { 265 error = -len; 266 } 267 268 return error; 269 } 270 271 272 static void 273 recompute_portmap(fserver *fs) 274 { 275 int error; 276 u_long mnt_version; 277 278 /* 279 * No portmap calls for pure WebNFS servers. 280 */ 281 if (fs->fs_flags & FSF_WEBNFS) 282 return; 283 284 if (nfs_auth) 285 error = 0; 286 else 287 error = make_nfs_auth(); 288 289 if (error) { 290 nfs_private *np = (nfs_private *) fs->fs_private; 291 np->np_error = error; 292 return; 293 } 294 295 if (fs->fs_version == 0) 296 plog(XLOG_WARNING, "recompute_portmap: nfs_version = 0 fixed"); 297 298 plog(XLOG_INFO, "recompute_portmap: NFS version %d on %s", 299 (int) fs->fs_version, fs->fs_host); 300 #ifdef HAVE_FS_NFS3 301 if (fs->fs_version == NFS_VERSION3) 302 mnt_version = AM_MOUNTVERS3; 303 else 304 #endif /* HAVE_FS_NFS3 */ 305 mnt_version = MOUNTVERS; 306 307 plog(XLOG_INFO, "Using MOUNT version: %d", (int) mnt_version); 308 call_portmap(fs, nfs_auth, MOUNTPROG, mnt_version, (u_long) IPPROTO_UDP); 309 } 310 311 312 int 313 get_mountd_port(fserver *fs, u_short *port, wchan_t wchan) 314 { 315 int error = -1; 316 if (FSRV_ISDOWN(fs)) 317 return EWOULDBLOCK; 318 319 if (FSRV_ISUP(fs)) { 320 nfs_private *np = (nfs_private *) fs->fs_private; 321 if (np->np_error == 0) { 322 *port = np->np_mountd; 323 error = 0; 324 } else { 325 error = np->np_error; 326 } 327 /* 328 * Now go get the port mapping again in case it changed. 329 * Note that it is used even if (np_mountd_inval) 330 * is True. The flag is used simply as an 331 * indication that the mountd may be invalid, not 332 * that it is known to be invalid. 333 */ 334 if (np->np_mountd_inval) 335 recompute_portmap(fs); 336 else 337 np->np_mountd_inval = TRUE; 338 } 339 if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) { 340 /* 341 * If a wait channel is supplied, and no 342 * error has yet occurred, then arrange 343 * that a wakeup is done on the wait channel, 344 * whenever a wakeup is done on this fs node. 345 * Wakeup's are done on the fs node whenever 346 * it changes state - thus causing control to 347 * come back here and new, better things to happen. 348 */ 349 fs->fs_flags |= FSF_WANT; 350 sched_task(wakeup_task, wchan, (wchan_t) fs); 351 } 352 return error; 353 } 354 355 356 /* 357 * This is called when we get a reply to an RPC ping. 358 * The value of id was taken from the nfs_private 359 * structure when the ping was transmitted. 360 */ 361 static void 362 nfs_keepalive_callback(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, voidp idv, int done) 363 { 364 int xid = (long) idv; /* cast needed for 64-bit archs */ 365 fserver *fs; 366 int found_map = 0; 367 368 if (!done) 369 return; 370 371 /* 372 * For each node... 373 */ 374 ITER(fs, fserver, &nfs_srvr_list) { 375 nfs_private *np = (nfs_private *) fs->fs_private; 376 if (np->np_xid == xid && (fs->fs_flags & FSF_PINGING)) { 377 /* 378 * Reset the ping counter. 379 * Update the keepalive timer. 380 * Log what happened. 381 */ 382 if (fs->fs_flags & FSF_DOWN) { 383 fs->fs_flags &= ~FSF_DOWN; 384 if (fs->fs_flags & FSF_VALID) { 385 srvrlog(fs, "is up"); 386 } else { 387 if (np->np_ping > 1) 388 srvrlog(fs, "ok"); 389 else 390 srvrlog(fs, "starts up"); 391 fs->fs_flags |= FSF_VALID; 392 } 393 394 map_flush_srvr(fs); 395 } else { 396 if (fs->fs_flags & FSF_VALID) { 397 dlog("file server %s type nfs is still up", fs->fs_host); 398 } else { 399 if (np->np_ping > 1) 400 srvrlog(fs, "ok"); 401 fs->fs_flags |= FSF_VALID; 402 } 403 } 404 405 /* 406 * Adjust ping interval 407 */ 408 untimeout(fs->fs_cid); 409 fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs); 410 411 /* 412 * Update ttl for this server 413 */ 414 np->np_ttl = clocktime(NULL) + 415 (MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1; 416 417 /* 418 * New RPC xid... 419 */ 420 np->np_xid = XID_ALLOC(); 421 422 /* 423 * Failed pings is zero... 424 */ 425 np->np_ping = 0; 426 427 /* 428 * Recompute portmap information if not known 429 */ 430 if (np->np_mountd_inval) 431 recompute_portmap(fs); 432 433 found_map++; 434 break; 435 } 436 } 437 438 if (found_map == 0) 439 dlog("Spurious ping packet"); 440 } 441 442 443 static void 444 check_fs_addr_change(fserver *fs) 445 { 446 struct hostent *hp = NULL; 447 struct in_addr ia; 448 char *old_ipaddr, *new_ipaddr; 449 450 hp = gethostbyname(fs->fs_host); 451 if (!hp || 452 hp->h_addrtype != AF_INET || 453 !STREQ((char *) hp->h_name, fs->fs_host) || 454 memcmp((voidp) &fs->fs_ip->sin_addr, 455 (voidp) hp->h_addr, 456 sizeof(fs->fs_ip->sin_addr)) == 0) 457 return; 458 /* if got here: downed server changed IP address */ 459 old_ipaddr = strdup(inet_ntoa(fs->fs_ip->sin_addr)); 460 memmove((voidp) &ia, (voidp) hp->h_addr, sizeof(struct in_addr)); 461 new_ipaddr = inet_ntoa(ia); /* ntoa uses static buf */ 462 plog(XLOG_WARNING, "EZK: down fileserver %s changed ip: %s -> %s", 463 fs->fs_host, old_ipaddr, new_ipaddr); 464 XFREE(old_ipaddr); 465 /* copy new IP addr */ 466 memmove((voidp) &fs->fs_ip->sin_addr, 467 (voidp) hp->h_addr, 468 sizeof(fs->fs_ip->sin_addr)); 469 /* XXX: do we need to un/set these flags? */ 470 fs->fs_flags &= ~FSF_DOWN; 471 fs->fs_flags |= FSF_VALID | FSF_WANT; 472 map_flush_srvr(fs); /* XXX: a race with flush_srvr_nfs_cache? */ 473 flush_srvr_nfs_cache(fs); 474 fs->fs_flags |= FSF_FORCE_UNMOUNT; 475 476 #if 0 477 flush_nfs_fhandle_cache(fs); /* done in caller: nfs_keepalive_timeout */ 478 /* XXX: need to purge nfs_private so that somehow it will get re-initialized? */ 479 #endif /* 0 */ 480 } 481 482 483 /* 484 * Called when no ping-reply received 485 */ 486 static void 487 nfs_keepalive_timeout(voidp v) 488 { 489 fserver *fs = v; 490 nfs_private *np = (nfs_private *) fs->fs_private; 491 492 /* 493 * Another ping has failed 494 */ 495 np->np_ping++; 496 if (np->np_ping > 1) 497 srvrlog(fs, "not responding"); 498 499 /* 500 * Not known to be up any longer 501 */ 502 if (FSRV_ISUP(fs)) 503 fs->fs_flags &= ~FSF_VALID; 504 505 /* 506 * If ttl has expired then guess that it is dead 507 */ 508 if (np->np_ttl < clocktime(NULL)) { 509 int oflags = fs->fs_flags; 510 dlog("ttl has expired"); 511 if ((fs->fs_flags & FSF_DOWN) == 0) { 512 /* 513 * Server was up, but is now down. 514 */ 515 srvrlog(fs, "is down"); 516 fs->fs_flags |= FSF_DOWN | FSF_VALID; 517 /* 518 * Since the server is down, the portmap 519 * information may now be wrong, so it 520 * must be flushed from the local cache 521 */ 522 flush_nfs_fhandle_cache(fs); 523 np->np_error = -1; 524 check_fs_addr_change(fs); /* check if IP addr of fserver changed */ 525 } else { 526 /* 527 * Known to be down 528 */ 529 if ((fs->fs_flags & FSF_VALID) == 0) 530 srvrlog(fs, "starts down"); 531 fs->fs_flags |= FSF_VALID; 532 } 533 if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT)) 534 wakeup_srvr(fs); 535 /* 536 * Reset failed ping count 537 */ 538 np->np_ping = 0; 539 } else { 540 if (np->np_ping > 1) 541 dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS); 542 } 543 544 /* 545 * New RPC xid, so any late responses to the previous ping 546 * get ignored... 547 */ 548 np->np_xid = XID_ALLOC(); 549 550 /* 551 * Run keepalive again 552 */ 553 nfs_keepalive(fs); 554 } 555 556 557 /* 558 * Keep track of whether a server is alive 559 */ 560 static void 561 nfs_keepalive(voidp v) 562 { 563 fserver *fs = v; 564 int error; 565 nfs_private *np = (nfs_private *) fs->fs_private; 566 int fstimeo = -1; 567 568 /* 569 * Send an NFS ping to this node 570 */ 571 572 if (ping_len[fs->fs_version - NFS_VERSION] == 0) 573 create_ping_payload(fs->fs_version); 574 575 /* 576 * Queue the packet... 577 */ 578 error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid), 579 ping_buf[fs->fs_version - NFS_VERSION], 580 ping_len[fs->fs_version - NFS_VERSION], 581 fs->fs_ip, 582 (struct sockaddr_in *) NULL, 583 (voidp) ((long) np->np_xid), /* cast needed for 64-bit archs */ 584 nfs_keepalive_callback); 585 586 /* 587 * See if a hard error occurred 588 */ 589 switch (error) { 590 case ENETDOWN: 591 case ENETUNREACH: 592 case EHOSTDOWN: 593 case EHOSTUNREACH: 594 np->np_ping = MAX_ALLOWED_PINGS; /* immediately down */ 595 np->np_ttl = (time_t) 0; 596 /* 597 * This causes an immediate call to nfs_keepalive_timeout 598 * whenever the server was thought to be up. 599 * See +++ below. 600 */ 601 fstimeo = 0; 602 break; 603 604 case 0: 605 dlog("Sent NFS ping to %s", fs->fs_host); 606 break; 607 } 608 609 /* 610 * Back off the ping interval if we are not getting replies and 611 * the remote system is known to be down. 612 */ 613 switch (fs->fs_flags & (FSF_DOWN | FSF_VALID)) { 614 case FSF_VALID: /* Up */ 615 if (fstimeo < 0) /* +++ see above */ 616 fstimeo = FAST_NFS_PING; 617 break; 618 619 case FSF_VALID | FSF_DOWN: /* Down */ 620 fstimeo = fs->fs_pinger; 621 break; 622 623 default: /* Unknown */ 624 fstimeo = FAST_NFS_PING; 625 break; 626 } 627 628 dlog("NFS timeout in %d seconds", fstimeo); 629 630 fs->fs_cid = timeout(fstimeo, nfs_keepalive_timeout, (voidp) fs); 631 } 632 633 634 static void 635 start_nfs_pings(fserver *fs, int pingval) 636 { 637 if (pingval == 0) /* could be because ping mnt option not found */ 638 pingval = AM_PINGER; 639 /* if pings haven't been initalized, then init them for first time */ 640 if (fs->fs_flags & FSF_PING_UNINIT) { 641 fs->fs_flags &= ~FSF_PING_UNINIT; 642 plog(XLOG_INFO, "initializing %s's pinger to %d sec", fs->fs_host, pingval); 643 goto do_pings; 644 } 645 646 if ((fs->fs_flags & FSF_PINGING) && fs->fs_pinger == pingval) { 647 dlog("already running pings to %s", fs->fs_host); 648 return; 649 } 650 651 /* if got here, then we need to update the ping value */ 652 plog(XLOG_INFO, "changing %s's ping value from %d%s to %d%s", 653 fs->fs_host, 654 fs->fs_pinger, (fs->fs_pinger < 0 ? " (off)" : ""), 655 pingval, (pingval < 0 ? " (off)" : "")); 656 do_pings: 657 fs->fs_pinger = pingval; 658 659 if (fs->fs_cid) 660 untimeout(fs->fs_cid); 661 if (pingval < 0) { 662 srvrlog(fs, "wired up (pings disabled)"); 663 fs->fs_flags |= FSF_VALID; 664 fs->fs_flags &= ~FSF_DOWN; 665 } else { 666 fs->fs_flags |= FSF_PINGING; 667 nfs_keepalive(fs); 668 } 669 } 670 671 672 /* 673 * Find an nfs server for a host. 674 */ 675 fserver * 676 find_nfs_srvr(mntfs *mf) 677 { 678 char *host = mf->mf_fo->opt_rhost; 679 fserver *fs; 680 int pingval; 681 mntent_t mnt; 682 nfs_private *np; 683 struct hostent *hp = NULL; 684 struct sockaddr_in *ip = NULL; 685 u_long nfs_version = 0; /* default is no version specified */ 686 u_long best_nfs_version = 0; 687 char *nfs_proto = NULL; /* no IP protocol either */ 688 int nfs_port = 0; 689 int nfs_port_opt = 0; 690 int fserver_is_down = 0; 691 692 /* 693 * Get ping interval from mount options. 694 * Current only used to decide whether pings 695 * are required or not. < 0 = no pings. 696 */ 697 mnt.mnt_opts = mf->mf_mopts; 698 pingval = hasmntval(&mnt, "ping"); 699 700 if (mf->mf_flags & MFF_NFS_SCALEDOWN) { 701 /* 702 * the server granted us a filehandle, but we were unable to mount it. 703 * therefore, scale down to NFSv2/UDP and try again. 704 */ 705 nfs_version = NFS_VERSION; 706 nfs_proto = "udp"; 707 plog(XLOG_WARNING, "find_nfs_srvr: NFS mount failed, trying again with NFSv2/UDP"); 708 mf->mf_flags &= ~MFF_NFS_SCALEDOWN; 709 } else { 710 /* 711 * Get the NFS version from the mount options. This is used 712 * to decide the highest NFS version to try. 713 */ 714 #ifdef MNTTAB_OPT_VERS 715 nfs_version = hasmntval(&mnt, MNTTAB_OPT_VERS); 716 #endif /* MNTTAB_OPT_VERS */ 717 718 #ifdef MNTTAB_OPT_PROTO 719 { 720 char *proto_opt = hasmnteq(&mnt, MNTTAB_OPT_PROTO); 721 if (proto_opt) { 722 char **p; 723 for (p = protocols; *p; p++) 724 if (NSTREQ(proto_opt, *p, strlen(*p))) { 725 nfs_proto = *p; 726 break; 727 } 728 if (*p == NULL) 729 plog(XLOG_WARNING, "ignoring unknown protocol option for %s:%s", 730 host, mf->mf_fo->opt_rfs); 731 } 732 } 733 #endif /* MNTTAB_OPT_PROTO */ 734 735 #ifdef HAVE_NFS_NFSV2_H 736 /* allow overriding if nfsv2 option is specified in mount options */ 737 if (amu_hasmntopt(&mnt, "nfsv2")) { 738 nfs_version = NFS_VERSION;/* nullify any ``vers=X'' statements */ 739 nfs_proto = "udp"; /* nullify any ``proto=tcp'' statements */ 740 plog(XLOG_WARNING, "found compatibility option \"nfsv2\": set options vers=2,proto=udp for host %s", host); 741 } 742 #endif /* HAVE_NFS_NFSV2_H */ 743 744 /* check if we've globally overridden the NFS version/protocol */ 745 if (gopt.nfs_vers) { 746 nfs_version = gopt.nfs_vers; 747 plog(XLOG_INFO, "find_nfs_srvr: force NFS version to %d", 748 (int) nfs_version); 749 } 750 if (gopt.nfs_proto) { 751 nfs_proto = gopt.nfs_proto; 752 plog(XLOG_INFO, "find_nfs_srvr: force NFS protocol transport to %s", nfs_proto); 753 } 754 } 755 756 /* 757 * lookup host address and canonical name 758 */ 759 hp = gethostbyname(host); 760 761 /* 762 * New code from Bob Harris <harris@basil-rathbone.mit.edu> 763 * Use canonical name to keep track of file server 764 * information. This way aliases do not generate 765 * multiple NFS pingers. (Except when we're normalizing 766 * hosts.) 767 */ 768 if (hp && !(gopt.flags & CFM_NORMALIZE_HOSTNAMES)) 769 host = (char *) hp->h_name; 770 771 if (hp) { 772 switch (hp->h_addrtype) { 773 case AF_INET: 774 ip = ALLOC(struct sockaddr_in); 775 memset((voidp) ip, 0, sizeof(*ip)); 776 /* as per POSIX, sin_len need not be set (used internally by kernel) */ 777 ip->sin_family = AF_INET; 778 memmove((voidp) &ip->sin_addr, (voidp) hp->h_addr, sizeof(ip->sin_addr)); 779 break; 780 781 default: 782 plog(XLOG_USER, "No IP address for host %s", host); 783 goto no_dns; 784 } 785 } else { 786 plog(XLOG_USER, "Unknown host: %s", host); 787 goto no_dns; 788 } 789 790 /* 791 * This may not be the best way to do things, but it really doesn't make 792 * sense to query a file server which is marked as 'down' for any 793 * version/proto combination. 794 */ 795 ITER(fs, fserver, &nfs_srvr_list) { 796 if (FSRV_ISDOWN(fs) && 797 STREQ(host, fs->fs_host)) { 798 plog(XLOG_WARNING, "fileserver %s is already hung - not running NFS proto/version discovery", host); 799 fs->fs_refc++; 800 if (ip) 801 XFREE(ip); 802 return fs; 803 } 804 } 805 806 /* 807 * Get the NFS Version, and verify server is up. 808 * If the client only supports NFSv2, hardcode it but still try to 809 * contact the remote portmapper to see if the service is running. 810 */ 811 #ifndef HAVE_FS_NFS3 812 nfs_version = NFS_VERSION; 813 nfs_proto = "udp"; 814 plog(XLOG_INFO, "The client supports only NFS(2,udp)"); 815 #endif /* not HAVE_FS_NFS3 */ 816 817 818 if (amu_hasmntopt(&mnt, MNTTAB_OPT_PUBLIC)) { 819 /* 820 * Use WebNFS to obtain file handles. 821 */ 822 mf->mf_flags |= MFF_WEBNFS; 823 plog(XLOG_INFO, "%s option used, NOT contacting the portmapper on %s", 824 MNTTAB_OPT_PUBLIC, host); 825 /* 826 * Prefer NFSv3/tcp if the client supports it (cf. RFC 2054, 7). 827 */ 828 if (!nfs_version) { 829 #ifdef HAVE_FS_NFS3 830 nfs_version = NFS_VERSION3; 831 #else /* not HAVE_FS_NFS3 */ 832 nfs_version = NFS_VERSION; 833 #endif /* not HAVE_FS_NFS3 */ 834 plog(XLOG_INFO, "No NFS version specified, will use NFSv%d", 835 (int) nfs_version); 836 } 837 if (!nfs_proto) { 838 #if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) 839 nfs_proto = "tcp"; 840 #else /* not defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */ 841 nfs_proto = "udp"; 842 #endif /* not defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */ 843 plog(XLOG_INFO, "No NFS protocol transport specified, will use %s", 844 nfs_proto); 845 } 846 } else { 847 /* 848 * Find the best combination of NFS version and protocol. 849 * When given a choice, use the highest available version, 850 * and use TCP over UDP if available. 851 */ 852 if (check_pmap_up(host, ip)) { 853 if (nfs_proto) { 854 best_nfs_version = get_nfs_version(host, ip, nfs_version, nfs_proto); 855 nfs_port = ip->sin_port; 856 } 857 #ifdef MNTTAB_OPT_PROTO 858 else { 859 u_int proto_nfs_version; 860 char **p; 861 862 for (p = protocols; *p; p++) { 863 proto_nfs_version = get_nfs_version(host, ip, nfs_version, *p); 864 865 if (proto_nfs_version > best_nfs_version) { 866 best_nfs_version = proto_nfs_version; 867 nfs_proto = *p; 868 nfs_port = ip->sin_port; 869 } 870 } 871 } 872 #endif /* MNTTAB_OPT_PROTO */ 873 } else { 874 plog(XLOG_INFO, "portmapper service not running on %s", host); 875 } 876 877 /* use the portmapper results only nfs_version is not set yet */ 878 if (!best_nfs_version) { 879 /* 880 * If the NFS server is down or does not support the portmapper call 881 * (such as certain Novell NFS servers) we mark it as version 2 and we 882 * let the nfs code deal with the case when it is down. If/when the 883 * server comes back up and it can support NFSv3 and/or TCP, it will 884 * use those. 885 */ 886 if (nfs_version == 0) { 887 nfs_version = NFS_VERSION; 888 nfs_proto = "udp"; 889 } 890 plog(XLOG_INFO, "NFS service not running on %s", host); 891 fserver_is_down = 1; 892 } else { 893 if (nfs_version == 0) 894 nfs_version = best_nfs_version; 895 plog(XLOG_INFO, "Using NFS version %d, protocol %s on host %s", 896 (int) nfs_version, nfs_proto, host); 897 } 898 } 899 900 /* 901 * Determine the NFS port. 902 * 903 * A valid "port" mount option overrides anything else. 904 * If the port has been determined from the portmapper, use that. 905 * Default to NFS_PORT otherwise (cf. RFC 2054, 3). 906 */ 907 nfs_port_opt = hasmntval(&mnt, MNTTAB_OPT_PORT); 908 if (nfs_port_opt > 0) 909 nfs_port = htons(nfs_port_opt); 910 if (!nfs_port) 911 nfs_port = htons(NFS_PORT); 912 913 dlog("find_nfs_srvr: using port %d for nfs on %s", 914 (int) ntohs(nfs_port), host); 915 ip->sin_port = nfs_port; 916 917 no_dns: 918 /* 919 * Try to find an existing fs server structure for this host. 920 * Note that differing versions or protocols have their own structures. 921 * XXX: Need to fix the ping mechanism to actually use the NFS protocol 922 * chosen here (right now it always uses datagram sockets). 923 */ 924 ITER(fs, fserver, &nfs_srvr_list) { 925 if (STREQ(host, fs->fs_host) && 926 nfs_version == fs->fs_version && 927 STREQ(nfs_proto, fs->fs_proto)) { 928 /* 929 * fill in the IP address -- this is only needed 930 * if there is a chance an IP address will change 931 * between mounts. 932 * Mike Mitchell, mcm@unx.sas.com, 09/08/93 933 */ 934 if (hp && fs->fs_ip && 935 memcmp((voidp) &fs->fs_ip->sin_addr, 936 (voidp) hp->h_addr, 937 sizeof(fs->fs_ip->sin_addr)) != 0) { 938 struct in_addr ia; 939 char *old_ipaddr, *new_ipaddr; 940 old_ipaddr = strdup(inet_ntoa(fs->fs_ip->sin_addr)); 941 memmove((voidp) &ia, (voidp) hp->h_addr, sizeof(struct in_addr)); 942 new_ipaddr = inet_ntoa(ia); /* ntoa uses static buf */ 943 plog(XLOG_WARNING, "fileserver %s changed ip: %s -> %s", 944 fs->fs_host, old_ipaddr, new_ipaddr); 945 XFREE(old_ipaddr); 946 flush_nfs_fhandle_cache(fs); 947 memmove((voidp) &fs->fs_ip->sin_addr, (voidp) hp->h_addr, sizeof(fs->fs_ip->sin_addr)); 948 } 949 950 /* 951 * If the new file systems doesn't use WebNFS, the nfs pings may 952 * try to contact the portmapper. 953 */ 954 if (!(mf->mf_flags & MFF_WEBNFS)) 955 fs->fs_flags &= ~FSF_WEBNFS; 956 957 /* check if pingval needs to be updated/set/reset */ 958 start_nfs_pings(fs, pingval); 959 960 /* 961 * Following if statement from Mike Mitchell <mcm@unx.sas.com> 962 * Initialize the ping data if we aren't pinging now. The np_ttl and 963 * np_ping fields are especially important. 964 */ 965 if (!(fs->fs_flags & FSF_PINGING)) { 966 np = (nfs_private *) fs->fs_private; 967 np->np_mountd_inval = TRUE; 968 np->np_xid = XID_ALLOC(); 969 np->np_error = -1; 970 np->np_ping = 0; 971 /* 972 * Initially the server will be deemed dead 973 * after MAX_ALLOWED_PINGS of the fast variety 974 * have failed. 975 */ 976 np->np_ttl = MAX_ALLOWED_PINGS * FAST_NFS_PING + clocktime(NULL) - 1; 977 start_nfs_pings(fs, pingval); 978 if (fserver_is_down) 979 fs->fs_flags |= FSF_VALID | FSF_DOWN; 980 } 981 982 fs->fs_refc++; 983 if (ip) 984 XFREE(ip); 985 return fs; 986 } 987 } 988 989 /* 990 * Get here if we can't find an entry 991 */ 992 993 /* 994 * Allocate a new server 995 */ 996 fs = ALLOC(struct fserver); 997 fs->fs_refc = 1; 998 fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname"); 999 if (gopt.flags & CFM_NORMALIZE_HOSTNAMES) 1000 host_normalize(&fs->fs_host); 1001 fs->fs_ip = ip; 1002 fs->fs_cid = 0; 1003 if (ip) { 1004 fs->fs_flags = FSF_DOWN; /* Starts off down */ 1005 } else { 1006 fs->fs_flags = FSF_ERROR | FSF_VALID; 1007 mf->mf_flags |= MFF_ERROR; 1008 mf->mf_error = ENOENT; 1009 } 1010 if (mf->mf_flags & MFF_WEBNFS) 1011 fs->fs_flags |= FSF_WEBNFS; 1012 fs->fs_version = nfs_version; 1013 fs->fs_proto = nfs_proto; 1014 fs->fs_type = MNTTAB_TYPE_NFS; 1015 fs->fs_pinger = AM_PINGER; 1016 fs->fs_flags |= FSF_PING_UNINIT; /* pinger hasn't been initialized */ 1017 np = ALLOC(struct nfs_private); 1018 memset((voidp) np, 0, sizeof(*np)); 1019 np->np_mountd_inval = TRUE; 1020 np->np_xid = XID_ALLOC(); 1021 np->np_error = -1; 1022 1023 /* 1024 * Initially the server will be deemed dead after 1025 * MAX_ALLOWED_PINGS of the fast variety have failed. 1026 */ 1027 np->np_ttl = clocktime(NULL) + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1; 1028 fs->fs_private = (voidp) np; 1029 fs->fs_prfree = (void (*)(voidp)) free; 1030 1031 if (!FSRV_ERROR(fs)) { 1032 /* start of keepalive timer, first updating pingval */ 1033 start_nfs_pings(fs, pingval); 1034 if (fserver_is_down) 1035 fs->fs_flags |= FSF_VALID | FSF_DOWN; 1036 } 1037 1038 /* 1039 * Add to list of servers 1040 */ 1041 ins_que(&fs->fs_q, &nfs_srvr_list); 1042 1043 return fs; 1044 } 1045