[mdns] add API to get the list of local host IP addresses (#11404)

This commit introduces an API to iterate over the local host IPv6 and
IPv4 addresses known to the OpenThread mDNS module.

The platform layer is responsible for monitoring and reporting all
host IPv4 and IPv6 addresses to the OpenThread mDNS module, which
then tracks the full address list
(see `otPlatMdnsHandleHostAddressEvent()`). The newly added function
allows iteration through this tracked list, primarily intended for
information and debugging purposes.

This commit also adds a CLI command to utilize the new API.
Additionally, the `test_mdns` unit test has been updated to validate
the functionality of the newly added API.
This commit is contained in:
Abtin Keshavarzian
2025-04-18 12:11:16 -07:00
committed by GitHub
parent b25d1af5f5
commit f70749d21d
7 changed files with 244 additions and 1 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 (496)
#define OPENTHREAD_API_VERSION (497)
/**
* @addtogroup api-instance
+36
View File
@@ -40,6 +40,7 @@
#include <openthread/error.h>
#include <openthread/instance.h>
#include <openthread/ip6.h>
#include <openthread/nat64.h>
#include <openthread/platform/dnssd.h>
#ifdef __cplusplus
@@ -141,6 +142,20 @@ typedef enum otMdnsEntryState
OT_MDNS_ENTRY_STATE_REMOVING, ///< Entry is being removed (sending "goodbye" announcements).
} otMdnsEntryState;
/**
* Represents a local host IPv4 or IPv6 address entry.
*/
typedef struct otMdnsLocalHostAddress
{
bool mIsIp6; ///< Indicates whether the address is IPv6 (`true`) or IPv4 (`false`).
uint32_t mInfraIfIndex; ///< The infrastructure network interface index.
union
{
otIp6Address mIp6; ///< The IPv6 address (valid when `mIsIp6` is true).
otIp4Address mIp4; ///< The IPv4 address (valid when `mIsIp6` is false).
} mAddress; ///< The address.
} otMdnsLocalHostAddress;
/**
* Enables or disables the mDNS module.
*
@@ -497,6 +512,27 @@ otError otMdnsGetNextService(otInstance *aInstance,
*/
otError otMdnsGetNextKey(otInstance *aInstance, otMdnsIterator *aIterator, otMdnsKey *aKey, otMdnsEntryState *aState);
/**
* Iterates over the local host IPv6 and IPv4 addresses tracked by OpenThread mDNS module.
*
* Requires `OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE`.
*
* The platform layer is responsible for monitoring and reporting all host IPv4 and IPv6 addresses to the OpenThread
* mDNS module, which then tracks the full address list (see `otPlatMdnsHandleHostAddressEvent()`). This function
* allows iteration through this tracked list, primarily intended for information and debugging purposes.
*
* @param[in] aInstance The OpenThread instance.
* @param[out] aIterator Pointer to the iterator to use.
* @param[out] aAddress Pointer to an `otMdnsLocalHostAddress` to output the next address entry.
*
* @retval OT_ERROR_NONE The @p aAddress, and @p aIterator are updated successfully.
* @retval OT_ERROR_NOT_FOUND Reached the end of the list.
* @retval OT_ERROR_INVALID_ARGS Iterator is not valid.
*/
otError otMdnsGetNextLocalHostAddress(otInstance *aInstance,
otMdnsIterator *aIterator,
otMdnsLocalHostAddress *aAddress);
/**
* Represents a service browser.
*
+48
View File
@@ -605,6 +605,51 @@ exit:
return error;
}
template <> otError Mdns::Process<Cmd("localhostaddrs")>(Arg aArgs[])
{
otError error = OT_ERROR_NONE;
otMdnsIterator *iterator = nullptr;
otMdnsLocalHostAddress addr;
VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
iterator = otMdnsAllocateIterator(GetInstancePtr());
VerifyOrExit(iterator != nullptr, error = OT_ERROR_NO_BUFS);
while (true)
{
error = otMdnsGetNextLocalHostAddress(GetInstancePtr(), iterator, &addr);
if (error == OT_ERROR_NOT_FOUND)
{
error = OT_ERROR_NONE;
ExitNow();
}
SuccessOrExit(error);
if (addr.mIsIp6)
{
OutputIp6AddressLine(addr.mAddress.mIp6);
}
else
{
char ip4AddressString[OT_IP4_ADDRESS_STRING_SIZE];
otIp4AddressToString(&addr.mAddress.mIp4, ip4AddressString, sizeof(ip4AddressString));
OutputLine("%s", ip4AddressString);
}
}
exit:
if (iterator != nullptr)
{
otMdnsFreeIterator(GetInstancePtr(), iterator);
}
return error;
}
#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
otError Mdns::ParseStartOrStop(const Arg &aArg, bool &aIsStart)
@@ -1213,6 +1258,9 @@ otError Mdns::Process(Arg aArgs[])
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
CmdEntry("ip6resolvers"),
CmdEntry("keys"),
#endif
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
CmdEntry("localhostaddrs"),
#endif
CmdEntry("localhostname"),
CmdEntry("recordquerier"),
+11
View File
@@ -166,6 +166,17 @@ otError otMdnsGetNextKey(otInstance *aInstance, otMdnsIterator *aIterator, otMdn
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().GetNextKey(*aIterator, *aKey, *aState);
}
otError otMdnsGetNextLocalHostAddress(otInstance *aInstance,
otMdnsIterator *aIterator,
otMdnsLocalHostAddress *aAddress)
{
AssertPointerIsNotNull(aIterator);
AssertPointerIsNotNull(aAddress);
return AsCoreType(aInstance).Get<Dns::Multicast::Core>().GetNextLocalHostAddress(*aIterator, *aAddress);
}
#endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
otError otMdnsStartBrowser(otInstance *aInstance, const otMdnsBrowser *aBroswer)
+49
View File
@@ -244,6 +244,11 @@ Error Core::GetNextKey(Iterator &aIterator, Key &aKey, EntryState &aState) const
return static_cast<EntryIterator &>(aIterator).GetNextKey(aKey, aState);
}
Error Core::GetNextLocalHostAddress(Iterator &aIterator, LocalHostAddress &aAddress)
{
return static_cast<EntryIterator &>(aIterator).GetNextLocalHostAddress(aAddress);
}
Error Core::GetNextBrowser(Iterator &aIterator, Browser &aBrowser, CacheInfo &aInfo) const
{
return static_cast<EntryIterator &>(aIterator).GetNextBrowser(aBrowser, aInfo);
@@ -7402,6 +7407,50 @@ exit:
return error;
}
Error Core::EntryIterator::GetNextLocalHostAddress(LocalHostAddress &aAddress)
{
Error error = kErrorNone;
uint16_t index;
Ip4::Address ip4Addr;
if (mType == kUnspecified)
{
mLocalHostAddrIndex = 0;
mType = kLocalHostAddress;
}
else
{
VerifyOrExit(mType == kLocalHostAddress, error = kErrorInvalidArgs);
}
ClearAllBytes(aAddress);
index = mLocalHostAddrIndex;
if (index < Get<Core>().mLocalHost.GetIp6Addresses().GetLength())
{
aAddress.mIsIp6 = true;
aAddress.mAddress.mIp6 = Get<Core>().mLocalHost.GetIp6Addresses()[index];
}
else
{
index -= Get<Core>().mLocalHost.GetIp6Addresses().GetLength();
VerifyOrExit(index < Get<Core>().mLocalHost.GetIp4Addresses().GetLength(), error = kErrorNotFound);
IgnoreError(ip4Addr.ExtractFromIp4MappedIp6Address(Get<Core>().mLocalHost.GetIp4Addresses()[index]));
aAddress.mIsIp6 = false;
aAddress.mAddress.mIp4 = ip4Addr;
}
aAddress.mInfraIfIndex = Get<Core>().mInfraIfIndex;
mLocalHostAddrIndex++;
exit:
return error;
}
Error Core::EntryIterator::GetNextBrowser(Browser &aBrowser, CacheInfo &aInfo)
{
Error error = kErrorNone;
+18
View File
@@ -118,6 +118,7 @@ public:
typedef otMdnsHost Host; ///< Host information.
typedef otMdnsService Service; ///< Service information.
typedef otMdnsKey Key; ///< Key information.
typedef otMdnsLocalHostAddress LocalHostAddress; ///< Local host address information.
typedef otMdnsBrowser Browser; ///< Browser.
typedef otMdnsBrowseCallback BrowseCallback; ///< Browser callback.
typedef otMdnsBrowseResult BrowseResult; ///< Browser result.
@@ -668,6 +669,7 @@ public:
* structure (like `mServiceType`) remain valid until the next call to any OpenThread stack's public or platform
* API/callback.
*
* @param[in] aIterator The iterator to use.
* @param[out] aService A `Service` to return the information about the next service entry.
* @param[out] aState An `EntryState` to return the entry state.
*
@@ -683,6 +685,7 @@ public:
* On success, @p aKey is populated with information about the next key. Pointers within the `Key` structure
* (like `mName`) remain valid until the next call to any OpenThread stack's public or platform API/callback.
*
* @param[in] aIterator The iterator to use.
* @param[out] aKey A `Key` to return the information about the next key entry.
* @param[out] aState An `EntryState` to return the entry state.
*
@@ -692,6 +695,18 @@ public:
*/
Error GetNextKey(Iterator &aIterator, Key &aKey, EntryState &aState) const;
/**
* Iterates over the local host IPv6 and IPv4 addresses.
*
* @param[in] aIterator The iterator to use.
* @param[out] aAddress A `LocalHostAddress` to output the next address entry.
*
* @retval kErrorNone The @p aAddress and @p aIterator are updated successfully.
* @retval kErrorNotFound Reached the end of the list.
* @retval kErrorInvalidArgs Iterator is not valid.
*/
Error GetNextLocalHostAddress(Iterator &aIterator, LocalHostAddress &aAddress);
/**
* Iterates over browsers.
*
@@ -2148,6 +2163,7 @@ private:
Error GetNextHost(Host &aHost, EntryState &aState);
Error GetNextService(Service &aService, EntryState &aState);
Error GetNextKey(Key &aKey, EntryState &aState);
Error GetNextLocalHostAddress(LocalHostAddress &aAddress);
Error GetNextBrowser(Browser &aBrowser, CacheInfo &aInfo);
Error GetNextSrvResolver(SrvResolver &aResolver, CacheInfo &aInfo);
Error GetNextTxtResolver(TxtResolver &aResolver, CacheInfo &aInfo);
@@ -2165,6 +2181,7 @@ private:
kService,
kHostKey,
kServiceKey,
kLocalHostAddress,
kBrowser,
kSrvResolver,
kTxtResolver,
@@ -2181,6 +2198,7 @@ private:
{
const HostEntry *mHostEntry;
const ServiceEntry *mServiceEntry;
uint16_t mLocalHostAddrIndex;
const BrowseCache *mBrowseCache;
const SrvCache *mSrvCache;
const TxtCache *mTxtCache;
+81
View File
@@ -2164,6 +2164,69 @@ void TestHostReg(void)
//---------------------------------------------------------------------------------------------------------------------
void ValidateLocalHostAddresses(Core &aMdns, const LocalHost &aLocalHost)
{
#if OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE
Core::Iterator *iterator;
Array<Ip6::Address, LocalHost::kMaxAddrs> ip6Addrs;
Array<Ip4::Address, LocalHost::kMaxAddrs> ip4Addrs;
iterator = aMdns.AllocateIterator();
VerifyOrQuit(iterator != nullptr);
Log("Verifying local host addresses (%u IPv6 and %u IPv4 addresses)", aLocalHost.mIp6Addrs.GetLength(),
aLocalHost.mIp4Addrs.GetLength());
while (true)
{
Error error;
Core::LocalHostAddress addr;
error = aMdns.GetNextLocalHostAddress(*iterator, addr);
if (error == kErrorNotFound)
{
break;
}
SuccessOrQuit(error);
VerifyOrQuit(addr.mInfraIfIndex == kInfraIfIndex);
if (addr.mIsIp6)
{
SuccessOrQuit(ip6Addrs.PushBack(AsCoreType(&addr.mAddress.mIp6)));
Log(" %s", ip6Addrs.Back()->ToString().AsCString());
}
else
{
SuccessOrQuit(ip4Addrs.PushBack(AsCoreType(&addr.mAddress.mIp4)));
Log(" %s", ip4Addrs.Back()->ToString().AsCString());
}
}
aMdns.FreeIterator(*iterator);
VerifyOrQuit(ip6Addrs.GetLength() == aLocalHost.mIp6Addrs.GetLength());
for (const Ip6::Address &ip6Addr : ip6Addrs)
{
VerifyOrQuit(aLocalHost.mIp6Addrs.Contains(ip6Addr));
}
VerifyOrQuit(ip4Addrs.GetLength() == aLocalHost.mIp4Addrs.GetLength());
for (const Ip4::Address &ip4Addr : ip4Addrs)
{
VerifyOrQuit(aLocalHost.mIp4Addrs.Contains(ip4Addr));
}
#else
OT_UNUSED_VARIABLE(aMdns);
OT_UNUSED_VARIABLE(aLocalHost);
#endif
}
void TestLocalHost(void)
{
Core *mdns = InitTest();
@@ -2188,6 +2251,8 @@ void TestLocalHost(void)
hostFullName.Append("%s.local.", localHost.mName);
ValidateLocalHostAddresses(*mdns, localHost);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Add an IP6 address and IP4 address for local host, check probes and announcements");
@@ -2230,6 +2295,8 @@ void TestLocalHost(void)
VerifyOrQuit(dnsMsg->GetNext() == nullptr);
}
ValidateLocalHostAddresses(*mdns, localHost);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Send a query for AAAA record and validate the response");
@@ -2314,6 +2381,8 @@ void TestLocalHost(void)
sDnsMessages.Clear();
}
ValidateLocalHostAddresses(*mdns, localHost);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Signal new host IPv6 addresses added and removed");
@@ -2351,6 +2420,8 @@ void TestLocalHost(void)
sDnsMessages.Clear();
}
ValidateLocalHostAddresses(*mdns, localHost);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Signal three new host IPv4 addresses added");
@@ -2387,6 +2458,8 @@ void TestLocalHost(void)
sDnsMessages.Clear();
}
ValidateLocalHostAddresses(*mdns, localHost);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Signal removal of all host addresses and add them all back");
@@ -2532,6 +2605,8 @@ void TestLocalHost(void)
AdvanceTime(10 * 1000);
VerifyOrQuit(sDnsMessages.IsEmpty());
ValidateLocalHostAddresses(*mdns, localHost);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Signal removal of an IPv6 host address which was not added earlier");
@@ -2545,6 +2620,8 @@ void TestLocalHost(void)
AdvanceTime(10 * 1000);
VerifyOrQuit(sDnsMessages.IsEmpty());
ValidateLocalHostAddresses(*mdns, localHost);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Signal remove and re-add of the same host IPv6 address quickly");
@@ -2560,6 +2637,8 @@ void TestLocalHost(void)
AdvanceTime(10 * 1000);
VerifyOrQuit(sDnsMessages.IsEmpty());
ValidateLocalHostAddresses(*mdns, localHost);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
Log("Validate `SetLocalHostName()`");
@@ -2629,6 +2708,8 @@ void TestLocalHost(void)
VerifyOrQuit(dnsMsg->GetNext() == nullptr);
}
ValidateLocalHostAddresses(*mdns, localHost);
AdvanceTime(1000);
Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");