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