[mdns] support registering local host and its IPv6/IPv4 addresses (#11353)

This commit enhances the native OpenThread mDNS implementation to
streamline the registration of the local host and its IPv6/IPv4
addresses.

Previously, registering the local host required tracking host
addresses and using `otMdnsRegisterHost()`, similar to registering
any other host. This commit introduces a simpler alternative that
handles both IPv6 and IPv4 addresses.

The changes in this PR include:

- The local host name can be explicitly set by the caller using new
  API `otMdnsSetLocalHostName`. However, if not provided, the mDNS
  module automatically generates a name derived from the device's
  Extended MAC address.
- A new platform API callback, `otPlatMdnsHandleHostAddressEvent`, is
  introduced to notify the OpenThread mDNS module of host address
  changes.
- The OpenThread mDNS maintains an internal list of host addresses,
  automatically updating it based on platform callbacks. A short
  guard time is used to group multiple changes before announcing
  them. Transient changes (e.g., address removal and re-addition) are
  handled to prevent unnecessary announcements.
- Host IPv4 addresses (A records) are now supported. The `HostEntry`
  class is updated to optionally include IPv4 addresses, in addition
  to the required IPv6 addresses.
- A detailed test case in `test_mdns` covers all new local
  host-related behaviors.
This commit is contained in:
Abtin Keshavarzian
2025-03-31 14:05:48 -07:00
committed by GitHub
parent 921a7c542b
commit 793dd9896d
9 changed files with 1118 additions and 76 deletions
+1 -1
View File
@@ -52,7 +52,7 @@ extern "C" {
*
* @note This number versions both OpenThread platform and user APIs.
*/
#define OPENTHREAD_API_VERSION (490)
#define OPENTHREAD_API_VERSION (491)
/**
* @addtogroup api-instance
+23
View File
@@ -203,6 +203,29 @@ bool otMdnsIsQuestionUnicastAllowed(otInstance *aInstance);
*/
void otMdnsSetConflictCallback(otInstance *aInstance, otMdnsConflictCallback aCallback);
/**
* Gets the local host name.
*
* @param[in] aInstance The OpenThread instance.
*
* @returns The local host name.
*/
const char *otMdnsGetLocalHostName(otInstance *aInstance);
/**
* Sets the local host name.
*
* The local host name can be set only when the mDNS module is disabled. If not set the mDNS module itself will
* auto-generate the local host name.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aName The local host name to use, can be to `NULL` to allow the mDNS module to choose the name.
*
* @retval OT_ERROR_NONE The local host name was successfully set.
* @retval OT_ERROR_INVALID_STATE mDNS module is already enabled.
*/
otError otMdnsSetLocalHostName(otInstance *aInstance, const char *aName);
/**
* Registers or updates a host on mDNS.
*
+45
View File
@@ -75,6 +75,10 @@ typedef struct otPlatMdnsAddressInfo
*
* While enabled, all received messages MUST be reported back using `otPlatMdnsHandleReceive()` callback.
*
* When enabled, the platform MUST also monitor and report all IPv4 and IPv6 addresses assigned to the network
* interface using the `otPlatMdnsHandleHostAddressEvent()` callback function. Refer to the documentation of this
* callback for detailed information on the callback's usage and parameters.
*
* @param[in] aInstance The OpernThread instance.
* @param[in] aEnable Indicate whether to enable or disable.
* @param[in] aInfraInfIndex The infrastructure network interface index.
@@ -154,6 +158,47 @@ extern void otPlatMdnsHandleReceive(otInstance *aInstance,
bool aIsUnicast,
const otPlatMdnsAddressInfo *aAddress);
/**
* Callback to notify OpenThread mDNS module of host address changes.
*
* When `otPlatMdnsSetListeningEnabled()` enables mDNS listening on an @p aInfraIfIndex, the platform MUST monitor and
* report ALL IPv4 and IPv6 addresses assigned to this network interface.
*
* When mDNS is enabled:
* - The platform MUST retrieve ALL currently assigned IPv4 and IPv6 addresses on the specified interface.
* - For each retrieved address, the platform MUST call `otPlatMdnsHandleHostAddressEvent()`.
* - The IPv4 addresses are represented using IPv4-mapped IPv6 format.
*
* Ongoing monitoring (while enabled):
* - The platform MUST continuously monitor the specified interface for address changes.
* - If any addresses are added or removed, the platform MUST call this callback for each affected address, indicating
* the change (addition or removal using @p aAdded).
*
* When mDNS is disabled:
* - The platform MUST cease monitoring for address changes on the interface.
* - The platform does NOT need to explicitly signal the removal of addresses upon disable. The OpenThread stack
* automatically clears its internal address list.
* - If address monitoring is re-enabled later, the platform MUST repeat the "enable" steps again, retrieving and
* reporting ALL current addresses.
*
* The OpenThread stack maintains an internal list of host addresses. It updates this list automatically upon receiving
* calls to `otPlatMdnsHandleHostAddressEvent()`.
* - OpenThread's mDNS implementation uses a short guard time (4 msec) before taking action (e.g., announcing new
* addresses). This allows multiple changes to be grouped and announced together.
* - OpenThread's mDNS implementation also handles transient changes, e.g., an address is removed and then quickly
* re-added. It ensures that announcements are only made when there is a change to the list (from what was
* announced before). This simplifies the platform's responsibility as it can simply report all observed changes.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aAddress IP Address. IPv4-mapped IPv6 format is used to represent an IPv4 address.
* @param[in] aAdded Boolean to indicate whether the address added (`TRUE`) or removed (`FALSE`).
* @param[in] aInfraIfIndex The interface index.
*/
extern void otPlatMdnsHandleHostAddressEvent(otInstance *aInstance,
const otIp6Address *aAddress,
bool aAdded,
uint32_t aInfraIfIndex);
/**
* @}
*/
+6
View File
@@ -86,6 +86,11 @@ template <> otError Mdns::Process<Cmd("unicastquestion")>(Arg aArgs[])
return ProcessEnableDisable(aArgs, otMdnsIsQuestionUnicastAllowed, otMdnsSetQuestionUnicastAllowed);
}
template <> otError Mdns::Process<Cmd("localhostname")>(Arg aArgs[])
{
return ProcessGetSet(aArgs, otMdnsGetLocalHostName, otMdnsSetLocalHostName);
}
void Mdns::OutputHost(const otMdnsHost &aHost)
{
OutputLine("Host %s", aHost.mHostName);
@@ -1209,6 +1214,7 @@ otError Mdns::Process(Arg aArgs[])
CmdEntry("ip6resolvers"),
CmdEntry("keys"),
#endif
CmdEntry("localhostname"),
CmdEntry("recordquerier"),
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
CmdEntry("recordqueriers"),
+10
View File
@@ -61,6 +61,16 @@ void otMdnsSetConflictCallback(otInstance *aInstance, otMdnsConflictCallback aCa
AsCoreType(aInstance).Get<Dns::Multicast::Core>().SetConflictCallback(aCallback);
}
const char *otMdnsGetLocalHostName(otInstance *aInstance)
{
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().GetLocalHostName();
}
otError otMdnsSetLocalHostName(otInstance *aInstance, const char *aName)
{
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().SetLocalHostName(aName);
}
otError otMdnsRegisterHost(otInstance *aInstance,
const otMdnsHost *aHost,
otMdnsRequestId aRequestId,
+4
View File
@@ -405,6 +405,10 @@ void Instance::AfterInit(void)
Get<Trel::Link>().AfterInit();
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
Get<Dns::Multicast::Core>().AfterInstanceInit();
#endif
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
#if OPENTHREAD_ENABLE_VENDOR_EXTENSION
+440 -62
View File
@@ -55,6 +55,14 @@ extern "C" void otPlatMdnsHandleReceive(otInstance *aInstance,
AsCoreType(aInstance).Get<Core>().HandleMessage(AsCoreType(aMessage), aIsUnicast, AsCoreType(aAddress));
}
extern "C" void otPlatMdnsHandleHostAddressEvent(otInstance *aInstance,
const otIp6Address *aAddress,
bool aAdded,
uint32_t aInfraIfIndex)
{
AsCoreType(aInstance).Get<Core>().HandleHostAddressEvent(AsCoreType(aAddress), aAdded, aInfraIfIndex);
}
//----------------------------------------------------------------------------------------------------------------------
// Core
@@ -70,6 +78,7 @@ Core::Core(Instance &aInstance)
, mIsQuestionUnicastAllowed(kDefaultQuAllowed)
, mMaxMessageSize(kMaxMessageSize)
, mInfraIfIndex(0)
, mLocalHost(aInstance)
, mMultiPacketRxMessages(aInstance)
, mNextProbeTxTime(TimerMilli::GetNow() - 1)
, mEntryTimer(aInstance)
@@ -82,6 +91,19 @@ Core::Core(Instance &aInstance)
{
}
void Core::AfterInstanceInit(void)
{
// This is called immediately after the OpenThread `Instance` is
// initialized (i.e., after all constructors are called and saved
// information from `Settings` is restored). This call triggers
// the generation of the local host name, which is derived from
// the device's extended MAC address. This ensures that the MAC
// address is restored from the non-volatile settings, and the
// generated name remains consistent across device reboots.
mLocalHost.GenerateName();
}
Error Core::SetEnabled(bool aEnable, uint32_t aInfraIfIndex)
{
Error error = kErrorNone;
@@ -99,10 +121,8 @@ Error Core::SetEnabled(bool aEnable, uint32_t aInfraIfIndex)
else
{
LogInfo("Disabling");
}
if (!mIsEnabled)
{
mLocalHost.ClearAddresses();
mHostEntries.Clear();
mServiceEntries.Clear();
mServiceTypes.Clear();
@@ -258,6 +278,12 @@ void Core::InvokeConflictCallback(const char *aName, const char *aServiceType)
mConflictCallback(&GetInstance(), aName, aServiceType);
}
}
void Core::HandleHostAddressEvent(const Ip6::Address &aAddress, bool aAdded, uint32_t aInfraIfIndex)
{
mLocalHost.HandleAddressEvent(aAddress, aAdded, aInfraIfIndex);
}
void Core::HandleMessage(Message &aMessage, bool aIsUnicast, const AddressInfo &aSenderAddress)
{
OwnedPtr<Message> messagePtr(&aMessage);
@@ -540,6 +566,11 @@ exit:
return matches;
}
bool Core::AddressArray::Matches(const AddressArray &aOther) const
{
return Matches(aOther.AsCArray(), aOther.GetLength());
}
void Core::AddressArray::SetFrom(const Ip6::Address *aAddresses, uint16_t aNumAddresses)
{
Free();
@@ -1198,7 +1229,7 @@ bool Core::Entry::ShouldAnswerNsec(TimeMilli aNow) const
return mMulticastNsecPending && (GetNsecAnswerTime() <= aNow);
}
void Core::Entry::AnswerNonProbe(const AnswerInfo &aInfo, RecordAndType *aRecords, uint16_t aRecordsLength)
void Core::Entry::AnswerNonProbe(const AnswerInfo &aInfo, RecordAndTypeArray &aRecordAndTypes)
{
// Schedule answers for all matching records in `aRecords` array
// to a given non-probe question.
@@ -1206,9 +1237,9 @@ void Core::Entry::AnswerNonProbe(const AnswerInfo &aInfo, RecordAndType *aRecord
bool allEmptyOrZeroTtl = true;
bool answerNsec = true;
for (uint16_t index = 0; index < aRecordsLength; index++)
for (RecordAndType &recordAndType : aRecordAndTypes)
{
RecordInfo &record = aRecords[index].mRecord;
RecordInfo &record = *recordAndType.mRecord;
if (!record.CanAnswer())
{
@@ -1218,7 +1249,7 @@ void Core::Entry::AnswerNonProbe(const AnswerInfo &aInfo, RecordAndType *aRecord
allEmptyOrZeroTtl = false;
if (QuestionMatches(aInfo.mQuestionRrType, aRecords[index].mType))
if (QuestionMatches(aInfo.mQuestionRrType, recordAndType.mType))
{
answerNsec = false;
record.ScheduleAnswer(aInfo);
@@ -1235,7 +1266,7 @@ void Core::Entry::AnswerNonProbe(const AnswerInfo &aInfo, RecordAndType *aRecord
}
}
void Core::Entry::AnswerProbe(const AnswerInfo &aInfo, RecordAndType *aRecords, uint16_t aRecordsLength)
void Core::Entry::AnswerProbe(const AnswerInfo &aInfo, RecordAndTypeArray &aRecordAndTypes)
{
bool allEmptyOrZeroTtl = true;
bool shouldDelay = false;
@@ -1246,9 +1277,9 @@ void Core::Entry::AnswerProbe(const AnswerInfo &aInfo, RecordAndType *aRecords,
OT_ASSERT(info.mIsProbe);
for (uint16_t index = 0; index < aRecordsLength; index++)
for (RecordAndType &recordAndType : aRecordAndTypes)
{
RecordInfo &record = aRecords[index].mRecord;
RecordInfo &record = *recordAndType.mRecord;
TimeMilli lastMulticastTime;
if (!record.CanAnswer())
@@ -1294,9 +1325,9 @@ void Core::Entry::AnswerProbe(const AnswerInfo &aInfo, RecordAndType *aRecords,
info.mAnswerDelay = 0;
}
for (uint16_t index = 0; index < aRecordsLength; index++)
for (RecordAndType &recordAndType : aRecordAndTypes)
{
aRecords[index].mRecord.ScheduleAnswer(info);
recordAndType.mRecord->ScheduleAnswer(info);
}
exit:
@@ -1469,6 +1500,180 @@ exit:
return error;
}
//----------------------------------------------------------------------------------------------------------------------
// Core::Entry::RecordAndTypeArray
void Core::Entry::RecordAndTypeArray::Add(RecordInfo &aRecord, uint16_t aType)
{
RecordAndType *entry = PushBack();
OT_ASSERT(entry != nullptr);
entry->mRecord = &aRecord;
entry->mType = aType;
}
//----------------------------------------------------------------------------------------------------------------------
// Core::LocalHost
Core::LocalHost::LocalHost(Instance &aInstance)
: InstanceLocator(aInstance)
, mEventTimer(aInstance)
{
GenerateName();
}
Error Core::LocalHost::SetName(const char *aName)
{
Error error = kErrorNone;
VerifyOrExit(!Get<Core>().mIsEnabled, error = kErrorInvalidState);
if (aName == nullptr)
{
GenerateName();
}
else
{
SuccessOrAssert(mName.Set(aName));
}
exit:
return error;
}
void Core::LocalHost::GenerateName(void)
{
Name::LabelBuffer name;
StringWriter writer(name, sizeof(name));
writer.Append("ot%s", Get<Mac::Mac>().GetExtAddress().ToString().AsCString());
SuccessOrAssert(mName.Set(name));
}
void Core::LocalHost::ClearAddresses(void)
{
mIp4Addresses.Free();
mIp6Addresses.Free();
mAddrEvents.Clear();
mEventTimer.Stop();
}
void Core::LocalHost::HandleAddressEvent(const Ip6::Address &aAddress, bool aAdded, uint32_t aInfraIfIndex)
{
AddrEvent *addrEvent;
VerifyOrExit(Get<Core>().mIsEnabled);
VerifyOrExit(aInfraIfIndex == Get<Core>().mInfraIfIndex);
LogInfo("Host address %s event: %s", aAddress.ToString().AsCString(), aAdded ? "added" : "removed");
addrEvent = AddrEvent::Allocate(aAddress, aAdded);
OT_ASSERT(addrEvent != nullptr);
// Before we add the new event, we remove any previous events in the
// list that match the same address. This way we always track the
// latest event for each address. This handles the case where
// a "removed" address event is quickly followed by an "added" event
// for the same address.
//
// The events are processed after a short guard delay time
// `kGuardTimeToProcessAddrEvents`. This ensures multiple changes
// to be grouped and announced together.
mAddrEvents.RemoveAndFreeAllMatching(aAddress);
mAddrEvents.Push(*addrEvent);
if (!mEventTimer.IsRunning())
{
mEventTimer.Start(kGuardTimeToProcessAddrEvents);
}
exit:
return;
}
void Core::LocalHost::HandleEventTimer(void)
{
// Process all saved `AddrEvents` and update IPv4 and IPv6
// address lists.
static const AddrType kAddrTypes[] = {kIp4AddrType, kIp6AddrType};
VerifyOrExit(Get<Core>().mIsEnabled);
for (AddrType addrType : kAddrTypes)
{
AddressArray &addresses = (addrType == kIp4AddrType) ? mIp4Addresses : mIp6Addresses;
AddressArray oldAddresses;
oldAddresses.TakeFrom(static_cast<AddressArray &&>(addresses));
addresses.Clear();
// First, add existing addresses (from old list) that did not
// change (there is no "removed" event).
for (const Ip6::Address &address : oldAddresses)
{
const AddrEvent *addrEvent = mAddrEvents.FindMatching(address);
if ((addrEvent == nullptr) || addrEvent->mAdded)
{
SuccessOrAssert(addresses.PushBack(address));
}
}
// Next, add any new addresses for which we got an "added"
// event.
for (const AddrEvent &addrEvent : mAddrEvents)
{
if (!addrEvent.Matches(addrType))
{
continue;
}
if (addrEvent.mAdded && !addresses.Contains(addrEvent.mAddress))
{
SuccessOrAssert(addresses.PushBack(addrEvent.mAddress));
}
}
}
IgnoreError(Get<Core>().Register<HostEntry>(*this, /* aRequestId */ 0, /* aCallback */ nullptr));
exit:
mAddrEvents.Clear();
}
//----------------------------------------------------------------------------------------------------------------------
// Core::LocalHost::AddrEvent
Core::LocalHost::AddrEvent::AddrEvent(const Ip6::Address &aAddress, bool aAdded)
: mNext(nullptr)
, mAddress(aAddress)
, mAdded(aAdded)
{
}
bool Core::LocalHost::AddrEvent::Matches(AddrType aType) const
{
bool matches = false;
bool isIp4 = mAddress.IsIp4Mapped();
switch (aType)
{
case kIp4AddrType:
matches = isIp4;
break;
case kIp6AddrType:
matches = !isIp4;
break;
}
return matches;
}
//----------------------------------------------------------------------------------------------------------------------
// Core::HostEntry
@@ -1492,11 +1697,24 @@ bool Core::HostEntry::Matches(const Name &aName) const
bool Core::HostEntry::Matches(const Host &aHost) const { return NameMatch(mName, aHost.mHostName); }
bool Core::HostEntry::Matches(const LocalHost &aLocalHost) const { return NameMatch(mName, aLocalHost.GetName()); }
bool Core::HostEntry::Matches(const Key &aKey) const { return !IsKeyForService(aKey) && NameMatch(mName, aKey.mName); }
bool Core::HostEntry::Matches(const Heap::String &aName) const { return NameMatch(mName, aName); }
bool Core::HostEntry::IsEmpty(void) const { return !mAddrRecord.IsPresent() && !mKeyRecord.IsPresent(); }
bool Core::HostEntry::IsEmpty(void) const
{
bool isEmpty = false;
VerifyOrExit(!mKeyRecord.IsPresent() && !mIp6AddrRecord.IsPresent());
VerifyOrExit((mIp4AddrRecord == nullptr) || !mIp4AddrRecord->IsPresent());
isEmpty = true;
exit:
return isEmpty;
}
void Core::HostEntry::Register(const Host &aHost, const Callback &aCallback)
{
@@ -1525,8 +1743,8 @@ void Core::HostEntry::Register(const Host &aHost, const Callback &aCallback)
ExitNow();
}
mAddrRecord.UpdateTtl(DetermineTtl(aHost.mTtl, kDefaultTtl));
mAddrRecord.UpdateProperty(mAddresses, AsCoreTypePtr(aHost.mAddresses), aHost.mAddressesLength);
mIp6AddrRecord.UpdateTtl(DetermineTtl(aHost.mTtl, kDefaultTtl));
mIp6AddrRecord.UpdateAddresses(aHost);
DetermineNextFireTime();
ScheduleTimer();
@@ -1535,6 +1753,46 @@ exit:
return;
}
void Core::HostEntry::Register(const LocalHost &aLocalHost, const Callback &aCallback)
{
SetCallback(aCallback);
if (aLocalHost.GetIp6Addresses().IsEmpty())
{
if (mIp6AddrRecord.IsPresent())
{
mIp6AddrRecord.UpdateTtl(0);
}
}
else
{
mIp6AddrRecord.UpdateTtl(kDefaultTtl);
mIp6AddrRecord.UpdateAddresses(aLocalHost.GetIp6Addresses());
}
if (aLocalHost.GetIp4Addresses().IsEmpty())
{
if ((mIp4AddrRecord != nullptr) && mIp4AddrRecord->IsPresent())
{
mIp4AddrRecord->UpdateTtl(0);
}
}
else
{
if (mIp4AddrRecord == nullptr)
{
mIp4AddrRecord.Reset(AddrRecord::Allocate());
OT_ASSERT(mIp4AddrRecord != nullptr);
}
mIp4AddrRecord->UpdateTtl(kDefaultTtl);
mIp4AddrRecord->UpdateAddresses(aLocalHost.GetIp4Addresses());
}
DetermineNextFireTime();
ScheduleTimer();
}
void Core::HostEntry::Register(const Key &aKey, const Callback &aCallback)
{
Entry::Register(aKey, aCallback);
@@ -1547,14 +1805,14 @@ void Core::HostEntry::Unregister(const Host &aHost)
{
OT_UNUSED_VARIABLE(aHost);
VerifyOrExit(mAddrRecord.IsPresent());
VerifyOrExit(mIp6AddrRecord.IsPresent());
ClearCallback();
switch (GetState())
{
case kRegistered:
mAddrRecord.UpdateTtl(0);
mIp6AddrRecord.UpdateTtl(0);
DetermineNextFireTime();
ScheduleTimer();
break;
@@ -1585,8 +1843,12 @@ void Core::HostEntry::Unregister(const Key &aKey)
void Core::HostEntry::ClearHost(void)
{
mAddrRecord.Clear();
mAddresses.Free();
mIp6AddrRecord.Clear();
if (mIp4AddrRecord != nullptr)
{
mIp4AddrRecord->Clear();
}
}
void Core::HostEntry::ScheduleToRemoveIfEmpty(void)
@@ -1612,20 +1874,25 @@ exit:
void Core::HostEntry::AnswerQuestion(const AnswerInfo &aInfo)
{
RecordAndType records[] = {
{mAddrRecord, ResourceRecord::kTypeAaaa},
{mKeyRecord, ResourceRecord::kTypeKey},
};
RecordAndTypeArray recordAndTypes;
VerifyOrExit(GetState() == kRegistered);
recordAndTypes.Add(mIp6AddrRecord, ResourceRecord::kTypeAaaa);
recordAndTypes.Add(mKeyRecord, ResourceRecord::kTypeKey);
if (mIp4AddrRecord != nullptr)
{
recordAndTypes.Add(*mIp4AddrRecord, ResourceRecord::kTypeA);
}
if (aInfo.mIsProbe)
{
AnswerProbe(aInfo, records, GetArrayLength(records));
AnswerProbe(aInfo, recordAndTypes);
}
else
{
AnswerNonProbe(aInfo, records, GetArrayLength(records));
AnswerNonProbe(aInfo, recordAndTypes);
}
DetermineNextFireTime();
@@ -1644,7 +1911,12 @@ void Core::HostEntry::ClearAppendState(void)
Entry::ClearAppendState();
mAddrRecord.MarkAsNotAppended();
mIp6AddrRecord.MarkAsNotAppended();
if (mIp4AddrRecord != nullptr)
{
mIp4AddrRecord->MarkAsNotAppended();
}
mNameOffset = kUnspecifiedOffset;
}
@@ -1660,7 +1932,8 @@ void Core::HostEntry::PrepareProbe(TxMessage &aProbe)
AppendNameTo(aProbe, kQuestionSection);
AppendQuestionTo(aProbe);
AppendAddressRecordsTo(aProbe, kAuthoritySection);
AppendIp6AddressRecordsTo(aProbe, kAuthoritySection);
AppendIp4AddressRecordsTo(aProbe, kAuthoritySection);
AppendKeyRecordTo(aProbe, kAuthoritySection);
aProbe.CheckSizeLimitToPrepareAgain(prepareAgain);
@@ -1670,7 +1943,13 @@ void Core::HostEntry::PrepareProbe(TxMessage &aProbe)
void Core::HostEntry::StartAnnouncing(void)
{
mAddrRecord.StartAnnouncing();
mIp6AddrRecord.StartAnnouncing();
if (mIp4AddrRecord != nullptr)
{
mIp4AddrRecord->StartAnnouncing();
}
mKeyRecord.StartAnnouncing();
}
@@ -1695,9 +1974,15 @@ void Core::HostEntry::PrepareResponseRecords(EntryContext &aContext)
bool appendNsec = false;
TxMessage &response = aContext.mResponseMessage;
if (mAddrRecord.ShouldAppendTo(aContext))
if (mIp6AddrRecord.ShouldAppendTo(aContext))
{
AppendAddressRecordsTo(response, kAnswerSection);
AppendIp6AddressRecordsTo(response, kAnswerSection);
appendNsec = true;
}
if ((mIp4AddrRecord != nullptr) && mIp4AddrRecord->ShouldAppendTo(aContext))
{
AppendIp4AddressRecordsTo(response, kAnswerSection);
appendNsec = true;
}
@@ -1718,7 +2003,12 @@ void Core::HostEntry::UpdateRecordsState(const TxMessage &aResponse)
// Updates state after a response is prepared.
Entry::UpdateRecordsState(aResponse);
mAddrRecord.UpdateStateAfterAnswer(aResponse);
mIp6AddrRecord.UpdateStateAfterAnswer(aResponse);
if (mIp4AddrRecord != nullptr)
{
mIp4AddrRecord->UpdateStateAfterAnswer(aResponse);
}
if (IsEmpty())
{
@@ -1731,7 +2021,12 @@ void Core::HostEntry::DetermineNextFireTime(void)
VerifyOrExit(GetState() == kRegistered);
Entry::DetermineNextFireTime();
mAddrRecord.UpdateFireTimeOn(*this);
mIp6AddrRecord.UpdateFireTimeOn(*this);
if (mIp4AddrRecord != nullptr)
{
mIp4AddrRecord->UpdateFireTimeOn(*this);
}
exit:
return;
@@ -1742,33 +2037,72 @@ void Core::HostEntry::DetermineNextAggrTxTime(NextFireTime &aNextAggrTxTime) con
VerifyOrExit(GetState() == kRegistered);
Entry::DetermineNextAggrTxTime(aNextAggrTxTime);
mAddrRecord.DetermineNextAggrTxTime(aNextAggrTxTime);
mIp6AddrRecord.DetermineNextAggrTxTime(aNextAggrTxTime);
if (mIp4AddrRecord != nullptr)
{
mIp4AddrRecord->DetermineNextAggrTxTime(aNextAggrTxTime);
}
exit:
return;
}
void Core::HostEntry::AppendAddressRecordsTo(TxMessage &aTxMessage, Section aSection)
void Core::HostEntry::AppendIp6AddressRecordsTo(TxMessage &aTxMessage, Section aSection)
{
AppendAddressRecordsTo(aTxMessage, aSection, mIp6AddrRecord, /* aIp6 */ true);
}
void Core::HostEntry::AppendIp4AddressRecordsTo(TxMessage &aTxMessage, Section aSection)
{
if (mIp4AddrRecord != nullptr)
{
AppendAddressRecordsTo(aTxMessage, aSection, *mIp4AddrRecord, /* aIp6 */ false);
}
}
void Core::HostEntry::AppendAddressRecordsTo(TxMessage &aTxMessage,
Section aSection,
AddrRecord &aAddrRecord,
bool aIp6)
{
Message *message;
bool isLegacyUnicast = (aTxMessage.GetType() == TxMessage::kLegacyUnicastResponse);
VerifyOrExit(mAddrRecord.CanAppend());
mAddrRecord.MarkAsAppended(aTxMessage, aSection);
VerifyOrExit(aAddrRecord.CanAppend());
aAddrRecord.MarkAsAppended(aTxMessage, aSection);
message = &aTxMessage.SelectMessageFor(aSection);
for (const Ip6::Address &address : mAddresses)
for (const Ip6::Address &address : aAddrRecord.mAddresses)
{
AaaaRecord aaaaRecord;
aaaaRecord.Init();
aaaaRecord.SetAddress(address);
aaaaRecord.SetTtl(mAddrRecord.GetTtl(isLegacyUnicast));
UpdateCacheFlushFlagIn(aaaaRecord, aSection, isLegacyUnicast);
AppendNameTo(aTxMessage, aSection);
SuccessOrAssert(message->Append(aaaaRecord));
if (aIp6)
{
AaaaRecord aaaaRecord;
aaaaRecord.Init();
aaaaRecord.SetAddress(address);
aaaaRecord.SetTtl(aAddrRecord.GetTtl(isLegacyUnicast));
UpdateCacheFlushFlagIn(aaaaRecord, aSection, isLegacyUnicast);
SuccessOrAssert(message->Append(aaaaRecord));
}
else
{
Ip4::Address ip4Address;
ARecord aRecord;
SuccessOrAssert(ip4Address.ExtractFromIp4MappedIp6Address(address));
aRecord.Init();
aRecord.SetAddress(ip4Address);
aRecord.SetTtl(aAddrRecord.GetTtl(isLegacyUnicast));
UpdateCacheFlushFlagIn(aRecord, aSection, isLegacyUnicast);
SuccessOrAssert(message->Append(aRecord));
}
aTxMessage.IncrementRecordCount(aSection);
}
@@ -1786,11 +2120,16 @@ void Core::HostEntry::AppendNsecRecordTo(TxMessage &aTxMessage, Section aSection
{
TypeArray types;
if (mAddrRecord.IsPresent() && (mAddrRecord.GetTtl() > 0))
if (mIp6AddrRecord.IsPresent() && (mIp6AddrRecord.GetTtl() > 0))
{
types.Add(ResourceRecord::kTypeAaaa);
}
if ((mIp4AddrRecord != nullptr) && mIp4AddrRecord->IsPresent() && (mIp4AddrRecord->GetTtl() > 0))
{
types.Add(ResourceRecord::kTypeA);
}
if (mKeyRecord.IsPresent() && (mKeyRecord.GetTtl() > 0))
{
types.Add(ResourceRecord::kTypeKey);
@@ -1820,18 +2159,28 @@ exit:
return;
}
void Core::HostEntry::MarkToAppendAddrRecordsInAdditionalData(void)
{
mIp6AddrRecord.MarkToAppendInAdditionalData();
if (mIp4AddrRecord != nullptr)
{
mIp4AddrRecord->MarkToAppendInAdditionalData();
}
}
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
Error Core::HostEntry::CopyInfoTo(Host &aHost, EntryState &aState) const
{
Error error = kErrorNone;
VerifyOrExit(mAddrRecord.IsPresent(), error = kErrorNotFound);
VerifyOrExit(mIp6AddrRecord.IsPresent(), error = kErrorNotFound);
aHost.mHostName = mName.AsCString();
aHost.mAddresses = mAddresses.AsCArray();
aHost.mAddressesLength = mAddresses.GetLength();
aHost.mTtl = mAddrRecord.GetTtl();
aHost.mAddresses = mIp6AddrRecord.mAddresses.AsCArray();
aHost.mAddressesLength = mIp6AddrRecord.mAddresses.GetLength();
aHost.mTtl = mIp6AddrRecord.GetTtl();
aHost.mInfraIfIndex = Get<Core>().mInfraIfIndex;
aState = static_cast<EntryState>(GetState());
@@ -1854,6 +2203,25 @@ exit:
#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
//----------------------------------------------------------------------------------------------------------------------
// Core::HostEntry::AddrRecord
void Core::HostEntry::AddrRecord::Clear(void)
{
RecordInfo::Clear();
mAddresses.Free();
}
void Core::HostEntry::AddrRecord::UpdateAddresses(const Host &aHost)
{
UpdateProperty(mAddresses, AsCoreTypePtr(aHost.mAddresses), aHost.mAddressesLength);
}
void Core::HostEntry::AddrRecord::UpdateAddresses(const AddressArray &aAddresses)
{
UpdateProperty(mAddresses, aAddresses.AsCArray(), aAddresses.GetLength());
}
//----------------------------------------------------------------------------------------------------------------------
// Core::ServiceEntry
@@ -2103,21 +2471,21 @@ exit:
void Core::ServiceEntry::AnswerServiceNameQuestion(const AnswerInfo &aInfo)
{
RecordAndType records[] = {
{mSrvRecord, ResourceRecord::kTypeSrv},
{mTxtRecord, ResourceRecord::kTypeTxt},
{mKeyRecord, ResourceRecord::kTypeKey},
};
RecordAndTypeArray recordAndTypes;
VerifyOrExit(GetState() == kRegistered);
recordAndTypes.Add(mSrvRecord, ResourceRecord::kTypeSrv);
recordAndTypes.Add(mTxtRecord, ResourceRecord::kTypeTxt);
recordAndTypes.Add(mKeyRecord, ResourceRecord::kTypeKey);
if (aInfo.mIsProbe)
{
AnswerProbe(aInfo, records, GetArrayLength(records));
AnswerProbe(aInfo, recordAndTypes);
}
else
{
AnswerNonProbe(aInfo, records, GetArrayLength(records));
AnswerNonProbe(aInfo, recordAndTypes);
}
DetermineNextFireTime();
@@ -2313,7 +2681,7 @@ void Core::ServiceEntry::PrepareResponseRecords(EntryContext &aContext)
if (hostEntry != nullptr)
{
hostEntry->mAddrRecord.MarkToAppendInAdditionalData();
hostEntry->MarkToAppendAddrRecordsInAdditionalData();
}
}
@@ -2324,7 +2692,7 @@ void Core::ServiceEntry::PrepareResponseRecords(EntryContext &aContext)
if ((mSrvRecord.GetTtl() > 0) && (hostEntry != nullptr))
{
hostEntry->mAddrRecord.MarkToAppendInAdditionalData();
hostEntry->MarkToAppendAddrRecordsInAdditionalData();
}
}
@@ -2352,9 +2720,17 @@ void Core::ServiceEntry::PrepareResponseRecords(EntryContext &aContext)
AppendTxtRecordTo(response, kAdditionalDataSection);
}
if ((hostEntry != nullptr) && (hostEntry->mAddrRecord.ShouldAppendInAdditionalDataSection()))
if (hostEntry != nullptr)
{
hostEntry->AppendAddressRecordsTo(response, kAdditionalDataSection);
if (hostEntry->mIp6AddrRecord.ShouldAppendInAdditionalDataSection())
{
hostEntry->AppendIp6AddressRecordsTo(response, kAdditionalDataSection);
}
if ((hostEntry->mIp4AddrRecord != nullptr) && hostEntry->mIp4AddrRecord->ShouldAppendInAdditionalDataSection())
{
hostEntry->AppendIp4AddressRecordsTo(response, kAdditionalDataSection);
}
}
if (appendNsec || ShouldAnswerNsec(aContext.GetNow()))
@@ -2428,6 +2804,8 @@ void Core::ServiceEntry::DiscoverOffsetsAndHost(HostEntry *&aHostEntry)
// and name compression offsets from the previously appended
// entries.
// TODO: Need to handle name matching host name
aHostEntry = Get<Core>().mHostEntries.FindMatching(mHostName);
if ((aHostEntry != nullptr) && (aHostEntry->GetState() != GetState()))
@@ -3709,7 +4087,7 @@ void Core::RxMessage::ProcessQuestion(Question &aQuestion)
ExitNow();
}
// Check if question name matches a `HostEntry` or a `ServiceEntry`
// Check if question name matches a `HostEntry` or a `ServiceEntry`.
aQuestion.mEntry = Get<Core>().mHostEntries.FindMatching(name);
+113 -9
View File
@@ -77,16 +77,28 @@ extern "C" void otPlatMdnsHandleReceive(otInstance *aInstance,
bool aIsUnicast,
const otPlatMdnsAddressInfo *aAddress);
extern "C" void otPlatMdnsHandleHostAddressEvent(otInstance *aInstance,
const otIp6Address *aAddress,
bool aAdded,
uint32_t aInfraIfIndex);
/**
* Implements Multicast DNS (mDNS) core.
*/
class Core : public InstanceLocator, private NonCopyable
{
friend class ot::Instance;
friend void otPlatMdnsHandleReceive(otInstance *aInstance,
otMessage *aMessage,
bool aIsUnicast,
const otPlatMdnsAddressInfo *aAddress);
friend void otPlatMdnsHandleHostAddressEvent(otInstance *aInstance,
const otIp6Address *aAddress,
bool aAdded,
uint32_t aInfraIfIndex);
public:
/**
* Initializes a `Core` instance.
@@ -165,6 +177,26 @@ public:
*/
bool IsEnabled(void) const { return mIsEnabled; }
/**
* Gets the local host name.
*
* @returns The local host name.
*/
const char *GetLocalHostName(void) { return mLocalHost.GetName(); }
/**
* Sets the local host name.
*
* The local host name can be set only when the mDNS module is disabled. If not set the mDNS module itself will
* generate the local host name.
*
* @param[in] aName The local host name to use, can be to `nullptr` to allow the mDNS module to choose the name.
*
* @retval kErrorNone The local host name was successfully set.
* @retval kErrorInvalidState mDNS module is already enabled.
*/
Error SetLocalHostName(const char *aName) { return mLocalHost.SetName(aName); }
#if OPENTHREAD_CONFIG_MULTICAST_DNS_AUTO_ENABLE_ON_INFRA_IF
/**
* Notifies `AdvertisingProxy` that `InfraIf` state changed.
@@ -817,6 +849,12 @@ private:
kAppendedLabels,
};
enum AddrType : uint8_t
{
kIp4AddrType,
kIp6AddrType,
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Forward declarations
@@ -897,7 +935,9 @@ private:
class AddressArray : public Heap::Array<Ip6::Address>
{
public:
bool IsEmpty(void) const { return (GetLength() == 0); }
bool Matches(const Ip6::Address *aAddresses, uint16_t aNumAddresses) const;
bool Matches(const AddressArray &aOther) const;
void SetFrom(const Ip6::Address *aAddresses, uint16_t aNumAddresses);
};
@@ -1031,10 +1071,15 @@ private:
struct RecordAndType
{
RecordInfo &mRecord;
RecordInfo *mRecord;
uint16_t mType;
};
struct RecordAndTypeArray : public Array<RecordAndType, kTypeArraySize>
{
void Add(RecordInfo &aRecord, uint16_t aType);
};
typedef void (*NameAppender)(Entry &aEntry, TxMessage &aTxMessage, Section aSection);
Entry(void);
@@ -1056,8 +1101,8 @@ private:
void DetermineNextFireTime(void);
void DetermineNextAggrTxTime(NextFireTime &aNextAggrTxTime) const;
void ScheduleTimer(void);
void AnswerProbe(const AnswerInfo &aInfo, RecordAndType *aRecords, uint16_t aRecordsLength);
void AnswerNonProbe(const AnswerInfo &aInfo, RecordAndType *aRecords, uint16_t aRecordsLength);
void AnswerProbe(const AnswerInfo &aInfo, RecordAndTypeArray &aRecordAndTypes);
void AnswerNonProbe(const AnswerInfo &aInfo, RecordAndTypeArray &aRecordAndTypes);
void ScheduleNsecAnswer(const AnswerInfo &aInfo);
template <typename EntryType> void HandleTimer(EntryContext &aContext);
@@ -1086,6 +1131,47 @@ private:
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void HandleLocalHostEventTimer(void) { mLocalHost.HandleEventTimer(); }
class LocalHost : public InstanceLocator
{
public:
explicit LocalHost(Instance &aInstance);
const char *GetName(void) const { return mName.AsCString(); }
Error SetName(const char *aName);
void GenerateName(void);
const AddressArray &GetIp4Addresses(void) const { return mIp4Addresses; }
const AddressArray &GetIp6Addresses(void) const { return mIp6Addresses; }
void HandleAddressEvent(const Ip6::Address &aAddress, bool aAdded, uint32_t aInfraIfIndex);
void HandleEventTimer(void);
void ClearAddresses(void);
private:
static constexpr uint32_t kGuardTimeToProcessAddrEvents = 4; // msec
struct AddrEvent : public LinkedListEntry<AddrEvent>, public Heap::Allocatable<AddrEvent>
{
AddrEvent(const Ip6::Address &aAddress, bool aAdded);
bool Matches(const Ip6::Address &aAddress) const { return mAddress == aAddress; }
bool Matches(AddrType aType) const;
AddrEvent *mNext;
Ip6::Address mAddress;
bool mAdded;
};
using EventTimer = TimerMilliIn<Core, &Core::HandleLocalHostEventTimer>;
Heap::String mName;
AddressArray mIp4Addresses;
AddressArray mIp6Addresses;
OwningList<AddrEvent> mAddrEvents;
EventTimer mEventTimer;
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class HostEntry : public Entry, public LinkedListEntry<HostEntry>, public Heap::Allocatable<HostEntry>
{
friend class LinkedListEntry<HostEntry>;
@@ -1095,15 +1181,18 @@ private:
public:
HostEntry(void);
Error Init(Instance &aInstance, const Host &aHost) { return Init(aInstance, aHost.mHostName); }
Error Init(Instance &aInstance, const LocalHost &aLocalHost) { return Init(aInstance, aLocalHost.GetName()); }
Error Init(Instance &aInstance, const Key &aKey) { return Init(aInstance, aKey.mName); }
bool IsEmpty(void) const;
bool Matches(const Name &aName) const;
bool Matches(const Host &aHost) const;
bool Matches(const LocalHost &aLocalHost) const;
bool Matches(const Key &aKey) const;
bool Matches(const Heap::String &aName) const;
bool Matches(State aState) const { return GetState() == aState; }
bool Matches(const HostEntry &aEntry) const { return (this == &aEntry); }
void Register(const Host &aHost, const Callback &aCallback);
void Register(const LocalHost &aLocalHost, const Callback &aCallback);
void Register(const Key &aKey, const Callback &aCallback);
void Unregister(const Host &aHost);
void Unregister(const Key &aKey);
@@ -1119,6 +1208,15 @@ private:
#endif
private:
struct AddrRecord : public RecordInfo, public Heap::Allocatable<AddrRecord>
{
void Clear(void);
void UpdateAddresses(const Host &aHost);
void UpdateAddresses(const AddressArray &aAddresses);
AddressArray mAddresses;
};
Error Init(Instance &aInstance, const char *aName);
void ClearHost(void);
void ScheduleToRemoveIfEmpty(void);
@@ -1127,18 +1225,21 @@ private:
void PrepareResponseRecords(EntryContext &aContext);
void UpdateRecordsState(const TxMessage &aResponse);
void DetermineNextFireTime(void);
void AppendAddressRecordsTo(TxMessage &aTxMessage, Section aSection);
void AppendIp6AddressRecordsTo(TxMessage &aTxMessage, Section aSection);
void AppendIp4AddressRecordsTo(TxMessage &aTxMessage, Section aSection);
void AppendAddressRecordsTo(TxMessage &aTxMessage, Section aSection, AddrRecord &aAddrRecord, bool aIp6);
void AppendKeyRecordTo(TxMessage &aTxMessage, Section aSection);
void AppendNsecRecordTo(TxMessage &aTxMessage, Section aSection);
void AppendNameTo(TxMessage &aTxMessage, Section aSection);
void MarkToAppendAddrRecordsInAdditionalData(void);
static void AppendEntryName(Entry &aEntry, TxMessage &aTxMessage, Section aSection);
HostEntry *mNext;
Heap::String mName;
RecordInfo mAddrRecord;
AddressArray mAddresses;
uint16_t mNameOffset;
HostEntry *mNext;
Heap::String mName;
AddrRecord mIp6AddrRecord;
OwnedPtr<AddrRecord> mIp4AddrRecord;
uint16_t mNameOffset;
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -2101,6 +2202,8 @@ private:
template <typename CacheType, typename BrowserResolverType>
Error Stop(const BrowserResolverType &aBrowserOrResolver);
void AfterInstanceInit(void);
void HandleHostAddressEvent(const Ip6::Address &aAddress, bool aAdded, uint32_t aInfraIfIndex);
void InvokeConflictCallback(const char *aName, const char *aServiceType);
void HandleMessage(Message &aMessage, bool aIsUnicast, const AddressInfo &aSenderAddress);
void AddPassiveSrvTxtCache(const char *aServiceInstance, const char *aServiceType);
@@ -2140,6 +2243,7 @@ private:
bool mIsQuestionUnicastAllowed;
uint16_t mMaxMessageSize;
uint32_t mInfraIfIndex;
LocalHost mLocalHost;
OwningList<HostEntry> mHostEntries;
OwningList<ServiceEntry> mServiceEntries;
OwningList<ServiceType> mServiceTypes;
+476 -4
View File
@@ -245,7 +245,8 @@ struct DnsRecord : public Allocatable<DnsRecord>, public LinkedListEntry<DnsReco
{
RecordData(void) { memset(this, 0, sizeof(*this)); }
Ip6::Address mIp6Address; // For AAAAA (or A)
Ip6::Address mIp6Address; // For AAAAA
Ip4::Address mIp4Address; // For A
SrvData mSrv; // For SRV
Array<uint8_t, kMaxDataSize> mData; // For TXT or KEY
DnsName mPtrName; // For PTR
@@ -285,6 +286,12 @@ struct DnsRecord : public Allocatable<DnsRecord>, public LinkedListEntry<DnsReco
switch (mType)
{
case ResourceRecord::kTypeA:
VerifyOrQuit(record.GetLength() == sizeof(Ip4::Address));
SuccessOrQuit(aMessage.Read(offset, mData.mIp4Address));
logStr.Append(" %s", mData.mIp4Address.ToString().AsCString());
break;
case ResourceRecord::kTypeAaaa:
VerifyOrQuit(record.GetLength() == sizeof(Ip6::Address));
SuccessOrQuit(aMessage.Read(offset, mData.mIp6Address));
@@ -415,6 +422,31 @@ struct DnsRecords : public OwningList<DnsRecord>
return contains;
}
bool ContainsA(const DnsNameString &aFullName,
const Ip4::Address &aAddress,
bool aCacheFlush,
TtlCheckMode aTtlCheckMode,
uint32_t aTtl = 0) const
{
bool contains = false;
for (const DnsRecord &record : *this)
{
if (record.Matches(aFullName.AsCString()) && (record.mType == ResourceRecord::kTypeA) &&
(record.mData.mIp4Address == aAddress))
{
VerifyOrExit(record.mClass == ResourceRecord::kClassInternet);
VerifyOrExit(record.mCacheFlush == aCacheFlush);
VerifyOrExit(record.MatchesTtl(aTtlCheckMode, aTtl));
contains = true;
ExitNow();
}
}
exit:
return contains;
}
bool ContainsKey(const DnsNameString &aFullName,
const Data &aKeyData,
bool aCacheFlush,
@@ -556,7 +588,7 @@ struct DnsRecords : public OwningList<DnsRecord>
}
};
// Bit-flags used in `Validate()` with a `Service`
// Bit-flags used in `Validate()` with a `Service` or `LocalHost`
// to specify which records should be checked in the announce
// message.
@@ -566,6 +598,8 @@ static constexpr uint8_t kCheckSrv = (1 << 0);
static constexpr uint8_t kCheckTxt = (1 << 1);
static constexpr uint8_t kCheckPtr = (1 << 2);
static constexpr uint8_t kCheckServicesPtr = (1 << 3);
static constexpr uint8_t kCheckAaaa = (1 << 4);
static constexpr uint8_t kCheckA = (1 << 5);
enum GoodBye : bool // Used to indicate "goodbye" records (with zero TTL)
{
@@ -581,6 +615,16 @@ enum DnsMessageType : uint8_t
kLegacyUnicastResponse,
};
struct LocalHost
{
static constexpr uint32_t kTtl = 120;
static constexpr uint16_t kMaxAddrs = 16;
char mName[Name::kMaxNameSize];
Array<Ip6::Address, kMaxAddrs> mIp6Addrs;
Array<Ip4::Address, kMaxAddrs> mIp4Addrs;
};
struct DnsMessage : public Allocatable<DnsMessage>, public LinkedListEntry<DnsMessage>
{
DnsMessage *mNext;
@@ -743,6 +787,27 @@ struct DnsMessage : public Allocatable<DnsMessage>, public LinkedListEntry<DnsMe
}
}
void ValidateAsProbeFor(const LocalHost &aLocalHost, bool aUnicastResponse) const
{
DnsNameString fullName;
VerifyOrQuit(mHeader.GetType() == Header::kTypeQuery);
VerifyOrQuit(!mHeader.IsTruncationFlagSet());
fullName.Append("%s.local.", aLocalHost.mName);
VerifyOrQuit(mQuestions.Contains(fullName, aUnicastResponse));
for (const Ip6::Address &ip6Addr : aLocalHost.mIp6Addrs)
{
VerifyOrQuit(mAuthRecords.ContainsAaaa(fullName, ip6Addr, !kCacheFlush, kNonZeroTtl, LocalHost::kTtl));
}
for (const Ip4::Address &ip4Addr : aLocalHost.mIp4Addrs)
{
VerifyOrQuit(mAuthRecords.ContainsA(fullName, ip4Addr, !kCacheFlush, kNonZeroTtl, LocalHost::kTtl));
}
}
void ValidateAsProbeFor(const Core::Service &aService, bool aUnicastResponse) const
{
DnsNameString serviceName;
@@ -776,8 +841,7 @@ struct DnsMessage : public Allocatable<DnsMessage>, public LinkedListEntry<DnsMe
{
DnsNameString fullName;
TtlCheckMode ttlCheck;
bool cacheFlushSet = (mType == kLegacyUnicastResponse) ? !kCacheFlush : kCacheFlush;
bool cacheFlushSet = (mType == kLegacyUnicastResponse) ? !kCacheFlush : kCacheFlush;
ttlCheck = DetermineTtlCheckMode(mType, aIsGoodBye);
@@ -797,6 +861,49 @@ struct DnsMessage : public Allocatable<DnsMessage>, public LinkedListEntry<DnsMe
}
}
void Validate(const LocalHost &aLocalHost,
Section aSection,
AnnounceCheckFlags aCheckFlags,
GoodBye aIsGoodBye = kNotGoodBye) const
{
DnsNameString fullName;
TtlCheckMode ttlCheck;
bool cacheFlushSet = (mType == kLegacyUnicastResponse) ? !kCacheFlush : kCacheFlush;
ttlCheck = DetermineTtlCheckMode(mType, aIsGoodBye);
VerifyOrQuit(mHeader.GetType() == Header::kTypeResponse);
fullName.Append("%s.local.", aLocalHost.mName);
if (aCheckFlags & kCheckAaaa)
{
for (const Ip6::Address &ip6Addr : aLocalHost.mIp6Addrs)
{
VerifyOrQuit(
RecordsFor(aSection).ContainsAaaa(fullName, ip6Addr, cacheFlushSet, ttlCheck, LocalHost::kTtl));
}
}
if (aCheckFlags & kCheckA)
{
for (const Ip4::Address &ip4Addr : aLocalHost.mIp4Addrs)
{
VerifyOrQuit(
RecordsFor(aSection).ContainsA(fullName, ip4Addr, cacheFlushSet, ttlCheck, LocalHost::kTtl));
}
}
if (!aIsGoodBye && (aSection == kInAnswerSection))
{
bool shouldSeeAaaa = (aLocalHost.mIp6Addrs.GetLength() != 0);
bool shouldSeeA = (aLocalHost.mIp4Addrs.GetLength() != 0);
VerifyOrQuit(mAdditionalRecords.ContainsNsec(fullName, ResourceRecord::kTypeAaaa) == shouldSeeAaaa);
VerifyOrQuit(mAdditionalRecords.ContainsNsec(fullName, ResourceRecord::kTypeA) == shouldSeeA);
}
}
void Validate(const Core::Service &aService,
Section aSection,
AnnounceCheckFlags aCheckFlags,
@@ -2057,6 +2164,370 @@ void TestHostReg(void)
//---------------------------------------------------------------------------------------------------------------------
void TestLocalHost(void)
{
Core *mdns = InitTest();
LocalHost localHost;
Ip6::Address ip6Address;
Ip4::Address ip4Address;
const DnsMessage *dnsMsg;
uint16_t heapAllocations;
DnsNameString hostFullName;
Log("-------------------------------------------------------------------------------------------");
Log("TestLocalHost");
AdvanceTime(1);
heapAllocations = sHeapAllocatedPtrs.GetLength();
SuccessOrQuit(mdns->SetEnabled(true, kInfraIfIndex));
SuccessOrQuit(StringCopy(localHost.mName, mdns->GetLocalHostName()));
Log("Local host name is \"%s\"", localHost.mName);
hostFullName.Append("%s.local.", localHost.mName);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Add an IP6 address and IP4 address for local host, check probes and announcements");
SuccessOrQuit(ip6Address.FromString("fd00:cafe::1"));
SuccessOrQuit(localHost.mIp6Addrs.PushBack(ip6Address));
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ true, kInfraIfIndex);
SuccessOrQuit(ip4Address.FromString("200.1.5.6"));
SuccessOrQuit(localHost.mIp4Addrs.PushBack(ip4Address));
ip6Address.SetToIp4Mapped(ip4Address);
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ true, kInfraIfIndex);
AdvanceTime(4);
sDnsMessages.Clear();
for (uint8_t probeCount = 0; probeCount < 3; probeCount++)
{
sDnsMessages.Clear();
AdvanceTime(250);
VerifyOrQuit(!sDnsMessages.IsEmpty());
dnsMsg = sDnsMessages.GetHead();
dnsMsg->ValidateHeader(kMulticastQuery, /* Q */ 1, /* Ans */ 0, /* Auth */ 2, /* Addnl */ 0);
dnsMsg->ValidateAsProbeFor(localHost, /* aUnicastRequest */ (probeCount == 0));
VerifyOrQuit(dnsMsg->GetNext() == nullptr);
}
for (uint8_t anncCount = 0; anncCount < kNumAnnounces; anncCount++)
{
sDnsMessages.Clear();
AdvanceTime((anncCount == 0) ? 250 : (1U << (anncCount - 1)) * 1000);
VerifyOrQuit(!sDnsMessages.IsEmpty());
dnsMsg = sDnsMessages.GetHead();
dnsMsg->ValidateHeader(kMulticastResponse, /* Q */ 0, /* Ans */ 2, /* Auth */ 0, /* Addnl */ 1);
dnsMsg->Validate(localHost, kInAnswerSection, kCheckAaaa | kCheckA);
VerifyOrQuit(dnsMsg->GetNext() == nullptr);
}
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Send a query for AAAA record and validate the response");
AdvanceTime(2000);
sDnsMessages.Clear();
SendQuery(hostFullName.AsCString(), ResourceRecord::kTypeAaaa);
AdvanceTime(1000);
dnsMsg = sDnsMessages.GetHead();
VerifyOrQuit(dnsMsg != nullptr);
dnsMsg->ValidateHeader(kMulticastResponse, /* Q */ 0, /* Ans */ 1, /* Auth */ 0, /* Addnl */ 1);
dnsMsg->Validate(localHost, kInAnswerSection, kCheckAaaa);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Send a query for A record and validate the response");
AdvanceTime(2000);
sDnsMessages.Clear();
SendQuery(hostFullName.AsCString(), ResourceRecord::kTypeA);
AdvanceTime(1000);
dnsMsg = sDnsMessages.GetHead();
VerifyOrQuit(dnsMsg != nullptr);
dnsMsg->ValidateHeader(kMulticastResponse, /* Q */ 0, /* Ans */ 1, /* Auth */ 0, /* Addnl */ 1);
dnsMsg->Validate(localHost, kInAnswerSection, kCheckA);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Send a query for ANY record and validate the response");
AdvanceTime(2000);
sDnsMessages.Clear();
SendQuery(hostFullName.AsCString(), ResourceRecord::kTypeAny);
AdvanceTime(1000);
dnsMsg = sDnsMessages.GetHead();
VerifyOrQuit(dnsMsg != nullptr);
dnsMsg->ValidateHeader(kMulticastResponse, /* Q */ 0, /* Ans */ 2, /* Auth */ 0, /* Addnl */ 1);
dnsMsg->Validate(localHost, kInAnswerSection, kCheckAaaa | kCheckA);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Send a query for non-existing record and validate the response with NSEC");
AdvanceTime(2000);
sDnsMessages.Clear();
SendQuery(hostFullName.AsCString(), ResourceRecord::kTypeKey);
AdvanceTime(1000);
dnsMsg = sDnsMessages.GetHead();
VerifyOrQuit(dnsMsg != nullptr);
dnsMsg->ValidateHeader(kMulticastResponse, /* Q */ 0, /* Ans */ 0, /* Auth */ 0, /* Addnl */ 1);
VerifyOrQuit(dnsMsg->mAdditionalRecords.ContainsNsec(hostFullName, ResourceRecord::kTypeAaaa));
VerifyOrQuit(dnsMsg->mAdditionalRecords.ContainsNsec(hostFullName, ResourceRecord::kTypeA));
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Signal a new host IPv6 address is added and validate new announcements");
SuccessOrQuit(ip6Address.FromString("fd00:cafe::22"));
SuccessOrQuit(localHost.mIp6Addrs.PushBack(ip6Address));
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ true, kInfraIfIndex);
sDnsMessages.Clear();
AdvanceTime(5);
for (uint8_t anncCount = 0; anncCount < kNumAnnounces; anncCount++)
{
AdvanceTime((anncCount == 0) ? 0 : (1U << (anncCount - 1)) * 1000);
VerifyOrQuit(!sDnsMessages.IsEmpty());
dnsMsg = sDnsMessages.GetHead();
dnsMsg->ValidateHeader(kMulticastResponse, /* Q */ 0, /* Ans */ 2, /* Auth */ 0, /* Addnl */ 1);
dnsMsg->Validate(localHost, kInAnswerSection, kCheckAaaa);
VerifyOrQuit(dnsMsg->GetNext() == nullptr);
sDnsMessages.Clear();
}
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Signal new host IPv6 addresses added and removed");
SuccessOrQuit(ip6Address.FromString("fd00:cafe::22"));
localHost.mIp6Addrs.Remove(ip6Address);
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ false, kInfraIfIndex);
// Add and then remove the same address quickly
// It should not be included in the announcements.
SuccessOrQuit(ip6Address.FromString("fd00:cafe::333"));
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ true, kInfraIfIndex);
AdvanceTime(1);
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ false, kInfraIfIndex);
SuccessOrQuit(ip6Address.FromString("fd00:cafe::4444"));
SuccessOrQuit(localHost.mIp6Addrs.PushBack(ip6Address));
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ true, kInfraIfIndex);
Log("Validate the announcements");
sDnsMessages.Clear();
AdvanceTime(4);
for (uint8_t anncCount = 0; anncCount < kNumAnnounces; anncCount++)
{
AdvanceTime((anncCount == 0) ? 0 : (1U << (anncCount - 1)) * 1000);
VerifyOrQuit(!sDnsMessages.IsEmpty());
dnsMsg = sDnsMessages.GetHead();
dnsMsg->ValidateHeader(kMulticastResponse, /* Q */ 0, /* Ans */ 2, /* Auth */ 0, /* Addnl */ 1);
dnsMsg->Validate(localHost, kInAnswerSection, kCheckAaaa);
VerifyOrQuit(dnsMsg->GetNext() == nullptr);
sDnsMessages.Clear();
}
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Signal three new host IPv4 addresses added");
SuccessOrQuit(ip4Address.FromString("200.1.5.7"));
SuccessOrQuit(localHost.mIp4Addrs.PushBack(ip4Address));
ip6Address.SetToIp4Mapped(ip4Address);
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ true, kInfraIfIndex);
SuccessOrQuit(ip4Address.FromString("200.1.2.100"));
SuccessOrQuit(localHost.mIp4Addrs.PushBack(ip4Address));
ip6Address.SetToIp4Mapped(ip4Address);
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ true, kInfraIfIndex);
SuccessOrQuit(ip4Address.FromString("200.1.4.0"));
SuccessOrQuit(localHost.mIp4Addrs.PushBack(ip4Address));
ip6Address.SetToIp4Mapped(ip4Address);
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ true, kInfraIfIndex);
Log("Validate the announcements");
sDnsMessages.Clear();
AdvanceTime(5);
for (uint8_t anncCount = 0; anncCount < kNumAnnounces; anncCount++)
{
AdvanceTime((anncCount == 0) ? 0 : (1U << (anncCount - 1)) * 1000);
VerifyOrQuit(!sDnsMessages.IsEmpty());
dnsMsg = sDnsMessages.GetHead();
dnsMsg->ValidateHeader(kMulticastResponse, /* Q */ 0, /* Ans */ 4, /* Auth */ 0, /* Addnl */ 1);
dnsMsg->Validate(localHost, kInAnswerSection, kCheckA);
VerifyOrQuit(dnsMsg->GetNext() == nullptr);
sDnsMessages.Clear();
}
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Signal all host IPv4 addresses are removed, validate goodbye announcements");
for (const Ip4::Address &ip4Addr : localHost.mIp4Addrs)
{
ip6Address.SetToIp4Mapped(ip4Addr);
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ false, kInfraIfIndex);
}
localHost.mIp4Addrs.Clear();
sDnsMessages.Clear();
AdvanceTime(5);
for (uint8_t anncCount = 0; anncCount < kNumAnnounces; anncCount++)
{
AdvanceTime((anncCount == 0) ? 0 : (1U << (anncCount - 1)) * 1000);
VerifyOrQuit(!sDnsMessages.IsEmpty());
dnsMsg = sDnsMessages.GetHead();
dnsMsg->ValidateHeader(kMulticastResponse, /* Q */ 0, /* Ans */ 4, /* Auth */ 0, /* Addnl */ 1);
dnsMsg->Validate(localHost, kInAnswerSection, kCheckA, kGoodBye);
VerifyOrQuit(dnsMsg->GetNext() == nullptr);
sDnsMessages.Clear();
}
AdvanceTime(10 * 1000);
VerifyOrQuit(sDnsMessages.IsEmpty());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Signal removal of an IPv6 host address which was not added earlier");
SuccessOrQuit(ip6Address.FromString("fd00:cafe::beef"));
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ false, kInfraIfIndex);
Log("Validate that there are no announcements");
sDnsMessages.Clear();
AdvanceTime(10 * 1000);
VerifyOrQuit(sDnsMessages.IsEmpty());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Signal remove and re-add of the same host IPv6 address quickly");
ip6Address = localHost.mIp6Addrs[0];
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ false, kInfraIfIndex);
AdvanceTime(1);
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ true, kInfraIfIndex);
Log("Validate that there are no announcements");
sDnsMessages.Clear();
AdvanceTime(10 * 1000);
VerifyOrQuit(sDnsMessages.IsEmpty());
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Validate `SetLocalHostName()`");
VerifyOrQuit(mdns->SetLocalHostName("itsme") == kErrorInvalidState);
SuccessOrQuit(mdns->SetEnabled(false, kInfraIfIndex));
SuccessOrQuit(mdns->SetLocalHostName("itsme"));
VerifyOrQuit(StringMatch(mdns->GetLocalHostName(), "itsme"));
localHost.mIp4Addrs.Clear();
localHost.mIp6Addrs.Clear();
SuccessOrQuit(StringCopy(localHost.mName, mdns->GetLocalHostName()));
Log("Local host name is \"%s\"", localHost.mName);
hostFullName.Append("%s.local.", localHost.mName);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Re-enable mDNS module, add 4 new host IPv6 address and validate probe and announcements");
SuccessOrQuit(mdns->SetEnabled(true, kInfraIfIndex));
SuccessOrQuit(ip6Address.FromString("fd00:beef::a"));
SuccessOrQuit(localHost.mIp6Addrs.PushBack(ip6Address));
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ true, kInfraIfIndex);
SuccessOrQuit(ip6Address.FromString("fd00:beef::b"));
SuccessOrQuit(localHost.mIp6Addrs.PushBack(ip6Address));
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ true, kInfraIfIndex);
SuccessOrQuit(ip6Address.FromString("fd00:beef::c"));
SuccessOrQuit(localHost.mIp6Addrs.PushBack(ip6Address));
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ true, kInfraIfIndex);
SuccessOrQuit(ip6Address.FromString("fd00:beef::d"));
SuccessOrQuit(localHost.mIp6Addrs.PushBack(ip6Address));
otPlatMdnsHandleHostAddressEvent(sInstance, &ip6Address, /* aAdded */ true, kInfraIfIndex);
AdvanceTime(4);
sDnsMessages.Clear();
for (uint8_t probeCount = 0; probeCount < 3; probeCount++)
{
sDnsMessages.Clear();
AdvanceTime(250);
VerifyOrQuit(!sDnsMessages.IsEmpty());
dnsMsg = sDnsMessages.GetHead();
dnsMsg->ValidateHeader(kMulticastQuery, /* Q */ 1, /* Ans */ 0, /* Auth */ 4, /* Addnl */ 0);
dnsMsg->ValidateAsProbeFor(localHost, /* aUnicastRequest */ (probeCount == 0));
VerifyOrQuit(dnsMsg->GetNext() == nullptr);
}
for (uint8_t anncCount = 0; anncCount < kNumAnnounces; anncCount++)
{
sDnsMessages.Clear();
AdvanceTime((anncCount == 0) ? 250 : (1U << (anncCount - 1)) * 1000);
VerifyOrQuit(!sDnsMessages.IsEmpty());
dnsMsg = sDnsMessages.GetHead();
dnsMsg->ValidateHeader(kMulticastResponse, /* Q */ 0, /* Ans */ 4, /* Auth */ 0, /* Addnl */ 1);
dnsMsg->Validate(localHost, kInAnswerSection, kCheckAaaa | kCheckA);
VerifyOrQuit(dnsMsg->GetNext() == nullptr);
}
AdvanceTime(1000);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
SuccessOrQuit(mdns->SetEnabled(false, kInfraIfIndex));
VerifyOrQuit(sHeapAllocatedPtrs.GetLength() <= heapAllocations);
Log("End of test");
testFreeInstance(sInstance);
}
//---------------------------------------------------------------------------------------------------------------------
void TestKeyReg(void)
{
Core *mdns = InitTest();
@@ -8182,6 +8653,7 @@ int main(void)
{
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENABLE
ot::Dns::Multicast::TestHostReg();
ot::Dns::Multicast::TestLocalHost();
ot::Dns::Multicast::TestKeyReg();
ot::Dns::Multicast::TestServiceReg();
ot::Dns::Multicast::TestUnregisterBeforeProbeFinished();