• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
30import ipaddress
31import logging
32import time
33import unittest
34from typing import Union, List
35
36import config
37import network_layer
38import thread_cert
39
40logging.basicConfig(level=logging.DEBUG)
41
42_, BBR_1, BBR_2, ROUTER_1_2, ROUTER_1_1, SED_1, MED_1, MED_2, FED_1 = range(9)
43
44WAIT_ATTACH = 5
45WAIT_REDUNDANCE = 3
46ROUTER_SELECTION_JITTER = 1
47BBR_REGISTRATION_JITTER = 5
48SED_POLL_PERIOD = 1000  # 1s
49
50REREG_DELAY = 10
51MLR_TIMEOUT = 300
52PARENT_AGGREGATE_DELAY = 5
53
54MA1 = 'ff04::1234:777a:1'
55MA1g = 'ff0e::1234:777a:1'
56MA2 = 'ff05::1234:777a:2'
57MA3 = 'ff0e::1234:777a:3'
58MA4 = 'ff05::1234:777a:4'
59
60MA5 = 'ff03::1234:777a:5'
61MA6 = 'ff02::10'
62"""
63 Initial topology:
64
65   BBR_1---BBR_2
66     |     /   \                       |
67     |    /     \                      |
68   ROUTER_1_2  ROUTER_1_1
69    |     |  \____                     |
70    |     |       \                    |
71  SED_1  MED_1/2  FED_1
72"""
73
74
75class TestMulticastListenerRegistration(thread_cert.TestCase):
76    TOPOLOGY = {
77        BBR_1: {
78            'version': '1.2',
79            'allowlist': [BBR_2, ROUTER_1_2],
80            'is_bbr': True,
81        },
82        BBR_2: {
83            'version': '1.2',
84            'allowlist': [BBR_1, ROUTER_1_2, ROUTER_1_1],
85            'is_bbr': True,
86        },
87        ROUTER_1_2: {
88            'version': '1.2',
89            'allowlist': [BBR_1, BBR_2, SED_1, MED_1, MED_2, FED_1],
90        },
91        ROUTER_1_1: {
92            'version': '1.1',
93            'allowlist': [BBR_2],
94        },
95        MED_1: {
96            'mode': 'rn',
97            'version': '1.2',
98            'allowlist': [ROUTER_1_2],
99            'timeout': config.DEFAULT_CHILD_TIMEOUT,
100        },
101        MED_2: {
102            'mode': 'rn',
103            'version': '1.2',
104            'allowlist': [ROUTER_1_2],
105            'timeout': config.DEFAULT_CHILD_TIMEOUT,
106        },
107        SED_1: {
108            'mode': 'n',
109            'version': '1.2',
110            'allowlist': [ROUTER_1_2],
111            'timeout': config.DEFAULT_CHILD_TIMEOUT,
112        },
113        FED_1: {
114            'mode': 'rdn',
115            'version': '1.2',
116            'allowlist': [ROUTER_1_2],
117            'router_eligible': False,
118            'timeout': config.DEFAULT_CHILD_TIMEOUT,
119        },
120    }
121    """All nodes are created with default configurations"""
122
123    def _bootstrap(self, bbr_1_enable_backbone_router=True, turn_on_bbr_2=True, turn_on_router_1_1=True):
124        assert (turn_on_bbr_2 or not turn_on_router_1_1)  # ROUTER_1_1 needs BBR_2
125
126        # starting context id
127        t0 = time.time()
128        context_id = 1
129
130        # Bring up BBR_1, BBR_1 becomes Leader and Primary Backbone Router
131        self.nodes[BBR_1].set_router_selection_jitter(ROUTER_SELECTION_JITTER)
132        self.nodes[BBR_1].set_bbr_registration_jitter(BBR_REGISTRATION_JITTER)
133
134        self.nodes[BBR_1].set_backbone_router(seqno=1, reg_delay=REREG_DELAY, mlr_timeout=MLR_TIMEOUT)
135        self.nodes[BBR_1].start()
136        WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
137        self.simulator.go(WAIT_TIME * 2)
138        self.assertEqual(self.nodes[BBR_1].get_state(), 'leader')
139
140        if bbr_1_enable_backbone_router:
141            self.nodes[BBR_1].enable_backbone_router()
142            WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
143            self.simulator.go(WAIT_TIME)
144            self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(), 'Primary')
145
146        self.pbbr_seq = 1
147        self.pbbr_id = BBR_1
148
149        if turn_on_bbr_2:
150            # Bring up BBR_2, BBR_2 becomes Router and Secondary Backbone Router
151            self.nodes[BBR_2].set_router_selection_jitter(ROUTER_SELECTION_JITTER)
152            self.nodes[BBR_2].set_bbr_registration_jitter(BBR_REGISTRATION_JITTER)
153            self.nodes[BBR_2].set_backbone_router(seqno=2, reg_delay=REREG_DELAY, mlr_timeout=MLR_TIMEOUT)
154            self.nodes[BBR_2].start()
155            WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
156            self.simulator.go(WAIT_TIME)
157            self.assertEqual(self.nodes[BBR_2].get_state(), 'router')
158            self.nodes[BBR_2].enable_backbone_router()
159            WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
160            self.simulator.go(WAIT_TIME)
161            self.assertEqual(self.nodes[BBR_2].get_backbone_router_state(), 'Secondary')
162
163        self.simulator.set_lowpan_context(context_id, config.DOMAIN_PREFIX)
164        domain_prefix_cid = context_id
165
166        # Bring up ROUTER_1_2
167        self.nodes[ROUTER_1_2].set_router_selection_jitter(ROUTER_SELECTION_JITTER)
168        self.nodes[ROUTER_1_2].start()
169        WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
170        self.simulator.go(WAIT_TIME)
171        self.assertEqual(self.nodes[ROUTER_1_2].get_state(), 'router')
172
173        if turn_on_router_1_1:
174            # Bring up ROUTER_1_1
175            self.nodes[ROUTER_1_1].set_router_selection_jitter(ROUTER_SELECTION_JITTER)
176            self.nodes[ROUTER_1_1].start()
177            WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
178            self.simulator.go(WAIT_TIME)
179            self.assertEqual(self.nodes[ROUTER_1_1].get_state(), 'router')
180
181        # Bring up FED_1
182        self.nodes[FED_1].start()
183        self.simulator.go(WAIT_ATTACH)
184        self.assertEqual(self.nodes[FED_1].get_state(), 'child')
185
186        # Bring up MED_1
187        self.nodes[MED_1].start()
188        self.simulator.go(WAIT_ATTACH)
189        self.assertEqual(self.nodes[MED_1].get_state(), 'child')
190
191        # Bring up MED_2
192        self.nodes[MED_2].start()
193        self.simulator.go(WAIT_ATTACH)
194        self.assertEqual(self.nodes[MED_2].get_state(), 'child')
195
196        # Bring up SED_1
197        self.nodes[SED_1].set_pollperiod(SED_POLL_PERIOD)
198        self.nodes[SED_1].start()
199        self.simulator.go(WAIT_ATTACH)
200        self.assertEqual(self.nodes[SED_1].get_state(), 'child')
201
202        logging.info("bootstrap takes %f seconds", time.time() - t0)
203
204    def test(self):
205        self._bootstrap()
206
207        # Verify MLR.req for each device when parent is 1.2
208        self.__check_mlr_ok(ROUTER_1_2, is_ftd=True)
209        self.__check_mlr_ok(FED_1, is_ftd=True)
210        self.__check_mlr_ok(MED_1, is_ftd=False)
211        self.__check_mlr_ok(SED_1, is_ftd=False)
212
213        # Switch to parent 1.1
214        self.__switch_to_1_1_parent()
215
216        # Verify MLR.req for each device when parent is 1.1
217        self.__check_mlr_ok(FED_1, is_ftd=True, is_parent_1p1=True)
218        self.__check_mlr_ok(MED_1, is_ftd=False, is_parent_1p1=True)
219        self.__check_mlr_ok(SED_1, is_ftd=False, is_parent_1p1=True)
220
221        # Switch to parent 1.2
222        self.__switch_to_1_2_parent()
223
224    def testParentMergeMedMlrReq(self):
225        self._bootstrap()
226
227        # Make sure Parent registers multiple MAs of MED Children in one MLR.req
228        self.__check_parent_merge_med_mlr_req([MED_1, MED_2], ROUTER_1_2)
229
230    def testNotSendMlrReqIfSubscribed(self):
231        self._bootstrap()
232
233        # Make sure Parent does not send MLR.req of Child if it's already subscribed by Netif or other Children
234        self.__check_not_send_mlr_req_if_subscribed([MED_1, MED_2], ROUTER_1_2)
235
236    def testIpmaddrAddBeforeBBREnable(self):
237        self._bootstrap(bbr_1_enable_backbone_router=False, turn_on_bbr_2=False, turn_on_router_1_1=False)
238
239        self.flush_all()
240
241        # Subscribing to MAs when there is no PBBR should not send MLR.req
242        self.nodes[ROUTER_1_2].add_ipmaddr("ff04::1")
243        self.nodes[FED_1].add_ipmaddr("ff04::2")
244        self.nodes[MED_1].add_ipmaddr("ff04::3")
245        self.nodes[SED_1].add_ipmaddr("ff04::4")
246
247        self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
248        router_reg = ["ff04::1", "ff04::2", "ff04::3", "ff04::4"]
249        self.__check_send_mlr_req(ROUTER_1_2, router_reg, should_send=False)
250
251        self.flush_all()
252
253        # Turn on PBBR
254        self.nodes[BBR_1].enable_backbone_router()
255
256        WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
257        self.simulator.go(WAIT_TIME)
258        self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(), 'Primary')
259
260        self.simulator.go(REREG_DELAY)
261        # Expect MLR.req sent by ROUTER_1_2 and FED_1
262        self.__check_send_mlr_req(ROUTER_1_2, router_reg, should_send=True, expect_mlr_rsp=True)
263
264    def testMulticastListenersTableAdd(self):
265        self._bootstrap()
266        self.__test_multicast_listeners_table_add()
267
268    def testMulticastListenersTableExpire(self):
269        self._bootstrap()
270        self.__test_multicast_listeners_table_expire()
271
272    def testMulticastListenersTableFull(self):
273        self._bootstrap()
274        self.__test_multicast_listeners_table_full()
275
276    def testMulticastListenersTableTwoFreeSlot(self):
277        self._bootstrap()
278        self.__test_multicast_listeners_table_two_free_slots()
279
280    def testMulticastListenerTableAPI(self):
281        self._bootstrap()
282        self.__test_multicast_listeners_table_api()
283
284    def testMlrConfigResponse(self):
285        self._bootstrap()
286        self.__test_mlr_config_response()
287
288    def __test_mlr_config_response(self):
289        bbr = self.nodes[BBR_1]
290        router = self.nodes[ROUTER_1_2]
291
292        self.flush_all()
293
294        # Configure next response to 0
295        bbr.set_next_mlr_response(0)
296        router.add_ipmaddr("ff04::1")
297        self.simulator.go(WAIT_REDUNDANCE)
298        self.__check_send_mlr_req(ROUTER_1_2, ["ff04::1"],
299                                  should_send=True,
300                                  expect_mlr_rsp=True,
301                                  expect_mlr_rsp_status=0)
302        self.assertNotIn(ipaddress.IPv6Address("ff04::1"), bbr.multicast_listener_list())
303
304        router.del_ipmaddr("ff04::1")
305        self.simulator.go(WAIT_REDUNDANCE)
306        self.flush_all()
307
308        # Configure next response to 2
309        bbr.set_next_mlr_response(2)
310        router.add_ipmaddr("ff04::2")
311        self.simulator.go(WAIT_REDUNDANCE)
312        self.__check_send_mlr_req(ROUTER_1_2, ["ff04::2"],
313                                  should_send=True,
314                                  expect_mlr_rsp=True,
315                                  expect_mlr_rsp_status=2)
316
317        router.del_ipmaddr("ff04::2")
318        self.simulator.go(WAIT_REDUNDANCE)
319        self.flush_all()
320
321        # Configure next response to 4
322        bbr.set_next_mlr_response(4)
323        router.add_ipmaddr("ff04::4")
324        self.simulator.go(WAIT_REDUNDANCE)
325        self.__check_send_mlr_req(ROUTER_1_2, ["ff04::4"],
326                                  should_send=True,
327                                  expect_mlr_rsp=True,
328                                  expect_mlr_rsp_status=4)
329
330        # The MA should be eventually registered after reregistration
331        self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE)
332        self.assertIn(ipaddress.IPv6Address("ff04::4"), bbr.multicast_listener_list())
333
334        router.del_ipmaddr("ff04::4")
335        self.simulator.go(WAIT_REDUNDANCE)
336        self.flush_all()
337
338    def testCommissionerRegisterMulticastListeners(self):
339        self._bootstrap()
340
341        # Use ROUTER_1_2 as the Commissioner
342        commissioiner = self.nodes[ROUTER_1_2]
343
344        self.assertEqual((0, []), commissioiner.register_multicast_listener("ff04::1"))
345
346        commissioiner.commissioner_start()
347        self.simulator.go(10)
348
349        # Now the Commissioner should be able to register MAs
350        for ip in ["ff04::1", "ff04::2"]:
351            status, failed_ips = commissioiner.register_multicast_listener(ip)
352            self.assertTrue(status == 0 and not failed_ips)
353            self.__check_multicast_listener(ip, expect_mlr_timeout_range=[290, 300])
354
355        # Register existing MA with a new timeout should be able to update the timeout
356        for ip in ["ff04::1", "ff04::2"]:
357            status, failed_ips = commissioiner.register_multicast_listener(ip, timeout=1000)
358            self.assertTrue(status == 0 and not failed_ips)
359            self.__check_multicast_listener(ip, expect_mlr_timeout_range=[990, 1000])
360
361        # Register MAs with given timeouts
362        for ip, timeout in [("ff05::1", 400), ("ff05::2", 500), ("ff05::3", 600)]:
363            status, failed_ips = commissioiner.register_multicast_listener(ip, timeout=timeout)
364            self.assertTrue(status == 0 and not failed_ips)
365            self.__check_multicast_listener(ip, expect_mlr_timeout_range=[timeout - 10, timeout])
366
367        # Register multiple MAs with one call
368        ips = ["ff05::4", "ff05::5", "ff05::6"]
369        status, failed_ips = commissioiner.register_multicast_listener(*ips, timeout=700)
370        self.assertTrue(status == 0 and not failed_ips)
371        for ip in ips:
372            self.__check_multicast_listener(ip, expect_mlr_timeout_range=[690, 700])
373
374        # Register multiple MAs with one call (without timeout)
375        ips = ["ff05::7", "ff05::8", "ff05::9"]
376        status, failed_ips = commissioiner.register_multicast_listener(*ips)
377        self.assertTrue(status == 0 and not failed_ips)
378        for ip in ips:
379            self.__check_multicast_listener(ip, expect_mlr_timeout_range=[290, 300])
380
381        # Unregister MAs using timeout=0
382        for ip in ["ff05::1", "ff05::2", "ff05::3"]:
383            status, failed_ips = commissioiner.register_multicast_listener(ip, timeout=0)
384            self.assertTrue(status == 0 and not failed_ips)
385            self.__check_multicast_listener(ip, expect_not_present=True)
386
387        # Unregister multiple MAs
388        ips = ["ff05::4", "ff05::5", "ff05::6"]
389        status, failed_ips = commissioiner.register_multicast_listener(*ips, timeout=0)
390        self.assertTrue(status == 0 and not failed_ips)
391        self.__check_multicast_listener(ip, expect_not_present=True)
392
393        # Remove MAs that are not subscribed should not fail
394        ips = ["ff06::1", "ff02::1", "2001::1"]
395        status, failed_ips = commissioiner.register_multicast_listener(*ips, timeout=0)
396        self.assertTrue(status == 0 and not failed_ips)
397        self.__check_multicast_listener(ip, expect_not_present=True)
398
399        # Register invalid MAs should fail
400        for ip in ["ff02::1", "ff03::1", "2001::1"]:
401            status, failed_ips = commissioiner.register_multicast_listener(ip)
402            self.assertEqual(status, 2)
403            self.assertEqual(set(failed_ips), {ipaddress.IPv6Address(ip)})
404            self.__check_multicast_listener(ip, expect_not_present=True)
405
406        # Register valid MAs with invalid MAs should succeed partially
407        ips = ["ff05::1", "ff05::2", "ff02::1", "2001::1"]
408        status, failed_ips = commissioiner.register_multicast_listener(*ips)
409        self.assertEqual(status, 2)
410        self.assertEqual(set(failed_ips), {ipaddress.IPv6Address("ff02::1"), ipaddress.IPv6Address("2001::1")})
411        self.__check_multicast_listener("ff05::1")
412        self.__check_multicast_listener("ff05::2")
413        self.__check_multicast_listener("ff02::1", expect_not_present=True)
414        self.__check_multicast_listener("2001::1", expect_not_present=True)
415
416        # Registering persistent MAs should fail for now
417        status, failed_ips = commissioiner.register_multicast_listener("ff06::1", timeout=0xffffffff)
418        self.assertEqual(status, 3)
419        # "ff06::1" should not be included in failed IPs because all IPs failed
420        self.assertTrue(not failed_ips == 0)
421
422    def __check_multicast_listener(self, *addrs, expect_mlr_timeout_range=None, expect_not_present=False):
423        addrs = map(ipaddress.IPv6Address, addrs)
424        listeners = self.nodes[BBR_1].multicast_listener_list()
425        logging.info("__check_multicast_listener get listeners: %s", listeners)
426
427        for addr in addrs:
428            if not expect_not_present:
429                self.assertIn(addr, listeners)
430                if expect_mlr_timeout_range is not None:
431                    self.assertGreaterEqual(listeners[addr], expect_mlr_timeout_range[0])
432                    self.assertLessEqual(listeners[addr], expect_mlr_timeout_range[1])
433            else:
434                self.assertNotIn(addr, listeners)
435
436    def __test_multicast_listeners_table_api(self):
437        self.assertTrue(self.nodes[BBR_1].multicast_listener_list() == {})
438
439        self.nodes[BBR_1].multicast_listener_add("ff04::1")
440        self.assertEqual(1, len(self.nodes[BBR_1].multicast_listener_list()))
441        self.nodes[BBR_1].multicast_listener_add("ff04::2", 300)
442        self.assertEqual(2, len(self.nodes[BBR_1].multicast_listener_list()))
443        self.nodes[BBR_1].multicast_listener_clear()
444        self.assertTrue(self.nodes[BBR_1].multicast_listener_list() == {})
445
446    def __test_multicast_listeners_table_add(self):
447        self.assertTrue(self.nodes[BBR_1].multicast_listener_list() == {})
448
449        all_mas = set()
450
451        CHECK_LIST = [(ROUTER_1_2, "ff04::1"), (FED_1, "ff04::2"), (MED_1, "ff04::3"), (SED_1, "ff04::4")]
452
453        for id, ip in CHECK_LIST:
454            self.nodes[id].add_ipmaddr(ip)
455            self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
456            all_mas.add(ipaddress.IPv6Address(ip))
457            self.assertEqual(all_mas, set(self.nodes[BBR_1].multicast_listener_list().keys()))
458
459        # restore
460        for id, ip in CHECK_LIST:
461            self.nodes[id].del_ipmaddr(ip)
462
463        self.simulator.go(WAIT_REDUNDANCE)
464
465    def __test_multicast_listeners_table_expire(self):
466        self.assertEqual({}, self.nodes[BBR_1].multicast_listener_list())
467
468        all_mas = set()
469
470        CHECK_LIST = [(ROUTER_1_2, "ff04::1"), (FED_1, "ff04::2"), (MED_1, "ff04::3"), (SED_1, "ff04::4")]
471
472        for id, ip in CHECK_LIST:
473            self.nodes[id].add_ipmaddr(ip)
474            self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
475            all_mas.add(ipaddress.IPv6Address(ip))
476            self.assertEqual(all_mas, set(self.nodes[BBR_1].multicast_listener_list().keys()))
477
478        # remove MAs from nodes, and wait for Multicast Listeners to expire on BBR_1
479        for id, ip in CHECK_LIST:
480            self.nodes[id].del_ipmaddr(ip)
481
482        # Wait for MLR_TIMEOUT/3, and expect Multicast Listeners not to expire.
483        self.simulator.go(MLR_TIMEOUT / 3)
484        self.assertEqual(all_mas, set(self.nodes[BBR_1].multicast_listener_list().keys()))
485
486        # Wait for MLR_TIMEOUT*2/3, and expect all Multicast Listeners to expire.
487        self.simulator.go(MLR_TIMEOUT * 2 / 3 + WAIT_REDUNDANCE)
488        self.assertEqual({}, self.nodes[BBR_1].multicast_listener_list())
489
490    def __test_multicast_listeners_table_full(self):
491        self.assertTrue(self.nodes[BBR_1].multicast_listener_list() == {})
492
493        table = set()
494
495        # Add to Multicast Listeners Table until it's full
496        for i in range(1, 76):
497            self.nodes[BBR_1].multicast_listener_add(f"ff04::{i}")
498            table.add(ipaddress.IPv6Address(f"ff04::{i}"))
499            self.assertEqual(table, set(self.nodes[BBR_1].multicast_listener_list().keys()))
500
501        # Add when Multicast Listeners Table is full should not succeed
502        self.nodes[BBR_1].multicast_listener_add(f"ff05::1")
503        self.assertEqual(table, set(self.nodes[BBR_1].multicast_listener_list().keys()))
504
505        self.flush_all()
506
507        # Expect PBBR to respond with MLR_NO_RESOURCES Multicast Listeners Table when it's full
508        self.nodes[ROUTER_1_2].add_ipmaddr("ff06::1")
509        self.simulator.go(WAIT_REDUNDANCE)
510
511        self.__check_send_mlr_req(ROUTER_1_2,
512                                  "ff06::1",
513                                  should_send=True,
514                                  expect_mlr_rsp=True,
515                                  expect_mlr_rsp_status=4)
516
517        self.assertEqual(table, set(self.nodes[BBR_1].multicast_listener_list().keys()))
518
519        self.nodes[MED_1].add_ipmaddr("ff06::2")
520        self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
521
522        self.__check_send_mlr_req(ROUTER_1_2,
523                                  "ff06::2",
524                                  should_send=True,
525                                  expect_mlr_rsp=True,
526                                  expect_mlr_rsp_status=4)
527
528        self.assertEqual(table, set(self.nodes[BBR_1].multicast_listener_list().keys()))
529
530        # the ROUTER_1_2 should be resending both ff06::1 and ff06::2
531        for i in range(3):
532            self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE)
533            self.__check_send_mlr_req(ROUTER_1_2, ['ff06::1', 'ff06::2'],
534                                      should_send=True,
535                                      expect_mlr_rsp=True,
536                                      expect_mlr_rsp_status=4)
537
538        # Restore
539        self.nodes[ROUTER_1_2].del_ipmaddr("ff06::1")
540        self.nodes[MED_1].del_ipmaddr("ff06::2")
541        self.simulator.go(WAIT_REDUNDANCE)
542
543    def __test_multicast_listeners_table_two_free_slots(self):
544
545        # Add to Multicast Listeners Table until there is only two free slots
546        for i in range(1, 74):
547            self.nodes[BBR_1].multicast_listener_add(f"ff04::{i}")
548
549        self.assertEqual(73, len(self.nodes[BBR_1].multicast_listener_list()))
550
551        self.nodes[MED_1].add_ipmaddr("ff05::1")
552        self.nodes[MED_1].add_ipmaddr("ff05::2")
553        self.nodes[MED_2].add_ipmaddr("ff05::3")
554        self.nodes[MED_2].add_ipmaddr("ff05::4")
555        self.nodes[SED_1].add_ipmaddr("ff05::5")
556        self.nodes[SED_1].add_ipmaddr("ff05::6")
557
558        self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
559        self.__check_send_mlr_req(ROUTER_1_2, ["ff05::1", "ff05::2", "ff05::3", "ff05::4", "ff05::5", "ff05::6"])
560
561        self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
562        self.flush_all()
563        # two addresses should be registered, others can not
564        for i in range(3):
565            self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
566            self.assertEqual(4, len(set(self.__get_registered_MAs(ROUTER_1_2))))
567
568        # Restore
569        self.nodes[MED_1].del_ipmaddr("ff05::1")
570        self.nodes[MED_1].del_ipmaddr("ff05::2")
571        self.nodes[MED_2].del_ipmaddr("ff05::3")
572        self.nodes[MED_2].del_ipmaddr("ff05::4")
573        self.nodes[SED_1].del_ipmaddr("ff05::5")
574        self.nodes[SED_1].del_ipmaddr("ff05::6")
575
576    def __check_mlr_ok(self, id, is_ftd, is_parent_1p1=False):
577        """Check if MLR works for the node"""
578        # Add MA1 and send MLR.req
579        logging.info("======== checking MLR: Node%d (%s), Parent=%s ========" %
580                     (id, 'FTD' if is_ftd else 'MTD', '1.1' if is_parent_1p1 else '1.2'))
581        expect_mlr_req = is_ftd or is_parent_1p1
582
583        if id == ROUTER_1_2:
584            parent_id = None
585        else:
586            parent_id = ROUTER_1_1 if is_parent_1p1 else ROUTER_1_2
587
588        for addr in [MA1, MA1g, MA2, MA3, MA4]:
589            self.__check_ipmaddr_add(id,
590                                     parent_id,
591                                     addr,
592                                     expect_mlr_req=expect_mlr_req,
593                                     expect_mlr_req_proxied=(not expect_mlr_req))
594
595        for addr in [MA5, MA6]:
596            self.__check_ipmaddr_add(id, parent_id, addr, expect_mlr_req=False, expect_mlr_req_proxied=False)
597        logging.info('=' * 120)
598
599    def __check_ipmaddr_add(self, id, parent_id, addr, expect_mlr_req=True, expect_mlr_req_proxied=False):
600        """Check MLR works for the added multicast address"""
601        logging.info("Node %d: ipmaddr %s" % (id, addr))
602        self.flush_all()
603        self.nodes[id].add_ipmaddr(addr)
604        self.assertTrue(self.nodes[id].has_ipmaddr(addr))
605        self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
606
607        self.__check_send_mlr_req(id, addr, should_send=expect_mlr_req, expect_mlr_rsp=expect_mlr_req)
608        # Parent should either forward or proxy the MLR.req
609        if parent_id:
610            self.__check_send_mlr_req(parent_id,
611                                      addr,
612                                      should_send=expect_mlr_req or expect_mlr_req_proxied,
613                                      expect_mlr_rsp=expect_mlr_req_proxied)
614
615        self.__check_rereg(id,
616                           parent_id,
617                           addr,
618                           expect_mlr_req=expect_mlr_req,
619                           expect_mlr_req_proxied=expect_mlr_req_proxied)
620        self.__check_renewing(id,
621                              parent_id,
622                              addr,
623                              expect_mlr_req=expect_mlr_req,
624                              expect_mlr_req_proxied=expect_mlr_req_proxied)
625
626        self.nodes[id].del_ipmaddr(addr)
627        self.simulator.go(1)
628
629    def __check_send_mlr_req(self,
630                             id,
631                             addrs: Union[List[str], str],
632                             should_send=True,
633                             expect_mlr_rsp=False,
634                             expect_mlr_rsp_status=0,
635                             expect_mlr_req_num=None,
636                             expect_unique_reg=False):
637        if isinstance(addrs, str):
638            addrs = [addrs]
639
640        message_ids = []
641        reg_mas = self.__get_registered_MAs(id, expect_mlr_req_num=expect_mlr_req_num, message_ids=message_ids)
642        if should_send:
643            for addr in addrs:
644                self.assertIn(ipaddress.IPv6Address(addr), reg_mas)
645                if expect_unique_reg:
646                    self.assertEqual(1, reg_mas.count(ipaddress.IPv6Address(addr)))
647
648            # BBR should send MLR.rsp ACK
649            if expect_mlr_rsp:
650                message_id = message_ids[0]
651                rsp = self.__expect_MLR_rsp(message_id)
652
653                logging.info('MLR.rsp %s uri_path=%s, payload=%s', rsp, rsp.coap.uri_path, rsp.coap.payload)
654
655                statusTlv = None
656                for tlv in rsp.coap.payload:
657                    if isinstance(tlv, network_layer.Status):
658                        statusTlv = tlv
659                        break
660
661                self.assertIsNotNone(statusTlv)
662                self.assertEqual(expect_mlr_rsp_status, statusTlv.status)
663
664        else:
665            for addr in addrs:
666                self.assertNotIn(ipaddress.IPv6Address(addr), reg_mas)
667
668    def __expect_MLR_rsp(self, message_id):
669        logging.info("Expecting MLR.rsp with message ID = %s", message_id)
670        messages = self.simulator.get_messages_sent_by(self.pbbr_id)
671        logging.info("PBBR %d messages: %s", self.pbbr_id, messages)
672
673        while True:
674            msg = messages.next_coap_message('2.04')
675            logging.info('Check ACK for %s: %s, %s, %s, %s', message_id, msg, msg.coap.message_id, msg.coap.uri_path,
676                         msg.coap.payload)
677            if msg.coap.message_id == message_id:
678                return msg
679
680    def __get_registered_MAs(self, id, expect_mlr_req_num=None, message_ids=None):
681        """Get MAs registered via MLR.req by the node"""
682        messages = self.simulator.get_messages_sent_by(id)
683        reg_mas = []
684        while True:
685            msg = messages.next_coap_message('0.02', '/n/mr', assert_enabled=False)
686            if not msg:
687                break
688
689            logging.info("MLR.req: %s %s" % (msg.coap.message_id, msg.coap.payload))
690            addrs = msg.get_coap_message_tlv(network_layer.IPv6Addresses)
691            reg_mas.append(addrs)
692            if message_ids is not None:
693                message_ids.append(msg.coap.message_id)
694
695        logging.info('Node %d registered MAs: %s' % (id, reg_mas))
696
697        if expect_mlr_req_num is not None:
698            self.assertEqual(len(reg_mas), expect_mlr_req_num)
699
700        # expand from [[...], [...], ...] to [...]
701        reg_mas = [ma for mas in reg_mas for ma in mas]
702
703        return reg_mas
704
705    def __check_renewing(self, id, parent_id, addr, expect_mlr_req=True, expect_mlr_req_proxied=False):
706        """Check if MLR works that a node can renew it's registered MAs"""
707        assert self.pbbr_id == BBR_1
708        self.flush_all()
709        self.simulator.go(MLR_TIMEOUT + WAIT_REDUNDANCE)
710
711        self.__check_send_mlr_req(id, addr, should_send=expect_mlr_req, expect_mlr_rsp=expect_mlr_req)
712        # Parent should either forward or proxy the MLR.req
713        if parent_id:
714            self.__check_send_mlr_req(parent_id,
715                                      addr,
716                                      should_send=expect_mlr_req or expect_mlr_req_proxied,
717                                      expect_mlr_rsp=expect_mlr_req_proxied)
718
719    def __check_rereg(self, id, parent_id, addr, expect_mlr_req=True, expect_mlr_req_proxied=False):
720        """Check if MLR works that a node can do MLR reregistration when necessary"""
721        self.__check_rereg_seqno(id,
722                                 parent_id,
723                                 addr,
724                                 expect_mlr_req=expect_mlr_req,
725                                 expect_mlr_req_proxied=expect_mlr_req_proxied)
726        self.__check_rereg_pbbr_change(id,
727                                       parent_id,
728                                       addr,
729                                       expect_mlr_req=expect_mlr_req,
730                                       expect_mlr_req_proxied=expect_mlr_req_proxied)
731
732    def __check_rereg_seqno(self, id, parent_id, addr, expect_mlr_req=True, expect_mlr_req_proxied=False):
733        """Check if MLR works that a node can do MLR reregistration when PBBR seqno changes"""
734        # Change seq on PBBR and expect MLR.req within REREG_DELAY
735        self.flush_all()
736        self.pbbr_seq = (self.pbbr_seq + 10) % 256
737        self.nodes[BBR_1].set_backbone_router(seqno=self.pbbr_seq)
738        self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE)
739
740        self.__check_send_mlr_req(id, addr, should_send=expect_mlr_req, expect_mlr_rsp=expect_mlr_req)
741        # Parent should either forward or proxy the MLR.req
742        if parent_id:
743            self.__check_send_mlr_req(parent_id,
744                                      addr,
745                                      should_send=expect_mlr_req or expect_mlr_req_proxied,
746                                      expect_mlr_rsp=expect_mlr_req_proxied)
747
748    def __check_rereg_pbbr_change(self, id, parent_id, addr, expect_mlr_req=True, expect_mlr_req_proxied=False):
749        """Check if MLR works that a node can do MLR reregistration when PBBR changes"""
750        # Make BBR_2 to be Primary and expect MLR.req within REREG_DELAY
751        assert self.pbbr_id == BBR_1
752
753        self.flush_all()
754        self.nodes[BBR_1].disable_backbone_router()
755        self.simulator.go(BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE)
756        self.assertEqual(self.nodes[BBR_2].get_backbone_router_state(), 'Primary')
757        self.pbbr_id = BBR_2
758
759        self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE)
760
761        self.__check_send_mlr_req(id, addr, should_send=expect_mlr_req, expect_mlr_rsp=expect_mlr_req)
762        # Parent should either forward or proxy the MLR.req
763        if parent_id:
764            self.__check_send_mlr_req(parent_id,
765                                      addr,
766                                      should_send=expect_mlr_req or expect_mlr_req_proxied,
767                                      expect_mlr_rsp=expect_mlr_req_proxied)
768
769        # Restore BBR_1 to be Primary and BBR_2 to be Secondary
770        self.nodes[BBR_2].disable_backbone_router()
771        self.nodes[BBR_1].enable_backbone_router()
772        self.simulator.go(BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE)
773        self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(), 'Primary')
774        self.nodes[BBR_2].enable_backbone_router()
775        self.simulator.go(BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE)
776        self.assertEqual(self.nodes[BBR_2].get_backbone_router_state(), 'Secondary')
777        self.pbbr_id = BBR_1
778
779    def __switch_to_1_1_parent(self):
780        """Check if MLR works when nodes are switching to a 1.1 parent"""
781        # Add MA1 to EDs to prepare for parent switching
782        logging.info("=" * 10 + " switching to 1.1 parent " + '=' * 10)
783
784        self.flush_all()
785
786        self.nodes[FED_1].add_ipmaddr(MA1)
787        self.nodes[MED_1].add_ipmaddr(MA1)
788        self.nodes[SED_1].add_ipmaddr(MA1)
789
790        self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE)
791        self.assertIn(ipaddress.IPv6Address(MA1), self.__get_registered_MAs(FED_1))
792        self.assertNotIn(ipaddress.IPv6Address(MA1), self.__get_registered_MAs(MED_1))
793        self.assertNotIn(ipaddress.IPv6Address(MA1), self.__get_registered_MAs(SED_1))
794
795        self.flush_all()
796
797        # Turn off Router 1.2 and turn on Router 1.1
798        self.nodes[ROUTER_1_2].stop()
799        for id in [FED_1, MED_1, SED_1]:
800            self.nodes[ROUTER_1_1].add_allowlist(self.nodes[id].get_addr64())
801            self.nodes[id].add_allowlist(self.nodes[ROUTER_1_1].get_addr64())
802            self.simulator.go(config.DEFAULT_CHILD_TIMEOUT + WAIT_REDUNDANCE)
803
804            self.assertEqual(self.nodes[id].get_state(), 'child')
805            self.assertEqual(self.nodes[id].get_router_id(), self.nodes[ROUTER_1_1].get_router_id())
806
807        self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE)
808
809        # Verify all FED send MLR.req within REREG_DELAY when parent is 1.1
810        self.assertIn(ipaddress.IPv6Address(MA1), self.__get_registered_MAs(FED_1))
811        self.assertIn(ipaddress.IPv6Address(MA1), self.__get_registered_MAs(MED_1))
812        self.assertIn(ipaddress.IPv6Address(MA1), self.__get_registered_MAs(SED_1))
813
814        self.nodes[FED_1].del_ipmaddr(MA1)
815        self.nodes[MED_1].del_ipmaddr(MA1)
816        self.nodes[SED_1].del_ipmaddr(MA1)
817
818        self.simulator.go(WAIT_REDUNDANCE)
819
820    def __switch_to_1_2_parent(self):
821        """Check if MLR works when nodes are switching to a 1.2 parent"""
822        # Add MA1 to EDs to prepare for parent switching
823        logging.info("=" * 10, "switching to 1.2 parent", '=' * 10)
824
825        self.flush_all()
826
827        self.nodes[FED_1].add_ipmaddr(MA1)
828        self.nodes[MED_1].add_ipmaddr(MA1)
829        self.nodes[SED_1].add_ipmaddr(MA1)
830
831        self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE)
832        self.assertIn(ipaddress.IPv6Address(MA1), self.__get_registered_MAs(FED_1))
833        self.assertIn(ipaddress.IPv6Address(MA1), self.__get_registered_MAs(MED_1))
834        self.assertIn(ipaddress.IPv6Address(MA1), self.__get_registered_MAs(SED_1))
835
836        self.flush_all()
837
838        # Turn off Router 1.1 and turn on Router 1.2
839        self.nodes[ROUTER_1_1].stop()
840        self.nodes[ROUTER_1_2].start()
841        for id in [FED_1, MED_1, SED_1]:
842            self.simulator.go(config.DEFAULT_CHILD_TIMEOUT + WAIT_REDUNDANCE)
843
844            self.assertEqual(self.nodes[id].get_state(), 'child')
845            self.assertEqual(self.nodes[id].get_router_id(), self.nodes[ROUTER_1_2].get_router_id())
846
847        self.simulator.go(REREG_DELAY + WAIT_REDUNDANCE)
848
849        # Verify only FTD sends MLR.req within REREG_DELAY when parent is 1.2
850        self.assertIn(ipaddress.IPv6Address(MA1), self.__get_registered_MAs(FED_1))
851
852        # MED and SED might still send MLR.req during this period because it could be sending to it's 1.2 parent.
853
854        self.nodes[FED_1].del_ipmaddr(MA1)
855        self.nodes[MED_1].del_ipmaddr(MA1)
856        self.nodes[SED_1].del_ipmaddr(MA1)
857
858        self.simulator.go(WAIT_REDUNDANCE)
859
860    def __check_parent_merge_med_mlr_req(self, meds, parent_id):
861        """Check that the 1.2 parent merge multiple multicast addresses for MED children."""
862        self.flush_all()
863        for med in meds:
864            self.nodes[med].add_ipmaddr(MA1)
865
866        self.nodes[meds[0]].add_ipmaddr(MA2)
867        self.nodes[meds[1]].add_ipmaddr(MA3)
868
869        self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
870
871        self.__check_send_mlr_req(parent_id, [MA1, MA2, MA3],
872                                  should_send=True,
873                                  expect_mlr_rsp=True,
874                                  expect_mlr_req_num=1,
875                                  expect_unique_reg=True)
876
877        # restore
878        self.nodes[meds[0]].del_ipmaddr(MA2)
879        self.nodes[meds[1]].del_ipmaddr(MA3)
880        for med in meds:
881            self.nodes[med].del_ipmaddr(MA1)
882
883        self.simulator.go(WAIT_REDUNDANCE)
884
885    def __check_not_send_mlr_req_if_subscribed(self, meds, parent_id):
886        """Check that the 1.2 parent does not send MLR.req if the MA is already subscribed."""
887        # Parent should register MA1 on Netif
888        self.flush_all()
889        self.nodes[parent_id].add_ipmaddr(MA1)
890        self.simulator.go(WAIT_REDUNDANCE)
891        self.__check_send_mlr_req(parent_id, MA1, should_send=True, expect_mlr_rsp=True)
892
893        # Parent should not register MA1 of Child 1 because it's already registerd
894        self.flush_all()
895        self.nodes[meds[0]].add_ipmaddr(MA1)
896        self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
897        self.__check_send_mlr_req(parent_id, MA1, should_send=False)
898
899        # Parent should register MA2 of Child 1 because it's new
900        self.flush_all()
901        self.nodes[meds[0]].add_ipmaddr(MA2)
902        self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
903        self.__check_send_mlr_req(parent_id, MA2, should_send=True)
904
905        # Parent should not register MA2 of Child 2 because it's already registered for Child 1
906        self.flush_all()
907        self.nodes[meds[1]].add_ipmaddr(MA2)
908        self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
909        self.__check_send_mlr_req(parent_id, MA2, should_send=False)
910
911        # Parent should register MA3 of Child 2 because it's new
912        self.flush_all()
913        self.nodes[meds[1]].add_ipmaddr(MA3)
914        self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
915        self.__check_send_mlr_req(parent_id, MA3, should_send=True)
916
917        # Parent should not register MA2 and MA3 because they are already registered for Child2 itself
918        self.flush_all()
919        self.nodes[meds[1]].del_ipmaddr(MA2)
920        self.nodes[meds[1]].del_ipmaddr(MA3)
921        self.nodes[meds[1]].add_ipmaddr(MA2)
922        self.nodes[meds[1]].add_ipmaddr(MA3)
923        self.simulator.go(PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE)
924        self.__check_send_mlr_req(parent_id, [MA2, MA3], should_send=False)
925
926        # Restore
927        self.nodes[parent_id].del_ipmaddr(MA1)
928        self.nodes[meds[0]].del_ipmaddr(MA1)
929        self.nodes[meds[0]].del_ipmaddr(MA2)
930        self.nodes[meds[1]].del_ipmaddr(MA2)
931        self.nodes[meds[1]].del_ipmaddr(MA3)
932        self.simulator.go(WAIT_REDUNDANCE)
933
934
935if __name__ == '__main__':
936    unittest.main()
937