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: Address Cache Table 36# 37# This test verifies the behavior of `AddressResolver` module and entries in 38# address cache table. It also tests the behavior of nodes when there are 39# topology changes in the network (e.g., a child switches parent). In 40# particular, the test covers the address cache update through snooping, i.e., 41# the logic which inspects forwarded frames to update address cache table if 42# source RLOC16 on a received frame differs from an existing entry in the 43# address cache table. 44# 45# Network topology: 46# 47# r1 ---- r2 ---- r3 48# | | | 49# | | | 50# c1 c2(s) c3 51# 52# c1 and c3 are FED children, c2 is an SED which is first attached to r2 and 53# then forced to switch to r3. 54 55test_name = __file__[:-3] if __file__.endswith('.py') else __file__ 56print('-' * 120) 57print('Starting \'{}\''.format(test_name)) 58 59# ----------------------------------------------------------------------------------------------------------------------- 60# Creating `cli.Nodes` instances 61 62speedup = 25 63cli.Node.set_time_speedup_factor(speedup) 64 65r1 = cli.Node() 66r2 = cli.Node() 67r3 = cli.Node() 68c1 = cli.Node() 69c2 = cli.Node() 70c3 = cli.Node() 71 72# ----------------------------------------------------------------------------------------------------------------------- 73# Form topology 74 75r1.allowlist_node(r2) 76r1.allowlist_node(c1) 77 78r2.allowlist_node(r1) 79r2.allowlist_node(r3) 80r2.allowlist_node(c2) 81 82r3.allowlist_node(r2) 83r3.allowlist_node(c3) 84 85c1.allowlist_node(r1) 86c2.allowlist_node(r2) 87c3.allowlist_node(r3) 88 89r1.form('addrrslvr') 90r2.join(r1) 91r3.join(r1) 92c1.join(r1, cli.JOIN_TYPE_REED) 93c2.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE) 94c3.join(r1, cli.JOIN_TYPE_REED) 95c2.set_pollperiod(400) 96 97verify(r1.get_state() == 'leader') 98verify(r2.get_state() == 'router') 99verify(r3.get_state() == 'router') 100verify(c1.get_state() == 'child') 101verify(c2.get_state() == 'child') 102verify(c3.get_state() == 'child') 103 104# ----------------------------------------------------------------------------------------------------------------------- 105# Test Implementation 106 107# Wait till first router has either established a link or 108# has a valid "next hop" towards all other routers. 109 110r1_rloc16 = int(r1.get_rloc16(), 16) 111 112 113def check_r1_router_table(): 114 table = r1.get_router_table() 115 verify(len(table) == 3) 116 for entry in table: 117 verify(int(entry['RLOC16'], 0) == r1_rloc16 or int(entry['Link']) == 1 or int(entry['Next Hop']) != 63) 118 119 120verify_within(check_r1_router_table, 120) 121 122# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 123 124r1_address = r1.get_mleid_ip_addr() 125c1_address = c1.get_mleid_ip_addr() 126c2_address = c2.get_mleid_ip_addr() 127c3_address = c3.get_mleid_ip_addr() 128 129r1_rloc16 = int(r1.get_rloc16(), 16) 130r2_rloc16 = int(r2.get_rloc16(), 16) 131r3_rloc16 = int(r3.get_rloc16(), 16) 132c1_rloc16 = int(c1.get_rloc16(), 16) 133c3_rloc16 = int(c3.get_rloc16(), 16) 134 135# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 136# From r1 ping c2 and c3 137 138r1.ping(c2_address) 139r1.ping(c3_address) 140 141# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 142# Verify that address cache table contains both c2 and c3 addresses c2 143# address should match its parent r2 (since c2 is sleepy), and c1 144# address should match to itself (since c3 is fed). 145 146cache_table = r1.get_eidcache() 147verify(len(cache_table) >= 2) 148 149for entry in cache_table: 150 fields = entry.strip().split(' ') 151 verify(fields[2] == 'cache') 152 if fields[0] == c2_address: 153 verify(int(fields[1], 16) == r2_rloc16) 154 elif fields[0] == c3_address: 155 verify(int(fields[1], 16) == c3_rloc16) 156 else: 157 verify(False) 158 159# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 160# Force c2 to switch its parent from r2 to r3 161# 162# New network topology 163# 164# r1 ---- r2 ---- r3 165# | /\ 166# | / \ 167# c1 c2(s) c3 168 169r2.un_allowlist_node(c2) 170r3.allowlist_node(c2) 171c2.allowlist_node(r3) 172 173c2.thread_stop() 174c2.thread_start() 175 176 177def check_c2_attaches_to_r3(): 178 verify(c2.get_state() == 'child') 179 verify(int(c2.get_parent_info()['Rloc'], 16) == r3_rloc16) 180 181 182verify_within(check_c2_attaches_to_r3, 10) 183 184 185def check_r2_child_table_is_empty(): 186 verify(len(r2.get_child_table()) == 0) 187 188 189verify_within(check_r2_child_table_is_empty, 10) 190 191# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 192# Note that r1 still has r2 as the destination for c2's address in its 193# address cache table. But since r2 is aware that c2 is no longer its 194# child, when it receives the IPv6 message with c2's address, r2 195# itself would do an address query for the address and forward the 196# IPv6 message. 197 198cache_table = r1.get_eidcache() 199for entry in cache_table: 200 fields = entry.strip().split(' ') 201 if fields[0] == c2_address: 202 verify(int(fields[1], 16) == r2_rloc16) 203 break 204else: 205 verify(False) 206 207r1.ping(c2_address) 208 209# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 210# Ping c1 from c2. This will go through c1's parent r1. r1 upon 211# receiving and forwarding the message should update its address 212# cache table for c2 (address cache update through snooping). 213 214c2.ping(c1_address) 215 216cache_table = r1.get_eidcache() 217 218for entry in cache_table: 219 fields = entry.strip().split(' ') 220 if fields[0] == c2_address: 221 verify(int(fields[1], 16) == r3_rloc16) 222 verify(fields[2] == 'snoop') 223 break 224else: 225 verify(False) 226 227# ----------------------------------------------------------------------------------------------------------------------- 228# Test finished 229 230cli.Node.finalize_all_nodes() 231 232print('\'{}\' passed.'.format(test_name)) 233