[nexus] implement BBR-TC-01 and add infra ICMPv6 echo reply support (#12717)

This commit implements the BBR-TC-01 test case in the Nexus simulation
framework to verify that a Backbone Router (BBR) device automatically
sends its BBR dataset to the Leader if none exists in the network.

To support the Host receiving echo replies on its infrastructure
interface, the Nexus InfraIf class is extended to support custom
ICMPv6 Echo Reply handlers.

Key changes:
- Implement BBR-TC-01 C++ test case and Python verification script.
- Add EchoReplyHandler callback and registration to Nexus InfraIf.
- Update InfraIf::Receive to handle and dispatch ICMPv6 Echo Replies.
- Register 1_2_BBR_TC_1 in CMake and the test runner script.
This commit is contained in:
Jonathan Hui
2026-03-18 19:47:30 -05:00
committed by GitHub
parent 0db2bade37
commit 23caf33af5
6 changed files with 582 additions and 4 deletions
+1
View File
@@ -251,6 +251,7 @@ ot_nexus_test(1_2_MATN_TC_21 "cert;nexus")
ot_nexus_test(1_2_MATN_TC_22 "cert;nexus")
ot_nexus_test(1_2_MATN_TC_23 "cert;nexus")
ot_nexus_test(1_2_MATN_TC_26 "cert;nexus")
ot_nexus_test(1_2_BBR_TC_1 "cert;nexus")
# Misc tests
ot_nexus_test(border_admitter "core;nexus")
+16
View File
@@ -324,6 +324,7 @@ void InfraIf::Receive(Node &aSrcNode, const Ip6::Header &aHeader, Message &aMess
Node &node = GetNode();
bool isIcmp6Nd = false;
bool isEchoRequest = false;
bool isEchoReply = false;
VerifyOrExit(!node.mInfraIf.HasAddress(aHeader.GetSource()));
VerifyOrExit(!node.Get<NetworkData::Leader>().IsOnMesh(aHeader.GetSource()));
@@ -359,6 +360,9 @@ void InfraIf::Receive(Node &aSrcNode, const Ip6::Header &aHeader, Message &aMess
case Ip6::Icmp::Header::kTypeEchoRequest:
isEchoRequest = true;
break;
case Ip6::Icmp::Header::kTypeEchoReply:
isEchoReply = true;
break;
default:
break;
}
@@ -380,6 +384,10 @@ void InfraIf::Receive(Node &aSrcNode, const Ip6::Header &aHeader, Message &aMess
{
HandleEchoRequest(aHeader, aMessage);
}
else if (isEchoReply)
{
HandleEchoReply(aHeader, aMessage);
}
else
{
// We also deliver generic IPv6 packets to the stack if they are NOT ICMPv6 ND packets.
@@ -442,6 +450,14 @@ void InfraIf::HandleEchoRequest(const Ip6::Header &aHeader, Message &aMessage)
mPendingTxQueue.Enqueue(*replyMessage);
}
void InfraIf::HandleEchoReply(const Ip6::Header &aHeader, Message &aMessage)
{
Ip6::Icmp::Header icmpHeader;
SuccessOrQuit(aMessage.Read(sizeof(Ip6::Header), icmpHeader));
mEchoReplyCallback.InvokeIfSet(aHeader.GetSource(), icmpHeader.GetId(), icmpHeader.GetSequence());
}
void InfraIf::GetLinkLayerAddress(LinkLayerAddress &aLinkLayerAddress) const
{
// Use a unique MAC address based on Node ID
+10 -4
View File
@@ -75,6 +75,10 @@ public:
void Receive(Node &aSrcNode, const Ip6::Header &aHeader, Message &aMessage);
void GetLinkLayerAddress(LinkLayerAddress &aLinkLayerAddress) const;
typedef void (*EchoReplyHandler)(void *aContext, const Ip6::Address &aSource, uint16_t aId, uint16_t aSequence);
void SetEchoReplyHandler(EchoReplyHandler aHandler, void *aContext) { mEchoReplyCallback.Set(aHandler, aContext); }
Node &GetNode(void);
const Node &GetNode(void) const;
@@ -84,11 +88,13 @@ private:
void ProcessIcmp6Nd(const Ip6::Address &aSrcAddress, const uint8_t *aBuffer, uint16_t aBufferLength);
void HandlePrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio);
void HandleEchoRequest(const Ip6::Header &aHeader, Message &aMessage);
void HandleEchoReply(const Ip6::Header &aHeader, Message &aMessage);
Node *mNode;
uint32_t mNodeId;
uint32_t mIfIndex;
Heap::Array<Ip6::Address> mAddresses;
Node *mNode;
uint32_t mNodeId;
uint32_t mIfIndex;
Heap::Array<Ip6::Address> mAddresses;
Callback<EchoReplyHandler> mEchoReplyCallback;
};
} // namespace Nexus
+1
View File
@@ -186,6 +186,7 @@ DEFAULT_TESTS=(
"1_2_MATN_TC_22"
"1_2_MATN_TC_23"
"1_2_MATN_TC_26"
"1_2_BBR_TC_1"
)
# Use provided arguments or the default test list
+365
View File
@@ -0,0 +1,365 @@
/*
* 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 BBR registration process, in milliseconds.
*/
static constexpr uint32_t kBbrRegistrationTime = 10 * 1000;
/**
* Time to advance for the MLR registration process, in milliseconds.
*/
static constexpr uint32_t kMlrRegistrationTime = 5 * 1000;
/**
* Time to advance for the ping response, in milliseconds.
*/
static constexpr uint32_t kPingResponseTime = 5 * 1000;
/**
* Leader weight to prevent the DUT from creating its own partition.
*/
static constexpr uint8_t kLeaderWeight = 72;
/**
* Multicast address MA1 used in the test.
*/
static const char kMa1Address[] = "ff04::1234";
/**
* Context for ICMP Echo Response.
*/
struct EchoContext
{
uint16_t mIdentifier;
bool mReceived;
};
/**
* Handler for ICMP Echo Response.
*/
static void HandleEchoResponse(void *aContext, const Ip6::Address &aSource, uint16_t aId, uint16_t aSequence)
{
OT_UNUSED_VARIABLE(aSource);
OT_UNUSED_VARIABLE(aSequence);
EchoContext *context = static_cast<EchoContext *>(aContext);
if (aId == context->mIdentifier)
{
context->mReceived = true;
}
}
/**
* ICMP Echo Request identifier.
*/
static constexpr uint16_t kEchoIdentifier = 0x1234;
/**
* ICMP Echo Request payload size.
*/
static constexpr uint16_t kEchoPayloadSize = 10;
/**
* ICMP Echo Request hop limit.
*/
static constexpr uint8_t kEchoHopLimit = 64;
/**
* Backbone interface index.
*/
static constexpr uint32_t kBackboneIfIndex = 1;
void Test_1_2_BBR_TC_1(void)
{
/**
* 5.11.1 BBR-TC-01: Device MUST send its BBR dataset to Leader if none exists
*
* 5.11.1.1 Topology
* - BR_1 (DUT): BR device operating as a BBR.
* - Leader: Test bed bed device operating as a Thread Leader.
* - Router: Test bed device operating as a Thread Router.
* - Host: Test bed BR device operating as a non-Thread device.
*
* 5.11.1.2 Purpose & Description
* The purpose of this test case is to verify that if no BBR dataset is present in a network (i.e. not sent around
* by Leader), then a BBR function MUST send its BBR dataset to the Leader.
*
* Spec Reference | V1.2 Section
* ----------------|-------------
* BBR Dataset | 5.21.3
*/
Core nexus;
Node &leader = nexus.CreateNode();
Node &router = nexus.CreateNode();
Node &dut = nexus.CreateNode();
Node &host = nexus.CreateNode();
leader.SetName("LEADER");
router.SetName("ROUTER");
dut.SetName("BR_1");
host.SetName("HOST");
nexus.AdvanceTime(0);
Instance::SetLogLevel(kLogLevelNote);
Log("---------------------------------------------------------------------------------------");
Log("Step 0: Topology formation - Leader, Router");
/**
* Step 0
* - Device: N/A
* - Description: Topology formation - Leader, Router
* - Pass Criteria: N/A
*/
leader.Get<Mle::Mle>().SetLeaderWeight(kLeaderWeight);
leader.Form();
nexus.AdvanceTime(kFormNetworkTime);
VerifyOrQuit(leader.Get<Mle::Mle>().IsLeader());
router.Join(leader);
nexus.AdvanceTime(kJoinNetworkTime);
VerifyOrQuit(router.Get<Mle::Mle>().IsRouter());
Log("---------------------------------------------------------------------------------------");
Log("Step 1: Topology addition - BR_1 (DUT)");
/**
* Step 1
* - Device: BR_1 (DUT)
* - Description: Topology addition - BR_1 (DUT). The DUT must be booted and configured join the Thread Network.
* - Pass Criteria:
* - The DUT MUST attach to the Thread Network.
*/
dut.Get<BorderRouter::InfraIf>().Init(kBackboneIfIndex, true);
dut.Get<BorderRouter::RoutingManager>().Init();
SuccessOrQuit(dut.Get<BorderRouter::RoutingManager>().SetEnabled(true));
dut.Get<BackboneRouter::Local>().SetEnabled(true);
dut.Join(leader);
nexus.AdvanceTime(kJoinNetworkTime);
VerifyOrQuit(dut.Get<Mle::Mle>().IsAttached());
Log("---------------------------------------------------------------------------------------");
Log("Step 2: Leader Automatically sends Network Data with no BBR Dataset.");
/**
* Step 2
* - Device: Leader
* - Description: Automatically sends Network Data with no BBR Dataset.
* - Pass Criteria: N/A
*/
Log("---------------------------------------------------------------------------------------");
Log("Step 3: BR_1 (DUT) sends its BBR Dataset to the Leader.");
/**
* Step 3
* - Device: BR_1 (DUT)
* - Description: Checks received Network Data and automatically determines that it needs to send its BBR Dataset
* to the Leader to become primary BBR.
* - Pass Criteria:
* - The DUT MUST unicast a SVR_DATA.ntf CoAP notification to Leader.
*/
Log("---------------------------------------------------------------------------------------");
Log("Step 4: Leader responds to the Server Data notification.");
/**
* Step 4
* - Device: Leader
* - Description: Automatically responds to the Server Data notification.
* - Pass Criteria: N/A
*/
Log("---------------------------------------------------------------------------------------");
Log("Step 5: Leader multicasts MLE Data Response message.");
/**
* Step 5
* - Device: Leader
* - Description: Automatically multicasts MLE Data Response message with BR_1's short address in the BBR Dataset,
* electing BR_1 as Primary.
* - Pass Criteria: N/A
*/
nexus.AdvanceTime(kBbrRegistrationTime);
Log("---------------------------------------------------------------------------------------");
Log("Step 6: BR_1 (DUT) becomes the Primary BBR.");
/**
* Step 6
* - Device: BR_1 (DUT)
* - Description: Automatically acknowledges the presence of its short address in the BBR Dataset and becomes the
* Primary BBR.
* - Pass Criteria: None
*/
VerifyOrQuit(dut.Get<BackboneRouter::Local>().GetState() == BackboneRouter::Local::kStatePrimary);
Log("---------------------------------------------------------------------------------------");
Log("Step 7: Router registers the multicast address, MA1, at BR_1 (DUT).");
/**
* Step 7
* - Device: Router
* - Description: Harness instructs the device to register the multicast address, MA1, at BR_1 (DUT). Unicasts an
* MLR.req CoAP request to BR_1 (DUT).
* - Pass Criteria: N/A
*/
Ip6::Address ma1;
SuccessOrQuit(ma1.FromString(kMa1Address));
SuccessOrQuit(router.Get<ThreadNetif>().SubscribeExternalMulticast(ma1));
Log("---------------------------------------------------------------------------------------");
Log("Step 8: BR_1 (DUT) internally registers the multicast address MA1.");
/**
* Step 8
* - Device: BR_1 (DUT)
* - Description: Automatically internally registers the multicast address MA1 in its Multicast Listeners Table.
* - Pass Criteria: None.
*/
Log("---------------------------------------------------------------------------------------");
Log("Step 9: BR_1 (DUT) responds to the multicast listener registration.");
/**
* Step 9
* - Device: BR_1 (DUT)
* - Description: Automatically responds to the multicast listener registration.
* - Pass Criteria:
* - The DUT MUST unicast a MLR.rsp CoAP response to Router.
*/
nexus.AdvanceTime(kMlrRegistrationTime);
VerifyOrQuit(dut.Get<BackboneRouter::MulticastListenersTable>().Has(ma1));
Log("---------------------------------------------------------------------------------------");
Log("Step 10: BR_1 (DUT) multicasts an MLDv2 message.");
/**
* Step 10
* - Device: BR_1 (DUT)
* - Description: Automatically multicasts an MLDv2 message to start listening to the group MA1.
* - Pass Criteria:
* - The DUT MUST multicast an MLDv2 message of type "Version 2 Multicast Listener Report".
*/
Log("---------------------------------------------------------------------------------------");
Log("Step 11: Host sends ICMPv6 Echo Request packet to destination MA1.");
/**
* Step 11
* - Device: Host
* - Description: Harness instructs the device to sends ICMPv6 Echo (ping) Request packet to destination MA1.
* - Pass Criteria: N/A
*/
const Ip6::Address &hostAddr = host.mInfraIf.FindMatchingAddress("fd00::/8");
nexus.AddTestVar("HOST_BACKBONE_ULA", hostAddr.ToString().AsCString());
EchoContext echoContext;
echoContext.mIdentifier = kEchoIdentifier;
echoContext.mReceived = false;
host.mInfraIf.SetEchoReplyHandler(HandleEchoResponse, &echoContext);
host.mInfraIf.SendEchoRequest(hostAddr, ma1, kEchoIdentifier, kEchoPayloadSize, kEchoHopLimit);
Log("---------------------------------------------------------------------------------------");
Log("Step 12: BR_1 (DUT) determines that the multicast packet needs to be forwarded.");
/**
* Step 12
* - Device: BR_1 (DUT)
* - Description: Internally determines that the multicast packet to MA1 needs to be forwarded.
* - Pass Criteria: None.
*/
Log("---------------------------------------------------------------------------------------");
Log("Step 13: BR_1 (DUT) forwards the multicast ping request packet.");
/**
* Step 13
* - Device: BR_1 (DUT)
* - Description: Automatically forwards the multicast ping request packet to Router.
* - Pass Criteria: Not explicitly validated in this step.
*/
Log("---------------------------------------------------------------------------------------");
Log("Step 14: Router receives ping request packet and responds back to Host.");
/**
* Step 14
* - Device: Router
* - Description: Receives ping request packet and automatically responds back to Host via BR_1.
* - Pass Criteria: Receives the ping packet sent by Host.
*/
nexus.AdvanceTime(kPingResponseTime);
VerifyOrQuit(echoContext.mReceived);
nexus.SaveTestInfo("test_1_2_BBR_TC_1.json");
}
} // namespace Nexus
} // namespace ot
int main(void)
{
ot::Nexus::Test_1_2_BBR_TC_1();
printf("All tests passed\n");
return 0;
}
+189
View File
@@ -0,0 +1,189 @@
#!/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
def verify(pv):
# 5.11.1 BBR-TC-01: Device MUST send its BBR dataset to Leader if none exists
#
# 5.11.1.1 Topology
# - BR_1 (DUT): BR device operating as a BBR.
# - Leader: Test bed bed device operating as a Thread Leader.
# - Router: Test bed device operating as a Thread Router.
# - Host: Test bed BR device operating as a non-Thread device.
#
# 5.11.1.2 Purpose and Description
# The purpose of this test case is to verify that if no BBR dataset is present in a network (i.e. not sent around
# by Leader), then a BBR function MUST send its BBR dataset to the Leader.
#
# Spec Reference | V1.2 Section
# ----------------|-------------
# BBR Dataset | 5.21.3
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
BR_1 = pv.vars['BR_1']
ROUTER = pv.vars['ROUTER']
BR_1_RLOC16 = pv.vars['BR_1_RLOC16']
MA1 = Ipv6Addr("ff04::1234")
# Step 1: Topology addition - BR_1 (DUT)
# - Description: Topology addition - BR_1 (DUT). The DUT must be booted and configured join the Thread Network.
# - Pass Criteria:
# - The DUT MUST attach to the Thread Network.
print("Step 1: BR_1 (DUT) attaches to the Thread Network.")
pkts.filter_wpan_src64(BR_1).\
filter_mle_cmd(consts.MLE_CHILD_ID_REQUEST).\
must_next()
# Step 2: Leader
# - Description: Automatically sends Network Data with no BBR Dataset.
# - Pass Criteria: N/A
print("Step 2: Leader sends Network Data with no BBR Dataset.")
# Step 3: BR_1 (DUT)
# - Description: Checks received Network Data and automatically determines that it needs to send its BBR Dataset
# to the Leader to become primary BBR.
# - Pass Criteria:
# - The DUT MUST unicast a SVR_DATA.ntf CoAP notification to Leader.
# - Where the payload contains (amongst other fields): Service TLV: BBR Dataset with T=1, S_service_data = 0x01
# - Server TLV: BBR Dataset containing BR_1 RLOC
print("Step 3: BR_1 (DUT) sends SVR_DATA.ntf CoAP notification to Leader.")
# We just verify that BR_1 sends a Server Data notification to the Leader.
# The content is verified in Step 5 when the Leader multicasts the new Network Data.
pkts.filter_wpan_src64(BR_1).\
filter_coap_request(consts.SVR_DATA_URI).\
must_next()
# Step 4: Leader
# - Description: Automatically responds to the Server Data notification.
# - Pass Criteria: N/A
print("Step 4: Leader responds to SVR_DATA.ntf.")
pkts.filter_wpan_src64(LEADER).\
filter_coap_ack(consts.SVR_DATA_URI).\
must_next()
# Step 5: Leader
# - Description: Automatically multicasts MLE Data Response message with BR_1's short address in the BBR Dataset,
# electing BR_1 as Primary.
# - Pass Criteria: N/A
print("Step 5: Leader multicasts MLE Data Response electing BR_1 as Primary.")
pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter_mle_cmd(consts.MLE_DATA_RESPONSE).\
filter(lambda p: consts.NWD_SERVICE_TLV in p.thread_nwd.tlv.type).\
filter(lambda p: BR_1_RLOC16 in p.thread_nwd.tlv.server_16).\
must_next()
# Step 6: BR_1 (DUT)
# - Description: Automatically acknowledges the presence of its short address in the BBR Dataset and becomes the
# Primary BBR.
# - Pass Criteria: None
print("Step 6: BR_1 (DUT) becomes Primary BBR.")
# Step 7: Router
# - Description: Harness instructs the device to register the multicast address, MA1, at BR_1 (DUT). Unicasts an
# MLR.req CoAP request to BR_1 (DUT).
# - Pass Criteria: N/A
print("Step 7: Router registers MA1 at BR_1 (DUT).")
pkts.filter_wpan_src64(ROUTER).\
filter_coap_request('/n/mr').\
filter(lambda p: MA1 in p.coap.tlv.ipv6_address).\
must_next()
# Step 8: BR_1 (DUT)
# - Description: Automatically internally registers the multicast address MA1 in its Multicast Listeners Table.
# - Pass Criteria: None.
print("Step 8: BR_1 (DUT) registers MA1 internally.")
# Step 9: BR_1 (DUT)
# - Description: Automatically responds to the multicast listener registration.
# - Pass Criteria:
# - The DUT MUST unicast a MLR.rsp CoAP response to Router.
print("Step 9: BR_1 (DUT) responds with MLR.rsp.")
pkts.filter_wpan_src64(BR_1).\
filter_coap_ack('/n/mr').\
filter(lambda p: p.coap.tlv.status == 0).\
must_next()
# Step 10: BR_1 (DUT)
# - Description: Automatically multicasts an MLDv2 message to start listening to the group MA1.
# - Pass Criteria:
# - The DUT MUST multicast an MLDv2 message of type "Version 2 Multicast Listener Report".
#
# Note: OpenThread does not currently implement sending MLDv2 messages in this simulation environment.
print("Step 10: BR_1 (DUT) multicasts MLDv2 message (SKIPPED).")
# Step 11: Host
# - Description: Harness instructs the device to sends ICMPv6 Echo (ping) Request packet to destination MA1.
# - Pass Criteria: N/A
print("Step 11: Host sends ICMPv6 Echo Request to MA1.")
pkts.filter_eth_src(pv.vars['HOST_ETH']).\
filter_ipv6_dst(MA1).\
filter_ping_request().\
must_next()
# Step 12: BR_1 (DUT)
# - Description: Internally determines that the multicast packet to MA1 needs to be forwarded.
# - Pass Criteria: None.
print("Step 12: BR_1 (DUT) determines forwarding needed.")
# Step 13: BR_1 (DUT)
# - Description: Automatically forwards the multicast ping request packet to Router.
# - Pass Criteria: Not explicitly validated in this step.
print("Step 13: BR_1 (DUT) forwards the multicast ping.")
pkts.filter_wpan_src64(BR_1).\
filter_ping_request().\
filter(lambda p: p.ipv6.dst == 'ff03::fc' or p.ipv6.dst == MA1).\
must_next()
# Step 14: Router receives ping request packet and automatically responds back to Host via BR_1.
# - Pass Criteria: Receives the ping packet sent by Host.
print("Step 14: Router receives ping and responds.")
pkts.filter_eth_src(pv.vars['BR_1_ETH']).\
filter(lambda p: p.ipv6.dst == Ipv6Addr(pv.vars['HOST_BACKBONE_ULA'])).\
filter_ping_reply().\
must_next()
if __name__ == '__main__':
verify_utils.run_main(verify)