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
print_str(uintptr_t addr)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
smb_co_walk_init(mdb_walk_state_t * wsp,int level)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
smb_vc_walk_init(mdb_walk_state_t * wsp)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
smb_ss_walk_init(mdb_walk_state_t * wsp)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
smb_fh_walk_init(mdb_walk_state_t * wsp)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
smb_co_walk_step(mdb_walk_state_t * wsp)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
smb_fh_cb(uintptr_t addr,const void * data,void * arg)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
smb_ss_cb(uintptr_t addr,const void * data,void * arg)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 *
vcstate_str(smb_co_cbdata_t * cbd,int stval)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
smb_vc_cb(uintptr_t addr,const void * data,void * arg)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
smb_vc_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
smb_vc_help(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
rqlist_walk_init(mdb_walk_state_t * wsp)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
rqlist_walk_step(mdb_walk_state_t * wsp)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
rqlist_cb(uintptr_t addr,const void * data,void * arg)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
rqlist_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
pwtree_walk_init(mdb_walk_state_t * wsp)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
pwtree_walk_step(mdb_walk_state_t * wsp)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
pwtree_cb(uintptr_t addr,const void * data,void * arg)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
pwtree_dcmd(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
pwtree_help(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 *
_mdb_init(void)697 _mdb_init(void)
698 {
699 return (&modinfo);
700 }
701