• 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 unittest
31import copy
32
33import thread_cert
34import config
35from pktverify.consts import MLE_PARENT_REQUEST, MLE_PARENT_RESPONSE, MLE_CHILD_ID_REQUEST, MLE_CHILD_ID_RESPONSE, ADDR_SOL_URI, SOURCE_ADDRESS_TLV, MODE_TLV, TIMEOUT_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, MLE_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, NL_STATUS_TLV, NL_ROUTER_MASK_TLV, COAP_CODE_ACK
36from pktverify.packet_verifier import PacketVerifier
37
38LEADER = 1
39ROUTER = 2
40REED_1 = 3
41REED_2 = 4
42MTD = 5
43
44# Test Purpose and Description:
45# -----------------------------
46# The purpose of this test case is to validate that the DUT will pick REED_1
47# as its parent because of its better connectivity.
48#
49# Test Topology:
50# -------------
51#  Router - Leader
52#    |    /  |
53#  REED_1  REED_2
54#      \   /
55#       DUT
56#
57# DUT Types:
58# ----------
59#  ED
60#  SED
61
62
63class Cert_6_1_4_REEDAttachConnectivity_Base(thread_cert.TestCase):
64    USE_MESSAGE_FACTORY = False
65
66    TOPOLOGY = {
67        LEADER: {
68            'name': 'LEADER',
69            'mode': 'rdn',
70            'allowlist': [ROUTER, REED_1, REED_2]
71        },
72        ROUTER: {
73            'name': 'ROUTER',
74            'mode': 'rdn',
75            'allowlist': [LEADER, REED_1]
76        },
77        REED_1: {
78            'name': 'REED_1',
79            'mode': 'rdn',
80            'router_upgrade_threshold': 0,
81            'allowlist': [LEADER, ROUTER, MTD]
82        },
83        REED_2: {
84            'name': 'REED_2',
85            'mode': 'rdn',
86            'router_upgrade_threshold': 0,
87            'allowlist': [LEADER, MTD]
88        },
89        MTD: {
90            'name': 'DUT',
91            'is_mtd': True,
92            'timeout': config.DEFAULT_CHILD_TIMEOUT,
93            'allowlist': [REED_1, REED_2]
94        },
95    }
96
97    def test(self):
98        self.nodes[LEADER].start()
99        self.simulator.go(config.LEADER_STARTUP_DELAY)
100        self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
101
102        self.nodes[ROUTER].start()
103        self.simulator.go(config.ROUTER_STARTUP_DELAY)
104        self.assertEqual(self.nodes[ROUTER].get_state(), 'router')
105
106        for i in (3, 4):
107            self.nodes[i].start()
108            self.simulator.go(5)
109            self.assertEqual(self.nodes[i].get_state(), 'child')
110
111        self.collect_rloc16s()
112        self.collect_rlocs()
113
114        self.nodes[MTD].start()
115        self.simulator.go(5)
116        self.assertEqual(self.nodes[MTD].get_state(), 'child')
117        self.assertEqual(self.nodes[REED_1].get_state(), 'router')
118
119        self.simulator.go(config.DEFAULT_CHILD_TIMEOUT)
120        self.collect_ipaddrs()
121
122        dut_addr = self.nodes[MTD].get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL)
123        self.assertTrue(self.nodes[REED_1].ping(dut_addr))
124
125    def verify(self, pv):
126        pkts = pv.pkts
127        pv.summary.show()
128
129        LEADER = pv.vars['LEADER']
130        LEADER_RLOC = pv.vars['LEADER_RLOC']
131        ROUTER = pv.vars['ROUTER']
132        REED_1 = pv.vars['REED_1']
133        REED_1_RLOC = pv.vars['REED_1_RLOC']
134        REED_1_LLA = pv.vars['REED_1_LLA']
135        REED_1_RLOC16 = pv.vars['REED_1_RLOC16']
136        DUT = pv.vars['DUT']
137        DUT_RLOC = pv.vars['DUT_RLOC']
138        DUT_LLA = pv.vars['DUT_LLA']
139
140        # Step 1: Setup the topology without the DUT. Ensure all routers and
141        #         leader are sending MLE advertisements
142
143        pkts.filter_wpan_src64(LEADER).\
144            filter_mle_advertisement('Leader').\
145            must_next()
146
147        pv.verify_attached('ROUTER', 'LEADER')
148
149        # Step 2: DUT sends a MLE Parent Request with an IP hop limit of
150        #         255 to the Link-Local All Routers multicast address (FF02::2).
151        #         The following TLVs MUST be present in the MLE Parent Request:
152        #            - Challenge TLV
153        #            - Mode TLV
154        #            - Scan Mask TLV
155        #                Verify that the first one is sent to routers only
156        #            -  Version TLV
157        #         If the first MLE Parent Request was sent to all Routers and
158        #         REED_1S, the test fails.
159
160        pkts.filter_wpan_src64(DUT).\
161            filter_LLARMA().\
162            filter_mle_cmd(MLE_PARENT_REQUEST).\
163            filter(lambda p: {
164                              CHALLENGE_TLV,
165                              MODE_TLV,
166                              SCAN_MASK_TLV,
167                              VERSION_TLV
168                              } <= set(p.mle.tlv.type) and\
169                   p.ipv6.hlim == 255 and\
170                   p.mle.tlv.scan_mask.r == 1 and\
171                   p.mle.tlv.scan_mask.e == 0 and\
172                   p.wpan.aux_sec.key_id_mode == 0x2
173                   ).\
174            must_next()
175        index1 = pkts.index
176
177        # Step 4: DUT sends a MLE Parent Request with an IP hop limit of
178        #         255 to the Link-Local All Routers multicast address (FF02::2).
179        #         The following TLVs MUST be present in the MLE Parent Request:
180        #            - Challenge TLV
181        #            - Mode TLV
182        #            - Scan Mask TLV
183        #                    Verify that it is sent to Routers AND REED_1s
184        #            -  Version TLV
185        #         If request was not sent to all routers and REED_1S, then the test
186        #         has failed.
187        pkts.filter_wpan_src64(DUT).\
188            filter_LLARMA().\
189            filter_mle_cmd(MLE_PARENT_REQUEST).\
190            filter(lambda p: {
191                              CHALLENGE_TLV,
192                              MODE_TLV,
193                              SCAN_MASK_TLV,
194                              VERSION_TLV
195                              } <= set(p.mle.tlv.type) and\
196                   p.ipv6.hlim == 255 and\
197                   p.mle.tlv.scan_mask.r == 1 and\
198                   p.mle.tlv.scan_mask.e == 1 and\
199                   p.wpan.aux_sec.key_id_mode == 0x2
200                   ).\
201            must_next()
202        index2 = pkts.index
203
204        # Step 3: REED_1 and REED_2 sends no response to Parent Request after
205        #         DUT sent the first Parent Request
206        for i in (1, 2):
207            pkts.range(index1, index2).\
208                filter_wpan_src64(pv.vars['REED_%d' %i]).\
209                filter_wpan_dst64(DUT).\
210                filter_mle_cmd(MLE_PARENT_RESPONSE).\
211                must_not_next()
212
213        # Step 5: REED_1 Respond with MLE Parent Response
214        for i in (1, 2):
215            with pkts.save_index():
216                pkts.filter_wpan_src64(pv.vars['REED_%d' %i]).\
217                    filter_wpan_dst64(DUT).\
218                    filter_mle_cmd(MLE_PARENT_RESPONSE).\
219                    filter(lambda p: {
220                                      CHALLENGE_TLV,
221                                      CONNECTIVITY_TLV,
222                                      LEADER_DATA_TLV,
223                                      LINK_LAYER_FRAME_COUNTER_TLV,
224                                      LINK_MARGIN_TLV,
225                                      RESPONSE_TLV,
226                                      SOURCE_ADDRESS_TLV,
227                                      VERSION_TLV
228                                       } <= set(p.mle.tlv.type)).\
229                     must_next()
230
231        # Step 6: DUT sends a MLE Child ID Request to REED_1
232        #         The following TLVs MUST be present in the MLE Child ID Request:
233        #             - Address Registration TLV
234        #             - Link-layer Frame Counter TLV
235        #             - Mode TLV
236        #             - Response TLV
237        #             - Timeout TLV
238        #             - TLV Request TLV
239        #             - Version TLV
240        #             - MLE Frame Counter TLV (optional)
241
242        pkts.filter_wpan_src64(DUT).\
243            filter_wpan_dst64(REED_1).\
244            filter_mle_cmd(MLE_CHILD_ID_REQUEST).\
245            filter(lambda p: {
246                              ADDRESS_REGISTRATION_TLV,
247                              LINK_LAYER_FRAME_COUNTER_TLV,
248                              MODE_TLV,
249                              RESPONSE_TLV,
250                              TIMEOUT_TLV,
251                              TLV_REQUEST_TLV,
252                              VERSION_TLV
253                             } <= set(p.mle.tlv.type) and\
254                   p.wpan.aux_sec.key_id_mode == 0x2
255                   ).\
256             must_next()
257
258        # Step 7: REED_1 sends an Address Solicit Request to Leader;
259        #         Leader responds with an Address Solicit Response and REED_1
260        #         becomes active router;
261        #         REED_1 sends Child ID Response with DUT’s new 16-bit Address.
262
263        pkts.filter_wpan_src64(REED_1).\
264            filter_ipv6_dst(LEADER_RLOC).\
265            filter_coap_request(ADDR_SOL_URI).\
266            filter(lambda p: {
267                              NL_MAC_EXTENDED_ADDRESS_TLV,
268                              NL_STATUS_TLV
269                              } <= set(p.coap.tlv.type)
270                   ).\
271            must_next()
272        pkts.filter_wpan_src64(LEADER).\
273            filter_ipv6_dst(REED_1_RLOC).\
274            filter_coap_ack(ADDR_SOL_URI).\
275            filter(lambda p: {
276                              NL_STATUS_TLV,
277                              NL_RLOC16_TLV,
278                              NL_ROUTER_MASK_TLV
279                              } == set(p.coap.tlv.type) and\
280                   p.thread_address.tlv.status == 0
281                   ).\
282            must_next()
283
284        pkts.filter_wpan_src64(REED_1).\
285            filter_wpan_dst64(DUT).\
286            filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\
287            filter(lambda p: {
288                              ADDRESS16_TLV,
289                              LEADER_DATA_TLV,
290                              NETWORK_DATA_TLV,
291                              SOURCE_ADDRESS_TLV
292                              } <= set(p.mle.tlv.type) and\
293                   p.mle.tlv.source_addr != REED_1_RLOC16
294                   ).\
295            must_next()
296
297        # Step 8: REED_1 verifies connectivity by sending an ICMPv6 Echo Request
298        #          to the DUT link local address
299        #          DUT responds with ICMPv6 Echo Reply
300
301        _pkt = pkts.filter_ping_request().\
302            filter_ipv6_src_dst(REED_1_LLA, DUT_LLA).\
303            must_next()
304        pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
305            filter_ipv6_src_dst(DUT_LLA, REED_1_LLA).\
306            must_next()
307
308
309class Cert_6_1_4_REEDAttachConnectivity_ED(Cert_6_1_4_REEDAttachConnectivity_Base):
310    TOPOLOGY = copy.deepcopy(Cert_6_1_4_REEDAttachConnectivity_Base.TOPOLOGY)
311    TOPOLOGY[MTD]['mode'] = 'rn'
312
313
314class Cert_6_1_4_REEDAttachConnectivity_SED(Cert_6_1_4_REEDAttachConnectivity_Base):
315    TOPOLOGY = copy.deepcopy(Cert_6_1_4_REEDAttachConnectivity_Base.TOPOLOGY)
316    TOPOLOGY[MTD]['mode'] = '-'
317
318
319del (Cert_6_1_4_REEDAttachConnectivity_Base)
320
321if __name__ == '__main__':
322    unittest.main()
323