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