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