mirror of
https://github.com/espressif/openthread.git
synced 2026-08-02 17:17:45 +00:00
[border-router] learn RA header and parameters from infra interface (#6431)
The commit enhances the Routing Manager to learn RA header and parameters from RA messages initiated from infra interface. This addresses the issue that the BR may be using different RA header against another RA daemon which is working on the same infra interface (Have BR and another RA daemon working on the same interface is common for Wi-Fi + Thread routers). We also changed to accept RS messages initiated from the infra interface: this is for case that multiple Border Routers run on the same host (as we are trying to support multiple ot-daemons on the same host).
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2021, 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 logging
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import pktverify
|
||||
from pktverify import packet_verifier, packet_filter, consts
|
||||
import thread_cert
|
||||
|
||||
# Test description:
|
||||
# This test verifies that the Border Router will use the same RA parameters
|
||||
# when there is already a running radvd service on the same host.
|
||||
#
|
||||
# Topology:
|
||||
# ----------------(eth)--------------------
|
||||
# | |
|
||||
# BR (Leader) HOST
|
||||
# |
|
||||
# ROUTER
|
||||
#
|
||||
|
||||
BR = 1
|
||||
ROUTER = 2
|
||||
HOST = 3
|
||||
|
||||
ON_LINK_PREFIX = 'fd00::/64'
|
||||
|
||||
|
||||
class SingleBorderRouter(thread_cert.TestCase):
|
||||
USE_MESSAGE_FACTORY = False
|
||||
|
||||
TOPOLOGY = {
|
||||
BR: {
|
||||
'name': 'BR',
|
||||
'allowlist': [ROUTER],
|
||||
'is_otbr': True,
|
||||
'version': '1.2',
|
||||
},
|
||||
ROUTER: {
|
||||
'name': 'Router',
|
||||
'allowlist': [BR],
|
||||
'version': '1.2',
|
||||
'router_selection_jitter': 1,
|
||||
},
|
||||
HOST: {
|
||||
'name': 'Host',
|
||||
'is_host': True
|
||||
},
|
||||
}
|
||||
|
||||
def test(self):
|
||||
br = self.nodes[BR]
|
||||
router = self.nodes[ROUTER]
|
||||
host = self.nodes[HOST]
|
||||
|
||||
host.start(start_radvd=False)
|
||||
self.simulator.go(5)
|
||||
|
||||
br.start_radvd_service(prefix=ON_LINK_PREFIX, slaac=True)
|
||||
self.simulator.go(5)
|
||||
|
||||
br.start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('leader', br.get_state())
|
||||
|
||||
router.start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('router', router.get_state())
|
||||
|
||||
self.simulator.go(10)
|
||||
self.collect_ipaddrs()
|
||||
|
||||
logging.info("BR addrs: %r", br.get_addrs())
|
||||
logging.info("ROUTER addrs: %r", router.get_addrs())
|
||||
logging.info("HOST addrs: %r", host.get_addrs())
|
||||
|
||||
self.assertEqual(len(br.get_prefixes()), 1)
|
||||
self.assertEqual(len(router.get_prefixes()), 1)
|
||||
self.assertEqual(len(br.get_routes()), 1)
|
||||
self.assertEqual(len(router.get_routes()), 1)
|
||||
|
||||
self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
|
||||
self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
|
||||
self.assertEqual(len(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)), 1)
|
||||
|
||||
self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
|
||||
self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True))
|
||||
|
||||
# Stop the radvd service and wait for the Border Router
|
||||
# to start adverting on-link prefix on its own.
|
||||
br.stop_radvd_service()
|
||||
self.simulator.go(15)
|
||||
|
||||
self.assertEqual(len(br.get_prefixes()), 1)
|
||||
self.assertEqual(len(router.get_prefixes()), 1)
|
||||
self.assertEqual(len(br.get_routes()), 1)
|
||||
self.assertEqual(len(router.get_routes()), 1)
|
||||
|
||||
self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
|
||||
self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1)
|
||||
self.assertEqual(len(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)), 1)
|
||||
|
||||
self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
|
||||
self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True))
|
||||
|
||||
self.collect_ipaddrs()
|
||||
self.collect_rloc16s()
|
||||
self.collect_rlocs()
|
||||
self.collect_extra_vars()
|
||||
|
||||
def verify(self, pv: pktverify.packet_verifier.PacketVerifier):
|
||||
RA_OPT_TYPE_PIO = 3
|
||||
RA_OPT_TYPE_RIO = 24
|
||||
|
||||
pkts = pv.pkts
|
||||
vars = pv.vars
|
||||
pv.summary.show()
|
||||
logging.info(f'vars = {vars}')
|
||||
|
||||
# Ensure the topology is formed correctly
|
||||
pv.verify_attached('Router', 'BR')
|
||||
|
||||
# verify that radvd sends RA messages with PIO.
|
||||
radvd_ra = pkts.filter_eth_src(vars['BR_ETH']) \
|
||||
.filter_icmpv6_nd_ra() \
|
||||
.filter(lambda p: RA_OPT_TYPE_PIO in p.icmpv6.opt.type) \
|
||||
.must_next()
|
||||
|
||||
# Verify that the BR sends RA messages with the same parameters.
|
||||
br_ra = pkts.filter_eth_src(vars['BR_ETH']) \
|
||||
.filter_icmpv6_nd_ra() \
|
||||
.filter(lambda p: RA_OPT_TYPE_RIO in p.icmpv6.opt.type) \
|
||||
.filter(lambda p: p.icmpv6.nd.ra.router_lifetime == radvd_ra.icmpv6.nd.ra.router_lifetime) \
|
||||
.filter(lambda p: p.icmpv6.nd.ra.retrans_timer == radvd_ra.icmpv6.nd.ra.retrans_timer) \
|
||||
.filter(lambda p: p.icmpv6.nd.ra.reachable_time == radvd_ra.icmpv6.nd.ra.reachable_time) \
|
||||
.must_next()
|
||||
|
||||
# Verify that radvd sends at lease one RA message with zero Router Lifetime
|
||||
# when it is stopped.
|
||||
pkts.filter_eth_src(vars['BR_ETH']) \
|
||||
.filter_icmpv6_nd_ra() \
|
||||
.filter(lambda p: RA_OPT_TYPE_PIO in p.icmpv6.opt.type) \
|
||||
.filter(lambda p: p.icmpv6.nd.ra.router_lifetime == 0) \
|
||||
.must_next()
|
||||
|
||||
# Verify that the BR forgets radvd's RA parameters.
|
||||
pkts.filter_eth_src(vars['BR_ETH']) \
|
||||
.filter_icmpv6_nd_ra() \
|
||||
.filter(lambda p: RA_OPT_TYPE_RIO in p.icmpv6.opt.type) \
|
||||
.filter(lambda p: p.icmpv6.nd.ra.router_lifetime == 0) \
|
||||
.filter(lambda p: p.icmpv6.nd.ra.retrans_timer == 0) \
|
||||
.filter(lambda p: p.icmpv6.nd.ra.reachable_time == 0) \
|
||||
.must_next()
|
||||
|
||||
# TODO: verify that the BR forgets the radvd's RA parameters in
|
||||
# a period after radvd is force killed.
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -99,8 +99,8 @@ class SingleBorderRouter(thread_cert.TestCase):
|
||||
self.simulator.go(10)
|
||||
self.collect_ipaddrs()
|
||||
|
||||
logging.info("BR addrs: %r", br.get_addrs())
|
||||
logging.info("ROUTER addrs: %r", router.get_addrs())
|
||||
logging.info("BR addrs: %r", br.get_addrs())
|
||||
logging.info("ROUTER addrs: %r", router.get_addrs())
|
||||
logging.info("HOST addrs: %r", host.get_addrs())
|
||||
|
||||
self.assertEqual(len(br.get_prefixes()), 1)
|
||||
|
||||
@@ -2812,18 +2812,21 @@ class LinuxHost():
|
||||
self.bash("""cat >/etc/radvd.conf <<EOF
|
||||
interface eth0
|
||||
{
|
||||
AdvSendAdvert on;
|
||||
AdvSendAdvert on;
|
||||
|
||||
MinRtrAdvInterval 3;
|
||||
MaxRtrAdvInterval 30;
|
||||
AdvDefaultPreference low;
|
||||
AdvReachableTime 200;
|
||||
AdvRetransTimer 200;
|
||||
AdvDefaultLifetime 1800;
|
||||
MinRtrAdvInterval 3;
|
||||
MaxRtrAdvInterval 30;
|
||||
AdvDefaultPreference low;
|
||||
|
||||
prefix %s
|
||||
{
|
||||
AdvOnLink on;
|
||||
AdvAutonomous %s;
|
||||
AdvRouterAddr off;
|
||||
};
|
||||
prefix %s
|
||||
{
|
||||
AdvOnLink on;
|
||||
AdvAutonomous %s;
|
||||
AdvRouterAddr off;
|
||||
};
|
||||
};
|
||||
EOF
|
||||
""" % (prefix, 'on' if slaac else 'off'))
|
||||
|
||||
@@ -615,6 +615,9 @@ class PacketFilter(object):
|
||||
return self.filter(lambda p:
|
||||
(p.icmpv6.is_neighbor_advertisement and p.icmpv6.nd.na.target_address == target_address))
|
||||
|
||||
def filter_icmpv6_nd_ra(self):
|
||||
return self.filter(lambda p: p.icmpv6.is_router_advertisement)
|
||||
|
||||
def filter_has_bbr_dataset(self):
|
||||
return self.filter("""
|
||||
thread_nwd.tlv.server.has('16')
|
||||
|
||||
Reference in New Issue
Block a user