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# 29 30import unittest 31 32import command 33import config 34import thread_cert 35 36# Test description: 37# 38# This test verifies behavior child supervision. 39# 40# 41# Topology: 42# 43# Parent (leader) 44# | 45# | 46# Child (sleepy). 47 48PARENT = 1 49CHILD = 2 50 51 52class ChildSupervision(thread_cert.TestCase): 53 USE_MESSAGE_FACTORY = False 54 SUPPORT_NCP = False 55 56 TOPOLOGY = { 57 PARENT: { 58 'name': 'PARENT', 59 'mode': 'rdn', 60 }, 61 CHILD: { 62 'name': 'CHILD', 63 'is_mtd': True, 64 'mode': 'n', 65 }, 66 } 67 68 def test(self): 69 parent = self.nodes[PARENT] 70 child = self.nodes[CHILD] 71 72 # Form the network. 73 74 parent.start() 75 self.simulator.go(config.LEADER_STARTUP_DELAY) 76 self.assertEqual(parent.get_state(), 'leader') 77 78 child.start() 79 self.simulator.go(5) 80 self.assertEqual(child.get_state(), 'child') 81 child.set_pollperiod(500) 82 83 self.assertEqual(int(child.get_child_supervision_check_failure_counter()), 0) 84 85 # Check the parent's child table. 86 87 table = parent.get_child_table() 88 self.assertEqual(len(table), 1) 89 self.assertEqual(table[1]['suprvsn'], int(child.get_child_supervision_interval())) 90 91 # Change the supervision interval on child. This should trigger an 92 # MLE Child Update exchange from child to parent so to inform parent 93 # about the change. Verify that parent is notified by checking the 94 # parent's child table. 95 96 child.set_child_supervision_interval(20) 97 98 self.simulator.go(2) 99 100 self.assertEqual(int(child.get_child_supervision_interval()), 20) 101 table = parent.get_child_table() 102 self.assertEqual(len(table), 1) 103 self.assertEqual(table[1]['suprvsn'], int(child.get_child_supervision_interval())) 104 105 # Change supervision check timeout on the child. 106 107 child.set_child_supervision_check_timeout(25) 108 self.assertEqual(int(child.get_child_supervision_check_timeout()), 25) 109 110 # Wait for multiple supervision intervals and ensure that child 111 # stays attached (child supervision working as expected). 112 113 self.simulator.go(110) 114 115 self.assertEqual(child.get_state(), 'child') 116 table = parent.get_child_table() 117 self.assertEqual(len(table), 1) 118 self.assertEqual(int(child.get_child_supervision_check_failure_counter()), 0) 119 120 # Disable supervision check on child. 121 122 child.set_child_supervision_check_timeout(0) 123 124 # Enable allowlist on parent without adding the child. After child 125 # timeout expires, the parent should remove the child from its child 126 # table. 127 128 parent.clear_allowlist() 129 parent.enable_allowlist() 130 131 table = parent.get_child_table() 132 child_timeout = table[1]['timeout'] 133 134 self.simulator.go(child_timeout + 1) 135 table = parent.get_child_table() 136 self.assertEqual(len(table), 0) 137 138 # Since supervision check is disabled on the child, it should 139 # continue to stay attached to parent (since data polls are acked by 140 # radio driver). 141 142 self.assertEqual(child.get_state(), 'child') 143 self.assertEqual(int(child.get_child_supervision_check_failure_counter()), 0) 144 145 # Re-enable supervision check on child. After the check timeout the 146 # child must try to exchange "Child Update" messages with parent and 147 # then detect that parent is not responding and detach. 148 149 child.set_child_supervision_check_timeout(25) 150 151 self.simulator.go(35) 152 self.assertEqual(child.get_state(), 'detached') 153 self.assertTrue(int(child.get_child_supervision_check_failure_counter()) > 0) 154 155 # Disable allowlist on parent. Child should be able to attach again. 156 157 parent.disable_allowlist() 158 self.simulator.go(30) 159 self.assertEqual(child.get_state(), 'child') 160 child.reset_child_supervision_check_failure_counter() 161 self.assertEqual(int(child.get_child_supervision_check_failure_counter()), 0) 162 163 # Set the supervision interval to zero on child (child is asking 164 # parent not to supervise it anymore). This practically behaves 165 # the same as if parent does not support child supervision 166 # feature. 167 168 child.set_child_supervision_interval(0) 169 child.set_child_supervision_check_timeout(25) 170 self.simulator.go(2) 171 172 self.assertEqual(int(child.get_child_supervision_interval()), 0) 173 self.assertEqual(int(child.get_child_supervision_check_timeout()), 25) 174 175 table = parent.get_child_table() 176 self.assertEqual(len(table), 1) 177 self.assertEqual(table[2]['suprvsn'], int(child.get_child_supervision_interval())) 178 179 # Wait for multiple check timeouts. The child should still stay 180 # attached to parent. 181 182 self.simulator.go(100) 183 self.assertEqual(child.get_state(), 'child') 184 self.assertEqual(len(parent.get_child_table()), 1) 185 self.assertTrue(int(child.get_child_supervision_check_failure_counter()) > 0) 186 187 188if __name__ == '__main__': 189 unittest.main() 190