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