/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MIH2MIO(mihp) (&(mihp)->mh_o) #define ADDR_V6_WIDTH 23 #define ADDR_V4_WIDTH 15 #define NETSTAT_ALL 0x01 #define NETSTAT_VERBOSE 0x02 #define NETSTAT_ROUTE 0x04 #define NETSTAT_V4 0x08 #define NETSTAT_V6 0x10 #define NETSTAT_UNIX 0x20 #define NETSTAT_FIRST 0x80000000u /* * Print an IPv4 address and port number in a compact and easy to read format * The arguments are in network byte order */ static void net_ipv4addrport_pr(const in6_addr_t *nipv6addr, in_port_t nport) { uint32_t naddr = V4_PART_OF_V6((*nipv6addr)); mdb_nhconvert(&nport, &nport, sizeof (nport)); mdb_printf("%*I.%-5hu", ADDR_V4_WIDTH, naddr, nport); } /* * Print an IPv6 address and port number in a compact and easy to read format * The arguments are in network byte order */ static void net_ipv6addrport_pr(const in6_addr_t *naddr, in_port_t nport) { mdb_nhconvert(&nport, &nport, sizeof (nport)); mdb_printf("%*N.%-5hu", ADDR_V6_WIDTH, naddr, nport); } static int net_tcp_active(const tcp_t *tcp) { return (tcp->tcp_state >= TCPS_ESTABLISHED); } static int net_tcp_ipv4(const tcp_t *tcp) { return ((tcp->tcp_ipversion == IPV4_VERSION) || (IN6_IS_ADDR_UNSPECIFIED(&tcp->tcp_ip_src_v6) && (tcp->tcp_state <= TCPS_LISTEN))); } static int net_tcp_ipv6(const tcp_t *tcp) { return (tcp->tcp_ipversion == IPV6_VERSION); } static int net_udp_active(const udp_t *udp) { return ((udp->udp_state == TS_IDLE) || (udp->udp_state == TS_DATA_XFER)); } static int net_udp_ipv4(const udp_t *udp) { return ((udp->udp_ipversion == IPV4_VERSION) || (IN6_IS_ADDR_UNSPECIFIED(&udp->udp_v6src) && (udp->udp_state <= TS_IDLE))); } static int net_udp_ipv6(const udp_t *udp) { return (udp->udp_ipversion == IPV6_VERSION); } int sonode_walk_init(mdb_walk_state_t *wsp) { if (wsp->walk_addr == NULL) { GElf_Sym sym; struct socklist *slp; if (mdb_lookup_by_obj("sockfs", "socklist", &sym) == -1) { mdb_warn("failed to lookup sockfs`socklist"); return (WALK_ERR); } slp = (struct socklist *)(uintptr_t)sym.st_value; if (mdb_vread(&wsp->walk_addr, sizeof (wsp->walk_addr), (uintptr_t)&slp->sl_list) == -1) { mdb_warn("failed to read address of initial sonode " "at %p", &slp->sl_list); return (WALK_ERR); } } wsp->walk_data = mdb_alloc(sizeof (struct sonode), UM_SLEEP); return (WALK_NEXT); } int sonode_walk_step(mdb_walk_state_t *wsp) { int status; struct sonode *sonodep; if (wsp->walk_addr == NULL) return (WALK_DONE); if (mdb_vread(wsp->walk_data, sizeof (struct sonode), wsp->walk_addr) == -1) { mdb_warn("failed to read sonode at %p", wsp->walk_addr); return (WALK_ERR); } status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, wsp->walk_cbdata); sonodep = wsp->walk_data; wsp->walk_addr = (uintptr_t)sonodep->so_next; return (status); } void sonode_walk_fini(mdb_walk_state_t *wsp) { mdb_free(wsp->walk_data, sizeof (struct sonode)); } struct mi_walk_data { uintptr_t mi_wd_miofirst; MI_O mi_wd_miodata; }; int mi_walk_init(mdb_walk_state_t *wsp) { struct mi_walk_data *wdp; if (wsp->walk_addr == NULL) { mdb_warn("mi doesn't support global walks\n"); return (WALK_ERR); } wdp = mdb_alloc(sizeof (struct mi_walk_data), UM_SLEEP); /* So that we do not immediately return WALK_DONE below */ wdp->mi_wd_miofirst = NULL; wsp->walk_data = wdp; return (WALK_NEXT); } int mi_walk_step(mdb_walk_state_t *wsp) { struct mi_walk_data *wdp = wsp->walk_data; MI_OP miop = &wdp->mi_wd_miodata; int status; /* Always false in the first iteration */ if ((wsp->walk_addr == (uintptr_t)NULL) || (wsp->walk_addr == wdp->mi_wd_miofirst)) { return (WALK_DONE); } if (mdb_vread(miop, sizeof (MI_O), wsp->walk_addr) == -1) { mdb_warn("failed to read MI object at %p", wsp->walk_addr); return (WALK_ERR); } status = wsp->walk_callback(wsp->walk_addr, miop, wsp->walk_cbdata); /* Only true in the first iteration */ if (wdp->mi_wd_miofirst == NULL) wdp->mi_wd_miofirst = wsp->walk_addr; wsp->walk_addr = (uintptr_t)miop->mi_o_next; return (status); } void mi_walk_fini(mdb_walk_state_t *wsp) { mdb_free(wsp->walk_data, sizeof (struct mi_walk_data)); } typedef struct mi_payload_walk_data_s { uintptr_t mi_pwd_first; void *mi_pwd_data; } mi_payload_walk_data_t; static void delete_mi_payload_walk_data(mi_payload_walk_data_t *pwdp, size_t payload_size) { mdb_free(pwdp->mi_pwd_data, payload_size); mdb_free(pwdp, sizeof (mi_payload_walk_data_t)); } typedef struct mi_payload_walk_arg_s { const char *mi_pwa_obj; /* load object of mi_o_head_t * */ const char *mi_pwa_sym; /* symbol name of mi_o_head_t * */ const size_t mi_pwa_size; /* size of mi payload */ const uint_t mi_pwa_flags; /* device and/or module */ } mi_payload_walk_arg_t; #define MI_PAYLOAD_DEVICE 0x1 #define MI_PAYLOAD_MODULE 0x2 int mi_payload_walk_init(mdb_walk_state_t *wsp) { const mi_payload_walk_arg_t *arg = wsp->walk_arg; mi_payload_walk_data_t *pwdp; GElf_Sym sym; mi_head_t *mihp; /* Determine the address to start or end the walk with */ if (mdb_lookup_by_obj(arg->mi_pwa_obj, arg->mi_pwa_sym, &sym) == -1) { mdb_warn("failed to lookup %s`%s", arg->mi_pwa_obj, arg->mi_pwa_sym); return (WALK_ERR); } if (mdb_vread(&mihp, sizeof (mihp), (uintptr_t)sym.st_value) == -1) { mdb_warn("failed to read address of global MI Head " "mi_o_head_t at %p", (uintptr_t)sym.st_value); return (WALK_ERR); } pwdp = mdb_alloc(sizeof (mi_payload_walk_data_t), UM_SLEEP); pwdp->mi_pwd_data = mdb_alloc(arg->mi_pwa_size, UM_SLEEP); wsp->walk_data = pwdp; if (wsp->walk_addr == NULL) { /* Do not immediately return WALK_DONE below */ pwdp->mi_pwd_first = NULL; /* We determined where to begin */ wsp->walk_addr = (uintptr_t)MIH2MIO(mihp); } else { /* Do not cycle through all of the MI_O objects */ pwdp->mi_pwd_first = (uintptr_t)MIH2MIO(mihp); /* We were given where to begin */ wsp->walk_addr = (uintptr_t)((MI_OP)wsp->walk_addr - 1); } if (mdb_layered_walk("genunix`mi", wsp) == -1) { mdb_warn("failed to walk genunix`mi"); delete_mi_payload_walk_data(pwdp, arg->mi_pwa_size); return (WALK_ERR); } return (WALK_NEXT); } int mi_payload_walk_step(mdb_walk_state_t *wsp) { const mi_payload_walk_arg_t *arg = wsp->walk_arg; mi_payload_walk_data_t *pwdp = wsp->walk_data; void *payload = pwdp->mi_pwd_data; uintptr_t payload_kaddr = (uintptr_t)((MI_OP)wsp->walk_addr + 1); const MI_O *mio = wsp->walk_layer; /* If this is a local walk, prevent cycling */ if (wsp->walk_addr == pwdp->mi_pwd_first) return (WALK_DONE); /* * This was a global walk, prevent reading this payload as the * initial MI_O is the head of the list and is not the header * to a valid payload */ if (pwdp->mi_pwd_first == NULL) { pwdp->mi_pwd_first = wsp->walk_addr; return (WALK_NEXT); } if (mio->mi_o_isdev == B_FALSE) { /* mio is a module */ if (!(arg->mi_pwa_flags & MI_PAYLOAD_MODULE)) return (WALK_NEXT); } else { /* mio is a device */ if (!(arg->mi_pwa_flags & MI_PAYLOAD_DEVICE)) return (WALK_NEXT); } if (mdb_vread(payload, arg->mi_pwa_size, payload_kaddr) == -1) { mdb_warn("failed to read payload at %p", payload_kaddr); return (WALK_ERR); } return (wsp->walk_callback(payload_kaddr, payload, wsp->walk_cbdata)); } void mi_payload_walk_fini(mdb_walk_state_t *wsp) { const mi_payload_walk_arg_t *arg = wsp->walk_arg; delete_mi_payload_walk_data(wsp->walk_data, arg->mi_pwa_size); } const mi_payload_walk_arg_t mi_ar_arg = { "arp", "ar_g_head", sizeof (ar_t), MI_PAYLOAD_DEVICE | MI_PAYLOAD_MODULE }; const mi_payload_walk_arg_t mi_icmp_arg = { "icmp", "icmp_g_head", sizeof (icmp_t), MI_PAYLOAD_DEVICE | MI_PAYLOAD_MODULE }; const mi_payload_walk_arg_t mi_ill_arg = { "ip", "ip_g_head", sizeof (ill_t), MI_PAYLOAD_MODULE }; int sonode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { const char *optf = NULL; const char *optt = NULL; const char *optp = NULL; int family, type, proto; int filter = 0; struct sonode so; if (!(flags & DCMD_ADDRSPEC)) { if (mdb_walk_dcmd("genunix`sonode", "genunix`sonode", argc, argv) == -1) { mdb_warn("failed to walk sonode"); return (DCMD_ERR); } return (DCMD_OK); } if (mdb_getopts(argc, argv, 'f', MDB_OPT_STR, &optf, 't', MDB_OPT_STR, &optt, 'p', MDB_OPT_STR, &optp, NULL) != argc) return (DCMD_USAGE); if (optf != NULL) { if (strcmp("inet", optf) == 0) family = AF_INET; else if (strcmp("inet6", optf) == 0) family = AF_INET6; else if (strcmp("unix", optf) == 0) family = AF_UNIX; else family = mdb_strtoull(optf); filter = 1; } if (optt != NULL) { if (strcmp("stream", optt) == 0) type = SOCK_STREAM; else if (strcmp("dgram", optt) == 0) type = SOCK_DGRAM; else if (strcmp("raw", optt) == 0) type = SOCK_RAW; else type = mdb_strtoull(optt); filter = 1; } if (optp != NULL) { proto = mdb_strtoull(optp); filter = 1; } if (DCMD_HDRSPEC(flags) && !filter) { mdb_printf("%%-?s Family Type Proto State Mode Flag " "AccessVP%\n", "Sonode:"); } if (mdb_vread(&so, sizeof (so), addr) == -1) { mdb_warn("failed to read sonode at %p", addr); return (DCMD_ERR); } if ((optf != NULL) && (so.so_family != family)) return (DCMD_OK); if ((optt != NULL) && (so.so_type != type)) return (DCMD_OK); if ((optp != NULL) && (so.so_protocol != proto)) return (DCMD_OK); if (filter) { mdb_printf("%0?p\n", addr); return (DCMD_OK); } mdb_printf("%0?p ", addr); switch (so.so_family) { case AF_UNIX: mdb_printf("unix "); break; case AF_INET: mdb_printf("inet "); break; case AF_INET6: mdb_printf("inet6 "); break; default: mdb_printf("%6hi", so.so_family); } switch (so.so_type) { case SOCK_STREAM: mdb_printf(" strm"); break; case SOCK_DGRAM: mdb_printf(" dgrm"); break; case SOCK_RAW: mdb_printf(" raw "); break; default: mdb_printf(" %4hi", so.so_type); } mdb_printf(" %5hi %05x %04x %04hx %0?p\n", so.so_protocol, so.so_state, so.so_mode, so.so_flag, so.so_accessvp); return (DCMD_OK); } #define MI_PAYLOAD 0x1 #define MI_DEVICE 0x2 #define MI_MODULE 0x4 int mi(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { uint_t opts = 0; MI_O mio; if (!(flags & DCMD_ADDRSPEC)) return (DCMD_USAGE); if (mdb_getopts(argc, argv, 'p', MDB_OPT_SETBITS, MI_PAYLOAD, &opts, 'd', MDB_OPT_SETBITS, MI_DEVICE, &opts, 'm', MDB_OPT_SETBITS, MI_MODULE, &opts, NULL) != argc) return (DCMD_USAGE); if ((opts & (MI_DEVICE | MI_MODULE)) == (MI_DEVICE | MI_MODULE)) { mdb_warn("at most one filter, d for devices or m " "for modules, may be specified\n"); return (DCMD_USAGE); } if ((opts == 0) && (DCMD_HDRSPEC(flags))) { mdb_printf("%%-?s %-?s %-?s IsDev Dev%\n", "MI_O", "Next", "Prev"); } if (mdb_vread(&mio, sizeof (mio), addr) == -1) { mdb_warn("failed to read mi object MI_O at %p", addr); return (DCMD_ERR); } if (opts != 0) { if (mio.mi_o_isdev == B_FALSE) { /* mio is a module */ if (!(opts & MI_MODULE) && (opts & MI_DEVICE)) return (DCMD_OK); } else { /* mio is a device */ if (!(opts & MI_DEVICE) && (opts & MI_MODULE)) return (DCMD_OK); } if (opts & MI_PAYLOAD) mdb_printf("%p\n", addr + sizeof (MI_O)); else mdb_printf("%p\n", addr); return (DCMD_OK); } mdb_printf("%0?p %0?p %0?p ", addr, mio.mi_o_next, mio.mi_o_prev); if (mio.mi_o_isdev == B_FALSE) mdb_printf("FALSE"); else mdb_printf("TRUE "); mdb_printf(" %0?p\n", mio.mi_o_dev); return (DCMD_OK); } static void netstat_tcp_verbose_pr(const tcp_t *tcp) { mdb_printf(" %5i %08x %08x %5i %08x %08x %5li %5i\n", tcp->tcp_swnd, tcp->tcp_snxt, tcp->tcp_suna, tcp->tcp_rwnd, tcp->tcp_rack, tcp->tcp_rnxt, tcp->tcp_rto, tcp->tcp_mss); } /*ARGSUSED*/ static int netstat_tcp_cb(uintptr_t kaddr, const void *walk_data, void *cb_data, int af) { const uintptr_t opts = (uintptr_t)cb_data; static size_t itc_size = 0; uintptr_t tcp_kaddr; conn_t *connp; tcp_t *tcp; if (itc_size == 0) { mdb_ctf_id_t id; if (mdb_ctf_lookup_by_name("itc_t", &id) != 0) { mdb_warn("failed to lookup type 'itc_t'"); return (WALK_ERR); } itc_size = mdb_ctf_type_size(id); } connp = (conn_t *)mdb_alloc(itc_size, UM_SLEEP | UM_GC); if (mdb_vread(connp, itc_size, kaddr) == -1) { mdb_warn("failed to read connection info at %p", kaddr); return (WALK_ERR); } tcp_kaddr = (uintptr_t)connp->conn_tcp; tcp = (tcp_t *)((uintptr_t)connp + (tcp_kaddr - kaddr)); if ((uintptr_t)tcp < (uintptr_t)connp || (uintptr_t)(tcp + 1) > (uintptr_t)connp + itc_size || (uintptr_t)tcp->tcp_connp != kaddr) { mdb_warn("conn_tcp %p is invalid", tcp_kaddr); return (WALK_NEXT); } connp->conn_tcp = tcp; tcp->tcp_connp = connp; if (!((opts & NETSTAT_ALL) || net_tcp_active(tcp)) || (af == AF_INET && !net_tcp_ipv4(tcp)) || (af == AF_INET6 && !net_tcp_ipv6(tcp))) { return (WALK_NEXT); } mdb_printf("%0?p %2i ", tcp_kaddr, tcp->tcp_state); if (af == AF_INET) { net_ipv4addrport_pr(&tcp->tcp_ip_src_v6, tcp->tcp_lport); mdb_printf(" "); net_ipv4addrport_pr(&tcp->tcp_remote_v6, tcp->tcp_fport); } else if (af == AF_INET6) { net_ipv6addrport_pr(&tcp->tcp_ip_src_v6, tcp->tcp_lport); mdb_printf(" "); net_ipv6addrport_pr(&tcp->tcp_remote_v6, tcp->tcp_fport); } mdb_printf(" %4i\n", connp->conn_zoneid); if (opts & NETSTAT_VERBOSE) netstat_tcp_verbose_pr(tcp); return (WALK_NEXT); } static int netstat_tcpv4_cb(uintptr_t kaddr, const void *walk_data, void *cb_data) { return (netstat_tcp_cb(kaddr, walk_data, cb_data, AF_INET)); } static int netstat_tcpv6_cb(uintptr_t kaddr, const void *walk_data, void *cb_data) { return (netstat_tcp_cb(kaddr, walk_data, cb_data, AF_INET6)); } /*ARGSUSED*/ static int netstat_udp_cb(uintptr_t kaddr, const void *walk_data, void *cb_data, int af) { const uintptr_t opts = (uintptr_t)cb_data; udp_t udp; conn_t connp; if (mdb_vread(&udp, sizeof (udp_t), kaddr) == -1) { mdb_warn("failed to read udp at %p", kaddr); return (WALK_ERR); } if (mdb_vread(&connp, sizeof (conn_t), (uintptr_t)udp.udp_connp) == -1) { mdb_warn("failed to read udp_connp at %p", (uintptr_t)udp.udp_connp); return (WALK_ERR); } if (!((opts & NETSTAT_ALL) || net_udp_active(&udp)) || (af == AF_INET && !net_udp_ipv4(&udp)) || (af == AF_INET6 && !net_udp_ipv6(&udp))) { return (WALK_NEXT); } mdb_printf("%0?p %2i ", kaddr, udp.udp_state); if (af == AF_INET) { net_ipv4addrport_pr(&udp.udp_v6src, udp.udp_port); mdb_printf(" "); net_ipv4addrport_pr(&udp.udp_v6dst, udp.udp_dstport); } else if (af == AF_INET6) { net_ipv6addrport_pr(&udp.udp_v6src, udp.udp_port); mdb_printf(" "); net_ipv6addrport_pr(&udp.udp_v6dst, udp.udp_dstport); } mdb_printf(" %4i\n", connp.conn_zoneid); return (WALK_NEXT); } static int netstat_udpv4_cb(uintptr_t kaddr, const void *walk_data, void *cb_data) { return (netstat_udp_cb(kaddr, walk_data, cb_data, AF_INET)); } static int netstat_udpv6_cb(uintptr_t kaddr, const void *walk_data, void *cb_data) { return (netstat_udp_cb(kaddr, walk_data, cb_data, AF_INET6)); } /* * print the address of a unix domain socket * * so is the address of a AF_UNIX struct sonode in mdb's address space * soa is the address of the struct soaddr to print * * returns 0 on success, -1 otherwise */ static int netstat_unix_name_pr(const struct sonode *so, const struct soaddr *soa) { const char none[] = " (none)"; if ((so->so_state & SS_ISBOUND) && (soa->soa_len != 0)) { if (so->so_state & SS_FADDR_NOXLATE) { mdb_printf("%-14s ", " (socketpair)"); } else { if (soa->soa_len > sizeof (sa_family_t)) { char addr[MAXPATHLEN + 1]; if (mdb_readstr(addr, sizeof (addr), (uintptr_t)&soa->soa_sa->sa_data) == -1) { mdb_warn("failed to read unix address " "at %p", &soa->soa_sa->sa_data); return (-1); } mdb_printf("%-14s ", addr); } else { mdb_printf("%-14s ", none); } } } else { mdb_printf("%-14s ", none); } return (0); } /* based on sockfs_snapshot */ /*ARGSUSED*/ static int netstat_unix_cb(uintptr_t kaddr, const void *walk_data, void *cb_data) { const struct sonode *so = walk_data; if (so->so_accessvp == NULL) return (WALK_NEXT); if (so->so_family != AF_UNIX) { mdb_warn("sonode of family %hi at %p\n", so->so_family, kaddr); return (WALK_ERR); } mdb_printf("%-?p ", kaddr); switch (so->so_serv_type) { case T_CLTS: mdb_printf("%-10s ", "dgram"); break; case T_COTS: mdb_printf("%-10s ", "stream"); break; case T_COTS_ORD: mdb_printf("%-10s ", "stream-ord"); break; default: mdb_printf("%-10i ", so->so_serv_type); } if ((so->so_state & SS_ISBOUND) && (so->so_ux_laddr.soua_magic == SOU_MAGIC_EXPLICIT)) { mdb_printf("%0?p ", so->so_ux_laddr.soua_vp); } else { mdb_printf("%0?p ", NULL); } if ((so->so_state & SS_ISCONNECTED) && (so->so_ux_faddr.soua_magic == SOU_MAGIC_EXPLICIT)) { mdb_printf("%0?p ", so->so_ux_faddr.soua_vp); } else { mdb_printf("%0?p ", NULL); } if (netstat_unix_name_pr(so, &so->so_laddr) == -1) return (WALK_ERR); if (netstat_unix_name_pr(so, &so->so_faddr) == -1) return (WALK_ERR); mdb_printf("%4i\n", so->so_zoneid); return (WALK_NEXT); } static void netstat_tcp_verbose_header_pr(void) { mdb_printf(" %%-5s %-8s %-8s %-5s %-8s %-8s %5s %5s%\n", "Swind", "Snext", "Suna", "Rwind", "Rack", "Rnext", "Rto", "Mss"); } static void get_ifname(const ire_t *ire, char *intf) { ill_t ill; *intf = '\0'; if (ire->ire_type == IRE_CACHE) { queue_t stq; if (mdb_vread(&stq, sizeof (stq), (uintptr_t)ire->ire_stq) == -1) return; if (mdb_vread(&ill, sizeof (ill), (uintptr_t)stq.q_ptr) == -1) return; (void) mdb_readstr(intf, MIN(LIFNAMSIZ, ill.ill_name_length), (uintptr_t)ill.ill_name); } else if (ire->ire_ipif != NULL) { ipif_t ipif; char *cp; if (mdb_vread(&ipif, sizeof (ipif), (uintptr_t)ire->ire_ipif) == -1) return; if (mdb_vread(&ill, sizeof (ill), (uintptr_t)ipif.ipif_ill) == -1) return; (void) mdb_readstr(intf, MIN(LIFNAMSIZ, ill.ill_name_length), (uintptr_t)ill.ill_name); if (ipif.ipif_id != 0) { cp = intf + strlen(intf); (void) mdb_snprintf(cp, LIFNAMSIZ + 1 - (cp - intf), ":%u", ipif.ipif_id); } } } static void get_v4flags(const ire_t *ire, char *flags) { (void) strcpy(flags, "U"); if (ire->ire_type == IRE_DEFAULT || ire->ire_type == IRE_PREFIX || ire->ire_type == IRE_HOST || ire->ire_type == IRE_HOST_REDIRECT) (void) strcat(flags, "G"); if (ire->ire_mask == IP_HOST_MASK) (void) strcat(flags, "H"); if (ire->ire_type == IRE_HOST_REDIRECT) (void) strcat(flags, "D"); if (ire->ire_type == IRE_CACHE) (void) strcat(flags, "A"); if (ire->ire_type == IRE_BROADCAST) (void) strcat(flags, "B"); if (ire->ire_type == IRE_LOCAL) (void) strcat(flags, "L"); if (ire->ire_flags & RTF_MULTIRT) (void) strcat(flags, "M"); if (ire->ire_flags & RTF_SETSRC) (void) strcat(flags, "S"); } static int ip_mask_to_plen(ipaddr_t mask) { int i; if (mask == 0) return (0); for (i = 32; i > 0; i--, mask >>= 1) if (mask & 1) break; return (i); } static int netstat_irev4_cb(uintptr_t kaddr, const void *walk_data, void *cb_data) { const ire_t *ire = walk_data; uint_t *opts = cb_data; ipaddr_t gate; char flags[10], intf[LIFNAMSIZ + 1]; if (ire->ire_ipversion != IPV4_VERSION || ire->ire_in_src_addr != 0 || ire->ire_in_ill != NULL) return (WALK_NEXT); if (!(*opts & NETSTAT_ALL) && (ire->ire_type == IRE_CACHE || ire->ire_type == IRE_BROADCAST || ire->ire_type == IRE_LOCAL)) return (WALK_NEXT); if (*opts & NETSTAT_FIRST) { *opts &= ~NETSTAT_FIRST; mdb_printf("%%s Table: IPv4%\n", (*opts & NETSTAT_VERBOSE) ? "IRE" : "Routing"); if (*opts & NETSTAT_VERBOSE) { mdb_printf("%%-?s %-*s %-*s %-*s Device Mxfrg Rtt " " Ref Flg Out In/Fwd%\n", "Address", ADDR_V4_WIDTH, "Destination", ADDR_V4_WIDTH, "Mask", ADDR_V4_WIDTH, "Gateway"); } else { mdb_printf("%%-?s %-*s %-*s Flags Ref Use " "Interface%\n", "Address", ADDR_V4_WIDTH, "Destination", ADDR_V4_WIDTH, "Gateway"); } } gate = (ire->ire_type & (IRE_INTERFACE|IRE_LOOPBACK|IRE_BROADCAST)) ? ire->ire_src_addr : ire->ire_gateway_addr; get_v4flags(ire, flags); get_ifname(ire, intf); if (*opts & NETSTAT_VERBOSE) { mdb_printf("%?p %-*I %-*I %-*I %-6s %5u%c %4u %3u %-3s %5u " "%u\n", kaddr, ADDR_V4_WIDTH, ire->ire_addr, ADDR_V4_WIDTH, ire->ire_mask, ADDR_V4_WIDTH, gate, intf, ire->ire_max_frag, ire->ire_frag_flag ? '*' : ' ', ire->ire_uinfo.iulp_rtt, ire->ire_refcnt, flags, ire->ire_ob_pkt_count, ire->ire_ib_pkt_count); } else { mdb_printf("%?p %-*I %-*I %-5s %4u %5u %s\n", kaddr, ADDR_V4_WIDTH, ire->ire_addr, ADDR_V4_WIDTH, gate, flags, ire->ire_refcnt, ire->ire_ob_pkt_count + ire->ire_ib_pkt_count, intf); } return (WALK_NEXT); } static int netstat_irev4src_cb(uintptr_t kaddr, const void *walk_data, void *cb_data) { const ire_t *ire = walk_data; uint_t *opts = cb_data; ipaddr_t gate; char flags[10], intf[LIFNAMSIZ + 1], srcif[LIFNAMSIZ + 1]; char dest[ADDR_V4_WIDTH + 3 + 1]; ill_t ill; if (ire->ire_ipversion != IPV4_VERSION || (ire->ire_in_src_addr == 0 && ire->ire_in_ill == NULL)) return (WALK_NEXT); if (!(*opts & NETSTAT_ALL) && (ire->ire_type == IRE_CACHE || ire->ire_type == IRE_BROADCAST || ire->ire_type == IRE_LOCAL)) return (WALK_NEXT); if (*opts & NETSTAT_FIRST) { *opts &= ~NETSTAT_FIRST; mdb_printf("\n%%s Table: IPv4 Source-Specific%\n", (*opts & NETSTAT_VERBOSE) ? "IRE" : "Routing"); if (*opts & NETSTAT_VERBOSE) { mdb_printf("%%-?s %-*s In If %-*s %-*s " "Out If Mxfrg Rtt Ref Flg Out In/Fwd" "%\n", "Address", ADDR_V4_WIDTH+3, "Destination", ADDR_V4_WIDTH, "Source", ADDR_V4_WIDTH, "Gateway"); } else { mdb_printf("%%-?s %-*s In If %-*s %-*s Flags " "Ref Use Out If%\n", "Address", ADDR_V4_WIDTH+3, "Destination", ADDR_V4_WIDTH, "Source", ADDR_V4_WIDTH, "Gateway"); } } gate = (ire->ire_type & (IRE_INTERFACE|IRE_LOOPBACK|IRE_BROADCAST)) ? ire->ire_src_addr : ire->ire_gateway_addr; get_v4flags(ire, flags); get_ifname(ire, intf); srcif[0] = '\0'; if (mdb_vread(&ill, sizeof (ill), (uintptr_t)ire->ire_in_ill) != -1) (void) mdb_readstr(srcif, MIN(LIFNAMSIZ, ill.ill_name_length), (uintptr_t)ill.ill_name); if (ire->ire_in_src_addr != 0 && ire->ire_addr == 0 && ire->ire_mask == 0) strcpy(dest, " --"); else mdb_snprintf(dest, sizeof (dest), "%I/%d", ire->ire_addr, ip_mask_to_plen(ire->ire_mask)); if (*opts & NETSTAT_VERBOSE) { mdb_printf("%?p %-*s %-11s %-*I %-*I %-11s %5u%c %4u %3u %-3s " "%5u %u\n", kaddr, ADDR_V4_WIDTH+3, dest, srcif, ADDR_V4_WIDTH, ire->ire_in_src_addr, ADDR_V4_WIDTH, gate, intf, ire->ire_max_frag, ire->ire_frag_flag ? '*' : ' ', ire->ire_uinfo.iulp_rtt, ire->ire_refcnt, flags, ire->ire_ob_pkt_count, ire->ire_ib_pkt_count); } else { mdb_printf("%?p %-*s %-8s %-*I %-*I %-5s %4u %5u %s\n", kaddr, ADDR_V4_WIDTH+3, dest, srcif, ADDR_V4_WIDTH, ire->ire_in_src_addr, ADDR_V4_WIDTH, gate, flags, ire->ire_refcnt, ire->ire_ob_pkt_count + ire->ire_ib_pkt_count, intf); } return (WALK_NEXT); } int ip_mask_to_plen_v6(const in6_addr_t *v6mask) { int plen; int i; uint32_t val; for (i = 3; i >= 0; i--) if (v6mask->s6_addr32[i] != 0) break; if (i < 0) return (0); plen = 32 + 32 * i; val = v6mask->s6_addr32[i]; while (!(val & 1)) { val >>= 1; plen--; } return (plen); } static int netstat_irev6_cb(uintptr_t kaddr, const void *walk_data, void *cb_data) { const ire_t *ire = walk_data; uint_t *opts = cb_data; const in6_addr_t *gatep; char deststr[ADDR_V6_WIDTH + 5]; char flags[10], intf[LIFNAMSIZ + 1]; int masklen; if (ire->ire_ipversion != IPV6_VERSION) return (WALK_NEXT); if (!(*opts & NETSTAT_ALL) && ire->ire_type == IRE_CACHE) return (WALK_NEXT); if (*opts & NETSTAT_FIRST) { *opts &= ~NETSTAT_FIRST; mdb_printf("\n%%s Table: IPv6%\n", (*opts & NETSTAT_VERBOSE) ? "IRE" : "Routing"); if (*opts & NETSTAT_VERBOSE) { mdb_printf("%%-?s %-*s %-*s If PMTU Rtt Ref " "Flags Out In/Fwd%\n", "Address", ADDR_V6_WIDTH+4, "Destination/Mask", ADDR_V6_WIDTH, "Gateway"); } else { mdb_printf("%%-?s %-*s %-*s Flags Ref Use If" "%\n", "Address", ADDR_V6_WIDTH+4, "Destination/Mask", ADDR_V6_WIDTH, "Gateway"); } } gatep = (ire->ire_type & (IRE_INTERFACE|IRE_LOOPBACK)) ? &ire->ire_src_addr_v6 : &ire->ire_gateway_addr_v6; masklen = ip_mask_to_plen_v6(&ire->ire_mask_v6); (void) mdb_snprintf(deststr, sizeof (deststr), "%N/%d", &ire->ire_addr_v6, masklen); (void) strcpy(flags, "U"); if (ire->ire_type == IRE_DEFAULT || ire->ire_type == IRE_PREFIX || ire->ire_type == IRE_HOST || ire->ire_type == IRE_HOST_REDIRECT) (void) strcat(flags, "G"); if (masklen == IPV6_ABITS) (void) strcat(flags, "H"); if (ire->ire_type == IRE_HOST_REDIRECT) (void) strcat(flags, "D"); if (ire->ire_type == IRE_CACHE) (void) strcat(flags, "A"); if (ire->ire_type == IRE_LOCAL) (void) strcat(flags, "L"); if (ire->ire_flags & RTF_MULTIRT) (void) strcat(flags, "M"); if (ire->ire_flags & RTF_SETSRC) (void) strcat(flags, "S"); get_ifname(ire, intf); if (*opts & NETSTAT_VERBOSE) { mdb_printf("%?p %-*s %-*N %-5s %5u%c %5u %3u %-5s %6u %u\n", kaddr, ADDR_V6_WIDTH+4, deststr, ADDR_V6_WIDTH, gatep, intf, ire->ire_max_frag, ire->ire_frag_flag ? '*' : ' ', ire->ire_uinfo.iulp_rtt, ire->ire_refcnt, flags, ire->ire_ob_pkt_count, ire->ire_ib_pkt_count); } else { mdb_printf("%?p %-*s %-*N %-5s %3u %6u %s\n", kaddr, ADDR_V6_WIDTH+4, deststr, ADDR_V6_WIDTH, gatep, flags, ire->ire_refcnt, ire->ire_ob_pkt_count + ire->ire_ib_pkt_count, intf); } return (WALK_NEXT); } /*ARGSUSED*/ int netstat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { uint_t opts = 0; const char *optf = NULL; const char *optP = NULL; if (mdb_getopts(argc, argv, 'a', MDB_OPT_SETBITS, NETSTAT_ALL, &opts, 'f', MDB_OPT_STR, &optf, 'P', MDB_OPT_STR, &optP, 'r', MDB_OPT_SETBITS, NETSTAT_ROUTE, &opts, 'v', MDB_OPT_SETBITS, NETSTAT_VERBOSE, &opts, NULL) != argc) return (DCMD_USAGE); if (optP != NULL) { if ((strcmp("tcp", optP) != 0) && (strcmp("udp", optP) != 0)) return (DCMD_USAGE); if (opts & NETSTAT_ROUTE) return (DCMD_USAGE); } if (optf == NULL) opts |= NETSTAT_V4 | NETSTAT_V6 | NETSTAT_UNIX; else if (strcmp("inet", optf) == 0) opts |= NETSTAT_V4; else if (strcmp("inet6", optf) == 0) opts |= NETSTAT_V6; else if (strcmp("unix", optf) == 0) opts |= NETSTAT_UNIX; else return (DCMD_USAGE); if (opts & NETSTAT_ROUTE) { if (!(opts & (NETSTAT_V4|NETSTAT_V6))) return (DCMD_USAGE); if (opts & NETSTAT_V4) { opts |= NETSTAT_FIRST; if (mdb_walk("ip`ire", netstat_irev4_cb, &opts) == -1) { mdb_warn("failed to walk ip`ire"); return (DCMD_ERR); } opts |= NETSTAT_FIRST; if (mdb_walk("ip`ire", netstat_irev4src_cb, &opts) == -1) { mdb_warn("failed to walk ip`ire"); return (DCMD_ERR); } } if (opts & NETSTAT_V6) { opts |= NETSTAT_FIRST; if (mdb_walk("ip`ire", netstat_irev6_cb, &opts) == -1) { mdb_warn("failed to walk ip`ire"); return (DCMD_ERR); } } return (DCMD_OK); } if ((optP == NULL) || (strcmp("tcp", optP) == 0)) { if ((optf == NULL) || (strcmp("inet", optf) == 0)) { /* Print TCPv4 connection */ mdb_printf( "%%-?s St %*s %*s %s%\n", "TCPv4", ADDR_V4_WIDTH, "Local Address", ADDR_V4_WIDTH, "Remote Address", "Zone"); if (opts & NETSTAT_VERBOSE) netstat_tcp_verbose_header_pr(); if (mdb_walk("ipcl_tcpconn_cache", netstat_tcpv4_cb, (void *)(uintptr_t)opts) == -1) { mdb_warn("failed to walk ipcl_tcpconn_cache"); return (DCMD_ERR); } } if ((optf == NULL) || (strcmp("inet6", optf) == 0)) { /* Print TCPv6 connection */ mdb_printf( "%%-?s St %*s %*s %s\n%", "TCPv6", ADDR_V6_WIDTH, "Local Address", ADDR_V6_WIDTH, "Remote Address", "Zone"); if (opts & NETSTAT_VERBOSE) netstat_tcp_verbose_header_pr(); if (mdb_walk("ipcl_tcpconn_cache", netstat_tcpv6_cb, (void *)(uintptr_t)opts) == -1) { mdb_warn("failed to walk ipcl_tcpconn_cache"); return (DCMD_ERR); } } } if ((optP == NULL) || (strcmp("udp", optP) == 0)) { if ((optf == NULL) || (strcmp("inet", optf) == 0)) { /* Print UDPv4 connection */ mdb_printf( "%%-?s St %*s %*s %s\n%", "UDPv4", ADDR_V4_WIDTH, "Local Address", ADDR_V4_WIDTH, "Remote Address", "Zone"); if (mdb_walk("udp_cache", netstat_udpv4_cb, (void *)(uintptr_t)opts) == -1) { mdb_warn("failed to walk genunix`udp"); return (DCMD_ERR); } } if ((optf == NULL) || (strcmp("inet6", optf) == 0)) { /* Print UDPv6 connection */ mdb_printf( "%%-?s St %*s %*s %s\n%", "UDPv6", ADDR_V6_WIDTH, "Local Address", ADDR_V6_WIDTH, "Remote Address", "Zone"); if (mdb_walk("udp_cache", netstat_udpv6_cb, (void *)(uintptr_t)opts) == -1) { mdb_warn("failed to walk genunix`udp"); return (DCMD_ERR); } } } if (((optf == NULL) || (strcmp("unix", optf) == 0)) && (optP == NULL)) { /* Print Unix Domain Sockets */ mdb_printf("%%-?s %-10s %-?s %-?s %-14s %-14s %s%\n", "AF_UNIX", "Type", "Vnode", "Conn", "Local Addr", "Remote Addr", "Zone"); if (mdb_walk("genunix`sonode", netstat_unix_cb, NULL) == -1) { mdb_warn("failed to walk genunix`sonode"); return (DCMD_ERR); } } return (DCMD_OK); }