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_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 37from pktverify.packet_verifier import PacketVerifier 38from pktverify.null_field import nullField 39 40LEADER = 1 41ROUTER = 16 42DUT_REED = 17 43MED = 18 44REED_ADVERTISEMENT_INTERVAL = 570 45REED_ADVERTISEMENT_MAX_JITTER = 60 46ROUTER_SELECTION_JITTER = 1 47 48# Test Purpose and Description: 49# ----------------------------- 50# The purpose of this test case is to: 51# 1) Verify that the DUT does not attempt to become a router if there 52# are already 16 active routers on the Thread network AND it is not 53# bringing children 54# 2) Verify that the DUT transmits MLE Advertisement messages every 55# REED_ADVERTISEMENT_INTERVAL (+REED_ADVERTISEMENT_MAX_JITTER) seconds 56# 3) Verify that the DUT upgrades to a router by sending an Address Solicit 57# Request when a child attempts to attach to it. 58# 59# Test Topology: 60# ------------- 61# Router_15 - Leader 62# ... / \ 63# Router_n Router_1(DUT) 64# | 65# REED(DUT) 66# | 67# MED 68# 69# DUT Types: 70# ---------- 71# REED 72 73 74class Cert_5_2_4_REEDUpgrade(thread_cert.TestCase): 75 USE_MESSAGE_FACTORY = False 76 77 TOPOLOGY = { 78 LEADER: { 79 'name': 'LEADER', 80 'mode': 'rdn', 81 'allowlist': [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ROUTER] 82 }, 83 2: { 84 'name': 'ROUTER_1', 85 'mode': 'rdn', 86 'allowlist': [LEADER] 87 }, 88 3: { 89 'name': 'ROUTER_2', 90 'mode': 'rdn', 91 'allowlist': [LEADER] 92 }, 93 4: { 94 'name': 'ROUTER_3', 95 'mode': 'rdn', 96 'allowlist': [LEADER] 97 }, 98 5: { 99 'name': 'ROUTER_4', 100 'mode': 'rdn', 101 'allowlist': [LEADER] 102 }, 103 6: { 104 'name': 'ROUTER_5', 105 'mode': 'rdn', 106 'allowlist': [LEADER] 107 }, 108 7: { 109 'name': 'ROUTER_6', 110 'mode': 'rdn', 111 'allowlist': [LEADER] 112 }, 113 8: { 114 'name': 'ROUTER_7', 115 'mode': 'rdn', 116 'allowlist': [LEADER] 117 }, 118 9: { 119 'name': 'ROUTER_8', 120 'mode': 'rdn', 121 'allowlist': [LEADER] 122 }, 123 10: { 124 'name': 'ROUTER_9', 125 'mode': 'rdn', 126 'allowlist': [LEADER] 127 }, 128 11: { 129 'name': 'ROUTER_10', 130 'mode': 'rdn', 131 'allowlist': [LEADER] 132 }, 133 12: { 134 'name': 'ROUTER_11', 135 'mode': 'rdn', 136 'allowlist': [LEADER] 137 }, 138 13: { 139 'name': 'ROUTER_12', 140 'mode': 'rdn', 141 'allowlist': [LEADER] 142 }, 143 14: { 144 'name': 'ROUTER_13', 145 'mode': 'rdn', 146 'allowlist': [LEADER] 147 }, 148 15: { 149 'name': 'ROUTER_14', 150 'mode': 'rdn', 151 'allowlist': [LEADER] 152 }, 153 ROUTER: { 154 'name': 'ROUTER_15', 155 'mode': 'rdn', 156 'allowlist': [LEADER, DUT_REED] 157 }, 158 DUT_REED: { 159 'name': 'REED', 160 'mode': 'rdn', 161 'allowlist': [ROUTER, MED] 162 }, 163 MED: { 164 'name': 'MED', 165 'mode': 'rdn', 166 'allowlist': [DUT_REED] 167 }, 168 } 169 170 def test(self): 171 self.nodes[LEADER].start() 172 self.simulator.go(config.LEADER_STARTUP_DELAY) 173 self.assertEqual(self.nodes[LEADER].get_state(), 'leader') 174 175 for i in range(2, 17): 176 self.nodes[i].start() 177 self.simulator.go(config.ROUTER_STARTUP_DELAY) 178 self.assertEqual(self.nodes[i].get_state(), 'router') 179 180 self.nodes[DUT_REED].start() 181 self.simulator.go(config.ROUTER_STARTUP_DELAY) 182 self.simulator.go(ROUTER_SELECTION_JITTER) 183 184 self.collect_rloc16s() 185 self.collect_rlocs() 186 187 self.simulator.go(REED_ADVERTISEMENT_INTERVAL + REED_ADVERTISEMENT_MAX_JITTER) 188 189 self.nodes[MED].start() 190 self.simulator.go(config.ROUTER_STARTUP_DELAY) 191 self.collect_ipaddrs() 192 193 mleid = self.nodes[LEADER].get_ip6_address(config.ADDRESS_TYPE.ML_EID) 194 self.assertTrue(self.nodes[MED].ping(mleid)) 195 196 def verify(self, pv): 197 pkts = pv.pkts 198 pv.summary.show() 199 200 LEADER_RLOC16 = pv.vars['LEADER_RLOC16'] 201 LEADER_RLOC = pv.vars['LEADER_RLOC'] 202 LEADER_MLEID = pv.vars['LEADER_MLEID'] 203 REED = pv.vars['REED'] 204 REED_RLOC16 = pv.vars['REED_RLOC16'] 205 MED = pv.vars['MED'] 206 MED_RLOC16 = pv.vars['MED_RLOC16'] 207 MED_MLEID = pv.vars['MED_MLEID'] 208 209 # Step 1: Verify topology is formed correctly except REED. 210 211 with pkts.save_index(): 212 for i in range(1, 16): 213 pv.verify_attached('ROUTER_%d' % i) 214 215 # Step 2: REED attaches to the network with 2-hops from the Leader 216 # and MUST NOT attempt to become an active router by sending 217 # an Address Solicit Request 218 219 pv.verify_attached('REED') 220 lstart = pkts.index 221 222 # Step 3: REED MUST send properly formatted MLE Advertisements. 223 # MLE Advertisements MUST be sent with an IP Hop Limit of 224 # 255 to the Link-Local All Nodes multicast address (FF02::1). 225 # The following TLVs MUST be present in the MLE Advertisements: 226 # - Leader Data TLV 227 # - Source Address TLV 228 # The following TLV MUST NOT be present in the MLE Advertisements: 229 # - Route64 TLV 230 231 _pkt = pkts.filter_wpan_src64(REED).\ 232 filter_mle_advertisement('REED').\ 233 must_next() 234 lend = pkts.index 235 236 pkts.range(lstart, lend).filter_wpan_src64(REED).\ 237 filter_coap_request(ADDR_SOL_URI).\ 238 filter(lambda p: { 239 NL_MAC_EXTENDED_ADDRESS_TLV, 240 NL_STATUS_TLV 241 } <= set(p.coap.tlv.type) 242 ).\ 243 must_not_next() 244 245 # Step 5: REED MUST send a second MLE Advertisement after 246 # REED_ADVERTISEMENT_INTERVAL+JITTER where 247 # JITTER <= REED_ADVERTISEMENT_MAX_JITTER 248 249 pkts.filter_wpan_src64(REED).\ 250 filter_mle_advertisement('REED').\ 251 filter(lambda p: 252 REED_ADVERTISEMENT_INTERVAL < 253 p.sniff_timestamp - _pkt.sniff_timestamp <= 254 REED_ADVERTISEMENT_INTERVAL + 255 REED_ADVERTISEMENT_MAX_JITTER 256 ).\ 257 must_next() 258 259 # Step 6: MED sends multicast MLE Parent Request 260 # First one is to all routers, the second one is to 261 # all routers and reeds 262 263 pkts.filter_wpan_src64(MED).\ 264 filter_LLARMA().\ 265 filter_mle_cmd(MLE_PARENT_REQUEST).\ 266 filter(lambda p: { 267 CHALLENGE_TLV, 268 MODE_TLV, 269 SCAN_MASK_TLV, 270 VERSION_TLV 271 } <= set(p.mle.tlv.type) and\ 272 p.ipv6.hlim == 255 and\ 273 p.mle.tlv.scan_mask.r == 1 and\ 274 p.mle.tlv.scan_mask.e == 0).\ 275 must_next() 276 pkts.filter_wpan_src64(MED).\ 277 filter_LLARMA().\ 278 filter_mle_cmd(MLE_PARENT_REQUEST).\ 279 filter(lambda p: { 280 CHALLENGE_TLV, 281 MODE_TLV, 282 SCAN_MASK_TLV, 283 VERSION_TLV 284 } <= set(p.mle.tlv.type) and\ 285 p.ipv6.hlim == 255 and\ 286 p.mle.tlv.scan_mask.r == 1 and\ 287 p.mle.tlv.scan_mask.e == 1).\ 288 must_next() 289 290 # Step 7: REED MUST reply with a properly formatted MLE Parent Response 291 292 pkts.filter_wpan_src64(REED).\ 293 filter_wpan_dst64(MED).\ 294 filter_mle_cmd(MLE_PARENT_RESPONSE).\ 295 filter(lambda p: { 296 CHALLENGE_TLV, 297 CONNECTIVITY_TLV, 298 LEADER_DATA_TLV, 299 LINK_LAYER_FRAME_COUNTER_TLV, 300 LINK_MARGIN_TLV, 301 RESPONSE_TLV, 302 SOURCE_ADDRESS_TLV, 303 VERSION_TLV 304 } <= set(p.mle.tlv.type)).\ 305 must_next() 306 307 # Step 8: MED sends MLE Child ID Request to REED 308 309 _pkt = pkts.filter_wpan_src64(MED).\ 310 filter_wpan_dst64(REED).\ 311 filter_mle_cmd(MLE_CHILD_ID_REQUEST).\ 312 filter(lambda p: { 313 LINK_LAYER_FRAME_COUNTER_TLV, 314 MODE_TLV, 315 RESPONSE_TLV, 316 TIMEOUT_TLV, 317 TLV_REQUEST_TLV, 318 ADDRESS16_TLV, 319 NETWORK_DATA_TLV, 320 VERSION_TLV 321 } <= set(p.mle.tlv.type) and\ 322 p.mle.tlv.addr16 is nullField and\ 323 p.thread_nwd.tlv.type is nullField 324 ).\ 325 must_next() 326 _pkt.must_not_verify(lambda p: (ADDRESS_REGISTRATION_TLV) in p.mle.tlv.type) 327 328 # Step 9: REED sends an Address Solicit Request to the Leader 329 # Ensure the Address Solicit Request is properly formatted: 330 # CoAP Request URI 331 # coap://<leader address>:MM/a/as 332 # CoAP Payload 333 # - MAC Extended Address TLV 334 # - Status TLV 335 336 pkts.filter_wpan_src64(REED).\ 337 filter_ipv6_dst(LEADER_RLOC).\ 338 filter_coap_request(ADDR_SOL_URI).\ 339 filter(lambda p: { 340 NL_MAC_EXTENDED_ADDRESS_TLV, 341 NL_STATUS_TLV 342 } <= set(p.coap.tlv.type) 343 ).\ 344 must_next() 345 346 # Step 10: REED Sends a Link Request Message. 347 # This step is skipped due to change that new router no 348 # longer send multicast Link Request. 349 350 # Step 11: The REED MLE Child ID Response MUST be properly 351 # formatted with MED_1’s new 16-bit address. 352 353 with pkts.save_index(): 354 pkts.filter_wpan_src64(REED).\ 355 filter_wpan_dst64(MED).\ 356 filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\ 357 filter(lambda p: { 358 ADDRESS16_TLV, 359 LEADER_DATA_TLV, 360 NETWORK_DATA_TLV, 361 SOURCE_ADDRESS_TLV, 362 ROUTE64_TLV 363 } <= set(p.mle.tlv.type) or\ 364 { 365 ADDRESS16_TLV, 366 LEADER_DATA_TLV, 367 NETWORK_DATA_TLV, 368 SOURCE_ADDRESS_TLV 369 } <= set(p.mle.tlv.type) and\ 370 p.mle.tlv.source_addr != REED_RLOC16 and\ 371 p.mle.tlv.addr16 != MED_RLOC16 372 ).\ 373 must_next() 374 375 # Step 12: The Leader MUST respond with an ICMPv6 Echo Reply 376 377 _pkt = pkts.filter_ipv6_src_dst(MED_MLEID, LEADER_MLEID).\ 378 filter_ping_request().\ 379 must_next() 380 pkts.filter_ipv6_src_dst(LEADER_MLEID, MED_MLEID).\ 381 filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\ 382 must_next() 383 384 385if __name__ == '__main__': 386 unittest.main() 387