xref: /illumos-gate/usr/src/cmd/mdb/common/modules/nsmb/nsmb.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
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