Use new connection ID in connection migration on the client side.
Code is protected by connection_migration_use_new_cid_, which will be enabled by reloadable flag in follow up CL once the feature is complete.
PiperOrigin-RevId: 369297584
Change-Id: Ie5ce5e2ac987beb0f04f121227633e5934ed2210
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 62e9f4d..74931d0 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -2855,6 +2855,7 @@
MaybeSendInResponseToPacket();
}
SetPingAlarm();
+ RetirePeerIssuedConnectionIdsNoLongerOnPath();
current_packet_data_ = nullptr;
is_current_packet_connectivity_probing_ = false;
}
@@ -3672,6 +3673,7 @@
SetRetransmissionAlarm();
}
SetPingAlarm();
+ RetirePeerIssuedConnectionIdsNoLongerOnPath();
// The packet number length must be updated after OnPacketSent, because it
// may change the packet number length in packet.
@@ -6337,21 +6339,22 @@
bool QuicConnection::SendNewConnectionId(
const QuicNewConnectionIdFrame& frame) {
- QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER);
visitor_->SendNewConnectionId(frame);
return connected_;
}
void QuicConnection::OnNewConnectionIdIssued(
const QuicConnectionId& connection_id) {
- QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER);
- visitor_->OnServerConnectionIdIssued(connection_id);
+ if (perspective_ == Perspective::IS_SERVER) {
+ visitor_->OnServerConnectionIdIssued(connection_id);
+ }
}
void QuicConnection::OnSelfIssuedConnectionIdRetired(
const QuicConnectionId& connection_id) {
- QUICHE_DCHECK(perspective_ == Perspective::IS_SERVER);
- visitor_->OnServerConnectionIdRetired(connection_id);
+ if (perspective_ == Perspective::IS_SERVER) {
+ visitor_->OnServerConnectionIdRetired(connection_id);
+ }
}
void QuicConnection::MaybeUpdateAckTimeout() {
@@ -6468,7 +6471,8 @@
std::unique_ptr<QuicPathValidationContext> context,
std::unique_ptr<QuicPathValidator::ResultDelegate> result_delegate) {
QUICHE_DCHECK(use_path_validator_);
- if (perspective_ == Perspective::IS_CLIENT &&
+ if (!connection_migration_use_new_cid_ &&
+ perspective_ == Perspective::IS_CLIENT &&
!IsDefaultPath(context->self_address(), context->peer_address())) {
alternative_path_ = PathState(
context->self_address(), context->peer_address(),
@@ -6480,6 +6484,43 @@
// Cancel and fail any earlier validation.
path_validator_.CancelPathValidation();
}
+ if (connection_migration_use_new_cid_ &&
+ perspective_ == Perspective::IS_CLIENT &&
+ !IsDefaultPath(context->self_address(), context->peer_address())) {
+ if (self_issued_cid_manager_ != nullptr) {
+ self_issued_cid_manager_->MaybeSendNewConnectionIds();
+ if (!connected_) {
+ return;
+ }
+ }
+ if ((self_issued_cid_manager_ != nullptr &&
+ !self_issued_cid_manager_->HasConnectionIdToConsume()) ||
+ (peer_issued_cid_manager_ != nullptr &&
+ !peer_issued_cid_manager_->HasUnusedConnectionId())) {
+ QUIC_DVLOG(1) << "Client cannot start new path validation as there is no "
+ "requried connection ID is available.";
+ result_delegate->OnPathValidationFailure(std::move(context));
+ return;
+ }
+ QuicConnectionId client_connection_id, server_connection_id;
+ StatelessResetToken stateless_reset_token;
+ bool stateless_reset_token_received = false;
+ if (self_issued_cid_manager_ != nullptr) {
+ client_connection_id =
+ *self_issued_cid_manager_->ConsumeOneConnectionId();
+ }
+ if (peer_issued_cid_manager_ != nullptr) {
+ const auto* connection_id_data =
+ peer_issued_cid_manager_->ConsumeOneUnusedConnectionId();
+ server_connection_id = connection_id_data->connection_id;
+ stateless_reset_token_received = true;
+ stateless_reset_token = connection_id_data->stateless_reset_token;
+ }
+ alternative_path_ =
+ PathState(context->self_address(), context->peer_address(),
+ client_connection_id, server_connection_id,
+ stateless_reset_token_received, stateless_reset_token);
+ }
path_validator_.StartPathValidation(std::move(context),
std::move(result_delegate));
}
@@ -6550,12 +6591,80 @@
path_validator_.CancelPathValidation();
}
-void QuicConnection::MigratePath(const QuicSocketAddress& self_address,
+bool QuicConnection::UpdateConnectionIdsOnClientMigration(
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address) {
+ QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT);
+ if (IsAlternativePath(self_address, peer_address)) {
+ // Client migration is after path validation.
+ if (peer_issued_cid_manager_ != nullptr) {
+ QUICHE_DCHECK(!default_path_.server_connection_id.IsEmpty());
+ packet_creator_.FlushCurrentPacket();
+ }
+ default_path_.client_connection_id = alternative_path_.client_connection_id;
+ default_path_.server_connection_id = alternative_path_.server_connection_id;
+ default_path_.stateless_reset_token =
+ alternative_path_.stateless_reset_token;
+ default_path_.stateless_reset_token_received =
+ alternative_path_.stateless_reset_token_received;
+ packet_creator_.SetClientConnectionId(default_path_.client_connection_id);
+ packet_creator_.SetServerConnectionId(default_path_.server_connection_id);
+ return true;
+ }
+ // Client migration is without path validation.
+ if (self_issued_cid_manager_ != nullptr) {
+ self_issued_cid_manager_->MaybeSendNewConnectionIds();
+ if (!connected_) {
+ return false;
+ }
+ }
+ if ((self_issued_cid_manager_ != nullptr &&
+ !self_issued_cid_manager_->HasConnectionIdToConsume()) ||
+ (peer_issued_cid_manager_ != nullptr &&
+ !peer_issued_cid_manager_->HasUnusedConnectionId())) {
+ return false;
+ }
+ if (self_issued_cid_manager_ != nullptr) {
+ default_path_.client_connection_id =
+ *self_issued_cid_manager_->ConsumeOneConnectionId();
+ }
+ if (peer_issued_cid_manager_ != nullptr) {
+ const auto* connection_id_data =
+ peer_issued_cid_manager_->ConsumeOneUnusedConnectionId();
+ default_path_.server_connection_id = connection_id_data->connection_id;
+ default_path_.stateless_reset_token_received = true;
+ default_path_.stateless_reset_token =
+ connection_id_data->stateless_reset_token;
+ }
+ packet_creator_.SetClientConnectionId(default_path_.client_connection_id);
+ packet_creator_.SetServerConnectionId(default_path_.server_connection_id);
+ return true;
+}
+
+void QuicConnection::RetirePeerIssuedConnectionIdsNoLongerOnPath() {
+ if (!connection_migration_use_new_cid_ ||
+ peer_issued_cid_manager_ == nullptr) {
+ return;
+ }
+ if (perspective_ == Perspective::IS_CLIENT) {
+ peer_issued_cid_manager_->MaybeRetireUnusedConnectionIds(
+ {default_path_.server_connection_id,
+ alternative_path_.server_connection_id});
+ }
+ // TODO(haoyuewang) Do the same on the server side.
+}
+
+bool QuicConnection::MigratePath(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
QuicPacketWriter* writer,
bool owns_writer) {
if (!connected_) {
- return;
+ return false;
+ }
+
+ if (connection_migration_use_new_cid_ &&
+ !UpdateConnectionIdsOnClientMigration(self_address, peer_address)) {
+ return false;
}
const auto self_address_change_type = QuicUtils::DetermineAddressChangeType(
@@ -6572,6 +6681,14 @@
UpdatePeerAddress(peer_address);
SetQuicPacketWriter(writer, owns_writer);
OnSuccessfulMigration(is_port_change);
+ return true;
+}
+
+void QuicConnection::OnPathValidationFailureAtClient() {
+ if (connection_migration_use_new_cid_) {
+ QUICHE_DCHECK(perspective_ == Perspective::IS_CLIENT);
+ alternative_path_.Clear();
+ }
}
std::vector<QuicConnectionId> QuicConnection::GetActiveServerConnectionIds()
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 454bf88..b81eb02 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -1211,11 +1211,17 @@
void CancelPathValidation();
- void MigratePath(const QuicSocketAddress& self_address,
+ // Returns true if the migration succeeds, otherwise returns false (e.g., no
+ // available CIDs, connection disconnected, etc).
+ bool MigratePath(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
QuicPacketWriter* writer,
bool owns_writer);
+ // Called to clear the alternative_path_ when path validation failed on the
+ // client side.
+ void OnPathValidationFailureAtClient();
+
void SetSourceAddressTokenToSend(absl::string_view token);
void SendPing() {
@@ -1236,6 +1242,10 @@
return use_connection_id_on_default_path_;
}
+ bool connection_migration_use_new_cid() const {
+ return connection_migration_use_new_cid_;
+ }
+
// Instantiates connection ID manager.
void CreateConnectionIdManager();
@@ -1505,6 +1515,16 @@
bool* stateless_reset_token_received,
StatelessResetToken* stateless_reset_token) const;
+ // Update the connection IDs when client migrates with/without validation.
+ // Returns false if required connection ID is not available.
+ bool UpdateConnectionIdsOnClientMigration(
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address);
+
+ // Retire active peer issued connection IDs after they are no longer used on
+ // any path.
+ void RetirePeerIssuedConnectionIdsNoLongerOnPath();
+
// Writes the given packet to socket, encrypted with packet's
// encryption_level. Returns true on successful write, and false if the writer
// was blocked and the write needs to be tried again. Notifies the
@@ -2249,6 +2269,9 @@
// PATH_CHALLENGE received.
bool should_proactively_validate_peer_address_on_path_challenge_ = false;
+ // Enable this via reloadable flag once this feature is complete.
+ bool connection_migration_use_new_cid_ = false;
+
const bool group_path_response_and_challenge_sending_closer_ =
GetQuicReloadableFlag(
quic_group_path_response_and_challenge_sending_closer);
diff --git a/quic/core/quic_connection_id_manager.cc b/quic/core/quic_connection_id_manager.cc
index 5069234..f7089b7 100644
--- a/quic/core/quic_connection_id_manager.cc
+++ b/quic/core/quic_connection_id_manager.cc
@@ -199,6 +199,22 @@
}
}
+void QuicPeerIssuedConnectionIdManager::MaybeRetireUnusedConnectionIds(
+ const std::vector<QuicConnectionId>& active_connection_ids_on_path) {
+ std::vector<QuicConnectionId> cids_to_retire;
+ for (const auto& cid_data : active_connection_id_data_) {
+ if (std::find(active_connection_ids_on_path.begin(),
+ active_connection_ids_on_path.end(),
+ cid_data.connection_id) ==
+ active_connection_ids_on_path.end()) {
+ cids_to_retire.push_back(cid_data.connection_id);
+ }
+ }
+ for (const auto& cid : cids_to_retire) {
+ PrepareToRetireActiveConnectionId(cid);
+ }
+}
+
bool QuicPeerIssuedConnectionIdManager::IsConnectionIdActive(
const QuicConnectionId& cid) const {
return FindConnectionIdData(active_connection_id_data_, cid) !=
diff --git a/quic/core/quic_connection_id_manager.h b/quic/core/quic_connection_id_manager.h
index f42915a..5e7fc8c 100644
--- a/quic/core/quic_connection_id_manager.h
+++ b/quic/core/quic_connection_id_manager.h
@@ -78,8 +78,10 @@
// Id.
const QuicConnectionIdData* ConsumeOneUnusedConnectionId();
- // Add the connection Id to the pending retirement connection Id list.
- void PrepareToRetireActiveConnectionId(const QuicConnectionId& cid);
+ // Add each active connection Id that is no longer on path to the pending
+ // retirement connection Id list.
+ void MaybeRetireUnusedConnectionIds(
+ const std::vector<QuicConnectionId>& active_connection_ids_on_path);
bool IsConnectionIdActive(const QuicConnectionId& cid) const;
@@ -95,6 +97,10 @@
private:
friend class test::QuicConnectionIdManagerPeer;
+ // Add the connection Id to the pending retirement connection Id list and
+ // schedule an alarm if needed.
+ void PrepareToRetireActiveConnectionId(const QuicConnectionId& cid);
+
bool IsConnectionIdNew(const QuicNewConnectionIdFrame& frame);
void PrepareToRetireConnectionIdPriorTo(
diff --git a/quic/core/quic_connection_id_manager_test.cc b/quic/core/quic_connection_id_manager_test.cc
index d339a2c..7baeb2b 100644
--- a/quic/core/quic_connection_id_manager_test.cc
+++ b/quic/core/quic_connection_id_manager_test.cc
@@ -126,8 +126,8 @@
frame.stateless_reset_token);
// Connection migration succeed. Prepares to retire CID #0.
- peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
- TestConnectionId(0));
+ peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds(
+ {TestConnectionId(1)});
cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(1));
ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
@@ -150,8 +150,8 @@
// Start to use CID #2 for alternative path.
peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
// Connection migration succeed. Prepares to retire CID #1.
- peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
- TestConnectionId(1));
+ peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds(
+ {TestConnectionId(2)});
cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(2));
ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
@@ -174,8 +174,8 @@
// Start to use CID #3 for alternative path.
peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
// Connection migration succeed. Prepares to retire CID #2.
- peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
- TestConnectionId(2));
+ peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds(
+ {TestConnectionId(3)});
cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(3));
ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
@@ -214,8 +214,8 @@
// Start to use CID #1 for alternative path.
peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
// Connection migration fails. Prepares to retire CID #1.
- peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
- TestConnectionId(1));
+ peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds(
+ {initial_connection_id_});
// Actually retires CID #1.
ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
@@ -238,8 +238,8 @@
// Start to use CID #2 for alternative path.
peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
// Connection migration fails again. Prepares to retire CID #2.
- peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
- TestConnectionId(2));
+ peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds(
+ {initial_connection_id_});
// Actually retires CID #2.
ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
@@ -262,8 +262,8 @@
// Start to use CID #3 for alternative path.
peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
// Connection migration succeed. Prepares to retire CID #0.
- peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
- TestConnectionId(0));
+ peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds(
+ {TestConnectionId(3)});
// After CID #3 is default (i.e., when there is no pending frame to write
// associated with CID #0), #0 can actually be retired.
cid_manager_visitor_.SetCurrentPeerConnectionId(TestConnectionId(3));
@@ -374,8 +374,8 @@
// Outcome: (active: #0 #1 unused: None)
peer_issued_cid_manager_.ConsumeOneUnusedConnectionId();
// Prepare to retire CID #1 as path validation fails.
- peer_issued_cid_manager_.PrepareToRetireActiveConnectionId(
- TestConnectionId(1));
+ peer_issued_cid_manager_.MaybeRetireUnusedConnectionIds(
+ {initial_connection_id_});
// Actually retires CID #1.
ASSERT_TRUE(retire_peer_issued_cid_alarm_->IsSet());
alarm_factory_.FireAlarm(retire_peer_issued_cid_alarm_);
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index fcdcf4f..e73298d 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -21,6 +21,7 @@
#include "quic/core/crypto/quic_decrypter.h"
#include "quic/core/crypto/quic_encrypter.h"
#include "quic/core/frames/quic_connection_close_frame.h"
+#include "quic/core/frames/quic_new_connection_id_frame.h"
#include "quic/core/frames/quic_path_response_frame.h"
#include "quic/core/quic_connection_id.h"
#include "quic/core/quic_constants.h"
@@ -2193,10 +2194,12 @@
class TestValidationResultDelegate : public QuicPathValidator::ResultDelegate {
public:
- TestValidationResultDelegate(const QuicSocketAddress& expected_self_address,
+ TestValidationResultDelegate(QuicConnection* connection,
+ const QuicSocketAddress& expected_self_address,
const QuicSocketAddress& expected_peer_address,
bool* success)
: QuicPathValidator::ResultDelegate(),
+ connection_(connection),
expected_self_address_(expected_self_address),
expected_peer_address_(expected_peer_address),
success_(success) {}
@@ -2211,10 +2214,14 @@
std::unique_ptr<QuicPathValidationContext> context) override {
EXPECT_EQ(expected_self_address_, context->self_address());
EXPECT_EQ(expected_peer_address_, context->peer_address());
+ if (connection_->perspective() == Perspective::IS_CLIENT) {
+ connection_->OnPathValidationFailureAtClient();
+ }
*success_ = false;
}
private:
+ QuicConnection* connection_;
QuicSocketAddress expected_self_address_;
QuicSocketAddress expected_peer_address_;
bool* success_;
@@ -2300,7 +2307,8 @@
std::make_unique<TestQuicPathValidationContext>(
connection_.self_address(), kNewPeerAddress, writer_.get()),
std::make_unique<TestValidationResultDelegate>(
- connection_.self_address(), kNewPeerAddress, &success));
+ &connection_, connection_.self_address(), kNewPeerAddress,
+ &success));
}
EXPECT_EQ((connection_.validate_client_address() ? 2 : 3) * bytes_sent,
QuicConnectionPeer::BytesSentOnAlternativePath(&connection_));
@@ -8908,7 +8916,7 @@
std::make_unique<TestQuicPathValidationContext>(
kNewSelfAddress, connection_.peer_address(), &new_writer),
std::make_unique<TestValidationResultDelegate>(
- kNewSelfAddress, connection_.peer_address(), &success));
+ &connection_, kNewSelfAddress, connection_.peer_address(), &success));
// Receiving a PATH_CHALLENGE on the alternative path. Response to this
// PATH_CHALLENGE should be sent via the alternative writer.
@@ -11759,7 +11767,7 @@
std::make_unique<TestQuicPathValidationContext>(
kNewSelfAddress, connection_.peer_address(), &new_writer),
std::make_unique<TestValidationResultDelegate>(
- kNewSelfAddress, connection_.peer_address(), &success));
+ &connection_, kNewSelfAddress, connection_.peer_address(), &success));
EXPECT_EQ(0u, writer_->packets_write_attempts());
QuicFrames frames;
@@ -11793,7 +11801,7 @@
std::make_unique<TestQuicPathValidationContext>(
kNewSelfAddress, connection_.peer_address(), &new_writer),
std::make_unique<TestValidationResultDelegate>(
- kNewSelfAddress, connection_.peer_address(), &success));
+ &connection_, kNewSelfAddress, connection_.peer_address(), &success));
EXPECT_EQ(0u, writer_->packets_write_attempts());
// Start another path validation request.
@@ -11814,7 +11822,8 @@
std::make_unique<TestQuicPathValidationContext>(
kNewSelfAddress2, connection_.peer_address(), &new_writer2),
std::make_unique<TestValidationResultDelegate>(
- kNewSelfAddress2, connection_.peer_address(), &success2));
+ &connection_, kNewSelfAddress2, connection_.peer_address(),
+ &success2));
EXPECT_FALSE(success);
EXPECT_TRUE(connection_.HasPendingPathValidation());
}
@@ -11834,12 +11843,12 @@
EXPECT_EQ(1u, writer_->padding_frames().size());
}));
bool success = true;
- connection_.ValidatePath(
- std::make_unique<TestQuicPathValidationContext>(
- connection_.self_address(), connection_.peer_address(),
- writer_.get()),
- std::make_unique<TestValidationResultDelegate>(
- connection_.self_address(), connection_.peer_address(), &success));
+ connection_.ValidatePath(std::make_unique<TestQuicPathValidationContext>(
+ connection_.self_address(),
+ connection_.peer_address(), writer_.get()),
+ std::make_unique<TestValidationResultDelegate>(
+ &connection_, connection_.self_address(),
+ connection_.peer_address(), &success));
EXPECT_EQ(1u, writer_->packets_write_attempts());
EXPECT_TRUE(connection_.HasPendingPathValidation());
@@ -11881,7 +11890,7 @@
std::make_unique<TestQuicPathValidationContext>(
kNewSelfAddress, connection_.peer_address(), &new_writer),
std::make_unique<TestValidationResultDelegate>(
- kNewSelfAddress, connection_.peer_address(), &success));
+ &connection_, kNewSelfAddress, connection_.peer_address(), &success));
EXPECT_EQ(0u, writer_->packets_write_attempts());
EXPECT_TRUE(connection_.HasPendingPathValidation());
@@ -11925,7 +11934,7 @@
std::make_unique<TestQuicPathValidationContext>(
kNewSelfAddress, connection_.peer_address(), &new_writer),
std::make_unique<TestValidationResultDelegate>(
- kNewSelfAddress, connection_.peer_address(), &success));
+ &connection_, kNewSelfAddress, connection_.peer_address(), &success));
EXPECT_EQ(0u, writer_->packets_write_attempts());
new_writer.SetWritable();
@@ -11985,7 +11994,8 @@
std::make_unique<TestQuicPathValidationContext>(
connection_.self_address(), kNewPeerAddress, writer_.get()),
std::make_unique<TestValidationResultDelegate>(
- connection_.self_address(), kNewPeerAddress, &success));
+ &connection_, connection_.self_address(), kNewPeerAddress,
+ &success));
}
EXPECT_EQ(1u, writer_->packets_write_attempts());
@@ -12029,7 +12039,7 @@
std::make_unique<TestQuicPathValidationContext>(
kNewSelfAddress, connection_.peer_address(), &new_writer),
std::make_unique<TestValidationResultDelegate>(
- kNewSelfAddress, connection_.peer_address(), &success));
+ &connection_, kNewSelfAddress, connection_.peer_address(), &success));
EXPECT_EQ(1u, new_writer.packets_write_attempts());
EXPECT_EQ(1u, new_writer.path_challenge_frames().size());
EXPECT_EQ(1u, new_writer.padding_frames().size());
@@ -12061,12 +12071,12 @@
// packet creator.
bool success = false;
QuicConnection::ScopedPacketFlusher flusher(&connection_);
- connection_.ValidatePath(
- std::make_unique<TestQuicPathValidationContext>(
- connection_.self_address(), connection_.peer_address(),
- writer_.get()),
- std::make_unique<TestValidationResultDelegate>(
- connection_.self_address(), connection_.peer_address(), &success));
+ connection_.ValidatePath(std::make_unique<TestQuicPathValidationContext>(
+ connection_.self_address(),
+ connection_.peer_address(), writer_.get()),
+ std::make_unique<TestValidationResultDelegate>(
+ &connection_, connection_.self_address(),
+ connection_.peer_address(), &success));
}
EXPECT_EQ(1u, writer_->packets_write_attempts());
EXPECT_EQ(1u, writer_->path_challenge_frames().size());
@@ -12098,7 +12108,7 @@
std::make_unique<TestQuicPathValidationContext>(
connection_.self_address(), kNewPeerAddress, writer_.get()),
std::make_unique<TestValidationResultDelegate>(
- connection_.self_address(), kNewPeerAddress, &success));
+ &connection_, connection_.self_address(), kNewPeerAddress, &success));
EXPECT_EQ(1u, writer_->packets_write_attempts());
EXPECT_FALSE(connection_.HasPendingPathValidation());
@@ -12129,7 +12139,7 @@
std::make_unique<TestQuicPathValidationContext>(
connection_.self_address(), kNewPeerAddress, writer_.get()),
std::make_unique<TestValidationResultDelegate>(
- connection_.self_address(), kNewPeerAddress, &success));
+ &connection_, connection_.self_address(), kNewPeerAddress, &success));
EXPECT_TRUE(connection_.HasPendingPathValidation());
// Connection shouldn't be closed.
EXPECT_TRUE(connection_.connected());
@@ -13486,7 +13496,7 @@
std::make_unique<TestQuicPathValidationContext>(
kNewSelfAddress, connection_.peer_address(), &new_writer),
std::make_unique<TestValidationResultDelegate>(
- kNewSelfAddress, connection_.peer_address(), &success));
+ &connection_, kNewSelfAddress, connection_.peer_address(), &success));
EXPECT_TRUE(connection_.HasPendingPathValidation());
EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath(
&connection_, kNewSelfAddress, connection_.peer_address()));
@@ -14019,6 +14029,236 @@
}
TEST_P(QuicConnectionTest,
+ PathValidationFailedOnClientDueToLackOfServerConnectionId) {
+ if (!connection_.support_multiple_connection_ids() ||
+ !connection_.use_connection_id_on_default_path()) {
+ return;
+ }
+ QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
+ PathProbeTestInit(Perspective::IS_CLIENT);
+
+ const QuicSocketAddress kNewSelfAddress(QuicIpAddress::Loopback4(),
+ /*port=*/34567);
+
+ bool success;
+ connection_.ValidatePath(
+ std::make_unique<TestQuicPathValidationContext>(
+ kNewSelfAddress, connection_.peer_address(), writer_.get()),
+ std::make_unique<TestValidationResultDelegate>(
+ &connection_, kNewSelfAddress, connection_.peer_address(), &success));
+
+ EXPECT_FALSE(success);
+}
+
+TEST_P(QuicConnectionTest,
+ PathValidationFailedOnClientDueToLackOfClientConnectionIdTheSecondTime) {
+ if (!connection_.support_multiple_connection_ids() ||
+ !connection_.use_connection_id_on_default_path()) {
+ return;
+ }
+
+ QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
+ PathProbeTestInit(Perspective::IS_CLIENT);
+ SetClientConnectionId(TestConnectionId(1));
+
+ // Make sure server connection ID is available for the 1st validation.
+ QuicNewConnectionIdFrame frame1;
+ frame1.connection_id = TestConnectionId(2);
+ frame1.sequence_number = 1u;
+ frame1.retire_prior_to = 0u;
+ frame1.stateless_reset_token =
+ QuicUtils::GenerateStatelessResetToken(frame1.connection_id);
+ connection_.OnNewConnectionIdFrame(frame1);
+
+ // Client will issue a new client connection ID to server.
+ QuicConnectionId new_client_connection_id;
+ EXPECT_CALL(visitor_, SendNewConnectionId(_))
+ .WillOnce(Invoke([&](const QuicNewConnectionIdFrame& frame) {
+ new_client_connection_id = frame.connection_id;
+ }));
+
+ const QuicSocketAddress kSelfAddress1(QuicIpAddress::Any4(), 12345);
+ ASSERT_NE(kSelfAddress1, connection_.self_address());
+ bool success1;
+ connection_.ValidatePath(
+ std::make_unique<TestQuicPathValidationContext>(
+ kSelfAddress1, connection_.peer_address(), writer_.get()),
+ std::make_unique<TestValidationResultDelegate>(
+ &connection_, kSelfAddress1, connection_.peer_address(), &success1));
+
+ // Migrate upon 1st validation success.
+ TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
+ ASSERT_TRUE(connection_.MigratePath(kSelfAddress1, connection_.peer_address(),
+ &new_writer, /*owns_writer=*/false));
+ QuicConnectionPeer::RetirePeerIssuedConnectionIdsNoLongerOnPath(&connection_);
+ const auto* default_path = QuicConnectionPeer::GetDefaultPath(&connection_);
+ EXPECT_EQ(default_path->client_connection_id, new_client_connection_id);
+ EXPECT_EQ(default_path->server_connection_id, frame1.connection_id);
+ EXPECT_EQ(default_path->stateless_reset_token, frame1.stateless_reset_token);
+ EXPECT_TRUE(default_path->stateless_reset_token_received);
+ const auto* alternative_path =
+ QuicConnectionPeer::GetAlternativePath(&connection_);
+ EXPECT_TRUE(alternative_path->client_connection_id.IsEmpty());
+ EXPECT_TRUE(alternative_path->server_connection_id.IsEmpty());
+ EXPECT_FALSE(alternative_path->stateless_reset_token_received);
+
+ // Client will retire server connection ID on old default_path.
+ auto* retire_peer_issued_cid_alarm =
+ connection_.GetRetirePeerIssuedConnectionIdAlarm();
+ ASSERT_TRUE(retire_peer_issued_cid_alarm->IsSet());
+ EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/0u));
+ retire_peer_issued_cid_alarm->Fire();
+
+ // Another server connection ID is available to client.
+ QuicNewConnectionIdFrame frame2;
+ frame2.connection_id = TestConnectionId(4);
+ frame2.sequence_number = 2u;
+ frame2.retire_prior_to = 1u;
+ frame2.stateless_reset_token =
+ QuicUtils::GenerateStatelessResetToken(frame2.connection_id);
+ connection_.OnNewConnectionIdFrame(frame2);
+
+ const QuicSocketAddress kSelfAddress2(QuicIpAddress::Loopback4(),
+ /*port=*/45678);
+ bool success2;
+ connection_.ValidatePath(
+ std::make_unique<TestQuicPathValidationContext>(
+ kSelfAddress2, connection_.peer_address(), writer_.get()),
+ std::make_unique<TestValidationResultDelegate>(
+ &connection_, kSelfAddress2, connection_.peer_address(), &success2));
+ // Since server does not retire any client connection ID yet, 2nd validation
+ // would fail due to lack of client connection ID.
+ EXPECT_FALSE(success2);
+}
+
+TEST_P(QuicConnectionTest, ServerConnectionIdRetiredUponPathValidationFailure) {
+ if (!connection_.support_multiple_connection_ids() ||
+ !connection_.use_connection_id_on_default_path()) {
+ return;
+ }
+
+ QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
+ PathProbeTestInit(Perspective::IS_CLIENT);
+
+ // Make sure server connection ID is available for validation.
+ QuicNewConnectionIdFrame frame;
+ frame.connection_id = TestConnectionId(2);
+ frame.sequence_number = 1u;
+ frame.retire_prior_to = 0u;
+ frame.stateless_reset_token =
+ QuicUtils::GenerateStatelessResetToken(frame.connection_id);
+ connection_.OnNewConnectionIdFrame(frame);
+
+ const QuicSocketAddress kNewSelfAddress(QuicIpAddress::Loopback4(),
+ /*port=*/34567);
+ bool success;
+ connection_.ValidatePath(
+ std::make_unique<TestQuicPathValidationContext>(
+ kNewSelfAddress, connection_.peer_address(), writer_.get()),
+ std::make_unique<TestValidationResultDelegate>(
+ &connection_, kNewSelfAddress, connection_.peer_address(), &success));
+
+ auto* path_validator = QuicConnectionPeer::path_validator(&connection_);
+ path_validator->CancelPathValidation();
+ QuicConnectionPeer::RetirePeerIssuedConnectionIdsNoLongerOnPath(&connection_);
+ EXPECT_FALSE(success);
+ const auto* alternative_path =
+ QuicConnectionPeer::GetAlternativePath(&connection_);
+ EXPECT_TRUE(alternative_path->client_connection_id.IsEmpty());
+ EXPECT_TRUE(alternative_path->server_connection_id.IsEmpty());
+ EXPECT_FALSE(alternative_path->stateless_reset_token_received);
+
+ // Client will retire server connection ID on alternative_path.
+ auto* retire_peer_issued_cid_alarm =
+ connection_.GetRetirePeerIssuedConnectionIdAlarm();
+ ASSERT_TRUE(retire_peer_issued_cid_alarm->IsSet());
+ EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/1u));
+ retire_peer_issued_cid_alarm->Fire();
+}
+
+TEST_P(QuicConnectionTest,
+ MigratePathDirectlyFailedDueToLackOfServerConnectionId) {
+ if (!connection_.support_multiple_connection_ids() ||
+ !connection_.use_connection_id_on_default_path()) {
+ return;
+ }
+ QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
+ PathProbeTestInit(Perspective::IS_CLIENT);
+ const QuicSocketAddress kSelfAddress1(QuicIpAddress::Any4(), 12345);
+ ASSERT_NE(kSelfAddress1, connection_.self_address());
+
+ TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
+ ASSERT_FALSE(connection_.MigratePath(kSelfAddress1,
+ connection_.peer_address(), &new_writer,
+ /*owns_writer=*/false));
+}
+
+TEST_P(QuicConnectionTest,
+ MigratePathDirectlyFailedDueToLackOfClientConnectionIdTheSecondTime) {
+ if (!connection_.support_multiple_connection_ids() ||
+ !connection_.use_connection_id_on_default_path()) {
+ return;
+ }
+ QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
+ PathProbeTestInit(Perspective::IS_CLIENT);
+ SetClientConnectionId(TestConnectionId(1));
+
+ // Make sure server connection ID is available for the 1st migration.
+ QuicNewConnectionIdFrame frame1;
+ frame1.connection_id = TestConnectionId(2);
+ frame1.sequence_number = 1u;
+ frame1.retire_prior_to = 0u;
+ frame1.stateless_reset_token =
+ QuicUtils::GenerateStatelessResetToken(frame1.connection_id);
+ connection_.OnNewConnectionIdFrame(frame1);
+
+ // Client will issue a new client connection ID to server.
+ QuicConnectionId new_client_connection_id;
+ EXPECT_CALL(visitor_, SendNewConnectionId(_))
+ .WillOnce(Invoke([&](const QuicNewConnectionIdFrame& frame) {
+ new_client_connection_id = frame.connection_id;
+ }));
+
+ // 1st migration is successful.
+ const QuicSocketAddress kSelfAddress1(QuicIpAddress::Any4(), 12345);
+ ASSERT_NE(kSelfAddress1, connection_.self_address());
+ TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
+ ASSERT_TRUE(connection_.MigratePath(kSelfAddress1, connection_.peer_address(),
+ &new_writer,
+ /*owns_writer=*/false));
+ QuicConnectionPeer::RetirePeerIssuedConnectionIdsNoLongerOnPath(&connection_);
+ const auto* default_path = QuicConnectionPeer::GetDefaultPath(&connection_);
+ EXPECT_EQ(default_path->client_connection_id, new_client_connection_id);
+ EXPECT_EQ(default_path->server_connection_id, frame1.connection_id);
+ EXPECT_EQ(default_path->stateless_reset_token, frame1.stateless_reset_token);
+ EXPECT_TRUE(default_path->stateless_reset_token_received);
+
+ // Client will retire server connection ID on old default_path.
+ auto* retire_peer_issued_cid_alarm =
+ connection_.GetRetirePeerIssuedConnectionIdAlarm();
+ ASSERT_TRUE(retire_peer_issued_cid_alarm->IsSet());
+ EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/0u));
+ retire_peer_issued_cid_alarm->Fire();
+
+ // Another server connection ID is available to client.
+ QuicNewConnectionIdFrame frame2;
+ frame2.connection_id = TestConnectionId(4);
+ frame2.sequence_number = 2u;
+ frame2.retire_prior_to = 1u;
+ frame2.stateless_reset_token =
+ QuicUtils::GenerateStatelessResetToken(frame2.connection_id);
+ connection_.OnNewConnectionIdFrame(frame2);
+
+ // Since server does not retire any client connection ID yet, 2nd migration
+ // would fail due to lack of client connection ID.
+ const QuicSocketAddress kSelfAddress2(QuicIpAddress::Loopback4(),
+ /*port=*/45678);
+ ASSERT_FALSE(connection_.MigratePath(kSelfAddress2,
+ connection_.peer_address(), &new_writer,
+ /*owns_writer=*/false));
+}
+
+TEST_P(QuicConnectionTest,
CloseConnectionAfterReceiveNewConnectionIdFromPeerUsingEmptyCID) {
if (!version().HasIetfQuicFrames()) {
return;
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 871e326..65dd0dd 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -2609,11 +2609,12 @@
return connection_->HasPendingPathValidation();
}
-void QuicSession::MigratePath(const QuicSocketAddress& self_address,
+bool QuicSession::MigratePath(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
QuicPacketWriter* writer,
bool owns_writer) {
- connection_->MigratePath(self_address, peer_address, writer, owns_writer);
+ return connection_->MigratePath(self_address, peer_address, writer,
+ owns_writer);
}
bool QuicSession::ValidateToken(absl::string_view token) const {
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 40f2d18..b6c1367 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -455,7 +455,7 @@
bool HasPendingPathValidation() const;
// Switch to the path described in |context| without validating the path.
- void MigratePath(const QuicSocketAddress& self_address,
+ bool MigratePath(const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
QuicPacketWriter* writer,
bool owns_writer);
diff --git a/quic/test_tools/quic_connection_peer.cc b/quic/test_tools/quic_connection_peer.cc
index 3039685..bdd9eff 100644
--- a/quic/test_tools/quic_connection_peer.cc
+++ b/quic/test_tools/quic_connection_peer.cc
@@ -490,6 +490,12 @@
}
// 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;
@@ -507,5 +513,11 @@
return &connection->alternative_path_;
}
+// static
+void QuicConnectionPeer::RetirePeerIssuedConnectionIdsNoLongerOnPath(
+ QuicConnection* connection) {
+ connection->RetirePeerIssuedConnectionIdsNoLongerOnPath();
+}
+
} // namespace test
} // namespace quic
diff --git a/quic/test_tools/quic_connection_peer.h b/quic/test_tools/quic_connection_peer.h
index 174e2c3..df2a823 100644
--- a/quic/test_tools/quic_connection_peer.h
+++ b/quic/test_tools/quic_connection_peer.h
@@ -199,12 +199,18 @@
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);
static QuicConnection::PathState* GetAlternativePath(
QuicConnection* connection);
+
+ static void RetirePeerIssuedConnectionIdsNoLongerOnPath(
+ QuicConnection* connection);
};
} // namespace test
diff --git a/quic/tools/quic_client_base.cc b/quic/tools/quic_client_base.cc
index 7077f16..66f28b7 100644
--- a/quic/tools/quic_client_base.cc
+++ b/quic/tools/quic_client_base.cc
@@ -465,6 +465,7 @@
std::unique_ptr<QuicPathValidationContext> context) override {
QUIC_LOG(WARNING) << "Fail to validate path " << *context
<< ", stop migrating.";
+ client_->session()->connection()->OnPathValidationFailureAtClient();
}
private: