xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/net.c (revision a73c0fe4e90b82a478f821ef3adb5cf34f6a9346)
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 2008 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 
66 /* Walkers for various *_stack_t */
67 int
68 ar_stacks_walk_init(mdb_walk_state_t *wsp)
69 {
70 	if (mdb_layered_walk("netstack", wsp) == -1) {
71 		mdb_warn("can't walk 'netstack'");
72 		return (WALK_ERR);
73 	}
74 	return (WALK_NEXT);
75 }
76 
77 int
78 ar_stacks_walk_step(mdb_walk_state_t *wsp)
79 {
80 	uintptr_t kaddr;
81 	netstack_t nss;
82 
83 	if (mdb_vread(&nss, sizeof (nss), wsp->walk_addr) == -1) {
84 		mdb_warn("can't read netstack at %p", wsp->walk_addr);
85 		return (WALK_ERR);
86 	}
87 	kaddr = (uintptr_t)nss.netstack_modules[NS_ARP];
88 	return (wsp->walk_callback(kaddr, wsp->walk_layer, wsp->walk_cbdata));
89 }
90 
91 int
92 icmp_stacks_walk_init(mdb_walk_state_t *wsp)
93 {
94 	if (mdb_layered_walk("netstack", wsp) == -1) {
95 		mdb_warn("can't walk 'netstack'");
96 		return (WALK_ERR);
97 	}
98 	return (WALK_NEXT);
99 }
100 
101 int
102 icmp_stacks_walk_step(mdb_walk_state_t *wsp)
103 {
104 	uintptr_t kaddr;
105 	netstack_t nss;
106 
107 	if (mdb_vread(&nss, sizeof (nss), wsp->walk_addr) == -1) {
108 		mdb_warn("can't read netstack at %p", wsp->walk_addr);
109 		return (WALK_ERR);
110 	}
111 	kaddr = (uintptr_t)nss.netstack_modules[NS_ICMP];
112 	return (wsp->walk_callback(kaddr, wsp->walk_layer, wsp->walk_cbdata));
113 }
114 
115 int
116 tcp_stacks_walk_init(mdb_walk_state_t *wsp)
117 {
118 	if (mdb_layered_walk("netstack", wsp) == -1) {
119 		mdb_warn("can't walk 'netstack'");
120 		return (WALK_ERR);
121 	}
122 	return (WALK_NEXT);
123 }
124 
125 int
126 tcp_stacks_walk_step(mdb_walk_state_t *wsp)
127 {
128 	uintptr_t kaddr;
129 	netstack_t nss;
130 
131 	if (mdb_vread(&nss, sizeof (nss), wsp->walk_addr) == -1) {
132 		mdb_warn("can't read netstack at %p", wsp->walk_addr);
133 		return (WALK_ERR);
134 	}
135 	kaddr = (uintptr_t)nss.netstack_modules[NS_TCP];
136 	return (wsp->walk_callback(kaddr, wsp->walk_layer, wsp->walk_cbdata));
137 }
138 
139 int
140 udp_stacks_walk_init(mdb_walk_state_t *wsp)
141 {
142 	if (mdb_layered_walk("netstack", wsp) == -1) {
143 		mdb_warn("can't walk 'netstack'");
144 		return (WALK_ERR);
145 	}
146 	return (WALK_NEXT);
147 }
148 
149 int
150 udp_stacks_walk_step(mdb_walk_state_t *wsp)
151 {
152 	uintptr_t kaddr;
153 	netstack_t nss;
154 
155 	if (mdb_vread(&nss, sizeof (nss), wsp->walk_addr) == -1) {
156 		mdb_warn("can't read netstack at %p", wsp->walk_addr);
157 		return (WALK_ERR);
158 	}
159 	kaddr = (uintptr_t)nss.netstack_modules[NS_UDP];
160 	return (wsp->walk_callback(kaddr, wsp->walk_layer, wsp->walk_cbdata));
161 }
162 
163 /*
164  * Print an IPv4 address and port number in a compact and easy to read format
165  * The arguments are in network byte order
166  */
167 static void
168 net_ipv4addrport_pr(const in6_addr_t *nipv6addr, in_port_t nport)
169 {
170 	uint32_t naddr = V4_PART_OF_V6((*nipv6addr));
171 
172 	mdb_nhconvert(&nport, &nport, sizeof (nport));
173 	mdb_printf("%*I.%-5hu", ADDR_V4_WIDTH, naddr, nport);
174 }
175 
176 /*
177  * Print an IPv6 address and port number in a compact and easy to read format
178  * The arguments are in network byte order
179  */
180 static void
181 net_ipv6addrport_pr(const in6_addr_t *naddr, in_port_t nport)
182 {
183 	mdb_nhconvert(&nport, &nport, sizeof (nport));
184 	mdb_printf("%*N.%-5hu", ADDR_V6_WIDTH, naddr, nport);
185 }
186 
187 static int
188 net_tcp_active(const tcp_t *tcp)
189 {
190 	return (tcp->tcp_state >= TCPS_ESTABLISHED);
191 }
192 
193 static int
194 net_tcp_ipv4(const tcp_t *tcp)
195 {
196 	return ((tcp->tcp_ipversion == IPV4_VERSION) ||
197 	    (IN6_IS_ADDR_UNSPECIFIED(&tcp->tcp_ip_src_v6) &&
198 	    (tcp->tcp_state <= TCPS_LISTEN)));
199 }
200 
201 static int
202 net_tcp_ipv6(const tcp_t *tcp)
203 {
204 	return (tcp->tcp_ipversion == IPV6_VERSION);
205 }
206 
207 static int
208 net_udp_active(const udp_t *udp)
209 {
210 	return ((udp->udp_state == TS_IDLE) ||
211 	    (udp->udp_state == TS_DATA_XFER));
212 }
213 
214 static int
215 net_udp_ipv4(const udp_t *udp)
216 {
217 	return ((udp->udp_ipversion == IPV4_VERSION) ||
218 	    (IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src) &&
219 	    (udp->udp_state <= TS_IDLE)));
220 }
221 
222 static int
223 net_udp_ipv6(const udp_t *udp)
224 {
225 	return (udp->udp_ipversion == IPV6_VERSION);
226 }
227 
228 int
229 sonode_walk_init(mdb_walk_state_t *wsp)
230 {
231 	if (wsp->walk_addr == NULL) {
232 		GElf_Sym sym;
233 		struct socklist *slp;
234 
235 		if (mdb_lookup_by_obj("sockfs", "socklist", &sym) == -1) {
236 			mdb_warn("failed to lookup sockfs`socklist");
237 			return (WALK_ERR);
238 		}
239 
240 		slp = (struct socklist *)(uintptr_t)sym.st_value;
241 
242 		if (mdb_vread(&wsp->walk_addr, sizeof (wsp->walk_addr),
243 		    (uintptr_t)&slp->sl_list) == -1) {
244 			mdb_warn("failed to read address of initial sonode "
245 			    "at %p", &slp->sl_list);
246 			return (WALK_ERR);
247 		}
248 	}
249 
250 	wsp->walk_data = mdb_alloc(sizeof (struct sotpi_sonode), UM_SLEEP);
251 	return (WALK_NEXT);
252 }
253 
254 int
255 sonode_walk_step(mdb_walk_state_t *wsp)
256 {
257 	int status;
258 	struct sotpi_sonode *stp;
259 
260 	if (wsp->walk_addr == NULL)
261 		return (WALK_DONE);
262 
263 	if (mdb_vread(wsp->walk_data, sizeof (struct sotpi_sonode),
264 	    wsp->walk_addr) == -1) {
265 		mdb_warn("failed to read sonode at %p", wsp->walk_addr);
266 		return (WALK_ERR);
267 	}
268 
269 	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
270 	    wsp->walk_cbdata);
271 
272 	stp = wsp->walk_data;
273 
274 	wsp->walk_addr = (uintptr_t)stp->st_info.sti_next_so;
275 	return (status);
276 }
277 
278 void
279 sonode_walk_fini(mdb_walk_state_t *wsp)
280 {
281 	mdb_free(wsp->walk_data, sizeof (struct sotpi_sonode));
282 }
283 
284 struct mi_walk_data {
285 	uintptr_t mi_wd_miofirst;
286 	MI_O mi_wd_miodata;
287 };
288 
289 int
290 mi_walk_init(mdb_walk_state_t *wsp)
291 {
292 	struct mi_walk_data *wdp;
293 
294 	if (wsp->walk_addr == NULL) {
295 		mdb_warn("mi doesn't support global walks\n");
296 		return (WALK_ERR);
297 	}
298 
299 	wdp = mdb_alloc(sizeof (struct mi_walk_data), UM_SLEEP);
300 
301 	/* So that we do not immediately return WALK_DONE below */
302 	wdp->mi_wd_miofirst = NULL;
303 
304 	wsp->walk_data = wdp;
305 	return (WALK_NEXT);
306 }
307 
308 int
309 mi_walk_step(mdb_walk_state_t *wsp)
310 {
311 	struct mi_walk_data *wdp = wsp->walk_data;
312 	MI_OP miop = &wdp->mi_wd_miodata;
313 	int status;
314 
315 	/* Always false in the first iteration */
316 	if ((wsp->walk_addr == (uintptr_t)NULL) ||
317 	    (wsp->walk_addr == wdp->mi_wd_miofirst)) {
318 		return (WALK_DONE);
319 	}
320 
321 	if (mdb_vread(miop, sizeof (MI_O), wsp->walk_addr) == -1) {
322 		mdb_warn("failed to read MI object at %p", wsp->walk_addr);
323 		return (WALK_ERR);
324 	}
325 
326 	/* Only true in the first iteration */
327 	if (wdp->mi_wd_miofirst == NULL) {
328 		wdp->mi_wd_miofirst = wsp->walk_addr;
329 		status = WALK_NEXT;
330 	} else {
331 		status = wsp->walk_callback(wsp->walk_addr + sizeof (MI_O),
332 		    &miop[1], wsp->walk_cbdata);
333 	}
334 
335 	wsp->walk_addr = (uintptr_t)miop->mi_o_next;
336 	return (status);
337 }
338 
339 void
340 mi_walk_fini(mdb_walk_state_t *wsp)
341 {
342 	mdb_free(wsp->walk_data, sizeof (struct mi_walk_data));
343 }
344 
345 typedef struct mi_payload_walk_arg_s {
346 	const char *mi_pwa_walker;	/* Underlying walker */
347 	const off_t mi_pwa_head_off;	/* Offset for mi_o_head_t * in stack */
348 	const size_t mi_pwa_size;	/* size of mi payload */
349 	const uint_t mi_pwa_flags;	/* device and/or module */
350 } mi_payload_walk_arg_t;
351 
352 #define	MI_PAYLOAD_DEVICE	0x1
353 #define	MI_PAYLOAD_MODULE	0x2
354 
355 int
356 mi_payload_walk_init(mdb_walk_state_t *wsp)
357 {
358 	const mi_payload_walk_arg_t *arg = wsp->walk_arg;
359 
360 	if (mdb_layered_walk(arg->mi_pwa_walker, wsp) == -1) {
361 		mdb_warn("can't walk '%s'", arg->mi_pwa_walker);
362 		return (WALK_ERR);
363 	}
364 	return (WALK_NEXT);
365 }
366 
367 int
368 mi_payload_walk_step(mdb_walk_state_t *wsp)
369 {
370 	const mi_payload_walk_arg_t *arg = wsp->walk_arg;
371 	uintptr_t kaddr;
372 
373 	kaddr = wsp->walk_addr + arg->mi_pwa_head_off;
374 
375 	if (mdb_vread(&kaddr, sizeof (kaddr), kaddr) == -1) {
376 		mdb_warn("can't read address of mi head at %p for %s",
377 		    kaddr, arg->mi_pwa_walker);
378 		return (WALK_ERR);
379 	}
380 
381 	if (kaddr == 0) {
382 		/* Empty list */
383 		return (WALK_DONE);
384 	}
385 
386 	if (mdb_pwalk("genunix`mi", wsp->walk_callback,
387 	    wsp->walk_cbdata, kaddr) == -1) {
388 		mdb_warn("failed to walk genunix`mi");
389 		return (WALK_ERR);
390 	}
391 	return (WALK_NEXT);
392 }
393 
394 const mi_payload_walk_arg_t mi_ar_arg = {
395 	"ar_stacks", OFFSETOF(arp_stack_t, as_head), sizeof (ar_t),
396 	MI_PAYLOAD_DEVICE | MI_PAYLOAD_MODULE
397 };
398 
399 const mi_payload_walk_arg_t mi_icmp_arg = {
400 	"icmp_stacks", OFFSETOF(icmp_stack_t, is_head), sizeof (icmp_t),
401 	MI_PAYLOAD_DEVICE | MI_PAYLOAD_MODULE
402 };
403 
404 const mi_payload_walk_arg_t mi_ill_arg = {
405 	"ip_stacks", OFFSETOF(ip_stack_t, ips_ip_g_head), sizeof (ill_t),
406 	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, int af)
617 {
618 	const uintptr_t opts = (uintptr_t)cb_data;
619 	uintptr_t tcp_kaddr;
620 	conn_t conns, *connp;
621 	tcp_t tcps, *tcp;
622 
623 	if (mdb_vread(&conns, sizeof (conn_t), kaddr) == -1) {
624 		mdb_warn("failed to read conn_t at %p", kaddr);
625 		return (WALK_ERR);
626 	}
627 	connp = &conns;
628 
629 	tcp_kaddr = (uintptr_t)connp->conn_tcp;
630 	if (mdb_vread(&tcps, sizeof (tcp_t), tcp_kaddr) == -1) {
631 		mdb_warn("failed to read tcp_t at %p", kaddr);
632 		return (WALK_ERR);
633 	}
634 
635 	tcp = &tcps;
636 
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(" %4i", ns_to_stackid((uintptr_t)connp->conn_netstack));
657 
658 	mdb_printf(" %4i\n", connp->conn_zoneid);
659 
660 	if (opts & NETSTAT_VERBOSE)
661 		netstat_tcp_verbose_pr(tcp);
662 
663 	return (WALK_NEXT);
664 }
665 
666 static int
667 netstat_tcpv4_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
668 {
669 	return (netstat_tcp_cb(kaddr, walk_data, cb_data, AF_INET));
670 }
671 
672 static int
673 netstat_tcpv6_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
674 {
675 	return (netstat_tcp_cb(kaddr, walk_data, cb_data, AF_INET6));
676 }
677 
678 /*ARGSUSED*/
679 static int
680 netstat_udp_cb(uintptr_t kaddr, const void *walk_data, void *cb_data, int af)
681 {
682 	const uintptr_t opts = (uintptr_t)cb_data;
683 	udp_t udp;
684 	conn_t conns;
685 
686 	if (mdb_vread(&conns, sizeof (conn_t), kaddr) == -1) {
687 		mdb_warn("failed to read conn_t at %p", kaddr);
688 		return (WALK_ERR);
689 	}
690 
691 	if (mdb_vread(&udp, sizeof (udp_t),
692 	    (uintptr_t)conns.conn_udp) == -1) {
693 		mdb_warn("failed to read conn_udp at %p",
694 		    (uintptr_t)conns.conn_udp);
695 		return (WALK_ERR);
696 	}
697 
698 	if (!((opts & NETSTAT_ALL) || net_udp_active(&udp)) ||
699 	    (af == AF_INET && !net_udp_ipv4(&udp)) ||
700 	    (af == AF_INET6 && !net_udp_ipv6(&udp))) {
701 		return (WALK_NEXT);
702 	}
703 
704 	mdb_printf("%0?p %2i ", kaddr, udp.udp_state);
705 	if (af == AF_INET) {
706 		net_ipv4addrport_pr(&udp.udp_v6src, udp.udp_port);
707 		mdb_printf(" ");
708 		net_ipv4addrport_pr(&udp.udp_v6dst, udp.udp_dstport);
709 	} else if (af == AF_INET6) {
710 		net_ipv6addrport_pr(&udp.udp_v6src, udp.udp_port);
711 		mdb_printf(" ");
712 		net_ipv6addrport_pr(&udp.udp_v6dst, udp.udp_dstport);
713 	}
714 	mdb_printf(" %4i", ns_to_stackid((uintptr_t)conns.conn_netstack));
715 
716 	mdb_printf(" %4i\n", conns.conn_zoneid);
717 
718 	return (WALK_NEXT);
719 }
720 
721 static int
722 netstat_udpv4_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
723 {
724 	return (netstat_udp_cb(kaddr, walk_data, cb_data, AF_INET));
725 }
726 
727 static int
728 netstat_udpv6_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
729 {
730 	return (netstat_udp_cb(kaddr, walk_data, cb_data, AF_INET6));
731 }
732 
733 /*
734  * print the address of a unix domain socket
735  *
736  * so is the address of a AF_UNIX struct sonode in mdb's address space
737  * soa is the address of the struct soaddr to print
738  *
739  * returns 0 on success, -1 otherwise
740  */
741 static int
742 netstat_unix_name_pr(const struct sotpi_sonode *st, const struct soaddr *soa)
743 {
744 	const struct sonode *so = &st->st_sonode;
745 	const char none[] = " (none)";
746 
747 	if ((so->so_state & SS_ISBOUND) && (soa->soa_len != 0)) {
748 		if (st->st_info.sti_faddr_noxlate) {
749 			mdb_printf("%-14s ", " (socketpair)");
750 		} else {
751 			if (soa->soa_len > sizeof (sa_family_t)) {
752 				char addr[MAXPATHLEN + 1];
753 
754 				if (mdb_readstr(addr, sizeof (addr),
755 				    (uintptr_t)&soa->soa_sa->sa_data) == -1) {
756 					mdb_warn("failed to read unix address "
757 					    "at %p", &soa->soa_sa->sa_data);
758 					return (-1);
759 				}
760 
761 				mdb_printf("%-14s ", addr);
762 			} else {
763 				mdb_printf("%-14s ", none);
764 			}
765 		}
766 	} else {
767 		mdb_printf("%-14s ", none);
768 	}
769 
770 	return (0);
771 }
772 
773 /* based on sockfs_snapshot */
774 /*ARGSUSED*/
775 static int
776 netstat_unix_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
777 {
778 	const struct sotpi_sonode *st = walk_data;
779 	const struct sonode *so = &st->st_sonode;
780 	const struct sotpi_info *sti = &st->st_info;
781 
782 	if (so->so_count == 0)
783 		return (WALK_NEXT);
784 
785 	if (so->so_family != AF_UNIX) {
786 		mdb_warn("sonode of family %hi at %p\n", so->so_family, kaddr);
787 		return (WALK_ERR);
788 	}
789 
790 	mdb_printf("%-?p ", kaddr);
791 
792 	switch (sti->sti_serv_type) {
793 	case T_CLTS:
794 		mdb_printf("%-10s ", "dgram");
795 		break;
796 	case T_COTS:
797 		mdb_printf("%-10s ", "stream");
798 		break;
799 	case T_COTS_ORD:
800 		mdb_printf("%-10s ", "stream-ord");
801 		break;
802 	default:
803 		mdb_printf("%-10i ", sti->sti_serv_type);
804 	}
805 
806 	if ((so->so_state & SS_ISBOUND) &&
807 	    (sti->sti_ux_laddr.soua_magic == SOU_MAGIC_EXPLICIT)) {
808 		mdb_printf("%0?p ", sti->sti_ux_laddr.soua_vp);
809 	} else {
810 		mdb_printf("%0?p ", NULL);
811 	}
812 
813 	if ((so->so_state & SS_ISCONNECTED) &&
814 	    (sti->sti_ux_faddr.soua_magic == SOU_MAGIC_EXPLICIT)) {
815 		mdb_printf("%0?p ", sti->sti_ux_faddr.soua_vp);
816 	} else {
817 		mdb_printf("%0?p ", NULL);
818 	}
819 
820 	if (netstat_unix_name_pr(st, &sti->sti_laddr) == -1)
821 		return (WALK_ERR);
822 
823 	if (netstat_unix_name_pr(st, &sti->sti_faddr) == -1)
824 		return (WALK_ERR);
825 
826 	mdb_printf("%4i\n", so->so_zoneid);
827 
828 	return (WALK_NEXT);
829 }
830 
831 static void
832 netstat_tcp_verbose_header_pr(void)
833 {
834 	mdb_printf("       %<u>%-5s %-8s %-8s %-5s %-8s %-8s %5s %5s%</u>\n",
835 	    "Swind", "Snext", "Suna", "Rwind", "Rack", "Rnext", "Rto", "Mss");
836 }
837 
838 static void
839 get_ifname(const ire_t *ire, char *intf)
840 {
841 	ill_t ill;
842 
843 	*intf = '\0';
844 	if (ire->ire_type == IRE_CACHE) {
845 		queue_t stq;
846 
847 		if (mdb_vread(&stq, sizeof (stq), (uintptr_t)ire->ire_stq) ==
848 		    -1)
849 			return;
850 		if (mdb_vread(&ill, sizeof (ill), (uintptr_t)stq.q_ptr) == -1)
851 			return;
852 		(void) mdb_readstr(intf, MIN(LIFNAMSIZ, ill.ill_name_length),
853 		    (uintptr_t)ill.ill_name);
854 	} else if (ire->ire_ipif != NULL) {
855 		ipif_t ipif;
856 		char *cp;
857 
858 		if (mdb_vread(&ipif, sizeof (ipif),
859 		    (uintptr_t)ire->ire_ipif) == -1)
860 			return;
861 		if (mdb_vread(&ill, sizeof (ill), (uintptr_t)ipif.ipif_ill) ==
862 		    -1)
863 			return;
864 		(void) mdb_readstr(intf, MIN(LIFNAMSIZ, ill.ill_name_length),
865 		    (uintptr_t)ill.ill_name);
866 		if (ipif.ipif_id != 0) {
867 			cp = intf + strlen(intf);
868 			(void) mdb_snprintf(cp, LIFNAMSIZ + 1 - (cp - intf),
869 			    ":%u", ipif.ipif_id);
870 		}
871 	}
872 }
873 
874 static void
875 get_v4flags(const ire_t *ire, char *flags)
876 {
877 	(void) strcpy(flags, "U");
878 	if (ire->ire_type == IRE_DEFAULT || ire->ire_type == IRE_PREFIX ||
879 	    ire->ire_type == IRE_HOST || ire->ire_type == IRE_HOST_REDIRECT)
880 		(void) strcat(flags, "G");
881 	if (ire->ire_mask == IP_HOST_MASK)
882 		(void) strcat(flags, "H");
883 	if (ire->ire_type == IRE_HOST_REDIRECT)
884 		(void) strcat(flags, "D");
885 	if (ire->ire_type == IRE_CACHE)
886 		(void) strcat(flags, "A");
887 	if (ire->ire_type == IRE_BROADCAST)
888 		(void) strcat(flags, "B");
889 	if (ire->ire_type == IRE_LOCAL)
890 		(void) strcat(flags, "L");
891 	if (ire->ire_flags & RTF_MULTIRT)
892 		(void) strcat(flags, "M");
893 	if (ire->ire_flags & RTF_SETSRC)
894 		(void) strcat(flags, "S");
895 }
896 
897 static int
898 netstat_irev4_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
899 {
900 	const ire_t *ire = walk_data;
901 	uint_t *opts = cb_data;
902 	ipaddr_t gate;
903 	char flags[10], intf[LIFNAMSIZ + 1];
904 
905 	if (ire->ire_ipversion != IPV4_VERSION)
906 		return (WALK_NEXT);
907 
908 	if (!(*opts & NETSTAT_ALL) && (ire->ire_type == IRE_CACHE ||
909 	    ire->ire_type == IRE_BROADCAST || ire->ire_type == IRE_LOCAL))
910 		return (WALK_NEXT);
911 
912 	if (*opts & NETSTAT_FIRST) {
913 		*opts &= ~NETSTAT_FIRST;
914 		mdb_printf("%<u>%s Table: IPv4%</u>\n",
915 		    (*opts & NETSTAT_VERBOSE) ? "IRE" : "Routing");
916 		if (*opts & NETSTAT_VERBOSE) {
917 			mdb_printf("%<u>%-?s %-*s %-*s %-*s Device Mxfrg Rtt  "
918 			    " Ref Flg Out   In/Fwd%</u>\n",
919 			    "Address", ADDR_V4_WIDTH, "Destination",
920 			    ADDR_V4_WIDTH, "Mask", ADDR_V4_WIDTH, "Gateway");
921 		} else {
922 			mdb_printf("%<u>%-?s %-*s %-*s Flags Ref  Use   "
923 			    "Interface%</u>\n",
924 			    "Address", ADDR_V4_WIDTH, "Destination",
925 			    ADDR_V4_WIDTH, "Gateway");
926 		}
927 	}
928 
929 	gate = (ire->ire_type & (IRE_INTERFACE|IRE_LOOPBACK|IRE_BROADCAST)) ?
930 	    ire->ire_src_addr : ire->ire_gateway_addr;
931 
932 	get_v4flags(ire, flags);
933 
934 	get_ifname(ire, intf);
935 
936 	if (*opts & NETSTAT_VERBOSE) {
937 		mdb_printf("%?p %-*I %-*I %-*I %-6s %5u%c %4u %3u %-3s %5u "
938 		    "%u\n", kaddr, ADDR_V4_WIDTH, ire->ire_addr, ADDR_V4_WIDTH,
939 		    ire->ire_mask, ADDR_V4_WIDTH, gate, intf,
940 		    ire->ire_max_frag, ire->ire_frag_flag ? '*' : ' ',
941 		    ire->ire_uinfo.iulp_rtt, ire->ire_refcnt, flags,
942 		    ire->ire_ob_pkt_count, ire->ire_ib_pkt_count);
943 	} else {
944 		mdb_printf("%?p %-*I %-*I %-5s %4u %5u %s\n", kaddr,
945 		    ADDR_V4_WIDTH, ire->ire_addr, ADDR_V4_WIDTH, gate, flags,
946 		    ire->ire_refcnt,
947 		    ire->ire_ob_pkt_count + ire->ire_ib_pkt_count, intf);
948 	}
949 
950 	return (WALK_NEXT);
951 }
952 
953 int
954 ip_mask_to_plen_v6(const in6_addr_t *v6mask)
955 {
956 	int plen;
957 	int i;
958 	uint32_t val;
959 
960 	for (i = 3; i >= 0; i--)
961 		if (v6mask->s6_addr32[i] != 0)
962 			break;
963 	if (i < 0)
964 		return (0);
965 	plen = 32 + 32 * i;
966 	val = v6mask->s6_addr32[i];
967 	while (!(val & 1)) {
968 		val >>= 1;
969 		plen--;
970 	}
971 
972 	return (plen);
973 }
974 
975 static int
976 netstat_irev6_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
977 {
978 	const ire_t *ire = walk_data;
979 	uint_t *opts = cb_data;
980 	const in6_addr_t *gatep;
981 	char deststr[ADDR_V6_WIDTH + 5];
982 	char flags[10], intf[LIFNAMSIZ + 1];
983 	int masklen;
984 
985 	if (ire->ire_ipversion != IPV6_VERSION)
986 		return (WALK_NEXT);
987 
988 	if (!(*opts & NETSTAT_ALL) && ire->ire_type == IRE_CACHE)
989 		return (WALK_NEXT);
990 
991 	if (*opts & NETSTAT_FIRST) {
992 		*opts &= ~NETSTAT_FIRST;
993 		mdb_printf("\n%<u>%s Table: IPv6%</u>\n",
994 		    (*opts & NETSTAT_VERBOSE) ? "IRE" : "Routing");
995 		if (*opts & NETSTAT_VERBOSE) {
996 			mdb_printf("%<u>%-?s %-*s %-*s If    PMTU   Rtt   Ref "
997 			    "Flags Out    In/Fwd%</u>\n",
998 			    "Address", ADDR_V6_WIDTH+4, "Destination/Mask",
999 			    ADDR_V6_WIDTH, "Gateway");
1000 		} else {
1001 			mdb_printf("%<u>%-?s %-*s %-*s Flags Ref Use    If"
1002 			    "%</u>\n",
1003 			    "Address", ADDR_V6_WIDTH+4, "Destination/Mask",
1004 			    ADDR_V6_WIDTH, "Gateway");
1005 		}
1006 	}
1007 
1008 	gatep = (ire->ire_type & (IRE_INTERFACE|IRE_LOOPBACK)) ?
1009 	    &ire->ire_src_addr_v6 : &ire->ire_gateway_addr_v6;
1010 
1011 	masklen = ip_mask_to_plen_v6(&ire->ire_mask_v6);
1012 	(void) mdb_snprintf(deststr, sizeof (deststr), "%N/%d",
1013 	    &ire->ire_addr_v6, masklen);
1014 
1015 	(void) strcpy(flags, "U");
1016 	if (ire->ire_type == IRE_DEFAULT || ire->ire_type == IRE_PREFIX ||
1017 	    ire->ire_type == IRE_HOST || ire->ire_type == IRE_HOST_REDIRECT)
1018 		(void) strcat(flags, "G");
1019 	if (masklen == IPV6_ABITS)
1020 		(void) strcat(flags, "H");
1021 	if (ire->ire_type == IRE_HOST_REDIRECT)
1022 		(void) strcat(flags, "D");
1023 	if (ire->ire_type == IRE_CACHE)
1024 		(void) strcat(flags, "A");
1025 	if (ire->ire_type == IRE_LOCAL)
1026 		(void) strcat(flags, "L");
1027 	if (ire->ire_flags & RTF_MULTIRT)
1028 		(void) strcat(flags, "M");
1029 	if (ire->ire_flags & RTF_SETSRC)
1030 		(void) strcat(flags, "S");
1031 
1032 	get_ifname(ire, intf);
1033 
1034 	if (*opts & NETSTAT_VERBOSE) {
1035 		mdb_printf("%?p %-*s %-*N %-5s %5u%c %5u %3u %-5s %6u %u\n",
1036 		    kaddr, ADDR_V6_WIDTH+4, deststr, ADDR_V6_WIDTH, gatep,
1037 		    intf, ire->ire_max_frag, ire->ire_frag_flag ? '*' : ' ',
1038 		    ire->ire_uinfo.iulp_rtt, ire->ire_refcnt,
1039 		    flags, ire->ire_ob_pkt_count, ire->ire_ib_pkt_count);
1040 	} else {
1041 		mdb_printf("%?p %-*s %-*N %-5s %3u %6u %s\n", kaddr,
1042 		    ADDR_V6_WIDTH+4, deststr, ADDR_V6_WIDTH, gatep, flags,
1043 		    ire->ire_refcnt,
1044 		    ire->ire_ob_pkt_count + ire->ire_ib_pkt_count, intf);
1045 	}
1046 
1047 	return (WALK_NEXT);
1048 }
1049 
1050 /*ARGSUSED*/
1051 int
1052 netstat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1053 {
1054 	uint_t opts = 0;
1055 	const char *optf = NULL;
1056 	const char *optP = NULL;
1057 
1058 	if (mdb_getopts(argc, argv,
1059 	    'a', MDB_OPT_SETBITS, NETSTAT_ALL, &opts,
1060 	    'f', MDB_OPT_STR, &optf,
1061 	    'P', MDB_OPT_STR, &optP,
1062 	    'r', MDB_OPT_SETBITS, NETSTAT_ROUTE, &opts,
1063 	    'v', MDB_OPT_SETBITS, NETSTAT_VERBOSE, &opts,
1064 	    NULL) != argc)
1065 		return (DCMD_USAGE);
1066 
1067 	if (optP != NULL) {
1068 		if ((strcmp("tcp", optP) != 0) && (strcmp("udp", optP) != 0))
1069 			return (DCMD_USAGE);
1070 		if (opts & NETSTAT_ROUTE)
1071 			return (DCMD_USAGE);
1072 	}
1073 
1074 	if (optf == NULL)
1075 		opts |= NETSTAT_V4 | NETSTAT_V6 | NETSTAT_UNIX;
1076 	else if (strcmp("inet", optf) == 0)
1077 		opts |= NETSTAT_V4;
1078 	else if (strcmp("inet6", optf) == 0)
1079 		opts |= NETSTAT_V6;
1080 	else if (strcmp("unix", optf) == 0)
1081 		opts |= NETSTAT_UNIX;
1082 	else
1083 		return (DCMD_USAGE);
1084 
1085 	if (opts & NETSTAT_ROUTE) {
1086 		if (!(opts & (NETSTAT_V4|NETSTAT_V6)))
1087 			return (DCMD_USAGE);
1088 		if (opts & NETSTAT_V4) {
1089 			opts |= NETSTAT_FIRST;
1090 			if (mdb_walk("ip`ire", netstat_irev4_cb, &opts) == -1) {
1091 				mdb_warn("failed to walk ip`ire");
1092 				return (DCMD_ERR);
1093 			}
1094 		}
1095 		if (opts & NETSTAT_V6) {
1096 			opts |= NETSTAT_FIRST;
1097 			if (mdb_walk("ip`ire", netstat_irev6_cb, &opts) == -1) {
1098 				mdb_warn("failed to walk ip`ire");
1099 				return (DCMD_ERR);
1100 			}
1101 		}
1102 		return (DCMD_OK);
1103 	}
1104 
1105 	if ((optP == NULL) || (strcmp("tcp", optP) == 0)) {
1106 		if ((optf == NULL) || (strcmp("inet", optf) == 0)) {
1107 			/* Print TCPv4 connection */
1108 			mdb_printf("%<u>%-?s St %*s       %*s       "
1109 			    "%s%       %s%</u>\n",
1110 			    "TCPv4", ADDR_V4_WIDTH, "Local Address",
1111 			    ADDR_V4_WIDTH, "Remote Address", "Stack", "Zone");
1112 
1113 			if (opts & NETSTAT_VERBOSE)
1114 				netstat_tcp_verbose_header_pr();
1115 
1116 			if (mdb_walk("tcp_conn_cache", netstat_tcpv4_cb,
1117 			    (void *)(uintptr_t)opts) == -1) {
1118 				mdb_warn("failed to walk tcp_conn_cache");
1119 				return (DCMD_ERR);
1120 			}
1121 		}
1122 
1123 		if ((optf == NULL) || (strcmp("inet6", optf) == 0)) {
1124 			/* Print TCPv6 connection */
1125 			mdb_printf("%<u>%-?s St %*s       %*s       "
1126 			    "%s       %s%\n%</u>",
1127 			    "TCPv6", ADDR_V6_WIDTH, "Local Address",
1128 			    ADDR_V6_WIDTH, "Remote Address", "Stack", "Zone");
1129 
1130 			if (opts & NETSTAT_VERBOSE)
1131 				netstat_tcp_verbose_header_pr();
1132 
1133 			if (mdb_walk("tcp_conn_cache", netstat_tcpv6_cb,
1134 			    (void *)(uintptr_t)opts) == -1) {
1135 				mdb_warn("failed to walk tcp_conn_cache");
1136 				return (DCMD_ERR);
1137 			}
1138 		}
1139 	}
1140 
1141 	if ((optP == NULL) || (strcmp("udp", optP) == 0)) {
1142 		if ((optf == NULL) || (strcmp("inet", optf) == 0)) {
1143 			/* Print UDPv4 connection */
1144 			mdb_printf("%<u>%-?s St %*s       %*s       "
1145 			    "%s       %s%\n%</u>",
1146 			    "UDPv4", ADDR_V4_WIDTH, "Local Address",
1147 			    ADDR_V4_WIDTH, "Remote Address", "Stack", "Zone");
1148 
1149 			if (mdb_walk("udp_conn_cache", netstat_udpv4_cb,
1150 			    (void *)(uintptr_t)opts) == -1) {
1151 				mdb_warn("failed to walk udp_conn_cache");
1152 				return (DCMD_ERR);
1153 			}
1154 
1155 		}
1156 
1157 		if ((optf == NULL) || (strcmp("inet6", optf) == 0)) {
1158 			/* Print UDPv6 connection */
1159 			mdb_printf("%<u>%-?s St %*s       %*s       "
1160 			    "%s       %s%\n%</u>",
1161 			    "UDPv6", ADDR_V6_WIDTH, "Local Address",
1162 			    ADDR_V6_WIDTH, "Remote Address", "Stack", "Zone");
1163 
1164 			if (mdb_walk("udp_conn_cache", netstat_udpv6_cb,
1165 			    (void *)(uintptr_t)opts) == -1) {
1166 				mdb_warn("failed to walk udp_conn_cache");
1167 				return (DCMD_ERR);
1168 			}
1169 		}
1170 	}
1171 
1172 	if (((optf == NULL) || (strcmp("unix", optf) == 0)) && (optP == NULL)) {
1173 		/* Print Unix Domain Sockets */
1174 		mdb_printf("%<u>%-?s %-10s %-?s %-?s %-14s %-14s %s%</u>\n",
1175 		    "AF_UNIX", "Type", "Vnode", "Conn", "Local Addr",
1176 		    "Remote Addr", "Zone");
1177 
1178 		if (mdb_walk("genunix`sonode", netstat_unix_cb, NULL) == -1) {
1179 			mdb_warn("failed to walk genunix`sonode");
1180 			return (DCMD_ERR);
1181 		}
1182 	}
1183 
1184 	return (DCMD_OK);
1185 }
1186