xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/net.c (revision 4eaa471005973e11a6110b69fe990530b3b95a38)
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 <mdb/mdb_modapi.h>
27 #include <mdb/mdb_ks.h>
28 #include <mdb/mdb_ctf.h>
29 #include <sys/types.h>
30 #include <sys/tihdr.h>
31 #include <inet/led.h>
32 #include <inet/common.h>
33 #include <netinet/in.h>
34 #include <netinet/ip6.h>
35 #include <netinet/icmp6.h>
36 #include <inet/ip.h>
37 #include <inet/ip6.h>
38 #include <inet/ipclassifier.h>
39 #include <inet/tcp.h>
40 #include <sys/stream.h>
41 #include <sys/vfs.h>
42 #include <sys/stropts.h>
43 #include <sys/tpicommon.h>
44 #include <sys/socket.h>
45 #include <sys/socketvar.h>
46 #include <sys/cred_impl.h>
47 #include <inet/udp_impl.h>
48 #include <inet/arp_impl.h>
49 #include <inet/rawip_impl.h>
50 #include <inet/mi.h>
51 #include <fs/sockfs/socktpi_impl.h>
52 #include <net/bridge_impl.h>
53 #include <io/trill_impl.h>
54 #include <sys/mac_impl.h>
55 
56 #define	ADDR_V6_WIDTH	23
57 #define	ADDR_V4_WIDTH	15
58 
59 #define	NETSTAT_ALL	0x01
60 #define	NETSTAT_VERBOSE	0x02
61 #define	NETSTAT_ROUTE	0x04
62 #define	NETSTAT_V4	0x08
63 #define	NETSTAT_V6	0x10
64 #define	NETSTAT_UNIX	0x20
65 
66 #define	NETSTAT_FIRST	0x80000000u
67 
68 typedef struct netstat_cb_data_s {
69 	uint_t	opts;
70 	conn_t	conn;
71 	int	af;
72 } netstat_cb_data_t;
73 
74 /* Walkers for various *_stack_t */
75 int
76 ar_stacks_walk_init(mdb_walk_state_t *wsp)
77 {
78 	if (mdb_layered_walk("netstack", wsp) == -1) {
79 		mdb_warn("can't walk 'netstack'");
80 		return (WALK_ERR);
81 	}
82 	return (WALK_NEXT);
83 }
84 
85 int
86 ar_stacks_walk_step(mdb_walk_state_t *wsp)
87 {
88 	uintptr_t kaddr;
89 	netstack_t nss;
90 
91 	if (mdb_vread(&nss, sizeof (nss), wsp->walk_addr) == -1) {
92 		mdb_warn("can't read netstack at %p", wsp->walk_addr);
93 		return (WALK_ERR);
94 	}
95 	kaddr = (uintptr_t)nss.netstack_modules[NS_ARP];
96 	return (wsp->walk_callback(kaddr, wsp->walk_layer, wsp->walk_cbdata));
97 }
98 
99 int
100 icmp_stacks_walk_init(mdb_walk_state_t *wsp)
101 {
102 	if (mdb_layered_walk("netstack", wsp) == -1) {
103 		mdb_warn("can't walk 'netstack'");
104 		return (WALK_ERR);
105 	}
106 	return (WALK_NEXT);
107 }
108 
109 int
110 icmp_stacks_walk_step(mdb_walk_state_t *wsp)
111 {
112 	uintptr_t kaddr;
113 	netstack_t nss;
114 
115 	if (mdb_vread(&nss, sizeof (nss), wsp->walk_addr) == -1) {
116 		mdb_warn("can't read netstack at %p", wsp->walk_addr);
117 		return (WALK_ERR);
118 	}
119 	kaddr = (uintptr_t)nss.netstack_modules[NS_ICMP];
120 	return (wsp->walk_callback(kaddr, wsp->walk_layer, wsp->walk_cbdata));
121 }
122 
123 int
124 tcp_stacks_walk_init(mdb_walk_state_t *wsp)
125 {
126 	if (mdb_layered_walk("netstack", wsp) == -1) {
127 		mdb_warn("can't walk 'netstack'");
128 		return (WALK_ERR);
129 	}
130 	return (WALK_NEXT);
131 }
132 
133 int
134 tcp_stacks_walk_step(mdb_walk_state_t *wsp)
135 {
136 	uintptr_t kaddr;
137 	netstack_t nss;
138 
139 	if (mdb_vread(&nss, sizeof (nss), wsp->walk_addr) == -1) {
140 		mdb_warn("can't read netstack at %p", wsp->walk_addr);
141 		return (WALK_ERR);
142 	}
143 	kaddr = (uintptr_t)nss.netstack_modules[NS_TCP];
144 	return (wsp->walk_callback(kaddr, wsp->walk_layer, wsp->walk_cbdata));
145 }
146 
147 int
148 udp_stacks_walk_init(mdb_walk_state_t *wsp)
149 {
150 	if (mdb_layered_walk("netstack", wsp) == -1) {
151 		mdb_warn("can't walk 'netstack'");
152 		return (WALK_ERR);
153 	}
154 	return (WALK_NEXT);
155 }
156 
157 int
158 udp_stacks_walk_step(mdb_walk_state_t *wsp)
159 {
160 	uintptr_t kaddr;
161 	netstack_t nss;
162 
163 	if (mdb_vread(&nss, sizeof (nss), wsp->walk_addr) == -1) {
164 		mdb_warn("can't read netstack at %p", wsp->walk_addr);
165 		return (WALK_ERR);
166 	}
167 	kaddr = (uintptr_t)nss.netstack_modules[NS_UDP];
168 	return (wsp->walk_callback(kaddr, wsp->walk_layer, wsp->walk_cbdata));
169 }
170 
171 /*
172  * Print an IPv4 address and port number in a compact and easy to read format
173  * The arguments are in network byte order
174  */
175 static void
176 net_ipv4addrport_pr(const in6_addr_t *nipv6addr, in_port_t nport)
177 {
178 	uint32_t naddr = V4_PART_OF_V6((*nipv6addr));
179 
180 	mdb_nhconvert(&nport, &nport, sizeof (nport));
181 	mdb_printf("%*I.%-5hu", ADDR_V4_WIDTH, naddr, nport);
182 }
183 
184 /*
185  * Print an IPv6 address and port number in a compact and easy to read format
186  * The arguments are in network byte order
187  */
188 static void
189 net_ipv6addrport_pr(const in6_addr_t *naddr, in_port_t nport)
190 {
191 	mdb_nhconvert(&nport, &nport, sizeof (nport));
192 	mdb_printf("%*N.%-5hu", ADDR_V6_WIDTH, naddr, nport);
193 }
194 
195 static int
196 net_tcp_active(const tcp_t *tcp)
197 {
198 	return (tcp->tcp_state >= TCPS_ESTABLISHED);
199 }
200 
201 static int
202 net_tcp_ipv4(const tcp_t *tcp)
203 {
204 	return ((tcp->tcp_ipversion == IPV4_VERSION) ||
205 	    (IN6_IS_ADDR_UNSPECIFIED(&tcp->tcp_ip_src_v6) &&
206 	    (tcp->tcp_state <= TCPS_LISTEN)));
207 }
208 
209 static int
210 net_tcp_ipv6(const tcp_t *tcp)
211 {
212 	return (tcp->tcp_ipversion == IPV6_VERSION);
213 }
214 
215 static int
216 net_udp_active(const udp_t *udp)
217 {
218 	return ((udp->udp_state == TS_IDLE) ||
219 	    (udp->udp_state == TS_DATA_XFER));
220 }
221 
222 static int
223 net_udp_ipv4(const udp_t *udp)
224 {
225 	return ((udp->udp_ipversion == IPV4_VERSION) ||
226 	    (IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src) &&
227 	    (udp->udp_state <= TS_IDLE)));
228 }
229 
230 static int
231 net_udp_ipv6(const udp_t *udp)
232 {
233 	return (udp->udp_ipversion == IPV6_VERSION);
234 }
235 
236 int
237 sonode_walk_init(mdb_walk_state_t *wsp)
238 {
239 	if (wsp->walk_addr == NULL) {
240 		GElf_Sym sym;
241 		struct socklist *slp;
242 
243 		if (mdb_lookup_by_obj("sockfs", "socklist", &sym) == -1) {
244 			mdb_warn("failed to lookup sockfs`socklist");
245 			return (WALK_ERR);
246 		}
247 
248 		slp = (struct socklist *)(uintptr_t)sym.st_value;
249 
250 		if (mdb_vread(&wsp->walk_addr, sizeof (wsp->walk_addr),
251 		    (uintptr_t)&slp->sl_list) == -1) {
252 			mdb_warn("failed to read address of initial sonode "
253 			    "at %p", &slp->sl_list);
254 			return (WALK_ERR);
255 		}
256 	}
257 
258 	wsp->walk_data = mdb_alloc(sizeof (struct sotpi_sonode), UM_SLEEP);
259 	return (WALK_NEXT);
260 }
261 
262 int
263 sonode_walk_step(mdb_walk_state_t *wsp)
264 {
265 	int status;
266 	struct sotpi_sonode *stp;
267 
268 	if (wsp->walk_addr == NULL)
269 		return (WALK_DONE);
270 
271 	if (mdb_vread(wsp->walk_data, sizeof (struct sotpi_sonode),
272 	    wsp->walk_addr) == -1) {
273 		mdb_warn("failed to read sonode at %p", wsp->walk_addr);
274 		return (WALK_ERR);
275 	}
276 
277 	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
278 	    wsp->walk_cbdata);
279 
280 	stp = wsp->walk_data;
281 
282 	wsp->walk_addr = (uintptr_t)stp->st_info.sti_next_so;
283 	return (status);
284 }
285 
286 void
287 sonode_walk_fini(mdb_walk_state_t *wsp)
288 {
289 	mdb_free(wsp->walk_data, sizeof (struct sotpi_sonode));
290 }
291 
292 struct mi_walk_data {
293 	uintptr_t mi_wd_miofirst;
294 	MI_O mi_wd_miodata;
295 };
296 
297 int
298 mi_walk_init(mdb_walk_state_t *wsp)
299 {
300 	struct mi_walk_data *wdp;
301 
302 	if (wsp->walk_addr == NULL) {
303 		mdb_warn("mi doesn't support global walks\n");
304 		return (WALK_ERR);
305 	}
306 
307 	wdp = mdb_alloc(sizeof (struct mi_walk_data), UM_SLEEP);
308 
309 	/* So that we do not immediately return WALK_DONE below */
310 	wdp->mi_wd_miofirst = NULL;
311 
312 	wsp->walk_data = wdp;
313 	return (WALK_NEXT);
314 }
315 
316 int
317 mi_walk_step(mdb_walk_state_t *wsp)
318 {
319 	struct mi_walk_data *wdp = wsp->walk_data;
320 	MI_OP miop = &wdp->mi_wd_miodata;
321 	int status;
322 
323 	/* Always false in the first iteration */
324 	if ((wsp->walk_addr == (uintptr_t)NULL) ||
325 	    (wsp->walk_addr == wdp->mi_wd_miofirst)) {
326 		return (WALK_DONE);
327 	}
328 
329 	if (mdb_vread(miop, sizeof (MI_O), wsp->walk_addr) == -1) {
330 		mdb_warn("failed to read MI object at %p", wsp->walk_addr);
331 		return (WALK_ERR);
332 	}
333 
334 	/* Only true in the first iteration */
335 	if (wdp->mi_wd_miofirst == NULL) {
336 		wdp->mi_wd_miofirst = wsp->walk_addr;
337 		status = WALK_NEXT;
338 	} else {
339 		status = wsp->walk_callback(wsp->walk_addr + sizeof (MI_O),
340 		    &miop[1], wsp->walk_cbdata);
341 	}
342 
343 	wsp->walk_addr = (uintptr_t)miop->mi_o_next;
344 	return (status);
345 }
346 
347 void
348 mi_walk_fini(mdb_walk_state_t *wsp)
349 {
350 	mdb_free(wsp->walk_data, sizeof (struct mi_walk_data));
351 }
352 
353 typedef struct mi_payload_walk_arg_s {
354 	const char *mi_pwa_walker;	/* Underlying walker */
355 	const off_t mi_pwa_head_off;	/* Offset for mi_o_head_t * in stack */
356 	const size_t mi_pwa_size;	/* size of mi payload */
357 	const uint_t mi_pwa_flags;	/* device and/or module */
358 } mi_payload_walk_arg_t;
359 
360 #define	MI_PAYLOAD_DEVICE	0x1
361 #define	MI_PAYLOAD_MODULE	0x2
362 
363 int
364 mi_payload_walk_init(mdb_walk_state_t *wsp)
365 {
366 	const mi_payload_walk_arg_t *arg = wsp->walk_arg;
367 
368 	if (mdb_layered_walk(arg->mi_pwa_walker, wsp) == -1) {
369 		mdb_warn("can't walk '%s'", arg->mi_pwa_walker);
370 		return (WALK_ERR);
371 	}
372 	return (WALK_NEXT);
373 }
374 
375 int
376 mi_payload_walk_step(mdb_walk_state_t *wsp)
377 {
378 	const mi_payload_walk_arg_t *arg = wsp->walk_arg;
379 	uintptr_t kaddr;
380 
381 	kaddr = wsp->walk_addr + arg->mi_pwa_head_off;
382 
383 	if (mdb_vread(&kaddr, sizeof (kaddr), kaddr) == -1) {
384 		mdb_warn("can't read address of mi head at %p for %s",
385 		    kaddr, arg->mi_pwa_walker);
386 		return (WALK_ERR);
387 	}
388 
389 	if (kaddr == 0) {
390 		/* Empty list */
391 		return (WALK_DONE);
392 	}
393 
394 	if (mdb_pwalk("genunix`mi", wsp->walk_callback,
395 	    wsp->walk_cbdata, kaddr) == -1) {
396 		mdb_warn("failed to walk genunix`mi");
397 		return (WALK_ERR);
398 	}
399 	return (WALK_NEXT);
400 }
401 
402 const mi_payload_walk_arg_t mi_ar_arg = {
403 	"ar_stacks", OFFSETOF(arp_stack_t, as_head), sizeof (ar_t),
404 	MI_PAYLOAD_DEVICE | MI_PAYLOAD_MODULE
405 };
406 
407 const mi_payload_walk_arg_t mi_icmp_arg = {
408 	"icmp_stacks", OFFSETOF(icmp_stack_t, is_head), sizeof (icmp_t),
409 	MI_PAYLOAD_DEVICE | MI_PAYLOAD_MODULE
410 };
411 
412 int
413 sonode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
414 {
415 	const char *optf = NULL;
416 	const char *optt = NULL;
417 	const char *optp = NULL;
418 	int family, type, proto;
419 	int filter = 0;
420 	struct sonode so;
421 
422 	if (!(flags & DCMD_ADDRSPEC)) {
423 		if (mdb_walk_dcmd("genunix`sonode", "genunix`sonode", argc,
424 		    argv) == -1) {
425 			mdb_warn("failed to walk sonode");
426 			return (DCMD_ERR);
427 		}
428 
429 		return (DCMD_OK);
430 	}
431 
432 	if (mdb_getopts(argc, argv,
433 	    'f', MDB_OPT_STR, &optf,
434 	    't', MDB_OPT_STR, &optt,
435 	    'p', MDB_OPT_STR, &optp,
436 	    NULL) != argc)
437 		return (DCMD_USAGE);
438 
439 	if (optf != NULL) {
440 		if (strcmp("inet", optf) == 0)
441 			family = AF_INET;
442 		else if (strcmp("inet6", optf) == 0)
443 			family = AF_INET6;
444 		else if (strcmp("unix", optf) == 0)
445 			family = AF_UNIX;
446 		else
447 			family = mdb_strtoull(optf);
448 		filter = 1;
449 	}
450 
451 	if (optt != NULL) {
452 		if (strcmp("stream", optt) == 0)
453 			type = SOCK_STREAM;
454 		else if (strcmp("dgram", optt) == 0)
455 			type = SOCK_DGRAM;
456 		else if (strcmp("raw", optt) == 0)
457 			type = SOCK_RAW;
458 		else
459 			type = mdb_strtoull(optt);
460 		filter = 1;
461 	}
462 
463 	if (optp != NULL) {
464 		proto = mdb_strtoull(optp);
465 		filter = 1;
466 	}
467 
468 	if (DCMD_HDRSPEC(flags) && !filter) {
469 		mdb_printf("%<u>%-?s Family Type Proto State Mode Flag "
470 		    "AccessVP%</u>\n", "Sonode:");
471 	}
472 
473 	if (mdb_vread(&so, sizeof (so), addr) == -1) {
474 		mdb_warn("failed to read sonode at %p", addr);
475 		return (DCMD_ERR);
476 	}
477 
478 	if ((optf != NULL) && (so.so_family != family))
479 		return (DCMD_OK);
480 
481 	if ((optt != NULL) && (so.so_type != type))
482 		return (DCMD_OK);
483 
484 	if ((optp != NULL) && (so.so_protocol != proto))
485 		return (DCMD_OK);
486 
487 	if (filter) {
488 		mdb_printf("%0?p\n", addr);
489 		return (DCMD_OK);
490 	}
491 
492 	mdb_printf("%0?p ", addr);
493 
494 	switch (so.so_family) {
495 	case AF_UNIX:
496 		mdb_printf("unix  ");
497 		break;
498 	case AF_INET:
499 		mdb_printf("inet  ");
500 		break;
501 	case AF_INET6:
502 		mdb_printf("inet6 ");
503 		break;
504 	default:
505 		mdb_printf("%6hi", so.so_family);
506 	}
507 
508 	switch (so.so_type) {
509 	case SOCK_STREAM:
510 		mdb_printf(" strm");
511 		break;
512 	case SOCK_DGRAM:
513 		mdb_printf(" dgrm");
514 		break;
515 	case SOCK_RAW:
516 		mdb_printf(" raw ");
517 		break;
518 	default:
519 		mdb_printf(" %4hi", so.so_type);
520 	}
521 
522 	mdb_printf(" %5hi %05x %04x %04hx\n",
523 	    so.so_protocol, so.so_state, so.so_mode,
524 	    so.so_flag);
525 
526 	return (DCMD_OK);
527 }
528 
529 #define	MI_PAYLOAD	0x1
530 #define	MI_DEVICE	0x2
531 #define	MI_MODULE	0x4
532 
533 int
534 mi(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
535 {
536 	uint_t opts = 0;
537 	MI_O	mio;
538 
539 	if (!(flags & DCMD_ADDRSPEC))
540 		return (DCMD_USAGE);
541 
542 	if (mdb_getopts(argc, argv,
543 	    'p', MDB_OPT_SETBITS, MI_PAYLOAD, &opts,
544 	    'd', MDB_OPT_SETBITS, MI_DEVICE, &opts,
545 	    'm', MDB_OPT_SETBITS, MI_MODULE, &opts,
546 	    NULL) != argc)
547 		return (DCMD_USAGE);
548 
549 	if ((opts & (MI_DEVICE | MI_MODULE)) == (MI_DEVICE | MI_MODULE)) {
550 		mdb_warn("at most one filter, d for devices or m "
551 		    "for modules, may be specified\n");
552 		return (DCMD_USAGE);
553 	}
554 
555 	if ((opts == 0) && (DCMD_HDRSPEC(flags))) {
556 		mdb_printf("%<u>%-?s %-?s %-?s IsDev Dev%</u>\n",
557 		    "MI_O", "Next", "Prev");
558 	}
559 
560 	if (mdb_vread(&mio, sizeof (mio), addr) == -1) {
561 		mdb_warn("failed to read mi object MI_O at %p", addr);
562 		return (DCMD_ERR);
563 	}
564 
565 	if (opts != 0) {
566 		if (mio.mi_o_isdev == B_FALSE) {
567 			/* mio is a module */
568 			if (!(opts & MI_MODULE) && (opts & MI_DEVICE))
569 				return (DCMD_OK);
570 		} else {
571 			/* mio is a device */
572 			if (!(opts & MI_DEVICE) && (opts & MI_MODULE))
573 				return (DCMD_OK);
574 		}
575 
576 		if (opts & MI_PAYLOAD)
577 			mdb_printf("%p\n", addr + sizeof (MI_O));
578 		else
579 			mdb_printf("%p\n", addr);
580 		return (DCMD_OK);
581 	}
582 
583 	mdb_printf("%0?p %0?p %0?p ", addr, mio.mi_o_next, mio.mi_o_prev);
584 
585 	if (mio.mi_o_isdev == B_FALSE)
586 		mdb_printf("FALSE");
587 	else
588 		mdb_printf("TRUE ");
589 
590 	mdb_printf(" %0?p\n", mio.mi_o_dev);
591 
592 	return (DCMD_OK);
593 }
594 
595 static int
596 ns_to_stackid(uintptr_t kaddr)
597 {
598 	netstack_t nss;
599 
600 	if (mdb_vread(&nss, sizeof (nss), kaddr) == -1) {
601 		mdb_warn("failed to read netstack_t %p", kaddr);
602 		return (0);
603 	}
604 	return (nss.netstack_stackid);
605 }
606 
607 
608 
609 static void
610 netstat_tcp_verbose_pr(const tcp_t *tcp)
611 {
612 	mdb_printf("       %5i %08x %08x %5i %08x %08x %5li %5i\n",
613 	    tcp->tcp_swnd, tcp->tcp_snxt, tcp->tcp_suna, tcp->tcp_rwnd,
614 	    tcp->tcp_rack, tcp->tcp_rnxt, tcp->tcp_rto, tcp->tcp_mss);
615 }
616 
617 /*ARGSUSED*/
618 static int
619 netstat_tcp_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
620 {
621 	netstat_cb_data_t *ncb = cb_data;
622 	uint_t opts = ncb->opts;
623 	int af = ncb->af;
624 	uintptr_t tcp_kaddr;
625 	conn_t *connp = &ncb->conn;
626 	tcp_t tcps, *tcp;
627 
628 	if (mdb_vread(connp, sizeof (conn_t), kaddr) == -1) {
629 		mdb_warn("failed to read conn_t at %p", kaddr);
630 		return (WALK_ERR);
631 	}
632 
633 	tcp_kaddr = (uintptr_t)connp->conn_tcp;
634 	if (mdb_vread(&tcps, sizeof (tcp_t), tcp_kaddr) == -1) {
635 		mdb_warn("failed to read tcp_t at %p", kaddr);
636 		return (WALK_ERR);
637 	}
638 
639 	tcp = &tcps;
640 	connp->conn_tcp = tcp;
641 	tcp->tcp_connp = connp;
642 
643 	if (!((opts & NETSTAT_ALL) || net_tcp_active(tcp)) ||
644 	    (af == AF_INET && !net_tcp_ipv4(tcp)) ||
645 	    (af == AF_INET6 && !net_tcp_ipv6(tcp))) {
646 		return (WALK_NEXT);
647 	}
648 
649 	mdb_printf("%0?p %2i ", tcp_kaddr, tcp->tcp_state);
650 	if (af == AF_INET) {
651 		net_ipv4addrport_pr(&tcp->tcp_ip_src_v6, tcp->tcp_lport);
652 		mdb_printf(" ");
653 		net_ipv4addrport_pr(&tcp->tcp_remote_v6, tcp->tcp_fport);
654 	} else if (af == AF_INET6) {
655 		net_ipv6addrport_pr(&tcp->tcp_ip_src_v6, tcp->tcp_lport);
656 		mdb_printf(" ");
657 		net_ipv6addrport_pr(&tcp->tcp_remote_v6, tcp->tcp_fport);
658 	}
659 	mdb_printf(" %5i", ns_to_stackid((uintptr_t)connp->conn_netstack));
660 	mdb_printf(" %4i\n", connp->conn_zoneid);
661 	if (opts & NETSTAT_VERBOSE)
662 		netstat_tcp_verbose_pr(tcp);
663 
664 	return (WALK_NEXT);
665 }
666 
667 /*ARGSUSED*/
668 static int
669 netstat_udp_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
670 {
671 	netstat_cb_data_t *ncb = cb_data;
672 	uint_t opts = ncb->opts;
673 	int af = ncb->af;
674 	udp_t udp;
675 	conn_t *connp = &ncb->conn;
676 	char *state;
677 
678 	if (mdb_vread(connp, sizeof (conn_t), kaddr) == -1) {
679 		mdb_warn("failed to read conn_t at %p", kaddr);
680 		return (WALK_ERR);
681 	}
682 
683 	if (mdb_vread(&udp, sizeof (udp_t),
684 	    (uintptr_t)connp->conn_udp) == -1) {
685 		mdb_warn("failed to read conn_udp at %p",
686 		    (uintptr_t)connp->conn_udp);
687 		return (WALK_ERR);
688 	}
689 
690 	if (!((opts & NETSTAT_ALL) || net_udp_active(&udp)) ||
691 	    (af == AF_INET && !net_udp_ipv4(&udp)) ||
692 	    (af == AF_INET6 && !net_udp_ipv6(&udp))) {
693 		return (WALK_NEXT);
694 	}
695 
696 	if (udp.udp_state == TS_UNBND)
697 		state = "UNBOUND";
698 	else if (udp.udp_state == TS_IDLE)
699 		state = "IDLE";
700 	else if (udp.udp_state == TS_DATA_XFER)
701 		state = "CONNECTED";
702 	else
703 		state = "UNKNOWN";
704 
705 	mdb_printf("%0?p %10s ", (uintptr_t)connp->conn_udp, state);
706 	if (af == AF_INET) {
707 		net_ipv4addrport_pr(&udp.udp_v6src, udp.udp_port);
708 		mdb_printf(" ");
709 		net_ipv4addrport_pr(&udp.udp_v6dst, udp.udp_dstport);
710 	} else if (af == AF_INET6) {
711 		net_ipv6addrport_pr(&udp.udp_v6src, udp.udp_port);
712 		mdb_printf(" ");
713 		net_ipv6addrport_pr(&udp.udp_v6dst, udp.udp_dstport);
714 	}
715 	mdb_printf(" %5i", ns_to_stackid((uintptr_t)connp->conn_netstack));
716 	mdb_printf(" %4i\n", connp->conn_zoneid);
717 
718 	return (WALK_NEXT);
719 }
720 
721 /*ARGSUSED*/
722 static int
723 netstat_icmp_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
724 {
725 	netstat_cb_data_t *ncb = cb_data;
726 	int af = ncb->af;
727 	icmp_t icmp;
728 	conn_t *connp = &ncb->conn;
729 	char *state;
730 
731 	if (mdb_vread(connp, sizeof (conn_t), kaddr) == -1) {
732 		mdb_warn("failed to read conn_t at %p", kaddr);
733 		return (WALK_ERR);
734 	}
735 
736 	if (mdb_vread(&icmp, sizeof (icmp_t),
737 	    (uintptr_t)connp->conn_icmp) == -1) {
738 		mdb_warn("failed to read conn_icmp at %p",
739 		    (uintptr_t)connp->conn_icmp);
740 		return (WALK_ERR);
741 	}
742 
743 	if ((af == AF_INET && icmp.icmp_ipversion != IPV4_VERSION) ||
744 	    (af == AF_INET6 && icmp.icmp_ipversion != IPV6_VERSION)) {
745 		return (WALK_NEXT);
746 	}
747 
748 	if (icmp.icmp_state == TS_UNBND)
749 		state = "UNBOUND";
750 	else if (icmp.icmp_state == TS_IDLE)
751 		state = "IDLE";
752 	else if (icmp.icmp_state == TS_DATA_XFER)
753 		state = "CONNECTED";
754 	else
755 		state = "UNKNOWN";
756 
757 	mdb_printf("%0?p %10s ", (uintptr_t)connp->conn_icmp, state);
758 	if (af == AF_INET) {
759 		mdb_printf("%*I ", ADDR_V4_WIDTH,
760 		    V4_PART_OF_V6((icmp.icmp_v6src)));
761 		mdb_printf("%*I ", ADDR_V4_WIDTH,
762 		    V4_PART_OF_V6((icmp.icmp_v6dst.sin6_addr)));
763 	} else if (af == AF_INET6) {
764 		mdb_printf("%*N ", ADDR_V6_WIDTH, &icmp.icmp_v6src);
765 		mdb_printf("%*N ", ADDR_V6_WIDTH, &icmp.icmp_v6dst);
766 	}
767 	mdb_printf(" %5i", ns_to_stackid((uintptr_t)connp->conn_netstack));
768 	mdb_printf(" %4i\n", icmp.icmp_zoneid);
769 
770 	return (WALK_NEXT);
771 }
772 
773 /*
774  * print the address of a unix domain socket
775  *
776  * so is the address of a AF_UNIX struct sonode in mdb's address space
777  * soa is the address of the struct soaddr to print
778  *
779  * returns 0 on success, -1 otherwise
780  */
781 static int
782 netstat_unix_name_pr(const struct sotpi_sonode *st, const struct soaddr *soa)
783 {
784 	const struct sonode *so = &st->st_sonode;
785 	const char none[] = " (none)";
786 
787 	if ((so->so_state & SS_ISBOUND) && (soa->soa_len != 0)) {
788 		if (st->st_info.sti_faddr_noxlate) {
789 			mdb_printf("%-14s ", " (socketpair)");
790 		} else {
791 			if (soa->soa_len > sizeof (sa_family_t)) {
792 				char addr[MAXPATHLEN + 1];
793 
794 				if (mdb_readstr(addr, sizeof (addr),
795 				    (uintptr_t)&soa->soa_sa->sa_data) == -1) {
796 					mdb_warn("failed to read unix address "
797 					    "at %p", &soa->soa_sa->sa_data);
798 					return (-1);
799 				}
800 
801 				mdb_printf("%-14s ", addr);
802 			} else {
803 				mdb_printf("%-14s ", none);
804 			}
805 		}
806 	} else {
807 		mdb_printf("%-14s ", none);
808 	}
809 
810 	return (0);
811 }
812 
813 /* based on sockfs_snapshot */
814 /*ARGSUSED*/
815 static int
816 netstat_unix_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
817 {
818 	const struct sotpi_sonode *st = walk_data;
819 	const struct sonode *so = &st->st_sonode;
820 	const struct sotpi_info *sti = &st->st_info;
821 
822 	if (so->so_count == 0)
823 		return (WALK_NEXT);
824 
825 	if (so->so_family != AF_UNIX) {
826 		mdb_warn("sonode of family %hi at %p\n", so->so_family, kaddr);
827 		return (WALK_ERR);
828 	}
829 
830 	mdb_printf("%-?p ", kaddr);
831 
832 	switch (sti->sti_serv_type) {
833 	case T_CLTS:
834 		mdb_printf("%-10s ", "dgram");
835 		break;
836 	case T_COTS:
837 		mdb_printf("%-10s ", "stream");
838 		break;
839 	case T_COTS_ORD:
840 		mdb_printf("%-10s ", "stream-ord");
841 		break;
842 	default:
843 		mdb_printf("%-10i ", sti->sti_serv_type);
844 	}
845 
846 	if ((so->so_state & SS_ISBOUND) &&
847 	    (sti->sti_ux_laddr.soua_magic == SOU_MAGIC_EXPLICIT)) {
848 		mdb_printf("%0?p ", sti->sti_ux_laddr.soua_vp);
849 	} else {
850 		mdb_printf("%0?p ", NULL);
851 	}
852 
853 	if ((so->so_state & SS_ISCONNECTED) &&
854 	    (sti->sti_ux_faddr.soua_magic == SOU_MAGIC_EXPLICIT)) {
855 		mdb_printf("%0?p ", sti->sti_ux_faddr.soua_vp);
856 	} else {
857 		mdb_printf("%0?p ", NULL);
858 	}
859 
860 	if (netstat_unix_name_pr(st, &sti->sti_laddr) == -1)
861 		return (WALK_ERR);
862 
863 	if (netstat_unix_name_pr(st, &sti->sti_faddr) == -1)
864 		return (WALK_ERR);
865 
866 	mdb_printf("%4i\n", so->so_zoneid);
867 
868 	return (WALK_NEXT);
869 }
870 
871 static void
872 netstat_tcp_verbose_header_pr(void)
873 {
874 	mdb_printf("       %<u>%-5s %-8s %-8s %-5s %-8s %-8s %5s %5s%</u>\n",
875 	    "Swind", "Snext", "Suna", "Rwind", "Rack", "Rnext", "Rto", "Mss");
876 }
877 
878 static void
879 get_ifname(const ire_t *ire, char *intf)
880 {
881 	ill_t ill;
882 
883 	*intf = '\0';
884 	if (ire->ire_type == IRE_CACHE) {
885 		queue_t stq;
886 
887 		if (mdb_vread(&stq, sizeof (stq), (uintptr_t)ire->ire_stq) ==
888 		    -1)
889 			return;
890 		if (mdb_vread(&ill, sizeof (ill), (uintptr_t)stq.q_ptr) == -1)
891 			return;
892 		(void) mdb_readstr(intf, MIN(LIFNAMSIZ, ill.ill_name_length),
893 		    (uintptr_t)ill.ill_name);
894 	} else if (ire->ire_ipif != NULL) {
895 		ipif_t ipif;
896 		char *cp;
897 
898 		if (mdb_vread(&ipif, sizeof (ipif),
899 		    (uintptr_t)ire->ire_ipif) == -1)
900 			return;
901 		if (mdb_vread(&ill, sizeof (ill), (uintptr_t)ipif.ipif_ill) ==
902 		    -1)
903 			return;
904 		(void) mdb_readstr(intf, MIN(LIFNAMSIZ, ill.ill_name_length),
905 		    (uintptr_t)ill.ill_name);
906 		if (ipif.ipif_id != 0) {
907 			cp = intf + strlen(intf);
908 			(void) mdb_snprintf(cp, LIFNAMSIZ + 1 - (cp - intf),
909 			    ":%u", ipif.ipif_id);
910 		}
911 	}
912 }
913 
914 static void
915 get_v4flags(const ire_t *ire, char *flags)
916 {
917 	(void) strcpy(flags, "U");
918 	if (ire->ire_type == IRE_DEFAULT || ire->ire_type == IRE_PREFIX ||
919 	    ire->ire_type == IRE_HOST || ire->ire_type == IRE_HOST_REDIRECT)
920 		(void) strcat(flags, "G");
921 	if (ire->ire_mask == IP_HOST_MASK)
922 		(void) strcat(flags, "H");
923 	if (ire->ire_type == IRE_HOST_REDIRECT)
924 		(void) strcat(flags, "D");
925 	if (ire->ire_type == IRE_CACHE)
926 		(void) strcat(flags, "A");
927 	if (ire->ire_type == IRE_BROADCAST)
928 		(void) strcat(flags, "B");
929 	if (ire->ire_type == IRE_LOCAL)
930 		(void) strcat(flags, "L");
931 	if (ire->ire_flags & RTF_MULTIRT)
932 		(void) strcat(flags, "M");
933 	if (ire->ire_flags & RTF_SETSRC)
934 		(void) strcat(flags, "S");
935 }
936 
937 static int
938 netstat_irev4_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
939 {
940 	const ire_t *ire = walk_data;
941 	uint_t *opts = cb_data;
942 	ipaddr_t gate;
943 	char flags[10], intf[LIFNAMSIZ + 1];
944 
945 	if (ire->ire_ipversion != IPV4_VERSION)
946 		return (WALK_NEXT);
947 
948 	if (!(*opts & NETSTAT_ALL) && (ire->ire_type == IRE_CACHE ||
949 	    ire->ire_type == IRE_BROADCAST || ire->ire_type == IRE_LOCAL))
950 		return (WALK_NEXT);
951 
952 	if (*opts & NETSTAT_FIRST) {
953 		*opts &= ~NETSTAT_FIRST;
954 		mdb_printf("%<u>%s Table: IPv4%</u>\n",
955 		    (*opts & NETSTAT_VERBOSE) ? "IRE" : "Routing");
956 		if (*opts & NETSTAT_VERBOSE) {
957 			mdb_printf("%<u>%-?s %-*s %-*s %-*s Device Mxfrg Rtt  "
958 			    " Ref Flg Out   In/Fwd%</u>\n",
959 			    "Address", ADDR_V4_WIDTH, "Destination",
960 			    ADDR_V4_WIDTH, "Mask", ADDR_V4_WIDTH, "Gateway");
961 		} else {
962 			mdb_printf("%<u>%-?s %-*s %-*s Flags Ref  Use   "
963 			    "Interface%</u>\n",
964 			    "Address", ADDR_V4_WIDTH, "Destination",
965 			    ADDR_V4_WIDTH, "Gateway");
966 		}
967 	}
968 
969 	gate = (ire->ire_type & (IRE_INTERFACE|IRE_LOOPBACK|IRE_BROADCAST)) ?
970 	    ire->ire_src_addr : ire->ire_gateway_addr;
971 
972 	get_v4flags(ire, flags);
973 
974 	get_ifname(ire, intf);
975 
976 	if (*opts & NETSTAT_VERBOSE) {
977 		mdb_printf("%?p %-*I %-*I %-*I %-6s %5u%c %4u %3u %-3s %5u "
978 		    "%u\n", kaddr, ADDR_V4_WIDTH, ire->ire_addr, ADDR_V4_WIDTH,
979 		    ire->ire_mask, ADDR_V4_WIDTH, gate, intf,
980 		    ire->ire_max_frag, ire->ire_frag_flag ? '*' : ' ',
981 		    ire->ire_uinfo.iulp_rtt, ire->ire_refcnt, flags,
982 		    ire->ire_ob_pkt_count, ire->ire_ib_pkt_count);
983 	} else {
984 		mdb_printf("%?p %-*I %-*I %-5s %4u %5u %s\n", kaddr,
985 		    ADDR_V4_WIDTH, ire->ire_addr, ADDR_V4_WIDTH, gate, flags,
986 		    ire->ire_refcnt,
987 		    ire->ire_ob_pkt_count + ire->ire_ib_pkt_count, intf);
988 	}
989 
990 	return (WALK_NEXT);
991 }
992 
993 int
994 ip_mask_to_plen_v6(const in6_addr_t *v6mask)
995 {
996 	int plen;
997 	int i;
998 	uint32_t val;
999 
1000 	for (i = 3; i >= 0; i--)
1001 		if (v6mask->s6_addr32[i] != 0)
1002 			break;
1003 	if (i < 0)
1004 		return (0);
1005 	plen = 32 + 32 * i;
1006 	val = v6mask->s6_addr32[i];
1007 	while (!(val & 1)) {
1008 		val >>= 1;
1009 		plen--;
1010 	}
1011 
1012 	return (plen);
1013 }
1014 
1015 static int
1016 netstat_irev6_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
1017 {
1018 	const ire_t *ire = walk_data;
1019 	uint_t *opts = cb_data;
1020 	const in6_addr_t *gatep;
1021 	char deststr[ADDR_V6_WIDTH + 5];
1022 	char flags[10], intf[LIFNAMSIZ + 1];
1023 	int masklen;
1024 
1025 	if (ire->ire_ipversion != IPV6_VERSION)
1026 		return (WALK_NEXT);
1027 
1028 	if (!(*opts & NETSTAT_ALL) && ire->ire_type == IRE_CACHE)
1029 		return (WALK_NEXT);
1030 
1031 	if (*opts & NETSTAT_FIRST) {
1032 		*opts &= ~NETSTAT_FIRST;
1033 		mdb_printf("\n%<u>%s Table: IPv6%</u>\n",
1034 		    (*opts & NETSTAT_VERBOSE) ? "IRE" : "Routing");
1035 		if (*opts & NETSTAT_VERBOSE) {
1036 			mdb_printf("%<u>%-?s %-*s %-*s If    PMTU   Rtt   Ref "
1037 			    "Flags Out    In/Fwd%</u>\n",
1038 			    "Address", ADDR_V6_WIDTH+4, "Destination/Mask",
1039 			    ADDR_V6_WIDTH, "Gateway");
1040 		} else {
1041 			mdb_printf("%<u>%-?s %-*s %-*s Flags Ref Use    If"
1042 			    "%</u>\n",
1043 			    "Address", ADDR_V6_WIDTH+4, "Destination/Mask",
1044 			    ADDR_V6_WIDTH, "Gateway");
1045 		}
1046 	}
1047 
1048 	gatep = (ire->ire_type & (IRE_INTERFACE|IRE_LOOPBACK)) ?
1049 	    &ire->ire_src_addr_v6 : &ire->ire_gateway_addr_v6;
1050 
1051 	masklen = ip_mask_to_plen_v6(&ire->ire_mask_v6);
1052 	(void) mdb_snprintf(deststr, sizeof (deststr), "%N/%d",
1053 	    &ire->ire_addr_v6, masklen);
1054 
1055 	(void) strcpy(flags, "U");
1056 	if (ire->ire_type == IRE_DEFAULT || ire->ire_type == IRE_PREFIX ||
1057 	    ire->ire_type == IRE_HOST || ire->ire_type == IRE_HOST_REDIRECT)
1058 		(void) strcat(flags, "G");
1059 	if (masklen == IPV6_ABITS)
1060 		(void) strcat(flags, "H");
1061 	if (ire->ire_type == IRE_HOST_REDIRECT)
1062 		(void) strcat(flags, "D");
1063 	if (ire->ire_type == IRE_CACHE)
1064 		(void) strcat(flags, "A");
1065 	if (ire->ire_type == IRE_LOCAL)
1066 		(void) strcat(flags, "L");
1067 	if (ire->ire_flags & RTF_MULTIRT)
1068 		(void) strcat(flags, "M");
1069 	if (ire->ire_flags & RTF_SETSRC)
1070 		(void) strcat(flags, "S");
1071 
1072 	get_ifname(ire, intf);
1073 
1074 	if (*opts & NETSTAT_VERBOSE) {
1075 		mdb_printf("%?p %-*s %-*N %-5s %5u%c %5u %3u %-5s %6u %u\n",
1076 		    kaddr, ADDR_V6_WIDTH+4, deststr, ADDR_V6_WIDTH, gatep,
1077 		    intf, ire->ire_max_frag, ire->ire_frag_flag ? '*' : ' ',
1078 		    ire->ire_uinfo.iulp_rtt, ire->ire_refcnt,
1079 		    flags, ire->ire_ob_pkt_count, ire->ire_ib_pkt_count);
1080 	} else {
1081 		mdb_printf("%?p %-*s %-*N %-5s %3u %6u %s\n", kaddr,
1082 		    ADDR_V6_WIDTH+4, deststr, ADDR_V6_WIDTH, gatep, flags,
1083 		    ire->ire_refcnt,
1084 		    ire->ire_ob_pkt_count + ire->ire_ib_pkt_count, intf);
1085 	}
1086 
1087 	return (WALK_NEXT);
1088 }
1089 
1090 static void
1091 netstat_header_v4(int proto)
1092 {
1093 	if (proto == IPPROTO_TCP)
1094 		mdb_printf("%<u>%-?s ", "TCPv4");
1095 	else if (proto == IPPROTO_UDP)
1096 		mdb_printf("%<u>%-?s ", "UDPv4");
1097 	else if (proto == IPPROTO_ICMP)
1098 		mdb_printf("%<u>%-?s ", "ICMPv4");
1099 	mdb_printf("State %6s%*s %6s%*s %-5s %-4s%</u>\n",
1100 	    "", ADDR_V4_WIDTH, "Local Address",
1101 	    "", ADDR_V4_WIDTH, "Remote Address", "Stack", "Zone");
1102 }
1103 
1104 static void
1105 netstat_header_v6(int proto)
1106 {
1107 	if (proto == IPPROTO_TCP)
1108 		mdb_printf("%<u>%-?s ", "TCPv6");
1109 	else if (proto == IPPROTO_UDP)
1110 		mdb_printf("%<u>%-?s ", "UDPv6");
1111 	else if (proto == IPPROTO_ICMP)
1112 		mdb_printf("%<u>%-?s ", "ICMPv6");
1113 	mdb_printf("State %6s%*s %6s%*s %-5s %-4s%</u>\n",
1114 	    "", ADDR_V6_WIDTH, "Local Address",
1115 	    "", ADDR_V6_WIDTH, "Remote Address", "Stack", "Zone");
1116 }
1117 
1118 static int
1119 netstat_print_conn(const char *cache, int proto, mdb_walk_cb_t cbfunc,
1120     void *cbdata)
1121 {
1122 	netstat_cb_data_t *ncb = cbdata;
1123 
1124 	if ((ncb->opts & NETSTAT_VERBOSE) && proto == IPPROTO_TCP)
1125 		netstat_tcp_verbose_header_pr();
1126 	if (mdb_walk(cache, cbfunc, cbdata) == -1) {
1127 		mdb_warn("failed to walk %s", cache);
1128 		return (DCMD_ERR);
1129 	}
1130 	return (DCMD_OK);
1131 }
1132 
1133 static int
1134 netstat_print_common(const char *cache, int proto, mdb_walk_cb_t cbfunc,
1135     void *cbdata)
1136 {
1137 	netstat_cb_data_t *ncb = cbdata;
1138 	int af = ncb->af;
1139 	int status = DCMD_OK;
1140 
1141 	if (af != AF_INET6) {
1142 		ncb->af = AF_INET;
1143 		netstat_header_v4(proto);
1144 		status = netstat_print_conn(cache, proto, cbfunc, cbdata);
1145 	}
1146 	if (status == DCMD_OK && af != AF_INET) {
1147 		ncb->af = AF_INET6;
1148 		netstat_header_v6(proto);
1149 		status = netstat_print_conn(cache, proto, cbfunc, cbdata);
1150 	}
1151 	ncb->af = af;
1152 	return (status);
1153 }
1154 
1155 /*ARGSUSED*/
1156 int
1157 netstat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1158 {
1159 	uint_t opts = 0;
1160 	const char *optf = NULL;
1161 	const char *optP = NULL;
1162 	netstat_cb_data_t *cbdata;
1163 	int status;
1164 	int af = 0;
1165 
1166 	if (mdb_getopts(argc, argv,
1167 	    'a', MDB_OPT_SETBITS, NETSTAT_ALL, &opts,
1168 	    'f', MDB_OPT_STR, &optf,
1169 	    'P', MDB_OPT_STR, &optP,
1170 	    'r', MDB_OPT_SETBITS, NETSTAT_ROUTE, &opts,
1171 	    'v', MDB_OPT_SETBITS, NETSTAT_VERBOSE, &opts,
1172 	    NULL) != argc)
1173 		return (DCMD_USAGE);
1174 
1175 	if (optP != NULL) {
1176 		if ((strcmp("tcp", optP) != 0) && (strcmp("udp", optP) != 0) &&
1177 		    (strcmp("icmp", optP) != 0))
1178 			return (DCMD_USAGE);
1179 		if (opts & NETSTAT_ROUTE)
1180 			return (DCMD_USAGE);
1181 	}
1182 
1183 	if (optf == NULL)
1184 		opts |= NETSTAT_V4 | NETSTAT_V6 | NETSTAT_UNIX;
1185 	else if (strcmp("inet", optf) == 0)
1186 		opts |= NETSTAT_V4;
1187 	else if (strcmp("inet6", optf) == 0)
1188 		opts |= NETSTAT_V6;
1189 	else if (strcmp("unix", optf) == 0)
1190 		opts |= NETSTAT_UNIX;
1191 	else
1192 		return (DCMD_USAGE);
1193 
1194 	if (opts & NETSTAT_ROUTE) {
1195 		if (!(opts & (NETSTAT_V4|NETSTAT_V6)))
1196 			return (DCMD_USAGE);
1197 		if (opts & NETSTAT_V4) {
1198 			opts |= NETSTAT_FIRST;
1199 			if (mdb_walk("ip`ire", netstat_irev4_cb, &opts) == -1) {
1200 				mdb_warn("failed to walk ip`ire");
1201 				return (DCMD_ERR);
1202 			}
1203 		}
1204 		if (opts & NETSTAT_V6) {
1205 			opts |= NETSTAT_FIRST;
1206 			if (mdb_walk("ip`ire", netstat_irev6_cb, &opts) == -1) {
1207 				mdb_warn("failed to walk ip`ire");
1208 				return (DCMD_ERR);
1209 			}
1210 		}
1211 		return (DCMD_OK);
1212 	}
1213 
1214 	if ((opts & NETSTAT_UNIX) && (optP == NULL)) {
1215 		/* Print Unix Domain Sockets */
1216 		mdb_printf("%<u>%-?s %-10s %-?s %-?s %-14s %-14s %s%</u>\n",
1217 		    "AF_UNIX", "Type", "Vnode", "Conn", "Local Addr",
1218 		    "Remote Addr", "Zone");
1219 
1220 		if (mdb_walk("genunix`sonode", netstat_unix_cb, NULL) == -1) {
1221 			mdb_warn("failed to walk genunix`sonode");
1222 			return (DCMD_ERR);
1223 		}
1224 		if (!(opts & (NETSTAT_V4 | NETSTAT_V6)))
1225 			return (DCMD_OK);
1226 	}
1227 
1228 	cbdata = mdb_alloc(sizeof (netstat_cb_data_t), UM_SLEEP);
1229 	cbdata->opts = opts;
1230 	if ((optf != NULL) && (opts & NETSTAT_V4))
1231 		af = AF_INET;
1232 	else if ((optf != NULL) && (opts & NETSTAT_V6))
1233 		af = AF_INET6;
1234 
1235 	cbdata->af = af;
1236 	if ((optP == NULL) || (strcmp("tcp", optP) == 0)) {
1237 		status = netstat_print_common("tcp_conn_cache", IPPROTO_TCP,
1238 		    netstat_tcp_cb, cbdata);
1239 		if (status != DCMD_OK)
1240 			goto out;
1241 	}
1242 
1243 	if ((optP == NULL) || (strcmp("udp", optP) == 0)) {
1244 		status = netstat_print_common("udp_conn_cache", IPPROTO_UDP,
1245 		    netstat_udp_cb, cbdata);
1246 		if (status != DCMD_OK)
1247 			goto out;
1248 	}
1249 
1250 	if ((optP == NULL) || (strcmp("icmp", optP) == 0)) {
1251 		status = netstat_print_common("rawip_conn_cache", IPPROTO_ICMP,
1252 		    netstat_icmp_cb, cbdata);
1253 		if (status != DCMD_OK)
1254 			goto out;
1255 	}
1256 out:
1257 	mdb_free(cbdata, sizeof (netstat_cb_data_t));
1258 	return (status);
1259 }
1260 
1261 /*
1262  * "::dladm show-bridge" support
1263  */
1264 typedef struct {
1265 	uint_t opt_l;
1266 	uint_t opt_f;
1267 	uint_t opt_t;
1268 	const char *name;
1269 	clock_t lbolt;
1270 	boolean_t found;
1271 	uint_t nlinks;
1272 	uint_t nfwd;
1273 
1274 	/*
1275 	 * These structures are kept inside the 'args' for allocation reasons.
1276 	 * They're all large data structures (over 1K), and may cause the stack
1277 	 * to explode.  mdb and kmdb will fail in these cases, and thus we
1278 	 * allocate them from the heap.
1279 	 */
1280 	trill_inst_t ti;
1281 	bridge_link_t bl;
1282 	mac_impl_t mi;
1283 } show_bridge_args_t;
1284 
1285 static void
1286 show_vlans(const uint8_t *vlans)
1287 {
1288 	int i, bit;
1289 	uint8_t val;
1290 	int rstart = -1, rnext = -1;
1291 
1292 	for (i = 0; i < BRIDGE_VLAN_ARR_SIZE; i++) {
1293 		val = vlans[i];
1294 		if (i == 0)
1295 			val &= ~1;
1296 		while ((bit = mdb_ffs(val)) != 0) {
1297 			bit--;
1298 			val &= ~(1 << bit);
1299 			bit += i * sizeof (*vlans) * NBBY;
1300 			if (bit != rnext) {
1301 				if (rnext != -1 && rstart + 1 != rnext)
1302 					mdb_printf("-%d", rnext - 1);
1303 				if (rstart != -1)
1304 					mdb_printf(",");
1305 				mdb_printf("%d", bit);
1306 				rstart = bit;
1307 			}
1308 			rnext = bit + 1;
1309 		}
1310 	}
1311 	if (rnext != -1 && rstart + 1 != rnext)
1312 		mdb_printf("-%d", rnext - 1);
1313 	mdb_printf("\n");
1314 }
1315 
1316 /*
1317  * This callback is invoked by a walk of the links attached to a bridge.  If
1318  * we're showing link details, then they're printed here.  If not, then we just
1319  * count up the links for the bridge summary.
1320  */
1321 static int
1322 do_bridge_links(uintptr_t addr, const void *data, void *ptr)
1323 {
1324 	show_bridge_args_t *args = ptr;
1325 	const bridge_link_t *blp = data;
1326 	char macaddr[ETHERADDRL * 3];
1327 	const char *name;
1328 
1329 	args->nlinks++;
1330 
1331 	if (!args->opt_l)
1332 		return (WALK_NEXT);
1333 
1334 	if (mdb_vread(&args->mi, sizeof (args->mi),
1335 	    (uintptr_t)blp->bl_mh) == -1) {
1336 		mdb_warn("cannot read mac data at %p", blp->bl_mh);
1337 		name = "?";
1338 	} else  {
1339 		name = args->mi.mi_name;
1340 	}
1341 
1342 	mdb_mac_addr(blp->bl_local_mac, ETHERADDRL, macaddr,
1343 	    sizeof (macaddr));
1344 
1345 	mdb_printf("%-?p %-16s %-17s %03X %-4d ", addr, name, macaddr,
1346 	    blp->bl_flags, blp->bl_pvid);
1347 
1348 	if (blp->bl_trilldata == NULL) {
1349 		switch (blp->bl_state) {
1350 		case BLS_BLOCKLISTEN:
1351 			name = "BLOCK";
1352 			break;
1353 		case BLS_LEARNING:
1354 			name = "LEARN";
1355 			break;
1356 		case BLS_FORWARDING:
1357 			name = "FWD";
1358 			break;
1359 		default:
1360 			name = "?";
1361 		}
1362 		mdb_printf("%-5s ", name);
1363 		show_vlans(blp->bl_vlans);
1364 	} else {
1365 		show_vlans(blp->bl_afs);
1366 	}
1367 
1368 	return (WALK_NEXT);
1369 }
1370 
1371 /*
1372  * It seems a shame to duplicate this code, but merging it with the link
1373  * printing code above is more trouble than it would be worth.
1374  */
1375 static void
1376 print_link_name(show_bridge_args_t *args, uintptr_t addr, char sep)
1377 {
1378 	const char *name;
1379 
1380 	if (mdb_vread(&args->bl, sizeof (args->bl), addr) == -1) {
1381 		mdb_warn("cannot read bridge link at %p", addr);
1382 		return;
1383 	}
1384 
1385 	if (mdb_vread(&args->mi, sizeof (args->mi),
1386 	    (uintptr_t)args->bl.bl_mh) == -1) {
1387 		name = "?";
1388 	} else  {
1389 		name = args->mi.mi_name;
1390 	}
1391 
1392 	mdb_printf("%s%c", name, sep);
1393 }
1394 
1395 static int
1396 do_bridge_fwd(uintptr_t addr, const void *data, void *ptr)
1397 {
1398 	show_bridge_args_t *args = ptr;
1399 	const bridge_fwd_t *bfp = data;
1400 	char macaddr[ETHERADDRL * 3];
1401 	int i;
1402 #define	MAX_FWD_LINKS	16
1403 	bridge_link_t *links[MAX_FWD_LINKS];
1404 	uint_t nlinks;
1405 
1406 	args->nfwd++;
1407 
1408 	if (!args->opt_f)
1409 		return (WALK_NEXT);
1410 
1411 	if ((nlinks = bfp->bf_nlinks) > MAX_FWD_LINKS)
1412 		nlinks = MAX_FWD_LINKS;
1413 
1414 	if (mdb_vread(links, sizeof (links[0]) * nlinks,
1415 	    (uintptr_t)bfp->bf_links) == -1) {
1416 		mdb_warn("cannot read bridge forwarding links at %p",
1417 		    bfp->bf_links);
1418 		return (WALK_ERR);
1419 	}
1420 
1421 	mdb_mac_addr(bfp->bf_dest, ETHERADDRL, macaddr, sizeof (macaddr));
1422 
1423 	mdb_printf("%-?p %-17s ", addr, macaddr);
1424 	if (bfp->bf_flags & BFF_LOCALADDR)
1425 		mdb_printf("%-7s", "[self]");
1426 	else
1427 		mdb_printf("t-%-5d", args->lbolt - bfp->bf_lastheard);
1428 	mdb_printf(" %-7u ", bfp->bf_refs);
1429 
1430 	if (bfp->bf_trill_nick != 0) {
1431 		mdb_printf("%d\n", bfp->bf_trill_nick);
1432 	} else {
1433 		for (i = 0; i < bfp->bf_nlinks; i++) {
1434 			print_link_name(args, (uintptr_t)links[i],
1435 			    i == bfp->bf_nlinks - 1 ? '\n' : ' ');
1436 		}
1437 	}
1438 
1439 	return (WALK_NEXT);
1440 }
1441 
1442 static int
1443 do_show_bridge(uintptr_t addr, const void *data, void *ptr)
1444 {
1445 	show_bridge_args_t *args = ptr;
1446 	bridge_inst_t bi;
1447 	const bridge_inst_t *bip;
1448 	trill_node_t tn;
1449 	trill_sock_t tsp;
1450 	trill_nickinfo_t tni;
1451 	char bname[MAXLINKNAMELEN];
1452 	char macaddr[ETHERADDRL * 3];
1453 	char *cp;
1454 	uint_t nnicks;
1455 	int i;
1456 
1457 	if (data != NULL) {
1458 		bip = data;
1459 	} else {
1460 		if (mdb_vread(&bi, sizeof (bi), addr) == -1) {
1461 			mdb_warn("cannot read bridge instance at %p", addr);
1462 			return (WALK_ERR);
1463 		}
1464 		bip = &bi;
1465 	}
1466 
1467 	(void) strncpy(bname, bip->bi_name, sizeof (bname) - 1);
1468 	bname[MAXLINKNAMELEN - 1] = '\0';
1469 	cp = bname + strlen(bname);
1470 	if (cp > bname && cp[-1] == '0')
1471 		cp[-1] = '\0';
1472 
1473 	if (args->name != NULL && strcmp(args->name, bname) != 0)
1474 		return (WALK_NEXT);
1475 
1476 	args->found = B_TRUE;
1477 	args->nlinks = args->nfwd = 0;
1478 
1479 	if (args->opt_l) {
1480 		mdb_printf("%-?s %-16s %-17s %3s %-4s ", "ADDR", "LINK",
1481 		    "MAC-ADDR", "FLG", "PVID");
1482 		if (bip->bi_trilldata == NULL)
1483 			mdb_printf("%-5s %s\n", "STATE", "VLANS");
1484 		else
1485 			mdb_printf("%s\n", "FWD-VLANS");
1486 	}
1487 
1488 	if (!args->opt_f && !args->opt_t &&
1489 	    mdb_pwalk("list", do_bridge_links, args,
1490 	    addr + offsetof(bridge_inst_t, bi_links)) != DCMD_OK)
1491 		return (WALK_ERR);
1492 
1493 	if (args->opt_f)
1494 		mdb_printf("%-?s %-17s %-7s %-7s %s\n", "ADDR", "DEST", "TIME",
1495 		    "REFS", "OUTPUT");
1496 
1497 	if (!args->opt_l && !args->opt_t &&
1498 	    mdb_pwalk("avl", do_bridge_fwd, args,
1499 	    addr + offsetof(bridge_inst_t, bi_fwd)) != DCMD_OK)
1500 		return (WALK_ERR);
1501 
1502 	nnicks = 0;
1503 	if (bip->bi_trilldata != NULL && !args->opt_l && !args->opt_f) {
1504 		if (mdb_vread(&args->ti, sizeof (args->ti),
1505 		    (uintptr_t)bip->bi_trilldata) == -1) {
1506 			mdb_warn("cannot read trill instance at %p",
1507 			    bip->bi_trilldata);
1508 			return (WALK_ERR);
1509 		}
1510 		if (args->opt_t)
1511 			mdb_printf("%-?s %-5s %-17s %s\n", "ADDR",
1512 			    "NICK", "NEXT-HOP", "LINK");
1513 		for (i = 0; i < RBRIDGE_NICKNAME_MAX; i++) {
1514 			if (args->ti.ti_nodes[i] == NULL)
1515 				continue;
1516 			if (args->opt_t) {
1517 				if (mdb_vread(&tn, sizeof (tn),
1518 				    (uintptr_t)args->ti.ti_nodes[i]) == -1) {
1519 					mdb_warn("cannot read trill node %d at "
1520 					    "%p", i, args->ti.ti_nodes[i]);
1521 					return (WALK_ERR);
1522 				}
1523 				if (mdb_vread(&tni, sizeof (tni),
1524 				    (uintptr_t)tn.tn_ni) == -1) {
1525 					mdb_warn("cannot read trill node info "
1526 					    "%d at %p", i, tn.tn_ni);
1527 					return (WALK_ERR);
1528 				}
1529 				mdb_mac_addr(tni.tni_adjsnpa, ETHERADDRL,
1530 				    macaddr, sizeof (macaddr));
1531 				if (tni.tni_nick == args->ti.ti_nick) {
1532 					(void) strcpy(macaddr, "[self]");
1533 				}
1534 				mdb_printf("%-?p %-5u %-17s ",
1535 				    args->ti.ti_nodes[i], tni.tni_nick,
1536 				    macaddr);
1537 				if (tn.tn_tsp != NULL) {
1538 					if (mdb_vread(&tsp, sizeof (tsp),
1539 					    (uintptr_t)tn.tn_tsp) == -1) {
1540 						mdb_warn("cannot read trill "
1541 						    "socket info at %p",
1542 						    tn.tn_tsp);
1543 						return (WALK_ERR);
1544 					}
1545 					if (tsp.ts_link != NULL) {
1546 						print_link_name(args,
1547 						    (uintptr_t)tsp.ts_link,
1548 						    '\n');
1549 						continue;
1550 					}
1551 				}
1552 				mdb_printf("--\n");
1553 			} else {
1554 				nnicks++;
1555 			}
1556 		}
1557 	} else {
1558 		if (args->opt_t)
1559 			mdb_printf("bridge is not running TRILL\n");
1560 	}
1561 
1562 	if (!args->opt_l && !args->opt_f && !args->opt_t) {
1563 		mdb_printf("%-?p %-7s %-16s %-7u %-7u", addr,
1564 		    bip->bi_trilldata == NULL ? "stp" : "trill", bname,
1565 		    args->nlinks, args->nfwd);
1566 		if (bip->bi_trilldata != NULL)
1567 			mdb_printf(" %-7u %u\n", nnicks, args->ti.ti_nick);
1568 		else
1569 			mdb_printf(" %-7s %s\n", "--", "--");
1570 	}
1571 	return (WALK_NEXT);
1572 }
1573 
1574 static int
1575 dladm_show_bridge(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1576 {
1577 	show_bridge_args_t *args;
1578 	GElf_Sym sym;
1579 	int i;
1580 
1581 	args = mdb_zalloc(sizeof (*args), UM_SLEEP);
1582 
1583 	i = mdb_getopts(argc, argv,
1584 	    'l', MDB_OPT_SETBITS, 1, &args->opt_l,
1585 	    'f', MDB_OPT_SETBITS, 1, &args->opt_f,
1586 	    't', MDB_OPT_SETBITS, 1, &args->opt_t,
1587 	    NULL);
1588 
1589 	argc -= i;
1590 	argv += i;
1591 
1592 	if (argc > 1 || (argc == 1 && argv[0].a_type != MDB_TYPE_STRING)) {
1593 		mdb_free(args, sizeof (*args));
1594 		return (DCMD_USAGE);
1595 	}
1596 	if (argc == 1)
1597 		args->name = argv[0].a_un.a_str;
1598 
1599 	if (mdb_readvar(&args->lbolt,
1600 	    mdb_prop_postmortem ? "panic_lbolt" : "lbolt") == -1) {
1601 		mdb_warn("failed to read lbolt");
1602 		goto err;
1603 	}
1604 
1605 	if (flags & DCMD_ADDRSPEC) {
1606 		if (args->name != NULL) {
1607 			mdb_printf("bridge name and address are mutually "
1608 			    "exclusive\n");
1609 			goto err;
1610 		}
1611 		if (!args->opt_l && !args->opt_f && !args->opt_t)
1612 			mdb_printf("%-?s %-7s %-16s %-7s %-7s\n", "ADDR",
1613 			    "PROTECT", "NAME", "NLINKS", "NFWD");
1614 		if (do_show_bridge(addr, NULL, args) != WALK_NEXT)
1615 			goto err;
1616 		mdb_free(args, sizeof (*args));
1617 		return (DCMD_OK);
1618 	} else {
1619 		if ((args->opt_l || args->opt_f || args->opt_t) &&
1620 		    args->name == NULL) {
1621 			mdb_printf("need bridge name or address with -[lft]\n");
1622 			goto err;
1623 		}
1624 		if (mdb_lookup_by_obj("bridge", "inst_list", &sym) == -1) {
1625 			mdb_warn("failed to find 'bridge`inst_list'");
1626 			goto err;
1627 		}
1628 		if (!args->opt_l && !args->opt_f && !args->opt_t)
1629 			mdb_printf("%-?s %-7s %-16s %-7s %-7s %-7s %s\n",
1630 			    "ADDR", "PROTECT", "NAME", "NLINKS", "NFWD",
1631 			    "NNICKS", "NICK");
1632 		if (mdb_pwalk("list", do_show_bridge, args,
1633 		    (uintptr_t)sym.st_value) != DCMD_OK)
1634 			goto err;
1635 		if (!args->found && args->name != NULL) {
1636 			mdb_printf("bridge instance %s not found\n",
1637 			    args->name);
1638 			goto err;
1639 		}
1640 		mdb_free(args, sizeof (*args));
1641 		return (DCMD_OK);
1642 	}
1643 
1644 err:
1645 	mdb_free(args, sizeof (*args));
1646 	return (DCMD_ERR);
1647 }
1648 
1649 /*
1650  * Support for the "::dladm" dcmd
1651  */
1652 int
1653 dladm(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1654 {
1655 	if (argc < 1 || argv[0].a_type != MDB_TYPE_STRING)
1656 		return (DCMD_USAGE);
1657 
1658 	/*
1659 	 * This could be a bit more elaborate, once we support more of the
1660 	 * dladm show-* subcommands.
1661 	 */
1662 	argc--;
1663 	argv++;
1664 	if (strcmp(argv[-1].a_un.a_str, "show-bridge") == 0)
1665 		return (dladm_show_bridge(addr, flags, argc, argv));
1666 
1667 	return (DCMD_USAGE);
1668 }
1669 
1670 void
1671 dladm_help(void)
1672 {
1673 	mdb_printf("Subcommands:\n"
1674 	    "  show-bridge [-flt] [<name>]\n"
1675 	    "\t     Show bridge information; -l for links and -f for "
1676 	    "forwarding\n"
1677 	    "\t     entries, and -t for TRILL nicknames.  Address is required "
1678 	    "if name\n"
1679 	    "\t     is not specified.\n");
1680 }
1681