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