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 6669251085 Add support for more PP fields (#12864)
6669251085 is described below

commit 66692510853a4f9a324af7e67cc33f21578c138f
Author: Masakazu Kitajo <[email protected]>
AuthorDate: Tue Feb 10 16:33:18 2026 -0700

    Add support for more PP fields (#12864)
    
    * Add support for PP2_SUBTYPE_SSL_CIPHER
    
    * Add pptc log field for PP2_SUBTYPE_SSL_CIPHER
    
    * Add support for PP2_SUBTYPE_SSL_VERSION
    
    * Add pptv log field for PP2_SUBTYPE_SSL_VERSION
    
    * Fix copy pasta in documentation
    
    * Change parameter type
    
    * Fix off by one error
    
    * Add a length check
    
    * Print the default value if data sources are unavailable
    
    * Add a test case
---
 doc/admin-guide/logging/formatting.en.rst       |  4 ++
 include/iocore/net/ProxyProtocol.h              |  4 ++
 include/proxy/logging/LogAccess.h               |  2 +
 src/iocore/net/ProxyProtocol.cc                 | 78 +++++++++++++++++++++++++
 src/iocore/net/unit_tests/test_ProxyProtocol.cc | 42 ++++++++++++-
 src/proxy/logging/Log.cc                        | 10 ++++
 src/proxy/logging/LogAccess.cc                  | 42 +++++++++++++
 7 files changed, 180 insertions(+), 2 deletions(-)

diff --git a/doc/admin-guide/logging/formatting.en.rst 
b/doc/admin-guide/logging/formatting.en.rst
index bac1fbeeb6..b85e6407ad 100644
--- a/doc/admin-guide/logging/formatting.en.rst
+++ b/doc/admin-guide/logging/formatting.en.rst
@@ -532,6 +532,10 @@ ppd   Proxy Protocol Destination IP received via Proxy 
Protocol context from the
       Dest IP        to the |TS|
 ppa   Proxy Protocol The Authority TLV from Proxy Protocol context from the LB
       Authority      to the |TS|
+pptc  Proxy Protocol The TLS cipher from Proxy Protocol context from the LB
+      TLS Cipher     to the |TS|
+pptv  Proxy Protocol The TLS version from Proxy Protocol context from the LB
+      TLS version    to the |TS|
 ===== ============== ==========================================================
 
 .. note::
diff --git a/include/iocore/net/ProxyProtocol.h 
b/include/iocore/net/ProxyProtocol.h
index 2cc4848dfb..5c73019144 100644
--- a/include/iocore/net/ProxyProtocol.h
+++ b/include/iocore/net/ProxyProtocol.h
@@ -86,6 +86,8 @@ public:
   void set_ipv6_addrs(const in6_addr &src_addr, uint16_t src_port, const 
in6_addr &dst_addr, uint16_t dst_port);
 
   std::optional<std::string_view> get_tlv(const uint8_t tlvCode) const;
+  std::optional<std::string_view> get_tlv_ssl_version() const;
+  std::optional<std::string_view> get_tlv_ssl_cipher() const;
 
   ProxyProtocolVersion                          version   = 
ProxyProtocolVersion::UNDEFINED;
   uint16_t                                      ip_family = AF_UNSPEC;
@@ -134,6 +136,8 @@ public:
 
 private:
   std::string additional_data;
+
+  std::optional<std::string_view> _get_tlv_ssl_subtype(uint8_t subtype) const;
 };
 
 const size_t PPv1_CONNECTION_HEADER_LEN_MAX = 108;
diff --git a/include/proxy/logging/LogAccess.h 
b/include/proxy/logging/LogAccess.h
index 0cdd6e1098..7cdf8ed48b 100644
--- a/include/proxy/logging/LogAccess.h
+++ b/include/proxy/logging/LogAccess.h
@@ -270,6 +270,8 @@ public:
   int marshal_proxy_protocol_src_ip(char *);                       // STR
   int marshal_proxy_protocol_dst_ip(char *);                       // STR
   int marshal_proxy_protocol_authority(char *);                    // STR
+  int marshal_proxy_protocol_tls_cipher(char *);                   // STR
+  int marshal_proxy_protocol_tls_version(char *);                  // STR
 
   // named fields from within a http header
   //
diff --git a/src/iocore/net/ProxyProtocol.cc b/src/iocore/net/ProxyProtocol.cc
index 0422e70771..96939412c8 100644
--- a/src/iocore/net/ProxyProtocol.cc
+++ b/src/iocore/net/ProxyProtocol.cc
@@ -547,6 +547,84 @@ ProxyProtocol::get_tlv(const uint8_t tlvCode) const
   return std::nullopt;
 }
 
+/*
+ * PP2_TYPE_SSL
+ * struct pp2_tlv_ssl {
+ *   uint8_t  client;
+ *   uint32_t verify;
+ *   struct pp2_tlv sub_tlv[0];
+ * };
+ */
+
+std::optional<std::string_view>
+ProxyProtocol::_get_tlv_ssl_subtype(uint8_t subtype) const
+{
+  if (auto v = tlv.find(PP2_TYPE_SSL); v != tlv.end() && v->second.length() != 
0) {
+    auto ssl = v->second;
+
+    // Is the client connected over TLS
+    if ((ssl.data()[0] & 0x01) == 0) {
+      // Not over TLS
+      return std::nullopt;
+    }
+
+    if (ssl.length() < 5) {
+      return std::nullopt;
+    }
+
+    // Find the given subtype
+    uint16_t    len = ssl.length();
+    const char *p   = ssl.data() + 5; // Skip client (uint8_t) + verify 
(uint32_t)
+    const char *end = ssl.data() + len;
+    while (p != end) {
+      if (end - p < 3) {
+        // The size of a sub TLV entry must be 3 bytes or more
+        Dbg(dbg_ctl_proxyprotocol_v2, "Remaining data (%ld bytes) is not 
enough for a sub TLV field", end - p);
+        return std::nullopt;
+      }
+
+      // Type
+      uint8_t type  = *p;
+      p            += 1;
+
+      // Length
+      uint16_t length  = ntohs(*reinterpret_cast<const uint16_t *>(p));
+      p               += 2;
+
+      // Value
+      if (end - p < length) {
+        // Does not have enough data
+        Dbg(dbg_ctl_proxyprotocol_v2, "Remaining data (%ld bytes) is not 
enough for a TLV field (ID:%u LEN:%hu)", end - p, type,
+            length);
+        return std::nullopt;
+      }
+
+      // Found it?
+      if (type == subtype) {
+        Dbg(dbg_ctl_proxyprotocol, "TLV: ID=%u LEN=%hu", type, length);
+        return std::string_view(p, length);
+      }
+
+      p += length;
+    }
+  }
+  return std::nullopt;
+}
+
+std::optional<std::string_view>
+ProxyProtocol::get_tlv_ssl_version() const
+{
+  // The specification only says "the US-ASCII string representation of the 
TLS version".
+  // HAProxy sends a string returned by SSL_get_version.
+  return this->_get_tlv_ssl_subtype(PP2_SUBTYPE_SSL_VERSION);
+}
+
+std::optional<std::string_view>
+ProxyProtocol::get_tlv_ssl_cipher() const
+{
+  return this->_get_tlv_ssl_subtype(PP2_SUBTYPE_SSL_CIPHER);
+}
+
 int
 ProxyProtocol::set_additional_data(std::string_view data)
 {
diff --git a/src/iocore/net/unit_tests/test_ProxyProtocol.cc 
b/src/iocore/net/unit_tests/test_ProxyProtocol.cc
index 70ead688be..53958b30b2 100644
--- a/src/iocore/net/unit_tests/test_ProxyProtocol.cc
+++ b/src/iocore/net/unit_tests/test_ProxyProtocol.cc
@@ -303,13 +303,16 @@ TEST_CASE("PROXY Protocol v2 Parser", 
"[ProxyProtocol][ProxyProtocolv2]")
       0x55, 0x49, 0x54, 0x0A,                         ///<
       0x21,                                           ///< version & command
       0x11,                                           ///< protocol & family
-      0x00, 0x17,                                     ///< len
+      0x00, 0x2B,                                     ///< len
       0xC0, 0x00, 0x02, 0x01,                         ///< src_addr
       0xC6, 0x33, 0x64, 0x01,                         ///< dst_addr
       0xC3, 0x50,                                     ///< src_port
       0x01, 0xBB,                                     ///< dst_port
       0x01, 0x00, 0x02, 0x68, 0x32,                   /// PP2_TYPE_ALPN (h2)
-      0x02, 0x00, 0x03, 0x61, 0x62, 0x63              /// PP2_TYPE_AUTHORITY 
(abc)
+      0x02, 0x00, 0x03, 0x61, 0x62, 0x63,             /// PP2_TYPE_AUTHORITY 
(abc)
+      0x20, 0x00, 0x11, 0x01, 0x00, 0x00, 0x00, 0x00, /// PP2_TYPE_SSL 
(client=0x01, verify=0)
+      0x23, 0x00, 0x03, 0x58, 0x59, 0x5A,             /// 
PP2_SUBTYPE_SSL_CIPHER (XYZ)
+      0x21, 0x00, 0x03, 0x54, 0x4C, 0x53,             /// 
PP2_SUBTYPE_SSL_VERSION (TLS)
     };
 
     swoc::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data));
@@ -327,6 +330,41 @@ TEST_CASE("PROXY Protocol v2 Parser", 
"[ProxyProtocol][ProxyProtocolv2]")
 
     CHECK(pp_info.tlv[PP2_TYPE_ALPN] == "h2");
     CHECK(pp_info.tlv[PP2_TYPE_AUTHORITY] == "abc");
+
+    CHECK(pp_info.get_tlv_ssl_cipher() == "XYZ");
+    CHECK(pp_info.get_tlv_ssl_version() == "TLS");
+  }
+
+  SECTION("TLVs with PP2_TYPE_SSL but SSL is unused")
+  {
+    uint8_t raw_data[] = {
+      0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, ///< preface
+      0x55, 0x49, 0x54, 0x0A,                         ///<
+      0x21,                                           ///< version & command
+      0x11,                                           ///< protocol & family
+      0x00, 0x14,                                     ///< len
+      0xC0, 0x00, 0x02, 0x01,                         ///< src_addr
+      0xC6, 0x33, 0x64, 0x01,                         ///< dst_addr
+      0xC3, 0x50,                                     ///< src_port
+      0x01, 0xBB,                                     ///< dst_port
+      0x20, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, /// PP2_TYPE_SSL 
(client=0x00, verify=0)
+    };
+
+    swoc::TextView tv(reinterpret_cast<char *>(raw_data), sizeof(raw_data));
+
+    ProxyProtocol pp_info;
+    REQUIRE(proxy_protocol_parse(&pp_info, tv) == tv.size());
+
+    REQUIRE(ats_ip_pton("192.0.2.1:50000", src_addr) == 0);
+    REQUIRE(ats_ip_pton("198.51.100.1:443", dst_addr) == 0);
+
+    CHECK(pp_info.version == ProxyProtocolVersion::V2);
+    CHECK(pp_info.ip_family == AF_INET);
+    CHECK(pp_info.src_addr == src_addr);
+    CHECK(pp_info.dst_addr == dst_addr);
+
+    CHECK(pp_info.get_tlv_ssl_cipher().has_value() == false);
+    CHECK(pp_info.get_tlv_ssl_version().has_value() == false);
   }
 
   SECTION("TLVs with extra data")
diff --git a/src/proxy/logging/Log.cc b/src/proxy/logging/Log.cc
index 35d2c87187..50e975e284 100644
--- a/src/proxy/logging/Log.cc
+++ b/src/proxy/logging/Log.cc
@@ -1034,6 +1034,16 @@ Log::init_fields()
   global_field_list.add(field, false);
   field_symbol_hash.emplace("ppa", field);
 
+  field = new LogField("proxy_protocol_tls_cipher", "pptc", LogField::STRING, 
&LogAccess::marshal_proxy_protocol_tls_cipher,
+                       &LogAccess::unmarshal_str);
+  global_field_list.add(field, false);
+  field_symbol_hash.emplace("pptc", field);
+
+  field = new LogField("proxy_protocol_tls_version", "pptv", LogField::STRING, 
&LogAccess::marshal_proxy_protocol_tls_version,
+                       &LogAccess::unmarshal_str);
+  global_field_list.add(field, false);
+  field_symbol_hash.emplace("pptv", field);
+
   field = new LogField("version_build_number", "vbn", LogField::STRING, 
&LogAccess::marshal_version_build_number,
                        &LogAccess::unmarshal_str);
   global_field_list.add(field, false);
diff --git a/src/proxy/logging/LogAccess.cc b/src/proxy/logging/LogAccess.cc
index 9c0ce48bd4..908ec8c22f 100644
--- a/src/proxy/logging/LogAccess.cc
+++ b/src/proxy/logging/LogAccess.cc
@@ -1664,6 +1664,48 @@ LogAccess::marshal_proxy_protocol_authority(char *buf)
   return len;
 }
 
+int
+LogAccess::marshal_proxy_protocol_tls_cipher(char *buf)
+{
+  int len = INK_MIN_ALIGN;
+
+  if (m_http_sm) {
+    if (auto cipher = m_http_sm->t_state.pp_info.get_tlv_ssl_cipher(); cipher) 
{
+      len = padded_length(cipher->size() + 1);
+      if (buf) {
+        marshal_mem(buf, cipher->data(), cipher->size(), len);
+      }
+    } else {
+      if (buf) {
+        // This prints the default value ("-")
+        marshal_mem(buf, nullptr, 0, len);
+      }
+    }
+  }
+  return len;
+}
+
+int
+LogAccess::marshal_proxy_protocol_tls_version(char *buf)
+{
+  int len = INK_MIN_ALIGN;
+
+  if (m_http_sm) {
+    if (auto version = m_http_sm->t_state.pp_info.get_tlv_ssl_version(); 
version) {
+      len = padded_length(version->size() + 1);
+      if (buf) {
+        marshal_mem(buf, version->data(), version->size(), len);
+      }
+    } else {
+      if (buf) {
+        // This prints the default value ("-")
+        marshal_mem(buf, nullptr, 0, len);
+      }
+    }
+  }
+  return len;
+}
+
 /*-------------------------------------------------------------------------
   -------------------------------------------------------------------------*/
 int

Reply via email to