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: This test validates the mechanism to discover and update the TREL peer info when processing
36# a received TREL packet from a peer (when peer IPv6 address or port number gets changes).
37#
38
39test_name = __file__[:-3] if __file__.endswith('.py') else __file__
40print('-' * 120)
41print('Starting \'{}\''.format(test_name))
42
43# -----------------------------------------------------------------------------------------------------------------------
44# Creating `cli.Node` instances
45
46speedup = 10
47cli.Node.set_time_speedup_factor(speedup)
48
49r1 = cli.Node(cli.RADIO_15_4_TREL)
50r2 = cli.Node(cli.RADIO_15_4_TREL)
51c1 = cli.Node(cli.RADIO_15_4_TREL)
52
53# -----------------------------------------------------------------------------------------------------------------------
54# Build network topology
55
56r1.form("trel-peer-disc")
57c1.join(r1, cli.JOIN_TYPE_REED)
58r2.join(r1)
59
60verify(r1.get_state() == 'leader')
61verify(r2.get_state() == 'router')
62verify(c1.get_state() == 'child')
63
64verify(r1.multiradio_get_radios() == '[15.4, TREL]')
65verify(r2.multiradio_get_radios() == '[15.4, TREL]')
66verify(c1.multiradio_get_radios() == '[15.4, TREL]')
67
68# -----------------------------------------------------------------------------------------------------------------------
69# Test Implementation
70
71
72def check_trel_peers(node, peer_nodes):
73    # Validate that `node` has discovered `peer_nodes` as its TREL peers
74    # and validate that the correct TREL socket address is discovered
75    # and used for each.
76    peers = node.trel_get_peers()
77    verify(len(peers) == len(peer_nodes))
78    peers_ext_addrs = [nd.get_ext_addr() for nd in peer_nodes]
79    for peer in peers:
80        for peer_node in peer_nodes:
81            if peer['Ext MAC Address'] == peer_node.get_ext_addr():
82                verify(peer_node.trel_test_get_sock_addr() == peer['IPv6 Socket Address'])
83                break
84        else:
85            verify(False)  # Did not find peer in `peer_nodes`
86
87
88# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
89# Check that all nodes see each other as TREL peers with the correct
90# TREL socket address.
91
92check_trel_peers(r1, [r2, c1])
93check_trel_peers(r2, [r1, c1])
94check_trel_peers(c1, [r1, r2])
95
96verify(int(r1.trel_test_get_notify_addr_counter()) == 0)
97verify(int(r2.trel_test_get_notify_addr_counter()) == 0)
98verify(int(c1.trel_test_get_notify_addr_counter()) == 0)
99
100# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
101# Use test CLI command to force `r2` to change its TREL socket
102# address (IPv6 address only).
103
104time.sleep(10 / speedup)
105
106old_sock_addr = r2.trel_test_get_sock_addr()
107r2.trel_test_change_sock_addr()
108verify(r2.trel_test_get_sock_addr() != old_sock_addr)
109
110# Wait for longer than the Link Advertisement interval for `r2` to
111# send an advertisement. Other nodes that receive and process this
112# adv should then update the socket address of `r2` in their TREL
113# peer table.
114
115time.sleep(35 / speedup)
116
117check_trel_peers(r1, [r2, c1])
118check_trel_peers(r2, [r1, c1])
119check_trel_peers(c1, [r1, r2])
120
121# Validate that the platform is notified of the socket address
122# discrepancy on `r1` and `c1`.
123
124verify(int(r1.trel_test_get_notify_addr_counter()) == 1)
125verify(int(r2.trel_test_get_notify_addr_counter()) == 0)
126verify(int(c1.trel_test_get_notify_addr_counter()) == 1)
127
128# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
129# Use test CLI command to force `r2` to change its TREL socket
130# address port.
131
132time.sleep(10 / speedup)
133
134old_sock_addr = r2.trel_test_get_sock_addr()
135r2.trel_test_change_sock_port()
136verify(r2.trel_test_get_sock_addr() != old_sock_addr)
137
138# Wait for longer than the Link Advertisement interval for `r2` to
139# send an advertisement. Other nodes that receive and process this
140# adv should then update the socket address of `r2` in their TREL
141# peer table.
142
143time.sleep(35 / speedup)
144
145check_trel_peers(r1, [r2, c1])
146check_trel_peers(r2, [r1, c1])
147check_trel_peers(c1, [r1, r2])
148
149# Validate that the platform is notified of the socket address
150# discrepancy on `r1` and `c1`.
151
152verify(int(r1.trel_test_get_notify_addr_counter()) == 2)
153verify(int(r2.trel_test_get_notify_addr_counter()) == 0)
154verify(int(c1.trel_test_get_notify_addr_counter()) == 2)
155
156# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
157# Use test CLI command to force `c1` to change its TREL socket
158# address (IPv6 address only).
159
160old_sock_addr = c1.trel_test_get_sock_addr()
161c1.trel_test_change_sock_addr()
162verify(c1.trel_test_get_sock_addr() != old_sock_addr)
163
164# Send a ping from `c1` to `r1` to trigger communication
165# between `r1` and `c1`. The receipt of a secure data frame
166# from `c1` should update the TREL socket address on `r1`
167
168c1.ping(r1.get_rloc_ip_addr())
169
170check_trel_peers(r1, [r2, c1])
171verify(int(r1.trel_test_get_notify_addr_counter()) == 3)
172
173# -----------------------------------------------------------------------------------------------------------------------
174# Test finished
175
176cli.Node.finalize_all_nodes()
177
178print('\'{}\' passed.'.format(test_name))
179