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