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
32from config import ADDRESS_TYPE
33from mle import LinkMetricsSubTlvType, TlvType
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
42CHILD = 2
43
44SERIES_ID_1 = 1
45SERIES_ID_2 = 2
46
47
48class LowPower_test_ForwardTrackingSeries(thread_cert.TestCase):
49    TOPOLOGY = {
50        LEADER: {
51            'version': '1.2',
52            'name': 'LEADER',
53            'mode': 'rdn',
54            'allowlist': [CHILD],
55        },
56        CHILD: {
57            'version': '1.2',
58            'name': 'CHILD',
59            'mode': 'rn',
60            'allowlist': [LEADER],
61        }
62    }
63    """All nodes are created with default configurations"""
64
65    def test(self):
66        self.nodes[LEADER].start()
67        self.simulator.go(config.LEADER_STARTUP_DELAY)
68        self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
69
70        self.nodes[CHILD].start()
71        self.simulator.go(5)
72        self.assertEqual(self.nodes[CHILD].get_state(), 'child')
73
74        leader_addr = self.nodes[LEADER].get_ip6_address(ADDRESS_TYPE.LINK_LOCAL)
75
76        # 1. Child configures a Forward Tracking Series successfully.
77        # The Series tracks the count of MAC Data Request.
78        # Child should get a response with status 0 (SUCCESS).
79        self.nodes[CHILD].link_metrics_config_req_forward_tracking_series(leader_addr, SERIES_ID_1, 'r', 'p', 'async')
80        self.simulator.go(1)
81
82        # 2. Child configures the same Forward Tracking Series again.
83        # Child should get a response with status 2 (SERIES_ID_ALREADY_REGISTERED).
84        self.nodes[CHILD].link_metrics_config_req_forward_tracking_series(leader_addr, SERIES_ID_1, 'r', 'p', 'async')
85        self.simulator.go(1)
86
87        # 3. Child queries a Series that doesn't exist (using a wrong Series ID).
88        # Child should get a report with status 3 (SERIES_ID_NOT_RECOGNIZED).
89        self.nodes[CHILD].link_metrics_request_forward_tracking_series(leader_addr, SERIES_ID_2, 'async')
90        self.simulator.go(1)
91
92        # 4. Child queries a Series that don't have matched frames yet.
93        # Child should get a report with status 4 (NO_MATCHING_FRAMES_RECEIVED).
94        self.nodes[CHILD].link_metrics_request_forward_tracking_series(leader_addr, SERIES_ID_1, 'async')
95        self.simulator.go(1)
96
97        # 5. Child clears a Forward Tracking Series that doesn't exist.
98        # Child should get a response with status 3 (SERIES_ID_NOT_RECOGNIZED).
99        self.nodes[CHILD].link_metrics_config_req_forward_tracking_series(leader_addr, SERIES_ID_2, 'X', '', 'async')
100        self.simulator.go(1)
101
102        # 6. Child clears a Forward Tracking Series successfully.
103        # Child should get a response with status 0 (SUCCESS).
104        self.nodes[CHILD].link_metrics_config_req_forward_tracking_series(leader_addr, SERIES_ID_1, 'X', '', 'async')
105        self.simulator.go(1)
106
107        # 7. Child configures a new Forward Tracking Series successfully.
108        # The Series tracks the count of all MAC Data frames.
109        # Child should get a response with status 0 (SUCCESS).
110        self.nodes[CHILD].link_metrics_config_req_forward_tracking_series(leader_addr, SERIES_ID_2, 'd', 'pqmr',
111                                                                          'async')
112        self.simulator.go(1)
113
114        # 8. Child sends an MLE Link Probe message to the Subject for the newly configured Series.
115        self.nodes[CHILD].link_metrics_send_link_probe(leader_addr, SERIES_ID_2, 1)
116
117        # 9. Child queries the newly configured Series successfully.
118        # Child should get a report with valid values.
119        self.nodes[CHILD].link_metrics_request_forward_tracking_series(leader_addr, SERIES_ID_2, 'async')
120        self.simulator.go(1)
121
122    def verify(self, pv):
123        pkts = pv.pkts
124        pv.summary.show()
125        LEADER = pv.vars['LEADER']
126        CHILD = pv.vars['CHILD']
127
128        # 1. Child should get a response with status 0 (SUCCESS).
129        pkts.filter_wpan_src64(LEADER) \
130            .filter_wpan_dst64(CHILD) \
131            .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
132            .filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SUCCESS) \
133            .must_next()
134
135        # 2. Child should get a response with status 2 (SERIES_ID_ALREADY_REGISTERED).
136        pkts.filter_wpan_src64(LEADER) \
137            .filter_wpan_dst64(CHILD) \
138            .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
139            .filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SERIES_ID_ALREADY_REGISTERED) \
140            .must_next()
141
142        # 3. Child should get a report with status 3 (SERIES_ID_NOT_RECOGNIZED).
143        pkts.filter_wpan_src64(LEADER) \
144            .filter_wpan_dst64(CHILD) \
145            .filter_mle_cmd(consts.MLE_DATA_RESPONSE) \
146            .filter_mle_has_tlv(TlvType.LINK_METRICS_REPORT) \
147            .filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SERIES_ID_NOT_RECOGNIZED) \
148            .must_next()
149
150        # 4. Child should get a report with status 4 (NO_MATCHING_FRAMES_RECEIVED).
151        pkts.filter_wpan_src64(LEADER) \
152            .filter_wpan_dst64(CHILD) \
153            .filter_mle_cmd(consts.MLE_DATA_RESPONSE) \
154            .filter_mle_has_tlv(TlvType.LINK_METRICS_REPORT) \
155            .filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED) \
156            .must_next()
157
158        # 5. Child should get a response with status 3 (SERIES_ID_NOT_RECOGNIZED).
159        pkts.filter_wpan_src64(LEADER) \
160            .filter_wpan_dst64(CHILD) \
161            .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
162            .filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SERIES_ID_NOT_RECOGNIZED) \
163            .must_next()
164
165        # 6. Child should get a response with status 0 (SUCCESS).
166        pkts.filter_wpan_src64(LEADER) \
167            .filter_wpan_dst64(CHILD) \
168            .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
169            .filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SUCCESS) \
170            .must_next()
171
172        # 7. Child should get a response with status 0 (SUCCESS).
173        pkts.filter_wpan_src64(LEADER) \
174            .filter_wpan_dst64(CHILD) \
175            .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
176            .filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SUCCESS) \
177            .must_next()
178
179        # 8. Child sends an MLE Link Probe message to the Subject for the newly configured Series.
180        pkts.filter_wpan_src64(CHILD) \
181            .filter_wpan_dst64(LEADER) \
182            .filter_mle_cmd(consts.MLE_LINK_PROBE) \
183            .must_next()
184
185        # 9. Child should get a report with valid values.
186        pkts.filter_wpan_src64(LEADER) \
187            .filter_wpan_dst64(CHILD) \
188            .filter_mle_cmd(consts.MLE_DATA_RESPONSE) \
189            .filter(lambda p: TlvType.LINK_METRICS_REPORT in p.mle.tlv.type) \
190            .must_next()
191
192
193if __name__ == '__main__':
194    unittest.main()
195