Skip to content

Commit

Permalink
ECDS: Add Listener filter ECDS Support (envoyproxy#21133)
Browse files Browse the repository at this point in the history
Signed-off-by: Yanjun Xiang <yanjunxiang@google.com>
Signed-off-by: Tianyu Xia <tyxia@google.com>
  • Loading branch information
yanjunxiang-google authored and tyxia committed Jun 14, 2022
1 parent f71cb3b commit af4f5da
Show file tree
Hide file tree
Showing 27 changed files with 813 additions and 83 deletions.
1 change: 1 addition & 0 deletions docs/root/configuration/listeners/stats.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ with the following statistics:
downstream_global_cx_overflow, Counter, Total connections rejected due to enforcement of global connection limit
downstream_pre_cx_timeout, Counter, Sockets that timed out during listener filter processing
downstream_pre_cx_active, Gauge, Sockets currently undergoing listener filter processing
extension_config_missing, Counter, Total connections closed due to missing listener filter extension configuration
global_cx_overflow, Counter, Total connections rejected due to enforcement of the global connection limit
no_filter_chain_match, Counter, Total connections that didn't match any filter chain
downstream_listener_filter_remote_close, Counter, Total connections closed by remote when peek data for listener filters
Expand Down
5 changes: 4 additions & 1 deletion envoy/config/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ envoy_cc_library(
envoy_cc_library(
name = "extension_config_provider_interface",
hdrs = ["extension_config_provider.h"],
deps = ["//source/common/protobuf"],
deps = [
"//envoy/network:filter_interface",
"//source/common/protobuf",
],
)

envoy_cc_library(
Expand Down
5 changes: 5 additions & 0 deletions envoy/config/extension_config_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "envoy/common/optref.h"
#include "envoy/common/pure.h"
#include "envoy/network/filter.h"

#include "source/common/protobuf/protobuf.h"

Expand Down Expand Up @@ -62,6 +63,10 @@ class DynamicExtensionConfigProviderBase {
* Applies the default configuration if one is set, otherwise does nothing.
*/
virtual void applyDefaultConfiguration() PURE;
/**
* Return Network::ListenerFilterMatcherSharedPtr& the listener filter matcher.
*/
virtual const Network::ListenerFilterMatcherSharedPtr& getListenerFilterMatcher() PURE;
};

template <class FactoryCallback>
Expand Down
9 changes: 8 additions & 1 deletion envoy/filter/config_provider_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ using DynamicFilterConfigProvider = Envoy::Config::DynamicExtensionConfigProvide
template <class FactoryCb>
using DynamicFilterConfigProviderPtr = std::unique_ptr<DynamicFilterConfigProvider<FactoryCb>>;

// Listener filter config provider aliases
using ListenerFilterFactoriesList =
std::vector<FilterConfigProviderPtr<Network::ListenerFilterFactoryCb>>;

/**
* The FilterConfigProviderManager exposes the ability to get an FilterConfigProvider
* for both static and dynamic filter config providers.
Expand All @@ -37,12 +41,15 @@ template <class FactoryCb, class FactoryCtx> class FilterConfigProviderManager {
* @param last_filter_in_filter_chain indicates whether this filter is the last filter in the
* configured chain
* @param filter_chain_type is the filter chain type
* @param listener_filter_matcher is the filter matcher for TCP listener filter. nullptr for other
* filter types.
*/
virtual DynamicFilterConfigProviderPtr<FactoryCb> createDynamicFilterConfigProvider(
const envoy::config::core::v3::ExtensionConfigSource& config_source,
const std::string& filter_config_name, FactoryCtx& factory_context,
const std::string& stat_prefix, bool last_filter_in_filter_chain,
const std::string& filter_chain_type) PURE;
const std::string& filter_chain_type,
const Network::ListenerFilterMatcherSharedPtr& listener_filter_matcher) PURE;

/**
* Get an FilterConfigProviderPtr for a statically inlined filter config.
Expand Down
1 change: 1 addition & 0 deletions envoy/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ envoy_cc_library(
":drain_manager_interface",
":filter_config_interface",
":guarddog_interface",
"//envoy/filter:config_provider_manager_interface",
"//envoy/network:filter_interface",
"//envoy/network:listen_socket_interface",
"//envoy/network:socket_interface_interface",
Expand Down
16 changes: 14 additions & 2 deletions envoy/server/listener_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "envoy/config/core/v3/config_source.pb.h"
#include "envoy/config/listener/v3/listener.pb.h"
#include "envoy/config/listener/v3/listener_components.pb.h"
#include "envoy/filter/config_provider_manager.h"
#include "envoy/network/filter.h"
#include "envoy/network/listen_socket.h"
#include "envoy/network/listener.h"
Expand All @@ -18,6 +19,10 @@
#include "source/common/protobuf/protobuf.h"

namespace Envoy {
namespace Filter {
class TcpListenerFilterConfigProviderManagerImpl;
} // namespace Filter

namespace Server {

/**
Expand Down Expand Up @@ -88,9 +93,9 @@ class ListenerComponentFactory {
* Creates a list of listener filter factories.
* @param filters supplies the JSON configuration.
* @param context supplies the factory creation context.
* @return std::vector<Network::ListenerFilterFactoryCb> the list of filter factories.
* @return Filter::ListenerFilterFactoriesList the list of filter factories.
*/
virtual std::vector<Network::ListenerFilterFactoryCb> createListenerFilterFactoryList(
virtual Filter::ListenerFilterFactoriesList createListenerFilterFactoryList(
const Protobuf::RepeatedPtrField<envoy::config::listener::v3::ListenerFilter>& filters,
Configuration::ListenerFactoryContext& context) PURE;

Expand All @@ -115,6 +120,13 @@ class ListenerComponentFactory {
* @return uint64_t a listener tag usable for connection handler tracking.
*/
virtual uint64_t nextListenerTag() PURE;

/**
* @return Filter::TcpListenerFilterConfigProviderManagerImpl* the pointer of the TCP listener
* config provider manager.
*/
virtual Filter::TcpListenerFilterConfigProviderManagerImpl*
getTcpListenerConfigProviderManager() PURE;
};

/**
Expand Down
61 changes: 35 additions & 26 deletions source/common/filter/config_discovery_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,17 @@ template <class FactoryCb>
class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBase,
public DynamicFilterConfigProvider<FactoryCb> {
public:
DynamicFilterConfigProviderImpl(FilterConfigSubscriptionSharedPtr& subscription,
const absl::flat_hash_set<std::string>& require_type_urls,
Server::Configuration::FactoryContext& factory_context,
ProtobufTypes::MessagePtr&& default_config,
bool last_filter_in_filter_chain,
const std::string& filter_chain_type,
absl::string_view stat_prefix)
DynamicFilterConfigProviderImpl(
FilterConfigSubscriptionSharedPtr& subscription,
const absl::flat_hash_set<std::string>& require_type_urls,
Server::Configuration::FactoryContext& factory_context,
ProtobufTypes::MessagePtr&& default_config, bool last_filter_in_filter_chain,
const std::string& filter_chain_type, absl::string_view stat_prefix,
const Network::ListenerFilterMatcherSharedPtr& listener_filter_matcher)
: DynamicFilterConfigProviderImplBase(subscription, require_type_urls,
last_filter_in_filter_chain, filter_chain_type),
stat_prefix_(stat_prefix), main_config_(std::make_shared<MainConfig>()),
listener_filter_matcher_(listener_filter_matcher), stat_prefix_(stat_prefix),
main_config_(std::make_shared<MainConfig>()),
default_configuration_(std::move(default_config)), tls_(factory_context.threadLocal()) {
tls_.set([](Event::Dispatcher&) { return std::make_shared<ThreadLocalConfig>(); });
};
Expand Down Expand Up @@ -120,9 +121,13 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa
onConfigUpdate(*default_configuration_, "", nullptr);
}
}
const Network::ListenerFilterMatcherSharedPtr& getListenerFilterMatcher() override {
return listener_filter_matcher_;
}

protected:
const std::string& getStatPrefix() const { return stat_prefix_; }
const Network::ListenerFilterMatcherSharedPtr listener_filter_matcher_;

private:
virtual FactoryCb instantiateFilterFactory(const Protobuf::Message& message) const PURE;
Expand Down Expand Up @@ -163,16 +168,16 @@ class DynamicFilterConfigProviderImpl : public DynamicFilterConfigProviderImplBa
class HttpDynamicFilterConfigProviderImpl
: public DynamicFilterConfigProviderImpl<Http::FilterFactoryCb> {
public:
HttpDynamicFilterConfigProviderImpl(FilterConfigSubscriptionSharedPtr& subscription,
const absl::flat_hash_set<std::string>& require_type_urls,
Server::Configuration::FactoryContext& factory_context,
ProtobufTypes::MessagePtr&& default_config,
bool last_filter_in_filter_chain,
const std::string& filter_chain_type,
absl::string_view stat_prefix)
HttpDynamicFilterConfigProviderImpl(
FilterConfigSubscriptionSharedPtr& subscription,
const absl::flat_hash_set<std::string>& require_type_urls,
Server::Configuration::FactoryContext& factory_context,
ProtobufTypes::MessagePtr&& default_config, bool last_filter_in_filter_chain,
const std::string& filter_chain_type, absl::string_view stat_prefix,
const Network::ListenerFilterMatcherSharedPtr& listener_filter_matcher)
: DynamicFilterConfigProviderImpl(subscription, require_type_urls, factory_context,
std::move(default_config), last_filter_in_filter_chain,
filter_chain_type, stat_prefix),
filter_chain_type, stat_prefix, listener_filter_matcher),
factory_context_(factory_context) {}
void validateMessage(const std::string& config_name, const Protobuf::Message& message,
const std::string& factory_name) const override {
Expand Down Expand Up @@ -203,10 +208,11 @@ class ListenerDynamicFilterConfigProviderImpl : public DynamicFilterConfigProvid
const absl::flat_hash_set<std::string>& require_type_urls,
Server::Configuration::ListenerFactoryContext& factory_context,
ProtobufTypes::MessagePtr&& default_config, bool last_filter_in_filter_chain,
const std::string& filter_chain_type, absl::string_view stat_prefix)
const std::string& filter_chain_type, absl::string_view stat_prefix,
const Network::ListenerFilterMatcherSharedPtr& listener_filter_matcher)
: DynamicFilterConfigProviderImpl<FactoryCb>(
subscription, require_type_urls, factory_context, std::move(default_config),
last_filter_in_filter_chain, filter_chain_type, stat_prefix),
last_filter_in_filter_chain, filter_chain_type, stat_prefix, listener_filter_matcher),
factory_context_(factory_context) {}

void validateMessage(const std::string&, const Protobuf::Message&,
Expand All @@ -227,8 +233,8 @@ class TcpListenerDynamicFilterConfigProviderImpl
auto* factory =
Registry::FactoryRegistry<Server::Configuration::NamedListenerFilterConfigFactory>::
getFactoryByType(message.GetTypeName());
// TODO(yanjunxiang): Change nullptr to actual listener filter matcher.
return factory->createListenerFilterFactoryFromProto(message, nullptr, factory_context_);
return factory->createListenerFilterFactoryFromProto(message, listener_filter_matcher_,
factory_context_);
}
};

Expand Down Expand Up @@ -385,7 +391,8 @@ class FilterConfigProviderManagerImpl : public FilterConfigProviderManagerImplBa
const envoy::config::core::v3::ExtensionConfigSource& config_source,
const std::string& filter_config_name, FactoryCtx& factory_context,
const std::string& stat_prefix, bool last_filter_in_filter_chain,
const std::string& filter_chain_type) override {
const std::string& filter_chain_type,
const Network::ListenerFilterMatcherSharedPtr& listener_filter_matcher) override {
std::string subscription_stat_prefix;
absl::string_view provider_stat_prefix;
if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.top_level_ecds_stats")) {
Expand Down Expand Up @@ -419,9 +426,10 @@ class FilterConfigProviderManagerImpl : public FilterConfigProviderManagerImplBa
last_filter_in_filter_chain, filter_chain_type, require_type_urls);
}

auto provider = createFilterConfigProviderImpl(
subscription, require_type_urls, factory_context, std::move(default_config),
last_filter_in_filter_chain, filter_chain_type, provider_stat_prefix);
auto provider = createFilterConfigProviderImpl(subscription, require_type_urls, factory_context,
std::move(default_config),
last_filter_in_filter_chain, filter_chain_type,
provider_stat_prefix, listener_filter_matcher);

// Ensure the subscription starts if it has not already.
if (config_source.apply_default_config_without_warming()) {
Expand Down Expand Up @@ -476,10 +484,11 @@ class FilterConfigProviderManagerImpl : public FilterConfigProviderManagerImplBa
FilterConfigSubscriptionSharedPtr& subscription,
const absl::flat_hash_set<std::string>& require_type_urls, FactoryCtx& factory_context,
ProtobufTypes::MessagePtr&& default_config, bool last_filter_in_filter_chain,
const std::string& filter_chain_type, absl::string_view stat_prefix) {
const std::string& filter_chain_type, absl::string_view stat_prefix,
const Network::ListenerFilterMatcherSharedPtr& listener_filter_matcher) {
return std::make_unique<DynamicFilterConfigImpl>(
subscription, require_type_urls, factory_context, std::move(default_config),
last_filter_in_filter_chain, filter_chain_type, stat_prefix);
last_filter_in_filter_chain, filter_chain_type, stat_prefix, listener_filter_matcher);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ void HttpConnectionManagerConfig::processDynamicFilterConfig(

auto filter_config_provider = filter_config_provider_manager_.createDynamicFilterConfigProvider(
config_discovery, name, context_, stats_prefix_, last_filter_in_current_config,
filter_chain_type);
filter_chain_type, nullptr);
filter_factories.push_back(std::move(filter_config_provider));
}

Expand Down
1 change: 1 addition & 0 deletions source/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ envoy_cc_library(
hdrs = ["configuration_impl.h"],
deps = [
"//envoy/config:typed_config_interface",
"//envoy/filter:config_provider_manager_interface",
"//envoy/http:filter_interface",
"//envoy/network:connection_interface",
"//envoy/network:filter_interface",
Expand Down
10 changes: 8 additions & 2 deletions source/server/config_validation/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,11 @@ class ValidationInstance final : Logger::Loggable<Logger::Id::main>,
return ProdListenerComponentFactory::createNetworkFilterFactoryListImpl(
filters, filter_chain_factory_context);
}
std::vector<Network::ListenerFilterFactoryCb> createListenerFilterFactoryList(
Filter::ListenerFilterFactoriesList createListenerFilterFactoryList(
const Protobuf::RepeatedPtrField<envoy::config::listener::v3::ListenerFilter>& filters,
Configuration::ListenerFactoryContext& context) override {
return ProdListenerComponentFactory::createListenerFilterFactoryListImpl(filters, context);
return ProdListenerComponentFactory::createListenerFilterFactoryListImpl(
filters, context, tcp_listener_config_provider_manager_);
}
std::vector<Network::UdpListenerFilterFactoryCb> createUdpListenerFilterFactoryList(
const Protobuf::RepeatedPtrField<envoy::config::listener::v3::ListenerFilter>& filters,
Expand All @@ -172,6 +173,10 @@ class ValidationInstance final : Logger::Loggable<Logger::Id::main>,
return nullptr;
}
uint64_t nextListenerTag() override { return 0; }
Filter::TcpListenerFilterConfigProviderManagerImpl*
getTcpListenerConfigProviderManager() override {
return &tcp_listener_config_provider_manager_;
}

// Server::WorkerFactory
WorkerPtr createWorker(uint32_t, OverloadManager&, const std::string&) override {
Expand Down Expand Up @@ -230,6 +235,7 @@ class ValidationInstance final : Logger::Loggable<Logger::Id::main>,
Event::TimeSystem& time_system_;
ServerFactoryContextImpl server_contexts_;
Quic::QuicStatNames quic_stat_names_;
Filter::TcpListenerFilterConfigProviderManagerImpl tcp_listener_config_provider_manager_;
};

} // namespace Server
Expand Down
59 changes: 54 additions & 5 deletions source/server/configuration_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,60 @@ bool FilterChainUtility::buildFilterChain(Network::FilterManager& filter_manager
return filter_manager.initializeReadFilters();
}

bool FilterChainUtility::buildFilterChain(
Network::ListenerFilterManager& filter_manager,
const std::vector<Network::ListenerFilterFactoryCb>& factories) {
for (const Network::ListenerFilterFactoryCb& factory : factories) {
factory(filter_manager);
/**
* All missing listener config stats. @see stats_macros.h
*/
#define ALL_MISSING_LISTENER_CONFIG_STATS(COUNTER) COUNTER(extension_config_missing)
/**
* Struct definition for all missing listener config stats. @see stats_macros.h
*/
struct MissingListenerConfigStats {
ALL_MISSING_LISTENER_CONFIG_STATS(GENERATE_COUNTER_STRUCT)
};

class MissingConfigTcpListenerFilter : public Network::ListenerFilter,
public Logger::Loggable<Logger::Id::filter> {
public:
MissingConfigTcpListenerFilter(Stats::Scope& stats_scope)
: scope_(stats_scope), stats_({ALL_MISSING_LISTENER_CONFIG_STATS(POOL_COUNTER(scope_))}) {}

// Network::ListenerFilter
Network::FilterStatus onAccept(Network::ListenerFilterCallbacks& cb) override {
ENVOY_LOG(debug, "Listener filter: new connection accepted while missing configuration. "
"Close socket and stop the iteration onAccept.");
cb.socket().ioHandle().close();
stats_.extension_config_missing_.inc();
return Network::FilterStatus::StopIteration;
}
Network::FilterStatus onData(Network::ListenerFilterBuffer&) override {
// The socket is already closed onAccept. Just return StopIteration here.
return Network::FilterStatus::StopIteration;
}
size_t maxReadBytes() const override { return 0; }

private:
Stats::Scope& scope_;
MissingListenerConfigStats stats_;
};

bool FilterChainUtility::buildFilterChain(Network::ListenerFilterManager& filter_manager,
const Filter::ListenerFilterFactoriesList& factories,
Stats::Scope& stats_scope) {
bool added_missing_config_filter = false;
for (const auto& filter_config_provider : factories) {
auto config = filter_config_provider->config();
if (config.has_value()) {
auto config_value = config.value();
config_value(filter_manager);
continue;
}

// If a filter config is missing after warming, stop iteration.
if (!added_missing_config_filter) {
filter_manager.addAcceptFilter(nullptr,
std::make_unique<MissingConfigTcpListenerFilter>(stats_scope));
added_missing_config_filter = true;
}
}

return true;
Expand Down
4 changes: 3 additions & 1 deletion source/server/configuration_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "envoy/config/bootstrap/v3/bootstrap.pb.h"
#include "envoy/config/trace/v3/http_tracer.pb.h"
#include "envoy/config/typed_config.h"
#include "envoy/filter/config_provider_manager.h"
#include "envoy/http/filter.h"
#include "envoy/network/filter.h"
#include "envoy/server/configuration.h"
Expand Down Expand Up @@ -81,7 +82,8 @@ class FilterChainUtility {
* TODO(sumukhs): Coalesce with the above as they are very similar
*/
static bool buildFilterChain(Network::ListenerFilterManager& filter_manager,
const std::vector<Network::ListenerFilterFactoryCb>& factories);
const Filter::ListenerFilterFactoriesList& factories,
Stats::Scope& stats_scope);

/**
* Given a UdpListenerFilterManager and a list of factories, create a new filter chain. Chain
Expand Down
Loading

0 comments on commit af4f5da

Please sign in to comment.