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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <mdb/mdb_modapi.h>
29 #include <mdb/mdb_ks.h>
30 #include <mdb/mdb_ctf.h>
31 #include <sys/types.h>
32 #include <sys/tihdr.h>
33 #include <inet/led.h>
34 #include <inet/common.h>
35 #include <netinet/in.h>
36 #include <netinet/ip6.h>
37 #include <netinet/icmp6.h>
38 #include <inet/ip.h>
39 #include <inet/ip6.h>
40 #include <inet/ipclassifier.h>
41 #include <inet/tcp.h>
42 #include <sys/stream.h>
43 #include <sys/vfs.h>
44 #include <sys/stropts.h>
45 #include <sys/tpicommon.h>
46 #include <sys/socket.h>
47 #include <sys/socketvar.h>
48 #include <sys/cred_impl.h>
49 #include <inet/udp_impl.h>
50 #include <inet/arp_impl.h>
51 #include <inet/rawip_impl.h>
52 #include <inet/mi.h>
53 
54 #define	MIH2MIO(mihp) (&(mihp)->mh_o)
55 
56 #define	ADDR_V6_WIDTH	23
57 #define	ADDR_V4_WIDTH	15
58 
59 #define	NETSTAT_ALL	0x01
60 #define	NETSTAT_VERBOSE	0x02
61 #define	NETSTAT_ROUTE	0x04
62 #define	NETSTAT_V4	0x08
63 #define	NETSTAT_V6	0x10
64 #define	NETSTAT_UNIX	0x20
65 
66 #define	NETSTAT_FIRST	0x80000000u
67 
68 /*
69  * Print an IPv4 address and port number in a compact and easy to read format
70  * The arguments are in network byte order
71  */
72 static void
73 net_ipv4addrport_pr(const in6_addr_t *nipv6addr, in_port_t nport)
74 {
75 	uint32_t naddr = V4_PART_OF_V6((*nipv6addr));
76 
77 	mdb_nhconvert(&nport, &nport, sizeof (nport));
78 	mdb_printf("%*I.%-5hu", ADDR_V4_WIDTH, naddr, nport);
79 }
80 
81 /*
82  * Print an IPv6 address and port number in a compact and easy to read format
83  * The arguments are in network byte order
84  */
85 static void
86 net_ipv6addrport_pr(const in6_addr_t *naddr, in_port_t nport)
87 {
88 	mdb_nhconvert(&nport, &nport, sizeof (nport));
89 	mdb_printf("%*N.%-5hu", ADDR_V6_WIDTH, naddr, nport);
90 }
91 
92 static int
93 net_tcp_active(const tcp_t *tcp)
94 {
95 	return (tcp->tcp_state >= TCPS_ESTABLISHED);
96 }
97 
98 static int
99 net_tcp_ipv4(const tcp_t *tcp)
100 {
101 	return ((tcp->tcp_ipversion == IPV4_VERSION) ||
102 	    (IN6_IS_ADDR_UNSPECIFIED(&tcp->tcp_ip_src_v6) &&
103 	    (tcp->tcp_state <= TCPS_LISTEN)));
104 }
105 
106 static int
107 net_tcp_ipv6(const tcp_t *tcp)
108 {
109 	return (tcp->tcp_ipversion == IPV6_VERSION);
110 }
111 
112 static int
113 net_udp_active(const udp_t *udp)
114 {
115 	return ((udp->udp_state == TS_IDLE) ||
116 	    (udp->udp_state == TS_DATA_XFER));
117 }
118 
119 static int
120 net_udp_ipv4(const udp_t *udp)
121 {
122 	return ((udp->udp_ipversion == IPV4_VERSION) ||
123 	    (IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src) &&
124 	    (udp->udp_state <= TS_IDLE)));
125 }
126 
127 static int
128 net_udp_ipv6(const udp_t *udp)
129 {
130 	return (udp->udp_ipversion == IPV6_VERSION);
131 }
132 
133 int
134 sonode_walk_init(mdb_walk_state_t *wsp)
135 {
136 	if (wsp->walk_addr == NULL) {
137 		GElf_Sym sym;
138 		struct socklist *slp;
139 
140 		if (mdb_lookup_by_obj("sockfs", "socklist", &sym) == -1) {
141 			mdb_warn("failed to lookup sockfs`socklist");
142 			return (WALK_ERR);
143 		}
144 
145 		slp = (struct socklist *)(uintptr_t)sym.st_value;
146 
147 		if (mdb_vread(&wsp->walk_addr, sizeof (wsp->walk_addr),
148 		    (uintptr_t)&slp->sl_list) == -1) {
149 			mdb_warn("failed to read address of initial sonode "
150 			    "at %p", &slp->sl_list);
151 			return (WALK_ERR);
152 		}
153 	}
154 
155 	wsp->walk_data = mdb_alloc(sizeof (struct sonode), UM_SLEEP);
156 	return (WALK_NEXT);
157 }
158 
159 int
160 sonode_walk_step(mdb_walk_state_t *wsp)
161 {
162 	int status;
163 	struct sonode *sonodep;
164 
165 	if (wsp->walk_addr == NULL)
166 		return (WALK_DONE);
167 
168 	if (mdb_vread(wsp->walk_data, sizeof (struct sonode),
169 	    wsp->walk_addr) == -1) {
170 		mdb_warn("failed to read sonode at %p", wsp->walk_addr);
171 		return (WALK_ERR);
172 	}
173 
174 	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
175 	    wsp->walk_cbdata);
176 
177 	sonodep = wsp->walk_data;
178 
179 	wsp->walk_addr = (uintptr_t)sonodep->so_next;
180 	return (status);
181 }
182 
183 void
184 sonode_walk_fini(mdb_walk_state_t *wsp)
185 {
186 	mdb_free(wsp->walk_data, sizeof (struct sonode));
187 }
188 
189 struct mi_walk_data {
190 	uintptr_t mi_wd_miofirst;
191 	MI_O mi_wd_miodata;
192 };
193 
194 int
195 mi_walk_init(mdb_walk_state_t *wsp)
196 {
197 	struct mi_walk_data *wdp;
198 
199 	if (wsp->walk_addr == NULL) {
200 		mdb_warn("mi doesn't support global walks\n");
201 		return (WALK_ERR);
202 	}
203 
204 	wdp = mdb_alloc(sizeof (struct mi_walk_data), UM_SLEEP);
205 
206 	/* So that we do not immediately return WALK_DONE below */
207 	wdp->mi_wd_miofirst = NULL;
208 
209 	wsp->walk_data = wdp;
210 	return (WALK_NEXT);
211 }
212 
213 int
214 mi_walk_step(mdb_walk_state_t *wsp)
215 {
216 	struct mi_walk_data *wdp = wsp->walk_data;
217 	MI_OP miop = &wdp->mi_wd_miodata;
218 	int status;
219 
220 	/* Always false in the first iteration */
221 	if ((wsp->walk_addr == (uintptr_t)NULL) ||
222 	    (wsp->walk_addr == wdp->mi_wd_miofirst)) {
223 		return (WALK_DONE);
224 	}
225 
226 	if (mdb_vread(miop, sizeof (MI_O), wsp->walk_addr) == -1) {
227 		mdb_warn("failed to read MI object at %p", wsp->walk_addr);
228 		return (WALK_ERR);
229 	}
230 
231 	status = wsp->walk_callback(wsp->walk_addr, miop, wsp->walk_cbdata);
232 
233 	/* Only true in the first iteration */
234 	if (wdp->mi_wd_miofirst == NULL)
235 		wdp->mi_wd_miofirst = wsp->walk_addr;
236 
237 	wsp->walk_addr = (uintptr_t)miop->mi_o_next;
238 	return (status);
239 }
240 
241 void
242 mi_walk_fini(mdb_walk_state_t *wsp)
243 {
244 	mdb_free(wsp->walk_data, sizeof (struct mi_walk_data));
245 }
246 
247 typedef struct mi_payload_walk_data_s {
248 	uintptr_t mi_pwd_first;
249 	void *mi_pwd_data;
250 } mi_payload_walk_data_t;
251 
252 static void
253 delete_mi_payload_walk_data(mi_payload_walk_data_t *pwdp, size_t payload_size)
254 {
255 	mdb_free(pwdp->mi_pwd_data, payload_size);
256 	mdb_free(pwdp, sizeof (mi_payload_walk_data_t));
257 }
258 
259 typedef struct mi_payload_walk_arg_s {
260 	const char *mi_pwa_obj;		/* load object of mi_o_head_t * */
261 	const char *mi_pwa_sym;		/* symbol name of mi_o_head_t * */
262 	const size_t mi_pwa_size;	/* size of mi payload */
263 	const uint_t mi_pwa_flags;	/* device and/or module */
264 } mi_payload_walk_arg_t;
265 
266 #define	MI_PAYLOAD_DEVICE	0x1
267 #define	MI_PAYLOAD_MODULE	0x2
268 
269 int
270 mi_payload_walk_init(mdb_walk_state_t *wsp)
271 {
272 	const mi_payload_walk_arg_t *arg = wsp->walk_arg;
273 	mi_payload_walk_data_t *pwdp;
274 	GElf_Sym sym;
275 	mi_head_t *mihp;
276 
277 	/* Determine the address to start or end the walk with */
278 	if (mdb_lookup_by_obj(arg->mi_pwa_obj, arg->mi_pwa_sym, &sym) == -1) {
279 		mdb_warn("failed to lookup %s`%s",
280 		    arg->mi_pwa_obj, arg->mi_pwa_sym);
281 		return (WALK_ERR);
282 	}
283 
284 	if (mdb_vread(&mihp, sizeof (mihp), (uintptr_t)sym.st_value) == -1) {
285 		mdb_warn("failed to read address of global MI Head "
286 		    "mi_o_head_t at %p", (uintptr_t)sym.st_value);
287 		return (WALK_ERR);
288 	}
289 
290 	pwdp = mdb_alloc(sizeof (mi_payload_walk_data_t), UM_SLEEP);
291 	pwdp->mi_pwd_data = mdb_alloc(arg->mi_pwa_size, UM_SLEEP);
292 	wsp->walk_data = pwdp;
293 
294 	if (wsp->walk_addr == NULL) {
295 		/* Do not immediately return WALK_DONE below */
296 		pwdp->mi_pwd_first = NULL;
297 		/* We determined where to begin */
298 		wsp->walk_addr = (uintptr_t)MIH2MIO(mihp);
299 	} else {
300 		/* Do not cycle through all of the MI_O objects */
301 		pwdp->mi_pwd_first = (uintptr_t)MIH2MIO(mihp);
302 		/* We were given where to begin */
303 		wsp->walk_addr = (uintptr_t)((MI_OP)wsp->walk_addr - 1);
304 	}
305 
306 	if (mdb_layered_walk("genunix`mi", wsp) == -1) {
307 		mdb_warn("failed to walk genunix`mi");
308 		delete_mi_payload_walk_data(pwdp, arg->mi_pwa_size);
309 		return (WALK_ERR);
310 	}
311 
312 	return (WALK_NEXT);
313 }
314 
315 int
316 mi_payload_walk_step(mdb_walk_state_t *wsp)
317 {
318 	const mi_payload_walk_arg_t *arg = wsp->walk_arg;
319 	mi_payload_walk_data_t *pwdp = wsp->walk_data;
320 	void *payload = pwdp->mi_pwd_data;
321 	uintptr_t payload_kaddr = (uintptr_t)((MI_OP)wsp->walk_addr + 1);
322 	const MI_O *mio = wsp->walk_layer;
323 
324 	/* If this is a local walk, prevent cycling */
325 	if (wsp->walk_addr == pwdp->mi_pwd_first)
326 		return (WALK_DONE);
327 
328 	/*
329 	 * This was a global walk, prevent reading this payload as the
330 	 * initial MI_O is the head of the list and is not the header
331 	 * to a valid payload
332 	 */
333 	if (pwdp->mi_pwd_first == NULL) {
334 		pwdp->mi_pwd_first = wsp->walk_addr;
335 		return (WALK_NEXT);
336 	}
337 
338 	if (mio->mi_o_isdev == B_FALSE) {
339 		/* mio is a module */
340 		if (!(arg->mi_pwa_flags & MI_PAYLOAD_MODULE))
341 			return (WALK_NEXT);
342 	} else {
343 		/* mio is a device */
344 		if (!(arg->mi_pwa_flags & MI_PAYLOAD_DEVICE))
345 			return (WALK_NEXT);
346 	}
347 
348 	if (mdb_vread(payload, arg->mi_pwa_size, payload_kaddr) == -1) {
349 		mdb_warn("failed to read payload at %p", payload_kaddr);
350 		return (WALK_ERR);
351 	}
352 
353 	return (wsp->walk_callback(payload_kaddr, payload, wsp->walk_cbdata));
354 }
355 
356 void
357 mi_payload_walk_fini(mdb_walk_state_t *wsp)
358 {
359 	const mi_payload_walk_arg_t *arg = wsp->walk_arg;
360 
361 	delete_mi_payload_walk_data(wsp->walk_data, arg->mi_pwa_size);
362 }
363 
364 const mi_payload_walk_arg_t mi_ar_arg = {
365 	"arp", "ar_g_head", sizeof (ar_t),
366 	MI_PAYLOAD_DEVICE | MI_PAYLOAD_MODULE
367 };
368 
369 const mi_payload_walk_arg_t mi_icmp_arg = {
370 	"icmp", "icmp_g_head", sizeof (icmp_t),
371 	MI_PAYLOAD_DEVICE | MI_PAYLOAD_MODULE
372 };
373 
374 const mi_payload_walk_arg_t mi_ill_arg =
375 	{ "ip", "ip_g_head", sizeof (ill_t), MI_PAYLOAD_MODULE };
376 
377 int
378 sonode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
379 {
380 	const char *optf = NULL;
381 	const char *optt = NULL;
382 	const char *optp = NULL;
383 	int family, type, proto;
384 	int filter = 0;
385 	struct sonode so;
386 
387 	if (!(flags & DCMD_ADDRSPEC)) {
388 		if (mdb_walk_dcmd("genunix`sonode", "genunix`sonode", argc,
389 		    argv) == -1) {
390 			mdb_warn("failed to walk sonode");
391 			return (DCMD_ERR);
392 		}
393 
394 		return (DCMD_OK);
395 	}
396 
397 	if (mdb_getopts(argc, argv,
398 	    'f', MDB_OPT_STR, &optf,
399 	    't', MDB_OPT_STR, &optt,
400 	    'p', MDB_OPT_STR, &optp,
401 	    NULL) != argc)
402 		return (DCMD_USAGE);
403 
404 	if (optf != NULL) {
405 		if (strcmp("inet", optf) == 0)
406 			family = AF_INET;
407 		else if (strcmp("inet6", optf) == 0)
408 			family = AF_INET6;
409 		else if (strcmp("unix", optf) == 0)
410 			family = AF_UNIX;
411 		else
412 			family = mdb_strtoull(optf);
413 		filter = 1;
414 	}
415 
416 	if (optt != NULL) {
417 		if (strcmp("stream", optt) == 0)
418 			type = SOCK_STREAM;
419 		else if (strcmp("dgram", optt) == 0)
420 			type = SOCK_DGRAM;
421 		else if (strcmp("raw", optt) == 0)
422 			type = SOCK_RAW;
423 		else
424 			type = mdb_strtoull(optt);
425 		filter = 1;
426 	}
427 
428 	if (optp != NULL) {
429 		proto = mdb_strtoull(optp);
430 		filter = 1;
431 	}
432 
433 	if (DCMD_HDRSPEC(flags) && !filter) {
434 		mdb_printf("%<u>%-?s Family Type Proto State Mode Flag "
435 		    "AccessVP%</u>\n", "Sonode:");
436 	}
437 
438 	if (mdb_vread(&so, sizeof (so), addr) == -1) {
439 		mdb_warn("failed to read sonode at %p", addr);
440 		return (DCMD_ERR);
441 	}
442 
443 	if ((optf != NULL) && (so.so_family != family))
444 		return (DCMD_OK);
445 
446 	if ((optt != NULL) && (so.so_type != type))
447 		return (DCMD_OK);
448 
449 	if ((optp != NULL) && (so.so_protocol != proto))
450 		return (DCMD_OK);
451 
452 	if (filter) {
453 		mdb_printf("%0?p\n", addr);
454 		return (DCMD_OK);
455 	}
456 
457 	mdb_printf("%0?p ", addr);
458 
459 	switch (so.so_family) {
460 	    case AF_UNIX:
461 		mdb_printf("unix  ");
462 		break;
463 	    case AF_INET:
464 		mdb_printf("inet  ");
465 		break;
466 	    case AF_INET6:
467 		mdb_printf("inet6 ");
468 		break;
469 	    default:
470 		mdb_printf("%6hi", so.so_family);
471 	}
472 
473 	switch (so.so_type) {
474 	    case SOCK_STREAM:
475 		mdb_printf(" strm");
476 		break;
477 	    case SOCK_DGRAM:
478 		mdb_printf(" dgrm");
479 		break;
480 	    case SOCK_RAW:
481 		mdb_printf(" raw ");
482 		break;
483 	    default:
484 		mdb_printf(" %4hi", so.so_type);
485 	}
486 
487 	mdb_printf(" %5hi %05x %04x %04hx %0?p\n",
488 	    so.so_protocol, so.so_state, so.so_mode,
489 	    so.so_flag, so.so_accessvp);
490 
491 	return (DCMD_OK);
492 }
493 
494 #define	MI_PAYLOAD	0x1
495 #define	MI_DEVICE	0x2
496 #define	MI_MODULE	0x4
497 
498 int
499 mi(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
500 {
501 	uint_t opts = 0;
502 	MI_O	mio;
503 
504 	if (!(flags & DCMD_ADDRSPEC))
505 		return (DCMD_USAGE);
506 
507 	if (mdb_getopts(argc, argv,
508 	    'p', MDB_OPT_SETBITS, MI_PAYLOAD, &opts,
509 	    'd', MDB_OPT_SETBITS, MI_DEVICE, &opts,
510 	    'm', MDB_OPT_SETBITS, MI_MODULE, &opts,
511 	    NULL) != argc)
512 		return (DCMD_USAGE);
513 
514 	if ((opts & (MI_DEVICE | MI_MODULE)) == (MI_DEVICE | MI_MODULE)) {
515 		mdb_warn("at most one filter, d for devices or m "
516 		    "for modules, may be specified\n");
517 		return (DCMD_USAGE);
518 	}
519 
520 	if ((opts == 0) && (DCMD_HDRSPEC(flags))) {
521 		mdb_printf("%<u>%-?s %-?s %-?s IsDev Dev%</u>\n",
522 		    "MI_O", "Next", "Prev");
523 	}
524 
525 	if (mdb_vread(&mio, sizeof (mio), addr) == -1) {
526 		mdb_warn("failed to read mi object MI_O at %p", addr);
527 		return (DCMD_ERR);
528 	}
529 
530 	if (opts != 0) {
531 		if (mio.mi_o_isdev == B_FALSE) {
532 			/* mio is a module */
533 			if (!(opts & MI_MODULE) && (opts & MI_DEVICE))
534 				return (DCMD_OK);
535 		} else {
536 			/* mio is a device */
537 			if (!(opts & MI_DEVICE) && (opts & MI_MODULE))
538 				return (DCMD_OK);
539 		}
540 
541 		if (opts & MI_PAYLOAD)
542 			mdb_printf("%p\n", addr + sizeof (MI_O));
543 		else
544 			mdb_printf("%p\n", addr);
545 		return (DCMD_OK);
546 	}
547 
548 	mdb_printf("%0?p %0?p %0?p ", addr, mio.mi_o_next, mio.mi_o_prev);
549 
550 	if (mio.mi_o_isdev == B_FALSE)
551 		mdb_printf("FALSE");
552 	else
553 		mdb_printf("TRUE ");
554 
555 	mdb_printf(" %0?p\n", mio.mi_o_dev);
556 
557 	return (DCMD_OK);
558 }
559 
560 static void
561 netstat_tcp_verbose_pr(const tcp_t *tcp)
562 {
563 	mdb_printf("       %5i %08x %08x %5i %08x %08x %5li %5i\n",
564 	    tcp->tcp_swnd, tcp->tcp_snxt, tcp->tcp_suna, tcp->tcp_rwnd,
565 	    tcp->tcp_rack, tcp->tcp_rnxt, tcp->tcp_rto, tcp->tcp_mss);
566 }
567 
568 /*ARGSUSED*/
569 static int
570 netstat_tcp_cb(uintptr_t kaddr, const void *walk_data, void *cb_data, int af)
571 {
572 	const uintptr_t opts = (uintptr_t)cb_data;
573 	static size_t itc_size = 0;
574 	uintptr_t tcp_kaddr;
575 	conn_t *connp;
576 	tcp_t *tcp;
577 
578 	if (itc_size == 0) {
579 		mdb_ctf_id_t id;
580 
581 		if (mdb_ctf_lookup_by_name("itc_t", &id) != 0) {
582 			mdb_warn("failed to lookup type 'itc_t'");
583 			return (WALK_ERR);
584 		}
585 		itc_size = mdb_ctf_type_size(id);
586 	}
587 
588 	connp = (conn_t *)mdb_alloc(itc_size, UM_SLEEP | UM_GC);
589 
590 	if (mdb_vread(connp, itc_size, kaddr) == -1) {
591 		mdb_warn("failed to read connection info at %p", kaddr);
592 		return (WALK_ERR);
593 	}
594 
595 	tcp_kaddr = (uintptr_t)connp->conn_tcp;
596 	tcp = (tcp_t *)((uintptr_t)connp + (tcp_kaddr - kaddr));
597 
598 	if ((uintptr_t)tcp < (uintptr_t)connp ||
599 	    (uintptr_t)(tcp + 1) > (uintptr_t)connp + itc_size ||
600 	    (uintptr_t)tcp->tcp_connp != kaddr) {
601 		mdb_warn("conn_tcp %p is invalid", tcp_kaddr);
602 		return (WALK_NEXT);
603 	}
604 	connp->conn_tcp = tcp;
605 	tcp->tcp_connp = connp;
606 
607 	if (!((opts & NETSTAT_ALL) || net_tcp_active(tcp)) ||
608 	    (af == AF_INET && !net_tcp_ipv4(tcp)) ||
609 	    (af == AF_INET6 && !net_tcp_ipv6(tcp))) {
610 		return (WALK_NEXT);
611 	}
612 
613 	mdb_printf("%0?p %2i ", tcp_kaddr, tcp->tcp_state);
614 	if (af == AF_INET) {
615 		net_ipv4addrport_pr(&tcp->tcp_ip_src_v6, tcp->tcp_lport);
616 		mdb_printf(" ");
617 		net_ipv4addrport_pr(&tcp->tcp_remote_v6, tcp->tcp_fport);
618 	} else if (af == AF_INET6) {
619 		net_ipv6addrport_pr(&tcp->tcp_ip_src_v6, tcp->tcp_lport);
620 		mdb_printf(" ");
621 		net_ipv6addrport_pr(&tcp->tcp_remote_v6, tcp->tcp_fport);
622 	}
623 	mdb_printf(" %4i\n", connp->conn_zoneid);
624 
625 	if (opts & NETSTAT_VERBOSE)
626 		netstat_tcp_verbose_pr(tcp);
627 
628 	return (WALK_NEXT);
629 }
630 
631 static int
632 netstat_tcpv4_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
633 {
634 	return (netstat_tcp_cb(kaddr, walk_data, cb_data, AF_INET));
635 }
636 
637 static int
638 netstat_tcpv6_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
639 {
640 	return (netstat_tcp_cb(kaddr, walk_data, cb_data, AF_INET6));
641 }
642 
643 /*ARGSUSED*/
644 static int
645 netstat_udp_cb(uintptr_t kaddr, const void *walk_data, void *cb_data, int af)
646 {
647 	const uintptr_t opts = (uintptr_t)cb_data;
648 	udp_t udp;
649 	conn_t connp;
650 
651 	if (mdb_vread(&udp, sizeof (udp_t), kaddr) == -1) {
652 		mdb_warn("failed to read udp at %p", kaddr);
653 		return (WALK_ERR);
654 	}
655 
656 	if (mdb_vread(&connp, sizeof (conn_t),
657 	    (uintptr_t)udp.udp_connp) == -1) {
658 		mdb_warn("failed to read udp_connp at %p",
659 		    (uintptr_t)udp.udp_connp);
660 		return (WALK_ERR);
661 	}
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 	mdb_printf("%0?p %2i ", kaddr, udp.udp_state);
670 	if (af == AF_INET) {
671 		net_ipv4addrport_pr(&udp.udp_v6src, udp.udp_port);
672 		mdb_printf(" ");
673 		net_ipv4addrport_pr(&udp.udp_v6dst, udp.udp_dstport);
674 	} else if (af == AF_INET6) {
675 		net_ipv6addrport_pr(&udp.udp_v6src, udp.udp_port);
676 		mdb_printf(" ");
677 		net_ipv6addrport_pr(&udp.udp_v6dst, udp.udp_dstport);
678 	}
679 	mdb_printf(" %4i\n", connp.conn_zoneid);
680 
681 	return (WALK_NEXT);
682 }
683 
684 static int
685 netstat_udpv4_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
686 {
687 	return (netstat_udp_cb(kaddr, walk_data, cb_data, AF_INET));
688 }
689 
690 static int
691 netstat_udpv6_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
692 {
693 	return (netstat_udp_cb(kaddr, walk_data, cb_data, AF_INET6));
694 }
695 
696 /*
697  * print the address of a unix domain socket
698  *
699  * so is the address of a AF_UNIX struct sonode in mdb's address space
700  * soa is the address of the struct soaddr to print
701  *
702  * returns 0 on success, -1 otherwise
703  */
704 static int
705 netstat_unix_name_pr(const struct sonode *so, const struct soaddr *soa)
706 {
707 	const char none[] = " (none)";
708 
709 	if ((so->so_state & SS_ISBOUND) && (soa->soa_len != 0)) {
710 		if (so->so_state & SS_FADDR_NOXLATE) {
711 			mdb_printf("%-14s ", " (socketpair)");
712 		} else {
713 			if (soa->soa_len > sizeof (sa_family_t)) {
714 				char addr[MAXPATHLEN + 1];
715 
716 				if (mdb_readstr(addr, sizeof (addr),
717 				    (uintptr_t)&soa->soa_sa->sa_data) == -1) {
718 					mdb_warn("failed to read unix address "
719 					    "at %p", &soa->soa_sa->sa_data);
720 					return (-1);
721 				}
722 
723 				mdb_printf("%-14s ", addr);
724 			} else {
725 				mdb_printf("%-14s ", none);
726 			}
727 		}
728 	} else {
729 		mdb_printf("%-14s ", none);
730 	}
731 
732 	return (0);
733 }
734 
735 /* based on sockfs_snapshot */
736 /*ARGSUSED*/
737 static int
738 netstat_unix_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
739 {
740 	const struct sonode *so = walk_data;
741 
742 	if (so->so_accessvp == NULL)
743 		return (WALK_NEXT);
744 
745 	if (so->so_family != AF_UNIX) {
746 		mdb_warn("sonode of family %hi at %p\n", so->so_family, kaddr);
747 		return (WALK_ERR);
748 	}
749 
750 	mdb_printf("%-?p ", kaddr);
751 
752 	switch (so->so_serv_type) {
753 	    case T_CLTS:
754 		mdb_printf("%-10s ", "dgram");
755 		break;
756 	    case T_COTS:
757 		mdb_printf("%-10s ", "stream");
758 		break;
759 	    case T_COTS_ORD:
760 		mdb_printf("%-10s ", "stream-ord");
761 		break;
762 	    default:
763 		    mdb_printf("%-10i ", so->so_serv_type);
764 	}
765 
766 	if ((so->so_state & SS_ISBOUND) &&
767 	    (so->so_ux_laddr.soua_magic == SOU_MAGIC_EXPLICIT)) {
768 		mdb_printf("%0?p ", so->so_ux_laddr.soua_vp);
769 	} else {
770 		mdb_printf("%0?p ", NULL);
771 	}
772 
773 	if ((so->so_state & SS_ISCONNECTED) &&
774 	    (so->so_ux_faddr.soua_magic == SOU_MAGIC_EXPLICIT)) {
775 		mdb_printf("%0?p ", so->so_ux_faddr.soua_vp);
776 	} else {
777 		mdb_printf("%0?p ", NULL);
778 	}
779 
780 	if (netstat_unix_name_pr(so, &so->so_laddr) == -1)
781 		return (WALK_ERR);
782 
783 	if (netstat_unix_name_pr(so, &so->so_faddr) == -1)
784 		return (WALK_ERR);
785 
786 	mdb_printf("%4i\n", so->so_zoneid);
787 
788 	return (WALK_NEXT);
789 }
790 
791 static void
792 netstat_tcp_verbose_header_pr(void)
793 {
794 	mdb_printf("       %<u>%-5s %-8s %-8s %-5s %-8s %-8s %5s %5s%</u>\n",
795 	    "Swind", "Snext", "Suna", "Rwind", "Rack", "Rnext", "Rto", "Mss");
796 }
797 
798 static void
799 get_ifname(const ire_t *ire, char *intf)
800 {
801 	ill_t ill;
802 
803 	*intf = '\0';
804 	if (ire->ire_type == IRE_CACHE) {
805 		queue_t stq;
806 
807 		if (mdb_vread(&stq, sizeof (stq), (uintptr_t)ire->ire_stq) ==
808 		    -1)
809 			return;
810 		if (mdb_vread(&ill, sizeof (ill), (uintptr_t)stq.q_ptr) == -1)
811 			return;
812 		(void) mdb_readstr(intf, MIN(LIFNAMSIZ, ill.ill_name_length),
813 		    (uintptr_t)ill.ill_name);
814 	} else if (ire->ire_ipif != NULL) {
815 		ipif_t ipif;
816 		char *cp;
817 
818 		if (mdb_vread(&ipif, sizeof (ipif),
819 		    (uintptr_t)ire->ire_ipif) == -1)
820 			return;
821 		if (mdb_vread(&ill, sizeof (ill), (uintptr_t)ipif.ipif_ill) ==
822 		    -1)
823 			return;
824 		(void) mdb_readstr(intf, MIN(LIFNAMSIZ, ill.ill_name_length),
825 		    (uintptr_t)ill.ill_name);
826 		if (ipif.ipif_id != 0) {
827 			cp = intf + strlen(intf);
828 			(void) mdb_snprintf(cp, LIFNAMSIZ + 1 - (cp - intf),
829 			    ":%u", ipif.ipif_id);
830 		}
831 	}
832 }
833 
834 static void
835 get_v4flags(const ire_t *ire, char *flags)
836 {
837 	(void) strcpy(flags, "U");
838 	if (ire->ire_type == IRE_DEFAULT || ire->ire_type == IRE_PREFIX ||
839 	    ire->ire_type == IRE_HOST || ire->ire_type == IRE_HOST_REDIRECT)
840 		(void) strcat(flags, "G");
841 	if (ire->ire_mask == IP_HOST_MASK)
842 		(void) strcat(flags, "H");
843 	if (ire->ire_type == IRE_HOST_REDIRECT)
844 		(void) strcat(flags, "D");
845 	if (ire->ire_type == IRE_CACHE)
846 		(void) strcat(flags, "A");
847 	if (ire->ire_type == IRE_BROADCAST)
848 		(void) strcat(flags, "B");
849 	if (ire->ire_type == IRE_LOCAL)
850 		(void) strcat(flags, "L");
851 	if (ire->ire_flags & RTF_MULTIRT)
852 		(void) strcat(flags, "M");
853 	if (ire->ire_flags & RTF_SETSRC)
854 		(void) strcat(flags, "S");
855 }
856 
857 static int
858 ip_mask_to_plen(ipaddr_t mask)
859 {
860 	int i;
861 
862 	if (mask == 0)
863 		return (0);
864 	for (i = 32; i > 0; i--, mask >>= 1)
865 		if (mask & 1)
866 			break;
867 	return (i);
868 }
869 
870 static int
871 netstat_irev4_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
872 {
873 	const ire_t *ire = walk_data;
874 	uint_t *opts = cb_data;
875 	ipaddr_t gate;
876 	char flags[10], intf[LIFNAMSIZ + 1];
877 
878 	if (ire->ire_ipversion != IPV4_VERSION || ire->ire_in_src_addr != 0 ||
879 	    ire->ire_in_ill != NULL)
880 		return (WALK_NEXT);
881 
882 	if (!(*opts & NETSTAT_ALL) && (ire->ire_type == IRE_CACHE ||
883 	    ire->ire_type == IRE_BROADCAST || ire->ire_type == IRE_LOCAL))
884 		return (WALK_NEXT);
885 
886 	if (*opts & NETSTAT_FIRST) {
887 		*opts &= ~NETSTAT_FIRST;
888 		mdb_printf("%<u>%s Table: IPv4%</u>\n",
889 		    (*opts & NETSTAT_VERBOSE) ? "IRE" : "Routing");
890 		if (*opts & NETSTAT_VERBOSE) {
891 			mdb_printf("%<u>%-?s %-*s %-*s %-*s Device Mxfrg Rtt  "
892 			    " Ref Flg Out   In/Fwd%</u>\n",
893 			    "Address", ADDR_V4_WIDTH, "Destination",
894 			    ADDR_V4_WIDTH, "Mask", ADDR_V4_WIDTH, "Gateway");
895 		} else {
896 			mdb_printf("%<u>%-?s %-*s %-*s Flags Ref  Use   "
897 			    "Interface%</u>\n",
898 			    "Address", ADDR_V4_WIDTH, "Destination",
899 			    ADDR_V4_WIDTH, "Gateway");
900 		}
901 	}
902 
903 	gate = (ire->ire_type & (IRE_INTERFACE|IRE_LOOPBACK|IRE_BROADCAST)) ?
904 	    ire->ire_src_addr : ire->ire_gateway_addr;
905 
906 	get_v4flags(ire, flags);
907 
908 	get_ifname(ire, intf);
909 
910 	if (*opts & NETSTAT_VERBOSE) {
911 		mdb_printf("%?p %-*I %-*I %-*I %-6s %5u%c %4u %3u %-3s %5u "
912 		    "%u\n", kaddr, ADDR_V4_WIDTH, ire->ire_addr, ADDR_V4_WIDTH,
913 		    ire->ire_mask, ADDR_V4_WIDTH, gate, intf,
914 		    ire->ire_max_frag, ire->ire_frag_flag ? '*' : ' ',
915 		    ire->ire_uinfo.iulp_rtt, ire->ire_refcnt, flags,
916 		    ire->ire_ob_pkt_count, ire->ire_ib_pkt_count);
917 	} else {
918 		mdb_printf("%?p %-*I %-*I %-5s %4u %5u %s\n", kaddr,
919 		    ADDR_V4_WIDTH, ire->ire_addr, ADDR_V4_WIDTH, gate, flags,
920 		    ire->ire_refcnt,
921 		    ire->ire_ob_pkt_count + ire->ire_ib_pkt_count, intf);
922 	}
923 
924 	return (WALK_NEXT);
925 }
926 
927 static int
928 netstat_irev4src_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
929 {
930 	const ire_t *ire = walk_data;
931 	uint_t *opts = cb_data;
932 	ipaddr_t gate;
933 	char flags[10], intf[LIFNAMSIZ + 1], srcif[LIFNAMSIZ + 1];
934 	char dest[ADDR_V4_WIDTH + 3 + 1];
935 	ill_t ill;
936 
937 	if (ire->ire_ipversion != IPV4_VERSION ||
938 	    (ire->ire_in_src_addr == 0 && ire->ire_in_ill == NULL))
939 		return (WALK_NEXT);
940 
941 	if (!(*opts & NETSTAT_ALL) && (ire->ire_type == IRE_CACHE ||
942 	    ire->ire_type == IRE_BROADCAST || ire->ire_type == IRE_LOCAL))
943 		return (WALK_NEXT);
944 
945 	if (*opts & NETSTAT_FIRST) {
946 		*opts &= ~NETSTAT_FIRST;
947 		mdb_printf("\n%<u>%s Table: IPv4 Source-Specific%</u>\n",
948 		    (*opts & NETSTAT_VERBOSE) ? "IRE" : "Routing");
949 		if (*opts & NETSTAT_VERBOSE) {
950 			mdb_printf("%<u>%-?s %-*s In If       %-*s %-*s "
951 			    "Out If      Mxfrg Rtt   Ref Flg Out   In/Fwd"
952 			    "%</u>\n",
953 			    "Address", ADDR_V4_WIDTH+3, "Destination",
954 			    ADDR_V4_WIDTH, "Source", ADDR_V4_WIDTH, "Gateway");
955 		} else {
956 			mdb_printf("%<u>%-?s %-*s In If    %-*s %-*s Flags "
957 			    "Ref  Use   Out If%</u>\n",
958 			    "Address", ADDR_V4_WIDTH+3, "Destination",
959 			    ADDR_V4_WIDTH, "Source", ADDR_V4_WIDTH, "Gateway");
960 		}
961 	}
962 
963 	gate = (ire->ire_type & (IRE_INTERFACE|IRE_LOOPBACK|IRE_BROADCAST)) ?
964 	    ire->ire_src_addr : ire->ire_gateway_addr;
965 
966 	get_v4flags(ire, flags);
967 
968 	get_ifname(ire, intf);
969 
970 	srcif[0] = '\0';
971 	if (mdb_vread(&ill, sizeof (ill), (uintptr_t)ire->ire_in_ill) != -1)
972 		(void) mdb_readstr(srcif, MIN(LIFNAMSIZ, ill.ill_name_length),
973 		    (uintptr_t)ill.ill_name);
974 
975 	if (ire->ire_in_src_addr != 0 && ire->ire_addr == 0 &&
976 	    ire->ire_mask == 0)
977 		strcpy(dest, "  --");
978 	else
979 		mdb_snprintf(dest, sizeof (dest), "%I/%d", ire->ire_addr,
980 		    ip_mask_to_plen(ire->ire_mask));
981 
982 	if (*opts & NETSTAT_VERBOSE) {
983 		mdb_printf("%?p %-*s %-11s %-*I %-*I %-11s %5u%c %4u %3u %-3s "
984 		    "%5u %u\n", kaddr, ADDR_V4_WIDTH+3, dest, srcif,
985 		    ADDR_V4_WIDTH, ire->ire_in_src_addr, ADDR_V4_WIDTH, gate,
986 		    intf, ire->ire_max_frag, ire->ire_frag_flag ? '*' : ' ',
987 		    ire->ire_uinfo.iulp_rtt, ire->ire_refcnt, flags,
988 		    ire->ire_ob_pkt_count, ire->ire_ib_pkt_count);
989 	} else {
990 		mdb_printf("%?p %-*s %-8s %-*I %-*I %-5s %4u %5u %s\n", kaddr,
991 		    ADDR_V4_WIDTH+3, dest, srcif, ADDR_V4_WIDTH,
992 		    ire->ire_in_src_addr, ADDR_V4_WIDTH, gate, flags,
993 		    ire->ire_refcnt,
994 		    ire->ire_ob_pkt_count + ire->ire_ib_pkt_count, intf);
995 	}
996 
997 	return (WALK_NEXT);
998 }
999 
1000 int
1001 ip_mask_to_plen_v6(const in6_addr_t *v6mask)
1002 {
1003 	int plen;
1004 	int i;
1005 	uint32_t val;
1006 
1007 	for (i = 3; i >= 0; i--)
1008 		if (v6mask->s6_addr32[i] != 0)
1009 			break;
1010 	if (i < 0)
1011 		return (0);
1012 	plen = 32 + 32 * i;
1013 	val = v6mask->s6_addr32[i];
1014 	while (!(val & 1)) {
1015 		val >>= 1;
1016 		plen--;
1017 	}
1018 
1019 	return (plen);
1020 }
1021 
1022 static int
1023 netstat_irev6_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
1024 {
1025 	const ire_t *ire = walk_data;
1026 	uint_t *opts = cb_data;
1027 	const in6_addr_t *gatep;
1028 	char deststr[ADDR_V6_WIDTH + 5];
1029 	char flags[10], intf[LIFNAMSIZ + 1];
1030 	int masklen;
1031 
1032 	if (ire->ire_ipversion != IPV6_VERSION)
1033 		return (WALK_NEXT);
1034 
1035 	if (!(*opts & NETSTAT_ALL) && ire->ire_type == IRE_CACHE)
1036 		return (WALK_NEXT);
1037 
1038 	if (*opts & NETSTAT_FIRST) {
1039 		*opts &= ~NETSTAT_FIRST;
1040 		mdb_printf("\n%<u>%s Table: IPv6%</u>\n",
1041 		    (*opts & NETSTAT_VERBOSE) ? "IRE" : "Routing");
1042 		if (*opts & NETSTAT_VERBOSE) {
1043 			mdb_printf("%<u>%-?s %-*s %-*s If    PMTU   Rtt   Ref "
1044 			    "Flags Out    In/Fwd%</u>\n",
1045 			    "Address", ADDR_V6_WIDTH+4, "Destination/Mask",
1046 			    ADDR_V6_WIDTH, "Gateway");
1047 		} else {
1048 			mdb_printf("%<u>%-?s %-*s %-*s Flags Ref Use    If"
1049 			    "%</u>\n",
1050 			    "Address", ADDR_V6_WIDTH+4, "Destination/Mask",
1051 			    ADDR_V6_WIDTH, "Gateway");
1052 		}
1053 	}
1054 
1055 	gatep = (ire->ire_type & (IRE_INTERFACE|IRE_LOOPBACK)) ?
1056 	    &ire->ire_src_addr_v6 : &ire->ire_gateway_addr_v6;
1057 
1058 	masklen = ip_mask_to_plen_v6(&ire->ire_mask_v6);
1059 	(void) mdb_snprintf(deststr, sizeof (deststr), "%N/%d",
1060 	    &ire->ire_addr_v6, masklen);
1061 
1062 	(void) strcpy(flags, "U");
1063 	if (ire->ire_type == IRE_DEFAULT || ire->ire_type == IRE_PREFIX ||
1064 	    ire->ire_type == IRE_HOST || ire->ire_type == IRE_HOST_REDIRECT)
1065 		(void) strcat(flags, "G");
1066 	if (masklen == IPV6_ABITS)
1067 		(void) strcat(flags, "H");
1068 	if (ire->ire_type == IRE_HOST_REDIRECT)
1069 		(void) strcat(flags, "D");
1070 	if (ire->ire_type == IRE_CACHE)
1071 		(void) strcat(flags, "A");
1072 	if (ire->ire_type == IRE_LOCAL)
1073 		(void) strcat(flags, "L");
1074 	if (ire->ire_flags & RTF_MULTIRT)
1075 		(void) strcat(flags, "M");
1076 	if (ire->ire_flags & RTF_SETSRC)
1077 		(void) strcat(flags, "S");
1078 
1079 	get_ifname(ire, intf);
1080 
1081 	if (*opts & NETSTAT_VERBOSE) {
1082 		mdb_printf("%?p %-*s %-*N %-5s %5u%c %5u %3u %-5s %6u %u\n",
1083 		    kaddr, ADDR_V6_WIDTH+4, deststr, ADDR_V6_WIDTH, gatep,
1084 		    intf, ire->ire_max_frag, ire->ire_frag_flag ? '*' : ' ',
1085 		    ire->ire_uinfo.iulp_rtt, ire->ire_refcnt,
1086 		    flags, ire->ire_ob_pkt_count, ire->ire_ib_pkt_count);
1087 	} else {
1088 		mdb_printf("%?p %-*s %-*N %-5s %3u %6u %s\n", kaddr,
1089 		    ADDR_V6_WIDTH+4, deststr, ADDR_V6_WIDTH, gatep, flags,
1090 		    ire->ire_refcnt,
1091 		    ire->ire_ob_pkt_count + ire->ire_ib_pkt_count, intf);
1092 	}
1093 
1094 	return (WALK_NEXT);
1095 }
1096 
1097 /*ARGSUSED*/
1098 int
1099 netstat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1100 {
1101 	uint_t opts = 0;
1102 	const char *optf = NULL;
1103 	const char *optP = NULL;
1104 
1105 	if (mdb_getopts(argc, argv,
1106 	    'a', MDB_OPT_SETBITS, NETSTAT_ALL, &opts,
1107 	    'f', MDB_OPT_STR, &optf,
1108 	    'P', MDB_OPT_STR, &optP,
1109 	    'r', MDB_OPT_SETBITS, NETSTAT_ROUTE, &opts,
1110 	    'v', MDB_OPT_SETBITS, NETSTAT_VERBOSE, &opts,
1111 	    NULL) != argc)
1112 		return (DCMD_USAGE);
1113 
1114 	if (optP != NULL) {
1115 		if ((strcmp("tcp", optP) != 0) && (strcmp("udp", optP) != 0))
1116 			return (DCMD_USAGE);
1117 		if (opts & NETSTAT_ROUTE)
1118 			return (DCMD_USAGE);
1119 	}
1120 
1121 	if (optf == NULL)
1122 		opts |= NETSTAT_V4 | NETSTAT_V6 | NETSTAT_UNIX;
1123 	else if (strcmp("inet", optf) == 0)
1124 		opts |= NETSTAT_V4;
1125 	else if (strcmp("inet6", optf) == 0)
1126 		opts |= NETSTAT_V6;
1127 	else if (strcmp("unix", optf) == 0)
1128 		opts |= NETSTAT_UNIX;
1129 	else
1130 		return (DCMD_USAGE);
1131 
1132 	if (opts & NETSTAT_ROUTE) {
1133 		if (!(opts & (NETSTAT_V4|NETSTAT_V6)))
1134 			return (DCMD_USAGE);
1135 		if (opts & NETSTAT_V4) {
1136 			opts |= NETSTAT_FIRST;
1137 			if (mdb_walk("ip`ire", netstat_irev4_cb, &opts) == -1) {
1138 				mdb_warn("failed to walk ip`ire");
1139 				return (DCMD_ERR);
1140 			}
1141 			opts |= NETSTAT_FIRST;
1142 			if (mdb_walk("ip`ire", netstat_irev4src_cb,
1143 			    &opts) == -1) {
1144 				mdb_warn("failed to walk ip`ire");
1145 				return (DCMD_ERR);
1146 			}
1147 		}
1148 		if (opts & NETSTAT_V6) {
1149 			opts |= NETSTAT_FIRST;
1150 			if (mdb_walk("ip`ire", netstat_irev6_cb, &opts) == -1) {
1151 				mdb_warn("failed to walk ip`ire");
1152 				return (DCMD_ERR);
1153 			}
1154 		}
1155 		return (DCMD_OK);
1156 	}
1157 
1158 	if ((optP == NULL) || (strcmp("tcp", optP) == 0)) {
1159 		if ((optf == NULL) || (strcmp("inet", optf) == 0)) {
1160 			/* Print TCPv4 connection */
1161 			mdb_printf(
1162 			    "%<u>%-?s St %*s       %*s       %s%</u>\n",
1163 			    "TCPv4", ADDR_V4_WIDTH, "Local Address",
1164 			    ADDR_V4_WIDTH, "Remote Address", "Zone");
1165 
1166 			if (opts & NETSTAT_VERBOSE)
1167 				netstat_tcp_verbose_header_pr();
1168 
1169 			if (mdb_walk("ipcl_tcpconn_cache", netstat_tcpv4_cb,
1170 			    (void *)(uintptr_t)opts) == -1) {
1171 				mdb_warn("failed to walk ipcl_tcpconn_cache");
1172 				return (DCMD_ERR);
1173 			}
1174 		}
1175 
1176 		if ((optf == NULL) || (strcmp("inet6", optf) == 0)) {
1177 			/* Print TCPv6 connection */
1178 			mdb_printf(
1179 			    "%<u>%-?s St %*s       %*s       %s\n%</u>",
1180 			    "TCPv6", ADDR_V6_WIDTH, "Local Address",
1181 			    ADDR_V6_WIDTH, "Remote Address", "Zone");
1182 
1183 			if (opts & NETSTAT_VERBOSE)
1184 				netstat_tcp_verbose_header_pr();
1185 
1186 			if (mdb_walk("ipcl_tcpconn_cache", netstat_tcpv6_cb,
1187 			    (void *)(uintptr_t)opts) == -1) {
1188 				mdb_warn("failed to walk ipcl_tcpconn_cache");
1189 				return (DCMD_ERR);
1190 			}
1191 		}
1192 	}
1193 
1194 	if ((optP == NULL) || (strcmp("udp", optP) == 0)) {
1195 		if ((optf == NULL) || (strcmp("inet", optf) == 0)) {
1196 			/* Print UDPv4 connection */
1197 			mdb_printf(
1198 			    "%<u>%-?s St %*s       %*s       %s\n%</u>",
1199 			    "UDPv4", ADDR_V4_WIDTH, "Local Address",
1200 			    ADDR_V4_WIDTH, "Remote Address", "Zone");
1201 
1202 			if (mdb_walk("udp_cache", netstat_udpv4_cb,
1203 			    (void *)(uintptr_t)opts) == -1) {
1204 				mdb_warn("failed to walk genunix`udp");
1205 				return (DCMD_ERR);
1206 			}
1207 
1208 		}
1209 
1210 		if ((optf == NULL) || (strcmp("inet6", optf) == 0)) {
1211 			/* Print UDPv6 connection */
1212 			mdb_printf(
1213 			    "%<u>%-?s St %*s       %*s       %s\n%</u>",
1214 			    "UDPv6", ADDR_V6_WIDTH, "Local Address",
1215 			    ADDR_V6_WIDTH, "Remote Address", "Zone");
1216 
1217 			if (mdb_walk("udp_cache", netstat_udpv6_cb,
1218 			    (void *)(uintptr_t)opts) == -1) {
1219 				mdb_warn("failed to walk genunix`udp");
1220 				return (DCMD_ERR);
1221 			}
1222 		}
1223 	}
1224 
1225 	if (((optf == NULL) || (strcmp("unix", optf) == 0)) && (optP == NULL)) {
1226 		/* Print Unix Domain Sockets */
1227 		mdb_printf("%<u>%-?s %-10s %-?s %-?s %-14s %-14s %s%</u>\n",
1228 		    "AF_UNIX", "Type", "Vnode", "Conn", "Local Addr",
1229 		    "Remote Addr", "Zone");
1230 
1231 		if (mdb_walk("genunix`sonode", netstat_unix_cb, NULL) == -1) {
1232 			mdb_warn("failed to walk genunix`sonode");
1233 			return (DCMD_ERR);
1234 		}
1235 	}
1236 
1237 	return (DCMD_OK);
1238 }
1239