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