• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3#  Copyright (c) 2019, 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
33import config
34import mle
35
36LEADER_1_2 = 1
37ROUTER_1_1 = 2
38REED_1_2 = 3
39ROUTER_1_2 = 4
40REED_1_1 = 5
41MED_1_1 = 6
42MED_1_2 = 7
43
44# Topology
45#               (lq:2)  (pp:1)
46#     REED_1_2  ----- ROUTER_1_2
47#        |     \    /     |      \
48#        |       \/    REED_1_1    \
49# (lq:2) |      /  \  /  `router`    \
50#        | (lq:2)    \                 \
51#        |  /      /   \                 \
52#       LEADER_1_2 --- ROUTER_1_1 -- MED_1_2
53#                \        |
54#                  \      |
55#                    \    |
56#                       MED_1_1
57#
58# 1) Bring up LEADER_1_2 and ROUTER_1_1,
59# 2) Config link quality (LEADER_1_2->REED_1_2) as 2, bring up REED_1_2 which would attach to ROUTER_1_1
60#    due to higher two-way link quality,
61# 3) Config link quality(LEADER_1_2->ROUTER_1_2) and link quality(REED_1_2->ROUTER_1_2) as 2, bring up
62#    ROUTER_1_2 which would attach to LEADER_1_2 due to active router is preferred,
63# 4) Config parent priority as 1 on ROUTER_1_2, bring up REED_1_1 which would attach to ROUTER_1_2 due to
64#    higher parent priority,
65# 5) Upgrade REED_1_1 to `router` role, bring up MED_1_1 which would attach to LEADER_1_2 which has higher
66#    link quality of 3,
67# 6) Config parent priority as 1 on ROUTER_1_1, bring up MED_1_2 which would attach to ROUTER_1_2 due to
68#    higher version
69#
70
71
72class TestParentSelection(thread_cert.TestCase):
73    TOPOLOGY = {
74        LEADER_1_2: {
75            'version': '1.2',
76            'allowlist': [REED_1_2, ROUTER_1_2, REED_1_1, ROUTER_1_1, MED_1_1],
77        },
78        ROUTER_1_1: {
79            'version': '1.1',
80            'allowlist': [LEADER_1_2, REED_1_2, MED_1_2, MED_1_1],
81        },
82        REED_1_2: {
83            'version': '1.2',
84            'allowlist': [ROUTER_1_2, ROUTER_1_1, LEADER_1_2],
85        },
86        ROUTER_1_2: {
87            'version': '1.2',
88            'allowlist': [REED_1_2, MED_1_2, REED_1_1, LEADER_1_2],
89        },
90        REED_1_1: {
91            'version': '1.1',
92            'allowlist': [ROUTER_1_2, LEADER_1_2]
93        },
94        MED_1_1: {
95            'mode': 'r',
96            'version': '1.1',
97            'allowlist': [LEADER_1_2, ROUTER_1_1],
98        },
99        MED_1_2: {
100            'mode': 'r',
101            'version': '1.2',
102            'allowlist': [ROUTER_1_1, ROUTER_1_2],
103        },
104    }
105    """All nodes are created with default configurations"""
106
107    def test(self):
108
109        self.nodes[LEADER_1_2].start()
110        self.simulator.go(config.LEADER_STARTUP_DELAY)
111        self.assertEqual(self.nodes[LEADER_1_2].get_state(), 'leader')
112
113        self.nodes[ROUTER_1_1].set_router_selection_jitter(1)
114        self.nodes[ROUTER_1_1].start()
115        self.simulator.go(config.ROUTER_STARTUP_DELAY)
116        self.assertEqual(self.nodes[ROUTER_1_1].get_state(), 'router')
117
118        # Mesh Impacting Criteria - Highest Two-way link quality
119        # REED_1_2 would attach to ROUTER_1_1
120        # Attach to ROUTER_1_1 which has highest two-way link quality
121
122        # Flush relative message queues
123        self.flush_nodes([LEADER_1_2, ROUTER_1_1])
124
125        self.nodes[LEADER_1_2].set_link_quality(self.nodes[REED_1_2].get_addr64(), 2)
126        self.nodes[REED_1_2].set_router_selection_jitter(1)
127        self.nodes[REED_1_2].set_router_upgrade_threshold(1)
128        self.nodes[REED_1_2].start()
129        self.simulator.go(5)
130        self.assertEqual(self.nodes[REED_1_2].get_state(), 'child')
131
132        # Check Parent Response
133        messages = self.simulator.get_messages_sent_by(ROUTER_1_1)
134        parent_prefer = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
135        assert (parent_prefer), "Error: Expected parent response not found"
136
137        messages = self.simulator.get_messages_sent_by(LEADER_1_2)
138        parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
139        assert (parent_cmp), "Error: Expected parent response not found"
140
141        # Known that link margin for link quality 3 is 80 and link quality 2 is 15
142        self.assertGreater((parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin -
143                            parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin), 20)
144
145        # Check Child Id Request
146        messages = self.simulator.get_messages_sent_by(REED_1_2)
147        msg = messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
148        msg.assertSentToNode(self.nodes[ROUTER_1_1])
149
150        # Mesh Impacting Criteria - Active Routers over REEDs
151        # ROUTER_1_2 would attach to LEADER_1_2
152        # Link quality configuration, so that REED_1_2 has the chance to respond
153
154        # Flush relative message queues
155        self.flush_nodes([LEADER_1_2, REED_1_2])
156
157        self.nodes[LEADER_1_2].set_link_quality(self.nodes[ROUTER_1_2].get_addr64(), 2)
158        self.nodes[REED_1_2].set_link_quality(self.nodes[ROUTER_1_2].get_addr64(), 2)
159        self.nodes[ROUTER_1_2].set_router_selection_jitter(1)
160        self.nodes[ROUTER_1_2].start()
161        self.simulator.go(config.ROUTER_STARTUP_DELAY)
162        self.assertEqual(self.nodes[ROUTER_1_2].get_state(), 'router')
163
164        # Check Parent Response
165        messages = self.simulator.get_messages_sent_by(LEADER_1_2)
166
167        # Skip first response for first parent request
168        assert messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
169
170        parent_prefer = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
171        assert (parent_prefer), "Error: Expected parent response not found"
172
173        messages = self.simulator.get_messages_sent_by(REED_1_2)
174        parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
175        assert (parent_cmp), "Error: Expected parent response not found"
176
177        self.assertEqual(
178            parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
179            parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
180
181        # Check Child Id Request
182        messages = self.simulator.get_messages_sent_by(ROUTER_1_2)
183        msg = messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
184        msg.assertSentToNode(self.nodes[LEADER_1_2])
185
186        # Mesh Impacting Criteria - Highest Parent Priority value in the Connectivity TLV
187        # REED_1_1 would attach to ROUTER_1_2
188
189        # Flush relative message queues
190        self.flush_nodes([LEADER_1_2, ROUTER_1_2])
191
192        self.nodes[ROUTER_1_2].set_parent_priority(1)
193        self.nodes[REED_1_1].set_router_selection_jitter(1)
194        self.nodes[REED_1_1].set_router_upgrade_threshold(1)
195        self.nodes[REED_1_1].start()
196        self.simulator.go(5)
197        self.assertEqual(self.nodes[REED_1_1].get_state(), 'child')
198
199        # Check Parent Response
200        messages = self.simulator.get_messages_sent_by(ROUTER_1_2)
201        parent_prefer = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
202        assert (parent_prefer), "Error: Expected parent response not found"
203
204        messages = self.simulator.get_messages_sent_by(LEADER_1_2)
205        parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
206        assert (parent_cmp), "Error: Expected parent response not found"
207
208        self.assertEqual(
209            parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
210            parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
211
212        self.assertGreater(
213            parent_prefer.get_mle_message_tlv(mle.Connectivity).pp,
214            parent_cmp.get_mle_message_tlv(mle.Connectivity).pp)
215
216        # Check Child Id Request
217        messages = self.simulator.get_messages_sent_by(REED_1_1)
218        msg = messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
219        msg.assertSentToNode(self.nodes[ROUTER_1_2])
220
221        # Mesh Impacting Criteria - Router with the most high-quality neighbors
222        # (Link Quality 3 field in the Connectivity TLV)
223        # MED_1_1 would attach to LEADER_1_2
224
225        self.nodes[REED_1_1].set_state('router')
226        self.simulator.go(config.ROUTER_STARTUP_DELAY)
227        self.assertEqual(self.nodes[REED_1_1].get_state(), 'router')
228
229        # Flush relative message queues
230        self.flush_nodes([LEADER_1_2, ROUTER_1_1])
231
232        self.nodes[MED_1_1].start()
233        self.simulator.go(5)
234        self.assertEqual(self.nodes[MED_1_1].get_state(), 'child')
235
236        # Check Parent Response
237        messages = self.simulator.get_messages_sent_by(LEADER_1_2)
238        parent_prefer = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
239        assert (parent_prefer), "Error: Expected parent response not found"
240
241        messages = self.simulator.get_messages_sent_by(ROUTER_1_1)
242        parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
243        assert (parent_cmp), "Error: Expected parent response not found"
244
245        self.assertEqual(
246            parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
247            parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
248        self.assertEqual(
249            parent_prefer.get_mle_message_tlv(mle.Connectivity).pp,
250            parent_cmp.get_mle_message_tlv(mle.Connectivity).pp)
251        self.assertGreater(
252            parent_prefer.get_mle_message_tlv(mle.Connectivity).link_quality_3,
253            parent_cmp.get_mle_message_tlv(mle.Connectivity).link_quality_3)
254
255        # Check Child Id Request
256        messages = self.simulator.get_messages_sent_by(MED_1_1)
257        msg = messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
258        msg.assertSentToNode(self.nodes[LEADER_1_2])
259
260        # Child Impacting Criteria - A Version number in the Version TLV
261        # equal to or higher than the version that implements features
262        # desirable to the Child MED_1_2 would attach to ROUTER_1_2
263
264        # Flush relative message queues
265        self.flush_nodes([ROUTER_1_2, ROUTER_1_1])
266
267        self.nodes[ROUTER_1_1].set_parent_priority(1)
268        self.nodes[MED_1_2].start()
269        self.simulator.go(15)
270        self.assertEqual(self.nodes[MED_1_2].get_state(), 'child')
271
272        # Check Parent Response
273        messages = self.simulator.get_messages_sent_by(ROUTER_1_2)
274        parent_prefer = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
275        assert (parent_prefer), "Error: Expected parent response not found"
276
277        messages = self.simulator.get_messages_sent_by(ROUTER_1_1)
278        parent_cmp = messages.next_mle_message(mle.CommandType.PARENT_RESPONSE)
279        assert (parent_cmp), "Error: Expected parent response not found"
280
281        self.assertEqual(
282            parent_prefer.get_mle_message_tlv(mle.LinkMargin).link_margin,
283            parent_cmp.get_mle_message_tlv(mle.LinkMargin).link_margin)
284        self.assertEqual(
285            parent_prefer.get_mle_message_tlv(mle.Connectivity).pp,
286            parent_cmp.get_mle_message_tlv(mle.Connectivity).pp)
287        self.assertEqual(
288            parent_prefer.get_mle_message_tlv(mle.Connectivity).link_quality_3,
289            parent_cmp.get_mle_message_tlv(mle.Connectivity).link_quality_3)
290        self.assertGreater(
291            parent_prefer.get_mle_message_tlv(mle.Version).version,
292            parent_cmp.get_mle_message_tlv(mle.Version).version)
293
294        # Check Child Id Request
295        messages = self.simulator.get_messages_sent_by(MED_1_2)
296        msg = messages.next_mle_message(mle.CommandType.CHILD_ID_REQUEST)
297        msg.assertSentToNode(self.nodes[ROUTER_1_2])
298
299
300if __name__ == '__main__':
301    unittest.main()
302