1#!/usr/bin/env python3
2#
3#  Copyright (c) 2023, 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
32from config import ADDRESS_TYPE
33from mle import LinkMetricsSubTlvType
34from pktverify import consts
35from pktverify.null_field import nullField
36from pktverify.packet_verifier import PacketVerifier
37
38import config
39import thread_cert
40
41LEADER = 1
42SED_1 = 2
43SSED_1 = 3
44
45SERIES_ID = 1
46SERIES_ID_2 = 2
47POLL_PERIOD = 2000  # 2s
48
49
50class LowPower_7_1_02_SingleProbeLinkMetricsWithoutEnhancedAck(thread_cert.TestCase):
51    USE_MESSAGE_FACTORY = False
52    TOPOLOGY = {
53        LEADER: {
54            'version': '1.2',
55            'name': 'LEADER',
56            'mode': 'rdn',
57            'allowlist': [SED_1, SSED_1],
58        },
59        SED_1: {
60            'version': '1.2',
61            'name': 'SED_1',
62            'mode': '-',
63            'allowlist': [LEADER],
64        },
65        SSED_1: {
66            'version': '1.2',
67            'name': 'SSED_1',
68            'mode': '-',
69            'allowlist': [LEADER],
70        }
71    }
72    """All nodes are created with default configurations"""
73
74    def test(self):
75        self.nodes[LEADER].start()
76        self.simulator.go(config.LEADER_STARTUP_DELAY)
77        self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
78
79        self.nodes[SED_1].set_pollperiod(POLL_PERIOD)
80        self.nodes[SED_1].start()
81        self.simulator.go(5)
82        self.assertEqual(self.nodes[SED_1].get_state(), 'child')
83
84        self.nodes[SSED_1].set_csl_period(consts.CSL_DEFAULT_PERIOD)
85        self.nodes[SSED_1].start()
86        self.simulator.go(5)
87        self.assertEqual(self.nodes[SSED_1].get_state(), 'child')
88
89        leader_addr = self.nodes[LEADER].get_ip6_address(ADDRESS_TYPE.LINK_LOCAL)
90        sed_extaddr = self.nodes[SED_1].get_addr64()
91
92        # Step 3 - Verify connectivity by instructing each device to send an ICMPv6 Echo Request to the DUT
93        self.assertTrue(self.nodes[SED_1].ping(leader_addr, timeout=POLL_PERIOD / 1000))
94        self.assertTrue(self.nodes[SSED_1].ping(leader_addr, timeout=(2 * consts.CSL_DEFAULT_PERIOD_IN_SECOND)))
95        self.simulator.go(5)
96
97        # Step 4 - SED_1 sends a Single Probe Link Metric for RSSI using MLE Data Request
98        # MLE Data Request Payload:
99        # - TLV Request TLV (Link Metrics Report TLV specified)
100        # - Link Metrics Query TLV
101        # -- Link Metrics Query ID Sub-TLV
102        # --- Query ID = 0 (Single Probe Query)
103        # -- Link Metrics Query Options Sub-TLV
104        # --- Metric Type ID Flags
105        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
106        # ---- Metric Enum = 3  (RSSI)
107        #
108        # In this step, SED_1 should set its TxPower to 'High'. In simulation, this will be implemented by
109        # setting Macfilter on the Rx side (Leader).
110        self.nodes[LEADER].add_allowlist(sed_extaddr, -30)
111        res = self.nodes[SED_1].link_metrics_request_single_probe(leader_addr, 'r')
112        rss_1 = int(res['RSSI'])
113        self.simulator.go(5)
114
115        # Step 6 - SED_1 sends a Single Probe Link Metric for RSSI using MLE Data Request
116        # MLE Data Request Payload:
117        # - TLV Request TLV (Link Metrics Report TLV specified)
118        # - Link Metrics Query TLV
119        # -- Link Metrics Query ID Sub-TLV
120        # --- Query ID = 0 (Single Probe Query)
121        # -- Link Metrics Query Options Sub-TLV
122        # --- Metric Type ID Flags
123        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
124        # ---- Metric Enum = 3  (RSSI)
125        #
126        # In this step, SED_1 should set its TxPower to 'Low'.
127        self.nodes[LEADER].add_allowlist(sed_extaddr, -95)
128        res = self.nodes[SED_1].link_metrics_request_single_probe(leader_addr, 'r')
129        rss_2 = int(res['RSSI'])
130        self.simulator.go(5)
131
132        # Step 8 - Compare the rssi value in step 5 & 7, RSSI in 5 should be larger than RSSI in 7
133        self.assertTrue(rss_1 > rss_2)
134
135        # Step 9 - SSED_1 sends a Single Probe Link Metric for Layer 2 LQI using MLE Data Request
136        # MLE Data Request Payload:
137        # - TLV Request TLV (Link Metrics Report TLV specified)
138        # - Link Metrics Query TLV
139        # -- Link Metrics Query ID Sub-TLV
140        # --- Query ID = 0 (Single Probe Query)
141        # -- Link Metrics Query Options Sub-TLV
142        # --- Metric Type ID Flags
143        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
144        # ---- Metric Enum = 1  (Layer 2 LQI)
145        self.nodes[SSED_1].link_metrics_request_single_probe(leader_addr, 'q')
146        self.simulator.go(5)
147
148        # Step 11 - SSED_1 sends a Single Probe Link Metric for Link Margin using MLE Data Request
149        # MLE Data Request Payload:
150        # - TLV Request TLV (Link Metrics Report TLV specified)
151        # - Link Metrics Query TLV
152        # -- Link Metrics Query ID Sub-TLV
153        # --- Query ID = 0 (Single Probe Query)
154        # -- Link Metrics Query Options Sub-TLV
155        # --- Metric Type ID Flags
156        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
157        # ---- Metric Enum = 2  (Link Margin)
158        self.nodes[SSED_1].link_metrics_request_single_probe(leader_addr, 'm')
159        self.simulator.go(5)
160
161        # Step 13 - SSED_1 sends a Single Probe Link Metric using MLE Data Request
162        # MLE Data Request Payload:
163        # - TLV Request TLV (Link Metrics Report TLV specified)
164        # - Link Metrics Query TLV
165        # -- Link Metrics Query ID Sub-TLV
166        # --- Query ID = 0 (Single Probe Query)
167        # -- Link Metrics Query Options Sub-TLV
168        # --- Metric Type ID Flags
169        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
170        # ---- Metric Enum = 1  (Layer 2 LQI)
171        # ---- Metric Enum = 2  (Link Margin)
172        # ---- Metric Enum = 3  (RSSI)
173        self.nodes[SSED_1].link_metrics_request_single_probe(leader_addr, 'qmr')
174        self.simulator.go(5)
175
176    def verify(self, pv):
177        pkts = pv.pkts
178        pv.summary.show()
179        LEADER = pv.vars['LEADER']
180        SED_1 = pv.vars['SED_1']
181        SSED_1 = pv.vars['SSED_1']
182
183        # Step 3 - The DUT MUST send ICMPv6 Echo Responses to both SED1 & SSED1
184        pkts.filter_wpan_src64(LEADER) \
185            .filter_wpan_dst64(SED_1) \
186            .filter_ping_reply() \
187            .must_next()
188        pkts.filter_wpan_src64(LEADER) \
189            .filter_wpan_dst64(SSED_1) \
190            .filter_ping_reply() \
191            .must_next()
192
193        # Step 4 - SED_1 sends a Single Probe Link Metric for RSSI using MLE Data Request
194        # MLE Data Request Payload:
195        # - TLV Request TLV (Link Metrics Report TLV specified)
196        # - Link Metrics Query TLV
197        # -- Link Metrics Query ID Sub-TLV
198        # --- Query ID = 0 (Single Probe Query)
199        # -- Link Metrics Query Options Sub-TLV
200        # --- Metric Type ID Flags
201        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
202        # ---- Metric Enum = 3  (RSSI)
203        # TODO: Currently the ot-pktverify version of wireshark cannot parse Link Metrics Query Options Sub-TLV correctly. Will add check for the fields after fixing this.
204        pkts.filter_wpan_src64(SED_1) \
205           .filter_wpan_dst64(LEADER) \
206           .filter_mle_cmd(consts.MLE_DATA_REQUEST) \
207           .filter_mle_has_tlv(consts.TLV_REQUEST_TLV) \
208           .filter_mle_has_tlv(consts.LINK_METRICS_QUERY_TLV) \
209           .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_ID in p.mle.tlv.link_sub_tlv) \
210           .filter(lambda p: p.mle.tlv.query_id == 0) \
211           .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_OPTIONS in p.mle.tlv.link_sub_tlv) \
212           .must_next()
213
214        # Step 5 The DUT MUST reply to SED_1 with MLE Data Response with the following:
215        # - Link Metrics Report TLV
216        # -- Link Metrics Report Sub-TLV
217        # --- Metric Type ID Flags
218        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
219        # ---- Metric Enum = 3  (RSSI)
220        # --- RSSI Value (1-byte)
221        pkts.filter_wpan_src64(LEADER) \
222           .filter_wpan_dst64(SED_1) \
223           .filter_mle_cmd(consts.MLE_DATA_RESPONSE) \
224           .filter_mle_has_tlv(consts.LINK_METRICS_REPORT_TLV) \
225           .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_REPORT in p.mle.tlv.link_sub_tlv) \
226           .filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_EXPONENTIAL in p.mle.tlv.metric_type_id_flags.type) \
227           .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_RSSI in p.mle.tlv.metric_type_id_flags.metric) \
228           .must_next()
229
230        # Step 6 - SED_1 sends a Single Probe Link Metric for RSSI using MLE Data Request
231        # MLE Data Request Payload:
232        # - TLV Request TLV (Link Metrics Report TLV specified)
233        # - Link Metrics Query TLV
234        # -- Link Metrics Query ID Sub-TLV
235        # --- Query ID = 0 (Single Probe Query)
236        # -- Link Metrics Query Options Sub-TLV
237        # --- Metric Type ID Flags
238        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
239        # ---- Metric Enum = 3  (RSSI)
240        pkts.filter_wpan_src64(SED_1) \
241           .filter_wpan_dst64(LEADER) \
242           .filter_mle_cmd(consts.MLE_DATA_REQUEST) \
243           .filter_mle_has_tlv(consts.TLV_REQUEST_TLV) \
244           .filter_mle_has_tlv(consts.LINK_METRICS_QUERY_TLV) \
245           .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_ID in p.mle.tlv.link_sub_tlv) \
246           .filter(lambda p: p.mle.tlv.query_id == 0) \
247           .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_OPTIONS in p.mle.tlv.link_sub_tlv) \
248           .must_next()
249
250        # Step 7 The DUT MUST reply to SED_1 with MLE Data Response with the following:
251        # - Link Metrics Report TLV
252        # -- Link Metrics Report Sub-TLV
253        # --- Metric Type ID Flags
254        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
255        # ---- Metric Enum = 3  (RSSI)
256        # --- RSSI Value (1-byte)
257        pkts.filter_wpan_src64(LEADER) \
258           .filter_wpan_dst64(SED_1) \
259           .filter_mle_cmd(consts.MLE_DATA_RESPONSE) \
260           .filter_mle_has_tlv(consts.LINK_METRICS_REPORT_TLV) \
261           .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_REPORT in p.mle.tlv.link_sub_tlv) \
262           .filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_EXPONENTIAL in p.mle.tlv.metric_type_id_flags.type) \
263           .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_RSSI in p.mle.tlv.metric_type_id_flags.metric) \
264           .must_next()
265
266        # Step 9 - SSED_1 sends a Single Probe Link Metric for Layer 2 LQI using MLE Data Request
267        # MLE Data Request Payload:
268        # - TLV Request TLV (Link Metrics Report TLV specified)
269        # - Link Metrics Query TLV
270        # -- Link Metrics Query ID Sub-TLV
271        # --- Query ID = 0 (Single Probe Query)
272        # -- Link Metrics Query Options Sub-TLV
273        # --- Metric Type ID Flags
274        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
275        # ---- Metric Enum = 1  (Layer 2 LQI)
276        pkts.filter_wpan_src64(SSED_1) \
277           .filter_wpan_dst64(LEADER) \
278           .filter_mle_cmd(consts.MLE_DATA_REQUEST) \
279           .filter_mle_has_tlv(consts.TLV_REQUEST_TLV) \
280           .filter_mle_has_tlv(consts.LINK_METRICS_QUERY_TLV) \
281           .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_ID in p.mle.tlv.link_sub_tlv) \
282           .filter(lambda p: p.mle.tlv.query_id == 0) \
283           .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_OPTIONS in p.mle.tlv.link_sub_tlv) \
284           .must_next()
285
286        # Step 10 The DUT MUST reply to SSED_1 with MLE Data Response with the following:
287        # - Link Metrics Report TLV
288        # -- Link Metrics Report Sub-TLV
289        # --- Metric Type ID Flags
290        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
291        # ---- Metric Enum = 1  (Layer 2 LQI)
292        # --- Layer 2 LQI value (1-byte)
293        pkts.filter_wpan_src64(LEADER) \
294           .filter_wpan_dst64(SSED_1) \
295           .filter_mle_cmd(consts.MLE_DATA_RESPONSE) \
296           .filter_mle_has_tlv(consts.LINK_METRICS_REPORT_TLV) \
297           .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_REPORT in p.mle.tlv.link_sub_tlv) \
298           .filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_EXPONENTIAL in p.mle.tlv.metric_type_id_flags.type) \
299           .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_LQI in p.mle.tlv.metric_type_id_flags.metric) \
300           .must_next()
301
302        # Step 11 - SSED_1 sends a Single Probe Link Metric for Link Margin using MLE Data Request
303        # MLE Data Request Payload:
304        # - TLV Request TLV (Link Metrics Report TLV specified)
305        # - Link Metrics Query TLV
306        # -- Link Metrics Query ID Sub-TLV
307        # --- Query ID = 0 (Single Probe Query)
308        # -- Link Metrics Query Options Sub-TLV
309        # --- Metric Type ID Flags
310        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
311        # ---- Metric Enum = 2  (Link Margin)
312        pkts.filter_wpan_src64(SSED_1) \
313           .filter_wpan_dst64(LEADER) \
314           .filter_mle_cmd(consts.MLE_DATA_REQUEST) \
315           .filter_mle_has_tlv(consts.TLV_REQUEST_TLV) \
316           .filter_mle_has_tlv(consts.LINK_METRICS_QUERY_TLV) \
317           .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_ID in p.mle.tlv.link_sub_tlv) \
318           .filter(lambda p: p.mle.tlv.query_id == 0) \
319           .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_OPTIONS in p.mle.tlv.link_sub_tlv) \
320           .must_next()
321
322        # Step 12 The DUT MUST reply to SSED_1 with MLE Data Response with the following:
323        # - Link Metrics Report TLV
324        # -- Link Metrics Report Sub-TLV
325        # --- Metric Type ID Flags
326        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
327        # ---- Metric Enum = 2  (Link Margin)
328        # --- Link Margin value (1-byte)
329        pkts.filter_wpan_src64(LEADER) \
330           .filter_wpan_dst64(SSED_1) \
331           .filter_mle_cmd(consts.MLE_DATA_RESPONSE) \
332           .filter_mle_has_tlv(consts.LINK_METRICS_REPORT_TLV) \
333           .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_REPORT in p.mle.tlv.link_sub_tlv) \
334           .filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_EXPONENTIAL in p.mle.tlv.metric_type_id_flags.type) \
335           .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_LINK_MARGIN in p.mle.tlv.metric_type_id_flags.metric) \
336           .must_next()
337
338        # Step 13 - SSED_1 sends a Single Probe Link Metric using MLE Data Request
339        # MLE Data Request Payload:
340        # - TLV Request TLV (Link Metrics Report TLV specified)
341        # - Link Metrics Query TLV
342        # -- Link Metrics Query ID Sub-TLV
343        # --- Query ID = 0 (Single Probe Query)
344        # -- Link Metrics Query Options Sub-TLV
345        # --- Metric Type ID Flags
346        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
347        # ---- Metric Enum = 1  (Layer 2 LQI)
348        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
349        # ---- Metric Enum = 2  (Link Margin)
350        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
351        # ---- Metric Enum = 3  (RSSI)
352        pkts.filter_wpan_src64(SSED_1) \
353           .filter_wpan_dst64(LEADER) \
354           .filter_mle_cmd(consts.MLE_DATA_REQUEST) \
355           .filter_mle_has_tlv(consts.TLV_REQUEST_TLV) \
356           .filter_mle_has_tlv(consts.LINK_METRICS_QUERY_TLV) \
357           .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_ID in p.mle.tlv.link_sub_tlv) \
358           .filter(lambda p: p.mle.tlv.query_id == 0) \
359           .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_OPTIONS in p.mle.tlv.link_sub_tlv) \
360           .must_next()
361
362        # Step 14 The DUT MUST reply to SSED_1 with MLE Data Response with the following:
363        # - Link Metrics Report TLV
364        # -- Link Metrics Report Sub-TLV
365        # --- Metric Type ID Flags
366        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
367        # ---- Metric Enum = 1  (Layer 2 LQI)
368        # ---- Layer 2 LQI value (1-byte)
369        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
370        # ---- Metric Enum = 2  (Link Margin)
371        # ---- Link Margin value (1-byte)
372        # ---- Type / Average Enum = 1  (Exponential Moving Avg)
373        # ---- Metric Enum = 3  (RSSI)
374        # ---- RSSI value (1-byte)
375        pkts.filter_wpan_src64(LEADER) \
376           .filter_wpan_dst64(SSED_1) \
377           .filter_mle_cmd(consts.MLE_DATA_RESPONSE) \
378           .filter_mle_has_tlv(consts.LINK_METRICS_REPORT_TLV) \
379           .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_REPORT in p.mle.tlv.link_sub_tlv) \
380           .filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_EXPONENTIAL in p.mle.tlv.metric_type_id_flags.type) \
381           .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_LQI in p.mle.tlv.metric_type_id_flags.metric) \
382           .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_LINK_MARGIN in p.mle.tlv.metric_type_id_flags.metric) \
383           .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_RSSI in p.mle.tlv.metric_type_id_flags.metric) \
384           .must_next()
385
386
387if __name__ == '__main__':
388    unittest.main()
389