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