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