xref: /freebsd/contrib/tcpdump/print-nfs.c (revision e28a4053)
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.111 2007-12-22 03:08:04 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 	u_int32_t access_flags;
514 	struct nfsv3_sattr sa3;
515 	char srcid[20], dstid[20];	/*fits 32bit*/
516 
517 	nfserr = 0;		/* assume no error */
518 	rp = (const struct sunrpc_msg *)bp;
519 
520 	TCHECK(rp->rm_xid);
521 	if (!nflag) {
522 		snprintf(srcid, sizeof(srcid), "%u",
523 		    EXTRACT_32BITS(&rp->rm_xid));
524 		strlcpy(dstid, "nfs", sizeof(dstid));
525 	} else {
526 		snprintf(srcid, sizeof(srcid), "%u",
527 		    EXTRACT_32BITS(&rp->rm_xid));
528 		snprintf(dstid, sizeof(dstid), "%u", NFS_PORT);
529 	}
530 	print_nfsaddr(bp2, srcid, dstid);
531 	(void)printf("%d", length);
532 
533 	if (!xid_map_enter(rp, bp2))	/* record proc number for later on */
534 		goto trunc;
535 
536 	v3 = (EXTRACT_32BITS(&rp->rm_call.cb_vers) == NFS_VER3);
537 	proc = EXTRACT_32BITS(&rp->rm_call.cb_proc);
538 
539 	if (!v3 && proc < NFS_NPROCS)
540 		proc =  nfsv3_procid[proc];
541 
542 	switch (proc) {
543 	case NFSPROC_NOOP:
544 		printf(" nop");
545 		return;
546 	case NFSPROC_NULL:
547 		printf(" null");
548 		return;
549 
550 	case NFSPROC_GETATTR:
551 		printf(" getattr");
552 		if ((dp = parsereq(rp, length)) != NULL &&
553 		    parsefh(dp, v3) != NULL)
554 			return;
555 		break;
556 
557 	case NFSPROC_SETATTR:
558 		printf(" setattr");
559 		if ((dp = parsereq(rp, length)) != NULL &&
560 		    parsefh(dp, v3) != NULL)
561 			return;
562 		break;
563 
564 	case NFSPROC_LOOKUP:
565 		printf(" lookup");
566 		if ((dp = parsereq(rp, length)) != NULL &&
567 		    parsefhn(dp, v3) != NULL)
568 			return;
569 		break;
570 
571 	case NFSPROC_ACCESS:
572 		printf(" access");
573 		if ((dp = parsereq(rp, length)) != NULL &&
574 		    (dp = parsefh(dp, v3)) != NULL) {
575 			TCHECK(dp[0]);
576 			access_flags = EXTRACT_32BITS(&dp[0]);
577 			if (access_flags & ~NFSV3ACCESS_FULL) {
578 				/* NFSV3ACCESS definitions aren't up to date */
579 				printf(" %04x", access_flags);
580 			} else if ((access_flags & NFSV3ACCESS_FULL) == NFSV3ACCESS_FULL) {
581 				printf(" NFS_ACCESS_FULL");
582 			} else {
583 				char separator = ' ';
584 				if (access_flags & NFSV3ACCESS_READ) {
585 					printf(" NFS_ACCESS_READ");
586 					separator = '|';
587 				}
588 				if (access_flags & NFSV3ACCESS_LOOKUP) {
589 					printf("%cNFS_ACCESS_LOOKUP", separator);
590 					separator = '|';
591 				}
592 				if (access_flags & NFSV3ACCESS_MODIFY) {
593 					printf("%cNFS_ACCESS_MODIFY", separator);
594 					separator = '|';
595 				}
596 				if (access_flags & NFSV3ACCESS_EXTEND) {
597 					printf("%cNFS_ACCESS_EXTEND", separator);
598 					separator = '|';
599 				}
600 				if (access_flags & NFSV3ACCESS_DELETE) {
601 					printf("%cNFS_ACCESS_DELETE", separator);
602 					separator = '|';
603 				}
604 				if (access_flags & NFSV3ACCESS_EXECUTE)
605 					printf("%cNFS_ACCESS_EXECUTE", separator);
606 			}
607 			return;
608 		}
609 		break;
610 
611 	case NFSPROC_READLINK:
612 		printf(" readlink");
613 		if ((dp = parsereq(rp, length)) != NULL &&
614 		    parsefh(dp, v3) != NULL)
615 			return;
616 		break;
617 
618 	case NFSPROC_READ:
619 		printf(" read");
620 		if ((dp = parsereq(rp, length)) != NULL &&
621 		    (dp = parsefh(dp, v3)) != NULL) {
622 			if (v3) {
623 				TCHECK(dp[2]);
624 				printf(" %u bytes @ %" PRIu64,
625 				       EXTRACT_32BITS(&dp[2]),
626 				       EXTRACT_64BITS(&dp[0]));
627 			} else {
628 				TCHECK(dp[1]);
629 				printf(" %u bytes @ %u",
630 				    EXTRACT_32BITS(&dp[1]),
631 				    EXTRACT_32BITS(&dp[0]));
632 			}
633 			return;
634 		}
635 		break;
636 
637 	case NFSPROC_WRITE:
638 		printf(" write");
639 		if ((dp = parsereq(rp, length)) != NULL &&
640 		    (dp = parsefh(dp, v3)) != NULL) {
641 			if (v3) {
642 				TCHECK(dp[2]);
643 				printf(" %u (%u) bytes @ %" PRIu64,
644 						EXTRACT_32BITS(&dp[4]),
645 						EXTRACT_32BITS(&dp[2]),
646 						EXTRACT_64BITS(&dp[0]));
647 				if (vflag) {
648 					dp += 3;
649 					TCHECK(dp[0]);
650 					printf(" <%s>",
651 						tok2str(nfsv3_writemodes,
652 							NULL, EXTRACT_32BITS(dp)));
653 				}
654 			} else {
655 				TCHECK(dp[3]);
656 				printf(" %u (%u) bytes @ %u (%u)",
657 						EXTRACT_32BITS(&dp[3]),
658 						EXTRACT_32BITS(&dp[2]),
659 						EXTRACT_32BITS(&dp[1]),
660 						EXTRACT_32BITS(&dp[0]));
661 			}
662 			return;
663 		}
664 		break;
665 
666 	case NFSPROC_CREATE:
667 		printf(" create");
668 		if ((dp = parsereq(rp, length)) != NULL &&
669 		    parsefhn(dp, v3) != NULL)
670 			return;
671 		break;
672 
673 	case NFSPROC_MKDIR:
674 		printf(" mkdir");
675 		if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp, v3) != 0)
676 			return;
677 		break;
678 
679 	case NFSPROC_SYMLINK:
680 		printf(" symlink");
681 		if ((dp = parsereq(rp, length)) != 0 &&
682 		    (dp = parsefhn(dp, v3)) != 0) {
683 			fputs(" ->", stdout);
684 			if (v3 && (dp = parse_sattr3(dp, &sa3)) == 0)
685 				break;
686 			if (parsefn(dp) == 0)
687 				break;
688 			if (v3 && vflag)
689 				print_sattr3(&sa3, vflag);
690 			return;
691 		}
692 		break;
693 
694 	case NFSPROC_MKNOD:
695 		printf(" mknod");
696 		if ((dp = parsereq(rp, length)) != 0 &&
697 		    (dp = parsefhn(dp, v3)) != 0) {
698 			TCHECK(*dp);
699 			type = (nfs_type)EXTRACT_32BITS(dp);
700 			dp++;
701 			if ((dp = parse_sattr3(dp, &sa3)) == 0)
702 				break;
703 			printf(" %s", tok2str(type2str, "unk-ft %d", type));
704 			if (vflag && (type == NFCHR || type == NFBLK)) {
705 				TCHECK(dp[1]);
706 				printf(" %u/%u",
707 				       EXTRACT_32BITS(&dp[0]),
708 				       EXTRACT_32BITS(&dp[1]));
709 				dp += 2;
710 			}
711 			if (vflag)
712 				print_sattr3(&sa3, vflag);
713 			return;
714 		}
715 		break;
716 
717 	case NFSPROC_REMOVE:
718 		printf(" remove");
719 		if ((dp = parsereq(rp, length)) != NULL &&
720 		    parsefhn(dp, v3) != NULL)
721 			return;
722 		break;
723 
724 	case NFSPROC_RMDIR:
725 		printf(" rmdir");
726 		if ((dp = parsereq(rp, length)) != NULL &&
727 		    parsefhn(dp, v3) != NULL)
728 			return;
729 		break;
730 
731 	case NFSPROC_RENAME:
732 		printf(" rename");
733 		if ((dp = parsereq(rp, length)) != NULL &&
734 		    (dp = parsefhn(dp, v3)) != NULL) {
735 			fputs(" ->", stdout);
736 			if (parsefhn(dp, v3) != NULL)
737 				return;
738 		}
739 		break;
740 
741 	case NFSPROC_LINK:
742 		printf(" link");
743 		if ((dp = parsereq(rp, length)) != NULL &&
744 		    (dp = parsefh(dp, v3)) != NULL) {
745 			fputs(" ->", stdout);
746 			if (parsefhn(dp, v3) != NULL)
747 				return;
748 		}
749 		break;
750 
751 	case NFSPROC_READDIR:
752 		printf(" readdir");
753 		if ((dp = parsereq(rp, length)) != NULL &&
754 		    (dp = parsefh(dp, v3)) != NULL) {
755 			if (v3) {
756 				TCHECK(dp[4]);
757 				/*
758 				 * We shouldn't really try to interpret the
759 				 * offset cookie here.
760 				 */
761 				printf(" %u bytes @ %" PRId64,
762 				    EXTRACT_32BITS(&dp[4]),
763 				    EXTRACT_64BITS(&dp[0]));
764 				if (vflag)
765 					printf(" verf %08x%08x", dp[2],
766 					       dp[3]);
767 			} else {
768 				TCHECK(dp[1]);
769 				/*
770 				 * Print the offset as signed, since -1 is
771 				 * common, but offsets > 2^31 aren't.
772 				 */
773 				printf(" %u bytes @ %d",
774 				    EXTRACT_32BITS(&dp[1]),
775 				    EXTRACT_32BITS(&dp[0]));
776 			}
777 			return;
778 		}
779 		break;
780 
781 	case NFSPROC_READDIRPLUS:
782 		printf(" readdirplus");
783 		if ((dp = parsereq(rp, length)) != NULL &&
784 		    (dp = parsefh(dp, v3)) != NULL) {
785 			TCHECK(dp[4]);
786 			/*
787 			 * We don't try to interpret the offset
788 			 * cookie here.
789 			 */
790 			printf(" %u bytes @ %" PRId64,
791 				EXTRACT_32BITS(&dp[4]),
792 				EXTRACT_64BITS(&dp[0]));
793 			if (vflag) {
794 				TCHECK(dp[5]);
795 				printf(" max %u verf %08x%08x",
796 				       EXTRACT_32BITS(&dp[5]), dp[2], dp[3]);
797 			}
798 			return;
799 		}
800 		break;
801 
802 	case NFSPROC_FSSTAT:
803 		printf(" fsstat");
804 		if ((dp = parsereq(rp, length)) != NULL &&
805 		    parsefh(dp, v3) != NULL)
806 			return;
807 		break;
808 
809 	case NFSPROC_FSINFO:
810 		printf(" fsinfo");
811 		if ((dp = parsereq(rp, length)) != NULL &&
812 		    parsefh(dp, v3) != NULL)
813 			return;
814 		break;
815 
816 	case NFSPROC_PATHCONF:
817 		printf(" pathconf");
818 		if ((dp = parsereq(rp, length)) != NULL &&
819 		    parsefh(dp, v3) != NULL)
820 			return;
821 		break;
822 
823 	case NFSPROC_COMMIT:
824 		printf(" commit");
825 		if ((dp = parsereq(rp, length)) != NULL &&
826 		    (dp = parsefh(dp, v3)) != NULL) {
827 			TCHECK(dp[2]);
828 			printf(" %u bytes @ %" PRIu64,
829 				EXTRACT_32BITS(&dp[2]),
830 				EXTRACT_64BITS(&dp[0]));
831 			return;
832 		}
833 		break;
834 
835 	default:
836 		printf(" proc-%u", EXTRACT_32BITS(&rp->rm_call.cb_proc));
837 		return;
838 	}
839 
840 trunc:
841 	if (!nfserr)
842 		fputs(" [|nfs]", stdout);
843 }
844 
845 /*
846  * Print out an NFS file handle.
847  * We assume packet was not truncated before the end of the
848  * file handle pointed to by dp.
849  *
850  * Note: new version (using portable file-handle parser) doesn't produce
851  * generation number.  It probably could be made to do that, with some
852  * additional hacking on the parser code.
853  */
854 static void
855 nfs_printfh(register const u_int32_t *dp, const u_int len)
856 {
857 	my_fsid fsid;
858 	ino_t ino;
859 	const char *sfsname = NULL;
860 	char *spacep;
861 
862 	if (uflag) {
863 		u_int i;
864 		char const *sep = "";
865 
866 		printf(" fh[");
867 		for (i=0; i<len; i++) {
868 			(void)printf("%s%x", sep, dp[i]);
869 			sep = ":";
870 		}
871 		printf("]");
872 		return;
873 	}
874 
875 	Parse_fh((const u_char *)dp, len, &fsid, &ino, NULL, &sfsname, 0);
876 
877 	if (sfsname) {
878 		/* file system ID is ASCII, not numeric, for this server OS */
879 		static char temp[NFSX_V3FHMAX+1];
880 
881 		/* Make sure string is null-terminated */
882 		strncpy(temp, sfsname, NFSX_V3FHMAX);
883 		temp[sizeof(temp) - 1] = '\0';
884 		/* Remove trailing spaces */
885 		spacep = strchr(temp, ' ');
886 		if (spacep)
887 			*spacep = '\0';
888 
889 		(void)printf(" fh %s/", temp);
890 	} else {
891 		(void)printf(" fh %d,%d/",
892 			     fsid.Fsid_dev.Major, fsid.Fsid_dev.Minor);
893 	}
894 
895 	if(fsid.Fsid_dev.Minor == 257)
896 		/* Print the undecoded handle */
897 		(void)printf("%s", fsid.Opaque_Handle);
898 	else
899 		(void)printf("%ld", (long) ino);
900 }
901 
902 /*
903  * Maintain a small cache of recent client.XID.server/proc pairs, to allow
904  * us to match up replies with requests and thus to know how to parse
905  * the reply.
906  */
907 
908 struct xid_map_entry {
909 	u_int32_t	xid;		/* transaction ID (net order) */
910 	int ipver;			/* IP version (4 or 6) */
911 #ifdef INET6
912 	struct in6_addr	client;		/* client IP address (net order) */
913 	struct in6_addr	server;		/* server IP address (net order) */
914 #else
915 	struct in_addr	client;		/* client IP address (net order) */
916 	struct in_addr	server;		/* server IP address (net order) */
917 #endif
918 	u_int32_t	proc;		/* call proc number (host order) */
919 	u_int32_t	vers;		/* program version (host order) */
920 };
921 
922 /*
923  * Map entries are kept in an array that we manage as a ring;
924  * new entries are always added at the tail of the ring.  Initially,
925  * all the entries are zero and hence don't match anything.
926  */
927 
928 #define	XIDMAPSIZE	64
929 
930 struct xid_map_entry xid_map[XIDMAPSIZE];
931 
932 int	xid_map_next = 0;
933 int	xid_map_hint = 0;
934 
935 static int
936 xid_map_enter(const struct sunrpc_msg *rp, const u_char *bp)
937 {
938 	struct ip *ip = NULL;
939 #ifdef INET6
940 	struct ip6_hdr *ip6 = NULL;
941 #endif
942 	struct xid_map_entry *xmep;
943 
944 	if (!TTEST(rp->rm_call.cb_vers))
945 		return (0);
946 	switch (IP_V((struct ip *)bp)) {
947 	case 4:
948 		ip = (struct ip *)bp;
949 		break;
950 #ifdef INET6
951 	case 6:
952 		ip6 = (struct ip6_hdr *)bp;
953 		break;
954 #endif
955 	default:
956 		return (1);
957 	}
958 
959 	xmep = &xid_map[xid_map_next];
960 
961 	if (++xid_map_next >= XIDMAPSIZE)
962 		xid_map_next = 0;
963 
964 	xmep->xid = rp->rm_xid;
965 	if (ip) {
966 		xmep->ipver = 4;
967 		memcpy(&xmep->client, &ip->ip_src, sizeof(ip->ip_src));
968 		memcpy(&xmep->server, &ip->ip_dst, sizeof(ip->ip_dst));
969 	}
970 #ifdef INET6
971 	else if (ip6) {
972 		xmep->ipver = 6;
973 		memcpy(&xmep->client, &ip6->ip6_src, sizeof(ip6->ip6_src));
974 		memcpy(&xmep->server, &ip6->ip6_dst, sizeof(ip6->ip6_dst));
975 	}
976 #endif
977 	xmep->proc = EXTRACT_32BITS(&rp->rm_call.cb_proc);
978 	xmep->vers = EXTRACT_32BITS(&rp->rm_call.cb_vers);
979 	return (1);
980 }
981 
982 /*
983  * Returns 0 and puts NFSPROC_xxx in proc return and
984  * version in vers return, or returns -1 on failure
985  */
986 static int
987 xid_map_find(const struct sunrpc_msg *rp, const u_char *bp, u_int32_t *proc,
988 	     u_int32_t *vers)
989 {
990 	int i;
991 	struct xid_map_entry *xmep;
992 	u_int32_t xid = rp->rm_xid;
993 	struct ip *ip = (struct ip *)bp;
994 #ifdef INET6
995 	struct ip6_hdr *ip6 = (struct ip6_hdr *)bp;
996 #endif
997 	int cmp;
998 
999 	/* Start searching from where we last left off */
1000 	i = xid_map_hint;
1001 	do {
1002 		xmep = &xid_map[i];
1003 		cmp = 1;
1004 		if (xmep->ipver != IP_V(ip) || xmep->xid != xid)
1005 			goto nextitem;
1006 		switch (xmep->ipver) {
1007 		case 4:
1008 			if (memcmp(&ip->ip_src, &xmep->server,
1009 				   sizeof(ip->ip_src)) != 0 ||
1010 			    memcmp(&ip->ip_dst, &xmep->client,
1011 				   sizeof(ip->ip_dst)) != 0) {
1012 				cmp = 0;
1013 			}
1014 			break;
1015 #ifdef INET6
1016 		case 6:
1017 			if (memcmp(&ip6->ip6_src, &xmep->server,
1018 				   sizeof(ip6->ip6_src)) != 0 ||
1019 			    memcmp(&ip6->ip6_dst, &xmep->client,
1020 				   sizeof(ip6->ip6_dst)) != 0) {
1021 				cmp = 0;
1022 			}
1023 			break;
1024 #endif
1025 		default:
1026 			cmp = 0;
1027 			break;
1028 		}
1029 		if (cmp) {
1030 			/* match */
1031 			xid_map_hint = i;
1032 			*proc = xmep->proc;
1033 			*vers = xmep->vers;
1034 			return 0;
1035 		}
1036 	nextitem:
1037 		if (++i >= XIDMAPSIZE)
1038 			i = 0;
1039 	} while (i != xid_map_hint);
1040 
1041 	/* search failed */
1042 	return (-1);
1043 }
1044 
1045 /*
1046  * Routines for parsing reply packets
1047  */
1048 
1049 /*
1050  * Return a pointer to the beginning of the actual results.
1051  * If the packet was truncated, return 0.
1052  */
1053 static const u_int32_t *
1054 parserep(register const struct sunrpc_msg *rp, register u_int length)
1055 {
1056 	register const u_int32_t *dp;
1057 	u_int len;
1058 	enum sunrpc_accept_stat astat;
1059 
1060 	/*
1061 	 * Portability note:
1062 	 * Here we find the address of the ar_verf credentials.
1063 	 * Originally, this calculation was
1064 	 *	dp = (u_int32_t *)&rp->rm_reply.rp_acpt.ar_verf
1065 	 * On the wire, the rp_acpt field starts immediately after
1066 	 * the (32 bit) rp_stat field.  However, rp_acpt (which is a
1067 	 * "struct accepted_reply") contains a "struct opaque_auth",
1068 	 * whose internal representation contains a pointer, so on a
1069 	 * 64-bit machine the compiler inserts 32 bits of padding
1070 	 * before rp->rm_reply.rp_acpt.ar_verf.  So, we cannot use
1071 	 * the internal representation to parse the on-the-wire
1072 	 * representation.  Instead, we skip past the rp_stat field,
1073 	 * which is an "enum" and so occupies one 32-bit word.
1074 	 */
1075 	dp = ((const u_int32_t *)&rp->rm_reply) + 1;
1076 	TCHECK(dp[1]);
1077 	len = EXTRACT_32BITS(&dp[1]);
1078 	if (len >= length)
1079 		return (NULL);
1080 	/*
1081 	 * skip past the ar_verf credentials.
1082 	 */
1083 	dp += (len + (2*sizeof(u_int32_t) + 3)) / sizeof(u_int32_t);
1084 	TCHECK2(dp[0], 0);
1085 
1086 	/*
1087 	 * now we can check the ar_stat field
1088 	 */
1089 	astat = (enum sunrpc_accept_stat) EXTRACT_32BITS(dp);
1090 	switch (astat) {
1091 
1092 	case SUNRPC_SUCCESS:
1093 		break;
1094 
1095 	case SUNRPC_PROG_UNAVAIL:
1096 		printf(" PROG_UNAVAIL");
1097 		nfserr = 1;		/* suppress trunc string */
1098 		return (NULL);
1099 
1100 	case SUNRPC_PROG_MISMATCH:
1101 		printf(" PROG_MISMATCH");
1102 		nfserr = 1;		/* suppress trunc string */
1103 		return (NULL);
1104 
1105 	case SUNRPC_PROC_UNAVAIL:
1106 		printf(" PROC_UNAVAIL");
1107 		nfserr = 1;		/* suppress trunc string */
1108 		return (NULL);
1109 
1110 	case SUNRPC_GARBAGE_ARGS:
1111 		printf(" GARBAGE_ARGS");
1112 		nfserr = 1;		/* suppress trunc string */
1113 		return (NULL);
1114 
1115 	case SUNRPC_SYSTEM_ERR:
1116 		printf(" SYSTEM_ERR");
1117 		nfserr = 1;		/* suppress trunc string */
1118 		return (NULL);
1119 
1120 	default:
1121 		printf(" ar_stat %d", astat);
1122 		nfserr = 1;		/* suppress trunc string */
1123 		return (NULL);
1124 	}
1125 	/* successful return */
1126 	TCHECK2(*dp, sizeof(astat));
1127 	return ((u_int32_t *) (sizeof(astat) + ((char *)dp)));
1128 trunc:
1129 	return (0);
1130 }
1131 
1132 static const u_int32_t *
1133 parsestatus(const u_int32_t *dp, int *er)
1134 {
1135 	int errnum;
1136 
1137 	TCHECK(dp[0]);
1138 
1139 	errnum = EXTRACT_32BITS(&dp[0]);
1140 	if (er)
1141 		*er = errnum;
1142 	if (errnum != 0) {
1143 		if (!qflag)
1144 			printf(" ERROR: %s",
1145 			    tok2str(status2str, "unk %d", errnum));
1146 		nfserr = 1;
1147 	}
1148 	return (dp + 1);
1149 trunc:
1150 	return NULL;
1151 }
1152 
1153 static const u_int32_t *
1154 parsefattr(const u_int32_t *dp, int verbose, int v3)
1155 {
1156 	const struct nfs_fattr *fap;
1157 
1158 	fap = (const struct nfs_fattr *)dp;
1159 	TCHECK(fap->fa_gid);
1160 	if (verbose) {
1161 		printf(" %s %o ids %d/%d",
1162 		    tok2str(type2str, "unk-ft %d ",
1163 		    EXTRACT_32BITS(&fap->fa_type)),
1164 		    EXTRACT_32BITS(&fap->fa_mode),
1165 		    EXTRACT_32BITS(&fap->fa_uid),
1166 		    EXTRACT_32BITS(&fap->fa_gid));
1167 		if (v3) {
1168 			TCHECK(fap->fa3_size);
1169 			printf(" sz %" PRIu64,
1170 				EXTRACT_64BITS((u_int32_t *)&fap->fa3_size));
1171 		} else {
1172 			TCHECK(fap->fa2_size);
1173 			printf(" sz %d", EXTRACT_32BITS(&fap->fa2_size));
1174 		}
1175 	}
1176 	/* print lots more stuff */
1177 	if (verbose > 1) {
1178 		if (v3) {
1179 			TCHECK(fap->fa3_ctime);
1180 			printf(" nlink %d rdev %d/%d",
1181 			       EXTRACT_32BITS(&fap->fa_nlink),
1182 			       EXTRACT_32BITS(&fap->fa3_rdev.specdata1),
1183 			       EXTRACT_32BITS(&fap->fa3_rdev.specdata2));
1184 			printf(" fsid %" PRIx64,
1185 				EXTRACT_64BITS((u_int32_t *)&fap->fa3_fsid));
1186 			printf(" fileid %" PRIx64,
1187 				EXTRACT_64BITS((u_int32_t *)&fap->fa3_fileid));
1188 			printf(" a/m/ctime %u.%06u",
1189 			       EXTRACT_32BITS(&fap->fa3_atime.nfsv3_sec),
1190 			       EXTRACT_32BITS(&fap->fa3_atime.nfsv3_nsec));
1191 			printf(" %u.%06u",
1192 			       EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_sec),
1193 			       EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_nsec));
1194 			printf(" %u.%06u",
1195 			       EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_sec),
1196 			       EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_nsec));
1197 		} else {
1198 			TCHECK(fap->fa2_ctime);
1199 			printf(" nlink %d rdev %x fsid %x nodeid %x a/m/ctime",
1200 			       EXTRACT_32BITS(&fap->fa_nlink),
1201 			       EXTRACT_32BITS(&fap->fa2_rdev),
1202 			       EXTRACT_32BITS(&fap->fa2_fsid),
1203 			       EXTRACT_32BITS(&fap->fa2_fileid));
1204 			printf(" %u.%06u",
1205 			       EXTRACT_32BITS(&fap->fa2_atime.nfsv2_sec),
1206 			       EXTRACT_32BITS(&fap->fa2_atime.nfsv2_usec));
1207 			printf(" %u.%06u",
1208 			       EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_sec),
1209 			       EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_usec));
1210 			printf(" %u.%06u",
1211 			       EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_sec),
1212 			       EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_usec));
1213 		}
1214 	}
1215 	return ((const u_int32_t *)((unsigned char *)dp +
1216 		(v3 ? NFSX_V3FATTR : NFSX_V2FATTR)));
1217 trunc:
1218 	return (NULL);
1219 }
1220 
1221 static int
1222 parseattrstat(const u_int32_t *dp, int verbose, 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 
1232 	return (parsefattr(dp, verbose, v3) != NULL);
1233 }
1234 
1235 static int
1236 parsediropres(const u_int32_t *dp)
1237 {
1238 	int er;
1239 
1240 	if (!(dp = parsestatus(dp, &er)))
1241 		return (0);
1242 	if (er)
1243 		return (1);
1244 
1245 	dp = parsefh(dp, 0);
1246 	if (dp == NULL)
1247 		return (0);
1248 
1249 	return (parsefattr(dp, vflag, 0) != NULL);
1250 }
1251 
1252 static int
1253 parselinkres(const u_int32_t *dp, int v3)
1254 {
1255 	int er;
1256 
1257 	dp = parsestatus(dp, &er);
1258 	if (dp == NULL)
1259 		return(0);
1260 	if (er)
1261 		return(1);
1262 	if (v3 && !(dp = parse_post_op_attr(dp, vflag)))
1263 		return (0);
1264 	putchar(' ');
1265 	return (parsefn(dp) != NULL);
1266 }
1267 
1268 static int
1269 parsestatfs(const u_int32_t *dp, int v3)
1270 {
1271 	const struct nfs_statfs *sfsp;
1272 	int er;
1273 
1274 	dp = parsestatus(dp, &er);
1275 	if (dp == NULL)
1276 		return (0);
1277 	if (!v3 && er)
1278 		return (1);
1279 
1280 	if (qflag)
1281 		return(1);
1282 
1283 	if (v3) {
1284 		if (vflag)
1285 			printf(" POST:");
1286 		if (!(dp = parse_post_op_attr(dp, vflag)))
1287 			return (0);
1288 	}
1289 
1290 	TCHECK2(*dp, (v3 ? NFSX_V3STATFS : NFSX_V2STATFS));
1291 
1292 	sfsp = (const struct nfs_statfs *)dp;
1293 
1294 	if (v3) {
1295 		printf(" tbytes %" PRIu64 " fbytes %" PRIu64 " abytes %" PRIu64,
1296 			EXTRACT_64BITS((u_int32_t *)&sfsp->sf_tbytes),
1297 			EXTRACT_64BITS((u_int32_t *)&sfsp->sf_fbytes),
1298 			EXTRACT_64BITS((u_int32_t *)&sfsp->sf_abytes));
1299 		if (vflag) {
1300 			printf(" tfiles %" PRIu64 " ffiles %" PRIu64 " afiles %" PRIu64 " invar %u",
1301 			       EXTRACT_64BITS((u_int32_t *)&sfsp->sf_tfiles),
1302 			       EXTRACT_64BITS((u_int32_t *)&sfsp->sf_ffiles),
1303 			       EXTRACT_64BITS((u_int32_t *)&sfsp->sf_afiles),
1304 			       EXTRACT_32BITS(&sfsp->sf_invarsec));
1305 		}
1306 	} else {
1307 		printf(" tsize %d bsize %d blocks %d bfree %d bavail %d",
1308 			EXTRACT_32BITS(&sfsp->sf_tsize),
1309 			EXTRACT_32BITS(&sfsp->sf_bsize),
1310 			EXTRACT_32BITS(&sfsp->sf_blocks),
1311 			EXTRACT_32BITS(&sfsp->sf_bfree),
1312 			EXTRACT_32BITS(&sfsp->sf_bavail));
1313 	}
1314 
1315 	return (1);
1316 trunc:
1317 	return (0);
1318 }
1319 
1320 static int
1321 parserddires(const u_int32_t *dp)
1322 {
1323 	int er;
1324 
1325 	dp = parsestatus(dp, &er);
1326 	if (dp == NULL)
1327 		return (0);
1328 	if (er)
1329 		return (1);
1330 	if (qflag)
1331 		return (1);
1332 
1333 	TCHECK(dp[2]);
1334 	printf(" offset %x size %d ",
1335 	       EXTRACT_32BITS(&dp[0]), EXTRACT_32BITS(&dp[1]));
1336 	if (dp[2] != 0)
1337 		printf(" eof");
1338 
1339 	return (1);
1340 trunc:
1341 	return (0);
1342 }
1343 
1344 static const u_int32_t *
1345 parse_wcc_attr(const u_int32_t *dp)
1346 {
1347 	printf(" sz %" PRIu64, EXTRACT_64BITS(&dp[0]));
1348 	printf(" mtime %u.%06u ctime %u.%06u",
1349 	       EXTRACT_32BITS(&dp[2]), EXTRACT_32BITS(&dp[3]),
1350 	       EXTRACT_32BITS(&dp[4]), EXTRACT_32BITS(&dp[5]));
1351 	return (dp + 6);
1352 }
1353 
1354 /*
1355  * Pre operation attributes. Print only if vflag > 1.
1356  */
1357 static const u_int32_t *
1358 parse_pre_op_attr(const u_int32_t *dp, int verbose)
1359 {
1360 	TCHECK(dp[0]);
1361 	if (!EXTRACT_32BITS(&dp[0]))
1362 		return (dp + 1);
1363 	dp++;
1364 	TCHECK2(*dp, 24);
1365 	if (verbose > 1) {
1366 		return parse_wcc_attr(dp);
1367 	} else {
1368 		/* If not verbose enough, just skip over wcc_attr */
1369 		return (dp + 6);
1370 	}
1371 trunc:
1372 	return (NULL);
1373 }
1374 
1375 /*
1376  * Post operation attributes are printed if vflag >= 1
1377  */
1378 static const u_int32_t *
1379 parse_post_op_attr(const u_int32_t *dp, int verbose)
1380 {
1381 	TCHECK(dp[0]);
1382 	if (!EXTRACT_32BITS(&dp[0]))
1383 		return (dp + 1);
1384 	dp++;
1385 	if (verbose) {
1386 		return parsefattr(dp, verbose, 1);
1387 	} else
1388 		return (dp + (NFSX_V3FATTR / sizeof (u_int32_t)));
1389 trunc:
1390 	return (NULL);
1391 }
1392 
1393 static const u_int32_t *
1394 parse_wcc_data(const u_int32_t *dp, int verbose)
1395 {
1396 	if (verbose > 1)
1397 		printf(" PRE:");
1398 	if (!(dp = parse_pre_op_attr(dp, verbose)))
1399 		return (0);
1400 
1401 	if (verbose)
1402 		printf(" POST:");
1403 	return parse_post_op_attr(dp, verbose);
1404 }
1405 
1406 static const u_int32_t *
1407 parsecreateopres(const u_int32_t *dp, int verbose)
1408 {
1409 	int er;
1410 
1411 	if (!(dp = parsestatus(dp, &er)))
1412 		return (0);
1413 	if (er)
1414 		dp = parse_wcc_data(dp, verbose);
1415 	else {
1416 		TCHECK(dp[0]);
1417 		if (!EXTRACT_32BITS(&dp[0]))
1418 			return (dp + 1);
1419 		dp++;
1420 		if (!(dp = parsefh(dp, 1)))
1421 			return (0);
1422 		if (verbose) {
1423 			if (!(dp = parse_post_op_attr(dp, verbose)))
1424 				return (0);
1425 			if (vflag > 1) {
1426 				printf(" dir attr:");
1427 				dp = parse_wcc_data(dp, verbose);
1428 			}
1429 		}
1430 	}
1431 	return (dp);
1432 trunc:
1433 	return (NULL);
1434 }
1435 
1436 static int
1437 parsewccres(const u_int32_t *dp, int verbose)
1438 {
1439 	int er;
1440 
1441 	if (!(dp = parsestatus(dp, &er)))
1442 		return (0);
1443 	return parse_wcc_data(dp, verbose) != 0;
1444 }
1445 
1446 static const u_int32_t *
1447 parsev3rddirres(const u_int32_t *dp, int verbose)
1448 {
1449 	int er;
1450 
1451 	if (!(dp = parsestatus(dp, &er)))
1452 		return (0);
1453 	if (vflag)
1454 		printf(" POST:");
1455 	if (!(dp = parse_post_op_attr(dp, verbose)))
1456 		return (0);
1457 	if (er)
1458 		return dp;
1459 	if (vflag) {
1460 		TCHECK(dp[1]);
1461 		printf(" verf %08x%08x", dp[0], dp[1]);
1462 		dp += 2;
1463 	}
1464 	return dp;
1465 trunc:
1466 	return (NULL);
1467 }
1468 
1469 static int
1470 parsefsinfo(const u_int32_t *dp)
1471 {
1472 	struct nfsv3_fsinfo *sfp;
1473 	int er;
1474 
1475 	if (!(dp = parsestatus(dp, &er)))
1476 		return (0);
1477 	if (vflag)
1478 		printf(" POST:");
1479 	if (!(dp = parse_post_op_attr(dp, vflag)))
1480 		return (0);
1481 	if (er)
1482 		return (1);
1483 
1484 	sfp = (struct nfsv3_fsinfo *)dp;
1485 	TCHECK(*sfp);
1486 	printf(" rtmax %u rtpref %u wtmax %u wtpref %u dtpref %u",
1487 	       EXTRACT_32BITS(&sfp->fs_rtmax),
1488 	       EXTRACT_32BITS(&sfp->fs_rtpref),
1489 	       EXTRACT_32BITS(&sfp->fs_wtmax),
1490 	       EXTRACT_32BITS(&sfp->fs_wtpref),
1491 	       EXTRACT_32BITS(&sfp->fs_dtpref));
1492 	if (vflag) {
1493 		printf(" rtmult %u wtmult %u maxfsz %" PRIu64,
1494 		       EXTRACT_32BITS(&sfp->fs_rtmult),
1495 		       EXTRACT_32BITS(&sfp->fs_wtmult),
1496 		       EXTRACT_64BITS((u_int32_t *)&sfp->fs_maxfilesize));
1497 		printf(" delta %u.%06u ",
1498 		       EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_sec),
1499 		       EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_nsec));
1500 	}
1501 	return (1);
1502 trunc:
1503 	return (0);
1504 }
1505 
1506 static int
1507 parsepathconf(const u_int32_t *dp)
1508 {
1509 	int er;
1510 	struct nfsv3_pathconf *spp;
1511 
1512 	if (!(dp = parsestatus(dp, &er)))
1513 		return (0);
1514 	if (vflag)
1515 		printf(" POST:");
1516 	if (!(dp = parse_post_op_attr(dp, vflag)))
1517 		return (0);
1518 	if (er)
1519 		return (1);
1520 
1521 	spp = (struct nfsv3_pathconf *)dp;
1522 	TCHECK(*spp);
1523 
1524 	printf(" linkmax %u namemax %u %s %s %s %s",
1525 	       EXTRACT_32BITS(&spp->pc_linkmax),
1526 	       EXTRACT_32BITS(&spp->pc_namemax),
1527 	       EXTRACT_32BITS(&spp->pc_notrunc) ? "notrunc" : "",
1528 	       EXTRACT_32BITS(&spp->pc_chownrestricted) ? "chownres" : "",
1529 	       EXTRACT_32BITS(&spp->pc_caseinsensitive) ? "igncase" : "",
1530 	       EXTRACT_32BITS(&spp->pc_casepreserving) ? "keepcase" : "");
1531 	return (1);
1532 trunc:
1533 	return (0);
1534 }
1535 
1536 static void
1537 interp_reply(const struct sunrpc_msg *rp, u_int32_t proc, u_int32_t vers, int length)
1538 {
1539 	register const u_int32_t *dp;
1540 	register int v3;
1541 	int er;
1542 
1543 	v3 = (vers == NFS_VER3);
1544 
1545 	if (!v3 && proc < NFS_NPROCS)
1546 		proc = nfsv3_procid[proc];
1547 
1548 	switch (proc) {
1549 
1550 	case NFSPROC_NOOP:
1551 		printf(" nop");
1552 		return;
1553 
1554 	case NFSPROC_NULL:
1555 		printf(" null");
1556 		return;
1557 
1558 	case NFSPROC_GETATTR:
1559 		printf(" getattr");
1560 		dp = parserep(rp, length);
1561 		if (dp != NULL && parseattrstat(dp, !qflag, v3) != 0)
1562 			return;
1563 		break;
1564 
1565 	case NFSPROC_SETATTR:
1566 		printf(" setattr");
1567 		if (!(dp = parserep(rp, length)))
1568 			return;
1569 		if (v3) {
1570 			if (parsewccres(dp, vflag))
1571 				return;
1572 		} else {
1573 			if (parseattrstat(dp, !qflag, 0) != 0)
1574 				return;
1575 		}
1576 		break;
1577 
1578 	case NFSPROC_LOOKUP:
1579 		printf(" lookup");
1580 		if (!(dp = parserep(rp, length)))
1581 			break;
1582 		if (v3) {
1583 			if (!(dp = parsestatus(dp, &er)))
1584 				break;
1585 			if (er) {
1586 				if (vflag > 1) {
1587 					printf(" post dattr:");
1588 					dp = parse_post_op_attr(dp, vflag);
1589 				}
1590 			} else {
1591 				if (!(dp = parsefh(dp, v3)))
1592 					break;
1593 				if ((dp = parse_post_op_attr(dp, vflag)) &&
1594 				    vflag > 1) {
1595 					printf(" post dattr:");
1596 					dp = parse_post_op_attr(dp, vflag);
1597 				}
1598 			}
1599 			if (dp)
1600 				return;
1601 		} else {
1602 			if (parsediropres(dp) != 0)
1603 				return;
1604 		}
1605 		break;
1606 
1607 	case NFSPROC_ACCESS:
1608 		printf(" access");
1609 		if (!(dp = parserep(rp, length)))
1610 			break;
1611 		if (!(dp = parsestatus(dp, &er)))
1612 			break;
1613 		if (vflag)
1614 			printf(" attr:");
1615 		if (!(dp = parse_post_op_attr(dp, vflag)))
1616 			break;
1617 		if (!er)
1618 			printf(" c %04x", EXTRACT_32BITS(&dp[0]));
1619 		return;
1620 
1621 	case NFSPROC_READLINK:
1622 		printf(" readlink");
1623 		dp = parserep(rp, length);
1624 		if (dp != NULL && parselinkres(dp, v3) != 0)
1625 			return;
1626 		break;
1627 
1628 	case NFSPROC_READ:
1629 		printf(" read");
1630 		if (!(dp = parserep(rp, length)))
1631 			break;
1632 		if (v3) {
1633 			if (!(dp = parsestatus(dp, &er)))
1634 				break;
1635 			if (!(dp = parse_post_op_attr(dp, vflag)))
1636 				break;
1637 			if (er)
1638 				return;
1639 			if (vflag) {
1640 				TCHECK(dp[1]);
1641 				printf(" %u bytes", EXTRACT_32BITS(&dp[0]));
1642 				if (EXTRACT_32BITS(&dp[1]))
1643 					printf(" EOF");
1644 			}
1645 			return;
1646 		} else {
1647 			if (parseattrstat(dp, vflag, 0) != 0)
1648 				return;
1649 		}
1650 		break;
1651 
1652 	case NFSPROC_WRITE:
1653 		printf(" write");
1654 		if (!(dp = parserep(rp, length)))
1655 			break;
1656 		if (v3) {
1657 			if (!(dp = parsestatus(dp, &er)))
1658 				break;
1659 			if (!(dp = parse_wcc_data(dp, vflag)))
1660 				break;
1661 			if (er)
1662 				return;
1663 			if (vflag) {
1664 				TCHECK(dp[0]);
1665 				printf(" %u bytes", EXTRACT_32BITS(&dp[0]));
1666 				if (vflag > 1) {
1667 					TCHECK(dp[1]);
1668 					printf(" <%s>",
1669 						tok2str(nfsv3_writemodes,
1670 							NULL, EXTRACT_32BITS(&dp[1])));
1671 				}
1672 				return;
1673 			}
1674 		} else {
1675 			if (parseattrstat(dp, vflag, v3) != 0)
1676 				return;
1677 		}
1678 		break;
1679 
1680 	case NFSPROC_CREATE:
1681 		printf(" create");
1682 		if (!(dp = parserep(rp, length)))
1683 			break;
1684 		if (v3) {
1685 			if (parsecreateopres(dp, vflag) != 0)
1686 				return;
1687 		} else {
1688 			if (parsediropres(dp) != 0)
1689 				return;
1690 		}
1691 		break;
1692 
1693 	case NFSPROC_MKDIR:
1694 		printf(" mkdir");
1695 		if (!(dp = parserep(rp, length)))
1696 			break;
1697 		if (v3) {
1698 			if (parsecreateopres(dp, vflag) != 0)
1699 				return;
1700 		} else {
1701 			if (parsediropres(dp) != 0)
1702 				return;
1703 		}
1704 		break;
1705 
1706 	case NFSPROC_SYMLINK:
1707 		printf(" symlink");
1708 		if (!(dp = parserep(rp, length)))
1709 			break;
1710 		if (v3) {
1711 			if (parsecreateopres(dp, vflag) != 0)
1712 				return;
1713 		} else {
1714 			if (parsestatus(dp, &er) != 0)
1715 				return;
1716 		}
1717 		break;
1718 
1719 	case NFSPROC_MKNOD:
1720 		printf(" mknod");
1721 		if (!(dp = parserep(rp, length)))
1722 			break;
1723 		if (parsecreateopres(dp, vflag) != 0)
1724 			return;
1725 		break;
1726 
1727 	case NFSPROC_REMOVE:
1728 		printf(" remove");
1729 		if (!(dp = parserep(rp, length)))
1730 			break;
1731 		if (v3) {
1732 			if (parsewccres(dp, vflag))
1733 				return;
1734 		} else {
1735 			if (parsestatus(dp, &er) != 0)
1736 				return;
1737 		}
1738 		break;
1739 
1740 	case NFSPROC_RMDIR:
1741 		printf(" rmdir");
1742 		if (!(dp = parserep(rp, length)))
1743 			break;
1744 		if (v3) {
1745 			if (parsewccres(dp, vflag))
1746 				return;
1747 		} else {
1748 			if (parsestatus(dp, &er) != 0)
1749 				return;
1750 		}
1751 		break;
1752 
1753 	case NFSPROC_RENAME:
1754 		printf(" rename");
1755 		if (!(dp = parserep(rp, length)))
1756 			break;
1757 		if (v3) {
1758 			if (!(dp = parsestatus(dp, &er)))
1759 				break;
1760 			if (vflag) {
1761 				printf(" from:");
1762 				if (!(dp = parse_wcc_data(dp, vflag)))
1763 					break;
1764 				printf(" to:");
1765 				if (!(dp = parse_wcc_data(dp, vflag)))
1766 					break;
1767 			}
1768 			return;
1769 		} else {
1770 			if (parsestatus(dp, &er) != 0)
1771 				return;
1772 		}
1773 		break;
1774 
1775 	case NFSPROC_LINK:
1776 		printf(" link");
1777 		if (!(dp = parserep(rp, length)))
1778 			break;
1779 		if (v3) {
1780 			if (!(dp = parsestatus(dp, &er)))
1781 				break;
1782 			if (vflag) {
1783 				printf(" file POST:");
1784 				if (!(dp = parse_post_op_attr(dp, vflag)))
1785 					break;
1786 				printf(" dir:");
1787 				if (!(dp = parse_wcc_data(dp, vflag)))
1788 					break;
1789 				return;
1790 			}
1791 		} else {
1792 			if (parsestatus(dp, &er) != 0)
1793 				return;
1794 		}
1795 		break;
1796 
1797 	case NFSPROC_READDIR:
1798 		printf(" readdir");
1799 		if (!(dp = parserep(rp, length)))
1800 			break;
1801 		if (v3) {
1802 			if (parsev3rddirres(dp, vflag))
1803 				return;
1804 		} else {
1805 			if (parserddires(dp) != 0)
1806 				return;
1807 		}
1808 		break;
1809 
1810 	case NFSPROC_READDIRPLUS:
1811 		printf(" readdirplus");
1812 		if (!(dp = parserep(rp, length)))
1813 			break;
1814 		if (parsev3rddirres(dp, vflag))
1815 			return;
1816 		break;
1817 
1818 	case NFSPROC_FSSTAT:
1819 		printf(" fsstat");
1820 		dp = parserep(rp, length);
1821 		if (dp != NULL && parsestatfs(dp, v3) != 0)
1822 			return;
1823 		break;
1824 
1825 	case NFSPROC_FSINFO:
1826 		printf(" fsinfo");
1827 		dp = parserep(rp, length);
1828 		if (dp != NULL && parsefsinfo(dp) != 0)
1829 			return;
1830 		break;
1831 
1832 	case NFSPROC_PATHCONF:
1833 		printf(" pathconf");
1834 		dp = parserep(rp, length);
1835 		if (dp != NULL && parsepathconf(dp) != 0)
1836 			return;
1837 		break;
1838 
1839 	case NFSPROC_COMMIT:
1840 		printf(" commit");
1841 		dp = parserep(rp, length);
1842 		if (dp != NULL && parsewccres(dp, vflag) != 0)
1843 			return;
1844 		break;
1845 
1846 	default:
1847 		printf(" proc-%u", proc);
1848 		return;
1849 	}
1850 trunc:
1851 	if (!nfserr)
1852 		fputs(" [|nfs]", stdout);
1853 }
1854