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;