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