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 copy 31import unittest 32 33import command 34import config 35import copy 36import ipv6 37import thread_cert 38from pktverify.consts import WIRESHARK_OVERRIDE_PREFS, ADDR_QRY_URI, ADDR_NTF_URI, NL_ML_EID_TLV, NL_RLOC16_TLV, NL_TARGET_EID_TLV 39from pktverify.packet_verifier import PacketVerifier 40from pktverify.bytes import Bytes 41from pktverify.addrs import Ipv6Addr 42 43LEADER = 1 44BR = 2 45ROUTER1 = 3 46DUT_ROUTER2 = 4 47MED1 = 5 48 49PREFIX_1 = '2003::/64' 50GUA_1_START = '2003' 51PREFIX_2 = '2004::/64' 52 53# Test Purpose and Description: 54# ----------------------------- 55# The purpose of this test case is to validate that the DUT is able to generate 56# Address Query and Address Notification messages 57# The Border Router is configured as a SLAAC server for prefixes 2003:: & 2004:: 58# 59# Test Topology: 60# ------------- 61# BorderRouter - Leader 62# / \ 63# Router_1 - Router_2(DUT) 64# | 65# MED 66# 67# DUT Types: 68# ---------- 69# Router 70 71 72class Cert_5_3_10_AddressQuery(thread_cert.TestCase): 73 USE_MESSAGE_FACTORY = False 74 SUPPORT_NCP = False 75 76 TOPOLOGY = { 77 LEADER: { 78 'name': 'LEADER', 79 'mode': 'rdn', 80 'allowlist': [BR, ROUTER1, DUT_ROUTER2] 81 }, 82 BR: { 83 'name': 'BR', 84 'mode': 'rdn', 85 'allowlist': [LEADER] 86 }, 87 ROUTER1: { 88 'name': 'ROUTER_1', 89 'mode': 'rdn', 90 'allowlist': [LEADER, DUT_ROUTER2] 91 }, 92 DUT_ROUTER2: { 93 'name': 'ROUTER_2', 94 'mode': 'rdn', 95 'allowlist': [LEADER, ROUTER1, MED1] 96 }, 97 MED1: { 98 'name': 'MED', 99 'is_mtd': True, 100 'mode': 'rn', 101 'allowlist': [DUT_ROUTER2] 102 }, 103 } 104 # override wireshark preferences with case needed parameters 105 CASE_WIRESHARK_PREFS = copy.deepcopy(WIRESHARK_OVERRIDE_PREFS) 106 CASE_WIRESHARK_PREFS['6lowpan.context1'] = PREFIX_1 107 CASE_WIRESHARK_PREFS['6lowpan.context2'] = PREFIX_2 108 109 def test(self): 110 # 1 & 2 111 self.nodes[LEADER].start() 112 self.simulator.go(config.LEADER_STARTUP_DELAY) 113 self.assertEqual(self.nodes[LEADER].get_state(), 'leader') 114 115 self.nodes[BR].start() 116 self.simulator.go(config.ROUTER_STARTUP_DELAY) 117 self.assertEqual(self.nodes[BR].get_state(), 'router') 118 119 # Configure two On-Mesh Prefixes on the BR 120 self.nodes[BR].add_prefix(PREFIX_1, 'paros') 121 self.nodes[BR].add_prefix(PREFIX_2, 'paros') 122 self.nodes[BR].register_netdata() 123 124 self.nodes[DUT_ROUTER2].start() 125 self.simulator.go(config.ROUTER_STARTUP_DELAY) 126 self.assertEqual(self.nodes[DUT_ROUTER2].get_state(), 'router') 127 128 self.nodes[ROUTER1].start() 129 self.simulator.go(config.ROUTER_STARTUP_DELAY) 130 self.assertEqual(self.nodes[ROUTER1].get_state(), 'router') 131 132 self.nodes[MED1].start() 133 self.simulator.go(5) 134 self.assertEqual(self.nodes[MED1].get_state(), 'child') 135 136 self.collect_rlocs() 137 self.collect_rloc16s() 138 self.collect_ipaddrs() 139 140 # 3 MED1: MED1 sends an ICMPv6 Echo Request to Router1 using GUA 141 # PREFIX_1 address 142 router1_addr = self.nodes[ROUTER1].get_addr(PREFIX_1) 143 self.assertTrue(router1_addr is not None) 144 self.assertTrue(self.nodes[MED1].ping(router1_addr)) 145 self.simulator.go(1) 146 147 # 4 BR: BR sends an ICMPv6 Echo Request to MED1 using GUA PREFIX_1 148 # address 149 med1_addr = self.nodes[MED1].get_addr(PREFIX_1) 150 self.assertTrue(med1_addr is not None) 151 self.assertTrue(self.nodes[BR].ping(med1_addr)) 152 self.simulator.go(1) 153 154 # 5 MED1: MED1 sends an ICMPv6 Echo Request to ROUTER1 using GUA PREFIX_1 155 # address 156 self.assertTrue(self.nodes[MED1].ping(router1_addr)) 157 self.simulator.go(1) 158 159 # 6 DUT_ROUTER2: Power off ROUTER1 and wait 580 seconds to allow the 160 # LEADER to expire its Router ID 161 router1_id = self.nodes[ROUTER1].get_router_id() 162 self.nodes[ROUTER1].stop() 163 self.simulator.go(580) 164 165 # Send an ICMPv6 Echo Request from MED1 to ROUTER1 GUA PREFIX_1 address 166 self.assertFalse(self.nodes[MED1].ping(router1_addr)) 167 self.simulator.go(1) 168 169 # 7 MED1: Power off MED1 and wait to allow DUT_ROUTER2 to timeout the 170 # child 171 self.nodes[MED1].stop() 172 self.simulator.go(config.MLE_END_DEVICE_TIMEOUT) 173 174 # BR sends two ICMPv6 Echo Requests to MED1 GUA PREFIX_1 address 175 self.assertFalse(self.nodes[BR].ping(med1_addr)) 176 self.assertFalse(self.nodes[BR].ping(med1_addr)) 177 178 def verify(self, pv): 179 pkts = pv.pkts 180 pv.summary.show() 181 182 LEADER = pv.vars['LEADER'] 183 ROUTER_1 = pv.vars['ROUTER_1'] 184 ROUTER_2 = pv.vars['ROUTER_2'] 185 ROUTER_2_RLOC = pv.vars['ROUTER_2_RLOC'] 186 ROUTER_2_RLOC16 = pv.vars['ROUTER_2_RLOC16'] 187 BR = pv.vars['BR'] 188 BR_RLOC = pv.vars['BR_RLOC'] 189 MED = pv.vars['MED'] 190 MED_RLOC16 = pv.vars['MED_RLOC16'] 191 MM = pv.vars['MM_PORT'] 192 GUA1 = {} 193 194 for node in ('ROUTER_1', 'BR', 'MED'): 195 for addr in pv.vars['%s_IPADDRS' % node]: 196 if addr.startswith(Bytes(GUA_1_START)): 197 GUA1[node] = addr 198 199 # Step 2: Build the topology as described 200 201 pv.verify_attached('BR', 'LEADER') 202 for i in (2, 1): 203 pv.verify_attached('ROUTER_%d' % i, 'LEADER') 204 pv.verify_attached('MED', 'ROUTER_2', 'MTD') 205 206 # Step 3: MED sends an ICMPv6 Echo Request to Router_1 using GUA 2003:: 207 # address 208 # The DUT MUST generate an Address Query Request on MED’s behalf 209 # to find each node’s RLOC. 210 # The Address Query Request MUST be sent to the Realm-Local 211 # All-Routers address (FF03::2) 212 # CoAP URI-Path 213 # - NON POST coap://<FF03::2> 214 # CoAP Payload 215 # - Target EID TLV 216 # The DUT MUST receive and process the incoming Address Query 217 # Response and forward the ICMPv6 Echo Request packet to Router_1 218 219 _pkt = pkts.filter_ping_request().\ 220 filter_wpan_src64(MED).\ 221 filter_ipv6_dst(GUA1['ROUTER_1']).\ 222 must_next() 223 pkts.filter_wpan_src64(ROUTER_2).\ 224 filter_RLARMA().\ 225 filter_coap_request(ADDR_QRY_URI, port=MM).\ 226 filter(lambda p: p.thread_address.tlv.target_eid == GUA1['ROUTER_1']).\ 227 must_next() 228 pkts.filter_ping_request(identifier=_pkt.icmpv6.echo.identifier).\ 229 filter_wpan_src64(ROUTER_2).\ 230 filter_ipv6_dst(GUA1['ROUTER_1']).\ 231 must_next() 232 pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\ 233 filter_wpan_src64(ROUTER_1).\ 234 filter_ipv6_dst(GUA1['MED']).\ 235 must_next() 236 pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\ 237 filter_wpan_src64(ROUTER_2).\ 238 filter_wpan_dst16(MED_RLOC16).\ 239 must_next() 240 241 # Step 4: Border Router sends an ICMPv6 Echo Request to MED using GUA 2003:: 242 # address 243 # The DUT MUST respond to the Address Query Request with a properly 244 # formatted Address Notification Message: 245 # CoAP URI-Path 246 # - CON POST coap://[<Address Query Source>]:MM/a/an 247 # CoAP Payload 248 # - ML-EID TLV 249 # - RLOC16 TLV 250 # - Target EID TLV 251 # The IPv6 Source address MUST be the RLOC of the originator 252 # The IPv6 Destination address MUST be the RLOC of the destination 253 254 pkts.filter_wpan_src64(BR).\ 255 filter_RLARMA().\ 256 filter_coap_request(ADDR_QRY_URI, port=MM).\ 257 filter(lambda p: p.thread_address.tlv.target_eid == GUA1['MED']).\ 258 must_next() 259 pkts.filter_ipv6_src_dst(ROUTER_2_RLOC, BR_RLOC).\ 260 filter_coap_request(ADDR_NTF_URI, port=MM).\ 261 filter(lambda p: { 262 NL_ML_EID_TLV, 263 NL_RLOC16_TLV, 264 NL_TARGET_EID_TLV 265 } <= set(p.coap.tlv.type) and\ 266 p.thread_address.tlv.target_eid == GUA1['MED'] and\ 267 p.thread_address.tlv.rloc16 == ROUTER_2_RLOC16 268 ).\ 269 must_next() 270 _pkt = pkts.filter_ping_request().\ 271 filter_wpan_src64(BR).\ 272 filter_ipv6_dst(GUA1['MED']).\ 273 must_next() 274 pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\ 275 filter_wpan_src64(MED).\ 276 filter_ipv6_dst(GUA1['BR']).\ 277 must_next() 278 279 # Step 5: MED sends an ICMPv6 Echo Request to Router_1 using GUA 2003:: 280 # address 281 # The DUT MUST not send an Address Query as Router_1 address should 282 # be cached. 283 # The DUT MUST forward the ICMPv6 Echo Reply to MED 284 285 _pkt = pkts.filter_ping_request().\ 286 filter_wpan_src64(MED).\ 287 filter_ipv6_dst(GUA1['ROUTER_1']).\ 288 must_next() 289 lstart = pkts.index 290 pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\ 291 filter_wpan_src64(ROUTER_1).\ 292 filter_ipv6_dst(GUA1['MED']).\ 293 must_next() 294 pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\ 295 filter_wpan_src64(ROUTER_2).\ 296 filter_wpan_dst16(MED_RLOC16).\ 297 must_next() 298 lend = pkts.index 299 pkts.range(lstart, lend).filter_wpan_src64(ROUTER_2).\ 300 filter_RLARMA().\ 301 filter_coap_request(ADDR_QRY_URI, port=MM).\ 302 must_not_next() 303 304 # Step 6: MED sends an ICMPv6 Echo Request to Router_1 using GUA 2003:: 305 # address 306 # The DUT MUST update its address cache and remove all entries 307 # based on Router_1’s Router ID. 308 # The DUT MUST send an Address Query to discover Router_1’s RLOC address. 309 310 pkts.filter_ping_request().\ 311 filter_wpan_src64(MED).\ 312 filter_ipv6_dst(GUA1['ROUTER_1']).\ 313 must_next() 314 pkts.filter_wpan_src64(ROUTER_2).\ 315 filter_RLARMA().\ 316 filter_coap_request(ADDR_QRY_URI, port=MM).\ 317 filter(lambda p: p.thread_address.tlv.target_eid == GUA1['ROUTER_1']).\ 318 must_next() 319 320 # Step 7: Border Router sends two ICMPv6 Echo Requests to MED using GUA 2003:: 321 # address 322 # The DUT MUST NOT respond with an Address Notification message 323 324 pkts.filter_wpan_src64(ROUTER_2).\ 325 filter_ipv6_dst(BR_RLOC).\ 326 filter_coap_request(ADDR_NTF_URI, port=MM).\ 327 must_not_next() 328 pkts.filter_ping_request().\ 329 filter_wpan_src64(BR).\ 330 filter_ipv6_dst(GUA1['MED']).\ 331 must_next() 332 333 334if __name__ == '__main__': 335 unittest.main() 336