Fix 2 QUIC reverse path validation related GFE_BUG.
Flush queued frames when PathValidator::retry_timer_ fires.
Cancel retransmission alarm after resetting congestion controller.
Protected by quic_reloadable_flag_quic_server_reverse_validate_new_path2.
PiperOrigin-RevId: 362972561
Change-Id: I49cd49615a1afb4f9b0f4ceb04d9c41ef2338a35
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 76bc891..30387ba 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -375,7 +375,7 @@
validate_client_addresses_(
framer_.version().HasIetfQuicFrames() && use_path_validator_ &&
count_bytes_on_alternative_path_separately_ &&
- GetQuicReloadableFlag(quic_server_reverse_validate_new_path)) {
+ GetQuicReloadableFlag(quic_server_reverse_validate_new_path2)) {
QUIC_BUG_IF_V2(quic_bug_12714_1,
!start_peer_migration_earlier_ && send_path_response_);
@@ -1677,6 +1677,7 @@
if (!validate_client_addresses_) {
return OnPathChallengeFrameInternal(frame);
}
+ QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path2, 1, 5);
{
// UpdatePacketStateAndReplyPathChallenge() may start reverse path
// validation, if so bundle the PATH_CHALLENGE together with the
@@ -4961,6 +4962,7 @@
if (!validate_client_addresses_) {
return;
}
+ QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path2, 2, 5);
if (debug_visitor_ != nullptr) {
const QuicTime now = clock_->ApproximateNow();
if (now >= stats_.handshake_completion_time) {
@@ -5005,6 +5007,7 @@
return;
}
+ QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path2, 3, 5);
if (type == NO_CHANGE) {
UpdatePeerAddress(last_packet_source_address_);
QUIC_BUG_V2(quic_bug_10511_36)
@@ -5052,14 +5055,7 @@
// congestion controller to initial state first and then change to the one
// on alternative path.
// TODO(danzh) combine these two steps into one after deprecating gQUIC.
- previous_default_path.send_algorithm =
- sent_packet_manager_.OnConnectionMigration(
- /*reset_send_algorithm=*/true);
- // OnConnectionMigration() might have marked in-flight packets to be
- // retransmitted if there is any.
- QUICHE_DCHECK(!sent_packet_manager_.HasInFlightPackets());
- // Stop detections in quiecense.
- blackhole_detector_.StopDetection();
+ previous_default_path.send_algorithm = OnPeerIpAddressChanged();
if (alternative_path_.peer_address.host() ==
current_effective_peer_address.host() &&
@@ -5290,6 +5286,7 @@
alternative_path_ = PathState(last_packet_destination_address_,
current_effective_peer_address);
} else if (!default_path_.validated) {
+ QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path2, 4, 5);
// Skip reverse path validation because either handshake hasn't
// completed or the connection is validating the default path. Using
// PATH_CHALLENGE to validate alternative client address before
@@ -5303,6 +5300,7 @@
IsHandshakeConfirmed() && !alternative_path_.validated)
<< "No validated peer address to send after handshake comfirmed.";
} else if (!IsReceivedPeerAddressValidated()) {
+ QUIC_CODE_COUNT_N(quic_server_reverse_validate_new_path2, 5, 5);
// Only override alternative path state upon receiving a PATH_CHALLENGE
// from an unvalidated peer address, and the connection isn't validating
// a recent peer migration.
@@ -6192,6 +6190,7 @@
const QuicSocketAddress& /*effective_peer_address*/,
QuicPacketWriter* writer) {
if (writer == writer_) {
+ ScopedPacketFlusher flusher(this);
{
// It's on current path, add the PATH_CHALLENGE the same way as other
// frames.
@@ -6541,10 +6540,7 @@
}
// Revert congestion control context to old state.
- sent_packet_manager_.OnConnectionMigration(true);
- QUICHE_DCHECK(!sent_packet_manager_.HasInFlightPackets());
- // Stop detections in quiecense.
- blackhole_detector_.StopDetection();
+ OnPeerIpAddressChanged();
if (alternative_path_.send_algorithm != nullptr) {
sent_packet_manager_.SetSendAlgorithm(
@@ -6565,5 +6561,22 @@
WriteIfNotBlocked();
}
+std::unique_ptr<SendAlgorithmInterface>
+QuicConnection::OnPeerIpAddressChanged() {
+ QUICHE_DCHECK(validate_client_addresses_);
+ std::unique_ptr<SendAlgorithmInterface> old_send_algorithm =
+ sent_packet_manager_.OnConnectionMigration(
+ /*reset_send_algorithm=*/true);
+ // OnConnectionMigration() should have marked in-flight packets to be
+ // retransmitted if there is any.
+ QUICHE_DCHECK(!sent_packet_manager_.HasInFlightPackets());
+ // OnConnectionMigration() may have changed the retransmission timer, so
+ // re-arm it.
+ SetRetransmissionAlarm();
+ // Stop detections in quiecense.
+ blackhole_detector_.StopDetection();
+ return old_send_algorithm;
+}
+
#undef ENDPOINT // undef for jumbo builds
} // namespace quic
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h
index ec866a6..65338d5 100644
--- a/quic/core/quic_connection.h
+++ b/quic/core/quic_connection.h
@@ -1722,6 +1722,11 @@
virtual std::unique_ptr<QuicSelfIssuedConnectionIdManager>
MakeSelfIssuedConnectionIdManager();
+ // Called on peer IP change or restoring to previous address to reset
+ // congestion window, RTT stats, retransmission timer, etc. Only used in IETF
+ // QUIC.
+ std::unique_ptr<SendAlgorithmInterface> OnPeerIpAddressChanged();
+
// Process NewConnectionIdFrame either sent from peer or synsthesized from
// preferred_address transport parameter.
bool OnNewConnectionIdFrameInner(const QuicNewConnectionIdFrame& frame);
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index d2f7499..aff4029 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -1726,6 +1726,7 @@
EXPECT_CALL(visitor_, GetHandshakeState())
.WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
QuicConnectionPeer::SetAddressValidated(&connection_);
+ connection_.OnHandshakeComplete();
// Enable 5 RTO
QuicConfig config;
@@ -1771,6 +1772,7 @@
connection_.SendStreamData3();
EXPECT_EQ(1u, writer_->packets_write_attempts());
EXPECT_TRUE(connection_.BlackholeDetectionInProgress());
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
// Process another packet with a different peer address on server side will
// start connection migration.
@@ -1792,6 +1794,7 @@
EXPECT_EQ(IPV6_TO_IPV4_CHANGE,
connection_.active_effective_peer_migration_type());
EXPECT_FALSE(connection_.BlackholeDetectionInProgress());
+ EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet());
EXPECT_EQ(2u, writer_->packets_write_attempts());
EXPECT_FALSE(writer_->path_challenge_frames().empty());
@@ -1979,6 +1982,7 @@
EXPECT_CALL(visitor_, GetHandshakeState())
.WillRepeatedly(Return(HANDSHAKE_CONFIRMED));
QuicConnectionPeer::SetAddressValidated(&connection_);
+ connection_.OnHandshakeComplete();
// Clear direct_peer_address.
QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress());
@@ -2016,14 +2020,16 @@
QuicFrames frames2;
frames2.push_back(QuicFrame(frame2_));
+ QuicPaddingFrame padding;
+ frames2.push_back(QuicFrame(padding));
ProcessFramesPacketWithAddresses(frames2, kSelfAddress, kNewPeerAddress,
ENCRYPTION_FORWARD_SECURE);
EXPECT_EQ(kNewPeerAddress, connection_.peer_address());
EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address());
EXPECT_EQ(IPV6_TO_IPV4_CHANGE,
connection_.active_effective_peer_migration_type());
- EXPECT_EQ(1u, writer_->packets_write_attempts());
- EXPECT_FALSE(writer_->path_challenge_frames().empty());
+ EXPECT_LT(0u, writer_->packets_write_attempts());
+ EXPECT_TRUE(connection_.HasPendingPathValidation());
EXPECT_NE(connection_.sent_packet_manager().GetSendAlgorithm(),
send_algorithm_);
EXPECT_EQ(kNewPeerAddress, writer_->last_write_peer_address());
@@ -2040,6 +2046,9 @@
EXPECT_EQ(IPV6_TO_IPV4_CHANGE,
connection_.active_effective_peer_migration_type());
+ SendStreamDataToPeer(1, "foo", 0, NO_FIN, nullptr);
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
+
// Advance the time so that the reverse path validation times out.
clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs));
static_cast<TestAlarmFactory::TestAlarm*>(
@@ -2051,6 +2060,7 @@
EXPECT_EQ(kPeerAddress, connection_.effective_peer_address());
EXPECT_EQ(connection_.sent_packet_manager().GetSendAlgorithm(),
send_algorithm_);
+ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet());
}
TEST_P(QuicConnectionTest, ReceivePathProbeWithNoAddressChangeAtServer) {
@@ -11780,6 +11790,40 @@
EXPECT_TRUE(connection_.HasPendingPathValidation());
}
+// Regression test for b/182571515.
+TEST_P(QuicConnectionTest, PathValidationRetry) {
+ if (!VersionHasIetfQuicFrames(connection_.version().transport_version) ||
+ !connection_.use_path_validator()) {
+ return;
+ }
+ PathProbeTestInit(Perspective::IS_CLIENT);
+
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(2u)
+ .WillRepeatedly(Invoke([&]() {
+ EXPECT_EQ(1u, writer_->path_challenge_frames().size());
+ 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));
+ EXPECT_EQ(1u, writer_->packets_write_attempts());
+ EXPECT_TRUE(connection_.HasPendingPathValidation());
+
+ // Retry after time out.
+ clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(3 * kInitialRttMs));
+ static_cast<test::MockRandom*>(helper_->GetRandomGenerator())->ChangeValue();
+ static_cast<TestAlarmFactory::TestAlarm*>(
+ QuicPathValidatorPeer::retry_timer(
+ QuicConnectionPeer::path_validator(&connection_)))
+ ->Fire();
+ EXPECT_EQ(2u, writer_->packets_write_attempts());
+}
+
TEST_P(QuicConnectionTest, PathValidationReceivesStatelessReset) {
if (!VersionHasIetfQuicFrames(connection_.version().transport_version) ||
!connection_.use_path_validator()) {
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 1894b24..4dd8816 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -55,7 +55,7 @@
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_path_response2, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_timestamps, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_send_tls_crypto_error_code, true)
-QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_server_reverse_validate_new_path, false)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_server_reverse_validate_new_path2, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_single_ack_in_packet2, false)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_start_peer_migration_earlier, true)
QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_stateless_reset, true)
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc
index 8b8bfce..9b476b1 100644
--- a/quic/core/quic_packet_creator.cc
+++ b/quic/core/quic_packet_creator.cc
@@ -2055,6 +2055,12 @@
void QuicPacketCreator::AddPathChallengeFrame(
const QuicPathFrameBuffer& payload) {
+ // TODO(danzh) Unify similar checks at several entry points into one in
+ // AddFrame(). Sort out test helper functions and peer class that don't
+ // enforce this check.
+ QUIC_BUG_IF_V2(quic_bug_10752_39, !flusher_attached_)
+ << "Packet flusher is not attached when "
+ "generator tries to write stream data.";
// Write a PATH_CHALLENGE frame, which has a random 8-byte payload.
auto path_challenge_frame = new QuicPathChallengeFrame(0, payload);
QuicFrame frame(path_challenge_frame);