forked from project-chip/connectedhomeip
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathActiveResolveAttempts.h
323 lines (276 loc) · 12 KB
/
ActiveResolveAttempts.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/*
*
* Copyright (c) 2021 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <cstddef>
#include <cstdint>
#include <lib/core/Optional.h>
#include <lib/core/PeerId.h>
#include <lib/dnssd/Resolver.h>
#include <lib/dnssd/minimal_mdns/core/HeapQName.h>
#include <lib/support/Variant.h>
#include <system/SystemClock.h>
namespace mdns {
namespace Minimal {
/// Keeps track of active resolve attempts
///
/// Maintains a list of 'pending mdns resolve queries' and provides operations
/// for:
/// - add/remove to the list
/// - figuring out a 'next query time' for items in the list
/// - iterating through the 'schedule now' items of the list
///
class ActiveResolveAttempts
{
public:
static constexpr size_t kRetryQueueSize = 4;
static constexpr chip::System::Clock::Timeout kMaxRetryDelay = chip::System::Clock::Seconds16(16);
struct ScheduledAttempt
{
struct Browse
{
Browse(const chip::Dnssd::DiscoveryFilter discoveryFilter, const chip::Dnssd::DiscoveryType discoveryType) :
filter(discoveryFilter), type(discoveryType)
{}
chip::Dnssd::DiscoveryFilter filter;
chip::Dnssd::DiscoveryType type;
};
struct Resolve
{
chip::PeerId peerId;
uint32_t consumerCount = 0;
Resolve(chip::PeerId id) : peerId(id) {}
};
struct IpResolve
{
HeapQName hostName;
IpResolve(HeapQName && host) : hostName(std::move(host)) {}
};
ScheduledAttempt()
{
static_assert(sizeof(Resolve) <= sizeof(Browse) || sizeof(Resolve) <= sizeof(HeapQName),
"Figure out where to put the Resolve counter so that Resolve is not making ScheduledAttempt bigger than "
"it has to be anyway to handle the other attempt types.");
}
ScheduledAttempt(const chip::PeerId & peer, bool first) :
resolveData(chip::InPlaceTemplateType<Resolve>(), peer), firstSend(first)
{}
ScheduledAttempt(const chip::Dnssd::DiscoveryFilter discoveryFilter, const chip::Dnssd::DiscoveryType type, bool first) :
resolveData(chip::InPlaceTemplateType<Browse>(), discoveryFilter, type), firstSend(first)
{}
ScheduledAttempt(IpResolve && ipResolve, bool first) :
resolveData(chip::InPlaceTemplateType<IpResolve>(), ipResolve), firstSend(first)
{}
bool operator==(const ScheduledAttempt & other) const { return Matches(other) && other.firstSend == firstSend; }
bool Matches(const ScheduledAttempt & other) const
{
if (!resolveData.Valid())
{
return !other.resolveData.Valid();
}
if (resolveData.Is<Browse>())
{
if (!other.resolveData.Is<Browse>())
{
return false;
}
auto & a = resolveData.Get<Browse>();
auto & b = other.resolveData.Get<Browse>();
return (a.filter == b.filter && a.type == b.type);
}
if (resolveData.Is<Resolve>())
{
if (!other.resolveData.Is<Resolve>())
{
return false;
}
auto & a = resolveData.Get<Resolve>();
auto & b = other.resolveData.Get<Resolve>();
return a.peerId == b.peerId;
}
if (resolveData.Is<IpResolve>())
{
if (!other.resolveData.Is<IpResolve>())
{
return false;
}
auto & a = resolveData.Get<IpResolve>();
auto & b = other.resolveData.Get<IpResolve>();
return a.hostName == b.hostName;
}
return false;
}
bool MatchesIpResolve(SerializedQNameIterator hostName) const
{
return resolveData.Is<IpResolve>() && (hostName == resolveData.Get<IpResolve>().hostName.Content());
}
bool Matches(const chip::PeerId & peer) const
{
return resolveData.Is<Resolve>() && (resolveData.Get<Resolve>().peerId == peer);
}
bool Matches(const chip::Dnssd::DiscoveredNodeData & data, chip::Dnssd::DiscoveryType type) const
{
if (!resolveData.Is<Browse>())
{
return false;
}
auto & browse = resolveData.Get<Browse>();
if (browse.type != type)
{
return false;
}
switch (browse.filter.type)
{
case chip::Dnssd::DiscoveryFilterType::kNone:
return true;
case chip::Dnssd::DiscoveryFilterType::kShortDiscriminator:
return browse.filter.code == static_cast<uint64_t>((data.commissionData.longDiscriminator >> 8) & 0x0F);
case chip::Dnssd::DiscoveryFilterType::kLongDiscriminator:
return browse.filter.code == data.commissionData.longDiscriminator;
case chip::Dnssd::DiscoveryFilterType::kVendorId:
return browse.filter.code == data.commissionData.vendorId;
case chip::Dnssd::DiscoveryFilterType::kDeviceType:
return browse.filter.code == data.commissionData.deviceType;
case chip::Dnssd::DiscoveryFilterType::kCommissioningMode:
return browse.filter.code == data.commissionData.commissioningMode;
case chip::Dnssd::DiscoveryFilterType::kInstanceName:
return strncmp(browse.filter.instanceName, data.commissionData.instanceName,
chip::Dnssd::Commission::kInstanceNameMaxLength + 1) == 0;
case chip::Dnssd::DiscoveryFilterType::kCommissioner:
case chip::Dnssd::DiscoveryFilterType::kCompressedFabricId:
default:
// These are for other discovery types.
return false;
}
}
bool IsEmpty() const { return !resolveData.Valid(); }
bool IsResolve() const { return resolveData.Is<Resolve>(); }
bool IsBrowse() const { return resolveData.Is<Browse>(); }
bool IsIpResolve() const { return resolveData.Is<IpResolve>(); }
void Clear() { resolveData = DataType(); }
// Called when this scheduled attempt will replace an existing scheduled
// attempt, because either they match or we have run out of attempt
// slots. When this happens, we want to propagate the consumer count
// from the existing attempt to the new one, if they're matching resolve
// attempts.
void WillCoalesceWith(const ScheduledAttempt & existing)
{
if (!IsResolve())
{
// Consumer count is only tracked for resolve requests
return;
}
if (!existing.Matches(*this))
{
// Out of attempt slots, so just dropping the existing attempt.
return;
}
// Adding another consumer to the same query. Propagate along the
// consumer count to our new attempt, which will replace the
// existing one.
resolveData.Get<Resolve>().consumerCount = existing.resolveData.Get<Resolve>().consumerCount + 1;
}
void ConsumerRemoved()
{
if (!IsResolve())
{
return;
}
auto & count = resolveData.Get<Resolve>().consumerCount;
if (count > 0)
{
--count;
}
if (count == 0)
{
Clear();
}
}
const Browse & BrowseData() const { return resolveData.Get<Browse>(); }
const Resolve & ResolveData() const { return resolveData.Get<Resolve>(); }
const IpResolve & IpResolveData() const { return resolveData.Get<IpResolve>(); }
using DataType = chip::Variant<Browse, Resolve, IpResolve>;
DataType resolveData;
// First packet send is marked separately: minMDNS logic can choose
// to first send a unicast query followed by a multicast one.
bool firstSend = false;
};
ActiveResolveAttempts(chip::System::Clock::ClockBase * clock) : mClock(clock) { Reset(); }
/// Clear out the internal queue
void Reset();
/// Mark a resolution as a success, removing it from the internal list
void Complete(const chip::PeerId & peerId);
void CompleteCommissioner(const chip::Dnssd::DiscoveredNodeData & data);
void CompleteCommissionable(const chip::Dnssd::DiscoveredNodeData & data);
void CompleteIpResolution(SerializedQNameIterator targetHostName);
/// Mark all browse-type scheduled attemptes as a success, removing them
/// from the internal list.
CHIP_ERROR CompleteAllBrowses();
/// Note that resolve attempts for the given peer id now have one fewer
/// consumer.
void NodeIdResolutionNoLongerNeeded(const chip::PeerId & peerId);
/// Mark that a resolution is pending, adding it to the internal list
///
/// Once this complete, this peer id will be returned immediately
/// by NextScheduled (potentially with others as well)
void MarkPending(const chip::PeerId & peerId);
void MarkPending(const chip::Dnssd::DiscoveryFilter & filter, const chip::Dnssd::DiscoveryType type);
void MarkPending(ScheduledAttempt::IpResolve && resolve);
// Get minimum time until the next pending reply is required.
//
// Returns missing if no actively tracked elements exist.
chip::Optional<chip::System::Clock::Timeout> GetTimeUntilNextExpectedResponse() const;
// Get the peer Id that needs scheduling for a query
//
// Assumes that the resolution is being sent and will apply internal
// query logic. This means:
// - internal tracking of 'next due time' will updated as 'request sent
// now'
// - there is NO sorting implied by this call. Returned value will be
// any peer that needs a new request sent
chip::Optional<ScheduledAttempt> NextScheduled();
/// Check if any of the pending queries are for the given host name for
/// IP resolution.
bool IsWaitingForIpResolutionFor(SerializedQNameIterator hostName) const;
/// Determines if address resolution for the given peer ID is required
///
/// IP Addresses are required for active operational discovery of specific peers
/// or if an active browse is being performed.
bool ShouldResolveIpAddress(chip::PeerId peerId) const;
/// Check if a browse operation is active for the given discovery type
bool HasBrowseFor(chip::Dnssd::DiscoveryType type) const;
private:
struct RetryEntry
{
ScheduledAttempt attempt;
// When a reply is expected for this item
chip::System::Clock::Timestamp queryDueTime;
// Next expected delay for sending if reply is not reached by
// 'queryDueTimeMs'
//
// Based on RFC 6762 expectations are:
// - the interval between the first two queries MUST be at least
// one second
// - the intervals between successive queries MUST increase by at
// least a factor of two
chip::System::Clock::Timeout nextRetryDelay = chip::System::Clock::Seconds16(1);
};
void MarkPending(ScheduledAttempt && attempt);
chip::System::Clock::ClockBase * mClock;
RetryEntry mRetryQueue[kRetryQueueSize];
};
} // namespace Minimal
} // namespace mdns