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