3 * Copyright (c) 2021 Project CHIP Authors
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include "network-commissioning.h"
22 #include <type_traits>
24 #include <gen/att-storage.h>
25 #include <gen/attribute-id.h>
26 #include <gen/attribute-type.h>
27 #include <gen/callback.h>
28 #include <gen/cluster-id.h>
29 #include <gen/command-id.h>
30 #include <gen/enums.h>
33 #include <lib/support/CodeUtils.h>
34 #include <lib/support/SafeInt.h>
35 #include <lib/support/Span.h>
36 #include <lib/support/logging/CHIPLogging.h>
37 #include <platform/CHIPDeviceLayer.h>
38 #include <platform/ConnectivityManager.h>
40 #if CHIP_DEVICE_CONFIG_ENABLE_THREAD
41 #include <platform/ThreadStackManager.h>
42 #endif // CHIP_DEVICE_CONFIG_ENABLE_THREAD
44 #include <transport/NetworkProvisioning.h>
46 // Include DeviceNetworkProvisioningDelegateImpl for WiFi provisioning.
47 // TODO: Enable wifi network should be done by ConnectivityManager. (Or other platform neutral interfaces)
48 #if defined(CHIP_DEVICE_LAYER_TARGET)
49 #define DEVICENETWORKPROVISIONING_HEADER <platform/CHIP_DEVICE_LAYER_TARGET/DeviceNetworkProvisioningDelegateImpl.h>
50 #include DEVICENETWORKPROVISIONING_HEADER
54 using namespace chip::app;
55 using namespace chip::app::clusters;
56 using namespace chip::app::clusters::NetworkCommissioning;
61 namespace NetworkCommissioning {
63 constexpr uint8_t kMaxNetworkIDLen = 32;
64 constexpr uint8_t kMaxThreadDatasetLen = 254; // As defined in Thread spec.
65 constexpr uint8_t kMaxWiFiSSIDLen = 32;
66 constexpr uint8_t kMaxWiFiCredentialsLen = 64;
67 constexpr uint8_t kMaxNetworks = 4;
69 // The temporary network id which will be used by thread networks in CHIP before we have the API for getting extpanid from dataset
71 #if CHIP_DEVICE_CONFIG_ENABLE_THREAD
72 constexpr uint8_t kTemporaryThreadNetworkId[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef };
75 enum class NetworkType : uint8_t
83 struct ThreadNetworkInfo
85 uint8_t mDataset[kMaxThreadDatasetLen];
89 struct WiFiNetworkInfo
91 uint8_t mSSID[kMaxWiFiSSIDLen + 1];
93 uint8_t mCredentials[kMaxWiFiCredentialsLen];
94 uint8_t mCredentialsLen;
99 uint8_t mNetworkID[kMaxNetworkIDLen];
100 uint8_t mNetworkIDLen;
102 NetworkType mNetworkType;
105 ThreadNetworkInfo mThread;
106 WiFiNetworkInfo mWiFi;
111 // The internal network info containing credentials. Need to find some better place to save these info.
112 NetworkInfo sNetworks[kMaxNetworks];
115 EmberAfNetworkCommissioningError OnAddThreadNetworkCommandCallbackInternal(app::Command *, EndpointId, ByteSpan operationalDataset,
116 uint64_t breadcrumb, uint32_t timeoutMs)
118 #if CHIP_DEVICE_CONFIG_ENABLE_THREAD
119 EmberAfNetworkCommissioningError err = EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_BOUNDS_EXCEEDED;
121 for (size_t i = 0; i < kMaxNetworks; i++)
123 if (sNetworks[i].mNetworkType == NetworkType::kUndefined)
125 VerifyOrExit(operationalDataset.size() <= sizeof(ThreadNetworkInfo::mDataset),
126 err = EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_OUT_OF_RANGE);
127 memcpy(sNetworks[i].mData.mThread.mDataset, operationalDataset.data(), operationalDataset.size());
129 using ThreadDatasetLenType = decltype(sNetworks[i].mData.mThread.mDatasetLen);
130 VerifyOrExit(chip::CanCastTo<ThreadDatasetLenType>(operationalDataset.size()),
131 err = EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_OUT_OF_RANGE);
132 sNetworks[i].mData.mThread.mDatasetLen = static_cast<ThreadDatasetLenType>(operationalDataset.size());
134 // A 64bit id for thread networks, currently, we are missing some API for getting the thread network extpanid from the
136 static_assert(sizeof(kTemporaryThreadNetworkId) <= sizeof(sNetworks[i].mNetworkID),
137 "TemporaryThreadNetworkId too long");
138 memcpy(sNetworks[i].mNetworkID, kTemporaryThreadNetworkId, sizeof(kTemporaryThreadNetworkId));
139 sNetworks[i].mNetworkIDLen = sizeof(kTemporaryThreadNetworkId);
141 sNetworks[i].mNetworkType = NetworkType::kThread;
142 sNetworks[i].mEnabled = false;
144 err = EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_SUCCESS;
150 // TODO: We should encode response command here.
152 ChipLogDetail(Zcl, "AddThreadNetwork: %d", err);
155 // The target does not supports ThreadNetwork. We should not add AddThreadNetwork command in that case then the upper layer will
156 // return "Command not found" error.
157 return EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_UNKNOWN_ERROR;
161 EmberAfNetworkCommissioningError OnAddWiFiNetworkCommandCallbackInternal(app::Command *, EndpointId, ByteSpan ssid,
162 ByteSpan credentials, uint64_t breadcrumb,
165 EmberAfNetworkCommissioningError err = EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_BOUNDS_EXCEEDED;
167 for (size_t i = 0; i < kMaxNetworks; i++)
169 if (sNetworks[i].mNetworkType == NetworkType::kUndefined)
171 VerifyOrExit(ssid.size() <= sizeof(sNetworks[i].mData.mWiFi.mSSID),
172 err = EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_OUT_OF_RANGE);
173 memcpy(sNetworks[i].mData.mWiFi.mSSID, ssid.data(), ssid.size());
175 using WiFiSSIDLenType = decltype(sNetworks[i].mData.mWiFi.mSSIDLen);
176 VerifyOrExit(chip::CanCastTo<WiFiSSIDLenType>(ssid.size()), err = EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_OUT_OF_RANGE);
177 sNetworks[i].mData.mWiFi.mSSIDLen = static_cast<WiFiSSIDLenType>(ssid.size());
179 VerifyOrExit(credentials.size() <= sizeof(sNetworks[i].mData.mWiFi.mCredentials),
180 err = EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_OUT_OF_RANGE);
181 memcpy(sNetworks[i].mData.mWiFi.mCredentials, credentials.data(), credentials.size());
183 using WiFiCredentialsLenType = decltype(sNetworks[i].mData.mWiFi.mCredentialsLen);
184 VerifyOrExit(chip::CanCastTo<WiFiCredentialsLenType>(ssid.size()),
185 err = EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_OUT_OF_RANGE);
186 sNetworks[i].mData.mWiFi.mCredentialsLen = static_cast<WiFiCredentialsLenType>(credentials.size());
188 VerifyOrExit(ssid.size() <= sizeof(sNetworks[i].mNetworkID), err = EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_OUT_OF_RANGE);
189 memcpy(sNetworks[i].mNetworkID, sNetworks[i].mData.mWiFi.mSSID, ssid.size());
191 using NetworkIDLenType = decltype(sNetworks[i].mNetworkIDLen);
192 VerifyOrExit(chip::CanCastTo<NetworkIDLenType>(ssid.size()), err = EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_OUT_OF_RANGE);
193 sNetworks[i].mNetworkIDLen = static_cast<NetworkIDLenType>(ssid.size());
195 sNetworks[i].mNetworkType = NetworkType::kWiFi;
196 sNetworks[i].mEnabled = false;
198 err = EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_SUCCESS;
203 VerifyOrExit(err == EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_SUCCESS, );
205 ChipLogDetail(Zcl, "WiFi provisioning data: SSID: %s", ssid);
207 // TODO: We should encode response command here.
209 ChipLogDetail(Zcl, "AddWiFiNetwork: %d", err);
214 CHIP_ERROR DoEnableNetwork(NetworkInfo * network)
216 switch (network->mNetworkType)
218 case NetworkType::kThread:
219 #if CHIP_DEVICE_CONFIG_ENABLE_THREAD
220 ReturnErrorOnFailure(DeviceLayer::ThreadStackMgr().SetThreadEnabled(false));
221 ReturnErrorOnFailure(
222 DeviceLayer::ThreadStackMgr().SetThreadProvision(network->mData.mThread.mDataset, network->mData.mThread.mDatasetLen));
223 ReturnErrorOnFailure(DeviceLayer::ThreadStackMgr().SetThreadEnabled(true));
225 return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
228 case NetworkType::kWiFi:
229 #if defined(CHIP_DEVICE_LAYER_TARGET)
231 // TODO: Currently, DeviceNetworkProvisioningDelegateImpl assumes that ssid and credentials are null terminated strings,
232 // which is not correct, this should be changed once we have better method for commissioning wifi networks.
233 DeviceLayer::DeviceNetworkProvisioningDelegateImpl deviceDelegate;
234 ReturnErrorOnFailure(deviceDelegate.ProvisionWiFi(reinterpret_cast<const char *>(network->mData.mWiFi.mSSID),
235 reinterpret_cast<const char *>(network->mData.mWiFi.mCredentials)));
239 return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
242 case NetworkType::kEthernet:
243 case NetworkType::kUndefined:
245 return CHIP_ERROR_NOT_IMPLEMENTED;
247 network->mEnabled = true;
248 return CHIP_NO_ERROR;
252 EmberAfNetworkCommissioningError OnEnableNetworkCommandCallbackInternal(app::Command *, EndpointId, ByteSpan networkID,
253 uint64_t breadcrumb, uint32_t timeoutMs)
256 EmberAfNetworkCommissioningError err = EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_NETWORK_ID_NOT_FOUND;
258 for (networkSeq = 0; networkSeq < kMaxNetworks; networkSeq++)
260 if (sNetworks[networkSeq].mNetworkIDLen == networkID.size() &&
261 sNetworks[networkSeq].mNetworkType != NetworkType::kUndefined &&
262 memcmp(sNetworks[networkSeq].mNetworkID, networkID.data(), networkID.size()) == 0)
264 // TODO: Currently, we cannot figure out the detailed error from network provisioning on DeviceLayer, we should
265 // implement this in device layer.
266 VerifyOrExit(DoEnableNetwork(&sNetworks[networkSeq]) == CHIP_NO_ERROR,
267 err = EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_UNKNOWN_ERROR);
268 ExitNow(err = EMBER_ZCL_NETWORK_COMMISSIONING_ERROR_SUCCESS);
271 // TODO: We should encode response command here.
276 } // namespace NetworkCommissioning
277 } // namespace clusters