Move the creation of server preferred address path validation context from connection to session. Also removed SetIPv(4|6)AlternateServerAddressToSend that sets connection IDs. Note for chromium merge: Please replace SetIPv(4|6)AlternateServerAddressToSend(address, cid, token) with: SetIPv(4|6)AlternateServerAddressToSend(address); SetPreferredAddressConnectionIdAndTokenToSend(cid, token); PiperOrigin-RevId: 498064870
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc index b889402..f5af621 100644 --- a/quiche/quic/core/http/end_to_end_test.cc +++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -506,8 +506,11 @@ server_supported_versions_, &memory_cache_backend_, expected_server_connection_id_length_); test_server->SetEventLoopFactory(GetParam().event_loop); - server_thread_ = - std::make_unique<ServerThread>(std::move(test_server), server_address_); + const QuicSocketAddress server_listening_address = + server_listening_address_.has_value() ? *server_listening_address_ + : server_address_; + server_thread_ = std::make_unique<ServerThread>(std::move(test_server), + server_listening_address); if (chlo_multiplier_ != 0) { server_thread_->server()->SetChloMultiplier(chlo_multiplier_); } @@ -885,6 +888,7 @@ // Default is true. bool connect_to_server_on_initialize_; QuicSocketAddress server_address_; + absl::optional<QuicSocketAddress> server_listening_address_; std::string server_hostname_; QuicTestBackend memory_cache_backend_; std::unique_ptr<ServerThread> server_thread_; @@ -5468,6 +5472,36 @@ stream->Reset(QuicRstStreamErrorCode::QUIC_STREAM_NO_ERROR); } +TEST_P(EndToEndTest, SimpleServerPreferredAddressTest) { + const QuicSocketAddress kServerPreferredAddress(TestLoopback(1), 443); + server_address_ = QuicSocketAddress(TestLoopback(2), 443); + ASSERT_NE(kServerPreferredAddress, server_address_); + // Send server preferred address and let server listen on Any. + if (kServerPreferredAddress.host().IsIPv4()) { + server_listening_address_ = QuicSocketAddress(QuicIpAddress::Any4(), 443); + server_config_.SetIPv4AlternateServerAddressToSend(kServerPreferredAddress); + } else { + server_listening_address_ = QuicSocketAddress(QuicIpAddress::Any6(), 443); + server_config_.SetIPv6AlternateServerAddressToSend(kServerPreferredAddress); + } + ASSERT_TRUE(Initialize()); + if (!GetClientConnection()->connection_migration_use_new_cid()) { + return; + } + client_config_.SetConnectionOptionsToSend(QuicTagVector{kRVCM}); + client_config_.SetClientConnectionOptions(QuicTagVector{kSPAD}); + client_.reset(CreateQuicClient(nullptr)); + EXPECT_TRUE(client_->client()->WaitForHandshakeConfirmed()); + while (client_->client()->HasPendingPathValidation()) { + client_->client()->WaitForEvents(); + } + // TODO(b/262386897): Currently, server drops packets received on preferred + // address because self address change is disallowed. + const auto client_stats = GetClientConnection()->GetStats(); + EXPECT_FALSE(client_stats.server_preferred_address_validated); + EXPECT_TRUE(client_stats.failed_to_validate_server_preferred_address); +} + TEST_P(EndToEndPacketReorderingTest, ReorderedPathChallenge) { ASSERT_TRUE(Initialize()); if (!version_.HasIetfQuicFrames()) {
diff --git a/quiche/quic/core/quic_config.cc b/quiche/quic/core/quic_config.cc index 161426d..f8895b0 100644 --- a/quiche/quic/core/quic_config.cc +++ b/quiche/quic/core/quic_config.cc
@@ -859,21 +859,6 @@ alternate_server_address_ipv6_.SetSendValue(alternate_server_address_ipv6); } -void QuicConfig::SetIPv6AlternateServerAddressToSend( - const QuicSocketAddress& alternate_server_address_ipv6, - const QuicConnectionId& connection_id, - const StatelessResetToken& stateless_reset_token) { - if (!alternate_server_address_ipv6.host().IsIPv6()) { - QUIC_BUG(quic_bug_10575_10) - << "Cannot use SetIPv6AlternateServerAddressToSend with " - << alternate_server_address_ipv6; - return; - } - alternate_server_address_ipv6_.SetSendValue(alternate_server_address_ipv6); - preferred_address_connection_id_and_token_ = - std::make_pair(connection_id, stateless_reset_token); -} - bool QuicConfig::HasReceivedIPv6AlternateServerAddress() const { return alternate_server_address_ipv6_.HasReceivedValue(); } @@ -894,21 +879,6 @@ alternate_server_address_ipv4_.SetSendValue(alternate_server_address_ipv4); } -void QuicConfig::SetIPv4AlternateServerAddressToSend( - const QuicSocketAddress& alternate_server_address_ipv4, - const QuicConnectionId& connection_id, - const StatelessResetToken& stateless_reset_token) { - if (!alternate_server_address_ipv4.host().IsIPv4()) { - QUIC_BUG(quic_bug_10575_12) - << "Cannot use SetIPv4AlternateServerAddressToSend with " - << alternate_server_address_ipv4; - return; - } - alternate_server_address_ipv4_.SetSendValue(alternate_server_address_ipv4); - preferred_address_connection_id_and_token_ = - std::make_pair(connection_id, stateless_reset_token); -} - bool QuicConfig::HasReceivedIPv4AlternateServerAddress() const { return alternate_server_address_ipv4_.HasReceivedValue(); } @@ -918,6 +888,26 @@ return alternate_server_address_ipv4_.GetReceivedValue(); } +void QuicConfig::SetPreferredAddressConnectionIdAndTokenToSend( + const QuicConnectionId& connection_id, + const StatelessResetToken& stateless_reset_token) { + if (!CanSendPreferredAddressConnectionIdAndToken()) { + QUIC_BUG(quic_bug_10575_17) + << "Can not send connection ID and token for preferred address"; + return; + } + preferred_address_connection_id_and_token_ = + std::make_pair(connection_id, stateless_reset_token); +} + +bool QuicConfig::CanSendPreferredAddressConnectionIdAndToken() const { + if (!alternate_server_address_ipv4_.HasSendValue() && + !alternate_server_address_ipv6_.HasSendValue()) { + return false; + } + return !preferred_address_connection_id_and_token_.has_value(); +} + bool QuicConfig::HasReceivedPreferredAddressConnectionIdAndToken() const { return (HasReceivedIPv6AlternateServerAddress() || HasReceivedIPv4AlternateServerAddress()) &&
diff --git a/quiche/quic/core/quic_config.h b/quiche/quic/core/quic_config.h index 241fe84..52cc3b4 100644 --- a/quiche/quic/core/quic_config.h +++ b/quiche/quic/core/quic_config.h
@@ -390,23 +390,27 @@ // IPv6 alternate server address. void SetIPv6AlternateServerAddressToSend( const QuicSocketAddress& alternate_server_address_ipv6); - void SetIPv6AlternateServerAddressToSend( - const QuicSocketAddress& alternate_server_address_ipv6, - const QuicConnectionId& connection_id, - const StatelessResetToken& stateless_reset_token); bool HasReceivedIPv6AlternateServerAddress() const; const QuicSocketAddress& ReceivedIPv6AlternateServerAddress() const; // IPv4 alternate server address. void SetIPv4AlternateServerAddressToSend( const QuicSocketAddress& alternate_server_address_ipv4); - void SetIPv4AlternateServerAddressToSend( - const QuicSocketAddress& alternate_server_address_ipv4, - const QuicConnectionId& connection_id, - const StatelessResetToken& stateless_reset_token); bool HasReceivedIPv4AlternateServerAddress() const; const QuicSocketAddress& ReceivedIPv4AlternateServerAddress() const; + // Called to set |connection_id| and |stateless_reset_token| if server + // preferred address has been set via SetIPv(4|6)AlternateServerAddressToSend. + // Please note, this is different from SetStatelessResetTokenToSend(const + // StatelessResetToken&) which is used to send the token corresponding to the + // existing server_connection_id. + void SetPreferredAddressConnectionIdAndTokenToSend( + const QuicConnectionId& connection_id, + const StatelessResetToken& stateless_reset_token); + // Returns true if server preferred address has been set via + // SetIPv(4|6)AlternateServerAddressToSend. + bool CanSendPreferredAddressConnectionIdAndToken() const; + // Preferred Address Connection ID and Token. bool HasReceivedPreferredAddressConnectionIdAndToken() const; const std::pair<QuicConnectionId, StatelessResetToken>&
diff --git a/quiche/quic/core/quic_config_test.cc b/quiche/quic/core/quic_config_test.cc index 244d967..495127c 100644 --- a/quiche/quic/core/quic_config_test.cc +++ b/quiche/quic/core/quic_config_test.cc
@@ -482,8 +482,12 @@ QuicConnectionId new_connection_id = TestConnectionId(5); StatelessResetToken new_stateless_reset_token = QuicUtils::GenerateStatelessResetToken(new_connection_id); - config_.SetIPv4AlternateServerAddressToSend( - kTestServerAddress, new_connection_id, new_stateless_reset_token); + EXPECT_FALSE(config_.CanSendPreferredAddressConnectionIdAndToken()); + config_.SetIPv4AlternateServerAddressToSend(kTestServerAddress); + ASSERT_TRUE(config_.CanSendPreferredAddressConnectionIdAndToken()); + config_.SetPreferredAddressConnectionIdAndTokenToSend( + new_connection_id, new_stateless_reset_token); + EXPECT_FALSE(config_.CanSendPreferredAddressConnectionIdAndToken()); TransportParameters params; config_.FillTransportParameters(¶ms);
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc index d9f9f6c..2f5f56f 100644 --- a/quiche/quic/core/quic_connection.cc +++ b/quiche/quic/core/quic_connection.cc
@@ -242,24 +242,6 @@ return false; } -// This context stores the path info from client address to server preferred -// address while client is validating this address. -class ServerPreferredAddressPathValidationContext - : public QuicPathValidationContext { - public: - ServerPreferredAddressPathValidationContext( - const QuicSocketAddress& server_preferred_address, - QuicConnection* connection) - : QuicPathValidationContext(connection->self_address(), - server_preferred_address), - connection_(connection) {} - - QuicPacketWriter* WriterToUse() override { return connection_->writer(); } - - private: - QuicConnection* connection_; -}; - // Client migrates to server preferred address on path validation suceeds. // Otherwise, client cleans up alternative path. class ServerPreferredAddressResultDelegate @@ -274,6 +256,7 @@ << " validated. Migrating path, self_address: " << context->self_address() << ", peer_address: " << context->peer_address(); + connection_->mutable_stats().server_preferred_address_validated = true; const bool success = connection_->MigratePath(context->self_address(), context->peer_address(), context->WriterToUse(), @@ -287,6 +270,8 @@ std::unique_ptr<QuicPathValidationContext> context) override { QUIC_DLOG(INFO) << "Failed to validate server preferred address : " << context->peer_address(); + connection_->mutable_stats().failed_to_validate_server_preferred_address = + true; connection_->OnPathValidationFailureAtClient(/*is_multi_port=*/false); } @@ -721,6 +706,9 @@ config.HasReceivedIPv6AlternateServerAddress()) { server_preferred_address_ = config.ReceivedIPv6AlternateServerAddress(); } + QUIC_DLOG_IF(INFO, server_preferred_address_.IsInitialized()) + << ENDPOINT + << "Received server preferred address: " << server_preferred_address_; AddKnownServerAddress(server_preferred_address_); } if (config.HasReceivedMaxPacketSize()) { @@ -2763,7 +2751,8 @@ } QUIC_DVLOG(1) << ENDPOINT << "time of last received packet: " << packet.receipt_time().ToDebuggingValue() << " from peer " - << last_received_packet_info_.source_address; + << last_received_packet_info_.source_address << ", to " + << last_received_packet_info_.destination_address; ScopedPacketFlusher flusher(this); if (!framer_.ProcessPacket(packet)) { @@ -4012,11 +4001,16 @@ QUICHE_DCHECK_EQ(Perspective::IS_CLIENT, perspective_); // Validate received server preferred address. auto context = - std::make_unique<ServerPreferredAddressPathValidationContext>( - server_preferred_address_, this); - auto result_delegate = - std::make_unique<ServerPreferredAddressResultDelegate>(this); - ValidatePath(std::move(context), std::move(result_delegate)); + visitor_->CreatePathValidationContextForServerPreferredAddress( + server_preferred_address_); + if (context != nullptr) { + QUICHE_DLOG(INFO) << ENDPOINT + << "Start validating server preferred address: " + << server_preferred_address_; + auto result_delegate = + std::make_unique<ServerPreferredAddressResultDelegate>(this); + ValidatePath(std::move(context), std::move(result_delegate)); + } } } @@ -6420,6 +6414,15 @@ known_server_addresses_.push_back(address); } +absl::optional<QuicNewConnectionIdFrame> +QuicConnection::MaybeIssueNewConnectionIdForPreferredAddress() { + if (self_issued_cid_manager_ == nullptr) { + return absl::nullopt; + } + return self_issued_cid_manager_ + ->MaybeIssueNewConnectionIdForPreferredAddress(); +} + bool QuicConnection::ShouldDetectBlackhole() const { if (!connected_ || blackhole_detection_disabled_) { return false;
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h index a3eddff..cc22c19 100644 --- a/quiche/quic/core/quic_connection.h +++ b/quiche/quic/core/quic_connection.h
@@ -240,6 +240,11 @@ // Returns context needed for the connection to probe on the alternative path. virtual std::unique_ptr<QuicPathValidationContext> CreateContextForMultiPortPath() = 0; + + // Creates context to validate server preferred address. + virtual std::unique_ptr<QuicPathValidationContext> + CreatePathValidationContextForServerPreferredAddress( + const QuicSocketAddress& server_preferred_address) = 0; }; // Interface which gets callbacks from the QuicConnection at interesting @@ -1264,6 +1269,9 @@ void AddKnownServerAddress(const QuicSocketAddress& address); + absl::optional<QuicNewConnectionIdFrame> + MaybeIssueNewConnectionIdForPreferredAddress(); + protected: // Calls cancel() on all the alarms owned by this connection. void CancelAllAlarms();
diff --git a/quiche/quic/core/quic_connection_stats.cc b/quiche/quic/core/quic_connection_stats.cc index b5561eb..0321498 100644 --- a/quiche/quic/core/quic_connection_stats.cc +++ b/quiche/quic/core/quic_connection_stats.cc
@@ -63,6 +63,10 @@ os << " address_validated_via_decrypting_packet: " << s.address_validated_via_decrypting_packet; os << " address_validated_via_token: " << s.address_validated_via_token; + os << " server_preferred_address_validated: " + << s.server_preferred_address_validated; + os << " failed_to_validate_server_preferred_address: " + << s.failed_to_validate_server_preferred_address; os << " }"; return os;
diff --git a/quiche/quic/core/quic_connection_stats.h b/quiche/quic/core/quic_connection_stats.h index bf21073..94acd93 100644 --- a/quiche/quic/core/quic_connection_stats.h +++ b/quiche/quic/core/quic_connection_stats.h
@@ -218,6 +218,9 @@ // Number of RETIRE_CONNECTION_ID frames sent. size_t num_retire_connection_id_sent = 0; + bool server_preferred_address_validated = false; + bool failed_to_validate_server_preferred_address = false; + struct QUIC_NO_EXPORT TlsServerOperationStats { bool success = false; // If the operation is performed asynchronously, how long did it take.
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc index ebc8891..314f491 100644 --- a/quiche/quic/core/quic_connection_test.cc +++ b/quiche/quic/core/quic_connection_test.cc
@@ -1483,6 +1483,8 @@ QuicAckFrame frame = InitAckFrame(1); // Received ACK for packet 1. ProcessFramePacketAtLevel(1, QuicFrame(&frame), ENCRYPTION_INITIAL); + // Discard INITIAL key. + connection_.RemoveEncrypter(ENCRYPTION_INITIAL); QuicConfig config; config.SetConnectionOptionsToSend(QuicTagVector{kRVCM}); @@ -15988,6 +15990,9 @@ ServerPreferredAddressInit(); const QuicSocketAddress kServerPreferredAddress = QuicConnectionPeer::GetServerPreferredAddress(&connection_); + const QuicSocketAddress kNewSelfAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); + TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT); const StatelessResetToken kNewStatelessResetToken = QuicUtils::GenerateStatelessResetToken(TestConnectionId(17)); connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); @@ -15995,17 +16000,25 @@ .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); // Kick off path validation of server preferred address on handshake // confirmed. + EXPECT_CALL(visitor_, CreatePathValidationContextForServerPreferredAddress( + kServerPreferredAddress)) + .WillOnce(Return( + testing::ByMove(std::make_unique<TestQuicPathValidationContext>( + kNewSelfAddress, kServerPreferredAddress, &new_writer)))); connection_.OnHandshakeComplete(); EXPECT_TRUE(connection_.HasPendingPathValidation()); + EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath( + &connection_, kNewSelfAddress, kServerPreferredAddress)); EXPECT_EQ(TestConnectionId(17), - writer_->last_packet_header().destination_connection_id); - EXPECT_EQ(kServerPreferredAddress, writer_->last_write_peer_address()); + new_writer.last_packet_header().destination_connection_id); + EXPECT_EQ(kServerPreferredAddress, new_writer.last_write_peer_address()); - ASSERT_FALSE(writer_->path_challenge_frames().empty()); + ASSERT_FALSE(new_writer.path_challenge_frames().empty()); QuicPathFrameBuffer payload = - writer_->path_challenge_frames().front().data_buffer; + new_writer.path_challenge_frames().front().data_buffer; // Send data packet while path validation is pending. connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN); + ASSERT_FALSE(writer_->stream_frames().empty()); // While path validation is pending, packet is sent on default path. EXPECT_EQ(TestConnectionId(), writer_->last_packet_header().destination_connection_id); @@ -16019,13 +16032,17 @@ // Verify send_algorithm gets reset after migration (new sent packet is not // updated to exsting send_algorithm_). EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); - ProcessFramesPacketWithAddresses( - frames, kSelfAddress, kServerPreferredAddress, ENCRYPTION_FORWARD_SECURE); + ProcessFramesPacketWithAddresses(frames, kNewSelfAddress, + kServerPreferredAddress, + ENCRYPTION_FORWARD_SECURE); ASSERT_FALSE(connection_.HasPendingPathValidation()); + EXPECT_TRUE(QuicConnectionPeer::IsDefaultPath(&connection_, kNewSelfAddress, + kServerPreferredAddress)); + ASSERT_FALSE(new_writer.stream_frames().empty()); // Verify stream data is retransmitted on new path. EXPECT_EQ(TestConnectionId(17), - writer_->last_packet_header().destination_connection_id); - EXPECT_EQ(kServerPreferredAddress, writer_->last_write_peer_address()); + new_writer.last_packet_header().destination_connection_id); + EXPECT_EQ(kServerPreferredAddress, new_writer.last_write_peer_address()); // Verify stateless reset token gets changed. EXPECT_FALSE( connection_.IsValidStatelessResetToken(kTestStatelessResetToken)); @@ -16037,6 +16054,9 @@ // Verify client retires connection ID with sequence number 0. EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/0u)); retire_peer_issued_cid_alarm->Fire(); + EXPECT_TRUE(connection_.GetStats().server_preferred_address_validated); + EXPECT_FALSE( + connection_.GetStats().failed_to_validate_server_preferred_address); } TEST_P(QuicConnectionTest, ClientValidatedServerPreferredAddress2) { @@ -16048,18 +16068,27 @@ ServerPreferredAddressInit(); const QuicSocketAddress kServerPreferredAddress = QuicConnectionPeer::GetServerPreferredAddress(&connection_); + const QuicSocketAddress kNewSelfAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); + TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT); connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); EXPECT_CALL(visitor_, GetHandshakeState()) .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); // Kick off path validation of server preferred address on handshake // confirmed. + EXPECT_CALL(visitor_, CreatePathValidationContextForServerPreferredAddress( + kServerPreferredAddress)) + .WillOnce(Return( + testing::ByMove(std::make_unique<TestQuicPathValidationContext>( + kNewSelfAddress, kServerPreferredAddress, &new_writer)))); connection_.OnHandshakeComplete(); EXPECT_TRUE(connection_.HasPendingPathValidation()); - ASSERT_FALSE(writer_->path_challenge_frames().empty()); + ASSERT_FALSE(new_writer.path_challenge_frames().empty()); QuicPathFrameBuffer payload = - writer_->path_challenge_frames().front().data_buffer; + new_writer.path_challenge_frames().front().data_buffer; // Send data packet while path validation is pending. connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN); + ASSERT_FALSE(writer_->stream_frames().empty()); EXPECT_EQ(TestConnectionId(), writer_->last_packet_header().destination_connection_id); EXPECT_EQ(kPeerAddress, writer_->last_write_peer_address()); @@ -16067,13 +16096,14 @@ // Receive path challenge from original server address. QuicFrames frames; frames.push_back(QuicFrame(QuicPathResponseFrame(99, payload))); - ProcessFramesPacketWithAddresses(frames, kSelfAddress, kPeerAddress, + ProcessFramesPacketWithAddresses(frames, kNewSelfAddress, kPeerAddress, ENCRYPTION_FORWARD_SECURE); ASSERT_FALSE(connection_.HasPendingPathValidation()); + ASSERT_FALSE(new_writer.stream_frames().empty()); // Verify stream data is retransmitted on new path. EXPECT_EQ(TestConnectionId(17), - writer_->last_packet_header().destination_connection_id); - EXPECT_EQ(kServerPreferredAddress, writer_->last_write_peer_address()); + new_writer.last_packet_header().destination_connection_id); + EXPECT_EQ(kServerPreferredAddress, new_writer.last_write_peer_address()); auto* retire_peer_issued_cid_alarm = connection_.GetRetirePeerIssuedConnectionIdAlarm(); @@ -16088,6 +16118,9 @@ frames.push_back(QuicFrame(frame1_)); ProcessFramesPacketWithAddresses(frames, kSelfAddress, kPeerAddress, ENCRYPTION_FORWARD_SECURE); + EXPECT_TRUE(connection_.GetStats().server_preferred_address_validated); + EXPECT_FALSE( + connection_.GetStats().failed_to_validate_server_preferred_address); } TEST_P(QuicConnectionTest, ClientFailedToValidateServerPreferredAddress) { @@ -16097,22 +16130,36 @@ return; } ServerPreferredAddressInit(); + const QuicSocketAddress kServerPreferredAddress = + QuicConnectionPeer::GetServerPreferredAddress(&connection_); + const QuicSocketAddress kNewSelfAddress = + QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); + TestPacketWriter new_writer(version(), &clock_, Perspective::IS_CLIENT); connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); EXPECT_CALL(visitor_, GetHandshakeState()) .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); // Kick off path validation of server preferred address on handshake // confirmed. + EXPECT_CALL(visitor_, CreatePathValidationContextForServerPreferredAddress( + kServerPreferredAddress)) + .WillOnce(Return( + testing::ByMove(std::make_unique<TestQuicPathValidationContext>( + kNewSelfAddress, kServerPreferredAddress, &new_writer)))); connection_.OnHandshakeComplete(); EXPECT_TRUE(connection_.HasPendingPathValidation()); - ASSERT_FALSE(writer_->path_challenge_frames().empty()); + EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath( + &connection_, kNewSelfAddress, kServerPreferredAddress)); + ASSERT_FALSE(new_writer.path_challenge_frames().empty()); // Receive mismatched path challenge from original server address. QuicFrames frames; frames.push_back( QuicFrame(QuicPathResponseFrame(99, {0, 1, 2, 3, 4, 5, 6, 7}))); - ProcessFramesPacketWithAddresses(frames, kSelfAddress, kPeerAddress, + ProcessFramesPacketWithAddresses(frames, kNewSelfAddress, kPeerAddress, ENCRYPTION_FORWARD_SECURE); ASSERT_TRUE(connection_.HasPendingPathValidation()); + EXPECT_TRUE(QuicConnectionPeer::IsAlternativePath( + &connection_, kNewSelfAddress, kServerPreferredAddress)); // Simluate path validation times out. for (size_t i = 0; i < QuicPathValidator::kMaxRetryTimes + 1; ++i) { @@ -16123,8 +16170,11 @@ ->Fire(); } EXPECT_FALSE(connection_.HasPendingPathValidation()); + EXPECT_FALSE(QuicConnectionPeer::IsAlternativePath( + &connection_, kNewSelfAddress, kServerPreferredAddress)); // Verify stream data is sent on the default path. connection_.SendStreamDataWithString(3, "foo", 0, NO_FIN); + ASSERT_FALSE(writer_->stream_frames().empty()); EXPECT_EQ(TestConnectionId(), writer_->last_packet_header().destination_connection_id); EXPECT_EQ(kPeerAddress, writer_->last_write_peer_address()); @@ -16136,6 +16186,9 @@ EXPECT_CALL(visitor_, SendRetireConnectionId(/*sequence_number=*/1u)); retire_peer_issued_cid_alarm->Fire(); EXPECT_TRUE(connection_.IsValidStatelessResetToken(kTestStatelessResetToken)); + EXPECT_FALSE(connection_.GetStats().server_preferred_address_validated); + EXPECT_TRUE( + connection_.GetStats().failed_to_validate_server_preferred_address); } } // namespace
diff --git a/quiche/quic/core/quic_session.cc b/quiche/quic/core/quic_session.cc index ae7c41a..5f132d0 100644 --- a/quiche/quic/core/quic_session.cc +++ b/quiche/quic/core/quic_session.cc
@@ -1330,6 +1330,16 @@ config_.ReceivedInitialSessionFlowControlWindowBytes()); } + if (version().HasIetfQuicFrames() && + config_.CanSendPreferredAddressConnectionIdAndToken()) { + absl::optional<QuicNewConnectionIdFrame> frame = + connection_->MaybeIssueNewConnectionIdForPreferredAddress(); + if (frame.has_value()) { + config_.SetPreferredAddressConnectionIdAndTokenToSend( + frame->connection_id, frame->stateless_reset_token); + } + } + is_configured_ = true; connection()->OnConfigNegotiated();
diff --git a/quiche/quic/core/quic_session.h b/quiche/quic/core/quic_session.h index b2b8403..6576325 100644 --- a/quiche/quic/core/quic_session.h +++ b/quiche/quic/core/quic_session.h
@@ -180,6 +180,12 @@ return nullptr; } + std::unique_ptr<QuicPathValidationContext> + CreatePathValidationContextForServerPreferredAddress( + const QuicSocketAddress& /*server_preferred_address*/) override { + return nullptr; + } + // QuicStreamFrameDataProducer WriteStreamDataResult WriteStreamData(QuicStreamId id, QuicStreamOffset offset,
diff --git a/quiche/quic/test_tools/quic_connection_peer.cc b/quiche/quic/test_tools/quic_connection_peer.cc index d8f3070..b33bc29 100644 --- a/quiche/quic/test_tools/quic_connection_peer.cc +++ b/quiche/quic/test_tools/quic_connection_peer.cc
@@ -503,6 +503,13 @@ } // static +bool QuicConnectionPeer::IsDefaultPath(QuicConnection* connection, + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address) { + return connection->IsDefaultPath(self_address, peer_address); +} + +// static QuicConnection::PathState* QuicConnectionPeer::GetAlternativePath( QuicConnection* connection) { return &connection->alternative_path_;
diff --git a/quiche/quic/test_tools/quic_connection_peer.h b/quiche/quic/test_tools/quic_connection_peer.h index 91350d0..794e991 100644 --- a/quiche/quic/test_tools/quic_connection_peer.h +++ b/quiche/quic/test_tools/quic_connection_peer.h
@@ -201,6 +201,10 @@ static QuicConnection::PathState* GetDefaultPath(QuicConnection* connection); + static bool IsDefaultPath(QuicConnection* connection, + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address); + static QuicConnection::PathState* GetAlternativePath( QuicConnection* connection);
diff --git a/quiche/quic/test_tools/quic_test_utils.h b/quiche/quic/test_tools/quic_test_utils.h index 35f51a8..f2e38f5 100644 --- a/quiche/quic/test_tools/quic_test_utils.h +++ b/quiche/quic/test_tools/quic_test_utils.h
@@ -502,6 +502,9 @@ MOCK_METHOD(bool, MaybeSendAddressToken, (), (override)); MOCK_METHOD(std::unique_ptr<QuicPathValidationContext>, CreateContextForMultiPortPath, (), (override)); + MOCK_METHOD(std::unique_ptr<QuicPathValidationContext>, + CreatePathValidationContextForServerPreferredAddress, + (const QuicSocketAddress&), (override)); void OnBandwidthUpdateTimeout() override {} };
diff --git a/quiche/quic/test_tools/simulator/quic_endpoint.h b/quiche/quic/test_tools/simulator/quic_endpoint.h index 03e86f3..d41bef3 100644 --- a/quiche/quic/test_tools/simulator/quic_endpoint.h +++ b/quiche/quic/test_tools/simulator/quic_endpoint.h
@@ -109,6 +109,11 @@ override { return nullptr; } + std::unique_ptr<QuicPathValidationContext> + CreatePathValidationContextForServerPreferredAddress( + const QuicSocketAddress& /*server_preferred_address*/) override { + return nullptr; + } // End QuicConnectionVisitorInterface implementation.
diff --git a/quiche/quic/tools/quic_simple_client_session.cc b/quiche/quic/tools/quic_simple_client_session.cc index 2350052..c1e1b2a 100644 --- a/quiche/quic/tools/quic_simple_client_session.cc +++ b/quiche/quic/tools/quic_simple_client_session.cc
@@ -56,4 +56,22 @@ network_helper_->GetLatestClientAddress(), peer_address()); } +std::unique_ptr<QuicPathValidationContext> +QuicSimpleClientSession::CreatePathValidationContextForServerPreferredAddress( + const QuicSocketAddress& server_preferred_address) { + const auto self_address = connection()->self_address(); + if (network_helper_ == nullptr || + !network_helper_->CreateUDPSocketAndBind(server_preferred_address, + self_address.host(), 0)) { + return nullptr; + } + QuicPacketWriter* writer = network_helper_->CreateQuicPacketWriter(); + if (writer == nullptr) { + return nullptr; + } + return std::make_unique<PathMigrationContext>( + std::unique_ptr<QuicPacketWriter>(writer), + network_helper_->GetLatestClientAddress(), server_preferred_address); +} + } // namespace quic
diff --git a/quiche/quic/tools/quic_simple_client_session.h b/quiche/quic/tools/quic_simple_client_session.h index 763a1a3..631b36b 100644 --- a/quiche/quic/tools/quic_simple_client_session.h +++ b/quiche/quic/tools/quic_simple_client_session.h
@@ -27,6 +27,9 @@ HttpDatagramSupport LocalHttpDatagramSupport() override; std::unique_ptr<QuicPathValidationContext> CreateContextForMultiPortPath() override; + std::unique_ptr<QuicPathValidationContext> + CreatePathValidationContextForServerPreferredAddress( + const QuicSocketAddress& server_preferred_address) override; bool drop_response_body() const { return drop_response_body_; } private: