def test08_verify_behavior_when_cache_table_is_full(self): # Check behavior when cache table is full. for num in range(NUM_QUERY_ADDRS): self.r1.send_udp_data(PREFIX + "900:" + str(num), PORT, random_string(10), self.r1_address, timeout=0.05) cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) verify(len(cache_table) == MAX_CACHE_ENTRIES) # Send from c2 to r1 and verify that snoop optimization uses at most`MAX_SNOOPED_NON_EVICTABLE` entries. for num in range(NUM_ADDRESSES): self.transmit_receive_udp_message(self.c2, self.r1, PREFIX + "c2:" + str(num), self.r1_address, PORT) cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) verify(len(cache_table) == MAX_CACHE_ENTRIES) snooped_entries = [ entry for entry in cache_table if entry.state == wpan.ADDRESS_CACHE_ENTRY_STATE_SNOOPED ] verify(len(snooped_entries) == MAX_SNOOPED_NON_EVICTABLE) for entry in snooped_entries: verify(entry.rloc16 == self.c2_rloc) verify(entry.state == wpan.ADDRESS_CACHE_ENTRY_STATE_SNOOPED) verify(entry.can_evict() == False) verify(entry.timeout > 0) # Now send from r1 to c2, some of the snooped entries would be used, others would go through full address query. for num in range(NUM_ADDRESSES): self.transmit_receive_udp_message(self.r1, self.c2, self.r1_address, PREFIX + "c2:" + str(num), PORT) cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) verify(len(cache_table) == MAX_CACHE_ENTRIES) # Verify that c2 entries are now at the top of cache list. for index in range(NUM_ADDRESSES): verify(cache_table[index].address == PREFIX + "c2:" + str(NUM_ADDRESSES - index - 1)) verify(cache_table[index].rloc16 == self.c2_rloc) verify(cache_table[index].state == wpan.ADDRESS_CACHE_ENTRY_STATE_CACHED)
def test06_verify_snoop_optimization(self): for num in range(NUM_ADDRESSES): self.transmit_receive_udp_message(self.r2, self.r1, PREFIX + "2:" + str(num), self.r1_address, PORT) cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) # We expect to see new "snooped" entries at the top of list. # Also verify that only MAX_SNOOPED_NON_EVICTABLE of snooped entries are non-evictable. verify(len(cache_table) >= NUM_ADDRESSES) for index in range(NUM_ADDRESSES): verify(cache_table[index].address == PREFIX + "2:" + str(NUM_ADDRESSES - index - 1)) verify(cache_table[index].rloc16 == self.r2_rloc) verify(cache_table[index].state == wpan.ADDRESS_CACHE_ENTRY_STATE_SNOOPED) if index < NUM_ADDRESSES - MAX_SNOOPED_NON_EVICTABLE: verify(cache_table[index].can_evict() == True) verify(cache_table[index].timeout == 0) else: print(index) verify(cache_table[index].can_evict() == False) verify(cache_table[index].timeout > 0) # From r1 send to r2 using the addresses from snooped entries: for num in range(NUM_ADDRESSES): self.transmit_receive_udp_message(self.r1, self.r2, self.r1_address, PREFIX + "2:" + str(num), PORT) cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) # We expect to see the entries to be in "cached" state now. verify(len(cache_table) >= NUM_ADDRESSES) for index in range(NUM_ADDRESSES): verify(cache_table[index].address == PREFIX + "2:" + str(NUM_ADDRESSES - index - 1)) verify(cache_table[index].rloc16 == self.r2_rloc) verify(cache_table[index].state == wpan.ADDRESS_CACHE_ENTRY_STATE_CACHED)
def test09_verify_address_cache_table_on_r1_updates_sed2(self): # New network topology # # r1 ---- r2 ---- r3 # | | | # | | | # fed1 sed2 fed3 # Send a UDP message from sed2 to fed1. # This message will be forwarded by r1 to its FED child fed1. self.transmit_receive_udp_message([ (self.sed2, self.fed1, self.sed2_address, self.fed1_address) ]) time.sleep(ADDRESS_CACHE_TABLE_WAIT_TIME) # r1 upon receiving and forwarding the message from sed2 (through r2 now) should # update its address cache table for sed2 (address cache update through snooping). # # verify that the address cache table is updated correctly. addr_cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) addr_cache_table = wpan_table_parser.parse_address_cache_table_result( addr_cache_table) verify(len(addr_cache_table) == 2) for entry in addr_cache_table: if entry.address == self.sed2_address: # Entry for sed2's address should now point to r2 verify(entry.rloc16 == self.r2_rloc) elif entry.address == self.fed3_address: # Entry for fed3's address should still point to fed3 verify(entry.rloc16 == self.fed3_rloc) else: raise VerifyError("Unknown entry in the address cache table")
def test06_verify_address_cache_table_on_r1_removes_sed2(self): # New network topology # # r1 ---- r2 ---- r3 # | /\ # | / \ # fed1 sed2 fed3 # From r1 send again to sed2 (which is now a child of r3). # # Note that r1 still has r2 as the destination for sed2's address in its address # cache table. But since r2 is aware that sed2 is no longer its child, when it # receives the IPv6 message with sed2's address, r2 itself would do an address # query for the address and forward the IPv6 message. self.transmit_receive_udp_message([ (self.r1, self.sed2, self.r1_address, self.sed2_address) ]) time.sleep(ADDRESS_CACHE_TABLE_WAIT_TIME) # The address cache table on r1 should have sed2's address removed. addr_cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) addr_cache_table = wpan_table_parser.parse_address_cache_table_result( addr_cache_table) verify(len(addr_cache_table) == 1) for entry in addr_cache_table: if entry.address == self.fed3_address: # Entry for fed3 should still point towards fed3 verify(entry.rloc16 == self.fed3_rloc) else: raise VerifyError("Unknown entry in the address cache table")
def test04_verify_address_cache_table_on_r1(self): # Send a single UDP message from r1 to sed2 # Send a single UDP message from r1 to fed3 self.transmit_receive_udp_message([ (self.r1, self.sed2, self.r1_address, self.sed2_address), (self.r1, self.fed3, self.r1_address, self.fed3_address) ]) time.sleep(ADDRESS_CACHE_TABLE_WAIT_TIME) # The address cache table on r1 should contain two entries for sed2 and fed3 addresses. addr_cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) addr_cache_table = wpan_table_parser.parse_address_cache_table_result( addr_cache_table) verify(len(addr_cache_table) == 2) for entry in addr_cache_table: if entry.address == self.sed2_address: # Entry for sed2 should point towards its parent r2. verify(entry.rloc16 == self.r2_rloc) elif entry.address == self.fed3_address: # Entry for fed3 should point towards fed3 itself. verify(entry.rloc16 == self.fed3_rloc) else: raise VerifyError("Unknown entry in the address cache table")
def check_cache_entry_switch_to_query_state(): cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) for index in range(NUM_QUERY_ADDRS): verify(cache_table[index].state == wpan.ADDRESS_CACHE_ENTRY_STATE_QUERY) verify(cache_table[index].can_evict() == True)
def check_cache_entry_in_retry_state_to_get_to_zero_timeout(): cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) for index in range(NUM_QUERY_ADDRS): verify(cache_table[index].state == wpan.ADDRESS_CACHE_ENTRY_STATE_RETRY_QUERY) verify(cache_table[index].timeout == 0)
def check_cache_entry_switch_to_retry_state(): cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) for index in range(NUM_QUERY_ADDRS): self.logger.info( f"{cache_table[index].state}, {wpan.ADDRESS_CACHE_ENTRY_STATE_RETRY_QUERY}" ) verify(cache_table[index].state == wpan.ADDRESS_CACHE_ENTRY_STATE_RETRY_QUERY) verify( cache_table[index].retry_delay >= 2 * INITIAL_RETRY_DELAY)
def test04_verify_cache_table_entries_in_query_state(self): # From r1 send msg to a group of addresses (not provided by any nodes in network). for num in range(NUM_QUERY_ADDRS): self.r1.send_udp_data(PREFIX + "800:" + str(num), PORT, random_string(10), self.r1_address, timeout=0.05) # Wait before next tx to stagger the address queries request ensuring different timeouts time.sleep(MAX_STAGGER_INTERVAL / NUM_QUERY_ADDRS) # Verify that we do see entries in cache table for all the addresses and all are in "query" state addr_cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) addr_cache_table = wpan_table_parser.parse_address_cache_table_result( addr_cache_table) verify(len(addr_cache_table) == NUM_QUERY_ADDRS) for entry in addr_cache_table: verify(entry.state == wpan.ADDRESS_CACHE_ENTRY_STATE_QUERY) verify(not entry.can_evict()) verify(entry.timeout > 0) verify(entry.retry_delay >= INITIAL_RETRY_DELAY)
def test07_verify_address_cache_table_on_r1_adds_sed2(self): # Send a UDP message from r1 to sed2. self.transmit_receive_udp_message([ (self.r1, self.sed2, self.r1_address, self.sed2_address) ]) time.sleep(ADDRESS_CACHE_TABLE_WAIT_TIME) # The address cache table on r1 should have both fed1 and sed2. addr_cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) addr_cache_table = wpan_table_parser.parse_address_cache_table_result( addr_cache_table) verify(len(addr_cache_table) == 2) for entry in addr_cache_table: if entry.address == self.sed2_address: # Entry for sed2 should point towards r3 verify(entry.rloc16 == self.r3_rloc) elif entry.address == self.fed3_address: # Entry for fed3 should still point towards fed3 verify(entry.rloc16 == self.fed3_rloc) else: raise VerifyError("Unknown entry in the address cache table")
def test07_verify_query_requests_last_transaction_time(self): # Check query requests, last transaction time # Send from r1 to all addresses on r3. for num in range(NUM_ADDRESSES): self.transmit_receive_udp_message(self.r1, self.r3, self.r1_address, PREFIX + "3:" + str(num), PORT) cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) # We expect to see the cache entries for the addresses pointing to r3. cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) for index in range(NUM_ADDRESSES): verify(cache_table[index].address == PREFIX + "3:" + str(NUM_ADDRESSES - index - 1)) verify(cache_table[index].rloc16 == self.r3_rloc) verify(cache_table[index].state == wpan.ADDRESS_CACHE_ENTRY_STATE_CACHED) verify(cache_table[index].last_trans == 0) # Send from r1 to all addresses on c3 (sleepy child of r3) for num in range(NUM_ADDRESSES): self.transmit_receive_udp_message(self.r1, self.c3, self.r1_address, PREFIX + "c3:" + str(num), PORT) # We expect to see the cache entries for c3 addresses pointing to r3. cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) for index in range(NUM_ADDRESSES): verify(cache_table[index].address == PREFIX + "c3:" + str(NUM_ADDRESSES - index - 1)) verify(cache_table[index].rloc16 == self.r3_rloc) verify(cache_table[index].state == wpan.ADDRESS_CACHE_ENTRY_STATE_CACHED) # SED's keep-alive period (`POLL_PERIOD`) is 200ms, `last_trans` should always be 0 as it is # the number of seconds since a keep-alive was last received from the child. verify(cache_table[index].last_trans == 0) # Send again to r2. This should cause the related cache entries to be moved to top of the list: for num in range(NUM_ADDRESSES): self.transmit_receive_udp_message(self.r1, self.r2, self.r1_address, PREFIX + "2:" + str(num), PORT) cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) for index in range(NUM_ADDRESSES): verify(cache_table[index].address == PREFIX + "2:" + str(NUM_ADDRESSES - index - 1)) verify(cache_table[index].rloc16 == self.r2_rloc) verify(cache_table[index].state == wpan.ADDRESS_CACHE_ENTRY_STATE_CACHED) verify(len(cache_table) == MAX_CACHE_ENTRIES)
def test05_verify_retry_query_behavior(self): # Wait till all the address queries time out and verify they enter "retry-query" state. def check_cache_entry_switch_to_retry_state(): cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) for index in range(NUM_QUERY_ADDRS): self.logger.info( f"{cache_table[index].state}, {wpan.ADDRESS_CACHE_ENTRY_STATE_RETRY_QUERY}" ) verify(cache_table[index].state == wpan.ADDRESS_CACHE_ENTRY_STATE_RETRY_QUERY) verify( cache_table[index].retry_delay >= 2 * INITIAL_RETRY_DELAY) verify_within(check_cache_entry_switch_to_retry_state, WAIT_TIME) # Try sending again to same addresses which are in "retry-delay" state. for num in range(NUM_QUERY_ADDRS): self.r1.send_udp_data(PREFIX + "800:" + str(num), PORT, random_string(10), self.r1_address, timeout=0.05) # Make sure the entries stayed in retry-delay as before. verify_within(check_cache_entry_switch_to_retry_state, WAIT_TIME) # Now wait for them to get to zero timeout. cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) def check_cache_entry_in_retry_state_to_get_to_zero_timeout(): cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) for index in range(NUM_QUERY_ADDRS): verify(cache_table[index].state == wpan.ADDRESS_CACHE_ENTRY_STATE_RETRY_QUERY) verify(cache_table[index].timeout == 0) verify_within(check_cache_entry_in_retry_state_to_get_to_zero_timeout, WAIT_TIME) # Now try again using the same addresses. for num in range(NUM_QUERY_ADDRS): self.r1.send_udp_data(PREFIX + "800:" + str(num), PORT, random_string(10), self.r1_address, timeout=0.05) # We expect now after the delay to see retries for same addresses. def check_cache_entry_switch_to_query_state(): cache_table = self.r1.wpanctl( "get", "get " + wpan.WPAN_THREAD_ADDRESS_CACHE_TABLE, 2) cache_table = wpan_table_parser.parse_address_cache_table_result( cache_table) for index in range(NUM_QUERY_ADDRS): verify(cache_table[index].state == wpan.ADDRESS_CACHE_ENTRY_STATE_QUERY) verify(cache_table[index].can_evict() == True) verify_within(check_cache_entry_switch_to_query_state, WAIT_TIME)