blob: 02f454bb768ab97e4d1c9e0c5a83014e3d8590da [file]
/*
* Copyright (c) 2024, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file
* This file includes definitions for the spinel based Thread controller.
*/
#ifndef OTBR_AGENT_NCP_SPINEL_HPP_
#define OTBR_AGENT_NCP_SPINEL_HPP_
#include <functional>
#include <memory>
#include <vector>
#include <openthread/dataset.h>
#include <openthread/error.h>
#include <openthread/link.h>
#include <openthread/thread.h>
#include <openthread/platform/dnssd.h>
#include "lib/spinel/spinel.h"
#include "lib/spinel/spinel_buffer.hpp"
#include "lib/spinel/spinel_driver.hpp"
#include "lib/spinel/spinel_encoder.hpp"
#include "common/task_runner.hpp"
#include "common/types.hpp"
#include "host/async_task.hpp"
#include "host/posix/infra_if.hpp"
#include "host/posix/netif.hpp"
#include "mdns/mdns.hpp"
namespace otbr {
namespace Host {
/**
* This interface is an observer to subscribe the network properties from NCP.
*/
class PropsObserver
{
public:
/**
* Updates the device role.
*
* @param[in] aRole The device role.
*/
virtual void SetDeviceRole(otDeviceRole aRole) = 0;
/**
* Updates the active dataset.
*
* @param[in] aActiveOpDatasetTlvs The active dataset tlvs.
*/
virtual void SetDatasetActiveTlvs(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs) = 0;
/**
* The destructor.
*/
virtual ~PropsObserver(void) = default;
};
/**
* The class provides methods for controlling the Thread stack on the network co-processor (NCP).
*/
class NcpSpinel : public Netif::Dependencies, public InfraIf::Dependencies
{
public:
using Ip6AddressTableCallback = std::function<void(const std::vector<Ip6AddressInfo> &)>;
using Ip6MulticastAddressTableCallback = std::function<void(const std::vector<Ip6Address> &)>;
using NetifStateChangedCallback = std::function<void(bool)>;
using Ip6ReceiveCallback = std::function<void(const uint8_t *, uint16_t)>;
using InfraIfSendIcmp6NdCallback = std::function<void(uint32_t, const otIp6Address &, const uint8_t *, uint16_t)>;
/**
* Constructor.
*/
NcpSpinel(void);
/**
* Do the initialization.
*
* @param[in] aSpinelDriver A reference to the SpinelDriver instance that this object depends.
* @param[in] aObserver A reference to the Network properties observer.
*/
void Init(ot::Spinel::SpinelDriver &aSpinelDriver, PropsObserver &aObserver);
/**
* Do the de-initialization.
*/
void Deinit(void);
/**
* Returns the Co-processor version string.
*/
const char *GetCoprocessorVersion(void) { return mSpinelDriver->GetVersion(); }
/**
* This method sets the active dataset on the NCP.
*
* If this method is called again before the previous call completed, no action will be taken.
* The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY.
*
* @param[in] aActiveOpDatasetTlvs A reference to the active operational dataset of the Thread network.
* @param[in] aAsyncTask A pointer to an async result to receive the result of this operation.
*/
void DatasetSetActiveTlvs(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs, AsyncTaskPtr aAsyncTask);
/**
* This method instructs the NCP to send a MGMT_SET to set Thread Pending Operational Dataset.
*
* If this method is called again before the previous call completed, no action will be taken.
* The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY.
*
* @param[in] aPendingOpDatasetTlvsPtr A shared pointer to the pending operational dataset of the Thread network.
* @param[in] aAsyncTask A pointer to an async result to receive the result of this operation.
*/
void DatasetMgmtSetPending(std::shared_ptr<otOperationalDatasetTlvs> aPendingOpDatasetTlvsPtr,
AsyncTaskPtr aAsyncTask);
/**
* This method enableds/disables the IP6 on the NCP.
*
* If this method is called again before the previous call completed, no action will be taken.
* The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY.
*
* @param[in] aEnable TRUE to enable and FALSE to disable.
* @param[in] aAsyncTask A pointer to an async result to receive the result of this operation.
*/
void Ip6SetEnabled(bool aEnable, AsyncTaskPtr aAsyncTask);
/**
* This method sets the callback to receive the IPv6 address table from the NCP.
*
* The callback will be invoked when receiving an IPv6 address table from the NCP. When the
* callback is invoked, the callback MUST copy the otIp6AddressInfo objects and maintain it
* if it's not used immediately (within the callback).
*
* @param[in] aCallback The callback to handle the IP6 address table.
*/
void Ip6SetAddressCallback(const Ip6AddressTableCallback &aCallback) { mIp6AddressTableCallback = aCallback; }
/**
* This method sets the callback to receive the IPv6 multicast address table from the NCP.
*
* @param[in] aCallback The callback to handle the IPv6 address table.
*
* The callback will be invoked when receiving an IPv6 multicast address table from the NCP.
* When the callback is invoked, the callback MUST copy the otIp6Address objects and maintain it
* if it's not used immediately (within the callback).
*/
void Ip6SetAddressMulticastCallback(const Ip6MulticastAddressTableCallback &aCallback)
{
mIp6MulticastAddressTableCallback = aCallback;
}
/**
* This method sets the callback to receive IP6 datagrams.
*
* @param[in] aCallback The callback to receive IP6 datagrams.
*/
void Ip6SetReceiveCallback(const Ip6ReceiveCallback &aCallback) { mIp6ReceiveCallback = aCallback; }
/**
* This methods sends an IP6 datagram through the NCP.
*
* @param[in] aData A pointer to the beginning of the IP6 datagram.
* @param[in] aLength The length of the datagram.
*
* @retval OTBR_ERROR_NONE The datagram is sent to NCP successfully.
* @retval OTBR_ERROR_BUSY NcpSpinel is busy with other requests.
*/
otbrError Ip6Send(const uint8_t *aData, uint16_t aLength) override;
/**
* This method enableds/disables the Thread network on the NCP.
*
* If this method is called again before the previous call completed, no action will be taken.
* The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY.
*
* @param[in] aEnable TRUE to enable and FALSE to disable.
* @param[in] aAsyncTask A pointer to an async result to receive the result of this operation.
*/
void ThreadSetEnabled(bool aEnable, AsyncTaskPtr aAsyncTask);
/**
* This method instructs the device to leave the current network gracefully.
*
* If this method is called again before the previous call completed, no action will be taken.
* The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY.
*
* @param[in] aAsyncTask A pointer to an async result to receive the result of this operation.
*/
void ThreadDetachGracefully(AsyncTaskPtr aAsyncTask);
/**
* This method instructs the NCP to erase the persistent network info.
*
* If this method is called again before the previous call completed, no action will be taken.
* The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY.
*
* @param[in] aAsyncTask A pointer to an async result to receive the result of this operation.
*/
void ThreadErasePersistentInfo(AsyncTaskPtr aAsyncTask);
/**
* This method sets the callback invoked when the network interface state changes.
*
* @param[in] aCallback The callback invoked when the network interface state changes.
*/
void NetifSetStateChangedCallback(const NetifStateChangedCallback &aCallback)
{
mNetifStateChangedCallback = aCallback;
}
/**
* This method sets the function to send an Icmp6 ND message on the infrastructure link.
*
* @param[in] aCallback The callback to send an Icmp6 ND message on the infrastructure link.
*/
void InfraIfSetIcmp6NdSendCallback(const InfraIfSendIcmp6NdCallback &aCallback)
{
mInfraIfIcmp6NdCallback = aCallback;
}
#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
/**
* This method enables/disables the SRP Server on NCP.
*
* @param[in] aEnable A boolean to enable/disable the SRP server.
*/
void SrpServerSetEnabled(bool aEnabled);
/**
* This method enables/disables the auto-enable mode on SRP Server on NCP.
*
* @param[in] aEnable A boolean to enable/disable the SRP server.
*/
void SrpServerSetAutoEnableMode(bool aEnabled);
/**
* This method sets the dnssd state on NCP.
*
* @param[in] aState The dnssd state.
*/
void DnssdSetState(Mdns::Publisher::State aState);
/**
* This method sets the mDNS Publisher object.
*
* @param[in] aPublisher A pointer to the mDNS Publisher object.
*/
void SetMdnsPublisher(otbr::Mdns::Publisher *aPublisher)
{
mPublisher = aPublisher;
}
#endif // OTBR_ENABLE_SRP_ADVERTISING_PROXY
private:
using FailureHandler = std::function<void(otError)>;
static constexpr uint8_t kMaxTids = 16;
static constexpr uint16_t kCallbackDataMaxSize = sizeof(uint64_t); // Maximum size of a function pointer.
static constexpr uint16_t kMaxSubTypes = 8; // Maximum number of sub types in a MDNS service.
template <typename Function, typename... Args> static void SafeInvoke(Function &aFunc, Args &&...aArgs)
{
if (aFunc)
{
aFunc(std::forward<Args>(aArgs)...);
}
}
static void CallAndClear(AsyncTaskPtr &aResult, otError aError, const std::string &aErrorInfo = "")
{
if (aResult)
{
aResult->SetResult(aError, aErrorInfo);
aResult = nullptr;
}
}
static otbrError SpinelDataUnpack(const uint8_t *aDataIn, spinel_size_t aDataLen, const char *aPackFormat, ...);
static void HandleReceivedFrame(const uint8_t *aFrame,
uint16_t aLength,
uint8_t aHeader,
bool &aSave,
void *aContext);
void HandleReceivedFrame(const uint8_t *aFrame, uint16_t aLength, uint8_t aHeader, bool &aShouldSaveFrame);
static void HandleSavedFrame(const uint8_t *aFrame, uint16_t aLength, void *aContext);
static otDeviceRole SpinelRoleToDeviceRole(spinel_net_role_t aRole);
void HandleNotification(const uint8_t *aFrame, uint16_t aLength);
void HandleResponse(spinel_tid_t aTid, const uint8_t *aFrame, uint16_t aLength);
void HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength);
void HandleValueInserted(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength);
void HandleValueRemoved(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength);
otbrError HandleResponseForPropSet(spinel_tid_t aTid,
spinel_prop_key_t aKey,
const uint8_t *aData,
uint16_t aLength);
otbrError HandleResponseForPropInsert(spinel_tid_t aTid,
spinel_command_t aCmd,
spinel_prop_key_t aKey,
const uint8_t *aData,
uint16_t aLength);
otbrError HandleResponseForPropRemove(spinel_tid_t aTid,
spinel_command_t aCmd,
spinel_prop_key_t aKey,
const uint8_t *aData,
uint16_t aLength);
otbrError Ip6MulAddrUpdateSubscription(const otIp6Address &aAddress, bool aIsAdded) override;
spinel_tid_t GetNextTid(void);
void FreeTidTableItem(spinel_tid_t aTid);
using EncodingFunc = std::function<otError(ot::Spinel::Encoder &aEncoder)>;
otError SendCommand(spinel_command_t aCmd, spinel_prop_key_t aKey, const EncodingFunc &aEncodingFunc);
otError SetProperty(spinel_prop_key_t aKey, const EncodingFunc &aEncodingFunc);
otError InsertProperty(spinel_prop_key_t aKey, const EncodingFunc &aEncodingFunc);
otError RemoveProperty(spinel_prop_key_t aKey, const EncodingFunc &aEncodingFunc);
otError SendEncodedFrame(void);
otError ParseIp6AddressTable(const uint8_t *aBuf, uint16_t aLength, std::vector<Ip6AddressInfo> &aAddressTable);
otError ParseIp6MulticastAddresses(const uint8_t *aBuf, uint16_t aLen, std::vector<Ip6Address> &aAddressList);
otError ParseIp6StreamNet(const uint8_t *aBuf, uint16_t aLen, const uint8_t *&aData, uint16_t &aDataLen);
otError ParseOperationalDatasetTlvs(const uint8_t *aBuf, uint16_t aLen, otOperationalDatasetTlvs &aDatasetTlvs);
otError ParseInfraIfIcmp6Nd(const uint8_t *aBuf,
uint8_t aLen,
uint32_t &aInfraIfIndex,
const otIp6Address *&aAddr,
const uint8_t *&aData,
uint16_t &aDataLen);
otError SendDnssdResult(otPlatDnssdRequestId aRequestId, const std::vector<uint8_t> &aCallbackData, otError aError);
otbrError SetInfraIf(uint32_t aInfraIfIndex,
bool aIsRunning,
const std::vector<Ip6Address> &aIp6Addresses) override;
otbrError HandleIcmp6Nd(uint32_t aInfraIfIndex,
const Ip6Address &aIp6Address,
const uint8_t *aData,
uint16_t aDataLen) override;
ot::Spinel::SpinelDriver *mSpinelDriver;
uint16_t mCmdTidsInUse; ///< Used transaction ids.
spinel_tid_t mCmdNextTid; ///< Next available transaction id.
spinel_prop_key_t mWaitingKeyTable[kMaxTids]; ///< The property keys of ongoing transactions.
spinel_command_t mCmdTable[kMaxTids]; ///< The mapping of spinel command and tids when the response
///< is LAST_STATUS.
static constexpr uint16_t kTxBufferSize = 2048;
uint8_t mTxBuffer[kTxBufferSize];
ot::Spinel::Buffer mNcpBuffer;
ot::Spinel::Encoder mEncoder;
spinel_iid_t mIid; /// < Interface Id used to in Spinel header
TaskRunner mTaskRunner;
PropsObserver *mPropsObserver;
#if OTBR_ENABLE_SRP_ADVERTISING_PROXY
otbr::Mdns::Publisher *mPublisher;
#endif
AsyncTaskPtr mDatasetSetActiveTask;
AsyncTaskPtr mDatasetMgmtSetPendingTask;
AsyncTaskPtr mIp6SetEnabledTask;
AsyncTaskPtr mThreadSetEnabledTask;
AsyncTaskPtr mThreadDetachGracefullyTask;
AsyncTaskPtr mThreadErasePersistentInfoTask;
Ip6AddressTableCallback mIp6AddressTableCallback;
Ip6MulticastAddressTableCallback mIp6MulticastAddressTableCallback;
Ip6ReceiveCallback mIp6ReceiveCallback;
NetifStateChangedCallback mNetifStateChangedCallback;
InfraIfSendIcmp6NdCallback mInfraIfIcmp6NdCallback;
};
} // namespace Host
} // namespace otbr
#endif // OTBR_AGENT_NCP_SPINEL_HPP_