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