def test05_child_quick_re_attach_after_parent_reset(self): # Check the child recovery after a parent reset using quick re-attach # achieved via "Child Update" exchange. self.logger.info("Disable supervision check on the child {}.".format( self.child.name)) self.child.set(wpan.WPAN_CHILD_SUPERVISION_CHECK_TIMEOUT, "1000") self.child.set(wpan.WPAN_POLL_INTERVAL, "10000") time.sleep(0.1) # We use the "stat:ncp" wpantund property to verify that child does not # get detached. child_num_state_changes = len( wpan_table_parser.parse_list(self.child.get("stat:ncp"))) # Reset parent and wait for it to be associated. self.parent.reset_thread_radio() self.wait_for_completion(self.device_list) def check_parent_is_associated(): verify(is_associated(self.parent)) verify_within(check_parent_is_associated, WAIT_TIME) self.child.set(wpan.WPAN_POLL_INTERVAL, "100") # Verify that we again see all the child addresses in the parent's child # table. Note that child should register its addresses using "Child Update # Request" exchange. verify_within(self.check_child_addresses_on_parent, WAIT_TIME) # Verify that there was no state change on child. self.assertEqual( child_num_state_changes, len(wpan_table_parser.parse_list(self.child.get("stat:ncp"))))
def test03_Add_Multicast_Address(self): for node in self.device_list: node.add(wpan.WPAN_IP6_MULTICAST_ADDRESSES, MCAST_ADDR) addrs = wpan_table_parser.parse_list( node.get(wpan.WPAN_IP6_MULTICAST_ADDRESSES)) verify(MCAST_ADDR in addrs) node.remove(wpan.WPAN_IP6_MULTICAST_ADDRESSES, MCAST_ADDR) addrs = wpan_table_parser.parse_list( node.get(wpan.WPAN_IP6_MULTICAST_ADDRESSES)) verify(not MCAST_ADDR in addrs)
def check_off_mesh_routes(): # If a node itself adds a route, the route entry will be seen twice in # its WPAN_THREAD_OFF_MESH_ROUTES list (one time as part of network-wide # network data and again as part of the local network data). Note that # `r1 and `r2` each add a route, while `sed2` does not. r1_routes = wpan_table_parser.parse_list( self.r1.get(wpan.WPAN_THREAD_OFF_MESH_ROUTES)) self.assertEqual(len(r1_routes), NUM_ROUTES + NUM_ROUTES_LOCAL) r2_routes = wpan_table_parser.parse_list( self.r2.get(wpan.WPAN_THREAD_OFF_MESH_ROUTES)) self.assertEqual(len(r2_routes), NUM_ROUTES + NUM_ROUTES_LOCAL) sed2_routes = wpan_table_parser.parse_list( self.sed2.get(wpan.WPAN_THREAD_OFF_MESH_ROUTES)) self.assertEqual(len(sed2_routes), NUM_ROUTES)
def check_ip6_addresses(): # Verify that SLAAC addresses are removed on r2 and fed1 wpan_util.verify_no_address([self.r2, self.fed1], PREFIX) # And that user-added address matching the prefix is not removed on r1 r1_addrs = wpan_table_parser.parse_list( self.r1.get(wpan.WPAN_IP6_ALL_ADDRESSES)) verify(IP_ADDRESS in r1_addrs)
def test03_Change_Parent(self): # Remove the `child` from allowlist of `parent2` and add it to allowlist of `parent1` instead. self.child_num_state_changes = len( wpan_table_parser.parse_list(self.child1.get("stat:ncp"))) print(self.child_num_state_changes) self.parent1.allowlist_node(self.child1) self.parent2.un_allowlist_node(self.child1) # Enable supervision check on the `child` and also on `parent1`. self.child1.setprop(wpan.WPAN_CHILD_SUPERVISION_CHECK_TIMEOUT, str(CHILD_SUPERVISION_CHECK_TIMEOUT)) self.parent1.setprop(wpan.WPAN_CHILD_SUPERVISION_INTERVAL, str(PARENT_SUPERVISION_INTERVAL)) # Since child supervision is not enabled on `parent2` and the `child` is # removed from allowlist on `parent2`, after the supervision check timeout # the `child` should realize that it can no longer talk to its current # parent (`parent2`) and try to reattach. All re-attach attempts to `parent2` # should fail (due to allowlist) and cause the `child` to get detached and # search for a new parent and then attach to `parent1`. # # To verify that the `child` does get detached and attach to a new parent, we # monitor the number of state changes using wpantund property "stat:ncp". verify_within(self.check_child_is_reattached, 60, 5)
def check_child_is_reattached(self): child_stat_ncp_changes = len( wpan_table_parser.parse_list(self.child1.get("stat:ncp"))) print(child_stat_ncp_changes) verify(child_stat_ncp_changes > self.child_num_state_changes) verify(is_associated(self.child1))
def find_ip6_address_with_prefix(self, prefix): """Find an IPv6 address on node matching a given prefix. `prefix` should be an string containing the prefix. Returns a string containing the IPv6 address matching the prefix or empty string if no address found. """ if len(prefix) > 2 and prefix[-1] == ":" and prefix[-2] == ":": prefix = prefix[:-1] all_addrs = wpan_table_parser.parse_list( self.get(wpan.WPAN_IP6_ALL_ADDRESSES)) matched_addr = [addr for addr in all_addrs if addr.startswith(prefix)] return matched_addr[0] if len(matched_addr) >= 1 else ""
def test03_Verify_Parent_Reset(self): # Remember number of NCP state changes (using "stat:ncp" property) per child child_num_state_changes = [] for child in self.all_children: child_num_state_changes.append(len(wpan_table_parser.parse_list(child.get("stat:ncp")))) # Reset the parent self.router.reset_thread_radio() self.wait_for_completion(self.device_list) def check_parent_is_associated(): verify(is_associated(self.router)) verify_within(check_parent_is_associated, 20, 3) # Verify that all the children are recovered and present in the parent's child table again (within 30 seconds). verify_within(self.check_child_table, 30, 5) # Verify that number of state changes on all children stays as before (indicating they did not get detached). for i in range(len(self.all_children)): verify( child_num_state_changes[i] == len(wpan_table_parser.parse_list(self.all_children[i].get("stat:ncp"))))
def test02_verify_router2_attaches_to_router1(self): # router2 is expected to attach to router1 and adopt the mesh-local prefix from router1 verify(is_associated(self.router2)) verify( self.router2.get(wpan.WPAN_IP6_MESH_LOCAL_PREFIX) == self.router1.get(wpan.WPAN_IP6_MESH_LOCAL_PREFIX)) # Ensure that no ipv6 address starting with ML_PREFIX_2 i.e. fd00:2:: is seen verify(self.router2.find_ip6_address_with_prefix(ML_PREFIX_2) == "") # There are only 3 addresses on the router2 (link-local and mesh-local address obtained from # ML_PREFIX_1 i.e. fd00:1:: and ipv6 address added on the router2 interface) and that RLOC # address is correctly filtered (by wpantund). self.assertEqual( len( wpan_table_parser.parse_list( self.router2.get(wpan.WPAN_IP6_ALL_ADDRESSES))), 3)
def test05_add_joiners_on_commissioner(self): self.commissioner.commissioner_start() self.wait_for_completion(self.device_list) # Add `joiner1` and `joiner2` using joiner discerner. self.commissioner.commissioner_add_joiner_with_discerner( DISCERNER1, D_LEN1, PSK1, JOINER_TIMOUT) self.commissioner.commissioner_add_joiner_with_discerner( DISCERNER2, D_LEN2, PSK2, JOINER_TIMOUT) # Add `joiner3` using EUI64 Hardware Address of joiner3. joiner3_hw_addr = self.joiner3.get( wpan.WPAN_HW_ADDRESS)[1:-1] # Remove the `[]` self.commissioner.commissioner_add_joiner(joiner3_hw_addr, PSK3, JOINER_TIMOUT) verify( len( wpan_table_parser.parse_list( self.commissioner.get( wpan.WPAN_THREAD_COMMISSIONER_JOINERS))) == 3)
def test10_add_ipv6_address_for_same_prefix(self): # Explicitly add an ipv6 address with the prefix on r1, this should add the prefix on all nodes # but no ipv6 address should be added on other nodes self.r1.add_ip6_address_on_interface(IP_ADDRESS) # Add the prefix on r2 (with SLAAC flag), this should add SLAAC prefix on all nodes. And slaac related # ipv6 address on all nodes except r1 as it already has an address with SLAAC prefix self.r2.add_prefix(PREFIX, stable=True, on_mesh=True, slaac=True) self.wait_for_completion(self.device_list) verify_within(self.check_prefix_and_slaac_address_are_added, WAIT_INTERVAL) # Verify that on r1 we do see the user-added address r1_addrs = wpan_table_parser.parse_list( self.r1.get(wpan.WPAN_IP6_ALL_ADDRESSES)) verify(IP_ADDRESS in r1_addrs) # Also verify that adding the prefix on r2 did not add a SLAAC address for same prefix on r1 r1_addrs.remove(IP_ADDRESS) self.logger.info("r1_addrs: {}".format(r1_addrs)) self.wait_for_completion(self.device_list) verify(all([not addr.startswith(PREFIX[:-1]) for addr in r1_addrs]))
def test04_verify_network_data_on_same_prefix_add_remove(self): # Repeat the `test_prefix_add_remove()` under different situations # where same prefix is added/removed by other nodes in the network # or added as an off-mesh route. num_routes = 0 self.logger.info( f"========= Add same stable prefix {prefix1} on leader node =========" ) self.leader.add_prefix(prefix1, stable=False) self.prefix_add_remove() self.logger.info( f"========= Add same temporary prefix {prefix2} on leader node =========" ) self.leader.add_prefix(prefix2, stable=True) self.prefix_add_remove() self.logger.info( f"========= Remove stable prefix {prefix1} from leader node =========" ) self.leader.remove_prefix(prefix1) self.prefix_add_remove() self.logger.info( f"========= Remove temporary prefix {prefix2} from leader node =========" ) self.leader.remove_prefix(prefix2) self.prefix_add_remove() self.logger.info( f"-------- Add off-mesh route based on temporary prefix {prefix1} on leader node --------" ) self.leader.add_route_using_prefix(prefix1, stable=False) # Wait till network data is updated and all nodes see all the added off-mesh routes. time.sleep(WAIT_TIME) num_routes += 1 self.prefix_add_remove() verify( len( wpan_table_parser.parse_list( self.ed2.get(wpan.WPAN_THREAD_OFF_MESH_ROUTES))) == num_routes) self.logger.info( f"-------- Add off-mesh route based on stable prefix {prefix2} on leader node --------" ) self.leader.add_route_using_prefix(prefix2, stable=True) # Wait till network data is updated and all nodes see all the added off-mesh routes. time.sleep(WAIT_TIME) num_routes += 1 self.prefix_add_remove() verify( len( wpan_table_parser.parse_list( self.ed2.get(wpan.WPAN_THREAD_OFF_MESH_ROUTES))) == num_routes) self.logger.info( f"========= Add stable prefix {prefix3} on leader node =========") self.leader.add_prefix(prefix3, stable=True) # Wait till network data is updated and all nodes see all the added off-mesh routes. self.prefix_add_remove() verify( len( wpan_table_parser.parse_list( self.ed2.get(wpan.WPAN_THREAD_OFF_MESH_ROUTES))) == num_routes) self.logger.info( f"-------- Remove off-mesh route based on stable prefix {prefix2} on leader node --------" ) self.leader.remove_route(prefix2) # Wait till network data is updated and all nodes see all the added off-mesh routes. time.sleep(WAIT_TIME) num_routes -= 1 self.prefix_add_remove() verify( len( wpan_table_parser.parse_list( self.ed2.get(wpan.WPAN_THREAD_OFF_MESH_ROUTES))) == num_routes) self.logger.info( f"-------- Remove off-mesh route based on temporary prefix {prefix1} on leader node --------" ) self.leader.remove_route(prefix1) # Wait till network data is updated and all nodes see all the added off-mesh routes. time.sleep(WAIT_TIME) num_routes -= 1 self.prefix_add_remove() verify( len( wpan_table_parser.parse_list( self.ed2.get(wpan.WPAN_THREAD_OFF_MESH_ROUTES))) == num_routes)
def check_child_is_removed_from_parent2_table(self): child_table = wpan_table_parser.parse_list( self.parent2.get(wpan.WPAN_THREAD_CHILD_TABLE)) verify(len(child_table) == 0)
def check_multicast_addresses(self, node, mcast_addr_list): addrs = wpan_table_parser.parse_list( node.get(wpan.WPAN_IP6_MULTICAST_ADDRESSES)) for addr in mcast_addr_list: verify(addr in addrs)