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 }