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