1 // Copyright (c) 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "quiche/quic/core/quic_network_blackhole_detector.h"
6
7 #include "quiche/quic/core/quic_constants.h"
8
9 namespace quic {
10
11 namespace {
12
13 class AlarmDelegate : public QuicAlarm::DelegateWithContext {
14 public:
AlarmDelegate(QuicNetworkBlackholeDetector * detector,QuicConnectionContext * context)15 explicit AlarmDelegate(QuicNetworkBlackholeDetector* detector,
16 QuicConnectionContext* context)
17 : QuicAlarm::DelegateWithContext(context), detector_(detector) {}
18 AlarmDelegate(const AlarmDelegate&) = delete;
19 AlarmDelegate& operator=(const AlarmDelegate&) = delete;
20
OnAlarm()21 void OnAlarm() override { detector_->OnAlarm(); }
22
23 private:
24 QuicNetworkBlackholeDetector* detector_;
25 };
26
27 } // namespace
28
QuicNetworkBlackholeDetector(Delegate * delegate,QuicConnectionArena * arena,QuicAlarmFactory * alarm_factory,QuicConnectionContext * context)29 QuicNetworkBlackholeDetector::QuicNetworkBlackholeDetector(
30 Delegate* delegate, QuicConnectionArena* arena,
31 QuicAlarmFactory* alarm_factory, QuicConnectionContext* context)
32 : delegate_(delegate),
33 alarm_(alarm_factory->CreateAlarm(
34 arena->New<AlarmDelegate>(this, context), arena)) {}
35
OnAlarm()36 void QuicNetworkBlackholeDetector::OnAlarm() {
37 QuicTime next_deadline = GetEarliestDeadline();
38 if (!next_deadline.IsInitialized()) {
39 QUIC_BUG(quic_bug_10328_1) << "BlackholeDetector alarm fired unexpectedly";
40 return;
41 }
42
43 QUIC_DVLOG(1) << "BlackholeDetector alarm firing. next_deadline:"
44 << next_deadline
45 << ", path_degrading_deadline_:" << path_degrading_deadline_
46 << ", path_mtu_reduction_deadline_:"
47 << path_mtu_reduction_deadline_
48 << ", blackhole_deadline_:" << blackhole_deadline_;
49 if (path_degrading_deadline_ == next_deadline) {
50 path_degrading_deadline_ = QuicTime::Zero();
51 delegate_->OnPathDegradingDetected();
52 }
53
54 if (path_mtu_reduction_deadline_ == next_deadline) {
55 path_mtu_reduction_deadline_ = QuicTime::Zero();
56 delegate_->OnPathMtuReductionDetected();
57 }
58
59 if (blackhole_deadline_ == next_deadline) {
60 blackhole_deadline_ = QuicTime::Zero();
61 delegate_->OnBlackholeDetected();
62 }
63
64 UpdateAlarm();
65 }
66
StopDetection(bool permanent)67 void QuicNetworkBlackholeDetector::StopDetection(bool permanent) {
68 if (permanent) {
69 alarm_->PermanentCancel();
70 } else {
71 alarm_->Cancel();
72 }
73 path_degrading_deadline_ = QuicTime::Zero();
74 blackhole_deadline_ = QuicTime::Zero();
75 path_mtu_reduction_deadline_ = QuicTime::Zero();
76 }
77
RestartDetection(QuicTime path_degrading_deadline,QuicTime blackhole_deadline,QuicTime path_mtu_reduction_deadline)78 void QuicNetworkBlackholeDetector::RestartDetection(
79 QuicTime path_degrading_deadline, QuicTime blackhole_deadline,
80 QuicTime path_mtu_reduction_deadline) {
81 path_degrading_deadline_ = path_degrading_deadline;
82 blackhole_deadline_ = blackhole_deadline;
83 path_mtu_reduction_deadline_ = path_mtu_reduction_deadline;
84
85 QUIC_BUG_IF(quic_bug_12708_1, blackhole_deadline_.IsInitialized() &&
86 blackhole_deadline_ != GetLastDeadline())
87 << "Blackhole detection deadline should be the last deadline.";
88
89 UpdateAlarm();
90 }
91
GetEarliestDeadline() const92 QuicTime QuicNetworkBlackholeDetector::GetEarliestDeadline() const {
93 QuicTime result = QuicTime::Zero();
94 for (QuicTime t : {path_degrading_deadline_, blackhole_deadline_,
95 path_mtu_reduction_deadline_}) {
96 if (!t.IsInitialized()) {
97 continue;
98 }
99
100 if (!result.IsInitialized() || t < result) {
101 result = t;
102 }
103 }
104
105 return result;
106 }
107
GetLastDeadline() const108 QuicTime QuicNetworkBlackholeDetector::GetLastDeadline() const {
109 return std::max({path_degrading_deadline_, blackhole_deadline_,
110 path_mtu_reduction_deadline_});
111 }
112
UpdateAlarm() const113 void QuicNetworkBlackholeDetector::UpdateAlarm() const {
114 // If called after OnBlackholeDetected(), the alarm may have been permanently
115 // cancelled and is not safe to be armed again.
116 if (alarm_->IsPermanentlyCancelled()) {
117 return;
118 }
119
120 QuicTime next_deadline = GetEarliestDeadline();
121
122 QUIC_DVLOG(1) << "Updating alarm. next_deadline:" << next_deadline
123 << ", path_degrading_deadline_:" << path_degrading_deadline_
124 << ", path_mtu_reduction_deadline_:"
125 << path_mtu_reduction_deadline_
126 << ", blackhole_deadline_:" << blackhole_deadline_;
127
128 alarm_->Update(next_deadline, kAlarmGranularity);
129 }
130
IsDetectionInProgress() const131 bool QuicNetworkBlackholeDetector::IsDetectionInProgress() const {
132 return alarm_->IsSet();
133 }
134
135 } // namespace quic
136