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 2022/08/31 17:49:11 UTC
[trafficserver] branch master updated: Adds efficient IP range matching to HRW conditions (#9031)
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 c58edbb6b Adds efficient IP range matching to HRW conditions (#9031)
c58edbb6b is described below
commit c58edbb6bcdd6383605721b7d457067b1a62faee
Author: Leif Hedstrom <zw...@apache.org>
AuthorDate: Wed Aug 31 11:49:05 2022 -0600
Adds efficient IP range matching to HRW conditions (#9031)
* Adds efficient IP range matching to HRW conditions
* Changes to use the native TextView parser
* Fixes typo in docs
* Undo the hack around macos builds failing
* Adds failure checks from IP range parsing
---
doc/admin-guide/plugins/header_rewrite.en.rst | 15 +++++
plugins/header_rewrite/Makefile.inc | 2 +
plugins/header_rewrite/condition.cc | 4 ++
plugins/header_rewrite/conditions.cc | 95 ++++++++++++++++++++++-----
plugins/header_rewrite/conditions.h | 6 +-
plugins/header_rewrite/ipranges_helper.cc | 43 ++++++++++++
plugins/header_rewrite/ipranges_helper.h | 46 +++++++++++++
plugins/header_rewrite/matcher.h | 57 +++++++++++++++-
plugins/header_rewrite/ruleset.cc | 4 +-
9 files changed, 249 insertions(+), 23 deletions(-)
diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst b/doc/admin-guide/plugins/header_rewrite.en.rst
index 554228a5d..4d331cf35 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -374,6 +374,14 @@ string. Therefore the condition is treated as if it were ::
which is true when the connection is not TLS. The arguments ``H2``, ``IPV4``, and ``IPV6`` work the
same way.
+As a special matcher, the inbound IP addresses can be matched against a list of IP ranges, e.g.
+::
+
+ cond %{INBOUND:REMOTE-ADDR} {192.168.201.0/24,10.0.0.0/8}
+
+Note that this will not work against the non-IP based conditions, such as the protocol families,
+and the configuration parser will error out. The format here is very specific, in particular no
+white spaces are allowed between the ranges.
IP
~~
@@ -400,6 +408,13 @@ actually as a value to an operator, e.g. ::
set-header X-Server-IP %{IP:SERVER}
set-header X-Outbound-IP %{IP:OUTBOUND}
+As a special matcher, the `IP` can be matched against a list of IP ranges, e.g.
+::
+
+ cond %{IP:CLIENT} {192.168.201.0/24,10.0.0.0/8}
+
+The format here is very specific, in particular no white spaces are allowed between the
+ranges.
INTERNAL-TRANSACTION
~~~~~~~~~~~~~~~~~~~~
diff --git a/plugins/header_rewrite/Makefile.inc b/plugins/header_rewrite/Makefile.inc
index a64fffa93..a74c2d6dc 100644
--- a/plugins/header_rewrite/Makefile.inc
+++ b/plugins/header_rewrite/Makefile.inc
@@ -35,6 +35,8 @@ header_rewrite_header_rewrite_la_SOURCES = \
header_rewrite/operators.h \
header_rewrite/regex_helper.cc \
header_rewrite/regex_helper.h \
+ header_rewrite/ipranges_helper.cc \
+ header_rewrite/ipranges_helper.h \
header_rewrite/resources.cc \
header_rewrite/resources.h \
header_rewrite/ruleset.cc \
diff --git a/plugins/header_rewrite/condition.cc b/plugins/header_rewrite/condition.cc
index f407778ed..717742d8c 100644
--- a/plugins/header_rewrite/condition.cc
+++ b/plugins/header_rewrite/condition.cc
@@ -46,6 +46,10 @@ parse_matcher_op(std::string &arg)
arg.erase(arg.length() - 1, arg.length());
return MATCH_REGULAR_EXPRESSION;
break;
+ case '{':
+ arg.erase(0, 1);
+ arg.erase(arg.length() - 1, arg.length());
+ return MATCH_IP_RANGES;
default:
return MATCH_EQUAL;
break;
diff --git a/plugins/header_rewrite/conditions.cc b/plugins/header_rewrite/conditions.cc
index fa0e092ed..3f7ebca11 100644
--- a/plugins/header_rewrite/conditions.cc
+++ b/plugins/header_rewrite/conditions.cc
@@ -528,10 +528,17 @@ ConditionIp::initialize(Parser &p)
{
Condition::initialize(p);
- MatcherType *match = new MatcherType(_cond_op);
+ if (_cond_op == MATCH_IP_RANGES) { // Special hack for IP ranges for now ...
+ MatcherTypeIp *match = new MatcherTypeIp(_cond_op);
- match->set(p.get_arg());
- _matcher = match;
+ match->set(p.get_arg());
+ _matcher = match;
+ } else {
+ MatcherType *match = new MatcherType(_cond_op);
+
+ match->set(p.get_arg());
+ _matcher = match;
+ }
}
void
@@ -557,14 +564,39 @@ ConditionIp::set_qualifier(const std::string &q)
bool
ConditionIp::eval(const Resources &res)
{
- std::string s;
+ if (_matcher->op() == MATCH_IP_RANGES) {
+ const sockaddr *addr = nullptr;
- append_value(s, res);
- bool rval = static_cast<const Matchers<std::string> *>(_matcher)->test(s);
+ switch (_ip_qual) {
+ case IP_QUAL_CLIENT:
+ addr = TSHttpTxnClientAddrGet(res.txnp);
+ break;
+ case IP_QUAL_INBOUND:
+ addr = TSHttpTxnIncomingAddrGet(res.txnp);
+ break;
+ case IP_QUAL_SERVER:
+ addr = TSHttpTxnServerAddrGet(res.txnp);
+ break;
+ case IP_QUAL_OUTBOUND:
+ addr = TSHttpTxnOutgoingAddrGet(res.txnp);
+ break;
+ }
+
+ if (addr) {
+ return static_cast<const Matchers<const sockaddr *> *>(_matcher)->test(addr);
+ } else {
+ return false;
+ }
+ } else {
+ std::string s;
- TSDebug(PLUGIN_NAME, "Evaluating IP(): %s - rval: %d", s.c_str(), rval);
+ append_value(s, res);
+ bool rval = static_cast<const Matchers<std::string> *>(_matcher)->test(s);
- return rval;
+ TSDebug(PLUGIN_NAME, "Evaluating IP(): %s - rval: %d", s.c_str(), rval);
+
+ return rval;
+ }
}
void
@@ -1029,10 +1061,17 @@ ConditionInbound::initialize(Parser &p)
{
Condition::initialize(p);
- MatcherType *match = new MatcherType(_cond_op);
+ if (_cond_op == MATCH_IP_RANGES) { // Special hack for IP ranges for now ...
+ MatcherTypeIp *match = new MatcherTypeIp(_cond_op);
- match->set(p.get_arg());
- _matcher = match;
+ match->set(p.get_arg());
+ _matcher = match;
+ } else {
+ MatcherType *match = new MatcherType(_cond_op);
+
+ match->set(p.get_arg());
+ _matcher = match;
+ }
}
void
@@ -1070,14 +1109,38 @@ ConditionInbound::set_qualifier(const std::string &q)
bool
ConditionInbound::eval(const Resources &res)
{
- std::string s;
+ // Special hack for IP-Ranges since we really don't need to do a string conversion for the comparison.
+ if (_matcher->op() == MATCH_IP_RANGES) {
+ const sockaddr *addr = nullptr;
- append_value(s, res);
- bool rval = static_cast<const Matchers<std::string> *>(_matcher)->test(s);
+ switch (_net_qual) {
+ case NET_QUAL_LOCAL_ADDR:
+ addr = TSHttpTxnIncomingAddrGet(res.txnp);
+ break;
+ case NET_QUAL_REMOTE_ADDR:
+ addr = TSHttpTxnClientAddrGet(res.txnp);
+ break;
+ default:
+ // Only support actual IP addresses of course...
+ TSError("[%s] %%{%s:%s} is not supported, only IP-Addresses allowed", PLUGIN_NAME, TAG, get_qualifier().c_str());
+ break;
+ }
+
+ if (addr) {
+ return static_cast<const Matchers<const sockaddr *> *>(_matcher)->test(addr);
+ } else {
+ return false;
+ }
+ } else {
+ std::string s;
- TSDebug(PLUGIN_NAME, "Evaluating %s(): %s - rval: %d", TAG, s.c_str(), rval);
+ append_value(s, res);
+ bool rval = static_cast<const Matchers<std::string> *>(_matcher)->test(s);
- return rval;
+ TSDebug(PLUGIN_NAME, "Evaluating %s(): %s - rval: %d", TAG, s.c_str(), rval);
+
+ return rval;
+ }
}
void
diff --git a/plugins/header_rewrite/conditions.h b/plugins/header_rewrite/conditions.h
index 323a5bace..aedac2168 100644
--- a/plugins/header_rewrite/conditions.h
+++ b/plugins/header_rewrite/conditions.h
@@ -348,6 +348,7 @@ protected:
class ConditionIp : public Condition
{
typedef Matchers<std::string> MatcherType;
+ typedef Matchers<const sockaddr *> MatcherTypeIp;
public:
explicit ConditionIp() { TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for ConditionIp"); };
@@ -503,8 +504,9 @@ private:
/// Information about the inbound (client) session.
class ConditionInbound : public Condition
{
- using MatcherType = Matchers<std::string>;
- using self = ConditionInbound;
+ using MatcherType = Matchers<std::string>;
+ using MatcherTypeIp = Matchers<const sockaddr *>;
+ using self = ConditionInbound;
public:
explicit ConditionInbound() { TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for ConditionInbound"); };
diff --git a/plugins/header_rewrite/ipranges_helper.cc b/plugins/header_rewrite/ipranges_helper.cc
new file mode 100644
index 000000000..35ee9ded4
--- /dev/null
+++ b/plugins/header_rewrite/ipranges_helper.cc
@@ -0,0 +1,43 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+#include "ipranges_helper.h"
+#include "tscpp/util/TextView.h"
+
+#include <vector>
+
+bool
+ipRangesHelper::addIpRanges(const std::string &s)
+{
+ ts::TextView src{s};
+
+ while (src) {
+ IpAddr start, end;
+
+ if (TS_SUCCESS == ats_ip_range_parse(src.take_prefix_at(','), start, end)) {
+ _ipRanges.mark(start, end);
+ }
+ }
+
+ if (_ipRanges.count() > 0) {
+ TSDebug(PLUGIN_NAME, " Added %zu IP ranges while parsing", _ipRanges.count());
+ return true;
+ } else {
+ TSDebug(PLUGIN_NAME, " No IP ranges added, possibly bad input");
+ return false;
+ }
+}
diff --git a/plugins/header_rewrite/ipranges_helper.h b/plugins/header_rewrite/ipranges_helper.h
new file mode 100644
index 000000000..5f3c5a6bd
--- /dev/null
+++ b/plugins/header_rewrite/ipranges_helper.h
@@ -0,0 +1,46 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+#pragma once
+
+#include "tscore/IpMap.h"
+#include "tscore/ink_inet.h"
+
+#include "ts/ts.h"
+
+#include "lulu.h"
+
+#include <string>
+
+class ipRangesHelper
+{
+public:
+ ~ipRangesHelper() {}
+
+ bool addIpRanges(const std::string &s);
+
+ bool
+ ipRangesMatch(const sockaddr *addr) const
+ {
+ void *ptr = nullptr;
+
+ return _ipRanges.contains(addr, &ptr);
+ }
+
+private:
+ IpMap _ipRanges;
+};
diff --git a/plugins/header_rewrite/matcher.h b/plugins/header_rewrite/matcher.h
index 2e524d979..45787b18a 100644
--- a/plugins/header_rewrite/matcher.h
+++ b/plugins/header_rewrite/matcher.h
@@ -28,6 +28,7 @@
#include "ts/ts.h"
#include "regex_helper.h"
+#include "ipranges_helper.h"
#include "lulu.h"
// Possible operators that we support (at least partially)
@@ -36,6 +37,7 @@ enum MatcherOps {
MATCH_LESS_THEN,
MATCH_GREATER_THEN,
MATCH_REGULAR_EXPRESSION,
+ MATCH_IP_RANGES,
};
///////////////////////////////////////////////////////////////////////////////
@@ -51,6 +53,12 @@ public:
Matcher(const Matcher &) = delete;
void operator=(const Matcher &) = delete;
+ MatcherOps
+ op() const
+ {
+ return _op;
+ }
+
protected:
const MatcherOps _op;
};
@@ -70,7 +78,7 @@ public:
void
setRegex(const std::string & /* data ATS_UNUSED */)
{
- if (!helper.setRegexMatch(_data)) {
+ if (!reHelper.setRegexMatch(_data)) {
std::stringstream ss;
ss << _data;
TSError("[%s] Invalid regex: failed to precompile: %s", PLUGIN_NAME, ss.str().c_str());
@@ -118,6 +126,11 @@ public:
case MATCH_REGULAR_EXPRESSION:
return test_reg(t);
break;
+ case MATCH_IP_RANGES:
+ // This is an error, the Matcher doesn't make sense to match on IP ranges
+ TSError("[%s] Invalid matcher: MATCH_IP_RANGES", PLUGIN_NAME);
+ throw std::runtime_error("Can not match on IP ranges");
+ break;
default:
// ToDo: error
break;
@@ -189,7 +202,7 @@ private:
int ovector[OVECCOUNT];
TSDebug(PLUGIN_NAME, "Test regular expression %s : %s", _data.c_str(), t.c_str());
- if (helper.regexMatch(t.c_str(), t.length(), ovector) > 0) {
+ if (reHelper.regexMatch(t.c_str(), t.length(), ovector) > 0) {
TSDebug(PLUGIN_NAME, "Successfully found regular expression match");
return true;
}
@@ -197,5 +210,43 @@ private:
}
T _data;
- regexHelper helper;
+ regexHelper reHelper;
+};
+
+// Specialized case matcher for the IP addresses matches.
+// ToDo: we should specialize the regex matcher as well.
+template <> class Matchers<const sockaddr *> : public Matcher
+{
+public:
+ explicit Matchers<const sockaddr *>(const MatcherOps op) : Matcher(op) {}
+
+ void
+ set(const std::string &data)
+ {
+ if (!ipHelper.addIpRanges(data)) {
+ TSError("[%s] Invalid IP-range: failed to parse: %s", PLUGIN_NAME, data.c_str());
+ TSDebug(PLUGIN_NAME, "Invalid IP-range: failed to parse: %s", data.c_str());
+ throw std::runtime_error("Malformed IP-range");
+ } else {
+ TSDebug(PLUGIN_NAME, "IP-range precompiled successfully");
+ }
+ }
+
+ bool
+ test(const sockaddr *addr) const
+ {
+ if (ipHelper.ipRangesMatch(addr) > 0) {
+ if (TSIsDebugTagSet(PLUGIN_NAME)) {
+ char text[INET6_ADDRSTRLEN];
+
+ TSDebug(PLUGIN_NAME, "Successfully found IP-range match on %s", getIP(addr, text));
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+private:
+ ipRangesHelper ipHelper;
};
diff --git a/plugins/header_rewrite/ruleset.cc b/plugins/header_rewrite/ruleset.cc
index b2b7d8c1e..c17205102 100644
--- a/plugins/header_rewrite/ruleset.cc
+++ b/plugins/header_rewrite/ruleset.cc
@@ -46,7 +46,7 @@ RuleSet::add_condition(Parser &p, const char *filename, int lineno)
Condition *c = condition_factory(p.get_op());
if (nullptr != c) {
- TSDebug(PLUGIN_NAME, " Adding condition: %%{%s} with arg: %s", p.get_op().c_str(), p.get_arg().c_str());
+ TSDebug(PLUGIN_NAME, " Adding condition: %%{%s} with arg: %s", p.get_op().c_str(), p.get_arg().c_str());
c->initialize(p);
if (!c->set_hook(_hook)) {
delete c;
@@ -76,7 +76,7 @@ RuleSet::add_operator(Parser &p, const char *filename, int lineno)
Operator *o = operator_factory(p.get_op());
if (nullptr != o) {
- TSDebug(PLUGIN_NAME, " Adding operator: %s(%s)=\"%s\"", p.get_op().c_str(), p.get_arg().c_str(), p.get_value().c_str());
+ TSDebug(PLUGIN_NAME, " Adding operator: %s(%s)=\"%s\"", p.get_op().c_str(), p.get_arg().c_str(), p.get_value().c_str());
o->initialize(p);
if (!o->set_hook(_hook)) {
delete o;