1#!/usr/bin/env python3 2# 3# Copyright (c) 2018, 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 command 33import config 34import mle 35import thread_cert 36from 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 37from pktverify.packet_verifier import PacketVerifier 38from pktverify.null_field import nullField 39 40LEADER = 1 41DUT_ROUTER1 = 2 42REED1 = 3 43MED1 = 4 44 45# Test Purpose and Description: 46# ----------------------------- 47# The purpose of this test case is to show that the DUT is able to attach 48# a REED and forward address solicits two hops away from the Leader. 49# 50# Test Topology: 51# ------------- 52# Leader 53# | 54# Router_1 55# | 56# REED_1 57# | 58# MED_1 59# 60# DUT Types: 61# ---------- 62# Router 63 64 65class Cert_5_2_01_REEDAttach(thread_cert.TestCase): 66 USE_MESSAGE_FACTORY = False 67 68 TOPOLOGY = { 69 LEADER: { 70 'name': 'LEADER', 71 'mode': 'rdn', 72 'allowlist': [DUT_ROUTER1] 73 }, 74 DUT_ROUTER1: { 75 'name': 'ROUTER_1', 76 'mode': 'rdn', 77 'allowlist': [LEADER, REED1] 78 }, 79 REED1: { 80 'name': 'REED_1', 81 'mode': 'rdn', 82 'router_upgrade_threshold': 1, 83 'allowlist': [DUT_ROUTER1, MED1] 84 }, 85 MED1: { 86 'name': 'MED_1', 87 'is_mtd': True, 88 'mode': 'rn', 89 'allowlist': [REED1] 90 }, 91 } 92 93 def test(self): 94 self.nodes[LEADER].start() 95 self.simulator.go(config.LEADER_STARTUP_DELAY) 96 self.assertEqual(self.nodes[LEADER].get_state(), 'leader') 97 98 self.nodes[DUT_ROUTER1].start() 99 self.simulator.go(config.ROUTER_STARTUP_DELAY) 100 self.assertEqual(self.nodes[DUT_ROUTER1].get_state(), 'router') 101 102 self.nodes[REED1].start() 103 self.simulator.go(config.ROUTER_STARTUP_DELAY) 104 self.assertEqual(self.nodes[REED1].get_state(), 'child') 105 106 self.nodes[MED1].start() 107 self.simulator.go(5) 108 self.assertEqual(self.nodes[MED1].get_state(), 'child') 109 110 self.collect_rloc16s() 111 self.collect_ipaddrs() 112 113 self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL) 114 115 reed_mleid = self.nodes[REED1].get_ip6_address(config.ADDRESS_TYPE.ML_EID) 116 self.assertTrue(self.nodes[LEADER].ping(reed_mleid)) 117 118 def verify(self, pv): 119 pkts = pv.pkts 120 pv.summary.show() 121 122 LEADER = pv.vars['LEADER'] 123 LEADER_MLEID = pv.vars['LEADER_MLEID'] 124 LEADER_RLOC16 = pv.vars['LEADER_RLOC16'] 125 ROUTER_1 = pv.vars['ROUTER_1'] 126 ROUTER_1_RLOC16 = pv.vars['ROUTER_1_RLOC16'] 127 REED_1 = pv.vars['REED_1'] 128 REED_1_MLEID = pv.vars['REED_1_MLEID'] 129 130 # Step 1: Router_1 attaches to Leader and sends properly formatted MLE 131 # advertisements 132 # Advertisements MUST be sent with an IP hop limit of 255 to 133 # the Link-Local All Nodes multicast address (FF02::1). 134 # The following TLVs MUST be present in the MLE Advertisements: 135 # - Leader Data TLV 136 # - Route64 TLV 137 # - Source Address TLV 138 139 pv.verify_attached('ROUTER_1') 140 pkts.filter_wpan_src64(ROUTER_1).\ 141 filter_LLANMA().\ 142 filter_mle_cmd(MLE_ADVERTISEMENT).\ 143 filter(lambda p: { 144 LEADER_DATA_TLV, 145 ROUTE64_TLV, 146 SOURCE_ADDRESS_TLV 147 } == set(p.mle.tlv.type) and\ 148 p.ipv6.hlim == 255 149 ).\ 150 must_next() 151 152 # Step 2: Attach REED_1 to Router_1; REED_1 sends MLE Parent Request with 153 # an IP hop limit of 255 to the Link-Local All Routers multicast 154 # address (FF02::2). 155 # The following TLVs MUST be present in the MLE Parent Request: 156 # - Challenge TLV 157 # - Mode TLV 158 # - Scan Mask TLV 159 # If the DUT sends multiple MLE Parent Requests 160 # - The first one MUST be sent only to all Routers 161 # - Subsequent ones MAY be sent to all Routers and REEDS 162 # - Version TLV 163 164 pkts.filter_wpan_src64(REED_1).\ 165 filter_LLARMA().\ 166 filter_mle_cmd(MLE_PARENT_REQUEST).\ 167 filter(lambda p: { 168 CHALLENGE_TLV, 169 MODE_TLV, 170 SCAN_MASK_TLV, 171 VERSION_TLV 172 } <= set(p.mle.tlv.type) and\ 173 p.ipv6.hlim == 255 and\ 174 p.mle.tlv.scan_mask.r == 1 and\ 175 p.mle.tlv.scan_mask.e == 0).\ 176 must_next() 177 178 # Step 3: Router_1 must respond with a MLE Parent Response. 179 # The following TLVs MUST be present in the MLE Parent Response: 180 # - Challenge TLV 181 # - Connectivity TLV 182 # - Leader Data TLV 183 # - Link-layer Frame Counter TLV 184 # - Link Margin TLV 185 # - Response TLV 186 # - Source Address 187 # - Version TLV 188 # - MLE Frame Counter TLV (optional) 189 190 pkts.filter_wpan_src64(ROUTER_1).\ 191 filter_wpan_dst64(REED_1).\ 192 filter_mle_cmd(MLE_PARENT_RESPONSE).\ 193 filter(lambda p: { 194 CHALLENGE_TLV, 195 CONNECTIVITY_TLV, 196 LEADER_DATA_TLV, 197 LINK_LAYER_FRAME_COUNTER_TLV, 198 LINK_MARGIN_TLV, 199 RESPONSE_TLV, 200 SOURCE_ADDRESS_TLV, 201 VERSION_TLV 202 } <= set(p.mle.tlv.type)).\ 203 must_next() 204 205 # Step 4: Router_1 must respond with a Child ID Response. 206 # The following TLVs MUST be present in the Child ID Response: 207 # - Address16 TLV 208 # - Leader Data TLV 209 # - Network Data TLV 210 # - Source Address TLV 211 # - Route64 TLV (if requested) 212 213 pkts.filter_wpan_src64(ROUTER_1).\ 214 filter_wpan_dst64(REED_1).\ 215 filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\ 216 filter(lambda p: { 217 ADDRESS16_TLV, 218 LEADER_DATA_TLV, 219 NETWORK_DATA_TLV, 220 SOURCE_ADDRESS_TLV, 221 ROUTE64_TLV 222 } <= set(p.mle.tlv.type) or\ 223 { 224 ADDRESS16_TLV, 225 LEADER_DATA_TLV, 226 NETWORK_DATA_TLV, 227 SOURCE_ADDRESS_TLV 228 } <= set(p.mle.tlv.type) 229 ).\ 230 must_next() 231 232 # Step 7: REED_1 sends an Address Solicit Request to Router_1. 233 # Ensure the Address Solicit Request is properly formatted: 234 # CoAP Request URI 235 # coap://<leader address>:MM/a/as 236 # CoAP Payload 237 # - MAC Extended Address TLV 238 # - Status TLV 239 240 _pkt1 = pkts.filter_wpan_src64(REED_1).\ 241 filter_wpan_dst16(ROUTER_1_RLOC16).\ 242 filter_coap_request(ADDR_SOL_URI).\ 243 filter(lambda p: { 244 NL_MAC_EXTENDED_ADDRESS_TLV, 245 NL_STATUS_TLV 246 } == set(p.coap.tlv.type)\ 247 ).\ 248 must_next() 249 250 # Step 8: Router_1 forward the REED_1's Address Solicit Request to 251 # Leader and Leader's Address Solicit Response to REED_1. 252 # Ensure the Address Solicit Response is properly formatted: 253 # CoAP Response Code 254 # 2.04 Changed 255 # CoAP Payload 256 # - Status TLV (value = Success) 257 # - RLOC16 TLV 258 # - Router Mask TLV 259 260 _pkt2 = pkts.filter_wpan_src64(ROUTER_1).\ 261 filter_wpan_dst16(LEADER_RLOC16).\ 262 filter_coap_request(ADDR_SOL_URI).\ 263 filter(lambda p: { 264 NL_MAC_EXTENDED_ADDRESS_TLV, 265 NL_STATUS_TLV 266 } == set(p.coap.tlv.type)\ 267 ).\ 268 must_next() 269 270 pkts.filter_wpan_src64(LEADER).\ 271 filter_wpan_dst16(_pkt2.wpan.src16).\ 272 filter_coap_ack(ADDR_SOL_URI).\ 273 filter(lambda p: { 274 NL_STATUS_TLV, 275 NL_RLOC16_TLV, 276 NL_ROUTER_MASK_TLV 277 } == set(p.coap.tlv.type) and\ 278 p.coap.code == COAP_CODE_ACK and\ 279 p.thread_address.tlv.status == 0\ 280 ).\ 281 must_next() 282 pkts.filter_wpan_src64(ROUTER_1).\ 283 filter_wpan_dst16(_pkt1.wpan.src16).\ 284 filter_coap_ack(ADDR_SOL_URI).\ 285 filter(lambda p: { 286 NL_STATUS_TLV, 287 NL_RLOC16_TLV, 288 NL_ROUTER_MASK_TLV 289 } == set(p.coap.tlv.type) and\ 290 p.coap.code == COAP_CODE_ACK and\ 291 p.thread_address.tlv.status == 0\ 292 ).\ 293 must_next() 294 295 # Step 9: REED_1 responds with ICMPv6 Echo Reply 296 _pkt = pkts.filter_ipv6_src_dst(LEADER_MLEID, REED_1_MLEID).\ 297 filter_ping_request().\ 298 must_next() 299 pkts.filter_ipv6_src_dst(REED_1_MLEID, LEADER_MLEID).\ 300 filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\ 301 must_next() 302 303 304if __name__ == '__main__': 305 unittest.main() 306