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 }