[nexus] add DBR-TC-8 test for bi-directional reachability (#12751)

This commit introduces the 1_3_DBR_TC_8 Nexus test case, which
verifies bi-directional reachability in a topology with multiple
Border Routers (BRs) and the presence of OMR prefixes with different
lifetimes.

Key features of this test:
- Simulates a network with two BRs and a Thread Router.
- Configures an infrastructure link with a GUA prefix.
- Configures an OMR prefix (OMR_4, P_preferred=false) in Network
  Data and ensures the DUT BR correctly generates its own OMR
  prefix when existing ones are not usable.
- Verifies that the DUT BR correctly multicasts Router
  Advertisements (RAs) on the infrastructure link containing OMR
  routes but excluding deprecated OMR_4 routes.
- Confirms bi-directional ICMPv6 connectivity between an
  infrastructure device and a Thread Router.
- Ensures the DUT BR continues to advertise OMR routes even after
  the originating BR (BR_2) is disabled.

The implementation includes:
- tests/nexus/test_1_3_DBR_TC_8.cpp: Test execution logic using
  direct method calls and Note-level logging.
- tests/nexus/verify_1_3_DBR_TC_8.py: PCAP-based verification script
  with robust Network Data and RA checking.
- Updates to tests/nexus/CMakeLists.txt and
  tests/nexus/run_nexus_tests.sh to register the new test case.
This commit is contained in:
Jonathan Hui
2026-03-24 21:56:43 -05:00
committed by GitHub
parent 6fd5c443de
commit 81d49bf906
7 changed files with 1707 additions and 0 deletions
+1
View File
@@ -261,6 +261,7 @@ ot_nexus_test(1_3_DBR_TC_6 "cert;nexus")
ot_nexus_test(1_3_DBR_TC_7A "cert;nexus")
ot_nexus_test(1_3_DBR_TC_7B "cert;nexus")
ot_nexus_test(1_3_DBR_TC_7C "cert;nexus")
ot_nexus_test(1_3_DBR_TC_8 "cert;nexus")
# Misc tests
ot_nexus_test(border_admitter "core;nexus")
+1
View File
@@ -197,6 +197,7 @@ DEFAULT_TESTS=(
"1_3_DBR_TC_7A"
"1_3_DBR_TC_7B"
"1_3_DBR_TC_7C"
"1_3_DBR_TC_8"
)
# Use provided arguments or the default test list
+942
View File
@@ -0,0 +1,942 @@
/*
* 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 = 20 * 1000;
/**
* Time to advance for a node to join as a child and upgrade to a router, in milliseconds.
*/
static constexpr uint32_t kJoinNetworkTime = 300 * 1000;
/**
* Time to advance for the BR to perform automatic actions (RA, Network Data), in milliseconds.
*/
static constexpr uint32_t kBrActionTime = 100 * 1000;
/**
* Longer time to advance for network stabilization and address configuration.
*/
static constexpr uint32_t kLongActionTime = 500 * 1000;
/**
* Time to advance for the ping response, in milliseconds.
*/
static constexpr uint32_t kPingResponseTime = 100 * 1000;
/**
* Default Hop Limit for Echo Request.
*/
static constexpr uint8_t kDefaultHopLimit = 64;
/**
* Infrastructure interface index.
*/
static constexpr uint32_t kInfraIfIndex = 1;
/**
* Echo Request identifier.
*/
static constexpr uint16_t kEchoIdentifier = 0x1234;
/**
* Echo Request payload size.
*/
static constexpr uint16_t kEchoPayloadSize = 10;
/**
* Max number of seconds to wait for a condition.
*/
static constexpr uint32_t kMaxWaitTime = 120;
/**
* OMR prefix 3 string (numerically very low to be "winning").
*/
static constexpr char kOmr3PrefixStr[] = "fd00:3::/64";
/**
* OMR prefix 4 string (numerically very high to be "losing" if same preference).
*/
static constexpr char kOmr4PrefixStr[] = "fdff:4::/64";
/**
* Infrastructure GUA address string for Eth_1.
*/
static constexpr char kEth1GuaAddrStr[] = "2001:db8:1::1";
/**
* Infrastructure GUA prefix string.
*/
static constexpr char kGua1PrefixStr[] = "2001:db8:1::/64";
static void DumpNetworkData(Node &aNode)
{
NetworkData::Iterator iterator = NetworkData::kIteratorInit;
NetworkData::OnMeshPrefixConfig prefixConfig;
NetworkData::ExternalRouteConfig routeConfig;
Log("Network Data for %s (Version: %d):", aNode.GetName(),
aNode.Get<NetworkData::Leader>().GetVersion(NetworkData::kFullSet));
while (aNode.Get<NetworkData::Leader>().GetNext(iterator, prefixConfig) == kErrorNone)
{
Log(" Prefix: %s (pref:%d, preferred:%s)", prefixConfig.GetPrefix().ToString().AsCString(),
prefixConfig.mPreference, ToYesNo(prefixConfig.mPreferred));
}
iterator = NetworkData::kIteratorInit;
while (aNode.Get<NetworkData::Leader>().GetNext(iterator, routeConfig) == kErrorNone)
{
Log(" Route: %s (pref:%d)", routeConfig.GetPrefix().ToString().AsCString(), routeConfig.mPreference);
}
}
static bool HasNetDataPrefix(Node &aNode, const Ip6::Prefix &aPrefix)
{
return aNode.Get<NetworkData::Leader>().ContainsOmrPrefix(aPrefix);
}
static bool HasNetDataRoute(Node &aNode, const char *aPrefixStr)
{
bool hasRoute = false;
NetworkData::Iterator iterator = NetworkData::kIteratorInit;
NetworkData::ExternalRouteConfig routeConfig;
Ip6::Prefix target;
SuccessOrQuit(target.FromString(aPrefixStr));
while (aNode.Get<NetworkData::Leader>().GetNext(iterator, routeConfig) == kErrorNone)
{
if (AsCoreType(&routeConfig.mPrefix) == target)
{
hasRoute = true;
break;
}
}
return hasRoute;
}
static void WaitForPrefix(Core &aNexus, Node &aNode, const Ip6::Prefix &aPrefix, bool aPresent)
{
for (uint32_t i = 0; i < kMaxWaitTime; i++)
{
if (HasNetDataPrefix(aNode, aPrefix) == aPresent)
{
break;
}
aNexus.AdvanceTime(1000);
}
VerifyOrQuit(HasNetDataPrefix(aNode, aPrefix) == aPresent);
}
static void WaitForRoute(Core &aNexus, Node &aNode, const char *aPrefixStr, bool aPresent)
{
for (uint32_t i = 0; i < kMaxWaitTime; i++)
{
if (HasNetDataRoute(aNode, aPrefixStr) == aPresent)
{
break;
}
aNexus.AdvanceTime(1000);
}
VerifyOrQuit(HasNetDataRoute(aNode, aPrefixStr) == aPresent);
}
static void PublishOmrPrefix(Node &aNode,
const Ip6::Prefix &aPrefix,
NetworkData::RoutePreference aPreference,
bool aIsPreferred)
{
NetworkData::OnMeshPrefixConfig config;
config.Clear();
config.GetPrefix() = aPrefix;
config.mPreference = aPreference;
config.mSlaac = true;
config.mOnMesh = true;
config.mStable = true;
config.mPreferred = aIsPreferred;
SuccessOrQuit(aNode.Get<NetworkData::Publisher>().PublishOnMeshPrefix(config, NetworkData::Publisher::kFromUser));
}
void Test_1_3_DBR_TC_8(void)
{
/**
* 1.8. [1.3] [CERT] Reachability - Multiple BRs - Single Thread / Single IPv6 Infrastructure - OMR prefix selection
*
* 1.8.1. Purpose
* - To test the following:
* - Maintain bi-directional reachability between Thread devices and infrastructure devices during prefix changes
* - DUT BR should observe proper OMR prefix selection when testbed-BR advertises various other prefixes:
* - Testbed advertises a winning prefix with equal preference, DUT should withdraw or deprecate its OMR prefix
* - Testbed advertises a numerically higher, non-winning prefix with equal preference, DUT should not change
* behavior.
* - Testbed advertises a numerically higher prefix with higher preference, i.e. winning OMR prefix, DUT should
* withdraw its OMR prefix.
* - Testbed withdraws winning OMR prefix, DUT should advertise its prefix with 'Low' preference again
* - Testbed OMR prefix becomes deprecated, DUT should advertise its prefix with 'Low' preference again
*
* 1.8.2. Topology
* - Eth_1 - Adjacent Infrastructure Link Reference Device
* - BR_1 (DUT) - Border Router
* - BR_2 - Border Router Reference Device and Leader
* - ED_1 - Thread Reference Device (End Device) attached to BR_1
*
* Spec Reference | V1.1 Section | V1.3.0 Section
* -----------------|--------------|---------------
* Reachability | N/A | 1.3
*/
Core nexus;
Node &eth1 = nexus.CreateNode();
Node &br1 = nexus.CreateNode();
Node &br2 = nexus.CreateNode();
Node &ed1 = nexus.CreateNode();
eth1.SetName("Eth_1");
br1.SetName("BR_1");
br2.SetName("BR_2");
ed1.SetName("ED_1");
nexus.AdvanceTime(0);
Instance::SetLogLevel(kLogLevelNote);
Log("---------------------------------------------------------------------------------------");
/**
* Step 0
* - Device: Eth_1
* - Description (DBR-1.8):
* - Harness configures Ethernet link with an on-link IPv6 GUA prefix GUA_1. Eth_1 is configured to multicast ND
* RAs.
* - Automatically configures a global address “Eth_1 GUA”.
* - Pass Criteria
* - N/A
*/
Log("Step 0: Eth_1 configured with GUA_1.");
eth1.mInfraIf.Init(eth1);
Ip6::Address eth1Gua;
SuccessOrQuit(eth1Gua.FromString(kEth1GuaAddrStr));
eth1.mInfraIf.AddAddress(eth1Gua);
Ip6::Prefix gua1;
SuccessOrQuit(gua1.FromString(kGua1PrefixStr));
eth1.mInfraIf.StartRouterAdvertisement(gua1);
nexus.AddTestVar("ETH_1_GUA_ADDR", kEth1GuaAddrStr);
Log("---------------------------------------------------------------------------------------");
/**
* Step 1
* - Device: Eth_1, BR_2, ED_1, ED_2
* - Description (DBR-1.8):
* - Enable; BR_2 automatically configures an OMR prefix for the mesh, OMR_1. Harness records the value of OMR_1
* and saves it for verification use.
* - Note: the OT command br omrprefix should provide the prefix from BR_2.
* - Form topology. Wait for BR_2 to:
* - Become Leader
* - Register as border router in Thread Network Data with its OMR prefix OMR_1
* - Send multicast ND RAs on AIL
* - Automatically advertise a route to GUA_1 in the Thread Network Data, using a Prefix TLV:
* - Prefix = ::/0
* - Domain ID = <self-selected value>
* - Has Route sub-TLV
* - Pass Criteria
* - N/A
*/
Log("Step 1: BR_2 becomes Leader and registers OMR_1.");
br1.AllowList(br2);
br1.AllowList(ed1);
br2.AllowList(br1);
ed1.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);
WaitForRoute(nexus, br2, "::/0", true);
Ip6::Prefix omr1;
SuccessOrQuit(br2.Get<BorderRouter::RoutingManager>().GetOmrPrefix(omr1));
nexus.AddTestVar("OMR_1_PREFIX", omr1.ToString().AsCString());
Log("OMR_1_PREFIX: %s", omr1.ToString().AsCString());
DumpNetworkData(br2);
Log("---------------------------------------------------------------------------------------");
/**
* Step 2
* - Device: BR_1 (DUT)
* - Description (DBR-1.8): Enable: switch on.
* - Pass Criteria
* - N/A
*/
Log("Step 2: Enable BR_1 (DUT).");
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("---------------------------------------------------------------------------------------");
/**
* Step 3
* - Device: BR_1 (DUT)
* - Description (DBR-1.8):
* - Automatically registers itself as a border router in the Thread Network Data.
* - Pass Criteria:
* - The DUT MUST register a route to GUA_1 in the Thread Network Data as follows:
* - Prefix TLV: Prefix = ::/0
* - Domain ID = <same value as that of BR_2 in step 1>
* - Has Route sub-TLV
* - R_preference = 00 (medium) or 11 (low)
* - DUT MUST NOT register an OMR prefix (e.g. OMR_1, or other) in Thread Network Data.
*/
Log("Step 3: BR_1 (DUT) registers route to GUA_1.");
nexus.AdvanceTime(kBrActionTime);
DumpNetworkData(br2);
Log("---------------------------------------------------------------------------------------");
/**
* Step 4
* - Device: BR_1 (DUT)
* - Description (DBR-1.8):
* - Automatically announces the route to OMR_1 on the AIL.
* - Pass Criteria:
* - The DUT MUST multicast ND RAs on the infrastructure link:
* - IPv6 destination MUST be ff02::1
* - MUST NOT contain a Prefix Information Option (PIO) with a ULA prefix.
* - MUST contain a Route Information Option (RIO) with OMR_1.
* - "Prf" bits MUST be 00 (medium) or 11 (low)
*/
Log("Step 4: BR_1 (DUT) announces OMR_1 on AIL.");
nexus.AdvanceTime(kBrActionTime);
Log("---------------------------------------------------------------------------------------");
/**
* Step 5
* - Device: BR_2
* - Description (DBR-1.8):
* - Harness instructs device to remove the existing OMR prefix (OMR_1) being advertised and send out new network
* data without any prefix.
* - Note: for 1.3.x cert, this could be implemented by 'br disable' or 'netdata unpublish <OMR_1>' on BR_2.
* - Pass Criteria:
* - N/A
*/
Log("Step 5: BR_2 removes OMR_1.");
SuccessOrQuit(br2.Get<BorderRouter::RoutingManager>().SetEnabled(false));
WaitForPrefix(nexus, br2, omr1, false);
nexus.AdvanceTime(kBrActionTime);
DumpNetworkData(br2);
Log("---------------------------------------------------------------------------------------");
/**
* Step 6
* - Device: BR_1 (DUT)
* - Description (DBR-1.8):
* - Automatically chooses a new OMR prefix OMR_2 and includes it in the Network Data.
* - Pass Criteria:
* - The DUT MUST register a new OMR prefix (OMR_2) in the Thread Network Data using Prefix TLV as follows:
* - Prefix = OMR_2
* - Border Router sub-TLV
* - P_preference = 11 (Low)
* - P_default = true
* - P_stable = true
* - P_on_mesh = true
* - P_preferred = true
* - P_slaac = true
* - P_dhcp = false
* - P_dp = false
* - OMR_2 MUST be 64 bits long and start with 0xFD.
* - OMR_2 MUST differ from OMR_1.
* - Also MUST contain a Prefix TLV as follows:
* - Prefix = ::/0
* - Domain ID <same value as BR_2 Domain ID>
* - Has Route TLV
* - R_preference = 00 (medium) or 11 (low)
* - Note: one possible way to check this is to verify the contents of the SVR_DATA.ntf message sent by DUT to
* Leader.
*/
Log("Step 6: BR_1 (DUT) chooses OMR_2 and registers in Network Data.");
nexus.AdvanceTime(kBrActionTime);
Ip6::Prefix omr2;
SuccessOrQuit(br1.Get<BorderRouter::RoutingManager>().GetOmrPrefix(omr2));
VerifyOrQuit(omr2 != omr1);
nexus.AddTestVar("OMR_2_PREFIX", omr2.ToString().AsCString());
Log("OMR_2_PREFIX: %s", omr2.ToString().AsCString());
DumpNetworkData(br2);
Log("---------------------------------------------------------------------------------------");
/**
* Step 7
* - Device: BR_1 (DUT)
* - Description (DBR-1.8):
* - Automatically multicasts ND RAs on Adjacent Infrastructure Link.
* - Pass Criteria:
* - The DUT MUST multicast ND RAs on the infrastructure link:
* - IPv6 destination MUST be ff02::1
* - MUST NOT contain a Prefix Information Option (PIO) with a ULA prefix.
* - MUST contain a Route Information Option (RIO) with OMR_2.
* - "Prf" bits MUST be 00 (medium) or 11 (low).
* - MUST NOT contain a Route Information Option (RIO) with OMR_1.
*/
Log("Step 7: BR_1 (DUT) multicasts ND RA with OMR_2, no OMR_1.");
nexus.AdvanceTime(kBrActionTime);
Log("---------------------------------------------------------------------------------------");
/**
* Step 8
* - Device: Eth_1
* - Description (DBR-1.8):
* - Harness instructs the device to send an ICMPv6 Echo Request to ED_1 via either one of BR_1 or BR_2.
* - IPv6 Source: Eth_1 GUA
* - IPv6 Destination: ED_1 OMR address (using prefix OMR_2)
* - Pass Criteria:
* - Eth_1 receives an ICMPv6 Echo Reply from ED_1.
* - IPv6 Source: ED_1 OMR (using prefix OMR_2)
* - IPv6 Destination: Eth_1 GUA
*/
Log("Step 8: Eth_1 pings ED_1 OMR_2.");
ed1.Join(br1, Node::kAsFed);
nexus.AdvanceTime(kJoinNetworkTime);
const Ip6::Address &ed1Omr2 = ed1.FindMatchingAddress(omr2.ToString().AsCString());
nexus.AddTestVar("ED_1_OMR_2_ADDR", ed1Omr2.ToString().AsCString());
eth1.mInfraIf.SendEchoRequest(eth1Gua, ed1Omr2, kEchoIdentifier, kEchoPayloadSize);
nexus.AdvanceTime(kPingResponseTime);
Log("---------------------------------------------------------------------------------------");
/**
* Step 9
* - Device: ED_1
* - Description (DBR-1.8):
* - Harness instructs the device to send an ICMPv6 Echo Request to Eth_1.
* - IPv6 Source: ED_1 OMR address (using prefix OMR_2)
* - IPv6 Destination: Eth_1 GUA
* - Pass Criteria:
* - ED_1 receives an ICMPv6 Echo Reply from Eth_1.
* - IPv6 Source: Eth_1 GUA
* - IPv6 Destination: ED_1 OMR address (using prefix OMR_2)
*/
Log("Step 9: ED_1 OMR_2 pings Eth_1 GUA.");
ed1.SendEchoRequest(eth1Gua, kEchoIdentifier, kEchoPayloadSize, kDefaultHopLimit, &ed1Omr2);
nexus.AdvanceTime(kPingResponseTime);
Log("---------------------------------------------------------------------------------------");
/**
* Step 10
* - Device: BR_2
* - Description (DBR-1.8):
* - Harness instructs BR_2 to add an OMR prefix OMR_3 numerically lower than OMR_2, but with same preference
* "Low".
* - Note: can use OT command 'netdata publish prefix' for this.
* - Pass Criteria:
* - N/A
*/
Log("Step 10: BR_2 adds OMR_3.");
Ip6::Prefix omr3;
SuccessOrQuit(omr3.FromString(kOmr3PrefixStr));
PublishOmrPrefix(br2, omr3, NetworkData::kRoutePreferenceLow, true);
nexus.AdvanceTime(kLongActionTime);
WaitForPrefix(nexus, br2, omr3, true);
DumpNetworkData(br2);
nexus.AddTestVar("OMR_3_PREFIX", kOmr3PrefixStr);
Log("OMR_3_PREFIX: %s", kOmr3PrefixStr);
Log("---------------------------------------------------------------------------------------");
/**
* Step 11
* - Device: BR_1 (DUT)
* - Description (DBR-1.8):
* - Automatically withdraws its own OMR prefix OMR_2 from the Thread Network Data.
* - Pass Criteria:
* - The DUT MUST register new Network Data:
* - MUST NOT contain any of the OMR prefixes OMR_1, OMR_2, or OMR_3 in a Prefix TLV.
* - MUST contain a Prefix TLV:
* - Prefix = ::/0
* - Domain ID <same value as BR_2 Domain ID>
* - Has Route TLV
* - R_preference = 00 (medium) or 11 (low)
*/
Log("Step 11: BR_1 (DUT) withdraws OMR_2 from Network Data.");
WaitForPrefix(nexus, br2, omr2, false);
nexus.AdvanceTime(kLongActionTime);
DumpNetworkData(br2);
Log("---------------------------------------------------------------------------------------");
/**
* Step 12
* - Device: BR_1 (DUT)
* - Description (DBR-1.8):
* - Automatically multicasts ND RAs on Adjacent Infrastructure Link including OMR_2/OMR_3.
* - Note: the reason that OMR_2 is still included initially, is that OMR_2 has been created by BR_1 originally
* and as seen by BR_1, it stays in a deprecated state for at most OMR_ADDR_DEPRECATION_TIME
* - Pass Criteria:
* - The DUT MUST multicast ND RAs on the infrastructure link:
* - IPv6 destination MUST be ff02::1
* - MUST NOT contain a Prefix Information Option (PIO) with a ULA prefix.
* - MUST NOT contain a Route Information Option (RIO) with OMR_1.
* - MUST contain a Route Information Option (RIO) with OMR_2.
* - "Prf" bits MUST be 00 (medium) or 11 (low)
* - MUST contain a Route Information Option (RIO) with OMR_3.
* - "Prf" bits MUST be 00 (medium) or 11 (low)
*/
Log("Step 12: BR_1 (DUT) multicasts ND RA with OMR_2 and OMR_3.");
nexus.AdvanceTime(kBrActionTime);
DumpNetworkData(br1);
DumpNetworkData(br2);
Log("---------------------------------------------------------------------------------------");
/**
* Step 13
* - Device: Eth_1
* - Description (DBR-1.8):
* - Harness instructs the device to send an ICMPv6 Echo Request to ED_1 via BR_1 or BR_2.
* - IPv6 Source: Eth_1 GUA
* - IPv6 Destination: ED_1 OMR address (using prefix OMR_3)
* - Pass Criteria:
* - Eth_1 receives an ICMPv6 Echo Reply from ED_1.
* - IPv6 Source: ED_1 OMR (using prefix OMR_3)
* - IPv6 Destination: Eth_1 GUA
*/
Log("Step 13: Eth_1 pings ED_1 OMR_3.");
const Ip6::Address &ed1Omr3 = ed1.FindMatchingAddress(kOmr3PrefixStr);
nexus.AddTestVar("ED_1_OMR_3_ADDR", ed1Omr3.ToString().AsCString());
eth1.mInfraIf.SendEchoRequest(eth1Gua, ed1Omr3, kEchoIdentifier, kEchoPayloadSize);
nexus.AdvanceTime(kPingResponseTime);
Log("---------------------------------------------------------------------------------------");
/**
* Step 14
* - Device: ED_1
* - Description (DBR-1.8):
* - Harness instructs the device to send an ICMPv6 Echo Request to Eth_1.
* - IPv6 Source: ED_1 OMR address (using prefix OMR_3)
* - IPv6 Destination: Eth_1 GUA
* - Pass Criteria:
* - ED_1 receives an ICMPv6 Echo Reply from Eth_1.
* - IPv6 Source: Eth_1 GUA
* - IPv6 Destination: ED_1 OMR address (using prefix OMR_3)
*/
Log("Step 14: ED_1 OMR_3 pings Eth_1 GUA.");
ed1.SendEchoRequest(eth1Gua, kEchoIdentifier, kEchoPayloadSize, kDefaultHopLimit, &ed1Omr3);
nexus.AdvanceTime(kPingResponseTime);
Log("---------------------------------------------------------------------------------------");
/**
* Step 15
* - Device: BR_2
* - Description (DBR-1.8):
* - Harness instructs device to remove existing OMR prefix (OMR_3) that is being advertised and sends out new
* network data without the prefix OMR_3.
* - Note: can use OT command 'netdata unpublish <prefix>' for this.
* - Pass Criteria:
* - N/A
*/
Log("Step 15: BR_2 removes OMR_3.");
SuccessOrQuit(br2.Get<NetworkData::Publisher>().UnpublishPrefix(omr3));
WaitForPrefix(nexus, br2, omr3, false);
nexus.AdvanceTime(kLongActionTime);
DumpNetworkData(br2);
Log("---------------------------------------------------------------------------------------");
/**
* Step 16
* - Device: BR_1 (DUT)
* - Description (DBR-1.8):
* - Automatically starts advertising its own OMR prefix OMR_2 again.
* - Pass Criteria:
* - The DUT MUST register its OMR prefix OMR_2 in the Thread Network Data. Flags in the Border Router sub-TLV
* MUST be:
* - P_preference = 11 (Low)
* - P_default = true
* - P_stable = true
* - P_on_mesh = true
* - P_preferred = true
* - P_slaac = true
* - P_dhcp = false
* - P_dp = false
* - OMR_2 MUST be 64 bits long and start with 0xFD.
* - OMR_2 MUST be equal to the OMR_2 values as used in previous test steps.
*/
Log("Step 16: BR_1 (DUT) re-registers OMR_2.");
WaitForPrefix(nexus, br2, omr2, true);
nexus.AdvanceTime(kBrActionTime);
DumpNetworkData(br2);
Log("---------------------------------------------------------------------------------------");
/**
* Step 17
* - Device: BR_1 (DUT)
* - Description (DBR-1.8):
* - Automatically multicasts ND RAs on Adjacent Infrastructure Link with OMR_2.
* - Note: the reason that OMR_3 is not advertised, is that OMR_3 was withdrawn and was not originally created by
* BR_1. So BR_1 has no responsibility to advertise this deprecated prefix.
* - Pass Criteria:
* - The DUT MUST multicast ND RAs on the infrastructure link:
* - IPv6 destination MUST be ff02::1
* - MUST NOT contain a Prefix Information Option (PIO) with a ULA prefix.
* - MUST NOT contain a Route Information Option (RIO) with OMR_1.
* - MUST contain a Route Information Option (RIO) with OMR_2.
* - "Prf" bits MUST be 00 (medium) or 11 (low)
* - MUST NOT contain a Route Information Option (RIO) with OMR_3.
*/
Log("Step 17: BR_1 (DUT) multicasts ND RA with OMR_2, no OMR_3.");
nexus.AdvanceTime(kBrActionTime);
Log("---------------------------------------------------------------------------------------");
/**
* Step 18
* - Device: BR_2
* - Description (DBR-1.8):
* - Harness instructs the device to add new OMR prefix (OMR_4) numerically higher than OMR_2, but with same
* preference "Low".
* - Pass Criteria:
* - N/A
*/
Log("Step 18: BR_2 adds OMR_4.");
Ip6::Prefix omr4;
SuccessOrQuit(omr4.FromString(kOmr4PrefixStr));
PublishOmrPrefix(br2, omr4, NetworkData::kRoutePreferenceLow, true);
WaitForPrefix(nexus, br2, omr4, true);
nexus.AdvanceTime(kLongActionTime);
nexus.AddTestVar("OMR_4_PREFIX", kOmr4PrefixStr);
Log("OMR_4_PREFIX: %s", kOmr4PrefixStr);
DumpNetworkData(br2);
Log("---------------------------------------------------------------------------------------");
/**
* Step 19
* - Device: BR_1 (DUT)
* - Description (DBR-1.8):
* - No change in behavior for advertising its OMR prefix, OMR_2.
* - Automatically starts advertising a route for the new OMR_4 prefix.
* - Pass Criteria:
* - The DUT MUST NOT send a Network Data update with any one of the following prefixes in a Prefix TLV:
* - OMR_1, OMR_2, OMR_3, OMR_4
* - The DUT MUST multicast ND RAs on the infrastructure link:
* - IPv6 destination MUST be ff02::1
* - MUST NOT contain a Prefix Information Option (PIO) with a ULA prefix.
* - MUST contain a Route Information Option (RIO) with OMR_2.
* - "Prf" bits MUST be 00 (medium) or 11 (low).
* - MUST contain a Route Information Option (RIO) with OMR_4.
* - "Prf" bits MUST be 00 (medium) or 11 (low).
* - MUST NOT contain RIO with any one of OMR_1, OMR_3.
*/
Log("Step 19: BR_1 (DUT) continues OMR_2, adds OMR_4 on AIL.");
nexus.AdvanceTime(kBrActionTime);
DumpNetworkData(br2);
Log("---------------------------------------------------------------------------------------");
/**
* Step 20
* - Device: BR_2
* - Description (DBR-1.8):
* - Harness instructs the device to update the numerically higher OMR_4 with a higher preference value 00
* ('Medium').
* - Pass Criteria:
* - N/A
*/
Log("Step 20: BR_2 updates OMR_4 to Medium pref.");
PublishOmrPrefix(br2, omr4, NetworkData::kRoutePreferenceMedium, true);
nexus.AdvanceTime(kLongActionTime);
DumpNetworkData(br2);
Log("---------------------------------------------------------------------------------------");
/**
* Step 21
* - Device: BR_1 (DUT)
* - Description (DBR-1.8): Automatically withdraws its current OMR prefix OMR_2.
* - Pass Criteria:
* - The DUT MUST send a Network Data update. In the new network data:
* - the following prefixes MUST NOT be present in a Prefix TLV: OMR_1, OMR_2, OMR_3
* - The DUT MUST multicast ND RAs on the infrastructure link:
* - IPv6 destination MUST be ff02::1
* - MUST NOT contain a Prefix Information Option (PIO) with a ULA prefix.
* - MUST contain a Route Information Option (RIO) with OMR_2:
* - "Prf" bits MUST be 00 (medium) or 11 (low).
* - MUST contain a Route Information Option (RIO) with OMR_4:
* - "Prf" bits MUST be 00 (medium) or 11 (low).
* - MUST NOT contain a Route Information Option (RIO) with OMR_1 or OMR_3.
*/
Log("Step 21: BR_1 (DUT) withdraws OMR_2.");
WaitForPrefix(nexus, br2, omr2, false);
nexus.AdvanceTime(kBrActionTime);
DumpNetworkData(br2);
Log("---------------------------------------------------------------------------------------");
/**
* Step 22
* - Device: BR_2
* - Description (DBR-1.8):
* - Harness instructs the device to update the prefix OMR_4 to a deprecated state by setting P_preferred =
* 'false'.
* - Note: can use OT command 'netdata publish prefix' for this where the flags do not contain the 'p' (Preferred)
* flag.
* - Pass Criteria:
* - N/A
*/
Log("Step 22: BR_2 deprecates OMR_4.");
PublishOmrPrefix(br2, omr4, NetworkData::kRoutePreferenceMedium, false);
nexus.AdvanceTime(kLongActionTime);
DumpNetworkData(br2);
Log("---------------------------------------------------------------------------------------");
/**
* Step 23
* - Device: BR_1 (DUT)
* - Description (DBR-1.8): Automatically starts advertising its own OMR prefix OMR_2 again.
* - Pass Criteria:
* - The DUT MUST register its OMR prefix OMR_2 in the Thread Network Data. Flags in the Border Router sub-TLV
* MUST be:
* - P_preference = 11 (Low)
* - P_default = true
* - P_stable = true
* - P_on_mesh = true
* - P_preferred = true
* - P_slaac = true
* - P_dhcp = false
* - P_dp = false
* - OMR_2 MUST be 64 bits long and start with 0xFD.
* - OMR_2 MUST be equal to the OMR_2 values as used in previous test steps.
*/
Log("Step 23: BR_1 (DUT) re-registers OMR_2.");
WaitForPrefix(nexus, br2, omr2, true);
nexus.AdvanceTime(kBrActionTime);
DumpNetworkData(br2);
Log("---------------------------------------------------------------------------------------");
/**
* Step 24
* - Device: BR_1 (DUT)
* - Description (DBR-1.8):
* - Automatically multicasts ND RAs on Adjacent Infrastructure Link.
* - Note: even though OMR_4 is deprecated, it is still valid so it is advertised on the AIL in a RIO.
* - Pass Criteria:
* - The DUT MUST multicast ND RAs on the infrastructure link:
* - IPv6 destination MUST be ff02::1
* - MUST NOT contain a Prefix Information Option (PIO) with a ULA prefix.
* - MUST NOT contain a Route Information Option (RIO) with OMR_1.
* - MUST contain a Route Information Option (RIO) with OMR_2.
* - "Prf" bits MUST be 00 (medium) or 11 (low)
* - MUST NOT contain a Route Information Option (RIO) with OMR_3.
* - MUST contain a Route Information Option (RIO) with OMR_4.
* - "Prf" bits MUST be 00 (medium) or 11 (low)
*/
Log("Step 24: BR_1 (DUT) multicasts ND RA with OMR_2 and OMR_4.");
nexus.AdvanceTime(kBrActionTime);
Log("---------------------------------------------------------------------------------------");
/**
* Step 25
* - Device: Eth_1
* - Description (DBR-1.8):
* - Harness instructs the device to send an ICMPv6 Echo Request to ED_1 via BR_1.
* - IPv6 Source: Eth_1 GUA
* - IPv6 Destination: ED_1 OMR address (using prefix OMR_2)
* - Pass Criteria:
* - Eth_1 receives an ICMPv6 Echo Reply from ED_1.
* - IPv6 Source: ED_1 OMR (using prefix OMR_2)
* - IPv6 Destination: Eth_1 GUA
*/
Log("Step 25: Eth_1 pings ED_1 OMR_2.");
eth1.mInfraIf.SendEchoRequest(eth1Gua, ed1Omr2, kEchoIdentifier, kEchoPayloadSize);
nexus.AdvanceTime(kPingResponseTime);
Log("---------------------------------------------------------------------------------------");
/**
* Step 26
* - Device: ED_1
* - Description (DBR-1.8):
* - Harness instructs the device to send an ICMPv6 Echo Request to Eth_1.
* - IPv6 Source: ED_1 OMR address (using prefix OMR_2)
* - IPv6 Destination: Eth_1 GUA
* - Pass Criteria:
* - ED_1 receives an ICMPv6 Echo Reply from Eth_1.
* - IPv6 Source: Eth_1 GUA
* - IPv6 Destination: ED_1 OMR address (using prefix OMR_2)
*/
Log("Step 26: ED_1 OMR_2 pings Eth_1 GUA.");
ed1.SendEchoRequest(eth1Gua, kEchoIdentifier, kEchoPayloadSize, kDefaultHopLimit, &ed1Omr2);
nexus.AdvanceTime(kPingResponseTime);
Log("---------------------------------------------------------------------------------------");
/**
* Step 27
* - Device: BR_2
* - Description (DBR-1.8):
* - Harness disables the BR function of the device. Or if that is not possible, switch it off.
* - Note: disabling BR may use 'br disable' CLI command.
* - Note: in case of switching off BR_2, the OMR prefix OMR_4 that was advertised by BR_2 remains active for some
* time. The Leader timeout of Leader BR_2 does not happen yet during the below steps of this test.
* - Pass Criteria:
* - N/A
*/
Log("Step 27: BR_2 disabled.");
SuccessOrQuit(br2.Get<BorderRouter::RoutingManager>().SetEnabled(false));
nexus.AdvanceTime(kBrActionTime);
DumpNetworkData(br2);
Log("---------------------------------------------------------------------------------------");
/**
* Step 28
* - Device: Eth_1
* - Description (DBR-1.8):
* - Harness instructs the device to send an ICMPv6 Echo Request to ED_1 via BR_1.
* - IPv6 Source: Eth_1 GUA
* - IPv6 Destination: ED_1 OMR address (using prefix OMR_2)
* - Pass Criteria:
* - Eth_1 receives an ICMPv6 Echo Reply from ED_1.
* - IPv6 Source: ED_1 OMR (using prefix OMR_2)
* - IPv6 Destination: Eth_1 GUA
*/
Log("Step 28: Eth_1 pings ED_1 OMR_2.");
eth1.mInfraIf.SendEchoRequest(eth1Gua, ed1Omr2, kEchoIdentifier, kEchoPayloadSize);
nexus.AdvanceTime(kPingResponseTime);
Log("---------------------------------------------------------------------------------------");
/**
* Step 29
* - Device: ED_1
* - Description (DBR-1.8):
* - Harness instructs the device to send an ICMPv6 Echo Request to Eth_1.
* - IPv6 Source: ED_1 OMR address (using prefix OMR_4)
* - IPv6 Destination: Eth_1 GUA.
* - Note: ED_1 is forced here to use a source address based on prefix OMR_4, even though that prefix is
* deprecated. In OT CLI this can most likely be achieved using the command:
* - ping -I <ED_1-OMR_4-address> <Eth_1-GUA>
* - Pass Criteria:
* - N/A
*/
Log("Step 29: ED_1 pings Eth_1 using OMR_4.");
const Ip6::Address &ed1Omr4 = ed1.FindMatchingAddress(kOmr4PrefixStr);
nexus.AddTestVar("ED_1_OMR_4_ADDR", ed1Omr4.ToString().AsCString());
ed1.SendEchoRequest(eth1Gua, kEchoIdentifier, kEchoPayloadSize, kDefaultHopLimit, &ed1Omr4);
Log("---------------------------------------------------------------------------------------");
/**
* Step 30
* - Device: Eth_1
* - Description (DBR-1.8):
* - Automatically replies to the ICMPv6 Echo Request with Echo Reply, that is sent to BR_1.
* - Pass Criteria:
* - N/A
*/
Log("Step 30: Eth_1 replies to OMR_4.");
nexus.AdvanceTime(kPingResponseTime);
Log("---------------------------------------------------------------------------------------");
/**
* Step 31
* - Device: BR_1 (DUT)
* - Description (DBR-1.8):
* - Automatically attempts to deliver the Echo Reply to a node on the mesh.
* - Pass Criteria:
* - The DUT MUST attempt to deliver the packet to a node on the mesh, either:
* - 1. BR_1 sends a multicast Address Query for ED_1 OMR_4 based address into the mesh;
* - 2. or ED_1 receives the Echo Reply from Eth_1 as follows:
* - IPv6 Source: Eth_1 GUA
* - IPv6 Destination: ED_1 OMR address (using prefix OMR_4)
*/
Log("Step 31: BR_1 delivers Echo Reply for OMR_4.");
nexus.AdvanceTime(kPingResponseTime);
nexus.SaveTestInfo("test_1_3_DBR_TC_8.json");
}
} // namespace Nexus
} // namespace ot
int main(void)
{
ot::Nexus::Test_1_3_DBR_TC_8();
printf("All tests passed\n");
return 0;
}
+1
View File
@@ -56,6 +56,7 @@ ICMPV6_TYPE_ROUTER_SOLICITATION = 133
def get_val(field_values, index):
vals = verify_utils.as_list(field_values)
assert index < len(vals), f"Index {index} is out of bounds for field values with length {len(vals)}"
return vals[index]
+1
View File
@@ -56,6 +56,7 @@ ICMPV6_TYPE_ROUTER_SOLICITATION = 133
def get_val(field_values, index):
vals = verify_utils.as_list(field_values)
assert index < len(vals), f"Index {index} is out of bounds for field values with length {len(vals)}"
return vals[index]
+1
View File
@@ -55,6 +55,7 @@ ICMPV6_TYPE_ROUTER_SOLICITATION = 133
def get_val(field_values, index):
vals = verify_utils.as_list(field_values)
assert index < len(vals), f"Index {index} is out of bounds for field values with length {len(vals)}"
return vals[index]
+760
View File
@@ -0,0 +1,760 @@
#!/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.addrs import Ipv6Addr
from pktverify import consts
from pktverify.null_field import nullField
# Protocol Constants
ULA_PREFIX_START_BYTE = 0xfd
BR_PREFERENCE_LOW = 3
BR_PREFERENCE_MEDIUM = 0
BR_FLAG_R_TRUE = 1
BR_FLAG_O_TRUE = 1
BR_FLAG_P_TRUE = 1
BR_FLAG_P_FALSE = 0
BR_FLAG_S_TRUE = 1
BR_FLAG_D_FALSE = 0
BR_FLAG_DP_FALSE = 0
def check_nwd_has_route(packet, prefix):
"""Checks if Network Data has an External Route with the given prefix."""
try:
if not hasattr(packet, 'thread_nwd'):
return False
types = verify_utils.as_list(packet.thread_nwd.tlv.type)
prefixes = verify_utils.as_list(packet.thread_nwd.tlv.prefix)
except (AttributeError, IndexError):
return False
prefix_idx = 0
is_target = False
for t in types:
if t == consts.NWD_PREFIX_TLV:
if prefix_idx < len(prefixes):
current_prefix = prefixes[prefix_idx]
prefix_idx += 1
if current_prefix:
is_target = (Ipv6Addr(current_prefix) == Ipv6Addr(prefix))
else:
is_target = False
elif t == consts.NWD_HAS_ROUTER_TLV:
if is_target:
return True
elif t in (consts.NWD_COMMISSIONING_DATA_TLV, consts.NWD_SERVICE_TLV):
is_target = False
return False
def check_ra_rio(packet, prefix, not_prefixes=None):
"""Checks if an ICMPv6 RA contains a Route Information Option (RIO) with the given prefix."""
rio_prefixes, _ = verify_utils.get_ra_prefixes(packet)
target_addr = Ipv6Addr(prefix)
found = any(Ipv6Addr(p) == target_addr for p in rio_prefixes)
if not found:
return False
if not_prefixes:
for np in not_prefixes:
if any(Ipv6Addr(p) == Ipv6Addr(np) for p in rio_prefixes):
return False
return True
def check_nwd_contains_prefix(packet, prefix):
"""Simple check if Network Data contains the given prefix in a Prefix TLV."""
try:
if not hasattr(packet, 'thread_nwd') or not hasattr(packet.thread_nwd.tlv, 'prefix'):
return False
prefixes = verify_utils.as_list(packet.thread_nwd.tlv.prefix)
target_addr = Ipv6Addr(prefix)
return any(p and Ipv6Addr(p) == target_addr for p in prefixes)
except (AttributeError, IndexError):
return False
def verify(pv):
pkts = pv.pkts
BR_1 = pv.vars['BR_1']
ED_1 = pv.vars['ED_1']
ETH_1_GUA = pv.vars['ETH_1_GUA_ADDR']
# Strip /len from prefixes for Ipv6Addr matching
OMR_1_PREFIX = pv.vars['OMR_1_PREFIX'].split('/')[0]
OMR_2_PREFIX = pv.vars['OMR_2_PREFIX'].split('/')[0]
OMR_3_PREFIX = pv.vars['OMR_3_PREFIX'].split('/')[0]
OMR_4_PREFIX = pv.vars['OMR_4_PREFIX'].split('/')[0]
ED_1_OMR_2 = pv.vars['ED_1_OMR_2_ADDR']
ED_1_OMR_3 = pv.vars['ED_1_OMR_3_ADDR']
ED_1_OMR_4 = pv.vars['ED_1_OMR_4_ADDR']
# Step 0
# - Device: Eth_1
# - Description (DBR-1.8):
# - Harness configures Ethernet link with an on-link IPv6 GUA prefix GUA_1. Eth_1 is configured to multicast ND
# RAs.
# - Automatically configures a global address “Eth_1 GUA”.
# - Pass Criteria
# - N/A
print("Step 0: Eth_1 configured with GUA_1.")
# Step 1
# - Device: Eth_1, BR_2, ED_1, ED_2
# - Description (DBR-1.8):
# - Enable; BR_2 automatically configures an OMR prefix for the mesh, OMR_1. Harness records the value of OMR_1
# and saves it for verification use.
# - Note: the OT command br omrprefix should provide the prefix from BR_2.
# - Form topology. Wait for BR_2 to:
# - Become Leader
# - Register as border router in Thread Network Data with its OMR prefix OMR_1
# - Send multicast ND RAs on AIL
# - Automatically advertise a route to GUA_1 in the Thread Network Data, using a Prefix TLV:
# - Prefix = ::/0
# - Domain ID = <self-selected value>
# - Has Route sub-TLV
# - Pass Criteria
# - N/A
print("Step 1: BR_2 becomes Leader and registers OMR_1.")
# Step 2
# - Device: BR_1 (DUT)
# - Description (DBR-1.8): Enable: switch on.
# - Pass Criteria
# - N/A
print("Step 2: Enable BR_1 (DUT).")
# Step 3
# - Device: BR_1 (DUT)
# - Description (DBR-1.8):
# - Automatically registers itself as a border router in the Thread Network Data.
# - Pass Criteria:
# - The DUT MUST register a route to GUA_1 in the Thread Network Data as follows:
# - Prefix TLV: Prefix = ::/0
# - Domain ID = <same value as that of BR_2 in step 1>
# - Has Route sub-TLV
# - R_preference = 00 (medium) or 11 (low)
# - DUT MUST NOT register an OMR prefix (e.g. OMR_1, or other) in Thread Network Data.
print("Step 3: BR_1 (DUT) registers route to GUA_1.")
pkts.filter_wpan_src64(BR_1).\
filter(lambda p: hasattr(p, 'mle') and p.mle.cmd == consts.MLE_DATA_RESPONSE).\
filter(lambda p: check_nwd_has_route(p, "::")).\
must_next()
# Step 4
# - Device: BR_1 (DUT)
# - Description (DBR-1.8):
# - Automatically announces the route to OMR_1 on the AIL.
# - Pass Criteria:
# - The DUT MUST multicast ND RAs on the infrastructure link:
# - IPv6 destination MUST be ff02::1
# - MUST NOT contain a Prefix Information Option (PIO) with a ULA prefix.
# - MUST contain a Route Information Option (RIO) with OMR_1.
# - "Prf" bits MUST be 00 (medium) or 11 (low)
print("Step 4: BR_1 (DUT) announces OMR_1 on AIL.")
pkts.filter_eth_src(pv.vars['BR_1_ETH']).\
filter_ipv6_dst("ff02::1").\
filter(lambda p: check_ra_rio(p, OMR_1_PREFIX)).\
filter(lambda p: not any(Ipv6Addr(pref)[0] == ULA_PREFIX_START_BYTE
for pref in verify_utils.get_ra_prefixes(p)[1])).\
must_next()
# Step 5
# - Device: BR_2
# - Description (DBR-1.8):
# - Harness instructs device to remove the existing OMR prefix (OMR_1) being advertised and send out new network
# data without any prefix.
# - Note: for 1.3.x cert, this could be implemented by 'br disable' or 'netdata unpublish <OMR_1>' on BR_2.
# - Pass Criteria:
# - N/A
print("Step 5: BR_2 removes OMR_1.")
# Step 6
# - Device: BR_1 (DUT)
# - Description (DBR-1.8):
# - Automatically chooses a new OMR prefix OMR_2 and includes it in the Network Data.
# - Pass Criteria:
# - The DUT MUST register a new OMR prefix (OMR_2) in the Thread Network Data using Prefix TLV as follows:
# - Prefix = OMR_2
# - Border Router sub-TLV
# - P_preference = 11 (Low)
# - P_default = true
# - P_stable = true
# - P_on_mesh = true
# - P_preferred = true
# - P_slaac = true
# - P_dhcp = false
# - P_dp = false
# - OMR_2 MUST be 64 bits long and start with 0xFD.
# - OMR_2 MUST differ from OMR_1.
# - Also MUST contain a Prefix TLV as follows:
# - Prefix = ::/0
# - Domain ID <same value as BR_2 Domain ID>
# - Has Route TLV
# - R_preference = 00 (medium) or 11 (low)
# - Note: one possible way to check this is to verify the contents of the SVR_DATA.ntf message sent by DUT to
# Leader.
print("Step 6: BR_1 (DUT) chooses OMR_2 and registers in Network Data.")
if Ipv6Addr(OMR_2_PREFIX) == Ipv6Addr(OMR_1_PREFIX):
raise verify_utils.VerificationError(f"OMR_2 ({OMR_2_PREFIX}) must differ from OMR_1 ({OMR_1_PREFIX})")
# OMR_2 MUST be 64 bits long and start with 0xFD.
omr2_addr = Ipv6Addr(OMR_2_PREFIX)
if omr2_addr[0] != 0xfd:
raise verify_utils.VerificationError(f"OMR_2 ({OMR_2_PREFIX}) must start with 0xFD")
if int(pv.vars['OMR_2_PREFIX'].split('/')[-1]) != 64:
raise verify_utils.VerificationError(f"OMR_2 ({pv.vars['OMR_2_PREFIX']}) must be 64 bits long")
pkts.filter_wpan_src64(BR_1).\
filter(lambda p: hasattr(p, 'mle') and p.mle.cmd == consts.MLE_DATA_RESPONSE).\
filter(lambda p: verify_utils.check_nwd_prefix_flags(p,
OMR_2_PREFIX,
stable=1,
pref=BR_PREFERENCE_LOW,
r=BR_FLAG_R_TRUE,
o=BR_FLAG_O_TRUE,
p=BR_FLAG_P_TRUE,
s=BR_FLAG_S_TRUE,
d=BR_FLAG_D_FALSE,
dp=BR_FLAG_DP_FALSE)).\
filter(lambda p: check_nwd_has_route(p, "::")).\
must_next()
# Step 7
# - Device: BR_1 (DUT)
# - Description (DBR-1.8):
# - Automatically multicasts ND RAs on Adjacent Infrastructure Link.
# - Pass Criteria:
# - The DUT MUST multicast ND RAs on the infrastructure link:
# - IPv6 destination MUST be ff02::1
# - MUST NOT contain a Prefix Information Option (PIO) with a ULA prefix.
# - MUST contain a Route Information Option (RIO) with OMR_2.
# - "Prf" bits MUST be 00 (medium) or 11 (low).
# - MUST NOT contain a Route Information Option (RIO) with OMR_1.
print("Step 7: BR_1 (DUT) multicasts ND RA with OMR_2, no OMR_1.")
pkts.filter_eth_src(pv.vars['BR_1_ETH']).\
filter_ipv6_dst("ff02::1").\
filter(lambda p: check_ra_rio(p, OMR_2_PREFIX, not_prefixes=[OMR_1_PREFIX])).\
must_next()
# Step 8
# - Device: Eth_1
# - Description (DBR-1.8):
# - Harness instructs the device to send an ICMPv6 Echo Request to ED_1 via either one of BR_1 or BR_2.
# - IPv6 Source: Eth_1 GUA
# - IPv6 Destination: ED_1 OMR address (using prefix OMR_2)
# - Pass Criteria:
# - Eth_1 receives an ICMPv6 Echo Reply from ED_1.
# - IPv6 Source: ED_1 OMR (using prefix OMR_2)
# - IPv6 Destination: Eth_1 GUA
print("Step 8: Eth_1 pings ED_1 OMR_2.")
_pkt = pkts.filter_eth_src(pv.vars['Eth_1_ETH']).\
filter_ipv6_src(ETH_1_GUA).\
filter_ipv6_dst(ED_1_OMR_2).\
filter_ping_request().\
must_next()
pkts.filter(lambda p: p.eth.dst == pv.vars['Eth_1_ETH']).\
filter_ipv6_src(ED_1_OMR_2).\
filter_ipv6_dst(ETH_1_GUA).\
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
must_next()
# Step 9
# - Device: ED_1
# - Description (DBR-1.8):
# - Harness instructs the device to send an ICMPv6 Echo Request to Eth_1.
# - IPv6 Source: ED_1 OMR address (using prefix OMR_2)
# - IPv6 Destination: Eth_1 GUA
# - Pass Criteria:
# - ED_1 receives an ICMPv6 Echo Reply from Eth_1.
# - IPv6 Source: Eth_1 GUA
# - IPv6 Destination: ED_1 OMR address (using prefix OMR_2)
print("Step 9: ED_1 OMR_2 pings Eth_1 GUA.")
_pkt = pkts.filter_ipv6_src(ED_1_OMR_2).\
filter_ipv6_dst(ETH_1_GUA).\
filter_ping_request().\
must_next()
pkts.filter(lambda p: p.ipv6.src == ETH_1_GUA).\
filter_ipv6_dst(ED_1_OMR_2).\
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
must_next()
# Step 10
# - Device: BR_2
# - Description (DBR-1.8):
# - Harness instructs BR_2 to add an OMR prefix OMR_3 numerically lower than OMR_2, but with same preference
# "Low".
# - Note: can use OT command 'netdata publish prefix' for this.
# - Pass Criteria:
# - N/A
print("Step 10: BR_2 adds OMR_3.")
# Check if OMR_3 is present in ANY node's Network Data (MLE) or BR_1's RAs.
# Search from beginning of pcap as it might appear early due to propagation.
_pkt_step10 = pkts.copy().filter(lambda p: (hasattr(p, 'thread_nwd') and\
verify_utils.check_nwd_prefix_flags(p, OMR_3_PREFIX)) or\
check_ra_rio(p, OMR_3_PREFIX)).\
must_next()
# Step 11
# - Device: BR_1 (DUT)
# - Description (DBR-1.8):
# - Automatically withdraws its own OMR prefix OMR_2 from the Thread Network Data.
# - Pass Criteria:
# - The DUT MUST register new Network Data:
# - MUST NOT contain any of the OMR prefixes OMR_1, OMR_2, or OMR_3 in a Prefix TLV.
# - MUST contain a Prefix TLV:
# - Prefix = ::/0
# - Domain ID <same value as BR_2 Domain ID>
# - Has Route TLV
# - R_preference = 00 (medium) or 11 (low)
print("Step 11: BR_1 (DUT) withdraws OMR_2 from Network Data.")
# Step 11 must happen after Step 10.
pkts.index = (_pkt_step10.number, _pkt_step10.number)
pkts.filter(lambda p: hasattr(p, 'thread_nwd') and not check_nwd_contains_prefix(p, OMR_2_PREFIX)).must_next()
# Step 12
# - Device: BR_1 (DUT)
# - Description (DBR-1.8):
# - Automatically multicasts ND RAs on Adjacent Infrastructure Link including OMR_2/OMR_3.
# - Note: the reason that OMR_2 is still included initially, is that OMR_2 has been created by BR_1 originally
# and as seen by BR_1, it stays in a deprecated state for at most OMR_ADDR_DEPRECATION_TIME
# - Pass Criteria:
# - The DUT MUST multicast ND RAs on the infrastructure link:
# - IPv6 destination MUST be ff02::1
# - MUST NOT contain a Prefix Information Option (PIO) with a ULA prefix.
# - MUST NOT contain a Route Information Option (RIO) with OMR_1.
# - MUST contain a Route Information Option (RIO) with OMR_2.
# - "Prf" bits MUST be 00 (medium) or 11 (low)
# - MUST contain a Route Information Option (RIO) with OMR_3.
# - "Prf" bits MUST be 00 (medium) or 11 (low)
print("Step 12: BR_1 (DUT) multicasts ND RA with OMR_2 and OMR_3.")
pkts.filter_eth_src(pv.vars['BR_1_ETH']).\
filter_ipv6_dst("ff02::1").\
filter(lambda p: check_ra_rio(p, OMR_2_PREFIX)).\
filter(lambda p: check_ra_rio(p, OMR_3_PREFIX)).\
must_next()
# Step 13
# - Device: Eth_1
# - Description (DBR-1.8):
# - Harness instructs the device to send an ICMPv6 Echo Request to ED_1 via BR_1 or BR_2.
# - IPv6 Source: Eth_1 GUA
# - IPv6 Destination: ED_1 OMR address (using prefix OMR_3)
# - Pass Criteria:
# - Eth_1 receives an ICMPv6 Echo Reply from ED_1.
# - IPv6 Source: ED_1 OMR (using prefix OMR_3)
# - IPv6 Destination: Eth_1 GUA
print("Step 13: Eth_1 pings ED_1 OMR_3.")
_pkt = pkts.filter_eth_src(pv.vars['Eth_1_ETH']).\
filter_ipv6_src(ETH_1_GUA).\
filter_ipv6_dst(ED_1_OMR_3).\
filter_ping_request().\
must_next()
pkts.filter(lambda p: p.eth.dst == pv.vars['Eth_1_ETH']).\
filter_ipv6_src(ED_1_OMR_3).\
filter_ipv6_dst(ETH_1_GUA).\
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
must_next()
# Step 14
# - Device: ED_1
# - Description (DBR-1.8):
# - Harness instructs the device to send an ICMPv6 Echo Request to Eth_1.
# - IPv6 Source: ED_1 OMR address (using prefix OMR_3)
# - IPv6 Destination: Eth_1 GUA
# - Pass Criteria:
# - ED_1 receives an ICMPv6 Echo Reply from Eth_1.
# - IPv6 Source: Eth_1 GUA
# - IPv6 Destination: ED_1 OMR address (using prefix OMR_3)
print("Step 14: ED_1 OMR_3 pings Eth_1 GUA.")
_pkt = pkts.filter_ipv6_src(ED_1_OMR_3).\
filter_ipv6_dst(ETH_1_GUA).\
filter_ping_request().\
must_next()
pkts.filter(lambda p: p.ipv6.src == ETH_1_GUA).\
filter_ipv6_dst(ED_1_OMR_3).\
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
must_next()
# Step 15
# - Device: BR_2
# - Description (DBR-1.8):
# - Harness instructs device to remove existing OMR prefix (OMR_3) that is being advertised and sends out new
# network data without the prefix OMR_3.
# - Note: can use OT command 'netdata unpublish <prefix>' for this.
# - Pass Criteria:
# - N/A
print("Step 15: BR_2 removes OMR_3.")
# Step 16
# - Device: BR_1 (DUT)
# - Description (DBR-1.8):
# - Automatically starts advertising its own OMR prefix OMR_2 again.
# - Pass Criteria:
# - The DUT MUST register its OMR prefix OMR_2 in the Thread Network Data. Flags in the Border Router sub-TLV
# MUST be:
# - P_preference = 11 (Low)
# - P_default = true
# - P_stable = true
# - P_on_mesh = true
# - P_preferred = true
# - P_slaac = true
# - P_dhcp = false
# - P_dp = false
# - OMR_2 MUST be 64 bits long and start with 0xFD.
# - OMR_2 MUST be equal to the OMR_2 values as used in previous test steps.
print("Step 16: BR_1 (DUT) re-registers OMR_2.")
# OMR_2 MUST be 64 bits long and start with 0xFD.
omr2_addr = Ipv6Addr(OMR_2_PREFIX)
if omr2_addr[0] != 0xfd:
raise verify_utils.VerificationError(f"OMR_2 ({OMR_2_PREFIX}) must start with 0xFD")
if int(pv.vars['OMR_2_PREFIX'].split('/')[-1]) != 64:
raise verify_utils.VerificationError(f"OMR_2 ({pv.vars['OMR_2_PREFIX']}) must be 64 bits long")
pkts.filter(lambda p: verify_utils.check_nwd_prefix_flags(p,
OMR_2_PREFIX,
stable=1,
pref=BR_PREFERENCE_LOW,
r=BR_FLAG_R_TRUE,
o=BR_FLAG_O_TRUE,
p=BR_FLAG_P_TRUE,
s=BR_FLAG_S_TRUE,
d=BR_FLAG_D_FALSE,
dp=BR_FLAG_DP_FALSE)).\
must_next()
# Step 17
# - Device: BR_1 (DUT)
# - Description (DBR-1.8):
# - Automatically multicasts ND RAs on Adjacent Infrastructure Link with OMR_2.
# - Note: the reason that OMR_3 is not advertised, is that OMR_3 was withdrawn and was not originally created by
# BR_1. So BR_1 has no responsibility to advertise this deprecated prefix.
# - Pass Criteria:
# - The DUT MUST multicast ND RAs on the infrastructure link:
# - IPv6 destination MUST be ff02::1
# - MUST NOT contain a Prefix Information Option (PIO) with a ULA prefix.
# - MUST NOT contain a Route Information Option (RIO) with OMR_1.
# - MUST contain a Route Information Option (RIO) with OMR_2.
# - "Prf" bits MUST be 00 (medium) or 11 (low)
# - MUST NOT contain a Route Information Option (RIO) with OMR_3.
print("Step 17: BR_1 (DUT) multicasts ND RA with OMR_2, no OMR_3.")
pkts.filter_eth_src(pv.vars['BR_1_ETH']).\
filter_ipv6_dst("ff02::1").\
filter(lambda p: check_ra_rio(p, OMR_2_PREFIX)).\
filter(lambda p: not check_ra_rio(p, OMR_3_PREFIX)).\
must_next()
# Step 18
# - Device: BR_2
# - Description (DBR-1.8):
# - Harness instructs the device to add new OMR prefix (OMR_4) numerically higher than OMR_2, but with same
# preference "Low".
# - Pass Criteria:
# - N/A
print("Step 18: BR_2 adds OMR_4.")
pkts.filter(lambda p: check_nwd_contains_prefix(p, OMR_4_PREFIX)).\
must_next()
# Step 19
# - Device: BR_1 (DUT)
# - Description (DBR-1.8):
# - No change in behavior for advertising its OMR prefix, OMR_2.
# - Automatically starts advertising a route for the new OMR_4 prefix.
# - Pass Criteria:
# - The DUT MUST NOT send a Network Data update with any one of the following prefixes in a Prefix TLV:
# - OMR_1, OMR_2, OMR_3, OMR_4
# - The DUT MUST multicast ND RAs on the infrastructure link:
# - IPv6 destination MUST be ff02::1
# - MUST NOT contain a Prefix Information Option (PIO) with a ULA prefix.
# - MUST contain a Route Information Option (RIO) with OMR_2.
# - "Prf" bits MUST be 00 (medium) or 11 (low).
# - MUST contain a Route Information Option (RIO) with OMR_4.
# - "Prf" bits MUST be 00 (medium) or 11 (low).
# - MUST NOT contain RIO with any one of OMR_1, OMR_3.
print("Step 19: BR_1 (DUT) continues OMR_2, adds OMR_4 on AIL.")
pkts.filter_eth_src(pv.vars['BR_1_ETH']).\
filter_ipv6_dst("ff02::1").\
filter(lambda p: check_ra_rio(p, OMR_2_PREFIX)).\
filter(lambda p: check_ra_rio(p, OMR_4_PREFIX)).\
must_next()
# Step 20
# - Device: BR_2
# - Description (DBR-1.8):
# - Harness instructs the device to update the numerically higher OMR_4 with a higher preference value 00
# ('Medium').
# - Pass Criteria:
# - N/A
print("Step 20: BR_2 updates OMR_4 to Medium pref.")
pkts.filter(lambda p: verify_utils.check_nwd_prefix_flags(p, OMR_4_PREFIX, pref=BR_PREFERENCE_MEDIUM)).\
must_next()
# Step 21
# - Device: BR_1 (DUT)
# - Description (DBR-1.8): Automatically withdraws its current OMR prefix OMR_2.
# - Pass Criteria:
# - The DUT MUST send a Network Data update. In the new network data:
# - the following prefixes MUST NOT be present in a Prefix TLV: OMR_1, OMR_2, OMR_3
# - The DUT MUST multicast ND RAs on the infrastructure link:
# - IPv6 destination MUST be ff02::1
# - MUST NOT contain a Prefix Information Option (PIO) with a ULA prefix.
# - MUST contain a Route Information Option (RIO) with OMR_2:
# - "Prf" bits MUST be 00 (medium) or 11 (low).
# - MUST contain a Route Information Option (RIO) with OMR_4:
# - "Prf" bits MUST be 00 (medium) or 11 (low).
# - MUST NOT contain a Route Information Option (RIO) with OMR_1 or OMR_3.
print("Step 21: BR_1 (DUT) withdraws OMR_2.")
pkts.filter(lambda p: not check_nwd_contains_prefix(p, OMR_2_PREFIX)).\
must_next()
# Step 22
# - Device: BR_2
# - Description (DBR-1.8):
# - Harness instructs the device to update the prefix OMR_4 to a deprecated state by setting P_preferred =
# 'false'.
# - Note: can use OT command 'netdata publish prefix' for this where the flags do not contain the 'p' (Preferred)
# flag.
# - Pass Criteria:
# - N/A
print("Step 22: BR_2 deprecates OMR_4.")
pkts.filter(lambda p: verify_utils.check_nwd_prefix_flags(p, OMR_4_PREFIX, p=BR_FLAG_P_FALSE)).\
must_next()
# Step 23
# - Device: BR_1 (DUT)
# - Description (DBR-1.8): Automatically starts advertising its own OMR prefix OMR_2 again.
# - Pass Criteria:
# - The DUT MUST register its OMR prefix OMR_2 in the Thread Network Data. Flags in the Border Router sub-TLV
# MUST be:
# - P_preference = 11 (Low)
# - P_default = true
# - P_stable = true
# - P_on_mesh = true
# - P_preferred = true
# - P_slaac = true
# - P_dhcp = false
# - P_dp = false
# - OMR_2 MUST be 64 bits long and start with 0xFD.
# - OMR_2 MUST be equal to the OMR_2 values as used in previous test steps.
print("Step 23: BR_1 (DUT) re-registers OMR_2.")
# OMR_2 MUST be 64 bits long and start with 0xFD.
omr2_addr = Ipv6Addr(OMR_2_PREFIX)
if omr2_addr[0] != 0xfd:
raise verify_utils.VerificationError(f"OMR_2 ({OMR_2_PREFIX}) must start with 0xFD")
if int(pv.vars['OMR_2_PREFIX'].split('/')[-1]) != 64:
raise verify_utils.VerificationError(f"OMR_2 ({pv.vars['OMR_2_PREFIX']}) must be 64 bits long")
pkts.filter(lambda p: verify_utils.check_nwd_prefix_flags(p,
OMR_2_PREFIX,
stable=1,
pref=BR_PREFERENCE_LOW,
r=BR_FLAG_R_TRUE,
o=BR_FLAG_O_TRUE,
p=BR_FLAG_P_TRUE,
s=BR_FLAG_S_TRUE,
d=BR_FLAG_D_FALSE,
dp=BR_FLAG_DP_FALSE)).\
must_next()
# Step 24
# - Device: BR_1 (DUT)
# - Description (DBR-1.8):
# - Automatically multicasts ND RAs on Adjacent Infrastructure Link.
# - Note: even though OMR_4 is deprecated, it is still valid so it is advertised on the AIL in a RIO.
# - Pass Criteria:
# - The DUT MUST multicast ND RAs on the infrastructure link:
# - IPv6 destination MUST be ff02::1
# - MUST NOT contain a Prefix Information Option (PIO) with a ULA prefix.
# - MUST NOT contain a Route Information Option (RIO) with OMR_1.
# - MUST contain a Route Information Option (RIO) with OMR_2.
# - "Prf" bits MUST be 00 (medium) or 11 (low)
# - MUST NOT contain a Route Information Option (RIO) with OMR_3.
# - MUST contain a Route Information Option (RIO) with OMR_4.
# - "Prf" bits MUST be 00 (medium) or 11 (low)
print("Step 24: BR_1 (DUT) multicasts ND RA with OMR_2 and OMR_4.")
pkts.filter_eth_src(pv.vars['BR_1_ETH']).\
filter_ipv6_dst("ff02::1").\
filter(lambda p: check_ra_rio(p, OMR_2_PREFIX)).\
filter(lambda p: check_ra_rio(p, OMR_4_PREFIX)).\
must_next()
# Step 25
# - Device: Eth_1
# - Description (DBR-1.8):
# - Harness instructs the device to send an ICMPv6 Echo Request to ED_1 via BR_1.
# - IPv6 Source: Eth_1 GUA
# - IPv6 Destination: ED_1 OMR address (using prefix OMR_2)
# - Pass Criteria:
# - Eth_1 receives an ICMPv6 Echo Reply from ED_1.
# - IPv6 Source: ED_1 OMR (using prefix OMR_2)
# - IPv6 Destination: Eth_1 GUA
print("Step 25: Eth_1 pings ED_1 OMR_2.")
_pkt = pkts.filter_eth_src(pv.vars['Eth_1_ETH']).\
filter_ipv6_src(ETH_1_GUA).\
filter_ipv6_dst(ED_1_OMR_2).\
filter_ping_request().\
must_next()
pkts.filter(lambda p: p.eth.dst == pv.vars['Eth_1_ETH']).\
filter_ipv6_src(ED_1_OMR_2).\
filter_ipv6_dst(ETH_1_GUA).\
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
must_next()
# Step 26
# - Device: ED_1
# - Description (DBR-1.8):
# - Harness instructs the device to send an ICMPv6 Echo Request to Eth_1.
# - IPv6 Source: ED_1 OMR address (using prefix OMR_2)
# - IPv6 Destination: Eth_1 GUA
# - Pass Criteria:
# - ED_1 receives an ICMPv6 Echo Reply from Eth_1.
# - IPv6 Source: Eth_1 GUA
# - IPv6 Destination: ED_1 OMR address (using prefix OMR_2)
print("Step 26: ED_1 OMR_2 pings Eth_1 GUA.")
_pkt = pkts.filter_ipv6_src(ED_1_OMR_2).\
filter_ipv6_dst(ETH_1_GUA).\
filter_ping_request().\
must_next()
pkts.filter(lambda p: p.ipv6.src == ETH_1_GUA).\
filter_ipv6_dst(ED_1_OMR_2).\
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
must_next()
# Step 27
# - Device: BR_2
# - Description (DBR-1.8):
# - Harness disables the BR function of the device. Or if that is not possible, switch it off.
# - Note: disabling BR may use 'br disable' CLI command.
# - Note: in case of switching off BR_2, the OMR prefix OMR_4 that was advertised by BR_2 remains active for some
# time. The Leader timeout of Leader BR_2 does not happen yet during the below steps of this test.
# - Pass Criteria:
# - N/A
print("Step 27: BR_2 disabled.")
# Step 28
# - Device: Eth_1
# - Description (DBR-1.8):
# - Harness instructs the device to send an ICMPv6 Echo Request to ED_1 via BR_1.
# - IPv6 Source: Eth_1 GUA
# - IPv6 Destination: ED_1 OMR address (using prefix OMR_2)
# - Pass Criteria:
# - Eth_1 receives an ICMPv6 Echo Reply from ED_1.
# - IPv6 Source: ED_1 OMR (using prefix OMR_2)
# - IPv6 Destination: Eth_1 GUA
print("Step 28: Eth_1 pings ED_1 OMR_2.")
_pkt = pkts.filter_eth_src(pv.vars['Eth_1_ETH']).\
filter_ipv6_src(ETH_1_GUA).\
filter_ipv6_dst(ED_1_OMR_2).\
filter_ping_request().\
must_next()
pkts.filter(lambda p: p.eth.dst == pv.vars['Eth_1_ETH']).\
filter_ipv6_src(ED_1_OMR_2).\
filter_ipv6_dst(ETH_1_GUA).\
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
must_next()
# Step 29
# - Device: ED_1
# - Description (DBR-1.8):
# - Harness instructs the device to send an ICMPv6 Echo Request to Eth_1.
# - IPv6 Source: ED_1 OMR address (using prefix OMR_4)
# - IPv6 Destination: Eth_1 GUA.
# - Note: ED_1 is forced here to use a source address based on prefix OMR_4, even though that prefix is
# deprecated. In OT CLI this can most likely be achieved using the command:
# - ping -I <ED_1-OMR_4-address> <Eth_1-GUA>
# - Pass Criteria:
# - N/A
print("Step 29: ED_1 pings Eth_1 using OMR_4.")
_pkt = pkts.filter_ipv6_src(ED_1_OMR_4).\
filter_ipv6_dst(ETH_1_GUA).\
filter_ping_request().\
must_next()
# Step 30
# - Device: Eth_1
# - Description (DBR-1.8):
# - Automatically replies to the ICMPv6 Echo Request with Echo Reply, that is sent to BR_1.
# - Pass Criteria:
# Step 30: Eth_1 replies to OMR_4.
print("Step 30: Eth_1 replies to OMR_4.")
_pkt_step30 = pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).filter(lambda p: (
hasattr(p, 'eth') and p.eth.src == pv.vars['Eth_1_ETH'] and \
hasattr(p, 'ipv6') and \
getattr(p.ipv6, 'dst', None) and Ipv6Addr(p.ipv6.dst)[:8] == Ipv6Addr(OMR_4_PREFIX)[:8] and \
getattr(p.ipv6, 'src', None) and Ipv6Addr(p.ipv6.src) == ETH_1_GUA
)).must_next()
# Step 31
# - Device: BR_1 (DUT)
# - Description (DBR-1.8):
# - Automatically attempts to deliver the Echo Reply to a node on the mesh.
# - Pass Criteria:
# - The DUT MUST attempt to deliver the packet to a node on the mesh, either:
# - 1. BR_1 sends a multicast Address Query for ED_1 OMR_4 based address into the mesh;
# - 2. or ED_1 receives the Echo Reply from Eth_1 as follows:
# - IPv6 Source: Eth_1 GUA
# - IPv6 Destination: ED_1 OMR address (using prefix OMR_4)
print("Step 31: BR_1 delivers Echo Reply for OMR_4.")
def check_step_31(p):
is_aq = (hasattr(p, 'thread_address') and \
hasattr(p.thread_address, 'tlv') and \
getattr(p.thread_address.tlv, 'target_eid', None) and \
Ipv6Addr(p.thread_address.tlv.target_eid)[:8] == Ipv6Addr(OMR_4_PREFIX)[:8])
# We allow matching by IID as well because 6LoWPAN context IDs might
# not be correctly mapped in the verifier's Wireshark preferences.
is_er = (hasattr(p, 'wpan') and \
hasattr(p, 'ipv6') and \
getattr(p.ipv6, 'dst', None) and \
(Ipv6Addr(p.ipv6.dst) == ED_1_OMR_4 or \
Ipv6Addr(p.ipv6.dst)[8:] == Ipv6Addr(ED_1_OMR_4)[8:]) and\
hasattr(p, 'icmpv6') and p.icmpv6.type == consts.ICMPV6_TYPE_ECHO_REPLY)
return is_aq or (is_er and p.wpan.src64 == BR_1 and p.icmpv6.echo.identifier == _pkt.icmpv6.echo.identifier)
pkts.filter(check_step_31).must_next()
# Note: Step 31 must be observed. Even though BR_2 is disabled, the route to OMR_4
# prefix remains in Network Data, and BR_1 should still be able to forward the
# Echo Reply using the EID-to-RLOC cache.
if __name__ == '__main__':
verify_utils.run_main(verify)