xref: /aosp_15_r20/external/openthread/tests/toranj/ncp/test-023-multicast-traffic.py (revision cfb92d1480a9e65faed56933e9c12405f45898b4)
1#!/usr/bin/env python3
2#
3#  Copyright (c) 2018, 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
29import time
30import wpan
31from wpan import verify
32
33# -----------------------------------------------------------------------------------------------------------------------
34# Test description: Multicast traffic
35#
36# Network topology
37#
38#     r1 ---- r2 ---- r3 ---- r4
39#             |               |
40#             |               |
41#            fed             sed
42#
43# Test covers the following multicast traffic:
44#
45# - r2  =>> link-local all-nodes.   Expected to receive on [r1, r2, r3, fed].
46# - r3  =>> mesh-local all-nodes.   Expected to receive on [r1, r2, r3, r4, fed].
47# - r3  =>> link-local all-routers. Expected to receive on [r2, r3, r4].
48# - r3  =>> mesh-local all-routers. Expected to receive on all routers.
49# - r1  =>> link-local all-thread.  Expected to receive on [r1, r2].
50# - fed =>> mesh-local all-thread.  Expected to receive on all nodes.
51# - r1  =>> specific address (on r2 and sed). Expected to receive on [r2, sed].
52# - Check behavior with different multicast hop limit values (1-hop up to 4-hops).
53#
54
55test_name = __file__[:-3] if __file__.endswith('.py') else __file__
56print('-' * 120)
57print('Starting \'{}\''.format(test_name))
58
59# -----------------------------------------------------------------------------------------------------------------------
60# Utility functions
61
62
63def send_mcast(
64    src_node,
65    src_addr,
66    mcast_addr,
67    recving_nodes,
68    non_recving_nodes=[],
69    msg_len=30,
70    mcast_hops=5,
71):
72    """
73    Send a multicast message with given `len` from `src_node` using `src_addr` to the multicast address `mcast_addr`.
74    Verify that the message is received on all nodes in `recving_nodes` list and that it is not received on all
75    nodes in `non_recving_nodes` list.
76    """
77    sender = src_node.prepare_tx(src_addr, mcast_addr, msg_len, mcast_hops=mcast_hops)
78    recvers = [node.prepare_rx(sender) for node in recving_nodes]
79    listeners = [node.prepare_listener(sender.dst_port, timeout=0.5) for node in non_recving_nodes]
80
81    wpan.Node.perform_async_tx_rx()
82
83    verify(sender.was_successful)
84    for recvr in recvers:
85        verify(recvr.was_successful)
86    for lsnr in listeners:
87        # `all_rx_msg` contains a list of (msg_content, (src_addr, src_port)).
88        verify(
89            len(lsnr.all_rx_msg) == 0 or
90            all([msg[1][0] != sender.src_addr and msg[1][1] != sender.src_port for msg in lsnr.all_rx_msg]))
91
92
93# -----------------------------------------------------------------------------------------------------------------------
94# Creating `wpan.Nodes` instances
95
96speedup = 4
97wpan.Node.set_time_speedup_factor(speedup)
98
99r1 = wpan.Node()
100r2 = wpan.Node()
101r3 = wpan.Node()
102r4 = wpan.Node()
103fed = wpan.Node()
104sed = wpan.Node()
105
106all_routers = [r1, r2, r3, r4]
107all_nodes = all_routers + [fed, sed]
108
109# -----------------------------------------------------------------------------------------------------------------------
110# Init all nodes
111
112wpan.Node.init_all_nodes()
113
114# -----------------------------------------------------------------------------------------------------------------------
115# Build network topology
116#
117# Test topology:
118#
119#    r1 ---- r2 ---- r3 ---- r4
120#            |               |
121#            |               |
122#           fed             sed
123#
124
125r1.form("mcast-traffic")
126
127r1.allowlist_node(r2)
128r2.allowlist_node(r1)
129r2.join_node(r1, wpan.JOIN_TYPE_ROUTER)
130
131r2.allowlist_node(fed)
132fed.allowlist_node(r2)
133fed.join_node(r2, wpan.JOIN_TYPE_END_DEVICE)
134
135r2.allowlist_node(r3)
136r3.allowlist_node(r2)
137r3.join_node(r2, wpan.JOIN_TYPE_ROUTER)
138
139r3.allowlist_node(r4)
140r4.allowlist_node(r3)
141r4.join_node(r3, wpan.JOIN_TYPE_ROUTER)
142
143r4.allowlist_node(sed)
144sed.allowlist_node(r4)
145sed.join_node(r4, wpan.JOIN_TYPE_SLEEPY_END_DEVICE)
146sed.set(wpan.WPAN_POLL_INTERVAL, '600')
147
148# -----------------------------------------------------------------------------------------------------------------------
149# Test implementation
150
151ml1 = r1.get(wpan.WPAN_IP6_MESH_LOCAL_ADDRESS)[1:-1]
152ll1 = r1.get(wpan.WPAN_IP6_LINK_LOCAL_ADDRESS)[1:-1]
153
154ml2 = r2.get(wpan.WPAN_IP6_MESH_LOCAL_ADDRESS)[1:-1]
155ll2 = r2.get(wpan.WPAN_IP6_LINK_LOCAL_ADDRESS)[1:-1]
156
157ml3 = r3.get(wpan.WPAN_IP6_MESH_LOCAL_ADDRESS)[1:-1]
158ll3 = r3.get(wpan.WPAN_IP6_LINK_LOCAL_ADDRESS)[1:-1]
159
160ml4 = r4.get(wpan.WPAN_IP6_MESH_LOCAL_ADDRESS)[1:-1]
161ll4 = r4.get(wpan.WPAN_IP6_LINK_LOCAL_ADDRESS)[1:-1]
162
163ml_fed = fed.get(wpan.WPAN_IP6_MESH_LOCAL_ADDRESS)[1:-1]
164ll_fed = fed.get(wpan.WPAN_IP6_LINK_LOCAL_ADDRESS)[1:-1]
165
166# Multicast addresses
167
168ll_all_nodes = "ff02::1"
169ml_all_nodes = "ff03::1"
170ml_all_mlp_fwder_nodes = "ff03::fc"
171
172ll_all_routers = "ff02::2"
173ml_all_routers = "ff03::2"
174
175ml_prefix = r1.get(wpan.WPAN_IP6_MESH_LOCAL_PREFIX)[1:-1].split('/')[0]
176ll_all_thread_nodes_addr = 'ff32:40:' + ml_prefix + '1'
177ml_all_thread_nodes_addr = 'ff33:40:' + ml_prefix + '1'
178
179#
180#    r1 ---- r2 ---- r3 ---- r4
181#            |               |
182#            |               |
183#           fed             sed
184#
185
186# r2 =>> link-local all-nodes.
187send_mcast(r2, ll2, ll_all_nodes, [r1, r2, r3, fed], [r4, sed])
188
189# r3 =>> mesh-local all-nodes.
190send_mcast(r3, ml3, ml_all_nodes, [r1, r2, r3, r4, fed])
191
192# r3 =>> link-local all-routers.
193send_mcast(r3, ml3, ll_all_routers, [r2, r3, r4], [r1, fed, sed])
194
195# r3 =>> mesh-local all-routers.
196send_mcast(r3, ml3, ml_all_routers, all_routers, [sed])
197
198# r1 =>> link-local all-thread.
199send_mcast(r1, ll1, ll_all_thread_nodes_addr, [r1, r2], [fed, r3, r4, sed])
200
201# fed =>> mesh-local all-thread.
202send_mcast(fed, ml_fed, ml_all_thread_nodes_addr, all_nodes)
203
204# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
205# Send a large multicast message (requiring MAC level fragmentations)
206
207send_mcast(r3, ml3, ml_all_thread_nodes_addr, all_nodes, msg_len=400)
208
209# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
210# Check the hop limit behavior
211
212# r1 =>> mesh-local all-thread (one hop)
213send_mcast(
214    r1,
215    ml1,
216    ml_all_thread_nodes_addr,
217    [r1, r2],
218    [fed, r3, r4, sed],
219    mcast_hops=1,
220)
221
222# r1 =>> mesh-local all-thread (two hops)
223send_mcast(
224    r1,
225    ml1,
226    ml_all_thread_nodes_addr,
227    [r1, r2, fed, r3],
228    [r4, sed],
229    mcast_hops=2,
230)
231
232# r1 =>> mesh-local all-thread (three hops)
233send_mcast(
234    r1,
235    ml1,
236    ml_all_thread_nodes_addr,
237    [r1, r2, fed, r3, r4],
238    [sed],
239    mcast_hops=3,
240)
241
242# r1 =>> mesh-local all-thread (four hops)
243send_mcast(r1, ml1, ml_all_thread_nodes_addr, [r1, r2, fed, r3, r4, sed], mcast_hops=4)
244
245# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
246# Subscribe to a specific multicast address on r2 and sed
247
248mcast_addr = "ff03::114"
249r2.add(wpan.WPAN_IP6_MULTICAST_ADDRESSES, mcast_addr)
250sed.add(wpan.WPAN_IP6_MULTICAST_ADDRESSES, mcast_addr)
251time.sleep(1)
252
253# r1 =>> specific address
254send_mcast(r1, ml1, mcast_addr, [r2, sed], [r1, r3, r4, fed])
255
256# -----------------------------------------------------------------------------------------------------------------------
257# Test finished
258
259wpan.Node.finalize_all_nodes()
260
261print('\'{}\' passed.'.format(test_name))
262