You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by am...@apache.org on 2017/07/09 21:25:56 UTC

[trafficserver] branch master updated: header_rewrite: Add "INBOUND" condition for data on the inbound network connection.

This is an automated email from the ASF dual-hosted git repository.

amc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new cbca361  header_rewrite: Add "INBOUND" condition for data on the inbound network connection.
cbca361 is described below

commit cbca36172be9da8e1c5378b6160d028a7bdbe449
Author: Alan M. Carroll <am...@apache.org>
AuthorDate: Fri Jul 7 17:22:00 2017 -0500

    header_rewrite: Add "INBOUND" condition for data on the inbound network connection.
---
 doc/_build/html/.gitignore                    |   0
 doc/admin-guide/plugins/header_rewrite.en.rst | 115 +++++++++++++++++-------
 plugins/header_rewrite/conditions.cc          | 123 ++++++++++++++++++++++++++
 plugins/header_rewrite/conditions.h           |  25 ++++++
 plugins/header_rewrite/expander.cc            |  21 +++++
 plugins/header_rewrite/factory.cc             |   2 +
 plugins/header_rewrite/statement.h            |  13 +++
 7 files changed, 265 insertions(+), 34 deletions(-)

diff --git a/doc/_build/html/.gitignore b/doc/_build/html/.gitignore
deleted file mode 100644
index e69de29..0000000
diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst b/doc/admin-guide/plugins/header_rewrite.en.rst
index 76127b0..418498c 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -16,6 +16,7 @@
    under the License.
 
 .. include:: ../../common.defs
+.. highlight:: none
 
 .. _admin-plugins-header-rewrite:
 
@@ -183,7 +184,7 @@ CLIENT-IP
 Remote IP address, as a string, of the client connection for the current
 transaction.
 
-This condition is *deprecated* as of ATS v7.1.x, please use %{IP:CLIENT} instead.
+This condition is *deprecated* as of ATS v7.1.x, please use `%{INBOUND:REMOTE-ADDR}` instead.
 
 CLIENT-URL
 ~~~~~~~~~~
@@ -276,7 +277,7 @@ ID
 This condition provides access to three identifier values that ATS uses
 internally for things like logging and debugging. Since these are IDs, they
 are mostly useful as a value (operand) to other operators. The three types of
-IDs are
+IDs are ::
 
     %{ID:REQUEST}    A unique, sequence number for the transaction
     %{ID:PROCESS}    A UUID string, generated every time ATS restarts
@@ -287,6 +288,43 @@ arguments to another operator. For example::
 
     set-header ATS-Req-UUID %{ID:UNIQUE}
 
+INBOUND
+~~~~~~~
+::
+
+   cond %{INBOUND:TLS} /./
+
+This condition provides access to information about the inbound (client, user agent) connection to ATS.
+The data that can be checked is ::
+
+   %{INBOUND:LOCAL-ADDR}      The local (ATS) address for the connection. Equivalent to %{IP:INBOUND}.
+   %{INBOUND:LOCAL-PORT}      The local (ATS) port for the connection. Equivalent to %{INCOMING-PORT}.
+   %{INBOUND:REMOTE-ADDR}     The client address for the connection. Equivalent to %{IP::CLIENT}.
+   %{INBOUND:REMOTE-PORT}     The client port for the connection.
+   %{INBOUND:TLS}             The TLS protocol if the connection is over TLS, otherwise the empty string.
+   %{INBOUND:H2}              The string "h2" if the connection is HTTP/2, otherwise the empty string.
+   %{INBOUND:IPV4}            The string "ipv4" if the connection is IPv4, otherwise the emtpy string.
+   %{INBOUND:IPV6}            The string "ipv6" if the connection is IPv6, otherwise the empty string.
+   %{INBOUND:IP-FAMILY}       The IP family, either "ipv4" or "ipv6".
+   %{INBOUND:STACK}           The full protocol stack separated by ','.
+
+All of the tags generated by this condition are from the :ref:`protocol stack tags <protocol_tags>`.
+
+Because of the implicit match rules, using these as conditions is a bit unexpected. The condition
+listed above will be true if the inbound connection is over TLS because ``%{INBOUND:TLS}`` will only
+return a non-empty string for TLS connections. In contrast ::
+
+   cond %{INBOUND:TLS}
+
+will be true for *non*-TLS connections because it will be true when ``%{INBOUND:TLS}`` is the empty
+string. This happens because the default matching is equality and the default value the empty
+string. Therefore the condition is treated as if it were ::
+
+  cond %{INBOUND:TLS}=""
+
+which is true when the connection is not TLS. The arguments ``H2``, ``IPV4``, and ``IPV6`` work the
+same way.
+
 
 INCOMING-PORT
 ~~~~~~~~~~~~~
@@ -297,6 +335,8 @@ INCOMING-PORT
 TCP port, as a decimal integer, on which the incoming client connection was
 made.
 
+This condition is *deprecated* as of ATS v8.0.x, please use ``%{INBOUND:LOCAL-PORT}`` instead.
+
 IP
 ~~
 ::
@@ -307,15 +347,14 @@ This is one of four possible IPs associated with the transaction, with the
 possible parts being
 ::
 
-    %{IP:CLIENT}     Clients IP
-    %{IP:INBOUND}    ATS's server IP the client connected to
-    %{IP:SERVER}     Upstream (next-hop) server IP (typically origin, or parent)
-    %{IP:OUTBOUND}   ATS's outbound IP, that was used to connect upstream (next-hop)
+    %{IP:CLIENT}     Client's IP address. Equivalent to %{INBOUND:REMOTE-ADDR}.
+    %{IP:INBOUND}    ATS's IP address to which the client connected. Equivalent to %{INBOUND:LOCAL-ADDR}
+    %{IP:SERVER}     Upstream (next-hop) server IP address (typically origin, or parent)
+    %{IP:OUTBOUND}   ATS's outbound IP address, that was used to connect upstream (next-hop)
 
-Note that both %{IP:SERVER} and %{IP:OUTBOUND} can be unset, in which case the
+Note that both `%{IP:SERVER}` and `%{IP:OUTBOUND}` can be unset, in which case the
 empty string is returned. The common use for this condition is
-actually as a value to an operator, e.g.
-::
+actually as a value to an operator, e.g. ::
 
    cond %{SEND_RESPONSE_HDR_HOOK}
      set-header X-Client-IP %{IP:CLIENT}
@@ -445,12 +484,8 @@ TXN-COUNT
 
     cond %{TXN-COUNT} <operand>
 
-Returns the current HTTP client session, which permits detection of requests
-which are sharing a client session. Shared client sessions occur when multiple
-simultaneous requests are received for the same cache object. Instead of
-contacting the origin server separately for each of those client requests, one
-origin connection is used to fulfill all of the requests assigned to the shared
-client session.
+Returns the number of transactions (including the current one) that have been sent on the current
+client connection. This can be used to detect if the current transaction is the first transaction.
 
 URL
 ~~~
@@ -735,20 +770,31 @@ QSA    Append the results of the rule to the query string.
 Variable Expansion
 ------------------
 
-Only limited variable expansion is supported in `add-header`_ and `set-header`_
-. Supported substitutions are currently:
-
-============ ==================================================================
-Variable     Description
-============ ==================================================================
-%<proto>     Protocol
-%<port>      Port
-%<chi>       Client IP
-%<cqhl>      Client request length
-%<cqhm>      Client HTTP method
-%<cque>      Client effective URI
-%<cquup>     Client unmapped URI path
-============ ==================================================================
+Only limited variable expansion is supported in `add-header`_ and `set-header`_ . Supported
+substitutions are currently:
+
+======================= ==================================================================================
+Variable                Description
+======================= ==================================================================================
+%<proto>                Protocol
+%<port>                 Port
+%<chi>                  Client IP
+%<cqhl>                 Client request length
+%<cqhm>                 Client HTTP method
+%<cque>                 Client effective URI
+%<cquup>                Client unmapped URI path
+%<INBOUND:LOCAL-ADDR>   The local (ATS) address for the inbound connection.
+%<INBOUND:LOCAL-PORT>   The local (ATS) port for the inbound connection.
+%<INBOUND:REMOTE-ADDR>  The client address for the inbound connectoin.
+%<INBOUND:REMOTE-PORT>  The client port for the inbound connectoin.
+%<INBOUND:TLS>          The TLS protocol for the inbound connection if it is over TLS, otherwise the
+                        empty string.
+%<INBOUND:H2>           The string "h2" if the inbound connection is HTTP/2, otherwise the empty string.
+%<INBOUND:IPV4>         The string "ipv4" if the inbound connection is IPv4, otherwise the emtpy string.
+%<INBOUND:IPV6>         The string "ipv6" if the inbound connection is IPv6, otherwise the empty string.
+%<INBOUND:IP-FAMILY>    The IP family of the inbound connection (either "ipv4" or "ipv6").
+%<INBOUND:STACK>        The full protocol stack of the inbound connection separated by ','.
+======================= ==================================================================================
 
 Variables to be expanded must be enclosed in double quotes ``"``, as in this example using the arbitrary header
 ``Custom-Client-IP``::
@@ -1057,7 +1103,7 @@ Useless Example From Purpose
 Even that useless example from `Purpose`_ in the beginning of this document is
 possible to accomplish::
 
-    cond %{INCOMING-PORT} =8090
+    cond %{INBOUND:LOCAL-PORT} =8090
     cond %{METHOD} =HEAD
     cond %{CLIENT-HEADER:Accept-Language} /es-py/ [NOT]
     cond %{STATUS} =304 [OR]
@@ -1069,7 +1115,7 @@ Add Cache Control Headers Based on Origin Path
 
 This rule adds cache control headers to CDN responses based matching the origin
 path.  One provides a max age and the other provides a “no-cache” statement to
-two different file paths.::
+two different file paths. ::
 
     cond %{SEND_RESPONSE_HDR_HOOK}
     cond %{PATH} /examplepath1/
@@ -1104,8 +1150,9 @@ already set to some value, and the status code is a 2xx::
 Add HSTS
 --------
 
-Add the HTTP Strict Transport Security (HSTS) header if it does not exist: ::
+Add the HTTP Strict Transport Security (HSTS) header if it does not exist and the inbound connection is TLS::
 
     cond %{READ_RESPONSE_HDR_HOOK} [AND]
-    cond %{HEADER:Strict-Transport-Security} =""
-    set-header Strict-Transport-Security "Strict-Transport-Security: max-age=63072000; includeSubDomains; preload"
+    cond %{HEADER:Strict-Transport-Security} ="" [AND]
+    cond %{INBOUND:TLS} /./
+    set-header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
diff --git a/plugins/header_rewrite/conditions.cc b/plugins/header_rewrite/conditions.cc
index 6b95892..f2f81cb 100644
--- a/plugins/header_rewrite/conditions.cc
+++ b/plugins/header_rewrite/conditions.cc
@@ -24,6 +24,7 @@
 #include <arpa/inet.h>
 #include <cctype>
 #include <sstream>
+#include <array>
 
 #include "ts/ts.h"
 
@@ -1181,3 +1182,125 @@ ConditionId::eval(const Resources &res)
     return rval;
   }
 }
+
+void
+ConditionInbound::initialize(Parser &p)
+{
+  Condition::initialize(p);
+
+  MatcherType *match = new MatcherType(_cond_op);
+
+  match->set(p.get_arg());
+  _matcher = match;
+}
+
+void
+ConditionInbound::set_qualifier(const std::string &q)
+{
+  Condition::set_qualifier(q);
+
+  TSDebug(PLUGIN_NAME, "\tParsing %%{%s:%s} qualifier", TAG, q.c_str());
+
+  if (q == "LOCAL-ADDR") {
+    _net_qual = NET_QUAL_LOCAL_ADDR;
+  } else if (q == "LOCAL-PORT") {
+    _net_qual = NET_QUAL_LOCAL_PORT;
+  } else if (q == "REMOTE-ADDR") {
+    _net_qual = NET_QUAL_REMOTE_ADDR;
+  } else if (q == "REMOTE-PORT") {
+    _net_qual = NET_QUAL_REMOTE_PORT;
+  } else if (q == "TLS") {
+    _net_qual = NET_QUAL_TLS;
+  } else if (q == "H2") {
+    _net_qual = NET_QUAL_H2;
+  } else if (q == "IPV4") {
+    _net_qual = NET_QUAL_IPV4;
+  } else if (q == "IPV6") {
+    _net_qual = NET_QUAL_IPV6;
+  } else if (q == "IP-FAMILY") {
+    _net_qual = NET_QUAL_IP_FAMILY;
+  } else if (q == "STACK") {
+    _net_qual = NET_QUAL_STACK;
+  } else {
+    TSError("[%s] Unknown %s() qualifier: %s", PLUGIN_NAME, TAG, q.c_str());
+  }
+}
+
+bool
+ConditionInbound::eval(const Resources &res)
+{
+  std::string s;
+
+  append_value(s, res);
+  bool rval = static_cast<const Matchers<std::string> *>(_matcher)->test(s);
+
+  TSDebug(PLUGIN_NAME, "Evaluating %s(): %s - rval: %d", TAG, s.c_str(), rval);
+
+  return rval;
+}
+
+void
+ConditionInbound::append_value(std::string &s, const Resources &res)
+{
+  this->append_value(s, res, _net_qual);
+}
+
+void
+ConditionInbound::append_value(std::string &s, const Resources &res, NetworkSessionQualifiers qual)
+{
+  const char *zret = nullptr;
+  char text[INET6_ADDRSTRLEN];
+
+  switch (qual) {
+  case NET_QUAL_LOCAL_ADDR: {
+    zret = getIP(TSHttpTxnIncomingAddrGet(res.txnp), text);
+  } break;
+  case NET_QUAL_LOCAL_PORT: {
+    uint16_t port = getPort(TSHttpTxnIncomingAddrGet(res.txnp));
+    snprintf(text, sizeof(text), "%d", port);
+    zret = text;
+  } break;
+  case NET_QUAL_REMOTE_ADDR: {
+    zret = getIP(TSHttpTxnClientAddrGet(res.txnp), text);
+  } break;
+  case NET_QUAL_REMOTE_PORT: {
+    uint16_t port = getPort(TSHttpTxnClientAddrGet(res.txnp));
+    snprintf(text, sizeof(text), "%d", port);
+    zret = text;
+  } break;
+  case NET_QUAL_TLS:
+    zret = TSHttpTxnClientProtocolStackContains(res.txnp, "tls/");
+    break;
+  case NET_QUAL_H2:
+    zret = TSHttpTxnClientProtocolStackContains(res.txnp, "h2");
+    break;
+  case NET_QUAL_IPV4:
+    zret = TSHttpTxnClientProtocolStackContains(res.txnp, "ipv4");
+    break;
+  case NET_QUAL_IPV6:
+    zret = TSHttpTxnClientProtocolStackContains(res.txnp, "ipv6");
+    break;
+  case NET_QUAL_IP_FAMILY:
+    zret = TSHttpTxnClientProtocolStackContains(res.txnp, "ip");
+    break;
+  case NET_QUAL_STACK: {
+    std::array<char const *, 8> tags;
+    int count  = 0;
+    size_t len = 0;
+    TSHttpTxnClientProtocolStackGet(res.txnp, tags.size(), tags.data(), &count);
+    for (int i = 0; i < count; ++i) {
+      len += 1 + strlen(tags[i]);
+    }
+    s.reserve(len);
+    for (int i = 0; i < count; ++i) {
+      if (i)
+        s += ',';
+      s += tags[i];
+    }
+  } break;
+  }
+
+  if (zret) {
+    s += zret;
+  }
+}
diff --git a/plugins/header_rewrite/conditions.h b/plugins/header_rewrite/conditions.h
index 640f1a5..2119b4b 100644
--- a/plugins/header_rewrite/conditions.h
+++ b/plugins/header_rewrite/conditions.h
@@ -498,4 +498,29 @@ private:
   IdQualifiers _id_qual;
 };
 
+/// Information about the inbound (client) session.
+class ConditionInbound : public Condition
+{
+  using MatcherType = Matchers<std::string>;
+  using self        = ConditionInbound;
+
+public:
+  explicit ConditionInbound() { TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for ConditionInbound"); };
+  ConditionInbound(self &) = delete;
+  self &operator=(self &) = delete;
+
+  void initialize(Parser &p);
+  void set_qualifier(const std::string &q);
+  void append_value(std::string &s, const Resources &res);
+  static void append_value(std::string &s, const Resources &res, NetworkSessionQualifiers qual);
+
+  static constexpr const char *TAG = "INBOUND";
+
+protected:
+  bool eval(const Resources &res);
+
+private:
+  NetworkSessionQualifiers _net_qual = NET_QUAL_STACK;
+};
+
 #endif // __CONDITIONS_H
diff --git a/plugins/header_rewrite/expander.cc b/plugins/header_rewrite/expander.cc
index ceb46b7..dd3d1ab 100644
--- a/plugins/header_rewrite/expander.cc
+++ b/plugins/header_rewrite/expander.cc
@@ -28,6 +28,7 @@
 #include "statement.h"
 #include "parser.h"
 #include "expander.h"
+#include "conditions.h"
 
 // Main expander method
 std::string
@@ -122,6 +123,26 @@ VariableExpander::expand(const Resources &res)
       }
       free(url);
       url = nullptr;
+    } else if (variable == "%<INBOUND:REMOTE-ADDR>") {
+      ConditionInbound::append_value(resolved_variable, res, NET_QUAL_REMOTE_ADDR);
+    } else if (variable == "%<INBOUND:REMOTE-PORT>") {
+      ConditionInbound::append_value(resolved_variable, res, NET_QUAL_REMOTE_PORT);
+    } else if (variable == "%<INBOUND:LOCAL-ADDR>") {
+      ConditionInbound::append_value(resolved_variable, res, NET_QUAL_LOCAL_ADDR);
+    } else if (variable == "%<INBOUND:LOCAL-PORT>") {
+      ConditionInbound::append_value(resolved_variable, res, NET_QUAL_LOCAL_PORT);
+    } else if (variable == "%<INBOUND:TLS>") {
+      ConditionInbound::append_value(resolved_variable, res, NET_QUAL_TLS);
+    } else if (variable == "%<INBOUND:H2>") {
+      ConditionInbound::append_value(resolved_variable, res, NET_QUAL_H2);
+    } else if (variable == "%<INBOUND:IPV4>") {
+      ConditionInbound::append_value(resolved_variable, res, NET_QUAL_IPV4);
+    } else if (variable == "%<INBOUND:IPV6>") {
+      ConditionInbound::append_value(resolved_variable, res, NET_QUAL_IPV6);
+    } else if (variable == "%<INBOUND:IP-FAMILY>") {
+      ConditionInbound::append_value(resolved_variable, res, NET_QUAL_IP_FAMILY);
+    } else if (variable == "%<INBOUND:STACK>") {
+      ConditionInbound::append_value(resolved_variable, res, NET_QUAL_STACK);
     }
 
     // TODO(SaveTheRbtz): Can be optimized
diff --git a/plugins/header_rewrite/factory.cc b/plugins/header_rewrite/factory.cc
index 3b70c7a..28defb7 100644
--- a/plugins/header_rewrite/factory.cc
+++ b/plugins/header_rewrite/factory.cc
@@ -141,6 +141,8 @@ condition_factory(const std::string &cond)
     c = new ConditionGeo();
   } else if (c_name == "ID") {
     c = new ConditionId();
+  } else if (c_name == "INBOUND") {
+    c = new ConditionInbound();
   } else {
     TSError("[%s] Unknown condition: %s", PLUGIN_NAME, c_name.c_str());
     return nullptr;
diff --git a/plugins/header_rewrite/statement.h b/plugins/header_rewrite/statement.h
index 69e2df5..8e538ba 100644
--- a/plugins/header_rewrite/statement.h
+++ b/plugins/header_rewrite/statement.h
@@ -81,6 +81,19 @@ enum IpQualifiers {
   IP_QUAL_OUTBOUND,
 };
 
+enum NetworkSessionQualifiers {
+  NET_QUAL_LOCAL_ADDR,  ///< Local address.
+  NET_QUAL_LOCAL_PORT,  ///< Local port.
+  NET_QUAL_REMOTE_ADDR, ///< Remote address.
+  NET_QUAL_REMOTE_PORT, ///< Remote port.
+  NET_QUAL_TLS,         ///< TLS protocol
+  NET_QUAL_H2,          ///< 'h2' or not.
+  NET_QUAL_IPV4,        ///< 'ipv4' or not.
+  NET_QUAL_IPV6,        ///< 'ipv6' or not.
+  NET_QUAL_IP_FAMILY,   ///< IP protocol family.
+  NET_QUAL_STACK,       ///< Full protocol stack.
+};
+
 class Statement
 {
 public:

-- 
To stop receiving notification emails like this one, please contact
['"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>'].