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