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 unittest
31
32import command
33import config
34import ipaddress
35import mle
36import thread_cert
37
38BBR_1 = 1  # Collapsed with Leader Role
39ROUTER_1_1 = 2
40ROUTER_1_2 = 3
41MED_1_2 = 4
42SED_1_2 = 5
43
44WAIT_ATTACH = 5
45WAIT_REDUNDANCE = 3
46ROUTER_SELECTION_JITTER = 1
47BBR_REGISTRATION_JITTER = 5
48SED_POLL_PERIOD = 2000  # 2s
49MED_TIMEOUT = 20  # 20s
50
51DUA_IID_MANUAL1 = '4444333322221111'
52DUA_IID_MANUAL2 = '5555444433332222'
53
54TEST_PREFIX1 = '2001:0:0:1::/64'
55TEST_PREFIX2 = '2001:0:0:2::/64'
56TEST_PREFIX3 = '2001:0:0:3::/64'
57"""
58 Topology
59
60
61   SED_1_2
62     |
63     |
64 ROUTER_1_1         MED_1_2
65     |                 |
66     |                 |
67 BBR_1 (LEADER) --- ROUTER_1_2
68
69
70 1) Bring up BBR_1, BBR_1 becomes Leader and Primary Backbone Router, with Domain
71    Prefix without `P_slaac`.
72
73 2) Bring up ROUTER_1_1, no DUA was added due to that `P_slaac` flag is not set.
74
75 3) Bring up ROUTER_1_2, verify that it has DUA generated.
76
77 4) Bring up MED_1_2 with DUA_IID_MANUAL1 set in advance, verify
78    a) DUA_IID_MANUAL1 is registered in Address Registration TLV via Child Update Request.
79    b) Remove DUA_IID_MANUAL1, a new DUA generated via SLAAC would be registered in Address
80       Registration TLV via Child Update Request.
81    c) Set DUA_IID_MANUAL2 which should override the generated one and be registered in Address
82       Registration TLV via Child Update Request.
83    d) Remove DUA_IID_MANUAL2, a new DUA generated via SLAAC, the same as in above b) would
84       be registered in Address Registration TLV via  Child Update Request.
85
86 5) Change BBR_1 from config.DOMAIN_PREFIX to config.DOMAIN_PRFIX_ALTER. Verify that MED_1_2
87    generate a new Interface Identifier different from the one generated in 4d) due to the
88    Domain Prefix change.
89
90 6) Recover config.Domain_Prefix on BBR_1. Verify that MED_1_2 generates and registers the same
91    DUA as in step 4b).
92
93 7) Configure ROUTER_1_1 as Border Router with 3 SLAAC prefixes, verify MED_1_2 would register
94    its DUA in Address Registration TLV.
95
96 8) Bring up SED_1_2, verify it generates one DUA, and registers it to its parent, though the parent
97    is a Thread 1.1 device.
98
99"""
100
101
102class TestDomainUnicastAddress(thread_cert.TestCase):
103    TOPOLOGY = {
104        BBR_1: {
105            'version': '1.2',
106            'allowlist': [ROUTER_1_1, ROUTER_1_2],
107            'is_bbr': True
108        },
109        ROUTER_1_1: {
110            'version': '1.1',
111            'allowlist': [BBR_1, SED_1_2]
112        },
113        ROUTER_1_2: {
114            'version': '1.2',
115            'allowlist': [BBR_1, MED_1_2]
116        },
117        MED_1_2: {
118            'mode': 'rn',
119            'version': '1.2',
120            'allowlist': [ROUTER_1_2],
121        },
122        SED_1_2: {
123            'mode': 'n',
124            'version': '1.2',
125            'allowlist': [ROUTER_1_1],
126        },
127    }
128    """All nodes are created with default configurations"""
129
130    def __get_iid(self, address):
131        ''' Get the interface identifier of an IPv6 address.
132
133        Args:
134            address (string): An IPv6 address;
135        '''
136        return ''.join(ipaddress.ip_address(address).exploded.split(':')[4:])
137
138    def __check_dua_registration(self, node, iid, dp_cid):
139        ''' Check whether or not the specified Domain Unicast Address is registered in Address
140        Registraion TLV.
141
142        Args:
143            node (int) : The device id
144            iid (string): The Interface Identifier
145            dp_cid (int): The context id of the domain prefix.
146        '''
147
148        messages = self.simulator.get_messages_sent_by(node)
149        msg = messages.next_mle_message(mle.CommandType.CHILD_UPDATE_REQUEST)
150        command.check_compressed_address_registration_tlv(msg, dp_cid, iid, cid_present_once=True)
151
152    def test(self):
153        # starting context id
154        context_id = 1
155
156        # 1) Bring up BBR_1, BBR_1 becomes Leader and Primary Backbone Router, with Domain
157        #    Prefix without `P_slaac`.
158        self.nodes[BBR_1].set_router_selection_jitter(ROUTER_SELECTION_JITTER)
159        self.nodes[BBR_1].set_bbr_registration_jitter(BBR_REGISTRATION_JITTER)
160        self.nodes[BBR_1].set_backbone_router(seqno=1)
161        self.nodes[BBR_1].start()
162        WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
163        self.simulator.go(WAIT_TIME * 2)
164        self.assertEqual(self.nodes[BBR_1].get_state(), 'leader')
165        self.nodes[BBR_1].enable_backbone_router()
166        WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
167        self.simulator.go(WAIT_TIME)
168        self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(), 'Primary')
169
170        self.nodes[BBR_1].set_domain_prefix(config.DOMAIN_PREFIX, 'prosD')
171        WAIT_TIME = WAIT_REDUNDANCE
172        self.simulator.go(WAIT_TIME)
173
174        self.simulator.set_lowpan_context(context_id, config.DOMAIN_PREFIX)
175        domain_prefix_cid = context_id
176
177        # 2) Bring up ROUTER_1_1, no DUA was added due to that `P_slaac` flag is not set.
178        self.nodes[ROUTER_1_1].set_router_selection_jitter(ROUTER_SELECTION_JITTER)
179        WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
180        self.nodes[ROUTER_1_1].start()
181        self.simulator.go(WAIT_TIME)
182        self.assertEqual(self.nodes[ROUTER_1_1].get_state(), 'router')
183        dua = self.nodes[ROUTER_1_1].get_addr(config.DOMAIN_PREFIX)
184        assert not dua, 'Error: Unexpected DUA ({})'.format(dua)
185
186        # 3) Bring up ROUTER_1_2, verify that it has DUA generated.
187        self.nodes[ROUTER_1_2].set_router_selection_jitter(ROUTER_SELECTION_JITTER)
188        self.nodes[ROUTER_1_2].start()
189        WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
190        self.simulator.go(WAIT_TIME)
191        self.assertEqual(self.nodes[ROUTER_1_2].get_state(), 'router')
192        dua = self.nodes[ROUTER_1_2].get_addr(config.DOMAIN_PREFIX)
193        assert dua, 'Error: Expected DUA not found'
194        self.assertTrue(self.nodes[BBR_1].ping(dua))
195
196        # 4) Bring up MED_1_2 with DUA_IID_MANUAL1 set in advance
197        self.nodes[MED_1_2].set_dua_iid(DUA_IID_MANUAL1)
198        self.nodes[MED_1_2].set_timeout(MED_TIMEOUT)
199        self.nodes[MED_1_2].start()
200        WAIT_TIME = WAIT_ATTACH
201        self.simulator.go(WAIT_TIME)
202        self.assertEqual(self.nodes[MED_1_2].get_state(), 'child')
203
204        # 4a) DUA_IID_MANUAL1 is registered in Address Registration TLV via Child Update Request.
205        self.__check_dua_registration(MED_1_2, DUA_IID_MANUAL1, domain_prefix_cid)
206
207        # 4b) Remove DUA_IID_MANUAL1, a new DUA generated via SLAAC would be registered in Address
208        #   Registration TLV via Child Update Request.
209
210        # Flush relative message queues.
211        messages = self.simulator.get_messages_sent_by(MED_1_2)
212
213        self.nodes[MED_1_2].clear_dua_iid()
214        WAIT_TIME = MED_TIMEOUT + WAIT_REDUNDANCE
215        self.simulator.go(WAIT_TIME)
216
217        med_1_2_dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
218        assert med_1_2_dua, 'Error: Expected DUA not found'
219
220        med_1_2_dua_iid = self.__get_iid(med_1_2_dua)
221        self.__check_dua_registration(MED_1_2, med_1_2_dua_iid, domain_prefix_cid)
222
223        # 4c) Set DUA_IID_MANUAL2 which should override the generated one and be registered in Address
224        #   Registration TLV via Child Update Request.
225
226        # Flush relative message queues.
227        messages = self.simulator.get_messages_sent_by(MED_1_2)
228        self.nodes[MED_1_2].set_dua_iid(DUA_IID_MANUAL2)
229        WAIT_TIME = WAIT_REDUNDANCE
230        self.simulator.go(WAIT_TIME)
231
232        dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
233
234        self.__check_dua_registration(MED_1_2, DUA_IID_MANUAL2, domain_prefix_cid)
235
236        # 4d) Remove DUA_IID_MANUAL2, a new DUA generated via SLAAC, the same as in above b) would
237        #     be registered in Address Registration TLV via  Child Update Request.
238
239        # Flush relative message queues.
240        messages = self.simulator.get_messages_sent_by(MED_1_2)
241        self.nodes[MED_1_2].clear_dua_iid()
242        WAIT_TIME = WAIT_REDUNDANCE
243        self.simulator.go(WAIT_TIME)
244        dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
245        assert ipaddress.ip_address(dua) == ipaddress.ip_address(
246            med_1_2_dua), 'Error: Expected SLAAC DUA not generated'
247        assert ipaddress.ip_address(med_1_2_dua) == ipaddress.ip_address(
248            dua), 'Error: Expected same SLAAC DUA not generated'
249
250        self.__check_dua_registration(MED_1_2, med_1_2_dua_iid, domain_prefix_cid)
251
252        # 5) Change BBR_1 from config.DOMAIN_PREFIX to config.DOMAIN_PRFIX_ALTER. Verify that MED_1_2
253        #   generates a new Interface Identifier different from the one generated in 4d) due to the
254        #   Domain Prefix change.
255        context_id += 1
256        self.simulator.set_lowpan_context(context_id, config.DOMAIN_PREFIX_ALTER)
257        self.nodes[BBR_1].set_domain_prefix(config.DOMAIN_PREFIX_ALTER)
258        WAIT_TIME = WAIT_REDUNDANCE
259        self.simulator.go(WAIT_TIME)
260
261        med_1_2_dua2 = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX_ALTER)
262        med_1_2_dua2_iid = self.__get_iid(med_1_2_dua2)
263        self.__check_dua_registration(MED_1_2, med_1_2_dua2_iid, context_id)
264
265        #6) Recover config.Domain_Prefix on BBR_1. Verify that MED_1_2 generates and registers the same
266        #   DUA as in step 4b).
267        self.nodes[BBR_1].set_domain_prefix(config.DOMAIN_PREFIX)
268        WAIT_TIME = WAIT_REDUNDANCE
269        self.simulator.go(WAIT_TIME)
270        dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
271        assert dua, 'Error: Expected DUA not found'
272        assert ipaddress.ip_address(med_1_2_dua) == ipaddress.ip_address(
273            dua), 'Error: Expected same SLAAC DUA not generated'
274
275        self.__check_dua_registration(MED_1_2, med_1_2_dua_iid, domain_prefix_cid)
276
277        #7) Configure ROUTER_1_1 as Border Router with 3 SLAAC prefixes, verify MED_1_2 would register
278        #   its DUA in Address Registration TLV.
279
280        # Flush relative message queues.
281        messages = self.simulator.get_messages_sent_by(MED_1_2)
282
283        context_id += 1
284        self.simulator.set_lowpan_context(context_id, TEST_PREFIX1)
285        self.nodes[ROUTER_1_1].add_prefix(TEST_PREFIX1)
286
287        context_id += 1
288        self.simulator.set_lowpan_context(context_id, TEST_PREFIX2)
289        self.nodes[ROUTER_1_1].add_prefix(TEST_PREFIX2)
290        context_id += 1
291        self.simulator.set_lowpan_context(context_id, TEST_PREFIX3)
292        self.nodes[ROUTER_1_1].add_prefix(TEST_PREFIX3)
293        self.nodes[ROUTER_1_1].register_netdata()
294
295        WAIT_TIME = WAIT_REDUNDANCE
296        self.simulator.go(WAIT_TIME)
297
298        WAIT_TIME = MED_TIMEOUT
299        self.simulator.go(WAIT_TIME)
300        dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
301        assert dua, 'Error: Expected DUA not found'
302        assert ipaddress.ip_address(med_1_2_dua) == ipaddress.ip_address(
303            dua), 'Error: Expected same SLAAC DUA not generated'
304
305        self.__check_dua_registration(MED_1_2, med_1_2_dua_iid, domain_prefix_cid)
306
307        #8) Bring up SED_1_2, verify that it generates one DUA, and registers it to its parent, though the parent
308        #   is a Thread 1.1 device.
309        self.nodes[SED_1_2].set_pollperiod(SED_POLL_PERIOD)
310        self.nodes[SED_1_2].start()
311        WAIT_TIME = WAIT_ATTACH
312        self.simulator.go(WAIT_TIME)
313
314        dua = self.nodes[SED_1_2].get_addr(config.DOMAIN_PREFIX)
315        assert dua, 'Error: Expected DUA not found'
316
317        dua_iid = self.__get_iid(dua)
318        self.__check_dua_registration(SED_1_2, dua_iid, domain_prefix_cid)
319
320        self.assertTrue(self.nodes[BBR_1].ping(dua))
321
322
323if __name__ == '__main__':
324    unittest.main()
325