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 : * Declaration 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 : #pragma once
28 :
29 : #include <controller/DevicePairingDelegate.h>
30 : #include <lib/core/CHIPError.h>
31 : #include <lib/core/NodeId.h>
32 : #include <lib/support/DLLUtil.h>
33 : #include <lib/support/ThreadOperationalDataset.h>
34 : #include <platform/CHIPDeviceConfig.h>
35 : #include <protocols/secure_channel/RendezvousParameters.h>
36 : #include <setup_payload/ManualSetupPayloadParser.h>
37 : #include <setup_payload/QRCodeSetupPayloadParser.h>
38 :
39 : #if CONFIG_NETWORK_LAYER_BLE
40 : #include <ble/Ble.h>
41 : #endif // CONFIG_NETWORK_BLE
42 :
43 : #if CHIP_DEVICE_CONFIG_ENABLE_NFC_BASED_COMMISSIONING
44 : #include <nfc/NFC.h>
45 : #endif // CHIP_DEVICE_CONFIG_ENABLE_NFC_BASED_COMMISSIONING
46 :
47 : #if CHIP_SUPPORT_THREAD_MESHCOP
48 : #include <controller/ThreadMeshcopCommissionProxy.h>
49 : #endif // CHIP_SUPPORT_THREAD_MESHCOP
50 :
51 : #include <controller/DeviceDiscoveryDelegate.h>
52 :
53 : #include <deque>
54 : #include <optional>
55 : #include <vector>
56 :
57 : namespace chip {
58 :
59 : namespace Testing {
60 :
61 : class SetUpCodePairerTestAccess;
62 :
63 : } // namespace Testing
64 :
65 : namespace Controller {
66 :
67 : class DeviceCommissioner;
68 :
69 : /**
70 : * A class that represents a discovered device. This includes both the inputs to discovery (via the
71 : * RendezvousParameters super-class), and the outputs from discovery (the PeerAddress in
72 : * RendezvousParameters but also some of our members like mHostName, mInterfaceId,
73 : * mLongDiscriminator).
74 : */
75 : class SetUpCodePairerParameters : public RendezvousParameters
76 : {
77 : public:
78 2 : SetUpCodePairerParameters() = default;
79 : SetUpCodePairerParameters(const Dnssd::CommonResolutionData & data, std::optional<uint16_t> longDiscriminator, size_t index);
80 : #if CONFIG_NETWORK_LAYER_BLE
81 : SetUpCodePairerParameters(BLE_CONNECTION_OBJECT connObj, std::optional<uint16_t> longDiscriminator, bool connected = true);
82 : #endif // CONFIG_NETWORK_LAYER_BLE
83 : char mHostName[Dnssd::kHostNameMaxLength + 1] = {};
84 : Inet::InterfaceId mInterfaceId;
85 :
86 : // The long discriminator of the device that was actually discovered, if this is known. This
87 : // differs from the mSetupDiscriminator member of RendezvousParameters in that the latter may be
88 : // a short discriminator from a numeric setup code (which may match multiple devices), while
89 : // this member, if set, is always a long discriminator that was actually advertised by the
90 : // device represented by our PeerAddress.
91 : std::optional<uint16_t> mLongDiscriminator = std::nullopt;
92 :
93 : // Whether this discovered candidate would drive the same PASE attempt as `other`, so one of the
94 : // two can be dropped. The comparison is the full RendezvousParameters base (all PASE connection
95 : // inputs) plus mLongDiscriminator, which selects which setup payload's passcode we use. mHostName
96 : // and mInterfaceId are discovery bookkeeping, not connection inputs, and are intentionally
97 : // excluded: the same non-link-local address advertised on multiple interfaces yields the same
98 : // interface-less PeerAddress and identical mLongDiscriminator, and should coalesce to a single
99 : // attempt. Deliberately not operator==, since it does not compare all members.
100 8 : bool CanCoalesceWith(const SetUpCodePairerParameters & other) const
101 : {
102 8 : return RendezvousParameters::operator==(other) && mLongDiscriminator == other.mLongDiscriminator;
103 : }
104 : };
105 :
106 : enum class SetupCodePairerBehaviour : uint8_t
107 : {
108 : kCommission,
109 : kPaseOnly,
110 : };
111 :
112 : enum class DiscoveryType : uint8_t
113 : {
114 : kDiscoveryNetworkOnly,
115 : kDiscoveryNetworkOnlyWithoutPASEAutoRetry,
116 : kAll,
117 : };
118 :
119 : class DLL_EXPORT SetUpCodePairer : public DevicePairingDelegate
120 : #if CHIP_DEVICE_CONFIG_ENABLE_NFC_BASED_COMMISSIONING
121 : ,
122 : public Nfc::NFCReaderTransportDelegate
123 : #endif
124 : {
125 : friend class chip::Testing::SetUpCodePairerTestAccess;
126 :
127 : public:
128 : struct ThreadMeshcopCommissionParameters
129 : {
130 : Transport::PeerAddress mBorderAgentAddress;
131 : uint8_t mPSKcBuffer[Thread::kSizePSKc];
132 : };
133 39 : SetUpCodePairer(DeviceCommissioner * commissioner) : mCommissioner(commissioner) {}
134 50 : virtual ~SetUpCodePairer() {}
135 :
136 : CHIP_ERROR PairDevice(chip::NodeId remoteId, const char * setUpCode,
137 : SetupCodePairerBehaviour connectionType = SetupCodePairerBehaviour::kCommission,
138 : DiscoveryType discoveryType = DiscoveryType::kAll,
139 : Optional<Dnssd::CommonResolutionData> resolutionData = NullOptional);
140 :
141 : // Called by the DeviceCommissioner to notify that we have discovered a new device.
142 : void NotifyCommissionableDeviceDiscovered(const chip::Dnssd::DiscoveredNodeData & nodeData);
143 :
144 0 : void SetSystemLayer(System::Layer * systemLayer) { mSystemLayer = systemLayer; };
145 :
146 : #if CONFIG_NETWORK_LAYER_BLE
147 0 : void SetBleLayer(Ble::BleLayer * bleLayer) { mBleLayer = bleLayer; };
148 : #endif // CONFIG_NETWORK_LAYER_BLE
149 :
150 : #if CHIP_SUPPORT_THREAD_MESHCOP
151 0 : void SetThreadMeshcopCommissionParamsAndProxy(ThreadMeshcopCommissionParameters & meshcopCommissionParams,
152 : ThreadMeshcopCommissionProxy * proxy)
153 : {
154 0 : mThreadMeshcopCommissionProxy = proxy;
155 0 : mThreadMeshcopCommissionParams.SetValue(meshcopCommissionParams);
156 0 : }
157 : #endif
158 :
159 : // Stop ongoing discovery / pairing of the specified node, or of
160 : // whichever node we're pairing if kUndefinedNodeId is passed.
161 : bool StopPairing(NodeId remoteId = kUndefinedNodeId);
162 :
163 : private:
164 : // DevicePairingDelegate implementation.
165 : void OnStatusUpdate(DevicePairingDelegate::Status status) override;
166 : void OnPairingComplete(CHIP_ERROR error, const std::optional<RendezvousParameters> & rendezvousParameters,
167 : const std::optional<SetupPayload> & setupPayload) override;
168 : void OnPairingDeleted(CHIP_ERROR error) override;
169 : void OnCommissioningComplete(NodeId deviceId, CHIP_ERROR error) override;
170 :
171 : #if CHIP_DEVICE_CONFIG_ENABLE_NFC_BASED_COMMISSIONING
172 : // Nfc::NFCReaderTransportDelegate implementation
173 : void OnTagDiscovered(const chip::Nfc::NFCTag::Identifier & identifer) override;
174 : void OnTagDiscoveryFailed(CHIP_ERROR error) override;
175 : #endif
176 :
177 : CHIP_ERROR Connect();
178 : CHIP_ERROR StartDiscoveryOverBLE();
179 : CHIP_ERROR StopDiscoveryOverBLE();
180 : CHIP_ERROR StartDiscoveryOverDNSSD();
181 : CHIP_ERROR StopDiscoveryOverDNSSD();
182 : CHIP_ERROR StartDiscoveryOverWiFiPAF();
183 : CHIP_ERROR StopDiscoveryOverWiFiPAF();
184 : CHIP_ERROR StartDiscoveryOverNFC();
185 : CHIP_ERROR StopDiscoveryOverNFC();
186 : CHIP_ERROR StartDiscoveryOverThreadMeshcop();
187 : CHIP_ERROR StopDiscoveryOverThreadMeshcop();
188 :
189 : // Returns whether we have kicked off a new connection attempt.
190 : bool ConnectToDiscoveredDevice();
191 :
192 : // Stop attempts to discover more things to connect to, but keep trying to
193 : // connect to the ones we have already discovered.
194 : void StopAllDiscoveryAttempts();
195 :
196 : // Reset our mWaitingForDiscovery/mDiscoveredParameters state to indicate no
197 : // pending work.
198 : void ResetDiscoveryState();
199 :
200 : // Get ready to start PASE establishment via mCommissioner. Sets up
201 : // whatever state is needed for that.
202 : void ExpectPASEEstablishment();
203 :
204 : // PASE establishment by mCommissioner has completed: we either have a PASE
205 : // session now or we failed to set one up, but we are done waiting on
206 : // mCommissioner.
207 : void PASEEstablishmentComplete();
208 :
209 : // Called when PASE establishment fails.
210 : //
211 : // May start a new PASE establishment.
212 : //
213 : // Will return whether we might in fact have more rendezvous parameters to
214 : // try (e.g. because we started a new PASE establishment or are waiting on
215 : // more device discovery).
216 : //
217 : // The commissioner can use the return value to decide whether pairing has
218 : // actually failed or not.
219 : bool TryNextRendezvousParameters();
220 :
221 : // True if we are still waiting on discovery to possibly produce new
222 : // RendezvousParameters in the future.
223 : bool DiscoveryInProgress() const;
224 :
225 : // If there is nothing left to try (no PASE in progress, no queued discovered
226 : // parameters, no discovery in progress), notify the commissioner that pairing
227 : // has failed. err is used as the failure error only if no PASE attempt has
228 : // produced an error yet.
229 : void StopPairingIfTransportsExhausted(CHIP_ERROR err);
230 :
231 : // Not an enum class because we use this for indexing into arrays.
232 : enum TransportTypes
233 : {
234 : kBLETransport = 0,
235 : kIPTransport,
236 : kWiFiPAFTransport,
237 : #if CHIP_DEVICE_CONFIG_ENABLE_NFC_BASED_COMMISSIONING
238 : kNFCTransport,
239 : #endif
240 : #if CHIP_SUPPORT_THREAD_MESHCOP
241 : kThreadMeshcopTransport,
242 : #endif
243 : kTransportTypeCount,
244 : };
245 :
246 : void NotifyCommissionableDeviceDiscovered(const chip::Dnssd::CommonResolutionData & resolutionData,
247 : std::optional<uint16_t> matchedLongDiscriminator);
248 :
249 : // Append newly discovered parameters to mDiscoveredParameters, unless they would drive the same
250 : // PASE attempt as something already queued (see SetUpCodePairerParameters::CanCoalesceWith), in
251 : // which case they are dropped.
252 : void EnqueueDiscoveredParametersIfNotDuplicate(SetUpCodePairerParameters && params);
253 :
254 : static void OnDeviceDiscoveredTimeoutCallback(System::Layer * layer, void * context);
255 :
256 : #if CONFIG_NETWORK_LAYER_BLE
257 : Ble::BleLayer * mBleLayer = nullptr;
258 : void OnDiscoveredDeviceOverBle(BLE_CONNECTION_OBJECT connObj, std::optional<uint16_t> matchedLongDiscriminator);
259 : void OnBLEDiscoveryError(CHIP_ERROR err);
260 : /////////// BLEConnectionDelegate Callbacks /////////
261 : static void OnDiscoveredDeviceOverBleSuccess(void * appState, BLE_CONNECTION_OBJECT connObj);
262 : static void OnDiscoveredDeviceWithDiscriminatorOverBleSuccess(void * appState, uint16_t matchedLongDiscriminator,
263 : BLE_CONNECTION_OBJECT connObj);
264 : static void OnDiscoveredDeviceOverBleError(void * appState, CHIP_ERROR err);
265 : #endif // CONFIG_NETWORK_LAYER_BLE
266 : #if CHIP_DEVICE_CONFIG_ENABLE_WIFIPAF
267 : void OnDiscoveredDeviceOverWifiPAF();
268 : void OnWifiPAFDiscoveryError(CHIP_ERROR err);
269 : static void OnWiFiPAFSubscribeComplete(void * appState);
270 : static void OnWiFiPAFSubscribeError(void * appState, CHIP_ERROR err);
271 : #endif
272 :
273 : bool NodeMatchesCurrentFilter(const Dnssd::DiscoveredNodeData & nodeData) const;
274 : static bool IdIsPresent(uint16_t vendorOrProductID);
275 :
276 : bool ShouldDiscoverUsing(RendezvousInformationFlag commissioningChannel) const;
277 :
278 : // kNotAvailable represents unavailable vendor/product ID values in setup payloads.
279 : static constexpr uint16_t kNotAvailable = 0;
280 :
281 : DeviceCommissioner * mCommissioner = nullptr;
282 : System::Layer * mSystemLayer = nullptr;
283 : chip::NodeId mRemoteId = kUndefinedNodeId;
284 : SetupCodePairerBehaviour mConnectionType = SetupCodePairerBehaviour::kCommission;
285 : DiscoveryType mDiscoveryType = DiscoveryType::kAll;
286 : std::vector<SetupPayload> mSetupPayloads;
287 :
288 : // The payload we are using for our current PASE connection attempt. Only
289 : // set while we are attempting PASE.
290 : std::optional<SetupPayload> mCurrentPASEPayload;
291 :
292 : // While we are trying to pair, we intercept the DevicePairingDelegate
293 : // notifications from mCommissioner. We want to make sure we send them on
294 : // to the original pairing delegate, if any.
295 : DevicePairingDelegate * mPairingDelegate = nullptr;
296 :
297 : // Boolean will be set to true if we currently have an async discovery
298 : // process happening via the relevant transport.
299 : bool mWaitingForDiscovery[kTransportTypeCount] = { false };
300 :
301 : // Double ended-queue of things we have discovered but not tried connecting to yet. The
302 : // general discovery/pairing process will terminate once this queue is empty
303 : // and all the booleans in mWaitingForDiscovery are false.
304 : std::deque<SetUpCodePairerParameters> mDiscoveredParameters;
305 :
306 : // Current thing we are trying to connect to over UDP. If a PASE connection fails with
307 : // a CHIP_ERROR_TIMEOUT, the discovered parameters will be used to ask the
308 : // mdns daemon to invalidate its caches.
309 : Optional<SetUpCodePairerParameters> mCurrentPASEParameters;
310 :
311 : // mWaitingForPASE is true if we have called either
312 : // EstablishPASEConnection or PairDevice on mCommissioner and are now just
313 : // waiting to see whether that works.
314 : bool mWaitingForPASE = false;
315 :
316 : // mLastPASEError is the error from the last OnPairingComplete call we got.
317 : CHIP_ERROR mLastPASEError = CHIP_NO_ERROR;
318 :
319 : #if CHIP_SUPPORT_THREAD_MESHCOP
320 : Optional<ThreadMeshcopCommissionParameters> mThreadMeshcopCommissionParams;
321 : ThreadMeshcopCommissionProxy * mThreadMeshcopCommissionProxy = nullptr;
322 : #endif
323 : };
324 :
325 : } // namespace Controller
326 : } // namespace chip
|