1 /*
   2  * CDDL HEADER START
   3  *
   4  * This file and its contents are supplied under the terms of the
   5  * Common Development and Distribution License ("CDDL"), version 1.0.
   6  * You may only use this file in accordance with the terms of version
   7  * 1.0 of the CDDL.
   8  *
   9  * A full copy of the text of the CDDL should have accompanied this
  10  * source.  A copy of the CDDL is also available via the Internet at
  11  * http://www.illumos.org/license/CDDL.
  12  *
  13  * CDDL HEADER END
  14  */
  15 
  16 /*
  17  * Copyright (c) 2013 by Delphix. All rights reserved.
  18  */
  19 
  20 #include <netinet/in.h>
  21 #include <sys/socket.h>
  22 
  23 #include <mdb/mdb_ctf.h>
  24 #include <mdb/mdb_modapi.h>
  25 #include <mdb/mdb_ks.h>
  26 
  27 
  28 /*
  29  * nlm_host dcmd implementation
  30  */
  31 
  32 typedef struct mdb_sockaddr_in { /* IPv4 struct */
  33         sa_family_t     sin_family;
  34         struct {
  35                 union { /* MUST specify all fields in the union */
  36                         struct {
  37                                 uint8_t s_b1;
  38                                 uint8_t s_b2;
  39                                 uint8_t s_b3;
  40                                 uint8_t s_b4;
  41                         } S_un_b;
  42                         struct {
  43                                 uint16_t s_w1;
  44                                 uint16_t s_w2;
  45                         } S_un_w;
  46                         uint32_t S_addr;
  47                 } S_un;
  48         } sin_addr;
  49 } mdb_sockaddr_in_t;
  50 
  51 typedef struct mdb_sockaddr_in6 { /* IPv6 struct */
  52         in6_addr_t sin6_addr;
  53 } mdb_sockaddr_in6_t;
  54 
  55 typedef struct mdb_nlm_host {
  56         uint_t          nh_refs;
  57         uintptr_t       nh_name;
  58         struct { /* struct netbuf in the os src code */
  59                 /*
  60                  * ptr to struct sockaddr_in/mdb_sockaddr_in_t
  61                  * or to struct sockaddr_in6/mdb_sockaddr_in6_t
  62                  */
  63                 uintptr_t       buf;
  64         } nh_addr;
  65         sysid_t         nh_sysid;
  66         uint8_t         nh_flags;
  67 } mdb_nlm_host_t;
  68 
  69 /*
  70  * Output looks like:
  71  * > ::nlm_host
  72  * NLM_HOST         IP ADDR               HOST             REFCNT  SYSID  FLAGS
  73  * ffffff01d80f7b48 172.16.203.114        delphix               0     14    0x5
  74  * ffffff01d80f7968 fe80::dc:ff:fe01:fdbf charleston.talisker   0     31    0x5
  75  */
  76 static int
  77 nlm_host_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
  78 {
  79         mdb_nlm_host_t nlm_host;
  80         char nh_name[1024];
  81         mdb_sockaddr_in_t sockaddr;
  82         mdb_sockaddr_in6_t sockaddr6;
  83         boolean_t ipv4;
  84 
  85         if (argc != 0) {
  86                 return (DCMD_USAGE);
  87         }
  88 
  89         if (!(flags & DCMD_ADDRSPEC)) {
  90                 if (mdb_walk_dcmd("nlm_host", "nlm_host", argc, argv) == -1) {
  91                         mdb_warn("can't walk all nlm_hosts");
  92                         return (DCMD_ERR);
  93                 }
  94                 return (DCMD_OK);
  95         }
  96 
  97 
  98         if (mdb_ctf_vread(&nlm_host, "struct nlm_host", "mdb_nlm_host_t",
  99             addr, 0) == -1) {
 100                 return (DCMD_ERR);
 101         }
 102 
 103         if (mdb_readstr(nh_name, sizeof (nh_name), nlm_host.nh_name) == -1) {
 104                 mdb_warn("failed to read nh_name at %p\n", nlm_host.nh_name);
 105                 (void) strcpy(nh_name, "<unknown>");
 106         }
 107 
 108         /*
 109          * We expect to primarily encounter IPv4 addresses, so use an IPv4
 110          * struct for the initial read.
 111          */
 112         if (mdb_ctf_vread(&sockaddr, "struct sockaddr_in", "mdb_sockaddr_in_t",
 113             nlm_host.nh_addr.buf, 0) == -1) {
 114                 return (DCMD_ERR);
 115         }
 116         if (sockaddr.sin_family == AF_INET) { /* IPv4 */
 117                 ipv4 = B_TRUE;
 118         } else { /* AF_INET6 == IPv6 */
 119                 ipv4 = B_FALSE;
 120                 if (mdb_ctf_vread(&sockaddr6, "struct sockaddr_in6",
 121                     "mdb_sockaddr_in6_t", nlm_host.nh_addr.buf, 0) == -1) {
 122                         return (DCMD_ERR);
 123                 }
 124         }
 125 
 126         if (DCMD_HDRSPEC(flags)) {
 127                 mdb_printf("%<u>%-16s %-24s %-16s %6s %6s %6s%</u>\n",
 128                     "NLM_HOST", "IP ADDR", "HOST", "REFCNT",
 129                     "SYSID", "FLAGS");
 130         }
 131 
 132         mdb_printf("%-16p ", addr);
 133         if (ipv4) {
 134                 mdb_printf("%-24I", sockaddr.sin_addr.S_un.S_addr);
 135         } else {
 136                 mdb_printf("%-24N", &sockaddr6.sin6_addr);
 137         }
 138         mdb_printf(" %-20s %2u %6u %6#x\n",
 139             nh_name, nlm_host.nh_refs, nlm_host.nh_sysid, nlm_host.nh_flags);
 140 
 141         return (DCMD_OK);
 142 }
 143 
 144 /*
 145  * nlm_host walker implementation
 146  */
 147 
 148 typedef struct mdb_nlm_globals_list {
 149         uintptr_t tqh_first; /* first element */
 150 } mdb_nlm_globals_list_t;
 151 
 152 typedef struct mdb_nlm_globals {
 153         struct {
 154                 uintptr_t tqe_next; /* next element */
 155         } nlm_link;
 156 } mdb_nlm_globals_t;
 157 
 158 static int
 159 nlm_host_walk_init(mdb_walk_state_t *wsp)
 160 {
 161         mdb_nlm_globals_list_t globals_list;
 162         GElf_Sym sym;
 163         mdb_nlm_globals_t nlm_global;
 164 
 165         /*
 166          * 1. Find the global list of zones.
 167          * 2. Read the first element in the list.
 168          * 3. Read the AVL tree field and call ::walk avl.
 169          */
 170         if (mdb_lookup_by_name("nlm_zones_list", &sym) != 0) {
 171                 return (WALK_ERR);
 172         }
 173 
 174         if (mdb_ctf_vread(&globals_list, "struct nlm_globals_list",
 175             "mdb_nlm_globals_list_t", sym.st_value, 0) == -1) {
 176                 return (WALK_ERR);
 177         }
 178 
 179         if (globals_list.tqh_first == 0) {
 180                 mdb_warn("empty zones list!\n");
 181                 return (WALK_ERR);
 182         }
 183 
 184         /* This walk works for a single zone. Warn if there is > 1 zone. */
 185         if (mdb_ctf_vread(&nlm_global, "struct nlm_globals",
 186             "mdb_nlm_globals_t", globals_list.tqh_first, 0) == -1) {
 187                 return (WALK_ERR);
 188         }
 189         if (nlm_global.nlm_link.tqe_next != 0) {
 190                 mdb_warn("2+ zones present -- info for only the "
 191                     "first zone in the data structure will be printed.\n");
 192         }
 193 
 194         wsp->walk_addr = globals_list.tqh_first
 195             + mdb_ctf_offsetof_by_name("struct nlm_globals", "nlm_hosts_tree");
 196         if (mdb_layered_walk("avl", wsp) == -1) {
 197                 mdb_warn("failed to walk 'avl'\n");
 198                 return (WALK_ERR);
 199         }
 200 
 201         return (WALK_NEXT);
 202 }
 203 
 204 static int
 205 nlm_host_walk_step(mdb_walk_state_t *wsp)
 206 {
 207         return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer,
 208             wsp->walk_cbdata));
 209 }
 210 
 211 
 212 static const mdb_dcmd_t dcmds[] = {
 213         { "nlm_host", "", "dump an nlm_host structure",
 214             nlm_host_dcmd, NULL },
 215         { NULL }
 216 };
 217 
 218 static const mdb_walker_t walkers[] = {
 219         { "nlm_host", "dump all nlm_host structures",
 220             nlm_host_walk_init, nlm_host_walk_step, NULL },
 221         { NULL }
 222 };
 223 
 224 static const mdb_modinfo_t modinfo = {
 225         MDB_API_VERSION, dcmds, walkers
 226 };
 227 
 228 const mdb_modinfo_t *
 229 _mdb_init(void)
 230 {
 231         return (&modinfo);
 232 }