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