1#!/usr/bin/env python3
2#
3#  Copyright (c) 2024, 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
31import ipaddress
32
33import config
34import thread_cert
35
36from node import OtbrNode
37
38IPV4_CIDR_ADDR_CMD = f'ip addr show {config.BACKBONE_IFNAME} | grep -w inet | grep -Eo "[0-9.]+/[0-9]+"'
39
40
41class ThreeBRs_TwoInfra(thread_cert.TestCase):
42    """
43    Test that two border routers on different infrastructures can ping each other via Thread interface.
44
45    Topology:
46
47    ----(backbone0.0)---- | -------(backbone0.1)------
48             |                    |            |
49            BR1   .............  BR2 ........ BR3
50
51    """
52    USE_MESSAGE_FACTORY = False
53    BR1, BR2, BR3 = range(1, 4)
54
55    TOPOLOGY = {
56        BR1: {
57            'name': 'BR_1',
58            'backbone_network_id': 0,
59            'allowlist': [BR2],
60            'is_otbr': True,
61            'version': '1.4',
62        },
63        BR2: {
64            'name': 'BR_2',
65            'backbone_network_id': 1,
66            'allowlist': [BR1, BR3],
67            'is_otbr': True,
68            'version': '1.4',
69        },
70        BR3: {
71            'name': 'BR_3',
72            'backbone_network_id': 1,
73            'allowlist': [BR2],
74            'is_otbr': True,
75            'version': '1.4',
76        }
77    }
78
79    def test_multi_backbone_infra(self):
80        """This test ensures that the multiple backbone infra works as expected."""
81        br1: OtbrNode = self.nodes[self.BR1]
82        br2: OtbrNode = self.nodes[self.BR2]
83        br3: OtbrNode = self.nodes[self.BR3]
84
85        # start nodes
86        for br in [br1, br2, br3]:
87            br.start()
88        self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY)
89
90        # check BR1 and BR2 are not on the same subnet, but BR2 and BR3 are on the same subnet
91        br1_infra_ip_addr = br1.bash(IPV4_CIDR_ADDR_CMD)
92        br2_infra_ip_addr = br2.bash(IPV4_CIDR_ADDR_CMD)
93        br3_infra_ip_addr = br3.bash(IPV4_CIDR_ADDR_CMD)
94        assert len(br1_infra_ip_addr) == 1
95        assert len(br2_infra_ip_addr) == 1
96        assert len(br3_infra_ip_addr) == 1
97
98        self.assertNotEqual(ipaddress.ip_network(br1_infra_ip_addr[0].strip(), strict=False),
99                            ipaddress.ip_network(br2_infra_ip_addr[0].strip(), strict=False))
100        self.assertEqual(ipaddress.ip_network(br2_infra_ip_addr[0].strip(), strict=False),
101                         ipaddress.ip_network(br3_infra_ip_addr[0].strip(), strict=False))
102
103        # ping each other using Thread MLEID
104        br1_mleid = br1.get_ip6_address(config.ADDRESS_TYPE.ML_EID)
105        br2_mleid = br2.get_ip6_address(config.ADDRESS_TYPE.ML_EID)
106        br3_mleid = br3.get_ip6_address(config.ADDRESS_TYPE.ML_EID)
107
108        self.assertTrue(br1.ping(br2_mleid))
109        self.assertTrue(br1.ping(br3_mleid))
110        self.assertTrue(br2.ping(br1_mleid))
111        self.assertTrue(br2.ping(br3_mleid))
112        self.assertTrue(br3.ping(br1_mleid))
113        self.assertTrue(br3.ping(br2_mleid))
114
115        # ping each other using Infra ULA
116        br1_onlink_ula = br1.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]
117        br2_onlink_ula = br2.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]
118        br3_onlink_ula = br3.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]
119
120        self.assertFalse(br1.ping(br2_onlink_ula, backbone=True))
121        self.assertFalse(br1.ping(br3_onlink_ula, backbone=True))
122        self.assertFalse(br2.ping(br1_onlink_ula, backbone=True))
123        self.assertFalse(br3.ping(br1_onlink_ula, backbone=True))
124        self.assertTrue(br2.ping(br3_onlink_ula, backbone=True))
125        self.assertTrue(br3.ping(br2_onlink_ula, backbone=True))
126
127
128if __name__ == '__main__':
129    unittest.main(verbosity=2)
130