This is an automated email from the ASF dual-hosted git repository. cmcfarlen pushed a commit to branch 10.2.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit a8468d89c74c4fbbdd75f99d2a5779fa7968aae1 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 (cherry picked from commit 66692510853a4f9a324af7e67cc33f21578c138f) --- 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
