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