From 2c2e3a85209eedd30413d11dfddcd0c635731b34 Mon Sep 17 00:00:00 2001 From: Michael Warres Date: Sun, 6 Jan 2019 16:00:24 -0500 Subject: [PATCH 1/7] Add QuicListenerFactory and QuicListenerConfigFactory interfaces. Signed-off-by: Michael Warres --- include/envoy/quic/BUILD | 29 ++++++++++++++++++++ include/envoy/quic/config.h | 44 ++++++++++++++++++++++++++++++ include/envoy/quic/listener.h | 50 +++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 include/envoy/quic/BUILD create mode 100644 include/envoy/quic/config.h create mode 100644 include/envoy/quic/listener.h diff --git a/include/envoy/quic/BUILD b/include/envoy/quic/BUILD new file mode 100644 index 0000000000000..dcc02118ad216 --- /dev/null +++ b/include/envoy/quic/BUILD @@ -0,0 +1,29 @@ +licenses(["notice"]) # Apache 2 + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +envoy_package() + +envoy_cc_library( + name = "config_interface", + hdrs = ["config.h"], + deps = [ + ":listener_interface", + "//include/envoy/server:filter_config_interface", + "//source/common/protobuf", + ], +) + +envoy_cc_library( + name = "listener_interface", + hdrs = ["listener.h"], + deps = [ + "//include/envoy/event:dispatcher_interface", + "//include/envoy/network:listen_socket_interface", + "//include/envoy/network:listener_interface", + ], +) diff --git a/include/envoy/quic/config.h b/include/envoy/quic/config.h new file mode 100644 index 0000000000000..5df7b780416b7 --- /dev/null +++ b/include/envoy/quic/config.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include "envoy/quic/listener.h" +#include "envoy/server/filter_config.h" + +#include "common/protobuf/protobuf.h" + +namespace Envoy { +namespace Quic { + +/** + * Implemented by the QUIC listener and registered via RegisterFactory. + */ +class QuicListenerConfigFactory { +public: + virtual ~QuicListenerConfigFactory() {} + + /** + * Create a particular QUIC listener factory implementation. If the implementation is unable to + * produce a factory with the provided parameters, it should throw an EnvoyException. + * @param config the protobuf configuration for the listener. + * @param context the listener's context. + */ + virtual QuicListenerFactoryPtr + createListenerFactoryFromProto(const Protobuf::Message& config, + Server::Configuration::ListenerFactoryContext& context) PURE; + + /** + * @return ProtobufTypes::MessagePtr create empty config proto message. The QUIC listener + * config, which arrives in an opaque message, will be parsed into this empty proto. + */ + virtual ProtobufTypes::MessagePtr createEmptyConfigProto() PURE; + + /** + * @return std::string the identifying name for a particular implementation of a QUIC listener + * produced by the factory. + */ + virtual std::string name() PURE; +}; + +} // namespace Quic +} // namespace Envoy diff --git a/include/envoy/quic/listener.h b/include/envoy/quic/listener.h new file mode 100644 index 0000000000000..d5f84efdea6cf --- /dev/null +++ b/include/envoy/quic/listener.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include "envoy/event/dispatcher.h" +#include "envoy/network/listen_socket.h" +#include "envoy/network/listener.h" + +namespace Envoy { +namespace Quic { + +/** + * Callbacks used by QuicListener instances to communicate with their execution + * environment. + */ +class QuicListenerCallbacks { +public: + virtual ~QuicListenerCallbacks() {} + + /** + * @return the socket via which the listener can send and receive datagrams. + */ + virtual Network::Socket& socket() PURE; + + /** + * @return the dispatcher via which the listener can register for events. + */ + virtual Event::Dispatcher& dispatcher() PURE; +}; + +/** + * Factory for creating QUIC listeners. + */ +class QuicListenerFactory { +public: + virtual ~QuicListenerFactory() {} + + /** + * Create a QUIC listener. The listener is responsible for registering for + * socket events as needed; both socket and dispatcher are accessible via the + * callbacks provided. + * @return the newly created listener. + */ + virtual Network::ListenerPtr createQuicListener(QuicListenerCallbacks& callbacks) PURE; +}; + +typedef std::unique_ptr QuicListenerFactoryPtr; + +} // namespace Quic +} // namespace Envoy From 7ec46e03ccdecbccf7c4ca4f998bbdeb32f5c1da Mon Sep 17 00:00:00 2001 From: Michael Warres Date: Sun, 6 Jan 2019 17:57:34 -0500 Subject: [PATCH 2/7] Add quic_listener field to Listener config protobuf. Signed-off-by: Michael Warres --- api/envoy/api/v2/lds.proto | 6 ++++++ api/envoy/api/v2/listener/listener.proto | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/api/envoy/api/v2/lds.proto b/api/envoy/api/v2/lds.proto index d05e37e5b080a..55bd14579abf6 100644 --- a/api/envoy/api/v2/lds.proto +++ b/api/envoy/api/v2/lds.proto @@ -131,6 +131,12 @@ message Listener { // timeout. If not specified, a default timeout of 15s is used. google.protobuf.Duration listener_filters_timeout = 15 [(gogoproto.stdduration) = true]; + // [#not-implemented-hide:] + // QUIC listener for handing UDP packets. Only used when :ref:`address + // ` specifies a UDP :ref:`SocketAddress + // `. + listener.QuicListener quic_listener = 16; + // Whether the listener should be set as a transparent socket. // When this flag is set to true, connections can be redirected to the listener using an // *iptables* *TPROXY* target, in which case the original source and destination addresses and diff --git a/api/envoy/api/v2/listener/listener.proto b/api/envoy/api/v2/listener/listener.proto index 0d617ceacf5ff..3816dcd7e47cf 100644 --- a/api/envoy/api/v2/listener/listener.proto +++ b/api/envoy/api/v2/listener/listener.proto @@ -215,3 +215,22 @@ message ListenerFilter { google.protobuf.Any typed_config = 3; } } + +// [#proto-status: experimental] +message QuicListener { + // The name of the QUIC listener to instantiate. The name must match a + // supported listener. Future listeners may include: + // + // * envoy.quic.quiche + // * envoy.quic.ngtcp2 + string name = 1 [(validate.rules).string.min_bytes = 1]; + + // Filter specific configuration which depends on the filter being instantiated. + // See the supported filters for further documentation. + oneof config_type { + google.protobuf.Struct config = 2; + + // [#not-implemented-hide:] + google.protobuf.Any typed_config = 3; + } +} From a5b9636ce2c1887c705efddde990545d84e02008 Mon Sep 17 00:00:00 2001 From: Michael Warres Date: Sun, 6 Jan 2019 17:58:34 -0500 Subject: [PATCH 3/7] Add Network::ListenerConfig::quicListenerFactory() method. Signed-off-by: Michael Warres --- include/envoy/network/BUILD | 1 + include/envoy/network/listener.h | 7 ++++ include/envoy/quic/BUILD | 2 -- include/envoy/quic/listener.h | 34 +++++++++++++++++-- source/server/http/admin.h | 1 + source/server/listener_manager_impl.h | 1 + .../proxy_protocol/proxy_protocol_test.cc | 2 ++ test/integration/fake_upstream.h | 1 + test/mocks/network/mocks.h | 1 + test/server/connection_handler_test.cc | 1 + 10 files changed, 46 insertions(+), 5 deletions(-) diff --git a/include/envoy/network/BUILD b/include/envoy/network/BUILD index 548e8af481643..de6cd23d203ca 100644 --- a/include/envoy/network/BUILD +++ b/include/envoy/network/BUILD @@ -85,6 +85,7 @@ envoy_cc_library( deps = [ ":connection_interface", ":listen_socket_interface", + "//include/envoy/quic:listener_interface", "//include/envoy/stats:stats_interface", ], ) diff --git a/include/envoy/network/listener.h b/include/envoy/network/listener.h index ae34c3151117f..daa2b5ed14267 100644 --- a/include/envoy/network/listener.h +++ b/include/envoy/network/listener.h @@ -7,6 +7,7 @@ #include "envoy/common/exception.h" #include "envoy/network/connection.h" #include "envoy/network/listen_socket.h" +#include "envoy/quic/listener.h" #include "envoy/stats/scope.h" namespace Envoy { @@ -31,6 +32,12 @@ class ListenerConfig { */ virtual FilterChainFactory& filterChainFactory() PURE; + /** + * @return QuicListenerFactory* the factory for creating QUIC listeners, or nullptr if none is + * configured. + */ + virtual Quic::QuicListenerFactory* quicListenerFactory() PURE; + /** * @return Socket& the actual listen socket. The address of this socket may be * different from configured if for example the configured address binds to port zero. diff --git a/include/envoy/quic/BUILD b/include/envoy/quic/BUILD index dcc02118ad216..df1bca6c570d3 100644 --- a/include/envoy/quic/BUILD +++ b/include/envoy/quic/BUILD @@ -22,8 +22,6 @@ envoy_cc_library( name = "listener_interface", hdrs = ["listener.h"], deps = [ - "//include/envoy/event:dispatcher_interface", "//include/envoy/network:listen_socket_interface", - "//include/envoy/network:listener_interface", ], ) diff --git a/include/envoy/quic/listener.h b/include/envoy/quic/listener.h index d5f84efdea6cf..b254a6f66be71 100644 --- a/include/envoy/quic/listener.h +++ b/include/envoy/quic/listener.h @@ -2,11 +2,13 @@ #include -#include "envoy/event/dispatcher.h" #include "envoy/network/listen_socket.h" -#include "envoy/network/listener.h" namespace Envoy { +namespace Event { +class Dispatcher; +} + namespace Quic { /** @@ -28,6 +30,32 @@ class QuicListenerCallbacks { virtual Event::Dispatcher& dispatcher() PURE; }; +/** + * Abstract interface for QUIC listeners. + */ +// Ideally, this class would extend Network::Listener, or Network::Listener +// could even be used directly instead of it. However, as currently stands, this +// would introduce a circular dependency, since Network::ListenerConfig (which +// refers to QuicListenerFactory) and Network::Listener (to which +// QuicListenerFactory would refer) are defined in the same build target +// (//include/envoy/network:listener_interface). +class QuicListener { +public: + virtual ~QuicListener() {} + + /** + * Temporarily disable accepting new connections. + */ + virtual void disable() PURE; + + /** + * Enable accepting new connections. + */ + virtual void enable() PURE; +}; + +typedef std::unique_ptr QuicListenerPtr; + /** * Factory for creating QUIC listeners. */ @@ -41,7 +69,7 @@ class QuicListenerFactory { * callbacks provided. * @return the newly created listener. */ - virtual Network::ListenerPtr createQuicListener(QuicListenerCallbacks& callbacks) PURE; + virtual QuicListenerPtr createQuicListener(QuicListenerCallbacks& callbacks) PURE; }; typedef std::unique_ptr QuicListenerFactoryPtr; diff --git a/source/server/http/admin.h b/source/server/http/admin.h index 979be5a9c718e..10a5dc21be3ce 100644 --- a/source/server/http/admin.h +++ b/source/server/http/admin.h @@ -256,6 +256,7 @@ class AdminImpl : public Admin, // Network::ListenerConfig Network::FilterChainManager& filterChainManager() override { return parent_; } Network::FilterChainFactory& filterChainFactory() override { return parent_; } + Quic::QuicListenerFactory* quicListenerFactory() override { return nullptr; } Network::Socket& socket() override { return parent_.mutable_socket(); } const Network::Socket& socket() const override { return parent_.mutable_socket(); } bool bindToPort() override { return true; } diff --git a/source/server/listener_manager_impl.h b/source/server/listener_manager_impl.h index bf860ad9baa31..67fe15324b85c 100644 --- a/source/server/listener_manager_impl.h +++ b/source/server/listener_manager_impl.h @@ -242,6 +242,7 @@ class ListenerImpl : public Network::ListenerConfig, // Network::ListenerConfig Network::FilterChainManager& filterChainManager() override { return *this; } Network::FilterChainFactory& filterChainFactory() override { return *this; } + Quic::QuicListenerFactory* quicListenerFactory() override { return nullptr; } Network::Socket& socket() override { return *socket_; } const Network::Socket& socket() const override { return *socket_; } bool bindToPort() override { return bind_to_port_; } 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 ea2dfa3be19f3..7c0747d757d00 100644 --- a/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc +++ b/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc @@ -65,6 +65,7 @@ class ProxyProtocolTest : public testing::TestWithParam, // Network::ListenerConfig Network::FilterChainManager& filterChainManager() override { return parent_; } Network::FilterChainFactory& filterChainFactory() override { return parent_; } + Quic::QuicListenerFactory* quicListenerFactory() override { return nullptr; } Network::Socket& socket() override { return *parent_.socket_; } const Network::Socket& socket() const override { return *parent_.socket_; } bool bindToPort() override { return true; } diff --git a/test/mocks/network/mocks.h b/test/mocks/network/mocks.h index f72084ad6774e..6871a5b3f2da3 100644 --- a/test/mocks/network/mocks.h +++ b/test/mocks/network/mocks.h @@ -257,6 +257,7 @@ class MockListenerConfig : public ListenerConfig { MOCK_METHOD0(filterChainManager, FilterChainManager&()); MOCK_METHOD0(filterChainFactory, FilterChainFactory&()); + MOCK_METHOD0(quicListenerFactory, Quic::QuicListenerFactory*()); MOCK_METHOD0(socket, Socket&()); MOCK_CONST_METHOD0(socket, const Socket&()); MOCK_METHOD0(bindToPort, bool()); diff --git a/test/server/connection_handler_test.cc b/test/server/connection_handler_test.cc index c22078eee68b8..0b693092b0f6d 100644 --- a/test/server/connection_handler_test.cc +++ b/test/server/connection_handler_test.cc @@ -43,6 +43,7 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable Date: Sun, 6 Jan 2019 20:18:05 -0500 Subject: [PATCH 4/7] Add Server::ListenerComponentFactory::createQuicListenerFactory(). Along with this, add Server::ProdListenerComponentFactory implementation, and Server::ListenerImpl use of it. Signed-off-by: Michael Warres --- include/envoy/server/BUILD | 1 + include/envoy/server/listener_manager.h | 11 +++++++++++ source/server/BUILD | 1 + source/server/config_validation/server.h | 6 ++++++ source/server/listener_manager_impl.cc | 21 +++++++++++++++++++++ source/server/listener_manager_impl.h | 15 ++++++++++++++- test/mocks/server/mocks.h | 4 ++++ test/server/listener_manager_impl_test.cc | 7 +++++++ 8 files changed, 65 insertions(+), 1 deletion(-) diff --git a/include/envoy/server/BUILD b/include/envoy/server/BUILD index bbf95b4a96149..6b3cd744c22ab 100644 --- a/include/envoy/server/BUILD +++ b/include/envoy/server/BUILD @@ -174,6 +174,7 @@ envoy_cc_library( ":guarddog_interface", "//include/envoy/network:filter_interface", "//include/envoy/network:listen_socket_interface", + "//include/envoy/quic:listener_interface", "//include/envoy/ssl:context_interface", "//source/common/protobuf", "@envoy_api//envoy/api/v2/listener:listener_cc", diff --git a/include/envoy/server/listener_manager.h b/include/envoy/server/listener_manager.h index fd2808cd6da76..1813e55093137 100644 --- a/include/envoy/server/listener_manager.h +++ b/include/envoy/server/listener_manager.h @@ -4,6 +4,7 @@ #include "envoy/network/filter.h" #include "envoy/network/listen_socket.h" #include "envoy/network/listener.h" +#include "envoy/quic/listener.h" #include "envoy/server/drain_manager.h" #include "envoy/server/filter_config.h" #include "envoy/server/guarddog.h" @@ -74,6 +75,16 @@ class ListenerComponentFactory { const Protobuf::RepeatedPtrField& filters, Configuration::ListenerFactoryContext& context) PURE; + /** + * Creates a QUIC listener factory. + * @param listener_config supplies the QUIC listenerconfiguration. + * @param context supplies the factory creation context. + * @return Quic::QuicListenerFactoryPtr the QUIC listener factory. + */ + virtual Quic::QuicListenerFactoryPtr + createQuicListenerFactory(const envoy::api::v2::listener::QuicListener& listener_config, + Configuration::ListenerFactoryContext& context) PURE; + /** * @return DrainManagerPtr a new drain manager. * @param drain_type supplies the type of draining to do for the owning listener. diff --git a/source/server/BUILD b/source/server/BUILD index 518d703237112..dde5801266788 100644 --- a/source/server/BUILD +++ b/source/server/BUILD @@ -205,6 +205,7 @@ envoy_cc_library( ":init_manager_lib", ":lds_api_lib", ":transport_socket_config_lib", + "//include/envoy/quic:config_interface", "//include/envoy/server:filter_config_interface", "//include/envoy/server:listener_manager_interface", "//include/envoy/server:transport_socket_config_interface", diff --git a/source/server/config_validation/server.h b/source/server/config_validation/server.h index df320e9bc0a1e..ab37cc547ea37 100644 --- a/source/server/config_validation/server.h +++ b/source/server/config_validation/server.h @@ -113,6 +113,12 @@ class ValidationInstance : Logger::Loggable, Configuration::ListenerFactoryContext& context) override { return ProdListenerComponentFactory::createListenerFilterFactoryList_(filters, context); } + Quic::QuicListenerFactoryPtr + createQuicListenerFactory(const envoy::api::v2::listener::QuicListener& listener_config, + Configuration::ListenerFactoryContext& context) override { + return ProdListenerComponentFactory::createQuicListenerFactory_(listener_config, context); + } + Network::SocketSharedPtr createListenSocket(Network::Address::InstanceConstSharedPtr, Network::Address::SocketType, const Network::Socket::OptionsSharedPtr&, diff --git a/source/server/listener_manager_impl.cc b/source/server/listener_manager_impl.cc index 06b3a659331b0..9a2f692696a65 100644 --- a/source/server/listener_manager_impl.cc +++ b/source/server/listener_manager_impl.cc @@ -1,6 +1,7 @@ #include "server/listener_manager_impl.h" #include "envoy/admin/v2alpha/config_dump.pb.h" +#include "envoy/quic/config.h" #include "envoy/registry/registry.h" #include "envoy/server/transport_socket_config.h" #include "envoy/stats/scope.h" @@ -97,6 +98,22 @@ ProdListenerComponentFactory::createListenerFilterFactoryList_( return ret; } +Quic::QuicListenerFactoryPtr ProdListenerComponentFactory::createQuicListenerFactory_( + const envoy::api::v2::listener::QuicListener& listener_config, + Configuration::ListenerFactoryContext& context) { + const ProtobufTypes::String string_name = listener_config.name(); + ENVOY_LOG(debug, "QUIC listener:"); + ENVOY_LOG(debug, " name: {}", string_name); + const Json::ObjectSharedPtr json_config = + MessageUtil::getJsonObjectFromMessage(listener_config.config()); + ENVOY_LOG(debug, " config: {}", json_config->asJsonString()); + + // Now see if there is a factory that will accept the config. + auto& factory = Config::Utility::getAndCheckFactory(string_name); + auto message = Config::Utility::translateToFactoryConfig(listener_config, factory); + return factory.createListenerFactoryFromProto(*message, context); +} + Network::SocketSharedPtr ProdListenerComponentFactory::createListenSocket( Network::Address::InstanceConstSharedPtr address, Network::Address::SocketType socket_type, const Network::Socket::OptionsSharedPtr& options, bool bind_to_port) { @@ -190,6 +207,10 @@ ListenerImpl::ListenerImpl(const envoy::api::v2::Listener& config, const std::st listener_filter_factories_ = parent_.factory_.createListenerFilterFactoryList(config.listener_filters(), *this); } + if (config.has_quic_listener()) { + quic_listener_factory_ = + parent_.factory_.createQuicListenerFactory(config.quic_listener(), *this); + } // Add original dst listener filter if 'use_original_dst' flag is set. if (PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, use_original_dst, false)) { auto& factory = diff --git a/source/server/listener_manager_impl.h b/source/server/listener_manager_impl.h index 67fe15324b85c..c994b65482204 100644 --- a/source/server/listener_manager_impl.h +++ b/source/server/listener_manager_impl.h @@ -43,6 +43,12 @@ class ProdListenerComponentFactory : public ListenerComponentFactory, static std::vector createListenerFilterFactoryList_( const Protobuf::RepeatedPtrField& filters, Configuration::ListenerFactoryContext& context); + /** + * Static worker for createQuicListenerFactory() that can be used directly in tests. + */ + static Quic::QuicListenerFactoryPtr + createQuicListenerFactory_(const envoy::api::v2::listener::QuicListener& listener_config, + Configuration::ListenerFactoryContext& context); // Server::ListenerComponentFactory LdsApiPtr createLdsApi(const envoy::api::v2::core::ConfigSource& lds_config) override { @@ -60,6 +66,12 @@ class ProdListenerComponentFactory : public ListenerComponentFactory, Configuration::ListenerFactoryContext& context) override { return createListenerFilterFactoryList_(filters, context); } + Quic::QuicListenerFactoryPtr + createQuicListenerFactory(const envoy::api::v2::listener::QuicListener& listener_config, + Configuration::ListenerFactoryContext& context) override { + return createQuicListenerFactory_(listener_config, context); + } + Network::SocketSharedPtr createListenSocket(Network::Address::InstanceConstSharedPtr address, Network::Address::SocketType socket_type, const Network::Socket::OptionsSharedPtr& options, @@ -242,7 +254,7 @@ class ListenerImpl : public Network::ListenerConfig, // Network::ListenerConfig Network::FilterChainManager& filterChainManager() override { return *this; } Network::FilterChainFactory& filterChainFactory() override { return *this; } - Quic::QuicListenerFactory* quicListenerFactory() override { return nullptr; } + Quic::QuicListenerFactory* quicListenerFactory() override { return quic_listener_factory_.get(); } Network::Socket& socket() override { return *socket_; } const Network::Socket& socket() const override { return *socket_; } bool bindToPort() override { return bind_to_port_; } @@ -405,6 +417,7 @@ class ListenerImpl : public Network::ListenerConfig, InitManagerImpl dynamic_init_manager_; bool initialize_canceled_{}; std::vector listener_filter_factories_; + Quic::QuicListenerFactoryPtr quic_listener_factory_; DrainManagerPtr local_drain_manager_; bool saw_listener_create_failure_{}; const envoy::api::v2::Listener config_; diff --git a/test/mocks/server/mocks.h b/test/mocks/server/mocks.h index b2f8cdf4c8564..cc3d94d4587a6 100644 --- a/test/mocks/server/mocks.h +++ b/test/mocks/server/mocks.h @@ -220,6 +220,10 @@ class MockListenerComponentFactory : public ListenerComponentFactory { std::vector( const Protobuf::RepeatedPtrField&, Configuration::ListenerFactoryContext& context)); + MOCK_METHOD2(createQuicListenerFactory, + Quic::QuicListenerFactoryPtr( + const envoy::api::v2::listener::QuicListener&, + Configuration::ListenerFactoryContext& context)); MOCK_METHOD4(createListenSocket, Network::SocketSharedPtr(Network::Address::InstanceConstSharedPtr address, Network::Address::SocketType socket_type, diff --git a/test/server/listener_manager_impl_test.cc b/test/server/listener_manager_impl_test.cc index cc12cdd052e29..a25e362b69837 100644 --- a/test/server/listener_manager_impl_test.cc +++ b/test/server/listener_manager_impl_test.cc @@ -142,6 +142,13 @@ class ListenerManagerImplWithRealFiltersTest : public ListenerManagerImplTest { return ProdListenerComponentFactory::createListenerFilterFactoryList_(filters, context); })); + ON_CALL(listener_factory_, createQuicListenerFactory(_, _)) + .WillByDefault(Invoke( + [](const envoy::api::v2::listener::QuicListener& listener_config, + Configuration::ListenerFactoryContext& context) -> Quic::QuicListenerFactoryPtr { + return ProdListenerComponentFactory::createQuicListenerFactory_(listener_config, + context); + })); socket_ = std::make_unique>(); local_address_.reset(new Network::Address::Ipv4Instance("127.0.0.1", 1234)); remote_address_.reset(new Network::Address::Ipv4Instance("127.0.0.1", 1234)); From c2331dbc01465aae2845363afa9169bae19514da Mon Sep 17 00:00:00 2001 From: Michael Warres Date: Mon, 7 Jan 2019 08:20:29 -0500 Subject: [PATCH 5/7] Add envoy.quic_listeners.quiche extension. Signed-off-by: Michael Warres --- .../config/quic_listener/quiche/v2alpha/BUILD | 8 +++ .../quic_listener/quiche/v2alpha/quiche.proto | 15 +++++ source/extensions/extensions_build_config.bzl | 5 ++ source/extensions/quic_listeners/BUILD | 17 ++++++ source/extensions/quic_listeners/quiche/BUILD | 22 ++++++++ .../quic_listeners/quiche/config.cc | 56 +++++++++++++++++++ .../quic_listeners/well_known_names.h | 27 +++++++++ test/mocks/quic/BUILD | 18 ++++++ test/mocks/quic/mocks.h | 22 ++++++++ test/server/BUILD | 2 + test/server/listener_manager_impl_test.cc | 35 ++++++++++++ 11 files changed, 227 insertions(+) create mode 100644 api/envoy/config/quic_listener/quiche/v2alpha/BUILD create mode 100644 api/envoy/config/quic_listener/quiche/v2alpha/quiche.proto create mode 100644 source/extensions/quic_listeners/BUILD create mode 100644 source/extensions/quic_listeners/quiche/BUILD create mode 100644 source/extensions/quic_listeners/quiche/config.cc create mode 100644 source/extensions/quic_listeners/well_known_names.h create mode 100644 test/mocks/quic/BUILD create mode 100644 test/mocks/quic/mocks.h diff --git a/api/envoy/config/quic_listener/quiche/v2alpha/BUILD b/api/envoy/config/quic_listener/quiche/v2alpha/BUILD new file mode 100644 index 0000000000000..a44e5da512c5a --- /dev/null +++ b/api/envoy/config/quic_listener/quiche/v2alpha/BUILD @@ -0,0 +1,8 @@ +load("//bazel:api_build_system.bzl", "api_proto_library") + +licenses(["notice"]) # Apache 2 + +api_proto_library( + name = "quiche", + srcs = ["quiche.proto"], +) diff --git a/api/envoy/config/quic_listener/quiche/v2alpha/quiche.proto b/api/envoy/config/quic_listener/quiche/v2alpha/quiche.proto new file mode 100644 index 0000000000000..412a9f8eeab4c --- /dev/null +++ b/api/envoy/config/quic_listener/quiche/v2alpha/quiche.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package envoy.config.quic_listener.quiche.v2alpha; +option java_package = "io.envoyproxy.envoy.config.quic_listener.quiche.v2alpha"; +option java_multiple_files = true; +option go_package = "v2"; + +// [#protodoc-title: QUICHE] + +import "validate/validate.proto"; + +// Configuration for QUICHE-based QUIC listener. This will provide QUIC support to Envoy. +// https://quiche.googlesource.com/quiche/ +message Quiche { +} diff --git a/source/extensions/extensions_build_config.bzl b/source/extensions/extensions_build_config.bzl index 23d472d14ab22..1dadd33995ec7 100644 --- a/source/extensions/extensions_build_config.bzl +++ b/source/extensions/extensions_build_config.bzl @@ -76,6 +76,11 @@ EXTENSIONS = { "envoy.filters.network.thrift_proxy": "//source/extensions/filters/network/thrift_proxy:config", "envoy.filters.network.sni_cluster": "//source/extensions/filters/network/sni_cluster:config", + # + # QUIC listeners + # + "envoy.quic_listeners.quiche": "//source/extensions/quic_listeners/quiche:config", + # # Resource monitors # diff --git a/source/extensions/quic_listeners/BUILD b/source/extensions/quic_listeners/BUILD new file mode 100644 index 0000000000000..6156949edef64 --- /dev/null +++ b/source/extensions/quic_listeners/BUILD @@ -0,0 +1,17 @@ +licenses(["notice"]) # Apache 2 + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +envoy_package() + +envoy_cc_library( + name = "well_known_names", + hdrs = ["well_known_names.h"], + deps = [ + "//source/common/singleton:const_singleton", + ], +) diff --git a/source/extensions/quic_listeners/quiche/BUILD b/source/extensions/quic_listeners/quiche/BUILD new file mode 100644 index 0000000000000..2bb6cf9dd6861 --- /dev/null +++ b/source/extensions/quic_listeners/quiche/BUILD @@ -0,0 +1,22 @@ +licenses(["notice"]) # Apache 2 + +# QUICHE-based QUIC listener implementation + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_library", + "envoy_package", +) + +envoy_package() + +envoy_cc_library( + name = "config", + srcs = ["config.cc"], + deps = [ + "//include/envoy/quic:config_interface", + "//include/envoy/registry", + "//source/extensions/quic_listeners:well_known_names", + "@envoy_api//envoy/config/quic_listener/quiche/v2alpha:quiche_cc", + ], +) diff --git a/source/extensions/quic_listeners/quiche/config.cc b/source/extensions/quic_listeners/quiche/config.cc new file mode 100644 index 0000000000000..bc72d7117f441 --- /dev/null +++ b/source/extensions/quic_listeners/quiche/config.cc @@ -0,0 +1,56 @@ +#include +#include + +#include "envoy/config/quic_listener/quiche/v2alpha/quiche.pb.h" +#include "envoy/registry/registry.h" +#include "envoy/quic/config.h" + +#include "extensions/quic_listeners/well_known_names.h" + +namespace Envoy { +namespace Extensions { +namespace QuicListeners { +namespace Quiche { + +class QuicheListener : public Quic::QuicListener { +public: + void disable() override {} + void enable() override {} +}; + +class QuicheListenerFactory : public Quic::QuicListenerFactory { +public: + Quic::QuicListenerPtr createQuicListener(Quic::QuicListenerCallbacks&) override { + return std::make_unique(); + } +}; + +/** + * Config registration for the QUICHE QUIC listener. @see QuicListenerConfigFactory. + */ +class QuicheListenerConfigFactory : public Quic::QuicListenerConfigFactory { +public: + // QuicListenerConfigFactory + Quic::QuicListenerFactoryPtr + createListenerFactoryFromProto(const Protobuf::Message&, + Server::Configuration::ListenerFactoryContext&) override { + return std::make_unique(); + } + + ProtobufTypes::MessagePtr createEmptyConfigProto() override { + return std::make_unique(); + } + + std::string name() override { return QuicListenerNames::get().Quiche; } +}; + +/** + * Static registration for the QUICHE QUIC listener. @see RegisterFactory. + */ +static Registry::RegisterFactory + registered_; + +} // namespace Quiche +} // namespace QuicListeners +} // namespace Extensions +} // namespace Envoy diff --git a/source/extensions/quic_listeners/well_known_names.h b/source/extensions/quic_listeners/well_known_names.h new file mode 100644 index 0000000000000..b4b8edb72832c --- /dev/null +++ b/source/extensions/quic_listeners/well_known_names.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include "common/singleton/const_singleton.h" + +namespace Envoy { +namespace Extensions { +namespace QuicListeners { + +/** + * Well-known QUIC listener names. + * NOTE: New filters should use the well known name: envoy.filters.listener.name. + */ +class QuicListenerNameValues { +public: + // QUICHE-based implementation + const std::string Quiche = "envoy.quic_listeners.quiche"; + // ngtcp2-based implementation + // const std::string Ngtcp2 = "envoy.quic_listeners.ngtcp2"; +}; + +typedef ConstSingleton QuicListenerNames; + +} // namespace QuicListeners +} // namespace Extensions +} // namespace Envoy diff --git a/test/mocks/quic/BUILD b/test/mocks/quic/BUILD new file mode 100644 index 0000000000000..33c16087453ac --- /dev/null +++ b/test/mocks/quic/BUILD @@ -0,0 +1,18 @@ +licenses(["notice"]) # Apache 2 + +load( + "//bazel:envoy_build_system.bzl", + "envoy_cc_mock", + "envoy_package", +) + +envoy_package() + +envoy_cc_mock( + name = "quic_mocks", + hdrs = ["mocks.h"], + deps = [ + "//include/envoy/event:dispatcher_interface", + "//include/envoy/quic:listener_interface", + ], +) diff --git a/test/mocks/quic/mocks.h b/test/mocks/quic/mocks.h new file mode 100644 index 0000000000000..5bf461d1b8d50 --- /dev/null +++ b/test/mocks/quic/mocks.h @@ -0,0 +1,22 @@ +#pragma once + +#include "envoy/event/dispatcher.h" +#include "envoy/quic/listener.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Quic { + +class MockQuicListenerCallbacks : public QuicListenerCallbacks { +public: + MockQuicListenerCallbacks() {} + ~MockQuicListenerCallbacks() {} + + MOCK_METHOD0(socket, Network::Socket&()); + MOCK_METHOD0(dispatcher, Event::Dispatcher&()); +}; + +} // namespace Quic +} // namespace Envoy diff --git a/test/server/BUILD b/test/server/BUILD index 5054dea51c9f8..a307e0ada4c98 100644 --- a/test/server/BUILD +++ b/test/server/BUILD @@ -165,10 +165,12 @@ envoy_cc_test( "//source/extensions/filters/listener/original_dst:config", "//source/extensions/filters/listener/tls_inspector:config", "//source/extensions/filters/network/http_connection_manager:config", + "//source/extensions/quic_listeners/quiche:config", "//source/extensions/transport_sockets/raw_buffer:config", "//source/extensions/transport_sockets/ssl:config", "//source/server:listener_manager_lib", "//test/mocks/network:network_mocks", + "//test/mocks/quic:quic_mocks", "//test/mocks/server:server_mocks", "//test/test_common:environment_lib", "//test/test_common:registry_lib", diff --git a/test/server/listener_manager_impl_test.cc b/test/server/listener_manager_impl_test.cc index a25e362b69837..b2495e7dc073f 100644 --- a/test/server/listener_manager_impl_test.cc +++ b/test/server/listener_manager_impl_test.cc @@ -18,6 +18,7 @@ #include "extensions/filters/listener/original_dst/original_dst.h" +#include "test/mocks/quic/mocks.h" #include "test/mocks/network/mocks.h" #include "test/mocks/server/mocks.h" #include "test/server/utility.h" @@ -3000,5 +3001,39 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, VerifyIgnoreExpirationWithCA) { EXPECT_NO_THROW(manager_->addOrUpdateListener(parseListenerFromV2Yaml(yaml), "", true)); } +TEST_F(ListenerManagerImplWithRealFiltersTest, NoQuicListener) { + const std::string yaml = R"EOF( + name: "foo" + address: + socket_address: { protocol: UDP, address: 127.0.0.1, port_value: 1234 } + filter_chains: + - filters: + )EOF"; + + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, true)); + EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV2Yaml(yaml), "", true)); + EXPECT_EQ(nullptr, manager_->listeners().front().get().quicListenerFactory()); +} + +TEST_F(ListenerManagerImplWithRealFiltersTest, QuicListener) { + const std::string yaml = R"EOF( + name: "foo" + address: + socket_address: { protocol: UDP, address: 127.0.0.1, port_value: 1234 } + filter_chains: + - filters: + quic_listener: + name: "envoy.quic_listeners.quiche" + config: {} + )EOF"; + + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, true)); + EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromV2Yaml(yaml), "", true)); + auto factory = manager_->listeners().front().get().quicListenerFactory(); + EXPECT_NE(nullptr, factory); + Quic::MockQuicListenerCallbacks callbacks; + EXPECT_NE(nullptr, factory->createQuicListener(callbacks)); +} + } // namespace Server } // namespace Envoy From aa6188cbb5e6fc30766a85aaa3ea03aec58b8301 Mon Sep 17 00:00:00 2001 From: Michael Warres Date: Mon, 7 Jan 2019 16:37:07 -0500 Subject: [PATCH 6/7] Add HttpConnectionManager field to Quiche config proto. Signed-off-by: Michael Warres --- .../network/http_connection_manager/v2/BUILD | 1 + .../config/quic_listener/quiche/v2alpha/BUILD | 1 + .../quic_listener/quiche/v2alpha/quiche.proto | 14 +++++++++++++- test/server/listener_manager_impl_test.cc | 10 +++++++++- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/api/envoy/config/filter/network/http_connection_manager/v2/BUILD b/api/envoy/config/filter/network/http_connection_manager/v2/BUILD index c89ea09ad2909..fc403cdc43b6d 100644 --- a/api/envoy/config/filter/network/http_connection_manager/v2/BUILD +++ b/api/envoy/config/filter/network/http_connection_manager/v2/BUILD @@ -5,6 +5,7 @@ licenses(["notice"]) # Apache 2 api_proto_library_internal( name = "http_connection_manager", srcs = ["http_connection_manager.proto"], + visibility = ["//envoy/config/quic_listener/quiche/v2alpha:__pkg__"], deps = [ "//envoy/api/v2:rds", "//envoy/api/v2/core:base", diff --git a/api/envoy/config/quic_listener/quiche/v2alpha/BUILD b/api/envoy/config/quic_listener/quiche/v2alpha/BUILD index a44e5da512c5a..f3e946611b37b 100644 --- a/api/envoy/config/quic_listener/quiche/v2alpha/BUILD +++ b/api/envoy/config/quic_listener/quiche/v2alpha/BUILD @@ -5,4 +5,5 @@ licenses(["notice"]) # Apache 2 api_proto_library( name = "quiche", srcs = ["quiche.proto"], + deps = ["//envoy/config/filter/network/http_connection_manager/v2:http_connection_manager"], ) diff --git a/api/envoy/config/quic_listener/quiche/v2alpha/quiche.proto b/api/envoy/config/quic_listener/quiche/v2alpha/quiche.proto index 412a9f8eeab4c..42bc0e915b5bc 100644 --- a/api/envoy/config/quic_listener/quiche/v2alpha/quiche.proto +++ b/api/envoy/config/quic_listener/quiche/v2alpha/quiche.proto @@ -5,11 +5,23 @@ option java_package = "io.envoyproxy.envoy.config.quic_listener.quiche.v2alpha"; option java_multiple_files = true; option go_package = "v2"; -// [#protodoc-title: QUICHE] +import "envoy/config/filter/network/http_connection_manager/v2/http_connection_manager.proto"; import "validate/validate.proto"; +import "gogoproto/gogo.proto"; + +// [#protodoc-title: QUICHE] // Configuration for QUICHE-based QUIC listener. This will provide QUIC support to Envoy. // https://quiche.googlesource.com/quiche/ message Quiche { + + // HTTP-level configuration for the QUIC listener. In particular, HTTP filters to be applied to + // QUIC traffic should be specified in the nested :ref:`HttpConnectionManager + // ` config + // protobuf. + envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager http_config = 1 + [(validate.rules).message.required = true, (gogoproto.nullable) = false]; + + // QUIC-specific config will be added as fields here. } diff --git a/test/server/listener_manager_impl_test.cc b/test/server/listener_manager_impl_test.cc index b2495e7dc073f..c40bf0e2223f7 100644 --- a/test/server/listener_manager_impl_test.cc +++ b/test/server/listener_manager_impl_test.cc @@ -3024,7 +3024,15 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, QuicListener) { - filters: quic_listener: name: "envoy.quic_listeners.quiche" - config: {} + config: + http_config: + route_config: + virtual_hosts: + - name: "some_virtual_host" + domains: ["some.domain"] + routes: + - match: { prefix: "/" } + route: { cluster: service_foo } )EOF"; EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, true)); From 056f0cf34f4a7f898e630350a15a8adef85d7ab6 Mon Sep 17 00:00:00 2001 From: Michael Warres Date: Tue, 8 Jan 2019 23:57:28 -0500 Subject: [PATCH 7/7] Add validation of Quiche config proto. Signed-off-by: Michael Warres --- source/extensions/quic_listeners/quiche/BUILD | 1 + .../extensions/quic_listeners/quiche/config.cc | 10 ++++++++-- test/server/listener_manager_impl_test.cc | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/source/extensions/quic_listeners/quiche/BUILD b/source/extensions/quic_listeners/quiche/BUILD index 2bb6cf9dd6861..24362fa15ff23 100644 --- a/source/extensions/quic_listeners/quiche/BUILD +++ b/source/extensions/quic_listeners/quiche/BUILD @@ -16,6 +16,7 @@ envoy_cc_library( deps = [ "//include/envoy/quic:config_interface", "//include/envoy/registry", + "//source/common/protobuf:utility_lib", "//source/extensions/quic_listeners:well_known_names", "@envoy_api//envoy/config/quic_listener/quiche/v2alpha:quiche_cc", ], diff --git a/source/extensions/quic_listeners/quiche/config.cc b/source/extensions/quic_listeners/quiche/config.cc index bc72d7117f441..b3e33c4edcd9d 100644 --- a/source/extensions/quic_listeners/quiche/config.cc +++ b/source/extensions/quic_listeners/quiche/config.cc @@ -2,11 +2,16 @@ #include #include "envoy/config/quic_listener/quiche/v2alpha/quiche.pb.h" +#include "envoy/config/quic_listener/quiche/v2alpha/quiche.pb.validate.h" #include "envoy/registry/registry.h" #include "envoy/quic/config.h" +#include "common/protobuf/utility.h" + #include "extensions/quic_listeners/well_known_names.h" +using QuicheConfigProto = envoy::config::quic_listener::quiche::v2alpha::Quiche; + namespace Envoy { namespace Extensions { namespace QuicListeners { @@ -32,13 +37,14 @@ class QuicheListenerConfigFactory : public Quic::QuicListenerConfigFactory { public: // QuicListenerConfigFactory Quic::QuicListenerFactoryPtr - createListenerFactoryFromProto(const Protobuf::Message&, + createListenerFactoryFromProto(const Protobuf::Message& proto_config, Server::Configuration::ListenerFactoryContext&) override { + MessageUtil::downcastAndValidate(proto_config); return std::make_unique(); } ProtobufTypes::MessagePtr createEmptyConfigProto() override { - return std::make_unique(); + return std::make_unique(); } std::string name() override { return QuicListenerNames::get().Quiche; } diff --git a/test/server/listener_manager_impl_test.cc b/test/server/listener_manager_impl_test.cc index c40bf0e2223f7..4ba5d04c9cd41 100644 --- a/test/server/listener_manager_impl_test.cc +++ b/test/server/listener_manager_impl_test.cc @@ -3026,6 +3026,7 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, QuicListener) { name: "envoy.quic_listeners.quiche" config: http_config: + stat_prefix: "quiche" route_config: virtual_hosts: - name: "some_virtual_host" @@ -3043,5 +3044,21 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, QuicListener) { EXPECT_NE(nullptr, factory->createQuicListener(callbacks)); } +TEST_F(ListenerManagerImplWithRealFiltersTest, QuicListenerBadConfig) { + const std::string yaml = R"EOF( + name: "foo" + address: + socket_address: { protocol: UDP, address: 127.0.0.1, port_value: 1234 } + filter_chains: + - filters: + quic_listener: + name: "envoy.quic_listeners.quiche" + config: {} + )EOF"; + + EXPECT_THROW(manager_->addOrUpdateListener(parseListenerFromV2Yaml(yaml), "", true), + ProtoValidationException); +} + } // namespace Server } // namespace Envoy