xref: /freebsd/contrib/tcpdump/print-nfs.c (revision 409a390c3341fb4f162cd7de1fd595a323ebbfd8)
1 /*
2  * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
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  * $FreeBSD$
22  */
23 
24 #ifndef lint
25 static const char rcsid[] _U_ =
26     "@(#) $Header: /tcpdump/master/tcpdump/print-nfs.c,v 1.110.2.1 2007-12-22 03:08:45 guy Exp $ (LBL)";
27 #endif
28 
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #include <tcpdump-stdinc.h>
34 
35 #include <pcap.h>
36 #include <stdio.h>
37 #include <string.h>
38 
39 #include "interface.h"
40 #include "addrtoname.h"
41 #include "extract.h"
42 
43 #include "nfs.h"
44 #include "nfsfh.h"
45 
46 #include "ip.h"
47 #ifdef INET6
48 #include "ip6.h"
49 #endif
50 #include "rpc_auth.h"
51 #include "rpc_msg.h"
52 
53 static void nfs_printfh(const u_int32_t *, const u_int);
54 static int xid_map_enter(const struct sunrpc_msg *, const u_char *);
55 static int32_t xid_map_find(const struct sunrpc_msg *, const u_char *,
56 			    u_int32_t *, u_int32_t *);
57 static void interp_reply(const struct sunrpc_msg *, u_int32_t, u_int32_t, int);
58 static const u_int32_t *parse_post_op_attr(const u_int32_t *, int);
59 static void print_sattr3(const struct nfsv3_sattr *sa3, int verbose);
60 static void print_nfsaddr(const u_char *, const char *, const char *);
61 
62 /*
63  * Mapping of old NFS Version 2 RPC numbers to generic numbers.
64  */
65 u_int32_t nfsv3_procid[NFS_NPROCS] = {
66 	NFSPROC_NULL,
67 	NFSPROC_GETATTR,
68 	NFSPROC_SETATTR,
69 	NFSPROC_NOOP,
70 	NFSPROC_LOOKUP,
71 	NFSPROC_READLINK,
72 	NFSPROC_READ,
73 	NFSPROC_NOOP,
74 	NFSPROC_WRITE,
75 	NFSPROC_CREATE,
76 	NFSPROC_REMOVE,
77 	NFSPROC_RENAME,
78 	NFSPROC_LINK,
79 	NFSPROC_SYMLINK,
80 	NFSPROC_MKDIR,
81 	NFSPROC_RMDIR,
82 	NFSPROC_READDIR,
83 	NFSPROC_FSSTAT,
84 	NFSPROC_NOOP,
85 	NFSPROC_NOOP,
86 	NFSPROC_NOOP,
87 	NFSPROC_NOOP,
88 	NFSPROC_NOOP,
89 	NFSPROC_NOOP,
90 	NFSPROC_NOOP,
91 	NFSPROC_NOOP
92 };
93 
94 /*
95  * NFS V2 and V3 status values.
96  *
97  * Some of these come from the RFCs for NFS V2 and V3, with the message
98  * strings taken from the FreeBSD C library "errlst.c".
99  *
100  * Others are errors that are not in the RFC but that I suspect some
101  * NFS servers could return; the values are FreeBSD errno values, as
102  * the first NFS server was the SunOS 2.0 one, and until 5.0 SunOS
103  * was primarily BSD-derived.
104  */
105 static struct tok status2str[] = {
106 	{ 1,     "Operation not permitted" },	/* EPERM */
107 	{ 2,     "No such file or directory" },	/* ENOENT */
108 	{ 5,     "Input/output error" },	/* EIO */
109 	{ 6,     "Device not configured" },	/* ENXIO */
110 	{ 11,    "Resource deadlock avoided" },	/* EDEADLK */
111 	{ 12,    "Cannot allocate memory" },	/* ENOMEM */
112 	{ 13,    "Permission denied" },		/* EACCES */
113 	{ 17,    "File exists" },		/* EEXIST */
114 	{ 18,    "Cross-device link" },		/* EXDEV */
115 	{ 19,    "Operation not supported by device" }, /* ENODEV */
116 	{ 20,    "Not a directory" },		/* ENOTDIR */
117 	{ 21,    "Is a directory" },		/* EISDIR */
118 	{ 22,    "Invalid argument" },		/* EINVAL */
119 	{ 26,    "Text file busy" },		/* ETXTBSY */
120 	{ 27,    "File too large" },		/* EFBIG */
121 	{ 28,    "No space left on device" },	/* ENOSPC */
122 	{ 30,    "Read-only file system" },	/* EROFS */
123 	{ 31,    "Too many links" },		/* EMLINK */
124 	{ 45,    "Operation not supported" },	/* EOPNOTSUPP */
125 	{ 62,    "Too many levels of symbolic links" }, /* ELOOP */
126 	{ 63,    "File name too long" },	/* ENAMETOOLONG */
127 	{ 66,    "Directory not empty" },	/* ENOTEMPTY */
128 	{ 69,    "Disc quota exceeded" },	/* EDQUOT */
129 	{ 70,    "Stale NFS file handle" },	/* ESTALE */
130 	{ 71,    "Too many levels of remote in path" }, /* EREMOTE */
131 	{ 99,    "Write cache flushed to disk" }, /* NFSERR_WFLUSH (not used) */
132 	{ 10001, "Illegal NFS file handle" },	/* NFS3ERR_BADHANDLE */
133 	{ 10002, "Update synchronization mismatch" }, /* NFS3ERR_NOT_SYNC */
134 	{ 10003, "READDIR/READDIRPLUS cookie is stale" }, /* NFS3ERR_BAD_COOKIE */
135 	{ 10004, "Operation not supported" },	/* NFS3ERR_NOTSUPP */
136 	{ 10005, "Buffer or request is too small" }, /* NFS3ERR_TOOSMALL */
137 	{ 10006, "Unspecified error on server" }, /* NFS3ERR_SERVERFAULT */
138 	{ 10007, "Object of that type not supported" }, /* NFS3ERR_BADTYPE */
139 	{ 10008, "Request couldn't be completed in time" }, /* NFS3ERR_JUKEBOX */
140 	{ 0,     NULL }
141 };
142 
143 static struct tok nfsv3_writemodes[] = {
144 	{ 0,		"unstable" },
145 	{ 1,		"datasync" },
146 	{ 2,		"filesync" },
147 	{ 0,		NULL }
148 };
149 
150 static struct tok type2str[] = {
151 	{ NFNON,	"NON" },
152 	{ NFREG,	"REG" },
153 	{ NFDIR,	"DIR" },
154 	{ NFBLK,	"BLK" },
155 	{ NFCHR,	"CHR" },
156 	{ NFLNK,	"LNK" },
157 	{ NFFIFO,	"FIFO" },
158 	{ 0,		NULL }
159 };
160 
161 static void
162 print_nfsaddr(const u_char *bp, const char *s, const char *d)
163 {
164 	struct ip *ip;
165 #ifdef INET6
166 	struct ip6_hdr *ip6;
167 	char srcaddr[INET6_ADDRSTRLEN], dstaddr[INET6_ADDRSTRLEN];
168 #else
169 #ifndef INET_ADDRSTRLEN
170 #define INET_ADDRSTRLEN	16
171 #endif
172 	char srcaddr[INET_ADDRSTRLEN], dstaddr[INET_ADDRSTRLEN];
173 #endif
174 
175 	srcaddr[0] = dstaddr[0] = '\0';
176 	switch (IP_V((struct ip *)bp)) {
177 	case 4:
178 		ip = (struct ip *)bp;
179 		strlcpy(srcaddr, ipaddr_string(&ip->ip_src), sizeof(srcaddr));
180 		strlcpy(dstaddr, ipaddr_string(&ip->ip_dst), sizeof(dstaddr));
181 		break;
182 #ifdef INET6
183 	case 6:
184 		ip6 = (struct ip6_hdr *)bp;
185 		strlcpy(srcaddr, ip6addr_string(&ip6->ip6_src),
186 		    sizeof(srcaddr));
187 		strlcpy(dstaddr, ip6addr_string(&ip6->ip6_dst),
188 		    sizeof(dstaddr));
189 		break;
190 #endif
191 	default:
192 		strlcpy(srcaddr, "?", sizeof(srcaddr));
193 		strlcpy(dstaddr, "?", sizeof(dstaddr));
194 		break;
195 	}
196 
197 	(void)printf("%s.%s > %s.%s: ", srcaddr, s, dstaddr, d);
198 }
199 
200 static const u_int32_t *
201 parse_sattr3(const u_int32_t *dp, struct nfsv3_sattr *sa3)
202 {
203 	TCHECK(dp[0]);
204 	sa3->sa_modeset = EXTRACT_32BITS(dp);
205 	dp++;
206 	if (sa3->sa_modeset) {
207 		TCHECK(dp[0]);
208 		sa3->sa_mode = EXTRACT_32BITS(dp);
209 		dp++;
210 	}
211 
212 	TCHECK(dp[0]);
213 	sa3->sa_uidset = EXTRACT_32BITS(dp);
214 	dp++;
215 	if (sa3->sa_uidset) {
216 		TCHECK(dp[0]);
217 		sa3->sa_uid = EXTRACT_32BITS(dp);
218 		dp++;
219 	}
220 
221 	TCHECK(dp[0]);
222 	sa3->sa_gidset = EXTRACT_32BITS(dp);
223 	dp++;
224 	if (sa3->sa_gidset) {
225 		TCHECK(dp[0]);
226 		sa3->sa_gid = EXTRACT_32BITS(dp);
227 		dp++;
228 	}
229 
230 	TCHECK(dp[0]);
231 	sa3->sa_sizeset = EXTRACT_32BITS(dp);
232 	dp++;
233 	if (sa3->sa_sizeset) {
234 		TCHECK(dp[0]);
235 		sa3->sa_size = EXTRACT_32BITS(dp);
236 		dp++;
237 	}
238 
239 	TCHECK(dp[0]);
240 	sa3->sa_atimetype = EXTRACT_32BITS(dp);
241 	dp++;
242 	if (sa3->sa_atimetype == NFSV3SATTRTIME_TOCLIENT) {
243 		TCHECK(dp[1]);
244 		sa3->sa_atime.nfsv3_sec = EXTRACT_32BITS(dp);
245 		dp++;
246 		sa3->sa_atime.nfsv3_nsec = EXTRACT_32BITS(dp);
247 		dp++;
248 	}
249 
250 	TCHECK(dp[0]);
251 	sa3->sa_mtimetype = EXTRACT_32BITS(dp);
252 	dp++;
253 	if (sa3->sa_mtimetype == NFSV3SATTRTIME_TOCLIENT) {
254 		TCHECK(dp[1]);
255 		sa3->sa_mtime.nfsv3_sec = EXTRACT_32BITS(dp);
256 		dp++;
257 		sa3->sa_mtime.nfsv3_nsec = EXTRACT_32BITS(dp);
258 		dp++;
259 	}
260 
261 	return dp;
262 trunc:
263 	return NULL;
264 }
265 
266 static int nfserr;		/* true if we error rather than trunc */
267 
268 static void
269 print_sattr3(const struct nfsv3_sattr *sa3, int verbose)
270 {
271 	if (sa3->sa_modeset)
272 		printf(" mode %o", sa3->sa_mode);
273 	if (sa3->sa_uidset)
274 		printf(" uid %u", sa3->sa_uid);
275 	if (sa3->sa_gidset)
276 		printf(" gid %u", sa3->sa_gid);
277 	if (verbose > 1) {
278 		if (sa3->sa_atimetype == NFSV3SATTRTIME_TOCLIENT)
279 			printf(" atime %u.%06u", sa3->sa_atime.nfsv3_sec,
280 			       sa3->sa_atime.nfsv3_nsec);
281 		if (sa3->sa_mtimetype == NFSV3SATTRTIME_TOCLIENT)
282 			printf(" mtime %u.%06u", sa3->sa_mtime.nfsv3_sec,
283 			       sa3->sa_mtime.nfsv3_nsec);
284 	}
285 }
286 
287 void
288 nfsreply_print(register const u_char *bp, u_int length,
289 	       register const u_char *bp2)
290 {
291 	register const struct sunrpc_msg *rp;
292 	u_int32_t proc, vers, reply_stat;
293 	char srcid[20], dstid[20];	/*fits 32bit*/
294 	enum sunrpc_reject_stat rstat;
295 	u_int32_t rlow;
296 	u_int32_t rhigh;
297 	enum sunrpc_auth_stat rwhy;
298 
299 	nfserr = 0;		/* assume no error */
300 	rp = (const struct sunrpc_msg *)bp;
301 
302 	TCHECK(rp->rm_xid);
303 	if (!nflag) {
304 		strlcpy(srcid, "nfs", sizeof(srcid));
305 		snprintf(dstid, sizeof(dstid), "%u",
306 		    EXTRACT_32BITS(&rp->rm_xid));
307 	} else {
308 		snprintf(srcid, sizeof(srcid), "%u", NFS_PORT);
309 		snprintf(dstid, sizeof(dstid), "%u",
310 		    EXTRACT_32BITS(&rp->rm_xid));
311 	}
312 	print_nfsaddr(bp2, srcid, dstid);
313 	TCHECK(rp->rm_reply.rp_stat);
314 	reply_stat = EXTRACT_32BITS(&rp->rm_reply.rp_stat);
315 	switch (reply_stat) {
316 
317 	case SUNRPC_MSG_ACCEPTED:
318 		(void)printf("reply ok %u", length);
319 		if (xid_map_find(rp, bp2, &proc, &vers) >= 0)
320 			interp_reply(rp, proc, vers, length);
321 		break;
322 
323 	case SUNRPC_MSG_DENIED:
324 		(void)printf("reply ERR %u: ", length);
325 		TCHECK(rp->rm_reply.rp_reject.rj_stat);
326 		rstat = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_stat);
327 		switch (rstat) {
328 
329 		case SUNRPC_RPC_MISMATCH:
330 			TCHECK(rp->rm_reply.rp_reject.rj_vers.high);
331 			rlow = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_vers.low);
332 			rhigh = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_vers.high);
333 			(void)printf("RPC Version mismatch (%u-%u)",
334 			    rlow, rhigh);
335 			break;
336 
337 		case SUNRPC_AUTH_ERROR:
338 			TCHECK(rp->rm_reply.rp_reject.rj_why);
339 			rwhy = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_why);
340 			(void)printf("Auth ");
341 			switch (rwhy) {
342 
343 			case SUNRPC_AUTH_OK:
344 				(void)printf("OK");
345 				break;
346 
347 			case SUNRPC_AUTH_BADCRED:
348 				(void)printf("Bogus Credentials (seal broken)");
349 				break;
350 
351 			case SUNRPC_AUTH_REJECTEDCRED:
352 				(void)printf("Rejected Credentials (client should begin new session)");
353 				break;
354 
355 			case SUNRPC_AUTH_BADVERF:
356 				(void)printf("Bogus Verifier (seal broken)");
357 				break;
358 
359 			case SUNRPC_AUTH_REJECTEDVERF:
360 				(void)printf("Verifier expired or was replayed");
361 				break;
362 
363 			case SUNRPC_AUTH_TOOWEAK:
364 				(void)printf("Credentials are too weak");
365 				break;
366 
367 			case SUNRPC_AUTH_INVALIDRESP:
368 				(void)printf("Bogus response verifier");
369 				break;
370 
371 			case SUNRPC_AUTH_FAILED:
372 				(void)printf("Unknown failure");
373 				break;
374 
375 			default:
376 				(void)printf("Invalid failure code %u",
377 				    (unsigned int)rwhy);
378 				break;
379 			}
380 			break;
381 
382 		default:
383 			(void)printf("Unknown reason for rejecting rpc message %u",
384 			    (unsigned int)rstat);
385 			break;
386 		}
387 		break;
388 
389 	default:
390 		(void)printf("reply Unknown rpc response code=%u %u",
391 		    reply_stat, length);
392 		break;
393 	}
394 	return;
395 
396 trunc:
397 	if (!nfserr)
398 		fputs(" [|nfs]", stdout);
399 }
400 
401 /*
402  * Return a pointer to the first file handle in the packet.
403  * If the packet was truncated, return 0.
404  */
405 static const u_int32_t *
406 parsereq(register const struct sunrpc_msg *rp, register u_int length)
407 {
408 	register const u_int32_t *dp;
409 	register u_int len;
410 
411 	/*
412 	 * find the start of the req data (if we captured it)
413 	 */
414 	dp = (u_int32_t *)&rp->rm_call.cb_cred;
415 	TCHECK(dp[1]);
416 	len = EXTRACT_32BITS(&dp[1]);
417 	if (len < length) {
418 		dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
419 		TCHECK(dp[1]);
420 		len = EXTRACT_32BITS(&dp[1]);
421 		if (len < length) {
422 			dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
423 			TCHECK2(dp[0], 0);
424 			return (dp);
425 		}
426 	}
427 trunc:
428 	return (NULL);
429 }
430 
431 /*
432  * Print out an NFS file handle and return a pointer to following word.
433  * If packet was truncated, return 0.
434  */
435 static const u_int32_t *
436 parsefh(register const u_int32_t *dp, int v3)
437 {
438 	u_int len;
439 
440 	if (v3) {
441 		TCHECK(dp[0]);
442 		len = EXTRACT_32BITS(dp) / 4;
443 		dp++;
444 	} else
445 		len = NFSX_V2FH / 4;
446 
447 	if (TTEST2(*dp, len * sizeof(*dp))) {
448 		nfs_printfh(dp, len);
449 		return (dp + len);
450 	}
451 trunc:
452 	return (NULL);
453 }
454 
455 /*
456  * Print out a file name and return pointer to 32-bit word past it.
457  * If packet was truncated, return 0.
458  */
459 static const u_int32_t *
460 parsefn(register const u_int32_t *dp)
461 {
462 	register u_int32_t len;
463 	register const u_char *cp;
464 
465 	/* Bail if we don't have the string length */
466 	TCHECK(*dp);
467 
468 	/* Fetch string length; convert to host order */
469 	len = *dp++;
470 	NTOHL(len);
471 
472 	TCHECK2(*dp, ((len + 3) & ~3));
473 
474 	cp = (u_char *)dp;
475 	/* Update 32-bit pointer (NFS filenames padded to 32-bit boundaries) */
476 	dp += ((len + 3) & ~3) / sizeof(*dp);
477 	putchar('"');
478 	if (fn_printn(cp, len, snapend)) {
479 		putchar('"');
480 		goto trunc;
481 	}
482 	putchar('"');
483 
484 	return (dp);
485 trunc:
486 	return NULL;
487 }
488 
489 /*
490  * Print out file handle and file name.
491  * Return pointer to 32-bit word past file name.
492  * If packet was truncated (or there was some other error), return 0.
493  */
494 static const u_int32_t *
495 parsefhn(register const u_int32_t *dp, int v3)
496 {
497 	dp = parsefh(dp, v3);
498 	if (dp == NULL)
499 		return (NULL);
500 	putchar(' ');
501 	return (parsefn(dp));
502 }
503 
504 void
505 nfsreq_print(register const u_char *bp, u_int length,
506     register const u_char *bp2)
507 {
508 	register const struct sunrpc_msg *rp;
509 	register const u_int32_t *dp;
510 	nfs_type type;
511 	int v3;
512 	u_int32_t proc;
513 	struct nfsv3_sattr sa3;
514 	char srcid[20], dstid[20];	/*fits 32bit*/
515 
516 	nfserr = 0;		/* assume no error */
517 	rp = (const struct sunrpc_msg *)bp;
518 
519 	TCHECK(rp->rm_xid);
520 	if (!nflag) {
521 		snprintf(srcid, sizeof(srcid), "%u",
522 		    EXTRACT_32BITS(&rp->rm_xid));
523 		strlcpy(dstid, "nfs", sizeof(dstid));
524 	} else {
525 		snprintf(srcid, sizeof(srcid), "%u",
526 		    EXTRACT_32BITS(&rp->rm_xid));
527 		snprintf(dstid, sizeof(dstid), "%u", NFS_PORT);
528 	}
529 	print_nfsaddr(bp2, srcid, dstid);
530 	(void)printf("%d", length);
531 
532 	if (!xid_map_enter(rp, bp2))	/* record proc number for later on */
533 		goto trunc;
534 
535 	v3 = (EXTRACT_32BITS(&rp->rm_call.cb_vers) == NFS_VER3);
536 	proc = EXTRACT_32BITS(&rp->rm_call.cb_proc);
537 
538 	if (!v3 && proc < NFS_NPROCS)
539 		proc =  nfsv3_procid[proc];
540 
541 	switch (proc) {
542 	case NFSPROC_NOOP:
543 		printf(" nop");
544 		return;
545 	case NFSPROC_NULL:
546 		printf(" null");
547 		return;
548 
549 	case NFSPROC_GETATTR:
550 		printf(" getattr");
551 		if ((dp = parsereq(rp, length)) != NULL &&
552 		    parsefh(dp, v3) != NULL)
553 			return;
554 		break;
555 
556 	case NFSPROC_SETATTR:
557 		printf(" setattr");
558 		if ((dp = parsereq(rp, length)) != NULL &&
559 		    parsefh(dp, v3) != NULL)
560 			return;
561 		break;
562 
563 	case NFSPROC_LOOKUP:
564 		printf(" lookup");
565 		if ((dp = parsereq(rp, length)) != NULL &&
566 		    parsefhn(dp, v3) != NULL)
567 			return;
568 		break;
569 
570 	case NFSPROC_ACCESS:
571 		printf(" access");
572 		if ((dp = parsereq(rp, length)) != NULL &&
573 		    (dp = parsefh(dp, v3)) != NULL) {
574 			TCHECK(dp[0]);
575 			printf(" %04x", EXTRACT_32BITS(&dp[0]));
576 			return;
577 		}
578 		break;
579 
580 	case NFSPROC_READLINK:
581 		printf(" readlink");
582 		if ((dp = parsereq(rp, length)) != NULL &&
583 		    parsefh(dp, v3) != NULL)
584 			return;
585 		break;
586 
587 	case NFSPROC_READ:
588 		printf(" read");
589 		if ((dp = parsereq(rp, length)) != NULL &&
590 		    (dp = parsefh(dp, v3)) != NULL) {
591 			if (v3) {
592 				TCHECK(dp[2]);
593 				printf(" %u bytes @ %" PRIu64,
594 				       EXTRACT_32BITS(&dp[2]),
595 				       EXTRACT_64BITS(&dp[0]));
596 			} else {
597 				TCHECK(dp[1]);
598 				printf(" %u bytes @ %u",
599 				    EXTRACT_32BITS(&dp[1]),
600 				    EXTRACT_32BITS(&dp[0]));
601 			}
602 			return;
603 		}
604 		break;
605 
606 	case NFSPROC_WRITE:
607 		printf(" write");
608 		if ((dp = parsereq(rp, length)) != NULL &&
609 		    (dp = parsefh(dp, v3)) != NULL) {
610 			if (v3) {
611 				TCHECK(dp[2]);
612 				printf(" %u (%u) bytes @ %" PRIu64,
613 						EXTRACT_32BITS(&dp[4]),
614 						EXTRACT_32BITS(&dp[2]),
615 						EXTRACT_64BITS(&dp[0]));
616 				if (vflag) {
617 					dp += 3;
618 					TCHECK(dp[0]);
619 					printf(" <%s>",
620 						tok2str(nfsv3_writemodes,
621 							NULL, EXTRACT_32BITS(dp)));
622 				}
623 			} else {
624 				TCHECK(dp[3]);
625 				printf(" %u (%u) bytes @ %u (%u)",
626 						EXTRACT_32BITS(&dp[3]),
627 						EXTRACT_32BITS(&dp[2]),
628 						EXTRACT_32BITS(&dp[1]),
629 						EXTRACT_32BITS(&dp[0]));
630 			}
631 			return;
632 		}
633 		break;
634 
635 	case NFSPROC_CREATE:
636 		printf(" create");
637 		if ((dp = parsereq(rp, length)) != NULL &&
638 		    parsefhn(dp, v3) != NULL)
639 			return;
640 		break;
641 
642 	case NFSPROC_MKDIR:
643 		printf(" mkdir");
644 		if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp, v3) != 0)
645 			return;
646 		break;
647 
648 	case NFSPROC_SYMLINK:
649 		printf(" symlink");
650 		if ((dp = parsereq(rp, length)) != 0 &&
651 		    (dp = parsefhn(dp, v3)) != 0) {
652 			fputs(" ->", stdout);
653 			if (v3 && (dp = parse_sattr3(dp, &sa3)) == 0)
654 				break;
655 			if (parsefn(dp) == 0)
656 				break;
657 			if (v3 && vflag)
658 				print_sattr3(&sa3, vflag);
659 			return;
660 		}
661 		break;
662 
663 	case NFSPROC_MKNOD:
664 		printf(" mknod");
665 		if ((dp = parsereq(rp, length)) != 0 &&
666 		    (dp = parsefhn(dp, v3)) != 0) {
667 			TCHECK(*dp);
668 			type = (nfs_type)EXTRACT_32BITS(dp);
669 			dp++;
670 			if ((dp = parse_sattr3(dp, &sa3)) == 0)
671 				break;
672 			printf(" %s", tok2str(type2str, "unk-ft %d", type));
673 			if (vflag && (type == NFCHR || type == NFBLK)) {
674 				TCHECK(dp[1]);
675 				printf(" %u/%u",
676 				       EXTRACT_32BITS(&dp[0]),
677 				       EXTRACT_32BITS(&dp[1]));
678 				dp += 2;
679 			}
680 			if (vflag)
681 				print_sattr3(&sa3, vflag);
682 			return;
683 		}
684 		break;
685 
686 	case NFSPROC_REMOVE:
687 		printf(" remove");
688 		if ((dp = parsereq(rp, length)) != NULL &&
689 		    parsefhn(dp, v3) != NULL)
690 			return;
691 		break;
692 
693 	case NFSPROC_RMDIR:
694 		printf(" rmdir");
695 		if ((dp = parsereq(rp, length)) != NULL &&
696 		    parsefhn(dp, v3) != NULL)
697 			return;
698 		break;
699 
700 	case NFSPROC_RENAME:
701 		printf(" rename");
702 		if ((dp = parsereq(rp, length)) != NULL &&
703 		    (dp = parsefhn(dp, v3)) != NULL) {
704 			fputs(" ->", stdout);
705 			if (parsefhn(dp, v3) != NULL)
706 				return;
707 		}
708 		break;
709 
710 	case NFSPROC_LINK:
711 		printf(" link");
712 		if ((dp = parsereq(rp, length)) != NULL &&
713 		    (dp = parsefh(dp, v3)) != NULL) {
714 			fputs(" ->", stdout);
715 			if (parsefhn(dp, v3) != NULL)
716 				return;
717 		}
718 		break;
719 
720 	case NFSPROC_READDIR:
721 		printf(" readdir");
722 		if ((dp = parsereq(rp, length)) != NULL &&
723 		    (dp = parsefh(dp, v3)) != NULL) {
724 			if (v3) {
725 				TCHECK(dp[4]);
726 				/*
727 				 * We shouldn't really try to interpret the
728 				 * offset cookie here.
729 				 */
730 				printf(" %u bytes @ %" PRId64,
731 				    EXTRACT_32BITS(&dp[4]),
732 				    EXTRACT_64BITS(&dp[0]));
733 				if (vflag)
734 					printf(" verf %08x%08x", dp[2],
735 					       dp[3]);
736 			} else {
737 				TCHECK(dp[1]);
738 				/*
739 				 * Print the offset as signed, since -1 is
740 				 * common, but offsets > 2^31 aren't.
741 				 */
742 				printf(" %u bytes @ %d",
743 				    EXTRACT_32BITS(&dp[1]),
744 				    EXTRACT_32BITS(&dp[0]));
745 			}
746 			return;
747 		}
748 		break;
749 
750 	case NFSPROC_READDIRPLUS:
751 		printf(" readdirplus");
752 		if ((dp = parsereq(rp, length)) != NULL &&
753 		    (dp = parsefh(dp, v3)) != NULL) {
754 			TCHECK(dp[4]);
755 			/*
756 			 * We don't try to interpret the offset
757 			 * cookie here.
758 			 */
759 			printf(" %u bytes @ %" PRId64,
760 				EXTRACT_32BITS(&dp[4]),
761 				EXTRACT_64BITS(&dp[0]));
762 			if (vflag) {
763 				TCHECK(dp[5]);
764 				printf(" max %u verf %08x%08x",
765 				       EXTRACT_32BITS(&dp[5]), dp[2], dp[3]);
766 			}
767 			return;
768 		}
769 		break;
770 
771 	case NFSPROC_FSSTAT:
772 		printf(" fsstat");
773 		if ((dp = parsereq(rp, length)) != NULL &&
774 		    parsefh(dp, v3) != NULL)
775 			return;
776 		break;
777 
778 	case NFSPROC_FSINFO:
779 		printf(" fsinfo");
780 		if ((dp = parsereq(rp, length)) != NULL &&
781 		    parsefh(dp, v3) != NULL)
782 			return;
783 		break;
784 
785 	case NFSPROC_PATHCONF:
786 		printf(" pathconf");
787 		if ((dp = parsereq(rp, length)) != NULL &&
788 		    parsefh(dp, v3) != NULL)
789 			return;
790 		break;
791 
792 	case NFSPROC_COMMIT:
793 		printf(" commit");
794 		if ((dp = parsereq(rp, length)) != NULL &&
795 		    (dp = parsefh(dp, v3)) != NULL) {
796 			TCHECK(dp[2]);
797 			printf(" %u bytes @ %" PRIu64,
798 				EXTRACT_32BITS(&dp[2]),
799 				EXTRACT_64BITS(&dp[0]));
800 			return;
801 		}
802 		break;
803 
804 	default:
805 		printf(" proc-%u", EXTRACT_32BITS(&rp->rm_call.cb_proc));
806 		return;
807 	}
808 
809 trunc:
810 	if (!nfserr)
811 		fputs(" [|nfs]", stdout);
812 }
813 
814 /*
815  * Print out an NFS file handle.
816  * We assume packet was not truncated before the end of the
817  * file handle pointed to by dp.
818  *
819  * Note: new version (using portable file-handle parser) doesn't produce
820  * generation number.  It probably could be made to do that, with some
821  * additional hacking on the parser code.
822  */
823 static void
824 nfs_printfh(register const u_int32_t *dp, const u_int len)
825 {
826 	my_fsid fsid;
827 	ino_t ino;
828 	const char *sfsname = NULL;
829 	char *spacep;
830 
831 	if (uflag) {
832 		u_int i;
833 		char const *sep = "";
834 
835 		printf(" fh[");
836 		for (i=0; i<len; i++) {
837 			(void)printf("%s%x", sep, dp[i]);
838 			sep = ":";
839 		}
840 		printf("]");
841 		return;
842 	}
843 
844 	Parse_fh((const u_char *)dp, len, &fsid, &ino, NULL, &sfsname, 0);
845 
846 	if (sfsname) {
847 		/* file system ID is ASCII, not numeric, for this server OS */
848 		static char temp[NFSX_V3FHMAX+1];
849 
850 		/* Make sure string is null-terminated */
851 		strncpy(temp, sfsname, NFSX_V3FHMAX);
852 		temp[sizeof(temp) - 1] = '\0';
853 		/* Remove trailing spaces */
854 		spacep = strchr(temp, ' ');
855 		if (spacep)
856 			*spacep = '\0';
857 
858 		(void)printf(" fh %s/", temp);
859 	} else {
860 		(void)printf(" fh %d,%d/",
861 			     fsid.Fsid_dev.Major, fsid.Fsid_dev.Minor);
862 	}
863 
864 	if(fsid.Fsid_dev.Minor == 257)
865 		/* Print the undecoded handle */
866 		(void)printf("%s", fsid.Opaque_Handle);
867 	else
868 		(void)printf("%ld", (long) ino);
869 }
870 
871 /*
872  * Maintain a small cache of recent client.XID.server/proc pairs, to allow
873  * us to match up replies with requests and thus to know how to parse
874  * the reply.
875  */
876 
877 struct xid_map_entry {
878 	u_int32_t	xid;		/* transaction ID (net order) */
879 	int ipver;			/* IP version (4 or 6) */
880 #ifdef INET6
881 	struct in6_addr	client;		/* client IP address (net order) */
882 	struct in6_addr	server;		/* server IP address (net order) */
883 #else
884 	struct in_addr	client;		/* client IP address (net order) */
885 	struct in_addr	server;		/* server IP address (net order) */
886 #endif
887 	u_int32_t	proc;		/* call proc number (host order) */
888 	u_int32_t	vers;		/* program version (host order) */
889 };
890 
891 /*
892  * Map entries are kept in an array that we manage as a ring;
893  * new entries are always added at the tail of the ring.  Initially,
894  * all the entries are zero and hence don't match anything.
895  */
896 
897 #define	XIDMAPSIZE	64
898 
899 struct xid_map_entry xid_map[XIDMAPSIZE];
900 
901 int	xid_map_next = 0;
902 int	xid_map_hint = 0;
903 
904 static int
905 xid_map_enter(const struct sunrpc_msg *rp, const u_char *bp)
906 {
907 	struct ip *ip = NULL;
908 #ifdef INET6
909 	struct ip6_hdr *ip6 = NULL;
910 #endif
911 	struct xid_map_entry *xmep;
912 
913 	if (!TTEST(rp->rm_call.cb_vers))
914 		return (0);
915 	switch (IP_V((struct ip *)bp)) {
916 	case 4:
917 		ip = (struct ip *)bp;
918 		break;
919 #ifdef INET6
920 	case 6:
921 		ip6 = (struct ip6_hdr *)bp;
922 		break;
923 #endif
924 	default:
925 		return (1);
926 	}
927 
928 	xmep = &xid_map[xid_map_next];
929 
930 	if (++xid_map_next >= XIDMAPSIZE)
931 		xid_map_next = 0;
932 
933 	xmep->xid = rp->rm_xid;
934 	if (ip) {
935 		xmep->ipver = 4;
936 		memcpy(&xmep->client, &ip->ip_src, sizeof(ip->ip_src));
937 		memcpy(&xmep->server, &ip->ip_dst, sizeof(ip->ip_dst));
938 	}
939 #ifdef INET6
940 	else if (ip6) {
941 		xmep->ipver = 6;
942 		memcpy(&xmep->client, &ip6->ip6_src, sizeof(ip6->ip6_src));
943 		memcpy(&xmep->server, &ip6->ip6_dst, sizeof(ip6->ip6_dst));
944 	}
945 #endif
946 	xmep->proc = EXTRACT_32BITS(&rp->rm_call.cb_proc);
947 	xmep->vers = EXTRACT_32BITS(&rp->rm_call.cb_vers);
948 	return (1);
949 }
950 
951 /*
952  * Returns 0 and puts NFSPROC_xxx in proc return and
953  * version in vers return, or returns -1 on failure
954  */
955 static int
956 xid_map_find(const struct sunrpc_msg *rp, const u_char *bp, u_int32_t *proc,
957 	     u_int32_t *vers)
958 {
959 	int i;
960 	struct xid_map_entry *xmep;
961 	u_int32_t xid = rp->rm_xid;
962 	struct ip *ip = (struct ip *)bp;
963 #ifdef INET6
964 	struct ip6_hdr *ip6 = (struct ip6_hdr *)bp;
965 #endif
966 	int cmp;
967 
968 	/* Start searching from where we last left off */
969 	i = xid_map_hint;
970 	do {
971 		xmep = &xid_map[i];
972 		cmp = 1;
973 		if (xmep->ipver != IP_V(ip) || xmep->xid != xid)
974 			goto nextitem;
975 		switch (xmep->ipver) {
976 		case 4:
977 			if (memcmp(&ip->ip_src, &xmep->server,
978 				   sizeof(ip->ip_src)) != 0 ||
979 			    memcmp(&ip->ip_dst, &xmep->client,
980 				   sizeof(ip->ip_dst)) != 0) {
981 				cmp = 0;
982 			}
983 			break;
984 #ifdef INET6
985 		case 6:
986 			if (memcmp(&ip6->ip6_src, &xmep->server,
987 				   sizeof(ip6->ip6_src)) != 0 ||
988 			    memcmp(&ip6->ip6_dst, &xmep->client,
989 				   sizeof(ip6->ip6_dst)) != 0) {
990 				cmp = 0;
991 			}
992 			break;
993 #endif
994 		default:
995 			cmp = 0;
996 			break;
997 		}
998 		if (cmp) {
999 			/* match */
1000 			xid_map_hint = i;
1001 			*proc = xmep->proc;
1002 			*vers = xmep->vers;
1003 			return 0;
1004 		}
1005 	nextitem:
1006 		if (++i >= XIDMAPSIZE)
1007 			i = 0;
1008 	} while (i != xid_map_hint);
1009 
1010 	/* search failed */
1011 	return (-1);
1012 }
1013 
1014 /*
1015  * Routines for parsing reply packets
1016  */
1017 
1018 /*
1019  * Return a pointer to the beginning of the actual results.
1020  * If the packet was truncated, return 0.
1021  */
1022 static const u_int32_t *
1023 parserep(register const struct sunrpc_msg *rp, register u_int length)
1024 {
1025 	register const u_int32_t *dp;
1026 	u_int len;
1027 	enum sunrpc_accept_stat astat;
1028 
1029 	/*
1030 	 * Portability note:
1031 	 * Here we find the address of the ar_verf credentials.
1032 	 * Originally, this calculation was
1033 	 *	dp = (u_int32_t *)&rp->rm_reply.rp_acpt.ar_verf
1034 	 * On the wire, the rp_acpt field starts immediately after
1035 	 * the (32 bit) rp_stat field.  However, rp_acpt (which is a
1036 	 * "struct accepted_reply") contains a "struct opaque_auth",
1037 	 * whose internal representation contains a pointer, so on a
1038 	 * 64-bit machine the compiler inserts 32 bits of padding
1039 	 * before rp->rm_reply.rp_acpt.ar_verf.  So, we cannot use
1040 	 * the internal representation to parse the on-the-wire
1041 	 * representation.  Instead, we skip past the rp_stat field,
1042 	 * which is an "enum" and so occupies one 32-bit word.
1043 	 */
1044 	dp = ((const u_int32_t *)&rp->rm_reply) + 1;
1045 	TCHECK(dp[1]);
1046 	len = EXTRACT_32BITS(&dp[1]);
1047 	if (len >= length)
1048 		return (NULL);
1049 	/*
1050 	 * skip past the ar_verf credentials.
1051 	 */
1052 	dp += (len + (2*sizeof(u_int32_t) + 3)) / sizeof(u_int32_t);
1053 	TCHECK2(dp[0], 0);
1054 
1055 	/*
1056 	 * now we can check the ar_stat field
1057 	 */
1058 	astat = (enum sunrpc_accept_stat) EXTRACT_32BITS(dp);
1059 	switch (astat) {
1060 
1061 	case SUNRPC_SUCCESS:
1062 		break;
1063 
1064 	case SUNRPC_PROG_UNAVAIL:
1065 		printf(" PROG_UNAVAIL");
1066 		nfserr = 1;		/* suppress trunc string */
1067 		return (NULL);
1068 
1069 	case SUNRPC_PROG_MISMATCH:
1070 		printf(" PROG_MISMATCH");
1071 		nfserr = 1;		/* suppress trunc string */
1072 		return (NULL);
1073 
1074 	case SUNRPC_PROC_UNAVAIL:
1075 		printf(" PROC_UNAVAIL");
1076 		nfserr = 1;		/* suppress trunc string */
1077 		return (NULL);
1078 
1079 	case SUNRPC_GARBAGE_ARGS:
1080 		printf(" GARBAGE_ARGS");
1081 		nfserr = 1;		/* suppress trunc string */
1082 		return (NULL);
1083 
1084 	case SUNRPC_SYSTEM_ERR:
1085 		printf(" SYSTEM_ERR");
1086 		nfserr = 1;		/* suppress trunc string */
1087 		return (NULL);
1088 
1089 	default:
1090 		printf(" ar_stat %d", astat);
1091 		nfserr = 1;		/* suppress trunc string */
1092 		return (NULL);
1093 	}
1094 	/* successful return */
1095 	TCHECK2(*dp, sizeof(astat));
1096 	return ((u_int32_t *) (sizeof(astat) + ((char *)dp)));
1097 trunc:
1098 	return (0);
1099 }
1100 
1101 static const u_int32_t *
1102 parsestatus(const u_int32_t *dp, int *er)
1103 {
1104 	int errnum;
1105 
1106 	TCHECK(dp[0]);
1107 
1108 	errnum = EXTRACT_32BITS(&dp[0]);
1109 	if (er)
1110 		*er = errnum;
1111 	if (errnum != 0) {
1112 		if (!qflag)
1113 			printf(" ERROR: %s",
1114 			    tok2str(status2str, "unk %d", errnum));
1115 		nfserr = 1;
1116 	}
1117 	return (dp + 1);
1118 trunc:
1119 	return NULL;
1120 }
1121 
1122 static const u_int32_t *
1123 parsefattr(const u_int32_t *dp, int verbose, int v3)
1124 {
1125 	const struct nfs_fattr *fap;
1126 
1127 	fap = (const struct nfs_fattr *)dp;
1128 	TCHECK(fap->fa_gid);
1129 	if (verbose) {
1130 		printf(" %s %o ids %d/%d",
1131 		    tok2str(type2str, "unk-ft %d ",
1132 		    EXTRACT_32BITS(&fap->fa_type)),
1133 		    EXTRACT_32BITS(&fap->fa_mode),
1134 		    EXTRACT_32BITS(&fap->fa_uid),
1135 		    EXTRACT_32BITS(&fap->fa_gid));
1136 		if (v3) {
1137 			TCHECK(fap->fa3_size);
1138 			printf(" sz %" PRIu64,
1139 				EXTRACT_64BITS((u_int32_t *)&fap->fa3_size));
1140 		} else {
1141 			TCHECK(fap->fa2_size);
1142 			printf(" sz %d", EXTRACT_32BITS(&fap->fa2_size));
1143 		}
1144 	}
1145 	/* print lots more stuff */
1146 	if (verbose > 1) {
1147 		if (v3) {
1148 			TCHECK(fap->fa3_ctime);
1149 			printf(" nlink %d rdev %d/%d",
1150 			       EXTRACT_32BITS(&fap->fa_nlink),
1151 			       EXTRACT_32BITS(&fap->fa3_rdev.specdata1),
1152 			       EXTRACT_32BITS(&fap->fa3_rdev.specdata2));
1153 			printf(" fsid %" PRIx64,
1154 				EXTRACT_64BITS((u_int32_t *)&fap->fa3_fsid));
1155 			printf(" fileid %" PRIx64,
1156 				EXTRACT_64BITS((u_int32_t *)&fap->fa3_fileid));
1157 			printf(" a/m/ctime %u.%06u",
1158 			       EXTRACT_32BITS(&fap->fa3_atime.nfsv3_sec),
1159 			       EXTRACT_32BITS(&fap->fa3_atime.nfsv3_nsec));
1160 			printf(" %u.%06u",
1161 			       EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_sec),
1162 			       EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_nsec));
1163 			printf(" %u.%06u",
1164 			       EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_sec),
1165 			       EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_nsec));
1166 		} else {
1167 			TCHECK(fap->fa2_ctime);
1168 			printf(" nlink %d rdev %x fsid %x nodeid %x a/m/ctime",
1169 			       EXTRACT_32BITS(&fap->fa_nlink),
1170 			       EXTRACT_32BITS(&fap->fa2_rdev),
1171 			       EXTRACT_32BITS(&fap->fa2_fsid),
1172 			       EXTRACT_32BITS(&fap->fa2_fileid));
1173 			printf(" %u.%06u",
1174 			       EXTRACT_32BITS(&fap->fa2_atime.nfsv2_sec),
1175 			       EXTRACT_32BITS(&fap->fa2_atime.nfsv2_usec));
1176 			printf(" %u.%06u",
1177 			       EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_sec),
1178 			       EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_usec));
1179 			printf(" %u.%06u",
1180 			       EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_sec),
1181 			       EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_usec));
1182 		}
1183 	}
1184 	return ((const u_int32_t *)((unsigned char *)dp +
1185 		(v3 ? NFSX_V3FATTR : NFSX_V2FATTR)));
1186 trunc:
1187 	return (NULL);
1188 }
1189 
1190 static int
1191 parseattrstat(const u_int32_t *dp, int verbose, int v3)
1192 {
1193 	int er;
1194 
1195 	dp = parsestatus(dp, &er);
1196 	if (dp == NULL)
1197 		return (0);
1198 	if (er)
1199 		return (1);
1200 
1201 	return (parsefattr(dp, verbose, v3) != NULL);
1202 }
1203 
1204 static int
1205 parsediropres(const u_int32_t *dp)
1206 {
1207 	int er;
1208 
1209 	if (!(dp = parsestatus(dp, &er)))
1210 		return (0);
1211 	if (er)
1212 		return (1);
1213 
1214 	dp = parsefh(dp, 0);
1215 	if (dp == NULL)
1216 		return (0);
1217 
1218 	return (parsefattr(dp, vflag, 0) != NULL);
1219 }
1220 
1221 static int
1222 parselinkres(const u_int32_t *dp, int v3)
1223 {
1224 	int er;
1225 
1226 	dp = parsestatus(dp, &er);
1227 	if (dp == NULL)
1228 		return(0);
1229 	if (er)
1230 		return(1);
1231 	if (v3 && !(dp = parse_post_op_attr(dp, vflag)))
1232 		return (0);
1233 	putchar(' ');
1234 	return (parsefn(dp) != NULL);
1235 }
1236 
1237 static int
1238 parsestatfs(const u_int32_t *dp, int v3)
1239 {
1240 	const struct nfs_statfs *sfsp;
1241 	int er;
1242 
1243 	dp = parsestatus(dp, &er);
1244 	if (dp == NULL)
1245 		return (0);
1246 	if (!v3 && er)
1247 		return (1);
1248 
1249 	if (qflag)
1250 		return(1);
1251 
1252 	if (v3) {
1253 		if (vflag)
1254 			printf(" POST:");
1255 		if (!(dp = parse_post_op_attr(dp, vflag)))
1256 			return (0);
1257 	}
1258 
1259 	TCHECK2(*dp, (v3 ? NFSX_V3STATFS : NFSX_V2STATFS));
1260 
1261 	sfsp = (const struct nfs_statfs *)dp;
1262 
1263 	if (v3) {
1264 		printf(" tbytes %" PRIu64 " fbytes %" PRIu64 " abytes %" PRIu64,
1265 			EXTRACT_64BITS((u_int32_t *)&sfsp->sf_tbytes),
1266 			EXTRACT_64BITS((u_int32_t *)&sfsp->sf_fbytes),
1267 			EXTRACT_64BITS((u_int32_t *)&sfsp->sf_abytes));
1268 		if (vflag) {
1269 			printf(" tfiles %" PRIu64 " ffiles %" PRIu64 " afiles %" PRIu64 " invar %u",
1270 			       EXTRACT_64BITS((u_int32_t *)&sfsp->sf_tfiles),
1271 			       EXTRACT_64BITS((u_int32_t *)&sfsp->sf_ffiles),
1272 			       EXTRACT_64BITS((u_int32_t *)&sfsp->sf_afiles),
1273 			       EXTRACT_32BITS(&sfsp->sf_invarsec));
1274 		}
1275 	} else {
1276 		printf(" tsize %d bsize %d blocks %d bfree %d bavail %d",
1277 			EXTRACT_32BITS(&sfsp->sf_tsize),
1278 			EXTRACT_32BITS(&sfsp->sf_bsize),
1279 			EXTRACT_32BITS(&sfsp->sf_blocks),
1280 			EXTRACT_32BITS(&sfsp->sf_bfree),
1281 			EXTRACT_32BITS(&sfsp->sf_bavail));
1282 	}
1283 
1284 	return (1);
1285 trunc:
1286 	return (0);
1287 }
1288 
1289 static int
1290 parserddires(const u_int32_t *dp)
1291 {
1292 	int er;
1293 
1294 	dp = parsestatus(dp, &er);
1295 	if (dp == NULL)
1296 		return (0);
1297 	if (er)
1298 		return (1);
1299 	if (qflag)
1300 		return (1);
1301 
1302 	TCHECK(dp[2]);
1303 	printf(" offset %x size %d ",
1304 	       EXTRACT_32BITS(&dp[0]), EXTRACT_32BITS(&dp[1]));
1305 	if (dp[2] != 0)
1306 		printf(" eof");
1307 
1308 	return (1);
1309 trunc:
1310 	return (0);
1311 }
1312 
1313 static const u_int32_t *
1314 parse_wcc_attr(const u_int32_t *dp)
1315 {
1316 	printf(" sz %" PRIu64, EXTRACT_64BITS(&dp[0]));
1317 	printf(" mtime %u.%06u ctime %u.%06u",
1318 	       EXTRACT_32BITS(&dp[2]), EXTRACT_32BITS(&dp[3]),
1319 	       EXTRACT_32BITS(&dp[4]), EXTRACT_32BITS(&dp[5]));
1320 	return (dp + 6);
1321 }
1322 
1323 /*
1324  * Pre operation attributes. Print only if vflag > 1.
1325  */
1326 static const u_int32_t *
1327 parse_pre_op_attr(const u_int32_t *dp, int verbose)
1328 {
1329 	TCHECK(dp[0]);
1330 	if (!EXTRACT_32BITS(&dp[0]))
1331 		return (dp + 1);
1332 	dp++;
1333 	TCHECK2(*dp, 24);
1334 	if (verbose > 1) {
1335 		return parse_wcc_attr(dp);
1336 	} else {
1337 		/* If not verbose enough, just skip over wcc_attr */
1338 		return (dp + 6);
1339 	}
1340 trunc:
1341 	return (NULL);
1342 }
1343 
1344 /*
1345  * Post operation attributes are printed if vflag >= 1
1346  */
1347 static const u_int32_t *
1348 parse_post_op_attr(const u_int32_t *dp, int verbose)
1349 {
1350 	TCHECK(dp[0]);
1351 	if (!EXTRACT_32BITS(&dp[0]))
1352 		return (dp + 1);
1353 	dp++;
1354 	if (verbose) {
1355 		return parsefattr(dp, verbose, 1);
1356 	} else
1357 		return (dp + (NFSX_V3FATTR / sizeof (u_int32_t)));
1358 trunc:
1359 	return (NULL);
1360 }
1361 
1362 static const u_int32_t *
1363 parse_wcc_data(const u_int32_t *dp, int verbose)
1364 {
1365 	if (verbose > 1)
1366 		printf(" PRE:");
1367 	if (!(dp = parse_pre_op_attr(dp, verbose)))
1368 		return (0);
1369 
1370 	if (verbose)
1371 		printf(" POST:");
1372 	return parse_post_op_attr(dp, verbose);
1373 }
1374 
1375 static const u_int32_t *
1376 parsecreateopres(const u_int32_t *dp, int verbose)
1377 {
1378 	int er;
1379 
1380 	if (!(dp = parsestatus(dp, &er)))
1381 		return (0);
1382 	if (er)
1383 		dp = parse_wcc_data(dp, verbose);
1384 	else {
1385 		TCHECK(dp[0]);
1386 		if (!EXTRACT_32BITS(&dp[0]))
1387 			return (dp + 1);
1388 		dp++;
1389 		if (!(dp = parsefh(dp, 1)))
1390 			return (0);
1391 		if (verbose) {
1392 			if (!(dp = parse_post_op_attr(dp, verbose)))
1393 				return (0);
1394 			if (vflag > 1) {
1395 				printf(" dir attr:");
1396 				dp = parse_wcc_data(dp, verbose);
1397 			}
1398 		}
1399 	}
1400 	return (dp);
1401 trunc:
1402 	return (NULL);
1403 }
1404 
1405 static int
1406 parsewccres(const u_int32_t *dp, int verbose)
1407 {
1408 	int er;
1409 
1410 	if (!(dp = parsestatus(dp, &er)))
1411 		return (0);
1412 	return parse_wcc_data(dp, verbose) != 0;
1413 }
1414 
1415 static const u_int32_t *
1416 parsev3rddirres(const u_int32_t *dp, int verbose)
1417 {
1418 	int er;
1419 
1420 	if (!(dp = parsestatus(dp, &er)))
1421 		return (0);
1422 	if (vflag)
1423 		printf(" POST:");
1424 	if (!(dp = parse_post_op_attr(dp, verbose)))
1425 		return (0);
1426 	if (er)
1427 		return dp;
1428 	if (vflag) {
1429 		TCHECK(dp[1]);
1430 		printf(" verf %08x%08x", dp[0], dp[1]);
1431 		dp += 2;
1432 	}
1433 	return dp;
1434 trunc:
1435 	return (NULL);
1436 }
1437 
1438 static int
1439 parsefsinfo(const u_int32_t *dp)
1440 {
1441 	struct nfsv3_fsinfo *sfp;
1442 	int er;
1443 
1444 	if (!(dp = parsestatus(dp, &er)))
1445 		return (0);
1446 	if (vflag)
1447 		printf(" POST:");
1448 	if (!(dp = parse_post_op_attr(dp, vflag)))
1449 		return (0);
1450 	if (er)
1451 		return (1);
1452 
1453 	sfp = (struct nfsv3_fsinfo *)dp;
1454 	TCHECK(*sfp);
1455 	printf(" rtmax %u rtpref %u wtmax %u wtpref %u dtpref %u",
1456 	       EXTRACT_32BITS(&sfp->fs_rtmax),
1457 	       EXTRACT_32BITS(&sfp->fs_rtpref),
1458 	       EXTRACT_32BITS(&sfp->fs_wtmax),
1459 	       EXTRACT_32BITS(&sfp->fs_wtpref),
1460 	       EXTRACT_32BITS(&sfp->fs_dtpref));
1461 	if (vflag) {
1462 		printf(" rtmult %u wtmult %u maxfsz %" PRIu64,
1463 		       EXTRACT_32BITS(&sfp->fs_rtmult),
1464 		       EXTRACT_32BITS(&sfp->fs_wtmult),
1465 		       EXTRACT_64BITS((u_int32_t *)&sfp->fs_maxfilesize));
1466 		printf(" delta %u.%06u ",
1467 		       EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_sec),
1468 		       EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_nsec));
1469 	}
1470 	return (1);
1471 trunc:
1472 	return (0);
1473 }
1474 
1475 static int
1476 parsepathconf(const u_int32_t *dp)
1477 {
1478 	int er;
1479 	struct nfsv3_pathconf *spp;
1480 
1481 	if (!(dp = parsestatus(dp, &er)))
1482 		return (0);
1483 	if (vflag)
1484 		printf(" POST:");
1485 	if (!(dp = parse_post_op_attr(dp, vflag)))
1486 		return (0);
1487 	if (er)
1488 		return (1);
1489 
1490 	spp = (struct nfsv3_pathconf *)dp;
1491 	TCHECK(*spp);
1492 
1493 	printf(" linkmax %u namemax %u %s %s %s %s",
1494 	       EXTRACT_32BITS(&spp->pc_linkmax),
1495 	       EXTRACT_32BITS(&spp->pc_namemax),
1496 	       EXTRACT_32BITS(&spp->pc_notrunc) ? "notrunc" : "",
1497 	       EXTRACT_32BITS(&spp->pc_chownrestricted) ? "chownres" : "",
1498 	       EXTRACT_32BITS(&spp->pc_caseinsensitive) ? "igncase" : "",
1499 	       EXTRACT_32BITS(&spp->pc_casepreserving) ? "keepcase" : "");
1500 	return (1);
1501 trunc:
1502 	return (0);
1503 }
1504 
1505 static void
1506 interp_reply(const struct sunrpc_msg *rp, u_int32_t proc, u_int32_t vers, int length)
1507 {
1508 	register const u_int32_t *dp;
1509 	register int v3;
1510 	int er;
1511 
1512 	v3 = (vers == NFS_VER3);
1513 
1514 	if (!v3 && proc < NFS_NPROCS)
1515 		proc = nfsv3_procid[proc];
1516 
1517 	switch (proc) {
1518 
1519 	case NFSPROC_NOOP:
1520 		printf(" nop");
1521 		return;
1522 
1523 	case NFSPROC_NULL:
1524 		printf(" null");
1525 		return;
1526 
1527 	case NFSPROC_GETATTR:
1528 		printf(" getattr");
1529 		dp = parserep(rp, length);
1530 		if (dp != NULL && parseattrstat(dp, !qflag, v3) != 0)
1531 			return;
1532 		break;
1533 
1534 	case NFSPROC_SETATTR:
1535 		printf(" setattr");
1536 		if (!(dp = parserep(rp, length)))
1537 			return;
1538 		if (v3) {
1539 			if (parsewccres(dp, vflag))
1540 				return;
1541 		} else {
1542 			if (parseattrstat(dp, !qflag, 0) != 0)
1543 				return;
1544 		}
1545 		break;
1546 
1547 	case NFSPROC_LOOKUP:
1548 		printf(" lookup");
1549 		if (!(dp = parserep(rp, length)))
1550 			break;
1551 		if (v3) {
1552 			if (!(dp = parsestatus(dp, &er)))
1553 				break;
1554 			if (er) {
1555 				if (vflag > 1) {
1556 					printf(" post dattr:");
1557 					dp = parse_post_op_attr(dp, vflag);
1558 				}
1559 			} else {
1560 				if (!(dp = parsefh(dp, v3)))
1561 					break;
1562 				if ((dp = parse_post_op_attr(dp, vflag)) &&
1563 				    vflag > 1) {
1564 					printf(" post dattr:");
1565 					dp = parse_post_op_attr(dp, vflag);
1566 				}
1567 			}
1568 			if (dp)
1569 				return;
1570 		} else {
1571 			if (parsediropres(dp) != 0)
1572 				return;
1573 		}
1574 		break;
1575 
1576 	case NFSPROC_ACCESS:
1577 		printf(" access");
1578 		if (!(dp = parserep(rp, length)))
1579 			break;
1580 		if (!(dp = parsestatus(dp, &er)))
1581 			break;
1582 		if (vflag)
1583 			printf(" attr:");
1584 		if (!(dp = parse_post_op_attr(dp, vflag)))
1585 			break;
1586 		if (!er)
1587 			printf(" c %04x", EXTRACT_32BITS(&dp[0]));
1588 		return;
1589 
1590 	case NFSPROC_READLINK:
1591 		printf(" readlink");
1592 		dp = parserep(rp, length);
1593 		if (dp != NULL && parselinkres(dp, v3) != 0)
1594 			return;
1595 		break;
1596 
1597 	case NFSPROC_READ:
1598 		printf(" read");
1599 		if (!(dp = parserep(rp, length)))
1600 			break;
1601 		if (v3) {
1602 			if (!(dp = parsestatus(dp, &er)))
1603 				break;
1604 			if (!(dp = parse_post_op_attr(dp, vflag)))
1605 				break;
1606 			if (er)
1607 				return;
1608 			if (vflag) {
1609 				TCHECK(dp[1]);
1610 				printf(" %u bytes", EXTRACT_32BITS(&dp[0]));
1611 				if (EXTRACT_32BITS(&dp[1]))
1612 					printf(" EOF");
1613 			}
1614 			return;
1615 		} else {
1616 			if (parseattrstat(dp, vflag, 0) != 0)
1617 				return;
1618 		}
1619 		break;
1620 
1621 	case NFSPROC_WRITE:
1622 		printf(" write");
1623 		if (!(dp = parserep(rp, length)))
1624 			break;
1625 		if (v3) {
1626 			if (!(dp = parsestatus(dp, &er)))
1627 				break;
1628 			if (!(dp = parse_wcc_data(dp, vflag)))
1629 				break;
1630 			if (er)
1631 				return;
1632 			if (vflag) {
1633 				TCHECK(dp[0]);
1634 				printf(" %u bytes", EXTRACT_32BITS(&dp[0]));
1635 				if (vflag > 1) {
1636 					TCHECK(dp[1]);
1637 					printf(" <%s>",
1638 						tok2str(nfsv3_writemodes,
1639 							NULL, EXTRACT_32BITS(&dp[1])));
1640 				}
1641 				return;
1642 			}
1643 		} else {
1644 			if (parseattrstat(dp, vflag, v3) != 0)
1645 				return;
1646 		}
1647 		break;
1648 
1649 	case NFSPROC_CREATE:
1650 		printf(" create");
1651 		if (!(dp = parserep(rp, length)))
1652 			break;
1653 		if (v3) {
1654 			if (parsecreateopres(dp, vflag) != 0)
1655 				return;
1656 		} else {
1657 			if (parsediropres(dp) != 0)
1658 				return;
1659 		}
1660 		break;
1661 
1662 	case NFSPROC_MKDIR:
1663 		printf(" mkdir");
1664 		if (!(dp = parserep(rp, length)))
1665 			break;
1666 		if (v3) {
1667 			if (parsecreateopres(dp, vflag) != 0)
1668 				return;
1669 		} else {
1670 			if (parsediropres(dp) != 0)
1671 				return;
1672 		}
1673 		break;
1674 
1675 	case NFSPROC_SYMLINK:
1676 		printf(" symlink");
1677 		if (!(dp = parserep(rp, length)))
1678 			break;
1679 		if (v3) {
1680 			if (parsecreateopres(dp, vflag) != 0)
1681 				return;
1682 		} else {
1683 			if (parsestatus(dp, &er) != 0)
1684 				return;
1685 		}
1686 		break;
1687 
1688 	case NFSPROC_MKNOD:
1689 		printf(" mknod");
1690 		if (!(dp = parserep(rp, length)))
1691 			break;
1692 		if (parsecreateopres(dp, vflag) != 0)
1693 			return;
1694 		break;
1695 
1696 	case NFSPROC_REMOVE:
1697 		printf(" remove");
1698 		if (!(dp = parserep(rp, length)))
1699 			break;
1700 		if (v3) {
1701 			if (parsewccres(dp, vflag))
1702 				return;
1703 		} else {
1704 			if (parsestatus(dp, &er) != 0)
1705 				return;
1706 		}
1707 		break;
1708 
1709 	case NFSPROC_RMDIR:
1710 		printf(" rmdir");
1711 		if (!(dp = parserep(rp, length)))
1712 			break;
1713 		if (v3) {
1714 			if (parsewccres(dp, vflag))
1715 				return;
1716 		} else {
1717 			if (parsestatus(dp, &er) != 0)
1718 				return;
1719 		}
1720 		break;
1721 
1722 	case NFSPROC_RENAME:
1723 		printf(" rename");
1724 		if (!(dp = parserep(rp, length)))
1725 			break;
1726 		if (v3) {
1727 			if (!(dp = parsestatus(dp, &er)))
1728 				break;
1729 			if (vflag) {
1730 				printf(" from:");
1731 				if (!(dp = parse_wcc_data(dp, vflag)))
1732 					break;
1733 				printf(" to:");
1734 				if (!(dp = parse_wcc_data(dp, vflag)))
1735 					break;
1736 			}
1737 			return;
1738 		} else {
1739 			if (parsestatus(dp, &er) != 0)
1740 				return;
1741 		}
1742 		break;
1743 
1744 	case NFSPROC_LINK:
1745 		printf(" link");
1746 		if (!(dp = parserep(rp, length)))
1747 			break;
1748 		if (v3) {
1749 			if (!(dp = parsestatus(dp, &er)))
1750 				break;
1751 			if (vflag) {
1752 				printf(" file POST:");
1753 				if (!(dp = parse_post_op_attr(dp, vflag)))
1754 					break;
1755 				printf(" dir:");
1756 				if (!(dp = parse_wcc_data(dp, vflag)))
1757 					break;
1758 				return;
1759 			}
1760 		} else {
1761 			if (parsestatus(dp, &er) != 0)
1762 				return;
1763 		}
1764 		break;
1765 
1766 	case NFSPROC_READDIR:
1767 		printf(" readdir");
1768 		if (!(dp = parserep(rp, length)))
1769 			break;
1770 		if (v3) {
1771 			if (parsev3rddirres(dp, vflag))
1772 				return;
1773 		} else {
1774 			if (parserddires(dp) != 0)
1775 				return;
1776 		}
1777 		break;
1778 
1779 	case NFSPROC_READDIRPLUS:
1780 		printf(" readdirplus");
1781 		if (!(dp = parserep(rp, length)))
1782 			break;
1783 		if (parsev3rddirres(dp, vflag))
1784 			return;
1785 		break;
1786 
1787 	case NFSPROC_FSSTAT:
1788 		printf(" fsstat");
1789 		dp = parserep(rp, length);
1790 		if (dp != NULL && parsestatfs(dp, v3) != 0)
1791 			return;
1792 		break;
1793 
1794 	case NFSPROC_FSINFO:
1795 		printf(" fsinfo");
1796 		dp = parserep(rp, length);
1797 		if (dp != NULL && parsefsinfo(dp) != 0)
1798 			return;
1799 		break;
1800 
1801 	case NFSPROC_PATHCONF:
1802 		printf(" pathconf");
1803 		dp = parserep(rp, length);
1804 		if (dp != NULL && parsepathconf(dp) != 0)
1805 			return;
1806 		break;
1807 
1808 	case NFSPROC_COMMIT:
1809 		printf(" commit");
1810 		dp = parserep(rp, length);
1811 		if (dp != NULL && parsewccres(dp, vflag) != 0)
1812 			return;
1813 		break;
1814 
1815 	default:
1816 		printf(" proc-%u", proc);
1817 		return;
1818 	}
1819 trunc:
1820 	if (!nfserr)
1821 		fputs(" [|nfs]", stdout);
1822 }
1823