Files
openthread/tests/nexus/verify_1_4_TREL_TC_2.py
Jonathan Hui d0949e1e92 [nexus] add 1_4_TREL_TC_2 for TREL multi-hop routing (#12804)
This commit adds Nexus test case 1_4_TREL_TC_2, which verifies 6LoWPAN
mesh header forwarding and fragmentation over multi-hop paths involving
both 15.4 and TREL radio links, according to the Thread 1.4 spec.

The implementation includes:
- tests/nexus/test_1_4_TREL_TC_2.cpp: Sets up a complex topology with
  a multi-radio Border Router (DUT), a multi-radio Leader, and several
  Routers and End Devices with varying radio capabilities (15.4-only
  or multi-radio). It triggers pings with large payloads (500B) to
  verify fragmentation and multi-hop routing through the DUT.
- tests/nexus/verify_1_4_TREL_TC_2.py: Verifies that packets follow the
  expected multi-hop path, checking that TREL is used for infrastructure
  segments (UDP) and 15.4 is used for Thread-only segments. It also
  ensures that 6LoWPAN fragmentation and mesh headers are correctly
  handled by the DUT when forwarding between different radio types.
- Updated tests/nexus/CMakeLists.txt and tests/nexus/run_nexus_tests.sh
  to include the new test.

This test ensures that the Thread stack correctly handles multi-hop
routing and MTU differences across heterogeneous radio links.
2026-03-31 12:30:43 -05:00

496 lines
22 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
# 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
def verify(pv):
# 8.2. [1.4] [CERT] Multi-hop routing with device (in the path) with different radio types and MTUs
#
# 8.2.1. Purpose
# This test covers the use of 6LoWPAN “Mesh Header” messages (messages sent over multi-hop) when
# underlying routes (in the path) can support different radio link types with different frame MTU sizes.
# Different types of devices (15.4, TREL, Router, End Device) are connected to the DUT.
#
# Spec Reference | V1.1 Section | V1.3.0 Section
# -------------------------|--------------|---------------
# TREL / Multi-radio Links | N/A | 8.2
pkts = pv.pkts
pv.summary.show()
ROUTER_1 = pv.vars['ROUTER_1'] # DUT
LEADER = pv.vars['LEADER']
ROUTER_2 = pv.vars['ROUTER_2']
ED_1 = pv.vars['ED_1']
ROUTER_3 = pv.vars['ROUTER_3']
ROUTER_4 = pv.vars['ROUTER_4']
ROUTER_1_RLOC16 = pv.vars['ROUTER_1_RLOC16']
LEADER_RLOC16 = pv.vars['LEADER_RLOC16']
ROUTER_2_RLOC16 = pv.vars['ROUTER_2_RLOC16']
ROUTER_3_RLOC16 = pv.vars['ROUTER_3_RLOC16']
ROUTER_4_RLOC16 = pv.vars['ROUTER_4_RLOC16']
ED_1_RLOC16 = pv.vars['ED_1_RLOC16']
ROUTER_1_ETH = pv.vars['ROUTER_1_ETH']
LEADER_ETH = pv.vars['LEADER_ETH']
ROUTER_4_ETH = pv.vars['ROUTER_4_ETH']
ROUTER_1_TREL_PORT = pv.vars['ROUTER_1_TREL_PORT']
LEADER_TREL_PORT = pv.vars['LEADER_TREL_PORT']
ROUTER_4_TREL_PORT = pv.vars['ROUTER_4_TREL_PORT']
ROUTER_1_MLEID = pv.vars['ROUTER_1_MLEID']
LEADER_MLEID = pv.vars['LEADER_MLEID']
ROUTER_2_MLEID = pv.vars['ROUTER_2_MLEID']
ED_1_MLEID = pv.vars['ED_1_MLEID']
ROUTER_3_MLEID = pv.vars['ROUTER_3_MLEID']
ROUTER_4_MLEID = pv.vars['ROUTER_4_MLEID']
# Step 1
# - Device: Leader, Router_1 (DUT), Router_2, ED_1, Router_3, Router_4
# - Description (TREL-8.2): Form the topology. Wait for Router_1, Router_2, Router_3 and Router_4 to become
# routers. Note: any devices not connected by line in the topology figure MUST NOT be able to communicate
# directly via a Thread Link or TREL link and not be in each other’s neighbor table (e.g. this may be realized
# by using the allow/deny list mechanism). ED_1 MUST be configured as a non-sleepy MTD child (mode `rn`). Note:
# ED_1 MUST attach to the Router_1 (DUT) as its parent and not the other routers (e.g., can be realized by using
# denylist on ED_1). Note: The requirement for ED_1 to attach as a non-sleepy MTD child to Router_1 (DUT) is
# important to ensure messages from ED_1 are sent to its parent as is without mesh header encapsulation, to
# then be forwarded within the mesh and that the fragmentation and adding of 6LoWPAN “mesh header” is performed
# by Router_1 (DUT) - which is being verified in the next step.
print("Step 1: Form the topology")
pkts.copy().\
filter_wpan_src64(ROUTER_2).\
filter_wpan_dst64(ROUTER_1).\
must_not_next()
pkts.copy().\
filter_wpan_src64(ROUTER_1).\
filter_wpan_dst64(ROUTER_2).\
must_not_next()
# Step 2
# - Device: ED_1
# - Description (TREL-8.2): Harness instructs device to send a ping of payload size of 500 bytes or more to
# destination Router_2 using the ML-EID address of Router_2 as the destination address. Note: this verifies the
# DUT behavior of forwarding 6LoWPAN fragmented frames from a Child (incoming over 15.4) over multiple hops.
# - Pass Criteria:
# - ED_1 MUST successfully receive a ping reply from Router_2, routed via DUT.
# - TREL UDP frames MUST be used for ping/ping-reply transport between DUT and Leader, and MUST be correct TREL
# frames.
print("Step 2: ED_1 pings Router_2 ML-EID")
# Path: ED_1 -(15.4)-> ROUTER_1 -(TREL)-> LEADER -(15.4)-> ROUTER_2
# 1. Request from ED_1 to ROUTER_1 over 15.4
_pkt_154_req = pkts.copy().\
filter_ping_request().\
filter_ipv6_src_dst(ED_1_MLEID, ROUTER_2_MLEID).\
filter_wpan_src16_dst16(ED_1_RLOC16, ROUTER_1_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
_identifier = _pkt_154_req.icmpv6.echo.identifier
# 2. Forwarded request on TREL (ROUTER_1 to LEADER)
pkts.range(pkts.index).\
filter_eth_src(ROUTER_1_ETH).\
filter(lambda p: p.eth.dst == LEADER_ETH).\
filter(lambda p: p.udp.dstport == LEADER_TREL_PORT).\
filter(lambda p: p.trel.source_addr == ROUTER_1 and p.trel.destination_addr == LEADER).\
filter(lambda p: p.trel.wpan.src16 == ROUTER_1_RLOC16 and p.trel.wpan.dst16 == LEADER_RLOC16).\
must_next()
# 3. Forwarded request on 15.4 (LEADER to ROUTER_2)
pkts.range(pkts.index).\
filter_ping_request(identifier=_identifier).\
filter_ipv6_src_dst(ED_1_MLEID, ROUTER_2_MLEID).\
filter_wpan_src16_dst16(LEADER_RLOC16, ROUTER_2_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
# 4. Reply from Router_2 to Leader over 15.4
pkts.copy().\
filter_ping_reply(identifier=_identifier).\
filter_ipv6_src_dst(ROUTER_2_MLEID, ED_1_MLEID).\
filter_wpan_src16_dst16(ROUTER_2_RLOC16, LEADER_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
# 5. Forwarded reply on TREL (LEADER to ROUTER_1)
pkts.range(pkts.index).\
filter_eth_src(LEADER_ETH).\
filter(lambda p: p.eth.dst == ROUTER_1_ETH).\
filter(lambda p: p.udp.dstport == ROUTER_1_TREL_PORT).\
filter(lambda p: p.trel.source_addr == LEADER and p.trel.destination_addr == ROUTER_1).\
filter(lambda p: p.trel.wpan.src16 == LEADER_RLOC16 and p.trel.wpan.dst16 == ROUTER_1_RLOC16).\
must_next()
# 6. Forwarded reply on 15.4 (ROUTER_1 to ED_1)
pkts.range(pkts.index).\
filter_ping_reply(identifier=_identifier).\
filter_ipv6_src_dst(ROUTER_2_MLEID, ED_1_MLEID).\
filter_wpan_src16_dst16(ROUTER_1_RLOC16, ED_1_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
# Step 3
# - Device: Router_2
# - Description (TREL-8.2): Harness instructs device to send a ping of payload size of 500 bytes or more from
# Router_2 to ED_1 using the ML-EID address of ED_1 as the destination address.
# - Pass Criteria:
# - Router_2 MUST successfully receive a ping reply from ED_1, routed via the DUT.
# - TREL UDP frames MUST be used for ping/ping-reply transport between DUT and Leader, and MUST be correct TREL
# frames.
print("Step 3: Router_2 pings ED_1 ML-EID")
# Path: ROUTER_2 -(15.4)-> LEADER -(TREL)-> ROUTER_1 -(15.4)-> ED_1
# 1. Request from ROUTER_2 to LEADER over 15.4
_pkt_154_req = pkts.copy().\
filter_ping_request().\
filter_ipv6_src_dst(ROUTER_2_MLEID, ED_1_MLEID).\
filter_wpan_src16_dst16(ROUTER_2_RLOC16, LEADER_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
_identifier = _pkt_154_req.icmpv6.echo.identifier
# 2. Forwarded request on TREL (LEADER to ROUTER_1)
pkts.range(pkts.index).\
filter_eth_src(LEADER_ETH).\
filter(lambda p: p.eth.dst == ROUTER_1_ETH).\
filter(lambda p: p.udp.dstport == ROUTER_1_TREL_PORT).\
filter(lambda p: p.trel.source_addr == LEADER and p.trel.destination_addr == ROUTER_1).\
filter(lambda p: p.trel.wpan.src16 == LEADER_RLOC16 and p.trel.wpan.dst16 == ROUTER_1_RLOC16).\
must_next()
# 3. Forwarded request on 15.4 (ROUTER_1 to ED_1)
pkts.range(pkts.index).\
filter_ping_request(identifier=_identifier).\
filter_ipv6_src_dst(ROUTER_2_MLEID, ED_1_MLEID).\
filter_wpan_src16_dst16(ROUTER_1_RLOC16, ED_1_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
# 4. Reply from ED_1 to ROUTER_1 over 15.4
pkts.copy().\
filter_ping_reply(identifier=_identifier).\
filter_ipv6_src_dst(ED_1_MLEID, ROUTER_2_MLEID).\
filter_wpan_src16_dst16(ED_1_RLOC16, ROUTER_1_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
# 5. Forwarded reply on TREL (ROUTER_1 to LEADER)
pkts.range(pkts.index).\
filter_eth_src(ROUTER_1_ETH).\
filter(lambda p: p.eth.dst == LEADER_ETH).\
filter(lambda p: p.udp.dstport == LEADER_TREL_PORT).\
filter(lambda p: p.trel.source_addr == ROUTER_1 and p.trel.destination_addr == LEADER).\
filter(lambda p: p.trel.wpan.src16 == ROUTER_1_RLOC16 and p.trel.wpan.dst16 == LEADER_RLOC16).\
must_next()
# 6. Forwarded reply on 15.4 (LEADER to ROUTER_2)
pkts.range(pkts.index).\
filter_ping_reply(identifier=_identifier).\
filter_ipv6_src_dst(ED_1_MLEID, ROUTER_2_MLEID).\
filter_wpan_src16_dst16(LEADER_RLOC16, ROUTER_2_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
# Step 4
# - Device: Router_4
# - Description (TREL-8.2): Harness instructs device to send a ping of payload size of 500 bytes or more from
# Router_4 to destination Router_2 using the ML-EID address of Router_2 as the destination address. Warning: see
# TESTPLAN-626 for an open issue (TBD) in this step. Note: this intends to verify the DUT behavior of
# forwarding 6LoWPAN “Mesh Header” messages, incoming over TREL, over multi-hop routes.
# - Pass Criteria:
# - Router_4 MUST successfully receive a ping reply from Router_2, routed via the DUT.
# - TREL UDP frames MUST be used for ping/ping-reply transport between DUT and Leader, and MUST be correct TREL
# frames.
# - TREL UDP frames MUST be used for ping/ping-reply transport between the DUT and Router_4, and MUST be correct
# TREL frames.
print("Step 4: Router_4 pings Router_2 ML-EID")
# Path: ROUTER_4 -(TREL)-> ROUTER_1 -(TREL)-> LEADER -(15.4)-> ROUTER_2
# 1. Request from ROUTER_4 to ROUTER_1 over TREL
pkts.copy().\
filter_eth_src(ROUTER_4_ETH).\
filter(lambda p: p.eth.dst == ROUTER_1_ETH).\
filter(lambda p: p.udp.dstport == ROUTER_1_TREL_PORT).\
filter(lambda p: p.trel.source_addr == ROUTER_4 and p.trel.destination_addr == ROUTER_1).\
filter(lambda p: p.trel.wpan.src16 == ROUTER_4_RLOC16 and p.trel.wpan.dst16 == ROUTER_1_RLOC16).\
must_next()
# 2. Forwarded request from ROUTER_1 to LEADER over TREL
pkts.range(pkts.index).\
filter_eth_src(ROUTER_1_ETH).\
filter(lambda p: p.eth.dst == LEADER_ETH).\
filter(lambda p: p.udp.dstport == LEADER_TREL_PORT).\
filter(lambda p: p.trel.source_addr == ROUTER_1 and p.trel.destination_addr == LEADER).\
filter(lambda p: p.trel.wpan.src16 == ROUTER_1_RLOC16 and p.trel.wpan.dst16 == LEADER_RLOC16).\
must_next()
# 3. Forwarded request on 15.4 (LEADER to ROUTER_2)
_pkt_154_req = pkts.range(pkts.index).\
filter_ping_request().\
filter_ipv6_src_dst(ROUTER_4_MLEID, ROUTER_2_MLEID).\
filter_wpan_src16_dst16(LEADER_RLOC16, ROUTER_2_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
_identifier = _pkt_154_req.icmpv6.echo.identifier
# 4. Reply from Router_2 to Leader over 15.4
pkts.copy().\
filter_ping_reply(identifier=_identifier).\
filter_ipv6_src_dst(ROUTER_2_MLEID, ROUTER_4_MLEID).\
filter_wpan_src16_dst16(ROUTER_2_RLOC16, LEADER_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
# 5. Forwarded reply on TREL (LEADER to ROUTER_1)
pkts.range(pkts.index).\
filter_eth_src(LEADER_ETH).\
filter(lambda p: p.eth.dst == ROUTER_1_ETH).\
filter(lambda p: p.udp.dstport == ROUTER_1_TREL_PORT).\
filter(lambda p: p.trel.source_addr == LEADER and p.trel.destination_addr == ROUTER_1).\
filter(lambda p: p.trel.wpan.src16 == LEADER_RLOC16 and p.trel.wpan.dst16 == ROUTER_1_RLOC16).\
must_next()
# 6. Forwarded reply on TREL (ROUTER_1 to ROUTER_4)
pkts.range(pkts.index).\
filter_eth_src(ROUTER_1_ETH).\
filter(lambda p: p.eth.dst == ROUTER_4_ETH).\
filter(lambda p: p.udp.dstport == ROUTER_4_TREL_PORT).\
filter(lambda p: p.trel.source_addr == ROUTER_1 and p.trel.destination_addr == ROUTER_4).\
filter(lambda p: p.trel.wpan.src16 == ROUTER_1_RLOC16 and p.trel.wpan.dst16 == ROUTER_4_RLOC16).\
must_next()
# Step 5
# - Device: Router_2
# - Description (TREL-8.2): Harness instructs device to send a ping of payload size of 500 bytes or more from
# Router_2 to Router_4 using the ML-EID address of Router_4 as the destination address.
# - Pass Criteria:
# - Router_2 MUST successfully receive a ping reply from Router_4, routed via the DUT.
# - TREL UDP frames MUST be used for ping/ping-reply transport between DUT and Leader, and MUST be correct TREL
# frames.
# - TREL UDP frames MUST be used for ping/ping-reply transport between the DUT and Router_4, and MUST be correct
# TREL frames.
print("Step 5: Router_2 pings Router_4 ML-EID")
# Path: ROUTER_2 -(15.4)-> LEADER -(TREL)-> ROUTER_1 -(TREL)-> ROUTER_4
# 1. Request from ROUTER_2 to LEADER over 15.4
_pkt_154_req = pkts.copy().\
filter_ping_request().\
filter_ipv6_src_dst(ROUTER_2_MLEID, ROUTER_4_MLEID).\
filter_wpan_src16_dst16(ROUTER_2_RLOC16, LEADER_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
_identifier = _pkt_154_req.icmpv6.echo.identifier
# 2. Forwarded request from LEADER to ROUTER_1 over TREL
pkts.range(pkts.index).\
filter_eth_src(LEADER_ETH).\
filter(lambda p: p.eth.dst == ROUTER_1_ETH).\
filter(lambda p: p.udp.dstport == ROUTER_1_TREL_PORT).\
filter(lambda p: p.trel.source_addr == LEADER and p.trel.destination_addr == ROUTER_1).\
filter(lambda p: p.trel.wpan.src16 == LEADER_RLOC16 and p.trel.wpan.dst16 == ROUTER_1_RLOC16).\
must_next()
# 3. Forwarded request from ROUTER_1 to ROUTER_4 over TREL
pkts.range(pkts.index).\
filter_eth_src(ROUTER_1_ETH).\
filter(lambda p: p.eth.dst == ROUTER_4_ETH).\
filter(lambda p: p.udp.dstport == ROUTER_4_TREL_PORT).\
filter(lambda p: p.trel.source_addr == ROUTER_1 and p.trel.destination_addr == ROUTER_4).\
filter(lambda p: p.trel.wpan.src16 == ROUTER_1_RLOC16 and p.trel.wpan.dst16 == ROUTER_4_RLOC16).\
must_next()
# 4. Reply on TREL (ROUTER_4 to ROUTER_1)
pkts.range(pkts.index).\
filter_eth_src(ROUTER_4_ETH).\
filter(lambda p: p.eth.dst == ROUTER_1_ETH).\
filter(lambda p: p.udp.dstport == ROUTER_1_TREL_PORT).\
filter(lambda p: p.trel.source_addr == ROUTER_4 and p.trel.destination_addr == ROUTER_1).\
filter(lambda p: p.trel.wpan.src16 == ROUTER_4_RLOC16 and p.trel.wpan.dst16 == ROUTER_1_RLOC16).\
must_next()
# 5. Forwarded reply on TREL (ROUTER_1 to LEADER)
pkts.range(pkts.index).\
filter_eth_src(ROUTER_1_ETH).\
filter(lambda p: p.eth.dst == LEADER_ETH).\
filter(lambda p: p.udp.dstport == LEADER_TREL_PORT).\
filter(lambda p: p.trel.source_addr == ROUTER_1 and p.trel.destination_addr == LEADER).\
filter(lambda p: p.trel.wpan.src16 == ROUTER_1_RLOC16 and p.trel.wpan.dst16 == LEADER_RLOC16).\
must_next()
# 6. Forwarded reply on 15.4 (LEADER to ROUTER_2)
pkts.range(pkts.index).\
filter_ping_reply(identifier=_identifier).\
filter_ipv6_src_dst(ROUTER_4_MLEID, ROUTER_2_MLEID).\
filter_wpan_src16_dst16(LEADER_RLOC16, ROUTER_2_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
# Step 6
# - Device: Router_3
# - Description (TREL-8.2): Harness instructs device to send a ping of payload size of 500 bytes or more from
# Router_3 to destination Router_2 using the ML-EID address of Router_2 as the destination address. Note: this
# verifies the DUT behavior of forwarding 6LoWPAN “Mesh Header” messages, incoming over 15.4, over multi-hop
# routes.
# - Pass Criteria:
# - Router_3 MUST successfully receive a ping reply from Router_2, routed via the DUT.
# - TREL UDP frames MUST be used for ping/ping-reply transport between the DUT and Leader, and MUST be correct
# TREL frames.
print("Step 6: Router_3 pings Router_2 ML-EID")
# Path: ROUTER_3 -(15.4)-> ROUTER_1 -(TREL)-> LEADER -(15.4)-> ROUTER_2
# 1. Request from ROUTER_3 to ROUTER_1 over 15.4
_pkt_154_req = pkts.copy().\
filter_ping_request().\
filter_ipv6_src_dst(ROUTER_3_MLEID, ROUTER_2_MLEID).\
filter_wpan_src16_dst16(ROUTER_3_RLOC16, ROUTER_1_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
_identifier = _pkt_154_req.icmpv6.echo.identifier
# 2. Check forwarded request on TREL from ROUTER_1 to LEADER
pkts.range(pkts.index).\
filter_eth_src(ROUTER_1_ETH).\
filter(lambda p: p.eth.dst == LEADER_ETH).\
filter(lambda p: p.udp.dstport == LEADER_TREL_PORT).\
filter(lambda p: p.trel.source_addr == ROUTER_1 and p.trel.destination_addr == LEADER).\
filter(lambda p: p.trel.wpan.src16 == ROUTER_1_RLOC16 and p.trel.wpan.dst16 == LEADER_RLOC16).\
must_next()
# 3. Forwarded request on 15.4 (LEADER to ROUTER_2)
pkts.range(pkts.index).\
filter_ping_request(identifier=_identifier).\
filter_ipv6_src_dst(ROUTER_3_MLEID, ROUTER_2_MLEID).\
filter_wpan_src16_dst16(LEADER_RLOC16, ROUTER_2_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
# 4. Reply from Router_2 to Leader over 15.4
pkts.copy().\
filter_ping_reply(identifier=_identifier).\
filter_ipv6_src_dst(ROUTER_2_MLEID, ROUTER_3_MLEID).\
filter_wpan_src16_dst16(ROUTER_2_RLOC16, LEADER_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
# 5. Check forwarded reply on TREL from LEADER to ROUTER_1
pkts.range(pkts.index).\
filter_eth_src(LEADER_ETH).\
filter(lambda p: p.eth.dst == ROUTER_1_ETH).\
filter(lambda p: p.udp.dstport == ROUTER_1_TREL_PORT).\
filter(lambda p: p.trel.source_addr == LEADER and p.trel.destination_addr == ROUTER_1).\
filter(lambda p: p.trel.wpan.src16 == LEADER_RLOC16 and p.trel.wpan.dst16 == ROUTER_1_RLOC16).\
must_next()
# 6. Forwarded reply on 15.4 (ROUTER_1 to ROUTER_3)
pkts.range(pkts.index).\
filter_ping_reply(identifier=_identifier).\
filter_ipv6_src_dst(ROUTER_2_MLEID, ROUTER_3_MLEID).\
filter_wpan_src16_dst16(ROUTER_1_RLOC16, ROUTER_3_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
# Step 7
# - Device: Router_2
# - Description (TREL-8.2): Harness instructs device to send a ping of payload size of 500 bytes or more from
# Router_2 to Router_3 using the ML-EID address of Router_3 as the destination address.
# - Pass Criteria:
# - Router_2 MUST successfully receive a ping reply from Router_3, routed via the DUT.
# - TREL UDP frames MUST be used for ping/ping-reply transport between the DUT and Leader, and MUST be correct
# TREL frames.
print("Step 7: Router_2 pings Router_3 ML-EID")
# Path: ROUTER_2 -(15.4)-> LEADER -(TREL)-> ROUTER_1 -(15.4)-> ROUTER_3
# 1. Request from ROUTER_2 to LEADER over 15.4
_pkt_154_req = pkts.copy().\
filter_ping_request().\
filter_ipv6_src_dst(ROUTER_2_MLEID, ROUTER_3_MLEID).\
filter_wpan_src16_dst16(ROUTER_2_RLOC16, LEADER_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
_identifier = _pkt_154_req.icmpv6.echo.identifier
# 2. Check forwarded request on TREL from LEADER to ROUTER_1
pkts.range(pkts.index).\
filter_eth_src(LEADER_ETH).\
filter(lambda p: p.eth.dst == ROUTER_1_ETH).\
filter(lambda p: p.udp.dstport == ROUTER_1_TREL_PORT).\
filter(lambda p: p.trel.source_addr == LEADER and p.trel.destination_addr == ROUTER_1).\
filter(lambda p: p.trel.wpan.src16 == LEADER_RLOC16 and p.trel.wpan.dst16 == ROUTER_1_RLOC16).\
must_next()
# 3. Forwarded request on 15.4 (ROUTER_1 to ROUTER_3)
pkts.range(pkts.index).\
filter_ping_request(identifier=_identifier).\
filter_ipv6_src_dst(ROUTER_2_MLEID, ROUTER_3_MLEID).\
filter_wpan_src16_dst16(ROUTER_1_RLOC16, ROUTER_3_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
# 4. Reply from Router_3 to Router_1 over 15.4
pkts.copy().\
filter_ping_reply(identifier=_identifier).\
filter_ipv6_src_dst(ROUTER_3_MLEID, ROUTER_2_MLEID).\
filter_wpan_src16_dst16(ROUTER_3_RLOC16, ROUTER_1_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
# 5. Check forwarded reply on TREL from ROUTER_1 to LEADER
pkts.range(pkts.index).\
filter_eth_src(ROUTER_1_ETH).\
filter(lambda p: p.eth.dst == LEADER_ETH).\
filter(lambda p: p.udp.dstport == LEADER_TREL_PORT).\
filter(lambda p: p.trel.source_addr == ROUTER_1 and p.trel.destination_addr == LEADER).\
filter(lambda p: p.trel.wpan.src16 == ROUTER_1_RLOC16 and p.trel.wpan.dst16 == LEADER_RLOC16).\
must_next()
# 6. Forwarded reply on 15.4 (LEADER to ROUTER_2)
pkts.range(pkts.index).\
filter_ping_reply(identifier=_identifier).\
filter_ipv6_src_dst(ROUTER_3_MLEID, ROUTER_2_MLEID).\
filter_wpan_src16_dst16(LEADER_RLOC16, ROUTER_2_RLOC16).\
filter(lambda p: p.wpan).\
must_next()
print("All verification steps PASSED")
if __name__ == '__main__':
verify_utils.run_main(verify)