| #!/usr/bin/env python |
| # |
| # Copyright (c) 2019, The OpenThread Authors. |
| # All rights reserved. |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are met: |
| # 1. Redistributions of source code must retain the above copyright |
| # notice, this list of conditions and the following disclaimer. |
| # 2. Redistributions in binary form must reproduce the above copyright |
| # notice, this list of conditions and the following disclaimer in the |
| # documentation and/or other materials provided with the distribution. |
| # 3. Neither the name of the copyright holder nor the |
| # names of its contributors may be used to endorse or promote products |
| # derived from this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| # POSSIBILITY OF SUCH DAMAGE. |
| |
| import wpan |
| from wpan import verify |
| |
| # ----------------------------------------------------------------------------------------------------------------------- |
| # Test description: |
| # |
| # This test covers behavior of wpantund feature for managing of host interface routes (related to off-mesh routes |
| # within the Thread network). This feature can be enabled using "Daemon:OffMeshRoute:AutoAddOnInterface" property (is |
| # enabled by default). |
| # |
| # A route corresponding to an off-mesh route would be added on host primary interface (by wpantund), |
| # if it is added by at least one (other) device within the network and |
| # (a) either it is not added by host/this-device, or |
| # (b) if it is also added by the device itself then |
| # - filtering of self added routes is not enabled, and |
| # - it is added at lower preference level. |
| |
| |
| test_name = __file__[:-3] if __file__.endswith('.py') else __file__ |
| print('-' * 120) |
| print('Starting \'{}\''.format(test_name)) |
| |
| # ----------------------------------------------------------------------------------------------------------------------- |
| # Utility functions |
| |
| |
| def verify_interface_routes(node, route_list): |
| """ |
| This function verifies that node has the same interface routes as given by `route_list` which is an array of |
| tuples of (route, prefix_len, metric). |
| """ |
| node_routes = wpan.parse_interface_routes_result(node.get(wpan.WPAN_IP6_INTERFACE_ROUTES)) |
| |
| verify(len(route_list) == len(node_routes)) |
| |
| for route in route_list: |
| for node_route in node_routes: |
| if (node_route.route_prefix, node_route.prefix_len, node_route.metric) == route: |
| break |
| else: |
| raise wpan.VerifyError( |
| 'Did not find route {} on node {}'.format(route, node) |
| ) |
| |
| |
| # ----------------------------------------------------------------------------------------------------------------------- |
| # Creating `wpan.Nodes` instances |
| |
| speedup = 4 |
| wpan.Node.set_time_speedup_factor(speedup) |
| |
| r1 = wpan.Node() |
| r2 = wpan.Node() |
| r3 = wpan.Node() |
| c3 = wpan.Node() |
| |
| # ----------------------------------------------------------------------------------------------------------------------- |
| # Init all nodes |
| |
| wpan.Node.init_all_nodes() |
| |
| # ----------------------------------------------------------------------------------------------------------------------- |
| # Build network topology |
| # |
| # Test topology: |
| # |
| # r1 ---- r2 |
| # \ / |
| # \ / |
| # \ / |
| # r3 ---- c3 |
| # |
| # 3 routers, c3 is added to ensure r3 is promoted to a router quickly! |
| |
| |
| r1.form("route-test") |
| |
| r1.whitelist_node(r2) |
| r2.whitelist_node(r1) |
| r2.join_node(r1, wpan.JOIN_TYPE_ROUTER) |
| |
| r3.whitelist_node(r2) |
| r2.whitelist_node(r3) |
| r3.join_node(r2, wpan.JOIN_TYPE_ROUTER) |
| |
| c3.whitelist_node(r3) |
| r3.whitelist_node(c3) |
| c3.join_node(r3, wpan.JOIN_TYPE_END_DEVICE) |
| |
| r3.whitelist_node(r1) |
| r1.whitelist_node(r3) |
| |
| # ----------------------------------------------------------------------------------------------------------------------- |
| # Test implementation |
| |
| ROUTE1 = 'fd00:abba::' |
| LEN1 = 64 |
| |
| ROUTE2 = 'fd00:cafe:feed::' |
| LEN2 = 64 |
| |
| ROUTE3 = 'fd00:abba::' |
| LEN3 = 48 |
| |
| ROUTE4 = 'fd00:1234::' |
| LEN4 = 64 |
| |
| # Route Priority for off-mesh routes |
| HIGH_PRIORITY = 1 |
| MEDIUM_PRIORITY = 0 |
| LOW_PRIORITY = -1 |
| |
| # Host route metric mapping to off-mesh route (note lower metric value is higher priority) |
| HIGH_METRIC = 1 |
| MEDIUM_METRIC = 256 |
| LOW_METRIC = 512 |
| |
| WAIT_TIME = 10 |
| |
| # Verify the default daemon configuration for managing host/off-mesh routes |
| verify(r1.get(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_AUTO_ADD_ON_INTERFACE) == 'true') |
| verify(r1.get(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_FILTER_SELF_AUTO_ADDED) == 'true') |
| |
| # Disable the auto route add on r2. |
| r2.set(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_AUTO_ADD_ON_INTERFACE, 'false') |
| |
| # Verify the host interface routes are empty when we start. |
| verify_interface_routes(r1, []) |
| |
| # Add all 3 routes on r2. |
| r2.add_route(ROUTE1, prefix_len=LEN1, priority=LOW_PRIORITY) |
| r2.add_route(ROUTE2, prefix_len=LEN2, priority=MEDIUM_PRIORITY) |
| r2.add_route(ROUTE3, prefix_len=LEN3, priority=HIGH_PRIORITY) |
| |
| |
| # We expect to see all 3 routes added on r1 host interface with same priority levels as r2. |
| def check_routes_on_r1_1(): |
| verify_interface_routes(r1, |
| [(ROUTE1, LEN1, LOW_METRIC), (ROUTE2, LEN2, MEDIUM_METRIC), (ROUTE3, LEN3, HIGH_METRIC)]) |
| |
| |
| wpan.verify_within(check_routes_on_r1_1, WAIT_TIME) |
| |
| # - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| |
| # Add the same routes on r3 with different priorities. |
| r3.add_route(ROUTE1, prefix_len=LEN1, priority=MEDIUM_PRIORITY) |
| r3.add_route(ROUTE2, prefix_len=LEN2, priority=LOW_PRIORITY) |
| |
| |
| # We expect the host interface routes on r1 to change accordingly |
| def check_routes_on_r1_2(): |
| route_list = [(ROUTE1, LEN1, MEDIUM_METRIC), (ROUTE2, LEN2, MEDIUM_METRIC), (ROUTE3, LEN3, HIGH_METRIC)] |
| verify_interface_routes(r1, route_list) |
| |
| |
| wpan.verify_within(check_routes_on_r1_2, WAIT_TIME) |
| verify_interface_routes(r2, []) |
| |
| # - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| |
| # Remove the previously added routes from r2. |
| r2.remove_route(ROUTE1, prefix_len=LEN1) |
| r2.remove_route(ROUTE2, prefix_len=LEN2) |
| r2.remove_route(ROUTE3, prefix_len=LEN3) |
| |
| |
| # We expect the host interface routes on r1 to again change accordingly: |
| def check_routes_on_r1_3(): |
| verify_interface_routes(r1, [(ROUTE1, LEN1, MEDIUM_METRIC), (ROUTE2, LEN2, LOW_METRIC)]) |
| |
| |
| wpan.verify_within(check_routes_on_r1_3, WAIT_TIME) |
| verify_interface_routes(r2, []) |
| |
| # - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| |
| # Disable "Daemon:OffMeshRoute:FilterSelfAutoAdded" feature on wpantund. |
| # |
| # The route should be added on host primary interface, if it |
| # is added by at least one other device within the network and, |
| # (a) either it is not added by host/this-device, or |
| # (b) if it is also added by device then |
| # - filtering of self added routes is not enabled, and |
| # - it is added at lower preference level. |
| |
| r1.set(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_FILTER_SELF_AUTO_ADDED, 'false') |
| verify(r1.get(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_FILTER_SELF_AUTO_ADDED) == 'false') |
| |
| # Add ROUTE1 on r1 with low-priority. Since it's also present on r3 with |
| # medium priority, we should still see the route on host (as medium). |
| |
| r1.add_route(ROUTE1, prefix_len=LEN1, priority=LOW_PRIORITY) |
| |
| verify_interface_routes(r1, [(ROUTE1, LEN1, MEDIUM_METRIC), (ROUTE2, LEN2, LOW_METRIC)]) |
| |
| # Now change ROUTE1 on r1 to be same priority as on r2, now the route should |
| # no longer be present on host interface routes. |
| |
| r1.remove_route(ROUTE1, prefix_len=LEN1) |
| r1.add_route(ROUTE1, prefix_len=LEN1, priority=MEDIUM_PRIORITY) |
| |
| verify_interface_routes(r1, [(ROUTE2, LEN2, LOW_METRIC)]) |
| |
| # Adding ROUTE2 with higher priority should remove it from interface routes |
| r1.add_route(ROUTE2, prefix_len=LEN2, priority=MEDIUM_PRIORITY) |
| |
| verify_interface_routes(r1, []) |
| |
| # Adding a new ROUTE4 on r1 should not change anything related to interface host routes. |
| r1.add_route(ROUTE4, prefix_len=LEN4, priority=MEDIUM_METRIC) |
| verify_interface_routes(r1, []) |
| |
| # Removing ROUTE1 and ROUT2 on r1 should cause them to be added back on host |
| # interface (since they are still present as off-mesh routes on r3). |
| r1.remove_route(ROUTE1, prefix_len=LEN1) |
| r1.remove_route(ROUTE2, prefix_len=LEN2) |
| |
| verify_interface_routes(r1, [(ROUTE1, LEN1, MEDIUM_METRIC), (ROUTE2, LEN2, LOW_METRIC)]) |
| |
| verify_interface_routes(r2, []) |
| |
| # - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| # Enable "Daemon:OffMeshRoute:FilterSelfAutoAdded" feature on wpantund. |
| |
| r1.set(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_FILTER_SELF_AUTO_ADDED, 'true') |
| verify(r1.get(wpan.WPAN_DAEMON_OFF_MESH_ROUTE_FILTER_SELF_AUTO_ADDED) == 'true') |
| |
| # Adding ROUTE1 with any priority should remove it from host interface routes. |
| r1.add_route(ROUTE1, prefix_len=LEN1, priority=LOW_PRIORITY) |
| |
| verify_interface_routes(r1, [(ROUTE2, LEN2, LOW_METRIC)]) |
| |
| r1.remove_route(ROUTE1, prefix_len=LEN1) |
| |
| verify_interface_routes(r1, [(ROUTE1, LEN1, MEDIUM_METRIC), (ROUTE2, LEN2, LOW_METRIC)]) |
| |
| verify_interface_routes(r2, []) |
| |
| # ----------------------------------------------------------------------------------------------------------------------- |
| # Test finished |
| |
| wpan.Node.finalize_all_nodes() |
| |
| print('\'{}\' passed.'.format(test_name)) |