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