From d34f279e5ae87a95c00e60b742e5e90a4c3329bf Mon Sep 17 00:00:00 2001 From: Irving-cl Date: Sun, 30 Dec 2018 01:50:12 +0800 Subject: [PATCH] [tests] enhance Cert_6_1_02_REEDAttach_MED/SED (#3387) --- .../thread-cert/Cert_5_1_06_RemoveRouterId.py | 3 +- ...ttach.py => Cert_6_1_02_REEDAttach_MED.py} | 61 +++++++-- .../thread-cert/Cert_6_1_02_REEDAttach_SED.py | 126 ++++++++++++++++++ tests/scripts/thread-cert/Makefile.am | 6 +- tests/scripts/thread-cert/command.py | 31 ++++- tests/scripts/thread-cert/mac802154.py | 4 + tests/scripts/thread-cert/message.py | 36 ++++- 7 files changed, 245 insertions(+), 22 deletions(-) rename tests/scripts/thread-cert/{Cert_6_1_02_REEDAttach.py => Cert_6_1_02_REEDAttach_MED.py} (54%) create mode 100755 tests/scripts/thread-cert/Cert_6_1_02_REEDAttach_SED.py diff --git a/tests/scripts/thread-cert/Cert_5_1_06_RemoveRouterId.py b/tests/scripts/thread-cert/Cert_5_1_06_RemoveRouterId.py index 986810ba0..7646ec312 100755 --- a/tests/scripts/thread-cert/Cert_5_1_06_RemoveRouterId.py +++ b/tests/scripts/thread-cert/Cert_5_1_06_RemoveRouterId.py @@ -107,8 +107,7 @@ class Cert_5_1_06_RemoveRouterId(unittest.TestCase): msg = router1_messages.next_mle_message(mle.CommandType.PARENT_REQUEST) command.check_parent_request(msg, is_first_request=True) - msg = router1_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST) - msg.assertSentToNode(self.nodes[LEADER]) + msg = router1_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST, sent_to_node=self.nodes[LEADER]) command.check_child_id_request(msg, tlv_request=CheckType.CONTAIN, mle_frame_counter=CheckType.OPTIONAL, address_registration=CheckType.NOT_CONTAIN, active_timestamp=CheckType.OPTIONAL, pending_timestamp=CheckType.OPTIONAL) diff --git a/tests/scripts/thread-cert/Cert_6_1_02_REEDAttach.py b/tests/scripts/thread-cert/Cert_6_1_02_REEDAttach_MED.py similarity index 54% rename from tests/scripts/thread-cert/Cert_6_1_02_REEDAttach.py rename to tests/scripts/thread-cert/Cert_6_1_02_REEDAttach_MED.py index 89cdd5860..b60dfee00 100755 --- a/tests/scripts/thread-cert/Cert_6_1_02_REEDAttach.py +++ b/tests/scripts/thread-cert/Cert_6_1_02_REEDAttach_MED.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright (c) 2016, The OpenThread Authors. +# Copyright (c) 2018, The OpenThread Authors. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -27,23 +27,27 @@ # POSSIBILITY OF SUCH DAMAGE. # -import time import unittest +from command import check_parent_request +from command import check_child_id_request +from command import check_child_update_request_by_child +from command import CheckType import config +import mle import node LEADER = 1 REED = 2 -ED = 3 +MED = 3 -class Cert_6_1_2_REEDAttach(unittest.TestCase): +class Cert_6_1_2_REEDAttach_MED(unittest.TestCase): def setUp(self): self.simulator = config.create_default_simulator() self.nodes = {} for i in range(1,4): - self.nodes[i] = node.Node(i, (i == ED), simulator=self.simulator) + self.nodes[i] = node.Node(i, (i == MED), simulator=self.simulator) self.nodes[LEADER].set_panid(0xface) self.nodes[LEADER].set_mode('rsdn') @@ -53,14 +57,15 @@ class Cert_6_1_2_REEDAttach(unittest.TestCase): self.nodes[REED].set_panid(0xface) self.nodes[REED].set_mode('rsdn') self.nodes[REED].add_whitelist(self.nodes[LEADER].get_addr64()) - self.nodes[REED].add_whitelist(self.nodes[ED].get_addr64()) + self.nodes[REED].add_whitelist(self.nodes[MED].get_addr64()) self.nodes[REED].enable_whitelist() self.nodes[REED].set_router_upgrade_threshold(0) - self.nodes[ED].set_panid(0xface) - self.nodes[ED].set_mode('rsn') - self.nodes[ED].add_whitelist(self.nodes[REED].get_addr64()) - self.nodes[ED].enable_whitelist() + self.nodes[MED].set_panid(0xface) + self.nodes[MED].set_mode('rsn') + self.nodes[MED].add_whitelist(self.nodes[REED].get_addr64()) + self.nodes[MED].enable_whitelist() + self.nodes[MED].set_timeout(config.DEFAULT_CHILD_TIMEOUT) def tearDown(self): for node in list(self.nodes.values()): @@ -77,10 +82,40 @@ class Cert_6_1_2_REEDAttach(unittest.TestCase): self.simulator.go(5) self.assertEqual(self.nodes[REED].get_state(), 'child') - self.nodes[ED].start() - self.simulator.go(5) - self.assertEqual(self.nodes[ED].get_state(), 'child') + self.nodes[MED].start() + + self.simulator.go(5) + self.assertEqual(self.nodes[MED].get_state(), 'child') self.assertEqual(self.nodes[REED].get_state(), 'router') + med_messages = self.simulator.get_messages_sent_by(MED) + + # Step 2 - DUT sends MLE Parent Request + msg = med_messages.next_mle_message(mle.CommandType.PARENT_REQUEST) + check_parent_request(msg, is_first_request=True) + + # Step 4 - DUT sends MLE Parent Request again + msg = med_messages.next_mle_message(mle.CommandType.PARENT_REQUEST) + check_parent_request(msg, is_first_request=False) + + # Step 6 - DUT sends Child ID Request + msg = med_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST, sent_to_node=self.nodes[REED]) + check_child_id_request(msg, address_registration=CheckType.CONTAIN, + tlv_request=CheckType.CONTAIN, mle_frame_counter=CheckType.OPTIONAL, + route64=CheckType.OPTIONAL) + + # Wait additional DEFAULT_CHILD_TIMEOUT to ensure the keep-alive message (child update request from MED) happens. + self.simulator.go(config.DEFAULT_CHILD_TIMEOUT) + med_messages = self.simulator.get_messages_sent_by(MED) + + # Step 8 - DUT sends Child Update messages + msg = med_messages.next_mle_message(mle.CommandType.CHILD_UPDATE_REQUEST) + check_child_update_request_by_child(msg) + + # Step 10 - Leader sends ICMPv6 echo request, to DUT link local address + med_addrs = self.nodes[MED].get_addrs() + for addr in med_addrs: + if addr[0:4] == 'fe80': + self.assertTrue(self.nodes[REED].ping(addr)) if __name__ == '__main__': unittest.main() diff --git a/tests/scripts/thread-cert/Cert_6_1_02_REEDAttach_SED.py b/tests/scripts/thread-cert/Cert_6_1_02_REEDAttach_SED.py new file mode 100755 index 000000000..9ddedb104 --- /dev/null +++ b/tests/scripts/thread-cert/Cert_6_1_02_REEDAttach_SED.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python +# +# 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 + +from command import check_parent_request +from command import check_child_id_request +from command import CheckType +import config +import mac802154 +import message +import mle +import node + +LEADER = 1 +REED = 2 +SED = 3 + +class Cert_6_1_2_REEDAttach_SED(unittest.TestCase): + def setUp(self): + self.simulator = config.create_default_simulator() + + self.nodes = {} + for i in range(1,4): + self.nodes[i] = node.Node(i, (i == SED), simulator=self.simulator) + + self.nodes[LEADER].set_panid(0xface) + self.nodes[LEADER].set_mode('rsdn') + self.nodes[LEADER].add_whitelist(self.nodes[REED].get_addr64()) + self.nodes[LEADER].enable_whitelist() + + self.nodes[REED].set_panid(0xface) + self.nodes[REED].set_mode('rsdn') + self.nodes[REED].add_whitelist(self.nodes[LEADER].get_addr64()) + self.nodes[REED].add_whitelist(self.nodes[SED].get_addr64()) + self.nodes[REED].enable_whitelist() + self.nodes[REED].set_router_upgrade_threshold(0) + + self.nodes[SED].set_panid(0xface) + self.nodes[SED].set_mode('s') + self.nodes[SED].add_whitelist(self.nodes[REED].get_addr64()) + self.nodes[SED].enable_whitelist() + self.nodes[SED].set_timeout(config.DEFAULT_CHILD_TIMEOUT) + + def tearDown(self): + for node in list(self.nodes.values()): + node.stop() + node.destroy() + self.simulator.stop() + + def test(self): + self.nodes[LEADER].start() + self.simulator.go(5) + self.assertEqual(self.nodes[LEADER].get_state(), 'leader') + + self.nodes[REED].start() + self.simulator.go(5) + self.assertEqual(self.nodes[REED].get_state(), 'child') + + self.nodes[SED].start() + self.simulator.go(5) + self.assertEqual(self.nodes[SED].get_state(), 'child') + self.assertEqual(self.nodes[REED].get_state(), 'router') + + sed_messages = self.simulator.get_messages_sent_by(SED) + + # Step 2 - DUT sends MLE Parent Request + msg = sed_messages.next_mle_message(mle.CommandType.PARENT_REQUEST) + check_parent_request(msg, is_first_request=True) + + # Step 4 - DUT sends MLE Parent Request again + msg = sed_messages.next_mle_message(mle.CommandType.PARENT_REQUEST) + check_parent_request(msg, is_first_request=False) + + # Step 6 - DUT sends Child ID Request + msg = sed_messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST, sent_to_node=self.nodes[REED]) + check_child_id_request(msg, address_registration=CheckType.CONTAIN, + tlv_request=CheckType.CONTAIN, mle_frame_counter=CheckType.OPTIONAL, + route64=CheckType.OPTIONAL) + + # Wait DEFAULT_CHILD_TIMEOUT seconds, + # ensure SED has received the CHILD_ID_RESPONSE, + # and the next data requests would be keep-alive messages + self.simulator.go(config.DEFAULT_CHILD_TIMEOUT) + sed_messages = self.simulator.get_messages_sent_by(SED) + + # Step 11 - SED sends periodic 802.15.4 Data Request messages + msg = sed_messages.next_message() + self.assertEqual(False, msg.isMacAddressTypeLong()) # Extra check, keep-alive messages are of short types of mac address + self.assertEqual(msg.type, message.MessageType.COMMAND) + self.assertEqual(msg.mac_header.command_type, mac802154.MacHeader.CommandIdentifier.DATA_REQUEST) + + # Step 12 - REED sends ICMPv6 echo request, to DUT link local address + sed_addrs = self.nodes[SED].get_addrs() + for addr in sed_addrs: + if addr[0:4] == 'fe80': + self.assertTrue(self.nodes[REED].ping(addr)) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/scripts/thread-cert/Makefile.am b/tests/scripts/thread-cert/Makefile.am index a12b1c325..ab37e8ca5 100644 --- a/tests/scripts/thread-cert/Makefile.am +++ b/tests/scripts/thread-cert/Makefile.am @@ -82,7 +82,8 @@ EXTRA_DIST = \ Cert_5_8_02_KeyIncrement.py \ Cert_5_8_03_KeyIncrementRollOver.py \ Cert_6_1_01_RouterAttach.py \ - Cert_6_1_02_REEDAttach.py \ + Cert_6_1_02_REEDAttach_MED.py \ + Cert_6_1_02_REEDAttach_SED.py \ Cert_6_1_03_RouterAttachConnectivity.py \ Cert_6_1_04_REEDAttachConnectivity.py \ Cert_6_1_05_RouterAttachLinkQuality.py \ @@ -217,7 +218,8 @@ check_SCRIPTS = \ Cert_5_8_02_KeyIncrement.py \ Cert_5_8_03_KeyIncrementRollOver.py \ Cert_6_1_01_RouterAttach.py \ - Cert_6_1_02_REEDAttach.py \ + Cert_6_1_02_REEDAttach_MED.py \ + Cert_6_1_02_REEDAttach_SED.py \ Cert_6_1_03_RouterAttachConnectivity.py \ Cert_6_1_04_REEDAttachConnectivity.py \ Cert_6_1_05_RouterAttachLinkQuality.py \ diff --git a/tests/scripts/thread-cert/command.py b/tests/scripts/thread-cert/command.py index 5e730721f..1d2bb9498 100644 --- a/tests/scripts/thread-cert/command.py +++ b/tests/scripts/thread-cert/command.py @@ -223,7 +223,7 @@ def get_routing_cost(command_msg, router_id): assert router_id_mask_str[router_id - prefix_len] == '1', "Error: The router isn't in the topology. \n" \ + "route64 tlv is: %s. \nrouter_id is: %s. \nrouting_entry_pos is: %s. \nrouter_id_mask_str is: %s." \ %(tlv, router_id, routing_entry_pos, router_id_mask_str) - + return tlv.link_quality_and_route_data[routing_entry_pos].route def check_mle_optional_tlv(command_msg, type, tlv): @@ -246,10 +246,14 @@ def check_mle_advertisement(command_msg): def check_parent_request(command_msg, is_first_request): """Verify a properly formatted Parent Request command message. """ + if command_msg.mle.aux_sec_hdr.key_id_mode != 0x2: + raise ValueError("The Key Identifier Mode of the Security Control Field SHALL be set to 0x02") + command_msg.assertSentWithHopLimit(255) command_msg.assertSentToDestinationAddress(config.LINK_LOCAL_ALL_ROUTERS_ADDRESS) command_msg.assertMleMessageContainsTlv(mle.Mode) command_msg.assertMleMessageContainsTlv(mle.Challenge) + command_msg.assertMleMessageContainsTlv(mle.Version) scan_mask = command_msg.assertMleMessageContainsTlv(mle.ScanMask) if not scan_mask.router: raise ValueError("Parent request without R bit set") @@ -259,8 +263,6 @@ def check_parent_request(command_msg, is_first_request): elif not scan_mask.end_device: raise ValueError("Second parent request without E bit set") - command_msg.assertMleMessageContainsTlv(mle.Version) - def check_parent_response(command_msg, mle_frame_counter = CheckType.OPTIONAL): """Verify a properly formatted Parent Response command message. """ @@ -277,9 +279,13 @@ def check_parent_response(command_msg, mle_frame_counter = CheckType.OPTIONAL): def check_child_id_request(command_msg, tlv_request = CheckType.OPTIONAL, \ mle_frame_counter = CheckType.OPTIONAL, address_registration = CheckType.OPTIONAL, \ - active_timestamp = CheckType.OPTIONAL, pending_timestamp = CheckType.OPTIONAL): + active_timestamp = CheckType.OPTIONAL, pending_timestamp = CheckType.OPTIONAL, + route64 = CheckType.OPTIONAL): """Verify a properly formatted Child Id Request command message. """ + if command_msg.mle.aux_sec_hdr.key_id_mode != 0x2: + raise ValueError("The Key Identifier Mode of the Security Control Field SHALL be set to 0x02") + command_msg.assertMleMessageContainsTlv(mle.LinkLayerFrameCounter) command_msg.assertMleMessageContainsTlv(mle.Mode) command_msg.assertMleMessageContainsTlv(mle.Response) @@ -291,6 +297,10 @@ def check_child_id_request(command_msg, tlv_request = CheckType.OPTIONAL, \ check_mle_optional_tlv(command_msg, address_registration, mle.AddressRegistration) check_mle_optional_tlv(command_msg, active_timestamp, mle.ActiveTimestamp) check_mle_optional_tlv(command_msg, pending_timestamp, mle.PendingTimestamp) + check_mle_optional_tlv(command_msg, route64, mle.Route64) + + check_tlv_request_tlv(command_msg, CheckType.CONTAIN, mle.TlvType.ADDRESS16) + check_tlv_request_tlv(command_msg, CheckType.CONTAIN, mle.TlvType.NETWORK_DATA) def check_child_id_response(command_msg, route64 = CheckType.OPTIONAL, network_data = CheckType.OPTIONAL, \ address_registration = CheckType.OPTIONAL, active_timestamp = CheckType.OPTIONAL, \ @@ -310,6 +320,11 @@ def check_child_id_response(command_msg, route64 = CheckType.OPTIONAL, network_d check_mle_optional_tlv(command_msg, active_operational_dataset, mle.ActiveOperationalDataset) check_mle_optional_tlv(command_msg, pending_operational_dataset, mle.PendingOperationalDataset) +def check_child_update_request_by_child(command_msg): + command_msg.assertMleMessageContainsTlv(mle.LeaderData) + command_msg.assertMleMessageContainsTlv(mle.Mode) + command_msg.assertMleMessageContainsTlv(mle.SourceAddress) + def check_coap_optional_tlv(coap_msg, type, tlv): if (type == CheckType.CONTAIN): coap_msg.assertCoapMessageContainsTlv(tlv) @@ -328,3 +343,11 @@ def check_router_id_cached(node, router_id, cached = True): assert any(router_id == (int(rloc, 16) >> 10) for (_, rloc) in eidcaches) else: assert any(router_id == (int(rloc, 16) >> 10) for (_, rloc) in eidcaches) is False + +def contains_tlv(sub_tlvs, tlv_type): + return any(isinstance(sub_tlv, tlv_type) for sub_tlv in sub_tlvs) + +def get_sub_tlv(tlvs, tlv_type): + for sub_tlv in tlvs: + if isinstance(sub_tlv, tlv_type): + return sub_tlv diff --git a/tests/scripts/thread-cert/mac802154.py b/tests/scripts/thread-cert/mac802154.py index a666e671a..dba707a1e 100644 --- a/tests/scripts/thread-cert/mac802154.py +++ b/tests/scripts/thread-cert/mac802154.py @@ -77,6 +77,9 @@ class MacHeader: SHORT = 2 EXTENDED = 3 + class CommandIdentifier: + DATA_REQUEST = 4 + def __init__(self, frame_type, frame_pending, ack_request, frame_version, seq, dest_pan_id=None, dest_address=None, src_pan_id=None, src_address=None, command_type=None, aux_sec_header=None, mic=None, @@ -92,6 +95,7 @@ class MacHeader: self.dest_address = dest_address self.src_pan_id = src_pan_id self.src_address = src_address + self.command_type = command_type self.aux_sec_header = aux_sec_header self.mic = mic diff --git a/tests/scripts/thread-cert/message.py b/tests/scripts/thread-cert/message.py index 3f496ee64..30ca6f152 100644 --- a/tests/scripts/thread-cert/message.py +++ b/tests/scripts/thread-cert/message.py @@ -303,6 +303,9 @@ class Message(object): def assertSentWithHopLimit(self, hop_limit): assert self.ipv6_packet.ipv6_header.hop_limit == hop_limit + def isMacAddressTypeLong(self): + return self.mac_header.dest_address.type == common.MacAddressType.LONG + def __repr__(self): return "Message(type={})".format(MessageType(self.type).name) @@ -369,12 +372,15 @@ class MessagesSet(object): return message - def next_mle_message(self, command_type, assert_enabled=True): + def next_mle_message(self, command_type, assert_enabled=True, sent_to_node=None): message = self.next_mle_message_of_one_of_command_types(command_type) if assert_enabled: assert message is not None, "Could not find MleMessage of the type: {}".format(command_type) + if sent_to_node != None: + message.assertSentToNode(sent_to_node) + return message def next_mle_message_of_one_of_command_types(self, *command_types): @@ -399,6 +405,34 @@ class MessagesSet(object): return message + def next_message(self, assert_enabled=True): + message = self.messages.pop(0) + if assert_enabled: + assert message is not None, "Could not find next Message" + return message + + def next_message_of(self, message_type, assert_enabled=True): + message = None + + while self.messages: + m = self.messages.pop(0) + if m.type != message_type: + continue + + message = m + break + + if assert_enabled: + assert message is not None, "Could not find Message of the type: {}".format(message_type) + + return message + + def next_data_message(self): + return self.next_message_of(MessageType.DATA) + + def next_command_message(self): + return self.next_message_of(MessageType.COMMAND) + def contains_icmp_message(self): for m in self.messages: if m.type == MessageType.ICMP: