blob: 8d68b40490205952187c05d92212296c324883f9 [file] [log] [blame]
/*
*
* 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/CodeUtils.h"
#include "support/logging/CHIPLogging.h"
#include "dbus/client/thread_api_dbus.hpp"
using otbr::DBus::ClientError;
using otbr::DBus::DeviceRole;
using otbr::DBus::LinkModeConfig;
#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), 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));
if (mConnection == nullptr)
{
dbus_error_free(&dbusError);
error = ClientError::ERROR_DBUS;
LogClientError(error);
return OTBR_TO_CHIP_ERROR(error);
}
mThreadApi = std::unique_ptr<otbr::DBus::ThreadApiDBus>(new otbr::DBus::ThreadApiDBus(mConnection.get()));
mThreadApi->AddDeviceRoleHandler([this](DeviceRole newRole) { this->_ThreadDevcieRoleChangedHandler(newRole); });
if (mThreadApi->GetDeviceRole(role) != ClientError::ERROR_NONE)
{
dbus_error_free(&dbusError);
error = ClientError::ERROR_DBUS;
LogClientError(error);
return OTBR_TO_CHIP_ERROR(error);
}
_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();
dbus_error_free(&dbusError);
return CHIP_NO_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)
{
const uint8_t * prefixBuffer;
const uint8_t * addrBuffer;
uint8_t wholeBytes = prefix.mLength / CHAR_BIT;
uint8_t pendingBits = prefix.mLength % CHAR_BIT;
if (!addr.IsIPv6() || prefix.mLength == 0)
{
return false;
}
prefixBuffer = static_cast<const uint8_t *>(&prefix.mPrefix[0]);
addrBuffer = reinterpret_cast<const uint8_t *>(&addr.Addr);
if (pendingBits)
{
uint8_t mask = static_cast<uint8_t>(((UINT8_MAX >> pendingBits) << (CHAR_BIT - pendingBits)));
if ((addrBuffer[wholeBytes] & mask) != (addrBuffer[wholeBytes] & mask))
{
return false;
}
}
return memcmp(addrBuffer, prefixBuffer, wholeBytes);
}
bool ThreadStackManagerImpl::_HaveRouteToAddress(const Inet::IPAddress & destAddr)
{
// TODO: Remove Weave legacy APIs
std::vector<otbr::DBus::ExternalRoute> routes;
bool match = false;
if (mThreadApi == nullptr || !_IsThreadAttached())
{
return false;
}
if (destAddr.IsIPv6LinkLocal())
{
return true;
}
if (mThreadApi->GetExternalRoutes(routes) != ClientError::ERROR_NONE)
{
return false;
}
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(ByteSpan netInfo)
{
VerifyOrReturnError(mThreadApi != nullptr, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(Thread::OperationalDataset::IsValid(netInfo), CHIP_ERROR_INVALID_ARGUMENT);
std::vector<uint8_t> data(netInfo.data(), netInfo.data() + netInfo.size());
ReturnErrorOnFailure(OTBR_TO_CHIP_ERROR(mThreadApi->SetActiveDatasetTlvs(data)));
// 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(ByteSpan & netInfo)
{
std::vector<uint8_t> data(Thread::kSizeOperationalDataset);
VerifyOrReturnError(mThreadApi != nullptr, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorOnFailure(OTBR_TO_CHIP_ERROR(mThreadApi->GetActiveDatasetTlvs(data)));
ReturnErrorOnFailure(mDataset.Init(ByteSpan(data.data(), data.size())));
netInfo = mDataset.AsByteSpan();
return CHIP_NO_ERROR;
}
bool ThreadStackManagerImpl::_IsThreadProvisioned()
{
return static_cast<Thread::OperationalDataset &>(mDataset).IsCommissioned();
}
void ThreadStackManagerImpl::_ErasePersistentInfo()
{
static_cast<Thread::OperationalDataset &>(mDataset).Clear();
}
bool ThreadStackManagerImpl::_IsThreadEnabled()
{
bool enabled = false;
DeviceRole role;
ClientError error;
if (mThreadApi == nullptr)
{
return false;
}
error = mThreadApi->GetDeviceRole(role);
if (error != ClientError::ERROR_NONE)
{
LogClientError(error);
return false;
}
enabled = (role != DeviceRole::OTBR_DEVICE_ROLE_DISABLED);
return enabled;
}
bool ThreadStackManagerImpl::_IsThreadAttached()
{
return mAttached;
}
CHIP_ERROR ThreadStackManagerImpl::_SetThreadEnabled(bool val)
{
VerifyOrReturnError(mThreadApi != nullptr, CHIP_ERROR_INCORRECT_STATE);
if (val)
{
ReturnErrorOnFailure(OTBR_TO_CHIP_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
{
mThreadApi->Reset();
}
return CHIP_NO_ERROR;
}
ConnectivityManager::ThreadDeviceType ThreadStackManagerImpl::_GetThreadDeviceType()
{
DeviceRole role;
LinkModeConfig linkMode;
ConnectivityManager::ThreadDeviceType type = ConnectivityManager::ThreadDeviceType::kThreadDeviceType_NotSupported;
if (mThreadApi == nullptr || mThreadApi->GetDeviceRole(role) != ClientError::ERROR_NONE)
{
ChipLogError(DeviceLayer, "Cannot get device role with Thread api client");
return ConnectivityManager::ThreadDeviceType::kThreadDeviceType_NotSupported;
}
switch (role)
{
case DeviceRole::OTBR_DEVICE_ROLE_DISABLED:
case DeviceRole::OTBR_DEVICE_ROLE_DETACHED:
return ConnectivityManager::ThreadDeviceType::kThreadDeviceType_NotSupported;
case DeviceRole::OTBR_DEVICE_ROLE_CHILD:
if (mThreadApi->GetLinkMode(linkMode) != ClientError::ERROR_NONE)
{
ChipLogError(DeviceLayer, "Cannot get link mode with Thread api client");
return ConnectivityManager::ThreadDeviceType::kThreadDeviceType_NotSupported;
}
if (!linkMode.mRxOnWhenIdle)
{
type = ConnectivityManager::ThreadDeviceType::kThreadDeviceType_SleepyEndDevice;
}
else
{
type = linkMode.mDeviceType ? ConnectivityManager::ThreadDeviceType::kThreadDeviceType_FullEndDevice
: ConnectivityManager::ThreadDeviceType::kThreadDeviceType_MinimalEndDevice;
}
return type;
case DeviceRole::OTBR_DEVICE_ROLE_ROUTER:
case DeviceRole::OTBR_DEVICE_ROLE_LEADER:
return ConnectivityManager::ThreadDeviceType::kThreadDeviceType_Router;
default:
ChipLogError(DeviceLayer, "Unknown Thread role: %d", static_cast<int>(role));
return ConnectivityManager::ThreadDeviceType::kThreadDeviceType_NotSupported;
break;
}
}
CHIP_ERROR ThreadStackManagerImpl::_SetThreadDeviceType(ConnectivityManager::ThreadDeviceType deviceType)
{
LinkModeConfig linkMode{ true, true, true };
ClientError error = ClientError::ERROR_NONE;
VerifyOrReturnError(mThreadApi != nullptr, CHIP_ERROR_INCORRECT_STATE);
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()
{
// TODO: Remove Weave legacy APIs
// For a leader with a child, the child is considered to have mesh connectivity
// and the leader is not, which is a very confusing definition.
// This API is Weave legacy and should be removed.
ChipLogError(DeviceLayer, "HaveMeshConnectivity has confusing behavior and shouldn't be called");
return CHIP_ERROR_NOT_IMPLEMENTED;
}
void ThreadStackManagerImpl::_OnMessageLayerActivityChanged(bool messageLayerIsActive)
{
(void) messageLayerIsActive;
}
CHIP_ERROR ThreadStackManagerImpl::_GetAndLogThreadStatsCounters()
{
// TODO: Remove Weave legacy APIs
return CHIP_ERROR_NOT_IMPLEMENTED;
}
CHIP_ERROR ThreadStackManagerImpl::_GetAndLogThreadTopologyMinimal()
{
// TODO: Remove Weave legacy APIs
return CHIP_ERROR_NOT_IMPLEMENTED;
}
CHIP_ERROR ThreadStackManagerImpl::_GetAndLogThreadTopologyFull()
{
// TODO: Remove Weave legacy APIs
return CHIP_ERROR_NOT_IMPLEMENTED;
}
CHIP_ERROR ThreadStackManagerImpl::_GetPrimary802154MACAddress(uint8_t * buf)
{
uint64_t extAddr;
VerifyOrReturnError(mThreadApi != nullptr, CHIP_ERROR_INCORRECT_STATE);
ReturnErrorOnFailure(OTBR_TO_CHIP_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;
}
return CHIP_NO_ERROR;
}
CHIP_ERROR ThreadStackManagerImpl::_GetExternalIPv6Address(chip::Inet::IPAddress & addr)
{
// TODO: Remove Weave legacy APIs
return CHIP_ERROR_NOT_IMPLEMENTED;
}
CHIP_ERROR ThreadStackManagerImpl::_GetPollPeriod(uint32_t & buf)
{
// TODO: Remove Weave legacy APIs
return CHIP_ERROR_NOT_IMPLEMENTED;
}
CHIP_ERROR ThreadStackManagerImpl::_JoinerStart()
{
// TODO: Remove Weave legacy APIs
return CHIP_ERROR_NOT_IMPLEMENTED;
}
ThreadStackManager & ThreadStackMgr()
{
return chip::DeviceLayer::ThreadStackManagerImpl::sInstance;
}
ThreadStackManagerImpl & ThreadStackMgrImpl()
{
return chip::DeviceLayer::ThreadStackManagerImpl::sInstance;
}
} // namespace DeviceLayer
} // namespace chip