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 * Copyright (c) 2015, 2016 by Delphix. All rights reserved.
17 */
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <inet/mib2.h>
23 #include <sys/stropts.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <inet/tcp.h>
28 #include <arpa/inet.h>
29 #include <ofmt.h>
30 #include <sys/time.h>
31 #include "connstat_mib.h"
32 #include "connstat_tcp.h"
33
34 typedef struct tcp_fields_buf_s {
35 char t_laddr[INET6_ADDRSTRLEN];
36 char t_raddr[INET6_ADDRSTRLEN];
37 uint16_t t_lport;
38 uint16_t t_rport;
39 uint64_t t_inbytes;
40 uint64_t t_insegs;
41 uint64_t t_inunorderbytes;
42 uint64_t t_inunordersegs;
43 uint64_t t_outbytes;
44 uint64_t t_outsegs;
45 uint64_t t_retransbytes;
46 uint64_t t_retranssegs;
47 uint32_t t_suna;
48 uint32_t t_unsent;
49 uint32_t t_swnd;
50 uint32_t t_cwnd;
51 uint32_t t_rwnd;
52 uint32_t t_mss;
53 uint32_t t_rto;
54 uint32_t t_rtt_cnt;
55 uint64_t t_rtt_sum;
56 int t_state;
57 uint64_t t_rtt;
58 } tcp_fields_buf_t;
59
60 static boolean_t print_tcp_state(ofmt_arg_t *, char *, uint_t);
61
62 static ofmt_field_t tcp_fields[] = {
63 { "LADDR", 15,
64 offsetof(tcp_fields_buf_t, t_laddr), print_string },
65 { "RADDR", 15,
66 offsetof(tcp_fields_buf_t, t_raddr), print_string },
67 { "LPORT", 6,
68 offsetof(tcp_fields_buf_t, t_lport), print_uint16 },
69 { "RPORT", 6,
70 offsetof(tcp_fields_buf_t, t_rport), print_uint16 },
71 { "INBYTES", 11,
72 offsetof(tcp_fields_buf_t, t_inbytes), print_uint64 },
73 { "INSEGS", 11,
74 offsetof(tcp_fields_buf_t, t_insegs), print_uint64 },
75 { "INUNORDERBYTES", 15,
76 offsetof(tcp_fields_buf_t, t_inunorderbytes), print_uint64 },
77 { "INUNORDERSEGS", 14,
78 offsetof(tcp_fields_buf_t, t_inunordersegs), print_uint64 },
79 { "OUTBYTES", 11,
80 offsetof(tcp_fields_buf_t, t_outbytes), print_uint64 },
81 { "OUTSEGS", 11,
82 offsetof(tcp_fields_buf_t, t_outsegs), print_uint64 },
83 { "RETRANSBYTES", 13,
84 offsetof(tcp_fields_buf_t, t_retransbytes), print_uint64 },
85 { "RETRANSSEGS", 12,
86 offsetof(tcp_fields_buf_t, t_retranssegs), print_uint64 },
87 { "SUNA", 11,
88 offsetof(tcp_fields_buf_t, t_suna), print_uint32 },
89 { "UNSENT", 11,
90 offsetof(tcp_fields_buf_t, t_unsent), print_uint32 },
91 { "SWND", 11,
92 offsetof(tcp_fields_buf_t, t_swnd), print_uint32 },
93 { "CWND", 11,
94 offsetof(tcp_fields_buf_t, t_cwnd), print_uint32 },
95 { "RWND", 11,
96 offsetof(tcp_fields_buf_t, t_rwnd), print_uint32 },
97 { "MSS", 6,
98 offsetof(tcp_fields_buf_t, t_mss), print_uint32 },
99 { "RTO", 8,
100 offsetof(tcp_fields_buf_t, t_rto), print_uint32 },
101 { "RTT", 8,
102 offsetof(tcp_fields_buf_t, t_rtt), print_uint64 },
103 { "RTTS", 8,
104 offsetof(tcp_fields_buf_t, t_rtt_sum), print_uint64 },
105 { "RTTC", 11,
106 offsetof(tcp_fields_buf_t, t_rtt_cnt), print_uint32 },
107 { "STATE", 12,
108 offsetof(tcp_fields_buf_t, t_state), print_tcp_state },
109 { NULL, 0, 0, NULL}
110 };
111
112 static tcp_fields_buf_t fields_buf;
113
114
115 typedef struct tcp_state_info_s {
116 int tsi_state;
117 const char *tsi_string;
118 } tcp_state_info_t;
119
120 tcp_state_info_t tcp_state_info[] = {
121 { TCPS_CLOSED, "CLOSED" },
122 { TCPS_IDLE, "IDLE" },
123 { TCPS_BOUND, "BOUND" },
124 { TCPS_LISTEN, "LISTEN" },
125 { TCPS_SYN_SENT, "SYN_SENT" },
126 { TCPS_SYN_RCVD, "SYN_RCVD" },
127 { TCPS_ESTABLISHED, "ESTABLISHED" },
128 { TCPS_CLOSE_WAIT, "CLOSE_WAIT" },
129 { TCPS_FIN_WAIT_1, "FIN_WAIT_1" },
130 { TCPS_CLOSING, "CLOSING" },
131 { TCPS_LAST_ACK, "LAST_ACK" },
132 { TCPS_FIN_WAIT_2, "FIN_WAIT_2" },
133 { TCPS_TIME_WAIT, "TIME_WAIT" },
134 { TCPS_CLOSED - 1, NULL }
135 };
136
137 ofmt_field_t *
138 tcp_get_fields()
139 {
140 return (tcp_fields);
141 }
142
143 /*
144 * Extract information from the connection info structure into the global
145 * output buffer.
146 */
147 static void
148 tcp_ci2buf(struct tcpConnEntryInfo_s *ci)
149 {
150 fields_buf.t_inbytes =
151 ci->ce_in_data_inorder_bytes + ci->ce_in_data_unorder_bytes;
152 fields_buf.t_insegs =
153 ci->ce_in_data_inorder_segs + ci->ce_in_data_unorder_segs;
154 fields_buf.t_inunorderbytes = ci->ce_in_data_unorder_bytes;
155 fields_buf.t_inunordersegs = ci->ce_in_data_unorder_segs;
156 fields_buf.t_outbytes = ci->ce_out_data_bytes;
157 fields_buf.t_outsegs = ci->ce_out_data_segs;
158 fields_buf.t_retransbytes = ci->ce_out_retrans_bytes;
159 fields_buf.t_retranssegs = ci->ce_out_retrans_segs;
160 fields_buf.t_suna = ci->ce_snxt - ci->ce_suna;
161 fields_buf.t_unsent = ci->ce_unsent;
162 fields_buf.t_swnd = ci->ce_swnd;
163 fields_buf.t_cwnd = ci->ce_cwnd;
164 fields_buf.t_rwnd = ci->ce_rwnd;
165 fields_buf.t_mss = ci->ce_mss;
166 fields_buf.t_rto = ci->ce_rto;
167 fields_buf.t_rtt = (ci->ce_out_data_segs == 0 ? 0 : ci->ce_rtt_sa);
168 fields_buf.t_rtt_sum = ci->ce_rtt_sum;
169 fields_buf.t_rtt_cnt = ci->ce_rtt_cnt;
170 fields_buf.t_state = ci->ce_state;
171 }
172
173 /*
174 * Extract information from the connection entry into the global output
175 * buffer.
176 */
177 static void
178 tcp_ipv4_ce2buf(mib2_tcpConnEntry_t *ce)
179 {
180 (void) inet_ntop(AF_INET, (void *)&ce->tcpConnLocalAddress,
181 fields_buf.t_laddr, sizeof (fields_buf.t_laddr));
182 (void) inet_ntop(AF_INET, (void *)&ce->tcpConnRemAddress,
183 fields_buf.t_raddr, sizeof (fields_buf.t_raddr));
184
185 fields_buf.t_lport = ce->tcpConnLocalPort;
186 fields_buf.t_rport = ce->tcpConnRemPort;
187
188 tcp_ci2buf(&ce->tcpConnEntryInfo);
189 }
190
191 static void
192 tcp_ipv6_ce2buf(mib2_tcp6ConnEntry_t *ce)
193 {
194 (void) inet_ntop(AF_INET6, (void *)&ce->tcp6ConnLocalAddress,
195 fields_buf.t_laddr, sizeof (fields_buf.t_laddr));
196 (void) inet_ntop(AF_INET6, (void *)&ce->tcp6ConnRemAddress,
197 fields_buf.t_raddr, sizeof (fields_buf.t_raddr));
198
199 fields_buf.t_lport = ce->tcp6ConnLocalPort;
200 fields_buf.t_rport = ce->tcp6ConnRemPort;
201
202 tcp_ci2buf(&ce->tcp6ConnEntryInfo);
203 }
204
205 /*
206 * Print a single IPv4 connection entry, taking into account possible
207 * filters that have been set in state.
208 */
209 static void
210 tcp_ipv4_print(mib2_tcpConnEntry_t *ce, conn_walk_state_t *state)
211 {
212 if (!(state->cws_flags & CS_LOOPBACK) &&
213 ntohl(ce->tcpConnLocalAddress) == INADDR_LOOPBACK) {
214 return;
215 }
216
217 if (state->cws_flags & CS_LADDR) {
218 struct sockaddr_in *sin =
219 (struct sockaddr_in *)&state->cws_filter.ca_laddr;
220 if (ce->tcpConnLocalAddress != sin->sin_addr.s_addr) {
221 return;
222 }
223 }
224 if (state->cws_flags & CS_RADDR) {
225 struct sockaddr_in *sin =
226 (struct sockaddr_in *)&state->cws_filter.ca_raddr;
227 if (ce->tcpConnRemAddress != sin->sin_addr.s_addr) {
228 return;
229 }
230 }
231 if (state->cws_flags & CS_LPORT) {
232 struct sockaddr_in *sin =
233 (struct sockaddr_in *)&state->cws_filter.ca_laddr;
234 if (ce->tcpConnLocalPort != ntohs(sin->sin_port)) {
235 return;
236 }
237 }
238 if (state->cws_flags & CS_RPORT) {
239 struct sockaddr_in *sin =
240 (struct sockaddr_in *)&state->cws_filter.ca_raddr;
241 if (ce->tcpConnRemPort != ntohs(sin->sin_port)) {
242 return;
243 }
244 }
245
246 if ((state->cws_flags & CS_STATE) &&
247 ce->tcpConnEntryInfo.ce_state != state->cws_filter.ca_state) {
248 return;
249 }
250
251 tcp_ipv4_ce2buf(ce);
252 ofmt_print(state->cws_ofmt, &fields_buf);
253 }
254
255 /*
256 * Print a single IPv6 connection entry, taking into account possible
257 * filters that have been set in state.
258 */
259 static void
260 tcp_ipv6_print(mib2_tcp6ConnEntry_t *ce, conn_walk_state_t *state)
261 {
262 if (!(state->cws_flags & CS_LOOPBACK) &&
263 IN6_IS_ADDR_LOOPBACK(
264 (struct in6_addr *)&ce->tcp6ConnLocalAddress)) {
265 return;
266 }
267
268 if (state->cws_flags & CS_LADDR) {
269 struct sockaddr_in6 *sin6 =
270 (struct sockaddr_in6 *)&state->cws_filter.ca_laddr;
271 if (!IN6_ARE_ADDR_EQUAL(
272 (struct in6_addr *)&ce->tcp6ConnLocalAddress,
273 &sin6->sin6_addr)) {
274 return;
275 }
276 }
277 if (state->cws_flags & CS_RADDR) {
278 struct sockaddr_in6 *sin6 =
279 (struct sockaddr_in6 *)&state->cws_filter.ca_raddr;
280 if (!IN6_ARE_ADDR_EQUAL(
281 (struct in6_addr *)&ce->tcp6ConnRemAddress,
282 &sin6->sin6_addr)) {
283 return;
284 }
285 }
286 if (state->cws_flags & CS_LPORT) {
287 struct sockaddr_in6 *sin6 =
288 (struct sockaddr_in6 *)&state->cws_filter.ca_laddr;
289 if (ce->tcp6ConnLocalPort != ntohs(sin6->sin6_port)) {
290 return;
291 }
292 }
293 if (state->cws_flags & CS_RPORT) {
294 struct sockaddr_in6 *sin6 =
295 (struct sockaddr_in6 *)&state->cws_filter.ca_raddr;
296 if (ce->tcp6ConnRemPort != ntohs(sin6->sin6_port)) {
297 return;
298 }
299 }
300
301 if ((state->cws_flags & CS_STATE) &&
302 ce->tcp6ConnEntryInfo.ce_state != state->cws_filter.ca_state) {
303 return;
304 }
305
306 tcp_ipv6_ce2buf(ce);
307 ofmt_print(state->cws_ofmt, &fields_buf);
308 }
309
310 void
311 tcp_walk_ipv4(struct strbuf *dbuf, conn_walk_state_t *state)
312 {
313 uint_t nconns = (dbuf->len / sizeof (mib2_tcpConnEntry_t));
314 /* LINTED E_BAD_PTR_CAST_ALIGN */
315 mib2_tcpConnEntry_t *ce = (mib2_tcpConnEntry_t *)dbuf->buf;
316
317 for (; nconns > 0; ce++, nconns--) {
318 tcp_ipv4_print(ce, state);
319 }
320 }
321
322 void
323 tcp_walk_ipv6(struct strbuf *dbuf, conn_walk_state_t *state)
324 {
325 uint_t nconns = (dbuf->len / sizeof (mib2_tcp6ConnEntry_t));
326 /* LINTED E_BAD_PTR_CAST_ALIGN */
327 mib2_tcp6ConnEntry_t *ce = (mib2_tcp6ConnEntry_t *)dbuf->buf;
328
329 for (; nconns > 0; ce++, nconns--) {
330 tcp_ipv6_print(ce, state);
331 }
332 }
333
334 static tcp_state_info_t *tcp_stateinfobystate(int state) {
335 tcp_state_info_t *sip;
336
337 for (sip = tcp_state_info; sip->tsi_string != NULL; sip++) {
338 if (sip->tsi_state == state) {
339 return (sip);
340 }
341 }
342 return (NULL);
343 }
344
345 static tcp_state_info_t *tcp_stateinfobystr(const char *statestr) {
346 tcp_state_info_t *sip;
347
348 for (sip = tcp_state_info; sip->tsi_string != NULL; sip++) {
349 if (strncasecmp(statestr, sip->tsi_string,
350 strlen(sip->tsi_string)) == 0) {
351 return (sip);
352 }
353 }
354 return (NULL);
355 }
356 int
357 tcp_str2state(const char *statestr)
358 {
359 tcp_state_info_t *sip = tcp_stateinfobystr(statestr);
360 return (sip == NULL ? TCPS_CLOSED - 1 : sip->tsi_state);
361 }
362
363 static const char *
364 tcp_state2str(int state)
365 {
366 tcp_state_info_t *sip = tcp_stateinfobystate(state);
367 return (sip == NULL ? NULL : sip->tsi_string);
368 }
369
370 static boolean_t
371 print_tcp_state(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
372 {
373 /* LINTED E_BAD_PTR_CAST_ALIGN */
374 int state = *(int *)((char *)ofarg->ofmt_cbarg + ofarg->ofmt_id);
375 const char *statestr = tcp_state2str(state);
376
377 if (statestr != NULL) {
378 (void) strlcpy(buf, statestr, bufsize);
379 } else {
380 (void) snprintf(buf, bufsize, "UNKNOWN(%d)", state);
381 }
382
383 return (B_TRUE);
384 }