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