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