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

maskit 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 f449d9837c header_rewrite: Add INBOUND_IP_SOURCE controller (#12246)
f449d9837c is described below

commit f449d9837cc4a16e8650bcbb16e2c980b3824922
Author: Masakazu Kitajo <[email protected]>
AuthorDate: Wed Jul 9 11:44:23 2025 -0600

    header_rewrite: Add INBOUND_IP_SOURCE controller (#12246)
    
    * header_rewrite: Add INBOUND_IP_SOURCE controller
    
    * Fix documentation format
    
    * Fix documentation format
    
    * Fix documentation format
    
    * Initialize private slot index
    
    * Add examples
    
     Conflicts:
            doc/admin-guide/plugins/header_rewrite.en.rst
---
 doc/admin-guide/plugins/header_rewrite.en.rst | 39 ++++++++++++++++++++----
 plugins/header_rewrite/conditions.cc          | 44 +++++++++++++++++++++------
 plugins/header_rewrite/conditions.h           | 32 ++++++++++++++++---
 plugins/header_rewrite/operators.cc           | 12 ++++++++
 plugins/header_rewrite/operators.h            |  1 +
 plugins/header_rewrite/statement.h            | 11 ++++++-
 6 files changed, 119 insertions(+), 20 deletions(-)

diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst 
b/doc/admin-guide/plugins/header_rewrite.en.rst
index be45b57c82..13be369d79 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -1147,12 +1147,16 @@ set-plugin-cntl
 This operator lets you control the fundamental behavior of this plugin for a 
particular transaction.
 The available controllers are:
 
-================== ===================== 
=============================================================================================
-Controller         Operators/Conditions  Description
-================== ===================== 
=============================================================================================
-TIMEZONE           ``NOW``               If ``GMT`` is passed, the operators 
and conditions use GMT regardles of the timezone setting
-                                         on your system. The default value is 
``LOCAL``.
-================== ===================== 
=============================================================================================
++===================+========================+==============================================================================================+
+| Controller        | Operators/Conditions   | Description                     
                                                             |
++===================+========================+==============================================================================================+
+| TIMEZONE          | ``NOW``                | If ``GMT`` is passed, the 
operators and conditions use GMT regardles of the timezone setting |
+|                   |                        | on your system. The default 
value is ``LOCAL``.                                              |
++===================+========================+==============================================================================================+
+| INBOUND_IP_SOURCE | ``IP``, ``INBOUND``,   | Selects which IP address to use 
for the operators and conditions. Available sources are      |
+|                   | ``CIDR``, and ``GEO``  | ``PEER`` (Uses the IP address 
of the peer), and ``PROXY`` (Uses the IP address from PROXY    |
+|                   |                        | protocol)                       
                                                             |
++===================+========================+==============================================================================================+
 
 Operator Flags
 --------------
@@ -1685,3 +1689,26 @@ This rule will deny all requests for URIs with the 
``.php`` file extension::
    cond %{REMAP_PSEUDO_HOOK} [AND]
    cond %{CLIENT-URL:PATH} ="php" [EXT,NOCASE]
       set-status 403
+
+Use GMT regardless of system timezone setting
+---------------------------------------------
+
+This rule will change the behavior of %{NOW}. It will always return time in 
GMT.
+
+   cond %{READ_REQUEST_HDR_HOOK}
+      set-plugin-cntl TIMEZONE GMT
+
+   cond %{SEND_RESPONSE_HDR_HOOK}
+     set-header hour %{NOW:HOUR}
+
+Use IP address provided by PROXY protocol
+-----------------------------------------
+
+This rule will change the behavior of all header_rewrite conditions which use 
the client's IP address on a connection.
+Those will pick the address provided by PROXY protocol, instead of the peer's 
address.
+
+   cond %{READ_REQUEST_HDR_HOOK}
+      set-plugin-cntl INBOUND_IP_SOURCE PROXY
+
+   cond %{SEND_RESPONSE_HDR_HOOK}
+      set-header real-ip %{INBOUND:REMOTE-ADDR}
diff --git a/plugins/header_rewrite/conditions.cc 
b/plugins/header_rewrite/conditions.cc
index 92033884d7..8a5a4185c4 100644
--- a/plugins/header_rewrite/conditions.cc
+++ b/plugins/header_rewrite/conditions.cc
@@ -34,6 +34,8 @@
 #include "conditions.h"
 #include "lulu.h"
 
+static const sockaddr *getClientAddr(TSHttpTxn txnp, int txn_private_slot);
+
 // ConditionStatus
 void
 ConditionStatus::initialize(Parser &p)
@@ -560,7 +562,7 @@ ConditionIp::eval(const Resources &res)
 
     switch (_ip_qual) {
     case IP_QUAL_CLIENT:
-      addr = TSHttpTxnClientAddrGet(res.txnp);
+      addr = getClientAddr(res.txnp, _txn_private_slot);
       break;
     case IP_QUAL_INBOUND:
       addr = TSHttpTxnIncomingAddrGet(res.txnp);
@@ -598,7 +600,7 @@ ConditionIp::append_value(std::string &s, const Resources 
&res)
 
   switch (_ip_qual) {
   case IP_QUAL_CLIENT:
-    ip_set = (nullptr != getIP(TSHttpTxnClientAddrGet(res.txnp), ip));
+    ip_set = (nullptr != getIP(getClientAddr(res.txnp, _txn_private_slot), 
ip));
     break;
   case IP_QUAL_INBOUND:
     ip_set = (nullptr != getIP(TSHttpTxnIncomingAddrGet(res.txnp), ip));
@@ -830,9 +832,9 @@ void
 ConditionGeo::append_value(std::string &s, const Resources &res)
 {
   if (is_int_type()) {
-    s += std::to_string(get_geo_int(TSHttpTxnClientAddrGet(res.txnp)));
+    s += std::to_string(get_geo_int(getClientAddr(res.txnp, 
_txn_private_slot)));
   } else {
-    s += get_geo_string(TSHttpTxnClientAddrGet(res.txnp));
+    s += get_geo_string(getClientAddr(res.txnp, _txn_private_slot));
   }
   Dbg(pi_dbg_ctl, "Appending GEO() to evaluation value -> %s", s.c_str());
 }
@@ -844,7 +846,7 @@ ConditionGeo::eval(const Resources &res)
 
   Dbg(pi_dbg_ctl, "Evaluating GEO()");
   if (is_int_type()) {
-    int64_t geo = get_geo_int(TSHttpTxnClientAddrGet(res.txnp));
+    int64_t geo = get_geo_int(getClientAddr(res.txnp, _txn_private_slot));
 
     ret = static_cast<const Matchers<int64_t> *>(_matcher.get())->test(geo, 
res);
   } else {
@@ -1001,7 +1003,7 @@ ConditionCidr::eval(const Resources &res)
 void
 ConditionCidr::append_value(std::string &s, const Resources &res)
 {
-  struct sockaddr const *addr = TSHttpTxnClientAddrGet(res.txnp);
+  struct sockaddr const *addr = getClientAddr(res.txnp, _txn_private_slot);
 
   if (addr) {
     switch (addr->sa_family) {
@@ -1107,7 +1109,7 @@ ConditionInbound::eval(const Resources &res)
       addr = TSHttpTxnIncomingAddrGet(res.txnp);
       break;
     case NET_QUAL_REMOTE_ADDR:
-      addr = TSHttpTxnClientAddrGet(res.txnp);
+      addr = getClientAddr(res.txnp, _txn_private_slot);
       break;
     default:
       // Only support actual IP addresses of course...
@@ -1154,10 +1156,10 @@ ConditionInbound::append_value(std::string &s, const 
Resources &res, NetworkSess
     zret = text;
   } break;
   case NET_QUAL_REMOTE_ADDR: {
-    zret = getIP(TSHttpTxnClientAddrGet(res.txnp), text);
+    zret = getIP(getClientAddr(res.txnp, _txn_private_slot), text);
   } break;
   case NET_QUAL_REMOTE_PORT: {
-    uint16_t port = getPort(TSHttpTxnClientAddrGet(res.txnp));
+    uint16_t port = getPort(getClientAddr(res.txnp, _txn_private_slot));
     snprintf(text, sizeof(text), "%d", port);
     zret = text;
   } break;
@@ -1612,3 +1614,27 @@ ConditionLastCapture::eval(const Resources &res)
 
   return static_cast<const MatcherType *>(_matcher.get())->test(s, res);
 }
+
+static const struct sockaddr *
+getClientAddr(TSHttpTxn txnp, int txn_private_slot)
+{
+  const struct sockaddr *addr = nullptr;
+  int                    addr_len;
+
+  PrivateSlotData private_data;
+  private_data.raw = reinterpret_cast<uint64_t>(TSUserArgGet(txnp, 
txn_private_slot));
+  switch (private_data.ip_source) {
+  case IP_SRC_PEER:
+    addr = TSHttpTxnClientAddrGet(txnp);
+    break;
+  case IP_SRC_PROXY:
+    TSVConnPPInfoGet(TSHttpSsnClientVConnGet(TSHttpTxnSsnGet(txnp)), 
TS_PP_INFO_SRC_ADDR, reinterpret_cast<const char **>(&addr),
+                     &addr_len);
+    break;
+  default:
+    Dbg(pi_dbg_ctl, "Unknown IP source (%d) was specified", 
private_data.ip_source);
+    addr = TSHttpTxnClientAddrGet(txnp);
+    break;
+  }
+  return addr;
+}
diff --git a/plugins/header_rewrite/conditions.h 
b/plugins/header_rewrite/conditions.h
index 7317197bac..db8ae26cc3 100644
--- a/plugins/header_rewrite/conditions.h
+++ b/plugins/header_rewrite/conditions.h
@@ -378,6 +378,12 @@ public:
   void append_value(std::string &s, const Resources &res) override;
 
 protected:
+  bool
+  need_txn_private_slot() const override
+  {
+    return true;
+  }
+
   bool eval(const Resources &res) override;
 
 private:
@@ -472,6 +478,12 @@ private:
   virtual std::string get_geo_string(const sockaddr *addr) const;
 
 protected:
+  bool
+  need_txn_private_slot() const override
+  {
+    return true;
+  }
+
   bool          eval(const Resources &res) override;
   GeoQualifiers _geo_qual = GEO_QUAL_COUNTRY;
   bool          _int_type = false;
@@ -523,6 +535,12 @@ public:
   void append_value(std::string &s, const Resources &res) override;
 
 protected:
+  bool
+  need_txn_private_slot() const override
+  {
+    return true;
+  }
+
   bool eval(const Resources &res) override;
 
 private:
@@ -547,18 +565,24 @@ public:
   ConditionInbound(SelfType &)    = delete;
   SelfType &operator=(SelfType &) = delete;
 
-  void        initialize(Parser &p) override;
-  void        set_qualifier(const std::string &q) override;
-  void        append_value(std::string &s, const Resources &res) override;
-  static void append_value(std::string &s, const Resources &res, 
NetworkSessionQualifiers qual);
+  void initialize(Parser &p) override;
+  void set_qualifier(const std::string &q) override;
+  void append_value(std::string &s, const Resources &res) override;
 
   static constexpr const char *TAG = "INBOUND";
 
 protected:
+  bool
+  need_txn_private_slot() const override
+  {
+    return true;
+  }
+
   bool eval(const Resources &res) override;
 
 private:
   NetworkSessionQualifiers _net_qual = NET_QUAL_STACK;
+  void                     append_value(std::string &s, const Resources &res, 
NetworkSessionQualifiers qual);
 };
 
 class ConditionStringLiteral : public Condition
diff --git a/plugins/header_rewrite/operators.cc 
b/plugins/header_rewrite/operators.cc
index 96d4898a21..edd40f5e01 100644
--- a/plugins/header_rewrite/operators.cc
+++ b/plugins/header_rewrite/operators.cc
@@ -1211,6 +1211,15 @@ OperatorSetPluginCntl::initialize(Parser &p)
     } else {
       TSError("[%s] Unknown value for TIMZEONE control: %s", PLUGIN_NAME, 
value.c_str());
     }
+  } else if (name == "INBOUND_IP_SOURCE") {
+    _name = PluginCtrl::INBOUND_IP_SOURCE;
+    if (value == "PEER") {
+      _value = IP_SRC_PEER;
+    } else if (value == "PROXY") {
+      _value = IP_SRC_PROXY;
+    } else {
+      TSError("[%s] Unknown value for INBOUND_IP_SOURCE control: %s", 
PLUGIN_NAME, value.c_str());
+    }
   }
 }
 
@@ -1238,6 +1247,9 @@ OperatorSetPluginCntl::exec(const Resources &res) const
   case PluginCtrl::TIMEZONE:
     private_data.timezone = _value;
     break;
+  case PluginCtrl::INBOUND_IP_SOURCE:
+    private_data.ip_source = _value;
+    break;
   }
 
   Dbg(pi_dbg_ctl, "   Setting plugin control %d to %d", 
static_cast<int>(_name), _value);
diff --git a/plugins/header_rewrite/operators.h 
b/plugins/header_rewrite/operators.h
index 2d273b19b9..b0943b7e19 100644
--- a/plugins/header_rewrite/operators.h
+++ b/plugins/header_rewrite/operators.h
@@ -466,6 +466,7 @@ public:
 
   enum class PluginCtrl {
     TIMEZONE,
+    INBOUND_IP_SOURCE,
   };
 
 protected:
diff --git a/plugins/header_rewrite/statement.h 
b/plugins/header_rewrite/statement.h
index bf1020451b..acbdcd69d0 100644
--- a/plugins/header_rewrite/statement.h
+++ b/plugins/header_rewrite/statement.h
@@ -208,8 +208,17 @@ private:
 union PrivateSlotData {
   uint64_t raw;
   struct {
-    uint64_t timezone : 1; // TIMEZONE_LOCAL, or TIMEZONE_GMT
+    uint64_t timezone  : 1; // TIMEZONE_LOCAL, or TIMEZONE_GMT
+    uint64_t ip_source : 2; // IP_SRC_PEER, IP_SRC_PROXY, IP_SRC_FORWARDED, or 
IP_SRC_PLUGIN
+    uint64_t unused    : 61;
   };
 };
 
 enum { TIMEZONE_LOCAL, TIMEZONE_GMT };
+
+enum {
+  IP_SRC_PEER,  // Immediate connection
+  IP_SRC_PROXY, // PROXY protocl
+  // IP_SRC_FORWARDED,  // Forwarded header field (TS core needs to support 
the header first. It can be done by a plugin as well.)
+  // IP_SRC_PLUGIN  // Plugin (Needs TS API to set and get a verified client 
IP address)
+};

Reply via email to