• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *    Copyright (c) 2020, 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 #define OTBR_LOG_TAG "UTILS"
30 
31 #include "utils/thread_helper.hpp"
32 
33 #include <assert.h>
34 #include <limits.h>
35 #include <string.h>
36 #include <time.h>
37 
38 #include <string>
39 
40 #include <openthread/border_router.h>
41 #include <openthread/channel_manager.h>
42 #include <openthread/jam_detection.h>
43 #include <openthread/joiner.h>
44 #include <openthread/thread_ftd.h>
45 #include <openthread/platform/radio.h>
46 
47 #include "common/byteswap.hpp"
48 #include "common/code_utils.hpp"
49 #include "common/logging.hpp"
50 #include "common/tlv.hpp"
51 #include "ncp/ncp_openthread.hpp"
52 
53 namespace otbr {
54 namespace agent {
55 namespace {
FindTlv(uint8_t aTlvType,const uint8_t * aTlvs,int aTlvsSize)56 const Tlv *FindTlv(uint8_t aTlvType, const uint8_t *aTlvs, int aTlvsSize)
57 {
58     const Tlv *result = nullptr;
59 
60     for (const Tlv *tlv = reinterpret_cast<const Tlv *>(aTlvs);
61          reinterpret_cast<const uint8_t *>(tlv) + sizeof(Tlv) < aTlvs + aTlvsSize; tlv = tlv->GetNext())
62     {
63         if (tlv->GetType() == aTlvType)
64         {
65             ExitNow(result = tlv);
66         }
67     }
68 
69 exit:
70     return result;
71 }
72 } // namespace
73 
ThreadHelper(otInstance * aInstance,otbr::Ncp::ControllerOpenThread * aNcp)74 ThreadHelper::ThreadHelper(otInstance *aInstance, otbr::Ncp::ControllerOpenThread *aNcp)
75     : mInstance(aInstance)
76     , mNcp(aNcp)
77 {
78 }
79 
StateChangedCallback(otChangedFlags aFlags)80 void ThreadHelper::StateChangedCallback(otChangedFlags aFlags)
81 {
82     if (aFlags & OT_CHANGED_THREAD_ROLE)
83     {
84         otDeviceRole role = otThreadGetDeviceRole(mInstance);
85 
86         for (const auto &handler : mDeviceRoleHandlers)
87         {
88             handler(role);
89         }
90 
91         if (role != OT_DEVICE_ROLE_DISABLED && role != OT_DEVICE_ROLE_DETACHED)
92         {
93             if (mAttachHandler != nullptr)
94             {
95                 if (mAttachPendingDatasetTlvs.mLength == 0)
96                 {
97                     AttachHandler handler = mAttachHandler;
98 
99                     mAttachHandler = nullptr;
100                     handler(OT_ERROR_NONE, mAttachDelayMs);
101                 }
102                 else
103                 {
104                     otOperationalDataset emptyDataset = {};
105                     otError              error =
106                         otDatasetSendMgmtPendingSet(mInstance, &emptyDataset, mAttachPendingDatasetTlvs.mTlvs,
107                                                     mAttachPendingDatasetTlvs.mLength, MgmtSetResponseHandler, this);
108                     if (error != OT_ERROR_NONE)
109                     {
110                         AttachHandler handler = mAttachHandler;
111 
112                         mAttachHandler            = nullptr;
113                         mAttachPendingDatasetTlvs = {};
114                         handler(error, 0);
115                     }
116                 }
117             }
118             else if (mJoinerHandler != nullptr)
119             {
120                 mJoinerHandler(OT_ERROR_NONE);
121                 mJoinerHandler = nullptr;
122             }
123         }
124     }
125 
126     if (aFlags & OT_CHANGED_ACTIVE_DATASET)
127     {
128         ActiveDatasetChangedCallback();
129     }
130 }
131 
ActiveDatasetChangedCallback()132 void ThreadHelper::ActiveDatasetChangedCallback()
133 {
134     otError                  error;
135     otOperationalDatasetTlvs datasetTlvs;
136 
137     SuccessOrExit(error = otDatasetGetActiveTlvs(mInstance, &datasetTlvs));
138 
139     for (const auto &handler : mActiveDatasetChangeHandlers)
140     {
141         handler(datasetTlvs);
142     }
143 
144 exit:
145     if (error != OT_ERROR_NONE)
146     {
147         otbrLogWarning("Error handling active dataset change: %s", otThreadErrorToString(error));
148     }
149 }
150 
151 #if OTBR_ENABLE_DBUS_SERVER
OnUpdateMeshCopTxt(std::map<std::string,std::vector<uint8_t>> aUpdate)152 void ThreadHelper::OnUpdateMeshCopTxt(std::map<std::string, std::vector<uint8_t>> aUpdate)
153 {
154     if (mUpdateMeshCopTxtHandler)
155     {
156         mUpdateMeshCopTxtHandler(std::move(aUpdate));
157     }
158     else
159     {
160         otbrLogErr("No UpdateMeshCopTxtHandler");
161     }
162 }
163 #endif
164 
AddDeviceRoleHandler(DeviceRoleHandler aHandler)165 void ThreadHelper::AddDeviceRoleHandler(DeviceRoleHandler aHandler)
166 {
167     mDeviceRoleHandlers.emplace_back(aHandler);
168 }
169 
Scan(ScanHandler aHandler)170 void ThreadHelper::Scan(ScanHandler aHandler)
171 {
172     otError error = OT_ERROR_NONE;
173 
174     VerifyOrExit(aHandler != nullptr);
175     mScanHandler = aHandler;
176     mScanResults.clear();
177 
178     error =
179         otLinkActiveScan(mInstance, /*scanChannels =*/0, /*scanDuration=*/0, &ThreadHelper::ActiveScanHandler, this);
180 
181 exit:
182     if (error != OT_ERROR_NONE)
183     {
184         if (aHandler)
185         {
186             mScanHandler(error, {});
187         }
188         mScanHandler = nullptr;
189     }
190 }
191 
EnergyScan(uint32_t aScanDuration,EnergyScanHandler aHandler)192 void ThreadHelper::EnergyScan(uint32_t aScanDuration, EnergyScanHandler aHandler)
193 {
194     otError  error             = OT_ERROR_NONE;
195     uint32_t preferredChannels = otPlatRadioGetPreferredChannelMask(mInstance);
196 
197     VerifyOrExit(aHandler != nullptr, error = OT_ERROR_BUSY);
198     VerifyOrExit(aScanDuration < UINT16_MAX, error = OT_ERROR_INVALID_ARGS);
199     mEnergyScanHandler = aHandler;
200     mEnergyScanResults.clear();
201 
202     error = otLinkEnergyScan(mInstance, preferredChannels, static_cast<uint16_t>(aScanDuration),
203                              &ThreadHelper::EnergyScanCallback, this);
204 
205 exit:
206     if (error != OT_ERROR_NONE)
207     {
208         if (aHandler)
209         {
210             mEnergyScanHandler(error, {});
211         }
212         mEnergyScanHandler = nullptr;
213     }
214 }
215 
RandomFill(void * aBuf,size_t size)216 void ThreadHelper::RandomFill(void *aBuf, size_t size)
217 {
218     std::uniform_int_distribution<> dist(0, UINT8_MAX);
219     uint8_t *                       buf = static_cast<uint8_t *>(aBuf);
220 
221     for (size_t i = 0; i < size; i++)
222     {
223         buf[i] = static_cast<uint8_t>(dist(mRandomDevice));
224     }
225 }
226 
ActiveScanHandler(otActiveScanResult * aResult,void * aThreadHelper)227 void ThreadHelper::ActiveScanHandler(otActiveScanResult *aResult, void *aThreadHelper)
228 {
229     ThreadHelper *helper = static_cast<ThreadHelper *>(aThreadHelper);
230 
231     helper->ActiveScanHandler(aResult);
232 }
233 
ActiveScanHandler(otActiveScanResult * aResult)234 void ThreadHelper::ActiveScanHandler(otActiveScanResult *aResult)
235 {
236     if (aResult == nullptr)
237     {
238         if (mScanHandler != nullptr)
239         {
240             mScanHandler(OT_ERROR_NONE, mScanResults);
241         }
242     }
243     else
244     {
245         mScanResults.push_back(*aResult);
246     }
247 }
248 
EnergyScanCallback(otEnergyScanResult * aResult,void * aThreadHelper)249 void ThreadHelper::EnergyScanCallback(otEnergyScanResult *aResult, void *aThreadHelper)
250 {
251     ThreadHelper *helper = static_cast<ThreadHelper *>(aThreadHelper);
252 
253     helper->EnergyScanCallback(aResult);
254 }
255 
EnergyScanCallback(otEnergyScanResult * aResult)256 void ThreadHelper::EnergyScanCallback(otEnergyScanResult *aResult)
257 {
258     if (aResult == nullptr)
259     {
260         if (mEnergyScanHandler != nullptr)
261         {
262             mEnergyScanHandler(OT_ERROR_NONE, mEnergyScanResults);
263         }
264     }
265     else
266     {
267         mEnergyScanResults.push_back(*aResult);
268     }
269 }
270 
RandomChannelFromChannelMask(uint32_t aChannelMask)271 uint8_t ThreadHelper::RandomChannelFromChannelMask(uint32_t aChannelMask)
272 {
273     // 8 bit per byte
274     constexpr uint8_t kNumChannels = sizeof(aChannelMask) * 8;
275     uint8_t           channels[kNumChannels];
276     uint8_t           numValidChannels = 0;
277 
278     for (uint8_t i = 0; i < kNumChannels; i++)
279     {
280         if (aChannelMask & (1 << i))
281         {
282             channels[numValidChannels++] = i;
283         }
284     }
285 
286     return channels[std::uniform_int_distribution<unsigned int>(0, numValidChannels - 1)(mRandomDevice)];
287 }
288 
ToOtExtendedPanId(uint64_t aExtPanId)289 static otExtendedPanId ToOtExtendedPanId(uint64_t aExtPanId)
290 {
291     otExtendedPanId extPanId;
292     uint64_t        mask = UINT8_MAX;
293 
294     for (size_t i = 0; i < sizeof(uint64_t); i++)
295     {
296         extPanId.m8[i] = static_cast<uint8_t>((aExtPanId >> ((sizeof(uint64_t) - i - 1) * 8)) & mask);
297     }
298 
299     return extPanId;
300 }
301 
Attach(const std::string & aNetworkName,uint16_t aPanId,uint64_t aExtPanId,const std::vector<uint8_t> & aNetworkKey,const std::vector<uint8_t> & aPSKc,uint32_t aChannelMask,AttachHandler aHandler)302 void ThreadHelper::Attach(const std::string &         aNetworkName,
303                           uint16_t                    aPanId,
304                           uint64_t                    aExtPanId,
305                           const std::vector<uint8_t> &aNetworkKey,
306                           const std::vector<uint8_t> &aPSKc,
307                           uint32_t                    aChannelMask,
308                           AttachHandler               aHandler)
309 
310 {
311     otError         error = OT_ERROR_NONE;
312     otExtendedPanId extPanId;
313     otNetworkKey    networkKey;
314     otPskc          pskc;
315     uint32_t        channelMask;
316     uint8_t         channel;
317 
318     VerifyOrExit(aHandler != nullptr, error = OT_ERROR_INVALID_ARGS);
319     VerifyOrExit(mAttachHandler == nullptr && mJoinerHandler == nullptr, error = OT_ERROR_INVALID_STATE);
320     VerifyOrExit(aNetworkKey.empty() || aNetworkKey.size() == sizeof(networkKey.m8), error = OT_ERROR_INVALID_ARGS);
321     VerifyOrExit(aPSKc.empty() || aPSKc.size() == sizeof(pskc.m8), error = OT_ERROR_INVALID_ARGS);
322     VerifyOrExit(aChannelMask != 0, error = OT_ERROR_INVALID_ARGS);
323 
324     while (aPanId == UINT16_MAX)
325     {
326         RandomFill(&aPanId, sizeof(aPanId));
327     }
328 
329     if (aExtPanId != UINT64_MAX)
330     {
331         extPanId = ToOtExtendedPanId(aExtPanId);
332     }
333     else
334     {
335         *reinterpret_cast<uint64_t *>(&extPanId) = UINT64_MAX;
336 
337         while (*reinterpret_cast<uint64_t *>(&extPanId) == UINT64_MAX)
338         {
339             RandomFill(extPanId.m8, sizeof(extPanId.m8));
340         }
341     }
342 
343     if (!aNetworkKey.empty())
344     {
345         memcpy(networkKey.m8, &aNetworkKey[0], sizeof(networkKey.m8));
346     }
347     else
348     {
349         RandomFill(networkKey.m8, sizeof(networkKey.m8));
350     }
351 
352     if (!aPSKc.empty())
353     {
354         memcpy(pskc.m8, &aPSKc[0], sizeof(pskc.m8));
355     }
356     else
357     {
358         RandomFill(pskc.m8, sizeof(pskc.m8));
359     }
360 
361     if (!otIp6IsEnabled(mInstance))
362     {
363         SuccessOrExit(error = otIp6SetEnabled(mInstance, true));
364     }
365 
366     SuccessOrExit(error = otThreadSetNetworkName(mInstance, aNetworkName.c_str()));
367     SuccessOrExit(error = otLinkSetPanId(mInstance, aPanId));
368     SuccessOrExit(error = otThreadSetExtendedPanId(mInstance, &extPanId));
369     SuccessOrExit(error = otThreadSetNetworkKey(mInstance, &networkKey));
370 
371     channelMask = otPlatRadioGetPreferredChannelMask(mInstance) & aChannelMask;
372 
373     if (channelMask == 0)
374     {
375         channelMask = otLinkGetSupportedChannelMask(mInstance) & aChannelMask;
376     }
377     VerifyOrExit(channelMask != 0, otbrLogWarning("Invalid channel mask"), error = OT_ERROR_INVALID_ARGS);
378 
379     channel = RandomChannelFromChannelMask(channelMask);
380     SuccessOrExit(otLinkSetChannel(mInstance, channel));
381 
382     SuccessOrExit(error = otThreadSetPskc(mInstance, &pskc));
383 
384     SuccessOrExit(error = otThreadSetEnabled(mInstance, true));
385     mAttachDelayMs = 0;
386     mAttachHandler = aHandler;
387 
388 exit:
389     if (error != OT_ERROR_NONE)
390     {
391         if (aHandler)
392         {
393             aHandler(error, 0);
394         }
395     }
396 }
397 
Attach(AttachHandler aHandler)398 void ThreadHelper::Attach(AttachHandler aHandler)
399 {
400     otError error = OT_ERROR_NONE;
401 
402     VerifyOrExit(mAttachHandler == nullptr && mJoinerHandler == nullptr, error = OT_ERROR_INVALID_STATE);
403 
404     if (!otIp6IsEnabled(mInstance))
405     {
406         SuccessOrExit(error = otIp6SetEnabled(mInstance, true));
407     }
408     SuccessOrExit(error = otThreadSetEnabled(mInstance, true));
409     mAttachHandler = aHandler;
410 
411 exit:
412     if (error != OT_ERROR_NONE)
413     {
414         if (aHandler)
415         {
416             aHandler(error, 0);
417         }
418     }
419 }
420 
Detach(void)421 otError ThreadHelper::Detach(void)
422 {
423     otError error = OT_ERROR_NONE;
424 
425     SuccessOrExit(error = otThreadSetEnabled(mInstance, false));
426     SuccessOrExit(error = otIp6SetEnabled(mInstance, false));
427 
428 exit:
429     return error;
430 }
431 
Reset(void)432 otError ThreadHelper::Reset(void)
433 {
434     mDeviceRoleHandlers.clear();
435     otInstanceReset(mInstance);
436 
437     return OT_ERROR_NONE;
438 }
439 
JoinerStart(const std::string & aPskd,const std::string & aProvisioningUrl,const std::string & aVendorName,const std::string & aVendorModel,const std::string & aVendorSwVersion,const std::string & aVendorData,ResultHandler aHandler)440 void ThreadHelper::JoinerStart(const std::string &aPskd,
441                                const std::string &aProvisioningUrl,
442                                const std::string &aVendorName,
443                                const std::string &aVendorModel,
444                                const std::string &aVendorSwVersion,
445                                const std::string &aVendorData,
446                                ResultHandler      aHandler)
447 {
448     otError error = OT_ERROR_NONE;
449 
450     VerifyOrExit(aHandler != nullptr, error = OT_ERROR_INVALID_ARGS);
451     VerifyOrExit(mAttachHandler == nullptr && mJoinerHandler == nullptr, error = OT_ERROR_INVALID_STATE);
452 
453     if (!otIp6IsEnabled(mInstance))
454     {
455         SuccessOrExit(error = otIp6SetEnabled(mInstance, true));
456     }
457     SuccessOrExit(error = otJoinerStart(mInstance, aPskd.c_str(), aProvisioningUrl.c_str(), aVendorName.c_str(),
458                                         aVendorModel.c_str(), aVendorSwVersion.c_str(), aVendorData.c_str(),
459                                         JoinerCallback, this));
460     mJoinerHandler = aHandler;
461 
462 exit:
463     if (error != OT_ERROR_NONE)
464     {
465         if (aHandler)
466         {
467             aHandler(error);
468         }
469     }
470 }
471 
JoinerCallback(otError aError,void * aThreadHelper)472 void ThreadHelper::JoinerCallback(otError aError, void *aThreadHelper)
473 {
474     ThreadHelper *helper = static_cast<ThreadHelper *>(aThreadHelper);
475 
476     helper->JoinerCallback(aError);
477 }
478 
JoinerCallback(otError aError)479 void ThreadHelper::JoinerCallback(otError aError)
480 {
481     if (aError != OT_ERROR_NONE)
482     {
483         otbrLogWarning("Failed to join Thread network: %s", otThreadErrorToString(aError));
484         mJoinerHandler(aError);
485         mJoinerHandler = nullptr;
486     }
487     else
488     {
489         LogOpenThreadResult("Start Thread network", otThreadSetEnabled(mInstance, true));
490     }
491 }
492 
TryResumeNetwork(void)493 otError ThreadHelper::TryResumeNetwork(void)
494 {
495     otError error = OT_ERROR_NONE;
496 
497     if (otLinkGetPanId(mInstance) != UINT16_MAX && otThreadGetDeviceRole(mInstance) == OT_DEVICE_ROLE_DISABLED)
498     {
499         if (!otIp6IsEnabled(mInstance))
500         {
501             SuccessOrExit(error = otIp6SetEnabled(mInstance, true));
502             SuccessOrExit(error = otThreadSetEnabled(mInstance, true));
503         }
504     }
505 
506 exit:
507     if (error != OT_ERROR_NONE)
508     {
509         (void)otIp6SetEnabled(mInstance, false);
510     }
511 
512     return error;
513 }
514 
LogOpenThreadResult(const char * aAction,otError aError)515 void ThreadHelper::LogOpenThreadResult(const char *aAction, otError aError)
516 {
517     if (aError == OT_ERROR_NONE)
518     {
519         otbrLogInfo("%s: %s", aAction, otThreadErrorToString(aError));
520     }
521     else
522     {
523         otbrLogWarning("%s: %s", aAction, otThreadErrorToString(aError));
524     }
525 }
526 
AttachAllNodesTo(const std::vector<uint8_t> & aDatasetTlvs,AttachHandler aHandler)527 void ThreadHelper::AttachAllNodesTo(const std::vector<uint8_t> &aDatasetTlvs, AttachHandler aHandler)
528 {
529     constexpr uint32_t kDelayTimerMilliseconds = 300 * 1000;
530 
531     otError                  error = OT_ERROR_NONE;
532     otOperationalDatasetTlvs datasetTlvs;
533     otOperationalDataset     dataset;
534     otOperationalDataset     emptyDataset{};
535     otDeviceRole             role = otThreadGetDeviceRole(mInstance);
536     Tlv *                    tlv;
537     uint64_t                 pendingTimestamp = 0;
538     timespec                 currentTime;
539 
540     assert(aHandler != nullptr);
541     VerifyOrExit(mAttachHandler == nullptr && mJoinerHandler == nullptr, error = OT_ERROR_BUSY);
542 
543     VerifyOrExit(aDatasetTlvs.size() <= sizeof(datasetTlvs.mTlvs), error = OT_ERROR_INVALID_ARGS);
544     std::copy(aDatasetTlvs.begin(), aDatasetTlvs.end(), datasetTlvs.mTlvs);
545     datasetTlvs.mLength = aDatasetTlvs.size();
546 
547     SuccessOrExit(error = otDatasetParseTlvs(&datasetTlvs, &dataset));
548     VerifyOrExit(dataset.mComponents.mIsActiveTimestampPresent, error = OT_ERROR_INVALID_ARGS);
549     VerifyOrExit(dataset.mComponents.mIsNetworkKeyPresent, error = OT_ERROR_INVALID_ARGS);
550     VerifyOrExit(dataset.mComponents.mIsNetworkNamePresent, error = OT_ERROR_INVALID_ARGS);
551     VerifyOrExit(dataset.mComponents.mIsExtendedPanIdPresent, error = OT_ERROR_INVALID_ARGS);
552     VerifyOrExit(dataset.mComponents.mIsMeshLocalPrefixPresent, error = OT_ERROR_INVALID_ARGS);
553     VerifyOrExit(dataset.mComponents.mIsPanIdPresent, error = OT_ERROR_INVALID_ARGS);
554     VerifyOrExit(dataset.mComponents.mIsChannelPresent, error = OT_ERROR_INVALID_ARGS);
555     VerifyOrExit(dataset.mComponents.mIsPskcPresent, error = OT_ERROR_INVALID_ARGS);
556     VerifyOrExit(dataset.mComponents.mIsSecurityPolicyPresent, error = OT_ERROR_INVALID_ARGS);
557     VerifyOrExit(dataset.mComponents.mIsChannelMaskPresent, error = OT_ERROR_INVALID_ARGS);
558 
559     VerifyOrExit(FindTlv(OT_MESHCOP_TLV_PENDINGTIMESTAMP, datasetTlvs.mTlvs, datasetTlvs.mLength) == nullptr &&
560                      FindTlv(OT_MESHCOP_TLV_DELAYTIMER, datasetTlvs.mTlvs, datasetTlvs.mLength) == nullptr,
561                  error = OT_ERROR_INVALID_ARGS);
562 
563     // There must be sufficient space for a Pending Timestamp TLV and a Delay Timer TLV.
564     VerifyOrExit(
565         static_cast<int>(datasetTlvs.mLength +
566                          (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint64_t))    // Pending Timestamp TLV (10 bytes)
567                          + (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t))) // Delay Timer TLV (6 bytes)
568             <= int{sizeof(datasetTlvs.mTlvs)},
569         error = OT_ERROR_INVALID_ARGS);
570 
571     tlv = reinterpret_cast<Tlv *>(datasetTlvs.mTlvs + datasetTlvs.mLength);
572     tlv->SetType(OT_MESHCOP_TLV_PENDINGTIMESTAMP);
573     clock_gettime(CLOCK_REALTIME, &currentTime);
574     pendingTimestamp |= (static_cast<uint64_t>(currentTime.tv_sec) << 16);
575     pendingTimestamp |= (((static_cast<uint64_t>(currentTime.tv_nsec) * 32768 / 1000000000) & 0x7fff) << 1);
576     tlv->SetValue(pendingTimestamp);
577 
578     tlv = tlv->GetNext();
579     tlv->SetType(OT_MESHCOP_TLV_DELAYTIMER);
580     tlv->SetValue(kDelayTimerMilliseconds);
581 
582     datasetTlvs.mLength = reinterpret_cast<uint8_t *>(tlv->GetNext()) - datasetTlvs.mTlvs;
583 
584     assert(datasetTlvs.mLength > 0);
585 
586     if (role == OT_DEVICE_ROLE_DISABLED || role == OT_DEVICE_ROLE_DETACHED)
587     {
588         otOperationalDataset existingDataset;
589         bool                 hasActiveDataset;
590 
591         error = otDatasetGetActive(mInstance, &existingDataset);
592         VerifyOrExit(error == OT_ERROR_NONE || error == OT_ERROR_NOT_FOUND);
593 
594         hasActiveDataset = (error == OT_ERROR_NONE);
595 
596         if (!hasActiveDataset)
597         {
598             SuccessOrExit(error = otDatasetSetActiveTlvs(mInstance, &datasetTlvs));
599         }
600 
601         if (!otIp6IsEnabled(mInstance))
602         {
603             SuccessOrExit(error = otIp6SetEnabled(mInstance, true));
604         }
605         SuccessOrExit(error = otThreadSetEnabled(mInstance, true));
606 
607         if (hasActiveDataset)
608         {
609             mAttachDelayMs            = kDelayTimerMilliseconds;
610             mAttachPendingDatasetTlvs = datasetTlvs;
611         }
612         else
613         {
614             mAttachDelayMs            = 0;
615             mAttachPendingDatasetTlvs = {};
616         }
617         mAttachHandler = aHandler;
618         ExitNow();
619     }
620 
621     SuccessOrExit(error = otDatasetSendMgmtPendingSet(mInstance, &emptyDataset, datasetTlvs.mTlvs, datasetTlvs.mLength,
622                                                       MgmtSetResponseHandler, this));
623     mAttachDelayMs = kDelayTimerMilliseconds;
624     mAttachHandler = aHandler;
625 
626 exit:
627     if (error != OT_ERROR_NONE)
628     {
629         aHandler(error, 0);
630     }
631 }
632 
MgmtSetResponseHandler(otError aResult,void * aContext)633 void ThreadHelper::MgmtSetResponseHandler(otError aResult, void *aContext)
634 {
635     static_cast<ThreadHelper *>(aContext)->MgmtSetResponseHandler(aResult);
636 }
637 
MgmtSetResponseHandler(otError aResult)638 void ThreadHelper::MgmtSetResponseHandler(otError aResult)
639 {
640     AttachHandler handler;
641     int64_t       attachDelayMs;
642 
643     LogOpenThreadResult("MgmtSetResponseHandler()", aResult);
644 
645     assert(mAttachHandler != nullptr);
646 
647     switch (aResult)
648     {
649     case OT_ERROR_NONE:
650     case OT_ERROR_REJECTED:
651         break;
652     default:
653         aResult = OT_ERROR_FAILED;
654         break;
655     }
656 
657     attachDelayMs             = mAttachDelayMs;
658     handler                   = mAttachHandler;
659     mAttachDelayMs            = 0;
660     mAttachHandler            = nullptr;
661     mAttachPendingDatasetTlvs = {};
662     if (aResult == OT_ERROR_NONE)
663     {
664         handler(aResult, attachDelayMs);
665     }
666     else
667     {
668         handler(aResult, 0);
669     }
670 }
671 
672 #if OTBR_ENABLE_UNSECURE_JOIN
PermitUnsecureJoin(uint16_t aPort,uint32_t aSeconds)673 otError ThreadHelper::PermitUnsecureJoin(uint16_t aPort, uint32_t aSeconds)
674 {
675     otError      error = OT_ERROR_NONE;
676     otExtAddress steeringData;
677 
678     // 0xff to allow all devices to join
679     memset(&steeringData.m8, 0xff, sizeof(steeringData.m8));
680     SuccessOrExit(error = otIp6AddUnsecurePort(mInstance, aPort));
681     otThreadSetSteeringData(mInstance, &steeringData);
682 
683     if (aSeconds > 0)
684     {
685         auto delay = Milliseconds(aSeconds * 1000);
686 
687         ++mUnsecurePortRefCounter[aPort];
688 
689         mNcp->PostTimerTask(delay, [this, aPort]() {
690             assert(mUnsecurePortRefCounter.find(aPort) != mUnsecurePortRefCounter.end());
691             assert(mUnsecurePortRefCounter[aPort] > 0);
692 
693             if (--mUnsecurePortRefCounter[aPort] == 0)
694             {
695                 otExtAddress noneAddress;
696 
697                 // 0 to clean steering data
698                 memset(&noneAddress.m8, 0, sizeof(noneAddress.m8));
699                 (void)otIp6RemoveUnsecurePort(mInstance, aPort);
700                 otThreadSetSteeringData(mInstance, &noneAddress);
701                 mUnsecurePortRefCounter.erase(aPort);
702             }
703         });
704     }
705     else
706     {
707         otExtAddress noneAddress;
708 
709         memset(&noneAddress.m8, 0, sizeof(noneAddress.m8));
710         (void)otIp6RemoveUnsecurePort(mInstance, aPort);
711         otThreadSetSteeringData(mInstance, &noneAddress);
712     }
713 
714 exit:
715     return error;
716 }
717 #endif
718 
AddActiveDatasetChangeHandler(DatasetChangeHandler aHandler)719 void ThreadHelper::AddActiveDatasetChangeHandler(DatasetChangeHandler aHandler)
720 {
721     mActiveDatasetChangeHandlers.push_back(std::move(aHandler));
722 }
723 
724 } // namespace agent
725 } // namespace otbr
726