]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/tcpdump/print-nfs.c
This commit was generated by cvs2svn to compensate for changes in r57419,
[FreeBSD/FreeBSD.git] / contrib / tcpdump / print-nfs.c
1 /*
2  * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that: (1) source code distributions
7  * retain the above copyright notice and this paragraph in its entirety, (2)
8  * distributions including binary code include the above copyright notice and
9  * this paragraph in its entirety in the documentation or other materials
10  * provided with the distribution, and (3) all advertising materials mentioning
11  * features or use of this software display the following acknowledgement:
12  * ``This product includes software developed by the University of California,
13  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14  * the University nor the names of its contributors may be used to endorse
15  * or promote products derived from this software without specific prior
16  * written permission.
17  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20  */
21
22 #ifndef lint
23 static const char rcsid[] =
24     "@(#) $Header: print-nfs.c,v 1.65 97/08/17 13:24:22 leres Exp $ (LBL)";
25 #endif
26
27 #include <sys/param.h>
28 #include <sys/time.h>
29 #include <sys/socket.h>
30
31 #include <net/if.h>
32
33 #include <netinet/in.h>
34 #include <net/ethernet.h>
35 #include <netinet/in_systm.h>
36 #include <netinet/ip.h>
37 #include <netinet/ip_var.h>
38
39 #include <rpc/rpc.h>
40
41 #include <ctype.h>
42 #include <pcap.h>
43 #include <stdio.h>
44 #include <string.h>
45
46 #include "interface.h"
47 #include "addrtoname.h"
48 #include "extract.h"                    /* must come after interface.h */
49
50 #include "nfs.h"
51 #include "nfsfh.h"
52
53 static void nfs_printfh(const u_int32_t *, const int);
54 static void xid_map_enter(const struct rpc_msg *, const struct ip *);
55 static int32_t xid_map_find(const struct rpc_msg *, const struct ip *, u_int32_t *,
56                           u_int32_t *);
57 static void interp_reply(const struct rpc_msg *, u_int32_t, u_int32_t, int);
58 static const u_int32_t *parse_post_op_attr(const u_int32_t *, int);
59
60 static int nfserr;              /* true if we error rather than trunc */
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 const char *nfsv3_writemodes[NFSV3WRITE_NMODES] = {
95         "unstable",
96         "datasync",
97         "filesync"
98 };
99
100 static struct tok type2str[] = {
101         { NFNON,        "NON" },
102         { NFREG,        "REG" },
103         { NFDIR,        "DIR" },
104         { NFBLK,        "BLK" },
105         { NFCHR,        "CHR" },
106         { NFLNK,        "LNK" },
107         { NFFIFO,       "FIFO" },
108         { 0,            NULL }
109 };
110
111 /*
112  * Print out a 64-bit integer. This appears to be different on each system,
113  * try to make the best of it. The integer stored as 2 consecutive XDR
114  * encoded 32-bit integers, to which a pointer is passed.
115  *
116  * Assume that a system that has INT64_FORMAT defined, has a 64-bit
117  * integer datatype and can print it.
118  */ 
119
120 #define UNSIGNED 0
121 #define SIGNED   1
122 #define HEX      2
123
124 #define   INT64_FORMAT   "%qd"
125 #define U_INT64_FORMAT   "%qu"
126 #define HEX_INT64_FORMAT "%qx"
127
128 int print_int64(const u_int32_t *dp, int how)
129 {
130 #ifdef INT64_FORMAT
131         u_int64_t res;
132
133         res = ((u_int64_t)ntohl(dp[0]) << 32) | (u_int64_t)ntohl(dp[1]);
134         switch (how) {
135         case SIGNED:
136                 printf(INT64_FORMAT, res);
137                 break;
138         case UNSIGNED:
139                 printf(U_INT64_FORMAT, res);
140                 break;
141         case HEX:
142                 printf(HEX_INT64_FORMAT, res);
143                 break;
144         default:
145                 return (0);
146         }
147 #else
148         /*
149          * XXX - throw upper 32 bits away.
150          * Could also go for hex: printf("0x%x%x", dp[0], dp[1]);
151          */
152         if (how == SIGNED)
153                 printf("%ld", (int)dp[1]);
154         else
155                 printf("%lu", (unsigned int)dp[1]);
156 #endif
157         return 1;
158 }
159
160 static const u_int32_t *
161 parse_sattr3(const u_int32_t *dp, struct nfsv3_sattr *sa3)
162 {
163         register const u_int32_t *ep = (u_int32_t *)snapend;
164
165         if (dp + 1 > ep)
166                 return (NULL);
167         if ((sa3->sa_modeset = ntohl(*dp++))) {
168                 if (dp + 1 > ep)
169                         return (NULL);
170                 sa3->sa_mode = ntohl(*dp++);
171         }
172
173         if (dp + 1 > ep)
174                 return (NULL);
175         if ((sa3->sa_uidset = ntohl(*dp++))) {
176                 if (dp + 1 > ep)
177                         return (NULL);
178                 sa3->sa_uid = ntohl(*dp++);
179         }
180
181         if (dp + 1 > ep)
182                 return (NULL);
183         if ((sa3->sa_gidset = ntohl(*dp++))) {
184                 if (dp + 1 > ep)
185                         return (NULL);
186                 sa3->sa_gid = ntohl(*dp++);
187         }
188
189         if (dp + 1 > ep)
190                 return (NULL);
191         if ((sa3->sa_sizeset = ntohl(*dp++))) {
192                 if (dp + 1 > ep)
193                         return (NULL);
194                 sa3->sa_size = ntohl(*dp++);
195         }
196
197         if (dp + 1 > ep)
198                 return (NULL);
199         if ((sa3->sa_atimetype = ntohl(*dp++)) == NFSV3SATTRTIME_TOCLIENT) {
200                 if (dp + 2 > ep)
201                         return (NULL);
202                 sa3->sa_atime.nfsv3_sec = ntohl(*dp++);
203                 sa3->sa_atime.nfsv3_nsec = ntohl(*dp++);
204         }
205
206         if (dp + 1 > ep)
207                 return (NULL);
208         if ((sa3->sa_mtimetype = ntohl(*dp++)) == NFSV3SATTRTIME_TOCLIENT) {
209                 if (dp + 2 > ep)
210                         return (NULL);
211                 sa3->sa_mtime.nfsv3_sec = ntohl(*dp++);
212                 sa3->sa_mtime.nfsv3_nsec = ntohl(*dp++);
213         }
214
215         return dp;
216 }
217
218 void
219 print_sattr3(const struct nfsv3_sattr *sa3, int verbose)
220 {
221         if (sa3->sa_modeset)
222                 printf(" mode %o", sa3->sa_mode);
223         if (sa3->sa_uidset)
224                 printf(" uid %u", sa3->sa_uid);
225         if (sa3->sa_gidset)
226                 printf(" gid %u", sa3->sa_gid);
227         if (verbose > 1) {
228                 if (sa3->sa_atimetype == NFSV3SATTRTIME_TOCLIENT)
229                         printf(" atime %u.%06u", sa3->sa_atime.nfsv3_sec,
230                                sa3->sa_atime.nfsv3_nsec);
231                 if (sa3->sa_mtimetype == NFSV3SATTRTIME_TOCLIENT)
232                         printf(" mtime %u.%06u", sa3->sa_mtime.nfsv3_sec,
233                                sa3->sa_mtime.nfsv3_nsec);
234         }
235 }
236
237 void
238 nfsreply_print(register const u_char *bp, u_int length,
239                register const u_char *bp2)
240 {
241         register const struct rpc_msg *rp;
242         register const struct ip *ip;
243         u_int32_t proc, vers;
244
245         nfserr = 0;             /* assume no error */
246         rp = (const struct rpc_msg *)bp;
247         ip = (const struct ip *)bp2;
248
249         if (!nflag)
250                 (void)printf("%s.nfs > %s.%u: reply %s %d",
251                              ipaddr_string(&ip->ip_src),
252                              ipaddr_string(&ip->ip_dst),
253                              (u_int32_t)ntohl(rp->rm_xid),
254                              ntohl(rp->rm_reply.rp_stat) == MSG_ACCEPTED?
255                                      "ok":"ERR",
256                              length);
257         else
258                 (void)printf("%s.%u > %s.%u: reply %s %d",
259                              ipaddr_string(&ip->ip_src),
260                              NFS_PORT,
261                              ipaddr_string(&ip->ip_dst),
262                              (u_int32_t)ntohl(rp->rm_xid),
263                              ntohl(rp->rm_reply.rp_stat) == MSG_ACCEPTED?
264                                 "ok":"ERR",
265                              length);
266
267         if (xid_map_find(rp, ip, &proc, &vers) >= 0)
268                 interp_reply(rp, proc, vers, length);
269 }
270
271 /*
272  * Return a pointer to the first file handle in the packet.
273  * If the packet was truncated, return NULL.
274  */
275 static const u_int32_t *
276 parsereq(register const struct rpc_msg *rp, register int length)
277 {
278         register const u_int32_t *dp;
279         register u_int len;
280
281         /*
282          * find the start of the req data (if we captured it)
283          */
284         dp = (u_int32_t *)&rp->rm_call.cb_cred;
285         TCHECK(dp[1]);
286         len = ntohl(dp[1]);
287         if (len < length) {
288                 dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
289                 TCHECK(dp[1]);
290                 len = ntohl(dp[1]);
291                 if (len < length) {
292                         dp += (len + (2 * sizeof(*dp) + 3)) / sizeof(*dp);
293                         TCHECK2(dp[0], 0);
294                         return (dp);
295                 }
296         }
297 trunc:
298         return (NULL);
299 }
300
301 /*
302  * Print out an NFS file handle and return a pointer to following word.
303  * If packet was truncated, return NULL.
304  */
305 static const u_int32_t *
306 parsefh(register const u_int32_t *dp, int v3)
307 {
308         int len;
309
310         if (v3) {
311                 TCHECK(dp[0]);
312                 len = (int)ntohl(*dp) / 4;
313                 dp++;
314         } else
315                 len = NFSX_V2FH / 4;
316
317         if (TTEST2(*dp, len * sizeof(*dp))) {
318                 nfs_printfh(dp, len);
319                 return (dp + len);
320         }
321 trunc:
322         return (NULL);
323 }
324
325 /*
326  * Print out a file name and return pointer to 32-bit word past it.
327  * If packet was truncated, return NULL.
328  */
329 static const u_int32_t *
330 parsefn(register const u_int32_t *dp)
331 {
332         register u_int32_t len;
333         register const u_char *cp;
334
335         /* Bail if we don't have the string length */
336         if ((u_char *)dp > snapend - sizeof(*dp))
337                 return (NULL);
338
339         /* Fetch string length; convert to host order */
340         len = *dp++;
341         NTOHL(len);
342
343         cp = (u_char *)dp;
344         /* Update 32-bit pointer (NFS filenames padded to 32-bit boundaries) */
345         dp += ((len + 3) & ~3) / sizeof(*dp);
346         if ((u_char *)dp > snapend)
347                 return (NULL);
348         /* XXX seems like we should be checking the length */
349         putchar('"');
350         (void) fn_printn(cp, len, NULL);
351         putchar('"');
352
353         return (dp);
354 }
355
356 /*
357  * Print out file handle and file name.
358  * Return pointer to 32-bit word past file name.
359  * If packet was truncated (or there was some other error), return NULL.
360  */
361 static const u_int32_t *
362 parsefhn(register const u_int32_t *dp, int v3)
363 {
364         dp = parsefh(dp, v3);
365         if (dp == NULL)
366                 return (NULL);
367         putchar(' ');
368         return (parsefn(dp));
369 }
370
371 void
372 nfsreq_print(register const u_char *bp, u_int length,
373     register const u_char *bp2)
374 {
375         register const struct rpc_msg *rp;
376         register const struct ip *ip;
377         register const u_int32_t *dp;
378         nfstype type;
379         int proc, v3;
380         struct nfsv3_sattr sa3;
381
382         nfserr = 0;             /* assume no error */
383         rp = (const struct rpc_msg *)bp;
384         ip = (const struct ip *)bp2;
385         if (!nflag)
386                 (void)printf("%s.%u > %s.nfs: %d",
387                              ipaddr_string(&ip->ip_src),
388                              (u_int32_t)ntohl(rp->rm_xid),
389                              ipaddr_string(&ip->ip_dst),
390                              length);
391         else
392                 (void)printf("%s.%u > %s.%u: %d",
393                              ipaddr_string(&ip->ip_src),
394                              (u_int32_t)ntohl(rp->rm_xid),
395                              ipaddr_string(&ip->ip_dst),
396                              NFS_PORT,
397                              length);
398
399         xid_map_enter(rp, ip);  /* record proc number for later on */
400
401         v3 = (ntohl(rp->rm_call.cb_vers) == NFS_VER3);
402         proc = ntohl(rp->rm_call.cb_proc);
403
404         if (!v3 && proc < NFS_NPROCS)
405                 proc =  nfsv3_procid[proc];
406
407         switch (proc) {
408         case NFSPROC_NOOP:
409                 printf(" nop");
410                 return;
411         case NFSPROC_NULL:
412                 printf(" null");
413                 return;
414
415         case NFSPROC_GETATTR:
416                 printf(" getattr");
417                 if ((dp = parsereq(rp, length)) != NULL && parsefh(dp, v3) != NULL)
418                         return;
419                 break;
420
421         case NFSPROC_SETATTR:
422                 printf(" setattr");
423                 if ((dp = parsereq(rp, length)) != NULL && parsefh(dp, v3) != NULL)
424                         return;
425                 break;
426
427         case NFSPROC_LOOKUP:
428                 printf(" lookup");
429                 if ((dp = parsereq(rp, length)) != NULL && parsefhn(dp, v3) != NULL)
430                         return;
431                 break;
432
433         case NFSPROC_ACCESS:
434                 printf(" access");
435                 if ((dp = parsereq(rp, length)) != NULL &&
436                     (dp = parsefh(dp, v3)) != NULL) {
437                         TCHECK(*dp);
438                         printf(" %04lx", ntohl(dp[0]));
439                         return;
440                 }
441                 break;
442
443         case NFSPROC_READLINK:
444                 printf(" readlink");
445                 if ((dp = parsereq(rp, length)) != NULL && parsefh(dp, v3) != NULL)
446                         return;
447                 break;
448
449         case NFSPROC_READ:
450                 printf(" read");
451                 if ((dp = parsereq(rp, length)) != NULL &&
452                     (dp = parsefh(dp, v3)) != NULL) {
453                         if (v3) {
454                                 TCHECK2(*dp, 3 * sizeof(*dp));
455                                 printf(" %lu bytes @ ", ntohl(dp[2]));
456                                 print_int64(dp, UNSIGNED);
457                         } else {
458                                 TCHECK2(*dp, 2 * sizeof(*dp));
459                                 printf(" %lu bytes @ %lu",
460                                        ntohl(dp[1]), ntohl(dp[0]));
461                         }
462                         return;
463                 }
464                 break;
465
466         case NFSPROC_WRITE:
467                 printf(" write");
468                 if ((dp = parsereq(rp, length)) != NULL &&
469                     (dp = parsefh(dp, v3)) != NULL) {
470                         if (v3) {
471                                 TCHECK2(*dp, 3 * sizeof(*dp));
472                                 printf(" %lu bytes @ ", ntohl(dp[4]));
473                                 print_int64(dp, UNSIGNED);
474                                 if (vflag) {
475                                         dp += 3;
476                                         TCHECK2(*dp, sizeof(*dp));
477                                         printf(" <%s>",
478                                                nfsv3_writemodes[ntohl(*dp)]);
479                                 }
480                         } else {
481                                 TCHECK2(*dp, 4 * sizeof(*dp));
482                                 printf(" %lu (%lu) bytes @ %lu (%lu)",
483                                        ntohl(dp[3]), ntohl(dp[2]),
484                                        ntohl(dp[1]), ntohl(dp[0]));
485                         }
486                         return;
487                 }
488                 break;
489
490         case NFSPROC_CREATE:
491                 printf(" create");
492                 if ((dp = parsereq(rp, length)) != NULL && parsefhn(dp, v3) != NULL)
493                         return;
494                 break;
495
496         case NFSPROC_MKDIR:
497                 printf(" mkdir");
498                 if ((dp = parsereq(rp, length)) != NULL && parsefhn(dp, v3) != NULL)
499                         return;
500                 break;
501
502         case NFSPROC_SYMLINK:
503                 printf(" symlink");
504                 if ((dp = parsereq(rp, length)) != NULL &&
505                     (dp = parsefhn(dp, v3)) != NULL) {
506                         fputs(" -> ", stdout);
507                         if (v3 && (dp = parse_sattr3(dp, &sa3)) == NULL)
508                                 break;
509                         if (parsefn(dp) == NULL)
510                                 break;
511                         if (v3 && vflag)
512                                 print_sattr3(&sa3, vflag);
513                         return;
514                 }
515                 break;
516
517         case NFSPROC_MKNOD:
518                 printf(" mknod");
519                 if ((dp = parsereq(rp, length)) != NULL &&
520                     (dp = parsefhn(dp, v3)) != NULL) {
521                         if (dp + 1 > (u_int32_t *)snapend)
522                                 break;
523                         type = (nfstype)ntohl(*dp++);
524                         if ((dp = parse_sattr3(dp, &sa3)) == NULL)
525                                 break;
526                         printf(" %s", tok2str(type2str, "unk-ft %d", type));
527                         if (vflag && (type == NFCHR || type == NFBLK)) {
528                                 if (dp + 2 > (u_int32_t *)snapend)
529                                         break;
530                                 printf(" %lu/%lu", ntohl(dp[0]), ntohl(dp[1]));
531                                 dp += 2;
532                         }
533                         if (vflag)
534                                 print_sattr3(&sa3, vflag);
535                         return;
536                 }
537                 break;
538
539         case NFSPROC_REMOVE:
540                 printf(" remove");
541                 if ((dp = parsereq(rp, length)) != NULL && parsefhn(dp, v3) != NULL)
542                         return;
543                 break;
544
545         case NFSPROC_RMDIR:
546                 printf(" rmdir");
547                 if ((dp = parsereq(rp, length)) != NULL && parsefhn(dp, v3) != NULL)
548                         return;
549                 break;
550
551         case NFSPROC_RENAME:
552                 printf(" rename");
553                 if ((dp = parsereq(rp, length)) != NULL &&
554                     (dp = parsefhn(dp, v3)) != NULL) {
555                         fputs(" ->", stdout);
556                         if (parsefhn(dp, v3) != NULL)
557                                 return;
558                 }
559                 break;
560
561         case NFSPROC_LINK:
562                 printf(" link");
563                 if ((dp = parsereq(rp, length)) != NULL &&
564                     (dp = parsefh(dp, v3)) != NULL) {
565                         fputs(" ->", stdout);
566                         if (parsefhn(dp, v3) != NULL)
567                                 return;
568                 }
569                 break;
570
571         case NFSPROC_READDIR:
572                 printf(" readdir");
573                 if ((dp = parsereq(rp, length)) != NULL &&
574                     (dp = parsefh(dp, v3)) != NULL) {
575                         if (v3) {
576                                 TCHECK2(*dp, 20);
577                                 /*
578                                  * We shouldn't really try to interpret the
579                                  * offset cookie here.
580                                  */
581                                 printf(" %lu bytes @ ", ntohl(dp[4]));
582                                 print_int64(dp, SIGNED);
583                                 if (vflag)
584                                         printf(" verf %08x%08x", dp[2],
585                                                dp[3]);
586                         } else {
587                                 TCHECK2(*dp, 2 * sizeof(*dp));
588                                 /*
589                                  * Print the offset as signed, since -1 is
590                                  * common, but offsets > 2^31 aren't.
591                                  */
592                                 printf(" %lu bytes @ %ld", ntohl(dp[1]),
593                                        ntohl(dp[0]));
594                         }
595                         return;
596                 }
597                 break;
598
599         case NFSPROC_READDIRPLUS:
600                 printf(" readdirplus");
601                 if ((dp = parsereq(rp, length)) != NULL &&
602                     (dp = parsefh(dp, v3)) != NULL) {
603                         TCHECK2(*dp, 20);
604                         /*
605                          * We don't try to interpret the offset
606                          * cookie here.
607                          */
608                         printf(" %lu bytes @ ", ntohl(dp[4]));
609                         print_int64(dp, SIGNED);
610                         if (vflag)
611                                 printf(" max %lu verf %08x%08x",
612                                        ntohl(dp[5]), dp[2], dp[3]);
613                         return;
614                 }
615                 break;
616
617         case NFSPROC_FSSTAT:
618                 printf(" fsstat");
619                 if ((dp = parsereq(rp, length)) != NULL && parsefh(dp, v3) != NULL)
620                         return;
621                 break;
622
623         case NFSPROC_FSINFO:
624                 printf(" fsinfo");
625                 break;
626
627         case NFSPROC_PATHCONF:
628                 printf(" pathconf");
629                 break;
630
631         case NFSPROC_COMMIT:
632                 printf(" commit");
633                 if ((dp = parsereq(rp, length)) != NULL &&
634                     (dp = parsefh(dp, v3)) != NULL) {
635                         printf(" %lu bytes @ ", ntohl(dp[2]));
636                         print_int64(dp, UNSIGNED);
637                         return;
638                 }
639                 break;
640
641         default:
642                 printf(" proc-%lu", ntohl(rp->rm_call.cb_proc));
643                 return;
644         }
645 trunc:
646         if (!nfserr)
647                 fputs(" [|nfs]", stdout);
648 }
649
650 /*
651  * Print out an NFS file handle.
652  * We assume packet was not truncated before the end of the
653  * file handle pointed to by dp.
654  *
655  * Note: new version (using portable file-handle parser) doesn't produce
656  * generation number.  It probably could be made to do that, with some
657  * additional hacking on the parser code.
658  */
659 static void
660 nfs_printfh(register const u_int32_t *dp, const int len)
661 {
662         my_fsid fsid;
663         ino_t ino;
664         char *sfsname = NULL;
665
666         Parse_fh((caddr_t *)dp, len, &fsid, &ino, NULL, &sfsname, 0);
667
668         if (sfsname) {
669                 /* file system ID is ASCII, not numeric, for this server OS */
670                 static char temp[NFSX_V3FHMAX+1];
671
672                 /* Make sure string is null-terminated */
673                 strncpy(temp, sfsname, NFSX_V3FHMAX);
674                 /* Remove trailing spaces */
675                 sfsname = strchr(temp, ' ');
676                 if (sfsname)
677                         *sfsname = 0;
678
679                 (void)printf(" fh %s/%u", temp, (u_int32_t)ino);
680         } else {
681                 (void)printf(" fh %u,%u/%u",
682                     fsid.Fsid_dev.Major, fsid.Fsid_dev.Minor, (u_int32_t)ino);
683         }
684 }
685
686 /*
687  * Maintain a small cache of recent client.XID.server/proc pairs, to allow
688  * us to match up replies with requests and thus to know how to parse
689  * the reply.
690  */
691
692 struct xid_map_entry {
693         u_int32_t               xid;            /* transaction ID (net order) */
694         struct in_addr  client;         /* client IP address (net order) */
695         struct in_addr  server;         /* server IP address (net order) */
696         u_int32_t               proc;           /* call proc number (host order) */
697         u_int32_t               vers;           /* program version (host order) */
698 };
699
700 /*
701  * Map entries are kept in an array that we manage as a ring;
702  * new entries are always added at the tail of the ring.  Initially,
703  * all the entries are zero and hence don't match anything.
704  */
705
706 #define XIDMAPSIZE      64
707
708 struct xid_map_entry xid_map[XIDMAPSIZE];
709
710 int     xid_map_next = 0;
711 int     xid_map_hint = 0;
712
713 static void
714 xid_map_enter(const struct rpc_msg *rp, const struct ip *ip)
715 {
716         struct xid_map_entry *xmep;
717
718         xmep = &xid_map[xid_map_next];
719
720         if (++xid_map_next >= XIDMAPSIZE)
721                 xid_map_next = 0;
722
723         xmep->xid = rp->rm_xid;
724         xmep->client = ip->ip_src;
725         xmep->server = ip->ip_dst;
726         xmep->proc = ntohl(rp->rm_call.cb_proc);
727         xmep->vers = ntohl(rp->rm_call.cb_vers);
728 }
729
730 /*
731  * Returns 0 and puts NFSPROC_xxx in proc return and
732  * version in vers return, or returns -1 on failure
733  */
734 static int
735 xid_map_find(const struct rpc_msg *rp, const struct ip *ip, u_int32_t *proc,
736              u_int32_t *vers)
737 {
738         int i;
739         struct xid_map_entry *xmep;
740         u_int32_t xid = rp->rm_xid;
741         u_int32_t clip = ip->ip_dst.s_addr;
742         u_int32_t sip = ip->ip_src.s_addr;
743
744         /* Start searching from where we last left off */
745         i = xid_map_hint;
746         do {
747                 xmep = &xid_map[i];
748                 if (xmep->xid == xid && xmep->client.s_addr == clip &&
749                     xmep->server.s_addr == sip) {
750                         /* match */
751                         xid_map_hint = i;
752                         *proc = xmep->proc;
753                         *vers = xmep->vers;
754                         return 0;
755                 }
756                 if (++i >= XIDMAPSIZE)
757                         i = 0;
758         } while (i != xid_map_hint);
759
760         /* search failed */
761         return (0);
762 }
763
764 /*
765  * Routines for parsing reply packets
766  */
767
768 /*
769  * Return a pointer to the beginning of the actual results.
770  * If the packet was truncated, return NULL.
771  */
772 static const u_int32_t *
773 parserep(register const struct rpc_msg *rp, register int length)
774 {
775         register const u_int32_t *dp;
776         int len;
777         enum accept_stat astat;
778
779         /*
780          * Portability note:
781          * Here we find the address of the ar_verf credentials.
782          * Originally, this calculation was
783          *      dp = (u_int32_t *)&rp->rm_reply.rp_acpt.ar_verf
784          * On the wire, the rp_acpt field starts immediately after
785          * the (32 bit) rp_stat field.  However, rp_acpt (which is a
786          * "struct accepted_reply") contains a "struct opaque_auth",
787          * whose internal representation contains a pointer, so on a
788          * 64-bit machine the compiler inserts 32 bits of padding
789          * before rp->rm_reply.rp_acpt.ar_verf.  So, we cannot use
790          * the internal representation to parse the on-the-wire
791          * representation.  Instead, we skip past the rp_stat field,
792          * which is an "enum" and so occupies one 32-bit word.
793          */
794         dp = ((const u_int32_t *)&rp->rm_reply) + 1;
795         TCHECK2(dp[0], 1);
796         len = ntohl(dp[1]);
797         if (len >= length)
798                 return (NULL);
799         /*
800          * skip past the ar_verf credentials.
801          */
802         dp += (len + (2*sizeof(u_int32_t) + 3)) / sizeof(u_int32_t);
803         TCHECK2(dp[0], 0);
804
805         /*
806          * now we can check the ar_stat field
807          */
808         astat = ntohl(*(enum accept_stat *)dp);
809         switch (astat) {
810
811         case SUCCESS:
812                 break;
813
814         case PROG_UNAVAIL:
815                 printf(" PROG_UNAVAIL");
816                 nfserr = 1;             /* suppress trunc string */
817                 return (NULL);
818
819         case PROG_MISMATCH:
820                 printf(" PROG_MISMATCH");
821                 nfserr = 1;             /* suppress trunc string */
822                 return (NULL);
823
824         case PROC_UNAVAIL:
825                 printf(" PROC_UNAVAIL");
826                 nfserr = 1;             /* suppress trunc string */
827                 return (NULL);
828
829         case GARBAGE_ARGS:
830                 printf(" GARBAGE_ARGS");
831                 nfserr = 1;             /* suppress trunc string */
832                 return (NULL);
833
834         case SYSTEM_ERR:
835                 printf(" SYSTEM_ERR");
836                 nfserr = 1;             /* suppress trunc string */
837                 return (NULL);
838
839         default:
840                 printf(" ar_stat %d", astat);
841                 nfserr = 1;             /* suppress trunc string */
842                 return (NULL);
843         }
844         /* successful return */
845         if ((sizeof(astat) + ((u_char *)dp)) < snapend)
846                 return ((u_int32_t *) (sizeof(astat) + ((char *)dp)));
847
848 trunc:
849         return (NULL);
850 }
851
852
853 static const u_int32_t *
854 parsestatus(const u_int32_t *dp, int *er)
855 {
856         register int errnum;
857
858         TCHECK(dp[0]);
859         errnum = ntohl(dp[0]);
860         if (er)
861                 *er = errnum;
862         if (errnum != 0) {
863                 if (!qflag)
864                         printf(" ERROR: %s", pcap_strerror(errnum));
865                 nfserr = 1;
866                 return (NULL);
867         }
868         return (dp + 1);
869 trunc:
870         return (NULL);
871 }
872
873 static const u_int32_t *
874 parsefattr(const u_int32_t *dp, int verbose, int v3)
875 {
876         const struct nfs_fattr *fap;
877
878         fap = (const struct nfs_fattr *)dp;
879         TCHECK(fap->fa_gid);
880         if (verbose) {
881                 printf(" %s %lo ids %ld/%ld",
882                     tok2str(type2str, "unk-ft %d ", ntohl(fap->fa_type)),
883                        ntohl(fap->fa_mode), ntohl(fap->fa_uid),
884                        ntohl(fap->fa_gid));
885                 if (v3) {
886                         TCHECK(fap->fa3_size);
887                         printf(" sz ");
888                         print_int64((u_int32_t *)&fap->fa3_size, UNSIGNED);
889                         putchar(' ');
890                 } else {
891                         TCHECK(fap->fa2_size);
892                         printf(" sz %ld ", ntohl(fap->fa2_size));
893                 }
894         }
895         /* print lots more stuff */
896         if (verbose > 1) {
897                 if (v3) {
898                         TCHECK(fap->fa3_ctime);
899                         printf("nlink %ld rdev %ld/%ld ",
900                                ntohl(fap->fa_nlink),
901                                ntohl(fap->fa3_rdev.specdata1),
902                                ntohl(fap->fa3_rdev.specdata2));
903                         printf("fsid ");
904                         print_int64((u_int32_t *)&fap->fa2_fsid, HEX);
905                         printf(" nodeid ");
906                         print_int64((u_int32_t *)&fap->fa2_fileid, HEX);
907                         printf(" a/m/ctime %lu.%06lu ",
908                                ntohl(fap->fa3_atime.nfsv3_sec),
909                                ntohl(fap->fa3_atime.nfsv3_nsec));
910                         printf("%lu.%06lu ",
911                                ntohl(fap->fa3_mtime.nfsv3_sec),
912                                ntohl(fap->fa3_mtime.nfsv3_nsec));
913                         printf("%lu.%06lu ",
914                                ntohl(fap->fa3_ctime.nfsv3_sec),
915                                ntohl(fap->fa3_ctime.nfsv3_nsec));
916                 } else {
917                         TCHECK(fap->fa2_ctime);
918                         printf("nlink %ld rdev %lx fsid %lx nodeid %lx a/m/ctime ",
919                                ntohl(fap->fa_nlink), ntohl(fap->fa2_rdev),
920                                ntohl(fap->fa2_fsid), ntohl(fap->fa2_fileid));
921                         printf("%lu.%06lu ",
922                                ntohl(fap->fa2_atime.nfsv2_sec),
923                                ntohl(fap->fa2_atime.nfsv2_usec));
924                         printf("%lu.%06lu ",
925                                ntohl(fap->fa2_mtime.nfsv2_sec),
926                                ntohl(fap->fa2_mtime.nfsv2_usec));
927                         printf("%lu.%06lu ",
928                                ntohl(fap->fa2_ctime.nfsv2_sec),
929                                ntohl(fap->fa2_ctime.nfsv2_usec));
930                 }
931         }
932         return ((const u_int32_t *)((unsigned char *)dp +
933                 (v3 ? NFSX_V3FATTR : NFSX_V2FATTR)));
934 trunc:
935         return (NULL);
936 }
937
938 static int
939 parseattrstat(const u_int32_t *dp, int verbose, int v3)
940 {
941         int er;
942
943         dp = parsestatus(dp, &er);
944         if (dp == NULL || er)
945                 return (0);
946
947         return (parsefattr(dp, verbose, v3) != NULL);
948 }
949
950 static int
951 parsediropres(const u_int32_t *dp)
952 {
953         int er;
954
955         dp = parsestatus(dp, &er);
956         if (dp == NULL || er)
957                 return (0);
958
959         dp = parsefh(dp, 0);
960         if (dp == NULL)
961                 return (0);
962
963         return (parsefattr(dp, vflag, 0) != NULL);
964 }
965
966 static int
967 parselinkres(const u_int32_t *dp, int v3)
968 {
969         int er;
970
971         dp = parsestatus(dp, &er);
972         if (dp == NULL || er)
973                 return(0);
974
975         if (v3 && ((dp = parse_post_op_attr(dp, vflag)) != NULL))
976                 return (0);
977
978         putchar(' ');
979         return (parsefn(dp) != NULL);
980 }
981
982 static int
983 parsestatfs(const u_int32_t *dp, int v3)
984 {
985         const struct nfs_statfs *sfsp;
986         int er;
987
988         dp = parsestatus(dp, &er);
989         if (dp == NULL || (!v3 && er))
990                 return(0);
991
992         if (qflag)
993                 return(1);
994
995         if (v3) {
996                 if (vflag)
997                         printf(" POST:");
998                 if ((dp = parse_post_op_attr(dp, vflag)) == NULL)
999                         return (0);
1000         }
1001
1002         TCHECK2(dp, (v3 ? NFSX_V3STATFS : NFSX_V2STATFS));
1003
1004         sfsp = (const struct nfs_statfs *)dp;
1005
1006         if (v3) {
1007                 printf(" tbytes ");
1008                 print_int64((u_int32_t *)&sfsp->sf_tbytes, UNSIGNED);
1009                 printf(" fbytes ");
1010                 print_int64((u_int32_t *)&sfsp->sf_fbytes, UNSIGNED);
1011                 printf(" abytes ");
1012                 print_int64((u_int32_t *)&sfsp->sf_abytes, UNSIGNED);
1013                 if (vflag) {
1014                         printf(" tfiles ");
1015                         print_int64((u_int32_t *)&sfsp->sf_tfiles, UNSIGNED);
1016                         printf(" ffiles ");
1017                         print_int64((u_int32_t *)&sfsp->sf_ffiles, UNSIGNED);
1018                         printf(" afiles ");
1019                         print_int64((u_int32_t *)&sfsp->sf_afiles, UNSIGNED);
1020                         printf(" invar %lu", ntohl(sfsp->sf_invarsec));
1021                 }
1022         } else {
1023                 printf(" tsize %ld bsize %ld blocks %ld bfree %ld bavail %ld",
1024                        ntohl(sfsp->sf_tsize), ntohl(sfsp->sf_bsize),
1025                        ntohl(sfsp->sf_blocks), ntohl(sfsp->sf_bfree),
1026                        ntohl(sfsp->sf_bavail));
1027         }
1028
1029         return (1);
1030 trunc:
1031         return (0);
1032 }
1033
1034 static int
1035 parserddires(const u_int32_t *dp)
1036 {
1037         int er;
1038
1039         dp = parsestatus(dp, &er);
1040         if (dp == NULL || er)
1041                 return (0);
1042         if (qflag)
1043                 return (1);
1044
1045         TCHECK(dp[2]);
1046         printf(" offset %lx size %ld ", ntohl(dp[0]), ntohl(dp[1]));
1047         if (dp[2] != 0)
1048                 printf("eof");
1049
1050         return (1);
1051 trunc:
1052         return (0);
1053 }
1054
1055 static const u_int32_t *
1056 parse_wcc_attr(const u_int32_t *dp)
1057 {
1058         printf(" sz ");
1059         print_int64(dp, UNSIGNED);
1060         printf(" mtime %lu.%06lu ctime %lu.%06lu", ntohl(dp[2]), ntohl(dp[3]),
1061                ntohl(dp[4]), ntohl(dp[5]));
1062         return (dp + 6);
1063 }
1064
1065 /*
1066  * Pre operation attributes. Print only if vflag > 1.
1067  */
1068 static const u_int32_t *
1069 parse_pre_op_attr(const u_int32_t *dp, int verbose)
1070 {
1071         TCHECK(dp[0]);
1072         if (!ntohl(dp[0]))
1073                 return (dp + 1);
1074         dp++;
1075         TCHECK2(dp, 24);
1076         if (verbose > 1) {
1077                 return parse_wcc_attr(dp);
1078         } else {
1079                 /* If not verbose enough, just skip over wcc_attr */
1080                 return (dp + 6);
1081         }
1082 trunc:
1083         return (NULL);
1084 }
1085
1086 /*
1087  * Post operation attributes are printed if vflag >= 1
1088  */
1089 static const u_int32_t *
1090 parse_post_op_attr(const u_int32_t *dp, int verbose)
1091 {
1092         TCHECK(dp[0]);
1093         if (!ntohl(dp[0]))
1094                 return (dp + 1);
1095         dp++;
1096         if (verbose) {
1097                 return parsefattr(dp, verbose, 1);
1098         } else
1099                 return (dp + (NFSX_V3FATTR / sizeof (u_int32_t)));
1100 trunc:
1101         return (NULL);
1102 }
1103
1104 static const u_int32_t *
1105 parse_wcc_data(const u_int32_t *dp, int verbose)
1106 {
1107         if (verbose > 1)
1108                 printf(" PRE:");
1109         if ((dp = parse_pre_op_attr(dp, verbose)) == NULL)
1110                 return (NULL);
1111
1112         if (verbose)
1113                 printf(" POST:");
1114         return parse_post_op_attr(dp, verbose);
1115 }
1116
1117 static const u_int32_t *
1118 parsecreateopres(const u_int32_t *dp, int verbose)
1119 {
1120         int er;
1121
1122         if ((dp = parsestatus(dp, &er)) == NULL)
1123                 return (NULL);
1124         if (er)
1125                 dp = parse_wcc_data(dp, verbose);
1126         else {
1127                 TCHECK(dp[0]);
1128                 if (!ntohl(dp[0]))
1129                         return (dp + 1);
1130                 dp++;
1131                 if ((dp = parsefh(dp, 1)) == NULL)
1132                         return (NULL);
1133                 if (verbose) {
1134                         if ((dp = parse_post_op_attr(dp, verbose)) == NULL)
1135                                 return (NULL);
1136                         if (vflag > 1) {
1137                                 printf("dir attr:");
1138                                 dp = parse_wcc_data(dp, verbose);
1139                         }
1140                 }
1141         }
1142         return (dp);
1143 trunc:
1144         return (NULL);
1145 }
1146
1147 static int
1148 parsewccres(const u_int32_t *dp, int verbose)
1149 {
1150         int er;
1151
1152         if ((dp = parsestatus(dp, &er)) == NULL)
1153                 return (0);
1154         return parse_wcc_data(dp, verbose) != NULL;
1155 }
1156
1157 static const u_int32_t *
1158 parsev3rddirres(const u_int32_t *dp, int verbose)
1159 {
1160         int er;
1161
1162         if ((dp = parsestatus(dp, &er)) == NULL)
1163                 return (NULL);
1164         if (vflag)
1165                 printf(" POST:");
1166         if ((dp = parse_post_op_attr(dp, verbose)) == NULL)
1167                 return (NULL);
1168         if (er)
1169                 return dp;
1170         if (vflag) {
1171                 TCHECK(dp[1]);
1172                 printf(" verf %08x%08x", dp[0], dp[1]);
1173                 dp += 2;
1174         }
1175         return dp;
1176 trunc:
1177         return (NULL);
1178 }
1179
1180 static int
1181 parsefsinfo(const u_int32_t *dp)
1182 {
1183         struct nfsv3_fsinfo *sfp;
1184         int er;
1185
1186         if ((dp = parsestatus(dp, &er)) == NULL)
1187                 return (0);
1188         if (vflag)
1189                 printf(" POST:");
1190         if ((dp = parse_post_op_attr(dp, vflag)) == NULL)
1191                 return (0);
1192         if (er)
1193                 return (1);
1194
1195         sfp = (struct nfsv3_fsinfo *)dp;
1196         TCHECK(*sfp);
1197         printf(" rtmax %lu rtpref %lu wtmax %lu wtpref %lu dtpref %lu",
1198                ntohl(sfp->fs_rtmax), ntohl(sfp->fs_rtpref),
1199                ntohl(sfp->fs_wtmax), ntohl(sfp->fs_wtpref),
1200                ntohl(sfp->fs_dtpref));
1201         if (vflag) {
1202                 printf(" rtmult %lu wtmult %lu maxfsz ",
1203                        ntohl(sfp->fs_rtmult), ntohl(sfp->fs_wtmult));
1204                 print_int64((u_int32_t *)&sfp->fs_maxfilesize, UNSIGNED);
1205                 printf(" delta %lu.%06lu ", ntohl(sfp->fs_timedelta.nfsv3_sec),
1206                        ntohl(sfp->fs_timedelta.nfsv3_nsec));
1207         }
1208         return (1);
1209 trunc:
1210         return (0);
1211 }
1212
1213 static int
1214 parsepathconf(const u_int32_t *dp)
1215 {
1216         int er;
1217         struct nfsv3_pathconf *spp;
1218
1219         if ((dp = parsestatus(dp, &er)) == NULL)
1220                 return (0);
1221         if (vflag)
1222                 printf(" POST:");
1223         if ((dp = parse_post_op_attr(dp, vflag)) == NULL)
1224                 return (0);
1225         if (er)
1226                 return (1);
1227
1228         spp = (struct nfsv3_pathconf *)dp;
1229         TCHECK(*spp);
1230
1231         printf(" linkmax %lu namemax %lu %s %s %s %s",
1232                ntohl(spp->pc_linkmax),
1233                ntohl(spp->pc_namemax),
1234                ntohl(spp->pc_notrunc) ? "notrunc" : "",
1235                ntohl(spp->pc_chownrestricted) ? "chownres" : "",
1236                ntohl(spp->pc_caseinsensitive) ? "igncase" : "",
1237                ntohl(spp->pc_casepreserving) ? "keepcase" : "");
1238         return (1);
1239 trunc:
1240         return (0);
1241 }
1242                                 
1243 static void
1244 interp_reply(const struct rpc_msg *rp, u_int32_t proc, u_int32_t vers, int length)
1245 {
1246         register const u_int32_t *dp;
1247         register int v3;
1248
1249         int er;
1250
1251         v3 = (vers == NFS_VER3);
1252
1253         if (!v3 && proc < NFS_NPROCS)
1254                 proc = nfsv3_procid[proc];
1255
1256         switch (proc) {
1257
1258         case NFSPROC_NOOP:
1259                 printf(" nop");
1260                 return;
1261
1262         case NFSPROC_NULL:
1263                 printf(" null");
1264                 return;
1265
1266         case NFSPROC_GETATTR:
1267                 printf(" getattr");
1268                 dp = parserep(rp, length);
1269                 if (dp != NULL && parseattrstat(dp, !qflag, v3) != 0)
1270                         return;
1271                 break;
1272
1273         case NFSPROC_SETATTR:
1274                 printf(" setattr");
1275                 if ((dp = parserep(rp, length)) == NULL)
1276                         return;
1277                 if (v3) {
1278                         if (parsewccres(dp, vflag) != 0)
1279                                 return;
1280                 } else {
1281                         if (parseattrstat(dp, !qflag, 0) != 0)
1282                                 return;
1283                 }
1284                 break;
1285
1286         case NFSPROC_LOOKUP:
1287                 printf(" lookup");
1288                 if ((dp = parserep(rp, length)) == NULL)
1289                         break;
1290                 if (v3) {
1291                         if ((dp = parsestatus(dp, &er)) == NULL)
1292                                 break;
1293                         if (er) {
1294                                 if (vflag > 1) {
1295                                         printf(" post dattr:");
1296                                         dp = parse_post_op_attr(dp, vflag);
1297                                 }
1298                         } else {
1299                                 if ((dp = parsefh(dp, v3)) == NULL)
1300                                         break;
1301                                 if (((dp = parse_post_op_attr(dp, vflag)) != NULL) &&
1302                                     (vflag > 1)) {
1303                                         printf(" post dattr:");
1304                                         dp = parse_post_op_attr(dp, vflag);
1305                                 }
1306                         }
1307                         if (dp != NULL)
1308                                 return;
1309                 } else {
1310                         if (parsediropres(dp) != 0)
1311                                 return;
1312                 }
1313                 break;
1314
1315         case NFSPROC_ACCESS:
1316                 printf(" access");
1317                 dp = parserep(rp, length);
1318                 if ((dp = parsestatus(dp, &er)) == NULL)
1319                         break;
1320                 if (vflag)
1321                         printf(" attr:");
1322                 if ((dp = parse_post_op_attr(dp, vflag)) == NULL)
1323                         break;
1324                 if (!er)
1325                         printf(" c %04lx", ntohl(dp[0]));
1326                 return;
1327
1328         case NFSPROC_READLINK:
1329                 printf(" readlink");
1330                 dp = parserep(rp, length);
1331                 if (dp != NULL && parselinkres(dp, v3) != 0)
1332                         return;
1333                 break;
1334
1335         case NFSPROC_READ:
1336                 printf(" read");
1337                 if ((dp = parserep(rp, length)) == NULL)
1338                         break;
1339                 if (v3) {
1340                         if ((dp = parsestatus(dp, &er)) == NULL)
1341                                 break;
1342                         if ((dp = parse_post_op_attr(dp, vflag)) == NULL)
1343                                 break;
1344                         if (er)
1345                                 return;
1346                         if (vflag) {
1347                                 TCHECK2(*dp, 8);
1348                                 printf("%lu bytes", ntohl(dp[0]));
1349                                 if (ntohl(dp[1]))
1350                                         printf(" EOF");
1351                         }
1352                         return;
1353                 } else {
1354                         if (parseattrstat(dp, vflag, 0) != 0)
1355                                 return;
1356                 }
1357                 break;
1358
1359         case NFSPROC_WRITE:
1360                 printf(" write");
1361                 if ((dp = parserep(rp, length)) == NULL)
1362                         break;
1363                 if (v3) {
1364                         if ((dp = parsestatus(dp, &er)) == NULL)
1365                                 break;
1366                         if ((dp = parse_wcc_data(dp, vflag)) == NULL)
1367                                 break;
1368                         if (er)
1369                                 return;
1370                         if (vflag) {
1371                                 TCHECK2(*dp, 4);
1372                                 printf("%lu bytes", ntohl(dp[0]));
1373                                 if (vflag > 1) {
1374                                         TCHECK2(*dp, 4);
1375                                         printf(" <%s>",
1376                                                nfsv3_writemodes[ntohl(dp[1])]);
1377                                 }
1378                                 return;
1379                         }
1380                 } else {
1381                         if (parseattrstat(dp, vflag, v3) != 0)
1382                                 return;
1383                 }
1384                 break;
1385
1386         case NFSPROC_CREATE:
1387                 printf(" create");
1388                 if ((dp = parserep(rp, length)) == NULL)
1389                         break;
1390                 if (v3) {
1391                         if (parsecreateopres(dp, vflag) != NULL)
1392                                 return;
1393                 } else {
1394                         if (parsediropres(dp) != 0)
1395                                 return;
1396                 }
1397                 break;
1398
1399         case NFSPROC_MKDIR:
1400                 printf(" mkdir");
1401                 if ((dp = parserep(rp, length)) == NULL)
1402                         break;
1403                 if (v3) {
1404                         if (parsecreateopres(dp, vflag) != NULL)
1405                                 return;
1406                 } else {
1407                         if (parsediropres(dp) != 0)
1408                                 return;
1409                 }
1410                 break;
1411
1412         case NFSPROC_SYMLINK:
1413                 printf(" symlink");
1414                 if ((dp = parserep(rp, length)) == NULL)
1415                         break;
1416                 if (v3) {
1417                         if (parsecreateopres(dp, vflag) != NULL)
1418                                 return;
1419                 } else {
1420                         if (parsestatus(dp, &er) != NULL)
1421                                 return;
1422                 }
1423                 break;
1424
1425         case NFSPROC_MKNOD:
1426                 printf(" mknod");
1427                 if ((dp = parserep(rp, length)) == NULL)
1428                         break;
1429                 if (parsecreateopres(dp, vflag) != NULL)
1430                         return;
1431                 break;
1432
1433         case NFSPROC_REMOVE:
1434                 printf(" remove");
1435                 if ((dp = parserep(rp, length)) == NULL)
1436                         break;
1437                 if (v3) {
1438                         if (parsewccres(dp, vflag) != 0)
1439                                 return;
1440                 } else {
1441                         if (parsestatus(dp, &er) != NULL)
1442                                 return;
1443                 }
1444                 break;
1445
1446         case NFSPROC_RMDIR:
1447                 printf(" rmdir");
1448                 if ((dp = parserep(rp, length)) == NULL)
1449                         break;
1450                 if (v3) {
1451                         if (parsewccres(dp, vflag) != 0)
1452                                 return;
1453                 } else {
1454                         if (parsestatus(dp, &er) != NULL)
1455                                 return;
1456                 }
1457                 break;
1458
1459         case NFSPROC_RENAME:
1460                 printf(" rename");
1461                 if ((dp = parserep(rp, length)) == NULL)
1462                         break;
1463                 if (v3) {
1464                         if ((dp = parsestatus(dp, &er)) == NULL)
1465                                 break;
1466                         if (vflag) {
1467                                 printf(" from:");
1468                                 if ((dp = parse_wcc_data(dp, vflag)) == NULL)
1469                                         break;
1470                                 printf(" to:");
1471                                 if ((dp = parse_wcc_data(dp, vflag)) == NULL)
1472                                         break;
1473                         }
1474                         return;
1475                 } else {
1476                         if (parsestatus(dp, &er) != NULL)
1477                                 return;
1478                 }
1479                 break;
1480
1481         case NFSPROC_LINK:
1482                 printf(" link");
1483                 if ((dp = parserep(rp, length)) == NULL)
1484                         break;
1485                 if (v3) {
1486                         if ((dp = parsestatus(dp, &er)) == NULL)
1487                                 break;
1488                         if (vflag) {
1489                                 printf(" file POST:");
1490                                 if ((dp = parse_post_op_attr(dp, vflag)) == NULL)
1491                                         break;
1492                                 printf(" dir:");
1493                                 if ((dp = parse_wcc_data(dp, vflag)) == NULL)
1494                                         break;
1495                                 return;
1496                         }
1497                 } else {
1498                         if (parsestatus(dp, &er) != NULL)
1499                                 return;
1500                 }
1501                 break;
1502
1503         case NFSPROC_READDIR:
1504                 printf(" readdir");
1505                 if ((dp = parserep(rp, length)) == NULL)
1506                         break;
1507                 if (v3) {
1508                         if (parsev3rddirres(dp, vflag) != NULL)
1509                                 return;
1510                 } else {
1511                         if (parserddires(dp) != 0)
1512                                 return;
1513                 }
1514                 break;
1515
1516         case NFSPROC_READDIRPLUS:
1517                 printf(" readdirplus");
1518                 if ((dp = parserep(rp, length)) == NULL)
1519                         break;
1520                 if (parsev3rddirres(dp, vflag) != NULL)
1521                         return;
1522                 break;
1523
1524         case NFSPROC_FSSTAT:
1525                 printf(" fsstat");
1526                 dp = parserep(rp, length);
1527                 if (dp != NULL && parsestatfs(dp, v3) != NULL)
1528                         return;
1529                 break;
1530
1531         case NFSPROC_FSINFO:
1532                 printf(" fsinfo");
1533                 dp = parserep(rp, length);
1534                 if (dp != NULL && parsefsinfo(dp) != NULL)
1535                         return;
1536                 break;
1537
1538         case NFSPROC_PATHCONF:
1539                 printf(" pathconf");
1540                 dp = parserep(rp, length);
1541                 if (dp != NULL && parsepathconf(dp) != 0)
1542                         return;
1543                 break;
1544
1545         case NFSPROC_COMMIT:
1546                 printf(" commit");
1547                 dp = parserep(rp, length);
1548                 if (dp != NULL && parsewccres(dp, vflag) != 0)
1549                         return;
1550                 break;
1551
1552         default:
1553                 printf(" proc-%u", proc);
1554                 return;
1555         }
1556
1557 trunc:
1558         if (!nfserr)
1559                 fputs(" [|nfs]", stdout);
1560 }