Use new connection IDs in IETF quic connection migration.
Feature is also guarded by "RVCM" connection option.
Also mark NEW_CONNECTION_ID_FRAME as retransmittable as it is a control frame type.
Protected by FLAGS_quic_reloadable_flag_quic_connection_migration_use_new_cid.
PiperOrigin-RevId: 372122692
Change-Id: I76005ba39f50fad976b8146bd34fb4687c4d2ba1
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index d5bff8c..4b30bae 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -17,6 +17,7 @@
#include "quic/core/http/http_constants.h"
#include "quic/core/http/quic_spdy_client_stream.h"
#include "quic/core/http/web_transport_http3.h"
+#include "quic/core/quic_connection.h"
#include "quic/core/quic_data_writer.h"
#include "quic/core/quic_epoll_connection_helper.h"
#include "quic/core/quic_error_codes.h"
@@ -750,6 +751,18 @@
}
}
+ void WaitForNewConnectionIds() {
+ // Wait until a new server CID is available for another migration.
+ const auto* client_connection = GetClientConnection();
+ while (!QuicConnectionPeer::HasUnusedPeerIssuedConnectionId(
+ client_connection) ||
+ (!client_connection->client_connection_id().IsEmpty() &&
+ !QuicConnectionPeer::HasSelfIssuedConnectionIdToConsume(
+ client_connection))) {
+ client_->client()->WaitForEvents();
+ }
+ }
+
ScopedEnvironmentForThreads environment_;
bool initialized_;
// If true, the Initialize() function will create |client_| and starts to
@@ -2620,52 +2633,250 @@
server_thread_->Resume();
}
-TEST_P(EndToEndTest, ConnectionMigrationClientIPChangedWithNonEmptyClientCID) {
+TEST_P(EndToEndTest, IetfConnectionMigrationClientIPChangedMultipleTimes) {
+ ASSERT_TRUE(Initialize());
+ if (!GetClientConnection()->connection_migration_use_new_cid()) {
+ return;
+ }
+ SendSynchronousFooRequestAndCheckResponse();
+
+ // Store the client IP address which was used to send the first request.
+ QuicIpAddress host0 =
+ client_->client()->network_helper()->GetLatestClientAddress().host();
+ QuicConnection* client_connection = GetClientConnection();
+ ASSERT_TRUE(client_connection != nullptr);
+
+ // Migrate socket to a new IP address.
+ QuicIpAddress host1 = TestLoopback(2);
+ EXPECT_NE(host0, host1);
+ ASSERT_TRUE(
+ QuicConnectionPeer::HasUnusedPeerIssuedConnectionId(client_connection));
+ QuicConnectionId server_cid0 = client_connection->connection_id();
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+ EXPECT_TRUE(client_->client()->MigrateSocket(host1));
+ QuicConnectionId server_cid1 = client_connection->connection_id();
+ EXPECT_FALSE(server_cid1.IsEmpty());
+ EXPECT_NE(server_cid0, server_cid1);
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+
+ // Send a request using the new socket.
+ SendSynchronousBarRequestAndCheckResponse();
+ EXPECT_EQ(1u,
+ client_connection->GetStats().num_connectivity_probing_received);
+
+ // Send another request and wait for response making sure path response is
+ // received at server.
+ SendSynchronousBarRequestAndCheckResponse();
+
+ // Migrate socket to a new IP address.
+ WaitForNewConnectionIds();
+ EXPECT_EQ(1u, client_connection->GetStats().num_retire_connection_id_sent);
+ QuicIpAddress host2 = TestLoopback(3);
+ EXPECT_NE(host0, host2);
+ EXPECT_NE(host1, host2);
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+ EXPECT_TRUE(client_->client()->MigrateSocket(host2));
+ QuicConnectionId server_cid2 = client_connection->connection_id();
+ EXPECT_FALSE(server_cid2.IsEmpty());
+ EXPECT_NE(server_cid0, server_cid2);
+ EXPECT_NE(server_cid1, server_cid2);
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+
+ // Send another request using the new socket and wait for response making sure
+ // path response is received at server.
+ SendSynchronousBarRequestAndCheckResponse();
+ EXPECT_EQ(2u,
+ client_connection->GetStats().num_connectivity_probing_received);
+
+ // Migrate socket back to an old IP address.
+ WaitForNewConnectionIds();
+ EXPECT_EQ(2u, client_connection->GetStats().num_retire_connection_id_sent);
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+ EXPECT_TRUE(client_->client()->MigrateSocket(host1));
+ QuicConnectionId server_cid3 = client_connection->connection_id();
+ EXPECT_FALSE(server_cid3.IsEmpty());
+ EXPECT_NE(server_cid0, server_cid3);
+ EXPECT_NE(server_cid1, server_cid3);
+ EXPECT_NE(server_cid2, server_cid3);
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+ const auto* client_packet_creator =
+ QuicConnectionPeer::GetPacketCreator(client_connection);
+ EXPECT_TRUE(client_packet_creator->GetClientConnectionId().IsEmpty());
+ EXPECT_EQ(server_cid3, client_packet_creator->GetServerConnectionId());
+
+ // Send another request using the new socket and wait for response making sure
+ // path response is received at server.
+ SendSynchronousBarRequestAndCheckResponse();
+ // Even this is an old path, server has forgotten about it and thus needs to
+ // validate the path again.
+ EXPECT_EQ(3u,
+ client_connection->GetStats().num_connectivity_probing_received);
+
+ WaitForNewConnectionIds();
+ EXPECT_EQ(3u, client_connection->GetStats().num_retire_connection_id_sent);
+
+ server_thread_->Pause();
+ QuicConnection* server_connection = GetServerConnection();
+ // By the time the 2nd request is completed, the PATH_RESPONSE must have been
+ // received by the server.
+ EXPECT_FALSE(server_connection->HasPendingPathValidation());
+ EXPECT_EQ(3u, server_connection->GetStats().num_validated_peer_migration);
+ EXPECT_EQ(server_cid3, server_connection->connection_id());
+ const auto* server_packet_creator =
+ QuicConnectionPeer::GetPacketCreator(server_connection);
+ EXPECT_EQ(server_cid3, server_packet_creator->GetServerConnectionId());
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ server_connection)
+ .IsEmpty());
+ EXPECT_EQ(4u, server_connection->GetStats().num_new_connection_id_sent);
+ server_thread_->Resume();
+}
+
+TEST_P(EndToEndTest,
+ ConnectionMigrationWithNonZeroConnectionIDClientIPChangedMultipleTimes) {
if (!version_.SupportsClientConnectionIds()) {
ASSERT_TRUE(Initialize());
return;
}
override_client_connection_id_length_ = kQuicDefaultConnectionIdLength;
ASSERT_TRUE(Initialize());
- if (!version_.HasIetfQuicFrames() ||
- !client_->client()->session()->connection()->validate_client_address()) {
+ if (!GetClientConnection()->connection_migration_use_new_cid()) {
return;
}
SendSynchronousFooRequestAndCheckResponse();
// Store the client IP address which was used to send the first request.
- QuicIpAddress old_host =
+ QuicIpAddress host0 =
client_->client()->network_helper()->GetLatestClientAddress().host();
- auto* client_connection = GetClientConnection();
- QuicConnectionId client_cid0 = client_connection->client_connection_id();
+ QuicConnection* client_connection = GetClientConnection();
+ ASSERT_TRUE(client_connection != nullptr);
+
+ // Migrate socket to a new IP address.
+ QuicIpAddress host1 = TestLoopback(2);
+ EXPECT_NE(host0, host1);
+ ASSERT_TRUE(
+ QuicConnectionPeer::HasUnusedPeerIssuedConnectionId(client_connection));
QuicConnectionId server_cid0 = client_connection->connection_id();
+ QuicConnectionId client_cid0 = client_connection->client_connection_id();
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+ EXPECT_TRUE(QuicConnectionPeer::GetClientConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+ EXPECT_TRUE(client_->client()->MigrateSocket(host1));
+ QuicConnectionId server_cid1 = client_connection->connection_id();
+ QuicConnectionId client_cid1 = client_connection->client_connection_id();
+ EXPECT_FALSE(server_cid1.IsEmpty());
+ EXPECT_FALSE(client_cid1.IsEmpty());
+ EXPECT_NE(server_cid0, server_cid1);
+ EXPECT_NE(client_cid0, client_cid1);
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+ EXPECT_TRUE(QuicConnectionPeer::GetClientConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
- // Migrate socket to the new IP address.
- QuicIpAddress new_host = TestLoopback(2);
- EXPECT_NE(old_host, new_host);
- ASSERT_TRUE(client_->client()->MigrateSocket(new_host));
-
- // Send a request using the new socket.
+ // Send another request to ensure that the server will have time to finish the
+ // reverse path validation and send address token.
SendSynchronousBarRequestAndCheckResponse();
-
EXPECT_EQ(1u,
client_connection->GetStats().num_connectivity_probing_received);
- // Send another request.
+ // Migrate socket to a new IP address.
+ WaitForNewConnectionIds();
+ EXPECT_EQ(1u, client_connection->GetStats().num_retire_connection_id_sent);
+ EXPECT_EQ(2u, client_connection->GetStats().num_new_connection_id_sent);
+ QuicIpAddress host2 = TestLoopback(3);
+ EXPECT_NE(host0, host2);
+ EXPECT_NE(host1, host2);
+ EXPECT_TRUE(client_->client()->MigrateSocket(host2));
+ QuicConnectionId server_cid2 = client_connection->connection_id();
+ QuicConnectionId client_cid2 = client_connection->client_connection_id();
+ EXPECT_FALSE(server_cid2.IsEmpty());
+ EXPECT_NE(server_cid0, server_cid2);
+ EXPECT_NE(server_cid1, server_cid2);
+ EXPECT_FALSE(client_cid2.IsEmpty());
+ EXPECT_NE(client_cid0, client_cid2);
+ EXPECT_NE(client_cid1, client_cid2);
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+ EXPECT_TRUE(QuicConnectionPeer::GetClientConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+
+ // Send another request to ensure that the server will have time to finish the
+ // reverse path validation and send address token.
SendSynchronousBarRequestAndCheckResponse();
+ EXPECT_EQ(2u,
+ client_connection->GetStats().num_connectivity_probing_received);
- EXPECT_EQ(client_cid0, client_connection->client_connection_id());
- EXPECT_EQ(server_cid0, client_connection->connection_id());
+ // Migrate socket back to an old IP address.
+ WaitForNewConnectionIds();
+ EXPECT_EQ(2u, client_connection->GetStats().num_retire_connection_id_sent);
+ EXPECT_EQ(3u, client_connection->GetStats().num_new_connection_id_sent);
+ EXPECT_TRUE(client_->client()->MigrateSocket(host1));
+ QuicConnectionId server_cid3 = client_connection->connection_id();
+ QuicConnectionId client_cid3 = client_connection->client_connection_id();
+ EXPECT_FALSE(server_cid3.IsEmpty());
+ EXPECT_NE(server_cid0, server_cid3);
+ EXPECT_NE(server_cid1, server_cid3);
+ EXPECT_NE(server_cid2, server_cid3);
+ EXPECT_FALSE(client_cid3.IsEmpty());
+ EXPECT_NE(client_cid0, client_cid3);
+ EXPECT_NE(client_cid1, client_cid3);
+ EXPECT_NE(client_cid2, client_cid3);
+ const auto* client_packet_creator =
+ QuicConnectionPeer::GetPacketCreator(client_connection);
+ EXPECT_EQ(client_cid3, client_packet_creator->GetClientConnectionId());
+ EXPECT_EQ(server_cid3, client_packet_creator->GetServerConnectionId());
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+ // Send another request to ensure that the server will have time to finish the
+ // reverse path validation and send address token.
+ SendSynchronousBarRequestAndCheckResponse();
+ // Even this is an old path, server has forgotten about it and thus needs to
+ // validate the path again.
+ EXPECT_EQ(3u,
+ client_connection->GetStats().num_connectivity_probing_received);
+
+ WaitForNewConnectionIds();
+ EXPECT_EQ(3u, client_connection->GetStats().num_retire_connection_id_sent);
+ EXPECT_EQ(4u, client_connection->GetStats().num_new_connection_id_sent);
+
+ server_thread_->Pause();
// By the time the 2nd request is completed, the PATH_RESPONSE must have been
// received by the server.
- server_thread_->Pause();
QuicConnection* server_connection = GetServerConnection();
- ASSERT_THAT(server_connection, NotNull());
EXPECT_FALSE(server_connection->HasPendingPathValidation());
- EXPECT_EQ(1u, server_connection->GetStats().num_validated_peer_migration);
- EXPECT_EQ(client_cid0, server_connection->client_connection_id());
- EXPECT_EQ(server_cid0, server_connection->connection_id());
+ EXPECT_EQ(3u, server_connection->GetStats().num_validated_peer_migration);
+ EXPECT_EQ(server_cid3, server_connection->connection_id());
+ EXPECT_EQ(client_cid3, server_connection->client_connection_id());
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ server_connection)
+ .IsEmpty());
+ const auto* server_packet_creator =
+ QuicConnectionPeer::GetPacketCreator(server_connection);
+ EXPECT_EQ(client_cid3, server_packet_creator->GetClientConnectionId());
+ EXPECT_EQ(server_cid3, server_packet_creator->GetServerConnectionId());
+ EXPECT_EQ(3u, server_connection->GetStats().num_retire_connection_id_sent);
+ EXPECT_EQ(4u, server_connection->GetStats().num_new_connection_id_sent);
server_thread_->Resume();
}
@@ -2693,7 +2904,7 @@
EXPECT_EQ(1u,
client_connection->GetStats().num_connectivity_probing_received);
- // Send another request to ensure that the server will time to finish the
+ // Send another request to ensure that the server will have time to finish the
// reverse path validation and send address token.
SendSynchronousBarRequestAndCheckResponse();
@@ -2758,14 +2969,19 @@
TEST_P(EndToEndTest, ClientAddressSpoofedForSomePeriod) {
ASSERT_TRUE(Initialize());
- if (!version_.HasIetfQuicFrames() ||
- !client_->client()->session()->connection()->validate_client_address()) {
+ if (!GetClientConnection()->connection_migration_use_new_cid()) {
return;
}
auto writer = new DuplicatePacketWithSpoofedSelfAddressWriter();
client_.reset(CreateQuicClient(writer));
+
+ // Make sure client has unused peer connection ID before migration.
+ SendSynchronousFooRequestAndCheckResponse();
+ ASSERT_TRUE(QuicConnectionPeer::HasUnusedPeerIssuedConnectionId(
+ GetClientConnection()));
+
QuicIpAddress real_host = TestLoopback(1);
- client_->MigrateSocket(real_host);
+ ASSERT_TRUE(client_->MigrateSocket(real_host));
SendSynchronousFooRequestAndCheckResponse();
EXPECT_EQ(
0u, GetClientConnection()->GetStats().num_connectivity_probing_received);
@@ -2804,10 +3020,10 @@
EXPECT_EQ(large_body, client_->response_body());
}
-TEST_P(EndToEndTest, AsynchronousConnectionMigrationClientIPChanged) {
+TEST_P(EndToEndTest,
+ AsynchronousConnectionMigrationClientIPChangedMultipleTimes) {
ASSERT_TRUE(Initialize());
- if (!version_.HasIetfQuicFrames() ||
- !client_->client()->session()->connection()->use_path_validator()) {
+ if (!GetClientConnection()->connection_migration_use_new_cid()) {
return;
}
client_.reset(CreateQuicClient(nullptr));
@@ -2815,24 +3031,86 @@
SendSynchronousFooRequestAndCheckResponse();
// Store the client IP address which was used to send the first request.
- QuicIpAddress old_host =
+ QuicIpAddress host0 =
client_->client()->network_helper()->GetLatestClientAddress().host();
+ QuicConnection* client_connection = GetClientConnection();
+ const QuicConnection* server_connection = GetServerConnection();
+ QuicConnectionId server_cid0 = client_connection->connection_id();
+ EXPECT_EQ(server_connection->connection_id(), server_cid0);
+ // Server should have one new connection ID upon handshake completion.
+ ASSERT_TRUE(
+ QuicConnectionPeer::HasUnusedPeerIssuedConnectionId(client_connection));
- // Migrate socket to the new IP address.
- QuicIpAddress new_host = TestLoopback(2);
- EXPECT_NE(old_host, new_host);
- ASSERT_TRUE(client_->client()->ValidateAndMigrateSocket(new_host));
+ // Migrate socket to new IP address #1.
+ QuicIpAddress host1 = TestLoopback(2);
+ EXPECT_NE(host0, host1);
+ ASSERT_TRUE(client_->client()->ValidateAndMigrateSocket(host1));
+ while (client_->client()->HasPendingPathValidation()) {
+ client_->client()->WaitForEvents();
+ }
+ EXPECT_EQ(host1, client_->client()->session()->self_address().host());
+ EXPECT_EQ(1u,
+ client_connection->GetStats().num_connectivity_probing_received);
+ QuicConnectionId server_cid1 = client_connection->connection_id();
+ EXPECT_NE(server_cid0, server_cid1);
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+
+ // Send a request using the new socket.
+ SendSynchronousBarRequestAndCheckResponse();
+ EXPECT_EQ(server_connection->connection_id(), server_cid1);
+
+ // Migrate socket to new IP address #2.
+ WaitForNewConnectionIds();
+ QuicIpAddress host2 = TestLoopback(3);
+ EXPECT_NE(host0, host1);
+ ASSERT_TRUE(client_->client()->ValidateAndMigrateSocket(host2));
while (client_->client()->HasPendingPathValidation()) {
client_->client()->WaitForEvents();
}
- EXPECT_EQ(new_host, client_->client()->session()->self_address().host());
- QuicConnection* client_connection = GetClientConnection();
- ASSERT_TRUE(client_connection);
- EXPECT_EQ(client_connection->validate_client_address() ? 1u : 0,
+ EXPECT_EQ(host2, client_->client()->session()->self_address().host());
+ EXPECT_EQ(2u,
client_connection->GetStats().num_connectivity_probing_received);
+ QuicConnectionId server_cid2 = client_connection->connection_id();
+ EXPECT_NE(server_cid0, server_cid2);
+ EXPECT_NE(server_cid1, server_cid2);
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+
// Send a request using the new socket.
SendSynchronousBarRequestAndCheckResponse();
+ EXPECT_EQ(server_connection->connection_id(), server_cid2);
+
+ // Migrate socket back to IP address #1.
+ WaitForNewConnectionIds();
+ ASSERT_TRUE(client_->client()->ValidateAndMigrateSocket(host1));
+
+ while (client_->client()->HasPendingPathValidation()) {
+ client_->client()->WaitForEvents();
+ }
+ EXPECT_EQ(host1, client_->client()->session()->self_address().host());
+ EXPECT_EQ(3u,
+ client_connection->GetStats().num_connectivity_probing_received);
+ QuicConnectionId server_cid3 = client_connection->connection_id();
+ EXPECT_NE(server_cid0, server_cid3);
+ EXPECT_NE(server_cid1, server_cid3);
+ EXPECT_NE(server_cid2, server_cid3);
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+
+ // Send a request using the new socket.
+ SendSynchronousBarRequestAndCheckResponse();
+ EXPECT_EQ(server_connection->connection_id(), server_cid3);
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ server_connection)
+ .IsEmpty());
+
+ // There should be 1 new connection ID issued by the server.
+ WaitForNewConnectionIds();
}
TEST_P(EndToEndTest,
@@ -2843,8 +3121,7 @@
}
override_client_connection_id_length_ = kQuicDefaultConnectionIdLength;
ASSERT_TRUE(Initialize());
- if (!version_.HasIetfQuicFrames() ||
- !client_->client()->session()->connection()->validate_client_address()) {
+ if (!GetClientConnection()->connection_migration_use_new_cid()) {
return;
}
client_.reset(CreateQuicClient(nullptr));
@@ -2869,13 +3146,23 @@
EXPECT_EQ(new_host, client_->client()->session()->self_address().host());
EXPECT_EQ(1u,
client_connection->GetStats().num_connectivity_probing_received);
+ QuicConnectionId client_cid1 = client_connection->client_connection_id();
+ QuicConnectionId server_cid1 = client_connection->connection_id();
+ const auto* client_packet_creator =
+ QuicConnectionPeer::GetPacketCreator(client_connection);
+ EXPECT_EQ(client_cid1, client_packet_creator->GetClientConnectionId());
+ EXPECT_EQ(server_cid1, client_packet_creator->GetServerConnectionId());
// Send a request using the new socket.
SendSynchronousBarRequestAndCheckResponse();
server_thread_->Pause();
QuicConnection* server_connection = GetServerConnection();
- EXPECT_EQ(client_cid0, server_connection->client_connection_id());
- EXPECT_EQ(server_cid0, server_connection->connection_id());
+ EXPECT_EQ(client_cid1, server_connection->client_connection_id());
+ EXPECT_EQ(server_cid1, server_connection->connection_id());
+ const auto* server_packet_creator =
+ QuicConnectionPeer::GetPacketCreator(server_connection);
+ EXPECT_EQ(client_cid1, server_packet_creator->GetClientConnectionId());
+ EXPECT_EQ(server_cid1, server_packet_creator->GetServerConnectionId());
server_thread_->Resume();
}
@@ -4794,6 +5081,219 @@
server_thread_->Resume();
}
+TEST_P(EndToEndPacketReorderingTest, MigrateAgainAfterPathValidationFailure) {
+ ASSERT_TRUE(Initialize());
+ if (!GetClientConnection()->connection_migration_use_new_cid()) {
+ return;
+ }
+
+ client_.reset(CreateQuicClient(nullptr));
+ // Finish one request to make sure handshake established.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+
+ // Wait for the connection to become idle, to make sure the packet gets
+ // delayed is the connectivity probing packet.
+ client_->WaitForDelayedAcks();
+
+ QuicSocketAddress addr1 = client_->client()->session()->self_address();
+ QuicConnection* client_connection = GetClientConnection();
+ QuicConnectionId server_cid1 = client_connection->connection_id();
+
+ // Migrate socket to the new IP address.
+ QuicIpAddress host2 = TestLoopback(2);
+ EXPECT_NE(addr1.host(), host2);
+
+ // Drop PATH_RESPONSE packets to timeout the path validation.
+ server_writer_->set_fake_packet_loss_percentage(100);
+ ASSERT_TRUE(
+ QuicConnectionPeer::HasUnusedPeerIssuedConnectionId(client_connection));
+
+ ASSERT_TRUE(client_->client()->ValidateAndMigrateSocket(host2));
+
+ QuicConnectionId server_cid2 =
+ QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection);
+ EXPECT_FALSE(server_cid2.IsEmpty());
+ EXPECT_NE(server_cid2, server_cid1);
+ // Wait until path validation fails at the client.
+ while (client_->client()->HasPendingPathValidation()) {
+ EXPECT_EQ(server_cid2,
+ QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection));
+ client_->client()->WaitForEvents();
+ }
+ EXPECT_EQ(addr1, client_->client()->session()->self_address());
+ EXPECT_EQ(server_cid1, GetClientConnection()->connection_id());
+
+ server_writer_->set_fake_packet_loss_percentage(0);
+ EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
+
+ WaitForNewConnectionIds();
+ EXPECT_EQ(1u, client_connection->GetStats().num_retire_connection_id_sent);
+ EXPECT_EQ(0u, client_connection->GetStats().num_new_connection_id_sent);
+
+ server_thread_->Pause();
+ QuicConnection* server_connection = GetServerConnection();
+ // Server has received 3 path challenges.
+ EXPECT_EQ(3u,
+ server_connection->GetStats().num_connectivity_probing_received);
+ EXPECT_EQ(server_cid1, server_connection->connection_id());
+ EXPECT_EQ(0u, server_connection->GetStats().num_retire_connection_id_sent);
+ EXPECT_EQ(2u, server_connection->GetStats().num_new_connection_id_sent);
+ server_thread_->Resume();
+
+ // Migrate socket to a new IP address again.
+ QuicIpAddress host3 = TestLoopback(3);
+ EXPECT_NE(addr1.host(), host3);
+ EXPECT_NE(host2, host3);
+
+ WaitForNewConnectionIds();
+ EXPECT_EQ(1u, client_connection->GetStats().num_retire_connection_id_sent);
+ EXPECT_EQ(0u, client_connection->GetStats().num_new_connection_id_sent);
+
+ ASSERT_TRUE(client_->client()->ValidateAndMigrateSocket(host3));
+ QuicConnectionId server_cid3 =
+ QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection);
+ EXPECT_FALSE(server_cid3.IsEmpty());
+ EXPECT_NE(server_cid1, server_cid3);
+ EXPECT_NE(server_cid2, server_cid3);
+ while (client_->client()->HasPendingPathValidation()) {
+ client_->client()->WaitForEvents();
+ }
+ EXPECT_EQ(host3, client_->client()->session()->self_address().host());
+ EXPECT_EQ(server_cid3, GetClientConnection()->connection_id());
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+ EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
+ EXPECT_EQ(server_cid3, server_connection->connection_id());
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ server_connection)
+ .IsEmpty());
+
+ // Server should send a new connection ID to client.
+ WaitForNewConnectionIds();
+ EXPECT_EQ(2u, client_connection->GetStats().num_retire_connection_id_sent);
+ EXPECT_EQ(0u, client_connection->GetStats().num_new_connection_id_sent);
+}
+
+TEST_P(EndToEndPacketReorderingTest,
+ MigrateAgainAfterPathValidationFailureWithNonZeroClientConnectionId) {
+ if (!version_.SupportsClientConnectionIds()) {
+ ASSERT_TRUE(Initialize());
+ return;
+ }
+ override_client_connection_id_length_ = kQuicDefaultConnectionIdLength;
+ ASSERT_TRUE(Initialize());
+ if (!GetClientConnection()->connection_migration_use_new_cid()) {
+ return;
+ }
+
+ client_.reset(CreateQuicClient(nullptr));
+ // Finish one request to make sure handshake established.
+ EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo"));
+
+ // Wait for the connection to become idle, to make sure the packet gets
+ // delayed is the connectivity probing packet.
+ client_->WaitForDelayedAcks();
+
+ QuicSocketAddress addr1 = client_->client()->session()->self_address();
+ QuicConnection* client_connection = GetClientConnection();
+ QuicConnectionId server_cid1 = client_connection->connection_id();
+ QuicConnectionId client_cid1 = client_connection->client_connection_id();
+
+ // Migrate socket to the new IP address.
+ QuicIpAddress host2 = TestLoopback(2);
+ EXPECT_NE(addr1.host(), host2);
+
+ // Drop PATH_RESPONSE packets to timeout the path validation.
+ server_writer_->set_fake_packet_loss_percentage(100);
+ ASSERT_TRUE(
+ QuicConnectionPeer::HasUnusedPeerIssuedConnectionId(client_connection));
+ ASSERT_TRUE(client_->client()->ValidateAndMigrateSocket(host2));
+ QuicConnectionId server_cid2 =
+ QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection);
+ EXPECT_FALSE(server_cid2.IsEmpty());
+ EXPECT_NE(server_cid2, server_cid1);
+ QuicConnectionId client_cid2 =
+ QuicConnectionPeer::GetClientConnectionIdOnAlternativePath(
+ client_connection);
+ EXPECT_FALSE(client_cid2.IsEmpty());
+ EXPECT_NE(client_cid2, client_cid1);
+ while (client_->client()->HasPendingPathValidation()) {
+ EXPECT_EQ(server_cid2,
+ QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection));
+ client_->client()->WaitForEvents();
+ }
+ EXPECT_EQ(addr1, client_->client()->session()->self_address());
+ EXPECT_EQ(server_cid1, GetClientConnection()->connection_id());
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+ server_writer_->set_fake_packet_loss_percentage(0);
+ EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
+ WaitForNewConnectionIds();
+ EXPECT_EQ(1u, client_connection->GetStats().num_retire_connection_id_sent);
+ EXPECT_EQ(2u, client_connection->GetStats().num_new_connection_id_sent);
+
+ server_thread_->Pause();
+ QuicConnection* server_connection = GetServerConnection();
+ if (server_connection != nullptr) {
+ EXPECT_EQ(3u,
+ server_connection->GetStats().num_connectivity_probing_received);
+ EXPECT_EQ(server_cid1, server_connection->connection_id());
+ } else {
+ ADD_FAILURE() << "Missing server connection";
+ }
+ EXPECT_EQ(1u, server_connection->GetStats().num_retire_connection_id_sent);
+ EXPECT_EQ(2u, server_connection->GetStats().num_new_connection_id_sent);
+ server_thread_->Resume();
+
+ // Migrate socket to a new IP address again.
+ QuicIpAddress host3 = TestLoopback(3);
+ EXPECT_NE(addr1.host(), host3);
+ EXPECT_NE(host2, host3);
+ ASSERT_TRUE(client_->client()->ValidateAndMigrateSocket(host3));
+
+ QuicConnectionId server_cid3 =
+ QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection);
+ EXPECT_FALSE(server_cid3.IsEmpty());
+ EXPECT_NE(server_cid1, server_cid3);
+ EXPECT_NE(server_cid2, server_cid3);
+ QuicConnectionId client_cid3 =
+ QuicConnectionPeer::GetClientConnectionIdOnAlternativePath(
+ client_connection);
+ EXPECT_NE(client_cid1, client_cid3);
+ EXPECT_NE(client_cid2, client_cid3);
+ while (client_->client()->HasPendingPathValidation()) {
+ client_->client()->WaitForEvents();
+ }
+ EXPECT_EQ(host3, client_->client()->session()->self_address().host());
+ EXPECT_EQ(server_cid3, GetClientConnection()->connection_id());
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ client_connection)
+ .IsEmpty());
+ EXPECT_EQ(kBarResponseBody, client_->SendSynchronousRequest("/bar"));
+ EXPECT_EQ(server_cid3, server_connection->connection_id());
+ EXPECT_EQ(client_cid3, server_connection->client_connection_id());
+ EXPECT_TRUE(QuicConnectionPeer::GetServerConnectionIdOnAlternativePath(
+ server_connection)
+ .IsEmpty());
+ EXPECT_TRUE(QuicConnectionPeer::GetClientConnectionIdOnAlternativePath(
+ server_connection)
+ .IsEmpty());
+
+ // Server should send new server connection ID to client and retires old
+ // client connection ID.
+ WaitForNewConnectionIds();
+ EXPECT_EQ(2u, client_connection->GetStats().num_retire_connection_id_sent);
+ EXPECT_EQ(3u, client_connection->GetStats().num_new_connection_id_sent);
+}
+
TEST_P(EndToEndPacketReorderingTest, Buffer0RttRequest) {
ASSERT_TRUE(Initialize());
// Finish one request to make sure handshake established.
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index d8efb89..9c26695 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -715,6 +715,17 @@
QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 6, 6);
validate_client_addresses_ = true;
}
+ // Having connection_migration_use_new_cid_ depends on the same set of flags
+ // and connection option on both client and server sides has the advantage of:
+ // 1) Less chance of skew in using new connection ID or not between client
+ // and server in unit tests with random flag combinations.
+ // 2) Client side's rollout can be protected by the same connection option.
+ connection_migration_use_new_cid_ =
+ support_multiple_connection_ids_ && validate_client_addresses_ &&
+ use_connection_id_on_default_path_ &&
+ group_path_response_and_challenge_sending_closer_ &&
+ GetQuicReloadableFlag(quic_drop_unsent_path_response) &&
+ GetQuicReloadableFlag(quic_connection_migration_use_new_cid);
if (config.HasReceivedMaxPacketSize()) {
peer_max_packet_size_ = config.ReceivedMaxPacketSize();
MaybeUpdatePacketCreatorMaxPacketLengthAndPadding();
@@ -1816,7 +1827,7 @@
<< current_effective_peer_address;
QUIC_CODE_COUNT_N(quic_kick_off_client_address_validation, 2, 6);
ValidatePath(std::make_unique<ReversePathValidationContext>(
- default_path_.self_address, current_effective_peer_address,
+ default_path_.self_address, last_packet_source_address_,
current_effective_peer_address, this),
std::make_unique<ReversePathValidationResultDelegate>(
this, peer_address()));
@@ -2013,6 +2024,7 @@
}
void QuicConnection::OnClientConnectionIdAvailable() {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid, 3, 5);
QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER);
if (!peer_issued_cid_manager_->HasUnusedConnectionId()) {
return;
@@ -2102,7 +2114,8 @@
if (debug_visitor_ != nullptr) {
debug_visitor_->OnRetireConnectionIdFrame(frame);
}
- if (use_connection_id_on_default_path_) {
+ if (use_connection_id_on_default_path_ &&
+ !connection_migration_use_new_cid_) {
// Do not respond to RetireConnectionId frame.
return true;
}
@@ -4110,6 +4123,11 @@
void QuicConnection::OnHandshakeComplete() {
sent_packet_manager_.SetHandshakeConfirmed();
+ if (connection_migration_use_new_cid_ &&
+ perspective_ == Perspective::IS_SERVER &&
+ self_issued_cid_manager_ != nullptr) {
+ self_issued_cid_manager_->MaybeSendNewConnectionIds();
+ }
if (send_ack_frequency_on_handshake_completion_ &&
sent_packet_manager_.CanSendAckFrequency()) {
QUIC_RELOADABLE_FLAG_COUNT_N(quic_can_send_ack_frequency, 2, 3);
@@ -6439,6 +6457,7 @@
peer_issued_cid_manager_->ConsumeToBeRetiredConnectionIdSequenceNumbers();
QUICHE_DCHECK(!retired_cid_sequence_numbers.empty());
for (const auto& sequence_number : retired_cid_sequence_numbers) {
+ ++stats_.num_retire_connection_id_sent;
visitor_->SendRetireConnectionId(sequence_number);
}
}
@@ -6446,6 +6465,7 @@
bool QuicConnection::SendNewConnectionId(
const QuicNewConnectionIdFrame& frame) {
visitor_->SendNewConnectionId(frame);
+ ++stats_.num_new_connection_id_sent;
return connected_;
}
@@ -6782,6 +6802,7 @@
}
void QuicConnection::RetirePeerIssuedConnectionIdsNoLongerOnPath() {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid, 4, 5);
if (!connection_migration_use_new_cid_ ||
peer_issued_cid_manager_ == nullptr) {
return;
diff --git a/quic/core/quic_connection_stats.h b/quic/core/quic_connection_stats.h
index 06b7f58..c2a64ce 100644
--- a/quic/core/quic_connection_stats.h
+++ b/quic/core/quic_connection_stats.h
@@ -209,6 +209,10 @@
// which was canceled because the peer migrated again. Such migration is also
// counted as invalid peer migration.
size_t num_peer_migration_while_validating_default_path = 0;
+ // Number of NEW_CONNECTION_ID frames sent.
+ size_t num_new_connection_id_sent = 0;
+ // Number of RETIRE_CONNECTION_ID frames sent.
+ size_t num_retire_connection_id_sent = 0;
struct QUIC_NO_EXPORT TlsServerOperationStats {
bool success = false;
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 00af323..833a692 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -1428,7 +1428,8 @@
EXPECT_TRUE(connection_.connected());
}
- void PathProbeTestInit(Perspective perspective) {
+ void PathProbeTestInit(Perspective perspective,
+ bool receive_new_server_connection_id = true) {
set_perspective(perspective);
connection_.CreateConnectionIdManager();
EXPECT_EQ(connection_.perspective(), perspective);
@@ -1462,6 +1463,17 @@
kPeerAddress, ENCRYPTION_FORWARD_SECURE);
EXPECT_EQ(kPeerAddress, connection_.peer_address());
EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+ if (perspective == Perspective::IS_CLIENT &&
+ receive_new_server_connection_id) {
+ QuicNewConnectionIdFrame frame;
+ frame.connection_id = TestConnectionId(1234);
+ ASSERT_NE(frame.connection_id, connection_.connection_id());
+ frame.stateless_reset_token =
+ QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+ frame.retire_prior_to = 0u;
+ frame.sequence_number = 1u;
+ connection_.OnNewConnectionIdFrame(frame);
+ }
}
void TestClientRetryHandling(bool invalid_retry_tag,
@@ -1877,14 +1889,11 @@
TEST_P(QuicConnectionTest, PeerIpAddressChangeAtServerWithMissingConnectionId) {
set_perspective(Perspective::IS_SERVER);
- if (!connection_.validate_client_address() ||
- !connection_.use_connection_id_on_default_path() ||
- !connection_.support_multiple_connection_ids()) {
+ if (!connection_.connection_migration_use_new_cid()) {
return;
}
QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
- QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
QuicConnectionId client_cid0 = TestConnectionId(1);
QuicConnectionId client_cid1 = TestConnectionId(3);
@@ -1896,14 +1905,13 @@
EXPECT_CALL(visitor_, GetHandshakeState())
.WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
QuicConnectionPeer::SetAddressValidated(&connection_);
- connection_.OnHandshakeComplete();
// Sends new server CID to client.
EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_))
.WillOnce(
Invoke([&](const QuicConnectionId& cid) { server_cid1 = cid; }));
EXPECT_CALL(visitor_, SendNewConnectionId(_));
- connection_.MaybeSendConnectionIdToClient();
+ connection_.OnHandshakeComplete();
// Clear direct_peer_address.
QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
@@ -2073,14 +2081,11 @@
TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) {
set_perspective(Perspective::IS_SERVER);
- if (!connection_.validate_client_address() ||
- !connection_.use_connection_id_on_default_path() ||
- !connection_.support_multiple_connection_ids()) {
+ if (!connection_.connection_migration_use_new_cid()) {
return;
}
QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective());
- QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
SetClientConnectionId(TestConnectionId(1));
connection_.CreateConnectionIdManager();
connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
@@ -2088,7 +2093,6 @@
EXPECT_CALL(visitor_, GetHandshakeState())
.WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
QuicConnectionPeer::SetAddressValidated(&connection_);
- connection_.OnHandshakeComplete();
QuicConnectionId client_cid0 = connection_.client_connection_id();
QuicConnectionId client_cid1 = TestConnectionId(2);
@@ -2099,7 +2103,7 @@
.WillOnce(
Invoke([&](const QuicConnectionId& cid) { server_cid1 = cid; }));
EXPECT_CALL(visitor_, SendNewConnectionId(_));
- connection_.MaybeSendConnectionIdToClient();
+ connection_.OnHandshakeComplete();
// Receives new client CID from client.
QuicNewConnectionIdFrame new_cid_frame;
new_cid_frame.connection_id = client_cid1;
@@ -11918,15 +11922,17 @@
const QuicSocketAddress kNewSelfAddress2(QuicIpAddress::Any4(), 12346);
EXPECT_NE(kNewSelfAddress2, connection_.self_address());
TestPacketWriter new_writer2(version(), &clock_, Perspective::IS_CLIENT);
- EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
- .Times(AtLeast(1u))
- .WillOnce(Invoke([&]() {
- EXPECT_EQ(1u, new_writer2.packets_write_attempts());
- EXPECT_EQ(1u, new_writer2.path_challenge_frames().size());
- EXPECT_EQ(1u, new_writer2.padding_frames().size());
- EXPECT_EQ(kNewSelfAddress2.host(),
- new_writer2.last_write_source_address());
- }));
+ if (!connection_.connection_migration_use_new_cid()) {
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(AtLeast(1u))
+ .WillOnce(Invoke([&]() {
+ EXPECT_EQ(1u, new_writer2.packets_write_attempts());
+ EXPECT_EQ(1u, new_writer2.path_challenge_frames().size());
+ EXPECT_EQ(1u, new_writer2.padding_frames().size());
+ EXPECT_EQ(kNewSelfAddress2.host(),
+ new_writer2.last_write_source_address());
+ }));
+ }
bool success2 = false;
connection_.ValidatePath(
std::make_unique<TestQuicPathValidationContext>(
@@ -11935,7 +11941,13 @@
&connection_, kNewSelfAddress2, connection_.peer_address(),
&success2));
EXPECT_FALSE(success);
- EXPECT_TRUE(connection_.HasPendingPathValidation());
+ if (connection_.connection_migration_use_new_cid()) {
+ // There is no pening path validation as there is no available connection
+ // ID.
+ EXPECT_FALSE(connection_.HasPendingPathValidation());
+ } else {
+ EXPECT_TRUE(connection_.HasPendingPathValidation());
+ }
}
// Regression test for b/182571515.
@@ -13889,13 +13901,10 @@
TEST_P(QuicConnectionTest, PathChallengeBeforePeerIpAddressChangeAtServer) {
set_perspective(Perspective::IS_SERVER);
- if (!connection_.validate_client_address() ||
- !connection_.use_connection_id_on_default_path() ||
- !connection_.support_multiple_connection_ids()) {
+ if (!connection_.connection_migration_use_new_cid()) {
return;
}
PathProbeTestInit(Perspective::IS_SERVER);
- QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
SetClientConnectionId(TestConnectionId(1));
connection_.CreateConnectionIdManager();
@@ -14039,13 +14048,10 @@
TEST_P(QuicConnectionTest,
PathValidationSucceedsBeforePeerIpAddressChangeAtServer) {
set_perspective(Perspective::IS_SERVER);
- if (!connection_.validate_client_address() ||
- !connection_.use_connection_id_on_default_path() ||
- !connection_.support_multiple_connection_ids()) {
+ if (!connection_.connection_migration_use_new_cid()) {
return;
}
PathProbeTestInit(Perspective::IS_SERVER);
- QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
connection_.CreateConnectionIdManager();
QuicConnectionId server_cid0 = connection_.connection_id();
@@ -14220,13 +14226,15 @@
TEST_P(QuicConnectionTest,
PathValidationFailedOnClientDueToLackOfServerConnectionId) {
- if (!connection_.support_multiple_connection_ids() ||
- !connection_.use_connection_id_on_default_path() ||
- !connection_.use_path_validator()) {
+ QuicConfig config;
+ config.SetConnectionOptionsToSend({kRVCM});
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ connection_.SetFromConfig(config);
+ if (!connection_.connection_migration_use_new_cid()) {
return;
}
- QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
- PathProbeTestInit(Perspective::IS_CLIENT);
+ PathProbeTestInit(Perspective::IS_CLIENT,
+ /*receive_new_server_connection_id=*/false);
const QuicSocketAddress kNewSelfAddress(QuicIpAddress::Loopback4(),
/*port=*/34567);
@@ -14243,14 +14251,15 @@
TEST_P(QuicConnectionTest,
PathValidationFailedOnClientDueToLackOfClientConnectionIdTheSecondTime) {
- if (!connection_.support_multiple_connection_ids() ||
- !connection_.use_connection_id_on_default_path() ||
- !connection_.use_path_validator()) {
+ QuicConfig config;
+ config.SetConnectionOptionsToSend({kRVCM});
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ connection_.SetFromConfig(config);
+ if (!connection_.connection_migration_use_new_cid()) {
return;
}
-
- QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
- PathProbeTestInit(Perspective::IS_CLIENT);
+ PathProbeTestInit(Perspective::IS_CLIENT,
+ /*receive_new_server_connection_id=*/false);
SetClientConnectionId(TestConnectionId(1));
// Make sure server connection ID is available for the 1st validation.
@@ -14331,13 +14340,13 @@
}
TEST_P(QuicConnectionTest, ServerConnectionIdRetiredUponPathValidationFailure) {
- if (!connection_.support_multiple_connection_ids() ||
- !connection_.use_connection_id_on_default_path() ||
- !connection_.use_path_validator()) {
+ QuicConfig config;
+ config.SetConnectionOptionsToSend({kRVCM});
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ connection_.SetFromConfig(config);
+ if (!connection_.connection_migration_use_new_cid()) {
return;
}
-
- QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
PathProbeTestInit(Perspective::IS_CLIENT);
// Make sure server connection ID is available for validation.
@@ -14378,12 +14387,15 @@
TEST_P(QuicConnectionTest,
MigratePathDirectlyFailedDueToLackOfServerConnectionId) {
- if (!connection_.support_multiple_connection_ids() ||
- !connection_.use_connection_id_on_default_path()) {
+ QuicConfig config;
+ config.SetConnectionOptionsToSend({kRVCM});
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ connection_.SetFromConfig(config);
+ if (!connection_.connection_migration_use_new_cid()) {
return;
}
- QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
- PathProbeTestInit(Perspective::IS_CLIENT);
+ PathProbeTestInit(Perspective::IS_CLIENT,
+ /*receive_new_server_connection_id=*/false);
const QuicSocketAddress kSelfAddress1(QuicIpAddress::Any4(), 12345);
ASSERT_NE(kSelfAddress1, connection_.self_address());
@@ -14395,13 +14407,15 @@
TEST_P(QuicConnectionTest,
MigratePathDirectlyFailedDueToLackOfClientConnectionIdTheSecondTime) {
- if (!connection_.support_multiple_connection_ids() ||
- !connection_.use_connection_id_on_default_path() ||
- !connection_.use_path_validator()) {
+ QuicConfig config;
+ config.SetConnectionOptionsToSend({kRVCM});
+ EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _));
+ connection_.SetFromConfig(config);
+ if (!connection_.connection_migration_use_new_cid()) {
return;
}
- QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
- PathProbeTestInit(Perspective::IS_CLIENT);
+ PathProbeTestInit(Perspective::IS_CLIENT,
+ /*receive_new_server_connection_id=*/false);
SetClientConnectionId(TestConnectionId(1));
// Make sure server connection ID is available for the 1st migration.
@@ -14695,6 +14709,9 @@
TEST_P(QuicConnectionTest,
ServerRetireSelfIssuedConnectionIdWithoutSendingNewConnectionIdBefore) {
+ if (!connection_.support_multiple_connection_ids()) {
+ return;
+ }
QuicConnectionPeer::EnableMultipleConnectionIdSupport(&connection_);
set_perspective(Perspective::IS_SERVER);
connection_.CreateConnectionIdManager();
@@ -14706,7 +14723,8 @@
QuicConnectionId cid0 = connection_id_;
QuicRetireConnectionIdFrame frame;
frame.sequence_number = 0u;
- if (!GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2)) {
+ if (!GetQuicReloadableFlag(quic_use_connection_id_on_default_path_v2) ||
+ connection_.connection_migration_use_new_cid()) {
EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)).Times(2);
EXPECT_CALL(visitor_, SendNewConnectionId(_)).Times(2);
}
diff --git a/quic/core/quic_dispatcher.cc b/quic/core/quic_dispatcher.cc
index ccf2c3f..394253b 100644
--- a/quic/core/quic_dispatcher.cc
+++ b/quic/core/quic_dispatcher.cc
@@ -1026,6 +1026,7 @@
<< server_connection_id << " new_connection_id: " << new_connection_id;
return;
}
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid, 5, 5);
auto insertion_result = reference_counted_session_map_.insert(
std::make_pair(new_connection_id, it->second));
QUICHE_DCHECK(insertion_result.second);
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index f056cd5..0698606 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -16,6 +16,7 @@
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_avoid_too_low_probe_bw_cwnd, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_fix_bw_lo_mode, true)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_can_send_ack_frequency, true)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_connection_migration_use_new_cid, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_connection_support_multiple_cids_v4, true)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_conservative_bursts, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_conservative_cwnd_and_pacing_gains, false)
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index fb564f0..60abaf1 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -2101,12 +2101,14 @@
}
void QuicSession::SendNewConnectionId(const QuicNewConnectionIdFrame& frame) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid, 1, 5);
control_frame_manager_.WriteOrBufferNewConnectionId(
frame.connection_id, frame.sequence_number, frame.retire_prior_to,
frame.stateless_reset_token);
}
void QuicSession::SendRetireConnectionId(uint64_t sequence_number) {
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_connection_migration_use_new_cid, 2, 5);
control_frame_manager_.WriteOrBufferRetireConnectionId(sequence_number);
}
diff --git a/quic/test_tools/quic_connection_peer.cc b/quic/test_tools/quic_connection_peer.cc
index bdd9eff..71036cb 100644
--- a/quic/test_tools/quic_connection_peer.cc
+++ b/quic/test_tools/quic_connection_peer.cc
@@ -490,12 +490,6 @@
}
// static
-void QuicConnectionPeer::EnableConnectionMigrationUseNewCID(
- QuicConnection* connection) {
- connection->connection_migration_use_new_cid_ = true;
-}
-
-// static
void QuicConnectionPeer::ResetPeerIssuedConnectionIdManager(
QuicConnection* connection) {
connection->peer_issued_cid_manager_ = nullptr;
@@ -519,5 +513,17 @@
connection->RetirePeerIssuedConnectionIdsNoLongerOnPath();
}
+// static
+bool QuicConnectionPeer::HasUnusedPeerIssuedConnectionId(
+ const QuicConnection* connection) {
+ return connection->peer_issued_cid_manager_->HasUnusedConnectionId();
+}
+
+// static
+bool QuicConnectionPeer::HasSelfIssuedConnectionIdToConsume(
+ const QuicConnection* connection) {
+ return connection->self_issued_cid_manager_->HasConnectionIdToConsume();
+}
+
} // namespace test
} // namespace quic
diff --git a/quic/test_tools/quic_connection_peer.h b/quic/test_tools/quic_connection_peer.h
index df2a823..cbc829a 100644
--- a/quic/test_tools/quic_connection_peer.h
+++ b/quic/test_tools/quic_connection_peer.h
@@ -199,9 +199,6 @@
static void EnableMultipleConnectionIdSupport(QuicConnection* connection);
- // Remove this method once the boolean is enabled via reloadable flag.
- static void EnableConnectionMigrationUseNewCID(QuicConnection* connection);
-
static void ResetPeerIssuedConnectionIdManager(QuicConnection* connection);
static QuicConnection::PathState* GetDefaultPath(QuicConnection* connection);
@@ -211,6 +208,11 @@
static void RetirePeerIssuedConnectionIdsNoLongerOnPath(
QuicConnection* connection);
+
+ static bool HasUnusedPeerIssuedConnectionId(const QuicConnection* connection);
+
+ static bool HasSelfIssuedConnectionIdToConsume(
+ const QuicConnection* connection);
};
} // namespace test
diff --git a/quic/tools/quic_client_base.cc b/quic/tools/quic_client_base.cc
index 66f28b7..53c6800 100644
--- a/quic/tools/quic_client_base.cc
+++ b/quic/tools/quic_client_base.cc
@@ -64,6 +64,7 @@
std::unique_ptr<QuicPathValidationContext> context) override {
QUIC_LOG(WARNING) << "Fail to validate path " << *context
<< ", stop migrating.";
+ client_->session()->connection()->OnPathValidationFailureAtClient();
}
private:
@@ -257,6 +258,8 @@
const QuicIpAddress& new_host,
int port) {
if (!connected()) {
+ QUICHE_DVLOG(1)
+ << "MigrateSocketWithSpecifiedPort failed as connection has closed";
return false;
}
@@ -264,11 +267,17 @@
std::unique_ptr<QuicPacketWriter> writer =
CreateWriterForNewNetwork(new_host, port);
if (writer == nullptr) {
+ QUICHE_DVLOG(1)
+ << "MigrateSocketWithSpecifiedPort failed from writer creation";
return false;
}
- session()->MigratePath(network_helper_->GetLatestClientAddress(),
- session()->connection()->peer_address(), writer.get(),
- false);
+ if (!session()->MigratePath(network_helper_->GetLatestClientAddress(),
+ session()->connection()->peer_address(),
+ writer.get(), false)) {
+ QUICHE_DVLOG(1)
+ << "MigrateSocketWithSpecifiedPort failed from session()->MigratePath";
+ return false;
+ }
set_writer(writer.release());
return true;
}