1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * Copyright 2018 Nexenta Systems, Inc. All rights reserved. 27 */ 28 29 30 #include <sys/mdb_modapi.h> 31 #include <mdb/mdb_ctf.h> 32 #include <sys/types.h> 33 #include <sys/socket.h> 34 35 #include <netsmb/smb_conn.h> 36 #include <netsmb/smb_rq.h> 37 #include <netsmb/smb_pass.h> 38 39 #ifdef _KERNEL 40 #define NSMB_OBJNAME "nsmb" 41 #else 42 #define NSMB_OBJNAME "libfknsmb.so.1" 43 #endif 44 45 #define OPT_VERBOSE 0x0001 /* Be [-v]erbose in dcmd's */ 46 #define OPT_RECURSE 0x0002 /* recursive display */ 47 48 /* 49 * We need to read in a private copy 50 * of every string we want to print out. 51 */ 52 void 53 print_str(uintptr_t addr) 54 { 55 char buf[32]; 56 int len, mx = sizeof (buf) - 4; 57 58 if ((len = mdb_readstr(buf, sizeof (buf), addr)) <= 0) { 59 mdb_printf(" (%p)", addr); 60 } else { 61 if (len > mx) 62 strcpy(&buf[mx], "..."); 63 mdb_printf(" %s", buf); 64 } 65 } 66 67 68 /* 69 * Walker for smb_connobj_t structures, including 70 * smb_vc_t and smb_share_t which "inherit" from it. 71 * Tricky: Exploit the "inheritance" of smb_connobj_t 72 * with common functions for walk_init, walk_next. 73 */ 74 typedef struct smb_co_walk_data { 75 uintptr_t pp; 76 int level; /* SMBL_SM, SMBL_VC, SMBL_SHARE, ... */ 77 int size; /* sizeof (union member) */ 78 union co_u { 79 smb_connobj_t co; /* copy of the list element */ 80 smb_vc_t vc; 81 smb_share_t ss; 82 smb_fh_t fh; 83 } u; 84 } smb_co_walk_data_t; 85 86 /* 87 * Common walk_init for walking structs inherited 88 * from smb_connobj_t (smb_vc_t, smb_share_t) 89 */ 90 int 91 smb_co_walk_init(mdb_walk_state_t *wsp, int level) 92 { 93 smb_co_walk_data_t *smbw; 94 size_t psz; 95 96 if (wsp->walk_addr == 0) 97 return (WALK_ERR); 98 99 smbw = mdb_alloc(sizeof (*smbw), UM_SLEEP | UM_GC); 100 wsp->walk_data = smbw; 101 102 /* 103 * Save the parent pointer for later checks, and 104 * the level so we know which union member it is. 105 * Also the size of this union member. 106 */ 107 smbw->pp = wsp->walk_addr; 108 smbw->level = level; 109 switch (level) { 110 case SMBL_SM: 111 smbw->size = sizeof (smbw->u.co); 112 break; 113 case SMBL_VC: 114 smbw->size = sizeof (smbw->u.vc); 115 break; 116 case SMBL_SHARE: 117 smbw->size = sizeof (smbw->u.ss); 118 break; 119 case SMBL_FH: 120 smbw->size = sizeof (smbw->u.fh); 121 break; 122 default: 123 smbw->size = sizeof (smbw->u); 124 break; 125 } 126 127 /* 128 * Read in the parent object. Just need the 129 * invariant part (smb_connobj_t) so we can 130 * get the list of children below it. 131 */ 132 psz = sizeof (smbw->u.co); 133 if (mdb_vread(&smbw->u.co, psz, smbw->pp) != psz) { 134 mdb_warn("cannot read connobj from %p", smbw->pp); 135 return (WALK_ERR); 136 } 137 138 /* 139 * Finally, setup to walk the list of children. 140 */ 141 wsp->walk_addr = (uintptr_t)smbw->u.co.co_children.slh_first; 142 143 return (WALK_NEXT); 144 } 145 146 /* 147 * Walk the (global) VC list. 148 */ 149 int 150 smb_vc_walk_init(mdb_walk_state_t *wsp) 151 { 152 GElf_Sym sym; 153 154 if (wsp->walk_addr != 0) { 155 mdb_warn("::walk smb_vc only supports global walks\n"); 156 return (WALK_ERR); 157 } 158 159 /* Locate the VC list head. */ 160 if (mdb_lookup_by_obj(NSMB_OBJNAME, "smb_vclist", &sym)) { 161 mdb_warn("failed to lookup `smb_vclist'\n"); 162 return (WALK_ERR); 163 } 164 wsp->walk_addr = sym.st_value; 165 166 return (smb_co_walk_init(wsp, SMBL_VC)); 167 } 168 169 /* 170 * Walk the share list below some VC. 171 */ 172 int 173 smb_ss_walk_init(mdb_walk_state_t *wsp) 174 { 175 176 /* 177 * Initial walk_addr is address of parent (VC) 178 */ 179 if (wsp->walk_addr == 0) { 180 mdb_warn("::walk smb_ss does not support global walks\n"); 181 return (WALK_ERR); 182 } 183 184 return (smb_co_walk_init(wsp, SMBL_SHARE)); 185 } 186 187 /* 188 * Walk the file hande list below some share. 189 */ 190 int 191 smb_fh_walk_init(mdb_walk_state_t *wsp) 192 { 193 194 /* 195 * Initial walk_addr is address of parent (share) 196 */ 197 if (wsp->walk_addr == 0) { 198 mdb_warn("::walk smb_fh does not support global walks\n"); 199 return (WALK_ERR); 200 } 201 202 return (smb_co_walk_init(wsp, SMBL_FH)); 203 } 204 205 /* 206 * Common walk_step for walking structs inherited 207 * from smb_connobj_t (smb_vc_t, smb_share_t) 208 */ 209 int 210 smb_co_walk_step(mdb_walk_state_t *wsp) 211 { 212 smb_co_walk_data_t *smbw = wsp->walk_data; 213 int status; 214 215 if (wsp->walk_addr == 0) 216 return (WALK_DONE); 217 218 if (mdb_vread(&smbw->u, smbw->size, wsp->walk_addr) 219 != smbw->size) { 220 mdb_warn("cannot read connobj from %p", wsp->walk_addr); 221 return (WALK_ERR); 222 } 223 224 /* XXX: Sanity check level? parent pointer? */ 225 226 status = wsp->walk_callback(wsp->walk_addr, &smbw->u, 227 wsp->walk_cbdata); 228 229 wsp->walk_addr = (uintptr_t)smbw->u.co.co_next.sle_next; 230 231 return (status); 232 } 233 234 235 /* 236 * Dcmd (and callback function) to print a summary of 237 * all VCs, and optionally all shares under each VC. 238 */ 239 240 typedef struct smb_co_cbdata { 241 int flags; /* OPT_... */ 242 int printed_header; 243 mdb_ctf_id_t ctf_id; 244 } smb_co_cbdata_t; 245 246 /* 247 * Call-back function for walking a file handle list. 248 */ 249 /* ARGSUSED */ 250 int 251 smb_fh_cb(uintptr_t addr, const void *data, void *arg) 252 { 253 const smb_fh_t *fhp = data; 254 // smb_co_cbdata_t *cbd = arg; 255 256 mdb_inc_indent(2); 257 mdb_printf(" %-p", addr); 258 if (fhp->fh_fid2.fid_volatile != 0) { 259 mdb_printf("\t0x%llx\n", 260 (long long) fhp->fh_fid2.fid_volatile); 261 } else { 262 mdb_printf("\t0x%x\n", fhp->fh_fid1); 263 } 264 265 mdb_dec_indent(2); 266 267 return (WALK_NEXT); 268 } 269 270 /* 271 * Call-back function for walking a share list. 272 */ 273 int 274 smb_ss_cb(uintptr_t addr, const void *data, void *arg) 275 { 276 const smb_share_t *ssp = data; 277 smb_co_cbdata_t *cbd = arg; 278 uint32_t tid; 279 280 tid = ssp->ss2_tree_id; 281 if (tid == 0) 282 tid = ssp->ss_tid; 283 284 mdb_printf(" %-p\t0x%x\t%s\n", addr, tid, ssp->ss_name); 285 286 if (cbd->flags & OPT_RECURSE) { 287 mdb_inc_indent(2); 288 if (mdb_pwalk("nsmb_fh", smb_fh_cb, cbd, addr) < 0) { 289 mdb_warn("failed to walk 'nsmb_fh'"); 290 /* Don't: return (WALK_ERR); */ 291 } 292 mdb_dec_indent(2); 293 } 294 295 return (WALK_NEXT); 296 } 297 298 static const char * 299 vcstate_str(smb_co_cbdata_t *cbd, int stval) 300 { 301 static const char prefix[] = "SMBIOD_ST_"; 302 int prefix_len = sizeof (prefix) - 1; 303 mdb_ctf_id_t vcst_enum; 304 const char *cp; 305 306 /* Got this in smb_vc_dcmd. */ 307 vcst_enum = cbd->ctf_id; 308 309 /* Get the name for the enum value. */ 310 if ((cp = mdb_ctf_enum_name(vcst_enum, stval)) == NULL) 311 return ("?"); 312 313 /* Skip the prefix part. */ 314 if (strncmp(cp, prefix, prefix_len) == 0) 315 cp += prefix_len; 316 317 return (cp); 318 } 319 320 /* 321 * Call-back function for walking the VC list. 322 */ 323 int 324 smb_vc_cb(uintptr_t addr, const void *data, void *arg) 325 { 326 const smb_vc_t *vcp = data; 327 smb_co_cbdata_t *cbd = arg; 328 329 if (cbd->printed_header == 0) { 330 cbd->printed_header = 1; 331 mdb_printf("// smb_vc_t uid server \tuser\t\tstate\n"); 332 } 333 334 mdb_printf("%-p", addr); 335 mdb_printf(" %7d", vcp->vc_owner); 336 337 switch (vcp->vc_srvaddr.sa.sa_family) { 338 case AF_INET: 339 mdb_printf(" %I", vcp->vc_srvaddr.sin.sin_addr); 340 break; 341 case AF_INET6: 342 mdb_printf(" %N", &vcp->vc_srvaddr.sin6.sin6_addr); 343 break; 344 default: 345 mdb_printf(" %15s", "(bad af)"); 346 break; 347 } 348 349 if (vcp->vc_username[0] != '\0') 350 mdb_printf("\t%s", vcp->vc_username); 351 else 352 mdb_printf("\t%s", "(?)"); 353 354 if (vcp->vc_domain[0] != '\0') 355 mdb_printf("@%s", vcp->vc_domain); 356 357 mdb_printf("\t%s\n", vcstate_str(cbd, vcp->vc_state)); 358 359 if (cbd->flags & OPT_RECURSE) { 360 mdb_inc_indent(2); 361 if (mdb_pwalk("nsmb_ss", smb_ss_cb, cbd, addr) < 0) { 362 mdb_warn("failed to walk 'nsmb_ss'"); 363 /* Don't: return (WALK_ERR); */ 364 } 365 mdb_dec_indent(2); 366 } 367 368 return (WALK_NEXT); 369 } 370 371 int 372 smb_vc_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 373 { 374 smb_co_cbdata_t cbd; 375 smb_vc_t *vcp; 376 size_t vcsz; 377 378 memset(&cbd, 0, sizeof (cbd)); 379 380 if (mdb_getopts(argc, argv, 381 'r', MDB_OPT_SETBITS, OPT_RECURSE, &cbd.flags, 382 'v', MDB_OPT_SETBITS, OPT_VERBOSE, &cbd.flags, 383 NULL) != argc) { 384 return (DCMD_USAGE); 385 } 386 387 if (mdb_ctf_lookup_by_name("enum smbiod_state", &cbd.ctf_id) == -1) { 388 mdb_warn("Could not find enum smbiod_state"); 389 } 390 391 if (!(flags & DCMD_ADDRSPEC)) { 392 if (mdb_walk("nsmb_vc", smb_vc_cb, &cbd) == -1) { 393 mdb_warn("failed to walk 'nsmb_vc'"); 394 return (DCMD_ERR); 395 } 396 return (DCMD_OK); 397 } 398 399 vcsz = sizeof (*vcp); 400 vcp = mdb_alloc(vcsz, UM_SLEEP | UM_GC); 401 if (mdb_vread(vcp, vcsz, addr) != vcsz) { 402 mdb_warn("cannot read VC from %p", addr); 403 return (DCMD_ERR); 404 } 405 smb_vc_cb(addr, vcp, &cbd); 406 407 return (DCMD_OK); 408 } 409 410 void 411 smb_vc_help(void) 412 { 413 mdb_printf("Options:\n" 414 " -r recursive display of share lists\n" 415 " -v be verbose when displaying smb_vc\n"); 416 } 417 418 /* 419 * Walker for the request list on a VC, 420 * and dcmd to show a summary. 421 */ 422 int 423 rqlist_walk_init(mdb_walk_state_t *wsp) 424 { 425 struct smb_rqhead rqh; 426 uintptr_t addr; 427 428 /* 429 * Initial walk_addr is the address of the VC. 430 * Add offsetof(iod_rqlist) to get the rqhead. 431 */ 432 if (wsp->walk_addr == 0) { 433 mdb_warn("::walk smb_ss does not support global walks\n"); 434 return (WALK_ERR); 435 } 436 addr = wsp->walk_addr; 437 addr += OFFSETOF(smb_vc_t, iod_rqlist); 438 439 if (mdb_vread(&rqh, sizeof (rqh), addr) == -1) { 440 mdb_warn("failed to read smb_rqhead at %p", addr); 441 return (WALK_ERR); 442 } 443 wsp->walk_addr = (uintptr_t)rqh.tqh_first; 444 445 return (WALK_NEXT); 446 } 447 448 int 449 rqlist_walk_step(mdb_walk_state_t *wsp) 450 { 451 smb_rq_t rq; 452 int status; 453 454 if (wsp->walk_addr == 0) 455 return (WALK_DONE); 456 457 if (mdb_vread(&rq, sizeof (rq), wsp->walk_addr) == -1) { 458 mdb_warn("cannot read smb_rq from %p", wsp->walk_addr); 459 return (WALK_ERR); 460 } 461 462 status = wsp->walk_callback(wsp->walk_addr, &rq, 463 wsp->walk_cbdata); 464 465 wsp->walk_addr = (uintptr_t)rq.sr_link.tqe_next; 466 467 return (status); 468 } 469 470 typedef struct rqlist_cbdata { 471 int printed_header; 472 int vcflags; 473 uintptr_t uid; /* optional filtering by UID */ 474 } rqlist_cbdata_t; 475 476 int 477 rqlist_cb(uintptr_t addr, const void *data, void *arg) 478 { 479 const smb_rq_t *rq = data; 480 rqlist_cbdata_t *cbd = arg; 481 482 if (cbd->printed_header == 0) { 483 cbd->printed_header = 1; 484 mdb_printf("// smb_rq_t MID cmd sr_state sr_flags\n"); 485 } 486 487 mdb_printf(" %-p", addr); /* smb_rq_t */ 488 if ((cbd->vcflags & SMBV_SMB2) != 0) { 489 mdb_printf(" x%04llx", (long long)rq->sr2_messageid); 490 mdb_printf(" x%02x", rq->sr2_command); 491 } else { 492 mdb_printf(" x%04x", rq->sr_mid); 493 mdb_printf(" x%02x", rq->sr_cmd); 494 } 495 mdb_printf(" %d", rq->sr_state); 496 mdb_printf(" x%x", rq->sr_flags); 497 mdb_printf("\n"); 498 499 return (WALK_NEXT); 500 } 501 502 /*ARGSUSED*/ 503 int 504 rqlist_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 505 { 506 rqlist_cbdata_t cbd; 507 smb_vc_t *vcp; 508 size_t vcsz; 509 510 memset(&cbd, 0, sizeof (cbd)); 511 512 /* Need the VC again to get */ 513 vcsz = sizeof (*vcp); 514 vcp = mdb_alloc(vcsz, UM_SLEEP | UM_GC); 515 if (mdb_vread(vcp, vcsz, addr) != vcsz) { 516 mdb_warn("cannot read VC from %p", addr); 517 return (DCMD_ERR); 518 } 519 cbd.vcflags = vcp->vc_flags; 520 521 /* 522 * Initial walk_addr is address of parent (VC) 523 */ 524 if (!(flags & DCMD_ADDRSPEC)) { 525 mdb_warn("address required\n"); 526 return (DCMD_ERR); 527 } 528 529 if (mdb_pwalk("nsmb_rqlist", rqlist_cb, &cbd, addr) == -1) { 530 mdb_warn("failed to walk 'nsmb_rqlist'"); 531 return (DCMD_ERR); 532 } 533 534 return (DCMD_OK); 535 } 536 537 538 /* 539 * AVL walker for the passwords AVL tree, 540 * and dcmd to show a summary. 541 */ 542 static int 543 pwtree_walk_init(mdb_walk_state_t *wsp) 544 { 545 GElf_Sym sym; 546 547 if (wsp->walk_addr != 0) { 548 mdb_warn("pwtree walk only supports global walks\n"); 549 return (WALK_ERR); 550 } 551 552 if (mdb_lookup_by_obj(NSMB_OBJNAME, "smb_ptd", &sym) == -1) { 553 mdb_warn("failed to find symbol 'smb_ptd'"); 554 return (WALK_ERR); 555 } 556 557 wsp->walk_addr = (uintptr_t)sym.st_value; 558 559 if (mdb_layered_walk("avl", wsp) == -1) { 560 mdb_warn("failed to walk 'avl'\n"); 561 return (WALK_ERR); 562 } 563 564 return (WALK_NEXT); 565 } 566 567 static int 568 pwtree_walk_step(mdb_walk_state_t *wsp) 569 { 570 smb_passid_t ptnode; 571 572 if (mdb_vread(&ptnode, sizeof (ptnode), wsp->walk_addr) == -1) { 573 mdb_warn("failed to read smb_passid_t at %p", wsp->walk_addr); 574 return (WALK_ERR); 575 } 576 577 return (wsp->walk_callback(wsp->walk_addr, &ptnode, wsp->walk_cbdata)); 578 } 579 580 typedef struct pwtree_cbdata { 581 int printed_header; 582 uid_t uid; /* optional filtering by UID */ 583 } pwtree_cbdata_t; 584 585 int 586 pwtree_cb(uintptr_t addr, const void *data, void *arg) 587 { 588 const smb_passid_t *ptn = data; 589 pwtree_cbdata_t *cbd = arg; 590 591 /* Optional filtering by UID. */ 592 if (cbd->uid != (uid_t)-1 && cbd->uid != ptn->uid) { 593 return (WALK_NEXT); 594 } 595 596 if (cbd->printed_header == 0) { 597 cbd->printed_header = 1; 598 mdb_printf("// smb_passid_t UID domain user\n"); 599 } 600 601 mdb_printf(" %-p", addr); /* smb_passid_t */ 602 mdb_printf(" %d", (uintptr_t)ptn->uid); 603 print_str((uintptr_t)ptn->srvdom); 604 print_str((uintptr_t)ptn->username); 605 mdb_printf("\n"); 606 607 return (WALK_NEXT); 608 } 609 610 /*ARGSUSED*/ 611 int 612 pwtree_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 613 { 614 pwtree_cbdata_t cbd; 615 char *uid_str = NULL; 616 char buf[32]; 617 618 memset(&cbd, 0, sizeof (cbd)); 619 620 if (mdb_getopts(argc, argv, 621 'u', MDB_OPT_STR, &uid_str, NULL) != argc) { 622 return (DCMD_USAGE); 623 } 624 if (uid_str) { 625 /* 626 * Want the the default radix to be 10 here. 627 * If the string has some kind of radix prefix, 628 * just use that as-is, otherwise prepend "0t". 629 * Cheating on the "not a digit" test, but 630 * mdb_strtoull will do a real syntax check. 631 */ 632 if (uid_str[0] == '0' && uid_str[1] > '9') { 633 cbd.uid = (uid_t)mdb_strtoull(uid_str); 634 } else { 635 strcpy(buf, "0t"); 636 strlcat(buf, uid_str, sizeof (buf)); 637 cbd.uid = (uid_t)mdb_strtoull(buf); 638 } 639 } else 640 cbd.uid = (uid_t)-1; 641 642 if (flags & DCMD_ADDRSPEC) { 643 mdb_warn("address not allowed\n"); 644 return (DCMD_ERR); 645 } 646 647 if (mdb_pwalk("nsmb_pwtree", pwtree_cb, &cbd, 0) == -1) { 648 mdb_warn("failed to walk 'nsmb_pwtree'"); 649 return (DCMD_ERR); 650 } 651 652 return (DCMD_OK); 653 } 654 655 void 656 pwtree_help(void) 657 { 658 mdb_printf("Options:\n" 659 " -u uid show only entries belonging to uid (decimal)\n"); 660 } 661 662 663 static const mdb_dcmd_t dcmds[] = { 664 { "nsmb_vc", "?[-rv]", 665 "show smb_vc (or list)", 666 smb_vc_dcmd, smb_vc_help }, 667 { "nsmb_rqlist", ":", 668 "show smb_rq list on a VC", 669 rqlist_dcmd, NULL }, 670 { "nsmb_pwtree", "?[-u uid]", 671 "list smb_passid_t (password tree)", 672 pwtree_dcmd, pwtree_help }, 673 {NULL} 674 }; 675 676 static const mdb_walker_t walkers[] = { 677 { "nsmb_vc", "walk nsmb VC list", 678 smb_vc_walk_init, smb_co_walk_step, NULL }, 679 { "nsmb_ss", "walk nsmb share list for some VC", 680 smb_ss_walk_init, smb_co_walk_step, NULL }, 681 { "nsmb_fh", "walk nsmb share list for some VC", 682 smb_fh_walk_init, smb_co_walk_step, NULL }, 683 { "nsmb_rqlist", "walk request list for some VC", 684 rqlist_walk_init, rqlist_walk_step, NULL }, 685 { "nsmb_pwtree", "walk passord AVL tree", 686 pwtree_walk_init, pwtree_walk_step, NULL }, 687 {NULL} 688 }; 689 690 static const mdb_modinfo_t modinfo = { 691 MDB_API_VERSION, 692 dcmds, 693 walkers 694 }; 695 696 const mdb_modinfo_t * 697 _mdb_init(void) 698 { 699 return (&modinfo); 700 } 701