• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *    Copyright (c) 2017, 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  *   The file implements the Thread border agent.
32  */
33 
34 #define OTBR_LOG_TAG "BA"
35 
36 #include "border_agent/border_agent.hpp"
37 
38 #include <arpa/inet.h>
39 #include <assert.h>
40 #include <errno.h>
41 #include <netinet/in.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 
47 #include <iomanip>
48 #include <random>
49 #include <sstream>
50 
51 #include <openthread/border_agent.h>
52 #include <openthread/border_routing.h>
53 #include <openthread/random_crypto.h>
54 #include <openthread/random_noncrypto.h>
55 #include <openthread/thread.h>
56 #include <openthread/thread_ftd.h>
57 #include <openthread/verhoeff_checksum.h>
58 #include <openthread/platform/settings.h>
59 #include <openthread/platform/toolchain.h>
60 
61 #include "agent/uris.hpp"
62 #include "host/rcp_host.hpp"
63 #if OTBR_ENABLE_BACKBONE_ROUTER
64 #include "backbone_router/backbone_agent.hpp"
65 #endif
66 #include "common/byteswap.hpp"
67 #include "common/code_utils.hpp"
68 #include "common/logging.hpp"
69 #include "common/tlv.hpp"
70 #include "common/types.hpp"
71 #include "utils/hex.hpp"
72 
73 #if !(OTBR_ENABLE_MDNS_AVAHI || OTBR_ENABLE_MDNS_MDNSSD || OTBR_ENABLE_MDNS_MOJO)
74 #error "Border Agent feature requires at least one `OTBR_MDNS` implementation"
75 #endif
76 
77 namespace otbr {
78 
79 static const char    kBorderAgentServiceType[]      = "_meshcop._udp";   ///< Border agent service type of mDNS
80 static const char    kBorderAgentEpskcServiceType[] = "_meshcop-e._udp"; ///< Border agent ePSKc service
81 static constexpr int kBorderAgentServiceDummyPort   = 49152;
82 static constexpr int kEpskcRandomGenLen             = 8;
83 
84 /**
85  * Locators
86  */
87 enum
88 {
89     kAloc16Leader   = 0xfc00, ///< leader anycast locator.
90     kInvalidLocator = 0xffff, ///< invalid locator.
91 };
92 
93 enum : uint8_t
94 {
95     kConnectionModeDisabled = 0,
96     kConnectionModePskc     = 1,
97     kConnectionModePskd     = 2,
98     kConnectionModeVendor   = 3,
99     kConnectionModeX509     = 4,
100 };
101 
102 enum : uint8_t
103 {
104     kThreadIfStatusNotInitialized = 0,
105     kThreadIfStatusInitialized    = 1,
106     kThreadIfStatusActive         = 2,
107 };
108 
109 enum : uint8_t
110 {
111     kThreadRoleDisabledOrDetached = 0,
112     kThreadRoleChild              = 1,
113     kThreadRoleRouter             = 2,
114     kThreadRoleLeader             = 3,
115 };
116 
117 enum : uint8_t
118 {
119     kAvailabilityInfrequent = 0,
120     kAvailabilityHigh       = 1,
121 };
122 
123 struct StateBitmap
124 {
125     uint32_t mConnectionMode : 3;
126     uint32_t mThreadIfStatus : 2;
127     uint32_t mAvailability : 2;
128     uint32_t mBbrIsActive : 1;
129     uint32_t mBbrIsPrimary : 1;
130     uint32_t mThreadRole : 2;
131     uint32_t mEpskcSupported : 1;
132 
StateBitmapotbr::StateBitmap133     StateBitmap(void)
134         : mConnectionMode(0)
135         , mThreadIfStatus(0)
136         , mAvailability(0)
137         , mBbrIsActive(0)
138         , mBbrIsPrimary(0)
139         , mThreadRole(kThreadRoleDisabledOrDetached)
140         , mEpskcSupported(0)
141     {
142     }
143 
ToUint32otbr::StateBitmap144     uint32_t ToUint32(void) const
145     {
146         uint32_t bitmap = 0;
147 
148         bitmap |= mConnectionMode << 0;
149         bitmap |= mThreadIfStatus << 3;
150         bitmap |= mAvailability << 5;
151         bitmap |= mBbrIsActive << 7;
152         bitmap |= mBbrIsPrimary << 8;
153         bitmap |= mThreadRole << 9;
154         bitmap |= mEpskcSupported << 11;
155         return bitmap;
156     }
157 };
158 
BorderAgent(otbr::Host::RcpHost & aHost,Mdns::Publisher & aPublisher)159 BorderAgent::BorderAgent(otbr::Host::RcpHost &aHost, Mdns::Publisher &aPublisher)
160     : mHost(aHost)
161     , mPublisher(aPublisher)
162 {
163     ClearState();
164 }
165 
Init(void)166 void BorderAgent::Init(void)
167 {
168     otbrLogInfo("Ephemeral Key is: %s during initialization", (mIsEphemeralKeyEnabled ? "enabled" : "disabled"));
169     mHost.AddThreadStateChangedCallback([this](otChangedFlags aFlags) { HandleThreadStateChanged(aFlags); });
170 }
171 
Deinit(void)172 void BorderAgent::Deinit(void)
173 {
174     ClearState();
175 }
176 
CreateEphemeralKey(std::string & aEphemeralKey)177 otbrError BorderAgent::CreateEphemeralKey(std::string &aEphemeralKey)
178 {
179     std::string digitString;
180     char        checksum;
181     uint8_t     candidateBuffer[1];
182     otbrError   error = OTBR_ERROR_NONE;
183 
184     for (uint8_t i = 0; i < kEpskcRandomGenLen; ++i)
185     {
186         while (true)
187         {
188             SuccessOrExit(otRandomCryptoFillBuffer(candidateBuffer, 1), error = OTBR_ERROR_ABORTED);
189             // Generates a random number in the range [0, 9] with equal probability.
190             if (candidateBuffer[0] < 250)
191             {
192                 digitString += static_cast<char>('0' + candidateBuffer[0] % 10);
193                 break;
194             }
195         }
196     }
197     SuccessOrExit(otVerhoeffChecksumCalculate(digitString.c_str(), &checksum), error = OTBR_ERROR_INVALID_ARGS);
198     aEphemeralKey = digitString + checksum;
199 
200 exit:
201     return error;
202 }
203 
SetMeshCopServiceValues(const std::string & aServiceInstanceName,const std::string & aProductName,const std::string & aVendorName,const std::vector<uint8_t> & aVendorOui,const Mdns::Publisher::TxtList & aNonStandardTxtEntries)204 otbrError BorderAgent::SetMeshCopServiceValues(const std::string              &aServiceInstanceName,
205                                                const std::string              &aProductName,
206                                                const std::string              &aVendorName,
207                                                const std::vector<uint8_t>     &aVendorOui,
208                                                const Mdns::Publisher::TxtList &aNonStandardTxtEntries)
209 {
210     otbrError error = OTBR_ERROR_NONE;
211 
212     VerifyOrExit(aProductName.size() <= kMaxProductNameLength, error = OTBR_ERROR_INVALID_ARGS);
213     VerifyOrExit(aVendorName.size() <= kMaxVendorNameLength, error = OTBR_ERROR_INVALID_ARGS);
214     VerifyOrExit(aVendorOui.empty() || aVendorOui.size() == kVendorOuiLength, error = OTBR_ERROR_INVALID_ARGS);
215     for (const auto &txtEntry : aNonStandardTxtEntries)
216     {
217         VerifyOrExit(!txtEntry.mKey.empty() && txtEntry.mKey.front() == 'v', error = OTBR_ERROR_INVALID_ARGS);
218     }
219 
220     mProductName = aProductName;
221     mVendorName  = aVendorName;
222     mVendorOui   = aVendorOui;
223     mMeshCopTxtUpdate.clear();
224     for (const auto &txtEntry : aNonStandardTxtEntries)
225     {
226         mMeshCopTxtUpdate[txtEntry.mKey] = txtEntry.mValue;
227     }
228 
229     mBaseServiceInstanceName = aServiceInstanceName;
230 
231 exit:
232     return error;
233 }
234 
SetEnabled(bool aIsEnabled)235 void BorderAgent::SetEnabled(bool aIsEnabled)
236 {
237     VerifyOrExit(IsEnabled() != aIsEnabled);
238     mIsEnabled = aIsEnabled;
239     if (mIsEnabled)
240     {
241         Start();
242     }
243     else
244     {
245         Stop();
246     }
247 exit:
248     return;
249 }
250 
SetEphemeralKeyEnabled(bool aIsEnabled)251 void BorderAgent::SetEphemeralKeyEnabled(bool aIsEnabled)
252 {
253     VerifyOrExit(GetEphemeralKeyEnabled() != aIsEnabled);
254     mIsEphemeralKeyEnabled = aIsEnabled;
255 
256     if (!mIsEphemeralKeyEnabled)
257     {
258         // If the ePSKc feature is enabled, we call the stop function which
259         // will wait for the session to close if it is in active use before
260         // removing ephemeral key and unpublishing the service.
261         otBorderAgentEphemeralKeyStop(mHost.GetInstance());
262     }
263 
264     UpdateMeshCopService();
265 
266 exit:
267     return;
268 }
269 
ClearState(void)270 void BorderAgent::ClearState(void)
271 {
272     mIsEnabled             = false;
273     mIsEphemeralKeyEnabled = (otThreadGetVersion() >= OT_THREAD_VERSION_1_4);
274     mMeshCopTxtUpdate.clear();
275     mVendorOui.clear();
276     mVendorName              = OTBR_VENDOR_NAME;
277     mProductName             = OTBR_PRODUCT_NAME;
278     mBaseServiceInstanceName = OTBR_MESHCOP_SERVICE_INSTANCE_NAME;
279     mServiceInstanceName.clear();
280     mEphemeralKeyChangedCallbacks.clear();
281 }
282 
Start(void)283 void BorderAgent::Start(void)
284 {
285     otbrLogInfo("Start Thread Border Agent");
286 
287 #if OTBR_ENABLE_DBUS_SERVER
288     mHost.GetThreadHelper()->SetUpdateMeshCopTxtHandler([this](std::map<std::string, std::vector<uint8_t>> aUpdate) {
289         HandleUpdateVendorMeshCoPTxtEntries(std::move(aUpdate));
290     });
291     mHost.RegisterResetHandler([this]() {
292         mHost.GetThreadHelper()->SetUpdateMeshCopTxtHandler(
293             [this](std::map<std::string, std::vector<uint8_t>> aUpdate) {
294                 HandleUpdateVendorMeshCoPTxtEntries(std::move(aUpdate));
295             });
296     });
297 #endif
298 
299     mServiceInstanceName = GetServiceInstanceNameWithExtAddr(mBaseServiceInstanceName);
300     UpdateMeshCopService();
301 
302     otBorderAgentEphemeralKeySetCallback(mHost.GetInstance(), BorderAgent::HandleEpskcStateChanged, this);
303 }
304 
Stop(void)305 void BorderAgent::Stop(void)
306 {
307     otbrLogInfo("Stop Thread Border Agent");
308     UnpublishMeshCopService();
309 }
310 
HandleEpskcStateChanged(void * aContext)311 void BorderAgent::HandleEpskcStateChanged(void *aContext)
312 {
313     static_cast<BorderAgent *>(aContext)->HandleEpskcStateChanged();
314 }
315 
HandleEpskcStateChanged(void)316 void BorderAgent::HandleEpskcStateChanged(void)
317 {
318     switch (otBorderAgentEphemeralKeyGetState(mHost.GetInstance()))
319     {
320     case OT_BORDER_AGENT_STATE_STARTED:
321     case OT_BORDER_AGENT_STATE_CONNECTED:
322     case OT_BORDER_AGENT_STATE_ACCEPTED:
323         PublishEpskcService();
324         break;
325     case OT_BORDER_AGENT_STATE_DISABLED:
326     case OT_BORDER_AGENT_STATE_STOPPED:
327         UnpublishEpskcService();
328         break;
329     }
330 
331     for (auto &ephemeralKeyCallback : mEphemeralKeyChangedCallbacks)
332     {
333         ephemeralKeyCallback();
334     }
335 }
336 
PublishEpskcService()337 void BorderAgent::PublishEpskcService()
338 {
339     otInstance *instance = mHost.GetInstance();
340     int         port     = otBorderAgentEphemeralKeyGetUdpPort(instance);
341 
342     otbrLogInfo("Publish meshcop-e service %s.%s.local. port %d", mServiceInstanceName.c_str(),
343                 kBorderAgentEpskcServiceType, port);
344 
345     mPublisher.PublishService(/* aHostName */ "", mServiceInstanceName, kBorderAgentEpskcServiceType,
346                               Mdns::Publisher::SubTypeList{}, port, /* aTxtData */ {}, [this](otbrError aError) {
347                                   if (aError == OTBR_ERROR_ABORTED)
348                                   {
349                                       // OTBR_ERROR_ABORTED is thrown when an ongoing service registration is
350                                       // cancelled. This can happen when the meshcop-e service is being updated
351                                       // frequently. To avoid false alarms, it should not be logged like a real error.
352                                       otbrLogInfo("Cancelled previous publishing meshcop-e service %s.%s.local",
353                                                   mServiceInstanceName.c_str(), kBorderAgentEpskcServiceType);
354                                   }
355                                   else
356                                   {
357                                       otbrLogResult(aError, "Result of publish meshcop-e service %s.%s.local",
358                                                     mServiceInstanceName.c_str(), kBorderAgentEpskcServiceType);
359                                   }
360 
361                                   if (aError == OTBR_ERROR_DUPLICATED)
362                                   {
363                                       // Try to unpublish current service in case we are trying to register
364                                       // multiple new services simultaneously when the original service name
365                                       // is conflicted.
366                                       // Potential risk that instance name is not the same with meshcop service.
367                                       UnpublishEpskcService();
368                                       mServiceInstanceName = GetAlternativeServiceInstanceName();
369                                       PublishEpskcService();
370                                   }
371                               });
372 }
373 
UnpublishEpskcService()374 void BorderAgent::UnpublishEpskcService()
375 {
376     otbrLogInfo("Unpublish meshcop-e service %s.%s.local", mServiceInstanceName.c_str(), kBorderAgentEpskcServiceType);
377 
378     mPublisher.UnpublishService(mServiceInstanceName, kBorderAgentEpskcServiceType, [this](otbrError aError) {
379         otbrLogResult(aError, "Result of unpublish meshcop-e service %s.%s.local", mServiceInstanceName.c_str(),
380                       kBorderAgentEpskcServiceType);
381     });
382 }
383 
AddEphemeralKeyChangedCallback(EphemeralKeyChangedCallback aCallback)384 void BorderAgent::AddEphemeralKeyChangedCallback(EphemeralKeyChangedCallback aCallback)
385 {
386     mEphemeralKeyChangedCallbacks.push_back(std::move(aCallback));
387 }
388 
HandleMdnsState(Mdns::Publisher::State aState)389 void BorderAgent::HandleMdnsState(Mdns::Publisher::State aState)
390 {
391     VerifyOrExit(IsEnabled());
392 
393     switch (aState)
394     {
395     case Mdns::Publisher::State::kReady:
396         UpdateMeshCopService();
397         break;
398     default:
399         otbrLogWarning("mDNS publisher not available!");
400         break;
401     }
402 exit:
403     return;
404 }
405 
ConvertTimestampToUint64(const otTimestamp & aTimestamp)406 static uint64_t ConvertTimestampToUint64(const otTimestamp &aTimestamp)
407 {
408     // 64 bits Timestamp fields layout
409     //-----48 bits------//-----15 bits-----//-------1 bit-------//
410     //     Seconds      //      Ticks      //  Authoritative    //
411     return (aTimestamp.mSeconds << 16) | static_cast<uint64_t>(aTimestamp.mTicks << 1) |
412            static_cast<uint64_t>(aTimestamp.mAuthoritative);
413 }
414 
415 #if OTBR_ENABLE_BORDER_ROUTING
AppendOmrTxtEntry(otInstance & aInstance,Mdns::Publisher::TxtList & aTxtList)416 void AppendOmrTxtEntry(otInstance &aInstance, Mdns::Publisher::TxtList &aTxtList)
417 {
418     otIp6Prefix       omrPrefix;
419     otRoutePreference preference;
420 
421     if (OT_ERROR_NONE == otBorderRoutingGetFavoredOmrPrefix(&aInstance, &omrPrefix, &preference))
422     {
423         std::vector<uint8_t> omrData;
424 
425         omrData.reserve(1 + OT_IP6_PREFIX_SIZE);
426         omrData.push_back(omrPrefix.mLength);
427         std::copy(omrPrefix.mPrefix.mFields.m8, omrPrefix.mPrefix.mFields.m8 + (omrPrefix.mLength + 7) / 8,
428                   std::back_inserter(omrData));
429         aTxtList.emplace_back("omr", omrData.data(), omrData.size());
430     }
431 }
432 #endif
433 
GetStateBitmap(otInstance & aInstance)434 StateBitmap GetStateBitmap(otInstance &aInstance)
435 {
436     StateBitmap state;
437 
438     state.mConnectionMode = kConnectionModePskc;
439     state.mAvailability   = kAvailabilityHigh;
440 
441     switch (otThreadGetDeviceRole(&aInstance))
442     {
443     case OT_DEVICE_ROLE_DISABLED:
444         state.mThreadIfStatus = kThreadIfStatusNotInitialized;
445         state.mThreadRole     = kThreadRoleDisabledOrDetached;
446         break;
447     case OT_DEVICE_ROLE_DETACHED:
448         state.mThreadIfStatus = kThreadIfStatusInitialized;
449         state.mThreadRole     = kThreadRoleDisabledOrDetached;
450         break;
451     case OT_DEVICE_ROLE_CHILD:
452         state.mThreadIfStatus = kThreadIfStatusActive;
453         state.mThreadRole     = kThreadRoleChild;
454         break;
455     case OT_DEVICE_ROLE_ROUTER:
456         state.mThreadIfStatus = kThreadIfStatusActive;
457         state.mThreadRole     = kThreadRoleRouter;
458         break;
459     case OT_DEVICE_ROLE_LEADER:
460         state.mThreadIfStatus = kThreadIfStatusActive;
461         state.mThreadRole     = kThreadRoleLeader;
462         break;
463     }
464 
465 #if OTBR_ENABLE_BACKBONE_ROUTER
466     state.mBbrIsActive = state.mThreadIfStatus == kThreadIfStatusActive &&
467                          otBackboneRouterGetState(&aInstance) != OT_BACKBONE_ROUTER_STATE_DISABLED;
468     state.mBbrIsPrimary = state.mThreadIfStatus == kThreadIfStatusActive &&
469                           otBackboneRouterGetState(&aInstance) == OT_BACKBONE_ROUTER_STATE_PRIMARY;
470 #endif
471 
472     return state;
473 }
474 
475 #if OTBR_ENABLE_BACKBONE_ROUTER
AppendBbrTxtEntries(otInstance & aInstance,StateBitmap aState,Mdns::Publisher::TxtList & aTxtList)476 void AppendBbrTxtEntries(otInstance &aInstance, StateBitmap aState, Mdns::Publisher::TxtList &aTxtList)
477 {
478     if (aState.mBbrIsActive)
479     {
480         otBackboneRouterConfig bbrConfig;
481         uint16_t               bbrPort = htobe16(BackboneRouter::BackboneAgent::kBackboneUdpPort);
482 
483         otBackboneRouterGetConfig(&aInstance, &bbrConfig);
484         aTxtList.emplace_back("sq", &bbrConfig.mSequenceNumber, sizeof(bbrConfig.mSequenceNumber));
485         aTxtList.emplace_back("bb", reinterpret_cast<const uint8_t *>(&bbrPort), sizeof(bbrPort));
486     }
487 
488     aTxtList.emplace_back("dn", otThreadGetDomainName(&aInstance));
489 }
490 #endif
491 
AppendActiveTimestampTxtEntry(otInstance & aInstance,Mdns::Publisher::TxtList & aTxtList)492 void AppendActiveTimestampTxtEntry(otInstance &aInstance, Mdns::Publisher::TxtList &aTxtList)
493 {
494     otError              error;
495     otOperationalDataset activeDataset;
496 
497     if ((error = otDatasetGetActive(&aInstance, &activeDataset)) != OT_ERROR_NONE)
498     {
499         otbrLogWarning("Failed to get active dataset: %s", otThreadErrorToString(error));
500     }
501     else
502     {
503         uint64_t activeTimestampValue = ConvertTimestampToUint64(activeDataset.mActiveTimestamp);
504 
505         activeTimestampValue = htobe64(activeTimestampValue);
506         aTxtList.emplace_back("at", reinterpret_cast<uint8_t *>(&activeTimestampValue), sizeof(activeTimestampValue));
507     }
508 }
509 
AppendVendorTxtEntries(const std::map<std::string,std::vector<uint8_t>> & aVendorEntries,Mdns::Publisher::TxtList & aTxtList)510 void AppendVendorTxtEntries(const std::map<std::string, std::vector<uint8_t>> &aVendorEntries,
511                             Mdns::Publisher::TxtList                          &aTxtList)
512 {
513     for (const auto &entry : aVendorEntries)
514     {
515         const std::string          &key   = entry.first;
516         const std::vector<uint8_t> &value = entry.second;
517         bool                        found = false;
518 
519         for (auto &addedEntry : aTxtList)
520         {
521             if (addedEntry.mKey == key)
522             {
523                 addedEntry.mValue              = value;
524                 addedEntry.mIsBooleanAttribute = false;
525                 found                          = true;
526                 break;
527             }
528         }
529         if (!found)
530         {
531             aTxtList.emplace_back(key.c_str(), value.data(), value.size());
532         }
533     }
534 }
535 
PublishMeshCopService(void)536 void BorderAgent::PublishMeshCopService(void)
537 {
538     StateBitmap              state;
539     uint32_t                 stateUint32;
540     otInstance              *instance    = mHost.GetInstance();
541     const otExtendedPanId   *extPanId    = otThreadGetExtendedPanId(instance);
542     const otExtAddress      *extAddr     = otLinkGetExtendedAddress(instance);
543     const char              *networkName = otThreadGetNetworkName(instance);
544     Mdns::Publisher::TxtList txtList{{"rv", "1"}};
545     Mdns::Publisher::TxtData txtData;
546     int                      port;
547     otbrError                error;
548 
549     OTBR_UNUSED_VARIABLE(error);
550 
551     otbrLogInfo("Publish meshcop service %s.%s.local.", mServiceInstanceName.c_str(), kBorderAgentServiceType);
552 
553 #if OTBR_ENABLE_PUBLISH_MESHCOP_BA_ID
554     {
555         otError         error;
556         otBorderAgentId id;
557 
558         error = otBorderAgentGetId(instance, &id);
559         if (error == OT_ERROR_NONE)
560         {
561             txtList.emplace_back("id", id.mId, sizeof(id));
562         }
563         else
564         {
565             otbrLogWarning("Failed to retrieve Border Agent ID: %s", otThreadErrorToString(error));
566         }
567     }
568 #endif
569 
570     if (!mVendorOui.empty())
571     {
572         txtList.emplace_back("vo", mVendorOui.data(), mVendorOui.size());
573     }
574     if (!mVendorName.empty())
575     {
576         txtList.emplace_back("vn", mVendorName.c_str());
577     }
578     if (!mProductName.empty())
579     {
580         txtList.emplace_back("mn", mProductName.c_str());
581     }
582     txtList.emplace_back("nn", networkName);
583     txtList.emplace_back("xp", extPanId->m8, sizeof(extPanId->m8));
584     txtList.emplace_back("tv", mHost.GetThreadVersion());
585 
586     // "xa" stands for Extended MAC Address (64-bit) of the Thread Interface of the Border Agent.
587     txtList.emplace_back("xa", extAddr->m8, sizeof(extAddr->m8));
588     state                 = GetStateBitmap(*instance);
589     state.mEpskcSupported = GetEphemeralKeyEnabled();
590     stateUint32           = htobe32(state.ToUint32());
591     txtList.emplace_back("sb", reinterpret_cast<uint8_t *>(&stateUint32), sizeof(stateUint32));
592 
593     if (state.mThreadIfStatus == kThreadIfStatusActive)
594     {
595         uint32_t partitionId;
596 
597         AppendActiveTimestampTxtEntry(*instance, txtList);
598         partitionId = otThreadGetPartitionId(instance);
599         txtList.emplace_back("pt", reinterpret_cast<uint8_t *>(&partitionId), sizeof(partitionId));
600     }
601 
602 #if OTBR_ENABLE_BACKBONE_ROUTER
603     AppendBbrTxtEntries(*instance, state, txtList);
604 #endif
605 #if OTBR_ENABLE_BORDER_ROUTING
606     AppendOmrTxtEntry(*instance, txtList);
607 #endif
608 
609     AppendVendorTxtEntries(mMeshCopTxtUpdate, txtList);
610 
611     if (otBorderAgentIsActive(instance))
612     {
613         port = otBorderAgentGetUdpPort(instance);
614     }
615     else
616     {
617         // When thread interface is not active, the border agent is not started, thus it's not listening to any port and
618         // not handling requests. In such situation, we use a dummy port number for publishing the MeshCoP service to
619         // advertise the status of the border router. One can learn the thread interface status from `sb` entry so it
620         // doesn't have to send requests to the dummy port when border agent is not running.
621         port = kBorderAgentServiceDummyPort;
622     }
623 
624     error = Mdns::Publisher::EncodeTxtData(txtList, txtData);
625     assert(error == OTBR_ERROR_NONE);
626 
627     mPublisher.PublishService(/* aHostName */ "", mServiceInstanceName, kBorderAgentServiceType,
628                               Mdns::Publisher::SubTypeList{}, port, txtData, [this](otbrError aError) {
629                                   if (aError == OTBR_ERROR_ABORTED)
630                                   {
631                                       // OTBR_ERROR_ABORTED is thrown when an ongoing service registration is
632                                       // cancelled. This can happen when the meshcop service is being updated
633                                       // frequently. To avoid false alarms, it should not be logged like a real error.
634                                       otbrLogInfo("Cancelled previous publishing meshcop service %s.%s.local",
635                                                   mServiceInstanceName.c_str(), kBorderAgentServiceType);
636                                   }
637                                   else
638                                   {
639                                       otbrLogResult(aError, "Result of publish meshcop service %s.%s.local",
640                                                     mServiceInstanceName.c_str(), kBorderAgentServiceType);
641                                   }
642                                   if (aError == OTBR_ERROR_DUPLICATED)
643                                   {
644                                       // Try to unpublish current service in case we are trying to register
645                                       // multiple new services simultaneously when the original service name
646                                       // is conflicted.
647                                       UnpublishMeshCopService();
648                                       mServiceInstanceName = GetAlternativeServiceInstanceName();
649                                       PublishMeshCopService();
650                                   }
651                               });
652 }
653 
UnpublishMeshCopService(void)654 void BorderAgent::UnpublishMeshCopService(void)
655 {
656     otbrLogInfo("Unpublish meshcop service %s.%s.local", mServiceInstanceName.c_str(), kBorderAgentServiceType);
657 
658     mPublisher.UnpublishService(mServiceInstanceName, kBorderAgentServiceType, [this](otbrError aError) {
659         otbrLogResult(aError, "Result of unpublish meshcop service %s.%s.local", mServiceInstanceName.c_str(),
660                       kBorderAgentServiceType);
661     });
662 }
663 
UpdateMeshCopService(void)664 void BorderAgent::UpdateMeshCopService(void)
665 {
666     VerifyOrExit(IsEnabled());
667     VerifyOrExit(mPublisher.IsStarted());
668     PublishMeshCopService();
669 
670 exit:
671     return;
672 }
673 
674 #if OTBR_ENABLE_DBUS_SERVER
HandleUpdateVendorMeshCoPTxtEntries(std::map<std::string,std::vector<uint8_t>> aUpdate)675 void BorderAgent::HandleUpdateVendorMeshCoPTxtEntries(std::map<std::string, std::vector<uint8_t>> aUpdate)
676 {
677     mMeshCopTxtUpdate = std::move(aUpdate);
678     UpdateMeshCopService();
679 }
680 #endif
681 
HandleThreadStateChanged(otChangedFlags aFlags)682 void BorderAgent::HandleThreadStateChanged(otChangedFlags aFlags)
683 {
684     VerifyOrExit(IsEnabled());
685 
686     if (aFlags & OT_CHANGED_THREAD_ROLE)
687     {
688         otbrLogInfo("Thread is %s", (IsThreadStarted() ? "up" : "down"));
689     }
690 
691     if (aFlags & (OT_CHANGED_THREAD_ROLE | OT_CHANGED_THREAD_EXT_PANID | OT_CHANGED_THREAD_NETWORK_NAME |
692                   OT_CHANGED_THREAD_BACKBONE_ROUTER_STATE | OT_CHANGED_THREAD_NETDATA))
693     {
694         UpdateMeshCopService();
695     }
696 
697 exit:
698     return;
699 }
700 
IsThreadStarted(void) const701 bool BorderAgent::IsThreadStarted(void) const
702 {
703     otDeviceRole role = mHost.GetDeviceRole();
704 
705     return role == OT_DEVICE_ROLE_CHILD || role == OT_DEVICE_ROLE_ROUTER || role == OT_DEVICE_ROLE_LEADER;
706 }
707 
GetServiceInstanceNameWithExtAddr(const std::string & aServiceInstanceName) const708 std::string BorderAgent::GetServiceInstanceNameWithExtAddr(const std::string &aServiceInstanceName) const
709 {
710     const otExtAddress *extAddress = otLinkGetExtendedAddress(mHost.GetInstance());
711     std::stringstream   ss;
712 
713     ss << aServiceInstanceName << " #";
714     ss << std::uppercase << std::hex << std::setfill('0');
715     ss << std::setw(2) << static_cast<int>(extAddress->m8[6]);
716     ss << std::setw(2) << static_cast<int>(extAddress->m8[7]);
717     return ss.str();
718 }
719 
GetAlternativeServiceInstanceName() const720 std::string BorderAgent::GetAlternativeServiceInstanceName() const
721 {
722     std::random_device                      r;
723     std::default_random_engine              engine(r());
724     std::uniform_int_distribution<uint16_t> uniform_dist(1, 0xFFFF);
725     uint16_t                                rand = uniform_dist(engine);
726     std::stringstream                       ss;
727 
728     ss << GetServiceInstanceNameWithExtAddr(mBaseServiceInstanceName) << " (" << rand << ")";
729     return ss.str();
730 }
731 
732 } // namespace otbr
733