[dua] dua registration and re-registration (#5276)

- registration workflow framework
- registration and reregistration
- registration and reregistration on behalf of children
- add pseudo response status for test/certification purpose
- add unit test
This commit is contained in:
Rongli Sun
2020-07-30 14:10:22 -07:00
committed by GitHub
parent 8c094c6ac2
commit 82e7e32bb5
27 changed files with 1481 additions and 55 deletions
+2
View File
@@ -491,6 +491,8 @@ def create_default_uri_path_based_payload_factories():
'/d/dq': network_diag_tlvs_factory,
'/d/dr': network_diag_tlvs_factory,
'/n/mr': network_layer_tlvs_factory,
'/n/dr': network_layer_tlvs_factory,
'/n/dn': network_layer_tlvs_factory,
}
+12
View File
@@ -522,6 +522,13 @@ class Node:
self.remove_prefix(prefix)
self.register_netdata()
def set_next_dua_response(self, status, iid=None):
cmd = 'bbr mgmt dua {}'.format(status)
if iid is not None:
cmd += ' ' + str(iid)
self.send_command(cmd)
self._expect('Done')
def set_dua_iid(self, iid):
cmd = 'dua iid {}'.format(iid)
self.send_command(cmd)
@@ -1481,6 +1488,11 @@ class Node:
def udp_check_rx(self, bytes_should_rx):
self._expect('%d bytes' % bytes_should_rx)
def set_routereligible(self, enable: bool):
cmd = f'routereligible {"enable" if enable else "disable"}'
self.send_command(cmd)
self._expect('Done')
def router_list(self):
cmd = 'router list'
self.send_command(cmd)
@@ -136,7 +136,7 @@ class TestDomainUnicastAddress(thread_cert.TestCase):
return ''.join(ipaddress.ip_address(address).exploded.split(':')[4:])
def __check_dua_registration(self, node, iid, dp_cid):
''' Check whether or not the specified domain unicast address is registered in Address
''' Check whether or not the specified Domain Unicast Address is registered in Address
Registraion TLV.
Args:
@@ -311,6 +311,7 @@ class TestDomainUnicastAddress(thread_cert.TestCase):
self.simulator.go(WAIT_TIME)
WAIT_TIME = MED_TIMEOUT
self.simulator.go(WAIT_TIME)
dua = self.nodes[MED_1_2].get_addr(config.DOMAIN_PREFIX)
assert dua, 'Error: Expected DUA not found'
assert ipaddress.ip_address(med_1_2_dua) == ipaddress.ip_address(
@@ -0,0 +1,379 @@
#!/usr/bin/env python3
#
# Copyright (c) 2020, 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 unittest
import command
import config
import ipaddress
import mle
import thread_cert
BBR_1 = 1 # Collapsed with Leader Role
ROUTER_1_1 = 2
ROUTER_1_2 = 3
FED_1_2_1 = 4
MED_1_2_1 = 5
SED_1_2_1 = 6
FED_1_2_2 = 7
MED_1_2_2 = 8
SED_1_2_2 = 9
WAIT_ATTACH = 5
WAIT_REDUNDANCE = 3
ROUTER_SELECTION_JITTER = 1
BBR_REGISTRATION_JITTER = 5
SED_POLL_PERIOD = 2000 # 2s
MED_TIMEOUT = 20 # 20s
PARENT_AGGREGATE_DELAY = 5 # 5s
DUA_IID_MANUAL1 = '4444333322221111'
ST_DUA_SUCCESS = 0
ST_DUA_REREGISTER = 1
ST_DUA_INVALID = 2
ST_DUA_DUPLICATE = 3
ST_DUA_NO_RESOURCES = 4
ST_DUA_BBR_NOT_PRIMARY = 5
ST_DUA_GENERAL_FAILURE = 6
BBR_REREGISTRATION_DELAY = 10
"""
Topology
MED_1_2_1 SED_1_2_1
\ |
\ |
FED_1_2_1 --- ROUTER_1_1 FED_1_2_2 MED_1_2_2
| | /
| | /
BBR_1 (LEADER) --- ROUTER_1_2 --- SED_1_2_2
1) Bring up BBR_1, BBR_1 becomes Leader and Primary Backbone Router, with Domain
Prefix without `P_slaac`.
2) Test behaviors of ROUTER_1_2 under various response status:
a) Bring up ROUTER_1_2 with DUA_IID_MANUAL1, one DUA.req should happen to register DUA.
b) Remove DUA_IID_MANUAL1, one DUA.req should happen for the new generated DUA via SLAAC.
c) Configure BBR_1 to respond with the fatal error ST_DUA_INVALID, update BBR_1 with
BBR_REREGISTRATION_DELAY, ROUTER_1_2 should re-register its DUA within BBR_REREGISTRATION_DELAY.
- ROUTER_1_2 should remove its dua
- update network data, ROUTER_1_2 would regenerate and register the same dua
d) Configure BBR_1 to respond with the fatal error ST_DUA_DUPLICATE, update seqno to trigger reregistration.
After received DUA.rsp with ST_DUA_DUPLICATE, ROUTER_1_2 should
- increase dad counter
- regenerate a new DUA
- send DUA.req
e) (repeated) Configure BBR_1 to respond with per remaining error status:
- increase BBR seqno to trigger reregistration
- ROUTER_1_2 should re-register within BBR_REREGISTRATION_DELAY. For the not fatal errors, ROUTER_1_2
should re-register within another BBR_REREGISTRATION_DELAY (with least delay if ST_DUA_REREGISTER)
3) Bring up FED_1_2_1, MED_1_2_1, SED_1_2_1, they should send DUA.req by themselves as the parent
is of Thread 1.1 version.
4) Bring up FED_1_2_2, it sends DUA.req itself as it it FTD.
5) MED_1_2_2, SED_1_2_2, MTDs should should register their DUA to their parent
by Child Update Request, and the parent would send DUA.req for them on behalf.
6) Increase seqno on BBR_1, within BBR_REREGISTRATION_DELAY, there should be one DUA.req from
per [FED_1_2_1, MED_1_2_1, SED_1_2_1, FED_1_2_2], and 3 DUA.req from ROUTER_1_2 among which
2 DUA.req are for its MTD children.
"""
class TestDomainUnicastAddressRegistration(thread_cert.TestCase):
TOPOLOGY = {
BBR_1: {
'version': '1.2',
'whitelist': [ROUTER_1_1, ROUTER_1_2],
'is_bbr': True
},
ROUTER_1_1: {
'version': '1.1',
'whitelist': [BBR_1, FED_1_2_1, MED_1_2_1, SED_1_2_1]
},
ROUTER_1_2: {
'version': '1.2',
'whitelist': [BBR_1, FED_1_2_2, MED_1_2_2, SED_1_2_2]
},
FED_1_2_1: {
'version': '1.2',
'whitelist': [ROUTER_1_1],
},
MED_1_2_1: {
'mode': 'rsn',
'version': '1.2',
'whitelist': [ROUTER_1_1],
},
SED_1_2_1: {
'mode': 'sn',
'version': '1.2',
'whitelist': [ROUTER_1_1],
},
FED_1_2_2: {
'version': '1.2',
'whitelist': [ROUTER_1_2],
},
MED_1_2_2: {
'mode': 'rsn',
'version': '1.2',
'whitelist': [ROUTER_1_2],
},
SED_1_2_2: {
'mode': 'sn',
'version': '1.2',
'whitelist': [ROUTER_1_2],
},
}
"""All nodes are created with default configurations"""
def __get_iid(self, address):
''' Get the interface identifier of an IPv6 address.
Args:
address (string): An IPv6 address;
'''
return ''.join(ipaddress.ip_address(address).exploded.split(':')[4:])
def __check_dua_registration_tmf(self, node, occurences=1):
messages = self.simulator.get_messages_sent_by(node)
for i in range(occurences):
msg = messages.next_coap_message('0.02', '/n/dr', False)
assert msg, 'Expected {}, but {}th not found\n node: {}(extaddr: {})'.format(
occurences, i + 1, node, self.nodes[node].get_addr64())
def test(self):
# starting context id
context_id = 1
seq_num = 1
# 1) Bring up BBR_1, BBR_1 becomes Leader and Primary Backbone Router, with Domain
# Prefix without `P_slaac`.
self.nodes[BBR_1].set_router_selection_jitter(ROUTER_SELECTION_JITTER)
self.nodes[BBR_1].set_bbr_registration_jitter(BBR_REGISTRATION_JITTER)
self.nodes[BBR_1].set_backbone_router(
seqno=seq_num, reg_delay=BBR_REREGISTRATION_DELAY)
self.nodes[BBR_1].start()
WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_1].get_state(), 'leader')
self.nodes[BBR_1].enable_backbone_router()
WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(),
'Primary')
assert self.nodes[BBR_1].has_ipmaddr(config.ALL_NETWORK_BBRS_ADDRESS)
assert not self.nodes[BBR_1].has_ipmaddr(config.ALL_DOMAIN_BBRS_ADDRESS)
self.nodes[BBR_1].set_domain_prefix(config.DOMAIN_PREFIX, 'prosD')
WAIT_TIME = WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
assert self.nodes[BBR_1].has_ipmaddr(config.ALL_DOMAIN_BBRS_ADDRESS)
self.simulator.set_lowpan_context(context_id, config.DOMAIN_PREFIX)
domain_prefix_cid = context_id
# 2) Test behaviors of ROUTER_1_2 under various response status:
# a) Bring up ROUTER_1_2 with DUA_IID_MANUAL1, one DUA.req should happen to register DUA.
# Flush relative message queues.
self.flush_nodes([ROUTER_1_2])
self.nodes[ROUTER_1_2].set_dua_iid(DUA_IID_MANUAL1)
self.nodes[ROUTER_1_2].set_router_selection_jitter(
ROUTER_SELECTION_JITTER)
self.nodes[ROUTER_1_2].start()
WAIT_TIME = WAIT_ATTACH
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[ROUTER_1_2].get_state(), 'router')
mliid = self.__get_iid(self.nodes[ROUTER_1_2].get_mleid())
WAIT_TIME = WAIT_ATTACH + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
self.__check_dua_registration_tmf(ROUTER_1_2)
# b) Remove DUA_IID_MANUAL1, one DUA.req should happen for the new generated DUA via SLAAC.
# Flush relative message queues.
self.flush_nodes([ROUTER_1_2])
self.nodes[ROUTER_1_2].clear_dua_iid()
WAIT_TIME = WAIT_ATTACH + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
self.__check_dua_registration_tmf(ROUTER_1_2)
#c) Configure BBR_1 to respond with the fatal error ST_DUA_INVALID, update BBR_1 with
# BBR_REREGISTRATION_DELAY, ROUTER_1_2 should re-register its DUA within BBR_REREGISTRATION_DELAY.
# - ROUTER_1_2 should remove its dua
# - update network data, ROUTER_1_2 would regenerate and register the same dua
# Flush relative message queues.
self.flush_nodes([ROUTER_1_2])
seq_num = seq_num + 1
self.nodes[BBR_1].set_next_dua_response(ST_DUA_INVALID, mliid)
self.nodes[BBR_1].set_backbone_router(seqno=seq_num)
WAIT_TIME = BBR_REREGISTRATION_DELAY + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
self.__check_dua_registration_tmf(ROUTER_1_2, 1)
dua = self.nodes[ROUTER_1_2].get_addr(config.DOMAIN_PREFIX)
assert not dua, 'Error: Unexpected DUA ({}) found'.format(dua)
# Retry after new network data is available
seq_num = seq_num + 1
dua = self.nodes[ROUTER_1_2].get_addr(config.DOMAIN_PREFIX)
self.nodes[BBR_1].set_backbone_router(seqno=seq_num)
WAIT_TIME = BBR_REREGISTRATION_DELAY + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
self.__check_dua_registration_tmf(ROUTER_1_2, 1)
dua = self.nodes[ROUTER_1_2].get_addr(config.DOMAIN_PREFIX)
assert dua, 'Error: Expected DUA ({}) not found'.format(dua)
#d) Configure BBR_1 to respond with the fatal error ST_DUA_DUPLICATE, update seqno to trigger reregistration.
# After received DUA.rsp with ST_DUA_DUPLICATE, ROUTER_1_2 should
# - increase dad counter
# - regenerate a new DUA
# - send DUA.req
# Flush relative message queues.
self.flush_nodes([ROUTER_1_2])
seq_num = seq_num + 1
self.nodes[BBR_1].set_next_dua_response(ST_DUA_DUPLICATE, mliid)
self.nodes[BBR_1].set_backbone_router(seqno=seq_num)
WAIT_TIME = BBR_REREGISTRATION_DELAY * 2 + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
self.__check_dua_registration_tmf(ROUTER_1_2, 2)
dua2 = self.nodes[ROUTER_1_2].get_addr(config.DOMAIN_PREFIX)
assert dua2, 'Error: Expected DUA ({}) not found'.format(dua2)
assert dua2 != dua, 'Error: Expected Different DUA not found, same DUA {}'.format(
dua2)
# e) (repeated) Configure BBR_1 to respond with per remaining error status:
# - increase BBR seqno to trigger reregistration
# - ROUTER_1_2 should re-register within BBR_REREGISTRATION_DELAY. For the not fatal errors, ROUTER_1_2
# should re-register within another BBR_REREGISTRATION_DELAY (with least delay if ST_DUA_REREGISTER)
for status in [
ST_DUA_REREGISTER, ST_DUA_NO_RESOURCES, ST_DUA_BBR_NOT_PRIMARY,
ST_DUA_GENERAL_FAILURE
]:
print('Testing Status %d...'.format(status))
# Flush relative message queues.
self.flush_nodes([ROUTER_1_2])
seq_num = seq_num + 1
self.nodes[BBR_1].set_next_dua_response(status, mliid)
self.nodes[BBR_1].set_backbone_router(seqno=seq_num)
WAIT_TIME = BBR_REREGISTRATION_DELAY + WAIT_REDUNDANCE
if status != ST_DUA_REREGISTER:
WAIT_TIME += BBR_REREGISTRATION_DELAY
self.simulator.go(WAIT_TIME)
self.__check_dua_registration_tmf(ROUTER_1_2, 2)
# Bring up Router_1_1
self.nodes[ROUTER_1_1].set_router_selection_jitter(
ROUTER_SELECTION_JITTER)
self.nodes[ROUTER_1_1].start()
WAIT_TIME = WAIT_ATTACH
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[ROUTER_1_1].get_state(), 'router')
dua = self.nodes[ROUTER_1_1].get_addr(config.DOMAIN_PREFIX)
assert not dua, 'Error: Unexpected DUA ({}) found'.format(dua)
# Configure children
for node in [FED_1_2_1, FED_1_2_2]:
self.nodes[node].set_routereligible(False)
for node in [SED_1_2_1, SED_1_2_2]:
self.nodes[node].set_pollperiod(SED_POLL_PERIOD)
for node in [MED_1_2_1, MED_1_2_2]:
self.nodes[node].set_timeout(MED_TIMEOUT)
# 3) Bring up FED_1_2_1, MED_1_2_1, SED_1_2_1, they should send DUA.req by themselves as the parent
# is of Thread 1.1 version.
# 4) Bring up FED_1_2_2, it sends DUA.req itself as it it FTD.
for node in [FED_1_2_1, MED_1_2_1, SED_1_2_1, FED_1_2_2]:
print("Starting child {} (extaddr: {})...".format(
node, self.nodes[node].get_addr64()))
# Flush all message queues.
self.flush_all()
self.nodes[node].start()
WAIT_TIME = WAIT_ATTACH
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[node].get_state(), 'child')
WAIT_TIME = PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
self.__check_dua_registration_tmf(node)
# 5) MED_1_2_2, SED_1_2_2, MTDs should should register their DUA to their parent
# by Child Update Request, and the parent would send DUA.req for them on behalf.
for node in [MED_1_2_2, SED_1_2_2]:
print("Starting child {} (extaddr: {})...".format(
node, self.nodes[node].get_addr64()))
# Flush all message queues.
self.flush_all()
self.nodes[node].start()
WAIT_TIME = WAIT_ATTACH
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[node].get_state(), 'child')
WAIT_TIME = PARENT_AGGREGATE_DELAY + WAIT_REDUNDANCE
print("waiting {}".format(WAIT_TIME))
self.simulator.go(WAIT_TIME)
self.__check_dua_registration_tmf(ROUTER_1_2, 1)
# 6) Increase seqno on BBR_1, within BBR_REREGISTRATION_DELAY, there should be one DUA.req from
# per [FED_1_2_1, MED_1_2_1, SED_1_2_1, FED_1_2_2], and 3 DUA.req from ROUTER_1_2 among which
# 2 DUA.req are for its MTD children.
# Flush all message queues.
self.flush_all()
seq_num = seq_num + 1
self.nodes[BBR_1].set_backbone_router(seqno=seq_num)
WAIT_TIME = BBR_REREGISTRATION_DELAY + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
WAIT_TIME = BBR_REREGISTRATION_DELAY + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
for node in [FED_1_2_1, MED_1_2_1, SED_1_2_1, FED_1_2_2]:
self.__check_dua_registration_tmf(node)
self.__check_dua_registration_tmf(ROUTER_1_2, 3)
if __name__ == '__main__':
unittest.main()