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