From e6ba6f69345a1fe6a207bc7a9b357f3ec4db10ed Mon Sep 17 00:00:00 2001 From: Jeff Elsloo Date: Wed, 14 Jul 2021 09:21:02 -0600 Subject: [PATCH] Adding TCP Info header support to header rewrite (#7516) (#174) * Adding TCP Info header support to header rewrite Adding documentation for TCP Info support for header rewrite plugin * Adding TCP Info header support to header rewrite Adding documentation for TCP Info support for header rewrite plugin * fixing indentation in doc * adding header file tcp info * adding header file tcp info fixing indentation in doc * adding header file tcp info fixing indentation in doc * Fixing headers where TCP info is written * adding message for internal transaction * Adding documenation for TCP-INFO in header rewrite * Fixing internal header debug message for TCP Info * adding message for internal transaction Co-authored-by: Divya Bhat (cherry picked from commit ebd9d38fbed5c13123b92641209da3cf92d201ef) Co-authored-by: dbhat --- doc/admin-guide/plugins/header_rewrite.en.rst | 27 ++++ plugins/header_rewrite/conditions.cc | 116 ++++++++++++++++++ plugins/header_rewrite/conditions.h | 39 ++++++ plugins/header_rewrite/factory.cc | 4 + plugins/header_rewrite/header_rewrite.cc | 6 + plugins/header_rewrite/parser.cc | 8 ++ plugins/header_rewrite/resources.cc | 18 +++ plugins/header_rewrite/statement.cc | 2 + 8 files changed, 220 insertions(+) diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst b/doc/admin-guide/plugins/header_rewrite.en.rst index 95363347eff..657d577e9eb 100644 --- a/doc/admin-guide/plugins/header_rewrite.en.rst +++ b/doc/admin-guide/plugins/header_rewrite.en.rst @@ -487,6 +487,24 @@ Refer to `Requests vs. Responses`_ for more information on determining the context in which the transaction's URL is evaluated. The ```` may be specified according to the options documented in `URL Parts`_. +SSN-TXN-COUNT +~~~~~~~~~~~~~ +:: + + cond %{SSN-TXN-COUNT} + +Returns the number of transactions between the Traffic Server proxy and the origin server from a single session. +Any value greater than zero indicates connection reuse. + +TCP-INFO +~~~~~~~~ +:: + + cond %{} + add-header @PropertyName "%{TCP-INFO}" + +This operation records TCP Info struct field values as an Internal remap as well as global header at the event hook specified by the condition. Supported hook conditions include TXN_START_HOOK, SEND_RESPONSE_HEADER_HOOK and TXN_CLOSE_HOOK in the Global plugin and REMAP_PSEUDO_HOOK, SEND_RESPONSE_HEADER_HOOK and TXN_CLOSE_HOOK in the Remap plugin. Conditions supported as request headers include TXN_START_HOOK and REMAP_PSEUDO_HOOK. The other conditions are supported as response headers. TCP Info fields currently recorded include rtt, rto, snd_cwnd and all_retrans. This operation is not supported on transactions originated within Traffic Server (for e.g using the |TS| :c:func:`TSHttpTxnIsInternal`) + Condition Operands ------------------ @@ -931,6 +949,15 @@ cache updates may have been performed. This hook context provides a means to modify aspects of the response sent to a client, while still caching the original versions of those attributes delivered by the origin server. +TXN_START_HOOK +~~~~~~~~~~~~~~ +Rulesets are evaluated when |TS| receives a request and accepts it. This hook context indicates that a HTTP transaction is initiated and therefore, can only be enabled as a global plugin. + +TXN_CLOSE_HOOK +~~~~~~~~~~~~~~ + +Rulesets are evaluated when |TS| completes a transaction, i.e., after a response has been sent to the client. Therefore, header modifications at this hook condition only makes sense for internal headers. + Affected Conditions ------------------- diff --git a/plugins/header_rewrite/conditions.cc b/plugins/header_rewrite/conditions.cc index 615e5bb7158..f88dadcb9c8 100644 --- a/plugins/header_rewrite/conditions.cc +++ b/plugins/header_rewrite/conditions.cc @@ -34,6 +34,17 @@ #include "conditions.h" #include "lulu.h" +// This is a bit of a hack, to get the more linux specific tcp_info struct ... +#if HAVE_STRUCT_LINUX_TCP_INFO +#ifndef _LINUX_TCP_H +#define _LINUX_TCP_H +#endif +#elif HAVE_NETINET_IN_H +#ifndef _NETINET_TCP_H +#define _NETINET_TCP_H +#endif +#endif + // ConditionStatus void ConditionStatus::initialize(Parser &p) @@ -1156,3 +1167,108 @@ ConditionStringLiteral::eval(const Resources &res) return static_cast(_matcher)->test(_literal); } + +// ConditionSessionTransactCount +void +ConditionSessionTransactCount::initialize(Parser &p) +{ + Condition::initialize(p); + MatcherType *match = new MatcherType(_cond_op); + std::string const &arg = p.get_arg(); + + match->set(strtol(arg.c_str(), nullptr, 10)); + _matcher = match; +} + +bool +ConditionSessionTransactCount::eval(const Resources &res) +{ + int const val = TSHttpTxnServerSsnTransactionCount(res.txnp); + + TSDebug(PLUGIN_NAME, "Evaluating SSN-TXN-COUNT()"); + return static_cast(_matcher)->test(val); +} + +void +ConditionSessionTransactCount::append_value(std::string &s, Resources const &res) +{ + char value[32]; // enough for UINT64_MAX + int const count = TSHttpTxnServerSsnTransactionCount(res.txnp); + int const length = ink_fast_itoa(count, value, sizeof(value)); + + if (length > 0) { + TSDebug(PLUGIN_NAME, "Appending SSN-TXN-COUNT %s to evaluation value %.*s", _qualifier.c_str(), length, value); + s.append(value, length); + } +} + +void +ConditionTcpInfo::initialize(Parser &p) +{ + Condition::initialize(p); + TSDebug(PLUGIN_NAME, "Initializing TCP Info"); + MatcherType *match = new MatcherType(_cond_op); + std::string const &arg = p.get_arg(); + + match->set(strtol(arg.c_str(), nullptr, 10)); + _matcher = match; +} + +void +ConditionTcpInfo::initialize_hooks() +{ + add_allowed_hook(TS_HTTP_TXN_START_HOOK); + add_allowed_hook(TS_HTTP_TXN_CLOSE_HOOK); + add_allowed_hook(TS_HTTP_SEND_RESPONSE_HDR_HOOK); +} + +bool +ConditionTcpInfo::eval(const Resources &res) +{ + std::string s; + + append_value(s, res); + bool rval = static_cast *>(_matcher)->test(s); + + TSDebug(PLUGIN_NAME, "Evaluating TCP-Info: %s - rval: %d", s.c_str(), rval); + + return rval; +} + +void +ConditionTcpInfo::append_value(std::string &s, Resources const &res) +{ +#if defined(TCP_INFO) && defined(HAVE_STRUCT_TCP_INFO) + if (TSHttpTxnIsInternal(res.txnp)) { + TSDebug(PLUGIN_NAME, "No TCP-INFO available for internal transactions"); + return; + } + TSReturnCode tsSsn; + int fd; + struct tcp_info info; + socklen_t tcp_info_len = sizeof(info); + tsSsn = TSHttpTxnClientFdGet(res.txnp, &fd); + if (tsSsn != TS_SUCCESS || fd <= 0) { + TSDebug(PLUGIN_NAME, "error getting the client socket fd from ssn"); + } + if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, &tcp_info_len) != 0) { + TSDebug(PLUGIN_NAME, "getsockopt(%d, TCP_INFO) failed: %s", fd, strerror(errno)); + } + + if (tsSsn == TS_SUCCESS) { + if (tcp_info_len > 0) { + char buf[12 * 4 + 9]; // 4x uint32's + 4x "; " + '\0' +#if !defined(freebsd) || defined(__GLIBC__) + snprintf(buf, sizeof(buf), "%" PRIu32 ";%" PRIu32 ";%" PRIu32 ";%" PRIu32 "", info.tcpi_rtt, info.tcpi_rto, + info.tcpi_snd_cwnd, info.tcpi_retrans); +#else + snprintf(buf, sizeof(buf), "%" PRIu32 ";%" PRIu32 ";%" PRIu32 ";%" PRIu32 "", info.tcpi_rtt, info.tcpi_rto, + info.tcpi_snd_cwnd, info.__tcpi_retrans); +#endif + s += buf; + } + } +#else + s += "-"; +#endif +} diff --git a/plugins/header_rewrite/conditions.h b/plugins/header_rewrite/conditions.h index 723b1d28ea5..64edb722165 100644 --- a/plugins/header_rewrite/conditions.h +++ b/plugins/header_rewrite/conditions.h @@ -544,3 +544,42 @@ class ConditionStringLiteral : public Condition private: std::string _literal; }; + +// Single Session Transaction Count +class ConditionSessionTransactCount : public Condition +{ + typedef Matchers MatcherType; + +public: + ConditionSessionTransactCount() { TSDebug(PLUGIN_NAME_DBG, "ConditionSessionTransactCount()"); } + + // noncopyable + ConditionSessionTransactCount(const ConditionSessionTransactCount &) = delete; + void operator=(const ConditionSessionTransactCount &) = delete; + + void initialize(Parser &p) override; + void append_value(std::string &s, const Resources &res) override; + +protected: + bool eval(const Resources &res) override; +}; + +// Tcp Info +class ConditionTcpInfo : public Condition +{ + typedef Matchers MatcherType; + +public: + ConditionTcpInfo() { TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for ConditionTcpInfo"); } + + // noncopyable + ConditionTcpInfo(const ConditionTcpInfo &) = delete; + void operator=(const ConditionTcpInfo &) = delete; + + void initialize(Parser &p) override; + void append_value(std::string &s, const Resources &res) override; + +protected: + bool eval(const Resources &res) override; + void initialize_hooks() override; // Return status only valid in certain hooks +}; diff --git a/plugins/header_rewrite/factory.cc b/plugins/header_rewrite/factory.cc index 38730d28004..961d17a5059 100644 --- a/plugins/header_rewrite/factory.cc +++ b/plugins/header_rewrite/factory.cc @@ -144,6 +144,10 @@ condition_factory(const std::string &cond) c = new ConditionCidr(); } else if (c_name == "INBOUND") { c = new ConditionInbound(); + } else if (c_name == "SSN-TXN-COUNT") { + c = new ConditionSessionTransactCount(); + } else if (c_name == "TCP-INFO") { + c = new ConditionTcpInfo(); } else { TSError("[%s] Unknown condition %s", PLUGIN_NAME, c_name.c_str()); return nullptr; diff --git a/plugins/header_rewrite/header_rewrite.cc b/plugins/header_rewrite/header_rewrite.cc index 305c3ccdeba..09593eacab4 100644 --- a/plugins/header_rewrite/header_rewrite.cc +++ b/plugins/header_rewrite/header_rewrite.cc @@ -266,6 +266,12 @@ cont_rewrite_headers(TSCont contp, TSEvent event, void *edata) case TS_EVENT_HTTP_SEND_RESPONSE_HDR: hook = TS_HTTP_SEND_RESPONSE_HDR_HOOK; break; + case TS_EVENT_HTTP_TXN_START: + hook = TS_HTTP_TXN_START_HOOK; + break; + case TS_EVENT_HTTP_TXN_CLOSE: + hook = TS_HTTP_TXN_CLOSE_HOOK; + break; default: TSError("[%s] unknown event for this plugin", PLUGIN_NAME); TSDebug(PLUGIN_NAME, "unknown event for this plugin"); diff --git a/plugins/header_rewrite/parser.cc b/plugins/header_rewrite/parser.cc index cb6e2c5cf3c..701227ed6e8 100644 --- a/plugins/header_rewrite/parser.cc +++ b/plugins/header_rewrite/parser.cc @@ -244,6 +244,14 @@ Parser::cond_is_hook(TSHttpHookID &hook) const hook = TS_REMAP_PSEUDO_HOOK; return true; } + if ("TXN_START_HOOK" == _op) { + hook = TS_HTTP_TXN_START_HOOK; + return true; + } + if ("TXN_CLOSE_HOOK" == _op) { + hook = TS_HTTP_TXN_CLOSE_HOOK; + return true; + } return false; } diff --git a/plugins/header_rewrite/resources.cc b/plugins/header_rewrite/resources.cc index bc861c6c2b7..0578d3b4a82 100644 --- a/plugins/header_rewrite/resources.cc +++ b/plugins/header_rewrite/resources.cc @@ -99,6 +99,24 @@ Resources::gather(const ResourceIDs ids, TSHttpHookID hook) } break; + case TS_HTTP_TXN_START_HOOK: + // Get TCP Info at transaction start + if (client_bufp && client_hdr_loc) { + TSDebug(PLUGIN_NAME, "\tAdding TXN client request header buffers for TXN Start instance"); + bufp = client_bufp; + hdr_loc = client_hdr_loc; + } + break; + + case TS_HTTP_TXN_CLOSE_HOOK: + // Get TCP Info at transaction close + TSDebug(PLUGIN_NAME, "\tAdding TXN close buffers"); + if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) { + TSDebug(PLUGIN_NAME, "could not gather bufp/hdr_loc for request"); + return; + } + break; + default: break; } diff --git a/plugins/header_rewrite/statement.cc b/plugins/header_rewrite/statement.cc index ee342dc8adc..fd2c17f40a7 100644 --- a/plugins/header_rewrite/statement.cc +++ b/plugins/header_rewrite/statement.cc @@ -69,6 +69,8 @@ Statement::initialize_hooks() add_allowed_hook(TS_HTTP_SEND_REQUEST_HDR_HOOK); add_allowed_hook(TS_HTTP_SEND_RESPONSE_HDR_HOOK); add_allowed_hook(TS_REMAP_PSEUDO_HOOK); + add_allowed_hook(TS_HTTP_TXN_START_HOOK); + add_allowed_hook(TS_HTTP_TXN_CLOSE_HOOK); } // Parse URL qualifiers, this one is special since it's used in a few places.