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;