Stop blackhole detection when a duplicate ACK causes packets to be detected lost. Protected by existing gfe2_reloadable_flag_quic_default_enable_5rto_blackhole_detection2. Do not close connection when blackhole is detected but there is no in flight packets. PiperOrigin-RevId: 326693232 Change-Id: Id75b01e399fad12dcfc12dabd7245a2f059663de
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc index bb0db5c..b7c9bb4 100644 --- a/quic/core/quic_connection.cc +++ b/quic/core/quic_connection.cc
@@ -4206,6 +4206,13 @@ SetRetransmissionAlarm(); if (acked_new_packet) { OnForwardProgressMade(); + } else if (default_enable_5rto_blackhole_detection_ && + !sent_packet_manager_.HasInFlightPackets() && + blackhole_detector_.IsDetectionInProgress()) { + // In case no new packets get acknowledged, it is possible packets are + // detected lost because of time based loss detection. Cancel blackhole + // detection if there is no packets in flight. + blackhole_detector_.StopDetection(); } if (send_stop_waiting) { @@ -4707,16 +4714,15 @@ } void QuicConnection::OnBlackholeDetected() { - QUIC_BUG_IF(default_enable_5rto_blackhole_detection_ && - !sent_packet_manager_.HasInFlightPackets()) - << ENDPOINT - << "Closing connection because of blackhole, but there is no bytes in " - "flight"; - const std::string error_detail = quiche::QuicheStrCat( - "Network blackhole detected", sent_packet_manager_.HasInFlightPackets() - ? "." - : " with no packets in flight."); - CloseConnection(QUIC_TOO_MANY_RTOS, error_detail, + if (default_enable_5rto_blackhole_detection_ && + !sent_packet_manager_.HasInFlightPackets()) { + QUIC_BUG << ENDPOINT + << "Blackhole detected, but there is no bytes in flight, version: " + << version(); + // Do not close connection if there is no bytes in flight. + return; + } + CloseConnection(QUIC_TOO_MANY_RTOS, "Network blackhole detected", ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); }
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc index 5340233..d80ca65 100644 --- a/quic/core/quic_connection_test.cc +++ b/quic/core/quic_connection_test.cc
@@ -11698,6 +11698,70 @@ connection_.GetPingAlarm()->Fire(); } +// Regression test for b/159698337 +TEST_P(QuicConnectionTest, DuplicateAckCausesLostPackets) { + if (!GetQuicReloadableFlag(quic_default_enable_5rto_blackhole_detection2)) { + return; + } + // Finish handshake. + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + peer_creator_.set_encryption_level(ENCRYPTION_FORWARD_SECURE); + notifier_.NeuterUnencryptedData(); + connection_.NeuterUnencryptedPackets(); + connection_.OnHandshakeComplete(); + EXPECT_CALL(visitor_, GetHandshakeState()) + .WillRepeatedly(Return(HANDSHAKE_COMPLETE)); + + std::string data(1200, 'a'); + // Send data packets 1 - 5. + for (size_t i = 0; i < 5; ++i) { + SendStreamDataToPeer( + GetNthClientInitiatedStreamId(1, connection_.transport_version()), data, + i * 1200, i == 4 ? FIN : NO_FIN, nullptr); + } + ASSERT_TRUE(connection_.BlackholeDetectionInProgress()); + + EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)).Times(3); + + // ACK packet 5 and 1 and 2 are detected lost. + QuicAckFrame frame = + InitAckFrame({{QuicPacketNumber(5), QuicPacketNumber(6)}}); + LostPacketVector lost_packets; + lost_packets.push_back( + LostPacket(QuicPacketNumber(1), kMaxOutgoingPacketSize)); + lost_packets.push_back( + LostPacket(QuicPacketNumber(2), kMaxOutgoingPacketSize)); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) + .Times(AnyNumber()) + .WillOnce(DoAll(SetArgPointee<5>(lost_packets), + Return(LossDetectionInterface::DetectionStats()))); + ProcessAckPacket(1, &frame); + EXPECT_TRUE(connection_.BlackholeDetectionInProgress()); + QuicAlarm* retransmission_alarm = connection_.GetRetransmissionAlarm(); + EXPECT_TRUE(retransmission_alarm->IsSet()); + + // ACK packet 1 - 5 and 7. + QuicAckFrame frame2 = + InitAckFrame({{QuicPacketNumber(1), QuicPacketNumber(6)}, + {QuicPacketNumber(7), QuicPacketNumber(8)}}); + ProcessAckPacket(2, &frame2); + EXPECT_TRUE(connection_.BlackholeDetectionInProgress()); + + // ACK packet 7 again and assume packet 6 is detected lost. + QuicAckFrame frame3 = + InitAckFrame({{QuicPacketNumber(7), QuicPacketNumber(8)}}); + lost_packets.clear(); + lost_packets.push_back( + LostPacket(QuicPacketNumber(6), kMaxOutgoingPacketSize)); + EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) + .Times(AnyNumber()) + .WillOnce(DoAll(SetArgPointee<5>(lost_packets), + Return(LossDetectionInterface::DetectionStats()))); + ProcessAckPacket(3, &frame3); + // Make sure loss detection is cancelled even there is no new acked packets. + EXPECT_FALSE(connection_.BlackholeDetectionInProgress()); +} + } // namespace } // namespace test } // namespace quic