| /* |
| * |
| * Copyright (c) 2020 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 <array> |
| #include <limits.h> |
| #include <string.h> |
| |
| #include "platform/internal/CHIPDeviceLayerInternal.h" |
| #include "platform/internal/DeviceNetworkInfo.h" |
| |
| #include "platform/PlatformManager.h" |
| #include "platform/ThreadStackManager.h" |
| #include "support/ErrorMacros.h" |
| #include "support/logging/CHIPLogging.h" |
| |
| #include "dbus/client/thread_api_dbus.hpp" |
| |
| using chip::DeviceLayer::Internal::DeviceNetworkInfo; |
| using otbr::DBus::ClientError; |
| using otbr::DBus::DeviceRole; |
| using otbr::DBus::LinkModeConfig; |
| using otbr::DBus::NeighborInfo; |
| |
| #ifndef CHIP_CONFIG_OTBR_CLIENT_ERROR_MIN |
| #define CHIP_CONFIG_OTBR_CLIENT_ERROR_MIN 8000000 |
| #endif |
| |
| #define OTBR_TO_CHIP_ERROR(x) \ |
| (x == ClientError::ERROR_NONE ? CHIP_NO_ERROR : _CHIP_ERROR(CHIP_CONFIG_OTBR_CLIENT_ERROR_MIN + static_cast<int>(x))) |
| |
| #define LogClientError(error) \ |
| do \ |
| { \ |
| if (error != ClientError::ERROR_NONE) \ |
| { \ |
| ChipLogError(DeviceLayer, __FILE__ " %d: Otbr ClientError %d", __LINE__, static_cast<int>(error)); \ |
| } \ |
| } while (0) |
| |
| constexpr int kDBusConnectionPollingTimeoutMS = 10; |
| |
| namespace chip { |
| namespace DeviceLayer { |
| |
| ThreadStackManagerImpl ThreadStackManagerImpl::sInstance; |
| |
| ThreadStackManagerImpl::ThreadStackManagerImpl() : mThreadApi(nullptr), mConnection(nullptr), mNetworkInfo(), mAttached(false) {} |
| |
| CHIP_ERROR ThreadStackManagerImpl::_InitThreadStack() |
| { |
| ClientError error; |
| DeviceRole role; |
| DBusError dbusError; |
| DBusConnection * dispatchConnection; |
| |
| dbus_error_init(&dbusError); |
| mConnection = UniqueDBusConnection(dbus_bus_get_private(DBUS_BUS_SYSTEM, &dbusError)); |
| |
| VerifyOrExit(mConnection != nullptr, error = ClientError::ERROR_DBUS); |
| mThreadApi = std::unique_ptr<otbr::DBus::ThreadApiDBus>(new otbr::DBus::ThreadApiDBus(mConnection.get())); |
| mThreadApi->AddDeviceRoleHandler([this](DeviceRole newRole) { this->_ThreadDevcieRoleChangedHandler(newRole); }); |
| |
| SuccessOrExit(error = mThreadApi->GetDeviceRole(role)); |
| _ThreadDevcieRoleChangedHandler(role); |
| mAttached = (role != DeviceRole::OTBR_DEVICE_ROLE_DETACHED && role != DeviceRole::OTBR_DEVICE_ROLE_DISABLED); |
| |
| dispatchConnection = mConnection.get(); |
| mDBusEventLoop = std::thread([dispatchConnection]() { |
| while (true) |
| { |
| // The dbus_connection_read_write will lock the connection until new message comes or timeout. |
| // This will block ot-br-posix APIs. Set timeout to 10ms so it can work. |
| // TODO: we should have a global event loop for dbus to take care of this. |
| dbus_connection_read_write_dispatch(dispatchConnection, kDBusConnectionPollingTimeoutMS); |
| } |
| }); |
| mDBusEventLoop.detach(); |
| exit: |
| dbus_error_free(&dbusError); |
| LogClientError(error); |
| return OTBR_TO_CHIP_ERROR(error); |
| } |
| |
| void ThreadStackManagerImpl::_ThreadDevcieRoleChangedHandler(DeviceRole role) |
| { |
| bool attached = (role != DeviceRole::OTBR_DEVICE_ROLE_DETACHED && role != DeviceRole::OTBR_DEVICE_ROLE_DISABLED); |
| ChipDeviceEvent event = ChipDeviceEvent{}; |
| |
| if (attached != mAttached) |
| { |
| event.Type = DeviceEventType::kThreadConnectivityChange; |
| event.ThreadConnectivityChange.Result = |
| attached ? ConnectivityChange::kConnectivity_Established : ConnectivityChange::kConnectivity_Lost; |
| PlatformMgr().PostEvent(&event); |
| } |
| |
| event.Type = DeviceEventType::kThreadStateChange; |
| event.ThreadStateChange.RoleChanged = true; |
| PlatformMgr().PostEvent(&event); |
| } |
| |
| void ThreadStackManagerImpl::_ProcessThreadActivity() {} |
| |
| static bool RouteMatch(const otbr::DBus::Ip6Prefix & prefix, const Inet::IPAddress & addr) |
| { |
| bool match = true; |
| const uint8_t * prefixBuffer; |
| const uint8_t * addrBuffer; |
| uint8_t wholeBytes = prefix.mLength / CHAR_BIT; |
| uint8_t pendingBits = prefix.mLength % CHAR_BIT; |
| |
| VerifyOrExit(addr.IsIPv6(), match = false); |
| VerifyOrExit(prefix.mLength > 0, match = false); |
| |
| prefixBuffer = static_cast<const uint8_t *>(&prefix.mPrefix[0]); |
| addrBuffer = reinterpret_cast<const uint8_t *>(&addr.Addr); |
| VerifyOrExit(memcmp(addrBuffer, prefixBuffer, wholeBytes) == 0, match = false); |
| if (pendingBits) |
| { |
| uint8_t mask = static_cast<uint8_t>(((UINT8_MAX >> pendingBits) << (CHAR_BIT - pendingBits))); |
| |
| VerifyOrExit((addrBuffer[wholeBytes] & mask) == (addrBuffer[wholeBytes] & mask), match = false); |
| } |
| VerifyOrExit(memcmp(addrBuffer, prefixBuffer, wholeBytes) == 0, match = false); |
| |
| exit: |
| return match; |
| } |
| |
| bool ThreadStackManagerImpl::_HaveRouteToAddress(const Inet::IPAddress & destAddr) |
| { |
| std::vector<otbr::DBus::ExternalRoute> routes; |
| bool match = false; |
| |
| VerifyOrExit(mThreadApi->GetExternalRoutes(routes) == ClientError::ERROR_NONE, match = false); |
| VerifyOrExit(_IsThreadAttached(), match = false); |
| VerifyOrExit(destAddr.IsIPv6LinkLocal(), match = true); |
| for (const auto & route : routes) |
| { |
| VerifyOrExit(!(match = RouteMatch(route.mPrefix, destAddr)), ); |
| } |
| |
| exit: |
| return match; |
| } |
| |
| void ThreadStackManagerImpl::_OnPlatformEvent(const ChipDeviceEvent * event) |
| { |
| (void) event; |
| // The otbr-agent processes the Thread state handling by itself so there |
| // isn't much to do in the Chip stack. |
| } |
| |
| CHIP_ERROR ThreadStackManagerImpl::_SetThreadProvision(const Internal::DeviceNetworkInfo & netInfo) |
| { |
| mNetworkInfo = netInfo; |
| |
| // post an event alerting other subsystems about change in provisioning state |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kServiceProvisioningChange; |
| event.ServiceProvisioningChange.IsServiceProvisioned = true; |
| PlatformMgr().PostEvent(&event); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR ThreadStackManagerImpl::_SetThreadProvision(const uint8_t * operationalDataset, size_t operationalDatasetLen) |
| { |
| mOperationalDatasetTlv = std::vector<uint8_t>(operationalDataset, operationalDataset + operationalDatasetLen); |
| |
| // post an event alerting other subsystems about change in provisioning state |
| ChipDeviceEvent event; |
| event.Type = DeviceEventType::kServiceProvisioningChange; |
| event.ServiceProvisioningChange.IsServiceProvisioned = true; |
| PlatformMgr().PostEvent(&event); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR ThreadStackManagerImpl::_GetThreadProvision(Internal::DeviceNetworkInfo & netInfo, bool includeCredentials) |
| { |
| netInfo = mNetworkInfo; |
| |
| if (!includeCredentials) |
| { |
| memset(&netInfo.ThreadMasterKey, 0, sizeof(netInfo.ThreadMasterKey)); |
| memset(&netInfo.ThreadPSKc, 0, sizeof(netInfo.ThreadPSKc)); |
| netInfo.FieldPresent.ThreadPSKc = false; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| bool ThreadStackManagerImpl::_IsThreadProvisioned() |
| { |
| return mNetworkInfo.ThreadNetworkName[0] != '\0'; |
| } |
| |
| void ThreadStackManagerImpl::_ErasePersistentInfo() |
| { |
| mNetworkInfo = Internal::DeviceNetworkInfo{}; |
| } |
| |
| bool ThreadStackManagerImpl::_IsThreadEnabled() |
| { |
| bool enabled = false; |
| DeviceRole role; |
| ClientError error; |
| |
| SuccessOrExit(error = mThreadApi->GetDeviceRole(role)); |
| enabled = (role != DeviceRole::OTBR_DEVICE_ROLE_DISABLED); |
| exit: |
| LogClientError(error); |
| return enabled; |
| } |
| |
| bool ThreadStackManagerImpl::_IsThreadAttached() |
| { |
| return mAttached; |
| } |
| |
| CHIP_ERROR ThreadStackManagerImpl::_SetThreadEnabled(bool val) |
| { |
| ClientError error = ClientError::ERROR_NONE; |
| |
| if (val) |
| { |
| if (mOperationalDatasetTlv.size() > 0) |
| { |
| SuccessOrExit(error = mThreadApi->SetActiveDatasetTlvs(mOperationalDatasetTlv)); |
| SuccessOrExit(error = mThreadApi->Attach([](ClientError result) { |
| // ThreadDevcieRoleChangedHandler should take care of this, so we don't emit another event. |
| ChipLogProgress(DeviceLayer, "Thread attach result %d", result); |
| })); |
| } |
| else |
| { |
| std::vector<uint8_t> masterkey(std::begin(mNetworkInfo.ThreadMasterKey), std::end(mNetworkInfo.ThreadMasterKey)); |
| std::vector<uint8_t> pskc; |
| uint64_t extPanId = UINT64_MAX; |
| uint32_t channelMask = UINT32_MAX; |
| |
| if (mNetworkInfo.FieldPresent.ThreadExtendedPANId) |
| { |
| extPanId = 0; |
| for (size_t i = 0; i < extPanId; i++) |
| { |
| extPanId <<= CHAR_BIT; |
| extPanId |= mNetworkInfo.ThreadExtendedPANId[i]; |
| } |
| } |
| if (mNetworkInfo.FieldPresent.ThreadPSKc) |
| { |
| pskc = std::vector<uint8_t>(std::begin(mNetworkInfo.ThreadPSKc), std::end(mNetworkInfo.ThreadPSKc)); |
| } |
| if (mNetworkInfo.ThreadChannel != Internal::kThreadChannel_NotSpecified) |
| { |
| channelMask = 1 << mNetworkInfo.ThreadChannel; |
| } |
| |
| if (mNetworkInfo.FieldPresent.ThreadMeshPrefix) |
| { |
| std::array<uint8_t, Internal::kThreadMeshPrefixLength> prefix; |
| |
| std::copy(std::begin(mNetworkInfo.ThreadMeshPrefix), std::end(mNetworkInfo.ThreadMeshPrefix), std::begin(prefix)); |
| SuccessOrExit(error = mThreadApi->SetMeshLocalPrefix(prefix)); |
| } |
| |
| mThreadApi->Attach(mNetworkInfo.ThreadNetworkName, mNetworkInfo.ThreadPANId, extPanId, masterkey, pskc, channelMask, |
| [](ClientError result) { ChipLogProgress(DeviceLayer, "Thread attach result %d", result); }); |
| } |
| } |
| else |
| { |
| mThreadApi->Reset(); |
| } |
| exit: |
| LogClientError(error); |
| return OTBR_TO_CHIP_ERROR(error); |
| } |
| |
| ConnectivityManager::ThreadDeviceType ThreadStackManagerImpl::_GetThreadDeviceType() |
| { |
| ClientError error; |
| DeviceRole role; |
| LinkModeConfig linkMode; |
| ConnectivityManager::ThreadDeviceType type = ConnectivityManager::ThreadDeviceType::kThreadDeviceType_NotSupported; |
| |
| SuccessOrExit(error = mThreadApi->GetDeviceRole(role)); |
| |
| switch (role) |
| { |
| case DeviceRole::OTBR_DEVICE_ROLE_DISABLED: |
| case DeviceRole::OTBR_DEVICE_ROLE_DETACHED: |
| break; |
| case DeviceRole::OTBR_DEVICE_ROLE_CHILD: |
| SuccessOrExit(error = mThreadApi->GetLinkMode(linkMode)); |
| if (!linkMode.mRxOnWhenIdle) |
| { |
| type = ConnectivityManager::ThreadDeviceType::kThreadDeviceType_SleepyEndDevice; |
| } |
| else |
| { |
| type = linkMode.mDeviceType ? ConnectivityManager::ThreadDeviceType::kThreadDeviceType_FullEndDevice |
| : ConnectivityManager::ThreadDeviceType::kThreadDeviceType_MinimalEndDevice; |
| } |
| break; |
| case DeviceRole::OTBR_DEVICE_ROLE_ROUTER: |
| case DeviceRole::OTBR_DEVICE_ROLE_LEADER: |
| type = ConnectivityManager::ThreadDeviceType::kThreadDeviceType_Router; |
| break; |
| default: |
| ChipLogError(DeviceLayer, "Unknown Thread role: %d", static_cast<int>(role)); |
| break; |
| } |
| |
| exit: |
| LogClientError(error); |
| return type; |
| } |
| |
| CHIP_ERROR ThreadStackManagerImpl::_SetThreadDeviceType(ConnectivityManager::ThreadDeviceType deviceType) |
| { |
| LinkModeConfig linkMode{ true, true, true }; |
| ClientError error = ClientError::ERROR_NONE; |
| |
| if (deviceType == ConnectivityManager::ThreadDeviceType::kThreadDeviceType_MinimalEndDevice) |
| { |
| linkMode.mNetworkData = false; |
| } |
| else if (deviceType == ConnectivityManager::ThreadDeviceType::kThreadDeviceType_SleepyEndDevice) |
| { |
| linkMode.mRxOnWhenIdle = false; |
| linkMode.mNetworkData = false; |
| } |
| |
| if (!linkMode.mNetworkData) |
| { |
| error = mThreadApi->SetLinkMode(linkMode); |
| } |
| |
| LogClientError(error); |
| return OTBR_TO_CHIP_ERROR(error); |
| } |
| |
| void ThreadStackManagerImpl::_GetThreadPollingConfig(ConnectivityManager::ThreadPollingConfig & pollingConfig) |
| { |
| (void) pollingConfig; |
| |
| ChipLogError(DeviceLayer, "Polling config is not supported on linux"); |
| } |
| |
| CHIP_ERROR ThreadStackManagerImpl::_SetThreadPollingConfig(const ConnectivityManager::ThreadPollingConfig & pollingConfig) |
| { |
| (void) pollingConfig; |
| |
| ChipLogError(DeviceLayer, "Polling config is not supported on linux"); |
| return CHIP_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| bool ThreadStackManagerImpl::_HaveMeshConnectivity() |
| { |
| DeviceRole role; |
| ClientError error; |
| bool hasConnectivity = false; |
| std::vector<NeighborInfo> neighbors; |
| |
| SuccessOrExit(error = mThreadApi->GetDeviceRole(role)); |
| VerifyOrExit(role != DeviceRole::OTBR_DEVICE_ROLE_DETACHED && role != DeviceRole::OTBR_DEVICE_ROLE_DISABLED, ); |
| if (role == DeviceRole::OTBR_DEVICE_ROLE_CHILD || role == DeviceRole::OTBR_DEVICE_ROLE_ROUTER) |
| { |
| hasConnectivity = true; |
| } |
| |
| SuccessOrExit(error = mThreadApi->GetNeighborTable(neighbors)); |
| for (const auto & neighbor : neighbors) |
| { |
| if (!neighbor.mIsChild) |
| { |
| hasConnectivity = true; |
| break; |
| } |
| } |
| |
| exit: |
| LogClientError(error); |
| return hasConnectivity; |
| } |
| |
| void ThreadStackManagerImpl::_OnMessageLayerActivityChanged(bool messageLayerIsActive) |
| { |
| (void) messageLayerIsActive; |
| } |
| |
| void ThreadStackManagerImpl::_OnCHIPoBLEAdvertisingStart() {} |
| |
| void ThreadStackManagerImpl::_OnCHIPoBLEAdvertisingStop() {} |
| |
| CHIP_ERROR ThreadStackManagerImpl::_GetAndLogThreadStatsCounters() |
| { |
| // TODO: implement after we decide on the profiling protocol |
| return CHIP_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| CHIP_ERROR ThreadStackManagerImpl::_GetAndLogThreadTopologyMinimal() |
| { |
| // TODO: implement after we decide on the profiling protocol |
| return CHIP_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| CHIP_ERROR ThreadStackManagerImpl::_GetAndLogThreadTopologyFull() |
| { |
| // TODO: implement after we decide on the profiling protocol |
| return CHIP_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| CHIP_ERROR ThreadStackManagerImpl::_GetPrimary802154MACAddress(uint8_t * buf) |
| { |
| uint64_t extAddr; |
| ClientError error; |
| SuccessOrExit(error = mThreadApi->GetExtendedAddress(extAddr)); |
| |
| for (size_t i = 0; i < sizeof(extAddr); i++) |
| { |
| buf[sizeof(uint64_t) - i - 1] = (extAddr & UINT8_MAX); |
| extAddr >>= CHAR_BIT; |
| } |
| |
| exit: |
| LogClientError(error); |
| return OTBR_TO_CHIP_ERROR(error); |
| } |
| |
| CHIP_ERROR ThreadStackManagerImpl::_GetFactoryAssignedEUI64(uint8_t (&buf)[8]) |
| { |
| return CHIP_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| CHIP_ERROR ThreadStackManagerImpl::_GetExternalIPv6Address(chip::Inet::IPAddress & addr) |
| { |
| return CHIP_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| CHIP_ERROR ThreadStackManagerImpl::_JoinerStart() |
| { |
| return CHIP_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| ThreadStackManager & ThreadStackMgr() |
| { |
| return chip::DeviceLayer::ThreadStackManagerImpl::sInstance; |
| } |
| |
| ThreadStackManagerImpl & ThreadStackMgrImpl() |
| { |
| return chip::DeviceLayer::ThreadStackManagerImpl::sInstance; |
| } |
| |
| } // namespace DeviceLayer |
| } // namespace chip |