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");
+ }
+
+}