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[2]);
    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 					dp += 3;
    638 					ND_TCHECK(dp[0]);
    639 					ND_PRINT((ndo, " <%s>",
    640 						tok2str(nfsv3_writemodes,
    641 							NULL, EXTRACT_32BITS(dp))));
    642 				}
    643 			} else {
    644 				ND_TCHECK(dp[3]);
    645 				ND_PRINT((ndo, " %u (%u) bytes @ %u (%u)",
    646 						EXTRACT_32BITS(&dp[3]),
    647 						EXTRACT_32BITS(&dp[2]),
    648 						EXTRACT_32BITS(&dp[1]),
    649 						EXTRACT_32BITS(&dp[0])));
    650 			}
    651 			return;
    652 		}
    653 		break;
    654 
    655 	case NFSPROC_SYMLINK:
    656 		if ((dp = parsereq(ndo, rp, length)) != NULL &&
    657 		    (dp = parsefhn(ndo, dp, v3)) != NULL) {
    658 			ND_PRINT((ndo, " ->"));
    659 			if (v3 && (dp = parse_sattr3(ndo, dp, &sa3)) == NULL)
    660 				break;
    661 			if (parsefn(ndo, dp) == NULL)
    662 				break;
    663 			if (v3 && ndo->ndo_vflag)
    664 				print_sattr3(ndo, &sa3, ndo->ndo_vflag);
    665 			return;
    666 		}
    667 		break;
    668 
    669 	case NFSPROC_MKNOD:
    670 		if ((dp = parsereq(ndo, rp, length)) != NULL &&
    671 		    (dp = parsefhn(ndo, dp, v3)) != NULL) {
    672 			ND_TCHECK(*dp);
    673 			type = (nfs_type)EXTRACT_32BITS(dp);
    674 			dp++;
    675 			if ((dp = parse_sattr3(ndo, dp, &sa3)) == NULL)
    676 				break;
    677 			ND_PRINT((ndo, " %s", tok2str(type2str, "unk-ft %d", type)));
    678 			if (ndo->ndo_vflag && (type == NFCHR || type == NFBLK)) {
    679 				ND_TCHECK(dp[1]);
    680 				ND_PRINT((ndo, " %u/%u",
    681 				       EXTRACT_32BITS(&dp[0]),
    682 				       EXTRACT_32BITS(&dp[1])));
    683 				dp += 2;
    684 			}
    685 			if (ndo->ndo_vflag)
    686 				print_sattr3(ndo, &sa3, ndo->ndo_vflag);
    687 			return;
    688 		}
    689 		break;
    690 
    691 	case NFSPROC_RENAME:
    692 		if ((dp = parsereq(ndo, rp, length)) != NULL &&
    693 		    (dp = parsefhn(ndo, dp, v3)) != NULL) {
    694 			ND_PRINT((ndo, " ->"));
    695 			if (parsefhn(ndo, dp, v3) != NULL)
    696 				return;
    697 		}
    698 		break;
    699 
    700 	case NFSPROC_LINK:
    701 		if ((dp = parsereq(ndo, rp, length)) != NULL &&
    702 		    (dp = parsefh(ndo, dp, v3)) != NULL) {
    703 			ND_PRINT((ndo, " ->"));
    704 			if (parsefhn(ndo, dp, v3) != NULL)
    705 				return;
    706 		}
    707 		break;
    708 
    709 	case NFSPROC_READDIR:
    710 		if ((dp = parsereq(ndo, rp, length)) != NULL &&
    711 		    (dp = parsefh(ndo, dp, v3)) != NULL) {
    712 			if (v3) {
    713 				ND_TCHECK(dp[4]);
    714 				/*
    715 				 * We shouldn't really try to interpret the
    716 				 * offset cookie here.
    717 				 */
    718 				ND_PRINT((ndo, " %u bytes @ %" PRId64,
    719 				    EXTRACT_32BITS(&dp[4]),
    720 				    EXTRACT_64BITS(&dp[0])));
    721 				if (ndo->ndo_vflag)
    722 					ND_PRINT((ndo, " verf %08x%08x", dp[2], dp[3]));
    723 			} else {
    724 				ND_TCHECK(dp[1]);
    725 				/*
    726 				 * Print the offset as signed, since -1 is
    727 				 * common, but offsets > 2^31 aren't.
    728 				 */
    729 				ND_PRINT((ndo, " %u bytes @ %d",
    730 				    EXTRACT_32BITS(&dp[1]),
    731 				    EXTRACT_32BITS(&dp[0])));
    732 			}
    733 			return;
    734 		}
    735 		break;
    736 
    737 	case NFSPROC_READDIRPLUS:
    738 		if ((dp = parsereq(ndo, rp, length)) != NULL &&
    739 		    (dp = parsefh(ndo, dp, v3)) != NULL) {
    740 			ND_TCHECK(dp[4]);
    741 			/*
    742 			 * We don't try to interpret the offset
    743 			 * cookie here.
    744 			 */
    745 			ND_PRINT((ndo, " %u bytes @ %" PRId64,
    746 				EXTRACT_32BITS(&dp[4]),
    747 				EXTRACT_64BITS(&dp[0])));
    748 			if (ndo->ndo_vflag) {
    749 				ND_TCHECK(dp[5]);
    750 				ND_PRINT((ndo, " max %u verf %08x%08x",
    751 				       EXTRACT_32BITS(&dp[5]), dp[2], dp[3]));
    752 			}
    753 			return;
    754 		}
    755 		break;
    756 
    757 	case NFSPROC_COMMIT:
    758 		if ((dp = parsereq(ndo, rp, length)) != NULL &&
    759 		    (dp = parsefh(ndo, dp, v3)) != NULL) {
    760 			ND_TCHECK(dp[2]);
    761 			ND_PRINT((ndo, " %u bytes @ %" PRIu64,
    762 				EXTRACT_32BITS(&dp[2]),
    763 				EXTRACT_64BITS(&dp[0])));
    764 			return;
    765 		}
    766 		break;
    767 
    768 	default:
    769 		return;
    770 	}
    771 
    772 trunc:
    773 	if (!nfserr)
    774 		ND_PRINT((ndo, "%s", tstr));
    775 }
    776 
    777 /*
    778  * Print out an NFS file handle.
    779  * We assume packet was not truncated before the end of the
    780  * file handle pointed to by dp.
    781  *
    782  * Note: new version (using portable file-handle parser) doesn't produce
    783  * generation number.  It probably could be made to do that, with some
    784  * additional hacking on the parser code.
    785  */
    786 static void
    787 nfs_printfh(netdissect_options *ndo,
    788             register const uint32_t *dp, const u_int len)
    789 {
    790 	my_fsid fsid;
    791 	uint32_t ino;
    792 	const char *sfsname = NULL;
    793 	char *spacep;
    794 
    795 	if (ndo->ndo_uflag) {
    796 		u_int i;
    797 		char const *sep = "";
    798 
    799 		ND_PRINT((ndo, " fh["));
    800 		for (i=0; i<len; i++) {
    801 			ND_PRINT((ndo, "%s%x", sep, dp[i]));
    802 			sep = ":";
    803 		}
    804 		ND_PRINT((ndo, "]"));
    805 		return;
    806 	}
    807 
    808 	Parse_fh((const u_char *)dp, len, &fsid, &ino, NULL, &sfsname, 0);
    809 
    810 	if (sfsname) {
    811 		/* file system ID is ASCII, not numeric, for this server OS */
    812 		static char temp[NFSX_V3FHMAX+1];
    813 
    814 		/* Make sure string is null-terminated */
    815 		strncpy(temp, sfsname, NFSX_V3FHMAX);
    816 		temp[sizeof(temp) - 1] = '\0';
    817 		/* Remove trailing spaces */
    818 		spacep = strchr(temp, ' ');
    819 		if (spacep)
    820 			*spacep = '\0';
    821 
    822 		ND_PRINT((ndo, " fh %s/", temp));
    823 	} else {
    824 		ND_PRINT((ndo, " fh %d,%d/",
    825 			     fsid.Fsid_dev.Major, fsid.Fsid_dev.Minor));
    826 	}
    827 
    828 	if(fsid.Fsid_dev.Minor == 257)
    829 		/* Print the undecoded handle */
    830 		ND_PRINT((ndo, "%s", fsid.Opaque_Handle));
    831 	else
    832 		ND_PRINT((ndo, "%ld", (long) ino));
    833 }
    834 
    835 /*
    836  * Maintain a small cache of recent client.XID.server/proc pairs, to allow
    837  * us to match up replies with requests and thus to know how to parse
    838  * the reply.
    839  */
    840 
    841 struct xid_map_entry {
    842 	uint32_t	xid;		/* transaction ID (net order) */
    843 	int ipver;			/* IP version (4 or 6) */
    844 	struct in6_addr	client;		/* client IP address (net order) */
    845 	struct in6_addr	server;		/* server IP address (net order) */
    846 	uint32_t	proc;		/* call proc number (host order) */
    847 	uint32_t	vers;		/* program version (host order) */
    848 };
    849 
    850 /*
    851  * Map entries are kept in an array that we manage as a ring;
    852  * new entries are always added at the tail of the ring.  Initially,
    853  * all the entries are zero and hence don't match anything.
    854  */
    855 
    856 #define	XIDMAPSIZE	64
    857 
    858 static struct xid_map_entry xid_map[XIDMAPSIZE];
    859 
    860 static int xid_map_next = 0;
    861 static int xid_map_hint = 0;
    862 
    863 static int
    864 xid_map_enter(netdissect_options *ndo,
    865               const struct sunrpc_msg *rp, const u_char *bp)
    866 {
    867 	const struct ip *ip = NULL;
    868 	const struct ip6_hdr *ip6 = NULL;
    869 	struct xid_map_entry *xmep;
    870 
    871 	if (!ND_TTEST(rp->rm_call.cb_vers))
    872 		return (0);
    873 	switch (IP_V((const struct ip *)bp)) {
    874 	case 4:
    875 		ip = (const struct ip *)bp;
    876 		break;
    877 	case 6:
    878 		ip6 = (const struct ip6_hdr *)bp;
    879 		break;
    880 	default:
    881 		return (1);
    882 	}
    883 
    884 	xmep = &xid_map[xid_map_next];
    885 
    886 	if (++xid_map_next >= XIDMAPSIZE)
    887 		xid_map_next = 0;
    888 
    889 	UNALIGNED_MEMCPY(&xmep->xid, &rp->rm_xid, sizeof(xmep->xid));
    890 	if (ip) {
    891 		xmep->ipver = 4;
    892 		UNALIGNED_MEMCPY(&xmep->client, &ip->ip_src, sizeof(ip->ip_src));
    893 		UNALIGNED_MEMCPY(&xmep->server, &ip->ip_dst, sizeof(ip->ip_dst));
    894 	}
    895 	else if (ip6) {
    896 		xmep->ipver = 6;
    897 		UNALIGNED_MEMCPY(&xmep->client, &ip6->ip6_src, sizeof(ip6->ip6_src));
    898 		UNALIGNED_MEMCPY(&xmep->server, &ip6->ip6_dst, sizeof(ip6->ip6_dst));
    899 	}
    900 	xmep->proc = EXTRACT_32BITS(&rp->rm_call.cb_proc);
    901 	xmep->vers = EXTRACT_32BITS(&rp->rm_call.cb_vers);
    902 	return (1);
    903 }
    904 
    905 /*
    906  * Returns 0 and puts NFSPROC_xxx in proc return and
    907  * version in vers return, or returns -1 on failure
    908  */
    909 static int
    910 xid_map_find(const struct sunrpc_msg *rp, const u_char *bp, uint32_t *proc,
    911 	     uint32_t *vers)
    912 {
    913 	int i;
    914 	struct xid_map_entry *xmep;
    915 	uint32_t xid;
    916 	const struct ip *ip = (const struct ip *)bp;
    917 	const struct ip6_hdr *ip6 = (const struct ip6_hdr *)bp;
    918 	int cmp;
    919 
    920 	UNALIGNED_MEMCPY(&xid, &rp->rm_xid, sizeof(xmep->xid));
    921 	/* Start searching from where we last left off */
    922 	i = xid_map_hint;
    923 	do {
    924 		xmep = &xid_map[i];
    925 		cmp = 1;
    926 		if (xmep->ipver != IP_V(ip) || xmep->xid != xid)
    927 			goto nextitem;
    928 		switch (xmep->ipver) {
    929 		case 4:
    930 			if (UNALIGNED_MEMCMP(&ip->ip_src, &xmep->server,
    931 				   sizeof(ip->ip_src)) != 0 ||
    932 			    UNALIGNED_MEMCMP(&ip->ip_dst, &xmep->client,
    933 				   sizeof(ip->ip_dst)) != 0) {
    934 				cmp = 0;
    935 			}
    936 			break;
    937 		case 6:
    938 			if (UNALIGNED_MEMCMP(&ip6->ip6_src, &xmep->server,
    939 				   sizeof(ip6->ip6_src)) != 0 ||
    940 			    UNALIGNED_MEMCMP(&ip6->ip6_dst, &xmep->client,
    941 				   sizeof(ip6->ip6_dst)) != 0) {
    942 				cmp = 0;
    943 			}
    944 			break;
    945 		default:
    946 			cmp = 0;
    947 			break;
    948 		}
    949 		if (cmp) {
    950 			/* match */
    951 			xid_map_hint = i;
    952 			*proc = xmep->proc;
    953 			*vers = xmep->vers;
    954 			return 0;
    955 		}
    956 	nextitem:
    957 		if (++i >= XIDMAPSIZE)
    958 			i = 0;
    959 	} while (i != xid_map_hint);
    960 
    961 	/* search failed */
    962 	return (-1);
    963 }
    964 
    965 /*
    966  * Routines for parsing reply packets
    967  */
    968 
    969 /*
    970  * Return a pointer to the beginning of the actual results.
    971  * If the packet was truncated, return 0.
    972  */
    973 static const uint32_t *
    974 parserep(netdissect_options *ndo,
    975          register const struct sunrpc_msg *rp, register u_int length)
    976 {
    977 	register const uint32_t *dp;
    978 	u_int len;
    979 	enum sunrpc_accept_stat astat;
    980 
    981 	/*
    982 	 * Portability note:
    983 	 * Here we find the address of the ar_verf credentials.
    984 	 * Originally, this calculation was
    985 	 *	dp = (uint32_t *)&rp->rm_reply.rp_acpt.ar_verf
    986 	 * On the wire, the rp_acpt field starts immediately after
    987 	 * the (32 bit) rp_stat field.  However, rp_acpt (which is a
    988 	 * "struct accepted_reply") contains a "struct opaque_auth",
    989 	 * whose internal representation contains a pointer, so on a
    990 	 * 64-bit machine the compiler inserts 32 bits of padding
    991 	 * before rp->rm_reply.rp_acpt.ar_verf.  So, we cannot use
    992 	 * the internal representation to parse the on-the-wire
    993 	 * representation.  Instead, we skip past the rp_stat field,
    994 	 * which is an "enum" and so occupies one 32-bit word.
    995 	 */
    996 	dp = ((const uint32_t *)&rp->rm_reply) + 1;
    997 	ND_TCHECK(dp[1]);
    998 	len = EXTRACT_32BITS(&dp[1]);
    999 	if (len >= length)
   1000 		return (NULL);
   1001 	/*
   1002 	 * skip past the ar_verf credentials.
   1003 	 */
   1004 	dp += (len + (2*sizeof(uint32_t) + 3)) / sizeof(uint32_t);
   1005 	ND_TCHECK2(dp[0], 0);
   1006 
   1007 	/*
   1008 	 * now we can check the ar_stat field
   1009 	 */
   1010 	astat = (enum sunrpc_accept_stat) EXTRACT_32BITS(dp);
   1011 	if (astat != SUNRPC_SUCCESS) {
   1012 		ND_PRINT((ndo, " %s", tok2str(sunrpc_str, "ar_stat %d", astat)));
   1013 		nfserr = 1;		/* suppress trunc string */
   1014 		return (NULL);
   1015 	}
   1016 	/* successful return */
   1017 	ND_TCHECK2(*dp, sizeof(astat));
   1018 	return ((const uint32_t *) (sizeof(astat) + ((const char *)dp)));
   1019 trunc:
   1020 	return (0);
   1021 }
   1022 
   1023 static const uint32_t *
   1024 parsestatus(netdissect_options *ndo,
   1025             const uint32_t *dp, int *er)
   1026 {
   1027 	int errnum;
   1028 
   1029 	ND_TCHECK(dp[0]);
   1030 
   1031 	errnum = EXTRACT_32BITS(&dp[0]);
   1032 	if (er)
   1033 		*er = errnum;
   1034 	if (errnum != 0) {
   1035 		if (!ndo->ndo_qflag)
   1036 			ND_PRINT((ndo, " ERROR: %s",
   1037 			    tok2str(status2str, "unk %d", errnum)));
   1038 		nfserr = 1;
   1039 	}
   1040 	return (dp + 1);
   1041 trunc:
   1042 	return NULL;
   1043 }
   1044 
   1045 static const uint32_t *
   1046 parsefattr(netdissect_options *ndo,
   1047            const uint32_t *dp, int verbose, int v3)
   1048 {
   1049 	const struct nfs_fattr *fap;
   1050 
   1051 	fap = (const struct nfs_fattr *)dp;
   1052 	ND_TCHECK(fap->fa_gid);
   1053 	if (verbose) {
   1054 		ND_PRINT((ndo, " %s %o ids %d/%d",
   1055 		    tok2str(type2str, "unk-ft %d ",
   1056 		    EXTRACT_32BITS(&fap->fa_type)),
   1057 		    EXTRACT_32BITS(&fap->fa_mode),
   1058 		    EXTRACT_32BITS(&fap->fa_uid),
   1059 		    EXTRACT_32BITS(&fap->fa_gid)));
   1060 		if (v3) {
   1061 			ND_TCHECK(fap->fa3_size);
   1062 			ND_PRINT((ndo, " sz %" PRIu64,
   1063 				EXTRACT_64BITS((const uint32_t *)&fap->fa3_size)));
   1064 		} else {
   1065 			ND_TCHECK(fap->fa2_size);
   1066 			ND_PRINT((ndo, " sz %d", EXTRACT_32BITS(&fap->fa2_size)));
   1067 		}
   1068 	}
   1069 	/* print lots more stuff */
   1070 	if (verbose > 1) {
   1071 		if (v3) {
   1072 			ND_TCHECK(fap->fa3_ctime);
   1073 			ND_PRINT((ndo, " nlink %d rdev %d/%d",
   1074 			       EXTRACT_32BITS(&fap->fa_nlink),
   1075 			       EXTRACT_32BITS(&fap->fa3_rdev.specdata1),
   1076 			       EXTRACT_32BITS(&fap->fa3_rdev.specdata2)));
   1077 			ND_PRINT((ndo, " fsid %" PRIx64,
   1078 				EXTRACT_64BITS((const uint32_t *)&fap->fa3_fsid)));
   1079 			ND_PRINT((ndo, " fileid %" PRIx64,
   1080 				EXTRACT_64BITS((const uint32_t *)&fap->fa3_fileid)));
   1081 			ND_PRINT((ndo, " a/m/ctime %u.%06u",
   1082 			       EXTRACT_32BITS(&fap->fa3_atime.nfsv3_sec),
   1083 			       EXTRACT_32BITS(&fap->fa3_atime.nfsv3_nsec)));
   1084 			ND_PRINT((ndo, " %u.%06u",
   1085 			       EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_sec),
   1086 			       EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_nsec)));
   1087 			ND_PRINT((ndo, " %u.%06u",
   1088 			       EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_sec),
   1089 			       EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_nsec)));
   1090 		} else {
   1091 			ND_TCHECK(fap->fa2_ctime);
   1092 			ND_PRINT((ndo, " nlink %d rdev 0x%x fsid 0x%x nodeid 0x%x a/m/ctime",
   1093 			       EXTRACT_32BITS(&fap->fa_nlink),
   1094 			       EXTRACT_32BITS(&fap->fa2_rdev),
   1095 			       EXTRACT_32BITS(&fap->fa2_fsid),
   1096 			       EXTRACT_32BITS(&fap->fa2_fileid)));
   1097 			ND_PRINT((ndo, " %u.%06u",
   1098 			       EXTRACT_32BITS(&fap->fa2_atime.nfsv2_sec),
   1099 			       EXTRACT_32BITS(&fap->fa2_atime.nfsv2_usec)));
   1100 			ND_PRINT((ndo, " %u.%06u",
   1101 			       EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_sec),
   1102 			       EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_usec)));
   1103 			ND_PRINT((ndo, " %u.%06u",
   1104 			       EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_sec),
   1105 			       EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_usec)));
   1106 		}
   1107 	}
   1108 	return ((const uint32_t *)((const unsigned char *)dp +
   1109 		(v3 ? NFSX_V3FATTR : NFSX_V2FATTR)));
   1110 trunc:
   1111 	return (NULL);
   1112 }
   1113 
   1114 static int
   1115 parseattrstat(netdissect_options *ndo,
   1116               const uint32_t *dp, int verbose, int v3)
   1117 {
   1118 	int er;
   1119 
   1120 	dp = parsestatus(ndo, dp, &er);
   1121 	if (dp == NULL)
   1122 		return (0);
   1123 	if (er)
   1124 		return (1);
   1125 
   1126 	return (parsefattr(ndo, dp, verbose, v3) != NULL);
   1127 }
   1128 
   1129 static int
   1130 parsediropres(netdissect_options *ndo,
   1131               const uint32_t *dp)
   1132 {
   1133 	int er;
   1134 
   1135 	if (!(dp = parsestatus(ndo, dp, &er)))
   1136 		return (0);
   1137 	if (er)
   1138 		return (1);
   1139 
   1140 	dp = parsefh(ndo, dp, 0);
   1141 	if (dp == NULL)
   1142 		return (0);
   1143 
   1144 	return (parsefattr(ndo, dp, ndo->ndo_vflag, 0) != NULL);
   1145 }
   1146 
   1147 static int
   1148 parselinkres(netdissect_options *ndo,
   1149              const uint32_t *dp, int v3)
   1150 {
   1151 	int er;
   1152 
   1153 	dp = parsestatus(ndo, dp, &er);
   1154 	if (dp == NULL)
   1155 		return(0);
   1156 	if (er)
   1157 		return(1);
   1158 	if (v3 && !(dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag)))
   1159 		return (0);
   1160 	ND_PRINT((ndo, " "));
   1161 	return (parsefn(ndo, dp) != NULL);
   1162 }
   1163 
   1164 static int
   1165 parsestatfs(netdissect_options *ndo,
   1166             const uint32_t *dp, int v3)
   1167 {
   1168 	const struct nfs_statfs *sfsp;
   1169 	int er;
   1170 
   1171 	dp = parsestatus(ndo, dp, &er);
   1172 	if (dp == NULL)
   1173 		return (0);
   1174 	if (!v3 && er)
   1175 		return (1);
   1176 
   1177 	if (ndo->ndo_qflag)
   1178 		return(1);
   1179 
   1180 	if (v3) {
   1181 		if (ndo->ndo_vflag)
   1182 			ND_PRINT((ndo, " POST:"));
   1183 		if (!(dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag)))
   1184 			return (0);
   1185 	}
   1186 
   1187 	ND_TCHECK2(*dp, (v3 ? NFSX_V3STATFS : NFSX_V2STATFS));
   1188 
   1189 	sfsp = (const struct nfs_statfs *)dp;
   1190 
   1191 	if (v3) {
   1192 		ND_PRINT((ndo, " tbytes %" PRIu64 " fbytes %" PRIu64 " abytes %" PRIu64,
   1193 			EXTRACT_64BITS((const uint32_t *)&sfsp->sf_tbytes),
   1194 			EXTRACT_64BITS((const uint32_t *)&sfsp->sf_fbytes),
   1195 			EXTRACT_64BITS((const uint32_t *)&sfsp->sf_abytes)));
   1196 		if (ndo->ndo_vflag) {
   1197 			ND_PRINT((ndo, " tfiles %" PRIu64 " ffiles %" PRIu64 " afiles %" PRIu64 " invar %u",
   1198 			       EXTRACT_64BITS((const uint32_t *)&sfsp->sf_tfiles),
   1199 			       EXTRACT_64BITS((const uint32_t *)&sfsp->sf_ffiles),
   1200 			       EXTRACT_64BITS((const uint32_t *)&sfsp->sf_afiles),
   1201 			       EXTRACT_32BITS(&sfsp->sf_invarsec)));
   1202 		}
   1203 	} else {
   1204 		ND_PRINT((ndo, " tsize %d bsize %d blocks %d bfree %d bavail %d",
   1205 			EXTRACT_32BITS(&sfsp->sf_tsize),
   1206 			EXTRACT_32BITS(&sfsp->sf_bsize),
   1207 			EXTRACT_32BITS(&sfsp->sf_blocks),
   1208 			EXTRACT_32BITS(&sfsp->sf_bfree),
   1209 			EXTRACT_32BITS(&sfsp->sf_bavail)));
   1210 	}
   1211 
   1212 	return (1);
   1213 trunc:
   1214 	return (0);
   1215 }
   1216 
   1217 static int
   1218 parserddires(netdissect_options *ndo,
   1219              const uint32_t *dp)
   1220 {
   1221 	int er;
   1222 
   1223 	dp = parsestatus(ndo, dp, &er);
   1224 	if (dp == NULL)
   1225 		return (0);
   1226 	if (er)
   1227 		return (1);
   1228 	if (ndo->ndo_qflag)
   1229 		return (1);
   1230 
   1231 	ND_TCHECK(dp[2]);
   1232 	ND_PRINT((ndo, " offset 0x%x size %d ",
   1233 	       EXTRACT_32BITS(&dp[0]), EXTRACT_32BITS(&dp[1])));
   1234 	if (dp[2] != 0)
   1235 		ND_PRINT((ndo, " eof"));
   1236 
   1237 	return (1);
   1238 trunc:
   1239 	return (0);
   1240 }
   1241 
   1242 static const uint32_t *
   1243 parse_wcc_attr(netdissect_options *ndo,
   1244                const uint32_t *dp)
   1245 {
   1246 	ND_PRINT((ndo, " sz %" PRIu64, EXTRACT_64BITS(&dp[0])));
   1247 	ND_PRINT((ndo, " mtime %u.%06u ctime %u.%06u",
   1248 	       EXTRACT_32BITS(&dp[2]), EXTRACT_32BITS(&dp[3]),
   1249 	       EXTRACT_32BITS(&dp[4]), EXTRACT_32BITS(&dp[5])));
   1250 	return (dp + 6);
   1251 }
   1252 
   1253 /*
   1254  * Pre operation attributes. Print only if vflag > 1.
   1255  */
   1256 static const uint32_t *
   1257 parse_pre_op_attr(netdissect_options *ndo,
   1258                   const uint32_t *dp, int verbose)
   1259 {
   1260 	ND_TCHECK(dp[0]);
   1261 	if (!EXTRACT_32BITS(&dp[0]))
   1262 		return (dp + 1);
   1263 	dp++;
   1264 	ND_TCHECK2(*dp, 24);
   1265 	if (verbose > 1) {
   1266 		return parse_wcc_attr(ndo, dp);
   1267 	} else {
   1268 		/* If not verbose enough, just skip over wcc_attr */
   1269 		return (dp + 6);
   1270 	}
   1271 trunc:
   1272 	return (NULL);
   1273 }
   1274 
   1275 /*
   1276  * Post operation attributes are printed if vflag >= 1
   1277  */
   1278 static const uint32_t *
   1279 parse_post_op_attr(netdissect_options *ndo,
   1280                    const uint32_t *dp, int verbose)
   1281 {
   1282 	ND_TCHECK(dp[0]);
   1283 	if (!EXTRACT_32BITS(&dp[0]))
   1284 		return (dp + 1);
   1285 	dp++;
   1286 	if (verbose) {
   1287 		return parsefattr(ndo, dp, verbose, 1);
   1288 	} else
   1289 		return (dp + (NFSX_V3FATTR / sizeof (uint32_t)));
   1290 trunc:
   1291 	return (NULL);
   1292 }
   1293 
   1294 static const uint32_t *
   1295 parse_wcc_data(netdissect_options *ndo,
   1296                const uint32_t *dp, int verbose)
   1297 {
   1298 	if (verbose > 1)
   1299 		ND_PRINT((ndo, " PRE:"));
   1300 	if (!(dp = parse_pre_op_attr(ndo, dp, verbose)))
   1301 		return (0);
   1302 
   1303 	if (verbose)
   1304 		ND_PRINT((ndo, " POST:"));
   1305 	return parse_post_op_attr(ndo, dp, verbose);
   1306 }
   1307 
   1308 static const uint32_t *
   1309 parsecreateopres(netdissect_options *ndo,
   1310                  const uint32_t *dp, int verbose)
   1311 {
   1312 	int er;
   1313 
   1314 	if (!(dp = parsestatus(ndo, dp, &er)))
   1315 		return (0);
   1316 	if (er)
   1317 		dp = parse_wcc_data(ndo, dp, verbose);
   1318 	else {
   1319 		ND_TCHECK(dp[0]);
   1320 		if (!EXTRACT_32BITS(&dp[0]))
   1321 			return (dp + 1);
   1322 		dp++;
   1323 		if (!(dp = parsefh(ndo, dp, 1)))
   1324 			return (0);
   1325 		if (verbose) {
   1326 			if (!(dp = parse_post_op_attr(ndo, dp, verbose)))
   1327 				return (0);
   1328 			if (ndo->ndo_vflag > 1) {
   1329 				ND_PRINT((ndo, " dir attr:"));
   1330 				dp = parse_wcc_data(ndo, dp, verbose);
   1331 			}
   1332 		}
   1333 	}
   1334 	return (dp);
   1335 trunc:
   1336 	return (NULL);
   1337 }
   1338 
   1339 static int
   1340 parsewccres(netdissect_options *ndo,
   1341             const uint32_t *dp, int verbose)
   1342 {
   1343 	int er;
   1344 
   1345 	if (!(dp = parsestatus(ndo, dp, &er)))
   1346 		return (0);
   1347 	return parse_wcc_data(ndo, dp, verbose) != NULL;
   1348 }
   1349 
   1350 static const uint32_t *
   1351 parsev3rddirres(netdissect_options *ndo,
   1352                 const uint32_t *dp, int verbose)
   1353 {
   1354 	int er;
   1355 
   1356 	if (!(dp = parsestatus(ndo, dp, &er)))
   1357 		return (0);
   1358 	if (ndo->ndo_vflag)
   1359 		ND_PRINT((ndo, " POST:"));
   1360 	if (!(dp = parse_post_op_attr(ndo, dp, verbose)))
   1361 		return (0);
   1362 	if (er)
   1363 		return dp;
   1364 	if (ndo->ndo_vflag) {
   1365 		ND_TCHECK(dp[1]);
   1366 		ND_PRINT((ndo, " verf %08x%08x", dp[0], dp[1]));
   1367 		dp += 2;
   1368 	}
   1369 	return dp;
   1370 trunc:
   1371 	return (NULL);
   1372 }
   1373 
   1374 static int
   1375 parsefsinfo(netdissect_options *ndo,
   1376             const uint32_t *dp)
   1377 {
   1378 	const struct nfsv3_fsinfo *sfp;
   1379 	int er;
   1380 
   1381 	if (!(dp = parsestatus(ndo, dp, &er)))
   1382 		return (0);
   1383 	if (ndo->ndo_vflag)
   1384 		ND_PRINT((ndo, " POST:"));
   1385 	if (!(dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag)))
   1386 		return (0);
   1387 	if (er)
   1388 		return (1);
   1389 
   1390 	sfp = (const struct nfsv3_fsinfo *)dp;
   1391 	ND_TCHECK(*sfp);
   1392 	ND_PRINT((ndo, " rtmax %u rtpref %u wtmax %u wtpref %u dtpref %u",
   1393 	       EXTRACT_32BITS(&sfp->fs_rtmax),
   1394 	       EXTRACT_32BITS(&sfp->fs_rtpref),
   1395 	       EXTRACT_32BITS(&sfp->fs_wtmax),
   1396 	       EXTRACT_32BITS(&sfp->fs_wtpref),
   1397 	       EXTRACT_32BITS(&sfp->fs_dtpref)));
   1398 	if (ndo->ndo_vflag) {
   1399 		ND_PRINT((ndo, " rtmult %u wtmult %u maxfsz %" PRIu64,
   1400 		       EXTRACT_32BITS(&sfp->fs_rtmult),
   1401 		       EXTRACT_32BITS(&sfp->fs_wtmult),
   1402 		       EXTRACT_64BITS((const uint32_t *)&sfp->fs_maxfilesize)));
   1403 		ND_PRINT((ndo, " delta %u.%06u ",
   1404 		       EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_sec),
   1405 		       EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_nsec)));
   1406 	}
   1407 	return (1);
   1408 trunc:
   1409 	return (0);
   1410 }
   1411 
   1412 static int
   1413 parsepathconf(netdissect_options *ndo,
   1414               const uint32_t *dp)
   1415 {
   1416 	int er;
   1417 	const struct nfsv3_pathconf *spp;
   1418 
   1419 	if (!(dp = parsestatus(ndo, dp, &er)))
   1420 		return (0);
   1421 	if (ndo->ndo_vflag)
   1422 		ND_PRINT((ndo, " POST:"));
   1423 	if (!(dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag)))
   1424 		return (0);
   1425 	if (er)
   1426 		return (1);
   1427 
   1428 	spp = (const struct nfsv3_pathconf *)dp;
   1429 	ND_TCHECK(*spp);
   1430 
   1431 	ND_PRINT((ndo, " linkmax %u namemax %u %s %s %s %s",
   1432 	       EXTRACT_32BITS(&spp->pc_linkmax),
   1433 	       EXTRACT_32BITS(&spp->pc_namemax),
   1434 	       EXTRACT_32BITS(&spp->pc_notrunc) ? "notrunc" : "",
   1435 	       EXTRACT_32BITS(&spp->pc_chownrestricted) ? "chownres" : "",
   1436 	       EXTRACT_32BITS(&spp->pc_caseinsensitive) ? "igncase" : "",
   1437 	       EXTRACT_32BITS(&spp->pc_casepreserving) ? "keepcase" : ""));
   1438 	return (1);
   1439 trunc:
   1440 	return (0);
   1441 }
   1442 
   1443 static void
   1444 interp_reply(netdissect_options *ndo,
   1445              const struct sunrpc_msg *rp, uint32_t proc, uint32_t vers, int length)
   1446 {
   1447 	register const uint32_t *dp;
   1448 	register int v3;
   1449 	int er;
   1450 
   1451 	v3 = (vers == NFS_VER3);
   1452 
   1453 	if (!v3 && proc < NFS_NPROCS)
   1454 		proc = nfsv3_procid[proc];
   1455 
   1456 	ND_PRINT((ndo, " %s", tok2str(nfsproc_str, "proc-%u", proc)));
   1457 	switch (proc) {
   1458 
   1459 	case NFSPROC_GETATTR:
   1460 		dp = parserep(ndo, rp, length);
   1461 		if (dp != NULL && parseattrstat(ndo, dp, !ndo->ndo_qflag, v3) != 0)
   1462 			return;
   1463 		break;
   1464 
   1465 	case NFSPROC_SETATTR:
   1466 		if (!(dp = parserep(ndo, rp, length)))
   1467 			return;
   1468 		if (v3) {
   1469 			if (parsewccres(ndo, dp, ndo->ndo_vflag))
   1470 				return;
   1471 		} else {
   1472 			if (parseattrstat(ndo, dp, !ndo->ndo_qflag, 0) != 0)
   1473 				return;
   1474 		}
   1475 		break;
   1476 
   1477 	case NFSPROC_LOOKUP:
   1478 		if (!(dp = parserep(ndo, rp, length)))
   1479 			break;
   1480 		if (v3) {
   1481 			if (!(dp = parsestatus(ndo, dp, &er)))
   1482 				break;
   1483 			if (er) {
   1484 				if (ndo->ndo_vflag > 1) {
   1485 					ND_PRINT((ndo, " post dattr:"));
   1486 					dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag);
   1487 				}
   1488 			} else {
   1489 				if (!(dp = parsefh(ndo, dp, v3)))
   1490 					break;
   1491 				if ((dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag)) &&
   1492 				    ndo->ndo_vflag > 1) {
   1493 					ND_PRINT((ndo, " post dattr:"));
   1494 					dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag);
   1495 				}
   1496 			}
   1497 			if (dp)
   1498 				return;
   1499 		} else {
   1500 			if (parsediropres(ndo, dp) != 0)
   1501 				return;
   1502 		}
   1503 		break;
   1504 
   1505 	case NFSPROC_ACCESS:
   1506 		if (!(dp = parserep(ndo, rp, length)))
   1507 			break;
   1508 		if (!(dp = parsestatus(ndo, dp, &er)))
   1509 			break;
   1510 		if (ndo->ndo_vflag)
   1511 			ND_PRINT((ndo, " attr:"));
   1512 		if (!(dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag)))
   1513 			break;
   1514 		if (!er)
   1515 			ND_PRINT((ndo, " c %04x", EXTRACT_32BITS(&dp[0])));
   1516 		return;
   1517 
   1518 	case NFSPROC_READLINK:
   1519 		dp = parserep(ndo, rp, length);
   1520 		if (dp != NULL && parselinkres(ndo, dp, v3) != 0)
   1521 			return;
   1522 		break;
   1523 
   1524 	case NFSPROC_READ:
   1525 		if (!(dp = parserep(ndo, rp, length)))
   1526 			break;
   1527 		if (v3) {
   1528 			if (!(dp = parsestatus(ndo, dp, &er)))
   1529 				break;
   1530 			if (!(dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag)))
   1531 				break;
   1532 			if (er)
   1533 				return;
   1534 			if (ndo->ndo_vflag) {
   1535 				ND_TCHECK(dp[1]);
   1536 				ND_PRINT((ndo, " %u bytes", EXTRACT_32BITS(&dp[0])));
   1537 				if (EXTRACT_32BITS(&dp[1]))
   1538 					ND_PRINT((ndo, " EOF"));
   1539 			}
   1540 			return;
   1541 		} else {
   1542 			if (parseattrstat(ndo, dp, ndo->ndo_vflag, 0) != 0)
   1543 				return;
   1544 		}
   1545 		break;
   1546 
   1547 	case NFSPROC_WRITE:
   1548 		if (!(dp = parserep(ndo, rp, length)))
   1549 			break;
   1550 		if (v3) {
   1551 			if (!(dp = parsestatus(ndo, dp, &er)))
   1552 				break;
   1553 			if (!(dp = parse_wcc_data(ndo, dp, ndo->ndo_vflag)))
   1554 				break;
   1555 			if (er)
   1556 				return;
   1557 			if (ndo->ndo_vflag) {
   1558 				ND_TCHECK(dp[0]);
   1559 				ND_PRINT((ndo, " %u bytes", EXTRACT_32BITS(&dp[0])));
   1560 				if (ndo->ndo_vflag > 1) {
   1561 					ND_TCHECK(dp[1]);
   1562 					ND_PRINT((ndo, " <%s>",
   1563 						tok2str(nfsv3_writemodes,
   1564 							NULL, EXTRACT_32BITS(&dp[1]))));
   1565 				}
   1566 				return;
   1567 			}
   1568 		} else {
   1569 			if (parseattrstat(ndo, dp, ndo->ndo_vflag, v3) != 0)
   1570 				return;
   1571 		}
   1572 		break;
   1573 
   1574 	case NFSPROC_CREATE:
   1575 	case NFSPROC_MKDIR:
   1576 		if (!(dp = parserep(ndo, rp, length)))
   1577 			break;
   1578 		if (v3) {
   1579 			if (parsecreateopres(ndo, dp, ndo->ndo_vflag) != NULL)
   1580 				return;
   1581 		} else {
   1582 			if (parsediropres(ndo, dp) != 0)
   1583 				return;
   1584 		}
   1585 		break;
   1586 
   1587 	case NFSPROC_SYMLINK:
   1588 		if (!(dp = parserep(ndo, rp, length)))
   1589 			break;
   1590 		if (v3) {
   1591 			if (parsecreateopres(ndo, dp, ndo->ndo_vflag) != NULL)
   1592 				return;
   1593 		} else {
   1594 			if (parsestatus(ndo, dp, &er) != NULL)
   1595 				return;
   1596 		}
   1597 		break;
   1598 
   1599 	case NFSPROC_MKNOD:
   1600 		if (!(dp = parserep(ndo, rp, length)))
   1601 			break;
   1602 		if (parsecreateopres(ndo, dp, ndo->ndo_vflag) != NULL)
   1603 			return;
   1604 		break;
   1605 
   1606 	case NFSPROC_REMOVE:
   1607 	case NFSPROC_RMDIR:
   1608 		if (!(dp = parserep(ndo, rp, length)))
   1609 			break;
   1610 		if (v3) {
   1611 			if (parsewccres(ndo, dp, ndo->ndo_vflag))
   1612 				return;
   1613 		} else {
   1614 			if (parsestatus(ndo, dp, &er) != NULL)
   1615 				return;
   1616 		}
   1617 		break;
   1618 
   1619 	case NFSPROC_RENAME:
   1620 		if (!(dp = parserep(ndo, rp, length)))
   1621 			break;
   1622 		if (v3) {
   1623 			if (!(dp = parsestatus(ndo, dp, &er)))
   1624 				break;
   1625 			if (ndo->ndo_vflag) {
   1626 				ND_PRINT((ndo, " from:"));
   1627 				if (!(dp = parse_wcc_data(ndo, dp, ndo->ndo_vflag)))
   1628 					break;
   1629 				ND_PRINT((ndo, " to:"));
   1630 				if (!(dp = parse_wcc_data(ndo, dp, ndo->ndo_vflag)))
   1631 					break;
   1632 			}
   1633 			return;
   1634 		} else {
   1635 			if (parsestatus(ndo, dp, &er) != NULL)
   1636 				return;
   1637 		}
   1638 		break;
   1639 
   1640 	case NFSPROC_LINK:
   1641 		if (!(dp = parserep(ndo, rp, length)))
   1642 			break;
   1643 		if (v3) {
   1644 			if (!(dp = parsestatus(ndo, dp, &er)))
   1645 				break;
   1646 			if (ndo->ndo_vflag) {
   1647 				ND_PRINT((ndo, " file POST:"));
   1648 				if (!(dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag)))
   1649 					break;
   1650 				ND_PRINT((ndo, " dir:"));
   1651 				if (!(dp = parse_wcc_data(ndo, dp, ndo->ndo_vflag)))
   1652 					break;
   1653 				return;
   1654 			}
   1655 		} else {
   1656 			if (parsestatus(ndo, dp, &er) != NULL)
   1657 				return;
   1658 		}
   1659 		break;
   1660 
   1661 	case NFSPROC_READDIR:
   1662 		if (!(dp = parserep(ndo, rp, length)))
   1663 			break;
   1664 		if (v3) {
   1665 			if (parsev3rddirres(ndo, dp, ndo->ndo_vflag))
   1666 				return;
   1667 		} else {
   1668 			if (parserddires(ndo, dp) != 0)
   1669 				return;
   1670 		}
   1671 		break;
   1672 
   1673 	case NFSPROC_READDIRPLUS:
   1674 		if (!(dp = parserep(ndo, rp, length)))
   1675 			break;
   1676 		if (parsev3rddirres(ndo, dp, ndo->ndo_vflag))
   1677 			return;
   1678 		break;
   1679 
   1680 	case NFSPROC_FSSTAT:
   1681 		dp = parserep(ndo, rp, length);
   1682 		if (dp != NULL && parsestatfs(ndo, dp, v3) != 0)
   1683 			return;
   1684 		break;
   1685 
   1686 	case NFSPROC_FSINFO:
   1687 		dp = parserep(ndo, rp, length);
   1688 		if (dp != NULL && parsefsinfo(ndo, dp) != 0)
   1689 			return;
   1690 		break;
   1691 
   1692 	case NFSPROC_PATHCONF:
   1693 		dp = parserep(ndo, rp, length);
   1694 		if (dp != NULL && parsepathconf(ndo, dp) != 0)
   1695 			return;
   1696 		break;
   1697 
   1698 	case NFSPROC_COMMIT:
   1699 		dp = parserep(ndo, rp, length);
   1700 		if (dp != NULL && parsewccres(ndo, dp, ndo->ndo_vflag) != 0)
   1701 			return;
   1702 		break;
   1703 
   1704 	default:
   1705 		return;
   1706 	}
   1707 trunc:
   1708 	if (!nfserr)
   1709 		ND_PRINT((ndo, "%s", tstr));
   1710 }
   1711