1 /* 2 * Copyright (c) 2024, The OpenThread Authors. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Neither the name of the copyright holder nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /** 30 * @file 31 * This file includes definitions for the spinel based Thread controller. 32 */ 33 34 #ifndef OTBR_AGENT_NCP_SPINEL_HPP_ 35 #define OTBR_AGENT_NCP_SPINEL_HPP_ 36 37 #include <functional> 38 #include <memory> 39 40 #include <vector> 41 42 #include <openthread/dataset.h> 43 #include <openthread/error.h> 44 #include <openthread/link.h> 45 #include <openthread/thread.h> 46 #include <openthread/platform/dnssd.h> 47 48 #include "lib/spinel/spinel.h" 49 #include "lib/spinel/spinel_buffer.hpp" 50 #include "lib/spinel/spinel_driver.hpp" 51 #include "lib/spinel/spinel_encoder.hpp" 52 53 #include "common/task_runner.hpp" 54 #include "common/types.hpp" 55 #include "host/async_task.hpp" 56 #include "host/posix/infra_if.hpp" 57 #include "host/posix/netif.hpp" 58 #include "mdns/mdns.hpp" 59 60 namespace otbr { 61 namespace Host { 62 63 /** 64 * This interface is an observer to subscribe the network properties from NCP. 65 */ 66 class PropsObserver 67 { 68 public: 69 /** 70 * Updates the device role. 71 * 72 * @param[in] aRole The device role. 73 */ 74 virtual void SetDeviceRole(otDeviceRole aRole) = 0; 75 76 /** 77 * Updates the active dataset. 78 * 79 * @param[in] aActiveOpDatasetTlvs The active dataset tlvs. 80 */ 81 virtual void SetDatasetActiveTlvs(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs) = 0; 82 83 /** 84 * The destructor. 85 */ 86 virtual ~PropsObserver(void) = default; 87 }; 88 89 /** 90 * The class provides methods for controlling the Thread stack on the network co-processor (NCP). 91 */ 92 class NcpSpinel : public Netif::Dependencies, public InfraIf::Dependencies 93 { 94 public: 95 using Ip6AddressTableCallback = std::function<void(const std::vector<Ip6AddressInfo> &)>; 96 using Ip6MulticastAddressTableCallback = std::function<void(const std::vector<Ip6Address> &)>; 97 using NetifStateChangedCallback = std::function<void(bool)>; 98 using Ip6ReceiveCallback = std::function<void(const uint8_t *, uint16_t)>; 99 using InfraIfSendIcmp6NdCallback = std::function<void(uint32_t, const otIp6Address &, const uint8_t *, uint16_t)>; 100 101 /** 102 * Constructor. 103 */ 104 NcpSpinel(void); 105 106 /** 107 * Do the initialization. 108 * 109 * @param[in] aSpinelDriver A reference to the SpinelDriver instance that this object depends. 110 * @param[in] aObserver A reference to the Network properties observer. 111 */ 112 void Init(ot::Spinel::SpinelDriver &aSpinelDriver, PropsObserver &aObserver); 113 114 /** 115 * Do the de-initialization. 116 */ 117 void Deinit(void); 118 119 /** 120 * Returns the Co-processor version string. 121 */ GetCoprocessorVersion(void)122 const char *GetCoprocessorVersion(void) { return mSpinelDriver->GetVersion(); } 123 124 /** 125 * This method sets the active dataset on the NCP. 126 * 127 * If this method is called again before the previous call completed, no action will be taken. 128 * The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY. 129 * 130 * @param[in] aActiveOpDatasetTlvs A reference to the active operational dataset of the Thread network. 131 * @param[in] aAsyncTask A pointer to an async result to receive the result of this operation. 132 */ 133 void DatasetSetActiveTlvs(const otOperationalDatasetTlvs &aActiveOpDatasetTlvs, AsyncTaskPtr aAsyncTask); 134 135 /** 136 * This method instructs the NCP to send a MGMT_SET to set Thread Pending Operational Dataset. 137 * 138 * If this method is called again before the previous call completed, no action will be taken. 139 * The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY. 140 * 141 * @param[in] aPendingOpDatasetTlvsPtr A shared pointer to the pending operational dataset of the Thread network. 142 * @param[in] aAsyncTask A pointer to an async result to receive the result of this operation. 143 */ 144 void DatasetMgmtSetPending(std::shared_ptr<otOperationalDatasetTlvs> aPendingOpDatasetTlvsPtr, 145 AsyncTaskPtr aAsyncTask); 146 147 /** 148 * This method enableds/disables the IP6 on the NCP. 149 * 150 * If this method is called again before the previous call completed, no action will be taken. 151 * The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY. 152 * 153 * @param[in] aEnable TRUE to enable and FALSE to disable. 154 * @param[in] aAsyncTask A pointer to an async result to receive the result of this operation. 155 */ 156 void Ip6SetEnabled(bool aEnable, AsyncTaskPtr aAsyncTask); 157 158 /** 159 * This method sets the callback to receive the IPv6 address table from the NCP. 160 * 161 * The callback will be invoked when receiving an IPv6 address table from the NCP. When the 162 * callback is invoked, the callback MUST copy the otIp6AddressInfo objects and maintain it 163 * if it's not used immediately (within the callback). 164 * 165 * @param[in] aCallback The callback to handle the IP6 address table. 166 */ Ip6SetAddressCallback(const Ip6AddressTableCallback & aCallback)167 void Ip6SetAddressCallback(const Ip6AddressTableCallback &aCallback) { mIp6AddressTableCallback = aCallback; } 168 169 /** 170 * This method sets the callback to receive the IPv6 multicast address table from the NCP. 171 * 172 * @param[in] aCallback The callback to handle the IPv6 address table. 173 * 174 * The callback will be invoked when receiving an IPv6 multicast address table from the NCP. 175 * When the callback is invoked, the callback MUST copy the otIp6Address objects and maintain it 176 * if it's not used immediately (within the callback). 177 */ Ip6SetAddressMulticastCallback(const Ip6MulticastAddressTableCallback & aCallback)178 void Ip6SetAddressMulticastCallback(const Ip6MulticastAddressTableCallback &aCallback) 179 { 180 mIp6MulticastAddressTableCallback = aCallback; 181 } 182 183 /** 184 * This method sets the callback to receive IP6 datagrams. 185 * 186 * @param[in] aCallback The callback to receive IP6 datagrams. 187 */ Ip6SetReceiveCallback(const Ip6ReceiveCallback & aCallback)188 void Ip6SetReceiveCallback(const Ip6ReceiveCallback &aCallback) { mIp6ReceiveCallback = aCallback; } 189 190 /** 191 * This methods sends an IP6 datagram through the NCP. 192 * 193 * @param[in] aData A pointer to the beginning of the IP6 datagram. 194 * @param[in] aLength The length of the datagram. 195 * 196 * @retval OTBR_ERROR_NONE The datagram is sent to NCP successfully. 197 * @retval OTBR_ERROR_BUSY NcpSpinel is busy with other requests. 198 */ 199 otbrError Ip6Send(const uint8_t *aData, uint16_t aLength) override; 200 201 /** 202 * This method enableds/disables the Thread network on the NCP. 203 * 204 * If this method is called again before the previous call completed, no action will be taken. 205 * The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY. 206 * 207 * @param[in] aEnable TRUE to enable and FALSE to disable. 208 * @param[in] aAsyncTask A pointer to an async result to receive the result of this operation. 209 */ 210 void ThreadSetEnabled(bool aEnable, AsyncTaskPtr aAsyncTask); 211 212 /** 213 * This method instructs the device to leave the current network gracefully. 214 * 215 * If this method is called again before the previous call completed, no action will be taken. 216 * The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY. 217 * 218 * @param[in] aAsyncTask A pointer to an async result to receive the result of this operation. 219 */ 220 void ThreadDetachGracefully(AsyncTaskPtr aAsyncTask); 221 222 /** 223 * This method instructs the NCP to erase the persistent network info. 224 * 225 * If this method is called again before the previous call completed, no action will be taken. 226 * The new receiver @p aAsyncTask will be set a result OT_ERROR_BUSY. 227 * 228 * @param[in] aAsyncTask A pointer to an async result to receive the result of this operation. 229 */ 230 void ThreadErasePersistentInfo(AsyncTaskPtr aAsyncTask); 231 232 /** 233 * This method sets the callback invoked when the network interface state changes. 234 * 235 * @param[in] aCallback The callback invoked when the network interface state changes. 236 */ NetifSetStateChangedCallback(const NetifStateChangedCallback & aCallback)237 void NetifSetStateChangedCallback(const NetifStateChangedCallback &aCallback) 238 { 239 mNetifStateChangedCallback = aCallback; 240 } 241 242 /** 243 * This method sets the function to send an Icmp6 ND message on the infrastructure link. 244 * 245 * @param[in] aCallback The callback to send an Icmp6 ND message on the infrastructure link. 246 */ InfraIfSetIcmp6NdSendCallback(const InfraIfSendIcmp6NdCallback & aCallback)247 void InfraIfSetIcmp6NdSendCallback(const InfraIfSendIcmp6NdCallback &aCallback) 248 { 249 mInfraIfIcmp6NdCallback = aCallback; 250 } 251 252 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY 253 /** 254 * This method enables/disables the SRP Server on NCP. 255 * 256 * @param[in] aEnable A boolean to enable/disable the SRP server. 257 */ 258 void SrpServerSetEnabled(bool aEnabled); 259 260 /** 261 * This method enables/disables the auto-enable mode on SRP Server on NCP. 262 * 263 * @param[in] aEnable A boolean to enable/disable the SRP server. 264 */ 265 void SrpServerSetAutoEnableMode(bool aEnabled); 266 267 /** 268 * This method sets the dnssd state on NCP. 269 * 270 * @param[in] aState The dnssd state. 271 */ 272 void DnssdSetState(Mdns::Publisher::State aState); 273 274 /** 275 * This method sets the mDNS Publisher object. 276 * 277 * @param[in] aPublisher A pointer to the mDNS Publisher object. 278 */ SetMdnsPublisher(otbr::Mdns::Publisher * aPublisher)279 void SetMdnsPublisher(otbr::Mdns::Publisher *aPublisher) 280 { 281 mPublisher = aPublisher; 282 } 283 #endif // OTBR_ENABLE_SRP_ADVERTISING_PROXY 284 285 private: 286 using FailureHandler = std::function<void(otError)>; 287 288 static constexpr uint8_t kMaxTids = 16; 289 static constexpr uint16_t kCallbackDataMaxSize = sizeof(uint64_t); // Maximum size of a function pointer. 290 static constexpr uint16_t kMaxSubTypes = 8; // Maximum number of sub types in a MDNS service. 291 SafeInvoke(Function & aFunc,Args &&...aArgs)292 template <typename Function, typename... Args> static void SafeInvoke(Function &aFunc, Args &&...aArgs) 293 { 294 if (aFunc) 295 { 296 aFunc(std::forward<Args>(aArgs)...); 297 } 298 } 299 CallAndClear(AsyncTaskPtr & aResult,otError aError,const std::string & aErrorInfo="")300 static void CallAndClear(AsyncTaskPtr &aResult, otError aError, const std::string &aErrorInfo = "") 301 { 302 if (aResult) 303 { 304 aResult->SetResult(aError, aErrorInfo); 305 aResult = nullptr; 306 } 307 } 308 309 static otbrError SpinelDataUnpack(const uint8_t *aDataIn, spinel_size_t aDataLen, const char *aPackFormat, ...); 310 311 static void HandleReceivedFrame(const uint8_t *aFrame, 312 uint16_t aLength, 313 uint8_t aHeader, 314 bool &aSave, 315 void *aContext); 316 void HandleReceivedFrame(const uint8_t *aFrame, uint16_t aLength, uint8_t aHeader, bool &aShouldSaveFrame); 317 static void HandleSavedFrame(const uint8_t *aFrame, uint16_t aLength, void *aContext); 318 319 static otDeviceRole SpinelRoleToDeviceRole(spinel_net_role_t aRole); 320 321 void HandleNotification(const uint8_t *aFrame, uint16_t aLength); 322 void HandleResponse(spinel_tid_t aTid, const uint8_t *aFrame, uint16_t aLength); 323 void HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength); 324 void HandleValueInserted(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength); 325 void HandleValueRemoved(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength); 326 otbrError HandleResponseForPropSet(spinel_tid_t aTid, 327 spinel_prop_key_t aKey, 328 const uint8_t *aData, 329 uint16_t aLength); 330 otbrError HandleResponseForPropInsert(spinel_tid_t aTid, 331 spinel_command_t aCmd, 332 spinel_prop_key_t aKey, 333 const uint8_t *aData, 334 uint16_t aLength); 335 otbrError HandleResponseForPropRemove(spinel_tid_t aTid, 336 spinel_command_t aCmd, 337 spinel_prop_key_t aKey, 338 const uint8_t *aData, 339 uint16_t aLength); 340 341 otbrError Ip6MulAddrUpdateSubscription(const otIp6Address &aAddress, bool aIsAdded) override; 342 343 spinel_tid_t GetNextTid(void); 344 void FreeTidTableItem(spinel_tid_t aTid); 345 346 using EncodingFunc = std::function<otError(ot::Spinel::Encoder &aEncoder)>; 347 otError SendCommand(spinel_command_t aCmd, spinel_prop_key_t aKey, const EncodingFunc &aEncodingFunc); 348 otError SetProperty(spinel_prop_key_t aKey, const EncodingFunc &aEncodingFunc); 349 otError InsertProperty(spinel_prop_key_t aKey, const EncodingFunc &aEncodingFunc); 350 otError RemoveProperty(spinel_prop_key_t aKey, const EncodingFunc &aEncodingFunc); 351 352 otError SendEncodedFrame(void); 353 354 otError ParseIp6AddressTable(const uint8_t *aBuf, uint16_t aLength, std::vector<Ip6AddressInfo> &aAddressTable); 355 otError ParseIp6MulticastAddresses(const uint8_t *aBuf, uint16_t aLen, std::vector<Ip6Address> &aAddressList); 356 otError ParseIp6StreamNet(const uint8_t *aBuf, uint16_t aLen, const uint8_t *&aData, uint16_t &aDataLen); 357 otError ParseOperationalDatasetTlvs(const uint8_t *aBuf, uint16_t aLen, otOperationalDatasetTlvs &aDatasetTlvs); 358 otError ParseInfraIfIcmp6Nd(const uint8_t *aBuf, 359 uint8_t aLen, 360 uint32_t &aInfraIfIndex, 361 const otIp6Address *&aAddr, 362 const uint8_t *&aData, 363 uint16_t &aDataLen); 364 otError SendDnssdResult(otPlatDnssdRequestId aRequestId, const std::vector<uint8_t> &aCallbackData, otError aError); 365 366 otbrError SetInfraIf(uint32_t aInfraIfIndex, 367 bool aIsRunning, 368 const std::vector<Ip6Address> &aIp6Addresses) override; 369 otbrError HandleIcmp6Nd(uint32_t aInfraIfIndex, 370 const Ip6Address &aIp6Address, 371 const uint8_t *aData, 372 uint16_t aDataLen) override; 373 374 ot::Spinel::SpinelDriver *mSpinelDriver; 375 uint16_t mCmdTidsInUse; ///< Used transaction ids. 376 spinel_tid_t mCmdNextTid; ///< Next available transaction id. 377 378 spinel_prop_key_t mWaitingKeyTable[kMaxTids]; ///< The property keys of ongoing transactions. 379 spinel_command_t mCmdTable[kMaxTids]; ///< The mapping of spinel command and tids when the response 380 ///< is LAST_STATUS. 381 382 static constexpr uint16_t kTxBufferSize = 2048; 383 uint8_t mTxBuffer[kTxBufferSize]; 384 ot::Spinel::Buffer mNcpBuffer; 385 ot::Spinel::Encoder mEncoder; 386 spinel_iid_t mIid; /// < Interface Id used to in Spinel header 387 388 TaskRunner mTaskRunner; 389 390 PropsObserver *mPropsObserver; 391 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY 392 otbr::Mdns::Publisher *mPublisher; 393 #endif 394 395 AsyncTaskPtr mDatasetSetActiveTask; 396 AsyncTaskPtr mDatasetMgmtSetPendingTask; 397 AsyncTaskPtr mIp6SetEnabledTask; 398 AsyncTaskPtr mThreadSetEnabledTask; 399 AsyncTaskPtr mThreadDetachGracefullyTask; 400 AsyncTaskPtr mThreadErasePersistentInfoTask; 401 402 Ip6AddressTableCallback mIp6AddressTableCallback; 403 Ip6MulticastAddressTableCallback mIp6MulticastAddressTableCallback; 404 Ip6ReceiveCallback mIp6ReceiveCallback; 405 NetifStateChangedCallback mNetifStateChangedCallback; 406 InfraIfSendIcmp6NdCallback mInfraIfIcmp6NdCallback; 407 }; 408 409 } // namespace Host 410 } // namespace otbr 411 412 #endif // OTBR_AGENT_NCP_SPINEL_HPP_ 413