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