1#!/usr/bin/env python3 2# 3# Copyright (c) 2021, 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 logging 31import unittest 32 33import pktverify 34from pktverify import packet_verifier, packet_filter, consts 35from pktverify.consts import MA1 36import config 37import thread_cert 38 39# Test description: 40# The purpose of this test case is to verify that a Secondary BBR can take over 41# forwarding of outbound multicast transmissions from the Thread Network when 42# the Primary BBR disconnects. The Secondary in that case becomes Primary. 43# 44# Topology: 45# ----------------(eth)------------------ 46# | | 47# BR_1 (Leader) ---- BR_2 48# | | 49# | | 50# ROUTER_1 -----------+ 51# 52from pktverify.null_field import nullField 53 54BR_1 = 1 55BR_2 = 2 56ROUTER_1 = 3 57 58 59class MATN_09_FailureOfPrimaryBBROutboundMulticast(thread_cert.TestCase): 60 USE_MESSAGE_FACTORY = False 61 62 TOPOLOGY = { 63 BR_1: { 64 'name': 'BR_1', 65 'is_otbr': True, 66 'allowlist': [BR_2, ROUTER_1], 67 'version': '1.2', 68 }, 69 BR_2: { 70 'name': 'BR_2', 71 'is_otbr': True, 72 'allowlist': [BR_1, ROUTER_1], 73 'version': '1.2', 74 }, 75 ROUTER_1: { 76 'name': 'Router_1', 77 'allowlist': [BR_1, BR_2], 78 'version': '1.2', 79 'partition_id': 1, 80 'network_id_timeout': 150, 81 }, 82 } 83 84 def test(self): 85 br1 = self.nodes[BR_1] 86 br2 = self.nodes[BR_2] 87 router1 = self.nodes[ROUTER_1] 88 89 br1.start() 90 self.simulator.go(config.LEADER_STARTUP_DELAY) 91 self.assertEqual('leader', br1.get_state()) 92 self.assertTrue(br1.is_primary_backbone_router) 93 94 router1.start() 95 self.simulator.go(10) 96 self.assertEqual('router', router1.get_state()) 97 98 br2.start() 99 self.simulator.go(5) 100 self.assertEqual('router', br2.get_state()) 101 self.assertFalse(br2.is_primary_backbone_router) 102 103 # 1. Router sends a ping packet to the multicast address, MA1, 104 # encapsulated in an MPL packet 105 self.assertFalse(router1.ping(MA1)) 106 self.simulator.go(5) 107 108 # 4a. Switch off BR_1 109 br1.disable_backbone_router() 110 br1.thread_stop() 111 br1.interface_down() 112 self.simulator.go(180) 113 114 # 4b. BR_2 detects the missing Primary BBR and becomes the the Leader of 115 # the Thread Network. 116 self.assertEqual('disabled', br1.get_state()) 117 self.assertEqual('leader', br2.get_state()) 118 self.assertEqual('router', router1.get_state()) 119 self.assertFalse(br1.is_primary_backbone_router) 120 self.assertTrue(br2.is_primary_backbone_router) 121 122 # 5. Router_1 sends a ping packet to the multicast address, MA1, 123 # encapsulated in an MPL packet. 124 self.assertFalse(router1.ping(MA1)) 125 126 self.collect_ipaddrs() 127 self.collect_rloc16s() 128 self.collect_rlocs() 129 self.collect_leader_aloc(BR_2) 130 self.collect_extra_vars() 131 132 def verify(self, pv: pktverify.packet_verifier.PacketVerifier): 133 pkts = pv.pkts 134 vars = pv.vars 135 pv.summary.show() 136 logging.info(f'vars = {vars}') 137 138 # Ensure the topology is formed correctly 139 pv.verify_attached('Router_1', 'BR_1') 140 pv.verify_attached('BR_2') 141 142 # 1. Router_1 sends a ping packet to the multicast address, MA1, 143 # encapsulated in an MPL packet. 144 _pkt = pkts.filter_wpan_src64(vars['Router_1']) \ 145 .filter_AMPLFMA(mpl_seed_id=vars['Router_1_RLOC']) \ 146 .filter_ping_request() \ 147 .must_next() 148 149 initial_identifier = _pkt.icmpv6.echo.identifier 150 151 # 2. BR_1 forwards the multicast ping packet with multicast address, 152 # MA1, to the LAN. 153 pkts.filter_eth_src(vars['BR_1_ETH']) \ 154 .filter_ipv6_dst(MA1) \ 155 .filter_ping_request(identifier=_pkt.icmpv6.echo.identifier) \ 156 .must_next() 157 158 with pkts.save_index(): 159 pv.verify_attached('Router_1', 'BR_2') 160 161 # 4b. BR_2 detects the missing Primary BBR and becomes the Leader of the 162 # Thread Network, distributing its BBR dataset. 163 # Verify that Router_1 has received the new BBR Dataset from BR_2, 164 # where: 165 # • RLOC16 in Server TLV == The RLOC16 of BR_2 166 # All fields in the Service TLV contain valid values. 167 pkts.filter_wpan_src64(vars['BR_2']) \ 168 .filter_mle() \ 169 .filter( 170 lambda p: p.thread_nwd.tlv.server_16 is not nullField and 171 vars['BR_2_RLOC16'] in p.thread_nwd.tlv.server_16) \ 172 .must_next() 173 174 # 5.Router_1 sends a ping packet to the multicast address, MA1, 175 # encapsulated in an MPL packet. 176 _pkt = pkts.filter_wpan_src64(vars['Router_1']) \ 177 .filter_AMPLFMA(mpl_seed_id=vars['Router_1_RLOC']) \ 178 .filter_ping_request() \ 179 .filter(lambda p: p.icmpv6.echo.identifier != initial_identifier) \ 180 .must_next() 181 182 # 6. BR_2 forwards the multicast ping packet with multicast address, 183 # MA1, to the LAN. 184 pkts.filter_eth_src(vars['BR_2_ETH']) \ 185 .filter_ipv6_dst(MA1) \ 186 .filter_ping_request(identifier=_pkt.icmpv6.echo.identifier) \ 187 .must_next() 188 189 190if __name__ == '__main__': 191 unittest.main() 192