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