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 2018/02/12 05:05:48 UTC

[trafficserver] branch master updated: Adds a new header_rewrite condition %{CIDR}

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

zwoop 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 f14efb4  Adds a new header_rewrite condition %{CIDR}
f14efb4 is described below

commit f14efb4fe11e4751a042c39a7e931f5745148e88
Author: Leif Hedstrom <zw...@apache.org>
AuthorDate: Fri Feb 9 22:22:35 2018 -0700

    Adds a new header_rewrite condition %{CIDR}
    
    This can be used to produced a header (typically an @ header) which
    can then be used in a custom log format to anonymize the client IP
    address. But, one could for example put this into a header that gets
    sent to the Origin server as well, again anonymized.
---
 doc/admin-guide/plugins/header_rewrite.en.rst | 33 +++++++++
 plugins/header_rewrite/conditions.cc          | 99 +++++++++++++++++++++++++++
 plugins/header_rewrite/conditions.h           | 32 +++++++++
 plugins/header_rewrite/factory.cc             |  2 +
 4 files changed, 166 insertions(+)

diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst b/doc/admin-guide/plugins/header_rewrite.en.rst
index 8275853..ef0d38e 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -288,6 +288,39 @@ arguments to another operator. For example::
 
     set-header ATS-Req-UUID %{ID:UNIQUE}
 
+CIDR
+~~
+::
+
+   set-header @Client-CIDR %{CIDR:24,48}
+
+This condition takes the client IP, and applies the provided CIDR style masks
+to the IP, before producing a string. The typical use of this conditions is as
+above, producing a header that contains a IP representation which has some
+privacy properties. It can of course also be used as a regular condition, and
+the output is a string that can be compared against. The two optional
+arguments are as follows:
+
+    IPv4-Mask    Length, in bits, of the IPv4 address to preserve. Default: 24
+    IPv6-Mask    Length, in bits, of the IPv6 address to preserve. Default: 48
+
+The two arguments, if provided, are comma separated. Valid syntax includes
+
+    %{CIDR}         Defaults to 24,48 (as above)
+    %{CIDR:16}      IPv4 CIDR mask is 16 bits, IPv6 mask is 48
+    %{CIDR:18,42}   IPv4 CIDR mask is 18 bits, IPv6 mask is 42 bits
+
+A typical use case is to insert the @-prefixed header as above, and then use
+this header in a custom log format, rather than logging the full client
+IP. Another use case could be to make a special condition on a sub-net,
+e.g. ::
+
+    cond %{CIDR:8} ="8.0.0.0"
+        set-header X-Is-Eight "Yes"
+
+This condition has no requirements other than access to the Client IP, hence,
+it should work in any and all hooks.
+
 INBOUND
 ~~~~~~~
 ::
diff --git a/plugins/header_rewrite/conditions.cc b/plugins/header_rewrite/conditions.cc
index b316fca..75fec11 100644
--- a/plugins/header_rewrite/conditions.cc
+++ b/plugins/header_rewrite/conditions.cc
@@ -1185,6 +1185,105 @@ ConditionId::eval(const Resources &res)
 }
 
 void
+ConditionCidr::initialize(Parser &p)
+{
+  Condition::initialize(p);
+
+  MatcherType *match = new MatcherType(_cond_op);
+
+  match->set(p.get_arg());
+  _matcher = match;
+}
+
+void
+ConditionCidr::set_qualifier(const std::string &q)
+{
+  bool ok = true;
+  int cidr;
+  char *endp;
+
+  Condition::set_qualifier(q);
+
+  TSDebug(PLUGIN_NAME, "\tParsing %%{CIDR:%s} qualifier", q.c_str());
+  cidr = strtol(q.c_str(), &endp, 10);
+  if (cidr >= 0 && cidr <= 32) {
+    _v4_mask.s_addr = UINT32_MAX >> (32 - cidr);
+    _v4_cidr        = cidr;
+    if (endp && (*endp == ',' || *endp == '/' || *endp == ':')) {
+      cidr = strtol(endp + 1, nullptr, 10);
+      if (cidr >= 0 && cidr <= 128) {
+        _v6_cidr = cidr;
+      } else {
+        TSError("[%s] Bad CIDR mask for IPv6: %s", PLUGIN_NAME, q.c_str());
+        ok = false;
+      }
+    }
+  } else {
+    TSError("[%s] Bad CIDR mask for IPv4: %s", PLUGIN_NAME, q.c_str());
+    ok = false;
+  }
+
+  // Update the bit-masks
+  if (ok) {
+    _create_masks();
+  }
+}
+
+bool
+ConditionCidr::eval(const Resources &res)
+{
+  std::string s;
+
+  append_value(s, res);
+  TSDebug(PLUGIN_NAME, "Evaluating CIDR()");
+
+  return static_cast<MatcherType *>(_matcher)->test(s);
+}
+
+void
+ConditionCidr::append_value(std::string &s, const Resources &res)
+{
+  struct sockaddr const *addr = TSHttpTxnClientAddrGet(res.txnp);
+
+  switch (addr->sa_family) {
+  case AF_INET: {
+    char res[INET_ADDRSTRLEN];
+    struct in_addr ipv4 = reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr;
+
+    ipv4.s_addr &= _v4_mask.s_addr;
+    inet_ntop(AF_INET, &ipv4, res, INET_ADDRSTRLEN);
+    if (res[0]) {
+      s += res;
+    }
+  } break;
+  case AF_INET6: {
+    char res[INET6_ADDRSTRLEN];
+    struct in6_addr ipv6 = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;
+
+    if (_v6_zero_bytes > 0) {
+      memset(&ipv6.s6_addr[16 - _v6_zero_bytes], 0, _v6_zero_bytes);
+    }
+    if (_v6_mask != 0xff) {
+      ipv6.s6_addr[16 - _v6_zero_bytes] &= _v6_mask;
+    }
+    inet_ntop(AF_INET6, &ipv6, res, INET6_ADDRSTRLEN);
+    if (res[0]) {
+      s += res;
+    }
+  } break;
+  }
+}
+
+// Little helper function, to create the masks
+void
+ConditionCidr::_create_masks()
+{
+  _v4_mask.s_addr = htonl(UINT32_MAX << (32 - _v4_cidr));
+  _v6_zero_bytes  = (128 - _v6_cidr) / 8;
+  _v6_mask        = 0xff >> ((128 - _v6_cidr) % 8);
+}
+
+void
 ConditionInbound::initialize(Parser &p)
 {
   Condition::initialize(p);
diff --git a/plugins/header_rewrite/conditions.h b/plugins/header_rewrite/conditions.h
index 2119b4b..a5c2db7 100644
--- a/plugins/header_rewrite/conditions.h
+++ b/plugins/header_rewrite/conditions.h
@@ -498,6 +498,38 @@ private:
   IdQualifiers _id_qual;
 };
 
+// cidr: A CIDR masked string representation of the Client's IP.
+class ConditionCidr : public Condition
+{
+  using MatcherType = Matchers<std::string>;
+  using self        = ConditionCidr;
+
+public:
+  explicit ConditionCidr()
+  {
+    _create_masks(); // This must be called here, because we might not have parameters specified
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for ConditionCidr");
+  };
+
+  ConditionCidr(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);
+
+protected:
+  bool eval(const Resources &res);
+
+private:
+  void _create_masks();
+  int _v4_cidr = 24;
+  int _v6_cidr = 48;
+  struct in_addr _v4_mask; // We do a 32-bit & using this mask, for efficiency
+  unsigned char _v6_mask;  // Only need one byte here, since we memset the rest (see next)
+  int _v6_zero_bytes;      // How many initial bytes to memset to 0
+};
+
 /// Information about the inbound (client) session.
 class ConditionInbound : public Condition
 {
diff --git a/plugins/header_rewrite/factory.cc b/plugins/header_rewrite/factory.cc
index 28defb7..2600471 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 == "CIDR") {
+    c = new ConditionCidr();
   } else if (c_name == "INBOUND") {
     c = new ConditionInbound();
   } else {

-- 
To stop receiving notification emails like this one, please contact
zwoop@apache.org.