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 config
33import thread_cert
34from pktverify.consts import MLE_ADVERTISEMENT, MLE_CHILD_ID_REQUEST, MLE_CHILD_ID_RESPONSE
35from pktverify.packet_verifier import PacketVerifier
36
37DUT_LEADER = 1
38ROUTER1 = 2
39ROUTER2 = 3
40
41# Test Purpose and Description:
42# -----------------------------
43# The purpose of this test case is to verify that the router ID mask is managed
44# correctly, as the connectivity to a router or group of routers is lost and / or
45# a new router is added to network.
46#
47# Test Topology:
48# -------------
49# Leader(DUT)
50#    |
51# Router_1
52#    |
53# Router_2
54#
55# DUT Types:
56# ----------
57#  Leader
58
59
60class Cert_5_3_6_RouterIdMask(thread_cert.TestCase):
61    USE_MESSAGE_FACTORY = False
62
63    TOPOLOGY = {
64        DUT_LEADER: {
65            'name': 'LEADER',
66            'mode': 'rdn',
67            'allowlist': [ROUTER1]
68        },
69        ROUTER1: {
70            'name': 'ROUTER_1',
71            'mode': 'rdn',
72            'allowlist': [DUT_LEADER, ROUTER2]
73        },
74        ROUTER2: {
75            'name': 'ROUTER_2',
76            'mode': 'rdn',
77            'allowlist': [ROUTER1]
78        },
79    }
80
81    def _setUpRouter2(self):
82        self.nodes[ROUTER2].add_allowlist(self.nodes[ROUTER1].get_addr64())
83        self.nodes[ROUTER2].enable_allowlist()
84        self.nodes[ROUTER2].set_router_selection_jitter(1)
85
86    def test(self):
87        # 1
88        self.nodes[DUT_LEADER].start()
89        self.simulator.go(config.LEADER_STARTUP_DELAY)
90        self.assertEqual(self.nodes[DUT_LEADER].get_state(), 'leader')
91
92        self.nodes[ROUTER1].start()
93        self.simulator.go(config.ROUTER_STARTUP_DELAY)
94        self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
95
96        self.nodes[ROUTER2].start()
97        self.simulator.go(config.ROUTER_STARTUP_DELAY)
98        self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
99
100        self.collect_rloc16s()
101
102        # Wait DUT_LEADER to establish routing to ROUTER2 via ROUTER1's MLE
103        # advertisement.
104        self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL)
105
106        # 2
107        self.nodes[ROUTER2].reset()
108        self._setUpRouter2()
109
110        # 3 & 4
111
112        self.simulator.go(720)
113
114        # 5
115
116        self.nodes[ROUTER2].start()
117        self.simulator.go(config.ROUTER_RESET_DELAY)
118        self.assertEqual(self.nodes[ROUTER2].get_state(), 'router')
119
120        self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL)
121
122        # 6
123        self.nodes[ROUTER1].reset()
124        self.nodes[ROUTER2].reset()
125
126        self.simulator.go(720)
127
128    def verify(self, pv):
129        pkts = pv.pkts
130        pv.summary.show()
131
132        LEADER = pv.vars['LEADER']
133        ROUTER_1 = pv.vars['ROUTER_1']
134        ROUTER_2 = pv.vars['ROUTER_2']
135
136        leader_rid = pv.vars['LEADER_RLOC16'] >> 10
137        router_1_rid = pv.vars['ROUTER_1_RLOC16'] >> 10
138        router_2_rid = pv.vars['ROUTER_2_RLOC16'] >> 10
139
140        # Step 1: Ensure topology is formed correctly
141        pv.verify_attached('ROUTER_1', 'LEADER')
142        pv.verify_attached('ROUTER_2', 'ROUTER_1')
143
144        pkts.filter_wpan_src64(LEADER).\
145            filter_LLANMA().\
146            filter_mle_cmd(MLE_ADVERTISEMENT).\
147            filter(lambda p:
148                   {1,2,1} == set(p.mle.tlv.route64.cost) and\
149                   {leader_rid, router_1_rid, router_2_rid} ==
150                   p.mle.tlv.route64.id_mask
151                   ).\
152            must_next()
153
154        # Step 4: The DUT’s routing cost to Router_2 MUST count to infinity
155        #         The DUT MUST remove Router_2 ID from its ID set
156        #         Verify route data has settled
157        _pkt = pkts.filter_wpan_src64(LEADER).\
158            filter_LLANMA().\
159            filter_mle_cmd(MLE_ADVERTISEMENT).\
160            filter(lambda p: {1,0,1} == set(p.mle.tlv.route64.cost)).\
161            must_next()
162        pkts.filter_wpan_src64(LEADER).\
163            filter_LLANMA().\
164            filter_mle_cmd(MLE_ADVERTISEMENT).\
165            filter(lambda p:
166                   {1,1} == set(p.mle.tlv.route64.cost) and\
167                   {leader_rid, router_1_rid} ==
168                   p.mle.tlv.route64.id_mask
169                   ).\
170            must_next()
171
172        # Step 5: Re-attach Router_2 to Router_1.
173        #         The DUT MUST reset the MLE Advertisement trickle timer and
174        #         send an Advertisement
175        pv.verify_attached('ROUTER_2', 'ROUTER_1')
176        # check trickle timer between the successive advertisements
177        with pkts.save_index():
178            _pkt = pkts.filter_wpan_src64(LEADER).\
179                filter_LLANMA().\
180                filter_mle_cmd(MLE_ADVERTISEMENT).\
181                must_next()
182            # Time between MLE Advertisements after reset can be up to 3.5 seconds
183            # 0.0s: Reset Interval, reset interval to 1 seconds, random interval at 0.5 second
184            # 0.5s: Transmit MLE Advertisement
185            # 1.0s: Set interval to 2 seconds, random interval at 2 seconds
186            # 3.0s: Receive MLE Advertisement, reset interval to 1 second, random interval at 1.0 second
187            # 4.0s: Transmit MLE Advertisement
188            pkts.filter_wpan_src64(LEADER).\
189                filter_LLANMA().\
190                filter_mle_cmd(MLE_ADVERTISEMENT).\
191                filter(lambda p: p.sniff_timestamp - _pkt.sniff_timestamp <= 4).\
192                must_next()
193
194        # check router cost after the re-attach
195        pkts.filter_wpan_src64(LEADER).\
196            filter_LLANMA().\
197            filter_mle_cmd(MLE_ADVERTISEMENT).\
198            filter(lambda p: {1,2,1} == set(p.mle.tlv.route64.cost) and\
199                   {leader_rid, router_1_rid, router_2_rid} ==
200                   p.mle.tlv.route64.id_mask
201                   ).\
202            must_next()
203
204        # Step 6: The DUT’s routing cost to Router_1 MUST go directly to
205        #         infinity as there is no multi-hop cost for Router_1
206        #         The DUT MUST remove Router_1 & Router_2 IDs from its ID set
207        pkts.filter_wpan_src64(LEADER).\
208            filter_LLANMA().\
209            filter_mle_cmd(MLE_ADVERTISEMENT).\
210            filter(lambda p: {0, 0, 1} == set(p.mle.tlv.route64.cost)).\
211            must_next()
212        pkts.filter_wpan_src64(LEADER).\
213            filter_LLANMA().\
214            filter_mle_cmd(MLE_ADVERTISEMENT).\
215            filter(lambda p: {0, 1} == set(p.mle.tlv.route64.cost)).\
216            must_next()
217        pkts.filter_wpan_src64(LEADER).\
218            filter_LLANMA().\
219            filter_mle_cmd(MLE_ADVERTISEMENT).\
220            filter(lambda p:
221                   [1] == p.mle.tlv.route64.cost and\
222                   {leader_rid} ==
223                   p.mle.tlv.route64.id_mask
224                   ).\
225            must_next()
226
227
228if __name__ == '__main__':
229    unittest.main()
230