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