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