xref: /illumos-gate/usr/src/cmd/mdb/common/modules/sppp/sppp.c (revision 78801af7286cd73dbc996d470f789e75993cf15d)
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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/stropts.h>
28 #include <sys/stream.h>
29 #include <sys/socket.h>
30 #include <net/if.h>
31 #define	SOL2
32 #include <net/ppp_defs.h>
33 #include <net/pppio.h>
34 #include <net/sppptun.h>
35 #include <netinet/in.h>
36 #include <netinet/ip6.h>
37 #include <inet/common.h>
38 #include <inet/mib2.h>
39 #include <inet/ip.h>
40 #include <inet/ip6.h>
41 #include <sppp/sppp.h>
42 #include <sppptun/sppptun_impl.h>
43 
44 #include <mdb/mdb_modapi.h>
45 #include <mdb/mdb_ks.h>
46 #include <stdio.h>
47 
48 /* ****************** sppp ****************** */
49 
50 static int
51 sppp_walk_init(mdb_walk_state_t *wsp)
52 {
53 	if (mdb_readvar(&wsp->walk_addr, "sps_list") == -1) {
54 		mdb_warn("failed to read sps_list");
55 		return (WALK_ERR);
56 	}
57 
58 	return (WALK_NEXT);
59 }
60 
61 static int
62 sppp_walk_step(mdb_walk_state_t *wsp)
63 {
64 	spppstr_t sps;
65 	int status;
66 
67 	if (wsp->walk_addr == 0)
68 		return (WALK_DONE);
69 
70 	if (mdb_vread(&sps, sizeof (sps), wsp->walk_addr) == -1) {
71 		mdb_warn("can't read spppstr_t at %p", wsp->walk_addr);
72 		return (WALK_ERR);
73 	}
74 
75 	status = (wsp->walk_callback(wsp->walk_addr, &sps, wsp->walk_cbdata));
76 
77 	wsp->walk_addr = (uintptr_t)sps.sps_nextmn;
78 	return (status);
79 }
80 
81 static int
82 sps_format(uintptr_t addr, const spppstr_t *sps, uint_t *qfmt)
83 {
84 	sppa_t ppa;
85 	queue_t upq;
86 	uintptr_t upaddr, illaddr;
87 	ill_t ill;
88 	ipif_t ipif;
89 
90 	mdb_printf("%?p ", addr);
91 	if (*qfmt)
92 		mdb_printf("%?p ", sps->sps_rq);
93 	if (sps->sps_ppa == NULL) {
94 		mdb_printf("?       unset     ");
95 	} else if (mdb_vread(&ppa, sizeof (ppa), (uintptr_t)sps->sps_ppa) ==
96 	    -1) {
97 		mdb_printf("?      ?%p ", sps->sps_ppa);
98 	} else {
99 		mdb_printf("%-6d sppp%-5d ", ppa.ppa_zoneid, ppa.ppa_ppa_id);
100 	}
101 	if (IS_SPS_CONTROL(sps)) {
102 		mdb_printf("Control\n");
103 	} else if (IS_SPS_PIOATTACH(sps)) {
104 		mdb_printf("Stats\n");
105 	} else if (sps->sps_dlstate == DL_UNATTACHED) {
106 		mdb_printf("Unknown\n");
107 	} else if (sps->sps_dlstate != DL_IDLE) {
108 		mdb_printf("DLPI Unbound\n");
109 	} else {
110 		upaddr = (uintptr_t)sps->sps_rq;
111 		upq.q_ptr = NULL;
112 		illaddr = 0;
113 		while (upaddr != 0) {
114 			if (mdb_vread(&upq, sizeof (upq), upaddr) == -1) {
115 				upq.q_ptr = NULL;
116 				break;
117 			}
118 			if ((upaddr = (uintptr_t)upq.q_next) != 0)
119 				illaddr = (uintptr_t)upq.q_ptr;
120 		}
121 		if (illaddr != 0) {
122 			if (mdb_vread(&ill, sizeof (ill), illaddr) == -1 ||
123 			    mdb_vread(&ipif, sizeof (ipif),
124 			    (uintptr_t)ill.ill_ipif) == -1) {
125 				illaddr = 0;
126 			}
127 		}
128 
129 		switch (sps->sps_req_sap) {
130 		case ETHERTYPE_IP:
131 			mdb_printf("DLPI IPv4 ");
132 			if (*qfmt) {
133 				mdb_printf("\n");
134 			} else if (illaddr == 0) {
135 				mdb_printf("(no addresses)\n");
136 			} else {
137 				/*
138 				 * SCCS oddity here -- % <capital> %
139 				 * suffers from keyword replacement.
140 				 * Avoid that by using ANSI string
141 				 * pasting.
142 				 */
143 				mdb_printf("%I:%I" "%s\n",
144 				    ipif.ipif_lcl_addr, ipif.ipif_pp_dst_addr,
145 				    (ipif.ipif_next ? " ..." : ""));
146 			}
147 			break;
148 		case ETHERTYPE_IPV6:
149 			mdb_printf("DLPI IPv6 ");
150 			if (*qfmt) {
151 				mdb_printf("\n");
152 				break;
153 			}
154 			if (illaddr == 0) {
155 				mdb_printf("(no addresses)\n");
156 				break;
157 			}
158 			mdb_printf("%N\n%?s%21s", &ipif.ipif_v6lcl_addr,
159 			    "", "");
160 			mdb_printf("%N\n", &ipif.ipif_v6pp_dst_addr);
161 			break;
162 		case ETHERTYPE_ALLSAP:
163 			mdb_printf("DLPI Snoop\n");
164 			break;
165 		default:
166 			mdb_printf("DLPI SAP 0x%04X\n", sps->sps_req_sap);
167 			break;
168 		}
169 	}
170 
171 	return (WALK_NEXT);
172 }
173 
174 static int
175 sppp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
176 {
177 	uint_t qfmt = FALSE;
178 	spppstr_t sps;
179 
180 	if (mdb_getopts(argc, argv, 'q', MDB_OPT_SETBITS, TRUE, &qfmt, NULL) !=
181 	    argc)
182 		return (DCMD_USAGE);
183 
184 	if ((flags & DCMD_LOOPFIRST) || !(flags & DCMD_LOOP)) {
185 		if (qfmt) {
186 			mdb_printf("%<u>%?s %?s %-6s %-9s %s%</u>\n", "Address",
187 			    "RecvQ", "ZoneID", "Interface", "Type");
188 		} else {
189 			mdb_printf("%<u>%?s %-6s %-9s %s%</u>\n", "Address",
190 			    "ZoneID", "Interface", "Type");
191 		}
192 	}
193 
194 	if (flags & DCMD_ADDRSPEC) {
195 		(void) mdb_vread(&sps, sizeof (sps), addr);
196 		(void) sps_format(addr, &sps, &qfmt);
197 	} else if (mdb_walk("sppp", (mdb_walk_cb_t)sps_format, &qfmt) == -1) {
198 		mdb_warn("failed to walk sps_list");
199 		return (DCMD_ERR);
200 	}
201 
202 	return (DCMD_OK);
203 }
204 
205 static int
206 sppa_walk_init(mdb_walk_state_t *wsp)
207 {
208 	if (mdb_readvar(&wsp->walk_addr, "ppa_list") == -1) {
209 		mdb_warn("failed to read ppa_list");
210 		return (WALK_ERR);
211 	}
212 
213 	return (WALK_NEXT);
214 }
215 
216 static int
217 sppa_walk_step(mdb_walk_state_t *wsp)
218 {
219 	sppa_t ppa;
220 	int status;
221 
222 	if (wsp->walk_addr == 0)
223 		return (WALK_DONE);
224 
225 	if (mdb_vread(&ppa, sizeof (ppa), wsp->walk_addr) == -1) {
226 		mdb_warn("can't read spppstr_t at %p", wsp->walk_addr);
227 		return (WALK_ERR);
228 	}
229 
230 	status = (wsp->walk_callback(wsp->walk_addr, &ppa, wsp->walk_cbdata));
231 
232 	wsp->walk_addr = (uintptr_t)ppa.ppa_nextppa;
233 	return (status);
234 }
235 
236 /* ARGSUSED */
237 static int
238 ppa_format(uintptr_t addr, const sppa_t *ppa, uint_t *qfmt)
239 {
240 	mdb_printf("%?p %-6d sppp%-5d %?p %?p\n", addr, ppa->ppa_zoneid,
241 	    ppa->ppa_ppa_id, ppa->ppa_ctl, ppa->ppa_lower_wq);
242 
243 	return (WALK_NEXT);
244 }
245 
246 /* ARGSUSED */
247 static int
248 sppa(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
249 {
250 	uint_t qfmt = FALSE;
251 	sppa_t ppa;
252 
253 	if ((flags & DCMD_LOOPFIRST) || !(flags & DCMD_LOOP)) {
254 		mdb_printf("%<u>%?s %-6s %-9s %?s %?s%</u>\n", "Address",
255 		    "ZoneID", "Interface", "Control", "LowerQ");
256 	}
257 
258 	if (flags & DCMD_ADDRSPEC) {
259 		(void) mdb_vread(&ppa, sizeof (ppa), addr);
260 		(void) ppa_format(addr, &ppa, &qfmt);
261 	} else if (mdb_walk("sppa", (mdb_walk_cb_t)ppa_format, &qfmt) == -1) {
262 		mdb_warn("failed to walk ppa_list");
263 		return (DCMD_ERR);
264 	}
265 
266 	return (DCMD_OK);
267 }
268 
269 static void
270 sppp_qinfo(const queue_t *q, char *buf, size_t nbytes)
271 {
272 	spppstr_t sps;
273 	sppa_t ppa;
274 
275 	if (mdb_vread(&sps, sizeof (sps), (uintptr_t)q->q_ptr) ==
276 	    sizeof (sps)) {
277 		if (sps.sps_ppa == NULL ||
278 		    mdb_vread(&ppa, sizeof (ppa), (uintptr_t)sps.sps_ppa) ==
279 		    -1) {
280 			(void) mdb_snprintf(buf, nbytes, "minor %d",
281 			    sps.sps_mn_id);
282 		} else {
283 			(void) mdb_snprintf(buf, nbytes, "sppp%d",
284 			    ppa.ppa_ppa_id);
285 		}
286 	}
287 }
288 
289 static uintptr_t
290 sppp_rnext(const queue_t *q)
291 {
292 	spppstr_t sps;
293 
294 	if (mdb_vread(&sps, sizeof (sps), (uintptr_t)q->q_ptr) == sizeof (sps))
295 		return ((uintptr_t)sps.sps_rq);
296 
297 	return (0);
298 }
299 
300 static uintptr_t
301 sppp_wnext(const queue_t *q)
302 {
303 	spppstr_t sps;
304 	sppa_t ppa;
305 
306 	if (mdb_vread(&sps, sizeof (sps), (uintptr_t)q->q_ptr) != sizeof (sps))
307 		return (0);
308 
309 	if (sps.sps_ppa != NULL &&
310 	    mdb_vread(&ppa, sizeof (ppa), (uintptr_t)sps.sps_ppa) ==
311 	    sizeof (ppa))
312 		return ((uintptr_t)ppa.ppa_lower_wq);
313 
314 	return (0);
315 }
316 
317 /* ****************** sppptun ****************** */
318 
319 struct tcl_walk_data {
320 	size_t tcl_nslots;
321 	size_t walkpos;
322 	tuncl_t *tcl_slots[1];
323 };
324 
325 static void
326 tuncl_walk_fini(mdb_walk_state_t *wsp)
327 {
328 	struct tcl_walk_data *twd;
329 
330 	if (wsp != NULL && wsp->walk_addr != 0) {
331 		twd = (struct tcl_walk_data *)wsp->walk_addr;
332 		mdb_free(twd, sizeof (*twd) + ((twd->tcl_nslots - 1) *
333 		    sizeof (twd->tcl_slots[0])));
334 		wsp->walk_addr = 0;
335 	}
336 }
337 
338 static int
339 tuncl_walk_init(mdb_walk_state_t *wsp)
340 {
341 	size_t tcl_nslots;
342 	tuncl_t **tcl_slots;
343 	struct tcl_walk_data *twd;
344 
345 	if (wsp == NULL)
346 		return (WALK_ERR);
347 
348 	if (wsp->walk_addr != 0)
349 		tuncl_walk_fini(wsp);
350 
351 	if (mdb_readvar(&tcl_nslots, "tcl_nslots") == -1) {
352 		mdb_warn("failed to read tcl_nslots");
353 		return (WALK_ERR);
354 	}
355 
356 	if (tcl_nslots == 0)
357 		return (WALK_DONE);
358 
359 	if (mdb_readvar(&tcl_slots, "tcl_slots") == -1) {
360 		mdb_warn("failed to read tcl_slots");
361 		return (WALK_ERR);
362 	}
363 
364 	twd = (struct tcl_walk_data *)mdb_alloc(sizeof (*twd) +
365 	    (tcl_nslots - 1) * sizeof (*tcl_slots), UM_NOSLEEP);
366 	if (twd == NULL)
367 		return (WALK_ERR);
368 	twd->tcl_nslots = tcl_nslots;
369 	twd->walkpos = 0;
370 	wsp->walk_addr = (uintptr_t)twd;
371 
372 	if (mdb_vread(twd->tcl_slots, tcl_nslots * sizeof (twd->tcl_slots[0]),
373 	    (uintptr_t)tcl_slots) == -1) {
374 		mdb_warn("can't read tcl_slots at %p", tcl_slots);
375 		tuncl_walk_fini(wsp);
376 		return (WALK_ERR);
377 	}
378 
379 	return (WALK_NEXT);
380 }
381 
382 static int
383 tuncl_walk_step(mdb_walk_state_t *wsp)
384 {
385 	tuncl_t tcl;
386 	int status;
387 	struct tcl_walk_data *twd;
388 	uintptr_t addr;
389 
390 	if (wsp == NULL || wsp->walk_addr == 0)
391 		return (WALK_DONE);
392 
393 	twd = (struct tcl_walk_data *)wsp->walk_addr;
394 
395 	while (twd->walkpos < twd->tcl_nslots &&
396 	    twd->tcl_slots[twd->walkpos] == NULL)
397 		twd->walkpos++;
398 	if (twd->walkpos >= twd->tcl_nslots)
399 		return (WALK_DONE);
400 
401 	addr = (uintptr_t)twd->tcl_slots[twd->walkpos];
402 	if (mdb_vread(&tcl, sizeof (tcl), addr) == -1) {
403 		mdb_warn("can't read tuncl_t at %p", addr);
404 		return (WALK_ERR);
405 	}
406 
407 	status = wsp->walk_callback(addr, &tcl, wsp->walk_cbdata);
408 
409 	twd->walkpos++;
410 	return (status);
411 }
412 
413 /* ARGSUSED */
414 static int
415 tuncl_format(uintptr_t addr, const tuncl_t *tcl, uint_t *qfmt)
416 {
417 	mdb_printf("%?p %-6d %?p %?p", addr, tcl->tcl_zoneid, tcl->tcl_data_tll,
418 	    tcl->tcl_ctrl_tll);
419 	mdb_printf(" %-2d %04X %04X ", tcl->tcl_style,
420 	    tcl->tcl_lsessid, tcl->tcl_rsessid);
421 	if (tcl->tcl_flags & TCLF_DAEMON) {
422 		mdb_printf("<daemon>\n");
423 	} else {
424 		mdb_printf("sppp%d\n", tcl->tcl_unit);
425 	}
426 
427 	return (WALK_NEXT);
428 }
429 
430 /* ARGSUSED */
431 static int
432 tuncl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
433 {
434 	uint_t qfmt = FALSE;
435 	tuncl_t tcl;
436 
437 	if ((flags & DCMD_LOOPFIRST) || !(flags & DCMD_LOOP)) {
438 		mdb_printf("%<u>%?s %-6s %?s %?s Ty LSes RSes %s%</u>\n",
439 		    "Address", "ZoneID", "Data", "Control", "Interface");
440 	}
441 
442 	if (flags & DCMD_ADDRSPEC) {
443 		if (mdb_vread(&tcl, sizeof (tcl), addr) == -1)
444 			mdb_warn("failed to read tuncl_t at %p", addr);
445 		else
446 			tuncl_format(addr, &tcl, &qfmt);
447 	} else if (mdb_walk("tuncl", (mdb_walk_cb_t)tuncl_format, &qfmt) ==
448 	    -1) {
449 		mdb_warn("failed to walk tcl_slots");
450 		return (DCMD_ERR);
451 	}
452 
453 	return (DCMD_OK);
454 }
455 
456 struct tll_walk_data {
457 	void *listhead;
458 	void *next;
459 };
460 
461 static void
462 tunll_walk_fini(mdb_walk_state_t *wsp)
463 {
464 	struct tll_walk_data *twd;
465 
466 	if (wsp != NULL && wsp->walk_addr != 0) {
467 		twd = (struct tll_walk_data *)wsp->walk_addr;
468 		mdb_free(twd, sizeof (*twd));
469 		wsp->walk_addr = 0;
470 	}
471 }
472 
473 static int
474 tunll_walk_init(mdb_walk_state_t *wsp)
475 {
476 	GElf_Sym sym;
477 	struct tll_walk_data *twd;
478 	struct qelem tunll_list;
479 
480 	if (wsp->walk_addr != 0)
481 		tunll_walk_fini(wsp);
482 
483 	if (mdb_lookup_by_obj("sppptun", "tunll_list", &sym) != 0) {
484 		mdb_warn("failed to find tunll_list");
485 		return (WALK_ERR);
486 	}
487 
488 	if (mdb_vread(&tunll_list, sizeof (tunll_list),
489 	    (uintptr_t)sym.st_value) == -1) {
490 		mdb_warn("can't read tunll_list at %p",
491 		    (uintptr_t)sym.st_value);
492 		return (WALK_ERR);
493 	}
494 
495 	twd = (struct tll_walk_data *)mdb_alloc(sizeof (*twd), UM_NOSLEEP);
496 	if (twd == NULL)
497 		return (WALK_ERR);
498 	twd->listhead = (void *)(uintptr_t)sym.st_value;
499 	twd->next = (void *)tunll_list.q_forw;
500 	wsp->walk_addr = (uintptr_t)twd;
501 
502 	return (WALK_NEXT);
503 }
504 
505 static int
506 tunll_walk_step(mdb_walk_state_t *wsp)
507 {
508 	struct tll_walk_data *twd;
509 	tunll_t tll;
510 	int status;
511 	uintptr_t addr;
512 
513 	if (wsp == NULL || wsp->walk_addr == 0)
514 		return (WALK_DONE);
515 
516 	twd = (struct tll_walk_data *)wsp->walk_addr;
517 	if (twd->next == NULL || twd->next == twd->listhead)
518 		return (WALK_DONE);
519 
520 	/* LINTED */
521 	addr = (uintptr_t)TO_TLL(twd->next);
522 	if (mdb_vread(&tll, sizeof (tll), addr) == -1) {
523 		mdb_warn("can't read tunll_t at %p", addr);
524 		return (WALK_ERR);
525 	}
526 
527 	status = wsp->walk_callback(addr, &tll, wsp->walk_cbdata);
528 
529 	twd->next = (void *)tll.tll_next;
530 	return (status);
531 }
532 
533 /* ARGSUSED */
534 static int
535 tunll_format(uintptr_t addr, const tunll_t *tll, uint_t *qfmt)
536 {
537 	mdb_printf("%?p %-6d %-14s %?p", addr, tll->tll_zoneid, tll->tll_name,
538 	    tll->tll_defcl);
539 	if (tll->tll_style == PTS_PPPOE) {
540 		mdb_printf(" %x:%x:%x:%x:%x:%x",
541 		    tll->tll_lcladdr.pta_pppoe.ptma_mac[0],
542 		    tll->tll_lcladdr.pta_pppoe.ptma_mac[1],
543 		    tll->tll_lcladdr.pta_pppoe.ptma_mac[2],
544 		    tll->tll_lcladdr.pta_pppoe.ptma_mac[3],
545 		    tll->tll_lcladdr.pta_pppoe.ptma_mac[4],
546 		    tll->tll_lcladdr.pta_pppoe.ptma_mac[5]);
547 	}
548 	mdb_printf("\n");
549 
550 	return (WALK_NEXT);
551 }
552 
553 /* ARGSUSED */
554 static int
555 tunll(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
556 {
557 	uint_t qfmt = FALSE;
558 	tunll_t tll;
559 
560 	if ((flags & DCMD_LOOPFIRST) || !(flags & DCMD_LOOP)) {
561 		mdb_printf("%<u>%?s %-6s %-14s %?s %s%</u>\n", "Address",
562 		    "ZoneID", "Interface Name", "Daemon", "Local Address");
563 	}
564 
565 	if (flags & DCMD_ADDRSPEC) {
566 		if (mdb_vread(&tll, sizeof (tll), addr) == -1)
567 			mdb_warn("failed to read tunll_t at %p", addr);
568 		else
569 			tunll_format(addr, &tll, &qfmt);
570 	} else if (mdb_walk("tunll", (mdb_walk_cb_t)tunll_format, &qfmt) ==
571 	    -1) {
572 		mdb_warn("failed to walk tunll_list");
573 		return (DCMD_ERR);
574 	}
575 
576 	return (DCMD_OK);
577 }
578 
579 union tun_state {
580 	uint32_t tunflags;
581 	tuncl_t tcl;
582 	tunll_t tll;
583 };
584 
585 static int
586 tun_state_read(void *ptr, union tun_state *ts)
587 {
588 	/*
589 	 * First, get the flags on this structure.  This is either a
590 	 * tuncl_t or a tunll_t.
591 	 */
592 	if (mdb_vread(&ts->tunflags, sizeof (ts->tunflags), (uintptr_t)ptr) ==
593 	    sizeof (ts->tunflags)) {
594 		if (ts->tunflags & TCLF_ISCLIENT) {
595 			if (mdb_vread(&ts->tcl, sizeof (ts->tcl),
596 			    (uintptr_t)ptr) == sizeof (ts->tcl)) {
597 				return (0);
598 			}
599 		} else {
600 			if (mdb_vread(&ts->tll, sizeof (ts->tll),
601 			    (uintptr_t)ptr) == sizeof (ts->tll)) {
602 				return (0);
603 			}
604 		}
605 	}
606 	return (-1);
607 }
608 
609 static void
610 sppptun_qinfo(const queue_t *q, char *buf, size_t nbytes)
611 {
612 	union tun_state ts;
613 
614 	if (tun_state_read(q->q_ptr, &ts) == -1)
615 		return;
616 
617 	if (ts.tcl.tcl_flags & TCLF_ISCLIENT)
618 		mdb_snprintf(buf, nbytes, "sppp%d client %04X",
619 		    ts.tcl.tcl_unit, ts.tcl.tcl_lsessid);
620 	else
621 		mdb_snprintf(buf, nbytes, "%s", ts.tll.tll_name);
622 }
623 
624 static uintptr_t
625 sppptun_rnext(const queue_t *q)
626 {
627 	union tun_state ts;
628 
629 	if (tun_state_read(q->q_ptr, &ts) == -1)
630 		return (0);
631 
632 	if (ts.tcl.tcl_flags & TCLF_ISCLIENT) {
633 		return ((uintptr_t)ts.tcl.tcl_rq);
634 	} else {
635 		/* Not quite right, but ... */
636 		return ((uintptr_t)ts.tll.tll_defcl);
637 	}
638 }
639 
640 static uintptr_t
641 sppptun_wnext(const queue_t *q)
642 {
643 	union tun_state ts;
644 
645 	if (tun_state_read(q->q_ptr, &ts) == -1)
646 		return (0);
647 
648 	if (ts.tcl.tcl_flags & TCLF_ISCLIENT) {
649 		if (ts.tcl.tcl_data_tll == NULL)
650 			return (0);
651 		if (mdb_vread(&ts.tll, sizeof (ts.tll),
652 		    (uintptr_t)ts.tcl.tcl_data_tll) != sizeof (ts.tll)) {
653 			return (0);
654 		}
655 	}
656 	return ((uintptr_t)ts.tll.tll_wq);
657 }
658 
659 static const mdb_dcmd_t dcmds[] = {
660 	{ "sppp", "[-q]", "display PPP stream state structures", sppp },
661 	{ "sppa", "", "display PPP attachment state structures", sppa },
662 	{ "tuncl", "", "display sppptun client stream state structures",
663 	    tuncl },
664 	{ "tunll", "", "display sppptun lower stream state structures",
665 	    tunll },
666 	{ NULL }
667 };
668 
669 static const mdb_walker_t walkers[] = {
670 	{ "sppp", "walk active spppstr_t structures",
671 	    sppp_walk_init, sppp_walk_step, NULL },
672 	{ "sppa", "walk active sppa_t structures",
673 	    sppa_walk_init, sppa_walk_step, NULL },
674 	{ "tuncl", "walk active tuncl_t structures",
675 	    tuncl_walk_init, tuncl_walk_step, tuncl_walk_fini },
676 	{ "tunll", "walk active tunll_t structures",
677 	    tunll_walk_init, tunll_walk_step, tunll_walk_fini },
678 	{ NULL }
679 };
680 
681 static const mdb_qops_t sppp_qops = { sppp_qinfo, sppp_rnext, sppp_wnext };
682 static const mdb_qops_t sppptun_qops = {
683 	sppptun_qinfo, sppptun_rnext, sppptun_wnext
684 };
685 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
686 
687 const mdb_modinfo_t *
688 _mdb_init(void)
689 {
690 	GElf_Sym sym;
691 
692 	if (mdb_lookup_by_obj("sppp", "sppp_uwinit", &sym) == 0)
693 		mdb_qops_install(&sppp_qops, (uintptr_t)sym.st_value);
694 
695 	if (mdb_lookup_by_obj("sppptun", "sppptun_uwinit", &sym) == 0)
696 		mdb_qops_install(&sppptun_qops, (uintptr_t)sym.st_value);
697 
698 	return (&modinfo);
699 }
700 
701 void
702 _mdb_fini(void)
703 {
704 	GElf_Sym sym;
705 
706 	if (mdb_lookup_by_obj("sppptun", "sppptun_uwinit", &sym) == 0)
707 		mdb_qops_remove(&sppptun_qops, (uintptr_t)sym.st_value);
708 
709 	if (mdb_lookup_by_obj("sppp", "sppp_uwinit", &sym) == 0)
710 		mdb_qops_remove(&sppp_qops, (uintptr_t)sym.st_value);
711 }
712