Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Minor Behavior Changes
* http: serve HEAD requests from cache.
* http: the behavior of the *present_match* in route header matcher changed. The value of *present_match* is ignored in the past. The new behavior is *present_match* performed when value is true. absent match performed when the value is false. Please reference :ref:`present_match
<envoy_v3_api_field_config.route.v3.HeaderMatcher.present_match>`.
* listener: added an option when balancing across active listeners and wildcard matching is used to return the listener that matches the IP family type associated with the listener's socket address. Any unexpected behavioral changes can be reverted by setting runtime guard ``envoy.reloadable_features.listener_wildcard_match_ip_family`` to false.
* listener: respect the :ref:`connection balance config <envoy_v3_api_field_config.listener.v3.Listener.connection_balance_config>`
defined within the listener where the sockets are redirected to. Clear that field to restore the previous behavior.
* tcp: switched to the new connection pool by default. Any unexpected behavioral changes can be reverted by setting runtime guard ``envoy.reloadable_features.new_tcp_connection_pool`` to false.
Expand Down
1 change: 1 addition & 0 deletions source/common/runtime/runtime_features.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ constexpr const char* runtime_features[] = {
"envoy.reloadable_features.http2_skip_encoding_empty_trailers",
"envoy.reloadable_features.improved_stream_limit_handling",
"envoy.reloadable_features.internal_redirects_with_body",
"envoy.reloadable_features.listener_wildcard_match_ip_family",
"envoy.reloadable_features.new_tcp_connection_pool",
"envoy.reloadable_features.prefer_quic_kernel_bpf_packet_routing",
"envoy.reloadable_features.preserve_downstream_scheme",
Expand Down
1 change: 1 addition & 0 deletions source/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ envoy_cc_library(
"//envoy/network:filter_interface",
"//envoy/network:listen_socket_interface",
"//envoy/network:listener_interface",
"//envoy/runtime:runtime_interface",
"//envoy/server:listener_manager_interface",
"//envoy/stats:timespan_interface",
"//source/common/common:linked_object",
Expand Down
39 changes: 28 additions & 11 deletions source/server/connection_handler_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "source/common/event/deferred_task.h"
#include "source/common/network/utility.h"
#include "source/common/runtime/runtime_features.h"
#include "source/server/active_tcp_listener.h"

namespace Envoy {
Expand Down Expand Up @@ -191,17 +192,33 @@ ConnectionHandlerImpl::getBalancedHandlerByAddress(const Network::Address::Insta
// 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.
listener_it =
std::find_if(listeners_.begin(), listeners_.end(),
[&address](const std::pair<Network::Address::InstanceConstSharedPtr,
ConnectionHandlerImpl::ActiveListenerDetails>& p) {
return absl::holds_alternative<std::reference_wrapper<ActiveTcpListener>>(
p.second.typed_listener_) &&
p.second.listener_->listener() != nullptr &&
p.first->type() == Network::Address::Type::Ip &&
p.first->ip()->port() == address.ip()->port() &&
p.first->ip()->isAnyAddress();
});
if (Runtime::runtimeFeatureEnabled(
"envoy.reloadable_features.listener_wildcard_match_ip_family")) {
listener_it =
std::find_if(listeners_.begin(), listeners_.end(),
[&address](const std::pair<Network::Address::InstanceConstSharedPtr,
ConnectionHandlerImpl::ActiveListenerDetails>& p) {
return absl::holds_alternative<std::reference_wrapper<ActiveTcpListener>>(
p.second.typed_listener_) &&
p.second.listener_->listener() != nullptr &&
p.first->type() == Network::Address::Type::Ip &&
p.first->ip()->port() == address.ip()->port() &&
p.first->ip()->isAnyAddress() &&
p.first->ip()->version() == address.ip()->version();
});
} else {
listener_it =
std::find_if(listeners_.begin(), listeners_.end(),
[&address](const std::pair<Network::Address::InstanceConstSharedPtr,
ConnectionHandlerImpl::ActiveListenerDetails>& p) {
return absl::holds_alternative<std::reference_wrapper<ActiveTcpListener>>(
p.second.typed_listener_) &&
p.second.listener_->listener() != nullptr &&
p.first->type() == Network::Address::Type::Ip &&
p.first->ip()->port() == address.ip()->port() &&
p.first->ip()->isAnyAddress();
});
}
return (listener_it != listeners_.end())
? Network::BalancedConnectionHandlerOptRef(
ActiveTcpListenerOptRef(absl::get<std::reference_wrapper<ActiveTcpListener>>(
Expand Down
1 change: 1 addition & 0 deletions test/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ envoy_cc_test(
"//test/mocks/api:api_mocks",
"//test/mocks/network:network_mocks",
"//test/test_common:network_utility_lib",
"//test/test_common:test_runtime_lib",
"//test/test_common:threadsafe_singleton_injector_lib",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
"@envoy_api//envoy/config/listener/v3:pkg_cc_proto",
Expand Down
157 changes: 157 additions & 0 deletions test/server/connection_handler_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "test/mocks/common.h"
#include "test/mocks/network/mocks.h"
#include "test/test_common/network_utility.h"
#include "test/test_common/test_runtime.h"
#include "test/test_common/threadsafe_singleton_injector.h"

#include "gmock/gmock.h"
Expand Down Expand Up @@ -746,6 +747,162 @@ TEST_F(ConnectionHandlerTest, FallbackToWildcardListener) {
EXPECT_CALL(*access_log_, log(_, _, _, _));
}

TEST_F(ConnectionHandlerTest, OldBehaviorMatchFirstWildcardListener) {
auto scoped_runtime = std::make_unique<TestScopedRuntime>();

Runtime::LoaderSingleton::getExisting()->mergeValues(
{{"envoy.reloadable_features.listener_wildcard_match_ip_family", "false"}});

Network::TcpListenerCallbacks* listener_callbacks1;
auto listener1 = new NiceMock<Network::MockListener>();
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(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(normal_address));
handler_->addListener(absl::nullopt, *test_listener1);

auto ipv4_overridden_filter_chain_manager =
std::make_shared<NiceMock<Network::MockFilterChainManager>>();
Network::TcpListenerCallbacks* ipv4_any_listener_callbacks;
auto listener2 = new NiceMock<Network::MockListener>();
TestListener* ipv4_any_listener =
addListener(1, 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(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(any_address));
handler_->addListener(absl::nullopt, *ipv4_any_listener);

auto ipv6_overridden_filter_chain_manager =
std::make_shared<NiceMock<Network::MockFilterChainManager>>();
Network::TcpListenerCallbacks* ipv6_any_listener_callbacks;
auto listener3 = new NiceMock<Network::MockListener>();
TestListener* ipv6_any_listener =
addListener(1, 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(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(any_address_ipv6));
handler_->addListener(absl::nullopt, *ipv6_any_listener);

Network::MockListenerFilter* test_filter = new Network::MockListenerFilter();
EXPECT_CALL(*test_filter, destroy_());
Network::MockConnectionSocket* accepted_socket = new NiceMock<Network::MockConnectionSocket>();
bool redirected = false;
EXPECT_CALL(factory_, createListenerFilterChain(_))
.WillRepeatedly(Invoke([&](Network::ListenerFilterManager& manager) -> bool {
// Insert the Mock filter.
if (!redirected) {
manager.addAcceptFilter(listener_filter_matcher_,
Network::ListenerFilterPtr{test_filter});
redirected = true;
}
return true;
}));

Network::Address::InstanceConstSharedPtr alt_address(
new Network::Address::Ipv6Instance("::2", 80, nullptr));
EXPECT_CALL(*test_filter, onAccept(_))
.WillOnce(Invoke([&](Network::ListenerFilterCallbacks& cb) -> Network::FilterStatus {
cb.socket().addressProvider().restoreLocalAddress(alt_address);
return Network::FilterStatus::Continue;
}));
EXPECT_CALL(manager_, findFilterChain(_)).Times(0);
EXPECT_CALL(*ipv4_overridden_filter_chain_manager, findFilterChain(_))
.WillOnce(Return(filter_chain_.get()));
EXPECT_CALL(*ipv6_overridden_filter_chain_manager, findFilterChain(_)).Times(0);
auto* connection = new NiceMock<Network::MockServerConnection>();
EXPECT_CALL(dispatcher_, createServerConnection_()).WillOnce(Return(connection));
EXPECT_CALL(factory_, createNetworkFilterChain(_, _)).WillOnce(Return(true));
listener_callbacks1->onAccept(Network::ConnectionSocketPtr{accepted_socket});
EXPECT_EQ(1UL, handler_->numConnections());

EXPECT_CALL(*listener3, onDestroy());
EXPECT_CALL(*listener2, onDestroy());
EXPECT_CALL(*listener1, onDestroy());
EXPECT_CALL(*access_log_, log(_, _, _, _));
}

TEST_F(ConnectionHandlerTest, MatchIPv6WildcardListener) {
auto scoped_runtime = std::make_unique<TestScopedRuntime>();

Network::TcpListenerCallbacks* listener_callbacks1;
auto listener1 = new NiceMock<Network::MockListener>();
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(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(normal_address));
handler_->addListener(absl::nullopt, *test_listener1);

auto ipv4_overridden_filter_chain_manager =
std::make_shared<NiceMock<Network::MockFilterChainManager>>();
Network::TcpListenerCallbacks* ipv4_any_listener_callbacks;
auto listener2 = new NiceMock<Network::MockListener>();
TestListener* ipv4_any_listener =
addListener(1, 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(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(any_address));
handler_->addListener(absl::nullopt, *ipv4_any_listener);

auto ipv6_overridden_filter_chain_manager =
std::make_shared<NiceMock<Network::MockFilterChainManager>>();
Network::TcpListenerCallbacks* ipv6_any_listener_callbacks;
auto listener3 = new NiceMock<Network::MockListener>();
TestListener* ipv6_any_listener =
addListener(1, 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(*socket_factory_, localAddress()).WillRepeatedly(ReturnRef(any_address_ipv6));
handler_->addListener(absl::nullopt, *ipv6_any_listener);

Network::MockListenerFilter* test_filter = new Network::MockListenerFilter();
EXPECT_CALL(*test_filter, destroy_());
Network::MockConnectionSocket* accepted_socket = new NiceMock<Network::MockConnectionSocket>();
bool redirected = false;
EXPECT_CALL(factory_, createListenerFilterChain(_))
.WillRepeatedly(Invoke([&](Network::ListenerFilterManager& manager) -> bool {
// Insert the Mock filter.
if (!redirected) {
manager.addAcceptFilter(listener_filter_matcher_,
Network::ListenerFilterPtr{test_filter});
redirected = true;
}
return true;
}));

Network::Address::InstanceConstSharedPtr alt_address(
new Network::Address::Ipv6Instance("::2", 80, nullptr));
EXPECT_CALL(*test_filter, onAccept(_))
.WillOnce(Invoke([&](Network::ListenerFilterCallbacks& cb) -> Network::FilterStatus {
cb.socket().addressProvider().restoreLocalAddress(alt_address);
return Network::FilterStatus::Continue;
}));
EXPECT_CALL(manager_, findFilterChain(_)).Times(0);
EXPECT_CALL(*ipv4_overridden_filter_chain_manager, findFilterChain(_)).Times(0);
EXPECT_CALL(*ipv6_overridden_filter_chain_manager, findFilterChain(_))
.WillOnce(Return(filter_chain_.get()));
auto* connection = new NiceMock<Network::MockServerConnection>();
EXPECT_CALL(dispatcher_, createServerConnection_()).WillOnce(Return(connection));
EXPECT_CALL(factory_, createNetworkFilterChain(_, _)).WillOnce(Return(true));
listener_callbacks1->onAccept(Network::ConnectionSocketPtr{accepted_socket});
EXPECT_EQ(1UL, handler_->numConnections());

EXPECT_CALL(*listener3, onDestroy());
EXPECT_CALL(*listener2, onDestroy());
EXPECT_CALL(*listener1, onDestroy());
EXPECT_CALL(*access_log_, log(_, _, _, _));
}

TEST_F(ConnectionHandlerTest, WildcardListenerWithOriginalDstInbound) {
Network::TcpListenerCallbacks* listener_callbacks1;
auto listener1 = new NiceMock<Network::MockListener>();
Expand Down