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