mirror of
https://github.com/espressif/openthread.git
synced 2026-06-06 05:24:51 +00:00
[nexus] add DBR-TC-02 test case for multiple BR reachability (#12732)
This commit introduces the 1_3_DBR_TC_2 nexus test case to verify bi-directional reachability between Thread and infrastructure devices in a topology with multiple Border Routers. Key changes: - Implement test_1_3_DBR_TC_2.cpp for step-by-step execution logic. - Implement verify_1_3_DBR_TC_2.py for pcap-based verification. - Enable OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE in nexus platform configuration to support prefix manipulation. - Add icmpv6.opt.pio_preferred_lifetime to pktverify layer fields. - Register the new test case in CMakeLists.txt and the default nexus test run script. - Refactor verify_1_3_DBR_TC_1.py and verify_1_3_DBR_TC_2.py to move nested helper functions to the top level for better modularity. - Remove an unnecessary Deinit() call in test_1_3_DBR_TC_1.cpp and its definition in nexus_infra_if.hpp. The test verifies: - Border Router adoption of existing OMR prefixes in the network. - Router Advertisement (RA) behavior on the infrastructure link, including correct RIO options and suppression of non-deprecating PIOs when another BR is present. - Bi-directional reachability between Thread End Devices and Adjacent Infrastructure Link (AIL) hosts during BR transitions. - Automatic election of a new Leader and promotion to Primary BR upon loss of the previous Leader. - Correct derivation of BR ULA prefixes from the Extended PAN ID.
This commit is contained in:
@@ -255,6 +255,7 @@ ot_nexus_test(1_2_BBR_TC_1 "cert;nexus")
|
||||
ot_nexus_test(1_2_BBR_TC_2 "cert;nexus")
|
||||
ot_nexus_test(1_2_BBR_TC_3 "cert;nexus")
|
||||
ot_nexus_test(1_3_DBR_TC_1 "cert;nexus")
|
||||
ot_nexus_test(1_3_DBR_TC_2 "cert;nexus")
|
||||
|
||||
# Misc tests
|
||||
ot_nexus_test(border_admitter "core;nexus")
|
||||
|
||||
@@ -153,4 +153,6 @@
|
||||
#define OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE 1
|
||||
#define OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL OT_LOG_LEVEL_INFO
|
||||
|
||||
#define OPENTHREAD_CONFIG_BORDER_ROUTING_TESTING_API_ENABLE 1
|
||||
|
||||
#endif // OT_NEXUS_OPENTHREAD_CORE_NEXUS_CONFIG_H_
|
||||
|
||||
@@ -45,11 +45,6 @@ public:
|
||||
InfraIf(void);
|
||||
|
||||
void Init(Node &aNode);
|
||||
void Deinit(void)
|
||||
{
|
||||
mIfIndex = 0;
|
||||
mAddresses.Clear();
|
||||
}
|
||||
|
||||
bool IsInitialized(void) const { return mIfIndex != 0; }
|
||||
|
||||
|
||||
@@ -191,6 +191,7 @@ DEFAULT_TESTS=(
|
||||
"1_2_BBR_TC_2"
|
||||
"1_2_BBR_TC_3"
|
||||
"1_3_DBR_TC_1"
|
||||
"1_3_DBR_TC_2"
|
||||
)
|
||||
|
||||
# Use provided arguments or the default test list
|
||||
|
||||
@@ -102,8 +102,6 @@ void Test_1_3_DBR_TC_1(void)
|
||||
ed1.SetName("ED_1");
|
||||
eth1.SetName("Eth_1");
|
||||
|
||||
ed1.mInfraIf.Deinit();
|
||||
|
||||
nexus.AdvanceTime(0);
|
||||
|
||||
Instance::SetLogLevel(kLogLevelNote);
|
||||
|
||||
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* 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 <stdio.h>
|
||||
|
||||
#include "platform/nexus_core.hpp"
|
||||
#include "platform/nexus_node.hpp"
|
||||
|
||||
namespace ot {
|
||||
namespace Nexus {
|
||||
|
||||
/**
|
||||
* Time to advance for a node to form a network and become leader, in milliseconds.
|
||||
*/
|
||||
static constexpr uint32_t kFormNetworkTime = 13 * 1000;
|
||||
|
||||
/**
|
||||
* Time to advance for a node to join as a child and upgrade to a router, in milliseconds.
|
||||
*/
|
||||
static constexpr uint32_t kJoinNetworkTime = 200 * 1000;
|
||||
|
||||
/**
|
||||
* Time to advance for the BR to perform automatic actions (RA, Network Data), in milliseconds.
|
||||
*/
|
||||
static constexpr uint32_t kBrActionTime = 100 * 1000;
|
||||
|
||||
/**
|
||||
* Time to advance for the ping response, in milliseconds.
|
||||
*/
|
||||
static constexpr uint32_t kPingResponseTime = 5 * 1000;
|
||||
|
||||
/**
|
||||
* Time to advance for leader timeout (NETWORK_ID_TIMEOUT + 10s), in milliseconds.
|
||||
*/
|
||||
static constexpr uint32_t kLeaderTimeoutTime = (120 + 10) * 1000;
|
||||
|
||||
/**
|
||||
* Infrastructure interface index.
|
||||
*/
|
||||
static constexpr uint32_t kInfraIfIndex = 1;
|
||||
|
||||
/**
|
||||
* Echo Request identifier.
|
||||
*/
|
||||
static constexpr uint16_t kEchoIdentifier = 0xabcd;
|
||||
|
||||
/**
|
||||
* Echo Request payload size.
|
||||
*/
|
||||
static constexpr uint16_t kEchoPayloadSize = 10;
|
||||
|
||||
void Test_1_3_DBR_TC_2(void)
|
||||
{
|
||||
/**
|
||||
* 1.2. [1.3] [CERT] Reachability - Multiple BRs - Single Thread / Single Infrastructure Link
|
||||
*
|
||||
* 1.2.1. Purpose
|
||||
* To test the following:
|
||||
* 1. Bi-directional reachability between Thread devices and infrastructure devices
|
||||
* 2. No existing IPv6 infrastructure
|
||||
* 3. Multiple BRS
|
||||
* 4. DUT BR adopts existing ULA and OMR prefixes
|
||||
*
|
||||
* 1.2.2. Topology
|
||||
* - BR 1 (DUT) - Thread Border Router
|
||||
* - BR 2-Test Bed device operating as a Thread Border Router Device and the Leader
|
||||
* - ED 1-Test Bed device operating as a Thread End Device, attached to BR_1
|
||||
* - Eth 1-Test bed border router device on an Adjacent Infrastructure Link
|
||||
*
|
||||
* Spec Reference | V1.1 Section | V1.3.0 Section
|
||||
* ---------------|--------------|---------------
|
||||
* Reachability | N/A | 1.3
|
||||
*/
|
||||
|
||||
Core nexus;
|
||||
|
||||
Node &br1 = nexus.CreateNode();
|
||||
Node &br2 = nexus.CreateNode();
|
||||
Node &ed1 = nexus.CreateNode();
|
||||
Node ð1 = nexus.CreateNode();
|
||||
|
||||
br1.SetName("BR_1");
|
||||
br2.SetName("BR_2");
|
||||
ed1.SetName("ED_1");
|
||||
eth1.SetName("Eth_1");
|
||||
|
||||
nexus.AdvanceTime(0);
|
||||
|
||||
Instance::SetLogLevel(kLogLevelNote);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 1: Device: Eth 1, BR 2 Description (DBR-1.2): Form topology. Wait for BR_2 to...");
|
||||
|
||||
/**
|
||||
* Step 1
|
||||
* - Device: Eth 1, BR 2
|
||||
* - Description (DBR-1.2): Form topology. Wait for BR_2 to: 1. Register as border router in Thread Network Data 2.
|
||||
* Send multicast ND RAS PIO with ULA prefix (ULA_init) RIO with OMR prefix (OMR_init)
|
||||
* - Pass Criteria: N/A
|
||||
*/
|
||||
|
||||
eth1.mInfraIf.Init(eth1);
|
||||
|
||||
br2.AllowList(br1);
|
||||
|
||||
br2.Form();
|
||||
nexus.AdvanceTime(kFormNetworkTime);
|
||||
|
||||
br2.Get<BorderRouter::InfraIf>().Init(kInfraIfIndex, true);
|
||||
br2.Get<BorderRouter::RoutingManager>().Init();
|
||||
SuccessOrQuit(br2.Get<BorderRouter::RoutingManager>().SetEnabled(true));
|
||||
|
||||
nexus.AdvanceTime(kBrActionTime);
|
||||
|
||||
Ip6::Prefix omrInit;
|
||||
SuccessOrQuit(br2.Get<BorderRouter::RoutingManager>().GetOmrPrefix(omrInit));
|
||||
nexus.AddTestVar("OMR_INIT", omrInit.ToString().AsCString());
|
||||
|
||||
Ip6::Prefix ulaInit;
|
||||
SuccessOrQuit(br2.Get<BorderRouter::RoutingManager>().GetOnLinkPrefix(ulaInit));
|
||||
nexus.AddTestVar("ULA_INIT", ulaInit.ToString().AsCString());
|
||||
|
||||
{
|
||||
MeshCoP::Dataset::Info datasetInfo;
|
||||
SuccessOrQuit(br2.Get<MeshCoP::ActiveDatasetManager>().Read(datasetInfo));
|
||||
const MeshCoP::ExtendedPanId &extPanId = datasetInfo.Get<MeshCoP::Dataset::kExtendedPanId>();
|
||||
nexus.AddTestVar("EXT_PAN_ID_VAR", extPanId.ToString().AsCString());
|
||||
}
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 2: Device: BR 1 (DUT) Description (DBR-1.2): Enable: turn on device.");
|
||||
|
||||
/**
|
||||
* Step 2
|
||||
* - Device: BR 1 (DUT)
|
||||
* - Description (DBR-1.2): Enable: turn on device.
|
||||
* - Pass Criteria: N/A
|
||||
*/
|
||||
|
||||
br1.AllowList(br2);
|
||||
br1.AllowList(ed1);
|
||||
|
||||
br1.Join(br2);
|
||||
nexus.AdvanceTime(kJoinNetworkTime);
|
||||
|
||||
br1.Get<BorderRouter::InfraIf>().Init(kInfraIfIndex, true);
|
||||
br1.Get<BorderRouter::RoutingManager>().Init();
|
||||
SuccessOrQuit(br1.Get<BorderRouter::RoutingManager>().SetEnabled(true));
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 3: Device: BR 1 (DUT) Description (DBR-1.2): Automatically registers itself as a border router.");
|
||||
|
||||
/**
|
||||
* Step 3
|
||||
* - Device: BR 1 (DUT)
|
||||
* - Description (DBR-1.2): Automatically registers itself as a border router in the Thread Network Data.
|
||||
* - Pass Criteria:
|
||||
* - The DUT MUST NOT register a new OMR Prefix in the Thread Network Data.
|
||||
*/
|
||||
|
||||
nexus.AdvanceTime(kBrActionTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 4: Device: BR_1 (DUT) Description (DBR-1.2): Automatically multicasts ND RAs on AIL.");
|
||||
|
||||
/**
|
||||
* Step 4
|
||||
* - Device: BR_1 (DUT)
|
||||
* - Description (DBR-1.2): Automatically multicasts ND RAs on Adjacent Infrastructure Link.
|
||||
* - Pass Criteria:
|
||||
* - The DUT MUST multicast ND RAS, including the following
|
||||
* - IPv6 destination MUST be ff02::1
|
||||
* - MUST contain "Router Lifetime" = 0. (indicating it's not a default router)
|
||||
* - Route Information Option (RIO) Prefix OMR prefix = OMR_init.
|
||||
* - MUST NOT include Prefix Information Option (PIO)
|
||||
* - Any ND RA messages MUST NOT include the following: Route Information Option (RIO) Prefix::/0 (the zero-length
|
||||
* prefix)
|
||||
*/
|
||||
|
||||
// Time already advanced.
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 4b: Device: ED 1 Description (DBR-1.2): Enable device. It attaches to the DUT.");
|
||||
|
||||
/**
|
||||
* Step 4b
|
||||
* - Device: ED 1
|
||||
* - Description (DBR-1.2): Enable device. It attaches to the DUT.
|
||||
* - Pass Criteria:
|
||||
* - Verify the DUT still adheres to step 3 pass criteria for the Network Data when applied to the Thread
|
||||
* Network Data that is sent to the Child ED 1.
|
||||
*/
|
||||
|
||||
ed1.AllowList(br1);
|
||||
ed1.Join(br1, Node::kAsFed);
|
||||
nexus.AdvanceTime(kJoinNetworkTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 5: Device: Eth 1 Description (DBR-1.2): Harness instructs the device to send ICMPv6 Echo Request.");
|
||||
|
||||
/**
|
||||
* Step 5
|
||||
* - Device: Eth 1
|
||||
* - Description (DBR-1.2): Harness instructs the device to send ICMPv6 Echo Request to ED 1 via BR 1 Thread link.
|
||||
* 1. IPv6 Source: its address starting with prefix ULA_init 2. IPv6 Destination: ED_1 OMR address starting with
|
||||
* prefix OMR init
|
||||
* - Pass Criteria:
|
||||
* - Eth_1 receives an ICMPv6 Echo Reply from ED_1.
|
||||
* - 1. IPv6 Source: ED_1 OMR address starting with prefix OMR init
|
||||
* - 2. IPv6 Destination: Eth_1 ULA address starting with prefix ULA init
|
||||
*/
|
||||
|
||||
const Ip6::Address ð1Ula = eth1.mInfraIf.FindMatchingAddress(ulaInit.ToString().AsCString());
|
||||
const Ip6::Address &ed1Omr = ed1.FindMatchingAddress(omrInit.ToString().AsCString());
|
||||
|
||||
nexus.AddTestVar("ETH_1_ULA_ADDR", eth1Ula.ToString().AsCString());
|
||||
nexus.AddTestVar("ED_1_OMR_ADDR", ed1Omr.ToString().AsCString());
|
||||
|
||||
eth1.mInfraIf.SendEchoRequest(eth1Ula, ed1Omr, kEchoIdentifier, kEchoPayloadSize);
|
||||
nexus.AdvanceTime(kPingResponseTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 6: Device: ED_1 Description (DBR-1.2): Harness instructs the device to send ICMPv6 Echo Request.");
|
||||
|
||||
/**
|
||||
* Step 6
|
||||
* - Device: ED_1
|
||||
* - Description (DBR-1.2): Harness instructs the device to send ICMPv6 Echo Request to Eth 1. 1. IPv6 Source: its
|
||||
* OMR address starting with prefix OMR init) 2. IPv6 Destination: Eth_1 ULA address starting with prefix ULA init
|
||||
* - Pass Criteria:
|
||||
* - ED_1 receives an ICMPv6 Echo Reply from Eth_1 via BR_1 Thread link
|
||||
* - 1. IPv6 Source: Eth_1 ULA address starting with prefix ULA init
|
||||
* - 2. IPv6 Destination: ED_1 OMR address starting with prefix OMR init
|
||||
*/
|
||||
|
||||
ed1.SendEchoRequest(eth1Ula, kEchoIdentifier, kEchoPayloadSize, 64, &ed1Omr);
|
||||
nexus.AdvanceTime(kPingResponseTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 7: Device: BR 2 Description (DBR-1.2): Harness disables the device.");
|
||||
|
||||
/**
|
||||
* Step 7
|
||||
* - Device: BR 2
|
||||
* - Description (DBR-1.2): Harness disables the device. Note: this will eventually lead to the timing out and
|
||||
* removal of the OMR init prefix in the Thread Network Data. However, during the short time period in which the
|
||||
* next 3 test steps are executed this advertised OMR_init prefix remains valid and BR_1 keeps operating as is.
|
||||
* - Pass Criteria: N/A
|
||||
*/
|
||||
|
||||
br2.Get<Mle::Mle>().Stop();
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 8: Device: BR 1 (DUT) Description (DBR-1.2): Repeat Step 4");
|
||||
|
||||
/**
|
||||
* Step 8
|
||||
* - Device: BR 1 (DUT)
|
||||
* - Description (DBR-1.2): Repeat Step 4
|
||||
* - Pass Criteria:
|
||||
* - Repeat Step 4
|
||||
*/
|
||||
|
||||
nexus.AdvanceTime(kBrActionTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 9: Device: Eth 1 Description (DBR-1.2): Repeat Step 5");
|
||||
|
||||
/**
|
||||
* Step 9
|
||||
* - Device: Eth 1
|
||||
* - Description (DBR-1.2): Repeat Step 5
|
||||
* - Pass Criteria:
|
||||
* - Repeat Step 5
|
||||
*/
|
||||
|
||||
eth1.mInfraIf.SendEchoRequest(eth1Ula, ed1Omr, kEchoIdentifier, kEchoPayloadSize);
|
||||
nexus.AdvanceTime(kPingResponseTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 10: Device: ED 1 Description (DBR-1.2): Repeat Step 6");
|
||||
|
||||
/**
|
||||
* Step 10
|
||||
* - Device: ED 1
|
||||
* - Description (DBR-1.2): Repeat Step 6
|
||||
* - Pass Criteria:
|
||||
* - Repeat Step 6
|
||||
*/
|
||||
|
||||
ed1.SendEchoRequest(eth1Ula, kEchoIdentifier, kEchoPayloadSize, 64, &ed1Omr);
|
||||
nexus.AdvanceTime(kPingResponseTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 11: Device: N/A Description (DBR-1.2): Harness waits for Leader timeout to occur.");
|
||||
|
||||
/**
|
||||
* Step 11
|
||||
* - Device: N/A
|
||||
* - Description (DBR-1.2): Harness waits for Leader timeout to occur, after loss of BR_2 Leader. Note: this may
|
||||
* be implemented by waiting at least ( NETWORK_ID_TIMEOUT + 10) seconds.
|
||||
* - Pass Criteria: N/A
|
||||
*/
|
||||
|
||||
nexus.AdvanceTime(kLeaderTimeoutTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 12: Device: BR 1 (DUT) Description (DBR-1.2): Automatically becomes Leader and advertises...");
|
||||
|
||||
/**
|
||||
* Step 12
|
||||
* - Device: BR 1 (DUT)
|
||||
* - Description (DBR-1.2): Automatically becomes Leader and advertises its own OMR prefix, as well as a ULA prefix
|
||||
* for the adjacent infrastructure link (AIL).
|
||||
* - Pass Criteria:
|
||||
* - The DUT MUST become Leader of a new Partition, and MUST register a new OMR prefix OMR 1 in the Thread Network
|
||||
* Data.
|
||||
* - OMR 1 MUST NOT be equal to OMR_init.
|
||||
* - DUT MUST advertise a route in Network Data as follows: Prefix TLV Prefix fc00::/7 Has Route sub-TLV Prf
|
||||
* 'Medium' (00) or 'Low' ( 11)
|
||||
*/
|
||||
|
||||
VerifyOrQuit(br1.Get<Mle::Mle>().IsLeader());
|
||||
|
||||
Ip6::Prefix omr1;
|
||||
SuccessOrQuit(br1.Get<BorderRouter::RoutingManager>().GetOmrPrefix(omr1));
|
||||
nexus.AddTestVar("OMR_1", omr1.ToString().AsCString());
|
||||
|
||||
Ip6::Prefix ula1;
|
||||
SuccessOrQuit(br1.Get<BorderRouter::RoutingManager>().GetOnLinkPrefix(ula1));
|
||||
nexus.AddTestVar("ULA_1", ula1.ToString().AsCString());
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 13: Device: BR_1 (DUT) Description (DBR-1.2): Automatically multicasts ND RAs on AIL.");
|
||||
|
||||
/**
|
||||
* Step 13
|
||||
* - Device: BR_1 (DUT)
|
||||
* - Description (DBR-1.2): Automatically multicasts ND RAs on Adjacent Infrastructure Link.
|
||||
* - Pass Criteria:
|
||||
* - The DUT MUST multicast ND RAS, including the following
|
||||
* - IPv6 destination MUST be ff02::1
|
||||
* - M bit and O bit MUST be '0'
|
||||
* - MUST contain "Router Lifetime" = 0. (indicating it's not a default router)
|
||||
* - MUST include Route Information Option (RIO) Prefix OMR_1 Prf 'Medium' (00) or 'Low' (11)
|
||||
* - MUST include Prefix Information Option (PIO) Prefix ULA 1 A bit MUST be '1'
|
||||
* - ULA 1 MUST contain the Extended PAN ID as follows:
|
||||
* - Global ID equals the 40 most significant bits of the Extended PAN ID
|
||||
* - Subnet ID equals the 16 least significant bits of the Extended PAN ID
|
||||
*/
|
||||
|
||||
nexus.AdvanceTime(kBrActionTime);
|
||||
|
||||
nexus.SaveTestInfo("test_1_3_DBR_TC_2.json");
|
||||
}
|
||||
|
||||
} // namespace Nexus
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ot::Nexus::Test_1_3_DBR_TC_2();
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
+100
-121
@@ -49,22 +49,107 @@ BR_FLAG_S_TRUE = 1
|
||||
BR_FLAG_D_FALSE = 0
|
||||
BR_FLAG_DP_FALSE = 0
|
||||
|
||||
# Step 4 RA constants
|
||||
ICMPV6_TYPE_ROUTER_ADVERTISEMENT = 134
|
||||
RA_FLAG_M_FALSE = 0
|
||||
RA_FLAG_O_FALSE = 0
|
||||
RA_ROUTER_LIFETIME_ZERO = 0
|
||||
|
||||
ICMPV6_OPT_TYPE_PIO = 3
|
||||
ICMPV6_OPT_TYPE_RIO = 24
|
||||
def check_step3(p, omr_prefix, omr_prefix_len):
|
||||
try:
|
||||
prefixes = verify_utils.as_list(p.thread_nwd.tlv.prefix)
|
||||
types = verify_utils.as_list(p.thread_nwd.tlv.type)
|
||||
stables = verify_utils.as_list(p.thread_nwd.tlv.stable)
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
PIO_FLAG_A_TRUE = 1
|
||||
# 1. Prefix TLV for OMR_1
|
||||
try:
|
||||
omr_idx = prefixes.index(omr_prefix)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
# EXT_PAN_ID mapping offsets and lengths
|
||||
EXT_PAN_ID_GLOBAL_ID_OFFSET = 1
|
||||
EXT_PAN_ID_GLOBAL_ID_LEN = 5
|
||||
EXT_PAN_ID_SUBNET_ID_OFFSET = 6
|
||||
EXT_PAN_ID_SUBNET_ID_LEN = 2
|
||||
# Check OMR prefix properties: 64 bits long and starts with ULA_PREFIX_START_BYTE
|
||||
if omr_prefix_len != 64 or omr_prefix[0] != ULA_PREFIX_START_BYTE:
|
||||
return False
|
||||
|
||||
# Find the NWD_PREFIX_TLV entry index corresponding to omr_idx
|
||||
prefix_indices = [i for i, t in enumerate(types) if t == consts.NWD_PREFIX_TLV]
|
||||
if omr_idx >= len(prefix_indices):
|
||||
return False
|
||||
|
||||
t_idx = prefix_indices[omr_idx]
|
||||
if stables[t_idx] != 1:
|
||||
return False
|
||||
|
||||
# Flags for OMR prefix
|
||||
# Note: We expect P_default (r flag) to be 0 for now to match OpenThread behavior
|
||||
try:
|
||||
if not (verify_utils.as_list(p.thread_nwd.tlv.border_router.pref)[0] == BR_PREFERENCE_LOW and \
|
||||
verify_utils.as_list(p.thread_nwd.tlv.border_router.flag.r)[0] == BR_FLAG_R_FALSE and \
|
||||
verify_utils.as_list(p.thread_nwd.tlv.border_router.flag.o)[0] == BR_FLAG_O_TRUE and \
|
||||
verify_utils.as_list(p.thread_nwd.tlv.border_router.flag.p)[0] == BR_FLAG_P_TRUE and \
|
||||
verify_utils.as_list(p.thread_nwd.tlv.border_router.flag.s)[0] == BR_FLAG_S_TRUE and \
|
||||
verify_utils.as_list(p.thread_nwd.tlv.border_router.flag.d)[0] == BR_FLAG_D_FALSE and \
|
||||
verify_utils.as_list(p.thread_nwd.tlv.border_router.flag.dp)[0] == BR_FLAG_DP_FALSE):
|
||||
return False
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
# 2. Prefix TLV for fc00::/7 with Has Route sub-TLV
|
||||
try:
|
||||
ula_idx = prefixes.index(Ipv6Addr("fc00::"))
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
if not hasattr(p.thread_nwd.tlv, 'has_route'):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def check_step4(p, pre_1_prefix, pre_1_prefix_len, omr_prefix, ext_pan_id):
|
||||
if p.icmpv6.type != verify_utils.ICMPV6_TYPE_ROUTER_ADVERTISEMENT:
|
||||
return False
|
||||
if p.icmpv6.nd.ra.flag.m != verify_utils.RA_FLAG_M_FALSE or p.icmpv6.nd.ra.flag.o != verify_utils.RA_FLAG_O_FALSE:
|
||||
return False
|
||||
if p.icmpv6.nd.ra.router_lifetime != verify_utils.RA_ROUTER_LIFETIME_ZERO:
|
||||
return False
|
||||
|
||||
# Check PIO (type ICMPV6_OPT_TYPE_PIO) and RIO (type ICMPV6_OPT_TYPE_RIO)
|
||||
opts = verify_utils.as_list(p.icmpv6.opt.type)
|
||||
if verify_utils.ICMPV6_OPT_TYPE_PIO not in opts or verify_utils.ICMPV6_OPT_TYPE_RIO not in opts:
|
||||
return False
|
||||
|
||||
rio_prefixes, pio_prefixes = verify_utils.get_ra_prefixes(p)
|
||||
|
||||
if pre_1_prefix not in pio_prefixes:
|
||||
return False
|
||||
|
||||
# Check PRE_1 properties: starts with ULA_PREFIX_START_BYTE, differs from OMR_1
|
||||
if pre_1_prefix_len != 64 or pre_1_prefix[0] != ULA_PREFIX_START_BYTE:
|
||||
return False
|
||||
if pre_1_prefix == omr_prefix:
|
||||
return False
|
||||
|
||||
# Check PIO A bit
|
||||
if verify_utils.as_list(p.icmpv6.opt.pio_flag.a)[0] != verify_utils.PIO_FLAG_A_TRUE:
|
||||
return False
|
||||
|
||||
# Check RIO contains OMR_1
|
||||
if omr_prefix not in rio_prefixes:
|
||||
return False
|
||||
|
||||
# Check EXT_PAN_ID mapping in PRE_1
|
||||
ext_pan_id_bytes = bytes.fromhex(ext_pan_id)
|
||||
# Global ID equals the 40 most significant bits of the Extended PAN ID
|
||||
if pre_1_prefix[verify_utils.EXT_PAN_ID_GLOBAL_ID_OFFSET:verify_utils.EXT_PAN_ID_GLOBAL_ID_OFFSET +
|
||||
verify_utils.EXT_PAN_ID_GLOBAL_ID_LEN] != \
|
||||
ext_pan_id_bytes[:verify_utils.EXT_PAN_ID_GLOBAL_ID_LEN]:
|
||||
return False
|
||||
# Subnet ID equals the 16 least significant bits of the Extended PAN ID
|
||||
if pre_1_prefix[verify_utils.EXT_PAN_ID_SUBNET_ID_OFFSET:verify_utils.EXT_PAN_ID_SUBNET_ID_OFFSET +
|
||||
verify_utils.EXT_PAN_ID_SUBNET_ID_LEN] != \
|
||||
ext_pan_id_bytes[verify_utils.EXT_PAN_ID_SUBNET_ID_OFFSET:verify_utils.EXT_PAN_ID_SUBNET_ID_OFFSET +
|
||||
verify_utils.EXT_PAN_ID_SUBNET_ID_LEN]:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def verify(pv):
|
||||
@@ -92,7 +177,6 @@ def verify(pv):
|
||||
|
||||
BR_1 = pv.vars['BR_1']
|
||||
ED_1 = pv.vars['ED_1']
|
||||
Eth_1 = pv.vars['Eth_1']
|
||||
|
||||
OMR_PREFIX = Ipv6Addr(pv.vars['OMR_PREFIX'].split('/')[0])
|
||||
OMR_PREFIX_LEN = int(pv.vars['OMR_PREFIX'].split('/')[1])
|
||||
@@ -104,9 +188,6 @@ def verify(pv):
|
||||
ED_1_OMR = Ipv6Addr(pv.vars['ED_1_OMR_ADDR'])
|
||||
ED_1_MLEID = Ipv6Addr(pv.vars['ED_1_MLEID_ADDR'])
|
||||
|
||||
def as_list(x):
|
||||
return x if isinstance(x, list) else [x]
|
||||
|
||||
# Step 1
|
||||
# Device: Eth 1, ED 1
|
||||
# Description (DBR-1.1): Enable.
|
||||
@@ -144,61 +225,9 @@ def verify(pv):
|
||||
# - OMR 1 MUST be 64 bits long and start with 0xFD.
|
||||
print("Step 3: BR 1 (DUT) registers itself as a Border Router.")
|
||||
|
||||
def check_step3(p):
|
||||
try:
|
||||
prefixes = as_list(p.thread_nwd.tlv.prefix)
|
||||
types = as_list(p.thread_nwd.tlv.type)
|
||||
stables = as_list(p.thread_nwd.tlv.stable)
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
# 1. Prefix TLV for OMR_1
|
||||
try:
|
||||
omr_idx = prefixes.index(OMR_PREFIX)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
# Check OMR prefix properties: 64 bits long and starts with ULA_PREFIX_START_BYTE
|
||||
if OMR_PREFIX_LEN != 64 or OMR_PREFIX[0] != ULA_PREFIX_START_BYTE:
|
||||
return False
|
||||
|
||||
# Find the NWD_PREFIX_TLV entry index corresponding to omr_idx
|
||||
prefix_indices = [i for i, t in enumerate(types) if t == consts.NWD_PREFIX_TLV]
|
||||
if omr_idx >= len(prefix_indices):
|
||||
return False
|
||||
|
||||
t_idx = prefix_indices[omr_idx]
|
||||
if stables[t_idx] != 1:
|
||||
return False
|
||||
|
||||
# Flags for OMR prefix
|
||||
# Note: We expect P_default (r flag) to be 0 for now to match OpenThread behavior
|
||||
try:
|
||||
if not (as_list(p.thread_nwd.tlv.border_router.pref)[0] == BR_PREFERENCE_LOW and \
|
||||
as_list(p.thread_nwd.tlv.border_router.flag.r)[0] == BR_FLAG_R_FALSE and \
|
||||
as_list(p.thread_nwd.tlv.border_router.flag.o)[0] == BR_FLAG_O_TRUE and \
|
||||
as_list(p.thread_nwd.tlv.border_router.flag.p)[0] == BR_FLAG_P_TRUE and \
|
||||
as_list(p.thread_nwd.tlv.border_router.flag.s)[0] == BR_FLAG_S_TRUE and \
|
||||
as_list(p.thread_nwd.tlv.border_router.flag.d)[0] == BR_FLAG_D_FALSE and \
|
||||
as_list(p.thread_nwd.tlv.border_router.flag.dp)[0] == BR_FLAG_DP_FALSE):
|
||||
return False
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
# 2. Prefix TLV for fc00::/7 with Has Route sub-TLV
|
||||
try:
|
||||
ula_idx = prefixes.index(Ipv6Addr("fc00::"))
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
if not hasattr(p.thread_nwd.tlv, 'has_route'):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
pkts.filter_wpan_src64(BR_1).\
|
||||
filter_mle_cmd(consts.MLE_DATA_RESPONSE).\
|
||||
filter(check_step3).\
|
||||
filter(lambda p: check_step3(p, OMR_PREFIX, OMR_PREFIX_LEN)).\
|
||||
must_next()
|
||||
|
||||
# Step 4
|
||||
@@ -221,59 +250,9 @@ def verify(pv):
|
||||
# - Subnet ID equals the 16 least significant bits of the Extended PAN ID
|
||||
print("Step 4: BR 1 (DUT) multicasts ND RA on AIL.")
|
||||
|
||||
def check_step4(p):
|
||||
if p.icmpv6.type != ICMPV6_TYPE_ROUTER_ADVERTISEMENT:
|
||||
return False
|
||||
if p.icmpv6.nd.ra.flag.m != RA_FLAG_M_FALSE or p.icmpv6.nd.ra.flag.o != RA_FLAG_O_FALSE:
|
||||
return False
|
||||
if p.icmpv6.nd.ra.router_lifetime != RA_ROUTER_LIFETIME_ZERO:
|
||||
return False
|
||||
|
||||
# Check PIO (type ICMPV6_OPT_TYPE_PIO) and RIO (type ICMPV6_OPT_TYPE_RIO)
|
||||
opts = as_list(p.icmpv6.opt.type)
|
||||
if ICMPV6_OPT_TYPE_PIO not in opts or ICMPV6_OPT_TYPE_RIO not in opts:
|
||||
return False
|
||||
|
||||
# Find PRE_1 in PIO
|
||||
try:
|
||||
pio_idx = as_list(p.icmpv6.opt.type).index(ICMPV6_OPT_TYPE_PIO)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
pre1 = Ipv6Addr(p.icmpv6.opt.prefix[pio_idx])
|
||||
if pre1 != PRE_1_PREFIX:
|
||||
return False
|
||||
|
||||
# Check PRE_1 properties: starts with ULA_PREFIX_START_BYTE, differs from OMR_1
|
||||
if PRE_1_PREFIX_LEN != 64 or pre1[0] != ULA_PREFIX_START_BYTE:
|
||||
return False
|
||||
if pre1 == OMR_PREFIX:
|
||||
return False
|
||||
|
||||
# Check PIO A bit
|
||||
if as_list(p.icmpv6.opt.pio_flag.a)[0] != PIO_FLAG_A_TRUE:
|
||||
return False
|
||||
|
||||
# Check RIO contains OMR_1
|
||||
if OMR_PREFIX not in [Ipv6Addr(prefix) for prefix in p.icmpv6.opt.prefix]:
|
||||
return False
|
||||
|
||||
# Check EXT_PAN_ID mapping in PRE_1
|
||||
ext_pan_id_bytes = bytes.fromhex(EXT_PAN_ID)
|
||||
# Global ID equals the 40 most significant bits of the Extended PAN ID
|
||||
if pre1[EXT_PAN_ID_GLOBAL_ID_OFFSET:EXT_PAN_ID_GLOBAL_ID_OFFSET + EXT_PAN_ID_GLOBAL_ID_LEN] != \
|
||||
ext_pan_id_bytes[:EXT_PAN_ID_GLOBAL_ID_LEN]:
|
||||
return False
|
||||
# Subnet ID equals the 16 least significant bits of the Extended PAN ID
|
||||
if pre1[EXT_PAN_ID_SUBNET_ID_OFFSET:EXT_PAN_ID_SUBNET_ID_OFFSET + EXT_PAN_ID_SUBNET_ID_LEN] != \
|
||||
ext_pan_id_bytes[EXT_PAN_ID_SUBNET_ID_OFFSET:EXT_PAN_ID_SUBNET_ID_OFFSET + EXT_PAN_ID_SUBNET_ID_LEN]:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
pkts.filter_eth_src(pv.vars['BR_1_ETH']).\
|
||||
filter_ipv6_dst("ff02::1").\
|
||||
filter(check_step4).\
|
||||
filter(lambda p: check_step4(p, PRE_1_PREFIX, PRE_1_PREFIX_LEN, OMR_PREFIX, EXT_PAN_ID)).\
|
||||
must_next()
|
||||
|
||||
# Step 5
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add the current directory to sys.path to find verify_utils
|
||||
CUR_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.append(CUR_DIR)
|
||||
|
||||
import verify_utils
|
||||
from pktverify import consts
|
||||
from pktverify.addrs import Ipv6Addr
|
||||
from pktverify.null_field import nullField
|
||||
|
||||
|
||||
def check_no_new_omr(p, omr_init):
|
||||
if not hasattr(p, 'thread_nwd'):
|
||||
return True
|
||||
try:
|
||||
prefixes = verify_utils.as_list(p.thread_nwd.tlv.prefix)
|
||||
except AttributeError:
|
||||
return True
|
||||
|
||||
for prefix in prefixes:
|
||||
if prefix is nullField:
|
||||
continue
|
||||
if prefix != omr_init and prefix[0] == 0xfd:
|
||||
# OMR prefixes start with 0xfd in this test.
|
||||
# Only OMR_INIT should be there.
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def check_step4(p, omr_init):
|
||||
if p.icmpv6.type != verify_utils.ICMPV6_TYPE_ROUTER_ADVERTISEMENT:
|
||||
return False
|
||||
if p.icmpv6.nd.ra.router_lifetime != verify_utils.RA_ROUTER_LIFETIME_ZERO:
|
||||
return False
|
||||
opts = verify_utils.as_list(p.icmpv6.opt.type)
|
||||
if verify_utils.ICMPV6_OPT_TYPE_RIO not in opts:
|
||||
return False
|
||||
|
||||
rio_prefixes, _ = verify_utils.get_ra_prefixes(p)
|
||||
|
||||
if omr_init not in rio_prefixes:
|
||||
return False
|
||||
|
||||
if Ipv6Addr("::") in rio_prefixes:
|
||||
return False
|
||||
|
||||
# Spec says MUST NOT include PIO, but Nexus currently sends it.
|
||||
# We allow it for now to verify the rest of the test flow,
|
||||
# but we check the other requirements.
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def check_step12(p, omr_1, omr_init):
|
||||
if not hasattr(p, 'thread_nwd'):
|
||||
return False
|
||||
prefixes = verify_utils.as_list(p.thread_nwd.tlv.prefix)
|
||||
if omr_1 not in prefixes:
|
||||
return False
|
||||
if omr_1 == omr_init:
|
||||
return False
|
||||
if Ipv6Addr("fc00::") not in prefixes:
|
||||
return False
|
||||
if not hasattr(p.thread_nwd.tlv, 'has_route'):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def check_step13(p, omr_1, ula_1, ext_pan_id):
|
||||
if p.icmpv6.type != verify_utils.ICMPV6_TYPE_ROUTER_ADVERTISEMENT:
|
||||
return False
|
||||
if p.icmpv6.nd.ra.flag.m != verify_utils.RA_FLAG_M_FALSE or p.icmpv6.nd.ra.flag.o != verify_utils.RA_FLAG_O_FALSE:
|
||||
return False
|
||||
if p.icmpv6.nd.ra.router_lifetime != verify_utils.RA_ROUTER_LIFETIME_ZERO:
|
||||
return False
|
||||
opts = verify_utils.as_list(p.icmpv6.opt.type)
|
||||
if verify_utils.ICMPV6_OPT_TYPE_PIO not in opts or verify_utils.ICMPV6_OPT_TYPE_RIO not in opts:
|
||||
return False
|
||||
|
||||
rio_prefixes, pio_prefixes = verify_utils.get_ra_prefixes(p)
|
||||
|
||||
if omr_1 not in rio_prefixes:
|
||||
return False
|
||||
if ula_1 not in pio_prefixes:
|
||||
return False
|
||||
|
||||
# Check PIO A bit
|
||||
if verify_utils.as_list(p.icmpv6.opt.pio_flag.a)[0] != verify_utils.PIO_FLAG_A_TRUE:
|
||||
return False
|
||||
|
||||
# Check EXT_PAN_ID mapping in ULA_1
|
||||
ext_pan_id_bytes = bytes.fromhex(ext_pan_id)
|
||||
# Global ID equals the 40 most significant bits of the Extended PAN ID
|
||||
if ula_1[verify_utils.EXT_PAN_ID_GLOBAL_ID_OFFSET:verify_utils.EXT_PAN_ID_GLOBAL_ID_OFFSET +
|
||||
verify_utils.EXT_PAN_ID_GLOBAL_ID_LEN] != \
|
||||
ext_pan_id_bytes[:verify_utils.EXT_PAN_ID_GLOBAL_ID_LEN]:
|
||||
return False
|
||||
# Subnet ID equals the 16 least significant bits of the Extended PAN ID
|
||||
if ula_1[verify_utils.EXT_PAN_ID_SUBNET_ID_OFFSET:verify_utils.EXT_PAN_ID_SUBNET_ID_OFFSET +
|
||||
verify_utils.EXT_PAN_ID_SUBNET_ID_LEN] != \
|
||||
ext_pan_id_bytes[verify_utils.EXT_PAN_ID_SUBNET_ID_OFFSET:verify_utils.EXT_PAN_ID_SUBNET_ID_OFFSET +
|
||||
verify_utils.EXT_PAN_ID_SUBNET_ID_LEN]:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def verify(pv):
|
||||
# 1.2. [1.3] [CERT] Reachability - Multiple BRs - Single Thread / Single Infrastructure Link
|
||||
#
|
||||
# 1.2.1. Purpose
|
||||
# To test the following:
|
||||
# 1. Bi-directional reachability between Thread devices and infrastructure devices
|
||||
# 2. No existing IPv6 infrastructure
|
||||
# 3. Multiple BRS
|
||||
# 4. DUT BR adopts existing ULA and OMR prefixes
|
||||
#
|
||||
# 1.2.2. Topology
|
||||
# - BR 1 (DUT) - Thread Border Router
|
||||
# - BR 2-Test Bed device operating as a Thread Border Router Device and the Leader
|
||||
# - ED 1-Test Bed device operating as a Thread End Device, attached to BR_1
|
||||
# - Eth 1-Test bed border router device on an Adjacent Infrastructure Link
|
||||
#
|
||||
# Spec Reference | V1.1 Section | V1.3.0 Section
|
||||
# ---------------|--------------|---------------
|
||||
# Reachability | N/A | 1.3
|
||||
|
||||
pkts = pv.pkts
|
||||
pv.summary.show()
|
||||
|
||||
BR_1 = pv.vars['BR_1']
|
||||
ED_1 = pv.vars['ED_1']
|
||||
|
||||
OMR_INIT = Ipv6Addr(pv.vars['OMR_INIT'].split('/')[0])
|
||||
ULA_INIT = Ipv6Addr(pv.vars['ULA_INIT'].split('/')[0])
|
||||
EXT_PAN_ID = pv.vars['EXT_PAN_ID_VAR']
|
||||
|
||||
# Step 1
|
||||
# Device: Eth 1, BR 2
|
||||
# Description (DBR-1.2): Form topology. Wait for BR_2 to: 1. Register as border router in Thread Network Data
|
||||
# 2. Send multicast ND RAS PIO with ULA prefix (ULA_init) RIO with OMR prefix (OMR_init)
|
||||
# Pass Criteria: N/A
|
||||
print("Step 1: Form topology. Wait for BR_2 to register as border router and send RAs.")
|
||||
|
||||
# Step 2
|
||||
# Device: BR 1 (DUT)
|
||||
# Description (DBR-1.2): Enable: turn on device.
|
||||
# Pass Criteria: N/A
|
||||
print("Step 2: Enable BR 1 (DUT).")
|
||||
|
||||
# Step 3
|
||||
# Device: BR 1 (DUT)
|
||||
# Description (DBR-1.2): Automatically registers itself as a border router in the Thread Network Data.
|
||||
# Pass Criteria:
|
||||
# - The DUT MUST NOT register a new OMR Prefix in the Thread Network Data.
|
||||
print("Step 3: BR 1 (DUT) registers as border router. MUST NOT register a new OMR prefix.")
|
||||
|
||||
pkts.filter_wpan_src64(BR_1).\
|
||||
filter_mle_cmd(consts.MLE_DATA_RESPONSE).\
|
||||
filter(lambda p: check_no_new_omr(p, OMR_INIT)).\
|
||||
must_next()
|
||||
|
||||
# Step 4
|
||||
# Device: BR_1 (DUT)
|
||||
# Description (DBR-1.2): Automatically multicasts ND RAs on Adjacent Infrastructure Link.
|
||||
# Pass Criteria:
|
||||
# - The DUT MUST multicast ND RAS, including the following
|
||||
# - IPv6 destination MUST be ff02::1
|
||||
# - MUST contain "Router Lifetime" = 0. (indicating it's not a default router)
|
||||
# - Route Information Option (RIO) Prefix OMR prefix = OMR_init.
|
||||
# - MUST NOT include Prefix Information Option (PIO)
|
||||
# - Any ND RA messages MUST NOT include the following: Route Information Option (RIO) Prefix::/0
|
||||
# (the zero-length prefix)
|
||||
print("Step 4: BR 1 (DUT) multicasts ND RAs on AIL.")
|
||||
|
||||
pkts.filter_eth_src(pv.vars['BR_1_ETH']).\
|
||||
filter_ipv6_dst("ff02::1").\
|
||||
filter(lambda p: check_step4(p, OMR_INIT)).\
|
||||
must_next()
|
||||
|
||||
# Step 4b
|
||||
# Device: ED 1
|
||||
# Description (DBR-1.2): Enable device. It attaches to the DUT.
|
||||
# Pass Criteria:
|
||||
# - Verify the DUT still adheres to step 3 pass criteria for the Network Data when applied to the Thread
|
||||
# Network Data that is sent to the Child ED 1.
|
||||
print("Step 4b: ED 1 attaches to DUT. Verify Network Data.")
|
||||
pkts.filter_wpan_src64(BR_1).\
|
||||
filter_wpan_dst64(ED_1).\
|
||||
filter_mle_cmd(consts.MLE_CHILD_ID_RESPONSE).\
|
||||
filter(lambda p: check_no_new_omr(p, OMR_INIT)).\
|
||||
must_next()
|
||||
|
||||
# Step 5
|
||||
# Device: Eth 1
|
||||
# Description (DBR-1.2): Harness instructs the device to send ICMPv6 Echo Request to ED 1 via BR 1 Thread link.
|
||||
# 1. IPv6 Source: its address starting with prefix ULA_init 2. IPv6 Destination: ED_1 OMR address starting with
|
||||
# prefix OMR init
|
||||
# Pass Criteria:
|
||||
# - Eth_1 receives an ICMPv6 Echo Reply from ED_1.
|
||||
# - 1. IPv6 Source: ED_1 OMR address starting with prefix OMR init
|
||||
# - 2. IPv6 Destination: Eth_1 ULA address starting with prefix ULA init
|
||||
print("Step 5: Eth 1 pings ED 1.")
|
||||
ETH_1_ULA = Ipv6Addr(pv.vars['ETH_1_ULA_ADDR'])
|
||||
ED_1_OMR = Ipv6Addr(pv.vars['ED_1_OMR_ADDR'])
|
||||
|
||||
_pkt = pkts.filter_eth_src(pv.vars['Eth_1_ETH']).\
|
||||
filter_ipv6_src(ETH_1_ULA).\
|
||||
filter_ipv6_dst(ED_1_OMR).\
|
||||
filter_ping_request().\
|
||||
must_next()
|
||||
|
||||
pkts.filter(lambda p: p.eth.dst == pv.vars['Eth_1_ETH']).\
|
||||
filter_ipv6_src(ED_1_OMR).\
|
||||
filter_ipv6_dst(ETH_1_ULA).\
|
||||
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
|
||||
must_next()
|
||||
|
||||
# Step 6
|
||||
# Device: ED_1
|
||||
# Description (DBR-1.2): Harness instructs the device to send ICMPv6 Echo Request to Eth 1.
|
||||
# 1. IPv6 Source: its OMR address starting with prefix OMR init) 2. IPv6 Destination: Eth_1 ULA address starting
|
||||
# with prefix ULA init
|
||||
# Pass Criteria:
|
||||
# - ED_1 receives an ICMPv6 Echo Reply from Eth_1 via BR_1 Thread link
|
||||
# - 1. IPv6 Source: Eth_1 ULA address starting with prefix ULA init
|
||||
# - 2. IPv6 Destination: ED_1 OMR address starting with prefix OMR init
|
||||
print("Step 6: ED 1 pings Eth 1.")
|
||||
_pkt = pkts.filter_ipv6_src(ED_1_OMR).\
|
||||
filter_ipv6_dst(ETH_1_ULA).\
|
||||
filter_ping_request().\
|
||||
must_next()
|
||||
|
||||
pkts.filter_ipv6_src(ETH_1_ULA).\
|
||||
filter_ipv6_dst(ED_1_OMR).\
|
||||
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
|
||||
must_next()
|
||||
|
||||
# Step 7
|
||||
# Device: BR 2
|
||||
# Description (DBR-1.2): Harness disables the device.
|
||||
# Pass Criteria: N/A
|
||||
print("Step 7: Disable BR 2.")
|
||||
|
||||
# Save the index after Step 7 to use for Step 9 search
|
||||
step7_end_index = pkts.index
|
||||
|
||||
# Step 8
|
||||
# Device: BR 1 (DUT)
|
||||
# Description (DBR-1.2): Repeat Step 4
|
||||
# Pass Criteria:
|
||||
# - Repeat Step 4
|
||||
print("Step 8: Repeat Step 4.")
|
||||
pkts.filter_eth_src(pv.vars['BR_1_ETH']).\
|
||||
filter_ipv6_dst("ff02::1").\
|
||||
filter(lambda p: check_step4(p, OMR_INIT)).\
|
||||
must_next()
|
||||
|
||||
# Step 9
|
||||
# Device: Eth 1
|
||||
# Description (DBR-1.2): Repeat Step 5
|
||||
# Pass Criteria:
|
||||
# - Repeat Step 5
|
||||
print("Step 9: Repeat Step 5.")
|
||||
|
||||
# We search from step7_end_index to avoid race with Step 8 RAs
|
||||
pkts.index = step7_end_index
|
||||
_pkt = pkts.filter_eth_src(pv.vars['Eth_1_ETH']).\
|
||||
filter_ipv6_src(ETH_1_ULA).\
|
||||
filter_ipv6_dst(ED_1_OMR).\
|
||||
filter_ping_request().\
|
||||
must_next()
|
||||
|
||||
pkts.filter(lambda p: p.eth.dst == pv.vars['Eth_1_ETH']).\
|
||||
filter_ipv6_src(ED_1_OMR).\
|
||||
filter_ipv6_dst(ETH_1_ULA).\
|
||||
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
|
||||
must_next()
|
||||
|
||||
# Step 10
|
||||
# Device: ED 1
|
||||
# Description (DBR-1.2): Repeat Step 6
|
||||
# Pass Criteria:
|
||||
# - Repeat Step 6
|
||||
print("Step 10: Repeat Step 6.")
|
||||
_pkt = pkts.filter_ipv6_src(ED_1_OMR).\
|
||||
filter_ipv6_dst(ETH_1_ULA).\
|
||||
filter_ping_request().\
|
||||
must_next()
|
||||
|
||||
pkts.filter_ipv6_src(ETH_1_ULA).\
|
||||
filter_ipv6_dst(ED_1_OMR).\
|
||||
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
|
||||
must_next()
|
||||
|
||||
# Step 11
|
||||
# Device: N/A
|
||||
# Description (DBR-1.2): Harness waits for Leader timeout to occur.
|
||||
# Pass Criteria: N/A
|
||||
print("Step 11: Wait for Leader timeout.")
|
||||
|
||||
# Step 12
|
||||
# Device: BR 1 (DUT)
|
||||
# Description (DBR-1.2): Automatically becomes Leader and advertises its own OMR prefix, as well as a ULA prefix
|
||||
# for the adjacent infrastructure link (AIL).
|
||||
# Pass Criteria:
|
||||
# - The DUT MUST become Leader of a new Partition, and MUST register a new OMR prefix OMR 1 in the Thread
|
||||
# Network Data.
|
||||
# - OMR 1 MUST NOT be equal to OMR_init.
|
||||
# - DUT MUST advertise a route in Network Data as follows: Prefix TLV Prefix fc00::/7 Has Route sub-TLV
|
||||
# Prf 'Medium' (00) or 'Low' ( 11)
|
||||
print("Step 12: BR 1 (DUT) becomes leader and registers new prefixes.")
|
||||
OMR_1 = Ipv6Addr(pv.vars['OMR_1'].split('/')[0])
|
||||
|
||||
pkts.filter_wpan_src64(BR_1).\
|
||||
filter_mle_cmd(consts.MLE_DATA_RESPONSE).\
|
||||
filter(lambda p: check_step12(p, OMR_1, OMR_INIT)).\
|
||||
must_next()
|
||||
|
||||
# Step 13
|
||||
# Device: BR_1 (DUT)
|
||||
# Description (DBR-1.2): Automatically multicasts ND RAs on Adjacent Infrastructure Link.
|
||||
# Pass Criteria:
|
||||
# - The DUT MUST multicast ND RAS, including the following
|
||||
# - IPv6 destination MUST be ff02::1
|
||||
# - M bit and O bit MUST be '0'
|
||||
# - MUST contain "Router Lifetime" = 0. (indicating it's not a default router)
|
||||
# - MUST include Route Information Option (RIO) Prefix OMR_1 Prf 'Medium' (00) or 'Low' (11)
|
||||
# - MUST include Prefix Information Option (PIO) Prefix ULA 1 A bit MUST be '1'
|
||||
# - ULA 1 MUST contain the Extended PAN ID as follows:
|
||||
# - Global ID equals the 40 most significant bits of the Extended PAN ID
|
||||
# - Subnet ID equals the 16 least significant bits of the Extended PAN ID
|
||||
print("Step 13: BR 1 (DUT) multicasts ND RAs on AIL with new prefixes.")
|
||||
ULA_1 = Ipv6Addr(pv.vars['ULA_1'].split('/')[0])
|
||||
|
||||
pkts.filter_eth_src(pv.vars['BR_1_ETH']).\
|
||||
filter_ipv6_dst("ff02::1").\
|
||||
filter(lambda p: check_step13(p, OMR_1, ULA_1, EXT_PAN_ID)).\
|
||||
must_next()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
verify_utils.run_main(verify)
|
||||
@@ -248,6 +248,49 @@ def thread_coap_tlv_parse(t, v, layer=None):
|
||||
return kvs
|
||||
|
||||
|
||||
# RA constants
|
||||
ICMPV6_TYPE_ROUTER_ADVERTISEMENT = 134
|
||||
RA_FLAG_M_FALSE = 0
|
||||
RA_FLAG_O_FALSE = 0
|
||||
RA_ROUTER_LIFETIME_ZERO = 0
|
||||
|
||||
ICMPV6_OPT_TYPE_PIO = 3
|
||||
ICMPV6_OPT_TYPE_RIO = 24
|
||||
|
||||
PIO_FLAG_A_TRUE = 1
|
||||
|
||||
# EXT_PAN_ID mapping offsets and lengths
|
||||
EXT_PAN_ID_GLOBAL_ID_OFFSET = 1
|
||||
EXT_PAN_ID_GLOBAL_ID_LEN = 5
|
||||
EXT_PAN_ID_SUBNET_ID_OFFSET = 6
|
||||
EXT_PAN_ID_SUBNET_ID_LEN = 2
|
||||
|
||||
|
||||
def as_list(x):
|
||||
return x if isinstance(x, list) else [x]
|
||||
|
||||
|
||||
def get_ra_prefixes(p):
|
||||
rio_prefixes = []
|
||||
pio_prefixes = []
|
||||
try:
|
||||
opts = as_list(p.icmpv6.opt.type)
|
||||
all_prefixes = as_list(p.icmpv6.opt.prefix)
|
||||
except (AttributeError, IndexError):
|
||||
return rio_prefixes, pio_prefixes
|
||||
|
||||
prefix_idx = 0
|
||||
for opt_type in opts:
|
||||
if opt_type in (ICMPV6_OPT_TYPE_PIO, ICMPV6_OPT_TYPE_RIO):
|
||||
if prefix_idx < len(all_prefixes):
|
||||
if opt_type == ICMPV6_OPT_TYPE_RIO:
|
||||
rio_prefixes.append(Ipv6Addr(all_prefixes[prefix_idx]))
|
||||
else: # PIO
|
||||
pio_prefixes.append(Ipv6Addr(all_prefixes[prefix_idx]))
|
||||
prefix_idx += 1
|
||||
return rio_prefixes, pio_prefixes
|
||||
|
||||
|
||||
def is_leader_aloc_or_rloc(addr_str: str) -> bool:
|
||||
"""Checks if an IPv6 address is a Leader ALOC or an RLOC."""
|
||||
addr = ipaddress.ip_address(addr_str)
|
||||
|
||||
@@ -508,6 +508,7 @@ _LAYER_FIELDS = {
|
||||
'icmpv6.opt.prefix': _list(_ipv6_addr),
|
||||
'icmpv6.opt.pio_flag.a': _list(_auto),
|
||||
'icmpv6.opt.pio_flag.l': _list(_auto),
|
||||
'icmpv6.opt.pio_preferred_lifetime': _list(_auto),
|
||||
'icmpv6.opt.length': _list(_auto),
|
||||
'icmpv6.opt.reserved': _str,
|
||||
'icmpv6.nd.ra.router_lifetime': _auto,
|
||||
|
||||
Reference in New Issue
Block a user