blob: c538ec52ec18f8fa3c7ad26663fcef56f9db1fcc [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright (c) 2019, 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 thread_cert
import config
LEADER_1_1 = 1
BBR_1 = 2
BBR_2 = 3
WAIT_ATTACH = 5
WAIT_REDUNDANCE = 3
ROUTER_SELECTION_JITTER = 1
BBR_REGISTRATION_JITTER = 5
"""
Topology
LEADER_1_1 --- BBR_1
\ |
\ |
\ |
BBR_2
1) Bring up Leader_1_1 and then BBR_1, BBR_1 becomes Primary Backbone Router.
2) Reset BBR_1, if bring back soon, it could restore the Backbone Router Service
from the network, after increasing sequence number, it will reregister its
Backbone Router Service to the Leader and become Primary.
3) Reset BBR_1, if bring back after it is released in the network, BBR_1 will
choose a random sequence number, register its Backbone Router Service to
Leader and become Primary.
4) Configure BBR_2 with highest sequence number and explicitly trigger SRV_DATA.ntf.
BBR_2 would become Primary and BBR_1 would change to Secondary with sequence
number increased by 1.
a) Check communication via DUA.
5) Stop BBR_2, BBR_1 would become Primary after detecting there is no available
Backbone Router Service in Thread Network.
6) Bring back BBR_2, and it would become Secondary.
a) Check the uniqueness of DUA by comparing the one in above 4a).
b) Check communication via DUA.
"""
class TestBackboneRouterService(thread_cert.TestCase):
TOPOLOGY = {
LEADER_1_1: {
'version': '1.1',
'allowlist': [BBR_1, BBR_2],
},
BBR_1: {
'version': '1.2',
'allowlist': [LEADER_1_1, BBR_2],
'is_bbr': True
},
BBR_2: {
'version': '1.2',
'allowlist': [LEADER_1_1, BBR_1],
'is_bbr': True
},
}
"""All nodes are created with default configurations"""
def test(self):
self.nodes[LEADER_1_1].start()
WAIT_TIME = WAIT_ATTACH
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[LEADER_1_1].get_state(), 'leader')
self.simulator.set_lowpan_context(1, config.DOMAIN_PREFIX)
# 1) First Backbone Router would become the Primary.
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=1)
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(), 'router')
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')
self.nodes[BBR_1].set_domain_prefix(config.DOMAIN_PREFIX)
WAIT_TIME = WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
# 2) Reset BBR_1 and bring it back soon.
# Verify that it restores Primary State with sequence number
# increased by 1.
self.nodes[BBR_1].reset()
self.nodes[BBR_1].set_bbr_registration_jitter(BBR_REGISTRATION_JITTER)
self.nodes[BBR_1].set_router_selection_jitter(ROUTER_SELECTION_JITTER)
self.nodes[BBR_1].set_domain_prefix(config.DOMAIN_PREFIX)
self.nodes[BBR_1].enable_backbone_router()
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(), '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].get_backbone_router()['seqno'] == 2
# 3) Reset BBR_1 and bring it back after its original router id is released
# 200s (100s MaxNeighborAge + 90s InfiniteCost + 10s redundance)
# Verify it becomes Primary again.
# Note: To ensure test in next step, here Step 3) will repeat until
# the random sequence number is not the highest value 255.
while True:
self.nodes[BBR_1].reset()
WAIT_TIME = 200
self.simulator.go(WAIT_TIME)
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_domain_prefix(config.DOMAIN_PREFIX)
self.nodes[BBR_1].enable_backbone_router()
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(), 'router')
WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(), 'Primary')
BBR_1_SEQNO = self.nodes[BBR_1].get_backbone_router()['seqno']
if (BBR_1_SEQNO != 255):
break
#4) Configure BBR_2 with highest sequence number (255) and
# explicitly trigger SRV_DATA.ntf.
# Verify BBR_2 would become Primary and BBR_1 would change to
# Secondary with sequence number increased by 1.
# Bring up BBR_2, it becomes Router with backbone function disabled
# by default.
self.nodes[BBR_2].set_router_selection_jitter(ROUTER_SELECTION_JITTER)
self.nodes[BBR_2].set_bbr_registration_jitter(BBR_REGISTRATION_JITTER)
self.nodes[BBR_2].set_domain_prefix(config.DOMAIN_PREFIX)
self.nodes[BBR_2].start()
WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_2].get_state(), 'router')
WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_2].get_backbone_router_state(), 'Disabled')
# Enable Backbone function, it will stay at Secondary state as
# there is Primary Backbone Router already.
# Here removes the Domain Prefix before enabling backbone function
# intentionally to avoid SRV_DATA.ntf due to prefix inconsistency.
self.nodes[BBR_2].remove_domain_prefix(config.DOMAIN_PREFIX)
self.nodes[BBR_2].enable_backbone_router()
self.nodes[BBR_2].set_backbone_router(seqno=255)
WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_2].get_backbone_router_state(), 'Secondary')
# Check no SRV_DATA.ntf.
messages = self.simulator.get_messages_sent_by(BBR_2)
msg = messages.next_coap_message('0.02', '/a/sd', False)
assert (msg is None), "Error: %d sent unexpected SRV_DATA.ntf when there is PBbr already"
# Flush relative message queue.
self.flush_nodes([BBR_1])
# BBR_2 registers SRV_DATA.ntf explicitly.
self.nodes[BBR_2].register_backbone_router()
WAIT_TIME = WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_2].get_backbone_router_state(), 'Primary')
# Verify BBR_1 becomes Secondary and sends SRV_DATA.ntf to deregister
# its service.
messages = self.simulator.get_messages_sent_by(BBR_1)
messages.next_coap_message('0.02', '/a/sd', True)
self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(), 'Secondary')
# Verify Sequence number increases when become Secondary from Primary.
assert self.nodes[BBR_1].get_backbone_router()['seqno'] == (BBR_1_SEQNO + 1)
# 4a) Check communication via DUA.
bbr2_dua = self.nodes[BBR_2].get_addr(config.DOMAIN_PREFIX)
self.assertTrue(self.nodes[BBR_1].ping(bbr2_dua))
# 5) Stop BBR_2, BBR_1 becomes Primary after detecting there is no
# available Backbone Router Service.
self.nodes[BBR_2].reset()
self.nodes[LEADER_1_1].release_router_id(self.nodes[BBR_2].get_router_id())
# Wait for the dissemination of Network Data without Backbone Router service
self.simulator.go(10)
# BBR_1 becomes Primary.
self.assertEqual(self.nodes[BBR_1].get_backbone_router_state(), 'Primary')
messages = self.simulator.get_messages_sent_by(BBR_1)
messages.next_coap_message('0.02', '/a/sd', True)
# 6) Bring back BBR_2.
# Verify that BBR_2 stays at Secondary.
self.nodes[BBR_2].set_router_selection_jitter(ROUTER_SELECTION_JITTER)
self.nodes[BBR_2].set_bbr_registration_jitter(BBR_REGISTRATION_JITTER)
self.nodes[BBR_1].set_domain_prefix(config.DOMAIN_PREFIX)
self.nodes[BBR_2].enable_backbone_router()
self.nodes[BBR_2].interface_up()
self.nodes[BBR_2].thread_start()
WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_2].get_state(), 'router')
WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE
self.simulator.go(WAIT_TIME)
self.assertEqual(self.nodes[BBR_2].get_backbone_router_state(), 'Secondary')
# 6a) Check the uniqueness of DUA by comparing the one in above 4a).
bbr2_dua2 = self.nodes[BBR_2].get_addr(config.DOMAIN_PREFIX)
assert bbr2_dua == bbr2_dua2, 'Error: Unexpected different DUA ({} v.s. {})'.format(bbr2_dua, bbr2_dua2)
# 6b) Check communication via DUA
self.assertTrue(self.nodes[BBR_1].ping(bbr2_dua))
if __name__ == '__main__':
unittest.main()