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