diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp index f81ea0af5..bbe1e24c2 100644 --- a/src/core/thread/address_resolver.cpp +++ b/src/core/thread/address_resolver.cpp @@ -748,6 +748,12 @@ template <> void AddressResolver::HandleTmf(Coap::Msg &aMsg) SuccessOrExit(error = Tlv::Find(aMsg.mMessage, target)); SuccessOrExit(error = Tlv::Find(aMsg.mMessage, meshLocalIid)); + if (Get().IsMeshLocalAddress(target) || target.IsLinkLocalUnicast()) + { + LogInfo("Ignoring %s for target %s", UriToString(), target.ToString().AsCString()); + ExitNow(); + } + for (Ip6::Netif::UnicastAddress &address : Get().GetUnicastAddresses()) { if (address.GetAddress() == target && Get().GetMeshLocalEid().GetIid() != meshLocalIid) diff --git a/tests/nexus/CMakeLists.txt b/tests/nexus/CMakeLists.txt index b1ae3863f..e49f6f9d8 100644 --- a/tests/nexus/CMakeLists.txt +++ b/tests/nexus/CMakeLists.txt @@ -395,6 +395,7 @@ ot_nexus_test(1_4_PIC_TC_4 "cert;nexus") ot_nexus_test(1_4_CS_TC_3 "cert;nexus") # Misc tests +ot_nexus_test(address_error_filter "core;nexus") ot_nexus_test(announce_no_flap_on_unmergeable_partitions "core;nexus") ot_nexus_test(anycast "core;nexus") ot_nexus_test(anycast_locator "core;nexus") diff --git a/tests/nexus/test_address_error_filter.cpp b/tests/nexus/test_address_error_filter.cpp new file mode 100644 index 000000000..554900946 --- /dev/null +++ b/tests/nexus/test_address_error_filter.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2026, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "platform/nexus_core.hpp" +#include "platform/nexus_node.hpp" +#include "thread/child_table.hpp" +#include "thread/thread_netif.hpp" +#include "thread/thread_tlvs.hpp" +#include "thread/tmf.hpp" + +namespace ot { +namespace Nexus { + +static constexpr uint32_t kFormNetworkTime = 13 * 1000; +static constexpr uint32_t kAttachToRouterTime = 200 * 1000; +static constexpr uint32_t kPropagationTime = 10 * 1000; + +static void SendAddressError(Node &aSender, const Ip6::Address &aDestination, const Ip6::Address &aTarget) +{ + Tmf::Message *message = aSender.Get().AllocateAndInitPostMessageTo(kUriAddressError, aDestination); + + VerifyOrQuit(message != nullptr); + SuccessOrQuit(Tlv::Append(*message, aTarget)); + SuccessOrQuit(Tlv::Append(*message, aSender.Get().GetMeshLocalEid().GetIid())); + SuccessOrQuit(aSender.Get().SendMessageTo(*message, aDestination)); +} + +void TestAddressErrorFilter(void) +{ + Core nexus; + Node &leader = nexus.CreateNode(); + Node &router = nexus.CreateNode(); + Node &med = nexus.CreateNode(); + + leader.SetName("LEADER"); + router.SetName("ROUTER"); + med.SetName("MED"); + + Log("1. Form a network with a router and a MED"); + + AllowLinkBetween(leader, router); + AllowLinkBetween(leader, med); + nexus.AdvanceTime(0); + + leader.Form(); + nexus.AdvanceTime(kFormNetworkTime); + VerifyOrQuit(leader.Get().IsLeader()); + + router.Join(leader, Node::kAsFtd); + med.Join(leader, Node::kAsMed); + nexus.AdvanceTime(kAttachToRouterTime); + VerifyOrQuit(router.Get().IsRouter()); + VerifyOrQuit(med.Get().IsChild()); + + const Ip6::Address routerRloc = router.Get().GetMeshLocalRloc(); + const Ip6::Address leaderRloc = leader.Get().GetMeshLocalRloc(); + const Ip6::Address routerMlEid = router.Get().GetMeshLocalEid(); + const Ip6::Address routerLinkLocal = router.Get().GetLinkLocalAddress(); + const Ip6::Address childMlEid = med.Get().GetMeshLocalEid(); + Ip6::Address leaderAloc; + + leader.Get().ComposeLeaderAloc(leaderAloc); + VerifyOrQuit(router.Get().HasUnicastAddress(routerRloc)); + VerifyOrQuit(leader.Get().HasUnicastAddress(leaderAloc)); + + Log("2. Confirm Address Errors remove ordinary EIDs on the router and leader"); + + Ip6::Netif::UnicastAddress eid; + + eid.InitAsSlaacOrigin(/* aPrefixLength */ 64, /* aPreferred */ true); + SuccessOrQuit(eid.GetAddress().FromString("2001:db8::1")); + SuccessOrQuit(router.Get().AddExternalUnicastAddress(eid)); + VerifyOrQuit(router.Get().HasUnicastAddress(eid.GetAddress())); + + SendAddressError(med, routerRloc, eid.GetAddress()); + nexus.AdvanceTime(kPropagationTime); + VerifyOrQuit(!router.Get().HasUnicastAddress(eid.GetAddress())); + + SuccessOrQuit(eid.GetAddress().FromString("2001:db8::2")); + SuccessOrQuit(leader.Get().AddExternalUnicastAddress(eid)); + VerifyOrQuit(leader.Get().HasUnicastAddress(eid.GetAddress())); + + SendAddressError(router, leaderRloc, eid.GetAddress()); + nexus.AdvanceTime(kPropagationTime); + VerifyOrQuit(!leader.Get().HasUnicastAddress(eid.GetAddress())); + + Log("3. Keep the router RLOC and leader ALOC"); + + SendAddressError(med, routerRloc, routerRloc); + nexus.AdvanceTime(kPropagationTime); + VerifyOrQuit(router.Get().HasUnicastAddress(routerRloc)); + nexus.SendAndVerifyEchoRequest(med, routerRloc); + + SendAddressError(router, leaderRloc, leaderAloc); + nexus.AdvanceTime(kPropagationTime); + VerifyOrQuit(leader.Get().HasUnicastAddress(leaderAloc)); + nexus.SendAndVerifyEchoRequest(med, leaderAloc); + + Log("4. Keep the router ML-EID and link-local address"); + + VerifyOrQuit(router.Get().HasUnicastAddress(routerMlEid)); + VerifyOrQuit(router.Get().HasUnicastAddress(routerLinkLocal)); + + SendAddressError(med, routerRloc, routerMlEid); + nexus.AdvanceTime(kPropagationTime); + VerifyOrQuit(router.Get().HasUnicastAddress(routerMlEid)); + + SendAddressError(med, routerRloc, routerLinkLocal); + nexus.AdvanceTime(kPropagationTime); + VerifyOrQuit(router.Get().HasUnicastAddress(routerLinkLocal)); + + Log("5. Keep the child's ML-EID in its parent address table"); + + Child *child = leader.Get().FindChild(med.Get().GetExtAddress(), Child::kInStateValid); + + VerifyOrQuit(child != nullptr); + VerifyOrQuit(child->HasIp6Address(childMlEid)); + + SendAddressError(router, leaderRloc, childMlEid); + nexus.AdvanceTime(kPropagationTime); + child = leader.Get().FindChild(med.Get().GetExtAddress(), Child::kInStateValid); + VerifyOrQuit(child != nullptr); + VerifyOrQuit(child->HasIp6Address(childMlEid)); + + Log("6. Keep global EID duplicate resolution with a locator-like IID"); + + SuccessOrQuit(eid.GetAddress().FromString("2001:db8::ff:fe00:1234")); + SuccessOrQuit(router.Get().AddExternalUnicastAddress(eid)); + VerifyOrQuit(router.Get().HasUnicastAddress(eid.GetAddress())); + + SendAddressError(med, routerRloc, eid.GetAddress()); + nexus.AdvanceTime(kPropagationTime); + VerifyOrQuit(!router.Get().HasUnicastAddress(eid.GetAddress())); + + Log("7. Verify the router remains a functional router"); + + VerifyOrQuit(router.Get().IsRouter()); + VerifyOrQuit(router.Get().HasUnicastAddress(routerRloc)); + nexus.SendAndVerifyEchoRequest(med, routerRloc); +} + +} // namespace Nexus +} // namespace ot + +int main(void) +{ + ot::Nexus::TestAddressErrorFilter(); + printf("All tests passed\n"); + return 0; +}