gfe-relnote: In QUIC, use QuicNetworkBlackholeDetector which detects both path degrading and network blackhole. Protected by gfe2_reloadable_flag_quic_use_blackhole_detector. Path degrading is only armed after handshake completes. And blackhole detection now is based on time rather than event driven (i.e., 5RTO, 6PTO, etc) PiperOrigin-RevId: 302526709 Change-Id: I43e776e18979d4f8ce4f26708107b4ef9356aa1a
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc index 5f2b031..dcb8ef8 100644 --- a/quic/core/quic_connection.cc +++ b/quic/core/quic_connection.cc
@@ -332,7 +332,8 @@ max_consecutive_ptos_(0), bytes_received_before_address_validation_(0), bytes_sent_before_address_validation_(0), - address_validated_(false) { + address_validated_(false), + blackhole_detector_(this, &arena_, alarm_factory_) { QUIC_DLOG(INFO) << ENDPOINT << "Created connection with server connection ID " << server_connection_id << " and version: " << ParsedQuicVersionToString(version()); @@ -2335,8 +2336,23 @@ debug_visitor_->OnPacketSent(*packet, packet->transmission_type, packet_send_time); } - if (IsRetransmittable(*packet) == HAS_RETRANSMITTABLE_DATA) { - if (!is_path_degrading_ && !path_degrading_alarm_->IsSet()) { + if (IsRetransmittable(*packet) == HAS_RETRANSMITTABLE_DATA && + !is_termination_packet) { + // Start blackhole/path degrading detections if the sent packet is not + // termination packet and contains retransmittable data. + if (use_blackhole_detector_) { + // Do not restart detection if detection is in progress indicating no + // forward progress has been made since last event (i.e., packet was sent + // or new packets were acknowledged). + if (!blackhole_detector_.IsDetectionInProgress()) { + // Try to start detections if no detection in progress. This could + // because either both detections are inactive when sending last packet + // or this connection just gets out of quiescence. + QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_blackhole_detector, 1, 4); + blackhole_detector_.RestartDetection(GetPathDegradingDeadline(), + GetNetworkBlackholeDeadline()); + } + } else if (!is_path_degrading_ && !path_degrading_alarm_->IsSet()) { // This is the first retransmittable packet on the working path. // Start the path degrading alarm to detect new path degrading. SetPathDegradingAlarm(); @@ -2640,20 +2656,26 @@ QuicPacketNumber previous_created_packet_number = packet_creator_.packet_number(); - if (close_connection_after_five_rtos_ && - sent_packet_manager_.GetConsecutiveRtoCount() >= 4) { - // Close on the 5th consecutive RTO, so after 4 previous RTOs have occurred. - CloseConnection(QUIC_TOO_MANY_RTOS, "5 consecutive retransmission timeouts", - ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); - return; - } - if (sent_packet_manager_.pto_enabled() && max_consecutive_ptos_ > 0 && - sent_packet_manager_.GetConsecutivePtoCount() >= max_consecutive_ptos_) { - CloseConnection(QUIC_TOO_MANY_RTOS, - quiche::QuicheStrCat(max_consecutive_ptos_ + 1, - "consecutive retransmission timeouts"), - ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); - return; + if (!use_blackhole_detector_) { + if (close_connection_after_five_rtos_ && + sent_packet_manager_.GetConsecutiveRtoCount() >= 4) { + // Close on the 5th consecutive RTO, so after 4 previous RTOs have + // occurred. + CloseConnection(QUIC_TOO_MANY_RTOS, + "5 consecutive retransmission timeouts", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } + if (sent_packet_manager_.pto_enabled() && max_consecutive_ptos_ > 0 && + sent_packet_manager_.GetConsecutivePtoCount() >= + max_consecutive_ptos_) { + CloseConnection( + QUIC_TOO_MANY_RTOS, + quiche::QuicheStrCat(max_consecutive_ptos_ + 1, + "consecutive retransmission timeouts"), + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); + return; + } } const auto retransmission_mode = @@ -3032,6 +3054,10 @@ mtu_discovery_alarm_->Cancel(); path_degrading_alarm_->Cancel(); process_undecryptable_packets_alarm_->Cancel(); + if (use_blackhole_detector_) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_blackhole_detector, 4, 4); + blackhole_detector_.StopDetection(); + } } QuicByteCount QuicConnection::max_packet_length() const { @@ -3222,6 +3248,7 @@ } void QuicConnection::SetPathDegradingAlarm() { + DCHECK(!use_blackhole_detector_); if (perspective_ == Perspective::IS_SERVER) { return; } @@ -3748,7 +3775,23 @@ // Always reset the retransmission alarm when an ack comes in, since we now // have a better estimate of the current rtt than when it was set. SetRetransmissionAlarm(); - MaybeSetPathDegradingAlarm(acked_new_packet); + if (use_blackhole_detector_) { + if (acked_new_packet) { + is_path_degrading_ = false; + if (sent_packet_manager_.HasInFlightPackets()) { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_blackhole_detector, 2, 4); + // Restart detections if forward progress has been made. + blackhole_detector_.RestartDetection(GetPathDegradingDeadline(), + GetNetworkBlackholeDeadline()); + } else { + QUIC_RELOADABLE_FLAG_COUNT_N(quic_use_blackhole_detector, 3, 4); + // Stop detections in quiecense. + blackhole_detector_.StopDetection(); + } + } + } else { + MaybeSetPathDegradingAlarm(acked_new_packet); + } if (send_stop_waiting) { ++stop_waiting_count_; @@ -3758,6 +3801,7 @@ } void QuicConnection::MaybeSetPathDegradingAlarm(bool acked_new_packet) { + DCHECK(!use_blackhole_detector_); if (!sent_packet_manager_.HasInFlightPackets()) { // There are no retransmittable packets on the wire, so it's impossible to // say if the connection has degraded. @@ -4134,5 +4178,54 @@ framer_.SetExpectedClientConnectionIdLength(client_connection_id_.length()); } +void QuicConnection::OnPathDegradingDetected() { + DCHECK(use_blackhole_detector_); + is_path_degrading_ = true; + visitor_->OnPathDegrading(); +} + +void QuicConnection::OnBlackholeDetected() { + DCHECK(use_blackhole_detector_); + CloseConnection(QUIC_TOO_MANY_RTOS, "Network blackhole detected.", + ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); +} + +QuicTime QuicConnection::GetPathDegradingDeadline() const { + DCHECK(use_blackhole_detector_); + if (!ShouldDetectPathDegrading()) { + return QuicTime::Zero(); + } + return clock_->ApproximateNow() + + sent_packet_manager_.GetPathDegradingDelay(); +} + +bool QuicConnection::ShouldDetectPathDegrading() const { + DCHECK(use_blackhole_detector_); + return connected_ && handshake_timeout_.IsInfinite() && + perspective_ == Perspective::IS_CLIENT && !is_path_degrading_; +} + +QuicTime QuicConnection::GetNetworkBlackholeDeadline() const { + DCHECK(use_blackhole_detector_); + if (!ShouldDetectBlackhole()) { + return QuicTime::Zero(); + } + return clock_->ApproximateNow() + + sent_packet_manager_.GetNetworkBlackholeDelay(); +} + +bool QuicConnection::ShouldDetectBlackhole() const { + DCHECK(use_blackhole_detector_); + if (!connected_) { + return false; + } + if (!handshake_timeout_.IsInfinite()) { + // No blackhole detection before handshake completes. + return false; + } + return close_connection_after_five_rtos_ || + (sent_packet_manager_.pto_enabled() && max_consecutive_ptos_ > 0); +} + #undef ENDPOINT // undef for jumbo builds } // namespace quic
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h index fcd177a..cd478a6 100644 --- a/quic/core/quic_connection.h +++ b/quic/core/quic_connection.h
@@ -36,6 +36,7 @@ #include "net/third_party/quiche/src/quic/core/quic_connection_stats.h" #include "net/third_party/quiche/src/quic/core/quic_framer.h" #include "net/third_party/quiche/src/quic/core/quic_mtu_discovery.h" +#include "net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.h" #include "net/third_party/quiche/src/quic/core/quic_one_block_arena.h" #include "net/third_party/quiche/src/quic/core/quic_packet_creator.h" #include "net/third_party/quiche/src/quic/core/quic_packet_writer.h" @@ -348,7 +349,8 @@ : public QuicFramerVisitorInterface, public QuicBlockedWriterInterface, public QuicPacketCreator::DelegateInterface, - public QuicSentPacketManager::NetworkChangeVisitor { + public QuicSentPacketManager::NetworkChangeVisitor, + public QuicNetworkBlackholeDetector::Delegate { public: // Constructs a new QuicConnection for |connection_id| and // |initial_peer_address| using |writer| to write packets. |owns_writer| @@ -575,6 +577,10 @@ void OnCongestionChange() override; void OnPathMtuIncreased(QuicPacketLength packet_size) override; + // QuicNetworkBlackholeDetector::Delegate + void OnPathDegradingDetected() override; + void OnBlackholeDetected() override; + // Please note, this is not a const function. For logging purpose, please use // ack_frame(). const QuicFrame GetUpdatedAckFrame(); @@ -1221,6 +1227,20 @@ // reverted to a previous(smaller) value to avoid write errors in the future. bool ShouldIgnoreWriteError(); + // Returns path degrading deadline. QuicTime::Zero() means no path degrading + // detection is needed. + QuicTime GetPathDegradingDeadline() const; + + // Returns true if path degrading should be detected. + bool ShouldDetectPathDegrading() const; + + // Returns network blackhole deadline. QuicTime::Zero() means no blackhole + // detection is needed. + QuicTime GetNetworkBlackholeDeadline() const; + + // Returns true if network blackhole should be detected. + bool ShouldDetectBlackhole() const; + QuicFramer framer_; // Contents received in the current packet, especially used to identify @@ -1379,6 +1399,8 @@ // An alarm that fires when an MTU probe should be sent. QuicArenaScopedPtr<QuicAlarm> mtu_discovery_alarm_; // An alarm that fires when this connection is considered degrading. + // TODO(fayang): Remove this when deprecating quic_use_blackhole_detector + // flag. QuicArenaScopedPtr<QuicAlarm> path_degrading_alarm_; // An alarm that fires to process undecryptable packets when new decyrption // keys are available. @@ -1562,6 +1584,11 @@ QuicCoalescedPacket coalesced_packet_; QuicConnectionMtuDiscoverer mtu_discoverer_; + + QuicNetworkBlackholeDetector blackhole_detector_; + + const bool use_blackhole_detector_ = + GetQuicReloadableFlag(quic_use_blackhole_detector); }; } // namespace quic
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc index cd83e94..1b1511b 100644 --- a/quic/core/quic_connection_test.cc +++ b/quic/core/quic_connection_test.cc
@@ -863,6 +863,34 @@ QuicConnectionPeer::GetProcessUndecryptablePacketsAlarm(this)); } + TestAlarmFactory::TestAlarm* GetBlackholeDetectorAlarm() { + DCHECK(GetQuicReloadableFlag(quic_use_blackhole_detector)); + return reinterpret_cast<TestAlarmFactory::TestAlarm*>( + QuicConnectionPeer::GetBlackholeDetectorAlarm(this)); + } + + void PathDegradingTimeout() { + DCHECK(PathDegradingDetectionInProgress()); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + GetBlackholeDetectorAlarm()->Fire(); + } else { + GetPathDegradingAlarm()->Fire(); + } + } + + bool PathDegradingDetectionInProgress() { + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + return QuicConnectionPeer::GetPathDegradingDeadline(this).IsInitialized(); + } + return GetPathDegradingAlarm()->IsSet(); + } + + bool BlackholeDetectionInProgress() { + DCHECK(GetQuicReloadableFlag(quic_use_blackhole_detector)); + return QuicConnectionPeer::GetBlackholeDetectionDeadline(this) + .IsInitialized(); + } + void SetMaxTailLossProbes(size_t max_tail_loss_probes) { QuicSentPacketManagerPeer::SetMaxTailLossProbes( QuicConnectionPeer::GetSentPacketManager(this), max_tail_loss_probes); @@ -4224,6 +4252,8 @@ QuicTagVector options; options.push_back(kTLPR); config.SetConnectionOptionsToSend(options); + QuicConfigPeer::ReceiveIdleNetworkTimeout(&config, SERVER, + kDefaultIdleTimeoutSecs); connection_.SetFromConfig(config); connection_.SetMaxTailLossProbes(1); @@ -4236,7 +4266,7 @@ EXPECT_TRUE(connection_.connected()); EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) .WillRepeatedly(Return(true)); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); EXPECT_FALSE(connection_.IsPathDegrading()); EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); @@ -4250,7 +4280,7 @@ // Path degrading alarm should be set when there is a retransmittable packet // on the wire. - EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); // Verify the path degrading delay. // First TLP with stream data. @@ -4283,7 +4313,7 @@ // Path degrading alarm should be cancelled as there is no more // reretransmittable packets on the wire. - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); // The ping alarm should be set to the retransmittable_on_wire_timeout. EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); EXPECT_EQ(retransmittable_on_wire_timeout, @@ -4297,7 +4327,7 @@ // The retransmission alarm and the path degrading alarm should be set as // there is a retransmittable packet (PING) on the wire, EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); - EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); // Verify the retransmission delay. QuicTime::Delta min_rto_timeout = @@ -6022,6 +6052,8 @@ QuicTagVector connection_options; connection_options.push_back(k5RTO); config.SetConnectionOptionsToSend(connection_options); + QuicConfigPeer::ReceiveIdleNetworkTimeout(&config, SERVER, + kDefaultIdleTimeoutSecs); connection_.SetFromConfig(config); // Send stream data. @@ -6036,6 +6068,10 @@ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); EXPECT_TRUE(connection_.connected()); } + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_CALL(visitor_, OnPathDegrading()); + connection_.PathDegradingTimeout(); + } EXPECT_EQ(2u, connection_.sent_packet_manager().GetConsecutiveTlpCount()); EXPECT_EQ(4u, connection_.sent_packet_manager().GetConsecutiveRtoCount()); @@ -6043,7 +6079,12 @@ EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AtLeast(1)); - connection_.GetRetransmissionAlarm()->Fire(); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + ASSERT_TRUE(connection_.BlackholeDetectionInProgress()); + connection_.GetBlackholeDetectorAlarm()->Fire(); + } else { + connection_.GetRetransmissionAlarm()->Fire(); + } EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); EXPECT_FALSE(connection_.connected()); TestConnectionCloseQuicErrorCode(QUIC_TOO_MANY_RTOS); @@ -7783,32 +7824,37 @@ TEST_P(QuicConnectionTest, PathDegradingAlarmForCryptoPacket) { EXPECT_TRUE(connection_.connected()); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); EXPECT_FALSE(connection_.IsPathDegrading()); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); connection_.SendCryptoStreamData(); - EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); EXPECT_FALSE(connection_.IsPathDegrading()); QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_) ->GetPathDegradingDelay(); - EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - - clock_.ApproximateNow()); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_EQ(delay, connection_.GetBlackholeDetectorAlarm()->deadline() - + clock_.ApproximateNow()); + } else { + EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - + clock_.ApproximateNow()); + } // Fire the path degrading alarm, path degrading signal should be sent to // the visitor. EXPECT_CALL(visitor_, OnPathDegrading()); clock_.AdvanceTime(delay); - connection_.GetPathDegradingAlarm()->Fire(); + connection_.PathDegradingTimeout(); EXPECT_TRUE(connection_.IsPathDegrading()); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); } // Includes regression test for b/69979024. TEST_P(QuicConnectionTest, PathDegradingAlarmForNonCryptoPackets) { EXPECT_TRUE(connection_.connected()); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); EXPECT_FALSE(connection_.IsPathDegrading()); const char data[] = "data"; @@ -7822,25 +7868,38 @@ GetNthClientInitiatedStreamId(1, connection_.transport_version()), data, offset, NO_FIN); offset += data_size; - EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); // Check the deadline of the path degrading alarm. QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_) ->GetPathDegradingDelay(); - EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - - clock_.ApproximateNow()); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_EQ(delay, connection_.GetBlackholeDetectorAlarm()->deadline() - + clock_.ApproximateNow()); + } else { + EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - + clock_.ApproximateNow()); + } // Send a second packet. The path degrading alarm's deadline should remain // the same. // Regression test for b/69979024. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); QuicTime prev_deadline = connection_.GetPathDegradingAlarm()->deadline(); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + prev_deadline = connection_.GetBlackholeDetectorAlarm()->deadline(); + } connection_.SendStreamDataWithString( GetNthClientInitiatedStreamId(1, connection_.transport_version()), data, offset, NO_FIN); offset += data_size; - EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); - EXPECT_EQ(prev_deadline, connection_.GetPathDegradingAlarm()->deadline()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_EQ(prev_deadline, + connection_.GetBlackholeDetectorAlarm()->deadline()); + } else { + EXPECT_EQ(prev_deadline, connection_.GetPathDegradingAlarm()->deadline()); + } // Now receive an ACK of the first packet. This should advance the path // degrading alarm's deadline since forward progress has been made. @@ -7852,12 +7911,17 @@ QuicAckFrame frame = InitAckFrame( {{QuicPacketNumber(1u + 2u * i), QuicPacketNumber(2u + 2u * i)}}); ProcessAckPacket(&frame); - EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); // Check the deadline of the path degrading alarm. delay = QuicConnectionPeer::GetSentPacketManager(&connection_) ->GetPathDegradingDelay(); - EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - - clock_.ApproximateNow()); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_EQ(delay, connection_.GetBlackholeDetectorAlarm()->deadline() - + clock_.ApproximateNow()); + } else { + EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - + clock_.ApproximateNow()); + } if (i == 0) { // Now receive an ACK of the second packet. Since there are no more @@ -7867,14 +7931,14 @@ EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}}); ProcessAckPacket(&frame); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); } else { // Advance time to the path degrading alarm's deadline and simulate // firing the alarm. clock_.AdvanceTime(delay); EXPECT_CALL(visitor_, OnPathDegrading()); - connection_.GetPathDegradingAlarm()->Fire(); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + connection_.PathDegradingTimeout(); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); } } EXPECT_TRUE(connection_.IsPathDegrading()); @@ -7890,7 +7954,7 @@ EXPECT_CALL(visitor_, ShouldKeepConnectionAlive()) .WillRepeatedly(Return(true)); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); EXPECT_FALSE(connection_.IsPathDegrading()); EXPECT_FALSE(connection_.GetPingAlarm()->IsSet()); @@ -7904,11 +7968,16 @@ // Now there's a retransmittable packet on the wire, so the path degrading // alarm should be set. // The retransmittable-on-wire alarm should not be set. - EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_) ->GetPathDegradingDelay(); - EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - - clock_.ApproximateNow()); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_EQ(delay, connection_.GetBlackholeDetectorAlarm()->deadline() - + clock_.ApproximateNow()); + } else { + EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - + clock_.ApproximateNow()); + } ASSERT_TRUE(connection_.sent_packet_manager().HasInFlightPackets()); // The ping alarm is set for the ping timeout, not the shorter // retransmittable_on_wire_timeout. @@ -7927,7 +7996,7 @@ // No more retransmittable packets on the wire, so the path degrading alarm // should be cancelled, and the ping alarm should be set to the // retransmittable_on_wire_timeout. - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); EXPECT_TRUE(connection_.GetPingAlarm()->IsSet()); EXPECT_EQ(retransmittable_on_wire_timeout, connection_.GetPingAlarm()->deadline() - clock_.ApproximateNow()); @@ -7941,11 +8010,16 @@ // Now there's a retransmittable packet (PING) on the wire, so the path // degrading alarm should be set. - EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); delay = QuicConnectionPeer::GetSentPacketManager(&connection_) ->GetPathDegradingDelay(); - EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - - clock_.ApproximateNow()); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_EQ(delay, connection_.GetBlackholeDetectorAlarm()->deadline() - + clock_.ApproximateNow()); + } else { + EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - + clock_.ApproximateNow()); + } } // This test verifies that the connection marks path as degrading and does not @@ -7953,7 +8027,7 @@ // degraded path. TEST_P(QuicConnectionTest, NoPathDegradingAlarmIfPathIsDegrading) { EXPECT_TRUE(connection_.connected()); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); EXPECT_FALSE(connection_.IsPathDegrading()); const char data[] = "data"; @@ -7964,21 +8038,34 @@ // the path degrading alarm should be set. connection_.SendStreamDataWithString(1, data, offset, NO_FIN); offset += data_size; - EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); // Check the deadline of the path degrading alarm. QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_) ->GetPathDegradingDelay(); - EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - - clock_.ApproximateNow()); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_EQ(delay, connection_.GetBlackholeDetectorAlarm()->deadline() - + clock_.ApproximateNow()); + } else { + EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - + clock_.ApproximateNow()); + } // Send a second packet. The path degrading alarm's deadline should remain // the same. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); QuicTime prev_deadline = connection_.GetPathDegradingAlarm()->deadline(); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + prev_deadline = connection_.GetBlackholeDetectorAlarm()->deadline(); + } connection_.SendStreamDataWithString(1, data, offset, NO_FIN); offset += data_size; - EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); - EXPECT_EQ(prev_deadline, connection_.GetPathDegradingAlarm()->deadline()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_EQ(prev_deadline, + connection_.GetBlackholeDetectorAlarm()->deadline()); + } else { + EXPECT_EQ(prev_deadline, connection_.GetPathDegradingAlarm()->deadline()); + } // Now receive an ACK of the first packet. This should advance the path // degrading alarm's deadline since forward progress has been made. @@ -7988,29 +8075,34 @@ QuicAckFrame frame = InitAckFrame({{QuicPacketNumber(1u), QuicPacketNumber(2u)}}); ProcessAckPacket(&frame); - EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); // Check the deadline of the path degrading alarm. delay = QuicConnectionPeer::GetSentPacketManager(&connection_) ->GetPathDegradingDelay(); - EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - - clock_.ApproximateNow()); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_EQ(delay, connection_.GetBlackholeDetectorAlarm()->deadline() - + clock_.ApproximateNow()); + } else { + EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - + clock_.ApproximateNow()); + } // Advance time to the path degrading alarm's deadline and simulate // firing the path degrading alarm. This path will be considered as // degrading. clock_.AdvanceTime(delay); EXPECT_CALL(visitor_, OnPathDegrading()).Times(1); - connection_.GetPathDegradingAlarm()->Fire(); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + connection_.PathDegradingTimeout(); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); EXPECT_TRUE(connection_.IsPathDegrading()); clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); // Send a third packet. The path degrading alarm is no longer set but path // should still be marked as degrading. connection_.SendStreamDataWithString(1, data, offset, NO_FIN); offset += data_size; - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); EXPECT_TRUE(connection_.IsPathDegrading()); } @@ -8019,7 +8111,7 @@ // after path has been marked degrading. TEST_P(QuicConnectionTest, UnmarkPathDegradingOnForwardProgress) { EXPECT_TRUE(connection_.connected()); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); EXPECT_FALSE(connection_.IsPathDegrading()); const char data[] = "data"; @@ -8030,21 +8122,34 @@ // the path degrading alarm should be set. connection_.SendStreamDataWithString(1, data, offset, NO_FIN); offset += data_size; - EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); // Check the deadline of the path degrading alarm. QuicTime::Delta delay = QuicConnectionPeer::GetSentPacketManager(&connection_) ->GetPathDegradingDelay(); - EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - - clock_.ApproximateNow()); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_EQ(delay, connection_.GetBlackholeDetectorAlarm()->deadline() - + clock_.ApproximateNow()); + } else { + EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - + clock_.ApproximateNow()); + } // Send a second packet. The path degrading alarm's deadline should remain // the same. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); QuicTime prev_deadline = connection_.GetPathDegradingAlarm()->deadline(); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + prev_deadline = connection_.GetBlackholeDetectorAlarm()->deadline(); + } connection_.SendStreamDataWithString(1, data, offset, NO_FIN); offset += data_size; - EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); - EXPECT_EQ(prev_deadline, connection_.GetPathDegradingAlarm()->deadline()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_EQ(prev_deadline, + connection_.GetBlackholeDetectorAlarm()->deadline()); + } else { + EXPECT_EQ(prev_deadline, connection_.GetPathDegradingAlarm()->deadline()); + } // Now receive an ACK of the first packet. This should advance the path // degrading alarm's deadline since forward progress has been made. @@ -8054,28 +8159,33 @@ QuicAckFrame frame = InitAckFrame({{QuicPacketNumber(1u), QuicPacketNumber(2u)}}); ProcessAckPacket(&frame); - EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); // Check the deadline of the path degrading alarm. delay = QuicConnectionPeer::GetSentPacketManager(&connection_) ->GetPathDegradingDelay(); - EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - - clock_.ApproximateNow()); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_EQ(delay, connection_.GetBlackholeDetectorAlarm()->deadline() - + clock_.ApproximateNow()); + } else { + EXPECT_EQ(delay, connection_.GetPathDegradingAlarm()->deadline() - + clock_.ApproximateNow()); + } // Advance time to the path degrading alarm's deadline and simulate // firing the alarm. clock_.AdvanceTime(delay); EXPECT_CALL(visitor_, OnPathDegrading()).Times(1); - connection_.GetPathDegradingAlarm()->Fire(); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + connection_.PathDegradingTimeout(); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); EXPECT_TRUE(connection_.IsPathDegrading()); // Send a third packet. The path degrading alarm is no longer set but path // should still be marked as degrading. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); connection_.SendStreamDataWithString(1, data, offset, NO_FIN); offset += data_size; - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); EXPECT_TRUE(connection_.IsPathDegrading()); // Now receive an ACK of the second packet. This should unmark the path as @@ -8085,7 +8195,7 @@ frame = InitAckFrame({{QuicPacketNumber(2), QuicPacketNumber(3)}}); ProcessAckPacket(&frame); EXPECT_FALSE(connection_.IsPathDegrading()); - EXPECT_TRUE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_TRUE(connection_.PathDegradingDetectionInProgress()); } TEST_P(QuicConnectionTest, NoPathDegradingOnServer) { @@ -8096,13 +8206,13 @@ QuicPacketCreatorPeer::SetSendVersionInPacket(creator_, false); EXPECT_FALSE(connection_.IsPathDegrading()); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); // Send data. const char data[] = "data"; connection_.SendStreamDataWithString(1, data, 0, NO_FIN); EXPECT_FALSE(connection_.IsPathDegrading()); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); // Ack data. clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(5)); @@ -8111,7 +8221,7 @@ InitAckFrame({{QuicPacketNumber(1u), QuicPacketNumber(2u)}}); ProcessAckPacket(&frame); EXPECT_FALSE(connection_.IsPathDegrading()); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); } TEST_P(QuicConnectionTest, NoPathDegradingAfterSendingAck) { @@ -8125,7 +8235,7 @@ EXPECT_FALSE(connection_.sent_packet_manager().unacked_packets().empty()); EXPECT_FALSE(connection_.sent_packet_manager().HasInFlightPackets()); EXPECT_FALSE(connection_.IsPathDegrading()); - EXPECT_FALSE(connection_.GetPathDegradingAlarm()->IsSet()); + EXPECT_FALSE(connection_.PathDegradingDetectionInProgress()); } TEST_P(QuicConnectionTest, MultipleCallsToCloseConnection) { @@ -9714,6 +9824,8 @@ connection_options.push_back(k1PTO); connection_options.push_back(k6PTO); config.SetConnectionOptionsToSend(connection_options); + QuicConfigPeer::ReceiveIdleNetworkTimeout(&config, SERVER, + kDefaultIdleTimeoutSecs); EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); connection_.SetFromConfig(config); EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); @@ -9732,6 +9844,10 @@ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); EXPECT_TRUE(connection_.connected()); } + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_CALL(visitor_, OnPathDegrading()); + connection_.PathDegradingTimeout(); + } EXPECT_EQ(0u, connection_.sent_packet_manager().GetConsecutiveTlpCount()); EXPECT_EQ(0u, connection_.sent_packet_manager().GetConsecutiveRtoCount()); @@ -9739,7 +9855,12 @@ // Closes connection on 6th PTO. EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)); - connection_.GetRetransmissionAlarm()->Fire(); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + ASSERT_TRUE(connection_.BlackholeDetectionInProgress()); + connection_.GetBlackholeDetectorAlarm()->Fire(); + } else { + connection_.GetRetransmissionAlarm()->Fire(); + } EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); EXPECT_FALSE(connection_.connected()); TestConnectionCloseQuicErrorCode(QUIC_TOO_MANY_RTOS); @@ -9751,6 +9872,8 @@ connection_options.push_back(k2PTO); connection_options.push_back(k7PTO); config.SetConnectionOptionsToSend(connection_options); + QuicConfigPeer::ReceiveIdleNetworkTimeout(&config, SERVER, + kDefaultIdleTimeoutSecs); EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); connection_.SetFromConfig(config); EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); @@ -9767,6 +9890,10 @@ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); EXPECT_TRUE(connection_.connected()); } + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_CALL(visitor_, OnPathDegrading()); + connection_.PathDegradingTimeout(); + } EXPECT_EQ(0u, connection_.sent_packet_manager().GetConsecutiveTlpCount()); EXPECT_EQ(0u, connection_.sent_packet_manager().GetConsecutiveRtoCount()); @@ -9775,7 +9902,12 @@ EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AtLeast(1)); - connection_.GetRetransmissionAlarm()->Fire(); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + ASSERT_TRUE(connection_.BlackholeDetectionInProgress()); + connection_.GetBlackholeDetectorAlarm()->Fire(); + } else { + connection_.GetRetransmissionAlarm()->Fire(); + } EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); EXPECT_FALSE(connection_.connected()); TestConnectionCloseQuicErrorCode(QUIC_TOO_MANY_RTOS); @@ -9786,6 +9918,8 @@ QuicTagVector connection_options; connection_options.push_back(k2PTO); connection_options.push_back(k8PTO); + QuicConfigPeer::ReceiveIdleNetworkTimeout(&config, SERVER, + kDefaultIdleTimeoutSecs); config.SetConnectionOptionsToSend(connection_options); EXPECT_CALL(*send_algorithm_, SetFromConfig(_, _)); connection_.SetFromConfig(config); @@ -9803,6 +9937,10 @@ EXPECT_TRUE(connection_.GetTimeoutAlarm()->IsSet()); EXPECT_TRUE(connection_.connected()); } + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + EXPECT_CALL(visitor_, OnPathDegrading()); + connection_.PathDegradingTimeout(); + } EXPECT_EQ(0u, connection_.sent_packet_manager().GetConsecutiveTlpCount()); EXPECT_EQ(0u, connection_.sent_packet_manager().GetConsecutiveRtoCount()); @@ -9811,7 +9949,12 @@ EXPECT_CALL(visitor_, OnConnectionClosed(_, ConnectionCloseSource::FROM_SELF)); EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(AtLeast(1)); - connection_.GetRetransmissionAlarm()->Fire(); + if (GetQuicReloadableFlag(quic_use_blackhole_detector)) { + ASSERT_TRUE(connection_.BlackholeDetectionInProgress()); + connection_.GetBlackholeDetectorAlarm()->Fire(); + } else { + connection_.GetRetransmissionAlarm()->Fire(); + } EXPECT_FALSE(connection_.GetTimeoutAlarm()->IsSet()); EXPECT_FALSE(connection_.connected()); TestConnectionCloseQuicErrorCode(QUIC_TOO_MANY_RTOS);
diff --git a/quic/core/quic_network_blackhole_detector.cc b/quic/core/quic_network_blackhole_detector.cc new file mode 100644 index 0000000..9206ae4 --- /dev/null +++ b/quic/core/quic_network_blackhole_detector.cc
@@ -0,0 +1,78 @@ +// Copyright (c) 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.h" + +#include "net/third_party/quiche/src/quic/core/quic_constants.h" + +namespace quic { + +namespace { + +class AlarmDelegate : public QuicAlarm::Delegate { + public: + explicit AlarmDelegate(QuicNetworkBlackholeDetector* detector) + : detector_(detector) {} + AlarmDelegate(const AlarmDelegate&) = delete; + AlarmDelegate& operator=(const AlarmDelegate&) = delete; + + void OnAlarm() override { detector_->OnAlarm(); } + + private: + QuicNetworkBlackholeDetector* detector_; +}; + +} // namespace + +QuicNetworkBlackholeDetector::QuicNetworkBlackholeDetector( + Delegate* delegate, + QuicConnectionArena* arena, + QuicAlarmFactory* alarm_factory) + : delegate_(delegate), + path_degrading_deadline_(QuicTime::Zero()), + blackhole_deadline_(QuicTime::Zero()), + alarm_( + alarm_factory->CreateAlarm(arena->New<AlarmDelegate>(this), arena)) {} + +void QuicNetworkBlackholeDetector::OnAlarm() { + if (path_degrading_deadline_.IsInitialized()) { + path_degrading_deadline_ = QuicTime::Zero(); + delegate_->OnPathDegradingDetected(); + // Switch to blackhole detection mode. + alarm_->Update(blackhole_deadline_, kAlarmGranularity); + return; + } + if (blackhole_deadline_.IsInitialized()) { + blackhole_deadline_ = QuicTime::Zero(); + delegate_->OnBlackholeDetected(); + } +} + +void QuicNetworkBlackholeDetector::StopDetection() { + alarm_->Cancel(); + path_degrading_deadline_ = QuicTime::Zero(); + blackhole_deadline_ = QuicTime::Zero(); +} + +void QuicNetworkBlackholeDetector::RestartDetection( + QuicTime path_degrading_deadline, + QuicTime blackhole_deadline) { + path_degrading_deadline_ = path_degrading_deadline; + blackhole_deadline_ = blackhole_deadline; + QUIC_BUG_IF(path_degrading_deadline_.IsInitialized() && + blackhole_deadline_.IsInitialized() && + path_degrading_deadline_ > blackhole_deadline_) + << "Path degrading timeout is later than blackhole detection timeout"; + alarm_->Update(path_degrading_deadline_, kAlarmGranularity); + if (alarm_->IsSet()) { + return; + } + alarm_->Update(blackhole_deadline_, kAlarmGranularity); +} + +bool QuicNetworkBlackholeDetector::IsDetectionInProgress() const { + return alarm_->IsSet(); +} + +} // namespace quic
diff --git a/quic/core/quic_network_blackhole_detector.h b/quic/core/quic_network_blackhole_detector.h new file mode 100644 index 0000000..b839d42 --- /dev/null +++ b/quic/core/quic_network_blackhole_detector.h
@@ -0,0 +1,76 @@ +// Copyright (c) 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef QUICHE_QUIC_CORE_QUIC_NETWORK_BLACKHOLE_DETECTOR_H_ +#define QUICHE_QUIC_CORE_QUIC_NETWORK_BLACKHOLE_DETECTOR_H_ + +#include "net/third_party/quiche/src/quic/core/quic_alarm.h" +#include "net/third_party/quiche/src/quic/core/quic_alarm_factory.h" +#include "net/third_party/quiche/src/quic/core/quic_one_block_arena.h" +#include "net/third_party/quiche/src/quic/core/quic_time.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_export.h" + +namespace quic { + +namespace test { +class QuicConnectionPeer; +class QuicNetworkBlackholeDetectorPeer; +} // namespace test + +// QuicNetworkBlackholeDetector can detect path degrading and/or network +// blackhole. If both detections are in progress, detector will be in path +// degrading detection mode. After reporting path degrading detected, detector +// switches to blackhole detection mode. So blackhole detection deadline must +// be later than path degrading deadline. +class QUIC_EXPORT_PRIVATE QuicNetworkBlackholeDetector { + public: + class QUIC_EXPORT_PRIVATE Delegate { + public: + virtual ~Delegate() {} + + // Called when the path degrading alarm fires. + virtual void OnPathDegradingDetected() = 0; + + // Called when the path blackhole alarm fires. + virtual void OnBlackholeDetected() = 0; + }; + + QuicNetworkBlackholeDetector(Delegate* delegate, + QuicConnectionArena* arena, + QuicAlarmFactory* alarm_factory); + + // Called to stop all detections. + void StopDetection(); + + // Called to restart path degrading or/and blackhole detections. Please note, + // if both deadlines are set, |blackhole_deadline| must be later than + // |path_degrading_deadline|. + void RestartDetection(QuicTime path_degrading_deadline, + QuicTime blackhole_deadline); + + // Called when |alarm_| fires. + void OnAlarm(); + + // Returns true if |alarm_| is set. + bool IsDetectionInProgress() const; + + private: + friend class test::QuicConnectionPeer; + friend class test::QuicNetworkBlackholeDetectorPeer; + + Delegate* delegate_; // Not owned. + + // Time that Delegate::OnPathDegrading will be called. 0 means no path + // degrading detection is in progress. + QuicTime path_degrading_deadline_; + // Time that Delegate::OnBlackholeDetected will be called. 0 means no + // blackhole detection is in progress. + QuicTime blackhole_deadline_; + + QuicArenaScopedPtr<QuicAlarm> alarm_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QUIC_NETWORK_BLACKHOLE_DETECTOR_H_
diff --git a/quic/core/quic_network_blackhole_detector_test.cc b/quic/core/quic_network_blackhole_detector_test.cc new file mode 100644 index 0000000..eca50e3 --- /dev/null +++ b/quic/core/quic_network_blackhole_detector_test.cc
@@ -0,0 +1,121 @@ +// Copyright (c) 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/third_party/quiche/src/quic/core/quic_network_blackhole_detector.h" + +#include "net/third_party/quiche/src/quic/core/quic_one_block_arena.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" +#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" + +namespace quic { +namespace test { + +class QuicNetworkBlackholeDetectorPeer { + public: + static QuicAlarm* GetAlarm(QuicNetworkBlackholeDetector* detector) { + return detector->alarm_.get(); + } +}; + +namespace { +class MockDelegate : public QuicNetworkBlackholeDetector::Delegate { + public: + MOCK_METHOD0(OnPathDegradingDetected, void()); + MOCK_METHOD0(OnBlackholeDetected, void()); +}; + +const size_t kPathDegradingDelayInSeconds = 5; +const size_t kBlackholeDelayInSeconds = 10; + +class QuicNetworkBlackholeDetectorTest : public QuicTest { + public: + QuicNetworkBlackholeDetectorTest() + : detector_(&delegate_, &arena_, &alarm_factory_), + alarm_(static_cast<MockAlarmFactory::TestAlarm*>( + QuicNetworkBlackholeDetectorPeer::GetAlarm(&detector_))), + path_degrading_delay_( + QuicTime::Delta::FromSeconds(kPathDegradingDelayInSeconds)), + blackhole_delay_( + QuicTime::Delta::FromSeconds(kBlackholeDelayInSeconds)) { + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + } + + protected: + testing::StrictMock<MockDelegate> delegate_; + QuicConnectionArena arena_; + MockAlarmFactory alarm_factory_; + + QuicNetworkBlackholeDetector detector_; + + MockAlarmFactory::TestAlarm* alarm_; + MockClock clock_; + const QuicTime::Delta path_degrading_delay_; + const QuicTime::Delta blackhole_delay_; +}; + +TEST_F(QuicNetworkBlackholeDetectorTest, StartAndFire) { + EXPECT_FALSE(detector_.IsDetectionInProgress()); + + detector_.RestartDetection(clock_.Now() + path_degrading_delay_, + clock_.Now() + blackhole_delay_); + EXPECT_TRUE(detector_.IsDetectionInProgress()); + EXPECT_EQ(clock_.Now() + path_degrading_delay_, alarm_->deadline()); + + // Fire path degrading alarm. + clock_.AdvanceTime(path_degrading_delay_); + EXPECT_CALL(delegate_, OnPathDegradingDetected()); + alarm_->Fire(); + // Verify blackhole detection is still in progress. + EXPECT_TRUE(detector_.IsDetectionInProgress()); + EXPECT_EQ(clock_.Now() + blackhole_delay_ - path_degrading_delay_, + alarm_->deadline()); + + // Fire blackhole detection alarm. + clock_.AdvanceTime(blackhole_delay_ - path_degrading_delay_); + EXPECT_CALL(delegate_, OnBlackholeDetected()); + alarm_->Fire(); + EXPECT_FALSE(detector_.IsDetectionInProgress()); +} + +TEST_F(QuicNetworkBlackholeDetectorTest, RestartAndStop) { + detector_.RestartDetection(clock_.Now() + path_degrading_delay_, + clock_.Now() + blackhole_delay_); + + clock_.AdvanceTime(QuicTime::Delta::FromSeconds(1)); + detector_.RestartDetection(clock_.Now() + path_degrading_delay_, + clock_.Now() + blackhole_delay_); + EXPECT_EQ(clock_.Now() + path_degrading_delay_, alarm_->deadline()); + + detector_.StopDetection(); + EXPECT_FALSE(detector_.IsDetectionInProgress()); +} + +TEST_F(QuicNetworkBlackholeDetectorTest, PathDegradingFiresAndRestart) { + EXPECT_FALSE(detector_.IsDetectionInProgress()); + detector_.RestartDetection(clock_.Now() + path_degrading_delay_, + clock_.Now() + blackhole_delay_); + EXPECT_TRUE(detector_.IsDetectionInProgress()); + EXPECT_EQ(clock_.Now() + path_degrading_delay_, alarm_->deadline()); + + // Fire path degrading alarm. + clock_.AdvanceTime(path_degrading_delay_); + EXPECT_CALL(delegate_, OnPathDegradingDetected()); + alarm_->Fire(); + // Verify blackhole detection is still in progress. + EXPECT_TRUE(detector_.IsDetectionInProgress()); + EXPECT_EQ(clock_.Now() + blackhole_delay_ - path_degrading_delay_, + alarm_->deadline()); + + // After 100ms, restart detections on forward progress. + clock_.AdvanceTime(QuicTime::Delta::FromMilliseconds(100)); + detector_.RestartDetection(clock_.Now() + path_degrading_delay_, + clock_.Now() + blackhole_delay_); + // Verify alarm is armed based on path degrading deadline. + EXPECT_EQ(clock_.Now() + path_degrading_delay_, alarm_->deadline()); +} + +} // namespace + +} // namespace test +} // namespace quic
diff --git a/quic/core/quic_sent_packet_manager.cc b/quic/core/quic_sent_packet_manager.cc index 4a041d8..d87d6c7 100644 --- a/quic/core/quic_sent_packet_manager.cc +++ b/quic/core/quic_sent_packet_manager.cc
@@ -32,6 +32,9 @@ // The path degrading delay is the sum of this number of consecutive RTO delays. const size_t kNumRetransmissionDelaysForPathDegradingDelay = 2; +// The blachkhole delay is the sum of this number of consecutive RTO delays. +const size_t kNumRetransmissionDelaysForBlackholeDelay = 5; + // Ensure the handshake timer isnt't faster than 10ms. // This limits the tenth retransmitted packet to 10s after the initial CHLO. static const int64_t kMinHandshakeTimeoutMs = 10; @@ -1107,6 +1110,11 @@ max_tail_loss_probes_ + kNumRetransmissionDelaysForPathDegradingDelay); } +const QuicTime::Delta QuicSentPacketManager::GetNetworkBlackholeDelay() const { + return GetNConsecutiveRetransmissionTimeoutDelay( + max_tail_loss_probes_ + kNumRetransmissionDelaysForBlackholeDelay); +} + const QuicTime::Delta QuicSentPacketManager::GetCryptoRetransmissionDelay() const { // This is equivalent to the TailLossProbeDelay, but slightly more aggressive
diff --git a/quic/core/quic_sent_packet_manager.h b/quic/core/quic_sent_packet_manager.h index ed51a9c..5f1de70 100644 --- a/quic/core/quic_sent_packet_manager.h +++ b/quic/core/quic_sent_packet_manager.h
@@ -213,6 +213,9 @@ // notify the session that this connection is degrading. const QuicTime::Delta GetPathDegradingDelay() const; + // Returns the current delay for detecting network blackhole. + const QuicTime::Delta GetNetworkBlackholeDelay() const; + const RttStats* GetRttStats() const { return &rtt_stats_; } // Returns the estimated bandwidth calculated by the congestion algorithm.
diff --git a/quic/test_tools/quic_config_peer.cc b/quic/test_tools/quic_config_peer.cc index 41527c2..20463ab 100644 --- a/quic/test_tools/quic_config_peer.cc +++ b/quic/test_tools/quic_config_peer.cc
@@ -97,5 +97,14 @@ config->max_packet_size_.SetReceivedValue(max_packet_size); } +// static +void QuicConfigPeer::ReceiveIdleNetworkTimeout(QuicConfig* config, + HelloType hello_type, + uint32_t idle_timeout_seconds) { + std::string error_details; + config->idle_network_timeout_seconds_.ReceiveValue( + idle_timeout_seconds, hello_type, &error_details); +} + } // namespace test } // namespace quic
diff --git a/quic/test_tools/quic_config_peer.h b/quic/test_tools/quic_config_peer.h index 61e2ef2..b52bf42 100644 --- a/quic/test_tools/quic_config_peer.h +++ b/quic/test_tools/quic_config_peer.h
@@ -58,6 +58,10 @@ static void SetReceivedMaxPacketSize(QuicConfig* config, uint32_t max_packet_size); + + static void ReceiveIdleNetworkTimeout(QuicConfig* config, + HelloType hello_type, + uint32_t idle_timeout_seconds); }; } // namespace test
diff --git a/quic/test_tools/quic_connection_peer.cc b/quic/test_tools/quic_connection_peer.cc index b38b64e..21a0105 100644 --- a/quic/test_tools/quic_connection_peer.cc +++ b/quic/test_tools/quic_connection_peer.cc
@@ -348,5 +348,29 @@ return count; } +// static +QuicNetworkBlackholeDetector& QuicConnectionPeer::GetBlackholeDetector( + QuicConnection* connection) { + return connection->blackhole_detector_; +} + +// static +QuicAlarm* QuicConnectionPeer::GetBlackholeDetectorAlarm( + QuicConnection* connection) { + return connection->blackhole_detector_.alarm_.get(); +} + +// static +QuicTime QuicConnectionPeer::GetPathDegradingDeadline( + QuicConnection* connection) { + return connection->blackhole_detector_.path_degrading_deadline_; +} + +// static +QuicTime QuicConnectionPeer::GetBlackholeDetectionDeadline( + QuicConnection* connection) { + return connection->blackhole_detector_.blackhole_deadline_; +} + } // namespace test } // namespace quic
diff --git a/quic/test_tools/quic_connection_peer.h b/quic/test_tools/quic_connection_peer.h index e0a97b8..ab14190 100644 --- a/quic/test_tools/quic_connection_peer.h +++ b/quic/test_tools/quic_connection_peer.h
@@ -135,6 +135,15 @@ const std::string& details); static size_t GetNumEncryptionLevels(QuicConnection* connection); + + static QuicNetworkBlackholeDetector& GetBlackholeDetector( + QuicConnection* connection); + + static QuicAlarm* GetBlackholeDetectorAlarm(QuicConnection* connection); + + static QuicTime GetPathDegradingDeadline(QuicConnection* connection); + + static QuicTime GetBlackholeDetectionDeadline(QuicConnection* connection); }; } // namespace test