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