diff --git a/.bazelversion b/.bazelversion index b61671799..18bb4182d 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -7.4.0 \ No newline at end of file +7.5.0 diff --git a/bazel/extensions/extensions_build_config.bzl b/bazel/extensions/extensions_build_config.bzl index b0d2f52d3..0b3359fe2 100644 --- a/bazel/extensions/extensions_build_config.bzl +++ b/bazel/extensions/extensions_build_config.bzl @@ -537,6 +537,9 @@ EXTENSIONS = { # "envoy.filters.generic.router": "//source/extensions/filters/network/generic_proxy/router:config", # "envoy.generic_proxy.codecs.dubbo": "//source/extensions/filters/network/generic_proxy/codecs/dubbo:config", # "envoy.generic_proxy.codecs.http1": "//source/extensions/filters/network/generic_proxy/codecs/http1:config", + + # Dynamic mocules + "envoy.filters.http.dynamic_modules": "//source/extensions/filters/http/dynamic_modules:factory_registration", } # These can be changed to ["//visibility:public"], for downstream builds which diff --git a/bazel/foreign_cc/001-tlv-support.patch b/bazel/foreign_cc/001-tlv-support.patch deleted file mode 100644 index 666b13b44..000000000 --- a/bazel/foreign_cc/001-tlv-support.patch +++ /dev/null @@ -1,1399 +0,0 @@ -commit 90e99327ff399e21d9320d73c47bee5bec577ebc -Author: Tim Flannagan -Date: Sun Feb 9 12:20:35 2025 -0500 - - upstream_proxy_protocol: Introduce custom TLV support (#37591) - - - - Commit Message: - - This commit introduces support for injecting custom TLVs into the Proxy - Protocol v2 (PP2) header for upstream transport sockets. This enables - xDS control planes to build upstream PP2 headers with greater - flexibility. Previously, upstream PP2 headers only passed through TLVs - from downstream connections when using the Proxy Protocol listener, - limiting customization. - - With this change, users can define custom TLVs by configuring the - `custom_tlvs` field in the upstream_proxy_protocol transport socket - config, or specifying host metadata in a well-known namespace in order - to provide dynamic and more granular control over PP2 header content. - For example: - - ```yaml - transport_socket: - name: envoy.transport_sockets.upstream_proxy_protocol - typed_config: - "@type": type.googleapis.com/envoy.extensions.transport_sockets.proxy_protocol.v3.ProxyProtocolUpstreamTransport - config: - version: V2 - added_tlvs: - - type: 150 - value: Zm9v - - type: 151 - value: YmFy - ``` - - And - - ```yaml - clusters: - - name: httpbin - load_assignment: - ... - endpoints: - - lbEndpoints: - - metadata: - filter_metadata: - envoy.transport_socket_match: - outbound-proxy: true - typed_filter_metadata: - envoy.transport_sockets.proxy_protocol: - "@type": type.googleapis.com/envoy.config.core.v3.ProxyProtocolConfig - added_tlvs: - - type: 0xD7 - value: b3Zy - - type: 0xD8 - value: bmV3 - ... - - ``` - - By decoupling upstream PP2 customization from downstream listener - config, this unlocks more flexible use cases for Proxy Protocol in - upstream paths. - - Additional Description: N/A - Risk Level: Low - Testing: Unit & integration - Docs Changes: Include in the protobuf docs - Release Notes: Included in the changelog as a new feature - Platform Specific Features: N/A - [Optional Runtime guard:] - [Optional Fixes #Issue] https://github.com/envoyproxy/envoy/issues/18520 - [Optional Fixes commit #PR or SHA] - [Optional Deprecated:] - [Optional [API - Considerations](https://github.com/envoyproxy/envoy/blob/main/api/review_checklist.md):] - - --------- - - Signed-off-by: timflannagan - -diff --git a/api/envoy/config/core/v3/proxy_protocol.proto b/api/envoy/config/core/v3/proxy_protocol.proto -index 32747dd228..564e76cb1e 100644 ---- api/envoy/config/core/v3/proxy_protocol.proto -+++ api/envoy/config/core/v3/proxy_protocol.proto -@@ -32,6 +32,15 @@ message ProxyProtocolPassThroughTLVs { - repeated uint32 tlv_type = 2 [(validate.rules).repeated = {items {uint32 {lt: 256}}}]; - } - -+// Represents a single Type-Length-Value (TLV) entry. -+message TlvEntry { -+ // The type of the TLV. Must be a uint8 (0-255) as per the Proxy Protocol v2 specification. -+ uint32 type = 1 [(validate.rules).uint32 = {lt: 256}]; -+ -+ // The value of the TLV. Must be at least one byte long. -+ bytes value = 2 [(validate.rules).bytes = {min_len: 1}]; -+} -+ - message ProxyProtocolConfig { - enum Version { - // PROXY protocol version 1. Human readable format. -@@ -47,4 +56,35 @@ message ProxyProtocolConfig { - // This config controls which TLVs can be passed to upstream if it is Proxy Protocol - // V2 header. If there is no setting for this field, no TLVs will be passed through. - ProxyProtocolPassThroughTLVs pass_through_tlvs = 2; -+ -+ // This config allows additional TLVs to be included in the upstream PROXY protocol -+ // V2 header. Unlike ``pass_through_tlvs``, which passes TLVs from the downstream request, -+ // ``added_tlvs`` provides an extension mechanism for defining new TLVs that are included -+ // with the upstream request. These TLVs may not be present in the downstream request and -+ // can be defined at either the transport socket level or the host level to provide more -+ // granular control over the TLVs that are included in the upstream request. -+ // -+ // Host-level TLVs are specified in the ``metadata.typed_filter_metadata`` field under the -+ // ``envoy.transport_sockets.proxy_protocol`` namespace. -+ // -+ // .. literalinclude:: /_configs/repo/proxy_protocol.yaml -+ // :language: yaml -+ // :lines: 49-57 -+ // :linenos: -+ // :lineno-start: 49 -+ // :caption: :download:`proxy_protocol.yaml ` -+ // -+ // **Precedence behavior**: -+ // -+ // - When a TLV is defined at both the host level and the transport socket level, the value -+ // from the host level configuration takes precedence. This allows users to define default TLVs -+ // at the transport socket level and override them at the host level. -+ // - Any TLV defined in the ``pass_through_tlvs`` field will be overridden by either the host-level -+ // or transport socket-level TLV. -+ repeated TlvEntry added_tlvs = 3; -+} -+ -+message PerHostConfig { -+ // Enables per-host configuration for Proxy Protocol. -+ repeated TlvEntry added_tlvs = 1; - } -diff --git a/configs/proxy_protocol.yaml b/configs/proxy_protocol.yaml -new file mode 100644 -index 0000000000..ab2e10a796 ---- /dev/null -+++ configs/proxy_protocol.yaml -@@ -0,0 +1,72 @@ -+admin: -+ address: -+ socket_address: -+ address: 0.0.0.0 -+ port_value: 9901 -+static_resources: -+ listeners: -+ - name: listener_0 -+ address: -+ socket_address: -+ protocol: TCP -+ address: 0.0.0.0 -+ port_value: 10000 -+ filter_chains: -+ - filters: -+ - name: envoy.filters.network.http_connection_manager -+ typed_config: -+ "@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager" -+ stat_prefix: ingress_http -+ access_log: -+ - name: envoy.access_loggers.stdout -+ typed_config: -+ "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog -+ route_config: -+ name: local_route -+ virtual_hosts: -+ - name: default -+ domains: -+ - "*" -+ routes: -+ - match: -+ prefix: "/" -+ direct_response: -+ status: 200 -+ body: -+ inline_string: "OK" -+ clusters: -+ - name: cluster_0 -+ type: STRICT_DNS -+ connect_timeout: 2s -+ lb_policy: ROUND_ROBIN -+ load_assignment: -+ cluster_name: cluster_0 -+ endpoints: -+ - lb_endpoints: -+ - endpoint: -+ address: -+ socket_address: -+ address: 127.0.0.1 -+ port_value: 8080 -+ metadata: -+ typed_filter_metadata: -+ envoy.transport_sockets.proxy_protocol: -+ "@type": type.googleapis.com/envoy.config.core.v3.PerHostConfig -+ added_tlvs: -+ - type: 0xEE -+ value: b3ZlcnJpZGU= -+ - type: 0xEF -+ value: bmV3dmFsdWU= -+ transport_socket: -+ name: envoy.transport_sockets.upstream_proxy_protocol -+ typed_config: -+ "@type": type.googleapis.com/envoy.extensions.transport_sockets.proxy_protocol.v3.ProxyProtocolUpstreamTransport -+ config: -+ version: V2 -+ added_tlvs: -+ - type: 0xEE -+ value: ZGVmYXVsdA== -+ transport_socket: -+ name: envoy.transport_sockets.raw_buffer -+ typed_config: -+ "@type": type.googleapis.com/envoy.extensions.transport_sockets.raw_buffer.v3.RawBuffer -diff --git a/source/common/config/well_known_names.h b/source/common/config/well_known_names.h -index 02c4a0b264..c31de828da 100644 ---- source/common/config/well_known_names.h -+++ source/common/config/well_known_names.h -@@ -34,6 +34,9 @@ public: - const std::string ENVOY_LB = "envoy.lb"; - // Filter namespace for built-in transport socket match in cluster. - const std::string ENVOY_TRANSPORT_SOCKET_MATCH = "envoy.transport_socket_match"; -+ // Filter namespace for storing custom upstream PP TLVs in metadata. -+ const std::string ENVOY_TRANSPORT_SOCKETS_PROXY_PROTOCOL = -+ "envoy.transport_sockets.proxy_protocol"; - // Proxy address configuration namespace for HTTP/1.1 proxy transport sockets. - const std::string ENVOY_HTTP11_PROXY_TRANSPORT_SOCKET_ADDR = - "envoy.http11_proxy_transport_socket.proxy_address"; -diff --git a/source/extensions/common/proxy_protocol/proxy_protocol_header.cc b/source/extensions/common/proxy_protocol/proxy_protocol_header.cc -index 1c8537964f..a2f7b88d70 100644 ---- source/extensions/common/proxy_protocol/proxy_protocol_header.cc -+++ source/extensions/common/proxy_protocol/proxy_protocol_header.cc -@@ -111,19 +111,47 @@ void generateV2Header(const Network::Address::Ip& source_address, - } - - bool generateV2Header(const Network::ProxyProtocolData& proxy_proto_data, Buffer::Instance& out, -- bool pass_all_tlvs, const absl::flat_hash_set& pass_through_tlvs) { -- uint64_t extension_length = 0; -- for (auto&& tlv : proxy_proto_data.tlv_vector_) { -+ bool pass_all_tlvs, const absl::flat_hash_set& pass_through_tlvs, -+ const std::vector& custom_tlvs) { -+ std::vector combined_tlv_vector; -+ std::vector final_tlvs; -+ combined_tlv_vector.reserve(custom_tlvs.size() + proxy_proto_data.tlv_vector_.size()); -+ -+ absl::flat_hash_set seen_types; -+ for (const auto& tlv : custom_tlvs) { -+ ASSERT(!seen_types.contains(tlv.type)); -+ combined_tlv_vector.emplace_back(tlv); -+ seen_types.insert(tlv.type); -+ } -+ -+ // Combine TLVs from the proxy_proto_data with the custom TLVs. -+ for (const auto& tlv : proxy_proto_data.tlv_vector_) { - if (!pass_all_tlvs && !pass_through_tlvs.contains(tlv.type)) { -+ // Skip any TLV that is not in the set of passthrough TLVs. - continue; - } -- extension_length += PROXY_PROTO_V2_TLV_TYPE_LENGTH_LEN + tlv.value.size(); -- if (extension_length > std::numeric_limits::max()) { -- ENVOY_LOG_MISC( -- warn, "Generating Proxy Protocol V2 header: TLVs exceed length limit {}, already got {}", -- std::numeric_limits::max(), extension_length); -- return false; -+ if (seen_types.contains(tlv.type)) { -+ // Skip any duplicate TLVs from being added to the combined TLV vector. -+ ENVOY_LOG_EVERY_POW_2_MISC(info, "Skipping duplicate TLV type {}", tlv.type); -+ continue; - } -+ seen_types.insert(tlv.type); -+ combined_tlv_vector.emplace_back(tlv); -+ } -+ -+ // Filter out TLVs that would exceed the 65535 limit. -+ uint64_t extension_length = 0; -+ bool skipped_tlvs = false; -+ for (auto&& tlv : combined_tlv_vector) { -+ uint64_t new_size = extension_length + PROXY_PROTO_V2_TLV_TYPE_LENGTH_LEN + tlv.value.size(); -+ if (new_size > std::numeric_limits::max()) { -+ ENVOY_LOG_MISC(warn, "Skipping TLV type {} because adding it would exceed the 65535 limit.", -+ tlv.type); -+ skipped_tlvs = true; -+ continue; -+ } -+ extension_length = new_size; -+ final_tlvs.push_back(tlv); - } - - ASSERT(extension_length <= std::numeric_limits::max()); -@@ -141,17 +169,16 @@ bool generateV2Header(const Network::ProxyProtocolData& proxy_proto_data, Buffer - generateV2Header(src.addressAsString(), dst.addressAsString(), src.port(), dst.port(), - src.version(), static_cast(extension_length), out); - -- // Generate the TLV vector. -- for (auto&& tlv : proxy_proto_data.tlv_vector_) { -- if (!pass_all_tlvs && !pass_through_tlvs.contains(tlv.type)) { -- continue; -- } -+ for (auto&& tlv : combined_tlv_vector) { - out.add(&tlv.type, 1); - uint16_t size = htons(static_cast(tlv.value.size())); - out.add(&size, sizeof(uint16_t)); - out.add(&tlv.value.front(), tlv.value.size()); - } -- return true; -+ -+ // return true if no TLVs were skipped, otherwise false to increment the counter -+ // in the upstream proxy protocol transport socket stats. -+ return !skipped_tlvs; - } - - void generateProxyProtoHeader(const envoy::config::core::v3::ProxyProtocolConfig& config, -diff --git a/source/extensions/common/proxy_protocol/proxy_protocol_header.h b/source/extensions/common/proxy_protocol/proxy_protocol_header.h -index a4a09f46f9..669ede766f 100644 ---- source/extensions/common/proxy_protocol/proxy_protocol_header.h -+++ source/extensions/common/proxy_protocol/proxy_protocol_header.h -@@ -70,7 +70,8 @@ void generateV2LocalHeader(Buffer::Instance& out); - - // Generates the v2 PROXY protocol header including the TLV vector into the specified buffer. - bool generateV2Header(const Network::ProxyProtocolData& proxy_proto_data, Buffer::Instance& out, -- bool pass_all_tlvs, const absl::flat_hash_set& pass_through_tlvs); -+ bool pass_all_tlvs, const absl::flat_hash_set& pass_through_tlvs, -+ const std::vector& custom_tlvs); - - } // namespace ProxyProtocol - } // namespace Common -diff --git a/source/extensions/filters/listener/proxy_protocol/proxy_protocol.cc b/source/extensions/filters/listener/proxy_protocol/proxy_protocol.cc -index 17bf1342b1..3e1d5a8eee 100644 ---- source/extensions/filters/listener/proxy_protocol/proxy_protocol.cc -+++ source/extensions/filters/listener/proxy_protocol/proxy_protocol.cc -@@ -612,6 +612,10 @@ ReadOrParseState Filter::readExtensions(Network::ListenerFilterBuffer& buffer) { - auto raw_slice = buffer.rawSlice(); - // waiting for more data if there is no enough data for extensions. - if (raw_slice.len_ < (proxy_protocol_header_.value().wholeHeaderLength())) { -+ ENVOY_LOG( -+ trace, -+ "waiting for more data to read extensions. Buffer length: {}, extension header length {}", -+ raw_slice.len_, proxy_protocol_header_.value().wholeHeaderLength()); - return ReadOrParseState::TryAgainLater; - } - -diff --git a/source/extensions/transport_sockets/proxy_protocol/BUILD b/source/extensions/transport_sockets/proxy_protocol/BUILD -index 4e91f1ad36..4f3654de43 100644 ---- source/extensions/transport_sockets/proxy_protocol/BUILD -+++ source/extensions/transport_sockets/proxy_protocol/BUILD -@@ -34,9 +34,11 @@ envoy_cc_library( - "//source/common/common:hex_lib", - "//source/common/common:scalar_to_byte_vector_lib", - "//source/common/common:utility_lib", -+ "//source/common/config:well_known_names", - "//source/common/network:address_lib", - "//source/extensions/common/proxy_protocol:proxy_protocol_header_lib", - "//source/extensions/transport_sockets/common:passthrough_lib", - "@envoy_api//envoy/config/core/v3:pkg_cc_proto", -+ "@envoy_api//envoy/extensions/transport_sockets/proxy_protocol/v3:pkg_cc_proto", - ], - ) -diff --git a/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc b/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc -index 672d856d33..ba34b1a616 100644 ---- source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc -+++ source/extensions/transport_sockets/proxy_protocol/proxy_protocol.cc -@@ -3,15 +3,20 @@ - #include - - #include "envoy/config/core/v3/proxy_protocol.pb.h" -+#include "envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.pb.h" -+#include "envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.pb.validate.h" - #include "envoy/network/transport_socket.h" - - #include "source/common/buffer/buffer_impl.h" - #include "source/common/common/hex.h" - #include "source/common/common/scalar_to_byte_vector.h" - #include "source/common/common/utility.h" -+#include "source/common/config/well_known_names.h" - #include "source/common/network/address_impl.h" -+#include "source/common/protobuf/utility.h" - #include "source/extensions/common/proxy_protocol/proxy_protocol_header.h" - -+using envoy::config::core::v3::PerHostConfig; - using envoy::config::core::v3::ProxyProtocolConfig; - using envoy::config::core::v3::ProxyProtocolConfig_Version; - using envoy::config::core::v3::ProxyProtocolPassThroughTLVs; -@@ -36,6 +41,11 @@ UpstreamProxyProtocolSocket::UpstreamProxyProtocolSocket( - pass_through_tlvs_.insert(0xFF & tlv_type); - } - } -+ for (const auto& entry : config.added_tlvs()) { -+ added_tlvs_.push_back(Network::ProxyProtocolTLV{ -+ static_cast(entry.type()), -+ std::vector(entry.value().begin(), entry.value().end())}); -+ } - } - - void UpstreamProxyProtocolSocket::setTransportSocketCallbacks( -@@ -91,9 +101,11 @@ void UpstreamProxyProtocolSocket::generateHeaderV2() { - if (!options_ || !options_->proxyProtocolOptions().has_value()) { - Common::ProxyProtocol::generateV2LocalHeader(header_buffer_); - } else { -+ std::vector custom_tlvs = buildCustomTLVs(); -+ - const auto options = options_->proxyProtocolOptions().value(); - if (!Common::ProxyProtocol::generateV2Header(options, header_buffer_, pass_all_tlvs_, -- pass_through_tlvs_)) { -+ pass_through_tlvs_, custom_tlvs)) { - // There is a warn log in generateV2Header method. - stats_.v2_tlvs_exceed_max_length_.inc(); - } -@@ -165,6 +177,56 @@ void UpstreamProxyProtocolSocketFactory::hashKey( - } - } - -+std::vector UpstreamProxyProtocolSocket::buildCustomTLVs() const { -+ std::vector custom_tlvs; -+ absl::flat_hash_set processed_tlv_types; -+ -+ // Attempt to parse host-level TLVs first. -+ const auto& upstream_info = callbacks_->connection().streamInfo().upstreamInfo(); -+ if (upstream_info && upstream_info->upstreamHost()) { -+ auto metadata = upstream_info->upstreamHost()->metadata(); -+ if (metadata) { -+ const auto filter_it = metadata->typed_filter_metadata().find( -+ Envoy::Config::MetadataFilters::get().ENVOY_TRANSPORT_SOCKETS_PROXY_PROTOCOL); -+ if (filter_it != metadata->typed_filter_metadata().end()) { -+ PerHostConfig host_tlv_metadata; -+ auto status = MessageUtil::unpackTo(filter_it->second, host_tlv_metadata); -+ if (!status.ok()) { -+ ENVOY_LOG(warn, -+ "Failed to unpack custom TLVs from upstream host metadata for host {}. " -+ "Error: {}. Will still use config-level TLVs.", -+ upstream_info->upstreamHost()->address()->asString(), status.message()); -+ } else { -+ // Insert host-level TLVs. -+ for (const auto& entry : host_tlv_metadata.added_tlvs()) { -+ if (processed_tlv_types.contains(entry.type())) { -+ ENVOY_LOG_EVERY_POW_2_MISC(info, "Skipping duplicate TLV type from host metadata {}", -+ entry.type()); -+ continue; -+ } -+ custom_tlvs.push_back(Network::ProxyProtocolTLV{ -+ static_cast(entry.type()), -+ std::vector(entry.value().begin(), entry.value().end())}); -+ processed_tlv_types.insert(entry.type()); -+ } -+ } -+ } -+ } -+ } -+ -+ // If host-level parse failed or was not present, we still read config-level TLVs. -+ for (const auto& tlv : added_tlvs_) { -+ if (processed_tlv_types.contains(tlv.type)) { -+ ENVOY_LOG_EVERY_POW_2_MISC(info, "Skipping duplicate TLV type from added_tlvs {}", tlv.type); -+ continue; -+ } -+ custom_tlvs.push_back(tlv); -+ processed_tlv_types.insert(tlv.type); -+ } -+ -+ return custom_tlvs; -+} -+ - } // namespace ProxyProtocol - } // namespace TransportSockets - } // namespace Extensions -diff --git a/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.h b/source/extensions/transport_sockets/proxy_protocol/proxy_protocol.h -index f86c9e9cce..647ee8d63d 100644 ---- source/extensions/transport_sockets/proxy_protocol/proxy_protocol.h -+++ source/extensions/transport_sockets/proxy_protocol/proxy_protocol.h -@@ -43,6 +43,10 @@ private: - void generateHeader(); - void generateHeaderV1(); - void generateHeaderV2(); -+ // Combine host-level and config-level TLVs, with fallback if metadata fails to unpack. -+ // Host-level has precedence over config-level TLVs. -+ // If we fail to parse host metadata, we still read config TLVs. -+ std::vector buildCustomTLVs() const; - Network::IoResult writeHeader(); - - Network::TransportSocketOptionsConstSharedPtr options_; -@@ -52,6 +56,7 @@ private: - const UpstreamProxyProtocolStats& stats_; - const bool pass_all_tlvs_; - absl::flat_hash_set pass_through_tlvs_{}; -+ std::vector added_tlvs_{}; - }; - - class UpstreamProxyProtocolSocketFactory : public PassthroughFactory { -diff --git a/test/extensions/common/proxy_protocol/proxy_protocol_header_test.cc b/test/extensions/common/proxy_protocol/proxy_protocol_header_test.cc -index 53eb70cc00..3698f0d42c 100644 ---- test/extensions/common/proxy_protocol/proxy_protocol_header_test.cc -+++ test/extensions/common/proxy_protocol/proxy_protocol_header_test.cc -@@ -133,8 +133,7 @@ TEST(ProxyProtocolHeaderTest, GeneratesV2IPv4HeaderWithTLVPassAll) { - Network::ProxyProtocolTLV tlv{0x5, {0x06, 0x07}}; - Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, {tlv}}; - Buffer::OwnedImpl buff{}; -- -- ASSERT_TRUE(generateV2Header(proxy_proto_data, buff, true, {})); -+ ASSERT_TRUE(generateV2Header(proxy_proto_data, buff, true, {}, {})); - - EXPECT_TRUE(TestUtility::buffersEqual(expectedBuff, buff)); - } -@@ -153,7 +152,7 @@ TEST(ProxyProtocolHeaderTest, GeneratesV2IPv4HeaderWithTLVPassEmpty) { - Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, {tlv}}; - Buffer::OwnedImpl buff{}; - -- ASSERT_TRUE(generateV2Header(proxy_proto_data, buff, false, {})); -+ ASSERT_TRUE(generateV2Header(proxy_proto_data, buff, false, {}, {})); - - EXPECT_TRUE(TestUtility::buffersEqual(expectedBuff, buff)); - } -@@ -172,7 +171,7 @@ TEST(ProxyProtocolHeaderTest, GeneratesV2IPv4HeaderWithTLVPassSpecific) { - Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, {tlv}}; - Buffer::OwnedImpl buff{}; - -- ASSERT_TRUE(generateV2Header(proxy_proto_data, buff, false, {0x5})); -+ ASSERT_TRUE(generateV2Header(proxy_proto_data, buff, false, {0x5}, {})); - - EXPECT_TRUE(TestUtility::buffersEqual(expectedBuff, buff)); - } -@@ -192,7 +191,7 @@ TEST(ProxyProtocolHeaderTest, GeneratesV2IPv6HeaderWithTLV) { - Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, {tlv}}; - - Buffer::OwnedImpl buff{}; -- ASSERT_TRUE(generateV2Header(proxy_proto_data, buff, true, {})); -+ ASSERT_TRUE(generateV2Header(proxy_proto_data, buff, true, {}, {})); - - EXPECT_TRUE(TestUtility::buffersEqual(expectedBuff, buff)); - } -@@ -207,8 +206,69 @@ TEST(ProxyProtocolHeaderTest, GeneratesV2WithTLVExceedingLengthLimit) { - Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, {tlv}}; - Buffer::OwnedImpl buff{}; - -- EXPECT_LOG_CONTAINS("warn", "Generating Proxy Protocol V2 header: TLVs exceed length limit 65535", -- generateV2Header(proxy_proto_data, buff, true, {})); -+ EXPECT_LOG_CONTAINS("warn", "Skipping TLV type 5 because adding it would exceed the 65535 limit", -+ generateV2Header(proxy_proto_data, buff, true, {}, {})); -+} -+ -+TEST(ProxyProtocolHeaderTest, GeneratesV2WithCustomTLVs) { -+ const uint8_t v2_protocol[] = { -+ 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a, 0x21, -+ 0x11, 0x00, 0x15, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, 0x01, 0x02, 0x03, 0x05, -+ 0x02, 0x01, 0x08, 0x00, 0x01, 0x08, 0xD3, 0x00, 0x02, 0x06, 0x07, -+ }; -+ -+ const Buffer::OwnedImpl expectedBuff(v2_protocol, sizeof(v2_protocol)); -+ auto src_addr = -+ Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("1.2.3.4", 773)); -+ auto dst_addr = -+ Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("0.1.1.2", 513)); -+ Network::ProxyProtocolTLV tlv{0xD3, {0x06, 0x07}}; -+ Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, {tlv}}; -+ std::vector custom_tlvs = { -+ {0x8, {0x08}}, -+ }; -+ Buffer::OwnedImpl buff{}; -+ -+ ASSERT_TRUE(generateV2Header(proxy_proto_data, buff, false, {0xD3}, custom_tlvs)); -+ EXPECT_TRUE(TestUtility::buffersEqual(expectedBuff, buff)); -+} -+ -+TEST(ProxyProtocolHeaderTest, GeneratesV2WithCustomTLVExceedingLengthLimit) { -+ auto src_addr = -+ Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("1.2.3.4", 773)); -+ auto dst_addr = -+ Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("0.1.1.2", 513)); -+ Network::ProxyProtocolTLV tlv{0x5, {0x06, 0x07}}; -+ Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, {tlv}}; -+ Buffer::OwnedImpl buff{}; -+ std::vector custom_tlvs = { -+ {0x8, std::vector(65536, 'a')}, -+ }; -+ EXPECT_LOG_CONTAINS("warn", "Skipping TLV type 8 because adding it would exceed the 65535 limit", -+ generateV2Header(proxy_proto_data, buff, true, {}, custom_tlvs)); -+} -+ -+TEST(ProxyProtocolHeaderTest, GeneratesV2WithCustomTLVsNoPassthrough) { -+ const uint8_t v2_protocol[] = { -+ 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, -+ 0x0a, 0x21, 0x11, 0x00, 0x10, 0x01, 0x02, 0x03, 0x04, 0x00, 0x01, -+ 0x01, 0x02, 0x03, 0x05, 0x02, 0x01, 0xD3, 0x00, 0x01, 0x09, -+ }; -+ -+ const Buffer::OwnedImpl expectedBuff(v2_protocol, sizeof(v2_protocol)); -+ auto src_addr = -+ Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("1.2.3.4", 773)); -+ auto dst_addr = -+ Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv4Instance("0.1.1.2", 513)); -+ Network::ProxyProtocolTLV tlv{0xD5, {0x06, 0x07}}; -+ Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, {tlv}}; -+ std::vector custom_tlvs = { -+ {0xD3, {0x09}}, -+ }; -+ Buffer::OwnedImpl buff{}; -+ -+ ASSERT_TRUE(generateV2Header(proxy_proto_data, buff, false, {}, custom_tlvs)); -+ EXPECT_TRUE(TestUtility::buffersEqual(expectedBuff, buff)); - } - - } // namespace -diff --git a/test/extensions/transport_sockets/proxy_protocol/BUILD b/test/extensions/transport_sockets/proxy_protocol/BUILD -index 0aa2be2b93..057a853897 100644 ---- test/extensions/transport_sockets/proxy_protocol/BUILD -+++ test/extensions/transport_sockets/proxy_protocol/BUILD -@@ -25,6 +25,7 @@ envoy_extension_cc_test( - "//test/mocks/network:network_mocks", - "//test/mocks/network:transport_socket_mocks", - "@envoy_api//envoy/config/core/v3:pkg_cc_proto", -+ "@envoy_api//envoy/extensions/transport_sockets/proxy_protocol/v3:pkg_cc_proto", - ], - ) - -diff --git a/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_integration_test.cc b/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_integration_test.cc -index ef2875dc8e..04a59d2490 100644 ---- test/extensions/transport_sockets/proxy_protocol/proxy_protocol_integration_test.cc -+++ test/extensions/transport_sockets/proxy_protocol/proxy_protocol_integration_test.cc -@@ -9,6 +9,7 @@ - #include "test/integration/http_integration.h" - #include "test/integration/integration.h" - -+using envoy::config::core::v3::PerHostConfig; - using envoy::config::core::v3::ProxyProtocolPassThroughTLVs; - namespace Envoy { - namespace { -@@ -415,11 +416,16 @@ public: - fake_upstreams_.clear(); - } - -- void setup(bool pass_all_tlvs, const std::vector& tlvs_listener, -- const std::vector& tlvs_upstream) { -+ void setup( -+ bool pass_all_tlvs, const std::vector& tlvs_listener, -+ const std::vector& tlvs_upstream, -+ const std::vector>>& custom_tlvs_from_host, -+ const std::vector>>& custom_tlvs_from_config) { - pass_all_tlvs_ = pass_all_tlvs; - tlvs_listener_.assign(tlvs_listener.begin(), tlvs_listener.end()); - tlvs_upstream_.assign(tlvs_upstream.begin(), tlvs_upstream.end()); -+ custom_tlvs_from_host_.assign(custom_tlvs_from_host.begin(), custom_tlvs_from_host.end()); -+ custom_tlvs_from_config_.assign(custom_tlvs_from_config.begin(), custom_tlvs_from_config.end()); - } - - void initialize() override { -@@ -463,6 +469,38 @@ public: - } - } - -+ // Add custom TLVs to host metadata if needed. -+ if (!custom_tlvs_from_host_.empty()) { -+ // Modify LB endpoints with metadata -+ auto* metadata = bootstrap.mutable_static_resources() -+ ->mutable_clusters(0) -+ ->mutable_load_assignment() -+ ->mutable_endpoints(0) -+ ->mutable_lb_endpoints(0) -+ ->mutable_metadata(); -+ -+ PerHostConfig tlvs_metadata; -+ for (const auto& tlv : custom_tlvs_from_host_) { -+ auto entry = tlvs_metadata.add_added_tlvs(); -+ entry->set_type(tlv.first); -+ entry->set_value(std::string(tlv.second.begin(), tlv.second.end())); -+ } -+ ProtobufWkt::Any typed_metadata; -+ typed_metadata.PackFrom(tlvs_metadata); -+ const std::string metadata_key = -+ Config::MetadataFilters::get().ENVOY_TRANSPORT_SOCKETS_PROXY_PROTOCOL; -+ metadata->mutable_typed_filter_metadata()->emplace( -+ std::make_pair(metadata_key, typed_metadata)); -+ } -+ // Add custom TLVs to proxy protocol config if needed. -+ if (!custom_tlvs_from_config_.empty()) { -+ for (const auto& tlv : custom_tlvs_from_config_) { -+ auto entry = proxy_protocol.add_added_tlvs(); -+ entry->set_type(tlv.first); -+ entry->set_value(std::string(tlv.second.begin(), tlv.second.end())); -+ } -+ } -+ - envoy::extensions::transport_sockets::proxy_protocol::v3::ProxyProtocolUpstreamTransport - proxy_proto_transport; - proxy_proto_transport.mutable_transport_socket()->MergeFrom(inner_socket); -@@ -479,6 +517,8 @@ private: - bool pass_all_tlvs_ = false; - std::vector tlvs_listener_; - std::vector tlvs_upstream_; -+ std::vector>> custom_tlvs_from_host_; -+ std::vector>> custom_tlvs_from_config_; - }; - - INSTANTIATE_TEST_SUITE_P(IpVersions, ProxyProtocolTLVsIntegrationTest, -@@ -488,7 +528,7 @@ INSTANTIATE_TEST_SUITE_P(IpVersions, ProxyProtocolTLVsIntegrationTest, - // This test adding the listener proxy protocol filter and upstream proxy filter, the TLVs - // are passed by listener and re-generated in transport socket based on API config. - TEST_P(ProxyProtocolTLVsIntegrationTest, TestV2TLVProxyProtocolPassSepcificTLVs) { -- setup(false, {0x05, 0x06}, {0x06}); -+ setup(false, {0x05, 0x06}, {0x06}, {}, {}); - initialize(); - - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("listener_0")); -@@ -559,7 +599,7 @@ TEST_P(ProxyProtocolTLVsIntegrationTest, TestV2TLVProxyProtocolPassSepcificTLVs) - } - - TEST_P(ProxyProtocolTLVsIntegrationTest, TestV2TLVProxyProtocolPassAll) { -- setup(true, {}, {}); -+ setup(true, {}, {}, {}, {}); - initialize(); - - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("listener_0")); -@@ -641,7 +681,7 @@ TEST_P(ProxyProtocolTLVsIntegrationTest, TestV2TLVProxyProtocolPassAll) { - } - - TEST_P(ProxyProtocolTLVsIntegrationTest, TestV2ProxyProtocolPassWithTypeLocal) { -- setup(true, {}, {}); -+ setup(true, {}, {}, {}, {}); - initialize(); - - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("listener_0")); -@@ -681,5 +721,193 @@ TEST_P(ProxyProtocolTLVsIntegrationTest, TestV2ProxyProtocolPassWithTypeLocal) { - ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); - } - -+TEST_P(ProxyProtocolTLVsIntegrationTest, TestV2TLVProxyProtocolWithCustomMetadata) { -+ std::vector>> custom_tlvs = { -+ {0x96, {'f', 'o', 'o'}}, {0x97, {'b', 'a', 'r'}}}; -+ setup(false, {}, {}, custom_tlvs, {}); -+ initialize(); -+ -+ IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("listener_0")); -+ std::string observed_data; -+ -+ if (GetParam() == Envoy::Network::Address::IpVersion::v4) { -+ // Expected downstream proxy protocol header (IPv4). -+ const uint8_t v2_protocol[] = { -+ 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a, 0x21, 0x11, 0x00, -+ 0x0c, 0x00, 0x01, 0xc0, 0xa8, 0x00, 0x01, 0xc0, 0xa8, 0x00, 0x02, 0x1f, 0x90, 0x23, 0xc4, -+ }; -+ Buffer::OwnedImpl buffer(v2_protocol, sizeof(v2_protocol)); -+ ASSERT_TRUE(tcp_client->write(buffer.toString())); -+ ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection_)); -+ ASSERT_TRUE(fake_upstream_connection_->waitForData( -+ 42, &observed_data)); // 30 inbound size + 12 for custom TLVs. -+ // Verify the custom TLV was injected from filter metadata. -+ EXPECT_EQ(observed_data.size(), 42); -+ size_t offset = 28; // Start after the header -+ // Verify custom TLV 0x96 -+ EXPECT_EQ(static_cast(observed_data[offset]), 0x96); -+ EXPECT_EQ(static_cast(observed_data[offset + 1]), 0x00); -+ EXPECT_EQ(static_cast(observed_data[offset + 2]), 0x03); -+ EXPECT_EQ(observed_data.substr(offset + 3, 3), "foo"); -+ offset += 6; -+ // Verify custom TLV 0x97 -+ EXPECT_EQ(static_cast(observed_data[offset]), 0x97); -+ EXPECT_EQ(static_cast(observed_data[offset + 1]), 0x00); -+ EXPECT_EQ(static_cast(observed_data[offset + 2]), 0x03); -+ EXPECT_EQ(observed_data.substr(offset + 3, 3), "bar"); -+ offset += 6; -+ } else if (GetParam() == Envoy::Network::Address::IpVersion::v6) { -+ // Expected downstream proxy protocol header (IPv6). -+ const uint8_t v2_protocol_ipv6[] = { -+ 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a, 0x21, -+ 0x21, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x02, -+ }; -+ Buffer::OwnedImpl buffer(v2_protocol_ipv6, sizeof(v2_protocol_ipv6)); -+ ASSERT_TRUE(tcp_client->write(buffer.toString())); -+ ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection_)); -+ ASSERT_TRUE(fake_upstream_connection_->waitForData( -+ 64, &observed_data)); // 52 inbound size + 12 for custom TLVs. -+ // Verify the custom TLV was injected from filter metadata. -+ EXPECT_EQ(observed_data.size(), 64); -+ size_t offset = 52; // Start after the header -+ // Verify custom TLV 0x96 -+ EXPECT_EQ(static_cast(observed_data[offset]), 0x96); -+ EXPECT_EQ(static_cast(observed_data[offset + 1]), 0x00); -+ EXPECT_EQ(static_cast(observed_data[offset + 2]), 0x03); -+ EXPECT_EQ(observed_data.substr(offset + 3, 3), "foo"); -+ offset += 6; -+ // Verify custom TLV 0x97 -+ EXPECT_EQ(static_cast(observed_data[offset]), 0x97); -+ EXPECT_EQ(static_cast(observed_data[offset + 1]), 0x00); -+ EXPECT_EQ(static_cast(observed_data[offset + 2]), 0x03); -+ EXPECT_EQ(observed_data.substr(offset + 3, 3), "bar"); -+ offset += 6; -+ } -+ -+ tcp_client->close(); -+ ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); -+} -+ -+TEST_P(ProxyProtocolTLVsIntegrationTest, TestV2TLVProxyProtocolWithPrecedence) { -+ // Setup with passthrough TLVs (0x05, 0x06), config-level TLVs (0x96, 0x97), -+ // and host metadata TLVs (0x96 overrides config, 0x98, and 0x05 overrides passthrough). -+ std::vector>> host_metadata_tlvs = { -+ {0x96, {'o', 'v', 'r'}}, -+ {0x98, {'n', 'e', 'w'}}, -+ {0x05, {'f', 'o', 'o'}}, -+ }; -+ std::vector>> config_tlvs = { -+ {0x96, {'f', 'o', 'o'}}, -+ {0x97, {'b', 'a', 'r'}}, -+ }; -+ -+ setup(true, {}, {}, host_metadata_tlvs, config_tlvs); -+ initialize(); -+ -+ IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("listener_0")); -+ std::string observed_data; -+ -+ if (GetParam() == Envoy::Network::Address::IpVersion::v4) { -+ // 2 TLVs are included: -+ // 0x05, 0x00, 0x02, 0x06, 0x07 -+ // 0x06, 0x00, 0x02, 0x11, 0x12 -+ const uint8_t v2_protocol[] = { -+ 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a, 0x21, -+ 0x11, 0x00, 0x16, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x03, 0x05, -+ 0x02, 0x01, 0x05, 0x00, 0x02, 0x06, 0x07, 0x06, 0x00, 0x02, 0x11, 0x12, -+ }; -+ Buffer::OwnedImpl buffer(v2_protocol, sizeof(v2_protocol)); -+ ASSERT_TRUE(tcp_client->write(buffer.toString())); -+ ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection_)); -+ ASSERT_TRUE(fake_upstream_connection_->waitForData(57, &observed_data)); -+ EXPECT_EQ(observed_data.size(), 57); -+ -+ size_t offset = 28; // Start after the header -+ // Verify host metadata TLV 0x96 overrides config TLV 0x96 value. -+ EXPECT_EQ(static_cast(observed_data[offset]), 0x96); -+ EXPECT_EQ(static_cast(observed_data[offset + 1]), 0x00); -+ EXPECT_EQ(static_cast(observed_data[offset + 2]), 0x03); -+ EXPECT_EQ(observed_data.substr(offset + 3, 3), "ovr"); -+ offset += 6; -+ // Verify host metadata TLV 0x98 is present. -+ EXPECT_EQ(static_cast(observed_data[offset]), 0x98); -+ EXPECT_EQ(static_cast(observed_data[offset + 1]), 0x00); -+ EXPECT_EQ(static_cast(observed_data[offset + 2]), 0x03); -+ EXPECT_EQ(observed_data.substr(offset + 3, 3), "new"); -+ offset += 6; -+ // Verify passthrough TLV 0x05 was overridden by host metadata. -+ EXPECT_EQ(static_cast(observed_data[offset]), 0x05); -+ EXPECT_EQ(static_cast(observed_data[offset + 1]), 0x00); -+ EXPECT_EQ(static_cast(observed_data[offset + 2]), 0x03); -+ EXPECT_EQ(observed_data.substr(offset + 3, 3), "foo"); -+ offset += 6; -+ // Verify config TLV 0x97 is present. -+ EXPECT_EQ(static_cast(observed_data[offset]), 0x97); -+ EXPECT_EQ(static_cast(observed_data[offset + 1]), 0x00); -+ EXPECT_EQ(static_cast(observed_data[offset + 2]), 0x03); -+ EXPECT_EQ(observed_data.substr(offset + 3, 3), "bar"); -+ offset += 6; -+ // Verify passthrough TLV 0x06 is present. -+ EXPECT_EQ(static_cast(observed_data[offset]), 0x06); -+ EXPECT_EQ(static_cast(observed_data[offset + 1]), 0x00); -+ EXPECT_EQ(static_cast(observed_data[offset + 2]), 0x02); -+ EXPECT_EQ(static_cast(observed_data[offset + 3]), 0x11); -+ EXPECT_EQ(static_cast(observed_data[offset + 4]), 0x12); -+ } else if (GetParam() == Envoy::Network::Address::IpVersion::v6) { -+ // 2 TLVs are included: -+ // 0x05, 0x00, 0x02, 0x06, 0x07 -+ // 0x06, 0x00, 0x02, 0x09, 0x0A -+ const uint8_t v2_protocol_ipv6[] = { -+ 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a, 0x21, -+ 0x21, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x02, -+ 0x05, 0x00, 0x02, 0x06, 0x07, 0x06, 0x00, 0x02, 0x09, 0x0A, -+ }; -+ Buffer::OwnedImpl buffer(v2_protocol_ipv6, sizeof(v2_protocol_ipv6)); -+ ASSERT_TRUE(tcp_client->write(buffer.toString())); -+ ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection_)); -+ ASSERT_TRUE(fake_upstream_connection_->waitForData(81, &observed_data)); -+ EXPECT_EQ(observed_data.size(), 81); -+ -+ size_t offset = 52; // Start after the header -+ // Verify host metadata TLV 0x96 overrides config TLV 0x96 value. -+ EXPECT_EQ(static_cast(observed_data[offset]), 0x96); -+ EXPECT_EQ(static_cast(observed_data[offset + 1]), 0x00); -+ EXPECT_EQ(static_cast(observed_data[offset + 2]), 0x03); -+ EXPECT_EQ(observed_data.substr(offset + 3, 3), "ovr"); -+ offset += 6; -+ // Verify host metadata TLV 0x98 is present. -+ EXPECT_EQ(static_cast(observed_data[offset]), 0x98); -+ EXPECT_EQ(static_cast(observed_data[offset + 1]), 0x00); -+ EXPECT_EQ(static_cast(observed_data[offset + 2]), 0x03); -+ EXPECT_EQ(observed_data.substr(offset + 3, 3), "new"); -+ offset += 6; -+ // Verify passthrough TLV 0x05 was overridden by host metadata. -+ EXPECT_EQ(static_cast(observed_data[offset]), 0x05); -+ EXPECT_EQ(static_cast(observed_data[offset + 1]), 0x00); -+ EXPECT_EQ(static_cast(observed_data[offset + 2]), 0x03); -+ EXPECT_EQ(observed_data.substr(offset + 3, 3), "foo"); -+ offset += 6; -+ // Verify config TLV 0x97 is present. -+ EXPECT_EQ(static_cast(observed_data[offset]), 0x97); -+ EXPECT_EQ(static_cast(observed_data[offset + 1]), 0x00); -+ EXPECT_EQ(static_cast(observed_data[offset + 2]), 0x03); -+ EXPECT_EQ(observed_data.substr(offset + 3, 3), "bar"); -+ offset += 6; -+ // Verify passthrough TLV 0x06 -+ EXPECT_EQ(static_cast(observed_data[offset]), 0x06); -+ EXPECT_EQ(static_cast(observed_data[offset + 1]), 0x00); -+ EXPECT_EQ(static_cast(observed_data[offset + 2]), 0x02); -+ EXPECT_EQ(static_cast(observed_data[offset + 3]), 0x09); -+ EXPECT_EQ(static_cast(observed_data[offset + 4]), 0x0A); -+ } -+ -+ tcp_client->close(); -+ ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect()); -+} -+ - } // namespace - } // namespace Envoy -diff --git a/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc b/test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc -index 748a411337..9c8abb2a69 100644 ---- test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc -+++ test/extensions/transport_sockets/proxy_protocol/proxy_protocol_test.cc -@@ -1,4 +1,6 @@ - #include "envoy/config/core/v3/proxy_protocol.pb.h" -+#include "envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.pb.h" -+#include "envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.pb.validate.h" - #include "envoy/network/proxy_protocol.h" - - #include "source/common/buffer/buffer_impl.h" -@@ -23,10 +25,10 @@ using testing::Return; - using testing::ReturnNull; - using testing::ReturnRef; - -+using envoy::config::core::v3::PerHostConfig; - using envoy::config::core::v3::ProxyProtocolConfig; - using envoy::config::core::v3::ProxyProtocolConfig_Version; - using envoy::config::core::v3::ProxyProtocolPassThroughTLVs; -- - namespace Envoy { - namespace Extensions { - namespace TransportSockets { -@@ -491,7 +493,7 @@ TEST_F(ProxyProtocolTest, V2IPV4DownstreamAddressesAndTLVs) { - ->setRemoteAddress(*Network::Utility::resolveUrl("tcp://3.3.3.3:80")); - Buffer::OwnedImpl expected_buff{}; - absl::flat_hash_set pass_tlvs_set{}; -- Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, true, pass_tlvs_set); -+ Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, true, pass_tlvs_set, {}); - - ProxyProtocolConfig config; - config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); -@@ -530,7 +532,8 @@ TEST_F(ProxyProtocolTest, V2IPV4PassSpecificTLVs) { - ->setRemoteAddress(*Network::Utility::resolveUrl("tcp://3.3.3.3:80")); - Buffer::OwnedImpl expected_buff{}; - absl::flat_hash_set pass_tlvs_set{0x05}; -- Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, false, pass_tlvs_set); -+ Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, false, pass_tlvs_set, -+ {}); - - ProxyProtocolConfig config; - config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); -@@ -570,7 +573,8 @@ TEST_F(ProxyProtocolTest, V2IPV4PassEmptyTLVs) { - ->setRemoteAddress(*Network::Utility::resolveUrl("tcp://3.3.3.3:80")); - Buffer::OwnedImpl expected_buff{}; - absl::flat_hash_set pass_tlvs_set{}; -- Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, false, pass_tlvs_set); -+ Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, false, pass_tlvs_set, -+ {}); - - ProxyProtocolConfig config; - config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); -@@ -614,6 +618,15 @@ TEST_F(ProxyProtocolTest, V2IPV4TLVsExceedLengthLimit) { - config.mutable_pass_through_tlvs()->set_match_type(ProxyProtocolPassThroughTLVs::INCLUDE_ALL); - initialize(config, socket_options); - -+ // expect the counter to be incremented but the output header to be written -+ // without the large TLV. -+ EXPECT_CALL(io_handle_, write(_)) -+ .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { -+ auto length = buffer.length(); -+ buffer.drain(length); -+ return {length, Api::IoError::none()}; -+ })); -+ - auto msg = Buffer::OwnedImpl("some data"); - proxy_protocol_socket_->doWrite(msg, false); - EXPECT_EQ(stats_store_.counter("upstream.proxyprotocol.v2_tlvs_exceed_max_length").value(), 1); -@@ -638,7 +651,8 @@ TEST_F(ProxyProtocolTest, V2IPV6DownstreamAddressesAndTLVs) { - ->setRemoteAddress(*Network::Utility::resolveUrl("tcp://[e:b:c:f::]:8080")); - Buffer::OwnedImpl expected_buff{}; - absl::flat_hash_set pass_through_tlvs{}; -- Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, true, pass_through_tlvs); -+ Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, true, pass_through_tlvs, -+ {}); - - ProxyProtocolConfig config; - config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); -@@ -676,8 +690,8 @@ TEST_F(ProxyProtocolTest, V2IPV6DownstreamAddressesAndTLVsWithoutPassConfig) { - ->setRemoteAddress(*Network::Utility::resolveUrl("tcp://[e:b:c:f::]:8080")); - Buffer::OwnedImpl expected_buff{}; - absl::flat_hash_set pass_through_tlvs{}; -- Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, false, -- pass_through_tlvs); -+ Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, false, pass_through_tlvs, -+ {}); - - ProxyProtocolConfig config; - config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); -@@ -695,6 +709,364 @@ TEST_F(ProxyProtocolTest, V2IPV6DownstreamAddressesAndTLVsWithoutPassConfig) { - proxy_protocol_socket_->doWrite(msg, false); - } - -+// Test verifies the happy path for custom TLVs defined in the config. -+TEST_F(ProxyProtocolTest, V2CustomTLVsFromConfig) { -+ auto src_addr = -+ Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv6Instance("1:2:3::4", 8)); -+ auto dst_addr = Network::Address::InstanceConstSharedPtr( -+ new Network::Address::Ipv6Instance("1:100:200:3::", 2)); -+ Network::ProxyProtocolTLVVector tlv_vector{Network::ProxyProtocolTLV{0x5, {'a', 'b', 'c'}}}; -+ Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, tlv_vector}; -+ Network::TransportSocketOptionsConstSharedPtr socket_options = -+ std::make_shared( -+ "", std::vector{}, std::vector{}, std::vector{}, -+ absl::optional(proxy_proto_data)); -+ transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ -+ ->setLocalAddress(*Network::Utility::resolveUrl("tcp://[1:100:200:3::]:50000")); -+ transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ -+ ->setRemoteAddress(*Network::Utility::resolveUrl("tcp://[e:b:c:f::]:8080")); -+ -+ absl::flat_hash_set pass_through_tlvs{}; -+ std::vector custom_tlvs = { -+ {0x96, {'m', 'o', 'r', 'e', 'd', 'a', 't', 'a'}}, -+ }; -+ Buffer::OwnedImpl expected_buff{}; -+ EXPECT_TRUE(Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, false, -+ pass_through_tlvs, custom_tlvs)); -+ -+ ProxyProtocolConfig config; -+ config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); -+ auto host_added_tlvs = config.add_added_tlvs(); -+ host_added_tlvs->set_type(0x96); -+ host_added_tlvs->set_value("moredata"); -+ initialize(config, socket_options); -+ -+ EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) -+ .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { -+ auto length = buffer.length(); -+ buffer.drain(length); -+ return {length, Api::IoError::none()}; -+ })); -+ auto msg = Buffer::OwnedImpl("some data"); -+ EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); -+ -+ auto resp = proxy_protocol_socket_->doWrite(msg, false); -+ EXPECT_EQ(resp.bytes_processed_, expected_buff.length()); -+} -+ -+// Test verifies the happy path for TLVs added from host metadata. -+TEST_F(ProxyProtocolTest, V2CustomTLVsFromHostMetadata) { -+ auto src_addr = -+ Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv6Instance("1:2:3::4", 8)); -+ auto dst_addr = Network::Address::InstanceConstSharedPtr( -+ new Network::Address::Ipv6Instance("1:100:200:3::", 2)); -+ Network::ProxyProtocolTLVVector tlv_vector{Network::ProxyProtocolTLV{0x5, {'a', 'b', 'c'}}}; -+ Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, tlv_vector}; -+ Network::TransportSocketOptionsConstSharedPtr socket_options = -+ std::make_shared( -+ "", std::vector{}, std::vector{}, std::vector{}, -+ absl::optional(proxy_proto_data)); -+ transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ -+ ->setLocalAddress(*Network::Utility::resolveUrl("tcp://[1:100:200:3::]:50000")); -+ transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ -+ ->setRemoteAddress(*Network::Utility::resolveUrl("tcp://[e:b:c:f::]:8080")); -+ -+ auto host = std::make_shared>(); -+ auto metadata = std::make_shared(); -+ const std::string metadata_key = -+ Config::MetadataFilters::get().ENVOY_TRANSPORT_SOCKETS_PROXY_PROTOCOL; -+ -+ PerHostConfig host_metadata_config; -+ auto host_added_tlvs = host_metadata_config.add_added_tlvs(); -+ host_added_tlvs->set_type(0x96); -+ host_added_tlvs->set_value("moredata"); -+ -+ ProtobufWkt::Any typed_metadata; -+ typed_metadata.PackFrom(host_metadata_config); -+ metadata->mutable_typed_filter_metadata()->emplace(std::make_pair(metadata_key, typed_metadata)); -+ EXPECT_CALL(*host, metadata()).Times(testing::AnyNumber()).WillRepeatedly(Return(metadata)); -+ transport_callbacks_.connection_.streamInfo().upstreamInfo()->setUpstreamHost(host); -+ -+ absl::flat_hash_set pass_through_tlvs{}; -+ std::vector custom_tlvs = { -+ {0x96, {'m', 'o', 'r', 'e', 'd', 'a', 't', 'a'}}, -+ }; -+ Buffer::OwnedImpl expected_buff{}; -+ EXPECT_TRUE(Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, false, -+ pass_through_tlvs, custom_tlvs)); -+ -+ ProxyProtocolConfig config; -+ config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); -+ auto config_added_tlvs = config.add_added_tlvs(); -+ config_added_tlvs->set_type(0x96); -+ config_added_tlvs->set_value("moredata"); -+ initialize(config, socket_options); -+ -+ EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) -+ .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { -+ auto length = buffer.length(); -+ buffer.drain(length); -+ return {length, Api::IoError::none()}; -+ })); -+ auto msg = Buffer::OwnedImpl("some data"); -+ EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); -+ -+ auto resp = proxy_protocol_socket_->doWrite(msg, false); -+ EXPECT_EQ(resp.bytes_processed_, expected_buff.length()); -+} -+ -+// Test verifies combined precedence: host-level > config-level > passthrough-level TLVs when keys -+// overlap. -+TEST_F(ProxyProtocolTest, V2CombinedPrecedenceHostConfigPassthrough) { -+ auto src_addr = -+ Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv6Instance("1:2:3::4", 8)); -+ auto dst_addr = Network::Address::InstanceConstSharedPtr( -+ new Network::Address::Ipv6Instance("1:100:200:3::", 2)); -+ Network::ProxyProtocolTLVVector tlv_vector{ -+ Network::ProxyProtocolTLV{0x99, {'p', 'a', 's', 's', 'V', 'a', 'l', 'u', 'e'}}}; -+ Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, tlv_vector}; -+ Network::TransportSocketOptionsConstSharedPtr socket_options = -+ std::make_shared( -+ "", std::vector{}, std::vector{}, std::vector{}, -+ absl::optional(proxy_proto_data)); -+ transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ -+ ->setLocalAddress(*Network::Utility::resolveUrl("tcp://[1:100:200:3::]:50000")); -+ transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ -+ ->setRemoteAddress(*Network::Utility::resolveUrl("tcp://[e:b:c:f::]:8080")); -+ -+ auto host = std::make_shared>(); -+ auto metadata = std::make_shared(); -+ const std::string metadata_key = -+ Config::MetadataFilters::get().ENVOY_TRANSPORT_SOCKETS_PROXY_PROTOCOL; -+ -+ PerHostConfig host_metadata_config; -+ auto host_added_tlvs = host_metadata_config.add_added_tlvs(); -+ host_added_tlvs->set_type(0x99); -+ host_added_tlvs->set_value("hostValue"); -+ -+ ProtobufWkt::Any typed_metadata; -+ typed_metadata.PackFrom(host_metadata_config); -+ metadata->mutable_typed_filter_metadata()->emplace(std::make_pair(metadata_key, typed_metadata)); -+ EXPECT_CALL(*host, metadata()).WillRepeatedly(Return(metadata)); -+ transport_callbacks_.connection_.streamInfo().upstreamInfo()->setUpstreamHost(host); -+ -+ absl::flat_hash_set pass_through_tlvs{0x99}; -+ std::vector custom_tlvs = { -+ {0x99, {'p', 'a', 's', 's', 'V', 'a', 'l', 'u', 'e'}}}; -+ std::vector expected_custom_tlvs = { -+ {0x99, {'h', 'o', 's', 't', 'V', 'a', 'l', 'u', 'e'}}}; -+ Buffer::OwnedImpl expected_buff{}; -+ EXPECT_TRUE(Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, false, -+ pass_through_tlvs, expected_custom_tlvs)); -+ -+ ProxyProtocolConfig config; -+ config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); -+ auto config_added_tlvs = config.add_added_tlvs(); -+ config_added_tlvs->set_type(0x99); -+ config_added_tlvs->set_value("configValue"); -+ initialize(config, socket_options); -+ -+ EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) -+ .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { -+ auto length = buffer.length(); -+ buffer.drain(length); -+ return {length, Api::IoError::none()}; -+ })); -+ auto msg = Buffer::OwnedImpl("some data"); -+ EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); -+ -+ auto resp = proxy_protocol_socket_->doWrite(msg, false); -+ EXPECT_EQ(resp.bytes_processed_, expected_buff.length()); -+} -+ -+// Test verifies that duplicate TLVs within the config and host metadata are properly handled. -+TEST_F(ProxyProtocolTest, V2DuplicateTLVsInConfigAndMetadataHandledProperly) { -+ auto src_addr = -+ Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv6Instance("1:2:3::4", 8)); -+ auto dst_addr = Network::Address::InstanceConstSharedPtr( -+ new Network::Address::Ipv6Instance("1:100:200:3::", 2)); -+ Network::ProxyProtocolTLVVector tlv_vector{Network::ProxyProtocolTLV{0x5, {'a', 'b', 'c'}}}; -+ Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, tlv_vector}; -+ Network::TransportSocketOptionsConstSharedPtr socket_options = -+ std::make_shared( -+ "", std::vector{}, std::vector{}, std::vector{}, -+ absl::optional(proxy_proto_data)); -+ transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ -+ ->setLocalAddress(*Network::Utility::resolveUrl("tcp://[1:100:200:3::]:50000")); -+ transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ -+ ->setRemoteAddress(*Network::Utility::resolveUrl("tcp://[e:b:c:f::]:8080")); -+ -+ auto host = std::make_shared>(); -+ auto metadata = std::make_shared(); -+ const std::string metadata_key = -+ Config::MetadataFilters::get().ENVOY_TRANSPORT_SOCKETS_PROXY_PROTOCOL; -+ -+ PerHostConfig host_metadata_config; -+ auto host_added_tlvs = host_metadata_config.add_added_tlvs(); -+ host_added_tlvs->set_type(0x98); -+ host_added_tlvs->set_value("d1"); -+ auto duplicate_host_entry = host_metadata_config.add_added_tlvs(); -+ duplicate_host_entry->set_type(0x98); -+ duplicate_host_entry->set_value("d2"); // Last duplicate value -+ ProtobufWkt::Any typed_metadata; -+ typed_metadata.PackFrom(host_metadata_config); -+ metadata->mutable_typed_filter_metadata()->emplace(std::make_pair(metadata_key, typed_metadata)); -+ EXPECT_CALL(*host, metadata()).WillRepeatedly(Return(metadata)); -+ transport_callbacks_.connection_.streamInfo().upstreamInfo()->setUpstreamHost(host); -+ -+ absl::flat_hash_set pass_through_tlvs{}; -+ // The output buffer will include the host TLVs before the config TLVs. -+ std::vector custom_tlvs = { -+ {0x98, {'d', '1'}}, -+ {0x96, {'b', 'a', 'r'}}, -+ {0x97, {'b', 'a', 'z'}}, -+ }; -+ Buffer::OwnedImpl expected_buff{}; -+ EXPECT_TRUE(Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, false, -+ pass_through_tlvs, custom_tlvs)); -+ -+ // Configure duplicate TLVs in the configuration. -+ ProxyProtocolConfig config; -+ config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); -+ auto tlv = config.add_added_tlvs(); -+ tlv->set_type(0x96); -+ tlv->set_value("bar"); -+ auto duplicate_tlv_entry = config.add_added_tlvs(); -+ duplicate_tlv_entry->set_type(0x96); -+ duplicate_tlv_entry->set_value("baz"); // Last duplicate value for type 0x96 -+ auto unique_tlv_entry = config.add_added_tlvs(); -+ unique_tlv_entry->set_type(0x97); -+ unique_tlv_entry->set_value("baz"); -+ initialize(config, socket_options); -+ -+ EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) -+ .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { -+ auto length = buffer.length(); -+ buffer.drain(length); -+ return {length, Api::IoError::none()}; -+ })); -+ auto msg = Buffer::OwnedImpl("some data"); -+ EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); -+ -+ auto resp = proxy_protocol_socket_->doWrite(msg, false); -+ EXPECT_EQ(resp.bytes_processed_, expected_buff.length()); -+} -+ -+// Test handles edge case where the well-known host metadata namespace is present, but the -+// TLVs are invalid and cannot be unpacked properly. Needed for code coverage. -+TEST_F(ProxyProtocolTest, V2CustomTLVMetadataInvalidFormat) { -+ auto src_addr = -+ Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv6Instance("1:2:3::4", 8)); -+ auto dst_addr = Network::Address::InstanceConstSharedPtr( -+ new Network::Address::Ipv6Instance("1:100:200:3::", 2)); -+ Network::ProxyProtocolTLVVector tlv_vector{Network::ProxyProtocolTLV{0x5, {'a', 'b', 'c'}}}; -+ Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, tlv_vector}; -+ Network::TransportSocketOptionsConstSharedPtr socket_options = -+ std::make_shared( -+ "", std::vector{}, std::vector{}, std::vector{}, -+ absl::optional(proxy_proto_data)); -+ transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ -+ ->setLocalAddress(*Network::Utility::resolveUrl("tcp://[1:100:200:3::]:50000")); -+ transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ -+ ->setRemoteAddress(*Network::Utility::resolveUrl("tcp://[e:b:c:f::]:8080")); -+ -+ auto host = std::make_shared>(); -+ auto metadata = std::make_shared(); -+ const std::string metadata_key = -+ Config::MetadataFilters::get().ENVOY_TRANSPORT_SOCKETS_PROXY_PROTOCOL; -+ -+ // bogus typed metadata in the well-known host metadata field. -+ envoy::config::core::v3::Address addr_proto; -+ addr_proto.mutable_socket_address()->set_address("0.0.0.0"); -+ addr_proto.mutable_socket_address()->set_port_value(1234); -+ ProtobufWkt::Any typed_metadata; -+ typed_metadata.PackFrom(addr_proto); -+ metadata->mutable_typed_filter_metadata()->emplace(std::make_pair(metadata_key, typed_metadata)); -+ EXPECT_CALL(*host, metadata()).Times(testing::AnyNumber()).WillRepeatedly(Return(metadata)); -+ transport_callbacks_.connection_.streamInfo().upstreamInfo()->setUpstreamHost(host); -+ -+ absl::flat_hash_set pass_through_tlvs{0x5}; -+ Buffer::OwnedImpl expected_buff{}; -+ EXPECT_TRUE(Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, true, -+ pass_through_tlvs, {})); -+ -+ ProxyProtocolConfig config; -+ config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); -+ config.mutable_pass_through_tlvs()->set_match_type(ProxyProtocolPassThroughTLVs::INCLUDE_ALL); -+ initialize(config, socket_options); -+ -+ EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) -+ .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { -+ auto length = buffer.length(); -+ buffer.drain(length); -+ return {length, Api::IoError::none()}; -+ })); -+ auto msg = Buffer::OwnedImpl("some data"); -+ EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); -+ -+ auto resp = proxy_protocol_socket_->doWrite(msg, false); -+ EXPECT_EQ(resp.bytes_processed_, expected_buff.length()); -+} -+ -+// Test verifies edge case where host has metadata available, but does not include the expected key. -+// Needed for code coverage. -+TEST_F(ProxyProtocolTest, V2CustomTLVHostMetadataMissing) { -+ auto src_addr = -+ Network::Address::InstanceConstSharedPtr(new Network::Address::Ipv6Instance("1:2:3::4", 8)); -+ auto dst_addr = Network::Address::InstanceConstSharedPtr( -+ new Network::Address::Ipv6Instance("1:100:200:3::", 2)); -+ Network::ProxyProtocolTLVVector tlv_vector{Network::ProxyProtocolTLV{0x5, {'a', 'b', 'c'}}}; -+ Network::ProxyProtocolData proxy_proto_data{src_addr, dst_addr, tlv_vector}; -+ Network::TransportSocketOptionsConstSharedPtr socket_options = -+ std::make_shared( -+ "", std::vector{}, std::vector{}, std::vector{}, -+ absl::optional(proxy_proto_data)); -+ transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ -+ ->setLocalAddress(*Network::Utility::resolveUrl("tcp://[1:100:200:3::]:50000")); -+ transport_callbacks_.connection_.stream_info_.downstream_connection_info_provider_ -+ ->setRemoteAddress(*Network::Utility::resolveUrl("tcp://[e:b:c:f::]:8080")); -+ -+ // Intentionally add host metadata with a different key, and not the expected key. -+ envoy::config::core::v3::Metadata socket_match_metadata; -+ TestUtility::loadFromYaml(R"EOF( -+filter_metadata: -+ envoy.transport_socket_match: -+ outbound-proxy-protocol: true -+)EOF", -+ socket_match_metadata); -+ ProtobufWkt::Any typed_metadata; -+ typed_metadata.PackFrom(socket_match_metadata); -+ -+ auto host = std::make_shared>(); -+ auto metadata = std::make_shared(); -+ metadata->mutable_typed_filter_metadata()->emplace("envoy.transport_socket_match", -+ typed_metadata); -+ EXPECT_CALL(*host, metadata()).Times(testing::AnyNumber()).WillRepeatedly(Return(metadata)); -+ transport_callbacks_.connection().streamInfo().upstreamInfo()->setUpstreamHost(host); -+ -+ absl::flat_hash_set pass_through_tlvs{0x5}; -+ Buffer::OwnedImpl expected_buff{}; -+ EXPECT_TRUE(Common::ProxyProtocol::generateV2Header(proxy_proto_data, expected_buff, true, -+ pass_through_tlvs, {})); -+ -+ ProxyProtocolConfig config; -+ config.set_version(ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V2); -+ config.mutable_pass_through_tlvs()->set_match_type(ProxyProtocolPassThroughTLVs::INCLUDE_ALL); -+ initialize(config, socket_options); -+ -+ EXPECT_CALL(io_handle_, write(BufferStringEqual(expected_buff.toString()))) -+ .WillOnce(Invoke([&](Buffer::Instance& buffer) -> Api::IoCallUint64Result { -+ auto length = buffer.length(); -+ buffer.drain(length); -+ return {length, Api::IoError::none()}; -+ })); -+ -+ auto msg = Buffer::OwnedImpl("some data"); -+ EXPECT_CALL(*inner_socket_, doWrite(BufferEqual(&msg), false)); -+ auto resp = proxy_protocol_socket_->doWrite(msg, false); -+ EXPECT_EQ(resp.bytes_processed_, expected_buff.length()); -+} -+ - class ProxyProtocolSocketFactoryTest : public testing::Test { - public: - void initialize() { -diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc -index e43bdf03f5..6764d30d98 100644 ---- test/integration/fake_upstream.cc -+++ test/integration/fake_upstream.cc -@@ -1054,7 +1054,8 @@ AssertionResult FakeRawConnection::waitForData(uint64_t num_bytes, std::string* - ENVOY_LOG(debug, "waiting for {} bytes of data", num_bytes); - if (!time_system_.waitFor(lock_, absl::Condition(&reached), timeout)) { - return AssertionFailure() << fmt::format( -- "Timed out waiting for data. Got '{}', waiting for {} bytes.", data_, num_bytes); -+ "Timed out waiting for data. Got '{}', expected {} bytes, waiting for {} bytes.", -+ data_, data_.size(), num_bytes); - } - if (data != nullptr) { - *data = data_; diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl index 186229d85..2d0fad151 100644 --- a/bazel/repositories.bzl +++ b/bazel/repositories.bzl @@ -67,7 +67,6 @@ def _repository_impl(name, **kwargs): def envoy_gloo_dependencies(): _repository_impl("envoy", patches=[ - "@envoy_gloo//bazel/foreign_cc:001-tlv-support.patch", #remove in 1.34 ]) _repository_impl("json", build_file = "@envoy_gloo//bazel/external:json.BUILD") _repository_impl("inja", build_file = "@envoy_gloo//bazel/external:inja.BUILD") diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index 26b37ae31..3b17afe79 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -2,8 +2,8 @@ REPOSITORY_LOCATIONS = dict( # can't have more than one comment between envoy line and commit line in # order to accommodate `check_extensions_build_config.sh` envoy = dict( - # envoy v1.33.0 - commit = "b0f43d67aa25c1b03c97186a200cc187f4c22db3", + # envoy v1.34.0-dev + commit = "87b4ae8dde4a0cd35bc8c3584ec79d3625522c4d", remote = "https://github.com/envoyproxy/envoy", ), inja = dict( diff --git a/changelog/v1.34.0-patch0/bumpenvoy.yaml b/changelog/v1.34.0-patch0/bumpenvoy.yaml new file mode 100644 index 000000000..a83f6e4b4 --- /dev/null +++ b/changelog/v1.34.0-patch0/bumpenvoy.yaml @@ -0,0 +1,9 @@ +changelog: + - type: DEPENDENCY_BUMP + issueLink: https://github.com/solo-io/envoy-gloo-ee/issues/887 + dependencyOwner: envoyproxy + dependencyRepo: envoy + dependencyTag: 1.34.0 + resolvesIssue: false + description: >- + Bumps to a prerelease of envoy 1.34.0 to allow for usage of dynamic modules \ No newline at end of file diff --git a/go/config/filter/http/aws_lambda/v2/aws_lambda.pb.go b/go/config/filter/http/aws_lambda/v2/aws_lambda.pb.go index 2aa82b284..2f176bdd1 100755 --- a/go/config/filter/http/aws_lambda/v2/aws_lambda.pb.go +++ b/go/config/filter/http/aws_lambda/v2/aws_lambda.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.30.0 -// protoc v5.29.2 +// protoc v5.29.3 // source: api/envoy/config/filter/http/aws_lambda/v2/aws_lambda.proto package v2 diff --git a/go/config/filter/http/nats/streaming/v2/nats_streaming.pb.go b/go/config/filter/http/nats/streaming/v2/nats_streaming.pb.go index c34b83947..4b595b969 100755 --- a/go/config/filter/http/nats/streaming/v2/nats_streaming.pb.go +++ b/go/config/filter/http/nats/streaming/v2/nats_streaming.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.30.0 -// protoc v5.29.2 +// protoc v5.29.3 // source: api/envoy/config/filter/http/nats/streaming/v2/nats_streaming.proto package v2 diff --git a/go/config/filter/http/nats/streaming/v2/payload.pb.go b/go/config/filter/http/nats/streaming/v2/payload.pb.go index 31bbba632..4a4d0e866 100755 --- a/go/config/filter/http/nats/streaming/v2/payload.pb.go +++ b/go/config/filter/http/nats/streaming/v2/payload.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.30.0 -// protoc v5.29.2 +// protoc v5.29.3 // source: api/envoy/config/filter/http/nats/streaming/v2/payload.proto package v2 diff --git a/go/config/filter/http/transformation/v2/transformation_filter.pb.go b/go/config/filter/http/transformation/v2/transformation_filter.pb.go index ceee34479..5e165342a 100755 --- a/go/config/filter/http/transformation/v2/transformation_filter.pb.go +++ b/go/config/filter/http/transformation/v2/transformation_filter.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.30.0 -// protoc v5.29.2 +// protoc v5.29.3 // source: api/envoy/config/filter/http/transformation/v2/transformation_filter.proto package v2 diff --git a/go/config/filter/http/upstream_wait/v2/upstream_wait_filter.pb.go b/go/config/filter/http/upstream_wait/v2/upstream_wait_filter.pb.go index 5b02cb1bf..3dd8eabe9 100755 --- a/go/config/filter/http/upstream_wait/v2/upstream_wait_filter.pb.go +++ b/go/config/filter/http/upstream_wait/v2/upstream_wait_filter.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.30.0 -// protoc v5.29.2 +// protoc v5.29.3 // source: api/envoy/config/filter/http/upstream_wait/v2/upstream_wait_filter.proto package v2 diff --git a/go/config/transformer/aws_lambda/v2/api_gateway_transformer.pb.go b/go/config/transformer/aws_lambda/v2/api_gateway_transformer.pb.go index fead9919d..b0c6a27cb 100755 --- a/go/config/transformer/aws_lambda/v2/api_gateway_transformer.pb.go +++ b/go/config/transformer/aws_lambda/v2/api_gateway_transformer.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.30.0 -// protoc v5.29.2 +// protoc v5.29.3 // source: api/envoy/config/transformer/aws_lambda/v2/api_gateway_transformer.proto package transformer diff --git a/go/type/streaming/protocol.pb.go b/go/type/streaming/protocol.pb.go index 98ff82480..c616be22f 100755 --- a/go/type/streaming/protocol.pb.go +++ b/go/type/streaming/protocol.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.30.0 -// protoc v5.29.2 +// protoc v5.29.3 // source: api/envoy/type/streaming/protocol.proto package streaming diff --git a/source/common/matcher/solo_matcher.cc b/source/common/matcher/solo_matcher.cc index 156b788ef..5d738f245 100644 --- a/source/common/matcher/solo_matcher.cc +++ b/source/common/matcher/solo_matcher.cc @@ -120,6 +120,9 @@ class CompiledStdMatcher : public Regex::CompiledMatcher { public: CompiledStdMatcher(std::regex &®ex) : regex_(std::move(regex)) {} + // TODO(nfuden) refactor matchers or find a way to get back onto upstream matchers + const std::string& pattern() const override { return EMPTY_STRING; } + // CompiledMatcher bool match(absl::string_view value) const override { try { diff --git a/source/common/matcher/solo_matcher.h b/source/common/matcher/solo_matcher.h index 23890a464..ee371cb81 100644 --- a/source/common/matcher/solo_matcher.h +++ b/source/common/matcher/solo_matcher.h @@ -37,7 +37,10 @@ class Matcher { static MatcherConstPtr create(const ::envoy::config::route::v3::RouteMatch &match, Server::Configuration::CommonFactoryContext& context); + }; + + } // namespace Matcher } // namespace Envoy diff --git a/source/common/tcp/conn_pool_impl.h b/source/common/tcp/conn_pool_impl.h index dc56bd7f1..e06c45eca 100644 --- a/source/common/tcp/conn_pool_impl.h +++ b/source/common/tcp/conn_pool_impl.h @@ -283,7 +283,7 @@ template class InstanceImpl : public Instance { } LbContextImpl lb_context(hash_key); Upstream::HostConstSharedPtr host = - cluster->loadBalancer().chooseHost(&lb_context); + Upstream::LoadBalancer::onlyAllowSynchronousHostSelection(cluster->loadBalancer().chooseHost(&lb_context)); if (!host) { // TODO(talnordan): // parent_.callbacks_->onFailure("no host"); diff --git a/source/extensions/filters/http/aws_lambda/aws_lambda_filter_config_factory.cc b/source/extensions/filters/http/aws_lambda/aws_lambda_filter_config_factory.cc index d3cfe0d2c..bd3aa7a87 100644 --- a/source/extensions/filters/http/aws_lambda/aws_lambda_filter_config_factory.cc +++ b/source/extensions/filters/http/aws_lambda/aws_lambda_filter_config_factory.cc @@ -24,7 +24,6 @@ AWSLambdaFilterConfigFactory::createFilterFactoryFromProtoTyped( // the upstream code from attempting to access the method. https://github.com/envoyproxy/envoy/issues/26653 auto chain = std::make_unique( server_context.api(), absl::nullopt /* ServerFactoryContextOptRef context */, - server_context.singletonManager(), // We pass an empty string if we don't have a region proto_config.has_service_account_credentials() ? proto_config.service_account_credentials().region() : "", nullptr); diff --git a/source/extensions/filters/http/transformation/transformation_filter_config.cc b/source/extensions/filters/http/transformation/transformation_filter_config.cc index 73ce414f4..a12e32ff8 100644 --- a/source/extensions/filters/http/transformation/transformation_filter_config.cc +++ b/source/extensions/filters/http/transformation/transformation_filter_config.cc @@ -64,7 +64,7 @@ class ResponseMatcherImpl : public ResponseMatcher { private: std::vector headers_; - absl::optional> response_code_details_match_; + absl::optional response_code_details_match_; }; bool ResponseMatcherImpl::matches( diff --git a/test/common/tcp/conn_pool_impl_test.cc b/test/common/tcp/conn_pool_impl_test.cc index a1eb4a44d..aa0f45d14 100644 --- a/test/common/tcp/conn_pool_impl_test.cc +++ b/test/common/tcp/conn_pool_impl_test.cc @@ -410,7 +410,9 @@ TEST_F(TcpConnPoolImplTest, NoHost) { T value; EXPECT_CALL(cm_.thread_local_cluster_.lb_, chooseHost(_)) - .WillOnce(Return(nullptr)); + .WillOnce(Invoke([] { + return Upstream::HostSelectionResponse{nullptr}; + })); conn_pool_->makeRequest("foo", value); conn_pool_ = {};