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