xref: /original-bsd/usr.sbin/amd/amd/srvr_nfs.c (revision c3e32dec)
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));
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);
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));
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));
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));
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));
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));
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));
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));
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));
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));
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