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