Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021 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 :
19 : /**
20 : * @file
21 : * Implementation of SetUp Code Pairer, a class that parses a given
22 : * setup code and uses the extracted informations to discover and
23 : * filter commissionables nodes, before initiating the pairing process.
24 : *
25 : */
26 :
27 : #include <controller/SetUpCodePairer.h>
28 :
29 : #include <controller/CHIPDeviceController.h>
30 : #include <lib/dnssd/Resolver.h>
31 : #include <lib/support/CodeUtils.h>
32 : #include <memory>
33 : #include <system/SystemClock.h>
34 : #include <tracing/metric_event.h>
35 : #include <vector>
36 :
37 : constexpr uint32_t kDeviceDiscoveredTimeout = CHIP_CONFIG_SETUP_CODE_PAIRER_DISCOVERY_TIMEOUT_SECS * chip::kMillisecondsPerSecond;
38 :
39 : using namespace chip::Tracing;
40 :
41 : namespace chip {
42 : namespace Controller {
43 :
44 0 : CHIP_ERROR SetUpCodePairer::PairDevice(NodeId remoteId, const char * setUpCode, SetupCodePairerBehaviour commission,
45 : DiscoveryType discoveryType, Optional<Dnssd::CommonResolutionData> resolutionData)
46 : {
47 0 : VerifyOrReturnErrorWithMetric(kMetricSetupCodePairerPairDevice, mSystemLayer != nullptr, CHIP_ERROR_INCORRECT_STATE);
48 0 : VerifyOrReturnErrorWithMetric(kMetricSetupCodePairerPairDevice, remoteId != kUndefinedNodeId, CHIP_ERROR_INVALID_ARGUMENT);
49 :
50 0 : std::vector<SetupPayload> payloads;
51 0 : ReturnErrorOnFailure(SetupPayload::FromStringRepresentation(setUpCode, payloads));
52 :
53 : // If the caller has provided a specific single resolution data, and we were
54 : // only looking for one commissionee, and the caller says that the provided
55 : // data matches that one commissionee, just go ahead and use the provided data.
56 : //
57 : // If we were looking for more than one device (i.e. if either of the
58 : // payload arrays involved does not have length 1), we can't make use of the
59 : // incoming resolution data, since it does not contain the long
60 : // discriminator of the thing that was discovered, and therefore we can't
61 : // tell which setup passcode to use for it.
62 0 : if (resolutionData.HasValue() && payloads.size() == 1 && mSetupPayloads.size() == 1)
63 : {
64 0 : VerifyOrReturnErrorWithMetric(kMetricSetupCodePairerPairDevice, discoveryType != DiscoveryType::kAll,
65 : CHIP_ERROR_INVALID_ARGUMENT);
66 0 : if (mRemoteId == remoteId && mSetupPayloads[0].setUpPINCode == payloads[0].setUpPINCode && mConnectionType == commission &&
67 0 : mDiscoveryType == discoveryType)
68 : {
69 : // Not passing a discriminator is ok, since we have only one payload.
70 0 : NotifyCommissionableDeviceDiscovered(resolutionData.Value(), /* matchedLongDiscriminator = */ std::nullopt);
71 0 : return CHIP_NO_ERROR;
72 : }
73 : }
74 :
75 0 : ResetDiscoveryState();
76 :
77 0 : mConnectionType = commission;
78 0 : mDiscoveryType = discoveryType;
79 0 : mRemoteId = remoteId;
80 0 : mSetupPayloads = std::move(payloads);
81 :
82 0 : if (resolutionData.HasValue() && mSetupPayloads.size() == 1)
83 : {
84 : // No need to pass in a discriminator if we have only one payload, which
85 : // is good because we don't have a full discriminator here anyway.
86 0 : NotifyCommissionableDeviceDiscovered(resolutionData.Value(), /* matchedLongDiscriminator = */ std::nullopt);
87 0 : return CHIP_NO_ERROR;
88 : }
89 :
90 0 : ReturnErrorOnFailureWithMetric(kMetricSetupCodePairerPairDevice, Connect());
91 : auto errorCode =
92 0 : mSystemLayer->StartTimer(System::Clock::Milliseconds32(kDeviceDiscoveredTimeout), OnDeviceDiscoveredTimeoutCallback, this);
93 0 : if (CHIP_NO_ERROR == errorCode)
94 : {
95 : MATTER_LOG_METRIC_BEGIN(kMetricSetupCodePairerPairDevice);
96 : }
97 0 : return errorCode;
98 0 : }
99 :
100 0 : CHIP_ERROR SetUpCodePairer::Connect()
101 : {
102 0 : if (mDiscoveryType == DiscoveryType::kAll)
103 : {
104 0 : if (ShouldDiscoverUsing(RendezvousInformationFlag::kBLE))
105 : {
106 0 : CHIP_ERROR err = StartDiscoveryOverBLE();
107 0 : if ((CHIP_ERROR_NOT_IMPLEMENTED == err) || (CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE == err))
108 : {
109 0 : ChipLogProgress(Controller,
110 : "Skipping commissionable node discovery over BLE since not supported by the controller!");
111 : }
112 0 : else if (err != CHIP_NO_ERROR)
113 : {
114 0 : ChipLogError(Controller, "Failed to start commissionable node discovery over BLE: %" CHIP_ERROR_FORMAT,
115 : err.Format());
116 : }
117 : }
118 0 : if (ShouldDiscoverUsing(RendezvousInformationFlag::kWiFiPAF))
119 : {
120 0 : CHIP_ERROR err = StartDiscoveryOverWiFiPAF();
121 0 : if ((CHIP_ERROR_NOT_IMPLEMENTED == err) || (CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE == err))
122 : {
123 0 : ChipLogProgress(Controller,
124 : "Skipping commissionable node discovery over Wi-Fi PAF since not supported by the controller!");
125 : }
126 0 : else if (err != CHIP_NO_ERROR)
127 : {
128 0 : ChipLogError(Controller, "Failed to start commissionable node discovery over Wi-Fi PAF: %" CHIP_ERROR_FORMAT,
129 : err.Format());
130 : }
131 : }
132 : }
133 :
134 : // We always want to search on network because any node that has already been commissioned will use on-network regardless of the
135 : // QR code flag.
136 0 : CHIP_ERROR err = StartDiscoveryOverDNSSD();
137 0 : if (err != CHIP_NO_ERROR)
138 : {
139 0 : ChipLogError(Controller, "Failed to start discovery over DNS-SD: %" CHIP_ERROR_FORMAT, err.Format());
140 : }
141 0 : return err;
142 : }
143 :
144 0 : CHIP_ERROR SetUpCodePairer::StartDiscoveryOverBLE()
145 : {
146 : #if CONFIG_NETWORK_LAYER_BLE
147 : #if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
148 : VerifyOrReturnError(mCommissioner != nullptr, CHIP_ERROR_INCORRECT_STATE);
149 : mCommissioner->ConnectBleTransportToSelf();
150 : #endif // CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
151 0 : VerifyOrReturnError(mBleLayer != nullptr, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE);
152 :
153 0 : ChipLogProgress(Controller, "Starting commissioning discovery over BLE");
154 :
155 : // Handle possibly-sync callbacks.
156 0 : mWaitingForDiscovery[kBLETransport] = true;
157 : CHIP_ERROR err;
158 : // Not all BLE backends support the new NewBleConnectionByDiscriminators
159 : // API, so use the old one when we can (i.e. when we only have one setup
160 : // payload), to avoid breaking existing API consumers.
161 0 : if (mSetupPayloads.size() == 1)
162 : {
163 0 : err = mBleLayer->NewBleConnectionByDiscriminator(mSetupPayloads[0].discriminator, this, OnDiscoveredDeviceOverBleSuccess,
164 : OnDiscoveredDeviceOverBleError);
165 : }
166 : else
167 : {
168 0 : std::vector<SetupDiscriminator> discriminators;
169 0 : discriminators.reserve(mSetupPayloads.size());
170 0 : for (auto & payload : mSetupPayloads)
171 : {
172 0 : discriminators.emplace_back(payload.discriminator);
173 : }
174 0 : err = mBleLayer->NewBleConnectionByDiscriminators(Span(discriminators.data(), discriminators.size()), this,
175 : OnDiscoveredDeviceWithDiscriminatorOverBleSuccess,
176 : OnDiscoveredDeviceOverBleError);
177 0 : }
178 0 : if (err != CHIP_NO_ERROR)
179 : {
180 0 : mWaitingForDiscovery[kBLETransport] = false;
181 : }
182 0 : return err;
183 : #else
184 : return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
185 : #endif // CONFIG_NETWORK_LAYER_BLE
186 : }
187 :
188 0 : CHIP_ERROR SetUpCodePairer::StopDiscoveryOverBLE()
189 : {
190 : // Make sure to not call CancelBleIncompleteConnection unless we are in fact
191 : // waiting on BLE discovery. It will cancel connections that are in fact
192 : // completed. In particular, if we just established PASE over BLE calling
193 : // CancelBleIncompleteConnection here unconditionally would cancel the BLE
194 : // connection underlying the PASE session. So make sure to only call
195 : // CancelBleIncompleteConnection if we're still waiting to hear back on the
196 : // BLE discovery bits.
197 0 : if (!mWaitingForDiscovery[kBLETransport])
198 : {
199 0 : return CHIP_NO_ERROR;
200 : }
201 :
202 0 : mWaitingForDiscovery[kBLETransport] = false;
203 : #if CONFIG_NETWORK_LAYER_BLE
204 0 : VerifyOrReturnError(mBleLayer != nullptr, CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE);
205 0 : ChipLogDetail(Controller, "Stopping commissioning discovery over BLE");
206 0 : return mBleLayer->CancelBleIncompleteConnection();
207 : #else
208 : return CHIP_NO_ERROR;
209 : #endif // CONFIG_NETWORK_LAYER_BLE
210 : }
211 :
212 0 : CHIP_ERROR SetUpCodePairer::StartDiscoveryOverDNSSD()
213 : {
214 0 : ChipLogProgress(Controller, "Starting commissioning discovery over DNS-SD");
215 :
216 0 : Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kNone);
217 0 : if (mSetupPayloads.size() == 1)
218 : {
219 0 : auto & discriminator = mSetupPayloads[0].discriminator;
220 0 : if (discriminator.IsShortDiscriminator())
221 : {
222 0 : filter.type = Dnssd::DiscoveryFilterType::kShortDiscriminator;
223 0 : filter.code = discriminator.GetShortValue();
224 : }
225 : else
226 : {
227 0 : filter.type = Dnssd::DiscoveryFilterType::kLongDiscriminator;
228 0 : filter.code = discriminator.GetLongValue();
229 : }
230 : }
231 :
232 : // In theory we could try to filter on the vendor ID if it's the same across all the setup
233 : // payloads, but DNS-SD advertisements are not required to include the Vendor ID subtype, so in
234 : // practice that's not doable.
235 :
236 : // Handle possibly-sync callbacks.
237 0 : mWaitingForDiscovery[kIPTransport] = true;
238 0 : CHIP_ERROR err = mCommissioner->DiscoverCommissionableNodes(filter);
239 0 : if (err != CHIP_NO_ERROR)
240 : {
241 0 : mWaitingForDiscovery[kIPTransport] = false;
242 : }
243 0 : return err;
244 : }
245 :
246 0 : CHIP_ERROR SetUpCodePairer::StopDiscoveryOverDNSSD()
247 : {
248 0 : ChipLogDetail(Controller, "Stopping commissioning discovery over DNS-SD");
249 :
250 0 : mWaitingForDiscovery[kIPTransport] = false;
251 :
252 0 : mCommissioner->StopCommissionableDiscovery();
253 0 : return CHIP_NO_ERROR;
254 : }
255 :
256 0 : CHIP_ERROR SetUpCodePairer::StartDiscoveryOverWiFiPAF()
257 : {
258 : #if CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF
259 0 : if (mSetupPayloads.size() != 1)
260 : {
261 0 : ChipLogError(Controller, "WiFiPAF commissioning does not support concatenated QR codes yet.");
262 0 : return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
263 : }
264 :
265 0 : auto & payload = mSetupPayloads[0];
266 :
267 0 : ChipLogProgress(Controller, "Starting commissioning discovery over WiFiPAF");
268 0 : VerifyOrReturnError(mCommissioner != nullptr, CHIP_ERROR_INCORRECT_STATE);
269 :
270 0 : const SetupDiscriminator connDiscriminator(payload.discriminator);
271 0 : VerifyOrReturnValue(!connDiscriminator.IsShortDiscriminator(), CHIP_ERROR_INVALID_ARGUMENT,
272 : ChipLogError(Controller, "Error, Long discriminator is required"));
273 0 : uint16_t discriminator = connDiscriminator.GetLongValue();
274 0 : WiFiPAF::WiFiPAFSession sessionInfo = { .role = WiFiPAF::WiFiPafRole::kWiFiPafRole_Subscriber,
275 0 : .nodeId = mRemoteId,
276 0 : .discriminator = discriminator };
277 0 : ReturnErrorOnFailure(
278 : DeviceLayer::ConnectivityMgr().GetWiFiPAF()->AddPafSession(WiFiPAF::PafInfoAccess::kAccNodeInfo, sessionInfo));
279 :
280 0 : mWaitingForDiscovery[kWiFiPAFTransport] = true;
281 0 : CHIP_ERROR err = DeviceLayer::ConnectivityMgr().WiFiPAFSubscribe(discriminator, (void *) this, OnWiFiPAFSubscribeComplete,
282 : OnWiFiPAFSubscribeError);
283 0 : if (err != CHIP_NO_ERROR)
284 : {
285 0 : ChipLogError(Controller, "Commissioning discovery over WiFiPAF failed, err = %" CHIP_ERROR_FORMAT, err.Format());
286 0 : mWaitingForDiscovery[kWiFiPAFTransport] = false;
287 : }
288 0 : return err;
289 : #else
290 : return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
291 : #endif // CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF
292 : }
293 :
294 0 : CHIP_ERROR SetUpCodePairer::StopDiscoveryOverWiFiPAF()
295 : {
296 0 : mWaitingForDiscovery[kWiFiPAFTransport] = false;
297 : #if CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF
298 0 : DeviceLayer::ConnectivityMgr().WiFiPAFCancelIncompleteSubscribe();
299 : #endif
300 0 : return CHIP_NO_ERROR;
301 : }
302 :
303 0 : bool SetUpCodePairer::ConnectToDiscoveredDevice()
304 : {
305 0 : if (mWaitingForPASE)
306 : {
307 : // Nothing to do. Just wait until we either succeed or fail at that
308 : // PASE session establishment.
309 0 : return false;
310 : }
311 :
312 0 : while (!mDiscoveredParameters.empty())
313 : {
314 : // Grab the first element from the queue and try connecting to it.
315 : // Remove it from the queue before we try to connect, in case the
316 : // connection attempt fails and calls right back into us to try the next
317 : // thing.
318 0 : SetUpCodePairerParameters params(mDiscoveredParameters.front());
319 0 : mDiscoveredParameters.pop_front();
320 :
321 0 : if (params.mLongDiscriminator)
322 : {
323 0 : auto longDiscriminator = *params.mLongDiscriminator;
324 : // Look for a matching setup passcode.
325 0 : bool found = false;
326 0 : for (auto & payload : mSetupPayloads)
327 : {
328 0 : if (payload.discriminator.MatchesLongDiscriminator(longDiscriminator))
329 : {
330 0 : params.SetSetupPINCode(payload.setUpPINCode);
331 0 : found = true;
332 0 : break;
333 : }
334 : }
335 0 : if (!found)
336 : {
337 0 : ChipLogError(Controller, "SetUpCodePairer: Discovered discriminator %u does not match any of our setup payloads",
338 : longDiscriminator);
339 : // Move on to the the next discovered params; nothing we can do here.
340 0 : continue;
341 : }
342 : }
343 : else
344 : {
345 : // No discriminator known for this discovered device. This can work if we have only one
346 : // setup payload, but otherwise we have no idea what setup passcode to use for it.
347 0 : if (mSetupPayloads.size() == 1)
348 : {
349 0 : params.SetSetupPINCode(mSetupPayloads[0].setUpPINCode);
350 : }
351 : else
352 : {
353 0 : ChipLogError(Controller,
354 : "SetUpCodePairer: Unable to handle discovered parameters with no discriminator, because it has %u "
355 : "possible payloads",
356 : static_cast<unsigned>(mSetupPayloads.size()));
357 0 : continue;
358 : }
359 : }
360 :
361 : #if CHIP_PROGRESS_LOGGING
362 : char buf[Transport::PeerAddress::kMaxToStringSize];
363 0 : params.GetPeerAddress().ToString(buf);
364 0 : ChipLogProgress(Controller, "Attempting PASE connection to %s", buf);
365 : #endif // CHIP_PROGRESS_LOGGING
366 :
367 : // Handle possibly-sync call backs from attempts to establish PASE.
368 0 : ExpectPASEEstablishment();
369 :
370 0 : if (params.GetPeerAddress().GetTransportType() == Transport::Type::kUdp)
371 : {
372 0 : mCurrentPASEParameters.SetValue(params);
373 : }
374 :
375 : CHIP_ERROR err;
376 0 : if (mConnectionType == SetupCodePairerBehaviour::kCommission)
377 : {
378 0 : err = mCommissioner->PairDevice(mRemoteId, params);
379 : }
380 : else
381 : {
382 0 : err = mCommissioner->EstablishPASEConnection(mRemoteId, params);
383 : }
384 :
385 0 : LogErrorOnFailure(err);
386 0 : if (err == CHIP_NO_ERROR)
387 : {
388 0 : return true;
389 : }
390 :
391 : // Failed to start establishing PASE. Move on to the next item.
392 0 : PASEEstablishmentComplete();
393 : }
394 :
395 0 : return false;
396 : }
397 :
398 : #if CONFIG_NETWORK_LAYER_BLE
399 0 : void SetUpCodePairer::OnDiscoveredDeviceOverBle(BLE_CONNECTION_OBJECT connObj, std::optional<uint16_t> matchedLongDiscriminator)
400 : {
401 0 : ChipLogProgress(Controller, "Discovered device to be commissioned over BLE");
402 :
403 0 : mWaitingForDiscovery[kBLETransport] = false;
404 :
405 : // In order to not wait for all the possible addresses discovered over mdns to
406 : // be tried before trying to connect over BLE, the discovered connection object is
407 : // inserted at the beginning of the list.
408 : //
409 : // It makes it the 'next' thing to try to connect to if there are already some
410 : // discovered parameters in the list.
411 : //
412 : // TODO: Consider implementing the SHOULD the spec has about commissioning things
413 : // in QR code order by waiting for a second or something before actually starting
414 : // the first PASE session when we have multiple setup payloads, and sorting the
415 : // results in setup payload order. If we do this, we might want to restrict it to
416 : // cases when the different payloads have different vendor/product IDs, since if
417 : // they are all the same product presumably ordering really does not matter.
418 0 : mDiscoveredParameters.emplace_front(connObj, matchedLongDiscriminator);
419 0 : ConnectToDiscoveredDevice();
420 0 : }
421 :
422 0 : void SetUpCodePairer::OnDiscoveredDeviceOverBleSuccess(void * appState, BLE_CONNECTION_OBJECT connObj)
423 : {
424 0 : (static_cast<SetUpCodePairer *>(appState))->OnDiscoveredDeviceOverBle(connObj, std::nullopt);
425 0 : }
426 :
427 0 : void SetUpCodePairer::OnDiscoveredDeviceWithDiscriminatorOverBleSuccess(void * appState, uint16_t matchedLongDiscriminator,
428 : BLE_CONNECTION_OBJECT connObj)
429 : {
430 0 : (static_cast<SetUpCodePairer *>(appState))->OnDiscoveredDeviceOverBle(connObj, std::make_optional(matchedLongDiscriminator));
431 0 : }
432 :
433 0 : void SetUpCodePairer::OnDiscoveredDeviceOverBleError(void * appState, CHIP_ERROR err)
434 : {
435 0 : static_cast<SetUpCodePairer *>(appState)->OnBLEDiscoveryError(err);
436 0 : }
437 :
438 0 : void SetUpCodePairer::OnBLEDiscoveryError(CHIP_ERROR err)
439 : {
440 0 : ChipLogError(Controller, "Commissioning discovery over BLE failed: %" CHIP_ERROR_FORMAT, err.Format());
441 0 : mWaitingForDiscovery[kBLETransport] = false;
442 0 : LogErrorOnFailure(err);
443 0 : }
444 : #endif // CONFIG_NETWORK_LAYER_BLE
445 :
446 : #if CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF
447 0 : void SetUpCodePairer::OnDiscoveredDeviceOverWifiPAF()
448 : {
449 0 : ChipLogProgress(Controller, "Discovered device to be commissioned over WiFiPAF, RemoteId: %lu", mRemoteId);
450 :
451 0 : mWaitingForDiscovery[kWiFiPAFTransport] = false;
452 0 : auto param = SetUpCodePairerParameters();
453 0 : param.SetPeerAddress(Transport::PeerAddress(Transport::Type::kWiFiPAF, mRemoteId));
454 : // TODO: This needs to support concatenated QR codes and set the relevant
455 : // long discriminator on param.
456 : //
457 : // See https://github.com/project-chip/connectedhomeip/issues/39134
458 0 : mDiscoveredParameters.emplace_back(param);
459 0 : ConnectToDiscoveredDevice();
460 0 : }
461 :
462 0 : void SetUpCodePairer::OnWifiPAFDiscoveryError(CHIP_ERROR err)
463 : {
464 0 : ChipLogError(Controller, "Commissioning discovery over WiFiPAF failed: %" CHIP_ERROR_FORMAT, err.Format());
465 0 : mWaitingForDiscovery[kWiFiPAFTransport] = false;
466 0 : }
467 :
468 0 : void SetUpCodePairer::OnWiFiPAFSubscribeComplete(void * appState)
469 : {
470 0 : auto self = reinterpret_cast<SetUpCodePairer *>(appState);
471 0 : self->OnDiscoveredDeviceOverWifiPAF();
472 0 : }
473 :
474 0 : void SetUpCodePairer::OnWiFiPAFSubscribeError(void * appState, CHIP_ERROR err)
475 : {
476 0 : auto self = reinterpret_cast<SetUpCodePairer *>(appState);
477 0 : self->OnWifiPAFDiscoveryError(err);
478 0 : }
479 : #endif
480 :
481 0 : bool SetUpCodePairer::IdIsPresent(uint16_t vendorOrProductID)
482 : {
483 0 : return vendorOrProductID != kNotAvailable;
484 : }
485 :
486 0 : bool SetUpCodePairer::NodeMatchesCurrentFilter(const Dnssd::DiscoveredNodeData & discNodeData) const
487 : {
488 0 : if (!discNodeData.Is<Dnssd::CommissionNodeData>())
489 : {
490 0 : return false;
491 : }
492 :
493 0 : const Dnssd::CommissionNodeData & nodeData = discNodeData.Get<Dnssd::CommissionNodeData>();
494 0 : if (nodeData.commissioningMode == 0)
495 : {
496 0 : ChipLogProgress(Controller, "Discovered device does not have an open commissioning window.");
497 0 : return false;
498 : }
499 :
500 : // Check whether this matches one of our setup payloads.
501 0 : for (auto & payload : mSetupPayloads)
502 : {
503 : // The advertisement may not include a vendor id, and the payload may not have one either.
504 0 : if (IdIsPresent(payload.vendorID) && IdIsPresent(nodeData.vendorId) && payload.vendorID != nodeData.vendorId)
505 : {
506 0 : ChipLogProgress(Controller, "Discovered device vendor ID (%u) does not match our vendor ID (%u).", nodeData.vendorId,
507 : payload.vendorID);
508 0 : continue;
509 : }
510 :
511 : // The advertisement may not include a product id, and the payload may not have one either.
512 0 : if (IdIsPresent(payload.productID) && IdIsPresent(nodeData.productId) && payload.productID != nodeData.productId)
513 : {
514 0 : ChipLogProgress(Controller, "Discovered device product ID (%u) does not match our product ID (%u).", nodeData.productId,
515 : payload.productID);
516 0 : continue;
517 : }
518 :
519 0 : if (!payload.discriminator.MatchesLongDiscriminator(nodeData.longDiscriminator))
520 : {
521 0 : ChipLogProgress(Controller, "Discovered device discriminator (%u) does not match our discriminator.",
522 : nodeData.longDiscriminator);
523 0 : continue;
524 : }
525 :
526 0 : ChipLogProgress(Controller, "Discovered device with discriminator %u matches one of our setup payloads",
527 : nodeData.longDiscriminator);
528 0 : return true;
529 : }
530 :
531 0 : return false;
532 : }
533 :
534 0 : void SetUpCodePairer::NotifyCommissionableDeviceDiscovered(const Dnssd::DiscoveredNodeData & nodeData)
535 : {
536 0 : if (!NodeMatchesCurrentFilter(nodeData))
537 : {
538 0 : return;
539 : }
540 :
541 0 : ChipLogProgress(Controller, "Discovered device to be commissioned over DNS-SD");
542 :
543 0 : auto & commissionableNodeData = nodeData.Get<Dnssd::CommissionNodeData>();
544 :
545 0 : NotifyCommissionableDeviceDiscovered(commissionableNodeData, std::make_optional(commissionableNodeData.longDiscriminator));
546 : }
547 :
548 0 : void SetUpCodePairer::NotifyCommissionableDeviceDiscovered(const Dnssd::CommonResolutionData & resolutionData,
549 : std::optional<uint16_t> matchedLongDiscriminator)
550 : {
551 0 : if (mDiscoveryType == DiscoveryType::kDiscoveryNetworkOnlyWithoutPASEAutoRetry)
552 : {
553 : // If the discovery type does not want the PASE auto retry mechanism, we will just store
554 : // a single IP. So the discovery process is stopped as it won't be of any help anymore.
555 0 : StopDiscoveryOverDNSSD();
556 0 : mDiscoveredParameters.emplace_back(resolutionData, matchedLongDiscriminator, 0);
557 : }
558 : else
559 : {
560 0 : for (size_t i = 0; i < resolutionData.numIPs; i++)
561 : {
562 0 : mDiscoveredParameters.emplace_back(resolutionData, matchedLongDiscriminator, i);
563 : }
564 : }
565 :
566 0 : ConnectToDiscoveredDevice();
567 0 : }
568 :
569 0 : bool SetUpCodePairer::StopPairing(NodeId remoteId)
570 : {
571 0 : VerifyOrReturnValue(mRemoteId != kUndefinedNodeId, false);
572 0 : VerifyOrReturnValue(remoteId == kUndefinedNodeId || remoteId == mRemoteId, false);
573 :
574 0 : if (mWaitingForPASE)
575 : {
576 0 : PASEEstablishmentComplete();
577 : }
578 :
579 0 : ResetDiscoveryState();
580 0 : mRemoteId = kUndefinedNodeId;
581 0 : return true;
582 : }
583 :
584 0 : bool SetUpCodePairer::TryNextRendezvousParameters()
585 : {
586 0 : if (ConnectToDiscoveredDevice())
587 : {
588 0 : ChipLogProgress(Controller, "Trying connection to commissionee over different transport");
589 0 : return true;
590 : }
591 :
592 0 : if (DiscoveryInProgress())
593 : {
594 0 : ChipLogProgress(Controller, "Waiting to discover commissionees that match our filters");
595 0 : return true;
596 : }
597 :
598 0 : return false;
599 : }
600 :
601 0 : bool SetUpCodePairer::DiscoveryInProgress() const
602 : {
603 0 : for (const auto & waiting : mWaitingForDiscovery)
604 : {
605 0 : if (waiting)
606 : {
607 0 : return true;
608 : }
609 : }
610 :
611 0 : return false;
612 : }
613 :
614 0 : void SetUpCodePairer::StopAllDiscoveryAttempts()
615 : {
616 0 : LogErrorOnFailure(StopDiscoveryOverBLE());
617 0 : LogErrorOnFailure(StopDiscoveryOverDNSSD());
618 0 : LogErrorOnFailure(StopDiscoveryOverWiFiPAF());
619 :
620 : // Just in case any of those failed to reset the waiting state properly.
621 0 : for (auto & waiting : mWaitingForDiscovery)
622 : {
623 0 : waiting = false;
624 : }
625 0 : }
626 :
627 0 : void SetUpCodePairer::ResetDiscoveryState()
628 : {
629 0 : StopAllDiscoveryAttempts();
630 :
631 0 : mDiscoveredParameters.clear();
632 0 : mCurrentPASEParameters.ClearValue();
633 0 : mLastPASEError = CHIP_NO_ERROR;
634 :
635 0 : mSetupPayloads.clear();
636 :
637 0 : mSystemLayer->CancelTimer(OnDeviceDiscoveredTimeoutCallback, this);
638 0 : }
639 :
640 0 : void SetUpCodePairer::ExpectPASEEstablishment()
641 : {
642 0 : VerifyOrDie(!mWaitingForPASE);
643 0 : mWaitingForPASE = true;
644 0 : auto * delegate = mCommissioner->GetPairingDelegate();
645 0 : VerifyOrDie(delegate != this);
646 0 : mPairingDelegate = delegate;
647 0 : mCommissioner->RegisterPairingDelegate(this);
648 0 : }
649 :
650 0 : void SetUpCodePairer::PASEEstablishmentComplete()
651 : {
652 0 : VerifyOrDie(mWaitingForPASE);
653 0 : mWaitingForPASE = false;
654 0 : mCommissioner->RegisterPairingDelegate(mPairingDelegate);
655 0 : mPairingDelegate = nullptr;
656 0 : }
657 :
658 0 : void SetUpCodePairer::OnStatusUpdate(DevicePairingDelegate::Status status)
659 : {
660 0 : if (status == DevicePairingDelegate::Status::SecurePairingFailed)
661 : {
662 : // If we're still waiting on discovery, don't propagate this failure
663 : // (which is due to PASE failure with something we discovered, but the
664 : // "something" may not have been the right thing) for now. Wait until
665 : // discovery completes. Then we will either succeed and notify
666 : // accordingly or time out and land in OnStatusUpdate again, but at that
667 : // point we will not be waiting on discovery anymore.
668 0 : if (!mDiscoveredParameters.empty())
669 : {
670 0 : ChipLogProgress(Controller, "Ignoring SecurePairingFailed status for now; we have more discovered devices to try");
671 0 : return;
672 : }
673 :
674 0 : if (DiscoveryInProgress())
675 : {
676 0 : ChipLogProgress(Controller,
677 : "Ignoring SecurePairingFailed status for now; we are waiting to see if we discover more devices");
678 0 : return;
679 : }
680 : }
681 :
682 0 : if (mPairingDelegate)
683 : {
684 0 : mPairingDelegate->OnStatusUpdate(status);
685 : }
686 : }
687 :
688 0 : void SetUpCodePairer::OnPairingComplete(CHIP_ERROR error)
689 : {
690 : // Save the pairing delegate so we can notify it. We want to notify it
691 : // _after_ we restore the state on the commissioner, in case the delegate
692 : // ends up immediately calling back into the commissioner again when
693 : // notified.
694 0 : auto * pairingDelegate = mPairingDelegate;
695 0 : PASEEstablishmentComplete();
696 :
697 0 : if (CHIP_NO_ERROR == error)
698 : {
699 0 : ChipLogProgress(Controller, "PASE session established with commissionee. Stopping discovery.");
700 0 : ResetDiscoveryState();
701 0 : mRemoteId = kUndefinedNodeId;
702 : MATTER_LOG_METRIC_END(kMetricSetupCodePairerPairDevice, error);
703 0 : if (pairingDelegate != nullptr)
704 : {
705 0 : pairingDelegate->OnPairingComplete(error);
706 : }
707 0 : return;
708 : }
709 :
710 : // It may happen that there is a stale DNS entry. If so, ReconfirmRecord will flush
711 : // the record from the daemon cache once it determines that it is invalid.
712 : // It may not help for this particular resolve, but may help subsequent resolves.
713 0 : if (CHIP_ERROR_TIMEOUT == error && mCurrentPASEParameters.HasValue())
714 : {
715 0 : const auto & params = mCurrentPASEParameters.Value();
716 0 : const auto & peer = params.GetPeerAddress();
717 0 : const auto & ip = peer.GetIPAddress();
718 0 : auto err = Dnssd::Resolver::Instance().ReconfirmRecord(params.mHostName, ip, params.mInterfaceId);
719 0 : if (CHIP_NO_ERROR != err && CHIP_ERROR_NOT_IMPLEMENTED != err)
720 : {
721 0 : ChipLogError(Controller, "Error when verifying the validity of an address: %" CHIP_ERROR_FORMAT, err.Format());
722 : }
723 : }
724 0 : mCurrentPASEParameters.ClearValue();
725 :
726 : // We failed to establish PASE. Try the next thing we have discovered, if
727 : // any.
728 0 : if (TryNextRendezvousParameters())
729 : {
730 : // Keep waiting until that finishes. Don't call OnPairingComplete yet.
731 0 : mLastPASEError = error;
732 0 : return;
733 : }
734 :
735 : MATTER_LOG_METRIC_END(kMetricSetupCodePairerPairDevice, error);
736 0 : if (pairingDelegate != nullptr)
737 : {
738 0 : pairingDelegate->OnPairingComplete(error);
739 : }
740 : }
741 :
742 0 : void SetUpCodePairer::OnPairingDeleted(CHIP_ERROR error)
743 : {
744 0 : if (mPairingDelegate)
745 : {
746 0 : mPairingDelegate->OnPairingDeleted(error);
747 : }
748 0 : }
749 :
750 0 : void SetUpCodePairer::OnCommissioningComplete(NodeId deviceId, CHIP_ERROR error)
751 : {
752 : // Not really expecting this, but handle it anyway.
753 0 : if (mPairingDelegate)
754 : {
755 0 : mPairingDelegate->OnCommissioningComplete(deviceId, error);
756 : }
757 0 : }
758 :
759 0 : void SetUpCodePairer::OnDeviceDiscoveredTimeoutCallback(System::Layer * layer, void * context)
760 : {
761 0 : ChipLogError(Controller, "Discovery timed out");
762 0 : auto * pairer = static_cast<SetUpCodePairer *>(context);
763 0 : pairer->StopAllDiscoveryAttempts();
764 0 : if (!pairer->mWaitingForPASE && pairer->mDiscoveredParameters.empty())
765 : {
766 : // We're not waiting on any more PASE attempts, and we're not going to
767 : // discover anything at this point, so we should just notify our
768 : // listener.
769 0 : CHIP_ERROR err = pairer->mLastPASEError;
770 0 : if (err == CHIP_NO_ERROR)
771 : {
772 0 : err = CHIP_ERROR_TIMEOUT;
773 : }
774 : MATTER_LOG_METRIC_END(kMetricSetupCodePairerPairDevice, err);
775 0 : pairer->mCommissioner->OnSessionEstablishmentError(err);
776 : }
777 0 : }
778 :
779 0 : bool SetUpCodePairer::ShouldDiscoverUsing(RendezvousInformationFlag commissioningChannel) const
780 : {
781 0 : for (auto & payload : mSetupPayloads)
782 : {
783 0 : auto & rendezvousInformation = payload.rendezvousInformation;
784 0 : if (!rendezvousInformation.HasValue())
785 : {
786 : // No idea which commissioning channels this device supports, so we
787 : // should be trying using all of them.
788 0 : return true;
789 : }
790 :
791 0 : if (rendezvousInformation.Value().Has(commissioningChannel))
792 : {
793 0 : return true;
794 : }
795 : }
796 :
797 : // None of the payloads claimed support for this commissioning channel.
798 0 : return false;
799 : }
800 :
801 0 : SetUpCodePairerParameters::SetUpCodePairerParameters(const Dnssd::CommonResolutionData & data,
802 0 : std::optional<uint16_t> longDiscriminator, size_t index) :
803 0 : mLongDiscriminator(longDiscriminator)
804 : {
805 0 : mInterfaceId = data.interfaceId;
806 0 : Platform::CopyString(mHostName, data.hostName);
807 :
808 0 : auto & ip = data.ipAddress[index];
809 0 : SetPeerAddress(Transport::PeerAddress::UDP(ip, data.port, ip.IsIPv6LinkLocal() ? data.interfaceId : Inet::InterfaceId::Null()));
810 :
811 0 : if (data.mrpRetryIntervalIdle.has_value())
812 : {
813 0 : SetIdleInterval(*data.mrpRetryIntervalIdle);
814 : }
815 :
816 0 : if (data.mrpRetryIntervalActive.has_value())
817 : {
818 0 : SetActiveInterval(*data.mrpRetryIntervalActive);
819 : }
820 0 : }
821 :
822 : #if CONFIG_NETWORK_LAYER_BLE
823 0 : SetUpCodePairerParameters::SetUpCodePairerParameters(BLE_CONNECTION_OBJECT connObj, std::optional<uint16_t> longDiscriminator,
824 0 : bool connected) :
825 0 : mLongDiscriminator(longDiscriminator)
826 : {
827 0 : Transport::PeerAddress peerAddress = Transport::PeerAddress::BLE();
828 0 : SetPeerAddress(peerAddress);
829 0 : if (connected)
830 : {
831 0 : SetConnectionObject(connObj);
832 : }
833 : else
834 : {
835 0 : SetDiscoveredObject(connObj);
836 : }
837 0 : }
838 : #endif // CONFIG_NETWORK_LAYER_BLE
839 :
840 : } // namespace Controller
841 : } // namespace chip
|