xref: /freebsd/contrib/tcpdump/print-nfs.c (revision e627b39baccd1ec9129690167cf5e6d860509655)
1 /*
2  * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that: (1) source code distributions
7  * retain the above copyright notice and this paragraph in its entirety, (2)
8  * distributions including binary code include the above copyright notice and
9  * this paragraph in its entirety in the documentation or other materials
10  * provided with the distribution, and (3) all advertising materials mentioning
11  * features or use of this software display the following acknowledgement:
12  * ``This product includes software developed by the University of California,
13  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14  * the University nor the names of its contributors may be used to endorse
15  * or promote products derived from this software without specific prior
16  * written permission.
17  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20  */
21 
22 #ifndef lint
23 static char rcsid[] =
24     "@(#) $Header: print-nfs.c,v 1.56 96/07/23 14:17:25 leres Exp $ (LBL)";
25 #endif
26 
27 #include <sys/param.h>
28 #include <sys/time.h>
29 #include <sys/socket.h>
30 
31 #if __STDC__
32 struct mbuf;
33 struct rtentry;
34 #endif
35 #include <net/if.h>
36 
37 #include <netinet/in.h>
38 #include <netinet/if_ether.h>
39 #include <netinet/in_systm.h>
40 #include <netinet/ip.h>
41 #include <netinet/ip_var.h>
42 
43 #include <rpc/rpc.h>
44 
45 #include <ctype.h>
46 #include <pcap.h>
47 #include <stdio.h>
48 #include <string.h>
49 
50 #include "interface.h"
51 #include "addrtoname.h"
52 
53 #include "nfsv2.h"
54 #include "nfsfh.h"
55 
56 static void nfs_printfh(const u_int32_t *);
57 static void xid_map_enter(const struct rpc_msg *, const struct ip *);
58 static int32_t xid_map_find(const struct rpc_msg *, const struct ip *);
59 static void interp_reply(const struct rpc_msg *, u_int32_t, u_int);
60 
61 void
62 nfsreply_print(register const u_char *bp, u_int length,
63 	       register const u_char *bp2)
64 {
65 	register const struct rpc_msg *rp;
66 	register const struct ip *ip;
67 	int32_t proc;
68 
69 	rp = (const struct rpc_msg *)bp;
70 	ip = (const struct ip *)bp2;
71 
72 	if (!nflag)
73 		(void)printf("%s.nfs > %s.%x: reply %s %d",
74 			     ipaddr_string(&ip->ip_src),
75 			     ipaddr_string(&ip->ip_dst),
76 			     (u_int32_t)ntohl(rp->rm_xid),
77 			     ntohl(rp->rm_reply.rp_stat) == MSG_ACCEPTED?
78 				     "ok":"ERR",
79 			     length);
80 	else
81 		(void)printf("%s.%x > %s.%x: reply %s %d",
82 			     ipaddr_string(&ip->ip_src),
83 			     NFS_PORT,
84 			     ipaddr_string(&ip->ip_dst),
85 			     (u_int32_t)ntohl(rp->rm_xid),
86 			     ntohl(rp->rm_reply.rp_stat) == MSG_ACCEPTED?
87 			     	"ok":"ERR",
88 			     length);
89 
90 	proc = xid_map_find(rp, ip);
91 	if (proc >= 0)
92 		interp_reply(rp, (u_int32_t)proc, length);
93 }
94 
95 /*
96  * Return a pointer to the first file handle in the packet.
97  * If the packet was truncated, return 0.
98  */
99 static const u_int32_t *
100 parsereq(register const struct rpc_msg *rp, register u_int length)
101 {
102 	register const u_int32_t *dp;
103 	register u_int len;
104 
105 	/*
106 	 * find the start of the req data (if we captured it)
107 	 */
108 	dp = (u_int32_t *)&rp->rm_call.cb_cred;
109 	TCHECK(dp[1]);
110 	len = ntohl(dp[1]);
111 	if (len < length) {
112 		dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
113 		TCHECK(dp[1]);
114 		len = ntohl(dp[1]);
115 		if (len < length) {
116 			dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
117 			TCHECK2(dp[0], 0);
118 			return (dp);
119 		}
120 	}
121 trunc:
122 	return (0);
123 }
124 
125 /*
126  * Print out an NFS file handle and return a pointer to following word.
127  * If packet was truncated, return 0.
128  */
129 static const u_int32_t *
130 parsefh(register const u_int32_t *dp)
131 {
132 	if (dp + 8 <= (u_int32_t *)snapend) {
133 		nfs_printfh(dp);
134 		return (dp + 8);
135 	}
136 	return (0);
137 }
138 
139 /*
140  * Print out a file name and return pointer to 32-bit word past it.
141  * If packet was truncated, return 0.
142  */
143 static const u_int32_t *
144 parsefn(register const u_int32_t *dp)
145 {
146 	register u_int32_t len;
147 	register const u_char *cp;
148 
149 	/* Bail if we don't have the string length */
150 	if ((u_char *)dp > snapend - sizeof(*dp))
151 		return(0);
152 
153 	/* Fetch string length; convert to host order */
154 	len = *dp++;
155 	NTOHL(len);
156 
157 	cp = (u_char *)dp;
158 	/* Update 32-bit pointer (NFS filenames padded to 32-bit boundaries) */
159 	dp += ((len + 3) & ~3) / sizeof(*dp);
160 	if ((u_char *)dp > snapend)
161 		return (0);
162 	/* XXX seems like we should be checking the length */
163 	putchar('"');
164 	(void) fn_printn(cp, len, NULL);
165 	putchar('"');
166 
167 	return (dp);
168 }
169 
170 /*
171  * Print out file handle and file name.
172  * Return pointer to 32-bit word past file name.
173  * If packet was truncated (or there was some other error), return 0.
174  */
175 static const u_int32_t *
176 parsefhn(register const u_int32_t *dp)
177 {
178 	dp = parsefh(dp);
179 	if (dp == 0)
180 		return (0);
181 	putchar(' ');
182 	return (parsefn(dp));
183 }
184 
185 void
186 nfsreq_print(register const u_char *bp, u_int length,
187     register const u_char *bp2)
188 {
189 	register const struct rpc_msg *rp;
190 	register const struct ip *ip;
191 	register const u_int32_t *dp;
192 
193 	rp = (const struct rpc_msg *)bp;
194 	ip = (const struct ip *)bp2;
195 	if (!nflag)
196 		(void)printf("%s.%x > %s.nfs: %d",
197 			     ipaddr_string(&ip->ip_src),
198 			     (u_int32_t)ntohl(rp->rm_xid),
199 			     ipaddr_string(&ip->ip_dst),
200 			     length);
201 	else
202 		(void)printf("%s.%x > %s.%x: %d",
203 			     ipaddr_string(&ip->ip_src),
204 			     (u_int32_t)ntohl(rp->rm_xid),
205 			     ipaddr_string(&ip->ip_dst),
206 			     NFS_PORT,
207 			     length);
208 
209 	xid_map_enter(rp, ip);	/* record proc number for later on */
210 
211 	switch (ntohl(rp->rm_call.cb_proc)) {
212 #ifdef NFSPROC_NOOP
213 	case NFSPROC_NOOP:
214 		printf(" nop");
215 		return;
216 #else
217 #define NFSPROC_NOOP -1
218 #endif
219 	case NFSPROC_NULL:
220 		printf(" null");
221 		return;
222 
223 	case NFSPROC_GETATTR:
224 		printf(" getattr");
225 		if ((dp = parsereq(rp, length)) != 0 && parsefh(dp) != 0)
226 			return;
227 		break;
228 
229 	case NFSPROC_SETATTR:
230 		printf(" setattr");
231 		if ((dp = parsereq(rp, length)) != 0 && parsefh(dp) != 0)
232 			return;
233 		break;
234 
235 #if NFSPROC_ROOT != NFSPROC_NOOP
236 	case NFSPROC_ROOT:
237 		printf(" root");
238 		break;
239 #endif
240 	case NFSPROC_LOOKUP:
241 		printf(" lookup");
242 		if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp) != 0)
243 			return;
244 		break;
245 
246 	case NFSPROC_READLINK:
247 		printf(" readlink");
248 		if ((dp = parsereq(rp, length)) != 0 && parsefh(dp) != 0)
249 			return;
250 		break;
251 
252 	case NFSPROC_READ:
253 		printf(" read");
254 		if ((dp = parsereq(rp, length)) != 0 &&
255 		    (dp = parsefh(dp)) != 0) {
256 			TCHECK2(dp[0], 3 * sizeof(*dp));
257 			printf(" %u bytes @ %u",
258 			    (u_int32_t)ntohl(dp[1]),
259 			    (u_int32_t)ntohl(dp[0]));
260 			return;
261 		}
262 		break;
263 
264 #if NFSPROC_WRITECACHE != NFSPROC_NOOP
265 	case NFSPROC_WRITECACHE:
266 		printf(" writecache");
267 		if ((dp = parsereq(rp, length)) != 0 &&
268 		    (dp = parsefh(dp)) != 0) {
269 			TCHECK2(dp[0], 4 * sizeof(*dp));
270 			printf(" %u (%u) bytes @ %u (%u)",
271 			    (u_int32_t)ntohl(dp[3]),
272 			    (u_int32_t)ntohl(dp[2]),
273 			    (u_int32_t)ntohl(dp[1]),
274 			    (u_int32_t)ntohl(dp[0]));
275 			return;
276 		}
277 		break;
278 #endif
279 	case NFSPROC_WRITE:
280 		printf(" write");
281 		if ((dp = parsereq(rp, length)) != 0 &&
282 		    (dp = parsefh(dp)) != 0) {
283 			TCHECK2(dp[0], 4 * sizeof(*dp));
284 			printf(" %u (%u) bytes @ %u (%u)",
285 			    (u_int32_t)ntohl(dp[3]),
286 			    (u_int32_t)ntohl(dp[2]),
287 			    (u_int32_t)ntohl(dp[1]),
288 			    (u_int32_t)ntohl(dp[0]));
289 			return;
290 		}
291 		break;
292 
293 	case NFSPROC_CREATE:
294 		printf(" create");
295 		if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp) != 0)
296 			return;
297 		break;
298 
299 	case NFSPROC_REMOVE:
300 		printf(" remove");
301 		if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp) != 0)
302 			return;
303 		break;
304 
305 	case NFSPROC_RENAME:
306 		printf(" rename");
307 		if ((dp = parsereq(rp, length)) != 0 &&
308 		    (dp = parsefhn(dp)) != 0) {
309 			fputs(" ->", stdout);
310 			if (parsefhn(dp) != 0)
311 				return;
312 		}
313 		break;
314 
315 	case NFSPROC_LINK:
316 		printf(" link");
317 		if ((dp = parsereq(rp, length)) != 0 &&
318 		    (dp = parsefh(dp)) != 0) {
319 			fputs(" ->", stdout);
320 			if (parsefhn(dp) != 0)
321 				return;
322 		}
323 		break;
324 
325 	case NFSPROC_SYMLINK:
326 		printf(" symlink");
327 		if ((dp = parsereq(rp, length)) != 0 &&
328 		    (dp = parsefhn(dp)) != 0) {
329 			fputs(" -> ", stdout);
330 			if (parsefn(dp) != 0)
331 				return;
332 		}
333 		break;
334 
335 	case NFSPROC_MKDIR:
336 		printf(" mkdir");
337 		if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp) != 0)
338 			return;
339 		break;
340 
341 	case NFSPROC_RMDIR:
342 		printf(" rmdir");
343 		if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp) != 0)
344 			return;
345 		break;
346 
347 	case NFSPROC_READDIR:
348 		printf(" readdir");
349 		if ((dp = parsereq(rp, length)) != 0 &&
350 		    (dp = parsefh(dp)) != 0) {
351 			TCHECK2(dp[0], 2 * sizeof(*dp));
352 			/*
353 			 * Print the offset as signed, since -1 is common,
354 			 * but offsets > 2^31 aren't.
355 			 */
356 			printf(" %u bytes @ %d",
357 			    (u_int32_t)ntohl(dp[1]),
358 			    (u_int32_t)ntohl(dp[0]));
359 			return;
360 		}
361 		break;
362 
363 	case NFSPROC_STATFS:
364 		printf(" statfs");
365 		if ((dp = parsereq(rp, length)) != 0 && parsefh(dp) != 0)
366 			return;
367 		break;
368 
369 	default:
370 		printf(" proc-%u", (u_int32_t)ntohl(rp->rm_call.cb_proc));
371 		return;
372 	}
373 trunc:
374 	fputs(" [|nfs]", stdout);
375 }
376 
377 /*
378  * Print out an NFS file handle.
379  * We assume packet was not truncated before the end of the
380  * file handle pointed to by dp.
381  *
382  * Note: new version (using portable file-handle parser) doesn't produce
383  * generation number.  It probably could be made to do that, with some
384  * additional hacking on the parser code.
385  */
386 static void
387 nfs_printfh(register const u_int32_t *dp)
388 {
389 	my_fsid fsid;
390 	ino_t ino;
391 	char *sfsname = NULL;
392 
393 	Parse_fh((caddr_t*)dp, &fsid, &ino, NULL, &sfsname, 0);
394 
395 	if (sfsname) {
396 	    /* file system ID is ASCII, not numeric, for this server OS */
397 	    static char temp[NFS_FHSIZE+1];
398 
399 	    /* Make sure string is null-terminated */
400 	    strncpy(temp, sfsname, NFS_FHSIZE);
401 	    /* Remove trailing spaces */
402 	    sfsname = strchr(temp, ' ');
403 	    if (sfsname)
404 		*sfsname = 0;
405 
406 	    (void)printf(" fh %s/%u", temp, (u_int32_t)ino);
407 	}
408 	else {
409 	    (void)printf(" fh %u,%u/%u",
410 		fsid.fsid_dev.Major,
411 		fsid.fsid_dev.Minor,
412 		(u_int32_t)ino);
413 	}
414 }
415 
416 /*
417  * Maintain a small cache of recent client.XID.server/proc pairs, to allow
418  * us to match up replies with requests and thus to know how to parse
419  * the reply.
420  */
421 
422 struct xid_map_entry {
423 	u_int32_t		xid;		/* transaction ID (net order) */
424 	struct in_addr	client;		/* client IP address (net order) */
425 	struct in_addr	server;		/* server IP address (net order) */
426 	u_int32_t		proc;		/* call proc number (host order) */
427 };
428 
429 /*
430  * Map entries are kept in an array that we manage as a ring;
431  * new entries are always added at the tail of the ring.  Initially,
432  * all the entries are zero and hence don't match anything.
433  */
434 
435 #define	XIDMAPSIZE	64
436 
437 struct xid_map_entry xid_map[XIDMAPSIZE];
438 
439 int	xid_map_next = 0;
440 int	xid_map_hint = 0;
441 
442 static void
443 xid_map_enter(const struct rpc_msg *rp, const struct ip *ip)
444 {
445 	struct xid_map_entry *xmep;
446 
447 	xmep = &xid_map[xid_map_next];
448 
449 	if (++xid_map_next >= XIDMAPSIZE)
450 		xid_map_next = 0;
451 
452 	xmep->xid = rp->rm_xid;
453 	xmep->client = ip->ip_src;
454 	xmep->server = ip->ip_dst;
455 	xmep->proc = ntohl(rp->rm_call.cb_proc);
456 }
457 
458 /* Returns NFSPROC_xxx or -1 on failure */
459 static int32_t
460 xid_map_find(const struct rpc_msg *rp, const struct ip *ip)
461 {
462 	int i;
463 	struct xid_map_entry *xmep;
464 	u_int32_t xid = rp->rm_xid;
465 	u_int32_t clip = ip->ip_dst.s_addr;
466 	u_int32_t sip = ip->ip_src.s_addr;
467 
468 	/* Start searching from where we last left off */
469 	i = xid_map_hint;
470 	do {
471 		xmep = &xid_map[i];
472 		if (xmep->xid == xid && xmep->client.s_addr == clip &&
473 		    xmep->server.s_addr == sip) {
474 			/* match */
475 			xid_map_hint = i;
476 			return ((int32_t)xmep->proc);
477 		}
478 		if (++i >= XIDMAPSIZE)
479 			i = 0;
480 	} while (i != xid_map_hint);
481 
482 	/* search failed */
483 	return(-1);
484 }
485 
486 /*
487  * Routines for parsing reply packets
488  */
489 
490 /*
491  * Return a pointer to the beginning of the actual results.
492  * If the packet was truncated, return 0.
493  */
494 static const u_int32_t *
495 parserep(register const struct rpc_msg *rp, register u_int length)
496 {
497 	register const u_int32_t *dp;
498 	u_int len;
499 	enum accept_stat astat;
500 
501 	/*
502 	 * Portability note:
503 	 * Here we find the address of the ar_verf credentials.
504 	 * Originally, this calculation was
505 	 *	dp = (u_int32_t *)&rp->rm_reply.rp_acpt.ar_verf
506 	 * On the wire, the rp_acpt field starts immediately after
507 	 * the (32 bit) rp_stat field.  However, rp_acpt (which is a
508 	 * "struct accepted_reply") contains a "struct opaque_auth",
509 	 * whose internal representation contains a pointer, so on a
510 	 * 64-bit machine the compiler inserts 32 bits of padding
511 	 * before rp->rm_reply.rp_acpt.ar_verf.  So, we cannot use
512 	 * the internal representation to parse the on-the-wire
513 	 * representation.  Instead, we skip past the rp_stat field,
514 	 * which is an "enum" and so occupies one 32-bit word.
515 	 */
516 	dp = ((const u_int32_t *)&rp->rm_reply) + 1;
517 	TCHECK2(dp[0], 1);
518 		return(0);
519 	len = ntohl(dp[1]);
520 	if (len >= length)
521 		return(0);
522 	/*
523 	 * skip past the ar_verf credentials.
524 	 */
525 	dp += (len + (2*sizeof(u_int32_t) + 3)) / sizeof(u_int32_t);
526 	TCHECK2(dp[0], 0);
527 
528 	/*
529 	 * now we can check the ar_stat field
530 	 */
531 	astat = ntohl(*(enum accept_stat *)dp);
532 	switch (astat) {
533 
534 	case SUCCESS:
535 		break;
536 
537 	case PROG_UNAVAIL:
538 		printf(" PROG_UNAVAIL");
539 		return(0);
540 
541 	case PROG_MISMATCH:
542 		printf(" PROG_MISMATCH");
543 		return(0);
544 
545 	case PROC_UNAVAIL:
546 		printf(" PROC_UNAVAIL");
547 		return(0);
548 
549 	case GARBAGE_ARGS:
550 		printf(" GARBAGE_ARGS");
551 		return(0);
552 
553 	case SYSTEM_ERR:
554 		printf(" SYSTEM_ERR");
555 		return(0);
556 
557 	default:
558 		printf(" ar_stat %d", astat);
559 		return(0);
560 	}
561 	/* successful return */
562 	if ((sizeof(astat) + ((u_char *)dp)) < snapend)
563 		return((u_int32_t *) (sizeof(astat) + ((char *)dp)));
564 
565 trunc:
566 	return (0);
567 }
568 
569 static const u_int32_t *
570 parsestatus(const u_int32_t *dp)
571 {
572 	int errnum;
573 
574 	TCHECK(dp[0]);
575 	errnum = ntohl(dp[0]);
576 	if (errnum != 0) {
577 		char *errmsg;
578 
579 		if (qflag)
580 			return(0);
581 
582 		errmsg = pcap_strerror(errnum);
583 		printf(" ERROR: %s", errmsg);
584 		return(0);
585 	}
586 	return (dp + 1);
587 trunc:
588 	return (0);
589 }
590 
591 static struct tok type2str[] = {
592 	{ NFNON,	"NON" },
593 	{ NFREG,	"REG" },
594 	{ NFDIR,	"DIR" },
595 	{ NFBLK,	"BLK" },
596 	{ NFCHR,	"CHR" },
597 	{ NFLNK,	"LNK" },
598 	{ 0,		NULL }
599 };
600 
601 static const u_int32_t *
602 parsefattr(const u_int32_t *dp, int verbose)
603 {
604 	const struct nfsv2_fattr *fap;
605 
606 	fap = (const struct nfsv2_fattr *)dp;
607 	if (verbose) {
608 		TCHECK(fap->fa_nfssize);
609 		printf(" %s %o ids %u/%u sz %u ",
610 		    tok2str(type2str, "unk-ft %d ",
611 		    (u_int32_t)ntohl(fap->fa_type)),
612 		    (u_int32_t)ntohl(fap->fa_mode),
613 		    (u_int32_t)ntohl(fap->fa_uid),
614 		    (u_int32_t)ntohl(fap->fa_gid),
615 		    (u_int32_t)ntohl(fap->fa_nfssize));
616 	}
617 	/* print lots more stuff */
618 	if (verbose > 1) {
619 		TCHECK(fap->fa_nfsfileid);
620 		printf("nlink %u rdev %x fsid %x nodeid %x a/m/ctime ",
621 		    (u_int32_t)ntohl(fap->fa_nlink),
622 		    (u_int32_t)ntohl(fap->fa_nfsrdev),
623 		    (u_int32_t)ntohl(fap->fa_nfsfsid),
624 		    (u_int32_t)ntohl(fap->fa_nfsfileid));
625 		TCHECK(fap->fa_nfsatime);
626 		printf("%u.%06u ",
627 		    (u_int32_t)ntohl(fap->fa_nfsatime.nfs_sec),
628 		    (u_int32_t)ntohl(fap->fa_nfsatime.nfs_usec));
629 		TCHECK(fap->fa_nfsmtime);
630 		printf("%u.%06u ",
631 		    (u_int32_t)ntohl(fap->fa_nfsmtime.nfs_sec),
632 		    (u_int32_t)ntohl(fap->fa_nfsmtime.nfs_usec));
633 		TCHECK(fap->fa_nfsctime);
634 		printf("%u.%06u ",
635 		    (u_int32_t)ntohl(fap->fa_nfsctime.nfs_sec),
636 		    (u_int32_t)ntohl(fap->fa_nfsctime.nfs_usec));
637 	}
638 	return ((const u_int32_t *)&fap[1]);
639 trunc:
640 	return (NULL);
641 }
642 
643 static int
644 parseattrstat(const u_int32_t *dp, int verbose)
645 {
646 	dp = parsestatus(dp);
647 	if (dp == NULL)
648 		return (0);
649 
650 	return (parsefattr(dp, verbose) != NULL);
651 }
652 
653 static int
654 parsediropres(const u_int32_t *dp)
655 {
656 	dp = parsestatus(dp);
657 	if (dp == NULL)
658 		return (0);
659 
660 	dp = parsefh(dp);
661 	if (dp == NULL)
662 		return (0);
663 
664 	return (parsefattr(dp, vflag) != NULL);
665 }
666 
667 static int
668 parselinkres(const u_int32_t *dp)
669 {
670 	dp = parsestatus(dp);
671 	if (dp == NULL)
672 		return(0);
673 
674 	putchar(' ');
675 	return (parsefn(dp) != NULL);
676 }
677 
678 static int
679 parsestatfs(const u_int32_t *dp)
680 {
681 	const struct nfsv2_statfs *sfsp;
682 
683 	dp = parsestatus(dp);
684 	if (dp == NULL)
685 		return(0);
686 
687 	if (!qflag) {
688 		sfsp = (const struct nfsv2_statfs *)dp;
689 		TCHECK(sfsp->sf_bavail);
690 		printf(" tsize %u bsize %u blocks %u bfree %u bavail %u",
691 		    (u_int32_t)ntohl(sfsp->sf_tsize),
692 		    (u_int32_t)ntohl(sfsp->sf_bsize),
693 		    (u_int32_t)ntohl(sfsp->sf_blocks),
694 		    (u_int32_t)ntohl(sfsp->sf_bfree),
695 		    (u_int32_t)ntohl(sfsp->sf_bavail));
696 	}
697 
698 	return (1);
699 trunc:
700 	return (0);
701 }
702 
703 static int
704 parserddires(const u_int32_t *dp)
705 {
706 	dp = parsestatus(dp);
707 	if (dp == 0)
708 		return (0);
709 	if (!qflag) {
710 		TCHECK(dp[0]);
711 		printf(" offset %x", (u_int32_t)ntohl(dp[0]));
712 		TCHECK(dp[1]);
713 		printf(" size %u", (u_int32_t)ntohl(dp[1]));
714 		TCHECK(dp[2]);
715 		if (dp[2] != 0)
716 			printf(" eof");
717 	}
718 
719 	return (1);
720 trunc:
721 	return (0);
722 }
723 
724 static void
725 interp_reply(const struct rpc_msg *rp, u_int32_t proc, u_int length)
726 {
727 	register const u_int32_t *dp;
728 
729 	switch (proc) {
730 
731 #ifdef NFSPROC_NOOP
732 	case NFSPROC_NOOP:
733 		printf(" nop");
734 		return;
735 #else
736 #define NFSPROC_NOOP -1
737 #endif
738 	case NFSPROC_NULL:
739 		printf(" null");
740 		return;
741 
742 	case NFSPROC_GETATTR:
743 		printf(" getattr");
744 		dp = parserep(rp, length);
745 		if (dp != 0 && parseattrstat(dp, !qflag) != 0)
746 			return;
747 		break;
748 
749 	case NFSPROC_SETATTR:
750 		printf(" setattr");
751 		dp = parserep(rp, length);
752 		if (dp != 0 && parseattrstat(dp, !qflag) != 0)
753 			return;
754 		break;
755 
756 #if NFSPROC_ROOT != NFSPROC_NOOP
757 	case NFSPROC_ROOT:
758 		printf(" root");
759 		break;
760 #endif
761 	case NFSPROC_LOOKUP:
762 		printf(" lookup");
763 		dp = parserep(rp, length);
764 		if (dp != 0 && parsediropres(dp) != 0)
765 			return;
766 		break;
767 
768 	case NFSPROC_READLINK:
769 		printf(" readlink");
770 		dp = parserep(rp, length);
771 		if (dp != 0 && parselinkres(dp) != 0)
772 			return;
773 		break;
774 
775 	case NFSPROC_READ:
776 		printf(" read");
777 		dp = parserep(rp, length);
778 		if (dp != 0 && parseattrstat(dp, vflag) != 0)
779 			return;
780 		break;
781 
782 #if NFSPROC_WRITECACHE != NFSPROC_NOOP
783 	case NFSPROC_WRITECACHE:
784 		printf(" writecache");
785 		break;
786 #endif
787 	case NFSPROC_WRITE:
788 		printf(" write");
789 		dp = parserep(rp, length);
790 		if (dp != 0 && parseattrstat(dp, vflag) != 0)
791 			return;
792 		break;
793 
794 	case NFSPROC_CREATE:
795 		printf(" create");
796 		dp = parserep(rp, length);
797 		if (dp != 0 && parsediropres(dp) != 0)
798 			return;
799 		break;
800 
801 	case NFSPROC_REMOVE:
802 		printf(" remove");
803 		dp = parserep(rp, length);
804 		if (dp != 0 && parsestatus(dp) != 0)
805 			return;
806 		break;
807 
808 	case NFSPROC_RENAME:
809 		printf(" rename");
810 		dp = parserep(rp, length);
811 		if (dp != 0 && parsestatus(dp) != 0)
812 			return;
813 		break;
814 
815 	case NFSPROC_LINK:
816 		printf(" link");
817 		dp = parserep(rp, length);
818 		if (dp != 0 && parsestatus(dp) != 0)
819 			return;
820 		break;
821 
822 	case NFSPROC_SYMLINK:
823 		printf(" symlink");
824 		dp = parserep(rp, length);
825 		if (dp != 0 && parsestatus(dp) != 0)
826 			return;
827 		break;
828 
829 	case NFSPROC_MKDIR:
830 		printf(" mkdir");
831 		dp = parserep(rp, length);
832 		if (dp != 0 && parsediropres(dp) != 0)
833 			return;
834 		break;
835 
836 	case NFSPROC_RMDIR:
837 		printf(" rmdir");
838 		dp = parserep(rp, length);
839 		if (dp != 0 && parsestatus(dp) != 0)
840 			return;
841 		break;
842 
843 	case NFSPROC_READDIR:
844 		printf(" readdir");
845 		dp = parserep(rp, length);
846 		if (dp != 0 && parserddires(dp) != 0)
847 			return;
848 		break;
849 
850 	case NFSPROC_STATFS:
851 		printf(" statfs");
852 		dp = parserep(rp, length);
853 		if (dp != 0 && parsestatfs(dp) != 0)
854 			return;
855 		break;
856 
857 	default:
858 		printf(" proc-%u", proc);
859 		return;
860 	}
861 	fputs(" [|nfs]", stdout);
862 }
863