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