1#!/usr/bin/env python3 2# 3# Copyright (c) 2016, The OpenThread Authors. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are met: 8# 1. Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# 2. Redistributions in binary form must reproduce the above copyright 11# notice, this list of conditions and the following disclaimer in the 12# documentation and/or other materials provided with the distribution. 13# 3. Neither the name of the copyright holder nor the 14# names of its contributors may be used to endorse or promote products 15# derived from this software without specific prior written permission. 16# 17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27# POSSIBILITY OF SUCH DAMAGE. 28# 29 30import unittest 31 32import config 33import mle 34import network_layer 35import thread_cert 36from 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 37from pktverify.packet_verifier import PacketVerifier 38 39LEADER = 1 40ROUTER1 = 2 41ROUTER2 = 3 42 43# Test Purpose and Description: 44# ----------------------------- 45# The purpose of this test case is to verify that when the original Leader is 46# removed from the network, the DUT will create a new partition as Leader and 47# will assign a router ID if a specific ID is requested. 48# 49# Test Topology: 50# ------------- 51# Leader 52# / \ 53# Router_1 - Router_2 54# [DUT] 55# 56# DUT Types: 57# ---------- 58# Router 59 60 61class Cert_5_1_04_RouterAddressReallocation(thread_cert.TestCase): 62 USE_MESSAGE_FACTORY = False 63 SUPPORT_NCP = False 64 65 TOPOLOGY = { 66 LEADER: { 67 'name': 'LEADER', 68 'mode': 'rdn', 69 'partition_id': 1, 70 'allowlist': [ROUTER1, ROUTER2] 71 }, 72 ROUTER1: { 73 'name': 'ROUTER_1', 74 'mode': 'rdn', 75 'allowlist': [LEADER, ROUTER2] 76 }, 77 ROUTER2: { 78 'name': 'ROUTER_2', 79 'mode': 'rdn', 80 'allowlist': [LEADER, ROUTER1] 81 }, 82 } 83 84 def test(self): 85 self.nodes[LEADER].start() 86 self.simulator.go(config.LEADER_STARTUP_DELAY) 87 self.assertEqual(self.nodes[LEADER].get_state(), 'leader') 88 89 self.nodes[ROUTER1].start() 90 self.simulator.go(config.ROUTER_STARTUP_DELAY) 91 self.assertEqual(self.nodes[ROUTER1].get_state(), 'router') 92 93 self.nodes[ROUTER2].start() 94 self.simulator.go(config.ROUTER_STARTUP_DELAY) 95 self.assertEqual(self.nodes[ROUTER2].get_state(), 'router') 96 97 self.nodes[ROUTER2].set_network_id_timeout(200) 98 self.nodes[LEADER].stop() 99 self.simulator.go(220) 100 self.assertEqual(self.nodes[ROUTER1].get_state(), 'leader') 101 self.assertEqual(self.nodes[ROUTER2].get_state(), 'router') 102 103 def verify(self, pv): 104 pkts = pv.pkts 105 pv.summary.show() 106 107 LEADER = pv.vars['LEADER'] 108 ROUTER_1 = pv.vars['ROUTER_1'] 109 ROUTER_2 = pv.vars['ROUTER_2'] 110 MM = pv.vars['MM_PORT'] 111 112 # Step 3: Verify topology is formed correctly. 113 114 pv.verify_attached('ROUTER_1') 115 _pkt_pt = pkts.filter_wpan_src64(ROUTER_1).\ 116 filter_LLANMA().\ 117 filter_mle_cmd(MLE_ADVERTISEMENT).\ 118 must_next() 119 with pkts.save_index(): 120 _pkt_id = pkts.filter_wpan_src64(ROUTER_1).\ 121 filter_wpan_dst64(ROUTER_2).\ 122 filter_mle_cmd(MLE_PARENT_RESPONSE).\ 123 must_next() 124 125 pv.verify_attached('ROUTER_2') 126 _pkt_as = pkts.filter_wpan_src64(LEADER).\ 127 filter_coap_ack(ADDR_SOL_URI, port=MM).\ 128 must_next() 129 130 # Step 5: Router_1 MUST attempt to reattach to its original partition 131 # by sending a MLE Parent Request with a hop limit of 255 to 132 # the All-Routers multicast address (FF02::2). 133 # The following TLVs MUST be present in the MLE Parent Request: 134 # - Challenge TLV 135 # - Mode TLV 136 # - Scan Mask TLV (MUST have E and R flags set) 137 # - Version TLV 138 # The DUT MUST make two separate attempts to reconnect to its 139 # original partition in this manner 140 141 with pkts.save_index(): 142 for i in range(1, 3): 143 pkts.filter_wpan_src64(ROUTER_1).\ 144 filter_LLARMA().\ 145 filter_mle_cmd(MLE_PARENT_REQUEST).\ 146 filter(lambda p: { 147 CHALLENGE_TLV, 148 MODE_TLV, 149 SCAN_MASK_TLV, 150 VERSION_TLV 151 } <= set(p.mle.tlv.type) and\ 152 p.ipv6.hlim == 255 and\ 153 p.mle.tlv.scan_mask.r == 1 and\ 154 p.mle.tlv.scan_mask.e == 1 155 ).\ 156 must_next() 157 158 # Step 6: Router_1 MUST attempt to attach to any other partition 159 # within range by sending a MLE Parent Request. 160 # The following TLVs MUST be present in the MLE Parent Request: 161 # - Challenge TLV 162 # - Mode TLV 163 # - Scan Mask TLV 164 # - Version TLV 165 166 pkts.filter_wpan_src64(ROUTER_1).\ 167 filter_LLARMA().\ 168 filter_mle_cmd(MLE_PARENT_REQUEST).\ 169 filter(lambda p: { 170 CHALLENGE_TLV, 171 MODE_TLV, 172 SCAN_MASK_TLV, 173 VERSION_TLV 174 } <= set(p.mle.tlv.type) and\ 175 p.ipv6.hlim == 255 and\ 176 p.mle.tlv.scan_mask.r == 1 and\ 177 p.mle.tlv.scan_mask.e == 0 178 ).\ 179 must_next() 180 181 # Step 7: Router_1 MUST create a new partition and update the following 182 # values randomly: 183 # - Partition ID 184 # - Initial VN_version & VN_stable_version 185 # - Initial ID sequence number 186 # Notes: Considerring the randomly created VN_version & VN_stable_version 187 # could equal the previous versions, checking one of them to 188 # reduce case failures 189 190 pkts.filter_wpan_src64(ROUTER_1).\ 191 filter_LLANMA().\ 192 filter_mle_cmd(MLE_ADVERTISEMENT).\ 193 filter(lambda p:\ 194 p.mle.tlv.leader_data.partition_id != 195 _pkt_pt.mle.tlv.leader_data.partition_id and\ 196 (p.mle.tlv.leader_data.data_version != 197 _pkt_pt.mle.tlv.leader_data.data_version or\ 198 p.mle.tlv.leader_data.stable_data_version != 199 _pkt_pt.mle.tlv.leader_data.stable_data_version) 200 ).\ 201 must_next() 202 203 # Step 9: Router_1 MUST send a properly formatted Parent Response and 204 # Child ID Response to Router_2. 205 206 pkts.filter_wpan_src64(ROUTER_1).\ 207 filter_wpan_dst64(ROUTER_2).\ 208 filter_mle_cmd(MLE_PARENT_RESPONSE).\ 209 filter(lambda p: { 210 CHALLENGE_TLV, 211 CONNECTIVITY_TLV, 212 LEADER_DATA_TLV, 213 LINK_LAYER_FRAME_COUNTER_TLV, 214 LINK_MARGIN_TLV, 215 RESPONSE_TLV, 216 SOURCE_ADDRESS_TLV, 217 VERSION_TLV 218 } <= set(p.mle.tlv.type) and\ 219 p.mle.tlv.conn.id_seq != _pkt_id.mle.tlv.conn.id_seq 220 ).\ 221 must_next() 222 223 pkts.filter_wpan_src64(ROUTER_1).\ 224 filter_wpan_dst64(ROUTER_2).\ 225 filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\ 226 filter(lambda p: { 227 ADDRESS16_TLV, 228 LEADER_DATA_TLV, 229 NETWORK_DATA_TLV, 230 SOURCE_ADDRESS_TLV, 231 ROUTE64_TLV 232 } <= set(p.mle.tlv.type) or\ 233 { 234 ADDRESS16_TLV, 235 LEADER_DATA_TLV, 236 NETWORK_DATA_TLV, 237 SOURCE_ADDRESS_TLV 238 } <= set(p.mle.tlv.type)\ 239 ).\ 240 must_next() 241 242 # Step 10: Router_1 MUST send a properly-formatted Address Solicit 243 # Response Message to Router_2. 244 # If a specific router ID is requested, the DUT MUST provide this 245 # router ID: 246 # CoAP Response Code 247 # - 2.04 Changed 248 # CoAP Payload 249 # - Status TLV (value = Success) 250 # - RLOC16 TLV 251 # - Router Mask TLV 252 253 pkts.filter_wpan_src64(ROUTER_1).\ 254 filter_coap_ack(ADDR_SOL_URI, port=MM).\ 255 filter(lambda p: { 256 NL_STATUS_TLV, 257 NL_RLOC16_TLV, 258 NL_ROUTER_MASK_TLV 259 } == set(p.coap.tlv.type) and\ 260 p.coap.code == COAP_CODE_ACK and\ 261 p.thread_address.tlv.rloc16 == 262 _pkt_as.thread_address.tlv.rloc16 and\ 263 p.thread_address.tlv.status == 0 264 ).\ 265 must_next() 266 267 268if __name__ == '__main__': 269 unittest.main() 270