You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2014/04/17 18:44:43 UTC

[44/50] git commit: TS-2714: promote the tcp_info plugin to stable as 'tcpinfo'

TS-2714: promote the tcp_info plugin to stable as 'tcpinfo'


Project: http://git-wip-us.apache.org/repos/asf/trafficserver/repo
Commit: http://git-wip-us.apache.org/repos/asf/trafficserver/commit/86453b99
Tree: http://git-wip-us.apache.org/repos/asf/trafficserver/tree/86453b99
Diff: http://git-wip-us.apache.org/repos/asf/trafficserver/diff/86453b99

Branch: refs/heads/5.0.x
Commit: 86453b9930827d69e6647770b4073a74a71312ed
Parents: d461c33
Author: James Peach <jp...@apache.org>
Authored: Mon Apr 14 11:56:05 2014 -0700
Committer: James Peach <jp...@apache.org>
Committed: Wed Apr 16 10:29:11 2014 -0700

----------------------------------------------------------------------
 CHANGES                                       |   2 +
 configure.ac                                  |   2 +-
 doc/reference/plugins/index.en.rst            |   1 +
 doc/reference/plugins/tcpinfo.en.rst          | 122 ++++++
 plugins/Makefile.am                           |   1 +
 plugins/experimental/Makefile.am              |   1 -
 plugins/experimental/tcp_info/Makefile.am     |  22 --
 plugins/experimental/tcp_info/README          |  76 ----
 plugins/experimental/tcp_info/tcp_info.cc     | 424 ---------------------
 plugins/experimental/tcp_info/tcp_info.config |   3 -
 plugins/tcpinfo/Makefile.am                   |  22 ++
 plugins/tcpinfo/tcpinfo.cc                    | 424 +++++++++++++++++++++
 12 files changed, 573 insertions(+), 527 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/86453b99/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index 932f614..b952ea0 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,8 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache Traffic Server 5.0.0
 
+  *) [TS-2714] Promote the tcp_info plugin to stable as 'tcpinfo'.
+
   *) [TS-2708] Refactor and modernize the tcp_info plugin.
 
   *) [TS-2555] Adding global plugin support to ts_lua plugin.

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/86453b99/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 2a19d01..bd3ddcf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1927,6 +1927,7 @@ AC_CONFIG_FILES([
   plugins/libloader/Makefile
   plugins/regex_remap/Makefile
   plugins/stats_over_http/Makefile
+  plugins/tcpinfo/Makefile
   plugins/experimental/Makefile
   plugins/experimental/authproxy/Makefile
   plugins/experimental/background_fetch/Makefile
@@ -1944,7 +1945,6 @@ AC_CONFIG_FILES([
   plugins/experimental/rfc5861/Makefile
   plugins/experimental/s3_auth/Makefile
   plugins/experimental/spdy/Makefile
-  plugins/experimental/tcp_info/Makefile
   plugins/experimental/ts_lua/Makefile
   plugins/experimental/xdebug/Makefile
   proxy/Makefile

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/86453b99/doc/reference/plugins/index.en.rst
----------------------------------------------------------------------
diff --git a/doc/reference/plugins/index.en.rst b/doc/reference/plugins/index.en.rst
index 669b8d2..eff4c5b 100644
--- a/doc/reference/plugins/index.en.rst
+++ b/doc/reference/plugins/index.en.rst
@@ -46,6 +46,7 @@ Apache Traffic Server releases.
   header_rewrite.en
   regex_remap.en
   stats_over_http.en
+  tcpinfo.en
 
 Experimental plugins
 ====================

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/86453b99/doc/reference/plugins/tcpinfo.en.rst
----------------------------------------------------------------------
diff --git a/doc/reference/plugins/tcpinfo.en.rst b/doc/reference/plugins/tcpinfo.en.rst
new file mode 100644
index 0000000..688fe45
--- /dev/null
+++ b/doc/reference/plugins/tcpinfo.en.rst
@@ -0,0 +1,122 @@
+.. _tcpinfo-plugin:
+
+TCPInfo Plugin
+**************
+
+.. Licensed to the Apache Software Foundation (ASF) under one
+   or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+
+This global plugin logs TCP metrics at various points in the HTTP
+processing pipeline. The TCP information is retrieved by the
+:manpage:`getsockopt(2)` function using the ``TCP_INFO`` option.
+This is only supported on systems that support the ``TCP_INFO``
+option, currently Linux and BSD.
+
+Log rolling is enabled automatically, based on the
+:ts:cv:`proxy.config.log.rolling_size_mb` configuration variable.
+
+Plugin Options
+--------------
+
+The following options may be specified in :file:`plugin.config`:
+
+--hooks=LIST
+  This option specifies when TCP information should be logged. The
+  argument is a comma-separated list of the event names listed
+  below. TCP information will be sampled and logged each time the
+  specified set of events occurs.
+
+  ==============  ===============================================
+   Event Name     Triggered when
+  ==============  ===============================================
+   send_resp_hdr  The server begins sending an HTTP response.
+   ssn_close      The TCP connection closes.
+   ssn_start      A new TCP connection is accepted.
+   txn_close      A HTTP transaction is completed.
+   txn_start      A HTTP transaction is initiated.
+  ==============  ===============================================
+
+--log-file=NAME
+  This specifies the base name of the file where TCP information
+  should be logged. If this option is not specified, the name
+  ``tcpinfo`` is used. Traffic Server will automatically append
+  the ``.log`` suffix.
+
+--log-level=LEVEL
+  The log level can be either ``1`` to log only the round trip
+  time estimate, or ``2`` to log the complete set of TCP information.
+
+  The following fields are logged when the log level is ``1``:
+
+  ==========    ==================================================
+  Field Name    Description
+  ==========    ==================================================
+  timestamp     Event timestamp
+  event         Event name (one of the names listed above)
+  client        Client IP address
+  server        Server IP address
+  rtt           Estimated round trip time
+  ==========    ==================================================
+
+  The following fields are logged when the log level is ``2``:
+
+  ==============    ==================================================
+  Field Name        Description
+  ==============    ==================================================
+  timestamp         Event timestamp
+  event             Event name (one of the names listed above)
+  client            Client IP address
+  server            Server IP address
+  rtt               Estimated round trip time
+  rttvar
+  last_sent
+  last_recv
+  snd_ssthresh
+  rcv_ssthresh
+  unacked
+  sacked
+  lost
+  retrans
+  fackets
+  ==============    ==================================================
+
+--sample-rate=COUNT
+
+  This is the number of times per 1000 requests that the data will
+  be logged.  A pseudo-random number generator is used to determine if a
+  request will be logged.  The default value is 1000 and this option is
+  not required to be in the configuration file.  To achieve a log rate
+  of 1% you would set this value to 10.
+
+Examples:
+---------
+
+This example logs the simple TCP information to ``jpeach.log``
+at the start of a TCP connection and once for each HTTP
+transaction thereafter::
+
+  tcp_info.so --log-file=jpeach --log-level=1 --hooks=ssn_start,txn_start
+
+The file ``jpeach.log`` will contain the following log format::
+
+  timestamp event client server rtt
+  20140414.17h40m14s ssn_start 127.0.0.1 127.0.0.1 4000
+  20140414.17h40m14s txn_start 127.0.0.1 127.0.0.1 4000
+  20140414.17h40m16s ssn_start 127.0.0.1 127.0.0.1 4000
+  20140414.17h40m16s txn_start 127.0.0.1 127.0.0.1 4000
+  20140414.17h40m16s ssn_start 127.0.0.1 127.0.0.1 4000
+  20140414.17h40m16s txn_start 127.0.0.1 127.0.0.1 4000

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/86453b99/plugins/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index 6e00ec5..a810f8a 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -24,4 +24,5 @@ SUBDIRS = \
   libloader \
   regex_remap \
   stats_over_http \
+  tcpinfo \
   experimental

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/86453b99/plugins/experimental/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/experimental/Makefile.am b/plugins/experimental/Makefile.am
index 9aac6ad..4b811c1 100644
--- a/plugins/experimental/Makefile.am
+++ b/plugins/experimental/Makefile.am
@@ -33,7 +33,6 @@ SUBDIRS = \
  rfc5861 \
  s3_auth \
  spdy \
- tcp_info \
  ts_lua \
  xdebug
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/86453b99/plugins/experimental/tcp_info/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/experimental/tcp_info/Makefile.am b/plugins/experimental/tcp_info/Makefile.am
deleted file mode 100644
index daabefd..0000000
--- a/plugins/experimental/tcp_info/Makefile.am
+++ /dev/null
@@ -1,22 +0,0 @@
-#  Licensed to the Apache Software Foundation (ASF) under one
-#  or more contributor license agreements.  See the NOTICE file
-#  distributed with this work for additional information
-#  regarding copyright ownership.  The ASF licenses this file
-#  to you under the Apache License, Version 2.0 (the
-#  "License"); you may not use this file except in compliance
-#  with the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-
-include $(top_srcdir)/build/plugins.mk
-
-pkglib_LTLIBRARIES = tcp_info.la
-tcp_info_la_SOURCES = tcp_info.cc
-tcp_info_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS)
-

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/86453b99/plugins/experimental/tcp_info/README
----------------------------------------------------------------------
diff --git a/plugins/experimental/tcp_info/README b/plugins/experimental/tcp_info/README
deleted file mode 100644
index 56c911f..0000000
--- a/plugins/experimental/tcp_info/README
+++ /dev/null
@@ -1,76 +0,0 @@
-Apache Traffic Server TCP info plugin
-
-This plugin was written to log client round trip times.  This
-information is retrieved from the getsockopt() function using the
-TCP_INFO option.  Other information can also be gathered from TCP_INFO
-and there is an option to log most of the data structure.
-
-
-Configuration:
-The configuration files default location is in the etc or
-configuration directory under the default install directory.  For
-example if your installation directory is "/usr/local", then the
-plugin will first look in "/usr/local/etc" and then in
-"/usr/local/conf" for the configuration file.  The configuration
-filename is "tcp_info.config".
-
-
---------------------------------------------------------------------------------
-Configuration Options:
-
-"sample" is the number of times per 1000 requests that the data will
-be logged.  A pseudo-random number generator is used to determine if a
-request will be logged.  The default value is 1000 and this option is
-not required to be in the configuration file.  To achieve a log rate
-of 1% you would set this value to 10.
-example:
-sample=1000
-
-"log_file" is the full path to the log file.  There is no default
-value and this option is required to be set in the config file.  If
-the plugin can't open the log file the plugin will assert.
-example:
-log_file=/usr/local/var/log/trafficserver/tcp_info.log
-
-"log_level" determines how much information you want in the log file.
-The default is 1 and this option is not required to be in the config
-file.  Log level of 1 will only log the rtt value from the TCP_INFO
-data structure.  To log most of the TCP_INFO data structure use the
-value of 2.
-example:
-log_level=1
-
-
-*** "hook" DOESN'T WORK RIGHT NOW - ATS NEEDS ANOTHER HOOK BEFORE CLOSING THE SOCKET ***
-"hook" tells the plugin when to log the information.  The default is 1
-and this will log the TCP_INFO data when the client makes a TCP
-connection.  A value of 2 will log the information at the end of the
-TCP connection.  If you are looking for information about retransmit
-or lost, then you most likely would want to set this to 2.  This
-configuration option is not required to be in the config file.
-example:
-hook=1
-
---------------------------------------------------------------------------------
-Log Format:
-
-log_level=1
-[unix seconds] [client ip] [server ip] [rtt]
-example:
-1344483780 192.168.1.12 192.168.1.1 1875
-
-log_level=2
-[unix seconds] [microseconds] [client ip] [server ip] [last_data_sent]
-[last_data_recv] [snd_cwnd] [snd_ssthresh] [rcv_ssthresh] [rtt]
-[rttvar] [unacked] [sacked] [lost] [retrans] [fackets]
-
-
-example:
-1344483817 281068 192.168.1.12 192.168.1.1 2 2 10 2147483647 32768 3875 4500 0 0 0 0 0
-
---------------------------------------------------------------------------------
-Version 1.0.1 (8/9/2012, bcall)
-  * Documentation and code cleanup
-
-Version 1.0.0 (8/8/2012, bcall)
-  * Initial Version

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/86453b99/plugins/experimental/tcp_info/tcp_info.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/tcp_info/tcp_info.cc b/plugins/experimental/tcp_info/tcp_info.cc
deleted file mode 100644
index e828ded..0000000
--- a/plugins/experimental/tcp_info/tcp_info.cc
+++ /dev/null
@@ -1,424 +0,0 @@
-/** @file
-
-  tcp_info: A plugin to log TCP session information.
-
-  @section license License
-
-  Licensed to the Apache Software Foundation (ASF) under one
-  or more contributor license agreements.  See the NOTICE file
-  distributed with this work for additional information
-  regarding copyright ownership.  The ASF licenses this file
-  to you under the Apache License, Version 2.0 (the
-  "License"); you may not use this file except in compliance
-  with the License.  You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <ts/ts.h>
-#include <unistd.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <getopt.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/time.h>
-#include <arpa/inet.h>
-
-#include "ink_defs.h"
-
-#if defined(TCP_INFO) && defined(HAVE_STRUCT_TCP_INFO)
-#define TCPI_PLUGIN_SUPPORTED 1
-#endif
-
-#define TCPI_HOOK_SSN_START     0x01u
-#define TCPI_HOOK_TXN_START     0x02u
-#define TCPI_HOOK_SEND_RESPONSE 0x04u
-#define TCPI_HOOK_SSN_CLOSE     0x08u
-#define TCPI_HOOK_TXN_CLOSE     0x10u
-
-// Log format headers. These are emitted once at the start of a log file. Note that we
-// carefully order the fields so the field ordering is compatible. This lets you change
-// the verbosity without breaking a perser that is moderately robust.
-static const char * tcpi_headers[] = {
-  "timestamp event client server rtt",
-  "timestamp event client server rtt rttvar last_sent last_recv "
-    "snd_ssthresh rcv_ssthresh unacked sacked lost retrans fackets",
-};
-
-struct Config {
-  int sample;
-  unsigned log_level;
-  TSTextLogObject log;
-
-  Config() : sample(1000), log_level(1), log(NULL) {
-  }
-
-  ~Config() {
-    if (log) {
-      TSTextLogObjectDestroy(log);
-    }
-  }
-};
-
-union const_sockaddr_ptr {
-  const struct sockaddr * sa;
-  const struct sockaddr_in * in;
-  const struct sockaddr_in6 * in6;
-
-  const void * addr() const {
-    switch (sa->sa_family) {
-    case AF_INET: return &(in->sin_addr);
-    case AF_INET6: return &(in6->sin6_addr);
-    default: return NULL;
-    }
-  }
-
-};
-
-#if TCPI_PLUGIN_SUPPORTED
-
-static void
-log_tcp_info(Config * config, const char * event_name, TSHttpSsn ssnp)
-{
-  char                client_str[INET6_ADDRSTRLEN];
-  char                server_str[INET6_ADDRSTRLEN];
-  const_sockaddr_ptr  client_addr;
-  const_sockaddr_ptr  server_addr;
-
-  struct tcp_info     info;
-  socklen_t           tcp_info_len = sizeof(info);
-  int                 fd;
-
-  TSReleaseAssert(config->log != NULL);
-
-  if (TSHttpSsnClientFdGet(ssnp, &fd) != TS_SUCCESS) {
-    TSDebug("tcp_info", "error getting the client socket fd");
-    return;
-  }
-
-  if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, &tcp_info_len) != 0) {
-    TSDebug("tcp_info", "getsockopt(%d, TCP_INFO) failed: %s", fd, strerror(errno));
-    return;
-  }
-
-  client_addr.sa = TSHttpSsnClientAddrGet(ssnp);
-  server_addr.sa = TSHttpSsnIncomingAddrGet(ssnp);
-
-  if (client_addr.sa == NULL || server_addr.sa == NULL) {
-    return;
-  }
-
-  // convert ip to string
-  inet_ntop(client_addr.sa->sa_family, client_addr.addr(), client_str, sizeof(client_str));
-  inet_ntop(server_addr.sa->sa_family, server_addr.addr(), server_str, sizeof(server_str));
-
-  TSReturnCode ret;
-
-  if (config->log_level == 2) {
-#if !defined(freebsd) || defined(__GLIBC__)
-    ret = TSTextLogObjectWrite(config->log, "%s %s %s %u %u %u %u %u %u %u %u %u %u %u %u",
-                     event_name,
-                     client_str,
-                     server_str,
-                     info.tcpi_rtt,
-                     info.tcpi_rttvar,
-                     info.tcpi_last_data_sent,
-                     info.tcpi_last_data_recv,
-                     info.tcpi_snd_cwnd,
-                     info.tcpi_snd_ssthresh,
-                     info.tcpi_rcv_ssthresh,
-                     info.tcpi_unacked,
-                     info.tcpi_sacked,
-                     info.tcpi_lost,
-                     info.tcpi_retrans,
-                     info.tcpi_fackets);
-#else
-    ret = TSTextLogObjectWrite(config->log, "%s %s %s %u %u %u %u %u %u %u %u %u %u %u %u",
-                     event_name,
-                     client_str,
-                     server_str,
-                     info.tcpi_rtt,
-                     info.tcpi_rttvar,
-                     info.__tcpi_last_data_sent,
-                     info.tcpi_last_data_recv,
-                     info.tcpi_snd_cwnd,
-                     info.tcpi_snd_ssthresh,
-                     info.__tcpi_rcv_ssthresh,
-                     info.__tcpi_unacked,
-                     info.__tcpi_sacked,
-                     info.__tcpi_lost,
-                     info.__tcpi_retrans,
-                     info.__tcpi_fackets);
-#endif
-  } else {
-    ret = TSTextLogObjectWrite(config->log, "%s %s %s %u",
-                     event_name,
-                     client_str,
-                     server_str,
-                     info.tcpi_rtt);
-  }
-
-  // It's really not clear how we should handle logging failures. It a failure transient
-  // or persistent? Should we try to re-open the logs? How frequently should we do that?
-  if (ret != TS_SUCCESS) {
-    TSError("[tcp_info] log write failed, disabling logging");
-    TSTextLogObjectDestroy(config->log);
-    config->log = NULL;
-  }
-}
-
-#else /* TCPI_PLUGIN_SUPPORTED */
-
-static void
-log_tcp_info(Config * /* config */, const char * /* event_name */, TSHttpSsn /* ssnp */)
-{
-  return; // TCP metrics not supported.
-}
-
-#endif /* TCPI_PLUGIN_SUPPORTED */
-
-static int
-tcp_info_hook(TSCont contp, TSEvent event, void *edata)
-{
-  TSHttpSsn ssnp = NULL;
-  TSHttpTxn txnp = NULL;
-  int       random = 0;
-  Config *  config = (Config *)TSContDataGet(contp);
-
-  const char *event_name;
-  switch (event) {
-  case TS_EVENT_HTTP_SSN_START:
-    ssnp = (TSHttpSsn)edata;
-    event_name = "ssn_start";
-    break;
-  case TS_EVENT_HTTP_TXN_START:
-    txnp = (TSHttpTxn)edata;
-    ssnp = TSHttpTxnSsnGet(txnp);
-    event_name = "txn_start";
-    break;
-  case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
-    txnp = (TSHttpTxn)edata;
-    ssnp = TSHttpTxnSsnGet(txnp);
-    event_name = "send_resp_hdr";
-    break;
-  case TS_EVENT_HTTP_SSN_CLOSE:
-    ssnp = (TSHttpSsn)edata;
-    event_name = "ssn_close";
-  default:
-    return 0;
-  }
-
-  TSDebug("tcp_info", "logging hook called for %s (%s) with log object %p",
-      TSHttpEventNameLookup(event), event_name, config->log);
-
-  if (config->log == NULL) {
-    goto done;
-  }
-
-  // no need to run rand if we are always going log (100%)
-  if (config->sample < 1000) {
-    random = rand() % 1000;
-    TSDebug("tcp_info", "random: %d, config->sample: %d", random, config->sample);
-  }
-
-  if (random < config->sample) {
-    TSDebug("tcp_info", "sampling TCP metrics for %s event", event_name);
-    log_tcp_info(config, event_name, ssnp);
-  }
-
-done:
-  if (txnp != NULL) {
-    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
-  } else if (ssnp != NULL) {
-    TSHttpSsnReenable(ssnp, TS_EVENT_HTTP_CONTINUE);
-  }
-
-  return TS_EVENT_NONE;
-}
-
-static bool
-parse_unsigned(const char * str, unsigned long& lval)
-{
-  char * end = NULL;
-
-  if (*str == '\0') {
-    return false;
-  }
-
-  lval = strtoul(str, &end, 0 /* base */);
-  if (end == str) {
-    // No valid characters.
-    return false;
-  }
-
-  if (end && *end != '\0') {
-    // Not all charaters consumed.
-    return false;
-  }
-
-  return true;
-}
-
-// Parse a comma-separated list of hook names into a hook bitmask.
-static unsigned
-parse_hook_list(const char * hook_list)
-{
-  unsigned mask = 0;
-  char * tok;
-  char * str;
-  char * last;
-
-  const struct hookmask { const char * name; unsigned mask; } hooks[] = {
-    { "ssn_start", TCPI_HOOK_SSN_START },
-    { "txn_start", TCPI_HOOK_TXN_START },
-    { "send_resp_hdr", TCPI_HOOK_SEND_RESPONSE },
-    { "ssn_close", TCPI_HOOK_SSN_CLOSE },
-    { "txn_close", TCPI_HOOK_TXN_CLOSE },
-    { NULL, 0u }
-  };
-
-  str = TSstrdup(hook_list);
-
-  for (tok = strtok_r(str, ",", &last); tok; tok = strtok_r(NULL, ",", &last)) {
-    bool match = false;
-
-    for (const struct hookmask * m = hooks; m->name != NULL; ++m) {
-      if (strcmp(m->name, tok) == 0) {
-        mask |= m->mask;
-        match = true;
-        break;
-      }
-    }
-
-    if (!match) {
-      TSError("[tcp_info] invalid hook name '%s'", tok);
-    }
-  }
-
-  TSfree(str);
-  return mask;
-}
-
-void
-TSPluginInit(int argc, const char * argv[])
-{
-  static const char usage[] = "tcp_info.so [--log-file=PATH] [--log-level=LEVEL] [--hooks=LIST] [--sample-rate=COUNT]";
-  static const struct option longopts[] = {
-    { const_cast<char *>("sample-rate"), required_argument, NULL, 'r' },
-    { const_cast<char *>("log-file"), required_argument, NULL, 'f' },
-    { const_cast<char *>("log-level"), required_argument, NULL, 'l' },
-    { const_cast<char *>("hooks"), required_argument, NULL, 'h' },
-    { NULL, 0, NULL, 0 }
-  };
-
-  TSPluginRegistrationInfo info;
-  Config *  config = new Config();
-  const char *  filename = "tcp_info";
-  TSCont    cont;
-  unsigned  hooks = 0;
-
-  info.plugin_name = (char*)"tcp_info";
-  info.vendor_name = (char*)"Apache Software Foundation";
-  info.support_email = (char*)"dev@trafficserver.apache.org";
-
-  if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS) {
-    TSError("tcp_info: plugin registration failed");
-  }
-
-  optind = 0;
-  for (;;) {
-    unsigned long lval;
-
-    switch (getopt_long(argc, (char * const *)argv, "r:f:l:h:", longopts, NULL)) {
-    case 'r':
-      if (parse_unsigned(optarg, lval)) {
-        config->sample = atoi(optarg);
-      } else {
-        TSError("[tcp_info] invalid sample rate '%s'", optarg);
-      }
-      break;
-    case 'f':
-      filename = optarg;
-      break;
-    case 'l':
-      if (parse_unsigned(optarg, lval) && (lval <= countof(tcpi_headers))) {
-        config->log_level = lval;
-      } else {
-        TSError("[tcp_info] invalid log level '%s'", optarg);
-      }
-      break;
-    case 'h':
-      hooks = parse_hook_list(optarg);
-      break;
-    case -1:
-        goto init;
-    default:
-        TSError("[tcp_info] usage: %s", usage);
-    }
-  }
-
-init:
-
-#if !TCPI_PLUGIN_SUPPORTED
-  TSError("[tcp_info] TCP metrics are not supported on this platform");
-#endif
-
-  TSDebug("tcp_info", "sample: %d", config->sample);
-  TSDebug("tcp_info", "log filename: %s", filename);
-  TSDebug("tcp_info", "log_level: %u", config->log_level);
-  TSDebug("tcp_info", "hook mask: 0x%x", hooks);
-
-  if (TSTextLogObjectCreate(filename, TS_LOG_MODE_ADD_TIMESTAMP, &config->log) != TS_SUCCESS) {
-    TSError("[tcp_info] failed to create log file '%s'", filename);
-    delete config;
-    return;
-  }
-
-  TSTextLogObjectHeaderSet(config->log, tcpi_headers[config->log_level - 1]);
-
-  // Enable log rolling. Unless we specify an explicit rolling period, the log will
-  // be rolled based on the size specified in proxy.config.log.rolling_size_mb.
-  TSTextLogObjectRollingEnabledSet(config->log, 1 /* rolling_enabled */);
-
-  cont = TSContCreate(tcp_info_hook, NULL);
-  TSContDataSet(cont, config);
-
-  if (hooks & TCPI_HOOK_SSN_START) {
-    TSHttpHookAdd(TS_HTTP_SSN_START_HOOK, cont);
-    TSDebug("tcp_info", "added hook to the start of the TCP connection");
-  }
-
-  if (hooks & TCPI_HOOK_TXN_START) {
-    TSHttpHookAdd(TS_HTTP_TXN_START_HOOK, cont);
-    TSDebug("tcp_info", "added hook to the close of the transaction");
-  }
-
-  if (hooks & TCPI_HOOK_SEND_RESPONSE) {
-    TSHttpHookAdd(TS_HTTP_SEND_RESPONSE_HDR_HOOK, cont);
-    TSDebug("tcp_info", "added hook to the sending of the headers");
-  }
-
-  if (hooks & TCPI_HOOK_SSN_CLOSE) {
-    TSHttpHookAdd(TS_HTTP_SSN_CLOSE_HOOK, cont);
-    TSDebug("tcp_info", "added hook to the close of the TCP connection");
-  }
-
-  if (hooks & TCPI_HOOK_TXN_CLOSE) {
-    TSHttpHookAdd(TS_HTTP_TXN_CLOSE_HOOK, cont);
-    TSDebug("tcp_info", "added hook to the close of the transaction");
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/86453b99/plugins/experimental/tcp_info/tcp_info.config
----------------------------------------------------------------------
diff --git a/plugins/experimental/tcp_info/tcp_info.config b/plugins/experimental/tcp_info/tcp_info.config
deleted file mode 100644
index 52321de..0000000
--- a/plugins/experimental/tcp_info/tcp_info.config
+++ /dev/null
@@ -1,3 +0,0 @@
-log_file=/usr/local/var/log/trafficserver/tcp_info.log
-sample=1000
-log_level=1

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/86453b99/plugins/tcpinfo/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/tcpinfo/Makefile.am b/plugins/tcpinfo/Makefile.am
new file mode 100644
index 0000000..ae1078b
--- /dev/null
+++ b/plugins/tcpinfo/Makefile.am
@@ -0,0 +1,22 @@
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+
+include $(top_srcdir)/build/plugins.mk
+
+pkglib_LTLIBRARIES = tcpinfo.la
+tcpinfo_la_SOURCES = tcpinfo.cc
+tcpinfo_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS)
+

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/86453b99/plugins/tcpinfo/tcpinfo.cc
----------------------------------------------------------------------
diff --git a/plugins/tcpinfo/tcpinfo.cc b/plugins/tcpinfo/tcpinfo.cc
new file mode 100644
index 0000000..cad921e
--- /dev/null
+++ b/plugins/tcpinfo/tcpinfo.cc
@@ -0,0 +1,424 @@
+/** @file
+
+  tcpinfo: A plugin to log TCP session information.
+
+  @section license License
+
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ts/ts.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+
+#include "ink_defs.h"
+
+#if defined(TCP_INFO) && defined(HAVE_STRUCT_TCP_INFO)
+#define TCPI_PLUGIN_SUPPORTED 1
+#endif
+
+#define TCPI_HOOK_SSN_START     0x01u
+#define TCPI_HOOK_TXN_START     0x02u
+#define TCPI_HOOK_SEND_RESPONSE 0x04u
+#define TCPI_HOOK_SSN_CLOSE     0x08u
+#define TCPI_HOOK_TXN_CLOSE     0x10u
+
+// Log format headers. These are emitted once at the start of a log file. Note that we
+// carefully order the fields so the field ordering is compatible. This lets you change
+// the verbosity without breaking a perser that is moderately robust.
+static const char * tcpi_headers[] = {
+  "timestamp event client server rtt",
+  "timestamp event client server rtt rttvar last_sent last_recv "
+    "snd_ssthresh rcv_ssthresh unacked sacked lost retrans fackets",
+};
+
+struct Config {
+  int sample;
+  unsigned log_level;
+  TSTextLogObject log;
+
+  Config() : sample(1000), log_level(1), log(NULL) {
+  }
+
+  ~Config() {
+    if (log) {
+      TSTextLogObjectDestroy(log);
+    }
+  }
+};
+
+union const_sockaddr_ptr {
+  const struct sockaddr * sa;
+  const struct sockaddr_in * in;
+  const struct sockaddr_in6 * in6;
+
+  const void * addr() const {
+    switch (sa->sa_family) {
+    case AF_INET: return &(in->sin_addr);
+    case AF_INET6: return &(in6->sin6_addr);
+    default: return NULL;
+    }
+  }
+
+};
+
+#if TCPI_PLUGIN_SUPPORTED
+
+static void
+log_tcp_info(Config * config, const char * event_name, TSHttpSsn ssnp)
+{
+  char                client_str[INET6_ADDRSTRLEN];
+  char                server_str[INET6_ADDRSTRLEN];
+  const_sockaddr_ptr  client_addr;
+  const_sockaddr_ptr  server_addr;
+
+  struct tcp_info     info;
+  socklen_t           tcp_info_len = sizeof(info);
+  int                 fd;
+
+  TSReleaseAssert(config->log != NULL);
+
+  if (TSHttpSsnClientFdGet(ssnp, &fd) != TS_SUCCESS) {
+    TSDebug("tcpinfo", "error getting the client socket fd");
+    return;
+  }
+
+  if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, &tcp_info_len) != 0) {
+    TSDebug("tcpinfo", "getsockopt(%d, TCP_INFO) failed: %s", fd, strerror(errno));
+    return;
+  }
+
+  client_addr.sa = TSHttpSsnClientAddrGet(ssnp);
+  server_addr.sa = TSHttpSsnIncomingAddrGet(ssnp);
+
+  if (client_addr.sa == NULL || server_addr.sa == NULL) {
+    return;
+  }
+
+  // convert ip to string
+  inet_ntop(client_addr.sa->sa_family, client_addr.addr(), client_str, sizeof(client_str));
+  inet_ntop(server_addr.sa->sa_family, server_addr.addr(), server_str, sizeof(server_str));
+
+  TSReturnCode ret;
+
+  if (config->log_level == 2) {
+#if !defined(freebsd) || defined(__GLIBC__)
+    ret = TSTextLogObjectWrite(config->log, "%s %s %s %u %u %u %u %u %u %u %u %u %u %u %u",
+                     event_name,
+                     client_str,
+                     server_str,
+                     info.tcpi_rtt,
+                     info.tcpi_rttvar,
+                     info.tcpi_last_data_sent,
+                     info.tcpi_last_data_recv,
+                     info.tcpi_snd_cwnd,
+                     info.tcpi_snd_ssthresh,
+                     info.tcpi_rcv_ssthresh,
+                     info.tcpi_unacked,
+                     info.tcpi_sacked,
+                     info.tcpi_lost,
+                     info.tcpi_retrans,
+                     info.tcpi_fackets);
+#else
+    ret = TSTextLogObjectWrite(config->log, "%s %s %s %u %u %u %u %u %u %u %u %u %u %u %u",
+                     event_name,
+                     client_str,
+                     server_str,
+                     info.tcpi_rtt,
+                     info.tcpi_rttvar,
+                     info.__tcpi_last_data_sent,
+                     info.tcpi_last_data_recv,
+                     info.tcpi_snd_cwnd,
+                     info.tcpi_snd_ssthresh,
+                     info.__tcpi_rcv_ssthresh,
+                     info.__tcpi_unacked,
+                     info.__tcpi_sacked,
+                     info.__tcpi_lost,
+                     info.__tcpi_retrans,
+                     info.__tcpi_fackets);
+#endif
+  } else {
+    ret = TSTextLogObjectWrite(config->log, "%s %s %s %u",
+                     event_name,
+                     client_str,
+                     server_str,
+                     info.tcpi_rtt);
+  }
+
+  // It's really not clear how we should handle logging failures. It a failure transient
+  // or persistent? Should we try to re-open the logs? How frequently should we do that?
+  if (ret != TS_SUCCESS) {
+    TSError("[tcpinfo] log write failed, disabling logging");
+    TSTextLogObjectDestroy(config->log);
+    config->log = NULL;
+  }
+}
+
+#else /* TCPI_PLUGIN_SUPPORTED */
+
+static void
+log_tcp_info(Config * /* config */, const char * /* event_name */, TSHttpSsn /* ssnp */)
+{
+  return; // TCP metrics not supported.
+}
+
+#endif /* TCPI_PLUGIN_SUPPORTED */
+
+static int
+tcp_info_hook(TSCont contp, TSEvent event, void *edata)
+{
+  TSHttpSsn ssnp = NULL;
+  TSHttpTxn txnp = NULL;
+  int       random = 0;
+  Config *  config = (Config *)TSContDataGet(contp);
+
+  const char *event_name;
+  switch (event) {
+  case TS_EVENT_HTTP_SSN_START:
+    ssnp = (TSHttpSsn)edata;
+    event_name = "ssn_start";
+    break;
+  case TS_EVENT_HTTP_TXN_START:
+    txnp = (TSHttpTxn)edata;
+    ssnp = TSHttpTxnSsnGet(txnp);
+    event_name = "txn_start";
+    break;
+  case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
+    txnp = (TSHttpTxn)edata;
+    ssnp = TSHttpTxnSsnGet(txnp);
+    event_name = "send_resp_hdr";
+    break;
+  case TS_EVENT_HTTP_SSN_CLOSE:
+    ssnp = (TSHttpSsn)edata;
+    event_name = "ssn_close";
+  default:
+    return 0;
+  }
+
+  TSDebug("tcpinfo", "logging hook called for %s (%s) with log object %p",
+      TSHttpEventNameLookup(event), event_name, config->log);
+
+  if (config->log == NULL) {
+    goto done;
+  }
+
+  // no need to run rand if we are always going log (100%)
+  if (config->sample < 1000) {
+    random = rand() % 1000;
+    TSDebug("tcpinfo", "random: %d, config->sample: %d", random, config->sample);
+  }
+
+  if (random < config->sample) {
+    TSDebug("tcpinfo", "sampling TCP metrics for %s event", event_name);
+    log_tcp_info(config, event_name, ssnp);
+  }
+
+done:
+  if (txnp != NULL) {
+    TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+  } else if (ssnp != NULL) {
+    TSHttpSsnReenable(ssnp, TS_EVENT_HTTP_CONTINUE);
+  }
+
+  return TS_EVENT_NONE;
+}
+
+static bool
+parse_unsigned(const char * str, unsigned long& lval)
+{
+  char * end = NULL;
+
+  if (*str == '\0') {
+    return false;
+  }
+
+  lval = strtoul(str, &end, 0 /* base */);
+  if (end == str) {
+    // No valid characters.
+    return false;
+  }
+
+  if (end && *end != '\0') {
+    // Not all charaters consumed.
+    return false;
+  }
+
+  return true;
+}
+
+// Parse a comma-separated list of hook names into a hook bitmask.
+static unsigned
+parse_hook_list(const char * hook_list)
+{
+  unsigned mask = 0;
+  char * tok;
+  char * str;
+  char * last;
+
+  const struct hookmask { const char * name; unsigned mask; } hooks[] = {
+    { "ssn_start", TCPI_HOOK_SSN_START },
+    { "txn_start", TCPI_HOOK_TXN_START },
+    { "send_resp_hdr", TCPI_HOOK_SEND_RESPONSE },
+    { "ssn_close", TCPI_HOOK_SSN_CLOSE },
+    { "txn_close", TCPI_HOOK_TXN_CLOSE },
+    { NULL, 0u }
+  };
+
+  str = TSstrdup(hook_list);
+
+  for (tok = strtok_r(str, ",", &last); tok; tok = strtok_r(NULL, ",", &last)) {
+    bool match = false;
+
+    for (const struct hookmask * m = hooks; m->name != NULL; ++m) {
+      if (strcmp(m->name, tok) == 0) {
+        mask |= m->mask;
+        match = true;
+        break;
+      }
+    }
+
+    if (!match) {
+      TSError("[tcpinfo] invalid hook name '%s'", tok);
+    }
+  }
+
+  TSfree(str);
+  return mask;
+}
+
+void
+TSPluginInit(int argc, const char * argv[])
+{
+  static const char usage[] = "tcpinfo.so [--log-file=PATH] [--log-level=LEVEL] [--hooks=LIST] [--sample-rate=COUNT]";
+  static const struct option longopts[] = {
+    { const_cast<char *>("sample-rate"), required_argument, NULL, 'r' },
+    { const_cast<char *>("log-file"), required_argument, NULL, 'f' },
+    { const_cast<char *>("log-level"), required_argument, NULL, 'l' },
+    { const_cast<char *>("hooks"), required_argument, NULL, 'h' },
+    { NULL, 0, NULL, 0 }
+  };
+
+  TSPluginRegistrationInfo info;
+  Config *  config = new Config();
+  const char *  filename = "tcpinfo";
+  TSCont    cont;
+  unsigned  hooks = 0;
+
+  info.plugin_name = (char*)"tcpinfo";
+  info.vendor_name = (char*)"Apache Software Foundation";
+  info.support_email = (char*)"dev@trafficserver.apache.org";
+
+  if (TSPluginRegister(TS_SDK_VERSION_3_0, &info) != TS_SUCCESS) {
+    TSError("[tcpinfo] plugin registration failed");
+  }
+
+  optind = 0;
+  for (;;) {
+    unsigned long lval;
+
+    switch (getopt_long(argc, (char * const *)argv, "r:f:l:h:", longopts, NULL)) {
+    case 'r':
+      if (parse_unsigned(optarg, lval)) {
+        config->sample = atoi(optarg);
+      } else {
+        TSError("[tcpinfo] invalid sample rate '%s'", optarg);
+      }
+      break;
+    case 'f':
+      filename = optarg;
+      break;
+    case 'l':
+      if (parse_unsigned(optarg, lval) && (lval <= countof(tcpi_headers))) {
+        config->log_level = lval;
+      } else {
+        TSError("[tcpinfo] invalid log level '%s'", optarg);
+      }
+      break;
+    case 'h':
+      hooks = parse_hook_list(optarg);
+      break;
+    case -1:
+        goto init;
+    default:
+        TSError("[tcpinfo] usage: %s", usage);
+    }
+  }
+
+init:
+
+#if !TCPI_PLUGIN_SUPPORTED
+  TSError("[tcpinfo] TCP metrics are not supported on this platform");
+#endif
+
+  TSDebug("tcpinfo", "sample: %d", config->sample);
+  TSDebug("tcpinfo", "log filename: %s", filename);
+  TSDebug("tcpinfo", "log_level: %u", config->log_level);
+  TSDebug("tcpinfo", "hook mask: 0x%x", hooks);
+
+  if (TSTextLogObjectCreate(filename, TS_LOG_MODE_ADD_TIMESTAMP, &config->log) != TS_SUCCESS) {
+    TSError("[tcpinfo] failed to create log file '%s'", filename);
+    delete config;
+    return;
+  }
+
+  TSTextLogObjectHeaderSet(config->log, tcpi_headers[config->log_level - 1]);
+
+  // Enable log rolling. Unless we specify an explicit rolling period, the log will
+  // be rolled based on the size specified in proxy.config.log.rolling_size_mb.
+  TSTextLogObjectRollingEnabledSet(config->log, 1 /* rolling_enabled */);
+
+  cont = TSContCreate(tcp_info_hook, NULL);
+  TSContDataSet(cont, config);
+
+  if (hooks & TCPI_HOOK_SSN_START) {
+    TSHttpHookAdd(TS_HTTP_SSN_START_HOOK, cont);
+    TSDebug("tcpinfo", "added hook to the start of the TCP connection");
+  }
+
+  if (hooks & TCPI_HOOK_TXN_START) {
+    TSHttpHookAdd(TS_HTTP_TXN_START_HOOK, cont);
+    TSDebug("tcpinfo", "added hook to the close of the transaction");
+  }
+
+  if (hooks & TCPI_HOOK_SEND_RESPONSE) {
+    TSHttpHookAdd(TS_HTTP_SEND_RESPONSE_HDR_HOOK, cont);
+    TSDebug("tcpinfo", "added hook to the sending of the headers");
+  }
+
+  if (hooks & TCPI_HOOK_SSN_CLOSE) {
+    TSHttpHookAdd(TS_HTTP_SSN_CLOSE_HOOK, cont);
+    TSDebug("tcpinfo", "added hook to the close of the TCP connection");
+  }
+
+  if (hooks & TCPI_HOOK_TXN_CLOSE) {
+    TSHttpHookAdd(TS_HTTP_TXN_CLOSE_HOOK, cont);
+    TSDebug("tcpinfo", "added hook to the close of the transaction");
+  }
+
+}