Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : /**
19 : * @file
20 : * This file defines the CHIP Device Network Provisioning object.
21 : *
22 : */
23 :
24 : #pragma once
25 :
26 : #include <credentials/CHIPCert.h>
27 : #include <crypto/CHIPCryptoPAL.h>
28 : #include <lib/core/CHIPCore.h>
29 : #include <lib/core/CHIPSafeCasts.h>
30 : #include <lib/core/Optional.h>
31 : #include <lib/support/ThreadOperationalDataset.h>
32 : #include <lib/support/TypeTraits.h>
33 : #include <lib/support/Variant.h>
34 : #include <platform/CHIPDeviceConfig.h>
35 : #include <platform/internal/DeviceNetworkInfo.h>
36 :
37 : #include <app-common/zap-generated/cluster-enums.h>
38 :
39 : #include <limits>
40 :
41 : namespace chip {
42 : namespace DeviceLayer {
43 : /**
44 : * We are using a namespace here, for most use cases, this namespace will be used by `using DeviceLayer::NetworkCommissioning`, but
45 : * this still worth a dedicated namespace since:
46 : *
47 : * - The BaseDriver / WirelessDriver is not expected to be implemented directly by users, the only occurrence is in the network
48 : * commissioning cluster.
49 : * - We can safely name the drivers as WiFiDriver / ThreadDriver, it should not be ambiguous for most cases
50 : * - We can safely name the Status enum to Status, and some other structs -- if we are using using, then we should in the context of
51 : * writing something dedicated to network commissioning, then a single word Status should be clear enough.
52 : */
53 : namespace NetworkCommissioning {
54 :
55 : inline constexpr size_t kMaxNetworkIDLen = 32;
56 :
57 : // TODO: This is exactly the same as the one in GroupDataProvider, this could be moved to src/lib/support
58 : template <typename T>
59 : class Iterator
60 : {
61 : public:
62 3 : virtual ~Iterator() = default;
63 : /**
64 : * @retval The number of entries in total that will be iterated.
65 : */
66 : virtual size_t Count() = 0;
67 : /**
68 : * @param[out] item Value associated with the next element in the iteration.
69 : * @retval true if the next entry is successfully retrieved.
70 : * @retval false if no more entries can be found.
71 : */
72 : virtual bool Next(T & item) = 0;
73 : /**
74 : * Release the memory allocated by this iterator.
75 : * Must be called before the pointer goes out of scope.
76 : */
77 : virtual void Release() = 0;
78 :
79 : protected:
80 3 : Iterator() = default;
81 : };
82 :
83 : // The following structs follows the generated cluster object structs.
84 : struct Network
85 : {
86 : uint8_t networkID[kMaxNetworkIDLen];
87 : uint8_t networkIDLen = 0;
88 : #if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC
89 : Optional<Credentials::CertificateKeyIdStorage> networkIdentifier;
90 : Optional<Credentials::CertificateKeyIdStorage> clientIdentifier;
91 : #endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC
92 : bool connected = false;
93 : };
94 :
95 : static_assert(sizeof(Network::networkID) <= std::numeric_limits<decltype(Network::networkIDLen)>::max(),
96 : "Max length of networkID ssid exceeds the limit of networkIDLen field");
97 :
98 : /**
99 : * Wireless signal strength assessment or measurement type.
100 : */
101 : enum class WirelessSignalType
102 : {
103 : /**
104 : * No signal strength information available.
105 : */
106 : kNone,
107 :
108 : /**
109 : * Signal strength is quantitative, in dBm.
110 : */
111 : kdBm,
112 :
113 : /**
114 : * Signal strength is qualitative, increasing from 0 (none/worst)
115 : * to 100 (best).
116 : */
117 : kQualitative
118 : };
119 :
120 : /**
121 : * Wireless signal type and strength assessment.
122 : */
123 : struct WirelessSignal
124 : {
125 : /**
126 : * Wireless signal strength assessment or measurement type.
127 : *
128 : * This determines how to interpret @a strength.
129 : */
130 : WirelessSignalType type;
131 :
132 : /**
133 : * Wireless signal strength assessment or measurement.
134 : *
135 : * This is interpretted based on @a type.
136 : */
137 : int8_t strength;
138 : };
139 :
140 : struct WiFiScanResponse
141 : {
142 : chip::BitFlags<app::Clusters::NetworkCommissioning::WiFiSecurityBitmap> security;
143 : uint8_t ssid[DeviceLayer::Internal::kMaxWiFiSSIDLength];
144 : uint8_t ssidLen;
145 : uint8_t bssid[6];
146 : uint16_t channel;
147 : app::Clusters::NetworkCommissioning::WiFiBandEnum wiFiBand;
148 : WirelessSignal signal;
149 : };
150 :
151 : static_assert(sizeof(WiFiScanResponse::ssid) <= std::numeric_limits<decltype(WiFiScanResponse::ssidLen)>::max(),
152 : "Max length of WiFi ssid exceeds the limit of ssidLen field");
153 :
154 : struct ThreadScanResponse
155 : {
156 : uint16_t panId;
157 : uint64_t extendedPanId;
158 : char networkName[16];
159 : uint8_t networkNameLen;
160 : uint16_t channel;
161 : uint8_t version;
162 : uint64_t extendedAddress;
163 : int8_t rssi;
164 : uint8_t lqi;
165 : };
166 :
167 : static_assert(sizeof(ThreadScanResponse::networkName) <= std::numeric_limits<decltype(ThreadScanResponse::networkNameLen)>::max(),
168 : "Max length of WiFi credentials exceeds the limit of credentialsLen field");
169 :
170 : using NetworkIterator = Iterator<Network>;
171 : using WiFiScanResponseIterator = Iterator<WiFiScanResponse>;
172 : using ThreadScanResponseIterator = Iterator<ThreadScanResponse>;
173 : using Status = app::Clusters::NetworkCommissioning::NetworkCommissioningStatusEnum;
174 : using WiFiBandEnum = app::Clusters::NetworkCommissioning::WiFiBandEnum;
175 : // For backwards compatibility with pre-rename enum values.
176 : using WiFiBand = WiFiBandEnum;
177 : using WiFiSecurityBitmap = app::Clusters::NetworkCommissioning::WiFiSecurityBitmap;
178 : // For backwards compatibility with pre-rename bitmap values.
179 : using WiFiSecurity = WiFiSecurityBitmap;
180 : using ThreadCapabilities = app::Clusters::NetworkCommissioning::ThreadCapabilitiesBitmap;
181 :
182 : // BaseDriver and WirelessDriver are the common interfaces for a network driver, platform drivers should not implement this
183 : // directly, instead, users are expected to implement WiFiDriver, ThreadDriver and EthernetDriver.
184 : namespace Internal {
185 : class BaseDriver
186 : {
187 : public:
188 : class NetworkStatusChangeCallback
189 : {
190 : public:
191 : /**
192 : * @brief Callback for the network driver pushing the event of network status change to the network commissioning cluster.
193 : * The platforms is expected to push the status from operations such as autonomous connection after loss of connectivity or
194 : * during initial establishment.
195 : *
196 : * This function must be called in a thread-safe manner with CHIP stack.
197 : */
198 : virtual void OnNetworkingStatusChange(Status commissioningError, Optional<ByteSpan> networkId,
199 : Optional<int32_t> connectStatus) = 0;
200 :
201 3 : virtual ~NetworkStatusChangeCallback() = default;
202 : };
203 :
204 : /**
205 : * @brief Initializes the driver, this function will be called when initializing the network commissioning cluster.
206 : */
207 0 : virtual CHIP_ERROR Init(NetworkStatusChangeCallback * networkStatusChangeCallback) { return CHIP_NO_ERROR; }
208 :
209 : /**
210 : * @brief Shuts down the driver, this function will be called when shutting down the network commissioning cluster.
211 : */
212 2 : virtual void Shutdown() {}
213 :
214 : /**
215 : * @brief Returns maximum number of network configs can be added to the driver.
216 : */
217 : virtual uint8_t GetMaxNetworks() = 0;
218 :
219 : /**
220 : * @brief Returns an iterator for reading the networks, the user will always call NetworkIterator::Release. The iterator should
221 : * be consumed in the same context as calling GetNetworks(). Users must call Release() when the iterator goes out of scope.
222 : */
223 : virtual NetworkIterator * GetNetworks() = 0;
224 :
225 : /**
226 : * @brief Sets the status of the interface, this is an optional feature of a network driver.
227 : */
228 0 : virtual CHIP_ERROR SetEnabled(bool enabled) { return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; }
229 :
230 : /**
231 : * @brief Returns the status of the interface, this is an optional feature of a network driver the driver will be enabled by
232 : * default.
233 : */
234 0 : virtual bool GetEnabled() { return true; };
235 :
236 3 : virtual ~BaseDriver() = default;
237 : };
238 :
239 : class WirelessDriver : public Internal::BaseDriver
240 : {
241 : public:
242 : class ConnectCallback
243 : {
244 : public:
245 : virtual void OnResult(Status commissioningError, CharSpan debugText, int32_t connectStatus) = 0;
246 :
247 3 : virtual ~ConnectCallback() = default;
248 : };
249 :
250 : /**
251 : * @brief Persists the network configurations. This function is expected to be called when CommissioningComplete event is fired.
252 : */
253 : virtual CHIP_ERROR CommitConfiguration() = 0;
254 :
255 : /**
256 : * @brief Reverts the network configurations to the last committed one. This function is expected to be called when failsafe
257 : * timeout reached.
258 : */
259 : virtual CHIP_ERROR RevertConfiguration() = 0;
260 :
261 : virtual uint8_t GetScanNetworkTimeoutSeconds() = 0;
262 : virtual uint8_t GetConnectNetworkTimeoutSeconds() = 0;
263 :
264 : /**
265 : * @brief Remove a network from the device. The driver should fill the outDebugText field to pass any human-readable messages to
266 : * the client. The driver should reduce the size of outDebugText to 0 to omit it from the response when no debug text needs to
267 : * be delivered. On success, the driver should set outNetworkIndex to the index of the network just removed. The value of
268 : * network index is discarded on failure.
269 : *
270 : * Note: The capacity of outDebugText passed by network commissioning cluster can be configured via
271 : * CHIP_CONFIG_NETWORK_COMMISSIONING_DEBUG_TEXT_BUFFER_SIZE.
272 : */
273 : virtual Status RemoveNetwork(ByteSpan networkId, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex) = 0;
274 :
275 : /**
276 : * @brief Reorder the networks on the device. The driver should fill the outDebugText field to pass any human-readable messages
277 : * to the client. The driver should reduce the size of outDebugText to 0 to omit it from the response when no debug text needs
278 : * to be delivered.
279 : *
280 : * Note: The capacity of outDebugText passed by network commissioning cluster can be configured via
281 : * CHIP_CONFIG_NETWORK_COMMISSIONING_DEBUG_TEXT_BUFFER_SIZE.
282 : */
283 : virtual Status ReorderNetwork(ByteSpan networkId, uint8_t index, MutableCharSpan & outDebugText) = 0;
284 :
285 : /**
286 : * @brief Initializes a network join. callback->OnResult must be called, on both success and error. Callback can be
287 : * called inside ConnectNetwork.
288 : */
289 : virtual void ConnectNetwork(ByteSpan networkId, ConnectCallback * callback) = 0;
290 :
291 : #if CHIP_DEVICE_CONFIG_SUPPORTS_CONCURRENT_CONNECTION
292 : /**
293 : * @brief Disconnect from network, if currently connected.
294 : */
295 0 : virtual CHIP_ERROR DisconnectFromNetwork() { return CHIP_ERROR_NOT_IMPLEMENTED; }
296 : #endif
297 : };
298 : } // namespace Internal
299 :
300 : class WiFiDriver : public Internal::WirelessDriver
301 : {
302 : public:
303 : class ScanCallback
304 : {
305 : public:
306 : /**
307 : * Indicates the scan is finished, and accepts a iterator of networks discovered.
308 : * - networks can be nullptr when no networks discovered, or error occurred during scanning the networks.
309 : * OnFinished() must be called in a thread-safe manner with CHIP stack. (e.g. using ScheduleWork or ScheduleLambda)
310 : * - Users can assume the networks will always be used (and Release will be called) inside this function call. However, the
311 : * iterator might be not fully consumed (i.e. There are too many networks scanned to fit in the buffer for scan response
312 : * message.)
313 : */
314 : virtual void OnFinished(Status status, CharSpan debugText, WiFiScanResponseIterator * networks) = 0;
315 :
316 3 : virtual ~ScanCallback() = default;
317 : };
318 :
319 : /**
320 : * @brief Adds or updates a WiFi network on the device. The driver should fill the outDebugText field to pass any human-readable
321 : * messages to the client. The driver should reduce the size of outDebugText to 0 to omit it from the response when no debug
322 : * text needs to be delivered. On success, the driver should set outNetworkIndex to the index of the network just added or
323 : * updated. The value of network index is discarded on failure.
324 : *
325 : * Note: The capacity of outDebugText passed by network commissioning cluster can be configured via
326 : * CHIP_CONFIG_NETWORK_COMMISSIONING_DEBUG_TEXT_BUFFER_SIZE.
327 : */
328 : virtual Status AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials, MutableCharSpan & outDebugText,
329 : uint8_t & outNetworkIndex) = 0;
330 :
331 : /**
332 : * @brief Initializes a WiFi network scan. callback->OnFinished must be called, on both success and error. Callback can
333 : * be called inside ScanNetworks.
334 : *
335 : * @param ssid The interested SSID, the scanning SHALL be restricted to the given SSID if the ssid is not empty (i.e.
336 : * ssid.empty() is false).
337 : * @param callback Callback that will be invoked upon finishing the scan
338 : */
339 : virtual void ScanNetworks(ByteSpan ssid, ScanCallback * callback) = 0;
340 :
341 : #if CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC
342 : virtual bool SupportsPerDeviceCredentials() { return false; };
343 :
344 : /**
345 : * @brief Adds or updates a WiFi network with Per-Device Credentials on the device.
346 : *
347 : * @param ssid The SSID of the network to be added / updated.
348 : * @param networkIdentity The Network Identity of the network, in compact-pdc-identity format.
349 : * @param clientIdentityNetworkIndex If present, the index of the existing network configuration of which the Client
350 : * Identity is to be re-used. Otherwise a new Client Identity shall be generated.
351 : * @param outStatus The application-level status code (Status::kSuccess on success).
352 : * @param outDebugText A debug text buffer that may be populated by the driver. The size of the span
353 : * must be reduced to the length of text emitted (or 0, if none).
354 : * @param outClientIdentity On success, the Client Identity that was generated or copied, depending on the
355 : * presence of `clientIdentityNetworkIndex`.
356 : * @param outNextworkIndex On success, the index of the network entry that was added or updated.
357 : *
358 : * @retval CHIP_NO_ERROR and outStatus == kSuccess on success.
359 : * @retval CHIP_NO_ERROR and outStatus != kSuccess for application-level errors. outDebugText should be populated.
360 : * @retval CHIP_ERROR_* on internal errors. None of the output parameters will be examined in this case.
361 : */
362 : virtual CHIP_ERROR AddOrUpdateNetworkWithPDC(ByteSpan ssid, ByteSpan networkIdentity,
363 : Optional<uint8_t> clientIdentityNetworkIndex, Status & outStatus,
364 : MutableCharSpan & outDebugText, MutableByteSpan & outClientIdentity,
365 : uint8_t & outNetworkIndex)
366 : {
367 : return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
368 : }
369 :
370 : /**
371 : * @brief Retrieves the Network Identity associated with a network.
372 : *
373 : * @param networkIndex The 0-based index of the network.
374 : * @param outNetworkIdentity The output buffer to be populated with the Network
375 : * Identity in compact-pdc-identity TLV format.
376 : *
377 : * @return CHIP_NO_ERROR on success or a CHIP_ERROR on failure.
378 : */
379 : virtual CHIP_ERROR GetNetworkIdentity(uint8_t networkIndex, MutableByteSpan & outNetworkIdentity)
380 : {
381 : return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
382 : }
383 :
384 : /**
385 : * @brief Retrieves the Network Client Identity associated with a network.
386 : *
387 : * @param networkIndex The 0-based index of the network.
388 : * @param outNetworkIdentity The output buffer to be populated with the Network
389 : * Client Identity in compact-pdc-identity TLV format.
390 : *
391 : * @return CHIP_NO_ERROR on success or a CHIP_ERROR on failure.
392 : */
393 : virtual CHIP_ERROR GetClientIdentity(uint8_t networkIndex, MutableByteSpan & outClientIdentity)
394 : {
395 : return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
396 : }
397 :
398 : /**
399 : * @brief Signs the specified message with the private key of a Network Client Identity.
400 : */
401 : virtual CHIP_ERROR SignWithClientIdentity(uint8_t networkIndex, const ByteSpan & message,
402 : Crypto::P256ECDSASignature & outSignature)
403 : {
404 : return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
405 : }
406 : #endif // CHIP_DEVICE_CONFIG_ENABLE_WIFI_PDC
407 :
408 : /**
409 : * @brief Provide all the frequency bands supported by the Wi-Fi interface.
410 : *
411 : * The default implementation returns the 2.4 GHz band support.
412 : * Note: WiFi platforms should implement this function in their WiFiDriver to provide their complete device capabilities.
413 : *
414 : * The returned bit mask has values of WiFiBandEnum packed into the bits. For example:
415 : *
416 : * - Bit 0 = (WiFiBandEnum::k2g4 == 0) --> (1 << 0) == (1 << WiFiBandEnum::k2g4)
417 : * - Bit 2 = (WiFiBandEnum::k5g == 2) --> (1 << 2) == (1 << WiFiBandEnum::k5g)
418 : * - If both 2.4G and 5G are supported --> ((1 << k2g4) || (1 << k5g)) == (1 || 4) == 5
419 : *
420 : * On error, return 0 (no bands supported). This should never happen... Note that
421 : * certification tests will REQUIRE at least one bit set in the set.
422 : *
423 : * @return a bitmask of supported Wi-Fi bands where each bit is associated with a WiFiBandEnum value.
424 : */
425 0 : virtual uint32_t GetSupportedWiFiBandsMask() const
426 : {
427 : // Default to 2.4G support (100% example platform coverage as of Matter 1.3) listed.
428 0 : return static_cast<uint32_t>(1UL << chip::to_underlying(WiFiBandEnum::k2g4));
429 : }
430 :
431 2 : ~WiFiDriver() override = default;
432 : };
433 :
434 : class ThreadDriver : public Internal::WirelessDriver
435 : {
436 : public:
437 : class ScanCallback
438 : {
439 : public:
440 : /**
441 : * Indicates the scan is finished, and accepts a iterator of networks discovered.
442 : * - networks can be nullptr when no networks discovered, or error occurred during scanning the networks.
443 : * OnFinished() must be called in a thread-safe manner with CHIP stack. (e.g. using ScheduleWork or ScheduleLambda)
444 : * - Users can assume the networks will always be used (and Release will be called) inside this function call. However, the
445 : * iterator might be not fully consumed (i.e. There are too many networks scanned to fit in the buffer for scan response
446 : * message.)
447 : */
448 : virtual void OnFinished(Status err, CharSpan debugText, ThreadScanResponseIterator * networks) = 0;
449 :
450 3 : virtual ~ScanCallback() = default;
451 : };
452 :
453 : /**
454 : * @brief Adds or updates a Thread network on the device. The driver should fill the outDebugText field to pass any
455 : * human-readable messages to the client. The driver should reduce the size of outDebugText to 0 to omit it from the response
456 : * when no debug text needs to be delivered. On success, the driver should set outNetworkIndex to the index of the network just
457 : * added or updated. The value of the network index is discarded on failure.
458 : *
459 : * Note: The capacity of outDebugText passed by network commissioning cluster can be configured via
460 : * CHIP_CONFIG_NETWORK_COMMISSIONING_DEBUG_TEXT_BUFFER_SIZE.
461 : */
462 : virtual Status AddOrUpdateNetwork(ByteSpan operationalDataset, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex) = 0;
463 :
464 : /**
465 : * @brief Initializes a Thread network scan. callback->OnFinished must be called, on both success and error. Callback can
466 : * be called inside ScanNetworks.
467 : */
468 : virtual void ScanNetworks(ScanCallback * callback) = 0;
469 :
470 : /**
471 : * @brief Provide all of the Thread features supported by the Thread interface
472 : */
473 : virtual ThreadCapabilities GetSupportedThreadFeatures() = 0;
474 :
475 : /**
476 : * @brief Return the Thread version supported by the Thread interface
477 : */
478 : virtual uint16_t GetThreadVersion() = 0;
479 :
480 0 : ~ThreadDriver() override = default;
481 : };
482 :
483 : class EthernetDriver : public Internal::BaseDriver
484 : {
485 : // Ethernet driver does not have any special operations.
486 : };
487 :
488 : } // namespace NetworkCommissioning
489 : } // namespace DeviceLayer
490 : } // namespace chip
|