@@ -1646,6 +1646,75 @@ TEST_P(ProxyProtocolTest, V2ExtractMultipleTlvsOfInterestAndSanitiseNonUtf8) {
16461646 EXPECT_EQ (stats_store_.counter (" proxy_proto.versions.v2.found" ).value (), 1 );
16471647}
16481648
1649+ TEST_P (ProxyProtocolTest, V2ExtractMultipleTlvsOfInterestAndEncodeAsBase64) {
1650+ // A well-formed ipv4/tcp with a pair of TLV extensions is accepted.
1651+ constexpr uint8_t buffer[] = {0x0d , 0x0a , 0x0d , 0x0a , 0x00 , 0x0d , 0x0a , 0x51 , 0x55 , 0x49 ,
1652+ 0x54 , 0x0a , 0x21 , 0x11 , 0x00 , 0x39 , 0x01 , 0x02 , 0x03 , 0x04 ,
1653+ 0x00 , 0x01 , 0x01 , 0x02 , 0x03 , 0x05 , 0x00 , 0x02 };
1654+ // A TLV of type 0x00 with size of 4 (1 byte is value).
1655+ constexpr uint8_t tlv1[] = {0x00 , 0x00 , 0x01 , 0xff };
1656+ // A TLV of type 0x02 with size of 10 bytes (7 bytes are value). Second and last bytes in the
1657+ // value are non utf8 characters.
1658+ constexpr uint8_t tlv_type_authority[] = {0x02 , 0x00 , 0x07 , 0x66 , 0xfe ,
1659+ 0x6f , 0x2e , 0x63 , 0x6f , 0xc1 };
1660+ // A TLV of type 0x0f with size of 6 bytes (3 bytes are value).
1661+ constexpr uint8_t tlv3[] = {0x0f , 0x00 , 0x03 , 0xf0 , 0x00 , 0x0f };
1662+ // A TLV of type 0xea with size of 25 bytes (22 bytes are value). 7th and 21st bytes are non utf8
1663+ // characters.
1664+ constexpr uint8_t tlv_vpc_id[] = {0xea , 0x00 , 0x16 , 0x01 , 0x76 , 0x70 , 0x63 , 0x2d , 0x30 ,
1665+ 0xc0 , 0x35 , 0x74 , 0x65 , 0x73 , 0x74 , 0x32 , 0x66 , 0x61 ,
1666+ 0x36 , 0x63 , 0x36 , 0x33 , 0x68 , 0xf9 , 0x37 };
1667+ constexpr uint8_t data[] = {' D' , ' A' , ' T' , ' A' };
1668+
1669+ envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol proto_config;
1670+ auto rule_type_authority = proto_config.add_rules ();
1671+ rule_type_authority->set_tlv_type (0x02 );
1672+ rule_type_authority->mutable_on_tlv_present ()->set_key (" PP2 type authority" );
1673+ rule_type_authority->mutable_on_tlv_present ()->set_value_string_encoding (
1674+ envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol::KeyValuePair::
1675+ BASE64 );
1676+
1677+ auto rule_vpc_id = proto_config.add_rules ();
1678+ rule_vpc_id->set_tlv_type (0xea );
1679+ rule_vpc_id->mutable_on_tlv_present ()->set_key (" PP2 vpc id" );
1680+ rule_vpc_id->mutable_on_tlv_present ()->set_value_string_encoding (
1681+ envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol::KeyValuePair::
1682+ BASE64 );
1683+
1684+ // A rule with default (SANITIZED_UTF8) encoding for comparison: the value will be sanitized
1685+ // to a valid UTF-8 string.
1686+ auto rule_tlv1 = proto_config.add_rules ();
1687+ rule_tlv1->set_tlv_type (0x00 );
1688+ rule_tlv1->mutable_on_tlv_present ()->set_key (" PP2 tlv1" );
1689+
1690+ connect (true , &proto_config);
1691+ write (buffer, sizeof (buffer));
1692+ dispatcher_->run (Event::Dispatcher::RunType::NonBlock);
1693+
1694+ write (tlv1, sizeof (tlv1));
1695+ write (tlv_type_authority, sizeof (tlv_type_authority));
1696+ write (tlv3, sizeof (tlv3));
1697+ write (tlv_vpc_id, sizeof (tlv_vpc_id));
1698+ write (data, sizeof (data));
1699+ expectData (" DATA" );
1700+
1701+ auto metadata = server_connection_->streamInfo ().dynamicMetadata ().filter_metadata ();
1702+ EXPECT_EQ (1 , metadata.size ());
1703+ EXPECT_EQ (1 , metadata.count (ProxyProtocol));
1704+
1705+ auto fields = metadata.at (ProxyProtocol).fields ();
1706+ EXPECT_EQ (3 , fields.size ());
1707+
1708+ // The raw TLV values (including the non utf8 characters) are encoded as base64.
1709+ EXPECT_EQ (" Zv5vLmNvwQ==" , fields.at (" PP2 type authority" ).string_value ());
1710+ EXPECT_EQ (" AXZwYy0wwDV0ZXN0MmZhNmM2M2j5Nw==" , fields.at (" PP2 vpc id" ).string_value ());
1711+ // The default encoding sanitizes the value to a valid UTF-8 string: the non utf8 byte
1712+ // 0xff is replaced with the `!` character.
1713+ EXPECT_EQ (" !" , fields.at (" PP2 tlv1" ).string_value ());
1714+ disconnect ();
1715+ EXPECT_EQ (stats_store_.counter (" proxy_proto.versions.v2.found" ).value (), 1 );
1716+ }
1717+
16491718TEST_P (ProxyProtocolTest, V2ExtractMultipleTlvsOfInterestAndEmitTypedAndUntypedMetadata) {
16501719 // A well-formed ipv4/tcp with a pair of TLV extensions is accepted
16511720 constexpr uint8_t buffer[] = {0x0d , 0x0a , 0x0d , 0x0a , 0x00 , 0x0d , 0x0a , 0x51 , 0x55 , 0x49 ,
@@ -2097,6 +2166,77 @@ TEST_P(ProxyProtocolTest, V2ExtractTLVToFilterStateAsStringAccessor) {
20972166 EXPECT_EQ (stats_store_.counter (" proxy_proto.versions.v2.found" ).value (), 1 );
20982167}
20992168
2169+ TEST_P (ProxyProtocolTest, V2ExtractTLVToFilterStateWithBase64Encoding) {
2170+ constexpr uint8_t buffer[] = {0x0d , 0x0a , 0x0d , 0x0a , 0x00 , 0x0d , 0x0a , 0x51 , 0x55 , 0x49 ,
2171+ 0x54 , 0x0a , 0x21 , 0x11 , 0x00 , 0x27 , 0x01 , 0x02 , 0x03 , 0x04 ,
2172+ 0x00 , 0x01 , 0x01 , 0x02 , 0x03 , 0x05 , 0x00 , 0x02 };
2173+ constexpr uint8_t tlv1[] = {0x0 , 0x0 , 0x1 , 0xff };
2174+ constexpr uint8_t tlv_type_authority[] = {0x02 , 0x00 , 0x07 , 0x66 , 0x6f ,
2175+ 0x6f , 0x2e , 0x63 , 0x6f , 0x6d };
2176+ constexpr uint8_t tlv_vpce[] = {0xea , 0x00 , 0x0a , 0x21 , 0x76 , 0x70 , 0x63 ,
2177+ 0x65 , 0x2d , 0x30 , 0x78 , 0x78 , 0x78 };
2178+ constexpr uint8_t data[] = {' D' , ' A' , ' T' , ' A' };
2179+
2180+ envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol proto_config;
2181+ proto_config.set_tlv_location (
2182+ envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol::FILTER_STATE );
2183+ auto rule1 = proto_config.add_rules ();
2184+ rule1->set_tlv_type (0x02 );
2185+ rule1->mutable_on_tlv_present ()->set_key (" PP2 type authority" );
2186+ rule1->mutable_on_tlv_present ()->set_value_string_encoding (
2187+ envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol::KeyValuePair::
2188+ BASE64 );
2189+ auto rule2 = proto_config.add_rules ();
2190+ rule2->set_tlv_type (0xea );
2191+ rule2->mutable_on_tlv_present ()->set_key (" aws_vpce_id" );
2192+ rule2->mutable_on_tlv_present ()->set_value_string_encoding (
2193+ envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol::KeyValuePair::
2194+ BASE64 );
2195+ // A rule with default (SANITIZED_UTF8) encoding for comparison: the value will be sanitized
2196+ // to a valid UTF-8 string.
2197+ auto rule3 = proto_config.add_rules ();
2198+ rule3->set_tlv_type (0x00 );
2199+ rule3->mutable_on_tlv_present ()->set_key (" PP2 tlv1" );
2200+ rule3->mutable_on_tlv_present ()->set_value_string_encoding (
2201+ envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol::KeyValuePair::
2202+ SANITIZED_UTF8 );
2203+
2204+ connect (true , &proto_config);
2205+ write (buffer, sizeof (buffer));
2206+ dispatcher_->run (Event::Dispatcher::RunType::NonBlock);
2207+
2208+ write (tlv1, sizeof (tlv1));
2209+ write (tlv_type_authority, sizeof (tlv_type_authority));
2210+ write (tlv_vpce, sizeof (tlv_vpce));
2211+ write (data, sizeof (data));
2212+ expectData (" DATA" );
2213+
2214+ auto & filter_state = server_connection_->streamInfo ().filterState ();
2215+
2216+ constexpr absl::string_view kFilterStateKey = " envoy.network.proxy_protocol.tlv" ;
2217+ EXPECT_TRUE (filter_state->hasDataWithName (kFilterStateKey ));
2218+ const auto * tlv_obj = filter_state->getDataReadOnlyGeneric (kFilterStateKey );
2219+ ASSERT_NE (nullptr , tlv_obj);
2220+
2221+ // The raw TLV values are encoded as base64.
2222+ auto field1 = tlv_obj->getField (" PP2 type authority" );
2223+ ASSERT_TRUE (absl::holds_alternative<absl::string_view>(field1));
2224+ EXPECT_EQ (" Zm9vLmNvbQ==" , absl::get<absl::string_view>(field1));
2225+
2226+ auto field2 = tlv_obj->getField (" aws_vpce_id" );
2227+ ASSERT_TRUE (absl::holds_alternative<absl::string_view>(field2));
2228+ EXPECT_EQ (" IXZwY2UtMHh4eA==" , absl::get<absl::string_view>(field2));
2229+
2230+ // The default encoding sanitizes the value to a valid UTF-8 string: the non utf8 byte
2231+ // 0xff is replaced with the `!` character.
2232+ auto field3 = tlv_obj->getField (" PP2 tlv1" );
2233+ ASSERT_TRUE (absl::holds_alternative<absl::string_view>(field3));
2234+ EXPECT_EQ (" !" , absl::get<absl::string_view>(field3));
2235+
2236+ disconnect ();
2237+ EXPECT_EQ (stats_store_.counter (" proxy_proto.versions.v2.found" ).value (), 1 );
2238+ }
2239+
21002240TEST_P (ProxyProtocolTest, V2ExtractTLVToFilterStateDefaultBehavior) {
21012241 // Test that default behavior (DYNAMIC_METADATA) still works when tlv_location is not set
21022242 constexpr uint8_t buffer[] = {0x0d , 0x0a , 0x0d , 0x0a , 0x00 , 0x0d , 0x0a , 0x51 , 0x55 , 0x49 ,
0 commit comments