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