gfe-relnote: In QUIC, truncate IETF ACK ranges if ACK frame does not fit. Protected by existing gfe2_reloadable_flag_quic_enable_version_draft_25_v3. Also rewrote GetIetfAckFrameSize andAppendIetfAckFrameAndTypeByte. PiperOrigin-RevId: 299906874 Change-Id: Ib670d89b35c6f35aa7a346027b3604c81c5389e7
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc index 771848e..eeaf258 100644 --- a/quic/core/quic_framer.cc +++ b/quic/core/quic_framer.cc
@@ -469,12 +469,34 @@ size_t QuicFramer::GetMinAckFrameSize( QuicTransportVersion version, const QuicAckFrame& ack_frame, + uint32_t local_ack_delay_exponent, QuicPacketNumberLength largest_observed_length) { if (VersionHasIetfQuicFrames(version)) { - // The minimal ack frame consists of the following four fields: Largest - // Acknowledged, ACK Delay, ACK Block Count, and First ACK Block. Minimum - // size of each is 1 byte. - return kQuicFrameTypeSize + 4; + // The minimal ack frame consists of the following fields: Largest + // Acknowledged, ACK Delay, 0 ACK Block Count, First ACK Block and ECN + // counts. + // Type byte + largest acked. + size_t min_size = + kQuicFrameTypeSize + + QuicDataWriter::GetVarInt62Len(LargestAcked(ack_frame).ToUint64()); + // Ack delay. + min_size += QuicDataWriter::GetVarInt62Len( + ack_frame.ack_delay_time.ToMicroseconds() >> local_ack_delay_exponent); + // 0 ack block count. + min_size += QuicDataWriter::GetVarInt62Len(0); + // First ack block. + min_size += QuicDataWriter::GetVarInt62Len( + ack_frame.packets.Empty() ? 0 + : ack_frame.packets.rbegin()->Length() - 1); + // ECN counts. + if (ack_frame.ecn_counters_populated && + (ack_frame.ect_0_count || ack_frame.ect_1_count || + ack_frame.ecn_ce_count)) { + min_size += (QuicDataWriter::GetVarInt62Len(ack_frame.ect_0_count) + + QuicDataWriter::GetVarInt62Len(ack_frame.ect_1_count) + + QuicDataWriter::GetVarInt62Len(ack_frame.ecn_ce_count)); + } + return min_size; } if (GetQuicReloadableFlag(quic_use_ack_frame_to_get_min_size)) { QUIC_RELOADABLE_FLAG_COUNT(quic_use_ack_frame_to_get_min_size); @@ -797,9 +819,9 @@ } bool can_truncate = frame.type == ACK_FRAME && - free_bytes >= GetMinAckFrameSize(version_.transport_version, - *frame.ack_frame, - PACKET_6BYTE_PACKET_NUMBER); + free_bytes >= GetMinAckFrameSize( + version_.transport_version, *frame.ack_frame, + local_ack_delay_exponent_, PACKET_6BYTE_PACKET_NUMBER); if (can_truncate) { // Truncate the frame so the packet will not exceed kMaxOutgoingPacketSize. // Note that we may not use every byte of the writer in this case. @@ -3739,26 +3761,6 @@ ack_frame->ack_delay_time = QuicTime::Delta::FromMicroseconds(ack_delay_time_in_us); } - if (frame_type == IETF_ACK_ECN) { - ack_frame->ecn_counters_populated = true; - if (!reader->ReadVarInt62(&ack_frame->ect_0_count)) { - set_detailed_error("Unable to read ack ect_0_count."); - return false; - } - if (!reader->ReadVarInt62(&ack_frame->ect_1_count)) { - set_detailed_error("Unable to read ack ect_1_count."); - return false; - } - if (!reader->ReadVarInt62(&ack_frame->ecn_ce_count)) { - set_detailed_error("Unable to read ack ecn_ce_count."); - return false; - } - } else { - ack_frame->ecn_counters_populated = false; - ack_frame->ect_0_count = 0; - ack_frame->ect_1_count = 0; - ack_frame->ecn_ce_count = 0; - } if (!visitor_->OnAckFrameStart(QuicPacketNumber(largest_acked), ack_frame->ack_delay_time)) { // The visitor suppresses further processing of the packet. Although this is @@ -3872,6 +3874,27 @@ ack_block_count--; } + if (frame_type == IETF_ACK_ECN) { + ack_frame->ecn_counters_populated = true; + if (!reader->ReadVarInt62(&ack_frame->ect_0_count)) { + set_detailed_error("Unable to read ack ect_0_count."); + return false; + } + if (!reader->ReadVarInt62(&ack_frame->ect_1_count)) { + set_detailed_error("Unable to read ack ect_1_count."); + return false; + } + if (!reader->ReadVarInt62(&ack_frame->ecn_ce_count)) { + set_detailed_error("Unable to read ack ecn_ce_count."); + return false; + } + } else { + ack_frame->ecn_counters_populated = false; + ack_frame->ect_0_count = 0; + ack_frame->ect_1_count = 0; + ack_frame->ecn_ce_count = 0; + } + // TODO(fayang): Report ECN counts to visitor when they are actually used. if (!visitor_->OnAckFrameEnd(QuicPacketNumber(block_low))) { set_detailed_error( "Error occurs when visitor finishes processing the ACK frame."); @@ -4587,8 +4610,32 @@ ack_delay_time_us = ack_delay_time_us >> local_ack_delay_exponent_; ack_frame_size += QuicDataWriter::GetVarInt62Len(ack_delay_time_us); - // If |ecn_counters_populated| is true and any of the ecn counters is non-0 - // then the ecn counters are included... + if (frame.packets.Empty() || frame.packets.Max() != largest_acked) { + QUIC_BUG << "Malformed ack frame"; + // ACK frame serialization will fail and connection will be closed. + return ack_frame_size; + } + + // Ack block count. + ack_frame_size += + QuicDataWriter::GetVarInt62Len(frame.packets.NumIntervals() - 1); + + // First Ack range. + auto iter = frame.packets.rbegin(); + ack_frame_size += QuicDataWriter::GetVarInt62Len(iter->Length() - 1); + QuicPacketNumber previous_smallest = iter->min(); + ++iter; + + // Ack blocks. + for (; iter != frame.packets.rend(); ++iter) { + const uint64_t gap = previous_smallest - iter->max() - 1; + const uint64_t ack_range = iter->Length() - 1; + ack_frame_size += (QuicDataWriter::GetVarInt62Len(gap) + + QuicDataWriter::GetVarInt62Len(ack_range)); + previous_smallest = iter->min(); + } + + // ECN counts. if (frame.ecn_counters_populated && (frame.ect_0_count || frame.ect_1_count || frame.ecn_ce_count)) { ack_frame_size += QuicDataWriter::GetVarInt62Len(frame.ect_0_count); @@ -4596,72 +4643,6 @@ ack_frame_size += QuicDataWriter::GetVarInt62Len(frame.ecn_ce_count); } - // The rest (ack_block_count, first_ack_block, and additional ack - // blocks, if any) depends: - uint64_t ack_block_count = frame.packets.NumIntervals(); - if (ack_block_count == 0) { - // If the QuicAckFrame has no Intervals, then it is interpreted - // as an ack of a single packet at QuicAckFrame.largest_acked. - // The resulting ack will consist of only the frame's - // largest_ack & first_ack_block fields. The first ack block will be 0 - // (indicating a single packet) and the ack block_count will be 0. - // Each 0 takes 1 byte when VarInt62 encoded. - ack_frame_size += 2; - return ack_frame_size; - } - - auto itr = frame.packets.rbegin(); - QuicPacketNumber ack_block_largest = largest_acked; - QuicPacketNumber ack_block_smallest; - if ((itr->max() - 1) == largest_acked) { - // If largest_acked + 1 is equal to the Max() of the first Interval - // in the QuicAckFrame then the first Interval is the first ack block of the - // frame; remaining Intervals are additional ack blocks. The QuicAckFrame's - // first Interval is encoded in the frame's largest_acked/first_ack_block, - // the remaining Intervals are encoded in additional ack blocks in the - // frame, and the packet's ack_block_count is the number of QuicAckFrame - // Intervals - 1. - ack_block_smallest = itr->min(); - itr++; - ack_block_count--; - } else { - // If QuicAckFrame.largest_acked is NOT equal to the Max() of - // the first Interval then it is interpreted as acking a single - // packet at QuicAckFrame.largest_acked, with additional - // Intervals indicating additional ack blocks. The encoding is - // a) The packet's largest_acked is the QuicAckFrame's largest - // acked, - // b) the first ack block size is 0, - // c) The packet's ack_block_count is the number of QuicAckFrame - // Intervals, and - // d) The QuicAckFrame Intervals are encoded in additional ack - // blocks in the packet. - ack_block_smallest = largest_acked; - } - size_t ack_block_count_size = QuicDataWriter::GetVarInt62Len(ack_block_count); - ack_frame_size += ack_block_count_size; - - uint64_t first_ack_block = ack_block_largest - ack_block_smallest; - size_t first_ack_block_size = QuicDataWriter::GetVarInt62Len(first_ack_block); - ack_frame_size += first_ack_block_size; - - // Account for the remaining Intervals, if any. - while (ack_block_count != 0) { - uint64_t gap_size = ack_block_smallest - itr->max(); - // Decrement per the protocol specification - size_t size_of_gap_size = QuicDataWriter::GetVarInt62Len(gap_size - 1); - ack_frame_size += size_of_gap_size; - - uint64_t block_size = itr->max() - itr->min(); - // Decrement per the protocol specification - size_t size_of_block_size = QuicDataWriter::GetVarInt62Len(block_size - 1); - ack_frame_size += size_of_block_size; - - ack_block_smallest = itr->min(); - itr++; - ack_block_count--; - } - return ack_frame_size; } @@ -4681,7 +4662,8 @@ GetMinPacketNumberLength(QuicPacketNumber(ack_info.max_block_length)); ack_size = - GetMinAckFrameSize(version_.transport_version, ack, largest_acked_length); + GetMinAckFrameSize(version_.transport_version, ack, + local_ack_delay_exponent_, largest_acked_length); // First ack block length. ack_size += ack_block_length; if (ack_info.num_ack_blocks != 0) { @@ -5147,13 +5129,10 @@ int32_t available_timestamp_and_ack_block_bytes = writer->capacity() - writer->length() - ack_block_length - GetMinAckFrameSize(version_.transport_version, frame, - largest_acked_length) - + local_ack_delay_exponent_, largest_acked_length) - (new_ack_info.num_ack_blocks != 0 ? kNumberOfAckBlocksSize : 0); DCHECK_LE(0, available_timestamp_and_ack_block_bytes); - // Write out the type byte by setting the low order bits and doing shifts - // to make room for the next bit flags to be set. - // Whether there are multiple ack blocks. uint8_t type_byte = 0; SetBit(&type_byte, new_ack_info.num_ack_blocks != 0, kQuicHasMultipleAckBlocksOffset); @@ -5374,57 +5353,17 @@ return true; } -int QuicFramer::CalculateIetfAckBlockCount(const QuicAckFrame& frame, - QuicDataWriter* /*writer*/, - size_t available_space) { - // Number of blocks requested in the frame - uint64_t ack_block_count = frame.packets.NumIntervals(); - - auto itr = frame.packets.rbegin(); - - int actual_block_count = 1; - uint64_t block_length = itr->max() - itr->min(); - size_t encoded_size = QuicDataWriter::GetVarInt62Len(block_length); - if (encoded_size > available_space) { - return 0; - } - available_space -= encoded_size; - QuicPacketNumber previous_ack_end = itr->min(); - ack_block_count--; - - while (ack_block_count) { - // Each block is a gap followed by another ACK. Calculate each value, - // determine the encoded lengths, and check against the available space. - itr++; - size_t gap = previous_ack_end - itr->max() - 1; - encoded_size = QuicDataWriter::GetVarInt62Len(gap); - - // Add the ACK block. - block_length = itr->max() - itr->min(); - encoded_size += QuicDataWriter::GetVarInt62Len(block_length); - - if (encoded_size > available_space) { - // No room for this block, so what we've - // done up to now is all that can be done. - return actual_block_count; - } - available_space -= encoded_size; - actual_block_count++; - previous_ack_end = itr->min(); - ack_block_count--; - } - // Ran through the whole thing! We can do all blocks. - return actual_block_count; -} - bool QuicFramer::AppendIetfAckFrameAndTypeByte(const QuicAckFrame& frame, QuicDataWriter* writer) { - // Assume frame is an IETF_ACK frame. If |ecn_counters_populated| is true and - // any of the ECN counters is non-0 then turn it into an IETF_ACK+ECN frame. uint8_t type = IETF_ACK; + uint64_t ecn_size = 0; if (frame.ecn_counters_populated && (frame.ect_0_count || frame.ect_1_count || frame.ecn_ce_count)) { + // Change frame type to ACK_ECN if any ECN count is available. type = IETF_ACK_ECN; + ecn_size = (QuicDataWriter::GetVarInt62Len(frame.ect_0_count) + + QuicDataWriter::GetVarInt62Len(frame.ect_1_count) + + QuicDataWriter::GetVarInt62Len(frame.ecn_ce_count)); } if (!writer->WriteUInt8(type)) { @@ -5449,8 +5388,67 @@ set_detailed_error("No room for ack-delay in ack frame"); return false; } + + if (frame.packets.Empty() || frame.packets.Max() != largest_acked) { + QUIC_BUG << "Malformed ack frame: " << frame; + set_detailed_error("Malformed ack frame"); + return false; + } + + // Latch ack_block_count for potential truncation. + const uint64_t ack_block_count = frame.packets.NumIntervals() - 1; + QuicDataWriter count_writer(QuicDataWriter::GetVarInt62Len(ack_block_count), + writer->data() + writer->length()); + if (!writer->WriteVarInt62(ack_block_count)) { + set_detailed_error("No room for ack block count in ack frame"); + return false; + } + auto iter = frame.packets.rbegin(); + if (!writer->WriteVarInt62(iter->Length() - 1)) { + set_detailed_error("No room for first ack block in ack frame"); + return false; + } + QuicPacketNumber previous_smallest = iter->min(); + ++iter; + // Append remaining ACK blocks. + uint64_t appended_ack_blocks = 0; + for (; iter != frame.packets.rend(); ++iter) { + const uint64_t gap = previous_smallest - iter->max() - 1; + const uint64_t ack_range = iter->Length() - 1; + + if (writer->remaining() < ecn_size || + writer->remaining() - ecn_size < + QuicDataWriter::GetVarInt62Len(gap) + + QuicDataWriter::GetVarInt62Len(ack_range)) { + // ACK range does not fit, truncate it. + break; + } + const bool success = + writer->WriteVarInt62(gap) && writer->WriteVarInt62(ack_range); + DCHECK(success); + previous_smallest = iter->min(); + ++appended_ack_blocks; + } + + if (appended_ack_blocks < ack_block_count) { + // Truncation is needed, rewrite the ack block count. + if (QuicDataWriter::GetVarInt62Len(appended_ack_blocks) != + QuicDataWriter::GetVarInt62Len(ack_block_count) || + !count_writer.WriteVarInt62(appended_ack_blocks)) { + // This should never happen as ack_block_count is limited by + // max_ack_ranges_. + QUIC_BUG << "Ack frame truncation fails. ack_block_count: " + << ack_block_count + << ", appended count: " << appended_ack_blocks; + set_detailed_error("ACK frame truncation fails"); + return false; + } + QUIC_DLOG(INFO) << ENDPOINT << "ACK ranges get truncated from " + << ack_block_count << " to " << appended_ack_blocks; + } + if (type == IETF_ACK_ECN) { - // Encode the ACK ECN fields + // Encode the ECN counts. if (!writer->WriteVarInt62(frame.ect_0_count)) { set_detailed_error("No room for ect_0_count in ack frame"); return false; @@ -5465,84 +5463,6 @@ } } - uint64_t ack_block_count = frame.packets.NumIntervals(); - if (ack_block_count == 0) { - // If the QuicAckFrame has no Intervals, then it is interpreted - // as an ack of a single packet at QuicAckFrame.largest_acked. - // The resulting ack will consist of only the frame's - // largest_ack & first_ack_block fields. The first ack block will be 0 - // (indicating a single packet) and the ack block_count will be 0. - if (!writer->WriteVarInt62(0)) { - set_detailed_error("No room for ack block count in ack frame"); - return false; - } - // size of the first block is 1 packet - if (!writer->WriteVarInt62(0)) { - set_detailed_error("No room for first ack block in ack frame"); - return false; - } - return true; - } - // Case 2 or 3 - auto itr = frame.packets.rbegin(); - - QuicPacketNumber ack_block_largest(largest_acked); - QuicPacketNumber ack_block_smallest; - if ((itr->max() - 1) == QuicPacketNumber(largest_acked)) { - // If largest_acked + 1 is equal to the Max() of the first Interval - // in the QuicAckFrame then the first Interval is the first ack block of the - // frame; remaining Intervals are additional ack blocks. The QuicAckFrame's - // first Interval is encoded in the frame's largest_acked/first_ack_block, - // the remaining Intervals are encoded in additional ack blocks in the - // frame, and the packet's ack_block_count is the number of QuicAckFrame - // Intervals - 1. - ack_block_smallest = itr->min(); - itr++; - ack_block_count--; - } else { - // If QuicAckFrame.largest_acked is NOT equal to the Max() of - // the first Interval then it is interpreted as acking a single - // packet at QuicAckFrame.largest_acked, with additional - // Intervals indicating additional ack blocks. The encoding is - // a) The packet's largest_acked is the QuicAckFrame's largest - // acked, - // b) the first ack block size is 0, - // c) The packet's ack_block_count is the number of QuicAckFrame - // Intervals, and - // d) The QuicAckFrame Intervals are encoded in additional ack - // blocks in the packet. - ack_block_smallest = largest_acked; - } - - if (!writer->WriteVarInt62(ack_block_count)) { - set_detailed_error("No room for ack block count in ack frame"); - return false; - } - - uint64_t first_ack_block = ack_block_largest - ack_block_smallest; - if (!writer->WriteVarInt62(first_ack_block)) { - set_detailed_error("No room for first ack block in ack frame"); - return false; - } - - // For the remaining QuicAckFrame Intervals, if any - while (ack_block_count != 0) { - uint64_t gap_size = ack_block_smallest - itr->max(); - if (!writer->WriteVarInt62(gap_size - 1)) { - set_detailed_error("No room for gap block in ack frame"); - return false; - } - - uint64_t block_size = itr->max() - itr->min(); - if (!writer->WriteVarInt62(block_size - 1)) { - set_detailed_error("No room for nth ack block in ack frame"); - return false; - } - - ack_block_smallest = itr->min(); - itr++; - ack_block_count--; - } return true; }
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h index c05177c..85cc7fb 100644 --- a/quic/core/quic_framer.h +++ b/quic/core/quic_framer.h
@@ -317,6 +317,7 @@ static size_t GetMinAckFrameSize( QuicTransportVersion version, const QuicAckFrame& ack_frame, + uint32_t local_ack_delay_exponent, QuicPacketNumberLength largest_observed_length); // Size in bytes of a stop waiting frame. static size_t GetStopWaitingFrameSize( @@ -882,11 +883,6 @@ bool AppendIetfAckFrameAndTypeByte(const QuicAckFrame& frame, QuicDataWriter* writer); - // Used by AppendIetfAckFrameAndTypeByte to figure out how many ack - // blocks can be included. - int CalculateIetfAckBlockCount(const QuicAckFrame& frame, - QuicDataWriter* writer, - size_t available_space); bool AppendStopWaitingFrame(const QuicPacketHeader& header, const QuicStopWaitingFrame& frame, QuicDataWriter* builder);
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc index 2248909..e23a454 100644 --- a/quic/core/quic_framer_test.cc +++ b/quic/core/quic_framer_test.cc
@@ -324,7 +324,8 @@ ack_frame.ack_delay_time = ack_delay_time; ack_frames_.push_back(std::make_unique<QuicAckFrame>(ack_frame)); if (VersionHasIetfQuicFrames(transport_version_)) { - EXPECT_EQ(IETF_ACK, framer_->current_received_frame_type()); + EXPECT_TRUE(IETF_ACK == framer_->current_received_frame_type() || + IETF_ACK_ECN == framer_->current_received_frame_type()); } else { EXPECT_EQ(0u, framer_->current_received_frame_type()); } @@ -335,7 +336,8 @@ DCHECK(!ack_frames_.empty()); ack_frames_[ack_frames_.size() - 1]->packets.AddRange(start, end); if (VersionHasIetfQuicFrames(transport_version_)) { - EXPECT_EQ(IETF_ACK, framer_->current_received_frame_type()); + EXPECT_TRUE(IETF_ACK == framer_->current_received_frame_type() || + IETF_ACK_ECN == framer_->current_received_frame_type()); } else { EXPECT_EQ(0u, framer_->current_received_frame_type()); } @@ -9148,6 +9150,56 @@ EXPECT_EQ(QuicPacketNumber(600u), processed_ack_frame.packets.Max()); } +// Regression test for b/150386368. +TEST_P(QuicFramerTest, IetfAckFrameTruncation) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicAckFrame ack_frame; + // Create a packet with just the ack. + ack_frame = MakeAckFrameWithGaps(/*gap_size=*/0xffffffff, + /*max_num_gaps=*/200, + /*largest_acked=*/kMaxIetfVarInt); + ack_frame.ecn_counters_populated = true; + ack_frame.ect_0_count = 100; + ack_frame.ect_1_count = 10000; + ack_frame.ecn_ce_count = 1000000; + QuicFrames frames = {QuicFrame(&ack_frame)}; + // Build an ACK packet. + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + std::unique_ptr<QuicPacket> raw_ack_packet(BuildDataPacket(header, frames)); + ASSERT_TRUE(raw_ack_packet != nullptr); + char buffer[kMaxOutgoingPacketSize]; + size_t encrypted_length = + framer_.EncryptPayload(ENCRYPTION_INITIAL, header.packet_number, + *raw_ack_packet, buffer, kMaxOutgoingPacketSize); + ASSERT_NE(0u, encrypted_length); + // Now make sure we can turn our ack packet back into an ack frame. + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + ASSERT_TRUE(framer_.ProcessPacket( + QuicEncryptedPacket(buffer, encrypted_length, false))); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(QuicPacketNumber(kMaxIetfVarInt), + LargestAcked(processed_ack_frame)); + // Verify ACK frame gets truncated. + ASSERT_LT(processed_ack_frame.packets.NumPacketsSlow(), + ack_frame.packets.NumIntervals()); + EXPECT_EQ(157u, processed_ack_frame.packets.NumPacketsSlow()); + EXPECT_LT(processed_ack_frame.packets.NumIntervals(), + ack_frame.packets.NumIntervals()); + EXPECT_EQ(QuicPacketNumber(kMaxIetfVarInt), + processed_ack_frame.packets.Max()); +} + TEST_P(QuicFramerTest, AckTruncationSmallPacket) { if (VersionHasIetfQuicFrames(framer_.transport_version())) { // This test is not applicable to this version; the range count is
diff --git a/quic/test_tools/quic_test_utils.cc b/quic/test_tools/quic_test_utils.cc index bfbf512..aade80b 100644 --- a/quic/test_tools/quic_test_utils.cc +++ b/quic/test_tools/quic_test_utils.cc
@@ -111,6 +111,22 @@ return ack; } +QuicAckFrame MakeAckFrameWithGaps(uint64_t gap_size, + size_t max_num_gaps, + uint64_t largest_acked) { + QuicAckFrame ack; + ack.largest_acked = QuicPacketNumber(largest_acked); + ack.packets.Add(QuicPacketNumber(largest_acked)); + for (size_t i = 0; i < max_num_gaps; ++i) { + if (largest_acked <= gap_size) { + break; + } + largest_acked -= gap_size; + ack.packets.Add(QuicPacketNumber(largest_acked)); + } + return ack; +} + EncryptionLevel HeaderToEncryptionLevel(const QuicPacketHeader& header) { if (header.form == IETF_QUIC_SHORT_HEADER_PACKET) { return ENCRYPTION_FORWARD_SECURE;
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h index 6b8403c..a7c580f 100644 --- a/quic/test_tools/quic_test_utils.h +++ b/quic/test_tools/quic_test_utils.h
@@ -196,6 +196,12 @@ QuicAckFrame MakeAckFrameWithAckBlocks(size_t num_ack_blocks, uint64_t least_unacked); +// Testing convenice method to construct a QuicAckFrame with |largest_acked|, +// ack blocks of width 1 packet and |gap_size|. +QuicAckFrame MakeAckFrameWithGaps(uint64_t gap_size, + size_t max_num_gaps, + uint64_t largest_acked); + // Returns the encryption level that corresponds to the header type in // |header|. If the header is for GOOGLE_QUIC_PACKET instead of an // IETF-invariants packet, this function returns ENCRYPTION_INITIAL.