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