xref: /illumos-gate/usr/src/cmd/mdb/common/modules/ip/ip.c (revision 6a8288c722a74d7a3058a4605849927f2ed7759c)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <sys/stropts.h>
30 #include <sys/stream.h>
31 #include <sys/socket.h>
32 #include <sys/avl_impl.h>
33 #include <net/if.h>
34 #include <net/route.h>
35 #include <netinet/in.h>
36 #include <netinet/ip6.h>
37 #include <netinet/udp.h>
38 #include <netinet/sctp.h>
39 #include <inet/mib2.h>
40 #include <inet/common.h>
41 #include <inet/ip.h>
42 #include <inet/ip_ire.h>
43 #include <inet/ip6.h>
44 #include <inet/ipclassifier.h>
45 #include <inet/mi.h>
46 #include <sys/squeue_impl.h>
47 #include <sys/modhash_impl.h>
48 
49 #include <mdb/mdb_modapi.h>
50 #include <mdb/mdb_ks.h>
51 
52 #define	ADDR_WIDTH 11
53 
54 typedef struct {
55 	const char *bit_name;	/* name of bit */
56 	const char *bit_descr;	/* description of bit's purpose */
57 } bitname_t;
58 
59 static const bitname_t squeue_states[] = {
60 	{ "SQS_PROC",		"being processed" },
61 	{ "SQS_WORKER",		"... by a worker thread" },
62 	{ "SQS_ENTER",		"... by an squeue_enter() thread" },
63 	{ "SQS_FAST",		"... in fast-path mode" },
64 	{ "SQS_USER", 		"A non interrupt user" },
65 	{ "SQS_BOUND",		"worker thread bound to CPU" },
66 	{ "SQS_PROFILE",	"profiling enabled" },
67 	{ "SQS_REENTER",	"re-entered thred" },
68 	{ NULL }
69 };
70 
71 typedef struct illif_walk_data {
72 	ill_g_head_t ill_g_heads[MAX_G_HEADS];
73 	int ill_list;
74 	ill_if_t ill_if;
75 } illif_walk_data_t;
76 
77 typedef struct th_walk_data {
78 	uint_t		thw_non_zero_only;
79 	boolean_t	thw_match;
80 	uintptr_t	thw_matchkey;
81 	uintptr_t	thw_ipst;
82 	clock_t		thw_lbolt;
83 } th_walk_data_t;
84 
85 static int iphdr(uintptr_t, uint_t, int, const mdb_arg_t *);
86 static int ip6hdr(uintptr_t, uint_t, int, const mdb_arg_t *);
87 
88 static int ire_format(uintptr_t addr, const ire_t *irep, uint_t *verbose);
89 
90 /*
91  * Given the kernel address of an ip_stack_t, return the stackid
92  */
93 static int
94 ips_to_stackid(uintptr_t kaddr)
95 {
96 	ip_stack_t ipss;
97 	netstack_t nss;
98 
99 	if (mdb_vread(&ipss, sizeof (ipss), kaddr) == -1) {
100 		mdb_warn("failed to read ip_stack_t %p", kaddr);
101 		return (0);
102 	}
103 	kaddr = (uintptr_t)ipss.ips_netstack;
104 	if (mdb_vread(&nss, sizeof (nss), kaddr) == -1) {
105 		mdb_warn("failed to read netstack_t %p", kaddr);
106 		return (0);
107 	}
108 	return (nss.netstack_stackid);
109 }
110 
111 int
112 ip_stacks_walk_init(mdb_walk_state_t *wsp)
113 {
114 	if (mdb_layered_walk("netstack", wsp) == -1) {
115 		mdb_warn("can't walk 'netstack'");
116 		return (WALK_ERR);
117 	}
118 	return (WALK_NEXT);
119 }
120 
121 int
122 ip_stacks_walk_step(mdb_walk_state_t *wsp)
123 {
124 	uintptr_t kaddr;
125 	netstack_t nss;
126 
127 #ifdef DEBUG
128 	mdb_printf("DEBUG: ip_stacks_walk_step: addr %p\n", wsp->walk_addr);
129 #endif
130 	if (mdb_vread(&nss, sizeof (nss), wsp->walk_addr) == -1) {
131 		mdb_warn("can't read netstack at %p", wsp->walk_addr);
132 		return (WALK_ERR);
133 	}
134 	kaddr = (uintptr_t)nss.netstack_modules[NS_IP];
135 
136 #ifdef DEBUG
137 	mdb_printf("DEBUG: ip_stacks_walk_step: ip_stack_t at %p\n", kaddr);
138 #endif
139 	return (wsp->walk_callback(kaddr, wsp->walk_layer, wsp->walk_cbdata));
140 }
141 
142 int
143 th_hash_walk_init(mdb_walk_state_t *wsp)
144 {
145 	GElf_Sym sym;
146 	list_node_t *next;
147 
148 	if (wsp->walk_addr == NULL) {
149 		if (mdb_lookup_by_obj("ip", "ip_thread_list", &sym) == 0) {
150 			wsp->walk_addr = sym.st_value;
151 		} else {
152 			mdb_warn("unable to locate ip_thread_list\n");
153 			return (WALK_ERR);
154 		}
155 	}
156 
157 	if (mdb_vread(&next, sizeof (next),
158 	    wsp->walk_addr + offsetof(list_t, list_head) +
159 	    offsetof(list_node_t, list_next)) == -1 ||
160 	    next == NULL) {
161 		mdb_warn("non-DEBUG image; cannot walk th_hash list\n");
162 		return (WALK_ERR);
163 	}
164 
165 	if (mdb_layered_walk("list", wsp) == -1) {
166 		mdb_warn("can't walk 'list'");
167 		return (WALK_ERR);
168 	} else {
169 		return (WALK_NEXT);
170 	}
171 }
172 
173 int
174 th_hash_walk_step(mdb_walk_state_t *wsp)
175 {
176 	return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer,
177 	    wsp->walk_cbdata));
178 }
179 
180 /*
181  * Called with walk_addr being the address of ips_ill_g_heads
182  */
183 int
184 illif_stack_walk_init(mdb_walk_state_t *wsp)
185 {
186 	illif_walk_data_t *iw;
187 
188 	if (wsp->walk_addr == NULL) {
189 		mdb_warn("illif_stack supports only local walks\n");
190 		return (WALK_ERR);
191 	}
192 
193 	iw = mdb_alloc(sizeof (illif_walk_data_t), UM_SLEEP);
194 
195 	if (mdb_vread(iw->ill_g_heads, MAX_G_HEADS * sizeof (ill_g_head_t),
196 	    wsp->walk_addr) == -1) {
197 		mdb_warn("failed to read 'ips_ill_g_heads' at %p",
198 		    wsp->walk_addr);
199 		mdb_free(iw, sizeof (illif_walk_data_t));
200 		return (WALK_ERR);
201 	}
202 
203 	iw->ill_list = 0;
204 	wsp->walk_addr = (uintptr_t)iw->ill_g_heads[0].ill_g_list_head;
205 	wsp->walk_data = iw;
206 
207 	return (WALK_NEXT);
208 }
209 
210 int
211 illif_stack_walk_step(mdb_walk_state_t *wsp)
212 {
213 	uintptr_t addr = wsp->walk_addr;
214 	illif_walk_data_t *iw = wsp->walk_data;
215 	int list = iw->ill_list;
216 
217 	if (mdb_vread(&iw->ill_if, sizeof (ill_if_t), addr) == -1) {
218 		mdb_warn("failed to read ill_if_t at %p", addr);
219 		return (WALK_ERR);
220 	}
221 
222 	wsp->walk_addr = (uintptr_t)iw->ill_if.illif_next;
223 
224 	if (wsp->walk_addr ==
225 	    (uintptr_t)iw->ill_g_heads[list].ill_g_list_head) {
226 
227 		if (++list >= MAX_G_HEADS)
228 			return (WALK_DONE);
229 
230 		iw->ill_list = list;
231 		wsp->walk_addr =
232 		    (uintptr_t)iw->ill_g_heads[list].ill_g_list_head;
233 		return (WALK_NEXT);
234 	}
235 
236 	return (wsp->walk_callback(addr, iw, wsp->walk_cbdata));
237 }
238 
239 void
240 illif_stack_walk_fini(mdb_walk_state_t *wsp)
241 {
242 	mdb_free(wsp->walk_data, sizeof (illif_walk_data_t));
243 }
244 
245 typedef struct illif_cbdata {
246 	uint_t ill_flags;
247 	uintptr_t ill_addr;
248 	int ill_printlist;	/* list to be printed (MAX_G_HEADS for all) */
249 	boolean_t ill_printed;
250 } illif_cbdata_t;
251 
252 static int
253 illif_cb(uintptr_t addr, const illif_walk_data_t *iw, illif_cbdata_t *id)
254 {
255 	const char *version;
256 
257 	if (id->ill_printlist < MAX_G_HEADS &&
258 	    id->ill_printlist != iw->ill_list)
259 		return (WALK_NEXT);
260 
261 	if (id->ill_flags & DCMD_ADDRSPEC && id->ill_addr != addr)
262 		return (WALK_NEXT);
263 
264 	if (id->ill_flags & DCMD_PIPE_OUT) {
265 		mdb_printf("%p\n", addr);
266 		return (WALK_NEXT);
267 	}
268 
269 	switch (iw->ill_list) {
270 		case IP_V4_G_HEAD:	version = "v4";	break;
271 		case IP_V6_G_HEAD:	version = "v6";	break;
272 		default:		version = "??"; break;
273 	}
274 
275 	mdb_printf("%?p %2s %?p %10d %?p %s\n",
276 	    addr, version, addr + offsetof(ill_if_t, illif_avl_by_ppa),
277 	    iw->ill_if.illif_avl_by_ppa.avl_numnodes,
278 	    iw->ill_if.illif_ppa_arena, iw->ill_if.illif_name);
279 
280 	id->ill_printed = TRUE;
281 
282 	return (WALK_NEXT);
283 }
284 
285 int
286 illif_walk_init(mdb_walk_state_t *wsp)
287 {
288 	if (mdb_layered_walk("ip_stacks", wsp) == -1) {
289 		mdb_warn("can't walk 'ip_stacks'");
290 		return (WALK_ERR);
291 	}
292 
293 	return (WALK_NEXT);
294 }
295 
296 int
297 illif_walk_step(mdb_walk_state_t *wsp)
298 {
299 	uintptr_t kaddr;
300 
301 #ifdef DEBUG
302 	mdb_printf("DEBUG: illif_walk_step: addr %p\n", wsp->walk_addr);
303 #endif
304 
305 	kaddr = wsp->walk_addr + OFFSETOF(ip_stack_t, ips_ill_g_heads);
306 
307 	if (mdb_vread(&kaddr, sizeof (kaddr), kaddr) == -1) {
308 		mdb_warn("can't read ips_ip_cache_table at %p", kaddr);
309 		return (WALK_ERR);
310 	}
311 #ifdef DEBUG
312 	mdb_printf("DEBUG: illif_walk_step: ips_ill_g_heads %p\n", kaddr);
313 #endif
314 
315 	if (mdb_pwalk("illif_stack", wsp->walk_callback,
316 	    wsp->walk_cbdata, kaddr) == -1) {
317 		mdb_warn("couldn't walk 'illif_stack' for ips_ill_g_heads %p",
318 		    kaddr);
319 		return (WALK_ERR);
320 	}
321 	return (WALK_NEXT);
322 }
323 
324 int
325 illif(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
326 {
327 	illif_cbdata_t id;
328 	ill_if_t ill_if;
329 	const char *opt_P = NULL;
330 	int printlist = MAX_G_HEADS;
331 
332 	if (mdb_getopts(argc, argv,
333 	    'P', MDB_OPT_STR, &opt_P, NULL) != argc)
334 		return (DCMD_USAGE);
335 
336 	if (opt_P != NULL) {
337 		if (strcmp("v4", opt_P) == 0) {
338 			printlist = IP_V4_G_HEAD;
339 		} else if (strcmp("v6", opt_P) == 0) {
340 			printlist = IP_V6_G_HEAD;
341 		} else {
342 			mdb_warn("invalid protocol '%s'\n", opt_P);
343 			return (DCMD_USAGE);
344 		}
345 	}
346 
347 	if (DCMD_HDRSPEC(flags) && (flags & DCMD_PIPE_OUT) == 0) {
348 		mdb_printf("%<u>%?s %2s %?s %10s %?s %-10s%</u>\n",
349 		    "ADDR", "IP", "AVLADDR", "NUMNODES", "ARENA", "NAME");
350 	}
351 
352 	id.ill_flags = flags;
353 	id.ill_addr = addr;
354 	id.ill_printlist = printlist;
355 	id.ill_printed = FALSE;
356 
357 	if (mdb_walk("illif", (mdb_walk_cb_t)illif_cb, &id) == -1) {
358 		mdb_warn("can't walk ill_if_t structures");
359 		return (DCMD_ERR);
360 	}
361 
362 	if (!(flags & DCMD_ADDRSPEC) || opt_P != NULL || id.ill_printed)
363 		return (DCMD_OK);
364 
365 	/*
366 	 * If an address is specified and the walk doesn't find it,
367 	 * print it anyway.
368 	 */
369 	if (mdb_vread(&ill_if, sizeof (ill_if_t), addr) == -1) {
370 		mdb_warn("failed to read ill_if_t at %p", addr);
371 		return (DCMD_ERR);
372 	}
373 
374 	mdb_printf("%?p %2s %?p %10d %?p %s\n",
375 	    addr, "??", addr + offsetof(ill_if_t, illif_avl_by_ppa),
376 	    ill_if.illif_avl_by_ppa.avl_numnodes,
377 	    ill_if.illif_ppa_arena, ill_if.illif_name);
378 
379 	return (DCMD_OK);
380 }
381 
382 static void
383 illif_help(void)
384 {
385 	mdb_printf("Options:\n");
386 	mdb_printf("\t-P v4 | v6"
387 	    "\tfilter interface structures for the specified protocol\n");
388 }
389 
390 int
391 ire_walk_init(mdb_walk_state_t *wsp)
392 {
393 	if (mdb_layered_walk("ire_cache", wsp) == -1) {
394 		mdb_warn("can't walk 'ire_cache'");
395 		return (WALK_ERR);
396 	}
397 
398 	return (WALK_NEXT);
399 }
400 
401 int
402 ire_walk_step(mdb_walk_state_t *wsp)
403 {
404 	ire_t ire;
405 
406 	if (mdb_vread(&ire, sizeof (ire), wsp->walk_addr) == -1) {
407 		mdb_warn("can't read ire at %p", wsp->walk_addr);
408 		return (WALK_ERR);
409 	}
410 
411 	return (wsp->walk_callback(wsp->walk_addr, &ire, wsp->walk_cbdata));
412 }
413 
414 int
415 ire_ctable_walk_init(mdb_walk_state_t *wsp)
416 {
417 	if (mdb_layered_walk("ip_stacks", wsp) == -1) {
418 		mdb_warn("can't walk 'ip_stacks'");
419 		return (WALK_ERR);
420 	}
421 
422 	return (WALK_NEXT);
423 }
424 
425 int
426 ire_ctable_walk_step(mdb_walk_state_t *wsp)
427 {
428 	uintptr_t kaddr;
429 	irb_t *irb;
430 	int verbose = 0;
431 	uint32_t cache_table_size;
432 	int i;
433 
434 #ifdef DEBUG
435 	mdb_printf("DEBUG: ire_ctable_walk_step: addr %p\n", wsp->walk_addr);
436 #endif
437 
438 	kaddr = wsp->walk_addr + OFFSETOF(ip_stack_t, ips_ip_cache_table_size);
439 
440 	if (mdb_vread(&cache_table_size, sizeof (uint32_t), kaddr) == -1) {
441 		mdb_warn("can't read ips_ip_cache_table at %p", kaddr);
442 		return (WALK_ERR);
443 	}
444 #ifdef DEBUG
445 	mdb_printf("DEBUG: ire_ctable_walk_step: ips_ip_cache_table_size %u\n",
446 	    cache_table_size);
447 #endif
448 
449 	kaddr = wsp->walk_addr + OFFSETOF(ip_stack_t, ips_ip_cache_table);
450 	if (mdb_vread(&kaddr, sizeof (kaddr), kaddr) == -1) {
451 		mdb_warn("can't read ips_ip_cache_table at %p", kaddr);
452 		return (WALK_ERR);
453 	}
454 #ifdef DEBUG
455 	mdb_printf("DEBUG: ire_ctable_walk_step: ips_ip_cache_table %p\n",
456 	    kaddr);
457 #endif
458 
459 	irb = mdb_alloc(sizeof (irb_t) * cache_table_size, UM_SLEEP|UM_GC);
460 	if (mdb_vread(irb, sizeof (irb_t) * cache_table_size, kaddr) == -1) {
461 		mdb_warn("can't read irb at %p", kaddr);
462 		return (WALK_ERR);
463 	}
464 	for (i = 0; i < cache_table_size; i++) {
465 		kaddr = (uintptr_t)irb[i].irb_ire;
466 #ifdef DEBUG
467 		mdb_printf("DEBUG: ire_ctable_walk_step: %d ire %p\n",
468 		    i, kaddr);
469 #endif
470 
471 		if (mdb_pwalk("ire_next", (mdb_walk_cb_t)ire_format, &verbose,
472 		    kaddr) == -1) {
473 			mdb_warn("can't walk 'ire_next' for ire %p", kaddr);
474 			return (WALK_ERR);
475 		}
476 	}
477 	return (WALK_NEXT);
478 }
479 
480 /* ARGSUSED */
481 int
482 ire_next_walk_init(mdb_walk_state_t *wsp)
483 {
484 #ifdef DEBUG
485 	mdb_printf("DEBUG: ire_next_walk_init: addr %p\n", wsp->walk_addr);
486 #endif
487 	return (WALK_NEXT);
488 }
489 
490 int
491 ire_next_walk_step(mdb_walk_state_t *wsp)
492 {
493 	ire_t ire;
494 	int status;
495 
496 #ifdef DEBUG
497 	mdb_printf("DEBUG: ire_next_walk_step: addr %p\n", wsp->walk_addr);
498 #endif
499 
500 	if (wsp->walk_addr == NULL)
501 		return (WALK_DONE);
502 
503 	if (mdb_vread(&ire, sizeof (ire), wsp->walk_addr) == -1) {
504 		mdb_warn("can't read ire at %p", wsp->walk_addr);
505 		return (WALK_ERR);
506 	}
507 	status = wsp->walk_callback(wsp->walk_addr, &ire,
508 	    wsp->walk_cbdata);
509 
510 	if (status != WALK_NEXT)
511 		return (status);
512 
513 	wsp->walk_addr = (uintptr_t)ire.ire_next;
514 #ifdef DEBUG
515 	mdb_printf("DEBUG: ire_ctable_walk_step: next %p\n", wsp->walk_addr);
516 #endif
517 	return (status);
518 }
519 
520 static int
521 ire_format(uintptr_t addr, const ire_t *irep, uint_t *verbose)
522 {
523 	static const mdb_bitmask_t tmasks[] = {
524 		{ "BROADCAST",	IRE_BROADCAST,		IRE_BROADCAST	},
525 		{ "DEFAULT",	IRE_DEFAULT,		IRE_DEFAULT	},
526 		{ "LOCAL",	IRE_LOCAL,		IRE_LOCAL	},
527 		{ "LOOPBACK",	IRE_LOOPBACK,		IRE_LOOPBACK	},
528 		{ "PREFIX",	IRE_PREFIX,		IRE_PREFIX	},
529 		{ "CACHE",	IRE_CACHE,		IRE_CACHE	},
530 		{ "IF_NORESOLVER", IRE_IF_NORESOLVER,	IRE_IF_NORESOLVER },
531 		{ "IF_RESOLVER", IRE_IF_RESOLVER,	IRE_IF_RESOLVER	},
532 		{ "HOST",	IRE_HOST,		IRE_HOST	},
533 		{ "HOST_REDIRECT", IRE_HOST_REDIRECT,	IRE_HOST_REDIRECT },
534 		{ NULL,		0,			0		}
535 	};
536 
537 	static const mdb_bitmask_t mmasks[] = {
538 		{ "CONDEMNED",	IRE_MARK_CONDEMNED,	IRE_MARK_CONDEMNED },
539 		{ "NORECV",	IRE_MARK_NORECV,	IRE_MARK_NORECV	},
540 		{ "HIDDEN",	IRE_MARK_HIDDEN,	IRE_MARK_HIDDEN	},
541 		{ "NOADD",	IRE_MARK_NOADD,		IRE_MARK_NOADD	},
542 		{ "TEMPORARY",	IRE_MARK_TEMPORARY,	IRE_MARK_TEMPORARY },
543 		{ NULL,		0,			0		}
544 	};
545 
546 	static const mdb_bitmask_t fmasks[] = {
547 		{ "UP",		RTF_UP,			RTF_UP		},
548 		{ "GATEWAY",	RTF_GATEWAY,		RTF_GATEWAY	},
549 		{ "HOST",	RTF_HOST,		RTF_HOST	},
550 		{ "REJECT",	RTF_REJECT,		RTF_REJECT	},
551 		{ "DYNAMIC",	RTF_DYNAMIC,		RTF_DYNAMIC	},
552 		{ "MODIFIED",	RTF_MODIFIED,		RTF_MODIFIED	},
553 		{ "DONE",	RTF_DONE,		RTF_DONE	},
554 		{ "MASK",	RTF_MASK,		RTF_MASK	},
555 		{ "CLONING",	RTF_CLONING,		RTF_CLONING	},
556 		{ "XRESOLVE",	RTF_XRESOLVE,		RTF_XRESOLVE	},
557 		{ "LLINFO",	RTF_LLINFO,		RTF_LLINFO	},
558 		{ "STATIC",	RTF_STATIC,		RTF_STATIC	},
559 		{ "BLACKHOLE",	RTF_BLACKHOLE,		RTF_BLACKHOLE	},
560 		{ "PRIVATE",	RTF_PRIVATE,		RTF_PRIVATE	},
561 		{ "PROTO2",	RTF_PROTO2,		RTF_PROTO2	},
562 		{ "PROTO1",	RTF_PROTO1,		RTF_PROTO1	},
563 		{ "MULTIRT",	RTF_MULTIRT,		RTF_MULTIRT	},
564 		{ "SETSRC",	RTF_SETSRC,		RTF_SETSRC	},
565 		{ NULL,		0,			0		}
566 	};
567 
568 	if (irep->ire_ipversion == 6 && *verbose) {
569 
570 		mdb_printf("%<b>%?p%</b> %40N <%hb>\n"
571 		    "%?s %40N <%hb>\n"
572 		    "%?s %40d %4d <%hb>\n",
573 		    addr, &irep->ire_src_addr_v6, irep->ire_type, tmasks,
574 		    "", &irep->ire_addr_v6, (ushort_t)irep->ire_marks, mmasks,
575 		    "", ips_to_stackid((uintptr_t)irep->ire_ipst),
576 		    irep->ire_zoneid,
577 		    irep->ire_flags, fmasks);
578 
579 	} else if (irep->ire_ipversion == 6) {
580 
581 		mdb_printf("%?p %30N %30N %5d %4d\n",
582 		    addr, &irep->ire_src_addr_v6,
583 		    &irep->ire_addr_v6,
584 		    ips_to_stackid((uintptr_t)irep->ire_ipst),
585 		    irep->ire_zoneid);
586 
587 	} else if (*verbose) {
588 
589 		mdb_printf("%<b>%?p%</b> %40I <%hb>\n"
590 		    "%?s %40I <%hb>\n"
591 		    "%?s %40d <%hb>\n",
592 		    addr, irep->ire_src_addr, irep->ire_type, tmasks,
593 		    "", irep->ire_addr, (ushort_t)irep->ire_marks, mmasks,
594 		    "", ips_to_stackid((uintptr_t)irep->ire_ipst),
595 		    irep->ire_zoneid, irep->ire_flags, fmasks);
596 
597 	} else {
598 
599 		mdb_printf("%?p %30I %30I %5d %4d\n", addr, irep->ire_src_addr,
600 		    irep->ire_addr, ips_to_stackid((uintptr_t)irep->ire_ipst),
601 		    irep->ire_zoneid);
602 	}
603 
604 	return (WALK_NEXT);
605 }
606 
607 /*
608  * There are faster ways to do this.  Given the interactive nature of this
609  * use I don't think its worth much effort.
610  */
611 static unsigned short
612 ipcksum(void *p, int len)
613 {
614 	int32_t	sum = 0;
615 
616 	while (len > 1) {
617 		/* alignment */
618 		sum += *(uint16_t *)p;
619 		p = (char *)p + sizeof (uint16_t);
620 		if (sum & 0x80000000)
621 			sum = (sum & 0xFFFF) + (sum >> 16);
622 		len -= 2;
623 	}
624 
625 	if (len)
626 		sum += (uint16_t)*(unsigned char *)p;
627 
628 	while (sum >> 16)
629 		sum = (sum & 0xFFFF) + (sum >> 16);
630 
631 	return (~sum);
632 }
633 
634 static const mdb_bitmask_t tcp_flags[] = {
635 	{ "SYN",	TH_SYN,		TH_SYN	},
636 	{ "ACK",	TH_ACK,		TH_ACK	},
637 	{ "FIN",	TH_FIN,		TH_FIN	},
638 	{ "RST",	TH_RST,		TH_RST	},
639 	{ "PSH",	TH_PUSH,	TH_PUSH	},
640 	{ "ECE",	TH_ECE,		TH_ECE	},
641 	{ "CWR",	TH_CWR,		TH_CWR	},
642 	{ NULL,		0,		0	}
643 };
644 
645 static void
646 tcphdr_print(struct tcphdr *tcph)
647 {
648 	in_port_t	sport, dport;
649 	tcp_seq		seq, ack;
650 	uint16_t	win, urp;
651 
652 	mdb_printf("%<b>TCP header%</b>\n");
653 
654 	mdb_nhconvert(&sport, &tcph->th_sport, sizeof (sport));
655 	mdb_nhconvert(&dport, &tcph->th_dport, sizeof (dport));
656 	mdb_nhconvert(&seq, &tcph->th_seq, sizeof (seq));
657 	mdb_nhconvert(&ack, &tcph->th_ack, sizeof (ack));
658 	mdb_nhconvert(&win, &tcph->th_win, sizeof (win));
659 	mdb_nhconvert(&urp, &tcph->th_urp, sizeof (urp));
660 
661 	mdb_printf("%<u>%6s %6s %10s %10s %4s %5s %5s %5s %-15s%</u>\n",
662 	    "SPORT", "DPORT", "SEQ", "ACK", "HLEN", "WIN", "CSUM", "URP",
663 	    "FLAGS");
664 	mdb_printf("%6hu %6hu %10u %10u %4d %5hu %5hu %5hu <%b>\n",
665 	    sport, dport, seq, ack, tcph->th_off << 2, win,
666 	    tcph->th_sum, urp, tcph->th_flags, tcp_flags);
667 	mdb_printf("0x%04x 0x%04x 0x%08x 0x%08x\n\n",
668 	    sport, dport, seq, ack);
669 }
670 
671 /* ARGSUSED */
672 static int
673 tcphdr(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
674 {
675 	struct tcphdr	tcph;
676 
677 	if (!(flags & DCMD_ADDRSPEC))
678 		return (DCMD_USAGE);
679 
680 	if (mdb_vread(&tcph, sizeof (tcph), addr) == -1) {
681 		mdb_warn("failed to read TCP header at %p", addr);
682 		return (DCMD_ERR);
683 	}
684 	tcphdr_print(&tcph);
685 	return (DCMD_OK);
686 }
687 
688 static void
689 udphdr_print(struct udphdr *udph)
690 {
691 	in_port_t	sport, dport;
692 	uint16_t	hlen;
693 
694 	mdb_printf("%<b>UDP header%</b>\n");
695 
696 	mdb_nhconvert(&sport, &udph->uh_sport, sizeof (sport));
697 	mdb_nhconvert(&dport, &udph->uh_dport, sizeof (dport));
698 	mdb_nhconvert(&hlen, &udph->uh_ulen, sizeof (hlen));
699 
700 	mdb_printf("%<u>%14s %14s %5s %6s%</u>\n",
701 	    "SPORT", "DPORT", "LEN", "CSUM");
702 	mdb_printf("%5hu (0x%04x) %5hu (0x%04x) %5hu 0x%04hx\n\n", sport, sport,
703 	    dport, dport, hlen, udph->uh_sum);
704 }
705 
706 /* ARGSUSED */
707 static int
708 udphdr(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
709 {
710 	struct udphdr	udph;
711 
712 	if (!(flags & DCMD_ADDRSPEC))
713 		return (DCMD_USAGE);
714 
715 	if (mdb_vread(&udph, sizeof (udph), addr) == -1) {
716 		mdb_warn("failed to read UDP header at %p", addr);
717 		return (DCMD_ERR);
718 	}
719 	udphdr_print(&udph);
720 	return (DCMD_OK);
721 }
722 
723 static void
724 sctphdr_print(sctp_hdr_t *sctph)
725 {
726 	in_port_t sport, dport;
727 
728 	mdb_printf("%<b>SCTP header%</b>\n");
729 	mdb_nhconvert(&sport, &sctph->sh_sport, sizeof (sport));
730 	mdb_nhconvert(&dport, &sctph->sh_dport, sizeof (dport));
731 
732 	mdb_printf("%<u>%14s %14s %10s %10s%</u>\n",
733 	    "SPORT", "DPORT", "VTAG", "CHKSUM");
734 	mdb_printf("%5hu (0x%04x) %5hu (0x%04x) %10u 0x%08x\n\n", sport, sport,
735 	    dport, dport, sctph->sh_verf, sctph->sh_chksum);
736 }
737 
738 /* ARGSUSED */
739 static int
740 sctphdr(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
741 {
742 	sctp_hdr_t sctph;
743 
744 	if (!(flags & DCMD_ADDRSPEC))
745 		return (DCMD_USAGE);
746 
747 	if (mdb_vread(&sctph, sizeof (sctph), addr) == -1) {
748 		mdb_warn("failed to read SCTP header at %p", addr);
749 		return (DCMD_ERR);
750 	}
751 
752 	sctphdr_print(&sctph);
753 	return (DCMD_OK);
754 }
755 
756 static int
757 transport_hdr(int proto, uintptr_t addr)
758 {
759 	mdb_printf("\n");
760 	switch (proto) {
761 	case IPPROTO_TCP: {
762 		struct tcphdr tcph;
763 
764 		if (mdb_vread(&tcph, sizeof (tcph), addr) == -1) {
765 			mdb_warn("failed to read TCP header at %p", addr);
766 			return (DCMD_ERR);
767 		}
768 		tcphdr_print(&tcph);
769 		break;
770 	}
771 	case IPPROTO_UDP:  {
772 		struct udphdr udph;
773 
774 		if (mdb_vread(&udph, sizeof (udph), addr) == -1) {
775 			mdb_warn("failed to read UDP header at %p", addr);
776 			return (DCMD_ERR);
777 		}
778 		udphdr_print(&udph);
779 		break;
780 	}
781 	case IPPROTO_SCTP: {
782 		sctp_hdr_t sctph;
783 
784 		if (mdb_vread(&sctph, sizeof (sctph), addr) == -1) {
785 			mdb_warn("failed to read SCTP header at %p", addr);
786 			return (DCMD_ERR);
787 		}
788 		sctphdr_print(&sctph);
789 		break;
790 	}
791 	default:
792 		break;
793 	}
794 
795 	return (DCMD_OK);
796 }
797 
798 static const mdb_bitmask_t ip_flags[] = {
799 	{ "DF",	IPH_DF, IPH_DF	},
800 	{ "MF", IPH_MF,	IPH_MF	},
801 	{ NULL, 0,	0	}
802 };
803 
804 /* ARGSUSED */
805 static int
806 iphdr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
807 {
808 	uint_t		verbose = FALSE, force = FALSE;
809 	ipha_t		iph[1];
810 	uint16_t	ver, totlen, hdrlen, ipid, off, csum;
811 	uintptr_t	nxt_proto;
812 	char		exp_csum[8];
813 
814 	if (mdb_getopts(argc, argv,
815 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
816 	    'f', MDB_OPT_SETBITS, TRUE, &force, NULL) != argc)
817 		return (DCMD_USAGE);
818 
819 	if (mdb_vread(iph, sizeof (*iph), addr) == -1) {
820 		mdb_warn("failed to read IPv4 header at %p", addr);
821 		return (DCMD_ERR);
822 	}
823 
824 	ver = (iph->ipha_version_and_hdr_length & 0xf0) >> 4;
825 	if (ver != IPV4_VERSION) {
826 		if (ver == IPV6_VERSION) {
827 			return (ip6hdr(addr, flags, argc, argv));
828 		} else if (!force) {
829 			mdb_warn("unknown IP version: %d\n", ver);
830 			return (DCMD_ERR);
831 		}
832 	}
833 
834 	mdb_printf("%<b>IPv4 header%</b>\n");
835 	mdb_printf("%-34s %-34s\n"
836 	    "%<u>%-4s %-4s %-5s %-5s %-6s %-5s %-5s %-6s %-8s %-6s%</u>\n",
837 	    "SRC", "DST",
838 	    "HLEN", "TOS", "LEN", "ID", "OFFSET", "TTL", "PROTO", "CHKSUM",
839 	    "EXP-CSUM", "FLGS");
840 
841 	hdrlen = (iph->ipha_version_and_hdr_length & 0x0f) << 2;
842 	mdb_nhconvert(&totlen, &iph->ipha_length, sizeof (totlen));
843 	mdb_nhconvert(&ipid, &iph->ipha_ident, sizeof (ipid));
844 	mdb_nhconvert(&off, &iph->ipha_fragment_offset_and_flags, sizeof (off));
845 	if (hdrlen == IP_SIMPLE_HDR_LENGTH) {
846 		if ((csum = ipcksum(iph, sizeof (*iph))) != 0)
847 			csum = ~(~csum + ~iph->ipha_hdr_checksum);
848 		else
849 			csum = iph->ipha_hdr_checksum;
850 		mdb_snprintf(exp_csum, 8, "%u", csum);
851 	} else {
852 		mdb_snprintf(exp_csum, 8, "<n/a>");
853 	}
854 
855 	mdb_printf("%-34I %-34I%\n"
856 	    "%-4d %-4d %-5hu %-5hu %-6hu %-5hu %-5hu %-6u %-8s <%5hb>\n",
857 	    iph->ipha_src, iph->ipha_dst,
858 	    hdrlen, iph->ipha_type_of_service, totlen, ipid,
859 	    (off << 3) & 0xffff, iph->ipha_ttl, iph->ipha_protocol,
860 	    iph->ipha_hdr_checksum, exp_csum, off, ip_flags);
861 
862 	if (verbose) {
863 		nxt_proto = addr + hdrlen;
864 		return (transport_hdr(iph->ipha_protocol, nxt_proto));
865 	} else {
866 		return (DCMD_OK);
867 	}
868 }
869 
870 /* ARGSUSED */
871 static int
872 ip6hdr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
873 {
874 	uint_t		verbose = FALSE, force = FALSE;
875 	ip6_t		iph[1];
876 	int		ver, class, flow;
877 	uint16_t	plen;
878 	uintptr_t	nxt_proto;
879 
880 	if (mdb_getopts(argc, argv,
881 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
882 	    'f', MDB_OPT_SETBITS, TRUE, &force, NULL) != argc)
883 		return (DCMD_USAGE);
884 
885 	if (mdb_vread(iph, sizeof (*iph), addr) == -1) {
886 		mdb_warn("failed to read IPv6 header at %p", addr);
887 		return (DCMD_ERR);
888 	}
889 
890 	ver = (iph->ip6_vfc & 0xf0) >> 4;
891 	if (ver != IPV6_VERSION) {
892 		if (ver == IPV4_VERSION) {
893 			return (iphdr(addr, flags, argc, argv));
894 		} else if (!force) {
895 			mdb_warn("unknown IP version: %d\n", ver);
896 			return (DCMD_ERR);
897 		}
898 	}
899 
900 	mdb_printf("%<b>IPv6 header%</b>\n");
901 	mdb_printf("%<u>%-26s %-26s %4s %7s %5s %3s %3s%</u>\n",
902 	    "SRC", "DST", "TCLS", "FLOW-ID", "PLEN", "NXT", "HOP");
903 
904 	class = (iph->ip6_vcf & IPV6_FLOWINFO_TCLASS) >> 20;
905 	mdb_nhconvert(&class, &class, sizeof (class));
906 	flow = iph->ip6_vcf & IPV6_FLOWINFO_FLOWLABEL;
907 	mdb_nhconvert(&flow, &flow, sizeof (flow));
908 	mdb_nhconvert(&plen, &iph->ip6_plen, sizeof (plen));
909 
910 	mdb_printf("%-26N %-26N %4d %7d %5hu %3d %3d\n",
911 	    &iph->ip6_src, &iph->ip6_dst,
912 	    class, flow, plen, iph->ip6_nxt, iph->ip6_hlim);
913 
914 	if (verbose) {
915 		nxt_proto = addr + sizeof (ip6_t);
916 		return (transport_hdr(iph->ip6_nxt, nxt_proto));
917 	} else {
918 		return (DCMD_OK);
919 	}
920 }
921 
922 int
923 ire(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
924 {
925 	uint_t verbose = FALSE;
926 	ire_t ire;
927 
928 	if (mdb_getopts(argc, argv,
929 	    'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL) != argc)
930 		return (DCMD_USAGE);
931 
932 	if ((flags & DCMD_LOOPFIRST) || !(flags & DCMD_LOOP)) {
933 
934 		if (verbose) {
935 			mdb_printf("%?s %40s %-20s%\n"
936 			    "%?s %40s %-20s%\n"
937 			    "%<u>%?s %40s %4s %-20s%</u>\n",
938 			    "ADDR", "SRC", "TYPE",
939 			    "", "DST", "MARKS",
940 			    "", "STACK", "ZONE", "FLAGS");
941 		} else {
942 			mdb_printf("%<u>%?s %30s %30s %5s %4s%</u>\n",
943 			    "ADDR", "SRC", "DST", "STACK", "ZONE");
944 		}
945 	}
946 
947 	if (flags & DCMD_ADDRSPEC) {
948 		(void) mdb_vread(&ire, sizeof (ire_t), addr);
949 		(void) ire_format(addr, &ire, &verbose);
950 	} else if (mdb_walk("ire", (mdb_walk_cb_t)ire_format, &verbose) == -1) {
951 		mdb_warn("failed to walk ire table");
952 		return (DCMD_ERR);
953 	}
954 
955 	return (DCMD_OK);
956 }
957 
958 static size_t
959 mi_osize(const queue_t *q)
960 {
961 	/*
962 	 * The code in common/inet/mi.c allocates an extra word to store the
963 	 * size of the allocation.  An mi_o_s is thus a size_t plus an mi_o_s.
964 	 */
965 	struct mi_block {
966 		size_t mi_nbytes;
967 		struct mi_o_s mi_o;
968 	} m;
969 
970 	if (mdb_vread(&m, sizeof (m), (uintptr_t)q->q_ptr -
971 	    sizeof (m)) == sizeof (m))
972 		return (m.mi_nbytes - sizeof (m));
973 
974 	return (0);
975 }
976 
977 static void
978 ip_ill_qinfo(const queue_t *q, char *buf, size_t nbytes)
979 {
980 	char name[32];
981 	ill_t ill;
982 
983 	if (mdb_vread(&ill, sizeof (ill),
984 	    (uintptr_t)q->q_ptr) == sizeof (ill) &&
985 	    mdb_readstr(name, sizeof (name), (uintptr_t)ill.ill_name) > 0)
986 		(void) mdb_snprintf(buf, nbytes, "if: %s", name);
987 }
988 
989 void
990 ip_qinfo(const queue_t *q, char *buf, size_t nbytes)
991 {
992 	size_t size = mi_osize(q);
993 
994 	if (size == sizeof (ill_t))
995 		ip_ill_qinfo(q, buf, nbytes);
996 }
997 
998 uintptr_t
999 ip_rnext(const queue_t *q)
1000 {
1001 	size_t size = mi_osize(q);
1002 	ill_t ill;
1003 
1004 	if (size == sizeof (ill_t) && mdb_vread(&ill, sizeof (ill),
1005 	    (uintptr_t)q->q_ptr) == sizeof (ill))
1006 		return ((uintptr_t)ill.ill_rq);
1007 
1008 	return (NULL);
1009 }
1010 
1011 uintptr_t
1012 ip_wnext(const queue_t *q)
1013 {
1014 	size_t size = mi_osize(q);
1015 	ill_t ill;
1016 
1017 	if (size == sizeof (ill_t) && mdb_vread(&ill, sizeof (ill),
1018 	    (uintptr_t)q->q_ptr) == sizeof (ill))
1019 		return ((uintptr_t)ill.ill_wq);
1020 
1021 	return (NULL);
1022 }
1023 
1024 /*
1025  * Print the core fields in an squeue_t.  With the "-v" argument,
1026  * provide more verbose output.
1027  */
1028 static int
1029 squeue(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1030 {
1031 	unsigned int	i;
1032 	unsigned int	verbose = FALSE;
1033 	const int	SQUEUE_STATEDELT = (int)(sizeof (uintptr_t) + 9);
1034 	boolean_t	arm;
1035 	squeue_t	squeue;
1036 
1037 	if (!(flags & DCMD_ADDRSPEC)) {
1038 		if (mdb_walk_dcmd("genunix`squeue_cache", "ip`squeue",
1039 		    argc, argv) == -1) {
1040 			mdb_warn("failed to walk squeue cache");
1041 			return (DCMD_ERR);
1042 		}
1043 		return (DCMD_OK);
1044 	}
1045 
1046 	if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL)
1047 	    != argc)
1048 		return (DCMD_USAGE);
1049 
1050 	if (!DCMD_HDRSPEC(flags) && verbose)
1051 		mdb_printf("\n\n");
1052 
1053 	if (DCMD_HDRSPEC(flags) || verbose) {
1054 		mdb_printf("%?s %-5s %-3s %?s %?s %?s\n",
1055 		    "ADDR", "STATE", "CPU",
1056 		    "FIRST", "LAST", "WORKER");
1057 	}
1058 
1059 	if (mdb_vread(&squeue, sizeof (squeue_t), addr) == -1) {
1060 		mdb_warn("cannot read squeue_t at %p", addr);
1061 		return (DCMD_ERR);
1062 	}
1063 
1064 	mdb_printf("%0?p %05x %3d %0?p %0?p %0?p\n",
1065 	    addr, squeue.sq_state, squeue.sq_bind,
1066 	    squeue.sq_first, squeue.sq_last, squeue.sq_worker);
1067 
1068 	if (!verbose)
1069 		return (DCMD_OK);
1070 
1071 	arm = B_TRUE;
1072 	for (i = 0; squeue_states[i].bit_name != NULL; i++) {
1073 		if (((squeue.sq_state) & (1 << i)) == 0)
1074 			continue;
1075 
1076 		if (arm) {
1077 			mdb_printf("%*s|\n", SQUEUE_STATEDELT, "");
1078 			mdb_printf("%*s+-->  ", SQUEUE_STATEDELT, "");
1079 			arm = B_FALSE;
1080 		} else
1081 			mdb_printf("%*s      ", SQUEUE_STATEDELT, "");
1082 
1083 		mdb_printf("%-12s %s\n", squeue_states[i].bit_name,
1084 		    squeue_states[i].bit_descr);
1085 	}
1086 
1087 	return (DCMD_OK);
1088 }
1089 
1090 static void
1091 ip_squeue_help(void)
1092 {
1093 	mdb_printf("Print the core information for a given NCA squeue_t.\n\n");
1094 	mdb_printf("Options:\n");
1095 	mdb_printf("\t-v\tbe verbose (more descriptive)\n");
1096 }
1097 
1098 /*
1099  * This is called by ::th_trace (via a callback) when walking the th_hash
1100  * list.  It calls modent to find the entries.
1101  */
1102 /* ARGSUSED */
1103 static int
1104 modent_summary(uintptr_t addr, const void *data, void *private)
1105 {
1106 	th_walk_data_t *thw = private;
1107 	const struct mod_hash_entry *mhe = data;
1108 	th_trace_t th;
1109 
1110 	if (mdb_vread(&th, sizeof (th), (uintptr_t)mhe->mhe_val) == -1) {
1111 		mdb_warn("failed to read th_trace_t %p", mhe->mhe_val);
1112 		return (WALK_ERR);
1113 	}
1114 
1115 	if (th.th_refcnt == 0 && thw->thw_non_zero_only)
1116 		return (WALK_NEXT);
1117 
1118 	if (!thw->thw_match) {
1119 		mdb_printf("%?p %?p %?p %8d %?p\n", thw->thw_ipst, mhe->mhe_key,
1120 		    mhe->mhe_val, th.th_refcnt, th.th_id);
1121 	} else if (thw->thw_matchkey == (uintptr_t)mhe->mhe_key) {
1122 		int i, j, k;
1123 		tr_buf_t *tr;
1124 
1125 		mdb_printf("Object %p in IP stack %p:\n", mhe->mhe_key,
1126 		    thw->thw_ipst);
1127 		i = th.th_trace_lastref;
1128 		mdb_printf("\tThread %p refcnt %d:\n", th.th_id,
1129 		    th.th_refcnt);
1130 		for (j = TR_BUF_MAX; j > 0; j--) {
1131 			tr = th.th_trbuf + i;
1132 			if (tr->tr_depth == 0 || tr->tr_depth > TR_STACK_DEPTH)
1133 				break;
1134 			mdb_printf("\t  T%+ld:\n", tr->tr_time -
1135 			    thw->thw_lbolt);
1136 			for (k = 0; k < tr->tr_depth; k++)
1137 				mdb_printf("\t\t%a\n", tr->tr_stack[k]);
1138 			if (--i < 0)
1139 				i = TR_BUF_MAX - 1;
1140 		}
1141 	}
1142 	return (WALK_NEXT);
1143 }
1144 
1145 /*
1146  * This is called by ::th_trace (via a callback) when walking the th_hash
1147  * list.  It calls modent to find the entries.
1148  */
1149 /* ARGSUSED */
1150 static int
1151 th_hash_summary(uintptr_t addr, const void *data, void *private)
1152 {
1153 	const th_hash_t *thh = data;
1154 	th_walk_data_t *thw = private;
1155 
1156 	thw->thw_ipst = (uintptr_t)thh->thh_ipst;
1157 	return (mdb_pwalk("modent", modent_summary, private,
1158 	    (uintptr_t)thh->thh_hash));
1159 }
1160 
1161 /*
1162  * Print or summarize the th_trace_t structures.
1163  */
1164 static int
1165 th_trace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1166 {
1167 	th_walk_data_t thw;
1168 
1169 	(void) memset(&thw, 0, sizeof (thw));
1170 
1171 	if (mdb_getopts(argc, argv,
1172 	    'n', MDB_OPT_SETBITS, TRUE, &thw.thw_non_zero_only,
1173 	    NULL) != argc)
1174 		return (DCMD_USAGE);
1175 
1176 	if (!(flags & DCMD_ADDRSPEC)) {
1177 		/*
1178 		 * No address specified.  Walk all of the th_hash_t in the
1179 		 * system, and summarize the th_trace_t entries in each.
1180 		 */
1181 		mdb_printf("%?s %?s %?s %8s %?s\n",
1182 		    "IPSTACK", "OBJECT", "TRACE", "REFCNT", "THREAD");
1183 		thw.thw_match = B_FALSE;
1184 	} else {
1185 		thw.thw_match = B_TRUE;
1186 		thw.thw_matchkey = addr;
1187 		if (mdb_readvar(&thw.thw_lbolt,
1188 		    mdb_prop_postmortem ? "panic_lbolt" : "lbolt") == -1) {
1189 			mdb_warn("failed to read lbolt");
1190 			return (DCMD_ERR);
1191 		}
1192 	}
1193 	if (mdb_pwalk("th_hash", th_hash_summary, &thw, NULL) == -1) {
1194 		mdb_warn("can't walk th_hash entries");
1195 		return (DCMD_ERR);
1196 	}
1197 	return (DCMD_OK);
1198 }
1199 
1200 static void
1201 th_trace_help(void)
1202 {
1203 	mdb_printf("If given an address of an ill_t, ipif_t, ire_t, or nce_t, "
1204 	    "print the\n"
1205 	    "corresponding th_trace_t structure in detail.  Otherwise, if no "
1206 	    "address is\n"
1207 	    "given, then summarize all th_trace_t structures.\n\n");
1208 	mdb_printf("Options:\n"
1209 	    "\t-n\tdisplay only entries with non-zero th_refcnt\n");
1210 }
1211 
1212 static const mdb_dcmd_t dcmds[] = {
1213 	{ "illif", "?[-P v4 | v6]",
1214 	    "display or filter IP Lower Level InterFace structures", illif,
1215 	    illif_help },
1216 	{ "iphdr", ":[-vf]", "display an IPv4 header", iphdr },
1217 	{ "ip6hdr", ":[-vf]", "display an IPv6 header", ip6hdr },
1218 	{ "ire", "?[-v]", "display Internet Route Entry structures", ire },
1219 	{ "squeue", ":[-v]", "print core squeue_t info", squeue,
1220 	    ip_squeue_help },
1221 	{ "tcphdr", ":", "display a TCP header", tcphdr },
1222 	{ "udphdr", ":", "display an UDP header", udphdr },
1223 	{ "sctphdr", ":", "display an SCTP header", sctphdr },
1224 	{ "th_trace", "?[-n]", "display th_trace_t structures", th_trace,
1225 	    th_trace_help },
1226 	{ NULL }
1227 };
1228 
1229 static const mdb_walker_t walkers[] = {
1230 	{ "illif", "walk list of ill interface types for all stacks",
1231 		illif_walk_init, illif_walk_step, NULL },
1232 	{ "illif_stack", "walk list of ill interface types",
1233 		illif_stack_walk_init, illif_stack_walk_step,
1234 		illif_stack_walk_fini },
1235 	{ "ire", "walk active ire_t structures",
1236 		ire_walk_init, ire_walk_step, NULL },
1237 	{ "ire_ctable", "walk ire_t structures in the ctable",
1238 		ire_ctable_walk_init, ire_ctable_walk_step, NULL },
1239 	{ "ire_next", "walk ire_t structures in the ctable",
1240 		ire_next_walk_init, ire_next_walk_step, NULL },
1241 	{ "ip_stacks", "walk all the ip_stack_t",
1242 		ip_stacks_walk_init, ip_stacks_walk_step, NULL },
1243 	{ "th_hash", "walk all the th_hash_t entries",
1244 		th_hash_walk_init, th_hash_walk_step, NULL },
1245 	{ NULL }
1246 };
1247 
1248 static const mdb_qops_t ip_qops = { ip_qinfo, ip_rnext, ip_wnext };
1249 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
1250 
1251 const mdb_modinfo_t *
1252 _mdb_init(void)
1253 {
1254 	GElf_Sym sym;
1255 
1256 	if (mdb_lookup_by_obj("ip", "ipwinit", &sym) == 0)
1257 		mdb_qops_install(&ip_qops, (uintptr_t)sym.st_value);
1258 
1259 	return (&modinfo);
1260 }
1261 
1262 void
1263 _mdb_fini(void)
1264 {
1265 	GElf_Sym sym;
1266 
1267 	if (mdb_lookup_by_obj("ip", "ipwinit", &sym) == 0)
1268 		mdb_qops_remove(&ip_qops, (uintptr_t)sym.st_value);
1269 }
1270