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