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