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