From 1b58b6fddb2e4f55ec93aacf9e3e48f7a17be402 Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Wed, 14 Mar 2018 15:40:29 -0700 Subject: [PATCH] listener: Add support for LDS API "transparent" option. Insert a Listener Socket Option if "transparent" option is requested. Signed-off-by: Jarno Rajahalme --- bazel/repository_locations.bzl | 2 +- source/server/listener_manager_impl.cc | 36 +++++++++++++++++++++++ test/server/listener_manager_impl_test.cc | 20 +++++++++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index e74714f832f7..bfb26de56a3b 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -76,7 +76,7 @@ REPOSITORY_LOCATIONS = dict( urls = ["https://github.com/google/protobuf/archive/v3.5.0.tar.gz"], ), envoy_api = dict( - commit = "15dc537b6078988ac6f7de5ffec697e876a4652f", + commit = "4702452e358190a1444c7bb6dc256285de1f63f9", remote = "https://github.com/envoyproxy/data-plane-api", ), grpc_httpjson_transcoding = dict( diff --git a/source/server/listener_manager_impl.cc b/source/server/listener_manager_impl.cc index 3e68dcd2203d..86a6196ccf00 100644 --- a/source/server/listener_manager_impl.cc +++ b/source/server/listener_manager_impl.cc @@ -105,6 +105,36 @@ ProdListenerComponentFactory::createDrainManager(envoy::api::v2::Listener::Drain return DrainManagerPtr{new DrainManagerImpl(server_, drain_type)}; } +// Socket::Option implementation for API-defined listener socket options. +// This same object can be extended to handle additional listener socket options. +class ListenerSocketOption : public Network::Socket::Option, Logger::Loggable { +public: + ListenerSocketOption(bool transparent) : transparent_(transparent) {} + + // Network::Socket::Option + bool setOption(Network::Socket& socket, bool pre_bind) const override { + // IP_TRANSPARENT makes a difference only for the following bind(). Set it before the bind + // and ignore it after. + if (transparent_ && pre_bind) { +#ifdef SOL_IP + int on = 1; + if (setsockopt(socket.fd(), SOL_IP, IP_TRANSPARENT, &on, sizeof(on)) == -1) { + ENVOY_LOG(warn, "Setting IP_TRANSPARENT on listener socket failed: {}", strerror(errno)); + throw EnvoyException("ListenSocketOption: Error setting IP_TRANSPARENT socket option"); + } +#else + UNREFERENCED_PARAMETER(socket); + throw EnvoyException("ListenSocketOption: Error setting IP_TRANSPARENT socket option"); +#endif + } + return true; + } + void hashKey(std::vector&) const override{}; // Not used for listener sockets. + +private: + bool transparent_; +}; + ListenerImpl::ListenerImpl(const envoy::api::v2::Listener& config, ListenerManagerImpl& parent, const std::string& name, bool modifiable, bool workers_started, uint64_t hash) @@ -126,6 +156,12 @@ ListenerImpl::ListenerImpl(const envoy::api::v2::Listener& config, ListenerManag // filter chain #1308. ASSERT(config.filter_chains().size() >= 1); + // Add listen socket options, if any. + bool transparent = config.transparent(); + if (transparent) { + setListenSocketOption(std::make_shared(transparent)); + } + if (!config.listener_filters().empty()) { listener_filter_factories_ = parent_.factory_.createListenerFilterFactoryList(config.listener_filters(), *this); diff --git a/test/server/listener_manager_impl_test.cc b/test/server/listener_manager_impl_test.cc index 7cdef31188e9..fdf88d61fc7a 100644 --- a/test/server/listener_manager_impl_test.cc +++ b/test/server/listener_manager_impl_test.cc @@ -1391,6 +1391,26 @@ TEST_F(ListenerManagerImplWithRealFiltersTest, OriginalDstTestFilterOptionFail) EXPECT_EQ(0U, manager_->listeners().size()); } +TEST_F(ListenerManagerImplWithRealFiltersTest, TransparentListener) { + const std::string yaml = TestEnvironment::substitute(R"EOF( + name: "TransparentListener" + address: + socket_address: { address: 127.0.0.1, port_value: 1111 } + filter_chains: + - filters: + transparent: true + )EOF", + Network::Address::IpVersion::v4); + + EXPECT_CALL(listener_factory_, createListenSocket(_, _, true)); + + // MockListenerSocket is not a real socket, so this always fails in testing. + EXPECT_THROW_WITH_MESSAGE(manager_->addOrUpdateListener(parseListenerFromV2Yaml(yaml), true), + EnvoyException, + "ListenSocketOption: Error setting IP_TRANSPARENT socket option"); + EXPECT_EQ(0U, manager_->listeners().size()); +} + TEST_F(ListenerManagerImplWithRealFiltersTest, CRLFilename) { const std::string yaml = TestEnvironment::substitute(R"EOF( address: