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