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 2016/03/26 17:27:06 UTC

trafficserver git commit: TS-3639 Implements GeoIP information into header_rewrite conditions and statements

Repository: trafficserver
Updated Branches:
  refs/heads/master cbcaf312b -> 24893fc94


TS-3639 Implements GeoIP information into header_rewrite conditions and statements

This closes #533


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

Branch: refs/heads/master
Commit: 24893fc947a04558fbaa6e5ac18961d7aa41ab75
Parents: cbcaf31
Author: Leif Hedstrom <zw...@apache.org>
Authored: Tue May 26 17:47:50 2015 -0600
Committer: Leif Hedstrom <zw...@apache.org>
Committed: Sat Mar 26 10:26:21 2016 -0600

----------------------------------------------------------------------
 doc/admin-guide/plugins/header_rewrite.en.rst |  41 +++-
 plugins/header_rewrite/Examples/Geo           |   6 +
 plugins/header_rewrite/Makefile.am            |   3 +-
 plugins/header_rewrite/conditions.cc          | 214 +++++++++++++++++++++
 plugins/header_rewrite/conditions.h           |  43 +++++
 plugins/header_rewrite/factory.cc             |   2 +
 plugins/header_rewrite/header_rewrite.cc      |  34 ++++
 plugins/header_rewrite/resources.cc           |   1 +
 plugins/header_rewrite/resources.h            |   4 +
 plugins/header_rewrite/statement.h            |   8 +
 10 files changed, 347 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/trafficserver/blob/24893fc9/doc/admin-guide/plugins/header_rewrite.en.rst
----------------------------------------------------------------------
diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst b/doc/admin-guide/plugins/header_rewrite.en.rst
index 3e98162..3d1f23b 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -215,6 +215,31 @@ Per-Mapping`_ above.
 The ``<part>`` allows the operand to match against just a component of the URL,
 as documented in `URL Parts`_ below.
 
+GEO
+~~~
+::
+    cond %{GEO:<part>} <operand>
+
+Perform a GeoIP lookup of the client-IP, using a 3rd party library and
+DB. Currently only the MaxMind GeoIP API is supported. The default is to
+do a Country lookup, but the following qualifiers are supported:
+
+    %{GEO:COUNTRY}      The country code (e.g. "US")
+    %{GEO:COUNTRY-ISO}  The country ISO code (e.g. 225)
+    %{GEO:ASN}          The AS number for the provider network (e.g. 7922)
+    %{GEO:ASN-NAME}     A descriptive string of the AS provider
+
+These operators can be used both as conditionals, as well as values for
+setting headers. For example::
+
+    cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+    cond %${GEO:COUNTRY} =US
+        set-header ATS-Geo-Country %{GEO:COUNTRY}
+        set-header ATS-Geo-Country-ISO %{GEO:COUNTRY-ISO}
+        set-header ATS-Geo-ASN %{GEO:ASN}
+        set-header ATS-Geo-ASN-NAME %{GEO:ASN-NAME}
+
+
 HEADER
 ~~~~~~
 ::
@@ -268,7 +293,7 @@ NOW
 ~~~
 ::
 
-   cond %{NOW} >1453484915
+    cond %{NOW:<part>} <operand>
 
 This is the current time, in the local timezone as set on the machine,
 typically GMC. Without any further qualifiers, this is the time in seconds
@@ -276,13 +301,13 @@ since EPOCH aka Unix time. Qualifiers can be used to give various other
 values, such as year, month etc.
 ::
 
-   %{NOW:YEAR}      Current year (e.g. 2016)
-   %{NOW:MONTH}     Current month (0-11, 0 == January)
-   %{NOW:DAY}       Current day of the month (1-31)
-   %{NOW:HOUR}      Current hour (0-23, in the 24h system)
-   %{NOW:MIN}       Current minute (0-59}
-   %{NOW:WEEKDAY}   Current weekday (0-6, 0 == Sunday)
-   %{NOW:YEARDAY}   Current day of the year (0-365, 0 == Jan 1st)
+    %{NOW:YEAR}      Current year (e.g. 2016)
+    %{NOW:MONTH}     Current month (0-11, 0 == January)
+    %{NOW:DAY}       Current day of the month (1-31)
+    %{NOW:HOUR}      Current hour (0-23, in the 24h system)
+    %{NOW:MIN}       Current minute (0-59}
+    %{NOW:WEEKDAY}   Current weekday (0-6, 0 == Sunday)
+    %{NOW:YEARDAY}   Current day of the year (0-365, 0 == Jan 1st)
 
 PATH
 ~~~~

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/24893fc9/plugins/header_rewrite/Examples/Geo
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/Examples/Geo b/plugins/header_rewrite/Examples/Geo
new file mode 100644
index 0000000..8213ee8
--- /dev/null
+++ b/plugins/header_rewrite/Examples/Geo
@@ -0,0 +1,6 @@
+cond %{SEND_RESPONSE_HDR_HOOK} [AND]
+cond %${GEO:COUNTRY} =US
+    set-header ATS-Geo-Country %{GEO:COUNTRY}
+    set-header ATS-Geo-Country-ISO %{GEO:COUNTRY-ISO}
+    set-header ATS-Geo-ASN %{GEO:ASN}
+    set-header ATS-Geo-ASN-NAME %{GEO:ASN-NAME}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/24893fc9/plugins/header_rewrite/Makefile.am
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/Makefile.am b/plugins/header_rewrite/Makefile.am
index 5a7acd3..7ef75b1 100644
--- a/plugins/header_rewrite/Makefile.am
+++ b/plugins/header_rewrite/Makefile.am
@@ -31,8 +31,9 @@ header_rewrite_la_SOURCES = \
   resources.cc \
   ruleset.cc \
   statement.cc
-  
+
 header_rewrite_la_LDFLAGS = $(TS_PLUGIN_LDFLAGS)
+header_rewrite_la_LIBADD = $(GEO_LIBS)
 
 bin_PROGRAMS = header_rewrite_test
 header_rewrite_test_SOURCES = parser.cc header_rewrite_test.cc

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/24893fc9/plugins/header_rewrite/conditions.cc
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/conditions.cc b/plugins/header_rewrite/conditions.cc
index 59a98c2..c8a0008 100644
--- a/plugins/header_rewrite/conditions.cc
+++ b/plugins/header_rewrite/conditions.cc
@@ -22,6 +22,7 @@
 #include <sys/time.h>
 #include <unistd.h>
 #include <arpa/inet.h>
+#include <ctype.h>
 #include <sstream>
 
 #include "ts/ts.h"
@@ -709,3 +710,216 @@ ConditionNow::eval(const Resources &res)
 
   return static_cast<const Matchers<int64_t> *>(_matcher)->test(now);
 }
+
+
+// ConditionGeo: Geo-based information (integer). See ConditionGeoCountry for the string version.
+const char *
+ConditionGeo::get_geo_string(const sockaddr *addr)
+{
+  const char *ret = NULL;
+  int v = 4;
+
+  switch (_geo_qual) {
+  // Country database
+  case GEO_QUAL_COUNTRY:
+    switch (addr->sa_family) {
+    case AF_INET:
+      if (gGeoIP[GEOIP_COUNTRY_EDITION]) {
+        uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);
+
+        ret = GeoIP_country_code_by_ipnum(gGeoIP[GEOIP_COUNTRY_EDITION], ip);
+      }
+      break;
+    case AF_INET6: {
+      if (gGeoIP[GEOIP_COUNTRY_EDITION_V6]) {
+        geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;
+
+        v = 6;
+        ret = GeoIP_country_code_by_ipnum_v6(gGeoIP[GEOIP_COUNTRY_EDITION_V6], ip);
+      }
+    } break;
+    default:
+      break;
+    }
+    TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from Country: %s", v, ret);
+    break;
+
+  // ASN database
+  case GEO_QUAL_ASN_NAME:
+    switch (addr->sa_family) {
+    case AF_INET:
+      if (gGeoIP[GEOIP_ASNUM_EDITION]) {
+        uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);
+
+        ret = GeoIP_name_by_ipnum(gGeoIP[GEOIP_ASNUM_EDITION], ip);
+      }
+      break;
+    case AF_INET6: {
+      if (gGeoIP[GEOIP_ASNUM_EDITION_V6]) {
+        geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;
+
+        v = 6;
+        ret = GeoIP_name_by_ipnum_v6(gGeoIP[GEOIP_ASNUM_EDITION_V6], ip);
+      }
+    } break;
+    default:
+      break;
+    }
+    TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from ASN Name: %s", v, ret);
+    break;
+
+  default:
+    break;
+  }
+
+  return ret ? ret : "(unknown)";
+}
+
+int64_t
+ConditionGeo::get_geo_int(const sockaddr *addr)
+{
+  int64_t ret = -1;
+  int v = 4;
+
+  switch (_geo_qual) {
+  // Country Databse
+  case GEO_QUAL_COUNTRY_ISO:
+    switch (addr->sa_family) {
+    case AF_INET:
+      if (gGeoIP[GEOIP_COUNTRY_EDITION]) {
+        uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);
+
+        ret = GeoIP_id_by_ipnum(gGeoIP[GEOIP_COUNTRY_EDITION], ip);
+      }
+      break;
+    case AF_INET6: {
+      if (gGeoIP[GEOIP_COUNTRY_EDITION_V6]) {
+        geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;
+
+        v = 6;
+        ret = GeoIP_id_by_ipnum_v6(gGeoIP[GEOIP_COUNTRY_EDITION_V6], ip);
+      }
+    } break;
+    default:
+      break;
+    }
+    TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from Country ISO: %" PRId64, v, ret);
+    break;
+
+  case GEO_QUAL_ASN: {
+    const char *asn_name = NULL;
+
+    switch (addr->sa_family) {
+    case AF_INET:
+      if (gGeoIP[GEOIP_ASNUM_EDITION]) {
+        uint32_t ip = ntohl(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr);
+
+        asn_name = GeoIP_name_by_ipnum(gGeoIP[GEOIP_ASNUM_EDITION], ip);
+      }
+      break;
+    case AF_INET6:
+      if (gGeoIP[GEOIP_ASNUM_EDITION_V6]) {
+        geoipv6_t ip = reinterpret_cast<const struct sockaddr_in6 *>(addr)->sin6_addr;
+
+        v = 6;
+        asn_name = GeoIP_name_by_ipnum_v6(gGeoIP[GEOIP_ASNUM_EDITION_V6], ip);
+      }
+      break;
+    }
+    if (asn_name) {
+      // This is a little odd, but the strings returned are e.g. "AS1234 Acme Inc"
+      while (*asn_name && !(isdigit(*asn_name))) {
+        ++asn_name;
+      }
+      ret = strtol(asn_name, NULL, 10);
+    }
+  }
+    TSDebug(PLUGIN_NAME, "eval(): Client IPv%d seems to come from ASN #: %" PRId64, v, ret);
+    break;
+
+  // Likely shouldn't trip, should we assert?
+  default:
+    break;
+  }
+
+  return ret;
+}
+
+void
+ConditionGeo::initialize(Parser &p)
+{
+  Condition::initialize(p);
+
+  if (is_int_type()) {
+    Matchers<int64_t> *match = new Matchers<int64_t>(_cond_op);
+
+    match->set(static_cast<int64_t>(strtol(p.get_arg().c_str(), NULL, 10)));
+    _matcher = match;
+  } else {
+    // The default is to have a string matcher
+    Matchers<std::string> *match = new Matchers<std::string>(_cond_op);
+
+    match->set(p.get_arg());
+    _matcher = match;
+  }
+}
+
+void
+ConditionGeo::set_qualifier(const std::string &q)
+{
+  Condition::set_qualifier(q);
+
+  TSDebug(PLUGIN_NAME, "\tParsing %%{GEO:%s} qualifier", q.c_str());
+
+  if (q == "COUNTRY") {
+    _geo_qual = GEO_QUAL_COUNTRY;
+    is_int_type(false);
+  } else if (q == "COUNTRY-ISO") {
+    _geo_qual = GEO_QUAL_COUNTRY_ISO;
+    is_int_type(true);
+  } else if (q == "ASN") {
+    _geo_qual = GEO_QUAL_ASN;
+    is_int_type(true);
+  } else if (q == "ASN-NAME") {
+    _geo_qual = GEO_QUAL_ASN_NAME;
+    is_int_type(false);
+  } else {
+    TSError("[%s] Unknown Geo qualifier: %s", PLUGIN_NAME, q.c_str());
+  }
+}
+
+void
+ConditionGeo::append_value(std::string &s, const Resources &res)
+{
+  std::ostringstream oss;
+
+  if (is_int_type()) {
+    oss << get_geo_int(TSHttpTxnClientAddrGet(res.txnp));
+    s += oss.str();
+    TSDebug(PLUGIN_NAME, "Appending GEO() to evaluation value -> %s", s.c_str());
+  } else {
+    oss << get_geo_string(TSHttpTxnClientAddrGet(res.txnp));
+    s += oss.str();
+    TSDebug(PLUGIN_NAME, "Appending GEO() to evaluation value -> %s", s.c_str());
+  }
+}
+
+bool
+ConditionGeo::eval(const Resources &res)
+{
+  if (is_int_type()) {
+    int64_t geo = get_geo_int(TSHttpTxnClientAddrGet(res.txnp));
+
+    TSDebug(PLUGIN_NAME, "Evaluating GEO() -> %" PRId64, geo);
+
+    return static_cast<const Matchers<int64_t> *>(_matcher)->test(geo);
+  } else {
+    std::string s;
+
+    append_value(s, res);
+    bool rval = static_cast<const Matchers<std::string> *>(_matcher)->test(s);
+
+    TSDebug(PLUGIN_NAME, "Evaluating GEO(): %s - rval: %d", s.c_str(), rval);
+    return rval;
+  }
+}

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/24893fc9/plugins/header_rewrite/conditions.h
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/conditions.h b/plugins/header_rewrite/conditions.h
index 5a77907..bb918a3 100644
--- a/plugins/header_rewrite/conditions.h
+++ b/plugins/header_rewrite/conditions.h
@@ -220,6 +220,7 @@ private:
   };
 };
 
+
 // header
 class ConditionHeader : public Condition
 {
@@ -241,6 +242,7 @@ private:
   bool _client;
 };
 
+
 // path
 class ConditionPath : public Condition
 {
@@ -374,6 +376,7 @@ private:
   DISALLOW_COPY_AND_ASSIGN(ConditionIncomingPort);
 };
 
+
 // Transact Count
 class ConditionTransactCount : public Condition
 {
@@ -392,6 +395,7 @@ private:
   DISALLOW_COPY_AND_ASSIGN(ConditionTransactCount);
 };
 
+
 // now: Keeping track of current time / day / hour etc.
 class ConditionNow : public Condition
 {
@@ -411,4 +415,43 @@ private:
 };
 
 
+// GeoIP class for the "integer" based Geo information pieces
+class ConditionGeo : public Condition
+{
+public:
+  explicit ConditionGeo() : _geo_qual(GEO_QUAL_COUNTRY), _int_type(false)
+  {
+    TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for ConditionGeo");
+  };
+
+  void initialize(Parser &p);
+  void set_qualifier(const std::string &q);
+  void append_value(std::string &s, const Resources &res);
+
+  // These are special for this sub-class
+  bool
+  is_int_type() const
+  {
+    return _int_type;
+  }
+  void
+  is_int_type(bool flag)
+  {
+    _int_type = flag;
+  }
+
+  int64_t get_geo_int(const sockaddr *addr);
+  const char *get_geo_string(const sockaddr *addr);
+
+protected:
+  bool eval(const Resources &res);
+
+private:
+  DISALLOW_COPY_AND_ASSIGN(ConditionGeo);
+
+  GeoQualifiers _geo_qual;
+  bool _int_type;
+};
+
+
 #endif // __CONDITIONS_H

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/24893fc9/plugins/header_rewrite/factory.cc
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/factory.cc b/plugins/header_rewrite/factory.cc
index 1c6a901..c4a2aa4 100644
--- a/plugins/header_rewrite/factory.cc
+++ b/plugins/header_rewrite/factory.cc
@@ -129,6 +129,8 @@ condition_factory(const std::string &cond)
     c = new ConditionTransactCount();
   } else if (c_name == "NOW") {
     c = new ConditionNow();
+  } else if (c_name == "GEO") {
+    c = new ConditionGeo();
   } else {
     TSError("[%s] Unknown condition: %s", PLUGIN_NAME, c_name.c_str());
     return NULL;

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/24893fc9/plugins/header_rewrite/header_rewrite.cc
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/header_rewrite.cc b/plugins/header_rewrite/header_rewrite.cc
index 3f2dea4..7ebf5b4 100644
--- a/plugins/header_rewrite/header_rewrite.cc
+++ b/plugins/header_rewrite/header_rewrite.cc
@@ -31,9 +31,40 @@
 const char PLUGIN_NAME[] = "header_rewrite";
 const char PLUGIN_NAME_DBG[] = "dbg_header_rewrite";
 
+
+// Geo information, currently only Maxmind. These have to be initialized when the plugin loads.
+#if HAVE_GEOIP_H
+#include <GeoIP.h>
+
+GeoIP *gGeoIP[NUM_DB_TYPES];
+
+static void
+initGeoIP()
+{
+  GeoIPDBTypes dbs[] = {GEOIP_COUNTRY_EDITION, GEOIP_COUNTRY_EDITION_V6, GEOIP_ASNUM_EDITION, GEOIP_ASNUM_EDITION_V6};
+
+  for (unsigned i = 0; i < sizeof(dbs) / sizeof(dbs[0]); ++i) {
+    if (!gGeoIP[dbs[i]] && GeoIP_db_avail(dbs[i])) {
+      // GEOIP_STANDARD seems to break threaded apps...
+      gGeoIP[dbs[i]] = GeoIP_open_type(dbs[i], GEOIP_MMAP_CACHE);
+      TSDebug(PLUGIN_NAME, "initialized GeoIP-DB[%d] %s", dbs[i], GeoIP_database_info(gGeoIP[dbs[i]]));
+    }
+  }
+}
+
+#else
+
+static void
+initGeoIP()
+{
+}
+#endif
+
+
 // Forward declaration for the main continuation.
 static int cont_rewrite_headers(TSCont, TSEvent, void *);
 
+
 // Simple wrapper around a configuration file / set. This is useful such that
 // we can reuse most of the code for both global and per-remap rule sets.
 class RulesConfig
@@ -304,6 +335,7 @@ TSPluginInit(int argc, const char *argv[])
   RulesConfig *conf = new RulesConfig;
   bool got_config = false;
 
+  initGeoIP();
   conf->hold();
 
   for (int i = 1; i < argc; ++i) {
@@ -358,7 +390,9 @@ TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size)
     return TS_ERROR;
   }
 
+  initGeoIP();
   TSDebug(PLUGIN_NAME, "Remap plugin is successfully initialized");
+
   return TS_SUCCESS;
 }
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/24893fc9/plugins/header_rewrite/resources.cc
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/resources.cc b/plugins/header_rewrite/resources.cc
index d934e4a..2d40391 100644
--- a/plugins/header_rewrite/resources.cc
+++ b/plugins/header_rewrite/resources.cc
@@ -24,6 +24,7 @@
 #include "resources.h"
 #include "lulu.h"
 
+
 // Collect all resources
 void
 Resources::gather(const ResourceIDs ids, TSHttpHookID hook)

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/24893fc9/plugins/header_rewrite/resources.h
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/resources.h b/plugins/header_rewrite/resources.h
index 69374ab..8ad9cd7 100644
--- a/plugins/header_rewrite/resources.h
+++ b/plugins/header_rewrite/resources.h
@@ -29,6 +29,10 @@
 
 #include "lulu.h"
 
+#if HAVE_GEOIP_H
+#include <GeoIP.h>
+extern GeoIP *gGeoIP[NUM_DB_TYPES];
+#endif
 
 #define TS_REMAP_PSEUDO_HOOK TS_HTTP_LAST_HOOK // Ugly, but use the "last hook" for remap instances.
 

http://git-wip-us.apache.org/repos/asf/trafficserver/blob/24893fc9/plugins/header_rewrite/statement.h
----------------------------------------------------------------------
diff --git a/plugins/header_rewrite/statement.h b/plugins/header_rewrite/statement.h
index 254cf55..a161b83 100644
--- a/plugins/header_rewrite/statement.h
+++ b/plugins/header_rewrite/statement.h
@@ -58,6 +58,13 @@ enum NowQualifiers {
   NOW_QUAL_YEARDAY
 };
 
+enum GeoQualifiers {
+  GEO_QUAL_COUNTRY,
+  GEO_QUAL_COUNTRY_ISO,
+  GEO_QUAL_ASN,
+  GEO_QUAL_ASN_NAME,
+};
+
 
 class Statement
 {
@@ -128,6 +135,7 @@ protected:
   virtual void initialize_hooks();
 
   UrlQualifiers parse_url_qualifier(const std::string &q) const;
+
   NowQualifiers parse_now_qualifier(const std::string &q) const;
   int64_t get_now_qualified(NowQualifiers qual) const;