xref: /illumos-gate/usr/src/cmd/mdb/common/modules/sctp/sctp.c (revision 7ae111d47a973fff4c6e231cc31f271dd9cef473)
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 2006 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/stream.h>
31 #include <sys/mdb_modapi.h>
32 #include <sys/socket.h>
33 #include <sys/list.h>
34 #include <sys/strsun.h>
35 
36 #include <mdb/mdb_stdlib.h>
37 
38 #include <netinet/in.h>
39 #include <netinet/ip6.h>
40 #include <netinet/sctp.h>
41 
42 #include <inet/common.h>
43 #include <inet/ip.h>
44 #include <inet/ip6.h>
45 #include <inet/ipclassifier.h>
46 
47 #include <sctp/sctp_impl.h>
48 #include <sctp/sctp_addr.h>
49 
50 #define	MDB_SCTP_SHOW_FLAGS	0x1
51 #define	MDB_SCTP_DUMP_ADDRS	0x2
52 #define	MDB_SCTP_SHOW_HASH	0x4
53 #define	MDB_SCTP_SHOW_OUT	0x8
54 #define	MDB_SCTP_SHOW_IN	0x10
55 #define	MDB_SCTP_SHOW_MISC	0x20
56 #define	MDB_SCTP_SHOW_RTT	0x40
57 #define	MDB_SCTP_SHOW_STATS	0x80
58 #define	MDB_SCTP_SHOW_FLOW	0x100
59 #define	MDB_SCTP_SHOW_HDR	0x200
60 #define	MDB_SCTP_SHOW_PMTUD	0x400
61 #define	MDB_SCTP_SHOW_RXT	0x800
62 #define	MDB_SCTP_SHOW_CONN	0x1000
63 #define	MDB_SCTP_SHOW_CLOSE	0x2000
64 #define	MDB_SCTP_SHOW_EXT	0x4000
65 
66 #define	MDB_SCTP_SHOW_ALL	0xffffffff
67 
68 uint_t sctp_conn_hash_size;
69 static GElf_Sym sctp_list_sym;
70 static list_t sctp_list;
71 
72 /*
73  * Both the ill and ipif global arrays are small, so we just read
74  * in the whole arrays.
75  */
76 static sctp_ill_hash_t local_g_ills[SCTP_ILL_HASH];
77 static sctp_ipif_hash_t local_g_ipifs[SCTP_IPIF_HASH];
78 
79 /*
80  * Copy from usr/src/uts/common/os/list.c.  Should we have a generic
81  * mdb list walker?
82  */
83 #define	list_object(a, node) ((void *)(((char *)node) - (a)->list_offset))
84 
85 static char *
86 sctp_faddr_state(int state)
87 {
88 	char *statestr;
89 
90 	switch (state) {
91 	case SCTP_FADDRS_UNREACH:
92 		statestr = "Unreachable";
93 		break;
94 	case SCTP_FADDRS_DOWN:
95 		statestr = "Down";
96 		break;
97 	case SCTP_FADDRS_ALIVE:
98 		statestr = "Alive";
99 		break;
100 	case SCTP_FADDRS_UNCONFIRMED:
101 		statestr = "Unconfirmed";
102 		break;
103 	default:
104 		statestr = "Unknown";
105 		break;
106 	}
107 	return (statestr);
108 }
109 
110 /* ARGSUSED */
111 static int
112 sctp_faddr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
113 {
114 	sctp_faddr_t fa[1];
115 	char *statestr;
116 
117 	if (!(flags & DCMD_ADDRSPEC))
118 		return (DCMD_USAGE);
119 
120 	if (mdb_vread(fa, sizeof (*fa), addr) == -1) {
121 		mdb_warn("cannot read fadder at %p", addr);
122 		return (DCMD_ERR);
123 	}
124 
125 	statestr = sctp_faddr_state(fa->state);
126 	mdb_printf("%<u>%p\t%<b>%N%</b>\t%s%</u>\n", addr, &fa->faddr,
127 	    statestr);
128 	mdb_printf("next\t\t%?p\tsaddr\t%N\n", fa->next, &fa->saddr);
129 	mdb_printf("rto\t\t%?d\tsrtt\t\t%?d\n", fa->rto, fa->srtt);
130 	mdb_printf("rttvar\t\t%?d\trtt_updates\t%?u\n", fa->rttvar,
131 	    fa->rtt_updates);
132 	mdb_printf("strikes\t\t%?d\tmax_retr\t%?d\n", fa->strikes,
133 	    fa->max_retr);
134 	mdb_printf("hb_expiry\t%?ld\thb_interval\t%?u\n", fa->hb_expiry,
135 	    fa->hb_interval);
136 	mdb_printf("pmss\t\t%?u\tcwnd\t\t%?u\n", fa->sfa_pmss, fa->cwnd);
137 	mdb_printf("ssthresh\t%?u\tsuna\t\t%?u\n", fa->ssthresh, fa->suna);
138 	mdb_printf("pba\t\t%?u\tacked\t\t%?u\n", fa->pba, fa->acked);
139 	mdb_printf("lastactive\t%?ld\thb_secret\t%?#lx\n", fa->lastactive,
140 	    fa->hb_secret);
141 	mdb_printf("timer_mp\t%?p\tire\t\t%?p\n", fa->timer_mp, fa->ire);
142 	mdb_printf("hb_pending\t%?d\ttimer_running\t%?d\n"
143 	    "df\t\t%?d\tpmtu_discovered\t%?d\n"
144 	    "isv4\t\t%?d\tretransmissions\t%?u\n",
145 	    fa->hb_pending, fa->timer_running, fa->df, fa->pmtu_discovered,
146 	    fa->isv4, fa->T3expire);
147 
148 	return (DCMD_OK);
149 }
150 
151 static void
152 print_set(sctp_set_t *sp)
153 {
154 	mdb_printf("\tbegin\t%<b>%?x%</b>\t\tend\t%<b>%?x%</b>\n",
155 	    sp->begin, sp->end);
156 	mdb_printf("\tnext\t%?p\tprev\t%?p\n", sp->next, sp->prev);
157 }
158 
159 /* ARGSUSED */
160 static int
161 sctp_set(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
162 {
163 	sctp_set_t sp[1];
164 
165 	if (!(flags & DCMD_ADDRSPEC))
166 		return (DCMD_USAGE);
167 
168 	if (mdb_vread(sp, sizeof (*sp), addr) == -1)
169 		return (DCMD_ERR);
170 
171 	print_set(sp);
172 
173 	return (DCMD_OK);
174 }
175 
176 static void
177 dump_sack_info(uintptr_t addr)
178 {
179 	sctp_set_t sp[1];
180 
181 	while (addr != 0) {
182 		if (mdb_vread(sp, sizeof (*sp), addr) == -1) {
183 			mdb_warn("failed to read sctp_set at %p", addr);
184 			return;
185 		}
186 
187 		addr = (uintptr_t)sp->next;
188 		print_set(sp);
189 	}
190 }
191 
192 static int
193 dump_msghdr(mblk_t *meta)
194 {
195 	sctp_msg_hdr_t smh;
196 
197 	if (mdb_vread(&smh, sizeof (smh), (uintptr_t)meta->b_rptr) == -1)
198 		return (-1);
199 
200 	mdb_printf("%<u>msg_hdr_t at \t%?p\tsentto\t%?p%</u>\n",
201 	    meta->b_rptr, SCTP_CHUNK_DEST(meta));
202 	mdb_printf("\tttl\t%?ld\ttob\t%?ld\n", smh.smh_ttl, smh.smh_tob);
203 	mdb_printf("\tsid\t%?u\tssn\t%?u\n", smh.smh_sid, smh.smh_ssn);
204 	mdb_printf("\tppid\t%?u\tflags\t%?s\n", smh.smh_ppid,
205 	    smh.smh_flags & MSG_UNORDERED ? "unordered" : " ");
206 	mdb_printf("\tcontext\t%?u\tmsglen\t%?d\n", smh.smh_context,
207 	    smh.smh_msglen);
208 
209 	return (0);
210 }
211 
212 static int
213 dump_datahdr(mblk_t *mp)
214 {
215 	sctp_data_hdr_t	sdc;
216 	uint16_t		sdh_int16;
217 	uint32_t		sdh_int32;
218 
219 	if (mdb_vread(&sdc, sizeof (sdc), (uintptr_t)mp->b_rptr) == -1)
220 		return (-1);
221 
222 	mdb_printf("%<u>data_chunk_t \t%?p\tsentto\t%?p%</u>\n",
223 	    mp->b_rptr, SCTP_CHUNK_DEST(mp));
224 	mdb_printf("\tsent\t%?d\t", SCTP_CHUNK_ISSENT(mp)?1:0);
225 	mdb_printf("retrans\t%?d\n", SCTP_CHUNK_WANT_REXMIT(mp)?1:0);
226 	mdb_printf("\tacked\t%?d\t", SCTP_CHUNK_ISACKED(mp)?1:0);
227 	mdb_printf("sackcnt\t%?u\n", SCTP_CHUNK_SACKCNT(mp));
228 
229 	mdb_nhconvert(&sdh_int16, &sdc.sdh_len, sizeof (sdc.sdh_len));
230 	mdb_printf("\tlen\t%?d\t", sdh_int16);
231 	mdb_printf("BBIT=%d", SCTP_DATA_GET_BBIT(&sdc) == 0 ? 0 : 1);
232 	mdb_printf("EBIT=%d", SCTP_DATA_GET_EBIT(&sdc) == 0 ? 0 : 1);
233 
234 	mdb_nhconvert(&sdh_int32, &sdc.sdh_tsn, sizeof (sdc.sdh_tsn));
235 	mdb_nhconvert(&sdh_int16, &sdc.sdh_sid, sizeof (sdc.sdh_sid));
236 	mdb_printf("\ttsn\t%?x\tsid\t%?hu\n", sdh_int32, sdh_int16);
237 
238 	mdb_nhconvert(&sdh_int16, &sdc.sdh_ssn, sizeof (sdc.sdh_ssn));
239 	mdb_nhconvert(&sdh_int32, &sdc.sdh_payload_id,
240 	    sizeof (sdc.sdh_payload_id));
241 	mdb_printf("\tssn\t%?hu\tppid\t%?d\n", sdh_int16, sdh_int32);
242 
243 	return (0);
244 }
245 
246 static int
247 sctp_sent_list(mblk_t *addr)
248 {
249 	mblk_t meta, mp;
250 
251 	if (!addr)
252 		return (0);
253 
254 	if (mdb_vread(&meta, sizeof (meta), (uintptr_t)addr) == -1)
255 		return (-1);
256 
257 	for (;;) {
258 		dump_msghdr(&meta);
259 
260 		if (meta.b_cont == NULL) {
261 			mdb_printf("No data chunks with message header!\n");
262 			return (-1);
263 		}
264 		if (mdb_vread(&mp, sizeof (mp),
265 			(uintptr_t)meta.b_cont) == -1) {
266 			return (-1);
267 		}
268 		for (;;) {
269 			dump_datahdr(&mp);
270 			if (!mp.b_next)
271 				break;
272 
273 			if (mdb_vread(&mp, sizeof (mp),
274 			    (uintptr_t)(mp.b_next)) == -1)
275 				return (-1);
276 		}
277 		if (meta.b_next == NULL)
278 			break;
279 		if (mdb_vread(&meta, sizeof (meta),
280 		    (uintptr_t)meta.b_next) == -1)
281 			return (-1);
282 	}
283 
284 	return (0);
285 }
286 
287 static int
288 sctp_unsent_list(mblk_t *addr)
289 {
290 	mblk_t meta;
291 
292 	if (!addr)
293 		return (0);
294 
295 	if (mdb_vread(&meta, sizeof (meta), (uintptr_t)addr) == -1)
296 		return (-1);
297 
298 	for (;;) {
299 		dump_msghdr(&meta);
300 
301 		if (meta.b_next == NULL)
302 			break;
303 
304 		if (mdb_vread(&meta, sizeof (meta),
305 		    (uintptr_t)meta.b_next) == -1)
306 			return (-1);
307 	}
308 
309 	return (0);
310 }
311 
312 /* ARGSUSED */
313 static int
314 sctp_xmit_list(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
315 {
316 	sctp_t sctp;
317 
318 	if (!(flags & DCMD_ADDRSPEC))
319 		return (DCMD_USAGE);
320 
321 	if (mdb_vread(&sctp, sizeof (sctp), addr) == -1)
322 		return (DCMD_ERR);
323 
324 	mdb_printf("%<b>Chunkified TX list%</b>\n");
325 	if (sctp_sent_list(sctp.sctp_xmit_head) < 0)
326 		return (DCMD_ERR);
327 
328 	mdb_printf("%<b>Unchunkified TX list%</b>\n");
329 	if (sctp_unsent_list(sctp.sctp_xmit_unsent) < 0)
330 		return (DCMD_ERR);
331 
332 	return (DCMD_OK);
333 }
334 
335 /* ARGSUSED */
336 static int
337 sctp_mdata_chunk(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
338 {
339 	sctp_data_hdr_t dc;
340 	mblk_t mp;
341 
342 	if (!(flags & DCMD_ADDRSPEC))
343 		return (DCMD_USAGE);
344 
345 	if (mdb_vread(&mp, sizeof (mp), addr) == -1)
346 		return (DCMD_ERR);
347 
348 	if (mdb_vread(&dc, sizeof (dc), (uintptr_t)mp.b_rptr) == -1)
349 		return (DCMD_ERR);
350 
351 	mdb_printf("%<b>%-?p%</b>tsn\t%?x\tsid\t%?hu\n", addr,
352 	    dc.sdh_tsn, dc.sdh_sid);
353 	mdb_printf("%-?sssn\t%?hu\tppid\t%?x\n", "", dc.sdh_ssn,
354 	    dc.sdh_payload_id);
355 
356 	return (DCMD_OK);
357 }
358 
359 /* ARGSUSED */
360 static int
361 sctp_istr_msgs(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
362 {
363 	mblk_t			istrmp;
364 	mblk_t			dmp;
365 	sctp_data_hdr_t 	dp;
366 	uintptr_t		daddr;
367 	uintptr_t		chaddr;
368 	boolean_t		bbit;
369 	boolean_t		ebit;
370 
371 	if (!(flags & DCMD_ADDRSPEC))
372 		return (DCMD_USAGE);
373 
374 	do {
375 		if (mdb_vread(&istrmp, sizeof (istrmp), addr) == -1)
376 			return (DCMD_ERR);
377 
378 		mdb_printf("\tistr mblk at %p: next: %?p\n"
379 		    "\t\tprev: %?p\tcont: %?p\n", addr, istrmp.b_next,
380 		    istrmp.b_prev, istrmp.b_cont);
381 		daddr = (uintptr_t)&istrmp;
382 		do {
383 			if (mdb_vread(&dmp, sizeof (dmp), daddr) == -1)
384 				break;
385 			chaddr = (uintptr_t)dmp.b_rptr;
386 			if (mdb_vread(&dp, sizeof (dp), chaddr) == -1)
387 				break;
388 
389 			bbit = (SCTP_DATA_GET_BBIT(&dp) != 0);
390 			ebit = (SCTP_DATA_GET_EBIT(&dp) != 0);
391 
392 			mdb_printf("\t\t\ttsn: %x  bbit: %d  ebit: %d\n",
393 			    dp.sdh_tsn, bbit, ebit);
394 
395 
396 			daddr = (uintptr_t)dmp.b_cont;
397 		} while (daddr != NULL);
398 
399 		addr = (uintptr_t)istrmp.b_next;
400 	} while (addr != NULL);
401 
402 	return (DCMD_OK);
403 }
404 
405 /* ARGSUSED */
406 static int
407 sctp_reass_list(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
408 {
409 	sctp_reass_t srp;
410 	mblk_t srpmp;
411 	sctp_data_hdr_t dp;
412 	mblk_t dmp;
413 	uintptr_t daddr;
414 	uintptr_t chaddr;
415 	boolean_t bbit, ebit;
416 
417 	if (!(flags & DCMD_ADDRSPEC))
418 		return (DCMD_USAGE);
419 
420 	do {
421 		if (mdb_vread(&srpmp, sizeof (srpmp), addr) == -1)
422 			return (DCMD_ERR);
423 
424 		if (mdb_vread(&srp, sizeof (srp),
425 		    (uintptr_t)srpmp.b_datap->db_base) == -1)
426 			return (DCMD_ERR);
427 
428 		mdb_printf("\treassembly mblk at %p: next: %?p\n"
429 		    "\t\tprev: %?p\tcont: %?p\n", addr, srpmp.b_next,
430 		    srpmp.b_prev, srpmp.b_cont);
431 		mdb_printf("\t\tssn: %hu\tneeded: %hu\tgot: %hu\ttail: %?p\n"
432 		    "\t\tpartial_delivered: %s\n", srp.ssn, srp.needed,
433 		    srp.got, srp.tail, srp.partial_delivered ? "TRUE" :
434 		    "FALSE");
435 
436 		/* display the contents of this ssn's reassemby list */
437 		daddr = DB_TYPE(&srpmp) == M_CTL ? (uintptr_t)srpmp.b_cont :
438 		    (uintptr_t)&srpmp;
439 		do {
440 			if (mdb_vread(&dmp, sizeof (dmp), daddr) == -1)
441 				break;
442 			chaddr = (uintptr_t)dmp.b_rptr;
443 			if (mdb_vread(&dp, sizeof (dp), chaddr) == -1)
444 				break;
445 
446 			bbit = (SCTP_DATA_GET_BBIT(&dp) != 0);
447 			ebit = (SCTP_DATA_GET_EBIT(&dp) != 0);
448 
449 			mdb_printf("\t\t\ttsn: %x  bbit: %d  ebit: %d\n",
450 			    dp.sdh_tsn, bbit, ebit);
451 
452 			daddr = (uintptr_t)dmp.b_cont;
453 		} while (daddr != NULL);
454 
455 		addr = (uintptr_t)srpmp.b_next;
456 	} while (addr != NULL);
457 
458 	return (DCMD_OK);
459 }
460 
461 /* ARGSUSED */
462 static int
463 sctp_uo_reass_list(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
464 {
465 	sctp_data_hdr_t	dp;
466 	mblk_t		dmp;
467 	uintptr_t	chaddr;
468 	boolean_t	bbit;
469 	boolean_t	ebit;
470 	boolean_t	ubit;
471 
472 	if (!(flags & DCMD_ADDRSPEC))
473 		return (DCMD_USAGE);
474 
475 	do {
476 		if (mdb_vread(&dmp, sizeof (dmp), addr) == -1)
477 			return (DCMD_ERR);
478 
479 		mdb_printf("\treassembly mblk at %p: next: %?p\n"
480 		    "\t\tprev: %?p\n", addr, dmp.b_next, dmp.b_prev);
481 
482 		chaddr = (uintptr_t)dmp.b_rptr;
483 		if (mdb_vread(&dp, sizeof (dp), chaddr) == -1)
484 			break;
485 
486 		bbit = (SCTP_DATA_GET_BBIT(&dp) != 0);
487 		ebit = (SCTP_DATA_GET_EBIT(&dp) != 0);
488 		ubit = (SCTP_DATA_GET_UBIT(&dp) != 0);
489 
490 		mdb_printf("\t\t\tsid: %hu ssn: %hu tsn: %x "
491 		    "flags: %x (U=%d B=%d E=%d)\n", dp.sdh_sid, dp.sdh_ssn,
492 		    dp.sdh_tsn, dp.sdh_flags, ubit, bbit, ebit);
493 
494 		addr = (uintptr_t)dmp.b_next;
495 	} while (addr != NULL);
496 
497 	return (DCMD_OK);
498 }
499 
500 static int
501 sctp_instr(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
502 {
503 	sctp_instr_t sip;
504 
505 	if (!(flags & DCMD_ADDRSPEC))
506 		return (DCMD_USAGE);
507 
508 	if (mdb_vread(&sip, sizeof (sip), addr) == -1)
509 		return (DCMD_ERR);
510 
511 	mdb_printf("%<b>%-?p%</b>\n\tmsglist\t%?p\tnmsgs\t%?d\n"
512 	    "\tnextseq\t%?d\treass\t%?p\n", addr, sip.istr_msgs,
513 	    sip.istr_nmsgs, sip.nextseq, sip.istr_reass);
514 	mdb_set_dot(addr + sizeof (sip));
515 
516 	return (sctp_reass_list((uintptr_t)sip.istr_reass, flags, ac, av));
517 }
518 
519 static const char *
520 state2str(sctp_t *sctp)
521 {
522 	switch (sctp->sctp_state) {
523 	case SCTPS_IDLE:		return ("SCTPS_IDLE");
524 	case SCTPS_BOUND:		return ("SCTPS_BOUND");
525 	case SCTPS_LISTEN:		return ("SCTPS_LISTEN");
526 	case SCTPS_COOKIE_WAIT:		return ("SCTPS_COOKIE_WAIT");
527 	case SCTPS_COOKIE_ECHOED:	return ("SCTPS_COOKIE_ECHOED");
528 	case SCTPS_ESTABLISHED:		return ("SCTPS_ESTABLISHED");
529 	case SCTPS_SHUTDOWN_PENDING:	return ("SCTPS_SHUTDOWN_PENDING");
530 	case SCTPS_SHUTDOWN_SENT:	return ("SCTPS_SHUTDOWN_SENT");
531 	case SCTPS_SHUTDOWN_RECEIVED:	return ("SCTPS_SHUTDOWN_RECEIVED");
532 	case SCTPS_SHUTDOWN_ACK_SENT:	return ("SCTPS_SHUTDOWN_ACK_SENT");
533 	default:			return ("UNKNOWN STATE");
534 	}
535 }
536 
537 static void
538 show_sctp_flags(sctp_t *sctp)
539 {
540 	mdb_printf("\tunderstands_asconf\t%d\n",
541 	    sctp->sctp_understands_asconf);
542 	mdb_printf("\tdebug\t\t\t%d\n", sctp->sctp_debug);
543 	mdb_printf("\tcchunk_pend\t\t%d\n", sctp->sctp_cchunk_pend);
544 	mdb_printf("\tdgram_errind\t\t%d\n", sctp->sctp_dgram_errind);
545 
546 	mdb_printf("\tlinger\t\t\t%d\n", sctp->sctp_linger);
547 	if (sctp->sctp_lingering)
548 		return;
549 	mdb_printf("\tlingering\t\t%d\n", sctp->sctp_lingering);
550 	mdb_printf("\tloopback\t\t%d\n", sctp->sctp_loopback);
551 	mdb_printf("\tforce_sack\t\t%d\n", sctp->sctp_force_sack);
552 
553 	mdb_printf("\tack_timer_runing\t%d\n", sctp->sctp_ack_timer_running);
554 	mdb_printf("\trecvdstaddr\t\t%d\n", sctp->sctp_recvdstaddr);
555 	mdb_printf("\thwcksum\t\t\t%d\n", sctp->sctp_hwcksum);
556 	mdb_printf("\tunderstands_addip\t%d\n", sctp->sctp_understands_addip);
557 
558 	mdb_printf("\tbound_to_all\t\t%d\n", sctp->sctp_bound_to_all);
559 	mdb_printf("\tcansleep\t\t%d\n", sctp->sctp_cansleep);
560 	mdb_printf("\tdetached\t\t%d\n", sctp->sctp_detached);
561 	mdb_printf("\tsend_adaption\t\t%d\n", sctp->sctp_send_adaption);
562 
563 	mdb_printf("\trecv_adaption\t\t%d\n", sctp->sctp_recv_adaption);
564 	mdb_printf("\tndelay\t\t\t%d\n", sctp->sctp_ndelay);
565 	mdb_printf("\tcondemned\t\t%d\n", sctp->sctp_condemned);
566 	mdb_printf("\tchk_fast_rexmit\t\t%d\n", sctp->sctp_chk_fast_rexmit);
567 
568 	mdb_printf("\tprsctp_aware\t\t%d\n", sctp->sctp_prsctp_aware);
569 	mdb_printf("\tlinklocal\t\t%d\n", sctp->sctp_linklocal);
570 	mdb_printf("\trexmitting\t\t%d\n", sctp->sctp_rexmitting);
571 	mdb_printf("\tzero_win_probe\t\t%d\n", sctp->sctp_zero_win_probe);
572 
573 	mdb_printf("\trecvsndrcvinfo\t\t%d\n", sctp->sctp_recvsndrcvinfo);
574 	mdb_printf("\trecvassocevnt\t\t%d\n", sctp->sctp_recvassocevnt);
575 	mdb_printf("\trecvpathevnt\t\t%d\n", sctp->sctp_recvpathevnt);
576 	mdb_printf("\trecvsendfailevnt\t%d\n", sctp->sctp_recvsendfailevnt);
577 
578 	mdb_printf("\trecvpeerevnt\t\t%d\n", sctp->sctp_recvpeererr);
579 	mdb_printf("\trecvchutdownevnt\t%d\n", sctp->sctp_recvshutdownevnt);
580 	mdb_printf("\trecvcpdnevnt\t\t%d\n", sctp->sctp_recvpdevnt);
581 	mdb_printf("\trecvcalevnt\t\t%d\n\n", sctp->sctp_recvalevnt);
582 }
583 
584 /*
585  * Given a sctp_saddr_ipif_t, print out its address.  This assumes
586  * that addr contains the sctp_addr_ipif_t structure already and this
587  * function does not need to read it in.
588  */
589 /* ARGSUSED */
590 static int
591 print_saddr(uintptr_t ptr, const void *addr, void *cbdata)
592 {
593 	sctp_saddr_ipif_t *saddr = (sctp_saddr_ipif_t *)addr;
594 	sctp_ipif_t ipif;
595 	char *statestr;
596 
597 	/* Read in the sctp_ipif object */
598 	if (mdb_vread(&ipif, sizeof (ipif), (uintptr_t)saddr->saddr_ipifp) ==
599 	    -1) {
600 		mdb_warn("cannot read ipif at %p", saddr->saddr_ipifp);
601 		return (WALK_ERR);
602 	}
603 
604 	switch (ipif.sctp_ipif_state) {
605 	case SCTP_IPIFS_CONDEMNED:
606 		statestr = "Condemned";
607 		break;
608 	case SCTP_IPIFS_INVALID:
609 		statestr = "Invalid";
610 		break;
611 	case SCTP_IPIFS_DOWN:
612 		statestr = "Down";
613 		break;
614 	case SCTP_IPIFS_UP:
615 		statestr = "Up";
616 		break;
617 	default:
618 		statestr = "Unknown";
619 		break;
620 	}
621 	mdb_printf("\t%p\t%N% (%s", saddr->saddr_ipifp, &ipif.sctp_ipif_saddr,
622 	    statestr);
623 	if (saddr->saddr_ipif_dontsrc == 1)
624 		mdb_printf("/Dontsrc");
625 	if (saddr->saddr_ipif_unconfirmed == 1)
626 		mdb_printf("/Unconfirmed");
627 	if (saddr->saddr_ipif_delete_pending == 1)
628 		mdb_printf("/DeletePending");
629 	mdb_printf(")\n");
630 	mdb_printf("\t\t\tMTU %d id %d zoneid %d IPIF flags %x\n",
631 	    ipif.sctp_ipif_mtu, ipif.sctp_ipif_id,
632 	    ipif.sctp_ipif_zoneid, ipif.sctp_ipif_flags);
633 	return (WALK_NEXT);
634 }
635 
636 /*
637  * Given a sctp_faddr_t, print out its address.  This assumes that
638  * addr contains the sctp_faddr_t structure already and this function
639  * does not need to read it in.
640  */
641 static int
642 print_faddr(uintptr_t ptr, const void *addr, void *cbdata)
643 {
644 	char	*statestr;
645 	sctp_faddr_t *faddr = (sctp_faddr_t *)addr;
646 	int *i = cbdata;
647 
648 	statestr = sctp_faddr_state(faddr->state);
649 
650 	mdb_printf("\t%d:\t%N\t%?p (%s)\n", (*i)++, &faddr->faddr, ptr,
651 	    statestr);
652 	return (WALK_NEXT);
653 }
654 
655 int
656 sctp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
657 {
658 	sctp_t sctp;
659 	conn_t connp;
660 	int i;
661 	uint_t opts = 0;
662 	uint_t paddr = 0;
663 	in_port_t lport, fport;
664 
665 	if (!(flags & DCMD_ADDRSPEC))
666 		return (DCMD_USAGE);
667 
668 	if (mdb_vread(&sctp, sizeof (sctp), addr) == -1) {
669 		mdb_warn("failed to read sctp_t at: %p\n", addr);
670 		return (DCMD_ERR);
671 	}
672 	if (mdb_vread(&connp, sizeof (connp),
673 	    (uintptr_t)sctp.sctp_connp) == -1) {
674 		mdb_warn("failed to read conn_t at: %p\n", sctp.sctp_connp);
675 		return (DCMD_ERR);
676 	}
677 
678 	if (mdb_getopts(argc, argv,
679 		'a', MDB_OPT_SETBITS, MDB_SCTP_SHOW_ALL, &opts,
680 		'f', MDB_OPT_SETBITS, MDB_SCTP_SHOW_FLAGS, &opts,
681 		'h', MDB_OPT_SETBITS, MDB_SCTP_SHOW_HASH, &opts,
682 		'o', MDB_OPT_SETBITS, MDB_SCTP_SHOW_OUT, &opts,
683 		'i', MDB_OPT_SETBITS, MDB_SCTP_SHOW_IN, &opts,
684 		'm', MDB_OPT_SETBITS, MDB_SCTP_SHOW_MISC, &opts,
685 		'r', MDB_OPT_SETBITS, MDB_SCTP_SHOW_RTT, &opts,
686 		'S', MDB_OPT_SETBITS, MDB_SCTP_SHOW_STATS, &opts,
687 		'F', MDB_OPT_SETBITS, MDB_SCTP_SHOW_FLOW, &opts,
688 		'H', MDB_OPT_SETBITS, MDB_SCTP_SHOW_HDR, &opts,
689 		'p', MDB_OPT_SETBITS, MDB_SCTP_SHOW_PMTUD, &opts,
690 		'R', MDB_OPT_SETBITS, MDB_SCTP_SHOW_RXT, &opts,
691 		'C', MDB_OPT_SETBITS, MDB_SCTP_SHOW_CONN, &opts,
692 		'c', MDB_OPT_SETBITS, MDB_SCTP_SHOW_CLOSE, &opts,
693 		'e', MDB_OPT_SETBITS, MDB_SCTP_SHOW_EXT, &opts,
694 		'P', MDB_OPT_SETBITS, 1, &paddr,
695 		'd', MDB_OPT_SETBITS, MDB_SCTP_DUMP_ADDRS, &opts) != argc) {
696 		return (DCMD_USAGE);
697 	}
698 
699 	/* non-verbose faddrs, suitable for pipelines to sctp_faddr */
700 	if (paddr != 0) {
701 		sctp_faddr_t faddr, *fp;
702 		for (fp = sctp.sctp_faddrs; fp != NULL; fp = faddr.next) {
703 			if (mdb_vread(&faddr, sizeof (faddr), (uintptr_t)fp)
704 			    == -1) {
705 				mdb_warn("failed to read faddr at %p",
706 				    fp);
707 				return (DCMD_ERR);
708 			}
709 			mdb_printf("%p\n", fp);
710 		}
711 		return (DCMD_OK);
712 	}
713 
714 	mdb_nhconvert(&lport, &sctp.sctp_lport, sizeof (lport));
715 	mdb_nhconvert(&fport, &sctp.sctp_fport, sizeof (fport));
716 	mdb_printf("%<u>%p% %22s S=%-6hu D=%-6hu% ZONE=%d%</u>", addr,
717 	    state2str(&sctp), lport, fport, connp.conn_zoneid);
718 
719 	if (sctp.sctp_faddrs) {
720 		sctp_faddr_t faddr;
721 		if (mdb_vread(&faddr, sizeof (faddr),
722 		    (uintptr_t)sctp.sctp_faddrs) != -1)
723 			mdb_printf("%<u> %N%</u>", &faddr.faddr);
724 	}
725 	mdb_printf("\n");
726 
727 	if (opts & MDB_SCTP_DUMP_ADDRS) {
728 		mdb_printf("%<b>Local and Peer Addresses%</b>\n");
729 
730 		/* Display source addresses */
731 		mdb_printf("nsaddrs\t\t%?d\n", sctp.sctp_nsaddrs);
732 		(void) mdb_pwalk("sctp_walk_saddr", print_saddr, NULL, addr);
733 
734 		/* Display peer addresses */
735 		mdb_printf("nfaddrs\t\t%?d\n", sctp.sctp_nfaddrs);
736 		i = 1;
737 		(void) mdb_pwalk("sctp_walk_faddr", print_faddr, &i, addr);
738 
739 		mdb_printf("lastfaddr\t%?p\tprimary\t\t%?p\n",
740 		    sctp.sctp_lastfaddr, sctp.sctp_primary);
741 		mdb_printf("current\t\t%?p\tlastdata\t%?p\n",
742 		    sctp.sctp_current, sctp.sctp_lastdata);
743 	}
744 
745 	if (opts & MDB_SCTP_SHOW_OUT) {
746 		mdb_printf("%<b>Outbound Data%</b>\n");
747 		mdb_printf("xmit_head\t%?p\txmit_tail\t%?p\n",
748 		    sctp.sctp_xmit_head, sctp.sctp_xmit_tail);
749 		mdb_printf("xmit_unsent\t%?p\txmit_unsent_tail%?p\n",
750 		    sctp.sctp_xmit_unsent, sctp.sctp_xmit_unsent_tail);
751 		mdb_printf("xmit_unacked\t%?p\n", sctp.sctp_xmit_unacked);
752 		mdb_printf("unacked\t\t%?u\tunsent\t\t%?ld\n",
753 		    sctp.sctp_unacked, sctp.sctp_unsent);
754 		mdb_printf("ltsn\t\t%?x\tlastack_rxd\t%?x\n",
755 		    sctp.sctp_ltsn, sctp.sctp_lastack_rxd);
756 		mdb_printf("recovery_tsn\t%?x\tadv_pap\t\t%?x\n",
757 		    sctp.sctp_recovery_tsn, sctp.sctp_adv_pap);
758 		mdb_printf("num_ostr\t%?hu\tostrcntrs\t%?p\n",
759 		    sctp.sctp_num_ostr, sctp.sctp_ostrcntrs);
760 
761 		mdb_printf("%<b>Default Send Parameters%</b>\n");
762 		mdb_printf("def_stream\t%?u\tdef_flags\t%?x\n",
763 		    sctp.sctp_def_stream, sctp.sctp_def_flags);
764 		mdb_printf("def_ppid\t%?x\tdef_context\t%?x\n",
765 		    sctp.sctp_def_ppid, sctp.sctp_def_context);
766 		mdb_printf("def_timetolive\t%?u\n",
767 		    sctp.sctp_def_timetolive);
768 	}
769 
770 	if (opts & MDB_SCTP_SHOW_IN) {
771 		mdb_printf("%<b>Inbound Data%</b>\n");
772 		mdb_printf("sack_info\t%?p\tsack_gaps\t%?d\n",
773 		    sctp.sctp_sack_info, sctp.sctp_sack_gaps);
774 		dump_sack_info((uintptr_t)sctp.sctp_sack_info);
775 		mdb_printf("ftsn\t\t%?x\tlastacked\t%?x\n",
776 		    sctp.sctp_ftsn, sctp.sctp_lastacked);
777 		mdb_printf("istr_nmsgs\t%?d\tsack_toggle\t%?d\n",
778 		    sctp.sctp_istr_nmsgs, sctp.sctp_sack_toggle);
779 		mdb_printf("ack_mp\t\t%?p\n", sctp.sctp_ack_mp);
780 		mdb_printf("num_istr\t%?hu\tinstr\t\t%?p\n",
781 		    sctp.sctp_num_istr, sctp.sctp_instr);
782 		mdb_printf("unord_reass\t%?p\n", sctp.sctp_uo_frags);
783 	}
784 
785 	if (opts & MDB_SCTP_SHOW_RTT) {
786 		mdb_printf("%<b>RTT Tracking%</b>\n");
787 		mdb_printf("rtt_tsn\t\t%?x\tout_time\t%?ld\n",
788 		    sctp.sctp_rtt_tsn, sctp.sctp_out_time);
789 	}
790 
791 	if (opts & MDB_SCTP_SHOW_FLOW) {
792 		mdb_printf("%<b>Flow Control%</b>\n");
793 		mdb_printf("txmit_hiwater\t%?d\n"
794 		    "xmit_lowater\t%?d\tfrwnd\t\t%?u\n"
795 		    "rwnd\t\t%?u\tinitial rwnd\t%?u\n"
796 		    "rxqueued\t%?u\tcwnd_max\t%?u\n", sctp.sctp_xmit_hiwater,
797 		    sctp.sctp_xmit_lowater, sctp.sctp_frwnd,
798 		    sctp.sctp_rwnd, sctp.sctp_irwnd, sctp.sctp_rxqueued,
799 		    sctp.sctp_cwnd_max);
800 	}
801 
802 	if (opts & MDB_SCTP_SHOW_HDR) {
803 		mdb_printf("%<b>Composite Headers%</b>\n");
804 		mdb_printf("iphc\t\t%?p\tiphc6\t\t%?p\n"
805 		    "iphc_len\t%?d\tiphc6_len\t%?d\n"
806 		    "hdr_len\t\t%?d\thdr6_len\t%?d\n"
807 		    "ipha\t\t%?p\tip6h\t\t%?p\n"
808 		    "ip_hdr_len\t%?d\tip_hdr6_len\t%?d\n"
809 		    "sctph\t\t%?p\tsctph6\t\t%?p\n"
810 		    "lvtag\t\t%?x\tfvtag\t\t%?x\n", sctp.sctp_iphc,
811 		    sctp.sctp_iphc6, sctp.sctp_iphc_len,
812 		    sctp.sctp_iphc6_len, sctp.sctp_hdr_len,
813 		    sctp.sctp_hdr6_len, sctp.sctp_ipha, sctp.sctp_ip6h,
814 		    sctp.sctp_ip_hdr_len, sctp.sctp_ip_hdr6_len,
815 		    sctp.sctp_sctph, sctp.sctp_sctph6, sctp.sctp_lvtag,
816 		    sctp.sctp_fvtag);
817 	}
818 
819 	if (opts & MDB_SCTP_SHOW_PMTUD) {
820 		mdb_printf("%<b>PMTUd%</b>\n");
821 		mdb_printf("last_mtu_probe\t%?ld\tmtu_probe_intvl\t%?ld\n"
822 		    "mss\t\t%?u\n",
823 		    sctp.sctp_last_mtu_probe, sctp.sctp_mtu_probe_intvl,
824 		    sctp.sctp_mss);
825 	}
826 
827 	if (opts & MDB_SCTP_SHOW_RXT) {
828 		mdb_printf("%<b>Retransmit Info%</b>\n");
829 		mdb_printf("cookie_mp\t%?p\tstrikes\t\t%?d\n"
830 		    "max_init_rxt\t%?d\tpa_max_rxt\t%?d\n"
831 		    "pp_max_rxt\t%?d\trto_max\t\t%?u\n"
832 		    "rto_min\t\t%?u\trto_initial\t%?u\n"
833 		    "init_rto_max\t%?u\n"
834 		    "rxt_nxttsn\t%?u\trxt_maxtsn\t%?u\n", sctp.sctp_cookie_mp,
835 		    sctp.sctp_strikes, sctp.sctp_max_init_rxt,
836 		    sctp.sctp_pa_max_rxt, sctp.sctp_pp_max_rxt,
837 		    sctp.sctp_rto_max, sctp.sctp_rto_min,
838 		    sctp.sctp_rto_initial, sctp.sctp_init_rto_max,
839 		    sctp.sctp_rxt_nxttsn, sctp.sctp_rxt_maxtsn);
840 	}
841 
842 	if (opts & MDB_SCTP_SHOW_CONN) {
843 		mdb_printf("%<b>Connection State%</b>\n");
844 		mdb_printf("last_secret_update%?ld\n",
845 		    sctp.sctp_last_secret_update);
846 
847 		mdb_printf("secret\t\t");
848 		for (i = 0; i < SCTP_SECRET_LEN; i++) {
849 			if (i % 2 == 0)
850 				mdb_printf("0x%02x", sctp.sctp_secret[i]);
851 			else
852 				mdb_printf("%02x ", sctp.sctp_secret[i]);
853 		}
854 		mdb_printf("\n");
855 		mdb_printf("old_secret\t");
856 		for (i = 0; i < SCTP_SECRET_LEN; i++) {
857 			if (i % 2 == 0)
858 				mdb_printf("0x%02x", sctp.sctp_old_secret[i]);
859 			else
860 				mdb_printf("%02x ", sctp.sctp_old_secret[i]);
861 		}
862 		mdb_printf("\n");
863 	}
864 
865 	if (opts & MDB_SCTP_SHOW_STATS) {
866 		mdb_printf("%<b>Stats Counters%</b>\n");
867 		mdb_printf("opkts\t\t%?llu\tobchunks\t%?llu\n"
868 		    "odchunks\t%?llu\toudchunks\t%?llu\n"
869 		    "rxtchunks\t%?llu\tT1expire\t%?lu\n"
870 		    "T2expire\t%?lu\tT3expire\t%?lu\n"
871 		    "msgcount\t%?llu\tprsctpdrop\t%?llu\n"
872 		    "AssocStartTime\t%?lu\n",
873 		    sctp.sctp_opkts, sctp.sctp_obchunks,
874 		    sctp.sctp_odchunks, sctp.sctp_oudchunks,
875 		    sctp.sctp_rxtchunks, sctp.sctp_T1expire,
876 		    sctp.sctp_T2expire, sctp.sctp_T3expire,
877 		    sctp.sctp_msgcount, sctp.sctp_prsctpdrop,
878 		    sctp.sctp_assoc_start_time);
879 		mdb_printf("ipkts\t\t%?llu\tibchunks\t%?llu\n"
880 		    "idchunks\t%?llu\tiudchunks\t%?llu\n"
881 		    "fragdmsgs\t%?llu\treassmsgs\t%?llu\n",
882 		    sctp.sctp_ipkts, sctp.sctp_ibchunks,
883 		    sctp.sctp_idchunks, sctp.sctp_iudchunks,
884 		    sctp.sctp_fragdmsgs, sctp.sctp_reassmsgs);
885 	}
886 
887 	if (opts & MDB_SCTP_SHOW_HASH) {
888 		mdb_printf("%<b>Hash Tables%</b>\n");
889 		mdb_printf("conn_hash_next\t%?p\t", sctp.sctp_conn_hash_next);
890 		mdb_printf("conn_hash_prev\t%?p\n", sctp.sctp_conn_hash_prev);
891 		mdb_printf("[ conn_hash bucket\t%?d ]\n",
892 		    SCTP_CONN_HASH(sctp.sctp_ports));
893 
894 		mdb_printf("listen_hash_next%?p\t",
895 		    sctp.sctp_listen_hash_next);
896 		mdb_printf("listen_hash_prev%?p\n",
897 		    sctp.sctp_listen_hash_prev);
898 		mdb_nhconvert(&lport, &sctp.sctp_lport, sizeof (lport));
899 		mdb_printf("[ listen_hash bucket\t%?d ]\n",
900 		    SCTP_LISTEN_HASH(lport));
901 
902 		mdb_printf("conn_tfp\t%?p\t", sctp.sctp_conn_tfp);
903 		mdb_printf("listen_tfp\t%?p\n", sctp.sctp_listen_tfp);
904 
905 		mdb_printf("bind_hash\t%?p\tptpbhn\t\t%?p\n",
906 		    sctp.sctp_bind_hash, sctp.sctp_ptpbhn);
907 		mdb_printf("bind_lockp\t%?p\n",
908 		    sctp.sctp_bind_lockp);
909 		mdb_printf("[ bind_hash bucket\t%?d ]\n",
910 		    SCTP_BIND_HASH(lport));
911 	}
912 
913 	if (opts & MDB_SCTP_SHOW_CLOSE) {
914 		mdb_printf("%<b>Cleanup / Close%</b>\n");
915 		mdb_printf("shutdown_faddr\t%?p\tclient_errno\t%?d\n"
916 		    "lingertime\t%?d\trefcnt\t\t%?hu\n",
917 		    sctp.sctp_shutdown_faddr, sctp.sctp_client_errno,
918 		    sctp.sctp_lingertime, sctp.sctp_refcnt);
919 	}
920 
921 	if (opts & MDB_SCTP_SHOW_MISC) {
922 		mdb_printf("%<b>Miscellaneous%</b>\n");
923 		mdb_printf("bound_if\t%?u\theartbeat_mp\t%?p\n"
924 		    "family\t\t%?u\tipversion\t%?hu\n"
925 		    "hb_interval\t%?u\tautoclose\t%?d\n"
926 		    "active\t\t%?ld\ttx_adaption_code%?x\n"
927 		    "rx_adaption_code%?x\ttimer_mp\t%?p\n",
928 		    sctp.sctp_bound_if, sctp.sctp_heartbeat_mp,
929 		    sctp.sctp_family, sctp.sctp_ipversion,
930 		    sctp.sctp_hb_interval, sctp.sctp_autoclose,
931 		    sctp.sctp_active, sctp.sctp_tx_adaption_code,
932 		    sctp.sctp_rx_adaption_code, sctp.sctp_timer_mp);
933 	}
934 
935 	if (opts & MDB_SCTP_SHOW_EXT) {
936 		mdb_printf("%<b>Extensions and Reliable Ctl Chunks%</b>\n");
937 		mdb_printf("cxmit_list\t%?p\tlcsn\t\t%?x\n"
938 		    "fcsn\t\t%?x\n", sctp.sctp_cxmit_list, sctp.sctp_lcsn,
939 		    sctp.sctp_fcsn);
940 	}
941 
942 	if (opts & MDB_SCTP_SHOW_FLAGS) {
943 		mdb_printf("%<b>Flags%</b>\n");
944 		show_sctp_flags(&sctp);
945 	}
946 
947 	return (DCMD_OK);
948 }
949 
950 typedef struct fanout_walk_data {
951 	int index;
952 	int size;
953 	uintptr_t sctp;
954 	sctp_tf_t *fanout;
955 	uintptr_t (*getnext)(sctp_t *);
956 } fanout_walk_data_t;
957 
958 typedef struct fanout_init {
959 	const char *symname;
960 	int (*getsize)();
961 	uintptr_t (*getnext)(sctp_t *);
962 } fanout_init_t;
963 
964 static uintptr_t
965 listen_next(sctp_t *sctp)
966 {
967 	return ((uintptr_t)sctp->sctp_listen_hash_next);
968 }
969 
970 static int
971 listen_size(void)
972 {
973 	return (SCTP_LISTEN_FANOUT_SIZE);
974 }
975 
976 static uintptr_t
977 conn_next(sctp_t *sctp)
978 {
979 	return ((uintptr_t)sctp->sctp_conn_hash_next);
980 }
981 
982 static int
983 conn_size(void)
984 {
985 	GElf_Sym sym;
986 	int size;
987 
988 	if (mdb_lookup_by_name("sctp_conn_hash_size", &sym) == -1) {
989 		mdb_warn("can't read 'sctp_conn_hash_size'");
990 		return (1);
991 	}
992 	if (mdb_vread(&size, sizeof (size), sym.st_value) == -1) {
993 		mdb_warn("can't dereference 'sctp_conn_hash_size'");
994 		return (1);
995 	}
996 	return (size);
997 }
998 
999 static uintptr_t
1000 bind_next(sctp_t *sctp)
1001 {
1002 	return ((uintptr_t)sctp->sctp_bind_hash);
1003 }
1004 
1005 static int
1006 bind_size(void)
1007 {
1008 	return (SCTP_BIND_FANOUT_SIZE);
1009 }
1010 
1011 static intptr_t
1012 find_next_hash_item(fanout_walk_data_t *fw)
1013 {
1014 	sctp_tf_t tf;
1015 	sctp_t sctp;
1016 
1017 	/* first try to continue down the hash chain */
1018 	if (fw->sctp != NULL) {
1019 		/* try to get next in hash chain */
1020 		if (mdb_vread(&sctp, sizeof (sctp), fw->sctp) == -1) {
1021 			mdb_warn("failed to read sctp at %p", fw->sctp);
1022 			return (NULL);
1023 		}
1024 		fw->sctp = fw->getnext(&sctp);
1025 		if (fw->sctp != NULL)
1026 			return (fw->sctp);
1027 		else
1028 			/* end of chain; go to next bucket */
1029 			fw->index++;
1030 	}
1031 
1032 	/* find a new hash chain, traversing the buckets */
1033 	for (; fw->index < fw->size; fw->index++) {
1034 		/* read the current hash line for an sctp */
1035 		if (mdb_vread(&tf, sizeof (tf),
1036 			(uintptr_t)(fw->fanout + fw->index)) == -1) {
1037 			mdb_warn("failed to read tf at %p",
1038 			    fw->fanout + fw->index);
1039 			return (NULL);
1040 		}
1041 		if (tf.tf_sctp != NULL) {
1042 			/* start of a new chain */
1043 			fw->sctp = (uintptr_t)tf.tf_sctp;
1044 			return (fw->sctp);
1045 		}
1046 	}
1047 	return (NULL);
1048 }
1049 
1050 static int
1051 fanout_walk_init(mdb_walk_state_t *wsp)
1052 {
1053 	GElf_Sym sym;
1054 	fanout_walk_data_t *lw;
1055 	fanout_init_t *fi = wsp->walk_arg;
1056 
1057 	if (mdb_lookup_by_name(fi->symname, &sym) == -1) {
1058 		mdb_warn("failed to read '%s'", fi->symname);
1059 		return (WALK_ERR);
1060 	}
1061 
1062 	lw = mdb_alloc(sizeof (*lw), UM_SLEEP);
1063 	lw->index = 0;
1064 	lw->size = fi->getsize();
1065 	lw->sctp = NULL;
1066 	lw->fanout = (sctp_tf_t *)(uintptr_t)sym.st_value;
1067 	lw->getnext = fi->getnext;
1068 
1069 	if ((wsp->walk_addr = find_next_hash_item(lw)) == NULL) {
1070 		return (WALK_DONE);
1071 	}
1072 	wsp->walk_data = lw;
1073 	return (WALK_NEXT);
1074 }
1075 
1076 static int
1077 fanout_walk_step(mdb_walk_state_t *wsp)
1078 {
1079 	fanout_walk_data_t *fw = wsp->walk_data;
1080 	uintptr_t addr = wsp->walk_addr;
1081 	sctp_t sctp;
1082 	int status;
1083 
1084 	if (mdb_vread(&sctp, sizeof (sctp), addr) == -1) {
1085 		mdb_warn("failed to read sctp at %p", addr);
1086 		return (WALK_DONE);
1087 	}
1088 
1089 	status = wsp->walk_callback(addr, &sctp, wsp->walk_cbdata);
1090 	if (status != WALK_NEXT)
1091 		return (status);
1092 
1093 	if ((wsp->walk_addr = find_next_hash_item(fw)) == NULL)
1094 		return (WALK_DONE);
1095 
1096 	return (WALK_NEXT);
1097 }
1098 
1099 static void
1100 fanout_walk_fini(mdb_walk_state_t *wsp)
1101 {
1102 	fanout_walk_data_t *fw = wsp->walk_data;
1103 
1104 	mdb_free(fw, sizeof (*fw));
1105 }
1106 
1107 static int
1108 sctp_walk_init(mdb_walk_state_t *wsp)
1109 {
1110 	wsp->walk_addr = (uintptr_t)list_object(&sctp_list,
1111 	    sctp_list.list_head.list_next);
1112 	return (WALK_NEXT);
1113 }
1114 
1115 static int
1116 sctp_walk_step(mdb_walk_state_t *wsp)
1117 {
1118 	uintptr_t psctp = wsp->walk_addr;
1119 	sctp_t sctp;
1120 	int status;
1121 
1122 	if (mdb_vread(&sctp, sizeof (sctp), psctp) == -1) {
1123 		mdb_warn("failed to read sctp at %p", psctp);
1124 		return (WALK_ERR);
1125 	}
1126 	status = wsp->walk_callback(psctp, &sctp, wsp->walk_cbdata);
1127 	if (status != WALK_NEXT)
1128 		return (status);
1129 
1130 	if ((psctp = (uintptr_t)sctp.sctp_list.list_next) ==
1131 	    sctp_list_sym.st_value + OFFSETOF(list_t, list_head)) {
1132 		return (WALK_DONE);
1133 	} else {
1134 		wsp->walk_addr = (uintptr_t)list_object(&sctp_list, psctp);
1135 		return (WALK_NEXT);
1136 	}
1137 }
1138 
1139 static int
1140 sctp_walk_faddr_init(mdb_walk_state_t *wsp)
1141 {
1142 	sctp_t sctp;
1143 
1144 	if (wsp->walk_addr == NULL)
1145 		return (WALK_ERR);
1146 
1147 	if (mdb_vread(&sctp, sizeof (sctp), wsp->walk_addr) == -1) {
1148 		mdb_warn("failed to read sctp at %p", wsp->walk_addr);
1149 		return (WALK_ERR);
1150 	}
1151 	if ((wsp->walk_addr = (uintptr_t)sctp.sctp_faddrs) != NULL)
1152 		return (WALK_NEXT);
1153 	else
1154 		return (WALK_DONE);
1155 }
1156 
1157 static int
1158 sctp_walk_faddr_step(mdb_walk_state_t *wsp)
1159 {
1160 	uintptr_t faddr_ptr = wsp->walk_addr;
1161 	sctp_faddr_t sctp_faddr;
1162 	int status;
1163 
1164 	if (mdb_vread(&sctp_faddr, sizeof (sctp_faddr_t), faddr_ptr) == -1) {
1165 		mdb_warn("failed to read sctp_faddr_t at %p", faddr_ptr);
1166 		return (WALK_ERR);
1167 	}
1168 	status = wsp->walk_callback(faddr_ptr, &sctp_faddr, wsp->walk_cbdata);
1169 	if (status != WALK_NEXT)
1170 		return (status);
1171 	if ((faddr_ptr = (uintptr_t)sctp_faddr.next) == NULL) {
1172 		return (WALK_DONE);
1173 	} else {
1174 		wsp->walk_addr = faddr_ptr;
1175 		return (WALK_NEXT);
1176 	}
1177 }
1178 
1179 /*
1180  * Helper structure for sctp_walk_saddr.  It stores the sctp_t being walked,
1181  * the current index to the sctp_saddrs[], and the current count of the
1182  * sctp_saddr_ipif_t list.
1183  */
1184 typedef struct {
1185 	sctp_t	sctp;
1186 	int	hash_index;
1187 	int	cur_cnt;
1188 } saddr_walk_t;
1189 
1190 static int
1191 sctp_walk_saddr_init(mdb_walk_state_t *wsp)
1192 {
1193 	sctp_t *sctp;
1194 	int i;
1195 	saddr_walk_t *swalker;
1196 
1197 	if (wsp->walk_addr == NULL)
1198 		return (WALK_ERR);
1199 
1200 	swalker = mdb_alloc(sizeof (saddr_walk_t), UM_SLEEP);
1201 	sctp = &swalker->sctp;
1202 	if (mdb_vread(sctp, sizeof (sctp_t), wsp->walk_addr) == -1) {
1203 		mdb_warn("failed to read sctp at %p", wsp->walk_addr);
1204 		mdb_free(swalker, sizeof (saddr_walk_t));
1205 		return (WALK_ERR);
1206 	}
1207 
1208 	/* Find the first source address. */
1209 	for (i = 0; i < SCTP_IPIF_HASH; i++) {
1210 		if (sctp->sctp_saddrs[i].ipif_count > 0) {
1211 			list_t *addr_list;
1212 
1213 			addr_list = &sctp->sctp_saddrs[i].sctp_ipif_list;
1214 			wsp->walk_addr = (uintptr_t)list_object(addr_list,
1215 			    addr_list->list_head.list_next);
1216 
1217 			/* Recode the current info */
1218 			swalker->hash_index = i;
1219 			swalker->cur_cnt = 1;
1220 			wsp->walk_data = swalker;
1221 
1222 			return (WALK_NEXT);
1223 		}
1224 	}
1225 	return (WALK_DONE);
1226 }
1227 
1228 static int
1229 sctp_walk_saddr_step(mdb_walk_state_t *wsp)
1230 {
1231 	uintptr_t saddr_ptr = wsp->walk_addr;
1232 	sctp_saddr_ipif_t saddr;
1233 	saddr_walk_t *swalker;
1234 	sctp_t *sctp;
1235 	int status;
1236 	int i, j;
1237 
1238 	if (mdb_vread(&saddr, sizeof (sctp_saddr_ipif_t), saddr_ptr) == -1) {
1239 		mdb_warn("failed to read sctp_saddr_ipif_t at %p", saddr_ptr);
1240 		return (WALK_ERR);
1241 	}
1242 	status = wsp->walk_callback(saddr_ptr, &saddr, wsp->walk_cbdata);
1243 	if (status != WALK_NEXT)
1244 		return (status);
1245 
1246 	swalker = (saddr_walk_t *)wsp->walk_data;
1247 	sctp = &swalker->sctp;
1248 	i = swalker->hash_index;
1249 	j = swalker->cur_cnt;
1250 
1251 	/*
1252 	 * If there is still a source address in the current list, return it.
1253 	 * Otherwise, go to the next list in the sctp_saddrs[].
1254 	 */
1255 	if (j++ < sctp->sctp_saddrs[i].ipif_count) {
1256 		wsp->walk_addr = (uintptr_t)saddr.saddr_ipif.list_next;
1257 		swalker->cur_cnt = j;
1258 		return (WALK_NEXT);
1259 	} else {
1260 		list_t *lst;
1261 
1262 		for (i = i + 1; i < SCTP_IPIF_HASH; i++) {
1263 			if (sctp->sctp_saddrs[i].ipif_count > 0) {
1264 				lst = &sctp->sctp_saddrs[i].sctp_ipif_list;
1265 				wsp->walk_addr = (uintptr_t)list_object(
1266 				    lst, lst->list_head.list_next);
1267 				swalker->hash_index = i;
1268 				swalker->cur_cnt = 1;
1269 				return (WALK_NEXT);
1270 			}
1271 		}
1272 	}
1273 	return (WALK_DONE);
1274 }
1275 
1276 static void
1277 sctp_walk_saddr_fini(mdb_walk_state_t *wsp)
1278 {
1279 	saddr_walk_t *swalker = (saddr_walk_t *)wsp->walk_data;
1280 
1281 	mdb_free(swalker, sizeof (saddr_walk_t));
1282 }
1283 
1284 static int
1285 sctp_walk_ill_init(mdb_walk_state_t *wsp)
1286 {
1287 	intptr_t i;
1288 
1289 	/* Find the first ill. */
1290 	for (i = 0; i < SCTP_ILL_HASH; i++) {
1291 		if (local_g_ills[i].ill_count > 0) {
1292 			wsp->walk_addr = (uintptr_t)list_object(
1293 			    &local_g_ills[i].sctp_ill_list,
1294 			    local_g_ills[i].sctp_ill_list.list_head.list_next);
1295 			wsp->walk_data = (void *)i;
1296 			wsp->walk_arg = (void *)1;
1297 			return (WALK_NEXT);
1298 		}
1299 	}
1300 	return (WALK_DONE);
1301 }
1302 
1303 static int
1304 sctp_walk_ill_step(mdb_walk_state_t *wsp)
1305 {
1306 	uintptr_t ill_ptr = wsp->walk_addr;
1307 	sctp_ill_t ill;
1308 	int status;
1309 	intptr_t i, j;
1310 
1311 	if (mdb_vread(&ill, sizeof (sctp_ill_t), ill_ptr) == -1) {
1312 		mdb_warn("failed to read sctp_ill_t at %p", ill_ptr);
1313 		return (WALK_ERR);
1314 	}
1315 	status = wsp->walk_callback(ill_ptr, &ill, wsp->walk_cbdata);
1316 	if (status != WALK_NEXT)
1317 		return (status);
1318 
1319 	i = (intptr_t)wsp->walk_data;
1320 	j = (intptr_t)wsp->walk_arg;
1321 
1322 	/*
1323 	 * If there is still an ill in the current list, return it.
1324 	 * Otherwise, go to the next list and find another one.
1325 	 */
1326 	if (j++ < local_g_ills[i].ill_count) {
1327 		wsp->walk_addr = (uintptr_t)ill.sctp_ills.list_next;
1328 		wsp->walk_data = (void *)i;
1329 		wsp->walk_arg = (void *)j;
1330 		return (WALK_NEXT);
1331 	} else {
1332 		list_t *ill_list;
1333 
1334 		for (i = i + 1; i < SCTP_ILL_HASH; i++) {
1335 			if (local_g_ills[i].ill_count > 0) {
1336 				ill_list = &local_g_ills[i].sctp_ill_list;
1337 				wsp->walk_addr = (uintptr_t)list_object(
1338 				    ill_list, ill_list->list_head.list_next);
1339 
1340 				/* Record the current position. */
1341 				wsp->walk_data = (void *)i;
1342 				wsp->walk_arg = (void *)1;
1343 				return (WALK_NEXT);
1344 			}
1345 		}
1346 	}
1347 	return (WALK_DONE);
1348 }
1349 
1350 static int
1351 sctp_walk_ipif_init(mdb_walk_state_t *wsp)
1352 {
1353 	intptr_t i;
1354 	list_t *ipif_list;
1355 
1356 	for (i = 0; i < SCTP_IPIF_HASH; i++) {
1357 		if (local_g_ipifs[i].ipif_count > 0) {
1358 			ipif_list = &local_g_ipifs[i].sctp_ipif_list;
1359 
1360 			wsp->walk_addr = (uintptr_t)list_object(ipif_list,
1361 			    ipif_list->list_head.list_next);
1362 			wsp->walk_data = (void *)i;
1363 			wsp->walk_arg = (void *)1;
1364 			return (WALK_NEXT);
1365 		}
1366 	}
1367 	return (WALK_DONE);
1368 }
1369 
1370 static int
1371 sctp_walk_ipif_step(mdb_walk_state_t *wsp)
1372 {
1373 	uintptr_t ipif_ptr = wsp->walk_addr;
1374 	sctp_ipif_t ipif;
1375 	int status;
1376 	intptr_t i, j;
1377 
1378 	if (mdb_vread(&ipif, sizeof (sctp_ipif_t), ipif_ptr) == -1) {
1379 		mdb_warn("failed to read sctp_ipif_t at %p", ipif_ptr);
1380 		return (WALK_ERR);
1381 	}
1382 	status = wsp->walk_callback(ipif_ptr, &ipif, wsp->walk_cbdata);
1383 	if (status != WALK_NEXT)
1384 		return (status);
1385 
1386 	i = (intptr_t)wsp->walk_data;
1387 	j = (intptr_t)wsp->walk_arg;
1388 
1389 	/*
1390 	 * If there is still an ipif in the current list, return it.
1391 	 * Otherwise, go to the next list and find another one.
1392 	 */
1393 	if (j++ < local_g_ipifs[i].ipif_count) {
1394 		wsp->walk_addr = (uintptr_t)ipif.sctp_ipifs.list_next;
1395 		wsp->walk_data = (void *)i;
1396 		wsp->walk_arg = (void *)j;
1397 		return (WALK_NEXT);
1398 	} else {
1399 		list_t *ipif_list;
1400 
1401 		for (i = i + 1; i < SCTP_IPIF_HASH; i++) {
1402 			if (local_g_ipifs[i].ipif_count > 0) {
1403 				ipif_list = &local_g_ipifs[i].sctp_ipif_list;
1404 				wsp->walk_addr = (uintptr_t)list_object(
1405 				    ipif_list, ipif_list->list_head.list_next);
1406 
1407 				/* Record the current position */
1408 				wsp->walk_data = (void *)i;
1409 				wsp->walk_arg = (void *)1;
1410 				return (WALK_NEXT);
1411 			}
1412 		}
1413 	}
1414 	return (WALK_DONE);
1415 }
1416 
1417 static void
1418 sctp_help(void)
1419 {
1420 	mdb_printf("Print information for a given SCTP sctp_t\n\n");
1421 	mdb_printf("Options:\n");
1422 	mdb_printf("\t-a\t All the information\n");
1423 	mdb_printf("\t-f\t Flags\n");
1424 	mdb_printf("\t-h\t Hash Tables\n");
1425 	mdb_printf("\t-o\t Outbound Data\n");
1426 	mdb_printf("\t-i\t Inbound Data\n");
1427 	mdb_printf("\t-m\t Miscellaneous Information\n");
1428 	mdb_printf("\t-r\t RTT Tracking\n");
1429 	mdb_printf("\t-S\t Stats Counters\n");
1430 	mdb_printf("\t-F\t Flow Control\n");
1431 	mdb_printf("\t-H\t Composite Headers\n");
1432 	mdb_printf("\t-p\t PMTUD\n");
1433 	mdb_printf("\t-R\t Retransmit Information\n");
1434 	mdb_printf("\t-C\t Connection State\n");
1435 	mdb_printf("\t-c\t Cleanup / Close\n");
1436 	mdb_printf("\t-e\t Extensions and Reliable Control Chunks\n");
1437 	mdb_printf("\t-d\t Local and Peer addresses\n");
1438 	mdb_printf("\t-P\t Peer addresses\n");
1439 }
1440 static const mdb_dcmd_t dcmds[] = {
1441 	{ "sctp", ":[-afhoimrSFHpRCcedP]",
1442 	    "display sctp control structure", sctp, sctp_help },
1443 	{ "sctp_set", ":", "display a SCTP set", sctp_set },
1444 	{ "sctp_faddr", ":", "display a faddr", sctp_faddr },
1445 	{ "sctp_istr_msgs", ":", "display msg list on an instream",
1446 	    sctp_istr_msgs },
1447 	{ "sctp_mdata_chunk", ":", "display a data chunk in an mblk",
1448 	    sctp_mdata_chunk },
1449 	{ "sctp_xmit_list", ":", "display sctp xmit lists", sctp_xmit_list },
1450 	{ "sctp_instr", ":", "display instr", sctp_instr },
1451 	{ "sctp_reass_list", ":", "display reass list", sctp_reass_list },
1452 	{ "sctp_uo_reass_list", ":", "display un-ordered reass list",
1453 	    sctp_uo_reass_list },
1454 	{ NULL }
1455 };
1456 
1457 static const fanout_init_t listen_fanout_init = {
1458 	"sctp_listen_fanout", listen_size, listen_next
1459 };
1460 
1461 static const fanout_init_t conn_fanout_init = {
1462 	"sctp_conn_fanout", conn_size, conn_next
1463 };
1464 
1465 static const fanout_init_t bind_fanout_init = {
1466 	"sctp_bind_fanout", bind_size, bind_next
1467 };
1468 
1469 static const mdb_walker_t walkers[] = {
1470 	{ "sctps", "walk the full chain of sctps",
1471 	    sctp_walk_init, sctp_walk_step, NULL },
1472 	{ "sctp_listen_fanout", "walk the sctp listen fanout",
1473 	    fanout_walk_init, fanout_walk_step, fanout_walk_fini,
1474 	    (void *)&listen_fanout_init },
1475 	{ "sctp_conn_fanout", "walk the sctp conn fanout",
1476 	    fanout_walk_init, fanout_walk_step, fanout_walk_fini,
1477 	    (void *)&conn_fanout_init },
1478 	{ "sctp_bind_fanout", "walk the sctp bind fanout",
1479 	    fanout_walk_init, fanout_walk_step, fanout_walk_fini,
1480 	    (void *)&bind_fanout_init },
1481 	{ "sctp_walk_faddr", "walk the peer address list of a given sctp_t",
1482 	    sctp_walk_faddr_init, sctp_walk_faddr_step, NULL },
1483 	{ "sctp_walk_saddr", "walk the local address list of a given sctp_t",
1484 	    sctp_walk_saddr_init, sctp_walk_saddr_step, sctp_walk_saddr_fini },
1485 	{ "sctp_walk_ill", "walk the sctp_g_ills list",
1486 	    sctp_walk_ill_init, sctp_walk_ill_step, NULL },
1487 	{ "sctp_walk_ipif", "walk the sctp_g_ipif list",
1488 	    sctp_walk_ipif_init, sctp_walk_ipif_step, NULL },
1489 	{ NULL }
1490 };
1491 
1492 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
1493 
1494 const mdb_modinfo_t *
1495 _mdb_init(void)
1496 {
1497 	GElf_Sym sym;
1498 	GElf_Sym ills_sym;
1499 	GElf_Sym ipifs_sym;
1500 
1501 	if (mdb_lookup_by_name("sctp_g_list", &sctp_list_sym) == -1) {
1502 		mdb_warn("failed to find 'sctp_g_list'");
1503 		return (NULL);
1504 	}
1505 	if (mdb_vread(&sctp_list, sizeof (list_t),
1506 	    (uintptr_t)sctp_list_sym.st_value) == -1) {
1507 		mdb_warn("failed to read 'sctp_g_list'");
1508 		return (NULL);
1509 	}
1510 	if (mdb_lookup_by_name("sctp_conn_hash_size", &sym) != -1) {
1511 		if (mdb_vread(&sctp_conn_hash_size,
1512 		    sizeof (sctp_conn_hash_size), sym.st_value) == -1) {
1513 			mdb_warn("failed to read 'sctp_conn_hash_size'");
1514 			return (NULL);
1515 		}
1516 	}
1517 	if (mdb_lookup_by_name("sctp_g_ills", &ills_sym) == -1) {
1518 		mdb_warn("failed to find 'sctp_g_ills'");
1519 		return (NULL);
1520 	}
1521 	if (mdb_vread(&local_g_ills, sizeof (local_g_ills),
1522 	    (uintptr_t)ills_sym.st_value) == -1) {
1523 		mdb_warn("failed to read 'sctp_g_ills'");
1524 		return (NULL);
1525 	}
1526 	if (mdb_lookup_by_name("sctp_g_ipifs", &ipifs_sym) == -1) {
1527 		mdb_warn("failed to find 'sctp_g_ipifs'");
1528 		return (NULL);
1529 	}
1530 	if (mdb_vread(&local_g_ipifs, sizeof (local_g_ipifs),
1531 	    (uintptr_t)ipifs_sym.st_value) == -1) {
1532 		mdb_warn("failed to read 'sctp_g_ipifs'");
1533 		return (NULL);
1534 	}
1535 
1536 	return (&modinfo);
1537 }
1538