1#!/usr/bin/env python3 2# 3# Copyright (c) 2020, 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 31import copy 32 33import thread_cert 34import config 35from pktverify.consts import MLE_PARENT_REQUEST, MLE_PARENT_RESPONSE, MLE_CHILD_ID_REQUEST, MLE_CHILD_ID_RESPONSE, 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 36from pktverify.packet_verifier import PacketVerifier 37 38LEADER = 1 39ROUTER = 2 40REED_1 = 3 41REED_2 = 4 42MTD = 5 43 44# Test Purpose and Description: 45# ----------------------------- 46# The purpose of this test case is to validate that the DUT will pick REED_1 47# as its parent because of its better connectivity. 48# 49# Test Topology: 50# ------------- 51# Router - Leader 52# | / | 53# REED_1 REED_2 54# \ / 55# DUT 56# 57# DUT Types: 58# ---------- 59# ED 60# SED 61 62 63class Cert_6_1_4_REEDAttachConnectivity_Base(thread_cert.TestCase): 64 USE_MESSAGE_FACTORY = False 65 66 TOPOLOGY = { 67 LEADER: { 68 'name': 'LEADER', 69 'mode': 'rdn', 70 'allowlist': [ROUTER, REED_1, REED_2] 71 }, 72 ROUTER: { 73 'name': 'ROUTER', 74 'mode': 'rdn', 75 'allowlist': [LEADER, REED_1] 76 }, 77 REED_1: { 78 'name': 'REED_1', 79 'mode': 'rdn', 80 'router_upgrade_threshold': 0, 81 'allowlist': [LEADER, ROUTER, MTD] 82 }, 83 REED_2: { 84 'name': 'REED_2', 85 'mode': 'rdn', 86 'router_upgrade_threshold': 0, 87 'allowlist': [LEADER, MTD] 88 }, 89 MTD: { 90 'name': 'DUT', 91 'is_mtd': True, 92 'timeout': config.DEFAULT_CHILD_TIMEOUT, 93 'allowlist': [REED_1, REED_2] 94 }, 95 } 96 97 def test(self): 98 self.nodes[LEADER].start() 99 self.simulator.go(config.LEADER_STARTUP_DELAY) 100 self.assertEqual(self.nodes[LEADER].get_state(), 'leader') 101 102 self.nodes[ROUTER].start() 103 self.simulator.go(config.ROUTER_STARTUP_DELAY) 104 self.assertEqual(self.nodes[ROUTER].get_state(), 'router') 105 106 for i in (3, 4): 107 self.nodes[i].start() 108 self.simulator.go(5) 109 self.assertEqual(self.nodes[i].get_state(), 'child') 110 111 self.collect_rloc16s() 112 self.collect_rlocs() 113 114 self.nodes[MTD].start() 115 self.simulator.go(5) 116 self.assertEqual(self.nodes[MTD].get_state(), 'child') 117 self.assertEqual(self.nodes[REED_1].get_state(), 'router') 118 119 self.simulator.go(config.DEFAULT_CHILD_TIMEOUT) 120 self.collect_ipaddrs() 121 122 dut_addr = self.nodes[MTD].get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL) 123 self.assertTrue(self.nodes[REED_1].ping(dut_addr)) 124 125 def verify(self, pv): 126 pkts = pv.pkts 127 pv.summary.show() 128 129 LEADER = pv.vars['LEADER'] 130 LEADER_RLOC = pv.vars['LEADER_RLOC'] 131 ROUTER = pv.vars['ROUTER'] 132 REED_1 = pv.vars['REED_1'] 133 REED_1_RLOC = pv.vars['REED_1_RLOC'] 134 REED_1_LLA = pv.vars['REED_1_LLA'] 135 REED_1_RLOC16 = pv.vars['REED_1_RLOC16'] 136 DUT = pv.vars['DUT'] 137 DUT_RLOC = pv.vars['DUT_RLOC'] 138 DUT_LLA = pv.vars['DUT_LLA'] 139 140 # Step 1: Setup the topology without the DUT. Ensure all routers and 141 # leader are sending MLE advertisements 142 143 pkts.filter_wpan_src64(LEADER).\ 144 filter_mle_advertisement('Leader').\ 145 must_next() 146 147 pv.verify_attached('ROUTER', 'LEADER') 148 149 # Step 2: DUT sends a MLE Parent Request with an IP hop limit of 150 # 255 to the Link-Local All Routers multicast address (FF02::2). 151 # The following TLVs MUST be present in the MLE Parent Request: 152 # - Challenge TLV 153 # - Mode TLV 154 # - Scan Mask TLV 155 # Verify that the first one is sent to routers only 156 # - Version TLV 157 # If the first MLE Parent Request was sent to all Routers and 158 # REED_1S, the test fails. 159 160 pkts.filter_wpan_src64(DUT).\ 161 filter_LLARMA().\ 162 filter_mle_cmd(MLE_PARENT_REQUEST).\ 163 filter(lambda p: { 164 CHALLENGE_TLV, 165 MODE_TLV, 166 SCAN_MASK_TLV, 167 VERSION_TLV 168 } <= set(p.mle.tlv.type) and\ 169 p.ipv6.hlim == 255 and\ 170 p.mle.tlv.scan_mask.r == 1 and\ 171 p.mle.tlv.scan_mask.e == 0 and\ 172 p.wpan.aux_sec.key_id_mode == 0x2 173 ).\ 174 must_next() 175 index1 = pkts.index 176 177 # Step 4: DUT sends a MLE Parent Request with an IP hop limit of 178 # 255 to the Link-Local All Routers multicast address (FF02::2). 179 # The following TLVs MUST be present in the MLE Parent Request: 180 # - Challenge TLV 181 # - Mode TLV 182 # - Scan Mask TLV 183 # Verify that it is sent to Routers AND REED_1s 184 # - Version TLV 185 # If request was not sent to all routers and REED_1S, then the test 186 # has failed. 187 pkts.filter_wpan_src64(DUT).\ 188 filter_LLARMA().\ 189 filter_mle_cmd(MLE_PARENT_REQUEST).\ 190 filter(lambda p: { 191 CHALLENGE_TLV, 192 MODE_TLV, 193 SCAN_MASK_TLV, 194 VERSION_TLV 195 } <= set(p.mle.tlv.type) and\ 196 p.ipv6.hlim == 255 and\ 197 p.mle.tlv.scan_mask.r == 1 and\ 198 p.mle.tlv.scan_mask.e == 1 and\ 199 p.wpan.aux_sec.key_id_mode == 0x2 200 ).\ 201 must_next() 202 index2 = pkts.index 203 204 # Step 3: REED_1 and REED_2 sends no response to Parent Request after 205 # DUT sent the first Parent Request 206 for i in (1, 2): 207 pkts.range(index1, index2).\ 208 filter_wpan_src64(pv.vars['REED_%d' %i]).\ 209 filter_wpan_dst64(DUT).\ 210 filter_mle_cmd(MLE_PARENT_RESPONSE).\ 211 must_not_next() 212 213 # Step 5: REED_1 Respond with MLE Parent Response 214 for i in (1, 2): 215 with pkts.save_index(): 216 pkts.filter_wpan_src64(pv.vars['REED_%d' %i]).\ 217 filter_wpan_dst64(DUT).\ 218 filter_mle_cmd(MLE_PARENT_RESPONSE).\ 219 filter(lambda p: { 220 CHALLENGE_TLV, 221 CONNECTIVITY_TLV, 222 LEADER_DATA_TLV, 223 LINK_LAYER_FRAME_COUNTER_TLV, 224 LINK_MARGIN_TLV, 225 RESPONSE_TLV, 226 SOURCE_ADDRESS_TLV, 227 VERSION_TLV 228 } <= set(p.mle.tlv.type)).\ 229 must_next() 230 231 # Step 6: DUT sends a MLE Child ID Request to REED_1 232 # The following TLVs MUST be present in the MLE Child ID Request: 233 # - Address Registration TLV 234 # - Link-layer Frame Counter TLV 235 # - Mode TLV 236 # - Response TLV 237 # - Timeout TLV 238 # - TLV Request TLV 239 # - Version TLV 240 # - MLE Frame Counter TLV (optional) 241 242 pkts.filter_wpan_src64(DUT).\ 243 filter_wpan_dst64(REED_1).\ 244 filter_mle_cmd(MLE_CHILD_ID_REQUEST).\ 245 filter(lambda p: { 246 ADDRESS_REGISTRATION_TLV, 247 LINK_LAYER_FRAME_COUNTER_TLV, 248 MODE_TLV, 249 RESPONSE_TLV, 250 TIMEOUT_TLV, 251 TLV_REQUEST_TLV, 252 VERSION_TLV 253 } <= set(p.mle.tlv.type) and\ 254 p.wpan.aux_sec.key_id_mode == 0x2 255 ).\ 256 must_next() 257 258 # Step 7: REED_1 sends an Address Solicit Request to Leader; 259 # Leader responds with an Address Solicit Response and REED_1 260 # becomes active router; 261 # REED_1 sends Child ID Response with DUT’s new 16-bit Address. 262 263 pkts.filter_wpan_src64(REED_1).\ 264 filter_ipv6_dst(LEADER_RLOC).\ 265 filter_coap_request(ADDR_SOL_URI).\ 266 filter(lambda p: { 267 NL_MAC_EXTENDED_ADDRESS_TLV, 268 NL_STATUS_TLV 269 } <= set(p.coap.tlv.type) 270 ).\ 271 must_next() 272 pkts.filter_wpan_src64(LEADER).\ 273 filter_ipv6_dst(REED_1_RLOC).\ 274 filter_coap_ack(ADDR_SOL_URI).\ 275 filter(lambda p: { 276 NL_STATUS_TLV, 277 NL_RLOC16_TLV, 278 NL_ROUTER_MASK_TLV 279 } == set(p.coap.tlv.type) and\ 280 p.thread_address.tlv.status == 0 281 ).\ 282 must_next() 283 284 pkts.filter_wpan_src64(REED_1).\ 285 filter_wpan_dst64(DUT).\ 286 filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\ 287 filter(lambda p: { 288 ADDRESS16_TLV, 289 LEADER_DATA_TLV, 290 NETWORK_DATA_TLV, 291 SOURCE_ADDRESS_TLV 292 } <= set(p.mle.tlv.type) and\ 293 p.mle.tlv.source_addr != REED_1_RLOC16 294 ).\ 295 must_next() 296 297 # Step 8: REED_1 verifies connectivity by sending an ICMPv6 Echo Request 298 # to the DUT link local address 299 # DUT responds with ICMPv6 Echo Reply 300 301 _pkt = pkts.filter_ping_request().\ 302 filter_ipv6_src_dst(REED_1_LLA, DUT_LLA).\ 303 must_next() 304 pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\ 305 filter_ipv6_src_dst(DUT_LLA, REED_1_LLA).\ 306 must_next() 307 308 309class Cert_6_1_4_REEDAttachConnectivity_ED(Cert_6_1_4_REEDAttachConnectivity_Base): 310 TOPOLOGY = copy.deepcopy(Cert_6_1_4_REEDAttachConnectivity_Base.TOPOLOGY) 311 TOPOLOGY[MTD]['mode'] = 'rn' 312 313 314class Cert_6_1_4_REEDAttachConnectivity_SED(Cert_6_1_4_REEDAttachConnectivity_Base): 315 TOPOLOGY = copy.deepcopy(Cert_6_1_4_REEDAttachConnectivity_Base.TOPOLOGY) 316 TOPOLOGY[MTD]['mode'] = '-' 317 318 319del (Cert_6_1_4_REEDAttachConnectivity_Base) 320 321if __name__ == '__main__': 322 unittest.main() 323