Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #include "ActiveResolveAttempts.h"
19 :
20 : #include <lib/dnssd/minimal_mdns/MinMdnsConfig.h>
21 : #include <lib/support/logging/CHIPLogging.h>
22 :
23 : using namespace chip;
24 :
25 : namespace mdns {
26 : namespace Minimal {
27 :
28 : constexpr chip::System::Clock::Timeout ActiveResolveAttempts::kMaxRetryDelay;
29 :
30 109 : void ActiveResolveAttempts::Reset()
31 :
32 : {
33 545 : for (auto & item : mRetryQueue)
34 : {
35 436 : item.attempt.Clear();
36 : }
37 109 : }
38 :
39 4 : void ActiveResolveAttempts::Complete(const PeerId & peerId)
40 : {
41 6 : for (auto & item : mRetryQueue)
42 : {
43 6 : if (item.attempt.Matches(peerId))
44 : {
45 4 : item.attempt.Clear();
46 4 : return;
47 : }
48 : }
49 :
50 : #if CHIP_MINMDNS_HIGH_VERBOSITY
51 : // This may happen during boot time adverisements: nodes come online
52 : // and advertise their IP without any explicit queries for them
53 : ChipLogProgress(Discovery, "Discovered node without a pending query");
54 : #endif
55 : }
56 :
57 12 : bool ActiveResolveAttempts::HasBrowseFor(chip::Dnssd::DiscoveryType type) const
58 : {
59 60 : for (auto & item : mRetryQueue)
60 : {
61 48 : if (!item.attempt.IsBrowse())
62 : {
63 48 : continue;
64 : }
65 :
66 0 : if (item.attempt.BrowseData().type == type)
67 : {
68 0 : return true;
69 : }
70 : }
71 :
72 12 : return false;
73 : }
74 :
75 12 : void ActiveResolveAttempts::CompleteIpResolution(SerializedQNameIterator targetHostName)
76 : {
77 60 : for (auto & item : mRetryQueue)
78 : {
79 48 : if (item.attempt.MatchesIpResolve(targetHostName))
80 : {
81 0 : item.attempt.Clear();
82 0 : return;
83 : }
84 : }
85 : }
86 :
87 2 : CHIP_ERROR ActiveResolveAttempts::CompleteAllBrowses()
88 : {
89 10 : for (auto & item : mRetryQueue)
90 : {
91 8 : if (item.attempt.IsBrowse())
92 : {
93 2 : item.attempt.Clear();
94 : }
95 : }
96 :
97 2 : return CHIP_NO_ERROR;
98 : }
99 :
100 0 : void ActiveResolveAttempts::NodeIdResolutionNoLongerNeeded(const PeerId & peerId)
101 : {
102 0 : for (auto & item : mRetryQueue)
103 : {
104 0 : if (item.attempt.Matches(peerId))
105 : {
106 0 : item.attempt.ConsumerRemoved();
107 0 : return;
108 : }
109 : }
110 : }
111 :
112 13 : void ActiveResolveAttempts::MarkPending(const chip::PeerId & peerId)
113 : {
114 13 : MarkPending(ScheduledAttempt(peerId, /* firstSend */ true));
115 13 : }
116 :
117 4 : void ActiveResolveAttempts::MarkPending(const chip::Dnssd::DiscoveryFilter & filter, const chip::Dnssd::DiscoveryType type)
118 : {
119 4 : MarkPending(ScheduledAttempt(filter, type, /* firstSend */ true));
120 4 : }
121 :
122 0 : void ActiveResolveAttempts::MarkPending(ScheduledAttempt::IpResolve && resolve)
123 : {
124 0 : MarkPending(ScheduledAttempt(std::move(resolve), /* firstSend */ true));
125 0 : }
126 :
127 17 : void ActiveResolveAttempts::MarkPending(ScheduledAttempt && attempt)
128 : {
129 : // Strategy when picking the peer id to use:
130 : // 1 if a matching peer id is already found, use that one
131 : // 2 if an 'unused' entry is found, use that
132 : // 3 otherwise expire the one with the largest nextRetryDelay
133 : // or if equal nextRetryDelay, pick the one with the oldest
134 : // queryDueTime
135 :
136 17 : RetryEntry * entryToUse = &mRetryQueue[0];
137 :
138 62 : for (size_t i = 1; i < kRetryQueueSize; i++)
139 : {
140 47 : if (entryToUse->attempt.Matches(attempt))
141 : {
142 2 : break; // best match possible
143 : }
144 :
145 45 : RetryEntry * entry = mRetryQueue + i;
146 :
147 : // Rule 1: attempt match always matches
148 45 : if (entry->attempt.Matches(attempt))
149 : {
150 0 : entryToUse = entry;
151 0 : continue;
152 : }
153 :
154 : // Rule 2: select unused entries
155 45 : if (!entryToUse->attempt.IsEmpty() && entry->attempt.IsEmpty())
156 : {
157 6 : entryToUse = entry;
158 6 : continue;
159 : }
160 39 : if (entryToUse->attempt.IsEmpty())
161 : {
162 32 : continue;
163 : }
164 :
165 : // Rule 3: both choices are used (have a defined node id):
166 : // - try to find the one with the largest next delay (oldest request)
167 : // - on same delay, use queryDueTime to determine the oldest request
168 : // (the one with the smallest due time was issued the longest time
169 : // ago)
170 7 : if (entry->nextRetryDelay > entryToUse->nextRetryDelay)
171 : {
172 0 : entryToUse = entry;
173 : }
174 7 : else if ((entry->nextRetryDelay == entryToUse->nextRetryDelay) && (entry->queryDueTime < entryToUse->queryDueTime))
175 : {
176 0 : entryToUse = entry;
177 : }
178 : }
179 :
180 17 : if ((!entryToUse->attempt.IsEmpty()) && (!entryToUse->attempt.Matches(attempt)))
181 : {
182 : // TODO: node was evicted here, if/when resolution failures are
183 : // supported this could be a place for error callbacks
184 : //
185 : // Note however that this is NOT an actual 'timeout' it is showing
186 : // a burst of lookups for which we cannot maintain state. A reply may
187 : // still be received for this peer id (query was already sent on the
188 : // network)
189 1 : ChipLogError(Discovery, "Re-using pending resolve entry before reply was received.");
190 : }
191 :
192 17 : attempt.WillCoalesceWith(entryToUse->attempt);
193 17 : entryToUse->attempt = attempt;
194 17 : entryToUse->queryDueTime = mClock->GetMonotonicTimestamp();
195 17 : entryToUse->nextRetryDelay = System::Clock::Seconds16(1);
196 17 : }
197 :
198 64 : std::optional<System::Clock::Timeout> ActiveResolveAttempts::GetTimeUntilNextExpectedResponse() const
199 : {
200 64 : std::optional<System::Clock::Timeout> minDelay = std::nullopt;
201 :
202 64 : chip::System::Clock::Timestamp now = mClock->GetMonotonicTimestamp();
203 :
204 274 : for (auto & entry : mRetryQueue)
205 : {
206 222 : if (entry.attempt.IsEmpty())
207 : {
208 139 : continue;
209 : }
210 :
211 83 : if (now >= entry.queryDueTime)
212 : {
213 : // found an entry that needs processing right now
214 12 : return std::make_optional<System::Clock::Timeout>(0);
215 : }
216 :
217 71 : System::Clock::Timeout entryDelay = entry.queryDueTime - now;
218 71 : if (!minDelay.has_value() || (*minDelay > entryDelay))
219 : {
220 42 : minDelay.emplace(entryDelay);
221 : }
222 : }
223 :
224 52 : return minDelay;
225 : }
226 :
227 85 : std::optional<ActiveResolveAttempts::ScheduledAttempt> ActiveResolveAttempts::NextScheduled()
228 : {
229 85 : chip::System::Clock::Timestamp now = mClock->GetMonotonicTimestamp();
230 :
231 289 : for (auto & entry : mRetryQueue)
232 : {
233 247 : if (entry.attempt.IsEmpty())
234 : {
235 204 : continue; // not a pending item
236 : }
237 :
238 159 : if (entry.queryDueTime > now)
239 : {
240 112 : continue; // not yet due
241 : }
242 :
243 47 : if (entry.nextRetryDelay > kMaxRetryDelay)
244 : {
245 4 : ChipLogError(Discovery, "Timeout waiting for mDNS resolution.");
246 4 : entry.attempt.Clear();
247 4 : continue;
248 : }
249 :
250 43 : entry.queryDueTime = now + entry.nextRetryDelay;
251 43 : entry.nextRetryDelay *= 2;
252 :
253 43 : std::optional<ScheduledAttempt> attempt = std::make_optional(entry.attempt);
254 43 : entry.attempt.firstSend = false;
255 :
256 43 : return attempt;
257 43 : }
258 :
259 42 : return std::nullopt;
260 : }
261 :
262 0 : bool ActiveResolveAttempts::ShouldResolveIpAddress(PeerId peerId) const
263 : {
264 0 : for (auto & item : mRetryQueue)
265 : {
266 0 : if (item.attempt.IsEmpty())
267 : {
268 0 : continue;
269 : }
270 0 : if (item.attempt.IsBrowse())
271 : {
272 0 : return true;
273 : }
274 :
275 0 : if (item.attempt.IsResolve())
276 : {
277 0 : auto & data = item.attempt.ResolveData();
278 0 : if (data.peerId == peerId)
279 : {
280 0 : return true;
281 : }
282 : }
283 : }
284 :
285 0 : return false;
286 : }
287 :
288 0 : bool ActiveResolveAttempts::IsWaitingForIpResolutionFor(SerializedQNameIterator hostName) const
289 : {
290 0 : for (auto & entry : mRetryQueue)
291 : {
292 0 : if (entry.attempt.IsEmpty())
293 : {
294 0 : continue; // not a pending item
295 : }
296 :
297 0 : if (!entry.attempt.IsIpResolve())
298 : {
299 0 : continue;
300 : }
301 :
302 0 : if (hostName == entry.attempt.IpResolveData().hostName.Content())
303 : {
304 0 : return true;
305 : }
306 : }
307 :
308 0 : return false;
309 : }
310 :
311 : } // namespace Minimal
312 : } // namespace mdns
|