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