1#!/usr/bin/env python3
2#
3#  Copyright (c) 2024, 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
29from cli import verify
30from cli import verify_within
31import cli
32import time
33
34# -----------------------------------------------------------------------------------------------------------------------
35# Test description:
36#
37# This test validates the behavior of MTD children regarding the
38# registration of their IPv6 addresses with their parent.
39# Specifically, it covers the scenario where SLAAC-based addresses
40# remain unchanged, but their assigned LoWPAN Context ID for the
41# corresponding on-mesh prefix in the Network Data changes. In this
42# case, the MTD child should schedule an MLE Child Update Request
43# exchange with its parent to re-register the addresses. This ensures
44# that any earlier registration failures due to incorrect context ID
45# compression are resolved.
46#
47
48test_name = __file__[:-3] if __file__.endswith('.py') else __file__
49print('-' * 120)
50print('Starting \'{}\''.format(test_name))
51
52# -----------------------------------------------------------------------------------------------------------------------
53# Creating `cli.Nodes` instances
54
55speedup = 25
56cli.Node.set_time_speedup_factor(speedup)
57
58leader = cli.Node()
59sed = cli.Node()
60
61# -----------------------------------------------------------------------------------------------------------------------
62# Form topology
63
64leader.form('cid-chng')
65
66verify(leader.get_state() == 'leader')
67
68# -----------------------------------------------------------------------------------------------------------------------
69# Test Implementation
70
71# Set the "context ID reuse delay" to a short interval of 3 seconds.
72
73leader.set_context_reuse_delay(3)
74verify(int(leader.get_context_reuse_delay()) == 3)
75
76# Add two on-link prefixes on `leader`.
77
78leader.add_prefix('fd00:1::/64', 'poas')
79leader.add_prefix('fd00:2::/64', 'poas')
80leader.register_netdata()
81
82# Check that context ID 2 is assigned to prefix `fd00:2::/64`.
83
84time.sleep(0.5 / speedup)
85
86netdata = leader.get_netdata()
87contexts = netdata['contexts']
88verify(len(contexts) == 2)
89verify(any([context.startswith('fd00:1:0:0::/64 1 c') for context in contexts]))
90verify(any([context.startswith('fd00:2:0:0::/64 2 c') for context in contexts]))
91
92# Remove the first prefix.
93
94leader.remove_prefix('fd00:1::/64')
95leader.register_netdata()
96
97# Wait longer than Context ID reuse delay to ensure that context ID
98# associated with the removed prefix is aged and removed.
99
100# Wait longer than the Context ID reuse delay to ensure the removed
101# prefix's associated context ID is aged out and removed.
102
103time.sleep(3.5 / speedup)
104
105netdata = leader.get_netdata()
106contexts = netdata['contexts']
107verify(len(contexts) == 1)
108verify(any([context.startswith('fd00:2:0:0::/64 2 c') for context in contexts]))
109
110# Have `sed` attach as a child of `leader`.
111
112sed.set_child_timeout(10)
113sed.set_pollperiod(1000)
114
115sed.join(leader, cli.JOIN_TYPE_SLEEPY_END_DEVICE)
116verify(sed.get_state() == 'child')
117
118verify(int(sed.get_child_timeout()) == 10)
119
120# Check the Network Data on `sed`.
121
122netdata = sed.get_netdata()
123contexts = netdata['contexts']
124verify(len(contexts) == 1)
125verify(any([context.startswith('fd00:2:0:0::/64 2 c') for context in contexts]))
126
127# Find the `sed` address associated with on-mesh prefix `fd00:2::`.
128
129sed_ip_addrs = sed.get_ip_addrs()
130
131for ip_addr in sed_ip_addrs:
132    if ip_addr.startswith('fd00:2:0:0:'):
133        sed_addr = ip_addr
134        break
135else:
136    verify(False)
137
138# Check the parent's child table, and that `sed` child has registered its
139# IPv6 addresses with the parent.
140
141verify(len(leader.get_child_table()) == 1)
142ip_addrs = leader.get_child_ip()
143verify(len(ip_addrs) == 2)
144
145# Stop MLE operation on `sed`
146
147sed.thread_stop()
148verify(sed.get_state() == 'disabled')
149
150# Remove the `fd00::2` prefix and wait for the child entry
151# on parent to expire. Child timeout is set to 10 seconds.
152
153leader.remove_prefix('fd00:2::/64')
154leader.register_netdata()
155
156time.sleep(10.5 / speedup)
157
158# Validate that the Context ID associated with `fd00::2` is expired
159# and removed.
160
161netdata = leader.get_netdata()
162contexts = netdata['contexts']
163verify(len(contexts) == 0)
164
165# Re-add the `fd00::2` prefix and check that it gets a different
166# Context ID.
167
168leader.add_prefix('fd00:2::/64', 'poas')
169leader.register_netdata()
170
171time.sleep(0.5 / speedup)
172
173netdata = leader.get_netdata()
174contexts = netdata['contexts']
175verify(len(contexts) == 1)
176verify(any([context.startswith('fd00:2:0:0::/64 1 c') for context in contexts]))
177
178# Make sure that child is timed out and removed on parent.
179
180verify(leader.get_child_table() == [])
181
182# Re-enable MLE operation on `sed` for it to attach back.
183
184sed.thread_start()
185
186time.sleep(5.0 / speedup)
187
188verify(sed.get_state() == 'child')
189
190# Check the `sed` IPv6 addresses and that `sed` still has the same
191# SLAAC address based on the `fd00:2::` prefix
192
193sed_ip_addrs = sed.get_ip_addrs()
194verify(any([ip_addr == sed_addr for ip_addr in sed_ip_addrs]))
195
196# Validate that all `sed` IPv6 addresses are successfully
197# registered on parent.
198
199verify(len(leader.get_child_table()) == 1)
200ip_addrs = leader.get_child_ip()
201verify(len(ip_addrs) == 2)
202verify(any([ip_addr.endswith(sed_addr) for ip_addr in ip_addrs]))
203
204# -----------------------------------------------------------------------------------------------------------------------
205# Test finished
206
207cli.Node.finalize_all_nodes()
208
209print('\'{}\' passed.'.format(test_name))
210