Files
openthread/tests/nexus/verify_1_2_MATN_TC_5.py
Jonathan Hui 725b101150 [nexus] add MATN-TC-05 test case for multicast re-registration (#12693)
Implement Thread 1.2 test MATN-TC-05: Re-registration to same Multicast
Group. This test verifies that a Primary Backbone Router (BBR)
correctly manages multicast address re-registration and handles UDP
multicast traffic between the backbone and Thread network.

Key additions:
- Added SendUdp to Nexus InfraIf to support simulated UDP multicast
  traffic from backbone hosts.
- Implemented test_1_2_MATN_TC_5.cpp to simulate the network topology
  (DUT, BR_2, Router, and Host) and the test steps.
- Implemented verify_1_2_MATN_TC_5.py for pcap-based verification of
  multicast forwarding and BBR timeout behavior.
- Integrated the new test into the Nexus build system and the default
  test execution script.
2026-03-16 18:57:51 -05:00

236 lines
9.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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
import struct
# 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.bytes import Bytes
# CoAP port.
COAP_PORT = 5683
# Status SUCCESS
ST_MLR_SUCCESS = 0
def verify(pv):
# 5.10.5 MATN-TC-05: Re-registration to same Multicast Group
#
# 5.10.5.1 Topology
# - BR_1 (DUT)
# - BR_2
# - Router
# - Host
#
# 5.10.5.2 Purpose & Description
# Verify that a Primary BBR (DUT) can manage a re-registration of a device on its network to remain receiving
# multicasts. The test also verifies the usage of UDP multicast packets across backbone and internal Thread
# network.
#
# Spec Reference | V1.2 Section | V1.3.0 Section
# -----------------|--------------|---------------
# Multicast | 5.10.5 | 5.10.5
pkts = pv.pkts
pv.summary.show()
BR_1 = pv.vars['BR_1']
MA1 = pv.vars['MA1']
HOST_ULA = pv.vars['HOST_ULA']
BR_1_RLOC = pv.vars['BR_1_RLOC']
BR_1_RLOC16 = pv.vars['BR_1_RLOC16']
def check_mpl(p):
if p.ipv6.src == Ipv6Addr(BR_1_RLOC):
return p.ipv6.opt.mpl.flag.s == 0
else:
return p.ipv6.opt.mpl.flag.s == 1 and \
p.ipv6.opt.mpl.seed_id == Bytes(struct.pack('>H', BR_1_RLOC16))
# Step 0
# - Device: BR_1 (DUT)
# - Description: Topology formation – BR_1 (DUT). Boot DUT. Configure the MLR timeout value of the DUT to be
# MLR_TIMEOUT_MIN seconds.
# - Pass Criteria:
# - N/A
print("Step 0: Topology formation – BR_1 (DUT)")
# Step 0 (cont.)
# - Device: N/A
# - Description: Topology formation – BR_2, Router
# - Pass Criteria:
# - N/A
print("Step 0 (cont.): Topology formation – BR_2, Router")
# Step 1
# - Device: Router
# - Description: Harness instructs the device to register multicast address, MA1
# - Pass Criteria:
# - N/A
print("Step 1: Router registers multicast address, MA1")
pkts.filter_coap_request('/n/mr').\
filter(lambda p: p.ipv6.dst == Ipv6Addr(BR_1_RLOC) or \
Ipv6Addr(p.ipv6.dst).endswith(Bytes('000000fffe00fc00'))).\
filter(lambda p: MA1 in p.coap.tlv.ipv6_address).\
must_next()
pkts.filter_coap_ack('/n/mr').\
filter(lambda p: p.coap.tlv.status == ST_MLR_SUCCESS).\
must_next()
# Step 2
# - Device: Host
# - Description: Harness instructs the device to send an ICMPv6 Echo (ping) Request packet to the multicast
# address, MA1.
# - Pass Criteria:
# - N/A
print("Step 2: Host sends an ICMPv6 Echo (ping) Request packet to the multicast address, MA1.")
pkts.filter_ping_request().\
filter(lambda p: p.ipv6.src == Ipv6Addr(HOST_ULA)).\
filter(lambda p: p.ipv6.dst == Ipv6Addr(MA1)).\
must_next()
# Step 3
# - Device: BR_1 (DUT)
# - Description: Automatically forwards the ping request packet to its Thread Network.
# - Pass Criteria:
# - The DUT MUST forward the ICMPv6 Echo (ping) Request packet with multicast address, MA1, to its Thread
# Network encapsulated in an MPL packet.
print("Step 3: BR_1 (DUT) automatically forwards the ping request packet to its Thread Network.")
pkts.filter_wpan_src64(BR_1).\
filter_ping_request().\
filter(lambda p: p.ipv6.dst == 'ff03::fc' or \
p.ipv6.dst == Ipv6Addr(MA1)).\
filter(check_mpl).\
must_next()
# Step 4
# - Device: Router
# - Description: Receives the MPL packet containing an encapsulated ping request packet to MA1, sent by Host, and
# automatically unicasts an ICMPv6 (ping) Reply packet back to Host.
# - Pass Criteria:
# - N/A
print("Step 4: Router automatically unicasts an ICMPv6 (ping) Reply packet back to Host.")
# Step 4a
# - Device: Router
# - Description: Within MLR_TIMEOUT_MIN seconds of initial registration, Harness instructs the device to
# re-register for multicast address, MA1, at BR_1 by performing test case MATN-TC-02 (see 5.10.2) using Router
# as TD with the respective pass criteria.
# - Pass Criteria:
# - N/A
# - Note: MLDv2 and BLMR.ntf checks are intentionally skipped because they are part of MATN-TC-02.
print("Step 4a: Router re-registers for multicast address, MA1, at BR_1.")
pkts.filter_coap_request('/n/mr').\
filter(lambda p: p.ipv6.dst == Ipv6Addr(BR_1_RLOC) or \
Ipv6Addr(p.ipv6.dst).endswith(Bytes('000000fffe00fc00'))).\
filter(lambda p: MA1 in p.coap.tlv.ipv6_address).\
must_next()
pkts.filter_coap_ack('/n/mr').\
filter(lambda p: p.coap.tlv.status == ST_MLR_SUCCESS).\
must_next()
# Step 5
# - Device: Host
# - Description: Within MLR_TIMEOUT_MIN seconds, Harness instructs the device to send an ICMPv6 Echo (ping) Request
# packet to the multicast address, MA1. The destination port 5683 is used for the UDP Multicast packet
# transmission.
# - Pass Criteria:
# - N/A
print("Step 5: Host sends an ICMPv6 Echo (ping) Request packet (UDP) to the multicast address, MA1, port 5683.")
pkts.filter(lambda p: p.ipv6.src == Ipv6Addr(HOST_ULA)).\
filter(lambda p: p.ipv6.dst == Ipv6Addr(MA1)).\
filter(lambda p: p.udp.dstport == COAP_PORT).\
must_next()
# Step 6
# - Device: BR_1 (DUT)
# - Description: Automatically forwards the UDP ping request packet to its Thread Network.
# - Pass Criteria:
# - The DUT MUST forward the UDP Echo (ping) Request packet with multicast address, MA1, to its Thread Network
# encapsulated in an MPL packet.
print("Step 6: BR_1 (DUT) automatically forwards the UDP ping request packet to its Thread Network.")
pkts.filter_wpan_src64(BR_1).\
filter(lambda p: p.ipv6.dst == 'ff03::fc' or \
p.ipv6.dst == Ipv6Addr(MA1)).\
filter(lambda p: p.udp.dstport == COAP_PORT).\
filter(check_mpl).\
must_next()
# Step 7
# - Device: Router
# - Description: Receives the ping request packet. Automatically uses the port 5683 (CoAP port) to verify that the
# UDP Multicast packet is received.
# - Pass Criteria:
# - N/A
print("Step 7: Router receives the UDP multicast packet.")
# Step 7a
# - Device: Router
# - Description: Harness instructs the device to stop listening to the multicast address, MA1.
# - Pass Criteria:
# - N/A
print("Step 7a: Router stops listening to the multicast address, MA1.")
# Step 8
# - Device: Host
# - Description: After (MLR_TIMEOUT_MIN+2) seconds, harness instructs the device to multicast an ICMPv6 Echo (ping)
# Request packet to multicast address, MA1, on the backbone link.
# - Pass Criteria:
# - N/A
print("Step 8: After (MLR_TIMEOUT_MIN+2) seconds, Host multicasts an ICMPv6 Echo Request to MA1.")
pkts.filter_ping_request().\
filter(lambda p: p.ipv6.src == Ipv6Addr(HOST_ULA)).\
filter(lambda p: p.ipv6.dst == Ipv6Addr(MA1)).\
must_next()
# Step 9
# - Device: BR_1 (DUT)
# - Description: Does not forward the ping request packet to its Thread Network.
# - Pass Criteria:
# - The DUT MUST NOT forward the ICMPv6 Echo (ping) Request packet with multicast address, MA1, to its Thread
# Network.
print("Step 9: BR_1 (DUT) does not forward the ping request packet to its Thread Network.")
pkts.filter_wpan_src64(BR_1).\
filter_ping_request().\
filter(lambda p: p.ipv6.dst == 'ff03::fc' or p.ipv6.dst == Ipv6Addr(MA1)).\
must_not_next()
if __name__ == '__main__':
verify_utils.run_main(verify)