gfe-relnote: In QUIC, add SetSoftMaxPacketLength to set a soft max packet length in creator. If a single frame is failed to be serialized, restore the actual max packet length. Not used yet. Not protected. PiperOrigin-RevId: 278424350 Change-Id: Ie89ba741b6798dee0683b4678edc9242f37f7672
diff --git a/quic/core/crypto/aead_base_encrypter.cc b/quic/core/crypto/aead_base_encrypter.cc index 405292e..a3173f5 100644 --- a/quic/core/crypto/aead_base_encrypter.cc +++ b/quic/core/crypto/aead_base_encrypter.cc
@@ -168,7 +168,7 @@ } size_t AeadBaseEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { - return ciphertext_size - auth_tag_size_; + return ciphertext_size - std::min(ciphertext_size, auth_tag_size_); } size_t AeadBaseEncrypter::GetCiphertextSize(size_t plaintext_size) const {
diff --git a/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc b/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc index 5ccc44d..d529d5d 100644 --- a/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc +++ b/quic/core/crypto/aes_128_gcm_12_encrypter_test.cc
@@ -228,6 +228,7 @@ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012)); EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112)); EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22)); + EXPECT_EQ(0u, encrypter.GetMaxPlaintextSize(11)); } TEST_F(Aes128Gcm12EncrypterTest, GetCiphertextSize) {
diff --git a/quic/core/crypto/null_encrypter.cc b/quic/core/crypto/null_encrypter.cc index 4ad9b2a..1fe0fdf 100644 --- a/quic/core/crypto/null_encrypter.cc +++ b/quic/core/crypto/null_encrypter.cc
@@ -75,7 +75,7 @@ } size_t NullEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) const { - return ciphertext_size - GetHashLength(); + return ciphertext_size - std::min(ciphertext_size, GetHashLength()); } size_t NullEncrypter::GetCiphertextSize(size_t plaintext_size) const {
diff --git a/quic/core/crypto/null_encrypter_test.cc b/quic/core/crypto/null_encrypter_test.cc index fd95cc6..c6a89ef 100644 --- a/quic/core/crypto/null_encrypter_test.cc +++ b/quic/core/crypto/null_encrypter_test.cc
@@ -87,6 +87,7 @@ EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1012)); EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(112)); EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(22)); + EXPECT_EQ(0u, encrypter.GetMaxPlaintextSize(11)); } TEST_F(NullEncrypterTest, GetCiphertextSize) {
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc index fa45cf9..9f7f160 100644 --- a/quic/core/quic_packet_creator.cc +++ b/quic/core/quic_packet_creator.cc
@@ -124,6 +124,7 @@ next_transmission_type_(NOT_RETRANSMISSION), flusher_attached_(false), fully_pad_crypto_handshake_packets_(true), + latched_hard_max_packet_length_(0), combine_generator_and_creator_( GetQuicReloadableFlag(quic_combine_generator_and_creator)), populate_nonretransmittable_frames_( @@ -168,6 +169,25 @@ << "Attempted to set max packet length too small"; } +void QuicPacketCreator::SetSoftMaxPacketLength(QuicByteCount length) { + DCHECK(CanSetMaxPacketLength()); + if (length > max_packet_length_) { + QUIC_BUG << ENDPOINT + << "Try to increase max_packet_length_ in " + "SetSoftMaxPacketLength, use SetMaxPacketLength instead."; + return; + } + if (framer_->GetMaxPlaintextSize(length) < + PacketHeaderSize() + MinPlaintextPacketSize(framer_->version())) { + QUIC_DLOG(INFO) << length << " is too small to fit packet header"; + return; + } + QUIC_DVLOG(1) << "Setting soft max packet length to: " << length; + latched_hard_max_packet_length_ = max_packet_length_; + max_packet_length_ = length; + max_plaintext_size_ = framer_->GetMaxPlaintextSize(length); +} + // Stops serializing version of the protocol in packets sent after this call. // A packet that is already open might send kQuicVersionSize bytes less than the // maximum packet size if we stop sending version before it is serialized. @@ -295,14 +315,28 @@ bool QuicPacketCreator::HasRoomForStreamFrame(QuicStreamId id, QuicStreamOffset offset, size_t data_size) { - return BytesFree() > - QuicFramer::GetMinStreamFrameSize(framer_->transport_version(), id, - offset, true, data_size); + const size_t min_stream_frame_size = QuicFramer::GetMinStreamFrameSize( + framer_->transport_version(), id, offset, /*last_frame_in_packet=*/true, + data_size); + if (BytesFree() > min_stream_frame_size) { + return true; + } + if (!RemoveSoftMaxPacketLength()) { + return false; + } + return BytesFree() > min_stream_frame_size; } bool QuicPacketCreator::HasRoomForMessageFrame(QuicByteCount length) { - return BytesFree() >= QuicFramer::GetMessageFrameSize( - framer_->transport_version(), true, length); + const size_t message_frame_size = QuicFramer::GetMessageFrameSize( + framer_->transport_version(), /*last_frame_in_packet=*/true, length); + if (BytesFree() >= message_frame_size) { + return true; + } + if (!RemoveSoftMaxPacketLength()) { + return false; + } + return BytesFree() >= message_frame_size; } // TODO(fkastenholz): this method should not use constant values for @@ -388,7 +422,8 @@ QuicFrame* frame) { size_t min_frame_size = QuicFramer::GetMinCryptoFrameSize(write_length, offset); - if (BytesFree() <= min_frame_size) { + if (BytesFree() <= min_frame_size && + (!RemoveSoftMaxPacketLength() || BytesFree() <= min_frame_size)) { return false; } size_t max_write_length = BytesFree() - min_frame_size; @@ -423,6 +458,7 @@ SerializedPacket packet(std::move(packet_)); ClearPacket(); + RemoveSoftMaxPacketLength(); delegate_->OnSerializedPacket(&packet); } @@ -730,6 +766,7 @@ QuicPacketCreator::SerializeConnectivityProbingPacket() { QUIC_BUG_IF(VersionHasIetfQuicFrames(framer_->transport_version())) << "Must not be version 99 to serialize padded ping connectivity probe"; + RemoveSoftMaxPacketLength(); QuicPacketHeader header; // FillPacketHeader increments packet_number_. FillPacketHeader(&header); @@ -765,6 +802,7 @@ << "Must be version 99 to serialize path challenge connectivity probe, " "is version " << framer_->transport_version(); + RemoveSoftMaxPacketLength(); QuicPacketHeader header; // FillPacketHeader increments packet_number_. FillPacketHeader(&header); @@ -801,6 +839,7 @@ << "Must be version 99 to serialize path response connectivity probe, is " "version " << framer_->transport_version(); + RemoveSoftMaxPacketLength(); QuicPacketHeader header; // FillPacketHeader increments packet_number_. FillPacketHeader(&header); @@ -1115,7 +1154,8 @@ // the slow path loop. bool run_fast_path = !has_handshake && state != FIN_AND_PADDING && !HasPendingFrames() && - write_length - total_bytes_consumed > kMaxOutgoingPacketSize; + write_length - total_bytes_consumed > kMaxOutgoingPacketSize && + latched_hard_max_packet_length_ == 0; while (!run_fast_path && delegate_->ShouldGeneratePacket( HAS_RETRANSMITTABLE_DATA, @@ -1154,7 +1194,8 @@ run_fast_path = !has_handshake && state != FIN_AND_PADDING && !HasPendingFrames() && - write_length - total_bytes_consumed > kMaxOutgoingPacketSize; + write_length - total_bytes_consumed > kMaxOutgoingPacketSize && + latched_hard_max_packet_length_ == 0; } if (run_fast_path) { @@ -1449,6 +1490,12 @@ size_t frame_len = framer_->GetSerializedFrameLength( frame, BytesFree(), queued_frames_.empty(), /* last_frame_in_packet= */ true, GetPacketNumberLength()); + if (frame_len == 0 && RemoveSoftMaxPacketLength()) { + // Remove soft max_packet_length and retry. + frame_len = framer_->GetSerializedFrameLength( + frame, BytesFree(), queued_frames_.empty(), + /* last_frame_in_packet= */ true, GetPacketNumberLength()); + } if (frame_len == 0) { // Current open packet is full. FlushCurrentPacket(); @@ -1528,6 +1575,21 @@ return true; } +bool QuicPacketCreator::RemoveSoftMaxPacketLength() { + if (latched_hard_max_packet_length_ == 0) { + return false; + } + if (!CanSetMaxPacketLength()) { + return false; + } + QUIC_DVLOG(1) << "Restoring max packet length to: " + << latched_hard_max_packet_length_; + SetMaxPacketLength(latched_hard_max_packet_length_); + // Reset latched_max_packet_length_. + latched_hard_max_packet_length_ = 0; + return true; +} + void QuicPacketCreator::MaybeAddPadding() { // The current packet should have no padding bytes because padding is only // added when this method is called just before the packet is serialized. @@ -1639,8 +1701,12 @@ VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, GetLengthLength()); // This is the largest possible message payload when the length field is // omitted. - return max_plaintext_size_ - - std::min(max_plaintext_size_, packet_header_size + kQuicFrameTypeSize); + size_t max_plaintext_size = + latched_hard_max_packet_length_ == 0 + ? max_plaintext_size_ + : framer_->GetMaxPlaintextSize(latched_hard_max_packet_length_); + return max_plaintext_size - + std::min(max_plaintext_size, packet_header_size + kQuicFrameTypeSize); } QuicPacketLength QuicPacketCreator::GetGuaranteedLargestMessagePayload() const { @@ -1669,9 +1735,13 @@ VARIABLE_LENGTH_INTEGER_LENGTH_0, 0, length_length); // This is the largest possible message payload when the length field is // omitted. + size_t max_plaintext_size = + latched_hard_max_packet_length_ == 0 + ? max_plaintext_size_ + : framer_->GetMaxPlaintextSize(latched_hard_max_packet_length_); const QuicPacketLength largest_payload = - max_plaintext_size_ - - std::min(max_plaintext_size_, packet_header_size + kQuicFrameTypeSize); + max_plaintext_size - + std::min(max_plaintext_size, packet_header_size + kQuicFrameTypeSize); // This must always be less than or equal to GetCurrentLargestMessagePayload. DCHECK_LE(largest_payload, GetCurrentLargestMessagePayload()); return largest_payload;
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h index 73e69c1..b8240c1 100644 --- a/quic/core/quic_packet_creator.h +++ b/quic/core/quic_packet_creator.h
@@ -270,6 +270,11 @@ // Sets the maximum packet length. void SetMaxPacketLength(QuicByteCount length); + // Set a soft maximum packet length in the creator. If a packet cannot be + // successfully created, creator will remove the soft limit and use the actual + // max packet length. + void SetSoftMaxPacketLength(QuicByteCount length); + // Increases pending_padding_bytes by |size|. Pending padding will be sent by // MaybeAddPadding(). void AddPendingPadding(QuicByteCount size); @@ -476,6 +481,13 @@ // Returns true on success. bool MaybeCoalesceStreamFrame(const QuicStreamFrame& frame); + // Called to remove the soft max_packet_length and restores + // latched_hard_max_packet_length_ if the packet cannot accommodate a single + // frame. Returns true if the soft limit is successfully removed. Returns + // false if either there is no current soft limit or there are queued frames + // (such that the packet length cannot be changed). + bool RemoveSoftMaxPacketLength(); + // Returns true if a diversification nonce should be included in the current // packet's header. bool IncludeNonceInPublicHeader() const; @@ -575,6 +587,11 @@ // flusher detaches. QuicPacketNumber write_start_packet_number_; + // If not 0, this latches the actual max_packet_length when + // SetSoftMaxPacketLength is called and max_packet_length_ gets + // set to a soft value. + QuicByteCount latched_hard_max_packet_length_; + // Latched value of quic_combine_generator_and_creator. const bool combine_generator_and_creator_;
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc index 5a0c9db..0ba7bd1 100644 --- a/quic/core/quic_packet_creator_test.cc +++ b/quic/core/quic_packet_creator_test.cc
@@ -2167,6 +2167,75 @@ } } +TEST_P(QuicPacketCreatorTest, SoftMaxPacketLength) { + creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + QuicByteCount previous_max_packet_length = creator_.max_packet_length(); + const size_t overhead = + GetPacketHeaderOverhead(client_framer_.transport_version()) + + QuicPacketCreator::MinPlaintextPacketSize(client_framer_.version()) + + GetEncryptionOverhead(); + // Make sure a length which cannot accommodate header (includes header + // protection minimal length) gets rejected. + creator_.SetSoftMaxPacketLength(overhead - 1); + EXPECT_EQ(previous_max_packet_length, creator_.max_packet_length()); + + creator_.SetSoftMaxPacketLength(overhead); + EXPECT_EQ(overhead, creator_.max_packet_length()); + + // Verify creator has room for stream frame because max_packet_length_ gets + // restored. + ASSERT_TRUE(creator_.HasRoomForStreamFrame(GetNthClientInitiatedStreamId(1), + kMaxIetfVarInt, kMaxIetfVarInt)); + EXPECT_EQ(previous_max_packet_length, creator_.max_packet_length()); + + // Same for message frame. + if (VersionSupportsMessageFrames(client_framer_.transport_version())) { + creator_.SetSoftMaxPacketLength(overhead); + // Verify GetCurrentLargestMessagePayload is based on the actual + // max_packet_length. + EXPECT_LT(1u, creator_.GetCurrentLargestMessagePayload()); + EXPECT_EQ(overhead, creator_.max_packet_length()); + ASSERT_TRUE(creator_.HasRoomForMessageFrame( + creator_.GetCurrentLargestMessagePayload())); + EXPECT_EQ(previous_max_packet_length, creator_.max_packet_length()); + } + + // Verify creator can consume crypto data because max_packet_length_ gets + // restored. + creator_.SetSoftMaxPacketLength(overhead); + EXPECT_EQ(overhead, creator_.max_packet_length()); + std::string data = "crypto data"; + MakeIOVector(data, &iov_); + QuicFrame frame; + if (!QuicVersionUsesCryptoFrames(client_framer_.transport_version())) { + ASSERT_TRUE(creator_.ConsumeDataToFillCurrentPacket( + QuicUtils::GetCryptoStreamId(client_framer_.transport_version()), &iov_, + 1u, iov_.iov_len, 0u, kOffset, false, true, NOT_RETRANSMISSION, + &frame)); + size_t bytes_consumed = frame.stream_frame.data_length; + EXPECT_LT(0u, bytes_consumed); + } else { + producer_.SaveCryptoData(ENCRYPTION_INITIAL, kOffset, data); + ASSERT_TRUE(creator_.ConsumeCryptoDataToFillCurrentPacket( + ENCRYPTION_INITIAL, data.length(), kOffset, + /*needs_full_padding=*/true, NOT_RETRANSMISSION, &frame)); + size_t bytes_consumed = frame.crypto_frame->data_length; + EXPECT_LT(0u, bytes_consumed); + } + EXPECT_TRUE(creator_.HasPendingFrames()); + EXPECT_CALL(delegate_, OnSerializedPacket(_)) + .WillOnce(Invoke(this, &QuicPacketCreatorTest::SaveSerializedPacket)); + creator_.FlushCurrentPacket(); + + // Verify ACK frame can be consumed. + creator_.SetSoftMaxPacketLength(overhead); + EXPECT_EQ(overhead, creator_.max_packet_length()); + QuicAckFrame ack_frame(InitAckFrame(10u)); + EXPECT_TRUE( + creator_.AddSavedFrame(QuicFrame(&ack_frame), NOT_RETRANSMISSION)); + EXPECT_TRUE(creator_.HasPendingFrames()); +} + } // namespace } // namespace test } // namespace quic