diff --git a/api/envoy/api/v2/BUILD b/api/envoy/api/v2/BUILD index 48ddb2e13025..b86ca2a788bf 100644 --- a/api/envoy/api/v2/BUILD +++ b/api/envoy/api/v2/BUILD @@ -109,6 +109,7 @@ api_proto_library_internal( "//envoy/api/v2/core:address", "//envoy/api/v2/core:base", "//envoy/api/v2/listener", + "//envoy/api/v2/listener:udp_listener_config", ], ) @@ -120,6 +121,7 @@ api_go_grpc_library( "//envoy/api/v2/core:address_go_proto", "//envoy/api/v2/core:base_go_proto", "//envoy/api/v2/listener:listener_go_proto", + "//envoy/api/v2/listener:udp_listener_config_go_proto", ], ) diff --git a/api/envoy/api/v2/lds.proto b/api/envoy/api/v2/lds.proto index 195401341c96..643ac146213b 100644 --- a/api/envoy/api/v2/lds.proto +++ b/api/envoy/api/v2/lds.proto @@ -12,6 +12,7 @@ import "envoy/api/v2/core/address.proto"; import "envoy/api/v2/core/base.proto"; import "envoy/api/v2/discovery.proto"; import "envoy/api/v2/listener/listener.proto"; +import "envoy/api/v2/listener/udp_listener_config.proto"; import "google/api/annotations.proto"; import "google/protobuf/duration.proto"; @@ -44,7 +45,7 @@ service ListenerDiscoveryService { } } -// [#comment:next free field: 18] +// [#comment:next free field: 19] message Listener { // The unique name by which this listener is known. If no name is provided, // Envoy will allocate an internal UUID for the listener. If the listener is to be dynamically @@ -194,4 +195,12 @@ message Listener { // Specifies the intended direction of the traffic relative to the local Envoy. core.TrafficDirection traffic_direction = 16; + + // If the protocol in the listener socket address in :ref:`protocol + // ` is :ref:'UDP + // `, this field specifies the actual udp listener to create, + // i.e. :ref:`udp_listener_name + // ` = "raw_udp_listener" for + // creating a packet-oriented UDP listener. If not present, treat it as "raw_udp_listener". + listener.UdpListenerConfig udp_listener_config = 18; } diff --git a/api/envoy/api/v2/listener/BUILD b/api/envoy/api/v2/listener/BUILD index 9eb0c0ec982f..e539c4b8c090 100644 --- a/api/envoy/api/v2/listener/BUILD +++ b/api/envoy/api/v2/listener/BUILD @@ -22,3 +22,20 @@ api_go_proto_library( "//envoy/api/v2/core:base_go_proto", ], ) + +api_proto_library_internal( + name = "udp_listener_config", + srcs = ["udp_listener_config.proto"], + visibility = ["//envoy/api/v2:friends"], + deps = [ + "//envoy/api/v2/core:base", + ], +) + +api_go_proto_library( + name = "udp_listener_config", + proto = ":udp_listener_config", + deps = [ + "//envoy/api/v2/core:base_go_proto", + ], +) diff --git a/api/envoy/api/v2/listener/udp_listener_config.proto b/api/envoy/api/v2/listener/udp_listener_config.proto new file mode 100644 index 000000000000..88a2a35d3cfc --- /dev/null +++ b/api/envoy/api/v2/listener/udp_listener_config.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +package envoy.api.v2.listener; + +option java_outer_classname = "ListenerProto"; +option java_multiple_files = true; +option java_package = "io.envoyproxy.envoy.api.v2.listener"; +option go_package = "listener"; +option csharp_namespace = "Envoy.Api.V2.ListenerNS"; +option ruby_package = "Envoy::Api::V2::ListenerNS"; + +import "google/protobuf/struct.proto"; +import "google/protobuf/any.proto"; + +// [#protodoc-title: Udp Listener Config] +// Listener :ref:`configuration overview ` + +message UdpListenerConfig { + // Used to look up UDP listener factory, matches "raw_udp_listener" or + // "quic_listener" to create a specific udp listener. + // If not specified, treat as "raw_udp_listener". + string udp_listener_name = 1; + + // Used to create a specific listener factory. To some factory, e.g. + // "raw_udp_listener", config is not needed. + oneof config_type { + google.protobuf.Struct config = 2; + + google.protobuf.Any typed_config = 3; + } +} diff --git a/docs/build.sh b/docs/build.sh index b147712dd537..66a9256903a5 100755 --- a/docs/build.sh +++ b/docs/build.sh @@ -82,6 +82,7 @@ PROTO_RST=" /envoy/api/v2/srds/envoy/api/v2/srds.proto.rst /envoy/api/v2/lds/envoy/api/v2/lds.proto.rst /envoy/api/v2/listener/listener/envoy/api/v2/listener/listener.proto.rst + /envoy/api/v2/listener/udp_listener_config/envoy/api/v2/listener/udp_listener_config.proto.rst /envoy/api/v2/ratelimit/ratelimit/envoy/api/v2/ratelimit/ratelimit.proto.rst /envoy/config/accesslog/v2/als/envoy/config/accesslog/v2/als.proto.rst /envoy/config/accesslog/v2/file/envoy/config/accesslog/v2/file.proto.rst diff --git a/docs/root/api-v2/listeners/listeners.rst b/docs/root/api-v2/listeners/listeners.rst index d933ccd32d66..6ed0279da7de 100644 --- a/docs/root/api-v2/listeners/listeners.rst +++ b/docs/root/api-v2/listeners/listeners.rst @@ -7,3 +7,4 @@ Listeners ../api/v2/lds.proto ../api/v2/listener/listener.proto + ../api/v2/listener/udp_listener_config.proto diff --git a/include/envoy/network/connection_handler.h b/include/envoy/network/connection_handler.h index 9a36aed1cc49..6c24a814db5f 100644 --- a/include/envoy/network/connection_handler.h +++ b/include/envoy/network/connection_handler.h @@ -68,9 +68,59 @@ class ConnectionHandler { * after they have been temporarily disabled. */ virtual void enableListeners() PURE; + + /** + * Used by ConnectionHandler to manage listeners. + */ + class ActiveListener { + public: + virtual ~ActiveListener() = default; + + /** + * @return the tag value as configured. + */ + virtual uint64_t listenerTag() PURE; + /** + * @return the actual Listener object. + */ + virtual Listener* listener() PURE; + /** + * Destroy the actual Listener it wraps. + */ + virtual void destroy() PURE; + }; + + using ActiveListenerPtr = std::unique_ptr; }; using ConnectionHandlerPtr = std::unique_ptr; +/** + * A registered factory interface to create different kinds of + * ActiveUdpListener. + */ +class ActiveUdpListenerFactory { +public: + virtual ~ActiveUdpListenerFactory() = default; + + /** + * Creates an ActiveUdpListener object and a corresponding UdpListener + * according to given config. + * @param parent is the owner of the created ActiveListener objects. + * @param dispatcher is used to create actual UDP listener. + * @param logger might not need to be passed in. + * TODO(danzh): investigate if possible to use statically defined logger in ActiveUdpListener + * implementation instead. + * @param config provides information needed to create ActiveUdpListener and + * UdpListener objects. + * @return the ActiveUdpListener created. + */ + virtual ConnectionHandler::ActiveListenerPtr + createActiveUdpListener(ConnectionHandler& parent, Event::Dispatcher& disptacher, + spdlog::logger& logger, Network::ListenerConfig& config) const PURE; +}; + +using ActiveUdpListenerFactoryPtr = std::unique_ptr; + } // namespace Network } // namespace Envoy diff --git a/include/envoy/network/listener.h b/include/envoy/network/listener.h index 53b06b01be65..451a76508581 100644 --- a/include/envoy/network/listener.h +++ b/include/envoy/network/listener.h @@ -14,6 +14,7 @@ namespace Envoy { namespace Network { class UdpListenerFilterManager; +class ActiveUdpListenerFactory; /** * A configuration for an individual listener. @@ -90,6 +91,12 @@ class ListenerConfig { * @return const std::string& the listener's name. */ virtual const std::string& name() const PURE; + + /** + * @return factory pointer if listening on UDP socket, otherwise return + * nullptr. + */ + virtual const ActiveUdpListenerFactory* udpListenerFactory() PURE; }; /** diff --git a/include/envoy/server/BUILD b/include/envoy/server/BUILD index 2dbd768aab91..aa8d651ba02c 100644 --- a/include/envoy/server/BUILD +++ b/include/envoy/server/BUILD @@ -255,3 +255,9 @@ envoy_cc_library( "@envoy_api//envoy/config/trace/v2:trace_cc", ], ) + +envoy_cc_library( + name = "active_udp_listener_config_interface", + hdrs = ["active_udp_listener_config.h"], + deps = ["//include/envoy/network:connection_handler_interface"], +) diff --git a/include/envoy/server/active_udp_listener_config.h b/include/envoy/server/active_udp_listener_config.h new file mode 100644 index 000000000000..810d25add389 --- /dev/null +++ b/include/envoy/server/active_udp_listener_config.h @@ -0,0 +1,29 @@ +#pragma once + +#include "envoy/network/connection_handler.h" + +namespace Envoy { +namespace Server { + +/** + * Interface to create udp listener according to + * envoy::api::v2::listener::UdpListenerConfig.udp_listener_name. + */ +class ActiveUdpListenerConfigFactory { +public: + virtual ~ActiveUdpListenerConfigFactory() = default; + + /** + * Create an ActiveUdpListenerFactory object according to given message. + */ + virtual Network::ActiveUdpListenerFactoryPtr + createActiveUdpListenerFactory(const Protobuf::Message& message) PURE; + + /** + * Used to identify which udp listener to create: quic or raw udp. + */ + virtual std::string name() PURE; +}; + +} // namespace Server +} // namespace Envoy diff --git a/source/server/BUILD b/source/server/BUILD index 2e83ab4671c6..32e441e19d91 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -64,6 +64,7 @@ envoy_cc_library( "//include/envoy/network:filter_interface", "//include/envoy/network:listen_socket_interface", "//include/envoy/network:listener_interface", + "//include/envoy/server:active_udp_listener_config_interface", "//include/envoy/server:listener_manager_interface", "//include/envoy/stats:timespan", "//source/common/common:linked_object", @@ -260,6 +261,8 @@ envoy_cc_library( ":filter_chain_manager_lib", ":lds_api_lib", ":transport_socket_config_lib", + ":well_known_names_lib", + "//include/envoy/server:active_udp_listener_config_interface", "//include/envoy/server:filter_config_interface", "//include/envoy/server:listener_manager_interface", "//include/envoy/server:transport_socket_config_interface", @@ -440,3 +443,21 @@ envoy_cc_library( "//include/envoy/server:transport_socket_config_interface", ], ) + +envoy_cc_library( + name = "well_known_names_lib", + hdrs = ["well_known_names.h"], + deps = ["//source/common/singleton:const_singleton"], +) + +envoy_cc_library( + name = "active_raw_udp_listener_config", + srcs = ["active_raw_udp_listener_config.cc"], + hdrs = ["active_raw_udp_listener_config.h"], + deps = [ + ":connection_handler_lib", + ":well_known_names_lib", + "//include/envoy/registry", + "//include/envoy/server:active_udp_listener_config_interface", + ], +) diff --git a/source/server/active_raw_udp_listener_config.cc b/source/server/active_raw_udp_listener_config.cc new file mode 100644 index 000000000000..f60074dd4ea0 --- /dev/null +++ b/source/server/active_raw_udp_listener_config.cc @@ -0,0 +1,26 @@ +#include "server/active_raw_udp_listener_config.h" + +#include "server/connection_handler_impl.h" +#include "server/well_known_names.h" + +namespace Envoy { +namespace Server { + +Network::ConnectionHandler::ActiveListenerPtr ActiveRawUdpListenerFactory::createActiveUdpListener( + Network::ConnectionHandler& /*parent*/, Event::Dispatcher& dispatcher, + spdlog::logger& /*logger*/, Network::ListenerConfig& config) const { + return std::make_unique(dispatcher, config); +} + +Network::ActiveUdpListenerFactoryPtr +ActiveRawUdpListenerConfigFactory::createActiveUdpListenerFactory( + const Protobuf::Message& /*message*/) { + return std::make_unique(); +} + +std::string ActiveRawUdpListenerConfigFactory::name() { return UdpListenerNames::get().RawUdp; } + +REGISTER_FACTORY(ActiveRawUdpListenerConfigFactory, Server::ActiveUdpListenerConfigFactory); + +} // namespace Server +} // namespace Envoy diff --git a/source/server/active_raw_udp_listener_config.h b/source/server/active_raw_udp_listener_config.h new file mode 100644 index 000000000000..157ff28f6b41 --- /dev/null +++ b/source/server/active_raw_udp_listener_config.h @@ -0,0 +1,31 @@ +#pragma once + +#include "envoy/network/connection_handler.h" +#include "envoy/registry/registry.h" +#include "envoy/server/active_udp_listener_config.h" + +namespace Envoy { +namespace Server { + +class ActiveRawUdpListenerFactory : public Network::ActiveUdpListenerFactory { +public: + Network::ConnectionHandler::ActiveListenerPtr + createActiveUdpListener(Network::ConnectionHandler& parent, Event::Dispatcher& disptacher, + spdlog::logger& logger, Network::ListenerConfig& config) const override; +}; + +// This class uses a protobuf config to create a UDP listener factory which +// creates a Server::ConnectionHandlerImpl::ActiveUdpListener. +// This is the default UDP listener if not specified in config. +class ActiveRawUdpListenerConfigFactory : public ActiveUdpListenerConfigFactory { +public: + Network::ActiveUdpListenerFactoryPtr + createActiveUdpListenerFactory(const Protobuf::Message&) override; + + std::string name() override; +}; + +DECLARE_FACTORY(ActiveRawUdpListenerConfigFactory); + +} // namespace Server +} // namespace Envoy diff --git a/source/server/connection_handler_impl.cc b/source/server/connection_handler_impl.cc index b7e08a31441b..31fb7e64f2d0 100644 --- a/source/server/connection_handler_impl.cc +++ b/source/server/connection_handler_impl.cc @@ -18,28 +18,27 @@ ConnectionHandlerImpl::ConnectionHandlerImpl(spdlog::logger& logger, Event::Disp : logger_(logger), dispatcher_(dispatcher), disable_listeners_(false) {} void ConnectionHandlerImpl::addListener(Network::ListenerConfig& config) { - ActiveListenerBasePtr listener; + Network::ConnectionHandler::ActiveListenerPtr listener; Network::Address::SocketType socket_type = config.socket().socketType(); if (socket_type == Network::Address::SocketType::Stream) { - ActiveTcpListenerPtr tcp(new ActiveTcpListener(*this, config)); - listener = std::move(tcp); + listener = std::make_unique(*this, config); } else { ASSERT(socket_type == Network::Address::SocketType::Datagram, "Only datagram/stream listener supported"); - ActiveUdpListenerPtr udp(new ActiveUdpListener(*this, config)); - listener = std::move(udp); + listener = + config.udpListenerFactory()->createActiveUdpListener(*this, dispatcher_, logger_, config); } if (disable_listeners_) { - listener->listener_->disable(); + listener->listener()->disable(); } listeners_.emplace_back(config.socket().localAddress(), std::move(listener)); } void ConnectionHandlerImpl::removeListeners(uint64_t listener_tag) { for (auto listener = listeners_.begin(); listener != listeners_.end();) { - if (listener->second->listener_tag_ == listener_tag) { + if (listener->second->listenerTag() == listener_tag) { listener = listeners_.erase(listener); } else { ++listener; @@ -49,29 +48,29 @@ void ConnectionHandlerImpl::removeListeners(uint64_t listener_tag) { void ConnectionHandlerImpl::stopListeners(uint64_t listener_tag) { for (auto& listener : listeners_) { - if (listener.second->listener_tag_ == listener_tag) { - listener.second->listener_.reset(); + if (listener.second->listenerTag() == listener_tag) { + listener.second->destroy(); } } } void ConnectionHandlerImpl::stopListeners() { for (auto& listener : listeners_) { - listener.second->listener_.reset(); + listener.second->destroy(); } } void ConnectionHandlerImpl::disableListeners() { disable_listeners_ = true; for (auto& listener : listeners_) { - listener.second->listener_->disable(); + listener.second->listener()->disable(); } } void ConnectionHandlerImpl::enableListeners() { disable_listeners_ = false; for (auto& listener : listeners_) { - listener.second->listener_->enable(); + listener.second->listener()->enable(); } } @@ -84,11 +83,9 @@ void ConnectionHandlerImpl::ActiveTcpListener::removeConnection(ActiveConnection parent_.num_connections_--; } -ConnectionHandlerImpl::ActiveListenerBase::ActiveListenerBase(ConnectionHandlerImpl& parent, - Network::ListenerPtr&& listener, - Network::ListenerConfig& config) - : parent_(parent), listener_(std::move(listener)), - stats_(generateStats(config.listenerScope())), +ConnectionHandlerImpl::ActiveListenerImplBase::ActiveListenerImplBase( + Network::ListenerPtr&& listener, Network::ListenerConfig& config) + : listener_(std::move(listener)), stats_(generateStats(config.listenerScope())), listener_filters_timeout_(config.listenerFiltersTimeout()), continue_on_listener_filters_timeout_(config.continueOnListenerFiltersTimeout()), listener_tag_(config.listenerTag()), config_(config) {} @@ -104,7 +101,7 @@ ConnectionHandlerImpl::ActiveTcpListener::ActiveTcpListener(ConnectionHandlerImp ConnectionHandlerImpl::ActiveTcpListener::ActiveTcpListener(ConnectionHandlerImpl& parent, Network::ListenerPtr&& listener, Network::ListenerConfig& config) - : ConnectionHandlerImpl::ActiveListenerBase(parent, std::move(listener), config) {} + : ConnectionHandlerImpl::ActiveListenerImplBase(std::move(listener), config), parent_(parent) {} ConnectionHandlerImpl::ActiveTcpListener::~ActiveTcpListener() { // Purge sockets that have not progressed to connections. This should only happen when @@ -123,22 +120,22 @@ ConnectionHandlerImpl::ActiveTcpListener::~ActiveTcpListener() { Network::Listener* ConnectionHandlerImpl::findListenerByAddress(const Network::Address::Instance& address) { - ActiveListenerBase* listener = findActiveListenerByAddress(address); - return listener ? listener->listener_.get() : nullptr; + Network::ConnectionHandler::ActiveListener* listener = findActiveListenerByAddress(address); + return listener ? listener->listener() : nullptr; } -ConnectionHandlerImpl::ActiveListenerBase* +Network::ConnectionHandler::ActiveListener* ConnectionHandlerImpl::findActiveListenerByAddress(const Network::Address::Instance& address) { // This is a linear operation, may need to add a map to improve performance. // However, linear performance might be adequate since the number of listeners is small. // We do not return stopped listeners. - auto listener_it = std::find_if( - listeners_.begin(), listeners_.end(), - [&address]( - const std::pair& p) { - return p.second->listener_ != nullptr && p.first->type() == Network::Address::Type::Ip && - *(p.first) == address; - }); + auto listener_it = + std::find_if(listeners_.begin(), listeners_.end(), + [&address](const std::pair& p) { + return p.second->listener() != nullptr && + p.first->type() == Network::Address::Type::Ip && *(p.first) == address; + }); // If there is exact address match, return the corresponding listener. if (listener_it != listeners_.end()) { @@ -150,9 +147,9 @@ ConnectionHandlerImpl::findActiveListenerByAddress(const Network::Address::Insta // TODO(wattli): consolidate with previous search for more efficiency. listener_it = std::find_if( listeners_.begin(), listeners_.end(), - [&address]( - const std::pair& p) { - return p.second->listener_ != nullptr && p.first->type() == Network::Address::Type::Ip && + [&address](const std::pair& p) { + return p.second->listener() != nullptr && p.first->type() == Network::Address::Type::Ip && p.first->ip()->port() == address.ip()->port() && p.first->ip()->isAnyAddress(); }); return (listener_it != listeners_.end()) ? listener_it->second.get() : nullptr; @@ -210,7 +207,7 @@ void ConnectionHandlerImpl::ActiveSocket::continueFilterChain(bool success) { void ConnectionHandlerImpl::ActiveSocket::newConnection() { // Check if the socket may need to be redirected to another listener. - ActiveListenerBase* new_listener = nullptr; + ConnectionHandler::ActiveListener* new_listener = nullptr; if (hand_off_restored_destination_connections_ && socket_->localAddressRestored()) { // Find a listener associated with the original destination address. @@ -318,15 +315,12 @@ ListenerStats ConnectionHandlerImpl::generateStats(Stats::Scope& scope) { return {ALL_LISTENER_STATS(POOL_COUNTER(scope), POOL_GAUGE(scope), POOL_HISTOGRAM(scope))}; } -ConnectionHandlerImpl::ActiveUdpListener::ActiveUdpListener(ConnectionHandlerImpl& parent, - Network::ListenerConfig& config) - : ActiveUdpListener(parent, parent.dispatcher_.createUdpListener(config.socket(), *this), - config) {} +ActiveUdpListener::ActiveUdpListener(Event::Dispatcher& dispatcher, Network::ListenerConfig& config) + : ActiveUdpListener(dispatcher.createUdpListener(config.socket(), *this), config) {} -ConnectionHandlerImpl::ActiveUdpListener::ActiveUdpListener(ConnectionHandlerImpl& parent, - Network::ListenerPtr&& listener, - Network::ListenerConfig& config) - : ConnectionHandlerImpl::ActiveListenerBase(parent, std::move(listener), config), +ActiveUdpListener::ActiveUdpListener(Network::ListenerPtr&& listener, + Network::ListenerConfig& config) + : ConnectionHandlerImpl::ActiveListenerImplBase(std::move(listener), config), udp_listener_(dynamic_cast(listener_.get())), read_filter_(nullptr) { // TODO(sumukhs): Try to avoid dynamic_cast by coming up with a better interface design ASSERT(udp_listener_ != nullptr, ""); @@ -342,32 +336,27 @@ ConnectionHandlerImpl::ActiveUdpListener::ActiveUdpListener(ConnectionHandlerImp } } -void ConnectionHandlerImpl::ActiveUdpListener::onData(Network::UdpRecvData& data) { - read_filter_->onData(data); -} +void ActiveUdpListener::onData(Network::UdpRecvData& data) { read_filter_->onData(data); } -void ConnectionHandlerImpl::ActiveUdpListener::onWriteReady(const Network::Socket&) { +void ActiveUdpListener::onWriteReady(const Network::Socket&) { // TODO(sumukhs): This is not used now. When write filters are implemented, this is a // trigger to invoke the on write ready API on the filters which is when they can write // data } -void ConnectionHandlerImpl::ActiveUdpListener::onReceiveError( - const Network::UdpListenerCallbacks::ErrorCode&, Api::IoError::IoErrorCode) { +void ActiveUdpListener::onReceiveError(const Network::UdpListenerCallbacks::ErrorCode&, + Api::IoError::IoErrorCode) { // TODO(sumukhs): Determine what to do on receive error. // Would the filters need to know on error? Can't foresee a scenario where they // would take an action } -void ConnectionHandlerImpl::ActiveUdpListener::addReadFilter( - Network::UdpListenerReadFilterPtr&& filter) { +void ActiveUdpListener::addReadFilter(Network::UdpListenerReadFilterPtr&& filter) { ASSERT(read_filter_ == nullptr, "Cannot add a 2nd UDP read filter"); read_filter_ = std::move(filter); } -Network::UdpListener& ConnectionHandlerImpl::ActiveUdpListener::udpListener() { - return *udp_listener_; -} +Network::UdpListener& ActiveUdpListener::udpListener() { return *udp_listener_; } } // namespace Server } // namespace Envoy diff --git a/source/server/connection_handler_impl.h b/source/server/connection_handler_impl.h index 7ca0088abe61..4c1354e29870 100644 --- a/source/server/connection_handler_impl.h +++ b/source/server/connection_handler_impl.h @@ -12,6 +12,7 @@ #include "envoy/network/filter.h" #include "envoy/network/listen_socket.h" #include "envoy/network/listener.h" +#include "envoy/server/active_udp_listener_config.h" #include "envoy/server/listener_manager.h" #include "envoy/stats/scope.h" #include "envoy/stats/timespan.h" @@ -59,33 +60,21 @@ class ConnectionHandlerImpl : public Network::ConnectionHandler, NonCopyable { Network::Listener* findListenerByAddress(const Network::Address::Instance& address) override; -private: - struct ActiveListenerBase; - using ActiveListenerBasePtr = std::unique_ptr; - - struct ActiveTcpListener; - using ActiveTcpListenerPtr = std::unique_ptr; - - struct ActiveUdpListener; - using ActiveUdpListenerPtr = std::unique_ptr; - - ActiveListenerBase* findActiveListenerByAddress(const Network::Address::Instance& address); - - struct ActiveConnection; - using ActiveConnectionPtr = std::unique_ptr; - struct ActiveSocket; - using ActiveSocketPtr = std::unique_ptr; + Network::ConnectionHandler::ActiveListener* + findActiveListenerByAddress(const Network::Address::Instance& address); /** * Wrapper for an active listener owned by this handler. */ - struct ActiveListenerBase { - ActiveListenerBase(ConnectionHandlerImpl& parent, Network::ListenerPtr&& listener, - Network::ListenerConfig& config); + class ActiveListenerImplBase : public Network::ConnectionHandler::ActiveListener { + public: + ActiveListenerImplBase(Network::ListenerPtr&& listener, Network::ListenerConfig& config); - virtual ~ActiveListenerBase() = default; + // Network::ConnectionHandler::ActiveListener. + uint64_t listenerTag() override { return listener_tag_; } + Network::Listener* listener() override { return listener_.get(); } + void destroy() override { listener_.reset(); } - ConnectionHandlerImpl& parent_; Network::ListenerPtr listener_; ListenerStats stats_; const std::chrono::milliseconds listener_filters_timeout_; @@ -94,38 +83,19 @@ class ConnectionHandlerImpl : public Network::ConnectionHandler, NonCopyable { Network::ListenerConfig& config_; }; - /** - * Wrapper for an active udp listener owned by this handler. - */ - struct ActiveUdpListener : public Network::UdpListenerCallbacks, - public ActiveListenerBase, - public Network::UdpListenerFilterManager, - public Network::UdpReadFilterCallbacks { - ActiveUdpListener(ConnectionHandlerImpl& parent, Network::ListenerConfig& config); - - ActiveUdpListener(ConnectionHandlerImpl& parent, Network::ListenerPtr&& listener, - Network::ListenerConfig& config); - - // Network::UdpListenerCallbacks - void onData(Network::UdpRecvData& data) override; - void onWriteReady(const Network::Socket& socket) override; - void onReceiveError(const Network::UdpListenerCallbacks::ErrorCode& error_code, - Api::IoError::IoErrorCode err) override; - - // Network::UdpListenerFilterManager - void addReadFilter(Network::UdpListenerReadFilterPtr&& filter) override; - - // Network::UdpReadFilterCallbacks - Network::UdpListener& udpListener() override; - - Network::UdpListener* udp_listener_; - Network::UdpListenerReadFilterPtr read_filter_; - }; +private: + class ActiveTcpListener; + using ActiveTcpListenerPtr = std::unique_ptr; + struct ActiveConnection; + using ActiveConnectionPtr = std::unique_ptr; + struct ActiveSocket; + using ActiveSocketPtr = std::unique_ptr; /** * Wrapper for an active tcp listener owned by this handler. */ - struct ActiveTcpListener : public Network::ListenerCallbacks, public ActiveListenerBase { + class ActiveTcpListener : public Network::ListenerCallbacks, public ActiveListenerImplBase { + public: ActiveTcpListener(ConnectionHandlerImpl& parent, Network::ListenerConfig& config); ActiveTcpListener(ConnectionHandlerImpl& parent, Network::ListenerPtr&& listener, @@ -149,6 +119,7 @@ class ConnectionHandlerImpl : public Network::ConnectionHandler, NonCopyable { */ void newConnection(Network::ConnectionSocketPtr&& socket); + ConnectionHandlerImpl& parent_; std::list sockets_; std::list connections_; }; @@ -225,10 +196,42 @@ class ConnectionHandlerImpl : public Network::ConnectionHandler, NonCopyable { spdlog::logger& logger_; Event::Dispatcher& dispatcher_; - std::list> listeners_; + std::list> + listeners_; std::atomic num_connections_{}; bool disable_listeners_; }; +/** + * Wrapper for an active udp listener owned by this handler. + * TODO(danzh): rename to ActiveRawUdpListener. + */ +class ActiveUdpListener : public Network::UdpListenerCallbacks, + public ConnectionHandlerImpl::ActiveListenerImplBase, + public Network::UdpListenerFilterManager, + public Network::UdpReadFilterCallbacks { +public: + ActiveUdpListener(Event::Dispatcher& dispatcher, Network::ListenerConfig& config); + + ActiveUdpListener(Network::ListenerPtr&& listener, Network::ListenerConfig& config); + + // Network::UdpListenerCallbacks + void onData(Network::UdpRecvData& data) override; + void onWriteReady(const Network::Socket& socket) override; + void onReceiveError(const Network::UdpListenerCallbacks::ErrorCode& error_code, + Api::IoError::IoErrorCode err) override; + + // Network::UdpListenerFilterManager + void addReadFilter(Network::UdpListenerReadFilterPtr&& filter) override; + + // Network::UdpReadFilterCallbacks + Network::UdpListener& udpListener() override; + +private: + Network::UdpListener* udp_listener_; + Network::UdpListenerReadFilterPtr read_filter_; +}; + } // namespace Server } // namespace Envoy diff --git a/source/server/http/admin.h b/source/server/http/admin.h index e3806f58e188..21fe0ae567d4 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -319,6 +319,9 @@ class AdminImpl : public Admin, Stats::Scope& listenerScope() override { return *scope_; } uint64_t listenerTag() const override { return 0; } const std::string& name() const override { return name_; } + const Network::ActiveUdpListenerFactory* udpListenerFactory() override { + NOT_REACHED_GCOVR_EXCL_LINE; + } AdminImpl& parent_; const std::string name_; diff --git a/source/server/listener_manager_impl.cc b/source/server/listener_manager_impl.cc index 0f2ec74ee62b..1b305d3157fd 100644 --- a/source/server/listener_manager_impl.cc +++ b/source/server/listener_manager_impl.cc @@ -4,6 +4,7 @@ #include "envoy/admin/v2alpha/config_dump.pb.h" #include "envoy/registry/registry.h" +#include "envoy/server/active_udp_listener_config.h" #include "envoy/server/transport_socket_config.h" #include "envoy/stats/scope.h" @@ -23,6 +24,7 @@ #include "server/drain_manager_impl.h" #include "server/filter_chain_manager_impl.h" #include "server/transport_socket_config_impl.h" +#include "server/well_known_names.h" #include "extensions/filters/listener/well_known_names.h" #include "extensions/transport_sockets/well_known_names.h" @@ -226,6 +228,16 @@ ListenerImpl::ListenerImpl(const envoy::api::v2::Listener& config, const std::st addListenSocketOptions(Network::SocketOptionFactory::buildIpPacketInfoOptions()); // Needed to return receive buffer overflown indicator. addListenSocketOptions(Network::SocketOptionFactory::buildRxQueueOverFlowOptions()); + std::string listener_name = + config.has_udp_listener_config() ? config.udp_listener_config().udp_listener_name() : ""; + if (listener_name.empty()) { + listener_name = UdpListenerNames::get().RawUdp; + } + udp_listener_factory_ = + Config::Utility::getAndCheckFactory(listener_name) + .createActiveUdpListenerFactory(config.has_udp_listener_config() + ? config.udp_listener_config() + : envoy::api::v2::listener::UdpListenerConfig()); } if (!config.listener_filters().empty()) { diff --git a/source/server/listener_manager_impl.h b/source/server/listener_manager_impl.h index 1197ac82a9be..b634a1bf6537 100644 --- a/source/server/listener_manager_impl.h +++ b/source/server/listener_manager_impl.h @@ -279,6 +279,9 @@ class ListenerImpl : public Network::ListenerConfig, Stats::Scope& listenerScope() override { return *listener_scope_; } uint64_t listenerTag() const override { return listener_tag_; } const std::string& name() const override { return name_; } + const Network::ActiveUdpListenerFactory* udpListenerFactory() override { + return udp_listener_factory_.get(); + } // Server::Configuration::ListenerFactoryContext AccessLog::AccessLogManager& accessLogManager() override { @@ -379,6 +382,7 @@ class ListenerImpl : public Network::ListenerConfig, Network::Socket::OptionsSharedPtr listen_socket_options_; const std::chrono::milliseconds listener_filters_timeout_; const bool continue_on_listener_filters_timeout_; + Network::ActiveUdpListenerFactoryPtr udp_listener_factory_; // to access ListenerManagerImpl::factory_. friend class ListenerFilterChainFactoryBuilder; }; diff --git a/source/server/well_known_names.h b/source/server/well_known_names.h new file mode 100644 index 000000000000..23d202d996ae --- /dev/null +++ b/source/server/well_known_names.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "common/singleton/const_singleton.h" + +namespace Envoy { +namespace Server { + +/** + * Well-known active UDP listener names. + */ +class UdpListenerNameValues { +public: + const std::string RawUdp = "raw_udp_listener"; +}; + +using UdpListenerNames = ConstSingleton; + +} // namespace Server +} // namespace Envoy diff --git a/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc b/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc index 2f37b861df43..cdc225696c3d 100644 --- a/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc +++ b/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc @@ -77,6 +77,7 @@ class ProxyProtocolTest : public testing::TestWithParam, Stats::Scope& listenerScope() override { return parent_.stats_store_; } uint64_t listenerTag() const override { return 0; } const std::string& name() const override { return name_; } + Network::ActiveUdpListenerFactory* udpListenerFactory() override { + return udp_listener_factory_.get(); + } FakeUpstream& parent_; + Network::ActiveUdpListenerFactoryPtr udp_listener_factory_; std::string name_; }; diff --git a/test/mocks/network/mocks.h b/test/mocks/network/mocks.h index e63d3f22957b..33931209fffe 100644 --- a/test/mocks/network/mocks.h +++ b/test/mocks/network/mocks.h @@ -311,6 +311,7 @@ class MockListenerConfig : public ListenerConfig { MOCK_METHOD0(listenerScope, Stats::Scope&()); MOCK_CONST_METHOD0(listenerTag, uint64_t()); MOCK_CONST_METHOD0(name, const std::string&()); + MOCK_METHOD0(udpListenerFactory, const Network::ActiveUdpListenerFactory*()); testing::NiceMock filter_chain_factory_; testing::NiceMock socket_; diff --git a/test/server/BUILD b/test/server/BUILD index 6f2893539e87..7ffe0fcfd36b 100644 --- a/test/server/BUILD +++ b/test/server/BUILD @@ -53,6 +53,7 @@ envoy_cc_test( "//source/common/common:utility_lib", "//source/common/network:address_lib", "//source/common/stats:stats_lib", + "//source/server:active_raw_udp_listener_config", "//source/server:connection_handler_lib", "//test/mocks/network:network_mocks", "//test/mocks/server:server_mocks", @@ -180,6 +181,7 @@ envoy_cc_test( "//source/extensions/transport_sockets/raw_buffer:config", "//source/extensions/transport_sockets/tls:config", "//source/extensions/transport_sockets/tls:ssl_socket_lib", + "//source/server:active_raw_udp_listener_config", "//source/server:listener_manager_lib", "//test/mocks/network:network_mocks", "//test/mocks/server:server_mocks", diff --git a/test/server/connection_handler_test.cc b/test/server/connection_handler_test.cc index 7e4e2e03237c..f73fb9b81e77 100644 --- a/test/server/connection_handler_test.cc +++ b/test/server/connection_handler_test.cc @@ -1,3 +1,4 @@ +#include "envoy/server/active_udp_listener_config.h" #include "envoy/stats/scope.h" #include "common/common/utility.h" @@ -44,6 +45,12 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable(listener_name) + .createActiveUdpListenerFactory(dummy); EXPECT_CALL(socket_, socketType()).WillOnce(Return(socket_type)); } @@ -66,6 +73,9 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable udp_listener_factory_; }; using TestListenerPtr = std::unique_ptr;