mirror of
https://github.com/espressif/openthread.git
synced 2026-08-10 12:47:47 +00:00
[scripts] add MLR Backbone multicast routing test (#5578)
- backbone/test_mlr_multicast_routing.py verifies the basic features of Backbone multicast routing - backbone/test_mlr_multicast_routing_timeout.py verifies that MLR timeout works - backbone/test_mlr_multicast_routing_commissioner_timeout.py verifies that Commissioner MLR timeout works
This commit is contained in:
@@ -227,7 +227,7 @@ jobs:
|
||||
COVERAGE: 1
|
||||
MULTIPLY: 3
|
||||
PYTHONUNBUFFERED: 1
|
||||
OTBR_COMMIT: "bf28f461fa2a8551f0a4161c04b87f7240856608" # Date: Wed Oct 14 12:00:44 2020 +0800
|
||||
VERBOSE: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Build OTBR Docker
|
||||
|
||||
+3
-2
@@ -271,8 +271,8 @@ do_build_otbr_docker()
|
||||
echo "Building OTBR Docker ..."
|
||||
local otdir
|
||||
local otbrdir
|
||||
local otbr_options="-DOTBR_BACKBONE_ROUTER=ON -DOT_DUA=ON -DOT_MLR=ON -DOT_REFERENCE_DEVICE=ON -DOT_COVERAGE=ON"
|
||||
local otbr_commit=${OTBR_COMMIT:-master}
|
||||
local otbr_options="-DOT_DUA=ON -DOT_MLR=ON -DOT_COVERAGE=ON"
|
||||
local otbr_commit=${OTBR_COMMIT:-ce1cc3f9141571505bc9a8b705d2eb775849c92c} # ot-br-posix #532
|
||||
local otbr_docker_image=${OTBR_DOCKER_IMAGE:-otbr-ot12-backbone-ci}
|
||||
|
||||
otbrdir=$(mktemp -d -t otbr_XXXXXX)
|
||||
@@ -289,6 +289,7 @@ do_build_otbr_docker()
|
||||
cp -r "${otdir}" third_party/openthread/repo
|
||||
rm -rf .git
|
||||
docker build -t "${otbr_docker_image}" -f etc/docker/Dockerfile . \
|
||||
--build-arg BACKBONE_ROUTER=1 \
|
||||
--build-arg REFERENCE_DEVICE=1 \
|
||||
--build-arg OT_BACKBONE_CI=1 \
|
||||
--build-arg NAT64=0 \
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
#!/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 test verifies that the basic MLR feature works.
|
||||
#
|
||||
import logging
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import thread_cert
|
||||
from pktverify.packet_verifier import PacketVerifier
|
||||
|
||||
PBBR = 1
|
||||
SBBR = 2
|
||||
ROUTER = 3
|
||||
LEADER = 4
|
||||
FED = 5
|
||||
MED = 6
|
||||
SED = 7
|
||||
HOST = 8
|
||||
|
||||
SED_POLL_PERIOD = 1000 # Milliseconds
|
||||
|
||||
MA1 = 'ff04::1234:777a:1'
|
||||
MA2 = 'ff05::1234:777a:2'
|
||||
MA3 = 'ff05::1234:777a:3'
|
||||
MA4 = 'ff05::1234:777a:4'
|
||||
MA5 = 'ff05::1234:777a:5'
|
||||
|
||||
BBR_REGISTRATION_JITTER = 1
|
||||
WAIT_REDUNDANCE = 3
|
||||
|
||||
|
||||
class TestMlr(thread_cert.TestCase):
|
||||
USE_MESSAGE_FACTORY = False
|
||||
|
||||
# Topology:
|
||||
# --------(eth)---------
|
||||
# | | |
|
||||
# PBBR---SBBR HOST
|
||||
# \ / \
|
||||
# \ / \
|
||||
# ROUTER--LEADER
|
||||
# / | \
|
||||
# / | \
|
||||
# FED MED SED
|
||||
#
|
||||
# More links:
|
||||
# PBBR---LEADER
|
||||
#
|
||||
TOPOLOGY = {
|
||||
PBBR: {
|
||||
'name': 'PBBR',
|
||||
'allowlist': [SBBR, ROUTER, LEADER],
|
||||
'is_otbr': True,
|
||||
'version': '1.2',
|
||||
'router_selection_jitter': 1,
|
||||
'bbr_registration_jitter': BBR_REGISTRATION_JITTER,
|
||||
},
|
||||
SBBR: {
|
||||
'name': 'SBBR',
|
||||
'allowlist': [PBBR, ROUTER, LEADER],
|
||||
'is_otbr': True,
|
||||
'version': '1.2',
|
||||
'router_selection_jitter': 1,
|
||||
'bbr_registration_jitter': BBR_REGISTRATION_JITTER,
|
||||
},
|
||||
ROUTER: {
|
||||
'name': 'ROUTER',
|
||||
'allowlist': [PBBR, SBBR, LEADER, FED, MED, SED],
|
||||
'version': '1.2',
|
||||
'router_selection_jitter': 1,
|
||||
},
|
||||
LEADER: {
|
||||
'name': 'LEADER',
|
||||
'allowlist': [PBBR, SBBR, ROUTER],
|
||||
'version': '1.2',
|
||||
'router_selection_jitter': 1,
|
||||
},
|
||||
FED: {
|
||||
'name': 'FED',
|
||||
'allowlist': [ROUTER],
|
||||
'version': '1.2',
|
||||
'router_upgrade_threshold': 0,
|
||||
},
|
||||
MED: {
|
||||
'name': 'MED',
|
||||
'allowlist': [ROUTER],
|
||||
'version': '1.2',
|
||||
'mode': 'rn',
|
||||
},
|
||||
SED: {
|
||||
'name': 'SED',
|
||||
'allowlist': [ROUTER],
|
||||
'version': '1.2',
|
||||
'mode': 'n'
|
||||
},
|
||||
HOST: {
|
||||
'name': 'Host',
|
||||
'is_host': True
|
||||
},
|
||||
}
|
||||
|
||||
def test(self):
|
||||
# Bring up Host
|
||||
self.nodes[HOST].start()
|
||||
|
||||
# Bring up Leader
|
||||
self.nodes[LEADER].start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('leader', self.nodes[LEADER].get_state())
|
||||
|
||||
# Bring up Router
|
||||
self.nodes[ROUTER].start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('router', self.nodes[ROUTER].get_state())
|
||||
|
||||
# Bring up PBBR
|
||||
self.nodes[PBBR].start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('router', self.nodes[PBBR].get_state())
|
||||
self.nodes[PBBR].enable_backbone_router()
|
||||
self.simulator.go(10)
|
||||
self.assertTrue(self.nodes[PBBR].is_primary_backbone_router)
|
||||
self.nodes[PBBR].add_prefix(config.DOMAIN_PREFIX, "parosD")
|
||||
self.nodes[PBBR].register_netdata()
|
||||
|
||||
# Bring up SBBR
|
||||
self.nodes[SBBR].start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('router', self.nodes[SBBR].get_state())
|
||||
self.nodes[SBBR].enable_backbone_router()
|
||||
self.simulator.go(10)
|
||||
self.assertFalse(self.nodes[SBBR].is_primary_backbone_router)
|
||||
|
||||
# Bring up FED, MED, SED
|
||||
self.nodes[FED].start()
|
||||
self.nodes[MED].start()
|
||||
self.nodes[SED].set_pollperiod(SED_POLL_PERIOD)
|
||||
self.nodes[SED].start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('child', self.nodes[FED].get_state())
|
||||
self.assertEqual('child', self.nodes[MED].get_state())
|
||||
self.assertEqual('child', self.nodes[SED].get_state())
|
||||
|
||||
# Verify Multicast Routing works for all devices
|
||||
self._verify_multicast_routing(ROUTER, MA1)
|
||||
self._verify_multicast_routing(LEADER, MA2)
|
||||
self._verify_multicast_routing(FED, MA3)
|
||||
self._verify_multicast_routing(MED, MA4, is_med=True)
|
||||
self._verify_multicast_routing(SED, MA5, is_med=True, is_sed=True)
|
||||
|
||||
# Verify MA_scope2 is not reachable from Host
|
||||
MA_scope2 = 'ff02::10'
|
||||
self.nodes[ROUTER].add_ipmaddr(MA_scope2)
|
||||
self.simulator.go(3)
|
||||
self.assertFalse(self.nodes[HOST].ping(MA_scope2, backbone=True, ttl=10))
|
||||
self.nodes[ROUTER].del_ipmaddr(MA_scope2)
|
||||
|
||||
# Verify MA_scope3 is not reachable from Host
|
||||
MA_scope3 = 'ff03::1234:777a:5'
|
||||
self.nodes[ROUTER].add_ipmaddr(MA_scope3)
|
||||
self.simulator.go(3)
|
||||
self.assertFalse(self.nodes[HOST].ping(MA_scope3, backbone=True, ttl=10))
|
||||
self.nodes[ROUTER].del_ipmaddr(MA_scope3)
|
||||
|
||||
# Router subscribes MA2 and MA3 at the same time and verify that they are both reachable
|
||||
self.nodes[ROUTER].add_ipmaddr(MA2)
|
||||
self.nodes[ROUTER].add_ipmaddr(MA3)
|
||||
self.simulator.go(3)
|
||||
self.assertTrue(self.nodes[HOST].ping(MA2, backbone=True, ttl=10))
|
||||
self.assertTrue(self.nodes[HOST].ping(MA3, backbone=True, ttl=10))
|
||||
self.nodes[ROUTER].del_ipmaddr(MA2)
|
||||
self.nodes[ROUTER].del_ipmaddr(MA3)
|
||||
self.simulator.go(1)
|
||||
|
||||
def _verify_multicast_routing(self, nodeid: int, ma: str, is_med=False, is_sed=False):
|
||||
logging.info('_verify_multicast_routing: nodeid=%d, MA=%s', nodeid, ma)
|
||||
# Verify MA is not reachable from Host initially
|
||||
self.assertFalse(self.nodes[HOST].ping(ma, backbone=True, ttl=10))
|
||||
|
||||
# Device subscribes MA
|
||||
self.nodes[nodeid].add_ipmaddr(ma)
|
||||
self.simulator.go(3 + (SED_POLL_PERIOD * 2 / 1000) * is_sed + config.PARENT_AGGREGATIOIN_DELAY * is_med +
|
||||
WAIT_REDUNDANCE)
|
||||
|
||||
# Verify MA is reachable from Host
|
||||
self.assertTrue(self.nodes[HOST].ping(ma, backbone=True, ttl=10))
|
||||
|
||||
# Device unsubscribes MA
|
||||
self.nodes[nodeid].del_ipmaddr(ma)
|
||||
self.simulator.go(1)
|
||||
|
||||
# Verify MA is not reachable from Host after unsubscribed
|
||||
self.assertFalse(self.nodes[HOST].ping(ma, backbone=True, ttl=10))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
#!/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 test verifies that the MLR timeout configured by Commissioner works for multicast routing.
|
||||
#
|
||||
import unittest
|
||||
|
||||
from pktverify.consts import NM_COMMISSIONER_SESSION_ID_TLV
|
||||
from pktverify.packet_verifier import PacketVerifier
|
||||
from test_mlr_multicast_routing_timeout import ROUTER, HOST
|
||||
from test_mlr_multicast_routing_timeout import TestMlrTimeout, MLR_TIMEOUT, WAIT_REDUNDANCE, MA1
|
||||
|
||||
COMMISSIONER_MLR_TIMEOUT = 360
|
||||
|
||||
assert COMMISSIONER_MLR_TIMEOUT > MLR_TIMEOUT + WAIT_REDUNDANCE * 3, "COMMISSIONER_MLR_TIMEOUT should be significantly larger than MLR_TIMEOUT"
|
||||
|
||||
|
||||
class TestMlrCommissionerTimeout(TestMlrTimeout):
|
||||
|
||||
# Topology (same as TestMlrTimeout):
|
||||
# --------(eth)---------
|
||||
# | |
|
||||
# PBBR(Leader) HOST
|
||||
# |
|
||||
# ROUTER
|
||||
#
|
||||
|
||||
def test(self):
|
||||
self._bootstrap()
|
||||
|
||||
# Ping 1: PBBR should not forward to Thread network
|
||||
self.assertFalse(self.nodes[HOST].ping(MA1, backbone=True, ttl=10))
|
||||
|
||||
# Router starts Commissioner
|
||||
self.nodes[ROUTER].commissioner_start()
|
||||
self.simulator.go(10)
|
||||
self.assertEqual('active', self.nodes[ROUTER].commissioner_state())
|
||||
|
||||
# Router (Commissioner) registers MA1
|
||||
self.nodes[ROUTER].register_multicast_listener(MA1, timeout=COMMISSIONER_MLR_TIMEOUT)
|
||||
self.simulator.go(WAIT_REDUNDANCE)
|
||||
|
||||
# Ping 2: PBBR should forward to Thread network
|
||||
self.assertFalse(self.nodes[HOST].ping(MA1, backbone=True, ttl=10))
|
||||
|
||||
self.simulator.go(MLR_TIMEOUT + WAIT_REDUNDANCE)
|
||||
|
||||
# Ping 3: PBBR should forward to Thread network
|
||||
self.assertFalse(self.nodes[HOST].ping(MA1, backbone=True, ttl=10))
|
||||
|
||||
self.simulator.go(COMMISSIONER_MLR_TIMEOUT - MLR_TIMEOUT)
|
||||
|
||||
# Ping 4: PBBR should NOT forward to Thread network
|
||||
self.assertFalse(self.nodes[HOST].ping(MA1, backbone=True, ttl=10))
|
||||
|
||||
def verify(self, pv: PacketVerifier):
|
||||
pkts = pv.pkts
|
||||
pv.add_common_vars()
|
||||
pv.summary.show()
|
||||
|
||||
ROUTER = pv.vars['ROUTER']
|
||||
PBBR = pv.vars['PBBR']
|
||||
MM = pv.vars['MM_PORT']
|
||||
HOST_ETH = pv.vars['Host_ETH']
|
||||
HOST_BGUA = pv.vars['Host_BGUA']
|
||||
|
||||
start = pkts.index
|
||||
|
||||
# Verify that Router sends MLR.req for MA1 with Commissioner timeout
|
||||
pkts.filter_wpan_src64(ROUTER).filter_coap_request('/n/mr', port=MM).must_next().must_verify(
|
||||
"""
|
||||
thread_meshcop.tlv.ipv6_addr == {ipv6_addr}
|
||||
and thread_nm.tlv.timeout == {timeout}
|
||||
and {NM_COMMISSIONER_SESSION_ID_TLV} in thread_nm.tlv.type
|
||||
""",
|
||||
ipv6_addr=[MA1],
|
||||
timeout=COMMISSIONER_MLR_TIMEOUT,
|
||||
NM_COMMISSIONER_SESSION_ID_TLV=NM_COMMISSIONER_SESSION_ID_TLV)
|
||||
|
||||
mr_index = pkts.index
|
||||
|
||||
# Ping 1: Host pings Router before MLR.reg
|
||||
before_mr_pkts = pkts.range(start, mr_index, cascade=False)
|
||||
ping1 = before_mr_pkts.filter_eth_src(HOST_ETH).filter_ipv6_dst(MA1).filter_ping_request().must_next()
|
||||
|
||||
# PBBR should not forward Ping 1 to Thread network
|
||||
before_mr_pkts.filter_wpan_src64(PBBR).filter_ipv6_dst(MA1).filter_ping_request(
|
||||
identifier=ping1.icmpv6.echo.identifier).must_not_next()
|
||||
|
||||
# Ping 2: Host pings Router after MLR.req
|
||||
ping2 = pkts.filter_eth_src(HOST_ETH).filter_ipv6_dst(MA1).filter_ping_request().must_next()
|
||||
# PBBR should forward this ping request to Thread network
|
||||
pkts.filter_wpan_src64(PBBR).filter_AMPLFMA().filter_ping_request(
|
||||
identifier=ping2.icmpv6.echo.identifier).must_next()
|
||||
|
||||
# Ping 3: Host pings Router after delayed MLR Timeout
|
||||
ping3 = pkts.filter_eth_src(HOST_ETH).filter_ipv6_dst(MA1).filter_ping_request().must_next()
|
||||
# PBBR should forward this ping request to Thread network because MA doesn't expire yet
|
||||
pkts.filter_wpan_src64(PBBR).filter_AMPLFMA().filter_ping_request(
|
||||
identifier=ping3.icmpv6.echo.identifier).must_next()
|
||||
|
||||
# Ping 4: Host pings Router after delayed Commissioner MLR Timeout
|
||||
ping4 = pkts.filter_eth_src(HOST_ETH).filter_ipv6_dst(MA1).filter_ping_request().must_next()
|
||||
# PBBR should NOT forward this ping request to Thread network because MLR has timeout
|
||||
pkts.filter_wpan_src64(PBBR).filter_AMPLFMA().filter_ping_request(
|
||||
identifier=ping4.icmpv6.echo.identifier).must_not_next()
|
||||
|
||||
|
||||
del TestMlrTimeout
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,182 @@
|
||||
#!/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 test verifies that the MLR timeout works for multicast routing.
|
||||
#
|
||||
import unittest
|
||||
|
||||
import config
|
||||
import thread_cert
|
||||
from pktverify.packet_verifier import PacketVerifier
|
||||
|
||||
PBBR = 1
|
||||
ROUTER = 2
|
||||
HOST = 3
|
||||
|
||||
MA1 = 'ff05::1234:777a:1'
|
||||
|
||||
BBR_REGISTRATION_JITTER = 1
|
||||
REG_DELAY = 10
|
||||
MLR_TIMEOUT = 300
|
||||
WAIT_REDUNDANCE = 3
|
||||
|
||||
|
||||
class TestMlrTimeout(thread_cert.TestCase):
|
||||
USE_MESSAGE_FACTORY = False
|
||||
|
||||
# Topology:
|
||||
# --------(eth)---------
|
||||
# | |
|
||||
# PBBR(Leader) HOST
|
||||
# |
|
||||
# ROUTER
|
||||
#
|
||||
TOPOLOGY = {
|
||||
PBBR: {
|
||||
'name': 'PBBR',
|
||||
'allowlist': [ROUTER],
|
||||
'is_otbr': True,
|
||||
'version': '1.2',
|
||||
'router_selection_jitter': 1,
|
||||
'bbr_registration_jitter': BBR_REGISTRATION_JITTER,
|
||||
},
|
||||
ROUTER: {
|
||||
'name': 'ROUTER',
|
||||
'allowlist': [PBBR],
|
||||
'version': '1.2',
|
||||
'router_selection_jitter': 1,
|
||||
},
|
||||
HOST: {
|
||||
'name': 'Host',
|
||||
'is_host': True
|
||||
},
|
||||
}
|
||||
|
||||
def _bootstrap(self):
|
||||
# Bring up Host
|
||||
self.nodes[HOST].start()
|
||||
|
||||
# Bring up PBBR
|
||||
self.nodes[PBBR].start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('leader', self.nodes[PBBR].get_state())
|
||||
self.nodes[PBBR].enable_backbone_router()
|
||||
self.nodes[PBBR].set_backbone_router(reg_delay=REG_DELAY, mlr_timeout=MLR_TIMEOUT)
|
||||
self.simulator.go(10)
|
||||
self.assertTrue(self.nodes[PBBR].is_primary_backbone_router)
|
||||
self.nodes[PBBR].add_prefix(config.DOMAIN_PREFIX, "parosD")
|
||||
self.nodes[PBBR].register_netdata()
|
||||
|
||||
# Bring up Router
|
||||
self.nodes[ROUTER].start()
|
||||
self.simulator.go(5)
|
||||
self.assertEqual('router', self.nodes[ROUTER].get_state())
|
||||
|
||||
self.collect_ipaddrs()
|
||||
|
||||
def test(self):
|
||||
self._bootstrap()
|
||||
|
||||
self.assertFalse(self.nodes[HOST].ping(MA1, backbone=True, ttl=10))
|
||||
|
||||
# Router subscribes MA
|
||||
self.nodes[ROUTER].add_ipmaddr(MA1)
|
||||
self.simulator.go(WAIT_REDUNDANCE)
|
||||
|
||||
# Verify MA is reachable from Host
|
||||
self.assertTrue(self.nodes[HOST].ping(MA1, backbone=True, ttl=10))
|
||||
self.simulator.go(WAIT_REDUNDANCE)
|
||||
|
||||
# Router unsubscribes MA
|
||||
self.nodes[ROUTER].del_ipmaddr(MA1)
|
||||
self.simulator.go(WAIT_REDUNDANCE)
|
||||
|
||||
# Verify MA is not reachable from Host after unsubscribed
|
||||
# But PBBR should still forward the Ping Requests to Thread network
|
||||
self.assertFalse(self.nodes[HOST].ping(MA1, backbone=True, ttl=10))
|
||||
|
||||
self.simulator.go(MLR_TIMEOUT + WAIT_REDUNDANCE)
|
||||
|
||||
# Verify MA is not reachable from Host after MLR timeout
|
||||
# PBBR should not forward the Ping Requests to Thread network
|
||||
self.assertFalse(self.nodes[HOST].ping(MA1, backbone=True, ttl=10))
|
||||
|
||||
def verify(self, pv: PacketVerifier):
|
||||
pkts = pv.pkts
|
||||
pv.add_common_vars()
|
||||
pv.summary.show()
|
||||
|
||||
ROUTER = pv.vars['ROUTER']
|
||||
PBBR = pv.vars['PBBR']
|
||||
MM = pv.vars['MM_PORT']
|
||||
HOST_ETH = pv.vars['Host_ETH']
|
||||
HOST_BGUA = pv.vars['Host_BGUA']
|
||||
|
||||
start = pkts.index
|
||||
|
||||
# Verify that Router sends MLR.req for MA1
|
||||
pkts.filter_wpan_src64(ROUTER).filter_coap_request('/n/mr', port=MM).must_next().must_verify(
|
||||
'thread_meshcop.tlv.ipv6_addr == {ipv6_addr}', ipv6_addr=[MA1])
|
||||
|
||||
mr_index = pkts.index
|
||||
|
||||
# Host pings Router before MLR.reg
|
||||
before_mr_pkts = pkts.range(start, mr_index, cascade=False)
|
||||
ping = before_mr_pkts.filter_eth_src(HOST_ETH).filter_ipv6_dst(MA1).filter_ping_request().must_next()
|
||||
|
||||
# PBBR should not forward this ping request to Thread network
|
||||
before_mr_pkts.filter_wpan_src64(PBBR).filter_ipv6_dst(MA1).filter_ping_request(
|
||||
identifier=ping.icmpv6.echo.identifier).must_not_next()
|
||||
|
||||
# Host pings Router after MLR.req
|
||||
ping = pkts.filter_eth_src(HOST_ETH).filter_ipv6_dst(MA1).filter_ping_request().must_next()
|
||||
# PBBR should forward this ping request to Thread network
|
||||
pkts.filter_wpan_src64(PBBR).filter_AMPLFMA().filter_ping_request(
|
||||
identifier=ping.icmpv6.echo.identifier).must_next()
|
||||
# Router should reply
|
||||
pkts.filter_wpan_src64(ROUTER).filter_ipv6_dst(HOST_BGUA).filter_ping_reply(
|
||||
identifier=ping.icmpv6.echo.identifier).must_next()
|
||||
|
||||
# Host pings Router after Router unsubscribed MA
|
||||
ping = pkts.filter_eth_src(HOST_ETH).filter_ipv6_dst(MA1).filter_ping_request().must_next()
|
||||
# PBBR should forward this ping request to Thread network because MA doesn't expire yet
|
||||
pkts.filter_wpan_src64(PBBR).filter_AMPLFMA().filter_ping_request(
|
||||
identifier=ping.icmpv6.echo.identifier).must_next()
|
||||
# Router should NOT reply because it has unsubcribed the multicast address
|
||||
pkts.filter_wpan_src64(ROUTER).filter_ipv6_dst(HOST_BGUA).filter_ping_reply(
|
||||
identifier=ping.icmpv6.echo.identifier).must_not_next()
|
||||
|
||||
# Host pings Router after MLR timeout
|
||||
ping = pkts.filter_eth_src(HOST_ETH).filter_ipv6_dst(MA1).filter_ping_request().must_next()
|
||||
# PBBR should NOT forward this ping request to Thread network because MLR has timeout
|
||||
pkts.filter_wpan_src64(PBBR).filter_AMPLFMA().filter_ping_request(
|
||||
identifier=ping.icmpv6.echo.identifier).must_not_next()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -71,7 +71,6 @@ BACKBONE_IFNAME = 'eth0'
|
||||
|
||||
OTBR_DOCKER_IMAGE = os.getenv('OTBR_DOCKER_IMAGE', 'otbr-ot12-backbone-ci')
|
||||
OTBR_DOCKER_NAME_PREFIX = f'otbr_{PORT_OFFSET}_'
|
||||
OTBR_COMMIT = os.getenv('OTBR_COMMIT', 'master')
|
||||
|
||||
ALL_NETWORK_BBRS_ADDRESS = 'ff32:40:fd00:db8:0:0:0:3'
|
||||
|
||||
|
||||
@@ -1956,11 +1956,14 @@ class LinuxHost():
|
||||
|
||||
assert False, output
|
||||
|
||||
def ping_ether(self, ipaddr, num_responses=1, size=None, timeout=5) -> int:
|
||||
def ping_ether(self, ipaddr, num_responses=1, size=None, timeout=5, ttl=None) -> int:
|
||||
cmd = f'ping -6 {ipaddr} -I eth0 -c {num_responses} -W {timeout}'
|
||||
if size is not None:
|
||||
cmd += f' -s {size}'
|
||||
|
||||
if ttl is not None:
|
||||
cmd += f' -t {ttl}'
|
||||
|
||||
resp_count = 0
|
||||
|
||||
try:
|
||||
@@ -1990,6 +1993,7 @@ class LinuxHost():
|
||||
class OtbrNode(LinuxHost, NodeImpl, OtbrDocker):
|
||||
is_otbr = True
|
||||
is_bbr = True # OTBR is also BBR
|
||||
node_type = 'otbr-docker'
|
||||
|
||||
def __repr__(self):
|
||||
return f'Otbr<{self.nodeid}>'
|
||||
|
||||
@@ -136,6 +136,7 @@ NL_ROUTER_MASK_TLV = 7
|
||||
NL_ND_OPTION_TLV = 8
|
||||
NL_ND_DATA_TLV = 9
|
||||
NL_THREAD_NETWORK_DATA_TLV = 10
|
||||
NM_COMMISSIONER_SESSION_ID_TLV = 15
|
||||
|
||||
# Network Layer Status
|
||||
NL_SUCESS = 0
|
||||
|
||||
@@ -128,6 +128,7 @@ class Packet(object):
|
||||
|
||||
def must_verify(self, func: Union[str, Callable], **vars):
|
||||
if not self.verify(func, **vars):
|
||||
self.debug_fields()
|
||||
raise errors.VerifyFailed(self)
|
||||
|
||||
def must_not_verify(self, func: Union[str, Callable], **vars):
|
||||
|
||||
@@ -436,8 +436,10 @@ class PacketFilter(object):
|
||||
assert isinstance(addr, (str, ExtAddr)), addr
|
||||
return self.filter(lambda p: p.wpan.dst64 == addr, **kwargs)
|
||||
|
||||
def filter_ping_request(self, **kwargs):
|
||||
return self.filter(lambda p: p.icmpv6.is_ping_request, **kwargs)
|
||||
def filter_ping_request(self, identifier=None, **kwargs):
|
||||
return self.filter(
|
||||
lambda p: p.icmpv6.is_ping_request and (identifier is None or p.icmpv6.echo.identifier == identifier),
|
||||
**kwargs)
|
||||
|
||||
def filter_ping_reply(self, **kwargs):
|
||||
identifier = kwargs.pop('identifier', None)
|
||||
@@ -475,6 +477,9 @@ class PacketFilter(object):
|
||||
def filter_LLARMA(self, **kwargs):
|
||||
return self.filter(lambda p: p.ipv6.dst == consts.LINK_LOCAL_ALL_ROUTERS_MULTICAST_ADDRESS, **kwargs)
|
||||
|
||||
def filter_AMPLFMA(self, **kwargs):
|
||||
return self.filter(lambda p: p.ipv6.dst == consts.ALL_MPL_FORWARDERS_MA, **kwargs)
|
||||
|
||||
def filter_mle(self, **kwargs):
|
||||
return self.filter(attrgetter('mle'), **kwargs)
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ from collections import Counter
|
||||
|
||||
import config
|
||||
|
||||
MULTIPLE_JOBS = 10
|
||||
MULTIPLE_JOBS = 20
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format='File "%(pathname)s", line %(lineno)d, in %(funcName)s\n'
|
||||
|
||||
@@ -208,6 +208,9 @@ class TestCase(NcpSupportMixin, unittest.TestCase):
|
||||
if 'max_children' in params:
|
||||
self.nodes[i].set_max_children(params['max_children'])
|
||||
|
||||
if 'bbr_registration_jitter' in params:
|
||||
self.nodes[i].set_bbr_registration_jitter(params['bbr_registration_jitter'])
|
||||
|
||||
# we have to add allowlist after nodes are all created
|
||||
for i, params in initial_topology.items():
|
||||
allowlist = params['allowlist']
|
||||
@@ -364,7 +367,6 @@ class TestCase(NcpSupportMixin, unittest.TestCase):
|
||||
'interface': config.BACKBONE_DOCKER_NETWORK_NAME,
|
||||
'prefix': config.BACKBONE_PREFIX,
|
||||
},
|
||||
'otbr_commit': config.OTBR_COMMIT,
|
||||
'domain_prefix': config.DOMAIN_PREFIX,
|
||||
'env': {
|
||||
'PORT_OFFSET': config.PORT_OFFSET,
|
||||
|
||||
Reference in New Issue
Block a user