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