Receive ECN marks on inbound packets and report counts in ACK_ECN frames. It does not support marking outbound packets except for a minimal capability for test. QuicPacketReader receives ECN marks from the socket API via QuicUdpPacketInfo.. QuicPacketReader then stores this in a new member of QuicReceivedPacket. This arrives at QuicConnection, which adds to the last_received_packet_info_. When the framer calls QuicConnection::OnPacketHeader(), QuicConnection updates the pending ack frame with the new ECN count via QuicReceivedPacketManager, which updates the counts in the ack frame. To enable an end-to-end test, this CL also includes minimal changes to mark outbound packets and report the contents of ACK_ECN frames. QuicConnection::per_packet_options_ is extended to include an ECN codepoint, which QuicDefaultPacketWriter reads, and passes the instruction to the QuicUdpSocketApi via QuicUdpPacketInfo. The test explicitly sets per_packet_options_ to mark ECT(0) after the handshake. When receiving ACK_ECN, the results are reported to QuicConnection in OnAckFrameEnd(), which then just stores them in a new data structure QuicEcnCounts. Protected by FLAGS_quic_reloadable_flag_quic_receive_ecn. PiperOrigin-RevId: 501596181
diff --git a/quiche/quic/core/chlo_extractor.cc b/quiche/quic/core/chlo_extractor.cc index c3dc584..267823e 100644 --- a/quiche/quic/core/chlo_extractor.cc +++ b/quiche/quic/core/chlo_extractor.cc
@@ -59,6 +59,7 @@ bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override; bool OnAckTimestamp(QuicPacketNumber packet_number, QuicTime timestamp) override; + void OnAckEcnCounts(const QuicEcnCounts& ecn_counts) override; bool OnAckFrameEnd(QuicPacketNumber start) override; bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override; bool OnPingFrame(const QuicPingFrame& frame) override; @@ -219,6 +220,8 @@ return true; } +void ChloFramerVisitor::OnAckEcnCounts(const QuicEcnCounts& /*ecn_counts*/) {} + bool ChloFramerVisitor::OnAckFrameEnd(QuicPacketNumber /*start*/) { return true; }
diff --git a/quiche/quic/core/frames/quic_ack_frame.cc b/quiche/quic/core/frames/quic_ack_frame.cc index cd14d9d..1e42b7d 100644 --- a/quiche/quic/core/frames/quic_ack_frame.cc +++ b/quiche/quic/core/frames/quic_ack_frame.cc
@@ -42,11 +42,11 @@ os << p.first << " at " << p.second.ToDebuggingValue() << " "; } os << " ]"; - os << ", ecn_counters_populated: " << ack_frame.ecn_counters_populated; - if (ack_frame.ecn_counters_populated) { - os << ", ect_0_count: " << ack_frame.ect_0_count - << ", ect_1_count: " << ack_frame.ect_1_count - << ", ecn_ce_count: " << ack_frame.ecn_ce_count; + os << ", ecn_counters_populated: " << ack_frame.ecn_counters.has_value(); + if (ack_frame.ecn_counters.has_value()) { + os << ", ect_0_count: " << ack_frame.ecn_counters->ect0 + << ", ect_1_count: " << ack_frame.ecn_counters->ect1 + << ", ecn_ce_count: " << ack_frame.ecn_counters->ce; } os << " }\n";
diff --git a/quiche/quic/core/frames/quic_ack_frame.h b/quiche/quic/core/frames/quic_ack_frame.h index 4a20e66..6828a8c 100644 --- a/quiche/quic/core/frames/quic_ack_frame.h +++ b/quiche/quic/core/frames/quic_ack_frame.h
@@ -115,12 +115,8 @@ // Set of packets. PacketNumberQueue packets; - // ECN counters, used only in version 99's ACK frame and valid only when - // |ecn_counters_populated| is true. - bool ecn_counters_populated = false; - QuicPacketCount ect_0_count = 0; - QuicPacketCount ect_1_count = 0; - QuicPacketCount ecn_ce_count = 0; + // ECN counters. + absl::optional<QuicEcnCounts> ecn_counters; }; // The highest acked packet number we've observed from the peer. If no packets
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc index a6e97f9..2524521 100644 --- a/quiche/quic/core/http/end_to_end_test.cc +++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -7116,6 +7116,52 @@ server_thread_->Resume(); } +TEST_P(EndToEndTest, EcnMarksReportedCorrectly) { + // Client connects using not-ECT. + ASSERT_TRUE(Initialize()); + QuicConnection* client_connection = GetClientConnection(); + QuicEcnCounts* ecn = + QuicConnectionPeer::GetEcnCounts(client_connection, APPLICATION_DATA); + EXPECT_EQ(ecn->ect0, 0); + EXPECT_EQ(ecn->ect1, 0); + EXPECT_EQ(ecn->ce, 0); + QuicPacketCount ect0 = 0, ect1 = 0; + TestPerPacketOptions options; + client_connection->set_per_packet_options(&options); + for (QuicEcnCodepoint codepoint : {ECN_NOT_ECT, ECN_ECT0, ECN_ECT1, ECN_CE}) { + options.ecn_codepoint = codepoint; + client_->SendSynchronousRequest("/foo"); + if (!GetQuicRestartFlag(quic_receive_ecn) || + !GetQuicRestartFlag(quic_quiche_ecn_sockets) || + !VersionHasIetfQuicFrames(version_.transport_version) || + codepoint == ECN_NOT_ECT) { + EXPECT_EQ(ecn->ect0, 0); + EXPECT_EQ(ecn->ect1, 0); + EXPECT_EQ(ecn->ce, 0); + continue; + } + EXPECT_GT(ecn->ect0, 0); + if (codepoint == ECN_CE) { + EXPECT_EQ(ect0, ecn->ect0); // No more ECT(0) arriving + EXPECT_GE(ecn->ect1, ect1); // Late-arriving ECT(1) control packets + EXPECT_GT(ecn->ce, 0); + continue; + } + EXPECT_EQ(ecn->ce, 0); + if (codepoint == ECN_ECT1) { + EXPECT_GE(ecn->ect0, ect0); // Late-arriving ECT(0) control packets + ect0 = ecn->ect0; + ect1 = ecn->ect1; + EXPECT_GT(ect1, 0); + continue; + } + // codepoint == ECN_ECT0 + ect0 = ecn->ect0; + EXPECT_EQ(ecn->ect1, 0); + } + client_->Disconnect(); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc index f22d8ed..053e33b 100644 --- a/quiche/quic/core/quic_connection.cc +++ b/quiche/quic/core/quic_connection.cc
@@ -1328,7 +1328,8 @@ } uber_received_packet_manager_.RecordPacketReceived( last_received_packet_info_.decrypted_level, - last_received_packet_info_.header, receipt_time); + last_received_packet_info_.header, receipt_time, + last_received_packet_info_.ecn_codepoint); if (EnforceAntiAmplificationLimit() && !IsHandshakeConfirmed() && !header.retry_token.empty() && visitor_->ValidateToken(header.retry_token)) { @@ -1494,6 +1495,14 @@ return true; } +void QuicConnection::OnAckEcnCounts(const QuicEcnCounts& ecn_counts) { + QUIC_DVLOG(1) << ENDPOINT << "OnAckEcnCounts: [" << ecn_counts.ToString() + << "]"; + PacketNumberSpace space = QuicUtils::GetPacketNumberSpace( + last_received_packet_info_.decrypted_level); + peer_ack_ecn_counts_[space] = ecn_counts; +} + bool QuicConnection::OnAckFrameEnd(QuicPacketNumber start) { QUIC_BUG_IF(quic_bug_12714_7, !connected_) << "Processing ACK frame end when connection is closed. Received packet " @@ -2671,8 +2680,9 @@ if (debug_visitor_ != nullptr) { debug_visitor_->OnPacketReceived(self_address, peer_address, packet); } - last_received_packet_info_ = ReceivedPacketInfo( - self_address, peer_address, packet.receipt_time(), packet.length()); + last_received_packet_info_ = + ReceivedPacketInfo(self_address, peer_address, packet.receipt_time(), + packet.length(), packet.ecn_codepoint()); current_packet_data_ = packet.data(); if (!default_path_.self_address.IsInitialized()) { @@ -4807,11 +4817,12 @@ QuicConnection::ReceivedPacketInfo::ReceivedPacketInfo( const QuicSocketAddress& destination_address, const QuicSocketAddress& source_address, QuicTime receipt_time, - QuicByteCount length) + QuicByteCount length, QuicEcnCodepoint ecn_codepoint) : destination_address(destination_address), source_address(source_address), receipt_time(receipt_time), - length(length) {} + length(length), + ecn_codepoint(ecn_codepoint) {} std::ostream& operator<<(std::ostream& os, const QuicConnection::ReceivedPacketInfo& info) {
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h index b71904d..9fb1aaa 100644 --- a/quiche/quic/core/quic_connection.h +++ b/quiche/quic/core/quic_connection.h
@@ -673,6 +673,7 @@ bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override; bool OnAckTimestamp(QuicPacketNumber packet_number, QuicTime timestamp) override; + void OnAckEcnCounts(const quic::QuicEcnCounts& ecn_counts) override; bool OnAckFrameEnd(QuicPacketNumber start) override; bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override; bool OnPaddingFrame(const QuicPaddingFrame& frame) override; @@ -1482,7 +1483,8 @@ explicit ReceivedPacketInfo(QuicTime receipt_time); ReceivedPacketInfo(const QuicSocketAddress& destination_address, const QuicSocketAddress& source_address, - QuicTime receipt_time, QuicByteCount length); + QuicTime receipt_time, QuicByteCount length, + QuicEcnCodepoint ecn_codepoint); QuicSocketAddress destination_address; QuicSocketAddress source_address; @@ -1496,6 +1498,7 @@ EncryptionLevel decrypted_level = ENCRYPTION_INITIAL; QuicPacketHeader header; absl::InlinedVector<QuicFrameType, 1> frames; + QuicEcnCodepoint ecn_codepoint; }; QUIC_EXPORT_PRIVATE friend std::ostream& operator<<( @@ -2316,6 +2319,11 @@ GetQuicFlag(quic_enforce_strict_amplification_factor); ConnectionIdGeneratorInterface& connection_id_generator_; + + // Most recent ECN codepoint counts received in ACK_ECN frames sent from the + // peer. For now, this is only stored for tests. + QuicEcnCounts + peer_ack_ecn_counts_[PacketNumberSpace::NUM_PACKET_NUMBER_SPACES]; }; } // namespace quic
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc index b567587..a3a4144 100644 --- a/quiche/quic/core/quic_connection_test.cc +++ b/quiche/quic/core/quic_connection_test.cc
@@ -16497,6 +16497,61 @@ EXPECT_TRUE(alt_path->validated); } +TEST_P(QuicConnectionTest, EcnMarksCorrectlyRecorded) { + set_perspective(Perspective::IS_SERVER); + QuicPacketHeader header = ConstructPacketHeader(1, ENCRYPTION_FORWARD_SECURE); + QuicFrames frames; + QuicPingFrame ping_frame; + QuicPaddingFrame padding_frame; + frames.push_back(QuicFrame(ping_frame)); + frames.push_back(QuicFrame(padding_frame)); + std::unique_ptr<QuicPacket> packet = + BuildUnsizedDataPacket(&peer_framer_, header, frames); + char buffer[kMaxOutgoingPacketSize]; + size_t encrypted_length = peer_framer_.EncryptPayload( + ENCRYPTION_FORWARD_SECURE, QuicPacketNumber(1), *packet, buffer, + kMaxOutgoingPacketSize); + QuicReceivedPacket received_packet(buffer, encrypted_length, clock_.Now(), + false, 0, true, nullptr, 0, false, + ECN_ECT0); + if (connection_.SupportsMultiplePacketNumberSpaces()) { + EXPECT_FALSE(connection_.received_packet_manager() + .GetAckFrame(APPLICATION_DATA) + .ecn_counters.has_value()); + connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, received_packet); + if (GetQuicRestartFlag(quic_receive_ecn)) { + EXPECT_TRUE(connection_.received_packet_manager() + .GetAckFrame(APPLICATION_DATA) + .ecn_counters.has_value()); + EXPECT_EQ(connection_.received_packet_manager() + .GetAckFrame(APPLICATION_DATA) + .ecn_counters->ect0, + 1); + } else { + EXPECT_FALSE(connection_.received_packet_manager() + .GetAckFrame(APPLICATION_DATA) + .ecn_counters.has_value()); + } + } else { + EXPECT_FALSE(connection_.received_packet_manager() + .ack_frame() + .ecn_counters.has_value()); + connection_.ProcessUdpPacket(kSelfAddress, kPeerAddress, received_packet); + if (GetQuicRestartFlag(quic_receive_ecn)) { + EXPECT_TRUE(connection_.received_packet_manager() + .ack_frame() + .ecn_counters.has_value()); + EXPECT_EQ( + connection_.received_packet_manager().ack_frame().ecn_counters->ect0, + 1); + } else { + EXPECT_FALSE(connection_.received_packet_manager() + .ack_frame() + .ecn_counters.has_value()); + } + } +} + } // namespace } // namespace test } // namespace quic
diff --git a/quiche/quic/core/quic_default_packet_writer.cc b/quiche/quic/core/quic_default_packet_writer.cc index 3ba64b0..78feee5 100644 --- a/quiche/quic/core/quic_default_packet_writer.cc +++ b/quiche/quic/core/quic_default_packet_writer.cc
@@ -17,11 +17,12 @@ const char* buffer, size_t buf_len, const QuicIpAddress& self_address, const QuicSocketAddress& peer_address, PerPacketOptions* options) { QUICHE_DCHECK(!write_blocked_); - QUICHE_DCHECK(nullptr == options) - << "QuicDefaultPacketWriter does not accept any options."; QuicUdpPacketInfo packet_info; packet_info.SetPeerAddress(peer_address); packet_info.SetSelfIp(self_address); + if (options != nullptr) { + packet_info.SetEcnCodepoint(options->ecn_codepoint); + } WriteResult result = QuicUdpSocketApi().WritePacket(fd_, buffer, buf_len, packet_info); if (IsWriteBlockedStatus(result.status)) {
diff --git a/quiche/quic/core/quic_flags_list.h b/quiche/quic/core/quic_flags_list.h index b205e3e..d9333b2 100644 --- a/quiche/quic/core/quic_flags_list.h +++ b/quiche/quic/core/quic_flags_list.h
@@ -87,6 +87,8 @@ QUIC_FLAG(quic_reloadable_flag_quic_default_to_bbr, false) // When true, quiche UDP sockets report Explicit Congestion Notification (ECN) [RFC3168, RFC9330] results. QUIC_FLAG(quic_restart_flag_quic_quiche_ecn_sockets, false) +// When true, report received ECN markings to the peer. +QUIC_FLAG(quic_restart_flag_quic_receive_ecn, false) // When true, support draft-ietf-quic-v2-08 QUIC_FLAG(quic_reloadable_flag_quic_enable_version_2_draft_08, false) // When true, the BB2U copt causes BBR2 to wait two rounds with out draining the queue before exiting PROBE_UP and BB2S has the same effect in STARTUP.
diff --git a/quiche/quic/core/quic_framer.cc b/quiche/quic/core/quic_framer.cc index b362c51..8a506fd 100644 --- a/quiche/quic/core/quic_framer.cc +++ b/quiche/quic/core/quic_framer.cc
@@ -22,6 +22,7 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" +#include "absl/types/optional.h" #include "quiche/quic/core/crypto/crypto_framer.h" #include "quiche/quic/core/crypto/crypto_handshake.h" #include "quiche/quic/core/crypto/crypto_handshake_message.h" @@ -392,6 +393,16 @@ ":", initial_error_string); } +// Return the minimum size of the ECN fields in an ACK frame +size_t AckEcnCountSize(const QuicAckFrame& ack_frame) { + if (!ack_frame.ecn_counters.has_value()) { + return 0; + } + return (QuicDataWriter::GetVarInt62Len(ack_frame.ecn_counters->ect0) + + QuicDataWriter::GetVarInt62Len(ack_frame.ecn_counters->ect1) + + QuicDataWriter::GetVarInt62Len(ack_frame.ecn_counters->ce)); +} + } // namespace QuicFramer::QuicFramer(const ParsedQuicVersionVector& supported_versions, @@ -499,13 +510,8 @@ if (use_ietf_ack_with_receive_timestamp) { // 0 Timestamp Range Count. min_size += QuicDataWriter::GetVarInt62Len(0); - } else if (ack_frame.ecn_counters_populated && - (ack_frame.ect_0_count || ack_frame.ect_1_count || - ack_frame.ecn_ce_count)) { - // ECN counts. - min_size += (QuicDataWriter::GetVarInt62Len(ack_frame.ect_0_count) + - QuicDataWriter::GetVarInt62Len(ack_frame.ect_1_count) + - QuicDataWriter::GetVarInt62Len(ack_frame.ecn_ce_count)); + } else { + min_size += AckEcnCountSize(ack_frame); } return min_size; } @@ -4113,32 +4119,32 @@ ack_block_count--; } + QUICHE_DCHECK(!ack_frame->ecn_counters.has_value()); if (frame_type == IETF_ACK_RECEIVE_TIMESTAMPS) { QUICHE_DCHECK(process_timestamps_); if (!ProcessIetfTimestampsInAckFrame(ack_frame->largest_acked, reader)) { return false; } } else if (frame_type == IETF_ACK_ECN) { - ack_frame->ecn_counters_populated = true; - if (!reader->ReadVarInt62(&ack_frame->ect_0_count)) { + ack_frame->ecn_counters = QuicEcnCounts(); + if (!reader->ReadVarInt62(&ack_frame->ecn_counters->ect0)) { set_detailed_error("Unable to read ack ect_0_count."); return false; } - if (!reader->ReadVarInt62(&ack_frame->ect_1_count)) { + if (!reader->ReadVarInt62(&ack_frame->ecn_counters->ect1)) { set_detailed_error("Unable to read ack ect_1_count."); return false; } - if (!reader->ReadVarInt62(&ack_frame->ecn_ce_count)) { + if (!reader->ReadVarInt62(&ack_frame->ecn_counters->ce)) { 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 (GetQuicRestartFlag(quic_receive_ecn)) { + QUIC_RESTART_FLAG_COUNT_N(quic_receive_ecn, 2, 3); + visitor_->OnAckEcnCounts(*ack_frame->ecn_counters); + } } - // 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."); @@ -5103,12 +5109,8 @@ if (UseIetfAckWithReceiveTimestamp(frame)) { ack_frame_size += GetIetfAckFrameTimestampSize(frame); - } else if (frame.ecn_counters_populated && - (frame.ect_0_count || frame.ect_1_count || frame.ecn_ce_count)) { - // ECN counts. - ack_frame_size += QuicDataWriter::GetVarInt62Len(frame.ect_0_count); - ack_frame_size += QuicDataWriter::GetVarInt62Len(frame.ect_1_count); - ack_frame_size += QuicDataWriter::GetVarInt62Len(frame.ecn_ce_count); + } else { + ack_frame_size += AckEcnCountSize(frame); } return ack_frame_size; @@ -6040,13 +6042,10 @@ uint64_t ecn_size = 0; if (UseIetfAckWithReceiveTimestamp(frame)) { type = IETF_ACK_RECEIVE_TIMESTAMPS; - } else if (frame.ecn_counters_populated && - (frame.ect_0_count || frame.ect_1_count || frame.ecn_ce_count)) { + } else if (frame.ecn_counters.has_value()) { // 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)); + ecn_size = AckEcnCountSize(frame); } if (!writer->WriteVarInt62(type)) { @@ -6141,15 +6140,15 @@ if (type == IETF_ACK_ECN) { // Encode the ECN counts. - if (!writer->WriteVarInt62(frame.ect_0_count)) { + if (!writer->WriteVarInt62(frame.ecn_counters->ect0)) { set_detailed_error("No room for ect_0_count in ack frame"); return false; } - if (!writer->WriteVarInt62(frame.ect_1_count)) { + if (!writer->WriteVarInt62(frame.ecn_counters->ect1)) { set_detailed_error("No room for ect_1_count in ack frame"); return false; } - if (!writer->WriteVarInt62(frame.ecn_ce_count)) { + if (!writer->WriteVarInt62(frame.ecn_counters->ce)) { set_detailed_error("No room for ecn_ce_count in ack frame"); return false; }
diff --git a/quiche/quic/core/quic_framer.h b/quiche/quic/core/quic_framer.h index 3f1d011..5b0e8e2 100644 --- a/quiche/quic/core/quic_framer.h +++ b/quiche/quic/core/quic_framer.h
@@ -164,6 +164,10 @@ virtual bool OnAckTimestamp(QuicPacketNumber packet_number, QuicTime timestamp) = 0; + // Called when an ACK frame arrives that includes Explicit Congestion + // Notification (ECN) packet counts. + virtual void OnAckEcnCounts(const QuicEcnCounts& ecn_counts) = 0; + // Called after the last ack range in an AckFrame has been parsed. // |start| is the starting value of the last ack range. virtual bool OnAckFrameEnd(QuicPacketNumber start) = 0;
diff --git a/quiche/quic/core/quic_framer_test.cc b/quiche/quic/core/quic_framer_test.cc index d2eae77..2c82bcb 100644 --- a/quiche/quic/core/quic_framer_test.cc +++ b/quiche/quic/core/quic_framer_test.cc
@@ -51,6 +51,7 @@ const uint64_t kMask = kEpoch - 1; const uint8_t kPacket0ByteConnectionId = 0; const uint8_t kPacket8ByteConnectionId = 8; +constexpr size_t kTagSize = 16; const StatelessResetToken kTestStatelessResetToken{ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, @@ -396,6 +397,8 @@ return true; } + void OnAckEcnCounts(const QuicEcnCounts& /*ecn_counts*/) override {} + bool OnAckFrameEnd(QuicPacketNumber /*start*/) override { return true; } bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override { @@ -10646,10 +10649,7 @@ 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; + ack_frame.ecn_counters = QuicEcnCounts(100, 10000, 1000000); QuicFrames frames = {QuicFrame(&ack_frame)}; // Build an ACK packet. QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); @@ -16480,6 +16480,67 @@ EXPECT_EQ(detailed_error, ""); } +TEST_P(QuicFramerTest, ReportEcnCountsIfPresent) { + 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; + + for (bool ecn_marks : { false, true }) { + // Add some padding, because TestEncrypter doesn't add an authentication + // tag. For a small packet, this will cause QuicFramer to fail to get a + // header protection sample. + QuicPaddingFrame padding_frame(kTagSize); + // Create a packet with just an ack. + QuicAckFrame ack_frame = InitAckFrame(5); + if (ecn_marks) { + ack_frame.ecn_counters = QuicEcnCounts(100, 10000, 1000000); + } else { + ack_frame.ecn_counters = absl::nullopt; + } + QuicFrames frames = {QuicFrame(padding_frame), 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); + MockFramerVisitor visitor; + framer_.set_visitor(&visitor); + EXPECT_CALL(visitor, OnPacket()).Times(1); + EXPECT_CALL(visitor, OnUnauthenticatedPublicHeader(_)) + .Times(1) + .WillOnce(Return(true)); + EXPECT_CALL(visitor, OnUnauthenticatedHeader(_)) + .Times(1) + .WillOnce(Return(true)); + EXPECT_CALL(visitor, OnPacketHeader(_)).Times(1); + EXPECT_CALL(visitor, OnDecryptedPacket(_, _)).Times(1); + EXPECT_CALL(visitor, OnAckFrameStart(_, _)).Times(1).WillOnce(Return(true)); + EXPECT_CALL(visitor, OnAckRange(_, _)).Times(1).WillOnce(Return(true)); + if (GetQuicRestartFlag(quic_receive_ecn) && ecn_marks) { + EXPECT_CALL(visitor, OnAckEcnCounts(_)).Times(1); + } else { + EXPECT_CALL(visitor, OnAckEcnCounts(_)).Times(0); + } + EXPECT_CALL(visitor, OnAckFrameEnd(_)).Times(1).WillOnce(Return(true)); + EXPECT_CALL(visitor, OnPacketComplete()).Times(1); + ASSERT_TRUE(framer_.ProcessPacket( + QuicEncryptedPacket(buffer, encrypted_length, false))); + } +} + } // namespace } // namespace test } // namespace quic
diff --git a/quiche/quic/core/quic_packet_reader.cc b/quiche/quic/core/quic_packet_reader.cc index ec72824..eaa3441 100644 --- a/quiche/quic/core/quic_packet_reader.cc +++ b/quiche/quic/core/quic_packet_reader.cc
@@ -7,6 +7,8 @@ #include "absl/base/macros.h" #include "quiche/quic/core/quic_packets.h" #include "quiche/quic/core/quic_process_packet_interface.h" +#include "quiche/quic/core/quic_udp_socket.h" +#include "quiche/quic/core/quic_utils.h" #include "quiche/quic/platform/api/quic_bug_tracker.h" #include "quiche/quic/platform/api/quic_flag_utils.h" #include "quiche/quic/platform/api/quic_flags.h" @@ -47,15 +49,19 @@ // arriving at the host and now is considered part of the network delay. QuicTime now = clock.Now(); - size_t packets_read = socket_api_.ReadMultiplePackets( - fd, - BitMask64(QuicUdpPacketInfoBit::DROPPED_PACKETS, - QuicUdpPacketInfoBit::PEER_ADDRESS, - QuicUdpPacketInfoBit::V4_SELF_IP, - QuicUdpPacketInfoBit::V6_SELF_IP, - QuicUdpPacketInfoBit::RECV_TIMESTAMP, QuicUdpPacketInfoBit::TTL, - QuicUdpPacketInfoBit::GOOGLE_PACKET_HEADER), - &read_results_); + BitMask64 info_bits{QuicUdpPacketInfoBit::DROPPED_PACKETS, + QuicUdpPacketInfoBit::PEER_ADDRESS, + QuicUdpPacketInfoBit::V4_SELF_IP, + QuicUdpPacketInfoBit::V6_SELF_IP, + QuicUdpPacketInfoBit::RECV_TIMESTAMP, + QuicUdpPacketInfoBit::TTL, + QuicUdpPacketInfoBit::GOOGLE_PACKET_HEADER}; + if (GetQuicRestartFlag(quic_receive_ecn)) { + QUIC_RESTART_FLAG_COUNT_N(quic_receive_ecn, 3, 3); + info_bits.Set(QuicUdpPacketInfoBit::ECN); + } + size_t packets_read = + socket_api_.ReadMultiplePackets(fd, info_bits, &read_results_); for (size_t i = 0; i < packets_read; ++i) { auto& result = read_results_[i]; if (!result.ok) { @@ -97,8 +103,7 @@ QuicReceivedPacket packet( result.packet_buffer.buffer, result.packet_buffer.buffer_len, now, /*owns_buffer=*/false, ttl, has_ttl, headers, headers_length, - /*owns_header_buffer=*/false); - + /*owns_header_buffer=*/false, result.packet_info.ecn_codepoint()); QuicSocketAddress self_address(self_ip, port); processor->ProcessPacket(self_address, peer_address, packet); }
diff --git a/quiche/quic/core/quic_packet_writer.h b/quiche/quic/core/quic_packet_writer.h index 95d167b..5ebbfe0 100644 --- a/quiche/quic/core/quic_packet_writer.h +++ b/quiche/quic/core/quic_packet_writer.h
@@ -34,6 +34,8 @@ QuicTime::Delta release_time_delay = QuicTime::Delta::Zero(); // Whether it is allowed to send this packet without |release_time_delay|. bool allow_burst = false; + // ECN codepoint to use when sending this packet. + QuicEcnCodepoint ecn_codepoint; }; // An interface between writers and the entity managing the
diff --git a/quiche/quic/core/quic_packets.cc b/quiche/quic/core/quic_packets.cc index b0f7535..d884adf 100644 --- a/quiche/quic/core/quic_packets.cc +++ b/quiche/quic/core/quic_packets.cc
@@ -344,7 +344,7 @@ : quic::QuicReceivedPacket(buffer, length, receipt_time, owns_buffer, ttl, ttl_valid, nullptr /* packet_headers */, 0 /* headers_length */, - false /* owns_header_buffer */) {} + false /* owns_header_buffer */, ECN_NOT_ECT) {} QuicReceivedPacket::QuicReceivedPacket(const char* buffer, size_t length, QuicTime receipt_time, bool owns_buffer, @@ -352,12 +352,21 @@ char* packet_headers, size_t headers_length, bool owns_header_buffer) + : quic::QuicReceivedPacket(buffer, length, receipt_time, owns_buffer, ttl, + ttl_valid, packet_headers, headers_length, + owns_header_buffer, ECN_NOT_ECT) {} + +QuicReceivedPacket::QuicReceivedPacket( + const char* buffer, size_t length, QuicTime receipt_time, bool owns_buffer, + int ttl, bool ttl_valid, char* packet_headers, size_t headers_length, + bool owns_header_buffer, QuicEcnCodepoint ecn_codepoint) : QuicEncryptedPacket(buffer, length, owns_buffer), receipt_time_(receipt_time), ttl_(ttl_valid ? ttl : -1), packet_headers_(packet_headers), headers_length_(headers_length), - owns_header_buffer_(owns_header_buffer) {} + owns_header_buffer_(owns_header_buffer), + ecn_codepoint_(ecn_codepoint) {} QuicReceivedPacket::~QuicReceivedPacket() { if (owns_header_buffer_) {
diff --git a/quiche/quic/core/quic_packets.h b/quiche/quic/core/quic_packets.h index 263bdb9..a1c743a 100644 --- a/quiche/quic/core/quic_packets.h +++ b/quiche/quic/core/quic_packets.h
@@ -291,6 +291,10 @@ bool owns_buffer, int ttl, bool ttl_valid, char* packet_headers, size_t headers_length, bool owns_header_buffer); + QuicReceivedPacket(const char* buffer, size_t length, QuicTime receipt_time, + bool owns_buffer, int ttl, bool ttl_valid, + char* packet_headers, size_t headers_length, + bool owns_header_buffer, QuicEcnCodepoint ecn_codepoint); ~QuicReceivedPacket(); QuicReceivedPacket(const QuicReceivedPacket&) = delete; QuicReceivedPacket& operator=(const QuicReceivedPacket&) = delete; @@ -317,6 +321,8 @@ QUIC_EXPORT_PRIVATE friend std::ostream& operator<<( std::ostream& os, const QuicReceivedPacket& s); + QuicEcnCodepoint ecn_codepoint() const { return ecn_codepoint_; } + private: const QuicTime receipt_time_; int ttl_; @@ -326,6 +332,7 @@ int headers_length_; // Whether owns the buffer for packet headers. bool owns_header_buffer_; + QuicEcnCodepoint ecn_codepoint_; }; // SerializedPacket contains information of a serialized(encrypted) packet.
diff --git a/quiche/quic/core/quic_received_packet_manager.cc b/quiche/quic/core/quic_received_packet_manager.cc index bb7b87d..0486618 100644 --- a/quiche/quic/core/quic_received_packet_manager.cc +++ b/quiche/quic/core/quic_received_packet_manager.cc
@@ -10,7 +10,9 @@ #include "quiche/quic/core/congestion_control/rtt_stats.h" #include "quiche/quic/core/crypto/crypto_protocol.h" +#include "quiche/quic/core/quic_config.h" #include "quiche/quic/core/quic_connection_stats.h" +#include "quiche/quic/core/quic_types.h" #include "quiche/quic/platform/api/quic_bug_tracker.h" #include "quiche/quic/platform/api/quic_flags.h" #include "quiche/quic/platform/api/quic_logging.h" @@ -70,7 +72,8 @@ } void QuicReceivedPacketManager::RecordPacketReceived( - const QuicPacketHeader& header, QuicTime receipt_time) { + const QuicPacketHeader& header, QuicTime receipt_time, + const QuicEcnCodepoint ecn) { const QuicPacketNumber packet_number = header.packet_number; QUICHE_DCHECK(IsAwaitingPacket(packet_number)) << " packet_number:" << packet_number; @@ -119,6 +122,27 @@ } } + if (GetQuicRestartFlag(quic_receive_ecn) && ecn != ECN_NOT_ECT) { + QUIC_RESTART_FLAG_COUNT_N(quic_receive_ecn, 1, 3); + if (!ack_frame_.ecn_counters.has_value()) { + ack_frame_.ecn_counters = QuicEcnCounts(); + } + switch (ecn) { + case ECN_NOT_ECT: + QUICHE_NOTREACHED(); + break; // It's impossible to get here, but the compiler complains. + case ECN_ECT0: + ack_frame_.ecn_counters->ect0++; + break; + case ECN_ECT1: + ack_frame_.ecn_counters->ect1++; + break; + case ECN_CE: + ack_frame_.ecn_counters->ce++; + break; + } + } + if (least_received_packet_number_.IsInitialized()) { least_received_packet_number_ = std::min(least_received_packet_number_, packet_number);
diff --git a/quiche/quic/core/quic_received_packet_manager.h b/quiche/quic/core/quic_received_packet_manager.h index d13e09b..ab298d4 100644 --- a/quiche/quic/core/quic_received_packet_manager.h +++ b/quiche/quic/core/quic_received_packet_manager.h
@@ -11,6 +11,7 @@ #include "quiche/quic/core/quic_config.h" #include "quiche/quic/core/quic_framer.h" #include "quiche/quic/core/quic_packets.h" +#include "quiche/quic/core/quic_types.h" #include "quiche/quic/platform/api/quic_export.h" namespace quic { @@ -41,7 +42,8 @@ // header: the packet header. // timestamp: the arrival time of the packet. virtual void RecordPacketReceived(const QuicPacketHeader& header, - QuicTime receipt_time); + QuicTime receipt_time, + QuicEcnCodepoint ecn); // Checks whether |packet_number| is missing and less than largest observed. virtual bool IsMissing(QuicPacketNumber packet_number);
diff --git a/quiche/quic/core/quic_received_packet_manager_test.cc b/quiche/quic/core/quic_received_packet_manager_test.cc index c453707..143654e 100644 --- a/quiche/quic/core/quic_received_packet_manager_test.cc +++ b/quiche/quic/core/quic_received_packet_manager_test.cc
@@ -13,6 +13,7 @@ #include "quiche/quic/core/crypto/crypto_protocol.h" #include "quiche/quic/core/quic_connection_stats.h" #include "quiche/quic/core/quic_constants.h" +#include "quiche/quic/core/quic_types.h" #include "quiche/quic/platform/api/quic_expect_bug.h" #include "quiche/quic/platform/api/quic_flags.h" #include "quiche/quic/platform/api/quic_test.h" @@ -54,9 +55,14 @@ } void RecordPacketReceipt(uint64_t packet_number, QuicTime receipt_time) { + RecordPacketReceipt(packet_number, receipt_time, ECN_NOT_ECT); + } + + void RecordPacketReceipt(uint64_t packet_number, QuicTime receipt_time, + QuicEcnCodepoint ecn_codepoint) { QuicPacketHeader header; header.packet_number = QuicPacketNumber(packet_number); - received_manager_.RecordPacketReceived(header, receipt_time); + received_manager_.RecordPacketReceived(header, receipt_time, ecn_codepoint); } bool HasPendingAck() { @@ -91,9 +97,9 @@ TEST_F(QuicReceivedPacketManagerTest, DontWaitForPacketsBefore) { QuicPacketHeader header; header.packet_number = QuicPacketNumber(2u); - received_manager_.RecordPacketReceived(header, QuicTime::Zero()); + received_manager_.RecordPacketReceived(header, QuicTime::Zero(), ECN_NOT_ECT); header.packet_number = QuicPacketNumber(7u); - received_manager_.RecordPacketReceived(header, QuicTime::Zero()); + received_manager_.RecordPacketReceived(header, QuicTime::Zero(), ECN_NOT_ECT); EXPECT_TRUE(received_manager_.IsAwaitingPacket(QuicPacketNumber(3u))); EXPECT_TRUE(received_manager_.IsAwaitingPacket(QuicPacketNumber(6u))); received_manager_.DontWaitForPacketsBefore(QuicPacketNumber(4)); @@ -106,7 +112,7 @@ header.packet_number = QuicPacketNumber(2u); QuicTime two_ms = QuicTime::Zero() + QuicTime::Delta::FromMilliseconds(2); EXPECT_FALSE(received_manager_.ack_frame_updated()); - received_manager_.RecordPacketReceived(header, two_ms); + received_manager_.RecordPacketReceived(header, two_ms, ECN_NOT_ECT); EXPECT_TRUE(received_manager_.ack_frame_updated()); QuicFrame ack = received_manager_.GetUpdatedAckFrame(QuicTime::Zero()); @@ -129,11 +135,11 @@ EXPECT_EQ(1u, ack.ack_frame->received_packet_times.size()); header.packet_number = QuicPacketNumber(999u); - received_manager_.RecordPacketReceived(header, two_ms); + received_manager_.RecordPacketReceived(header, two_ms, ECN_NOT_ECT); header.packet_number = QuicPacketNumber(4u); - received_manager_.RecordPacketReceived(header, two_ms); + received_manager_.RecordPacketReceived(header, two_ms, ECN_NOT_ECT); header.packet_number = QuicPacketNumber(1000u); - received_manager_.RecordPacketReceived(header, two_ms); + received_manager_.RecordPacketReceived(header, two_ms, ECN_NOT_ECT); EXPECT_TRUE(received_manager_.ack_frame_updated()); ack = received_manager_.GetUpdatedAckFrame(two_ms); received_manager_.ResetAckStates(); @@ -676,6 +682,23 @@ CheckAckTimeout(clock_.ApproximateNow()); } +TEST_F(QuicReceivedPacketManagerTest, CountEcnPackets) { + EXPECT_FALSE(HasPendingAck()); + RecordPacketReceipt(3, QuicTime::Zero(), ECN_NOT_ECT); + RecordPacketReceipt(4, QuicTime::Zero(), ECN_ECT0); + RecordPacketReceipt(5, QuicTime::Zero(), ECN_ECT1); + RecordPacketReceipt(6, QuicTime::Zero(), ECN_CE); + QuicFrame ack = received_manager_.GetUpdatedAckFrame(QuicTime::Zero()); + if (GetQuicRestartFlag(quic_receive_ecn)) { + EXPECT_TRUE(ack.ack_frame->ecn_counters.has_value()); + EXPECT_EQ(ack.ack_frame->ecn_counters->ect0, 1); + EXPECT_EQ(ack.ack_frame->ecn_counters->ect1, 1); + EXPECT_EQ(ack.ack_frame->ecn_counters->ce, 1); + } else { + EXPECT_FALSE(ack.ack_frame->ecn_counters.has_value()); + } +} + } // namespace } // namespace test } // namespace quic
diff --git a/quiche/quic/core/quic_types.h b/quiche/quic/core/quic_types.h index f4a2394..fac4f38 100644 --- a/quiche/quic/core/quic_types.h +++ b/quiche/quic/core/quic_types.h
@@ -878,6 +878,25 @@ ECN_CE = 3, }; +// This struct reports the Explicit Congestion Notification (ECN) contents of +// the ACK_ECN frame. They are the cumulative number of QUIC packets received +// for that codepoint in a given Packet Number Space. +struct QUIC_EXPORT_PRIVATE QuicEcnCounts { + QuicEcnCounts() = default; + QuicEcnCounts(QuicPacketCount ect0, QuicPacketCount ect1, QuicPacketCount ce) + : ect0(ect0), ect1(ect1), ce(ce) {} + + std::string ToString() const { + return absl::StrFormat("ECT(0): %s, ECT(1): %s, CE: %s", + std::to_string(ect0), std::to_string(ect1), + std::to_string(ce)); + } + + QuicPacketCount ect0 = 0; + QuicPacketCount ect1 = 0; + QuicPacketCount ce = 0; +}; + } // namespace quic #endif // QUICHE_QUIC_CORE_QUIC_TYPES_H_
diff --git a/quiche/quic/core/tls_chlo_extractor.h b/quiche/quic/core/tls_chlo_extractor.h index b48065f..4173953 100644 --- a/quiche/quic/core/tls_chlo_extractor.h +++ b/quiche/quic/core/tls_chlo_extractor.h
@@ -112,6 +112,7 @@ QuicTime /*timestamp*/) override { return true; } + void OnAckEcnCounts(const QuicEcnCounts& /*ecn_counts*/) override {} bool OnAckFrameEnd(QuicPacketNumber /*start*/) override { return true; } bool OnStopWaitingFrame(const QuicStopWaitingFrame& /*frame*/) override { return true;
diff --git a/quiche/quic/core/uber_received_packet_manager.cc b/quiche/quic/core/uber_received_packet_manager.cc index 2456795..4efe788 100644 --- a/quiche/quic/core/uber_received_packet_manager.cc +++ b/quiche/quic/core/uber_received_packet_manager.cc
@@ -48,14 +48,15 @@ void UberReceivedPacketManager::RecordPacketReceived( EncryptionLevel decrypted_packet_level, const QuicPacketHeader& header, - QuicTime receipt_time) { + QuicTime receipt_time, QuicEcnCodepoint ecn_codepoint) { if (!supports_multiple_packet_number_spaces_) { - received_packet_managers_[0].RecordPacketReceived(header, receipt_time); + received_packet_managers_[0].RecordPacketReceived(header, receipt_time, + ecn_codepoint); return; } received_packet_managers_[QuicUtils::GetPacketNumberSpace( decrypted_packet_level)] - .RecordPacketReceived(header, receipt_time); + .RecordPacketReceived(header, receipt_time, ecn_codepoint); } void UberReceivedPacketManager::DontWaitForPacketsBefore(
diff --git a/quiche/quic/core/uber_received_packet_manager.h b/quiche/quic/core/uber_received_packet_manager.h index 1eb15d9..0e436c0 100644 --- a/quiche/quic/core/uber_received_packet_manager.h +++ b/quiche/quic/core/uber_received_packet_manager.h
@@ -31,7 +31,8 @@ // been parsed. void RecordPacketReceived(EncryptionLevel decrypted_packet_level, const QuicPacketHeader& header, - QuicTime receipt_time); + QuicTime receipt_time, + QuicEcnCodepoint ecn_codepoint); // Retrieves a frame containing a QuicAckFrame. The ack frame must be // serialized before another packet is received, or it will change.
diff --git a/quiche/quic/core/uber_received_packet_manager_test.cc b/quiche/quic/core/uber_received_packet_manager_test.cc index ad62d8f..97b2a80 100644 --- a/quiche/quic/core/uber_received_packet_manager_test.cc +++ b/quiche/quic/core/uber_received_packet_manager_test.cc
@@ -74,8 +74,8 @@ uint64_t packet_number, QuicTime receipt_time) { QuicPacketHeader header; header.packet_number = QuicPacketNumber(packet_number); - manager_->RecordPacketReceived(decrypted_packet_level, header, - receipt_time); + manager_->RecordPacketReceived(decrypted_packet_level, header, receipt_time, + ECN_NOT_ECT); } bool HasPendingAck() {
diff --git a/quiche/quic/test_tools/quic_connection_peer.cc b/quiche/quic/test_tools/quic_connection_peer.cc index b33bc29..ad58d9c 100644 --- a/quiche/quic/test_tools/quic_connection_peer.cc +++ b/quiche/quic/test_tools/quic_connection_peer.cc
@@ -576,5 +576,11 @@ return connection->server_preferred_address_; } +// static +QuicEcnCounts* QuicConnectionPeer::GetEcnCounts( + QuicConnection* connection, PacketNumberSpace packet_number_space) { + return &connection->peer_ack_ecn_counts_[packet_number_space]; +} + } // namespace test } // namespace quic
diff --git a/quiche/quic/test_tools/quic_connection_peer.h b/quiche/quic/test_tools/quic_connection_peer.h index 794e991..fbdedb3 100644 --- a/quiche/quic/test_tools/quic_connection_peer.h +++ b/quiche/quic/test_tools/quic_connection_peer.h
@@ -12,6 +12,7 @@ #include "quiche/quic/core/quic_connection_id.h" #include "quiche/quic/core/quic_connection_stats.h" #include "quiche/quic/core/quic_packets.h" +#include "quiche/quic/core/quic_path_validator.h" #include "quiche/quic/core/quic_types.h" #include "quiche/quic/platform/api/quic_socket_address.h" @@ -235,6 +236,9 @@ static QuicSocketAddress GetServerPreferredAddress( QuicConnection* connection); + + static QuicEcnCounts* GetEcnCounts(QuicConnection* connection, + PacketNumberSpace packet_number_space); }; } // namespace test
diff --git a/quiche/quic/test_tools/quic_test_utils.cc b/quiche/quic/test_tools/quic_test_utils.cc index 1a63557..bc1cf2f 100644 --- a/quiche/quic/test_tools/quic_test_utils.cc +++ b/quiche/quic/test_tools/quic_test_utils.cc
@@ -348,6 +348,8 @@ return true; } +void NoOpFramerVisitor::OnAckEcnCounts(const QuicEcnCounts& /*ecn_counts*/) {} + bool NoOpFramerVisitor::OnAckFrameEnd(QuicPacketNumber /*start*/) { return true; }
diff --git a/quiche/quic/test_tools/quic_test_utils.h b/quiche/quic/test_tools/quic_test_utils.h index d8271e1..8609d5f 100644 --- a/quiche/quic/test_tools/quic_test_utils.h +++ b/quiche/quic/test_tools/quic_test_utils.h
@@ -310,6 +310,7 @@ MOCK_METHOD(bool, OnAckRange, (QuicPacketNumber, QuicPacketNumber), (override)); MOCK_METHOD(bool, OnAckTimestamp, (QuicPacketNumber, QuicTime), (override)); + MOCK_METHOD(void, OnAckEcnCounts, (const QuicEcnCounts&), (override)); MOCK_METHOD(bool, OnAckFrameEnd, (QuicPacketNumber), (override)); MOCK_METHOD(bool, OnStopWaitingFrame, (const QuicStopWaitingFrame& frame), (override)); @@ -393,6 +394,7 @@ bool OnAckRange(QuicPacketNumber start, QuicPacketNumber end) override; bool OnAckTimestamp(QuicPacketNumber packet_number, QuicTime timestamp) override; + void OnAckEcnCounts(const QuicEcnCounts& ecn_counts) override; bool OnAckFrameEnd(QuicPacketNumber start) override; bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override; bool OnPaddingFrame(const QuicPaddingFrame& frame) override; @@ -1382,7 +1384,8 @@ ~MockReceivedPacketManager() override; MOCK_METHOD(void, RecordPacketReceived, - (const QuicPacketHeader& header, QuicTime receipt_time), + (const QuicPacketHeader& header, QuicTime receipt_time, + const QuicEcnCodepoint ecn), (override)); MOCK_METHOD(bool, IsMissing, (QuicPacketNumber packet_number), (override)); MOCK_METHOD(bool, IsAwaitingPacket, (QuicPacketNumber packet_number), @@ -2145,6 +2148,13 @@ return result; } +struct TestPerPacketOptions : PerPacketOptions { + public: + std::unique_ptr<quic::PerPacketOptions> Clone() const override { + return std::make_unique<TestPerPacketOptions>(*this); + } +}; + } // namespace test } // namespace quic
diff --git a/quiche/quic/test_tools/simple_quic_framer.cc b/quiche/quic/test_tools/simple_quic_framer.cc index 793afc4..da9f232 100644 --- a/quiche/quic/test_tools/simple_quic_framer.cc +++ b/quiche/quic/test_tools/simple_quic_framer.cc
@@ -112,6 +112,8 @@ return true; } + void OnAckEcnCounts(const QuicEcnCounts& /*ecn_counts*/) override {} + bool OnAckFrameEnd(QuicPacketNumber /*start*/) override { return true; } bool OnStopWaitingFrame(const QuicStopWaitingFrame& frame) override {
diff --git a/quiche/quic/tools/quic_packet_printer_bin.cc b/quiche/quic/tools/quic_packet_printer_bin.cc index 740971c..3e8b07b 100644 --- a/quiche/quic/tools/quic_packet_printer_bin.cc +++ b/quiche/quic/tools/quic_packet_printer_bin.cc
@@ -128,6 +128,9 @@ << timestamp.ToDebuggingValue() << ")"; return true; } + void OnAckEcnCounts(const QuicEcnCounts& ecn_counts) override { + std::cerr << "OnAckEcnCounts: " << ecn_counts.ToString(); + } bool OnAckFrameEnd(QuicPacketNumber start) override { std::cerr << "OnAckFrameEnd, start: " << start; return true;