diff --git a/api/envoy/admin/v2alpha/listeners.proto b/api/envoy/admin/v2alpha/listeners.proto index ee385de793c72..fa305c796aef1 100644 --- a/api/envoy/admin/v2alpha/listeners.proto +++ b/api/envoy/admin/v2alpha/listeners.proto @@ -26,7 +26,7 @@ message ListenerStatus { // Name of the listener string name = 1; - // The actual local address that the listener is listening on. If a listener was configured + // The actual local addresses that the listener is listening on. If a listener was configured // to listen on port 0, then this address has the port that was allocated by the OS. api.v2.core.Address local_address = 2; } diff --git a/api/envoy/admin/v3/listeners.proto b/api/envoy/admin/v3/listeners.proto index 86e35890bd67b..383706b6c2f14 100644 --- a/api/envoy/admin/v3/listeners.proto +++ b/api/envoy/admin/v3/listeners.proto @@ -31,7 +31,7 @@ message ListenerStatus { // Name of the listener string name = 1; - // The actual local address that the listener is listening on. If a listener was configured + // The actual local addresses that the listener is listening on. If a listener was configured // to listen on port 0, then this address has the port that was allocated by the OS. - config.core.v3.Address local_address = 2; + repeated config.core.v3.Address local_addresses = 2; } diff --git a/api/envoy/config/listener/v3/listener.proto b/api/envoy/config/listener/v3/listener.proto index d8982b0a97a7d..cce4b8609d707 100644 --- a/api/envoy/config/listener/v3/listener.proto +++ b/api/envoy/config/listener/v3/listener.proto @@ -38,7 +38,7 @@ message ListenerCollection { repeated xds.core.v3.CollectionEntry entries = 1; } -// [#next-free-field: 33] +// [#next-free-field: 34] message Listener { option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.Listener"; @@ -107,8 +107,15 @@ message Listener { // that is governed by the bind rules of the OS. E.g., multiple listeners can listen on port 0 on // Linux as the actual port will be allocated by the OS. // Required unless *api_listener* or *listener_specifier* is populated. + // TODO (soulxu): deprecated the `address` field after `addresses` implemented. core.v3.Address address = 2; + // A list of addresses that the listener should listen on. In general, the address must be unique, though + // that is governed by the bind rules of the OS. E.g., multiple listeners can listen on port 0 on + // Linux as the actual port will be allocated by the OS. For multiple addresses in single listener, + // all addresses are the same protocol, and multiple internal addresses don't support now. + repeated core.v3.Address addresses = 33; + // Optional prefix to use on listener stats. If empty, the stats will be rooted at // `listener.
.`. If non-empty, stats will be rooted at // `listener..`. diff --git a/envoy/network/connection_handler.h b/envoy/network/connection_handler.h index 1b5ff88200158..03e970ca4d1a5 100644 --- a/envoy/network/connection_handler.h +++ b/envoy/network/connection_handler.h @@ -13,6 +13,8 @@ #include "source/common/common/interval_value.h" +#include "address.h" + namespace Envoy { namespace Network { @@ -178,10 +180,12 @@ class TcpConnectionHandler : public virtual ConnectionHandler { /** * Obtain the rebalancer of the tcp listener. * @param listener_tag supplies the tag of the tcp listener that was passed to addListener(). + * @param address is used to query the address specific handler. * @return BalancedConnectionHandlerOptRef the balancer attached to the listener. `nullopt` if * listener doesn't exist or rebalancer doesn't exist. */ - virtual BalancedConnectionHandlerOptRef getBalancedHandlerByTag(uint64_t listener_tag) PURE; + virtual BalancedConnectionHandlerOptRef + getBalancedHandlerByTag(uint64_t listener_tag, const Network::Address::Instance& address) PURE; /** * Obtain the rebalancer of the tcp listener. @@ -199,11 +203,12 @@ class TcpConnectionHandler : public virtual ConnectionHandler { class UdpConnectionHandler : public virtual ConnectionHandler { public: /** - * Get the ``UdpListenerCallbacks`` associated with ``listener_tag``. This will be + * Get the ``UdpListenerCallbacks`` associated with ``listener_tag`` and ``address``. This will be * absl::nullopt for non-UDP listeners and for ``listener_tag`` values that have already been * removed. */ - virtual UdpListenerCallbacksOptRef getUdpListenerCallbacks(uint64_t listener_tag) PURE; + virtual UdpListenerCallbacksOptRef + getUdpListenerCallbacks(uint64_t listener_tag, const Network::Address::Instance& address) PURE; }; /** @@ -219,6 +224,7 @@ class ActiveUdpListenerFactory { * @param runtime the runtime for this server. * @param worker_index The index of the worker this listener is being created on. * @param parent is the owner of the created ActiveListener objects. + * @param listen_socket_ptr is the UDP socket. * @param dispatcher is used to create actual UDP listener. * @param config provides information needed to create ActiveUdpListener and * UdpListener objects. @@ -226,8 +232,9 @@ class ActiveUdpListenerFactory { */ virtual ConnectionHandler::ActiveUdpListenerPtr createActiveUdpListener(Runtime::Loader& runtime, uint32_t worker_index, - UdpConnectionHandler& parent, Event::Dispatcher& dispatcher, - Network::ListenerConfig& config) PURE; + UdpConnectionHandler& parent, + Network::SocketSharedPtr&& listen_socket_ptr, + Event::Dispatcher& dispatcher, Network::ListenerConfig& config) PURE; /** * @return true if the UDP passing through listener doesn't form stateful connections. diff --git a/envoy/network/listener.h b/envoy/network/listener.h index 2a4c3f97908ce..bf11bcd5e3403 100644 --- a/envoy/network/listener.h +++ b/envoy/network/listener.h @@ -20,6 +20,8 @@ #include "source/common/common/interval_value.h" +#include "address.h" + namespace Envoy { namespace Network { @@ -95,9 +97,11 @@ class UdpListenerConfig { virtual UdpPacketWriterFactory& packetWriterFactory() PURE; /** + * @param address is used to query the address specific router. * @return the UdpListenerWorkerRouter for this listener. */ - virtual UdpListenerWorkerRouter& listenerWorkerRouter() PURE; + virtual UdpListenerWorkerRouter& + listenerWorkerRouter(const Network::Address::Instance& address) PURE; /** * @return the configuration for the listener. @@ -148,14 +152,14 @@ class ListenerConfig { /** * @return ListenSocketFactory& the factory to create listen socket. */ - virtual ListenSocketFactory& listenSocketFactory() PURE; + virtual const std::vector& listenSocketFactories() PURE; /** * @return bool specifies whether the listener should actually listen on the port. * A listener that doesn't listen on a port can only receive connections * redirected from other listeners. */ - virtual bool bindToPort() PURE; + virtual bool bindToPort() const PURE; /** * @return bool if a connection should be handed off to another Listener after the original @@ -215,10 +219,11 @@ class ListenerConfig { virtual envoy::config::core::v3::TrafficDirection direction() const PURE; /** + * @param address is used for query the address specific connection balancer. * @return the connection balancer for this listener. All listeners have a connection balancer, * though the implementation may be a NOP balancer. */ - virtual ConnectionBalancer& connectionBalancer() PURE; + virtual ConnectionBalancer& connectionBalancer(const Network::Address::Instance& address) PURE; /** * Open connection resources for this listener. diff --git a/source/common/network/utility.h b/source/common/network/utility.h index 54cff85a81d17..9916e4a02e9a0 100644 --- a/source/common/network/utility.h +++ b/source/common/network/utility.h @@ -418,5 +418,14 @@ class Utility { static absl::uint128 flipOrder(const absl::uint128& input); }; +/** + * Log formatter for a list of addresses. + */ +struct AddressStrFormatter { + void operator()(std::string* out, const Network::Address::InstanceConstSharedPtr& instance) { + out->append(instance->asString()); + } +}; + } // namespace Network } // namespace Envoy diff --git a/source/common/quic/active_quic_listener.cc b/source/common/quic/active_quic_listener.cc index 17775ef19f663..8cf1db5699bdf 100644 --- a/source/common/quic/active_quic_listener.cc +++ b/source/common/quic/active_quic_listener.cc @@ -26,21 +26,7 @@ bool ActiveQuicListenerFactory::disable_kernel_bpf_packet_routing_for_test_ = fa ActiveQuicListener::ActiveQuicListener( Runtime::Loader& runtime, uint32_t worker_index, uint32_t concurrency, Event::Dispatcher& dispatcher, Network::UdpConnectionHandler& parent, - Network::ListenerConfig& listener_config, const quic::QuicConfig& quic_config, - bool kernel_worker_routing, const envoy::config::core::v3::RuntimeFeatureFlag& enabled, - QuicStatNames& quic_stat_names, uint32_t packets_received_to_connection_count_ratio, - EnvoyQuicCryptoServerStreamFactoryInterface& crypto_server_stream_factory, - EnvoyQuicProofSourceFactoryInterface& proof_source_factory) - : ActiveQuicListener(runtime, worker_index, concurrency, dispatcher, parent, - listener_config.listenSocketFactory().getListenSocket(worker_index), - listener_config, quic_config, kernel_worker_routing, enabled, - quic_stat_names, packets_received_to_connection_count_ratio, - crypto_server_stream_factory, proof_source_factory) {} - -ActiveQuicListener::ActiveQuicListener( - Runtime::Loader& runtime, uint32_t worker_index, uint32_t concurrency, - Event::Dispatcher& dispatcher, Network::UdpConnectionHandler& parent, - Network::SocketSharedPtr listen_socket, Network::ListenerConfig& listener_config, + Network::SocketSharedPtr&& listen_socket, Network::ListenerConfig& listener_config, const quic::QuicConfig& quic_config, bool kernel_worker_routing, const envoy::config::core::v3::RuntimeFeatureFlag& enabled, QuicStatNames& quic_stat_names, uint32_t packets_to_read_to_connection_count_ratio, @@ -338,11 +324,12 @@ ActiveQuicListenerFactory::ActiveQuicListenerFactory( Network::ConnectionHandler::ActiveUdpListenerPtr ActiveQuicListenerFactory::createActiveUdpListener( Runtime::Loader& runtime, uint32_t worker_index, Network::UdpConnectionHandler& parent, - Event::Dispatcher& disptacher, Network::ListenerConfig& config) { + Network::SocketSharedPtr&& listen_socket_ptr, Event::Dispatcher& disptacher, + Network::ListenerConfig& config) { ASSERT(crypto_server_stream_factory_.has_value()); return std::make_unique( - runtime, worker_index, concurrency_, disptacher, parent, config, quic_config_, - kernel_worker_routing_, enabled_, quic_stat_names_, + runtime, worker_index, concurrency_, disptacher, parent, std::move(listen_socket_ptr), config, + quic_config_, kernel_worker_routing_, enabled_, quic_stat_names_, packets_to_read_to_connection_count_ratio_, crypto_server_stream_factory_.value(), proof_source_factory_.value()); } diff --git a/source/common/quic/active_quic_listener.h b/source/common/quic/active_quic_listener.h index 6feda1abc7c22..b8a1b3a58d118 100644 --- a/source/common/quic/active_quic_listener.h +++ b/source/common/quic/active_quic_listener.h @@ -30,17 +30,7 @@ class ActiveQuicListener : public Envoy::Server::ActiveUdpListenerBase, ActiveQuicListener(Runtime::Loader& runtime, uint32_t worker_index, uint32_t concurrency, Event::Dispatcher& dispatcher, Network::UdpConnectionHandler& parent, - Network::ListenerConfig& listener_config, const quic::QuicConfig& quic_config, - bool kernel_worker_routing, - const envoy::config::core::v3::RuntimeFeatureFlag& enabled, - QuicStatNames& quic_stat_names, - uint32_t packets_to_read_to_connection_count_ratio, - EnvoyQuicCryptoServerStreamFactoryInterface& crypto_server_stream_factory, - EnvoyQuicProofSourceFactoryInterface& proof_source_factory); - - ActiveQuicListener(Runtime::Loader& runtime, uint32_t worker_index, uint32_t concurrency, - Event::Dispatcher& dispatcher, Network::UdpConnectionHandler& parent, - Network::SocketSharedPtr listen_socket, + Network::SocketSharedPtr&& listen_socket, Network::ListenerConfig& listener_config, const quic::QuicConfig& quic_config, bool kernel_worker_routing, const envoy::config::core::v3::RuntimeFeatureFlag& enabled, @@ -108,8 +98,9 @@ class ActiveQuicListenerFactory : public Network::ActiveUdpListenerFactory, // Network::ActiveUdpListenerFactory. Network::ConnectionHandler::ActiveUdpListenerPtr createActiveUdpListener(Runtime::Loader& runtime, uint32_t worker_index, - Network::UdpConnectionHandler& parent, Event::Dispatcher& disptacher, - Network::ListenerConfig& config) override; + Network::UdpConnectionHandler& parent, + Network::SocketSharedPtr&& listen_socket_ptr, + Event::Dispatcher& disptacher, Network::ListenerConfig& config) override; bool isTransportConnectionless() const override { return false; } const Network::Socket::OptionsSharedPtr& socketOptions() const override { return options_; } diff --git a/source/server/active_raw_udp_listener_config.cc b/source/server/active_raw_udp_listener_config.cc index e149dadf00716..dca62508ed01e 100644 --- a/source/server/active_raw_udp_listener_config.cc +++ b/source/server/active_raw_udp_listener_config.cc @@ -15,10 +15,11 @@ ActiveRawUdpListenerFactory::ActiveRawUdpListenerFactory(uint32_t concurrency) Network::ConnectionHandler::ActiveUdpListenerPtr ActiveRawUdpListenerFactory::createActiveUdpListener(Runtime::Loader&, uint32_t worker_index, Network::UdpConnectionHandler& parent, + Network::SocketSharedPtr&& listen_socket_ptr, Event::Dispatcher& dispatcher, Network::ListenerConfig& config) { - return std::make_unique(worker_index, concurrency_, parent, dispatcher, - config); + return std::make_unique(worker_index, concurrency_, parent, + std::move(listen_socket_ptr), dispatcher, config); } } // namespace Server diff --git a/source/server/active_raw_udp_listener_config.h b/source/server/active_raw_udp_listener_config.h index 4b79d9ed6d4f5..a94e31e0c1fe3 100644 --- a/source/server/active_raw_udp_listener_config.h +++ b/source/server/active_raw_udp_listener_config.h @@ -11,8 +11,9 @@ class ActiveRawUdpListenerFactory : public Network::ActiveUdpListenerFactory { Network::ConnectionHandler::ActiveUdpListenerPtr createActiveUdpListener(Runtime::Loader&, uint32_t worker_index, - Network::UdpConnectionHandler& parent, Event::Dispatcher& disptacher, - Network::ListenerConfig& config) override; + Network::UdpConnectionHandler& parent, + Network::SocketSharedPtr&& listen_socket_ptr, + Event::Dispatcher& disptacher, Network::ListenerConfig& config) override; bool isTransportConnectionless() const override { return true; } const Network::Socket::OptionsSharedPtr& socketOptions() const override { return options_; } diff --git a/source/server/active_tcp_listener.cc b/source/server/active_tcp_listener.cc index 78b435c0aad62..1542730837d9e 100644 --- a/source/server/active_tcp_listener.cc +++ b/source/server/active_tcp_listener.cc @@ -14,28 +14,32 @@ namespace Server { ActiveTcpListener::ActiveTcpListener(Network::TcpConnectionHandler& parent, Network::ListenerConfig& config, Runtime::Loader& runtime, - uint32_t worker_index) - : OwnedActiveStreamListenerBase(parent, parent.dispatcher(), - parent.dispatcher().createListener( - config.listenSocketFactory().getListenSocket(worker_index), - *this, runtime, config.bindToPort(), - config.ignoreGlobalConnLimit()), - config), - tcp_conn_handler_(parent) { - config.connectionBalancer().registerHandler(*this); + Network::SocketSharedPtr&& socket, + Network::Address::InstanceConstSharedPtr& address, + Network::ConnectionBalancer& connection_balancer) + : OwnedActiveStreamListenerBase( + parent, parent.dispatcher(), + parent.dispatcher().createListener(std::move(socket), *this, runtime, config.bindToPort(), + config.ignoreGlobalConnLimit()), + config), + tcp_conn_handler_(parent), connection_balancer_(connection_balancer), address_(address) { + connection_balancer_.registerHandler(*this); } ActiveTcpListener::ActiveTcpListener(Network::TcpConnectionHandler& parent, Network::ListenerPtr&& listener, - Network::ListenerConfig& config, Runtime::Loader&) + Network::Address::InstanceConstSharedPtr& address, + Network::ListenerConfig& config, + Network::ConnectionBalancer& connection_balancer, + Runtime::Loader&) : OwnedActiveStreamListenerBase(parent, parent.dispatcher(), std::move(listener), config), - tcp_conn_handler_(parent) { - config.connectionBalancer().registerHandler(*this); + tcp_conn_handler_(parent), connection_balancer_(connection_balancer), address_(address) { + connection_balancer_.registerHandler(*this); } ActiveTcpListener::~ActiveTcpListener() { is_deleting_ = true; - config_->connectionBalancer().unregisterHandler(*this); + connection_balancer_.unregisterHandler(*this); // Purge sockets that have not progressed to connections. This should only happen when // a listener filter stops iteration and never resumes. @@ -65,7 +69,6 @@ ActiveTcpListener::~ActiveTcpListener() { void ActiveTcpListener::updateListenerConfig(Network::ListenerConfig& config) { ENVOY_LOG(trace, "replacing listener ", config_->listenerTag(), " by ", config.listenerTag()); - ASSERT(&config_->connectionBalancer() == &config.connectionBalancer()); config_ = &config; } @@ -98,7 +101,7 @@ void ActiveTcpListener::onAcceptWorker(Network::ConnectionSocketPtr&& socket, bool rebalanced) { if (!rebalanced) { Network::BalancedConnectionHandler& target_handler = - config_->connectionBalancer().pickTargetHandler(*this); + connection_balancer_.pickTargetHandler(*this); if (&target_handler != this) { target_handler.post(std::move(socket)); return; @@ -153,10 +156,11 @@ void ActiveTcpListener::post(Network::ConnectionSocketPtr&& socket) { RebalancedSocketSharedPtr socket_to_rebalance = std::make_shared(); socket_to_rebalance->socket = std::move(socket); - dispatcher().post([socket_to_rebalance, tag = config_->listenerTag(), + dispatcher().post([socket_to_rebalance, address = address_, tag = config_->listenerTag(), &tcp_conn_handler = tcp_conn_handler_, handoff = config_->handOffRestoredDestinationConnections()]() { - auto balanced_handler = tcp_conn_handler.getBalancedHandlerByTag(tag); + // `getBalancedHandlerByAddress` only support the IP address but there can be unix socket also. + auto balanced_handler = tcp_conn_handler.getBalancedHandlerByTag(tag, *address); if (balanced_handler.has_value()) { balanced_handler->get().onAcceptWorker(std::move(socket_to_rebalance->socket), handoff, true); return; diff --git a/source/server/active_tcp_listener.h b/source/server/active_tcp_listener.h index ba3fc15fbdb63..f0a0ae49bd4a9 100644 --- a/source/server/active_tcp_listener.h +++ b/source/server/active_tcp_listener.h @@ -27,9 +27,13 @@ class ActiveTcpListener final : public Network::TcpListenerCallbacks, public Network::BalancedConnectionHandler { public: ActiveTcpListener(Network::TcpConnectionHandler& parent, Network::ListenerConfig& config, - Runtime::Loader& runtime, uint32_t worker_index); + Runtime::Loader& runtime, Network::SocketSharedPtr&& socket, + Network::Address::InstanceConstSharedPtr& address, + Network::ConnectionBalancer& connection_balancer); ActiveTcpListener(Network::TcpConnectionHandler& parent, Network::ListenerPtr&& listener, - Network::ListenerConfig& config, Runtime::Loader& runtime); + Network::Address::InstanceConstSharedPtr& address, + Network::ListenerConfig& config, + Network::ConnectionBalancer& connection_balancer, Runtime::Loader& runtime); ~ActiveTcpListener() override; bool listenerConnectionLimitReached() const { @@ -81,6 +85,9 @@ class ActiveTcpListener final : public Network::TcpListenerCallbacks, // The number of connections currently active on this listener. This is typically used for // connection balancing across per-handler listeners. std::atomic num_listener_connections_{}; + + Network::ConnectionBalancer& connection_balancer_; + Network::Address::InstanceConstSharedPtr address_; }; using ActiveTcpListenerOptRef = absl::optional>; diff --git a/source/server/active_udp_listener.cc b/source/server/active_udp_listener.cc index e0d4c64f73f89..ed5195be40055 100644 --- a/source/server/active_udp_listener.cc +++ b/source/server/active_udp_listener.cc @@ -18,13 +18,15 @@ ActiveUdpListenerBase::ActiveUdpListenerBase(uint32_t worker_index, uint32_t con : ActiveListenerImplBase(parent, config), worker_index_(worker_index), concurrency_(concurrency), parent_(parent), listen_socket_(listen_socket), udp_listener_(std::move(listener)), - udp_stats_({ALL_UDP_LISTENER_STATS(POOL_COUNTER_PREFIX(config->listenerScope(), "udp"))}) { + udp_stats_({ALL_UDP_LISTENER_STATS(POOL_COUNTER_PREFIX(config->listenerScope(), "udp"))}), + udp_listener_worker_router_(config_->udpListenerConfig()->listenerWorkerRouter( + *listen_socket.connectionInfoProvider().localAddress())) { ASSERT(worker_index_ < concurrency_); - config_->udpListenerConfig()->listenerWorkerRouter().registerWorkerForListener(*this); + udp_listener_worker_router_.registerWorkerForListener(*this); } ActiveUdpListenerBase::~ActiveUdpListenerBase() { - config_->udpListenerConfig()->listenerWorkerRouter().unregisterWorkerForListener(*this); + udp_listener_worker_router_.unregisterWorkerForListener(*this); } void ActiveUdpListenerBase::post(Network::UdpRecvData&& data) { @@ -38,13 +40,14 @@ void ActiveUdpListenerBase::post(Network::UdpRecvData&& data) { auto data_to_post = std::make_shared(); *data_to_post = std::move(data); - udp_listener_->dispatcher().post( - [data_to_post, tag = config_->listenerTag(), &parent = parent_]() { - Network::UdpListenerCallbacksOptRef listener = parent.getUdpListenerCallbacks(tag); - if (listener.has_value()) { - listener->get().onDataWorker(std::move(*data_to_post)); - } - }); + auto address = listen_socket_.connectionInfoProvider().localAddress(); + udp_listener_->dispatcher().post([data_to_post, tag = config_->listenerTag(), &parent = parent_, + address]() { + Network::UdpListenerCallbacksOptRef listener = parent.getUdpListenerCallbacks(tag, *address); + if (listener.has_value()) { + listener->get().onDataWorker(std::move(*data_to_post)); + } + }); } void ActiveUdpListenerBase::onData(Network::UdpRecvData&& data) { @@ -59,18 +62,10 @@ void ActiveUdpListenerBase::onData(Network::UdpRecvData&& data) { if (dest == worker_index_) { onDataWorker(std::move(data)); } else { - config_->udpListenerConfig()->listenerWorkerRouter().deliver(dest, std::move(data)); + udp_listener_worker_router_.deliver(dest, std::move(data)); } } -ActiveRawUdpListener::ActiveRawUdpListener(uint32_t worker_index, uint32_t concurrency, - Network::UdpConnectionHandler& parent, - Event::Dispatcher& dispatcher, - Network::ListenerConfig& config) - : ActiveRawUdpListener(worker_index, concurrency, parent, - config.listenSocketFactory().getListenSocket(worker_index), dispatcher, - config) {} - ActiveRawUdpListener::ActiveRawUdpListener(uint32_t worker_index, uint32_t concurrency, Network::UdpConnectionHandler& parent, Network::SocketSharedPtr listen_socket_ptr, diff --git a/source/server/active_udp_listener.h b/source/server/active_udp_listener.h index dff9406c44366..9691d66825d6a 100644 --- a/source/server/active_udp_listener.h +++ b/source/server/active_udp_listener.h @@ -55,6 +55,7 @@ class ActiveUdpListenerBase : public ActiveListenerImplBase, Network::Socket& listen_socket_; Network::UdpListenerPtr udp_listener_; UdpListenerStats udp_stats_; + Network::UdpListenerWorkerRouter& udp_listener_worker_router_; }; /** @@ -65,9 +66,6 @@ class ActiveRawUdpListener : public ActiveUdpListenerBase, public Network::UdpReadFilterCallbacks, Logger::Loggable { public: - ActiveRawUdpListener(uint32_t worker_index, uint32_t concurrency, - Network::UdpConnectionHandler& parent, Event::Dispatcher& dispatcher, - Network::ListenerConfig& config); ActiveRawUdpListener(uint32_t worker_index, uint32_t concurrency, Network::UdpConnectionHandler& parent, Network::SocketSharedPtr listen_socket_ptr, Event::Dispatcher& dispatcher, diff --git a/source/server/admin/admin.cc b/source/server/admin/admin.cc index 629b8fcda0171..9efe33c00a191 100644 --- a/source/server/admin/admin.cc +++ b/source/server/admin/admin.cc @@ -128,7 +128,7 @@ void AdminImpl::startHttpListener(const std::list& socket_ = std::make_shared(address, socket_options, true); RELEASE_ASSERT(0 == socket_->ioHandle().listen(ENVOY_TCP_BACKLOG_SIZE).return_value_, "listen() failed on admin listener"); - socket_factory_ = std::make_unique(socket_); + socket_factories_.emplace_back(std::make_unique(socket_)); listener_ = std::make_unique(*this, std::move(listener_scope)); ENVOY_LOG(info, "admin address: {}", socket().connectionInfoProvider().localAddress()->asString()); diff --git a/source/server/admin/admin.h b/source/server/admin/admin.h index 151d10ba6eee9..b09df29494840 100644 --- a/source/server/admin/admin.h +++ b/source/server/admin/admin.h @@ -389,10 +389,10 @@ class AdminImpl : public Admin, // Network::ListenerConfig Network::FilterChainManager& filterChainManager() override { return parent_; } Network::FilterChainFactory& filterChainFactory() override { return parent_; } - Network::ListenSocketFactory& listenSocketFactory() override { - return *parent_.socket_factory_; + const std::vector& listenSocketFactories() override { + return parent_.socket_factories_; } - bool bindToPort() override { return true; } + bool bindToPort() const override { return true; } bool handOffRestoredDestinationConnections() const override { return false; } uint32_t perConnectionBufferLimitBytes() const override { return 0; } std::chrono::milliseconds listenerFiltersTimeout() const override { return {}; } @@ -409,7 +409,9 @@ class AdminImpl : public Admin, envoy::config::core::v3::TrafficDirection direction() const override { return envoy::config::core::v3::UNSPECIFIED; } - Network::ConnectionBalancer& connectionBalancer() override { return connection_balancer_; } + Network::ConnectionBalancer& connectionBalancer(const Network::Address::Instance&) override { + return connection_balancer_; + } ResourceLimit& openConnections() override { return open_connections_; } const std::vector& accessLogs() const override { return empty_access_logs_; @@ -495,7 +497,7 @@ class AdminImpl : public Admin, ConfigTrackerImpl config_tracker_; const Network::FilterChainSharedPtr admin_filter_chain_; Network::SocketSharedPtr socket_; - Network::ListenSocketFactoryPtr socket_factory_; + std::vector socket_factories_; AdminListenerPtr listener_; const AdminInternalAddressConfig internal_address_config_; const LocalReply::LocalReplyPtr local_reply_; diff --git a/source/server/admin/listeners_handler.cc b/source/server/admin/listeners_handler.cc index 9e6ddbbc746bc..e588132be8144 100644 --- a/source/server/admin/listeners_handler.cc +++ b/source/server/admin/listeners_handler.cc @@ -57,16 +57,20 @@ void ListenersHandler::writeListenersAsJson(Buffer::Instance& response) { for (const auto& listener : server_.listenerManager().listeners()) { envoy::admin::v3::ListenerStatus& listener_status = *listeners.add_listener_statuses(); listener_status.set_name(listener.get().name()); - Network::Utility::addressToProtobufAddress(*listener.get().listenSocketFactory().localAddress(), - *listener_status.mutable_local_address()); + for (auto& socket_factory : listener.get().listenSocketFactories()) { + auto address = listener_status.add_local_addresses(); + Network::Utility::addressToProtobufAddress(*socket_factory->localAddress(), *address); + } } response.add(MessageUtil::getJsonStringFromMessageOrError(listeners, true)); // pretty-print } void ListenersHandler::writeListenersAsText(Buffer::Instance& response) { for (const auto& listener : server_.listenerManager().listeners()) { - response.add(fmt::format("{}::{}\n", listener.get().name(), - listener.get().listenSocketFactory().localAddress()->asString())); + for (auto& socket_factory : listener.get().listenSocketFactories()) { + response.add(fmt::format("{}::{}\n", listener.get().name(), + socket_factory->localAddress()->asString())); + } } } diff --git a/source/server/connection_handler_impl.cc b/source/server/connection_handler_impl.cc index 94699da0ed53a..448d2cd3eab28 100644 --- a/source/server/connection_handler_impl.cc +++ b/source/server/connection_handler_impl.cc @@ -36,12 +36,16 @@ void ConnectionHandlerImpl::addListener(absl::optional overridden_list ActiveListenerDetailsOptRef listener_detail = findActiveListenerByTag(overridden_listener.value()); ASSERT(listener_detail.has_value()); - listener_detail->get().listener_->updateListenerConfig(config); + listener_detail->get().invokeListenerMethod( + [&config](Network::ConnectionHandler::ActiveListener& listener) { + listener.updateListenerConfig(config); + }); return; } - auto details = std::make_shared(); + auto details = std::make_unique(); if (config.internalListenerConfig().has_value()) { + auto pre_address_details = std::make_shared(); // Ensure the this ConnectionHandlerImpl link to the thread local registry. Ideally this step // should be done only once. However, an extra phase and interface is overkill. Network::InternalListenerRegistry& internal_listener_registry = @@ -53,90 +57,99 @@ void ConnectionHandlerImpl::addListener(absl::optional overridden_list if (overridden_listener.has_value()) { if (auto iter = listener_map_by_tag_.find(overridden_listener.value()); iter != listener_map_by_tag_.end()) { - iter->second->internalListener()->get().updateListenerConfig(config); + iter->second->invokeListenerMethod( + [&config](Network::ConnectionHandler::ActiveListener& listener) { + listener.updateListenerConfig(config); + }); return; } IS_ENVOY_BUG("unexpected"); } auto internal_listener = std::make_unique(*this, dispatcher(), config); - details->typed_listener_ = *internal_listener; - details->listener_ = std::move(internal_listener); - } else if (config.listenSocketFactory().socketType() == Network::Socket::Type::Stream) { + // The internal address doesn't support multiple addresses. + ASSERT(config.listenSocketFactories().size() == 1); + details->addActiveListener(config, config.listenSocketFactories()[0]->localAddress(), + listener_reject_fraction_, disable_listeners_, + std::move(internal_listener)); + } else if (config.listenSocketFactories()[0]->socketType() == Network::Socket::Type::Stream) { if (!support_udp_in_place_filter_chain_update && overridden_listener.has_value()) { if (auto iter = listener_map_by_tag_.find(overridden_listener.value()); iter != listener_map_by_tag_.end()) { - iter->second->tcpListener()->get().updateListenerConfig(config); + iter->second->invokeListenerMethod( + [&config](Network::ConnectionHandler::ActiveListener& listener) { + listener.updateListenerConfig(config); + }); return; } IS_ENVOY_BUG("unexpected"); } - // worker_index_ doesn't have a value on the main thread for the admin server. - auto tcp_listener = std::make_unique( - *this, config, runtime, worker_index_.has_value() ? *worker_index_ : 0); - details->typed_listener_ = *tcp_listener; - details->listener_ = std::move(tcp_listener); + for (auto& socket_factory : config.listenSocketFactories()) { + auto address = socket_factory->localAddress(); + // worker_index_ doesn't have a value on the main thread for the admin server. + details->addActiveListener( + config, address, listener_reject_fraction_, disable_listeners_, + std::make_unique( + *this, config, runtime, + socket_factory->getListenSocket(worker_index_.has_value() ? *worker_index_ : 0), + address, config.connectionBalancer(*address))); + } } else { ASSERT(config.udpListenerConfig().has_value(), "UDP listener factory is not initialized."); ASSERT(worker_index_.has_value()); - ConnectionHandler::ActiveUdpListenerPtr udp_listener = - config.udpListenerConfig()->listenerFactory().createActiveUdpListener( - runtime, *worker_index_, *this, dispatcher_, config); - details->typed_listener_ = *udp_listener; - details->listener_ = std::move(udp_listener); - } - - if (disable_listeners_) { - details->listener_->pauseListening(); - } - if (auto* listener = details->listener_->listener(); listener != nullptr) { - listener->setRejectFraction(listener_reject_fraction_); + for (auto& socket_factory : config.listenSocketFactories()) { + auto address = socket_factory->localAddress(); + details->addActiveListener( + config, address, listener_reject_fraction_, disable_listeners_, + config.udpListenerConfig()->listenerFactory().createActiveUdpListener( + runtime, *worker_index_, *this, socket_factory->getListenSocket(*worker_index_), + dispatcher_, config)); + } } - details->listener_tag_ = config.listenerTag(); - details->address_ = config.listenSocketFactory().localAddress(); - ASSERT(!listener_map_by_tag_.contains(config.listenerTag())); - listener_map_by_tag_.emplace(config.listenerTag(), details); - // This map only store the new listener. - if (absl::holds_alternative>( - details->typed_listener_)) { - tcp_listener_map_by_address_.insert_or_assign( - config.listenSocketFactory().localAddress()->asStringView(), details); - - auto& address = details->address_; - // If the address is Ipv6 and isn't v6only, parse out the ipv4 compatible address from the Ipv6 - // address and put an item to the map. Then this allows the `getBalancedHandlerByAddress` - // can match the Ipv4 request to Ipv4-mapped address also. - if (address->type() == Network::Address::Type::Ip && - address->ip()->version() == Network::Address::IpVersion::v6 && - !address->ip()->ipv6()->v6only()) { - if (address->ip()->isAnyAddress()) { - // Since both "::" with ipv4_compat and "0.0.0.0" can be supported. - // If there already one and it isn't shutdown for compatible addr, - // then won't insert a new one. - auto ipv4_any_address = Network::Address::Ipv4Instance(address->ip()->port()).asString(); - auto ipv4_any_listener = tcp_listener_map_by_address_.find(ipv4_any_address); - if (ipv4_any_listener == tcp_listener_map_by_address_.end() || - ipv4_any_listener->second->listener_->listener() == nullptr) { - tcp_listener_map_by_address_.insert_or_assign(ipv4_any_address, details); - } - } else { - auto v4_compatible_addr = address->ip()->ipv6()->v4CompatibleAddress(); - // Remove this check when runtime flag - // `envoy.reloadable_features.strict_check_on_ipv4_compat` deprecated. - // If this isn't a valid Ipv4-mapped address, then do nothing. - if (v4_compatible_addr != nullptr) { - tcp_listener_map_by_address_.insert_or_assign(v4_compatible_addr->asStringView(), - details); + for (auto& per_address_details : details->per_address_details_) { + // This map only store the new listener. + if (absl::holds_alternative>( + per_address_details->typed_listener_)) { + tcp_listener_map_by_address_.insert_or_assign(per_address_details->address_->asStringView(), + per_address_details); + + auto& address = per_address_details->address_; + // If the address is Ipv6 and isn't v6only, parse out the ipv4 compatible address from the + // Ipv6 address and put an item to the map. Then this allows the `getBalancedHandlerByAddress` + // can match the Ipv4 request to Ipv4-mapped address also. + if (address->type() == Network::Address::Type::Ip && + address->ip()->version() == Network::Address::IpVersion::v6 && + !address->ip()->ipv6()->v6only()) { + if (address->ip()->isAnyAddress()) { + // Since both "::" with ipv4_compat and "0.0.0.0" can be supported. + // If there already one and it isn't shutdown for compatible addr, + // then won't insert a new one. + auto ipv4_any_address = Network::Address::Ipv4Instance(address->ip()->port()).asString(); + auto ipv4_any_listener = tcp_listener_map_by_address_.find(ipv4_any_address); + if (ipv4_any_listener == tcp_listener_map_by_address_.end() || + ipv4_any_listener->second->listener_->listener() == nullptr) { + tcp_listener_map_by_address_.insert_or_assign(ipv4_any_address, per_address_details); + } + } else { + auto v4_compatible_addr = address->ip()->ipv6()->v4CompatibleAddress(); + // Remove this check when runtime flag + // `envoy.reloadable_features.strict_check_on_ipv4_compat` deprecated. + // If this isn't a valid Ipv4-mapped address, then do nothing. + if (v4_compatible_addr != nullptr) { + tcp_listener_map_by_address_.insert_or_assign(v4_compatible_addr->asStringView(), + per_address_details); + } } } + } else if (absl::holds_alternative>( + per_address_details->typed_listener_)) { + internal_listener_map_by_address_.insert_or_assign( + per_address_details->address_->asStringView(), per_address_details); } - } else if (absl::holds_alternative>( - details->typed_listener_)) { - internal_listener_map_by_address_.insert_or_assign( - config.listenSocketFactory().localAddress()->asStringView(), details); } + listener_map_by_tag_.emplace(config.listenerTag(), std::move(details)); } void ConnectionHandlerImpl::removeListeners(uint64_t listener_tag) { @@ -144,54 +157,61 @@ void ConnectionHandlerImpl::removeListeners(uint64_t listener_tag) { listener_iter != listener_map_by_tag_.end()) { // listener_map_by_address_ may already update to the new listener. Compare it with the one // which find from listener_map_by_tag_, only delete it when it is same listener. - auto& address = listener_iter->second->address_; - auto address_view = address->asStringView(); - if (tcp_listener_map_by_address_.contains(address_view) && - tcp_listener_map_by_address_[address_view]->listener_tag_ == - listener_iter->second->listener_tag_) { - tcp_listener_map_by_address_.erase(address_view); - - // If the address is Ipv6 and isn't v6only, delete the corresponding Ipv4 item from the map. - if (address->type() == Network::Address::Type::Ip && - address->ip()->version() == Network::Address::IpVersion::v6 && - !address->ip()->ipv6()->v6only()) { - if (address->ip()->isAnyAddress()) { - auto ipv4_any_addr_iter = tcp_listener_map_by_address_.find( - Network::Address::Ipv4Instance(address->ip()->port()).asStringView()); - // Since both "::" with ipv4_compat and "0.0.0.0" can be supported, ensure they are same - // listener by tag. - if (ipv4_any_addr_iter != tcp_listener_map_by_address_.end() && - ipv4_any_addr_iter->second->listener_tag_ == listener_iter->second->listener_tag_) { - tcp_listener_map_by_address_.erase(ipv4_any_addr_iter); - } - } else { - auto v4_compatible_addr = address->ip()->ipv6()->v4CompatibleAddress(); - // Remove this check when runtime flag - // `envoy.reloadable_features.strict_check_on_ipv4_compat` deprecated. - if (v4_compatible_addr != nullptr) { - // both "::FFFF:" with ipv4_compat and "" isn't valid case, - // remove the v4 compatible addr item directly. - tcp_listener_map_by_address_.erase(v4_compatible_addr->asStringView()); + for (auto& per_address_details : listener_iter->second->per_address_details_) { + auto& address = per_address_details->address_; + auto address_view = address->asStringView(); + if (tcp_listener_map_by_address_.contains(address_view) && + tcp_listener_map_by_address_[address_view]->listener_tag_ == + per_address_details->listener_tag_) { + tcp_listener_map_by_address_.erase(address_view); + + // If the address is Ipv6 and isn't v6only, delete the corresponding Ipv4 item from the map. + if (address->type() == Network::Address::Type::Ip && + address->ip()->version() == Network::Address::IpVersion::v6 && + !address->ip()->ipv6()->v6only()) { + if (address->ip()->isAnyAddress()) { + auto ipv4_any_addr_iter = tcp_listener_map_by_address_.find( + Network::Address::Ipv4Instance(address->ip()->port()).asStringView()); + // Since both "::" with ipv4_compat and "0.0.0.0" can be supported, ensure they are same + // listener by tag. + if (ipv4_any_addr_iter != tcp_listener_map_by_address_.end() && + ipv4_any_addr_iter->second->listener_tag_ == per_address_details->listener_tag_) { + tcp_listener_map_by_address_.erase(ipv4_any_addr_iter); + } + } else { + auto v4_compatible_addr = address->ip()->ipv6()->v4CompatibleAddress(); + // Remove this check when runtime flag + // `envoy.reloadable_features.strict_check_on_ipv4_compat` deprecated. + if (v4_compatible_addr != nullptr) { + // both "::FFFF:" with ipv4_compat and "" isn't valid case, + // remove the v4 compatible addr item directly. + tcp_listener_map_by_address_.erase(v4_compatible_addr->asStringView()); + } } } + } else if (internal_listener_map_by_address_.contains(address_view) && + internal_listener_map_by_address_[address_view]->listener_tag_ == + per_address_details->listener_tag_) { + internal_listener_map_by_address_.erase(address_view); } - } else if (internal_listener_map_by_address_.contains(address_view) && - internal_listener_map_by_address_[address_view]->listener_tag_ == - listener_iter->second->listener_tag_) { - internal_listener_map_by_address_.erase(address_view); } listener_map_by_tag_.erase(listener_iter); } } Network::UdpListenerCallbacksOptRef -ConnectionHandlerImpl::getUdpListenerCallbacks(uint64_t listener_tag) { +ConnectionHandlerImpl::getUdpListenerCallbacks(uint64_t listener_tag, + const Network::Address::Instance& address) { auto listener = findActiveListenerByTag(listener_tag); if (listener.has_value()) { // If the tag matches this must be a UDP listener. - auto udp_listener = listener->get().udpListener(); - ASSERT(udp_listener.has_value()); - return udp_listener; + for (auto& details : listener->get().per_address_details_) { + if (*details->address_ == address) { + auto udp_listener = details->udpListener(); + ASSERT(udp_listener.has_value()); + return details->udpListener(); + } + } } return absl::nullopt; @@ -202,7 +222,10 @@ void ConnectionHandlerImpl::removeFilterChains( std::function completion) { if (auto listener_it = listener_map_by_tag_.find(listener_tag); listener_it != listener_map_by_tag_.end()) { - listener_it->second->listener_->onFilterChainDraining(filter_chains); + listener_it->second->invokeListenerMethod( + [&filter_chains](Network::ConnectionHandler::ActiveListener& listener) { + listener.onFilterChainDraining(filter_chains); + }); } // Reach here if the target listener is found or the target listener was removed by a full @@ -213,44 +236,55 @@ void ConnectionHandlerImpl::removeFilterChains( void ConnectionHandlerImpl::stopListeners(uint64_t listener_tag) { if (auto iter = listener_map_by_tag_.find(listener_tag); iter != listener_map_by_tag_.end()) { - if (iter->second->listener_->listener() != nullptr) { - iter->second->listener_->shutdownListener(); - } + iter->second->invokeListenerMethod([](Network::ConnectionHandler::ActiveListener& listener) { + if (listener.listener() != nullptr) { + listener.shutdownListener(); + } + }); } } void ConnectionHandlerImpl::stopListeners() { for (auto& iter : listener_map_by_tag_) { - if (iter.second->listener_->listener() != nullptr) { - iter.second->listener_->shutdownListener(); - } + iter.second->invokeListenerMethod([](Network::ConnectionHandler::ActiveListener& listener) { + if (listener.listener() != nullptr) { + listener.shutdownListener(); + } + }); } } void ConnectionHandlerImpl::disableListeners() { disable_listeners_ = true; for (auto& iter : listener_map_by_tag_) { - if (iter.second->listener_->listener() != nullptr) { - iter.second->listener_->pauseListening(); - } + iter.second->invokeListenerMethod([](Network::ConnectionHandler::ActiveListener& listener) { + if (listener.listener() != nullptr) { + listener.pauseListening(); + } + }); } } void ConnectionHandlerImpl::enableListeners() { disable_listeners_ = false; for (auto& iter : listener_map_by_tag_) { - if (iter.second->listener_->listener() != nullptr) { - iter.second->listener_->resumeListening(); - } + iter.second->invokeListenerMethod([](Network::ConnectionHandler::ActiveListener& listener) { + if (listener.listener() != nullptr) { + listener.resumeListening(); + } + }); } } void ConnectionHandlerImpl::setListenerRejectFraction(UnitFloat reject_fraction) { listener_reject_fraction_ = reject_fraction; for (auto& iter : listener_map_by_tag_) { - if (iter.second->listener_->listener() != nullptr) { - iter.second->listener_->listener()->setRejectFraction(reject_fraction); - } + iter.second->invokeListenerMethod( + [&reject_fraction](Network::ConnectionHandler::ActiveListener& listener) { + if (listener.listener() != nullptr) { + listener.listener()->setRejectFraction(reject_fraction); + } + }); } } @@ -265,19 +299,19 @@ ConnectionHandlerImpl::findByAddress(const Network::Address::InstanceConstShared } ConnectionHandlerImpl::ActiveTcpListenerOptRef -ConnectionHandlerImpl::ActiveListenerDetails::tcpListener() { +ConnectionHandlerImpl::PerAddressActiveListenerDetails::tcpListener() { auto* val = absl::get_if>(&typed_listener_); return (val != nullptr) ? absl::make_optional(*val) : absl::nullopt; } ConnectionHandlerImpl::UdpListenerCallbacksOptRef -ConnectionHandlerImpl::ActiveListenerDetails::udpListener() { +ConnectionHandlerImpl::PerAddressActiveListenerDetails::udpListener() { auto* val = absl::get_if>(&typed_listener_); return (val != nullptr) ? absl::make_optional(*val) : absl::nullopt; } ConnectionHandlerImpl::ActiveInternalListenerOptRef -ConnectionHandlerImpl::ActiveListenerDetails::internalListener() { +ConnectionHandlerImpl::PerAddressActiveListenerDetails::internalListener() { auto* val = absl::get_if>(&typed_listener_); return (val != nullptr) ? absl::make_optional(*val) : absl::nullopt; } @@ -291,14 +325,18 @@ ConnectionHandlerImpl::findActiveListenerByTag(uint64_t listener_tag) { } Network::BalancedConnectionHandlerOptRef -ConnectionHandlerImpl::getBalancedHandlerByTag(uint64_t listener_tag) { +ConnectionHandlerImpl::getBalancedHandlerByTag(uint64_t listener_tag, + const Network::Address::Instance& address) { auto active_listener = findActiveListenerByTag(listener_tag); if (active_listener.has_value()) { - ASSERT(absl::holds_alternative>( - active_listener->get().typed_listener_) && - active_listener->get().listener_->listener() != nullptr); - return Network::BalancedConnectionHandlerOptRef( - active_listener->get().tcpListener().value().get()); + for (auto& details : active_listener->get().per_address_details_) { + if (*details->address_ == address) { + ASSERT(absl::holds_alternative>( + details->typed_listener_) && + details->listener_->listener() != nullptr); + return {details->tcpListener().value().get()}; + } + } } return absl::nullopt; } @@ -313,11 +351,10 @@ ConnectionHandlerImpl::getBalancedHandlerByAddress(const Network::Address::Insta if (auto listener_it = tcp_listener_map_by_address_.find(address.asStringView()); listener_it != tcp_listener_map_by_address_.end() && listener_it->second->listener_->listener() != nullptr) { - return Network::BalancedConnectionHandlerOptRef( - listener_it->second->tcpListener().value().get()); + return {listener_it->second->tcpListener().value().get()}; } - OptRef details; + OptRef details; // Otherwise, we need to look for the wild card match, i.e., 0.0.0.0:[address_port]. // We do not return stopped listeners. // TODO(wattli): consolidate with previous search for more efficiency. diff --git a/source/server/connection_handler_impl.h b/source/server/connection_handler_impl.h index 52a365ce7c689..4acc71cdca851 100644 --- a/source/server/connection_handler_impl.h +++ b/source/server/connection_handler_impl.h @@ -60,19 +60,23 @@ class ConnectionHandlerImpl : public Network::TcpConnectionHandler, // Network::TcpConnectionHandler Event::Dispatcher& dispatcher() override { return dispatcher_; } - Network::BalancedConnectionHandlerOptRef getBalancedHandlerByTag(uint64_t listener_tag) override; + Network::BalancedConnectionHandlerOptRef + getBalancedHandlerByTag(uint64_t listener_tag, + const Network::Address::Instance& address) override; Network::BalancedConnectionHandlerOptRef getBalancedHandlerByAddress(const Network::Address::Instance& address) override; // Network::UdpConnectionHandler - Network::UdpListenerCallbacksOptRef getUdpListenerCallbacks(uint64_t listener_tag) override; + Network::UdpListenerCallbacksOptRef + getUdpListenerCallbacks(uint64_t listener_tag, + const Network::Address::Instance& address) override; // Network::InternalListenerManager Network::InternalListenerOptRef findByAddress(const Network::Address::InstanceConstSharedPtr& listen_address) override; private: - struct ActiveListenerDetails { + struct PerAddressActiveListenerDetails { // Strong pointer to the listener, whether TCP, UDP, QUIC, etc. Network::ConnectionHandler::ActiveListenerPtr listener_; Network::Address::InstanceConstSharedPtr address_; @@ -88,6 +92,39 @@ class ConnectionHandlerImpl : public Network::TcpConnectionHandler, UdpListenerCallbacksOptRef udpListener(); ActiveInternalListenerOptRef internalListener(); }; + + struct ActiveListenerDetails { + std::vector> per_address_details_; + + using ListenerMethodFn = std::function; + + void invokeListenerMethod(ListenerMethodFn fn) { + std::for_each(per_address_details_.begin(), per_address_details_.end(), + [&fn](std::shared_ptr& details) { + fn(*details->listener_); + }); + } + + template + void addActiveListener(Network::ListenerConfig& config, + const Network::Address::InstanceConstSharedPtr& address, + UnitFloat& listener_reject_fraction, bool disable_listeners, + ActiveListener&& listener) { + auto pre_address_details = std::make_shared(); + pre_address_details->typed_listener_ = *listener; + pre_address_details->listener_ = std::move(listener); + pre_address_details->address_ = address; + if (disable_listeners) { + pre_address_details->listener_->pauseListening(); + } + if (auto* listener = pre_address_details->listener_->listener(); listener != nullptr) { + listener->setRejectFraction(listener_reject_fraction); + } + pre_address_details->listener_tag_ = config.listenerTag(); + per_address_details_.emplace_back(pre_address_details); + } + }; + using ActiveListenerDetailsOptRef = absl::optional>; ActiveListenerDetailsOptRef findActiveListenerByTag(uint64_t listener_tag); @@ -95,10 +132,10 @@ class ConnectionHandlerImpl : public Network::TcpConnectionHandler, const absl::optional worker_index_; Event::Dispatcher& dispatcher_; const std::string per_handler_stat_prefix_; - absl::flat_hash_map> listener_map_by_tag_; - absl::flat_hash_map> + absl::flat_hash_map> listener_map_by_tag_; + absl::flat_hash_map> tcp_listener_map_by_address_; - absl::flat_hash_map> + absl::flat_hash_map> internal_listener_map_by_address_; std::atomic num_handler_connections_{}; diff --git a/source/server/filter_chain_manager_impl.cc b/source/server/filter_chain_manager_impl.cc index f8b12bc525873..6dde01909da41 100644 --- a/source/server/filter_chain_manager_impl.cc +++ b/source/server/filter_chain_manager_impl.cc @@ -10,6 +10,7 @@ #include "source/common/network/matching/data_impl.h" #include "source/common/network/matching/inputs.h" #include "source/common/network/socket_interface.h" +#include "source/common/network/utility.h" #include "source/common/protobuf/utility.h" #include "source/server/configuration_impl.h" @@ -194,10 +195,10 @@ bool PerFilterChainFactoryContextImpl::isQuicListener() const { } FilterChainManagerImpl::FilterChainManagerImpl( - const Network::Address::InstanceConstSharedPtr& address, + const std::vector& addresses, Configuration::FactoryContext& factory_context, Init::Manager& init_manager, const FilterChainManagerImpl& parent_manager) - : address_(address), parent_context_(factory_context), origin_(&parent_manager), + : addresses_(addresses), parent_context_(factory_context), origin_(&parent_manager), init_manager_(init_manager) {} bool FilterChainManagerImpl::isWildcardServerName(const std::string& name) { @@ -220,17 +221,19 @@ void FilterChainManagerImpl::addFilterChains( for (const auto& filter_chain : filter_chain_span) { const auto& filter_chain_match = filter_chain->filter_chain_match(); if (!filter_chain_match.address_suffix().empty() || filter_chain_match.has_suffix_len()) { - throw EnvoyException(fmt::format("error adding listener '{}': filter chain '{}' contains " - "unimplemented fields", - address_->asString(), filter_chain->name())); + throw EnvoyException(fmt::format( + "error adding listener '{}': filter chain '{}' contains " + "unimplemented fields", + absl::StrJoin(addresses_, ",", Network::AddressStrFormatter()), filter_chain->name())); } if (!filter_chain_matcher) { const auto& matching_iter = filter_chains.find(filter_chain_match); if (matching_iter != filter_chains.end()) { - throw EnvoyException(fmt::format("error adding listener '{}': filter chain '{}' has " - "the same matching rules defined as '{}'", - address_->asString(), filter_chain->name(), - matching_iter->second)); + throw EnvoyException( + fmt::format("error adding listener '{}': filter chain '{}' has " + "the same matching rules defined as '{}'", + absl::StrJoin(addresses_, ",", Network::AddressStrFormatter()), + filter_chain->name(), matching_iter->second)); } filter_chains.insert({filter_chain_match, filter_chain->name()}); } @@ -250,14 +253,14 @@ void FilterChainManagerImpl::addFilterChains( if (filter_chain->name().empty()) { throw EnvoyException(fmt::format( "error adding listener '{}': \"name\" field is required when using a listener matcher", - address_->asString())); + absl::StrJoin(addresses_, ",", Network::AddressStrFormatter()))); } auto [_, inserted] = filter_chains_by_name.try_emplace(filter_chain->name(), filter_chain_impl); if (!inserted) { - throw EnvoyException( - fmt::format("error adding listener '{}': \"name\" field is duplicated with value '{}'", - address_->asString(), filter_chain->name())); + throw EnvoyException(fmt::format( + "error adding listener '{}': \"name\" field is duplicated with value '{}'", + absl::StrJoin(addresses_, ",", Network::AddressStrFormatter()), filter_chain->name())); } if (filter_chain->has_filter_chain_match()) { ENVOY_LOG(debug, "filter chain match in chain '{}' is ignored", filter_chain->name()); @@ -288,7 +291,7 @@ void FilterChainManagerImpl::addFilterChains( throw EnvoyException( fmt::format("error adding listener '{}': partial wildcards are not supported in " "\"server_names\"", - address_->asString())); + absl::StrJoin(addresses_, ",", Network::AddressStrFormatter()))); } server_names.push_back(absl::AsciiStrToLower(server_name)); } @@ -512,9 +515,10 @@ void FilterChainManagerImpl::addFilterChainForSourcePorts( // If we got here and found already configured branch, then it means that this FilterChainMatch // is a duplicate, and that there is some overlap in the repeated fields with already processed // FilterChainMatches. - throw EnvoyException(fmt::format("error adding listener '{}': multiple filter chains with " - "overlapping matching rules are defined", - address_->asString())); + throw EnvoyException( + fmt::format("error adding listener '{}': multiple filter chains with " + "overlapping matching rules are defined", + absl::StrJoin(addresses_, ",", Network::AddressStrFormatter()))); } } diff --git a/source/server/filter_chain_manager_impl.h b/source/server/filter_chain_manager_impl.h index e3322602c35f1..3d802cdba1cb0 100644 --- a/source/server/filter_chain_manager_impl.h +++ b/source/server/filter_chain_manager_impl.h @@ -199,12 +199,12 @@ class FilterChainManagerImpl : public Network::FilterChainManager, using FcContextMap = absl::flat_hash_map; - FilterChainManagerImpl(const Network::Address::InstanceConstSharedPtr& address, + FilterChainManagerImpl(const std::vector& addresses, Configuration::FactoryContext& factory_context, Init::Manager& init_manager) - : address_(address), parent_context_(factory_context), init_manager_(init_manager) {} + : addresses_(addresses), parent_context_(factory_context), init_manager_(init_manager) {} - FilterChainManagerImpl(const Network::Address::InstanceConstSharedPtr& address, + FilterChainManagerImpl(const std::vector& addresses, Configuration::FactoryContext& factory_context, Init::Manager& init_manager, const FilterChainManagerImpl& parent_manager); @@ -379,7 +379,7 @@ class FilterChainManagerImpl : public Network::FilterChainManager, // and application protocols, using structures defined above. DestinationPortsMap destination_ports_map_; - const Network::Address::InstanceConstSharedPtr address_; + const std::vector& addresses_; // This is the reference to a factory context which all the generations of listener share. Configuration::FactoryContext& parent_context_; std::list> factory_contexts_; diff --git a/source/server/hot_restarting_parent.cc b/source/server/hot_restarting_parent.cc index a94b762722a65..7c289fa14364b 100644 --- a/source/server/hot_restarting_parent.cc +++ b/source/server/hot_restarting_parent.cc @@ -108,23 +108,24 @@ HotRestartingParent::Internal::getListenSocketsForChild(const HotRestartMessage: Network::Utility::resolveUrl(request.pass_listen_socket().address()); for (const auto& listener : server_->listenerManager().listeners()) { - Network::ListenSocketFactory& socket_factory = listener.get().listenSocketFactory(); - if (*socket_factory.localAddress() == *addr && listener.get().bindToPort()) { - StatusOr socket_type = - Network::Utility::socketTypeFromUrl(request.pass_listen_socket().address()); - // socketTypeFromUrl should return a valid value since resolveUrl returned a valid address. - ASSERT(socket_type.ok()); - - if (socket_factory.socketType() == *socket_type) { - // worker_index() will default to 0 if not set which is the behavior before this field - // was added. Thus, this should be safe for both roll forward and roll back. - if (request.pass_listen_socket().worker_index() < server_->options().concurrency()) { - wrapped_reply.mutable_reply()->mutable_pass_listen_socket()->set_fd( - socket_factory.getListenSocket(request.pass_listen_socket().worker_index()) - ->ioHandle() - .fdDoNotUse()); + for (auto& socket_factory : listener.get().listenSocketFactories()) { + if (*socket_factory->localAddress() == *addr && listener.get().bindToPort()) { + StatusOr socket_type = + Network::Utility::socketTypeFromUrl(request.pass_listen_socket().address()); + // socketTypeFromUrl should return a valid value since resolveUrl returned a valid address. + ASSERT(socket_type.ok()); + + if (socket_factory->socketType() == *socket_type) { + // worker_index() will default to 0 if not set which is the behavior before this field + // was added. Thus, this should be safe for both roll forward and roll back. + if (request.pass_listen_socket().worker_index() < server_->options().concurrency()) { + wrapped_reply.mutable_reply()->mutable_pass_listen_socket()->set_fd( + socket_factory->getListenSocket(request.pass_listen_socket().worker_index()) + ->ioHandle() + .fdDoNotUse()); + } + break; } - break; } } } diff --git a/source/server/listener_impl.cc b/source/server/listener_impl.cc index 957c62694279f..bc6340d2ee153 100644 --- a/source/server/listener_impl.cc +++ b/source/server/listener_impl.cc @@ -217,11 +217,12 @@ ListenerFactoryContextBaseImpl::ListenerFactoryContextBaseImpl( const envoy::config::listener::v3::Listener& config, DrainManagerPtr drain_manager) : server_(server), metadata_(config.metadata()), typed_metadata_(config.metadata()), direction_(config.traffic_direction()), global_scope_(server.stats().createScope("")), - listener_scope_(server_.stats().createScope( - fmt::format("listener.{}.", - !config.stat_prefix().empty() - ? config.stat_prefix() - : Network::Address::resolveProtoAddress(config.address())->asString()))), + listener_scope_(server_.stats().createScope(fmt::format( + "listener.{}.", !config.stat_prefix().empty() + ? config.stat_prefix() + : Network::Address::resolveProtoAddress( + config.has_address() ? config.address() : config.addresses(0)) + ->asString()))), validation_visitor_(validation_visitor), drain_manager_(std::move(drain_manager)), is_quic_(config.udp_listener_config().has_quic_options()) {} @@ -297,7 +298,10 @@ ListenerImpl::ListenerImpl(const envoy::config::listener::v3::Listener& config, const std::string& version_info, ListenerManagerImpl& parent, const std::string& name, bool added_via_api, bool workers_started, uint64_t hash, uint32_t concurrency) - : parent_(parent), address_(Network::Address::resolveProtoAddress(config.address())), + : parent_(parent), + socket_type_(config.has_address() + ? Network::Utility::protobufAddressSocketType(config.address()) + : Network::Utility::protobufAddressSocketType(config.addresses(0))), bind_to_port_(shouldBindToPort(config)), mptcp_enabled_(config.enable_mptcp()), hand_off_restored_destination_connections_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, use_original_dst, false)), @@ -322,8 +326,6 @@ ListenerImpl::ListenerImpl(const envoy::config::listener::v3::Listener& config, listener_factory_context_(std::make_shared( parent.server_, validation_visitor_, config, this, *this, parent.factory_.createDrainManager(config.drain_type()))), - filter_chain_manager_(address_, listener_factory_context_->parentFactoryContext(), - initManager()), reuse_port_(getReusePortOrDefault(parent_.server_, config_)), cx_limit_runtime_key_("envoy.resource_limits.listener." + config_.name() + ".connection_limit"), @@ -348,20 +350,26 @@ ListenerImpl::ListenerImpl(const envoy::config::listener::v3::Listener& config, parent_.server_.singletonManager(), parent_.server_.threadLocal(), validation_visitor_, parent_.server_.api(), parent_.server_.options(), parent_.server_.accessLogManager())), - quic_stat_names_(parent_.quicStatNames()) { - - if ((address_->type() == Network::Address::Type::Ip && - config.address().socket_address().ipv4_compat()) && - (address_->ip()->version() != Network::Address::IpVersion::v6 || - (!address_->ip()->isAnyAddress() && - address_->ip()->ipv6()->v4CompatibleAddress() == nullptr))) { - if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.strict_check_on_ipv4_compat")) { - throw EnvoyException(fmt::format( - "Only IPv6 address '::' or valid IPv4-mapped IPv6 address can set ipv4_compat: {}", - address_->asStringView())); - } else { - ENVOY_LOG(warn, "An invalid IPv4-mapped IPv6 address is used when ipv4_compat is set: {}", - address_->asStringView()); + quic_stat_names_(parent_.quicStatNames()), concurrency_(concurrency) { + // All the addresses should be same socket type, so get the first address's socket type is enough. + auto socket_type = config.has_address() + ? Network::Utility::protobufAddressSocketType(config.address()) + : Network::Utility::protobufAddressSocketType(config.addresses(0)); + if (config.has_address()) { + auto address = Network::Address::resolveProtoAddress(config.address()); + checkIpv4CompatAddress(address, config.address()); + addresses_.emplace_back(address); + } else if (config.addresses_size() > 0) { + for (auto i = 0; i < config.addresses_size(); i++) { + if (socket_type != Network::Utility::protobufAddressSocketType(config.addresses(i))) { + throw EnvoyException( + fmt::format("listener {}: has different socket type. The listener only " + "support same socket type for all the addresses.", + name_)); + } + auto address = Network::Address::resolveProtoAddress(config.addresses(i)); + checkIpv4CompatAddress(address, config.addresses(i)); + addresses_.emplace_back(address); } } @@ -374,17 +382,19 @@ ListenerImpl::ListenerImpl(const envoy::config::listener::v3::Listener& config, cx_limit_runtime_key_, config_.name()); } + filter_chain_manager_ = std::make_unique( + addresses_, listener_factory_context_->parentFactoryContext(), initManager()), + buildAccessLog(); - auto socket_type = Network::Utility::protobufAddressSocketType(config.address()); - validateConfig(socket_type); + validateConfig(); // buildUdpListenerFactory() must come before buildListenSocketOptions() because the UDP // listener factory can provide additional options. - buildUdpListenerFactory(socket_type, concurrency); - buildListenSocketOptions(socket_type); - createListenerFilterFactories(socket_type); - validateFilterChains(socket_type); + buildUdpListenerFactory(concurrency); + buildListenSocketOptions(); + createListenerFilterFactories(); + validateFilterChains(); buildFilterChains(); - if (socket_type != Network::Socket::Type::Datagram) { + if (socket_type_ != Network::Socket::Type::Datagram) { buildSocketOptions(); buildOriginalDstListenerFilter(); buildProxyProtocolListenerFilter(); @@ -404,8 +414,8 @@ ListenerImpl::ListenerImpl(ListenerImpl& origin, const std::string& version_info, ListenerManagerImpl& parent, const std::string& name, bool added_via_api, bool workers_started, uint64_t hash) - : parent_(parent), address_(origin.address_), bind_to_port_(shouldBindToPort(config)), - mptcp_enabled_(config.enable_mptcp()), + : parent_(parent), socket_type_(origin.socket_type_), addresses_(origin.addresses_), + bind_to_port_(shouldBindToPort(config)), mptcp_enabled_(config.enable_mptcp()), hand_off_restored_destination_connections_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(config, use_original_dst, false)), per_connection_buffer_limit_bytes_( @@ -427,11 +437,12 @@ ListenerImpl::ListenerImpl(ListenerImpl& origin, PROTOBUF_GET_MS_OR_DEFAULT(config, listener_filters_timeout, 15000)), continue_on_listener_filters_timeout_(config.continue_on_listener_filters_timeout()), udp_listener_config_(origin.udp_listener_config_), - connection_balancer_(origin.connection_balancer_), + connection_balancers_(origin.connection_balancers_), listener_factory_context_(std::make_shared( origin.listener_factory_context_->listener_factory_context_base_, this, *this)), - filter_chain_manager_(address_, origin.listener_factory_context_->parentFactoryContext(), - initManager(), origin.filter_chain_manager_), + filter_chain_manager_(std::make_unique( + addresses_, origin.listener_factory_context_->parentFactoryContext(), initManager(), + *origin.filter_chain_manager_)), reuse_port_(origin.reuse_port_), local_init_watcher_(fmt::format("Listener-local-init-watcher {}", name), [this] { @@ -441,14 +452,13 @@ ListenerImpl::ListenerImpl(ListenerImpl& origin, transport_factory_context_(origin.transport_factory_context_), quic_stat_names_(parent_.quicStatNames()) { buildAccessLog(); - auto socket_type = Network::Utility::protobufAddressSocketType(config.address()); - validateConfig(socket_type); - buildListenSocketOptions(socket_type); - createListenerFilterFactories(socket_type); - validateFilterChains(socket_type); + validateConfig(); + buildListenSocketOptions(); + createListenerFilterFactories(); + validateFilterChains(); buildFilterChains(); buildInternalListener(); - if (socket_type == Network::Socket::Type::Stream) { + if (socket_type_ == Network::Socket::Type::Stream) { // Apply the options below only for TCP. buildSocketOptions(); buildOriginalDstListenerFilter(); @@ -457,15 +467,35 @@ ListenerImpl::ListenerImpl(ListenerImpl& origin, } } -void ListenerImpl::validateConfig(Network::Socket::Type socket_type) { +void ListenerImpl::checkIpv4CompatAddress(const Network::Address::InstanceConstSharedPtr& address, + const envoy::config::core::v3::Address& proto_address) { + if ((address->type() == Network::Address::Type::Ip && + proto_address.socket_address().ipv4_compat()) && + (address->ip()->version() != Network::Address::IpVersion::v6 || + (!address->ip()->isAnyAddress() && + address->ip()->ipv6()->v4CompatibleAddress() == nullptr))) { + if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.strict_check_on_ipv4_compat")) { + throw EnvoyException(fmt::format( + "Only IPv6 address '::' or valid IPv4-mapped IPv6 address can set ipv4_compat: {}", + address->asStringView())); + } else { + ENVOY_LOG(warn, "An invalid IPv4-mapped IPv6 address is used when ipv4_compat is set: {}", + address->asStringView()); + } + } +} + +void ListenerImpl::validateConfig() { if (mptcp_enabled_) { - if (socket_type != Network::Socket::Type::Stream) { + if (socket_type_ != Network::Socket::Type::Stream) { throw EnvoyException( fmt::format("listener {}: enable_mptcp can only be used with TCP listeners", name_)); } - if (address_->type() != Network::Address::Type::Ip) { - throw EnvoyException( - fmt::format("listener {}: enable_mptcp can only be used with IP addresses", name_)); + for (auto& address : addresses_) { + if (address->type() != Network::Address::Type::Ip) { + throw EnvoyException( + fmt::format("listener {}: enable_mptcp can only be used with IP addresses", name_)); + } } if (!Api::OsSysCallsSingleton::get().supportsMptcp()) { throw EnvoyException(fmt::format( @@ -484,11 +514,12 @@ void ListenerImpl::buildAccessLog() { } void ListenerImpl::buildInternalListener() { - if (config_.address().has_envoy_internal_address()) { + if (config_.has_address() ? config_.address().has_envoy_internal_address() + : config_.addresses(0).has_envoy_internal_address()) { if (config_.has_api_listener()) { throw EnvoyException( fmt::format("error adding listener '{}': internal address cannot be used in api listener", - address_->asString())); + absl::StrJoin(addresses_, ",", Network::AddressStrFormatter()))); } if ((config_.has_connection_balance_config() && config_.connection_balance_config().has_exact_balance()) || @@ -499,11 +530,12 @@ void ListenerImpl::buildInternalListener() { (config_.has_transparent() && config_.transparent().value())) { throw EnvoyException( fmt::format("error adding listener '{}': has unsupported tcp listener feature", - address_->asString())); + absl::StrJoin(addresses_, ",", Network::AddressStrFormatter()))); } if (!config_.socket_options().empty()) { - throw EnvoyException(fmt::format("error adding listener '{}': does not support socket option", - address_->asString())); + throw EnvoyException( + fmt::format("error adding listener '{}': does not support socket option", + absl::StrJoin(addresses_, ",", Network::AddressStrFormatter()))); } std::shared_ptr internal_listener_registry = parent_.server_.singletonManager().getTyped( @@ -511,22 +543,35 @@ void ListenerImpl::buildInternalListener() { if (internal_listener_registry == nullptr) { throw EnvoyException( fmt::format("error adding listener '{}': internal listener registry is not initialized.", - address_->asString())); + absl::StrJoin(addresses_, ",", Network::AddressStrFormatter()))); } internal_listener_config_ = std::make_unique(*internal_listener_registry); } else { if (config_.has_internal_listener()) { - throw EnvoyException(fmt::format("error adding listener '{}': address is not an internal " - "address but an internal listener config is provided", - address_->asString())); + throw EnvoyException( + fmt::format("error adding listener '{}': address is not an internal " + "address but an internal listener config is provided", + absl::StrJoin(addresses_, ",", Network::AddressStrFormatter()))); } } } -void ListenerImpl::buildUdpListenerFactory(Network::Socket::Type socket_type, - uint32_t concurrency) { - if (socket_type != Network::Socket::Type::Datagram) { +void ListenerImpl::buildUdpListenerWorkerRouter(const Network::Address::Instance& address, + uint32_t concurrency) { + if (socket_type_ != Network::Socket::Type::Datagram) { + return; + } + auto iter = udp_listener_config_->listener_worker_routers_.find(address.asString()); + if (iter != udp_listener_config_->listener_worker_routers_.end()) { + return; + } + udp_listener_config_->listener_worker_routers_.emplace( + address.asString(), std::make_unique(concurrency)); +} + +void ListenerImpl::buildUdpListenerFactory(uint32_t concurrency) { + if (socket_type_ != Network::Socket::Type::Datagram) { return; } if (!reuse_port_ && concurrency > 1) { @@ -562,14 +607,12 @@ void ListenerImpl::buildUdpListenerFactory(Network::Socket::Type socket_type, udp_listener_config_->listener_factory_ = std::make_unique(concurrency); } - udp_listener_config_->listener_worker_router_ = - std::make_unique(concurrency); if (udp_listener_config_->writer_factory_ == nullptr) { udp_listener_config_->writer_factory_ = std::make_unique(); } } -void ListenerImpl::buildListenSocketOptions(Network::Socket::Type socket_type) { +void ListenerImpl::buildListenSocketOptions() { // The process-wide `signal()` handling may fail to handle SIGPIPE if overridden // in the process (i.e., on a mobile client). Some OSes support handling it at the socket layer: if (ENVOY_SOCKET_SO_NOSIGPIPE.hasValue()) { @@ -588,7 +631,7 @@ void ListenerImpl::buildListenSocketOptions(Network::Socket::Type socket_type) { addListenSocketOptions( Network::SocketOptionFactory::buildLiteralOptions(config_.socket_options())); } - if (socket_type == Network::Socket::Type::Datagram) { + if (socket_type_ == Network::Socket::Type::Datagram) { // Needed for recvmsg to return destination address in IP header. addListenSocketOptions(Network::SocketOptionFactory::buildIpPacketInfoOptions()); // Needed to return receive buffer overflown indicator. @@ -606,9 +649,9 @@ void ListenerImpl::buildListenSocketOptions(Network::Socket::Type socket_type) { } } -void ListenerImpl::createListenerFilterFactories(Network::Socket::Type socket_type) { +void ListenerImpl::createListenerFilterFactories() { if (!config_.listener_filters().empty()) { - switch (socket_type) { + switch (socket_type_) { case Network::Socket::Type::Datagram: udp_listener_filter_factories_ = parent_.factory_.createUdpListenerFilterFactoryList( config_.listener_filters(), *listener_factory_context_); @@ -621,23 +664,25 @@ void ListenerImpl::createListenerFilterFactories(Network::Socket::Type socket_ty } } -void ListenerImpl::validateFilterChains(Network::Socket::Type socket_type) { +void ListenerImpl::validateFilterChains() { if (config_.filter_chains().empty() && !config_.has_default_filter_chain() && - (socket_type == Network::Socket::Type::Stream || + (socket_type_ == Network::Socket::Type::Stream || !udp_listener_config_->listener_factory_->isTransportConnectionless())) { // If we got here, this is a tcp listener or connection-oriented udp listener, so ensure there // is a filter chain specified - throw EnvoyException(fmt::format("error adding listener '{}': no filter chains specified", - address_->asString())); + throw EnvoyException( + fmt::format("error adding listener '{}': no filter chains specified", + absl::StrJoin(addresses_, ",", Network::AddressStrFormatter()))); } else if (udp_listener_config_ != nullptr && !udp_listener_config_->listener_factory_->isTransportConnectionless()) { // Early fail if any filter chain doesn't have transport socket configured. if (anyFilterChain(config_, [](const auto& filter_chain) { return !filter_chain.has_transport_socket(); })) { - throw EnvoyException(fmt::format("error adding listener '{}': no transport socket " - "specified for connection oriented UDP listener", - address_->asString())); + throw EnvoyException( + fmt::format("error adding listener '{}': no transport socket " + "specified for connection oriented UDP listener", + absl::StrJoin(addresses_, ",", Network::AddressStrFormatter()))); } } else if (Runtime::runtimeFeatureEnabled( "envoy.reloadable_features.udp_listener_updates_filter_chain_in_place") && @@ -647,23 +692,24 @@ void ListenerImpl::validateFilterChains(Network::Socket::Type socket_type) { throw EnvoyException(fmt::format("error adding listener '{}': {} filter chain(s) specified for " "connection-less UDP listener.", - address_->asString(), config_.filter_chains_size())); + absl::StrJoin(addresses_, ",", Network::AddressStrFormatter()), + config_.filter_chains_size())); } } void ListenerImpl::buildFilterChains() { transport_factory_context_->setInitManager(*dynamic_init_manager_); ListenerFilterChainFactoryBuilder builder(*this, *transport_factory_context_); - filter_chain_manager_.addFilterChains( + filter_chain_manager_->addFilterChains( config_.has_filter_chain_matcher() ? &config_.filter_chain_matcher() : nullptr, config_.filter_chains(), config_.has_default_filter_chain() ? &config_.default_filter_chain() : nullptr, builder, - filter_chain_manager_); + *filter_chain_manager_); } -void ListenerImpl::buildSocketOptions() { - // TCP specific setup. - if (connection_balancer_ == nullptr) { +void ListenerImpl::buildConnectionBalancer(const Network::Address::Instance& address) { + auto iter = connection_balancers_.find(address.asString()); + if (iter == connection_balancers_.end() && socket_type_ == Network::Socket::Type::Stream) { #ifdef WIN32 // On Windows we use the exact connection balancer to dispatch connections // from worker 1 to all workers. This is a perf hit but it is the only way @@ -675,19 +721,24 @@ void ListenerImpl::buildSocketOptions() { "Envoy is running on Windows." "ExactBalance is used to load balance connections between workers on Windows.", config_.name()); - connection_balancer_ = std::make_shared(); + connection_balancers_.emplace(address.asString(), + std::make_shared()); #else // Not in place listener update. if (config_.has_connection_balance_config()) { // Currently exact balance is the only supported type and there are no options. ASSERT(config_.connection_balance_config().has_exact_balance()); - connection_balancer_ = std::make_shared(); + connection_balancers_.emplace(address.asString(), + std::make_shared()); } else { - connection_balancer_ = std::make_shared(); + connection_balancers_.emplace(address.asString(), + std::make_shared()); } #endif } +} +void ListenerImpl::buildSocketOptions() { if (config_.has_tcp_fast_open_queue_length()) { addListenSocketOptions(Network::SocketOptionFactory::buildTcpFastOpenOptions( config_.tcp_fast_open_queue_length().value())); @@ -827,7 +878,7 @@ void ListenerImpl::createUdpListenerFilterChain(Network::UdpListenerFilterManage void ListenerImpl::debugLog(const std::string& message) { UNREFERENCED_PARAMETER(message); ENVOY_LOG(debug, "{}: name={}, hash={}, tag={}, address={}", message, name_, hash_, listener_tag_, - address_->asString()); + absl::StrJoin(addresses_, ",", Network::AddressStrFormatter())); } void ListenerImpl::initialize() { @@ -853,9 +904,10 @@ ListenerImpl::~ListenerImpl() { Init::Manager& ListenerImpl::initManager() { return *dynamic_init_manager_; } -void ListenerImpl::setSocketFactory(Network::ListenSocketFactoryPtr&& socket_factory) { - ASSERT(!socket_factory_); - socket_factory_ = std::move(socket_factory); +void ListenerImpl::addSocketFactory(Network::ListenSocketFactoryPtr&& socket_factory) { + buildConnectionBalancer(*socket_factory->localAddress()); + buildUdpListenerWorkerRouter(*socket_factory->localAddress(), concurrency_); + socket_factories_.emplace_back(std::move(socket_factory)); } bool ListenerImpl::supportUpdateFilterChain(const envoy::config::listener::v3::Listener& config, @@ -868,9 +920,11 @@ bool ListenerImpl::supportUpdateFilterChain(const envoy::config::listener::v3::L if (!Runtime::runtimeFeatureEnabled( "envoy.reloadable_features.udp_listener_updates_filter_chain_in_place") && - (Network::Utility::protobufAddressSocketType(config_.address()) != + (Network::Utility::protobufAddressSocketType(config_.has_address() ? config_.address() + : config_.addresses(0)) != Network::Socket::Type::Stream || - Network::Utility::protobufAddressSocketType(config.address()) != + Network::Utility::protobufAddressSocketType(config.has_address() ? config.address() + : config.addresses(0)) != Network::Socket::Type::Stream)) { return false; } @@ -901,10 +955,10 @@ ListenerImpl::newListenerWithFilterChain(const envoy::config::listener::v3::List void ListenerImpl::diffFilterChain(const ListenerImpl& another_listener, std::function callback) { - for (const auto& message_and_filter_chain : filter_chain_manager_.filterChainsByMessage()) { - if (another_listener.filter_chain_manager_.filterChainsByMessage().find( + for (const auto& message_and_filter_chain : filter_chain_manager_->filterChainsByMessage()) { + if (another_listener.filter_chain_manager_->filterChainsByMessage().find( message_and_filter_chain.first) == - another_listener.filter_chain_manager_.filterChainsByMessage().end()) { + another_listener.filter_chain_manager_->filterChainsByMessage().end()) { // The filter chain exists in `this` listener but not in the listener passed in. callback(*message_and_filter_chain.second); } @@ -912,11 +966,11 @@ void ListenerImpl::diffFilterChain(const ListenerImpl& another_listener, // Filter chain manager maintains an optional default filter chain besides the filter chains // indexed by message. if (auto eq = MessageUtil(); - filter_chain_manager_.defaultFilterChainMessage().has_value() && - (!another_listener.filter_chain_manager_.defaultFilterChainMessage().has_value() || - !eq(*another_listener.filter_chain_manager_.defaultFilterChainMessage(), - *filter_chain_manager_.defaultFilterChainMessage()))) { - callback(*filter_chain_manager_.defaultFilterChain()); + filter_chain_manager_->defaultFilterChainMessage().has_value() && + (!another_listener.filter_chain_manager_->defaultFilterChainMessage().has_value() || + !eq(*another_listener.filter_chain_manager_->defaultFilterChainMessage(), + *filter_chain_manager_->defaultFilterChainMessage()))) { + callback(*filter_chain_manager_->defaultFilterChain()); } } @@ -945,7 +999,8 @@ bool ListenerImpl::getReusePortOrDefault(Server::Instance& server, }(); #ifndef __linux__ - const auto socket_type = Network::Utility::protobufAddressSocketType(config.address()); + const auto socket_type = Network::Utility::protobufAddressSocketType( + config.has_address() ? config.address() : config.addresses(0)); if (initial_reuse_port_value && socket_type == Network::Socket::Type::Stream) { // reuse_port is the default on Linux for TCP. On other platforms even if set it is disabled // and the user is warned. For UDP it's always the default even if not effective. @@ -961,9 +1016,59 @@ bool ListenerImpl::getReusePortOrDefault(Server::Instance& server, } bool ListenerImpl::hasCompatibleAddress(const ListenerImpl& other) const { - return *address() == *other.address() && - Network::Utility::protobufAddressSocketType(config_.address()) == - Network::Utility::protobufAddressSocketType(other.config_.address()); + if ((socket_type_ != other.socket_type_) || (addresses_.size() != other.addresses().size())) { + return false; + } + + // Since there can be multiple zero port addresses, create a copy + // of other's addresses, remove it for any finding to avoid matching same zero port + // address to the same one. + auto other_addresses = other.addresses(); + for (auto& addr : addresses()) { + auto iter = std::find_if(other_addresses.begin(), other_addresses.end(), + [&addr](const Network::Address::InstanceConstSharedPtr& other_addr) { + return *addr == *other_addr; + }); + if (iter == other_addresses.end()) { + return false; + } + other_addresses.erase(iter); + } + return true; +} + +bool ListenerImpl::hasDuplicatedAddress(const ListenerImpl& other) const { + if (socket_type_ != other.socket_type_) { + return false; + } + // For listeners that do not bind or listeners that do not bind to port 0 we must check to make + // sure we are not duplicating the address. This avoids ambiguity about which non-binding + // listener is used or even worse for the binding to port != 0 and reuse port case multiple + // different listeners receiving connections destined for the same port. + for (auto& other_addr : other.addresses()) { + if (other_addr->ip() == nullptr || + (other_addr->ip() != nullptr && (other_addr->ip()->port() != 0 || !bindToPort()))) { + if (find_if(addresses_.begin(), addresses_.end(), + [&other_addr](const Network::Address::InstanceConstSharedPtr& addr) { + return *other_addr == *addr; + }) != addresses_.end()) { + return true; + } + } + } + return false; +} + +void ListenerImpl::cloneSocketFactoryFrom(const ListenerImpl& other) { + for (auto& socket_factory : other.getSocketFactories()) { + addSocketFactory(socket_factory->clone()); + } +} + +void ListenerImpl::closeAllSockets() { + for (auto& socket_factory : socket_factories_) { + socket_factory->closeAllSockets(); + } } bool ListenerMessageUtil::filterChainOnlyChange(const envoy::config::listener::v3::Listener& lhs, diff --git a/source/server/listener_impl.h b/source/server/listener_impl.h index 8e2eee26d7bfe..ee6e800140f57 100644 --- a/source/server/listener_impl.h +++ b/source/server/listener_impl.h @@ -282,15 +282,19 @@ class ListenerImpl final : public Network::ListenerConfig, bool blockUpdate(uint64_t new_hash) { return new_hash == hash_ || !added_via_api_; } bool blockRemove() { return !added_via_api_; } - Network::Address::InstanceConstSharedPtr address() const { return address_; } + const std::vector& addresses() const { + return addresses_; + } const envoy::config::listener::v3::Listener& config() const { return config_; } - const Network::ListenSocketFactory& getSocketFactory() const { return *socket_factory_; } + const std::vector& getSocketFactories() const { + return socket_factories_; + } void debugLog(const std::string& message); void initialize(); DrainManager& localDrainManager() const { return listener_factory_context_->listener_factory_context_base_->drainManager(); } - void setSocketFactory(Network::ListenSocketFactoryPtr&& socket_factory); + void addSocketFactory(Network::ListenSocketFactoryPtr&& socket_factory); void setSocketAndOptions(const Network::SocketSharedPtr& socket); const Network::Socket::OptionsSharedPtr& listenSocketOptions() { return listen_socket_options_; } const std::string& versionInfo() const { return version_info_; } @@ -300,12 +304,16 @@ class ListenerImpl final : public Network::ListenerConfig, // Check whether a new listener can share sockets with this listener. bool hasCompatibleAddress(const ListenerImpl& other) const; + // Check whether a new listener has duplicated listening address this listener. + bool hasDuplicatedAddress(const ListenerImpl& other) const; // Network::ListenerConfig - Network::FilterChainManager& filterChainManager() override { return filter_chain_manager_; } + Network::FilterChainManager& filterChainManager() override { return *filter_chain_manager_; } Network::FilterChainFactory& filterChainFactory() override { return *this; } - Network::ListenSocketFactory& listenSocketFactory() override { return *socket_factory_; } - bool bindToPort() override { return bind_to_port_; } + const std::vector& listenSocketFactories() override { + return socket_factories_; + } + bool bindToPort() const override { return bind_to_port_; } bool mptcpEnabled() { return mptcp_enabled_; } bool handOffRestoredDestinationConnections() const override { return hand_off_restored_destination_connections_; @@ -330,7 +338,12 @@ class ListenerImpl final : public Network::ListenerConfig, return internal_listener_config_ != nullptr ? *internal_listener_config_ : Network::InternalListenerConfigOptRef(); } - Network::ConnectionBalancer& connectionBalancer() override { return *connection_balancer_; } + Network::ConnectionBalancer& + connectionBalancer(const Network::Address::Instance& address) override { + auto balancer = connection_balancers_.find(address.asString()); + ASSERT(balancer != connection_balancers_.end()); + return *balancer->second; + } ResourceLimit& openConnections() override { return *open_connections_; } const std::vector& accessLogs() const override { return access_logs_; @@ -349,6 +362,11 @@ class ListenerImpl final : public Network::ListenerConfig, } } + void cloneSocketFactoryFrom(const ListenerImpl& other); + void closeAllSockets(); + + Network::Socket::Type socketType() const { return socket_type_; } + // Network::FilterChainFactory bool createNetworkFilterChain(Network::Connection& connection, const std::vector& factories) override; @@ -366,15 +384,18 @@ class ListenerImpl final : public Network::ListenerConfig, // Network::UdpListenerConfig Network::ActiveUdpListenerFactory& listenerFactory() override { return *listener_factory_; } Network::UdpPacketWriterFactory& packetWriterFactory() override { return *writer_factory_; } - Network::UdpListenerWorkerRouter& listenerWorkerRouter() override { - return *listener_worker_router_; + Network::UdpListenerWorkerRouter& + listenerWorkerRouter(const Network::Address::Instance& address) override { + auto iter = listener_worker_routers_.find(address.asString()); + ASSERT(iter != listener_worker_routers_.end()); + return *iter->second; } const envoy::config::listener::v3::UdpListenerConfig& config() override { return config_; } const envoy::config::listener::v3::UdpListenerConfig config_; Network::ActiveUdpListenerFactoryPtr listener_factory_; Network::UdpPacketWriterFactoryPtr writer_factory_; - Network::UdpListenerWorkerRouterPtr listener_worker_router_; + absl::flat_hash_map listener_worker_routers_; }; class InternalListenerConfigImpl : public Network::InternalListenerConfig { @@ -400,15 +421,20 @@ class ListenerImpl final : public Network::ListenerConfig, // Helpers for constructor. void buildAccessLog(); void buildInternalListener(); - void validateConfig(Network::Socket::Type socket_type); - void buildUdpListenerFactory(Network::Socket::Type socket_type, uint32_t concurrency); - void buildListenSocketOptions(Network::Socket::Type socket_type); - void createListenerFilterFactories(Network::Socket::Type socket_type); - void validateFilterChains(Network::Socket::Type socket_type); + void validateConfig(); + void buildUdpListenerWorkerRouter(const Network::Address::Instance& address, + uint32_t concurrency); + void buildUdpListenerFactory(uint32_t concurrency); + void buildListenSocketOptions(); + void createListenerFilterFactories(); + void validateFilterChains(); void buildFilterChains(); + void buildConnectionBalancer(const Network::Address::Instance& address); void buildSocketOptions(); void buildOriginalDstListenerFilter(); void buildProxyProtocolListenerFilter(); + void checkIpv4CompatAddress(const Network::Address::InstanceConstSharedPtr& address, + const envoy::config::core::v3::Address& proto_address); void addListenSocketOptions(const Network::Socket::OptionsSharedPtr& options) { ensureSocketOptions(); @@ -416,9 +442,10 @@ class ListenerImpl final : public Network::ListenerConfig, } ListenerManagerImpl& parent_; - Network::Address::InstanceConstSharedPtr address_; + const Network::Socket::Type socket_type_; + std::vector addresses_; - Network::ListenSocketFactoryPtr socket_factory_; + std::vector socket_factories_; const bool bind_to_port_; const bool mptcp_enabled_; const bool hand_off_restored_destination_connections_; @@ -448,9 +475,9 @@ class ListenerImpl final : public Network::ListenerConfig, const bool continue_on_listener_filters_timeout_; std::shared_ptr udp_listener_config_; std::unique_ptr internal_listener_config_; - Network::ConnectionBalancerSharedPtr connection_balancer_; + absl::flat_hash_map connection_balancers_; std::shared_ptr listener_factory_context_; - FilterChainManagerImpl filter_chain_manager_; + std::unique_ptr filter_chain_manager_; const bool reuse_port_; // Per-listener connection limits are only specified via runtime. @@ -468,6 +495,7 @@ class ListenerImpl final : public Network::ListenerConfig, transport_factory_context_; Quic::QuicStatNames& quic_stat_names_; + uint32_t concurrency_; // to access ListenerManagerImpl::factory_. friend class ListenerFilterChainFactoryBuilder; diff --git a/source/server/listener_manager_impl.cc b/source/server/listener_manager_impl.cc index 1d675eb8422db..8e5e6f8f519eb 100644 --- a/source/server/listener_manager_impl.cc +++ b/source/server/listener_manager_impl.cc @@ -335,11 +335,40 @@ ListenerManagerStats ListenerManagerImpl::generateStats(Stats::Scope& scope) { bool ListenerManagerImpl::addOrUpdateListener(const envoy::config::listener::v3::Listener& config, const std::string& version_info, bool added_via_api) { + std::string name; + if (!config.name().empty()) { + name = config.name(); + } else { + name = server_.api().randomGenerator().uuid(); + } + + // TODO(soulxu): remove this validation to the `PGV` when deprecate the old `address` field. + if (config.has_address() && config.addresses_size() > 0) { + throw EnvoyException( + fmt::format("listener {}: only one of `address` and `addresses` can be set.", name)); + } else if (!config.has_address() && config.addresses_size() == 0) { + throw EnvoyException( + fmt::format("listener {}: one of `address` and `addresses` must be set.", name)); + } + + // There is no use-case for multiple internal addresses yet. This can be supported + // when the use-case show up in the future. + if (!config.has_address()) { + if (config.addresses(0).has_envoy_internal_address() && config.addresses_size() > 1) { + throw EnvoyException( + fmt::format("listener {}: internal address doesn't support multiple addresses.", name)); + } + } + if (!Runtime::runtimeFeatureEnabled("envoy.reloadable_features.internal_address")) { RELEASE_ASSERT( - !config.address().has_envoy_internal_address(), + !(config.has_address() ? config.address().has_envoy_internal_address() + : config.addresses(0).has_envoy_internal_address()), fmt::format("listener {} has envoy internal address {}. This runtime feature is disabled.", - config.name(), config.address().envoy_internal_address().DebugString())); + config.name(), + config.has_address() + ? config.address().envoy_internal_address().DebugString() + : config.addresses(0).envoy_internal_address().DebugString())); } // TODO(junr03): currently only one ApiListener can be installed via bootstrap to avoid having to // build a collection of listeners, and to have to be able to warm and drain the listeners. In the @@ -357,13 +386,6 @@ bool ListenerManagerImpl::addOrUpdateListener(const envoy::config::listener::v3: } } - std::string name; - if (!config.name().empty()) { - name = config.name(); - } else { - name = server_.api().randomGenerator().uuid(); - } - auto it = error_state_tracker_.find(name); TRY_ASSERT_MAIN_THREAD { return addOrUpdateListenerInternal(config, version_info, added_via_api, name); @@ -447,18 +469,18 @@ bool ListenerManagerImpl::addOrUpdateListenerInternal( ASSERT(workers_started_); new_listener->debugLog("update warming listener"); if (!(*existing_warming_listener)->hasCompatibleAddress(*new_listener)) { - setNewOrDrainingSocketFactory(name, config.address(), *new_listener); + setNewOrDrainingSocketFactory(name, *new_listener); } else { - new_listener->setSocketFactory((*existing_warming_listener)->getSocketFactory().clone()); + new_listener->cloneSocketFactoryFrom(**existing_warming_listener); } *existing_warming_listener = std::move(new_listener); } else if (existing_active_listener != active_listeners_.end()) { // In this case we have no warming listener, so what we do depends on whether workers // have been started or not. if (!(*existing_active_listener)->hasCompatibleAddress(*new_listener)) { - setNewOrDrainingSocketFactory(name, config.address(), *new_listener); + setNewOrDrainingSocketFactory(name, *new_listener); } else { - new_listener->setSocketFactory((*existing_active_listener)->getSocketFactory().clone()); + new_listener->cloneSocketFactoryFrom(**existing_active_listener); } if (workers_started_) { new_listener->debugLog("add warming listener"); @@ -470,7 +492,7 @@ bool ListenerManagerImpl::addOrUpdateListenerInternal( } else { // We have no warming or active listener so we need to make a new one. What we do depends on // whether workers have been started or not. - setNewOrDrainingSocketFactory(name, config.address(), *new_listener); + setNewOrDrainingSocketFactory(name, *new_listener); if (workers_started_) { new_listener->debugLog("add warming listener"); warming_listeners_.emplace_back(std::move(new_listener)); @@ -493,10 +515,10 @@ bool ListenerManagerImpl::addOrUpdateListenerInternal( return true; } -bool ListenerManagerImpl::hasListenerWithCompatibleAddress(const ListenerList& list, +bool ListenerManagerImpl::hasListenerWithDuplicatedAddress(const ListenerList& list, const ListenerImpl& listener) { for (const auto& existing_listener : list) { - if (existing_listener->hasCompatibleAddress(listener)) { + if (existing_listener->hasDuplicatedAddress(listener)) { return true; } } @@ -592,7 +614,9 @@ ListenerManagerImpl::listeners(ListenerState state) { bool ListenerManagerImpl::doFinalPreWorkerListenerInit(ListenerImpl& listener) { TRY_ASSERT_MAIN_THREAD { - listener.listenSocketFactory().doFinalPreWorkerInit(); + for (auto& socket_factory : listener.listenSocketFactories()) { + socket_factory->doFinalPreWorkerInit(); + } return true; } END_TRY @@ -966,19 +990,13 @@ Network::DrainableFilterChainSharedPtr ListenerFilterChainFactoryBuilder::buildF return filter_chain_res; } -void ListenerManagerImpl::setNewOrDrainingSocketFactory( - const std::string& name, const envoy::config::core::v3::Address& proto_address, - ListenerImpl& listener) { - // For listeners that do not bind or listeners that do not bind to port 0 we must check to make - // sure we are not duplicating the address. This avoids ambiguity about which non-binding - // listener is used or even worse for the binding to port != 0 and reuse port case multiple - // different listeners receiving connections destined for the same port. - if ((!listener.bindToPort() || listener.config().address().socket_address().port_value() != 0) && - (hasListenerWithCompatibleAddress(warming_listeners_, listener) || - hasListenerWithCompatibleAddress(active_listeners_, listener))) { +void ListenerManagerImpl::setNewOrDrainingSocketFactory(const std::string& name, + ListenerImpl& listener) { + if (hasListenerWithDuplicatedAddress(warming_listeners_, listener) || + hasListenerWithDuplicatedAddress(active_listeners_, listener)) { const std::string message = fmt::format("error adding listener: '{}' has duplicate address '{}' as existing listener", - name, listener.address()->asString()); + name, absl::StrJoin(listener.addresses(), ",", Network::AddressStrFormatter())); ENVOY_LOG(warn, "{}", message); throw EnvoyException(message); } @@ -987,43 +1005,45 @@ void ListenerManagerImpl::setNewOrDrainingSocketFactory( // the same address we are configured for. This is an edge case, but // may happen if a listener is removed and then added back with a same or different name and // intended to listen on the same address. This should work and not fail. - const Network::ListenSocketFactory* draining_listen_socket_factory = nullptr; - auto existing_draining_listener = std::find_if( - draining_listeners_.cbegin(), draining_listeners_.cend(), - [&listener](const DrainingListener& draining_listener) { - return draining_listener.listener_->listenSocketFactory().getListenSocket(0)->isOpen() && - listener.hasCompatibleAddress(*draining_listener.listener_); - }); + const ListenerImpl* draining_listener_ptr = nullptr; + auto existing_draining_listener = + std::find_if(draining_listeners_.cbegin(), draining_listeners_.cend(), + [&listener](const DrainingListener& draining_listener) { + return draining_listener.listener_->listenSocketFactories()[0] + ->getListenSocket(0) + ->isOpen() && + listener.hasCompatibleAddress(*draining_listener.listener_); + }); if (existing_draining_listener != draining_listeners_.cend()) { - draining_listen_socket_factory = &existing_draining_listener->listener_->getSocketFactory(); existing_draining_listener->listener_->debugLog("clones listener sockets"); + draining_listener_ptr = existing_draining_listener->listener_.get(); } else { auto existing_draining_filter_chain = std::find_if( draining_filter_chains_manager_.cbegin(), draining_filter_chains_manager_.cend(), [&listener](const DrainingFilterChainsManager& draining_filter_chain) { return draining_filter_chain.getDrainingListener() - .listenSocketFactory() - .getListenSocket(0) + .listenSocketFactories()[0] + ->getListenSocket(0) ->isOpen() && listener.hasCompatibleAddress(draining_filter_chain.getDrainingListener()); }); if (existing_draining_filter_chain != draining_filter_chains_manager_.cend()) { - draining_listen_socket_factory = - &existing_draining_filter_chain->getDrainingListener().getSocketFactory(); existing_draining_filter_chain->getDrainingListener().debugLog("clones listener socket"); + draining_listener_ptr = &existing_draining_filter_chain->getDrainingListener(); } } - listener.setSocketFactory(draining_listen_socket_factory != nullptr - ? draining_listen_socket_factory->clone() - : createListenSocketFactory(proto_address, listener)); + if (draining_listener_ptr != nullptr) { + listener.cloneSocketFactoryFrom(*draining_listener_ptr); + } else { + createListenSocketFactory(listener); + } } -Network::ListenSocketFactoryPtr ListenerManagerImpl::createListenSocketFactory( - const envoy::config::core::v3::Address& proto_address, ListenerImpl& listener) { - Network::Socket::Type socket_type = Network::Utility::protobufAddressSocketType(proto_address); +void ListenerManagerImpl::createListenSocketFactory(ListenerImpl& listener) { + Network::Socket::Type socket_type = listener.socketType(); ListenerComponentFactory::BindType bind_type = ListenerComponentFactory::BindType::NoBind; if (listener.bindToPort()) { bind_type = listener.reusePort() ? ListenerComponentFactory::BindType::ReusePort @@ -1032,9 +1052,11 @@ Network::ListenSocketFactoryPtr ListenerManagerImpl::createListenSocketFactory( TRY_ASSERT_MAIN_THREAD { Network::SocketCreationOptions creation_options; creation_options.mptcp_enabled_ = listener.mptcpEnabled(); - return std::make_unique( - factory_, listener.address(), socket_type, listener.listenSocketOptions(), listener.name(), - listener.tcpBacklogSize(), bind_type, creation_options, server_.options().concurrency()); + for (auto& address : listener.addresses()) { + listener.addSocketFactory(std::make_unique( + factory_, address, socket_type, listener.listenSocketOptions(), listener.name(), + listener.tcpBacklogSize(), bind_type, creation_options, server_.options().concurrency())); + } } END_TRY catch (const EnvoyException& e) { @@ -1052,7 +1074,7 @@ void ListenerManagerImpl::maybeCloseSocketsForListener(ListenerImpl& listener) { // already waiting for long timeout. However, connection-oriented UDP listeners shouldn't // close the socket because they need to receive packets for existing connections via the // listen sockets. - listener.listenSocketFactory().closeAllSockets(); + listener.closeAllSockets(); // In case of this listener was in-place updated previously and in the filter chains draining // procedure, so close the sockets for the previous draining listener. @@ -1060,7 +1082,7 @@ void ListenerManagerImpl::maybeCloseSocketsForListener(ListenerImpl& listener) { // A listener can be in-place updated multiple times, so there may // have multiple draining listeners with same tag. if (manager.getDrainingListenerTag() == listener.listenerTag()) { - manager.getDrainingListener().listenSocketFactory().closeAllSockets(); + manager.getDrainingListener().closeAllSockets(); } } } diff --git a/source/server/listener_manager_impl.h b/source/server/listener_manager_impl.h index f2baedbd66a66..1744f3cc617ff 100644 --- a/source/server/listener_manager_impl.h +++ b/source/server/listener_manager_impl.h @@ -235,7 +235,7 @@ class ListenerManagerImpl : public ListenerManager, Logger::LoggableaddOptions(Network::SocketOptionFactory::buildRxQueueOverFlowOptions()); ASSERT_TRUE(Network::Socket::applyOptions(listen_socket_->options(), *listen_socket_, envoy::config::core::v3::SocketOption::STATE_BOUND)); - - ON_CALL(listener_config_, listenSocketFactory()).WillByDefault(ReturnRef(socket_factory_)); - ON_CALL(socket_factory_, getListenSocket(_)).WillByDefault(Return(listen_socket_)); + socket_factories_.emplace_back(std::make_unique>()); + ON_CALL(listener_config_, listenSocketFactories()).WillByDefault(ReturnRef(socket_factories_)); + ON_CALL(*static_cast*>(socket_factories_[0].get()), + getListenSocket(_)) + .WillByDefault(Return(listen_socket_)); // Use UdpGsoBatchWriter to perform non-batched writes for the purpose of this test, if it is // supported. @@ -120,7 +122,9 @@ class ActiveQuicListenerTest : public testing::TestWithParam(listener_factory_->createActiveUdpListener( - scoped_runtime_.loader(), 0, connection_handler_, *dispatcher_, listener_config_)); + scoped_runtime_.loader(), 0, connection_handler_, + listener_config_.listenSocketFactories()[0]->getListenSocket(0), *dispatcher_, + listener_config_)); quic_dispatcher_ = ActiveQuicListenerPeer::quicDispatcher(*quic_listener_); quic::QuicCryptoServerConfig& crypto_config = ActiveQuicListenerPeer::cryptoConfig(*quic_listener_); @@ -291,7 +295,7 @@ class ActiveQuicListenerTest : public testing::TestWithParam quic_listener_; Network::ActiveUdpListenerFactoryPtr listener_factory_; - NiceMock socket_factory_; + std::vector socket_factories_; EnvoyQuicDispatcher* quic_dispatcher_; NiceMock tls_; diff --git a/test/config/integration/BUILD b/test/config/integration/BUILD index 94c9f35bd99d5..0d7fbf3b4a36d 100644 --- a/test/config/integration/BUILD +++ b/test/config/integration/BUILD @@ -9,6 +9,7 @@ envoy_package() exports_files([ "server.yaml", + "server_multiple_addresses.yaml", "server_unix_listener.yaml", ]) @@ -36,6 +37,7 @@ filegroup( name = "server_config_files", srcs = [ "server.yaml", + "server_multiple_addresses.yaml", "server_unix_listener.yaml", ], ) diff --git a/test/config/integration/server_multiple_addresses.yaml b/test/config/integration/server_multiple_addresses.yaml new file mode 100644 index 0000000000000..bc146892e9959 --- /dev/null +++ b/test/config/integration/server_multiple_addresses.yaml @@ -0,0 +1,180 @@ +static_resources: + listeners: + - addresses: + - socket_address: + address: "{{ ip_loopback_address }}" + port_value: 0 + - socket_address: + address: "{{ ip_loopback_address }}" + port_value: 0 + enable_reuse_port: {{ enable_reuse_port }} + filter_chains: + - filters: + - name: http + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager + drain_timeout: 5s + route_config: + virtual_hosts: + - require_tls: all + routes: + - route: {cluster: cluster_1} + match: {prefix: "/"} + domains: + - www.redirect.com + name: redirect + - routes: + - match: {prefix: "/"} + route: + cluster: cluster_1 + - match: {prefix: "/test/long/url"} + route: + rate_limits: + - actions: + - destination_cluster: {} + cluster: cluster_1 + - match: {prefix: "/test/"} + route: {cluster: cluster_2} + - match: {prefix: "/websocket/test"} + route: + prefix_rewrite: "/websocket" + cluster: cluster_1 + domains: + - "*" + name: integration + codec_type: http1 + stat_prefix: router + http_filters: + - name: health + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.health_check.v3.HealthCheck + pass_through_mode: false + - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router + access_log: + - name: accesslog + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: "{{ null_device_path }}" + filter: + or_filter: + filters: + - status_code_filter: + comparison: + op: GE + value: + default_value: 500 + runtime_key: access_log.access_error.status + - duration_filter: + comparison: + op: GE + value: + default_value: 1000 + runtime_key: access_log.access_error.duration + clusters: + - name: cluster_1 + connect_timeout: 5s + load_assignment: + cluster_name: cluster_1 + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: "{{ ip_loopback_address }}" + port_value: "{{ upstream_0 }}" + dns_lookup_family: "{{ dns_lookup_family }}" + - name: cluster_2 + type: STRICT_DNS + connect_timeout: 5s + load_assignment: + cluster_name: cluster_2 + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: localhost + port_value: "{{ upstream_1 }}" + dns_lookup_family: "{{ dns_lookup_family }}" + - name: cluster_3 + connect_timeout: 5s + per_connection_buffer_limit_bytes: 1024 + load_assignment: + cluster_name: cluster_3 + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: "{{ ip_loopback_address }}" + port_value: "{{ upstream_0 }}" + dns_lookup_family: "{{ dns_lookup_family }}" + - name: statsd + type: STRICT_DNS + connect_timeout: 5s + load_assignment: + cluster_name: statsd + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: localhost + port_value: 4 + dns_lookup_family: "{{ dns_lookup_family }}" + - name: redis + type: STRICT_DNS + connect_timeout: 5s + lb_policy: RING_HASH + load_assignment: + cluster_name: redis + endpoints: + - lb_endpoints: + - endpoint: + address: + socket_address: + address: localhost + port_value: 4 + dns_lookup_family: "{{ dns_lookup_family }}" + outlier_detection: {} +dynamic_resources: {} +cluster_manager: {} +flags_path: "/invalid_flags" +stats_sinks: +- name: local_stats + typed_config: + "@type": type.googleapis.com/envoy.config.metrics.v3.StatsdSink + address: + socket_address: + address: "{{ ip_loopback_address }}" + port_value: 8125 +- name: tcp_stats + typed_config: + "@type": type.googleapis.com/envoy.config.metrics.v3.StatsdSink + tcp_cluster_name: statsd +layered_runtime: + layers: + - name: root + disk_layer: + symlink_root: "{{ test_tmpdir }}/test/common/runtime/test_data/current" + subdirectory: envoy + - name: override + disk_layer: + symlink_root: "{{ test_tmpdir }}/test/common/runtime/test_data/current" + subdirectory: envoy_override + append_service_cluster: true + - name: admin + admin_layer: {} +admin: + access_log: + - name: envoy.access_loggers.file + typed_config: + "@type": type.googleapis.com/envoy.extensions.access_loggers.file.v3.FileAccessLog + path: "{{ null_device_path }}" + profile_path: "{{ test_tmpdir }}/envoy.prof" + address: + socket_address: + address: "{{ ip_loopback_address }}" + port_value: 0 diff --git a/test/config/utility.cc b/test/config/utility.cc index 619050253de55..3af72afc8161b 100644 --- a/test/config/utility.cc +++ b/test/config/utility.cc @@ -73,7 +73,20 @@ std::string ConfigHelper::baseConfigNoListeners() { Platform::null_device_path, Platform::null_device_path); } -std::string ConfigHelper::baseConfig() { +std::string ConfigHelper::baseConfig(bool multiple_addresses) { + if (multiple_addresses) { + return absl::StrCat(baseConfigNoListeners(), R"EOF( + listeners: + - name: listener_0 + addresses: + - socket_address: + address: 127.0.0.1 + port_value: 0 + - socket_address: + address: 127.0.0.1 + port_value: 0 +)EOF"); + } return absl::StrCat(baseConfigNoListeners(), R"EOF( listeners: - name: listener_0 @@ -84,8 +97,9 @@ std::string ConfigHelper::baseConfig() { )EOF"); } -std::string ConfigHelper::baseUdpListenerConfig(std::string listen_address) { - return fmt::format(R"EOF( +std::string ConfigHelper::baseUdpListenerConfig(std::string listen_address, + bool multiple_addresses) { + std::string config = fmt::format(R"EOF( admin: access_log: - name: envoy.access_loggers.file @@ -108,6 +122,26 @@ std::string ConfigHelper::baseUdpListenerConfig(std::string listen_address) { socket_address: address: 127.0.0.1 port_value: 0 +)EOF", + Platform::null_device_path); + + if (multiple_addresses) { + return absl::StrCat(config, fmt::format(R"EOF( + listeners: + name: listener_0 + addresses: + - socket_address: + address: {} + port_value: 0 + protocol: udp + - socket_address: + address: {} + port_value: 0 + protocol: udp +)EOF", + listen_address, listen_address)); + } + return absl::StrCat(config, fmt::format(R"EOF( listeners: name: listener_0 address: @@ -116,7 +150,7 @@ std::string ConfigHelper::baseUdpListenerConfig(std::string listen_address) { port_value: 0 protocol: udp )EOF", - Platform::null_device_path, listen_address); + listen_address)); } std::string ConfigHelper::tcpProxyConfig() { @@ -177,11 +211,11 @@ name: "envoy.filters.listener.tls_inspector" )EOF"; } -std::string ConfigHelper::httpProxyConfig(bool downstream_use_quic) { +std::string ConfigHelper::httpProxyConfig(bool downstream_use_quic, bool multiple_addresses) { if (downstream_use_quic) { - return quicHttpProxyConfig(); + return quicHttpProxyConfig(multiple_addresses); } - return absl::StrCat(baseConfig(), fmt::format(R"EOF( + return absl::StrCat(baseConfig(multiple_addresses), fmt::format(R"EOF( filter_chains: filters: name: http @@ -213,14 +247,15 @@ std::string ConfigHelper::httpProxyConfig(bool downstream_use_quic) { domains: "*" name: route_config_0 )EOF", - Platform::null_device_path)); + Platform::null_device_path)); } // TODO(danzh): For better compatibility with HTTP integration test framework, // it's better to combine with HTTP_PROXY_CONFIG, and use config modifiers to // specify quic specific things. -std::string ConfigHelper::quicHttpProxyConfig() { - return absl::StrCat(baseUdpListenerConfig("127.0.0.1"), fmt::format(R"EOF( +std::string ConfigHelper::quicHttpProxyConfig(bool multiple_addresses) { + return absl::StrCat(baseUdpListenerConfig("127.0.0.1", multiple_addresses), + fmt::format(R"EOF( filter_chains: transport_socket: name: envoy.transport_sockets.quic @@ -256,7 +291,7 @@ std::string ConfigHelper::quicHttpProxyConfig() { udp_listener_config: quic_options: {{}} )EOF", - Platform::null_device_path)); + Platform::null_device_path)); } std::string ConfigHelper::defaultBufferFilter() { @@ -685,23 +720,37 @@ ConfigHelper::ConfigHelper(const Network::Address::IpVersion version, Api::Api& auto* static_resources = bootstrap_.mutable_static_resources(); for (int i = 0; i < static_resources->listeners_size(); ++i) { auto* listener = static_resources->mutable_listeners(i); - if (listener->mutable_address()->has_envoy_internal_address()) { - ENVOY_LOG_MISC( - debug, "Listener {} has internal address {}. Will not reset to loop back socket address.", - i, listener->mutable_address()->envoy_internal_address().server_listener_name()); - continue; - } - if (listener->mutable_address()->has_pipe()) { - ENVOY_LOG_MISC(debug, - "Listener {} has pipe address {}. Will not reset to loop back socket address.", - i, listener->mutable_address()->pipe().path()); - continue; - } - auto* listener_socket_addr = listener->mutable_address()->mutable_socket_address(); - if (listener_socket_addr->address() == "0.0.0.0" || listener_socket_addr->address() == "::") { - listener_socket_addr->set_address(Network::Test::getAnyAddressString(version)); + if (listener->addresses_size() == 0) { + if (listener->mutable_address()->has_envoy_internal_address()) { + ENVOY_LOG_MISC( + debug, + "Listener {} has internal address {}. Will not reset to loop back socket address.", i, + listener->mutable_address()->envoy_internal_address().server_listener_name()); + continue; + } + if (listener->mutable_address()->has_pipe()) { + ENVOY_LOG_MISC( + debug, "Listener {} has pipe address {}. Will not reset to loop back socket address.", + i, listener->mutable_address()->pipe().path()); + continue; + } + + auto* listener_socket_addr = listener->mutable_address()->mutable_socket_address(); + if (listener_socket_addr->address() == "0.0.0.0" || listener_socket_addr->address() == "::") { + listener_socket_addr->set_address(Network::Test::getAnyAddressString(version)); + } else { + listener_socket_addr->set_address(Network::Test::getLoopbackAddressString(version)); + } } else { - listener_socket_addr->set_address(Network::Test::getLoopbackAddressString(version)); + for (int i = 0; i < listener->addresses_size(); i++) { + auto* listener_socket_addr = listener->mutable_addresses(i)->mutable_socket_address(); + if (listener_socket_addr->address() == "0.0.0.0" || + listener_socket_addr->address() == "::") { + listener_socket_addr->set_address(Network::Test::getAnyAddressString(version)); + } else { + listener_socket_addr->set_address(Network::Test::getLoopbackAddressString(version)); + } + } } } diff --git a/test/config/utility.h b/test/config/utility.h index 4cfca969e90ff..040aed8120b2c 100644 --- a/test/config/utility.h +++ b/test/config/utility.h @@ -138,7 +138,7 @@ class ConfigHelper { // By default, this runs with an L7 proxy config, but config can be set to TCP_PROXY_CONFIG // to test L4 proxying. ConfigHelper(const Network::Address::IpVersion version, Api::Api& api, - const std::string& config = httpProxyConfig(false)); + const std::string& config = httpProxyConfig(false, false)); static void initializeTls(const ServerSslOptions& options, @@ -154,10 +154,11 @@ class ConfigHelper { static std::string baseConfigNoListeners(); // A basic configuration (admin port, cluster_0, one listener) with no network filters. - static std::string baseConfig(); + static std::string baseConfig(bool multiple_addresses = false); // A basic configuration (admin port, cluster_0, one udp listener) with no network filters. - static std::string baseUdpListenerConfig(std::string listen_address = "0.0.0.0"); + static std::string baseUdpListenerConfig(std::string listen_address = "0.0.0.0", + bool multiple_addresses = false); // A string for a tls inspector listener filter which can be used with addListenerFilter() static std::string tlsInspectorFilter(bool enable_ja3_fingerprinting = false); @@ -168,9 +169,10 @@ class ConfigHelper { // A basic configuration for L4 proxying. static std::string tcpProxyConfig(); // A basic configuration for L7 proxying. - static std::string httpProxyConfig(bool downstream_use_quic = false); + static std::string httpProxyConfig(bool downstream_use_quic = false, + bool multiple_addresses = false); // A basic configuration for L7 proxying with QUIC transport. - static std::string quicHttpProxyConfig(); + static std::string quicHttpProxyConfig(bool multiple_addresses = false); // A string for a basic buffer filter, which can be used with prependFilter() static std::string defaultBufferFilter(); // A string for a small buffer filter, which can be used with prependFilter() diff --git a/test/extensions/common/proxy_protocol/proxy_protocol_regression_test.cc b/test/extensions/common/proxy_protocol/proxy_protocol_regression_test.cc index fe07517d8c84d..c4e86e7c3b085 100644 --- a/test/extensions/common/proxy_protocol/proxy_protocol_regression_test.cc +++ b/test/extensions/common/proxy_protocol/proxy_protocol_regression_test.cc @@ -46,10 +46,16 @@ class ProxyProtocolRegressionTest : public testing::TestWithParam()); + EXPECT_CALL(*static_cast(socket_factories_[0].get()), + socketType()) + .WillOnce(Return(Network::Socket::Type::Stream)); + EXPECT_CALL(*static_cast(socket_factories_[0].get()), + localAddress()) .WillRepeatedly(ReturnRef(socket_->connectionInfoProvider().localAddress())); - EXPECT_CALL(socket_factory_, getListenSocket(_)).WillOnce(Return(socket_)); + EXPECT_CALL(*static_cast(socket_factories_[0].get()), + getListenSocket(_)) + .WillOnce(Return(socket_)); connection_handler_->addListener(absl::nullopt, *this, runtime_); conn_ = dispatcher_->createClientConnection(socket_->connectionInfoProvider().localAddress(), Network::Address::InstanceConstSharedPtr(), @@ -60,8 +66,10 @@ class ProxyProtocolRegressionTest : public testing::TestWithParam& listenSocketFactories() override { + return socket_factories_; + } + bool bindToPort() const override { return true; } bool handOffRestoredDestinationConnections() const override { return false; } uint32_t perConnectionBufferLimitBytes() const override { return 0; } std::chrono::milliseconds listenerFiltersTimeout() const override { return {}; } @@ -79,7 +87,9 @@ class ProxyProtocolRegressionTest : public testing::TestWithParam& accessLogs() const override { return empty_access_logs_; } @@ -166,7 +176,7 @@ class ProxyProtocolRegressionTest : public testing::TestWithParam socket_; - Network::MockListenSocketFactory socket_factory_; + std::vector socket_factories_; Network::NopConnectionBalancerImpl connection_balancer_; Network::ConnectionHandlerPtr connection_handler_; Network::MockFilterChainFactory factory_; diff --git a/test/extensions/filters/listener/common/fuzz/listener_filter_fuzzer.cc b/test/extensions/filters/listener/common/fuzz/listener_filter_fuzzer.cc index 88241d66d935d..06edc6a57b87c 100644 --- a/test/extensions/filters/listener/common/fuzz/listener_filter_fuzzer.cc +++ b/test/extensions/filters/listener/common/fuzz/listener_filter_fuzzer.cc @@ -35,10 +35,16 @@ ListenerFilterWithDataFuzzer::ListenerFilterWithDataFuzzer() connection_handler_(new Server::ConnectionHandlerImpl(*dispatcher_, absl::nullopt)), name_("proxy"), filter_chain_(Network::Test::createEmptyFilterChainWithRawBufferSockets()), init_manager_(nullptr) { - EXPECT_CALL(socket_factory_, socketType()).WillOnce(Return(Network::Socket::Type::Stream)); - EXPECT_CALL(socket_factory_, localAddress()) + socket_factories_.emplace_back(std::make_unique()); + EXPECT_CALL(*static_cast(socket_factories_[0].get()), + socketType()) + .WillOnce(Return(Network::Socket::Type::Stream)); + EXPECT_CALL(*static_cast(socket_factories_[0].get()), + localAddress()) .WillRepeatedly(ReturnRef(socket_->connectionInfoProvider().localAddress())); - EXPECT_CALL(socket_factory_, getListenSocket(_)).WillOnce(Return(socket_)); + EXPECT_CALL(*static_cast(socket_factories_[0].get()), + getListenSocket(_)) + .WillOnce(Return(socket_)); connection_handler_->addListener(absl::nullopt, *this, runtime_); conn_ = dispatcher_->createClientConnection(socket_->connectionInfoProvider().localAddress(), Network::Address::InstanceConstSharedPtr(), diff --git a/test/extensions/filters/listener/common/fuzz/listener_filter_fuzzer.h b/test/extensions/filters/listener/common/fuzz/listener_filter_fuzzer.h index 9a52da99823c0..4dc25ce0c30a3 100644 --- a/test/extensions/filters/listener/common/fuzz/listener_filter_fuzzer.h +++ b/test/extensions/filters/listener/common/fuzz/listener_filter_fuzzer.h @@ -48,8 +48,10 @@ class ListenerFilterWithDataFuzzer : public Network::ListenerConfig, // Network::ListenerConfig Network::FilterChainManager& filterChainManager() override { return *this; } Network::FilterChainFactory& filterChainFactory() override { return factory_; } - Network::ListenSocketFactory& listenSocketFactory() override { return socket_factory_; } - bool bindToPort() override { return true; } + const std::vector& listenSocketFactories() override { + return socket_factories_; + } + bool bindToPort() const override { return true; } bool handOffRestoredDestinationConnections() const override { return false; } uint32_t perConnectionBufferLimitBytes() const override { return 0; } std::chrono::milliseconds listenerFiltersTimeout() const override { return {}; } @@ -67,7 +69,9 @@ class ListenerFilterWithDataFuzzer : public Network::ListenerConfig, envoy::config::core::v3::TrafficDirection direction() const override { return envoy::config::core::v3::UNSPECIFIED; } - Network::ConnectionBalancer& connectionBalancer() override { return connection_balancer_; } + Network::ConnectionBalancer& connectionBalancer(const Network::Address::Instance&) override { + return connection_balancer_; + } const std::vector& accessLogs() const override { return empty_access_logs_; } @@ -98,7 +102,7 @@ class ListenerFilterWithDataFuzzer : public Network::ListenerConfig, BasicResourceLimitImpl open_connections_; Event::DispatcherPtr dispatcher_; std::shared_ptr socket_; - Network::MockListenSocketFactory socket_factory_; + std::vector socket_factories_; Network::NopConnectionBalancerImpl connection_balancer_; Network::ConnectionHandlerPtr connection_handler_; Network::MockFilterChainFactory factory_; 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 afeba9505b0ac..53c031efef6cf 100644 --- a/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc +++ b/test/extensions/filters/listener/proxy_protocol/proxy_protocol_test.cc @@ -63,10 +63,16 @@ class ProxyProtocolTest : public testing::TestWithParam()); + EXPECT_CALL(*static_cast(socket_factories_[0].get()), + socketType()) + .WillOnce(Return(Network::Socket::Type::Stream)); + EXPECT_CALL(*static_cast(socket_factories_[0].get()), + localAddress()) .WillRepeatedly(ReturnRef(socket_->connectionInfoProvider().localAddress())); - EXPECT_CALL(socket_factory_, getListenSocket(_)).WillOnce(Return(socket_)); + EXPECT_CALL(*static_cast(socket_factories_[0].get()), + getListenSocket(_)) + .WillOnce(Return(socket_)); connection_handler_->addListener(absl::nullopt, *this, runtime_); conn_ = dispatcher_->createClientConnection(socket_->connectionInfoProvider().localAddress(), Network::Address::InstanceConstSharedPtr(), @@ -77,8 +83,10 @@ class ProxyProtocolTest : public testing::TestWithParam& listenSocketFactories() override { + return socket_factories_; + } + bool bindToPort() const override { return true; } bool handOffRestoredDestinationConnections() const override { return false; } uint32_t perConnectionBufferLimitBytes() const override { return 0; } std::chrono::milliseconds listenerFiltersTimeout() const override { return {}; } @@ -96,7 +104,9 @@ class ProxyProtocolTest : public testing::TestWithParam& accessLogs() const override { return empty_access_logs_; } @@ -198,7 +208,7 @@ class ProxyProtocolTest : public testing::TestWithParam socket_; - Network::MockListenSocketFactory socket_factory_; + std::vector socket_factories_; Network::NopConnectionBalancerImpl connection_balancer_; Network::ConnectionHandlerPtr connection_handler_; Network::MockFilterChainFactory factory_; @@ -1745,10 +1755,16 @@ class WildcardProxyProtocolTest : public testing::TestWithParam()); + EXPECT_CALL(*static_cast(socket_factories_[0].get()), + socketType()) + .WillOnce(Return(Network::Socket::Type::Stream)); + EXPECT_CALL(*static_cast(socket_factories_[0].get()), + localAddress()) .WillRepeatedly(ReturnRef(socket_->connectionInfoProvider().localAddress())); - EXPECT_CALL(socket_factory_, getListenSocket(_)).WillOnce(Return(socket_)); + EXPECT_CALL(*static_cast(socket_factories_[0].get()), + getListenSocket(_)) + .WillOnce(Return(socket_)); connection_handler_->addListener(absl::nullopt, *this, runtime_); conn_ = dispatcher_->createClientConnection(local_dst_address_, Network::Address::InstanceConstSharedPtr(), @@ -1769,8 +1785,10 @@ class WildcardProxyProtocolTest : public testing::TestWithParam& listenSocketFactories() override { + return socket_factories_; + } + bool bindToPort() const override { return true; } bool handOffRestoredDestinationConnections() const override { return false; } uint32_t perConnectionBufferLimitBytes() const override { return 0; } std::chrono::milliseconds listenerFiltersTimeout() const override { return {}; } @@ -1788,7 +1806,9 @@ class WildcardProxyProtocolTest : public testing::TestWithParam& accessLogs() const override { return empty_access_logs_; } @@ -1849,7 +1869,7 @@ class WildcardProxyProtocolTest : public testing::TestWithParam socket_factories_; std::shared_ptr socket_; Network::Address::InstanceConstSharedPtr local_dst_address_; Network::NopConnectionBalancerImpl connection_balancer_; diff --git a/test/extensions/filters/network/echo/echo_integration_test.cc b/test/extensions/filters/network/echo/echo_integration_test.cc index e18b7cec2ee52..0416bc36cd5b7 100644 --- a/test/extensions/filters/network/echo/echo_integration_test.cc +++ b/test/extensions/filters/network/echo/echo_integration_test.cc @@ -78,8 +78,8 @@ name: new_listener .listenerManager() .listeners()[1] .get() - .listenSocketFactory() - .localAddress() + .listenSocketFactories()[0] + ->localAddress() ->ip() ->port(); diff --git a/test/integration/base_integration_test.cc b/test/integration/base_integration_test.cc index 90a7e4d570a5d..9cab0691b21a7 100644 --- a/test/integration/base_integration_test.cc +++ b/test/integration/base_integration_test.cc @@ -348,11 +348,16 @@ void BaseIntegrationTest::registerTestServerPorts(const std::vector auto listener_it = listeners.cbegin(); auto port_it = port_names.cbegin(); - for (; port_it != port_names.end() && listener_it != listeners.end(); ++port_it, ++listener_it) { - const auto listen_addr = listener_it->get().listenSocketFactory().localAddress(); - if (listen_addr->type() == Network::Address::Type::Ip) { - ENVOY_LOG(debug, "registered '{}' as port {}.", *port_it, listen_addr->ip()->port()); - registerPort(*port_it, listen_addr->ip()->port()); + for (; port_it != port_names.end() && listener_it != listeners.end(); ++listener_it) { + auto socket_factory_it = listener_it->get().listenSocketFactories().begin(); + for (; socket_factory_it != listener_it->get().listenSocketFactories().end() && + port_it != port_names.end(); + ++socket_factory_it, ++port_it) { + const auto listen_addr = (*socket_factory_it)->localAddress(); + if (listen_addr->type() == Network::Address::Type::Ip) { + ENVOY_LOG(debug, "registered '{}' as port {}.", *port_it, listen_addr->ip()->port()); + registerPort(*port_it, listen_addr->ip()->port()); + } } } const auto admin_addr = diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc index 5312bdd4c7141..e392418f08566 100644 --- a/test/integration/fake_upstream.cc +++ b/test/integration/fake_upstream.cc @@ -561,7 +561,6 @@ FakeUpstream::FakeUpstream(Network::TransportSocketFactoryPtr&& transport_socket : http_type_(config.upstream_protocol_), http2_options_(config.http2_options_), http3_options_(config.http3_options_), quic_options_(config.quic_options_), socket_(Network::SocketSharedPtr(listen_socket.release())), - socket_factory_(std::make_unique(socket_)), api_(Api::createApiForTest(stats_store_)), time_system_(config.time_system_), dispatcher_(api_->allocateDispatcher("fake_upstream")), handler_(new Server::ConnectionHandlerImpl(*dispatcher_, 0)), config_(config), @@ -571,6 +570,7 @@ FakeUpstream::FakeUpstream(Network::TransportSocketFactoryPtr&& transport_socket stats_scope_(stats_store_.createScope("test_server_scope")) { ENVOY_LOG(info, "starting fake server at {}. UDP={} codec={}", localAddress()->asString(), config.udp_fake_upstream_.has_value(), FakeHttpConnection::typeToString(http_type_)); + socket_factories_.emplace_back(std::make_unique(socket_)); if (config.udp_fake_upstream_.has_value() && config.udp_fake_upstream_->max_rx_datagram_size_.has_value()) { listener_.udp_listener_config_.config_.mutable_downstream_socket_config() @@ -624,7 +624,7 @@ void FakeUpstream::createUdpListenerFilterChain(Network::UdpListenerFilterManage } void FakeUpstream::threadRoutine() { - socket_factory_->doFinalPreWorkerInit(); + socket_factories_[0]->doFinalPreWorkerInit(); handler_->addListener(absl::nullopt, listener_, runtime_); server_initialized_.setReady(); dispatcher_->run(Event::Dispatcher::RunType::Block); diff --git a/test/integration/fake_upstream.h b/test/integration/fake_upstream.h index 1f49cfacea48d..bc3fd3f5b9a87 100644 --- a/test/integration/fake_upstream.h +++ b/test/integration/fake_upstream.h @@ -760,7 +760,8 @@ class FakeUpstream : Logger::Loggable, // Network::UdpListenerConfig Network::ActiveUdpListenerFactory& listenerFactory() override { return *listener_factory_; } Network::UdpPacketWriterFactory& packetWriterFactory() override { return *writer_factory_; } - Network::UdpListenerWorkerRouter& listenerWorkerRouter() override { + Network::UdpListenerWorkerRouter& + listenerWorkerRouter(const Network::Address::Instance&) override { return listener_worker_router_; } const envoy::config::listener::v3::UdpListenerConfig& config() override { return config_; } @@ -794,10 +795,10 @@ class FakeUpstream : Logger::Loggable, // Network::ListenerConfig Network::FilterChainManager& filterChainManager() override { return parent_; } Network::FilterChainFactory& filterChainFactory() override { return parent_; } - Network::ListenSocketFactory& listenSocketFactory() override { - return *parent_.socket_factory_; + const std::vector& listenSocketFactories() override { + return parent_.socket_factories_; } - bool bindToPort() override { return true; } + bool bindToPort() const override { return true; } bool handOffRestoredDestinationConnections() const override { return false; } uint32_t perConnectionBufferLimitBytes() const override { return 0; } std::chrono::milliseconds listenerFiltersTimeout() const override { return {}; } @@ -806,10 +807,10 @@ class FakeUpstream : Logger::Loggable, uint64_t listenerTag() const override { return 0; } const std::string& name() const override { return name_; } Network::UdpListenerConfigOptRef udpListenerConfig() override { return udp_listener_config_; } - Network::InternalListenerConfigOptRef internalListenerConfig() override { - return Network::InternalListenerConfigOptRef(); + Network::InternalListenerConfigOptRef internalListenerConfig() override { return {}; } + Network::ConnectionBalancer& connectionBalancer(const Network::Address::Instance&) override { + return connection_balancer_; } - Network::ConnectionBalancer& connectionBalancer() override { return connection_balancer_; } envoy::config::core::v3::TrafficDirection direction() const override { return envoy::config::core::v3::UNSPECIFIED; } @@ -845,7 +846,7 @@ class FakeUpstream : Logger::Loggable, const envoy::config::core::v3::Http3ProtocolOptions http3_options_; envoy::config::listener::v3::QuicProtocolOptions quic_options_; Network::SocketSharedPtr socket_; - Network::ListenSocketFactoryPtr socket_factory_; + std::vector socket_factories_; ConditionalInitializer server_initialized_; // Guards any objects which can be altered both in the upstream thread and the // main test thread. diff --git a/test/integration/hotrestart_test.sh b/test/integration/hotrestart_test.sh index 5c26beaae5299..baf23e8cc8374 100755 --- a/test/integration/hotrestart_test.sh +++ b/test/integration/hotrestart_test.sh @@ -75,6 +75,19 @@ sed -e "s#{{ upstream_. }}#0#g" "${TEST_SRCDIR}/envoy"/test/config/integration/s cat > "${HOT_RESTART_JSON_REUSE_PORT}" JSON_TEST_ARRAY+=("${HOT_RESTART_JSON_REUSE_PORT}") +# Test reuse_port listener with multiple addresses. +HOT_RESTART_JSON_REUSE_PORT_MULTI_ADDRESSES="${TEST_TMPDIR}"/hot_restart_v4_multiple_addresses.yaml +echo "building ${HOT_RESTART_JSON_V4} ..." +sed -e "s#{{ upstream_. }}#0#g" "${TEST_SRCDIR}/envoy"/test/config/integration/server_multiple_addresses.yaml | \ + sed -e "s#{{ test_rundir }}#$TEST_SRCDIR/envoy#" | \ + sed -e "s#{{ test_tmpdir }}#$TEST_TMPDIR#" | \ + sed -e "s#{{ ip_loopback_address }}#127.0.0.1#" | \ + sed -e "s#{{ enable_reuse_port }}#true#" | \ + sed -e "s#{{ dns_lookup_family }}#V4_ONLY#" | \ + sed -e "s#{{ null_device_path }}#/dev/null#" | \ + cat > "${HOT_RESTART_JSON_REUSE_PORT_MULTI_ADDRESSES}" +JSON_TEST_ARRAY+=("${HOT_RESTART_JSON_REUSE_PORT_MULTI_ADDRESSES}") + # Shared memory size varies by architecture SHARED_MEMORY_SIZE="104" [[ "$(uname -m)" == "aarch64" ]] && SHARED_MEMORY_SIZE="120" @@ -93,8 +106,9 @@ function run_testsuite() { fi start_test validation + check "${ENVOY_BIN}" -c "${HOT_RESTART_JSON}" --mode validate --service-cluster cluster \ - --service-node node --disable-hot-restart + --service-node node --disable-hot-restart -l debug BASE_ID_PATH=$(mktemp 'envoy_test_base_id.XXXXXX') echo "Selected dynamic base id path ${BASE_ID_PATH}" diff --git a/test/integration/integration_admin_test.cc b/test/integration/integration_admin_test.cc index 93f9e7c55e584..2678ea0607cc7 100644 --- a/test/integration/integration_admin_test.cc +++ b/test/integration/integration_admin_test.cc @@ -325,10 +325,11 @@ TEST_P(IntegrationAdminTest, Admin) { auto listeners = test_server_->server().listenerManager().listeners(); auto listener_it = listeners.cbegin(); for (; listener_it != listeners.end(); ++listener_it) { - EXPECT_THAT(response->body(), - HasSubstr(fmt::format( - "{}::{}", listener_it->get().name(), - listener_it->get().listenSocketFactory().localAddress()->asString()))); + for (auto& socket_factory : listener_it->get().listenSocketFactories()) { + EXPECT_THAT(response->body(), + HasSubstr(fmt::format("{}::{}", listener_it->get().name(), + socket_factory->localAddress()->asString()))); + } } EXPECT_EQ("200", request("admin", "GET", "/listeners?format=json", response)); @@ -341,12 +342,16 @@ TEST_P(IntegrationAdminTest, Admin) { listener_it = listeners.cbegin(); for (; listener_info_it != listener_info.end() && listener_it != listeners.end(); ++listener_info_it, ++listener_it) { - auto local_address = (*listener_info_it)->getObject("local_address"); - auto socket_address = local_address->getObject("socket_address"); - EXPECT_EQ(listener_it->get().listenSocketFactory().localAddress()->ip()->addressAsString(), - socket_address->getString("address")); - EXPECT_EQ(listener_it->get().listenSocketFactory().localAddress()->ip()->port(), - socket_address->getInteger("port_value")); + std::vector local_addresses = + (*listener_info_it)->getObjectArray("local_addresses"); + for (std::vector::size_type i = 0; i < local_addresses.size(); i++) { + auto socket_address = local_addresses[i]->getObject("socket_address"); + EXPECT_EQ( + listener_it->get().listenSocketFactories()[i]->localAddress()->ip()->addressAsString(), + socket_address->getString("address")); + EXPECT_EQ(listener_it->get().listenSocketFactories()[i]->localAddress()->ip()->port(), + socket_address->getInteger("port_value")); + } } EXPECT_EQ("200", request("admin", "GET", "/config_dump", response)); diff --git a/test/integration/integration_admin_test.h b/test/integration/integration_admin_test.h index 7d036b98a7230..f149d9f362805 100644 --- a/test/integration/integration_admin_test.h +++ b/test/integration/integration_admin_test.h @@ -15,17 +15,26 @@ namespace Envoy { class IntegrationAdminTest : public HttpProtocolIntegrationTest { public: void initialize() override { - config_helper_.addConfigModifier( - [](envoy::config::bootstrap::v3::Bootstrap& bootstrap) -> void { - auto& hist_settings = - *bootstrap.mutable_stats_config()->mutable_histogram_bucket_settings(); - envoy::config::metrics::v3::HistogramBucketSettings* setting = hist_settings.Add(); - setting->mutable_match()->set_suffix("upstream_cx_connect_ms"); - setting->mutable_buckets()->Add(1); - setting->mutable_buckets()->Add(2); - setting->mutable_buckets()->Add(3); - setting->mutable_buckets()->Add(4); - }); + config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) + -> void { + auto listener_config = bootstrap.mutable_static_resources()->mutable_listeners(0); + listener_config->clear_address(); + auto address1 = listener_config->add_addresses(); + envoy::config::core::v3::SocketAddress& socket_address1 = *address1->mutable_socket_address(); + socket_address1.set_address("127.0.0.1"); + socket_address1.set_port_value(0); + auto address2 = listener_config->add_addresses(); + envoy::config::core::v3::SocketAddress& socket_address2 = *address2->mutable_socket_address(); + socket_address2.set_address("127.0.0.2"); + socket_address2.set_port_value(0); + auto& hist_settings = *bootstrap.mutable_stats_config()->mutable_histogram_bucket_settings(); + envoy::config::metrics::v3::HistogramBucketSettings* setting = hist_settings.Add(); + setting->mutable_match()->set_suffix("upstream_cx_connect_ms"); + setting->mutable_buckets()->Add(1); + setting->mutable_buckets()->Add(2); + setting->mutable_buckets()->Add(3); + setting->mutable_buckets()->Add(4); + }); HttpIntegrationTest::initialize(); } diff --git a/test/integration/listener_lds_integration_test.cc b/test/integration/listener_lds_integration_test.cc index 7226abde41ba1..3a35b93411044 100644 --- a/test/integration/listener_lds_integration_test.cc +++ b/test/integration/listener_lds_integration_test.cc @@ -26,18 +26,21 @@ using testing::StartsWith; namespace Envoy { namespace { -class ListenerIntegrationTest : public HttpIntegrationTest, - public Grpc::GrpcClientIntegrationParamTest { -protected: +class ListenerIntegrationTestBase : public HttpIntegrationTest { +public: struct FakeUpstreamInfo { FakeHttpConnectionPtr connection_; FakeUpstream* upstream_{}; absl::flat_hash_map stream_by_resource_name_; }; - ListenerIntegrationTest() : HttpIntegrationTest(Http::CodecType::HTTP1, ipVersion()) {} + ListenerIntegrationTestBase(Network::Address::IpVersion version, const std::string& config) + : HttpIntegrationTest(Http::CodecType::HTTP1, version, config) {} + + ~ListenerIntegrationTestBase() override { resetConnections(); } - ~ListenerIntegrationTest() override { resetConnections(); } + virtual void setUpGrpcRds() PURE; + virtual void setUpGrpcLds() PURE; void initialize() override { // We want to use the GRPC based LDS. @@ -59,43 +62,13 @@ class ListenerIntegrationTest : public HttpIntegrationTest, ConfigHelper::setHttp2(*rds_cluster); }); - config_helper_.addConfigModifier( - [this]( - envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& - http_connection_manager) { - auto* rds_config = http_connection_manager.mutable_rds(); - rds_config->set_route_config_name(route_table_name_); - rds_config->mutable_config_source()->set_resource_api_version( - envoy::config::core::v3::ApiVersion::V3); - envoy::config::core::v3::ApiConfigSource* rds_api_config_source = - rds_config->mutable_config_source()->mutable_api_config_source(); - rds_api_config_source->set_api_type(envoy::config::core::v3::ApiConfigSource::GRPC); - rds_api_config_source->set_transport_api_version(envoy::config::core::v3::V3); - envoy::config::core::v3::GrpcService* grpc_service = - rds_api_config_source->add_grpc_services(); - setGrpcService(*grpc_service, "rds_cluster", getRdsFakeUpstream().localAddress()); - }); - + setUpGrpcRds(); // Note this has to be the last modifier as it nuke static_resource listeners. setUpGrpcLds(); + ENVOY_LOG_MISC(debug, "listener config: {}", listener_config_.DebugString()); + HttpIntegrationTest::initialize(); } - void setUpGrpcLds() { - config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { - listener_config_.Swap(bootstrap.mutable_static_resources()->mutable_listeners(0)); - listener_config_.set_name(listener_name_); - ENVOY_LOG_MISC(debug, "listener config: {}", listener_config_.DebugString()); - bootstrap.mutable_static_resources()->mutable_listeners()->Clear(); - auto* lds_config_source = bootstrap.mutable_dynamic_resources()->mutable_lds_config(); - lds_config_source->set_resource_api_version(envoy::config::core::v3::ApiVersion::V3); - auto* lds_api_config_source = lds_config_source->mutable_api_config_source(); - lds_api_config_source->set_api_type(envoy::config::core::v3::ApiConfigSource::GRPC); - lds_api_config_source->set_transport_api_version(envoy::config::core::v3::V3); - envoy::config::core::v3::GrpcService* grpc_service = - lds_api_config_source->add_grpc_services(); - setGrpcService(*grpc_service, "lds_cluster", getLdsFakeUpstream().localAddress()); - }); - } void createUpstreams() override { HttpIntegrationTest::createUpstreams(); @@ -194,9 +167,100 @@ class ListenerIntegrationTest : public HttpIntegrationTest, FakeUpstreamInfo rds_upstream_info_; }; +class ListenerIntegrationTest : public ListenerIntegrationTestBase, + public Grpc::GrpcClientIntegrationParamTest { +public: + ListenerIntegrationTest() + : ListenerIntegrationTestBase(ipVersion(), + ConfigHelper::httpProxyConfig(/*downstream_use_quic=*/false, + /*multiple_addresses=*/false)) {} + + void setUpGrpcRds() override { + config_helper_.addConfigModifier( + [this]( + envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + http_connection_manager) { + auto* rds_config = http_connection_manager.mutable_rds(); + rds_config->set_route_config_name(route_table_name_); + rds_config->mutable_config_source()->set_resource_api_version( + envoy::config::core::v3::ApiVersion::V3); + envoy::config::core::v3::ApiConfigSource* rds_api_config_source = + rds_config->mutable_config_source()->mutable_api_config_source(); + rds_api_config_source->set_api_type(envoy::config::core::v3::ApiConfigSource::GRPC); + rds_api_config_source->set_transport_api_version(envoy::config::core::v3::V3); + envoy::config::core::v3::GrpcService* grpc_service = + rds_api_config_source->add_grpc_services(); + setGrpcService(*grpc_service, "rds_cluster", getRdsFakeUpstream().localAddress()); + }); + } + + void setUpGrpcLds() override { + config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + listener_config_.Swap(bootstrap.mutable_static_resources()->mutable_listeners(0)); + listener_config_.set_name(listener_name_); + bootstrap.mutable_static_resources()->mutable_listeners()->Clear(); + auto* lds_config_source = bootstrap.mutable_dynamic_resources()->mutable_lds_config(); + lds_config_source->set_resource_api_version(envoy::config::core::v3::ApiVersion::V3); + auto* lds_api_config_source = lds_config_source->mutable_api_config_source(); + lds_api_config_source->set_api_type(envoy::config::core::v3::ApiConfigSource::GRPC); + lds_api_config_source->set_transport_api_version(envoy::config::core::v3::V3); + envoy::config::core::v3::GrpcService* grpc_service = + lds_api_config_source->add_grpc_services(); + setGrpcService(*grpc_service, "lds_cluster", getLdsFakeUpstream().localAddress()); + }); + } +}; + +class ListenerMultiAddressesIntegrationTest : public ListenerIntegrationTestBase, + public Grpc::GrpcClientIntegrationParamTest { +public: + ListenerMultiAddressesIntegrationTest() + : ListenerIntegrationTestBase(ipVersion(), + ConfigHelper::httpProxyConfig(/*downstream_use_quic=*/false, + /*multiple_addresses=*/true)) {} + + void setUpGrpcRds() override { + config_helper_.addConfigModifier( + [this]( + envoy::extensions::filters::network::http_connection_manager::v3::HttpConnectionManager& + http_connection_manager) { + auto* rds_config = http_connection_manager.mutable_rds(); + rds_config->set_route_config_name(route_table_name_); + rds_config->mutable_config_source()->set_resource_api_version( + envoy::config::core::v3::ApiVersion::V3); + envoy::config::core::v3::ApiConfigSource* rds_api_config_source = + rds_config->mutable_config_source()->mutable_api_config_source(); + rds_api_config_source->set_api_type(envoy::config::core::v3::ApiConfigSource::GRPC); + rds_api_config_source->set_transport_api_version(envoy::config::core::v3::V3); + envoy::config::core::v3::GrpcService* grpc_service = + rds_api_config_source->add_grpc_services(); + setGrpcService(*grpc_service, "rds_cluster", getRdsFakeUpstream().localAddress()); + }); + } + + void setUpGrpcLds() override { + config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + listener_config_.Swap(bootstrap.mutable_static_resources()->mutable_listeners(0)); + listener_config_.set_name(listener_name_); + bootstrap.mutable_static_resources()->mutable_listeners()->Clear(); + auto* lds_config_source = bootstrap.mutable_dynamic_resources()->mutable_lds_config(); + lds_config_source->set_resource_api_version(envoy::config::core::v3::ApiVersion::V3); + auto* lds_api_config_source = lds_config_source->mutable_api_config_source(); + lds_api_config_source->set_api_type(envoy::config::core::v3::ApiConfigSource::GRPC); + lds_api_config_source->set_transport_api_version(envoy::config::core::v3::V3); + envoy::config::core::v3::GrpcService* grpc_service = + lds_api_config_source->add_grpc_services(); + setGrpcService(*grpc_service, "lds_cluster", getLdsFakeUpstream().localAddress()); + }); + } +}; + INSTANTIATE_TEST_SUITE_P(IpVersionsAndGrpcTypes, ListenerIntegrationTest, GRPC_CLIENT_INTEGRATION_PARAMS); +INSTANTIATE_TEST_SUITE_P(IpVersionsAndGrpcTypes, ListenerMultiAddressesIntegrationTest, + GRPC_CLIENT_INTEGRATION_PARAMS); + // Tests that an update with an unknown filter config proto is rejected. TEST_P(ListenerIntegrationTest, CleanlyRejectsUnknownFilterConfigProto) { on_server_init_function_ = [&]() { @@ -373,6 +437,67 @@ TEST_P(ListenerIntegrationTest, RemoveLastUninitializedListener) { EXPECT_EQ(test_server_->server().initManager().state(), Init::Manager::State::Initialized); } +TEST_P(ListenerMultiAddressesIntegrationTest, BasicSuccessWithMultiAddresses) { + on_server_init_function_ = [&]() { + createLdsStream(); + sendLdsResponse({MessageUtil::getYamlStringFromMessage(listener_config_)}, "1"); + createRdsStream(route_table_name_); + }; + initialize(); + test_server_->waitForCounterGe("listener_manager.lds.update_success", 1); + // testing-listener-0 is not initialized as we haven't pushed any RDS yet. + EXPECT_EQ(test_server_->server().initManager().state(), Init::Manager::State::Initializing); + // Workers not started, the LDS added listener 0 is in active_listeners_ list. + EXPECT_EQ(test_server_->server().listenerManager().listeners().size(), 1); + registerTestServerPorts({"address1", "address2"}); + + const std::string route_config_tmpl = R"EOF( + name: {} + virtual_hosts: + - name: integration + domains: ["*"] + routes: + - match: {{ prefix: "/" }} + route: {{ cluster: {} }} +)EOF"; + sendRdsResponse(fmt::format(route_config_tmpl, route_table_name_, "cluster_0"), "1"); + test_server_->waitForCounterGe( + fmt::format("http.config_test.rds.{}.update_success", route_table_name_), 1); + // Now testing-listener-0 finishes initialization, Server initManager will be ready. + EXPECT_EQ(test_server_->server().initManager().state(), Init::Manager::State::Initialized); + + test_server_->waitUntilListenersReady(); + // NOTE: The line above doesn't tell you if listener is up and listening. + test_server_->waitForCounterGe("listener_manager.listener_create_success", 1); + // Request is sent to cluster_0. + + int response_size = 800; + int request_size = 10; + Http::TestResponseHeaderMapImpl response_headers{{":status", "200"}, + {"server_id", "cluster_0, backend_0"}}; + + codec_client_ = makeHttpConnection(lookupPort("address1")); + auto response = sendRequestAndWaitForResponse( + Http::TestResponseHeaderMapImpl{ + {":method", "GET"}, {":path", "/"}, {":authority", "host"}, {":scheme", "http"}}, + request_size, response_headers, response_size, /*cluster_0*/ 0); + verifyResponse(std::move(response), "200", response_headers, std::string(response_size, 'a')); + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_EQ(request_size, upstream_request_->bodyLength()); + codec_client_->close(); + // Wait for the client to be disconnected. + ASSERT_TRUE(codec_client_->waitForDisconnect()); + + codec_client_ = makeHttpConnection(lookupPort("address2")); + auto response2 = sendRequestAndWaitForResponse( + Http::TestResponseHeaderMapImpl{ + {":method", "GET"}, {":path", "/"}, {":authority", "host"}, {":scheme", "http"}}, + request_size, response_headers, response_size, /*cluster_0*/ 0); + verifyResponse(std::move(response2), "200", response_headers, std::string(response_size, 'a')); + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_EQ(request_size, upstream_request_->bodyLength()); +} + // Tests that a LDS adding listener works as expected. TEST_P(ListenerIntegrationTest, BasicSuccess) { on_server_init_function_ = [&]() { @@ -490,6 +615,162 @@ TEST_P(ListenerIntegrationTest, MultipleLdsUpdatesSharingListenSocketFactory) { } } +// This is multiple addresses version test for the above one. +TEST_P(ListenerMultiAddressesIntegrationTest, + MultipleLdsUpdatesSharingListenSocketFactoryWithMultiAddresses) { + on_server_init_function_ = [&]() { + createLdsStream(); + sendLdsResponse({MessageUtil::getYamlStringFromMessage(listener_config_)}, "1"); + createRdsStream(route_table_name_); + }; + initialize(); + test_server_->waitForCounterGe("listener_manager.lds.update_success", 1); + // testing-listener-0 is not initialized as we haven't pushed any RDS yet. + EXPECT_EQ(test_server_->server().initManager().state(), Init::Manager::State::Initializing); + // Workers not started, the LDS added listener 0 is in active_listeners_ list. + EXPECT_EQ(test_server_->server().listenerManager().listeners().size(), 1); + registerTestServerPorts({"address1", "address2"}); + + const std::string route_config_tmpl = R"EOF( + name: {} + virtual_hosts: + - name: integration + domains: ["*"] + routes: + - match: {{ prefix: "/" }} + route: {{ cluster: {} }} +)EOF"; + sendRdsResponse(fmt::format(route_config_tmpl, route_table_name_, "cluster_0"), "1"); + test_server_->waitForCounterGe( + fmt::format("http.config_test.rds.{}.update_success", route_table_name_), 1); + // Now testing-listener-0 finishes initialization, Server initManager will be ready. + EXPECT_EQ(test_server_->server().initManager().state(), Init::Manager::State::Initialized); + + test_server_->waitUntilListenersReady(); + // NOTE: The line above doesn't tell you if listener is up and listening. + test_server_->waitForCounterGe("listener_manager.listener_create_success", 1); + // Make a connection to the listener from version 1. + codec_client_ = makeHttpConnection(lookupPort("address1")); + + for (int version = 2; version <= 10; version++) { + // Touch the metadata to get a different hash. + (*(*listener_config_.mutable_metadata()->mutable_filter_metadata())["random_filter_name"] + .mutable_fields())["random_key"] + .set_number_value(version); + sendLdsResponse({MessageUtil::getYamlStringFromMessage(listener_config_)}, + absl::StrCat(version)); + sendRdsResponse(fmt::format(route_config_tmpl, route_table_name_, "cluster_0"), + absl::StrCat(version)); + + test_server_->waitForCounterGe("listener_manager.listener_create_success", version); + + // Wait for the client to be disconnected. + ASSERT_TRUE(codec_client_->waitForDisconnect()); + + const uint32_t response_size = 800; + const uint32_t request_size = 10; + Http::TestResponseHeaderMapImpl response_headers{{":status", "200"}, + {"server_id", "cluster_0, backend_0"}}; + + // Make a new connection to the new listener's first address. + codec_client_ = makeHttpConnection(lookupPort("address1")); + auto response = sendRequestAndWaitForResponse( + Http::TestResponseHeaderMapImpl{ + {":method", "GET"}, {":path", "/"}, {":authority", "host"}, {":scheme", "http"}}, + request_size, response_headers, response_size, /*cluster_0*/ 0); + verifyResponse(std::move(response), "200", response_headers, std::string(response_size, 'a')); + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_EQ(request_size, upstream_request_->bodyLength()); + codec_client_->close(); + // Wait for the client to be disconnected. + ASSERT_TRUE(codec_client_->waitForDisconnect()); + + // Make a new connection to the new listener's second address. + codec_client_ = makeHttpConnection(lookupPort("address2")); + auto response2 = sendRequestAndWaitForResponse( + Http::TestResponseHeaderMapImpl{ + {":method", "GET"}, {":path", "/"}, {":authority", "host"}, {":scheme", "http"}}, + request_size, response_headers, response_size, /*cluster_0*/ 0); + verifyResponse(std::move(response2), "200", response_headers, std::string(response_size, 'a')); + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_EQ(request_size, upstream_request_->bodyLength()); + } +} + +TEST_P(ListenerMultiAddressesIntegrationTest, MultipleAddressesListenerInPlaceUpdate) { + on_server_init_function_ = [&]() { + createLdsStream(); + sendLdsResponse({MessageUtil::getYamlStringFromMessage(listener_config_)}, "1"); + createRdsStream(route_table_name_); + }; + setDrainTime(std::chrono::seconds(30)); + initialize(); + test_server_->waitForCounterGe("listener_manager.lds.update_success", 1); + // testing-listener-0 is not initialized as we haven't pushed any RDS yet. + EXPECT_EQ(test_server_->server().initManager().state(), Init::Manager::State::Initializing); + // Workers not started, the LDS added listener 0 is in active_listeners_ list. + EXPECT_EQ(test_server_->server().listenerManager().listeners().size(), 1); + registerTestServerPorts({"address1", "address2"}); + + const std::string route_config_tmpl = R"EOF( + name: {} + virtual_hosts: + - name: integration + domains: ["*"] + routes: + - match: {{ prefix: "/" }} + route: {{ cluster: {} }} +)EOF"; + sendRdsResponse(fmt::format(route_config_tmpl, route_table_name_, "cluster_0"), "1"); + test_server_->waitForCounterGe( + fmt::format("http.config_test.rds.{}.update_success", route_table_name_), 1); + // Now testing-listener-0 finishes initialization, Server initManager will be ready. + EXPECT_EQ(test_server_->server().initManager().state(), Init::Manager::State::Initialized); + + test_server_->waitUntilListenersReady(); + // NOTE: The line above doesn't tell you if listener is up and listening. + test_server_->waitForCounterGe("listener_manager.listener_create_success", 1); + + // Trigger a listener in-place updating. + listener_config_.mutable_filter_chains(0)->mutable_filters(0)->set_name("http_filter"); + sendLdsResponse({MessageUtil::getYamlStringFromMessage(listener_config_)}, "2"); + sendRdsResponse(fmt::format(route_config_tmpl, route_table_name_, "cluster_0"), "2"); + + test_server_->waitForCounterGe("listener_manager.listener_create_success", 2); + test_server_->waitForCounterEq("listener_manager.listener_in_place_updated", 1); + test_server_->waitForGaugeEq("listener_manager.total_filter_chains_draining", 1); + + const uint32_t response_size = 800; + const uint32_t request_size = 10; + Http::TestResponseHeaderMapImpl response_headers{{":status", "200"}, + {"server_id", "cluster_0, backend_0"}}; + + // Make a new connection to the new listener's first address. + codec_client_ = makeHttpConnection(lookupPort("address1")); + + auto response1 = sendRequestAndWaitForResponse( + Http::TestResponseHeaderMapImpl{ + {":method", "GET"}, {":path", "/"}, {":authority", "host"}, {":scheme", "http"}}, + request_size, response_headers, response_size, /*cluster_0*/ 0); + verifyResponse(std::move(response1), "200", response_headers, std::string(response_size, 'a')); + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_EQ(request_size, upstream_request_->bodyLength()); + codec_client_->close(); + // Wait for the client to be disconnected. + ASSERT_TRUE(codec_client_->waitForDisconnect()); + + // Make a new connection to the new listener's second address. + codec_client_ = makeHttpConnection(lookupPort("address2")); + + auto response2 = sendRequestAndWaitForResponse( + Http::TestResponseHeaderMapImpl{ + {":method", "GET"}, {":path", "/"}, {":authority", "host"}, {":scheme", "http"}}, + request_size, response_headers, response_size, /*cluster_0*/ 0); + verifyResponse(std::move(response2), "200", response_headers, std::string(response_size, 'a')); + EXPECT_TRUE(upstream_request_->complete()); + EXPECT_EQ(request_size, upstream_request_->bodyLength()); +} + // Create a listener, then do an in-place update for the listener. // Remove the listener before the filter chain draining is done, // then expect the connection will be reset. diff --git a/test/integration/quic_http_integration_test.cc b/test/integration/quic_http_integration_test.cc index 8511b172d97b0..056fcef762d59 100644 --- a/test/integration/quic_http_integration_test.cc +++ b/test/integration/quic_http_integration_test.cc @@ -131,16 +131,14 @@ class TestEnvoyQuicClientConnection : public EnvoyQuicClientConnection { }; // A test that sets up its own client connection with customized quic version and connection ID. -class QuicHttpIntegrationTest : public HttpIntegrationTest, - public testing::TestWithParam { +class QuicHttpIntegrationTestBase : public HttpIntegrationTest { public: - QuicHttpIntegrationTest() - : HttpIntegrationTest(Http::CodecType::HTTP3, GetParam(), - ConfigHelper::quicHttpProxyConfig()), + QuicHttpIntegrationTestBase(Network::Address::IpVersion version, std::string config) + : HttpIntegrationTest(Http::CodecType::HTTP3, version, config), supported_versions_(quic::CurrentSupportedHttp3Versions()), conn_helper_(*dispatcher_), alarm_factory_(*dispatcher_, *conn_helper_.GetClock()) {} - ~QuicHttpIntegrationTest() override { + ~QuicHttpIntegrationTestBase() override { cleanupUpstreamAndDownstream(); // Release the client before destroying |conn_helper_|. No such need once |conn_helper_| is // moved into a client connection factory in the base test class. @@ -288,13 +286,13 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, } constexpr auto timeout_first = std::chrono::seconds(15); constexpr auto timeout_subsequent = std::chrono::milliseconds(10); - if (GetParam() == Network::Address::IpVersion::v4) { + if (version_ == Network::Address::IpVersion::v4) { test_server_->waitForCounterEq("listener.127.0.0.1_0.downstream_cx_total", 8u, timeout_first); } else { test_server_->waitForCounterEq("listener.[__1]_0.downstream_cx_total", 8u, timeout_first); } for (size_t i = 0; i < concurrency_; ++i) { - if (GetParam() == Network::Address::IpVersion::v4) { + if (version_ == Network::Address::IpVersion::v4) { test_server_->waitForGaugeEq( fmt::format("listener.127.0.0.1_0.worker_{}.downstream_cx_active", i), 1u, timeout_subsequent); @@ -347,10 +345,118 @@ class QuicHttpIntegrationTest : public HttpIntegrationTest, bool validation_failure_on_path_response_{false}; }; +class QuicHttpIntegrationTest : public QuicHttpIntegrationTestBase, + public testing::TestWithParam { +public: + QuicHttpIntegrationTest() + : QuicHttpIntegrationTestBase(GetParam(), ConfigHelper::quicHttpProxyConfig()) {} +}; + +class QuicHttpMultiAddressesIntegrationTest + : public QuicHttpIntegrationTestBase, + public testing::TestWithParam { +public: + QuicHttpMultiAddressesIntegrationTest() + : QuicHttpIntegrationTestBase(GetParam(), ConfigHelper::quicHttpProxyConfig(true)) {} + + void testMultipleQuicConnections() { + // Enabling SO_REUSEPORT with 8 workers. Unfortunately this setting makes the test rarely flaky + // if it is configured to run with --runs_per_test=N where N > 1 but without --jobs=1. + setConcurrency(8); + initialize(); + std::vector addresses({"address1", "address2"}); + registerTestServerPorts(addresses); + std::vector codec_clients1; + std::vector codec_clients2; + for (size_t i = 1; i <= concurrency_; ++i) { + // The BPF filter and ActiveQuicListener::destination() look at the 1st word of connection id + // in the packet header. And currently all QUIC versions support >= 8 bytes connection id. So + // create connections with the first 4 bytes of connection id different from each + // other so they should be evenly distributed. + designated_connection_ids_.push_back(quic::test::TestConnectionId(i << 32)); + // TODO(sunjayBhatia,wrowe): deserialize this, establishing all connections in parallel + // (Expected to save ~14s each across 6 tests on Windows) + codec_clients1.push_back(makeHttpConnection(lookupPort("address1"))); + designated_connection_ids_.push_back(quic::test::TestConnectionId(i << 32)); + codec_clients2.push_back(makeHttpConnection(lookupPort("address2"))); + } + constexpr auto timeout_first = std::chrono::seconds(15); + constexpr auto timeout_subsequent = std::chrono::milliseconds(10); + if (version_ == Network::Address::IpVersion::v4) { + test_server_->waitForCounterEq("listener.127.0.0.1_0.downstream_cx_total", 16u, + timeout_first); + } else { + test_server_->waitForCounterEq("listener.[__1]_0.downstream_cx_total", 16u, timeout_first); + } + for (size_t i = 0; i < concurrency_; ++i) { + if (version_ == Network::Address::IpVersion::v4) { + test_server_->waitForGaugeEq( + fmt::format("listener.127.0.0.1_0.worker_{}.downstream_cx_active", i), 2u, + timeout_subsequent); + test_server_->waitForCounterEq( + fmt::format("listener.127.0.0.1_0.worker_{}.downstream_cx_total", i), 2u, + timeout_subsequent); + } else { + test_server_->waitForGaugeEq( + fmt::format("listener.[__1]_0.worker_{}.downstream_cx_active", i), 2u, + timeout_subsequent); + test_server_->waitForCounterEq( + fmt::format("listener.[__1]_0.worker_{}.downstream_cx_total", i), 2u, + timeout_subsequent); + } + } + for (size_t i = 0; i < concurrency_; ++i) { + fake_upstream_connection_ = nullptr; + upstream_request_ = nullptr; + + auto encoder_decoder = codec_clients1[i]->startRequest( + Http::TestRequestHeaderMapImpl{{":method", "GET"}, + {":path", "/test/long/url"}, + {":scheme", "http"}, + {":authority", "host"}}); + auto& request_encoder = encoder_decoder.first; + auto response = std::move(encoder_decoder.second); + codec_clients1[i]->sendData(request_encoder, 1000, true); + waitForNextUpstreamRequest(); + upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}, + {"set-cookie", "foo"}, + {"set-cookie", "bar"}}, + true); + + ASSERT_TRUE(response->waitForEndStream()); + EXPECT_TRUE(response->complete()); + codec_clients1[i]->close(); + + auto encoder_decoder2 = codec_clients2[i]->startRequest( + Http::TestRequestHeaderMapImpl{{":method", "GET"}, + {":path", "/test/long/url"}, + {":scheme", "http"}, + {":authority", "host"}}); + auto& request_encoder2 = encoder_decoder2.first; + auto response2 = std::move(encoder_decoder2.second); + codec_clients2[i]->sendData(request_encoder2, 1000, true); + waitForNextUpstreamRequest(); + upstream_request_->encodeHeaders(Http::TestResponseHeaderMapImpl{{":status", "200"}, + {"set-cookie", "foo"}, + {"set-cookie", "bar"}}, + true); + + ASSERT_TRUE(response2->waitForEndStream()); + EXPECT_TRUE(response2->complete()); + codec_clients2[i]->close(); + } + } +}; + INSTANTIATE_TEST_SUITE_P(QuicHttpIntegrationTests, QuicHttpIntegrationTest, testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), TestUtility::ipTestParamsToString); +INSTANTIATE_TEST_SUITE_P(QuicHttpMultiAddressesIntegrationTest, + QuicHttpMultiAddressesIntegrationTest, + testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), + TestUtility::ipTestParamsToString); + TEST_P(QuicHttpIntegrationTest, GetRequestAndEmptyResponse) { testRouterHeaderOnlyRequestAndResponse(); } @@ -419,7 +525,7 @@ TEST_P(QuicHttpIntegrationTest, ZeroRtt) { EXPECT_TRUE(quic_session->ssl()->peerCertificateValidated()); // Close the second connection. codec_client_->close(); - if (GetParam() == Network::Address::IpVersion::v4) { + if (version_ == Network::Address::IpVersion::v4) { test_server_->waitForCounterEq( "listener.127.0.0.1_0.http3.downstream.rx.quic_connection_close_error_" "code_QUIC_NO_ERROR", @@ -503,6 +609,11 @@ TEST_P(QuicHttpIntegrationTest, MultipleQuicConnectionsDefaultMode) { testMultipleQuicConnections(); } +// Ensure multiple quic connections work, regardless of platform BPF support +TEST_P(QuicHttpMultiAddressesIntegrationTest, MultipleQuicConnectionsDefaultMode) { + testMultipleQuicConnections(); +} + TEST_P(QuicHttpIntegrationTest, MultipleQuicConnectionsNoBPF) { // Note: This setting is a no-op on platforms without BPF class DisableBpf { @@ -515,6 +626,18 @@ TEST_P(QuicHttpIntegrationTest, MultipleQuicConnectionsNoBPF) { testMultipleQuicConnections(); } +TEST_P(QuicHttpMultiAddressesIntegrationTest, MultipleQuicConnectionsNoBPF) { + // Note: This setting is a no-op on platforms without BPF + class DisableBpf { + public: + DisableBpf() { ActiveQuicListenerFactory::setDisableKernelBpfPacketRoutingForTest(true); } + ~DisableBpf() { ActiveQuicListenerFactory::setDisableKernelBpfPacketRoutingForTest(false); } + }; + + DisableBpf disable; + testMultipleQuicConnections(); +} + // Tests that the packets from a connection with CID longer than 8 bytes are routed to the same // worker. TEST_P(QuicHttpIntegrationTest, MultiWorkerWithLongConnectionId) { diff --git a/test/integration/server.cc b/test/integration/server.cc index 2985c30d69be8..0af5bec20d082 100644 --- a/test/integration/server.cc +++ b/test/integration/server.cc @@ -132,7 +132,7 @@ void IntegrationTestServer::start( if (tap_path) { std::vector ports; for (auto listener : server().listenerManager().listeners()) { - const auto listen_addr = listener.get().listenSocketFactory().localAddress(); + const auto listen_addr = listener.get().listenSocketFactories()[0]->localAddress(); if (listen_addr->type() == Network::Address::Type::Ip) { ports.push_back(listen_addr->ip()->port()); } diff --git a/test/mocks/network/mocks.cc b/test/mocks/network/mocks.cc index f27c1738ed368..ba72b0ea1f01a 100644 --- a/test/mocks/network/mocks.cc +++ b/test/mocks/network/mocks.cc @@ -27,18 +27,20 @@ namespace Network { MockUdpListenerConfig::MockUdpListenerConfig() : udp_listener_worker_router_(std::make_unique(1)) { - ON_CALL(*this, listenerWorkerRouter()).WillByDefault(ReturnRef(*udp_listener_worker_router_)); + ON_CALL(*this, listenerWorkerRouter(_)).WillByDefault(ReturnRef(*udp_listener_worker_router_)); ON_CALL(*this, config()).WillByDefault(ReturnRef(config_)); } MockUdpListenerConfig::~MockUdpListenerConfig() = default; MockListenerConfig::MockListenerConfig() : socket_(std::make_shared>()) { + socket_factories_.emplace_back(std::make_unique()); ON_CALL(*this, filterChainFactory()).WillByDefault(ReturnRef(filter_chain_factory_)); - ON_CALL(*this, listenSocketFactory()).WillByDefault(ReturnRef(socket_factory_)); - ON_CALL(socket_factory_, localAddress()) + ON_CALL(*this, listenSocketFactories()).WillByDefault(ReturnRef(socket_factories_)); + ON_CALL(*static_cast(socket_factories_[0].get()), localAddress()) .WillByDefault(ReturnRef(socket_->connectionInfoProvider().localAddress())); - ON_CALL(socket_factory_, getListenSocket(_)).WillByDefault(Return(socket_)); + ON_CALL(*static_cast(socket_factories_[0].get()), getListenSocket(_)) + .WillByDefault(Return(socket_)); ON_CALL(*this, listenerScope()).WillByDefault(ReturnRef(scope_)); ON_CALL(*this, name()).WillByDefault(ReturnRef(name_)); } diff --git a/test/mocks/network/mocks.h b/test/mocks/network/mocks.h index 27c5799b00f9a..b45245fff7912 100644 --- a/test/mocks/network/mocks.h +++ b/test/mocks/network/mocks.h @@ -412,7 +412,7 @@ class MockUdpListenerConfig : public UdpListenerConfig { MOCK_METHOD(ActiveUdpListenerFactory&, listenerFactory, ()); MOCK_METHOD(UdpPacketWriterFactory&, packetWriterFactory, ()); - MOCK_METHOD(UdpListenerWorkerRouter&, listenerWorkerRouter, ()); + MOCK_METHOD(UdpListenerWorkerRouter&, listenerWorkerRouter, (const Network::Address::Instance&)); MOCK_METHOD(const envoy::config::listener::v3::UdpListenerConfig&, config, ()); UdpListenerWorkerRouterPtr udp_listener_worker_router_; @@ -426,8 +426,8 @@ class MockListenerConfig : public ListenerConfig { MOCK_METHOD(FilterChainManager&, filterChainManager, ()); MOCK_METHOD(FilterChainFactory&, filterChainFactory, ()); - MOCK_METHOD(ListenSocketFactory&, listenSocketFactory, ()); - MOCK_METHOD(bool, bindToPort, ()); + MOCK_METHOD(std::vector&, listenSocketFactories, ()); + MOCK_METHOD(bool, bindToPort, (), (const)); MOCK_METHOD(bool, handOffRestoredDestinationConnections, (), (const)); MOCK_METHOD(uint32_t, perConnectionBufferLimitBytes, (), (const)); MOCK_METHOD(std::chrono::milliseconds, listenerFiltersTimeout, (), (const)); @@ -437,7 +437,7 @@ class MockListenerConfig : public ListenerConfig { MOCK_METHOD(const std::string&, name, (), (const)); MOCK_METHOD(Network::UdpListenerConfigOptRef, udpListenerConfig, ()); MOCK_METHOD(InternalListenerConfigOptRef, internalListenerConfig, ()); - MOCK_METHOD(ConnectionBalancer&, connectionBalancer, ()); + MOCK_METHOD(ConnectionBalancer&, connectionBalancer, (const Network::Address::Instance&)); MOCK_METHOD(ResourceLimit&, openConnections, ()); MOCK_METHOD(uint32_t, tcpBacklogSize, (), (const)); MOCK_METHOD(Init::Manager&, initManager, ()); @@ -452,7 +452,7 @@ class MockListenerConfig : public ListenerConfig { } testing::NiceMock filter_chain_factory_; - MockListenSocketFactory socket_factory_; + std::vector socket_factories_; SocketSharedPtr socket_; Stats::IsolatedStoreImpl scope_; std::string name_; diff --git a/test/mocks/server/worker.cc b/test/mocks/server/worker.cc index f20c6564b90a5..2e2559853ea43 100644 --- a/test/mocks/server/worker.cc +++ b/test/mocks/server/worker.cc @@ -17,7 +17,7 @@ MockWorker::MockWorker() { Network::ListenerConfig& config, AddListenerCompletion completion, Runtime::Loader&) -> void { UNREFERENCED_PARAMETER(overridden_listener); - config.listenSocketFactory().getListenSocket(0); + config.listenSocketFactories()[0]->getListenSocket(0); EXPECT_EQ(nullptr, add_listener_completion_); add_listener_completion_ = completion; })); diff --git a/test/server/active_tcp_listener_test.cc b/test/server/active_tcp_listener_test.cc index 159b04338efaa..bf19d9a19b156 100644 --- a/test/server/active_tcp_listener_test.cc +++ b/test/server/active_tcp_listener_test.cc @@ -36,7 +36,7 @@ class MockTcpConnectionHandler : public Network::TcpConnectionHandler, public: MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); MOCK_METHOD(Network::BalancedConnectionHandlerOptRef, getBalancedHandlerByTag, - (uint64_t listener_tag)); + (uint64_t listener_tag, const Network::Address::Instance&)); MOCK_METHOD(Network::BalancedConnectionHandlerOptRef, getBalancedHandlerByAddress, (const Network::Address::Instance& address)); }; @@ -51,7 +51,6 @@ class ActiveTcpListenerTest : public testing::Test, protected Logger::Loggable>(); EXPECT_CALL(*generic_listener_, onDestroy()); - generic_active_listener_ = std::make_unique( - conn_handler_, std::move(generic_listener_), listener_config_, runtime_); + Network::Address::InstanceConstSharedPtr address( + new Network::Address::Ipv4Instance("127.0.0.1", 10001)); + generic_active_listener_ = + std::make_unique(conn_handler_, std::move(generic_listener_), address, + listener_config_, balancer_, runtime_); generic_active_listener_->incNumConnections(); generic_accepted_socket_ = std::make_unique>(); EXPECT_CALL(*generic_accepted_socket_, ioHandle()).WillRepeatedly(ReturnRef(io_handle_)); @@ -90,8 +92,11 @@ class ActiveTcpListenerTest : public testing::Test, protected Logger::Loggable>(); EXPECT_CALL(*generic_listener_, onDestroy()); - generic_active_listener_ = std::make_unique( - conn_handler_, std::move(generic_listener_), listener_config_, runtime_); + Network::Address::InstanceConstSharedPtr address( + new Network::Address::Ipv4Instance("127.0.0.1", 10001)); + generic_active_listener_ = + std::make_unique(conn_handler_, std::move(generic_listener_), address, + listener_config_, balancer_, runtime_); generic_active_listener_->incNumConnections(); generic_accepted_socket_ = std::make_unique>(); EXPECT_CALL(*generic_accepted_socket_, ioHandle()).WillRepeatedly(ReturnRef(io_handle_)); @@ -220,8 +225,11 @@ TEST_F(ActiveTcpListenerTest, ListenerFilterWithInspectDataMultipleFilters) { auto listener = std::make_unique>(); EXPECT_CALL(*listener, onDestroy()); - auto active_listener = std::make_unique(conn_handler_, std::move(listener), - listener_config_, runtime_); + Network::NopConnectionBalancerImpl balancer; + Network::Address::InstanceConstSharedPtr address( + new Network::Address::Ipv4Instance("127.0.0.1", 10001)); + auto active_listener = std::make_unique( + conn_handler_, std::move(listener), address, listener_config_, balancer, runtime_); auto accepted_socket = std::make_unique>(); EXPECT_CALL(*accepted_socket, ioHandle()).WillRepeatedly(ReturnRef(io_handle_)); @@ -309,8 +317,11 @@ TEST_F(ActiveTcpListenerTest, ListenerFilterWithInspectDataMultipleFilters2) { auto listener = std::make_unique>(); EXPECT_CALL(*listener, onDestroy()); - auto active_listener = std::make_unique(conn_handler_, std::move(listener), - listener_config_, runtime_); + Network::NopConnectionBalancerImpl balancer; + Network::Address::InstanceConstSharedPtr address( + new Network::Address::Ipv4Instance("127.0.0.1", 10001)); + auto active_listener = std::make_unique( + conn_handler_, std::move(listener), address, listener_config_, balancer, runtime_); auto accepted_socket = std::make_unique>(); EXPECT_CALL(*accepted_socket, ioHandle()).WillRepeatedly(ReturnRef(io_handle_)); @@ -477,7 +488,6 @@ TEST_F(ActiveTcpListenerTest, RedirectedRebalancer) { Network::Address::InstanceConstSharedPtr normal_address( new Network::Address::Ipv4Instance("127.0.0.1", 10001)); EXPECT_CALL(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(normal_address)); - EXPECT_CALL(listener_config1, connectionBalancer()).WillRepeatedly(ReturnRef(balancer1)); EXPECT_CALL(listener_config1, listenerScope).Times(testing::AnyNumber()); EXPECT_CALL(listener_config1, listenerFiltersTimeout()); EXPECT_CALL(listener_config1, continueOnListenerFiltersTimeout()); @@ -488,8 +498,9 @@ TEST_F(ActiveTcpListenerTest, RedirectedRebalancer) { auto mock_listener_will_be_moved1 = std::make_unique(); auto& listener1 = *mock_listener_will_be_moved1; - auto active_listener1 = std::make_unique( - conn_handler_, std::move(mock_listener_will_be_moved1), listener_config1, runtime_); + auto active_listener1 = + std::make_unique(conn_handler_, std::move(mock_listener_will_be_moved1), + normal_address, listener_config1, balancer1, runtime_); NiceMock listener_config2; Network::MockConnectionBalancer balancer2; @@ -500,7 +511,6 @@ TEST_F(ActiveTcpListenerTest, RedirectedRebalancer) { new Network::Address::Ipv4Instance("127.0.0.2", 20002)); EXPECT_CALL(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(alt_address)); EXPECT_CALL(listener_config2, listenerFiltersTimeout()); - EXPECT_CALL(listener_config2, connectionBalancer()).WillRepeatedly(ReturnRef(balancer2)); EXPECT_CALL(listener_config2, listenerScope).Times(testing::AnyNumber()); EXPECT_CALL(listener_config2, handOffRestoredDestinationConnections()) .WillRepeatedly(Return(false)); @@ -509,8 +519,9 @@ TEST_F(ActiveTcpListenerTest, RedirectedRebalancer) { EXPECT_CALL(listener_config2, openConnections()).WillRepeatedly(ReturnRef(resource_limit_)); auto mock_listener_will_be_moved2 = std::make_unique(); auto& listener2 = *mock_listener_will_be_moved2; - auto active_listener2 = std::make_shared( - conn_handler_, std::move(mock_listener_will_be_moved2), listener_config2, runtime_); + auto active_listener2 = + std::make_shared(conn_handler_, std::move(mock_listener_will_be_moved2), + alt_address, listener_config2, balancer2, runtime_); auto* test_filter = new NiceMock(); EXPECT_CALL(*test_filter, destroy_()); @@ -582,6 +593,89 @@ TEST_F(ActiveTcpListenerTest, RedirectedRebalancer) { EXPECT_CALL(listener2, onDestroy()); active_listener2.reset(); } + +TEST_F(ActiveTcpListenerTest, Rebalance) { + NiceMock listener_config1; + NiceMock balancer1; + EXPECT_CALL(balancer1, registerHandler(_)).Times(2); + EXPECT_CALL(balancer1, unregisterHandler(_)).Times(2); + + Network::Address::InstanceConstSharedPtr normal_address( + new Network::Address::Ipv4Instance("127.0.0.1", 10001)); + EXPECT_CALL(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(normal_address)); + EXPECT_CALL(listener_config1, listenerScope).Times(testing::AnyNumber()); + EXPECT_CALL(listener_config1, listenerFiltersTimeout()); + EXPECT_CALL(listener_config1, continueOnListenerFiltersTimeout()); + EXPECT_CALL(listener_config1, filterChainManager()).WillRepeatedly(ReturnRef(manager_)); + EXPECT_CALL(listener_config1, openConnections()).WillRepeatedly(ReturnRef(resource_limit_)); + EXPECT_CALL(listener_config1, handOffRestoredDestinationConnections()) + .WillRepeatedly(Return(true)); + + auto mock_listener_will_be_moved1 = std::make_unique(); + auto& listener1 = *mock_listener_will_be_moved1; + auto active_listener1 = + std::make_unique(conn_handler_, std::move(mock_listener_will_be_moved1), + normal_address, listener_config1, balancer1, runtime_); + + NiceMock listener_config2; + + EXPECT_CALL(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(normal_address)); + EXPECT_CALL(listener_config2, listenerFiltersTimeout()); + EXPECT_CALL(listener_config2, listenerScope).Times(testing::AnyNumber()); + EXPECT_CALL(listener_config2, handOffRestoredDestinationConnections()) + .WillRepeatedly(Return(false)); + EXPECT_CALL(listener_config2, continueOnListenerFiltersTimeout()); + EXPECT_CALL(listener_config2, filterChainManager()).WillRepeatedly(ReturnRef(manager_)); + EXPECT_CALL(listener_config2, openConnections()).WillRepeatedly(ReturnRef(resource_limit_)); + auto mock_listener_will_be_moved2 = std::make_unique(); + auto& listener2 = *mock_listener_will_be_moved2; + auto active_listener2 = + std::make_shared(conn_handler_, std::move(mock_listener_will_be_moved2), + normal_address, listener_config2, balancer1, runtime_); + Network::MockConnectionSocket* accepted_socket = new NiceMock(); + + // active_listener1 re-balance. Set the balance target to the the active_listener2. + EXPECT_CALL(balancer1, pickTargetHandler(_)) + .WillOnce(testing::DoAll(testing::WithArg<0>(Invoke([&active_listener2](auto&) { + active_listener2->incNumConnections(); + })), + ReturnRef(*active_listener2))); + + EXPECT_CALL(conn_handler_, getBalancedHandlerByTag) + .WillOnce(Invoke([&normal_address, + &active_listener2](uint64_t, const Network::Address::Instance& address) { + EXPECT_EQ(address, *normal_address); + return Network::BalancedConnectionHandlerOptRef(*active_listener2); + })); + auto filter_factory_callback = std::make_shared>(); + auto transport_socket_factory = Network::Test::createRawBufferSocketFactory(); + filter_chain_ = std::make_shared>(); + + EXPECT_CALL(conn_handler_, incNumConnections()); + EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(filter_chain_.get())); + EXPECT_CALL(*filter_chain_, transportSocketFactory) + .WillOnce(testing::ReturnRef(*transport_socket_factory)); + EXPECT_CALL(*filter_chain_, networkFilterFactories).WillOnce(ReturnRef(*filter_factory_callback)); + EXPECT_CALL(listener_config2, filterChainFactory()) + .WillRepeatedly(ReturnRef(filter_chain_factory_)); + + auto* connection = new NiceMock(); + EXPECT_CALL(dispatcher_, createServerConnection_()).WillOnce(Return(connection)); + EXPECT_CALL(filter_chain_factory_, createNetworkFilterChain(_, _)).WillOnce(Return(true)); + active_listener1->onAccept(Network::ConnectionSocketPtr{accepted_socket}); + + // Verify per-listener connection stats. + EXPECT_EQ(1UL, conn_handler_.numConnections()); + + EXPECT_CALL(conn_handler_, decNumConnections()); + connection->close(Network::ConnectionCloseType::NoFlush); + + EXPECT_CALL(listener1, onDestroy()); + active_listener1.reset(); + EXPECT_CALL(listener2, onDestroy()); + active_listener2.reset(); +} + } // namespace } // namespace Server } // namespace Envoy diff --git a/test/server/active_udp_listener_test.cc b/test/server/active_udp_listener_test.cc index 7337c0d2e5550..ba537c0a4d61d 100644 --- a/test/server/active_udp_listener_test.cc +++ b/test/server/active_udp_listener_test.cc @@ -30,7 +30,7 @@ class MockUdpConnectionHandler : public Network::UdpConnectionHandler, public: MOCK_METHOD(Event::Dispatcher&, dispatcher, ()); MOCK_METHOD(Network::UdpListenerCallbacksOptRef, getUdpListenerCallbacks, - (uint64_t listener_tag)); + (uint64_t listener_tag, const Network::Address::Instance& address)); }; class ActiveUdpListenerTest : public testing::TestWithParam, @@ -50,9 +50,12 @@ class ActiveUdpListenerTest : public testing::TestWithParamaddOptions(Network::SocketOptionFactory::buildRxQueueOverFlowOptions()); ASSERT_TRUE(Network::Socket::applyOptions(listen_socket_->options(), *listen_socket_, envoy::config::core::v3::SocketOption::STATE_BOUND)); - - ON_CALL(socket_factory_, getListenSocket(_)).WillByDefault(Return(listen_socket_)); - EXPECT_CALL(listener_config_, listenSocketFactory()).WillRepeatedly(ReturnRef(socket_factory_)); + socket_factories_.emplace_back(std::make_unique()); + ON_CALL(*static_cast(socket_factories_[0].get()), + getListenSocket(_)) + .WillByDefault(Return(listen_socket_)); + EXPECT_CALL(listener_config_, listenSocketFactories()) + .WillRepeatedly(ReturnRef(socket_factories_)); // Use UdpGsoBatchWriter to perform non-batched writes for the purpose of this test, if it is // supported. @@ -77,8 +80,8 @@ class ActiveUdpListenerTest : public testing::TestWithParam(0, 1, conn_handler_, dispatcher_, listener_config_); + active_listener_ = std::make_unique(0, 1, conn_handler_, listen_socket_, + dispatcher_, listener_config_); } std::string listener_stat_prefix_{"listener_stat_prefix"}; @@ -87,7 +90,7 @@ class ActiveUdpListenerTest : public testing::TestWithParam socket_factory_; + std::vector socket_factories_; Stats::IsolatedStoreImpl scope_; NiceMock udp_listener_config_; NiceMock udp_packet_writer_factory_; diff --git a/test/server/connection_handler_test.cc b/test/server/connection_handler_test.cc index 9fba04c607ca2..859d558e30aa7 100644 --- a/test/server/connection_handler_test.cc +++ b/test/server/connection_handler_test.cc @@ -74,7 +74,7 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable> filter_chain_manager = nullptr, uint32_t tcp_backlog_size = ENVOY_TCP_BACKLOG_SIZE, Network::ConnectionBalancerSharedPtr connection_balancer = nullptr, - bool ignore_global_conn_limit = false) + bool ignore_global_conn_limit = false, int num_of_socket_factories = 1) : parent_(parent), socket_(std::make_shared>()), tag_(tag), bind_to_port_(bind_to_port), tcp_backlog_size_(tcp_backlog_size), hand_off_restored_destination_connections_(hand_off_restored_destination_connections), @@ -85,6 +85,9 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable()); + } envoy::config::listener::v3::UdpListenerConfig udp_config; udp_listener_config_ = std::make_unique(udp_config); udp_listener_config_->listener_factory_ = @@ -100,7 +103,8 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable(socket_factories_[index].get()); + } Network::FilterChainFactory& filterChainFactory() override { return parent_.factory_; } - Network::ListenSocketFactory& listenSocketFactory() override { return socket_factory_; } - bool bindToPort() override { return bind_to_port_; } + const std::vector& listenSocketFactories() override { + return socket_factories_; + } + bool bindToPort() const override { return bind_to_port_; } bool handOffRestoredDestinationConnections() const override { return hand_off_restored_destination_connections_; } @@ -177,7 +187,9 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable& accessLogs() const override { return access_logs_; } @@ -192,7 +204,7 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable> socket_; - Network::MockListenSocketFactory socket_factory_; + std::vector socket_factories_; uint64_t tag_; bool bind_to_port_; const uint32_t tcp_backlog_size_; @@ -254,6 +266,7 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable connection_balancer = nullptr, Network::BalancedConnectionHandler** balanced_connection_handler = nullptr, Network::Socket::Type socket_type = Network::Socket::Type::Stream, @@ -273,8 +286,15 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggablesocket_factory_, socketType()).WillOnce(Return(socket_type)); - EXPECT_CALL(listeners_.back()->socket_factory_, getListenSocket(_)) + EXPECT_CALL(listeners_.back()->socketFactory(), socketType()).WillOnce(Return(socket_type)); + if (address == nullptr) { + EXPECT_CALL(listeners_.back()->socketFactory(), localAddress()) + .WillRepeatedly(ReturnRef(local_address_)); + } else { + EXPECT_CALL(listeners_.back()->socketFactory(), localAddress()) + .WillRepeatedly(ReturnRef(address)); + } + EXPECT_CALL(listeners_.back()->socketFactory(), getListenSocket(_)) .WillOnce(Return(listeners_.back()->socket_)); if (socket_type == Network::Socket::Type::Stream) { EXPECT_CALL(dispatcher_, createListener_(_, _, _, _, _)) @@ -307,6 +327,43 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable& mock_listeners, + std::vector& addresses, + Network::TcpListenerCallbacks** listener_callbacks = nullptr, bool disable_listener = false) { + listeners_.emplace_back(std::make_unique( + *this, tag, bind_to_port, hand_off_restored_destination_connections, name, + Network::Socket::Type::Stream, std::chrono::milliseconds(15000), false, access_log_, + nullptr, ENVOY_TCP_BACKLOG_SIZE, nullptr, false, mock_listeners.size())); + + EXPECT_CALL(listeners_.back()->socketFactory(0), socketType()) + .WillOnce(Return(Network::Socket::Type::Stream)); + for (std::vector::size_type i = 0; i < mock_listeners.size(); i++) { + EXPECT_CALL(listeners_.back()->socketFactory(i), localAddress()) + .WillRepeatedly(ReturnRef(addresses[i])); + EXPECT_CALL(listeners_.back()->socketFactory(i), getListenSocket(_)) + .WillOnce(Return(listeners_.back()->socket_)); + + EXPECT_CALL(dispatcher_, createListener_(_, _, _, _, _)) + .WillOnce(Invoke([i, &mock_listeners, listener_callbacks]( + Network::SocketSharedPtr&&, Network::TcpListenerCallbacks& cb, + Runtime::Loader&, bool, bool) -> Network::Listener* { + if (listener_callbacks != nullptr) { + *listener_callbacks = &cb; + } + return mock_listeners[i]; + })) + .RetiresOnSaturation(); + + if (disable_listener) { + EXPECT_CALL(*static_cast(mock_listeners[i]), disable()); + } + } + + return listeners_.back().get(); + } + TestListener* addInternalListener( uint64_t tag, const std::string& name, std::chrono::milliseconds listener_filters_timeout = std::chrono::milliseconds(15000), @@ -332,7 +389,7 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggableip()->port()); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) + EXPECT_CALL(test_listener->socketFactory(), localAddress()) .WillRepeatedly(ReturnRef(any_address)); handler_->addListener(absl::nullopt, *test_listener, runtime_); @@ -387,7 +444,8 @@ class ConnectionHandlerTest : public testing::Test, protected Logger::Loggable(); Network::BalancedConnectionHandler* current_handler; TestListener* test_listener = - addListener(1, true, false, "test_listener", listener, &listener_callbacks, + addListener(1, true, false, "test_listener", listener, &listener_callbacks, nullptr, connection_balancer, ¤t_handler); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); // Fake a balancer posting a connection to us. @@ -435,24 +491,20 @@ TEST_F(ConnectionHandlerTest, RemoveListenerDuringRebalance) { TEST_F(ConnectionHandlerTest, ListenerConnectionLimitEnforced) { Network::TcpListenerCallbacks* listener_callbacks1; auto listener1 = new NiceMock(); - TestListener* test_listener1 = - addListener(1, false, false, "test_listener1", listener1, &listener_callbacks1); Network::Address::InstanceConstSharedPtr normal_address( new Network::Address::Ipv4Instance("127.0.0.1", 10001)); - EXPECT_CALL(test_listener1->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(normal_address)); + TestListener* test_listener1 = addListener(1, false, false, "test_listener1", listener1, + &listener_callbacks1, normal_address); // Only allow a single connection on this listener. test_listener1->setMaxConnections(1); handler_->addListener(absl::nullopt, *test_listener1, runtime_); auto listener2 = new NiceMock(); Network::TcpListenerCallbacks* listener_callbacks2; - TestListener* test_listener2 = - addListener(2, false, false, "test_listener2", listener2, &listener_callbacks2); Network::Address::InstanceConstSharedPtr alt_address( new Network::Address::Ipv4Instance("127.0.0.2", 20002)); - EXPECT_CALL(test_listener2->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(alt_address)); + TestListener* test_listener2 = + addListener(2, false, false, "test_listener2", listener2, &listener_callbacks2, alt_address); // Do not allow any connections on this listener. test_listener2->setMaxConnections(0); handler_->addListener(absl::nullopt, *test_listener2, runtime_); @@ -524,8 +576,6 @@ TEST_F(ConnectionHandlerTest, RemoveListener) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, true, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); Network::MockConnectionSocket* connection = new NiceMock(); @@ -548,6 +598,47 @@ TEST_F(ConnectionHandlerTest, RemoveListener) { handler_->removeListeners(0); } +TEST_F(ConnectionHandlerTest, RemoveListenerWithMultiAddrs) { + InSequence s; + + Network::TcpListenerCallbacks* listener_callbacks; + auto listener1 = new NiceMock(); + auto listener2 = new NiceMock(); + std::vector mock_listeners; + mock_listeners.emplace_back(listener1); + mock_listeners.emplace_back(listener2); + Network::Address::InstanceConstSharedPtr address1( + new Network::Address::Ipv4Instance("127.0.0.1", 80, nullptr)); + Network::Address::InstanceConstSharedPtr address2( + new Network::Address::Ipv4Instance("127.0.0.2", 80, nullptr)); + std::vector addresses; + addresses.emplace_back(address1); + addresses.emplace_back(address2); + TestListener* test_listener = addMultiAddrsListener( + 1, true, false, "test_listener", mock_listeners, addresses, &listener_callbacks); + handler_->addListener(absl::nullopt, *test_listener, runtime_); + + Network::MockConnectionSocket* connection = new NiceMock(); + EXPECT_CALL(*access_log_, log(_, _, _, _)); + listener_callbacks->onAccept(Network::ConnectionSocketPtr{connection}); + EXPECT_EQ(0UL, handler_->numConnections()); + + // Test stop/remove of not existent listener. + handler_->stopListeners(0); + handler_->removeListeners(0); + + EXPECT_CALL(*listener1, onDestroy()); + EXPECT_CALL(*listener2, onDestroy()); + handler_->stopListeners(1); + EXPECT_CALL(dispatcher_, clearDeferredDeleteList()).Times(2); + handler_->removeListeners(1); + EXPECT_EQ(0UL, handler_->numConnections()); + + // Test stop/remove of not existent listener. + handler_->stopListeners(0); + handler_->removeListeners(0); +} + TEST_F(ConnectionHandlerTest, DisableListener) { InSequence s; @@ -555,8 +646,6 @@ TEST_F(ConnectionHandlerTest, DisableListener) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, false, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); EXPECT_CALL(*listener, disable()); @@ -565,6 +654,32 @@ TEST_F(ConnectionHandlerTest, DisableListener) { handler_->disableListeners(); } +TEST_F(ConnectionHandlerTest, DisableListenerWithMultiAddrs) { + Network::TcpListenerCallbacks* listener_callbacks; + auto listener1 = new NiceMock(); + auto listener2 = new NiceMock(); + std::vector mock_listeners; + mock_listeners.emplace_back(listener1); + mock_listeners.emplace_back(listener2); + Network::Address::InstanceConstSharedPtr address1( + new Network::Address::Ipv4Instance("127.0.0.1", 80, nullptr)); + Network::Address::InstanceConstSharedPtr address2( + new Network::Address::Ipv4Instance("127.0.0.2", 80, nullptr)); + std::vector addresses; + addresses.emplace_back(address1); + addresses.emplace_back(address2); + TestListener* test_listener = addMultiAddrsListener( + 1, false, false, "test_listener", mock_listeners, addresses, &listener_callbacks); + handler_->addListener(absl::nullopt, *test_listener, runtime_); + + EXPECT_CALL(*listener1, disable()); + EXPECT_CALL(*listener2, disable()); + EXPECT_CALL(*listener1, onDestroy()); + EXPECT_CALL(*listener2, onDestroy()); + + handler_->disableListeners(); +} + // Envoy doesn't have such case yet, just ensure the code won't break with it. TEST_F(ConnectionHandlerTest, StopAndDisableStoppedListener) { InSequence s; @@ -573,8 +688,6 @@ TEST_F(ConnectionHandlerTest, StopAndDisableStoppedListener) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, false, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); EXPECT_CALL(*listener, onDestroy()); @@ -595,14 +708,35 @@ TEST_F(ConnectionHandlerTest, AddDisabledListener) { TestListener* test_listener = addListener(1, false, false, "test_listener", listener, &listener_callbacks); EXPECT_CALL(*listener, disable()); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); EXPECT_CALL(*listener, onDestroy()); handler_->disableListeners(); handler_->addListener(absl::nullopt, *test_listener, runtime_); } +TEST_F(ConnectionHandlerTest, AddDisabledListenerWithMultiAddrs) { + Network::TcpListenerCallbacks* listener_callbacks; + auto listener1 = new NiceMock(); + auto listener2 = new NiceMock(); + std::vector mock_listeners; + mock_listeners.emplace_back(listener1); + mock_listeners.emplace_back(listener2); + Network::Address::InstanceConstSharedPtr address1( + new Network::Address::Ipv4Instance("127.0.0.1", 80, nullptr)); + Network::Address::InstanceConstSharedPtr address2( + new Network::Address::Ipv4Instance("127.0.0.2", 80, nullptr)); + std::vector addresses; + addresses.emplace_back(address1); + addresses.emplace_back(address2); + TestListener* test_listener = addMultiAddrsListener( + 1, false, false, "test_listener", mock_listeners, addresses, &listener_callbacks, true); + EXPECT_CALL(*listener1, onDestroy()); + EXPECT_CALL(*listener2, onDestroy()); + + handler_->disableListeners(); + handler_->addListener(absl::nullopt, *test_listener, runtime_); +} + TEST_F(ConnectionHandlerTest, SetListenerRejectFraction) { InSequence s; @@ -610,8 +744,6 @@ TEST_F(ConnectionHandlerTest, SetListenerRejectFraction) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, false, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); EXPECT_CALL(*listener, setRejectFraction(UnitFloat(0.1234f))); @@ -628,8 +760,6 @@ TEST_F(ConnectionHandlerTest, AddListenerSetRejectFraction) { TestListener* test_listener = addListener(1, false, false, "test_listener", listener, &listener_callbacks); EXPECT_CALL(*listener, setRejectFraction(UnitFloat(0.12345f))); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); EXPECT_CALL(*listener, onDestroy()); handler_->setListenerRejectFraction(UnitFloat(0.12345f)); @@ -644,8 +774,6 @@ TEST_F(ConnectionHandlerTest, SetsTransportSocketConnectTimeout) { TestListener* test_listener = addListener(1, false, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); auto server_connection = new NiceMock(); @@ -670,8 +798,6 @@ TEST_F(ConnectionHandlerTest, DestroyCloseConnections) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, true, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); Network::MockConnectionSocket* connection = new NiceMock(); @@ -691,8 +817,6 @@ TEST_F(ConnectionHandlerTest, CloseDuringFilterChainCreate) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, true, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(filter_chain_.get())); @@ -716,8 +840,6 @@ TEST_F(ConnectionHandlerTest, CloseConnectionOnEmptyFilterChain) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, true, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(filter_chain_.get())); @@ -737,22 +859,18 @@ TEST_F(ConnectionHandlerTest, CloseConnectionOnEmptyFilterChain) { TEST_F(ConnectionHandlerTest, NormalRedirect) { Network::TcpListenerCallbacks* listener_callbacks1; auto listener1 = new NiceMock(); - TestListener* test_listener1 = - addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1); Network::Address::InstanceConstSharedPtr normal_address( new Network::Address::Ipv4Instance("127.0.0.1", 10001)); - EXPECT_CALL(test_listener1->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(normal_address)); + TestListener* test_listener1 = + addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1, normal_address); handler_->addListener(absl::nullopt, *test_listener1, runtime_); Network::TcpListenerCallbacks* listener_callbacks2; auto listener2 = new NiceMock(); - TestListener* test_listener2 = - addListener(2, false, false, "test_listener2", listener2, &listener_callbacks2); Network::Address::InstanceConstSharedPtr alt_address( new Network::Address::Ipv4Instance("127.0.0.2", 20002)); - EXPECT_CALL(test_listener2->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(alt_address)); + TestListener* test_listener2 = + addListener(2, false, false, "test_listener2", listener2, &listener_callbacks2, alt_address); handler_->addListener(absl::nullopt, *test_listener2, runtime_); auto* test_filter = new NiceMock(); @@ -801,6 +919,70 @@ TEST_F(ConnectionHandlerTest, NormalRedirect) { EXPECT_CALL(*listener1, onDestroy()); } +TEST_F(ConnectionHandlerTest, NormalRedirectWithMultiAddrs) { + Network::TcpListenerCallbacks* listener_callbacks1; + auto listener1 = new NiceMock(); + auto listener2 = new NiceMock(); + std::vector mock_listeners; + mock_listeners.emplace_back(listener1); + mock_listeners.emplace_back(listener2); + Network::Address::InstanceConstSharedPtr normal_address( + new Network::Address::Ipv4Instance("127.0.0.1", 10001, nullptr)); + Network::Address::InstanceConstSharedPtr alt_address( + new Network::Address::Ipv4Instance("127.0.0.2", 20002, nullptr)); + std::vector addresses; + addresses.emplace_back(normal_address); + addresses.emplace_back(alt_address); + TestListener* test_listener1 = addMultiAddrsListener( + 1, true, true, "test_listener1", mock_listeners, addresses, &listener_callbacks1); + handler_->addListener(absl::nullopt, *test_listener1, runtime_); + + auto* test_filter = new NiceMock(); + EXPECT_CALL(*test_filter, destroy_()); + Network::MockConnectionSocket* accepted_socket = new NiceMock(); + bool redirected = false; + EXPECT_CALL(factory_, createListenerFilterChain(_)) + .WillRepeatedly(Invoke([&](Network::ListenerFilterManager& manager) -> bool { + // Insert the Mock filter. + if (!redirected) { + manager.addAcceptFilter(nullptr, Network::ListenerFilterPtr{test_filter}); + redirected = true; + } + return true; + })); + EXPECT_CALL(*test_filter, onAccept(_)) + .WillOnce(Invoke([&](Network::ListenerFilterCallbacks& cb) -> Network::FilterStatus { + cb.socket().connectionInfoProvider().restoreLocalAddress(alt_address); + return Network::FilterStatus::Continue; + })); + EXPECT_CALL(manager_, findFilterChain(_)).WillOnce(Return(filter_chain_.get())); + auto* connection = new NiceMock(); + EXPECT_CALL(dispatcher_, createServerConnection_()).WillOnce(Return(connection)); + EXPECT_CALL(factory_, createNetworkFilterChain(_, _)).WillOnce(Return(true)); + listener_callbacks1->onAccept(Network::ConnectionSocketPtr{accepted_socket}); + + // Verify per-listener connection stats. + EXPECT_EQ(1UL, handler_->numConnections()); + EXPECT_EQ(1UL, TestUtility::findCounter(stats_store_, "downstream_cx_total")->value()); + EXPECT_EQ(1UL, TestUtility::findGauge(stats_store_, "downstream_cx_active")->value()); + EXPECT_EQ(1UL, TestUtility::findCounter(stats_store_, "test.downstream_cx_total")->value()); + EXPECT_EQ(1UL, TestUtility::findGauge(stats_store_, "test.downstream_cx_active")->value()); + + EXPECT_CALL(*access_log_, log(_, _, _, _)) + .WillOnce( + Invoke([&](const Http::RequestHeaderMap*, const Http::ResponseHeaderMap*, + const Http::ResponseTrailerMap*, const StreamInfo::StreamInfo& stream_info) { + EXPECT_EQ(alt_address, stream_info.downstreamAddressProvider().localAddress()); + })); + connection->close(Network::ConnectionCloseType::NoFlush); + dispatcher_.clearDeferredDeleteList(); + EXPECT_EQ(0UL, TestUtility::findGauge(stats_store_, "downstream_cx_active")->value()); + EXPECT_EQ(0UL, TestUtility::findGauge(stats_store_, "test.downstream_cx_active")->value()); + + EXPECT_CALL(*listener2, onDestroy()); + EXPECT_CALL(*listener1, onDestroy()); +} + // When update a listener, the old listener will be stopped and the new listener will // be added into ConnectionHandler before remove the old listener from ConnectionHandler. // This test ensure ConnectionHandler can query the correct Listener when redirect the connection @@ -811,36 +993,30 @@ TEST_F(ConnectionHandlerTest, MatchLatestListener) { auto listener1 = new NiceMock(); TestListener* test_listener1 = addListener(1, true, true, "test_listener1", listener1, &listener_callbacks); - EXPECT_CALL(test_listener1->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener1, runtime_); // Listener2 will be replaced by Listener3. auto listener2_overridden_filter_chain_manager = std::make_shared>(); auto listener2 = new NiceMock(); - TestListener* test_listener2 = - addListener(2, false, false, "test_listener2", listener2, nullptr, nullptr, nullptr, - Network::Socket::Type::Stream, std::chrono::milliseconds(15000), false, - listener2_overridden_filter_chain_manager); Network::Address::InstanceConstSharedPtr listener2_address( new Network::Address::Ipv4Instance("127.0.0.1", 10002)); - EXPECT_CALL(test_listener2->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(listener2_address)); + TestListener* test_listener2 = + addListener(2, false, false, "test_listener2", listener2, nullptr, listener2_address, nullptr, + nullptr, Network::Socket::Type::Stream, std::chrono::milliseconds(15000), false, + listener2_overridden_filter_chain_manager); handler_->addListener(absl::nullopt, *test_listener2, runtime_); // Listener3 will replace the listener2. auto listener3_overridden_filter_chain_manager = std::make_shared>(); auto listener3 = new NiceMock(); - TestListener* test_listener3 = - addListener(3, false, false, "test_listener3", listener3, nullptr, nullptr, nullptr, - Network::Socket::Type::Stream, std::chrono::milliseconds(15000), false, - listener3_overridden_filter_chain_manager); Network::Address::InstanceConstSharedPtr listener3_address( new Network::Address::Ipv4Instance("127.0.0.1", 10002)); - EXPECT_CALL(test_listener3->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(listener3_address)); + TestListener* test_listener3 = + addListener(3, false, false, "test_listener3", listener3, nullptr, listener3_address, nullptr, + nullptr, Network::Socket::Type::Stream, std::chrono::milliseconds(15000), false, + listener3_overridden_filter_chain_manager); // This emulated the case of update listener in-place. Stop the old listener and // add the new listener. @@ -894,21 +1070,17 @@ TEST_F(ConnectionHandlerTest, EnsureNotMatchStoppedListener) { auto listener1 = new NiceMock(); TestListener* test_listener1 = addListener(1, true, true, "test_listener1", listener1, &listener_callbacks); - EXPECT_CALL(test_listener1->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener1, runtime_); auto listener2_overridden_filter_chain_manager = std::make_shared>(); auto listener2 = new NiceMock(); - TestListener* test_listener2 = - addListener(2, false, false, "test_listener2", listener2, nullptr, nullptr, nullptr, - Network::Socket::Type::Stream, std::chrono::milliseconds(15000), false, - listener2_overridden_filter_chain_manager); Network::Address::InstanceConstSharedPtr listener2_address( new Network::Address::Ipv4Instance("127.0.0.1", 10002)); - EXPECT_CALL(test_listener2->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(listener2_address)); + TestListener* test_listener2 = + addListener(2, false, false, "test_listener2", listener2, nullptr, listener2_address, nullptr, + nullptr, Network::Socket::Type::Stream, std::chrono::milliseconds(15000), false, + listener2_overridden_filter_chain_manager); handler_->addListener(absl::nullopt, *test_listener2, runtime_); // Stop the listener2. @@ -958,21 +1130,17 @@ TEST_F(ConnectionHandlerTest, EnsureNotMatchStoppedAnyAddressListener) { auto listener1 = new NiceMock(); TestListener* test_listener1 = addListener(1, true, true, "test_listener1", listener1, &listener_callbacks); - EXPECT_CALL(test_listener1->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener1, runtime_); auto listener2_overridden_filter_chain_manager = std::make_shared>(); auto listener2 = new NiceMock(); - TestListener* test_listener2 = - addListener(2, false, false, "test_listener2", listener2, nullptr, nullptr, nullptr, - Network::Socket::Type::Stream, std::chrono::milliseconds(15000), false, - listener2_overridden_filter_chain_manager); Network::Address::InstanceConstSharedPtr listener2_address( new Network::Address::Ipv4Instance("0.0.0.0", 10002)); - EXPECT_CALL(test_listener2->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(listener2_address)); + TestListener* test_listener2 = + addListener(2, false, false, "test_listener2", listener2, nullptr, listener2_address, nullptr, + nullptr, Network::Socket::Type::Stream, std::chrono::milliseconds(15000), false, + listener2_overridden_filter_chain_manager); handler_->addListener(absl::nullopt, *test_listener2, runtime_); // Stop the listener2. @@ -1019,21 +1187,17 @@ TEST_F(ConnectionHandlerTest, EnsureNotMatchStoppedAnyAddressListener) { TEST_F(ConnectionHandlerTest, FallbackToWildcardListener) { Network::TcpListenerCallbacks* listener_callbacks1; auto listener1 = new NiceMock(); - TestListener* test_listener1 = - addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1); Network::Address::InstanceConstSharedPtr normal_address( new Network::Address::Ipv4Instance("127.0.0.1", 10001)); - EXPECT_CALL(test_listener1->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(normal_address)); + TestListener* test_listener1 = + addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1, normal_address); handler_->addListener(absl::nullopt, *test_listener1, runtime_); Network::TcpListenerCallbacks* listener_callbacks2; auto listener2 = new NiceMock(); - TestListener* test_listener2 = - addListener(2, false, false, "test_listener2", listener2, &listener_callbacks2); Network::Address::InstanceConstSharedPtr any_address = Network::Utility::getIpv4AnyAddress(); - EXPECT_CALL(test_listener2->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(any_address)); + TestListener* test_listener2 = + addListener(2, false, false, "test_listener2", listener2, &listener_callbacks2, any_address); handler_->addListener(absl::nullopt, *test_listener2, runtime_); Network::MockListenerFilter* test_filter = new Network::MockListenerFilter(); @@ -1073,41 +1237,34 @@ TEST_F(ConnectionHandlerTest, FallbackToWildcardListener) { TEST_F(ConnectionHandlerTest, MatchIPv6WildcardListener) { Network::TcpListenerCallbacks* listener_callbacks1; auto listener1 = new NiceMock(); - TestListener* test_listener1 = - addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1); Network::Address::InstanceConstSharedPtr normal_address( new Network::Address::Ipv4Instance("127.0.0.1", 10001)); - EXPECT_CALL(test_listener1->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(normal_address)); + TestListener* test_listener1 = + addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1, normal_address); handler_->addListener(absl::nullopt, *test_listener1, runtime_); auto ipv4_overridden_filter_chain_manager = std::make_shared>(); Network::TcpListenerCallbacks* ipv4_any_listener_callbacks; auto listener2 = new NiceMock(); - TestListener* ipv4_any_listener = - addListener(2, false, false, "ipv4_any_test_listener", listener2, - &ipv4_any_listener_callbacks, nullptr, nullptr, Network::Socket::Type::Stream, - std::chrono::milliseconds(15000), false, ipv4_overridden_filter_chain_manager); - Network::Address::InstanceConstSharedPtr any_address( new Network::Address::Ipv4Instance("0.0.0.0", 80)); - EXPECT_CALL(ipv4_any_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(any_address)); + TestListener* ipv4_any_listener = addListener( + 2, false, false, "ipv4_any_test_listener", listener2, &ipv4_any_listener_callbacks, + any_address, nullptr, nullptr, Network::Socket::Type::Stream, + std::chrono::milliseconds(15000), false, ipv4_overridden_filter_chain_manager); handler_->addListener(absl::nullopt, *ipv4_any_listener, runtime_); auto ipv6_overridden_filter_chain_manager = std::make_shared>(); Network::TcpListenerCallbacks* ipv6_any_listener_callbacks; auto listener3 = new NiceMock(); - TestListener* ipv6_any_listener = - addListener(3, false, false, "ipv6_any_test_listener", listener3, - &ipv6_any_listener_callbacks, nullptr, nullptr, Network::Socket::Type::Stream, - std::chrono::milliseconds(15000), false, ipv6_overridden_filter_chain_manager); Network::Address::InstanceConstSharedPtr any_address_ipv6( new Network::Address::Ipv6Instance("::", 80)); - EXPECT_CALL(ipv6_any_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(any_address_ipv6)); + TestListener* ipv6_any_listener = addListener( + 3, false, false, "ipv6_any_test_listener", listener3, &ipv6_any_listener_callbacks, + any_address_ipv6, nullptr, nullptr, Network::Socket::Type::Stream, + std::chrono::milliseconds(15000), false, ipv6_overridden_filter_chain_manager); handler_->addListener(absl::nullopt, *ipv6_any_listener, runtime_); Network::MockListenerFilter* test_filter = new Network::MockListenerFilter(); @@ -1156,27 +1313,23 @@ TEST_F(ConnectionHandlerTest, MatchIPv6WildcardListener) { TEST_F(ConnectionHandlerTest, MatchIPv6WildcardListenerWithAnyAddressAndIpv4CompatFlag) { Network::TcpListenerCallbacks* listener_callbacks1; auto listener1 = new NiceMock(); - TestListener* test_listener1 = - addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1); Network::Address::InstanceConstSharedPtr normal_address( new Network::Address::Ipv4Instance("127.0.0.1", 10001)); - EXPECT_CALL(test_listener1->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(normal_address)); + TestListener* test_listener1 = + addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1, normal_address); handler_->addListener(absl::nullopt, *test_listener1, runtime_); auto ipv6_overridden_filter_chain_manager = std::make_shared>(); Network::TcpListenerCallbacks* ipv6_any_listener_callbacks; auto listener2 = new NiceMock(); - TestListener* ipv6_any_listener = - addListener(2, false, false, "ipv6_any_test_listener", listener2, - &ipv6_any_listener_callbacks, nullptr, nullptr, Network::Socket::Type::Stream, - std::chrono::milliseconds(15000), false, ipv6_overridden_filter_chain_manager); // Set the ipv6only as false. Network::Address::InstanceConstSharedPtr any_address_ipv6( new Network::Address::Ipv6Instance("::", 80, nullptr, false)); - EXPECT_CALL(ipv6_any_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(any_address_ipv6)); + TestListener* ipv6_any_listener = addListener( + 2, false, false, "ipv6_any_test_listener", listener2, &ipv6_any_listener_callbacks, + any_address_ipv6, nullptr, nullptr, Network::Socket::Type::Stream, + std::chrono::milliseconds(15000), false, ipv6_overridden_filter_chain_manager); handler_->addListener(absl::nullopt, *ipv6_any_listener, runtime_); Network::MockListenerFilter* test_filter = new Network::MockListenerFilter(); @@ -1225,27 +1378,23 @@ TEST_F(ConnectionHandlerTest, MatchIPv6WildcardListenerWithAnyAddressAndIpv4Comp TEST_F(ConnectionHandlerTest, MatchhIpv4CompatiableIPv6ListenerWithIpv4CompatFlag) { Network::TcpListenerCallbacks* listener_callbacks1; auto listener1 = new NiceMock(); - TestListener* test_listener1 = - addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1); Network::Address::InstanceConstSharedPtr normal_address( new Network::Address::Ipv4Instance("127.0.0.1", 10001)); - EXPECT_CALL(test_listener1->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(normal_address)); + TestListener* test_listener1 = + addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1, normal_address); handler_->addListener(absl::nullopt, *test_listener1, runtime_); auto ipv6_overridden_filter_chain_manager = std::make_shared>(); Network::TcpListenerCallbacks* ipv6_listener_callbacks; auto listener2 = new NiceMock(); - TestListener* ipv6_listener = - addListener(2, false, false, "ipv6_test_listener", listener2, &ipv6_listener_callbacks, - nullptr, nullptr, Network::Socket::Type::Stream, std::chrono::milliseconds(15000), - false, ipv6_overridden_filter_chain_manager); // Set the ipv6only as false. Network::Address::InstanceConstSharedPtr ipv4_mapped_ipv6_address( new Network::Address::Ipv6Instance("::FFFF:192.168.0.1", 80, nullptr, false)); - EXPECT_CALL(ipv6_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(ipv4_mapped_ipv6_address)); + TestListener* ipv6_listener = + addListener(2, false, false, "ipv6_test_listener", listener2, &ipv6_listener_callbacks, + ipv4_mapped_ipv6_address, nullptr, nullptr, Network::Socket::Type::Stream, + std::chrono::milliseconds(15000), false, ipv6_overridden_filter_chain_manager); handler_->addListener(absl::nullopt, *ipv6_listener, runtime_); Network::MockListenerFilter* test_filter = new Network::MockListenerFilter(); @@ -1294,27 +1443,23 @@ TEST_F(ConnectionHandlerTest, MatchhIpv4CompatiableIPv6ListenerWithIpv4CompatFla TEST_F(ConnectionHandlerTest, NotMatchIPv6WildcardListenerWithoutIpv4CompatFlag) { Network::TcpListenerCallbacks* listener_callbacks1; auto listener1 = new NiceMock(); - TestListener* test_listener1 = - addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1); Network::Address::InstanceConstSharedPtr normal_address( new Network::Address::Ipv4Instance("127.0.0.1", 10001)); - EXPECT_CALL(test_listener1->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(normal_address)); + TestListener* test_listener1 = + addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1, normal_address); handler_->addListener(absl::nullopt, *test_listener1, runtime_); auto ipv6_overridden_filter_chain_manager = std::make_shared>(); Network::TcpListenerCallbacks* ipv6_any_listener_callbacks; auto listener2 = new NiceMock(); - TestListener* ipv6_any_listener = - addListener(2, false, false, "ipv6_any_test_listener", listener2, - &ipv6_any_listener_callbacks, nullptr, nullptr, Network::Socket::Type::Stream, - std::chrono::milliseconds(15000), false, ipv6_overridden_filter_chain_manager); // not set the ipv6only flag, the default value is true. Network::Address::InstanceConstSharedPtr any_address_ipv6( new Network::Address::Ipv6Instance("::", 80)); - EXPECT_CALL(ipv6_any_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(any_address_ipv6)); + TestListener* ipv6_any_listener = addListener( + 2, false, false, "ipv6_any_test_listener", listener2, &ipv6_any_listener_callbacks, + any_address_ipv6, nullptr, nullptr, Network::Socket::Type::Stream, + std::chrono::milliseconds(15000), false, ipv6_overridden_filter_chain_manager); handler_->addListener(absl::nullopt, *ipv6_any_listener, runtime_); Network::MockListenerFilter* test_filter = new Network::MockListenerFilter(); @@ -1360,12 +1505,10 @@ TEST_F(ConnectionHandlerTest, MatchhIpv4WhenBothIpv4AndIPv6WithIpv4CompatFlag) { // Listener1 is response for redirect the connection. Network::TcpListenerCallbacks* listener_callbacks1; auto listener1 = new NiceMock(); - TestListener* test_listener1 = - addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1); Network::Address::InstanceConstSharedPtr normal_address( new Network::Address::Ipv4Instance("127.0.0.1", 10001)); - EXPECT_CALL(test_listener1->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(normal_address)); + TestListener* test_listener1 = + addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1, normal_address); handler_->addListener(absl::nullopt, *test_listener1, runtime_); // Listener2 is listening on an ipv4-mapped ipv6 address. @@ -1373,15 +1516,13 @@ TEST_F(ConnectionHandlerTest, MatchhIpv4WhenBothIpv4AndIPv6WithIpv4CompatFlag) { std::make_shared>(); Network::TcpListenerCallbacks* ipv6_any_listener_callbacks; auto listener2 = new NiceMock(); - TestListener* ipv6_listener = - addListener(2, false, false, "ipv6_test_listener", listener2, &ipv6_any_listener_callbacks, - nullptr, nullptr, Network::Socket::Type::Stream, std::chrono::milliseconds(15000), - false, ipv6_overridden_filter_chain_manager); // Set the ipv6only as false. Network::Address::InstanceConstSharedPtr ipv6_any_address( new Network::Address::Ipv6Instance("::", 80, nullptr, false)); - EXPECT_CALL(ipv6_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(ipv6_any_address)); + TestListener* ipv6_listener = + addListener(2, false, false, "ipv6_test_listener", listener2, &ipv6_any_listener_callbacks, + ipv6_any_address, nullptr, nullptr, Network::Socket::Type::Stream, + std::chrono::milliseconds(15000), false, ipv6_overridden_filter_chain_manager); handler_->addListener(absl::nullopt, *ipv6_listener, runtime_); // Listener3 is listening on an ipv4 address. @@ -1389,14 +1530,12 @@ TEST_F(ConnectionHandlerTest, MatchhIpv4WhenBothIpv4AndIPv6WithIpv4CompatFlag) { std::make_shared>(); Network::TcpListenerCallbacks* ipv4_listener_callbacks; auto listener3 = new NiceMock(); - TestListener* ipv4_listener = - addListener(3, false, false, "ipv4_test_listener", listener3, &ipv4_listener_callbacks, - nullptr, nullptr, Network::Socket::Type::Stream, std::chrono::milliseconds(15000), - false, ipv4_overridden_filter_chain_manager); Network::Address::InstanceConstSharedPtr ipv4_address( new Network::Address::Ipv4Instance("0.0.0.0", 80, nullptr)); - EXPECT_CALL(ipv4_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(ipv4_address)); + TestListener* ipv4_listener = + addListener(3, false, false, "ipv4_test_listener", listener3, &ipv4_listener_callbacks, + ipv4_address, nullptr, nullptr, Network::Socket::Type::Stream, + std::chrono::milliseconds(15000), false, ipv4_overridden_filter_chain_manager); handler_->addListener(absl::nullopt, *ipv4_listener, runtime_); Network::MockListenerFilter* test_filter = new Network::MockListenerFilter(); @@ -1446,12 +1585,10 @@ TEST_F(ConnectionHandlerTest, MatchhIpv4WhenBothIpv4AndIPv6WithIpv4CompatFlag2) // Listener1 is response for redirect the connection. Network::TcpListenerCallbacks* listener_callbacks1; auto listener1 = new NiceMock(); - TestListener* test_listener1 = - addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1); Network::Address::InstanceConstSharedPtr normal_address( new Network::Address::Ipv4Instance("127.0.0.1", 10001)); - EXPECT_CALL(test_listener1->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(normal_address)); + TestListener* test_listener1 = + addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1, normal_address); handler_->addListener(absl::nullopt, *test_listener1, runtime_); // Listener3 is listening on an ipv4 address. @@ -1459,14 +1596,12 @@ TEST_F(ConnectionHandlerTest, MatchhIpv4WhenBothIpv4AndIPv6WithIpv4CompatFlag2) std::make_shared>(); Network::TcpListenerCallbacks* ipv4_listener_callbacks; auto listener3 = new NiceMock(); - TestListener* ipv4_listener = - addListener(3, false, false, "ipv4_test_listener", listener3, &ipv4_listener_callbacks, - nullptr, nullptr, Network::Socket::Type::Stream, std::chrono::milliseconds(15000), - false, ipv4_overridden_filter_chain_manager); Network::Address::InstanceConstSharedPtr ipv4_address( new Network::Address::Ipv4Instance("0.0.0.0", 80, nullptr)); - EXPECT_CALL(ipv4_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(ipv4_address)); + TestListener* ipv4_listener = + addListener(3, false, false, "ipv4_test_listener", listener3, &ipv4_listener_callbacks, + ipv4_address, nullptr, nullptr, Network::Socket::Type::Stream, + std::chrono::milliseconds(15000), false, ipv4_overridden_filter_chain_manager); handler_->addListener(absl::nullopt, *ipv4_listener, runtime_); // Listener2 is listening on an ipv4-mapped ipv6 address. @@ -1474,15 +1609,13 @@ TEST_F(ConnectionHandlerTest, MatchhIpv4WhenBothIpv4AndIPv6WithIpv4CompatFlag2) std::make_shared>(); Network::TcpListenerCallbacks* ipv6_any_listener_callbacks; auto listener2 = new NiceMock(); - TestListener* ipv6_listener = - addListener(2, false, false, "ipv6_test_listener", listener2, &ipv6_any_listener_callbacks, - nullptr, nullptr, Network::Socket::Type::Stream, std::chrono::milliseconds(15000), - false, ipv6_overridden_filter_chain_manager); // Set the ipv6only as false. Network::Address::InstanceConstSharedPtr ipv4_mapped_ipv6_address( new Network::Address::Ipv6Instance("::", 80, nullptr, false)); - EXPECT_CALL(ipv6_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(ipv4_mapped_ipv6_address)); + TestListener* ipv6_listener = + addListener(2, false, false, "ipv6_test_listener", listener2, &ipv6_any_listener_callbacks, + ipv4_mapped_ipv6_address, nullptr, nullptr, Network::Socket::Type::Stream, + std::chrono::milliseconds(15000), false, ipv6_overridden_filter_chain_manager); handler_->addListener(absl::nullopt, *ipv6_listener, runtime_); Network::MockListenerFilter* test_filter = new Network::MockListenerFilter(); @@ -1533,12 +1666,10 @@ TEST_F(ConnectionHandlerTest, AddIpv4MappedListenerAfterIpv4ListenerStopped) { // Listener1 is response for redirect the connection. Network::TcpListenerCallbacks* listener_callbacks1; auto listener1 = new NiceMock(); - TestListener* test_listener1 = - addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1); Network::Address::InstanceConstSharedPtr normal_address( new Network::Address::Ipv4Instance("127.0.0.1", 10001)); - EXPECT_CALL(test_listener1->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(normal_address)); + TestListener* test_listener1 = + addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1, normal_address); handler_->addListener(absl::nullopt, *test_listener1, runtime_); // Listener2 is listening on an Ipv4 address. @@ -1546,14 +1677,12 @@ TEST_F(ConnectionHandlerTest, AddIpv4MappedListenerAfterIpv4ListenerStopped) { std::make_shared>(); Network::TcpListenerCallbacks* ipv4_listener_callbacks; auto listener2 = new NiceMock(); - TestListener* ipv4_listener = - addListener(2, false, false, "ipv4_test_listener", listener2, &ipv4_listener_callbacks, - nullptr, nullptr, Network::Socket::Type::Stream, std::chrono::milliseconds(15000), - false, ipv4_overridden_filter_chain_manager); Network::Address::InstanceConstSharedPtr ipv4_address( new Network::Address::Ipv4Instance("0.0.0.0", 80, nullptr)); - EXPECT_CALL(ipv4_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(ipv4_address)); + TestListener* ipv4_listener = + addListener(2, false, false, "ipv4_test_listener", listener2, &ipv4_listener_callbacks, + ipv4_address, nullptr, nullptr, Network::Socket::Type::Stream, + std::chrono::milliseconds(15000), false, ipv4_overridden_filter_chain_manager); handler_->addListener(absl::nullopt, *ipv4_listener, runtime_); EXPECT_CALL(*listener2, onDestroy()); @@ -1565,15 +1694,13 @@ TEST_F(ConnectionHandlerTest, AddIpv4MappedListenerAfterIpv4ListenerStopped) { std::make_shared>(); Network::TcpListenerCallbacks* ipv6_any_listener_callbacks; auto listener3 = new NiceMock(); - TestListener* ipv6_listener = - addListener(3, false, false, "ipv6_test_listener", listener3, &ipv6_any_listener_callbacks, - nullptr, nullptr, Network::Socket::Type::Stream, std::chrono::milliseconds(15000), - false, ipv6_overridden_filter_chain_manager); // Set the ipv6only as false. Network::Address::InstanceConstSharedPtr ipv4_mapped_ipv6_address( new Network::Address::Ipv6Instance("::", 80, nullptr, false)); - EXPECT_CALL(ipv6_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(ipv4_mapped_ipv6_address)); + TestListener* ipv6_listener = + addListener(3, false, false, "ipv6_test_listener", listener3, &ipv6_any_listener_callbacks, + ipv4_mapped_ipv6_address, nullptr, nullptr, Network::Socket::Type::Stream, + std::chrono::milliseconds(15000), false, ipv6_overridden_filter_chain_manager); handler_->addListener(absl::nullopt, *ipv6_listener, runtime_); Network::MockListenerFilter* test_filter = new Network::MockListenerFilter(); @@ -1639,15 +1766,12 @@ TEST_F(ConnectionHandlerTest, WildcardListenerWithOriginalDstOutbound) { TEST_F(ConnectionHandlerTest, WildcardListenerWithNoOriginalDst) { Network::TcpListenerCallbacks* listener_callbacks1; auto listener1 = new NiceMock(); - TestListener* test_listener1 = - addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1); - Network::Address::InstanceConstSharedPtr normal_address( new Network::Address::Ipv4Instance("127.0.0.1", 80)); Network::Address::InstanceConstSharedPtr any_address = Network::Utility::getAddressWithPort( *Network::Utility::getIpv4AnyAddress(), normal_address->ip()->port()); - EXPECT_CALL(test_listener1->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(any_address)); + TestListener* test_listener1 = + addListener(1, true, true, "test_listener1", listener1, &listener_callbacks1, any_address); handler_->addListener(absl::nullopt, *test_listener1, runtime_); Network::MockListenerFilter* test_filter = new Network::MockListenerFilter(); @@ -1676,8 +1800,6 @@ TEST_F(ConnectionHandlerTest, TransportProtocolDefault) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, true, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); Network::MockConnectionSocket* accepted_socket = new NiceMock(); @@ -1696,8 +1818,6 @@ TEST_F(ConnectionHandlerTest, TransportProtocolCustom) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, true, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); Network::MockListenerFilter* test_filter = new Network::MockListenerFilter(); @@ -1731,8 +1851,6 @@ TEST_F(ConnectionHandlerTest, ListenerFilterTimeout) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, true, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); Network::MockListenerFilter* test_filter = new Network::MockListenerFilter(512); @@ -1783,9 +1901,7 @@ TEST_F(ConnectionHandlerTest, ContinueOnListenerFilterTimeout) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, true, false, "test_listener", listener, &listener_callbacks, nullptr, nullptr, - Network::Socket::Type::Stream, std::chrono::milliseconds(15000), true); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); + nullptr, Network::Socket::Type::Stream, std::chrono::milliseconds(15000), true); handler_->addListener(absl::nullopt, *test_listener, runtime_); Network::MockListenerFilter* test_filter = new NiceMock(128); @@ -1846,8 +1962,6 @@ TEST_F(ConnectionHandlerTest, ListenerFilterTimeoutResetOnSuccess) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, true, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); Network::MockListenerFilter* test_filter = new Network::MockListenerFilter(123); @@ -1899,9 +2013,7 @@ TEST_F(ConnectionHandlerTest, ListenerFilterDisabledTimeout) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, true, false, "test_listener", listener, &listener_callbacks, nullptr, nullptr, - Network::Socket::Type::Stream, std::chrono::milliseconds()); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); + nullptr, Network::Socket::Type::Stream, std::chrono::milliseconds()); handler_->addListener(absl::nullopt, *test_listener, runtime_); Network::MockListenerFilter* test_filter = new Network::MockListenerFilter(512); @@ -1937,8 +2049,6 @@ TEST_F(ConnectionHandlerTest, ListenerFilterReportError) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, true, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); Network::MockListenerFilter* first_filter = new Network::MockListenerFilter(); @@ -1978,12 +2088,12 @@ TEST_F(ConnectionHandlerTest, UdpListenerNoFilter) { auto listener = new NiceMock(); TestListener* test_listener = - addListener(1, true, false, "test_listener", listener, nullptr, nullptr, nullptr, + addListener(1, true, false, "test_listener", listener, nullptr, nullptr, nullptr, nullptr, Network::Socket::Type::Datagram, std::chrono::milliseconds()); EXPECT_CALL(factory_, createUdpListenerFilterChain(_, _)) .WillOnce(Invoke([&](Network::UdpListenerFilterManager&, Network::UdpReadFilterCallbacks&) -> bool { return true; })); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) + EXPECT_CALL(test_listener->socketFactory(), localAddress()) .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); @@ -2008,9 +2118,7 @@ TEST_F(ConnectionHandlerTest, TcpListenerInplaceUpdate) { TestListener* old_test_listener = addListener(old_listener_tag, true, false, "test_listener", old_listener, - &old_listener_callbacks, mock_connection_balancer, ¤t_handler); - EXPECT_CALL(old_test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); + &old_listener_callbacks, nullptr, mock_connection_balancer, ¤t_handler); handler_->addListener(absl::nullopt, *old_test_listener, runtime_); ASSERT_NE(old_test_listener, nullptr); @@ -2018,10 +2126,11 @@ TEST_F(ConnectionHandlerTest, TcpListenerInplaceUpdate) { auto overridden_filter_chain_manager = std::make_shared>(); - TestListener* new_test_listener = addListener( - new_listener_tag, true, false, "test_listener", /* Network::Listener */ nullptr, - &new_listener_callbacks, mock_connection_balancer, nullptr, Network::Socket::Type::Stream, - std::chrono::milliseconds(15000), false, overridden_filter_chain_manager); + TestListener* new_test_listener = + addListener(new_listener_tag, true, false, "test_listener", /* Network::Listener */ nullptr, + &new_listener_callbacks, nullptr, mock_connection_balancer, nullptr, + Network::Socket::Type::Stream, std::chrono::milliseconds(15000), false, + overridden_filter_chain_manager); handler_->addListener(old_listener_tag, *new_test_listener, runtime_); ASSERT_EQ(new_listener_callbacks, nullptr) << "new listener should be inplace added and callback should not change"; @@ -2047,8 +2156,6 @@ TEST_F(ConnectionHandlerTest, TcpListenerRemoveFilterChain) { auto listener = new NiceMock(); TestListener* test_listener = addListener(listener_tag, true, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); Network::MockConnectionSocket* connection = new NiceMock(); @@ -2096,8 +2203,6 @@ TEST_F(ConnectionHandlerTest, TcpListenerRemoveFilterChainCalledAfterListenerIsR auto listener = new NiceMock(); TestListener* test_listener = addListener(listener_tag, true, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); Network::MockConnectionSocket* connection = new NiceMock(); @@ -2159,8 +2264,6 @@ TEST_F(ConnectionHandlerTest, TcpListenerRemoveListener) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, true, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); Network::MockConnectionSocket* connection = new NiceMock(); @@ -2189,12 +2292,10 @@ TEST_F(ConnectionHandlerTest, TcpListenerRemoveIpv6AnyAddressWithIpv4CompatListe Network::TcpListenerCallbacks* listener_callbacks; auto listener = new NiceMock(); - TestListener* test_listener = - addListener(1, true, false, "test_listener", listener, &listener_callbacks); Network::Address::InstanceConstSharedPtr any_address_ipv6( new Network::Address::Ipv6Instance("::", 80, nullptr, false)); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(any_address_ipv6)); + TestListener* test_listener = + addListener(1, true, false, "test_listener", listener, &listener_callbacks, any_address_ipv6); handler_->addListener(absl::nullopt, *test_listener, runtime_); Network::MockConnectionSocket* connection = new NiceMock(); @@ -2220,12 +2321,10 @@ TEST_F(ConnectionHandlerTest, TcpListenerRemoveIpv4CompatAddressListener) { Network::TcpListenerCallbacks* listener_callbacks; auto listener = new NiceMock(); - TestListener* test_listener = - addListener(1, true, false, "test_listener", listener, &listener_callbacks); Network::Address::InstanceConstSharedPtr address_ipv6( new Network::Address::Ipv6Instance("::FFFF:192.168.0.1", 80, nullptr, false)); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(address_ipv6)); + TestListener* test_listener = + addListener(1, true, false, "test_listener", listener, &listener_callbacks, address_ipv6); handler_->addListener(absl::nullopt, *test_listener, runtime_); Network::MockConnectionSocket* connection = new NiceMock(); @@ -2251,22 +2350,18 @@ TEST_F(ConnectionHandlerTest, TcpListenerRemoveWithBothIpv4AnyAndIpv6Any) { Network::TcpListenerCallbacks* listener_callbacks; auto listener = new NiceMock(); - TestListener* test_listener = - addListener(1, true, false, "test_listener", listener, &listener_callbacks); Network::Address::InstanceConstSharedPtr address_ipv6( new Network::Address::Ipv6Instance("::", 80, nullptr, false)); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(address_ipv6)); + TestListener* test_listener = + addListener(1, true, false, "test_listener", listener, &listener_callbacks, address_ipv6); handler_->addListener(absl::nullopt, *test_listener, runtime_); Network::TcpListenerCallbacks* listener_callbacks2; auto listener2 = new NiceMock(); - TestListener* test_listener2 = - addListener(2, true, false, "test_listener2", listener2, &listener_callbacks2); Network::Address::InstanceConstSharedPtr address_ipv4( new Network::Address::Ipv4Instance("0.0.0.0", 80, nullptr)); - EXPECT_CALL(test_listener2->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(address_ipv4)); + TestListener* test_listener2 = + addListener(2, true, false, "test_listener2", listener2, &listener_callbacks2, address_ipv4); handler_->addListener(absl::nullopt, *test_listener2, runtime_); Network::MockConnectionSocket* connection = new NiceMock(); @@ -2305,8 +2400,6 @@ TEST_F(ConnectionHandlerTest, TcpListenerGlobalCxLimitReject) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, true, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); listener_callbacks->onReject(Network::TcpListenerCallbacks::RejectCause::GlobalCxLimit); @@ -2321,8 +2414,6 @@ TEST_F(ConnectionHandlerTest, TcpListenerOverloadActionReject) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, true, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); listener_callbacks->onReject(Network::TcpListenerCallbacks::RejectCause::OverloadAction); @@ -2338,8 +2429,6 @@ TEST_F(ConnectionHandlerTest, ListenerFilterWorks) { auto listener = new NiceMock(); TestListener* test_listener = addListener(1, true, false, "test_listener", listener, &listener_callbacks); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) - .WillRepeatedly(ReturnRef(local_address_)); handler_->addListener(absl::nullopt, *test_listener, runtime_); auto all_matcher = std::make_shared(); @@ -2374,7 +2463,7 @@ TEST_F(ConnectionHandlerTest, ShutdownUdpListener) { Network::MockUdpReadFilterCallbacks dummy_callbacks; auto listener = new NiceMock(*this); TestListener* test_listener = - addListener(1, true, false, "test_listener", listener, nullptr, nullptr, nullptr, + addListener(1, true, false, "test_listener", listener, nullptr, nullptr, nullptr, nullptr, Network::Socket::Type::Datagram, std::chrono::milliseconds(), false, nullptr); auto filter = std::make_unique>(*this, dummy_callbacks); @@ -2384,7 +2473,7 @@ TEST_F(ConnectionHandlerTest, ShutdownUdpListener) { udp_listener.addReadFilter(std::move(filter)); return true; })); - EXPECT_CALL(test_listener->socket_factory_, localAddress()) + EXPECT_CALL(test_listener->socketFactory(), localAddress()) .WillRepeatedly(ReturnRef(local_address_)); EXPECT_CALL(dummy_callbacks.udp_listener_, onDestroy()); @@ -2402,7 +2491,7 @@ TEST_F(ConnectionHandlerTest, DisableInternalListener) { TestListener* internal_listener = addInternalListener(1, "test_internal_listener", std::chrono::milliseconds(), false, nullptr); - EXPECT_CALL(internal_listener->socket_factory_, localAddress()) + EXPECT_CALL(internal_listener->socketFactory(), localAddress()) .WillRepeatedly(ReturnRef(local_address)); handler_->addListener(absl::nullopt, *internal_listener, runtime_); auto internal_listener_cb = handler_->findByAddress(local_address); @@ -2428,7 +2517,7 @@ TEST_F(ConnectionHandlerTest, InternalListenerInplaceUpdate) { TestListener* internal_listener = addInternalListener( old_listener_tag, "test_internal_listener", std::chrono::milliseconds(), false, nullptr); - EXPECT_CALL(internal_listener->socket_factory_, localAddress()) + EXPECT_CALL(internal_listener->socketFactory(), localAddress()) .WillRepeatedly(ReturnRef(local_address)); handler_->addListener(absl::nullopt, *internal_listener, runtime_); diff --git a/test/server/filter_chain_benchmark_test.cc b/test/server/filter_chain_benchmark_test.cc index de3c28389a622..a610aa2f3d5c9 100644 --- a/test/server/filter_chain_benchmark_test.cc +++ b/test/server/filter_chain_benchmark_test.cc @@ -227,10 +227,11 @@ BENCHMARK_DEFINE_F(FilterChainBenchmarkFixture, FilterChainManagerBuildTest) initialize(state); NiceMock factory_context; + std::vector addresses; + addresses.emplace_back(std::make_shared("127.0.0.1", 1234)); for (auto _ : state) { - FilterChainManagerImpl filter_chain_manager{ - std::make_shared("127.0.0.1", 1234), factory_context, - init_manager_}; + UNREFERENCED_PARAMETER(_); + FilterChainManagerImpl filter_chain_manager{addresses, factory_context, init_manager_}; filter_chain_manager.addFilterChains(nullptr, filter_chains_, nullptr, dummy_builder_, filter_chain_manager); } @@ -251,9 +252,9 @@ BENCHMARK_DEFINE_F(FilterChainBenchmarkFixture, FilterChainFindTest) 10000 + i, "127.0.0.1", "", "", "tls", {}, "8.8.8.8", 111))); } NiceMock factory_context; - FilterChainManagerImpl filter_chain_manager{ - std::make_shared("127.0.0.1", 1234), factory_context, - init_manager_}; + std::vector addresses; + addresses.emplace_back(std::make_shared("127.0.0.1", 1234)); + FilterChainManagerImpl filter_chain_manager{addresses, factory_context, init_manager_}; filter_chain_manager.addFilterChains(nullptr, filter_chains_, nullptr, dummy_builder_, filter_chain_manager); diff --git a/test/server/filter_chain_manager_impl_test.cc b/test/server/filter_chain_manager_impl_test.cc index 11d09c45258e6..4c9e3e39a9d7c 100644 --- a/test/server/filter_chain_manager_impl_test.cc +++ b/test/server/filter_chain_manager_impl_test.cc @@ -58,6 +58,9 @@ class MockFilterChainFactoryBuilder : public FilterChainFactoryBuilder { class FilterChainManagerImplTest : public testing::TestWithParam { public: void SetUp() override { + addresses_.emplace_back(std::make_shared("127.0.0.1", 1234)); + filter_chain_manager_ = + std::make_unique(addresses_, parent_context_, init_manager_); local_address_ = std::make_shared("127.0.0.1", 1234); remote_address_ = std::make_shared("127.0.0.1", 1234); TestUtility::loadFromYaml( @@ -95,16 +98,16 @@ class FilterChainManagerImplTest : public testing::TestWithParam { remote_address_ = Network::Utility::parseInternetAddress(source_address, source_port); } mock_socket->connection_info_provider_->setRemoteAddress(remote_address_); - return filter_chain_manager_.findFilterChain(*mock_socket); + return filter_chain_manager_->findFilterChain(*mock_socket); } void addSingleFilterChainHelper( const envoy::config::listener::v3::FilterChain& filter_chain, const envoy::config::listener::v3::FilterChain* fallback_filter_chain = nullptr) { - filter_chain_manager_.addFilterChains( + filter_chain_manager_->addFilterChains( GetParam() ? &matcher_ : nullptr, std::vector{&filter_chain}, - fallback_filter_chain, filter_chain_factory_builder_, filter_chain_manager_); + fallback_filter_chain, filter_chain_factory_builder_, *filter_chain_manager_); } // Intermediate states. @@ -155,10 +158,9 @@ class FilterChainManagerImplTest : public testing::TestWithParam { NiceMock filter_chain_factory_builder_; NiceMock parent_context_; + std::vector addresses_; // Test target. - FilterChainManagerImpl filter_chain_manager_{ - std::make_shared("127.0.0.1", 1234), parent_context_, - init_manager_}; + std::unique_ptr filter_chain_manager_; }; TEST_P(FilterChainManagerImplTest, FilterChainMatchNothing) { @@ -169,10 +171,10 @@ TEST_P(FilterChainManagerImplTest, FilterChainMatchNothing) { TEST_P(FilterChainManagerImplTest, FilterChainMatchCaseInSensitive) { envoy::config::listener::v3::FilterChain new_filter_chain = filter_chain_template_; new_filter_chain.mutable_filter_chain_match()->add_server_names("foo.EXAMPLE.com"); - filter_chain_manager_.addFilterChains( + filter_chain_manager_->addFilterChains( GetParam() ? &matcher_ : nullptr, std::vector{&new_filter_chain}, nullptr, - filter_chain_factory_builder_, filter_chain_manager_); + filter_chain_factory_builder_, *filter_chain_manager_); auto filter_chain = findFilterChainHelper(10000, "127.0.0.1", "FOO.example.com", "tls", {}, "8.8.8.8", 111); EXPECT_NE(filter_chain, nullptr); @@ -217,11 +219,11 @@ TEST_P(FilterChainManagerImplTest, LookupFilterChainContextByFilterChainMessage) filter_chain_messages.push_back(std::move(new_filter_chain)); } EXPECT_CALL(filter_chain_factory_builder_, buildFilterChain(_, _)).Times(2); - filter_chain_manager_.addFilterChains( + filter_chain_manager_->addFilterChains( GetParam() ? &matcher_ : nullptr, std::vector{&filter_chain_messages[0], &filter_chain_messages[1]}, - nullptr, filter_chain_factory_builder_, filter_chain_manager_); + nullptr, filter_chain_factory_builder_, *filter_chain_manager_); } TEST_P(FilterChainManagerImplTest, DuplicateContextsAreNotBuilt) { @@ -236,14 +238,12 @@ TEST_P(FilterChainManagerImplTest, DuplicateContextsAreNotBuilt) { } EXPECT_CALL(filter_chain_factory_builder_, buildFilterChain(_, _)); - filter_chain_manager_.addFilterChains( + filter_chain_manager_->addFilterChains( GetParam() ? &matcher_ : nullptr, std::vector{&filter_chain_messages[0]}, - nullptr, filter_chain_factory_builder_, filter_chain_manager_); - - FilterChainManagerImpl new_filter_chain_manager{ - std::make_shared("127.0.0.1", 1234), parent_context_, - init_manager_, filter_chain_manager_}; + nullptr, filter_chain_factory_builder_, *filter_chain_manager_); + FilterChainManagerImpl new_filter_chain_manager{addresses_, parent_context_, init_manager_, + *filter_chain_manager_}; // The new filter chain manager maintains 3 filter chains, but only 2 filter chain context is // built because it reuse the filter chain context in the previous filter chain manager EXPECT_CALL(filter_chain_factory_builder_, buildFilterChain(_, _)).Times(2); @@ -263,8 +263,8 @@ TEST_P(FilterChainManagerImplTest, CreatedFilterChainFactoryContextHasIndependen new_filter_chain.mutable_filter_chain_match()->mutable_destination_port()->set_value(10000 + i); filter_chain_messages.push_back(std::move(new_filter_chain)); } - auto context0 = filter_chain_manager_.createFilterChainFactoryContext(&filter_chain_messages[0]); - auto context1 = filter_chain_manager_.createFilterChainFactoryContext(&filter_chain_messages[1]); + auto context0 = filter_chain_manager_->createFilterChainFactoryContext(&filter_chain_messages[0]); + auto context1 = filter_chain_manager_->createFilterChainFactoryContext(&filter_chain_messages[1]); // Server as whole is not draining. MockDrainManager not_a_draining_manager; diff --git a/test/server/hot_restarting_parent_test.cc b/test/server/hot_restarting_parent_test.cc index e8670e9dee85b..dfeda8e2af3ba 100644 --- a/test/server/hot_restarting_parent_test.cc +++ b/test/server/hot_restarting_parent_test.cc @@ -55,8 +55,10 @@ TEST_F(HotRestartingParentTest, GetListenSocketsForChildNotBindPort) { EXPECT_CALL(server_, listenerManager()).WillOnce(ReturnRef(listener_manager)); EXPECT_CALL(listener_manager, listeners(ListenerManager::ListenerState::ACTIVE)) .WillOnce(Return(listeners)); - EXPECT_CALL(listener_config, listenSocketFactory()); - EXPECT_CALL(listener_config.socket_factory_, localAddress()); + EXPECT_CALL(listener_config, listenSocketFactories()); + EXPECT_CALL( + *static_cast(listener_config.socket_factories_[0].get()), + localAddress()); EXPECT_CALL(listener_config, bindToPort()).WillOnce(Return(false)); HotRestartMessage::Request request; @@ -79,21 +81,31 @@ TEST_F(HotRestartingParentTest, GetListenSocketsForChildSocketType) { EXPECT_CALL(server_, listenerManager()).WillOnce(ReturnRef(listener_manager)); EXPECT_CALL(listener_manager, listeners(ListenerManager::ListenerState::ACTIVE)) .WillOnce(Return(listeners)); - EXPECT_CALL(tcp_listener_config, listenSocketFactory()); - EXPECT_CALL(tcp_listener_config.socket_factory_, localAddress()); + EXPECT_CALL(tcp_listener_config, listenSocketFactories()); + EXPECT_CALL(*static_cast( + tcp_listener_config.socket_factories_[0].get()), + localAddress()); EXPECT_CALL(tcp_listener_config, bindToPort()).WillOnce(Return(true)); - EXPECT_CALL(tcp_listener_config.socket_factory_, socketType()) + EXPECT_CALL(*static_cast( + tcp_listener_config.socket_factories_[0].get()), + socketType()) .WillOnce(Return(Network::Socket::Type::Stream)); - EXPECT_CALL(udp_listener_config, listenSocketFactory()); - EXPECT_CALL(udp_listener_config.socket_factory_, localAddress()); + EXPECT_CALL(udp_listener_config, listenSocketFactories()); + EXPECT_CALL(*static_cast( + udp_listener_config.socket_factories_[0].get()), + localAddress()); EXPECT_CALL(udp_listener_config, bindToPort()).WillOnce(Return(true)); - EXPECT_CALL(udp_listener_config.socket_factory_, socketType()) + EXPECT_CALL(*static_cast( + udp_listener_config.socket_factories_[0].get()), + socketType()) .WillOnce(Return(Network::Socket::Type::Datagram)); EXPECT_CALL(server_, options()).WillOnce(ReturnRef(options)); EXPECT_CALL(options, concurrency()).WillOnce(Return(1)); - EXPECT_CALL(udp_listener_config.socket_factory_, getListenSocket(_)); + EXPECT_CALL(*static_cast( + udp_listener_config.socket_factories_[0].get()), + getListenSocket(_)); HotRestartMessage::Request request; request.mutable_pass_listen_socket()->set_address("udp://0.0.0.0:80"); @@ -115,15 +127,22 @@ TEST_F(HotRestartingParentTest, GetListenSocketsForChildUnixDomainSocket) { EXPECT_CALL(server_, listenerManager()).WillOnce(ReturnRef(listener_manager)); EXPECT_CALL(listener_manager, listeners(ListenerManager::ListenerState::ACTIVE)) .WillOnce(Return(listeners)); - EXPECT_CALL(listener_config, listenSocketFactory()); - EXPECT_CALL(listener_config.socket_factory_, localAddress()).WillOnce(ReturnRef(local_address)); + EXPECT_CALL(listener_config, listenSocketFactories()); + EXPECT_CALL( + *static_cast(listener_config.socket_factories_[0].get()), + localAddress()) + .WillOnce(ReturnRef(local_address)); EXPECT_CALL(listener_config, bindToPort()).WillOnce(Return(true)); - EXPECT_CALL(listener_config.socket_factory_, socketType()) + EXPECT_CALL( + *static_cast(listener_config.socket_factories_[0].get()), + socketType()) .WillOnce(Return(Network::Socket::Type::Stream)); EXPECT_CALL(server_, options()).WillOnce(ReturnRef(options)); EXPECT_CALL(options, concurrency()).WillOnce(Return(1)); - EXPECT_CALL(listener_config.socket_factory_, getListenSocket(_)); + EXPECT_CALL( + *static_cast(listener_config.socket_factories_[0].get()), + getListenSocket(_)); HotRestartMessage::Request request; request.mutable_pass_listen_socket()->set_address("unix://domain.socket"); diff --git a/test/server/listener_manager_impl_quic_only_test.cc b/test/server/listener_manager_impl_quic_only_test.cc index fe5e923f6384f..aceb86ff2ad6a 100644 --- a/test/server/listener_manager_impl_quic_only_test.cc +++ b/test/server/listener_manager_impl_quic_only_test.cc @@ -138,7 +138,7 @@ TEST_P(ListenerManagerImplQuicOnlyTest, QuicListenerFactoryAndSslContext) { ->listenerFactory() .isTransportConnectionless()); Network::SocketSharedPtr listen_socket = - manager_->listeners().front().get().listenSocketFactory().getListenSocket(0); + manager_->listeners().front().get().listenSocketFactories()[0]->getListenSocket(0); Network::UdpPacketWriterPtr udp_packet_writer = manager_->listeners() diff --git a/test/server/listener_manager_impl_test.cc b/test/server/listener_manager_impl_test.cc index ccb6f4989513b..e00961d964afc 100644 --- a/test/server/listener_manager_impl_test.cc +++ b/test/server/listener_manager_impl_test.cc @@ -251,6 +251,142 @@ name: bar "error adding listener: 'bar' has duplicate address '127.0.0.1:1234' as existing listener"); } +TEST_P(ListenerManagerImplWithRealFiltersTest, DuplicateNonIPAddressNotAllowed) { + const std::string yaml1 = R"EOF( +name: foo +address: + pipe: + path: /path +filter_chains: +- filters: [] + name: foo + )EOF"; + + const std::string yaml2 = R"EOF( +name: bar +address: + pipe: + path: /path +filter_chains: +- filters: [] + name: foo + )EOF"; + + addOrUpdateListener(parseListenerFromV3Yaml(yaml1)); + EXPECT_THROW_WITH_MESSAGE( + addOrUpdateListener(parseListenerFromV3Yaml(yaml2)), EnvoyException, + "error adding listener: 'bar' has duplicate address '/path' as existing listener"); +} + +TEST_P(ListenerManagerImplWithRealFiltersTest, MultipleAddressesDuplicatePortNotAllowed) { + const std::string yaml1 = R"EOF( +name: foo +addresses: +- socket_address: + address: 127.0.0.1 + port_value: 1234 +- socket_address: + address: 127.0.0.2 + port_value: 1234 +filter_chains: +- filters: [] + name: foo + )EOF"; + + const std::string yaml2 = R"EOF( +name: bar +addresses: +- socket_address: + address: 127.0.0.1 + port_value: 1234 +- socket_address: + address: 127.0.0.3 + port_value: 1234 +filter_chains: +- filters: [] + name: foo + )EOF"; + + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)).Times(2); + addOrUpdateListener(parseListenerFromV3Yaml(yaml1)); + EXPECT_THROW_WITH_MESSAGE(addOrUpdateListener(parseListenerFromV3Yaml(yaml2)), EnvoyException, + "error adding listener: 'bar' has duplicate address " + "'127.0.0.1:1234,127.0.0.3:1234' as existing listener"); +} + +TEST_P(ListenerManagerImplWithRealFiltersTest, + MultipleAddressesDuplicatePortNotAllowedWithZeroPortAndNonBind) { + const std::string yaml1 = R"EOF( +name: foo +addresses: +- socket_address: + address: 127.0.0.1 + port_value: 0 +- socket_address: + address: 127.0.0.2 + port_value: 0 +bind_to_port: false +filter_chains: +- filters: [] + name: foo + )EOF"; + + const std::string yaml2 = R"EOF( +name: bar +addresses: +- socket_address: + address: 127.0.0.1 + port_value: 0 +- socket_address: + address: 127.0.0.3 + port_value: 0 +bind_to_port: false +filter_chains: +- filters: [] + name: foo + )EOF"; + + addOrUpdateListener(parseListenerFromV3Yaml(yaml1)); + EXPECT_THROW_WITH_MESSAGE(addOrUpdateListener(parseListenerFromV3Yaml(yaml2)), EnvoyException, + "error adding listener: 'bar' has duplicate address " + "'127.0.0.1:0,127.0.0.3:0' as existing listener"); +} + +TEST_P(ListenerManagerImplWithRealFiltersTest, AllowCreateListenerWithMutipleZeroPorts) { + const std::string yaml1 = R"EOF( +name: foo +addresses: +- socket_address: + address: 127.0.0.1 + port_value: 0 +- socket_address: + address: 127.0.0.2 + port_value: 0 +filter_chains: +- filters: [] + name: foo + )EOF"; + + const std::string yaml2 = R"EOF( +name: bar +addresses: +- socket_address: + address: 127.0.0.1 + port_value: 0 +- socket_address: + address: 127.0.0.2 + port_value: 0 +filter_chains: +- filters: [] + name: foo + )EOF"; + + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)).Times(2); + addOrUpdateListener(parseListenerFromV3Yaml(yaml1)); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)).Times(2); + addOrUpdateListener(parseListenerFromV3Yaml(yaml2)); +} + TEST_P(ListenerManagerImplWithRealFiltersTest, SetListenerPerConnectionBufferLimit) { const std::string yaml = R"EOF( address: @@ -578,6 +714,109 @@ bind_to_port: false EXPECT_EQ(1UL, server_.stats_store_.counterFromString("listener.127.0.0.1_1234.foo").value()); } +TEST_P(ListenerManagerImplWithRealFiltersTest, UsingFirstAddressAsStatsPrefixForMultipleAddresses) { + TestScopedRuntime scoped_runtime; + scoped_runtime.mergeValues({{"envoy.reloadable_features.no_extension_lookup_by_name", "false"}}); + + TestStatsConfigFactory filter; + Registry::InjectFactory registered(filter); + + const std::string yaml = R"EOF( +addresses: +- socket_address: + address: 127.0.0.1 + port_value: 1234 +- socket_address: + address: 127.0.0.2 + port_value: 4567 +bind_to_port: false +filter_chains: +- filters: + - name: stats_test + name: foo + )EOF"; + + EXPECT_CALL(listener_factory_, + createListenSocket(_, _, _, ListenerComponentFactory::BindType::NoBind, _, 0)) + .Times(2); + addOrUpdateListener(parseListenerFromV3Yaml(yaml)); + manager_->listeners().front().get().listenerScope().counterFromString("foo").inc(); + + EXPECT_EQ(1UL, server_.stats_store_.counterFromString("bar").value()); + EXPECT_EQ(1UL, server_.stats_store_.counterFromString("listener.127.0.0.1_1234.foo").value()); +} + +TEST_P(ListenerManagerImplTest, BothAddressAndAddressesAreSpecified) { + const std::string yaml = R"EOF( + name: "foo" + address: + socket_address: + address: "::0" + port_value: 13333 + addresses: + - socket_address: + address: "::0" + port_value: 13334 + filter_chains: + - filters: [] + )EOF"; + + EXPECT_THROW_WITH_MESSAGE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true), + EnvoyException, + "listener foo: only one of `address` and `addresses` can be set."); +} + +TEST_P(ListenerManagerImplTest, NoneOfAddressAndAddressesSpecified) { + const std::string yaml = R"EOF( + name: "foo" + filter_chains: + - filters: [] + )EOF"; + + EXPECT_THROW_WITH_MESSAGE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true), + EnvoyException, + "listener foo: one of `address` and `addresses` must be set."); +} + +TEST_P(ListenerManagerImplTest, MultipleSocketTypeSpecifiedInAddresses) { + const std::string yaml = R"EOF( + name: "foo" + addresses: + - socket_address: + protocol: TCP + address: "::0" + port_value: 13333 + - socket_address: + protocol: UDP + address: "::0" + port_value: 13334 + filter_chains: + - filters: [] + )EOF"; + + EXPECT_THROW_WITH_MESSAGE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true), + EnvoyException, + "listener foo: has different socket type. The listener only " + "support same socket type for all the addresses."); +} + +TEST_P(ListenerManagerImplTest, RejectMutlipleInternalAddresses) { + const std::string yaml = R"EOF( + name: "foo" + addresses: + - envoy_internal_address: + server_listener_name: a_listener_name_1 + - envoy_internal_address: + server_listener_name: a_listener_name_2 + filter_chains: + - filters: [] + )EOF"; + + EXPECT_THROW_WITH_MESSAGE(manager_->addOrUpdateListener(parseListenerFromV3Yaml(yaml), "", true), + EnvoyException, + "listener foo: internal address doesn't support multiple addresses."); +} + TEST_P(ListenerManagerImplTest, RejectIpv4CompatOnIpv4Address) { const std::string yaml = R"EOF( name: "foo" @@ -1716,6 +1955,132 @@ per_connection_buffer_limit_bytes: 999 EXPECT_CALL(*listener_foo, onDestroy()); } +TEST_P(ListenerManagerImplTest, UpdateListenerWithCompatibleAddresses) { + InSequence s; + + EXPECT_CALL(*worker_, start(_, _)); + manager_->startWorkers(guard_dog_, callback_.AsStdFunction()); + + // Add and initialize foo listener. + const std::string listener_foo_yaml = R"EOF( +name: foo +addresses: +- socket_address: + address: 127.0.0.1 + port_value: 1234 +- socket_address: + address: 127.0.0.2 + port_value: 4567 +filter_chains: +- filters: [] + )EOF"; + + ListenerHandle* listener_foo = expectListenerCreate(true, true); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)).Times(2); + EXPECT_CALL(listener_foo->target_, initialize()); + EXPECT_TRUE(addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml))); + checkStats(__LINE__, 1, 0, 0, 1, 0, 0, 0); + EXPECT_CALL(*worker_, addListener(_, _, _, _)); + listener_foo->target_.ready(); + worker_->callAddCompletion(); + EXPECT_EQ(1UL, manager_->listeners().size()); + checkStats(__LINE__, 1, 0, 0, 0, 1, 0, 0); + + // Update foo into warming. + const std::string listener_foo_update1_yaml = R"EOF( +name: foo +addresses: +- socket_address: + address: 127.0.0.2 + port_value: 4567 +- socket_address: + address: 127.0.0.1 + port_value: 1234 +per_connection_buffer_limit_bytes: 999 +filter_chains: +- filters: [] + )EOF"; + + ListenerHandle* listener_foo_update1 = expectListenerCreate(true, true); + EXPECT_CALL(*listener_factory_.socket_, duplicate()).Times(2); + EXPECT_CALL(listener_foo_update1->target_, initialize()); + EXPECT_TRUE(addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_update1_yaml))); + + // Should be both active and warming now. + EXPECT_EQ(1UL, manager_->listeners(ListenerManager::WARMING).size()); + EXPECT_EQ(1UL, manager_->listeners(ListenerManager::ACTIVE).size()); + checkStats(__LINE__, 1, 1, 0, 1, 1, 0, 0); + + EXPECT_CALL(*listener_foo_update1, onDestroy()); + EXPECT_CALL(*listener_foo, onDestroy()); +} + +TEST_P(ListenerManagerImplTest, UpdateListenerWithCompatibleZeroPortAddresses) { + InSequence s; + + EXPECT_CALL(*worker_, start(_, _)); + manager_->startWorkers(guard_dog_, callback_.AsStdFunction()); + + // Add and initialize foo listener. + const std::string listener_foo_yaml = R"EOF( +name: foo +addresses: +- socket_address: + address: 127.0.0.1 + port_value: 0 +- socket_address: + address: 127.0.0.2 + port_value: 1234 +- socket_address: + address: 127.0.0.1 + port_value: 0 +filter_chains: +- filters: [] + )EOF"; + + ListenerHandle* listener_foo = expectListenerCreate(true, true); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)).Times(3); + EXPECT_CALL(listener_foo->target_, initialize()); + EXPECT_TRUE(addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml))); + checkStats(__LINE__, 1, 0, 0, 1, 0, 0, 0); + EXPECT_CALL(*worker_, addListener(_, _, _, _)); + listener_foo->target_.ready(); + worker_->callAddCompletion(); + EXPECT_EQ(1UL, manager_->listeners().size()); + checkStats(__LINE__, 1, 0, 0, 0, 1, 0, 0); + + // Update foo into warming. + const std::string listener_foo_update1_yaml = R"EOF( +name: foo +addresses: +- socket_address: + address: 127.0.0.1 + port_value: 0 +- socket_address: + address: 127.0.0.2 + port_value: 1234 +- socket_address: + address: 127.0.0.1 + port_value: 0 +per_connection_buffer_limit_bytes: 999 +filter_chains: +- filters: [] + )EOF"; + + ListenerHandle* listener_foo_update1 = expectListenerCreate(true, true); + EXPECT_CALL(*listener_factory_.socket_, duplicate()).Times(3); + EXPECT_CALL(listener_foo_update1->target_, initialize()); + EXPECT_TRUE(addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_update1_yaml))); + + // Should be both active and warming now. + EXPECT_EQ(1UL, manager_->listeners(ListenerManager::WARMING).size()); + EXPECT_EQ(1UL, manager_->listeners(ListenerManager::ACTIVE).size()); + checkStats(__LINE__, 1, 1, 0, 1, 1, 0, 0); + + EXPECT_CALL(*listener_foo_update1, onDestroy()); + EXPECT_CALL(*listener_foo, onDestroy()); +} + TEST_P(ListenerManagerImplTest, AddReusableDrainingListener) { InSequence s; @@ -1776,6 +2141,65 @@ name: foo EXPECT_CALL(*listener_foo2, onDestroy()); } +TEST_P(ListenerManagerImplTest, AddReusableDrainingListenerWithMultiAddresses) { + InSequence s; + + EXPECT_CALL(*worker_, start(_, _)); + manager_->startWorkers(guard_dog_, callback_.AsStdFunction()); + + // Add foo listener directly into active. + const std::string listener_foo_yaml = R"EOF( +name: foo +addresses: +- socket_address: + address: 127.0.0.1 + port_value: 1234 +- socket_address: + address: 127.0.0.2 + port_value: 1234 +filter_chains: +- filters: [] + )EOF"; + + ListenerHandle* listener_foo = expectListenerCreate(false, true); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)).Times(2); + EXPECT_CALL(*worker_, addListener(_, _, _, _)); + EXPECT_TRUE(addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml))); + worker_->callAddCompletion(); + checkStats(__LINE__, 1, 0, 0, 0, 1, 0, 0); + + // Remove foo into draining. + std::function stop_completion; + EXPECT_CALL(*worker_, stopListener(_, _)) + .WillOnce(Invoke( + [&stop_completion](Network::ListenerConfig&, std::function completion) -> void { + ASSERT_TRUE(completion != nullptr); + stop_completion = std::move(completion); + })); + EXPECT_CALL(*listener_foo->drain_manager_, startDrainSequence(_)); + EXPECT_TRUE(manager_->removeListener("foo")); + checkStats(__LINE__, 1, 0, 1, 0, 0, 1, 0); + EXPECT_CALL(*worker_, removeListener(_, _)); + listener_foo->drain_manager_->drain_sequence_completion_(); + checkStats(__LINE__, 1, 0, 1, 0, 0, 1, 0); + + // Add foo again. + ListenerHandle* listener_foo2 = expectListenerCreate(false, true); + EXPECT_CALL(*listener_factory_.socket_, duplicate()).Times(2); + EXPECT_CALL(*worker_, addListener(_, _, _, _)); + EXPECT_TRUE(addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml))); + worker_->callAddCompletion(); + checkStats(__LINE__, 2, 0, 1, 0, 1, 1, 0); + + stop_completion(); + + EXPECT_CALL(*listener_foo, onDestroy()); + worker_->callRemovalCompletion(); + checkStats(__LINE__, 2, 0, 1, 0, 1, 0, 0); + + EXPECT_CALL(*listener_foo2, onDestroy()); +} + TEST_P(ListenerManagerImplTest, AddClosedDrainingListener) { InSequence s; @@ -1829,6 +2253,57 @@ name: foo EXPECT_CALL(*listener_foo2, onDestroy()); } +TEST_P(ListenerManagerImplTest, AddClosedDrainingListenerWithMultiAddresses) { + InSequence s; + + EXPECT_CALL(*worker_, start(_, _)); + manager_->startWorkers(guard_dog_, callback_.AsStdFunction()); + + // Add foo listener directly into active. + const std::string listener_foo_yaml = R"EOF( +name: foo +addresses: +- socket_address: + address: 127.0.0.1 + port_value: 1234 +- socket_address: + address: 127.0.0.2 + port_value: 4567 +filter_chains: +- filters: [] + )EOF"; + + ListenerHandle* listener_foo = expectListenerCreate(false, true); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)).Times(2); + EXPECT_CALL(*worker_, addListener(_, _, _, _)); + EXPECT_TRUE(addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml))); + worker_->callAddCompletion(); + checkStats(__LINE__, 1, 0, 0, 0, 1, 0, 0); + + // Remove foo into draining. + EXPECT_CALL(*worker_, stopListener(_, _)); + EXPECT_CALL(*listener_factory_.socket_, close()).Times(2); + EXPECT_CALL(*listener_foo->drain_manager_, startDrainSequence(_)); + EXPECT_TRUE(manager_->removeListener("foo")); + checkStats(__LINE__, 1, 0, 1, 0, 0, 1, 0); + EXPECT_CALL(*worker_, removeListener(_, _)); + listener_foo->drain_manager_->drain_sequence_completion_(); + checkStats(__LINE__, 1, 0, 1, 0, 0, 1, 0); + + ListenerHandle* listener_foo2 = expectListenerCreate(false, true); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)).Times(2); + EXPECT_CALL(*worker_, addListener(_, _, _, _)); + EXPECT_TRUE(addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml))); + worker_->callAddCompletion(); + checkStats(__LINE__, 2, 0, 1, 0, 1, 1, 0); + + EXPECT_CALL(*listener_foo, onDestroy()); + worker_->callRemovalCompletion(); + checkStats(__LINE__, 2, 0, 1, 0, 1, 0, 0); + + EXPECT_CALL(*listener_foo2, onDestroy()); +} + TEST_P(ListenerManagerImplTest, BindToPortEqualToFalse) { InSequence s; auto mock_interface = std::make_unique( @@ -6392,6 +6867,106 @@ traffic_direction: INBOUND EXPECT_CALL(*listener_foo_expect_reuse_socket, onDestroy()); } +TEST_P(ListenerManagerImplTest, + ListenSocketFactoryIsClonedFromListenerDrainingFilterChainWithMultipleAddresses) { + InSequence s; + + EXPECT_CALL(*worker_, start(_, _)); + manager_->startWorkers(guard_dog_, callback_.AsStdFunction()); + + // Add foo listener. + const std::string listener_foo_yaml = R"EOF( +name: foo +traffic_direction: INBOUND +addresses: +- socket_address: + address: 127.0.0.1 + port_value: 1234 +- socket_address: + address: 127.0.0.2 + port_value: 4567 +filter_chains: +- filters: [] + )EOF"; + + ListenerHandle* listener_foo = expectListenerCreate(true, true); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, default_bind_type, _, 0)).Times(2); + EXPECT_CALL(listener_foo->target_, initialize()); + EXPECT_TRUE(addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml))); + checkStats(__LINE__, 1, 0, 0, 1, 0, 0, 0); + EXPECT_CALL(*worker_, addListener(_, _, _, _)); + listener_foo->target_.ready(); + worker_->callAddCompletion(); + EXPECT_EQ(1UL, manager_->listeners().size()); + + // Update foo into warming. + const std::string listener_foo_update1_yaml = R"EOF( +name: foo +traffic_direction: INBOUND +addresses: +- socket_address: + address: 127.0.0.1 + port_value: 1234 +- socket_address: + address: 127.0.0.2 + port_value: 4567 +filter_chains: +- filters: + filter_chain_match: + destination_port: 1234 + )EOF"; + + ListenerHandle* listener_foo_update1 = expectListenerOverridden(true, listener_foo); + EXPECT_CALL(*listener_factory_.socket_, duplicate()).Times(2); + EXPECT_CALL(listener_foo_update1->target_, initialize()); + EXPECT_TRUE(addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_update1_yaml))); + EXPECT_EQ(1UL, manager_->listeners().size()); + EXPECT_EQ(1, server_.stats_store_.counter("listener_manager.listener_in_place_updated").value()); + checkStats(__LINE__, 1, 1, 0, 1, 1, 0, 0); + + // The warmed up starts the drain timer. + EXPECT_CALL(*worker_, addListener(_, _, _, _)); + EXPECT_CALL(server_.options_, drainTime()).WillOnce(Return(std::chrono::seconds(600))); + Event::MockTimer* filter_chain_drain_timer = new Event::MockTimer(&server_.dispatcher_); + EXPECT_CALL(*filter_chain_drain_timer, enableTimer(std::chrono::milliseconds(600000), _)); + listener_foo_update1->target_.ready(); + checkStats(__LINE__, 1, 1, 0, 0, 1, 0, 1); + EXPECT_CALL(*worker_, removeFilterChains(_, _, _)); + filter_chain_drain_timer->invokeCallback(); + + // Stop the active listener listener_foo_update1. + std::function stop_completion; + EXPECT_CALL(*worker_, stopListener(_, _)) + .WillOnce(Invoke( + [&stop_completion](Network::ListenerConfig&, std::function completion) -> void { + ASSERT_TRUE(completion != nullptr); + stop_completion = std::move(completion); + })); + EXPECT_CALL(*listener_foo_update1->drain_manager_, startDrainSequence(_)); + EXPECT_TRUE(manager_->removeListener("foo")); + + EXPECT_CALL(*worker_, removeListener(_, _)); + listener_foo_update1->drain_manager_->drain_sequence_completion_(); + + EXPECT_CALL(*listener_foo_update1, onDestroy()); + worker_->callRemovalCompletion(); + + // The snapshot of the listener manager is + // 1) listener_foo is draining filter chain. The listen socket is open. + // 2) No listen is active. Note that listener_foo_update1 is stopped. + // + // The next step is to add a listener on the same socket address and the listen socket of + // listener_foo will be duplicated. + auto listener_foo_expect_reuse_socket = expectListenerCreate(true, true); + EXPECT_CALL(listener_factory_, createListenSocket(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(*listener_factory_.socket_, duplicate()).Times(2); + EXPECT_CALL(listener_foo_expect_reuse_socket->target_, initialize()); + EXPECT_TRUE(addOrUpdateListener(parseListenerFromV3Yaml(listener_foo_yaml), "version1", true)); + + EXPECT_CALL(*listener_foo, onDestroy()); + EXPECT_CALL(*listener_foo_expect_reuse_socket, onDestroy()); +} + TEST(ListenerMessageUtilTest, ListenerMessageSameAreEquivalent) { envoy::config::listener::v3::Listener listener1; envoy::config::listener::v3::Listener listener2; @@ -6603,7 +7178,7 @@ TEST_P(ListenerManagerImplTest, UdpDefaultWriterConfig) { addOrUpdateListener(listener); EXPECT_EQ(1U, manager_->listeners().size()); Network::SocketSharedPtr listen_socket = - manager_->listeners().front().get().listenSocketFactory().getListenSocket(0); + manager_->listeners().front().get().listenSocketFactories()[0]->getListenSocket(0); Network::UdpPacketWriterPtr udp_packet_writer = manager_->listeners() .front() diff --git a/test/tools/schema_validator/test/config/lds_pgv_fail.yaml b/test/tools/schema_validator/test/config/lds_pgv_fail.yaml index cbb48e3e8d4a3..9cc826d6d74fe 100644 --- a/test/tools/schema_validator/test/config/lds_pgv_fail.yaml +++ b/test/tools/schema_validator/test/config/lds_pgv_fail.yaml @@ -1,10 +1,14 @@ resources: - "@type": type.googleapis.com/envoy.config.listener.v3.Listener name: ingress_https + address: + socket_address: + protocol: TCP + address: 0.0.0.0 + port_value: 9901 filter_chains: - filters: - - name: envoy.filters.network.http_connection_manager - typed_config: + - typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager stat_prefix: ingress_http use_remote_address: true diff --git a/test/tools/schema_validator/test/schema_validator_test.cc b/test/tools/schema_validator/test/schema_validator_test.cc index fb4e84fbd8711..8676e621a70b7 100644 --- a/test/tools/schema_validator/test/schema_validator_test.cc +++ b/test/tools/schema_validator/test/schema_validator_test.cc @@ -98,9 +98,8 @@ TEST_F(SchemaValidatorTest, BootstrapPgvFail) { TEST_F(SchemaValidatorTest, LdsRecursivePgvFail) { EXPECT_THROW_WITH_REGEX( run("schema_validator_tool -c {} -t discovery_response", "lds_pgv_fail.yaml"), EnvoyException, - "Proto constraint validation failed \\(HttpConnectionManagerValidationError.RouteConfig:" - ".* caused by RouteConfigurationValidationError.VirtualHosts.*" - "VirtualHostValidationError.Domains: value must contain at least 1 item\\(s\\)"); + "Proto constraint validation failed \\(ListenerValidationError.FilterChains\\[0\\]: embedded " + "message failed validation"); } } // namespace Envoy diff --git a/tools/socket_passing.py b/tools/socket_passing.py index 4c94b18aa196d..c77ee9a211900 100755 --- a/tools/socket_passing.py +++ b/tools/socket_passing.py @@ -41,37 +41,38 @@ def generate_new_config(original_yaml, admin_address, updated_json): index = 0 for discovered in discovered_listeners['listener_statuses']: replaced = False - if 'pipe' in discovered['local_address']: - path = discovered['local_address']['pipe']['path'] - for index in range(index + 1, len(raw_yaml) - 1): - if 'pipe:' in raw_yaml[index] and 'path:' in raw_yaml[index + 1]: - raw_yaml[index + 1] = re.sub( - 'path:.*', 'path: "' + path + '"', raw_yaml[index + 1]) - replaced = True - break - else: - addr = discovered['local_address']['socket_address']['address'] - port = str(discovered['local_address']['socket_address']['port_value']) - if addr[0] == '[': - addr = addr[1:-1] # strip [] from ipv6 address. - for index in range(index + 1, len(raw_yaml) - 2): - if ('socket_address:' in raw_yaml[index] - and 'address:' in raw_yaml[index + 1] - and 'port_value:' in raw_yaml[index + 2]): - raw_yaml[index + 1] = re.sub( - 'address:.*', 'address: "' + addr + '"', raw_yaml[index + 1]) - raw_yaml[index + 2] = re.sub( - 'port_value:.*', 'port_value: ' + port, raw_yaml[index + 2]) - replaced = True - break - if replaced: - sys.stderr.write( - 'replaced listener at line ' + str(index) + ' with ' + str(discovered) - + '\n') - else: - sys.stderr.write( - 'Failed to replace a discovered listener ' + str(discovered) + '\n') - return False + for local_address in discovered['local_addresses']: + if 'pipe' in local_address: + path = local_address['pipe']['path'] + for index in range(index + 1, len(raw_yaml) - 1): + if 'pipe:' in raw_yaml[index] and 'path:' in raw_yaml[index + 1]: + raw_yaml[index + 1] = re.sub( + 'path:.*', 'path: "' + path + '"', raw_yaml[index + 1]) + replaced = True + break + else: + addr = local_address['socket_address']['address'] + port = str(local_address['socket_address']['port_value']) + if addr[0] == '[': + addr = addr[1:-1] # strip [] from ipv6 address. + for index in range(index + 1, len(raw_yaml) - 2): + if ('socket_address:' in raw_yaml[index] + and 'address:' in raw_yaml[index + 1] + and 'port_value:' in raw_yaml[index + 2]): + raw_yaml[index + 1] = re.sub( + 'address:.*', 'address: "' + addr + '"', raw_yaml[index + 1]) + raw_yaml[index + 2] = re.sub( + 'port_value:.*', 'port_value: ' + port, raw_yaml[index + 2]) + replaced = True + break + if replaced: + sys.stderr.write( + 'replaced listener at line ' + str(index) + ' with ' + str(discovered) + + '\n') + else: + sys.stderr.write( + 'Failed to replace a discovered listener ' + str(discovered) + '\n') + return False with open(updated_json, 'w') as outfile: outfile.writelines(raw_yaml) finally: