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