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