]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/tcpdump/print-nfs.c
MFC r368207,368607:
[FreeBSD/stable/10.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.111 2007-12-22 03:08:04 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 int 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         TCHECK(rp->rm_xid);
303         if (!nflag) {
304                 strlcpy(srcid, "nfs", sizeof(srcid));
305                 snprintf(dstid, sizeof(dstid), "%u",
306                     EXTRACT_32BITS(&rp->rm_xid));
307         } else {
308                 snprintf(srcid, sizeof(srcid), "%u", NFS_PORT);
309                 snprintf(dstid, sizeof(dstid), "%u",
310                     EXTRACT_32BITS(&rp->rm_xid));
311         }
312         print_nfsaddr(bp2, srcid, dstid);
313         TCHECK(rp->rm_reply.rp_stat);
314         reply_stat = EXTRACT_32BITS(&rp->rm_reply.rp_stat);
315         switch (reply_stat) {
316
317         case SUNRPC_MSG_ACCEPTED:
318                 (void)printf("reply ok %u", length);
319                 if (xid_map_find(rp, bp2, &proc, &vers) >= 0)
320                         interp_reply(rp, proc, vers, length);
321                 break;
322
323         case SUNRPC_MSG_DENIED:
324                 (void)printf("reply ERR %u: ", length);
325                 TCHECK(rp->rm_reply.rp_reject.rj_stat);
326                 rstat = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_stat);
327                 switch (rstat) {
328
329                 case SUNRPC_RPC_MISMATCH:
330                         TCHECK(rp->rm_reply.rp_reject.rj_vers.high);
331                         rlow = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_vers.low);
332                         rhigh = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_vers.high);
333                         (void)printf("RPC Version mismatch (%u-%u)",
334                             rlow, rhigh);
335                         break;
336
337                 case SUNRPC_AUTH_ERROR:
338                         TCHECK(rp->rm_reply.rp_reject.rj_why);
339                         rwhy = EXTRACT_32BITS(&rp->rm_reply.rp_reject.rj_why);
340                         (void)printf("Auth ");
341                         switch (rwhy) {
342
343                         case SUNRPC_AUTH_OK:
344                                 (void)printf("OK");
345                                 break;
346
347                         case SUNRPC_AUTH_BADCRED:
348                                 (void)printf("Bogus Credentials (seal broken)");
349                                 break;
350
351                         case SUNRPC_AUTH_REJECTEDCRED:
352                                 (void)printf("Rejected Credentials (client should begin new session)");
353                                 break;
354
355                         case SUNRPC_AUTH_BADVERF:
356                                 (void)printf("Bogus Verifier (seal broken)");
357                                 break;
358
359                         case SUNRPC_AUTH_REJECTEDVERF:
360                                 (void)printf("Verifier expired or was replayed");
361                                 break;
362
363                         case SUNRPC_AUTH_TOOWEAK:
364                                 (void)printf("Credentials are too weak");
365                                 break;
366
367                         case SUNRPC_AUTH_INVALIDRESP:
368                                 (void)printf("Bogus response verifier");
369                                 break;
370
371                         case SUNRPC_AUTH_FAILED:
372                                 (void)printf("Unknown failure");
373                                 break;
374
375                         default:
376                                 (void)printf("Invalid failure code %u",
377                                     (unsigned int)rwhy);
378                                 break;
379                         }
380                         break;
381
382                 default:
383                         (void)printf("Unknown reason for rejecting rpc message %u",
384                             (unsigned int)rstat);
385                         break;
386                 }
387                 break;
388
389         default:
390                 (void)printf("reply Unknown rpc response code=%u %u",
391                     reply_stat, length);
392                 break;
393         }
394         return;
395
396 trunc:
397         if (!nfserr)
398                 fputs(" [|nfs]", stdout);
399 }
400
401 /*
402  * Return a pointer to the first file handle in the packet.
403  * If the packet was truncated, return 0.
404  */
405 static const u_int32_t *
406 parsereq(register const struct sunrpc_msg *rp, register u_int length)
407 {
408         register const u_int32_t *dp;
409         register u_int len;
410
411         /*
412          * find the start of the req data (if we captured it)
413          */
414         dp = (u_int32_t *)&rp->rm_call.cb_cred;
415         TCHECK(dp[1]);
416         len = EXTRACT_32BITS(&dp[1]);
417         if (len < length) {
418                 dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
419                 TCHECK(dp[1]);
420                 len = EXTRACT_32BITS(&dp[1]);
421                 if (len < length) {
422                         dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
423                         TCHECK2(dp[0], 0);
424                         return (dp);
425                 }
426         }
427 trunc:
428         return (NULL);
429 }
430
431 /*
432  * Print out an NFS file handle and return a pointer to following word.
433  * If packet was truncated, return 0.
434  */
435 static const u_int32_t *
436 parsefh(register const u_int32_t *dp, int v3)
437 {
438         u_int len;
439
440         if (v3) {
441                 TCHECK(dp[0]);
442                 len = EXTRACT_32BITS(dp) / 4;
443                 dp++;
444         } else
445                 len = NFSX_V2FH / 4;
446
447         if (TTEST2(*dp, len * sizeof(*dp))) {
448                 nfs_printfh(dp, len);
449                 return (dp + len);
450         }
451 trunc:
452         return (NULL);
453 }
454
455 /*
456  * Print out a file name and return pointer to 32-bit word past it.
457  * If packet was truncated, return 0.
458  */
459 static const u_int32_t *
460 parsefn(register const u_int32_t *dp)
461 {
462         register u_int32_t len;
463         register const u_char *cp;
464
465         /* Bail if we don't have the string length */
466         TCHECK(*dp);
467
468         /* Fetch string length; convert to host order */
469         len = *dp++;
470         NTOHL(len);
471
472         TCHECK2(*dp, ((len + 3) & ~3));
473
474         cp = (u_char *)dp;
475         /* Update 32-bit pointer (NFS filenames padded to 32-bit boundaries) */
476         dp += ((len + 3) & ~3) / sizeof(*dp);
477         putchar('"');
478         if (fn_printn(cp, len, snapend)) {
479                 putchar('"');
480                 goto trunc;
481         }
482         putchar('"');
483
484         return (dp);
485 trunc:
486         return NULL;
487 }
488
489 /*
490  * Print out file handle and file name.
491  * Return pointer to 32-bit word past file name.
492  * If packet was truncated (or there was some other error), return 0.
493  */
494 static const u_int32_t *
495 parsefhn(register const u_int32_t *dp, int v3)
496 {
497         dp = parsefh(dp, v3);
498         if (dp == NULL)
499                 return (NULL);
500         putchar(' ');
501         return (parsefn(dp));
502 }
503
504 void
505 nfsreq_print(register const u_char *bp, u_int length,
506     register const u_char *bp2)
507 {
508         register const struct sunrpc_msg *rp;
509         register const u_int32_t *dp;
510         nfs_type type;
511         int v3;
512         u_int32_t proc;
513         u_int32_t access_flags;
514         struct nfsv3_sattr sa3;
515         char srcid[20], dstid[20];      /*fits 32bit*/
516
517         nfserr = 0;             /* assume no error */
518         rp = (const struct sunrpc_msg *)bp;
519
520         TCHECK(rp->rm_xid);
521         if (!nflag) {
522                 snprintf(srcid, sizeof(srcid), "%u",
523                     EXTRACT_32BITS(&rp->rm_xid));
524                 strlcpy(dstid, "nfs", sizeof(dstid));
525         } else {
526                 snprintf(srcid, sizeof(srcid), "%u",
527                     EXTRACT_32BITS(&rp->rm_xid));
528                 snprintf(dstid, sizeof(dstid), "%u", NFS_PORT);
529         }
530         print_nfsaddr(bp2, srcid, dstid);
531         (void)printf("%d", length);
532
533         if (!xid_map_enter(rp, bp2))    /* record proc number for later on */
534                 goto trunc;
535
536         v3 = (EXTRACT_32BITS(&rp->rm_call.cb_vers) == NFS_VER3);
537         proc = EXTRACT_32BITS(&rp->rm_call.cb_proc);
538
539         if (!v3 && proc < NFS_NPROCS)
540                 proc =  nfsv3_procid[proc];
541
542         switch (proc) {
543         case NFSPROC_NOOP:
544                 printf(" nop");
545                 return;
546         case NFSPROC_NULL:
547                 printf(" null");
548                 return;
549
550         case NFSPROC_GETATTR:
551                 printf(" getattr");
552                 if ((dp = parsereq(rp, length)) != NULL &&
553                     parsefh(dp, v3) != NULL)
554                         return;
555                 break;
556
557         case NFSPROC_SETATTR:
558                 printf(" setattr");
559                 if ((dp = parsereq(rp, length)) != NULL &&
560                     parsefh(dp, v3) != NULL)
561                         return;
562                 break;
563
564         case NFSPROC_LOOKUP:
565                 printf(" lookup");
566                 if ((dp = parsereq(rp, length)) != NULL &&
567                     parsefhn(dp, v3) != NULL)
568                         return;
569                 break;
570
571         case NFSPROC_ACCESS:
572                 printf(" access");
573                 if ((dp = parsereq(rp, length)) != NULL &&
574                     (dp = parsefh(dp, v3)) != NULL) {
575                         TCHECK(dp[0]);
576                         access_flags = EXTRACT_32BITS(&dp[0]);
577                         if (access_flags & ~NFSV3ACCESS_FULL) {
578                                 /* NFSV3ACCESS definitions aren't up to date */
579                                 printf(" %04x", access_flags);
580                         } else if ((access_flags & NFSV3ACCESS_FULL) == NFSV3ACCESS_FULL) {
581                                 printf(" NFS_ACCESS_FULL");
582                         } else {
583                                 char separator = ' ';
584                                 if (access_flags & NFSV3ACCESS_READ) {
585                                         printf(" NFS_ACCESS_READ");
586                                         separator = '|';
587                                 }
588                                 if (access_flags & NFSV3ACCESS_LOOKUP) {
589                                         printf("%cNFS_ACCESS_LOOKUP", separator);
590                                         separator = '|';
591                                 }
592                                 if (access_flags & NFSV3ACCESS_MODIFY) {
593                                         printf("%cNFS_ACCESS_MODIFY", separator);
594                                         separator = '|';
595                                 }
596                                 if (access_flags & NFSV3ACCESS_EXTEND) {
597                                         printf("%cNFS_ACCESS_EXTEND", separator);
598                                         separator = '|';
599                                 }
600                                 if (access_flags & NFSV3ACCESS_DELETE) {
601                                         printf("%cNFS_ACCESS_DELETE", separator);
602                                         separator = '|';
603                                 }
604                                 if (access_flags & NFSV3ACCESS_EXECUTE)
605                                         printf("%cNFS_ACCESS_EXECUTE", separator);
606                         }
607                         return;
608                 }
609                 break;
610
611         case NFSPROC_READLINK:
612                 printf(" readlink");
613                 if ((dp = parsereq(rp, length)) != NULL &&
614                     parsefh(dp, v3) != NULL)
615                         return;
616                 break;
617
618         case NFSPROC_READ:
619                 printf(" read");
620                 if ((dp = parsereq(rp, length)) != NULL &&
621                     (dp = parsefh(dp, v3)) != NULL) {
622                         if (v3) {
623                                 TCHECK(dp[2]);
624                                 printf(" %u bytes @ %" PRIu64,
625                                        EXTRACT_32BITS(&dp[2]),
626                                        EXTRACT_64BITS(&dp[0]));
627                         } else {
628                                 TCHECK(dp[1]);
629                                 printf(" %u bytes @ %u",
630                                     EXTRACT_32BITS(&dp[1]),
631                                     EXTRACT_32BITS(&dp[0]));
632                         }
633                         return;
634                 }
635                 break;
636
637         case NFSPROC_WRITE:
638                 printf(" write");
639                 if ((dp = parsereq(rp, length)) != NULL &&
640                     (dp = parsefh(dp, v3)) != NULL) {
641                         if (v3) {
642                                 TCHECK(dp[2]);
643                                 printf(" %u (%u) bytes @ %" PRIu64,
644                                                 EXTRACT_32BITS(&dp[4]),
645                                                 EXTRACT_32BITS(&dp[2]),
646                                                 EXTRACT_64BITS(&dp[0]));
647                                 if (vflag) {
648                                         dp += 3;
649                                         TCHECK(dp[0]);
650                                         printf(" <%s>",
651                                                 tok2str(nfsv3_writemodes,
652                                                         NULL, EXTRACT_32BITS(dp)));
653                                 }
654                         } else {
655                                 TCHECK(dp[3]);
656                                 printf(" %u (%u) bytes @ %u (%u)",
657                                                 EXTRACT_32BITS(&dp[3]),
658                                                 EXTRACT_32BITS(&dp[2]),
659                                                 EXTRACT_32BITS(&dp[1]),
660                                                 EXTRACT_32BITS(&dp[0]));
661                         }
662                         return;
663                 }
664                 break;
665
666         case NFSPROC_CREATE:
667                 printf(" create");
668                 if ((dp = parsereq(rp, length)) != NULL &&
669                     parsefhn(dp, v3) != NULL)
670                         return;
671                 break;
672
673         case NFSPROC_MKDIR:
674                 printf(" mkdir");
675                 if ((dp = parsereq(rp, length)) != 0 && parsefhn(dp, v3) != 0)
676                         return;
677                 break;
678
679         case NFSPROC_SYMLINK:
680                 printf(" symlink");
681                 if ((dp = parsereq(rp, length)) != 0 &&
682                     (dp = parsefhn(dp, v3)) != 0) {
683                         fputs(" ->", stdout);
684                         if (v3 && (dp = parse_sattr3(dp, &sa3)) == 0)
685                                 break;
686                         if (parsefn(dp) == 0)
687                                 break;
688                         if (v3 && vflag)
689                                 print_sattr3(&sa3, vflag);
690                         return;
691                 }
692                 break;
693
694         case NFSPROC_MKNOD:
695                 printf(" mknod");
696                 if ((dp = parsereq(rp, length)) != 0 &&
697                     (dp = parsefhn(dp, v3)) != 0) {
698                         TCHECK(*dp);
699                         type = (nfs_type)EXTRACT_32BITS(dp);
700                         dp++;
701                         if ((dp = parse_sattr3(dp, &sa3)) == 0)
702                                 break;
703                         printf(" %s", tok2str(type2str, "unk-ft %d", type));
704                         if (vflag && (type == NFCHR || type == NFBLK)) {
705                                 TCHECK(dp[1]);
706                                 printf(" %u/%u",
707                                        EXTRACT_32BITS(&dp[0]),
708                                        EXTRACT_32BITS(&dp[1]));
709                                 dp += 2;
710                         }
711                         if (vflag)
712                                 print_sattr3(&sa3, vflag);
713                         return;
714                 }
715                 break;
716
717         case NFSPROC_REMOVE:
718                 printf(" remove");
719                 if ((dp = parsereq(rp, length)) != NULL &&
720                     parsefhn(dp, v3) != NULL)
721                         return;
722                 break;
723
724         case NFSPROC_RMDIR:
725                 printf(" rmdir");
726                 if ((dp = parsereq(rp, length)) != NULL &&
727                     parsefhn(dp, v3) != NULL)
728                         return;
729                 break;
730
731         case NFSPROC_RENAME:
732                 printf(" rename");
733                 if ((dp = parsereq(rp, length)) != NULL &&
734                     (dp = parsefhn(dp, v3)) != NULL) {
735                         fputs(" ->", stdout);
736                         if (parsefhn(dp, v3) != NULL)
737                                 return;
738                 }
739                 break;
740
741         case NFSPROC_LINK:
742                 printf(" link");
743                 if ((dp = parsereq(rp, length)) != NULL &&
744                     (dp = parsefh(dp, v3)) != NULL) {
745                         fputs(" ->", stdout);
746                         if (parsefhn(dp, v3) != NULL)
747                                 return;
748                 }
749                 break;
750
751         case NFSPROC_READDIR:
752                 printf(" readdir");
753                 if ((dp = parsereq(rp, length)) != NULL &&
754                     (dp = parsefh(dp, v3)) != NULL) {
755                         if (v3) {
756                                 TCHECK(dp[4]);
757                                 /*
758                                  * We shouldn't really try to interpret the
759                                  * offset cookie here.
760                                  */
761                                 printf(" %u bytes @ %" PRId64,
762                                     EXTRACT_32BITS(&dp[4]),
763                                     EXTRACT_64BITS(&dp[0]));
764                                 if (vflag)
765                                         printf(" verf %08x%08x", dp[2],
766                                                dp[3]);
767                         } else {
768                                 TCHECK(dp[1]);
769                                 /*
770                                  * Print the offset as signed, since -1 is
771                                  * common, but offsets > 2^31 aren't.
772                                  */
773                                 printf(" %u bytes @ %d",
774                                     EXTRACT_32BITS(&dp[1]),
775                                     EXTRACT_32BITS(&dp[0]));
776                         }
777                         return;
778                 }
779                 break;
780
781         case NFSPROC_READDIRPLUS:
782                 printf(" readdirplus");
783                 if ((dp = parsereq(rp, length)) != NULL &&
784                     (dp = parsefh(dp, v3)) != NULL) {
785                         TCHECK(dp[4]);
786                         /*
787                          * We don't try to interpret the offset
788                          * cookie here.
789                          */
790                         printf(" %u bytes @ %" PRId64,
791                                 EXTRACT_32BITS(&dp[4]),
792                                 EXTRACT_64BITS(&dp[0]));
793                         if (vflag) {
794                                 TCHECK(dp[5]);
795                                 printf(" max %u verf %08x%08x",
796                                        EXTRACT_32BITS(&dp[5]), dp[2], dp[3]);
797                         }
798                         return;
799                 }
800                 break;
801
802         case NFSPROC_FSSTAT:
803                 printf(" fsstat");
804                 if ((dp = parsereq(rp, length)) != NULL &&
805                     parsefh(dp, v3) != NULL)
806                         return;
807                 break;
808
809         case NFSPROC_FSINFO:
810                 printf(" fsinfo");
811                 if ((dp = parsereq(rp, length)) != NULL &&
812                     parsefh(dp, v3) != NULL)
813                         return;
814                 break;
815
816         case NFSPROC_PATHCONF:
817                 printf(" pathconf");
818                 if ((dp = parsereq(rp, length)) != NULL &&
819                     parsefh(dp, v3) != NULL)
820                         return;
821                 break;
822
823         case NFSPROC_COMMIT:
824                 printf(" commit");
825                 if ((dp = parsereq(rp, length)) != NULL &&
826                     (dp = parsefh(dp, v3)) != NULL) {
827                         TCHECK(dp[2]);
828                         printf(" %u bytes @ %" PRIu64,
829                                 EXTRACT_32BITS(&dp[2]),
830                                 EXTRACT_64BITS(&dp[0]));
831                         return;
832                 }
833                 break;
834
835         default:
836                 printf(" proc-%u", EXTRACT_32BITS(&rp->rm_call.cb_proc));
837                 return;
838         }
839
840 trunc:
841         if (!nfserr)
842                 fputs(" [|nfs]", stdout);
843 }
844
845 /*
846  * Print out an NFS file handle.
847  * We assume packet was not truncated before the end of the
848  * file handle pointed to by dp.
849  *
850  * Note: new version (using portable file-handle parser) doesn't produce
851  * generation number.  It probably could be made to do that, with some
852  * additional hacking on the parser code.
853  */
854 static void
855 nfs_printfh(register const u_int32_t *dp, const u_int len)
856 {
857         my_fsid fsid;
858         ino_t ino;
859         const char *sfsname = NULL;
860         char *spacep;
861
862         if (uflag) {
863                 u_int i;
864                 char const *sep = "";
865
866                 printf(" fh[");
867                 for (i=0; i<len; i++) {
868                         (void)printf("%s%x", sep, dp[i]);
869                         sep = ":";
870                 }
871                 printf("]");
872                 return;
873         }
874
875         Parse_fh((const u_char *)dp, len, &fsid, &ino, NULL, &sfsname, 0);
876
877         if (sfsname) {
878                 /* file system ID is ASCII, not numeric, for this server OS */
879                 static char temp[NFSX_V3FHMAX+1];
880
881                 /* Make sure string is null-terminated */
882                 strncpy(temp, sfsname, NFSX_V3FHMAX);
883                 temp[sizeof(temp) - 1] = '\0';
884                 /* Remove trailing spaces */
885                 spacep = strchr(temp, ' ');
886                 if (spacep)
887                         *spacep = '\0';
888
889                 (void)printf(" fh %s/", temp);
890         } else {
891                 (void)printf(" fh %d,%d/",
892                              fsid.Fsid_dev.Major, fsid.Fsid_dev.Minor);
893         }
894
895         if(fsid.Fsid_dev.Minor == 257)
896                 /* Print the undecoded handle */
897                 (void)printf("%s", fsid.Opaque_Handle);
898         else
899                 (void)printf("%ld", (long) ino);
900 }
901
902 /*
903  * Maintain a small cache of recent client.XID.server/proc pairs, to allow
904  * us to match up replies with requests and thus to know how to parse
905  * the reply.
906  */
907
908 struct xid_map_entry {
909         u_int32_t       xid;            /* transaction ID (net order) */
910         int ipver;                      /* IP version (4 or 6) */
911 #ifdef INET6
912         struct in6_addr client;         /* client IP address (net order) */
913         struct in6_addr server;         /* server IP address (net order) */
914 #else
915         struct in_addr  client;         /* client IP address (net order) */
916         struct in_addr  server;         /* server IP address (net order) */
917 #endif
918         u_int32_t       proc;           /* call proc number (host order) */
919         u_int32_t       vers;           /* program version (host order) */
920 };
921
922 /*
923  * Map entries are kept in an array that we manage as a ring;
924  * new entries are always added at the tail of the ring.  Initially,
925  * all the entries are zero and hence don't match anything.
926  */
927
928 #define XIDMAPSIZE      64
929
930 struct xid_map_entry xid_map[XIDMAPSIZE];
931
932 int     xid_map_next = 0;
933 int     xid_map_hint = 0;
934
935 static int
936 xid_map_enter(const struct sunrpc_msg *rp, const u_char *bp)
937 {
938         struct ip *ip = NULL;
939 #ifdef INET6
940         struct ip6_hdr *ip6 = NULL;
941 #endif
942         struct xid_map_entry *xmep;
943
944         if (!TTEST(rp->rm_call.cb_vers))
945                 return (0);
946         switch (IP_V((struct ip *)bp)) {
947         case 4:
948                 ip = (struct ip *)bp;
949                 break;
950 #ifdef INET6
951         case 6:
952                 ip6 = (struct ip6_hdr *)bp;
953                 break;
954 #endif
955         default:
956                 return (1);
957         }
958
959         xmep = &xid_map[xid_map_next];
960
961         if (++xid_map_next >= XIDMAPSIZE)
962                 xid_map_next = 0;
963
964         xmep->xid = rp->rm_xid;
965         if (ip) {
966                 xmep->ipver = 4;
967                 memcpy(&xmep->client, &ip->ip_src, sizeof(ip->ip_src));
968                 memcpy(&xmep->server, &ip->ip_dst, sizeof(ip->ip_dst));
969         }
970 #ifdef INET6
971         else if (ip6) {
972                 xmep->ipver = 6;
973                 memcpy(&xmep->client, &ip6->ip6_src, sizeof(ip6->ip6_src));
974                 memcpy(&xmep->server, &ip6->ip6_dst, sizeof(ip6->ip6_dst));
975         }
976 #endif
977         xmep->proc = EXTRACT_32BITS(&rp->rm_call.cb_proc);
978         xmep->vers = EXTRACT_32BITS(&rp->rm_call.cb_vers);
979         return (1);
980 }
981
982 /*
983  * Returns 0 and puts NFSPROC_xxx in proc return and
984  * version in vers return, or returns -1 on failure
985  */
986 static int
987 xid_map_find(const struct sunrpc_msg *rp, const u_char *bp, u_int32_t *proc,
988              u_int32_t *vers)
989 {
990         int i;
991         struct xid_map_entry *xmep;
992         u_int32_t xid = rp->rm_xid;
993         struct ip *ip = (struct ip *)bp;
994 #ifdef INET6
995         struct ip6_hdr *ip6 = (struct ip6_hdr *)bp;
996 #endif
997         int cmp;
998
999         /* Start searching from where we last left off */
1000         i = xid_map_hint;
1001         do {
1002                 xmep = &xid_map[i];
1003                 cmp = 1;
1004                 if (xmep->ipver != IP_V(ip) || xmep->xid != xid)
1005                         goto nextitem;
1006                 switch (xmep->ipver) {
1007                 case 4:
1008                         if (memcmp(&ip->ip_src, &xmep->server,
1009                                    sizeof(ip->ip_src)) != 0 ||
1010                             memcmp(&ip->ip_dst, &xmep->client,
1011                                    sizeof(ip->ip_dst)) != 0) {
1012                                 cmp = 0;
1013                         }
1014                         break;
1015 #ifdef INET6
1016                 case 6:
1017                         if (memcmp(&ip6->ip6_src, &xmep->server,
1018                                    sizeof(ip6->ip6_src)) != 0 ||
1019                             memcmp(&ip6->ip6_dst, &xmep->client,
1020                                    sizeof(ip6->ip6_dst)) != 0) {
1021                                 cmp = 0;
1022                         }
1023                         break;
1024 #endif
1025                 default:
1026                         cmp = 0;
1027                         break;
1028                 }
1029                 if (cmp) {
1030                         /* match */
1031                         xid_map_hint = i;
1032                         *proc = xmep->proc;
1033                         *vers = xmep->vers;
1034                         return 0;
1035                 }
1036         nextitem:
1037                 if (++i >= XIDMAPSIZE)
1038                         i = 0;
1039         } while (i != xid_map_hint);
1040
1041         /* search failed */
1042         return (-1);
1043 }
1044
1045 /*
1046  * Routines for parsing reply packets
1047  */
1048
1049 /*
1050  * Return a pointer to the beginning of the actual results.
1051  * If the packet was truncated, return 0.
1052  */
1053 static const u_int32_t *
1054 parserep(register const struct sunrpc_msg *rp, register u_int length)
1055 {
1056         register const u_int32_t *dp;
1057         u_int len;
1058         enum sunrpc_accept_stat astat;
1059
1060         /*
1061          * Portability note:
1062          * Here we find the address of the ar_verf credentials.
1063          * Originally, this calculation was
1064          *      dp = (u_int32_t *)&rp->rm_reply.rp_acpt.ar_verf
1065          * On the wire, the rp_acpt field starts immediately after
1066          * the (32 bit) rp_stat field.  However, rp_acpt (which is a
1067          * "struct accepted_reply") contains a "struct opaque_auth",
1068          * whose internal representation contains a pointer, so on a
1069          * 64-bit machine the compiler inserts 32 bits of padding
1070          * before rp->rm_reply.rp_acpt.ar_verf.  So, we cannot use
1071          * the internal representation to parse the on-the-wire
1072          * representation.  Instead, we skip past the rp_stat field,
1073          * which is an "enum" and so occupies one 32-bit word.
1074          */
1075         dp = ((const u_int32_t *)&rp->rm_reply) + 1;
1076         TCHECK(dp[1]);
1077         len = EXTRACT_32BITS(&dp[1]);
1078         if (len >= length)
1079                 return (NULL);
1080         /*
1081          * skip past the ar_verf credentials.
1082          */
1083         dp += (len + (2*sizeof(u_int32_t) + 3)) / sizeof(u_int32_t);
1084         TCHECK2(dp[0], 0);
1085
1086         /*
1087          * now we can check the ar_stat field
1088          */
1089         astat = (enum sunrpc_accept_stat) EXTRACT_32BITS(dp);
1090         switch (astat) {
1091
1092         case SUNRPC_SUCCESS:
1093                 break;
1094
1095         case SUNRPC_PROG_UNAVAIL:
1096                 printf(" PROG_UNAVAIL");
1097                 nfserr = 1;             /* suppress trunc string */
1098                 return (NULL);
1099
1100         case SUNRPC_PROG_MISMATCH:
1101                 printf(" PROG_MISMATCH");
1102                 nfserr = 1;             /* suppress trunc string */
1103                 return (NULL);
1104
1105         case SUNRPC_PROC_UNAVAIL:
1106                 printf(" PROC_UNAVAIL");
1107                 nfserr = 1;             /* suppress trunc string */
1108                 return (NULL);
1109
1110         case SUNRPC_GARBAGE_ARGS:
1111                 printf(" GARBAGE_ARGS");
1112                 nfserr = 1;             /* suppress trunc string */
1113                 return (NULL);
1114
1115         case SUNRPC_SYSTEM_ERR:
1116                 printf(" SYSTEM_ERR");
1117                 nfserr = 1;             /* suppress trunc string */
1118                 return (NULL);
1119
1120         default:
1121                 printf(" ar_stat %d", astat);
1122                 nfserr = 1;             /* suppress trunc string */
1123                 return (NULL);
1124         }
1125         /* successful return */
1126         TCHECK2(*dp, sizeof(astat));
1127         return ((u_int32_t *) (sizeof(astat) + ((char *)dp)));
1128 trunc:
1129         return (0);
1130 }
1131
1132 static const u_int32_t *
1133 parsestatus(const u_int32_t *dp, int *er)
1134 {
1135         int errnum;
1136
1137         TCHECK(dp[0]);
1138
1139         errnum = EXTRACT_32BITS(&dp[0]);
1140         if (er)
1141                 *er = errnum;
1142         if (errnum != 0) {
1143                 if (!qflag)
1144                         printf(" ERROR: %s",
1145                             tok2str(status2str, "unk %d", errnum));
1146                 nfserr = 1;
1147         }
1148         return (dp + 1);
1149 trunc:
1150         return NULL;
1151 }
1152
1153 static const u_int32_t *
1154 parsefattr(const u_int32_t *dp, int verbose, int v3)
1155 {
1156         const struct nfs_fattr *fap;
1157
1158         fap = (const struct nfs_fattr *)dp;
1159         TCHECK(fap->fa_gid);
1160         if (verbose) {
1161                 printf(" %s %o ids %d/%d",
1162                     tok2str(type2str, "unk-ft %d ",
1163                     EXTRACT_32BITS(&fap->fa_type)),
1164                     EXTRACT_32BITS(&fap->fa_mode),
1165                     EXTRACT_32BITS(&fap->fa_uid),
1166                     EXTRACT_32BITS(&fap->fa_gid));
1167                 if (v3) {
1168                         TCHECK(fap->fa3_size);
1169                         printf(" sz %" PRIu64,
1170                                 EXTRACT_64BITS((u_int32_t *)&fap->fa3_size));
1171                 } else {
1172                         TCHECK(fap->fa2_size);
1173                         printf(" sz %d", EXTRACT_32BITS(&fap->fa2_size));
1174                 }
1175         }
1176         /* print lots more stuff */
1177         if (verbose > 1) {
1178                 if (v3) {
1179                         TCHECK(fap->fa3_ctime);
1180                         printf(" nlink %d rdev %d/%d",
1181                                EXTRACT_32BITS(&fap->fa_nlink),
1182                                EXTRACT_32BITS(&fap->fa3_rdev.specdata1),
1183                                EXTRACT_32BITS(&fap->fa3_rdev.specdata2));
1184                         printf(" fsid %" PRIx64,
1185                                 EXTRACT_64BITS((u_int32_t *)&fap->fa3_fsid));
1186                         printf(" fileid %" PRIx64,
1187                                 EXTRACT_64BITS((u_int32_t *)&fap->fa3_fileid));
1188                         printf(" a/m/ctime %u.%06u",
1189                                EXTRACT_32BITS(&fap->fa3_atime.nfsv3_sec),
1190                                EXTRACT_32BITS(&fap->fa3_atime.nfsv3_nsec));
1191                         printf(" %u.%06u",
1192                                EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_sec),
1193                                EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_nsec));
1194                         printf(" %u.%06u",
1195                                EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_sec),
1196                                EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_nsec));
1197                 } else {
1198                         TCHECK(fap->fa2_ctime);
1199                         printf(" nlink %d rdev %x fsid %x nodeid %x a/m/ctime",
1200                                EXTRACT_32BITS(&fap->fa_nlink),
1201                                EXTRACT_32BITS(&fap->fa2_rdev),
1202                                EXTRACT_32BITS(&fap->fa2_fsid),
1203                                EXTRACT_32BITS(&fap->fa2_fileid));
1204                         printf(" %u.%06u",
1205                                EXTRACT_32BITS(&fap->fa2_atime.nfsv2_sec),
1206                                EXTRACT_32BITS(&fap->fa2_atime.nfsv2_usec));
1207                         printf(" %u.%06u",
1208                                EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_sec),
1209                                EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_usec));
1210                         printf(" %u.%06u",
1211                                EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_sec),
1212                                EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_usec));
1213                 }
1214         }
1215         return ((const u_int32_t *)((unsigned char *)dp +
1216                 (v3 ? NFSX_V3FATTR : NFSX_V2FATTR)));
1217 trunc:
1218         return (NULL);
1219 }
1220
1221 static int
1222 parseattrstat(const u_int32_t *dp, int verbose, int v3)
1223 {
1224         int er;
1225
1226         dp = parsestatus(dp, &er);
1227         if (dp == NULL)
1228                 return (0);
1229         if (er)
1230                 return (1);
1231
1232         return (parsefattr(dp, verbose, v3) != NULL);
1233 }
1234
1235 static int
1236 parsediropres(const u_int32_t *dp)
1237 {
1238         int er;
1239
1240         if (!(dp = parsestatus(dp, &er)))
1241                 return (0);
1242         if (er)
1243                 return (1);
1244
1245         dp = parsefh(dp, 0);
1246         if (dp == NULL)
1247                 return (0);
1248
1249         return (parsefattr(dp, vflag, 0) != NULL);
1250 }
1251
1252 static int
1253 parselinkres(const u_int32_t *dp, int v3)
1254 {
1255         int er;
1256
1257         dp = parsestatus(dp, &er);
1258         if (dp == NULL)
1259                 return(0);
1260         if (er)
1261                 return(1);
1262         if (v3 && !(dp = parse_post_op_attr(dp, vflag)))
1263                 return (0);
1264         putchar(' ');
1265         return (parsefn(dp) != NULL);
1266 }
1267
1268 static int
1269 parsestatfs(const u_int32_t *dp, int v3)
1270 {
1271         const struct nfs_statfs *sfsp;
1272         int er;
1273
1274         dp = parsestatus(dp, &er);
1275         if (dp == NULL)
1276                 return (0);
1277         if (!v3 && er)
1278                 return (1);
1279
1280         if (qflag)
1281                 return(1);
1282
1283         if (v3) {
1284                 if (vflag)
1285                         printf(" POST:");
1286                 if (!(dp = parse_post_op_attr(dp, vflag)))
1287                         return (0);
1288         }
1289
1290         TCHECK2(*dp, (v3 ? NFSX_V3STATFS : NFSX_V2STATFS));
1291
1292         sfsp = (const struct nfs_statfs *)dp;
1293
1294         if (v3) {
1295                 printf(" tbytes %" PRIu64 " fbytes %" PRIu64 " abytes %" PRIu64,
1296                         EXTRACT_64BITS((u_int32_t *)&sfsp->sf_tbytes),
1297                         EXTRACT_64BITS((u_int32_t *)&sfsp->sf_fbytes),
1298                         EXTRACT_64BITS((u_int32_t *)&sfsp->sf_abytes));
1299                 if (vflag) {
1300                         printf(" tfiles %" PRIu64 " ffiles %" PRIu64 " afiles %" PRIu64 " invar %u",
1301                                EXTRACT_64BITS((u_int32_t *)&sfsp->sf_tfiles),
1302                                EXTRACT_64BITS((u_int32_t *)&sfsp->sf_ffiles),
1303                                EXTRACT_64BITS((u_int32_t *)&sfsp->sf_afiles),
1304                                EXTRACT_32BITS(&sfsp->sf_invarsec));
1305                 }
1306         } else {
1307                 printf(" tsize %d bsize %d blocks %d bfree %d bavail %d",
1308                         EXTRACT_32BITS(&sfsp->sf_tsize),
1309                         EXTRACT_32BITS(&sfsp->sf_bsize),
1310                         EXTRACT_32BITS(&sfsp->sf_blocks),
1311                         EXTRACT_32BITS(&sfsp->sf_bfree),
1312                         EXTRACT_32BITS(&sfsp->sf_bavail));
1313         }
1314
1315         return (1);
1316 trunc:
1317         return (0);
1318 }
1319
1320 static int
1321 parserddires(const u_int32_t *dp)
1322 {
1323         int er;
1324
1325         dp = parsestatus(dp, &er);
1326         if (dp == NULL)
1327                 return (0);
1328         if (er)
1329                 return (1);
1330         if (qflag)
1331                 return (1);
1332
1333         TCHECK(dp[2]);
1334         printf(" offset %x size %d ",
1335                EXTRACT_32BITS(&dp[0]), EXTRACT_32BITS(&dp[1]));
1336         if (dp[2] != 0)
1337                 printf(" eof");
1338
1339         return (1);
1340 trunc:
1341         return (0);
1342 }
1343
1344 static const u_int32_t *
1345 parse_wcc_attr(const u_int32_t *dp)
1346 {
1347         printf(" sz %" PRIu64, EXTRACT_64BITS(&dp[0]));
1348         printf(" mtime %u.%06u ctime %u.%06u",
1349                EXTRACT_32BITS(&dp[2]), EXTRACT_32BITS(&dp[3]),
1350                EXTRACT_32BITS(&dp[4]), EXTRACT_32BITS(&dp[5]));
1351         return (dp + 6);
1352 }
1353
1354 /*
1355  * Pre operation attributes. Print only if vflag > 1.
1356  */
1357 static const u_int32_t *
1358 parse_pre_op_attr(const u_int32_t *dp, int verbose)
1359 {
1360         TCHECK(dp[0]);
1361         if (!EXTRACT_32BITS(&dp[0]))
1362                 return (dp + 1);
1363         dp++;
1364         TCHECK2(*dp, 24);
1365         if (verbose > 1) {
1366                 return parse_wcc_attr(dp);
1367         } else {
1368                 /* If not verbose enough, just skip over wcc_attr */
1369                 return (dp + 6);
1370         }
1371 trunc:
1372         return (NULL);
1373 }
1374
1375 /*
1376  * Post operation attributes are printed if vflag >= 1
1377  */
1378 static const u_int32_t *
1379 parse_post_op_attr(const u_int32_t *dp, int verbose)
1380 {
1381         TCHECK(dp[0]);
1382         if (!EXTRACT_32BITS(&dp[0]))
1383                 return (dp + 1);
1384         dp++;
1385         if (verbose) {
1386                 return parsefattr(dp, verbose, 1);
1387         } else
1388                 return (dp + (NFSX_V3FATTR / sizeof (u_int32_t)));
1389 trunc:
1390         return (NULL);
1391 }
1392
1393 static const u_int32_t *
1394 parse_wcc_data(const u_int32_t *dp, int verbose)
1395 {
1396         if (verbose > 1)
1397                 printf(" PRE:");
1398         if (!(dp = parse_pre_op_attr(dp, verbose)))
1399                 return (0);
1400
1401         if (verbose)
1402                 printf(" POST:");
1403         return parse_post_op_attr(dp, verbose);
1404 }
1405
1406 static const u_int32_t *
1407 parsecreateopres(const u_int32_t *dp, int verbose)
1408 {
1409         int er;
1410
1411         if (!(dp = parsestatus(dp, &er)))
1412                 return (0);
1413         if (er)
1414                 dp = parse_wcc_data(dp, verbose);
1415         else {
1416                 TCHECK(dp[0]);
1417                 if (!EXTRACT_32BITS(&dp[0]))
1418                         return (dp + 1);
1419                 dp++;
1420                 if (!(dp = parsefh(dp, 1)))
1421                         return (0);
1422                 if (verbose) {
1423                         if (!(dp = parse_post_op_attr(dp, verbose)))
1424                                 return (0);
1425                         if (vflag > 1) {
1426                                 printf(" dir attr:");
1427                                 dp = parse_wcc_data(dp, verbose);
1428                         }
1429                 }
1430         }
1431         return (dp);
1432 trunc:
1433         return (NULL);
1434 }
1435
1436 static int
1437 parsewccres(const u_int32_t *dp, int verbose)
1438 {
1439         int er;
1440
1441         if (!(dp = parsestatus(dp, &er)))
1442                 return (0);
1443         return parse_wcc_data(dp, verbose) != 0;
1444 }
1445
1446 static const u_int32_t *
1447 parsev3rddirres(const u_int32_t *dp, int verbose)
1448 {
1449         int er;
1450
1451         if (!(dp = parsestatus(dp, &er)))
1452                 return (0);
1453         if (vflag)
1454                 printf(" POST:");
1455         if (!(dp = parse_post_op_attr(dp, verbose)))
1456                 return (0);
1457         if (er)
1458                 return dp;
1459         if (vflag) {
1460                 TCHECK(dp[1]);
1461                 printf(" verf %08x%08x", dp[0], dp[1]);
1462                 dp += 2;
1463         }
1464         return dp;
1465 trunc:
1466         return (NULL);
1467 }
1468
1469 static int
1470 parsefsinfo(const u_int32_t *dp)
1471 {
1472         struct nfsv3_fsinfo *sfp;
1473         int er;
1474
1475         if (!(dp = parsestatus(dp, &er)))
1476                 return (0);
1477         if (vflag)
1478                 printf(" POST:");
1479         if (!(dp = parse_post_op_attr(dp, vflag)))
1480                 return (0);
1481         if (er)
1482                 return (1);
1483
1484         sfp = (struct nfsv3_fsinfo *)dp;
1485         TCHECK(*sfp);
1486         printf(" rtmax %u rtpref %u wtmax %u wtpref %u dtpref %u",
1487                EXTRACT_32BITS(&sfp->fs_rtmax),
1488                EXTRACT_32BITS(&sfp->fs_rtpref),
1489                EXTRACT_32BITS(&sfp->fs_wtmax),
1490                EXTRACT_32BITS(&sfp->fs_wtpref),
1491                EXTRACT_32BITS(&sfp->fs_dtpref));
1492         if (vflag) {
1493                 printf(" rtmult %u wtmult %u maxfsz %" PRIu64,
1494                        EXTRACT_32BITS(&sfp->fs_rtmult),
1495                        EXTRACT_32BITS(&sfp->fs_wtmult),
1496                        EXTRACT_64BITS((u_int32_t *)&sfp->fs_maxfilesize));
1497                 printf(" delta %u.%06u ",
1498                        EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_sec),
1499                        EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_nsec));
1500         }
1501         return (1);
1502 trunc:
1503         return (0);
1504 }
1505
1506 static int
1507 parsepathconf(const u_int32_t *dp)
1508 {
1509         int er;
1510         struct nfsv3_pathconf *spp;
1511
1512         if (!(dp = parsestatus(dp, &er)))
1513                 return (0);
1514         if (vflag)
1515                 printf(" POST:");
1516         if (!(dp = parse_post_op_attr(dp, vflag)))
1517                 return (0);
1518         if (er)
1519                 return (1);
1520
1521         spp = (struct nfsv3_pathconf *)dp;
1522         TCHECK(*spp);
1523
1524         printf(" linkmax %u namemax %u %s %s %s %s",
1525                EXTRACT_32BITS(&spp->pc_linkmax),
1526                EXTRACT_32BITS(&spp->pc_namemax),
1527                EXTRACT_32BITS(&spp->pc_notrunc) ? "notrunc" : "",
1528                EXTRACT_32BITS(&spp->pc_chownrestricted) ? "chownres" : "",
1529                EXTRACT_32BITS(&spp->pc_caseinsensitive) ? "igncase" : "",
1530                EXTRACT_32BITS(&spp->pc_casepreserving) ? "keepcase" : "");
1531         return (1);
1532 trunc:
1533         return (0);
1534 }
1535
1536 static void
1537 interp_reply(const struct sunrpc_msg *rp, u_int32_t proc, u_int32_t vers, int length)
1538 {
1539         register const u_int32_t *dp;
1540         register int v3;
1541         int er;
1542
1543         v3 = (vers == NFS_VER3);
1544
1545         if (!v3 && proc < NFS_NPROCS)
1546                 proc = nfsv3_procid[proc];
1547
1548         switch (proc) {
1549
1550         case NFSPROC_NOOP:
1551                 printf(" nop");
1552                 return;
1553
1554         case NFSPROC_NULL:
1555                 printf(" null");
1556                 return;
1557
1558         case NFSPROC_GETATTR:
1559                 printf(" getattr");
1560                 dp = parserep(rp, length);
1561                 if (dp != NULL && parseattrstat(dp, !qflag, v3) != 0)
1562                         return;
1563                 break;
1564
1565         case NFSPROC_SETATTR:
1566                 printf(" setattr");
1567                 if (!(dp = parserep(rp, length)))
1568                         return;
1569                 if (v3) {
1570                         if (parsewccres(dp, vflag))
1571                                 return;
1572                 } else {
1573                         if (parseattrstat(dp, !qflag, 0) != 0)
1574                                 return;
1575                 }
1576                 break;
1577
1578         case NFSPROC_LOOKUP:
1579                 printf(" lookup");
1580                 if (!(dp = parserep(rp, length)))
1581                         break;
1582                 if (v3) {
1583                         if (!(dp = parsestatus(dp, &er)))
1584                                 break;
1585                         if (er) {
1586                                 if (vflag > 1) {
1587                                         printf(" post dattr:");
1588                                         dp = parse_post_op_attr(dp, vflag);
1589                                 }
1590                         } else {
1591                                 if (!(dp = parsefh(dp, v3)))
1592                                         break;
1593                                 if ((dp = parse_post_op_attr(dp, vflag)) &&
1594                                     vflag > 1) {
1595                                         printf(" post dattr:");
1596                                         dp = parse_post_op_attr(dp, vflag);
1597                                 }
1598                         }
1599                         if (dp)
1600                                 return;
1601                 } else {
1602                         if (parsediropres(dp) != 0)
1603                                 return;
1604                 }
1605                 break;
1606
1607         case NFSPROC_ACCESS:
1608                 printf(" access");
1609                 if (!(dp = parserep(rp, length)))
1610                         break;
1611                 if (!(dp = parsestatus(dp, &er)))
1612                         break;
1613                 if (vflag)
1614                         printf(" attr:");
1615                 if (!(dp = parse_post_op_attr(dp, vflag)))
1616                         break;
1617                 if (!er)
1618                         printf(" c %04x", EXTRACT_32BITS(&dp[0]));
1619                 return;
1620
1621         case NFSPROC_READLINK:
1622                 printf(" readlink");
1623                 dp = parserep(rp, length);
1624                 if (dp != NULL && parselinkres(dp, v3) != 0)
1625                         return;
1626                 break;
1627
1628         case NFSPROC_READ:
1629                 printf(" read");
1630                 if (!(dp = parserep(rp, length)))
1631                         break;
1632                 if (v3) {
1633                         if (!(dp = parsestatus(dp, &er)))
1634                                 break;
1635                         if (!(dp = parse_post_op_attr(dp, vflag)))
1636                                 break;
1637                         if (er)
1638                                 return;
1639                         if (vflag) {
1640                                 TCHECK(dp[1]);
1641                                 printf(" %u bytes", EXTRACT_32BITS(&dp[0]));
1642                                 if (EXTRACT_32BITS(&dp[1]))
1643                                         printf(" EOF");
1644                         }
1645                         return;
1646                 } else {
1647                         if (parseattrstat(dp, vflag, 0) != 0)
1648                                 return;
1649                 }
1650                 break;
1651
1652         case NFSPROC_WRITE:
1653                 printf(" write");
1654                 if (!(dp = parserep(rp, length)))
1655                         break;
1656                 if (v3) {
1657                         if (!(dp = parsestatus(dp, &er)))
1658                                 break;
1659                         if (!(dp = parse_wcc_data(dp, vflag)))
1660                                 break;
1661                         if (er)
1662                                 return;
1663                         if (vflag) {
1664                                 TCHECK(dp[0]);
1665                                 printf(" %u bytes", EXTRACT_32BITS(&dp[0]));
1666                                 if (vflag > 1) {
1667                                         TCHECK(dp[1]);
1668                                         printf(" <%s>",
1669                                                 tok2str(nfsv3_writemodes,
1670                                                         NULL, EXTRACT_32BITS(&dp[1])));
1671                                 }
1672                                 return;
1673                         }
1674                 } else {
1675                         if (parseattrstat(dp, vflag, v3) != 0)
1676                                 return;
1677                 }
1678                 break;
1679
1680         case NFSPROC_CREATE:
1681                 printf(" create");
1682                 if (!(dp = parserep(rp, length)))
1683                         break;
1684                 if (v3) {
1685                         if (parsecreateopres(dp, vflag) != 0)
1686                                 return;
1687                 } else {
1688                         if (parsediropres(dp) != 0)
1689                                 return;
1690                 }
1691                 break;
1692
1693         case NFSPROC_MKDIR:
1694                 printf(" mkdir");
1695                 if (!(dp = parserep(rp, length)))
1696                         break;
1697                 if (v3) {
1698                         if (parsecreateopres(dp, vflag) != 0)
1699                                 return;
1700                 } else {
1701                         if (parsediropres(dp) != 0)
1702                                 return;
1703                 }
1704                 break;
1705
1706         case NFSPROC_SYMLINK:
1707                 printf(" symlink");
1708                 if (!(dp = parserep(rp, length)))
1709                         break;
1710                 if (v3) {
1711                         if (parsecreateopres(dp, vflag) != 0)
1712                                 return;
1713                 } else {
1714                         if (parsestatus(dp, &er) != 0)
1715                                 return;
1716                 }
1717                 break;
1718
1719         case NFSPROC_MKNOD:
1720                 printf(" mknod");
1721                 if (!(dp = parserep(rp, length)))
1722                         break;
1723                 if (parsecreateopres(dp, vflag) != 0)
1724                         return;
1725                 break;
1726
1727         case NFSPROC_REMOVE:
1728                 printf(" remove");
1729                 if (!(dp = parserep(rp, length)))
1730                         break;
1731                 if (v3) {
1732                         if (parsewccres(dp, vflag))
1733                                 return;
1734                 } else {
1735                         if (parsestatus(dp, &er) != 0)
1736                                 return;
1737                 }
1738                 break;
1739
1740         case NFSPROC_RMDIR:
1741                 printf(" rmdir");
1742                 if (!(dp = parserep(rp, length)))
1743                         break;
1744                 if (v3) {
1745                         if (parsewccres(dp, vflag))
1746                                 return;
1747                 } else {
1748                         if (parsestatus(dp, &er) != 0)
1749                                 return;
1750                 }
1751                 break;
1752
1753         case NFSPROC_RENAME:
1754                 printf(" rename");
1755                 if (!(dp = parserep(rp, length)))
1756                         break;
1757                 if (v3) {
1758                         if (!(dp = parsestatus(dp, &er)))
1759                                 break;
1760                         if (vflag) {
1761                                 printf(" from:");
1762                                 if (!(dp = parse_wcc_data(dp, vflag)))
1763                                         break;
1764                                 printf(" to:");
1765                                 if (!(dp = parse_wcc_data(dp, vflag)))
1766                                         break;
1767                         }
1768                         return;
1769                 } else {
1770                         if (parsestatus(dp, &er) != 0)
1771                                 return;
1772                 }
1773                 break;
1774
1775         case NFSPROC_LINK:
1776                 printf(" link");
1777                 if (!(dp = parserep(rp, length)))
1778                         break;
1779                 if (v3) {
1780                         if (!(dp = parsestatus(dp, &er)))
1781                                 break;
1782                         if (vflag) {
1783                                 printf(" file POST:");
1784                                 if (!(dp = parse_post_op_attr(dp, vflag)))
1785                                         break;
1786                                 printf(" dir:");
1787                                 if (!(dp = parse_wcc_data(dp, vflag)))
1788                                         break;
1789                                 return;
1790                         }
1791                 } else {
1792                         if (parsestatus(dp, &er) != 0)
1793                                 return;
1794                 }
1795                 break;
1796
1797         case NFSPROC_READDIR:
1798                 printf(" readdir");
1799                 if (!(dp = parserep(rp, length)))
1800                         break;
1801                 if (v3) {
1802                         if (parsev3rddirres(dp, vflag))
1803                                 return;
1804                 } else {
1805                         if (parserddires(dp) != 0)
1806                                 return;
1807                 }
1808                 break;
1809
1810         case NFSPROC_READDIRPLUS:
1811                 printf(" readdirplus");
1812                 if (!(dp = parserep(rp, length)))
1813                         break;
1814                 if (parsev3rddirres(dp, vflag))
1815                         return;
1816                 break;
1817
1818         case NFSPROC_FSSTAT:
1819                 printf(" fsstat");
1820                 dp = parserep(rp, length);
1821                 if (dp != NULL && parsestatfs(dp, v3) != 0)
1822                         return;
1823                 break;
1824
1825         case NFSPROC_FSINFO:
1826                 printf(" fsinfo");
1827                 dp = parserep(rp, length);
1828                 if (dp != NULL && parsefsinfo(dp) != 0)
1829                         return;
1830                 break;
1831
1832         case NFSPROC_PATHCONF:
1833                 printf(" pathconf");
1834                 dp = parserep(rp, length);
1835                 if (dp != NULL && parsepathconf(dp) != 0)
1836                         return;
1837                 break;
1838
1839         case NFSPROC_COMMIT:
1840                 printf(" commit");
1841                 dp = parserep(rp, length);
1842                 if (dp != NULL && parsewccres(dp, vflag) != 0)
1843                         return;
1844                 break;
1845
1846         default:
1847                 printf(" proc-%u", proc);
1848                 return;
1849         }
1850 trunc:
1851         if (!nfserr)
1852                 fputs(" [|nfs]", stdout);
1853 }