xref: /illumos-gate/usr/src/cmd/mdb/common/modules/ip/ip.c (revision a724c049b7e0dd8612bc3aaec84e96e80511050d)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/stropts.h>
28 #include <sys/stream.h>
29 #include <sys/socket.h>
30 #include <sys/avl_impl.h>
31 #include <net/if.h>
32 #include <net/route.h>
33 #include <netinet/in.h>
34 #include <netinet/ip6.h>
35 #include <netinet/udp.h>
36 #include <netinet/sctp.h>
37 #include <inet/mib2.h>
38 #include <inet/common.h>
39 #include <inet/ip.h>
40 #include <inet/ip_ire.h>
41 #include <inet/ip6.h>
42 #include <inet/ipclassifier.h>
43 #include <inet/mi.h>
44 #include <sys/squeue_impl.h>
45 #include <sys/modhash_impl.h>
46 #include <inet/ip_ndp.h>
47 #include <inet/ip_if.h>
48 #include <sys/dlpi.h>
49 
50 #include <mdb/mdb_modapi.h>
51 #include <mdb/mdb_ks.h>
52 
53 #define	ADDR_WIDTH 11
54 #define	L2MAXADDRSTRLEN	255
55 #define	MAX_SAP_LEN	255
56 
57 typedef struct {
58 	const char *bit_name;	/* name of bit */
59 	const char *bit_descr;	/* description of bit's purpose */
60 } bitname_t;
61 
62 static const bitname_t squeue_states[] = {
63 	{ "SQS_PROC",		"being processed" },
64 	{ "SQS_WORKER",		"... by a worker thread" },
65 	{ "SQS_ENTER",		"... by an squeue_enter() thread" },
66 	{ "SQS_FAST",		"... in fast-path mode" },
67 	{ "SQS_USER", 		"A non interrupt user" },
68 	{ "SQS_BOUND",		"worker thread bound to CPU" },
69 	{ "SQS_PROFILE",	"profiling enabled" },
70 	{ "SQS_REENTER",	"re-entered thred" },
71 	{ NULL }
72 };
73 
74 typedef struct illif_walk_data {
75 	ill_g_head_t ill_g_heads[MAX_G_HEADS];
76 	int ill_list;
77 	ill_if_t ill_if;
78 } illif_walk_data_t;
79 
80 typedef struct nce_walk_data_s {
81 	struct ndp_g_s	nce_ip_ndp;
82 	int		nce_hash_tbl_index;
83 	nce_t 		nce;
84 } nce_walk_data_t;
85 
86 typedef struct nce_cbdata_s {
87 	uintptr_t nce_addr;
88 	int	  nce_ipversion;
89 } nce_cbdata_t;
90 
91 typedef struct ire_cbdata_s {
92 	int		ire_ipversion;
93 	boolean_t	verbose;
94 } ire_cbdata_t;
95 
96 typedef struct th_walk_data {
97 	uint_t		thw_non_zero_only;
98 	boolean_t	thw_match;
99 	uintptr_t	thw_matchkey;
100 	uintptr_t	thw_ipst;
101 	clock_t		thw_lbolt;
102 } th_walk_data_t;
103 
104 static int iphdr(uintptr_t, uint_t, int, const mdb_arg_t *);
105 static int ip6hdr(uintptr_t, uint_t, int, const mdb_arg_t *);
106 
107 static int ire_format(uintptr_t addr, const void *, void *);
108 static int nce_format(uintptr_t addr, const nce_t *nce, int ipversion);
109 static int nce(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv);
110 static int nce_walk_step(mdb_walk_state_t *wsp);
111 static int nce_stack_walk_init(mdb_walk_state_t *wsp);
112 static int nce_stack_walk_step(mdb_walk_state_t *wsp);
113 static void nce_stack_walk_fini(mdb_walk_state_t *wsp);
114 static int nce_cb(uintptr_t addr, const nce_walk_data_t *iw, nce_cbdata_t *id);
115 
116 /*
117  * Given the kernel address of an ip_stack_t, return the stackid
118  */
119 static int
120 ips_to_stackid(uintptr_t kaddr)
121 {
122 	ip_stack_t ipss;
123 	netstack_t nss;
124 
125 	if (mdb_vread(&ipss, sizeof (ipss), kaddr) == -1) {
126 		mdb_warn("failed to read ip_stack_t %p", kaddr);
127 		return (0);
128 	}
129 	kaddr = (uintptr_t)ipss.ips_netstack;
130 	if (mdb_vread(&nss, sizeof (nss), kaddr) == -1) {
131 		mdb_warn("failed to read netstack_t %p", kaddr);
132 		return (0);
133 	}
134 	return (nss.netstack_stackid);
135 }
136 
137 int
138 ip_stacks_walk_init(mdb_walk_state_t *wsp)
139 {
140 	if (mdb_layered_walk("netstack", wsp) == -1) {
141 		mdb_warn("can't walk 'netstack'");
142 		return (WALK_ERR);
143 	}
144 	return (WALK_NEXT);
145 }
146 
147 int
148 ip_stacks_walk_step(mdb_walk_state_t *wsp)
149 {
150 	uintptr_t kaddr;
151 	netstack_t nss;
152 
153 	if (mdb_vread(&nss, sizeof (nss), wsp->walk_addr) == -1) {
154 		mdb_warn("can't read netstack at %p", wsp->walk_addr);
155 		return (WALK_ERR);
156 	}
157 	kaddr = (uintptr_t)nss.netstack_modules[NS_IP];
158 
159 	return (wsp->walk_callback(kaddr, wsp->walk_layer, wsp->walk_cbdata));
160 }
161 
162 int
163 th_hash_walk_init(mdb_walk_state_t *wsp)
164 {
165 	GElf_Sym sym;
166 	list_node_t *next;
167 
168 	if (wsp->walk_addr == NULL) {
169 		if (mdb_lookup_by_obj("ip", "ip_thread_list", &sym) == 0) {
170 			wsp->walk_addr = sym.st_value;
171 		} else {
172 			mdb_warn("unable to locate ip_thread_list\n");
173 			return (WALK_ERR);
174 		}
175 	}
176 
177 	if (mdb_vread(&next, sizeof (next),
178 	    wsp->walk_addr + offsetof(list_t, list_head) +
179 	    offsetof(list_node_t, list_next)) == -1 ||
180 	    next == NULL) {
181 		mdb_warn("non-DEBUG image; cannot walk th_hash list\n");
182 		return (WALK_ERR);
183 	}
184 
185 	if (mdb_layered_walk("list", wsp) == -1) {
186 		mdb_warn("can't walk 'list'");
187 		return (WALK_ERR);
188 	} else {
189 		return (WALK_NEXT);
190 	}
191 }
192 
193 int
194 th_hash_walk_step(mdb_walk_state_t *wsp)
195 {
196 	return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer,
197 	    wsp->walk_cbdata));
198 }
199 
200 /*
201  * Called with walk_addr being the address of ips_ill_g_heads
202  */
203 int
204 illif_stack_walk_init(mdb_walk_state_t *wsp)
205 {
206 	illif_walk_data_t *iw;
207 
208 	if (wsp->walk_addr == NULL) {
209 		mdb_warn("illif_stack supports only local walks\n");
210 		return (WALK_ERR);
211 	}
212 
213 	iw = mdb_alloc(sizeof (illif_walk_data_t), UM_SLEEP);
214 
215 	if (mdb_vread(iw->ill_g_heads, MAX_G_HEADS * sizeof (ill_g_head_t),
216 	    wsp->walk_addr) == -1) {
217 		mdb_warn("failed to read 'ips_ill_g_heads' at %p",
218 		    wsp->walk_addr);
219 		mdb_free(iw, sizeof (illif_walk_data_t));
220 		return (WALK_ERR);
221 	}
222 
223 	iw->ill_list = 0;
224 	wsp->walk_addr = (uintptr_t)iw->ill_g_heads[0].ill_g_list_head;
225 	wsp->walk_data = iw;
226 
227 	return (WALK_NEXT);
228 }
229 
230 int
231 illif_stack_walk_step(mdb_walk_state_t *wsp)
232 {
233 	uintptr_t addr = wsp->walk_addr;
234 	illif_walk_data_t *iw = wsp->walk_data;
235 	int list = iw->ill_list;
236 
237 	if (mdb_vread(&iw->ill_if, sizeof (ill_if_t), addr) == -1) {
238 		mdb_warn("failed to read ill_if_t at %p", addr);
239 		return (WALK_ERR);
240 	}
241 
242 	wsp->walk_addr = (uintptr_t)iw->ill_if.illif_next;
243 
244 	if (wsp->walk_addr ==
245 	    (uintptr_t)iw->ill_g_heads[list].ill_g_list_head) {
246 
247 		if (++list >= MAX_G_HEADS)
248 			return (WALK_DONE);
249 
250 		iw->ill_list = list;
251 		wsp->walk_addr =
252 		    (uintptr_t)iw->ill_g_heads[list].ill_g_list_head;
253 		return (WALK_NEXT);
254 	}
255 
256 	return (wsp->walk_callback(addr, iw, wsp->walk_cbdata));
257 }
258 
259 void
260 illif_stack_walk_fini(mdb_walk_state_t *wsp)
261 {
262 	mdb_free(wsp->walk_data, sizeof (illif_walk_data_t));
263 }
264 
265 typedef struct illif_cbdata {
266 	uint_t ill_flags;
267 	uintptr_t ill_addr;
268 	int ill_printlist;	/* list to be printed (MAX_G_HEADS for all) */
269 	boolean_t ill_printed;
270 } illif_cbdata_t;
271 
272 static int
273 illif_cb(uintptr_t addr, const illif_walk_data_t *iw, illif_cbdata_t *id)
274 {
275 	const char *version;
276 
277 	if (id->ill_printlist < MAX_G_HEADS &&
278 	    id->ill_printlist != iw->ill_list)
279 		return (WALK_NEXT);
280 
281 	if (id->ill_flags & DCMD_ADDRSPEC && id->ill_addr != addr)
282 		return (WALK_NEXT);
283 
284 	if (id->ill_flags & DCMD_PIPE_OUT) {
285 		mdb_printf("%p\n", addr);
286 		return (WALK_NEXT);
287 	}
288 
289 	switch (iw->ill_list) {
290 		case IP_V4_G_HEAD:	version = "v4";	break;
291 		case IP_V6_G_HEAD:	version = "v6";	break;
292 		default:		version = "??"; break;
293 	}
294 
295 	mdb_printf("%?p %2s %?p %10d %?p %s\n",
296 	    addr, version, addr + offsetof(ill_if_t, illif_avl_by_ppa),
297 	    iw->ill_if.illif_avl_by_ppa.avl_numnodes,
298 	    iw->ill_if.illif_ppa_arena, iw->ill_if.illif_name);
299 
300 	id->ill_printed = TRUE;
301 
302 	return (WALK_NEXT);
303 }
304 
305 int
306 ip_stacks_common_walk_init(mdb_walk_state_t *wsp)
307 {
308 	if (mdb_layered_walk("ip_stacks", wsp) == -1) {
309 		mdb_warn("can't walk 'ip_stacks'");
310 		return (WALK_ERR);
311 	}
312 
313 	return (WALK_NEXT);
314 }
315 
316 int
317 illif_walk_step(mdb_walk_state_t *wsp)
318 {
319 	uintptr_t kaddr;
320 
321 	kaddr = wsp->walk_addr + OFFSETOF(ip_stack_t, ips_ill_g_heads);
322 
323 	if (mdb_vread(&kaddr, sizeof (kaddr), kaddr) == -1) {
324 		mdb_warn("can't read ips_ip_cache_table at %p", kaddr);
325 		return (WALK_ERR);
326 	}
327 
328 	if (mdb_pwalk("illif_stack", wsp->walk_callback,
329 	    wsp->walk_cbdata, kaddr) == -1) {
330 		mdb_warn("couldn't walk 'illif_stack' for ips_ill_g_heads %p",
331 		    kaddr);
332 		return (WALK_ERR);
333 	}
334 	return (WALK_NEXT);
335 }
336 
337 int
338 illif(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
339 {
340 	illif_cbdata_t id;
341 	ill_if_t ill_if;
342 	const char *opt_P = NULL;
343 	int printlist = MAX_G_HEADS;
344 
345 	if (mdb_getopts(argc, argv,
346 	    'P', MDB_OPT_STR, &opt_P, NULL) != argc)
347 		return (DCMD_USAGE);
348 
349 	if (opt_P != NULL) {
350 		if (strcmp("v4", opt_P) == 0) {
351 			printlist = IP_V4_G_HEAD;
352 		} else if (strcmp("v6", opt_P) == 0) {
353 			printlist = IP_V6_G_HEAD;
354 		} else {
355 			mdb_warn("invalid protocol '%s'\n", opt_P);
356 			return (DCMD_USAGE);
357 		}
358 	}
359 
360 	if (DCMD_HDRSPEC(flags) && (flags & DCMD_PIPE_OUT) == 0) {
361 		mdb_printf("%<u>%?s %2s %?s %10s %?s %-10s%</u>\n",
362 		    "ADDR", "IP", "AVLADDR", "NUMNODES", "ARENA", "NAME");
363 	}
364 
365 	id.ill_flags = flags;
366 	id.ill_addr = addr;
367 	id.ill_printlist = printlist;
368 	id.ill_printed = FALSE;
369 
370 	if (mdb_walk("illif", (mdb_walk_cb_t)illif_cb, &id) == -1) {
371 		mdb_warn("can't walk ill_if_t structures");
372 		return (DCMD_ERR);
373 	}
374 
375 	if (!(flags & DCMD_ADDRSPEC) || opt_P != NULL || id.ill_printed)
376 		return (DCMD_OK);
377 
378 	/*
379 	 * If an address is specified and the walk doesn't find it,
380 	 * print it anyway.
381 	 */
382 	if (mdb_vread(&ill_if, sizeof (ill_if_t), addr) == -1) {
383 		mdb_warn("failed to read ill_if_t at %p", addr);
384 		return (DCMD_ERR);
385 	}
386 
387 	mdb_printf("%?p %2s %?p %10d %?p %s\n",
388 	    addr, "??", addr + offsetof(ill_if_t, illif_avl_by_ppa),
389 	    ill_if.illif_avl_by_ppa.avl_numnodes,
390 	    ill_if.illif_ppa_arena, ill_if.illif_name);
391 
392 	return (DCMD_OK);
393 }
394 
395 static void
396 illif_help(void)
397 {
398 	mdb_printf("Options:\n");
399 	mdb_printf("\t-P v4 | v6"
400 	    "\tfilter interface structures for the specified protocol\n");
401 }
402 
403 int
404 ire_walk_init(mdb_walk_state_t *wsp)
405 {
406 	if (mdb_layered_walk("ire_cache", wsp) == -1) {
407 		mdb_warn("can't walk 'ire_cache'");
408 		return (WALK_ERR);
409 	}
410 
411 	return (WALK_NEXT);
412 }
413 
414 int
415 ire_walk_step(mdb_walk_state_t *wsp)
416 {
417 	ire_t ire;
418 
419 	if (mdb_vread(&ire, sizeof (ire), wsp->walk_addr) == -1) {
420 		mdb_warn("can't read ire at %p", wsp->walk_addr);
421 		return (WALK_ERR);
422 	}
423 
424 	return (wsp->walk_callback(wsp->walk_addr, &ire, wsp->walk_cbdata));
425 }
426 
427 
428 int
429 ire_ctable_walk_step(mdb_walk_state_t *wsp)
430 {
431 	uintptr_t kaddr;
432 	irb_t *irb;
433 	uint32_t cache_table_size;
434 	int i;
435 	ire_cbdata_t ire_cb;
436 
437 	ire_cb.verbose = B_FALSE;
438 	ire_cb.ire_ipversion = 0;
439 
440 
441 	kaddr = wsp->walk_addr + OFFSETOF(ip_stack_t, ips_ip_cache_table_size);
442 
443 	if (mdb_vread(&cache_table_size, sizeof (uint32_t), kaddr) == -1) {
444 		mdb_warn("can't read ips_ip_cache_table at %p", kaddr);
445 		return (WALK_ERR);
446 	}
447 
448 	kaddr = wsp->walk_addr + OFFSETOF(ip_stack_t, ips_ip_cache_table);
449 	if (mdb_vread(&kaddr, sizeof (kaddr), kaddr) == -1) {
450 		mdb_warn("can't read ips_ip_cache_table at %p", kaddr);
451 		return (WALK_ERR);
452 	}
453 
454 	irb = mdb_alloc(sizeof (irb_t) * cache_table_size, UM_SLEEP|UM_GC);
455 	if (mdb_vread(irb, sizeof (irb_t) * cache_table_size, kaddr) == -1) {
456 		mdb_warn("can't read irb at %p", kaddr);
457 		return (WALK_ERR);
458 	}
459 	for (i = 0; i < cache_table_size; i++) {
460 		kaddr = (uintptr_t)irb[i].irb_ire;
461 
462 		if (mdb_pwalk("ire_next", ire_format, &ire_cb,
463 		    kaddr) == -1) {
464 			mdb_warn("can't walk 'ire_next' for ire %p", kaddr);
465 			return (WALK_ERR);
466 		}
467 	}
468 	return (WALK_NEXT);
469 }
470 
471 /* ARGSUSED */
472 int
473 ire_next_walk_init(mdb_walk_state_t *wsp)
474 {
475 	return (WALK_NEXT);
476 }
477 
478 int
479 ire_next_walk_step(mdb_walk_state_t *wsp)
480 {
481 	ire_t ire;
482 	int status;
483 
484 
485 	if (wsp->walk_addr == NULL)
486 		return (WALK_DONE);
487 
488 	if (mdb_vread(&ire, sizeof (ire), wsp->walk_addr) == -1) {
489 		mdb_warn("can't read ire at %p", wsp->walk_addr);
490 		return (WALK_ERR);
491 	}
492 	status = wsp->walk_callback(wsp->walk_addr, &ire,
493 	    wsp->walk_cbdata);
494 
495 	if (status != WALK_NEXT)
496 		return (status);
497 
498 	wsp->walk_addr = (uintptr_t)ire.ire_next;
499 	return (status);
500 }
501 
502 static int
503 ire_format(uintptr_t addr, const void *ire_arg, void *ire_cb_arg)
504 {
505 	const ire_t *irep = ire_arg;
506 	ire_cbdata_t *ire_cb = ire_cb_arg;
507 	boolean_t verbose = ire_cb->verbose;
508 
509 	static const mdb_bitmask_t tmasks[] = {
510 		{ "BROADCAST",	IRE_BROADCAST,		IRE_BROADCAST	},
511 		{ "DEFAULT",	IRE_DEFAULT,		IRE_DEFAULT	},
512 		{ "LOCAL",	IRE_LOCAL,		IRE_LOCAL	},
513 		{ "LOOPBACK",	IRE_LOOPBACK,		IRE_LOOPBACK	},
514 		{ "PREFIX",	IRE_PREFIX,		IRE_PREFIX	},
515 		{ "CACHE",	IRE_CACHE,		IRE_CACHE	},
516 		{ "IF_NORESOLVER", IRE_IF_NORESOLVER,	IRE_IF_NORESOLVER },
517 		{ "IF_RESOLVER", IRE_IF_RESOLVER,	IRE_IF_RESOLVER	},
518 		{ "HOST",	IRE_HOST,		IRE_HOST	},
519 		{ "HOST_REDIRECT", IRE_HOST_REDIRECT,	IRE_HOST_REDIRECT },
520 		{ NULL,		0,			0		}
521 	};
522 
523 	static const mdb_bitmask_t mmasks[] = {
524 		{ "CONDEMNED",	IRE_MARK_CONDEMNED,	IRE_MARK_CONDEMNED },
525 		{ "TESTHIDDEN",	IRE_MARK_TESTHIDDEN,	IRE_MARK_TESTHIDDEN },
526 		{ "NOADD",	IRE_MARK_NOADD,		IRE_MARK_NOADD	},
527 		{ "TEMPORARY",	IRE_MARK_TEMPORARY,	IRE_MARK_TEMPORARY },
528 		{ "USESRC",	IRE_MARK_USESRC_CHECK,	IRE_MARK_USESRC_CHECK },
529 		{ "PRIVATE",	IRE_MARK_PRIVATE_ADDR,	IRE_MARK_PRIVATE_ADDR },
530 		{ "UNCACHED",	IRE_MARK_UNCACHED,	IRE_MARK_UNCACHED },
531 		{ NULL,		0,			0		}
532 	};
533 
534 	static const mdb_bitmask_t fmasks[] = {
535 		{ "UP",		RTF_UP,			RTF_UP		},
536 		{ "GATEWAY",	RTF_GATEWAY,		RTF_GATEWAY	},
537 		{ "HOST",	RTF_HOST,		RTF_HOST	},
538 		{ "REJECT",	RTF_REJECT,		RTF_REJECT	},
539 		{ "DYNAMIC",	RTF_DYNAMIC,		RTF_DYNAMIC	},
540 		{ "MODIFIED",	RTF_MODIFIED,		RTF_MODIFIED	},
541 		{ "DONE",	RTF_DONE,		RTF_DONE	},
542 		{ "MASK",	RTF_MASK,		RTF_MASK	},
543 		{ "CLONING",	RTF_CLONING,		RTF_CLONING	},
544 		{ "XRESOLVE",	RTF_XRESOLVE,		RTF_XRESOLVE	},
545 		{ "LLINFO",	RTF_LLINFO,		RTF_LLINFO	},
546 		{ "STATIC",	RTF_STATIC,		RTF_STATIC	},
547 		{ "BLACKHOLE",	RTF_BLACKHOLE,		RTF_BLACKHOLE	},
548 		{ "PRIVATE",	RTF_PRIVATE,		RTF_PRIVATE	},
549 		{ "PROTO2",	RTF_PROTO2,		RTF_PROTO2	},
550 		{ "PROTO1",	RTF_PROTO1,		RTF_PROTO1	},
551 		{ "MULTIRT",	RTF_MULTIRT,		RTF_MULTIRT	},
552 		{ "SETSRC",	RTF_SETSRC,		RTF_SETSRC	},
553 		{ NULL,		0,			0		}
554 	};
555 
556 	if (ire_cb->ire_ipversion != 0 &&
557 	    irep->ire_ipversion != ire_cb->ire_ipversion)
558 		return (WALK_NEXT);
559 
560 	if (irep->ire_ipversion == IPV6_VERSION && verbose) {
561 
562 		mdb_printf("%<b>%?p%</b> %40N <%hb>\n"
563 		    "%?s %40N <%hb>\n"
564 		    "%?s %40d %4d <%hb>\n",
565 		    addr, &irep->ire_src_addr_v6, irep->ire_type, tmasks,
566 		    "", &irep->ire_addr_v6, (ushort_t)irep->ire_marks, mmasks,
567 		    "", ips_to_stackid((uintptr_t)irep->ire_ipst),
568 		    irep->ire_zoneid,
569 		    irep->ire_flags, fmasks);
570 
571 	} else if (irep->ire_ipversion == IPV6_VERSION) {
572 
573 		mdb_printf("%?p %30N %30N %5d %4d\n",
574 		    addr, &irep->ire_src_addr_v6,
575 		    &irep->ire_addr_v6,
576 		    ips_to_stackid((uintptr_t)irep->ire_ipst),
577 		    irep->ire_zoneid);
578 
579 	} else if (verbose) {
580 
581 		mdb_printf("%<b>%?p%</b> %40I <%hb>\n"
582 		    "%?s %40I <%hb>\n"
583 		    "%?s %40d %4d <%hb>\n",
584 		    addr, irep->ire_src_addr, irep->ire_type, tmasks,
585 		    "", irep->ire_addr, (ushort_t)irep->ire_marks, mmasks,
586 		    "", ips_to_stackid((uintptr_t)irep->ire_ipst),
587 		    irep->ire_zoneid, irep->ire_flags, fmasks);
588 
589 	} else {
590 
591 		mdb_printf("%?p %30I %30I %5d %4d\n", addr, irep->ire_src_addr,
592 		    irep->ire_addr, ips_to_stackid((uintptr_t)irep->ire_ipst),
593 		    irep->ire_zoneid);
594 	}
595 
596 	return (WALK_NEXT);
597 }
598 
599 /*
600  * There are faster ways to do this.  Given the interactive nature of this
601  * use I don't think its worth much effort.
602  */
603 static unsigned short
604 ipcksum(void *p, int len)
605 {
606 	int32_t	sum = 0;
607 
608 	while (len > 1) {
609 		/* alignment */
610 		sum += *(uint16_t *)p;
611 		p = (char *)p + sizeof (uint16_t);
612 		if (sum & 0x80000000)
613 			sum = (sum & 0xFFFF) + (sum >> 16);
614 		len -= 2;
615 	}
616 
617 	if (len)
618 		sum += (uint16_t)*(unsigned char *)p;
619 
620 	while (sum >> 16)
621 		sum = (sum & 0xFFFF) + (sum >> 16);
622 
623 	return (~sum);
624 }
625 
626 static const mdb_bitmask_t tcp_flags[] = {
627 	{ "SYN",	TH_SYN,		TH_SYN	},
628 	{ "ACK",	TH_ACK,		TH_ACK	},
629 	{ "FIN",	TH_FIN,		TH_FIN	},
630 	{ "RST",	TH_RST,		TH_RST	},
631 	{ "PSH",	TH_PUSH,	TH_PUSH	},
632 	{ "ECE",	TH_ECE,		TH_ECE	},
633 	{ "CWR",	TH_CWR,		TH_CWR	},
634 	{ NULL,		0,		0	}
635 };
636 
637 static void
638 tcphdr_print(struct tcphdr *tcph)
639 {
640 	in_port_t	sport, dport;
641 	tcp_seq		seq, ack;
642 	uint16_t	win, urp;
643 
644 	mdb_printf("%<b>TCP header%</b>\n");
645 
646 	mdb_nhconvert(&sport, &tcph->th_sport, sizeof (sport));
647 	mdb_nhconvert(&dport, &tcph->th_dport, sizeof (dport));
648 	mdb_nhconvert(&seq, &tcph->th_seq, sizeof (seq));
649 	mdb_nhconvert(&ack, &tcph->th_ack, sizeof (ack));
650 	mdb_nhconvert(&win, &tcph->th_win, sizeof (win));
651 	mdb_nhconvert(&urp, &tcph->th_urp, sizeof (urp));
652 
653 	mdb_printf("%<u>%6s %6s %10s %10s %4s %5s %5s %5s %-15s%</u>\n",
654 	    "SPORT", "DPORT", "SEQ", "ACK", "HLEN", "WIN", "CSUM", "URP",
655 	    "FLAGS");
656 	mdb_printf("%6hu %6hu %10u %10u %4d %5hu %5hu %5hu <%b>\n",
657 	    sport, dport, seq, ack, tcph->th_off << 2, win,
658 	    tcph->th_sum, urp, tcph->th_flags, tcp_flags);
659 	mdb_printf("0x%04x 0x%04x 0x%08x 0x%08x\n\n",
660 	    sport, dport, seq, ack);
661 }
662 
663 /* ARGSUSED */
664 static int
665 tcphdr(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
666 {
667 	struct tcphdr	tcph;
668 
669 	if (!(flags & DCMD_ADDRSPEC))
670 		return (DCMD_USAGE);
671 
672 	if (mdb_vread(&tcph, sizeof (tcph), addr) == -1) {
673 		mdb_warn("failed to read TCP header at %p", addr);
674 		return (DCMD_ERR);
675 	}
676 	tcphdr_print(&tcph);
677 	return (DCMD_OK);
678 }
679 
680 static void
681 udphdr_print(struct udphdr *udph)
682 {
683 	in_port_t	sport, dport;
684 	uint16_t	hlen;
685 
686 	mdb_printf("%<b>UDP header%</b>\n");
687 
688 	mdb_nhconvert(&sport, &udph->uh_sport, sizeof (sport));
689 	mdb_nhconvert(&dport, &udph->uh_dport, sizeof (dport));
690 	mdb_nhconvert(&hlen, &udph->uh_ulen, sizeof (hlen));
691 
692 	mdb_printf("%<u>%14s %14s %5s %6s%</u>\n",
693 	    "SPORT", "DPORT", "LEN", "CSUM");
694 	mdb_printf("%5hu (0x%04x) %5hu (0x%04x) %5hu 0x%04hx\n\n", sport, sport,
695 	    dport, dport, hlen, udph->uh_sum);
696 }
697 
698 /* ARGSUSED */
699 static int
700 udphdr(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
701 {
702 	struct udphdr	udph;
703 
704 	if (!(flags & DCMD_ADDRSPEC))
705 		return (DCMD_USAGE);
706 
707 	if (mdb_vread(&udph, sizeof (udph), addr) == -1) {
708 		mdb_warn("failed to read UDP header at %p", addr);
709 		return (DCMD_ERR);
710 	}
711 	udphdr_print(&udph);
712 	return (DCMD_OK);
713 }
714 
715 static void
716 sctphdr_print(sctp_hdr_t *sctph)
717 {
718 	in_port_t sport, dport;
719 
720 	mdb_printf("%<b>SCTP header%</b>\n");
721 	mdb_nhconvert(&sport, &sctph->sh_sport, sizeof (sport));
722 	mdb_nhconvert(&dport, &sctph->sh_dport, sizeof (dport));
723 
724 	mdb_printf("%<u>%14s %14s %10s %10s%</u>\n",
725 	    "SPORT", "DPORT", "VTAG", "CHKSUM");
726 	mdb_printf("%5hu (0x%04x) %5hu (0x%04x) %10u 0x%08x\n\n", sport, sport,
727 	    dport, dport, sctph->sh_verf, sctph->sh_chksum);
728 }
729 
730 /* ARGSUSED */
731 static int
732 sctphdr(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
733 {
734 	sctp_hdr_t sctph;
735 
736 	if (!(flags & DCMD_ADDRSPEC))
737 		return (DCMD_USAGE);
738 
739 	if (mdb_vread(&sctph, sizeof (sctph), addr) == -1) {
740 		mdb_warn("failed to read SCTP header at %p", addr);
741 		return (DCMD_ERR);
742 	}
743 
744 	sctphdr_print(&sctph);
745 	return (DCMD_OK);
746 }
747 
748 static int
749 transport_hdr(int proto, uintptr_t addr)
750 {
751 	mdb_printf("\n");
752 	switch (proto) {
753 	case IPPROTO_TCP: {
754 		struct tcphdr tcph;
755 
756 		if (mdb_vread(&tcph, sizeof (tcph), addr) == -1) {
757 			mdb_warn("failed to read TCP header at %p", addr);
758 			return (DCMD_ERR);
759 		}
760 		tcphdr_print(&tcph);
761 		break;
762 	}
763 	case IPPROTO_UDP:  {
764 		struct udphdr udph;
765 
766 		if (mdb_vread(&udph, sizeof (udph), addr) == -1) {
767 			mdb_warn("failed to read UDP header at %p", addr);
768 			return (DCMD_ERR);
769 		}
770 		udphdr_print(&udph);
771 		break;
772 	}
773 	case IPPROTO_SCTP: {
774 		sctp_hdr_t sctph;
775 
776 		if (mdb_vread(&sctph, sizeof (sctph), addr) == -1) {
777 			mdb_warn("failed to read SCTP header at %p", addr);
778 			return (DCMD_ERR);
779 		}
780 		sctphdr_print(&sctph);
781 		break;
782 	}
783 	default:
784 		break;
785 	}
786 
787 	return (DCMD_OK);
788 }
789 
790 static const mdb_bitmask_t ip_flags[] = {
791 	{ "DF",	IPH_DF, IPH_DF	},
792 	{ "MF", IPH_MF,	IPH_MF	},
793 	{ NULL, 0,	0	}
794 };
795 
796 /* ARGSUSED */
797 static int
798 iphdr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
799 {
800 	uint_t		verbose = FALSE, force = FALSE;
801 	ipha_t		iph[1];
802 	uint16_t	ver, totlen, hdrlen, ipid, off, csum;
803 	uintptr_t	nxt_proto;
804 	char		exp_csum[8];
805 
806 	if (mdb_getopts(argc, argv,
807 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
808 	    'f', MDB_OPT_SETBITS, TRUE, &force, NULL) != argc)
809 		return (DCMD_USAGE);
810 
811 	if (mdb_vread(iph, sizeof (*iph), addr) == -1) {
812 		mdb_warn("failed to read IPv4 header at %p", addr);
813 		return (DCMD_ERR);
814 	}
815 
816 	ver = (iph->ipha_version_and_hdr_length & 0xf0) >> 4;
817 	if (ver != IPV4_VERSION) {
818 		if (ver == IPV6_VERSION) {
819 			return (ip6hdr(addr, flags, argc, argv));
820 		} else if (!force) {
821 			mdb_warn("unknown IP version: %d\n", ver);
822 			return (DCMD_ERR);
823 		}
824 	}
825 
826 	mdb_printf("%<b>IPv4 header%</b>\n");
827 	mdb_printf("%-34s %-34s\n"
828 	    "%<u>%-4s %-4s %-5s %-5s %-6s %-5s %-5s %-6s %-8s %-6s%</u>\n",
829 	    "SRC", "DST",
830 	    "HLEN", "TOS", "LEN", "ID", "OFFSET", "TTL", "PROTO", "CHKSUM",
831 	    "EXP-CSUM", "FLGS");
832 
833 	hdrlen = (iph->ipha_version_and_hdr_length & 0x0f) << 2;
834 	mdb_nhconvert(&totlen, &iph->ipha_length, sizeof (totlen));
835 	mdb_nhconvert(&ipid, &iph->ipha_ident, sizeof (ipid));
836 	mdb_nhconvert(&off, &iph->ipha_fragment_offset_and_flags, sizeof (off));
837 	if (hdrlen == IP_SIMPLE_HDR_LENGTH) {
838 		if ((csum = ipcksum(iph, sizeof (*iph))) != 0)
839 			csum = ~(~csum + ~iph->ipha_hdr_checksum);
840 		else
841 			csum = iph->ipha_hdr_checksum;
842 		mdb_snprintf(exp_csum, 8, "%u", csum);
843 	} else {
844 		mdb_snprintf(exp_csum, 8, "<n/a>");
845 	}
846 
847 	mdb_printf("%-34I %-34I%\n"
848 	    "%-4d %-4d %-5hu %-5hu %-6hu %-5hu %-5hu %-6u %-8s <%5hb>\n",
849 	    iph->ipha_src, iph->ipha_dst,
850 	    hdrlen, iph->ipha_type_of_service, totlen, ipid,
851 	    (off << 3) & 0xffff, iph->ipha_ttl, iph->ipha_protocol,
852 	    iph->ipha_hdr_checksum, exp_csum, off, ip_flags);
853 
854 	if (verbose) {
855 		nxt_proto = addr + hdrlen;
856 		return (transport_hdr(iph->ipha_protocol, nxt_proto));
857 	} else {
858 		return (DCMD_OK);
859 	}
860 }
861 
862 /* ARGSUSED */
863 static int
864 ip6hdr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
865 {
866 	uint_t		verbose = FALSE, force = FALSE;
867 	ip6_t		iph[1];
868 	int		ver, class, flow;
869 	uint16_t	plen;
870 	uintptr_t	nxt_proto;
871 
872 	if (mdb_getopts(argc, argv,
873 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
874 	    'f', MDB_OPT_SETBITS, TRUE, &force, NULL) != argc)
875 		return (DCMD_USAGE);
876 
877 	if (mdb_vread(iph, sizeof (*iph), addr) == -1) {
878 		mdb_warn("failed to read IPv6 header at %p", addr);
879 		return (DCMD_ERR);
880 	}
881 
882 	ver = (iph->ip6_vfc & 0xf0) >> 4;
883 	if (ver != IPV6_VERSION) {
884 		if (ver == IPV4_VERSION) {
885 			return (iphdr(addr, flags, argc, argv));
886 		} else if (!force) {
887 			mdb_warn("unknown IP version: %d\n", ver);
888 			return (DCMD_ERR);
889 		}
890 	}
891 
892 	mdb_printf("%<b>IPv6 header%</b>\n");
893 	mdb_printf("%<u>%-26s %-26s %4s %7s %5s %3s %3s%</u>\n",
894 	    "SRC", "DST", "TCLS", "FLOW-ID", "PLEN", "NXT", "HOP");
895 
896 	class = (iph->ip6_vcf & IPV6_FLOWINFO_TCLASS) >> 20;
897 	mdb_nhconvert(&class, &class, sizeof (class));
898 	flow = iph->ip6_vcf & IPV6_FLOWINFO_FLOWLABEL;
899 	mdb_nhconvert(&flow, &flow, sizeof (flow));
900 	mdb_nhconvert(&plen, &iph->ip6_plen, sizeof (plen));
901 
902 	mdb_printf("%-26N %-26N %4d %7d %5hu %3d %3d\n",
903 	    &iph->ip6_src, &iph->ip6_dst,
904 	    class, flow, plen, iph->ip6_nxt, iph->ip6_hlim);
905 
906 	if (verbose) {
907 		nxt_proto = addr + sizeof (ip6_t);
908 		return (transport_hdr(iph->ip6_nxt, nxt_proto));
909 	} else {
910 		return (DCMD_OK);
911 	}
912 }
913 
914 int
915 ire(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
916 {
917 	uint_t verbose = FALSE;
918 	ire_t ire;
919 	ire_cbdata_t ire_cb;
920 	int ipversion = 0;
921 	const char *opt_P = NULL;
922 
923 	if (mdb_getopts(argc, argv,
924 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
925 	    'P', MDB_OPT_STR, &opt_P, NULL) != argc)
926 		return (DCMD_USAGE);
927 
928 	if (opt_P != NULL) {
929 		if (strcmp("v4", opt_P) == 0) {
930 			ipversion = IPV4_VERSION;
931 		} else if (strcmp("v6", opt_P) == 0) {
932 			ipversion = IPV6_VERSION;
933 		} else {
934 			mdb_warn("invalid protocol '%s'\n", opt_P);
935 			return (DCMD_USAGE);
936 		}
937 	}
938 
939 	if ((flags & DCMD_LOOPFIRST) || !(flags & DCMD_LOOP)) {
940 
941 		if (verbose) {
942 			mdb_printf("%?s %40s %-20s%\n"
943 			    "%?s %40s %-20s%\n"
944 			    "%<u>%?s %40s %4s %-20s%</u>\n",
945 			    "ADDR", "SRC", "TYPE",
946 			    "", "DST", "MARKS",
947 			    "", "STACK", "ZONE", "FLAGS");
948 		} else {
949 			mdb_printf("%<u>%?s %30s %30s %5s %4s%</u>\n",
950 			    "ADDR", "SRC", "DST", "STACK", "ZONE");
951 		}
952 	}
953 
954 	ire_cb.verbose = (verbose == TRUE);
955 	ire_cb.ire_ipversion = ipversion;
956 
957 	if (flags & DCMD_ADDRSPEC) {
958 		(void) mdb_vread(&ire, sizeof (ire_t), addr);
959 		(void) ire_format(addr, &ire, &ire_cb);
960 	} else if (mdb_walk("ire", (mdb_walk_cb_t)ire_format, &ire_cb) == -1) {
961 		mdb_warn("failed to walk ire table");
962 		return (DCMD_ERR);
963 	}
964 
965 	return (DCMD_OK);
966 }
967 
968 static size_t
969 mi_osize(const queue_t *q)
970 {
971 	/*
972 	 * The code in common/inet/mi.c allocates an extra word to store the
973 	 * size of the allocation.  An mi_o_s is thus a size_t plus an mi_o_s.
974 	 */
975 	struct mi_block {
976 		size_t mi_nbytes;
977 		struct mi_o_s mi_o;
978 	} m;
979 
980 	if (mdb_vread(&m, sizeof (m), (uintptr_t)q->q_ptr -
981 	    sizeof (m)) == sizeof (m))
982 		return (m.mi_nbytes - sizeof (m));
983 
984 	return (0);
985 }
986 
987 static void
988 ip_ill_qinfo(const queue_t *q, char *buf, size_t nbytes)
989 {
990 	char name[32];
991 	ill_t ill;
992 
993 	if (mdb_vread(&ill, sizeof (ill),
994 	    (uintptr_t)q->q_ptr) == sizeof (ill) &&
995 	    mdb_readstr(name, sizeof (name), (uintptr_t)ill.ill_name) > 0)
996 		(void) mdb_snprintf(buf, nbytes, "if: %s", name);
997 }
998 
999 void
1000 ip_qinfo(const queue_t *q, char *buf, size_t nbytes)
1001 {
1002 	size_t size = mi_osize(q);
1003 
1004 	if (size == sizeof (ill_t))
1005 		ip_ill_qinfo(q, buf, nbytes);
1006 }
1007 
1008 uintptr_t
1009 ip_rnext(const queue_t *q)
1010 {
1011 	size_t size = mi_osize(q);
1012 	ill_t ill;
1013 
1014 	if (size == sizeof (ill_t) && mdb_vread(&ill, sizeof (ill),
1015 	    (uintptr_t)q->q_ptr) == sizeof (ill))
1016 		return ((uintptr_t)ill.ill_rq);
1017 
1018 	return (NULL);
1019 }
1020 
1021 uintptr_t
1022 ip_wnext(const queue_t *q)
1023 {
1024 	size_t size = mi_osize(q);
1025 	ill_t ill;
1026 
1027 	if (size == sizeof (ill_t) && mdb_vread(&ill, sizeof (ill),
1028 	    (uintptr_t)q->q_ptr) == sizeof (ill))
1029 		return ((uintptr_t)ill.ill_wq);
1030 
1031 	return (NULL);
1032 }
1033 
1034 /*
1035  * Print the core fields in an squeue_t.  With the "-v" argument,
1036  * provide more verbose output.
1037  */
1038 static int
1039 squeue(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1040 {
1041 	unsigned int	i;
1042 	unsigned int	verbose = FALSE;
1043 	const int	SQUEUE_STATEDELT = (int)(sizeof (uintptr_t) + 9);
1044 	boolean_t	arm;
1045 	squeue_t	squeue;
1046 
1047 	if (!(flags & DCMD_ADDRSPEC)) {
1048 		if (mdb_walk_dcmd("genunix`squeue_cache", "ip`squeue",
1049 		    argc, argv) == -1) {
1050 			mdb_warn("failed to walk squeue cache");
1051 			return (DCMD_ERR);
1052 		}
1053 		return (DCMD_OK);
1054 	}
1055 
1056 	if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL)
1057 	    != argc)
1058 		return (DCMD_USAGE);
1059 
1060 	if (!DCMD_HDRSPEC(flags) && verbose)
1061 		mdb_printf("\n\n");
1062 
1063 	if (DCMD_HDRSPEC(flags) || verbose) {
1064 		mdb_printf("%?s %-5s %-3s %?s %?s %?s\n",
1065 		    "ADDR", "STATE", "CPU",
1066 		    "FIRST", "LAST", "WORKER");
1067 	}
1068 
1069 	if (mdb_vread(&squeue, sizeof (squeue_t), addr) == -1) {
1070 		mdb_warn("cannot read squeue_t at %p", addr);
1071 		return (DCMD_ERR);
1072 	}
1073 
1074 	mdb_printf("%0?p %05x %3d %0?p %0?p %0?p\n",
1075 	    addr, squeue.sq_state, squeue.sq_bind,
1076 	    squeue.sq_first, squeue.sq_last, squeue.sq_worker);
1077 
1078 	if (!verbose)
1079 		return (DCMD_OK);
1080 
1081 	arm = B_TRUE;
1082 	for (i = 0; squeue_states[i].bit_name != NULL; i++) {
1083 		if (((squeue.sq_state) & (1 << i)) == 0)
1084 			continue;
1085 
1086 		if (arm) {
1087 			mdb_printf("%*s|\n", SQUEUE_STATEDELT, "");
1088 			mdb_printf("%*s+-->  ", SQUEUE_STATEDELT, "");
1089 			arm = B_FALSE;
1090 		} else
1091 			mdb_printf("%*s      ", SQUEUE_STATEDELT, "");
1092 
1093 		mdb_printf("%-12s %s\n", squeue_states[i].bit_name,
1094 		    squeue_states[i].bit_descr);
1095 	}
1096 
1097 	return (DCMD_OK);
1098 }
1099 
1100 static void
1101 ip_squeue_help(void)
1102 {
1103 	mdb_printf("Print the core information for a given NCA squeue_t.\n\n");
1104 	mdb_printf("Options:\n");
1105 	mdb_printf("\t-v\tbe verbose (more descriptive)\n");
1106 }
1107 
1108 /*
1109  * This is called by ::th_trace (via a callback) when walking the th_hash
1110  * list.  It calls modent to find the entries.
1111  */
1112 /* ARGSUSED */
1113 static int
1114 modent_summary(uintptr_t addr, const void *data, void *private)
1115 {
1116 	th_walk_data_t *thw = private;
1117 	const struct mod_hash_entry *mhe = data;
1118 	th_trace_t th;
1119 
1120 	if (mdb_vread(&th, sizeof (th), (uintptr_t)mhe->mhe_val) == -1) {
1121 		mdb_warn("failed to read th_trace_t %p", mhe->mhe_val);
1122 		return (WALK_ERR);
1123 	}
1124 
1125 	if (th.th_refcnt == 0 && thw->thw_non_zero_only)
1126 		return (WALK_NEXT);
1127 
1128 	if (!thw->thw_match) {
1129 		mdb_printf("%?p %?p %?p %8d %?p\n", thw->thw_ipst, mhe->mhe_key,
1130 		    mhe->mhe_val, th.th_refcnt, th.th_id);
1131 	} else if (thw->thw_matchkey == (uintptr_t)mhe->mhe_key) {
1132 		int i, j, k;
1133 		tr_buf_t *tr;
1134 
1135 		mdb_printf("Object %p in IP stack %p:\n", mhe->mhe_key,
1136 		    thw->thw_ipst);
1137 		i = th.th_trace_lastref;
1138 		mdb_printf("\tThread %p refcnt %d:\n", th.th_id,
1139 		    th.th_refcnt);
1140 		for (j = TR_BUF_MAX; j > 0; j--) {
1141 			tr = th.th_trbuf + i;
1142 			if (tr->tr_depth == 0 || tr->tr_depth > TR_STACK_DEPTH)
1143 				break;
1144 			mdb_printf("\t  T%+ld:\n", tr->tr_time -
1145 			    thw->thw_lbolt);
1146 			for (k = 0; k < tr->tr_depth; k++)
1147 				mdb_printf("\t\t%a\n", tr->tr_stack[k]);
1148 			if (--i < 0)
1149 				i = TR_BUF_MAX - 1;
1150 		}
1151 	}
1152 	return (WALK_NEXT);
1153 }
1154 
1155 /*
1156  * This is called by ::th_trace (via a callback) when walking the th_hash
1157  * list.  It calls modent to find the entries.
1158  */
1159 /* ARGSUSED */
1160 static int
1161 th_hash_summary(uintptr_t addr, const void *data, void *private)
1162 {
1163 	const th_hash_t *thh = data;
1164 	th_walk_data_t *thw = private;
1165 
1166 	thw->thw_ipst = (uintptr_t)thh->thh_ipst;
1167 	return (mdb_pwalk("modent", modent_summary, private,
1168 	    (uintptr_t)thh->thh_hash));
1169 }
1170 
1171 /*
1172  * Print or summarize the th_trace_t structures.
1173  */
1174 static int
1175 th_trace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1176 {
1177 	th_walk_data_t thw;
1178 
1179 	(void) memset(&thw, 0, sizeof (thw));
1180 
1181 	if (mdb_getopts(argc, argv,
1182 	    'n', MDB_OPT_SETBITS, TRUE, &thw.thw_non_zero_only,
1183 	    NULL) != argc)
1184 		return (DCMD_USAGE);
1185 
1186 	if (!(flags & DCMD_ADDRSPEC)) {
1187 		/*
1188 		 * No address specified.  Walk all of the th_hash_t in the
1189 		 * system, and summarize the th_trace_t entries in each.
1190 		 */
1191 		mdb_printf("%?s %?s %?s %8s %?s\n",
1192 		    "IPSTACK", "OBJECT", "TRACE", "REFCNT", "THREAD");
1193 		thw.thw_match = B_FALSE;
1194 	} else {
1195 		thw.thw_match = B_TRUE;
1196 		thw.thw_matchkey = addr;
1197 		if (mdb_readvar(&thw.thw_lbolt,
1198 		    mdb_prop_postmortem ? "panic_lbolt" : "lbolt") == -1) {
1199 			mdb_warn("failed to read lbolt");
1200 			return (DCMD_ERR);
1201 		}
1202 	}
1203 	if (mdb_pwalk("th_hash", th_hash_summary, &thw, NULL) == -1) {
1204 		mdb_warn("can't walk th_hash entries");
1205 		return (DCMD_ERR);
1206 	}
1207 	return (DCMD_OK);
1208 }
1209 
1210 static void
1211 th_trace_help(void)
1212 {
1213 	mdb_printf("If given an address of an ill_t, ipif_t, ire_t, or nce_t, "
1214 	    "print the\n"
1215 	    "corresponding th_trace_t structure in detail.  Otherwise, if no "
1216 	    "address is\n"
1217 	    "given, then summarize all th_trace_t structures.\n\n");
1218 	mdb_printf("Options:\n"
1219 	    "\t-n\tdisplay only entries with non-zero th_refcnt\n");
1220 }
1221 
1222 static const mdb_dcmd_t dcmds[] = {
1223 	{ "illif", "?[-P v4 | v6]",
1224 	    "display or filter IP Lower Level InterFace structures", illif,
1225 	    illif_help },
1226 	{ "iphdr", ":[-vf]", "display an IPv4 header", iphdr },
1227 	{ "ip6hdr", ":[-vf]", "display an IPv6 header", ip6hdr },
1228 	{ "ire", "?[-v] [-P v4|v6]",
1229 	    "display Internet Route Entry structures", ire },
1230 	{ "nce", "?[-P v4 | v6]", "display Neighbor Cache Entry structures",
1231 	    nce },
1232 	{ "squeue", ":[-v]", "print core squeue_t info", squeue,
1233 	    ip_squeue_help },
1234 	{ "tcphdr", ":", "display a TCP header", tcphdr },
1235 	{ "udphdr", ":", "display an UDP header", udphdr },
1236 	{ "sctphdr", ":", "display an SCTP header", sctphdr },
1237 	{ "th_trace", "?[-n]", "display th_trace_t structures", th_trace,
1238 	    th_trace_help },
1239 	{ NULL }
1240 };
1241 
1242 static const mdb_walker_t walkers[] = {
1243 	{ "illif", "walk list of ill interface types for all stacks",
1244 		ip_stacks_common_walk_init, illif_walk_step, NULL },
1245 	{ "illif_stack", "walk list of ill interface types",
1246 		illif_stack_walk_init, illif_stack_walk_step,
1247 		illif_stack_walk_fini },
1248 	{ "ire", "walk active ire_t structures",
1249 		ire_walk_init, ire_walk_step, NULL },
1250 	{ "ire_ctable", "walk ire_t structures in the ctable",
1251 		ip_stacks_common_walk_init, ire_ctable_walk_step, NULL },
1252 	{ "ire_next", "walk ire_t structures in the ctable",
1253 		ire_next_walk_init, ire_next_walk_step, NULL },
1254 	{ "ip_stacks", "walk all the ip_stack_t",
1255 		ip_stacks_walk_init, ip_stacks_walk_step, NULL },
1256 	{ "th_hash", "walk all the th_hash_t entries",
1257 		th_hash_walk_init, th_hash_walk_step, NULL },
1258 	{ "nce", "walk list of nce structures for all stacks",
1259 		ip_stacks_common_walk_init, nce_walk_step, NULL },
1260 	{ "nce_stack", "walk list of nce structures",
1261 		nce_stack_walk_init, nce_stack_walk_step,
1262 		nce_stack_walk_fini},
1263 	{ NULL }
1264 };
1265 
1266 static const mdb_qops_t ip_qops = { ip_qinfo, ip_rnext, ip_wnext };
1267 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
1268 
1269 const mdb_modinfo_t *
1270 _mdb_init(void)
1271 {
1272 	GElf_Sym sym;
1273 
1274 	if (mdb_lookup_by_obj("ip", "ipwinit", &sym) == 0)
1275 		mdb_qops_install(&ip_qops, (uintptr_t)sym.st_value);
1276 
1277 	return (&modinfo);
1278 }
1279 
1280 void
1281 _mdb_fini(void)
1282 {
1283 	GElf_Sym sym;
1284 
1285 	if (mdb_lookup_by_obj("ip", "ipwinit", &sym) == 0)
1286 		mdb_qops_remove(&ip_qops, (uintptr_t)sym.st_value);
1287 }
1288 
1289 static char *
1290 nce_state(int nce_state)
1291 {
1292 	switch (nce_state) {
1293 	case ND_UNCHANGED:
1294 		return ("unchanged");
1295 	case ND_INCOMPLETE:
1296 		return ("incomplete");
1297 	case ND_REACHABLE:
1298 		return ("reachable");
1299 	case ND_STALE:
1300 		return ("stale");
1301 	case ND_DELAY:
1302 		return ("delay");
1303 	case ND_PROBE:
1304 		return ("probe");
1305 	case ND_UNREACHABLE:
1306 		return ("unreach");
1307 	case ND_INITIAL:
1308 		return ("initial");
1309 	default:
1310 		return ("??");
1311 	}
1312 }
1313 
1314 static char *
1315 nce_l2_addr(const nce_t *nce, const ill_t *ill)
1316 {
1317 	uchar_t *h;
1318 	static char addr_buf[L2MAXADDRSTRLEN];
1319 	mblk_t mp;
1320 	size_t mblen;
1321 
1322 	if (ill->ill_flags & ILLF_XRESOLV) {
1323 		return ("XRESOLV");
1324 	}
1325 
1326 	if (nce->nce_res_mp == NULL) {
1327 		return ("None");
1328 	}
1329 
1330 	if (ill->ill_net_type == IRE_IF_RESOLVER) {
1331 
1332 		if (mdb_vread(&mp, sizeof (mblk_t),
1333 		    (uintptr_t)nce->nce_res_mp) == -1) {
1334 			mdb_warn("failed to read nce_res_mp at %p",
1335 			    nce->nce_res_mp);
1336 		}
1337 
1338 		if (ill->ill_nd_lla_len == 0)
1339 			return ("None");
1340 		mblen = mp.b_wptr - mp.b_rptr;
1341 		if (mblen > (sizeof (dl_unitdata_req_t) + MAX_SAP_LEN) ||
1342 		    ill->ill_nd_lla_len > MAX_SAP_LEN ||
1343 		    NCE_LL_ADDR_OFFSET(ill) + ill->ill_nd_lla_len > mblen) {
1344 			return ("Truncated");
1345 		}
1346 		h = mdb_zalloc(mblen, UM_SLEEP);
1347 		if (mdb_vread(h, mblen, (uintptr_t)(mp.b_rptr)) == -1) {
1348 			mdb_warn("failed to read hwaddr at %p",
1349 			    mp.b_rptr + NCE_LL_ADDR_OFFSET(ill));
1350 			return ("Unknown");
1351 		}
1352 		mdb_mac_addr(h + NCE_LL_ADDR_OFFSET(ill), ill->ill_nd_lla_len,
1353 		    addr_buf, sizeof (addr_buf));
1354 	} else {
1355 		return ("None");
1356 	}
1357 	mdb_free(h, mblen);
1358 	return (addr_buf);
1359 }
1360 
1361 static void
1362 nce_header(uint_t flags)
1363 {
1364 	if ((flags & DCMD_LOOPFIRST) || !(flags & DCMD_LOOP)) {
1365 
1366 		mdb_printf("%<u>%?s %-20s %-10s %-8s %-5s %s%</u>\n",
1367 		    "ADDR", "HW_ADDR", "STATE", "FLAGS", "ILL", "IP ADDR");
1368 	}
1369 }
1370 
1371 int
1372 nce(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1373 {
1374 	nce_t nce;
1375 	nce_cbdata_t id;
1376 	int ipversion = 0;
1377 	const char *opt_P = NULL;
1378 
1379 	if (mdb_getopts(argc, argv,
1380 	    'P', MDB_OPT_STR, &opt_P, NULL) != argc)
1381 		return (DCMD_USAGE);
1382 
1383 	if (opt_P != NULL) {
1384 		if (strcmp("v4", opt_P) == 0) {
1385 			ipversion = IPV4_VERSION;
1386 		} else if (strcmp("v6", opt_P) == 0) {
1387 			ipversion = IPV6_VERSION;
1388 		} else {
1389 			mdb_warn("invalid protocol '%s'\n", opt_P);
1390 			return (DCMD_USAGE);
1391 		}
1392 	}
1393 
1394 	if (flags & DCMD_ADDRSPEC) {
1395 
1396 		if (mdb_vread(&nce, sizeof (nce_t), addr) == -1) {
1397 			mdb_warn("failed to read nce at %p\n", addr);
1398 			return (DCMD_ERR);
1399 		}
1400 		if (ipversion != 0 && nce.nce_ipversion != ipversion) {
1401 			mdb_printf("IP Version mismatch\n");
1402 			return (DCMD_ERR);
1403 		}
1404 		nce_header(flags);
1405 		return (nce_format(addr, &nce, ipversion));
1406 
1407 	} else {
1408 		id.nce_addr = addr;
1409 		id.nce_ipversion = ipversion;
1410 		nce_header(flags);
1411 		if (mdb_walk("nce", (mdb_walk_cb_t)nce_cb, &id) == -1) {
1412 			mdb_warn("failed to walk nce table\n");
1413 			return (DCMD_ERR);
1414 		}
1415 	}
1416 	return (DCMD_OK);
1417 }
1418 
1419 static int
1420 nce_format(uintptr_t addr, const nce_t *nce, int ipversion)
1421 {
1422 	static const mdb_bitmask_t nce_flags[] = {
1423 		{ "P",	NCE_F_PERMANENT,	NCE_F_PERMANENT },
1424 		{ "R",	NCE_F_ISROUTER,		NCE_F_ISROUTER	},
1425 		{ "N",	NCE_F_NONUD,		NCE_F_NONUD	},
1426 		{ "A",	NCE_F_ANYCAST,		NCE_F_ANYCAST	},
1427 		{ "C",	NCE_F_CONDEMNED,	NCE_F_CONDEMNED	},
1428 		{ "U",	NCE_F_UNSOL_ADV,	NCE_F_UNSOL_ADV },
1429 		{ "B",	NCE_F_BCAST,		NCE_F_BCAST	},
1430 		{ NULL,	0,			0		}
1431 	};
1432 #define	NCE_MAX_FLAGS	(sizeof (nce_flags) / sizeof (mdb_bitmask_t))
1433 	struct in_addr nceaddr;
1434 	ill_t ill;
1435 	char ill_name[LIFNAMSIZ];
1436 	char flagsbuf[NCE_MAX_FLAGS];
1437 
1438 	if (mdb_vread(&ill, sizeof (ill), (uintptr_t)nce->nce_ill) == -1) {
1439 		mdb_warn("failed to read nce_ill at %p",
1440 		    nce->nce_ill);
1441 		return (DCMD_ERR);
1442 	}
1443 
1444 	(void) mdb_readstr(ill_name, MIN(LIFNAMSIZ, ill.ill_name_length),
1445 	    (uintptr_t)ill.ill_name);
1446 
1447 	mdb_snprintf(flagsbuf, sizeof (flagsbuf), "%hb",
1448 	    nce->nce_flags, nce_flags);
1449 
1450 	if (ipversion != 0 && nce->nce_ipversion != ipversion)
1451 		return (DCMD_OK);
1452 
1453 	if (nce->nce_ipversion == IPV4_VERSION) {
1454 		IN6_V4MAPPED_TO_INADDR(&nce->nce_addr, &nceaddr);
1455 		mdb_printf("%?p %-20s %-10s "
1456 		    "%-8s "
1457 		    "%-5s %I\n",
1458 		    addr, nce_l2_addr(nce, &ill),
1459 		    nce_state(nce->nce_state),
1460 		    flagsbuf,
1461 		    ill_name, nceaddr.s_addr);
1462 	} else {
1463 		mdb_printf("%?p %-20s %-10s %-8s %-5s %N\n",
1464 		    addr,  nce_l2_addr(nce, &ill),
1465 		    nce_state(nce->nce_state),
1466 		    flagsbuf,
1467 		    ill_name, &nce->nce_addr);
1468 	}
1469 
1470 	return (DCMD_OK);
1471 }
1472 
1473 static uintptr_t
1474 nce_get_next_hash_tbl(uintptr_t start, int *index, struct ndp_g_s ndp)
1475 {
1476 	uintptr_t addr = start;
1477 	int i = *index;
1478 
1479 	while (addr == NULL) {
1480 
1481 		if (++i >= NCE_TABLE_SIZE)
1482 			break;
1483 		addr = (uintptr_t)ndp.nce_hash_tbl[i];
1484 	}
1485 	*index = i;
1486 	return (addr);
1487 }
1488 
1489 static int
1490 nce_walk_step(mdb_walk_state_t *wsp)
1491 {
1492 	uintptr_t kaddr4, kaddr6;
1493 
1494 	kaddr4 = wsp->walk_addr + OFFSETOF(ip_stack_t, ips_ndp4);
1495 	kaddr6 = wsp->walk_addr + OFFSETOF(ip_stack_t, ips_ndp6);
1496 
1497 	if (mdb_vread(&kaddr4, sizeof (kaddr4), kaddr4) == -1) {
1498 		mdb_warn("can't read ips_ip_cache_table at %p", kaddr4);
1499 		return (WALK_ERR);
1500 	}
1501 	if (mdb_vread(&kaddr6, sizeof (kaddr6), kaddr6) == -1) {
1502 		mdb_warn("can't read ips_ip_cache_table at %p", kaddr6);
1503 		return (WALK_ERR);
1504 	}
1505 	if (mdb_pwalk("nce_stack", wsp->walk_callback, wsp->walk_cbdata,
1506 	    kaddr4) == -1) {
1507 		mdb_warn("couldn't walk 'nce_stack' for ips_ndp4 %p",
1508 		    kaddr4);
1509 		return (WALK_ERR);
1510 	}
1511 	if (mdb_pwalk("nce_stack", wsp->walk_callback,
1512 	    wsp->walk_cbdata, kaddr6) == -1) {
1513 		mdb_warn("couldn't walk 'nce_stack' for ips_ndp6 %p",
1514 		    kaddr6);
1515 		return (WALK_ERR);
1516 	}
1517 	return (WALK_NEXT);
1518 }
1519 
1520 /*
1521  * Called with walk_addr being the address of ips_ndp{4,6}
1522  */
1523 static int
1524 nce_stack_walk_init(mdb_walk_state_t *wsp)
1525 {
1526 	nce_walk_data_t *nw;
1527 
1528 	if (wsp->walk_addr == NULL) {
1529 		mdb_warn("nce_stack requires ndp_g_s address\n");
1530 		return (WALK_ERR);
1531 	}
1532 
1533 	nw = mdb_alloc(sizeof (nce_walk_data_t), UM_SLEEP);
1534 
1535 	if (mdb_vread(&nw->nce_ip_ndp, sizeof (struct ndp_g_s),
1536 	    wsp->walk_addr) == -1) {
1537 		mdb_warn("failed to read 'ip_ndp' at %p",
1538 		    wsp->walk_addr);
1539 		mdb_free(nw, sizeof (nce_walk_data_t));
1540 		return (WALK_ERR);
1541 	}
1542 
1543 	nw->nce_hash_tbl_index = 0;
1544 	wsp->walk_addr = nce_get_next_hash_tbl(NULL,
1545 	    &nw->nce_hash_tbl_index, nw->nce_ip_ndp);
1546 	wsp->walk_data = nw;
1547 
1548 	return (WALK_NEXT);
1549 }
1550 
1551 static int
1552 nce_stack_walk_step(mdb_walk_state_t *wsp)
1553 {
1554 	uintptr_t addr = wsp->walk_addr;
1555 	nce_walk_data_t *nw = wsp->walk_data;
1556 
1557 	if (addr == NULL)
1558 		return (WALK_DONE);
1559 
1560 	if (mdb_vread(&nw->nce, sizeof (nce_t), addr) == -1) {
1561 		mdb_warn("failed to read nce_t at %p", addr);
1562 		return (WALK_ERR);
1563 	}
1564 
1565 	wsp->walk_addr = (uintptr_t)nw->nce.nce_next;
1566 
1567 	wsp->walk_addr = nce_get_next_hash_tbl(wsp->walk_addr,
1568 	    &nw->nce_hash_tbl_index, nw->nce_ip_ndp);
1569 
1570 	return (wsp->walk_callback(addr, nw, wsp->walk_cbdata));
1571 }
1572 
1573 static void
1574 nce_stack_walk_fini(mdb_walk_state_t *wsp)
1575 {
1576 	mdb_free(wsp->walk_data, sizeof (nce_walk_data_t));
1577 }
1578 
1579 /* ARGSUSED */
1580 static int
1581 nce_cb(uintptr_t addr, const nce_walk_data_t *iw, nce_cbdata_t *id)
1582 {
1583 	nce_t nce;
1584 
1585 	if (mdb_vread(&nce, sizeof (nce_t), addr) == -1) {
1586 		mdb_warn("failed to read nce at %p", addr);
1587 		return (WALK_NEXT);
1588 	}
1589 	(void) nce_format(addr, &nce, id->nce_ipversion);
1590 	return (WALK_NEXT);
1591 }
1592