Internal QUICHE change PiperOrigin-RevId: 317961822 Change-Id: I82e3c50088a3c6b8f158bc1eb096b32c3d4a92aa
diff --git a/quic/core/chlo_extractor.cc b/quic/core/chlo_extractor.cc index b4907d4..1605b26 100644 --- a/quic/core/chlo_extractor.cc +++ b/quic/core/chlo_extractor.cc
@@ -11,6 +11,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/crypto_utils.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.h" #include "net/third_party/quiche/src/quic/core/quic_framer.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" @@ -77,6 +78,7 @@ bool OnPaddingFrame(const QuicPaddingFrame& frame) override; bool OnMessageFrame(const QuicMessageFrame& frame) override; bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) override; + bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& farme) override; void OnPacketComplete() override {} bool IsValidStatelessResetToken(QuicUint128 token) const override; void OnAuthenticatedIetfStatelessResetPacket( @@ -289,6 +291,11 @@ return true; } +bool ChloFramerVisitor::OnAckFrequencyFrame( + const QuicAckFrequencyFrame& /*frame*/) { + return true; +} + bool ChloFramerVisitor::IsValidStatelessResetToken( QuicUint128 /*token*/) const { return false;
diff --git a/quic/core/frames/quic_ack_frequency_frame.cc b/quic/core/frames/quic_ack_frequency_frame.cc new file mode 100644 index 0000000..b8c7efa --- /dev/null +++ b/quic/core/frames/quic_ack_frequency_frame.cc
@@ -0,0 +1,20 @@ +// Copyright (c) 2020 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 "net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.h" +#include <cstdint> +#include <limits> + +namespace quic { + +std::ostream& operator<<(std::ostream& os, const QuicAckFrequencyFrame& frame) { + os << "{ control_frame_id: " << frame.control_frame_id + << ", sequence_number: " << frame.sequence_number + << ", packet_tolerance: " << frame.packet_tolerance + << ", max_ack_delay_ms: " << frame.max_ack_delay.ToMilliseconds() + << ", ignore_order: " << frame.ignore_order << " }\n"; + return os; +} + +} // namespace quic
diff --git a/quic/core/frames/quic_ack_frequency_frame.h b/quic/core/frames/quic_ack_frequency_frame.h new file mode 100644 index 0000000..52b70da --- /dev/null +++ b/quic/core/frames/quic_ack_frequency_frame.h
@@ -0,0 +1,45 @@ +// Copyright (c) 2020 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. + +#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_ACK_FREQUENCY_FRAME_H_ +#define QUICHE_QUIC_CORE_FRAMES_QUIC_ACK_FREQUENCY_FRAME_H_ + +#include <cstdint> +#include <ostream> + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +// A frame that allows sender control of acknowledgement delays. +struct QUIC_EXPORT_PRIVATE QuicAckFrequencyFrame { + friend QUIC_EXPORT_PRIVATE std::ostream& operator<<( + std::ostream& os, + const QuicAckFrequencyFrame& ack_frequency_frame); + + // A unique identifier of this control frame. 0 when this frame is received, + // and non-zero when sent. + QuicControlFrameId control_frame_id = kInvalidControlFrameId; + + // If true, do not ack immediately upon observeation of packet reordering. + bool ignore_order = false; + + // Sequence number assigned to the ACK_FREQUENCY frame by the sender to allow + // receivers to ignore obsolete frames. + uint64_t sequence_number = 0; + + // The maximum number of ack-eliciting packets after which the receiver sends + // an acknowledgement. Invald if == 0. + uint64_t packet_tolerance = 0; + + // The maximum time that ack packets can be delayed. + QuicTime::Delta max_ack_delay = QuicTime::Delta::Zero(); +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_ACK_FREQUENCY_FRAME_H_
diff --git a/quic/core/frames/quic_frame.cc b/quic/core/frames/quic_frame.cc index d40202f..99b0963 100644 --- a/quic/core/frames/quic_frame.cc +++ b/quic/core/frames/quic_frame.cc
@@ -6,6 +6,7 @@ #include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h" #include "net/third_party/quiche/src/quic/core/quic_constants.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" @@ -75,6 +76,9 @@ QuicFrame::QuicFrame(QuicNewTokenFrame* frame) : type(NEW_TOKEN_FRAME), new_token_frame(frame) {} +QuicFrame::QuicFrame(QuicAckFrequencyFrame* frame) + : type(ACK_FREQUENCY_FRAME), ack_frequency_frame(frame) {} + void DeleteFrames(QuicFrames* frames) { for (QuicFrame& frame : *frames) { DeleteFrame(&frame); @@ -136,7 +140,9 @@ case NEW_TOKEN_FRAME: delete frame->new_token_frame; break; - + case ACK_FREQUENCY_FRAME: + delete frame->ack_frequency_frame; + break; case NUM_FRAME_TYPES: DCHECK(false) << "Cannot delete type: " << frame->type; } @@ -164,6 +170,7 @@ case PING_FRAME: case STOP_SENDING_FRAME: case HANDSHAKE_DONE_FRAME: + case ACK_FREQUENCY_FRAME: return true; default: return false; @@ -190,6 +197,8 @@ return frame.stop_sending_frame->control_frame_id; case HANDSHAKE_DONE_FRAME: return frame.handshake_done_frame.control_frame_id; + case ACK_FREQUENCY_FRAME: + return frame.ack_frequency_frame->control_frame_id; default: return kInvalidControlFrameId; } @@ -224,6 +233,9 @@ case HANDSHAKE_DONE_FRAME: frame->handshake_done_frame.control_frame_id = control_frame_id; return; + case ACK_FREQUENCY_FRAME: + frame->ack_frequency_frame->control_frame_id = control_frame_id; + return; default: QUIC_BUG << "Try to set control frame id of a frame without control frame id"; @@ -261,6 +273,9 @@ copy = QuicFrame( QuicHandshakeDoneFrame(frame.handshake_done_frame.control_frame_id)); break; + case ACK_FREQUENCY_FRAME: + copy = QuicFrame(new QuicAckFrequencyFrame(*frame.ack_frequency_frame)); + break; default: QUIC_BUG << "Try to copy a non-retransmittable control frame: " << frame; copy = QuicFrame(QuicPingFrame(kInvalidControlFrameId)); @@ -352,6 +367,9 @@ copy = QuicFrame( QuicHandshakeDoneFrame(frame.handshake_done_frame.control_frame_id)); break; + case ACK_FREQUENCY_FRAME: + copy = QuicFrame(new QuicAckFrequencyFrame(*frame.ack_frequency_frame)); + break; default: QUIC_BUG << "Cannot copy frame: " << frame; copy = QuicFrame(QuicPingFrame(kInvalidControlFrameId)); @@ -451,6 +469,9 @@ case HANDSHAKE_DONE_FRAME: os << "type { HANDSHAKE_DONE_FRAME } " << frame.handshake_done_frame; break; + case ACK_FREQUENCY_FRAME: + os << "type { ACK_FREQUENCY_FRAME } " << *(frame.ack_frequency_frame); + break; default: { QUIC_LOG(ERROR) << "Unknown frame type: " << frame.type; break;
diff --git a/quic/core/frames/quic_frame.h b/quic/core/frames/quic_frame.h index 756b69f..a8aabfc 100644 --- a/quic/core/frames/quic_frame.h +++ b/quic/core/frames/quic_frame.h
@@ -9,6 +9,7 @@ #include <vector> #include "net/third_party/quiche/src/quic/core/frames/quic_ack_frame.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.h" #include "net/third_party/quiche/src/quic/core/frames/quic_blocked_frame.h" #include "net/third_party/quiche/src/quic/core/frames/quic_connection_close_frame.h" #include "net/third_party/quiche/src/quic/core/frames/quic_crypto_frame.h" @@ -62,6 +63,7 @@ explicit QuicFrame(QuicStopSendingFrame* frame); explicit QuicFrame(QuicMessageFrame* message_frame); explicit QuicFrame(QuicCryptoFrame* crypto_frame); + explicit QuicFrame(QuicAckFrequencyFrame* ack_frequency_frame); QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(std::ostream& os, const QuicFrame& frame); @@ -102,6 +104,7 @@ QuicStopSendingFrame* stop_sending_frame; QuicMessageFrame* message_frame; QuicCryptoFrame* crypto_frame; + QuicAckFrequencyFrame* ack_frequency_frame; QuicNewTokenFrame* new_token_frame; }; };
diff --git a/quic/core/frames/quic_frames_test.cc b/quic/core/frames/quic_frames_test.cc index 22322ed..3f04c8c 100644 --- a/quic/core/frames/quic_frames_test.cc +++ b/quic/core/frames/quic_frames_test.cc
@@ -15,6 +15,7 @@ #include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h" #include "net/third_party/quiche/src/quic/core/frames/quic_window_update_frame.h" #include "net/third_party/quiche/src/quic/core/quic_interval.h" +#include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" @@ -243,6 +244,25 @@ EXPECT_TRUE(IsControlFrame(frame.type)); } +TEST_F(QuicFramesTest, QuicAckFreuqncyFrameToString) { + QuicAckFrequencyFrame ack_frequency_frame; + ack_frequency_frame.sequence_number = 1; + ack_frequency_frame.packet_tolerance = 2; + ack_frequency_frame.max_ack_delay = QuicTime::Delta::FromMilliseconds(25); + ack_frequency_frame.ignore_order = false; + QuicFrame frame(&ack_frequency_frame); + ASSERT_EQ(ACK_FREQUENCY_FRAME, frame.type); + SetControlFrameId(6, &frame); + EXPECT_EQ(6u, GetControlFrameId(frame)); + std::ostringstream stream; + stream << *frame.ack_frequency_frame; + EXPECT_EQ( + "{ control_frame_id: 6, sequence_number: 1, packet_tolerance: 2, " + "max_ack_delay_ms: 25, ignore_order: 0 }\n", + stream.str()); + EXPECT_TRUE(IsControlFrame(frame.type)); +} + TEST_F(QuicFramesTest, StreamFrameToString) { QuicStreamFrame frame; frame.stream_id = 1; @@ -558,6 +578,9 @@ case HANDSHAKE_DONE_FRAME: frames.push_back(QuicFrame(QuicHandshakeDoneFrame())); break; + case ACK_FREQUENCY_FRAME: + frames.push_back(QuicFrame(new QuicAckFrequencyFrame())); + break; default: ASSERT_TRUE(false) << "Please fix CopyQuicFrames if a new frame type is added.";
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc index b4c454f..2c601bb 100644 --- a/quic/core/quic_connection.cc +++ b/quic/core/quic_connection.cc
@@ -1657,6 +1657,12 @@ return connected_; } +bool QuicConnection::OnAckFrequencyFrame( + const QuicAckFrequencyFrame& /*frame*/) { + // TODO(b/148614353): implement this fully. + QUIC_LOG_EVERY_N_SEC(ERROR, 120) << "Get unexpected AckFrequencyFrame."; + return false; +} bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) { DCHECK(connected_);
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h index 3b602ef..2b1b8af 100644 --- a/quic/core/quic_connection.h +++ b/quic/core/quic_connection.h
@@ -584,6 +584,7 @@ bool OnNewTokenFrame(const QuicNewTokenFrame& frame) override; bool OnMessageFrame(const QuicMessageFrame& frame) override; bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) override; + bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) override; void OnPacketComplete() override; bool IsValidStatelessResetToken(QuicUint128 token) const override; void OnAuthenticatedIetfStatelessResetPacket(
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc index cb1480e..1398965 100644 --- a/quic/core/quic_framer.cc +++ b/quic/core/quic_framer.cc
@@ -6,6 +6,7 @@ #include <cstddef> #include <cstdint> +#include <limits> #include <memory> #include <string> #include <utility> @@ -20,6 +21,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.h" #include "net/third_party/quiche/src/quic/core/quic_connection_id.h" #include "net/third_party/quiche/src/quic/core/quic_constants.h" #include "net/third_party/quiche/src/quic/core/quic_data_reader.h" @@ -28,6 +30,7 @@ #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_socket_address_coder.h" #include "net/third_party/quiche/src/quic/core/quic_stream_frame_data_producer.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quic/core/quic_versions.h" @@ -622,6 +625,17 @@ } // static +size_t QuicFramer::GetAckFrequencyFrameSize( + const QuicAckFrequencyFrame& frame) { + return QuicDataWriter::GetVarInt62Len(IETF_ACK_FREQUENCY) + + QuicDataWriter::GetVarInt62Len(frame.sequence_number) + + QuicDataWriter::GetVarInt62Len(frame.packet_tolerance) + + QuicDataWriter::GetVarInt62Len(frame.max_ack_delay.ToMicroseconds()) + + // One byte for encoding boolean + 1; +} + +// static size_t QuicFramer::GetPathChallengeFrameSize( const QuicPathChallengeFrame& frame) { return kQuicFrameTypeSize + sizeof(frame.data_buffer); @@ -675,7 +689,8 @@ case HANDSHAKE_DONE_FRAME: // HANDSHAKE_DONE has no payload. return kQuicFrameTypeSize; - + case ACK_FREQUENCY_FRAME: + return GetAckFrequencyFrameSize(*frame.ack_frequency_frame); case STREAM_FRAME: case ACK_FRAME: case STOP_WAITING_FRAME: @@ -1185,6 +1200,12 @@ case HANDSHAKE_DONE_FRAME: // HANDSHAKE_DONE has no payload. break; + case ACK_FREQUENCY_FRAME: + if (!AppendAckFrequencyFrame(*frame.ack_frequency_frame, writer)) { + QUIC_BUG << "AppendAckFrequencyFrame failed: " << detailed_error(); + return 0; + } + break; default: set_detailed_error("Tried to append unknown frame type."); RaiseError(QUIC_INVALID_FRAME_DATA); @@ -3349,7 +3370,20 @@ << handshake_done_frame; break; } - + case IETF_ACK_FREQUENCY: { + QuicAckFrequencyFrame frame; + if (!ProcessAckFrequencyFrame(reader, &frame)) { + return RaiseError(QUIC_INVALID_FRAME_DATA); + } + QUIC_DVLOG(2) << ENDPOINT << "Processing IETF ack frequency frame " + << frame; + if (!visitor_->OnAckFrequencyFrame(frame)) { + QUIC_DVLOG(1) << "Visitor asked to stop further processing."; + // Returning true since there was no parsing error. + return true; + } + break; + } default: set_detailed_error("Illegal frame type."); QUIC_DLOG(WARNING) @@ -3529,6 +3563,47 @@ return true; } +bool QuicFramer::ProcessAckFrequencyFrame(QuicDataReader* reader, + QuicAckFrequencyFrame* frame) { + if (!reader->ReadVarInt62(&frame->sequence_number)) { + set_detailed_error("Unable to read sequence number."); + return false; + } + + if (!reader->ReadVarInt62(&frame->packet_tolerance)) { + set_detailed_error("Unable to read packet tolerance."); + return false; + } + if (frame->packet_tolerance == 0) { + set_detailed_error("Invalid packet tolerance."); + return false; + } + uint64_t max_ack_delay_us; + if (!reader->ReadVarInt62(&max_ack_delay_us)) { + set_detailed_error("Unable to read max_ack_delay_us."); + return false; + } + constexpr uint64_t kMaxAckDelayUsBound = 1u << 24; + if (max_ack_delay_us > kMaxAckDelayUsBound) { + set_detailed_error("Invalid max_ack_delay_us."); + return false; + } + frame->max_ack_delay = QuicTime::Delta::FromMicroseconds(max_ack_delay_us); + + uint8_t ignore_order; + if (!reader->ReadUInt8(&ignore_order)) { + set_detailed_error("Unable to read ignore_order."); + return false; + } + if (ignore_order > 1) { + set_detailed_error("Invalid ignore_order."); + return false; + } + frame->ignore_order = ignore_order; + + return true; +} + bool QuicFramer::ProcessAckFrame(QuicDataReader* reader, uint8_t frame_type) { const bool has_ack_blocks = ExtractBit(frame_type, kQuicHasMultipleAckBlocksOffset); @@ -4914,6 +4989,9 @@ case HANDSHAKE_DONE_FRAME: type_byte = IETF_HANDSHAKE_DONE; break; + case ACK_FREQUENCY_FRAME: + type_byte = IETF_ACK_FREQUENCY; + break; default: QUIC_BUG << "Attempt to generate a frame type for an unsupported value: " << frame.type; @@ -5131,6 +5209,29 @@ return true; } +bool QuicFramer::AppendAckFrequencyFrame(const QuicAckFrequencyFrame& frame, + QuicDataWriter* writer) { + if (!writer->WriteVarInt62(frame.sequence_number)) { + set_detailed_error("Writing sequence number failed."); + return false; + } + if (!writer->WriteVarInt62(frame.packet_tolerance)) { + set_detailed_error("Writing packet tolerance failed."); + return false; + } + if (!writer->WriteVarInt62( + static_cast<uint64_t>(frame.max_ack_delay.ToMicroseconds()))) { + set_detailed_error("Writing max_ack_delay_us failed."); + return false; + } + if (!writer->WriteUInt8(static_cast<uint8_t>(frame.ignore_order))) { + set_detailed_error("Writing ignore_order failed."); + return false; + } + + return true; +} + void QuicFramer::set_version(const ParsedQuicVersion version) { DCHECK(IsSupportedVersion(version)) << ParsedQuicVersionToString(version); version_ = version;
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h index 2fc2037..8e63fc2 100644 --- a/quic/core/quic_framer.h +++ b/quic/core/quic_framer.h
@@ -218,6 +218,9 @@ // Called when a handshake done frame has been parsed. virtual bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) = 0; + // Called when an AckFrequencyFrame has been parsed. + virtual bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) = 0; + // Called when a packet has been completely processed. virtual void OnPacketComplete() = 0; @@ -321,6 +324,8 @@ // Size in bytes of all reset stream frame fields. static size_t GetRstStreamFrameSize(QuicTransportVersion version, const QuicRstStreamFrame& frame); + // Size in bytes of all ack frenquency frame fields. + static size_t GetAckFrequencyFrameSize(const QuicAckFrequencyFrame& frame); // Size in bytes of all connection close frame fields, including the error // details. static size_t GetConnectionCloseFrameSize( @@ -489,6 +494,8 @@ bool last_frame_in_packet, QuicDataWriter* writer); bool AppendCryptoFrame(const QuicCryptoFrame& frame, QuicDataWriter* writer); + bool AppendAckFrequencyFrame(const QuicAckFrequencyFrame& frame, + QuicDataWriter* writer); // SetDecrypter sets the primary decrypter, replacing any that already exists. // If an alternative decrypter is in place then the function DCHECKs. This is @@ -917,7 +924,8 @@ bool ProcessCryptoFrame(QuicDataReader* reader, EncryptionLevel encryption_level, QuicCryptoFrame* frame); - + bool ProcessAckFrequencyFrame(QuicDataReader* reader, + QuicAckFrequencyFrame* frame); // IETF frame appending methods. All methods append the type byte as well. bool AppendIetfStreamFrame(const QuicStreamFrame& frame, bool last_frame_in_packet,
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc index a6ed3e7..5a5a8fa 100644 --- a/quic/core/quic_framer_test.cc +++ b/quic/core/quic_framer_test.cc
@@ -406,6 +406,15 @@ return true; } + bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) override { + ++frame_count_; + ack_frequency_frames_.emplace_back( + std::make_unique<QuicAckFrequencyFrame>(frame)); + DCHECK(VersionHasIetfQuicFrames(transport_version_)); + EXPECT_EQ(IETF_ACK_FREQUENCY, framer_->current_received_frame_type()); + return true; + } + void OnPacketComplete() override { ++complete_packets_; } bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override { @@ -574,6 +583,7 @@ std::vector<std::unique_ptr<QuicPingFrame>> ping_frames_; std::vector<std::unique_ptr<QuicMessageFrame>> message_frames_; std::vector<std::unique_ptr<QuicHandshakeDoneFrame>> handshake_done_frames_; + std::vector<std::unique_ptr<QuicAckFrequencyFrame>> ack_frequency_frames_; std::vector<std::unique_ptr<QuicEncryptedPacket>> coalesced_packets_; std::vector<std::unique_ptr<QuicEncryptedPacket>> undecryptable_packets_; std::vector<EncryptionLevel> undecryptable_decryption_levels_; @@ -5159,6 +5169,52 @@ EXPECT_EQ(1u, visitor_.handshake_done_frames_.size()); } +TEST_P(QuicFramerTest, ParseAckFrequencyFrame) { + SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // ack frequency frame type (which needs two bytes as it is > 0x3F) + 0x40, 0xAF, + // sequence_number + 0x11, + // packet_tolerance + 0x02, + // max_ack_delay_us = 2'5000 us + 0x80, 0x00, 0x61, 0xA8, + // ignore_order + 0x01 + }; + // clang-format on + + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + + QuicEncryptedPacket encrypted(AsChars(packet), QUICHE_ARRAYSIZE(packet), + false); + EXPECT_TRUE(framer_.ProcessPacket(encrypted)); + + EXPECT_THAT(framer_.error(), IsQuicNoError()); + ASSERT_TRUE(visitor_.header_.get()); + EXPECT_TRUE(CheckDecryption( + encrypted, !kIncludeVersion, !kIncludeDiversificationNonce, + PACKET_8BYTE_CONNECTION_ID, PACKET_0BYTE_CONNECTION_ID)); + + ASSERT_EQ(1u, visitor_.ack_frequency_frames_.size()); + const auto& frame = visitor_.ack_frequency_frames_.front(); + EXPECT_EQ(17u, frame->sequence_number); + EXPECT_EQ(2u, frame->packet_tolerance); + EXPECT_EQ(2'5000u, frame->max_ack_delay.ToMicroseconds()); + EXPECT_EQ(true, frame->ignore_order); +} + TEST_P(QuicFramerTest, MessageFrame) { if (!VersionSupportsMessageFrames(framer_.transport_version())) { return; @@ -8607,6 +8663,53 @@ QUICHE_ARRAYSIZE(packet)); } +TEST_P(QuicFramerTest, BuildAckFrequencyPacket) { + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicAckFrequencyFrame ack_frequency_frame; + ack_frequency_frame.sequence_number = 3; + ack_frequency_frame.packet_tolerance = 5; + ack_frequency_frame.max_ack_delay = QuicTime::Delta::FromMicroseconds(0x3fff); + ack_frequency_frame.ignore_order = false; + QuicFrames frames = {QuicFrame(&ack_frequency_frame)}; + + // clang-format off + unsigned char packet[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10, + // packet number + 0x12, 0x34, 0x56, 0x78, + + // frame type (Ack Frequency frame) + 0x40, 0xaf, + // sequence number + 0x03, + // packet tolerance + 0x05, + // max_ack_delay_us + 0x7f, 0xff, + // ignore_oder + 0x00 + }; + // clang-format on + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + + quiche::test::CompareCharArraysWithHexError( + "constructed packet", data->data(), data->length(), AsChars(packet), + QUICHE_ARRAYSIZE(packet)); +} + TEST_P(QuicFramerTest, BuildMessagePacket) { if (!VersionSupportsMessageFrames(framer_.transport_version())) { return;
diff --git a/quic/core/quic_trace_visitor.cc b/quic/core/quic_trace_visitor.cc index edbce0c..eeaf297 100644 --- a/quic/core/quic_trace_visitor.cc +++ b/quic/core/quic_trace_visitor.cc
@@ -64,6 +64,7 @@ case BLOCKED_FRAME: case PING_FRAME: case HANDSHAKE_DONE_FRAME: + case ACK_FREQUENCY_FRAME: PopulateFrameInfo(frame, event->add_frames()); break; @@ -218,6 +219,7 @@ case MESSAGE_FRAME: case CRYPTO_FRAME: case NEW_TOKEN_FRAME: + case ACK_FREQUENCY_FRAME: break; case NUM_FRAME_TYPES:
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h index 8a37475..0c736f9 100644 --- a/quic/core/quic_types.h +++ b/quic/core/quic_types.h
@@ -250,6 +250,7 @@ MESSAGE_FRAME, NEW_TOKEN_FRAME, RETIRE_CONNECTION_ID_FRAME, + ACK_FREQUENCY_FRAME, NUM_FRAME_TYPES }; @@ -309,6 +310,9 @@ IETF_EXTENSION_MESSAGE = 0x21, IETF_EXTENSION_MESSAGE_NO_LENGTH_V99 = 0x30, IETF_EXTENSION_MESSAGE_V99 = 0x31, + + // An QUIC extension frame for sender control of acknowledgement delays + IETF_ACK_FREQUENCY = 0xaf }; QUIC_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, const QuicIetfFrameType& c);
diff --git a/quic/core/tls_chlo_extractor.h b/quic/core/tls_chlo_extractor.h index 1762566..b50d2e2 100644 --- a/quic/core/tls_chlo_extractor.h +++ b/quic/core/tls_chlo_extractor.h
@@ -9,6 +9,7 @@ #include <string> #include <vector> #include "third_party/boringssl/src/include/openssl/ssl.h" +#include "net/third_party/quiche/src/quic/core/frames/quic_ack_frequency_frame.h" #include "net/third_party/quiche/src/quic/core/quic_framer.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/core/quic_stream_sequencer.h" @@ -151,6 +152,9 @@ bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& /*frame*/) override { return true; } + bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& /*frame*/) override { + return true; + } void OnPacketComplete() override {} bool IsValidStatelessResetToken(QuicUint128 /*token*/) const override { return true;
diff --git a/quic/test_tools/quic_test_utils.cc b/quic/test_tools/quic_test_utils.cc index ce2f7b8..41fb995 100644 --- a/quic/test_tools/quic_test_utils.cc +++ b/quic/test_tools/quic_test_utils.cc
@@ -419,6 +419,11 @@ return true; } +bool NoOpFramerVisitor::OnAckFrequencyFrame( + const QuicAckFrequencyFrame& /*frame*/) { + return true; +} + bool NoOpFramerVisitor::IsValidStatelessResetToken( QuicUint128 /*token*/) const { return false;
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h index f4f2aeb..2af8aeb 100644 --- a/quic/test_tools/quic_test_utils.h +++ b/quic/test_tools/quic_test_utils.h
@@ -396,6 +396,10 @@ OnHandshakeDoneFrame, (const QuicHandshakeDoneFrame& frame), (override)); + MOCK_METHOD(bool, + OnAckFrequencyFrame, + (const QuicAckFrequencyFrame& frame), + (override)); MOCK_METHOD(void, OnPacketComplete, (), (override)); MOCK_METHOD(bool, IsValidStatelessResetToken, @@ -460,6 +464,7 @@ bool OnBlockedFrame(const QuicBlockedFrame& frame) override; bool OnMessageFrame(const QuicMessageFrame& frame) override; bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) override; + bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) override; void OnPacketComplete() override {} bool IsValidStatelessResetToken(QuicUint128 token) const override; void OnAuthenticatedIetfStatelessResetPacket(
diff --git a/quic/test_tools/simple_quic_framer.cc b/quic/test_tools/simple_quic_framer.cc index 3d2652f..8ec56dc 100644 --- a/quic/test_tools/simple_quic_framer.cc +++ b/quic/test_tools/simple_quic_framer.cc
@@ -204,6 +204,11 @@ return true; } + bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) override { + ack_frequency_frames_.push_back(frame); + return true; + } + void OnPacketComplete() override {} bool IsValidStatelessResetToken(QuicUint128 /*token*/) const override { @@ -295,6 +300,7 @@ std::vector<QuicNewTokenFrame> new_token_frames_; std::vector<QuicMessageFrame> message_frames_; std::vector<QuicHandshakeDoneFrame> handshake_done_frames_; + std::vector<QuicAckFrequencyFrame> ack_frequency_frames_; std::vector<std::unique_ptr<std::string>> stream_data_; std::vector<std::unique_ptr<std::string>> crypto_data_; EncryptionLevel last_decrypted_level_;
diff --git a/quic/tools/quic_packet_printer_bin.cc b/quic/tools/quic_packet_printer_bin.cc index 798b66e..3192d30 100644 --- a/quic/tools/quic_packet_printer_bin.cc +++ b/quic/tools/quic_packet_printer_bin.cc
@@ -207,6 +207,10 @@ std::cerr << "OnHandshakeDoneFrame: " << frame; return true; } + bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) override { + std::cerr << "OnAckFrequencyFrame: " << frame; + return true; + } void OnPacketComplete() override { std::cerr << "OnPacketComplete\n"; } bool IsValidStatelessResetToken(QuicUint128 /*token*/) const override { std::cerr << "IsValidStatelessResetToken\n";