Limit the number of port migrations a QUIC client will attempt upon path degrading signals. This is to prevent the client from migrating port too frequently, which may cause port exhaustion. Also add CODE_COUNT for case where path degrading happens after the upper limit has been reached. PiperOrigin-RevId: 640248232
diff --git a/quiche/common/quiche_protocol_flags_list.h b/quiche/common/quiche_protocol_flags_list.h index bcdec6b..7e67582 100644 --- a/quiche/common/quiche_protocol_flags_list.h +++ b/quiche/common/quiche_protocol_flags_list.h
@@ -263,4 +263,8 @@ QUICHE_PROTOCOL_FLAG(int64_t, quic_default_delayed_ack_time_ms, 25, "Default maximum delayed ack time, in ms.") +QUICHE_PROTOCOL_FLAG( + uint32_t, quic_max_num_path_degrading_to_mitigate, 5, + "The maximum number of path degrading to mitigate with port migration. Any " + "further path degrading will not kick off port migration.") #endif
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc index f3388eb..ed788bb 100644 --- a/quiche/quic/core/http/end_to_end_test.cc +++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -5470,6 +5470,59 @@ EXPECT_GT(pto_count + 4, GetClientConnection()->GetStats().pto_count); } +TEST_P(EndToEndTest, ClientLimitPortMigrationOnPathDegrading) { + connect_to_server_on_initialize_ = false; + Initialize(); + if (!version_.HasIetfQuicFrames()) { + CreateClientWithWriter(); + return; + } + const uint32_t max_num_path_degrading_to_mitigate = + GetQuicFlag(quic_max_num_path_degrading_to_mitigate); + + delete client_writer_; + client_.reset(EndToEndTest::CreateQuicClient(nullptr)); + client_->client()->EnablePortMigrationUponPathDegrading(std::nullopt); + ASSERT_TRUE(client_->client()->WaitForHandshakeConfirmed()); + QuicConnection* client_connection = GetClientConnection(); + Http2HeaderBlock headers; + headers[":method"] = "POST"; + headers[":path"] = "/bar"; + headers[":scheme"] = "https"; + headers[":authority"] = server_hostname_; + // Manually trigger path degrading 5 times and expect they should all trigger + // port migration. + for (uint32_t i = 0; i < max_num_path_degrading_to_mitigate; ++i) { + client_->SendMessage(headers, "aaaa", false); + QuicSocketAddress original_self_addr = client_connection->self_address(); + WaitForNewConnectionIds(); + client_connection->OnPathDegradingDetected(); + client_->SendData("bbbb", true); + // By the time the response is received, path validation should have been + // finished. + client_->WaitForResponse(); + QuicSocketAddress new_self_addr = client_connection->self_address(); + EXPECT_NE(original_self_addr, new_self_addr); + } + + EXPECT_EQ(max_num_path_degrading_to_mitigate, + GetClientConnection()->GetStats().num_path_degrading); + EXPECT_EQ(max_num_path_degrading_to_mitigate, + GetClientConnection()->GetStats().num_path_response_received); + + // The next path degrading shouldn't trigger port migration. + WaitForNewConnectionIds(); + QuicSocketAddress original_self_addr = client_connection->self_address(); + client_connection->OnPathDegradingDetected(); + EXPECT_FALSE(client_->client()->HasPendingPathValidation()); + client_->SendSynchronousRequest("/eep"); + EXPECT_EQ(original_self_addr, client_connection->self_address()); + EXPECT_EQ(max_num_path_degrading_to_mitigate + 1, + GetClientConnection()->GetStats().num_path_degrading); + EXPECT_EQ(max_num_path_degrading_to_mitigate, + GetClientConnection()->GetStats().num_path_response_received); +} + TEST_P(EndToEndTest, ClientMultiPortMigrationOnPathDegrading) { client_config_.SetClientConnectionOptions(QuicTagVector{kMPQC, kMPQM}); ASSERT_TRUE(Initialize());
diff --git a/quiche/quic/tools/quic_client_base.cc b/quiche/quic/tools/quic_client_base.cc index 03001fd..2e834bc 100644 --- a/quiche/quic/tools/quic_client_base.cc +++ b/quiche/quic/tools/quic_client_base.cc
@@ -27,6 +27,7 @@ #include "quiche/quic/core/quic_utils.h" #include "quiche/quic/core/quic_versions.h" #include "quiche/quic/platform/api/quic_bug_tracker.h" +#include "quiche/quic/platform/api/quic_flag_utils.h" #include "quiche/quic/platform/api/quic_flags.h" #include "quiche/quic/platform/api/quic_ip_address.h" #include "quiche/quic/platform/api/quic_logging.h" @@ -237,6 +238,7 @@ session()->connection()->SetVersionNegotiated(); } set_connected_or_attempting_connect(true); + num_path_degrading_handled_ = 0; } void QuicClientBase::InitializeSession() { session()->Initialize(); } @@ -573,6 +575,11 @@ config_.DisableConnectionMigration()) { return; } + if (num_path_degrading_handled_ >= + GetQuicFlag(quic_max_num_path_degrading_to_mitigate)) { + QUIC_CODE_COUNT(reached_port_migration_upper_limit); + return; + } const auto self_address = session_->self_address(); if (network_helper_ == nullptr || !network_helper_->CreateUDPSocketAndBind(session_->peer_address(), @@ -583,6 +590,7 @@ if (writer == nullptr) { return; } + ++num_path_degrading_handled_; session()->ValidatePath( std::make_unique<PathMigrationContext>( std::unique_ptr<QuicPacketWriter>(writer),
diff --git a/quiche/quic/tools/quic_client_base.h b/quiche/quic/tools/quic_client_base.h index f425bfa..c707176 100644 --- a/quiche/quic/tools/quic_client_base.h +++ b/quiche/quic/tools/quic_client_base.h
@@ -8,6 +8,7 @@ #ifndef QUICHE_QUIC_TOOLS_QUIC_CLIENT_BASE_H_ #define QUICHE_QUIC_TOOLS_QUIC_CLIENT_BASE_H_ +#include <cstdint> #include <memory> #include <string> @@ -492,6 +493,7 @@ kQuicDefaultConnectionIdLength}; bool allow_port_migration_{false}; + uint32_t num_path_degrading_handled_{0}; }; } // namespace quic