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