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