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