[border-agent] new API to set vendor-specific extra TXT data (#11487)

This commit introduces a new API to allow setting vendor-specific
extra TXT data to be included when the Border Agent advertises its
mDNS `_meshcop._udp` service.

The provided vendor TXT data is appended as given to the TXT data
generated by the Border Agent and included in the `_meshcop._udp`
mDNS service advertisement.

This vendor TXT data can be set at any time, regardless of the Border
Agent's state. Any change from a previously set value will trigger an
update of the registered mDNS service to advertise the new TXT data.

This commit also updates `test_border_agent` by generalizing and
simplifying the validation of the Border Agent's `_meshcop._udp`
service TXT data. The tests are also expanded to validate the
complete registered service TXT data, including the newly added
support for vendor-specific extra TXT data.
This commit is contained in:
Abtin Keshavarzian
2025-05-13 17:56:36 -07:00
committed by GitHub
parent 7b1fdbf65f
commit 8244bc7fad
6 changed files with 397 additions and 154 deletions
+294 -146
View File
@@ -756,36 +756,107 @@ void TestBorderAgentEphemeralKey(void)
//----------------------------------------------------------------------------------------------------------------------
class TxtDataTester
struct TxtData
{
public:
TxtDataTester(BorderAgent &aBorderAgent)
: mBorderAgent(aBorderAgent)
, mIsRunning(false)
, mUdpPort(0)
, mCallbackInvoked(false)
void Init(const uint8_t *aData, uint16_t aLength) { mData = aData, mLength = aLength; }
void ValidateFormat(void)
{
TxtEntry::Iterator iter;
TxtEntry txtEntry;
iter.Init(mData, mLength);
while (true)
{
Error error = iter.GetNextEntry(txtEntry);
if (error == kErrorNotFound)
{
break;
}
SuccessOrQuit(error);
VerifyOrQuit(txtEntry.mKey != nullptr);
}
}
static void HandleServiceChanged(void *aContext) { static_cast<TxtDataTester *>(aContext)->HandleServiceChanged(); }
void HandleServiceChanged(void)
void LogAllTxtEntries(void)
{
mIsRunning = mBorderAgent.IsRunning();
mUdpPort = mBorderAgent.GetUdpPort();
SuccessOrQuit(mBorderAgent.PrepareServiceTxtData(mTxtData));
mCallbackInvoked = true;
static constexpr uint16_t kValueStringSize = 256;
char valueString[kValueStringSize];
TxtEntry::Iterator iter;
TxtEntry txtEntry;
Log("TXT data - length %u", mLength);
iter.Init(mData, mLength);
while (true)
{
Error error = iter.GetNextEntry(txtEntry);
StringWriter writer(valueString, sizeof(valueString));
if (error == kErrorNotFound)
{
break;
}
SuccessOrQuit(error);
VerifyOrQuit(txtEntry.mKey != nullptr);
writer.AppendHexBytes(txtEntry.mValue, txtEntry.mValueLength);
Log(" %s -> [%s] (len:%u)", txtEntry.mKey, valueString, txtEntry.mValueLength);
}
}
bool FindTxtEntry(const char *aKey, TxtEntry &aTxtEntry)
bool ContainsKey(const char *aKey) const
{
TxtEntry txtEntry;
return FindTxtEntry(aKey, txtEntry);
}
void ValidateKey(const char *aKey, const void *aValue, uint16_t aValueLength) const
{
TxtEntry txtEntry;
VerifyOrQuit(FindTxtEntry(aKey, txtEntry));
VerifyOrQuit(txtEntry.mValueLength == aValueLength);
VerifyOrQuit(memcmp(txtEntry.mValue, aValue, aValueLength) == 0);
}
template <typename ObjectType> void ValidateKey(const char *aKey, const ObjectType &aObject) const
{
static_assert(!TypeTraits::IsPointer<ObjectType>::kValue, "ObjectType must not be a pointer");
ValidateKey(aKey, &aObject, sizeof(aObject));
}
void ValidateKey(const char *aKey, const char *aString) const { ValidateKey(aKey, aString, strlen(aString)); }
uint32_t ReadUint32Key(const char *aKey) const
{
TxtEntry txtEntry;
VerifyOrQuit(FindTxtEntry(aKey, txtEntry));
VerifyOrQuit(txtEntry.mValueLength == sizeof(uint32_t));
return BigEndian::ReadUint32(txtEntry.mValue);
}
bool FindTxtEntry(const char *aKey, TxtEntry &aTxtEntry) const
{
bool found = false;
TxtEntry::Iterator iter;
iter.Init(mTxtData.mData, mTxtData.mLength);
iter.Init(mData, mLength);
while (iter.GetNextEntry(aTxtEntry) == kErrorNone)
{
if (strcmp(aTxtEntry.mKey, aKey) == 0)
VerifyOrQuit(aTxtEntry.mKey != nullptr);
if (StringMatch(aTxtEntry.mKey, aKey))
{
found = true;
break;
@@ -795,27 +866,11 @@ public:
return found;
}
BorderAgent &mBorderAgent;
BorderAgent::ServiceTxtData mTxtData;
bool mIsRunning;
uint16_t mUdpPort;
bool mCallbackInvoked;
const uint8_t *mData;
uint16_t mLength;
};
template <typename ObjectType> bool CheckObjectSameAsTxtEntryData(const TxtEntry &aTxtEntry, const ObjectType &aObject)
{
static_assert(!TypeTraits::IsPointer<ObjectType>::kValue, "ObjectType must not be a pointer");
return aTxtEntry.mValueLength == sizeof(ObjectType) && memcmp(aTxtEntry.mValue, &aObject, sizeof(ObjectType)) == 0;
}
template <> bool CheckObjectSameAsTxtEntryData<NameData>(const TxtEntry &aTxtEntry, const NameData &aNameData)
{
return aTxtEntry.mValueLength == aNameData.GetLength() &&
memcmp(aTxtEntry.mValue, aNameData.GetBuffer(), aNameData.GetLength()) == 0;
}
void TestBorderAgentTxtDataCallback(void)
void ValidateMeshCoPTxtData(TxtData &aTxtData, Node &aNode)
{
// State bitmap masks and field values
static constexpr uint32_t kMaskConnectionMode = 7 << 0;
@@ -832,15 +887,100 @@ void TestBorderAgentTxtDataCallback(void)
static constexpr uint32_t kThreadRoleLeader = 3 << 9;
static constexpr uint32_t kFlagEpskcSupported = 1 << 11;
Core nexus;
Node &node0 = nexus.CreateNode();
TxtDataTester txtDataTester(node0.Get<BorderAgent>());
TxtEntry txtEntry;
uint32_t stateBitmap;
#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
BorderAgent::Id id;
uint32_t stateBitmap;
uint32_t threadIfStatus;
uint32_t threadRole;
aTxtData.ValidateFormat();
aTxtData.LogAllTxtEntries();
SuccessOrQuit(aNode.Get<BorderAgent>().GetId(id));
aTxtData.ValidateKey("id", id);
aTxtData.ValidateKey("nn", aNode.Get<NetworkNameManager>().GetNetworkName().GetAsCString());
aTxtData.ValidateKey("xp", aNode.Get<ExtendedPanIdManager>().GetExtPanId());
aTxtData.ValidateKey("tv", kThreadVersionString);
aTxtData.ValidateKey("xa", aNode.Get<Mac::Mac>().GetExtAddress());
if (aNode.Get<Mle::Mle>().IsAttached())
{
aTxtData.ValidateKey("pt", BigEndian::HostSwap32(aNode.Get<Mle::Mle>().GetLeaderData().GetPartitionId()));
aTxtData.ValidateKey("at", aNode.Get<ActiveDatasetManager>().GetTimestamp());
}
else
{
VerifyOrQuit(!aTxtData.ContainsKey("pt"));
VerifyOrQuit(!aTxtData.ContainsKey("at"));
}
stateBitmap = aTxtData.ReadUint32Key("sb");
VerifyOrQuit((stateBitmap & kMaskConnectionMode) == aNode.Get<BorderAgent>().IsRunning() ? kConnectionModePskc
: kConnectionModeDisabled);
switch (aNode.Get<Mle::Mle>().GetRole())
{
case Mle::DeviceRole::kRoleDisabled:
threadIfStatus = kThreadIfStatusNotInitialized;
threadRole = kThreadRoleDisabledOrDetached;
break;
case Mle::DeviceRole::kRoleDetached:
threadIfStatus = kThreadIfStatusInitialized;
threadRole = kThreadRoleDisabledOrDetached;
break;
case Mle::DeviceRole::kRoleChild:
threadIfStatus = kThreadIfStatusActive;
threadRole = kThreadRoleChild;
break;
case Mle::DeviceRole::kRoleRouter:
threadIfStatus = kThreadIfStatusActive;
threadRole = kThreadRoleRouter;
break;
case Mle::DeviceRole::kRoleLeader:
threadIfStatus = kThreadIfStatusActive;
threadRole = kThreadRoleLeader;
break;
}
VerifyOrQuit((stateBitmap & kMaskThreadIfStatus) == threadIfStatus);
VerifyOrQuit((stateBitmap & kMaskThreadRole) == threadRole);
if (aNode.Get<BorderAgent>().Get<EphemeralKeyManager>().GetState() !=
BorderAgent::EphemeralKeyManager::kStateDisabled)
{
VerifyOrQuit(stateBitmap & kFlagEpskcSupported);
}
else
{
VerifyOrQuit(!(stateBitmap & kFlagEpskcSupported));
}
}
//----------------------------------------------------------------------------------------------------------------------
void HandleServiceChanged(void *aContext) // Callback used in `TestBorderAgentTxtDataCallback().`
{
// `aContext` is a boolean `callbackInvoked`
VerifyOrQuit(aContext != nullptr);
*static_cast<bool *>(aContext) = true;
}
void ReadAndValidateMeshCoPTxtData(Node &aNode)
{
BorderAgent::ServiceTxtData serviceTxtData;
TxtData txtData;
SuccessOrQuit(aNode.Get<BorderAgent>().PrepareServiceTxtData(serviceTxtData));
txtData.Init(serviceTxtData.mData, serviceTxtData.mLength);
ValidateMeshCoPTxtData(txtData, aNode);
}
void TestBorderAgentTxtDataCallback(void)
{
Core nexus;
Node &node0 = nexus.CreateNode();
bool callbackInvoked = false;
BorderAgent::Id newId;
#endif
Log("------------------------------------------------------------------------------------------------------");
Log("TestBorderAgentTxtDataCallback");
@@ -848,152 +988,88 @@ void TestBorderAgentTxtDataCallback(void)
nexus.AdvanceTime(0);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 1. Set MeshCoP service change callback. Will get initial values.
// Set MeshCoP service change callback. Will get initial values.
Log("Set MeshCoP service change callback and check initial values");
node0.Get<BorderAgent>().SetServiceChangedCallback(TxtDataTester::HandleServiceChanged, &txtDataTester);
node0.Get<BorderAgent>().SetServiceChangedCallback(HandleServiceChanged, &callbackInvoked);
nexus.AdvanceTime(1);
// 1.1 Check the initial TXT entries
VerifyOrQuit(txtDataTester.mCallbackInvoked);
#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
VerifyOrQuit(txtDataTester.FindTxtEntry("id", txtEntry));
VerifyOrQuit(node0.Get<BorderAgent>().GetId(id) == kErrorNone);
VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, id));
#endif
VerifyOrQuit(txtDataTester.FindTxtEntry("nn", txtEntry));
VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, node0.Get<NetworkNameManager>().GetNetworkName().GetAsData()));
VerifyOrQuit(txtDataTester.FindTxtEntry("xp", txtEntry));
VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, node0.Get<ExtendedPanIdManager>().GetExtPanId()));
VerifyOrQuit(txtDataTester.FindTxtEntry("tv", txtEntry));
VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, NameData(kThreadVersionString, strlen(kThreadVersionString))));
VerifyOrQuit(txtDataTester.FindTxtEntry("xa", txtEntry));
VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, node0.Get<Mac::Mac>().GetExtAddress()));
// Check the initial TXT entries
ReadAndValidateMeshCoPTxtData(node0);
VerifyOrQuit(txtDataTester.FindTxtEntry("sb", txtEntry));
VerifyOrQuit(txtEntry.mValueLength == sizeof(uint32_t));
stateBitmap = BigEndian::ReadUint32(txtEntry.mValue);
VerifyOrQuit((stateBitmap & kMaskConnectionMode) == kConnectionModeDisabled);
VerifyOrQuit((stateBitmap & kMaskThreadIfStatus) == kThreadIfStatusNotInitialized);
VerifyOrQuit((stateBitmap & kMaskThreadRole) == kThreadRoleDisabledOrDetached);
VerifyOrQuit(stateBitmap & kFlagEpskcSupported);
VerifyOrQuit(txtDataTester.FindTxtEntry("pt", txtEntry) == false);
VerifyOrQuit(txtDataTester.FindTxtEntry("at", txtEntry) == false);
// 1.2 Check the Border Agent state
VerifyOrQuit(txtDataTester.mIsRunning == false);
VerifyOrQuit(txtDataTester.mUdpPort == 0);
// Check the Border Agent state
VerifyOrQuit(!node0.Get<BorderAgent>().IsRunning());
VerifyOrQuit(node0.Get<BorderAgent>().GetUdpPort() == 0);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 2. Join Thread network and check updated values and states.
txtDataTester.mCallbackInvoked = false;
// Join Thread network and check updated values and states.
callbackInvoked = false;
Log("Join Thread network and check updated Txt data and states");
node0.Form();
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
// 2.1 Check the initial TXT entries
VerifyOrQuit(txtDataTester.mCallbackInvoked);
#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
VerifyOrQuit(txtDataTester.FindTxtEntry("id", txtEntry));
VerifyOrQuit(node0.Get<BorderAgent>().GetId(id) == kErrorNone);
VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, id));
#endif
VerifyOrQuit(txtDataTester.FindTxtEntry("nn", txtEntry));
VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, node0.Get<NetworkNameManager>().GetNetworkName().GetAsData()));
VerifyOrQuit(txtDataTester.FindTxtEntry("xp", txtEntry));
VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, node0.Get<ExtendedPanIdManager>().GetExtPanId()));
VerifyOrQuit(txtDataTester.FindTxtEntry("tv", txtEntry));
VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, NameData(kThreadVersionString, strlen(kThreadVersionString))));
VerifyOrQuit(txtDataTester.FindTxtEntry("xa", txtEntry));
VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, node0.Get<Mac::Mac>().GetExtAddress()));
VerifyOrQuit(txtDataTester.FindTxtEntry("pt", txtEntry));
VerifyOrQuit(CheckObjectSameAsTxtEntryData(
txtEntry, BigEndian::HostSwap32(node0.Get<Mle::Mle>().GetLeaderData().GetPartitionId())));
VerifyOrQuit(txtDataTester.FindTxtEntry("at", txtEntry));
VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, node0.Get<ActiveDatasetManager>().GetTimestamp()));
VerifyOrQuit(callbackInvoked);
ReadAndValidateMeshCoPTxtData(node0);
VerifyOrQuit(txtDataTester.FindTxtEntry("sb", txtEntry));
VerifyOrQuit(txtEntry.mValueLength == sizeof(uint32_t));
stateBitmap = BigEndian::ReadUint32(txtEntry.mValue);
VerifyOrQuit((stateBitmap & kMaskConnectionMode) == kConnectionModePskc);
VerifyOrQuit((stateBitmap & kMaskThreadIfStatus) == kThreadIfStatusActive);
VerifyOrQuit((stateBitmap & kMaskThreadRole) == kThreadRoleLeader);
VerifyOrQuit(stateBitmap & kFlagEpskcSupported);
// 2.2 Check the Border Agent state
VerifyOrQuit(txtDataTester.mIsRunning == true);
VerifyOrQuit(txtDataTester.mUdpPort != 0);
VerifyOrQuit(node0.Get<BorderAgent>().IsRunning());
VerifyOrQuit(node0.Get<BorderAgent>().GetUdpPort() != 0);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
Log("Change the Border Agent ID and validate that TXT data changed and callback is invoked");
newId.GenerateRandom();
VerifyOrQuit(newId != id);
txtDataTester.mCallbackInvoked = false;
callbackInvoked = false;
SuccessOrQuit(node0.Get<BorderAgent>().SetId(newId));
nexus.AdvanceTime(1);
VerifyOrQuit(txtDataTester.mCallbackInvoked);
VerifyOrQuit(txtDataTester.FindTxtEntry("id", txtEntry));
VerifyOrQuit(CheckObjectSameAsTxtEntryData(txtEntry, newId));
ReadAndValidateMeshCoPTxtData(node0);
// Validate that setting the ID to the same value as before is
// correctly detected and does not trigger the callback.
txtDataTester.mCallbackInvoked = false;
callbackInvoked = false;
SuccessOrQuit(node0.Get<BorderAgent>().SetId(newId));
nexus.AdvanceTime(1);
VerifyOrQuit(!txtDataTester.mCallbackInvoked);
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
VerifyOrQuit(!callbackInvoked);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Disable EphemeralKeyManager and validate that TXT data state bitmap indicates this");
txtDataTester.mCallbackInvoked = false;
callbackInvoked = false;
node0.Get<EphemeralKeyManager>().SetEnabled(false);
VerifyOrQuit(node0.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateDisabled);
nexus.AdvanceTime(1);
VerifyOrQuit(txtDataTester.mCallbackInvoked);
VerifyOrQuit(txtDataTester.FindTxtEntry("sb", txtEntry));
VerifyOrQuit(txtEntry.mValueLength == sizeof(uint32_t));
stateBitmap = BigEndian::ReadUint32(txtEntry.mValue);
VerifyOrQuit((stateBitmap & kMaskConnectionMode) == kConnectionModePskc);
VerifyOrQuit((stateBitmap & kMaskThreadIfStatus) == kThreadIfStatusActive);
VerifyOrQuit((stateBitmap & kMaskThreadRole) == kThreadRoleLeader);
VerifyOrQuit(!(stateBitmap & kFlagEpskcSupported));
VerifyOrQuit(callbackInvoked);
ReadAndValidateMeshCoPTxtData(node0);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Disable the MLE operation and validate the TXT data state bitmap");
txtDataTester.mCallbackInvoked = false;
callbackInvoked = false;
SuccessOrQuit(node0.Get<Mle::Mle>().Disable());
nexus.AdvanceTime(1);
VerifyOrQuit(txtDataTester.mCallbackInvoked);
VerifyOrQuit(txtDataTester.FindTxtEntry("sb", txtEntry));
VerifyOrQuit(txtEntry.mValueLength == sizeof(uint32_t));
stateBitmap = BigEndian::ReadUint32(txtEntry.mValue);
VerifyOrQuit((stateBitmap & kMaskConnectionMode) == kConnectionModeDisabled);
VerifyOrQuit((stateBitmap & kMaskThreadIfStatus) == kThreadIfStatusNotInitialized);
VerifyOrQuit((stateBitmap & kMaskThreadRole) == kThreadRoleDisabledOrDetached);
VerifyOrQuit(!(stateBitmap & kFlagEpskcSupported));
VerifyOrQuit(callbackInvoked);
ReadAndValidateMeshCoPTxtData(node0);
}
//----------------------------------------------------------------------------------------------------------------------
void ValidateRegisteredServiceData(Dns::Multicast::Core::Service &aService, Node &aNode)
{
TxtData txtData;
txtData.Init(aService.mTxtData, aService.mTxtDataLength);
ValidateMeshCoPTxtData(txtData, aNode);
}
void TestBorderAgentServiceRegisteration(void)
{
static const char kDefaultServiceBaseName[] = OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME;
static const char kEphemeralKey[] = "nexus1234";
static const char kDefaultServiceBaseName[] = OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME;
static const char kEphemeralKey[] = "nexus1234";
static const uint8_t kVendorTxtData[] = {8, 'v', 'n', '=', 'n', 'e', 'x', 'u', 's'};
static constexpr uint32_t kUdpPort = 49155;
static constexpr uint32_t kInfraIfIndex = 1;
@@ -1003,6 +1079,7 @@ void TestBorderAgentServiceRegisteration(void)
Dns::Multicast::Core::Iterator *iterator;
Dns::Multicast::Core::Service service;
Dns::Multicast::Core::EntryState entryState;
uint16_t txtDataLengthWithNoVendorData;
Log("------------------------------------------------------------------------------------------------------");
Log("TestBorderAgentServiceRegisteration");
@@ -1044,10 +1121,10 @@ void TestBorderAgentServiceRegisteration(void)
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent>().GetUdpPort());
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
VerifyOrQuit(service.mTxtDataLength > 1);
VerifyOrQuit(service.mTtl > 0);
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
ValidateRegisteredServiceData(service, node0);
// Check that there is no more registered mDNS service
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
@@ -1093,8 +1170,8 @@ void TestBorderAgentServiceRegisteration(void)
if (StringMatch(service.mServiceType, "_meshcop._udp"))
{
VerifyOrQuit(service.mTxtDataLength > 1);
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent>().GetUdpPort());
ValidateRegisteredServiceData(service, node0);
}
else if (StringMatch(service.mServiceType, "_meshcop-e._udp"))
{
@@ -1132,11 +1209,11 @@ void TestBorderAgentServiceRegisteration(void)
VerifyOrQuit(StringStartsWith(service.mServiceInstance, kDefaultServiceBaseName));
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
VerifyOrQuit(service.mTxtDataLength > 1);
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent>().GetUdpPort());
VerifyOrQuit(service.mTtl > 0);
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
ValidateRegisteredServiceData(service, node0);
// Check that there is no more registered mDNS service
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
@@ -1164,11 +1241,11 @@ void TestBorderAgentServiceRegisteration(void)
VerifyOrQuit(StringStartsWith(service.mServiceInstance, "OpenThreadAgent"));
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
VerifyOrQuit(service.mTxtDataLength > 1);
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent>().GetUdpPort());
VerifyOrQuit(service.mTtl > 0);
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
ValidateRegisteredServiceData(service, node0);
// Check that there is no more registered mDNS service
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
@@ -1212,11 +1289,82 @@ void TestBorderAgentServiceRegisteration(void)
VerifyOrQuit(StringStartsWith(service.mServiceInstance, "OpenThreadAgent"));
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
VerifyOrQuit(service.mTxtDataLength > 1);
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent>().GetUdpPort());
VerifyOrQuit(service.mTtl > 0);
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
ValidateRegisteredServiceData(service, node0);
txtDataLengthWithNoVendorData = service.mTxtDataLength;
// Check that there is no more registered mDNS service
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Set vendor TXT data and validate that it is included in the registered mDNS service");
node0.Get<BorderAgent>().SetVendorTxtData(kVendorTxtData, sizeof(kVendorTxtData));
nexus.AdvanceTime(5 * Time::kOneSecondInMsec);
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
VerifyOrQuit(iterator != nullptr);
SuccessOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState));
Log(" HostName: %s", service.mHostName);
Log(" ServiceInstance: %s", service.mServiceInstance);
Log(" ServiceType: %s", service.mServiceType);
Log(" Port: %u", service.mPort);
Log(" TTL: %lu", ToUlong(service.mTtl));
VerifyOrQuit(StringMatch(service.mServiceType, "_meshcop._udp"));
VerifyOrQuit(StringStartsWith(service.mServiceInstance, "OpenThreadAgent"));
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent>().GetUdpPort());
VerifyOrQuit(service.mTtl > 0);
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
ValidateRegisteredServiceData(service, node0);
// Check that vendor TXT data is included at the end of
// the registered service TXT data.
VerifyOrQuit(service.mTxtDataLength > txtDataLengthWithNoVendorData);
VerifyOrQuit(service.mTxtDataLength > sizeof(kVendorTxtData));
VerifyOrQuit(!memcmp(&service.mTxtData[service.mTxtDataLength - sizeof(kVendorTxtData)], kVendorTxtData,
sizeof(kVendorTxtData)));
// Check that there is no more registered mDNS service
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Log("Clear vendor TXT data and validate that the registered mDNS service is updated accordingly");
node0.Get<BorderAgent>().SetVendorTxtData(nullptr, 0);
nexus.AdvanceTime(5 * Time::kOneSecondInMsec);
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
VerifyOrQuit(iterator != nullptr);
SuccessOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState));
Log(" HostName: %s", service.mHostName);
Log(" ServiceInstance: %s", service.mServiceInstance);
Log(" ServiceType: %s", service.mServiceType);
Log(" Port: %u", service.mPort);
Log(" TTL: %lu", ToUlong(service.mTtl));
VerifyOrQuit(StringMatch(service.mServiceType, "_meshcop._udp"));
VerifyOrQuit(StringStartsWith(service.mServiceInstance, "OpenThreadAgent"));
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent>().GetUdpPort());
VerifyOrQuit(service.mTtl > 0);
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
ValidateRegisteredServiceData(service, node0);
VerifyOrQuit(service.mTxtDataLength == txtDataLengthWithNoVendorData);
// Check that there is no more registered mDNS service
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNotFound);