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