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