Home | History | Annotate | Download | only in genunix
      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 == NULL) {
    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 == NULL)
    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 == NULL) {
    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 = NULL;
    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 == NULL) {
    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 
    647 	if (mdb_vread(connp, sizeof (conn_t), kaddr) == -1) {
    648 		mdb_warn("failed to read conn_t at %p", kaddr);
    649 		return (WALK_ERR);
    650 	}
    651 
    652 	if (mdb_vread(&udp, sizeof (udp_t),
    653 	    (uintptr_t)connp->conn_udp) == -1) {
    654 		mdb_warn("failed to read conn_udp at %p",
    655 		    (uintptr_t)connp->conn_udp);
    656 		return (WALK_ERR);
    657 	}
    658 
    659 	connp->conn_udp = &udp;
    660 	udp.udp_connp = connp;
    661 
    662 	if (!((opts & NETSTAT_ALL) || net_udp_active(&udp)) ||
    663 	    (af == AF_INET && !net_udp_ipv4(&udp)) ||
    664 	    (af == AF_INET6 && !net_udp_ipv6(&udp))) {
    665 		return (WALK_NEXT);
    666 	}
    667 
    668 	if (udp.udp_state == TS_UNBND)
    669 		state = "UNBOUND";
    670 	else if (udp.udp_state == TS_IDLE)
    671 		state = "IDLE";
    672 	else if (udp.udp_state == TS_DATA_XFER)
    673 		state = "CONNECTED";
    674 	else
    675 		state = "UNKNOWN";
    676 
    677 	mdb_printf("%0?p %10s ", (uintptr_t)connp->conn_udp, state);
    678 	if (af == AF_INET) {
    679 		net_ipv4addrport_pr(&connp->conn_laddr_v6, connp->conn_lport);
    680 		mdb_printf(" ");
    681 		net_ipv4addrport_pr(&connp->conn_faddr_v6, connp->conn_fport);
    682 	} else if (af == AF_INET6) {
    683 		net_ipv6addrport_pr(&connp->conn_laddr_v6, connp->conn_lport);
    684 		mdb_printf(" ");
    685 		net_ipv6addrport_pr(&connp->conn_faddr_v6, connp->conn_fport);
    686 	}
    687 	mdb_printf(" %5i", ns_to_stackid((uintptr_t)connp->conn_netstack));
    688 	mdb_printf(" %4i\n", connp->conn_zoneid);
    689 
    690 	return (WALK_NEXT);
    691 }
    692 
    693 /*ARGSUSED*/
    694 static int
    695 netstat_icmp_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
    696 {
    697 	netstat_cb_data_t *ncb = cb_data;
    698 	int af = ncb->af;
    699 	icmp_t icmp;
    700 	conn_t *connp = &ncb->conn;
    701 	char *state;
    702 
    703 	if (mdb_vread(connp, sizeof (conn_t), kaddr) == -1) {
    704 		mdb_warn("failed to read conn_t at %p", kaddr);
    705 		return (WALK_ERR);
    706 	}
    707 
    708 	if (mdb_vread(&icmp, sizeof (icmp_t),
    709 	    (uintptr_t)connp->conn_icmp) == -1) {
    710 		mdb_warn("failed to read conn_icmp at %p",
    711 		    (uintptr_t)connp->conn_icmp);
    712 		return (WALK_ERR);
    713 	}
    714 
    715 	connp->conn_icmp = &icmp;
    716 	icmp.icmp_connp = connp;
    717 
    718 	if ((af == AF_INET && connp->conn_ipversion != IPV4_VERSION) ||
    719 	    (af == AF_INET6 && connp->conn_ipversion != IPV6_VERSION)) {
    720 		return (WALK_NEXT);
    721 	}
    722 
    723 	if (icmp.icmp_state == TS_UNBND)
    724 		state = "UNBOUND";
    725 	else if (icmp.icmp_state == TS_IDLE)
    726 		state = "IDLE";
    727 	else if (icmp.icmp_state == TS_DATA_XFER)
    728 		state = "CONNECTED";
    729 	else
    730 		state = "UNKNOWN";
    731 
    732 	mdb_printf("%0?p %10s ", (uintptr_t)connp->conn_icmp, state);
    733 	if (af == AF_INET) {
    734 		net_ipv4addrport_pr(&connp->conn_laddr_v6, connp->conn_lport);
    735 		mdb_printf(" ");
    736 		net_ipv4addrport_pr(&connp->conn_faddr_v6, connp->conn_fport);
    737 	} else if (af == AF_INET6) {
    738 		net_ipv6addrport_pr(&connp->conn_laddr_v6, connp->conn_lport);
    739 		mdb_printf(" ");
    740 		net_ipv6addrport_pr(&connp->conn_faddr_v6, connp->conn_fport);
    741 	}
    742 	mdb_printf(" %5i", ns_to_stackid((uintptr_t)connp->conn_netstack));
    743 	mdb_printf(" %4i\n", connp->conn_zoneid);
    744 
    745 	return (WALK_NEXT);
    746 }
    747 
    748 /*
    749  * print the address of a unix domain socket
    750  *
    751  * so is the address of a AF_UNIX struct sonode in mdb's address space
    752  * soa is the address of the struct soaddr to print
    753  *
    754  * returns 0 on success, -1 otherwise
    755  */
    756 static int
    757 netstat_unix_name_pr(const struct sotpi_sonode *st, const struct soaddr *soa)
    758 {
    759 	const struct sonode *so = &st->st_sonode;
    760 	const char none[] = " (none)";
    761 
    762 	if ((so->so_state & SS_ISBOUND) && (soa->soa_len != 0)) {
    763 		if (st->st_info.sti_faddr_noxlate) {
    764 			mdb_printf("%-14s ", " (socketpair)");
    765 		} else {
    766 			if (soa->soa_len > sizeof (sa_family_t)) {
    767 				char addr[MAXPATHLEN + 1];
    768 
    769 				if (mdb_readstr(addr, sizeof (addr),
    770 				    (uintptr_t)&soa->soa_sa->sa_data) == -1) {
    771 					mdb_warn("failed to read unix address "
    772 					    "at %p", &soa->soa_sa->sa_data);
    773 					return (-1);
    774 				}
    775 
    776 				mdb_printf("%-14s ", addr);
    777 			} else {
    778 				mdb_printf("%-14s ", none);
    779 			}
    780 		}
    781 	} else {
    782 		mdb_printf("%-14s ", none);
    783 	}
    784 
    785 	return (0);
    786 }
    787 
    788 /* based on sockfs_snapshot */
    789 /*ARGSUSED*/
    790 static int
    791 netstat_unix_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
    792 {
    793 	const struct sotpi_sonode *st = walk_data;
    794 	const struct sonode *so = &st->st_sonode;
    795 	const struct sotpi_info *sti = &st->st_info;
    796 
    797 	if (so->so_count == 0)
    798 		return (WALK_NEXT);
    799 
    800 	if (so->so_family != AF_UNIX) {
    801 		mdb_warn("sonode of family %hi at %p\n", so->so_family, kaddr);
    802 		return (WALK_ERR);
    803 	}
    804 
    805 	mdb_printf("%-?p ", kaddr);
    806 
    807 	switch (sti->sti_serv_type) {
    808 	case T_CLTS:
    809 		mdb_printf("%-10s ", "dgram");
    810 		break;
    811 	case T_COTS:
    812 		mdb_printf("%-10s ", "stream");
    813 		break;
    814 	case T_COTS_ORD:
    815 		mdb_printf("%-10s ", "stream-ord");
    816 		break;
    817 	default:
    818 		mdb_printf("%-10i ", sti->sti_serv_type);
    819 	}
    820 
    821 	if ((so->so_state & SS_ISBOUND) &&
    822 	    (sti->sti_ux_laddr.soua_magic == SOU_MAGIC_EXPLICIT)) {
    823 		mdb_printf("%0?p ", sti->sti_ux_laddr.soua_vp);
    824 	} else {
    825 		mdb_printf("%0?p ", NULL);
    826 	}
    827 
    828 	if ((so->so_state & SS_ISCONNECTED) &&
    829 	    (sti->sti_ux_faddr.soua_magic == SOU_MAGIC_EXPLICIT)) {
    830 		mdb_printf("%0?p ", sti->sti_ux_faddr.soua_vp);
    831 	} else {
    832 		mdb_printf("%0?p ", NULL);
    833 	}
    834 
    835 	if (netstat_unix_name_pr(st, &sti->sti_laddr) == -1)
    836 		return (WALK_ERR);
    837 
    838 	if (netstat_unix_name_pr(st, &sti->sti_faddr) == -1)
    839 		return (WALK_ERR);
    840 
    841 	mdb_printf("%4i\n", so->so_zoneid);
    842 
    843 	return (WALK_NEXT);
    844 }
    845 
    846 static void
    847 netstat_tcp_verbose_header_pr(void)
    848 {
    849 	mdb_printf("       %<u>%-5s %-8s %-8s %-5s %-8s %-8s %5s %5s%</u>\n",
    850 	    "Swind", "Snext", "Suna", "Rwind", "Rack", "Rnext", "Rto", "Mss");
    851 }
    852 
    853 static void
    854 get_ifname(const ire_t *ire, char *intf)
    855 {
    856 	ill_t ill;
    857 
    858 	*intf = '\0';
    859 	if (ire->ire_ill != NULL) {
    860 		if (mdb_vread(&ill, sizeof (ill),
    861 		    (uintptr_t)ire->ire_ill) == -1)
    862 			return;
    863 		(void) mdb_readstr(intf, MIN(LIFNAMSIZ, ill.ill_name_length),
    864 		    (uintptr_t)ill.ill_name);
    865 	}
    866 }
    867 
    868 const in6_addr_t ipv6_all_ones =
    869 	{ 0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU };
    870 
    871 static void
    872 get_ireflags(const ire_t *ire, char *flags)
    873 {
    874 	(void) strcpy(flags, "U");
    875 	/* RTF_INDIRECT wins over RTF_GATEWAY - don't display both */
    876 	if (ire->ire_flags & RTF_INDIRECT)
    877 		(void) strcat(flags, "I");
    878 	else if (ire->ire_type & IRE_OFFLINK)
    879 		(void) strcat(flags, "G");
    880 
    881 	/* IRE_IF_CLONE wins over RTF_HOST - don't display both */
    882 	if (ire->ire_type & IRE_IF_CLONE)
    883 		(void) strcat(flags, "C");
    884 	else if (ire->ire_ipversion == IPV4_VERSION) {
    885 		if (ire->ire_mask == IP_HOST_MASK)
    886 			(void) strcat(flags, "H");
    887 	} else {
    888 		if (IN6_ARE_ADDR_EQUAL(&ire->ire_mask_v6, &ipv6_all_ones))
    889 			(void) strcat(flags, "H");
    890 	}
    891 
    892 	if (ire->ire_flags & RTF_DYNAMIC)
    893 		(void) strcat(flags, "D");
    894 	if (ire->ire_type == IRE_BROADCAST)
    895 		(void) strcat(flags, "b");
    896 	if (ire->ire_type == IRE_MULTICAST)
    897 		(void) strcat(flags, "m");
    898 	if (ire->ire_type == IRE_LOCAL)
    899 		(void) strcat(flags, "L");
    900 	if (ire->ire_type == IRE_NOROUTE)
    901 		(void) strcat(flags, "N");
    902 	if (ire->ire_flags & RTF_MULTIRT)
    903 		(void) strcat(flags, "M");
    904 	if (ire->ire_flags & RTF_SETSRC)
    905 		(void) strcat(flags, "S");
    906 	if (ire->ire_flags & RTF_REJECT)
    907 		(void) strcat(flags, "R");
    908 	if (ire->ire_flags & RTF_BLACKHOLE)
    909 		(void) strcat(flags, "B");
    910 }
    911 
    912 static int
    913 netstat_irev4_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
    914 {
    915 	const ire_t *ire = walk_data;
    916 	uint_t *opts = cb_data;
    917 	ipaddr_t gate;
    918 	char flags[10], intf[LIFNAMSIZ + 1];
    919 
    920 	if (ire->ire_ipversion != IPV4_VERSION)
    921 		return (WALK_NEXT);
    922 
    923 	/* Skip certain IREs by default */
    924 	if (!(*opts & NETSTAT_ALL) &&
    925 	    (ire->ire_type &
    926 	    (IRE_BROADCAST|IRE_LOCAL|IRE_MULTICAST|IRE_NOROUTE|IRE_IF_CLONE)))
    927 		return (WALK_NEXT);
    928 
    929 	if (*opts & NETSTAT_FIRST) {
    930 		*opts &= ~NETSTAT_FIRST;
    931 		mdb_printf("%<u>%s Table: IPv4%</u>\n",
    932 		    (*opts & NETSTAT_VERBOSE) ? "IRE" : "Routing");
    933 		if (*opts & NETSTAT_VERBOSE) {
    934 			mdb_printf("%<u>%-?s %-*s %-*s %-*s Device Mxfrg Rtt  "
    935 			    " Ref Flg Out   In/Fwd%</u>\n",
    936 			    "Address", ADDR_V4_WIDTH, "Destination",
    937 			    ADDR_V4_WIDTH, "Mask", ADDR_V4_WIDTH, "Gateway");
    938 		} else {
    939 			mdb_printf("%<u>%-?s %-*s %-*s Flags Ref  Use   "
    940 			    "Interface%</u>\n",
    941 			    "Address", ADDR_V4_WIDTH, "Destination",
    942 			    ADDR_V4_WIDTH, "Gateway");
    943 		}
    944 	}
    945 
    946 	gate = ire->ire_gateway_addr;
    947 
    948 	get_ireflags(ire, flags);
    949 
    950 	get_ifname(ire, intf);
    951 
    952 	if (*opts & NETSTAT_VERBOSE) {
    953 		mdb_printf("%?p %-*I %-*I %-*I %-6s %5u%c %4u %3u %-3s %5u "
    954 		    "%u\n", kaddr, ADDR_V4_WIDTH, ire->ire_addr, ADDR_V4_WIDTH,
    955 		    ire->ire_mask, ADDR_V4_WIDTH, gate, intf,
    956 		    0, ' ',
    957 		    ire->ire_metrics.iulp_rtt, ire->ire_refcnt, flags,
    958 		    ire->ire_ob_pkt_count, ire->ire_ib_pkt_count);
    959 	} else {
    960 		mdb_printf("%?p %-*I %-*I %-5s %4u %5u %s\n", kaddr,
    961 		    ADDR_V4_WIDTH, ire->ire_addr, ADDR_V4_WIDTH, gate, flags,
    962 		    ire->ire_refcnt,
    963 		    ire->ire_ob_pkt_count + ire->ire_ib_pkt_count, intf);
    964 	}
    965 
    966 	return (WALK_NEXT);
    967 }
    968 
    969 int
    970 ip_mask_to_plen_v6(const in6_addr_t *v6mask)
    971 {
    972 	int plen;
    973 	int i;
    974 	uint32_t val;
    975 
    976 	for (i = 3; i >= 0; i--)
    977 		if (v6mask->s6_addr32[i] != 0)
    978 			break;
    979 	if (i < 0)
    980 		return (0);
    981 	plen = 32 + 32 * i;
    982 	val = v6mask->s6_addr32[i];
    983 	while (!(val & 1)) {
    984 		val >>= 1;
    985 		plen--;
    986 	}
    987 
    988 	return (plen);
    989 }
    990 
    991 static int
    992 netstat_irev6_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
    993 {
    994 	const ire_t *ire = walk_data;
    995 	uint_t *opts = cb_data;
    996 	const in6_addr_t *gatep;
    997 	char deststr[ADDR_V6_WIDTH + 5];
    998 	char flags[10], intf[LIFNAMSIZ + 1];
    999 	int masklen;
   1000 
   1001 	if (ire->ire_ipversion != IPV6_VERSION)
   1002 		return (WALK_NEXT);
   1003 
   1004 	/* Skip certain IREs by default */
   1005 	if (!(*opts & NETSTAT_ALL) &&
   1006 	    (ire->ire_type &
   1007 	    (IRE_BROADCAST|IRE_LOCAL|IRE_MULTICAST|IRE_NOROUTE|IRE_IF_CLONE)))
   1008 		return (WALK_NEXT);
   1009 
   1010 	if (*opts & NETSTAT_FIRST) {
   1011 		*opts &= ~NETSTAT_FIRST;
   1012 		mdb_printf("\n%<u>%s Table: IPv6%</u>\n",
   1013 		    (*opts & NETSTAT_VERBOSE) ? "IRE" : "Routing");
   1014 		if (*opts & NETSTAT_VERBOSE) {
   1015 			mdb_printf("%<u>%-?s %-*s %-*s If    PMTU   Rtt   Ref "
   1016 			    "Flags Out    In/Fwd%</u>\n",
   1017 			    "Address", ADDR_V6_WIDTH+4, "Destination/Mask",
   1018 			    ADDR_V6_WIDTH, "Gateway");
   1019 		} else {
   1020 			mdb_printf("%<u>%-?s %-*s %-*s Flags Ref Use    If"
   1021 			    "%</u>\n",
   1022 			    "Address", ADDR_V6_WIDTH+4, "Destination/Mask",
   1023 			    ADDR_V6_WIDTH, "Gateway");
   1024 		}
   1025 	}
   1026 
   1027 	gatep = &ire->ire_gateway_addr_v6;
   1028 
   1029 	masklen = ip_mask_to_plen_v6(&ire->ire_mask_v6);
   1030 	(void) mdb_snprintf(deststr, sizeof (deststr), "%N/%d",
   1031 	    &ire->ire_addr_v6, masklen);
   1032 
   1033 	get_ireflags(ire, flags);
   1034 
   1035 	get_ifname(ire, intf);
   1036 
   1037 	if (*opts & NETSTAT_VERBOSE) {
   1038 		mdb_printf("%?p %-*s %-*N %-5s %5u%c %5u %3u %-5s %6u %u\n",
   1039 		    kaddr, ADDR_V6_WIDTH+4, deststr, ADDR_V6_WIDTH, gatep,
   1040 		    intf, 0, ' ',
   1041 		    ire->ire_metrics.iulp_rtt, ire->ire_refcnt,
   1042 		    flags, ire->ire_ob_pkt_count, ire->ire_ib_pkt_count);
   1043 	} else {
   1044 		mdb_printf("%?p %-*s %-*N %-5s %3u %6u %s\n", kaddr,
   1045 		    ADDR_V6_WIDTH+4, deststr, ADDR_V6_WIDTH, gatep, flags,
   1046 		    ire->ire_refcnt,
   1047 		    ire->ire_ob_pkt_count + ire->ire_ib_pkt_count, intf);
   1048 	}
   1049 
   1050 	return (WALK_NEXT);
   1051 }
   1052 
   1053 static void
   1054 netstat_header_v4(int proto)
   1055 {
   1056 	if (proto == IPPROTO_TCP)
   1057 		mdb_printf("%<u>%-?s ", "TCPv4");
   1058 	else if (proto == IPPROTO_UDP)
   1059 		mdb_printf("%<u>%-?s ", "UDPv4");
   1060 	else if (proto == IPPROTO_ICMP)
   1061 		mdb_printf("%<u>%-?s ", "ICMPv4");
   1062 	mdb_printf("State %6s%*s %6s%*s %-5s %-4s%</u>\n",
   1063 	    "", ADDR_V4_WIDTH, "Local Address",
   1064 	    "", ADDR_V4_WIDTH, "Remote Address", "Stack", "Zone");
   1065 }
   1066 
   1067 static void
   1068 netstat_header_v6(int proto)
   1069 {
   1070 	if (proto == IPPROTO_TCP)
   1071 		mdb_printf("%<u>%-?s ", "TCPv6");
   1072 	else if (proto == IPPROTO_UDP)
   1073 		mdb_printf("%<u>%-?s ", "UDPv6");
   1074 	else if (proto == IPPROTO_ICMP)
   1075 		mdb_printf("%<u>%-?s ", "ICMPv6");
   1076 	mdb_printf("State %6s%*s %6s%*s %-5s %-4s%</u>\n",
   1077 	    "", ADDR_V6_WIDTH, "Local Address",
   1078 	    "", ADDR_V6_WIDTH, "Remote Address", "Stack", "Zone");
   1079 }
   1080 
   1081 static int
   1082 netstat_print_conn(const char *cache, int proto, mdb_walk_cb_t cbfunc,
   1083     void *cbdata)
   1084 {
   1085 	netstat_cb_data_t *ncb = cbdata;
   1086 
   1087 	if ((ncb->opts & NETSTAT_VERBOSE) && proto == IPPROTO_TCP)
   1088 		netstat_tcp_verbose_header_pr();
   1089 	if (mdb_walk(cache, cbfunc, cbdata) == -1) {
   1090 		mdb_warn("failed to walk %s", cache);
   1091 		return (DCMD_ERR);
   1092 	}
   1093 	return (DCMD_OK);
   1094 }
   1095 
   1096 static int
   1097 netstat_print_common(const char *cache, int proto, mdb_walk_cb_t cbfunc,
   1098     void *cbdata)
   1099 {
   1100 	netstat_cb_data_t *ncb = cbdata;
   1101 	int af = ncb->af;
   1102 	int status = DCMD_OK;
   1103 
   1104 	if (af != AF_INET6) {
   1105 		ncb->af = AF_INET;
   1106 		netstat_header_v4(proto);
   1107 		status = netstat_print_conn(cache, proto, cbfunc, cbdata);
   1108 	}
   1109 	if (status == DCMD_OK && af != AF_INET) {
   1110 		ncb->af = AF_INET6;
   1111 		netstat_header_v6(proto);
   1112 		status = netstat_print_conn(cache, proto, cbfunc, cbdata);
   1113 	}
   1114 	ncb->af = af;
   1115 	return (status);
   1116 }
   1117 
   1118 /*ARGSUSED*/
   1119 int
   1120 netstat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
   1121 {
   1122 	uint_t opts = 0;
   1123 	const char *optf = NULL;
   1124 	const char *optP = NULL;
   1125 	netstat_cb_data_t *cbdata;
   1126 	int status;
   1127 	int af = 0;
   1128 
   1129 	if (mdb_getopts(argc, argv,
   1130 	    'a', MDB_OPT_SETBITS, NETSTAT_ALL, &opts,
   1131 	    'f', MDB_OPT_STR, &optf,
   1132 	    'P', MDB_OPT_STR, &optP,
   1133 	    'r', MDB_OPT_SETBITS, NETSTAT_ROUTE, &opts,
   1134 	    'v', MDB_OPT_SETBITS, NETSTAT_VERBOSE, &opts,
   1135 	    NULL) != argc)
   1136 		return (DCMD_USAGE);
   1137 
   1138 	if (optP != NULL) {
   1139 		if ((strcmp("tcp", optP) != 0) && (strcmp("udp", optP) != 0) &&
   1140 		    (strcmp("icmp", optP) != 0))
   1141 			return (DCMD_USAGE);
   1142 		if (opts & NETSTAT_ROUTE)
   1143 			return (DCMD_USAGE);
   1144 	}
   1145 
   1146 	if (optf == NULL)
   1147 		opts |= NETSTAT_V4 | NETSTAT_V6 | NETSTAT_UNIX;
   1148 	else if (strcmp("inet", optf) == 0)
   1149 		opts |= NETSTAT_V4;
   1150 	else if (strcmp("inet6", optf) == 0)
   1151 		opts |= NETSTAT_V6;
   1152 	else if (strcmp("unix", optf) == 0)
   1153 		opts |= NETSTAT_UNIX;
   1154 	else
   1155 		return (DCMD_USAGE);
   1156 
   1157 	if (opts & NETSTAT_ROUTE) {
   1158 		if (!(opts & (NETSTAT_V4|NETSTAT_V6)))
   1159 			return (DCMD_USAGE);
   1160 		if (opts & NETSTAT_V4) {
   1161 			opts |= NETSTAT_FIRST;
   1162 			if (mdb_walk("ip`ire", netstat_irev4_cb, &opts) == -1) {
   1163 				mdb_warn("failed to walk ip`ire");
   1164 				return (DCMD_ERR);
   1165 			}
   1166 		}
   1167 		if (opts & NETSTAT_V6) {
   1168 			opts |= NETSTAT_FIRST;
   1169 			if (mdb_walk("ip`ire", netstat_irev6_cb, &opts) == -1) {
   1170 				mdb_warn("failed to walk ip`ire");
   1171 				return (DCMD_ERR);
   1172 			}
   1173 		}
   1174 		return (DCMD_OK);
   1175 	}
   1176 
   1177 	if ((opts & NETSTAT_UNIX) && (optP == NULL)) {
   1178 		/* Print Unix Domain Sockets */
   1179 		mdb_printf("%<u>%-?s %-10s %-?s %-?s %-14s %-14s %s%</u>\n",
   1180 		    "AF_UNIX", "Type", "Vnode", "Conn", "Local Addr",
   1181 		    "Remote Addr", "Zone");
   1182 
   1183 		if (mdb_walk("genunix`sonode", netstat_unix_cb, NULL) == -1) {
   1184 			mdb_warn("failed to walk genunix`sonode");
   1185 			return (DCMD_ERR);
   1186 		}
   1187 		if (!(opts & (NETSTAT_V4 | NETSTAT_V6)))
   1188 			return (DCMD_OK);
   1189 	}
   1190 
   1191 	cbdata = mdb_alloc(sizeof (netstat_cb_data_t), UM_SLEEP);
   1192 	cbdata->opts = opts;
   1193 	if ((optf != NULL) && (opts & NETSTAT_V4))
   1194 		af = AF_INET;
   1195 	else if ((optf != NULL) && (opts & NETSTAT_V6))
   1196 		af = AF_INET6;
   1197 
   1198 	cbdata->af = af;
   1199 	if ((optP == NULL) || (strcmp("tcp", optP) == 0)) {
   1200 		status = netstat_print_common("tcp_conn_cache", IPPROTO_TCP,
   1201 		    netstat_tcp_cb, cbdata);
   1202 		if (status != DCMD_OK)
   1203 			goto out;
   1204 	}
   1205 
   1206 	if ((optP == NULL) || (strcmp("udp", optP) == 0)) {
   1207 		status = netstat_print_common("udp_conn_cache", IPPROTO_UDP,
   1208 		    netstat_udp_cb, cbdata);
   1209 		if (status != DCMD_OK)
   1210 			goto out;
   1211 	}
   1212 
   1213 	if ((optP == NULL) || (strcmp("icmp", optP) == 0)) {
   1214 		status = netstat_print_common("rawip_conn_cache", IPPROTO_ICMP,
   1215 		    netstat_icmp_cb, cbdata);
   1216 		if (status != DCMD_OK)
   1217 			goto out;
   1218 	}
   1219 out:
   1220 	mdb_free(cbdata, sizeof (netstat_cb_data_t));
   1221 	return (status);
   1222 }
   1223 
   1224 /*
   1225  * "::dladm show-bridge" support
   1226  */
   1227 typedef struct {
   1228 	uint_t opt_l;
   1229 	uint_t opt_f;
   1230 	uint_t opt_t;
   1231 	const char *name;
   1232 	clock_t lbolt;
   1233 	boolean_t found;
   1234 	uint_t nlinks;
   1235 	uint_t nfwd;
   1236 
   1237 	/*
   1238 	 * These structures are kept inside the 'args' for allocation reasons.
   1239 	 * They're all large data structures (over 1K), and may cause the stack
   1240 	 * to explode.  mdb and kmdb will fail in these cases, and thus we
   1241 	 * allocate them from the heap.
   1242 	 */
   1243 	trill_inst_t ti;
   1244 	bridge_link_t bl;
   1245 	mac_impl_t mi;
   1246 } show_bridge_args_t;
   1247 
   1248 static void
   1249 show_vlans(const uint8_t *vlans)
   1250 {
   1251 	int i, bit;
   1252 	uint8_t val;
   1253 	int rstart = -1, rnext = -1;
   1254 
   1255 	for (i = 0; i < BRIDGE_VLAN_ARR_SIZE; i++) {
   1256 		val = vlans[i];
   1257 		if (i == 0)
   1258 			val &= ~1;
   1259 		while ((bit = mdb_ffs(val)) != 0) {
   1260 			bit--;
   1261 			val &= ~(1 << bit);
   1262 			bit += i * sizeof (*vlans) * NBBY;
   1263 			if (bit != rnext) {
   1264 				if (rnext != -1 && rstart + 1 != rnext)
   1265 					mdb_printf("-%d", rnext - 1);
   1266 				if (rstart != -1)
   1267 					mdb_printf(",");
   1268 				mdb_printf("%d", bit);
   1269 				rstart = bit;
   1270 			}
   1271 			rnext = bit + 1;
   1272 		}
   1273 	}
   1274 	if (rnext != -1 && rstart + 1 != rnext)
   1275 		mdb_printf("-%d", rnext - 1);
   1276 	mdb_printf("\n");
   1277 }
   1278 
   1279 /*
   1280  * This callback is invoked by a walk of the links attached to a bridge.  If
   1281  * we're showing link details, then they're printed here.  If not, then we just
   1282  * count up the links for the bridge summary.
   1283  */
   1284 static int
   1285 do_bridge_links(uintptr_t addr, const void *data, void *ptr)
   1286 {
   1287 	show_bridge_args_t *args = ptr;
   1288 	const bridge_link_t *blp = data;
   1289 	char macaddr[ETHERADDRL * 3];
   1290 	const char *name;
   1291 
   1292 	args->nlinks++;
   1293 
   1294 	if (!args->opt_l)
   1295 		return (WALK_NEXT);
   1296 
   1297 	if (mdb_vread(&args->mi, sizeof (args->mi),
   1298 	    (uintptr_t)blp->bl_mh) == -1) {
   1299 		mdb_warn("cannot read mac data at %p", blp->bl_mh);
   1300 		name = "?";
   1301 	} else  {
   1302 		name = args->mi.mi_name;
   1303 	}
   1304 
   1305 	mdb_mac_addr(blp->bl_local_mac, ETHERADDRL, macaddr,
   1306 	    sizeof (macaddr));
   1307 
   1308 	mdb_printf("%-?p %-16s %-17s %03X %-4d ", addr, name, macaddr,
   1309 	    blp->bl_flags, blp->bl_pvid);
   1310 
   1311 	if (blp->bl_trilldata == NULL) {
   1312 		switch (blp->bl_state) {
   1313 		case BLS_BLOCKLISTEN:
   1314 			name = "BLOCK";
   1315 			break;
   1316 		case BLS_LEARNING:
   1317 			name = "LEARN";
   1318 			break;
   1319 		case BLS_FORWARDING:
   1320 			name = "FWD";
   1321 			break;
   1322 		default:
   1323 			name = "?";
   1324 		}
   1325 		mdb_printf("%-5s ", name);
   1326 		show_vlans(blp->bl_vlans);
   1327 	} else {
   1328 		show_vlans(blp->bl_afs);
   1329 	}
   1330 
   1331 	return (WALK_NEXT);
   1332 }
   1333 
   1334 /*
   1335  * It seems a shame to duplicate this code, but merging it with the link
   1336  * printing code above is more trouble than it would be worth.
   1337  */
   1338 static void
   1339 print_link_name(show_bridge_args_t *args, uintptr_t addr, char sep)
   1340 {
   1341 	const char *name;
   1342 
   1343 	if (mdb_vread(&args->bl, sizeof (args->bl), addr) == -1) {
   1344 		mdb_warn("cannot read bridge link at %p", addr);
   1345 		return;
   1346 	}
   1347 
   1348 	if (mdb_vread(&args->mi, sizeof (args->mi),
   1349 	    (uintptr_t)args->bl.bl_mh) == -1) {
   1350 		name = "?";
   1351 	} else  {
   1352 		name = args->mi.mi_name;
   1353 	}
   1354 
   1355 	mdb_printf("%s%c", name, sep);
   1356 }
   1357 
   1358 static int
   1359 do_bridge_fwd(uintptr_t addr, const void *data, void *ptr)
   1360 {
   1361 	show_bridge_args_t *args = ptr;
   1362 	const bridge_fwd_t *bfp = data;
   1363 	char macaddr[ETHERADDRL * 3];
   1364 	int i;
   1365 #define	MAX_FWD_LINKS	16
   1366 	bridge_link_t *links[MAX_FWD_LINKS];
   1367 	uint_t nlinks;
   1368 
   1369 	args->nfwd++;
   1370 
   1371 	if (!args->opt_f)
   1372 		return (WALK_NEXT);
   1373 
   1374 	if ((nlinks = bfp->bf_nlinks) > MAX_FWD_LINKS)
   1375 		nlinks = MAX_FWD_LINKS;
   1376 
   1377 	if (mdb_vread(links, sizeof (links[0]) * nlinks,
   1378 	    (uintptr_t)bfp->bf_links) == -1) {
   1379 		mdb_warn("cannot read bridge forwarding links at %p",
   1380 		    bfp->bf_links);
   1381 		return (WALK_ERR);
   1382 	}
   1383 
   1384 	mdb_mac_addr(bfp->bf_dest, ETHERADDRL, macaddr, sizeof (macaddr));
   1385 
   1386 	mdb_printf("%-?p %-17s ", addr, macaddr);
   1387 	if (bfp->bf_flags & BFF_LOCALADDR)
   1388 		mdb_printf("%-7s", "[self]");
   1389 	else
   1390 		mdb_printf("t-%-5d", args->lbolt - bfp->bf_lastheard);
   1391 	mdb_printf(" %-7u ", bfp->bf_refs);
   1392 
   1393 	if (bfp->bf_trill_nick != 0) {
   1394 		mdb_printf("%d\n", bfp->bf_trill_nick);
   1395 	} else {
   1396 		for (i = 0; i < bfp->bf_nlinks; i++) {
   1397 			print_link_name(args, (uintptr_t)links[i],
   1398 			    i == bfp->bf_nlinks - 1 ? '\n' : ' ');
   1399 		}
   1400 	}
   1401 
   1402 	return (WALK_NEXT);
   1403 }
   1404 
   1405 static int
   1406 do_show_bridge(uintptr_t addr, const void *data, void *ptr)
   1407 {
   1408 	show_bridge_args_t *args = ptr;
   1409 	bridge_inst_t bi;
   1410 	const bridge_inst_t *bip;
   1411 	trill_node_t tn;
   1412 	trill_sock_t tsp;
   1413 	trill_nickinfo_t tni;
   1414 	char bname[MAXLINKNAMELEN];
   1415 	char macaddr[ETHERADDRL * 3];
   1416 	char *cp;
   1417 	uint_t nnicks;
   1418 	int i;
   1419 
   1420 	if (data != NULL) {
   1421 		bip = data;
   1422 	} else {
   1423 		if (mdb_vread(&bi, sizeof (bi), addr) == -1) {
   1424 			mdb_warn("cannot read bridge instance at %p", addr);
   1425 			return (WALK_ERR);
   1426 		}
   1427 		bip = &bi;
   1428 	}
   1429 
   1430 	(void) strncpy(bname, bip->bi_name, sizeof (bname) - 1);
   1431 	bname[MAXLINKNAMELEN - 1] = '\0';
   1432 	cp = bname + strlen(bname);
   1433 	if (cp > bname && cp[-1] == '0')
   1434 		cp[-1] = '\0';
   1435 
   1436 	if (args->name != NULL && strcmp(args->name, bname) != 0)
   1437 		return (WALK_NEXT);
   1438 
   1439 	args->found = B_TRUE;
   1440 	args->nlinks = args->nfwd = 0;
   1441 
   1442 	if (args->opt_l) {
   1443 		mdb_printf("%-?s %-16s %-17s %3s %-4s ", "ADDR", "LINK",
   1444 		    "MAC-ADDR", "FLG", "PVID");
   1445 		if (bip->bi_trilldata == NULL)
   1446 			mdb_printf("%-5s %s\n", "STATE", "VLANS");
   1447 		else
   1448 			mdb_printf("%s\n", "FWD-VLANS");
   1449 	}
   1450 
   1451 	if (!args->opt_f && !args->opt_t &&
   1452 	    mdb_pwalk("list", do_bridge_links, args,
   1453 	    addr + offsetof(bridge_inst_t, bi_links)) != DCMD_OK)
   1454 		return (WALK_ERR);
   1455 
   1456 	if (args->opt_f)
   1457 		mdb_printf("%-?s %-17s %-7s %-7s %s\n", "ADDR", "DEST", "TIME",
   1458 		    "REFS", "OUTPUT");
   1459 
   1460 	if (!args->opt_l && !args->opt_t &&
   1461 	    mdb_pwalk("avl", do_bridge_fwd, args,
   1462 	    addr + offsetof(bridge_inst_t, bi_fwd)) != DCMD_OK)
   1463 		return (WALK_ERR);
   1464 
   1465 	nnicks = 0;
   1466 	if (bip->bi_trilldata != NULL && !args->opt_l && !args->opt_f) {
   1467 		if (mdb_vread(&args->ti, sizeof (args->ti),
   1468 		    (uintptr_t)bip->bi_trilldata) == -1) {
   1469 			mdb_warn("cannot read trill instance at %p",
   1470 			    bip->bi_trilldata);
   1471 			return (WALK_ERR);
   1472 		}
   1473 		if (args->opt_t)
   1474 			mdb_printf("%-?s %-5s %-17s %s\n", "ADDR",
   1475 			    "NICK", "NEXT-HOP", "LINK");
   1476 		for (i = 0; i < RBRIDGE_NICKNAME_MAX; i++) {
   1477 			if (args->ti.ti_nodes[i] == NULL)
   1478 				continue;
   1479 			if (args->opt_t) {
   1480 				if (mdb_vread(&tn, sizeof (tn),
   1481 				    (uintptr_t)args->ti.ti_nodes[i]) == -1) {
   1482 					mdb_warn("cannot read trill node %d at "
   1483 					    "%p", i, args->ti.ti_nodes[i]);
   1484 					return (WALK_ERR);
   1485 				}
   1486 				if (mdb_vread(&tni, sizeof (tni),
   1487 				    (uintptr_t)tn.tn_ni) == -1) {
   1488 					mdb_warn("cannot read trill node info "
   1489 					    "%d at %p", i, tn.tn_ni);
   1490 					return (WALK_ERR);
   1491 				}
   1492 				mdb_mac_addr(tni.tni_adjsnpa, ETHERADDRL,
   1493 				    macaddr, sizeof (macaddr));
   1494 				if (tni.tni_nick == args->ti.ti_nick) {
   1495 					(void) strcpy(macaddr, "[self]");
   1496 				}
   1497 				mdb_printf("%-?p %-5u %-17s ",
   1498 				    args->ti.ti_nodes[i], tni.tni_nick,
   1499 				    macaddr);
   1500 				if (tn.tn_tsp != NULL) {
   1501 					if (mdb_vread(&tsp, sizeof (tsp),
   1502 					    (uintptr_t)tn.tn_tsp) == -1) {
   1503 						mdb_warn("cannot read trill "
   1504 						    "socket info at %p",
   1505 						    tn.tn_tsp);
   1506 						return (WALK_ERR);
   1507 					}
   1508 					if (tsp.ts_link != NULL) {
   1509 						print_link_name(args,
   1510 						    (uintptr_t)tsp.ts_link,
   1511 						    '\n');
   1512 						continue;
   1513 					}
   1514 				}
   1515 				mdb_printf("--\n");
   1516 			} else {
   1517 				nnicks++;
   1518 			}
   1519 		}
   1520 	} else {
   1521 		if (args->opt_t)
   1522 			mdb_printf("bridge is not running TRILL\n");
   1523 	}
   1524 
   1525 	if (!args->opt_l && !args->opt_f && !args->opt_t) {
   1526 		mdb_printf("%-?p %-7s %-16s %-7u %-7u", addr,
   1527 		    bip->bi_trilldata == NULL ? "stp" : "trill", bname,
   1528 		    args->nlinks, args->nfwd);
   1529 		if (bip->bi_trilldata != NULL)
   1530 			mdb_printf(" %-7u %u\n", nnicks, args->ti.ti_nick);
   1531 		else
   1532 			mdb_printf(" %-7s %s\n", "--", "--");
   1533 	}
   1534 	return (WALK_NEXT);
   1535 }
   1536 
   1537 static int
   1538 dladm_show_bridge(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
   1539 {
   1540 	show_bridge_args_t *args;
   1541 	GElf_Sym sym;
   1542 	int i;
   1543 
   1544 	args = mdb_zalloc(sizeof (*args), UM_SLEEP);
   1545 
   1546 	i = mdb_getopts(argc, argv,
   1547 	    'l', MDB_OPT_SETBITS, 1, &args->opt_l,
   1548 	    'f', MDB_OPT_SETBITS, 1, &args->opt_f,
   1549 	    't', MDB_OPT_SETBITS, 1, &args->opt_t,
   1550 	    NULL);
   1551 
   1552 	argc -= i;
   1553 	argv += i;
   1554 
   1555 	if (argc > 1 || (argc == 1 && argv[0].a_type != MDB_TYPE_STRING)) {
   1556 		mdb_free(args, sizeof (*args));
   1557 		return (DCMD_USAGE);
   1558 	}
   1559 	if (argc == 1)
   1560 		args->name = argv[0].a_un.a_str;
   1561 
   1562 	if ((args->lbolt = mdb_get_lbolt()) == -1) {
   1563 		mdb_warn("failed to read lbolt");
   1564 		goto err;
   1565 	}
   1566 
   1567 	if (flags & DCMD_ADDRSPEC) {
   1568 		if (args->name != NULL) {
   1569 			mdb_printf("bridge name and address are mutually "
   1570 			    "exclusive\n");
   1571 			goto err;
   1572 		}
   1573 		if (!args->opt_l && !args->opt_f && !args->opt_t)
   1574 			mdb_printf("%-?s %-7s %-16s %-7s %-7s\n", "ADDR",
   1575 			    "PROTECT", "NAME", "NLINKS", "NFWD");
   1576 		if (do_show_bridge(addr, NULL, args) != WALK_NEXT)
   1577 			goto err;
   1578 		mdb_free(args, sizeof (*args));
   1579 		return (DCMD_OK);
   1580 	} else {
   1581 		if ((args->opt_l || args->opt_f || args->opt_t) &&
   1582 		    args->name == NULL) {
   1583 			mdb_printf("need bridge name or address with -[lft]\n");
   1584 			goto err;
   1585 		}
   1586 		if (mdb_lookup_by_obj("bridge", "inst_list", &sym) == -1) {
   1587 			mdb_warn("failed to find 'bridge`inst_list'");
   1588 			goto err;
   1589 		}
   1590 		if (!args->opt_l && !args->opt_f && !args->opt_t)
   1591 			mdb_printf("%-?s %-7s %-16s %-7s %-7s %-7s %s\n",
   1592 			    "ADDR", "PROTECT", "NAME", "NLINKS", "NFWD",
   1593 			    "NNICKS", "NICK");
   1594 		if (mdb_pwalk("list", do_show_bridge, args,
   1595 		    (uintptr_t)sym.st_value) != DCMD_OK)
   1596 			goto err;
   1597 		if (!args->found && args->name != NULL) {
   1598 			mdb_printf("bridge instance %s not found\n",
   1599 			    args->name);
   1600 			goto err;
   1601 		}
   1602 		mdb_free(args, sizeof (*args));
   1603 		return (DCMD_OK);
   1604 	}
   1605 
   1606 err:
   1607 	mdb_free(args, sizeof (*args));
   1608 	return (DCMD_ERR);
   1609 }
   1610 
   1611 /*
   1612  * Support for the "::dladm" dcmd
   1613  */
   1614 int
   1615 dladm(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
   1616 {
   1617 	if (argc < 1 || argv[0].a_type != MDB_TYPE_STRING)
   1618 		return (DCMD_USAGE);
   1619 
   1620 	/*
   1621 	 * This could be a bit more elaborate, once we support more of the
   1622 	 * dladm show-* subcommands.
   1623 	 */
   1624 	argc--;
   1625 	argv++;
   1626 	if (strcmp(argv[-1].a_un.a_str, "show-bridge") == 0)
   1627 		return (dladm_show_bridge(addr, flags, argc, argv));
   1628 
   1629 	return (DCMD_USAGE);
   1630 }
   1631 
   1632 void
   1633 dladm_help(void)
   1634 {
   1635 	mdb_printf("Subcommands:\n"
   1636 	    "  show-bridge [-flt] [<name>]\n"
   1637 	    "\t     Show bridge information; -l for links and -f for "
   1638 	    "forwarding\n"
   1639 	    "\t     entries, and -t for TRILL nicknames.  Address is required "
   1640 	    "if name\n"
   1641 	    "\t     is not specified.\n");
   1642 }
   1643