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/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