Internal change
PiperOrigin-RevId: 515182736
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index 9d4f882..dbede25 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -1324,6 +1324,20 @@
last_received_packet_info_.header.packet_number;
}
+ switch (last_received_packet_info_.ecn_codepoint) {
+ case ECN_NOT_ECT:
+ break;
+ case ECN_ECT0:
+ stats_.num_ecn_marks_received.ect0++;
+ break;
+ case ECN_ECT1:
+ stats_.num_ecn_marks_received.ect1++;
+ break;
+ case ECN_CE:
+ stats_.num_ecn_marks_received.ce++;
+ break;
+ }
+
// Record packet receipt to populate ack info before processing stream
// frames, since the processing may result in sending a bundled ack.
QuicTime receipt_time = idle_network_detector_.time_of_last_received_packet();
@@ -3630,6 +3644,9 @@
stats_.bytes_sent += encrypted_length;
++stats_.packets_sent;
+ if (packet->has_ack_ecn) {
+ stats_.num_ack_frames_sent_with_ecn++;
+ }
QuicByteCount bytes_not_retransmitted =
packet->bytes_not_retransmitted.value_or(0);
diff --git a/quiche/quic/core/quic_connection_stats.h b/quiche/quic/core/quic_connection_stats.h
index 82b0255..336435e 100644
--- a/quiche/quic/core/quic_connection_stats.h
+++ b/quiche/quic/core/quic_connection_stats.h
@@ -188,6 +188,14 @@
QuicPacketCount
num_tls_server_zero_rtt_packets_received_after_discarding_decrypter = 0;
+ // Counts the number of packets received with each Explicit Congestion
+ // Notification (ECN) codepoint, except Not-ECT. There is one counter across
+ // all packet number spaces.
+ QuicEcnCounts num_ecn_marks_received;
+
+ // Counts the number of ACK frames sent with ECN counts.
+ QuicPacketCount num_ack_frames_sent_with_ecn = 0;
+
// True if address is validated via decrypting HANDSHAKE or 1-RTT packet.
bool address_validated_via_decrypting_packet = false;
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index 84c8cdb..a235314 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -16877,12 +16877,26 @@
connection_.SupportsMultiplePacketNumberSpaces()
? connection_.received_packet_manager().GetAckFrame(APPLICATION_DATA)
: connection_.received_packet_manager().ack_frame();
+ // Send two PINGs so that the ACK goes too. The second packet should not
+ // include an ACK, which checks that the packet state is cleared properly.
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ if (connection_.version().HasIetfQuicFrames()) {
+ QuicConnectionPeer::SendPing(&connection_);
+ QuicConnectionPeer::SendPing(&connection_);
+ }
+ QuicConnectionStats stats = connection_.GetStats();
if (GetQuicRestartFlag(quic_receive_ecn)) {
ASSERT_TRUE(ack_frame.ecn_counters.has_value());
EXPECT_EQ(ack_frame.ecn_counters->ect0, 1);
+ EXPECT_EQ(stats.num_ack_frames_sent_with_ecn,
+ connection_.version().HasIetfQuicFrames() ? 1 : 0);
} else {
EXPECT_FALSE(ack_frame.ecn_counters.has_value());
+ EXPECT_EQ(stats.num_ack_frames_sent_with_ecn, 0);
}
+ EXPECT_EQ(stats.num_ecn_marks_received.ect0, 1);
+ EXPECT_EQ(stats.num_ecn_marks_received.ect1, 0);
+ EXPECT_EQ(stats.num_ecn_marks_received.ce, 0);
}
TEST_P(QuicConnectionTest, EcnMarksCoalescedPacket) {
@@ -16914,6 +16928,16 @@
std::make_unique<TaggingEncrypter>(ENCRYPTION_HANDSHAKE));
EXPECT_CALL(visitor_, OnCryptoFrame(_)).Times(2);
ProcessCoalescedPacket(packets, ECN_ECT0);
+ // Send two PINGs so that the ACKs go too.
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ if (connection_.version().HasIetfQuicFrames()) {
+ EXPECT_CALL(visitor_, OnHandshakePacketSent()).Times(1);
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE);
+ QuicConnectionPeer::SendPing(&connection_);
+ connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ QuicConnectionPeer::SendPing(&connection_);
+ }
+ QuicConnectionStats stats = connection_.GetStats();
ack_frame =
connection_.SupportsMultiplePacketNumberSpaces()
? connection_.received_packet_manager().GetAckFrame(HANDSHAKE_DATA)
@@ -16929,6 +16953,16 @@
EXPECT_TRUE(ack_frame.ecn_counters.has_value());
EXPECT_EQ(ack_frame.ecn_counters->ect0, 1);
}
+ if (GetQuicRestartFlag(quic_receive_ecn)) {
+ EXPECT_EQ(stats.num_ecn_marks_received.ect0, 2);
+ EXPECT_EQ(stats.num_ack_frames_sent_with_ecn,
+ connection_.version().HasIetfQuicFrames() ? 2 : 0);
+ } else {
+ EXPECT_EQ(stats.num_ecn_marks_received.ect0, 0);
+ EXPECT_EQ(stats.num_ack_frames_sent_with_ecn, 0);
+ }
+ EXPECT_EQ(stats.num_ecn_marks_received.ect1, 0);
+ EXPECT_EQ(stats.num_ecn_marks_received.ce, 0);
}
TEST_P(QuicConnectionTest, EcnMarksUndecryptableCoalescedPacket) {
@@ -17047,6 +17081,12 @@
// Should be recorded as ECT(0), not CE.
EXPECT_EQ(ack_frame.ecn_counters->ect0,
connection_.SupportsMultiplePacketNumberSpaces() ? 1 : 2);
+ QuicConnectionStats stats = connection_.GetStats();
+ EXPECT_EQ(stats.num_ecn_marks_received.ect0,
+ GetQuicRestartFlag(quic_receive_ecn) ? 2 : 0);
+ EXPECT_EQ(stats.num_ecn_marks_received.ect1, 0);
+ EXPECT_EQ(stats.num_ecn_marks_received.ce,
+ GetQuicRestartFlag(quic_receive_ecn) ? 1 : 0);
}
TEST_P(QuicConnectionTest, ReceivedPacketInfoDefaults) {
diff --git a/quiche/quic/core/quic_packet_creator.cc b/quiche/quic/core/quic_packet_creator.cc
index 2a0974f..5599f8f 100644
--- a/quiche/quic/core/quic_packet_creator.cc
+++ b/quiche/quic/core/quic_packet_creator.cc
@@ -492,6 +492,7 @@
void QuicPacketCreator::ClearPacket() {
packet_.has_ack = false;
packet_.has_stop_waiting = false;
+ packet_.has_ack_ecn = false;
packet_.has_crypto_handshake = NOT_HANDSHAKE;
packet_.transmission_type = NOT_RETRANSMISSION;
packet_.encrypted_buffer = nullptr;
@@ -1829,6 +1830,9 @@
if (frame.type == ACK_FRAME) {
packet_.has_ack = true;
packet_.largest_acked = LargestAcked(*frame.ack_frame);
+ if (frame.ack_frame->ecn_counters.has_value()) {
+ packet_.has_ack_ecn = true;
+ }
} else if (frame.type == STOP_WAITING_FRAME) {
packet_.has_stop_waiting = true;
} else if (frame.type == ACK_FREQUENCY_FRAME) {
diff --git a/quiche/quic/core/quic_packets.cc b/quiche/quic/core/quic_packets.cc
index 746737b..39e7eaf 100644
--- a/quiche/quic/core/quic_packets.cc
+++ b/quiche/quic/core/quic_packets.cc
@@ -441,6 +441,7 @@
encryption_level(other.encryption_level),
has_ack(other.has_ack),
has_stop_waiting(other.has_stop_waiting),
+ has_ack_ecn(other.has_ack_ecn),
transmission_type(other.transmission_type),
largest_acked(other.largest_acked),
has_ack_frame_copy(other.has_ack_frame_copy),
@@ -498,6 +499,7 @@
copy->peer_address = serialized.peer_address;
copy->bytes_not_retransmitted = serialized.bytes_not_retransmitted;
copy->initial_header = serialized.initial_header;
+ copy->has_ack_ecn = serialized.has_ack_ecn;
if (copy_buffer) {
copy->encrypted_buffer = CopyBuffer(serialized);
diff --git a/quiche/quic/core/quic_packets.h b/quiche/quic/core/quic_packets.h
index c26a88a..d1eb52b 100644
--- a/quiche/quic/core/quic_packets.h
+++ b/quiche/quic/core/quic_packets.h
@@ -376,6 +376,7 @@
// TODO(fayang): Remove has_ack and has_stop_waiting.
bool has_ack;
bool has_stop_waiting;
+ bool has_ack_ecn = false; // ack frame contains ECN counts.
TransmissionType transmission_type;
// The largest acked of the AckFrame in this packet if has_ack is true,
// 0 otherwise.