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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2024 MNX Cloud, Inc.
25 */
26
27 #include <mdb/mdb_modapi.h>
28 #include <mdb/mdb_ks.h>
29 #include <mdb/mdb_ctf.h>
30 #include <sys/types.h>
31 #include <sys/tihdr.h>
32 #include <inet/led.h>
33 #include <inet/common.h>
34 #include <netinet/in.h>
35 #include <netinet/ip6.h>
36 #include <netinet/icmp6.h>
37 #include <inet/ip.h>
38 #include <inet/ip6.h>
39 #include <inet/ipclassifier.h>
40 #include <inet/tcp.h>
41 #include <sys/stream.h>
42 #include <sys/vfs.h>
43 #include <sys/stropts.h>
44 #include <sys/tpicommon.h>
45 #include <sys/socket.h>
46 #include <sys/socketvar.h>
47 #include <sys/cred_impl.h>
48 #include <inet/udp_impl.h>
49 #include <inet/rawip_impl.h>
50 #include <inet/mi.h>
51 #include <fs/sockfs/socktpi_impl.h>
52 #include <net/bridge_impl.h>
53 #include <io/trill_impl.h>
54 #include <sys/mac_impl.h>
55
56 #define ADDR_V6_WIDTH 23
57 #define ADDR_V4_WIDTH 15
58
59 #define NETSTAT_ALL 0x01
60 #define NETSTAT_VERBOSE 0x02
61 #define NETSTAT_ROUTE 0x04
62 #define NETSTAT_V4 0x08
63 #define NETSTAT_V6 0x10
64 #define NETSTAT_UNIX 0x20
65
66 #define NETSTAT_FIRST 0x80000000u
67
68 typedef struct netstat_cb_data_s {
69 uint_t opts;
70 conn_t conn;
71 int af;
72 union {
73 tcp_t tcp;
74 udp_t udp;
75 icmp_t icmp;
76 } cb_proto;
77 } netstat_cb_data_t;
78
79 int
icmp_stacks_walk_init(mdb_walk_state_t * wsp)80 icmp_stacks_walk_init(mdb_walk_state_t *wsp)
81 {
82 if (mdb_layered_walk("netstack", wsp) == -1) {
83 mdb_warn("can't walk 'netstack'");
84 return (WALK_ERR);
85 }
86 return (WALK_NEXT);
87 }
88
89 int
icmp_stacks_walk_step(mdb_walk_state_t * wsp)90 icmp_stacks_walk_step(mdb_walk_state_t *wsp)
91 {
92 uintptr_t kaddr;
93 netstack_t nss;
94
95 if (mdb_vread(&nss, sizeof (nss), wsp->walk_addr) == -1) {
96 mdb_warn("can't read netstack at %p", wsp->walk_addr);
97 return (WALK_ERR);
98 }
99 kaddr = (uintptr_t)nss.netstack_modules[NS_ICMP];
100 return (wsp->walk_callback(kaddr, wsp->walk_layer, wsp->walk_cbdata));
101 }
102
103 int
tcp_stacks_walk_init(mdb_walk_state_t * wsp)104 tcp_stacks_walk_init(mdb_walk_state_t *wsp)
105 {
106 if (mdb_layered_walk("netstack", wsp) == -1) {
107 mdb_warn("can't walk 'netstack'");
108 return (WALK_ERR);
109 }
110 return (WALK_NEXT);
111 }
112
113 int
tcp_stacks_walk_step(mdb_walk_state_t * wsp)114 tcp_stacks_walk_step(mdb_walk_state_t *wsp)
115 {
116 uintptr_t kaddr;
117 netstack_t nss;
118
119 if (mdb_vread(&nss, sizeof (nss), wsp->walk_addr) == -1) {
120 mdb_warn("can't read netstack at %p", wsp->walk_addr);
121 return (WALK_ERR);
122 }
123 kaddr = (uintptr_t)nss.netstack_modules[NS_TCP];
124 return (wsp->walk_callback(kaddr, wsp->walk_layer, wsp->walk_cbdata));
125 }
126
127 int
udp_stacks_walk_init(mdb_walk_state_t * wsp)128 udp_stacks_walk_init(mdb_walk_state_t *wsp)
129 {
130 if (mdb_layered_walk("netstack", wsp) == -1) {
131 mdb_warn("can't walk 'netstack'");
132 return (WALK_ERR);
133 }
134 return (WALK_NEXT);
135 }
136
137 int
udp_stacks_walk_step(mdb_walk_state_t * wsp)138 udp_stacks_walk_step(mdb_walk_state_t *wsp)
139 {
140 uintptr_t kaddr;
141 netstack_t nss;
142
143 if (mdb_vread(&nss, sizeof (nss), wsp->walk_addr) == -1) {
144 mdb_warn("can't read netstack at %p", wsp->walk_addr);
145 return (WALK_ERR);
146 }
147 kaddr = (uintptr_t)nss.netstack_modules[NS_UDP];
148 return (wsp->walk_callback(kaddr, wsp->walk_layer, wsp->walk_cbdata));
149 }
150
151 /*
152 * Print an IPv4 address and port number in a compact and easy to read format
153 * The arguments are in network byte order
154 */
155 static void
net_ipv4addrport_pr(const in6_addr_t * nipv6addr,in_port_t nport)156 net_ipv4addrport_pr(const in6_addr_t *nipv6addr, in_port_t nport)
157 {
158 uint32_t naddr = V4_PART_OF_V6((*nipv6addr));
159
160 mdb_nhconvert(&nport, &nport, sizeof (nport));
161 mdb_printf("%*I.%-5hu", ADDR_V4_WIDTH, naddr, nport);
162 }
163
164 /*
165 * Print an IPv6 address and port number in a compact and easy to read format
166 * The arguments are in network byte order
167 */
168 static void
net_ipv6addrport_pr(const in6_addr_t * naddr,in_port_t nport)169 net_ipv6addrport_pr(const in6_addr_t *naddr, in_port_t nport)
170 {
171 mdb_nhconvert(&nport, &nport, sizeof (nport));
172 mdb_printf("%*N.%-5hu", ADDR_V6_WIDTH, naddr, nport);
173 }
174
175 static int
net_tcp_active(const tcp_t * tcp)176 net_tcp_active(const tcp_t *tcp)
177 {
178 return (tcp->tcp_state >= TCPS_ESTABLISHED);
179 }
180
181 static int
net_tcp_ipv4(const tcp_t * tcp)182 net_tcp_ipv4(const tcp_t *tcp)
183 {
184 return ((tcp->tcp_connp->conn_ipversion == IPV4_VERSION) ||
185 (IN6_IS_ADDR_UNSPECIFIED(&tcp->tcp_connp->conn_laddr_v6) &&
186 (tcp->tcp_state <= TCPS_LISTEN)));
187 }
188
189 static int
net_tcp_ipv6(const tcp_t * tcp)190 net_tcp_ipv6(const tcp_t *tcp)
191 {
192 return (tcp->tcp_connp->conn_ipversion == IPV6_VERSION);
193 }
194
195 static int
net_udp_active(const udp_t * udp)196 net_udp_active(const udp_t *udp)
197 {
198 return ((udp->udp_state == TS_IDLE) ||
199 (udp->udp_state == TS_DATA_XFER));
200 }
201
202 static int
net_udp_ipv4(const udp_t * udp)203 net_udp_ipv4(const udp_t *udp)
204 {
205 return ((udp->udp_connp->conn_ipversion == IPV4_VERSION) ||
206 (IN6_IS_ADDR_UNSPECIFIED(&udp->udp_connp->conn_laddr_v6) &&
207 (udp->udp_state <= TS_IDLE)));
208 }
209
210 static int
net_udp_ipv6(const udp_t * udp)211 net_udp_ipv6(const udp_t *udp)
212 {
213 return (udp->udp_connp->conn_ipversion == IPV6_VERSION);
214 }
215
216 int
sonode_walk_init(mdb_walk_state_t * wsp)217 sonode_walk_init(mdb_walk_state_t *wsp)
218 {
219 if (wsp->walk_addr == 0) {
220 GElf_Sym sym;
221 struct socklist *slp;
222
223 if (mdb_lookup_by_obj("sockfs", "socklist", &sym) == -1) {
224 mdb_warn("failed to lookup sockfs`socklist");
225 return (WALK_ERR);
226 }
227
228 slp = (struct socklist *)(uintptr_t)sym.st_value;
229
230 if (mdb_vread(&wsp->walk_addr, sizeof (wsp->walk_addr),
231 (uintptr_t)&slp->sl_list) == -1) {
232 mdb_warn("failed to read address of initial sonode "
233 "at %p", &slp->sl_list);
234 return (WALK_ERR);
235 }
236 }
237
238 wsp->walk_data = mdb_alloc(sizeof (struct sotpi_sonode), UM_SLEEP);
239 return (WALK_NEXT);
240 }
241
242 int
sonode_walk_step(mdb_walk_state_t * wsp)243 sonode_walk_step(mdb_walk_state_t *wsp)
244 {
245 int status;
246 struct sotpi_sonode *stp;
247
248 if (wsp->walk_addr == 0)
249 return (WALK_DONE);
250
251 if (mdb_vread(wsp->walk_data, sizeof (struct sotpi_sonode),
252 wsp->walk_addr) == -1) {
253 mdb_warn("failed to read sonode at %p", wsp->walk_addr);
254 return (WALK_ERR);
255 }
256
257 status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
258 wsp->walk_cbdata);
259
260 stp = wsp->walk_data;
261
262 wsp->walk_addr = (uintptr_t)stp->st_info.sti_next_so;
263 return (status);
264 }
265
266 void
sonode_walk_fini(mdb_walk_state_t * wsp)267 sonode_walk_fini(mdb_walk_state_t *wsp)
268 {
269 mdb_free(wsp->walk_data, sizeof (struct sotpi_sonode));
270 }
271
272 struct mi_walk_data {
273 uintptr_t mi_wd_miofirst;
274 MI_O mi_wd_miodata;
275 };
276
277 int
mi_walk_init(mdb_walk_state_t * wsp)278 mi_walk_init(mdb_walk_state_t *wsp)
279 {
280 struct mi_walk_data *wdp;
281
282 if (wsp->walk_addr == 0) {
283 mdb_warn("mi doesn't support global walks\n");
284 return (WALK_ERR);
285 }
286
287 wdp = mdb_alloc(sizeof (struct mi_walk_data), UM_SLEEP);
288
289 /* So that we do not immediately return WALK_DONE below */
290 wdp->mi_wd_miofirst = 0;
291
292 wsp->walk_data = wdp;
293 return (WALK_NEXT);
294 }
295
296 int
mi_walk_step(mdb_walk_state_t * wsp)297 mi_walk_step(mdb_walk_state_t *wsp)
298 {
299 struct mi_walk_data *wdp = wsp->walk_data;
300 MI_OP miop = &wdp->mi_wd_miodata;
301 int status;
302
303 /* Always false in the first iteration */
304 if ((wsp->walk_addr == (uintptr_t)NULL) ||
305 (wsp->walk_addr == wdp->mi_wd_miofirst)) {
306 return (WALK_DONE);
307 }
308
309 if (mdb_vread(miop, sizeof (MI_O), wsp->walk_addr) == -1) {
310 mdb_warn("failed to read MI object at %p", wsp->walk_addr);
311 return (WALK_ERR);
312 }
313
314 /* Only true in the first iteration */
315 if (wdp->mi_wd_miofirst == 0) {
316 wdp->mi_wd_miofirst = wsp->walk_addr;
317 status = WALK_NEXT;
318 } else {
319 status = wsp->walk_callback(wsp->walk_addr + sizeof (MI_O),
320 &miop[1], wsp->walk_cbdata);
321 }
322
323 wsp->walk_addr = (uintptr_t)miop->mi_o_next;
324 return (status);
325 }
326
327 void
mi_walk_fini(mdb_walk_state_t * wsp)328 mi_walk_fini(mdb_walk_state_t *wsp)
329 {
330 mdb_free(wsp->walk_data, sizeof (struct mi_walk_data));
331 }
332
333 typedef struct mi_payload_walk_arg_s {
334 const char *mi_pwa_walker; /* Underlying walker */
335 const off_t mi_pwa_head_off; /* Offset for mi_o_head_t * in stack */
336 const size_t mi_pwa_size; /* size of mi payload */
337 const uint_t mi_pwa_flags; /* device and/or module */
338 } mi_payload_walk_arg_t;
339
340 #define MI_PAYLOAD_DEVICE 0x1
341 #define MI_PAYLOAD_MODULE 0x2
342
343 int
mi_payload_walk_init(mdb_walk_state_t * wsp)344 mi_payload_walk_init(mdb_walk_state_t *wsp)
345 {
346 const mi_payload_walk_arg_t *arg = wsp->walk_arg;
347
348 if (mdb_layered_walk(arg->mi_pwa_walker, wsp) == -1) {
349 mdb_warn("can't walk '%s'", arg->mi_pwa_walker);
350 return (WALK_ERR);
351 }
352 return (WALK_NEXT);
353 }
354
355 int
mi_payload_walk_step(mdb_walk_state_t * wsp)356 mi_payload_walk_step(mdb_walk_state_t *wsp)
357 {
358 const mi_payload_walk_arg_t *arg = wsp->walk_arg;
359 uintptr_t kaddr;
360
361 kaddr = wsp->walk_addr + arg->mi_pwa_head_off;
362
363 if (mdb_vread(&kaddr, sizeof (kaddr), kaddr) == -1) {
364 mdb_warn("can't read address of mi head at %p for %s",
365 kaddr, arg->mi_pwa_walker);
366 return (WALK_ERR);
367 }
368
369 if (kaddr == 0) {
370 /* Empty list */
371 return (WALK_DONE);
372 }
373
374 if (mdb_pwalk("genunix`mi", wsp->walk_callback,
375 wsp->walk_cbdata, kaddr) == -1) {
376 mdb_warn("failed to walk genunix`mi");
377 return (WALK_ERR);
378 }
379 return (WALK_NEXT);
380 }
381
382 const mi_payload_walk_arg_t mi_icmp_arg = {
383 "icmp_stacks", OFFSETOF(icmp_stack_t, is_head), sizeof (icmp_t),
384 MI_PAYLOAD_DEVICE | MI_PAYLOAD_MODULE
385 };
386
387 int
sonode(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)388 sonode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
389 {
390 const char *optf = NULL;
391 const char *optt = NULL;
392 const char *optp = NULL;
393 int family = AF_UNSPEC, type = 0, proto = 0;
394 int filter = 0;
395 struct sonode so;
396
397 if (!(flags & DCMD_ADDRSPEC)) {
398 if (mdb_walk_dcmd("genunix`sonode", "genunix`sonode", argc,
399 argv) == -1) {
400 mdb_warn("failed to walk sonode");
401 return (DCMD_ERR);
402 }
403
404 return (DCMD_OK);
405 }
406
407 if (mdb_getopts(argc, argv,
408 'f', MDB_OPT_STR, &optf,
409 't', MDB_OPT_STR, &optt,
410 'p', MDB_OPT_STR, &optp,
411 NULL) != argc)
412 return (DCMD_USAGE);
413
414 if (optf != NULL) {
415 if (strcmp("inet", optf) == 0)
416 family = AF_INET;
417 else if (strcmp("inet6", optf) == 0)
418 family = AF_INET6;
419 else if (strcmp("unix", optf) == 0)
420 family = AF_UNIX;
421 else
422 family = mdb_strtoull(optf);
423 filter = 1;
424 }
425
426 if (optt != NULL) {
427 if (strcmp("stream", optt) == 0)
428 type = SOCK_STREAM;
429 else if (strcmp("dgram", optt) == 0)
430 type = SOCK_DGRAM;
431 else if (strcmp("raw", optt) == 0)
432 type = SOCK_RAW;
433 else
434 type = mdb_strtoull(optt);
435 filter = 1;
436 }
437
438 if (optp != NULL) {
439 proto = mdb_strtoull(optp);
440 filter = 1;
441 }
442
443 if (DCMD_HDRSPEC(flags) && !filter) {
444 mdb_printf("%<u>%-?s Family Type Proto State Mode Flag "
445 "AccessVP%</u>\n", "Sonode:");
446 }
447
448 if (mdb_vread(&so, sizeof (so), addr) == -1) {
449 mdb_warn("failed to read sonode at %p", addr);
450 return (DCMD_ERR);
451 }
452
453 if ((optf != NULL) && (so.so_family != family))
454 return (DCMD_OK);
455
456 if ((optt != NULL) && (so.so_type != type))
457 return (DCMD_OK);
458
459 if ((optp != NULL) && (so.so_protocol != proto))
460 return (DCMD_OK);
461
462 if (filter) {
463 mdb_printf("%0?p\n", addr);
464 return (DCMD_OK);
465 }
466
467 mdb_printf("%0?p ", addr);
468
469 switch (so.so_family) {
470 case AF_UNIX:
471 mdb_printf("unix ");
472 break;
473 case AF_INET:
474 mdb_printf("inet ");
475 break;
476 case AF_INET6:
477 mdb_printf("inet6 ");
478 break;
479 default:
480 mdb_printf("%6hi", so.so_family);
481 }
482
483 switch (so.so_type) {
484 case SOCK_STREAM:
485 mdb_printf(" strm");
486 break;
487 case SOCK_DGRAM:
488 mdb_printf(" dgrm");
489 break;
490 case SOCK_RAW:
491 mdb_printf(" raw ");
492 break;
493 default:
494 mdb_printf(" %4hi", so.so_type);
495 }
496
497 mdb_printf(" %5hi %05x %04x %04hx\n",
498 so.so_protocol, so.so_state, so.so_mode,
499 so.so_flag);
500
501 return (DCMD_OK);
502 }
503
504 #define MI_PAYLOAD 0x1
505 #define MI_DEVICE 0x2
506 #define MI_MODULE 0x4
507
508 int
mi(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)509 mi(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
510 {
511 uint_t opts = 0;
512 MI_O mio;
513
514 if (!(flags & DCMD_ADDRSPEC))
515 return (DCMD_USAGE);
516
517 if (mdb_getopts(argc, argv,
518 'p', MDB_OPT_SETBITS, MI_PAYLOAD, &opts,
519 'd', MDB_OPT_SETBITS, MI_DEVICE, &opts,
520 'm', MDB_OPT_SETBITS, MI_MODULE, &opts,
521 NULL) != argc)
522 return (DCMD_USAGE);
523
524 if ((opts & (MI_DEVICE | MI_MODULE)) == (MI_DEVICE | MI_MODULE)) {
525 mdb_warn("at most one filter, d for devices or m "
526 "for modules, may be specified\n");
527 return (DCMD_USAGE);
528 }
529
530 if ((opts == 0) && (DCMD_HDRSPEC(flags))) {
531 mdb_printf("%<u>%-?s %-?s %-?s IsDev Dev%</u>\n",
532 "MI_O", "Next", "Prev");
533 }
534
535 if (mdb_vread(&mio, sizeof (mio), addr) == -1) {
536 mdb_warn("failed to read mi object MI_O at %p", addr);
537 return (DCMD_ERR);
538 }
539
540 if (opts != 0) {
541 if (mio.mi_o_isdev == B_FALSE) {
542 /* mio is a module */
543 if (!(opts & MI_MODULE) && (opts & MI_DEVICE))
544 return (DCMD_OK);
545 } else {
546 /* mio is a device */
547 if (!(opts & MI_DEVICE) && (opts & MI_MODULE))
548 return (DCMD_OK);
549 }
550
551 if (opts & MI_PAYLOAD)
552 mdb_printf("%p\n", addr + sizeof (MI_O));
553 else
554 mdb_printf("%p\n", addr);
555 return (DCMD_OK);
556 }
557
558 mdb_printf("%0?p %0?p %0?p ", addr, mio.mi_o_next, mio.mi_o_prev);
559
560 if (mio.mi_o_isdev == B_FALSE)
561 mdb_printf("FALSE");
562 else
563 mdb_printf("TRUE ");
564
565 mdb_printf(" %0?p\n", mio.mi_o_dev);
566
567 return (DCMD_OK);
568 }
569
570 static int
ns_to_stackid(uintptr_t kaddr)571 ns_to_stackid(uintptr_t kaddr)
572 {
573 netstack_t nss;
574
575 if (mdb_vread(&nss, sizeof (nss), kaddr) == -1) {
576 mdb_warn("failed to read netstack_t %p", kaddr);
577 return (0);
578 }
579 return (nss.netstack_stackid);
580 }
581
582
583
584 static void
netstat_tcp_verbose_pr(const tcp_t * tcp)585 netstat_tcp_verbose_pr(const tcp_t *tcp)
586 {
587 mdb_printf(" %5i %08x %08x %5i %08x %08x %5li %5i\n",
588 tcp->tcp_swnd, tcp->tcp_snxt, tcp->tcp_suna, tcp->tcp_rwnd,
589 tcp->tcp_rack, tcp->tcp_rnxt, tcp->tcp_rto, tcp->tcp_mss);
590 }
591
592 static int
netstat_tcp_cb(uintptr_t kaddr,const void * walk_data __unused,void * cb_data)593 netstat_tcp_cb(uintptr_t kaddr, const void *walk_data __unused, void *cb_data)
594 {
595 netstat_cb_data_t *ncb = cb_data;
596 uint_t opts = ncb->opts;
597 int af = ncb->af;
598 uintptr_t tcp_kaddr;
599 conn_t *connp = &ncb->conn;
600 tcp_t *tcp = &ncb->cb_proto.tcp;
601
602 if (mdb_vread(connp, sizeof (conn_t), kaddr) == -1) {
603 mdb_warn("failed to read conn_t at %p", kaddr);
604 return (WALK_ERR);
605 }
606
607 tcp_kaddr = (uintptr_t)connp->conn_tcp;
608 if (mdb_vread(tcp, sizeof (tcp_t), tcp_kaddr) == -1) {
609 mdb_warn("failed to read tcp_t at %p", tcp_kaddr);
610 return (WALK_ERR);
611 }
612
613 connp->conn_tcp = tcp;
614 tcp->tcp_connp = connp;
615
616 if (!((opts & NETSTAT_ALL) || net_tcp_active(tcp)) ||
617 (af == AF_INET && !net_tcp_ipv4(tcp)) ||
618 (af == AF_INET6 && !net_tcp_ipv6(tcp))) {
619 return (WALK_NEXT);
620 }
621
622 mdb_printf("%0?p %2i ", tcp_kaddr, tcp->tcp_state);
623 if (af == AF_INET) {
624 net_ipv4addrport_pr(&connp->conn_laddr_v6, connp->conn_lport);
625 mdb_printf(" ");
626 net_ipv4addrport_pr(&connp->conn_faddr_v6, connp->conn_fport);
627 } else if (af == AF_INET6) {
628 net_ipv6addrport_pr(&connp->conn_laddr_v6, connp->conn_lport);
629 mdb_printf(" ");
630 net_ipv6addrport_pr(&connp->conn_faddr_v6, connp->conn_fport);
631 }
632 mdb_printf(" %5i", ns_to_stackid((uintptr_t)connp->conn_netstack));
633 mdb_printf(" %4i\n", connp->conn_zoneid);
634 if (opts & NETSTAT_VERBOSE)
635 netstat_tcp_verbose_pr(tcp);
636
637 return (WALK_NEXT);
638 }
639
640 static int
netstat_udp_cb(uintptr_t kaddr,const void * walk_data __unused,void * cb_data)641 netstat_udp_cb(uintptr_t kaddr, const void *walk_data __unused, void *cb_data)
642 {
643 netstat_cb_data_t *ncb = cb_data;
644 uint_t opts = ncb->opts;
645 int af = ncb->af;
646 udp_t *udp = &ncb->cb_proto.udp;
647 conn_t *connp = &ncb->conn;
648 char *state;
649 uintptr_t udp_kaddr;
650
651 if (mdb_vread(connp, sizeof (conn_t), kaddr) == -1) {
652 mdb_warn("failed to read conn_t at %p", kaddr);
653 return (WALK_ERR);
654 }
655
656 udp_kaddr = (uintptr_t)connp->conn_udp;
657 if (mdb_vread(udp, sizeof (udp_t), udp_kaddr) == -1) {
658 mdb_warn("failed to read conn_udp at %p", udp_kaddr);
659 return (WALK_ERR);
660 }
661
662 /* Need to do these reassignments for the net_udp_*() routines below. */
663 connp->conn_udp = udp;
664 udp->udp_connp = connp;
665
666 if (!((opts & NETSTAT_ALL) || net_udp_active(udp)) ||
667 (af == AF_INET && !net_udp_ipv4(udp)) ||
668 (af == AF_INET6 && !net_udp_ipv6(udp))) {
669 return (WALK_NEXT);
670 }
671
672 if (udp->udp_state == TS_UNBND)
673 state = "UNBOUND";
674 else if (udp->udp_state == TS_IDLE)
675 state = "IDLE";
676 else if (udp->udp_state == TS_DATA_XFER)
677 state = "CONNECTED";
678 else
679 state = "UNKNOWN";
680
681 mdb_printf("%0?p %10s ", udp_kaddr, state);
682 if (af == AF_INET) {
683 net_ipv4addrport_pr(&connp->conn_laddr_v6, connp->conn_lport);
684 mdb_printf(" ");
685 net_ipv4addrport_pr(&connp->conn_faddr_v6, connp->conn_fport);
686 } else if (af == AF_INET6) {
687 net_ipv6addrport_pr(&connp->conn_laddr_v6, connp->conn_lport);
688 mdb_printf(" ");
689 net_ipv6addrport_pr(&connp->conn_faddr_v6, connp->conn_fport);
690 }
691 mdb_printf(" %5i", ns_to_stackid((uintptr_t)connp->conn_netstack));
692 mdb_printf(" %4i\n", connp->conn_zoneid);
693
694 return (WALK_NEXT);
695 }
696
697 static int
netstat_icmp_cb(uintptr_t kaddr,const void * walk_data __unused,void * cb_data)698 netstat_icmp_cb(uintptr_t kaddr, const void *walk_data __unused, void *cb_data)
699 {
700 netstat_cb_data_t *ncb = cb_data;
701 int af = ncb->af;
702 icmp_t *icmp = &ncb->cb_proto.icmp;
703 conn_t *connp = &ncb->conn;
704 char *state;
705
706 if (mdb_vread(connp, sizeof (conn_t), kaddr) == -1) {
707 mdb_warn("failed to read conn_t at %p", kaddr);
708 return (WALK_ERR);
709 }
710
711 if (mdb_vread(icmp, sizeof (icmp_t),
712 (uintptr_t)connp->conn_icmp) == -1) {
713 mdb_warn("failed to read conn_icmp at %p",
714 (uintptr_t)connp->conn_icmp);
715 return (WALK_ERR);
716 }
717
718 connp->conn_icmp = icmp;
719 icmp->icmp_connp = connp;
720
721 if ((af == AF_INET && connp->conn_ipversion != IPV4_VERSION) ||
722 (af == AF_INET6 && connp->conn_ipversion != IPV6_VERSION)) {
723 return (WALK_NEXT);
724 }
725
726 if (icmp->icmp_state == TS_UNBND)
727 state = "UNBOUND";
728 else if (icmp->icmp_state == TS_IDLE)
729 state = "IDLE";
730 else if (icmp->icmp_state == TS_DATA_XFER)
731 state = "CONNECTED";
732 else
733 state = "UNKNOWN";
734
735 mdb_printf("%0?p %10s ", (uintptr_t)connp->conn_icmp, state);
736 if (af == AF_INET) {
737 net_ipv4addrport_pr(&connp->conn_laddr_v6, connp->conn_lport);
738 mdb_printf(" ");
739 net_ipv4addrport_pr(&connp->conn_faddr_v6, connp->conn_fport);
740 } else if (af == AF_INET6) {
741 net_ipv6addrport_pr(&connp->conn_laddr_v6, connp->conn_lport);
742 mdb_printf(" ");
743 net_ipv6addrport_pr(&connp->conn_faddr_v6, connp->conn_fport);
744 }
745 mdb_printf(" %5i", ns_to_stackid((uintptr_t)connp->conn_netstack));
746 mdb_printf(" %4i\n", connp->conn_zoneid);
747
748 return (WALK_NEXT);
749 }
750
751 /*
752 * print the address of a unix domain socket
753 *
754 * so is the address of a AF_UNIX struct sonode in mdb's address space
755 * soa is the address of the struct soaddr to print
756 *
757 * returns 0 on success, -1 otherwise
758 */
759 static int
netstat_unix_name_pr(const struct sotpi_sonode * st,const struct soaddr * soa)760 netstat_unix_name_pr(const struct sotpi_sonode *st, const struct soaddr *soa)
761 {
762 const struct sonode *so = &st->st_sonode;
763 const char none[] = " (none)";
764
765 if ((so->so_state & SS_ISBOUND) && (soa->soa_len != 0)) {
766 if (st->st_info.sti_faddr_noxlate) {
767 mdb_printf("%-14s ", " (socketpair)");
768 } else {
769 if (soa->soa_len > sizeof (sa_family_t)) {
770 char addr[MAXPATHLEN + 1];
771
772 if (mdb_readstr(addr, sizeof (addr),
773 (uintptr_t)&soa->soa_sa->sa_data) == -1) {
774 mdb_warn("failed to read unix address "
775 "at %p", &soa->soa_sa->sa_data);
776 return (-1);
777 }
778
779 mdb_printf("%-14s ", addr);
780 } else {
781 mdb_printf("%-14s ", none);
782 }
783 }
784 } else {
785 mdb_printf("%-14s ", none);
786 }
787
788 return (0);
789 }
790
791 /* based on sockfs_snapshot */
792 /*ARGSUSED*/
793 static int
netstat_unix_cb(uintptr_t kaddr,const void * walk_data,void * cb_data)794 netstat_unix_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
795 {
796 const struct sotpi_sonode *st = walk_data;
797 const struct sonode *so = &st->st_sonode;
798 const struct sotpi_info *sti = &st->st_info;
799
800 if (so->so_count == 0)
801 return (WALK_NEXT);
802
803 if (so->so_family != AF_UNIX) {
804 mdb_warn("sonode of family %hi at %p\n", so->so_family, kaddr);
805 return (WALK_ERR);
806 }
807
808 mdb_printf("%-?p ", kaddr);
809
810 switch (sti->sti_serv_type) {
811 case T_CLTS:
812 mdb_printf("%-10s ", "dgram");
813 break;
814 case T_COTS:
815 mdb_printf("%-10s ", "stream");
816 break;
817 case T_COTS_ORD:
818 mdb_printf("%-10s ", "stream-ord");
819 break;
820 default:
821 mdb_printf("%-10i ", sti->sti_serv_type);
822 }
823
824 if ((so->so_state & SS_ISBOUND) &&
825 (sti->sti_ux_laddr.soua_magic == SOU_MAGIC_EXPLICIT)) {
826 mdb_printf("%0?p ", sti->sti_ux_laddr.soua_vp);
827 } else {
828 mdb_printf("%0?p ", NULL);
829 }
830
831 if ((so->so_state & SS_ISCONNECTED) &&
832 (sti->sti_ux_faddr.soua_magic == SOU_MAGIC_EXPLICIT)) {
833 mdb_printf("%0?p ", sti->sti_ux_faddr.soua_vp);
834 } else {
835 mdb_printf("%0?p ", NULL);
836 }
837
838 if (netstat_unix_name_pr(st, &sti->sti_laddr) == -1)
839 return (WALK_ERR);
840
841 if (netstat_unix_name_pr(st, &sti->sti_faddr) == -1)
842 return (WALK_ERR);
843
844 mdb_printf("%4i\n", so->so_zoneid);
845
846 return (WALK_NEXT);
847 }
848
849 static void
netstat_tcp_verbose_header_pr(void)850 netstat_tcp_verbose_header_pr(void)
851 {
852 mdb_printf(" %<u>%-5s %-8s %-8s %-5s %-8s %-8s %5s %5s%</u>\n",
853 "Swind", "Snext", "Suna", "Rwind", "Rack", "Rnext", "Rto", "Mss");
854 }
855
856 static void
get_ifname(const ire_t * ire,char * intf)857 get_ifname(const ire_t *ire, char *intf)
858 {
859 ill_t ill;
860
861 *intf = '\0';
862 if (ire->ire_ill != NULL) {
863 if (mdb_vread(&ill, sizeof (ill),
864 (uintptr_t)ire->ire_ill) == -1)
865 return;
866 (void) mdb_readstr(intf, MIN(LIFNAMSIZ, ill.ill_name_length),
867 (uintptr_t)ill.ill_name);
868 }
869 }
870
871 const in6_addr_t ipv6_all_ones =
872 { 0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU };
873
874 static void
get_ireflags(const ire_t * ire,char * flags)875 get_ireflags(const ire_t *ire, char *flags)
876 {
877 (void) strcpy(flags, "U");
878 /* RTF_INDIRECT wins over RTF_GATEWAY - don't display both */
879 if (ire->ire_flags & RTF_INDIRECT)
880 (void) strcat(flags, "I");
881 else if (ire->ire_type & IRE_OFFLINK)
882 (void) strcat(flags, "G");
883
884 /* IRE_IF_CLONE wins over RTF_HOST - don't display both */
885 if (ire->ire_type & IRE_IF_CLONE)
886 (void) strcat(flags, "C");
887 else if (ire->ire_ipversion == IPV4_VERSION) {
888 if (ire->ire_mask == IP_HOST_MASK)
889 (void) strcat(flags, "H");
890 } else {
891 if (IN6_ARE_ADDR_EQUAL(&ire->ire_mask_v6, &ipv6_all_ones))
892 (void) strcat(flags, "H");
893 }
894
895 if (ire->ire_flags & RTF_DYNAMIC)
896 (void) strcat(flags, "D");
897 if (ire->ire_type == IRE_BROADCAST)
898 (void) strcat(flags, "b");
899 if (ire->ire_type == IRE_MULTICAST)
900 (void) strcat(flags, "m");
901 if (ire->ire_type == IRE_LOCAL)
902 (void) strcat(flags, "L");
903 if (ire->ire_type == IRE_NOROUTE)
904 (void) strcat(flags, "N");
905 if (ire->ire_flags & RTF_MULTIRT)
906 (void) strcat(flags, "M");
907 if (ire->ire_flags & RTF_SETSRC)
908 (void) strcat(flags, "S");
909 if (ire->ire_flags & RTF_REJECT)
910 (void) strcat(flags, "R");
911 if (ire->ire_flags & RTF_BLACKHOLE)
912 (void) strcat(flags, "B");
913 }
914
915 static int
netstat_irev4_cb(uintptr_t kaddr,const void * walk_data,void * cb_data)916 netstat_irev4_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
917 {
918 const ire_t *ire = walk_data;
919 uint_t *opts = cb_data;
920 ipaddr_t gate;
921 char flags[10], intf[LIFNAMSIZ + 1];
922
923 if (ire->ire_ipversion != IPV4_VERSION)
924 return (WALK_NEXT);
925
926 /* Skip certain IREs by default */
927 if (!(*opts & NETSTAT_ALL) &&
928 (ire->ire_type &
929 (IRE_BROADCAST|IRE_LOCAL|IRE_MULTICAST|IRE_NOROUTE|IRE_IF_CLONE)))
930 return (WALK_NEXT);
931
932 if (*opts & NETSTAT_FIRST) {
933 *opts &= ~NETSTAT_FIRST;
934 mdb_printf("%<u>%s Table: IPv4%</u>\n",
935 (*opts & NETSTAT_VERBOSE) ? "IRE" : "Routing");
936 if (*opts & NETSTAT_VERBOSE) {
937 mdb_printf("%<u>%-?s %-*s %-*s %-*s Device Mxfrg Rtt "
938 " Ref Flg Out In/Fwd%</u>\n",
939 "Address", ADDR_V4_WIDTH, "Destination",
940 ADDR_V4_WIDTH, "Mask", ADDR_V4_WIDTH, "Gateway");
941 } else {
942 mdb_printf("%<u>%-?s %-*s %-*s Flags Ref Use "
943 "Interface%</u>\n",
944 "Address", ADDR_V4_WIDTH, "Destination",
945 ADDR_V4_WIDTH, "Gateway");
946 }
947 }
948
949 gate = ire->ire_gateway_addr;
950
951 get_ireflags(ire, flags);
952
953 get_ifname(ire, intf);
954
955 if (*opts & NETSTAT_VERBOSE) {
956 mdb_printf("%?p %-*I %-*I %-*I %-6s %5u%c %4u %3u %-3s %5u "
957 "%u\n", kaddr, ADDR_V4_WIDTH, ire->ire_addr, ADDR_V4_WIDTH,
958 ire->ire_mask, ADDR_V4_WIDTH, gate, intf,
959 0, ' ',
960 ire->ire_metrics.iulp_rtt, ire->ire_refcnt, flags,
961 ire->ire_ob_pkt_count, ire->ire_ib_pkt_count);
962 } else {
963 mdb_printf("%?p %-*I %-*I %-5s %4u %5u %s\n", kaddr,
964 ADDR_V4_WIDTH, ire->ire_addr, ADDR_V4_WIDTH, gate, flags,
965 ire->ire_refcnt,
966 ire->ire_ob_pkt_count + ire->ire_ib_pkt_count, intf);
967 }
968
969 return (WALK_NEXT);
970 }
971
972 int
ip_mask_to_plen_v6(const in6_addr_t * v6mask)973 ip_mask_to_plen_v6(const in6_addr_t *v6mask)
974 {
975 int plen;
976 int i;
977 uint32_t val;
978
979 for (i = 3; i >= 0; i--)
980 if (v6mask->s6_addr32[i] != 0)
981 break;
982 if (i < 0)
983 return (0);
984 plen = 32 + 32 * i;
985 val = v6mask->s6_addr32[i];
986 while (!(val & 1)) {
987 val >>= 1;
988 plen--;
989 }
990
991 return (plen);
992 }
993
994 static int
netstat_irev6_cb(uintptr_t kaddr,const void * walk_data,void * cb_data)995 netstat_irev6_cb(uintptr_t kaddr, const void *walk_data, void *cb_data)
996 {
997 const ire_t *ire = walk_data;
998 uint_t *opts = cb_data;
999 const in6_addr_t *gatep;
1000 char deststr[ADDR_V6_WIDTH + 5];
1001 char flags[10], intf[LIFNAMSIZ + 1];
1002 int masklen;
1003
1004 if (ire->ire_ipversion != IPV6_VERSION)
1005 return (WALK_NEXT);
1006
1007 /* Skip certain IREs by default */
1008 if (!(*opts & NETSTAT_ALL) &&
1009 (ire->ire_type &
1010 (IRE_BROADCAST|IRE_LOCAL|IRE_MULTICAST|IRE_NOROUTE|IRE_IF_CLONE)))
1011 return (WALK_NEXT);
1012
1013 if (*opts & NETSTAT_FIRST) {
1014 *opts &= ~NETSTAT_FIRST;
1015 mdb_printf("\n%<u>%s Table: IPv6%</u>\n",
1016 (*opts & NETSTAT_VERBOSE) ? "IRE" : "Routing");
1017 if (*opts & NETSTAT_VERBOSE) {
1018 mdb_printf("%<u>%-?s %-*s %-*s If PMTU Rtt Ref "
1019 "Flags Out In/Fwd%</u>\n",
1020 "Address", ADDR_V6_WIDTH+4, "Destination/Mask",
1021 ADDR_V6_WIDTH, "Gateway");
1022 } else {
1023 mdb_printf("%<u>%-?s %-*s %-*s Flags Ref Use If"
1024 "%</u>\n",
1025 "Address", ADDR_V6_WIDTH+4, "Destination/Mask",
1026 ADDR_V6_WIDTH, "Gateway");
1027 }
1028 }
1029
1030 gatep = &ire->ire_gateway_addr_v6;
1031
1032 masklen = ip_mask_to_plen_v6(&ire->ire_mask_v6);
1033 (void) mdb_snprintf(deststr, sizeof (deststr), "%N/%d",
1034 &ire->ire_addr_v6, masklen);
1035
1036 get_ireflags(ire, flags);
1037
1038 get_ifname(ire, intf);
1039
1040 if (*opts & NETSTAT_VERBOSE) {
1041 mdb_printf("%?p %-*s %-*N %-5s %5u%c %5u %3u %-5s %6u %u\n",
1042 kaddr, ADDR_V6_WIDTH+4, deststr, ADDR_V6_WIDTH, gatep,
1043 intf, 0, ' ',
1044 ire->ire_metrics.iulp_rtt, ire->ire_refcnt,
1045 flags, ire->ire_ob_pkt_count, ire->ire_ib_pkt_count);
1046 } else {
1047 mdb_printf("%?p %-*s %-*N %-5s %3u %6u %s\n", kaddr,
1048 ADDR_V6_WIDTH+4, deststr, ADDR_V6_WIDTH, gatep, flags,
1049 ire->ire_refcnt,
1050 ire->ire_ob_pkt_count + ire->ire_ib_pkt_count, intf);
1051 }
1052
1053 return (WALK_NEXT);
1054 }
1055
1056 static void
netstat_header_v4(int proto)1057 netstat_header_v4(int proto)
1058 {
1059 if (proto == IPPROTO_TCP)
1060 mdb_printf("%<u>%-?s ", "TCPv4");
1061 else if (proto == IPPROTO_UDP)
1062 mdb_printf("%<u>%-?s ", "UDPv4");
1063 else if (proto == IPPROTO_ICMP)
1064 mdb_printf("%<u>%-?s ", "ICMPv4");
1065 mdb_printf("State %6s%*s %6s%*s %-5s %-4s%</u>\n",
1066 "", ADDR_V4_WIDTH, "Local Address",
1067 "", ADDR_V4_WIDTH, "Remote Address", "Stack", "Zone");
1068 }
1069
1070 static void
netstat_header_v6(int proto)1071 netstat_header_v6(int proto)
1072 {
1073 if (proto == IPPROTO_TCP)
1074 mdb_printf("%<u>%-?s ", "TCPv6");
1075 else if (proto == IPPROTO_UDP)
1076 mdb_printf("%<u>%-?s ", "UDPv6");
1077 else if (proto == IPPROTO_ICMP)
1078 mdb_printf("%<u>%-?s ", "ICMPv6");
1079 mdb_printf("State %6s%*s %6s%*s %-5s %-4s%</u>\n",
1080 "", ADDR_V6_WIDTH, "Local Address",
1081 "", ADDR_V6_WIDTH, "Remote Address", "Stack", "Zone");
1082 }
1083
1084 static int
netstat_print_conn(const char * cache,int proto,mdb_walk_cb_t cbfunc,void * cbdata)1085 netstat_print_conn(const char *cache, int proto, mdb_walk_cb_t cbfunc,
1086 void *cbdata)
1087 {
1088 netstat_cb_data_t *ncb = cbdata;
1089
1090 if ((ncb->opts & NETSTAT_VERBOSE) && proto == IPPROTO_TCP)
1091 netstat_tcp_verbose_header_pr();
1092 if (mdb_walk(cache, cbfunc, cbdata) == -1) {
1093 mdb_warn("failed to walk %s", cache);
1094 return (DCMD_ERR);
1095 }
1096 return (DCMD_OK);
1097 }
1098
1099 static int
netstat_print_common(const char * cache,int proto,mdb_walk_cb_t cbfunc,void * cbdata)1100 netstat_print_common(const char *cache, int proto, mdb_walk_cb_t cbfunc,
1101 void *cbdata)
1102 {
1103 netstat_cb_data_t *ncb = cbdata;
1104 int af = ncb->af;
1105 int status = DCMD_OK;
1106
1107 if (af != AF_INET6) {
1108 ncb->af = AF_INET;
1109 netstat_header_v4(proto);
1110 status = netstat_print_conn(cache, proto, cbfunc, cbdata);
1111 }
1112 if (status == DCMD_OK && af != AF_INET) {
1113 ncb->af = AF_INET6;
1114 netstat_header_v6(proto);
1115 status = netstat_print_conn(cache, proto, cbfunc, cbdata);
1116 }
1117 ncb->af = af;
1118 return (status);
1119 }
1120
1121 /*ARGSUSED*/
1122 int
netstat(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1123 netstat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1124 {
1125 uint_t opts = 0;
1126 const char *optf = NULL;
1127 const char *optP = NULL;
1128 netstat_cb_data_t *cbdata;
1129 int status;
1130 int af = 0;
1131
1132 if (mdb_getopts(argc, argv,
1133 'a', MDB_OPT_SETBITS, NETSTAT_ALL, &opts,
1134 'f', MDB_OPT_STR, &optf,
1135 'P', MDB_OPT_STR, &optP,
1136 'r', MDB_OPT_SETBITS, NETSTAT_ROUTE, &opts,
1137 'v', MDB_OPT_SETBITS, NETSTAT_VERBOSE, &opts,
1138 NULL) != argc)
1139 return (DCMD_USAGE);
1140
1141 if (optP != NULL) {
1142 if ((strcmp("tcp", optP) != 0) && (strcmp("udp", optP) != 0) &&
1143 (strcmp("icmp", optP) != 0))
1144 return (DCMD_USAGE);
1145 if (opts & NETSTAT_ROUTE)
1146 return (DCMD_USAGE);
1147 }
1148
1149 if (optf == NULL)
1150 opts |= NETSTAT_V4 | NETSTAT_V6 | NETSTAT_UNIX;
1151 else if (strcmp("inet", optf) == 0)
1152 opts |= NETSTAT_V4;
1153 else if (strcmp("inet6", optf) == 0)
1154 opts |= NETSTAT_V6;
1155 else if (strcmp("unix", optf) == 0)
1156 opts |= NETSTAT_UNIX;
1157 else
1158 return (DCMD_USAGE);
1159
1160 if (opts & NETSTAT_ROUTE) {
1161 if (!(opts & (NETSTAT_V4|NETSTAT_V6)))
1162 return (DCMD_USAGE);
1163 if (opts & NETSTAT_V4) {
1164 opts |= NETSTAT_FIRST;
1165 if (mdb_walk("ip`ire", netstat_irev4_cb, &opts) == -1) {
1166 mdb_warn("failed to walk ip`ire");
1167 return (DCMD_ERR);
1168 }
1169 }
1170 if (opts & NETSTAT_V6) {
1171 opts |= NETSTAT_FIRST;
1172 if (mdb_walk("ip`ire", netstat_irev6_cb, &opts) == -1) {
1173 mdb_warn("failed to walk ip`ire");
1174 return (DCMD_ERR);
1175 }
1176 }
1177 return (DCMD_OK);
1178 }
1179
1180 if ((opts & NETSTAT_UNIX) && (optP == NULL)) {
1181 /* Print Unix Domain Sockets */
1182 mdb_printf("%<u>%-?s %-10s %-?s %-?s %-14s %-14s %s%</u>\n",
1183 "AF_UNIX", "Type", "Vnode", "Conn", "Local Addr",
1184 "Remote Addr", "Zone");
1185
1186 if (mdb_walk("genunix`sonode", netstat_unix_cb, NULL) == -1) {
1187 mdb_warn("failed to walk genunix`sonode");
1188 return (DCMD_ERR);
1189 }
1190 if (!(opts & (NETSTAT_V4 | NETSTAT_V6)))
1191 return (DCMD_OK);
1192 }
1193
1194 cbdata = mdb_alloc(sizeof (netstat_cb_data_t), UM_SLEEP);
1195 cbdata->opts = opts;
1196 if ((optf != NULL) && (opts & NETSTAT_V4))
1197 af = AF_INET;
1198 else if ((optf != NULL) && (opts & NETSTAT_V6))
1199 af = AF_INET6;
1200
1201 cbdata->af = af;
1202 if ((optP == NULL) || (strcmp("tcp", optP) == 0)) {
1203 status = netstat_print_common("tcp_conn_cache", IPPROTO_TCP,
1204 netstat_tcp_cb, cbdata);
1205 if (status != DCMD_OK)
1206 goto out;
1207 }
1208
1209 if ((optP == NULL) || (strcmp("udp", optP) == 0)) {
1210 status = netstat_print_common("udp_conn_cache", IPPROTO_UDP,
1211 netstat_udp_cb, cbdata);
1212 if (status != DCMD_OK)
1213 goto out;
1214 }
1215
1216 if ((optP == NULL) || (strcmp("icmp", optP) == 0)) {
1217 status = netstat_print_common("rawip_conn_cache", IPPROTO_ICMP,
1218 netstat_icmp_cb, cbdata);
1219 if (status != DCMD_OK)
1220 goto out;
1221 }
1222 out:
1223 mdb_free(cbdata, sizeof (netstat_cb_data_t));
1224 return (status);
1225 }
1226
1227 /*
1228 * "::dladm show-bridge" support
1229 */
1230 typedef struct {
1231 uint_t opt_l;
1232 uint_t opt_f;
1233 uint_t opt_t;
1234 const char *name;
1235 clock_t lbolt;
1236 boolean_t found;
1237 uint_t nlinks;
1238 uint_t nfwd;
1239
1240 /*
1241 * These structures are kept inside the 'args' for allocation reasons.
1242 * They're all large data structures (over 1K), and may cause the stack
1243 * to explode. mdb and kmdb will fail in these cases, and thus we
1244 * allocate them from the heap.
1245 */
1246 trill_inst_t ti;
1247 bridge_link_t bl;
1248 mac_impl_t mi;
1249 } show_bridge_args_t;
1250
1251 static void
show_vlans(const uint8_t * vlans)1252 show_vlans(const uint8_t *vlans)
1253 {
1254 int i, bit;
1255 uint8_t val;
1256 int rstart = -1, rnext = -1;
1257
1258 for (i = 0; i < BRIDGE_VLAN_ARR_SIZE; i++) {
1259 val = vlans[i];
1260 if (i == 0)
1261 val &= ~1;
1262 while ((bit = mdb_ffs(val)) != 0) {
1263 bit--;
1264 val &= ~(1 << bit);
1265 bit += i * sizeof (*vlans) * NBBY;
1266 if (bit != rnext) {
1267 if (rnext != -1 && rstart + 1 != rnext)
1268 mdb_printf("-%d", rnext - 1);
1269 if (rstart != -1)
1270 mdb_printf(",");
1271 mdb_printf("%d", bit);
1272 rstart = bit;
1273 }
1274 rnext = bit + 1;
1275 }
1276 }
1277 if (rnext != -1 && rstart + 1 != rnext)
1278 mdb_printf("-%d", rnext - 1);
1279 mdb_printf("\n");
1280 }
1281
1282 /*
1283 * This callback is invoked by a walk of the links attached to a bridge. If
1284 * we're showing link details, then they're printed here. If not, then we just
1285 * count up the links for the bridge summary.
1286 */
1287 static int
do_bridge_links(uintptr_t addr,const void * data,void * ptr)1288 do_bridge_links(uintptr_t addr, const void *data, void *ptr)
1289 {
1290 show_bridge_args_t *args = ptr;
1291 const bridge_link_t *blp = data;
1292 char macaddr[ETHERADDRL * 3];
1293 const char *name;
1294
1295 args->nlinks++;
1296
1297 if (!args->opt_l)
1298 return (WALK_NEXT);
1299
1300 if (mdb_vread(&args->mi, sizeof (args->mi),
1301 (uintptr_t)blp->bl_mh) == -1) {
1302 mdb_warn("cannot read mac data at %p", blp->bl_mh);
1303 name = "?";
1304 } else {
1305 name = args->mi.mi_name;
1306 }
1307
1308 mdb_mac_addr(blp->bl_local_mac, ETHERADDRL, macaddr,
1309 sizeof (macaddr));
1310
1311 mdb_printf("%-?p %-16s %-17s %03X %-4d ", addr, name, macaddr,
1312 blp->bl_flags, blp->bl_pvid);
1313
1314 if (blp->bl_trilldata == NULL) {
1315 switch (blp->bl_state) {
1316 case BLS_BLOCKLISTEN:
1317 name = "BLOCK";
1318 break;
1319 case BLS_LEARNING:
1320 name = "LEARN";
1321 break;
1322 case BLS_FORWARDING:
1323 name = "FWD";
1324 break;
1325 default:
1326 name = "?";
1327 }
1328 mdb_printf("%-5s ", name);
1329 show_vlans(blp->bl_vlans);
1330 } else {
1331 show_vlans(blp->bl_afs);
1332 }
1333
1334 return (WALK_NEXT);
1335 }
1336
1337 /*
1338 * It seems a shame to duplicate this code, but merging it with the link
1339 * printing code above is more trouble than it would be worth.
1340 */
1341 static void
print_link_name(show_bridge_args_t * args,uintptr_t addr,char sep)1342 print_link_name(show_bridge_args_t *args, uintptr_t addr, char sep)
1343 {
1344 const char *name;
1345
1346 if (mdb_vread(&args->bl, sizeof (args->bl), addr) == -1) {
1347 mdb_warn("cannot read bridge link at %p", addr);
1348 return;
1349 }
1350
1351 if (mdb_vread(&args->mi, sizeof (args->mi),
1352 (uintptr_t)args->bl.bl_mh) == -1) {
1353 name = "?";
1354 } else {
1355 name = args->mi.mi_name;
1356 }
1357
1358 mdb_printf("%s%c", name, sep);
1359 }
1360
1361 static int
do_bridge_fwd(uintptr_t addr,const void * data,void * ptr)1362 do_bridge_fwd(uintptr_t addr, const void *data, void *ptr)
1363 {
1364 show_bridge_args_t *args = ptr;
1365 const bridge_fwd_t *bfp = data;
1366 char macaddr[ETHERADDRL * 3];
1367 int i;
1368 #define MAX_FWD_LINKS 16
1369 bridge_link_t *links[MAX_FWD_LINKS];
1370 uint_t nlinks;
1371
1372 args->nfwd++;
1373
1374 if (!args->opt_f)
1375 return (WALK_NEXT);
1376
1377 if ((nlinks = bfp->bf_nlinks) > MAX_FWD_LINKS)
1378 nlinks = MAX_FWD_LINKS;
1379
1380 if (mdb_vread(links, sizeof (links[0]) * nlinks,
1381 (uintptr_t)bfp->bf_links) == -1) {
1382 mdb_warn("cannot read bridge forwarding links at %p",
1383 bfp->bf_links);
1384 return (WALK_ERR);
1385 }
1386
1387 mdb_mac_addr(bfp->bf_dest, ETHERADDRL, macaddr, sizeof (macaddr));
1388
1389 mdb_printf("%-?p %-17s ", addr, macaddr);
1390 if (bfp->bf_flags & BFF_LOCALADDR)
1391 mdb_printf("%-7s", "[self]");
1392 else
1393 mdb_printf("t-%-5d", args->lbolt - bfp->bf_lastheard);
1394 mdb_printf(" %-7u ", bfp->bf_refs);
1395
1396 if (bfp->bf_trill_nick != 0) {
1397 mdb_printf("%d\n", bfp->bf_trill_nick);
1398 } else {
1399 for (i = 0; i < bfp->bf_nlinks; i++) {
1400 print_link_name(args, (uintptr_t)links[i],
1401 i == bfp->bf_nlinks - 1 ? '\n' : ' ');
1402 }
1403 }
1404
1405 return (WALK_NEXT);
1406 }
1407
1408 static int
do_show_bridge(uintptr_t addr,const void * data,void * ptr)1409 do_show_bridge(uintptr_t addr, const void *data, void *ptr)
1410 {
1411 show_bridge_args_t *args = ptr;
1412 bridge_inst_t bi;
1413 const bridge_inst_t *bip;
1414 trill_node_t tn;
1415 trill_sock_t tsp;
1416 trill_nickinfo_t tni;
1417 char bname[MAXLINKNAMELEN];
1418 char macaddr[ETHERADDRL * 3];
1419 uint_t nnicks;
1420 int i;
1421
1422 if (data != NULL) {
1423 bip = data;
1424 } else {
1425 if (mdb_vread(&bi, sizeof (bi), addr) == -1) {
1426 mdb_warn("cannot read bridge instance at %p", addr);
1427 return (WALK_ERR);
1428 }
1429 bip = &bi;
1430 }
1431
1432 (void) strncpy(bname, bip->bi_name, sizeof (bname) - 1);
1433 bname[MAXLINKNAMELEN - 1] = '\0';
1434 i = strlen(bname);
1435 if (i > 1 && bname[i - 1] == '0')
1436 bname[i - 1] = '\0';
1437
1438 if (args->name != NULL && strcmp(args->name, bname) != 0)
1439 return (WALK_NEXT);
1440
1441 args->found = B_TRUE;
1442 args->nlinks = args->nfwd = 0;
1443
1444 if (args->opt_l) {
1445 mdb_printf("%-?s %-16s %-17s %3s %-4s ", "ADDR", "LINK",
1446 "MAC-ADDR", "FLG", "PVID");
1447 if (bip->bi_trilldata == NULL)
1448 mdb_printf("%-5s %s\n", "STATE", "VLANS");
1449 else
1450 mdb_printf("%s\n", "FWD-VLANS");
1451 }
1452
1453 if (!args->opt_f && !args->opt_t &&
1454 mdb_pwalk("list", do_bridge_links, args,
1455 addr + offsetof(bridge_inst_t, bi_links)) != DCMD_OK)
1456 return (WALK_ERR);
1457
1458 if (args->opt_f)
1459 mdb_printf("%-?s %-17s %-7s %-7s %s\n", "ADDR", "DEST", "TIME",
1460 "REFS", "OUTPUT");
1461
1462 if (!args->opt_l && !args->opt_t &&
1463 mdb_pwalk("avl", do_bridge_fwd, args,
1464 addr + offsetof(bridge_inst_t, bi_fwd)) != DCMD_OK)
1465 return (WALK_ERR);
1466
1467 nnicks = 0;
1468 if (bip->bi_trilldata != NULL && !args->opt_l && !args->opt_f) {
1469 if (mdb_vread(&args->ti, sizeof (args->ti),
1470 (uintptr_t)bip->bi_trilldata) == -1) {
1471 mdb_warn("cannot read trill instance at %p",
1472 bip->bi_trilldata);
1473 return (WALK_ERR);
1474 }
1475 if (args->opt_t)
1476 mdb_printf("%-?s %-5s %-17s %s\n", "ADDR",
1477 "NICK", "NEXT-HOP", "LINK");
1478 for (i = 0; i < RBRIDGE_NICKNAME_MAX; i++) {
1479 if (args->ti.ti_nodes[i] == NULL)
1480 continue;
1481 if (args->opt_t) {
1482 if (mdb_vread(&tn, sizeof (tn),
1483 (uintptr_t)args->ti.ti_nodes[i]) == -1) {
1484 mdb_warn("cannot read trill node %d at "
1485 "%p", i, args->ti.ti_nodes[i]);
1486 return (WALK_ERR);
1487 }
1488 if (mdb_vread(&tni, sizeof (tni),
1489 (uintptr_t)tn.tn_ni) == -1) {
1490 mdb_warn("cannot read trill node info "
1491 "%d at %p", i, tn.tn_ni);
1492 return (WALK_ERR);
1493 }
1494 mdb_mac_addr(tni.tni_adjsnpa, ETHERADDRL,
1495 macaddr, sizeof (macaddr));
1496 if (tni.tni_nick == args->ti.ti_nick) {
1497 (void) strcpy(macaddr, "[self]");
1498 }
1499 mdb_printf("%-?p %-5u %-17s ",
1500 args->ti.ti_nodes[i], tni.tni_nick,
1501 macaddr);
1502 if (tn.tn_tsp != NULL) {
1503 if (mdb_vread(&tsp, sizeof (tsp),
1504 (uintptr_t)tn.tn_tsp) == -1) {
1505 mdb_warn("cannot read trill "
1506 "socket info at %p",
1507 tn.tn_tsp);
1508 return (WALK_ERR);
1509 }
1510 if (tsp.ts_link != NULL) {
1511 print_link_name(args,
1512 (uintptr_t)tsp.ts_link,
1513 '\n');
1514 continue;
1515 }
1516 }
1517 mdb_printf("--\n");
1518 } else {
1519 nnicks++;
1520 }
1521 }
1522 } else {
1523 if (args->opt_t)
1524 mdb_printf("bridge is not running TRILL\n");
1525 }
1526
1527 if (!args->opt_l && !args->opt_f && !args->opt_t) {
1528 mdb_printf("%-?p %-7s %-16s %-7u %-7u", addr,
1529 bip->bi_trilldata == NULL ? "stp" : "trill", bname,
1530 args->nlinks, args->nfwd);
1531 if (bip->bi_trilldata != NULL)
1532 mdb_printf(" %-7u %u\n", nnicks, args->ti.ti_nick);
1533 else
1534 mdb_printf(" %-7s %s\n", "--", "--");
1535 }
1536 return (WALK_NEXT);
1537 }
1538
1539 static int
dladm_show_bridge(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1540 dladm_show_bridge(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1541 {
1542 show_bridge_args_t *args;
1543 GElf_Sym sym;
1544 int i;
1545
1546 args = mdb_zalloc(sizeof (*args), UM_SLEEP);
1547
1548 i = mdb_getopts(argc, argv,
1549 'l', MDB_OPT_SETBITS, 1, &args->opt_l,
1550 'f', MDB_OPT_SETBITS, 1, &args->opt_f,
1551 't', MDB_OPT_SETBITS, 1, &args->opt_t,
1552 NULL);
1553
1554 argc -= i;
1555 argv += i;
1556
1557 if (argc > 1 || (argc == 1 && argv[0].a_type != MDB_TYPE_STRING)) {
1558 mdb_free(args, sizeof (*args));
1559 return (DCMD_USAGE);
1560 }
1561 if (argc == 1)
1562 args->name = argv[0].a_un.a_str;
1563
1564 if ((args->lbolt = mdb_get_lbolt()) == -1) {
1565 mdb_warn("failed to read lbolt");
1566 goto err;
1567 }
1568
1569 if (flags & DCMD_ADDRSPEC) {
1570 if (args->name != NULL) {
1571 mdb_printf("bridge name and address are mutually "
1572 "exclusive\n");
1573 goto err;
1574 }
1575 if (!args->opt_l && !args->opt_f && !args->opt_t)
1576 mdb_printf("%-?s %-7s %-16s %-7s %-7s\n", "ADDR",
1577 "PROTECT", "NAME", "NLINKS", "NFWD");
1578 if (do_show_bridge(addr, NULL, args) != WALK_NEXT)
1579 goto err;
1580 mdb_free(args, sizeof (*args));
1581 return (DCMD_OK);
1582 } else {
1583 if ((args->opt_l || args->opt_f || args->opt_t) &&
1584 args->name == NULL) {
1585 mdb_printf("need bridge name or address with -[lft]\n");
1586 goto err;
1587 }
1588 if (mdb_lookup_by_obj("bridge", "inst_list", &sym) == -1) {
1589 mdb_warn("failed to find 'bridge`inst_list'");
1590 goto err;
1591 }
1592 if (!args->opt_l && !args->opt_f && !args->opt_t)
1593 mdb_printf("%-?s %-7s %-16s %-7s %-7s %-7s %s\n",
1594 "ADDR", "PROTECT", "NAME", "NLINKS", "NFWD",
1595 "NNICKS", "NICK");
1596 if (mdb_pwalk("list", do_show_bridge, args,
1597 (uintptr_t)sym.st_value) != DCMD_OK)
1598 goto err;
1599 if (!args->found && args->name != NULL) {
1600 mdb_printf("bridge instance %s not found\n",
1601 args->name);
1602 goto err;
1603 }
1604 mdb_free(args, sizeof (*args));
1605 return (DCMD_OK);
1606 }
1607
1608 err:
1609 mdb_free(args, sizeof (*args));
1610 return (DCMD_ERR);
1611 }
1612
1613 /*
1614 * Support for the "::dladm" dcmd
1615 */
1616 int
dladm(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1617 dladm(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1618 {
1619 if (argc < 1 || argv[0].a_type != MDB_TYPE_STRING)
1620 return (DCMD_USAGE);
1621
1622 /*
1623 * This could be a bit more elaborate, once we support more of the
1624 * dladm show-* subcommands.
1625 */
1626 argc--;
1627 argv++;
1628 if (strcmp(argv[-1].a_un.a_str, "show-bridge") == 0)
1629 return (dladm_show_bridge(addr, flags, argc, argv));
1630
1631 return (DCMD_USAGE);
1632 }
1633
1634 void
dladm_help(void)1635 dladm_help(void)
1636 {
1637 mdb_printf("Subcommands:\n"
1638 " show-bridge [-flt] [<name>]\n"
1639 "\t Show bridge information; -l for links and -f for "
1640 "forwarding\n"
1641 "\t entries, and -t for TRILL nicknames. Address is required "
1642 "if name\n"
1643 "\t is not specified.\n");
1644 }
1645