xref: /illumos-gate/usr/src/cmd/mdb/common/modules/genunix/net.c (revision bdfc6d18da790deeec2e0eb09c625902defe2498)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <mdb/mdb_modapi.h>
30 #include <mdb/mdb_ks.h>
31 #include <mdb/mdb_ctf.h>
32 #include <sys/types.h>
33 #include <sys/tihdr.h>
34 #include <inet/led.h>
35 #include <inet/common.h>
36 #include <netinet/in.h>
37 #include <netinet/ip6.h>
38 #include <netinet/icmp6.h>
39 #include <inet/ip.h>
40 #include <inet/ip6.h>
41 #include <inet/ipclassifier.h>
42 #include <inet/tcp.h>
43 #include <sys/stream.h>
44 #include <sys/vfs.h>
45 #include <sys/stropts.h>
46 #include <sys/tpicommon.h>
47 #include <sys/socket.h>
48 #include <sys/socketvar.h>
49 #include <sys/cred_impl.h>
50 #include <inet/udp_impl.h>
51 #include <inet/arp_impl.h>
52 #include <inet/rawip_impl.h>
53 #include <inet/mi.h>
54 
55 #define	MIH2MIO(mihp) (&(mihp)->mh_o)
56 
57 #define	ADDR_V6_WIDTH	23
58 #define	ADDR_V4_WIDTH	15
59 
60 #define	NETSTAT_ALL	0x1
61 #define	NETSTAT_VERBOSE	0x2
62 
63 /*
64  * Print an IPv4 address and port number in a compact and easy to read format
65  * The arguments are in network byte order
66  */
67 static void
68 net_ipv4addrport_pr(const in6_addr_t *nipv6addr, in_port_t nport)
69 {
70 	uint32_t naddr = V4_PART_OF_V6((*nipv6addr));
71 
72 	mdb_nhconvert(&nport, &nport, sizeof (nport));
73 	mdb_printf("%*I.%-5hu", ADDR_V4_WIDTH, naddr, nport);
74 }
75 
76 /*
77  * Print an IPv6 address and port number in a compact and easy to read format
78  * The arguments are in network byte order
79  */
80 static void
81 net_ipv6addrport_pr(const in6_addr_t *naddr, in_port_t nport)
82 {
83 	mdb_nhconvert(&nport, &nport, sizeof (nport));
84 	mdb_printf("%*N.%-5hu", ADDR_V6_WIDTH, naddr, nport);
85 }
86 
87 static int
88 net_tcp_active(const tcp_t *tcp)
89 {
90 	return (tcp->tcp_state >= TCPS_ESTABLISHED);
91 }
92 
93 static int
94 net_tcp_ipv4(const tcp_t *tcp)
95 {
96 	return ((tcp->tcp_ipversion == IPV4_VERSION) ||
97 	    (IN6_IS_ADDR_UNSPECIFIED(&tcp->tcp_ip_src_v6) &&
98 	    (tcp->tcp_state <= TCPS_LISTEN)));
99 }
100 
101 static int
102 net_tcp_ipv6(const tcp_t *tcp)
103 {
104 	return (tcp->tcp_ipversion == IPV6_VERSION);
105 }
106 
107 static int
108 net_udp_active(const udp_t *udp)
109 {
110 	return ((udp->udp_state != TS_UNBND) && (udp->udp_state != TS_IDLE));
111 }
112 
113 static int
114 net_udp_ipv4(const udp_t *udp)
115 {
116 	return ((udp->udp_ipversion == IPV4_VERSION) ||
117 	    (IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src) &&
118 	    (udp->udp_state <= TS_IDLE)));
119 }
120 
121 static int
122 net_udp_ipv6(const udp_t *udp)
123 {
124 	return (udp->udp_ipversion == IPV6_VERSION);
125 }
126 
127 int
128 sonode_walk_init(mdb_walk_state_t *wsp)
129 {
130 	if (wsp->walk_addr == NULL) {
131 		GElf_Sym sym;
132 		struct socklist *slp;
133 
134 		if (mdb_lookup_by_obj("sockfs", "socklist", &sym) == -1) {
135 			mdb_warn("failed to lookup sockfs`socklist");
136 			return (WALK_ERR);
137 		}
138 
139 		slp = (struct socklist *)(uintptr_t)sym.st_value;
140 
141 		if (mdb_vread(&wsp->walk_addr, sizeof (wsp->walk_addr),
142 		    (uintptr_t)&slp->sl_list) == -1) {
143 			mdb_warn("failed to read address of initial sonode "
144 			    "at %p", &slp->sl_list);
145 			return (WALK_ERR);
146 		}
147 	}
148 
149 	wsp->walk_data = mdb_alloc(sizeof (struct sonode), UM_SLEEP);
150 	return (WALK_NEXT);
151 }
152 
153 int
154 sonode_walk_step(mdb_walk_state_t *wsp)
155 {
156 	int status;
157 	struct sonode *sonodep;
158 
159 	if (wsp->walk_addr == NULL)
160 		return (WALK_DONE);
161 
162 	if (mdb_vread(wsp->walk_data, sizeof (struct sonode),
163 	    wsp->walk_addr) == -1) {
164 		mdb_warn("failed to read sonode at %p", wsp->walk_addr);
165 		return (WALK_ERR);
166 	}
167 
168 	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
169 	    wsp->walk_cbdata);
170 
171 	sonodep = wsp->walk_data;
172 
173 	wsp->walk_addr = (uintptr_t)sonodep->so_next;
174 	return (status);
175 }
176 
177 void
178 sonode_walk_fini(mdb_walk_state_t *wsp)
179 {
180 	mdb_free(wsp->walk_data, sizeof (struct sonode));
181 }
182 
183 struct mi_walk_data {
184 	uintptr_t mi_wd_miofirst;
185 	MI_O mi_wd_miodata;
186 };
187 
188 int
189 mi_walk_init(mdb_walk_state_t *wsp)
190 {
191 	struct mi_walk_data *wdp;
192 
193 	if (wsp->walk_addr == NULL) {
194 		mdb_warn("mi doesn't support global walks\n");
195 		return (WALK_ERR);
196 	}
197 
198 	wdp = mdb_alloc(sizeof (struct mi_walk_data), UM_SLEEP);
199 
200 	/* So that we do not immediately return WALK_DONE below */
201 	wdp->mi_wd_miofirst = NULL;
202 
203 	wsp->walk_data = wdp;
204 	return (WALK_NEXT);
205 }
206 
207 int
208 mi_walk_step(mdb_walk_state_t *wsp)
209 {
210 	struct mi_walk_data *wdp = wsp->walk_data;
211 	MI_OP miop = &wdp->mi_wd_miodata;
212 	int status;
213 
214 	/* Always false in the first iteration */
215 	if ((wsp->walk_addr == (uintptr_t)NULL) ||
216 	    (wsp->walk_addr == wdp->mi_wd_miofirst)) {
217 		return (WALK_DONE);
218 	}
219 
220 	if (mdb_vread(miop, sizeof (MI_O), wsp->walk_addr) == -1) {
221 		mdb_warn("failed to read MI object at %p", wsp->walk_addr);
222 		return (WALK_ERR);
223 	}
224 
225 	status = wsp->walk_callback(wsp->walk_addr, miop, wsp->walk_cbdata);
226 
227 	/* Only true in the first iteration */
228 	if (wdp->mi_wd_miofirst == NULL)
229 		wdp->mi_wd_miofirst = wsp->walk_addr;
230 
231 	wsp->walk_addr = (uintptr_t)miop->mi_o_next;
232 	return (status);
233 }
234 
235 void
236 mi_walk_fini(mdb_walk_state_t *wsp)
237 {
238 	mdb_free(wsp->walk_data, sizeof (struct mi_walk_data));
239 }
240 
241 typedef struct mi_payload_walk_data_s {
242 	uintptr_t mi_pwd_first;
243 	void *mi_pwd_data;
244 } mi_payload_walk_data_t;
245 
246 static void
247 delete_mi_payload_walk_data(mi_payload_walk_data_t *pwdp, size_t payload_size)
248 {
249 	mdb_free(pwdp->mi_pwd_data, payload_size);
250 	mdb_free(pwdp, sizeof (mi_payload_walk_data_t));
251 }
252 
253 typedef struct mi_payload_walk_arg_s {
254 	const char *mi_pwa_obj;		/* load object of mi_o_head_t * */
255 	const char *mi_pwa_sym;		/* symbol name of mi_o_head_t * */
256 	const size_t mi_pwa_size;	/* size of mi payload */
257 	const uint_t mi_pwa_flags;	/* device and/or module */
258 } mi_payload_walk_arg_t;
259 
260 #define	MI_PAYLOAD_DEVICE	0x1
261 #define	MI_PAYLOAD_MODULE	0x2
262 
263 int
264 mi_payload_walk_init(mdb_walk_state_t *wsp)
265 {
266 	const mi_payload_walk_arg_t *arg = wsp->walk_arg;
267 	mi_payload_walk_data_t *pwdp;
268 	GElf_Sym sym;
269 	mi_head_t *mihp;
270 
271 	/* Determine the address to start or end the walk with */
272 	if (mdb_lookup_by_obj(arg->mi_pwa_obj, arg->mi_pwa_sym, &sym) == -1) {
273 		mdb_warn("failed to lookup %s`%s",
274 		    arg->mi_pwa_obj, arg->mi_pwa_sym);
275 		return (WALK_ERR);
276 	}
277 
278 	if (mdb_vread(&mihp, sizeof (mihp), (uintptr_t)sym.st_value) == -1) {
279 		mdb_warn("failed to read address of global MI Head "
280 		    "mi_o_head_t at %p", (uintptr_t)sym.st_value);
281 		return (WALK_ERR);
282 	}
283 
284 	pwdp = mdb_alloc(sizeof (mi_payload_walk_data_t), UM_SLEEP);
285 	pwdp->mi_pwd_data = mdb_alloc(arg->mi_pwa_size, UM_SLEEP);
286 	wsp->walk_data = pwdp;
287 
288 	if (wsp->walk_addr == NULL) {
289 		/* Do not immediately return WALK_DONE below */
290 		pwdp->mi_pwd_first = NULL;
291 		/* We determined where to begin */
292 		wsp->walk_addr = (uintptr_t)MIH2MIO(mihp);
293 	} else {
294 		/* Do not cycle through all of the MI_O objects */
295 		pwdp->mi_pwd_first = (uintptr_t)MIH2MIO(mihp);
296 		/* We were given where to begin */
297 		wsp->walk_addr = (uintptr_t)((MI_OP)wsp->walk_addr - 1);
298 	}
299 
300 	if (mdb_layered_walk("genunix`mi", wsp) == -1) {
301 		mdb_warn("failed to walk genunix`mi");
302 		delete_mi_payload_walk_data(pwdp, arg->mi_pwa_size);
303 		return (WALK_ERR);
304 	}
305 
306 	return (WALK_NEXT);
307 }
308 
309 int
310 mi_payload_walk_step(mdb_walk_state_t *wsp)
311 {
312 	const mi_payload_walk_arg_t *arg = wsp->walk_arg;
313 	mi_payload_walk_data_t *pwdp = wsp->walk_data;
314 	void *payload = pwdp->mi_pwd_data;
315 	uintptr_t payload_kaddr = (uintptr_t)((MI_OP)wsp->walk_addr + 1);
316 	const MI_O *mio = wsp->walk_layer;
317 
318 	/* If this is a local walk, prevent cycling */
319 	if (wsp->walk_addr == pwdp->mi_pwd_first)
320 		return (WALK_DONE);
321 
322 	/*
323 	 * This was a global walk, prevent reading this payload as the
324 	 * initial MI_O is the head of the list and is not the header
325 	 * to a valid payload
326 	 */
327 	if (pwdp->mi_pwd_first == NULL) {
328 		pwdp->mi_pwd_first = wsp->walk_addr;
329 		return (WALK_NEXT);
330 	}
331 
332 	if (mio->mi_o_isdev == B_FALSE) {
333 		/* mio is a module */
334 		if (!(arg->mi_pwa_flags & MI_PAYLOAD_MODULE))
335 			return (WALK_NEXT);
336 	} else {
337 		/* mio is a device */
338 		if (!(arg->mi_pwa_flags & MI_PAYLOAD_DEVICE))
339 			return (WALK_NEXT);
340 	}
341 
342 	if (mdb_vread(payload, arg->mi_pwa_size, payload_kaddr) == -1) {
343 		mdb_warn("failed to read payload at %p", payload_kaddr);
344 		return (WALK_ERR);
345 	}
346 
347 	return (wsp->walk_callback(payload_kaddr, payload, wsp->walk_cbdata));
348 }
349 
350 void
351 mi_payload_walk_fini(mdb_walk_state_t *wsp)
352 {
353 	const mi_payload_walk_arg_t *arg = wsp->walk_arg;
354 
355 	delete_mi_payload_walk_data(wsp->walk_data, arg->mi_pwa_size);
356 }
357 
358 const mi_payload_walk_arg_t mi_udp_arg = {
359 	"udp", "udp_g_head", sizeof (udp_t),
360 	MI_PAYLOAD_DEVICE | MI_PAYLOAD_MODULE
361 };
362 
363 const mi_payload_walk_arg_t mi_ar_arg = {
364 	"arp", "ar_g_head", sizeof (ar_t),
365 	MI_PAYLOAD_DEVICE | MI_PAYLOAD_MODULE
366 };
367 
368 const mi_payload_walk_arg_t mi_icmp_arg = {
369 	"icmp", "icmp_g_head", sizeof (icmp_t),
370 	MI_PAYLOAD_DEVICE | MI_PAYLOAD_MODULE
371 };
372 
373 const mi_payload_walk_arg_t mi_ill_arg =
374 	{ "ip", "ip_g_head", sizeof (ill_t), MI_PAYLOAD_MODULE };
375 
376 int
377 sonode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
378 {
379 	const char *optf = NULL;
380 	const char *optt = NULL;
381 	const char *optp = NULL;
382 	int family, type, proto;
383 	int filter = 0;
384 	struct sonode so;
385 
386 	if (!(flags & DCMD_ADDRSPEC)) {
387 		if (mdb_walk_dcmd("genunix`sonode", "genunix`sonode", argc,
388 		    argv) == -1) {
389 			mdb_warn("failed to walk sonode");
390 			return (DCMD_ERR);
391 		}
392 
393 		return (DCMD_OK);
394 	}
395 
396 	if (mdb_getopts(argc, argv,
397 	    'f', MDB_OPT_STR, &optf,
398 	    't', MDB_OPT_STR, &optt,
399 	    'p', MDB_OPT_STR, &optp,
400 	    NULL) != argc)
401 		return (DCMD_USAGE);
402 
403 	if (optf != NULL) {
404 		if (strcmp("inet", optf) == 0)
405 			family = AF_INET;
406 		else if (strcmp("inet6", optf) == 0)
407 			family = AF_INET6;
408 		else if (strcmp("unix", optf) == 0)
409 			family = AF_UNIX;
410 		else
411 			family = mdb_strtoull(optf);
412 		filter = 1;
413 	}
414 
415 	if (optt != NULL) {
416 		if (strcmp("stream", optt) == 0)
417 			type = SOCK_STREAM;
418 		else if (strcmp("dgram", optt) == 0)
419 			type = SOCK_DGRAM;
420 		else if (strcmp("raw", optt) == 0)
421 			type = SOCK_RAW;
422 		else
423 			type = mdb_strtoull(optt);
424 		filter = 1;
425 	}
426 
427 	if (optp != NULL) {
428 		proto = mdb_strtoull(optp);
429 		filter = 1;
430 	}
431 
432 	if (DCMD_HDRSPEC(flags) && !filter) {
433 		mdb_printf("%<u>%-?s Family Type Proto State Mode Flag "
434 		    "AccessVP%</u>\n", "Sonode:");
435 	}
436 
437 	if (mdb_vread(&so, sizeof (so), addr) == -1) {
438 		mdb_warn("failed to read sonode at %p", addr);
439 		return (DCMD_ERR);
440 	}
441 
442 	if ((optf != NULL) && (so.so_family != family))
443 		return (DCMD_OK);
444 
445 	if ((optt != NULL) && (so.so_type != type))
446 		return (DCMD_OK);
447 
448 	if ((optp != NULL) && (so.so_protocol != proto))
449 		return (DCMD_OK);
450 
451 	if (filter) {
452 		mdb_printf("%0?p\n", addr);
453 		return (DCMD_OK);
454 	}
455 
456 	mdb_printf("%0?p ", addr);
457 
458 	switch (so.so_family) {
459 	    case AF_UNIX:
460 		mdb_printf("unix  ");
461 		break;
462 	    case AF_INET:
463 		mdb_printf("inet  ");
464 		break;
465 	    case AF_INET6:
466 		mdb_printf("inet6 ");
467 		break;
468 	    default:
469 		mdb_printf("%6hi", so.so_family);
470 	}
471 
472 	switch (so.so_type) {
473 	    case SOCK_STREAM:
474 		mdb_printf(" strm");
475 		break;
476 	    case SOCK_DGRAM:
477 		mdb_printf(" dgrm");
478 		break;
479 	    case SOCK_RAW:
480 		mdb_printf(" raw ");
481 		break;
482 	    default:
483 		mdb_printf(" %4hi", so.so_type);
484 	}
485 
486 	mdb_printf(" %5hi %05x %04x %04hx %0?p\n",
487 	    so.so_protocol, so.so_state, so.so_mode,
488 	    so.so_flag, so.so_accessvp);
489 
490 	return (DCMD_OK);
491 }
492 
493 #define	MI_PAYLOAD	0x1
494 #define	MI_DEVICE	0x2
495 #define	MI_MODULE	0x4
496 
497 int
498 mi(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
499 {
500 	uint_t opts = 0;
501 	MI_O	mio;
502 
503 	if (!(flags & DCMD_ADDRSPEC))
504 		return (DCMD_USAGE);
505 
506 	if (mdb_getopts(argc, argv,
507 	    'p', MDB_OPT_SETBITS, MI_PAYLOAD, &opts,
508 	    'd', MDB_OPT_SETBITS, MI_DEVICE, &opts,
509 	    'm', MDB_OPT_SETBITS, MI_MODULE, &opts,
510 	    NULL) != argc)
511 		return (DCMD_USAGE);
512 
513 	if ((opts & (MI_DEVICE | MI_MODULE)) == (MI_DEVICE | MI_MODULE)) {
514 		mdb_warn("at most one filter, d for devices or m "
515 		    "for modules, may be specified\n");
516 		return (DCMD_USAGE);
517 	}
518 
519 	if ((opts == 0) && (DCMD_HDRSPEC(flags))) {
520 		mdb_printf("%<u>%-?s %-?s %-?s IsDev Dev%</u>\n",
521 		    "MI_O", "Next", "Prev");
522 	}
523 
524 	if (mdb_vread(&mio, sizeof (mio), addr) == -1) {
525 		mdb_warn("failed to read mi object MI_O at %p", addr);
526 		return (DCMD_ERR);
527 	}
528 
529 	if (opts != 0) {
530 		if (mio.mi_o_isdev == B_FALSE) {
531 			/* mio is a module */
532 			if (!(opts & MI_MODULE) && (opts & MI_DEVICE))
533 				return (DCMD_OK);
534 		} else {
535 			/* mio is a device */
536 			if (!(opts & MI_DEVICE) && (opts & MI_MODULE))
537 				return (DCMD_OK);
538 		}
539 
540 		if (opts & MI_PAYLOAD)
541 			mdb_printf("%p\n", addr + sizeof (MI_O));
542 		else
543 			mdb_printf("%p\n", addr);
544 		return (DCMD_OK);
545 	}
546 
547 	mdb_printf("%0?p %0?p %0?p ", addr, mio.mi_o_next, mio.mi_o_prev);
548 
549 	if (mio.mi_o_isdev == B_FALSE)
550 		mdb_printf("FALSE");
551 	else
552 		mdb_printf("TRUE ");
553 
554 	mdb_printf(" %0?p\n", mio.mi_o_dev);
555 
556 	return (DCMD_OK);
557 }
558 
559 static void
560 netstat_tcp_verbose_pr(const tcp_t *tcp)
561 {
562 	mdb_printf("       %5i %08x %08x %5i %08x %08x %5li %5i\n",
563 	    tcp->tcp_swnd, tcp->tcp_snxt, tcp->tcp_suna, tcp->tcp_rwnd,
564 	    tcp->tcp_rack, tcp->tcp_rnxt, tcp->tcp_rto, tcp->tcp_mss);
565 }
566 
567 /*ARGSUSED*/
568 static int
569 netstat_tcp_cb(uintptr_t kaddr, const void *walk_data, void *cb_data, int af)
570 {
571 	const uintptr_t opts = (uintptr_t)cb_data;
572 	static size_t itc_size = 0;
573 	uintptr_t tcp_kaddr;
574 	conn_t *connp;
575 	tcp_t *tcp;
576 
577 	if (itc_size == 0) {
578 		mdb_ctf_id_t id;
579 
580 		if (mdb_ctf_lookup_by_name("itc_t", &id) != 0) {
581 			mdb_warn("failed to lookup type 'itc_t'");
582 			return (WALK_ERR);
583 		}
584 		itc_size = mdb_ctf_type_size(id);
585 	}
586 
587 	connp = (conn_t *)mdb_alloc(itc_size, UM_SLEEP | UM_GC);
588 
589 	if (mdb_vread(connp, itc_size, kaddr) == -1) {
590 		mdb_warn("failed to read connection info at %p", kaddr);
591 		return (WALK_ERR);
592 	}
593 
594 	tcp_kaddr = (uintptr_t)connp->conn_tcp;
595 	tcp = (tcp_t *)((uintptr_t)connp + (tcp_kaddr - kaddr));
596 
597 	if ((uintptr_t)tcp < (uintptr_t)connp ||
598 	    (uintptr_t)&tcp->tcp_connp > (uintptr_t)connp + itc_size ||
599 	    (uintptr_t)tcp->tcp_connp != kaddr) {
600 		mdb_warn("conn_tcp %p is invalid", tcp_kaddr);
601 		return (WALK_NEXT);
602 	}
603 	connp->conn_tcp = tcp;
604 	tcp->tcp_connp = connp;
605 
606 	if (!(opts & NETSTAT_ALL || net_tcp_active(tcp)) ||
607 	    (af == AF_INET && !net_tcp_ipv4(tcp)) ||
608 	    (af == AF_INET6 && !net_tcp_ipv6(tcp))) {
609 		return (WALK_NEXT);
610 	}
611 
612 	mdb_printf("%0?p %2i ", tcp_kaddr, tcp->tcp_state);
613 	if (af == AF_INET) {
614 		net_ipv4addrport_pr(&tcp->tcp_ip_src_v6, tcp->tcp_lport);
615 		mdb_printf(" ");
616 		net_ipv4addrport_pr(&tcp->tcp_remote_v6, tcp->tcp_fport);
617 	} else if (af == AF_INET6) {
618 		net_ipv6addrport_pr(&tcp->tcp_ip_src_v6, tcp->tcp_lport);
619 		mdb_printf(" ");
620 		net_ipv6addrport_pr(&tcp->tcp_remote_v6, tcp->tcp_fport);
621 	}
622 	mdb_printf(" %4i\n", connp->conn_zoneid);
623 
624 	if (opts & NETSTAT_VERBOSE)
625 		netstat_tcp_verbose_pr(tcp);
626 
627 	return (WALK_NEXT);
628 }
629 
630 static int
631 netstat_tcpv4_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
632 {
633 	return (netstat_tcp_cb(kaddr, walk_data, cb_data, AF_INET));
634 }
635 
636 static int
637 netstat_tcpv6_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
638 {
639 	return (netstat_tcp_cb(kaddr, walk_data, cb_data, AF_INET6));
640 }
641 
642 static int
643 netstat_udpv4_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
644 {
645 	const udp_t *udp = walk_data;
646 	const uintptr_t opts = (uintptr_t)cb_data;
647 
648 	if (!((opts & NETSTAT_ALL || net_udp_active(udp)) && net_udp_ipv4(udp)))
649 		return (WALK_NEXT);
650 
651 	mdb_printf("%0?p %2i ", kaddr, udp->udp_state);
652 	net_ipv4addrport_pr(&udp->udp_v6src, udp->udp_port);
653 	mdb_printf(" ");
654 	net_ipv4addrport_pr(&udp->udp_v6dst, udp->udp_dstport);
655 	mdb_printf(" %4i\n", udp->udp_zoneid);
656 
657 	return (WALK_NEXT);
658 }
659 
660 static int
661 netstat_udpv6_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
662 {
663 	const udp_t *udp = walk_data;
664 	const uintptr_t opts = (uintptr_t)cb_data;
665 
666 	if (!((opts & NETSTAT_ALL || net_udp_active(udp)) && net_udp_ipv6(udp)))
667 		return (WALK_NEXT);
668 
669 	mdb_printf("%0?p %2i ", kaddr, udp->udp_state);
670 	net_ipv6addrport_pr(&udp->udp_v6src, udp->udp_port);
671 	mdb_printf(" ");
672 
673 	/* Remote */
674 	if (udp->udp_state == TS_DATA_XFER)
675 		net_ipv6addrport_pr(&udp->udp_v6dst, udp->udp_dstport);
676 	else
677 		mdb_printf("%*s.0    ", ADDR_V6_WIDTH, "0:0:0:0:0:0:0:0");
678 	mdb_printf(" %4i\n", udp->udp_zoneid);
679 
680 	return (WALK_NEXT);
681 }
682 
683 /*
684  * print the address of a unix domain socket
685  *
686  * so is the address of a AF_UNIX struct sonode in mdb's address space
687  * soa is the address of the struct soaddr to print
688  *
689  * returns 0 on success, -1 otherwise
690  */
691 static int
692 netstat_unix_name_pr(const struct sonode *so, const struct soaddr *soa)
693 {
694 	const char none[] = " (none)";
695 
696 	if ((so->so_state & SS_ISBOUND) && (soa->soa_len != 0)) {
697 		if (so->so_state & SS_FADDR_NOXLATE) {
698 			mdb_printf("%-14s ", " (socketpair)");
699 		} else {
700 			if (soa->soa_len > sizeof (sa_family_t)) {
701 				char addr[MAXPATHLEN + 1];
702 
703 				if (mdb_readstr(addr, sizeof (addr),
704 				    (uintptr_t)&soa->soa_sa->sa_data) == -1) {
705 					mdb_warn("failed to read unix address "
706 					    "at %p", &soa->soa_sa->sa_data);
707 					return (-1);
708 				}
709 
710 				mdb_printf("%-14s ", addr);
711 			} else {
712 				mdb_printf("%-14s ", none);
713 			}
714 		}
715 	} else {
716 		mdb_printf("%-14s ", none);
717 	}
718 
719 	return (0);
720 }
721 
722 /* based on sockfs_snapshot */
723 /*ARGSUSED*/
724 static int
725 netstat_unix_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
726 {
727 	const struct sonode *so = walk_data;
728 
729 	if (so->so_accessvp == NULL)
730 		return (WALK_NEXT);
731 
732 	if (so->so_family != AF_UNIX) {
733 		mdb_warn("sonode of family %hi at %p\n", so->so_family, kaddr);
734 		return (WALK_ERR);
735 	}
736 
737 	mdb_printf("%-?p ", kaddr);
738 
739 	switch (so->so_serv_type) {
740 	    case T_CLTS:
741 		mdb_printf("%-10s ", "dgram");
742 		break;
743 	    case T_COTS:
744 		mdb_printf("%-10s ", "stream");
745 		break;
746 	    case T_COTS_ORD:
747 		mdb_printf("%-10s ", "stream-ord");
748 		break;
749 	    default:
750 		    mdb_printf("%-10i ", so->so_serv_type);
751 	}
752 
753 	if ((so->so_state & SS_ISBOUND) &&
754 	    (so->so_ux_laddr.soua_magic == SOU_MAGIC_EXPLICIT)) {
755 		mdb_printf("%0?p ", so->so_ux_laddr.soua_vp);
756 	} else {
757 		mdb_printf("%0?p ", NULL);
758 	}
759 
760 	if ((so->so_state & SS_ISCONNECTED) &&
761 	    (so->so_ux_faddr.soua_magic == SOU_MAGIC_EXPLICIT)) {
762 		mdb_printf("%0?p ", so->so_ux_faddr.soua_vp);
763 	} else {
764 		mdb_printf("%0?p ", NULL);
765 	}
766 
767 	if (netstat_unix_name_pr(so, &so->so_laddr) == -1)
768 		return (WALK_ERR);
769 
770 	if (netstat_unix_name_pr(so, &so->so_faddr) == -1)
771 		return (WALK_ERR);
772 
773 	mdb_printf("%4i\n", so->so_zoneid);
774 
775 	return (WALK_NEXT);
776 }
777 
778 static void
779 netstat_tcp_verbose_header_pr(void)
780 {
781 	mdb_printf("       %<u>%-5s %-8s %-8s %-5s %-8s %-8s %5s %5s%</u>\n",
782 	    "Swind", "Snext", "Suna", "Rwind", "Rack", "Rnext", "Rto", "Mss");
783 }
784 
785 /*ARGSUSED*/
786 int
787 netstat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
788 {
789 	uint_t opts = 0;
790 	const char *optf = NULL;
791 	const char *optP = NULL;
792 
793 	if (mdb_getopts(argc, argv,
794 	    'a', MDB_OPT_SETBITS, NETSTAT_ALL, &opts,
795 	    'v', MDB_OPT_SETBITS, NETSTAT_VERBOSE, &opts,
796 	    'f', MDB_OPT_STR, &optf,
797 	    'P', MDB_OPT_STR, &optP,
798 	    NULL) != argc)
799 		return (DCMD_USAGE);
800 
801 	if (optP != NULL) {
802 		if ((strcmp("tcp", optP) != 0) && (strcmp("udp", optP) != 0))
803 			return (DCMD_USAGE);
804 
805 	}
806 
807 	if (optf != NULL) {
808 		if ((strcmp("inet", optf) != 0) &&
809 		    (strcmp("inet6", optf) != 0) &&
810 		    (strcmp("unix", optf) != 0))
811 			return (DCMD_USAGE);
812 	}
813 
814 	if ((optP == NULL) || (strcmp("tcp", optP) == 0)) {
815 		if ((optf == NULL) || (strcmp("inet", optf) == 0)) {
816 			/* Print TCPv4 connection */
817 			mdb_printf(
818 			    "%<u>%-?s St %*s       %*s       %s%</u>\n",
819 			    "TCPv4", ADDR_V4_WIDTH, "Local Address",
820 			    ADDR_V4_WIDTH, "Remote Address", "Zone");
821 
822 			if (opts & NETSTAT_VERBOSE)
823 				netstat_tcp_verbose_header_pr();
824 
825 			if (mdb_walk("ipcl_tcpconn_cache", netstat_tcpv4_cb,
826 			    (void *)(uintptr_t)opts) == -1) {
827 				mdb_warn("failed to walk ipcl_tcpconn_cache");
828 				return (DCMD_ERR);
829 			}
830 		}
831 
832 		if ((optf == NULL) || (strcmp("inet6", optf) == 0)) {
833 			/* Print TCPv6 connection */
834 			mdb_printf(
835 			    "%<u>%-?s St %*s       %*s       %s\n%</u>",
836 			    "TCPv6", ADDR_V6_WIDTH, "Local Address",
837 			    ADDR_V6_WIDTH, "Remote Address", "Zone");
838 
839 			if (opts & NETSTAT_VERBOSE)
840 				netstat_tcp_verbose_header_pr();
841 
842 			if (mdb_walk("ipcl_tcpconn_cache", netstat_tcpv6_cb,
843 			    (void *)(uintptr_t)opts) == -1) {
844 				mdb_warn("failed to walk ipcl_tcpconn_cache");
845 				return (DCMD_ERR);
846 			}
847 		}
848 	}
849 
850 	if ((optP == NULL) || (strcmp("udp", optP) == 0)) {
851 		if ((optf == NULL) || (strcmp("inet", optf) == 0)) {
852 			/* Print UDPv4 connection */
853 			mdb_printf(
854 			    "%<u>%-?s St %*s       %*s       %s\n%</u>",
855 			    "UDPv4", ADDR_V4_WIDTH, "Local Address",
856 			    ADDR_V4_WIDTH, "Remote Address", "Zone");
857 
858 			if (mdb_walk("genunix`udp", netstat_udpv4_cb,
859 			    (void *)(uintptr_t)opts) == -1) {
860 				mdb_warn("failed to walk genunix`udp");
861 				return (DCMD_ERR);
862 			}
863 
864 		}
865 
866 		if ((optf == NULL) || (strcmp("inet6", optf) == 0)) {
867 			/* Print UDPv6 connection */
868 			mdb_printf(
869 			    "%<u>%-?s St %*s       %*s       %s\n%</u>",
870 			    "UDPv6", ADDR_V6_WIDTH, "Local Address",
871 			    ADDR_V6_WIDTH, "Remote Address", "Zone");
872 
873 			if (mdb_walk("genunix`udp", netstat_udpv6_cb,
874 			    (void *)(uintptr_t)opts) == -1) {
875 				mdb_warn("failed to walk genunix`udp");
876 				return (DCMD_ERR);
877 			}
878 
879 		}
880 	}
881 
882 	if (((optf == NULL) || (strcmp("unix", optf) == 0)) && (optP == NULL)) {
883 		/* Print Unix Domain Sockets */
884 		mdb_printf("%<u>%-?s %-10s %-?s %-?s %-14s %-14s %s%</u>\n",
885 		    "AF_UNIX", "Type", "Vnode", "Conn", "Local Addr",
886 		    "Remote Addr", "Zone");
887 
888 		if (mdb_walk("genunix`sonode", netstat_unix_cb, NULL) == -1) {
889 			mdb_warn("failed to walk genunix`sonode");
890 			return (DCMD_ERR);
891 		}
892 	}
893 
894 	return (DCMD_OK);
895 }
896