mirror of
https://github.com/espressif/openthread.git
synced 2026-06-05 21:14:49 +00:00
[border-agent] handle mDNS service name conflict by renaming (#12790)
This commit adds support for handling mDNS service name conflicts by automatically renaming the service when a collision is detected during registration. The new naming scheme appends a suffix based on the last two bytes of the device's Extended Address (e.g., " #AB1E"). If this name also conflicts, an additional index is appended (e.g., " #AB1E (1)"). Changes: - Added `mServiceRenameIndex` to `Manager` and `EphemeralKeyManager` to track re-naming attempts. - Updated `otBorderAgentSetMeshCoPServiceBaseName()` and CLI documentation to reflect the new naming and conflict resolution logic. - Updated `OT_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME_MAX_LENGTH` to ensure the full name fits within the 63-character DNS label limit. - Added Nexus tests to verify the renaming logic under conflict.
This commit is contained in:
committed by
GitHub
parent
68ddd042ab
commit
1e79496c57
@@ -258,10 +258,11 @@ otError otBorderAgentGetMeshCoPServiceTxtData(otInstance *aInstance, otBorderAge
|
||||
/**
|
||||
* Maximum string length of base name used in `otBorderAgentSetMeshCoPServiceBaseName()`.
|
||||
*
|
||||
* The full DNS label is constructed by appending the Extended Address of the device (as 16-character hex digits) to
|
||||
* To ensure name uniqueness and handle potential name conflicts, the OpenThread Border Agent module appends a
|
||||
* suffix (e.g., " #XXXX" where "XXXX" represents the last two bytes of the device's Extended Address in hex) to
|
||||
* the given base name.
|
||||
*/
|
||||
#define OT_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME_MAX_LENGTH (OT_DNS_MAX_LABEL_SIZE - 17)
|
||||
#define OT_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME_MAX_LENGTH (OT_DNS_MAX_LABEL_SIZE - 13)
|
||||
|
||||
/**
|
||||
* Sets the base name to construct the service instance name used when advertising the mDNS `_meshcop._udp` service by
|
||||
@@ -276,8 +277,9 @@ otError otBorderAgentGetMeshCoPServiceTxtData(otInstance *aInstance, otBorderAge
|
||||
* Per the Thread specification, the service instance should be a user-friendly name identifying the device model or
|
||||
* product. A recommended format is "VendorName ProductName".
|
||||
*
|
||||
* To construct the full name and ensure name uniqueness, the OpenThread Border Agent module will append the Extended
|
||||
* Address of the device (as 16-character hex digits) to the given base name.
|
||||
* To construct the full name and ensure name uniqueness, the OpenThread Border Agent module appends a suffix
|
||||
* (e.g., " #XXXX" where "XXXX" represents the last two bytes of the device's Extended Address in hex) to the given
|
||||
* base name. If a name conflict is detected on the network, an additional index may be appended (e.g., " #XXXX (1)").
|
||||
*
|
||||
* Note that the same name will be used for the ephemeral key service `_meshcop-e._udp` when the ephemeral key feature
|
||||
* is enabled and used.
|
||||
|
||||
@@ -52,7 +52,7 @@ extern "C" {
|
||||
*
|
||||
* @note This number versions both OpenThread platform and user APIs.
|
||||
*/
|
||||
#define OPENTHREAD_API_VERSION (588)
|
||||
#define OPENTHREAD_API_VERSION (589)
|
||||
|
||||
/**
|
||||
* @addtogroup api-instance
|
||||
|
||||
+1
-1
@@ -421,7 +421,7 @@ Requires the `OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE` feature.
|
||||
|
||||
The name can also be configured using the `OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME` configuration option (which is the recommended way to specify this name). This CLI command (and its corresponding API) is provided for projects where the name needs to be set after device initialization and at run-time.
|
||||
|
||||
Per the Thread specification, the service instance should be a user-friendly name identifying the device model or product. A recommended format is "VendorName ProductName". To construct the full name and ensure name uniqueness, the OpenThread Border Agent module will append the Extended Address of the device (as 16-character hex digits) to the given base name. Note that the same name will be used for the ephemeral key service `_meshcop-e._udp` when the ephemeral key feature is enabled and used.
|
||||
Per the Thread specification, the service instance should be a user-friendly name identifying the device model or product. A recommended format is "VendorName ProductName". To construct the full name and ensure name uniqueness, the OpenThread Border Agent module appends a suffix (e.g., " #XXXX" where "XXXX" represents the last two bytes of the device's Extended Address in hex) to the given base name. If a name conflict is detected on the network, an additional index may be appended (e.g., " #XXXX (1)"). Note that the same name will be used for the ephemeral key service `_meshcop-e._udp` when the ephemeral key feature is enabled and used.
|
||||
|
||||
```bash
|
||||
ba servicebasename OpenThreadBorderAgent
|
||||
|
||||
@@ -357,6 +357,16 @@ StringWriter &StringWriter::AppendHexBytes(const uint8_t *aBytes, uint16_t aLeng
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringWriter &StringWriter::AppendHexBytesUppercase(const uint8_t *aBytes, uint16_t aLength)
|
||||
{
|
||||
while (aLength--)
|
||||
{
|
||||
Append("%02X", *aBytes++);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
StringWriter &StringWriter::AppendCharMultipleTimes(char aChar, uint16_t aCount)
|
||||
{
|
||||
while (aCount--)
|
||||
|
||||
@@ -170,6 +170,8 @@ bool StringMatch(const char *aFirstString, const char *aSecondString, StringMatc
|
||||
/**
|
||||
* Copies a string into a given target buffer with a given size if it fits.
|
||||
*
|
||||
* Guarantees that @p aTargetBuffer remains unmodified if an error is returned.
|
||||
*
|
||||
* @param[out] aTargetBuffer A pointer to the target buffer to copy into.
|
||||
* @param[out] aTargetSize The size (number of characters) in @p aTargetBuffer array.
|
||||
* @param[in] aSource A pointer to null-terminated string to copy from. Can be `nullptr` which treated as "".
|
||||
@@ -179,13 +181,15 @@ bool StringMatch(const char *aFirstString, const char *aSecondString, StringMatc
|
||||
* @retval kErrorInvalidArgs The @p aSource does not fit in the given buffer.
|
||||
* @retval kErrorParse The @p aSource does not follow the encoding format specified by @p aEncodingCheck.
|
||||
*/
|
||||
Error StringCopy(char *TargetBuffer, uint16_t aTargetSize, const char *aSource, StringEncodingCheck aEncodingCheck);
|
||||
Error StringCopy(char *aTargetBuffer, uint16_t aTargetSize, const char *aSource, StringEncodingCheck aEncodingCheck);
|
||||
|
||||
/**
|
||||
* Copies a string into a given target buffer with a given size if it fits.
|
||||
*
|
||||
* @tparam kSize The size of buffer.
|
||||
*
|
||||
* Guarantees that @p aTargetBuffer remains unmodified if an error is returned.
|
||||
*
|
||||
* @param[out] aTargetBuffer A reference to the target buffer array to copy into.
|
||||
* @param[in] aSource A pointer to null-terminated string to copy from. Can be `nullptr` which treated as "".
|
||||
* @param[in] aEncodingCheck Specifies the encoding format check (e.g., UTF-8) to perform.
|
||||
@@ -483,15 +487,25 @@ public:
|
||||
StringWriter &AppendVarArgs(const char *aFormat, va_list aArgs) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 0);
|
||||
|
||||
/**
|
||||
* Appends an array of bytes in hex representation (using "%02x" style) to the buffer.
|
||||
* Appends an array of bytes in hex representation (using lowercase "%02x" style) to the buffer.
|
||||
*
|
||||
* @param[in] aBytes A pointer to buffer containing the bytes to append.
|
||||
* @param[in] aLength The length of @p aBytes buffer (in bytes).
|
||||
*
|
||||
* @returns The string writer.
|
||||
* @returns A reference to this string writer.
|
||||
*/
|
||||
StringWriter &AppendHexBytes(const uint8_t *aBytes, uint16_t aLength);
|
||||
|
||||
/**
|
||||
* Appends an array of bytes in hex representation (using uppercase "%02X" style) to the buffer.
|
||||
*
|
||||
* @param[in] aBytes A pointer to buffer containing the bytes to append.
|
||||
* @param[in] aLength The length of @p aBytes buffer (in bytes).
|
||||
*
|
||||
* @returns A reference to this string writer.
|
||||
*/
|
||||
StringWriter &AppendHexBytesUppercase(const uint8_t *aBytes, uint16_t aLength);
|
||||
|
||||
/**
|
||||
* Appends a given character a given number of times.
|
||||
*
|
||||
|
||||
@@ -116,10 +116,10 @@
|
||||
* Per the Thread specification, the service instance should be a user-friendly name identifying the device model or
|
||||
* product. A recommended format is "VendorName ProductName".
|
||||
*
|
||||
* The name MUST have a length less than or equal to `OT_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME_MAX_LENGTH` (47 chars).
|
||||
* The name MUST have a length less than or equal to `OT_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME_MAX_LENGTH`.
|
||||
*/
|
||||
#ifndef OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME
|
||||
#define OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME "OpenThread BR (unspecified vendor) "
|
||||
#define OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME "OpenThread BR (unspecified vendor)"
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
||||
@@ -70,6 +70,9 @@ Manager::Manager(Instance &aInstance)
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE
|
||||
, mIdInitialized(false)
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE
|
||||
, mServiceRenameIndex(0)
|
||||
#endif
|
||||
{
|
||||
mCommissionerAloc.InitAsThreadOriginMeshLocal();
|
||||
|
||||
@@ -80,6 +83,8 @@ Manager::Manager(Instance &aInstance)
|
||||
|
||||
static_assert(sizeof(kDefaultBaseServiceName) - 1 <= kBaseServiceNameMaxLen,
|
||||
"OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_BASE_NAME is too long");
|
||||
|
||||
SuccessOrAssert(StringCopy(mBaseServiceName, kDefaultBaseServiceName));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -394,18 +399,15 @@ exit:
|
||||
|
||||
Error Manager::SetServiceBaseName(const char *aBaseName)
|
||||
{
|
||||
Error error = kErrorNone;
|
||||
Dns::Name::LabelBuffer newName;
|
||||
Error error = kErrorNone;
|
||||
|
||||
VerifyOrExit(StringLength(aBaseName, kBaseServiceNameMaxLen + 1) <= kBaseServiceNameMaxLen,
|
||||
error = kErrorInvalidArgs);
|
||||
VerifyOrExit(!StringMatch(mBaseServiceName, aBaseName));
|
||||
|
||||
ConstructServiceName(aBaseName, newName);
|
||||
|
||||
VerifyOrExit(!StringMatch(newName, mServiceName));
|
||||
SuccessOrExit(error = StringCopy(mBaseServiceName, aBaseName));
|
||||
mServiceRenameIndex = 0;
|
||||
|
||||
UnregisterService();
|
||||
IgnoreError(StringCopy(mServiceName, newName));
|
||||
ConstructServiceName();
|
||||
RegisterService();
|
||||
|
||||
exit:
|
||||
@@ -416,17 +418,28 @@ const char *Manager::GetServiceName(void)
|
||||
{
|
||||
if (IsServiceNameEmpty())
|
||||
{
|
||||
ConstructServiceName(kDefaultBaseServiceName, mServiceName);
|
||||
ConstructServiceName();
|
||||
}
|
||||
|
||||
return mServiceName;
|
||||
}
|
||||
|
||||
void Manager::ConstructServiceName(const char *aBaseName, Dns::Name::LabelBuffer &aNameBuffer)
|
||||
{
|
||||
StringWriter writer(aNameBuffer, sizeof(Dns::Name::LabelBuffer));
|
||||
void Manager::ConstructServiceName(void) { ConstructServiceName(mServiceRenameIndex, mServiceName); }
|
||||
|
||||
writer.Append("%.*s%s", kBaseServiceNameMaxLen, aBaseName, Get<Mac::Mac>().GetExtAddress().ToString().AsCString());
|
||||
void Manager::ConstructServiceName(uint16_t aRenameIndex, Dns::Name::LabelBuffer &aNameBuffer)
|
||||
{
|
||||
static constexpr uint8_t kExtAddrSuffixLength = 2;
|
||||
|
||||
StringWriter writer(aNameBuffer, sizeof(Dns::Name::LabelBuffer));
|
||||
const Mac::ExtAddress &extAddr = Get<Mac::Mac>().GetExtAddress();
|
||||
|
||||
writer.Append("%.*s #", kBaseServiceNameMaxLen, mBaseServiceName);
|
||||
writer.AppendHexBytesUppercase(GetArrayEnd(extAddr.m8) - kExtAddrSuffixLength, kExtAddrSuffixLength);
|
||||
|
||||
if (aRenameIndex != 0)
|
||||
{
|
||||
writer.Append(" (%u)", aRenameIndex % 1000);
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::RegisterService(void)
|
||||
@@ -454,7 +467,8 @@ void Manager::RegisterService(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
Get<Dnssd>().RegisterService(service, /* aRequestId */ 0, /* aCallback */ nullptr);
|
||||
LogInfo("Registering service %s %s", service.mServiceInstance, kServiceType);
|
||||
Get<Dnssd>().RegisterService(service, /* aRequestId */ 0, HandleRegisterDone);
|
||||
|
||||
exit:
|
||||
return;
|
||||
@@ -471,12 +485,37 @@ void Manager::UnregisterService(void)
|
||||
service.mServiceInstance = GetServiceName();
|
||||
service.mServiceType = kServiceType;
|
||||
|
||||
LogInfo("Unregistering service %s %s", service.mServiceInstance, kServiceType);
|
||||
|
||||
Get<Dnssd>().UnregisterService(service, /* aRequestId */ 0, /* aCallback */ nullptr);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void Manager::HandleRegisterDone(otInstance *aInstance, otPlatDnssdRequestId aRequestId, otError aError)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aRequestId);
|
||||
AsCoreType(aInstance).Get<Manager>().HandleRegisterDone(aError);
|
||||
}
|
||||
|
||||
void Manager::HandleRegisterDone(Error aError)
|
||||
{
|
||||
VerifyOrExit(aError == kErrorDuplicated);
|
||||
|
||||
LogInfoOnError(aError, "register service %s %s - retrying with a new name", GetServiceName(), kServiceType);
|
||||
|
||||
UnregisterService();
|
||||
|
||||
mServiceRenameIndex++;
|
||||
ConstructServiceName();
|
||||
|
||||
RegisterService();
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_COMMISSIONER_EVICTION_API_ENABLE
|
||||
|
||||
@@ -408,9 +408,13 @@ private:
|
||||
|
||||
const char *GetServiceName(void);
|
||||
bool IsServiceNameEmpty(void) const { return mServiceName[0] == kNullChar; }
|
||||
void ConstructServiceName(const char *aBaseName, Dns::Name::LabelBuffer &aNameBuffer);
|
||||
void ConstructServiceName(void);
|
||||
void ConstructServiceName(uint16_t aRenameIndex, Dns::Name::LabelBuffer &aNameBuffer);
|
||||
void RegisterService(void);
|
||||
void UnregisterService(void);
|
||||
void HandleRegisterDone(Error aError);
|
||||
|
||||
static void HandleRegisterDone(otInstance *aInstance, otPlatDnssdRequestId aRequestId, otError aError);
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE
|
||||
@@ -435,7 +439,10 @@ private:
|
||||
bool mIdInitialized;
|
||||
#endif
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE
|
||||
|
||||
char mBaseServiceName[kBaseServiceNameMaxLen + 1];
|
||||
Dns::Name::LabelBuffer mServiceName;
|
||||
uint16_t mServiceRenameIndex;
|
||||
#endif
|
||||
Counters mCounters;
|
||||
};
|
||||
|
||||
@@ -59,7 +59,13 @@ EphemeralKeyManager::EphemeralKeyManager(Instance &aInstance)
|
||||
, mCoapDtlsSession(nullptr)
|
||||
, mTimer(aInstance)
|
||||
, mCallbackTask(aInstance)
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE
|
||||
, mServiceRenameIndex(0)
|
||||
#endif
|
||||
{
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE
|
||||
ClearAllBytes(mServiceName);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EphemeralKeyManager::SetEnabled(bool aEnabled)
|
||||
@@ -215,7 +221,8 @@ void EphemeralKeyManager::UpdateCountersAndRecordEvent(DeactivationReason aReaso
|
||||
void EphemeralKeyManager::SetState(State aState)
|
||||
{
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE
|
||||
bool isServiceRegistered = ShouldRegisterService();
|
||||
bool wasServiceRegistered = ShouldRegisterService();
|
||||
bool shouldRegister;
|
||||
#endif
|
||||
|
||||
VerifyOrExit(mState != aState);
|
||||
@@ -224,8 +231,22 @@ void EphemeralKeyManager::SetState(State aState)
|
||||
mCallbackTask.Post();
|
||||
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE
|
||||
VerifyOrExit(isServiceRegistered != ShouldRegisterService());
|
||||
RegisterOrUnregisterService();
|
||||
|
||||
shouldRegister = ShouldRegisterService();
|
||||
VerifyOrExit(wasServiceRegistered != shouldRegister);
|
||||
|
||||
if (shouldRegister)
|
||||
{
|
||||
// We start with the same service name as used by the `Manager`.
|
||||
// We keep track of it separately, so it can be renamed on its
|
||||
// own.
|
||||
|
||||
mServiceRenameIndex = Get<Manager>().mServiceRenameIndex;
|
||||
Get<Manager>().ConstructServiceName(mServiceRenameIndex, mServiceName);
|
||||
}
|
||||
|
||||
RegisterOrUnregisterService(shouldRegister);
|
||||
|
||||
#endif
|
||||
|
||||
exit:
|
||||
@@ -349,20 +370,27 @@ bool EphemeralKeyManager::ShouldRegisterService(void) const
|
||||
return shouldRegister;
|
||||
}
|
||||
|
||||
void EphemeralKeyManager::RegisterOrUnregisterService(void)
|
||||
void EphemeralKeyManager::ConstructServiceName(void)
|
||||
{
|
||||
Get<Manager>().ConstructServiceName(mServiceRenameIndex, mServiceName);
|
||||
}
|
||||
|
||||
void EphemeralKeyManager::RegisterOrUnregisterService(bool aRegister)
|
||||
{
|
||||
Dnssd::Service service;
|
||||
|
||||
VerifyOrExit(Get<Dnssd>().IsReady());
|
||||
|
||||
service.Clear();
|
||||
service.mServiceInstance = Get<Manager>().GetServiceName();
|
||||
service.mServiceInstance = mServiceName;
|
||||
service.mServiceType = kServiceType;
|
||||
service.mPort = GetUdpPort();
|
||||
|
||||
if (ShouldRegisterService())
|
||||
LogInfo("%segistering service %s %s", aRegister ? "R" : "Unr", mServiceName, kServiceType);
|
||||
|
||||
if (aRegister)
|
||||
{
|
||||
Get<Dnssd>().RegisterService(service, /* aRequestId */ 0, /* aCallback */ nullptr);
|
||||
Get<Dnssd>().RegisterService(service, /* aRequestId */ 0, HandleRegisterDone);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -373,6 +401,29 @@ exit:
|
||||
return;
|
||||
}
|
||||
|
||||
void EphemeralKeyManager::HandleRegisterDone(otInstance *aInstance, otPlatDnssdRequestId aRequestId, otError aError)
|
||||
{
|
||||
OT_UNUSED_VARIABLE(aRequestId);
|
||||
AsCoreType(aInstance).Get<EphemeralKeyManager>().HandleRegisterDone(aError);
|
||||
}
|
||||
|
||||
void EphemeralKeyManager::HandleRegisterDone(Error aError)
|
||||
{
|
||||
VerifyOrExit(aError == kErrorDuplicated);
|
||||
|
||||
LogInfoOnError(aError, "register service %s %s - retrying with a new name", mServiceName, kServiceType);
|
||||
|
||||
RegisterOrUnregisterService(/* aRegister */ false);
|
||||
|
||||
mServiceRenameIndex++;
|
||||
ConstructServiceName();
|
||||
|
||||
RegisterOrUnregisterService(/* aRegister */ true);
|
||||
|
||||
exit:
|
||||
return;
|
||||
}
|
||||
|
||||
#endif // OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE
|
||||
|
||||
const char *EphemeralKeyManager::StateToString(State aState)
|
||||
|
||||
@@ -229,7 +229,11 @@ private:
|
||||
void UpdateCountersAndRecordEvent(DeactivationReason aReason);
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE
|
||||
bool ShouldRegisterService(void) const;
|
||||
void RegisterOrUnregisterService(void);
|
||||
void ConstructServiceName(void);
|
||||
void RegisterOrUnregisterService(bool aRegister);
|
||||
void HandleRegisterDone(Error aError);
|
||||
|
||||
static void HandleRegisterDone(otInstance *aInstance, otPlatDnssdRequestId aRequestId, otError aError);
|
||||
#endif
|
||||
|
||||
// Session or Transport callbacks
|
||||
@@ -257,6 +261,10 @@ private:
|
||||
TimeoutTimer mTimer;
|
||||
CallbackTask mCallbackTask;
|
||||
Callback<CallbackHandler> mCallback;
|
||||
#if OPENTHREAD_CONFIG_BORDER_AGENT_MESHCOP_SERVICE_ENABLE
|
||||
Dns::Name::LabelBuffer mServiceName;
|
||||
uint16_t mServiceRenameIndex;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace BorderAgent
|
||||
|
||||
@@ -2350,6 +2350,222 @@ void TestBorderAgentServiceRegistration(void)
|
||||
node0.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
||||
}
|
||||
|
||||
void TestBorderAgentServiceRegistrationRename(void)
|
||||
{
|
||||
static const char kServiceBaseName[] = "VeryLongServiceBaseNameForTestingPurposeOfLimitsMax";
|
||||
static const char kEphemeralKey[] = "nexus1234";
|
||||
|
||||
static constexpr uint32_t kUdpPort = 49155;
|
||||
|
||||
Core nexus;
|
||||
Node &node0 = nexus.CreateNode();
|
||||
Node &node1 = nexus.CreateNode();
|
||||
Mac::ExtAddress extAddress;
|
||||
Dns::Multicast::Core::Iterator *iterator;
|
||||
Dns::Multicast::Core::Service service;
|
||||
Dns::Multicast::Core::EntryState entryState;
|
||||
String<Dns::Name::kMaxLabelSize> expectedName;
|
||||
bool foundService;
|
||||
bool foundEpskService;
|
||||
|
||||
Log("------------------------------------------------------------------------------------------------------");
|
||||
Log("TestBorderAgentServiceRegistrationRename");
|
||||
|
||||
nexus.AdvanceTime(0);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Set the same Ext address on both node0 and node1
|
||||
|
||||
extAddress.GenerateRandom();
|
||||
extAddress.m8[6] = 0xbe;
|
||||
extAddress.m8[7] = 0xef;
|
||||
|
||||
node0.Get<Mac::Mac>().SetExtAddress(extAddress);
|
||||
node1.Get<Mac::Mac>().SetExtAddress(extAddress);
|
||||
VerifyOrQuit(node1.Get<Mac::Mac>().GetExtAddress() == node0.Get<Mac::Mac>().GetExtAddress());
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Set the Service Base Name on both node0 and node1
|
||||
SuccessOrQuit(node0.Get<MeshCoP::BorderAgent::Manager>().SetServiceBaseName(kServiceBaseName));
|
||||
SuccessOrQuit(node1.Get<MeshCoP::BorderAgent::Manager>().SetServiceBaseName(kServiceBaseName));
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Disable Border Agent function on node1
|
||||
node1.Get<MeshCoP::BorderAgent::Manager>().SetEnabled(false);
|
||||
|
||||
node0.Form();
|
||||
node1.Form();
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Construct the expected service instance name label.
|
||||
|
||||
expectedName.Append("%s #%02X%02X", kServiceBaseName, extAddress.m8[6], extAddress.m8[7]);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
// Enable mDNS
|
||||
SuccessOrQuit(node0.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
|
||||
VerifyOrQuit(node0.Get<Dns::Multicast::Core>().IsEnabled());
|
||||
SuccessOrQuit(node1.Get<Dns::Multicast::Core>().SetEnabled(true, kInfraIfIndex));
|
||||
VerifyOrQuit(node1.Get<Dns::Multicast::Core>().IsEnabled());
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
nexus.AdvanceTime(50 * Time::kOneSecondInMsec);
|
||||
VerifyOrQuit(node0.Get<Mle::Mle>().IsLeader());
|
||||
VerifyOrQuit(node1.Get<Mle::Mle>().IsLeader());
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
VerifyOrQuit(node0.Get<MeshCoP::BorderAgent::Manager>().IsEnabled());
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Validate the registered mDNS MeshCop service by Border Agent");
|
||||
|
||||
iterator = node0.Get<Dns::Multicast::Core>().AllocateIterator();
|
||||
VerifyOrQuit(iterator != nullptr);
|
||||
|
||||
foundService = false;
|
||||
|
||||
while (node0.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
|
||||
{
|
||||
Log("- - - - - - - - - - - - - - - - -");
|
||||
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));
|
||||
|
||||
if (StringMatch(service.mServiceType, "_meshcop._udp"))
|
||||
{
|
||||
VerifyOrQuit(StringMatch(service.mServiceInstance, expectedName.AsCString()));
|
||||
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
|
||||
VerifyOrQuit(service.mPort == node0.Get<MeshCoP::BorderAgent::Manager>().GetUdpPort());
|
||||
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
|
||||
VerifyOrQuit(service.mTtl > 0);
|
||||
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
|
||||
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
|
||||
ValidateRegisteredServiceData(service, node0);
|
||||
foundService = true;
|
||||
}
|
||||
}
|
||||
|
||||
VerifyOrQuit(foundService);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Enable BorderAgent on node1");
|
||||
|
||||
node1.Get<MeshCoP::BorderAgent::Manager>().SetEnabled(true);
|
||||
|
||||
nexus.AdvanceTime(2 * Time::kOneSecondInMsec);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Validate the registered mDNS MeshCop service by Border Agent on node1");
|
||||
|
||||
iterator = node1.Get<Dns::Multicast::Core>().AllocateIterator();
|
||||
VerifyOrQuit(iterator != nullptr);
|
||||
|
||||
foundService = false;
|
||||
|
||||
while (node1.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
|
||||
{
|
||||
Log("- - - - - - - - - - - - - - - - -");
|
||||
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));
|
||||
|
||||
if (StringMatch(service.mServiceType, "_meshcop._udp"))
|
||||
{
|
||||
Log("Validate that node1 properly renamed the conflicted service name");
|
||||
|
||||
VerifyOrQuit(StringStartsWith(service.mServiceInstance, expectedName.AsCString()));
|
||||
expectedName.Append(" (1)");
|
||||
VerifyOrQuit(StringMatch(service.mServiceInstance, expectedName.AsCString()));
|
||||
|
||||
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
|
||||
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
|
||||
VerifyOrQuit(service.mTtl > 0);
|
||||
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
|
||||
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
|
||||
ValidateRegisteredServiceData(service, node1);
|
||||
foundService = true;
|
||||
}
|
||||
}
|
||||
|
||||
VerifyOrQuit(foundService);
|
||||
|
||||
node1.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Enable and start ephemeral key on node 1");
|
||||
|
||||
node1.Get<EphemeralKeyManager>().SetEnabled(true);
|
||||
VerifyOrQuit(node1.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStopped);
|
||||
node1.Get<EphemeralKeyManager>().SetCallback(HandleEphemeralKeyChange, &node1);
|
||||
|
||||
SuccessOrQuit(node1.Get<EphemeralKeyManager>().Start(kEphemeralKey, /* aTimeout */ 0, kUdpPort));
|
||||
|
||||
nexus.AdvanceTime(10 * Time::kOneSecondInMsec);
|
||||
|
||||
VerifyOrQuit(node1.Get<EphemeralKeyManager>().GetState() == EphemeralKeyManager::kStateStarted);
|
||||
VerifyOrQuit(node1.Get<EphemeralKeyManager>().GetUdpPort() == kUdpPort);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Log("Check the registered services - validate the same service name is used for `_meshcop-e` service");
|
||||
|
||||
iterator = node1.Get<Dns::Multicast::Core>().AllocateIterator();
|
||||
VerifyOrQuit(iterator != nullptr);
|
||||
|
||||
foundService = false;
|
||||
foundEpskService = false;
|
||||
|
||||
while (node1.Get<Dns::Multicast::Core>().GetNextService(*iterator, service, entryState) == kErrorNone)
|
||||
{
|
||||
Log("- - - - - - - - - - - - - - - - -");
|
||||
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));
|
||||
|
||||
if (StringMatch(service.mServiceType, "_meshcop._udp"))
|
||||
{
|
||||
VerifyOrQuit(StringMatch(service.mServiceInstance, expectedName.AsCString()));
|
||||
|
||||
VerifyOrQuit(service.mPort == node1.Get<MeshCoP::BorderAgent::Manager>().GetUdpPort());
|
||||
ValidateRegisteredServiceData(service, node1);
|
||||
|
||||
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
|
||||
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
|
||||
VerifyOrQuit(service.mTtl > 0);
|
||||
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
|
||||
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
|
||||
foundService = true;
|
||||
}
|
||||
else if (StringMatch(service.mServiceType, "_meshcop-e._udp"))
|
||||
{
|
||||
VerifyOrQuit(StringMatch(service.mServiceInstance, expectedName.AsCString()));
|
||||
|
||||
VerifyOrQuit(service.mPort == kUdpPort);
|
||||
VerifyOrQuit(service.mTxtDataLength == 1);
|
||||
VerifyOrQuit(service.mTxtData[0] == 0);
|
||||
|
||||
VerifyOrQuit(StringStartsWith(service.mHostName, "ot"));
|
||||
VerifyOrQuit(service.mSubTypeLabelsLength == 0);
|
||||
VerifyOrQuit(service.mTtl > 0);
|
||||
VerifyOrQuit(service.mInfraIfIndex == kInfraIfIndex);
|
||||
VerifyOrQuit(entryState == OT_MDNS_ENTRY_STATE_REGISTERED);
|
||||
foundEpskService = true;
|
||||
}
|
||||
}
|
||||
|
||||
VerifyOrQuit(foundService);
|
||||
VerifyOrQuit(foundEpskService);
|
||||
|
||||
node1.Get<Dns::Multicast::Core>().FreeIterator(*iterator);
|
||||
}
|
||||
|
||||
} // namespace Nexus
|
||||
} // namespace ot
|
||||
|
||||
@@ -2361,6 +2577,7 @@ int main(void)
|
||||
ot::Nexus::TestHistoryTrackerBorderAgentEpskcEvent();
|
||||
ot::Nexus::TestBorderAgentTxtDataCallback();
|
||||
ot::Nexus::TestBorderAgentServiceRegistration();
|
||||
ot::Nexus::TestBorderAgentServiceRegistrationRename();
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user