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