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 on-mesh prefixes 36# within the Thread network). 37# 38# When enabled, wpantund would add a route on host primary interface for any prefix from thread network (with on-mesh 39# flag set). This in turn ensures that traffic destined to an IPv6 address matching the prefix would be correctly 40# forwarded to the `wpan` interface on host. 41 42test_name = __file__[:-3] if __file__.endswith('.py') else __file__ 43print('-' * 120) 44print('Starting \'{}\''.format(test_name)) 45 46# ----------------------------------------------------------------------------------------------------------------------- 47# Utility functions 48 49 50def verify_interface_routes(node, route_list): 51 """ 52 This function verifies that node has the same interface routes as given by `route_list` which is an array of 53 tuples of (route, prefix_len, metric). 54 """ 55 node_routes = wpan.parse_interface_routes_result(node.get(wpan.WPAN_IP6_INTERFACE_ROUTES)) 56 57 verify(len(route_list) == len(node_routes)) 58 59 for route in route_list: 60 for node_route in node_routes: 61 if (node_route.route_prefix, node_route.prefix_len, node_route.metric) == route: 62 break 63 else: 64 raise wpan.VerifyError('Did not find route {} on node {}'.format(route, node)) 65 66 67# ----------------------------------------------------------------------------------------------------------------------- 68# Creating `wpan.Nodes` instances 69 70speedup = 4 71wpan.Node.set_time_speedup_factor(speedup) 72 73r1 = wpan.Node() 74r2 = wpan.Node() 75c2 = wpan.Node() 76 77# ----------------------------------------------------------------------------------------------------------------------- 78# Init all nodes 79 80wpan.Node.init_all_nodes() 81 82# ----------------------------------------------------------------------------------------------------------------------- 83# Build network topology 84# 85 86r1.form("prefix-route") 87 88r1.allowlist_node(r2) 89r2.allowlist_node(r1) 90r2.join_node(r1, wpan.JOIN_TYPE_ROUTER) 91 92c2.allowlist_node(r2) 93r2.allowlist_node(c2) 94c2.join_node(r2, wpan.JOIN_TYPE_END_DEVICE) 95 96# ----------------------------------------------------------------------------------------------------------------------- 97# Test implementation 98 99PREFIX1 = 'fd00:abba::' 100ADDRESS1 = PREFIX1 + '1' 101LEN1 = 64 102 103PREFIX2 = 'fd00:cafe:feed::' 104LEN2 = 64 105 106PREFIX3 = 'fd00:1234::' 107LEN3 = 64 108 109ROUTE4 = 'fd00:1234::' 110LEN4 = 48 111 112ROUTE5 = 'fd00:beef::' 113LEN5 = 64 114 115MEDIUM_METRIC = 256 116MEDIUM_PRIORITY = 0 117 118WAIT_TIME = 10 119 120# Verify the default daemon configuration 121verify(r1.get(wpan.WPAN_DAEMON_ON_MESH_PREFIX_AUTO_ADD_AS_INTERFACE_ROUTE) == 'true') 122 123r1.set(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_AUTO_ADD_ON_INTERFACE, 'false') 124verify(r1.get(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_AUTO_ADD_ON_INTERFACE) == 'false') 125 126# Add on-mesh prefixes 1, 2, and 3 and off-mesh route 5 on r2. 127r2.add_prefix(PREFIX1, prefix_len=LEN1, on_mesh=True, slaac=False) 128r2.add_prefix(PREFIX2, prefix_len=LEN2, on_mesh=True, slaac=True) 129r2.add_prefix(PREFIX3, prefix_len=LEN2, on_mesh=False, slaac=False) 130r2.add_route(ROUTE5, prefix_len=LEN5, priority=MEDIUM_PRIORITY) 131 132 133# We expect to only see routes associated the first two (which are on-mesh) on r1. 134def check_routes_on_r1_is_prefix1_and_prefix2(): 135 verify_interface_routes(r1, [(PREFIX1, LEN1, MEDIUM_METRIC), (PREFIX2, LEN2, MEDIUM_METRIC)]) 136 137 138wpan.verify_within(check_routes_on_r1_is_prefix1_and_prefix2, WAIT_TIME) 139 140# Remove all prefixes 141r2.remove_prefix(PREFIX1, prefix_len=LEN1) 142r2.remove_prefix(PREFIX2, prefix_len=LEN2) 143r2.remove_prefix(PREFIX3, prefix_len=LEN2) 144r2.remove_route(ROUTE5, prefix_len=LEN5) 145 146 147# We expect all associated routes to be removed on r1 148def check_routes_on_r1_is_empty(): 149 verify_interface_routes(r1, []) 150 151 152wpan.verify_within(check_routes_on_r1_is_empty, WAIT_TIME) 153 154# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 155# Test behavior when an address with same prefix in already added on the interface 156 157r1.add_ip6_address_on_interface(ADDRESS1, prefix_len=LEN1) 158r2.add_prefix(PREFIX1, prefix_len=LEN1, on_mesh=True, slaac=False) 159 160 161def check_routes_on_r1_is_only_prefix1(): 162 verify_interface_routes(r1, [(PREFIX1, LEN1, MEDIUM_METRIC)]) 163 164 165wpan.verify_within(check_routes_on_r1_is_only_prefix1, WAIT_TIME) 166 167r1.remove_ip6_address_on_interface(ADDRESS1, prefix_len=LEN1) 168wpan.verify_within(check_routes_on_r1_is_only_prefix1, WAIT_TIME) 169 170r2.remove_prefix(PREFIX1, prefix_len=LEN1) 171 172wpan.verify_within(check_routes_on_r1_is_empty, WAIT_TIME) 173 174# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 175# Test behavior when we have similar route and prefix (different prefix_len) 176 177r1.set(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_AUTO_ADD_ON_INTERFACE, 'true') 178verify(r1.get(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_AUTO_ADD_ON_INTERFACE) == 'true') 179 180r2.add_route(ROUTE5, prefix_len=LEN5, priority=MEDIUM_PRIORITY) 181r2.add_route(ROUTE4, prefix_len=LEN4, priority=MEDIUM_PRIORITY) 182r1.add_prefix(PREFIX3, prefix_len=LEN3, on_mesh=True, slaac=False) 183 184 185def check_routes_on_r1_is_prefix3_route4_and_route5(): 186 route_list = [(PREFIX3, LEN3, MEDIUM_METRIC), (ROUTE4, LEN4, MEDIUM_METRIC), (ROUTE5, LEN5, MEDIUM_METRIC)] 187 verify_interface_routes(r1, route_list) 188 189 190wpan.verify_within(check_routes_on_r1_is_prefix3_route4_and_route5, WAIT_TIME) 191 192r2.remove_route(ROUTE5, prefix_len=LEN5) 193r1.remove_prefix(PREFIX3, prefix_len=LEN3) 194 195 196def check_routes_on_r1_is_only_route4(): 197 verify_interface_routes(r1, [(ROUTE4, LEN4, MEDIUM_METRIC)]) 198 199 200wpan.verify_within(check_routes_on_r1_is_only_route4, WAIT_TIME) 201 202# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 203# Test behavior when feature is disabled 204 205r1.set(wpan.WPAN_DAEMON_ON_MESH_PREFIX_AUTO_ADD_AS_INTERFACE_ROUTE, 'false') 206verify(r1.get(wpan.WPAN_DAEMON_ON_MESH_PREFIX_AUTO_ADD_AS_INTERFACE_ROUTE) == 'false') 207 208r1.add_prefix(PREFIX3, prefix_len=LEN3, on_mesh=True, slaac=False) 209wpan.verify_within(check_routes_on_r1_is_only_route4, WAIT_TIME) 210 211# ----------------------------------------------------------------------------------------------------------------------- 212# Test finished 213 214wpan.Node.finalize_all_nodes() 215 216print('\'{}\' passed.'.format(test_name)) 217