2 * Copyright (c) 1997-1999 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.
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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
41 * $Id: srvr_nfs.c,v 1.5 1999/09/08 23:36:39 ezk Exp $
42 * $FreeBSD: src/contrib/amd/amd/srvr_nfs.c,v 1.4 1999/09/15 05:45:13 obrien Exp $
43 * $DragonFly: src/contrib/amd/amd/srvr_nfs.c,v 1.2 2003/06/17 04:23:56 dillon Exp $
53 #endif /* HAVE_CONFIG_H */
58 * Number of pings allowed to fail before host is declared down
59 * - three-fifths of the allowed mount time...
61 #define MAX_ALLOWED_PINGS (3 + /* for luck ... */ 1)
64 * How often to ping when starting a new server
66 #define FAST_NFS_PING 3
68 #if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME
69 # error: sanity check failed in srvr_nfs.c
71 * you cannot do things this way...
72 * sufficient fast pings must be given the chance to fail
73 * within the allowed mount time
75 #endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */
77 #define NPXID_ALLOC(struct ) (++np_xid)
79 /* structures and typedefs */
80 typedef struct nfs_private {
81 u_short np_mountd; /* Mount daemon port number */
82 char np_mountd_inval; /* Port *may* be invalid */
83 int np_ping; /* Number of failed ping attempts */
84 time_t np_ttl; /* Time when server is thought dead */
85 int np_xid; /* RPC transaction id for pings */
86 int np_error; /* Error during portmap request */
90 qelem nfs_srvr_list = {&nfs_srvr_list, &nfs_srvr_list};
93 static int np_xid; /* For NFS pings */
95 static char ping_buf[sizeof(struct rpc_msg) + 32];
97 #if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3)
98 /* protocols we know about, in order of preference */
99 static char *protocols[] = { "tcp", "udp", NULL };
100 #endif /* defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */
102 /* forward definitions */
103 static void nfs_keepalive(voidp);
108 * Flush any cached data
111 flush_srvr_nfs_cache(void)
115 ITER(fs, fserver, &nfs_srvr_list) {
116 nfs_private *np = (nfs_private *) fs->fs_private;
118 np->np_mountd_inval = TRUE;
126 * Startup the NFS ping for a particular version.
129 start_ping(u_long nfs_version)
132 struct rpc_msg ping_msg;
135 * Non nfs mounts like /afs/glue.umd.edu have ended up here.
137 if (nfs_version == 0) {
138 nfs_version = NFS_VERSION;
139 plog(XLOG_WARNING, "start_ping: nfs_version = 0 fixed");
141 plog(XLOG_INFO, "start_ping: nfs_version: %d", (int) nfs_version);
143 rpc_msg_init(&ping_msg, NFS_PROGRAM, nfs_version, NFSPROC_NULL);
146 * Create an XDR endpoint
148 xdrmem_create(&ping_xdr, ping_buf, sizeof(ping_buf), XDR_ENCODE);
151 * Create the NFS ping message
153 if (!xdr_callmsg(&ping_xdr, &ping_msg)) {
154 plog(XLOG_ERROR, "Couldn't create ping RPC message");
158 * Find out how long it is
160 ping_len = xdr_getpos(&ping_xdr);
163 * Destroy the XDR endpoint - we don't need it anymore
165 xdr_destroy(&ping_xdr);
170 * Called when a portmap reply arrives
173 got_portmap(voidp pkt, int len, struct sockaddr_in * sa, struct sockaddr_in * ia, voidp idv, int done)
175 fserver *fs2 = (fserver *) idv;
179 * Find which fileserver we are talking about
181 ITER(fs, fserver, &nfs_srvr_list)
186 u_long port = 0; /* XXX - should be short but protocol is naff */
187 int error = done ? pickup_rpc_reply(pkt, len, (voidp) &port, (XDRPROC_T_TYPE) xdr_u_long) : -1;
188 nfs_private *np = (nfs_private *) fs->fs_private;
190 if (!error && port) {
192 dlog("got port (%d) for mountd on %s", (int) port, fs->fs_host);
195 * Grab the port number. Portmap sends back
196 * an u_long in native ordering, so it
197 * needs converting to a u_short in
200 np->np_mountd = htons((u_short) port);
201 np->np_mountd_inval = FALSE;
205 dlog("Error fetching port for mountd on %s", fs->fs_host);
206 dlog("\t error=%d, port=%d", error, (int) port);
209 * Almost certainly no mountd running on remote host
211 np->np_error = error ? error : ETIMEDOUT;
214 if (fs->fs_flags & FSF_WANT)
218 dlog("Got portmap for old port request");
222 dlog("portmap request timed out");
229 * Obtain portmap information
232 call_portmap(fserver *fs, AUTH * auth, u_long prog, u_long vers, u_long prot)
234 struct rpc_msg pmap_msg;
236 char iobuf[UDPMSGSIZE];
240 rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, PMAPPROC_NULL);
245 len = make_rpc_packet(iobuf,
250 (XDRPROC_T_TYPE) xdr_pmap,
253 struct sockaddr_in sin;
254 memset((voidp) &sin, 0, sizeof(sin));
256 sin.sin_port = htons(PMAPPORT);
257 error = fwd_packet(RPC_XID_PORTMAP, (voidp) iobuf, len,
258 &sin, &sin, (voidp) fs, got_portmap);
268 recompute_portmap(fserver *fs)
276 error = make_nfs_auth();
279 nfs_private *np = (nfs_private *) fs->fs_private;
280 np->np_error = error;
284 if (fs->fs_version == 0)
285 plog(XLOG_WARNING, "recompute_portmap: nfs_version = 0 fixed");
287 plog(XLOG_INFO, "recompute_portmap: NFS version %d", (int) fs->fs_version);
289 if (fs->fs_version == NFS_VERSION3)
290 mnt_version = MOUNTVERS3;
292 #endif /* HAVE_FS_NFS3 */
293 mnt_version = MOUNTVERS;
295 plog(XLOG_INFO, "Using MOUNT version: %d", (int) mnt_version);
296 call_portmap(fs, nfs_auth, MOUNTPROG, mnt_version, (u_long) IPPROTO_UDP);
301 * This is called when we get a reply to an RPC ping.
302 * The value of id was taken from the nfs_private
303 * structure when the ping was transmitted.
306 nfs_pinged(voidp pkt, int len, struct sockaddr_in * sp, struct sockaddr_in * tsp, voidp idv, int done)
308 int xid = (long) idv; /* for 64-bit archs */
320 ITER(fs, fserver, &nfs_srvr_list) {
321 nfs_private *np = (nfs_private *) fs->fs_private;
322 if (np->np_xid == xid && (fs->fs_flags & FSF_PINGING)) {
324 * Reset the ping counter.
325 * Update the keepalive timer.
328 if (fs->fs_flags & FSF_DOWN) {
329 fs->fs_flags &= ~FSF_DOWN;
330 if (fs->fs_flags & FSF_VALID) {
331 srvrlog(fs, "is up");
337 srvrlog(fs, "starts up");
339 fs->fs_flags |= FSF_VALID;
344 if (fs->fs_flags & FSF_VALID) {
346 dlog("file server %s type nfs is still up", fs->fs_host);
351 fs->fs_flags |= FSF_VALID;
356 * Adjust ping interval
358 untimeout(fs->fs_cid);
359 fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs);
362 * Update ttl for this server
364 np->np_ttl = clocktime() +
365 (MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1;
370 np->np_xid = NPXID_ALLOC(struct );
373 * Failed pings is zero...
378 * Recompute portmap information if not known
380 if (np->np_mountd_inval)
381 recompute_portmap(fs);
392 dlog("Spurious ping packet");
398 * Called when no ping-reply received
401 nfs_timed_out(voidp v)
404 nfs_private *np = (nfs_private *) fs->fs_private;
407 * Another ping has failed
412 * Not known to be up any longer
415 fs->fs_flags &= ~FSF_VALID;
417 srvrlog(fs, "not responding");
421 * If ttl has expired then guess that it is dead
423 if (np->np_ttl < clocktime()) {
424 int oflags = fs->fs_flags;
425 if ((fs->fs_flags & FSF_DOWN) == 0) {
427 * Server was up, but is now down.
429 srvrlog(fs, "is down");
430 fs->fs_flags |= FSF_DOWN | FSF_VALID;
432 * Since the server is down, the portmap
433 * information may now be wrong, so it
434 * must be flushed from the local cache
436 flush_nfs_fhandle_cache(fs);
443 if ((fs->fs_flags & FSF_VALID) == 0)
444 srvrlog(fs, "starts down");
446 fs->fs_flags |= FSF_VALID;
448 if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT))
453 dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS);
458 * Run keepalive again
465 * Keep track of whether a server is alive
468 nfs_keepalive(voidp v)
472 nfs_private *np = (nfs_private *) fs->fs_private;
476 * Send an NFS ping to this node
480 start_ping(fs->fs_version);
483 * Queue the packet...
485 error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid),
489 (struct sockaddr_in *) 0,
490 (voidp) ((long) np->np_xid), /* for 64-bit archs */
494 * See if a hard error occurred
501 np->np_ping = MAX_ALLOWED_PINGS; /* immediately down */
502 np->np_ttl = (time_t) 0;
504 * This causes an immediate call to nfs_timed_out
505 * whenever the server was thought to be up.
513 dlog("Sent NFS ping to %s", fs->fs_host);
519 * Back off the ping interval if we are not getting replies and
520 * the remote system is know to be down.
522 switch (fs->fs_flags & (FSF_DOWN | FSF_VALID)) {
523 case FSF_VALID: /* Up */
524 if (fstimeo < 0) /* +++ see above */
525 fstimeo = FAST_NFS_PING;
528 case FSF_VALID | FSF_DOWN: /* Down */
529 fstimeo = fs->fs_pinger;
532 default: /* Unknown */
533 fstimeo = FAST_NFS_PING;
538 dlog("NFS timeout in %d seconds", fstimeo);
541 fs->fs_cid = timeout(fstimeo, nfs_timed_out, (voidp) fs);
546 nfs_srvr_port(fserver *fs, u_short * port, voidp wchan)
549 if ((fs->fs_flags & FSF_VALID) == FSF_VALID) {
550 if ((fs->fs_flags & FSF_DOWN) == 0) {
551 nfs_private *np = (nfs_private *) fs->fs_private;
552 if (np->np_error == 0) {
553 *port = np->np_mountd;
556 error = np->np_error;
559 * Now go get the port mapping again in case it changed.
560 * Note that it is used even if (np_mountd_inval)
561 * is True. The flag is used simply as an
562 * indication that the mountd may be invalid, not
563 * that it is known to be invalid.
565 if (np->np_mountd_inval)
566 recompute_portmap(fs);
568 np->np_mountd_inval = TRUE;
573 if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) {
575 * If a wait channel is supplied, and no
576 * error has yet occurred, then arrange
577 * that a wakeup is done on the wait channel,
578 * whenever a wakeup is done on this fs node.
579 * Wakeup's are done on the fs node whenever
580 * it changes state - thus causing control to
581 * come back here and new, better things to happen.
583 fs->fs_flags |= FSF_WANT;
584 sched_task(wakeup_task, wchan, (voidp) fs);
591 start_nfs_pings(fserver *fs, int pingval)
593 if (!(fs->fs_flags & FSF_PINGING)) {
594 fs->fs_flags |= FSF_PINGING;
596 untimeout(fs->fs_cid);
598 srvrlog(fs, "wired up");
599 fs->fs_flags |= FSF_VALID;
600 fs->fs_flags &= ~FSF_DOWN;
606 dlog("Already running pings to %s", fs->fs_host);
613 * Find an nfs server for a host.
616 find_nfs_srvr(mntfs *mf)
618 char *host = mf->mf_fo->opt_rhost;
619 char *nfs_proto = NULL;
624 struct hostent *hp = 0;
625 struct sockaddr_in *ip;
626 u_long nfs_version = 0; /* default is no version specified */
627 #ifdef MNTTAB_OPT_PROTO
628 char *rfsname = mf->mf_fo->opt_rfs;
629 #endif /* MNTTAB_OPT_PROTO */
632 * Get ping interval from mount options.
633 * Current only used to decide whether pings
634 * are required or not. < 0 = no pings.
636 mnt.mnt_opts = mf->mf_mopts;
637 pingval = hasmntval(&mnt, "ping");
640 * Get the NFS version from the mount options. This is used
641 * to decide the highest NFS version to try.
643 #ifdef MNTTAB_OPT_VERS
644 nfs_version = hasmntval(&mnt, MNTTAB_OPT_VERS);
645 #endif /* MNTTAB_OPT_VERS */
647 #ifdef MNTTAB_OPT_PROTO
649 char *proto_opt = hasmntopt(&mnt, MNTTAB_OPT_PROTO);
653 proto_opt += sizeof(MNTTAB_OPT_PROTO) - 1; /* skip the "proto" */
655 for (p = protocols; *p; p ++)
656 if (proto_opt[0] == '=' &&
657 NSTREQ(&proto_opt[1], *p, strlen(*p))) {
662 plog(XLOG_WARNING, "ignoring unknown protocol option for %s:%s",
666 #endif /* MNTTAB_OPT_PROTO */
668 #ifdef HAVE_NFS_NFSV2_H
669 /* allow overriding if nfsv2 option is specified in mount options */
670 if (hasmntopt(&mnt, "nfsv2")) {
671 nfs_version = (u_long) 2; /* nullify any ``vers=X'' statements */
672 nfs_proto = "udp"; /* nullify any ``proto=tcp'' statements */
673 plog(XLOG_WARNING, "found compatiblity option \"nfsv2\": set options vers=2, proto=udp for host %s", host);
675 #endif /* HAVE_NFS_NFSV2_H */
678 * lookup host address and canonical name
680 hp = gethostbyname(host);
683 * New code from Bob Harris <harris@basil-rathbone.mit.edu>
684 * Use canonical name to keep track of file server
685 * information. This way aliases do not generate
686 * multiple NFS pingers. (Except when we're normalizing
689 if (hp && !(gopt.flags & CFM_NORMALIZE_HOSTNAMES))
690 host = (char *) hp->h_name;
693 switch (hp->h_addrtype) {
695 ip = ALLOC(struct sockaddr_in);
696 memset((voidp) ip, 0, sizeof(*ip));
697 ip->sin_family = AF_INET;
698 memmove((voidp) &ip->sin_addr, (voidp) hp->h_addr, sizeof(ip->sin_addr));
700 ip->sin_port = htons(NFS_PORT);
708 plog(XLOG_USER, "Unknown host: %s", host);
713 * Get the NFS Version, and verify server is up. Probably no
714 * longer need to start server down below.
719 * Find the best combination of NFS version and protocol.
720 * When given a choice, use the highest available version,
721 * and use TCP over UDP if available.
724 nfs_version = get_nfs_version(host, ip, nfs_version, nfs_proto);
726 int best_nfs_version = 0;
727 int proto_nfs_version;
730 for (p = protocols; *p; p++) {
731 proto_nfs_version = get_nfs_version(host, ip, nfs_version, *p);
733 if (proto_nfs_version > best_nfs_version) {
734 best_nfs_version = proto_nfs_version;
738 nfs_version = best_nfs_version;
743 * If the NFS server is down or does not support the portmapper call
744 * (such as certain Novell NFS servers) we mark it as version 2 and we
745 * let the nfs code deal with the case that is down. If when the
746 * server comes back up, it can support NFS V.3 and/or TCP, it will
749 nfs_version = NFS_VERSION;
752 #else /* not HAVE_FS_NFS3 */
753 nfs_version = NFS_VERSION;
754 #endif /* not HAVE_FS_NFS3 */
760 plog(XLOG_INFO, "Using NFS version %d, protocol %s on host %s",
761 (int) nfs_version, nfs_proto, host);
764 * Try to find an existing fs server structure for this host.
765 * Note that differing versions or protocols have their own structures.
766 * XXX: Need to fix the ping mechanism to actually use the NFS protocol
767 * chosen here (right now it always uses datagram sockets).
769 ITER(fs, fserver, &nfs_srvr_list) {
770 if (STREQ(host, fs->fs_host) &&
771 nfs_version == fs->fs_version &&
772 STREQ(nfs_proto, fs->fs_proto)) {
774 * following if statement from Mike Mitchell
776 * Initialize the ping data if we aren't pinging
777 * now. The np_ttl and np_ping fields are
778 * especially important.
780 if (!(fs->fs_flags & FSF_PINGING)) {
781 np = (nfs_private *) fs->fs_private;
782 np->np_mountd_inval = TRUE;
783 np->np_xid = NPXID_ALLOC(struct );
787 * Initially the server will be deemed dead
788 * after MAX_ALLOWED_PINGS of the fast variety
791 np->np_ttl = MAX_ALLOWED_PINGS * FAST_NFS_PING + clocktime() - 1;
794 * fill in the IP address -- this is only needed
795 * if there is a chance an IP address will change
797 * Mike Mitchell, mcm@unx.sas.com, 09/08/93
800 memmove((voidp) &fs->fs_ip->sin_addr, (voidp) hp->h_addr, sizeof(fs->fs_ip->sin_addr));
802 start_nfs_pings(fs, pingval);
811 * Get here if we can't find an entry
815 * Allocate a new server
817 fs = ALLOC(struct fserver);
819 fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname");
820 if (gopt.flags & CFM_NORMALIZE_HOSTNAMES)
821 host_normalize(&fs->fs_host);
825 fs->fs_flags = FSF_DOWN; /* Starts off down */
827 fs->fs_flags = FSF_ERROR | FSF_VALID;
828 mf->mf_flags |= MFF_ERROR;
829 mf->mf_error = ENOENT;
831 fs->fs_version = nfs_version;
832 fs->fs_proto = nfs_proto;
833 fs->fs_type = MNTTAB_TYPE_NFS;
834 fs->fs_pinger = AM_PINGER;
835 np = ALLOC(struct nfs_private);
836 memset((voidp) np, 0, sizeof(*np));
837 np->np_mountd_inval = TRUE;
838 np->np_xid = NPXID_ALLOC(struct );
842 * Initially the server will be deemed dead after
843 * MAX_ALLOWED_PINGS of the fast variety have failed.
845 np->np_ttl = clocktime() + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1;
846 fs->fs_private = (voidp) np;
847 fs->fs_prfree = (void (*)(voidp)) free;
849 if (!(fs->fs_flags & FSF_ERROR)) {
851 * Start of keepalive timer
853 start_nfs_pings(fs, pingval);
857 * Add to list of servers
859 ins_que(&fs->fs_q, &nfs_srvr_list);