diff --git a/include/openthread/instance.h b/include/openthread/instance.h index fa23c6cac..71a0fe3c3 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -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 diff --git a/include/openthread/mdns.h b/include/openthread/mdns.h index 05f79812c..c0ed1cfe6 100644 --- a/include/openthread/mdns.h +++ b/include/openthread/mdns.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #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. * diff --git a/src/cli/cli_mdns.cpp b/src/cli/cli_mdns.cpp index 68f0c9f24..32b7399b2 100644 --- a/src/cli/cli_mdns.cpp +++ b/src/cli/cli_mdns.cpp @@ -605,6 +605,51 @@ exit: return error; } +template <> otError Mdns::Process(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"), diff --git a/src/core/api/mdns_api.cpp b/src/core/api/mdns_api.cpp index 596265cf3..1df04df0b 100644 --- a/src/core/api/mdns_api.cpp +++ b/src/core/api/mdns_api.cpp @@ -166,6 +166,17 @@ otError otMdnsGetNextKey(otInstance *aInstance, otMdnsIterator *aIterator, otMdn return AsCoreType(aInstance).Get().GetNextKey(*aIterator, *aKey, *aState); } +otError otMdnsGetNextLocalHostAddress(otInstance *aInstance, + otMdnsIterator *aIterator, + otMdnsLocalHostAddress *aAddress) + +{ + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aAddress); + + return AsCoreType(aInstance).Get().GetNextLocalHostAddress(*aIterator, *aAddress); +} + #endif // OPENTHREAD_CONFIG_MULTICAST_DNS_ENTRY_ITERATION_API_ENABLE otError otMdnsStartBrowser(otInstance *aInstance, const otMdnsBrowser *aBroswer) diff --git a/src/core/net/mdns.cpp b/src/core/net/mdns.cpp index 9248b5993..604f442ac 100644 --- a/src/core/net/mdns.cpp +++ b/src/core/net/mdns.cpp @@ -244,6 +244,11 @@ Error Core::GetNextKey(Iterator &aIterator, Key &aKey, EntryState &aState) const return static_cast(aIterator).GetNextKey(aKey, aState); } +Error Core::GetNextLocalHostAddress(Iterator &aIterator, LocalHostAddress &aAddress) +{ + return static_cast(aIterator).GetNextLocalHostAddress(aAddress); +} + Error Core::GetNextBrowser(Iterator &aIterator, Browser &aBrowser, CacheInfo &aInfo) const { return static_cast(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().mLocalHost.GetIp6Addresses().GetLength()) + { + aAddress.mIsIp6 = true; + aAddress.mAddress.mIp6 = Get().mLocalHost.GetIp6Addresses()[index]; + } + else + { + index -= Get().mLocalHost.GetIp6Addresses().GetLength(); + + VerifyOrExit(index < Get().mLocalHost.GetIp4Addresses().GetLength(), error = kErrorNotFound); + + IgnoreError(ip4Addr.ExtractFromIp4MappedIp6Address(Get().mLocalHost.GetIp4Addresses()[index])); + + aAddress.mIsIp6 = false; + aAddress.mAddress.mIp4 = ip4Addr; + } + + aAddress.mInfraIfIndex = Get().mInfraIfIndex; + mLocalHostAddrIndex++; + +exit: + return error; +} + Error Core::EntryIterator::GetNextBrowser(Browser &aBrowser, CacheInfo &aInfo) { Error error = kErrorNone; diff --git a/src/core/net/mdns.hpp b/src/core/net/mdns.hpp index 663994460..0246603a9 100644 --- a/src/core/net/mdns.hpp +++ b/src/core/net/mdns.hpp @@ -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; diff --git a/tests/unit/test_mdns.cpp b/tests/unit/test_mdns.cpp index 17a75e5d0..16edace9a 100644 --- a/tests/unit/test_mdns.cpp +++ b/tests/unit/test_mdns.cpp @@ -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 ip6Addrs; + Array 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("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");