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