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