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#
29import logging
30import unittest
31from ipaddress import IPv6Network
32
33import config
34import thread_cert
35
36# Test description:
37#   This test verifies bi-directional connectivity between Thread end device
38#   and infra host.
39#
40# Topology:
41#    ----------------(eth)--------------------
42#           |                 |
43#          BR (Leader)      HOST
44#           |
45#        ROUTER
46#
47
48BR = 1
49ROUTER = 2
50HOST = 3
51
52# The two prefixes are set small enough that a random-generated OMR prefix is
53# very likely greater than them. So that the BR will remove the random-generated one.
54ON_MESH_PREFIX1 = "fd00:00:00:01::/64"
55ON_MESH_PREFIX2 = "fd00:00:00:02::/64"
56
57
58class SingleBorderRouter(thread_cert.TestCase):
59    USE_MESSAGE_FACTORY = False
60
61    TOPOLOGY = {
62        BR: {
63            'name': 'BR',
64            'allowlist': [ROUTER],
65            'is_otbr': True,
66            'version': '1.2',
67        },
68        ROUTER: {
69            'name': 'Router',
70            'allowlist': [BR],
71            'version': '1.2',
72        },
73        HOST: {
74            'name': 'Host',
75            'is_host': True
76        },
77    }
78
79    def test(self):
80        br = self.nodes[BR]
81        router = self.nodes[ROUTER]
82        host = self.nodes[HOST]
83
84        host.start(start_radvd=False)
85        self.simulator.go(5)
86
87        br.start()
88        self.simulator.go(config.LEADER_STARTUP_DELAY)
89        self.assertEqual('leader', br.get_state())
90
91        router.start()
92        self.simulator.go(config.ROUTER_STARTUP_DELAY)
93        self.assertEqual('router', router.get_state())
94
95        #
96        # Case 1. There is no OMR prefix or on-link prefix.
97        #
98
99        self.simulator.go(10)
100        self.collect_ipaddrs()
101
102        logging.info("BR      addrs: %r", br.get_addrs())
103        logging.info("ROUTER  addrs: %r", router.get_addrs())
104        logging.info("HOST    addrs: %r", host.get_addrs())
105
106        self.assertEqual(len(br.get_netdata_omr_prefixes()), 1)
107        self.assertEqual(len(router.get_netdata_omr_prefixes()), 1)
108        self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1)
109        self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1)
110
111        omr_prefix = br.get_br_omr_prefix()
112        on_link_prefix = br.get_br_on_link_prefix()
113
114        self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
115        self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
116        self.assertEqual(len(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)), 1)
117
118        br1_omr_address = br.get_ip6_address(config.ADDRESS_TYPE.OMR)[0]
119        router1_omr_address = router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0]
120        host_ula_address = host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]
121
122        # Router1 can ping to/from the Host on infra link.
123        self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
124        self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True))
125
126        #
127        # Case 2. User adds smaller on-mesh prefix.
128        #         1. Should deregister our local OMR prefix.
129        #         2. Should re-register our local OMR prefix when user prefix
130        #            is removed.
131        #
132
133        br.add_prefix(ON_MESH_PREFIX1)
134        br.add_prefix(ON_MESH_PREFIX2)
135        br.register_netdata()
136
137        self.simulator.go(10)
138        self.collect_ipaddrs()
139
140        logging.info("BR     addrs: %r", br.get_addrs())
141        logging.info("ROUTER addrs: %r", router.get_addrs())
142        logging.info("HOST    addrs: %r", host.get_addrs())
143
144        self.assertGreaterEqual(len(host.get_addrs()), 2)
145
146        self.assertEqual(len(br.get_netdata_omr_prefixes()), 2)
147        self.assertEqual(len(router.get_netdata_omr_prefixes()), 2)
148        self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1)
149        self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1)
150
151        self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 2)
152        self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 2)
153        self.assertEqual(len(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)), 1)
154
155        # Router1 can ping to/from the Host on infra link.
156        self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
157        self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True))
158        self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[1], backbone=True))
159
160        # Remove user prefixes, should re-register local OMR prefix.
161        br.remove_prefix(ON_MESH_PREFIX1)
162        br.remove_prefix(ON_MESH_PREFIX2)
163        br.register_netdata()
164
165        self.simulator.go(10)
166        self.collect_ipaddrs()
167
168        logging.info("BR     addrs: %r", br.get_addrs())
169        logging.info("ROUTER addrs: %r", router.get_addrs())
170        logging.info("HOST    addrs: %r", host.get_addrs())
171
172        self.assertEqual(len(br.get_netdata_omr_prefixes()), 1)
173        self.assertEqual(len(router.get_netdata_omr_prefixes()), 1)
174        self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1)
175        self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1)
176
177        # The same local OMR and on-link prefix should be re-register.
178        self.assertEqual(br.get_netdata_omr_prefixes(), [omr_prefix])
179        self.assertEqual(router.get_netdata_omr_prefixes(), [omr_prefix])
180
181        self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
182        self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
183        self.assertEqual(len(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)), 1)
184
185        self.assertEqual(br.get_ip6_address(config.ADDRESS_TYPE.OMR), [br1_omr_address])
186        self.assertEqual(router.get_ip6_address(config.ADDRESS_TYPE.OMR), [router1_omr_address])
187        self.assertEqual(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA), [host_ula_address])
188
189        # Router1 can ping to/from the Host on infra link.
190        self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
191        self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True))
192
193        #
194        # Case 3. OMR and on-link prefixes should be removed when Border Routing is
195        #         explicitly disabled and added when Border Routing is enabled again.
196        #
197
198        br.disable_br()
199
200        self.simulator.go(10)
201        self.collect_ipaddrs()
202
203        logging.info("BR     addrs: %r", br.get_addrs())
204        logging.info("ROUTER addrs: %r", router.get_addrs())
205        logging.info("HOST    addrs: %r", host.get_addrs())
206
207        self.assertEqual(len(br.get_prefixes()), 0)
208        self.assertEqual(len(router.get_prefixes()), 0)
209        self.assertEqual(len(br.get_routes()), 0)
210        self.assertEqual(len(router.get_routes()), 0)
211        self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 0)
212        self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 0)
213
214        br.enable_br()
215
216        # It takes around 10 seconds to start sending RA messages.
217        self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY)
218        self.collect_ipaddrs()
219
220        logging.info("BR     addrs: %r", br.get_addrs())
221        logging.info("ROUTER addrs: %r", router.get_addrs())
222        logging.info("HOST    addrs: %r", host.get_addrs())
223
224        self.assertEqual(len(br.get_netdata_omr_prefixes()), 1)
225        self.assertEqual(len(router.get_netdata_omr_prefixes()), 1)
226        self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1)
227        self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1)
228
229        # The same local OMR and on-link prefix should be re-registered.
230        self.assertEqual(br.get_netdata_omr_prefixes(), [omr_prefix])
231        self.assertEqual(router.get_netdata_omr_prefixes(), [omr_prefix])
232
233        self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
234        self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
235        self.assertEqual(len(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)), 1)
236
237        self.assertEqual(br.get_ip6_address(config.ADDRESS_TYPE.OMR), [br1_omr_address])
238        self.assertEqual(router.get_ip6_address(config.ADDRESS_TYPE.OMR), [router1_omr_address])
239        self.assertEqual(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA), [host_ula_address])
240
241        # Router1 can ping to/from the Host on infra link.
242        self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
243        self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True))
244
245        #
246        # Case 4. The Routing Manager should be stopped if the infra interface went down.
247        #
248
249        br.disable_ether()
250
251        self.simulator.go(10)
252        self.collect_ipaddrs()
253
254        logging.info("BR     addrs: %r", br.get_addrs())
255        logging.info("ROUTER addrs: %r", router.get_addrs())
256        logging.info("HOST    addrs: %r", host.get_addrs())
257
258        self.assertEqual(len(br.get_prefixes()), 0)
259        self.assertEqual(len(router.get_prefixes()), 0)
260        self.assertEqual(len(br.get_routes()), 0)
261        self.assertEqual(len(router.get_routes()), 0)
262        self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 0)
263        self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 0)
264
265        br.enable_ether()
266
267        # The routing manager may fail to send RS and will wait for 4 seconds
268        # before retrying.
269        self.simulator.go(40)
270        self.collect_ipaddrs()
271
272        logging.info("BR     addrs: %r", br.get_addrs())
273        logging.info("ROUTER addrs: %r", router.get_addrs())
274        logging.info("HOST    addrs: %r", host.get_addrs())
275
276        self.assertEqual(len(br.get_netdata_omr_prefixes()), 1)
277        self.assertEqual(len(router.get_netdata_omr_prefixes()), 1)
278        self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1)
279        self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1)
280
281        # The same local OMR and on-link prefix should be re-registered.
282        self.assertEqual(br.get_netdata_omr_prefixes(), [omr_prefix])
283        self.assertEqual(router.get_netdata_omr_prefixes(), [omr_prefix])
284
285        self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
286        self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
287        self.assertEqual(len(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)), 1)
288
289        self.assertEqual(br.get_ip6_address(config.ADDRESS_TYPE.OMR), [br1_omr_address])
290        self.assertEqual(router.get_ip6_address(config.ADDRESS_TYPE.OMR), [router1_omr_address])
291        self.assertEqual(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA), [host_ula_address])
292
293        # Router1 can ping to/from the Host on infra link.
294        self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True))
295        self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
296
297        #
298        # Case 5. Test if the linux host is still reachable if rejoin the network.
299        #
300
301        host.disable_ether()
302        self.simulator.go(10)
303        host.enable_ether()
304        self.simulator.go(10)
305
306        self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
307        self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True))
308
309        #
310        # Case 6. Test if the Border Router will remove the on-link prefix when
311        #         another RA daemon is started on the same infra interface.
312        #
313
314        br.start_radvd_service(prefix=config.ONLINK_GUA_PREFIX, slaac=True)
315        self.simulator.go(5)
316
317        self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1)
318        self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1)
319
320        self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_GUA)[0]))
321        self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
322        self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True))
323
324        #
325        # Case 7. Test if Border Router changes on-link prefix when
326        #         Extended PAN ID changes.
327        #
328
329        prefixA = br.get_br_on_link_prefix()
330
331        router.commissioner_start()
332        self.simulator.go(5)
333
334        router.send_mgmt_active_set(
335            active_timestamp=100,
336            extended_panid='0001020304050607',
337        )
338        self.simulator.go(10)
339
340        prefixB = br.get_br_on_link_prefix()
341        self.assertNotEqual(IPv6Network(prefixA), IPv6Network(prefixB))
342
343
344if __name__ == '__main__':
345    unittest.main()
346