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