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 unittest
31
32import thread_cert
33
34# Test description:
35#
36#   This test verifies the behavior of the network when a zero length external route prefix "::/0" is added.
37#
38# Topology:
39#
40#    leader --- router1
41#       \         /
42#        \       /
43#         router2
44#
45
46LEADER = 1
47ROUTER1 = 2
48ROUTER2 = 3
49
50
51class ZeroLengthExternalRoute(thread_cert.TestCase):
52    USE_MESSAGE_FACTORY = False
53    SUPPORT_NCP = False
54
55    TOPOLOGY = {
56        LEADER: {
57            'name': 'LEADER',
58            'mode': 'rdn',
59        },
60        ROUTER1: {
61            'name': 'ROUTER2',
62            'mode': 'rdn',
63        },
64        ROUTER2: {
65            'name': 'ROUTER2',
66            'mode': 'rdn',
67        },
68    }
69
70    def test(self):
71        leader = self.nodes[LEADER]
72        router1 = self.nodes[ROUTER1]
73        router2 = self.nodes[ROUTER2]
74
75        # Start the nodes and form the network.
76
77        leader.start()
78        self.simulator.go(5)
79        self.assertEqual(leader.get_state(), 'leader')
80
81        router1.start()
82        router2.start()
83        self.simulator.go(5)
84        self.assertEqual(router1.get_state(), 'router')
85        self.assertEqual(router2.get_state(), 'router')
86
87        # Add an on-mesh prefix with SLAAC on router2 (without default
88        # route flag).
89
90        router2.add_prefix('fd00:1234::/64', 'paos')
91        router2.register_netdata()
92
93        # Add a zero length external route on router1. Also add an IPv6
94        # address on router1.
95
96        router1.add_route('::/0', stable=True)
97        router1.add_ipaddr('fd00:abcd::1')
98        router1.register_netdata()
99        self.simulator.go(5)
100
101        # Ping from leader the address added on router1. The zero
102        # length external route should ensure the message is routed
103        # to router1.
104
105        self.assertTrue(leader.ping('fd00:abcd::1'))
106
107        # Change the on-mesh prefix on router2 to now also have
108        # the default route flag.
109
110        router2.remove_prefix('fd00:1234::/64')
111        router2.add_prefix('fd00:5678::/64', 'paros')
112        router2.register_netdata()
113        self.simulator.go(5)
114
115        # Again ping from leader the same address. The explicit
116        # external route (even with zero length) on router1 should
117        # still be preferred over the default route flag of the
118        # on-mesh prefix from router2.
119
120        self.assertTrue(leader.ping('fd00:abcd::1'))
121
122        # Remove the external route on router1.
123
124        router1.remove_route("::/0")
125        router1.register_netdata()
126        self.simulator.go(5)
127
128        # Now the ping should fail since the message would be routed
129        # to router2 (due to its on-mesh prefix with default route
130        # flag).
131
132        self.assertFalse(leader.ping('fd00:abcd::1'))
133
134        # Remove the address from router1 and add it on router2 and
135        # ping it again from leader to verify that the message is
136        # being routed to router2.
137
138        router1.del_ipaddr('fd00:abcd::1')
139        router2.add_ipaddr('fd00:abcd::1')
140        self.simulator.go(5)
141        self.assertTrue(leader.ping('fd00:abcd::1'))
142
143
144if __name__ == '__main__':
145    unittest.main()
146