1#!/usr/bin/env python3 2# 3# Copyright (c) 2020, 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# This test verifies that the MLR timeout works for multicast routing. 30# 31import unittest 32 33import config 34import thread_cert 35from pktverify.packet_verifier import PacketVerifier 36 37PBBR = 1 38ROUTER = 2 39HOST = 3 40 41MA1 = 'ff05::1234:777a:1' 42 43BBR_REGISTRATION_JITTER = 1 44REG_DELAY = 10 45MLR_TIMEOUT = 300 46WAIT_REDUNDANCE = 3 47 48 49class TestMlrTimeout(thread_cert.TestCase): 50 USE_MESSAGE_FACTORY = False 51 52 # Topology: 53 # --------(eth)--------- 54 # | | 55 # PBBR(Leader) HOST 56 # | 57 # ROUTER 58 # 59 TOPOLOGY = { 60 PBBR: { 61 'name': 'PBBR', 62 'allowlist': [ROUTER], 63 'is_otbr': True, 64 'version': '1.2', 65 'bbr_registration_jitter': BBR_REGISTRATION_JITTER, 66 }, 67 ROUTER: { 68 'name': 'ROUTER', 69 'allowlist': [PBBR], 70 'version': '1.2', 71 }, 72 HOST: { 73 'name': 'Host', 74 'is_host': True 75 }, 76 } 77 78 def _bootstrap(self): 79 # Bring up Host 80 self.nodes[HOST].start() 81 82 # Bring up PBBR 83 self.nodes[PBBR].start() 84 self.simulator.go(config.LEADER_STARTUP_DELAY) 85 self.assertEqual('leader', self.nodes[PBBR].get_state()) 86 self.nodes[PBBR].enable_backbone_router() 87 self.nodes[PBBR].set_backbone_router(reg_delay=REG_DELAY, mlr_timeout=MLR_TIMEOUT) 88 self.simulator.go(10) 89 self.assertTrue(self.nodes[PBBR].is_primary_backbone_router) 90 self.nodes[PBBR].add_prefix(config.DOMAIN_PREFIX, "parosD") 91 self.nodes[PBBR].register_netdata() 92 93 # Bring up Router 94 self.nodes[ROUTER].start() 95 self.simulator.go(5) 96 self.assertEqual('router', self.nodes[ROUTER].get_state()) 97 98 self.collect_ipaddrs() 99 100 def test(self): 101 self._bootstrap() 102 103 self.assertFalse(self.nodes[HOST].ping(MA1, backbone=True, ttl=10)) 104 105 # Router subscribes MA 106 self.nodes[ROUTER].add_ipmaddr(MA1) 107 self.simulator.go(WAIT_REDUNDANCE) 108 109 # Verify MA is reachable from Host 110 self.assertTrue(self.nodes[HOST].ping(MA1, backbone=True, ttl=10)) 111 self.simulator.go(WAIT_REDUNDANCE) 112 113 # Router unsubscribes MA 114 self.nodes[ROUTER].del_ipmaddr(MA1) 115 self.simulator.go(WAIT_REDUNDANCE) 116 117 # Verify MA is not reachable from Host after unsubscribed 118 # But PBBR should still forward the Ping Requests to Thread network 119 self.nodes[HOST].ping(MA1, backbone=True, ttl=10) 120 121 self.simulator.go(MLR_TIMEOUT + WAIT_REDUNDANCE) 122 123 # Verify MA is not reachable from Host after MLR timeout 124 # PBBR should not forward the Ping Requests to Thread network 125 self.assertFalse(self.nodes[HOST].ping(MA1, backbone=True, ttl=10)) 126 127 def verify(self, pv: PacketVerifier): 128 pkts = pv.pkts 129 pv.add_common_vars() 130 pv.summary.show() 131 132 ROUTER = pv.vars['ROUTER'] 133 PBBR = pv.vars['PBBR'] 134 MM = pv.vars['MM_PORT'] 135 HOST_ETH = pv.vars['Host_ETH'] 136 HOST_BGUA = pv.vars['Host_BGUA'] 137 138 start = pkts.index 139 140 # Verify that Router sends MLR.req for MA1 141 pkts.filter_wpan_src64(ROUTER).filter_coap_request('/n/mr', port=MM).must_next().must_verify( 142 'thread_meshcop.tlv.ipv6_addr == {ipv6_addr}', ipv6_addr=[MA1]) 143 144 mr_index = pkts.index 145 146 # Host pings Router before MLR.reg 147 before_mr_pkts = pkts.range(start, mr_index, cascade=False) 148 ping = before_mr_pkts.filter_eth_src(HOST_ETH).filter_ipv6_dst(MA1).filter_ping_request().must_next() 149 150 # PBBR should not forward this ping request to Thread network 151 before_mr_pkts.filter_wpan_src64(PBBR).filter_ipv6_dst(MA1).filter_ping_request( 152 identifier=ping.icmpv6.echo.identifier).must_not_next() 153 154 # Host pings Router after MLR.req 155 ping = pkts.filter_eth_src(HOST_ETH).filter_ipv6_dst(MA1).filter_ping_request().must_next() 156 # PBBR should forward this ping request to Thread network 157 pkts.filter_wpan_src64(PBBR).filter_AMPLFMA().filter_ping_request( 158 identifier=ping.icmpv6.echo.identifier).must_next() 159 # Router should reply 160 pkts.filter_wpan_src64(ROUTER).filter_ipv6_dst(HOST_BGUA).filter_ping_reply( 161 identifier=ping.icmpv6.echo.identifier).must_next() 162 163 # Host pings Router after Router unsubscribed MA 164 ping = pkts.filter_eth_src(HOST_ETH).filter_ipv6_dst(MA1).filter_ping_request().must_next() 165 # PBBR should forward this ping request to Thread network because MA doesn't expire yet 166 pkts.filter_wpan_src64(PBBR).filter_AMPLFMA().filter_ping_request( 167 identifier=ping.icmpv6.echo.identifier).must_next() 168 # Router should NOT reply because it has unsubcribed the multicast address 169 pkts.filter_wpan_src64(ROUTER).filter_ipv6_dst(HOST_BGUA).filter_ping_reply( 170 identifier=ping.icmpv6.echo.identifier).must_not_next() 171 172 # Host pings Router after MLR timeout 173 ping = pkts.filter_eth_src(HOST_ETH).filter_ipv6_dst(MA1).filter_ping_request().must_next() 174 # PBBR should NOT forward this ping request to Thread network because MLR has timeout 175 pkts.filter_wpan_src64(PBBR).filter_AMPLFMA().filter_ping_request( 176 identifier=ping.icmpv6.echo.identifier).must_not_next() 177 178 179if __name__ == '__main__': 180 unittest.main() 181