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 basic MLR feature works.
30#
31import logging
32import unittest
33
34import config
35import thread_cert
36from pktverify.packet_verifier import PacketVerifier
37
38PBBR = 1
39SBBR = 2
40ROUTER = 3
41LEADER = 4
42FED = 5
43MED = 6
44SED = 7
45HOST = 8
46
47SED_POLL_PERIOD = 1000  # Milliseconds
48
49MA1 = 'ff04::1234:777a:1'
50MA2 = 'ff05::1234:777a:2'
51MA3 = 'ff05::1234:777a:3'
52MA4 = 'ff05::1234:777a:4'
53MA5 = 'ff05::1234:777a:5'
54
55BBR_REGISTRATION_JITTER = 1
56WAIT_REDUNDANCE = 3
57
58
59class TestMlr(thread_cert.TestCase):
60    USE_MESSAGE_FACTORY = False
61
62    # Topology:
63    #   --------(eth)---------
64    #       |     |      |
65    #     PBBR---SBBR   HOST
66    #       \    /  \
67    #        \  /    \
68    #        ROUTER--LEADER
69    #       /  |  \
70    #      /   |   \
71    #    FED  MED  SED
72    #
73    # More links:
74    #    PBBR---LEADER
75    #
76    TOPOLOGY = {
77        PBBR: {
78            'name': 'PBBR',
79            'allowlist': [SBBR, ROUTER, LEADER],
80            'is_otbr': True,
81            'version': '1.2',
82            'bbr_registration_jitter': BBR_REGISTRATION_JITTER,
83        },
84        SBBR: {
85            'name': 'SBBR',
86            'allowlist': [PBBR, ROUTER, LEADER],
87            'is_otbr': True,
88            'version': '1.2',
89            'bbr_registration_jitter': BBR_REGISTRATION_JITTER,
90        },
91        ROUTER: {
92            'name': 'ROUTER',
93            'allowlist': [PBBR, SBBR, LEADER, FED, MED, SED],
94            'version': '1.2',
95        },
96        LEADER: {
97            'name': 'LEADER',
98            'allowlist': [PBBR, SBBR, ROUTER],
99            'version': '1.2',
100        },
101        FED: {
102            'name': 'FED',
103            'allowlist': [ROUTER],
104            'version': '1.2',
105            'router_upgrade_threshold': 0,
106        },
107        MED: {
108            'name': 'MED',
109            'allowlist': [ROUTER],
110            'version': '1.2',
111            'mode': 'rn',
112        },
113        SED: {
114            'name': 'SED',
115            'allowlist': [ROUTER],
116            'version': '1.2',
117            'mode': 'n'
118        },
119        HOST: {
120            'name': 'Host',
121            'is_host': True
122        },
123    }
124
125    def test(self):
126        # Bring up Host
127        self.nodes[HOST].start()
128
129        # Bring up Leader
130        self.nodes[LEADER].start()
131        self.simulator.go(config.LEADER_STARTUP_DELAY)
132        self.assertEqual('leader', self.nodes[LEADER].get_state())
133
134        # Bring up Router
135        self.nodes[ROUTER].start()
136        self.simulator.go(5)
137        self.assertEqual('router', self.nodes[ROUTER].get_state())
138
139        # Bring up PBBR
140        self.nodes[PBBR].start()
141        self.simulator.go(5)
142        self.assertEqual('router', self.nodes[PBBR].get_state())
143        self.nodes[PBBR].enable_backbone_router()
144        self.simulator.go(10)
145        self.assertTrue(self.nodes[PBBR].is_primary_backbone_router)
146        self.nodes[PBBR].add_prefix(config.DOMAIN_PREFIX, "parosD")
147        self.nodes[PBBR].register_netdata()
148
149        # Bring up SBBR
150        self.nodes[SBBR].start()
151        self.simulator.go(5)
152        self.assertEqual('router', self.nodes[SBBR].get_state())
153        self.nodes[SBBR].enable_backbone_router()
154        self.simulator.go(10)
155        self.assertFalse(self.nodes[SBBR].is_primary_backbone_router)
156
157        # Bring up FED, MED, SED
158        self.nodes[FED].start()
159        self.nodes[MED].start()
160        self.nodes[SED].set_pollperiod(SED_POLL_PERIOD)
161        self.nodes[SED].start()
162        self.simulator.go(5)
163        self.assertEqual('child', self.nodes[FED].get_state())
164        self.assertEqual('child', self.nodes[MED].get_state())
165        self.assertEqual('child', self.nodes[SED].get_state())
166
167        # Verify Multicast Routing works for all devices
168        self._verify_multicast_routing(ROUTER, MA1)
169        self._verify_multicast_routing(LEADER, MA2)
170        self._verify_multicast_routing(FED, MA3)
171        self._verify_multicast_routing(MED, MA4, is_med=True)
172        self._verify_multicast_routing(SED, MA5, is_med=True, is_sed=True)
173
174        # Verify MA_scope2 is not reachable from Host
175        MA_scope2 = 'ff02::10'
176        self.nodes[ROUTER].add_ipmaddr(MA_scope2)
177        self.simulator.go(3)
178        self.assertFalse(self.nodes[HOST].ping(MA_scope2, backbone=True, ttl=10))
179        self.nodes[ROUTER].del_ipmaddr(MA_scope2)
180
181        # Verify MA_scope3 is not reachable from Host
182        MA_scope3 = 'ff03::1234:777a:5'
183        self.nodes[ROUTER].add_ipmaddr(MA_scope3)
184        self.simulator.go(3)
185        self.assertFalse(self.nodes[HOST].ping(MA_scope3, backbone=True, ttl=10))
186        self.nodes[ROUTER].del_ipmaddr(MA_scope3)
187
188        # Router subscribes MA2 and MA3 at the same time and verify that they are both reachable
189        self.nodes[ROUTER].add_ipmaddr(MA2)
190        self.nodes[ROUTER].add_ipmaddr(MA3)
191        self.simulator.go(3)
192        self.assertTrue(self.nodes[HOST].ping(MA2, backbone=True, ttl=10))
193        self.assertTrue(self.nodes[HOST].ping(MA3, backbone=True, ttl=10))
194        self.nodes[ROUTER].del_ipmaddr(MA2)
195        self.nodes[ROUTER].del_ipmaddr(MA3)
196        self.simulator.go(1)
197
198    def _verify_multicast_routing(self, nodeid: int, ma: str, is_med=False, is_sed=False):
199        logging.info('_verify_multicast_routing: nodeid=%d, MA=%s', nodeid, ma)
200        # Verify MA is not reachable from Host initially
201        self.assertFalse(self.nodes[HOST].ping(ma, backbone=True, ttl=10))
202
203        # Device subscribes MA
204        self.nodes[nodeid].add_ipmaddr(ma)
205        self.simulator.go(3 + (SED_POLL_PERIOD * 2 / 1000) * is_sed + config.PARENT_AGGREGATIOIN_DELAY * is_med +
206                          WAIT_REDUNDANCE)
207
208        # Verify MA is reachable from Host
209        self.assertTrue(self.nodes[HOST].ping(ma, backbone=True, ttl=10))
210
211        # Device unsubscribes MA
212        self.nodes[nodeid].del_ipmaddr(ma)
213        self.simulator.go(1)
214
215
216if __name__ == '__main__':
217    unittest.main()
218