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