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