1#!/usr/bin/env python3 2# 3# Copyright (c) 2022, 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: Multicast traffic 36# 37# Network topology 38# 39# r1 ---- r2 ---- r3 ---- r4 40# | | 41# | | 42# fed sed 43# 44# Test covers the following multicast traffic: 45# 46# - r2 =>> link-local all-nodes. Expected response from [r1, r3, fed]. 47# - r3 =>> mesh-local all-nodes. Expected response from [r1, r2, r4, fed]. 48# - r3 =>> link-local all-routers. Expected response from [r2, r4]. 49# - r3 =>> mesh-local all-routers. Expected response from all routers. 50# - r1 =>> link-local all-thread. Expected response from [r1, r2]. 51# - fed =>> mesh-local all-thread. Expected response from all nodes. 52# - r1 =>> mesh-local all-thread (one hop). Expected response from [r2]. 53# - r1 =>> mesh-local all-thread (two hops). Expected response from [r2, r3, fed]. 54# - r1 =>> mesh-local all-thread (three hops). Expected response from [r2, r3, r4, fed]. 55# - r1 =>> mesh-local all-thread (four hops). Expected response from [r2, r3, r4, fed, sed]. 56# 57# - r1 =>> realm-local scope mcast addr (on r2 and sed). Expected to receive on [r2, sed]. 58# - r2 =>> realm-local scope mcast addr (on r2 and sed) without `multicast-loop`. Expected to receive from [sed]. 59# - r2 =>> realm-local scope mcast addr (on r2 and sed) with `multicast-loop`. Expected to receive from [r2, sed]. 60# - sed =>> realm-local scope mcast addr (on r2 and sed) without `multicast-loop`. Expected to receive from [r2]. 61# - sed =>> realm-local scope mcast addr (on r2 and sed) with `multicast-loop`. Expected to receive from [r2, sed]. 62# 63# - r3 =>> site-local mcast addr (on r1, r2, sed). Expected to receive from [r1, r2, sed]. 64# - r1 =>> site-local mcast addr (on r1, r2, sed) with `multicast-loop`. Expected to receive from [r1, r2, sed]. 65# - r1 =>> site-local mcast addr (on r1, r2, sed) without `multicast-loop`. Expected to receive from [r2, sed]. 66# - sed =>> site-local mcast addr (on r1, r2, sed). Expected to receive from [r1, r2]. 67 68test_name = __file__[:-3] if __file__.endswith('.py') else __file__ 69print('-' * 120) 70print('Starting \'{}\''.format(test_name)) 71 72# ----------------------------------------------------------------------------------------------------------------------- 73# Creating `cli.Nodes` instances 74 75speedup = 10 76cli.Node.set_time_speedup_factor(speedup) 77 78r1 = cli.Node() 79r2 = cli.Node() 80r3 = cli.Node() 81r4 = cli.Node() 82fed = cli.Node() 83sed = cli.Node() 84 85# ----------------------------------------------------------------------------------------------------------------------- 86# Form topology 87 88r1.allowlist_node(r2) 89 90r2.allowlist_node(r1) 91r2.allowlist_node(fed) 92r2.allowlist_node(r3) 93 94fed.allowlist_node(r2) 95 96r3.allowlist_node(r2) 97r3.allowlist_node(r4) 98 99r4.allowlist_node(r3) 100r4.allowlist_node(sed) 101 102sed.allowlist_node(r4) 103 104r1.form("multicast") 105r2.join(r1) 106r3.join(r2) 107r4.join(r3) 108fed.join(r1, cli.JOIN_TYPE_REED) 109 110sed.join(r3, cli.JOIN_TYPE_SLEEPY_END_DEVICE) 111 112sed.set_pollperiod(600) 113 114verify(r1.get_state() == 'leader') 115verify(r2.get_state() == 'router') 116verify(r3.get_state() == 'router') 117verify(r4.get_state() == 'router') 118verify(fed.get_state() == 'child') 119verify(sed.get_state() == 'child') 120 121# ----------------------------------------------------------------------------------------------------------------------- 122# Test Implementation 123 124# Wait till first router has either established a link or 125# has a valid "next hop" towards all other routers. 126 127r1_rloc16 = int(r1.get_rloc16(), 16) 128 129 130def check_r1_router_table(): 131 table = r1.get_router_table() 132 verify(len(table) == 4) 133 for entry in table: 134 verify(int(entry['RLOC16'], 0) == r1_rloc16 or int(entry['Link']) == 1 or int(entry['Next Hop']) != 63) 135 136 137verify_within(check_r1_router_table, 160) 138 139# r2 =>> link-local all-nodes. Expected response from [r1, r3, fed]. 140 141outputs = r2.cli('ping ff02::1') 142verify(len(outputs) == 4) 143for node in [r1, r3, fed]: 144 ll_addr = node.get_linklocal_ip_addr() 145 verify(any(ll_addr in line for line in outputs)) 146 147# r3 =>> mesh-local all-nodes. Expected response from [r1, r2, r4, fed]. 148 149outputs = r3.cli('ping ff03::1') 150verify(len(outputs) == 5) 151for node in [r1, r2, r4, fed]: 152 ml_addr = node.get_mleid_ip_addr() 153 verify(any(ml_addr in line for line in outputs)) 154 155# r3 =>> link-local all-routers. Expected response from [r2, r4]. 156 157outputs = r3.cli('ping ff02::2') 158verify(len(outputs) == 3) 159for node in [r2, r4]: 160 ll_addr = node.get_linklocal_ip_addr() 161 verify(any(ll_addr in line for line in outputs)) 162 163# r3 =>> mesh-local all-routers. Expected response from all routers. 164 165outputs = r3.cli('ping ff03::2') 166verify(len(outputs) == 5) 167for node in [r1, r2, r4, fed]: 168 ml_addr = node.get_mleid_ip_addr() 169 verify(any(ml_addr in line for line in outputs)) 170 171# r1 =>> link-local all-thread. Expected response from [r2]. 172 173ml_prefix = r1.get_mesh_local_prefix().strip().split('/')[0] 174ll_all_thread_nodes_addr = 'ff32:40:' + ml_prefix + '1' 175outputs = r1.cli('ping', ll_all_thread_nodes_addr) 176verify(len(outputs) == 2) 177for node in [r2]: 178 ll_addr = node.get_linklocal_ip_addr() 179 verify(any(ll_addr in line for line in outputs)) 180 181# fed =>> mesh-local all-thread. Expected response from all nodes. 182 183ml_all_thread_nodes_addr = 'ff33:40:' + ml_prefix + '1' 184outputs = fed.cli('ping', ml_all_thread_nodes_addr) 185verify(len(outputs) == 6) 186for node in [r1, r2, r3, r4, sed]: 187 ml_addr = node.get_mleid_ip_addr() 188 verify(any(ml_addr in line for line in outputs)) 189 190# Repeat the same with larger ping msg requiring fragmentation 191 192outputs = fed.cli('ping', ml_all_thread_nodes_addr, 200) 193verify(len(outputs) == 6) 194for node in [r1, r2, r3, r4, sed]: 195 ml_addr = node.get_mleid_ip_addr() 196 verify(any(ml_addr in line for line in outputs)) 197 198# r1 =>> mesh-local all-thread (one hop). Expected response from [r2]. 199 200hop_limit = 1 201outputs = r1.cli('ping', ml_all_thread_nodes_addr, 10, 1, 0, hop_limit) 202verify(len(outputs) == 2) 203for node in [r2]: 204 ml_addr = node.get_mleid_ip_addr() 205 verify(any(ml_addr in line for line in outputs)) 206 207# r1 =>> mesh-local all-thread (two hops). Expected response from [r2, r3, fed]. 208 209hop_limit = 2 210outputs = r1.cli('ping', ml_all_thread_nodes_addr, 10, 1, 0, hop_limit) 211verify(len(outputs) == 4) 212for node in [r2, r3, fed]: 213 ml_addr = node.get_mleid_ip_addr() 214 verify(any(ml_addr in line for line in outputs)) 215 216# r1 =>> mesh-local all-thread (three hops). Expected response from [r2, r3, r4, fed]. 217 218hop_limit = 3 219outputs = r1.cli('ping', ml_all_thread_nodes_addr, 10, 1, 0, hop_limit) 220verify(len(outputs) == 5) 221for node in [r2, r3, r4, fed]: 222 ml_addr = node.get_mleid_ip_addr() 223 verify(any(ml_addr in line for line in outputs)) 224 225# r1 =>> mesh-local all-thread (four hops). Expected response from [r2, r3, r4, fed, sed]. 226 227hop_limit = 4 228outputs = r1.cli('ping', ml_all_thread_nodes_addr, 10, 1, 0, hop_limit) 229verify(len(outputs) == 6) 230for node in [r2, r3, r4, fed, sed]: 231 ml_addr = node.get_mleid_ip_addr() 232 verify(any(ml_addr in line for line in outputs)) 233 234# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 235# Subscribe to a realm-local scope multicast address on r2 and sed 236 237mcast_addr = 'ff03:0:0:0:0:0:0:114' 238r2.add_ip_maddr(mcast_addr) 239sed.add_ip_maddr(mcast_addr) 240time.sleep(1) 241maddrs = r2.get_ip_maddrs() 242verify(any(mcast_addr in maddr for maddr in maddrs)) 243maddrs = sed.get_ip_maddrs() 244verify(any(mcast_addr in maddr for maddr in maddrs)) 245 246# r1 =>> realm-local scope mcast addr (on r2 and sed). Expected to receive on [r2, sed]. 247 248outputs = r1.cli('ping', mcast_addr) 249verify(len(outputs) == 3) 250for node in [r2, sed]: 251 ml_addr = node.get_mleid_ip_addr() 252 verify(any(ml_addr in line for line in outputs)) 253 254# r2 =>> realm-local scope mcast addr (on r2 and sed) without `multicast-loop`. Expected to receive from [sed]. 255 256outputs = r2.cli('ping', mcast_addr) 257verify(len(outputs) == 2) 258for node in [sed]: 259 ml_addr = node.get_mleid_ip_addr() 260 verify(any(ml_addr in line for line in outputs)) 261 262# r2 =>> realm-local scope mcast addr (on r2 and sed) with `multicast-loop`. Expected to receive from [r2, sed]. 263 264outputs = r2.cli('ping', '-m', mcast_addr) 265verify(len(outputs) == 3) 266for node in [r2, sed]: 267 ml_addr = node.get_mleid_ip_addr() 268 verify(any(ml_addr in line for line in outputs)) 269 270# sed =>> realm-local scope mcast addr (on r2 and sed) without `multicast-loop`. Expected to receive from [r2]. 271 272outputs = sed.cli('ping', mcast_addr) 273verify(len(outputs) == 2) 274for node in [r2]: 275 ml_addr = node.get_mleid_ip_addr() 276 verify(any(ml_addr in line for line in outputs)) 277 278# sed =>> realm-local scope mcast addr (on r2 and sed) with `multicast-loop`. Expected to receive from [r2, sed]. 279 280outputs = sed.cli('ping', '-m', mcast_addr) 281verify(len(outputs) == 3) 282for node in [r2, sed]: 283 ml_addr = node.get_mleid_ip_addr() 284 verify(any(ml_addr in line for line in outputs)) 285 286# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 287# Subscribe to a larger than realm-local scope (site-local) multicast address on r1, r2, and sed 288 289mcast_addr = 'ff05:0:0:0:0:0:0:abcd' 290r1.add_ip_maddr(mcast_addr) 291r2.add_ip_maddr(mcast_addr) 292sed.add_ip_maddr(mcast_addr) 293time.sleep(1) 294maddrs = r1.get_ip_maddrs() 295verify(any(mcast_addr in maddr for maddr in maddrs)) 296maddrs = r2.get_ip_maddrs() 297verify(any(mcast_addr in maddr for maddr in maddrs)) 298maddrs = sed.get_ip_maddrs() 299verify(any(mcast_addr in maddr for maddr in maddrs)) 300 301# r3 =>> site-local mcast addr (on r1, r2, sed) with `multicast-loop`. Expected to receive from [r1, r2, sed]. 302 303outputs = r3.cli('ping', '-m', mcast_addr) 304verify(len(outputs) == 4) 305for node in [r1, r2, sed]: 306 ml_addr = node.get_mleid_ip_addr() 307 verify(any(ml_addr in line for line in outputs)) 308 309# r1 =>> site-local mcast addr (on r1, r2, sed) with `multicast-loop`. Expected to receive from [r1, r2, sed]. 310 311outputs = r1.cli('ping', '-m', mcast_addr) 312verify(len(outputs) == 4) 313for node in [r1, r2, sed]: 314 ml_addr = node.get_mleid_ip_addr() 315 verify(any(ml_addr in line for line in outputs)) 316 317# r1 =>> site-local mcast addr (on r1, r2, sed) without `multicast-loop`. Expected to receive from [r2, sed]. 318 319outputs = r1.cli('ping', mcast_addr) 320verify(len(outputs) == 3) 321for node in [r2, sed]: 322 ml_addr = node.get_mleid_ip_addr() 323 verify(any(ml_addr in line for line in outputs)) 324 325# sed =>> site-local mcast addr (on r1, r2, sed) without `multicast-loop`. Expected to receive from [r1, r2]. 326 327outputs = sed.cli('ping', mcast_addr) 328verify(len(outputs) >= 3) 329for node in [r1, r2]: 330 ml_addr = node.get_mleid_ip_addr() 331 verify(any(ml_addr in line for line in outputs)) 332 333# ----------------------------------------------------------------------------------------------------------------------- 334# Test finished 335 336cli.Node.finalize_all_nodes() 337 338print('\'{}\' passed.'.format(test_name)) 339