Send PATH_RESPONSE using the alternative socket if the PATH_CHALLENGE is received on that socket.
behind existing flags --gfe2_reloadable_flag_quic_send_path_response and --gfe2_reloadable_flag_quic_start_peer_migration_earlier.
PiperOrigin-RevId: 351603900
Change-Id: I00b43457817c8bba559fec46217495e7d609e638
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 2b994eb..568c891 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -1297,6 +1297,10 @@
// Test that server session will send a connectivity probe in response to a
// connectivity probe on the same path.
TEST_P(QuicSpdySessionTestServer, ServerReplyToConnecitivityProbe) {
+ if (VersionHasIetfQuicFrames(transport_version()) &&
+ connection_->send_path_response()) {
+ return;
+ }
connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
QuicSocketAddress old_peer_address =
QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort);
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc
index 7b5d984..b53e90d 100644
--- a/quic/core/quic_connection.cc
+++ b/quic/core/quic_connection.cc
@@ -5535,7 +5535,37 @@
QuicPacketCreator::ScopedPeerAddressContext context(&packet_creator_,
peer_address_to_send);
QUIC_DVLOG(1) << ENDPOINT << "Send PATH_RESPONSE to " << peer_address_to_send;
- return packet_creator_.AddPathResponseFrame(data_buffer);
+ if (self_address_ == last_packet_destination_address_) {
+ // The PATH_CHALLENGE is received on the default socket. Respond on the same
+ // socket.
+ return packet_creator_.AddPathResponseFrame(data_buffer);
+ }
+
+ DCHECK_EQ(Perspective::IS_CLIENT, perspective_);
+ // This PATH_CHALLENGE is received on an alternative socket which should be
+ // used to send PATH_RESPONSE.
+ if (!path_validator_.HasPendingPathValidation() ||
+ path_validator_.GetContext()->self_address() !=
+ last_packet_destination_address_) {
+ // Ignore this PATH_CHALLENGE if it's received from an uninteresting socket.
+ return true;
+ }
+ QuicPacketWriter* writer = path_validator_.GetContext()->WriterToUse();
+
+ std::unique_ptr<SerializedPacket> probing_packet =
+ packet_creator_.SerializePathResponseConnectivityProbingPacket(
+ {data_buffer}, /*is_padded=*/true);
+ DCHECK_EQ(IsRetransmittable(*probing_packet), NO_RETRANSMITTABLE_DATA);
+ QUIC_DVLOG(1) << ENDPOINT
+ << "Send PATH_RESPONSE from alternative socket with address "
+ << last_packet_destination_address_;
+ // Ignore the return value to treat write error on the alternative writer as
+ // part of network error. If the writer becomes blocked, wait for the peer to
+ // send another PATH_CHALLENGE.
+ WritePacketUsingWriter(std::move(probing_packet), writer,
+ last_packet_destination_address_, peer_address_to_send,
+ /*measure_rtt=*/false);
+ return true;
}
void QuicConnection::UpdatePeerAddress(QuicSocketAddress peer_address) {
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 2f5c09f..14716ce 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -8487,7 +8487,7 @@
sizeof(challenge_data)));
}
-TEST_P(QuicConnectionTest, ClientResponseToPathChallenge) {
+TEST_P(QuicConnectionTest, ClientResponseToPathChallengeOnDefaulSocket) {
if (!VersionHasIetfQuicFrames(connection_.version().transport_version) ||
!connection_.send_path_response()) {
return;
@@ -8528,6 +8528,57 @@
sizeof(challenge_data)));
}
+TEST_P(QuicConnectionTest, ClientResponseToPathChallengeOnAlternativeSocket) {
+ if (!VersionHasIetfQuicFrames(connection_.version().transport_version) ||
+ !connection_.send_path_response()) {
+ return;
+ }
+ PathProbeTestInit(Perspective::IS_CLIENT);
+ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false);
+
+ QuicSocketAddress kNewSelfAddress(QuicIpAddress::Loopback6(), /*port=*/23456);
+ TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT);
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(AtLeast(1u))
+ .WillOnce(Invoke([&]() {
+ 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());
+ EXPECT_EQ(kNewSelfAddress.host(),
+ new_writer.last_write_source_address());
+ }));
+ bool success = false;
+ connection_.ValidatePath(
+ std::make_unique<TestQuicPathValidationContext>(
+ kNewSelfAddress, connection_.peer_address(), &new_writer),
+ std::make_unique<TestValidationResultDelegate>(
+ 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.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _))
+ .Times(AtLeast(1u))
+ .WillOnce(Invoke([&]() {
+ EXPECT_EQ(2u, new_writer.packets_write_attempts());
+ EXPECT_EQ(1u, new_writer.path_response_frames().size());
+ EXPECT_EQ(1u, new_writer.padding_frames().size());
+ EXPECT_EQ(kNewSelfAddress.host(),
+ new_writer.last_write_source_address());
+ }));
+ std::unique_ptr<SerializedPacket> probing_packet = ConstructProbingPacket();
+ std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket(
+ QuicEncryptedPacket(probing_packet->encrypted_buffer,
+ probing_packet->encrypted_length),
+ clock_.Now()));
+ ProcessReceivedPacket(kNewSelfAddress, kPeerAddress, *received);
+
+ QuicSocketAddress kNewerSelfAddress(QuicIpAddress::Loopback6(),
+ /*port=*/34567);
+ // Receiving a PATH_CHALLENGE on an unknown socket should be ignored.
+ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0u);
+ ProcessReceivedPacket(kNewerSelfAddress, kPeerAddress, *received);
+}
+
TEST_P(QuicConnectionTest,
RestartPathDegradingDetectionAfterMigrationWithProbe) {
// TODO(b/150095484): add test coverage for IETF to verify that client takes