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