1 /* $NetBSD: srvr_nfs.c,v 1.1.1.3 2015/01/17 16:34:15 christos Exp $ */
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 */
73 typedef 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 */
84 qelem nfs_srvr_list = {&nfs_srvr_list, &nfs_srvr_list};
85
86 /* statics */
87 static 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 */
97 static int ping_len[NUM_NFS_VERS];
98 static 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 */
110 static char *protocols[] = { "tcp", "udp", NULL };
111 #endif /* defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */
112
113 /* forward definitions */
114 static void nfs_keepalive(voidp);
115
116
117 /*
118 * Flush cached data for an fserver (or for all, if fs==NULL)
119 */
120 void
flush_srvr_nfs_cache(fserver * fs)121 flush_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 */
140 static void
create_ping_payload(u_long nfs_version)141 create_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 */
185 static void
got_portmap(voidp pkt,int len,struct sockaddr_in * sa,struct sockaddr_in * ia,voidp idv,int done)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 = '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 */
236 static int
call_portmap(fserver * fs,AUTH * auth,u_long prog,u_long vers,u_long prot)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
recompute_portmap(fserver * fs)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, "%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
312 int
get_mountd_port(fserver * fs,u_short * port,wchan_t wchan)313 get_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 */
370 static void
nfs_keepalive_callback(voidp pkt,int len,struct sockaddr_in * sp,struct sockaddr_in * tsp,voidp idv,int done)371 nfs_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
452 static void
check_fs_addr_change(fserver * fs)453 check_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 */
495 static void
nfs_keepalive_timeout(voidp v)496 nfs_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 */
569 static void
nfs_keepalive(voidp v)570 nfs_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
645 static void
start_nfs_pings(fserver * fs,int pingval)646 start_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 */
686 fserver *
find_nfs_srvr(mntfs * mf)687 find_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
937 no_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