From d9ca7f05fc5f6d9b501f4ee20e51eede0d37bb57 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Fri, 10 Jul 2026 18:49:15 -0700 Subject: [PATCH] [ip6] drop forwarded packets with foreign link-local source address (#13317) This commit updates `Ip6::SendRaw()` to drop packets forwarded from the host stack or an adjacent infrastructure link into OpenThread if their IPv6 source address is a link-local address that does not belong to the Thread interface (`ThreadNetif`). Updates to Nexus tests and test platform: - Update `verify_1_3_DBR_TC_1.py` Step 7 by removing the destination address filter (`filter_ipv6_dst(ED_1_OMR)`). This strengthens the verification check to ensure that no packet with an infrastructure link-local source address (`ETH_1_LL_ADDR`) is ever forwarded onto the Thread mesh regardless of destination address. - Add `InfraIf::FindAddress(const Ip6::Prefix &)` to look up addresses matching an IPv6 prefix on simulated infrastructure interfaces. - Update `test_1_2_MATN_TC_21.cpp` Step 16 so the backbone host uses its address matching the Border Router's favored on-link prefix rather than its link-local address when sending multicast pings to `MA1`. --- src/core/net/ip6.cpp | 9 +++++++++ tests/nexus/platform/nexus_infra_if.cpp | 11 ++++++++--- tests/nexus/platform/nexus_infra_if.hpp | 1 + tests/nexus/test_1_2_MATN_TC_21.cpp | 13 +++++++++++-- tests/nexus/verify_1_3_DBR_TC_1.py | 1 - 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index 027e8f24c..7a6e0f1b7 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -1102,6 +1102,15 @@ Error Ip6::SendRaw(OwnedPtr aMessagePtr) ExitNow(error = kErrorDrop); } + // When the message is forwarded from host to Thread, drop it if + // its source is a link-local address that does not belong to the + // Thread interface. + if (header.GetSource().IsLinkLocalUnicast() && !Get().HasUnicastAddress(header.GetSource())) + { + LogInfo("Dropping packet with foreign link-local source address"); + ExitNow(error = kErrorDrop); + } + // When the packet is forwarded from host to Thread, if its source is on-mesh or its destination is // mesh-local, we'll drop the packet unless the packet originates from this device. if (Get().IsOnMesh(header.GetSource()) || diff --git a/tests/nexus/platform/nexus_infra_if.cpp b/tests/nexus/platform/nexus_infra_if.cpp index 413ffe67d..4779923b8 100644 --- a/tests/nexus/platform/nexus_infra_if.cpp +++ b/tests/nexus/platform/nexus_infra_if.cpp @@ -97,14 +97,19 @@ void InfraIf::RemoveAllAddresses(void) const Ip6::Address *InfraIf::FindAddress(const char *aPrefix) const { - Ip6::Prefix prefix; - const Ip6::Address *matchedAddress = nullptr; + Ip6::Prefix prefix; SuccessOrQuit(prefix.FromString(aPrefix)); + return FindAddress(prefix); +} + +const Ip6::Address *InfraIf::FindAddress(const Ip6::Prefix &aPrefix) const +{ + const Ip6::Address *matchedAddress = nullptr; for (const Ip6::Address &address : mAddresses) { - if (address.MatchesPrefix(prefix)) + if (address.MatchesPrefix(aPrefix)) { matchedAddress = &address; break; diff --git a/tests/nexus/platform/nexus_infra_if.hpp b/tests/nexus/platform/nexus_infra_if.hpp index 1b143ea63..15b533b53 100644 --- a/tests/nexus/platform/nexus_infra_if.hpp +++ b/tests/nexus/platform/nexus_infra_if.hpp @@ -53,6 +53,7 @@ public: void RemoveAllAddresses(void); const Ip6::Address *FindAddress(const char *aPrefix) const; + const Ip6::Address *FindAddress(const Ip6::Prefix &aPrefix) const; const Ip6::Address &FindMatchingAddress(const char *aPrefix) const; const Ip6::Address &GetLinkLocalAddress(void) const { return mAddresses[0]; } diff --git a/tests/nexus/test_1_2_MATN_TC_21.cpp b/tests/nexus/test_1_2_MATN_TC_21.cpp index faae49957..0e4a18a92 100644 --- a/tests/nexus/test_1_2_MATN_TC_21.cpp +++ b/tests/nexus/test_1_2_MATN_TC_21.cpp @@ -149,6 +149,7 @@ void TestMatnTc21(void) Ip6::Address mae1; Ip6::Address mae2; Ip6::Address mae3; + Ip6::Prefix onLinkPrefix; br1.SetName("BR_1"); br2.SetName("BR_2"); @@ -206,6 +207,8 @@ void TestMatnTc21(void) VerifyOrQuit(br1.Get().IsPrimary()); VerifyOrQuit(!br2.Get().IsPrimary()); + SuccessOrQuit(br1.Get().GetFavoredOnLinkPrefix(onLinkPrefix)); + nexus.AddTestVar("MA1", kMA1); nexus.AddTestVar("MA3", kMA3); nexus.AddTestVar("MA5", kMA5); @@ -410,8 +413,14 @@ void TestMatnTc21(void) * - N/A */ Log("Step 16: Host sends a ICMPv6 Echo Request to MA1 on the backbone link."); - host.mInfraIf.SendEchoRequest(host.mInfraIf.GetLinkLocalAddress(), ma1, kEchoIdentifier, kEchoPayloadSize); - nexus.AdvanceTime(0); + { + const Ip6::Address *srcAddr; + + srcAddr = host.mInfraIf.FindAddress(onLinkPrefix); + VerifyOrQuit(srcAddr != nullptr); + host.mInfraIf.SendEchoRequest(*srcAddr, ma1, kEchoIdentifier, kEchoPayloadSize); + nexus.AdvanceTime(0); + } /** * Step 17 diff --git a/tests/nexus/verify_1_3_DBR_TC_1.py b/tests/nexus/verify_1_3_DBR_TC_1.py index 0228dd6d5..8bb71754c 100644 --- a/tests/nexus/verify_1_3_DBR_TC_1.py +++ b/tests/nexus/verify_1_3_DBR_TC_1.py @@ -301,7 +301,6 @@ def verify(pv): pkts.filter_wpan_src64(BR_1).\ filter_ipv6_src(Ipv6Addr(pv.vars['ETH_1_LL_ADDR'])).\ - filter_ipv6_dst(ED_1_OMR).\ filter_ping_request().\ must_not_next()