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 }