print-nfs.c revision 276788
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: head/contrib/tcpdump/print-nfs.c 276788 2015-01-07 19:55:18Z delphij $
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
48static const char tstr[] = " [|nfs]";
49
50static void nfs_printfh(netdissect_options *, const uint32_t *, const u_int);
51static int xid_map_enter(netdissect_options *, const struct sunrpc_msg *, const u_char *);
52static int xid_map_find(const struct sunrpc_msg *, const u_char *,
53			    uint32_t *, uint32_t *);
54static void interp_reply(netdissect_options *, const struct sunrpc_msg *, uint32_t, uint32_t, int);
55static 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 */
60uint32_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
89static 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 */
127static 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
165static const struct tok nfsv3_writemodes[] = {
166	{ 0,		"unstable" },
167	{ 1,		"datasync" },
168	{ 2,		"filesync" },
169	{ 0,		NULL }
170};
171
172static 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
183static 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
195static 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
204static void
205print_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
244static const uint32_t *
245parse_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;
307trunc:
308	return NULL;
309}
310
311static int nfserr;		/* true if we error rather than trunc */
312
313static void
314print_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
333void
334nfsreply_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
359trunc:
360	if (!nfserr)
361		ND_PRINT((ndo, "%s", tstr));
362}
363
364void
365nfsreply_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
420trunc:
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 */
429static const uint32_t *
430parsereq(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	}
452trunc:
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 */
460static const uint32_t *
461parsefh(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	}
477trunc:
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 */
485static const uint32_t *
486parsefn(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);
512trunc:
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 */
521static const uint32_t *
522parsefhn(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
532void
533nfsreq_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
784trunc:
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 */
798static void
799nfs_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
853struct 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
875struct xid_map_entry xid_map[XIDMAPSIZE];
876
877int	xid_map_next = 0;
878int	xid_map_hint = 0;
879
880static int
881xid_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 */
932static int
933xid_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 */
999static const uint32_t *
1000parserep(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)));
1045trunc:
1046	return (0);
1047}
1048
1049static const uint32_t *
1050parsestatus(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);
1067trunc:
1068	return NULL;
1069}
1070
1071static const uint32_t *
1072parsefattr(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)));
1136trunc:
1137	return (NULL);
1138}
1139
1140static int
1141parseattrstat(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
1155static int
1156parsediropres(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
1173static int
1174parselinkres(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
1190static int
1191parsestatfs(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);
1239trunc:
1240	return (0);
1241}
1242
1243static int
1244parserddires(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);
1264trunc:
1265	return (0);
1266}
1267
1268static const uint32_t *
1269parse_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 */
1282static const uint32_t *
1283parse_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	}
1297trunc:
1298	return (NULL);
1299}
1300
1301/*
1302 * Post operation attributes are printed if vflag >= 1
1303 */
1304static const uint32_t *
1305parse_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)));
1316trunc:
1317	return (NULL);
1318}
1319
1320static const uint32_t *
1321parse_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
1334static const uint32_t *
1335parsecreateopres(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);
1361trunc:
1362	return (NULL);
1363}
1364
1365static int
1366parsewccres(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
1376static const uint32_t *
1377parsev3rddirres(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;
1396trunc:
1397	return (NULL);
1398}
1399
1400static int
1401parsefsinfo(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);
1434trunc:
1435	return (0);
1436}
1437
1438static int
1439parsepathconf(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);
1465trunc:
1466	return (0);
1467}
1468
1469static void
1470interp_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	}
1733trunc:
1734	if (!nfserr)
1735		ND_PRINT((ndo, "%s", tstr));
1736}
1737