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