xref: /aosp_15_r20/external/ot-br-posix/src/border_agent/border_agent.cpp (revision 4a64e381480ef79f0532b2421e44e6ee336b8e0d)
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 "ncp/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::Ncp::RcpHost & aHost,Mdns::Publisher & aPublisher)159 BorderAgent::BorderAgent(otbr::Ncp::RcpHost &aHost, Mdns::Publisher &aPublisher)
160     : mHost(aHost)
161     , mPublisher(aPublisher)
162     , mIsEnabled(false)
163     , mIsEphemeralKeyEnabled(otThreadGetVersion() >= OT_THREAD_VERSION_1_4)
164     , mVendorName(OTBR_VENDOR_NAME)
165     , mProductName(OTBR_PRODUCT_NAME)
166     , mBaseServiceInstanceName(OTBR_MESHCOP_SERVICE_INSTANCE_NAME)
167 {
168     mHost.AddThreadStateChangedCallback([this](otChangedFlags aFlags) { HandleThreadStateChanged(aFlags); });
169     otbrLogInfo("Ephemeral Key is: %s during initialization", (mIsEphemeralKeyEnabled ? "enabled" : "disabled"));
170 }
171 
CreateEphemeralKey(std::string & aEphemeralKey)172 otbrError BorderAgent::CreateEphemeralKey(std::string &aEphemeralKey)
173 {
174     std::string digitString;
175     char        checksum;
176     uint8_t     candidateBuffer[1];
177     otbrError   error = OTBR_ERROR_NONE;
178 
179     for (uint8_t i = 0; i < kEpskcRandomGenLen; ++i)
180     {
181         while (true)
182         {
183             SuccessOrExit(otRandomCryptoFillBuffer(candidateBuffer, 1), error = OTBR_ERROR_ABORTED);
184             // Generates a random number in the range [0, 9] with equal probability.
185             if (candidateBuffer[0] < 250)
186             {
187                 digitString += static_cast<char>('0' + candidateBuffer[0] % 10);
188                 break;
189             }
190         }
191     }
192     SuccessOrExit(otVerhoeffChecksumCalculate(digitString.c_str(), &checksum), error = OTBR_ERROR_INVALID_ARGS);
193     aEphemeralKey = digitString + checksum;
194 
195 exit:
196     return error;
197 }
198 
SetMeshCopServiceValues(const std::string & aServiceInstanceName,const std::string & aProductName,const std::string & aVendorName,const std::vector<uint8_t> & aVendorOui,const Mdns::Publisher::TxtList & aNonStandardTxtEntries)199 otbrError BorderAgent::SetMeshCopServiceValues(const std::string              &aServiceInstanceName,
200                                                const std::string              &aProductName,
201                                                const std::string              &aVendorName,
202                                                const std::vector<uint8_t>     &aVendorOui,
203                                                const Mdns::Publisher::TxtList &aNonStandardTxtEntries)
204 {
205     otbrError error = OTBR_ERROR_NONE;
206 
207     VerifyOrExit(aProductName.size() <= kMaxProductNameLength, error = OTBR_ERROR_INVALID_ARGS);
208     VerifyOrExit(aVendorName.size() <= kMaxVendorNameLength, error = OTBR_ERROR_INVALID_ARGS);
209     VerifyOrExit(aVendorOui.empty() || aVendorOui.size() == kVendorOuiLength, error = OTBR_ERROR_INVALID_ARGS);
210     for (const auto &txtEntry : aNonStandardTxtEntries)
211     {
212         VerifyOrExit(!txtEntry.mKey.empty() && txtEntry.mKey.front() == 'v', error = OTBR_ERROR_INVALID_ARGS);
213     }
214 
215     mProductName = aProductName;
216     mVendorName  = aVendorName;
217     mVendorOui   = aVendorOui;
218     mMeshCopTxtUpdate.clear();
219     for (const auto &txtEntry : aNonStandardTxtEntries)
220     {
221         mMeshCopTxtUpdate[txtEntry.mKey] = txtEntry.mValue;
222     }
223 
224     mBaseServiceInstanceName = aServiceInstanceName;
225 
226 exit:
227     return error;
228 }
229 
SetEnabled(bool aIsEnabled)230 void BorderAgent::SetEnabled(bool aIsEnabled)
231 {
232     VerifyOrExit(IsEnabled() != aIsEnabled);
233     mIsEnabled = aIsEnabled;
234     if (mIsEnabled)
235     {
236         Start();
237     }
238     else
239     {
240         Stop();
241     }
242 exit:
243     return;
244 }
245 
SetEphemeralKeyEnabled(bool aIsEnabled)246 void BorderAgent::SetEphemeralKeyEnabled(bool aIsEnabled)
247 {
248     VerifyOrExit(GetEphemeralKeyEnabled() != aIsEnabled);
249     mIsEphemeralKeyEnabled = aIsEnabled;
250 
251     if (!mIsEphemeralKeyEnabled)
252     {
253         // If the ePSKc feature is enabled, we call the clear function which
254         // will wait for the session to close if it is in active use before
255         // removing ephemeral key and unpublishing the service.
256         otBorderAgentClearEphemeralKey(mHost.GetInstance());
257     }
258 
259     UpdateMeshCopService();
260 
261 exit:
262     return;
263 }
264 
Start(void)265 void BorderAgent::Start(void)
266 {
267     otbrLogInfo("Start Thread Border Agent");
268 
269 #if OTBR_ENABLE_DBUS_SERVER
270     mHost.GetThreadHelper()->SetUpdateMeshCopTxtHandler([this](std::map<std::string, std::vector<uint8_t>> aUpdate) {
271         HandleUpdateVendorMeshCoPTxtEntries(std::move(aUpdate));
272     });
273     mHost.RegisterResetHandler([this]() {
274         mHost.GetThreadHelper()->SetUpdateMeshCopTxtHandler(
275             [this](std::map<std::string, std::vector<uint8_t>> aUpdate) {
276                 HandleUpdateVendorMeshCoPTxtEntries(std::move(aUpdate));
277             });
278     });
279 #endif
280 
281     mServiceInstanceName = GetServiceInstanceNameWithExtAddr(mBaseServiceInstanceName);
282     UpdateMeshCopService();
283 
284     otBorderAgentSetEphemeralKeyCallback(mHost.GetInstance(), BorderAgent::HandleEpskcStateChanged, this);
285 }
286 
Stop(void)287 void BorderAgent::Stop(void)
288 {
289     otbrLogInfo("Stop Thread Border Agent");
290     UnpublishMeshCopService();
291 }
292 
HandleEpskcStateChanged(void * aContext)293 void BorderAgent::HandleEpskcStateChanged(void *aContext)
294 {
295     BorderAgent *borderAgent = static_cast<BorderAgent *>(aContext);
296 
297     if (otBorderAgentIsEphemeralKeyActive(borderAgent->mHost.GetInstance()))
298     {
299         borderAgent->PublishEpskcService();
300     }
301     else
302     {
303         borderAgent->UnpublishEpskcService();
304     }
305 
306     for (auto &ephemeralKeyCallback : borderAgent->mEphemeralKeyChangedCallbacks)
307     {
308         ephemeralKeyCallback();
309     }
310 }
311 
PublishEpskcService()312 void BorderAgent::PublishEpskcService()
313 {
314     otInstance *instance = mHost.GetInstance();
315     int         port     = otBorderAgentGetUdpPort(instance);
316 
317     otbrLogInfo("Publish meshcop-e service %s.%s.local. port %d", mServiceInstanceName.c_str(),
318                 kBorderAgentEpskcServiceType, port);
319 
320     mPublisher.PublishService(/* aHostName */ "", mServiceInstanceName, kBorderAgentEpskcServiceType,
321                               Mdns::Publisher::SubTypeList{}, port, /* aTxtData */ {}, [this](otbrError aError) {
322                                   if (aError == OTBR_ERROR_ABORTED)
323                                   {
324                                       // OTBR_ERROR_ABORTED is thrown when an ongoing service registration is
325                                       // cancelled. This can happen when the meshcop-e service is being updated
326                                       // frequently. To avoid false alarms, it should not be logged like a real error.
327                                       otbrLogInfo("Cancelled previous publishing meshcop-e service %s.%s.local",
328                                                   mServiceInstanceName.c_str(), kBorderAgentEpskcServiceType);
329                                   }
330                                   else
331                                   {
332                                       otbrLogResult(aError, "Result of publish meshcop-e service %s.%s.local",
333                                                     mServiceInstanceName.c_str(), kBorderAgentEpskcServiceType);
334                                   }
335 
336                                   if (aError == OTBR_ERROR_DUPLICATED)
337                                   {
338                                       // Try to unpublish current service in case we are trying to register
339                                       // multiple new services simultaneously when the original service name
340                                       // is conflicted.
341                                       // Potential risk that instance name is not the same with meshcop service.
342                                       UnpublishEpskcService();
343                                       mServiceInstanceName = GetAlternativeServiceInstanceName();
344                                       PublishEpskcService();
345                                   }
346                               });
347 }
348 
UnpublishEpskcService()349 void BorderAgent::UnpublishEpskcService()
350 {
351     otbrLogInfo("Unpublish meshcop-e service %s.%s.local", mServiceInstanceName.c_str(), kBorderAgentEpskcServiceType);
352 
353     mPublisher.UnpublishService(mServiceInstanceName, kBorderAgentEpskcServiceType, [this](otbrError aError) {
354         otbrLogResult(aError, "Result of unpublish meshcop-e service %s.%s.local", mServiceInstanceName.c_str(),
355                       kBorderAgentEpskcServiceType);
356     });
357 }
358 
AddEphemeralKeyChangedCallback(EphemeralKeyChangedCallback aCallback)359 void BorderAgent::AddEphemeralKeyChangedCallback(EphemeralKeyChangedCallback aCallback)
360 {
361     mEphemeralKeyChangedCallbacks.push_back(std::move(aCallback));
362 }
363 
HandleMdnsState(Mdns::Publisher::State aState)364 void BorderAgent::HandleMdnsState(Mdns::Publisher::State aState)
365 {
366     VerifyOrExit(IsEnabled());
367 
368     switch (aState)
369     {
370     case Mdns::Publisher::State::kReady:
371         UpdateMeshCopService();
372         break;
373     default:
374         otbrLogWarning("mDNS publisher not available!");
375         break;
376     }
377 exit:
378     return;
379 }
380 
ConvertTimestampToUint64(const otTimestamp & aTimestamp)381 static uint64_t ConvertTimestampToUint64(const otTimestamp &aTimestamp)
382 {
383     // 64 bits Timestamp fields layout
384     //-----48 bits------//-----15 bits-----//-------1 bit-------//
385     //     Seconds      //      Ticks      //  Authoritative    //
386     return (aTimestamp.mSeconds << 16) | static_cast<uint64_t>(aTimestamp.mTicks << 1) |
387            static_cast<uint64_t>(aTimestamp.mAuthoritative);
388 }
389 
390 #if OTBR_ENABLE_BORDER_ROUTING
AppendOmrTxtEntry(otInstance & aInstance,Mdns::Publisher::TxtList & aTxtList)391 void AppendOmrTxtEntry(otInstance &aInstance, Mdns::Publisher::TxtList &aTxtList)
392 {
393     otIp6Prefix       omrPrefix;
394     otRoutePreference preference;
395 
396     if (OT_ERROR_NONE == otBorderRoutingGetFavoredOmrPrefix(&aInstance, &omrPrefix, &preference))
397     {
398         std::vector<uint8_t> omrData;
399 
400         omrData.reserve(1 + OT_IP6_PREFIX_SIZE);
401         omrData.push_back(omrPrefix.mLength);
402         std::copy(omrPrefix.mPrefix.mFields.m8, omrPrefix.mPrefix.mFields.m8 + (omrPrefix.mLength + 7) / 8,
403                   std::back_inserter(omrData));
404         aTxtList.emplace_back("omr", omrData.data(), omrData.size());
405     }
406 }
407 #endif
408 
GetStateBitmap(otInstance & aInstance)409 StateBitmap GetStateBitmap(otInstance &aInstance)
410 {
411     StateBitmap state;
412 
413     state.mConnectionMode = kConnectionModePskc;
414     state.mAvailability   = kAvailabilityHigh;
415 
416     switch (otThreadGetDeviceRole(&aInstance))
417     {
418     case OT_DEVICE_ROLE_DISABLED:
419         state.mThreadIfStatus = kThreadIfStatusNotInitialized;
420         state.mThreadRole     = kThreadRoleDisabledOrDetached;
421         break;
422     case OT_DEVICE_ROLE_DETACHED:
423         state.mThreadIfStatus = kThreadIfStatusInitialized;
424         state.mThreadRole     = kThreadRoleDisabledOrDetached;
425         break;
426     case OT_DEVICE_ROLE_CHILD:
427         state.mThreadIfStatus = kThreadIfStatusActive;
428         state.mThreadRole     = kThreadRoleChild;
429         break;
430     case OT_DEVICE_ROLE_ROUTER:
431         state.mThreadIfStatus = kThreadIfStatusActive;
432         state.mThreadRole     = kThreadRoleRouter;
433         break;
434     case OT_DEVICE_ROLE_LEADER:
435         state.mThreadIfStatus = kThreadIfStatusActive;
436         state.mThreadRole     = kThreadRoleLeader;
437         break;
438     }
439 
440 #if OTBR_ENABLE_BACKBONE_ROUTER
441     state.mBbrIsActive = state.mThreadIfStatus == kThreadIfStatusActive &&
442                          otBackboneRouterGetState(&aInstance) != OT_BACKBONE_ROUTER_STATE_DISABLED;
443     state.mBbrIsPrimary = state.mThreadIfStatus == kThreadIfStatusActive &&
444                           otBackboneRouterGetState(&aInstance) == OT_BACKBONE_ROUTER_STATE_PRIMARY;
445 #endif
446 
447     return state;
448 }
449 
450 #if OTBR_ENABLE_BACKBONE_ROUTER
AppendBbrTxtEntries(otInstance & aInstance,StateBitmap aState,Mdns::Publisher::TxtList & aTxtList)451 void AppendBbrTxtEntries(otInstance &aInstance, StateBitmap aState, Mdns::Publisher::TxtList &aTxtList)
452 {
453     if (aState.mBbrIsActive)
454     {
455         otBackboneRouterConfig bbrConfig;
456         uint16_t               bbrPort = htobe16(BackboneRouter::BackboneAgent::kBackboneUdpPort);
457 
458         otBackboneRouterGetConfig(&aInstance, &bbrConfig);
459         aTxtList.emplace_back("sq", &bbrConfig.mSequenceNumber, sizeof(bbrConfig.mSequenceNumber));
460         aTxtList.emplace_back("bb", reinterpret_cast<const uint8_t *>(&bbrPort), sizeof(bbrPort));
461     }
462 
463     aTxtList.emplace_back("dn", otThreadGetDomainName(&aInstance));
464 }
465 #endif
466 
AppendActiveTimestampTxtEntry(otInstance & aInstance,Mdns::Publisher::TxtList & aTxtList)467 void AppendActiveTimestampTxtEntry(otInstance &aInstance, Mdns::Publisher::TxtList &aTxtList)
468 {
469     otError              error;
470     otOperationalDataset activeDataset;
471 
472     if ((error = otDatasetGetActive(&aInstance, &activeDataset)) != OT_ERROR_NONE)
473     {
474         otbrLogWarning("Failed to get active dataset: %s", otThreadErrorToString(error));
475     }
476     else
477     {
478         uint64_t activeTimestampValue = ConvertTimestampToUint64(activeDataset.mActiveTimestamp);
479 
480         activeTimestampValue = htobe64(activeTimestampValue);
481         aTxtList.emplace_back("at", reinterpret_cast<uint8_t *>(&activeTimestampValue), sizeof(activeTimestampValue));
482     }
483 }
484 
AppendVendorTxtEntries(const std::map<std::string,std::vector<uint8_t>> & aVendorEntries,Mdns::Publisher::TxtList & aTxtList)485 void AppendVendorTxtEntries(const std::map<std::string, std::vector<uint8_t>> &aVendorEntries,
486                             Mdns::Publisher::TxtList                          &aTxtList)
487 {
488     for (const auto &entry : aVendorEntries)
489     {
490         const std::string          &key   = entry.first;
491         const std::vector<uint8_t> &value = entry.second;
492         bool                        found = false;
493 
494         for (auto &addedEntry : aTxtList)
495         {
496             if (addedEntry.mKey == key)
497             {
498                 addedEntry.mValue              = value;
499                 addedEntry.mIsBooleanAttribute = false;
500                 found                          = true;
501                 break;
502             }
503         }
504         if (!found)
505         {
506             aTxtList.emplace_back(key.c_str(), value.data(), value.size());
507         }
508     }
509 }
510 
PublishMeshCopService(void)511 void BorderAgent::PublishMeshCopService(void)
512 {
513     StateBitmap              state;
514     uint32_t                 stateUint32;
515     otInstance              *instance    = mHost.GetInstance();
516     const otExtendedPanId   *extPanId    = otThreadGetExtendedPanId(instance);
517     const otExtAddress      *extAddr     = otLinkGetExtendedAddress(instance);
518     const char              *networkName = otThreadGetNetworkName(instance);
519     Mdns::Publisher::TxtList txtList{{"rv", "1"}};
520     Mdns::Publisher::TxtData txtData;
521     int                      port;
522     otbrError                error;
523 
524     OTBR_UNUSED_VARIABLE(error);
525 
526     otbrLogInfo("Publish meshcop service %s.%s.local.", mServiceInstanceName.c_str(), kBorderAgentServiceType);
527 
528 #if OTBR_ENABLE_PUBLISH_MESHCOP_BA_ID
529     {
530         otError         error;
531         otBorderAgentId id;
532 
533         error = otBorderAgentGetId(instance, &id);
534         if (error == OT_ERROR_NONE)
535         {
536             txtList.emplace_back("id", id.mId, sizeof(id));
537         }
538         else
539         {
540             otbrLogWarning("Failed to retrieve Border Agent ID: %s", otThreadErrorToString(error));
541         }
542     }
543 #endif
544 
545     if (!mVendorOui.empty())
546     {
547         txtList.emplace_back("vo", mVendorOui.data(), mVendorOui.size());
548     }
549     if (!mVendorName.empty())
550     {
551         txtList.emplace_back("vn", mVendorName.c_str());
552     }
553     if (!mProductName.empty())
554     {
555         txtList.emplace_back("mn", mProductName.c_str());
556     }
557     txtList.emplace_back("nn", networkName);
558     txtList.emplace_back("xp", extPanId->m8, sizeof(extPanId->m8));
559     txtList.emplace_back("tv", mHost.GetThreadVersion());
560 
561     // "xa" stands for Extended MAC Address (64-bit) of the Thread Interface of the Border Agent.
562     txtList.emplace_back("xa", extAddr->m8, sizeof(extAddr->m8));
563     state                 = GetStateBitmap(*instance);
564     state.mEpskcSupported = GetEphemeralKeyEnabled();
565     stateUint32           = htobe32(state.ToUint32());
566     txtList.emplace_back("sb", reinterpret_cast<uint8_t *>(&stateUint32), sizeof(stateUint32));
567 
568     if (state.mThreadIfStatus == kThreadIfStatusActive)
569     {
570         uint32_t partitionId;
571 
572         AppendActiveTimestampTxtEntry(*instance, txtList);
573         partitionId = otThreadGetPartitionId(instance);
574         txtList.emplace_back("pt", reinterpret_cast<uint8_t *>(&partitionId), sizeof(partitionId));
575     }
576 
577 #if OTBR_ENABLE_BACKBONE_ROUTER
578     AppendBbrTxtEntries(*instance, state, txtList);
579 #endif
580 #if OTBR_ENABLE_BORDER_ROUTING
581     AppendOmrTxtEntry(*instance, txtList);
582 #endif
583 
584     AppendVendorTxtEntries(mMeshCopTxtUpdate, txtList);
585 
586     if (otBorderAgentGetState(instance) != OT_BORDER_AGENT_STATE_STOPPED)
587     {
588         port = otBorderAgentGetUdpPort(instance);
589     }
590     else
591     {
592         // When thread interface is not active, the border agent is not started, thus it's not listening to any port and
593         // not handling requests. In such situation, we use a dummy port number for publishing the MeshCoP service to
594         // advertise the status of the border router. One can learn the thread interface status from `sb` entry so it
595         // doesn't have to send requests to the dummy port when border agent is not running.
596         port = kBorderAgentServiceDummyPort;
597     }
598 
599     error = Mdns::Publisher::EncodeTxtData(txtList, txtData);
600     assert(error == OTBR_ERROR_NONE);
601 
602     mPublisher.PublishService(/* aHostName */ "", mServiceInstanceName, kBorderAgentServiceType,
603                               Mdns::Publisher::SubTypeList{}, port, txtData, [this](otbrError aError) {
604                                   if (aError == OTBR_ERROR_ABORTED)
605                                   {
606                                       // OTBR_ERROR_ABORTED is thrown when an ongoing service registration is
607                                       // cancelled. This can happen when the meshcop service is being updated
608                                       // frequently. To avoid false alarms, it should not be logged like a real error.
609                                       otbrLogInfo("Cancelled previous publishing meshcop service %s.%s.local",
610                                                   mServiceInstanceName.c_str(), kBorderAgentServiceType);
611                                   }
612                                   else
613                                   {
614                                       otbrLogResult(aError, "Result of publish meshcop service %s.%s.local",
615                                                     mServiceInstanceName.c_str(), kBorderAgentServiceType);
616                                   }
617                                   if (aError == OTBR_ERROR_DUPLICATED)
618                                   {
619                                       // Try to unpublish current service in case we are trying to register
620                                       // multiple new services simultaneously when the original service name
621                                       // is conflicted.
622                                       UnpublishMeshCopService();
623                                       mServiceInstanceName = GetAlternativeServiceInstanceName();
624                                       PublishMeshCopService();
625                                   }
626                               });
627 }
628 
UnpublishMeshCopService(void)629 void BorderAgent::UnpublishMeshCopService(void)
630 {
631     otbrLogInfo("Unpublish meshcop service %s.%s.local", mServiceInstanceName.c_str(), kBorderAgentServiceType);
632 
633     mPublisher.UnpublishService(mServiceInstanceName, kBorderAgentServiceType, [this](otbrError aError) {
634         otbrLogResult(aError, "Result of unpublish meshcop service %s.%s.local", mServiceInstanceName.c_str(),
635                       kBorderAgentServiceType);
636     });
637 }
638 
UpdateMeshCopService(void)639 void BorderAgent::UpdateMeshCopService(void)
640 {
641     VerifyOrExit(IsEnabled());
642     VerifyOrExit(mPublisher.IsStarted());
643     PublishMeshCopService();
644 
645 exit:
646     return;
647 }
648 
649 #if OTBR_ENABLE_DBUS_SERVER
HandleUpdateVendorMeshCoPTxtEntries(std::map<std::string,std::vector<uint8_t>> aUpdate)650 void BorderAgent::HandleUpdateVendorMeshCoPTxtEntries(std::map<std::string, std::vector<uint8_t>> aUpdate)
651 {
652     mMeshCopTxtUpdate = std::move(aUpdate);
653     UpdateMeshCopService();
654 }
655 #endif
656 
HandleThreadStateChanged(otChangedFlags aFlags)657 void BorderAgent::HandleThreadStateChanged(otChangedFlags aFlags)
658 {
659     VerifyOrExit(IsEnabled());
660 
661     if (aFlags & OT_CHANGED_THREAD_ROLE)
662     {
663         otbrLogInfo("Thread is %s", (IsThreadStarted() ? "up" : "down"));
664     }
665 
666     if (aFlags & (OT_CHANGED_THREAD_ROLE | OT_CHANGED_THREAD_EXT_PANID | OT_CHANGED_THREAD_NETWORK_NAME |
667                   OT_CHANGED_THREAD_BACKBONE_ROUTER_STATE | OT_CHANGED_THREAD_NETDATA))
668     {
669         UpdateMeshCopService();
670     }
671 
672 exit:
673     return;
674 }
675 
IsThreadStarted(void) const676 bool BorderAgent::IsThreadStarted(void) const
677 {
678     otDeviceRole role = mHost.GetDeviceRole();
679 
680     return role == OT_DEVICE_ROLE_CHILD || role == OT_DEVICE_ROLE_ROUTER || role == OT_DEVICE_ROLE_LEADER;
681 }
682 
GetServiceInstanceNameWithExtAddr(const std::string & aServiceInstanceName) const683 std::string BorderAgent::GetServiceInstanceNameWithExtAddr(const std::string &aServiceInstanceName) const
684 {
685     const otExtAddress *extAddress = otLinkGetExtendedAddress(mHost.GetInstance());
686     std::stringstream   ss;
687 
688     ss << aServiceInstanceName << " #";
689     ss << std::uppercase << std::hex << std::setfill('0');
690     ss << std::setw(2) << static_cast<int>(extAddress->m8[6]);
691     ss << std::setw(2) << static_cast<int>(extAddress->m8[7]);
692     return ss.str();
693 }
694 
GetAlternativeServiceInstanceName() const695 std::string BorderAgent::GetAlternativeServiceInstanceName() const
696 {
697     std::random_device                      r;
698     std::default_random_engine              engine(r());
699     std::uniform_int_distribution<uint16_t> uniform_dist(1, 0xFFFF);
700     uint16_t                                rand = uniform_dist(engine);
701     std::stringstream                       ss;
702 
703     ss << GetServiceInstanceNameWithExtAddr(mBaseServiceInstanceName) << " (" << rand << ")";
704     return ss.str();
705 }
706 
707 } // namespace otbr
708