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 }