net.c revision 8348:4137e18bfaf0
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 */
67int
68ar_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
77int
78ar_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
91int
92icmp_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
101int
102icmp_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
115int
116tcp_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
125int
126tcp_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
139int
140udp_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
149int
150udp_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 */
167static void
168net_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 */
180static void
181net_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
187static int
188net_tcp_active(const tcp_t *tcp)
189{
190	return (tcp->tcp_state >= TCPS_ESTABLISHED);
191}
192
193static int
194net_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
201static int
202net_tcp_ipv6(const tcp_t *tcp)
203{
204	return (tcp->tcp_ipversion == IPV6_VERSION);
205}
206
207static int
208net_udp_active(const udp_t *udp)
209{
210	return ((udp->udp_state == TS_IDLE) ||
211	    (udp->udp_state == TS_DATA_XFER));
212}
213
214static int
215net_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
222static int
223net_udp_ipv6(const udp_t *udp)
224{
225	return (udp->udp_ipversion == IPV6_VERSION);
226}
227
228int
229sonode_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
254int
255sonode_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
278void
279sonode_walk_fini(mdb_walk_state_t *wsp)
280{
281	mdb_free(wsp->walk_data, sizeof (struct sotpi_sonode));
282}
283
284struct mi_walk_data {
285	uintptr_t mi_wd_miofirst;
286	MI_O mi_wd_miodata;
287};
288
289int
290mi_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
308int
309mi_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
339void
340mi_walk_fini(mdb_walk_state_t *wsp)
341{
342	mdb_free(wsp->walk_data, sizeof (struct mi_walk_data));
343}
344
345typedef 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
355int
356mi_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
367int
368mi_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
394const 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
399const 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
404const 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
409int
410sonode(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
530int
531mi(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
592static int
593ns_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
606static void
607netstat_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*/
615static int
616netstat_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
666static int
667netstat_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
672static int
673netstat_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*/
679static int
680netstat_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
721static int
722netstat_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
727static int
728netstat_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 */
741static int
742netstat_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*/
775static int
776netstat_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
831static void
832netstat_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
838static void
839get_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
874static void
875get_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
897static int
898netstat_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
953int
954ip_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
975static int
976netstat_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*/
1051int
1052netstat(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