Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2024 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 : #pragma once
19 :
20 : #include <cstdint>
21 : #include <cstring>
22 : #include <optional>
23 :
24 : #include <inet/InetInterface.h>
25 : #include <lib/core/CHIPError.h>
26 : #include <lib/core/PeerId.h>
27 : #include <lib/dnssd/Constants.h>
28 : #include <lib/support/BytesToHex.h>
29 : #include <lib/support/Variant.h>
30 : #include <lib/support/logging/CHIPLogging.h>
31 : #include <messaging/ReliableMessageProtocolConfig.h>
32 :
33 : namespace chip {
34 : namespace Dnssd {
35 :
36 : enum class DiscoveryFilterType : uint8_t
37 : {
38 : kNone,
39 : kShortDiscriminator,
40 : kLongDiscriminator,
41 : kVendorId,
42 : kDeviceType,
43 : kCommissioningMode,
44 : kInstanceName,
45 : kCommissioner,
46 : kCompressedFabricId,
47 : };
48 :
49 : struct DiscoveryFilter
50 : {
51 : DiscoveryFilterType type;
52 : uint64_t code = 0;
53 : const char * instanceName = nullptr;
54 0 : DiscoveryFilter() : type(DiscoveryFilterType::kNone), code(0) {}
55 10 : DiscoveryFilter(const DiscoveryFilterType newType) : type(newType) {}
56 43 : DiscoveryFilter(const DiscoveryFilterType newType, uint64_t newCode) : type(newType), code(newCode) {}
57 : DiscoveryFilter(const DiscoveryFilterType newType, const char * newInstanceName) : type(newType), instanceName(newInstanceName)
58 : {}
59 0 : bool operator==(const DiscoveryFilter & other) const
60 : {
61 0 : if (type != other.type)
62 : {
63 0 : return false;
64 : }
65 0 : if (type == DiscoveryFilterType::kInstanceName)
66 : {
67 0 : return (instanceName != nullptr) && (other.instanceName != nullptr) && (strcmp(instanceName, other.instanceName) == 0);
68 : }
69 :
70 0 : return code == other.code;
71 : }
72 : };
73 :
74 : enum class DiscoveryType
75 : {
76 : kUnknown,
77 : kOperational,
78 : kCommissionableNode,
79 : kCommissionerNode
80 : };
81 :
82 : struct CommonResolutionData
83 : {
84 : static constexpr unsigned kMaxIPAddresses = CHIP_DEVICE_CONFIG_MAX_DISCOVERED_IP_ADDRESSES;
85 :
86 : Inet::InterfaceId interfaceId;
87 :
88 : size_t numIPs = 0; // number of valid IP addresses
89 : Inet::IPAddress ipAddress[kMaxIPAddresses];
90 :
91 : uint16_t port = 0;
92 : char hostName[kHostNameMaxLength + 1] = {};
93 : bool supportsTcpClient = false;
94 : bool supportsTcpServer = false;
95 : std::optional<bool> isICDOperatingAsLIT;
96 : std::optional<System::Clock::Milliseconds32> mrpRetryIntervalIdle;
97 : std::optional<System::Clock::Milliseconds32> mrpRetryIntervalActive;
98 : std::optional<System::Clock::Milliseconds16> mrpRetryActiveThreshold;
99 :
100 1296 : CommonResolutionData() { Reset(); }
101 :
102 145 : bool IsValid() const { return !IsHost("") && (numIPs > 0) && (ipAddress[0] != chip::Inet::IPAddress::Any); }
103 :
104 0 : ReliableMessageProtocolConfig GetRemoteMRPConfig() const
105 : {
106 0 : const ReliableMessageProtocolConfig defaultConfig = GetDefaultMRPConfig();
107 0 : return ReliableMessageProtocolConfig(GetMrpRetryIntervalIdle().value_or(defaultConfig.mIdleRetransTimeout),
108 0 : GetMrpRetryIntervalActive().value_or(defaultConfig.mActiveRetransTimeout),
109 0 : GetMrpRetryActiveThreshold().value_or(defaultConfig.mActiveThresholdTime));
110 : }
111 0 : std::optional<System::Clock::Milliseconds32> GetMrpRetryIntervalIdle() const { return mrpRetryIntervalIdle; }
112 0 : std::optional<System::Clock::Milliseconds32> GetMrpRetryIntervalActive() const { return mrpRetryIntervalActive; }
113 0 : std::optional<System::Clock::Milliseconds16> GetMrpRetryActiveThreshold() const { return mrpRetryActiveThreshold; }
114 :
115 : bool IsDeviceTreatedAsSleepy(const ReliableMessageProtocolConfig * defaultMRPConfig) const
116 : {
117 : // If either session interval (Idle - SII, Active - SAI) has a value and that value is greater
118 : // than the value passed to this function, then the peer device will be treated as if it is
119 : // a Sleepy End Device (SED)
120 : return (mrpRetryIntervalIdle.has_value() && (*mrpRetryIntervalIdle > defaultMRPConfig->mIdleRetransTimeout)) ||
121 : (mrpRetryIntervalActive.has_value() && (*mrpRetryIntervalActive > defaultMRPConfig->mActiveRetransTimeout));
122 : }
123 :
124 145 : bool IsHost(const char * host) const { return strcmp(host, hostName) == 0; }
125 :
126 228 : void Reset()
127 : {
128 228 : memset(hostName, 0, sizeof(hostName));
129 228 : mrpRetryIntervalIdle = std::nullopt;
130 228 : mrpRetryIntervalActive = std::nullopt;
131 228 : mrpRetryActiveThreshold = std::nullopt;
132 228 : isICDOperatingAsLIT = std::nullopt;
133 228 : numIPs = 0;
134 228 : port = 0;
135 228 : supportsTcpClient = false;
136 228 : supportsTcpServer = false;
137 228 : interfaceId = Inet::InterfaceId::Null();
138 1368 : for (auto & addr : ipAddress)
139 : {
140 1140 : addr = chip::Inet::IPAddress::Any;
141 : }
142 228 : }
143 :
144 : void LogDetail() const
145 : {
146 : if (!IsHost(""))
147 : {
148 : ChipLogDetail(Discovery, "\tHostname: %s", hostName);
149 : }
150 : #if CHIP_DETAIL_LOGGING
151 : for (unsigned j = 0; j < numIPs; j++)
152 : {
153 : char buf[Inet::IPAddress::kMaxStringLength];
154 : char * ipAddressOut = ipAddress[j].ToString(buf);
155 : ChipLogDetail(Discovery, "\tIP Address #%d: %s", j + 1, ipAddressOut);
156 : }
157 : #endif // CHIP_DETAIL_LOGGING
158 : if (port > 0)
159 : {
160 : ChipLogDetail(Discovery, "\tPort: %u", port);
161 : }
162 : if (mrpRetryIntervalIdle.has_value())
163 : {
164 : ChipLogDetail(Discovery, "\tMrp Interval idle: %" PRIu32 " ms", mrpRetryIntervalIdle->count());
165 : }
166 : else
167 : {
168 : ChipLogDetail(Discovery, "\tMrp Interval idle: not present");
169 : }
170 : if (mrpRetryIntervalActive.has_value())
171 : {
172 : ChipLogDetail(Discovery, "\tMrp Interval active: %" PRIu32 " ms", mrpRetryIntervalActive->count());
173 : }
174 : else
175 : {
176 : ChipLogDetail(Discovery, "\tMrp Interval active: not present");
177 : }
178 : if (mrpRetryActiveThreshold.has_value())
179 : {
180 : ChipLogDetail(Discovery, "\tMrp Active Threshold: %u ms", mrpRetryActiveThreshold->count());
181 : }
182 : else
183 : {
184 : ChipLogDetail(Discovery, "\tMrp Active Threshold: not present");
185 : }
186 : ChipLogDetail(Discovery, "\tTCP Client Supported: %d", supportsTcpClient);
187 : ChipLogDetail(Discovery, "\tTCP Server Supported: %d", supportsTcpServer);
188 : if (isICDOperatingAsLIT.has_value())
189 : {
190 : ChipLogDetail(Discovery, "\tThe ICD operates in %s", *isICDOperatingAsLIT ? "LIT" : "SIT");
191 : }
192 : else
193 : {
194 : ChipLogDetail(Discovery, "\tICD: not present");
195 : }
196 : }
197 : };
198 :
199 : /// Data that is specific to Operational Discovery of nodes
200 : struct OperationalNodeData
201 : {
202 : PeerId peerId;
203 : bool hasZeroTTL;
204 0 : void Reset() { peerId = PeerId(); }
205 : };
206 :
207 : struct OperationalNodeBrowseData : public OperationalNodeData
208 : {
209 0 : OperationalNodeBrowseData() { Reset(); };
210 : void LogDetail() const
211 : {
212 : ChipLogDetail(Discovery, "Discovered Operational node:\r\n");
213 : ChipLogDetail(Discovery, "\tNode Instance: " ChipLogFormatPeerId, ChipLogValuePeerId(peerId));
214 : ChipLogDetail(Discovery, "\thasZeroTTL: %s\r\n", hasZeroTTL ? "true" : "false");
215 : }
216 : };
217 :
218 : inline constexpr size_t kMaxDeviceNameLen = 32;
219 : inline constexpr size_t kMaxRotatingIdLen = 50;
220 : inline constexpr size_t kMaxPairingInstructionLen = 128;
221 :
222 : /// Data that is specific to commisionable/commissioning node discovery
223 : struct CommissionNodeData : public CommonResolutionData
224 : {
225 : size_t rotatingIdLen = 0;
226 : uint32_t deviceType = 0;
227 : uint16_t longDiscriminator = 0;
228 : uint16_t vendorId = 0;
229 : uint16_t productId = 0;
230 : uint16_t pairingHint = 0;
231 : uint8_t commissioningMode = 0;
232 : bool supportsCommissionerGeneratedPasscode = false;
233 : uint8_t rotatingId[kMaxRotatingIdLen] = {};
234 : char instanceName[Commission::kInstanceNameMaxLength + 1] = {};
235 : char deviceName[kMaxDeviceNameLen + 1] = {};
236 : char pairingInstruction[kMaxPairingInstructionLen + 1] = {};
237 :
238 95 : CommissionNodeData() {}
239 :
240 40 : void Reset()
241 : {
242 : // Let constructor clear things as default
243 40 : this->~CommissionNodeData();
244 40 : new (this) CommissionNodeData();
245 40 : }
246 :
247 : bool IsInstanceName(const char * instance) const { return strcmp(instance, instanceName) == 0; }
248 :
249 : void LogDetail() const
250 : {
251 : ChipLogDetail(Discovery, "Discovered commissionable/commissioner node:");
252 : CommonResolutionData::LogDetail();
253 :
254 : if (rotatingIdLen > 0)
255 : {
256 : char rotatingIdString[chip::Dnssd::kMaxRotatingIdLen * 2 + 1] = "";
257 : Encoding::BytesToUppercaseHexString(rotatingId, rotatingIdLen, rotatingIdString, sizeof(rotatingIdString));
258 : ChipLogDetail(Discovery, "\tRotating ID: %s", rotatingIdString);
259 : }
260 : if (strlen(deviceName) != 0)
261 : {
262 : ChipLogDetail(Discovery, "\tDevice Name: %s", deviceName);
263 : }
264 : if (vendorId > 0)
265 : {
266 : ChipLogDetail(Discovery, "\tVendor ID: %u", vendorId);
267 : }
268 : if (productId > 0)
269 : {
270 : ChipLogDetail(Discovery, "\tProduct ID: %u", productId);
271 : }
272 : if (deviceType > 0)
273 : {
274 : ChipLogDetail(Discovery, "\tDevice Type: %" PRIu32, deviceType);
275 : }
276 : if (longDiscriminator > 0)
277 : {
278 : ChipLogDetail(Discovery, "\tLong Discriminator: %u", longDiscriminator);
279 : }
280 : if (strlen(pairingInstruction) != 0)
281 : {
282 : ChipLogDetail(Discovery, "\tPairing Instruction: %s", pairingInstruction);
283 : }
284 : if (pairingHint > 0)
285 : {
286 : ChipLogDetail(Discovery, "\tPairing Hint: %u", pairingHint);
287 : }
288 : if (!IsInstanceName(""))
289 : {
290 : ChipLogDetail(Discovery, "\tInstance Name: %s", instanceName);
291 : }
292 : ChipLogDetail(Discovery, "\tCommissioning Mode: %u", commissioningMode);
293 : ChipLogDetail(Discovery, "\tSupports Commissioner Generated Passcode: %s",
294 : supportsCommissionerGeneratedPasscode ? "true" : "false");
295 : }
296 : };
297 :
298 : struct ResolvedNodeData
299 : {
300 : CommonResolutionData resolutionData;
301 : OperationalNodeData operationalData;
302 :
303 : void LogNodeIdResolved() const
304 : {
305 : #if CHIP_PROGRESS_LOGGING
306 : // Would be nice to log the interface id, but sorting out how to do so
307 : // across our different InterfaceId implementations is a pain.
308 : ChipLogProgress(Discovery, "Node ID resolved for " ChipLogFormatPeerId, ChipLogValuePeerId(operationalData.peerId));
309 : resolutionData.LogDetail();
310 : #endif // CHIP_PROGRESS_LOGGING
311 : }
312 : };
313 :
314 : using DiscoveredNodeData = Variant<CommissionNodeData, OperationalNodeBrowseData>;
315 :
316 : /// Callbacks for discovering nodes advertising both operational and non-operational status:
317 : /// - Commissioners
318 : /// - Nodes in commissioning modes over IP (e.g. ethernet devices, devices already
319 : /// connected to thread/wifi or devices with a commissioning window open)
320 : /// - Operational nodes
321 : class DiscoverNodeDelegate
322 : {
323 : public:
324 9 : virtual ~DiscoverNodeDelegate() = default;
325 :
326 : /// Called within the CHIP event loop once a node is discovered.
327 : ///
328 : /// May be called multiple times as more nodes send their answer to a
329 : /// multicast discovery query
330 : virtual void OnNodeDiscovered(const DiscoveredNodeData & nodeData) = 0;
331 : };
332 :
333 : } // namespace Dnssd
334 : } // namespace chip
|