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