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