1#!/usr/bin/env python 2# 3# Copyright (c) 2019, 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 wpan 30from wpan import verify 31 32# ----------------------------------------------------------------------------------------------------------------------- 33# Test description: 34# 35# This test covers behavior of wpantund feature for managing of host interface routes (related to off-mesh routes 36# within the Thread network). This feature can be enabled using "Daemon:OffMeshRoute:AutoAddOnInterface" property (is 37# enabled by default). 38# 39# A route corresponding to an off-mesh route would be added on host primary interface (by wpantund), 40# if it is added by at least one (other) device within the network and 41# (a) either it is not added by host/this-device, or 42# (b) if it is also added by the device itself then 43# - filtering of self added routes is not enabled, and 44# - it is added at lower preference level. 45 46test_name = __file__[:-3] if __file__.endswith('.py') else __file__ 47print('-' * 120) 48print('Starting \'{}\''.format(test_name)) 49 50# ----------------------------------------------------------------------------------------------------------------------- 51# Utility functions 52 53 54def verify_interface_routes(node, route_list): 55 """ 56 This function verifies that node has the same interface routes as given by `route_list` which is an array of 57 tuples of (route, prefix_len, metric). 58 """ 59 node_routes = wpan.parse_interface_routes_result(node.get(wpan.WPAN_IP6_INTERFACE_ROUTES)) 60 61 verify(len(route_list) == len(node_routes)) 62 63 for route in route_list: 64 for node_route in node_routes: 65 if (node_route.route_prefix, node_route.prefix_len, node_route.metric) == route: 66 break 67 else: 68 raise wpan.VerifyError('Did not find route {} on node {}'.format(route, node)) 69 70 71# ----------------------------------------------------------------------------------------------------------------------- 72# Creating `wpan.Nodes` instances 73 74speedup = 4 75wpan.Node.set_time_speedup_factor(speedup) 76 77r1 = wpan.Node() 78r2 = wpan.Node() 79r3 = wpan.Node() 80c3 = wpan.Node() 81 82# ----------------------------------------------------------------------------------------------------------------------- 83# Init all nodes 84 85wpan.Node.init_all_nodes() 86 87# ----------------------------------------------------------------------------------------------------------------------- 88# Build network topology 89# 90# Test topology: 91# 92# r1 ---- r2 93# \ / 94# \ / 95# \ / 96# r3 ---- c3 97# 98# 3 routers, c3 is added to ensure r3 is promoted to a router quickly! 99 100r1.form("route-test") 101 102r1.allowlist_node(r2) 103r2.allowlist_node(r1) 104r2.join_node(r1, wpan.JOIN_TYPE_ROUTER) 105 106r3.allowlist_node(r2) 107r2.allowlist_node(r3) 108r3.join_node(r2, wpan.JOIN_TYPE_ROUTER) 109 110c3.allowlist_node(r3) 111r3.allowlist_node(c3) 112c3.join_node(r3, wpan.JOIN_TYPE_END_DEVICE) 113 114r3.allowlist_node(r1) 115r1.allowlist_node(r3) 116 117# ----------------------------------------------------------------------------------------------------------------------- 118# Test implementation 119 120ROUTE1 = 'fd00:abba::' 121LEN1 = 64 122 123ROUTE2 = 'fd00:cafe:feed::' 124LEN2 = 64 125 126ROUTE3 = 'fd00:abba::' 127LEN3 = 48 128 129ROUTE4 = 'fd00:1234::' 130LEN4 = 64 131 132# Route Priority for off-mesh routes 133HIGH_PRIORITY = 1 134MEDIUM_PRIORITY = 0 135LOW_PRIORITY = -1 136 137# Host route metric mapping to off-mesh route (note lower metric value is higher priority) 138HIGH_METRIC = 1 139MEDIUM_METRIC = 256 140LOW_METRIC = 512 141 142WAIT_TIME = 10 143 144# Verify the default daemon configuration for managing host/off-mesh routes 145verify(r1.get(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_AUTO_ADD_ON_INTERFACE) == 'true') 146verify(r1.get(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_FILTER_SELF_AUTO_ADDED) == 'true') 147 148# Disable the auto route add on r2. 149r2.set(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_AUTO_ADD_ON_INTERFACE, 'false') 150 151# Verify the host interface routes are empty when we start. 152verify_interface_routes(r1, []) 153 154# Add all 3 routes on r2. 155r2.add_route(ROUTE1, prefix_len=LEN1, priority=LOW_PRIORITY) 156r2.add_route(ROUTE2, prefix_len=LEN2, priority=MEDIUM_PRIORITY) 157r2.add_route(ROUTE3, prefix_len=LEN3, priority=HIGH_PRIORITY) 158 159 160# We expect to see all 3 routes added on r1 host interface with same priority levels as r2. 161def check_routes_on_r1_1(): 162 verify_interface_routes(r1, [(ROUTE1, LEN1, LOW_METRIC), (ROUTE2, LEN2, MEDIUM_METRIC), 163 (ROUTE3, LEN3, HIGH_METRIC)]) 164 165 166wpan.verify_within(check_routes_on_r1_1, WAIT_TIME) 167 168# - - - - - - - - - - - - - - - - - - - - - - - - - - - - 169 170# Add the same routes on r3 with different priorities. 171r3.add_route(ROUTE1, prefix_len=LEN1, priority=MEDIUM_PRIORITY) 172r3.add_route(ROUTE2, prefix_len=LEN2, priority=LOW_PRIORITY) 173 174 175# We expect the host interface routes on r1 to change accordingly 176def check_routes_on_r1_2(): 177 route_list = [(ROUTE1, LEN1, MEDIUM_METRIC), (ROUTE2, LEN2, MEDIUM_METRIC), (ROUTE3, LEN3, HIGH_METRIC)] 178 verify_interface_routes(r1, route_list) 179 180 181wpan.verify_within(check_routes_on_r1_2, WAIT_TIME) 182verify_interface_routes(r2, []) 183 184# - - - - - - - - - - - - - - - - - - - - - - - - - - - - 185 186# Remove the previously added routes from r2. 187r2.remove_route(ROUTE1, prefix_len=LEN1) 188r2.remove_route(ROUTE2, prefix_len=LEN2) 189r2.remove_route(ROUTE3, prefix_len=LEN3) 190 191 192# We expect the host interface routes on r1 to again change accordingly: 193def check_routes_on_r1_3(): 194 verify_interface_routes(r1, [(ROUTE1, LEN1, MEDIUM_METRIC), (ROUTE2, LEN2, LOW_METRIC)]) 195 196 197wpan.verify_within(check_routes_on_r1_3, WAIT_TIME) 198verify_interface_routes(r2, []) 199 200# - - - - - - - - - - - - - - - - - - - - - - - - - - - - 201 202# Disable "Daemon:OffMeshRoute:FilterSelfAutoAdded" feature on wpantund. 203# 204# The route should be added on host primary interface, if it 205# is added by at least one other device within the network and, 206# (a) either it is not added by host/this-device, or 207# (b) if it is also added by device then 208# - filtering of self added routes is not enabled, and 209# - it is added at lower preference level. 210 211r1.set(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_FILTER_SELF_AUTO_ADDED, 'false') 212verify(r1.get(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_FILTER_SELF_AUTO_ADDED) == 'false') 213 214# Add ROUTE1 on r1 with low-priority. Since it's also present on r3 with 215# medium priority, we should still see the route on host (as medium). 216 217r1.add_route(ROUTE1, prefix_len=LEN1, priority=LOW_PRIORITY) 218 219verify_interface_routes(r1, [(ROUTE1, LEN1, MEDIUM_METRIC), (ROUTE2, LEN2, LOW_METRIC)]) 220 221# Now change ROUTE1 on r1 to be same priority as on r2, now the route should 222# no longer be present on host interface routes. 223 224r1.remove_route(ROUTE1, prefix_len=LEN1) 225r1.add_route(ROUTE1, prefix_len=LEN1, priority=MEDIUM_PRIORITY) 226 227verify_interface_routes(r1, [(ROUTE2, LEN2, LOW_METRIC)]) 228 229# Adding ROUTE2 with higher priority should remove it from interface routes 230r1.add_route(ROUTE2, prefix_len=LEN2, priority=MEDIUM_PRIORITY) 231 232verify_interface_routes(r1, []) 233 234# Adding a new ROUTE4 on r1 should not change anything related to interface host routes. 235r1.add_route(ROUTE4, prefix_len=LEN4, priority=MEDIUM_METRIC) 236verify_interface_routes(r1, []) 237 238# Removing ROUTE1 and ROUT2 on r1 should cause them to be added back on host 239# interface (since they are still present as off-mesh routes on r3). 240r1.remove_route(ROUTE1, prefix_len=LEN1) 241r1.remove_route(ROUTE2, prefix_len=LEN2) 242 243verify_interface_routes(r1, [(ROUTE1, LEN1, MEDIUM_METRIC), (ROUTE2, LEN2, LOW_METRIC)]) 244 245verify_interface_routes(r2, []) 246 247# - - - - - - - - - - - - - - - - - - - - - - - - - - - - 248# Enable "Daemon:OffMeshRoute:FilterSelfAutoAdded" feature on wpantund. 249 250r1.set(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_FILTER_SELF_AUTO_ADDED, 'true') 251verify(r1.get(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_FILTER_SELF_AUTO_ADDED) == 'true') 252 253# Adding ROUTE1 with any priority should remove it from host interface routes. 254r1.add_route(ROUTE1, prefix_len=LEN1, priority=LOW_PRIORITY) 255 256verify_interface_routes(r1, [(ROUTE2, LEN2, LOW_METRIC)]) 257 258r1.remove_route(ROUTE1, prefix_len=LEN1) 259 260verify_interface_routes(r1, [(ROUTE1, LEN1, MEDIUM_METRIC), (ROUTE2, LEN2, LOW_METRIC)]) 261 262verify_interface_routes(r2, []) 263 264# ----------------------------------------------------------------------------------------------------------------------- 265# Test finished 266 267wpan.Node.finalize_all_nodes() 268 269print('\'{}\' passed.'.format(test_name)) 270