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# This test verifies that PBBR sets DUA routes correctly. 30# 31import ipaddress 32import unittest 33 34import config 35import thread_cert 36from pktverify.consts import ADDR_QRY_URI, BACKBONE_QUERY_URI, BACKBONE_ANSWER_URI, ADDR_NTF_URI 37from pktverify.packet_verifier import PacketVerifier 38 39# Use two channels 40CH1 = 11 41CH2 = 22 42 43PBBR = 1 44ROUTER1 = 2 45MED1 = 3 46HOST = 4 47PBBR2 = 5 48MED2 = 6 49 50REREG_DELAY = 5 # Seconds 51MLR_TIMEOUT = 300 # Seconds 52WAIT_REDUNDANCE = 3 53 54 55class TestDuaRoutingMed(thread_cert.TestCase): 56 USE_MESSAGE_FACTORY = False 57 58 # Topology: 59 # ------(eth)---------------------- 60 # | | | 61 # PBBR HOST PBBR2 62 # / CH1\ | CH2 63 # MED1 ROUTER1 MED2 64 # 65 # PBBR2 is in the secondary channel 66 # 67 TOPOLOGY = { 68 PBBR: { 69 'name': 'PBBR', 70 'allowlist': [MED1, ROUTER1], 71 'is_otbr': True, 72 'version': '1.2', 73 'channel': CH1, 74 }, 75 ROUTER1: { 76 'name': 'ROUTER1', 77 'allowlist': [PBBR], 78 'version': '1.2', 79 'channel': CH1, 80 }, 81 MED1: { 82 'name': 'MED1', 83 'allowlist': [PBBR], 84 'version': '1.2', 85 'channel': CH1, 86 'mode': 'rn', 87 }, 88 HOST: { 89 'name': 'HOST', 90 'is_host': True 91 }, 92 PBBR2: { 93 'name': 'PBBR2', 94 'is_otbr': True, 95 'version': '1.2', 96 'channel': CH2, 97 }, 98 MED2: { 99 'name': 'MED2', 100 'version': '1.2', 101 'channel': CH2, 102 'mode': 'rn', 103 }, 104 } 105 106 def _bootstrap(self): 107 # Bring up HOST 108 self.nodes[HOST].start() 109 110 # Bring up PBBR 111 self.nodes[PBBR].start() 112 self.simulator.go(config.LEADER_STARTUP_DELAY) 113 self.assertEqual('leader', self.nodes[PBBR].get_state()) 114 self.wait_node_state(PBBR, 'leader', 10) 115 116 self.nodes[PBBR].set_backbone_router(reg_delay=REREG_DELAY, mlr_timeout=MLR_TIMEOUT) 117 self.nodes[PBBR].enable_backbone_router() 118 self.nodes[PBBR].set_domain_prefix(config.DOMAIN_PREFIX, 'prosD') 119 self.simulator.go(5) 120 self.assertTrue(self.nodes[PBBR].is_primary_backbone_router) 121 self.assertIsNotNone(self.nodes[PBBR].get_ip6_address(config.ADDRESS_TYPE.DUA)) 122 123 # Bring up ROUTER1 124 self.nodes[ROUTER1].start() 125 self.simulator.go(5) 126 self.assertEqual('router', self.nodes[ROUTER1].get_state()) 127 self.assertIsNotNone(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.DUA)) 128 129 # Bring up MED1 130 self.nodes[MED1].start() 131 self.simulator.go(5) 132 self.assertEqual('child', self.nodes[MED1].get_state()) 133 self.assertIsNotNone(self.nodes[MED1].get_ip6_address(config.ADDRESS_TYPE.DUA)) 134 135 # Bring up PBBR2 136 self.nodes[PBBR2].start() 137 self.simulator.go(config.LEADER_STARTUP_DELAY) 138 self.assertEqual('leader', self.nodes[PBBR2].get_state()) 139 self.wait_node_state(PBBR2, 'leader', 10) 140 141 self.nodes[PBBR2].set_backbone_router(reg_delay=REREG_DELAY, mlr_timeout=MLR_TIMEOUT) 142 self.nodes[PBBR2].enable_backbone_router() 143 self.nodes[PBBR2].set_domain_prefix(config.DOMAIN_PREFIX, 'prosD') 144 self.simulator.go(5) 145 self.assertTrue(self.nodes[PBBR2].is_primary_backbone_router) 146 self.assertIsNotNone(self.nodes[PBBR2].get_ip6_address(config.ADDRESS_TYPE.DUA)) 147 148 # Bring up MED2 149 self.nodes[MED2].start() 150 self.simulator.go(5) 151 self.assertEqual('child', self.nodes[MED2].get_state()) 152 self.assertIsNotNone(self.nodes[MED2].get_ip6_address(config.ADDRESS_TYPE.DUA)) 153 154 def test(self): 155 self._bootstrap() 156 157 self.collect_ipaddrs() 158 self.collect_rloc16s() 159 160 self.simulator.go(10) 161 162 # PBBR2 pings MED2 163 self.assertTrue(self.nodes[PBBR2].ping(self.nodes[MED2].get_ip6_address(config.ADDRESS_TYPE.DUA))) 164 self.simulator.go(WAIT_REDUNDANCE) 165 166 # MED2 pings PBBR2 167 self.assertTrue(self.nodes[MED2].ping(self.nodes[PBBR2].get_ip6_address(config.ADDRESS_TYPE.DUA))) 168 self.simulator.go(WAIT_REDUNDANCE) 169 170 # PBBR pings PBBR2 171 self.nodes[PBBR].ping(self.nodes[PBBR2].get_ip6_address(config.ADDRESS_TYPE.DUA)) 172 self.simulator.go(WAIT_REDUNDANCE) 173 174 # MED1 pings ROUTER1 should succeed 175 self.assertTrue(self.nodes[MED1].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.DUA))) 176 self.simulator.go(WAIT_REDUNDANCE) 177 178 # MED1 pings MED2 which should succeed 179 MED2_DUA = self.nodes[MED2].get_ip6_address(config.ADDRESS_TYPE.DUA) 180 self.assertTrue(self.nodes[MED1].ping(MED2_DUA)) 181 self.simulator.go(WAIT_REDUNDANCE) 182 183 def _get_mliid(self, nodeid: int) -> str: 184 mleid = self.nodes[nodeid].get_ip6_address(config.ADDRESS_TYPE.ML_EID) 185 mliid = ipaddress.IPv6Address(mleid).packed[-8:] 186 return ''.join(['%02x' % b for b in mliid]) 187 188 def verify(self, pv: PacketVerifier): 189 pkts = pv.pkts 190 pv.add_common_vars() 191 pv.summary.show() 192 193 PBBR = pv.vars['PBBR'] 194 ROUTER1 = pv.vars['ROUTER1'] 195 PBBR2 = pv.vars['PBBR2'] 196 MED1 = pv.vars['MED1'] 197 MED2 = pv.vars['MED2'] 198 MED1_DUA = pv.vars['MED1_DUA'] 199 ROUTER1_DUA = pv.vars['ROUTER1_DUA'] 200 PBBR_DUA = pv.vars['PBBR_DUA'] 201 PBBR2_DUA = pv.vars['PBBR2_DUA'] 202 MED2_DUA = pv.vars['MED2_DUA'] 203 PBBR_ETH = pv.vars['PBBR_ETH'] 204 PBBR2_ETH = pv.vars['PBBR2_ETH'] 205 206 # PBBR should never send Address Query for MED1 (Child) 207 pkts.filter_wpan_src64(PBBR).filter_RLARMA().filter_coap_request(ADDR_QRY_URI) \ 208 .filter('thread_address.tlv.target_eid == {eid}', eid=MED1_DUA) \ 209 .must_not_next() 210 211 # PBBR2 should never send Address Query for MED2 (Child) 212 pkts.filter_wpan_src64(PBBR2).filter_RLARMA().filter_coap_request(ADDR_QRY_URI) \ 213 .filter('thread_address.tlv.target_eid == {eid}', eid=MED2_DUA) \ 214 .must_not_next() 215 216 # MEDs should never send Address Query 217 pkts.filter_wpan_src64(MED1).filter_RLARMA().filter_coap_request(ADDR_QRY_URI).must_not_next() 218 pkts.filter_wpan_src64(MED2).filter_RLARMA().filter_coap_request(ADDR_QRY_URI).must_not_next() 219 220 # PBBR pings PBBR2 should succeed 221 ping_id = pkts.filter_ipv6_src_dst( 222 PBBR_DUA, PBBR2_DUA).filter_eth_src(PBBR_ETH).filter_ping_request().must_next().icmpv6.echo.identifier 223 pkts.filter_ipv6_src_dst(PBBR2_DUA, 224 PBBR_DUA).filter_eth_src(PBBR2_ETH).filter_ping_reply(identifier=ping_id).must_next() 225 226 # MED1 pings ROUTER1 227 ping_request_pkts = pkts.filter_ipv6_src_dst(MED1_DUA, ROUTER1_DUA) 228 # MED1 sends the Ping Request 229 ping_pkt = ping_request_pkts.filter_wpan_src64(MED1).filter_ping_request().must_next() 230 ping_id = ping_pkt.icmpv6.echo.identifier 231 # PBBR sends Address Query for ROUTER1_DUA 232 pkts.filter_wpan_src64(PBBR).filter_RLARMA().filter_coap_request(ADDR_QRY_URI) \ 233 .filter('thread_address.tlv.target_eid == {eid}', eid=ROUTER1_DUA) \ 234 .must_next() 235 # PBBR sends Backbone Query for ROUTER1_DUA 236 pkts.filter_eth_src(PBBR_ETH).filter_coap_request(BACKBONE_QUERY_URI) \ 237 .filter('thread_bl.tlv.target_eid == {eid}', eid=ROUTER1_DUA) \ 238 .must_next() 239 # ROUTER1 sends Address Answer for ROUTER1_DUA 240 pkts.filter_wpan_src64(ROUTER1).filter_coap_request(ADDR_NTF_URI, confirmable=True) \ 241 .filter('thread_address.tlv.target_eid == {eid}', eid=ROUTER1_DUA) \ 242 .must_next() 243 # PBBR forwards Ping Request to ROUTER1, decreasing TTL by 1 244 ping_request_pkts.filter_wpan_src64(PBBR).filter_ping_request(identifier=ping_id).must_next().must_verify( 245 'ipv6.hlim == {hlim}', hlim=ping_pkt.ipv6.hlim - 1) 246 247 # MED1 pings MED2 248 # Verify Ping Request: MED1 -> PBBR -> PBBR2 -> MED2 249 ping_request_pkts = pkts.filter_ipv6_src_dst(MED1_DUA, MED2_DUA) 250 # MED1 sends the Ping Request 251 ping_pkt = ping_request_pkts.filter_wpan_src64(MED1).filter_ping_request().must_next() 252 ping_id = ping_pkt.icmpv6.echo.identifier 253 # PBBR sends Address Query for MED2_DUA 254 pkts.filter_wpan_src64(PBBR).filter_RLARMA().filter_coap_request(ADDR_QRY_URI) \ 255 .filter('thread_address.tlv.target_eid == {eid}', eid=MED2_DUA) \ 256 .must_next() 257 # PBBR sends Backbone Query for MED2_DUA 258 pkts.filter_eth_src(PBBR_ETH).filter_coap_request(BACKBONE_QUERY_URI) \ 259 .filter('thread_bl.tlv.target_eid == {eid}', eid=MED2_DUA) \ 260 .must_next() 261 # PBBR2 sends Backbone Answer for MED2_DUA 262 pkts.filter_eth_src(PBBR2_ETH).filter_coap_request(BACKBONE_ANSWER_URI, confirmable=True) \ 263 .filter('thread_bl.tlv.target_eid == {eid}', eid=MED2_DUA) \ 264 .must_next() 265 # PBBR forwards Ping Request to Backbone link, decreasing TTL by 1 266 ping_request_pkts.filter_eth_src(PBBR_ETH).filter_ping_request(identifier=ping_id).must_next().must_verify( 267 'ipv6.hlim == {hlim}', hlim=ping_pkt.ipv6.hlim - 1) 268 ping_request_pkts.filter_wpan_src64(PBBR2).filter_ping_request(identifier=ping_id).must_next() 269 # Verify Ping Reply: MED2 -> PBBR2 -> PBBR -> MED1 270 ping_reply_pkts = pkts.filter_ipv6_src_dst(MED2_DUA, MED1_DUA).filter_ping_reply(identifier=ping_id) 271 ping_reply_pkts.filter_eth_src(PBBR2_ETH).must_next() 272 273 274if __name__ == '__main__': 275 unittest.main() 276