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