From 065030f9f9f8a8393bbb2cc386ae4ef3e30b8af9 Mon Sep 17 00:00:00 2001 From: aakugan Date: Mon, 4 May 2026 13:55:42 +0000 Subject: [PATCH] reverse_tunnel: add skip_rebalancing config Signed-off-by: aakugan --- .../reverse_tunnel/v3/reverse_tunnel.proto | 7 ++++++- .../reverse_tunnel/reverse_tunnel_filter.cc | 8 ++++---- .../reverse_tunnel/reverse_tunnel_filter.h | 4 ++++ .../network/reverse_tunnel/config_test.cc | 19 +++++++++++++++++++ .../reverse_tunnel/filter_unit_test.cc | 10 ++++++++++ 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/api/envoy/extensions/filters/network/reverse_tunnel/v3/reverse_tunnel.proto b/api/envoy/extensions/filters/network/reverse_tunnel/v3/reverse_tunnel.proto index ac9bf47174507..d8079c108acdc 100644 --- a/api/envoy/extensions/filters/network/reverse_tunnel/v3/reverse_tunnel.proto +++ b/api/envoy/extensions/filters/network/reverse_tunnel/v3/reverse_tunnel.proto @@ -96,7 +96,7 @@ message Validation { // Configuration for the reverse tunnel network filter. // This filter handles reverse tunnel connection acceptance and rejection by processing // HTTP requests where required identification values are provided via HTTP headers. -// [#next-free-field: 7] +// [#next-free-field: 8] message ReverseTunnel { // Ping interval for health checks on established reverse tunnel connections. // If not specified, defaults to ``2 seconds``. @@ -133,4 +133,9 @@ message ReverseTunnel { // via ``x-envoy-reverse-tunnel-upstream-cluster-name`` header. Connections with mismatched or missing // cluster names are rejected with HTTP ``400 Bad Request``. When empty, no cluster name validation is performed. string required_cluster_name = 6 [(validate.rules).string = {max_len: 255 ignore_empty: true}]; + + // When true, skip worker-thread rebalancing for accepted reverse tunnel connections. + // This avoids the cross-worker lock in pickLeastLoadedSocketManager. + // Default: false (rebalancing enabled). + bool skip_rebalancing = 7; } diff --git a/source/extensions/filters/network/reverse_tunnel/reverse_tunnel_filter.cc b/source/extensions/filters/network/reverse_tunnel/reverse_tunnel_filter.cc index ea24e8f9cb3ca..588c98d5130ab 100644 --- a/source/extensions/filters/network/reverse_tunnel/reverse_tunnel_filter.cc +++ b/source/extensions/filters/network/reverse_tunnel/reverse_tunnel_filter.cc @@ -132,8 +132,7 @@ ReverseTunnelFilterConfig::ReverseTunnelFilterConfig( ? std::chrono::milliseconds( DurationUtil::durationToMilliseconds(proto_config.ping_interval())) : std::chrono::milliseconds(2000)), - auto_close_connections_( - proto_config.auto_close_connections() ? proto_config.auto_close_connections() : false), + auto_close_connections_(proto_config.auto_close_connections()), request_path_( proto_config.request_path().empty() ? std::string(::Envoy::Extensions::Bootstrap::ReverseConnection:: @@ -156,7 +155,8 @@ ReverseTunnelFilterConfig::ReverseTunnelFilterConfig( !proto_config.validation().dynamic_metadata_namespace().empty() ? proto_config.validation().dynamic_metadata_namespace() : "envoy.filters.network.reverse_tunnel"), - required_cluster_name_(proto_config.required_cluster_name()) {} + required_cluster_name_(proto_config.required_cluster_name()), + skip_rebalancing_(proto_config.skip_rebalancing()) {} bool ReverseTunnelFilterConfig::validateIdentifiers( absl::string_view node_id, absl::string_view cluster_id, absl::string_view tenant_id, @@ -532,7 +532,7 @@ void ReverseTunnelFilter::processAcceptedConnection(absl::string_view node_id, ENVOY_CONN_LOG(trace, "reverse_tunnel: registering wrapped socket for reuse", connection); socket_manager->addConnectionSocket(socket_node_id, socket_cluster_id, std::move(wrapped_socket), - ping_seconds, false /* rebalanced */); + ping_seconds, config_->skipRebalancing() /* rebalanced */); ENVOY_CONN_LOG(debug, "reverse_tunnel: successfully registered wrapped socket for reuse", connection); diff --git a/source/extensions/filters/network/reverse_tunnel/reverse_tunnel_filter.h b/source/extensions/filters/network/reverse_tunnel/reverse_tunnel_filter.h index 08411407f8781..b75a203ffd4eb 100644 --- a/source/extensions/filters/network/reverse_tunnel/reverse_tunnel_filter.h +++ b/source/extensions/filters/network/reverse_tunnel/reverse_tunnel_filter.h @@ -62,6 +62,8 @@ class ReverseTunnelFilterConfig : public Logger::Loggable { // Returns the required cluster name for validation. const std::string& requiredClusterName() const { return required_cluster_name_; } + bool skipRebalancing() const { return skip_rebalancing_; } + private: ReverseTunnelFilterConfig( const envoy::extensions::filters::network::reverse_tunnel::v3::ReverseTunnel& proto_config, @@ -83,6 +85,8 @@ class ReverseTunnelFilterConfig : public Logger::Loggable { // Required cluster name for validation (empty means no validation). const std::string required_cluster_name_; + + const bool skip_rebalancing_; }; using ReverseTunnelFilterConfigSharedPtr = std::shared_ptr; diff --git a/test/extensions/filters/network/reverse_tunnel/config_test.cc b/test/extensions/filters/network/reverse_tunnel/config_test.cc index ac2eae1e54cc3..70c7bfd648ae3 100644 --- a/test/extensions/filters/network/reverse_tunnel/config_test.cc +++ b/test/extensions/filters/network/reverse_tunnel/config_test.cc @@ -358,6 +358,25 @@ request_method: GET EXPECT_THAT(result.status().message(), testing::HasSubstr("Failed to parse tenant_id_format")); } +// Tests that the ReverseTunnelFilterConfig is formed properly and the filter construction works. +TEST(ReverseTunnelFilterConfigFactoryTest, ConfigurationSkipRebalancingEnabled) { + envoy::extensions::filters::network::reverse_tunnel::v3::ReverseTunnel proto_config; + proto_config.set_skip_rebalancing(true); + proto_config.set_request_path("/request"); + proto_config.set_request_method(envoy::config::core::v3::POST); + + ReverseTunnelFilterConfigFactory factory; + NiceMock context; + auto result = factory.createFilterFactoryFromProto(proto_config, context); + ASSERT_TRUE(result.ok()); + Network::FilterFactoryCb cb = result.value(); + EXPECT_TRUE(cb != nullptr); + + Network::MockFilterManager filter_manager; + EXPECT_CALL(filter_manager, addReadFilter(_)); + cb(filter_manager); +} + } // namespace } // namespace ReverseTunnel } // namespace NetworkFilters diff --git a/test/extensions/filters/network/reverse_tunnel/filter_unit_test.cc b/test/extensions/filters/network/reverse_tunnel/filter_unit_test.cc index 492bb0a19d312..e7a2977a66377 100644 --- a/test/extensions/filters/network/reverse_tunnel/filter_unit_test.cc +++ b/test/extensions/filters/network/reverse_tunnel/filter_unit_test.cc @@ -479,6 +479,7 @@ TEST_F(ReverseTunnelFilterUnitTest, ConfigurationDefaults) { EXPECT_FALSE(config->autoCloseConnections()); EXPECT_EQ("/reverse_connections/request", config->requestPath()); EXPECT_EQ("GET", config->requestMethod()); + EXPECT_FALSE(config->skipRebalancing()); } // Test RequestDecoder methods not fully covered. @@ -2094,6 +2095,15 @@ TEST_F(ReverseTunnelFilterWithTenantIsolationTest, FilterReadsTenantIsolationFro EXPECT_EQ(1, parse_error->value()); } +TEST_F(ReverseTunnelFilterUnitTest, FilterConfigLoadsSkipRebalancing) { + envoy::extensions::filters::network::reverse_tunnel::v3::ReverseTunnel cfg; + cfg.set_skip_rebalancing(true); + + auto config_or_error = ReverseTunnelFilterConfig::create(cfg, factory_context_); + ASSERT_TRUE(config_or_error.ok()); + EXPECT_TRUE(config_or_error.value()->skipRebalancing()); +} + } // namespace } // namespace ReverseTunnel } // namespace NetworkFilters