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[4]); 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 ND_PRINT((ndo, " <%s>", 638 tok2str(nfsv3_writemodes, 639 NULL, EXTRACT_32BITS(&dp[3])))); 640 } 641 } else { 642 ND_TCHECK(dp[3]); 643 ND_PRINT((ndo, " %u (%u) bytes @ %u (%u)", 644 EXTRACT_32BITS(&dp[3]), 645 EXTRACT_32BITS(&dp[2]), 646 EXTRACT_32BITS(&dp[1]), 647 EXTRACT_32BITS(&dp[0]))); 648 } 649 return; 650 } 651 break; 652 653 case NFSPROC_SYMLINK: 654 if ((dp = parsereq(ndo, rp, length)) != NULL && 655 (dp = parsefhn(ndo, dp, v3)) != NULL) { 656 ND_PRINT((ndo, " ->")); 657 if (v3 && (dp = parse_sattr3(ndo, dp, &sa3)) == NULL) 658 break; 659 if (parsefn(ndo, dp) == NULL) 660 break; 661 if (v3 && ndo->ndo_vflag) 662 print_sattr3(ndo, &sa3, ndo->ndo_vflag); 663 return; 664 } 665 break; 666 667 case NFSPROC_MKNOD: 668 if ((dp = parsereq(ndo, rp, length)) != NULL && 669 (dp = parsefhn(ndo, dp, v3)) != NULL) { 670 ND_TCHECK(*dp); 671 type = (nfs_type)EXTRACT_32BITS(dp); 672 dp++; 673 if ((dp = parse_sattr3(ndo, dp, &sa3)) == NULL) 674 break; 675 ND_PRINT((ndo, " %s", tok2str(type2str, "unk-ft %d", type))); 676 if (ndo->ndo_vflag && (type == NFCHR || type == NFBLK)) { 677 ND_TCHECK(dp[1]); 678 ND_PRINT((ndo, " %u/%u", 679 EXTRACT_32BITS(&dp[0]), 680 EXTRACT_32BITS(&dp[1]))); 681 dp += 2; 682 } 683 if (ndo->ndo_vflag) 684 print_sattr3(ndo, &sa3, ndo->ndo_vflag); 685 return; 686 } 687 break; 688 689 case NFSPROC_RENAME: 690 if ((dp = parsereq(ndo, rp, length)) != NULL && 691 (dp = parsefhn(ndo, dp, v3)) != NULL) { 692 ND_PRINT((ndo, " ->")); 693 if (parsefhn(ndo, dp, v3) != NULL) 694 return; 695 } 696 break; 697 698 case NFSPROC_LINK: 699 if ((dp = parsereq(ndo, rp, length)) != NULL && 700 (dp = parsefh(ndo, dp, v3)) != NULL) { 701 ND_PRINT((ndo, " ->")); 702 if (parsefhn(ndo, dp, v3) != NULL) 703 return; 704 } 705 break; 706 707 case NFSPROC_READDIR: 708 if ((dp = parsereq(ndo, rp, length)) != NULL && 709 (dp = parsefh(ndo, dp, v3)) != NULL) { 710 if (v3) { 711 ND_TCHECK(dp[4]); 712 /* 713 * We shouldn't really try to interpret the 714 * offset cookie here. 715 */ 716 ND_PRINT((ndo, " %u bytes @ %" PRId64, 717 EXTRACT_32BITS(&dp[4]), 718 EXTRACT_64BITS(&dp[0]))); 719 if (ndo->ndo_vflag) 720 ND_PRINT((ndo, " verf %08x%08x", dp[2], dp[3])); 721 } else { 722 ND_TCHECK(dp[1]); 723 /* 724 * Print the offset as signed, since -1 is 725 * common, but offsets > 2^31 aren't. 726 */ 727 ND_PRINT((ndo, " %u bytes @ %d", 728 EXTRACT_32BITS(&dp[1]), 729 EXTRACT_32BITS(&dp[0]))); 730 } 731 return; 732 } 733 break; 734 735 case NFSPROC_READDIRPLUS: 736 if ((dp = parsereq(ndo, rp, length)) != NULL && 737 (dp = parsefh(ndo, dp, v3)) != NULL) { 738 ND_TCHECK(dp[4]); 739 /* 740 * We don't try to interpret the offset 741 * cookie here. 742 */ 743 ND_PRINT((ndo, " %u bytes @ %" PRId64, 744 EXTRACT_32BITS(&dp[4]), 745 EXTRACT_64BITS(&dp[0]))); 746 if (ndo->ndo_vflag) { 747 ND_TCHECK(dp[5]); 748 ND_PRINT((ndo, " max %u verf %08x%08x", 749 EXTRACT_32BITS(&dp[5]), dp[2], dp[3])); 750 } 751 return; 752 } 753 break; 754 755 case NFSPROC_COMMIT: 756 if ((dp = parsereq(ndo, rp, length)) != NULL && 757 (dp = parsefh(ndo, dp, v3)) != NULL) { 758 ND_TCHECK(dp[2]); 759 ND_PRINT((ndo, " %u bytes @ %" PRIu64, 760 EXTRACT_32BITS(&dp[2]), 761 EXTRACT_64BITS(&dp[0]))); 762 return; 763 } 764 break; 765 766 default: 767 return; 768 } 769 770 trunc: 771 if (!nfserr) 772 ND_PRINT((ndo, "%s", tstr)); 773 } 774 775 /* 776 * Print out an NFS file handle. 777 * We assume packet was not truncated before the end of the 778 * file handle pointed to by dp. 779 * 780 * Note: new version (using portable file-handle parser) doesn't produce 781 * generation number. It probably could be made to do that, with some 782 * additional hacking on the parser code. 783 */ 784 static void 785 nfs_printfh(netdissect_options *ndo, 786 register const uint32_t *dp, const u_int len) 787 { 788 my_fsid fsid; 789 uint32_t ino; 790 const char *sfsname = NULL; 791 char *spacep; 792 793 if (ndo->ndo_uflag) { 794 u_int i; 795 char const *sep = ""; 796 797 ND_PRINT((ndo, " fh[")); 798 for (i=0; i<len; i++) { 799 ND_PRINT((ndo, "%s%x", sep, dp[i])); 800 sep = ":"; 801 } 802 ND_PRINT((ndo, "]")); 803 return; 804 } 805 806 Parse_fh((const u_char *)dp, len, &fsid, &ino, NULL, &sfsname, 0); 807 808 if (sfsname) { 809 /* file system ID is ASCII, not numeric, for this server OS */ 810 char temp[NFSX_V3FHMAX+1]; 811 u_int stringlen; 812 813 /* Make sure string is null-terminated */ 814 stringlen = len; 815 if (stringlen > NFSX_V3FHMAX) 816 stringlen = NFSX_V3FHMAX; 817 strncpy(temp, sfsname, stringlen); 818 temp[stringlen] = '\0'; 819 /* Remove trailing spaces */ 820 spacep = strchr(temp, ' '); 821 if (spacep) 822 *spacep = '\0'; 823 824 ND_PRINT((ndo, " fh %s/", temp)); 825 } else { 826 ND_PRINT((ndo, " fh %d,%d/", 827 fsid.Fsid_dev.Major, fsid.Fsid_dev.Minor)); 828 } 829 830 if(fsid.Fsid_dev.Minor == 257) 831 /* Print the undecoded handle */ 832 ND_PRINT((ndo, "%s", fsid.Opaque_Handle)); 833 else 834 ND_PRINT((ndo, "%ld", (long) ino)); 835 } 836 837 /* 838 * Maintain a small cache of recent client.XID.server/proc pairs, to allow 839 * us to match up replies with requests and thus to know how to parse 840 * the reply. 841 */ 842 843 struct xid_map_entry { 844 uint32_t xid; /* transaction ID (net order) */ 845 int ipver; /* IP version (4 or 6) */ 846 struct in6_addr client; /* client IP address (net order) */ 847 struct in6_addr server; /* server IP address (net order) */ 848 uint32_t proc; /* call proc number (host order) */ 849 uint32_t vers; /* program version (host order) */ 850 }; 851 852 /* 853 * Map entries are kept in an array that we manage as a ring; 854 * new entries are always added at the tail of the ring. Initially, 855 * all the entries are zero and hence don't match anything. 856 */ 857 858 #define XIDMAPSIZE 64 859 860 static struct xid_map_entry xid_map[XIDMAPSIZE]; 861 862 static int xid_map_next = 0; 863 static int xid_map_hint = 0; 864 865 static int 866 xid_map_enter(netdissect_options *ndo, 867 const struct sunrpc_msg *rp, const u_char *bp) 868 { 869 const struct ip *ip = NULL; 870 const struct ip6_hdr *ip6 = NULL; 871 struct xid_map_entry *xmep; 872 873 if (!ND_TTEST(rp->rm_call.cb_proc)) 874 return (0); 875 switch (IP_V((const struct ip *)bp)) { 876 case 4: 877 ip = (const struct ip *)bp; 878 break; 879 case 6: 880 ip6 = (const struct ip6_hdr *)bp; 881 break; 882 default: 883 return (1); 884 } 885 886 xmep = &xid_map[xid_map_next]; 887 888 if (++xid_map_next >= XIDMAPSIZE) 889 xid_map_next = 0; 890 891 UNALIGNED_MEMCPY(&xmep->xid, &rp->rm_xid, sizeof(xmep->xid)); 892 if (ip) { 893 xmep->ipver = 4; 894 UNALIGNED_MEMCPY(&xmep->client, &ip->ip_src, sizeof(ip->ip_src)); 895 UNALIGNED_MEMCPY(&xmep->server, &ip->ip_dst, sizeof(ip->ip_dst)); 896 } 897 else if (ip6) { 898 xmep->ipver = 6; 899 UNALIGNED_MEMCPY(&xmep->client, &ip6->ip6_src, sizeof(ip6->ip6_src)); 900 UNALIGNED_MEMCPY(&xmep->server, &ip6->ip6_dst, sizeof(ip6->ip6_dst)); 901 } 902 xmep->proc = EXTRACT_32BITS(&rp->rm_call.cb_proc); 903 xmep->vers = EXTRACT_32BITS(&rp->rm_call.cb_vers); 904 return (1); 905 } 906 907 /* 908 * Returns 0 and puts NFSPROC_xxx in proc return and 909 * version in vers return, or returns -1 on failure 910 */ 911 static int 912 xid_map_find(const struct sunrpc_msg *rp, const u_char *bp, uint32_t *proc, 913 uint32_t *vers) 914 { 915 int i; 916 struct xid_map_entry *xmep; 917 uint32_t xid; 918 const struct ip *ip = (const struct ip *)bp; 919 const struct ip6_hdr *ip6 = (const struct ip6_hdr *)bp; 920 int cmp; 921 922 UNALIGNED_MEMCPY(&xid, &rp->rm_xid, sizeof(xmep->xid)); 923 /* Start searching from where we last left off */ 924 i = xid_map_hint; 925 do { 926 xmep = &xid_map[i]; 927 cmp = 1; 928 if (xmep->ipver != IP_V(ip) || xmep->xid != xid) 929 goto nextitem; 930 switch (xmep->ipver) { 931 case 4: 932 if (UNALIGNED_MEMCMP(&ip->ip_src, &xmep->server, 933 sizeof(ip->ip_src)) != 0 || 934 UNALIGNED_MEMCMP(&ip->ip_dst, &xmep->client, 935 sizeof(ip->ip_dst)) != 0) { 936 cmp = 0; 937 } 938 break; 939 case 6: 940 if (UNALIGNED_MEMCMP(&ip6->ip6_src, &xmep->server, 941 sizeof(ip6->ip6_src)) != 0 || 942 UNALIGNED_MEMCMP(&ip6->ip6_dst, &xmep->client, 943 sizeof(ip6->ip6_dst)) != 0) { 944 cmp = 0; 945 } 946 break; 947 default: 948 cmp = 0; 949 break; 950 } 951 if (cmp) { 952 /* match */ 953 xid_map_hint = i; 954 *proc = xmep->proc; 955 *vers = xmep->vers; 956 return 0; 957 } 958 nextitem: 959 if (++i >= XIDMAPSIZE) 960 i = 0; 961 } while (i != xid_map_hint); 962 963 /* search failed */ 964 return (-1); 965 } 966 967 /* 968 * Routines for parsing reply packets 969 */ 970 971 /* 972 * Return a pointer to the beginning of the actual results. 973 * If the packet was truncated, return 0. 974 */ 975 static const uint32_t * 976 parserep(netdissect_options *ndo, 977 register const struct sunrpc_msg *rp, register u_int length) 978 { 979 register const uint32_t *dp; 980 u_int len; 981 enum sunrpc_accept_stat astat; 982 983 /* 984 * Portability note: 985 * Here we find the address of the ar_verf credentials. 986 * Originally, this calculation was 987 * dp = (uint32_t *)&rp->rm_reply.rp_acpt.ar_verf 988 * On the wire, the rp_acpt field starts immediately after 989 * the (32 bit) rp_stat field. However, rp_acpt (which is a 990 * "struct accepted_reply") contains a "struct opaque_auth", 991 * whose internal representation contains a pointer, so on a 992 * 64-bit machine the compiler inserts 32 bits of padding 993 * before rp->rm_reply.rp_acpt.ar_verf. So, we cannot use 994 * the internal representation to parse the on-the-wire 995 * representation. Instead, we skip past the rp_stat field, 996 * which is an "enum" and so occupies one 32-bit word. 997 */ 998 dp = ((const uint32_t *)&rp->rm_reply) + 1; 999 ND_TCHECK(dp[1]); 1000 len = EXTRACT_32BITS(&dp[1]); 1001 if (len >= length) 1002 return (NULL); 1003 /* 1004 * skip past the ar_verf credentials. 1005 */ 1006 dp += (len + (2*sizeof(uint32_t) + 3)) / sizeof(uint32_t); 1007 1008 /* 1009 * now we can check the ar_stat field 1010 */ 1011 ND_TCHECK(dp[0]); 1012 astat = (enum sunrpc_accept_stat) EXTRACT_32BITS(dp); 1013 if (astat != SUNRPC_SUCCESS) { 1014 ND_PRINT((ndo, " %s", tok2str(sunrpc_str, "ar_stat %d", astat))); 1015 nfserr = 1; /* suppress trunc string */ 1016 return (NULL); 1017 } 1018 /* successful return */ 1019 ND_TCHECK2(*dp, sizeof(astat)); 1020 return ((const uint32_t *) (sizeof(astat) + ((const char *)dp))); 1021 trunc: 1022 return (0); 1023 } 1024 1025 static const uint32_t * 1026 parsestatus(netdissect_options *ndo, 1027 const uint32_t *dp, int *er) 1028 { 1029 int errnum; 1030 1031 ND_TCHECK(dp[0]); 1032 1033 errnum = EXTRACT_32BITS(&dp[0]); 1034 if (er) 1035 *er = errnum; 1036 if (errnum != 0) { 1037 if (!ndo->ndo_qflag) 1038 ND_PRINT((ndo, " ERROR: %s", 1039 tok2str(status2str, "unk %d", errnum))); 1040 nfserr = 1; 1041 } 1042 return (dp + 1); 1043 trunc: 1044 return NULL; 1045 } 1046 1047 static const uint32_t * 1048 parsefattr(netdissect_options *ndo, 1049 const uint32_t *dp, int verbose, int v3) 1050 { 1051 const struct nfs_fattr *fap; 1052 1053 fap = (const struct nfs_fattr *)dp; 1054 ND_TCHECK(fap->fa_gid); 1055 if (verbose) { 1056 ND_PRINT((ndo, " %s %o ids %d/%d", 1057 tok2str(type2str, "unk-ft %d ", 1058 EXTRACT_32BITS(&fap->fa_type)), 1059 EXTRACT_32BITS(&fap->fa_mode), 1060 EXTRACT_32BITS(&fap->fa_uid), 1061 EXTRACT_32BITS(&fap->fa_gid))); 1062 if (v3) { 1063 ND_TCHECK(fap->fa3_size); 1064 ND_PRINT((ndo, " sz %" PRIu64, 1065 EXTRACT_64BITS((const uint32_t *)&fap->fa3_size))); 1066 } else { 1067 ND_TCHECK(fap->fa2_size); 1068 ND_PRINT((ndo, " sz %d", EXTRACT_32BITS(&fap->fa2_size))); 1069 } 1070 } 1071 /* print lots more stuff */ 1072 if (verbose > 1) { 1073 if (v3) { 1074 ND_TCHECK(fap->fa3_ctime); 1075 ND_PRINT((ndo, " nlink %d rdev %d/%d", 1076 EXTRACT_32BITS(&fap->fa_nlink), 1077 EXTRACT_32BITS(&fap->fa3_rdev.specdata1), 1078 EXTRACT_32BITS(&fap->fa3_rdev.specdata2))); 1079 ND_PRINT((ndo, " fsid %" PRIx64, 1080 EXTRACT_64BITS((const uint32_t *)&fap->fa3_fsid))); 1081 ND_PRINT((ndo, " fileid %" PRIx64, 1082 EXTRACT_64BITS((const uint32_t *)&fap->fa3_fileid))); 1083 ND_PRINT((ndo, " a/m/ctime %u.%06u", 1084 EXTRACT_32BITS(&fap->fa3_atime.nfsv3_sec), 1085 EXTRACT_32BITS(&fap->fa3_atime.nfsv3_nsec))); 1086 ND_PRINT((ndo, " %u.%06u", 1087 EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_sec), 1088 EXTRACT_32BITS(&fap->fa3_mtime.nfsv3_nsec))); 1089 ND_PRINT((ndo, " %u.%06u", 1090 EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_sec), 1091 EXTRACT_32BITS(&fap->fa3_ctime.nfsv3_nsec))); 1092 } else { 1093 ND_TCHECK(fap->fa2_ctime); 1094 ND_PRINT((ndo, " nlink %d rdev 0x%x fsid 0x%x nodeid 0x%x a/m/ctime", 1095 EXTRACT_32BITS(&fap->fa_nlink), 1096 EXTRACT_32BITS(&fap->fa2_rdev), 1097 EXTRACT_32BITS(&fap->fa2_fsid), 1098 EXTRACT_32BITS(&fap->fa2_fileid))); 1099 ND_PRINT((ndo, " %u.%06u", 1100 EXTRACT_32BITS(&fap->fa2_atime.nfsv2_sec), 1101 EXTRACT_32BITS(&fap->fa2_atime.nfsv2_usec))); 1102 ND_PRINT((ndo, " %u.%06u", 1103 EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_sec), 1104 EXTRACT_32BITS(&fap->fa2_mtime.nfsv2_usec))); 1105 ND_PRINT((ndo, " %u.%06u", 1106 EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_sec), 1107 EXTRACT_32BITS(&fap->fa2_ctime.nfsv2_usec))); 1108 } 1109 } 1110 return ((const uint32_t *)((const unsigned char *)dp + 1111 (v3 ? NFSX_V3FATTR : NFSX_V2FATTR))); 1112 trunc: 1113 return (NULL); 1114 } 1115 1116 static int 1117 parseattrstat(netdissect_options *ndo, 1118 const uint32_t *dp, int verbose, int v3) 1119 { 1120 int er; 1121 1122 dp = parsestatus(ndo, dp, &er); 1123 if (dp == NULL) 1124 return (0); 1125 if (er) 1126 return (1); 1127 1128 return (parsefattr(ndo, dp, verbose, v3) != NULL); 1129 } 1130 1131 static int 1132 parsediropres(netdissect_options *ndo, 1133 const uint32_t *dp) 1134 { 1135 int er; 1136 1137 if (!(dp = parsestatus(ndo, dp, &er))) 1138 return (0); 1139 if (er) 1140 return (1); 1141 1142 dp = parsefh(ndo, dp, 0); 1143 if (dp == NULL) 1144 return (0); 1145 1146 return (parsefattr(ndo, dp, ndo->ndo_vflag, 0) != NULL); 1147 } 1148 1149 static int 1150 parselinkres(netdissect_options *ndo, 1151 const uint32_t *dp, int v3) 1152 { 1153 int er; 1154 1155 dp = parsestatus(ndo, dp, &er); 1156 if (dp == NULL) 1157 return(0); 1158 if (er) 1159 return(1); 1160 if (v3 && !(dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag))) 1161 return (0); 1162 ND_PRINT((ndo, " ")); 1163 return (parsefn(ndo, dp) != NULL); 1164 } 1165 1166 static int 1167 parsestatfs(netdissect_options *ndo, 1168 const uint32_t *dp, int v3) 1169 { 1170 const struct nfs_statfs *sfsp; 1171 int er; 1172 1173 dp = parsestatus(ndo, dp, &er); 1174 if (dp == NULL) 1175 return (0); 1176 if (!v3 && er) 1177 return (1); 1178 1179 if (ndo->ndo_qflag) 1180 return(1); 1181 1182 if (v3) { 1183 if (ndo->ndo_vflag) 1184 ND_PRINT((ndo, " POST:")); 1185 if (!(dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag))) 1186 return (0); 1187 } 1188 1189 ND_TCHECK2(*dp, (v3 ? NFSX_V3STATFS : NFSX_V2STATFS)); 1190 1191 sfsp = (const struct nfs_statfs *)dp; 1192 1193 if (v3) { 1194 ND_PRINT((ndo, " tbytes %" PRIu64 " fbytes %" PRIu64 " abytes %" PRIu64, 1195 EXTRACT_64BITS((const uint32_t *)&sfsp->sf_tbytes), 1196 EXTRACT_64BITS((const uint32_t *)&sfsp->sf_fbytes), 1197 EXTRACT_64BITS((const uint32_t *)&sfsp->sf_abytes))); 1198 if (ndo->ndo_vflag) { 1199 ND_PRINT((ndo, " tfiles %" PRIu64 " ffiles %" PRIu64 " afiles %" PRIu64 " invar %u", 1200 EXTRACT_64BITS((const uint32_t *)&sfsp->sf_tfiles), 1201 EXTRACT_64BITS((const uint32_t *)&sfsp->sf_ffiles), 1202 EXTRACT_64BITS((const uint32_t *)&sfsp->sf_afiles), 1203 EXTRACT_32BITS(&sfsp->sf_invarsec))); 1204 } 1205 } else { 1206 ND_PRINT((ndo, " tsize %d bsize %d blocks %d bfree %d bavail %d", 1207 EXTRACT_32BITS(&sfsp->sf_tsize), 1208 EXTRACT_32BITS(&sfsp->sf_bsize), 1209 EXTRACT_32BITS(&sfsp->sf_blocks), 1210 EXTRACT_32BITS(&sfsp->sf_bfree), 1211 EXTRACT_32BITS(&sfsp->sf_bavail))); 1212 } 1213 1214 return (1); 1215 trunc: 1216 return (0); 1217 } 1218 1219 static int 1220 parserddires(netdissect_options *ndo, 1221 const uint32_t *dp) 1222 { 1223 int er; 1224 1225 dp = parsestatus(ndo, dp, &er); 1226 if (dp == NULL) 1227 return (0); 1228 if (er) 1229 return (1); 1230 if (ndo->ndo_qflag) 1231 return (1); 1232 1233 ND_TCHECK(dp[2]); 1234 ND_PRINT((ndo, " offset 0x%x size %d ", 1235 EXTRACT_32BITS(&dp[0]), EXTRACT_32BITS(&dp[1]))); 1236 if (dp[2] != 0) 1237 ND_PRINT((ndo, " eof")); 1238 1239 return (1); 1240 trunc: 1241 return (0); 1242 } 1243 1244 static const uint32_t * 1245 parse_wcc_attr(netdissect_options *ndo, 1246 const uint32_t *dp) 1247 { 1248 /* Our caller has already checked this */ 1249 ND_PRINT((ndo, " sz %" PRIu64, EXTRACT_64BITS(&dp[0]))); 1250 ND_PRINT((ndo, " mtime %u.%06u ctime %u.%06u", 1251 EXTRACT_32BITS(&dp[2]), EXTRACT_32BITS(&dp[3]), 1252 EXTRACT_32BITS(&dp[4]), EXTRACT_32BITS(&dp[5]))); 1253 return (dp + 6); 1254 } 1255 1256 /* 1257 * Pre operation attributes. Print only if vflag > 1. 1258 */ 1259 static const uint32_t * 1260 parse_pre_op_attr(netdissect_options *ndo, 1261 const uint32_t *dp, int verbose) 1262 { 1263 ND_TCHECK(dp[0]); 1264 if (!EXTRACT_32BITS(&dp[0])) 1265 return (dp + 1); 1266 dp++; 1267 ND_TCHECK2(*dp, 24); 1268 if (verbose > 1) { 1269 return parse_wcc_attr(ndo, dp); 1270 } else { 1271 /* If not verbose enough, just skip over wcc_attr */ 1272 return (dp + 6); 1273 } 1274 trunc: 1275 return (NULL); 1276 } 1277 1278 /* 1279 * Post operation attributes are printed if vflag >= 1 1280 */ 1281 static const uint32_t * 1282 parse_post_op_attr(netdissect_options *ndo, 1283 const uint32_t *dp, int verbose) 1284 { 1285 ND_TCHECK(dp[0]); 1286 if (!EXTRACT_32BITS(&dp[0])) 1287 return (dp + 1); 1288 dp++; 1289 if (verbose) { 1290 return parsefattr(ndo, dp, verbose, 1); 1291 } else 1292 return (dp + (NFSX_V3FATTR / sizeof (uint32_t))); 1293 trunc: 1294 return (NULL); 1295 } 1296 1297 static const uint32_t * 1298 parse_wcc_data(netdissect_options *ndo, 1299 const uint32_t *dp, int verbose) 1300 { 1301 if (verbose > 1) 1302 ND_PRINT((ndo, " PRE:")); 1303 if (!(dp = parse_pre_op_attr(ndo, dp, verbose))) 1304 return (0); 1305 1306 if (verbose) 1307 ND_PRINT((ndo, " POST:")); 1308 return parse_post_op_attr(ndo, dp, verbose); 1309 } 1310 1311 static const uint32_t * 1312 parsecreateopres(netdissect_options *ndo, 1313 const uint32_t *dp, int verbose) 1314 { 1315 int er; 1316 1317 if (!(dp = parsestatus(ndo, dp, &er))) 1318 return (0); 1319 if (er) 1320 dp = parse_wcc_data(ndo, dp, verbose); 1321 else { 1322 ND_TCHECK(dp[0]); 1323 if (!EXTRACT_32BITS(&dp[0])) 1324 return (dp + 1); 1325 dp++; 1326 if (!(dp = parsefh(ndo, dp, 1))) 1327 return (0); 1328 if (verbose) { 1329 if (!(dp = parse_post_op_attr(ndo, dp, verbose))) 1330 return (0); 1331 if (ndo->ndo_vflag > 1) { 1332 ND_PRINT((ndo, " dir attr:")); 1333 dp = parse_wcc_data(ndo, dp, verbose); 1334 } 1335 } 1336 } 1337 return (dp); 1338 trunc: 1339 return (NULL); 1340 } 1341 1342 static int 1343 parsewccres(netdissect_options *ndo, 1344 const uint32_t *dp, int verbose) 1345 { 1346 int er; 1347 1348 if (!(dp = parsestatus(ndo, dp, &er))) 1349 return (0); 1350 return parse_wcc_data(ndo, dp, verbose) != NULL; 1351 } 1352 1353 static const uint32_t * 1354 parsev3rddirres(netdissect_options *ndo, 1355 const uint32_t *dp, int verbose) 1356 { 1357 int er; 1358 1359 if (!(dp = parsestatus(ndo, dp, &er))) 1360 return (0); 1361 if (ndo->ndo_vflag) 1362 ND_PRINT((ndo, " POST:")); 1363 if (!(dp = parse_post_op_attr(ndo, dp, verbose))) 1364 return (0); 1365 if (er) 1366 return dp; 1367 if (ndo->ndo_vflag) { 1368 ND_TCHECK(dp[1]); 1369 ND_PRINT((ndo, " verf %08x%08x", dp[0], dp[1])); 1370 dp += 2; 1371 } 1372 return dp; 1373 trunc: 1374 return (NULL); 1375 } 1376 1377 static int 1378 parsefsinfo(netdissect_options *ndo, 1379 const uint32_t *dp) 1380 { 1381 const struct nfsv3_fsinfo *sfp; 1382 int er; 1383 1384 if (!(dp = parsestatus(ndo, dp, &er))) 1385 return (0); 1386 if (ndo->ndo_vflag) 1387 ND_PRINT((ndo, " POST:")); 1388 if (!(dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag))) 1389 return (0); 1390 if (er) 1391 return (1); 1392 1393 sfp = (const struct nfsv3_fsinfo *)dp; 1394 ND_TCHECK(*sfp); 1395 ND_PRINT((ndo, " rtmax %u rtpref %u wtmax %u wtpref %u dtpref %u", 1396 EXTRACT_32BITS(&sfp->fs_rtmax), 1397 EXTRACT_32BITS(&sfp->fs_rtpref), 1398 EXTRACT_32BITS(&sfp->fs_wtmax), 1399 EXTRACT_32BITS(&sfp->fs_wtpref), 1400 EXTRACT_32BITS(&sfp->fs_dtpref))); 1401 if (ndo->ndo_vflag) { 1402 ND_PRINT((ndo, " rtmult %u wtmult %u maxfsz %" PRIu64, 1403 EXTRACT_32BITS(&sfp->fs_rtmult), 1404 EXTRACT_32BITS(&sfp->fs_wtmult), 1405 EXTRACT_64BITS((const uint32_t *)&sfp->fs_maxfilesize))); 1406 ND_PRINT((ndo, " delta %u.%06u ", 1407 EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_sec), 1408 EXTRACT_32BITS(&sfp->fs_timedelta.nfsv3_nsec))); 1409 } 1410 return (1); 1411 trunc: 1412 return (0); 1413 } 1414 1415 static int 1416 parsepathconf(netdissect_options *ndo, 1417 const uint32_t *dp) 1418 { 1419 int er; 1420 const struct nfsv3_pathconf *spp; 1421 1422 if (!(dp = parsestatus(ndo, dp, &er))) 1423 return (0); 1424 if (ndo->ndo_vflag) 1425 ND_PRINT((ndo, " POST:")); 1426 if (!(dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag))) 1427 return (0); 1428 if (er) 1429 return (1); 1430 1431 spp = (const struct nfsv3_pathconf *)dp; 1432 ND_TCHECK(*spp); 1433 1434 ND_PRINT((ndo, " linkmax %u namemax %u %s %s %s %s", 1435 EXTRACT_32BITS(&spp->pc_linkmax), 1436 EXTRACT_32BITS(&spp->pc_namemax), 1437 EXTRACT_32BITS(&spp->pc_notrunc) ? "notrunc" : "", 1438 EXTRACT_32BITS(&spp->pc_chownrestricted) ? "chownres" : "", 1439 EXTRACT_32BITS(&spp->pc_caseinsensitive) ? "igncase" : "", 1440 EXTRACT_32BITS(&spp->pc_casepreserving) ? "keepcase" : "")); 1441 return (1); 1442 trunc: 1443 return (0); 1444 } 1445 1446 static void 1447 interp_reply(netdissect_options *ndo, 1448 const struct sunrpc_msg *rp, uint32_t proc, uint32_t vers, int length) 1449 { 1450 register const uint32_t *dp; 1451 register int v3; 1452 int er; 1453 1454 v3 = (vers == NFS_VER3); 1455 1456 if (!v3 && proc < NFS_NPROCS) 1457 proc = nfsv3_procid[proc]; 1458 1459 ND_PRINT((ndo, " %s", tok2str(nfsproc_str, "proc-%u", proc))); 1460 switch (proc) { 1461 1462 case NFSPROC_GETATTR: 1463 dp = parserep(ndo, rp, length); 1464 if (dp != NULL && parseattrstat(ndo, dp, !ndo->ndo_qflag, v3) != 0) 1465 return; 1466 break; 1467 1468 case NFSPROC_SETATTR: 1469 if (!(dp = parserep(ndo, rp, length))) 1470 return; 1471 if (v3) { 1472 if (parsewccres(ndo, dp, ndo->ndo_vflag)) 1473 return; 1474 } else { 1475 if (parseattrstat(ndo, dp, !ndo->ndo_qflag, 0) != 0) 1476 return; 1477 } 1478 break; 1479 1480 case NFSPROC_LOOKUP: 1481 if (!(dp = parserep(ndo, rp, length))) 1482 break; 1483 if (v3) { 1484 if (!(dp = parsestatus(ndo, dp, &er))) 1485 break; 1486 if (er) { 1487 if (ndo->ndo_vflag > 1) { 1488 ND_PRINT((ndo, " post dattr:")); 1489 dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag); 1490 } 1491 } else { 1492 if (!(dp = parsefh(ndo, dp, v3))) 1493 break; 1494 if ((dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag)) && 1495 ndo->ndo_vflag > 1) { 1496 ND_PRINT((ndo, " post dattr:")); 1497 dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag); 1498 } 1499 } 1500 if (dp) 1501 return; 1502 } else { 1503 if (parsediropres(ndo, dp) != 0) 1504 return; 1505 } 1506 break; 1507 1508 case NFSPROC_ACCESS: 1509 if (!(dp = parserep(ndo, rp, length))) 1510 break; 1511 if (!(dp = parsestatus(ndo, dp, &er))) 1512 break; 1513 if (ndo->ndo_vflag) 1514 ND_PRINT((ndo, " attr:")); 1515 if (!(dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag))) 1516 break; 1517 if (!er) { 1518 ND_TCHECK(dp[0]); 1519 ND_PRINT((ndo, " c %04x", EXTRACT_32BITS(&dp[0]))); 1520 } 1521 return; 1522 1523 case NFSPROC_READLINK: 1524 dp = parserep(ndo, rp, length); 1525 if (dp != NULL && parselinkres(ndo, dp, v3) != 0) 1526 return; 1527 break; 1528 1529 case NFSPROC_READ: 1530 if (!(dp = parserep(ndo, rp, length))) 1531 break; 1532 if (v3) { 1533 if (!(dp = parsestatus(ndo, dp, &er))) 1534 break; 1535 if (!(dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag))) 1536 break; 1537 if (er) 1538 return; 1539 if (ndo->ndo_vflag) { 1540 ND_TCHECK(dp[1]); 1541 ND_PRINT((ndo, " %u bytes", EXTRACT_32BITS(&dp[0]))); 1542 if (EXTRACT_32BITS(&dp[1])) 1543 ND_PRINT((ndo, " EOF")); 1544 } 1545 return; 1546 } else { 1547 if (parseattrstat(ndo, dp, ndo->ndo_vflag, 0) != 0) 1548 return; 1549 } 1550 break; 1551 1552 case NFSPROC_WRITE: 1553 if (!(dp = parserep(ndo, rp, length))) 1554 break; 1555 if (v3) { 1556 if (!(dp = parsestatus(ndo, dp, &er))) 1557 break; 1558 if (!(dp = parse_wcc_data(ndo, dp, ndo->ndo_vflag))) 1559 break; 1560 if (er) 1561 return; 1562 if (ndo->ndo_vflag) { 1563 ND_TCHECK(dp[0]); 1564 ND_PRINT((ndo, " %u bytes", EXTRACT_32BITS(&dp[0]))); 1565 if (ndo->ndo_vflag > 1) { 1566 ND_TCHECK(dp[1]); 1567 ND_PRINT((ndo, " <%s>", 1568 tok2str(nfsv3_writemodes, 1569 NULL, EXTRACT_32BITS(&dp[1])))); 1570 } 1571 } 1572 return; 1573 } else { 1574 if (parseattrstat(ndo, dp, ndo->ndo_vflag, v3) != 0) 1575 return; 1576 } 1577 break; 1578 1579 case NFSPROC_CREATE: 1580 case NFSPROC_MKDIR: 1581 if (!(dp = parserep(ndo, rp, length))) 1582 break; 1583 if (v3) { 1584 if (parsecreateopres(ndo, dp, ndo->ndo_vflag) != NULL) 1585 return; 1586 } else { 1587 if (parsediropres(ndo, dp) != 0) 1588 return; 1589 } 1590 break; 1591 1592 case NFSPROC_SYMLINK: 1593 if (!(dp = parserep(ndo, rp, length))) 1594 break; 1595 if (v3) { 1596 if (parsecreateopres(ndo, dp, ndo->ndo_vflag) != NULL) 1597 return; 1598 } else { 1599 if (parsestatus(ndo, dp, &er) != NULL) 1600 return; 1601 } 1602 break; 1603 1604 case NFSPROC_MKNOD: 1605 if (!(dp = parserep(ndo, rp, length))) 1606 break; 1607 if (parsecreateopres(ndo, dp, ndo->ndo_vflag) != NULL) 1608 return; 1609 break; 1610 1611 case NFSPROC_REMOVE: 1612 case NFSPROC_RMDIR: 1613 if (!(dp = parserep(ndo, rp, length))) 1614 break; 1615 if (v3) { 1616 if (parsewccres(ndo, dp, ndo->ndo_vflag)) 1617 return; 1618 } else { 1619 if (parsestatus(ndo, dp, &er) != NULL) 1620 return; 1621 } 1622 break; 1623 1624 case NFSPROC_RENAME: 1625 if (!(dp = parserep(ndo, rp, length))) 1626 break; 1627 if (v3) { 1628 if (!(dp = parsestatus(ndo, dp, &er))) 1629 break; 1630 if (ndo->ndo_vflag) { 1631 ND_PRINT((ndo, " from:")); 1632 if (!(dp = parse_wcc_data(ndo, dp, ndo->ndo_vflag))) 1633 break; 1634 ND_PRINT((ndo, " to:")); 1635 if (!(dp = parse_wcc_data(ndo, dp, ndo->ndo_vflag))) 1636 break; 1637 } 1638 return; 1639 } else { 1640 if (parsestatus(ndo, dp, &er) != NULL) 1641 return; 1642 } 1643 break; 1644 1645 case NFSPROC_LINK: 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, " file POST:")); 1653 if (!(dp = parse_post_op_attr(ndo, dp, ndo->ndo_vflag))) 1654 break; 1655 ND_PRINT((ndo, " dir:")); 1656 if (!(dp = parse_wcc_data(ndo, dp, ndo->ndo_vflag))) 1657 break; 1658 } 1659 return; 1660 } else { 1661 if (parsestatus(ndo, dp, &er) != NULL) 1662 return; 1663 } 1664 break; 1665 1666 case NFSPROC_READDIR: 1667 if (!(dp = parserep(ndo, rp, length))) 1668 break; 1669 if (v3) { 1670 if (parsev3rddirres(ndo, dp, ndo->ndo_vflag)) 1671 return; 1672 } else { 1673 if (parserddires(ndo, dp) != 0) 1674 return; 1675 } 1676 break; 1677 1678 case NFSPROC_READDIRPLUS: 1679 if (!(dp = parserep(ndo, rp, length))) 1680 break; 1681 if (parsev3rddirres(ndo, dp, ndo->ndo_vflag)) 1682 return; 1683 break; 1684 1685 case NFSPROC_FSSTAT: 1686 dp = parserep(ndo, rp, length); 1687 if (dp != NULL && parsestatfs(ndo, dp, v3) != 0) 1688 return; 1689 break; 1690 1691 case NFSPROC_FSINFO: 1692 dp = parserep(ndo, rp, length); 1693 if (dp != NULL && parsefsinfo(ndo, dp) != 0) 1694 return; 1695 break; 1696 1697 case NFSPROC_PATHCONF: 1698 dp = parserep(ndo, rp, length); 1699 if (dp != NULL && parsepathconf(ndo, dp) != 0) 1700 return; 1701 break; 1702 1703 case NFSPROC_COMMIT: 1704 dp = parserep(ndo, rp, length); 1705 if (dp != NULL && parsewccres(ndo, dp, ndo->ndo_vflag) != 0) 1706 return; 1707 break; 1708 1709 default: 1710 return; 1711 } 1712 trunc: 1713 if (!nfserr) 1714 ND_PRINT((ndo, "%s", tstr)); 1715 } 1716