/* * * Copyright (c) 2021 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include using namespace ::chip::Inet; using namespace ::chip::DeviceLayer; using namespace ::chip::DeviceLayer::Internal; namespace chip { namespace DeviceLayer { namespace NetworkCommissioning { namespace { constexpr char kWiFiSSIDKeyName[] = "wifi-ssid"; constexpr char kWiFiCredentialsKeyName[] = "wifi-pass"; } // namespace CHIP_ERROR WiFiDriverImpl::Init(NetworkStatusChangeCallback * networkStatusChangeCallback) { size_t ssidLen = 0; size_t credentialsLen = 0; mSecurityType = NSAPI_SECURITY_WPA_WPA2; auto net_if = get_mbed_net_if(); if (net_if == nullptr) { ChipLogError(DeviceLayer, "No network interface available"); return CHIP_ERROR_NOT_IMPLEMENTED; } if (net_if->wifiInterface() != nullptr) { mWiFiInterface = net_if->wifiInterface(); // To add more listener to the interface mWiFiInterface->add_event_listener( [this](nsapi_event_t event, intptr_t data) { ConnectivityMgrImpl().AddQueueEvent(OnWiFiInterfaceEvent, event, data); }); mWiFiInterface->set_blocking(false); } mScanCallback = nullptr; mConnectCallback = nullptr; mScanSpecific = false; mStatusChangeCallback = networkStatusChangeCallback; mIp4Address = IPAddress::Any; mIp6Address = IPAddress::Any; auto err = PersistedStorage::KeyValueStoreMgr().Get(kWiFiSSIDKeyName, mSavedNetwork.ssid, sizeof(mSavedNetwork.ssid), &ssidLen); if (err == CHIP_NO_ERROR) { static_assert(sizeof(mSavedNetwork.ssid) <= UINT8_MAX, "Our length might not fit in mSavedNetwork.ssidLen"); mSavedNetwork.ssidLen = static_cast(ssidLen); } err = PersistedStorage::KeyValueStoreMgr().Get(kWiFiCredentialsKeyName, mSavedNetwork.credentials, sizeof(mSavedNetwork.credentials), &credentialsLen); if (err == CHIP_NO_ERROR) { static_assert(sizeof(mSavedNetwork.credentials) <= UINT8_MAX, "Our length might not fit in mSavedNetwork.credentialsLen"); mSavedNetwork.credentialsLen = static_cast(credentialsLen); } if (mSavedNetwork.ssidLen != 0 && mSavedNetwork.credentialsLen != 0) { mStagingNetwork = mSavedNetwork; } return CHIP_NO_ERROR; } void WiFiDriverImpl::Shutdown() { Network network; auto networks = GetNetworks(); for (; networks != nullptr && networks->Next(network);) { if (network.connected) { DisconnectNetwork(ByteSpan(network.networkID, network.networkIDLen)); } } if (networks != nullptr) { networks->Release(); } mScanCallback = nullptr; mConnectCallback = nullptr; mStatusChangeCallback = nullptr; mScanSpecific = false; mWiFiInterface = nullptr; mIp4Address = IPAddress::Any; mIp6Address = IPAddress::Any; mSecurityType = NSAPI_SECURITY_NONE; memset(mScanSSID, 0, sizeof(mScanSSID)); mStagingNetwork.ssidLen = 0; mStagingNetwork.credentialsLen = 0; mSavedNetwork.ssidLen = 0; mSavedNetwork.credentialsLen = 0; } CHIP_ERROR WiFiDriverImpl::CommitConfiguration() { ReturnErrorOnFailure(PersistedStorage::KeyValueStoreMgr().Put(kWiFiSSIDKeyName, mStagingNetwork.ssid, mStagingNetwork.ssidLen)); ReturnErrorOnFailure(PersistedStorage::KeyValueStoreMgr().Put(kWiFiCredentialsKeyName, mStagingNetwork.credentials, mStagingNetwork.credentialsLen)); mSavedNetwork = mStagingNetwork; return CHIP_NO_ERROR; } CHIP_ERROR WiFiDriverImpl::RevertConfiguration() { mStagingNetwork = mSavedNetwork; return CHIP_NO_ERROR; } bool WiFiDriverImpl::NetworkMatch(const WiFiNetwork & network, ByteSpan networkId) { return networkId.size() == network.ssidLen && memcmp(networkId.data(), network.ssid, network.ssidLen) == 0; } Status WiFiDriverImpl::AddOrUpdateNetwork(ByteSpan ssid, ByteSpan credentials, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex) { outDebugText.reduce_size(0); outNetworkIndex = 0; VerifyOrReturnError(mStagingNetwork.ssidLen == 0 || NetworkMatch(mStagingNetwork, ssid), Status::kBoundsExceeded); VerifyOrReturnError(credentials.size() <= sizeof(mStagingNetwork.credentials), Status::kOutOfRange); VerifyOrReturnError(ssid.size() <= sizeof(mStagingNetwork.ssid), Status::kOutOfRange); memcpy(mStagingNetwork.credentials, credentials.data(), credentials.size()); mStagingNetwork.credentialsLen = static_cast(credentials.size()); memcpy(mStagingNetwork.ssid, ssid.data(), ssid.size()); mStagingNetwork.ssidLen = static_cast(ssid.size()); return Status::kSuccess; } Status WiFiDriverImpl::RemoveNetwork(ByteSpan networkId, MutableCharSpan & outDebugText, uint8_t & outNetworkIndex) { outDebugText.reduce_size(0); outNetworkIndex = 0; VerifyOrReturnError(NetworkMatch(mStagingNetwork, networkId), Status::kNetworkIDNotFound); // Use empty ssid for representing invalid network mStagingNetwork.ssidLen = 0; return Status::kSuccess; } Status WiFiDriverImpl::ReorderNetwork(ByteSpan networkId, uint8_t index, MutableCharSpan & outDebugText) { outDebugText.reduce_size(0); // Only one network is supported now VerifyOrReturnError(index == 0, Status::kOutOfRange); VerifyOrReturnError(NetworkMatch(mStagingNetwork, networkId), Status::kNetworkIDNotFound); return Status::kSuccess; } void WiFiDriverImpl::ExecuteWiFiInterfaceChange(nsapi_connection_status_t status) { switch (status) { case NSAPI_STATUS_LOCAL_UP: ChipLogDetail(DeviceLayer, "Connection status - LOCAL_UP"); OnNetworkConnected(); break; case NSAPI_STATUS_GLOBAL_UP: ChipLogDetail(DeviceLayer, "Connection status - GLOBAL_UP"); OnNetworkConnected(); break; case NSAPI_STATUS_DISCONNECTED: ChipLogDetail(DeviceLayer, "Connection status - DISCONNECTED"); OnNetworkDisconnected(); break; case NSAPI_STATUS_CONNECTING: ChipLogDetail(DeviceLayer, "Connection status - CONNECTING"); OnNetworkConnecting(); break; default: ChipLogDetail(DeviceLayer, "Unknown connection status: 0x%08X", status); break; } } void WiFiDriverImpl::OnWiFiInterfaceEvent(nsapi_event_t event, intptr_t data) { if (event == NSAPI_EVENT_CONNECTION_STATUS_CHANGE) { GetInstance().ExecuteWiFiInterfaceChange((nsapi_connection_status_t) data); } } void WiFiDriverImpl::ExecuteConnectNetwork() { const char * ssid = mStagingNetwork.ssid; const char * key = mStagingNetwork.credentials; // Set WiFi credentials auto error = mWiFiInterface->set_credentials(ssid, key, mSecurityType); if (error) { ChipLogError(DeviceLayer, "Set WiFi credentials failed %d", error); if (mConnectCallback) { mConnectCallback->OnResult(Status::kUnknownError, CharSpan(), 0); mConnectCallback = nullptr; } return; } // Connect Wifi network error = mWiFiInterface->connect(); if (error) { ChipLogError(DeviceLayer, "Connect WiFi network failed %d", error); if (mConnectCallback) { mConnectCallback->OnResult(Status::kOtherConnectionFailure, CharSpan(), 0); mConnectCallback = nullptr; } return; } } void WiFiDriverImpl::OnConnectNetwork(intptr_t arg) { GetInstance().ExecuteConnectNetwork(); } void WiFiDriverImpl::ConnectNetwork(ByteSpan networkId, ConnectCallback * callback) { Status status = Status::kSuccess; VerifyOrExit(mWiFiInterface != nullptr, status = Status::kUnknownError); VerifyOrExit(NetworkMatch(mStagingNetwork, networkId), status = Status::kNetworkIDNotFound); VerifyOrExit(mConnectCallback == nullptr, status = Status::kUnknownError); ChipLogProgress(NetworkProvisioning, "Mbed WiFi driver connect network: SSID: %.*s", static_cast(networkId.size()), networkId.data()); mConnectCallback = callback; ConnectivityMgrImpl().AddTask(OnConnectNetwork, 0); exit: if (status != Status::kSuccess) { ChipLogError(DeviceLayer, "Connect WiFi network failed: 0x%x", int(status)); if (callback) { callback->OnResult(status, CharSpan(), 0); } } } void WiFiDriverImpl::DisconnectNetwork(ByteSpan networkId) { VerifyOrReturn(mWiFiInterface != nullptr, ChipLogError(DeviceLayer, "Wifi network not available")); VerifyOrReturn(NetworkMatch(mStagingNetwork, networkId), ChipLogError(DeviceLayer, "Network not found")); ChipLogProgress(NetworkProvisioning, "Mbed WiFi driver disconnect network: SSID: %.*s", static_cast(networkId.size()), networkId.data()); // Disconnect Wifi network auto error = mWiFiInterface->disconnect(); if (error) { ChipLogError(DeviceLayer, "Disconnect WiFi network failed %d", error); } } void WiFiDriverImpl::ExecuteScanNetwork() { uint16_t ap_number, ap_index = 0; WiFiAccessPoint * ap_buffer; MbedScanResponseIterator * scan_resp_iter; // Get APs number auto scan_result = mWiFiInterface->scan(nullptr, 0); if (scan_result <= 0) { if (scan_result == 0) { ChipLogProgress(DeviceLayer, "No AP found"); } else { ChipLogError(DeviceLayer, "Error scanning for APs: %d", scan_result); } if (mScanCallback) { mScanCallback->OnFinished(Status::kUnknownError, CharSpan(), nullptr); } goto exit; } ap_number = static_cast(scan_result); ap_buffer = new WiFiAccessPoint[ap_number]; if (ap_buffer == nullptr) { ChipLogError(DeviceLayer, "Can't malloc memory for AP list"); if (mScanCallback) { mScanCallback->OnFinished(Status::kUnknownError, CharSpan(), nullptr); } goto exit; } // Get APs details if (mWiFiInterface->scan(ap_buffer, ap_number) < 0) { if (mScanSpecific) { while (ap_index < ap_number) { if (strcmp(ap_buffer->get_ssid(), mScanSSID) == 0) { break; } ap_buffer++; ap_index++; } if (ap_index == ap_number) { ChipLogError(DeviceLayer, "Specific AP not found"); if (mScanCallback) { mScanCallback->OnFinished(Status::kNetworkNotFound, CharSpan(), nullptr); } goto exit; } ap_number = 1; } scan_resp_iter = new MbedScanResponseIterator(ap_number, ap_buffer); if (mScanCallback) { mScanCallback->OnFinished(Status::kSuccess, CharSpan(), scan_resp_iter); } } else { ChipLogError(DeviceLayer, "Scan Wifi AP failed"); if (mScanCallback) { mScanCallback->OnFinished(Status::kUnknownError, CharSpan(), nullptr); } goto exit; } exit: if (ap_buffer) { delete[] ap_buffer; } mScanCallback = nullptr; mScanSpecific = false; } void WiFiDriverImpl::OnScanNetwork(intptr_t arg) { GetInstance().ExecuteScanNetwork(); } void WiFiDriverImpl::ScanNetworks(ByteSpan ssid, WiFiDriver::ScanCallback * callback) { Status status = Status::kSuccess; VerifyOrExit(mWiFiInterface != nullptr, status = Status::kUnknownError); if (ssid.data()) { memset(mScanSSID, 0, sizeof(mScanSSID)); memcpy(mScanSSID, ssid.data(), ssid.size()); mScanSpecific = true; } mScanCallback = callback; ConnectivityMgrImpl().AddTask(OnScanNetwork, 0); exit: if (status != Status::kSuccess) { ChipLogError(DeviceLayer, "Scan WiFi networks failed: 0x%x", int(status)); if (callback) { callback->OnFinished(status, CharSpan(), nullptr); } } } CHIP_ERROR WiFiDriverImpl::SetLastDisconnectReason(const ChipDeviceEvent * event) { (void) event; mLastDisconnectedReason = 0; return CHIP_NO_ERROR; } int32_t WiFiDriverImpl::GetLastDisconnectReason() { return mLastDisconnectedReason; } size_t WiFiDriverImpl::WiFiNetworkIterator::Count() { return mDriver->mStagingNetwork.ssidLen == 0 ? 0 : 1; } bool WiFiDriverImpl::WiFiNetworkIterator::Next(Network & item) { if (mExhausted || mDriver->mStagingNetwork.ssidLen == 0) { return false; } memcpy(item.networkID, mDriver->mStagingNetwork.ssid, mDriver->mStagingNetwork.ssidLen); item.networkIDLen = mDriver->mStagingNetwork.ssidLen; item.connected = get_mbed_net_if()->wifiInterface()->get_connection_status() == NSAPI_STATUS_LOCAL_UP || get_mbed_net_if()->wifiInterface()->get_connection_status() == NSAPI_STATUS_GLOBAL_UP; mExhausted = true; return true; } void WiFiDriverImpl::OnNetworkConnected() { ChipLogDetail(DeviceLayer, "OnNetworkConnected"); if (mConnectCallback) { mConnectCallback->OnResult(Status::kSuccess, CharSpan(), 0); mConnectCallback = nullptr; } ChipDeviceEvent event; event.Type = DeviceEventType::kWiFiConnectivityChange; event.WiFiConnectivityChange.Result = kConnectivity_Established; ConnectivityMgrImpl().PostEvent(&event, true); // Update IP address SocketAddress address; auto error = mWiFiInterface->get_ip_address(&address); if (error != NSAPI_ERROR_OK) { if (mIp4Address != IPAddress::Any) { // Unexpected change, forward to the application mIp4Address = IPAddress::Any; ChipDeviceEvent event; event.Type = DeviceEventType::kInternetConnectivityChange; event.InternetConnectivityChange.IPv4 = kConnectivity_Lost; event.InternetConnectivityChange.IPv6 = kConnectivity_NoChange; event.InternetConnectivityChange.ipAddress = mIp4Address; ConnectivityMgrImpl().PostEvent(&event, true); ChipLogError(DeviceLayer, "Unexpected loss of Ip4 address"); } if (mIp6Address != IPAddress::Any) { // Unexpected change, forward to the application mIp6Address = IPAddress::Any; ChipDeviceEvent event; event.Type = DeviceEventType::kInternetConnectivityChange; event.InternetConnectivityChange.IPv4 = kConnectivity_NoChange; event.InternetConnectivityChange.IPv6 = kConnectivity_Lost; event.InternetConnectivityChange.ipAddress = mIp6Address; ConnectivityMgrImpl().PostEvent(&event, true); ChipLogError(DeviceLayer, "Unexpected loss of Ip6 address"); } } else { IPAddress addr; if (address.get_ip_version() == NSAPI_IPv4) { if (IPAddress::FromString(address.get_ip_address(), addr) && addr != mIp4Address) { mIp4Address = addr; ChipDeviceEvent event; event.Type = DeviceEventType::kInternetConnectivityChange; event.InternetConnectivityChange.IPv4 = kConnectivity_Established; event.InternetConnectivityChange.IPv6 = kConnectivity_NoChange; event.InternetConnectivityChange.ipAddress = mIp4Address; ConnectivityMgrImpl().PostEvent(&event, true); ChipLogProgress(DeviceLayer, "New Ip4 address set: %s", address.get_ip_address()); } error = mWiFiInterface->get_ipv6_link_local_address(&address); if (error) { if (mIp6Address != IPAddress::Any) { // Unexpected change, forward to the application mIp6Address = IPAddress::Any; ChipDeviceEvent event; event.Type = DeviceEventType::kInternetConnectivityChange; event.InternetConnectivityChange.IPv4 = kConnectivity_NoChange; event.InternetConnectivityChange.IPv6 = kConnectivity_Lost; event.InternetConnectivityChange.ipAddress = mIp6Address; ConnectivityMgrImpl().PostEvent(&event, true); ChipLogError(DeviceLayer, "Unexpected loss of Ip6 address"); } } else { if (IPAddress::FromString(address.get_ip_address(), addr) && addr != mIp6Address) { mIp6Address = addr; ChipDeviceEvent event; event.Type = DeviceEventType::kInternetConnectivityChange; event.InternetConnectivityChange.IPv4 = kConnectivity_NoChange; event.InternetConnectivityChange.IPv6 = kConnectivity_Established; event.InternetConnectivityChange.ipAddress = mIp6Address; ConnectivityMgrImpl().PostEvent(&event, true); ChipLogProgress(DeviceLayer, "New Ip6 address set %s", address.get_ip_address()); } } } else { if (IPAddress::FromString(address.get_ip_address(), addr) && addr != mIp6Address) { mIp6Address = addr; ChipDeviceEvent event; event.Type = DeviceEventType::kInternetConnectivityChange; event.InternetConnectivityChange.IPv4 = kConnectivity_NoChange; event.InternetConnectivityChange.IPv6 = kConnectivity_Established; event.InternetConnectivityChange.ipAddress = mIp6Address; ConnectivityMgrImpl().PostEvent(&event, true); ChipLogProgress(DeviceLayer, "New Ip6 address set %s", address.get_ip_address()); } } } } void WiFiDriverImpl::OnNetworkDisconnected() { ChipLogDetail(DeviceLayer, "OnStationDisconnected"); ChipDeviceEvent event; event.Type = DeviceEventType::kWiFiConnectivityChange; event.WiFiConnectivityChange.Result = kConnectivity_Lost; ConnectivityMgrImpl().PostEvent(&event, true); // Update IPv4 address if (mIp4Address != IPAddress::Any) { // Unexpected change, forward to the application mIp4Address = IPAddress::Any; ChipDeviceEvent event; event.Type = DeviceEventType::kInternetConnectivityChange; event.InternetConnectivityChange.IPv4 = kConnectivity_Lost; event.InternetConnectivityChange.IPv6 = kConnectivity_NoChange; event.InternetConnectivityChange.ipAddress = mIp4Address; ConnectivityMgrImpl().PostEvent(&event, true); ChipLogError(DeviceLayer, "Loss of Ip4 address"); } if (mIp6Address != IPAddress::Any) { // Unexpected change, forward to the application mIp6Address = IPAddress::Any; ChipDeviceEvent event; event.Type = DeviceEventType::kInternetConnectivityChange; event.InternetConnectivityChange.IPv4 = kConnectivity_NoChange; event.InternetConnectivityChange.IPv6 = kConnectivity_Lost; event.InternetConnectivityChange.ipAddress = mIp6Address; ConnectivityMgrImpl().PostEvent(&event, true); ChipLogError(DeviceLayer, "Loss of Ip6 address"); } } void WiFiDriverImpl::OnNetworkConnecting() { ChipLogDetail(DeviceLayer, "OnNetworkConnecting"); ChipDeviceEvent event; event.Type = DeviceEventType::kWiFiConnectivityChange; event.WiFiConnectivityChange.Result = kConnectivity_NoChange; ConnectivityMgrImpl().PostEvent(&event, true); } WiFiAuthSecurityType WiFiDriverImpl::NsapiToNetworkSecurity(nsapi_security_t nsapi_security) { switch (nsapi_security) { case NSAPI_SECURITY_NONE: return kWiFiSecurityType_None; case NSAPI_SECURITY_WEP: return kWiFiSecurityType_WEP; case NSAPI_SECURITY_WPA: return kWiFiSecurityType_WPAPersonal; case NSAPI_SECURITY_WPA2: return kWiFiSecurityType_WPA2Personal; case NSAPI_SECURITY_WPA_WPA2: return kWiFiSecurityType_WPA2MixedPersonal; case NSAPI_SECURITY_WPA2_ENT: return kWiFiSecurityType_WPA2Enterprise; case NSAPI_SECURITY_WPA3: return kWiFiSecurityType_WPA3Personal; case NSAPI_SECURITY_WPA3_WPA2: return kWiFiSecurityType_WPA3MixedPersonal; case NSAPI_SECURITY_PAP: case NSAPI_SECURITY_CHAP: case NSAPI_SECURITY_EAP_TLS: case NSAPI_SECURITY_PEAP: case NSAPI_SECURITY_UNKNOWN: default: return kWiFiSecurityType_NotSpecified; } } void WiFiDriverImpl::OnNetworkStatusChange() { // Network configuredNetwork; bool staEnabled = ConnectivityMgrImpl().IsWiFiStationEnabled(); bool staConnected = ConnectivityMgrImpl().IsWiFiStationConnected(); VerifyOrReturn(staEnabled && mStatusChangeCallback != nullptr); if (staConnected) { mStatusChangeCallback->OnNetworkingStatusChange( Status::kSuccess, MakeOptional(ByteSpan((const uint8_t *) mStagingNetwork.ssid, mStagingNetwork.ssidLen)), NullOptional); return; } mStatusChangeCallback->OnNetworkingStatusChange( Status::kUnknownError, MakeOptional(ByteSpan((const uint8_t *) mStagingNetwork.ssid, mStagingNetwork.ssidLen)), MakeOptional(GetLastDisconnectReason())); } } // namespace NetworkCommissioning } // namespace DeviceLayer } // namespace chip