mirror of
https://github.com/espressif/openthread.git
synced 2026-09-24 18:07:36 +00:00
[nexus] add test 5.1.2 and refactor nexus test infrastructure (#12363)
Adds a new Nexus test case for 'Child Address Timeout' (5.1.2) and
performs several refactorings to improve the nexus test framework.
Summary of changes:
- Implemented Nexus test 5.1.2 (Child Address Timeout):
- Added test_5_1_2.cpp: Sets up a Leader, Router (DUT), MED, and SED
topology with restricted connectivity using AllowList. Verifies
that the parent stops responding to Address Queries for children
after their timeout interval expires.
- Added verify_5_1_2.py: PCAP verification script for test 5.1.2.
- Refactored Nexus verification infrastructure:
- Created verify_utils.py: Shared module for common verification
logic, including monkey-patches for CoapTlvParser and
which_tshark, and a generic 'run_main' test runner function.
- Updated verify_5_1_1.py and verify_5_1_2.py to use verify_utils.py,
significantly reducing boilerplate code.
- Cleaned up imports in verify_utils.py by removing unused logging.
- Updated build and execution scripts:
- Modified CMakeLists.txt to build the new 5.1.2 test executable.
- Updated run_nexus_tests.sh to include 5.1.2 in the default test list.
This commit is contained in:
@@ -114,6 +114,7 @@ endmacro()
|
||||
#----------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
ot_nexus_test(5_1_1)
|
||||
ot_nexus_test(5_1_2)
|
||||
ot_nexus_test(border_agent)
|
||||
ot_nexus_test(border_agent_tracker)
|
||||
ot_nexus_test(discover_scan)
|
||||
|
||||
@@ -49,6 +49,7 @@ NEXUS_BIN_DIR="${BUILD_DIR}/tests/nexus"
|
||||
# Default test list if no arguments are provided
|
||||
DEFAULT_TESTS=(
|
||||
"5_1_1"
|
||||
"5_1_2"
|
||||
)
|
||||
|
||||
# Use provided arguments or the default test list
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static constexpr uint32_t kFormNetworkTime = 13 * 1000;
|
||||
|
||||
/**
|
||||
* Time to advance for a node to join as a child and upgrade to a router.
|
||||
*/
|
||||
static constexpr uint32_t kAttachToRouterTime = 200 * 1000;
|
||||
|
||||
/**
|
||||
* Time to advance for a node to join as a child.
|
||||
*/
|
||||
static constexpr uint32_t kAttachAsChildTime = 5 * 1000;
|
||||
|
||||
/**
|
||||
* Child timeout value in seconds.
|
||||
*/
|
||||
static constexpr uint32_t kChildTimeout = 10;
|
||||
|
||||
/**
|
||||
* Time to wait for child timeout to expire.
|
||||
*/
|
||||
static constexpr uint32_t kChildTimeoutWaitTime = (kChildTimeout + 2) * 1000;
|
||||
|
||||
/**
|
||||
* Time to wait for ICMPv6 Echo response (Address Query).
|
||||
*/
|
||||
static constexpr uint32_t kEchoRequestWaitTime = 5 * 1000;
|
||||
|
||||
static void SendEchoRequest(Node &aSender, const Ip6::Address &aPeerAddr, uint16_t aIdentifier)
|
||||
{
|
||||
Message *message = aSender.Get<Ip6::Icmp>().NewMessage();
|
||||
Ip6::MessageInfo messageInfo;
|
||||
|
||||
VerifyOrQuit(message != nullptr);
|
||||
messageInfo.SetPeerAddr(aPeerAddr);
|
||||
messageInfo.SetHopLimit(64);
|
||||
SuccessOrQuit(aSender.Get<Ip6::Icmp>().SendEchoRequest(*message, messageInfo, aIdentifier));
|
||||
}
|
||||
|
||||
void Test5_1_2(void)
|
||||
{
|
||||
/**
|
||||
* 5.1.2 Child Address Timeout
|
||||
*
|
||||
* 5.1.2.1 Topology
|
||||
* - Leader
|
||||
* - Router_1 (DUT)
|
||||
* - MED_1
|
||||
* - SED_1
|
||||
*
|
||||
* 5.1.2.2 Purpose & Description
|
||||
* The purpose of the test case is to verify that when the timer reaches the value of the Timeout TLV sent by the
|
||||
* Child, the Parent stops responding to Address Query on the Child's behalf.
|
||||
*
|
||||
* Spec Reference: Timing Out Children
|
||||
* V1.1 Section: 4.7.5
|
||||
* V1.3.0 Section: 4.6.3
|
||||
*/
|
||||
|
||||
Core nexus;
|
||||
|
||||
Node &leader = nexus.CreateNode();
|
||||
Node &router = nexus.CreateNode();
|
||||
Node &med = nexus.CreateNode();
|
||||
Node &sed = nexus.CreateNode();
|
||||
|
||||
leader.SetName("LEADER");
|
||||
router.SetName("ROUTER_1");
|
||||
med.SetName("MED_1");
|
||||
sed.SetName("SED_1");
|
||||
|
||||
nexus.AdvanceTime(0);
|
||||
|
||||
// Use AllowList feature to restrict the topology.
|
||||
leader.AllowList(router);
|
||||
router.AllowList(leader);
|
||||
|
||||
router.AllowList(med);
|
||||
med.AllowList(router);
|
||||
|
||||
router.AllowList(sed);
|
||||
sed.AllowList(router);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 1: All");
|
||||
|
||||
/**
|
||||
* Step 1: All
|
||||
* - Description: Verify topology is formed correctly
|
||||
* - Pass Criteria: N/A
|
||||
*/
|
||||
leader.Form();
|
||||
nexus.AdvanceTime(kFormNetworkTime);
|
||||
VerifyOrQuit(leader.Get<Mle::Mle>().IsLeader());
|
||||
|
||||
router.Join(leader);
|
||||
nexus.AdvanceTime(kAttachToRouterTime);
|
||||
VerifyOrQuit(router.Get<Mle::Mle>().IsRouter());
|
||||
|
||||
med.Get<Mle::Mle>().SetTimeout(kChildTimeout);
|
||||
med.Join(router, Node::kAsMed);
|
||||
nexus.AdvanceTime(kAttachAsChildTime);
|
||||
VerifyOrQuit(med.Get<Mle::Mle>().IsChild());
|
||||
|
||||
sed.Get<Mle::Mle>().SetTimeout(kChildTimeout);
|
||||
sed.Join(router, Node::kAsSed);
|
||||
nexus.AdvanceTime(kAttachAsChildTime);
|
||||
VerifyOrQuit(sed.Get<Mle::Mle>().IsChild());
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 2: MED_1, SED_1");
|
||||
|
||||
/**
|
||||
* Step 2: MED_1, SED_1
|
||||
* - Description: Harness silently powers-off both devices and waits for the keep-alive timeout to expire
|
||||
* - Pass Criteria: N/A
|
||||
*/
|
||||
med.Get<Mle::Mle>().Stop();
|
||||
sed.Get<Mle::Mle>().Stop();
|
||||
|
||||
nexus.AdvanceTime(kChildTimeoutWaitTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 3: Leader");
|
||||
|
||||
/**
|
||||
* Step 3: Leader
|
||||
* - Description: Harness instructs the Leader to send an ICMPv6 Echo Request to MED_1. As part of the process, the
|
||||
* Leader automatically attempts to perform address resolution by sending an Address Query Request
|
||||
* - Pass Criteria: N/A
|
||||
*/
|
||||
SendEchoRequest(leader, med.Get<Mle::Mle>().GetMeshLocalEid(), 0x1234);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 4: Router_1 (DUT)");
|
||||
|
||||
/**
|
||||
* Step 4: Router_1 (DUT)
|
||||
* - Description: Does not respond to Address Query Request
|
||||
* - Pass Criteria: The DUT MUST NOT respond with an Address Notification Message
|
||||
*/
|
||||
nexus.AdvanceTime(kEchoRequestWaitTime);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 6: Leader");
|
||||
|
||||
/**
|
||||
* Step 6: Leader
|
||||
* - Description: Harness instructs the Leader to send an ICMPv6 Echo Request to SED_1. As part of the process, the
|
||||
* Leader automatically attempts to perform address resolution by sending an Address Query Request
|
||||
* - Pass Criteria: N/A
|
||||
*/
|
||||
SendEchoRequest(leader, sed.Get<Mle::Mle>().GetMeshLocalEid(), 0x5678);
|
||||
|
||||
Log("---------------------------------------------------------------------------------------");
|
||||
Log("Step 7: Router_1 (DUT)");
|
||||
|
||||
/**
|
||||
* Step 7: Router_1 (DUT)
|
||||
* - Description: Does not to Address Query Request
|
||||
* - Pass Criteria: The DUT MUST NOT respond with an Address Notification Message
|
||||
*/
|
||||
nexus.AdvanceTime(kEchoRequestWaitTime);
|
||||
|
||||
nexus.SaveTestInfo("test_5_1_2.json");
|
||||
}
|
||||
|
||||
} // namespace Nexus
|
||||
} // namespace ot
|
||||
|
||||
int main(void)
|
||||
{
|
||||
ot::Nexus::Test5_1_2();
|
||||
printf("All tests passed\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -29,51 +29,14 @@
|
||||
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
# Add the thread-cert directory to sys.path to find pktverify
|
||||
# Add the current directory to sys.path to find verify_utils
|
||||
CUR_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
OT_ROOT = os.path.abspath(os.path.join(CUR_DIR, '../..'))
|
||||
THREAD_CERT_DIR = os.path.join(OT_ROOT, 'tests/scripts/thread-cert')
|
||||
sys.path.append(THREAD_CERT_DIR)
|
||||
sys.path.append(CUR_DIR)
|
||||
|
||||
import verify_utils
|
||||
from pktverify import consts
|
||||
from pktverify.packet_verifier import PacketVerifier
|
||||
from pktverify.null_field import nullField
|
||||
from pktverify import utils as pvutils
|
||||
from pktverify.coap import CoapTlvParser
|
||||
import struct
|
||||
|
||||
|
||||
# Monkey-patch CoapTlvParser to parse Thread TLVs in CoAP payload
|
||||
def thread_coap_tlv_parse(t, v):
|
||||
kvs = []
|
||||
if t == consts.NL_MAC_EXTENDED_ADDRESS_TLV:
|
||||
kvs.append(('mac_addr', v.hex()))
|
||||
elif t == consts.NL_RLOC16_TLV:
|
||||
kvs.append(('rloc16', hex(struct.unpack('>H', v)[0])))
|
||||
elif t == consts.NL_STATUS_TLV:
|
||||
kvs.append(('status', str(v[0])))
|
||||
elif t == consts.NL_ROUTER_MASK_TLV:
|
||||
kvs.append(('router_mask', v.hex()))
|
||||
return kvs
|
||||
|
||||
|
||||
CoapTlvParser.parse = staticmethod(thread_coap_tlv_parse)
|
||||
|
||||
|
||||
# Monkey-patch which_tshark to use system tshark if /tmp/thread-wireshark/tshark is not found
|
||||
def which_tshark_patch():
|
||||
default_path = '/tmp/thread-wireshark/tshark'
|
||||
if os.path.exists(default_path):
|
||||
return default_path
|
||||
import shutil
|
||||
return shutil.which('tshark') or '/usr/local/bin/tshark'
|
||||
|
||||
|
||||
pvutils.which_tshark = which_tshark_patch
|
||||
|
||||
|
||||
def verify(pv):
|
||||
@@ -365,37 +328,5 @@ def verify(pv):
|
||||
must_next()
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python3 verify_5_1_1.py <json_file>")
|
||||
sys.exit(1)
|
||||
|
||||
json_file = os.path.abspath(sys.argv[1])
|
||||
|
||||
with open(json_file, 'rt') as f:
|
||||
data = json.load(f)
|
||||
|
||||
try:
|
||||
wireshark_prefs = consts.WIRESHARK_OVERRIDE_PREFS.copy()
|
||||
|
||||
network_key = data.get('network_key')
|
||||
if network_key:
|
||||
wireshark_prefs['uat:ieee802154_keys'] = f'"{network_key}","1","Thread hash"'
|
||||
|
||||
mesh_local_prefix = data.get('extra_vars', {}).get('mesh_local_prefix')
|
||||
if mesh_local_prefix:
|
||||
prefix_addr = mesh_local_prefix.split('/')[0]
|
||||
wireshark_prefs['6lowpan.context0'] = f'{prefix_addr}/64'
|
||||
|
||||
pv = PacketVerifier(json_file, wireshark_prefs=wireshark_prefs)
|
||||
pv.add_common_vars()
|
||||
verify(pv)
|
||||
print("Verification PASSED")
|
||||
except Exception as e:
|
||||
print(f"Verification FAILED: {e}")
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
verify_utils.run_main(verify)
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
#!/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):
|
||||
"""
|
||||
5.1.2 Child Address Timeout
|
||||
|
||||
5.1.2.1 Topology
|
||||
- Leader
|
||||
- Router_1 (DUT)
|
||||
- MED_1
|
||||
- SED_1
|
||||
|
||||
5.1.2.2 Purpose & Description
|
||||
The purpose of the test case is to verify that when the timer reaches the value of the Timeout TLV sent by the Child, the Parent stops responding to Address Query on the Child's behalf.
|
||||
|
||||
Spec Reference: Timing Out Children
|
||||
V1.1 Section: 4.7.5
|
||||
V1.3.0 Section: 4.6.3
|
||||
"""
|
||||
|
||||
pkts = pv.pkts
|
||||
pv.summary.show()
|
||||
|
||||
LEADER = pv.vars['LEADER']
|
||||
ROUTER_1 = pv.vars['ROUTER_1']
|
||||
|
||||
# Step 1: All
|
||||
# - Description: Verify topology is formed correctly
|
||||
# - Pass Criteria: N/A
|
||||
print("Step 1: Verify topology is formed correctly")
|
||||
pkts.filter_wpan_src64(LEADER).filter_mle_cmd(consts.MLE_ADVERTISEMENT).must_next()
|
||||
pkts.filter_wpan_src64(ROUTER_1).filter_mle_cmd(consts.MLE_ADVERTISEMENT).must_next()
|
||||
|
||||
# Step 2: MED_1, SED_1
|
||||
# - Description: Harness silently powers-off both devices and waits for the keep-alive timeout to expire
|
||||
# - Pass Criteria: N/A
|
||||
print("Step 2: MED_1 and SED_1 power off")
|
||||
|
||||
# Step 3: Leader
|
||||
# - Description: Harness instructs the Leader to send an ICMPv6 Echo Request to MED_1. As part of the process, the Leader automatically attempts to perform address resolution by sending an Address Query Request
|
||||
# - Pass Criteria: N/A
|
||||
print("Step 3: Leader sends Address Query for MED_1")
|
||||
pkts.filter_wpan_src64(LEADER).\
|
||||
filter_coap_request(consts.ADDR_QRY_URI).\
|
||||
filter(lambda p: p.coap.tlv.target_eid == pv.vars['MED_1_MLEID']).\
|
||||
must_next()
|
||||
|
||||
# Step 4: Router_1 (DUT)
|
||||
# - Description: Does not respond to Address Query Request
|
||||
# - Pass Criteria: The DUT MUST NOT respond with an Address Notification Message
|
||||
print("Step 4: Router_1 (DUT) MUST NOT respond with an Address Notification Message")
|
||||
pkts.filter_wpan_src64(ROUTER_1).\
|
||||
filter_coap_request(consts.ADDR_NTF_URI).\
|
||||
must_not_next()
|
||||
|
||||
# Step 6: Leader
|
||||
# - Description: Harness instructs the Leader to send an ICMPv6 Echo Request to SED_1. As part of the process, the Leader automatically attempts to perform address resolution by sending an Address Query Request
|
||||
# - Pass Criteria: N/A
|
||||
print("Step 6: Leader sends Address Query for SED_1")
|
||||
pkts.filter_wpan_src64(LEADER).\
|
||||
filter_coap_request(consts.ADDR_QRY_URI).\
|
||||
filter(lambda p: p.coap.tlv.target_eid == pv.vars['SED_1_MLEID']).\
|
||||
must_next()
|
||||
|
||||
# Step 7: Router_1 (DUT)
|
||||
# - Description: Does not to Address Query Request
|
||||
# - Pass Criteria: The DUT MUST NOT respond with an Address Notification Message
|
||||
print("Step 7: Router_1 (DUT) MUST NOT respond with an Address Notification Message")
|
||||
pkts.filter_wpan_src64(ROUTER_1).\
|
||||
filter_coap_request(consts.ADDR_NTF_URI).\
|
||||
must_not_next()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
verify_utils.run_main(verify)
|
||||
@@ -0,0 +1,110 @@
|
||||
#!/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 json
|
||||
import traceback
|
||||
import struct
|
||||
|
||||
# Add the thread-cert directory to sys.path to find pktverify
|
||||
CUR_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
OT_ROOT = os.path.abspath(os.path.join(CUR_DIR, '../..'))
|
||||
THREAD_CERT_DIR = os.path.join(OT_ROOT, 'tests/scripts/thread-cert')
|
||||
if THREAD_CERT_DIR not in sys.path:
|
||||
sys.path.append(THREAD_CERT_DIR)
|
||||
|
||||
from pktverify import consts
|
||||
from pktverify.packet_verifier import PacketVerifier
|
||||
from pktverify import utils as pvutils
|
||||
from pktverify.coap import CoapTlvParser
|
||||
from pktverify.addrs import Ipv6Addr
|
||||
|
||||
|
||||
# Monkey-patch CoapTlvParser to parse Thread TLVs in CoAP payload
|
||||
def thread_coap_tlv_parse(t, v):
|
||||
kvs = []
|
||||
if t == consts.NL_TARGET_EID_TLV:
|
||||
kvs.append(('target_eid', str(Ipv6Addr(v))))
|
||||
elif t == consts.NL_MAC_EXTENDED_ADDRESS_TLV:
|
||||
kvs.append(('mac_addr', v.hex()))
|
||||
elif t == consts.NL_RLOC16_TLV:
|
||||
kvs.append(('rloc16', hex(struct.unpack('>H', v)[0])))
|
||||
elif t == consts.NL_STATUS_TLV:
|
||||
kvs.append(('status', str(v[0])))
|
||||
elif t == consts.NL_ROUTER_MASK_TLV:
|
||||
kvs.append(('router_mask', v.hex()))
|
||||
return kvs
|
||||
|
||||
|
||||
def apply_patches():
|
||||
CoapTlvParser.parse = staticmethod(thread_coap_tlv_parse)
|
||||
|
||||
def which_tshark_patch():
|
||||
default_path = '/tmp/thread-wireshark/tshark'
|
||||
if os.path.exists(default_path):
|
||||
return default_path
|
||||
import shutil
|
||||
return shutil.which('tshark') or '/usr/local/bin/tshark'
|
||||
|
||||
pvutils.which_tshark = which_tshark_patch
|
||||
|
||||
|
||||
def run_main(verify_func):
|
||||
if len(sys.argv) < 2:
|
||||
print(f"Usage: python3 {sys.argv[0]} <json_file>")
|
||||
sys.exit(1)
|
||||
|
||||
apply_patches()
|
||||
|
||||
json_file = os.path.abspath(sys.argv[1])
|
||||
|
||||
with open(json_file, 'rt') as f:
|
||||
data = json.load(f)
|
||||
|
||||
try:
|
||||
wireshark_prefs = consts.WIRESHARK_OVERRIDE_PREFS.copy()
|
||||
|
||||
network_key = data.get('network_key')
|
||||
if network_key:
|
||||
wireshark_prefs['uat:ieee802154_keys'] = f'"{network_key}","1","Thread hash"'
|
||||
|
||||
mesh_local_prefix = data.get('extra_vars', {}).get('mesh_local_prefix')
|
||||
if mesh_local_prefix:
|
||||
prefix_addr = mesh_local_prefix.split('/')[0]
|
||||
wireshark_prefs['6lowpan.context0'] = f'{prefix_addr}/64'
|
||||
|
||||
pv = PacketVerifier(json_file, wireshark_prefs=wireshark_prefs)
|
||||
pv.add_common_vars()
|
||||
verify_func(pv)
|
||||
print("Verification PASSED")
|
||||
except Exception as e:
|
||||
print(f"Verification FAILED: {e}")
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user