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
31
32import config
33import copy
34import mle
35import network_diag
36import network_layer
37import thread_cert
38from network_diag import TlvType
39from pktverify.consts import DIAG_RST_URI, DIAG_GET_URI, DG_MAC_EXTENDED_ADDRESS_TLV, DG_MAC_ADDRESS_TLV, DG_MODE_TLV, DG_CONNECTIVITY_TLV, DG_ROUTE64_TLV, DG_LEADER_DATA_TLV, DG_NETWORK_DATA_TLV, DG_IPV6_ADDRESS_LIST_TLV, DG_CHANNEL_PAGES_TLV, DG_TYPE_LIST_TLV, DG_MAC_COUNTERS_TLV, DG_TIMEOUT_TLV, DG_BATTERY_LEVEL_TLV, DG_SUPPLY_VOLTAGE_TLV, DG_CHILD_TABLE_TLV, DG_CHILD_TABLE_TLV
40from pktverify.packet_verifier import PacketVerifier
41from pktverify.null_field import nullField
42
43LEADER = 1
44ROUTER1 = 2
45REED1 = 3
46SED1 = 4
47MED1 = 5
48FED1 = 6
49
50MTDS = [MED1, SED1]
51
52# Test Purpose and Description:
53# -----------------------------
54# These cases test the Diagnostic Get and Reset Commands as a part of the
55# Network Management
56#
57# Test Topology:
58# -------------
59#        Leader
60#          |
61#  FED - Router - REED
62#        /    \
63#      MED    SED
64#
65# DUT Types:
66# ----------
67#  Router
68#  FED
69
70
71class Cert_5_7_01_CoapDiagCommands_Base(thread_cert.TestCase):
72    USE_MESSAGE_FACTORY = False
73    SUPPORT_NCP = False
74
75    TOPOLOGY = {
76        LEADER: {
77            'name': 'LEADER',
78            'mode': 'rdn',
79            'allowlist': [ROUTER1],
80        },
81        ROUTER1: {
82            'mode': 'rdn',
83            'allowlist': [LEADER, REED1, SED1, MED1, FED1],
84        },
85        REED1: {
86            'name': 'REED',
87            'mode': 'rdn',
88            'allowlist': [ROUTER1],
89            'router_upgrade_threshold': 0
90        },
91        SED1: {
92            'name': 'SED',
93            'is_mtd': True,
94            'mode': '-',
95            'allowlist': [ROUTER1],
96            'timeout': config.DEFAULT_CHILD_TIMEOUT
97        },
98        MED1: {
99            'name': 'MED',
100            'is_mtd': True,
101            'mode': 'rn',
102            'allowlist': [ROUTER1]
103        },
104        FED1: {
105            'allowlist': [ROUTER1],
106            'router_upgrade_threshold': 0
107        },
108    }
109
110    def test(self):
111        # 1 - Form topology
112        self.nodes[LEADER].start()
113        self.simulator.go(config.LEADER_STARTUP_DELAY)
114        self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
115
116        self.nodes[ROUTER1].start()
117        self.simulator.go(config.ROUTER_STARTUP_DELAY)
118        self.assertEqual(self.nodes[ROUTER1].get_state(), 'router')
119
120        for i in range(3, 7):
121            self.nodes[i].start()
122            self.simulator.go(10)
123            self.assertEqual(self.nodes[i].get_state(), 'child')
124
125        self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL)
126
127        self.collect_rlocs()
128        self.collect_rloc16s()
129        self.collect_ipaddrs()
130
131        DUT = ROUTER1
132        if self.TOPOLOGY[FED1]['name'] == 'DUT':
133            DUT = FED1
134
135        dut_rloc = self.nodes[DUT].get_ip6_address(config.ADDRESS_TYPE.RLOC)
136
137        # 2 - Leader sends DIAG_GET.req
138        tlv_types = [
139            TlvType.EXT_ADDRESS, TlvType.ADDRESS16, TlvType.MODE, TlvType.CONNECTIVITY, TlvType.ROUTE64,
140            TlvType.LEADER_DATA, TlvType.NETWORK_DATA, TlvType.IPV6_ADDRESS_LIST, TlvType.CHANNEL_PAGES
141        ]
142        self.nodes[LEADER].send_network_diag_get(dut_rloc, tlv_types)
143        self.simulator.go(2)
144
145        # 3 - Leader sends DIAG_GET.req (MAC Counters TLV type included)
146        self.nodes[LEADER].send_network_diag_get(dut_rloc, [TlvType.MAC_COUNTERS])
147        self.simulator.go(2)
148
149        # 4 - Leader sends DIAG_GET.req (Timeout/Polling Period TLV type included)
150        self.nodes[LEADER].send_network_diag_get(dut_rloc, [TlvType.POLLING_PERIOD])
151        self.simulator.go(2)
152
153        # 5 - Leader sends DIAG_GET.req (Battery Level and Supply Voltage TLV types included)
154        self.nodes[LEADER].send_network_diag_get(dut_rloc, [TlvType.BATTERY_LEVEL, TlvType.SUPPLY_VOLTAGE])
155        self.simulator.go(2)
156
157        # 6 - Leader sends DIAG_GET.req (Child Table TLV types included)
158        self.nodes[LEADER].send_network_diag_get(dut_rloc, [TlvType.CHILD_TABLE])
159        self.simulator.go(2)
160
161        # 7 - Leader sends DIAG_RST.ntf (MAC Counters TLV type included)
162        self.nodes[LEADER].send_network_diag_reset(dut_rloc, [TlvType.MAC_COUNTERS])
163        self.simulator.go(2)
164
165        # 8 - Leader Sends DIAG_GET.req (MAC Counters TLV type included)
166        self.nodes[LEADER].send_network_diag_get(dut_rloc, [TlvType.MAC_COUNTERS])
167        self.simulator.go(2)
168
169    def verify(self, pv):
170        pkts = pv.pkts
171        pv.summary.show()
172
173        LEADER = pv.vars['LEADER']
174        LEADER_RLOC = pv.vars['LEADER_RLOC']
175        DUT = pv.vars['DUT']
176        DUT_RLOC = pv.vars['DUT_RLOC']
177        DUT_RLOC16 = pv.vars['DUT_RLOC16']
178        REED = pv.vars['REED']
179        REED_RLOC = pv.vars['REED_RLOC']
180        MED = pv.vars['MED']
181        MED_RLOC = pv.vars['MED_RLOC']
182        SED = pv.vars['SED']
183        SED_RLOC = pv.vars['SED_RLOC']
184        MM = pv.vars['MM_PORT']
185
186        #DUT_IPADDR =
187
188        # Step 1: Ensure topology is formed correctly
189        if self.TOPOLOGY[ROUTER1]['name'] == 'DUT':
190            FED = pv.vars['FED']
191            pv.verify_attached('DUT', 'LEADER')
192            pv.verify_attached('REED', 'DUT')
193            pv.verify_attached('SED', 'DUT', 'MTD')
194            pv.verify_attached('MED', 'DUT', 'MTD')
195            pv.verify_attached('FED', 'DUT', 'FTD-ED')
196        else:
197            ROUTER = pv.vars['ROUTER']
198            pv.verify_attached('ROUTER', 'LEADER')
199            pv.verify_attached('REED', 'ROUTER')
200            pv.verify_attached('SED', 'ROUTER', 'MTD')
201            pv.verify_attached('MED', 'ROUTER', 'MTD')
202            pv.verify_attached('DUT', 'ROUTER', 'FTD-ED')
203
204        # Step 2: Leader to send DIAG_GET.req to DUT’s RLOC.
205        #         The DUT MUST respond with a DIAG_GET.rsp response containing
206        #         the requested diagnostic TLVs:
207        #         CoAP Response Code
208        #             2.04 Changed
209        #         CoAP Payload
210        #             TLV Type 0 - MAC Extended Address (64- bit)
211        #             TLV Type 1 - MAC Address (16-bit)
212        #             TLV Type 2 - Mode (Capability information)
213        #             TLV Type 4 – Connectivity
214        #             TLV Type 5 – Route64
215        #             TLV Type 6 – Leader Data
216        #             TLV Type 7 – Network Data
217        #             TLV Type 8 – IPv6 address list
218        #             TLV Type 17 – Channel Pages
219        pkts.filter_wpan_src64(LEADER).\
220            filter_ipv6_dst(DUT_RLOC).\
221            filter_coap_request(DIAG_GET_URI).\
222            filter(lambda p: {
223                              DG_TYPE_LIST_TLV,
224                              DG_MAC_EXTENDED_ADDRESS_TLV,
225                              DG_MAC_ADDRESS_TLV,
226                              DG_MODE_TLV,
227                              DG_CONNECTIVITY_TLV,
228                              DG_ROUTE64_TLV,
229                              DG_LEADER_DATA_TLV,
230                              DG_NETWORK_DATA_TLV,
231                              DG_IPV6_ADDRESS_LIST_TLV,
232                              DG_CHANNEL_PAGES_TLV
233                              } == set(p.thread_diagnostic.tlv.type)
234                   ).\
235            must_next()
236        pkts.filter_wpan_src64(DUT).\
237            filter_ipv6_dst(LEADER_RLOC).\
238            filter_coap_ack(DIAG_GET_URI).\
239            filter(lambda p: {
240                              DG_MAC_EXTENDED_ADDRESS_TLV,
241                              DG_MAC_ADDRESS_TLV,
242                              DG_MODE_TLV,
243                              DG_CONNECTIVITY_TLV,
244                              DG_ROUTE64_TLV,
245                              DG_LEADER_DATA_TLV,
246                              DG_NETWORK_DATA_TLV,
247                              DG_IPV6_ADDRESS_LIST_TLV,
248                              DG_CHANNEL_PAGES_TLV
249                              } == set(p.thread_diagnostic.tlv.type)
250                   ).\
251            must_next()
252
253        # Step 3: Leader to send DIAG_GET.req to DUT’s RLOC.
254        #         The DUT MUST respond with a DIAG_GET.rsp response containing
255        #         the requested diagnostic TLVs:
256        #         CoAP Response Code
257        #             2.04 Changed
258        #         CoAP Payload
259        #             TLV Type 9 - MAC Counters
260        pkts.filter_wpan_src64(LEADER).\
261            filter_ipv6_dst(DUT_RLOC).\
262            filter_coap_request(DIAG_GET_URI).\
263            filter(lambda p: {
264                              DG_TYPE_LIST_TLV,
265                              DG_MAC_COUNTERS_TLV
266                              } == set(p.thread_diagnostic.tlv.type)
267                   ).\
268            must_next()
269        pkts.filter_wpan_src64(DUT).\
270            filter_ipv6_dst(LEADER_RLOC).\
271            filter_coap_ack(DIAG_GET_URI).\
272            filter(lambda p: {
273                              DG_MAC_COUNTERS_TLV
274                              } == set(p.thread_diagnostic.tlv.type)
275                   ).\
276            must_next()
277
278        # Step 4: Leader to send DIAG_GET.req to DUT’s RLOC.
279        #         The DUT MUST respond with a DIAG_GET.rsp response containing
280        #         the requested diagnostic TLVs:
281        #         CoAP Response Code
282        #             2.04 Changed
283        #         CoAP Payload
284        #             TLV Type 3 - Timeout MUST be omitted from the response
285        pkts.filter_wpan_src64(LEADER).\
286            filter_ipv6_dst(DUT_RLOC).\
287            filter_coap_request(DIAG_GET_URI).\
288            filter(lambda p: {
289                              DG_TYPE_LIST_TLV,
290                              DG_TIMEOUT_TLV
291                              } == set(p.thread_diagnostic.tlv.type)
292                   ).\
293            must_next()
294        pkts.filter_wpan_src64(DUT).\
295            filter_ipv6_dst(LEADER_RLOC).\
296            filter_coap_ack(DIAG_GET_URI).\
297            filter(lambda p: p.thread_diagnostic.tlv.type is nullField).\
298            must_next()
299
300        # Step 5: Leader to send DIAG_GET.req to DUT’s RLOC.
301        #         The DUT MUST respond with a DIAG_GET.rsp response containing
302        #         the requested diagnostic TLVs:
303        #         CoAP Response Code
304        #             2.04 Changed
305        #         CoAP Payload
306        #             TLV Type 3 - Timeout MUST be omitted from the response
307        pkts.filter_wpan_src64(LEADER).\
308            filter_ipv6_dst(DUT_RLOC).\
309            filter_coap_request(DIAG_GET_URI).\
310            filter(lambda p: {
311                              DG_TYPE_LIST_TLV,
312                              DG_BATTERY_LEVEL_TLV,
313                              DG_SUPPLY_VOLTAGE_TLV
314                              } == set(p.thread_diagnostic.tlv.type)
315                   ).\
316            must_next()
317        pkts.filter_wpan_src64(DUT).\
318            filter_ipv6_dst(LEADER_RLOC).\
319            filter_coap_ack(DIAG_GET_URI).\
320            filter(lambda p: p.thread_diagnostic.tlv.type is nullField).\
321            must_next()
322
323        # Step 6: Leader to send DIAG_GET.req to DUT’s RLOC.
324        #         The DUT MUST respond with a DIAG_GET.rsp response containing
325        #         the requested diagnostic TLVs:
326        #         CoAP Response Code
327        #             2.04 Changed
328        #         CoAP Payload
329        #             TLV Type 16 - Child Table (is empty if FED is DUT)
330        #
331        pkts.filter_wpan_src64(LEADER).\
332            filter_ipv6_dst(DUT_RLOC).\
333            filter_coap_request(DIAG_GET_URI).\
334            filter(lambda p: {
335                              DG_TYPE_LIST_TLV,
336                              DG_CHILD_TABLE_TLV
337                              } == set(p.thread_diagnostic.tlv.type)
338                   ).\
339            must_next()
340
341        if self.TOPOLOGY[ROUTER1]['name'] == 'DUT':
342            _pkt = pkts.filter_wpan_src64(DUT).\
343                filter_ipv6_dst(LEADER_RLOC).\
344                filter_coap_ack(DIAG_GET_URI).\
345                filter(lambda p: {
346                                  DG_CHILD_TABLE_TLV
347                                  } == set(p.thread_diagnostic.tlv.type)
348                       ).\
349                must_next()
350        else:
351            pkts.filter_wpan_src64(DUT).\
352                filter_ipv6_dst(LEADER_RLOC).\
353                filter_coap_ack(DIAG_GET_URI).\
354                filter(lambda p: p.thread_diagnostic.tlv.type is nullField).\
355                must_next()
356
357        # Step 7: Leader to send DIAG_RST.req to DUT’s RLOC for the following diagnostic
358        #         TLV type:
359        #             TLV Type 9 - MAC Counters
360        #         The DUT MUST respond with a CoAP response
361        #         CoAP Response Code
362        #             2.04 Changed
363        pkts.filter_wpan_src64(LEADER).\
364            filter_ipv6_dst(DUT_RLOC).\
365            filter_coap_request(DIAG_RST_URI).\
366            filter(lambda p: {
367                              DG_TYPE_LIST_TLV,
368                              DG_MAC_COUNTERS_TLV
369                              } == set(p.thread_diagnostic.tlv.type)
370                   ).\
371            must_next()
372        pkts.filter_wpan_src64(DUT).\
373            filter_ipv6_dst(LEADER_RLOC).\
374            filter_coap_ack(DIAG_RST_URI).\
375            must_next()
376
377        # Step 8: Leader to send DIAG_GET.req to DUT’s RLOC.
378        #         The DUT MUST respond with a DIAG_GET.rsp response containing
379        #         the requested diagnostic TLVs:
380        #         CoAP Response Code
381        #             2.04 Changed
382        #         CoAP Payload
383        #             TLV Type 9 - MAC Counters
384        #         TLV Type 9 - MAC Counters MUST contain a list of MAC Counters
385        #         with 0 value or less than value returned in step 3.
386        pkts.filter_wpan_src64(LEADER).\
387            filter_ipv6_dst(DUT_RLOC).\
388            filter_coap_request(DIAG_GET_URI).\
389            filter(lambda p: {
390                              DG_TYPE_LIST_TLV,
391                              DG_MAC_COUNTERS_TLV
392                              } == set(p.thread_diagnostic.tlv.type)
393                   ).\
394            must_next()
395        pkts.filter_wpan_src64(DUT).\
396            filter_ipv6_dst(LEADER_RLOC).\
397            filter_coap_ack(DIAG_GET_URI).\
398            filter(lambda p: {
399                              DG_MAC_COUNTERS_TLV
400                              } == set(p.thread_diagnostic.tlv.type)
401                   ).\
402            must_next()
403
404
405class Cert_5_7_01_CoapDiagCommands_Base_ROUTER(Cert_5_7_01_CoapDiagCommands_Base):
406    TOPOLOGY = copy.deepcopy(Cert_5_7_01_CoapDiagCommands_Base.TOPOLOGY)
407    TOPOLOGY[ROUTER1]['name'] = 'DUT'
408    TOPOLOGY[FED1]['name'] = 'FED'
409
410
411class Cert_5_7_01_CoapDiagCommands_Base_FED(Cert_5_7_01_CoapDiagCommands_Base):
412    TOPOLOGY = copy.deepcopy(Cert_5_7_01_CoapDiagCommands_Base.TOPOLOGY)
413    TOPOLOGY[ROUTER1]['name'] = 'ROUTER'
414    TOPOLOGY[FED1]['name'] = 'DUT'
415
416
417del (Cert_5_7_01_CoapDiagCommands_Base)
418
419if __name__ == '__main__':
420    unittest.main()
421