1#!/usr/bin/env python3
2#
3#  Copyright (c) 2018, 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 WPAN_DATA_REQUEST, WPAN_ACK, MLE_PARENT_REQUEST, MLE_PARENT_RESPONSE, MLE_CHILD_UPDATE_REQUEST, MLE_CHILD_UPDATE_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
37from pktverify.null_field import nullField
38
39LEADER = 1
40REED = 2
41MTD = 3
42
43# Test Purpose and Description:
44# -----------------------------
45# The purpose of this test case is to validate that the DUT is able to successfully
46# attach to a network as an End Device through a REED.
47#
48# Test Topology:
49# -------------
50#  Leader
51#    |
52#  REED
53#    |
54#   DUT
55#
56# DUT Types:
57# ----------
58#  ED
59#  SED
60
61
62class Cert_6_1_2_REEDAttach_Base(thread_cert.TestCase):
63    USE_MESSAGE_FACTORY = False
64
65    TOPOLOGY = {
66        LEADER: {
67            'name': 'LEADER',
68            'mode': 'rdn',
69            'allowlist': [REED]
70        },
71        REED: {
72            'name': 'REED',
73            'mode': 'rdn',
74            'router_upgrade_threshold': 0,
75            'allowlist': [LEADER, MTD]
76        },
77        MTD: {
78            'name': 'DUT',
79            'is_mtd': True,
80            'timeout': config.DEFAULT_CHILD_TIMEOUT,
81            'allowlist': [REED]
82        },
83    }
84
85    def test(self):
86        self.nodes[LEADER].start()
87        self.simulator.go(config.LEADER_STARTUP_DELAY)
88        self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
89
90        self.nodes[REED].start()
91        self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL)
92        self.assertEqual(self.nodes[REED].get_state(), 'child')
93        self.collect_rloc16s()
94
95        self.nodes[MTD].start()
96        self.simulator.go(5)
97        self.assertEqual(self.nodes[MTD].get_state(), 'child')
98        self.assertEqual(self.nodes[REED].get_state(), 'router')
99        self.collect_ipaddrs()
100        self.collect_rlocs()
101
102        self.simulator.go(config.DEFAULT_CHILD_TIMEOUT)
103
104        dut_addr = self.nodes[MTD].get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL)
105        self.assertTrue(self.nodes[REED].ping(dut_addr))
106
107    def verify(self, pv):
108        pkts = pv.pkts
109        pv.summary.show()
110
111        LEADER = pv.vars['LEADER']
112        LEADER_RLOC = pv.vars['LEADER_RLOC']
113        REED = pv.vars['REED']
114        REED_RLOC = pv.vars['REED_RLOC']
115        REED_LLA = pv.vars['REED_LLA']
116        REED_RLOC16 = pv.vars['REED_RLOC16']
117        DUT = pv.vars['DUT']
118        DUT_RLOC = pv.vars['DUT_RLOC']
119        DUT_LLA = pv.vars['DUT_LLA']
120
121        # Step 1: Ensure the Leader is sending MLE Advertisements and is connected to REED
122
123        pkts.filter_wpan_src64(LEADER).\
124            filter_mle_advertisement('Leader').\
125            must_next()
126
127        pv.verify_attached('REED', 'LEADER')
128
129        # Step 2: DUT sends a MLE Parent Request with an IP hop limit of
130        #         255 to the Link-Local All Routers multicast address (FF02::2).
131        #         The following TLVs MUST be present in the MLE Parent Request:
132        #            - Challenge TLV
133        #            - Mode TLV
134        #            - Scan Mask TLV
135        #                Verify that the first one is sent to routers only
136        #            -  Version TLV
137        #         If the first MLE Parent Request was sent to all Routers and
138        #         REEDS, the test fails.
139        #         In securing the first three messages of the attaching process,
140        #         the full four-byte key sequence number MUST be included in
141        #         the Auxiliary Security Header used for MLE security.
142        #
143        #         To send the full four-byte key sequence number, the Key
144        #         Identifier Mode of the Security Control Field SHALL be set to
145        #         ‘0x02’, indicating the presence of a four-byte Key Source,
146        #         which SHALL contain the four-byte key sequence number in
147        #         network byte order.
148
149        pkts.filter_wpan_src64(DUT).\
150            filter_LLARMA().\
151            filter_mle_cmd(MLE_PARENT_REQUEST).\
152            filter(lambda p: {
153                              CHALLENGE_TLV,
154                              MODE_TLV,
155                              SCAN_MASK_TLV,
156                              VERSION_TLV
157                              } <= set(p.mle.tlv.type) and\
158                   p.ipv6.hlim == 255 and\
159                   p.mle.tlv.scan_mask.r == 1 and\
160                   p.mle.tlv.scan_mask.e == 0 and\
161                   p.wpan.aux_sec.key_id_mode == 0x2
162                   ).\
163            must_next()
164        index1 = pkts.index
165
166        # Step 4: DUT sends a MLE Parent Request with an IP hop limit of
167        #         255 to the Link-Local All Routers multicast address (FF02::2).
168        #         The following TLVs MUST be present in the MLE Parent Request:
169        #            - Challenge TLV
170        #            - Mode TLV
171        #            - Scan Mask TLV
172        #                    Verify that it is sent to Routers AND REEDs
173        #            -  Version TLV
174        #         If request was not sent to all routers and REEDS, then the test
175        #         has failed.
176        #         In securing the first three messages of the attaching process,
177        #         the full four-byte key sequence number MUST be included in
178        #         the Auxiliary Security Header used for MLE security.
179        #         To send the full four-byte key sequence number, the Key
180        #         Identifier Mode of the Security Control Field SHALL be set to
181        #         ‘0x02’, indicating the presence of a four-byte Key Source,
182        #         which SHALL contain the four-byte key sequence number in
183        #         network byte order.
184
185        pkts.filter_wpan_src64(DUT).\
186            filter_LLARMA().\
187            filter_mle_cmd(MLE_PARENT_REQUEST).\
188            filter(lambda p: {
189                              CHALLENGE_TLV,
190                              MODE_TLV,
191                              SCAN_MASK_TLV,
192                              VERSION_TLV
193                              } <= set(p.mle.tlv.type) and\
194                   p.ipv6.hlim == 255 and\
195                   p.mle.tlv.scan_mask.r == 1 and\
196                   p.mle.tlv.scan_mask.e == 1 and\
197                   p.wpan.aux_sec.key_id_mode == 0x2
198                   ).\
199            must_next()
200        index2 = pkts.index
201
202        # Step 3: REED doesn't response to the first Parent Request
203
204        pkts.range(index1, index2).\
205            filter_wpan_src64(REED).\
206            filter_wpan_dst64(DUT).\
207            filter_mle_cmd(MLE_PARENT_RESPONSE).\
208            must_not_next()
209
210        # Step 5: REED responds with MLE Parent Response for the second Parent Request
211
212        pkts.filter_wpan_src64(REED).\
213            filter_wpan_dst64(DUT).\
214            filter_mle_cmd(MLE_PARENT_RESPONSE).\
215            filter(lambda p: {
216                              CHALLENGE_TLV,
217                              CONNECTIVITY_TLV,
218                              LEADER_DATA_TLV,
219                              LINK_LAYER_FRAME_COUNTER_TLV,
220                              LINK_MARGIN_TLV,
221                              RESPONSE_TLV,
222                              SOURCE_ADDRESS_TLV,
223                              VERSION_TLV
224                               } <= set(p.mle.tlv.type)).\
225                   must_next()
226
227        # Step 6: DUT sends a MLE Child ID Request.
228        #         The following TLVs MUST be present in the MLE Child ID Request:
229        #             - Address Registration TLV
230        #             - Link-layer Frame Counter TLV
231        #             - Mode TLV
232        #             - Response TLV
233        #             - Timeout TLV
234        #             - TLV Request TLV
235        #             - Version TLV
236        #             - MLE Frame Counter TLV (optional)
237
238        pkts.filter_wpan_src64(DUT).\
239            filter_wpan_dst64(REED).\
240            filter_mle_cmd(MLE_CHILD_ID_REQUEST).\
241            filter(lambda p: {
242                              ADDRESS_REGISTRATION_TLV,
243                              LINK_LAYER_FRAME_COUNTER_TLV,
244                              MODE_TLV,
245                              RESPONSE_TLV,
246                              TIMEOUT_TLV,
247                              TLV_REQUEST_TLV,
248                              VERSION_TLV
249                             } <= set(p.mle.tlv.type) and\
250                   p.wpan.aux_sec.key_id_mode == 0x2
251                   ).\
252             must_next()
253
254        # Step 7: REED sends an Address Solicit Request to Leader;
255        #         Leader responds with an Address Solicit Response and REED
256        #         becomes active router;
257        #         REED sends Child ID Response with DUT’s new 16-bit Address.
258
259        _pkt = pkts.filter_wpan_src64(REED).\
260            filter_ipv6_dst(LEADER_RLOC).\
261            filter_coap_request(ADDR_SOL_URI).\
262            filter(lambda p: {
263                              NL_MAC_EXTENDED_ADDRESS_TLV,
264                              NL_STATUS_TLV
265                              } <= set(p.coap.tlv.type)
266                   ).\
267            must_next()
268        pkts.filter_wpan_src64(LEADER).\
269            filter_wpan_dst16(_pkt.wpan.src16).\
270            filter_coap_ack(ADDR_SOL_URI).\
271            filter(lambda p: {
272                              NL_STATUS_TLV,
273                              NL_RLOC16_TLV,
274                              NL_ROUTER_MASK_TLV
275                              } <= set(p.coap.tlv.type) and\
276                   p.coap.code == COAP_CODE_ACK and\
277                   p.thread_address.tlv.status == 0\
278                   ).\
279            must_next()
280        _pkt = pkts.filter_wpan_src64(REED).\
281            filter_wpan_dst64(DUT).\
282            filter_mle_cmd(MLE_CHILD_ID_RESPONSE).\
283            filter(lambda p: {
284                              ADDRESS16_TLV,
285                              LEADER_DATA_TLV,
286                              NETWORK_DATA_TLV,
287                              SOURCE_ADDRESS_TLV
288                              } <= set(p.mle.tlv.type) and\
289                   p.mle.tlv.source_addr != REED_RLOC16
290                   ).\
291            must_next()
292
293        if self.TOPOLOGY[MTD]['mode'] == 'rn':
294            # Step 8: DUT sends periodic Child Update messages as part of the
295            #         keep-alive message
296            #         The DUT MUST send MLE Child Update messages containing
297            #         the following TLVs:
298            #             - Leader Data TLV
299            #             - Mode TLV
300            #             - Source Address TLV
301            pkts.filter_wpan_src64(DUT).\
302                filter_wpan_dst64(REED).\
303                filter_mle_cmd(MLE_CHILD_UPDATE_REQUEST).\
304                filter(lambda p: {
305                                  SOURCE_ADDRESS_TLV,
306                                  MODE_TLV,
307                                  LEADER_DATA_TLV
308                                 } <= set(p.mle.tlv.type)
309                       ).\
310                must_next()
311
312            # Step 9: REED Respond to Child Update messages with a MLE Update
313            #         Response.
314            pkts.filter_wpan_src64(REED).\
315                filter_wpan_dst64(DUT).\
316                filter_mle_cmd(MLE_CHILD_UPDATE_RESPONSE).\
317                filter(lambda p: {
318                                  SOURCE_ADDRESS_TLV,
319                                  MODE_TLV,
320                                  LEADER_DATA_TLV
321                                 } <= set(p.mle.tlv.type)
322                       ).\
323                must_next()
324
325            # Step 10: Go to Step 12
326
327        else:
328            # Step 11: DUT sends periodic 802.15.4 Data Request messages as part
329            #          of the keep-alive message
330            #          The DUT must send a 802.15.4 Data Request command to the
331            #          parent device and receive an ACK message in response
332            _pkt2 = pkts.filter_wpan_src64(DUT).\
333                filter_wpan_dst16(_pkt.mle.tlv.source_addr).\
334                filter_wpan_cmd(WPAN_DATA_REQUEST).\
335                must_next()
336
337            pkts.filter(lambda p:
338                        p.wpan.seq_no == _pkt2.wpan.seq_no and\
339                        p.wpan.frame_type == WPAN_ACK
340                        ).\
341                must_next()
342
343        # Step 12: REED verifies connectivity by sending an ICMPv6 Echo Request
344        #          to the DUT link local address
345        #          DUT responds with ICMPv6 Echo Reply
346
347        _pkt = pkts.filter_ping_request().\
348            filter_ipv6_src_dst(REED_LLA, DUT_LLA).\
349            must_next()
350        pkts.filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier).\
351            filter_ipv6_src_dst(DUT_LLA, REED_LLA).\
352                must_next()
353
354
355class Cert_6_1_2_REEDAttach_ED(Cert_6_1_2_REEDAttach_Base):
356    TOPOLOGY = copy.deepcopy(Cert_6_1_2_REEDAttach_Base.TOPOLOGY)
357    TOPOLOGY[MTD]['mode'] = 'rn'
358
359
360class Cert_6_1_2_REEDAttach_SED(Cert_6_1_2_REEDAttach_Base):
361    TOPOLOGY = copy.deepcopy(Cert_6_1_2_REEDAttach_Base.TOPOLOGY)
362    TOPOLOGY[MTD]['mode'] = '-'
363
364
365del (Cert_6_1_2_REEDAttach_Base)
366
367if __name__ == '__main__':
368    unittest.main()
369