From 7267622611eb0b7ba042091b5108640862f91399 Mon Sep 17 00:00:00 2001 From: immanuwell Date: Wed, 1 Jul 2026 22:43:40 +0400 Subject: [PATCH] fix: handle missing generic L7 metadata entries Signed-off-by: immanuwell --- cilium/network_filter.cc | 6 ++- cilium/network_filter.h | 4 ++ tests/BUILD | 11 ++++ tests/network_filter_test.cc | 98 ++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 tests/network_filter_test.cc diff --git a/cilium/network_filter.cc b/cilium/network_filter.cc index bcf27fa23..201aa65e9 100644 --- a/cilium/network_filter.cc +++ b/cilium/network_filter.cc @@ -312,7 +312,11 @@ Network::FilterStatus Instance::onData(Buffer::Instance& data, bool end_stream) go_parser_->setOrigEndStream(end_stream); } else if (!l7proto_.empty()) { const auto& metadata = conn.streamInfo().dynamicMetadata(); - bool changed = log_entry_.updateFromMetadata(l7proto_, metadata.filter_metadata().at(l7proto_)); + const auto& filter_metadata = metadata.filter_metadata(); + const auto metadata_it = filter_metadata.find(l7proto_); + const Protobuf::Struct empty_metadata; + bool changed = log_entry_.updateFromMetadata( + l7proto_, metadata_it != filter_metadata.end() ? metadata_it->second : empty_metadata); // Policy may have changed since the connection was established, get fresh policy const auto policy_fs = diff --git a/cilium/network_filter.h b/cilium/network_filter.h index 04e33d44f..6a8009148 100644 --- a/cilium/network_filter.h +++ b/cilium/network_filter.h @@ -27,6 +27,8 @@ namespace Envoy { namespace Filter { namespace CiliumL3 { +class NetworkFilterTestPeer; + /** * Shared configuration for Cilium network filter worker * Instances. Each new network connection (on each worker thread) @@ -67,6 +69,8 @@ class Instance : public Network::Filter, Logger::Loggable { Network::FilterStatus onWrite(Buffer::Instance&, bool end_stream) override; private: + friend class NetworkFilterTestPeer; + // helper to be used either directly from onNewConnection (no L7 LB), // or from upstream callback (l7 lb) bool enforceNetworkPolicy(const Cilium::CiliumPolicyFilterState* policy_fs, diff --git a/tests/BUILD b/tests/BUILD index 9ae80acf1..be476c796 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -157,6 +157,17 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "network_filter_test", + srcs = ["network_filter_test.cc"], + repository = "@envoy", + deps = [ + "//cilium:network_filter_lib", + "@envoy//test/mocks/network:network_mocks", + "@envoy//test/mocks/server:listener_factory_context_mocks", + ], +) + envoy_cc_test( name = "cilium_tcp_integration_test", srcs = ["cilium_tcp_integration_test.cc"], diff --git a/tests/network_filter_test.cc b/tests/network_filter_test.cc new file mode 100644 index 000000000..b1d2aca8c --- /dev/null +++ b/tests/network_filter_test.cc @@ -0,0 +1,98 @@ +#include +#include +#include + +#include +#include + +#include "cilium/api/network_filter.pb.h" +#include "cilium/filter_state_cilium_policy.h" +#include "cilium/network_filter.h" +#include "cilium/network_policy.h" + +#include "envoy/buffer/buffer.h" +#include "envoy/network/address.h" +#include "envoy/network/connection.h" +#include "envoy/network/filter.h" +#include "envoy/stream_info/filter_state.h" +#include "envoy/stream_info/stream_info.h" +#include "envoy/upstream/host_description.h" + +#include "source/common/buffer/buffer_impl.h" + +#include "test/mocks/network/mocks.h" +#include "test/mocks/server/listener_factory_context.h" + +namespace Envoy { +namespace Filter { +namespace CiliumL3 { + +class NetworkFilterTestPeer { +public: + static void setL7Proto(Instance& instance, std::string l7proto) { + instance.l7proto_ = std::move(l7proto); + } + + static const Cilium::AccessLog::Entry& logEntry(const Instance& instance) { + return instance.log_entry_; + } +}; + +} // namespace CiliumL3 +} // namespace Filter + +namespace { + +using testing::NiceMock; + +class TestReadFilterCallbacks : public Network::MockReadFilterCallbacks { +public: + TestReadFilterCallbacks() { + ON_CALL(*this, addUpstreamCallback(testing::_)) + .WillByDefault(testing::Invoke([](const UpstreamCallback&) {})); + ON_CALL(*this, iterateUpstreamCallbacks(testing::_, testing::_)) + .WillByDefault(testing::Return(true)); + } + + MOCK_METHOD(void, addUpstreamCallback, (const UpstreamCallback& cb), (override)); + MOCK_METHOD(bool, iterateUpstreamCallbacks, + (Upstream::HostDescriptionConstSharedPtr, StreamInfo::StreamInfo&), (override)); +}; + +class AllowAllPolicyResolver : public Cilium::PolicyResolver { +public: + uint32_t resolvePolicyId(const Network::Address::Ip*) const override { return 123; } + + const Cilium::PolicyInstance& getPolicy(const std::string&) const override { + return Cilium::NetworkPolicyMap::getAllowAllEgressPolicy(); + } + + bool exists(const std::string&) const override { return true; } +}; + +TEST(CiliumNetworkFilterTest, MissingMetadataNamespaceDoesNotCrash) { + NiceMock context; + ::cilium::NetworkFilter proto_config; + auto config = std::make_shared(proto_config, context); + Filter::CiliumL3::Instance instance(config); + + NiceMock callbacks; + callbacks.connection_.stream_info_.filter_state_->setData( + Cilium::CiliumPolicyFilterState::key(), + std::make_shared( + 0, 456, false, false, 80, std::string("pod"), std::string(""), + std::make_shared(), 7, ""), + StreamInfo::FilterState::StateType::ReadOnly, + StreamInfo::FilterState::LifeSpan::Connection); + instance.initializeReadFilterCallbacks(callbacks); + Filter::CiliumL3::NetworkFilterTestPeer::setL7Proto(instance, "test.l7"); + + Buffer::OwnedImpl data("hello"); + EXPECT_EQ(instance.onData(data, false), Network::FilterStatus::Continue); + EXPECT_EQ(callbacks.connection_.state_, Network::Connection::State::Open); + EXPECT_EQ(Filter::CiliumL3::NetworkFilterTestPeer::logEntry(instance).entry_.generic_l7().proto(), + "test.l7"); +} + +} // namespace +} // namespace Envoy