1#!/usr/bin/env python3 2# 3# Copyright (c) 2018, 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: Test behavior of "Inform Previous Parent" feature 34# 35# With this feature enabled, when a child attaches to a new parent, it will send 36# an IP message (with empty payload and mesh-local IP address as the source 37# address) to its previous parent. Upon receiving this message the previous 38# parent would immediately remove the child from its child table. Without this 39# feature, the child entry on previous parent would stay (and parent would 40# continue to queue messages for the sleepy child) until the child is timed out 41# and removed from child table. 42# 43# 44# Test topology: 45# 46# `child` is first attached to `parent2`. It is then forced to switch to `parent1` 47# 48# parent1--- parent2 49# . / 50# \ / 51# . / 52# child 53# 54# This test verifies the behavior of the child and parents under this feature. 55# 56# Note that `OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH` is enabled in 57# `openthread-core-toranj.config.h` header file. 58# 59 60test_name = __file__[:-3] if __file__.endswith('.py') else __file__ 61print('-' * 120) 62print('Starting \'{}\''.format(test_name)) 63 64# ----------------------------------------------------------------------------------------------------------------------- 65# Creating `wpan.Nodes` instances 66 67speedup = 4 68wpan.Node.set_time_speedup_factor(speedup) 69 70parent1 = wpan.Node() 71parent2 = wpan.Node() 72child = wpan.Node() 73 74# ----------------------------------------------------------------------------------------------------------------------- 75# Init all nodes 76 77wpan.Node.init_all_nodes() 78 79# ----------------------------------------------------------------------------------------------------------------------- 80# Build network topology 81# 82# `child` is first attached to `parent2`. It is then forced to switch to `parent1`. 83# 84# parent1--- parent2 85# . / 86# \ / 87# . / 88# child 89# 90 91parent1.allowlist_node(parent2) 92parent2.allowlist_node(parent1) 93parent2.allowlist_node(child) 94 95parent1.form("inform-parent") 96parent2.join_node(parent1, wpan.JOIN_TYPE_ROUTER) 97child.join_node(parent2, wpan.JOIN_TYPE_SLEEPY_END_DEVICE) 98child.set(wpan.WPAN_POLL_INTERVAL, '300') 99 100# ----------------------------------------------------------------------------------------------------------------------- 101# Test implementation 102# 103 104CHILD_SUPERVISION_CHECK_TIMEOUT = 2 105PARENT_SUPERVISION_INTERVAL = 1 106 107# Verify the `child` is attached to `parent2`. 108child_table = wpan.parse_list(parent2.get(wpan.WPAN_THREAD_CHILD_TABLE)) 109verify(len(child_table) == 1) 110 111# Remove the `child` from allowlist of `parent2` and add it to allowlist 112# of `parent1` instead. 113parent1.allowlist_node(child) 114parent2.un_allowlist_node(child) 115 116# Enable supervision check on the `child` and also on `parent1`. 117 118child.set( 119 wpan.WPAN_CHILD_SUPERVISION_CHECK_TIMEOUT, 120 str(CHILD_SUPERVISION_CHECK_TIMEOUT), 121) 122parent1.set(wpan.WPAN_CHILD_SUPERVISION_INTERVAL, str(PARENT_SUPERVISION_INTERVAL)) 123 124# Since child supervision is not enabled on `parent2` and the `child` is 125# removed from allowlist on `parent2`, after the supervision check timeout 126# the `child` should realize that it can no longer talk to its current 127# parent (`parent2`) and try to reattach. All re-attach attempts to `parent2` 128# should fail (due to allowlist) and cause the `child` to get detached and 129# search for a new parent and then attach to `parent1`. 130# 131# To verify that the `child` does get detached and attach to a new parent, we 132# monitor the number of state changes using wpantund property "stat:ncp". 133 134child_num_state_changes = len(wpan.parse_list(child.get("stat:ncp"))) 135 136 137def check_child_is_reattached(): 138 verify(len(wpan.parse_list(child.get("stat:ncp"))) > child_num_state_changes) 139 verify(child.is_associated()) 140 141 142wpan.verify_within(check_child_is_reattached, CHILD_SUPERVISION_CHECK_TIMEOUT / speedup + 5) 143 144# Verify that the `child` is now attached to `parent1` 145child_table = wpan.parse_list(parent1.get(wpan.WPAN_THREAD_CHILD_TABLE)) 146verify(len(child_table) == 1) 147 148# Finally verify that the `child` is removed from previous parent's child 149# table (which indicates that the `child` did indeed inform its previous 150# parent). 151 152 153def check_child_is_removed_from_parent2_table(): 154 child_table = wpan.parse_list(parent2.get(wpan.WPAN_THREAD_CHILD_TABLE)) 155 verify(len(child_table) == 0) 156 157 158wpan.verify_within(check_child_is_removed_from_parent2_table, 1) 159 160# ----------------------------------------------------------------------------------------------------------------------- 161# Test finished 162 163wpan.Node.finalize_all_nodes() 164 165print('\'{}\' passed.'.format(test_name)) 166