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, 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
42ROUTER = 2
43CHILD = 3
44"""
45Acronyms:
46- EAP - Enhanced-ACK Based Probing
47
48Test Process:
491. Initiate a leader and a router at the beginning.
502. Enable Link Metrics Manager on leader.
513. Wait a few seconds.
52  - At this moment, leader should have configured EAP successfully at router.
534. Instruct the leader ping the router.
54  - Enhanced ACK sent by router should have Thread IE containing Link Metrics data.
555. Add a child into the network. The child should attach to the leader.
566. Wait 150 seconds.
57  - At this moment, leader should have configured EAP successfully at the child.
587. Instruct both the router and the child ping the leader.
59  - Enhanced ACK sent by router and child should have Thread IE containing Link Metrics data.
608. Shutdown the child.
619. Wait 300 seconds.
62  - The leader should not send a Link Management Request to the child.
63  - The leader should have sent a Link Management Request to the router and get a response.
6410. Disable Link Metrics Manager on leader.
65  - The leader shonld send a Link Management Request to the router to unregister EAP.
66"""
67
68
69class LowPower_test_LinkMetricsManager(thread_cert.TestCase):
70    TOPOLOGY = {
71        LEADER: {
72            'version': '1.2',
73            'name': 'LEADER',
74            'mode': 'rdn',
75            'allowlist': [ROUTER, CHILD],
76        },
77        ROUTER: {
78            'version': '1.2',
79            'name': 'ROUTER',
80            'mode': 'rdn',
81            'allowlist': [LEADER],
82        },
83        CHILD: {
84            'version': '1.2',
85            'name': 'CHILD',
86            'mode': 'r',
87            'allowlist': [LEADER],
88        }
89    }
90    """All nodes are created with default configurations"""
91
92    def test(self):
93        self.nodes[LEADER].start()
94        self.simulator.go(config.LEADER_STARTUP_DELAY)
95        self.assertEqual(self.nodes[LEADER].get_state(), 'leader')
96
97        self.nodes[ROUTER].start()
98        self.simulator.go(config.ROUTER_STARTUP_DELAY)
99        self.assertEqual(self.nodes[ROUTER].get_state(), 'router')
100
101        leader_addr = self.nodes[LEADER].get_ip6_address(ADDRESS_TYPE.LINK_LOCAL)
102        router_addr = self.nodes[ROUTER].get_ip6_address(ADDRESS_TYPE.LINK_LOCAL)
103
104        # Step 2 - Enable Link Metrics Manager on leader.
105        self.nodes[LEADER].link_metrics_mgr_set_enabled(True)
106
107        self.simulator.go(10)
108
109        # Step 4 - Instruct the leader ping the router.
110        self.nodes[LEADER].ping(router_addr)
111
112        # Step 5 - Add a child into the network.
113        self.nodes[CHILD].start()
114        self.simulator.go(10)
115        self.assertEqual(self.nodes[CHILD].get_state(), 'child')
116        child_addr = self.nodes[CHILD].get_ip6_address(ADDRESS_TYPE.LINK_LOCAL)
117
118        # Step 6 - Wait the leader to configure EAP at the child.
119        self.simulator.go(150)
120
121        # Step 7 - Instruct both the router and the child ping the leader.
122        self.nodes[LEADER].ping(router_addr)
123        self.nodes[LEADER].ping(child_addr)
124
125        # Step 8 - Shutdown the child.
126        self.nodes[CHILD].stop()
127
128        # Step 9 - Wait the leader to refresh EAP.
129        self.simulator.go(300)
130
131        # Step 10 - Disable Link Metrics Manager on leader.
132        self.nodes[LEADER].link_metrics_mgr_set_enabled(False)
133
134    def verify(self, pv):
135        pkts = pv.pkts
136        pv.summary.show()
137        LEADER = pv.vars['LEADER']
138        ROUTER = pv.vars['ROUTER']
139        CHILD = pv.vars['CHILD']
140
141        # Step 3 - Leader enables IEEE 802.15.4-2015 Enhanced ACK based Probing by sending a Link Metrics Management
142        # Request to the Router
143        # MLE Link Metrics Management TLV Payload:
144        # - Enhanced ACK Configuration Sub-TLV
145        # -- Enh-ACK Flags = 1 (register a configuration)
146        # -- Concatenation of Link Metric Type ID Flags = 0x00:
147        # --- Item1: (0)(0)(001)(010) = 0x0a
148        # ---- E = 0
149        # ---- L = 0
150        # ---- Type/Average Enum = 1 (Exponential Moving Avg)
151        # ---- Metrics Enum = 2 (Link Margin)
152        pkts.filter_wpan_src64(LEADER) \
153            .filter_wpan_dst64(ROUTER) \
154            .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
155            .filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \
156            .filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_REGISTER) \
157            .filter(lambda p: p.mle.tlv.link_requested_type_id_flags == '0a0b') \
158            .must_next()
159
160        # Step 3 - The Router MUST send a Link Metrics Management Response to Leader containing the following TLVs:
161        # - MLE LInk Metrics Management TLV
162        # -- Link Metrics Status Sub-TLV = 0 (Success)
163        pkts.filter_wpan_src64(ROUTER) \
164            .filter_wpan_dst64(LEADER) \
165            .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
166            .filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SUCCESS) \
167            .must_next()
168
169        # Step 4 - The Enhanced Ack sent by both Router should contain Thread specific IE
170        pkt = pkts.filter_wpan_src64(LEADER) \
171            .filter_wpan_dst64(ROUTER) \
172            .filter_ping_request() \
173            .must_next()
174        ack_seq_no = pkt.wpan.seq_no
175        pkts.filter_wpan_ack() \
176            .filter_wpan_seq(ack_seq_no) \
177            .filter(lambda p: p.wpan.payload_ie.vendor.oui == consts.THREAD_IEEE_802154_COMPANY_ID) \
178            .must_next()
179
180        # Step 6 - Leader enables IEEE 802.15.4-2015 Enhanced ACK based Probing by sending a Link Metrics Management
181        # Request to the Child
182        # MLE Link Metrics Management TLV Payload:
183        # - Enhanced ACK Configuration Sub-TLV
184        # -- Enh-ACK Flags = 1 (register a configuration)
185        # -- Concatenation of Link Metric Type ID Flags = 0x00:
186        # --- Item1: (0)(0)(001)(010) = 0x0a
187        # ---- E = 0
188        # ---- L = 0
189        # ---- Type/Average Enum = 1 (Exponential Moving Avg)
190        # ---- Metrics Enum = 2 (Link Margin)
191        pkts.filter_wpan_src64(LEADER) \
192            .filter_wpan_dst64(CHILD) \
193            .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
194            .filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \
195            .filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_REGISTER) \
196            .filter(lambda p: p.mle.tlv.link_requested_type_id_flags == '0a0b') \
197            .must_next()
198
199        # Step 6 - The Child MUST send a Link Metrics Management Response to Leader containing the following TLVs:
200        # - MLE LInk Metrics Management TLV
201        # -- Link Metrics Status Sub-TLV = 0 (Success)
202        pkts.filter_wpan_src64(CHILD) \
203            .filter_wpan_dst64(LEADER) \
204            .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
205            .filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SUCCESS) \
206            .must_next()
207
208        # Step 7 - The Enhanced Ack sent by both Router and Child should contain Thread specific IE
209        pkt1 = pkts.filter_wpan_src64(LEADER) \
210            .filter_wpan_dst64(ROUTER) \
211            .filter_ping_request() \
212            .must_next()
213        ack_seq_no = pkt1.wpan.seq_no
214        pkts.filter_wpan_ack() \
215            .filter_wpan_seq(ack_seq_no) \
216            .filter(lambda p: p.wpan.payload_ie.vendor.oui == consts.THREAD_IEEE_802154_COMPANY_ID) \
217            .must_next()
218
219        pkt2 = pkts.filter_wpan_src64(LEADER) \
220            .filter_wpan_dst64(CHILD) \
221            .filter_ping_request() \
222            .must_next()
223        ack_seq_no = pkt2.wpan.seq_no
224        pkts.filter_wpan_ack() \
225            .filter_wpan_seq(ack_seq_no) \
226            .filter(lambda p: p.wpan.payload_ie.vendor.oui == consts.THREAD_IEEE_802154_COMPANY_ID) \
227            .must_next()
228
229        # Step 9 - Leader refreshes IEEE 802.15.4-2015 Enhanced ACK based Probing by sending a Link Metrics Management
230        # Request to the Router
231        # MLE Link Metrics Management TLV Payload:
232        # - Enhanced ACK Configuration Sub-TLV
233        # -- Enh-ACK Flags = 1 (register a configuration)
234        # -- Concatenation of Link Metric Type ID Flags = 0x00:
235        # --- Item1: (0)(0)(001)(010) = 0x0a
236        # ---- E = 0
237        # ---- L = 0
238        # ---- Type/Average Enum = 1 (Exponential Moving Avg)
239        # ---- Metrics Enum = 2 (Link Margin)
240        pkts.filter_wpan_src64(LEADER) \
241            .filter_wpan_dst64(ROUTER) \
242            .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
243            .filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \
244            .filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_REGISTER) \
245            .filter(lambda p: p.mle.tlv.link_requested_type_id_flags == '0a0b') \
246            .must_next()
247
248        # Step 9 - The Router MUST send a Link Metrics Management Response to Leader containing the following TLVs:
249        # - MLE LInk Metrics Management TLV
250        # -- Link Metrics Status Sub-TLV = 0 (Success)
251        pkts.filter_wpan_src64(ROUTER) \
252            .filter_wpan_dst64(LEADER) \
253            .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_RESPONSE) \
254            .filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SUCCESS) \
255            .must_next()
256
257        # Step 10 - Leader unregisters IEEE 802.15.4-2015 Enhanced ACK based Probing by sending a Link Metrics Management
258        # Request to the Router
259        # MLE Link Metrics Management TLV Payload:
260        # - Enhanced ACK Configuration Sub-TLV
261        # -- Enh-ACK Flags = 0 (unregister a configuration)
262        pkts.filter_wpan_src64(LEADER) \
263            .filter_wpan_dst64(ROUTER) \
264            .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \
265            .filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \
266            .filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_CLEAR) \
267            .must_next()
268
269
270if __name__ == '__main__':
271    unittest.main()
272