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