mirror of
https://github.com/espressif/openthread.git
synced 2026-08-20 17:39:51 +00:00
[dua] handle AddressError (#5516)
This commit enhances DUA feature to handle Address Error by increasing the DAD counter and generating new DUA address if a DUA conflict is detected.
This commit is contained in:
@@ -704,7 +704,16 @@ void AddressResolver::HandleAddressError(Coap::Message &aMessage, const Ip6::Mes
|
||||
if (address->GetAddress() == target && Get<Mle::MleRouter>().GetMeshLocal64().GetIid() != meshLocalIid)
|
||||
{
|
||||
// Target EID matches address and Mesh Local EID differs
|
||||
Get<ThreadNetif>().RemoveUnicastAddress(*address);
|
||||
#if OPENTHREAD_CONFIG_DUA_ENABLE
|
||||
if (Get<BackboneRouter::Leader>().IsDomainUnicast(address->GetAddress()))
|
||||
{
|
||||
Get<DuaManager>().NotifyDuplicateDomainUnicastAddress();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
Get<ThreadNetif>().RemoveUnicastAddress(*address);
|
||||
}
|
||||
ExitNow();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +258,18 @@ void DuaManager::UpdateRegistrationDelay(uint8_t aDelay)
|
||||
UpdateTimeTickerRegistration();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void DuaManager::NotifyDuplicateDomainUnicastAddress(void)
|
||||
{
|
||||
RemoveDomainUnicastAddress();
|
||||
mDadCounter++;
|
||||
|
||||
if (GenerateDomainUnicastAddressIid() == OT_ERROR_NONE)
|
||||
{
|
||||
AddDomainUnicastAddress();
|
||||
}
|
||||
}
|
||||
#endif // OPENTHREAD_CONFIG_DUA_ENABLE
|
||||
|
||||
void DuaManager::UpdateReregistrationDelay(void)
|
||||
{
|
||||
@@ -584,14 +595,7 @@ otError DuaManager::ProcessDuaResponse(Coap::Message &aMessage)
|
||||
RemoveDomainUnicastAddress();
|
||||
break;
|
||||
case ThreadStatusTlv::kDuaDuplicate:
|
||||
RemoveDomainUnicastAddress();
|
||||
mDadCounter++;
|
||||
|
||||
if (GenerateDomainUnicastAddressIid() == OT_ERROR_NONE)
|
||||
{
|
||||
AddDomainUnicastAddress();
|
||||
}
|
||||
|
||||
NotifyDuplicateDomainUnicastAddress();
|
||||
break;
|
||||
case ThreadStatusTlv::kDuaNoResources:
|
||||
case ThreadStatusTlv::kDuaNotPrimary:
|
||||
|
||||
@@ -153,6 +153,12 @@ public:
|
||||
*
|
||||
*/
|
||||
void Restore(void);
|
||||
|
||||
/**
|
||||
* This method notifies duplicated Domain Unicast Address.
|
||||
*
|
||||
*/
|
||||
void NotifyDuplicateDomainUnicastAddress(void);
|
||||
#endif
|
||||
|
||||
#if OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE
|
||||
|
||||
@@ -127,6 +127,7 @@ AQ_TIMEOUT = 3
|
||||
ADDRESS_QUERY_INITIAL_RETRY_DELAY = 15
|
||||
DEFAULT_CHILD_TIMEOUT = 6
|
||||
VIRTUAL_TIME = int(os.getenv('VIRTUAL_TIME', 0))
|
||||
PARENT_AGGREGATIOIN_DELAY = 5
|
||||
|
||||
LEADER_NOTIFY_SED_BY_CHILD_UPDATE_REQUEST = True
|
||||
|
||||
|
||||
@@ -959,6 +959,16 @@ class NodeImpl:
|
||||
self.send_command('routerdowngradethreshold')
|
||||
return int(self._expect_result(r'\d+'))
|
||||
|
||||
def set_router_eligible(self, enable: bool):
|
||||
cmd = f'routereligible {"enable" if enable else "disable"}'
|
||||
self.send_command(cmd)
|
||||
self._expect('Done')
|
||||
|
||||
def get_router_eligible(self) -> bool:
|
||||
states = [r'Disabled', r'Enabled']
|
||||
self.send_command('routereligible')
|
||||
return self._expect_result(states) == 'Enabled'
|
||||
|
||||
def prefer_router_id(self, router_id):
|
||||
cmd = 'preferrouterid %d' % router_id
|
||||
self.send_command(cmd)
|
||||
@@ -1007,6 +1017,11 @@ class NodeImpl:
|
||||
self.send_command(cmd)
|
||||
self._expect('Done')
|
||||
|
||||
def del_ipaddr(self, ipaddr):
|
||||
cmd = 'ipaddr del %s' % ipaddr
|
||||
self.send_command(cmd)
|
||||
self._expect('Done')
|
||||
|
||||
def add_ipmaddr(self, ipmaddr):
|
||||
cmd = 'ipmaddr add %s' % ipmaddr
|
||||
self.send_command(cmd)
|
||||
|
||||
@@ -174,6 +174,8 @@ class TestCase(NcpSupportMixin, unittest.TestCase):
|
||||
self.nodes[i].set_router_upgrade_threshold(params['router_upgrade_threshold'])
|
||||
if 'router_downgrade_threshold' in params:
|
||||
self.nodes[i].set_router_downgrade_threshold(params['router_downgrade_threshold'])
|
||||
if 'router_eligible' in params:
|
||||
self.nodes[i].set_router_eligible(params['router_eligible'])
|
||||
|
||||
if 'timeout' in params:
|
||||
self.nodes[i].set_timeout(params['timeout'])
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
#!/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.
|
||||
#
|
||||
# This script tests how node would handle duplicated Domain Unicast Address.
|
||||
#
|
||||
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import thread_cert
|
||||
|
||||
BBR_1 = 1 # Collapsed with Leader Role
|
||||
ROUTER = 2
|
||||
FED = 3
|
||||
MED = 4
|
||||
|
||||
WAIT_ATTACH = 5
|
||||
WAIT_REDUNDANCE = 3
|
||||
ROUTER_SELECTION_JITTER = 1
|
||||
BBR_REGISTRATION_JITTER = 5
|
||||
SED_POLL_PERIOD = 2000 # 2s
|
||||
MED_TIMEOUT = 20 # 20s
|
||||
"""
|
||||
Topology
|
||||
|
||||
BBR_1 (Leader)
|
||||
|
|
||||
ROUTER_1---FED
|
||||
|
|
||||
MED
|
||||
"""
|
||||
|
||||
WAIT_REDUNDANCE = 3
|
||||
REG_DELAY = 5
|
||||
|
||||
|
||||
class TestDomainUnicastAddress(thread_cert.TestCase):
|
||||
TOPOLOGY = {
|
||||
BBR_1: {
|
||||
'version': '1.2',
|
||||
'allowlist': [ROUTER],
|
||||
'router_selection_jitter': ROUTER_SELECTION_JITTER,
|
||||
'is_bbr': True
|
||||
},
|
||||
ROUTER: {
|
||||
'version': '1.2',
|
||||
'allowlist': [BBR_1, FED, MED],
|
||||
'router_selection_jitter': ROUTER_SELECTION_JITTER,
|
||||
},
|
||||
FED: {
|
||||
'version': '1.2',
|
||||
'allowlist': [ROUTER],
|
||||
'router_eligible': False,
|
||||
'mode': 'rsdn',
|
||||
},
|
||||
MED: {
|
||||
'version': '1.2',
|
||||
'allowlist': [ROUTER],
|
||||
'mode': 'rsn',
|
||||
},
|
||||
}
|
||||
|
||||
def test(self):
|
||||
self.simulator.set_lowpan_context(1, config.DOMAIN_PREFIX)
|
||||
|
||||
# 1) Bring up BBR_1, BBR_1 becomes Leader and Primary Backbone Router, with Domain
|
||||
# Prefix without `P_slaac`.
|
||||
self.nodes[BBR_1].set_bbr_registration_jitter(BBR_REGISTRATION_JITTER)
|
||||
self.nodes[BBR_1].set_backbone_router(seqno=1, reg_delay=REG_DELAY)
|
||||
self.nodes[BBR_1].start()
|
||||
|
||||
self.simulator.go(WAIT_ATTACH + ROUTER_SELECTION_JITTER)
|
||||
self.assertEqual(self.nodes[BBR_1].get_state(), 'leader')
|
||||
self.nodes[BBR_1].enable_backbone_router()
|
||||
self.simulator.go(BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE)
|
||||
self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(), 'Primary')
|
||||
|
||||
self.nodes[BBR_1].set_domain_prefix(config.DOMAIN_PREFIX, 'prosD')
|
||||
self.simulator.go(WAIT_REDUNDANCE)
|
||||
assert self.nodes[BBR_1].has_ipmaddr(config.ALL_DOMAIN_BBRS_ADDRESS)
|
||||
|
||||
# 2) Bring up ROUTER_1
|
||||
self.nodes[ROUTER].start()
|
||||
self.simulator.go(WAIT_ATTACH + REG_DELAY + WAIT_REDUNDANCE)
|
||||
self.assertEqual(self.nodes[ROUTER].get_state(), 'router')
|
||||
|
||||
# Bring up FED
|
||||
self.nodes[FED].start()
|
||||
self.simulator.go(WAIT_ATTACH + REG_DELAY + WAIT_REDUNDANCE)
|
||||
self.assertEqual(self.nodes[FED].get_state(), 'child')
|
||||
|
||||
# Bring up MED
|
||||
self.nodes[MED].start()
|
||||
self.simulator.go(WAIT_ATTACH + config.PARENT_AGGREGATIOIN_DELAY + REG_DELAY + WAIT_REDUNDANCE)
|
||||
self.assertEqual(self.nodes[MED].get_state(), 'child')
|
||||
|
||||
self._verify_dua_handle_address_error(ROUTER)
|
||||
self._verify_dua_handle_address_error(FED)
|
||||
self._verify_dua_handle_address_error(MED, is_med=True)
|
||||
|
||||
def _verify_dua_handle_address_error(self, nodeid, is_med=False):
|
||||
dua = self.nodes[nodeid].get_addr(config.DOMAIN_PREFIX)
|
||||
self.assertIsNotNone(dua)
|
||||
|
||||
# Ping the DUA to verify reachability, and also fill the EID cache on BBR_1
|
||||
self.assertTrue(self.nodes[BBR_1].ping(dua))
|
||||
|
||||
self.simulator.go(WAIT_REDUNDANCE)
|
||||
|
||||
# Send fake /a/an from ROUTER to BBR_1 for the node's DUA
|
||||
pbbr_rloc = self.nodes[BBR_1].get_ip6_address(config.ADDRESS_TYPE.RLOC)
|
||||
self.nodes[ROUTER].send_address_notification(pbbr_rloc, dua, f'000000000000{nodeid:04x}')
|
||||
|
||||
self.simulator.go(config.PARENT_AGGREGATIOIN_DELAY * is_med + REG_DELAY + WAIT_REDUNDANCE + 50)
|
||||
|
||||
# Make sure device handles /a/ae correctly by generating new DUA
|
||||
new_dua = self.nodes[nodeid].get_addr(config.DOMAIN_PREFIX)
|
||||
self.assertNotEqual(dua, new_dua)
|
||||
self.assertTrue(self.nodes[BBR_1].ping(new_dua))
|
||||
self.assertFalse(self.nodes[BBR_1].ping(dua))
|
||||
|
||||
self.simulator.go(3)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user