In quic, consider frame expansion when extra padding is needed to meet the minimum plaintext size needed for header protection. protected by gfe2_reloadable_flag_quic_fix_extra_padding_bytes.
PiperOrigin-RevId: 318497490
Change-Id: If5ade70a1992e6daa7c6d116cb778cb88210981c
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 7fc45ca..e751f7b 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -11366,7 +11366,8 @@
if (!connection_.version().CanSendCoalescedPackets()) {
return;
}
- if (GetQuicReloadableFlag(quic_fix_min_crypto_frame_size)) {
+ if (GetQuicReloadableFlag(quic_fix_extra_padding_bytes) ||
+ GetQuicReloadableFlag(quic_fix_min_crypto_frame_size)) {
EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(1);
} else {
EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(0);
@@ -11382,7 +11383,8 @@
SendStreamDataToPeer(2, std::string(1286, 'a'), 0, NO_FIN, nullptr);
connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE);
// Try to coalesce a HANDSHAKE packet after 1-RTT packet.
- if (GetQuicReloadableFlag(quic_fix_min_crypto_frame_size)) {
+ if (GetQuicReloadableFlag(quic_fix_extra_padding_bytes) ||
+ GetQuicReloadableFlag(quic_fix_min_crypto_frame_size)) {
// Verify soft max packet length gets resumed and handshake packet gets
// successfully sent.
connection_.SendCryptoDataWithString("a", 0, ENCRYPTION_HANDSHAKE);
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc
index a31b179..56c3050 100644
--- a/quic/core/quic_packet_creator.cc
+++ b/quic/core/quic_packet_creator.cc
@@ -432,10 +432,8 @@
size_t min_frame_size =
QuicFramer::GetMinCryptoFrameSize(write_length, offset);
size_t min_plaintext_bytes = min_frame_size;
- if (fix_min_crypto_frame_size_ && queued_frames_.empty()) {
- // TODO(fayang): to make this more general, we need to move the checking
- // when trying to add a frame when GetSerializedFrameLength. Remove soft
- // limit if current packet needs extra padding and the space is too small.
+ if (!fix_extra_padding_bytes_ && fix_min_crypto_frame_size_ &&
+ queued_frames_.empty()) {
QUIC_RELOADABLE_FLAG_COUNT(quic_fix_min_crypto_frame_size);
min_plaintext_bytes =
std::max(min_frame_size, MinPlaintextPacketSize(framer_->version()));
@@ -1567,6 +1565,43 @@
EncryptionlevelToLongHeaderType(packet_.encryption_level);
}
+size_t QuicPacketCreator::GetSerializedFrameLength(const QuicFrame& frame) {
+ size_t serialized_frame_length = framer_->GetSerializedFrameLength(
+ frame, BytesFree(), queued_frames_.empty(),
+ /* last_frame_in_packet= */ true, GetPacketNumberLength());
+ if (!fix_extra_padding_bytes_) {
+ return serialized_frame_length;
+ }
+ if (!framer_->version().HasHeaderProtection() ||
+ serialized_frame_length == 0) {
+ return serialized_frame_length;
+ }
+ // Calculate frame bytes and bytes free with this frame added.
+ const size_t frame_bytes = PacketSize() - PacketHeaderSize() +
+ ExpansionOnNewFrame() + serialized_frame_length;
+ if (frame_bytes >= MinPlaintextPacketSize(framer_->version())) {
+ // No extra bytes is needed.
+ return serialized_frame_length;
+ }
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_fix_extra_padding_bytes, 1, 3);
+ if (BytesFree() < serialized_frame_length) {
+ QUIC_BUG << ENDPOINT << "Frame does not fit: " << frame;
+ return 0;
+ }
+ // Please note bytes_free does not take |frame|'s expansion into account.
+ size_t bytes_free = BytesFree() - serialized_frame_length;
+ // Extra bytes needed (this is NOT padding needed) should be at least 1
+ // padding + expansion.
+ const size_t extra_bytes_needed =
+ std::max(1 + ExpansionOnNewFrameWithLastFrame(frame),
+ MinPlaintextPacketSize(framer_->version()) - frame_bytes);
+ if (bytes_free < extra_bytes_needed) {
+ // This frame does not fit.
+ return 0;
+ }
+ return serialized_frame_length;
+}
+
bool QuicPacketCreator::AddFrame(const QuicFrame& frame,
TransmissionType transmission_type) {
QUIC_DVLOG(1) << ENDPOINT << "Adding frame with transmission type "
@@ -1594,14 +1629,10 @@
}
}
- size_t frame_len = framer_->GetSerializedFrameLength(
- frame, BytesFree(), queued_frames_.empty(),
- /* last_frame_in_packet= */ true, GetPacketNumberLength());
+ size_t frame_len = GetSerializedFrameLength(frame);
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());
+ frame_len = GetSerializedFrameLength(frame);
}
if (frame_len == 0) {
QUIC_DVLOG(1) << "Flushing because current open packet is full when adding "
@@ -1655,6 +1686,25 @@
return true;
}
+void QuicPacketCreator::MaybeAddExtraPaddingForHeaderProtection() {
+ DCHECK(fix_extra_padding_bytes_);
+ if (!framer_->version().HasHeaderProtection() || needs_full_padding_) {
+ return;
+ }
+ const size_t frame_bytes = PacketSize() - PacketHeaderSize();
+ if (frame_bytes >= MinPlaintextPacketSize(framer_->version())) {
+ return;
+ }
+ const QuicByteCount min_header_protection_padding =
+ std::max(1 + ExpansionOnNewFrame(),
+ MinPlaintextPacketSize(framer_->version()) - frame_bytes) -
+ ExpansionOnNewFrame();
+ QUIC_RELOADABLE_FLAG_COUNT_N(quic_fix_extra_padding_bytes, 2, 3);
+ // Update pending_padding_bytes_.
+ pending_padding_bytes_ =
+ std::max(pending_padding_bytes_, min_header_protection_padding);
+}
+
bool QuicPacketCreator::MaybeCoalesceStreamFrame(const QuicStreamFrame& frame) {
if (queued_frames_.empty() || queued_frames_.back().type != STREAM_FRAME) {
return false;
@@ -1736,15 +1786,21 @@
}
// Header protection requires a minimum plaintext packet size.
+ // TODO(fayang): remove extra_padding_bytes when deprecating
+ // quic_fix_extra_padding_bytes.
size_t extra_padding_bytes = 0;
- if (framer_->version().HasHeaderProtection()) {
- size_t frame_bytes = PacketSize() - PacketHeaderSize();
+ if (fix_extra_padding_bytes_) {
+ MaybeAddExtraPaddingForHeaderProtection();
+ } else {
+ if (framer_->version().HasHeaderProtection()) {
+ size_t frame_bytes = PacketSize() - PacketHeaderSize();
- if (frame_bytes + pending_padding_bytes_ <
- MinPlaintextPacketSize(framer_->version()) &&
- !needs_full_padding_) {
- extra_padding_bytes =
- MinPlaintextPacketSize(framer_->version()) - frame_bytes;
+ if (frame_bytes + pending_padding_bytes_ <
+ MinPlaintextPacketSize(framer_->version()) &&
+ !needs_full_padding_) {
+ extra_padding_bytes =
+ MinPlaintextPacketSize(framer_->version()) - frame_bytes;
+ }
}
}
diff --git a/quic/core/quic_packet_creator.h b/quic/core/quic_packet_creator.h
index ddc5e2d..c579e42 100644
--- a/quic/core/quic_packet_creator.h
+++ b/quic/core/quic_packet_creator.h
@@ -542,6 +542,14 @@
// Returns true if packet under construction has IETF long header.
bool HasIetfLongHeader() const;
+ // Get serialized frame length. Returns 0 if the frame does not fit into
+ // current packet.
+ size_t GetSerializedFrameLength(const QuicFrame& frame);
+
+ // Add extra padding to pending_padding_bytes_ to meet minimum plaintext
+ // packet size required for header protection.
+ void MaybeAddExtraPaddingForHeaderProtection();
+
// Does not own these delegates or the framer.
DelegateInterface* delegate_;
DebugDelegate* debug_delegate_;
@@ -622,7 +630,11 @@
// When true, this will override the padding generation code to disable it.
bool disable_padding_override_ = false;
- bool update_packet_size_ = GetQuicReloadableFlag(quic_update_packet_size);
+ const bool update_packet_size_ =
+ GetQuicReloadableFlag(quic_update_packet_size);
+
+ const bool fix_extra_padding_bytes_ =
+ GetQuicReloadableFlag(quic_fix_extra_padding_bytes);
};
} // namespace quic
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc
index 394b82e..7c4416d 100644
--- a/quic/core/quic_packet_creator_test.cc
+++ b/quic/core/quic_packet_creator_test.cc
@@ -3793,6 +3793,31 @@
EXPECT_EQ(TestConnectionId(0x33), creator_.GetSourceConnectionId());
}
+// Regresstion test for b/159812345.
+TEST_F(QuicPacketCreatorMultiplePacketsTest, ExtraPaddingNeeded) {
+ if (!framer_.version().HasHeaderProtection()) {
+ return;
+ }
+ delegate_.SetCanWriteAnything();
+
+ EXPECT_CALL(delegate_, OnSerializedPacket(_))
+ .WillOnce(
+ Invoke(this, &QuicPacketCreatorMultiplePacketsTest::SavePacket));
+ MakeIOVector("a", &iov_);
+ creator_.ConsumeData(QuicUtils::GetFirstBidirectionalStreamId(
+ framer_.transport_version(), Perspective::IS_CLIENT),
+ &iov_, 1u, iov_.iov_len, 0, FIN);
+ creator_.Flush();
+ ASSERT_FALSE(packets_[0].nonretransmittable_frames.empty());
+ QuicFrame padding = packets_[0].nonretransmittable_frames[0];
+ if (GetQuicReloadableFlag(quic_fix_extra_padding_bytes)) {
+ // Verify stream frame expansion is excluded.
+ padding.padding_frame.num_padding_bytes = 3;
+ } else {
+ padding.padding_frame.num_padding_bytes = 4;
+ }
+}
+
} // namespace
} // namespace test
} // namespace quic