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 command 33import config 34import thread_cert 35from pktverify.consts import WIRESHARK_OVERRIDE_PREFS, MLE_PARENT_REQUEST, MLE_PARENT_RESPONSE, MLE_CHILD_ID_REQUEST, MLE_CHILD_ID_RESPONSE, MLE_LINK_REQUEST, ADDR_SOL_URI, ADDR_NTF_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_TARGET_EID_TLV, NL_ML_EID_TLV, COAP_CODE_ACK 36from pktverify.packet_verifier import PacketVerifier 37from pktverify.null_field import nullField 38from pktverify.bytes import Bytes 39 40LEADER = 1 41ROUTER1 = 2 42BR = 3 43MED = 17 44DUT_REED = 18 45ROUTER_SELECTION_JITTER = 1 46 47# Test Purpose and Description: 48# ----------------------------- 49# The purpose of this test case is to validate that the DUT is able to generate 50# Address Notification messages in response to Address Query messages. 51# 52# - Build a topology that has a total of 16 active routers, including the Leader, 53# with no communication constraints and 54# - MED only allows Leader 55# - DUT only allows Router1 56# - DUT allows BR later as required in step 5. 57# - The Leader is configured as a DHCPv6 server for prefix 2001:: 58# - The Border Router is configured as a SLAAC server for prefix 2002:: 59# 60# Test Topology: 61# ------------- 62# MED 63# | 64# Router_15 - Leader 65# ... / \ 66# Router_2 Router_1 67# [BR] | 68# REED(DUT) 69# 70# DUT Types: 71# ---------- 72# REED 73 74 75class Cert_5_2_5_AddressQuery(thread_cert.TestCase): 76 USE_MESSAGE_FACTORY = False 77 78 TOPOLOGY = { 79 LEADER: { 80 'name': 'LEADER', 81 'mode': 'rdn', 82 'allowlist': [ROUTER1, BR, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, MED] 83 }, 84 ROUTER1: { 85 'name': 'ROUTER_1', 86 'mode': 'rdn', 87 'allowlist': [LEADER, DUT_REED] 88 }, 89 BR: { 90 'name': 'ROUTER_2', 91 'mode': 'rdn', 92 'allowlist': [LEADER] 93 }, 94 4: { 95 'name': 'ROUTER_3', 96 'mode': 'rdn', 97 'allowlist': [LEADER] 98 }, 99 5: { 100 'name': 'ROUTER_4', 101 'mode': 'rdn', 102 'allowlist': [LEADER] 103 }, 104 6: { 105 'name': 'ROUTER_5', 106 'mode': 'rdn', 107 'allowlist': [LEADER] 108 }, 109 7: { 110 'name': 'ROUTER_6', 111 'mode': 'rdn', 112 'allowlist': [LEADER] 113 }, 114 8: { 115 'name': 'ROUTER_7', 116 'mode': 'rdn', 117 'allowlist': [LEADER] 118 }, 119 9: { 120 'name': 'ROUTER_8', 121 'mode': 'rdn', 122 'allowlist': [LEADER] 123 }, 124 10: { 125 'name': 'ROUTER_9', 126 'mode': 'rdn', 127 'allowlist': [LEADER] 128 }, 129 11: { 130 'name': 'ROUTER_10', 131 'mode': 'rdn', 132 'allowlist': [LEADER] 133 }, 134 12: { 135 'name': 'ROUTER_11', 136 'mode': 'rdn', 137 'allowlist': [LEADER] 138 }, 139 13: { 140 'name': 'ROUTER_12', 141 'mode': 'rdn', 142 'allowlist': [LEADER] 143 }, 144 14: { 145 'name': 'ROUTER_13', 146 'mode': 'rdn', 147 'allowlist': [LEADER] 148 }, 149 15: { 150 'name': 'ROUTER_14', 151 'mode': 'rdn', 152 'allowlist': [LEADER] 153 }, 154 16: { 155 'name': 'ROUTER_15', 156 'mode': 'rdn', 157 'allowlist': [LEADER] 158 }, 159 MED: { 160 'name': 'MED', 161 'is_mtd': True, 162 'mode': 'rn', 163 'allowlist': [LEADER] 164 }, 165 DUT_REED: { 166 'name': 'REED', 167 'mode': 'rdn', 168 'allowlist': [ROUTER1] 169 }, 170 } 171 172 # override wireshark preferences with case needed parameters 173 CASE_WIRESHARK_PREFS = WIRESHARK_OVERRIDE_PREFS 174 CASE_WIRESHARK_PREFS['6lowpan.context1'] = '2001::/64' 175 CASE_WIRESHARK_PREFS['6lowpan.context2'] = '2002::/64' 176 177 def test(self): 178 # 1. LEADER: DHCPv6 Server for prefix 2001::/64. 179 self.nodes[LEADER].start() 180 self.simulator.go(5) 181 self.assertEqual(self.nodes[LEADER].get_state(), 'leader') 182 self.nodes[LEADER].add_prefix('2001::/64', 'pdros') 183 self.nodes[LEADER].register_netdata() 184 185 # 2. BR: SLAAC Server for prefix 2002::/64. 186 self.nodes[BR].start() 187 self.simulator.go(5) 188 self.assertEqual(self.nodes[BR].get_state(), 'router') 189 self.nodes[BR].add_prefix('2002::/64', 'paros') 190 self.nodes[BR].register_netdata() 191 192 # 3. Bring up remaining devices except DUT_REED. 193 for i in range(2, 17): 194 if i == BR: 195 continue 196 self.nodes[i].start() 197 self.simulator.go(5) 198 self.assertEqual(self.nodes[i].get_state(), 'router') 199 200 self.nodes[MED].start() 201 self.simulator.go(5) 202 self.assertEqual(self.nodes[MED].get_state(), 'child') 203 204 # 4. Bring up DUT_REED. 205 self.nodes[DUT_REED].start() 206 self.simulator.go(5) 207 self.simulator.go(ROUTER_SELECTION_JITTER) 208 209 # 5. Enable a link between the DUT and BR to create a one-way link. 210 self.nodes[DUT_REED].add_allowlist(self.nodes[BR].get_addr64()) 211 self.nodes[BR].add_allowlist(self.nodes[DUT_REED].get_addr64()) 212 213 self.collect_ipaddrs() 214 self.collect_rlocs() 215 216 # 6. Verify DUT_REED would send Address Notification when ping to its 217 # ML-EID. 218 mleid = self.nodes[DUT_REED].get_ip6_address(config.ADDRESS_TYPE.ML_EID) 219 self.assertTrue(self.nodes[MED].ping(mleid)) 220 221 # Wait for sniffer collecting packets 222 self.simulator.go(1) 223 224 # 7 & 8. Verify DUT_REED would send Address Notification when ping to 225 # its 2001::EID and 2002::EID. 226 flag2001 = 0 227 flag2002 = 0 228 for global_address in self.nodes[DUT_REED].get_ip6_address(config.ADDRESS_TYPE.GLOBAL): 229 if global_address[0:4] == '2001': 230 flag2001 += 1 231 elif global_address[0:4] == '2002': 232 flag2002 += 1 233 else: 234 raise "Error: Address is unexpected." 235 self.assertTrue(self.nodes[MED].ping(global_address)) 236 237 # Wait for sniffer collecting packets 238 self.simulator.go(1) 239 240 def verify(self, pv): 241 pkts = pv.pkts 242 pv.summary.show() 243 244 LEADER_RLOC = pv.vars['LEADER_RLOC'] 245 LEADER_MLEID = pv.vars['LEADER_MLEID'] 246 ROUTER_1 = pv.vars['ROUTER_1'] 247 REED = pv.vars['REED'] 248 REED_MLEID = pv.vars['REED_MLEID'] 249 REED_RLOC = pv.vars['REED_RLOC'] 250 MED = pv.vars['MED'] 251 MED_MLEID = pv.vars['MED_MLEID'] 252 MM = pv.vars['MM_PORT'] 253 REED2001 = '' 254 REED2002 = '' 255 MED2001 = '' 256 MED2002 = '' 257 258 for addr in pv.vars['REED_IPADDRS']: 259 if addr.startswith(Bytes('2001')): 260 REED2001 = addr 261 if addr.startswith(Bytes('2002')): 262 REED2002 = addr 263 264 for addr in pv.vars['MED_IPADDRS']: 265 if addr.startswith(Bytes('2001')): 266 MED2001 = addr 267 if addr.startswith(Bytes('2002')): 268 MED2002 = addr 269 270 # Step 3: Verify topology is formed correctly except REED. 271 272 for i in range(1, 16): 273 with pkts.save_index(): 274 pv.verify_attached('ROUTER_%d' % i) 275 276 # Step 4: REED attaches to Router_1 and MUST NOT attempt to become 277 # an active router by sending an Address Solicit Request 278 279 pv.verify_attached('REED', 'ROUTER_1') 280 pkts.filter_wpan_src64(REED).\ 281 filter_coap_request(ADDR_SOL_URI).\ 282 must_not_next() 283 284 # Step 6: MED sends an ICMPv6 Echo Request from MED to REED using ML-EID. 285 # The DUT MUST send a properly formatted Address Notification message: 286 # CoAP Request URI-PATH 287 # CON POST coap://[<Address Query Source>]:MM/a/an 288 # CoAP Payload 289 # - Target EID TLV 290 # - RLOC16 TLV 291 # - ML-EID TLV 292 # The IPv6 Source address MUST be the RLOC of the originator 293 # The IPv6 Destination address MUST be the RLOC of the destination 294 # The DUT MUST send an ICMPv6 Echo Reply 295 296 _pkt = pkts.filter_ipv6_src_dst(MED_MLEID, REED_MLEID).\ 297 filter_ping_request().\ 298 must_next() 299 pkts.filter_ipv6_src_dst(REED_RLOC, LEADER_RLOC).\ 300 filter_coap_request(ADDR_NTF_URI, port=MM).\ 301 filter(lambda p: { 302 NL_TARGET_EID_TLV, 303 NL_RLOC16_TLV, 304 NL_ML_EID_TLV 305 } == set(p.thread_address.tlv.type) 306 ).\ 307 must_next() 308 pkts.filter_ipv6_src_dst(REED_MLEID, MED_MLEID).\ 309 filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\ 310 must_next() 311 312 # Step 7: MED sends an ICMPv6 Echo Request from MED to REED using 2001::EID. 313 # The DUT MUST send a properly formatted Address Notification message: 314 # CoAP Request URI-PATH 315 # CON POST coap://[<Address Query Source>]:MM/a/an 316 # CoAP Payload 317 # - Target EID TLV 318 # - RLOC16 TLV 319 # - ML-EID TLV 320 # The IPv6 Source address MUST be the RLOC of the originator 321 # The IPv6 Destination address MUST be the RLOC of the destination 322 # The DUT MUST send an ICMPv6 Echo Reply 323 324 _pkt = pkts.filter_ipv6_src_dst(MED2001, REED2001).\ 325 filter_ping_request().\ 326 must_next() 327 pkts.filter_ipv6_src_dst(REED_RLOC, LEADER_RLOC).\ 328 filter_coap_request(ADDR_NTF_URI, port=MM).\ 329 filter(lambda p: { 330 NL_TARGET_EID_TLV, 331 NL_RLOC16_TLV, 332 NL_ML_EID_TLV 333 } == set(p.thread_address.tlv.type) 334 ).\ 335 must_next() 336 pkts.filter_ipv6_src_dst(REED2001, MED2001).\ 337 filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\ 338 must_next() 339 340 # Step 8: MED sends an ICMPv6 Echo Request from MED to REED using 2002::EID. 341 # The DUT MUST send a properly formatted Address Notification message: 342 # CoAP Request URI-PATH 343 # CON POST coap://[<Address Query Source>]:MM/a/an 344 # CoAP Payload 345 # - Target EID TLV 346 # - RLOC16 TLV 347 # - ML-EID TLV 348 # The IPv6 Source address MUST be the RLOC of the originator 349 # The IPv6 Destination address MUST be the RLOC of the destination 350 # The DUT MUST send an ICMPv6 Echo Reply 351 352 _pkt = pkts.filter_ipv6_src_dst(MED2002, REED2002).\ 353 filter_ping_request().\ 354 must_next() 355 pkts.filter_ipv6_src_dst(REED_RLOC, LEADER_RLOC).\ 356 filter_coap_request(ADDR_NTF_URI, port=MM).\ 357 filter(lambda p: { 358 NL_TARGET_EID_TLV, 359 NL_RLOC16_TLV, 360 NL_ML_EID_TLV 361 } == set(p.thread_address.tlv.type) 362 ).\ 363 must_next() 364 pkts.filter_ipv6_src_dst(REED2002, MED2002).\ 365 filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\ 366 must_next() 367 368 369if __name__ == '__main__': 370 unittest.main() 371