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