Use new connection ID in connection migration on the server 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: 371119297
Change-Id: I664a8cfcc348d4528b7a5a1ad0ed1b0bc9ca1a02
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 39150b8..38117a4 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -1064,6 +1064,13 @@
return true;
}
+ if (connection_migration_use_new_cid_ &&
+ perspective_ == Perspective::IS_SERVER &&
+ self_issued_cid_manager_ != nullptr &&
+ self_issued_cid_manager_->IsConnectionIdInUse(server_connection_id)) {
+ return true;
+ }
+
return false;
}
@@ -1089,10 +1096,10 @@
framer_.set_drop_incoming_retry_packets(true);
}
- QuicConnectionId server_connection_id =
- GetServerConnectionIdAsRecipient(header, perspective_);
if (!ValidateServerConnectionId(header)) {
++stats_.packets_dropped;
+ QuicConnectionId server_connection_id =
+ GetServerConnectionIdAsRecipient(header, perspective_);
QUIC_DLOG(INFO) << ENDPOINT
<< "Ignoring packet from unexpected server connection ID "
<< server_connection_id << " instead of "
@@ -1131,6 +1138,13 @@
return true;
}
+ if (connection_migration_use_new_cid_ &&
+ perspective_ == Perspective::IS_CLIENT &&
+ self_issued_cid_manager_ != nullptr &&
+ self_issued_cid_manager_->IsConnectionIdInUse(client_connection_id)) {
+ return true;
+ }
+
++stats_.packets_dropped;
QUIC_DLOG(INFO) << ENDPOINT
<< "Ignoring packet from unexpected client connection ID "
@@ -1297,6 +1311,35 @@
default_path_.peer_address,
GetEffectivePeerAddressFromCurrentPacket());
+ if (connection_migration_use_new_cid_) {
+ auto effective_peer_address = GetEffectivePeerAddressFromCurrentPacket();
+ // Since server does not send new connection ID to client before handshake
+ // completion and source connection ID is omitted in short header packet,
+ // the server_connection_id on PathState on the server side does not
+ // affect the packets server writes after handshake completion. On the
+ // other hand, it is still desirable to have the "correct" server
+ // connection ID set on path.
+ // 1) If client uses 1 unique server connection ID per path and the packet
+ // is received from an existing path, then
+ // last_packet_destination_connection_id_ will always be the same as the
+ // server connection ID on path. Server side will maintain the 1-to-1
+ // mapping from server connection ID to path.
+ // 2) If client uses multiple server connection IDs on the same path,
+ // compared to the server_connection_id on path,
+ // last_packet_destination_connection_id_ has the advantage that it is
+ // still present in the session map since the packet can be routed here
+ // regardless of packet reordering.
+ if (IsDefaultPath(last_packet_destination_address_,
+ effective_peer_address)) {
+ default_path_.server_connection_id =
+ last_packet_destination_connection_id_;
+ } else if (IsAlternativePath(last_packet_destination_address_,
+ effective_peer_address)) {
+ alternative_path_.server_connection_id =
+ last_packet_destination_connection_id_;
+ }
+ }
+
QUIC_DLOG_IF(INFO, current_effective_peer_migration_type_ != NO_CHANGE)
<< ENDPOINT << "Effective peer's ip:port changed from "
<< default_path_.peer_address.ToString() << " to "
@@ -1742,16 +1785,20 @@
}
std::unique_ptr<QuicPacketCreator::ScopedPeerAddressContext> context;
+ const QuicSocketAddress current_effective_peer_address =
+ GetEffectivePeerAddressFromCurrentPacket();
if (group_path_response_and_challenge_sending_closer_) {
+ QuicConnectionId client_cid, server_cid;
+ FindOnPathConnectionIds(last_packet_destination_address_,
+ current_effective_peer_address, &client_cid,
+ &server_cid);
context = std::make_unique<QuicPacketCreator::ScopedPeerAddressContext>(
- &packet_creator_, last_packet_source_address_,
- /*update_connection_id=*/false);
+ &packet_creator_, last_packet_source_address_, client_cid, server_cid,
+ connection_migration_use_new_cid_);
}
if (should_proactively_validate_peer_address_on_path_challenge_) {
QUIC_RELOADABLE_FLAG_COUNT(
quic_group_path_response_and_challenge_sending_closer);
- QuicSocketAddress current_effective_peer_address =
- GetEffectivePeerAddressFromCurrentPacket();
// Conditions to proactively validate peer address:
// The perspective is server
// The PATH_CHALLENGE is received on an unvalidated alternative path.
@@ -1780,7 +1827,8 @@
// the current incoming packet, even if it's not the default path or the
// alternative path.
const bool success =
- SendPathResponse(frame.data_buffer, last_packet_source_address_);
+ SendPathResponse(frame.data_buffer, last_packet_source_address_,
+ current_effective_peer_address);
if (GetQuicReloadableFlag(quic_drop_unsent_path_response)) {
QUIC_RELOADABLE_FLAG_COUNT(quic_drop_unsent_path_response);
}
@@ -2904,8 +2952,12 @@
QUIC_RELOADABLE_FLAG_COUNT_N(quic_send_path_response2, 4, 5);
const PendingPathChallenge& pending_path_challenge =
pending_path_challenge_payloads_.front();
+ // Note connection_migration_use_cid_ will depends on
+ // quic_drop_unsent_path_response flag eventually, and hence the empty
+ // effective_peer_address here will not be used.
if (!SendPathResponse(pending_path_challenge.received_path_challenge,
- pending_path_challenge.peer_address)) {
+ pending_path_challenge.peer_address,
+ /*effective_peer_address=*/QuicSocketAddress())) {
break;
}
pending_path_challenge_payloads_.pop_front();
@@ -2979,13 +3031,13 @@
packet_creator_.SetServerConnectionId(ServerConnectionId());
}
-void QuicConnection::FindMatchingClientConnectionIdOrToken(
+void QuicConnection::FindMatchingOrNewClientConnectionIdOrToken(
const PathState& default_path,
const PathState& alternative_path,
const QuicConnectionId& server_connection_id,
QuicConnectionId* client_connection_id,
bool* stateless_reset_token_received,
- StatelessResetToken* stateless_reset_token) const {
+ StatelessResetToken* stateless_reset_token) {
if (!use_connection_id_on_default_path_) {
return;
}
@@ -3005,7 +3057,44 @@
*stateless_reset_token = alternative_path.stateless_reset_token;
return;
}
- QUIC_BUG(quic_bug_46004) << "Cannot find matching connection ID.";
+ if (!connection_migration_use_new_cid_) {
+ QUIC_BUG(quic_bug_46004) << "Cannot find matching connection ID.";
+ return;
+ }
+ auto* connection_id_data =
+ peer_issued_cid_manager_->ConsumeOneUnusedConnectionId();
+ if (connection_id_data == nullptr) {
+ return;
+ }
+ *client_connection_id = connection_id_data->connection_id;
+ *stateless_reset_token = connection_id_data->stateless_reset_token;
+ *stateless_reset_token_received = true;
+}
+
+bool QuicConnection::FindOnPathConnectionIds(
+ const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ QuicConnectionId* client_connection_id,
+ QuicConnectionId* server_connection_id) const {
+ if (IsDefaultPath(self_address, peer_address)) {
+ *client_connection_id = default_path_.client_connection_id,
+ *server_connection_id = default_path_.server_connection_id;
+ return true;
+ }
+ if (IsAlternativePath(self_address, peer_address)) {
+ *client_connection_id = alternative_path_.client_connection_id,
+ *server_connection_id = alternative_path_.server_connection_id;
+ return true;
+ }
+ return false;
+}
+
+void QuicConnection::SetDefaultPathState(PathState new_path_state) {
+ default_path_ = std::move(new_path_state);
+ if (connection_migration_use_new_cid_) {
+ packet_creator_.SetClientConnectionId(default_path_.client_connection_id);
+ packet_creator_.SetServerConnectionId(default_path_.server_connection_id);
+ }
}
bool QuicConnection::ProcessValidatedPacket(const QuicPacketHeader& header) {
@@ -4476,7 +4565,8 @@
const std::string& details) {
// Always use the current path to send CONNECTION_CLOSE.
QuicPacketCreator::ScopedPeerAddressContext context(
- &packet_creator_, peer_address(), /*update_connection_id=*/false);
+ &packet_creator_, peer_address(), default_path_.client_connection_id,
+ default_path_.server_connection_id, connection_migration_use_new_cid_);
if (!SupportsMultiplePacketNumberSpaces()) {
QUIC_DLOG(INFO) << ENDPOINT << "Sending connection close packet.";
if (!use_encryption_level_context_) {
@@ -5249,19 +5339,19 @@
// Update the default path.
if (IsAlternativePath(last_packet_destination_address_,
current_effective_peer_address)) {
- default_path_ = std::move(alternative_path_);
+ SetDefaultPathState(std::move(alternative_path_));
} else {
QuicConnectionId client_connection_id;
bool stateless_reset_token_received = false;
StatelessResetToken stateless_reset_token;
- FindMatchingClientConnectionIdOrToken(
+ FindMatchingOrNewClientConnectionIdOrToken(
previous_default_path, alternative_path_,
last_packet_destination_connection_id_, &client_connection_id,
&stateless_reset_token_received, &stateless_reset_token);
- default_path_ = PathState(
+ SetDefaultPathState(PathState(
last_packet_destination_address_, current_effective_peer_address,
client_connection_id, last_packet_destination_connection_id_,
- stateless_reset_token_received, stateless_reset_token);
+ stateless_reset_token_received, stateless_reset_token));
// The path is considered validated if its peer IP address matches any
// validated path's peer IP address.
default_path_.validated =
@@ -5468,16 +5558,16 @@
<< current_effective_peer_address << ", self address "
<< last_packet_destination_address_;
if (!validate_client_addresses_) {
- QuicConnectionId client_connection_id;
+ QuicConnectionId client_cid;
bool stateless_reset_token_received = false;
StatelessResetToken stateless_reset_token;
- FindMatchingClientConnectionIdOrToken(
+ FindMatchingOrNewClientConnectionIdOrToken(
default_path_, alternative_path_,
- last_packet_destination_connection_id_, &client_connection_id,
+ last_packet_destination_connection_id_, &client_cid,
&stateless_reset_token_received, &stateless_reset_token);
alternative_path_ = PathState(
last_packet_destination_address_, current_effective_peer_address,
- client_connection_id, last_packet_destination_connection_id_,
+ client_cid, last_packet_destination_connection_id_,
stateless_reset_token_received, stateless_reset_token);
} else if (!default_path_.validated) {
QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path3, 4, 6);
@@ -5498,7 +5588,7 @@
QuicConnectionId client_connection_id;
bool stateless_reset_token_received;
StatelessResetToken stateless_reset_token;
- FindMatchingClientConnectionIdOrToken(
+ FindMatchingOrNewClientConnectionIdOrToken(
default_path_, alternative_path_,
last_packet_destination_connection_id_, &client_connection_id,
&stateless_reset_token_received, &stateless_reset_token);
@@ -5521,7 +5611,7 @@
<< current_effective_peer_address;
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()));
@@ -6438,8 +6528,34 @@
const QuicPathFrameBuffer& data_buffer,
const QuicSocketAddress& self_address,
const QuicSocketAddress& peer_address,
- const QuicSocketAddress& /*effective_peer_address*/,
+ const QuicSocketAddress& effective_peer_address,
QuicPacketWriter* writer) {
+ if (connection_migration_use_new_cid_) {
+ {
+ QuicConnectionId client_cid, server_cid;
+ FindOnPathConnectionIds(self_address, effective_peer_address, &client_cid,
+ &server_cid);
+ QuicPacketCreator::ScopedPeerAddressContext context(
+ &packet_creator_, peer_address, client_cid, server_cid,
+ connection_migration_use_new_cid_);
+ if (writer == writer_) {
+ ScopedPacketFlusher flusher(this);
+ // It's on current path, add the PATH_CHALLENGE the same way as other
+ // frames. This may cause connection to be closed.
+ packet_creator_.AddPathChallengeFrame(data_buffer);
+ } else {
+ std::unique_ptr<SerializedPacket> probing_packet =
+ packet_creator_.SerializePathChallengeConnectivityProbingPacket(
+ data_buffer);
+ QUICHE_DCHECK_EQ(IsRetransmittable(*probing_packet),
+ NO_RETRANSMITTABLE_DATA);
+ QUICHE_DCHECK_EQ(self_address, alternative_path_.self_address);
+ WritePacketUsingWriter(std::move(probing_packet), writer, self_address,
+ peer_address, /*measure_rtt=*/false);
+ }
+ }
+ return connected_;
+ }
if (writer == writer_) {
ScopedPacketFlusher flusher(this);
{
@@ -6531,13 +6647,21 @@
std::move(result_delegate));
}
-bool QuicConnection::SendPathResponse(const QuicPathFrameBuffer& data_buffer,
- QuicSocketAddress peer_address_to_send) {
+bool QuicConnection::SendPathResponse(
+ const QuicPathFrameBuffer& data_buffer,
+ const QuicSocketAddress& peer_address_to_send,
+ const QuicSocketAddress& effective_peer_address) {
+ QuicConnectionId client_cid, server_cid;
+ if (connection_migration_use_new_cid_) {
+ FindOnPathConnectionIds(last_packet_destination_address_,
+ effective_peer_address, &client_cid, &server_cid);
+ }
// Send PATH_RESPONSE using the provided peer address. If the creator has been
// using a different peer address, it will flush before and after serializing
// the current PATH_RESPONSE.
QuicPacketCreator::ScopedPeerAddressContext context(
- &packet_creator_, peer_address_to_send, /*update_connection_id=*/false);
+ &packet_creator_, peer_address_to_send, client_cid, server_cid,
+ connection_migration_use_new_cid_);
QUIC_DVLOG(1) << ENDPOINT << "Send PATH_RESPONSE to " << peer_address_to_send;
if (default_path_.self_address == last_packet_destination_address_) {
// The PATH_CHALLENGE is received on the default socket. Respond on the same
@@ -6656,8 +6780,11 @@
peer_issued_cid_manager_->MaybeRetireUnusedConnectionIds(
{default_path_.server_connection_id,
alternative_path_.server_connection_id});
+ } else {
+ peer_issued_cid_manager_->MaybeRetireUnusedConnectionIds(
+ {default_path_.client_connection_id,
+ alternative_path_.client_connection_id});
}
- // TODO(haoyuewang) Do the same on the server side.
}
bool QuicConnection::MigratePath(const QuicSocketAddress& self_address,
@@ -6941,7 +7068,7 @@
}
UpdatePeerAddress(original_direct_peer_address);
- default_path_ = std::move(alternative_path_);
+ SetDefaultPathState(std::move(alternative_path_));
active_effective_peer_migration_type_ = NO_CHANGE;
++stats_.num_invalid_peer_migration;
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index 588504b..f752a55 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -1506,14 +1506,27 @@
const QuicConnectionId& new_server_connection_id);
// Given the server_connection_id find if there is already a corresponding
- // client connection ID used on default/alternative path.
- void FindMatchingClientConnectionIdOrToken(
+ // client connection ID used on default/alternative path. If not, find if
+ // there is an unused connection ID.
+ void FindMatchingOrNewClientConnectionIdOrToken(
const PathState& default_path,
const PathState& alternative_path,
const QuicConnectionId& server_connection_id,
QuicConnectionId* client_connection_id,
bool* stateless_reset_token_received,
- StatelessResetToken* stateless_reset_token) const;
+ StatelessResetToken* stateless_reset_token);
+
+ // Returns true and sets connection IDs if (self_address, peer_address)
+ // corresponds to either the default path or alternative path. Returns false
+ // otherwise.
+ bool FindOnPathConnectionIds(const QuicSocketAddress& self_address,
+ const QuicSocketAddress& peer_address,
+ QuicConnectionId* client_connection_id,
+ QuicConnectionId* server_connection_id) const;
+
+ // Set default_path_ to the new_path_state and update the connection IDs in
+ // packet creator accordingly.
+ void SetDefaultPathState(PathState new_path_state);
// Returns true if header contains valid server connection ID.
bool ValidateServerConnectionId(const QuicPacketHeader& header) const;
@@ -1761,7 +1774,8 @@
// Send PATH_RESPONSE to the given peer address.
bool SendPathResponse(const QuicPathFrameBuffer& data_buffer,
- QuicSocketAddress peer_address_to_send);
+ const QuicSocketAddress& peer_address_to_send,
+ const QuicSocketAddress& effective_peer_address);
// Update both connection's and packet creator's peer address.
void UpdatePeerAddress(QuicSocketAddress peer_address);
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 741f2da..d806835 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -1875,6 +1875,87 @@
EXPECT_EQ(1u, connection_.GetStats().num_validated_peer_migration);
}
+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()) {
+ 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);
+ QuicConnectionId server_cid1;
+ SetClientConnectionId(client_cid0);
+ connection_.CreateConnectionIdManager();
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ // Prevent packets from being coalesced.
+ 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();
+
+ // Clear direct_peer_address.
+ QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
+ // Clear effective_peer_address, it is the same as direct_peer_address for
+ // this test.
+ QuicConnectionPeer::SetEffectivePeerAddress(&connection_,
+ QuicSocketAddress());
+ EXPECT_FALSE(connection_.effective_peer_address().IsInitialized());
+
+ const QuicSocketAddress kNewPeerAddress =
+ QuicSocketAddress(QuicIpAddress::Loopback4(), /*port=*/23456);
+ EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(2);
+ QuicFrames frames;
+ frames.push_back(QuicFrame(frame1_));
+ ProcessFramesPacketWithAddresses(frames, kSelfAddress, kPeerAddress,
+ ENCRYPTION_FORWARD_SECURE);
+ EXPECT_EQ(kPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
+
+ // Send some data to make connection has packets in flight.
+ connection_.SendStreamData3();
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+
+ // Process another packet with a different peer address on server side will
+ // start connection migration.
+ peer_creator_.SetServerConnectionId(server_cid1);
+ EXPECT_CALL(visitor_, OnConnectionMigration(IPV6_TO_IPV4_CHANGE)).Times(1);
+ // Do not propagate OnCanWrite() to session notifier.
+ EXPECT_CALL(visitor_, OnCanWrite()).Times(AtLeast(1u));
+
+ QuicFrames frames2;
+ frames2.push_back(QuicFrame(frame2_));
+ ProcessFramesPacketWithAddresses(frames2, kSelfAddress, kNewPeerAddress,
+ ENCRYPTION_FORWARD_SECURE);
+ EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
+ EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address());
+
+ // Writing path response & reverse path challenge is blocked due to missing
+ // client connection ID, i.e., packets_write_attempts is unchanged.
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+
+ // Receives new client CID from client would unblock write.
+ QuicNewConnectionIdFrame new_cid_frame;
+ new_cid_frame.connection_id = client_cid1;
+ new_cid_frame.sequence_number = 1u;
+ new_cid_frame.retire_prior_to = 0u;
+ connection_.OnNewConnectionIdFrame(new_cid_frame);
+ connection_.SendStreamData3();
+
+ EXPECT_EQ(2u, writer_->packets_write_attempts());
+}
+
TEST_P(QuicConnectionTest, EffectivePeerAddressChangeAtServer) {
set_perspective(Perspective::IS_SERVER);
QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
@@ -1992,11 +2073,16 @@
TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) {
set_perspective(Perspective::IS_SERVER);
- if (!connection_.validate_client_address()) {
+ if (!connection_.validate_client_address() ||
+ !connection_.use_connection_id_on_default_path() ||
+ !connection_.support_multiple_connection_ids()) {
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);
// Prevent packets from being coalesced.
EXPECT_CALL(visitor_, GetHandshakeState())
@@ -2004,6 +2090,26 @@
QuicConnectionPeer::SetAddressValidated(&connection_);
connection_.OnHandshakeComplete();
+ QuicConnectionId client_cid0 = connection_.client_connection_id();
+ QuicConnectionId client_cid1 = TestConnectionId(2);
+ QuicConnectionId server_cid0 = connection_.connection_id();
+ QuicConnectionId server_cid1;
+ // Sends new server CID to client.
+ EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_))
+ .WillOnce(
+ Invoke([&](const QuicConnectionId& cid) { server_cid1 = cid; }));
+ EXPECT_CALL(visitor_, SendNewConnectionId(_));
+ connection_.MaybeSendConnectionIdToClient();
+ // Receives new client CID from client.
+ QuicNewConnectionIdFrame new_cid_frame;
+ new_cid_frame.connection_id = client_cid1;
+ new_cid_frame.sequence_number = 1u;
+ new_cid_frame.retire_prior_to = 0u;
+ connection_.OnNewConnectionIdFrame(new_cid_frame);
+ auto* packet_creator = QuicConnectionPeer::GetPacketCreator(&connection_);
+ ASSERT_EQ(packet_creator->GetDestinationConnectionId(), client_cid0);
+ ASSERT_EQ(packet_creator->GetSourceConnectionId(), server_cid0);
+
// Clear direct_peer_address.
QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
// Clear effective_peer_address, it is the same as direct_peer_address for
@@ -2042,6 +2148,7 @@
frames2.push_back(QuicFrame(frame2_));
QuicPaddingFrame padding;
frames2.push_back(QuicFrame(padding));
+ peer_creator_.SetServerConnectionId(server_cid1);
ProcessFramesPacketWithAddresses(frames2, kSelfAddress, kNewPeerAddress,
ENCRYPTION_FORWARD_SECURE);
EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
@@ -2055,6 +2162,15 @@
EXPECT_EQ(kNewPeerAddress, writer_->last_write_peer_address());
EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address());
+ const auto* default_path = QuicConnectionPeer::GetDefaultPath(&connection_);
+ const auto* alternative_path =
+ QuicConnectionPeer::GetAlternativePath(&connection_);
+ EXPECT_EQ(default_path->client_connection_id, client_cid1);
+ EXPECT_EQ(default_path->server_connection_id, server_cid1);
+ EXPECT_EQ(alternative_path->client_connection_id, client_cid0);
+ EXPECT_EQ(alternative_path->server_connection_id, server_cid0);
+ EXPECT_EQ(packet_creator->GetDestinationConnectionId(), client_cid1);
+ EXPECT_EQ(packet_creator->GetSourceConnectionId(), server_cid1);
for (size_t i = 0; i < QuicPathValidator::kMaxRetryTimes; ++i) {
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs));
@@ -2081,6 +2197,19 @@
EXPECT_EQ(connection_.sent_packet_manager().GetSendAlgorithm(),
send_algorithm_);
EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+
+ // Verify that default_path_ is reverted and alternative_path_ is cleared.
+ EXPECT_EQ(default_path->client_connection_id, client_cid0);
+ EXPECT_EQ(default_path->server_connection_id, server_cid0);
+ EXPECT_TRUE(alternative_path->server_connection_id.IsEmpty());
+ EXPECT_FALSE(alternative_path->stateless_reset_token_received);
+ 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();
+ EXPECT_EQ(packet_creator->GetDestinationConnectionId(), client_cid0);
+ EXPECT_EQ(packet_creator->GetSourceConnectionId(), server_cid0);
}
TEST_P(QuicConnectionTest, ReceivePathProbeWithNoAddressChangeAtServer) {
@@ -13760,11 +13889,37 @@
TEST_P(QuicConnectionTest, PathChallengeBeforePeerIpAddressChangeAtServer) {
set_perspective(Perspective::IS_SERVER);
- if (!connection_.validate_client_address()) {
+ if (!connection_.validate_client_address() ||
+ !connection_.use_connection_id_on_default_path() ||
+ !connection_.support_multiple_connection_ids()) {
return;
}
PathProbeTestInit(Perspective::IS_SERVER);
+ QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
+ SetClientConnectionId(TestConnectionId(1));
+ connection_.CreateConnectionIdManager();
+ QuicConnectionId server_cid0 = connection_.connection_id();
+ QuicConnectionId client_cid0 = connection_.client_connection_id();
+ QuicConnectionId client_cid1 = TestConnectionId(2);
+ QuicConnectionId server_cid1;
+ // Sends new server CID to client.
+ EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_))
+ .WillOnce(
+ Invoke([&](const QuicConnectionId& cid) { server_cid1 = cid; }));
+ EXPECT_CALL(visitor_, SendNewConnectionId(_));
+ connection_.MaybeSendConnectionIdToClient();
+ // Receives new client CID from client.
+ QuicNewConnectionIdFrame new_cid_frame;
+ new_cid_frame.connection_id = client_cid1;
+ new_cid_frame.sequence_number = 1u;
+ new_cid_frame.retire_prior_to = 0u;
+ connection_.OnNewConnectionIdFrame(new_cid_frame);
+ auto* packet_creator = QuicConnectionPeer::GetPacketCreator(&connection_);
+ ASSERT_EQ(packet_creator->GetDestinationConnectionId(), client_cid0);
+ ASSERT_EQ(packet_creator->GetSourceConnectionId(), server_cid0);
+
+ peer_creator_.SetServerConnectionId(server_cid1);
const QuicSocketAddress kNewPeerAddress =
QuicSocketAddress(QuicIpAddress::Loopback4(), /*port=*/23456);
QuicPathFrameBuffer path_challenge_payload{0, 1, 2, 3, 4, 5, 6, 7};
@@ -13788,6 +13943,15 @@
EXPECT_EQ(kPeerAddress, connection_.peer_address());
EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
EXPECT_TRUE(connection_.HasPendingPathValidation());
+ const auto* default_path = QuicConnectionPeer::GetDefaultPath(&connection_);
+ const auto* alternative_path =
+ QuicConnectionPeer::GetAlternativePath(&connection_);
+ EXPECT_EQ(default_path->client_connection_id, client_cid0);
+ EXPECT_EQ(default_path->server_connection_id, server_cid0);
+ EXPECT_EQ(alternative_path->client_connection_id, client_cid1);
+ EXPECT_EQ(alternative_path->server_connection_id, server_cid1);
+ EXPECT_EQ(packet_creator->GetDestinationConnectionId(), client_cid0);
+ EXPECT_EQ(packet_creator->GetSourceConnectionId(), server_cid0);
// Process another packet with a different peer address on server side will
// start connection migration.
@@ -13824,6 +13988,14 @@
EXPECT_CALL(*send_algorithm_, InRecovery()).Times(AnyNumber());
EXPECT_CALL(*send_algorithm_, PopulateConnectionStats(_)).Times(AnyNumber());
connection_.SetSendAlgorithm(send_algorithm_);
+ EXPECT_EQ(default_path->client_connection_id, client_cid1);
+ EXPECT_EQ(default_path->server_connection_id, server_cid1);
+ // The previous default path is kept as alternative path before reverse path
+ // validation finishes.
+ EXPECT_EQ(alternative_path->client_connection_id, client_cid0);
+ EXPECT_EQ(alternative_path->server_connection_id, server_cid0);
+ EXPECT_EQ(packet_creator->GetDestinationConnectionId(), client_cid1);
+ EXPECT_EQ(packet_creator->GetSourceConnectionId(), server_cid1);
EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address());
@@ -13847,6 +14019,15 @@
ProcessFramesPacketWithAddresses(frames3, kSelfAddress, kNewPeerAddress,
ENCRYPTION_FORWARD_SECURE);
EXPECT_EQ(NO_CHANGE, connection_.active_effective_peer_migration_type());
+ // Verify that alternative_path_ is cleared and the peer CID is retired.
+ EXPECT_TRUE(alternative_path->client_connection_id.IsEmpty());
+ EXPECT_TRUE(alternative_path->server_connection_id.IsEmpty());
+ EXPECT_FALSE(alternative_path->stateless_reset_token_received);
+ 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();
// Verify the anti-amplification limit is lifted by sending a packet larger
// than the anti-amplification limit.
@@ -13858,12 +14039,28 @@
TEST_P(QuicConnectionTest,
PathValidationSucceedsBeforePeerIpAddressChangeAtServer) {
set_perspective(Perspective::IS_SERVER);
- if (!connection_.validate_client_address()) {
+ if (!connection_.validate_client_address() ||
+ !connection_.use_connection_id_on_default_path() ||
+ !connection_.support_multiple_connection_ids()) {
return;
}
PathProbeTestInit(Perspective::IS_SERVER);
+ QuicConnectionPeer::EnableConnectionMigrationUseNewCID(&connection_);
+ connection_.CreateConnectionIdManager();
+
+ QuicConnectionId server_cid0 = connection_.connection_id();
+ QuicConnectionId server_cid1;
+ // Sends new server CID to client.
+ EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_))
+ .WillOnce(
+ Invoke([&](const QuicConnectionId& cid) { server_cid1 = cid; }));
+ EXPECT_CALL(visitor_, SendNewConnectionId(_));
+ connection_.MaybeSendConnectionIdToClient();
+ auto* packet_creator = QuicConnectionPeer::GetPacketCreator(&connection_);
+ ASSERT_EQ(packet_creator->GetSourceConnectionId(), server_cid0);
// Receive probing packet with new peer address.
+ peer_creator_.SetServerConnectionId(server_cid1);
const QuicSocketAddress kNewPeerAddress(QuicIpAddress::Loopback4(),
/*port=*/23456);
QuicPathFrameBuffer payload;
@@ -13888,6 +14085,12 @@
ProcessFramesPacketWithAddresses(frames1, kSelfAddress, kNewPeerAddress,
ENCRYPTION_FORWARD_SECURE);
EXPECT_TRUE(connection_.HasPendingPathValidation());
+ const auto* default_path = QuicConnectionPeer::GetDefaultPath(&connection_);
+ const auto* alternative_path =
+ QuicConnectionPeer::GetAlternativePath(&connection_);
+ EXPECT_EQ(default_path->server_connection_id, server_cid0);
+ EXPECT_EQ(alternative_path->server_connection_id, server_cid1);
+ EXPECT_EQ(packet_creator->GetSourceConnectionId(), server_cid0);
// Receive PATH_RESPONSE should mark the new peer address validated.
QuicFrames frames3;
@@ -13925,6 +14128,12 @@
EXPECT_NE(connection_.sent_packet_manager().GetSendAlgorithm(),
send_algorithm_);
+ EXPECT_EQ(default_path->server_connection_id, server_cid1);
+ EXPECT_EQ(packet_creator->GetSourceConnectionId(), server_cid1);
+ // Verify that alternative_path_ is cleared.
+ EXPECT_TRUE(alternative_path->server_connection_id.IsEmpty());
+ EXPECT_FALSE(alternative_path->stateless_reset_token_received);
+
// Switch to use the mock send algorithm.
send_algorithm_ = new StrictMock<MockSendAlgorithm>();
EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true));
@@ -14045,19 +14254,25 @@
SetClientConnectionId(TestConnectionId(1));
// Make sure server connection ID is available for the 1st validation.
+ QuicConnectionId server_cid0 = connection_.connection_id();
+ QuicConnectionId server_cid1 = TestConnectionId(2);
+ QuicConnectionId server_cid2 = TestConnectionId(4);
+ QuicConnectionId client_cid1;
QuicNewConnectionIdFrame frame1;
- frame1.connection_id = TestConnectionId(2);
+ frame1.connection_id = server_cid1;
frame1.sequence_number = 1u;
frame1.retire_prior_to = 0u;
frame1.stateless_reset_token =
QuicUtils::GenerateStatelessResetToken(frame1.connection_id);
connection_.OnNewConnectionIdFrame(frame1);
+ const auto* packet_creator =
+ QuicConnectionPeer::GetPacketCreator(&connection_);
+ ASSERT_EQ(packet_creator->GetDestinationConnectionId(), server_cid0);
// 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;
+ client_cid1 = frame.connection_id;
}));
const QuicSocketAddress kSelfAddress1(QuicIpAddress::Any4(), 12345);
@@ -14075,8 +14290,8 @@
&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->client_connection_id, client_cid1);
+ EXPECT_EQ(default_path->server_connection_id, server_cid1);
EXPECT_EQ(default_path->stateless_reset_token, frame1.stateless_reset_token);
EXPECT_TRUE(default_path->stateless_reset_token_received);
const auto* alternative_path =
@@ -14084,6 +14299,7 @@
EXPECT_TRUE(alternative_path->client_connection_id.IsEmpty());
EXPECT_TRUE(alternative_path->server_connection_id.IsEmpty());
EXPECT_FALSE(alternative_path->stateless_reset_token_received);
+ ASSERT_EQ(packet_creator->GetDestinationConnectionId(), server_cid1);
// Client will retire server connection ID on old default_path.
auto* retire_peer_issued_cid_alarm =
@@ -14094,7 +14310,7 @@
// Another server connection ID is available to client.
QuicNewConnectionIdFrame frame2;
- frame2.connection_id = TestConnectionId(4);
+ frame2.connection_id = server_cid2;
frame2.sequence_number = 2u;
frame2.retire_prior_to = 1u;
frame2.stateless_reset_token =
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc
index 3d3eb67..6674ef4 100644
--- a/quic/core/quic_packet_creator.cc
+++ b/quic/core/quic_packet_creator.cc
@@ -2124,10 +2124,12 @@
"initialized.";
creator_->SetDefaultPeerAddress(address);
if (update_connection_id_) {
- QUICHE_DCHECK(address != old_peer_address_ ||
- ((client_connection_id == old_client_connection_id_) &&
- (server_connection_id == old_server_connection_id_)))
- << ENDPOINT2;
+ // Flush current packet if connection ID changes.
+ if (address == old_peer_address_ &&
+ ((client_connection_id != old_client_connection_id_) ||
+ (server_connection_id != old_server_connection_id_))) {
+ creator_->FlushCurrentPacket();
+ }
creator_->SetClientConnectionId(client_connection_id);
creator_->SetServerConnectionId(server_connection_id);
}