Skip to content

Commit ef3122b

Browse files
authored
[nexus] migrate test_ping_lla_src.py to nexus (openthread#12990)
This commit migrates the test_ping_lla_src.py script from thread-cert to the Nexus test framework. The new test_ping_lla_src.cpp implements the same test logic: - Forms a network with a Leader and two Routers. - Verifies that pings using a Link-Local Address (LLA) as the source succeed when sent to a neighbor's Mesh-Local EID (ML-EID). - Verifies that pings using an LLA source fail when sent to a non-neighbor's ML-EID, as LLAs are only valid for single-hop communication. - Verifies that external routes are not used for LLA-sourced packets. To support this migration, the Nexus Core class was enhanced with: - Overloads for SendAndVerifyEchoRequest that allow specifying a source address. - New SendAndVerifyNoEchoResponse methods to verify that no echo response is received (useful for negative test scenarios). Changes: - Added tests/nexus/test_ping_lla_src.cpp - Updated tests/nexus/CMakeLists.txt to include the new test. - Enhanced tests/nexus/platform/nexus_core.hpp/cpp with new helpers. - Removed tests/scripts/thread-cert/test_ping_lla_src.py.
1 parent 7525818 commit ef3122b

6 files changed

Lines changed: 194 additions & 129 deletions

File tree

tests/nexus/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ ot_nexus_test(nat64_translator "core;nexus")
410410
ot_nexus_test(netdata_publisher "core;nexus")
411411
ot_nexus_test(on_mesh_prefix "core;nexus")
412412
ot_nexus_test(pbbr_aloc "core;nexus")
413+
ot_nexus_test(ping_lla_src "core;nexus")
413414
ot_nexus_test(reed_address_solicit_rejected "core;nexus")
414415
ot_nexus_test(reset "core;nexus")
415416
ot_nexus_test(router_downgrade_on_sec_policy_change "core;nexus")

tests/nexus/platform/nexus_core.cpp

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,7 +1093,8 @@ void Core::SendAndVerifyEchoRequest(Node &aSender,
10931093
const Ip6::Address &aDestination,
10941094
uint16_t aPayloadSize,
10951095
uint8_t aHopLimit,
1096-
uint32_t aResponseTimeout)
1096+
uint32_t aResponseTimeout,
1097+
bool aForceSource)
10971098
{
10981099
static constexpr uint16_t kIdentifier = 0x1234;
10991100

@@ -1105,11 +1106,56 @@ void Core::SendAndVerifyEchoRequest(Node &aSender,
11051106

11061107
SuccessOrQuit(aSender.Get<Ip6::Icmp>().RegisterHandler(icmpHandler));
11071108

1108-
aSender.SendEchoRequest(aDestination, kIdentifier, aPayloadSize, aHopLimit);
1109+
aSender.SendEchoRequest(aDestination, kIdentifier, aPayloadSize, aHopLimit,
1110+
aForceSource ? &aExpectedSource : nullptr);
11091111
AdvanceTime(aResponseTimeout);
1112+
1113+
SuccessOrQuit(aSender.Get<Ip6::Icmp>().UnregisterHandler(icmpHandler));
1114+
11101115
VerifyOrQuit(icmpContext.mResponseReceived);
1116+
}
1117+
1118+
void Core::SendAndVerifyNoEchoResponse(Node &aSender,
1119+
const Ip6::Address &aDestination,
1120+
uint16_t aPayloadSize,
1121+
uint8_t aHopLimit,
1122+
uint32_t aResponseTimeout)
1123+
{
1124+
static constexpr uint16_t kIdentifier = 0x1234;
1125+
1126+
IcmpEchoResponseContext icmpContext(aSender, kIdentifier);
1127+
Ip6::Icmp::Handler icmpHandler(HandleIcmpResponse, &icmpContext);
1128+
1129+
SuccessOrQuit(aSender.Get<Ip6::Icmp>().RegisterHandler(icmpHandler));
1130+
1131+
aSender.SendEchoRequest(aDestination, kIdentifier, aPayloadSize, aHopLimit);
1132+
AdvanceTime(aResponseTimeout);
1133+
1134+
SuccessOrQuit(aSender.Get<Ip6::Icmp>().UnregisterHandler(icmpHandler));
1135+
1136+
VerifyOrQuit(!icmpContext.mResponseReceived);
1137+
}
1138+
1139+
void Core::SendAndVerifyNoEchoResponse(Node &aSender,
1140+
const Ip6::Address &aSrcAddress,
1141+
const Ip6::Address &aDestination,
1142+
uint16_t aPayloadSize,
1143+
uint8_t aHopLimit,
1144+
uint32_t aResponseTimeout)
1145+
{
1146+
static constexpr uint16_t kIdentifier = 0x1234;
1147+
1148+
IcmpEchoResponseContext icmpContext(aSender, kIdentifier);
1149+
Ip6::Icmp::Handler icmpHandler(HandleIcmpResponse, &icmpContext);
1150+
1151+
SuccessOrQuit(aSender.Get<Ip6::Icmp>().RegisterHandler(icmpHandler));
1152+
1153+
aSender.SendEchoRequest(aDestination, kIdentifier, aPayloadSize, aHopLimit, &aSrcAddress);
1154+
AdvanceTime(aResponseTimeout);
11111155

11121156
SuccessOrQuit(aSender.Get<Ip6::Icmp>().UnregisterHandler(icmpHandler));
1157+
1158+
VerifyOrQuit(!icmpContext.mResponseReceived);
11131159
}
11141160

11151161
} // namespace Nexus

tests/nexus/platform/nexus_core.hpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,21 @@ class Core
9696
const Ip6::Address &aDestination,
9797
uint16_t aPayloadSize = 0,
9898
uint8_t aHopLimit = Ip6::kDefaultHopLimit,
99-
uint32_t aResponseTimeout = 1000);
99+
uint32_t aResponseTimeout = 1000,
100+
bool aForceSource = true);
101+
102+
void SendAndVerifyNoEchoResponse(Node &aSender,
103+
const Ip6::Address &aDestination,
104+
uint16_t aPayloadSize = 0,
105+
uint8_t aHopLimit = Ip6::kDefaultHopLimit,
106+
uint32_t aResponseTimeout = 1000);
107+
108+
void SendAndVerifyNoEchoResponse(Node &aSender,
109+
const Ip6::Address &aSrcAddress,
110+
const Ip6::Address &aDestination,
111+
uint16_t aPayloadSize = 0,
112+
uint8_t aHopLimit = Ip6::kDefaultHopLimit,
113+
uint32_t aResponseTimeout = 1000);
100114

101115
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
102116
// Used by platform implementation

tests/nexus/test_ipv6_source_selection.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,25 +78,25 @@ void TestIPv6SourceSelection(void)
7878

7979
Log("---------------------------------------------------------------------------------------");
8080
Log("Step 1: RLOC source for RLOC destination");
81-
nexus.SendAndVerifyEchoRequest(router, routerRloc, leaderRloc);
81+
nexus.SendAndVerifyEchoRequest(router, routerRloc, leaderRloc, 0, Ip6::kDefaultHopLimit, 1000, false);
8282

8383
Log("---------------------------------------------------------------------------------------");
8484
Log("Step 2: ML-EID source for ALOC destination");
85-
nexus.SendAndVerifyEchoRequest(router, routerMleid, leaderAloc);
85+
nexus.SendAndVerifyEchoRequest(router, routerMleid, leaderAloc, 0, Ip6::kDefaultHopLimit, 1000, false);
8686

8787
Log("---------------------------------------------------------------------------------------");
8888
Log("Step 3: ML-EID source for ML-EID destination");
89-
nexus.SendAndVerifyEchoRequest(router, routerMleid, leaderMleid);
89+
nexus.SendAndVerifyEchoRequest(router, routerMleid, leaderMleid, 0, Ip6::kDefaultHopLimit, 1000, false);
9090

9191
Log("---------------------------------------------------------------------------------------");
9292
Log("Step 4: Link-local source for Link-local destination");
93-
nexus.SendAndVerifyEchoRequest(router, routerLinkLocal, leaderLinkLocal);
93+
nexus.SendAndVerifyEchoRequest(router, routerLinkLocal, leaderLinkLocal, 0, Ip6::kDefaultHopLimit, 1000, false);
9494

9595
Log("---------------------------------------------------------------------------------------");
9696
Log("Step 5: ML-EID source for Realm-local multicast destination");
9797
Ip6::Address multicastAddr;
9898
SuccessOrQuit(multicastAddr.FromString("ff03::1"));
99-
nexus.SendAndVerifyEchoRequest(router, routerMleid, multicastAddr);
99+
nexus.SendAndVerifyEchoRequest(router, routerMleid, multicastAddr, 0, Ip6::kDefaultHopLimit, 1000, false);
100100

101101
Log("---------------------------------------------------------------------------------------");
102102
Log("Step 6: GUA source for GUA destination");
@@ -120,7 +120,7 @@ void TestIPv6SourceSelection(void)
120120
VerifyOrQuit(!leaderGua.IsUnspecified());
121121
VerifyOrQuit(!routerGua.IsUnspecified());
122122

123-
nexus.SendAndVerifyEchoRequest(router, routerGua, leaderGua);
123+
nexus.SendAndVerifyEchoRequest(router, routerGua, leaderGua, 0, Ip6::kDefaultHopLimit, 1000, false);
124124

125125
Log("---------------------------------------------------------------------------------------");
126126
Log("Step 7: GUA source for external address (default route)");
@@ -137,7 +137,7 @@ void TestIPv6SourceSelection(void)
137137
extNetifAddr.mValid = true;
138138
SuccessOrQuit(otIp6AddUnicastAddress(&leader.GetInstance(), &extNetifAddr));
139139

140-
nexus.SendAndVerifyEchoRequest(router, routerGua, externalAddr);
140+
nexus.SendAndVerifyEchoRequest(router, routerGua, externalAddr, 0, Ip6::kDefaultHopLimit, 1000, false);
141141

142142
nexus.SaveTestInfo("test_ipv6_source_selection.json");
143143
}

tests/nexus/test_ping_lla_src.cpp

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright (c) 2026, The OpenThread Authors.
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
* 1. Redistributions of source code must retain the above copyright
8+
* notice, this list of conditions and the following disclaimer.
9+
* 2. Redistributions in binary form must reproduce the above copyright
10+
* notice, this list of conditions and the following disclaimer in the
11+
* documentation and/or other materials provided with the distribution.
12+
* 3. Neither the name of the copyright holder nor the
13+
* names of its contributors may be used to endorse or promote products
14+
* derived from this software without specific prior written permission.
15+
*
16+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26+
* POSSIBILITY OF SUCH DAMAGE.
27+
*/
28+
29+
#include <stdio.h>
30+
31+
#include "platform/nexus_core.hpp"
32+
#include "platform/nexus_node.hpp"
33+
34+
namespace ot {
35+
namespace Nexus {
36+
37+
void TestPingLlaSrc(void)
38+
{
39+
/**
40+
* Test ping with LLA source address.
41+
*
42+
* Topology:
43+
* ROUTER_2 ----- ROUTER_1 ---- ROUTER_3
44+
*
45+
* ROUTER_1 is leader. ROUTER_2 and ROUTER_3 are routers.
46+
*/
47+
48+
Core nexus;
49+
50+
Node &router1 = nexus.CreateNode();
51+
Node &router2 = nexus.CreateNode();
52+
Node &router3 = nexus.CreateNode();
53+
54+
router1.SetName("Router_1");
55+
router2.SetName("Router_2");
56+
router3.SetName("Router_3");
57+
58+
AllowLinkBetween(router1, router2);
59+
AllowLinkBetween(router1, router3);
60+
61+
nexus.AdvanceTime(0);
62+
63+
Log("---------------------------------------------------------------------------------------");
64+
Log("Step 1: Form network");
65+
66+
router1.Form();
67+
nexus.AdvanceTime(13 * 1000); // kFormNetworkTime
68+
VerifyOrQuit(router1.Get<Mle::Mle>().IsLeader());
69+
70+
router2.Join(router1);
71+
nexus.AdvanceTime(200 * 1000); // kAttachToRouterTime
72+
VerifyOrQuit(router2.Get<Mle::Mle>().IsRouter());
73+
74+
router3.Join(router1);
75+
nexus.AdvanceTime(200 * 1000); // kAttachToRouterTime
76+
VerifyOrQuit(router3.Get<Mle::Mle>().IsRouter());
77+
78+
Ip6::Address router1Mleid = router1.Get<Mle::Mle>().GetMeshLocalEid();
79+
Ip6::Address router2LinkLocal = router2.Get<Mle::Mle>().GetLinkLocalAddress();
80+
Ip6::Address router3Mleid = router3.Get<Mle::Mle>().GetMeshLocalEid();
81+
82+
Log("---------------------------------------------------------------------------------------");
83+
Log("Step 2: Ping a neighbor with LLA src should succeed");
84+
// router2 pings router1's MLEID using its own LLA as source.
85+
nexus.SendAndVerifyEchoRequest(router2, router2LinkLocal, router1Mleid);
86+
87+
Log("---------------------------------------------------------------------------------------");
88+
Log("Step 3: Ping a non-neighbor with LLA src should fail");
89+
// router2 pings router3's MLEID using its own LLA as source.
90+
// It should fail because LLA is only valid for one-hop.
91+
nexus.SendAndVerifyNoEchoResponse(router2, router2LinkLocal, router3Mleid);
92+
93+
Log("---------------------------------------------------------------------------------------");
94+
Log("Step 4: Make sure external routes are not used for LLA src");
95+
96+
NetworkData::ExternalRouteConfig routeConfig;
97+
routeConfig.Clear();
98+
SuccessOrQuit(routeConfig.GetPrefix().FromString("2001::/64"));
99+
routeConfig.mStable = true;
100+
101+
SuccessOrQuit(router1.Get<NetworkData::Local>().AddHasRoutePrefix(routeConfig));
102+
router1.Get<NetworkData::Notifier>().HandleServerDataUpdated();
103+
nexus.AdvanceTime(10 * 1000);
104+
105+
Ip6::Address externalAddr;
106+
SuccessOrQuit(externalAddr.FromString("2001::1"));
107+
108+
// router2 pings external address using its own LLA as source.
109+
// External routes should not be used for LLA source.
110+
nexus.SendAndVerifyNoEchoResponse(router2, router2LinkLocal, externalAddr);
111+
112+
nexus.SaveTestInfo("test_ping_lla_src.json");
113+
}
114+
115+
} // namespace Nexus
116+
} // namespace ot
117+
118+
int main(void)
119+
{
120+
ot::Nexus::TestPingLlaSrc();
121+
printf("All tests passed\n");
122+
return 0;
123+
}

tests/scripts/thread-cert/test_ping_lla_src.py

Lines changed: 0 additions & 119 deletions
This file was deleted.

0 commit comments

Comments
 (0)