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