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