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