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_path_validator.h"
6
7 #include "quiche/quic/core/quic_constants.h"
8 #include "quiche/quic/core/quic_types.h"
9 #include "quiche/quic/platform/api/quic_socket_address.h"
10
11 namespace quic {
12
13 class RetryAlarmDelegate : public QuicAlarm::DelegateWithContext {
14 public:
RetryAlarmDelegate(QuicPathValidator * path_validator,QuicConnectionContext * context)15 explicit RetryAlarmDelegate(QuicPathValidator* path_validator,
16 QuicConnectionContext* context)
17 : QuicAlarm::DelegateWithContext(context),
18 path_validator_(path_validator) {}
19 RetryAlarmDelegate(const RetryAlarmDelegate&) = delete;
20 RetryAlarmDelegate& operator=(const RetryAlarmDelegate&) = delete;
21
OnAlarm()22 void OnAlarm() override { path_validator_->OnRetryTimeout(); }
23
24 private:
25 QuicPathValidator* path_validator_;
26 };
27
operator <<(std::ostream & os,const QuicPathValidationContext & context)28 std::ostream& operator<<(std::ostream& os,
29 const QuicPathValidationContext& context) {
30 return os << " from " << context.self_address_ << " to "
31 << context.peer_address_;
32 }
33
QuicPathValidator(QuicAlarmFactory * alarm_factory,QuicConnectionArena * arena,SendDelegate * send_delegate,QuicRandom * random,const QuicClock * clock,QuicConnectionContext * context)34 QuicPathValidator::QuicPathValidator(QuicAlarmFactory* alarm_factory,
35 QuicConnectionArena* arena,
36 SendDelegate* send_delegate,
37 QuicRandom* random, const QuicClock* clock,
38 QuicConnectionContext* context)
39 : send_delegate_(send_delegate),
40 random_(random),
41 clock_(clock),
42 retry_timer_(alarm_factory->CreateAlarm(
43 arena->New<RetryAlarmDelegate>(this, context), arena)),
44 retry_count_(0u) {}
45
OnPathResponse(const QuicPathFrameBuffer & probing_data,QuicSocketAddress self_address)46 void QuicPathValidator::OnPathResponse(const QuicPathFrameBuffer& probing_data,
47 QuicSocketAddress self_address) {
48 if (!HasPendingPathValidation()) {
49 return;
50 }
51
52 QUIC_DVLOG(1) << "Match PATH_RESPONSE received on " << self_address;
53 QUIC_BUG_IF(quic_bug_12402_1, !path_context_->self_address().IsInitialized())
54 << "Self address should have been known by now";
55 if (self_address != path_context_->self_address()) {
56 QUIC_DVLOG(1) << "Expect the response to be received on "
57 << path_context_->self_address();
58 return;
59 }
60 // This iterates at most 3 times.
61 for (auto it = probing_data_.begin(); it != probing_data_.end(); ++it) {
62 if (it->frame_buffer == probing_data) {
63 result_delegate_->OnPathValidationSuccess(std::move(path_context_),
64 it->send_time);
65 ResetPathValidation();
66 return;
67 }
68 }
69 QUIC_DVLOG(1) << "PATH_RESPONSE with payload " << probing_data.data()
70 << " doesn't match the probing data.";
71 }
72
StartPathValidation(std::unique_ptr<QuicPathValidationContext> context,std::unique_ptr<ResultDelegate> result_delegate,PathValidationReason reason)73 void QuicPathValidator::StartPathValidation(
74 std::unique_ptr<QuicPathValidationContext> context,
75 std::unique_ptr<ResultDelegate> result_delegate,
76 PathValidationReason reason) {
77 QUICHE_DCHECK(context);
78 QUIC_DLOG(INFO) << "Start validating path " << *context
79 << " via writer: " << context->WriterToUse();
80 if (path_context_ != nullptr) {
81 QUIC_BUG(quic_bug_10876_1)
82 << "There is an on-going validation on path " << *path_context_;
83 ResetPathValidation();
84 }
85
86 reason_ = reason;
87 path_context_ = std::move(context);
88 result_delegate_ = std::move(result_delegate);
89 SendPathChallengeAndSetAlarm();
90 }
91
ResetPathValidation()92 void QuicPathValidator::ResetPathValidation() {
93 path_context_ = nullptr;
94 result_delegate_ = nullptr;
95 retry_timer_->Cancel();
96 retry_count_ = 0;
97 reason_ = PathValidationReason::kReasonUnknown;
98 }
99
CancelPathValidation()100 void QuicPathValidator::CancelPathValidation() {
101 if (path_context_ == nullptr) {
102 return;
103 }
104 QUIC_DVLOG(1) << "Cancel validation on path" << *path_context_;
105 result_delegate_->OnPathValidationFailure(std::move(path_context_));
106 ResetPathValidation();
107 }
108
HasPendingPathValidation() const109 bool QuicPathValidator::HasPendingPathValidation() const {
110 return path_context_ != nullptr;
111 }
112
GetContext() const113 QuicPathValidationContext* QuicPathValidator::GetContext() const {
114 return path_context_.get();
115 }
116
ReleaseContext()117 std::unique_ptr<QuicPathValidationContext> QuicPathValidator::ReleaseContext() {
118 auto ret = std::move(path_context_);
119 ResetPathValidation();
120 return ret;
121 }
122
GeneratePathChallengePayload()123 const QuicPathFrameBuffer& QuicPathValidator::GeneratePathChallengePayload() {
124 probing_data_.emplace_back(clock_->Now());
125 random_->RandBytes(probing_data_.back().frame_buffer.data(),
126 sizeof(QuicPathFrameBuffer));
127 return probing_data_.back().frame_buffer;
128 }
129
OnRetryTimeout()130 void QuicPathValidator::OnRetryTimeout() {
131 ++retry_count_;
132 if (retry_count_ > kMaxRetryTimes) {
133 CancelPathValidation();
134 return;
135 }
136 QUIC_DVLOG(1) << "Send another PATH_CHALLENGE on path " << *path_context_;
137 SendPathChallengeAndSetAlarm();
138 }
139
SendPathChallengeAndSetAlarm()140 void QuicPathValidator::SendPathChallengeAndSetAlarm() {
141 bool should_continue = send_delegate_->SendPathChallenge(
142 GeneratePathChallengePayload(), path_context_->self_address(),
143 path_context_->peer_address(), path_context_->effective_peer_address(),
144 path_context_->WriterToUse());
145
146 if (!should_continue) {
147 // The delegate doesn't want to continue the path validation.
148 CancelPathValidation();
149 return;
150 }
151 retry_timer_->Set(send_delegate_->GetRetryTimeout(
152 path_context_->peer_address(), path_context_->WriterToUse()));
153 }
154
IsValidatingPeerAddress(const QuicSocketAddress & effective_peer_address)155 bool QuicPathValidator::IsValidatingPeerAddress(
156 const QuicSocketAddress& effective_peer_address) {
157 return path_context_ != nullptr &&
158 path_context_->effective_peer_address() == effective_peer_address;
159 }
160
MaybeWritePacketToAddress(const char * buffer,size_t buf_len,const QuicSocketAddress & peer_address)161 void QuicPathValidator::MaybeWritePacketToAddress(
162 const char* buffer, size_t buf_len, const QuicSocketAddress& peer_address) {
163 if (!HasPendingPathValidation() ||
164 path_context_->peer_address() != peer_address) {
165 return;
166 }
167 QUIC_DVLOG(1) << "Path validator is sending packet of size " << buf_len
168 << " from " << path_context_->self_address() << " to "
169 << path_context_->peer_address();
170 path_context_->WriterToUse()->WritePacket(
171 buffer, buf_len, path_context_->self_address().host(),
172 path_context_->peer_address(), nullptr, QuicPacketWriterParams());
173 }
174
175 } // namespace quic
176