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