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