| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "quic/core/quic_connection.h" |
| |
| #include <errno.h> |
| |
| #include <memory> |
| #include <ostream> |
| #include <string> |
| #include <utility> |
| |
| #include "absl/base/macros.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "quic/core/congestion_control/loss_detection_interface.h" |
| #include "quic/core/congestion_control/send_algorithm_interface.h" |
| #include "quic/core/crypto/null_decrypter.h" |
| #include "quic/core/crypto/null_encrypter.h" |
| #include "quic/core/crypto/quic_decrypter.h" |
| #include "quic/core/crypto/quic_encrypter.h" |
| #include "quic/core/frames/quic_connection_close_frame.h" |
| #include "quic/core/frames/quic_new_connection_id_frame.h" |
| #include "quic/core/frames/quic_path_response_frame.h" |
| #include "quic/core/frames/quic_rst_stream_frame.h" |
| #include "quic/core/quic_connection_id.h" |
| #include "quic/core/quic_constants.h" |
| #include "quic/core/quic_error_codes.h" |
| #include "quic/core/quic_packet_creator.h" |
| #include "quic/core/quic_packets.h" |
| #include "quic/core/quic_path_validator.h" |
| #include "quic/core/quic_simple_buffer_allocator.h" |
| #include "quic/core/quic_types.h" |
| #include "quic/core/quic_utils.h" |
| #include "quic/core/quic_versions.h" |
| #include "quic/platform/api/quic_error_code_wrappers.h" |
| #include "quic/platform/api/quic_expect_bug.h" |
| #include "quic/platform/api/quic_flags.h" |
| #include "quic/platform/api/quic_ip_address.h" |
| #include "quic/platform/api/quic_logging.h" |
| #include "quic/platform/api/quic_reference_counted.h" |
| #include "quic/platform/api/quic_socket_address.h" |
| #include "quic/platform/api/quic_test.h" |
| #include "quic/test_tools/mock_clock.h" |
| #include "quic/test_tools/mock_random.h" |
| #include "quic/test_tools/quic_config_peer.h" |
| #include "quic/test_tools/quic_connection_peer.h" |
| #include "quic/test_tools/quic_framer_peer.h" |
| #include "quic/test_tools/quic_packet_creator_peer.h" |
| #include "quic/test_tools/quic_path_validator_peer.h" |
| #include "quic/test_tools/quic_sent_packet_manager_peer.h" |
| #include "quic/test_tools/quic_test_utils.h" |
| #include "quic/test_tools/simple_data_producer.h" |
| #include "quic/test_tools/simple_session_notifier.h" |
| |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::AtLeast; |
| using testing::DoAll; |
| using testing::ElementsAre; |
| using testing::Ge; |
| using testing::IgnoreResult; |
| using testing::InSequence; |
| using testing::Invoke; |
| using testing::InvokeWithoutArgs; |
| using testing::Lt; |
| using testing::Ref; |
| using testing::Return; |
| using testing::SaveArg; |
| using testing::SetArgPointee; |
| using testing::StrictMock; |
| |
| namespace quic { |
| namespace test { |
| namespace { |
| |
| const char data1[] = "foo data"; |
| const char data2[] = "bar data"; |
| |
| const bool kHasStopWaiting = true; |
| |
| const int kDefaultRetransmissionTimeMs = 500; |
| |
| DiversificationNonce kTestDiversificationNonce = { |
| 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', |
| 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', |
| 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', 'a', 'b', |
| }; |
| |
| const StatelessResetToken kTestStatelessResetToken{ |
| 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, |
| 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; |
| |
| const QuicSocketAddress kPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), |
| /*port=*/12345); |
| const QuicSocketAddress kSelfAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), |
| /*port=*/443); |
| |
| QuicStreamId GetNthClientInitiatedStreamId(int n, |
| QuicTransportVersion version) { |
| return QuicUtils::GetFirstBidirectionalStreamId(version, |
| Perspective::IS_CLIENT) + |
| n * 2; |
| } |
| |
| QuicLongHeaderType EncryptionlevelToLongHeaderType(EncryptionLevel level) { |
| switch (level) { |
| case ENCRYPTION_INITIAL: |
| return INITIAL; |
| case ENCRYPTION_HANDSHAKE: |
| return HANDSHAKE; |
| case ENCRYPTION_ZERO_RTT: |
| return ZERO_RTT_PROTECTED; |
| case ENCRYPTION_FORWARD_SECURE: |
| QUICHE_DCHECK(false); |
| return INVALID_PACKET_TYPE; |
| default: |
| QUICHE_DCHECK(false); |
| return INVALID_PACKET_TYPE; |
| } |
| } |
| |
| // A NullEncrypterWithConfidentialityLimit is a NullEncrypter that allows |
| // specifying the confidentiality limit on the maximum number of packets that |
| // may be encrypted per key phase in TLS+QUIC. |
| class NullEncrypterWithConfidentialityLimit : public NullEncrypter { |
| public: |
| NullEncrypterWithConfidentialityLimit(Perspective perspective, |
| QuicPacketCount confidentiality_limit) |
| : NullEncrypter(perspective), |
| confidentiality_limit_(confidentiality_limit) {} |
| |
| QuicPacketCount GetConfidentialityLimit() const override { |
| return confidentiality_limit_; |
| } |
| |
| private: |
| QuicPacketCount confidentiality_limit_; |
| }; |
| |
| class StrictTaggingDecrypterWithIntegrityLimit : public StrictTaggingDecrypter { |
| public: |
| StrictTaggingDecrypterWithIntegrityLimit(uint8_t tag, |
| QuicPacketCount integrity_limit) |
| : StrictTaggingDecrypter(tag), integrity_limit_(integrity_limit) {} |
| |
| QuicPacketCount GetIntegrityLimit() const override { |
| return integrity_limit_; |
| } |
| |
| private: |
| QuicPacketCount integrity_limit_; |
| }; |
| |
| class TestConnectionHelper : public QuicConnectionHelperInterface { |
| public: |
| TestConnectionHelper(MockClock* clock, MockRandom* random_generator) |
| : clock_(clock), random_generator_(random_generator) { |
| clock_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); |
| } |
| TestConnectionHelper(const TestConnectionHelper&) = delete; |
| TestConnectionHelper& operator=(const TestConnectionHelper&) = delete; |
| |
| // QuicConnectionHelperInterface |
| const QuicClock* GetClock() const override { return clock_; } |
| |
| QuicRandom* GetRandomGenerator() override { return random_generator_; } |
| |
| QuicBufferAllocator* GetStreamSendBufferAllocator() override { |
| return &buffer_allocator_; |
| } |
| |
| private: |
| MockClock* clock_; |
| MockRandom* random_generator_; |
| SimpleBufferAllocator buffer_allocator_; |
| }; |
| |
| class TestConnection : public QuicConnection { |
| public: |
| TestConnection(QuicConnectionId connection_id, |
| QuicSocketAddress initial_self_address, |
| QuicSocketAddress initial_peer_address, |
| TestConnectionHelper* helper, TestAlarmFactory* alarm_factory, |
| TestPacketWriter* writer, Perspective perspective, |
| ParsedQuicVersion version) |
| : QuicConnection(connection_id, initial_self_address, |
| initial_peer_address, helper, alarm_factory, writer, |
| /* owns_writer= */ false, perspective, |
| SupportedVersions(version)), |
| notifier_(nullptr) { |
| writer->set_perspective(perspective); |
| SetEncrypter(ENCRYPTION_FORWARD_SECURE, |
| std::make_unique<NullEncrypter>(perspective)); |
| SetDataProducer(&producer_); |
| } |
| TestConnection(const TestConnection&) = delete; |
| TestConnection& operator=(const TestConnection&) = delete; |
| |
| void SetSendAlgorithm(SendAlgorithmInterface* send_algorithm) { |
| QuicConnectionPeer::SetSendAlgorithm(this, send_algorithm); |
| } |
| |
| void SetLossAlgorithm(LossDetectionInterface* loss_algorithm) { |
| QuicConnectionPeer::SetLossAlgorithm(this, loss_algorithm); |
| } |
| |
| void SendPacket(EncryptionLevel /*level*/, uint64_t packet_number, |
| std::unique_ptr<QuicPacket> packet, |
| HasRetransmittableData retransmittable, bool has_ack, |
| bool has_pending_frames) { |
| ScopedPacketFlusher flusher(this); |
| char buffer[kMaxOutgoingPacketSize]; |
| size_t encrypted_length = |
| QuicConnectionPeer::GetFramer(this)->EncryptPayload( |
| ENCRYPTION_INITIAL, QuicPacketNumber(packet_number), *packet, |
| buffer, kMaxOutgoingPacketSize); |
| SerializedPacket serialized_packet( |
| QuicPacketNumber(packet_number), PACKET_4BYTE_PACKET_NUMBER, buffer, |
| encrypted_length, has_ack, has_pending_frames); |
| serialized_packet.peer_address = kPeerAddress; |
| if (retransmittable == HAS_RETRANSMITTABLE_DATA) { |
| serialized_packet.retransmittable_frames.push_back( |
| QuicFrame(QuicPingFrame())); |
| } |
| OnSerializedPacket(std::move(serialized_packet)); |
| } |
| |
| QuicConsumedData SaveAndSendStreamData(QuicStreamId id, |
| const struct iovec* iov, int iov_count, |
| size_t total_length, |
| QuicStreamOffset offset, |
| StreamSendingState state) { |
| ScopedPacketFlusher flusher(this); |
| producer_.SaveStreamData(id, iov, iov_count, 0u, total_length); |
| if (notifier_ != nullptr) { |
| return notifier_->WriteOrBufferData(id, total_length, state); |
| } |
| return QuicConnection::SendStreamData(id, total_length, offset, state); |
| } |
| |
| QuicConsumedData SendStreamDataWithString(QuicStreamId id, |
| absl::string_view data, |
| QuicStreamOffset offset, |
| StreamSendingState state) { |
| ScopedPacketFlusher flusher(this); |
| if (!QuicUtils::IsCryptoStreamId(transport_version(), id) && |
| this->encryption_level() == ENCRYPTION_INITIAL) { |
| this->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| if (perspective() == Perspective::IS_CLIENT && !IsHandshakeComplete()) { |
| OnHandshakeComplete(); |
| } |
| if (version().SupportsAntiAmplificationLimit()) { |
| QuicConnectionPeer::SetAddressValidated(this); |
| } |
| } |
| struct iovec iov; |
| MakeIOVector(data, &iov); |
| return SaveAndSendStreamData(id, &iov, 1, data.length(), offset, state); |
| } |
| |
| QuicConsumedData SendApplicationDataAtLevel(EncryptionLevel encryption_level, |
| QuicStreamId id, |
| absl::string_view data, |
| QuicStreamOffset offset, |
| StreamSendingState state) { |
| ScopedPacketFlusher flusher(this); |
| QUICHE_DCHECK(encryption_level >= ENCRYPTION_ZERO_RTT); |
| SetEncrypter(encryption_level, std::make_unique<TaggingEncrypter>(0x01)); |
| SetDefaultEncryptionLevel(encryption_level); |
| struct iovec iov; |
| MakeIOVector(data, &iov); |
| return SaveAndSendStreamData(id, &iov, 1, data.length(), offset, state); |
| } |
| |
| QuicConsumedData SendStreamData3() { |
| return SendStreamDataWithString( |
| GetNthClientInitiatedStreamId(1, transport_version()), "food", 0, |
| NO_FIN); |
| } |
| |
| QuicConsumedData SendStreamData5() { |
| return SendStreamDataWithString( |
| GetNthClientInitiatedStreamId(2, transport_version()), "food2", 0, |
| NO_FIN); |
| } |
| |
| // Ensures the connection can write stream data before writing. |
| QuicConsumedData EnsureWritableAndSendStreamData5() { |
| EXPECT_TRUE(CanWrite(HAS_RETRANSMITTABLE_DATA)); |
| return SendStreamData5(); |
| } |
| |
| // The crypto stream has special semantics so that it is not blocked by a |
| // congestion window limitation, and also so that it gets put into a separate |
| // packet (so that it is easier to reason about a crypto frame not being |
| // split needlessly across packet boundaries). As a result, we have separate |
| // tests for some cases for this stream. |
| QuicConsumedData SendCryptoStreamData() { |
| QuicStreamOffset offset = 0; |
| absl::string_view data("chlo"); |
| if (!QuicVersionUsesCryptoFrames(transport_version())) { |
| return SendCryptoDataWithString(data, offset); |
| } |
| producer_.SaveCryptoData(ENCRYPTION_INITIAL, offset, data); |
| size_t bytes_written; |
| if (notifier_) { |
| bytes_written = |
| notifier_->WriteCryptoData(ENCRYPTION_INITIAL, data.length(), offset); |
| } else { |
| bytes_written = QuicConnection::SendCryptoData(ENCRYPTION_INITIAL, |
| data.length(), offset); |
| } |
| return QuicConsumedData(bytes_written, /*fin_consumed*/ false); |
| } |
| |
| QuicConsumedData SendCryptoDataWithString(absl::string_view data, |
| QuicStreamOffset offset) { |
| return SendCryptoDataWithString(data, offset, ENCRYPTION_INITIAL); |
| } |
| |
| QuicConsumedData SendCryptoDataWithString(absl::string_view data, |
| QuicStreamOffset offset, |
| EncryptionLevel encryption_level) { |
| if (!QuicVersionUsesCryptoFrames(transport_version())) { |
| return SendStreamDataWithString( |
| QuicUtils::GetCryptoStreamId(transport_version()), data, offset, |
| NO_FIN); |
| } |
| producer_.SaveCryptoData(encryption_level, offset, data); |
| size_t bytes_written; |
| if (notifier_) { |
| bytes_written = |
| notifier_->WriteCryptoData(encryption_level, data.length(), offset); |
| } else { |
| bytes_written = QuicConnection::SendCryptoData(encryption_level, |
| data.length(), offset); |
| } |
| return QuicConsumedData(bytes_written, /*fin_consumed*/ false); |
| } |
| |
| void set_version(ParsedQuicVersion version) { |
| QuicConnectionPeer::GetFramer(this)->set_version(version); |
| } |
| |
| void SetSupportedVersions(const ParsedQuicVersionVector& versions) { |
| QuicConnectionPeer::GetFramer(this)->SetSupportedVersions(versions); |
| writer()->SetSupportedVersions(versions); |
| } |
| |
| // This should be called before setting customized encrypters/decrypters for |
| // connection and peer creator. |
| void set_perspective(Perspective perspective) { |
| writer()->set_perspective(perspective); |
| QuicConnectionPeer::ResetPeerIssuedConnectionIdManager(this); |
| QuicConnectionPeer::SetPerspective(this, perspective); |
| QuicSentPacketManagerPeer::SetPerspective( |
| QuicConnectionPeer::GetSentPacketManager(this), perspective); |
| QuicConnectionPeer::GetFramer(this)->SetInitialObfuscators( |
| TestConnectionId()); |
| for (EncryptionLevel level : {ENCRYPTION_ZERO_RTT, ENCRYPTION_HANDSHAKE, |
| ENCRYPTION_FORWARD_SECURE}) { |
| if (QuicConnectionPeer::GetFramer(this)->HasEncrypterOfEncryptionLevel( |
| level)) { |
| SetEncrypter(level, std::make_unique<NullEncrypter>(perspective)); |
| } |
| if (QuicConnectionPeer::GetFramer(this)->HasDecrypterOfEncryptionLevel( |
| level)) { |
| InstallDecrypter(level, std::make_unique<NullDecrypter>(perspective)); |
| } |
| } |
| } |
| |
| // Enable path MTU discovery. Assumes that the test is performed from the |
| // server perspective and the higher value of MTU target is used. |
| void EnablePathMtuDiscovery(MockSendAlgorithm* send_algorithm) { |
| ASSERT_EQ(Perspective::IS_SERVER, perspective()); |
| |
| if (GetQuicReloadableFlag(quic_enable_mtu_discovery_at_server)) { |
| OnConfigNegotiated(); |
| } else { |
| QuicConfig config; |
| QuicTagVector connection_options; |
| connection_options.push_back(kMTUH); |
| config.SetInitialReceivedConnectionOptions(connection_options); |
| EXPECT_CALL(*send_algorithm, SetFromConfig(_, _)); |
| SetFromConfig(config); |
| } |
| |
| // Normally, the pacing would be disabled in the test, but calling |
| // SetFromConfig enables it. Set nearly-infinite bandwidth to make the |
| // pacing algorithm work. |
| EXPECT_CALL(*send_algorithm, PacingRate(_)) |
| .WillRepeatedly(Return(QuicBandwidth::Infinite())); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetAckAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetAckAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetPingAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetPingAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetRetransmissionAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetRetransmissionAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetSendAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetSendAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetTimeoutAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetIdleNetworkDetectorAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetMtuDiscoveryAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetMtuDiscoveryAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetProcessUndecryptablePacketsAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetProcessUndecryptablePacketsAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetDiscardPreviousOneRttKeysAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetDiscardPreviousOneRttKeysAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetDiscardZeroRttDecryptionKeysAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetDiscardZeroRttDecryptionKeysAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetBlackholeDetectorAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetBlackholeDetectorAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetRetirePeerIssuedConnectionIdAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetRetirePeerIssuedConnectionIdAlarm(this)); |
| } |
| |
| TestAlarmFactory::TestAlarm* GetRetireSelfIssuedConnectionIdAlarm() { |
| return reinterpret_cast<TestAlarmFactory::TestAlarm*>( |
| QuicConnectionPeer::GetRetireSelfIssuedConnectionIdAlarm(this)); |
| } |
| |
| void PathDegradingTimeout() { |
| QUICHE_DCHECK(PathDegradingDetectionInProgress()); |
| GetBlackholeDetectorAlarm()->Fire(); |
| } |
| |
| bool PathDegradingDetectionInProgress() { |
| return QuicConnectionPeer::GetPathDegradingDeadline(this).IsInitialized(); |
| } |
| |
| bool BlackholeDetectionInProgress() { |
| return QuicConnectionPeer::GetBlackholeDetectionDeadline(this) |
| .IsInitialized(); |
| } |
| |
| bool PathMtuReductionDetectionInProgress() { |
| return QuicConnectionPeer::GetPathMtuReductionDetectionDeadline(this) |
| .IsInitialized(); |
| } |
| |
| void SetMaxTailLossProbes(size_t max_tail_loss_probes) { |
| QuicSentPacketManagerPeer::SetMaxTailLossProbes( |
| QuicConnectionPeer::GetSentPacketManager(this), max_tail_loss_probes); |
| } |
| |
| QuicByteCount GetBytesInFlight() { |
| return QuicConnectionPeer::GetSentPacketManager(this)->GetBytesInFlight(); |
| } |
| |
| void set_notifier(SimpleSessionNotifier* notifier) { notifier_ = notifier; } |
| |
| void ReturnEffectivePeerAddressForNextPacket(const QuicSocketAddress& addr) { |
| next_effective_peer_addr_ = std::make_unique<QuicSocketAddress>(addr); |
| } |
| |
| bool PtoEnabled() { |
| if (QuicConnectionPeer::GetSentPacketManager(this)->pto_enabled()) { |
| // PTO mode is default enabled for T099. And TLP/RTO related tests are |
| // stale. |
| QUICHE_DCHECK(PROTOCOL_TLS1_3 == version().handshake_protocol || |
| GetQuicReloadableFlag(quic_default_on_pto)); |
| return true; |
| } |
| return false; |
| } |
| |
| void SendOrQueuePacket(SerializedPacket packet) override { |
| QuicConnection::SendOrQueuePacket(std::move(packet)); |
| self_address_on_default_path_while_sending_packet_ = self_address(); |
| } |
| |
| QuicSocketAddress self_address_on_default_path_while_sending_packet() { |
| return self_address_on_default_path_while_sending_packet_; |
| } |
| |
| SimpleDataProducer* producer() { return &producer_; } |
| |
| using QuicConnection::active_effective_peer_migration_type; |
| using QuicConnection::IsCurrentPacketConnectivityProbing; |
| using QuicConnection::SelectMutualVersion; |
| using QuicConnection::SendProbingRetransmissions; |
| using QuicConnection::set_defer_send_in_response_to_packets; |
| |
| protected: |
| QuicSocketAddress GetEffectivePeerAddressFromCurrentPacket() const override { |
| if (next_effective_peer_addr_) { |
| return *std::move(next_effective_peer_addr_); |
| } |
| return QuicConnection::GetEffectivePeerAddressFromCurrentPacket(); |
| } |
| |
| private: |
| TestPacketWriter* writer() { |
| return static_cast<TestPacketWriter*>(QuicConnection::writer()); |
| } |
| |
| SimpleDataProducer producer_; |
| |
| SimpleSessionNotifier* notifier_; |
| |
| std::unique_ptr<QuicSocketAddress> next_effective_peer_addr_; |
| |
| QuicSocketAddress self_address_on_default_path_while_sending_packet_; |
| }; |
| |
| enum class AckResponse { kDefer, kImmediate }; |
| |
| // Run tests with combinations of {ParsedQuicVersion, AckResponse}. |
| struct TestParams { |
| TestParams(ParsedQuicVersion version, AckResponse ack_response, |
| bool no_stop_waiting) |
| : version(version), |
| ack_response(ack_response), |
| no_stop_waiting(no_stop_waiting) {} |
| |
| ParsedQuicVersion version; |
| AckResponse ack_response; |
| bool no_stop_waiting; |
| }; |
| |
| // Used by ::testing::PrintToStringParamName(). |
| std::string PrintToString(const TestParams& p) { |
| return absl::StrCat( |
| ParsedQuicVersionToString(p.version), "_", |
| (p.ack_response == AckResponse::kDefer ? "defer" : "immediate"), "_", |
| (p.no_stop_waiting ? "No" : ""), "StopWaiting"); |
| } |
| |
| // Constructs various test permutations. |
| std::vector<TestParams> GetTestParams() { |
| QuicFlagSaver flags; |
| std::vector<TestParams> params; |
| ParsedQuicVersionVector all_supported_versions = AllSupportedVersions(); |
| for (size_t i = 0; i < all_supported_versions.size(); ++i) { |
| for (AckResponse ack_response : |
| {AckResponse::kDefer, AckResponse::kImmediate}) { |
| params.push_back( |
| TestParams(all_supported_versions[i], ack_response, true)); |
| if (!all_supported_versions[i].HasIetfInvariantHeader()) { |
| params.push_back( |
| TestParams(all_supported_versions[i], ack_response, false)); |
| } |
| } |
| } |
| return params; |
| } |
| |
| class QuicConnectionTest : public QuicTestWithParam<TestParams> { |
| public: |
| // For tests that do silent connection closes, no such packet is generated. In |
| // order to verify the contents of the OnConnectionClosed upcall, EXPECTs |
| // should invoke this method, saving the frame, and then the test can verify |
| // the contents. |
| void SaveConnectionCloseFrame(const QuicConnectionCloseFrame& frame, |
| ConnectionCloseSource /*source*/) { |
| saved_connection_close_frame_ = frame; |
| connection_close_frame_count_++; |
| } |
| |
| protected: |
| QuicConnectionTest() |
| : connection_id_(TestConnectionId()), |
| framer_(SupportedVersions(version()), QuicTime::Zero(), |
| Perspective::IS_CLIENT, connection_id_.length()), |
| send_algorithm_(new StrictMock<MockSendAlgorithm>), |
| loss_algorithm_(new MockLossAlgorithm()), |
| helper_(new TestConnectionHelper(&clock_, &random_generator_)), |
| alarm_factory_(new TestAlarmFactory()), |
| peer_framer_(SupportedVersions(version()), QuicTime::Zero(), |
| Perspective::IS_SERVER, connection_id_.length()), |
| peer_creator_(connection_id_, &peer_framer_, |
| /*delegate=*/nullptr), |
| writer_( |
| new TestPacketWriter(version(), &clock_, Perspective::IS_CLIENT)), |
| connection_(connection_id_, kSelfAddress, kPeerAddress, helper_.get(), |
| alarm_factory_.get(), writer_.get(), Perspective::IS_CLIENT, |
| version()), |
| creator_(QuicConnectionPeer::GetPacketCreator(&connection_)), |
| manager_(QuicConnectionPeer::GetSentPacketManager(&connection_)), |
| frame1_(0, false, 0, absl::string_view(data1)), |
| frame2_(0, false, 3, absl::string_view(data2)), |
| crypto_frame_(ENCRYPTION_INITIAL, 0, absl::string_view(data1)), |
| packet_number_length_(PACKET_4BYTE_PACKET_NUMBER), |
| connection_id_included_(CONNECTION_ID_PRESENT), |
| notifier_(&connection_), |
| connection_close_frame_count_(0) { |
| QUIC_DVLOG(2) << "QuicConnectionTest(" << PrintToString(GetParam()) << ")"; |
| connection_.set_defer_send_in_response_to_packets(GetParam().ack_response == |
| AckResponse::kDefer); |
| framer_.SetInitialObfuscators(TestConnectionId()); |
| connection_.InstallInitialCrypters(TestConnectionId()); |
| CrypterPair crypters; |
| CryptoUtils::CreateInitialObfuscators(Perspective::IS_SERVER, version(), |
| TestConnectionId(), &crypters); |
| peer_creator_.SetEncrypter(ENCRYPTION_INITIAL, |
| std::move(crypters.encrypter)); |
| if (version().KnowsWhichDecrypterToUse()) { |
| peer_framer_.InstallDecrypter(ENCRYPTION_INITIAL, |
| std::move(crypters.decrypter)); |
| } else { |
| peer_framer_.SetDecrypter(ENCRYPTION_INITIAL, |
| std::move(crypters.decrypter)); |
| } |
| for (EncryptionLevel level : |
| {ENCRYPTION_ZERO_RTT, ENCRYPTION_FORWARD_SECURE}) { |
| peer_creator_.SetEncrypter( |
| level, std::make_unique<NullEncrypter>(peer_framer_.perspective())); |
| } |
| QuicFramerPeer::SetLastSerializedServerConnectionId( |
| QuicConnectionPeer::GetFramer(&connection_), connection_id_); |
| QuicFramerPeer::SetLastWrittenPacketNumberLength( |
| QuicConnectionPeer::GetFramer(&connection_), packet_number_length_); |
| if (version().HasIetfInvariantHeader()) { |
| EXPECT_TRUE(QuicConnectionPeer::GetNoStopWaitingFrames(&connection_)); |
| } else { |
| QuicConnectionPeer::SetNoStopWaitingFrames(&connection_, |
| GetParam().no_stop_waiting); |
| } |
| QuicStreamId stream_id; |
| if (QuicVersionUsesCryptoFrames(version().transport_version)) { |
| stream_id = QuicUtils::GetFirstBidirectionalStreamId( |
| version().transport_version, Perspective::IS_CLIENT); |
| } else { |
| stream_id = QuicUtils::GetCryptoStreamId(version().transport_version); |
| } |
| frame1_.stream_id = stream_id; |
| frame2_.stream_id = stream_id; |
| connection_.set_visitor(&visitor_); |
| connection_.SetSessionNotifier(¬ifier_); |
| connection_.set_notifier(¬ifier_); |
| connection_.SetSendAlgorithm(send_algorithm_); |
| connection_.SetLossAlgorithm(loss_algorithm_.get()); |
| EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) |
| .Times(AnyNumber()); |
| EXPECT_CALL(*send_algorithm_, OnPacketNeutered(_)).Times(AnyNumber()); |
| EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) |
| .WillRepeatedly(Return(kDefaultTCPMSS)); |
| EXPECT_CALL(*send_algorithm_, PacingRate(_)) |
| .WillRepeatedly(Return(QuicBandwidth::Zero())); |
| EXPECT_CALL(*send_algorithm_, BandwidthEstimate()) |
| .Times(AnyNumber()) |
| .WillRepeatedly(Return(QuicBandwidth::Zero())); |
| EXPECT_CALL(*send_algorithm_, PopulateConnectionStats(_)) |
| .Times(AnyNumber()); |
| EXPECT_CALL(*send_algorithm_, InSlowStart()).Times(AnyNumber()); |
| EXPECT_CALL(*send_algorithm_, InRecovery()).Times(AnyNumber()); |
| EXPECT_CALL(*send_algorithm_, GetCongestionControlType()) |
| .Times(AnyNumber()); |
| EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(AnyNumber()); |
| EXPECT_CALL(*send_algorithm_, GetCongestionControlType()) |
| .Times(AnyNumber()); |
| EXPECT_CALL(visitor_, WillingAndAbleToWrite()).Times(AnyNumber()); |
| EXPECT_CALL(visitor_, OnPacketDecrypted(_)).Times(AnyNumber()); |
| EXPECT_CALL(visitor_, OnCanWrite()) |
| .WillRepeatedly(Invoke(¬ifier_, &SimpleSessionNotifier::OnCanWrite)); |
| EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) |
| .WillRepeatedly(Return(false)); |
| EXPECT_CALL(visitor_, OnCongestionWindowChange(_)).Times(AnyNumber()); |
| EXPECT_CALL(visitor_, OnPacketReceived(_, _, _)).Times(AnyNumber()); |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)).Times(AnyNumber()); |
| EXPECT_CALL(visitor_, OnOneRttPacketAcknowledged()) |
| .Times(testing::AtMost(1)); |
| EXPECT_CALL(*loss_algorithm_, GetLossTimeout()) |
| .WillRepeatedly(Return(QuicTime::Zero())); |
| EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) |
| .Times(AnyNumber()); |
| EXPECT_CALL(visitor_, GetHandshakeState()) |
| .WillRepeatedly(Return(HANDSHAKE_START)); |
| if (connection_.version().KnowsWhichDecrypterToUse()) { |
| connection_.InstallDecrypter( |
| ENCRYPTION_FORWARD_SECURE, |
| std::make_unique<NullDecrypter>(Perspective::IS_CLIENT)); |
| } |
| peer_creator_.SetDefaultPeerAddress(kSelfAddress); |
| } |
| |
| QuicConnectionTest(const QuicConnectionTest&) = delete; |
| QuicConnectionTest& operator=(const QuicConnectionTest&) = delete; |
| |
| ParsedQuicVersion version() { return GetParam().version; } |
| |
| QuicStopWaitingFrame* stop_waiting() { |
| QuicConnectionPeer::PopulateStopWaitingFrame(&connection_, &stop_waiting_); |
| return &stop_waiting_; |
| } |
| |
| QuicPacketNumber least_unacked() { |
| if (writer_->stop_waiting_frames().empty()) { |
| return QuicPacketNumber(); |
| } |
| return writer_->stop_waiting_frames()[0].least_unacked; |
| } |
| |
| void use_tagging_decrypter() { writer_->use_tagging_decrypter(); } |
| |
| void SetClientConnectionId(const QuicConnectionId& client_connection_id) { |
| connection_.set_client_connection_id(client_connection_id); |
| writer_->framer()->framer()->SetExpectedClientConnectionIdLength( |
| client_connection_id.length()); |
| } |
| |
| void SetDecrypter(EncryptionLevel level, |
| std::unique_ptr<QuicDecrypter> decrypter) { |
| if (connection_.version().KnowsWhichDecrypterToUse()) { |
| connection_.InstallDecrypter(level, std::move(decrypter)); |
| } else { |
| connection_.SetDecrypter(level, std::move(decrypter)); |
| } |
| } |
| |
| void ProcessPacket(uint64_t number) { |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(1); |
| ProcessDataPacket(number); |
| if (connection_.GetSendAlarm()->IsSet()) { |
| connection_.GetSendAlarm()->Fire(); |
| } |
| } |
| |
| void ProcessReceivedPacket(const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| const QuicReceivedPacket& packet) { |
| connection_.ProcessUdpPacket(self_address, peer_address, packet); |
| if (connection_.GetSendAlarm()->IsSet()) { |
| connection_.GetSendAlarm()->Fire(); |
| } |
| } |
| |
| QuicFrame MakeCryptoFrame() const { |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| return QuicFrame(new QuicCryptoFrame(crypto_frame_)); |
| } |
| return QuicFrame(QuicStreamFrame( |
| QuicUtils::GetCryptoStreamId(connection_.transport_version()), false, |
| 0u, absl::string_view())); |
| } |
| |
| void ProcessFramePacket(QuicFrame frame) { |
| ProcessFramePacketWithAddresses(frame, kSelfAddress, kPeerAddress, |
| ENCRYPTION_FORWARD_SECURE); |
| } |
| |
| void ProcessFramePacketWithAddresses(QuicFrame frame, |
| QuicSocketAddress self_address, |
| QuicSocketAddress peer_address, |
| EncryptionLevel level) { |
| QuicFrames frames; |
| frames.push_back(QuicFrame(frame)); |
| return ProcessFramesPacketWithAddresses(frames, self_address, peer_address, |
| level); |
| } |
| |
| std::unique_ptr<QuicReceivedPacket> ConstructPacket(QuicFrames frames, |
| EncryptionLevel level, |
| char* buffer, |
| size_t buffer_len) { |
| QUICHE_DCHECK(peer_framer_.HasEncrypterOfEncryptionLevel(level)); |
| peer_creator_.set_encryption_level(level); |
| QuicPacketCreatorPeer::SetSendVersionInPacket( |
| &peer_creator_, |
| level < ENCRYPTION_FORWARD_SECURE && |
| connection_.perspective() == Perspective::IS_SERVER); |
| |
| SerializedPacket serialized_packet = |
| QuicPacketCreatorPeer::SerializeAllFrames(&peer_creator_, frames, |
| buffer, buffer_len); |
| return std::make_unique<QuicReceivedPacket>( |
| serialized_packet.encrypted_buffer, serialized_packet.encrypted_length, |
| clock_.Now()); |
| } |
| |
| void ProcessFramesPacketWithAddresses(QuicFrames frames, |
| QuicSocketAddress self_address, |
| QuicSocketAddress peer_address, |
| EncryptionLevel level) { |
| char buffer[kMaxOutgoingPacketSize]; |
| connection_.ProcessUdpPacket( |
| self_address, peer_address, |
| *ConstructPacket(std::move(frames), level, buffer, |
| kMaxOutgoingPacketSize)); |
| if (connection_.GetSendAlarm()->IsSet()) { |
| connection_.GetSendAlarm()->Fire(); |
| } |
| } |
| |
| // Bypassing the packet creator is unrealistic, but allows us to process |
| // packets the QuicPacketCreator won't allow us to create. |
| void ForceProcessFramePacket(QuicFrame frame) { |
| QuicFrames frames; |
| frames.push_back(QuicFrame(frame)); |
| bool send_version = connection_.perspective() == Perspective::IS_SERVER; |
| if (connection_.version().KnowsWhichDecrypterToUse()) { |
| send_version = true; |
| } |
| QuicPacketCreatorPeer::SetSendVersionInPacket(&peer_creator_, send_version); |
| QuicPacketHeader header; |
| QuicPacketCreatorPeer::FillPacketHeader(&peer_creator_, &header); |
| char encrypted_buffer[kMaxOutgoingPacketSize]; |
| size_t length = peer_framer_.BuildDataPacket( |
| header, frames, encrypted_buffer, kMaxOutgoingPacketSize, |
| ENCRYPTION_INITIAL); |
| QUICHE_DCHECK_GT(length, 0u); |
| |
| const size_t encrypted_length = peer_framer_.EncryptInPlace( |
| ENCRYPTION_INITIAL, header.packet_number, |
| GetStartOfEncryptedData(peer_framer_.version().transport_version, |
| header), |
| length, kMaxOutgoingPacketSize, encrypted_buffer); |
| QUICHE_DCHECK_GT(encrypted_length, 0u); |
| |
| connection_.ProcessUdpPacket( |
| kSelfAddress, kPeerAddress, |
| QuicReceivedPacket(encrypted_buffer, encrypted_length, clock_.Now())); |
| } |
| |
| size_t ProcessFramePacketAtLevel(uint64_t number, QuicFrame frame, |
| EncryptionLevel level) { |
| QuicFrames frames; |
| frames.push_back(frame); |
| return ProcessFramesPacketAtLevel(number, frames, level); |
| } |
| |
| size_t ProcessFramesPacketAtLevel(uint64_t number, const QuicFrames& frames, |
| EncryptionLevel level) { |
| QuicPacketHeader header = ConstructPacketHeader(number, level); |
| // Set the correct encryption level and encrypter on peer_creator and |
| // peer_framer, respectively. |
| peer_creator_.set_encryption_level(level); |
| if (QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_) > |
| ENCRYPTION_INITIAL) { |
| peer_framer_.SetEncrypter( |
| QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_), |
| std::make_unique<TaggingEncrypter>(0x01)); |
| // Set the corresponding decrypter. |
| if (connection_.version().KnowsWhichDecrypterToUse()) { |
| connection_.InstallDecrypter( |
| QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_), |
| std::make_unique<StrictTaggingDecrypter>(0x01)); |
| } else { |
| connection_.SetDecrypter( |
| QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_), |
| std::make_unique<StrictTaggingDecrypter>(0x01)); |
| } |
| } |
| std::unique_ptr<QuicPacket> packet(ConstructPacket(header, frames)); |
| |
| char buffer[kMaxOutgoingPacketSize]; |
| size_t encrypted_length = |
| peer_framer_.EncryptPayload(level, QuicPacketNumber(number), *packet, |
| buffer, kMaxOutgoingPacketSize); |
| connection_.ProcessUdpPacket( |
| kSelfAddress, kPeerAddress, |
| QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false)); |
| if (connection_.GetSendAlarm()->IsSet()) { |
| connection_.GetSendAlarm()->Fire(); |
| } |
| return encrypted_length; |
| } |
| |
| struct PacketInfo { |
| PacketInfo(uint64_t packet_number, QuicFrames frames, EncryptionLevel level) |
| : packet_number(packet_number), frames(frames), level(level) {} |
| |
| uint64_t packet_number; |
| QuicFrames frames; |
| EncryptionLevel level; |
| }; |
| |
| size_t ProcessCoalescedPacket(std::vector<PacketInfo> packets) { |
| char coalesced_buffer[kMaxOutgoingPacketSize]; |
| size_t coalesced_size = 0; |
| bool contains_initial = false; |
| for (const auto& packet : packets) { |
| QuicPacketHeader header = |
| ConstructPacketHeader(packet.packet_number, packet.level); |
| // Set the correct encryption level and encrypter on peer_creator and |
| // peer_framer, respectively. |
| peer_creator_.set_encryption_level(packet.level); |
| if (packet.level == ENCRYPTION_INITIAL) { |
| contains_initial = true; |
| } |
| if (QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_) > |
| ENCRYPTION_INITIAL) { |
| peer_framer_.SetEncrypter( |
| QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_), |
| std::make_unique<TaggingEncrypter>(0x01)); |
| // Set the corresponding decrypter. |
| if (connection_.version().KnowsWhichDecrypterToUse()) { |
| connection_.InstallDecrypter( |
| QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_), |
| std::make_unique<StrictTaggingDecrypter>(0x01)); |
| } else { |
| connection_.SetDecrypter( |
| QuicPacketCreatorPeer::GetEncryptionLevel(&peer_creator_), |
| std::make_unique<StrictTaggingDecrypter>(0x01)); |
| } |
| } |
| std::unique_ptr<QuicPacket> constructed_packet( |
| ConstructPacket(header, packet.frames)); |
| |
| char buffer[kMaxOutgoingPacketSize]; |
| size_t encrypted_length = peer_framer_.EncryptPayload( |
| packet.level, QuicPacketNumber(packet.packet_number), |
| *constructed_packet, buffer, kMaxOutgoingPacketSize); |
| QUICHE_DCHECK_LE(coalesced_size + encrypted_length, |
| kMaxOutgoingPacketSize); |
| memcpy(coalesced_buffer + coalesced_size, buffer, encrypted_length); |
| coalesced_size += encrypted_length; |
| } |
| if (contains_initial) { |
| // Padded coalesced packet to full if it contains initial packet. |
| memset(coalesced_buffer + coalesced_size, '0', |
| kMaxOutgoingPacketSize - coalesced_size); |
| } |
| connection_.ProcessUdpPacket( |
| kSelfAddress, kPeerAddress, |
| QuicReceivedPacket(coalesced_buffer, coalesced_size, clock_.Now(), |
| false)); |
| if (connection_.GetSendAlarm()->IsSet()) { |
| connection_.GetSendAlarm()->Fire(); |
| } |
| return coalesced_size; |
| } |
| |
| size_t ProcessDataPacket(uint64_t number) { |
| return ProcessDataPacketAtLevel(number, false, ENCRYPTION_FORWARD_SECURE); |
| } |
| |
| size_t ProcessDataPacket(QuicPacketNumber packet_number) { |
| return ProcessDataPacketAtLevel(packet_number, false, |
| ENCRYPTION_FORWARD_SECURE); |
| } |
| |
| size_t ProcessDataPacketAtLevel(QuicPacketNumber packet_number, |
| bool has_stop_waiting, |
| EncryptionLevel level) { |
| return ProcessDataPacketAtLevel(packet_number.ToUint64(), has_stop_waiting, |
| level); |
| } |
| |
| size_t ProcessCryptoPacketAtLevel(uint64_t number, EncryptionLevel level) { |
| QuicPacketHeader header = ConstructPacketHeader(number, level); |
| QuicFrames frames; |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| frames.push_back(QuicFrame(&crypto_frame_)); |
| } else { |
| frames.push_back(QuicFrame(frame1_)); |
| } |
| if (level == ENCRYPTION_INITIAL) { |
| frames.push_back(QuicFrame(QuicPaddingFrame(-1))); |
| } |
| std::unique_ptr<QuicPacket> packet = ConstructPacket(header, frames); |
| char buffer[kMaxOutgoingPacketSize]; |
| peer_creator_.set_encryption_level(level); |
| size_t encrypted_length = |
| peer_framer_.EncryptPayload(level, QuicPacketNumber(number), *packet, |
| buffer, kMaxOutgoingPacketSize); |
| connection_.ProcessUdpPacket( |
| kSelfAddress, kPeerAddress, |
| QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false)); |
| if (connection_.GetSendAlarm()->IsSet()) { |
| connection_.GetSendAlarm()->Fire(); |
| } |
| return encrypted_length; |
| } |
| |
| size_t ProcessDataPacketAtLevel(uint64_t number, bool has_stop_waiting, |
| EncryptionLevel level) { |
| std::unique_ptr<QuicPacket> packet( |
| ConstructDataPacket(number, has_stop_waiting, level)); |
| char buffer[kMaxOutgoingPacketSize]; |
| peer_creator_.set_encryption_level(level); |
| size_t encrypted_length = |
| peer_framer_.EncryptPayload(level, QuicPacketNumber(number), *packet, |
| buffer, kMaxOutgoingPacketSize); |
| connection_.ProcessUdpPacket( |
| kSelfAddress, kPeerAddress, |
| QuicReceivedPacket(buffer, encrypted_length, clock_.Now(), false)); |
| if (connection_.GetSendAlarm()->IsSet()) { |
| connection_.GetSendAlarm()->Fire(); |
| } |
| return encrypted_length; |
| } |
| |
| void ProcessClosePacket(uint64_t number) { |
| std::unique_ptr<QuicPacket> packet(ConstructClosePacket(number)); |
| char buffer[kMaxOutgoingPacketSize]; |
| size_t encrypted_length = peer_framer_.EncryptPayload( |
| ENCRYPTION_FORWARD_SECURE, QuicPacketNumber(number), *packet, buffer, |
| kMaxOutgoingPacketSize); |
| connection_.ProcessUdpPacket( |
| kSelfAddress, kPeerAddress, |
| QuicReceivedPacket(buffer, encrypted_length, QuicTime::Zero(), false)); |
| } |
| |
| QuicByteCount SendStreamDataToPeer(QuicStreamId id, absl::string_view data, |
| QuicStreamOffset offset, |
| StreamSendingState state, |
| QuicPacketNumber* last_packet) { |
| QuicByteCount packet_size; |
| // Save the last packet's size. |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) |
| .Times(AnyNumber()) |
| .WillRepeatedly(SaveArg<3>(&packet_size)); |
| connection_.SendStreamDataWithString(id, data, offset, state); |
| if (last_packet != nullptr) { |
| *last_packet = creator_->packet_number(); |
| } |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) |
| .Times(AnyNumber()); |
| return packet_size; |
| } |
| |
| void SendAckPacketToPeer() { |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); |
| { |
| QuicConnection::ScopedPacketFlusher flusher(&connection_); |
| connection_.SendAck(); |
| } |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) |
| .Times(AnyNumber()); |
| } |
| |
| void SendRstStream(QuicStreamId id, QuicRstStreamErrorCode error, |
| QuicStreamOffset bytes_written) { |
| notifier_.WriteOrBufferRstStream(id, error, bytes_written); |
| connection_.OnStreamReset(id, error); |
| } |
| |
| void SendPing() { notifier_.WriteOrBufferPing(); } |
| |
| MessageStatus SendMessage(absl::string_view message) { |
| connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| QuicMemSlice slice(QuicBuffer::Copy( |
| connection_.helper()->GetStreamSendBufferAllocator(), message)); |
| return connection_.SendMessage(1, absl::MakeSpan(&slice, 1), false); |
| } |
| |
| void ProcessAckPacket(uint64_t packet_number, QuicAckFrame* frame) { |
| if (packet_number > 1) { |
| QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, packet_number - 1); |
| } else { |
| QuicPacketCreatorPeer::ClearPacketNumber(&peer_creator_); |
| } |
| ProcessFramePacket(QuicFrame(frame)); |
| } |
| |
| void ProcessAckPacket(QuicAckFrame* frame) { |
| ProcessFramePacket(QuicFrame(frame)); |
| } |
| |
| void ProcessStopWaitingPacket(QuicStopWaitingFrame frame) { |
| ProcessFramePacket(QuicFrame(frame)); |
| } |
| |
| size_t ProcessStopWaitingPacketAtLevel(uint64_t number, |
| QuicStopWaitingFrame frame, |
| EncryptionLevel /*level*/) { |
| return ProcessFramePacketAtLevel(number, QuicFrame(frame), |
| ENCRYPTION_ZERO_RTT); |
| } |
| |
| void ProcessGoAwayPacket(QuicGoAwayFrame* frame) { |
| ProcessFramePacket(QuicFrame(frame)); |
| } |
| |
| bool IsMissing(uint64_t number) { |
| return IsAwaitingPacket(connection_.ack_frame(), QuicPacketNumber(number), |
| QuicPacketNumber()); |
| } |
| |
| std::unique_ptr<QuicPacket> ConstructPacket(const QuicPacketHeader& header, |
| const QuicFrames& frames) { |
| auto packet = BuildUnsizedDataPacket(&peer_framer_, header, frames); |
| EXPECT_NE(nullptr, packet.get()); |
| return packet; |
| } |
| |
| QuicPacketHeader ConstructPacketHeader(uint64_t number, |
| EncryptionLevel level) { |
| QuicPacketHeader header; |
| if (peer_framer_.version().HasIetfInvariantHeader() && |
| level < ENCRYPTION_FORWARD_SECURE) { |
| // Set long header type accordingly. |
| header.version_flag = true; |
| header.form = IETF_QUIC_LONG_HEADER_PACKET; |
| header.long_packet_type = EncryptionlevelToLongHeaderType(level); |
| if (QuicVersionHasLongHeaderLengths( |
| peer_framer_.version().transport_version)) { |
| header.length_length = VARIABLE_LENGTH_INTEGER_LENGTH_2; |
| if (header.long_packet_type == INITIAL) { |
| header.retry_token_length_length = VARIABLE_LENGTH_INTEGER_LENGTH_1; |
| } |
| } |
| } |
| // Set connection_id to peer's in memory representation as this data packet |
| // is created by peer_framer. |
| if (peer_framer_.perspective() == Perspective::IS_SERVER) { |
| header.source_connection_id = connection_id_; |
| header.source_connection_id_included = connection_id_included_; |
| header.destination_connection_id_included = CONNECTION_ID_ABSENT; |
| } else { |
| header.destination_connection_id = connection_id_; |
| header.destination_connection_id_included = connection_id_included_; |
| } |
| if (peer_framer_.version().HasIetfInvariantHeader() && |
| peer_framer_.perspective() == Perspective::IS_SERVER) { |
| if (!connection_.client_connection_id().IsEmpty()) { |
| header.destination_connection_id = connection_.client_connection_id(); |
| header.destination_connection_id_included = CONNECTION_ID_PRESENT; |
| } else { |
| header.destination_connection_id_included = CONNECTION_ID_ABSENT; |
| } |
| if (header.version_flag) { |
| header.source_connection_id = connection_id_; |
| header.source_connection_id_included = CONNECTION_ID_PRESENT; |
| if (GetParam().version.handshake_protocol == PROTOCOL_QUIC_CRYPTO && |
| header.long_packet_type == ZERO_RTT_PROTECTED) { |
| header.nonce = &kTestDiversificationNonce; |
| } |
| } |
| } |
| header.packet_number_length = packet_number_length_; |
| header.packet_number = QuicPacketNumber(number); |
| return header; |
| } |
| |
| std::unique_ptr<QuicPacket> ConstructDataPacket(uint64_t number, |
| bool has_stop_waiting, |
| EncryptionLevel level) { |
| QuicPacketHeader header = ConstructPacketHeader(number, level); |
| QuicFrames frames; |
| if (VersionHasIetfQuicFrames(version().transport_version) && |
| (level == ENCRYPTION_INITIAL || level == ENCRYPTION_HANDSHAKE)) { |
| frames.push_back(QuicFrame(QuicPingFrame())); |
| frames.push_back(QuicFrame(QuicPaddingFrame(100))); |
| } else { |
| frames.push_back(QuicFrame(frame1_)); |
| if (has_stop_waiting) { |
| frames.push_back(QuicFrame(stop_waiting_)); |
| } |
| } |
| return ConstructPacket(header, frames); |
| } |
| |
| std::unique_ptr<SerializedPacket> ConstructProbingPacket() { |
| peer_creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| if (VersionHasIetfQuicFrames(version().transport_version)) { |
| QuicPathFrameBuffer payload = { |
| {0xde, 0xad, 0xbe, 0xef, 0xba, 0xdc, 0x0f, 0xfe}}; |
| return QuicPacketCreatorPeer:: |
| SerializePathChallengeConnectivityProbingPacket(&peer_creator_, |
| payload); |
| } |
| return QuicPacketCreatorPeer::SerializeConnectivityProbingPacket( |
| &peer_creator_); |
| } |
| |
| std::unique_ptr<QuicPacket> ConstructClosePacket(uint64_t number) { |
| peer_creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| QuicPacketHeader header; |
| // Set connection_id to peer's in memory representation as this connection |
| // close packet is created by peer_framer. |
| if (peer_framer_.perspective() == Perspective::IS_SERVER) { |
| header.source_connection_id = connection_id_; |
| header.destination_connection_id_included = CONNECTION_ID_ABSENT; |
| if (!peer_framer_.version().HasIetfInvariantHeader()) { |
| header.source_connection_id_included = CONNECTION_ID_PRESENT; |
| } |
| } else { |
| header.destination_connection_id = connection_id_; |
| if (peer_framer_.version().HasIetfInvariantHeader()) { |
| header.destination_connection_id_included = CONNECTION_ID_ABSENT; |
| } |
| } |
| |
| header.packet_number = QuicPacketNumber(number); |
| |
| QuicErrorCode kQuicErrorCode = QUIC_PEER_GOING_AWAY; |
| QuicConnectionCloseFrame qccf(peer_framer_.transport_version(), |
| kQuicErrorCode, NO_IETF_QUIC_ERROR, "", |
| /*transport_close_frame_type=*/0); |
| QuicFrames frames; |
| frames.push_back(QuicFrame(&qccf)); |
| return ConstructPacket(header, frames); |
| } |
| |
| QuicTime::Delta DefaultRetransmissionTime() { |
| return QuicTime::Delta::FromMilliseconds(kDefaultRetransmissionTimeMs); |
| } |
| |
| QuicTime::Delta DefaultDelayedAckTime() { |
| return QuicTime::Delta::FromMilliseconds(kDefaultDelayedAckTimeMs); |
| } |
| |
| const QuicStopWaitingFrame InitStopWaitingFrame(uint64_t least_unacked) { |
| QuicStopWaitingFrame frame; |
| frame.least_unacked = QuicPacketNumber(least_unacked); |
| return frame; |
| } |
| |
| // Construct a ack_frame that acks all packet numbers between 1 and |
| // |largest_acked|, except |missing|. |
| // REQUIRES: 1 <= |missing| < |largest_acked| |
| QuicAckFrame ConstructAckFrame(uint64_t largest_acked, uint64_t missing) { |
| return ConstructAckFrame(QuicPacketNumber(largest_acked), |
| QuicPacketNumber(missing)); |
| } |
| |
| QuicAckFrame ConstructAckFrame(QuicPacketNumber largest_acked, |
| QuicPacketNumber missing) { |
| if (missing == QuicPacketNumber(1)) { |
| return InitAckFrame({{missing + 1, largest_acked + 1}}); |
| } |
| return InitAckFrame( |
| {{QuicPacketNumber(1), missing}, {missing + 1, largest_acked + 1}}); |
| } |
| |
| // Undo nacking a packet within the frame. |
| void AckPacket(QuicPacketNumber arrived, QuicAckFrame* frame) { |
| EXPECT_FALSE(frame->packets.Contains(arrived)); |
| frame->packets.Add(arrived); |
| } |
| |
| void TriggerConnectionClose() { |
| // Send an erroneous packet to close the connection. |
| EXPECT_CALL(visitor_, |
| OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)) |
| .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame)); |
| |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| // Triggers a connection by receiving ACK of unsent packet. |
| QuicAckFrame frame = InitAckFrame(10000); |
| ProcessAckPacket(1, &frame); |
| EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) == |
| nullptr); |
| EXPECT_EQ(1, connection_close_frame_count_); |
| EXPECT_THAT(saved_connection_close_frame_.quic_error_code, |
| IsError(QUIC_INVALID_ACK_DATA)); |
| } |
| |
| void BlockOnNextWrite() { |
| writer_->BlockOnNextWrite(); |
| EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AtLeast(1)); |
| } |
| |
| void SimulateNextPacketTooLarge() { writer_->SimulateNextPacketTooLarge(); } |
| |
| void AlwaysGetPacketTooLarge() { writer_->AlwaysGetPacketTooLarge(); } |
| |
| void SetWritePauseTimeDelta(QuicTime::Delta delta) { |
| writer_->SetWritePauseTimeDelta(delta); |
| } |
| |
| void CongestionBlockWrites() { |
| EXPECT_CALL(*send_algorithm_, CanSend(_)) |
| .WillRepeatedly(testing::Return(false)); |
| } |
| |
| void CongestionUnblockWrites() { |
| EXPECT_CALL(*send_algorithm_, CanSend(_)) |
| .WillRepeatedly(testing::Return(true)); |
| } |
| |
| void set_perspective(Perspective perspective) { |
| connection_.set_perspective(perspective); |
| if (perspective == Perspective::IS_SERVER) { |
| QuicConfig config; |
| if (!GetQuicReloadableFlag( |
| quic_remove_connection_migration_connection_option)) { |
| QuicTagVector connection_options; |
| connection_options.push_back(kRVCM); |
| config.SetInitialReceivedConnectionOptions(connection_options); |
| } |
| EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); |
| connection_.SetFromConfig(config); |
| |
| connection_.set_can_truncate_connection_ids(true); |
| QuicConnectionPeer::SetNegotiatedVersion(&connection_); |
| connection_.OnSuccessfulVersionNegotiation(); |
| } |
| QuicFramerPeer::SetPerspective(&peer_framer_, |
| QuicUtils::InvertPerspective(perspective)); |
| peer_framer_.SetInitialObfuscators(TestConnectionId()); |
| for (EncryptionLevel level : {ENCRYPTION_ZERO_RTT, ENCRYPTION_HANDSHAKE, |
| ENCRYPTION_FORWARD_SECURE}) { |
| if (peer_framer_.HasEncrypterOfEncryptionLevel(level)) { |
| peer_creator_.SetEncrypter( |
| level, std::make_unique<NullEncrypter>(peer_framer_.perspective())); |
| } |
| } |
| } |
| |
| void set_packets_between_probes_base( |
| const QuicPacketCount packets_between_probes_base) { |
| QuicConnectionPeer::ReInitializeMtuDiscoverer( |
| &connection_, packets_between_probes_base, |
| QuicPacketNumber(packets_between_probes_base)); |
| } |
| |
| bool IsDefaultTestConfiguration() { |
| TestParams p = GetParam(); |
| return p.ack_response == AckResponse::kImmediate && |
| p.version == AllSupportedVersions()[0] && p.no_stop_waiting; |
| } |
| |
| void TestConnectionCloseQuicErrorCode(QuicErrorCode expected_code) { |
| // Not strictly needed for this test, but is commonly done. |
| EXPECT_FALSE(QuicConnectionPeer::GetConnectionClosePacket(&connection_) == |
| nullptr); |
| const std::vector<QuicConnectionCloseFrame>& connection_close_frames = |
| writer_->connection_close_frames(); |
| ASSERT_EQ(1u, connection_close_frames.size()); |
| |
| EXPECT_THAT(connection_close_frames[0].quic_error_code, |
| IsError(expected_code)); |
| |
| if (!VersionHasIetfQuicFrames(version().transport_version)) { |
| EXPECT_THAT(connection_close_frames[0].wire_error_code, |
| IsError(expected_code)); |
| EXPECT_EQ(GOOGLE_QUIC_CONNECTION_CLOSE, |
| connection_close_frames[0].close_type); |
| return; |
| } |
| |
| QuicErrorCodeToIetfMapping mapping = |
| QuicErrorCodeToTransportErrorCode(expected_code); |
| |
| if (mapping.is_transport_close) { |
| // This Google QUIC Error Code maps to a transport close, |
| EXPECT_EQ(IETF_QUIC_TRANSPORT_CONNECTION_CLOSE, |
| connection_close_frames[0].close_type); |
| } else { |
| // This maps to an application close. |
| EXPECT_EQ(IETF_QUIC_APPLICATION_CONNECTION_CLOSE, |
| connection_close_frames[0].close_type); |
| } |
| EXPECT_EQ(mapping.error_code, connection_close_frames[0].wire_error_code); |
| } |
| |
| void MtuDiscoveryTestInit() { |
| set_perspective(Perspective::IS_SERVER); |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| if (version().SupportsAntiAmplificationLimit()) { |
| QuicConnectionPeer::SetAddressValidated(&connection_); |
| } |
| connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| peer_creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| // QuicFramer::GetMaxPlaintextSize uses the smallest max plaintext size |
| // across all encrypters. The initial encrypter used with IETF QUIC has a |
| // 16-byte overhead, while the NullEncrypter used throughout this test has a |
| // 12-byte overhead. This test tests behavior that relies on computing the |
| // packet size correctly, so by unsetting the initial encrypter, we avoid |
| // having a mismatch between the overheads for the encrypters used. In |
| // non-test scenarios all encrypters used for a given connection have the |
| // same overhead, either 12 bytes for ones using Google QUIC crypto, or 16 |
| // bytes for ones using TLS. |
| connection_.SetEncrypter(ENCRYPTION_INITIAL, nullptr); |
| // Prevent packets from being coalesced. |
| EXPECT_CALL(visitor_, GetHandshakeState()) |
| .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); |
| EXPECT_TRUE(connection_.connected()); |
| } |
| |
| void PathProbeTestInit(Perspective perspective, |
| bool receive_new_server_connection_id = true) { |
| set_perspective(perspective); |
| connection_.CreateConnectionIdManager(); |
| EXPECT_EQ(connection_.perspective(), perspective); |
| if (perspective == Perspective::IS_SERVER) { |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| } |
| connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| peer_creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); |
| // Discard INITIAL key. |
| connection_.RemoveEncrypter(ENCRYPTION_INITIAL); |
| connection_.NeuterUnencryptedPackets(); |
| // Prevent packets from being coalesced. |
| EXPECT_CALL(visitor_, GetHandshakeState()) |
| .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); |
| if (version().SupportsAntiAmplificationLimit() && |
| perspective == Perspective::IS_SERVER) { |
| QuicConnectionPeer::SetAddressValidated(&connection_); |
| } |
| // 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()); |
| |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); |
| } else { |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); |
| } |
| QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 2); |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, |
| kPeerAddress, ENCRYPTION_FORWARD_SECURE); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| if (perspective == Perspective::IS_CLIENT && |
| receive_new_server_connection_id && version().HasIetfQuicFrames()) { |
| QuicNewConnectionIdFrame frame; |
| frame.connection_id = TestConnectionId(1234); |
| ASSERT_NE(frame.connection_id, connection_.connection_id()); |
| frame.stateless_reset_token = |
| QuicUtils::GenerateStatelessResetToken(frame.connection_id); |
| frame.retire_prior_to = 0u; |
| frame.sequence_number = 1u; |
| connection_.OnNewConnectionIdFrame(frame); |
| } |
| } |
| |
| void TestClientRetryHandling(bool invalid_retry_tag, |
| bool missing_original_id_in_config, |
| bool wrong_original_id_in_config, |
| bool missing_retry_id_in_config, |
| bool wrong_retry_id_in_config); |
| |
| void TestReplaceConnectionIdFromInitial(); |
| |
| QuicConnectionId connection_id_; |
| QuicFramer framer_; |
| |
| MockSendAlgorithm* send_algorithm_; |
| std::unique_ptr<MockLossAlgorithm> loss_algorithm_; |
| MockClock clock_; |
| MockRandom random_generator_; |
| SimpleBufferAllocator buffer_allocator_; |
| std::unique_ptr<TestConnectionHelper> helper_; |
| std::unique_ptr<TestAlarmFactory> alarm_factory_; |
| QuicFramer peer_framer_; |
| QuicPacketCreator peer_creator_; |
| std::unique_ptr<TestPacketWriter> writer_; |
| TestConnection connection_; |
| QuicPacketCreator* creator_; |
| QuicSentPacketManager* manager_; |
| StrictMock<MockQuicConnectionVisitor> visitor_; |
| |
| QuicStreamFrame frame1_; |
| QuicStreamFrame frame2_; |
| QuicCryptoFrame crypto_frame_; |
| QuicAckFrame ack_; |
| QuicStopWaitingFrame stop_waiting_; |
| QuicPacketNumberLength packet_number_length_; |
| QuicConnectionIdIncluded connection_id_included_; |
| |
| SimpleSessionNotifier notifier_; |
| |
| QuicConnectionCloseFrame saved_connection_close_frame_; |
| int connection_close_frame_count_; |
| }; |
| |
| // Run all end to end tests with all supported versions. |
| INSTANTIATE_TEST_SUITE_P(QuicConnectionTests, QuicConnectionTest, |
| ::testing::ValuesIn(GetTestParams()), |
| ::testing::PrintToStringParamName()); |
| |
| // These two tests ensure that the QuicErrorCode mapping works correctly. |
| // Both tests expect to see a Google QUIC close if not running IETF QUIC. |
| // If running IETF QUIC, the first will generate a transport connection |
| // close, the second an application connection close. |
| // The connection close codes for the two tests are manually chosen; |
| // they are expected to always map to transport- and application- |
| // closes, respectively. If that changes, new codes should be chosen. |
| TEST_P(QuicConnectionTest, CloseErrorCodeTestTransport) { |
| EXPECT_TRUE(connection_.connected()); |
| EXPECT_CALL(visitor_, OnConnectionClosed(_, _)); |
| connection_.CloseConnection( |
| IETF_QUIC_PROTOCOL_VIOLATION, "Should be transport close", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| EXPECT_FALSE(connection_.connected()); |
| TestConnectionCloseQuicErrorCode(IETF_QUIC_PROTOCOL_VIOLATION); |
| } |
| |
| // Test that the IETF QUIC Error code mapping function works |
| // properly for application connection close codes. |
| TEST_P(QuicConnectionTest, CloseErrorCodeTestApplication) { |
| EXPECT_TRUE(connection_.connected()); |
| EXPECT_CALL(visitor_, OnConnectionClosed(_, _)); |
| connection_.CloseConnection( |
| QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE, |
| "Should be application close", |
| ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| EXPECT_FALSE(connection_.connected()); |
| TestConnectionCloseQuicErrorCode(QUIC_HEADERS_STREAM_DATA_DECOMPRESS_FAILURE); |
| } |
| |
| TEST_P(QuicConnectionTest, SelfAddressChangeAtClient) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| |
| EXPECT_EQ(Perspective::IS_CLIENT, connection_.perspective()); |
| EXPECT_TRUE(connection_.connected()); |
| |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)); |
| } else { |
| EXPECT_CALL(visitor_, OnStreamFrame(_)); |
| } |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, kPeerAddress, |
| ENCRYPTION_INITIAL); |
| // Cause change in self_address. |
| QuicIpAddress host; |
| host.FromString("1.1.1.1"); |
| QuicSocketAddress self_address(host, 123); |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)); |
| } else { |
| EXPECT_CALL(visitor_, OnStreamFrame(_)); |
| } |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), self_address, kPeerAddress, |
| ENCRYPTION_INITIAL); |
| EXPECT_TRUE(connection_.connected()); |
| } |
| |
| TEST_P(QuicConnectionTest, SelfAddressChangeAtServer) { |
| set_perspective(Perspective::IS_SERVER); |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| EXPECT_TRUE(connection_.connected()); |
| |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)); |
| } else { |
| EXPECT_CALL(visitor_, OnStreamFrame(_)); |
| } |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, kPeerAddress, |
| ENCRYPTION_INITIAL); |
| // Cause change in self_address. |
| QuicIpAddress host; |
| host.FromString("1.1.1.1"); |
| QuicSocketAddress self_address(host, 123); |
| EXPECT_CALL(visitor_, AllowSelfAddressChange()).WillOnce(Return(false)); |
| if (version().handshake_protocol == PROTOCOL_TLS1_3) { |
| EXPECT_CALL(visitor_, BeforeConnectionCloseSent()); |
| } |
| EXPECT_CALL(visitor_, OnConnectionClosed(_, _)); |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), self_address, kPeerAddress, |
| ENCRYPTION_INITIAL); |
| EXPECT_FALSE(connection_.connected()); |
| TestConnectionCloseQuicErrorCode(QUIC_ERROR_MIGRATING_ADDRESS); |
| } |
| |
| TEST_P(QuicConnectionTest, AllowSelfAddressChangeToMappedIpv4AddressAtServer) { |
| set_perspective(Perspective::IS_SERVER); |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| EXPECT_TRUE(connection_.connected()); |
| |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(3); |
| } else { |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(3); |
| } |
| QuicIpAddress host; |
| host.FromString("1.1.1.1"); |
| QuicSocketAddress self_address1(host, 443); |
| connection_.SetSelfAddress(self_address1); |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), self_address1, |
| kPeerAddress, ENCRYPTION_INITIAL); |
| // Cause self_address change to mapped Ipv4 address. |
| QuicIpAddress host2; |
| host2.FromString( |
| absl::StrCat("::ffff:", connection_.self_address().host().ToString())); |
| QuicSocketAddress self_address2(host2, connection_.self_address().port()); |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), self_address2, |
| kPeerAddress, ENCRYPTION_INITIAL); |
| EXPECT_TRUE(connection_.connected()); |
| // self_address change back to Ipv4 address. |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), self_address1, |
| kPeerAddress, ENCRYPTION_INITIAL); |
| EXPECT_TRUE(connection_.connected()); |
| } |
| |
| TEST_P(QuicConnectionTest, ClientAddressChangeAndPacketReordered) { |
| set_perspective(Perspective::IS_SERVER); |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| EXPECT_CALL(visitor_, GetHandshakeState()) |
| .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); |
| |
| // 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()); |
| |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); |
| } else { |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); |
| } |
| QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 5); |
| const QuicSocketAddress kNewPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), |
| /*port=*/23456); |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, |
| kNewPeerAddress, ENCRYPTION_INITIAL); |
| EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); |
| |
| // Decrease packet number to simulate out-of-order packets. |
| QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 4); |
| // This is an old packet, do not migrate. |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, kPeerAddress, |
| ENCRYPTION_INITIAL); |
| EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| TEST_P(QuicConnectionTest, PeerPortChangeAtServer) { |
| set_perspective(Perspective::IS_SERVER); |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| // Prevent packets from being coalesced. |
| EXPECT_CALL(visitor_, GetHandshakeState()) |
| .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); |
| if (version().SupportsAntiAmplificationLimit()) { |
| QuicConnectionPeer::SetAddressValidated(&connection_); |
| } |
| |
| // 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()); |
| |
| RttStats* rtt_stats = const_cast<RttStats*>(manager_->GetRttStats()); |
| QuicTime::Delta default_init_rtt = rtt_stats->initial_rtt(); |
| rtt_stats->set_initial_rtt(default_init_rtt * 2); |
| EXPECT_EQ(2 * default_init_rtt, rtt_stats->initial_rtt()); |
| |
| QuicSentPacketManagerPeer::SetConsecutiveRtoCount(manager_, 1); |
| EXPECT_EQ(1u, manager_->GetConsecutiveRtoCount()); |
| QuicSentPacketManagerPeer::SetConsecutiveTlpCount(manager_, 2); |
| EXPECT_EQ(2u, manager_->GetConsecutiveTlpCount()); |
| |
| const QuicSocketAddress kNewPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); |
| EXPECT_CALL(visitor_, OnStreamFrame(_)) |
| .WillOnce(Invoke( |
| [=]() { EXPECT_EQ(kPeerAddress, connection_.peer_address()); })) |
| .WillOnce(Invoke( |
| [=]() { EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); })); |
| 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()); |
| |
| // Process another packet with a different peer address on server side will |
| // start connection migration. |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); |
| 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()); |
| // PORT_CHANGE shouldn't state change in sent packet manager. |
| EXPECT_EQ(2 * default_init_rtt, rtt_stats->initial_rtt()); |
| EXPECT_EQ(1u, manager_->GetConsecutiveRtoCount()); |
| EXPECT_EQ(2u, manager_->GetConsecutiveTlpCount()); |
| EXPECT_EQ(manager_->GetSendAlgorithm(), send_algorithm_); |
| if (connection_.validate_client_address()) { |
| EXPECT_EQ(NO_CHANGE, connection_.active_effective_peer_migration_type()); |
| EXPECT_EQ(1u, connection_.GetStats().num_validated_peer_migration); |
| } |
| } |
| |
| TEST_P(QuicConnectionTest, PeerIpAddressChangeAtServer) { |
| set_perspective(Perspective::IS_SERVER); |
| if (!connection_.validate_client_address()) { |
| return; |
| } |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| // Discard INITIAL key. |
| connection_.RemoveEncrypter(ENCRYPTION_INITIAL); |
| connection_.NeuterUnencryptedPackets(); |
| // Prevent packets from being coalesced. |
| EXPECT_CALL(visitor_, GetHandshakeState()) |
| .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); |
| QuicConnectionPeer::SetAddressValidated(&connection_); |
| connection_.OnHandshakeComplete(); |
| |
| // Enable 5 RTO |
| QuicConfig config; |
| QuicTagVector connection_options; |
| connection_options.push_back(k5RTO); |
| config.SetInitialReceivedConnectionOptions(connection_options); |
| QuicConfigPeer::SetNegotiated(&config, true); |
| QuicConfigPeer::SetReceivedOriginalConnectionId(&config, |
| connection_.connection_id()); |
| QuicConfigPeer::SetReceivedInitialSourceConnectionId(&config, |
| QuicConnectionId()); |
| EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); |
| connection_.SetFromConfig(config); |
| |
| // 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(_)) |
| .WillOnce(Invoke( |
| [=]() { EXPECT_EQ(kPeerAddress, connection_.peer_address()); })) |
| .WillOnce(Invoke( |
| [=]() { EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); })); |
| 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()); |
| EXPECT_TRUE(connection_.BlackholeDetectionInProgress()); |
| EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); |
| |
| // Process another packet with a different peer address on server side will |
| // start connection migration. |
| EXPECT_CALL(visitor_, OnConnectionMigration(IPV6_TO_IPV4_CHANGE)).Times(1); |
| // IETF QUIC send algorithm should be changed to a different object, so no |
| // OnPacketSent() called on the old send algorithm. |
| EXPECT_CALL(*send_algorithm_, |
| OnPacketSent(_, _, _, _, NO_RETRANSMITTABLE_DATA)) |
| .Times(0); |
| // 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()); |
| 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()); |
| QuicPathFrameBuffer payload = |
| writer_->path_challenge_frames().front().data_buffer; |
| EXPECT_NE(connection_.sent_packet_manager().GetSendAlgorithm(), |
| send_algorithm_); |
| // Switch to use the mock send algorithm. |
| send_algorithm_ = new StrictMock<MockSendAlgorithm>(); |
| EXPECT_CALL(*send_algorithm_, CanSend(_)).WillRepeatedly(Return(true)); |
| EXPECT_CALL(*send_algorithm_, GetCongestionWindow()) |
| .WillRepeatedly(Return(kDefaultTCPMSS)); |
| EXPECT_CALL(*send_algorithm_, OnApplicationLimited(_)).Times(AnyNumber()); |
| EXPECT_CALL(*send_algorithm_, BandwidthEstimate()) |
| .Times(AnyNumber()) |
| .WillRepeatedly(Return(QuicBandwidth::Zero())); |
| EXPECT_CALL(*send_algorithm_, InSlowStart()).Times(AnyNumber()); |
| EXPECT_CALL(*send_algorithm_, InRecovery()).Times(AnyNumber()); |
| EXPECT_CALL(*send_algorithm_, PopulateConnectionStats(_)).Times(AnyNumber()); |
| connection_.SetSendAlgorithm(send_algorithm_); |
| |
| // PATH_CHALLENGE is expanded upto the max packet size which may exceeds the |
| // anti-amplification limit. |
| EXPECT_EQ(kNewPeerAddress, writer_->last_write_peer_address()); |
| EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); |
| EXPECT_EQ(1u, |
| connection_.GetStats().num_reverse_path_validtion_upon_migration); |
| |
| // Verify server is throttled by anti-amplification limit. |
| connection_.SendCryptoDataWithString("foo", 0); |
| EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); |
| |
| // Receiving an ACK to the packet sent after changing peer address doesn't |
| // finish migration validation. |
| QuicAckFrame ack_frame = InitAckFrame(2); |
| EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)); |
| ProcessFramePacketWithAddresses(QuicFrame(&ack_frame), 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()); |
| |
| // Receiving PATH_RESPONSE should lift the anti-amplification limit. |
| QuicFrames frames3; |
| frames3.push_back(QuicFrame(new QuicPathResponseFrame(99, payload))); |
| EXPECT_CALL(visitor_, MaybeSendAddressToken()); |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) |
| .Times(testing::AtLeast(1u)); |
| ProcessFramesPacketWithAddresses(frames3, kSelfAddress, kNewPeerAddress, |
| ENCRYPTION_FORWARD_SECURE); |
| EXPECT_EQ(NO_CHANGE, connection_.active_effective_peer_migration_type()); |
| |
| // Verify the anti-amplification limit is lifted by sending a packet larger |
| // than the anti-amplification limit. |
| connection_.SendCryptoDataWithString(std::string(1200, 'a'), 0); |
| EXPECT_EQ(1u, connection_.GetStats().num_validated_peer_migration); |
| } |
| |
| TEST_P(QuicConnectionTest, PeerIpAddressChangeAtServerWithMissingConnectionId) { |
| set_perspective(Perspective::IS_SERVER); |
| if (!connection_.connection_migration_use_new_cid()) { |
| return; |
| } |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| |
| 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_); |
| |
| // Sends new server CID to client. |
| EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)) |
| .WillOnce( |
| Invoke([&](const QuicConnectionId& cid) { server_cid1 = cid; })); |
| EXPECT_CALL(visitor_, SendNewConnectionId(_)); |
| connection_.OnHandshakeComplete(); |
| |
| // 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); |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| if (version().SupportsAntiAmplificationLimit()) { |
| QuicConnectionPeer::SetAddressValidated(&connection_); |
| } |
| connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| // Discard INITIAL key. |
| connection_.RemoveEncrypter(ENCRYPTION_INITIAL); |
| connection_.NeuterUnencryptedPackets(); |
| EXPECT_CALL(visitor_, GetHandshakeState()) |
| .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); |
| |
| // Clear direct_peer_address. |
| QuicConnectionPeer::SetDirectPeerAddress(&connection_, QuicSocketAddress()); |
| // Clear effective_peer_address, it is different from direct_peer_address for |
| // this test. |
| QuicConnectionPeer::SetEffectivePeerAddress(&connection_, |
| QuicSocketAddress()); |
| const QuicSocketAddress kEffectivePeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/43210); |
| connection_.ReturnEffectivePeerAddressForNextPacket(kEffectivePeerAddress); |
| |
| if (QuicVersionUsesCryptoFrames(connection_.transport_version())) { |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); |
| } else { |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); |
| } |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, kPeerAddress, |
| ENCRYPTION_FORWARD_SECURE); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kEffectivePeerAddress, connection_.effective_peer_address()); |
| |
| // Process another packet with the same direct peer address and different |
| // effective peer address on server side will start connection migration. |
| const QuicSocketAddress kNewEffectivePeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/54321); |
| connection_.ReturnEffectivePeerAddressForNextPacket(kNewEffectivePeerAddress); |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, kPeerAddress, |
| ENCRYPTION_FORWARD_SECURE); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewEffectivePeerAddress, connection_.effective_peer_address()); |
| EXPECT_EQ(kPeerAddress, writer_->last_write_peer_address()); |
| if (connection_.validate_client_address()) { |
| EXPECT_EQ(NO_CHANGE, connection_.active_effective_peer_migration_type()); |
| EXPECT_EQ(1u, connection_.GetStats().num_validated_peer_migration); |
| } |
| |
| // Process another packet with a different direct peer address and the same |
| // effective peer address on server side will not start connection migration. |
| const QuicSocketAddress kNewPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); |
| connection_.ReturnEffectivePeerAddressForNextPacket(kNewEffectivePeerAddress); |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| |
| if (!connection_.validate_client_address()) { |
| // ack_frame is used to complete the migration started by the last packet, |
| // we need to make sure a new migration does not start after the previous |
| // one is completed. |
| QuicAckFrame ack_frame = InitAckFrame(1); |
| EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)); |
| ProcessFramePacketWithAddresses(QuicFrame(&ack_frame), kSelfAddress, |
| kNewPeerAddress, ENCRYPTION_FORWARD_SECURE); |
| EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewEffectivePeerAddress, connection_.effective_peer_address()); |
| EXPECT_EQ(NO_CHANGE, connection_.active_effective_peer_migration_type()); |
| } |
| |
| // Process another packet with different direct peer address and different |
| // effective peer address on server side will start connection migration. |
| const QuicSocketAddress kNewerEffectivePeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/65432); |
| const QuicSocketAddress kFinalPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/34567); |
| connection_.ReturnEffectivePeerAddressForNextPacket( |
| kNewerEffectivePeerAddress); |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, |
| kFinalPeerAddress, ENCRYPTION_FORWARD_SECURE); |
| EXPECT_EQ(kFinalPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewerEffectivePeerAddress, connection_.effective_peer_address()); |
| if (connection_.validate_client_address()) { |
| EXPECT_EQ(NO_CHANGE, connection_.active_effective_peer_migration_type()); |
| EXPECT_EQ(send_algorithm_, |
| connection_.sent_packet_manager().GetSendAlgorithm()); |
| EXPECT_EQ(2u, connection_.GetStats().num_validated_peer_migration); |
| } |
| |
| // While the previous migration is ongoing, process another packet with the |
| // same direct peer address and different effective peer address on server |
| // side will start a new connection migration. |
| const QuicSocketAddress kNewestEffectivePeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback4(), /*port=*/65430); |
| connection_.ReturnEffectivePeerAddressForNextPacket( |
| kNewestEffectivePeerAddress); |
| EXPECT_CALL(visitor_, OnConnectionMigration(IPV6_TO_IPV4_CHANGE)).Times(1); |
| if (!connection_.validate_client_address()) { |
| EXPECT_CALL(*send_algorithm_, OnConnectionMigration()).Times(1); |
| } |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, |
| kFinalPeerAddress, ENCRYPTION_FORWARD_SECURE); |
| EXPECT_EQ(kFinalPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewestEffectivePeerAddress, connection_.effective_peer_address()); |
| EXPECT_EQ(IPV6_TO_IPV4_CHANGE, |
| connection_.active_effective_peer_migration_type()); |
| if (connection_.validate_client_address()) { |
| EXPECT_NE(send_algorithm_, |
| connection_.sent_packet_manager().GetSendAlgorithm()); |
| EXPECT_EQ(kFinalPeerAddress, writer_->last_write_peer_address()); |
| EXPECT_FALSE(writer_->path_challenge_frames().empty()); |
| EXPECT_EQ(0u, connection_.GetStats() |
| .num_peer_migration_while_validating_default_path); |
| EXPECT_TRUE(connection_.HasPendingPathValidation()); |
| } |
| } |
| |
| // Regression test for b/200020764. |
| TEST_P(QuicConnectionTest, ConnectionMigrationWithPendingPaddingBytes) { |
| // TODO(haoyuewang) Move these test setup code to a common member function. |
| set_perspective(Perspective::IS_SERVER); |
| if (!connection_.connection_migration_use_new_cid()) { |
| return; |
| } |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| connection_.CreateConnectionIdManager(); |
| connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| QuicConnectionPeer::SetPeerAddress(&connection_, kPeerAddress); |
| QuicConnectionPeer::SetEffectivePeerAddress(&connection_, kPeerAddress); |
| QuicConnectionPeer::SetAddressValidated(&connection_); |
| |
| // Sends new server CID to client. |
| QuicConnectionId new_cid; |
| EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)) |
| .WillOnce(Invoke([&](const QuicConnectionId& cid) { new_cid = cid; })); |
| EXPECT_CALL(visitor_, SendNewConnectionId(_)); |
| // Discard INITIAL key. |
| connection_.RemoveEncrypter(ENCRYPTION_INITIAL); |
| connection_.NeuterUnencryptedPackets(); |
| connection_.OnHandshakeComplete(); |
| EXPECT_CALL(visitor_, GetHandshakeState()) |
| .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); |
| |
| auto* packet_creator = QuicConnectionPeer::GetPacketCreator(&connection_); |
| packet_creator->FlushCurrentPacket(); |
| packet_creator->AddPendingPadding(50u); |
| const QuicSocketAddress kPeerAddress3 = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/56789); |
| auto ack_frame = InitAckFrame(1); |
| EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)); |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); |
| ProcessFramesPacketWithAddresses({QuicFrame(&ack_frame)}, kSelfAddress, |
| kPeerAddress3, ENCRYPTION_FORWARD_SECURE); |
| if (GetQuicReloadableFlag( |
| quic_flush_pending_frames_and_padding_bytes_on_migration)) { |
| // Any pending frames/padding should be flushed before default_path_ is |
| // temporarily reset. |
| ASSERT_EQ(connection_.self_address_on_default_path_while_sending_packet() |
| .host() |
| .address_family(), |
| IpAddressFamily::IP_V6); |
| } else { |
| ASSERT_EQ(connection_.self_address_on_default_path_while_sending_packet() |
| .host() |
| .address_family(), |
| IpAddressFamily::IP_UNSPEC); |
| } |
| } |
| |
| // Regression test for b/196208556. |
| TEST_P(QuicConnectionTest, |
| ReversePathValidationResponseReceivedFromUnexpectedPeerAddress) { |
| set_perspective(Perspective::IS_SERVER); |
| if (!connection_.connection_migration_use_new_cid()) { |
| return; |
| } |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| connection_.CreateConnectionIdManager(); |
| connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| QuicConnectionPeer::SetPeerAddress(&connection_, kPeerAddress); |
| QuicConnectionPeer::SetEffectivePeerAddress(&connection_, kPeerAddress); |
| QuicConnectionPeer::SetAddressValidated(&connection_); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| |
| // Sends new server CID to client. |
| QuicConnectionId new_cid; |
| EXPECT_CALL(visitor_, OnServerConnectionIdIssued(_)) |
| .WillOnce(Invoke([&](const QuicConnectionId& cid) { new_cid = cid; })); |
| EXPECT_CALL(visitor_, SendNewConnectionId(_)); |
| // Discard INITIAL key. |
| connection_.RemoveEncrypter(ENCRYPTION_INITIAL); |
| connection_.NeuterUnencryptedPackets(); |
| connection_.OnHandshakeComplete(); |
| EXPECT_CALL(visitor_, GetHandshakeState()) |
| .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); |
| |
| // Process a non-probing packet to migrate to path 2 and kick off reverse path |
| // validation. |
| EXPECT_CALL(visitor_, OnConnectionMigration(IPV6_TO_IPV4_CHANGE)).Times(1); |
| const QuicSocketAddress kPeerAddress2 = |
| QuicSocketAddress(QuicIpAddress::Loopback4(), /*port=*/23456); |
| peer_creator_.SetServerConnectionId(new_cid); |
| ProcessFramesPacketWithAddresses({QuicFrame(QuicPingFrame())}, kSelfAddress, |
| kPeerAddress2, ENCRYPTION_FORWARD_SECURE); |
| EXPECT_FALSE(writer_->path_challenge_frames().empty()); |
| QuicPathFrameBuffer reverse_path_challenge_payload = |
| writer_->path_challenge_frames().front().data_buffer; |
| |
| // Receiveds a packet from path 3 with PATH_RESPONSE frame intended to |
| // validate path 2 and a non-probing frame. |
| { |
| QuicConnection::ScopedPacketFlusher flusher(&connection_); |
| const QuicSocketAddress kPeerAddress3 = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/56789); |
| auto ack_frame = InitAckFrame(1); |
| EXPECT_CALL(visitor_, OnConnectionMigration(IPV4_TO_IPV6_CHANGE)).Times(1); |
| EXPECT_CALL(visitor_, MaybeSendAddressToken()).WillOnce(Invoke([this]() { |
| connection_.SendControlFrame( |
| QuicFrame(new QuicNewTokenFrame(1, "new_token"))); |
| return true; |
| })); |
| ProcessFramesPacketWithAddresses({QuicFrame(new QuicPathResponseFrame( |
| 0, reverse_path_challenge_payload)), |
| QuicFrame(&ack_frame)}, |
| kSelfAddress, kPeerAddress3, |
| ENCRYPTION_FORWARD_SECURE); |
| } |
| } |
| |
| TEST_P(QuicConnectionTest, ReversePathValidationFailureAtServer) { |
| set_perspective(Perspective::IS_SERVER); |
| if (!connection_.connection_migration_use_new_cid()) { |
| return; |
| } |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| SetClientConnectionId(TestConnectionId(1)); |
| connection_.CreateConnectionIdManager(); |
| connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| // Discard INITIAL key. |
| connection_.RemoveEncrypter(ENCRYPTION_INITIAL); |
| connection_.NeuterUnencryptedPackets(); |
| // Prevent packets from being coalesced. |
| EXPECT_CALL(visitor_, GetHandshakeState()) |
| .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); |
| QuicConnectionPeer::SetAddressValidated(&connection_); |
| |
| 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_.OnHandshakeComplete(); |
| // 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 |
| // 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(_)) |
| .WillOnce(Invoke( |
| [=]() { EXPECT_EQ(kPeerAddress, connection_.peer_address()); })) |
| .WillOnce(Invoke( |
| [=]() { EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); })); |
| 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()); |
| |
| // Process another packet with a different peer address on server side will |
| // start connection migration. |
| EXPECT_CALL(visitor_, OnConnectionMigration(IPV6_TO_IPV4_CHANGE)).Times(1); |
| // IETF QUIC send algorithm should be changed to a different object, so no |
| // OnPacketSent() called on the old send algorithm. |
| EXPECT_CALL(*send_algorithm_, OnConnectionMigration()).Times(0); |
| |
| QuicFrames frames2; |
| 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()); |
| EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); |
| EXPECT_EQ(IPV6_TO_IPV4_CHANGE, |
| connection_.active_effective_peer_migration_type()); |
| 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()); |
| 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)); |
| static_cast<TestAlarmFactory::TestAlarm*>( |
| QuicPathValidatorPeer::retry_timer( |
| QuicConnectionPeer::path_validator(&connection_))) |
| ->Fire(); |
| } |
| EXPECT_EQ(IPV6_TO_IPV4_CHANGE, |
| connection_.active_effective_peer_migration_type()); |
| |
| // Make sure anti-amplification limit is not reached. |
| ProcessFramesPacketWithAddresses( |
| {QuicFrame(QuicPingFrame()), QuicFrame(QuicPaddingFrame())}, kSelfAddress, |
| kNewPeerAddress, ENCRYPTION_FORWARD_SECURE); |
| 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*>( |
| QuicPathValidatorPeer::retry_timer( |
| QuicConnectionPeer::path_validator(&connection_))) |
| ->Fire(); |
| EXPECT_EQ(NO_CHANGE, connection_.active_effective_peer_migration_type()); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| 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.has_value()); |
| 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) { |
| PathProbeTestInit(Perspective::IS_SERVER); |
| |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| EXPECT_CALL(visitor_, OnPacketReceived(_, _, false)).Times(0); |
| |
| // Process a padded PING packet with no peer address change on server side |
| // will be ignored. But a PATH CHALLENGE packet with no peer address change |
| // will be considered as path probing. |
| std::unique_ptr<SerializedPacket> probing_packet = ConstructProbingPacket(); |
| |
| std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( |
| QuicEncryptedPacket(probing_packet->encrypted_buffer, |
| probing_packet->encrypted_length), |
| clock_.Now())); |
| |
| uint64_t num_probing_received = |
| connection_.GetStats().num_connectivity_probing_received; |
| ProcessReceivedPacket(kSelfAddress, kPeerAddress, *received); |
| |
| EXPECT_EQ(num_probing_received + (GetParam().version.HasIetfQuicFrames() && |
| connection_.send_path_response() |
| ? 1u |
| : 0u), |
| connection_.GetStats().num_connectivity_probing_received); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| // Regression test for b/150161358. |
| TEST_P(QuicConnectionTest, BufferedMtuPacketTooBig) { |
| EXPECT_CALL(visitor_, OnWriteBlocked()).Times(1); |
| writer_->SetWriteBlocked(); |
| |
| // Send a MTU packet while blocked. It should be buffered. |
| connection_.SendMtuDiscoveryPacket(kMaxOutgoingPacketSize); |
| EXPECT_EQ(1u, connection_.NumQueuedPackets()); |
| EXPECT_TRUE(writer_->IsWriteBlocked()); |
| |
| writer_->AlwaysGetPacketTooLarge(); |
| writer_->SetWritable(); |
| connection_.OnCanWrite(); |
| } |
| |
| TEST_P(QuicConnectionTest, WriteOutOfOrderQueuedPackets) { |
| // EXPECT_QUIC_BUG tests are expensive so only run one instance of them. |
| if (!IsDefaultTestConfiguration()) { |
| return; |
| } |
| |
| set_perspective(Perspective::IS_CLIENT); |
| |
| BlockOnNextWrite(); |
| |
| QuicStreamId stream_id = 2; |
| connection_.SendStreamDataWithString(stream_id, "foo", 0, NO_FIN); |
| |
| EXPECT_EQ(1u, connection_.NumQueuedPackets()); |
| |
| writer_->SetWritable(); |
| connection_.SendConnectivityProbingPacket(writer_.get(), |
| connection_.peer_address()); |
| EXPECT_CALL(visitor_, OnConnectionClosed(_, _)).Times(0); |
| connection_.OnCanWrite(); |
| } |
| |
| TEST_P(QuicConnectionTest, DiscardQueuedPacketsAfterConnectionClose) { |
| // Regression test for b/74073386. |
| { |
| InSequence seq; |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) |
| .Times(AtLeast(1)); |
| EXPECT_CALL(visitor_, OnConnectionClosed(_, _)).Times(AtLeast(1)); |
| } |
| |
| set_perspective(Perspective::IS_CLIENT); |
| |
| writer_->SimulateNextPacketTooLarge(); |
| |
| // This packet write should fail, which should cause the connection to close |
| // after sending a connection close packet, then the failed packet should be |
| // queued. |
| connection_.SendStreamDataWithString(/*id=*/2, "foo", 0, NO_FIN); |
| |
| EXPECT_FALSE(connection_.connected()); |
| // No need to buffer packets. |
| EXPECT_EQ(0u, connection_.NumQueuedPackets()); |
| |
| EXPECT_EQ(0u, connection_.GetStats().packets_discarded); |
| connection_.OnCanWrite(); |
| EXPECT_EQ(0u, connection_.GetStats().packets_discarded); |
| } |
| |
| class TestQuicPathValidationContext : public QuicPathValidationContext { |
| public: |
| TestQuicPathValidationContext(const QuicSocketAddress& self_address, |
| const QuicSocketAddress& peer_address, |
| |
| QuicPacketWriter* writer) |
| : QuicPathValidationContext(self_address, peer_address), |
| writer_(writer) {} |
| |
| QuicPacketWriter* WriterToUse() override { return writer_; } |
| |
| private: |
| QuicPacketWriter* writer_; |
| }; |
| |
| class TestValidationResultDelegate : public QuicPathValidator::ResultDelegate { |
| public: |
| TestValidationResultDelegate(QuicConnection* connection, |
| const QuicSocketAddress& expected_self_address, |
| const QuicSocketAddress& expected_peer_address, |
| bool* success) |
| : QuicPathValidator::ResultDelegate(), |
| connection_(connection), |
| expected_self_address_(expected_self_address), |
| expected_peer_address_(expected_peer_address), |
| success_(success) {} |
| void OnPathValidationSuccess( |
| std::unique_ptr<QuicPathValidationContext> context) override { |
| EXPECT_EQ(expected_self_address_, context->self_address()); |
| EXPECT_EQ(expected_peer_address_, context->peer_address()); |
| *success_ = true; |
| } |
| |
| void OnPathValidationFailure( |
| std::unique_ptr<QuicPathValidationContext> context) override { |
| EXPECT_EQ(expected_self_address_, context->self_address()); |
| EXPECT_EQ(expected_peer_address_, context->peer_address()); |
| if (connection_->perspective() == Perspective::IS_CLIENT) { |
| connection_->OnPathValidationFailureAtClient(); |
| } |
| *success_ = false; |
| } |
| |
| private: |
| QuicConnection* connection_; |
| QuicSocketAddress expected_self_address_; |
| QuicSocketAddress expected_peer_address_; |
| bool* success_; |
| }; |
| |
| // Receive a path probe request at the server side, i.e., |
| // in non-IETF version: receive a padded PING packet with a peer addess change; |
| // in IETF version: receive a packet contains PATH CHALLENGE with peer address |
| // change. |
| TEST_P(QuicConnectionTest, ReceivePathProbingAtServer) { |
| PathProbeTestInit(Perspective::IS_SERVER); |
| |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| QuicPathFrameBuffer payload; |
| if (!GetParam().version.HasIetfQuicFrames()) { |
| EXPECT_CALL(visitor_, |
| OnPacketReceived(_, _, /*is_connectivity_probe=*/true)) |
| .Times(1); |
| } else { |
| EXPECT_CALL(visitor_, OnPacketReceived(_, _, _)).Times(0); |
| if (connection_.validate_client_address()) { |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) |
| .Times(AtLeast(1u)) |
| .WillOnce(Invoke([&]() { |
| EXPECT_EQ(1u, writer_->path_challenge_frames().size()); |
| EXPECT_EQ(1u, writer_->path_response_frames().size()); |
| payload = writer_->path_challenge_frames().front().data_buffer; |
| })); |
| } |
| } |
| // Process a probing packet from a new peer address on server side |
| // is effectively receiving a connectivity probing. |
| const QuicSocketAddress kNewPeerAddress(QuicIpAddress::Loopback4(), |
| /*port=*/23456); |
| |
| std::unique_ptr<SerializedPacket> probing_packet = ConstructProbingPacket(); |
| std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( |
| QuicEncryptedPacket(probing_packet->encrypted_buffer, |
| probing_packet->encrypted_length), |
| clock_.Now())); |
| uint64_t num_probing_received = |
| connection_.GetStats().num_connectivity_probing_received; |
| ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received); |
| |
| EXPECT_EQ(num_probing_received + 1, |
| connection_.GetStats().num_connectivity_probing_received); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| if (GetParam().version.HasIetfQuicFrames() && |
| connection_.use_path_validator() && |
| GetQuicReloadableFlag(quic_count_bytes_on_alternative_path_seperately)) { |
| QuicByteCount bytes_sent = |
| QuicConnectionPeer::BytesSentOnAlternativePath(&connection_); |
| EXPECT_LT(0u, bytes_sent); |
| EXPECT_EQ(received->length(), |
| QuicConnectionPeer::BytesReceivedOnAlternativePath(&connection_)); |
| |
| // Receiving one more probing packet should update the bytes count. |
| probing_packet = ConstructProbingPacket(); |
| received.reset(ConstructReceivedPacket( |
| QuicEncryptedPacket(probing_packet->encrypted_buffer, |
| probing_packet->encrypted_length), |
| clock_.Now())); |
| ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received); |
| |
| EXPECT_EQ(num_probing_received + 2, |
| connection_.GetStats().num_connectivity_probing_received); |
| EXPECT_EQ(2 * bytes_sent, |
| QuicConnectionPeer::BytesSentOnAlternativePath(&connection_)); |
| EXPECT_EQ(2 * received->length(), |
| QuicConnectionPeer::BytesReceivedOnAlternativePath(&connection_)); |
| |
| bool success = false; |
| if (!connection_.validate_client_address()) { |
| EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) |
| .Times(AtLeast(1u)) |
| .WillOnce(Invoke([&]() { |
| EXPECT_EQ(1u, writer_->path_challenge_frames().size()); |
| payload = writer_->path_challenge_frames().front().data_buffer; |
| })); |
| |
| connection_.ValidatePath( |
| std::make_unique<TestQuicPathValidationContext>( |
| connection_.self_address(), kNewPeerAddress, writer_.get()), |
| std::make_unique<TestValidationResultDelegate>( |
| &connection_, connection_.self_address(), kNewPeerAddress, |
| &success)); |
| } |
| EXPECT_EQ((connection_.validate_client_address() ? 2 : 3) * bytes_sent, |
| QuicConnectionPeer::BytesSentOnAlternativePath(&connection_)); |
| QuicFrames frames; |
| frames.push_back(QuicFrame(new QuicPathResponseFrame(99, payload))); |
| ProcessFramesPacketWithAddresses(frames, connection_.self_address(), |
| kNewPeerAddress, |
| ENCRYPTION_FORWARD_SECURE); |
| EXPECT_LT(2 * received->length(), |
| QuicConnectionPeer::BytesReceivedOnAlternativePath(&connection_)); |
| if (connection_.validate_client_address()) { |
| EXPECT_TRUE(QuicConnectionPeer::IsAlternativePathValidated(&connection_)); |
| } |
| // Receiving another probing packet from a newer address with a different |
| // port shouldn't trigger another reverse path validation. |
| QuicSocketAddress kNewerPeerAddress(QuicIpAddress::Loopback4(), |
| /*port=*/34567); |
| probing_packet = ConstructProbingPacket(); |
| received.reset(ConstructReceivedPacket( |
| QuicEncryptedPacket(probing_packet->encrypted_buffer, |
| probing_packet->encrypted_length), |
| clock_.Now())); |
| ProcessReceivedPacket(kSelfAddress, kNewerPeerAddress, *received); |
| EXPECT_FALSE(connection_.HasPendingPathValidation()); |
| EXPECT_EQ(connection_.validate_client_address(), |
| QuicConnectionPeer::IsAlternativePathValidated(&connection_)); |
| } |
| |
| // Process another packet with the old peer address on server side will not |
| // start peer migration. |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, kPeerAddress, |
| ENCRYPTION_INITIAL); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| // Receive a padded PING packet with a port change on server side. |
| TEST_P(QuicConnectionTest, ReceivePaddedPingWithPortChangeAtServer) { |
| set_perspective(Perspective::IS_SERVER); |
| QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); |
| EXPECT_EQ(Perspective::IS_SERVER, connection_.perspective()); |
| if (version().SupportsAntiAmplificationLimit()) { |
| QuicConnectionPeer::SetAddressValidated(&connection_); |
| } |
| |
| // 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()); |
| |
| if (GetParam().version.UsesCryptoFrames()) { |
| EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(AnyNumber()); |
| } else { |
| EXPECT_CALL(visitor_, OnStreamFrame(_)).Times(AnyNumber()); |
| } |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, kPeerAddress, |
| ENCRYPTION_INITIAL); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| |
| if (GetParam().version.HasIetfQuicFrames()) { |
| // In IETF version, a padded PING packet with port change is not taken as |
| // connectivity probe. |
| EXPECT_CALL(visitor_, GetHandshakeState()) |
| .WillRepeatedly(Return(HANDSHAKE_CONFIRMED)); |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); |
| EXPECT_CALL(visitor_, OnPacketReceived(_, _, _)).Times(0); |
| } else { |
| // In non-IETF version, process a padded PING packet from a new peer |
| // address on server side is effectively receiving a connectivity probing. |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| EXPECT_CALL(visitor_, |
| OnPacketReceived(_, _, /*is_connectivity_probe=*/true)) |
| .Times(1); |
| } |
| const QuicSocketAddress kNewPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); |
| |
| QuicFrames frames; |
| // Write a PING frame, which has no data payload. |
| QuicPingFrame ping_frame; |
| frames.push_back(QuicFrame(ping_frame)); |
| |
| // Add padding to the rest of the packet. |
| QuicPaddingFrame padding_frame; |
| frames.push_back(QuicFrame(padding_frame)); |
| |
| uint64_t num_probing_received = |
| connection_.GetStats().num_connectivity_probing_received; |
| |
| ProcessFramesPacketWithAddresses(frames, kSelfAddress, kNewPeerAddress, |
| ENCRYPTION_INITIAL); |
| |
| if (GetParam().version.HasIetfQuicFrames()) { |
| // Padded PING with port changen is not considered as connectivity probe but |
| // a PORT CHANGE. |
| EXPECT_EQ(num_probing_received, |
| connection_.GetStats().num_connectivity_probing_received); |
| EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); |
| } else { |
| EXPECT_EQ(num_probing_received + 1, |
| connection_.GetStats().num_connectivity_probing_received); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| if (GetParam().version.HasIetfQuicFrames()) { |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); |
| } |
| // Process another packet with the old peer address on server side. gQUIC |
| // shouldn't regard this as a peer migration. |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, kPeerAddress, |
| ENCRYPTION_INITIAL); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| TEST_P(QuicConnectionTest, ReceiveReorderedPathProbingAtServer) { |
| PathProbeTestInit(Perspective::IS_SERVER); |
| |
| // Decrease packet number to simulate out-of-order packets. |
| QuicPacketCreatorPeer::SetPacketNumber(&peer_creator_, 4); |
| |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| if (!GetParam().version.HasIetfQuicFrames()) { |
| EXPECT_CALL(visitor_, |
| OnPacketReceived(_, _, /*is_connectivity_probe=*/true)) |
| .Times(1); |
| } else { |
| EXPECT_CALL(visitor_, OnPacketReceived(_, _, _)).Times(0); |
| } |
| |
| // Process a padded PING packet from a new peer address on server side |
| // is effectively receiving a connectivity probing, even if a newer packet has |
| // been received before this one. |
| const QuicSocketAddress kNewPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); |
| |
| std::unique_ptr<SerializedPacket> probing_packet = ConstructProbingPacket(); |
| std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( |
| QuicEncryptedPacket(probing_packet->encrypted_buffer, |
| probing_packet->encrypted_length), |
| clock_.Now())); |
| |
| uint64_t num_probing_received = |
| connection_.GetStats().num_connectivity_probing_received; |
| ProcessReceivedPacket(kSelfAddress, kNewPeerAddress, *received); |
| |
| EXPECT_EQ(num_probing_received + 1, |
| connection_.GetStats().num_connectivity_probing_received); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| TEST_P(QuicConnectionTest, MigrateAfterProbingAtServer) { |
| PathProbeTestInit(Perspective::IS_SERVER); |
| |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| if (!GetParam().version.HasIetfQuicFrames()) { |
| EXPECT_CALL(visitor_, |
| OnPacketReceived(_, _, /*is_connectivity_probe=*/true)) |
| .Times(1); |
| } else { |
| EXPECT_CALL(visitor_, OnPacketReceived(_, _, _)).Times(0); |
| } |
| |
| // Process a padded PING packet from a new peer address on server side |
| // is effectively receiving a connectivity probing. |
| const QuicSocketAddress kNewPeerAddress = |
| QuicSocketAddress(QuicIpAddress::Loopback6(), /*port=*/23456); |
| |
| 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(kSelfAddress, kNewPeerAddress, *received); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| |
| // Process another non-probing packet with the new peer address on server |
| // side will start peer migration. |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(1); |
| |
| ProcessFramePacketWithAddresses(MakeCryptoFrame(), kSelfAddress, |
| kNewPeerAddress, ENCRYPTION_INITIAL); |
| EXPECT_EQ(kNewPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kNewPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| TEST_P(QuicConnectionTest, ReceiveConnectivityProbingPacketAtClient) { |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| PathProbeTestInit(Perspective::IS_CLIENT); |
| |
| // Client takes all padded PING packet as speculative connectivity |
| // probing packet, and reports to visitor. |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| if (!connection_.send_path_response()) { |
| EXPECT_CALL(visitor_, OnPacketReceived(_, _, false)).Times(1); |
| } |
| |
| std::unique_ptr<SerializedPacket> probing_packet = ConstructProbingPacket(); |
| std::unique_ptr<QuicReceivedPacket> received(ConstructReceivedPacket( |
| QuicEncryptedPacket(probing_packet->encrypted_buffer, |
| probing_packet->encrypted_length), |
| clock_.Now())); |
| uint64_t num_probing_received = |
| connection_.GetStats().num_connectivity_probing_received; |
| ProcessReceivedPacket(kSelfAddress, kPeerAddress, *received); |
| |
| EXPECT_EQ(num_probing_received + (GetParam().version.HasIetfQuicFrames() && |
| connection_.send_path_response() |
| ? 1u |
| : 0u), |
| connection_.GetStats().num_connectivity_probing_received); |
| EXPECT_EQ(kPeerAddress, connection_.peer_address()); |
| EXPECT_EQ(kPeerAddress, connection_.effective_peer_address()); |
| } |
| |
| TEST_P(QuicConnectionTest, ReceiveConnectivityProbingResponseAtClient) { |
| // TODO(b/150095484): add test coverage for IETF to verify that client takes |
| // PATH RESPONSE with peer address change as correct validation on the new |
| // path. |
| if (GetParam().version.HasIetfQuicFrames()) { |
| return; |
| } |
| EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); |
| PathProbeTestInit(Perspective::IS_CLIENT); |
| |
| // Process a padded PING packet with a different self address on client side |
| // is effectively receiving a connectivity probing. |
| EXPECT_CALL(visitor_, OnConnectionMigration(PORT_CHANGE)).Times(0); |
| if (!GetParam().version.HasIetfQuicFrames()) { |
| EXPECT_CALL(visitor_, |
| OnPacketReceived(_, _, /*is_connectivity_probe=*/true)) |
| .Times(1); |
| } else { |
| EXPECT_CALL(visitor_, OnPacketReceived(_, _, _)).Times(0); |
| } |
| |
| const QuicSocketAddress kNewSelfAddress = |
| QuicSocketAddress( |