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$
22 */
23
24#ifndef lint
25static const char rcsid[] _U_ =
26    "@(#) $Header: /tcpdump/master/tcpdump/print-nfs.c,v 1.111 2007-12-22 03:08:04 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 int 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, reply_stat;
293	char srcid[20], dstid[20];	/*fits 32bit*/
294	enum sunrpc_reject_stat rstat;
295	u_int32_t rlow;
296	u_int32_t rhigh;
297	enum sunrpc_auth_stat rwhy;
298
299	nfserr = 0;		/* assume no error */
300	rp = (const struct sunrpc_msg *)bp;
301
302	TCHECK(rp->rm_xid);
303	if (!nflag) {
304		strlcpy(srcid, "nfs", sizeof(srcid));
305		snprintf(dstid, sizeof(dstid), "%u",
306		    EXTRACT_32BITS(&rp->rm_xid));
307	} else {
308		snprintf(srcid, sizeof(srcid), "%u", NFS_PORT);
309		snprintf(dstid, sizeof(dstid), "%u",
310		    EXTRACT_32BITS(&rp->rm_xid));
311	}
312	print_nfsaddr(bp2, srcid, dstid);
313	TCHECK(rp->rm_reply.rp_stat);
314	reply_stat = EXTRACT_32BITS(&rp->rm_reply.rp_stat);
315	switch (reply_stat) {
316
317	case SUNRPC_MSG_ACCEPTED:
318		(void)printf("reply ok %u", length);
319		if (xid_map_find(rp, bp2, &proc, &vers) >= 0)
320			interp_reply(rp, proc, vers, length);
321		break;
322
323	case SUNRPC_MSG_DENIED:
324		(void)printf("reply ERR %u: ", length);
325		TCHECK(rp->rm_reply.rp_reject.rj_stat);
326		rstat = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_stat);
327		switch (rstat) {
328
329		case SUNRPC_RPC_MISMATCH:
330			TCHECK(rp->rm_reply.rp_reject.rj_vers.high);
331			rlow = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_vers.low);
332			rhigh = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_vers.high);
333			(void)printf("RPC Version mismatch (%u-%u)",
334			    rlow, rhigh);
335			break;
336
337		case SUNRPC_AUTH_ERROR:
338			TCHECK(rp->rm_reply.rp_reject.rj_why);
339			rwhy = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_why);
340			(void)printf("Auth ");
341			switch (rwhy) {
342
343			case SUNRPC_AUTH_OK:
344				(void)printf("OK");
345				break;
346
347			case SUNRPC_AUTH_BADCRED:
348				(void)printf("Bogus Credentials (seal broken)");
349				break;
350
351			case SUNRPC_AUTH_REJECTEDCRED:
352				(void)printf("Rejected Credentials (client should begin new session)");
353				break;
354
355			case SUNRPC_AUTH_BADVERF:
356				(void)printf("Bogus Verifier (seal broken)");
357				break;
358
359			case SUNRPC_AUTH_REJECTEDVERF:
360				(void)printf("Verifier expired or was replayed");
361				break;
362
363			case SUNRPC_AUTH_TOOWEAK:
364				(void)printf("Credentials are too weak");
365				break;
366
367			case SUNRPC_AUTH_INVALIDRESP:
368				(void)printf("Bogus response verifier");
369				break;
370
371			case SUNRPC_AUTH_FAILED:
372				(void)printf("Unknown failure");
373				break;
374
375			default:
376				(void)printf("Invalid failure code %u",
377				    (unsigned int)rwhy);
378				break;
379			}
380			break;
381
382		default:
383			(void)printf("Unknown reason for rejecting rpc message %u",
384			    (unsigned int)rstat);
385			break;
386		}
387		break;
388
389	default:
390		(void)printf("reply Unknown rpc response code=%u %u",
391		    reply_stat, length);
392		break;
393	}
394	return;
395
396trunc:
397	if (!nfserr)
398		fputs(" [|nfs]", stdout);
399}
400
401/*
402 * Return a pointer to the first file handle in the packet.
403 * If the packet was truncated, return 0.
404 */
405static const u_int32_t *
406parsereq(register const struct sunrpc_msg *rp, register u_int length)
407{
408	register const u_int32_t *dp;
409	register u_int len;
410
411	/*
412	 * find the start of the req data (if we captured it)
413	 */
414	dp = (u_int32_t *)&rp->rm_call.cb_cred;
415	TCHECK(dp[1]);
416	len = EXTRACT_32BITS(&dp[1]);
417	if (len < length) {
418		dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
419		TCHECK(dp[1]);
420		len = EXTRACT_32BITS(&dp[1]);
421		if (len < length) {
422			dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
423			TCHECK2(dp[0], 0);
424			return (dp);
425		}
426	}
427trunc:
428	return (NULL);
429}
430
431/*
432 * Print out an NFS file handle and return a pointer to following word.
433 * If packet was truncated, return 0.
434 */
435static const u_int32_t *
436parsefh(register const u_int32_t *dp, int v3)
437{
438	u_int len;
439
440	if (v3) {
441		TCHECK(dp[0]);
442		len = EXTRACT_32BITS(dp) / 4;
443		dp++;
444	} else
445		len = NFSX_V2FH / 4;
446
447	if (TTEST2(*dp, len * sizeof(*dp))) {
448		nfs_printfh(dp, len);
449		return (dp + len);
450	}
451trunc:
452	return (NULL);
453}
454
455/*
456 * Print out a file name and return pointer to 32-bit word past it.
457 * If packet was truncated, return 0.
458 */
459static const u_int32_t *
460parsefn(register const u_int32_t *dp)
461{
462	register u_int32_t len;
463	register const u_char *cp;
464
465	/* Bail if we don't have the string length */
466	TCHECK(*dp);
467
468	/* Fetch string length; convert to host order */
469	len = *dp++;
470	NTOHL(len);
471
472	TCHECK2(*dp, ((len + 3) & ~3));
473
474	cp = (u_char *)dp;
475	/* Update 32-bit pointer (NFS filenames padded to 32-bit boundaries) */
476	dp += ((len + 3) & ~3) / sizeof(*dp);
477	putchar('"');
478	if (fn_printn(cp, len, snapend)) {
479		putchar('"');
480		goto trunc;
481	}
482	putchar('"');
483
484	return (dp);
485trunc:
486	return NULL;
487}
488
489/*
490 * Print out file handle and file name.
491 * Return pointer to 32-bit word past file name.
492 * If packet was truncated (or there was some other error), return 0.
493 */
494static const u_int32_t *
495parsefhn(register const u_int32_t *dp, int v3)
496{
497	dp = parsefh(dp, v3);
498	if (dp == NULL)
499		return (NULL);
500	putchar(' ');
501	return (parsefn(dp));
502}
503
504void
505nfsreq_print(register const u_char *bp, u_int length,
506    register const u_char *bp2)
507{
508	register const struct sunrpc_msg *rp;
509	register const u_int32_t *dp;
510	nfs_type type;
511	int v3;
512	u_int32_t proc;
513	u_int32_t access_flags;
514	struct nfsv3_sattr sa3;
515	char srcid[20], dstid[20];	/*fits 32bit*/
516
517	nfserr = 0;		/* assume no error */
518	rp = (const struct sunrpc_msg *)bp;
519
520	TCHECK(rp->rm_xid);
521	if (!nflag) {
522		snprintf(srcid, sizeof(srcid), "%u",
523		    EXTRACT_32BITS(&rp->rm_xid));
524		strlcpy(dstid, "nfs", sizeof(dstid));
525	} else {
526		snprintf(srcid, sizeof(srcid), "%u",
527		    EXTRACT_32BITS(&rp->rm_xid));
528		snprintf(dstid, sizeof(dstid), "%u", NFS_PORT);
529	}
530	print_nfsaddr(bp2, srcid, dstid);
531	(void)printf("%d", length);
532
533	if (!xid_map_enter(rp, bp2))	/* record proc number for later on */
534		goto trunc;
535
536	v3 = (EXTRACT_32BITS(&rp->rm_call.cb_vers) == NFS_VER3);
537	proc = EXTRACT_32BITS(&rp->rm_call.cb_proc);
538
539	if (!v3 && proc < NFS_NPROCS)
540		proc =  nfsv3_procid[proc];
541
542	switch (proc) {
543	case NFSPROC_NOOP:
544		printf(" nop");
545		return;
546	case NFSPROC_NULL:
547		printf(" null");
548		return;
549
550	case NFSPROC_GETATTR:
551		printf(" getattr");
552		if ((dp = parsereq(rp, length)) != NULL &&
553		    parsefh(dp, v3) != NULL)
554			return;
555		break;
556
557	case NFSPROC_SETATTR:
558		printf(" setattr");
559		if ((dp = parsereq(rp, length)) != NULL &&
560		    parsefh(dp, v3) != NULL)
561			return;
562		break;
563
564	case NFSPROC_LOOKUP:
565		printf(" lookup");
566		if ((dp = parsereq(rp, length)) != NULL &&
567		    parsefhn(dp, v3) != NULL)
568			return;
569		break;
570
571	case NFSPROC_ACCESS:
572		printf(" access");
573		if ((dp = parsereq(rp, length)) != NULL &&
574		    (dp = parsefh(dp, v3)) != NULL) {
575			TCHECK(dp[0]);
576			access_flags = EXTRACT_32BITS(&dp[0]);
577			if (access_flags & ~NFSV3ACCESS_FULL) {
578				/* NFSV3ACCESS definitions aren't up to date */
579				printf(" %04x", access_flags);
580			} else if ((access_flags & NFSV3ACCESS_FULL) == NFSV3ACCESS_FULL) {
581				printf(" NFS_ACCESS_FULL");
582			} else {
583				char separator = ' ';
584				if (access_flags & NFSV3ACCESS_READ) {
585					printf(" NFS_ACCESS_READ");
586					separator = '|';
587				}
588				if (access_flags & NFSV3ACCESS_LOOKUP) {
589					printf("%cNFS_ACCESS_LOOKUP", separator);
590					separator = '|';
591				}
592				if (access_flags & NFSV3ACCESS_MODIFY) {
593					printf("%cNFS_ACCESS_MODIFY", separator);
594					separator = '|';
595				}
596				if (access_flags & NFSV3ACCESS_EXTEND) {
597					printf("%cNFS_ACCESS_EXTEND", separator);
598					separator = '|';
599				}
600				if (access_flags & NFSV3ACCESS_DELETE) {
601					printf("%cNFS_ACCESS_DELETE", separator);
602					separator = '|';
603				}
604				if (access_flags & NFSV3ACCESS_EXECUTE)
605					printf("%cNFS_ACCESS_EXECUTE", separator);
606			}
607			return;
608		}
609		break;
610
611	case NFSPROC_READLINK:
612		printf(" readlink");
613		if ((dp = parsereq(rp, length)) != NULL &&
614		    parsefh(dp, v3) != NULL)
615			return;
616		break;
617
618	case NFSPROC_READ:
619		printf(" read");
620		if ((dp = parsereq(rp, length)) != NULL &&
621		    (dp = parsefh(dp, v3)) != NULL) {
622			if (v3) {
623				TCHECK(dp[2]);
624				printf(" %u bytes @ %" PRIu64,
625				       EXTRACT_32BITS(&dp[2]),
626				       EXTRACT_64BITS(&dp[0]));
627			} else {
628				TCHECK(dp[1]);
629				printf(" %u bytes @ %u",
630				    EXTRACT_32BITS(&dp[1]),
631				    EXTRACT_32BITS(&dp[0]));
632			}
633			return;
634		}
635		break;
636
637	case NFSPROC_WRITE:
638		printf(" write");
639		if ((dp = parsereq(rp, length)) != NULL &&
640		    (dp = parsefh(dp, v3)) != NULL) {
641			if (v3) {
642				TCHECK(dp[2]);
643				printf(" %u (%u) bytes @ %" PRIu64,
644						EXTRACT_32BITS(&dp[4]),
645						EXTRACT_32BITS(&dp[2]),
646						EXTRACT_64BITS(&dp[0]));
647				if (vflag) {
648					dp += 3;
649					TCHECK(dp[0]);
650					printf(" <%s>",
651						tok2str(nfsv3_writemodes,
652							NULL, EXTRACT_32BITS(dp)));
653				}
654			} else {
655				TCHECK(dp[3]);
656				printf(" %u (%u) bytes @ %u (%u)",
657						EXTRACT_32BITS(&dp[3]),
658						EXTRACT_32BITS(&dp[2]),
659						EXTRACT_32BITS(&dp[1]),
660						EXTRACT_32BITS(&dp[0]));
661			}
662			return;
663		}
664		break;
665
666	case NFSPROC_CREATE:
667		printf(" create");
668		if ((dp = parsereq(rp, length)) != NULL &&
669		    parsefhn(dp, v3) != NULL)
670			return;
671		break;
672
673	case NFSPROC_MKDIR:
674		printf(" mkdir");
675		if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp, v3) != 0)
676			return;
677		break;
678
679	case NFSPROC_SYMLINK:
680		printf(" symlink");
681		if ((dp = parsereq(rp, length)) != 0 &&
682		    (dp = parsefhn(dp, v3)) != 0) {
683			fputs(" ->", stdout);
684			if (v3 && (dp = parse_sattr3(dp, &sa3)) == 0)
685				break;
686			if (parsefn(dp) == 0)
687				break;
688			if (v3 && vflag)
689				print_sattr3(&sa3, vflag);
690			return;
691		}
692		break;
693
694	case NFSPROC_MKNOD:
695		printf(" mknod");
696		if ((dp = parsereq(rp, length)) != 0 &&
697		    (dp = parsefhn(dp, v3)) != 0) {
698			TCHECK(*dp);
699			type = (nfs_type)EXTRACT_32BITS(dp);
700			dp++;
701			if ((dp = parse_sattr3(dp, &sa3)) == 0)
702				break;
703			printf(" %s", tok2str(type2str, "unk-ft %d", type));
704			if (vflag && (type == NFCHR || type == NFBLK)) {
705				TCHECK(dp[1]);
706				printf(" %u/%u",
707				       EXTRACT_32BITS(&dp[0]),
708				       EXTRACT_32BITS(&dp[1]));
709				dp += 2;
710			}
711			if (vflag)
712				print_sattr3(&sa3, vflag);
713			return;
714		}
715		break;
716
717	case NFSPROC_REMOVE:
718		printf(" remove");
719		if ((dp = parsereq(rp, length)) != NULL &&
720		    parsefhn(dp, v3) != NULL)
721			return;
722		break;
723
724	case NFSPROC_RMDIR:
725		printf(" rmdir");
726		if ((dp = parsereq(rp, length)) != NULL &&
727		    parsefhn(dp, v3) != NULL)
728			return;
729		break;
730
731	case NFSPROC_RENAME:
732		printf(" rename");
733		if ((dp = parsereq(rp, length)) != NULL &&
734		    (dp = parsefhn(dp, v3)) != NULL) {
735			fputs(" ->", stdout);
736			if (parsefhn(dp, v3) != NULL)
737				return;
738		}
739		break;
740
741	case NFSPROC_LINK:
742		printf(" link");
743		if ((dp = parsereq(rp, length)) != NULL &&
744		    (dp = parsefh(dp, v3)) != NULL) {
745			fputs(" ->", stdout);
746			if (parsefhn(dp, v3) != NULL)
747				return;
748		}
749		break;
750
751	case NFSPROC_READDIR:
752		printf(" readdir");
753		if ((dp = parsereq(rp, length)) != NULL &&
754		    (dp = parsefh(dp, v3)) != NULL) {
755			if (v3) {
756				TCHECK(dp[4]);
757				/*
758				 * We shouldn't really try to interpret the
759				 * offset cookie here.
760				 */
761				printf(" %u bytes @ %" PRId64,
762				    EXTRACT_32BITS(&dp[4]),
763				    EXTRACT_64BITS(&dp[0]));
764				if (vflag)
765					printf(" verf %08x%08x", dp[2],
766					       dp[3]);
767			} else {
768				TCHECK(dp[1]);
769				/*
770				 * Print the offset as signed, since -1 is
771				 * common, but offsets > 2^31 aren't.
772				 */
773				printf(" %u bytes @ %d",
774				    EXTRACT_32BITS(&dp[1]),
775				    EXTRACT_32BITS(&dp[0]));
776			}
777			return;
778		}
779		break;
780
781	case NFSPROC_READDIRPLUS:
782		printf(" readdirplus");
783		if ((dp = parsereq(rp, length)) != NULL &&
784		    (dp = parsefh(dp, v3)) != NULL) {
785			TCHECK(dp[4]);
786			/*
787			 * We don't try to interpret the offset
788			 * cookie here.
789			 */
790			printf(" %u bytes @ %" PRId64,
791				EXTRACT_32BITS(&dp[4]),
792				EXTRACT_64BITS(&dp[0]));
793			if (vflag) {
794				TCHECK(dp[5]);
795				printf(" max %u verf %08x%08x",
796				       EXTRACT_32BITS(&dp[5]), dp[2], dp[3]);
797			}
798			return;
799		}
800		break;
801
802	case NFSPROC_FSSTAT:
803		printf(" fsstat");
804		if ((dp = parsereq(rp, length)) != NULL &&
805		    parsefh(dp, v3) != NULL)
806			return;
807		break;
808
809	case NFSPROC_FSINFO:
810		printf(" fsinfo");
811		if ((dp = parsereq(rp, length)) != NULL &&
812		    parsefh(dp, v3) != NULL)
813			return;
814		break;
815
816	case NFSPROC_PATHCONF:
817		printf(" pathconf");
818		if ((dp = parsereq(rp, length)) != NULL &&
819		    parsefh(dp, v3) != NULL)
820			return;
821		break;
822
823	case NFSPROC_COMMIT:
824		printf(" commit");
825		if ((dp = parsereq(rp, length)) != NULL &&
826		    (dp = parsefh(dp, v3)) != NULL) {
827			TCHECK(dp[2]);
828			printf(" %u bytes @ %" PRIu64,
829				EXTRACT_32BITS(&dp[2]),
830				EXTRACT_64BITS(&dp[0]));
831			return;
832		}
833		break;
834
835	default:
836		printf(" proc-%u", EXTRACT_32BITS(&rp->rm_call.cb_proc));
837		return;
838	}
839
840trunc:
841	if (!nfserr)
842		fputs(" [|nfs]", stdout);
843}
844
845/*
846 * Print out an NFS file handle.
847 * We assume packet was not truncated before the end of the
848 * file handle pointed to by dp.
849 *
850 * Note: new version (using portable file-handle parser) doesn't produce
851 * generation number.  It probably could be made to do that, with some
852 * additional hacking on the parser code.
853 */
854static void
855nfs_printfh(register const u_int32_t *dp, const u_int len)
856{
857	my_fsid fsid;
858	ino_t ino;
859	const char *sfsname = NULL;
860	char *spacep;
861
862	if (uflag) {
863		u_int i;
864		char const *sep = "";
865
866		printf(" fh[");
867		for (i=0; i<len; i++) {
868			(void)printf("%s%x", sep, dp[i]);
869			sep = ":";
870		}
871		printf("]");
872		return;
873	}
874
875	Parse_fh((const u_char *)dp, len, &fsid, &ino, NULL, &sfsname, 0);
876
877	if (sfsname) {
878		/* file system ID is ASCII, not numeric, for this server OS */
879		static char temp[NFSX_V3FHMAX+1];
880
881		/* Make sure string is null-terminated */
882		strncpy(temp, sfsname, NFSX_V3FHMAX);
883		temp[sizeof(temp) - 1] = '\0';
884		/* Remove trailing spaces */
885		spacep = strchr(temp, ' ');
886		if (spacep)
887			*spacep = '\0';
888
889		(void)printf(" fh %s/", temp);
890	} else {
891		(void)printf(" fh %d,%d/",
892			     fsid.Fsid_dev.Major, fsid.Fsid_dev.Minor);
893	}
894
895	if(fsid.Fsid_dev.Minor == 257)
896		/* Print the undecoded handle */
897		(void)printf("%s", fsid.Opaque_Handle);
898	else
899		(void)printf("%ld", (long) ino);
900}
901
902/*
903 * Maintain a small cache of recent client.XID.server/proc pairs, to allow
904 * us to match up replies with requests and thus to know how to parse
905 * the reply.
906 */
907
908struct xid_map_entry {
909	u_int32_t	xid;		/* transaction ID (net order) */
910	int ipver;			/* IP version (4 or 6) */
911#ifdef INET6
912	struct in6_addr	client;		/* client IP address (net order) */
913	struct in6_addr	server;		/* server IP address (net order) */
914#else
915	struct in_addr	client;		/* client IP address (net order) */
916	struct in_addr	server;		/* server IP address (net order) */
917#endif
918	u_int32_t	proc;		/* call proc number (host order) */
919	u_int32_t	vers;		/* program version (host order) */
920};
921
922/*
923 * Map entries are kept in an array that we manage as a ring;
924 * new entries are always added at the tail of the ring.  Initially,
925 * all the entries are zero and hence don't match anything.
926 */
927
928#define	XIDMAPSIZE	64
929
930struct xid_map_entry xid_map[XIDMAPSIZE];
931
932int	xid_map_next = 0;
933int	xid_map_hint = 0;
934
935static int
936xid_map_enter(const struct sunrpc_msg *rp, const u_char *bp)
937{
938	struct ip *ip = NULL;
939#ifdef INET6
940	struct ip6_hdr *ip6 = NULL;
941#endif
942	struct xid_map_entry *xmep;
943
944	if (!TTEST(rp->rm_call.cb_vers))
945		return (0);
946	switch (IP_V((struct ip *)bp)) {
947	case 4:
948		ip = (struct ip *)bp;
949		break;
950#ifdef INET6
951	case 6:
952		ip6 = (struct ip6_hdr *)bp;
953		break;
954#endif
955	default:
956		return (1);
957	}
958
959	xmep = &xid_map[xid_map_next];
960
961	if (++xid_map_next >= XIDMAPSIZE)
962		xid_map_next = 0;
963
964	xmep->xid = rp->rm_xid;
965	if (ip) {
966		xmep->ipver = 4;
967		memcpy(&xmep->client, &ip->ip_src, sizeof(ip->ip_src));
968		memcpy(&xmep->server, &ip->ip_dst, sizeof(ip->ip_dst));
969	}
970#ifdef INET6
971	else if (ip6) {
972		xmep->ipver = 6;
973		memcpy(&xmep->client, &ip6->ip6_src, sizeof(ip6->ip6_src));
974		memcpy(&xmep->server, &ip6->ip6_dst, sizeof(ip6->ip6_dst));
975	}
976#endif
977	xmep->proc = EXTRACT_32BITS(&rp->rm_call.cb_proc);
978	xmep->vers = EXTRACT_32BITS(&rp->rm_call.cb_vers);
979	return (1);
980}
981
982/*
983 * Returns 0 and puts NFSPROC_xxx in proc return and
984 * version in vers return, or returns -1 on failure
985 */
986static int
987xid_map_find(const struct sunrpc_msg *rp, const u_char *bp, u_int32_t *proc,
988	     u_int32_t *vers)
989{
990	int i;
991	struct xid_map_entry *xmep;
992	u_int32_t xid = rp->rm_xid;
993	struct ip *ip = (struct ip *)bp;
994#ifdef INET6
995	struct ip6_hdr *ip6 = (struct ip6_hdr *)bp;
996#endif
997	int cmp;
998
999	/* Start searching from where we last left off */
1000	i = xid_map_hint;
1001	do {
1002		xmep = &xid_map[i];
1003		cmp = 1;
1004		if (xmep->ipver != IP_V(ip) || xmep->xid != xid)
1005			goto nextitem;
1006		switch (xmep->ipver) {
1007		case 4:
1008			if (memcmp(&ip->ip_src, &xmep->server,
1009				   sizeof(ip->ip_src)) != 0 ||
1010			    memcmp(&ip->ip_dst, &xmep->client,
1011				   sizeof(ip->ip_dst)) != 0) {
1012				cmp = 0;
1013			}
1014			break;
1015#ifdef INET6
1016		case 6:
1017			if (memcmp(&ip6->ip6_src, &xmep->server,
1018				   sizeof(ip6->ip6_src)) != 0 ||
1019			    memcmp(&ip6->ip6_dst, &xmep->client,
1020				   sizeof(ip6->ip6_dst)) != 0) {
1021				cmp = 0;
1022			}
1023			break;
1024#endif
1025		default:
1026			cmp = 0;
1027			break;
1028		}
1029		if (cmp) {
1030			/* match */
1031			xid_map_hint = i;
1032			*proc = xmep->proc;
1033			*vers = xmep->vers;
1034			return 0;
1035		}
1036	nextitem:
1037		if (++i >= XIDMAPSIZE)
1038			i = 0;
1039	} while (i != xid_map_hint);
1040
1041	/* search failed */
1042	return (-1);
1043}
1044
1045/*
1046 * Routines for parsing reply packets
1047 */
1048
1049/*
1050 * Return a pointer to the beginning of the actual results.
1051 * If the packet was truncated, return 0.
1052 */
1053static const u_int32_t *
1054parserep(register const struct sunrpc_msg *rp, register u_int length)
1055{
1056	register const u_int32_t *dp;
1057	u_int len;
1058	enum sunrpc_accept_stat astat;
1059
1060	/*
1061	 * Portability note:
1062	 * Here we find the address of the ar_verf credentials.
1063	 * Originally, this calculation was
1064	 *	dp = (u_int32_t *)&rp->rm_reply.rp_acpt.ar_verf
1065	 * On the wire, the rp_acpt field starts immediately after
1066	 * the (32 bit) rp_stat field.  However, rp_acpt (which is a
1067	 * "struct accepted_reply") contains a "struct opaque_auth",
1068	 * whose internal representation contains a pointer, so on a
1069	 * 64-bit machine the compiler inserts 32 bits of padding
1070	 * before rp->rm_reply.rp_acpt.ar_verf.  So, we cannot use
1071	 * the internal representation to parse the on-the-wire
1072	 * representation.  Instead, we skip past the rp_stat field,
1073	 * which is an "enum" and so occupies one 32-bit word.
1074	 */
1075	dp = ((const u_int32_t *)&rp->rm_reply) + 1;
1076	TCHECK(dp[1]);
1077	len = EXTRACT_32BITS(&dp[1]);
1078	if (len >= length)
1079		return (NULL);
1080	/*
1081	 * skip past the ar_verf credentials.
1082	 */
1083	dp += (len + (2*sizeof(u_int32_t) + 3)) / sizeof(u_int32_t);
1084	TCHECK2(dp[0], 0);
1085
1086	/*
1087	 * now we can check the ar_stat field
1088	 */
1089	astat = (enum sunrpc_accept_stat) EXTRACT_32BITS(dp);
1090	switch (astat) {
1091
1092	case SUNRPC_SUCCESS:
1093		break;
1094
1095	case SUNRPC_PROG_UNAVAIL:
1096		printf(" PROG_UNAVAIL");
1097		nfserr = 1;		/* suppress trunc string */
1098		return (NULL);
1099
1100	case SUNRPC_PROG_MISMATCH:
1101		printf(" PROG_MISMATCH");
1102		nfserr = 1;		/* suppress trunc string */
1103		return (NULL);
1104
1105	case SUNRPC_PROC_UNAVAIL:
1106		printf(" PROC_UNAVAIL");
1107		nfserr = 1;		/* suppress trunc string */
1108		return (NULL);
1109
1110	case SUNRPC_GARBAGE_ARGS:
1111		printf(" GARBAGE_ARGS");
1112		nfserr = 1;		/* suppress trunc string */
1113		return (NULL);
1114
1115	case SUNRPC_SYSTEM_ERR:
1116		printf(" SYSTEM_ERR");
1117		nfserr = 1;		/* suppress trunc string */
1118		return (NULL);
1119
1120	default:
1121		printf(" ar_stat %d", astat);
1122		nfserr = 1;		/* suppress trunc string */
1123		return (NULL);
1124	}
1125	/* successful return */
1126	TCHECK2(*dp, sizeof(astat));
1127	return ((u_int32_t *) (sizeof(astat) + ((char *)dp)));
1128trunc:
1129	return (0);
1130}
1131
1132static const u_int32_t *
1133parsestatus(const u_int32_t *dp, int *er)
1134{
1135	int errnum;
1136
1137	TCHECK(dp[0]);
1138
1139	errnum = EXTRACT_32BITS(&dp[0]);
1140	if (er)
1141		*er = errnum;
1142	if (errnum != 0) {
1143		if (!qflag)
1144			printf(" ERROR: %s",
1145			    tok2str(status2str, "unk %d", errnum));
1146		nfserr = 1;
1147	}
1148	return (dp + 1);
1149trunc:
1150	return NULL;
1151}
1152
1153static const u_int32_t *
1154parsefattr(const u_int32_t *dp, int verbose, int v3)
1155{
1156	const struct nfs_fattr *fap;
1157
1158	fap = (const struct nfs_fattr *)dp;
1159	TCHECK(fap->fa_gid);
1160	if (verbose) {
1161		printf(" %s %o ids %d/%d",
1162		    tok2str(type2str, "unk-ft %d ",
1163		    EXTRACT_32BITS(&fap->fa_type)),
1164		    EXTRACT_32BITS(&fap->fa_mode),
1165		    EXTRACT_32BITS(&fap->fa_uid),
1166		    EXTRACT_32BITS(&fap->fa_gid));
1167		if (v3) {
1168			TCHECK(fap->fa3_size);
1169			printf(" sz %" PRIu64,
1170				EXTRACT_64BITS((u_int32_t *)&fap->fa3_size));
1171		} else {
1172			TCHECK(fap->fa2_size);
1173			printf(" sz %d", EXTRACT_32BITS(&fap->fa2_size));
1174		}
1175	}
1176	/* print lots more stuff */
1177	if (verbose > 1) {
1178		if (v3) {
1179			TCHECK(fap->fa3_ctime);
1180			printf(" nlink %d rdev %d/%d",
1181			       EXTRACT_32BITS(&fap->fa_nlink),
1182			       EXTRACT_32BITS(&fap->fa3_rdev.specdata1),
1183			       EXTRACT_32BITS(&fap->fa3_rdev.specdata2));
1184			printf(" fsid %" PRIx64,
1185				EXTRACT_64BITS((u_int32_t *)&fap->fa3_fsid));
1186			printf(" fileid %" PRIx64,
1187				EXTRACT_64BITS((u_int32_t *)&fap->fa3_fileid));
1188			printf(" a/m/ctime %u.%06u",
1189			       EXTRACT_32BITS(&fap->fa3_atime.nfsv3_sec),
1190			       EXTRACT_32BITS(&fap->fa3_atime.nfsv3_nsec));
1191			printf(" %u.%06u",
1192			       EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_sec),
1193			       EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_nsec));
1194			printf(" %u.%06u",
1195			       EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_sec),
1196			       EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_nsec));
1197		} else {
1198			TCHECK(fap->fa2_ctime);
1199			printf(" nlink %d rdev %x fsid %x nodeid %x a/m/ctime",
1200			       EXTRACT_32BITS(&fap->fa_nlink),
1201			       EXTRACT_32BITS(&fap->fa2_rdev),
1202			       EXTRACT_32BITS(&fap->fa2_fsid),
1203			       EXTRACT_32BITS(&fap->fa2_fileid));
1204			printf(" %u.%06u",
1205			       EXTRACT_32BITS(&fap->fa2_atime.nfsv2_sec),
1206			       EXTRACT_32BITS(&fap->fa2_atime.nfsv2_usec));
1207			printf(" %u.%06u",
1208			       EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_sec),
1209			       EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_usec));
1210			printf(" %u.%06u",
1211			       EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_sec),
1212			       EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_usec));
1213		}
1214	}
1215	return ((const u_int32_t *)((unsigned char *)dp +
1216		(v3 ? NFSX_V3FATTR : NFSX_V2FATTR)));
1217trunc:
1218	return (NULL);
1219}
1220
1221static int
1222parseattrstat(const u_int32_t *dp, int verbose, int v3)
1223{
1224	int er;
1225
1226	dp = parsestatus(dp, &er);
1227	if (dp == NULL)
1228		return (0);
1229	if (er)
1230		return (1);
1231
1232	return (parsefattr(dp, verbose, v3) != NULL);
1233}
1234
1235static int
1236parsediropres(const u_int32_t *dp)
1237{
1238	int er;
1239
1240	if (!(dp = parsestatus(dp, &er)))
1241		return (0);
1242	if (er)
1243		return (1);
1244
1245	dp = parsefh(dp, 0);
1246	if (dp == NULL)
1247		return (0);
1248
1249	return (parsefattr(dp, vflag, 0) != NULL);
1250}
1251
1252static int
1253parselinkres(const u_int32_t *dp, int v3)
1254{
1255	int er;
1256
1257	dp = parsestatus(dp, &er);
1258	if (dp == NULL)
1259		return(0);
1260	if (er)
1261		return(1);
1262	if (v3 && !(dp = parse_post_op_attr(dp, vflag)))
1263		return (0);
1264	putchar(' ');
1265	return (parsefn(dp) != NULL);
1266}
1267
1268static int
1269parsestatfs(const u_int32_t *dp, int v3)
1270{
1271	const struct nfs_statfs *sfsp;
1272	int er;
1273
1274	dp = parsestatus(dp, &er);
1275	if (dp == NULL)
1276		return (0);
1277	if (!v3 && er)
1278		return (1);
1279
1280	if (qflag)
1281		return(1);
1282
1283	if (v3) {
1284		if (vflag)
1285			printf(" POST:");
1286		if (!(dp = parse_post_op_attr(dp, vflag)))
1287			return (0);
1288	}
1289
1290	TCHECK2(*dp, (v3 ? NFSX_V3STATFS : NFSX_V2STATFS));
1291
1292	sfsp = (const struct nfs_statfs *)dp;
1293
1294	if (v3) {
1295		printf(" tbytes %" PRIu64 " fbytes %" PRIu64 " abytes %" PRIu64,
1296			EXTRACT_64BITS((u_int32_t *)&sfsp->sf_tbytes),
1297			EXTRACT_64BITS((u_int32_t *)&sfsp->sf_fbytes),
1298			EXTRACT_64BITS((u_int32_t *)&sfsp->sf_abytes));
1299		if (vflag) {
1300			printf(" tfiles %" PRIu64 " ffiles %" PRIu64 " afiles %" PRIu64 " invar %u",
1301			       EXTRACT_64BITS((u_int32_t *)&sfsp->sf_tfiles),
1302			       EXTRACT_64BITS((u_int32_t *)&sfsp->sf_ffiles),
1303			       EXTRACT_64BITS((u_int32_t *)&sfsp->sf_afiles),
1304			       EXTRACT_32BITS(&sfsp->sf_invarsec));
1305		}
1306	} else {
1307		printf(" tsize %d bsize %d blocks %d bfree %d bavail %d",
1308			EXTRACT_32BITS(&sfsp->sf_tsize),
1309			EXTRACT_32BITS(&sfsp->sf_bsize),
1310			EXTRACT_32BITS(&sfsp->sf_blocks),
1311			EXTRACT_32BITS(&sfsp->sf_bfree),
1312			EXTRACT_32BITS(&sfsp->sf_bavail));
1313	}
1314
1315	return (1);
1316trunc:
1317	return (0);
1318}
1319
1320static int
1321parserddires(const u_int32_t *dp)
1322{
1323	int er;
1324
1325	dp = parsestatus(dp, &er);
1326	if (dp == NULL)
1327		return (0);
1328	if (er)
1329		return (1);
1330	if (qflag)
1331		return (1);
1332
1333	TCHECK(dp[2]);
1334	printf(" offset %x size %d ",
1335	       EXTRACT_32BITS(&dp[0]), EXTRACT_32BITS(&dp[1]));
1336	if (dp[2] != 0)
1337		printf(" eof");
1338
1339	return (1);
1340trunc:
1341	return (0);
1342}
1343
1344static const u_int32_t *
1345parse_wcc_attr(const u_int32_t *dp)
1346{
1347	printf(" sz %" PRIu64, EXTRACT_64BITS(&dp[0]));
1348	printf(" mtime %u.%06u ctime %u.%06u",
1349	       EXTRACT_32BITS(&dp[2]), EXTRACT_32BITS(&dp[3]),
1350	       EXTRACT_32BITS(&dp[4]), EXTRACT_32BITS(&dp[5]));
1351	return (dp + 6);
1352}
1353
1354/*
1355 * Pre operation attributes. Print only if vflag > 1.
1356 */
1357static const u_int32_t *
1358parse_pre_op_attr(const u_int32_t *dp, int verbose)
1359{
1360	TCHECK(dp[0]);
1361	if (!EXTRACT_32BITS(&dp[0]))
1362		return (dp + 1);
1363	dp++;
1364	TCHECK2(*dp, 24);
1365	if (verbose > 1) {
1366		return parse_wcc_attr(dp);
1367	} else {
1368		/* If not verbose enough, just skip over wcc_attr */
1369		return (dp + 6);
1370	}
1371trunc:
1372	return (NULL);
1373}
1374
1375/*
1376 * Post operation attributes are printed if vflag >= 1
1377 */
1378static const u_int32_t *
1379parse_post_op_attr(const u_int32_t *dp, int verbose)
1380{
1381	TCHECK(dp[0]);
1382	if (!EXTRACT_32BITS(&dp[0]))
1383		return (dp + 1);
1384	dp++;
1385	if (verbose) {
1386		return parsefattr(dp, verbose, 1);
1387	} else
1388		return (dp + (NFSX_V3FATTR / sizeof (u_int32_t)));
1389trunc:
1390	return (NULL);
1391}
1392
1393static const u_int32_t *
1394parse_wcc_data(const u_int32_t *dp, int verbose)
1395{
1396	if (verbose > 1)
1397		printf(" PRE:");
1398	if (!(dp = parse_pre_op_attr(dp, verbose)))
1399		return (0);
1400
1401	if (verbose)
1402		printf(" POST:");
1403	return parse_post_op_attr(dp, verbose);
1404}
1405
1406static const u_int32_t *
1407parsecreateopres(const u_int32_t *dp, int verbose)
1408{
1409	int er;
1410
1411	if (!(dp = parsestatus(dp, &er)))
1412		return (0);
1413	if (er)
1414		dp = parse_wcc_data(dp, verbose);
1415	else {
1416		TCHECK(dp[0]);
1417		if (!EXTRACT_32BITS(&dp[0]))
1418			return (dp + 1);
1419		dp++;
1420		if (!(dp = parsefh(dp, 1)))
1421			return (0);
1422		if (verbose) {
1423			if (!(dp = parse_post_op_attr(dp, verbose)))
1424				return (0);
1425			if (vflag > 1) {
1426				printf(" dir attr:");
1427				dp = parse_wcc_data(dp, verbose);
1428			}
1429		}
1430	}
1431	return (dp);
1432trunc:
1433	return (NULL);
1434}
1435
1436static int
1437parsewccres(const u_int32_t *dp, int verbose)
1438{
1439	int er;
1440
1441	if (!(dp = parsestatus(dp, &er)))
1442		return (0);
1443	return parse_wcc_data(dp, verbose) != 0;
1444}
1445
1446static const u_int32_t *
1447parsev3rddirres(const u_int32_t *dp, int verbose)
1448{
1449	int er;
1450
1451	if (!(dp = parsestatus(dp, &er)))
1452		return (0);
1453	if (vflag)
1454		printf(" POST:");
1455	if (!(dp = parse_post_op_attr(dp, verbose)))
1456		return (0);
1457	if (er)
1458		return dp;
1459	if (vflag) {
1460		TCHECK(dp[1]);
1461		printf(" verf %08x%08x", dp[0], dp[1]);
1462		dp += 2;
1463	}
1464	return dp;
1465trunc:
1466	return (NULL);
1467}
1468
1469static int
1470parsefsinfo(const u_int32_t *dp)
1471{
1472	struct nfsv3_fsinfo *sfp;
1473	int er;
1474
1475	if (!(dp = parsestatus(dp, &er)))
1476		return (0);
1477	if (vflag)
1478		printf(" POST:");
1479	if (!(dp = parse_post_op_attr(dp, vflag)))
1480		return (0);
1481	if (er)
1482		return (1);
1483
1484	sfp = (struct nfsv3_fsinfo *)dp;
1485	TCHECK(*sfp);
1486	printf(" rtmax %u rtpref %u wtmax %u wtpref %u dtpref %u",
1487	       EXTRACT_32BITS(&sfp->fs_rtmax),
1488	       EXTRACT_32BITS(&sfp->fs_rtpref),
1489	       EXTRACT_32BITS(&sfp->fs_wtmax),
1490	       EXTRACT_32BITS(&sfp->fs_wtpref),
1491	       EXTRACT_32BITS(&sfp->fs_dtpref));
1492	if (vflag) {
1493		printf(" rtmult %u wtmult %u maxfsz %" PRIu64,
1494		       EXTRACT_32BITS(&sfp->fs_rtmult),
1495		       EXTRACT_32BITS(&sfp->fs_wtmult),
1496		       EXTRACT_64BITS((u_int32_t *)&sfp->fs_maxfilesize));
1497		printf(" delta %u.%06u ",
1498		       EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_sec),
1499		       EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_nsec));
1500	}
1501	return (1);
1502trunc:
1503	return (0);
1504}
1505
1506static int
1507parsepathconf(const u_int32_t *dp)
1508{
1509	int er;
1510	struct nfsv3_pathconf *spp;
1511
1512	if (!(dp = parsestatus(dp, &er)))
1513		return (0);
1514	if (vflag)
1515		printf(" POST:");
1516	if (!(dp = parse_post_op_attr(dp, vflag)))
1517		return (0);
1518	if (er)
1519		return (1);
1520
1521	spp = (struct nfsv3_pathconf *)dp;
1522	TCHECK(*spp);
1523
1524	printf(" linkmax %u namemax %u %s %s %s %s",
1525	       EXTRACT_32BITS(&spp->pc_linkmax),
1526	       EXTRACT_32BITS(&spp->pc_namemax),
1527	       EXTRACT_32BITS(&spp->pc_notrunc) ? "notrunc" : "",
1528	       EXTRACT_32BITS(&spp->pc_chownrestricted) ? "chownres" : "",
1529	       EXTRACT_32BITS(&spp->pc_caseinsensitive) ? "igncase" : "",
1530	       EXTRACT_32BITS(&spp->pc_casepreserving) ? "keepcase" : "");
1531	return (1);
1532trunc:
1533	return (0);
1534}
1535
1536static void
1537interp_reply(const struct sunrpc_msg *rp, u_int32_t proc, u_int32_t vers, int length)
1538{
1539	register const u_int32_t *dp;
1540	register int v3;
1541	int er;
1542
1543	v3 = (vers == NFS_VER3);
1544
1545	if (!v3 && proc < NFS_NPROCS)
1546		proc = nfsv3_procid[proc];
1547
1548	switch (proc) {
1549
1550	case NFSPROC_NOOP:
1551		printf(" nop");
1552		return;
1553
1554	case NFSPROC_NULL:
1555		printf(" null");
1556		return;
1557
1558	case NFSPROC_GETATTR:
1559		printf(" getattr");
1560		dp = parserep(rp, length);
1561		if (dp != NULL && parseattrstat(dp, !qflag, v3) != 0)
1562			return;
1563		break;
1564
1565	case NFSPROC_SETATTR:
1566		printf(" setattr");
1567		if (!(dp = parserep(rp, length)))
1568			return;
1569		if (v3) {
1570			if (parsewccres(dp, vflag))
1571				return;
1572		} else {
1573			if (parseattrstat(dp, !qflag, 0) != 0)
1574				return;
1575		}
1576		break;
1577
1578	case NFSPROC_LOOKUP:
1579		printf(" lookup");
1580		if (!(dp = parserep(rp, length)))
1581			break;
1582		if (v3) {
1583			if (!(dp = parsestatus(dp, &er)))
1584				break;
1585			if (er) {
1586				if (vflag > 1) {
1587					printf(" post dattr:");
1588					dp = parse_post_op_attr(dp, vflag);
1589				}
1590			} else {
1591				if (!(dp = parsefh(dp, v3)))
1592					break;
1593				if ((dp = parse_post_op_attr(dp, vflag)) &&
1594				    vflag > 1) {
1595					printf(" post dattr:");
1596					dp = parse_post_op_attr(dp, vflag);
1597				}
1598			}
1599			if (dp)
1600				return;
1601		} else {
1602			if (parsediropres(dp) != 0)
1603				return;
1604		}
1605		break;
1606
1607	case NFSPROC_ACCESS:
1608		printf(" access");
1609		if (!(dp = parserep(rp, length)))
1610			break;
1611		if (!(dp = parsestatus(dp, &er)))
1612			break;
1613		if (vflag)
1614			printf(" attr:");
1615		if (!(dp = parse_post_op_attr(dp, vflag)))
1616			break;
1617		if (!er)
1618			printf(" c %04x", EXTRACT_32BITS(&dp[0]));
1619		return;
1620
1621	case NFSPROC_READLINK:
1622		printf(" readlink");
1623		dp = parserep(rp, length);
1624		if (dp != NULL && parselinkres(dp, v3) != 0)
1625			return;
1626		break;
1627
1628	case NFSPROC_READ:
1629		printf(" read");
1630		if (!(dp = parserep(rp, length)))
1631			break;
1632		if (v3) {
1633			if (!(dp = parsestatus(dp, &er)))
1634				break;
1635			if (!(dp = parse_post_op_attr(dp, vflag)))
1636				break;
1637			if (er)
1638				return;
1639			if (vflag) {
1640				TCHECK(dp[1]);
1641				printf(" %u bytes", EXTRACT_32BITS(&dp[0]));
1642				if (EXTRACT_32BITS(&dp[1]))
1643					printf(" EOF");
1644			}
1645			return;
1646		} else {
1647			if (parseattrstat(dp, vflag, 0) != 0)
1648				return;
1649		}
1650		break;
1651
1652	case NFSPROC_WRITE:
1653		printf(" write");
1654		if (!(dp = parserep(rp, length)))
1655			break;
1656		if (v3) {
1657			if (!(dp = parsestatus(dp, &er)))
1658				break;
1659			if (!(dp = parse_wcc_data(dp, vflag)))
1660				break;
1661			if (er)
1662				return;
1663			if (vflag) {
1664				TCHECK(dp[0]);
1665				printf(" %u bytes", EXTRACT_32BITS(&dp[0]));
1666				if (vflag > 1) {
1667					TCHECK(dp[1]);
1668					printf(" <%s>",
1669						tok2str(nfsv3_writemodes,
1670							NULL, EXTRACT_32BITS(&dp[1])));
1671				}
1672				return;
1673			}
1674		} else {
1675			if (parseattrstat(dp, vflag, v3) != 0)
1676				return;
1677		}
1678		break;
1679
1680	case NFSPROC_CREATE:
1681		printf(" create");
1682		if (!(dp = parserep(rp, length)))
1683			break;
1684		if (v3) {
1685			if (parsecreateopres(dp, vflag) != 0)
1686				return;
1687		} else {
1688			if (parsediropres(dp) != 0)
1689				return;
1690		}
1691		break;
1692
1693	case NFSPROC_MKDIR:
1694		printf(" mkdir");
1695		if (!(dp = parserep(rp, length)))
1696			break;
1697		if (v3) {
1698			if (parsecreateopres(dp, vflag) != 0)
1699				return;
1700		} else {
1701			if (parsediropres(dp) != 0)
1702				return;
1703		}
1704		break;
1705
1706	case NFSPROC_SYMLINK:
1707		printf(" symlink");
1708		if (!(dp = parserep(rp, length)))
1709			break;
1710		if (v3) {
1711			if (parsecreateopres(dp, vflag) != 0)
1712				return;
1713		} else {
1714			if (parsestatus(dp, &er) != 0)
1715				return;
1716		}
1717		break;
1718
1719	case NFSPROC_MKNOD:
1720		printf(" mknod");
1721		if (!(dp = parserep(rp, length)))
1722			break;
1723		if (parsecreateopres(dp, vflag) != 0)
1724			return;
1725		break;
1726
1727	case NFSPROC_REMOVE:
1728		printf(" remove");
1729		if (!(dp = parserep(rp, length)))
1730			break;
1731		if (v3) {
1732			if (parsewccres(dp, vflag))
1733				return;
1734		} else {
1735			if (parsestatus(dp, &er) != 0)
1736				return;
1737		}
1738		break;
1739
1740	case NFSPROC_RMDIR:
1741		printf(" rmdir");
1742		if (!(dp = parserep(rp, length)))
1743			break;
1744		if (v3) {
1745			if (parsewccres(dp, vflag))
1746				return;
1747		} else {
1748			if (parsestatus(dp, &er) != 0)
1749				return;
1750		}
1751		break;
1752
1753	case NFSPROC_RENAME:
1754		printf(" rename");
1755		if (!(dp = parserep(rp, length)))
1756			break;
1757		if (v3) {
1758			if (!(dp = parsestatus(dp, &er)))
1759				break;
1760			if (vflag) {
1761				printf(" from:");
1762				if (!(dp = parse_wcc_data(dp, vflag)))
1763					break;
1764				printf(" to:");
1765				if (!(dp = parse_wcc_data(dp, vflag)))
1766					break;
1767			}
1768			return;
1769		} else {
1770			if (parsestatus(dp, &er) != 0)
1771				return;
1772		}
1773		break;
1774
1775	case NFSPROC_LINK:
1776		printf(" link");
1777		if (!(dp = parserep(rp, length)))
1778			break;
1779		if (v3) {
1780			if (!(dp = parsestatus(dp, &er)))
1781				break;
1782			if (vflag) {
1783				printf(" file POST:");
1784				if (!(dp = parse_post_op_attr(dp, vflag)))
1785					break;
1786				printf(" dir:");
1787				if (!(dp = parse_wcc_data(dp, vflag)))
1788					break;
1789				return;
1790			}
1791		} else {
1792			if (parsestatus(dp, &er) != 0)
1793				return;
1794		}
1795		break;
1796
1797	case NFSPROC_READDIR:
1798		printf(" readdir");
1799		if (!(dp = parserep(rp, length)))
1800			break;
1801		if (v3) {
1802			if (parsev3rddirres(dp, vflag))
1803				return;
1804		} else {
1805			if (parserddires(dp) != 0)
1806				return;
1807		}
1808		break;
1809
1810	case NFSPROC_READDIRPLUS:
1811		printf(" readdirplus");
1812		if (!(dp = parserep(rp, length)))
1813			break;
1814		if (parsev3rddirres(dp, vflag))
1815			return;
1816		break;
1817
1818	case NFSPROC_FSSTAT:
1819		printf(" fsstat");
1820		dp = parserep(rp, length);
1821		if (dp != NULL && parsestatfs(dp, v3) != 0)
1822			return;
1823		break;
1824
1825	case NFSPROC_FSINFO:
1826		printf(" fsinfo");
1827		dp = parserep(rp, length);
1828		if (dp != NULL && parsefsinfo(dp) != 0)
1829			return;
1830		break;
1831
1832	case NFSPROC_PATHCONF:
1833		printf(" pathconf");
1834		dp = parserep(rp, length);
1835		if (dp != NULL && parsepathconf(dp) != 0)
1836			return;
1837		break;
1838
1839	case NFSPROC_COMMIT:
1840		printf(" commit");
1841		dp = parserep(rp, length);
1842		if (dp != NULL && parsewccres(dp, vflag) != 0)
1843			return;
1844		break;
1845
1846	default:
1847		printf(" proc-%u", proc);
1848		return;
1849	}
1850trunc:
1851	if (!nfserr)
1852		fputs(" [|nfs]", stdout);
1853}
1854