[tests] remove thread-cert tests replaced by Nexus (#12443)

This commit removes the Python-based thread-cert tests for sections 5.1,
5.2, and 5.3. These tests have been replaced by C++ Nexus tests in
tests/nexus/, which offer better performance, reliability, and
integration with the core OpenThread codebase.

The following Python scripts have been removed from
tests/scripts/thread-cert/:
- Cert_5_1_01 through Cert_5_1_13
- Cert_5_2_01, Cert_5_2_03 through Cert_5_2_07
- Cert_5_3_01 through Cert_5_3_11

The corresponding Nexus tests are available in tests/nexus/ along with
their respective PCAP verification scripts.
This commit is contained in:
Jonathan Hui
2026-02-13 02:11:57 -06:00
committed by GitHub
parent ede310de75
commit 6122986233
30 changed files with 0 additions and 8095 deletions
@@ -1,318 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import mle
import network_layer
import thread_cert
from pktverify.consts import MLE_ADVERTISEMENT, MLE_PARENT_REQUEST, MLE_PARENT_RESPONSE, MLE_CHILD_UPDATE_RESPONSE, MLE_CHILD_ID_REQUEST, MLE_CHILD_ID_RESPONSE, MLE_LINK_REQUEST, MLE_LINK_ACCEPT_AND_REQUEST, ADDR_SOL_URI, SOURCE_ADDRESS_TLV, MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, MLE_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, TLV_REQUEST_TLV, SCAN_MASK_TLV, CONNECTIVITY_TLV, LINK_MARGIN_TLV, VERSION_TLV, ADDRESS_REGISTRATION_TLV, NL_MAC_EXTENDED_ADDRESS_TLV, NL_RLOC16_TLV, NL_STATUS_TLV, NL_ROUTER_MASK_TLV, COAP_CODE_ACK
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
LEADER = 1
ROUTER = 2
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to show that the Leader is able to form
# a network and the Router attaches to it with proper steps.
#
# Test Topology:
# -------------
# Leader
# |
# Router
#
# DUT Types:
# ----------
# Leader
# Router
class Cert_5_1_01_RouterAttach(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER]
},
ROUTER: {
'name': 'ROUTER',
'mode': 'rdn',
'allowlist': [LEADER]
},
}
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[ROUTER].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER].get_state(), 'router')
self.collect_rloc16s()
leader_addr = self.nodes[LEADER].get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL)
self.assertTrue(self.nodes[ROUTER].ping(leader_addr))
self.simulator.go(5)
router_addr = self.nodes[ROUTER].get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL)
self.assertTrue(self.nodes[LEADER].ping(router_addr))
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
LEADER_RLOC16 = pv.vars['LEADER_RLOC16']
ROUTER = pv.vars['ROUTER']
# Step 1: Leader is sending properly formatted MLE Advertisements.
# Advertisements MUST be sent with an IP hop limit of 255 to
# the Link-Local All Nodes multicast address (FF02::1).
# The following TLVs MUST be present in the MLE Advertisements:
# - Leader Data TLV
# - Route64 TLV
# - Source Address TLV
pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: {
LEADER_DATA_TLV,
ROUTE64_TLV,
SOURCE_ADDRESS_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255).\
must_next()
# Step 2: Router sends a MLE Parent Request with an IP hop limit of
# 255 to the Link-Local All Routers multicast address (FF02::2).
# The following TLVs MUST be present in the MLE Parent Request:
# - Challenge TLV
# - Mode TLV
# - Scan Mask TLV
# If the DUT sends multiple MLE Parent Requests
# - The first one MUST be sent only to all Routers
# - Subsequent ones MAY be sent to all Routers and REEDS
# - Version TLV
# If the first MLE Parent Request was sent to all Routers and
# REEDS, the test fails.
pkts.filter_wpan_src64(ROUTER).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255 and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 0).\
must_next()
# Step 3: Leader responds with a MLE Parent Response.
# The following TLVs MUST be present in the MLE Parent Response:
# - Challenge TLV
# - Connectivity TLV
# - Leader Data TLV
# - Link-layer Frame Counter TLV
# - Link Margin TLV
# - Response TLV
# - Source Address
# - Version TLV
# - MLE Frame Counter TLV (optional)
pkts.filter_wpan_src64(LEADER).\
filter_wpan_dst64(ROUTER).\
filter_mle_cmd(MLE_PARENT_RESPONSE).\
filter(lambda p: {
CHALLENGE_TLV,
CONNECTIVITY_TLV,
LEADER_DATA_TLV,
LINK_LAYER_FRAME_COUNTER_TLV,
LINK_MARGIN_TLV,
RESPONSE_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type)).\
must_next()
# Step 4: Router sends a MLE Child ID Request.
# The following TLVs MUST be present in the MLE Child ID Request:
# - Link-layer Frame Counter TLV
# - Mode TLV
# - Response TLV
# - Timeout TLV
# - TLV Request TLV
# - Address16 TLV
# - Network Data TLV
# - Route64 TLV (optional)
# - Version TLV
# - MLE Frame Counter TLV (optional)
# The following TLV MUST NOT be present in the MLE Child ID Request:
# - Address Registration TLV
_pkt = pkts.filter_wpan_src64(ROUTER).\
filter_wpan_dst64(LEADER).\
filter_mle_cmd(MLE_CHILD_ID_REQUEST).\
filter(lambda p: {
LINK_LAYER_FRAME_COUNTER_TLV,
MODE_TLV,
RESPONSE_TLV,
TIMEOUT_TLV,
TLV_REQUEST_TLV,
ADDRESS16_TLV,
NETWORK_DATA_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.addr16 is nullField and\
p.thread_nwd.tlv.type is nullField).\
must_next()
_pkt.must_not_verify(lambda p: (ADDRESS_REGISTRATION_TLV) in p.mle.tlv.type)
# Step 5: Leader responds with a Child ID Response.
# The following TLVs MUST be present in the Child ID Response:
# - Address16 TLV
# - Leader Data TLV
# - Network Data TLV
# - Source Address TLV
# - Route64 TLV (if requested)
pkts.filter_wpan_src64(LEADER).\
filter_wpan_dst64(ROUTER).\
filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\
filter(lambda p: {
ADDRESS16_TLV,
LEADER_DATA_TLV,
NETWORK_DATA_TLV,
SOURCE_ADDRESS_TLV,
ROUTE64_TLV
} <= set(p.mle.tlv.type) or\
{
ADDRESS16_TLV,
LEADER_DATA_TLV,
NETWORK_DATA_TLV,
SOURCE_ADDRESS_TLV
} <= set(p.mle.tlv.type)).\
must_next()
# Step 6: Router sends an Address Solicit Request.
# Ensure the Address Solicit Request is properly formatted:
# CoAP Request URI
# coap://<leader address>:MM/a/as
# CoAP Payload
# - MAC Extended Address TLV
# - Status TLV
_pkt = pkts.filter_wpan_src64(ROUTER).\
filter_wpan_dst16(LEADER_RLOC16).\
filter_coap_request(ADDR_SOL_URI).\
filter(lambda p: {
NL_MAC_EXTENDED_ADDRESS_TLV,
NL_STATUS_TLV
} <= set(p.coap.tlv.type)\
).\
must_next()
# Step 7: Leader sends an Address Solicit Response.
# Ensure the Address Solicit Response is properly formatted:
# CoAP Response Code
# 2.04 Changed
# CoAP Payload
# - Status TLV (value = Success)
# - RLOC16 TLV
# - Router Mask TLV
pkts.filter_wpan_src64(LEADER).\
filter_wpan_dst16(_pkt.wpan.src16).\
filter_coap_ack(ADDR_SOL_URI).\
filter(lambda p: {
NL_STATUS_TLV,
NL_RLOC16_TLV,
NL_ROUTER_MASK_TLV
} <= set(p.coap.tlv.type) and\
p.coap.code == COAP_CODE_ACK and\
p.thread_address.tlv.status == 0\
).\
must_next()
# Steps 8 and 9 are skipped due to change the Link establishment
# process (no multicast MLE Link Request by new router).
# Step 10: Router is sending properly formatted MLE Advertisements.
# MLE Advertisements MUST be sent with an IP Hop Limit of
# 255 to the Link-Local All Nodes multicast address (FF02::1).
# The following TLVs MUST be present in the MLE Advertisements:
# - Leader Data TLV
# - Route64 TLV
# - Source Address TLV
pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: {
LEADER_DATA_TLV,
ROUTE64_TLV,
SOURCE_ADDRESS_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255).\
must_next()
# Step 11: DUT responds with ICMPv6 Echo Reply
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(ROUTER).\
filter_wpan_dst64(LEADER).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(LEADER).\
filter_wpan_dst64(ROUTER).\
must_next()
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(LEADER).\
filter_wpan_dst64(ROUTER).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER).\
filter_wpan_dst64(LEADER).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,183 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import mle
import thread_cert
from pktverify.consts import MLE_CHILD_ID_RESPONSE, ADDR_QRY_URI, ADDR_NTF_URI, NL_TARGET_EID_TLV
from pktverify.packet_verifier import PacketVerifier
LEADER = 1
ROUTER = 2
MED = 3
SED = 4
MTDS = [MED, SED]
# Test Purpose and 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
#
# Test Topology:
# --------------
# Leader
# |
# Router
# / \
# MED SED
#
# DUT Types:
# ----------
# Router
class Cert_5_1_02_ChildAddressTimeout(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER]
},
ROUTER: {
'name': 'ROUTER',
'mode': 'rdn',
'allowlist': [LEADER, MED, SED]
},
MED: {
'name': 'MED',
'is_mtd': True,
'mode': 'rn',
'timeout': config.DEFAULT_CHILD_TIMEOUT,
'allowlist': [ROUTER]
},
SED: {
'name': 'SED',
'is_mtd': True,
'mode': 'n',
'timeout': config.DEFAULT_CHILD_TIMEOUT,
'allowlist': [ROUTER]
},
}
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[ROUTER].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER].get_state(), 'router')
self.nodes[MED].start()
self.simulator.go(5)
self.assertEqual(self.nodes[MED].get_state(), 'child')
self.nodes[SED].start()
self.simulator.go(5)
self.assertEqual(self.nodes[SED].get_state(), 'child')
self.collect_ipaddrs()
med_mleid = self.nodes[MED].get_ip6_address(config.ADDRESS_TYPE.ML_EID)
sed_mleid = self.nodes[SED].get_ip6_address(config.ADDRESS_TYPE.ML_EID)
self.nodes[MED].stop()
self.nodes[SED].stop()
self.simulator.go(config.DEFAULT_CHILD_TIMEOUT + 5)
self.assertFalse(self.nodes[LEADER].ping(med_mleid))
self.assertFalse(self.nodes[LEADER].ping(sed_mleid))
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
ROUTER = pv.vars['ROUTER']
MED = pv.vars['MED']
MED_MLEID = pv.vars['MED_MLEID']
SED = pv.vars['SED']
SED_MLEID = pv.vars['SED_MLEID']
MM = pv.vars['MM_PORT']
# Step 1: Verify topology is formed correctly
pv.verify_attached('ROUTER')
pkts.filter_wpan_src64(ROUTER).\
filter_wpan_dst64(MED).\
filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\
must_next()
pkts.filter_wpan_src64(ROUTER).\
filter_wpan_dst64(SED).\
filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\
must_next()
# Step 2: Power off both devices and allow for the keep-alive timeout to expire
# Step 3: The Leader sends an ICMPv6 Echo Request to MED and attempts to perform
# address resolution by sending an Address Query Request
pkts.filter_wpan_src64(LEADER).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
filter(lambda p: p.thread_address.tlv.type == [NL_TARGET_EID_TLV] and\
p.thread_address.tlv.target_eid == MED_MLEID).\
must_next()
# Step 4: Router MUST NOT respond with an Address Notification Message
pkts.filter_wpan_src64(ROUTER).\
filter_coap_request(ADDR_NTF_URI).\
must_not_next()
# Step 5: The Leader sends an ICMPv6 Echo Request to SED and attempts to perform
# address resolution by sending an Address Query Request
pkts.filter_wpan_src64(LEADER).\
filter_RLARMA().\
filter(lambda p: p.thread_address.tlv.type == [NL_TARGET_EID_TLV] and\
p.thread_address.tlv.target_eid == SED_MLEID).\
filter_coap_request(ADDR_QRY_URI, port=MM).\
must_next()
# Step 6: Router MUST NOT respond with an Address Notification Message
pkts.filter_wpan_src64(ROUTER).\
filter_coap_request(ADDR_NTF_URI).\
must_not_next()
if __name__ == '__main__':
unittest.main()
@@ -1,241 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import mle
import network_layer
import thread_cert
from pktverify.consts import MLE_ADVERTISEMENT, MLE_PARENT_REQUEST, MLE_CHILD_ID_REQUEST, ADDR_SOL_URI, MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, TLV_REQUEST_TLV, SCAN_MASK_TLV, VERSION_TLV, ADDRESS_REGISTRATION_TLV, NL_MAC_EXTENDED_ADDRESS_TLV, NL_STATUS_TLV, NL_RLOC16_TLV
from pktverify.packet_verifier import PacketVerifier
LEADER = 1
ROUTER1 = 2
ROUTER2 = 3
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to verify that after the removal of the
# Leader from the network, the DUT will first attempt to reattach to the
# original partition, then attach to a new partition and request its
# original short address.
#
# Test Topology:
# -------------
# Leader Router_2
# / \ -> |
# Router_1 - Router_2 Router_1[DUT]
#
# DUT Types:
# ----------
# Router
class Cert_5_1_03_RouterAddressReallocation(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER1, ROUTER2]
},
ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [LEADER, ROUTER2]
},
ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [LEADER, ROUTER1]
},
}
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
self.nodes[ROUTER2].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
self.nodes[ROUTER2].set_network_id_timeout(110)
self.nodes[LEADER].stop()
self.simulator.go(140)
self.assertEqual(self.nodes[ROUTER2].get_state(), 'leader')
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
self.collect_rloc16s()
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
LEADER_RLOC16 = pv.vars['LEADER_RLOC16']
ROUTER_1 = pv.vars['ROUTER_1']
ROUTER_2 = pv.vars['ROUTER_2']
ROUTER_2_RLOC16 = pv.vars['ROUTER_2_RLOC16']
# Step 2: Verify topology is formed correctly.
pv.verify_attached('ROUTER_1')
_pkt_as = pkts.filter_wpan_src64(LEADER).\
filter_coap_ack(ADDR_SOL_URI).\
must_next()
pv.verify_attached('ROUTER_2')
_pkt_pt = pkts.filter_wpan_src64(ROUTER_2).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
must_next()
# Step 5: Router_1 MUST attempt to reattach to its original partition
# by sending a MLE Parent Request with a hop limit of 255 to
# the All-Routers multicast address (FF02::2).
# The following TLVs MUST be present in the MLE Parent Request:
# - Challenge TLV
# - Mode TLV
# - Scan Mask TLV (MUST have E and R flags set)
# - Version TLV
# The DUT MUST make two separate attempts to reconnect to its
# original partition in this manner
with pkts.save_index():
for i in range(2):
pkts.filter_wpan_src64(ROUTER_1).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255 and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 1).\
must_next()
# Step 6: Router_1 MUST attempt to attach to any other partition
# within range by sending a MLE Parent Request.
# The following TLVs MUST be present in the MLE Parent Request:
# - Challenge TLV
# - Mode TLV
# - Scan Mask TLV
# - Version TLV
pkts.filter_wpan_src64(ROUTER_2).\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter_LLANMA().\
filter(lambda p:\
p.mle.tlv.leader_data.partition_id !=
_pkt_pt.mle.tlv.leader_data.partition_id).\
must_next()
pkts.filter_wpan_src64(ROUTER_1).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 0 and\
p.ipv6.hlim == 255).\
must_next()
# Step 7: Router_1 sends a MLE Child ID Request to Router_2.
# The following TLVs MUST be present in the MLE Child ID Request:
# - Link-layer Frame Counter TLV
# - Mode TLV
# - Response TLV
# - Timeout TLV
# - TLV Request TLV
# - Version TLV
# - MLE Frame Counter TLV (optional)
# The following TLV MUST NOT be present in the MLE Child ID Request:
# - Address Registration TLV
_pkt = pkts.filter_wpan_src64(ROUTER_1).\
filter_wpan_dst64(ROUTER_2).\
filter_mle_cmd(MLE_CHILD_ID_REQUEST).\
filter(lambda p: {
LINK_LAYER_FRAME_COUNTER_TLV,
MODE_TLV,
RESPONSE_TLV,
TIMEOUT_TLV,
TLV_REQUEST_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type)).\
must_next()
_pkt.must_not_verify(lambda p: (ADDRESS_REGISTRATION_TLV) in p.mle.tlv.type)
# Step 8: Router_1 sends an Address Solicit Request.
# Ensure the Address Solicit Request is properly formatted:
# CoAP Request URI
# coap://<leader address>:MM/a/as
# CoAP Payload
# - MAC Extended Address TLV
# - Status TLV
# - RLOC16 TLV
_pkt = pkts.filter_wpan_src64(ROUTER_1).\
filter_wpan_dst16(ROUTER_2_RLOC16).\
filter_coap_request(ADDR_SOL_URI).\
filter(lambda p: {
NL_MAC_EXTENDED_ADDRESS_TLV,
NL_STATUS_TLV,
NL_RLOC16_TLV
} <= set(p.coap.tlv.type) and\
p.thread_address.tlv.rloc16 ==
_pkt_as.thread_address.tlv.rloc16).\
must_next()
# Step 9: Router_2 automatically sends an Address Solicit Response.
pkts.filter_wpan_src64(ROUTER_2).\
filter_wpan_dst16(_pkt.wpan.src16).\
filter_coap_ack(ADDR_SOL_URI).\
filter(lambda p: p.thread_address.tlv.rloc16 ==
_pkt_as.thread_address.tlv.rloc16 and\
p.thread_address.tlv.status == 0).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,269 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import mle
import network_layer
import thread_cert
from pktverify.consts import MLE_ADVERTISEMENT, MLE_PARENT_REQUEST, MLE_PARENT_RESPONSE, MLE_CHILD_ID_RESPONSE, ADDR_SOL_URI, MODE_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, CONNECTIVITY_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, SCAN_MASK_TLV, VERSION_TLV, LINK_MARGIN_TLV, SOURCE_ADDRESS_TLV, COAP_CODE_ACK, NL_STATUS_TLV, NL_RLOC16_TLV, NL_ROUTER_MASK_TLV
from pktverify.packet_verifier import PacketVerifier
LEADER = 1
ROUTER1 = 2
ROUTER2 = 3
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to verify that when the original Leader is
# removed from the network, the DUT will create a new partition as Leader and
# will assign a router ID if a specific ID is requested.
#
# Test Topology:
# -------------
# Leader
# / \
# Router_1 - Router_2
# [DUT]
#
# DUT Types:
# ----------
# Router
class Cert_5_1_04_RouterAddressReallocation(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
SUPPORT_NCP = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'partition_id': 1,
'allowlist': [ROUTER1, ROUTER2]
},
ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [LEADER, ROUTER2]
},
ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [LEADER, ROUTER1]
},
}
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
self.nodes[ROUTER2].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
self.nodes[ROUTER2].set_network_id_timeout(200)
self.nodes[LEADER].stop()
self.simulator.go(220)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'leader')
self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
ROUTER_1 = pv.vars['ROUTER_1']
ROUTER_2 = pv.vars['ROUTER_2']
MM = pv.vars['MM_PORT']
# Step 3: Verify topology is formed correctly.
pv.verify_attached('ROUTER_1')
_pkt_pt = pkts.filter_wpan_src64(ROUTER_1).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
must_next()
with pkts.save_index():
_pkt_id = pkts.filter_wpan_src64(ROUTER_1).\
filter_wpan_dst64(ROUTER_2).\
filter_mle_cmd(MLE_PARENT_RESPONSE).\
must_next()
pv.verify_attached('ROUTER_2')
_pkt_as = pkts.filter_wpan_src64(LEADER).\
filter_coap_ack(ADDR_SOL_URI, port=MM).\
must_next()
# Step 5: Router_1 MUST attempt to reattach to its original partition
# by sending a MLE Parent Request with a hop limit of 255 to
# the All-Routers multicast address (FF02::2).
# The following TLVs MUST be present in the MLE Parent Request:
# - Challenge TLV
# - Mode TLV
# - Scan Mask TLV (MUST have E and R flags set)
# - Version TLV
# The DUT MUST make two separate attempts to reconnect to its
# original partition in this manner
with pkts.save_index():
for i in range(1, 3):
pkts.filter_wpan_src64(ROUTER_1).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255 and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 1
).\
must_next()
# Step 6: Router_1 MUST attempt to attach to any other partition
# within range by sending a MLE Parent Request.
# The following TLVs MUST be present in the MLE Parent Request:
# - Challenge TLV
# - Mode TLV
# - Scan Mask TLV
# - Version TLV
pkts.filter_wpan_src64(ROUTER_1).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255 and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 0
).\
must_next()
# Step 7: Router_1 MUST create a new partition and update the following
# values randomly:
# - Partition ID
# - Initial VN_version & VN_stable_version
# - Initial ID sequence number
# Notes: Considerring the randomly created VN_version & VN_stable_version
# could equal the previous versions, checking one of them to
# reduce case failures
pkts.filter_wpan_src64(ROUTER_1).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p:\
p.mle.tlv.leader_data.partition_id !=
_pkt_pt.mle.tlv.leader_data.partition_id and\
(p.mle.tlv.leader_data.data_version !=
_pkt_pt.mle.tlv.leader_data.data_version or\
p.mle.tlv.leader_data.stable_data_version !=
_pkt_pt.mle.tlv.leader_data.stable_data_version)
).\
must_next()
# Step 9: Router_1 MUST send a properly formatted Parent Response and
# Child ID Response to Router_2.
pkts.filter_wpan_src64(ROUTER_1).\
filter_wpan_dst64(ROUTER_2).\
filter_mle_cmd(MLE_PARENT_RESPONSE).\
filter(lambda p: {
CHALLENGE_TLV,
CONNECTIVITY_TLV,
LEADER_DATA_TLV,
LINK_LAYER_FRAME_COUNTER_TLV,
LINK_MARGIN_TLV,
RESPONSE_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.conn.id_seq != _pkt_id.mle.tlv.conn.id_seq
).\
must_next()
pkts.filter_wpan_src64(ROUTER_1).\
filter_wpan_dst64(ROUTER_2).\
filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\
filter(lambda p: {
ADDRESS16_TLV,
LEADER_DATA_TLV,
NETWORK_DATA_TLV,
SOURCE_ADDRESS_TLV,
ROUTE64_TLV
} <= set(p.mle.tlv.type) or\
{
ADDRESS16_TLV,
LEADER_DATA_TLV,
NETWORK_DATA_TLV,
SOURCE_ADDRESS_TLV
} <= set(p.mle.tlv.type)\
).\
must_next()
# Step 10: Router_1 MUST send a properly-formatted Address Solicit
# Response Message to Router_2.
# If a specific router ID is requested, the DUT MUST provide this
# router ID:
# CoAP Response Code
# - 2.04 Changed
# CoAP Payload
# - Status TLV (value = Success)
# - RLOC16 TLV
# - Router Mask TLV
pkts.filter_wpan_src64(ROUTER_1).\
filter_coap_ack(ADDR_SOL_URI, port=MM).\
filter(lambda p: {
NL_STATUS_TLV,
NL_RLOC16_TLV,
NL_ROUTER_MASK_TLV
} <= set(p.coap.tlv.type) and\
p.coap.code == COAP_CODE_ACK and\
p.thread_address.tlv.rloc16 ==
_pkt_as.thread_address.tlv.rloc16 and\
p.thread_address.tlv.status == 0
).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,314 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import mle
import network_layer
import thread_cert
from pktverify.consts import MLE_PARENT_RESPONSE, MLE_CHILD_ID_RESPONSE, MLE_LINK_REQUEST, ADDR_SOL_URI, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, CONNECTIVITY_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, VERSION_TLV, TLV_REQUEST_TLV, LINK_MARGIN_TLV, SOURCE_ADDRESS_TLV, COAP_CODE_ACK, COAP_CODE_POST, NL_MAC_EXTENDED_ADDRESS_TLV, NL_STATUS_TLV, NL_RLOC16_TLV, NL_ROUTER_MASK_TLV
from pktverify.packet_verifier import PacketVerifier
LEADER = 1
ROUTER1 = 2
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to verify that after deallocating a Router ID,
# Leader as a DUT doesn't reassign the Router ID for at least ID_REUSE_DELAY
# seconds
#
# Test Topology:
# -------------
# Leader
# |
# Router
#
# DUT Types:
# ----------
# Leader
class Cert_5_1_05_RouterAddressTimeout(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER1]
},
ROUTER1: {
'name': 'ROUTER',
'mode': 'rdn',
'allowlist': [LEADER]
},
}
def _setUpRouter1(self):
self.nodes[ROUTER1].add_allowlist(self.nodes[LEADER].get_addr64())
self.nodes[ROUTER1].enable_allowlist()
self.nodes[ROUTER1].set_router_selection_jitter(1)
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
self.nodes[ROUTER1].reset()
self._setUpRouter1()
self.simulator.go(200)
self.nodes[ROUTER1].start()
self.simulator.go(config.ROUTER_RESTORE_DELAY)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
self.nodes[ROUTER1].reset()
self._setUpRouter1()
self.simulator.go(300)
self.nodes[ROUTER1].start()
self.simulator.go(config.ROUTER_RESTORE_DELAY)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
ROUTER = pv.vars['ROUTER']
MM = pv.vars['MM_PORT']
# Step 1: Verify topology is formed correctly.
pv.verify_attached('ROUTER')
_pkt_as = pkts.filter_wpan_src64(LEADER).\
filter_coap_ack(ADDR_SOL_URI, port=MM).\
filter(lambda p: {
NL_STATUS_TLV,
NL_RLOC16_TLV,
NL_ROUTER_MASK_TLV
} <= set(p.coap.tlv.type) and\
p.coap.code == COAP_CODE_ACK and\
p.thread_address.tlv.status == 0
).\
must_next()
# Step 3: Router automatically sends a link request, reattaches and
# requests its original Router ID after reset within the
# ID_REUSE_DELAY interval
pkts.filter_wpan_src64(ROUTER).\
filter_LLARMA().\
filter_mle_cmd(MLE_LINK_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
VERSION_TLV,
TLV_REQUEST_TLV,
ADDRESS16_TLV,
ROUTE64_TLV
} <= set(p.mle.tlv.type)\
).\
must_next()
# Step 4: Leader MUST send a properly formatted Parent Response and
# Child ID Response to Router.
# And send Address Solicit Response Message to Router.
# The RLOC16 TLV in the Address Solicit Response message MUST
# contain a different Router ID then the one allocated in the
# original attach because ID_REUSE_DELAY interval has not
# timed out.
#
# CoAP Response Code
# - 2.04 Changed
# CoAP Payload
# - Status TLV (value = Success)
# - RLOC16 TLV
# - Router Mask TLV
pkts.filter_wpan_src64(LEADER).\
filter_wpan_dst64(ROUTER).\
filter_mle_cmd(MLE_PARENT_RESPONSE).\
filter(lambda p: {
CHALLENGE_TLV,
CONNECTIVITY_TLV,
LEADER_DATA_TLV,
LINK_LAYER_FRAME_COUNTER_TLV,
LINK_MARGIN_TLV,
RESPONSE_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type)
).\
must_next()
pkts.filter_wpan_src64(LEADER).\
filter_wpan_dst64(ROUTER).\
filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\
filter(lambda p: {
ADDRESS16_TLV,
LEADER_DATA_TLV,
NETWORK_DATA_TLV,
SOURCE_ADDRESS_TLV,
ROUTE64_TLV
} <= set(p.mle.tlv.type) or\
{
ADDRESS16_TLV,
LEADER_DATA_TLV,
NETWORK_DATA_TLV,
SOURCE_ADDRESS_TLV
} <= set(p.mle.tlv.type)\
).\
must_next()
_pkt_as2 = pkts.filter_wpan_src64(ROUTER).\
filter_coap_request(ADDR_SOL_URI, port=MM).\
filter(lambda p: {
NL_MAC_EXTENDED_ADDRESS_TLV,
NL_RLOC16_TLV,
NL_STATUS_TLV
} <= set(p.coap.tlv.type) and\
p.coap.code == COAP_CODE_POST and\
p.thread_address.tlv.rloc16 ==
_pkt_as.thread_address.tlv.rloc16
).\
must_next()
_pkt_as3 = pkts.filter_wpan_src64(LEADER).\
filter_coap_ack(ADDR_SOL_URI, port=MM).\
filter(lambda p: {
NL_STATUS_TLV,
NL_RLOC16_TLV,
NL_ROUTER_MASK_TLV
} <= set(p.coap.tlv.type) and\
p.coap.code == COAP_CODE_ACK and\
p.thread_address.tlv.rloc16 !=
_pkt_as2.thread_address.tlv.rloc16 and\
p.thread_address.tlv.status == 0
).\
must_next()
# Step 6: Router automatically sends a link request, reattaches and
# requests its most recent Router ID after reset after the
# ID_REUSE_DELAY interval
pkts.filter_wpan_src64(ROUTER).\
filter_LLARMA().\
filter_mle_cmd(MLE_LINK_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
VERSION_TLV,
TLV_REQUEST_TLV,
ADDRESS16_TLV,
ROUTE64_TLV
} <= set(p.mle.tlv.type)\
).\
must_next()
# Step 7: Leader MUST send a properly formatted Parent Response and
# Child ID Response to Router.
# And send Address Solicit Response Message to Router.
# The RLOC16 TLV in the Address Solicit Response message MUST
# contain the requested Router ID
#
# CoAP Response Code
# - 2.04 Changed
# CoAP Payload
# - Status TLV (value = Success)
# - RLOC16 TLV
# - Router Mask TLV
pkts.filter_wpan_src64(LEADER).\
filter_wpan_dst64(ROUTER).\
filter_mle_cmd(MLE_PARENT_RESPONSE).\
filter(lambda p: {
CHALLENGE_TLV,
CONNECTIVITY_TLV,
LEADER_DATA_TLV,
LINK_LAYER_FRAME_COUNTER_TLV,
LINK_MARGIN_TLV,
RESPONSE_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type)
).\
must_next()
pkts.filter_wpan_src64(LEADER).\
filter_wpan_dst64(ROUTER).\
filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\
filter(lambda p: {
ADDRESS16_TLV,
LEADER_DATA_TLV,
NETWORK_DATA_TLV,
SOURCE_ADDRESS_TLV,
ROUTE64_TLV
} <= set(p.mle.tlv.type) or\
{
ADDRESS16_TLV,
LEADER_DATA_TLV,
NETWORK_DATA_TLV,
SOURCE_ADDRESS_TLV
} <= set(p.mle.tlv.type)\
).\
must_next()
pkts.filter_wpan_src64(ROUTER).\
filter_coap_request(ADDR_SOL_URI, port=MM).\
filter(lambda p: {
NL_MAC_EXTENDED_ADDRESS_TLV,
NL_RLOC16_TLV,
NL_STATUS_TLV
} <= set(p.coap.tlv.type) and\
p.coap.code == COAP_CODE_POST and\
p.thread_address.tlv.rloc16 ==
_pkt_as3.thread_address.tlv.rloc16
).\
must_next()
pkts.filter_wpan_src64(LEADER).\
filter_coap_ack(ADDR_SOL_URI, port=MM).\
filter(lambda p: {
NL_STATUS_TLV,
NL_RLOC16_TLV,
NL_ROUTER_MASK_TLV
} <= set(p.coap.tlv.type) and\
p.coap.code == COAP_CODE_ACK and\
p.thread_address.tlv.rloc16 ==
_pkt_as3.thread_address.tlv.rloc16 and\
p.thread_address.tlv.status == 0
).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,199 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import command
import config
import mle
import thread_cert
from command import CheckType
from pktverify.consts import MLE_PARENT_REQUEST, MLE_CHILD_ID_REQUEST, ADDR_SOL_URI, MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ADDRESS16_TLV, NETWORK_DATA_TLV, TLV_REQUEST_TLV, SCAN_MASK_TLV, VERSION_TLV, ADDRESS_REGISTRATION_TLV, NL_MAC_EXTENDED_ADDRESS_TLV, NL_RLOC16_TLV, NL_STATUS_TLV, NL_ROUTER_MASK_TLV, COAP_CODE_POST, COAP_CODE_ACK
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
LEADER = 1
ROUTER1 = 2
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to verify that when the Leader
# de-allocates a router ID, the DUT, as a router, re-attaches.
#
# Test Topology:
# -------------
# Leader
# |
# Router
#
# DUT Types:
# ----------
# Router
class Cert_5_1_06_RemoveRouterId(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER1]
},
ROUTER1: {
'name': 'ROUTER',
'mode': 'rdn',
'allowlist': [LEADER]
},
}
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
rloc16 = self.nodes[ROUTER1].get_addr16()
self.collect_rloc16s()
router_lladdr = self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL)
self.assertTrue(self.nodes[LEADER].ping(router_lladdr))
self.nodes[LEADER].release_router_id(rloc16 >> 10)
self.simulator.go(5)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
self.assertTrue(self.nodes[LEADER].ping(router_lladdr))
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
LEADER_RLOC16 = pv.vars['LEADER_RLOC16']
ROUTER = pv.vars['ROUTER']
MM = pv.vars['MM_PORT']
# Step 1: Verify topology is formed correctly.
pv.verify_attached('ROUTER')
_pkt_as = pkts.filter_wpan_src64(LEADER).\
filter_coap_ack(ADDR_SOL_URI, port=MM).\
filter(lambda p: {
NL_STATUS_TLV,
NL_RLOC16_TLV,
NL_ROUTER_MASK_TLV
} <= set(p.coap.tlv.type) and\
p.coap.code == COAP_CODE_ACK and\
p.thread_address.tlv.status == 0
).\
must_next()
# Step 3: Router send a properly formatted
# MLE Parent Request,
# MLE Child ID Request,
# and Address Solicit Request
# messages to the Leader.
pkts.filter_wpan_src64(ROUTER).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255 and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 0).\
must_next()
_pkt = pkts.filter_wpan_src64(ROUTER).\
filter_wpan_dst64(LEADER).\
filter_mle_cmd(MLE_CHILD_ID_REQUEST).\
filter(lambda p: {
LINK_LAYER_FRAME_COUNTER_TLV,
MODE_TLV,
RESPONSE_TLV,
TIMEOUT_TLV,
TLV_REQUEST_TLV,
ADDRESS16_TLV,
NETWORK_DATA_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.addr16 is nullField and\
p.thread_nwd.tlv.type is nullField).\
must_next()
_pkt.must_not_verify(lambda p: (ADDRESS_REGISTRATION_TLV) in p.mle.tlv.type)
pkts.filter_wpan_src64(ROUTER).\
filter_wpan_dst16(LEADER_RLOC16).\
filter_coap_request(ADDR_SOL_URI, port=MM).\
filter(lambda p: {
NL_MAC_EXTENDED_ADDRESS_TLV,
NL_RLOC16_TLV,
NL_STATUS_TLV
} <= set(p.coap.tlv.type) and\
p.coap.code == COAP_CODE_POST and\
p.thread_address.tlv.rloc16 ==
_pkt_as.thread_address.tlv.rloc16
).\
must_next()
pkts.filter_wpan_src64(LEADER).\
filter_coap_ack(ADDR_SOL_URI, port=MM).\
filter(lambda p: {
NL_STATUS_TLV,
NL_RLOC16_TLV,
NL_ROUTER_MASK_TLV
} <= set(p.coap.tlv.type) and\
p.coap.code == COAP_CODE_ACK and\
p.thread_address.tlv.rloc16 !=
_pkt_as.thread_address.tlv.rloc16 and\
p.thread_address.tlv.status == 0
).\
must_next()
# Step 4: DUT responds with ICMPv6 Echo Reply
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(LEADER).\
filter_wpan_dst64(ROUTER).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER).\
filter_wpan_dst64(LEADER).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,279 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import thread_cert
from pktverify.consts import MLE_PARENT_RESPONSE, MLE_CHILD_ID_RESPONSE, SOURCE_ADDRESS_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, CONNECTIVITY_TLV, LINK_MARGIN_TLV, VERSION_TLV, ADDRESS_REGISTRATION_TLV
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
LEADER = 1
ROUTER = 2
SED1 = 7
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate the minimum
# conformance requirements for router-capable devices:
# a)Minimum number of supported children.
# b)Minimum MTU requirement when sending/forwarding an
# IPv6 datagram to a SED.
# c)Minimum number of sent/forwarded IPv6 datagrams to
# SED children.
#
# Test Topology:
# -------------
#
# Leader
# |
# Router[DUT]
# / \
# MED1 - MED4 SED1 - SED6
#
# DUT Types:
# ----------
# Router
class Cert_5_1_07_MaxChildCount(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER]
},
ROUTER: {
'name': 'ROUTER',
'max_children': 10,
'mode': 'rdn',
'allowlist': [LEADER, 3, 4, 5, 6, SED1, 8, 9, 10, 11, 12]
},
3: {
'name': 'MED1',
'is_mtd': True,
'mode': 'rn',
'timeout': config.DEFAULT_CHILD_TIMEOUT,
'allowlist': [ROUTER]
},
4: {
'name': 'MED2',
'is_mtd': True,
'mode': 'rn',
'timeout': config.DEFAULT_CHILD_TIMEOUT,
'allowlist': [ROUTER]
},
5: {
'name': 'MED3',
'is_mtd': True,
'mode': 'rn',
'timeout': config.DEFAULT_CHILD_TIMEOUT,
'allowlist': [ROUTER]
},
6: {
'name': 'MED4',
'is_mtd': True,
'mode': 'rn',
'timeout': config.DEFAULT_CHILD_TIMEOUT,
'allowlist': [ROUTER]
},
SED1: {
'name': 'SED1',
'is_mtd': True,
'mode': '-',
'timeout': config.DEFAULT_CHILD_TIMEOUT,
'allowlist': [ROUTER]
},
8: {
'name': 'SED2',
'is_mtd': True,
'mode': '-',
'timeout': config.DEFAULT_CHILD_TIMEOUT,
'allowlist': [ROUTER]
},
9: {
'name': 'SED3',
'is_mtd': True,
'mode': '-',
'timeout': config.DEFAULT_CHILD_TIMEOUT,
'allowlist': [ROUTER]
},
10: {
'name': 'SED4',
'is_mtd': True,
'mode': '-',
'timeout': config.DEFAULT_CHILD_TIMEOUT,
'allowlist': [ROUTER]
},
11: {
'name': 'SED5',
'is_mtd': True,
'mode': '-',
'timeout': config.DEFAULT_CHILD_TIMEOUT,
'allowlist': [ROUTER]
},
12: {
'name': 'SED6',
'is_mtd': True,
'mode': '-',
'timeout': config.DEFAULT_CHILD_TIMEOUT,
'allowlist': [ROUTER]
},
}
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[ROUTER].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER].get_state(), 'router')
for i in range(3, 13):
self.nodes[i].start()
self.simulator.go(7)
self.assertEqual(self.nodes[i].get_state(), 'child')
self.collect_rloc16s()
self.collect_ipaddrs()
ipaddrs = self.nodes[SED1].get_addrs()
for addr in ipaddrs:
if addr[0:4] != 'fe80' and 'ff:fe00' not in addr:
self.assertTrue(self.nodes[LEADER].ping(addr, size=1232))
break
for i in range(3, 13):
ipaddrs = self.nodes[i].get_addrs()
for addr in ipaddrs:
if addr[0:4] != 'fe80' and 'ff:fe00' not in addr:
self.assertTrue(self.nodes[LEADER].ping(addr, size=106))
break
def verify(self, pv: PacketVerifier):
pkts = pv.pkts
pv.summary.show()
ROUTER = pv.vars['ROUTER']
router_pkts = pkts.filter_wpan_src64(ROUTER)
# Step 1: The DUT MUST send properly formatted MLE Parent Response
# and MLE Child ID Response to each child.
for i in range(1, 7):
_pkts = router_pkts.copy().filter_wpan_dst64(pv.vars['SED%d' % i])
_pkts.filter_mle_cmd(MLE_PARENT_RESPONSE).\
filter(lambda p: {
CHALLENGE_TLV,
CONNECTIVITY_TLV,
LEADER_DATA_TLV,
LINK_LAYER_FRAME_COUNTER_TLV,
LINK_MARGIN_TLV,
RESPONSE_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type)
).\
must_next()
_pkts.filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\
filter(lambda p: {
SOURCE_ADDRESS_TLV,
LEADER_DATA_TLV,
ADDRESS16_TLV,
NETWORK_DATA_TLV,
ADDRESS_REGISTRATION_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.addr16 is not nullField and\
p.thread_nwd.tlv.type is not None and\
p.thread_meshcop.tlv.type is not None
).\
must_next()
for i in range(1, 5):
_pkts = router_pkts.copy().filter_wpan_dst64(pv.vars['MED%d' % i])
_pkts.filter_mle_cmd(MLE_PARENT_RESPONSE).\
filter(lambda p: {
CHALLENGE_TLV,
CONNECTIVITY_TLV,
LEADER_DATA_TLV,
LINK_LAYER_FRAME_COUNTER_TLV,
LINK_MARGIN_TLV,
RESPONSE_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type)
).\
must_next()
_pkts.filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\
filter(lambda p: {
SOURCE_ADDRESS_TLV,
LEADER_DATA_TLV,
ADDRESS16_TLV,
NETWORK_DATA_TLV,
ADDRESS_REGISTRATION_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.addr16 is not nullField and\
p.thread_meshcop.tlv.type is not None
).\
must_next()
# Step 2: The DUT MUST properly forward ICMPv6 Echo Requests to all MED children
# The DUT MUST properly forward ICMPv6 Echo Replies to the Leader
leader_rloc16 = pv.vars['LEADER_RLOC16']
for i in range(1, 5):
rloc16 = pv.vars['MED%d_RLOC16' % i]
_pkts = router_pkts.copy()
p = _pkts.filter('wpan.dst16 == {rloc16}', rloc16=rloc16).\
filter_ping_request().\
must_next()
_pkts.filter('wpan.dst16 == {rloc16}',
rloc16=leader_rloc16).\
filter_ping_reply(identifier=p.icmpv6.echo.identifier).\
must_next()
# Step 3: The DUT MUST properly forward ICMPv6 Echo Requests to all SED children
# The DUT MUST properly forward ICMPv6 Echo Replies to the Leader
for i in range(1, 7):
rloc16 = pv.vars['SED%d_RLOC16' % i]
_pkts = router_pkts.copy()
p = _pkts.filter('wpan.dst16 == {rloc16}', rloc16=rloc16).\
filter_ping_request().\
must_next()
_pkts.filter('wpan.dst16 == {rloc16}',
rloc16=leader_rloc16).\
filter_ping_reply(identifier=p.icmpv6.echo.identifier).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,220 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import mle
import thread_cert
from pktverify.consts import MLE_PARENT_REQUEST, MLE_PARENT_RESPONSE, MLE_CHILD_ID_REQUEST, SOURCE_ADDRESS_TLV, MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, TLV_REQUEST_TLV, SCAN_MASK_TLV, CONNECTIVITY_TLV, LINK_MARGIN_TLV, VERSION_TLV, ADDRESS_REGISTRATION_TLV
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
LEADER = 1
ROUTER1 = 2
ROUTER2 = 3
ROUTER3 = 4
ROUTER4 = 5
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to verify that the DUT chooses
# to attach to a router with better connectivity
#
# Test Topology:
# -------------
# Leader--Router1
# / \ /
# Router2 Router3
# \ /
# Router4[DUT]
#
# DUT Types:
# ----------
# Router
class Cert_5_1_08_RouterAttachConnectivity(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER1, ROUTER2, ROUTER3]
},
ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [LEADER, ROUTER3]
},
ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [LEADER, ROUTER4]
},
ROUTER3: {
'name': 'ROUTER_3',
'mode': 'rdn',
'allowlist': [LEADER, ROUTER1, ROUTER4]
},
ROUTER4: {
'name': 'ROUTER_4',
'mode': 'rdn',
'allowlist': [ROUTER2, ROUTER3]
},
}
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
for i in range(2, 5):
self.nodes[i].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
for i in range(2, 5):
self.assertEqual(self.nodes[i].get_state(), 'router')
self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL)
self.nodes[ROUTER4].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER4].get_state(), 'router')
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
ROUTER_2 = pv.vars['ROUTER_2']
ROUTER_3 = pv.vars['ROUTER_3']
ROUTER_4 = pv.vars['ROUTER_4']
# Step 1: Verify all routers and Leader are sending MLE advertisements.
for i in (1, 2, 3):
with pkts.save_index():
pv.verify_attached('ROUTER_%d' % i)
# Step 3: DUT sends a MLE Parent Request with an IP hop limit of
# 255 to the Link-Local All Routers multicast address (FF02::2).
# The following TLVs MUST be present in the MLE Parent Request:
# - Challenge TLV
# - Mode TLV
# - Scan Mask TLV
# If the DUT sends multiple MLE Parent Requests
# - The first one MUST be sent only to all Routers
# - Subsequent ones MAY be sent to all Routers and REEDS
# - Version TLV
# If the first MLE Parent Request was sent to all Routers and
# REEDS, the test fails.
pkts.filter_wpan_src64(ROUTER_4).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255 and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 0
).\
must_next()
# Step 4: Router2 and Router3 respond with a MLE Parent Response.
# The following TLVs MUST be present in the MLE Parent Response:
# - Challenge TLV
# - Connectivity TLV
# - Leader Data TLV
# - Link-layer Frame Counter TLV
# - Link Margin TLV
# - Response TLV
# - Source Address
# - Version TLV
# - MLE Frame Counter TLV (optional)
for i in (2, 3):
with pkts.save_index():
pkts.filter_wpan_src64(pv.vars['ROUTER_%d' % i]).\
filter_wpan_dst64(ROUTER_4).\
filter_mle_cmd(MLE_PARENT_RESPONSE).\
filter(lambda p: {
CHALLENGE_TLV,
CONNECTIVITY_TLV,
LEADER_DATA_TLV,
LINK_LAYER_FRAME_COUNTER_TLV,
LINK_MARGIN_TLV,
RESPONSE_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type)
).\
must_next()
# Step 5: DUT sends a MLE Child ID Request to Router3.
# The following TLVs MUST be present in the MLE Child ID Request:
# - Link-layer Frame Counter TLV
# - Mode TLV
# - Response TLV
# - Timeout TLV
# - TLV Request TLV
# - Address16 TLV
# - Network Data TLV
# - Route64 TLV (optional)
# - Version TLV
# - MLE Frame Counter TLV (optional)
# The following TLV MUST NOT be present in the MLE Child ID Request:
# - Address Registration TLV
_pkt = pkts.filter_wpan_src64(ROUTER_4).\
filter_wpan_dst64(ROUTER_3).\
filter_mle_cmd(MLE_CHILD_ID_REQUEST).\
filter(lambda p: {
LINK_LAYER_FRAME_COUNTER_TLV,
MODE_TLV,
RESPONSE_TLV,
TIMEOUT_TLV,
TLV_REQUEST_TLV,
ADDRESS16_TLV,
NETWORK_DATA_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.addr16 is nullField and\
p.thread_nwd.tlv.type is nullField
).\
must_next()
_pkt.must_not_verify(lambda p: (ADDRESS_REGISTRATION_TLV) in p.mle.tlv.type)
if __name__ == '__main__':
unittest.main()
@@ -1,274 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import mle
import thread_cert
from pktverify.consts import MLE_ADVERTISEMENT, MLE_PARENT_REQUEST, MLE_PARENT_RESPONSE, MLE_CHILD_ID_REQUEST, SOURCE_ADDRESS_TLV, MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, TLV_REQUEST_TLV, SCAN_MASK_TLV, CONNECTIVITY_TLV, LINK_MARGIN_TLV, VERSION_TLV, ADDRESS_REGISTRATION_TLV
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
LEADER = 1
ROUTER1 = 2
REED1 = 3
REED2 = 4
ROUTER2 = 5
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to verify that the DUT will
# pick REED_1 as its parent because of its better connectivity
#
# Test Topology:
# -------------
# Leader--Router1
# / \ /
# REED2 REED1
# \ /
# Router2[DUT]
#
# DUT Types:
# ----------
# Router
class Cert_5_1_09_REEDAttachConnectivity(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER1, REED1, REED2]
},
ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [LEADER, REED1]
},
REED1: {
'name': 'REED_1',
'mode': 'rdn',
'router_upgrade_threshold': 0,
'allowlist': [LEADER, ROUTER1, ROUTER2]
},
REED2: {
'name': 'REED_2',
'mode': 'rdn',
'router_upgrade_threshold': 0,
'allowlist': [LEADER, ROUTER2]
},
ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [REED1, REED2]
},
}
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
for i in range(2, 5):
self.nodes[i].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
self.assertEqual(self.nodes[REED1].get_state(), 'child')
self.assertEqual(self.nodes[REED2].get_state(), 'child')
self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL)
self.nodes[ROUTER2].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
self.assertEqual(self.nodes[REED1].get_state(), 'router')
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
REED_1 = pv.vars['REED_1']
ROUTER_1 = pv.vars['ROUTER_1']
ROUTER_2 = pv.vars['ROUTER_2']
# Step 1: Verify ROUTER_1 and Leader are sending MLE advertisements.
pkts.filter_wpan_src64(LEADER).\
filter_mle_cmd(MLE_ADVERTISEMENT).\
must_next()
pv.verify_attached('ROUTER_1')
# Step 3: The DUT sends a MLE Parent Request with an IP hop limit of
# 255 to the Link-Local All Routers multicast address (FF02::2).
# The following TLVs MUST be present in the MLE Parent Request:
# - Challenge TLV
# - Mode TLV
# - Scan Mask TLV
# If the DUT sends multiple MLE Parent Requests
# - The first one MUST be sent only to all Routers
# - Subsequent ones MAY be sent to all Routers and REEDS
# - Version TLV
# If the first MLE Parent Request was sent to all Routers and
# REEDS, the test fails.
pkts.filter_wpan_src64(ROUTER_2).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255 and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 0
).\
must_next()
lstart = pkts.index
# Step 5: The DUT MUST send a MLE Parent Request with an IP hop limit of
# 255 to the Link-Local All Routers multicast address (FF02::2).
# The following TLVs MUST be present in the MLE Parent Request:
# - Challenge TLV
# - Mode TLV
# - Scan Mask TLV
# - The Scan Mask TLV MUST be sent to Routers And REEDs
# - Version TLV
#
# In securing the first three messages of the attaching process,
# the full four-byte key sequence number MUST be included in
# the Auxiliary Security Header used for MLE security.
#
# To send the full four-byte key sequence number, the Key
# Identifier Mode of the Security Control Field SHALL be set to
# 0x02, indicating the presence of a four-byte Key Source,
# which SHALL contain the four-byte key sequence number in
# network byte order.
pkts.filter_wpan_src64(ROUTER_2).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255 and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 1 and\
p.wpan.aux_sec.key_id_mode == 0x2
).\
must_next()
lend = pkts.index
# Step 4: REED_1 and REED_2 no response to Parent Request meant for all routers.
for i in (1, 2):
pkts.range(lstart, lend).\
filter_wpan_src64(pv.vars['REED_%d' % i]).\
filter_wpan_dst64(ROUTER_2).\
filter_mle_cmd(MLE_PARENT_RESPONSE).\
must_not_next()
# Step 6: REED_1 and REED_2 respond with a MLE Parent Response.
# The following TLVs MUST be present in the MLE Parent Response:
# - Challenge TLV
# - Connectivity TLV
# - Leader Data TLV
# - Link-layer Frame Counter TLV
# - Link Margin TLV
# - Response TLV
# - Source Address
# - Version TLV
# - MLE Frame Counter TLV (optional)
for i in (1, 2):
with pkts.save_index():
pkts.filter_wpan_src64(pv.vars['REED_%d' % i]).\
filter_wpan_dst64(ROUTER_2).\
filter_mle_cmd(MLE_PARENT_RESPONSE).\
filter(lambda p: {
CHALLENGE_TLV,
CONNECTIVITY_TLV,
LEADER_DATA_TLV,
LINK_LAYER_FRAME_COUNTER_TLV,
LINK_MARGIN_TLV,
RESPONSE_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.wpan.aux_sec.key_id_mode == 0x2
).\
must_next()
# Step 7: DUT sends a MLE Child ID Request to REED_1.
# The following TLVs MUST be present in the MLE Child ID Request:
# - Link-layer Frame Counter TLV
# - Mode TLV
# - Response TLV
# - Timeout TLV
# - TLV Request TLV
# - Address16 TLV
# - Network Data TLV
# - Route64 TLV (optional)
# - Version TLV
# - MLE Frame Counter TLV (optional)
# The following TLV MUST NOT be present in the MLE Child ID Request:
# - Address Registration TLV
_pkt = pkts.filter_wpan_src64(ROUTER_2).\
filter_wpan_dst64(REED_1).\
filter_mle_cmd(MLE_CHILD_ID_REQUEST).\
filter(lambda p: {
LINK_LAYER_FRAME_COUNTER_TLV,
MODE_TLV,
RESPONSE_TLV,
TIMEOUT_TLV,
TLV_REQUEST_TLV,
ADDRESS16_TLV,
NETWORK_DATA_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.addr16 is nullField and\
p.thread_nwd.tlv.type is nullField and\
p.wpan.aux_sec.key_id_mode == 0x2
).\
must_next()
_pkt.must_not_verify(lambda p: (ADDRESS_REGISTRATION_TLV) in p.mle.tlv.type)
if __name__ == '__main__':
unittest.main()
@@ -1,214 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import mle
import thread_cert
from pktverify.consts import MLE_ADVERTISEMENT, MLE_PARENT_REQUEST, MLE_PARENT_RESPONSE, MLE_CHILD_ID_REQUEST, SOURCE_ADDRESS_TLV, MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, TLV_REQUEST_TLV, SCAN_MASK_TLV, CONNECTIVITY_TLV, LINK_MARGIN_TLV, VERSION_TLV, ADDRESS_REGISTRATION_TLV
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
LEADER = 1
ROUTER1 = 2
ROUTER2 = 3
ROUTER3 = 4
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate that the DUT
# will choose a router with better link quality as its parent.
#
# Test Topology:
# -------------
# Leader
# / \
# ROUTER2 ROUTER1
# \ /(better link_quality)
# Router3[DUT]
#
# DUT Types:
# ----------
# Router
class Cert_5_1_10_RouterAttachLinkQuality(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER1, ROUTER2]
},
ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [LEADER, ROUTER3]
},
ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [LEADER, (ROUTER3, -85)]
},
ROUTER3: {
'name': 'ROUTER_3',
'mode': 'rdn',
'allowlist': [ROUTER1, ROUTER2]
},
}
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
self.nodes[ROUTER2].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
self.nodes[ROUTER3].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER3].get_state(), 'router')
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
ROUTER_1 = pv.vars['ROUTER_1']
ROUTER_2 = pv.vars['ROUTER_2']
ROUTER_3 = pv.vars['ROUTER_3']
# Step 1: Verify ROUTER_1, Router2 and Leader are sending MLE advertisements.
pkts.filter_wpan_src64(LEADER).\
filter_mle_cmd(MLE_ADVERTISEMENT).\
must_next()
pv.verify_attached('ROUTER_1')
pv.verify_attached('ROUTER_2')
# Step 3: The DUT sends a MLE Parent Request with an IP hop limit of
# 255 to the Link-Local All Routers multicast address (FF02::2).
# The following TLVs MUST be present in the MLE Parent Request:
# - Challenge TLV
# - Mode TLV
# - Scan Mask TLV
# If the DUT sends multiple MLE Parent Requests
# - The first one MUST be sent only to all Routers
# - Subsequent ones MAY be sent to all Routers and REEDS
# - Version TLV
pkts.filter_wpan_src64(ROUTER_3).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255 and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 0
).\
must_next()
# Step 4: ROUTER_1 and ROUTER_2 respond with a MLE Parent Response.
# The following TLVs MUST be present in the MLE Parent Response:
# - Challenge TLV
# - Connectivity TLV
# - Leader Data TLV
# - Link-layer Frame Counter TLV
# - Link Margin TLV
# - Response TLV
# - Source Address
# - Version TLV
# - MLE Frame Counter TLV (optional)
for i in range(1, 3):
with pkts.save_index():
pkts.filter_wpan_src64(pv.vars['ROUTER_%d' % i]).\
filter_wpan_dst64(ROUTER_3).\
filter_mle_cmd(MLE_PARENT_RESPONSE).\
filter(lambda p: {
CHALLENGE_TLV,
CONNECTIVITY_TLV,
LEADER_DATA_TLV,
LINK_LAYER_FRAME_COUNTER_TLV,
LINK_MARGIN_TLV,
RESPONSE_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type)
).\
must_next()
# Step 5: The DUT MUST initiate the attach process with Router_1 by
# sending an MLE Child ID Request; if not, the test fails.
# The following TLVs MUST be present in the MLE Child ID Request:
# - Link-layer Frame Counter TLV
# - Mode TLV
# - Response TLV
# - Timeout TLV
# - TLV Request TLV
# - Address16 TLV
# - Network Data TLV
# - Route64 TLV (optional)
# - Version TLV
# - MLE Frame Counter TLV (optional)
# The following TLV MUST NOT be present in the MLE Child ID Request:
# - Address Registration TLV
_pkt = pkts.filter_wpan_src64(ROUTER_3).\
filter_wpan_dst64(ROUTER_1).\
filter_mle_cmd(MLE_CHILD_ID_REQUEST).\
filter(lambda p: {
LINK_LAYER_FRAME_COUNTER_TLV,
MODE_TLV,
RESPONSE_TLV,
TIMEOUT_TLV,
TLV_REQUEST_TLV,
ADDRESS16_TLV,
NETWORK_DATA_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.addr16 is nullField and\
p.thread_nwd.tlv.type is nullField
).\
must_next()
_pkt.must_not_verify(lambda p: (ADDRESS_REGISTRATION_TLV) in p.mle.tlv.type)
if __name__ == '__main__':
unittest.main()
@@ -1,213 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import mle
import thread_cert
from pktverify.consts import MLE_ADVERTISEMENT, MLE_PARENT_REQUEST, MLE_PARENT_RESPONSE, MLE_CHILD_ID_REQUEST, SOURCE_ADDRESS_TLV, MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, TLV_REQUEST_TLV, SCAN_MASK_TLV, CONNECTIVITY_TLV, LINK_MARGIN_TLV, VERSION_TLV, ADDRESS_REGISTRATION_TLV
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
LEADER = 1
REED = 2
ROUTER2 = 3
ROUTER1 = 4
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate that DUT will
# attach to a REED with the highest link quality, when routers
# with the highest link quality are not available.
#
# Test Topology:
# -------------
# Leader
# / \
# Router2 REED1
# \ /(better link_quality)
# Router1[DUT]
#
# DUT Types:
# ----------
# Router
class Cert_5_1_11_REEDAttachLinkQuality(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [REED, ROUTER2]
},
REED: {
'name': 'REED_1',
'mode': 'rdn',
'router_upgrade_threshold': 0,
'allowlist': [LEADER, ROUTER1]
},
ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [LEADER, (ROUTER1, -85)]
},
ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [REED, ROUTER2]
},
}
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[REED].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[REED].get_state(), 'child')
self.nodes[ROUTER2].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
self.nodes[ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
self.assertEqual(self.nodes[REED].get_state(), 'router')
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
REED_1 = pv.vars['REED_1']
ROUTER_1 = pv.vars['ROUTER_1']
ROUTER_2 = pv.vars['ROUTER_2']
# Step 1: Verify ROUTER_2 and Leader are sending MLE advertisements.
pkts.filter_wpan_src64(LEADER).\
filter_mle_cmd(MLE_ADVERTISEMENT).\
must_next()
pv.verify_attached('ROUTER_2')
# Step 3: The DUT sends a MLE Parent Request with an IP hop limit of
# 255 to the Link-Local All Routers multicast address (FF02::2).
# The following TLVs MUST be present in the MLE Parent Request:
# - Challenge TLV
# - Mode TLV
# - Scan Mask TLV
# If the DUT sends multiple MLE Parent Requests
# - The first one MUST be sent only to all Routers
# - Subsequent ones MAY be sent to all Routers and REEDS
# - Version TLV
# If the first MLE Parent Request was sent to all Routers and
# REEDS, the test fails.
pkts.filter_wpan_src64(ROUTER_1).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255 and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 0
).\
must_next()
# Step 5: The DUT MUST send a MLE Parent Request with an IP hop limit of
# 255 to the Link-Local All Routers multicast address (FF02::2).
# The following TLVs MUST be present in the MLE Parent Request:
# - Challenge TLV
# - Mode TLV
# - Scan Mask TLV
# - The Scan Mask TLV MUST be sent to Routers And REEDs
# - Version TLV
#
pkts.filter_wpan_src64(ROUTER_1).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255 and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 1
).\
must_next()
# Step 6: The DUT MUST initiate the attach process with REED_1 by
# sending an MLE Child ID Request; if not, the test fails.
# The following TLVs MUST be present in the MLE Child ID Request:
# - Link-layer Frame Counter TLV
# - Mode TLV
# - Response TLV
# - Timeout TLV
# - TLV Request TLV
# - Address16 TLV
# - Network Data TLV
# - Route64 TLV (optional)
# - Version TLV
# - MLE Frame Counter TLV (optional)
# The following TLV MUST NOT be present in the MLE Child ID Request:
# - Address Registration TLV
_pkt = pkts.filter_wpan_src64(ROUTER_1).\
filter_wpan_dst64(REED_1).\
filter_mle_cmd(MLE_CHILD_ID_REQUEST).\
filter(lambda p: {
LINK_LAYER_FRAME_COUNTER_TLV,
MODE_TLV,
RESPONSE_TLV,
TIMEOUT_TLV,
TLV_REQUEST_TLV,
ADDRESS16_TLV,
NETWORK_DATA_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.addr16 is nullField and\
p.thread_nwd.tlv.type is nullField
).\
must_next()
_pkt.must_not_verify(lambda p: (ADDRESS_REGISTRATION_TLV) in p.mle.tlv.type)
if __name__ == '__main__':
unittest.main()
@@ -1,208 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import mle
import thread_cert
from pktverify.consts import MLE_ADVERTISEMENT, MLE_PARENT_REQUEST, MLE_PARENT_RESPONSE, MLE_CHILD_UPDATE_RESPONSE, MLE_CHILD_ID_REQUEST, MLE_CHILD_ID_RESPONSE, MLE_LINK_REQUEST, MLE_LINK_ACCEPT, MLE_LINK_ACCEPT_AND_REQUEST, ADDR_SOL_URI, SOURCE_ADDRESS_TLV, MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, MLE_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, TLV_REQUEST_TLV, SCAN_MASK_TLV, CONNECTIVITY_TLV, LINK_MARGIN_TLV, VERSION_TLV, ADDRESS_REGISTRATION_TLV, NL_MAC_EXTENDED_ADDRESS_TLV, NL_RLOC16_TLV, NL_STATUS_TLV, NL_ROUTER_MASK_TLV, COAP_CODE_ACK
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
LEADER = 1
ROUTER1 = 2
ROUTER2 = 3
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate that when the DUT sees a new router for the first time, it will synchronize using the New Router Neighbor Synchronization procedure.
#
# Test Topology:
# -------------
# Leader
# / \
# Router2 Router1[DUT]
#
# DUT Types:
# ----------
# Router
class Cert_5_1_12_NewRouterSync(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER1, ROUTER2]
},
ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [LEADER]
},
ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [LEADER]
},
}
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
self.nodes[ROUTER2].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
self.simulator.go(10)
self.nodes[ROUTER1].add_allowlist(self.nodes[ROUTER2].get_addr64())
self.nodes[ROUTER2].add_allowlist(self.nodes[ROUTER1].get_addr64())
self.simulator.go(35)
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
ROUTER_1 = pv.vars['ROUTER_1']
ROUTER_2 = pv.vars['ROUTER_2']
# Step 1: Verify topology is formed correctly.
# Step 2: The DUT MUST send properly formatted MLE Advertisements with
# an IP Hop Limit of 255 to the Link-Local All Nodes multicast
# address (FF02::1).
# The following TLVs MUST be present in the MLE Advertisements:
# - Leader Data TLV
# - Route64 TLV
# - Source Address TLV
pv.verify_attached('ROUTER_1')
pkts.filter_wpan_src64(ROUTER_1).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: {
LEADER_DATA_TLV,
ROUTE64_TLV,
SOURCE_ADDRESS_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255).\
must_next()
pv.verify_attached('ROUTER_2')
pkts.filter_wpan_src64(ROUTER_2).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: {
LEADER_DATA_TLV,
ROUTE64_TLV,
SOURCE_ADDRESS_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255).\
must_next()
# Step 4: The DUT and Router_2 exchange unicast Link Request and unicast
# Link Accept messages OR Link Accept and Request messages.
#
# The Link Request Message MUST be unicast and contain
# the following TLVs:
# - Challenge TLV
# - Leader Data TLV
# - Source Address TLV
# - Version TLV
# - TLV Request TLV: Link Margin
#
# Link Accept or Link Accept And Request Messages MUST be
# Unicast.
# The following TLVs MUST be present in the Link Accept or
# Link Accept And Request Messages :
# - Leader Data TLV
# - Link-layer Frame Counter TLV
# - Link Margin TLV
# - Response TLV
# - Source Address TLV
# - Version TLV
# - TLV Request TLV: Link Margin
# - Challenge TLV (optional)
# - MLE Frame Counter TLV (optional)
# The Challenge TLV and TLV Request TLV MUST be included
# if the response is an Accept and Request message.
lq_src = ROUTER_1
lq_dst = ROUTER_2
_pkt = pkts.filter_mle_cmd(MLE_LINK_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
LEADER_DATA_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV,
TLV_REQUEST_TLV,
LINK_MARGIN_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.link_margin is nullField and\
(p.wpan.src64 == ROUTER_1 or\
p.wpan.src64 == ROUTER_2)
).\
must_next()
if _pkt.wpan.src64 != ROUTER_1:
_pkt.must_verify(lambda p: p.wpan.dst64 == ROUTER_1)
lq_src = ROUTER_2
lq_dst = ROUTER_1
_pkt = pkts.filter_wpan_src64(lq_dst).\
filter_wpan_dst64(lq_src).\
filter_mle_cmd2(MLE_LINK_ACCEPT, MLE_LINK_ACCEPT_AND_REQUEST).\
filter(lambda p: {
LEADER_DATA_TLV,
LINK_LAYER_FRAME_COUNTER_TLV,
LINK_MARGIN_TLV,
RESPONSE_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.link_margin is not nullField
).\
must_next()
if _pkt.mle.cmd == MLE_LINK_ACCEPT_AND_REQUEST:
_pkt.must_verify(lambda p: {CHALLENGE_TLV, TLV_REQUEST_TLV} <= set(p.mle.tlv.type))
if __name__ == '__main__':
unittest.main()
@@ -1,206 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import mle
import thread_cert
from pktverify.consts import MLE_ADVERTISEMENT, MLE_LINK_REQUEST, MLE_LINK_ACCEPT, MLE_LINK_ACCEPT_AND_REQUEST, SOURCE_ADDRESS_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, TLV_REQUEST_TLV, VERSION_TLV, MLE_MAX_RESPONSE_DELAY
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
LEADER = 1
ROUTER = 2
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate that when a router resets,
# it will synchronize upon returning by using the Router Synchronization
# after Reset procedure.
#
# Test Topology:
# -------------
# Leader
# |
# Router
#
# DUT Types:
# ----------
# Leader
# Router
class Cert_5_1_13_RouterReset(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER]
},
ROUTER: {
'name': 'ROUTER',
'mode': 'rdn',
'allowlist': [LEADER]
},
}
def _setUpRouter(self):
self.nodes[ROUTER].add_allowlist(self.nodes[LEADER].get_addr64())
self.nodes[ROUTER].enable_allowlist()
self.nodes[ROUTER].set_router_selection_jitter(1)
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[ROUTER].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER].get_state(), 'router')
self.collect_rloc16s()
self.nodes[ROUTER].reset()
self._setUpRouter()
self.simulator.go(5)
self.nodes[ROUTER].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER].get_state(), 'router')
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
ROUTER = pv.vars['ROUTER']
ROUTER_RLOC16 = pv.vars['ROUTER_RLOC16']
# Step 1: Verify topology is formed correctly.
# Step 2: Devices MUST send properly formatted MLE Advertisements with
# an IP Hop Limit of 255 to the Link-Local All Nodes multicast
# address (FF02::1).
# The following TLVs MUST be present in the MLE Advertisements:
# - Leader Data TLV
# - Route64 TLV
# - Source Address TLV
pv.verify_attached('ROUTER')
pkts.filter_wpan_src64(ROUTER).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: {
LEADER_DATA_TLV,
ROUTE64_TLV,
SOURCE_ADDRESS_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255
).\
must_next()
pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: {
LEADER_DATA_TLV,
ROUTE64_TLV,
SOURCE_ADDRESS_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255
).\
must_next()
# Step 4: Router sends multicast Link Request message
#
# The Link Request Message MUST be sent to the Link-Local All Routers
# Routers multicast address (FF02::2) and contain the following TLVs:
# - Challenge TLV
# - TLV Request TLV
# - Address16 TLV
# - Route64 TLV
# - Version TLV
#
_pkt_lq = pkts.filter_mle_cmd(MLE_LINK_REQUEST).\
filter_LLARMA().\
filter(lambda p: {
CHALLENGE_TLV,
VERSION_TLV,
TLV_REQUEST_TLV,
ADDRESS16_TLV,
ROUTE64_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.addr16 is nullField and \
p.mle.tlv.route64.id_mask is nullField
).\
must_next()
# Step 5: Leader replies to Router with Link Accept message
# The following TLVs MUST be present in the Link Accept message:
# - Leader Data TLV
# - Link-layer Frame Counter TLV
# - Link Margin TLV
# - Response TLV
# - Source Address TLV
# - Version TLV
# - TLV Request TLV: Link Margin
# - Challenge TLV (optional)
# - MLE Frame Counter TLV (optional)
# The Challenge TLV MUST be included if the response is an
# Accept and Request message.
# Responses to multicast Link Requests MUST be delayed by a
# random time of up to MLE_MAX_RESPONSE_DELAY (1 second).
_pkt = pkts.filter_wpan_src64(LEADER).\
filter_wpan_dst64(ROUTER).\
filter_mle_cmd2(MLE_LINK_ACCEPT, MLE_LINK_ACCEPT_AND_REQUEST).\
filter(lambda p: {
ADDRESS16_TLV,
LEADER_DATA_TLV,
LINK_LAYER_FRAME_COUNTER_TLV,
RESPONSE_TLV,
ROUTE64_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.addr16 is not nullField and \
p.mle.tlv.route64.id_mask is not nullField and\
p.mle.tlv.addr16 == ROUTER_RLOC16 and\
p.sniff_timestamp - _pkt_lq.sniff_timestamp <
MLE_MAX_RESPONSE_DELAY + 0.1
).\
must_next()
if _pkt.mle.cmd == MLE_LINK_ACCEPT_AND_REQUEST:
_pkt.must_verify(lambda p: [CHALLENGE_TLV] in p.mle.tlv.type)
if __name__ == '__main__':
unittest.main()
@@ -1,305 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2018, 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 unittest
import command
import config
import mle
import thread_cert
from pktverify.consts import MLE_ADVERTISEMENT, MLE_PARENT_REQUEST, MLE_PARENT_RESPONSE, MLE_CHILD_ID_RESPONSE, ADDR_SOL_URI, SOURCE_ADDRESS_TLV, MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, TLV_REQUEST_TLV, SCAN_MASK_TLV, CONNECTIVITY_TLV, LINK_MARGIN_TLV, VERSION_TLV, NL_MAC_EXTENDED_ADDRESS_TLV, NL_RLOC16_TLV, NL_STATUS_TLV, NL_ROUTER_MASK_TLV, COAP_CODE_ACK
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
LEADER = 1
DUT_ROUTER1 = 2
REED1 = 3
MED1 = 4
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to show that the DUT is able to attach
# a REED and forward address solicits two hops away from the Leader.
#
# Test Topology:
# -------------
# Leader
# |
# Router_1
# |
# REED_1
# |
# MED_1
#
# DUT Types:
# ----------
# Router
class Cert_5_2_01_REEDAttach(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [DUT_ROUTER1]
},
DUT_ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [LEADER, REED1]
},
REED1: {
'name': 'REED_1',
'mode': 'rdn',
'router_upgrade_threshold': 1,
'allowlist': [DUT_ROUTER1, MED1]
},
MED1: {
'name': 'MED_1',
'is_mtd': True,
'mode': 'rn',
'allowlist': [REED1]
},
}
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[DUT_ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[DUT_ROUTER1].get_state(), 'router')
self.nodes[REED1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[REED1].get_state(), 'child')
self.nodes[MED1].start()
self.simulator.go(5)
self.assertEqual(self.nodes[MED1].get_state(), 'child')
self.collect_rloc16s()
self.collect_ipaddrs()
self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL)
reed_mleid = self.nodes[REED1].get_ip6_address(config.ADDRESS_TYPE.ML_EID)
self.assertTrue(self.nodes[LEADER].ping(reed_mleid))
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
LEADER_MLEID = pv.vars['LEADER_MLEID']
LEADER_RLOC16 = pv.vars['LEADER_RLOC16']
ROUTER_1 = pv.vars['ROUTER_1']
ROUTER_1_RLOC16 = pv.vars['ROUTER_1_RLOC16']
REED_1 = pv.vars['REED_1']
REED_1_MLEID = pv.vars['REED_1_MLEID']
# Step 1: Router_1 attaches to Leader and sends properly formatted MLE
# advertisements
# Advertisements MUST be sent with an IP hop limit of 255 to
# the Link-Local All Nodes multicast address (FF02::1).
# The following TLVs MUST be present in the MLE Advertisements:
# - Leader Data TLV
# - Route64 TLV
# - Source Address TLV
pv.verify_attached('ROUTER_1')
pkts.filter_wpan_src64(ROUTER_1).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: {
LEADER_DATA_TLV,
ROUTE64_TLV,
SOURCE_ADDRESS_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255
).\
must_next()
# Step 2: Attach REED_1 to Router_1; REED_1 sends MLE Parent Request with
# an IP hop limit of 255 to the Link-Local All Routers multicast
# address (FF02::2).
# The following TLVs MUST be present in the MLE Parent Request:
# - Challenge TLV
# - Mode TLV
# - Scan Mask TLV
# If the DUT sends multiple MLE Parent Requests
# - The first one MUST be sent only to all Routers
# - Subsequent ones MAY be sent to all Routers and REEDS
# - Version TLV
pkts.filter_wpan_src64(REED_1).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255 and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 0).\
must_next()
# Step 3: Router_1 must respond with a MLE Parent Response.
# The following TLVs MUST be present in the MLE Parent Response:
# - Challenge TLV
# - Connectivity TLV
# - Leader Data TLV
# - Link-layer Frame Counter TLV
# - Link Margin TLV
# - Response TLV
# - Source Address
# - Version TLV
# - MLE Frame Counter TLV (optional)
pkts.filter_wpan_src64(ROUTER_1).\
filter_wpan_dst64(REED_1).\
filter_mle_cmd(MLE_PARENT_RESPONSE).\
filter(lambda p: {
CHALLENGE_TLV,
CONNECTIVITY_TLV,
LEADER_DATA_TLV,
LINK_LAYER_FRAME_COUNTER_TLV,
LINK_MARGIN_TLV,
RESPONSE_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type)).\
must_next()
# Step 4: Router_1 must respond with a Child ID Response.
# The following TLVs MUST be present in the Child ID Response:
# - Address16 TLV
# - Leader Data TLV
# - Network Data TLV
# - Source Address TLV
# - Route64 TLV (if requested)
pkts.filter_wpan_src64(ROUTER_1).\
filter_wpan_dst64(REED_1).\
filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\
filter(lambda p: {
ADDRESS16_TLV,
LEADER_DATA_TLV,
NETWORK_DATA_TLV,
SOURCE_ADDRESS_TLV,
ROUTE64_TLV
} <= set(p.mle.tlv.type) or\
{
ADDRESS16_TLV,
LEADER_DATA_TLV,
NETWORK_DATA_TLV,
SOURCE_ADDRESS_TLV
} <= set(p.mle.tlv.type)
).\
must_next()
# Step 7: REED_1 sends an Address Solicit Request to Router_1.
# Ensure the Address Solicit Request is properly formatted:
# CoAP Request URI
# coap://<leader address>:MM/a/as
# CoAP Payload
# - MAC Extended Address TLV
# - Status TLV
_pkt1 = pkts.filter_wpan_src64(REED_1).\
filter_wpan_dst16(ROUTER_1_RLOC16).\
filter_coap_request(ADDR_SOL_URI).\
filter(lambda p: {
NL_MAC_EXTENDED_ADDRESS_TLV,
NL_STATUS_TLV
} <= set(p.coap.tlv.type)\
).\
must_next()
# Step 8: Router_1 forward the REED_1's Address Solicit Request to
# Leader and Leader's Address Solicit Response to REED_1.
# Ensure the Address Solicit Response is properly formatted:
# CoAP Response Code
# 2.04 Changed
# CoAP Payload
# - Status TLV (value = Success)
# - RLOC16 TLV
# - Router Mask TLV
_pkt2 = pkts.filter_wpan_src64(ROUTER_1).\
filter_wpan_dst16(LEADER_RLOC16).\
filter_coap_request(ADDR_SOL_URI).\
filter(lambda p: {
NL_MAC_EXTENDED_ADDRESS_TLV,
NL_STATUS_TLV
} <= set(p.coap.tlv.type)\
).\
must_next()
pkts.filter_wpan_src64(LEADER).\
filter_wpan_dst16(_pkt2.wpan.src16).\
filter_coap_ack(ADDR_SOL_URI).\
filter(lambda p: {
NL_STATUS_TLV,
NL_RLOC16_TLV,
NL_ROUTER_MASK_TLV
} <= set(p.coap.tlv.type) and\
p.coap.code == COAP_CODE_ACK and\
p.thread_address.tlv.status == 0\
).\
must_next()
pkts.filter_wpan_src64(ROUTER_1).\
filter_wpan_dst16(_pkt1.wpan.src16).\
filter_coap_ack(ADDR_SOL_URI).\
filter(lambda p: {
NL_STATUS_TLV,
NL_RLOC16_TLV,
NL_ROUTER_MASK_TLV
} <= set(p.coap.tlv.type) and\
p.coap.code == COAP_CODE_ACK and\
p.thread_address.tlv.status == 0\
).\
must_next()
# Step 9: REED_1 responds with ICMPv6 Echo Reply
_pkt = pkts.filter_ipv6_src_dst(LEADER_MLEID, REED_1_MLEID).\
filter_ping_request().\
must_next()
pkts.filter_ipv6_src_dst(REED_1_MLEID, LEADER_MLEID).\
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,428 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import mle
import network_layer
import thread_cert
from pktverify.consts import MLE_ADVERTISEMENT, MLE_PARENT_RESPONSE, MLE_CHILD_ID_RESPONSE, ADDR_SOL_URI, SOURCE_ADDRESS_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ADDRESS16_TLV, ROUTE64_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, CONNECTIVITY_TLV, LINK_MARGIN_TLV, VERSION_TLV, ADDRESS_REGISTRATION_TLV, NL_MAC_EXTENDED_ADDRESS_TLV, NL_RLOC16_TLV, NL_STATUS_TLV, NL_ROUTER_MASK_TLV, COAP_CODE_ACK, ADDR_SOL_NA, ADDR_SOL_SUCCESS
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
DUT_LEADER = 1
ROUTER_1 = 2
ROUTER_31 = 32
ROUTER_32 = 33
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to show that the DUT will
# only allow 32 active routers on the network and reject the
# Address Solicit Request from a 33rd router - that is
# 2-hops away - with a No Address Available status.
#
# Test Topology:
# -------------
# Leader[DUT]
# / \
# Router_1 --- Router_31
# |
# |
# Router_32
#
# DUT Types:
# ----------
# Leader
class Cert_5_2_3_LeaderReject2Hops(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
DUT_LEADER: {
'name':
'LEADER',
'mode':
'rdn',
'panid':
0xface,
'router_downgrade_threshold':
33,
'router_upgrade_threshold':
32,
'allowlist': [
ROUTER_1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29, 30, 31, ROUTER_31
]
},
ROUTER_1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER, ROUTER_32]
},
3: {
'name': 'ROUTER_2',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
4: {
'name': 'ROUTER_3',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
5: {
'name': 'ROUTER_4',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
6: {
'name': 'ROUTER_5',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
7: {
'name': 'ROUTER_6',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
8: {
'name': 'ROUTER_7',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
9: {
'name': 'ROUTER_8',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
10: {
'name': 'ROUTER_9',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
11: {
'name': 'ROUTER_10',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
12: {
'name': 'ROUTER_11',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
13: {
'name': 'ROUTER_12',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
14: {
'name': 'ROUTER_13',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
15: {
'name': 'ROUTER_14',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
16: {
'name': 'ROUTER_15',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
17: {
'name': 'ROUTER_16',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
18: {
'name': 'ROUTER_17',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
19: {
'name': 'ROUTER_18',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
20: {
'name': 'ROUTER_19',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
21: {
'name': 'ROUTER_20',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
22: {
'name': 'ROUTER_21',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
23: {
'name': 'ROUTER_22',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
24: {
'name': 'ROUTER_23',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
25: {
'name': 'ROUTER_24',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
26: {
'name': 'ROUTER_25',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
27: {
'name': 'ROUTER_26',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
28: {
'name': 'ROUTER_27',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
29: {
'name': 'ROUTER_28',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
30: {
'name': 'ROUTER_29',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
31: {
'name': 'ROUTER_30',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
ROUTER_31: {
'name': 'ROUTER_31',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [DUT_LEADER]
},
ROUTER_32: {
'name': 'ROUTER_32',
'mode': 'rdn',
'router_downgrade_threshold': 33,
'router_upgrade_threshold': 33,
'allowlist': [ROUTER_1]
},
}
def test(self):
self.nodes[DUT_LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[DUT_LEADER].get_state(), 'leader')
for i in range(2, 32):
self.nodes[i].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[i].get_state(), 'router')
self.collect_rlocs()
self.nodes[ROUTER_31].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER_31].get_state(), 'router')
self.nodes[ROUTER_32].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
def verify(self, pv: PacketVerifier):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
LEADER_RLOC = pv.vars['LEADER_RLOC']
ROUTER_31 = pv.vars['ROUTER_31']
ROUTER_32 = pv.vars['ROUTER_32']
# Step 1: Topology is created, the DUT is the Leader of the network
# and there is a total of 32 active routers, including the Leader.
for i in range(1, 32):
pv.verify_attached('ROUTER_%d' % i)
# Step 2: Router_31 to attaches to the network and sends an Address
# Solicit Request to become an active router.
_pkt = pkts.filter_wpan_src64(ROUTER_31).\
filter_ipv6_dst(LEADER_RLOC).\
filter_coap_request(ADDR_SOL_URI).\
filter(lambda p: {
NL_MAC_EXTENDED_ADDRESS_TLV,
NL_STATUS_TLV
} <= set(p.coap.tlv.type)
).\
must_next()
# Step 3: Leader sends an Address Solicit Response.
# Ensure the Address Solicit Response is properly formatted:
# CoAP Response Code
# 2.04 Changed
# CoAP Payload
# - Status TLV (value = Success)
# - RLOC16 TLV
# - Router Mask TLV
pkts.filter_wpan_src64(LEADER).\
filter_ipv6_dst(_pkt.ipv6.src).\
filter_coap_ack(ADDR_SOL_URI).\
filter(lambda p: {
NL_STATUS_TLV,
NL_RLOC16_TLV,
NL_ROUTER_MASK_TLV
} <= set(p.coap.tlv.type) and\
p.coap.code == COAP_CODE_ACK and\
p.thread_address.tlv.status == ADDR_SOL_SUCCESS\
).\
must_next()
# Step 4: Leader The DUT sends MLE Advertisements.
# The MLE Advertisements from the Leader MUST contain
# the Route64 TLV with 32 assigned Router IDs.
pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: {
LEADER_DATA_TLV,
ROUTE64_TLV,
SOURCE_ADDRESS_TLV
} <= set(p.mle.tlv.type) and\
len(p.mle.tlv.route64.cost) == 32 and\
p.ipv6.hlim == 255
).\
must_next()
# Step 5: Router_32 to attach to any of the active routers, 2-hops
# from the leader, and to send an Address Solicit Request
# to become an active router.
_pkt = pkts.filter_wpan_src64(ROUTER_32).\
filter_ipv6_dst(LEADER_RLOC).\
filter_coap_request(ADDR_SOL_URI).\
filter(lambda p: {
NL_MAC_EXTENDED_ADDRESS_TLV,
NL_STATUS_TLV
} <= set(p.coap.tlv.type)
).\
must_next()
# Step 6: Leader sends an Address Solicit Response.
# Ensure the Address Solicit Response is properly formatted:
# CoAP Response Code
# 2.04 Changed
# CoAP Payload
# - Status TLV (value = No Address Available)
pkts.filter_wpan_src64(LEADER).\
filter_ipv6_dst(_pkt.ipv6.src).\
filter_coap_ack(ADDR_SOL_URI).\
filter(lambda p:
p.coap.code == COAP_CODE_ACK and\
p.thread_address.tlv.status == ADDR_SOL_NA
).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,386 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import mle
import network_layer
import thread_cert
from pktverify.consts import MLE_PARENT_REQUEST, MLE_PARENT_RESPONSE, MLE_CHILD_ID_REQUEST, MLE_CHILD_ID_RESPONSE, MLE_LINK_REQUEST, ADDR_SOL_URI, SOURCE_ADDRESS_TLV, MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, MLE_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, TLV_REQUEST_TLV, SCAN_MASK_TLV, CONNECTIVITY_TLV, LINK_MARGIN_TLV, VERSION_TLV, ADDRESS_REGISTRATION_TLV, NL_MAC_EXTENDED_ADDRESS_TLV, NL_RLOC16_TLV, NL_STATUS_TLV, NL_ROUTER_MASK_TLV, COAP_CODE_ACK
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
LEADER = 1
ROUTER = 16
DUT_REED = 17
MED = 18
REED_ADVERTISEMENT_INTERVAL = 570
REED_ADVERTISEMENT_MAX_JITTER = 60
ROUTER_SELECTION_JITTER = 1
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to:
# 1) Verify that the DUT does not attempt to become a router if there
# are already 16 active routers on the Thread network AND it is not
# bringing children
# 2) Verify that the DUT transmits MLE Advertisement messages every
# REED_ADVERTISEMENT_INTERVAL (+REED_ADVERTISEMENT_MAX_JITTER) seconds
# 3) Verify that the DUT upgrades to a router by sending an Address Solicit
# Request when a child attempts to attach to it.
#
# Test Topology:
# -------------
# Router_15 - Leader
# ... / \
# Router_n Router_1(DUT)
# |
# REED(DUT)
# |
# MED
#
# DUT Types:
# ----------
# REED
class Cert_5_2_4_REEDUpgrade(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ROUTER]
},
2: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [LEADER]
},
3: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [LEADER]
},
4: {
'name': 'ROUTER_3',
'mode': 'rdn',
'allowlist': [LEADER]
},
5: {
'name': 'ROUTER_4',
'mode': 'rdn',
'allowlist': [LEADER]
},
6: {
'name': 'ROUTER_5',
'mode': 'rdn',
'allowlist': [LEADER]
},
7: {
'name': 'ROUTER_6',
'mode': 'rdn',
'allowlist': [LEADER]
},
8: {
'name': 'ROUTER_7',
'mode': 'rdn',
'allowlist': [LEADER]
},
9: {
'name': 'ROUTER_8',
'mode': 'rdn',
'allowlist': [LEADER]
},
10: {
'name': 'ROUTER_9',
'mode': 'rdn',
'allowlist': [LEADER]
},
11: {
'name': 'ROUTER_10',
'mode': 'rdn',
'allowlist': [LEADER]
},
12: {
'name': 'ROUTER_11',
'mode': 'rdn',
'allowlist': [LEADER]
},
13: {
'name': 'ROUTER_12',
'mode': 'rdn',
'allowlist': [LEADER]
},
14: {
'name': 'ROUTER_13',
'mode': 'rdn',
'allowlist': [LEADER]
},
15: {
'name': 'ROUTER_14',
'mode': 'rdn',
'allowlist': [LEADER]
},
ROUTER: {
'name': 'ROUTER_15',
'mode': 'rdn',
'allowlist': [LEADER, DUT_REED]
},
DUT_REED: {
'name': 'REED',
'mode': 'rdn',
'allowlist': [ROUTER, MED]
},
MED: {
'name': 'MED',
'mode': 'rdn',
'allowlist': [DUT_REED]
},
}
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
for i in range(2, 17):
self.nodes[i].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[i].get_state(), 'router')
self.nodes[DUT_REED].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.simulator.go(ROUTER_SELECTION_JITTER)
self.collect_rloc16s()
self.collect_rlocs()
self.simulator.go(REED_ADVERTISEMENT_INTERVAL + REED_ADVERTISEMENT_MAX_JITTER)
self.nodes[MED].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.collect_ipaddrs()
mleid = self.nodes[LEADER].get_ip6_address(config.ADDRESS_TYPE.ML_EID)
self.assertTrue(self.nodes[MED].ping(mleid))
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER_RLOC16 = pv.vars['LEADER_RLOC16']
LEADER_RLOC = pv.vars['LEADER_RLOC']
LEADER_MLEID = pv.vars['LEADER_MLEID']
REED = pv.vars['REED']
REED_RLOC16 = pv.vars['REED_RLOC16']
MED = pv.vars['MED']
MED_RLOC16 = pv.vars['MED_RLOC16']
MED_MLEID = pv.vars['MED_MLEID']
# Step 1: Verify topology is formed correctly except REED.
with pkts.save_index():
for i in range(1, 16):
pv.verify_attached('ROUTER_%d' % i)
# Step 2: REED attaches to the network with 2-hops from the Leader
# and MUST NOT attempt to become an active router by sending
# an Address Solicit Request
pv.verify_attached('REED')
lstart = pkts.index
# Step 3: REED MUST send properly formatted MLE Advertisements.
# MLE Advertisements MUST be sent with an IP Hop Limit of
# 255 to the Link-Local All Nodes multicast address (FF02::1).
# The following TLVs MUST be present in the MLE Advertisements:
# - Leader Data TLV
# - Source Address TLV
# The following TLV MUST NOT be present in the MLE Advertisements:
# - Route64 TLV
_pkt = pkts.filter_wpan_src64(REED).\
filter_mle_advertisement('REED').\
must_next()
lend = pkts.index
pkts.range(lstart, lend).filter_wpan_src64(REED).\
filter_coap_request(ADDR_SOL_URI).\
filter(lambda p: {
NL_MAC_EXTENDED_ADDRESS_TLV,
NL_STATUS_TLV
} <= set(p.coap.tlv.type)
).\
must_not_next()
# Step 5: REED MUST send a second MLE Advertisement after
# REED_ADVERTISEMENT_INTERVAL+JITTER where
# JITTER <= REED_ADVERTISEMENT_MAX_JITTER
pkts.filter_wpan_src64(REED).\
filter_mle_advertisement('REED').\
filter(lambda p:
REED_ADVERTISEMENT_INTERVAL <
p.sniff_timestamp - _pkt.sniff_timestamp <=
REED_ADVERTISEMENT_INTERVAL +
REED_ADVERTISEMENT_MAX_JITTER
).\
must_next()
# Step 6: MED sends multicast MLE Parent Request
# First one is to all routers, the second one is to
# all routers and reeds
pkts.filter_wpan_src64(MED).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255 and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 0).\
must_next()
pkts.filter_wpan_src64(MED).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255 and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 1).\
must_next()
# Step 7: REED MUST reply with a properly formatted MLE Parent Response
pkts.filter_wpan_src64(REED).\
filter_wpan_dst64(MED).\
filter_mle_cmd(MLE_PARENT_RESPONSE).\
filter(lambda p: {
CHALLENGE_TLV,
CONNECTIVITY_TLV,
LEADER_DATA_TLV,
LINK_LAYER_FRAME_COUNTER_TLV,
LINK_MARGIN_TLV,
RESPONSE_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type)).\
must_next()
# Step 8: MED sends MLE Child ID Request to REED
_pkt = pkts.filter_wpan_src64(MED).\
filter_wpan_dst64(REED).\
filter_mle_cmd(MLE_CHILD_ID_REQUEST).\
filter(lambda p: {
LINK_LAYER_FRAME_COUNTER_TLV,
MODE_TLV,
RESPONSE_TLV,
TIMEOUT_TLV,
TLV_REQUEST_TLV,
ADDRESS16_TLV,
NETWORK_DATA_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.addr16 is nullField and\
p.thread_nwd.tlv.type is nullField
).\
must_next()
_pkt.must_not_verify(lambda p: (ADDRESS_REGISTRATION_TLV) in p.mle.tlv.type)
# Step 9: REED sends an Address Solicit Request to the Leader
# Ensure the Address Solicit Request is properly formatted:
# CoAP Request URI
# coap://<leader address>:MM/a/as
# CoAP Payload
# - MAC Extended Address TLV
# - Status TLV
pkts.filter_wpan_src64(REED).\
filter_ipv6_dst(LEADER_RLOC).\
filter_coap_request(ADDR_SOL_URI).\
filter(lambda p: {
NL_MAC_EXTENDED_ADDRESS_TLV,
NL_STATUS_TLV
} <= set(p.coap.tlv.type)
).\
must_next()
# Step 10: REED Sends a Link Request Message.
# This step is skipped due to change that new router no
# longer send multicast Link Request.
# Step 11: The REED MLE Child ID Response MUST be properly
# formatted with MED_1s new 16-bit address.
with pkts.save_index():
pkts.filter_wpan_src64(REED).\
filter_wpan_dst64(MED).\
filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\
filter(lambda p: {
ADDRESS16_TLV,
LEADER_DATA_TLV,
NETWORK_DATA_TLV,
SOURCE_ADDRESS_TLV,
ROUTE64_TLV
} <= set(p.mle.tlv.type) or\
{
ADDRESS16_TLV,
LEADER_DATA_TLV,
NETWORK_DATA_TLV,
SOURCE_ADDRESS_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.source_addr != REED_RLOC16 and\
p.mle.tlv.addr16 != MED_RLOC16
).\
must_next()
# Step 12: The Leader MUST respond with an ICMPv6 Echo Reply
_pkt = pkts.filter_ipv6_src_dst(MED_MLEID, LEADER_MLEID).\
filter_ping_request().\
must_next()
pkts.filter_ipv6_src_dst(LEADER_MLEID, MED_MLEID).\
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,370 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import command
import config
import thread_cert
from pktverify.consts import WIRESHARK_OVERRIDE_PREFS, MLE_PARENT_REQUEST, MLE_PARENT_RESPONSE, MLE_CHILD_ID_REQUEST, MLE_CHILD_ID_RESPONSE, MLE_LINK_REQUEST, ADDR_SOL_URI, ADDR_NTF_URI, SOURCE_ADDRESS_TLV, MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, MLE_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, TLV_REQUEST_TLV, SCAN_MASK_TLV, CONNECTIVITY_TLV, LINK_MARGIN_TLV, VERSION_TLV, ADDRESS_REGISTRATION_TLV, NL_MAC_EXTENDED_ADDRESS_TLV, NL_RLOC16_TLV, NL_STATUS_TLV, NL_TARGET_EID_TLV, NL_ML_EID_TLV, COAP_CODE_ACK
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
from pktverify.bytes import Bytes
LEADER = 1
ROUTER1 = 2
BR = 3
MED = 17
DUT_REED = 18
ROUTER_SELECTION_JITTER = 1
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate that the DUT is able to generate
# Address Notification messages in response to Address Query messages.
#
# - Build a topology that has a total of 16 active routers, including the Leader,
# with no communication constraints and
# - MED only allows Leader
# - DUT only allows Router1
# - DUT allows BR later as required in step 5.
# - The Leader is configured as a DHCPv6 server for prefix 2001::
# - The Border Router is configured as a SLAAC server for prefix 2002::
#
# Test Topology:
# -------------
# MED
# |
# Router_15 - Leader
# ... / \
# Router_2 Router_1
# [BR] |
# REED(DUT)
#
# DUT Types:
# ----------
# REED
class Cert_5_2_5_AddressQuery(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER1, BR, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, MED]
},
ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [LEADER, DUT_REED]
},
BR: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [LEADER]
},
4: {
'name': 'ROUTER_3',
'mode': 'rdn',
'allowlist': [LEADER]
},
5: {
'name': 'ROUTER_4',
'mode': 'rdn',
'allowlist': [LEADER]
},
6: {
'name': 'ROUTER_5',
'mode': 'rdn',
'allowlist': [LEADER]
},
7: {
'name': 'ROUTER_6',
'mode': 'rdn',
'allowlist': [LEADER]
},
8: {
'name': 'ROUTER_7',
'mode': 'rdn',
'allowlist': [LEADER]
},
9: {
'name': 'ROUTER_8',
'mode': 'rdn',
'allowlist': [LEADER]
},
10: {
'name': 'ROUTER_9',
'mode': 'rdn',
'allowlist': [LEADER]
},
11: {
'name': 'ROUTER_10',
'mode': 'rdn',
'allowlist': [LEADER]
},
12: {
'name': 'ROUTER_11',
'mode': 'rdn',
'allowlist': [LEADER]
},
13: {
'name': 'ROUTER_12',
'mode': 'rdn',
'allowlist': [LEADER]
},
14: {
'name': 'ROUTER_13',
'mode': 'rdn',
'allowlist': [LEADER]
},
15: {
'name': 'ROUTER_14',
'mode': 'rdn',
'allowlist': [LEADER]
},
16: {
'name': 'ROUTER_15',
'mode': 'rdn',
'allowlist': [LEADER]
},
MED: {
'name': 'MED',
'is_mtd': True,
'mode': 'rn',
'allowlist': [LEADER]
},
DUT_REED: {
'name': 'REED',
'mode': 'rdn',
'allowlist': [ROUTER1]
},
}
# override wireshark preferences with case needed parameters
CASE_WIRESHARK_PREFS = WIRESHARK_OVERRIDE_PREFS
CASE_WIRESHARK_PREFS['6lowpan.context1'] = '2001::/64'
CASE_WIRESHARK_PREFS['6lowpan.context2'] = '2002::/64'
def test(self):
# 1. LEADER: DHCPv6 Server for prefix 2001::/64.
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[LEADER].add_prefix('2001::/64', 'pdros')
self.nodes[LEADER].register_netdata()
# 2. BR: SLAAC Server for prefix 2002::/64.
self.nodes[BR].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[BR].get_state(), 'router')
self.nodes[BR].add_prefix('2002::/64', 'paros')
self.nodes[BR].register_netdata()
# 3. Bring up remaining devices except DUT_REED.
for i in range(2, 17):
if i == BR:
continue
self.nodes[i].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[i].get_state(), 'router')
self.nodes[MED].start()
self.simulator.go(5)
self.assertEqual(self.nodes[MED].get_state(), 'child')
# 4. Bring up DUT_REED.
self.nodes[DUT_REED].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.simulator.go(ROUTER_SELECTION_JITTER)
# 5. Enable a link between the DUT and BR to create a one-way link.
self.nodes[DUT_REED].add_allowlist(self.nodes[BR].get_addr64())
self.nodes[BR].add_allowlist(self.nodes[DUT_REED].get_addr64())
self.collect_ipaddrs()
self.collect_rlocs()
# 6. Verify DUT_REED would send Address Notification when ping to its
# ML-EID.
mleid = self.nodes[DUT_REED].get_ip6_address(config.ADDRESS_TYPE.ML_EID)
self.assertTrue(self.nodes[MED].ping(mleid))
# Wait for sniffer collecting packets
self.simulator.go(1)
# 7 & 8. Verify DUT_REED would send Address Notification when ping to
# its 2001::EID and 2002::EID.
flag2001 = 0
flag2002 = 0
for global_address in self.nodes[DUT_REED].get_ip6_address(config.ADDRESS_TYPE.GLOBAL):
if global_address[0:4] == '2001':
flag2001 += 1
elif global_address[0:4] == '2002':
flag2002 += 1
else:
raise "Error: Address is unexpected."
self.assertTrue(self.nodes[MED].ping(global_address))
# Wait for sniffer collecting packets
self.simulator.go(1)
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER_RLOC = pv.vars['LEADER_RLOC']
LEADER_MLEID = pv.vars['LEADER_MLEID']
ROUTER_1 = pv.vars['ROUTER_1']
REED = pv.vars['REED']
REED_MLEID = pv.vars['REED_MLEID']
REED_RLOC = pv.vars['REED_RLOC']
MED = pv.vars['MED']
MED_MLEID = pv.vars['MED_MLEID']
MM = pv.vars['MM_PORT']
REED2001 = ''
REED2002 = ''
MED2001 = ''
MED2002 = ''
for addr in pv.vars['REED_IPADDRS']:
if addr.startswith(Bytes('2001')):
REED2001 = addr
if addr.startswith(Bytes('2002')):
REED2002 = addr
for addr in pv.vars['MED_IPADDRS']:
if addr.startswith(Bytes('2001')):
MED2001 = addr
if addr.startswith(Bytes('2002')):
MED2002 = addr
# Step 3: Verify topology is formed correctly except REED.
for i in range(1, 16):
with pkts.save_index():
pv.verify_attached('ROUTER_%d' % i)
# Step 4: REED attaches to Router_1 and MUST NOT attempt to become
# an active router by sending an Address Solicit Request
pv.verify_attached('REED', 'ROUTER_1')
pkts.filter_wpan_src64(REED).\
filter_coap_request(ADDR_SOL_URI).\
must_not_next()
# Step 6: MED sends an ICMPv6 Echo Request from MED to REED using ML-EID.
# The DUT MUST send a properly formatted Address Notification message:
# CoAP Request URI-PATH
# CON POST coap://[<Address Query Source>]:MM/a/an
# CoAP Payload
# - Target EID TLV
# - RLOC16 TLV
# - ML-EID TLV
# The IPv6 Source address MUST be the RLOC of the originator
# The IPv6 Destination address MUST be the RLOC of the destination
# The DUT MUST send an ICMPv6 Echo Reply
_pkt = pkts.filter_ipv6_src_dst(MED_MLEID, REED_MLEID).\
filter_ping_request().\
must_next()
pkts.filter_ipv6_src_dst(REED_RLOC, LEADER_RLOC).\
filter_coap_request(ADDR_NTF_URI, port=MM).\
filter(lambda p: {
NL_TARGET_EID_TLV,
NL_RLOC16_TLV,
NL_ML_EID_TLV
} <= set(p.thread_address.tlv.type)
).\
must_next()
pkts.filter_ipv6_src_dst(REED_MLEID, MED_MLEID).\
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
must_next()
# Step 7: MED sends an ICMPv6 Echo Request from MED to REED using 2001::EID.
# The DUT MUST send a properly formatted Address Notification message:
# CoAP Request URI-PATH
# CON POST coap://[<Address Query Source>]:MM/a/an
# CoAP Payload
# - Target EID TLV
# - RLOC16 TLV
# - ML-EID TLV
# The IPv6 Source address MUST be the RLOC of the originator
# The IPv6 Destination address MUST be the RLOC of the destination
# The DUT MUST send an ICMPv6 Echo Reply
_pkt = pkts.filter_ipv6_src_dst(MED2001, REED2001).\
filter_ping_request().\
must_next()
pkts.filter_ipv6_src_dst(REED_RLOC, LEADER_RLOC).\
filter_coap_request(ADDR_NTF_URI, port=MM).\
filter(lambda p: {
NL_TARGET_EID_TLV,
NL_RLOC16_TLV,
NL_ML_EID_TLV
} <= set(p.thread_address.tlv.type)
).\
must_next()
pkts.filter_ipv6_src_dst(REED2001, MED2001).\
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
must_next()
# Step 8: MED sends an ICMPv6 Echo Request from MED to REED using 2002::EID.
# The DUT MUST send a properly formatted Address Notification message:
# CoAP Request URI-PATH
# CON POST coap://[<Address Query Source>]:MM/a/an
# CoAP Payload
# - Target EID TLV
# - RLOC16 TLV
# - ML-EID TLV
# The IPv6 Source address MUST be the RLOC of the originator
# The IPv6 Destination address MUST be the RLOC of the destination
# The DUT MUST send an ICMPv6 Echo Reply
_pkt = pkts.filter_ipv6_src_dst(MED2002, REED2002).\
filter_ping_request().\
must_next()
pkts.filter_ipv6_src_dst(REED_RLOC, LEADER_RLOC).\
filter_coap_request(ADDR_NTF_URI, port=MM).\
filter(lambda p: {
NL_TARGET_EID_TLV,
NL_RLOC16_TLV,
NL_ML_EID_TLV
} <= set(p.thread_address.tlv.type)
).\
must_next()
pkts.filter_ipv6_src_dst(REED2002, MED2002).\
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,327 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import command
import config
import mle
import thread_cert
import thread_cert
from pktverify.consts import MLE_PARENT_REQUEST, MLE_CHILD_ID_REQUEST, ADDR_REL_URI, SOURCE_ADDRESS_TLV, MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, TLV_REQUEST_TLV, SCAN_MASK_TLV, CONNECTIVITY_TLV, LINK_MARGIN_TLV, VERSION_TLV, ADDRESS_REGISTRATION_TLV, NL_MAC_EXTENDED_ADDRESS_TLV, NL_RLOC16_TLV, COAP_CODE_ACK
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
LEADER = 1
DUT_ROUTER1 = 2
ROUTER2 = 3
ROUTER23 = 24
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to verify that the DUT will downgrade
# to a REED when the network becomes too dense and the Router Downgrade
# Threshold conditions are met.
#
# Test Topology:
# -------------
# Leader
# / \
# Router_1 [DUT] ... Router_23
#
# DUT Types:
# ----------
# Router
class Cert_5_2_06_RouterDowngrade(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
DUT_ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
},
ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
4: {
'name': 'ROUTER_3',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
5: {
'name': 'ROUTER_4',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
6: {
'name': 'ROUTER_5',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
7: {
'name': 'ROUTER_6',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
8: {
'name': 'ROUTER_7',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
9: {
'name': 'ROUTER_8',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
10: {
'name': 'ROUTER_9',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
11: {
'name': 'ROUTER_10',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
12: {
'name': 'ROUTER_11',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
13: {
'name': 'ROUTER_12',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
14: {
'name': 'ROUTER_13',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
15: {
'name': 'ROUTER_14',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
16: {
'name': 'ROUTER_15',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
17: {
'name': 'ROUTER_16',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
18: {
'name': 'ROUTER_17',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
19: {
'name': 'ROUTER_18',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
20: {
'name': 'ROUTER_19',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
21: {
'name': 'ROUTER_20',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
22: {
'name': 'ROUTER_21',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
23: {
'name': 'ROUTER_22',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
ROUTER23: {
'name': 'ROUTER_23',
'mode': 'rdn',
'router_downgrade_threshold': 32,
'router_upgrade_threshold': 32
},
}
def test(self):
# 1 Ensure topology is formed correctly without ROUTER23.
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
for i in range(2, 24):
self.nodes[i].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[i].get_state(), 'router')
self.collect_rloc16s()
# All reference testbed devices have been configured with downgrade threshold as 32 except DUT_ROUTER1,
# so we don't need to ensure ROUTER23 has a better link quality on
# posix.
self.nodes[ROUTER23].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER23].get_state(), 'router')
self.simulator.go(10)
self.assertEqual(self.nodes[DUT_ROUTER1].get_state(), 'child')
self.collect_rlocs()
router1_rloc = self.nodes[DUT_ROUTER1].get_ip6_address(config.ADDRESS_TYPE.RLOC)
self.assertTrue(self.nodes[LEADER].ping(router1_rloc))
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
LEADER_RLOC = pv.vars['LEADER_RLOC']
ROUTER_1 = pv.vars['ROUTER_1']
ROUTER_1_RLOC = pv.vars['ROUTER_1_RLOC']
ROUTER_1_RLOC16 = pv.vars['ROUTER_1_RLOC16']
# Step 1: Ensure topology is formed correctly
for i in range(1, 24):
pv.verify_attached('ROUTER_%d' % i)
# Step 3: Allow enough time for the DUT to get Network Data Updates
# and resign its Router ID.
# The DUT MUST first reconnect to the network as a Child by
# sending properly formatted Parent Request and Child ID Request
# messages.
# Once the DUT attaches as a Child, it MUST send an Address
# Release Message to the Leader:
# CoAP Request URI
# coap://[<leader address>]:MM/a/ar
# CoAP Payload
# MAC Extended Address TLV
# RLOC16 TLV
pkts.filter_wpan_src64(ROUTER_1).\
filter_LLARMA().\
filter_mle_cmd(MLE_PARENT_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
MODE_TLV,
SCAN_MASK_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255 and\
p.mle.tlv.scan_mask.r == 1 and\
p.mle.tlv.scan_mask.e == 0
).\
must_next()
_pkt = pkts.filter_wpan_src64(ROUTER_1).\
filter_mle_cmd(MLE_CHILD_ID_REQUEST).\
filter(lambda p: {
LINK_LAYER_FRAME_COUNTER_TLV,
MODE_TLV,
RESPONSE_TLV,
TIMEOUT_TLV,
TLV_REQUEST_TLV,
ADDRESS16_TLV,
NETWORK_DATA_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type) and\
p.mle.tlv.addr16 is nullField and\
p.thread_nwd.tlv.type is nullField
).\
must_next()
_pkt.must_not_verify(lambda p: (ADDRESS_REGISTRATION_TLV) in p.mle.tlv.type)
pkts.filter_wpan_src64(ROUTER_1).\
filter_ipv6_dst(LEADER_RLOC).\
filter_coap_request(ADDR_REL_URI).\
filter(lambda p: {
NL_MAC_EXTENDED_ADDRESS_TLV,
NL_RLOC16_TLV
} <= set(p.coap.tlv.type) and\
p.thread_address.tlv.rloc16 == ROUTER_1_RLOC16
).\
must_next()
# Step 4: Leader receives Address Release messages and sends a2.04
# Changed CoAP response.
pkts.filter_wpan_src64(LEADER).\
filter_ipv6_dst(ROUTER_1_RLOC).\
filter_coap_ack(ADDR_REL_URI).\
must_next()
# Step 5: ROUTER_1 responds with ICMPv6 Echo Reply
_pkt = pkts.filter_ipv6_src_dst(LEADER_RLOC, ROUTER_1_RLOC).\
filter_ping_request().\
must_next()
pkts.filter_ipv6_src_dst(ROUTER_1_RLOC, LEADER_RLOC).\
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,263 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 copy
import unittest
import command
import config
import ipv6
import mle
import thread_cert
from pktverify.consts import ADDR_SOL_URI, MLE_LINK_REQUEST, MLE_LINK_ACCEPT, MLE_LINK_ACCEPT_AND_REQUEST, SOURCE_ADDRESS_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, LEADER_DATA_TLV, VERSION_TLV
from pktverify.packet_verifier import PacketVerifier
from pktverify.null_field import nullField
LEADER = 1
DUT_ROUTER1 = 2
ROUTER2 = 3
DUT_REED = 17
MLE_MIN_LINKS = 3
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate the REEDs Synchronization
# procedure after attaching to a network with multiple Routers. A REED
# MUST process incoming Advertisements and perform a one-way frame-counter
# synchronization with at least 3 neighboring Routers. When Router receives
# unicast MLE Link Request from REED, it replies with MLE Link Accept.
#
# Test Topology:
# -------------
# Router_15 - Leader
# ... / \
# Router_n Router_1(DUT)
# |
# REED(DUT)
#
# DUT Types:
# ----------
# Router
# REED
class Cert_5_2_7_REEDSynchronization_Base(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
},
DUT_ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
},
ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
},
4: {
'name': 'ROUTER_3',
'mode': 'rdn',
},
5: {
'name': 'ROUTER_4',
'mode': 'rdn',
},
6: {
'name': 'ROUTER_5',
'mode': 'rdn',
},
7: {
'name': 'ROUTER_6',
'mode': 'rdn',
},
8: {
'name': 'ROUTER_7',
'mode': 'rdn',
},
9: {
'name': 'ROUTER_8',
'mode': 'rdn',
},
10: {
'name': 'ROUTER_9',
'mode': 'rdn',
},
11: {
'name': 'ROUTER_10',
'mode': 'rdn',
},
12: {
'name': 'ROUTER_11',
'mode': 'rdn',
},
13: {
'name': 'ROUTER_12',
'mode': 'rdn',
},
14: {
'name': 'ROUTER_13',
'mode': 'rdn',
},
15: {
'name': 'ROUTER_14',
'mode': 'rdn',
},
16: {
'name': 'ROUTER_15',
'mode': 'rdn',
},
DUT_REED: {
'name': 'REED',
'mode': 'rdn',
},
}
DUT = 0
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
for i in range(2, 17):
self.nodes[i].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
for i in range(2, 17):
self.assertEqual(self.nodes[i].get_state(), 'router')
# Avoid DUT_REED attach to DUT_ROUTER1.
if self.DUT == DUT_REED:
self.nodes[DUT_REED].\
add_allowlist(self.nodes[DUT_ROUTER1].get_addr64(),
config.RSSI['LINK_QULITY_1'])
if self.DUT == DUT_ROUTER1:
self.nodes[DUT_REED].\
add_allowlist(self.nodes[DUT_ROUTER1].get_addr64(),
config.RSSI['LINK_QULITY_1'])
self.nodes[DUT_REED].add_allowlist(self.nodes[ROUTER2].get_addr64())
self.nodes[DUT_REED].enable_allowlist()
self.nodes[DUT_REED].start()
self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL)
self.assertEqual(self.nodes[DUT_REED].get_state(), 'child')
self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL)
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
ROUTER_1 = pv.vars['ROUTER_1']
REED = pv.vars['REED']
# Step 1: Verify topology is formed correctly except REED.
for i in range(1, 16):
with pkts.save_index():
pv.verify_attached('ROUTER_%d' % i)
# Step 2: REED attaches to the network and MUST NOT attempt to become
# an active router by sending an Address Solicit Request
pv.verify_attached('REED')
pkts.filter_wpan_src64(REED).\
filter_coap_request(ADDR_SOL_URI).\
filter(lambda p: {
NL_MAC_EXTENDED_ADDRESS_TLV,
NL_STATUS_TLV
} <= set(p.coap.tlv.type)
).\
must_not_next()
# Step 3: REED sends a unicast Link Request message to at lease 3 Routers
#
# The Link Request Message MUST contain the following TLVs:
# - Challenge TLV
# - Leader Data TLV
# - Source Address TLV
# - Version TLV
if self.DUT == DUT_REED:
for i in range(0, MLE_MIN_LINKS):
pkts.filter_wpan_src64(REED).\
filter_mle_cmd(MLE_LINK_REQUEST).\
filter(lambda p: {
CHALLENGE_TLV,
LEADER_DATA_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type)
).\
must_next()
# Step 4: Router_1 sends Link Accept message
# The following TLVs MUST be present in the Link Accept message:
# - Link-layer Frame Counter TLV
# - Source Address TLV
# - Response TLV
# - Version TLV
# - MLE Frame Counter TLV (optional)
#
# The recipient does not make any change to its local state and
# MUST NOT reply with a Link Accept And Request message.
if self.DUT == DUT_ROUTER1:
pkts.filter_wpan_dst64(REED).\
filter_wpan_src64(ROUTER_1).\
filter_mle_cmd(MLE_LINK_ACCEPT).\
filter(lambda p: {
LINK_LAYER_FRAME_COUNTER_TLV,
RESPONSE_TLV,
SOURCE_ADDRESS_TLV,
VERSION_TLV
} <= set(p.mle.tlv.type)
).\
must_next()
pkts.filter_wpan_src64(REED).\
filter_mle_cmd(MLE_LINK_ACCEPT_AND_REQUEST).\
must_not_next()
class Cert_5_2_7_REEDSynchronization_REED(Cert_5_2_7_REEDSynchronization_Base):
DUT = DUT_REED
class Cert_5_2_7_REEDSynchronization_ROUTER(Cert_5_2_7_REEDSynchronization_Base):
DUT = DUT_ROUTER1
if __name__ == '__main__':
unittest.main()
@@ -1,201 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import thread_cert
from pktverify.consts import LINK_LOCAL_All_THREAD_NODES_MULTICAST_ADDRESS
from pktverify.packet_verifier import PacketVerifier
LEADER = 1
DUT_ROUTER1 = 2
FRAGMENTED_DATA_LEN = 256
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate the Link-Local addresses
# that the DUT auto-configures.
#
# Test Topology:
# -------------
# Leader
# |
# Router(DUT)
#
# DUT Types:
# ----------
# Router
class Cert_5_3_1_LinkLocal(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
},
DUT_ROUTER1: {
'name': 'ROUTER',
'mode': 'rdn',
},
}
def test(self):
# 1
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[DUT_ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[DUT_ROUTER1].get_state(), 'router')
self.collect_rlocs()
self.collect_ipaddrs()
# 2 & 3
link_local = self.nodes[DUT_ROUTER1].get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL)
self.assertTrue(self.nodes[LEADER].ping(link_local, size=FRAGMENTED_DATA_LEN))
self.assertTrue(self.nodes[LEADER].ping(link_local))
# 4 & 5
self.assertTrue(self.nodes[LEADER].ping(config.LINK_LOCAL_ALL_NODES_ADDRESS, size=FRAGMENTED_DATA_LEN))
self.assertTrue(self.nodes[LEADER].ping(config.LINK_LOCAL_ALL_NODES_ADDRESS))
# 6 & 7
self.assertTrue(self.nodes[LEADER].ping(config.LINK_LOCAL_ALL_ROUTERS_ADDRESS, size=FRAGMENTED_DATA_LEN))
self.assertTrue(self.nodes[LEADER].ping(config.LINK_LOCAL_ALL_ROUTERS_ADDRESS))
# 8
self.assertTrue(self.nodes[LEADER].ping(config.LINK_LOCAL_All_THREAD_NODES_MULTICAST_ADDRESS))
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
LEADER_LLA = pv.vars['LEADER_LLA']
ROUTER_LLA = pv.vars['ROUTER_LLA']
# Step 1: Build the topology as described
pv.verify_attached('ROUTER')
# Step 2: Leader sends a Fragmented ICMPv6 Echo Request to DUTs
# MAC extended address based Link-Local address
# The DUT MUST respond with an ICMPv6 Echo Reply
_pkt = pkts.filter_ping_request().\
filter_ipv6_src_dst(LEADER_LLA, ROUTER_LLA).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_ipv6_src_dst(ROUTER_LLA, LEADER_LLA).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
# Step 3: Leader sends an Unfragmented ICMPv6 Echo Request to DUTs
# MAC extended address based Link-Local address
# The DUT MUST respond with an ICMPv6 Echo Reply
_pkt = pkts.filter_ping_request().\
filter_ipv6_src_dst(LEADER_LLA, ROUTER_LLA).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_ipv6_src_dst(ROUTER_LLA, LEADER_LLA).\
must_next()
# Step 4: Leader sends a Fragmented ICMPv6 Echo Request to the
# Link-Local All Nodes multicast address (FF02::1)
# The DUT MUST respond with an ICMPv6 Echo Reply
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_ipv6_src_dst(ROUTER_LLA, LEADER_LLA).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
# Step 5: Leader sends an Unfragmented ICMPv6 Echo Request to the
# Link-Local All Nodes multicast address (FF02::1)
# The DUT MUST respond with an ICMPv6 Echo Reply
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(LEADER).\
filter_LLANMA().\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_ipv6_src_dst(ROUTER_LLA, LEADER_LLA).\
must_next()
# Step 6: Leader sends a Fragmented ICMPv6 Echo Request to the
# Link-Local All Routers multicast address (FF02::2)
# The DUT MUST respond with an ICMPv6 Echo Reply
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(LEADER).\
filter_LLARMA().\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_ipv6_src_dst(ROUTER_LLA, LEADER_LLA).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
# Step 7: Leader sends an Unfragmented ICMPv6 Echo Request to the
# Link-Local All Routers multicast address (FF02::2)
# The DUT MUST respond with an ICMPv6 Echo Reply
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(LEADER).\
filter_LLARMA().\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_ipv6_src_dst(ROUTER_LLA, LEADER_LLA).\
must_next()
# Step 8: Leader sends an Unfragmented ICMPv6 Echo Request to the
# Link-Local All Thread Nodes multicast address
# The DUT MUST respond with an ICMPv6 Echo Reply
_pkt = pkts.filter_ping_request().\
filter_ipv6_src_dst(LEADER_LLA,
LINK_LOCAL_All_THREAD_NODES_MULTICAST_ADDRESS).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_ipv6_src_dst(ROUTER_LLA, LEADER_LLA).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,295 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import thread_cert
from pktverify.consts import MLE_CHILD_ID_REQUEST, MLE_CHILD_ID_RESPONSE, REALM_LOCAL_ALL_NODES_ADDRESS, REALM_LOCAL_ALL_ROUTERS_ADDRESS, REALM_LOCAL_All_THREAD_NODES_MULTICAST_ADDRESS
from pktverify.packet_verifier import PacketVerifier
LEADER = 1
ROUTER1 = 2
DUT_ROUTER2 = 3
SED1 = 4
FRAGMENTED_DATA_LEN = 256
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate the Realm-Local addresses
# that the DUT auto-configures.
#
# Test Topology:
# -------------
# Leader
# |
# Router_1 - Router_2(DUT)
# |
# SED
#
# DUT Types:
# ----------
# Router
class Cert_5_3_2_RealmLocal(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER1]
},
ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [LEADER, DUT_ROUTER2]
},
DUT_ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [ROUTER1, SED1]
},
SED1: {
'name': 'SED',
'is_mtd': True,
'mode': 'n',
'timeout': config.DEFAULT_CHILD_TIMEOUT,
'allowlist': [DUT_ROUTER2]
},
}
def test(self):
# 1
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
self.nodes[DUT_ROUTER2].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[DUT_ROUTER2].get_state(), 'router')
self.nodes[SED1].start()
self.simulator.go(5)
self.assertEqual(self.nodes[SED1].get_state(), 'child')
self.collect_ipaddrs()
self.collect_rloc16s()
# 2 & 3
mleid = self.nodes[DUT_ROUTER2].get_ip6_address(config.ADDRESS_TYPE.ML_EID)
self.assertTrue(self.nodes[LEADER].ping(mleid, size=FRAGMENTED_DATA_LEN))
self.simulator.go(2)
self.assertTrue(self.nodes[LEADER].ping(mleid))
self.simulator.go(2)
# 4 & 5
self.assertTrue(self.nodes[LEADER].ping('ff03::1', num_responses=2, size=FRAGMENTED_DATA_LEN))
self.simulator.go(5)
self.assertTrue(self.nodes[LEADER].ping('ff03::1', num_responses=2))
self.simulator.go(5)
# 6 & 7
self.assertTrue(self.nodes[LEADER].ping('ff03::2', num_responses=2, size=FRAGMENTED_DATA_LEN))
self.simulator.go(5)
self.assertTrue(self.nodes[LEADER].ping('ff03::2', num_responses=2))
self.simulator.go(5)
# 8
self.assertTrue(self.nodes[LEADER].ping(
config.REALM_LOCAL_All_THREAD_NODES_MULTICAST_ADDRESS,
num_responses=3,
size=FRAGMENTED_DATA_LEN,
))
self.simulator.go(5)
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
LEADER_MLEID = pv.vars['LEADER_MLEID']
ROUTER_1 = pv.vars['ROUTER_1']
ROUTER_2 = pv.vars['ROUTER_2']
ROUTER_2_RLOC16 = pv.vars['ROUTER_2_RLOC16']
ROUTER_2_MLEID = pv.vars['ROUTER_2_MLEID']
SED = pv.vars['SED']
SED_RLOC16 = pv.vars['SED_RLOC16']
# Step 1: Build the topology as described
pv.verify_attached('ROUTER_1', 'LEADER')
pv.verify_attached('ROUTER_2', 'ROUTER_1')
pv.verify_attached('SED', 'ROUTER_2', 'MTD')
# Step 2: Leader sends a Fragmented ICMPv6 Echo Request to
# DUT's ML-EID
# The DUT MUST respond with an ICMPv6 Echo Reply
_pkt = pkts.filter_ping_request().\
filter_ipv6_src_dst(LEADER_MLEID, ROUTER_2_MLEID).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_ipv6_src_dst(ROUTER_2_MLEID, LEADER_MLEID).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
# Step 3: Leader sends a Unfragmented ICMPv6 Echo Request to
# DUTs ML-EID
# The DUT MUST respond with an ICMPv6 Echo Reply
_pkt = pkts.filter_ping_request().\
filter_ipv6_src_dst(LEADER_MLEID, ROUTER_2_MLEID).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_ipv6_src_dst(ROUTER_2_MLEID, LEADER_MLEID).\
must_next()
# Step 4: Leader sends a Fragmented ICMPv6 Echo Request to the
# Realm-Local All Nodes multicast address (FF03::1)
# The DUT MUST respond with an ICMPv6 Echo Reply
# The DUT MUST NOT forward the ICMPv6 Echo Request to SED
_pkt1 = pkts.filter_ping_request().\
filter_wpan_src64(LEADER).\
filter_ipv6_dst(REALM_LOCAL_ALL_NODES_ADDRESS).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
with pkts.save_index():
pkts.filter_ping_reply(identifier=_pkt1.icmpv6.echo.identifier).\
filter_ipv6_src_dst(ROUTER_2_MLEID, LEADER_MLEID).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
pkts.filter_ping_request(identifier=_pkt1.icmpv6.echo.identifier).\
filter_wpan_src16_dst16(ROUTER_2_RLOC16, SED_RLOC16).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_not_next()
# Step 5: Leader sends an Unfragmented ICMPv6 Echo Request to the
# Realm-Local All Nodes multicast address (FF03::1)
# The DUT MUST respond with an ICMPv6 Echo Reply
# The DUT MUST NOT forward the ICMPv6 Echo Request to SED
_pkt2 = pkts.filter_ping_request().\
filter_wpan_src64(LEADER).\
filter_ipv6_dst(REALM_LOCAL_ALL_NODES_ADDRESS).\
filter(lambda p: p.icmpv6.echo.sequence_number !=
_pkt1.icmpv6.echo.sequence_number
).\
must_next()
with pkts.save_index():
pkts.filter_ping_reply(identifier=_pkt2.icmpv6.echo.identifier).\
filter_ipv6_src_dst(ROUTER_2_MLEID, LEADER_MLEID).\
must_next()
pkts.filter_ping_request(identifier = _pkt2.icmpv6.echo.identifier).\
filter_wpan_src16_dst16(ROUTER_2_RLOC16, SED_RLOC16).\
must_not_next()
# Step 6: Leader sends a Fragmented ICMPv6 Echo Request to the
# Realm-Local All Routers multicast address (FF03::2)
# The DUT MUST respond with an ICMPv6 Echo Reply
# The DUT MUST NOT forward the ICMPv6 Echo Request to SED
_pkt1 = pkts.filter_ping_request().\
filter_wpan_src64(LEADER).\
filter_ipv6_dst(REALM_LOCAL_ALL_ROUTERS_ADDRESS).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
with pkts.save_index():
pkts.filter_ping_reply(identifier=_pkt1.icmpv6.echo.identifier).\
filter_ipv6_src_dst(ROUTER_2_MLEID, LEADER_MLEID).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
pkts.filter_ping_request(identifier=_pkt1.icmpv6.echo.identifier).\
filter_wpan_src16_dst16(ROUTER_2_RLOC16, SED_RLOC16).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_not_next()
# Step 7: Leader sends an Unfragmented ICMPv6 Echo Request to the
# Realm-Local All Routers multicast address (FF03::2)
# The DUT MUST respond with an ICMPv6 Echo Reply
# The DUT MUST NOT forward the ICMPv6 Echo Request to SED
_pkt2 = pkts.filter_ping_request().\
filter_wpan_src64(LEADER).\
filter_ipv6_dst(REALM_LOCAL_ALL_ROUTERS_ADDRESS).\
filter(lambda p: p.icmpv6.echo.sequence_number !=
_pkt1.icmpv6.echo.sequence_number
).\
must_next()
with pkts.save_index():
pkts.filter_ping_reply(identifier=_pkt2.icmpv6.echo.identifier).\
filter_ipv6_src_dst(ROUTER_2_MLEID, LEADER_MLEID).\
must_next()
pkts.filter_ping_request(identifier=_pkt2.icmpv6.echo.identifier).\
filter_wpan_src16_dst16(ROUTER_2_RLOC16, SED_RLOC16).\
must_not_next()
# Step 8: Leader sends a Fragmented ICMPv6 Echo Request to the
# Realm-Local All Thread Nodes multicast address
# The DUT MUST respond with an ICMPv6 Echo Reply
# The Realm-Local All Thread Nodes multicast address
# MUST be a realm-local Unicast Prefix-Based Multicast
# Address [RFC 3306], with:
# - flgs set to 3 (P = 1 and T = 1)
# - scop set to 3
# - plen set to the Mesh Local Prefix length
# - network prefix set to the Mesh Local Prefix
# - group ID set to 1
# The DUT MUST use IEEE 802.15.4 indirect transmissions
# to forward packet to SED
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(LEADER).\
filter_ipv6_dst(REALM_LOCAL_All_THREAD_NODES_MULTICAST_ADDRESS).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
with pkts.save_index():
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_ipv6_src_dst(ROUTER_2_MLEID, LEADER_MLEID).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
pkts.filter_ping_request(identifier = _pkt.icmpv6.echo.identifier).\
filter_wpan_src16_dst16(ROUTER_2_RLOC16, SED_RLOC16).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(SED).\
filter_ipv6_dst(LEADER_MLEID).\
filter(lambda p: p.icmpv6.data.len == FRAGMENTED_DATA_LEN).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,352 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import command
import config
import thread_cert
from pktverify.consts import MLE_CHILD_ID_REQUEST, MLE_CHILD_ID_RESPONSE, ADDR_QRY_URI, ADDR_NTF_URI, NL_ML_EID_TLV, NL_RLOC16_TLV, NL_TARGET_EID_TLV, COAP_CODE_POST
from pktverify.packet_verifier import PacketVerifier
LEADER = 1
ROUTER1 = 2
DUT_ROUTER2 = 3
ROUTER3 = 4
MED1 = 5
MED1_TIMEOUT = 3
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate that the DUT is able to generate
# Address Query messages and properly respond with Address Notification messages.
#
# Test Topology:
# -------------
# Router_1 - Leader
# / \
# Router_3 - Router_2(DUT)
# |
# MED
#
# DUT Types:
# ----------
# Router
class Cert_5_3_3_AddressQuery(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER1, DUT_ROUTER2, ROUTER3]
},
ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [LEADER]
},
DUT_ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [LEADER, ROUTER3, MED1]
},
ROUTER3: {
'name': 'ROUTER_3',
'mode': 'rdn',
'allowlist': [LEADER, DUT_ROUTER2]
},
MED1: {
'name': 'MED',
'is_mtd': True,
'mode': 'rn',
'timeout': 3,
'allowlist': [DUT_ROUTER2]
},
}
def test(self):
# 1
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[ROUTER1].start()
self.nodes[DUT_ROUTER2].start()
self.nodes[ROUTER3].start()
self.nodes[MED1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
self.assertEqual(self.nodes[DUT_ROUTER2].get_state(), 'router')
self.assertEqual(self.nodes[ROUTER3].get_state(), 'router')
self.assertEqual(self.nodes[MED1].get_state(), 'child')
self.collect_ipaddrs()
self.collect_rlocs()
self.collect_rloc16s()
# 2
router3_mleid = self.nodes[ROUTER3].get_ip6_address(config.ADDRESS_TYPE.ML_EID)
self.assertTrue(self.nodes[MED1].ping(router3_mleid))
# 3
# Wait the finish of address resolution traffic triggered by previous
# ping.
self.simulator.go(5)
med1_mleid = self.nodes[MED1].get_ip6_address(config.ADDRESS_TYPE.ML_EID)
self.assertTrue(self.nodes[ROUTER1].ping(med1_mleid))
# 4
# Wait the finish of address resolution traffic triggered by previous
# ping.
self.simulator.go(5)
self.assertTrue(self.nodes[MED1].ping(router3_mleid))
# 5
# Power off ROUTER3 and wait for leader to expire its Router ID.
# In this topology, ROUTER3 has two neighbors (Leader and DUT_ROUTER2),
# so the wait time is (MAX_NEIGHBOR_AGE (100s) + worst propagation time (32s * 15) for bad routing +\
# INFINITE_COST_TIMEOUT (90s) + transmission time + extra redundancy),
# totally ~700s.
self.nodes[ROUTER3].stop()
self.simulator.go(700)
self.assertFalse(self.nodes[MED1].ping(router3_mleid))
# 6
self.nodes[MED1].stop()
self.simulator.go(MED1_TIMEOUT)
self.assertFalse(self.nodes[ROUTER1].ping(med1_mleid))
self.assertFalse(self.nodes[ROUTER1].ping(med1_mleid))
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
LEADER_MLEID = pv.vars['LEADER_MLEID']
ROUTER_1 = pv.vars['ROUTER_1']
ROUTER_1_RLOC = pv.vars['ROUTER_1_RLOC']
ROUTER_1_MLEID = pv.vars['ROUTER_1_MLEID']
ROUTER_2 = pv.vars['ROUTER_2']
ROUTER_2_RLOC16 = pv.vars['ROUTER_2_RLOC16']
ROUTER_2_RLOC = pv.vars['ROUTER_2_RLOC']
ROUTER_2_MLEID = pv.vars['ROUTER_2_MLEID']
MED = pv.vars['MED']
MED_RLOC16 = pv.vars['MED_RLOC16']
MED_MLEID = pv.vars['MED_MLEID']
ROUTER_3 = pv.vars['ROUTER_3']
ROUTER_3_MLEID = pv.vars['ROUTER_3_MLEID']
MM = pv.vars['MM_PORT']
# Step 1: Build the topology as described
for i in range(1, 4):
with pkts.save_index():
pv.verify_attached('ROUTER_%d' % i)
pkts.filter_wpan_src64(MED).\
filter_wpan_dst64(ROUTER_2).\
filter_mle_cmd(MLE_CHILD_ID_REQUEST).\
must_next()
pkts.filter_wpan_src64(ROUTER_2).\
filter_wpan_dst64(MED).\
filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\
must_next()
# Step 2: MED sends an ICMPv6 Echo Request to Router_3 ML-EID
# The DUT MUST generate an Address Query Request on MEDs behalf
# to find Router_3 address.
# The Address Query Request MUST be sent to the Realm-Local
# All-Routers address (FF03::2)
# CoAP URI-Path
# - NON POST coap://<FF03::2>
# CoAP Payload
# - Target EID TLV
# The DUT MUST receive and process the incoming Address Notification
# The DUT MUST then forward the ICMPv6 Echo Request from MED and
# forward the ICMPv6 Echo Reply to MED
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(MED).\
filter_ipv6_dst(ROUTER_3_MLEID).\
must_next()
pkts.filter_wpan_src64(ROUTER_2).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
filter(lambda p: p.thread_address.tlv.target_eid == ROUTER_3_MLEID).\
must_next()
_pkt1 = pkts.filter_wpan_src64(ROUTER_3).\
filter_ipv6_dst(ROUTER_2_RLOC).\
filter_coap_request(ADDR_NTF_URI, port=MM).\
filter(lambda p: {
NL_ML_EID_TLV,
NL_RLOC16_TLV,
NL_TARGET_EID_TLV
} <= set(p.coap.tlv.type) and\
p.thread_address.tlv.target_eid == ROUTER_3_MLEID and\
p.coap.code == COAP_CODE_POST
).\
must_next()
pkts.filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_2).\
filter_ipv6_dst(ROUTER_3_MLEID).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_3).\
filter_dst16(ROUTER_2_RLOC16).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_2).\
filter_wpan_dst16(MED_RLOC16).\
must_next()
# Step 3: Router_1 sends an ICMPv6 Echo Request to the MED ML-EID address
# The DUT MUST respond to the Address Query Request with a properly
# formatted Address Notification Message:
# CoAP URI-Path
# - CON POST coap://[<Address Query Source>]:MM/a/an
# CoAP Payload
# - ML-EID TLV
# - RLOC16 TLV
# - Target EID TLV
# The IPv6 Source address MUST be the RLOC of the originator
# The IPv6 Destination address MUST be the RLOC of the destination
pkts.filter_wpan_src64(ROUTER_1).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
filter(lambda p: p.thread_address.tlv.target_eid == MED_MLEID).\
must_next()
pkts.filter_ipv6_src_dst(ROUTER_2_RLOC, ROUTER_1_RLOC).\
filter_coap_request(ADDR_NTF_URI, port=MM).\
filter(lambda p: {
NL_ML_EID_TLV,
NL_RLOC16_TLV,
NL_TARGET_EID_TLV
} <= set(p.coap.tlv.type) and\
p.thread_address.tlv.target_eid == MED_MLEID and\
p.coap.code == COAP_CODE_POST
).\
must_next()
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(ROUTER_1).\
filter_ipv6_dst(MED_MLEID).\
must_next()
pkts.filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_2).\
filter_ipv6_dst(MED_MLEID).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(MED).\
filter_wpan_dst16(ROUTER_2_RLOC16).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_2).\
filter_ipv6_dst(ROUTER_1_MLEID).\
must_next()
# Step 4: MED sends an ICMPv6 Echo Request to the Router_3 ML-EID
# The DUT MUST NOT send an Address Query, as the Router_3 address
# should be cached
# The DUT MUST forward the ICMPv6 Echo Reply to MED
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(MED).\
filter_ipv6_dst(ROUTER_3_MLEID).\
must_next()
lstart = pkts.index
pkts.filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_2).\
filter_ipv6_dst(ROUTER_3_MLEID).\
must_next()
lend = pkts.index
pkts.range(lstart, lend).\
filter_wpan_src64(ROUTER_2).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI).\
must_not_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_3).\
filter_wpan_dst16(ROUTER_2_RLOC16).\
must_next()
_pkt1 = pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_2).\
filter_wpan_dst16(MED_RLOC16).\
must_next()
# Step 5: Power off Router_3 and wait for the Leader to expire its Router ID
# Send an ICMPv6 Echo Request from MED to the Router_3 ML-EID address
# The DUT MUST update its address cache and removes all entries based
# on Router_3s Router ID.
# The DUT MUST send an Address Query to discover Router_3s RLOC
# address.
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(MED).\
filter_ipv6_dst(ROUTER_3_MLEID).\
filter(lambda p: p.sniff_timestamp - _pkt1.sniff_timestamp >= 700).\
must_next()
pkts.filter_wpan_src64(ROUTER_2).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
filter(lambda p: p.thread_address.tlv.target_eid == ROUTER_3_MLEID and\
p.coap.code == COAP_CODE_POST
).\
must_next()
# Step 6: Power off MED and wait for the DUT to timeout the child.
# Send two ICMPv6 Echo Requests from Router_1 to MED ML-EID
# The DUT MUST NOT respond with an Address Notification message
pkts.filter_ping_request().\
filter_wpan_src64(ROUTER_1).\
filter_ipv6_dst(MED_MLEID).\
must_next()
pkts.filter_wpan_src64(ROUTER_1).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
filter(lambda p: p.thread_address.tlv.target_eid == MED_MLEID and\
p.coap.code == COAP_CODE_POST
).\
must_next()
pkts.filter_ipv6_src_dst(ROUTER_2_RLOC, ROUTER_1_RLOC).\
filter_coap_request(ADDR_NTF_URI).\
must_not_next()
if __name__ == '__main__':
unittest.main()
@@ -1,256 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import command
import config
import thread_cert
from pktverify.consts import MLE_CHILD_ID_REQUEST, MLE_CHILD_ID_RESPONSE, ADDR_QRY_URI, ADDR_NTF_URI, NL_ML_EID_TLV, NL_RLOC16_TLV, NL_TARGET_EID_TLV, COAP_CODE_POST
from pktverify.packet_verifier import PacketVerifier
LEADER = 1
DUT_ROUTER1 = 2
SED1 = 3
MED1 = 4
MED2 = 5
MED3 = 6
MED4 = 7
MTDS = [SED1, MED1, MED2, MED3, MED4]
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate that the DUT is able to
# maintain an EID-to-RLOC Map Cache for a Sleepy End Device child attached to
# it. Each EID-to-RLOC Set MUST support at least four non-link-local unicast
# IPv6 addresses.
#
# Test Topology:
# -------------
# Router_1 - Leader
# | / \
# SED MED_1 .. MED_4
#
# DUT Types:
# ----------
# Router
class Cert_5_3_4_AddressMapCache(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [DUT_ROUTER1, MED1, MED2, MED3, MED4]
},
DUT_ROUTER1: {
'name': 'ROUTER',
'mode': 'rdn',
'allowlist': [LEADER, SED1]
},
SED1: {
'name': 'SED',
'is_mtd': True,
'mode': '-',
'timeout': 5,
'allowlist': [DUT_ROUTER1]
},
MED1: {
'name': 'MED_1',
'is_mtd': True,
'mode': 'rn',
'allowlist': [LEADER]
},
MED2: {
'name': 'MED_2',
'is_mtd': True,
'mode': 'rn',
'allowlist': [LEADER]
},
MED3: {
'name': 'MED_3',
'is_mtd': True,
'mode': 'rn',
'allowlist': [LEADER]
},
MED4: {
'name': 'MED_4',
'is_mtd': True,
'mode': 'rn',
'allowlist': [LEADER]
},
}
def test(self):
# 1
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[DUT_ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[DUT_ROUTER1].get_state(), 'router')
for i in MTDS:
self.nodes[i].start()
self.simulator.go(5)
for i in MTDS:
self.assertEqual(self.nodes[i].get_state(), 'child')
self.collect_ipaddrs()
self.collect_rlocs()
self.collect_rloc16s()
# 2
for MED in [MED1, MED2, MED3, MED4]:
ed_mleid = self.nodes[MED].get_ip6_address(config.ADDRESS_TYPE.ML_EID)
self.assertTrue(self.nodes[SED1].ping(ed_mleid))
self.simulator.go(5)
# 3 & 4
for MED in [MED1, MED2, MED3, MED4]:
ed_mleid = self.nodes[MED].get_ip6_address(config.ADDRESS_TYPE.ML_EID)
self.assertTrue(self.nodes[SED1].ping(ed_mleid))
self.simulator.go(5)
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
LEADER_RLOC16 = pv.vars['LEADER_RLOC16']
ROUTER = pv.vars['ROUTER']
ROUTER_RLOC = pv.vars['ROUTER_RLOC']
SED = pv.vars['SED']
SED_RLOC = pv.vars['SED_RLOC']
SED_RLOC16 = pv.vars['SED_RLOC16']
SED_MLEID = pv.vars['SED_MLEID']
MM = pv.vars['MM_PORT']
# Step 1: Build the topology as described
pv.verify_attached('ROUTER')
for i in range(1, 5):
with pkts.save_index():
pkts.filter_wpan_src64(pv.vars['MED_%d' %i]).\
filter_wpan_dst64(LEADER).\
filter_mle_cmd(MLE_CHILD_ID_REQUEST).\
must_next()
pkts.filter_wpan_src64(LEADER).\
filter_wpan_dst64(pv.vars['MED_%d' %i]).\
filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\
must_next()
pkts.filter_wpan_src64(pv.vars['SED']).\
filter_wpan_dst64(ROUTER).\
filter_mle_cmd(MLE_CHILD_ID_REQUEST).\
must_next()
pkts.filter_wpan_src64(ROUTER).\
filter_wpan_dst64(pv.vars['SED']).\
filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\
must_next()
# Step 2: SED sends an ICMPv6 Echo Request to MED_1, MED_2, MED_3, MED_4 ML-EID
# The DUT MUST generate an Address Query Request on SEDs behalf
# to find each nodes RLOC.
# The Address Query Request MUST be sent to the Realm-Local
# All-Routers address (FF03::2)
# CoAP URI-Path
# - NON POST coap://<FF03::2>
# CoAP Payload
# - Target EID TLV
# Step 3: Leader sends Address Notification Messages with RLOC of MED_1, MED_2, MED_3, MED_4
for i in range(1, 5):
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(SED).\
filter_ipv6_dst(pv.vars['MED_%d_MLEID' %i]).\
must_next()
pkts.filter_wpan_src64(ROUTER).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
filter(lambda p: p.thread_address.tlv.target_eid == pv.vars['MED_%d_MLEID' %i]).\
must_next()
pkts.filter_wpan_src64(LEADER).\
filter_ipv6_dst(ROUTER_RLOC).\
filter_coap_request(ADDR_NTF_URI, port=MM).\
filter(lambda p: {
NL_ML_EID_TLV,
NL_RLOC16_TLV,
NL_TARGET_EID_TLV
} <= set(p.coap.tlv.type) and\
p.thread_address.tlv.target_eid == pv.vars['MED_%d_MLEID' %i] and\
p.thread_address.tlv.rloc16 == LEADER_RLOC16 and\
p.coap.code == COAP_CODE_POST
).\
must_next()
pkts.filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER).\
filter_ipv6_dst(pv.vars['MED_%d_MLEID' %i]).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(pv.vars['MED_%d' %i]).\
filter_ipv6_dst(SED_MLEID).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER).\
filter_wpan_dst16(SED_RLOC16).\
must_next()
# Step 4: SED sends another ICMPv6 Echo Request to MED_1, MED_2, MED_3, MED_4 ML-EID
# The DUT MUST not send an Address Query during this step; If an address query
# message is sent, the test fails
# An ICMPv6 Echo Reply MUST be sent for each ICMPv6 Echo Request from SED
for i in range(1, 5):
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(SED).\
filter_ipv6_dst(pv.vars['MED_%d_MLEID' %i]).\
must_next()
pkts.filter_wpan_src64(ROUTER).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
must_not_next()
pkts.filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER).\
filter_ipv6_dst(pv.vars['MED_%d_MLEID' %i]).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(pv.vars['MED_%d' %i]).\
filter_ipv6_dst(SED_MLEID).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER).\
filter_wpan_dst16(SED_RLOC16).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,243 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import command
import config
import thread_cert
from pktverify.packet_verifier import PacketVerifier
LEADER = 1
DUT_ROUTER1 = 2
ROUTER2 = 3
ROUTER3 = 4
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to ensure that the DUT routes traffic properly
# when link qualities between the nodes are adjusted.
#
# Test Topology:
# -------------
# Leader
# / \
# Router_2 - Router_1(DUT)
# |
# Router_3
#
# DUT Types:
# ----------
# Router
class Cert_5_3_5_RoutingLinkQuality(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [DUT_ROUTER1, ROUTER2]
},
DUT_ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [LEADER, ROUTER2, ROUTER3]
},
ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [LEADER, DUT_ROUTER1]
},
ROUTER3: {
'name': 'ROUTER_3',
'mode': 'rdn',
'allowlist': [DUT_ROUTER1]
},
}
def test(self):
# 1
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
for router in range(DUT_ROUTER1, ROUTER3 + 1):
self.nodes[router].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
for router in range(DUT_ROUTER1, ROUTER3 + 1):
self.assertEqual(self.nodes[router].get_state(), 'router')
self.collect_rlocs()
self.collect_rloc16s()
# 2 & 3
leader_rloc = self.nodes[LEADER].get_ip6_address(config.ADDRESS_TYPE.RLOC)
self.assertTrue(self.nodes[ROUTER3].ping(leader_rloc))
# 4 & 5
self.nodes[LEADER].add_allowlist(self.nodes[DUT_ROUTER1].get_addr64(), config.RSSI['LINK_QULITY_1'])
self.nodes[DUT_ROUTER1].add_allowlist(self.nodes[LEADER].get_addr64(), config.RSSI['LINK_QULITY_1'])
self.simulator.go(3 * config.MAX_ADVERTISEMENT_INTERVAL)
self.assertTrue(self.nodes[ROUTER3].ping(leader_rloc))
# 6 & 7
self.nodes[LEADER].add_allowlist(self.nodes[DUT_ROUTER1].get_addr64(), config.RSSI['LINK_QULITY_2'])
self.nodes[DUT_ROUTER1].add_allowlist(self.nodes[LEADER].get_addr64(), config.RSSI['LINK_QULITY_2'])
self.simulator.go(3 * config.MAX_ADVERTISEMENT_INTERVAL)
self.assertTrue(self.nodes[ROUTER3].ping(leader_rloc))
# 8 & 9
self.nodes[LEADER].add_allowlist(self.nodes[DUT_ROUTER1].get_addr64(), config.RSSI['LINK_QULITY_0'])
self.nodes[DUT_ROUTER1].add_allowlist(self.nodes[LEADER].get_addr64(), config.RSSI['LINK_QULITY_0'])
self.simulator.go(3 * config.MAX_ADVERTISEMENT_INTERVAL)
self.assertTrue(self.nodes[ROUTER3].ping(leader_rloc))
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
LEADER_RLOC = pv.vars['LEADER_RLOC']
LEADER_RLOC16 = pv.vars['LEADER_RLOC16']
ROUTER_1 = pv.vars['ROUTER_1']
ROUTER_1_RLOC = pv.vars['ROUTER_1_RLOC']
ROUTER_1_RLOC16 = pv.vars['ROUTER_1_RLOC16']
ROUTER_2 = pv.vars['ROUTER_2']
ROUTER_2_RLOC16 = pv.vars['ROUTER_2_RLOC16']
ROUTER_2_RLOC = pv.vars['ROUTER_2_RLOC']
ROUTER_3 = pv.vars['ROUTER_3']
ROUTER_3_RLOC = pv.vars['ROUTER_3_RLOC']
MM = pv.vars['MM_PORT']
# Step 1: Ensure topology is formed correctly
for i in range(1, 4):
with pkts.save_index():
pv.verify_attached('ROUTER_%d' % i)
# Step 2: Modify the link quality between the DUT and the Leader to be 3
# Step 3: Router_3 sends an ICMPv6 Echo Request to the Leader
# The ICMPv6 Echo Request MUST take the shortest path:
# Router_3 -> DUT -> Leader
# The hopsLft field of the 6LoWPAN Mesh Header MUST be greater than
# the route cost to the destination
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(ROUTER_3).\
filter_ipv6_dst(LEADER_RLOC).\
must_next()
_pkt.must_verify(lambda p: p.lowpan.mesh.hops > 2)
pkts.filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_1).\
filter_wpan_dst16(LEADER_RLOC16).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(LEADER).\
filter_ipv6_dst(ROUTER_3_RLOC).\
must_next()
# Step 4: Modify the link quality between the DUT and the Leader to be 1
# Step 5: Router_3 sends an ICMPv6 Echo Request to the Leader
# The ICMPv6 Echo Request MUST take the shortest path:
# Router_3 -> DUT -> Router_2 -> Leader
# The hopsLft field of the 6LoWPAN Mesh Header MUST be greater than
# the route cost to the destination
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(ROUTER_3).\
filter_ipv6_dst(LEADER_RLOC).\
must_next()
_pkt.must_verify(lambda p: p.lowpan.mesh.hops > 3)
pkts.filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_1).\
filter_wpan_dst16(ROUTER_2_RLOC16).\
must_next()
pkts.filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_2).\
filter_wpan_dst16(LEADER_RLOC16).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(LEADER).\
filter_ipv6_dst(ROUTER_3_RLOC).\
must_next()
# Step 6: Modify the link quality between the DUT and the Leader to be 2
# Step 7: Router_3 sends an ICMPv6 Echo Request to the Leader
# The ICMPv6 Echo Request MUST take the shortest path:
# Router_3 -> DUT -> Leader
# The hopsLft field of the 6LoWPAN Mesh Header MUST be greater than
# the route cost to the destination
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(ROUTER_3).\
filter_ipv6_dst(LEADER_RLOC).\
must_next()
_pkt.must_verify(lambda p: p.lowpan.mesh.hops > 2)
pkts.filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_1).\
filter_wpan_dst16(LEADER_RLOC16).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(LEADER).\
filter_ipv6_dst(ROUTER_3_RLOC).\
must_next()
# Step 8: Modify the link quality between the DUT and the Leader to be 0
# Step 9: Router_3 sends an ICMPv6 Echo Request to the Leader
# The ICMPv6 Echo Request MUST take the shortest path:
# Router_3 -> DUT -> Router_2 -> Leader
# The hopsLft field of the 6LoWPAN Mesh Header MUST be greater than
# the route cost to the destination
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(ROUTER_3).\
filter_ipv6_dst(LEADER_RLOC).\
must_next()
_pkt.must_verify(lambda p: p.lowpan.mesh.hops > 3)
pkts.filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_1).\
filter_wpan_dst16(ROUTER_2_RLOC16).\
must_next()
pkts.filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_2).\
filter_wpan_dst16(LEADER_RLOC16).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(LEADER).\
filter_ipv6_dst(ROUTER_3_RLOC).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,229 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import config
import thread_cert
from pktverify.consts import MLE_ADVERTISEMENT, MLE_CHILD_ID_REQUEST, MLE_CHILD_ID_RESPONSE
from pktverify.packet_verifier import PacketVerifier
DUT_LEADER = 1
ROUTER1 = 2
ROUTER2 = 3
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to verify that the router ID mask is managed
# correctly, as the connectivity to a router or group of routers is lost and / or
# a new router is added to network.
#
# Test Topology:
# -------------
# Leader(DUT)
# |
# Router_1
# |
# Router_2
#
# DUT Types:
# ----------
# Leader
class Cert_5_3_6_RouterIdMask(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
DUT_LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER1]
},
ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [DUT_LEADER, ROUTER2]
},
ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [ROUTER1]
},
}
def _setUpRouter2(self):
self.nodes[ROUTER2].add_allowlist(self.nodes[ROUTER1].get_addr64())
self.nodes[ROUTER2].enable_allowlist()
self.nodes[ROUTER2].set_router_selection_jitter(1)
def test(self):
# 1
self.nodes[DUT_LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[DUT_LEADER].get_state(), 'leader')
self.nodes[ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
self.nodes[ROUTER2].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
self.collect_rloc16s()
# Wait DUT_LEADER to establish routing to ROUTER2 via ROUTER1's MLE
# advertisement.
self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL)
# 2
self.nodes[ROUTER2].reset()
self._setUpRouter2()
# 3 & 4
self.simulator.go(720)
# 5
self.nodes[ROUTER2].start()
self.simulator.go(config.ROUTER_RESET_DELAY)
self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL)
# 6
self.nodes[ROUTER1].reset()
self.nodes[ROUTER2].reset()
self.simulator.go(720)
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
ROUTER_1 = pv.vars['ROUTER_1']
ROUTER_2 = pv.vars['ROUTER_2']
leader_rid = pv.vars['LEADER_RLOC16'] >> 10
router_1_rid = pv.vars['ROUTER_1_RLOC16'] >> 10
router_2_rid = pv.vars['ROUTER_2_RLOC16'] >> 10
# Step 1: Ensure topology is formed correctly
pv.verify_attached('ROUTER_1', 'LEADER')
pv.verify_attached('ROUTER_2', 'ROUTER_1')
pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p:
{1,2,1} == set(p.mle.tlv.route64.cost) and\
{leader_rid, router_1_rid, router_2_rid} ==
p.mle.tlv.route64.id_mask
).\
must_next()
# Step 4: The DUTs routing cost to Router_2 MUST count to infinity
# The DUT MUST remove Router_2 ID from its ID set
# Verify route data has settled
_pkt = pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: {1,0,1} == set(p.mle.tlv.route64.cost)).\
must_next()
pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p:
{1,1} == set(p.mle.tlv.route64.cost) and\
{leader_rid, router_1_rid} ==
p.mle.tlv.route64.id_mask
).\
must_next()
# Step 5: Re-attach Router_2 to Router_1.
# The DUT MUST reset the MLE Advertisement trickle timer and
# send an Advertisement
pv.verify_attached('ROUTER_2', 'ROUTER_1')
# check trickle timer between the successive advertisements
with pkts.save_index():
_pkt = pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
must_next()
# Time between MLE Advertisements after reset can be up to 3.5 seconds
# 0.0s: Reset Interval, reset interval to 1 seconds, random interval at 0.5 second
# 0.5s: Transmit MLE Advertisement
# 1.0s: Set interval to 2 seconds, random interval at 2 seconds
# 3.0s: Receive MLE Advertisement, reset interval to 1 second, random interval at 1.0 second
# 4.0s: Transmit MLE Advertisement
pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: p.sniff_timestamp - _pkt.sniff_timestamp <= 4).\
must_next()
# check router cost after the re-attach
pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: {1,2,1} == set(p.mle.tlv.route64.cost) and\
{leader_rid, router_1_rid, router_2_rid} ==
p.mle.tlv.route64.id_mask
).\
must_next()
# Step 6: The DUTs routing cost to Router_1 MUST go directly to
# infinity as there is no multi-hop cost for Router_1
# The DUT MUST remove Router_1 & Router_2 IDs from its ID set
pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: {0, 0, 1} == set(p.mle.tlv.route64.cost)).\
must_next()
pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: {0, 1} == set(p.mle.tlv.route64.cost)).\
must_next()
pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p:
[1] == p.mle.tlv.route64.cost and\
{leader_rid} ==
p.mle.tlv.route64.id_mask
).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,218 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import command
import config
import copy
import mle
import thread_cert
from pktverify.consts import WIRESHARK_OVERRIDE_PREFS, ADDR_ERR_URI, ADDR_QRY_URI, ADDR_NTF_URI, MLE_CHILD_ID_REQUEST, MLE_CHILD_ID_RESPONSE, REALM_LOCAL_ALL_ROUTERS_ADDRESS, NL_TARGET_EID_TLV, NL_RLOC16_TLV, NL_ML_EID_TLV
from pktverify.packet_verifier import PacketVerifier
DUT_LEADER = 1
ROUTER1 = 2
ROUTER2 = 3
MED1 = 4
SED1 = 5
MED2 = 6
MTDS = [MED1, SED1, MED2]
ON_MESH_PREFIX = '2001::/64'
IPV6_ADDR = '2001::1234'
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate the DUTs ability to perform
# duplicate address detection.
#
# Test Topology:
# -------------
# MED_2 - Leader(DUT)
# / \
# Router_1 Router_2
# | |
# MED_1 SED
#
# DUT Types:
# ----------
# Leader
class Cert_5_3_7_DuplicateAddress(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
DUT_LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER1, ROUTER2, MED2]
},
ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [DUT_LEADER, MED1]
},
ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [DUT_LEADER, SED1]
},
MED1: {
'name': 'MED_1',
'is_mtd': True,
'mode': 'rn',
'allowlist': [ROUTER1]
},
SED1: {
'name': 'SED',
'is_mtd': True,
'mode': '-',
'allowlist': [ROUTER2]
},
MED2: {
'name': 'MED_2',
'is_mtd': True,
'mode': 'rn',
'allowlist': [DUT_LEADER]
},
}
# override wireshark preferences with case needed parameters
CASE_WIRESHARK_PREFS = copy.deepcopy(WIRESHARK_OVERRIDE_PREFS)
CASE_WIRESHARK_PREFS['6lowpan.context1'] = ON_MESH_PREFIX
def test(self):
self.nodes[DUT_LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[DUT_LEADER].get_state(), 'leader')
for i in range(ROUTER1, MED2 + 1):
self.nodes[i].start()
self.simulator.go(5)
for i in [ROUTER1, ROUTER2]:
self.assertEqual(self.nodes[i].get_state(), 'router')
for i in MTDS:
self.assertEqual(self.nodes[i].get_state(), 'child')
self.collect_ipaddrs()
self.collect_rlocs()
self.nodes[ROUTER2].add_prefix(ON_MESH_PREFIX, 'paros')
self.nodes[ROUTER2].register_netdata()
self.nodes[MED1].add_ipaddr(IPV6_ADDR)
self.nodes[SED1].add_ipaddr(IPV6_ADDR)
self.simulator.go(5)
self.nodes[MED2].ping(IPV6_ADDR)
self.simulator.go(5)
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
LEADER_RLOC = pv.vars['LEADER_RLOC']
ROUTER_1 = pv.vars['ROUTER_1']
ROUTER_2 = pv.vars['ROUTER_2']
MED_1 = pv.vars['MED_1']
MED_2 = pv.vars['MED_2']
SED = pv.vars['SED']
MM = pv.vars['MM_PORT']
# Step 1: Ensure topology is formed correctly
for i in (1, 2):
pv.verify_attached('ROUTER_%d' % i, 'LEADER')
pv.verify_attached('MED_1', 'ROUTER_1', 'MTD')
pv.verify_attached('SED', 'ROUTER_2', 'MTD')
pv.verify_attached('MED_2', 'LEADER', 'MTD')
# Step 5: MED_2 sends ICMPv6 Echo Request to address configured on MED_1
# and SED_1 with Prefix 2001::
# The DUT MUST multicast an Address Query message to the Realm-Local
# All Routers address (FF03::2):
# CoAP URI-Path
# - NON POST coap://<FF03::2>
# CoAP Payload
# - Target EID TLV
pkts.filter_ping_request().\
filter_wpan_src64(MED_2). \
filter_ipv6_dst(IPV6_ADDR). \
must_next()
pkts.filter_wpan_src64(LEADER).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
filter(lambda p: p.thread_address.tlv.target_eid == IPV6_ADDR).\
must_next()
# Step 6: Router_1 & Router_2 respond with Address Notification message
# with matching Target TLVs
for i in (1, 2):
with pkts.save_index():
pkts.filter_wpan_src64(pv.vars['ROUTER_%d' %i]).\
filter_ipv6_dst(LEADER_RLOC).\
filter_coap_request(ADDR_NTF_URI, port=MM).\
filter(lambda p: {
NL_ML_EID_TLV,
NL_RLOC16_TLV,
NL_TARGET_EID_TLV
} <= set(p.coap.tlv.type) and\
p.thread_address.tlv.target_eid == IPV6_ADDR
).\
must_next()
# Step 7: The DUT MUST multicast an Address Error Notification to the Realm-Local
# All Routers address (FF03::2):
# CoAP URI-Path
# - NON POST coap://[<peer address>]:MM/a/ae
# CoAP Payload
# - Target EID TLV
# - ML-EID TLV
#
# The IPv6 Source address MUST be the RLOC of the originator
pkts.filter_ipv6_src_dst(LEADER_RLOC, REALM_LOCAL_ALL_ROUTERS_ADDRESS).\
filter_coap_request(ADDR_ERR_URI, port=MM).\
filter(lambda p: {
NL_ML_EID_TLV,
NL_TARGET_EID_TLV
} <= set(p.coap.tlv.type) and\
p.thread_address.tlv.target_eid == IPV6_ADDR
).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,222 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 unittest
import command
import config
import copy
import ipv6
import mle
import thread_cert
from pktverify.consts import WIRESHARK_OVERRIDE_PREFS, MLE_ADVERTISEMENT, ADDR_QRY_URI, SOURCE_ADDRESS_TLV, ROUTE64_TLV, LEADER_DATA_TLV
from pktverify.packet_verifier import PacketVerifier
from pktverify.bytes import Bytes
DUT_LEADER = 1
BR = 2
MED1 = 3
MED2 = 4
MTDS = [MED1, MED2]
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate that the DUT MTD Child Address Set
# can hold at least 4 IPv6 non-link-local addresses.
#
# Test Topology:
# -------------
# MED_1
# |
# BR - Leader(DUT) - MED_2
#
# DUT Types:
# ----------
# Leader
class Cert_5_3_8_ChildAddressSet(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
DUT_LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [BR, MED1, MED2]
},
BR: {
'mode': 'rdn',
'allowlist': [DUT_LEADER]
},
MED1: {
'name': 'MED_1',
'is_mtd': True,
'mode': 'rn',
'allowlist': [DUT_LEADER]
},
MED2: {
'name': 'MED_2',
'is_mtd': True,
'mode': 'rn',
'allowlist': [DUT_LEADER]
},
}
# override wireshark preferences with case needed parameters
CASE_WIRESHARK_PREFS = copy.deepcopy(WIRESHARK_OVERRIDE_PREFS)
CASE_WIRESHARK_PREFS['6lowpan.context1'] = '2001::/64'
CASE_WIRESHARK_PREFS['6lowpan.context2'] = '2002::/64'
CASE_WIRESHARK_PREFS['6lowpan.context3'] = '2003::/64'
def test(self):
self.nodes[DUT_LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[DUT_LEADER].get_state(), 'leader')
self.nodes[BR].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[BR].get_state(), 'router')
# 1 BR: Configure BR to be a DHCPv6 server
self.nodes[BR].add_prefix('2001::/64', 'pdros')
self.nodes[BR].add_prefix('2002::/64', 'pdros')
self.nodes[BR].add_prefix('2003::/64', 'pdros')
self.nodes[BR].register_netdata()
# 3 MED1, MED2: MED1 and MED2 attach to DUT_LEADER
for i in MTDS:
self.nodes[i].start()
self.simulator.go(5)
self.assertEqual(self.nodes[i].get_state(), 'child')
self.collect_ipaddrs()
# 4 MED1: MED1 send an ICMPv6 Echo Request to the MED2 ML-EID
med2_ml_eid = self.nodes[MED2].get_ip6_address(config.ADDRESS_TYPE.ML_EID)
self.assertTrue(med2_ml_eid is not None)
self.assertTrue(self.nodes[MED1].ping(med2_ml_eid))
# Wait for sniffer got packets
self.simulator.go(1)
# 5 MED1: MED1 send an ICMPv6 Echo Request to the MED2 2001::GUA
addr = self.nodes[MED2].get_addr("2001::/64")
self.assertTrue(addr is not None)
self.assertTrue(self.nodes[MED1].ping(addr))
# Wait for sniffer got packets
self.simulator.go(1)
# 6 MED1: MED1 send an ICMPv6 Echo Request to the MED2 2002::GUA
addr = self.nodes[MED2].get_addr("2002::/64")
self.assertTrue(addr is not None)
self.assertTrue(self.nodes[MED1].ping(addr))
# Wait for sniffer got packets
self.simulator.go(1)
# 7 MED1: MED1 send an ICMPv6 Echo Request to the MED2 2003::GUA
addr = self.nodes[MED2].get_addr("2003::/64")
self.assertTrue(addr is not None)
self.assertTrue(self.nodes[MED1].ping(addr))
# Wait for sniffer got packets
self.simulator.go(1)
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
MED_1 = pv.vars['MED_1']
MED_1_MLEID = pv.vars['MED_1_MLEID']
MED_2 = pv.vars['MED_2']
MED_2_MLEID = pv.vars['MED_2_MLEID']
MED_1_GUA = list()
MED_2_GUA = list()
MM = pv.vars['MM_PORT']
names = locals()
for i in (1, 2):
for addr in pv.vars['MED_%d_IPADDRS' % i]:
for j in range(1, 4):
if addr.startswith(Bytes('200%d' % j)):
names['MED_' + str(i) + '_GUA'].append(addr)
# Step 2: Leader is sending properly formatted MLE Advertisements.
# Advertisements MUST be sent with an IP hop limit of 255 to
# the Link-Local All Nodes multicast address (FF02::1).
# The following TLVs MUST be present in the MLE Advertisements:
# - Leader Data TLV
# - Route64 TLV
# - Source Address TLV
pkts.filter_wpan_src64(LEADER).\
filter_LLANMA().\
filter_mle_cmd(MLE_ADVERTISEMENT).\
filter(lambda p: {
LEADER_DATA_TLV,
ROUTE64_TLV,
SOURCE_ADDRESS_TLV
} <= set(p.mle.tlv.type) and\
p.ipv6.hlim == 255).\
must_next()
# Step 3: Send a ICMPv6 Echo Request to the MED_2 ML-EID
# The DUT MUST NOT send an Address Query Request
# MED_2 MUST respond with an ICMPv6 Echo Reply
_pkt = pkts.filter_ipv6_src_dst(MED_1_MLEID, MED_2_MLEID).\
filter_ping_request().\
must_next()
pkts.filter_wpan_src64(LEADER).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
must_not_next()
pkts.filter_ipv6_src_dst(MED_2_MLEID, MED_1_MLEID).\
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
must_next()
# Step 4-6: Send a ICMPv6 Echo Request to the MED_2 each GUA
# The DUT MUST NOT send an Address Query Request
# MED_2 MUST respond with an ICMPv6 Echo Reply
for med_1_addr, med_2_addr in zip(MED_1_GUA, MED_2_GUA):
_pkt = pkts.filter_ipv6_src_dst(med_1_addr, med_2_addr).\
filter_ping_request().\
must_next()
pkts.filter_wpan_src64(LEADER).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
must_not_next()
pkts.filter_ipv6_src_dst(med_2_addr, med_1_addr).\
filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,328 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 copy
import unittest
import command
import config
import copy
import ipv6
import thread_cert
from pktverify.consts import WIRESHARK_OVERRIDE_PREFS, ADDR_QRY_URI, ADDR_NTF_URI, NL_ML_EID_TLV, NL_RLOC16_TLV, NL_TARGET_EID_TLV
from pktverify.packet_verifier import PacketVerifier
from pktverify.bytes import Bytes
LEADER = 1
ROUTER1 = 2
DUT_ROUTER2 = 3
ROUTER3 = 4
SED1 = 5
PREFIX_1 = '2001::/64'
GUA_1_START = '2001'
PREFIX_2 = '2002::/64'
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate that the DUT is able to generate
# Address Query and Address Notification messages properly.
# The Leader is configured as a Border Router with DHCPv6 server for prefixes
# 2001:: & 2002::
#
# Test Topology:
# -------------
# Router_1 - Leader
# / \
# Router_3 Router_2(DUT)
# |
# SED
#
# DUT Types:
# ----------
# Router
class Cert_5_3_09_AddressQuery(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [ROUTER1, DUT_ROUTER2, ROUTER3]
},
ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [LEADER]
},
DUT_ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [LEADER, SED1]
},
ROUTER3: {
'name': 'ROUTER_3',
'mode': 'rdn',
'allowlist': [LEADER]
},
SED1: {
'name': 'SED',
'is_mtd': True,
'mode': '-',
'timeout': config.DEFAULT_CHILD_TIMEOUT,
'allowlist': [DUT_ROUTER2]
},
}
# override wireshark preferences with case needed parameters
CASE_WIRESHARK_PREFS = copy.deepcopy(WIRESHARK_OVERRIDE_PREFS)
CASE_WIRESHARK_PREFS['6lowpan.context1'] = PREFIX_1
CASE_WIRESHARK_PREFS['6lowpan.context2'] = PREFIX_2
def test(self):
# 1 & 2 ALL: Build and verify the topology
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
# Configure the LEADER to be a DHCPv6 Border Router for prefixes
self.nodes[LEADER].add_prefix(PREFIX_1, 'pdros')
self.nodes[LEADER].add_prefix(PREFIX_2, 'pdro')
self.nodes[LEADER].register_netdata()
self.nodes[ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
self.nodes[DUT_ROUTER2].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[DUT_ROUTER2].get_state(), 'router')
self.nodes[ROUTER3].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER3].get_state(), 'router')
self.nodes[SED1].start()
self.simulator.go(5)
self.assertEqual(self.nodes[SED1].get_state(), 'child')
self.collect_rlocs()
self.collect_rloc16s()
self.collect_ipaddrs()
# 3 SED1: The SED1 sends an ICMPv6 Echo Request to ROUTER3 using GUA
# PREFIX_1 address
router3_addr = self.nodes[ROUTER3].get_addr(PREFIX_1)
self.assertTrue(router3_addr is not None)
self.assertTrue(self.nodes[SED1].ping(router3_addr))
# 4 ROUTER1: ROUTER1 sends an ICMPv6 Echo Request to the SED1 using GUA
# PREFIX_1 address
sed1_addr = self.nodes[SED1].get_addr(PREFIX_1)
self.assertTrue(sed1_addr is not None)
self.assertTrue(self.nodes[ROUTER1].ping(sed1_addr))
self.simulator.go(1)
# 5 SED1: SED1 sends an ICMPv6 Echo Request to the ROUTER3 using GUA
# PREFIX_1 address
self.assertTrue(self.nodes[SED1].ping(router3_addr))
self.simulator.go(1)
# 6 DUT_ROUTER2: Power off ROUTER3 and wait 580s to allow LEADER to
# expire its Router ID
self.nodes[ROUTER3].stop()
self.simulator.go(580)
# The SED1 sends an ICMPv6 Echo Request to ROUTER3 GUA PREFIX_1 address
self.assertFalse(self.nodes[SED1].ping(router3_addr))
self.simulator.go(1)
# 7 SED1: Power off SED1 and wait to allow DUT_ROUTER2 to timeout the
# child
self.nodes[SED1].stop()
self.simulator.go(3)
self.simulator.go(config.DEFAULT_CHILD_TIMEOUT)
# ROUTER1 sends two ICMPv6 Echo Requests to SED1 GUA PREFIX_1 address
self.assertFalse(self.nodes[ROUTER1].ping(sed1_addr))
self.assertFalse(self.nodes[ROUTER1].ping(sed1_addr))
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
ROUTER_1 = pv.vars['ROUTER_1']
ROUTER_1_RLOC = pv.vars['ROUTER_1_RLOC']
ROUTER_2 = pv.vars['ROUTER_2']
ROUTER_2_RLOC = pv.vars['ROUTER_2_RLOC']
ROUTER_2_RLOC16 = pv.vars['ROUTER_2_RLOC16']
ROUTER_3 = pv.vars['ROUTER_3']
SED = pv.vars['SED']
SED_RLOC16 = pv.vars['SED_RLOC16']
MM = pv.vars['MM_PORT']
GUA1 = {}
for node in ('ROUTER_1', 'ROUTER_3', 'SED'):
for addr in pv.vars['%s_IPADDRS' % node]:
if addr.startswith(Bytes(GUA_1_START)):
GUA1[node] = addr
# Step 1: Build the topology as described
for i in range(1, 4):
pv.verify_attached('ROUTER_%d' % i, 'LEADER')
pv.verify_attached('SED', 'ROUTER_2', 'MTD')
# Step 2: SED sends an ICMPv6 Echo Request to Router_3 using GUA 2001::
# address
# The DUT MUST generate an Address Query Request on SEDs behalf
# to find each nodes RLOC.
# The Address Query Request MUST be sent to the Realm-Local
# All-Routers address (FF03::2)
# CoAP URI-Path
# - NON POST coap://<FF03::2>
# CoAP Payload
# - Target EID TLV
# The DUT MUST receive and process the incoming Address Query
# Response and forward the ICMPv6 Echo Reply packet to SED
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(SED).\
filter_ipv6_dst(GUA1['ROUTER_3']).\
must_next()
pkts.filter_wpan_src64(ROUTER_2).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
filter(lambda p: p.thread_address.tlv.target_eid == GUA1['ROUTER_3']).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_3).\
filter_ipv6_dst(GUA1['SED']).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_2).\
filter_wpan_dst16(SED_RLOC16).\
must_next()
# Step 3: Router_1 sends an ICMPv6 Echo Request to SED using GUA 2001::
# address
# The DUT MUST respond to the Address Query Request with a properly
# formatted Address Notification Message:
# CoAP URI-Path
# - CON POST coap://[<Address Query Source>]:MM/a/an
# CoAP Payload
# - ML-EID TLV
# - RLOC16 TLV
# - Target EID TLV
# The IPv6 Source address MUST be the RLOC of the originator
# The IPv6 Destination address MUST be the RLOC of the destination
pkts.filter_wpan_src64(ROUTER_1).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
filter(lambda p: p.thread_address.tlv.target_eid == GUA1['SED']).\
must_next()
pkts.filter_ipv6_src_dst(ROUTER_2_RLOC, ROUTER_1_RLOC).\
filter_coap_request(ADDR_NTF_URI, port=MM).\
filter(lambda p: {
NL_ML_EID_TLV,
NL_RLOC16_TLV,
NL_TARGET_EID_TLV
} <= set(p.coap.tlv.type) and\
p.thread_address.tlv.target_eid == GUA1['SED'] and\
p.thread_address.tlv.rloc16 == ROUTER_2_RLOC16
).\
must_next()
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(ROUTER_1).\
filter_ipv6_dst(GUA1['SED']).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(SED).\
filter_ipv6_dst(GUA1['ROUTER_1']).\
must_next()
# Step 5: SED sends an ICMPv6 Echo Request to Router_3 using GUA 2001::
# address
# The DUT MUST not send an Address Query as Router_3 address should
# be cached.
# The DUT MUST forward the ICMPv6 Echo Reply to SED
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(SED).\
filter_ipv6_dst(GUA1['ROUTER_3']).\
must_next()
lstart = pkts.index
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_3).\
filter_ipv6_dst(GUA1['SED']).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_2).\
filter_wpan_dst16(SED_RLOC16).\
must_next()
lend = pkts.index
pkts.range(lstart, lend).filter_wpan_src64(ROUTER_2).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
must_not_next()
# Step 6: SED sends an ICMPv6 Echo Request to Router_3 using GUA 2001::
# address
# The DUT MUST update its address cache and remove all entries
# based on Router_3s Router ID.
# The DUT MUST send an Address Query to discover Router_3s RLOC address.
pkts.filter_ping_request().\
filter_wpan_src64(SED).\
filter_ipv6_dst(GUA1['ROUTER_3']).\
must_next()
pkts.filter_wpan_src64(ROUTER_2).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
filter(lambda p: p.thread_address.tlv.target_eid == GUA1['ROUTER_3']).\
must_next()
# Step 7: Router_1 sends two ICMPv6 Echo Requests to SED using GUA 2001::
# address
# The DUT MUST NOT respond with an Address Notification message
pkts.filter_wpan_src64(ROUTER_2).\
filter_ipv6_dst(ROUTER_1_RLOC).\
filter_coap_request(ADDR_NTF_URI, port=MM).\
must_not_next()
pkts.filter_ping_request().\
filter_wpan_src64(ROUTER_1).\
filter_ipv6_dst(GUA1['SED']).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,335 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2016, 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 copy
import unittest
import command
import config
import copy
import ipv6
import thread_cert
from pktverify.consts import WIRESHARK_OVERRIDE_PREFS, ADDR_QRY_URI, ADDR_NTF_URI, NL_ML_EID_TLV, NL_RLOC16_TLV, NL_TARGET_EID_TLV
from pktverify.packet_verifier import PacketVerifier
from pktverify.bytes import Bytes
from pktverify.addrs import Ipv6Addr
LEADER = 1
BR = 2
ROUTER1 = 3
DUT_ROUTER2 = 4
MED1 = 5
PREFIX_1 = '2003::/64'
GUA_1_START = '2003'
PREFIX_2 = '2004::/64'
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate that the DUT is able to generate
# Address Query and Address Notification messages
# The Border Router is configured as a SLAAC server for prefixes 2003:: & 2004::
#
# Test Topology:
# -------------
# BorderRouter - Leader
# / \
# Router_1 - Router_2(DUT)
# |
# MED
#
# DUT Types:
# ----------
# Router
class Cert_5_3_10_AddressQuery(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
SUPPORT_NCP = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [BR, ROUTER1, DUT_ROUTER2]
},
BR: {
'name': 'BR',
'mode': 'rdn',
'allowlist': [LEADER]
},
ROUTER1: {
'name': 'ROUTER_1',
'mode': 'rdn',
'allowlist': [LEADER, DUT_ROUTER2]
},
DUT_ROUTER2: {
'name': 'ROUTER_2',
'mode': 'rdn',
'allowlist': [LEADER, ROUTER1, MED1]
},
MED1: {
'name': 'MED',
'is_mtd': True,
'mode': 'rn',
'allowlist': [DUT_ROUTER2]
},
}
# override wireshark preferences with case needed parameters
CASE_WIRESHARK_PREFS = copy.deepcopy(WIRESHARK_OVERRIDE_PREFS)
CASE_WIRESHARK_PREFS['6lowpan.context1'] = PREFIX_1
CASE_WIRESHARK_PREFS['6lowpan.context2'] = PREFIX_2
def test(self):
# 1 & 2
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[BR].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[BR].get_state(), 'router')
# Configure two On-Mesh Prefixes on the BR
self.nodes[BR].add_prefix(PREFIX_1, 'paros')
self.nodes[BR].add_prefix(PREFIX_2, 'paros')
self.nodes[BR].register_netdata()
self.nodes[DUT_ROUTER2].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[DUT_ROUTER2].get_state(), 'router')
self.nodes[ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
self.nodes[MED1].start()
self.simulator.go(5)
self.assertEqual(self.nodes[MED1].get_state(), 'child')
self.collect_rlocs()
self.collect_rloc16s()
self.collect_ipaddrs()
# 3 MED1: MED1 sends an ICMPv6 Echo Request to Router1 using GUA
# PREFIX_1 address
router1_addr = self.nodes[ROUTER1].get_addr(PREFIX_1)
self.assertTrue(router1_addr is not None)
self.assertTrue(self.nodes[MED1].ping(router1_addr))
self.simulator.go(1)
# 4 BR: BR sends an ICMPv6 Echo Request to MED1 using GUA PREFIX_1
# address
med1_addr = self.nodes[MED1].get_addr(PREFIX_1)
self.assertTrue(med1_addr is not None)
self.assertTrue(self.nodes[BR].ping(med1_addr))
self.simulator.go(1)
# 5 MED1: MED1 sends an ICMPv6 Echo Request to ROUTER1 using GUA PREFIX_1
# address
self.assertTrue(self.nodes[MED1].ping(router1_addr))
self.simulator.go(1)
# 6 DUT_ROUTER2: Power off ROUTER1 and wait 580 seconds to allow the
# LEADER to expire its Router ID
router1_id = self.nodes[ROUTER1].get_router_id()
self.nodes[ROUTER1].stop()
self.simulator.go(580)
# Send an ICMPv6 Echo Request from MED1 to ROUTER1 GUA PREFIX_1 address
self.assertFalse(self.nodes[MED1].ping(router1_addr))
self.simulator.go(1)
# 7 MED1: Power off MED1 and wait to allow DUT_ROUTER2 to timeout the
# child
self.nodes[MED1].stop()
self.simulator.go(config.MLE_END_DEVICE_TIMEOUT)
# BR sends two ICMPv6 Echo Requests to MED1 GUA PREFIX_1 address
self.assertFalse(self.nodes[BR].ping(med1_addr))
self.assertFalse(self.nodes[BR].ping(med1_addr))
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
ROUTER_1 = pv.vars['ROUTER_1']
ROUTER_2 = pv.vars['ROUTER_2']
ROUTER_2_RLOC = pv.vars['ROUTER_2_RLOC']
ROUTER_2_RLOC16 = pv.vars['ROUTER_2_RLOC16']
BR = pv.vars['BR']
BR_RLOC = pv.vars['BR_RLOC']
MED = pv.vars['MED']
MED_RLOC16 = pv.vars['MED_RLOC16']
MM = pv.vars['MM_PORT']
GUA1 = {}
for node in ('ROUTER_1', 'BR', 'MED'):
for addr in pv.vars['%s_IPADDRS' % node]:
if addr.startswith(Bytes(GUA_1_START)):
GUA1[node] = addr
# Step 2: Build the topology as described
pv.verify_attached('BR', 'LEADER')
for i in (2, 1):
pv.verify_attached('ROUTER_%d' % i, 'LEADER')
pv.verify_attached('MED', 'ROUTER_2', 'MTD')
# Step 3: MED sends an ICMPv6 Echo Request to Router_1 using GUA 2003::
# address
# The DUT MUST generate an Address Query Request on MEDs behalf
# to find each nodes RLOC.
# The Address Query Request MUST be sent to the Realm-Local
# All-Routers address (FF03::2)
# CoAP URI-Path
# - NON POST coap://<FF03::2>
# CoAP Payload
# - Target EID TLV
# The DUT MUST receive and process the incoming Address Query
# Response and forward the ICMPv6 Echo Request packet to Router_1
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(MED).\
filter_ipv6_dst(GUA1['ROUTER_1']).\
must_next()
pkts.filter_wpan_src64(ROUTER_2).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
filter(lambda p: p.thread_address.tlv.target_eid == GUA1['ROUTER_1']).\
must_next()
pkts.filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_2).\
filter_ipv6_dst(GUA1['ROUTER_1']).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_1).\
filter_ipv6_dst(GUA1['MED']).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_2).\
filter_wpan_dst16(MED_RLOC16).\
must_next()
# Step 4: Border Router sends an ICMPv6 Echo Request to MED using GUA 2003::
# address
# The DUT MUST respond to the Address Query Request with a properly
# formatted Address Notification Message:
# CoAP URI-Path
# - CON POST coap://[<Address Query Source>]:MM/a/an
# CoAP Payload
# - ML-EID TLV
# - RLOC16 TLV
# - Target EID TLV
# The IPv6 Source address MUST be the RLOC of the originator
# The IPv6 Destination address MUST be the RLOC of the destination
pkts.filter_wpan_src64(BR).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
filter(lambda p: p.thread_address.tlv.target_eid == GUA1['MED']).\
must_next()
pkts.filter_ipv6_src_dst(ROUTER_2_RLOC, BR_RLOC).\
filter_coap_request(ADDR_NTF_URI, port=MM).\
filter(lambda p: {
NL_ML_EID_TLV,
NL_RLOC16_TLV,
NL_TARGET_EID_TLV
} <= set(p.coap.tlv.type) and\
p.thread_address.tlv.target_eid == GUA1['MED'] and\
p.thread_address.tlv.rloc16 == ROUTER_2_RLOC16
).\
must_next()
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(BR).\
filter_ipv6_dst(GUA1['MED']).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(MED).\
filter_ipv6_dst(GUA1['BR']).\
must_next()
# Step 5: MED sends an ICMPv6 Echo Request to Router_1 using GUA 2003::
# address
# The DUT MUST not send an Address Query as Router_1 address should
# be cached.
# The DUT MUST forward the ICMPv6 Echo Reply to MED
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(MED).\
filter_ipv6_dst(GUA1['ROUTER_1']).\
must_next()
lstart = pkts.index
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_1).\
filter_ipv6_dst(GUA1['MED']).\
must_next()
pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
filter_wpan_src64(ROUTER_2).\
filter_wpan_dst16(MED_RLOC16).\
must_next()
lend = pkts.index
pkts.range(lstart, lend).filter_wpan_src64(ROUTER_2).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
must_not_next()
# Step 6: MED sends an ICMPv6 Echo Request to Router_1 using GUA 2003::
# address
# The DUT MUST update its address cache and remove all entries
# based on Router_1s Router ID.
# The DUT MUST send an Address Query to discover Router_1s RLOC address.
pkts.filter_ping_request().\
filter_wpan_src64(MED).\
filter_ipv6_dst(GUA1['ROUTER_1']).\
must_next()
pkts.filter_wpan_src64(ROUTER_2).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
filter(lambda p: p.thread_address.tlv.target_eid == GUA1['ROUTER_1']).\
must_next()
# Step 7: Border Router sends two ICMPv6 Echo Requests to MED using GUA 2003::
# address
# The DUT MUST NOT respond with an Address Notification message
pkts.filter_wpan_src64(ROUTER_2).\
filter_ipv6_dst(BR_RLOC).\
filter_coap_request(ADDR_NTF_URI, port=MM).\
must_not_next()
pkts.filter_ping_request().\
filter_wpan_src64(BR).\
filter_ipv6_dst(GUA1['MED']).\
must_next()
if __name__ == '__main__':
unittest.main()
@@ -1,199 +0,0 @@
#!/usr/bin/env python3
#
# Copyright (c) 2018, 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 unittest
import command
import config
import thread_cert
from pktverify.consts import ADDR_QRY_URI, ADDR_NTF_URI, NL_ML_EID_TLV, NL_RLOC16_TLV, NL_TARGET_EID_TLV
from pktverify.packet_verifier import PacketVerifier
from pktverify.bytes import Bytes
from pktverify.addrs import Ipv6Addr
LEADER = 1
DUT_ROUTER1 = 2
MED1 = 3
X = 'fd00:db8:0000:0000:aa55:aa55:aa55:aa55'
# Test Purpose and Description:
# -----------------------------
# The purpose of this test case is to validate the way AQ_TIMEOUT and
# AQ_RETRY_TIMEOUT intervals are used in the Address Query transmission
# algorithm.
#
# Test Topology:
# -------------
# Leader
# |
# Router(DUT)
# |
# MED
#
# DUT Types:
# ----------
# Router
class Cert_5_3_11_AddressQueryTimeoutIntervals(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
LEADER: {
'name': 'LEADER',
'mode': 'rdn',
'allowlist': [DUT_ROUTER1]
},
DUT_ROUTER1: {
'name': 'ROUTER',
'mode': 'rdn',
'allowlist': [LEADER, MED1]
},
MED1: {
'name': 'MED',
'is_mtd': True,
'mode': 'rn',
'allowlist': [DUT_ROUTER1]
},
}
def test(self):
self.nodes[LEADER].start()
self.simulator.go(config.LEADER_STARTUP_DELAY)
self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
self.nodes[DUT_ROUTER1].start()
self.simulator.go(config.ROUTER_STARTUP_DELAY)
self.assertEqual(self.nodes[DUT_ROUTER1].get_state(), 'router')
self.nodes[MED1].start()
self.simulator.go(5)
self.assertEqual(self.nodes[MED1].get_state(), 'child')
self.collect_rlocs()
# 2 MED1: MED1 sends an ICMPv6 Echo Request to a non-existent
# mesh-local address X
self.assertFalse(self.nodes[MED1].ping(X))
self.simulator.go(config.AQ_TIMEOUT)
# 3 MED1: MED1 sends an ICMPv6 Echo Request to a non-existent mesh-local address X before
# ADDRESS_QUERY_INITIAL_RETRY_DELAY timeout expires
self.assertFalse(self.nodes[MED1].ping(X))
self.simulator.go(config.ADDRESS_QUERY_INITIAL_RETRY_DELAY)
# 4 MED1: MED1 sends an ICMPv6 Echo Request to a non-existent mesh-local address X after
# ADDRESS_QUERY_INITIAL_RETRY_DELAY timeout expired
self.assertFalse(self.nodes[MED1].ping(X))
def verify(self, pv):
pkts = pv.pkts
pv.summary.show()
LEADER = pv.vars['LEADER']
ROUTER = pv.vars['ROUTER']
ROUTER_RLOC = pv.vars['ROUTER_RLOC']
MED = pv.vars['MED']
MM = pv.vars['MM_PORT']
# Step 1: Build the topology as described
pv.verify_attached('ROUTER', 'LEADER')
pv.verify_attached('MED', 'ROUTER', 'MTD')
# Step 2: MED sends an ICMPv6 Echo Request to a nonexistent mesh-local
# address X
# The DUT MUST generate an Address Query Request on MEDs behalf
# The Address Query Request MUST be sent to the Realm-Local
# All-Routers address (FF03::2)
# CoAP URI-Path
# - NON POST coap://<FF03::2>
# CoAP Payload
# - Target EID TLV non-existent mesh-local
# An Address Query Notification MUST NOT be received within
# AQ_TIMEOUT interval.
_pkt = pkts.filter_ping_request().\
filter_wpan_src64(MED).\
filter_ipv6_dst(Ipv6Addr(X)).\
must_next()
step2_start = pkts.index
pkts.filter_wpan_src64(ROUTER).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
filter(lambda p: p.thread_address.tlv.target_eid == Ipv6Addr(X)).\
must_next()
# Step 3: MED sends an ICMPv6 Echo Request to a nonexistent mesh-local
# address X before ADDRESS_QUERY_INITIAL_RETRY_DELAY timeout
# expired
# The DUT MUST NOT initiate a new Address Query frame
pkts.filter_ping_request().\
filter_wpan_src64(MED).\
filter_ipv6_dst(Ipv6Addr(X)).\
must_next()
step2_end = pkts.index
pkts.range(step2_start, step2_end).filter_ipv6_dst(ROUTER_RLOC).\
filter_coap_request(ADDR_NTF_URI, port=MM).\
must_not_next()
# Step 4: MED sends an ICMPv6 Echo Request to a nonexistent mesh-local
# address X after ADDRESS_QUERY_INITIAL_RETRY_DELAY timeout
# expired
# The DUT MUST generate an Address Query Request on MEDs behalf
# The Address Query Request MUST be sent to the Realm-Local
# All-Routers address (FF03::2)
# CoAP URI-Path
# - NON POST coap://<FF03::2>
# CoAP Payload
# - Target EID TLV non-existent mesh-local
pkts.filter_ping_request().\
filter_wpan_src64(MED).\
filter_ipv6_dst(Ipv6Addr(X)).\
must_next()
step3_end = pkts.index
pkts.range(step2_end, step3_end).filter_wpan_src64(ROUTER).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
must_not_next()
pkts.filter_wpan_src64(ROUTER).\
filter_RLARMA().\
filter_coap_request(ADDR_QRY_URI, port=MM).\
filter(lambda p: p.thread_address.tlv.target_eid == Ipv6Addr(X)).\
must_next()
if __name__ == '__main__':
unittest.main()