1#!/usr/bin/env python3
2#
3#  Copyright (c) 2016, 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 mle
35import thread_cert
36import thread_cert
37from pktverify.consts import MLE_PARENT_REQUEST, MLE_CHILD_ID_REQUEST, ADDR_REL_URI, SOURCE_ADDRESS_TLV, MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, NETWORK_DATA_TLV, TLV_REQUEST_TLV, SCAN_MASK_TLV, CONNECTIVITY_TLV, LINK_MARGIN_TLV, VERSION_TLV, ADDRESS_REGISTRATION_TLV, NL_MAC_EXTENDED_ADDRESS_TLV, NL_RLOC16_TLV, COAP_CODE_ACK
38from pktverify.packet_verifier import PacketVerifier
39from pktverify.null_field import nullField
40
41LEADER = 1
42DUT_ROUTER1 = 2
43ROUTER2 = 3
44ROUTER23 = 24
45
46# Test Purpose and Description:
47# -----------------------------
48# The purpose of this test case is to verify that the DUT will downgrade
49# to a REED when the network becomes too dense and the Router Downgrade
50# Threshold conditions are met.
51#
52# Test Topology:
53# -------------
54#           Leader
55#         /         \
56# Router_1 [DUT] ... Router_23
57#
58# DUT Types:
59# ----------
60#  Router
61
62
63class Cert_5_2_06_RouterDowngrade(thread_cert.TestCase):
64    USE_MESSAGE_FACTORY = False
65
66    TOPOLOGY = {
67        LEADER: {
68            'name': 'LEADER',
69            'mode': 'rdn',
70            'router_downgrade_threshold': 32,
71            'router_upgrade_threshold': 32
72        },
73        DUT_ROUTER1: {
74            'name': 'ROUTER_1',
75            'mode': 'rdn',
76        },
77        ROUTER2: {
78            'name': 'ROUTER_2',
79            'mode': 'rdn',
80            'router_downgrade_threshold': 32,
81            'router_upgrade_threshold': 32
82        },
83        4: {
84            'name': 'ROUTER_3',
85            'mode': 'rdn',
86            'router_downgrade_threshold': 32,
87            'router_upgrade_threshold': 32
88        },
89        5: {
90            'name': 'ROUTER_4',
91            'mode': 'rdn',
92            'router_downgrade_threshold': 32,
93            'router_upgrade_threshold': 32
94        },
95        6: {
96            'name': 'ROUTER_5',
97            'mode': 'rdn',
98            'router_downgrade_threshold': 32,
99            'router_upgrade_threshold': 32
100        },
101        7: {
102            'name': 'ROUTER_6',
103            'mode': 'rdn',
104            'router_downgrade_threshold': 32,
105            'router_upgrade_threshold': 32
106        },
107        8: {
108            'name': 'ROUTER_7',
109            'mode': 'rdn',
110            'router_downgrade_threshold': 32,
111            'router_upgrade_threshold': 32
112        },
113        9: {
114            'name': 'ROUTER_8',
115            'mode': 'rdn',
116            'router_downgrade_threshold': 32,
117            'router_upgrade_threshold': 32
118        },
119        10: {
120            'name': 'ROUTER_9',
121            'mode': 'rdn',
122            'router_downgrade_threshold': 32,
123            'router_upgrade_threshold': 32
124        },
125        11: {
126            'name': 'ROUTER_10',
127            'mode': 'rdn',
128            'router_downgrade_threshold': 32,
129            'router_upgrade_threshold': 32
130        },
131        12: {
132            'name': 'ROUTER_11',
133            'mode': 'rdn',
134            'router_downgrade_threshold': 32,
135            'router_upgrade_threshold': 32
136        },
137        13: {
138            'name': 'ROUTER_12',
139            'mode': 'rdn',
140            'router_downgrade_threshold': 32,
141            'router_upgrade_threshold': 32
142        },
143        14: {
144            'name': 'ROUTER_13',
145            'mode': 'rdn',
146            'router_downgrade_threshold': 32,
147            'router_upgrade_threshold': 32
148        },
149        15: {
150            'name': 'ROUTER_14',
151            'mode': 'rdn',
152            'router_downgrade_threshold': 32,
153            'router_upgrade_threshold': 32
154        },
155        16: {
156            'name': 'ROUTER_15',
157            'mode': 'rdn',
158            'router_downgrade_threshold': 32,
159            'router_upgrade_threshold': 32
160        },
161        17: {
162            'name': 'ROUTER_16',
163            'mode': 'rdn',
164            'router_downgrade_threshold': 32,
165            'router_upgrade_threshold': 32
166        },
167        18: {
168            'name': 'ROUTER_17',
169            'mode': 'rdn',
170            'router_downgrade_threshold': 32,
171            'router_upgrade_threshold': 32
172        },
173        19: {
174            'name': 'ROUTER_18',
175            'mode': 'rdn',
176            'router_downgrade_threshold': 32,
177            'router_upgrade_threshold': 32
178        },
179        20: {
180            'name': 'ROUTER_19',
181            'mode': 'rdn',
182            'router_downgrade_threshold': 32,
183            'router_upgrade_threshold': 32
184        },
185        21: {
186            'name': 'ROUTER_20',
187            'mode': 'rdn',
188            'router_downgrade_threshold': 32,
189            'router_upgrade_threshold': 32
190        },
191        22: {
192            'name': 'ROUTER_21',
193            'mode': 'rdn',
194            'router_downgrade_threshold': 32,
195            'router_upgrade_threshold': 32
196        },
197        23: {
198            'name': 'ROUTER_22',
199            'mode': 'rdn',
200            'router_downgrade_threshold': 32,
201            'router_upgrade_threshold': 32
202        },
203        ROUTER23: {
204            'name': 'ROUTER_23',
205            'mode': 'rdn',
206            'router_downgrade_threshold': 32,
207            'router_upgrade_threshold': 32
208        },
209    }
210
211    def test(self):
212        # 1 Ensure topology is formed correctly without ROUTER23.
213        self.nodes[LEADER].start()
214        self.simulator.go(config.LEADER_STARTUP_DELAY)
215        self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
216
217        for i in range(2, 24):
218            self.nodes[i].start()
219            self.simulator.go(config.ROUTER_STARTUP_DELAY)
220            self.assertEqual(self.nodes[i].get_state(), 'router')
221        self.collect_rloc16s()
222
223        # All reference testbed devices have been configured with downgrade threshold as 32 except DUT_ROUTER1,
224        # so we don't need to ensure ROUTER23 has a better link quality on
225        # posix.
226        self.nodes[ROUTER23].start()
227        self.simulator.go(config.ROUTER_STARTUP_DELAY)
228        self.assertEqual(self.nodes[ROUTER23].get_state(), 'router')
229
230        self.simulator.go(10)
231        self.assertEqual(self.nodes[DUT_ROUTER1].get_state(), 'child')
232        self.collect_rlocs()
233
234        router1_rloc = self.nodes[DUT_ROUTER1].get_ip6_address(config.ADDRESS_TYPE.RLOC)
235        self.assertTrue(self.nodes[LEADER].ping(router1_rloc))
236
237    def verify(self, pv):
238        pkts = pv.pkts
239        pv.summary.show()
240
241        LEADER = pv.vars['LEADER']
242        LEADER_RLOC = pv.vars['LEADER_RLOC']
243        ROUTER_1 = pv.vars['ROUTER_1']
244        ROUTER_1_RLOC = pv.vars['ROUTER_1_RLOC']
245        ROUTER_1_RLOC16 = pv.vars['ROUTER_1_RLOC16']
246
247        # Step 1: Ensure topology is formed correctly
248
249        for i in range(1, 24):
250            pv.verify_attached('ROUTER_%d' % i)
251
252        # Step 3: Allow enough time for the DUT to get Network Data Updates
253        #         and resign its Router ID.
254        #         The DUT MUST first reconnect to the network as a Child by
255        #         sending properly formatted Parent Request and Child ID Request
256        #         messages.
257        #         Once the DUT attaches as a Child, it MUST send an Address
258        #         Release Message to the Leader:
259        #         CoAP Request URI
260        #             coap://[<leader address>]:MM/a/ar
261        #         CoAP Payload
262        #             MAC Extended Address TLV
263        #             RLOC16 TLV
264
265        pkts.filter_wpan_src64(ROUTER_1).\
266            filter_LLARMA().\
267            filter_mle_cmd(MLE_PARENT_REQUEST).\
268            filter(lambda p: {
269                              CHALLENGE_TLV,
270                              MODE_TLV,
271                              SCAN_MASK_TLV,
272                              VERSION_TLV
273                              } <= set(p.mle.tlv.type) and\
274                   p.ipv6.hlim == 255 and\
275                   p.mle.tlv.scan_mask.r == 1 and\
276                   p.mle.tlv.scan_mask.e == 0
277                  ).\
278            must_next()
279
280        _pkt = pkts.filter_wpan_src64(ROUTER_1).\
281            filter_mle_cmd(MLE_CHILD_ID_REQUEST).\
282            filter(lambda p: {
283                              LINK_LAYER_FRAME_COUNTER_TLV,
284                              MODE_TLV,
285                              RESPONSE_TLV,
286                              TIMEOUT_TLV,
287                              TLV_REQUEST_TLV,
288                              ADDRESS16_TLV,
289                              NETWORK_DATA_TLV,
290                              VERSION_TLV
291                    } <= set(p.mle.tlv.type) and\
292                   p.mle.tlv.addr16 is nullField and\
293                   p.thread_nwd.tlv.type is nullField
294                  ).\
295            must_next()
296        _pkt.must_not_verify(lambda p: (ADDRESS_REGISTRATION_TLV) in p.mle.tlv.type)
297
298        pkts.filter_wpan_src64(ROUTER_1).\
299            filter_ipv6_dst(LEADER_RLOC).\
300            filter_coap_request(ADDR_REL_URI).\
301            filter(lambda p: {
302                              NL_MAC_EXTENDED_ADDRESS_TLV,
303                              NL_RLOC16_TLV
304                              } <= set(p.coap.tlv.type) and\
305                   p.thread_address.tlv.rloc16 == ROUTER_1_RLOC16
306                   ).\
307           must_next()
308
309        # Step 4: Leader receives Address Release messages and sends a2.04
310        #         Changed CoAP response.
311
312        pkts.filter_wpan_src64(LEADER).\
313            filter_ipv6_dst(ROUTER_1_RLOC).\
314            filter_coap_ack(ADDR_REL_URI).\
315            must_next()
316
317        # Step 5: ROUTER_1 responds with ICMPv6 Echo Reply
318        _pkt = pkts.filter_ipv6_src_dst(LEADER_RLOC, ROUTER_1_RLOC).\
319                    filter_ping_request().\
320                    must_next()
321        pkts.filter_ipv6_src_dst(ROUTER_1_RLOC, LEADER_RLOC).\
322            filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
323            must_next()
324
325
326if __name__ == '__main__':
327    unittest.main()
328