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