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