GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/amd/amd/srvr_nfs.c Lines: 0 195 0.0 %
Date: 2017-11-07 Branches: 0 116 0.0 %

Line Branch Exec Source
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
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
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
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
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
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
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
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
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
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
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 *
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
}