1 /*
2 * Copyright (c) 1990 Jan-Simon Pendry
3 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
4 * Copyright (c) 1990, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Jan-Simon Pendry at Imperial College, London.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * from: @(#)srvr_nfs.c 8.1 (Berkeley) 6/6/93
35 * $Id: srvr_nfs.c,v 1.10 2014/10/26 03:28:41 guenther Exp $
36 */
37
38 /*
39 * NFS server modeling
40 */
41
42 #include "am.h"
43 #include <netdb.h>
44 #include <rpc/pmap_prot.h>
45 #include "mount.h"
46
47 extern qelem nfs_srvr_list;
48 qelem nfs_srvr_list = { &nfs_srvr_list, &nfs_srvr_list };
49
50 typedef struct nfs_private {
51 u_short np_mountd; /* Mount daemon port number */
52 char np_mountd_inval; /* Port *may* be invalid */
53 int np_ping; /* Number of failed ping attempts */
54 time_t np_ttl; /* Time when server is thought dead */
55 int np_xid; /* RPC transaction id for pings */
56 int np_error; /* Error during portmap request */
57 } nfs_private;
58
59 static int np_xid; /* For NFS pings */
60 #define NPXID_ALLOC() (++np_xid)
61 /*#define NPXID_ALLOC() ((++np_xid&0x0fffffff) == 0 ? npxid_gc() : np_xid)*/
62
63 /*
64 * Number of pings allowed to fail before host is declared down
65 * - three-fifths of the allowed mount time...
66 #define MAX_ALLOWED_PINGS ((((ALLOWED_MOUNT_TIME + 5 * AM_PINGER - 1) * 3) / 5) / AM_PINGER)
67 */
68 #define MAX_ALLOWED_PINGS (3 + /* for luck ... */ 1)
69
70 /*
71 * How often to ping when starting a new server
72 */
73 #define FAST_NFS_PING 3
74
75 #if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME
76 #error: sanity check failed
77 /*
78 you cannot do things this way...
79 sufficient fast pings must be given the chance to fail
80 within the allowed mount time
81 */
82 #endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */
83
84 static int ping_len;
85 static char ping_buf[sizeof(struct rpc_msg) + 32];
86
87 /*
88 * Flush any cached data
89 */
90 void
flush_srvr_nfs_cache(void)91 flush_srvr_nfs_cache(void)
92 {
93 fserver *fs = 0;
94
95 ITER(fs, fserver, &nfs_srvr_list) {
96 nfs_private *np = (nfs_private *) fs->fs_private;
97 if (np) {
98 np->np_mountd_inval = TRUE;
99 np->np_error = -1;
100 }
101 }
102 }
103
104 /*
105 * Startup the NFS ping
106 */
107 static void
start_ping(void)108 start_ping(void)
109 {
110 XDR ping_xdr;
111 struct rpc_msg ping_msg;
112
113 rpc_msg_init(&ping_msg, NFS_PROGRAM, NFS_VERSION, NFSPROC_NULL);
114
115 /*
116 * Create an XDR endpoint
117 */
118 xdrmem_create(&ping_xdr, ping_buf, sizeof(ping_buf), XDR_ENCODE);
119
120 /*
121 * Create the NFS ping message
122 */
123 if (!xdr_callmsg(&ping_xdr, &ping_msg)) {
124 plog(XLOG_ERROR, "Couldn't create ping RPC message");
125 going_down(3);
126 }
127
128 /*
129 * Find out how long it is
130 */
131 ping_len = xdr_getpos(&ping_xdr);
132
133 /*
134 * Destroy the XDR endpoint - we don't need it anymore
135 */
136 xdr_destroy(&ping_xdr);
137 }
138
139
140 /*
141 * Called when a portmap reply arrives
142 */
143 static void
got_portmap(void * pkt,int len,struct sockaddr_in * sa,struct sockaddr_in * ia,void * idv,int done)144 got_portmap(void *pkt, int len, struct sockaddr_in *sa,
145 struct sockaddr_in *ia, void *idv, int done)
146 {
147 fserver *fs2 = (fserver *) idv;
148 fserver *fs = 0;
149
150 /*
151 * Find which fileserver we are talking about
152 */
153 ITER(fs, fserver, &nfs_srvr_list)
154 if (fs == fs2)
155 break;
156
157 if (fs == fs2) {
158 u_long port = 0; /* XXX - should be short but protocol is naff */
159 int error = done ? pickup_rpc_reply(pkt, len, &port, xdr_u_long) : -1;
160 nfs_private *np = (nfs_private *) fs->fs_private;
161 if (!error && port) {
162 #ifdef DEBUG
163 dlog("got port (%d) for mountd on %s", port, fs->fs_host);
164 #endif /* DEBUG */
165 /*
166 * Grab the port number. Portmap sends back
167 * an unsigned long in native ordering, so it
168 * needs converting to a unsigned short in
169 * network ordering.
170 */
171 np->np_mountd = htons((u_short) port);
172 np->np_mountd_inval = FALSE;
173 np->np_error = 0;
174 } else {
175 #ifdef DEBUG
176 dlog("Error fetching port for mountd on %s", fs->fs_host);
177 #endif /* DEBUG */
178 /*
179 * Almost certainly no mountd running on remote host
180 */
181 np->np_error = error ? error : ETIMEDOUT;
182 }
183 if (fs->fs_flags & FSF_WANT)
184 wakeup_srvr(fs);
185 } else if (done) {
186 #ifdef DEBUG
187 dlog("Got portmap for old port request");
188 #endif /* DEBUG */
189 } else {
190 #ifdef DEBUG
191 dlog("portmap request timed out");
192 #endif /* DEBUG */
193 }
194 }
195
196 /*
197 * Obtain portmap information
198 */
199 static int
call_portmap(fserver * fs,AUTH * auth,unsigned long prog,unsigned long vers,unsigned long prot)200 call_portmap(fserver *fs, AUTH *auth, unsigned long prog,
201 unsigned long vers, unsigned long prot)
202 {
203 struct rpc_msg pmap_msg;
204 int len;
205 char iobuf[UDPMSGSIZE];
206 int error;
207 struct pmap pmap;
208
209 rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, (unsigned long) 0);
210 pmap.pm_prog = prog;
211 pmap.pm_vers = vers;
212 pmap.pm_prot = prot;
213 pmap.pm_port = 0;
214 len = make_rpc_packet(iobuf, sizeof(iobuf), PMAPPROC_GETPORT,
215 &pmap_msg, &pmap, xdr_pmap, auth);
216 if (len > 0) {
217 struct sockaddr_in sin;
218 bzero(&sin, sizeof(sin));
219 sin = *fs->fs_ip;
220 sin.sin_port = htons(PMAPPORT);
221 error = fwd_packet(RPC_XID_PORTMAP, iobuf, len,
222 &sin, &sin, fs, got_portmap);
223 } else {
224 error = -len;
225 }
226 return error;
227 }
228
229 static void nfs_keepalive(void *);
230
231 static void
recompute_portmap(fserver * fs)232 recompute_portmap(fserver *fs)
233 {
234 int error;
235
236 if (nfs_auth)
237 error = 0;
238 else
239 error = make_nfs_auth();
240
241 if (error) {
242 nfs_private *np = (nfs_private *) fs->fs_private;
243 np->np_error = error;
244 } else {
245 call_portmap(fs, nfs_auth, MOUNTPROG,
246 MOUNTVERS, (unsigned long) IPPROTO_UDP);
247 }
248 }
249
250 /*
251 * This is called when we get a reply to an RPC ping.
252 * The value of id was taken from the nfs_private
253 * structure when the ping was transmitted.
254 */
255 static void
nfs_pinged(void * pkt,int len,struct sockaddr_in * sp,struct sockaddr_in * tsp,void * idv,int done)256 nfs_pinged(void *pkt, int len, struct sockaddr_in *sp,
257 struct sockaddr_in *tsp, void *idv, int done)
258 {
259 /* XXX EVIL! XXX */
260 int xid = (int) ((long)idv);
261 fserver *fs;
262 #ifdef DEBUG
263 int found_map = 0;
264 #endif /* DEBUG */
265
266 if (!done)
267 return;
268
269 /*
270 * For each node...
271 */
272 ITER(fs, fserver, &nfs_srvr_list) {
273 nfs_private *np = (nfs_private *) fs->fs_private;
274 if (np->np_xid == xid) {
275 /*
276 * Reset the ping counter.
277 * Update the keepalive timer.
278 * Log what happened.
279 */
280 if (fs->fs_flags & FSF_DOWN) {
281 fs->fs_flags &= ~FSF_DOWN;
282 if (fs->fs_flags & FSF_VALID) {
283 srvrlog(fs, "is up");
284 } else {
285 if (np->np_ping > 1)
286 srvrlog(fs, "ok");
287 #ifdef DEBUG
288 else
289 srvrlog(fs, "starts up");
290 #endif
291 fs->fs_flags |= FSF_VALID;
292 }
293
294 #ifdef notdef
295 /* why ??? */
296 if (fs->fs_flags & FSF_WANT)
297 wakeup_srvr(fs);
298 #endif /* notdef */
299 map_flush_srvr(fs);
300 } else {
301 if (fs->fs_flags & FSF_VALID) {
302 #ifdef DEBUG
303 dlog("file server %s type nfs is still up", fs->fs_host);
304 #endif /* DEBUG */
305 } else {
306 if (np->np_ping > 1)
307 srvrlog(fs, "ok");
308 fs->fs_flags |= FSF_VALID;
309 }
310 }
311
312 /*
313 * Adjust ping interval
314 */
315 untimeout(fs->fs_cid);
316 fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, fs);
317
318 /*
319 * Update ttl for this server
320 */
321 np->np_ttl = clocktime() +
322 (MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1;
323
324 /*
325 * New RPC xid...
326 */
327 np->np_xid = NPXID_ALLOC();
328
329 /*
330 * Failed pings is zero...
331 */
332 np->np_ping = 0;
333
334 /*
335 * Recompute portmap information if not known
336 */
337 if (np->np_mountd_inval)
338 recompute_portmap(fs);
339
340 #ifdef DEBUG
341 found_map++;
342 #endif /* DEBUG */
343 break;
344 }
345 }
346
347 #ifdef DEBUG
348 if (found_map == 0)
349 dlog("Spurious ping packet");
350 #endif /* DEBUG */
351 }
352
353 /*
354 * Called when no ping-reply received
355 */
356 static void
nfs_timed_out(void * arg)357 nfs_timed_out(void *arg)
358 {
359 fserver *fs = arg;
360
361 nfs_private *np = (nfs_private *) fs->fs_private;
362
363 /*
364 * Another ping has failed
365 */
366 np->np_ping++;
367
368 /*
369 * Not known to be up any longer
370 */
371 if (FSRV_ISUP(fs)) {
372 fs->fs_flags &= ~FSF_VALID;
373 if (np->np_ping > 1)
374 srvrlog(fs, "not responding");
375 }
376
377 /*
378 * If ttl has expired then guess that it is dead
379 */
380 if (np->np_ttl < clocktime()) {
381 int oflags = fs->fs_flags;
382 if ((fs->fs_flags & FSF_DOWN) == 0) {
383 /*
384 * Server was up, but is now down.
385 */
386 srvrlog(fs, "is down");
387 fs->fs_flags |= FSF_DOWN|FSF_VALID;
388 /*
389 * Since the server is down, the portmap
390 * information may now be wrong, so it
391 * must be flushed from the local cache
392 */
393 flush_nfs_fhandle_cache(fs);
394 np->np_error = -1;
395 #ifdef notdef
396 /*
397 * Pretend just one ping has failed now
398 */
399 np->np_ping = 1;
400 #endif
401 } else {
402 /*
403 * Known to be down
404 */
405 #ifdef DEBUG
406 if ((fs->fs_flags & FSF_VALID) == 0)
407 srvrlog(fs, "starts down");
408 #endif
409 fs->fs_flags |= FSF_VALID;
410 }
411 if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT))
412 wakeup_srvr(fs);
413 } else {
414 #ifdef DEBUG
415 if (np->np_ping > 1)
416 dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS);
417 #endif /* DEBUG */
418 }
419
420 /*
421 * Run keepalive again
422 */
423 nfs_keepalive(fs);
424 }
425
426 /*
427 * Keep track of whether a server is alive
428 */
429 static void
nfs_keepalive(void * arg)430 nfs_keepalive(void *arg)
431 {
432 fserver *fs = arg;
433
434 int error;
435 nfs_private *np = (nfs_private *) fs->fs_private;
436 int fstimeo = -1;
437
438 /*
439 * Send an NFS ping to this node
440 */
441
442 if (ping_len == 0)
443 start_ping();
444
445 /*
446 * Queue the packet...
447 */
448 /*
449 * XXX EVIL! We cast xid to a pointer, then back to an int when
450 * XXX we get the reply.
451 */
452 error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid), ping_buf,
453 ping_len, fs->fs_ip, NULL, (void *)((long)np->np_xid),
454 nfs_pinged);
455
456 /*
457 * See if a hard error occured
458 */
459 switch (error) {
460 case ENETDOWN:
461 case ENETUNREACH:
462 case EHOSTDOWN:
463 case EHOSTUNREACH:
464 np->np_ping = MAX_ALLOWED_PINGS; /* immediately down */
465 np->np_ttl = (time_t) 0;
466 /*
467 * This causes an immediate call to nfs_timed_out
468 * whenever the server was thought to be up.
469 * See +++ below.
470 */
471 fstimeo = 0;
472 break;
473
474 case 0:
475 #ifdef DEBUG
476 dlog("Sent NFS ping to %s", fs->fs_host);
477 #endif /* DEBUG */
478 break;
479 }
480
481 #ifdef DEBUG
482 /*dlog("keepalive, ping = %d", np->np_ping);*/
483 #endif /* DEBUG */
484
485 /*
486 * Back off the ping interval if we are not getting replies and
487 * the remote system is know to be down.
488 */
489 switch (fs->fs_flags & (FSF_DOWN|FSF_VALID)) {
490 case FSF_VALID: /* Up */
491 if (fstimeo < 0) /* +++ see above */
492 fstimeo = FAST_NFS_PING;
493 break;
494
495 case FSF_VALID|FSF_DOWN: /* Down */
496 fstimeo = fs->fs_pinger;
497 break;
498
499 default: /* Unknown */
500 fstimeo = FAST_NFS_PING;
501 break;
502 }
503
504 #ifdef DEBUG
505 dlog("NFS timeout in %d seconds", fstimeo);
506 #endif /* DEBUG */
507
508 fs->fs_cid = timeout(fstimeo, nfs_timed_out, fs);
509 }
510
511 int
nfs_srvr_port(fserver * fs,u_short * port,void * wchan)512 nfs_srvr_port(fserver *fs, u_short *port, void *wchan)
513 {
514 int error = -1;
515 if ((fs->fs_flags & FSF_VALID) == FSF_VALID) {
516 if ((fs->fs_flags & FSF_DOWN) == 0) {
517 nfs_private *np = (nfs_private *) fs->fs_private;
518 if (np->np_error == 0) {
519 *port = np->np_mountd;
520 error = 0;
521 } else {
522 error = np->np_error;
523 }
524 /*
525 * Now go get the port mapping again in case it changed.
526 * Note that it is used even if (np_mountd_inval)
527 * is True. The flag is used simply as an
528 * indication that the mountd may be invalid, not
529 * that it is known to be invalid.
530 */
531 if (np->np_mountd_inval)
532 recompute_portmap(fs);
533 else
534 np->np_mountd_inval = TRUE;
535 } else {
536 error = EWOULDBLOCK;
537 }
538 }
539 if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) {
540 /*
541 * If a wait channel is supplied, and no
542 * error has yet occured, then arrange
543 * that a wakeup is done on the wait channel,
544 * whenever a wakeup is done on this fs node.
545 * Wakeup's are done on the fs node whenever
546 * it changes state - thus causing control to
547 * come back here and new, better things to happen.
548 */
549 fs->fs_flags |= FSF_WANT;
550 sched_task(wakeup_task, wchan, fs);
551 }
552 return error;
553 }
554
555 static void
start_nfs_pings(fserver * fs,int pingval)556 start_nfs_pings(fserver *fs, int pingval)
557 {
558 if (!(fs->fs_flags & FSF_PINGING)) {
559 fs->fs_flags |= FSF_PINGING;
560 if (fs->fs_cid)
561 untimeout(fs->fs_cid);
562 if (pingval < 0) {
563 srvrlog(fs, "wired up");
564 fs->fs_flags |= FSF_VALID;
565 fs->fs_flags &= ~FSF_DOWN;
566 } else {
567 nfs_keepalive(fs);
568 }
569 } else {
570 #ifdef DEBUG
571 dlog("Already running pings to %s", fs->fs_host);
572 #endif /* DEBUG */
573 }
574 }
575
576 /*
577 * Find an nfs server for a host.
578 */
579 fserver *
find_nfs_srvr(mntfs * mf)580 find_nfs_srvr(mntfs *mf)
581 {
582 fserver *fs;
583 struct hostent *hp = 0;
584 char *host = mf->mf_fo->opt_rhost;
585 struct sockaddr_in *ip;
586 nfs_private *np;
587 int pingval;
588
589 /*
590 * Get ping interval from mount options.
591 * Current only used to decide whether pings
592 * are required or not. < 0 = no pings.
593 */
594 { struct mntent mnt;
595 mnt.mnt_opts = mf->mf_mopts;
596 pingval = hasmntval(&mnt, "ping");
597 /*
598 * Over TCP mount, don't bother to do pings.
599 * This is experimental - maybe you want to
600 * do pings anyway...
601 */
602 if (pingval == 0 && hasmntopt(&mnt, "tcp"))
603 pingval = -1;
604 }
605
606
607 /*
608 * lookup host address and canonical name
609 */
610 hp = gethostbyname(host);
611
612 /*
613 * New code from Bob Harris <harris@basil-rathbone.mit.edu>
614 * Use canonical name to keep track of file server
615 * information. This way aliases do not generate
616 * multiple NFS pingers. (Except when we're normalizing
617 * hosts.)
618 */
619 if (hp && !normalize_hosts) host = hp->h_name;
620
621 ITER(fs, fserver, &nfs_srvr_list) {
622 if (STREQ(host, fs->fs_host)) {
623 start_nfs_pings(fs, pingval);
624 fs->fs_refc++;
625 return fs;
626 }
627 }
628
629
630
631 /*
632 * Get here if we can't find an entry
633 */
634 if (hp) {
635 switch (hp->h_addrtype) {
636 case AF_INET:
637 ip = ALLOC(sockaddr_in);
638 bzero(ip, sizeof(*ip));
639 ip->sin_family = AF_INET;
640 bcopy(hp->h_addr, &ip->sin_addr, sizeof(ip->sin_addr));
641
642 ip->sin_port = htons(NFS_PORT);
643 break;
644
645 default:
646 ip = 0;
647 break;
648 }
649 } else {
650 plog(XLOG_USER, "Unknown host: %s", host);
651 ip = 0;
652 }
653
654 /*
655 * Allocate a new server
656 */
657 fs = ALLOC(fserver);
658 fs->fs_refc = 1;
659 fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname");
660 if (normalize_hosts) host_normalize(&fs->fs_host);
661 fs->fs_ip = ip;
662 fs->fs_cid = 0;
663 if (ip) {
664 fs->fs_flags = FSF_DOWN; /* Starts off down */
665 } else {
666 fs->fs_flags = FSF_ERROR|FSF_VALID;
667 mf->mf_flags |= MFF_ERROR;
668 mf->mf_error = ENOENT;
669 }
670 fs->fs_type = "nfs";
671 fs->fs_pinger = AM_PINGER;
672 np = ALLOC(nfs_private);
673 bzero(np, sizeof(*np));
674 np->np_mountd_inval = TRUE;
675 np->np_xid = NPXID_ALLOC();
676 np->np_error = -1;
677 /*
678 * Initially the server will be deemed dead after
679 * MAX_ALLOWED_PINGS of the fast variety have failed.
680 */
681 np->np_ttl = clocktime() + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1;
682 fs->fs_private = np;
683 fs->fs_prfree = free;
684
685 if (!(fs->fs_flags & FSF_ERROR)) {
686 /*
687 * Start of keepalive timer
688 */
689 start_nfs_pings(fs, pingval);
690 }
691
692 /*
693 * Add to list of servers
694 */
695 ins_que(&fs->fs_q, &nfs_srvr_list);
696
697 return fs;
698 }
699