[border-router] initial implementation of prefix/route management (#5856)

This is the initial implementation of Thread Border Router
prefix/route management with:

- Create & publish off-mesh routable (OMR) prefix for Thread network
  - configure the BR as default route.
  - converge to the smallest OMR prefix if multiple prefixes are
    present at the same time.
- Create & advertise on-link prefix for infrastructure network
  - monitor RA messages on infra link and stops myself if there is
    someone else advertising an on-link prefix.

It currently supports single infrastructure link with possibly
multiple BRs. Support for multiple infrastructure links is left for
future.
This commit is contained in:
kangping
2020-12-24 07:19:12 +08:00
committed by GitHub
parent 8f9894f7c0
commit 995ad66846
46 changed files with 3104 additions and 13 deletions
+60
View File
@@ -249,6 +249,10 @@ jobs:
MULTIPLY: 1
PYTHONUNBUFFERED: 1
VERBOSE: 1
# The border routing and DUA feature can coexist, but current wireshark
# packet verification can't handle it because of the order of context ID
# of OMR prefix and Domain prefix is not deterministic.
BORDER_ROUTING: 0
steps:
- uses: actions/checkout@v2
with:
@@ -294,6 +298,61 @@ jobs:
name: cov-thread-1-2-backbone
path: tmp/coverage.info
thread-border-routing:
runs-on: ubuntu-20.04
env:
REFERENCE_DEVICE: 1
VIRTUAL_TIME: 0
PACKET_VERIFICATION: 1
THREAD_VERSION: 1.2
INTER_OP: 1
COVERAGE: 1
MULTIPLY: 1
PYTHONUNBUFFERED: 1
VERBOSE: 1
steps:
- uses: actions/checkout@v2
- name: Build OTBR Docker
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
run: |
./script/test build_otbr_docker
- name: Bootstrap
run: |
sudo rm /etc/apt/sources.list.d/* && sudo apt-get update
sudo apt-get --no-install-recommends install -y python3-setuptools python3-wheel ninja-build socat lcov
python3 -m pip install -r tests/scripts/thread-cert/requirements.txt
- name: Build
run: |
./script/test build
- name: Get Thread-Wireshark
run: |
./script/test get_thread_wireshark
- name: Run
run: |
export CI_ENV="$(bash <(curl -s https://codecov.io/env)) -e GITHUB_ACTIONS -e COVERAGE"
echo "CI_ENV=${CI_ENV}"
sudo -E ./script/test cert_suite ./tests/scripts/thread-cert/border_routing/*.py || (sudo chmod a+r *.log *.json *.pcap && false)
- uses: actions/upload-artifact@v2
with:
name: cov-thread-border-routing-docker
path: /tmp/coverage/
- uses: actions/upload-artifact@v2
if: ${{ failure() }}
with:
name: thread-border-routing-results
path: |
*.pcap
*.json
*.log
- name: Generate Coverage
run: |
./script/test generate_coverage gcc
- uses: actions/upload-artifact@v2
with:
name: cov-thread-border-routing
path: tmp/coverage.info
upload-coverage:
needs:
- thread-1-2
@@ -301,6 +360,7 @@ jobs:
- packet-verification-1-1-on-1-2
- expects
- thread-1-2-backbone
- thread-border-routing
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
+3
View File
@@ -186,6 +186,9 @@ LOCAL_SRC_FILES := \
src/core/backbone_router/bbr_manager.cpp \
src/core/backbone_router/multicast_listeners_table.cpp \
src/core/backbone_router/ndproxy_table.cpp \
src/core/border_router/infra_if_platform.cpp \
src/core/border_router/router_advertisement.cpp \
src/core/border_router/routing_manager.cpp \
src/core/coap/coap.cpp \
src/core/coap/coap_message.cpp \
src/core/coap/coap_secure.cpp \
+6
View File
@@ -72,6 +72,11 @@ if(OT_BORDER_ROUTER)
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE=1")
endif()
option(OT_BORDER_ROUTING "enable (duckhorn) border routing support")
if(OT_BORDER_ROUTING)
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE=1")
endif()
if(NOT OT_EXTERNAL_MBEDTLS)
set(OT_MBEDTLS mbedtls)
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS=1")
@@ -290,6 +295,7 @@ if(OT_FULL_LOGS)
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_API=1")
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_ARP=1")
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_BBR=1")
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_BR=1")
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_CLI=1")
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_COAP=1")
target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_DUA=1")
+3
View File
@@ -90,6 +90,9 @@ if (openthread_enable_core_config_args) {
# Enable border router support
openthread_config_border_router_enable = false
# Enable border routing support
openthread_config_border_routing_enable = false
# Enable channel manager support
openthread_config_channel_manager_enable = false
+6
View File
@@ -32,6 +32,7 @@ BACKBONE_ROUTER ?= 0
BIG_ENDIAN ?= 0
BORDER_AGENT ?= 0
BORDER_ROUTER ?= 0
BORDER_ROUTING ?= 0
COAP ?= 0
COAP_OBSERVE ?= 0
COAPS ?= 0
@@ -97,6 +98,10 @@ ifeq ($(BORDER_ROUTER),1)
COMMONCFLAGS += -DOPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE=1
endif
ifeq ($(BORDER_ROUTING),1)
COMMONCFLAGS += -DOPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE=1
endif
ifeq ($(COAP),1)
COMMONCFLAGS += -DOPENTHREAD_CONFIG_COAP_API_ENABLE=1
endif
@@ -319,6 +324,7 @@ LOG_FLAGS += -DOPENTHREAD_CONFIG_LOG_LEVEL=OT_LOG_LEVEL_DEBG
LOG_FLAGS += -DOPENTHREAD_CONFIG_LOG_API=1
LOG_FLAGS += -DOPENTHREAD_CONFIG_LOG_ARP=1
LOG_FLAGS += -DOPENTHREAD_CONFIG_LOG_BBR=1
LOG_FLAGS += -DOPENTHREAD_CONFIG_LOG_BR=1
LOG_FLAGS += -DOPENTHREAD_CONFIG_LOG_CLI=1
LOG_FLAGS += -DOPENTHREAD_CONFIG_LOG_COAP=1
LOG_FLAGS += -DOPENTHREAD_CONFIG_LOG_DUA=1
+1
View File
@@ -89,6 +89,7 @@ ot_platform_headers = \
openthread/platform/diag.h \
openthread/platform/flash.h \
openthread/platform/entropy.h \
openthread/platform/infra_if.h \
openthread/platform/memory.h \
openthread/platform/misc.h \
openthread/platform/logging.h \
+1
View File
@@ -96,6 +96,7 @@ source_set("openthread") {
"platform/diag.h",
"platform/entropy.h",
"platform/flash.h",
"platform/infra_if.h",
"platform/logging.h",
"platform/memory.h",
"platform/messagepool.h",
+13
View File
@@ -52,6 +52,19 @@ extern "C" {
*
*/
/**
* This method initializes the Border Routing Manager on given infrastructure interface.
*
* @param[in] aInstance A pointer to an OpenThread instance.
* @param[in] aInfraIfIndex The infrastructure interface index.
*
* @retval OT_ERROR_NONE Successfully started the border routing manager on given infrastructure.
* @retval OT_ERROR_INVALID_ARGS The index of the infra interface is not valid.
* @retval OT_ERROR_FAILED Internal failure. This is usually failed to generate random prefixes.
*
*/
otError otBorderRoutingInit(otInstance *aInstance, uint32_t aInfraIfIndex);
/**
* This method provides a full or stable copy of the local Thread Network Data.
*
+2
View File
@@ -64,6 +64,8 @@ typedef enum otIcmp6Type
OT_ICMP6_TYPE_PARAMETER_PROBLEM = 4, ///< Parameter Problem
OT_ICMP6_TYPE_ECHO_REQUEST = 128, ///< Echo Request
OT_ICMP6_TYPE_ECHO_REPLY = 129, ///< Echo Reply
OT_ICMP6_TYPE_ROUTER_SOLICIT = 133, ///< Router Solicitation
OT_ICMP6_TYPE_ROUTER_ADVERT = 134, ///< Router Advertisement
} otIcmp6Type;
/**
+1 -1
View File
@@ -53,7 +53,7 @@ extern "C" {
* @note This number versions both OpenThread platform and user APIs.
*
*/
#define OPENTHREAD_API_VERSION (58)
#define OPENTHREAD_API_VERSION (59)
/**
* @addtogroup api-instance
+99
View File
@@ -0,0 +1,99 @@
/*
* 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.
*/
/**
* @file
* @brief
* This file includes the platform abstraction for the infrastructure network interface.
*
*/
#ifndef OPENTHREAD_PLATFORM_INFRA_IF_H_
#define OPENTHREAD_PLATFORM_INFRA_IF_H_
#include <stdint.h>
#include <openthread/error.h>
#include <openthread/instance.h>
#include <openthread/ip6.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* This method sends an ICMPv6 Neighbor Discovery message on given infrastructure interface.
*
* See RFC 4861: https://tools.ietf.org/html/rfc4861.
*
* @param[in] aInfraIfIndex The index of the infrastructure interface this message is sent to.
* @param[in] aDestAddress The destination address this message is sent to.
* @param[in] aBuffer The ICMPv6 message buffer.
* @param[in] aBufferLength The length of the message buffer.
*
* @note Per RFC 4861, the implementation should send the message with IPv6 link-local source address
* of interface @p aInfraIfIndex and IP Hop Limit 255.
*
* @retval OT_ERROR_NONE Successfully sent the ICMPv6 message.
* @retval OT_ERROR_FAILED Failed to send the ICMPv6 message.
*
*/
otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex,
const otIp6Address *aDestAddress,
const uint8_t * aBuffer,
uint16_t aBufferLength);
/**
* The infra interface driver calls this method to notify OpenThread
* that an ICMPv6 Neighbor Discovery message is received.
*
* See RFC 4861: https://tools.ietf.org/html/rfc4861.
*
* @param[in] aInstance The OpenThread instance structure.
* @param[in] aInfraIfIndex The index of the infrastructure interface on which the ICMPv6 message is received.
* @param[in] aSrcAddress The source address this message is received from.
* @param[in] aBuffer The ICMPv6 message buffer.
* @param[in] aBufferLength The length of the ICMPv6 message buffer.
*
* @note Per RFC 4861, the caller should enforce that the source address MUST be a IPv6 link-local
* address and the IP Hop Limit MUST be 255.
*
* @note ICMPv6 message received from @p aInfraIfIndex via multicast loopback should not be passed in.
*
*/
extern void otPlatInfraIfRecvIcmp6Nd(otInstance * aInstance,
uint32_t aInfraIfIndex,
const otIp6Address *aSrcAddress,
const uint8_t * aBuffer,
uint16_t aBufferLength);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // OPENTHREAD_PLATFORM_INFRA_IF_H_
+1
View File
@@ -137,6 +137,7 @@ typedef enum otLogRegion
OT_LOG_REGION_BBR = 17, ///< Backbone Router (available since Thread 1.2)
OT_LOG_REGION_MLR = 18, ///< Multicast Listener Registration (available since Thread 1.2)
OT_LOG_REGION_DUA = 19, ///< Domain Unicast Address (available since Thread 1.2)
OT_LOG_REGION_BR = 20, ///< Border Router
} otLogRegion;
/**
+6 -1
View File
@@ -44,6 +44,7 @@ readonly OT_NATIVE_IP="${OT_NATIVE_IP:-0}"
readonly THREAD_VERSION="${THREAD_VERSION:-1.1}"
readonly INTER_OP="${INTER_OP:-0}"
readonly VERBOSE="${VERBOSE:-0}"
readonly BORDER_ROUTING="${BORDER_ROUTING:-1}"
build_simulation()
{
@@ -231,9 +232,13 @@ do_build_otbr_docker()
echo "Building OTBR Docker ..."
local otdir
local otbrdir
local otbr_options="-DOT_DUA=ON -DOT_MLR=ON -DOT_COVERAGE=ON -DOTBR_REST=OFF -DOTBR_WEB=OFF"
local otbr_options="-DOT_SLAAC=ON -DOT_DUA=ON -DOT_MLR=ON -DOT_COVERAGE=ON -DOTBR_REST=OFF -DOTBR_WEB=OFF"
local otbr_docker_image=${OTBR_DOCKER_IMAGE:-otbr-ot12-backbone-ci}
if [[ ${BORDER_ROUTING} == "1" ]]; then
otbr_options="${otbr_options} -DOT_BORDER_ROUTING=ON"
fi
otbrdir=$(mktemp -d -t otbr_XXXXXX)
otdir=$(pwd)
+10
View File
@@ -74,6 +74,10 @@ if (openthread_enable_core_config_args) {
defines += [ "OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE=1" ]
}
if (openthread_config_border_routing_enable) {
defines += [ "OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE=1" ]
}
if (openthread_external_mbedtls != "") {
defines += [ "OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS=0" ]
} else if (!openthread_config_enable_builtin_mbedtls_management) {
@@ -234,6 +238,7 @@ if (openthread_enable_core_config_args) {
"OPENTHREAD_CONFIG_LOG_API=1",
"OPENTHREAD_CONFIG_LOG_ARP=1",
"OPENTHREAD_CONFIG_LOG_BBR=1",
"OPENTHREAD_CONFIG_LOG_BR=1",
"OPENTHREAD_CONFIG_LOG_CLI=1",
"OPENTHREAD_CONFIG_LOG_COAP=1",
"OPENTHREAD_CONFIG_LOG_DUA=1",
@@ -342,6 +347,11 @@ openthread_core_files = [
"backbone_router/multicast_listeners_table.hpp",
"backbone_router/ndproxy_table.cpp",
"backbone_router/ndproxy_table.hpp",
"border_router/infra_if_platform.cpp",
"border_router/router_advertisement.cpp",
"border_router/router_advertisement.hpp",
"border_router/routing_manager.cpp",
"border_router/routing_manager.hpp",
"coap/coap.cpp",
"coap/coap.hpp",
"coap/coap_message.cpp",
+3
View File
@@ -76,6 +76,9 @@ set(COMMON_SOURCES
backbone_router/bbr_manager.cpp
backbone_router/multicast_listeners_table.cpp
backbone_router/ndproxy_table.cpp
border_router/infra_if_platform.cpp
border_router/router_advertisement.cpp
border_router/routing_manager.cpp
coap/coap.cpp
coap/coap_message.cpp
coap/coap_secure.cpp
+5
View File
@@ -153,6 +153,9 @@ SOURCES_COMMON = \
backbone_router/bbr_manager.cpp \
backbone_router/multicast_listeners_table.cpp \
backbone_router/ndproxy_table.cpp \
border_router/infra_if_platform.cpp \
border_router/router_advertisement.cpp \
border_router/routing_manager.cpp \
coap/coap.cpp \
coap/coap_message.cpp \
coap/coap_secure.cpp \
@@ -346,6 +349,8 @@ HEADERS_COMMON = \
backbone_router/bbr_manager.hpp \
backbone_router/multicast_listeners_table.hpp \
backbone_router/ndproxy_table.hpp \
border_router/router_advertisement.hpp \
border_router/routing_manager.hpp \
coap/coap.hpp \
coap/coap_message.hpp \
coap/coap_secure.hpp \
+10
View File
@@ -37,11 +37,21 @@
#include <openthread/border_router.h>
#include "border_router/routing_manager.hpp"
#include "common/debug.hpp"
#include "common/instance.hpp"
using namespace ot;
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
otError otBorderRoutingInit(otInstance *aInstance, uint32_t aInfraIfIndex)
{
Instance &instance = *static_cast<Instance *>(aInstance);
return instance.Get<BorderRouter::RoutingManager>().Init(aInfraIfIndex);
}
#endif
otError otBorderRouterGetNetData(otInstance *aInstance, bool aStable, uint8_t *aData, uint8_t *aDataLength)
{
Instance &instance = *static_cast<Instance *>(aInstance);
@@ -0,0 +1,55 @@
/*
* 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.
*/
/**
* @file
* This file implements infrastructure interface platform APIs.
*/
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#include <openthread/platform/infra_if.h>
#include "border_router/routing_manager.hpp"
#include "common/instance.hpp"
using namespace ot;
extern "C" void otPlatInfraIfRecvIcmp6Nd(otInstance * aInstance,
uint32_t aInfraIfIndex,
const otIp6Address *aSrcAddress,
const uint8_t * aBuffer,
uint16_t aBufferLength)
{
Instance &instance = *static_cast<Instance *>(aInstance);
instance.Get<BorderRouter::RoutingManager>().RecvIcmp6Message(
aInfraIfIndex, static_cast<const Ip6::Address &>(*aSrcAddress), aBuffer, aBufferLength);
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
@@ -0,0 +1,164 @@
/*
* 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.
*/
/**
* @file
* This file includes implementations for ICMPv6 Router Advertisement.
*
*/
#include "border_router/router_advertisement.hpp"
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
namespace ot {
namespace BorderRouter {
namespace RouterAdv {
const Option *Option::GetNextOption(const Option *aCurOption, const uint8_t *aBuffer, uint16_t aBufferLength)
{
const uint8_t *nextOption = nullptr;
const uint8_t *bufferEnd = aBuffer + aBufferLength;
VerifyOrExit(aBuffer != nullptr, nextOption = nullptr);
if (aCurOption == nullptr)
{
nextOption = aBuffer;
}
else
{
nextOption = reinterpret_cast<const uint8_t *>(aCurOption) + aCurOption->GetLength();
}
VerifyOrExit(nextOption + sizeof(Option) <= bufferEnd, nextOption = nullptr);
VerifyOrExit(reinterpret_cast<const Option *>(nextOption)->GetLength() > 0, nextOption = nullptr);
VerifyOrExit(nextOption + reinterpret_cast<const Option *>(nextOption)->GetLength() <= bufferEnd,
nextOption = nullptr);
exit:
return reinterpret_cast<const Option *>(nextOption);
}
PrefixInfoOption::PrefixInfoOption(void)
: Option(Type::kPrefixInfo, sizeof(*this) / kLengthUnit)
, mPrefixLength(0)
, mReserved1(0)
, mValidLifetime(0)
, mPreferredLifetime(0)
, mReserved2(0)
{
OT_UNUSED_VARIABLE(mReserved2);
mPrefix.Clear();
}
void PrefixInfoOption::SetOnLink(bool aOnLink)
{
if (aOnLink)
{
mReserved1 |= kOnLinkFlagMask;
}
else
{
mReserved1 &= ~kOnLinkFlagMask;
}
}
void PrefixInfoOption::SetAutoAddrConfig(bool aAutoAddrConfig)
{
if (aAutoAddrConfig)
{
mReserved1 |= kAutoConfigFlagMask;
}
else
{
mReserved1 &= ~kAutoConfigFlagMask;
}
}
void PrefixInfoOption::SetPrefix(const Ip6::Prefix &aPrefix)
{
mPrefixLength = aPrefix.mLength;
mPrefix = static_cast<const Ip6::Address &>(aPrefix.mPrefix);
}
void PrefixInfoOption::GetPrefix(Ip6::Prefix &aPrefix) const
{
aPrefix.Set(mPrefix.GetBytes(), mPrefixLength);
}
RouteInfoOption::RouteInfoOption(void)
: Option(Type::kRouteInfo, 0)
, mPrefixLength(0)
, mReserved(0)
, mRouteLifetime(0)
{
OT_UNUSED_VARIABLE(mReserved);
mPrefix.Clear();
}
void RouteInfoOption::SetPrefix(const Ip6::Prefix &aPrefix)
{
// The total length (in bytes) of a Router Information Option
// is: (8 bytes fixed option header) + (0, 8, or 16 bytes prefix).
// Because the length of the option must be padded with 8 bytes,
// the length of the prefix (in bits) must be padded with 64 bits.
SetLength(((aPrefix.mLength + kLengthUnit * CHAR_BIT - 1) / (kLengthUnit * CHAR_BIT) + 1) * kLengthUnit);
mPrefixLength = aPrefix.mLength;
mPrefix = static_cast<const Ip6::Address &>(aPrefix.mPrefix);
}
RouterAdvMessage::RouterAdvMessage(void)
: mReachableTime(0)
, mRetransTimer(0)
{
OT_UNUSED_VARIABLE(mReachableTime);
OT_UNUSED_VARIABLE(mRetransTimer);
mHeader.Clear();
mHeader.SetType(Ip6::Icmp::Header::kTypeRouterAdvert);
}
RouterSolicitMessage::RouterSolicitMessage(void)
{
mHeader.Clear();
mHeader.SetType(Ip6::Icmp::Header::kTypeRouterSolicit);
}
} // namespace RouterAdv
} // namespace BorderRouter
} // namespace ot
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
@@ -0,0 +1,382 @@
/*
* 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.
*/
/**
* @file
* This file includes definitions for IPv6 Router Advertisement.
*
* See RFC 4861: Neighbor Discovery for IP version 6 (https://tools.ietf.org/html/rfc4861).
*
*/
#ifndef ROUTER_ADVERTISEMENT_HPP_
#define ROUTER_ADVERTISEMENT_HPP_
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#include <stdint.h>
#include <openthread/platform/toolchain.h>
#include "common/encoding.hpp"
#include "net/icmp6.hpp"
#include "net/ip6.hpp"
using ot::Encoding::BigEndian::HostSwap16;
using ot::Encoding::BigEndian::HostSwap32;
namespace ot {
namespace BorderRouter {
namespace RouterAdv {
/**
* This class represents the variable length options in Neighbor
* Discovery messages.
*
* @sa PrefixInfoOption
* @sa RouteInfoOption
*
*/
OT_TOOL_PACKED_BEGIN
class Option
{
public:
enum class Type : uint8_t
{
kPrefixInfo = 3, ///< Prefix Information Option.
kRouteInfo = 24, ///< Route Information Option.
};
enum : uint8_t
{
kLengthUnit = 8u, ///< The unit of length in octets.
};
/**
* This constructor initializes the option with given type and length.
*
* @param[in] aType The type of this option.
* @param[in] aLength The length of this option in unit of 8 octets.
*
*/
explicit Option(Type aType, uint8_t aLength = 0)
: mType(aType)
, mLength(aLength)
{
}
/**
* This method returns the type of this option.
*
* @returns The option type.
*
*/
Type GetType(void) const { return mType; }
/**
* This method sets the length of the option (in bytes).
*
* Since the option must end on their natural 64-bits boundaries,
* the actual length set to the option is padded to (aLength + 7) / 8 * 8.
*
* @param[in] aLength The length of the option in unit of 1 byte.
*
*/
void SetLength(uint16_t aLength) { mLength = (aLength + kLengthUnit - 1) / kLengthUnit; }
/**
* This method returns the length of the option (in bytes).
*
* @returns The length of the option.
*
*/
uint16_t GetLength(void) const { return mLength * 8; }
/**
* This helper method returns a pointer to the next valid option in the buffer.
*
* @param[in] aCurOption The current option. Use nullptr to get the first option.
* @param[in] aBuffer The buffer within which the options are held.
* @param[in] aBufferLength The length of the buffer.
*
* @returns A pointer to the next option if there are a valid one. Otherwise, nullptr.
*
*/
static const Option *GetNextOption(const Option *aCurOption, const uint8_t *aBuffer, uint16_t aBufferLength);
/**
* This method tells whether this option is valid.
*
* @return A boolean that indicates whether this option is valid.
*
*/
bool IsValid(void) const { return mLength > 0; }
private:
Type mType; // Type of the option.
uint8_t mLength; // Length of the option in unit of 8 octets,
// including the `type` and `length` fields.
} OT_TOOL_PACKED_END;
/**
* This class represents the Prefix Information Option.
*
* See section 4.6.2 of RFC 4861 for definition of this option.
* https://tools.ietf.org/html/rfc4861#section-4.6.2
*
*/
OT_TOOL_PACKED_BEGIN
class PrefixInfoOption : public Option
{
public:
/**
* This constructor initializes this option with zero prefix
* length, valid lifetime and preferred lifetime.
*
*/
PrefixInfoOption(void);
/**
* This method sets the on-link (L) flag.
*
* @param[in] aOnLink A boolean indicates whether the prefix is on-link or off-link.
*
*/
void SetOnLink(bool aOnLink);
/**
* This method sets the autonomous address-configuration (A) flag.
*
* @param[in] aAutoAddrConfig A boolean indicates whether this prefix can be used
* for SLAAC.
*
*/
void SetAutoAddrConfig(bool aAutoAddrConfig);
/**
* This method set the valid lifetime of the prefix in seconds.
*
* @param[in] aValidLifetime The valid lifetime in seconds.
*
*/
void SetValidLifetime(uint32_t aValidLifetime) { mValidLifetime = HostSwap32(aValidLifetime); }
/**
* THis method returns the valid lifetime of the prefix in seconds.
*
* @returns The valid lifetime in seconds.
*
*/
uint32_t GetValidLifetime(void) const { return HostSwap32(mValidLifetime); }
/**
* This method sets the preferred lifetime of the prefix in seconds.
*
* @param[in] aPreferredLifetime The preferred lifetime in seconds.
*
*/
void SetPreferredLifetime(uint32_t aPreferredLifetime) { mPreferredLifetime = HostSwap32(aPreferredLifetime); }
/**
* This method sets the prefix.
*
* @param[in] aPrefix The prefix contained in this option.
*
*/
void SetPrefix(const Ip6::Prefix &aPrefix);
/**
* THis method returns the prefix in this option.
*
* @param[out] aPrefix The prefix to output to.
*
*/
void GetPrefix(Ip6::Prefix &aPrefix) const;
/**
* This method tells whether this option is valid.
*
* @returns A boolean indicates whether this option is valid.
*
*/
bool IsValid(void) const
{
return (GetLength() == sizeof(*this)) && (mPrefixLength <= OT_IP6_ADDRESS_SIZE * CHAR_BIT);
}
private:
enum : uint8_t
{
kAutoConfigFlagMask = 0x40u, // Bit mask of the Automatic Address Configure flag.
kOnLinkFlagMask = 0x80u, // Bit mask of the On-link flag.
};
uint8_t mPrefixLength; // The prefix length in bits.
uint8_t mReserved1; // The reserved field.
uint32_t mValidLifetime; // The valid lifetime of the prefix.
uint32_t mPreferredLifetime; // The preferred lifetime of the prefix.
uint32_t mReserved2; // The reserved field.
Ip6::Address mPrefix; // The prefix.
} OT_TOOL_PACKED_END;
static_assert(sizeof(PrefixInfoOption) == 32, "invalid PrefixInfoOption structure");
/**
* This class represents the Route Information Option.
*
* See section 2.3 of RFC 4191 for definition of this option.
* https://tools.ietf.org/html/rfc4191#section-2.3
*
*/
OT_TOOL_PACKED_BEGIN
class RouteInfoOption : public Option
{
public:
/**
* This constructor initializes this option with zero prefix length.
*
*/
RouteInfoOption(void);
/**
* This method sets the lifetime of the route in seconds.
*
* @param[in] aLifetime The lifetime of the route in seconds.
*
*/
void SetRouteLifetime(uint32_t aLifetime) { mRouteLifetime = HostSwap32(aLifetime); }
/**
* This method sets the prefix.
*
* @param[in] aPrefix The prefix contained in this option.
*
*/
void SetPrefix(const Ip6::Prefix &aPrefix);
/**
* This method tells whether this option is valid.
*
* @returns A boolean indicates whether this option is valid.
*
*/
bool IsValid(void) const
{
return (GetLength() == kLengthUnit || GetLength() == 2 * kLengthUnit || GetLength() == 3 * kLengthUnit) &&
(mPrefixLength <= OT_IP6_ADDRESS_SIZE * CHAR_BIT);
}
private:
uint8_t mPrefixLength; // The prefix length in bits.
uint8_t mReserved; // The reserved field.
uint32_t mRouteLifetime; // The lifetime in seconds.
Ip6::Address mPrefix; // The prefix.
} OT_TOOL_PACKED_END;
static_assert(sizeof(RouteInfoOption) == 24, "invalid RouteInfoOption structure");
/**
* This class implements the Router Advertisement message.
*
* See section 4.2 of RFC 4861 for definition of this message.
* https://tools.ietf.org/html/rfc4861#section-4.2
*
*/
OT_TOOL_PACKED_BEGIN
class RouterAdvMessage
{
public:
/**
* This constructor initializes the Router Advertisement message with
* zero router lifetime, reachable time and retransmission timer.
*
*/
RouterAdvMessage(void);
/**
* This method sets the Router Lifetime in seconds.
*
* Zero Router Lifetime means we are not a default router.
*
* @param[in] aRouterLifetime The router lifetime in seconds.
*
*/
void SetRouterLifetime(uint16_t aRouterLifetime)
{
mHeader.mData.m16[kRouteLifetimeIdx] = HostSwap16(aRouterLifetime);
}
private:
enum : uint8_t
{
kRouteLifetimeIdx = 1u, // The index of Route Lifetime in ICMPv6 Header Data. in unit of 2 octets.
};
Ip6::Icmp::Header mHeader; // The common ICMPv6 header.
uint32_t mReachableTime; // The reachable time. In milliseconds.
uint32_t mRetransTimer; // The retransmission timer. In milliseconds.
} OT_TOOL_PACKED_END;
static_assert(sizeof(RouterAdvMessage) == 16, "invalid RouterAdvMessage structure");
/**
* This class implements the Router Solicitation message.
*
* See section 4.1 of RFC 4861 for definition of this message.
* https://tools.ietf.org/html/rfc4861#section-4.1
*
*/
OT_TOOL_PACKED_BEGIN
class RouterSolicitMessage
{
public:
/**
* This constructor initializes the Router Solicitation message.
*
*/
RouterSolicitMessage(void);
private:
Ip6::Icmp::Header mHeader; // The common ICMPv6 header.
} OT_TOOL_PACKED_END;
static_assert(sizeof(RouterSolicitMessage) == 8, "invalid RouterSolicitMessage structure");
} // namespace RouterAdv
} // namespace BorderRouter
} // namespace ot
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#endif // ROUTER_ADVERTISEMENT_HPP_
+795
View File
@@ -0,0 +1,795 @@
/*
* 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.
*/
/**
* @file
* This file includes implementation for the RA-based routing management.
*
*/
#include "border_router/routing_manager.hpp"
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#include <string.h>
#include <openthread/platform/infra_if.h>
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/instance.hpp"
#include "common/locator-getters.hpp"
#include "common/logging.hpp"
#include "common/random.hpp"
#include "common/settings.hpp"
#include "net/ip6.hpp"
#include "thread/network_data.hpp"
#include "thread/network_data_leader.hpp"
#include "thread/network_data_local.hpp"
#include "thread/network_data_notifier.hpp"
namespace ot {
namespace BorderRouter {
RoutingManager::RoutingManager(Instance &aInstance)
: InstanceLocator(aInstance)
, mIsRunning(false)
, mInfraIfIndex(0)
, mAdvertisedOmrPrefixNum(0)
, mAdvertisedOnLinkPrefix(nullptr)
, mRouterAdvertisementTimer(aInstance, HandleRouterAdvertisementTimer, this)
, mRouterAdvertisementCount(0)
, mRouterSolicitTimer(aInstance, HandleRouterSolicitTimer, this)
, mRouterSolicitCount(0)
, mDiscoveredOnLinkPrefixInvalidTimer(aInstance, HandleDiscoveredOnLinkPrefixInvalidTimer, this)
{
mLocalOmrPrefix.Clear();
memset(mAdvertisedOmrPrefixes, 0, sizeof(mAdvertisedOmrPrefixes));
mLocalOnLinkPrefix.Clear();
mDiscoveredOnLinkPrefix.Clear();
}
otError RoutingManager::Init(uint32_t aInfraIfIndex)
{
otError error;
OT_ASSERT(!IsInitialized() && !Get<Mle::MleRouter>().IsAttached());
VerifyOrExit(aInfraIfIndex > 0, error = OT_ERROR_INVALID_ARGS);
SuccessOrExit(error = LoadOrGenerateRandomOmrPrefix());
SuccessOrExit(error = LoadOrGenerateRandomOnLinkPrefix());
mInfraIfIndex = aInfraIfIndex;
exit:
return error;
}
otError RoutingManager::LoadOrGenerateRandomOmrPrefix(void)
{
otError error = OT_ERROR_NONE;
if (Get<Settings>().ReadOmrPrefix(mLocalOmrPrefix) != OT_ERROR_NONE || !IsValidOmrPrefix(mLocalOmrPrefix))
{
Ip6::NetworkPrefix randomOmrPrefix;
otLogNoteBr("no valid OMR prefix found in settings, generating new one");
error = randomOmrPrefix.GenerateRandomUla();
if (error != OT_ERROR_NONE)
{
otLogCritBr("failed to generate random OMR prefix");
ExitNow();
}
mLocalOmrPrefix.Set(randomOmrPrefix);
IgnoreError(Get<Settings>().SaveOmrPrefix(mLocalOmrPrefix));
}
exit:
return error;
}
otError RoutingManager::LoadOrGenerateRandomOnLinkPrefix(void)
{
otError error = OT_ERROR_NONE;
if (Get<Settings>().ReadOnLinkPrefix(mLocalOnLinkPrefix) != OT_ERROR_NONE ||
!IsValidOnLinkPrefix(mLocalOnLinkPrefix))
{
Ip6::NetworkPrefix randomOnLinkPrefix;
otLogNoteBr("no valid on-link prefix found in settings, generating new one");
error = randomOnLinkPrefix.GenerateRandomUla();
if (error != OT_ERROR_NONE)
{
otLogCritBr("failed to generate random on-link prefix");
ExitNow();
}
randomOnLinkPrefix.m8[6] = 0;
randomOnLinkPrefix.m8[7] = 0;
mLocalOnLinkPrefix.Set(randomOnLinkPrefix);
IgnoreError(Get<Settings>().SaveOnLinkPrefix(mLocalOnLinkPrefix));
}
exit:
return error;
}
void RoutingManager::Start(void)
{
if (!mIsRunning)
{
mIsRunning = true;
StartRouterSolicitation();
}
}
void RoutingManager::Stop(void)
{
VerifyOrExit(mIsRunning);
UnpublishLocalOmrPrefix();
// Use empty OMR & on-link prefixes to invalidate possible advertised prefixes.
SendRouterAdvertisement(nullptr, 0, nullptr);
memset(mAdvertisedOmrPrefixes, 0, sizeof(mAdvertisedOmrPrefixes));
mAdvertisedOmrPrefixNum = 0;
mAdvertisedOnLinkPrefix = nullptr;
mDiscoveredOnLinkPrefix.Clear();
mDiscoveredOnLinkPrefixInvalidTimer.Stop();
mRouterAdvertisementTimer.Stop();
mRouterAdvertisementCount = 0;
mRouterSolicitTimer.Stop();
mRouterSolicitCount = 0;
mIsRunning = false;
exit:
return;
}
void RoutingManager::RecvIcmp6Message(uint32_t aInfraIfIndex,
const Ip6::Address &aSrcAddress,
const uint8_t * aBuffer,
uint16_t aBufferLength)
{
const Ip6::Icmp::Header *icmp6Header;
VerifyOrExit(mIsRunning);
VerifyOrExit(aInfraIfIndex == mInfraIfIndex);
VerifyOrExit(aBuffer != nullptr && aBufferLength >= sizeof(*icmp6Header));
icmp6Header = reinterpret_cast<const Ip6::Icmp::Header *>(aBuffer);
switch (icmp6Header->GetType())
{
case Ip6::Icmp::Header::kTypeRouterAdvert:
HandleRouterAdvertisement(aSrcAddress, aBuffer, aBufferLength);
break;
case Ip6::Icmp::Header::kTypeRouterSolicit:
HandleRouterSolicit(aSrcAddress, aBuffer, aBufferLength);
break;
default:
break;
}
exit:
return;
}
void RoutingManager::HandleNotifierEvents(Events aEvents)
{
VerifyOrExit(IsInitialized());
if (aEvents.Contains(kEventThreadRoleChanged))
{
if (Get<Mle::MleRouter>().IsAttached())
{
Start();
}
else
{
Stop();
}
}
if (aEvents.Contains(kEventThreadNetdataChanged))
{
EvaluateRoutingPolicy();
}
exit:
return;
}
uint8_t RoutingManager::EvaluateOmrPrefix(Ip6::Prefix *aNewOmrPrefixes, uint8_t aMaxOmrPrefixNum)
{
uint8_t newOmrPrefixNum = 0;
NetworkData::Iterator iterator = NetworkData::kIteratorInit;
NetworkData::OnMeshPrefixConfig onMeshPrefixConfig;
Ip6::Prefix * smallestOmrPrefix = nullptr;
Ip6::Prefix * publishedLocalOmrPrefix = nullptr;
OT_ASSERT(mIsRunning);
while (Get<NetworkData::Leader>().GetNextOnMeshPrefix(iterator, onMeshPrefixConfig) == OT_ERROR_NONE)
{
uint8_t newPrefixIndex;
if (!IsValidOmrPrefix(onMeshPrefixConfig.GetPrefix()) || !onMeshPrefixConfig.mDefaultRoute ||
!onMeshPrefixConfig.mSlaac || onMeshPrefixConfig.mDp)
{
continue;
}
newPrefixIndex = 0;
while (newPrefixIndex < newOmrPrefixNum && onMeshPrefixConfig.GetPrefix() != aNewOmrPrefixes[newPrefixIndex])
{
++newPrefixIndex;
}
if (newPrefixIndex != newOmrPrefixNum)
{
// Ignore duplicate prefixes.
continue;
}
if (newOmrPrefixNum >= aMaxOmrPrefixNum)
{
otLogWarnBr("EvaluateOmrPrefix: too many OMR prefixes, ignoring prefix %s",
onMeshPrefixConfig.GetPrefix().ToString().AsCString());
continue;
}
aNewOmrPrefixes[newOmrPrefixNum] = onMeshPrefixConfig.GetPrefix();
if (smallestOmrPrefix == nullptr || IsPrefixSmallerThan(onMeshPrefixConfig.GetPrefix(), *smallestOmrPrefix))
{
smallestOmrPrefix = &aNewOmrPrefixes[newOmrPrefixNum];
}
if (aNewOmrPrefixes[newOmrPrefixNum] == mLocalOmrPrefix)
{
publishedLocalOmrPrefix = &aNewOmrPrefixes[newOmrPrefixNum];
}
++newOmrPrefixNum;
}
// Decide if we need to add or remove my local OMR prefix.
if (newOmrPrefixNum == 0)
{
otLogInfoBr("EvaluateOmrPrefix: no valid OMR prefixes found in Thread network");
if (PublishLocalOmrPrefix() == OT_ERROR_NONE)
{
aNewOmrPrefixes[newOmrPrefixNum++] = mLocalOmrPrefix;
}
// The `newOmrPrefixNum` is zero when we failed to publish the local OMR prefix.
}
else if (publishedLocalOmrPrefix != nullptr && smallestOmrPrefix != publishedLocalOmrPrefix)
{
otLogInfoBr("EvaluateOmrPrefix: there is already a smaller OMR prefix %s in the Thread network",
smallestOmrPrefix->ToString().AsCString());
UnpublishLocalOmrPrefix();
// Remove the local OMR prefix from the list by overwriting it with the last one.
*publishedLocalOmrPrefix = aNewOmrPrefixes[--newOmrPrefixNum];
}
return newOmrPrefixNum;
}
otError RoutingManager::PublishLocalOmrPrefix(void)
{
otError error = OT_ERROR_NONE;
NetworkData::OnMeshPrefixConfig omrPrefixConfig;
OT_ASSERT(mIsRunning);
omrPrefixConfig.Clear();
omrPrefixConfig.mPrefix = mLocalOmrPrefix;
omrPrefixConfig.mStable = true;
omrPrefixConfig.mSlaac = true;
omrPrefixConfig.mPreferred = true;
omrPrefixConfig.mOnMesh = true;
omrPrefixConfig.mDefaultRoute = true;
error = Get<NetworkData::Local>().AddOnMeshPrefix(omrPrefixConfig);
if (error != OT_ERROR_NONE)
{
otLogInfoBr("failed to publish local OMR prefix %s in Thread network: %s",
mLocalOmrPrefix.ToString().AsCString(), otThreadErrorToString(error));
}
else
{
Get<NetworkData::Notifier>().HandleServerDataUpdated();
otLogInfoBr("published local OMR prefix %s in Thread network", mLocalOmrPrefix.ToString().AsCString());
}
return error;
}
void RoutingManager::UnpublishLocalOmrPrefix(void)
{
VerifyOrExit(mIsRunning);
IgnoreError(Get<NetworkData::Local>().RemoveOnMeshPrefix(mLocalOmrPrefix));
Get<NetworkData::Notifier>().HandleServerDataUpdated();
otLogInfoBr("unpubished local OMR prefix %s from Thread network", mLocalOmrPrefix.ToString().AsCString());
exit:
return;
}
bool RoutingManager::ContainsPrefix(const Ip6::Prefix &aPrefix, const Ip6::Prefix *aPrefixList, uint8_t aPrefixNum)
{
bool ret = false;
for (uint8_t i = 0; i < aPrefixNum; ++i)
{
if (aPrefixList[i] == aPrefix)
{
ret = true;
break;
}
}
return ret;
}
const Ip6::Prefix *RoutingManager::EvaluateOnLinkPrefix(void) const
{
const Ip6::Prefix *newOnLinkPrefix = nullptr;
// We don't evaluate on-link prefix if we are doing
// Router Discovery or we have already discovered some
// on-link prefixes.
VerifyOrExit(!mRouterSolicitTimer.IsRunning());
if (IsValidOnLinkPrefix(mDiscoveredOnLinkPrefix))
{
otLogInfoBr("EvaluateOnLinkPrefix: there is already on-link prefix %s on interface %u",
mDiscoveredOnLinkPrefix.ToString().AsCString(), mInfraIfIndex);
ExitNow();
}
newOnLinkPrefix = &mLocalOnLinkPrefix;
exit:
return newOnLinkPrefix;
}
void RoutingManager::EvaluateRoutingPolicy(void)
{
const Ip6::Prefix *newOnLinkPrefix = nullptr;
Ip6::Prefix newOmrPrefixes[kMaxOmrPrefixNum];
uint8_t newOmrPrefixNum = 0;
VerifyOrExit(mIsRunning);
otLogInfoBr("evaluating routing policy");
// 0. Evaluate on-link & OMR prefixes.
newOnLinkPrefix = EvaluateOnLinkPrefix();
newOmrPrefixNum = EvaluateOmrPrefix(newOmrPrefixes, kMaxOmrPrefixNum);
// 1. Send Router Advertisement message if necessary.
SendRouterAdvertisement(newOmrPrefixes, newOmrPrefixNum, newOnLinkPrefix);
if (newOmrPrefixNum == 0)
{
// This is the very exceptional case and happens only when we failed to publish
// our local OMR prefix to the Thread network. We schedule the Router Advertisement
// timer to re-evaluate our routing policy in the future.
otLogWarnBr("no OMR prefix advertised! Start Router Advertisement timer for future evaluation");
}
// 2. Schedule Router Advertisement timer with random interval.
{
uint32_t nextSendTime;
nextSendTime = Random::NonCrypto::GetUint32InRange(kMinRtrAdvInterval, kMaxRtrAdvInterval);
if (mRouterAdvertisementCount <= kMaxInitRtrAdvertisements && nextSendTime > kMaxInitRtrAdvInterval)
{
nextSendTime = kMaxInitRtrAdvInterval;
}
otLogInfoBr("router advertisement scheduled in %u seconds", nextSendTime);
mRouterAdvertisementTimer.Start(nextSendTime * 1000);
}
// 3. Update advertised on-link & OMR prefixes information.
mAdvertisedOnLinkPrefix = newOnLinkPrefix;
static_assert(sizeof(mAdvertisedOmrPrefixes) == sizeof(newOmrPrefixes), "invalid new OMR prefix array size");
memcpy(mAdvertisedOmrPrefixes, newOmrPrefixes, sizeof(newOmrPrefixes[0]) * newOmrPrefixNum);
mAdvertisedOmrPrefixNum = newOmrPrefixNum;
exit:
return;
}
void RoutingManager::StartRouterSolicitation(void)
{
mRouterSolicitCount = 0;
mRouterSolicitTimer.Start(Random::NonCrypto::GetUint32InRange(0, kMaxRtrSolicitationDelay * 1000));
}
otError RoutingManager::SendRouterSolicitation(void)
{
Ip6::Address destAddress;
RouterAdv::RouterSolicitMessage routerSolicit;
OT_ASSERT(IsInitialized());
destAddress.SetToLinkLocalAllRoutersMulticast();
return otPlatInfraIfSendIcmp6Nd(mInfraIfIndex, &destAddress, reinterpret_cast<const uint8_t *>(&routerSolicit),
sizeof(routerSolicit));
}
void RoutingManager::SendRouterAdvertisement(const Ip6::Prefix *aNewOmrPrefixes,
uint8_t aNewOmrPrefixNum,
const Ip6::Prefix *aNewOnLinkPrefix)
{
uint8_t buffer[kMaxRouterAdvMessageLength];
uint16_t bufferLength = 0;
RouterAdv::RouterAdvMessage routerAdv;
// Set zero Router Lifetime to indicate that the Border Router is not the default
// router for infra link so that hosts on infra link will not create default route
// to the Border Router when received RA.
routerAdv.SetRouterLifetime(0);
OT_ASSERT(bufferLength + sizeof(routerAdv) <= sizeof(buffer));
memcpy(buffer, &routerAdv, sizeof(routerAdv));
bufferLength += sizeof(routerAdv);
if (aNewOnLinkPrefix != nullptr)
{
RouterAdv::PrefixInfoOption pio;
pio.SetOnLink(true);
pio.SetAutoAddrConfig(true);
pio.SetValidLifetime(kDefaultOnLinkPrefixLifetime);
pio.SetPreferredLifetime(kDefaultOnLinkPrefixLifetime);
pio.SetPrefix(*aNewOnLinkPrefix);
OT_ASSERT(bufferLength + pio.GetLength() <= sizeof(buffer));
memcpy(buffer + bufferLength, &pio, pio.GetLength());
bufferLength += pio.GetLength();
if (mAdvertisedOnLinkPrefix != nullptr)
{
otLogInfoBr("start advertising new on-link prefix %s on interface %u",
aNewOnLinkPrefix->ToString().AsCString(), mInfraIfIndex);
}
otLogInfoBr("send on-link prefix %s in PIO (valid lifetime = %u seconds)",
aNewOnLinkPrefix->ToString().AsCString(), kDefaultOnLinkPrefixLifetime);
}
else if (mAdvertisedOnLinkPrefix != nullptr)
{
RouterAdv::PrefixInfoOption pio;
// Set zero valid lifetime to immediately invalidate the advertised on-link prefix.
pio.SetValidLifetime(0);
pio.SetPreferredLifetime(0);
pio.SetPrefix(*mAdvertisedOnLinkPrefix);
OT_ASSERT(bufferLength + pio.GetLength() <= sizeof(buffer));
memcpy(buffer + bufferLength, &pio, pio.GetLength());
bufferLength += pio.GetLength();
otLogInfoBr("stop advertising on-link prefix %s on interface %u",
mAdvertisedOnLinkPrefix->ToString().AsCString(), mInfraIfIndex);
}
// Invalidate the advertised OMR prefixes if they are no longer in the new OMR prefix array.
for (uint8_t i = 0; i < mAdvertisedOmrPrefixNum; ++i)
{
const Ip6::Prefix &advertisedOmrPrefix = mAdvertisedOmrPrefixes[i];
if (!ContainsPrefix(advertisedOmrPrefix, aNewOmrPrefixes, aNewOmrPrefixNum))
{
RouterAdv::RouteInfoOption rio;
// Set zero route lifetime to immediately invalidate the advertised OMR prefix.
rio.SetRouteLifetime(0);
rio.SetPrefix(advertisedOmrPrefix);
OT_ASSERT(bufferLength + rio.GetLength() <= sizeof(buffer));
memcpy(buffer + bufferLength, &rio, rio.GetLength());
bufferLength += rio.GetLength();
otLogInfoBr("stop advertising OMR prefix %s on interface %u", advertisedOmrPrefix.ToString().AsCString(),
mInfraIfIndex);
}
}
for (uint8_t i = 0; i < aNewOmrPrefixNum; ++i)
{
const Ip6::Prefix & newOmrPrefix = aNewOmrPrefixes[i];
RouterAdv::RouteInfoOption rio;
rio.SetRouteLifetime(kDefaultOmrPrefixLifetime);
rio.SetPrefix(newOmrPrefix);
OT_ASSERT(bufferLength + rio.GetLength() <= sizeof(buffer));
memcpy(buffer + bufferLength, &rio, rio.GetLength());
bufferLength += rio.GetLength();
otLogInfoBr("send OMR prefix %s in RIO (valid lifetime = %u seconds)", newOmrPrefix.ToString().AsCString(),
kDefaultOmrPrefixLifetime);
}
// Send the message only when there are options.
if (bufferLength > sizeof(routerAdv))
{
otError error;
Ip6::Address destAddress;
++mRouterAdvertisementCount;
destAddress.SetToLinkLocalAllNodesMulticast();
error = otPlatInfraIfSendIcmp6Nd(mInfraIfIndex, &destAddress, buffer, bufferLength);
if (error == OT_ERROR_NONE)
{
otLogInfoBr("sent Router Advertisement on interface %u", mInfraIfIndex);
}
else
{
otLogWarnBr("failed to send Router Advertisement on interface %u: %s", mInfraIfIndex,
otThreadErrorToString(error));
}
}
}
bool RoutingManager::IsPrefixSmallerThan(const Ip6::Prefix &aFirstPrefix, const Ip6::Prefix &aSecondPrefix)
{
uint8_t matchedLength;
OT_ASSERT(aFirstPrefix.GetLength() == aSecondPrefix.GetLength());
matchedLength =
Ip6::Prefix::MatchLength(aFirstPrefix.GetBytes(), aSecondPrefix.GetBytes(), aFirstPrefix.GetBytesSize());
return matchedLength < aFirstPrefix.GetLength() &&
aFirstPrefix.GetBytes()[matchedLength / CHAR_BIT] < aSecondPrefix.GetBytes()[matchedLength / CHAR_BIT];
}
bool RoutingManager::IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix)
{
// Accept ULA prefix with length of 64 bits and GUA prefix.
return (aOmrPrefix.mLength == kOmrPrefixLength && aOmrPrefix.mPrefix.mFields.m8[0] == 0xfd) ||
(aOmrPrefix.mLength >= 3 && (aOmrPrefix.GetBytes()[0] & 0xE0) == 0x20);
}
bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix)
{
// Accept ULA prefix with length of 64 bits and GUA prefix.
return (aOnLinkPrefix.mLength == kOnLinkPrefixLength && aOnLinkPrefix.mPrefix.mFields.m8[0] == 0xfd) ||
(aOnLinkPrefix.mLength >= 3 && (aOnLinkPrefix.GetBytes()[0] & 0xE0) == 0x20);
}
void RoutingManager::HandleRouterAdvertisementTimer(Timer &aTimer)
{
aTimer.GetOwner<RoutingManager>().HandleRouterAdvertisementTimer();
}
void RoutingManager::HandleRouterAdvertisementTimer(void)
{
otLogInfoBr("router advertisement timer triggered");
EvaluateRoutingPolicy();
}
void RoutingManager::HandleRouterSolicitTimer(Timer &aTimer)
{
aTimer.GetOwner<RoutingManager>().HandleRouterSolicitTimer();
}
void RoutingManager::HandleRouterSolicitTimer(void)
{
otLogInfoBr("router solicitation times out");
if (mRouterSolicitCount < kMaxRtrSolicitations)
{
uint32_t nextSolicitationDelay;
otError error;
error = SendRouterSolicitation();
++mRouterSolicitCount;
if (error == OT_ERROR_NONE)
{
otLogDebgBr("successfully sent %uth Router Solicitation", mRouterSolicitCount);
}
else
{
otLogCritBr("failed to send %uth Router Solicitation: %s", mRouterSolicitCount,
otThreadErrorToString(error));
}
nextSolicitationDelay =
(mRouterSolicitCount == kMaxRtrSolicitations) ? kMaxRtrSolicitationDelay : kRtrSolicitationInterval;
otLogDebgBr("router solicitation timer scheduled in %u seconds", nextSolicitationDelay);
mRouterSolicitTimer.Start(nextSolicitationDelay * 1000);
}
else
{
// Re-evaluate our routing policy and send Router Advertisement if necessary.
EvaluateRoutingPolicy();
}
}
void RoutingManager::HandleDiscoveredOnLinkPrefixInvalidTimer(Timer &aTimer)
{
aTimer.GetOwner<RoutingManager>().HandleDiscoveredOnLinkPrefixInvalidTimer();
}
void RoutingManager::HandleDiscoveredOnLinkPrefixInvalidTimer(void)
{
otLogInfoBr("invalidate discovered on-link prefix: %s", mDiscoveredOnLinkPrefix.ToString().AsCString());
mDiscoveredOnLinkPrefix.Clear();
// The discovered on-link prefix becomes invalid, start Router Solicitation
// to discover new one.
StartRouterSolicitation();
}
void RoutingManager::HandleRouterSolicit(const Ip6::Address &aSrcAddress,
const uint8_t * aBuffer,
uint16_t aBufferLength)
{
OT_UNUSED_VARIABLE(aSrcAddress);
OT_UNUSED_VARIABLE(aBuffer);
OT_UNUSED_VARIABLE(aBufferLength);
otLogInfoBr("received Router Solicitation from %s on interface %u", aSrcAddress.ToString().AsCString(),
mInfraIfIndex);
mRouterAdvertisementTimer.Start(Random::NonCrypto::GetUint32InRange(0, kMaxRaDelayTime));
}
uint32_t RoutingManager::GetPrefixExpireDelay(uint32_t aValidLifetime)
{
uint32_t delay;
if (aValidLifetime * static_cast<uint64_t>(1000) > Timer::kMaxDelay)
{
delay = Timer::kMaxDelay;
}
else
{
delay = aValidLifetime * 1000;
}
return delay;
}
void RoutingManager::HandleRouterAdvertisement(const Ip6::Address &aSrcAddress,
const uint8_t * aBuffer,
uint16_t aBufferLength)
{
OT_UNUSED_VARIABLE(aSrcAddress);
using RouterAdv::Option;
using RouterAdv::PrefixInfoOption;
using RouterAdv::RouterAdvMessage;
bool needReevaluate = false;
const uint8_t *optionsBegin;
uint16_t optionsLength;
const Option * option;
VerifyOrExit(aBufferLength >= sizeof(RouterAdvMessage));
otLogInfoBr("received Router Advertisement from %s on interface %u", aSrcAddress.ToString().AsCString(),
mInfraIfIndex);
optionsBegin = aBuffer + sizeof(RouterAdvMessage);
optionsLength = aBufferLength - sizeof(RouterAdvMessage);
option = nullptr;
while ((option = Option::GetNextOption(option, optionsBegin, optionsLength)) != nullptr)
{
const PrefixInfoOption *pio;
Ip6::Prefix prefix;
if (option->GetType() != Option::Type::kPrefixInfo)
{
continue;
}
pio = static_cast<const PrefixInfoOption *>(option);
if (!pio->IsValid())
{
continue;
}
pio->GetPrefix(prefix);
if (!IsValidOnLinkPrefix(prefix))
{
otLogInfoBr("ignore invalid prefix in PIO: %s", prefix.ToString().AsCString());
continue;
}
if (pio->GetValidLifetime() == 0)
{
if (mDiscoveredOnLinkPrefix == prefix)
{
otLogInfoBr("invalidate discovered on-link prefix %s", prefix.ToString().AsCString());
mDiscoveredOnLinkPrefixInvalidTimer.Stop();
mDiscoveredOnLinkPrefix.Clear();
needReevaluate = true;
}
}
else
{
otLogInfoBr("set discovered on-link prefix to %s, valid lifetime: %u seconds",
prefix.ToString().AsCString(), pio->GetValidLifetime());
// We keep tracking the latest on-link prefix.
mDiscoveredOnLinkPrefixInvalidTimer.Start(GetPrefixExpireDelay(pio->GetValidLifetime()));
mDiscoveredOnLinkPrefix = prefix;
// Stop Router Solicitation if we found a valid on-link prefix.
// Otherwise, we wait till the Router Solicitation process times out.
// So the maximum delay before the Border Router starts advertising
// its own on-link prefix is 9 (4 + 4 + 1) seconds.
mRouterSolicitTimer.Stop();
needReevaluate = true;
}
}
if (needReevaluate)
{
EvaluateRoutingPolicy();
}
exit:
return;
}
} // namespace BorderRouter
} // namespace ot
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+309
View File
@@ -0,0 +1,309 @@
/*
* 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.
*/
/**
* @file
* This file includes definitions for the RA-based routing management.
*
*/
#ifndef ROUTING_MANAGER_HPP_
#define ROUTING_MANAGER_HPP_
#include "openthread-core-config.h"
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#include <openthread/error.h>
#include <openthread/platform/infra_if.h>
#include "border_router/router_advertisement.hpp"
#include "common/locator.hpp"
#include "common/notifier.hpp"
#include "common/timer.hpp"
#include "net/ip6.hpp"
namespace ot {
namespace BorderRouter {
/**
* This class implements bi-directional routing between Thread and
* Infrastructure networks.
*
* The routing manager works on both Thread interface and infrastructure
* interface. All ICMPv6 messages are sent/recv on the infrastructure
* interface.
*
*/
class RoutingManager : public InstanceLocator
{
friend class ot::Notifier;
public:
/**
* This constructor initializes the routing manager.
*
* @param[in] aInstance A OpenThread instance.
*
*/
explicit RoutingManager(Instance &aInstance);
/**
* This method initializes the routing manager on given infrastructure interface.
*
* @param[in] aInfraIfIndex An infrastructure network interface index.
*
* @retval OT_ERROR_NONE Successfully started the routing manager.
* @retval OT_ERROR_INVALID_ARGS The index of the infra interface is not valid.
*
*/
otError Init(uint32_t aInfraIfIndex);
/**
* This method receives an ICMPv6 message on the infrastructure interface.
*
* Malformed or undesired messages are dropped silently.
*
* @param[in] aInfraIfIndex The infrastructure interface index.
* @param[in] aSrcAddress The source address this message is sent from.
* @param[in] aBuffer THe ICMPv6 message buffer.
* @param[in] aLength The length of the ICMPv6 message buffer.
*
*/
void RecvIcmp6Message(uint32_t aInfraIfIndex,
const Ip6::Address &aSrcAddress,
const uint8_t * aBuffer,
uint16_t aBufferLength);
private:
enum : uint16_t
{
kMaxRouterAdvMessageLength = 256u, // The maximum RA message length we can handle.
};
enum : uint8_t
{
kMaxOmrPrefixNum =
OPENTHREAD_CONFIG_IP6_SLAAC_NUM_ADDRESSES, // The maximum number of the OMR prefixes to advertise.
kOmrPrefixLength = OT_IP6_PREFIX_BITSIZE, // The length of an OMR prefix. In bits.
kOnLinkPrefixLength = OT_IP6_PREFIX_BITSIZE, // The length of an On-link prefix. In bits.
};
enum : uint32_t
{
kDefaultOmrPrefixLifetime = 1800u, // The default OMR prefix valid lifetime. In seconds.
kDefaultOnLinkPrefixLifetime = 1800u, // The default on-link prefix valid lifetime. In seconds.
kMaxRtrAdvInterval = 600, // Maximum Router Advertisement Interval. In seconds.
kMinRtrAdvInterval = kMaxRtrAdvInterval / 3, // Minimum Router Advertisement Interval. In seconds.
kMaxInitRtrAdvInterval = 16, // Maximum Initial Router Advertisement Interval. In seconds.
kMaxRaDelayTime = 500, // The maximum delay of sending RA after receiving RS. In milliseconds.
kRtrSolicitationInterval = 4, // The interval between Router Solicitations. In seconds.
kMaxRtrSolicitationDelay = 1, // The maximum delay for initial solicitation. In seconds.
};
static_assert(kMinRtrAdvInterval <= 3 * kMaxRtrAdvInterval / 4, "invalid RA intervals");
static_assert(kDefaultOmrPrefixLifetime >= kMaxRtrAdvInterval, "invalid default OMR prefix lifetime");
static_assert(kDefaultOnLinkPrefixLifetime >= kMaxRtrAdvInterval, "invalid default on-link prefix lifetime");
enum : uint32_t
{
kMaxInitRtrAdvertisements = 3, // The maximum number of initial Router Advertisements.
kMaxRtrSolicitations = 3, // The Maximum number of Router Solicitations before sending Router Advertisements.
};
void Start(void);
void Stop(void);
void HandleNotifierEvents(Events aEvents);
bool IsInitialized(void) const { return mInfraIfIndex != 0; }
otError LoadOrGenerateRandomOmrPrefix(void);
otError LoadOrGenerateRandomOnLinkPrefix(void);
/**
* This method tells whether the first prefix is numerically smaller than the second one.
*
* @note The caller must guarantee that the two prefix has the same length.
*
*/
static bool IsPrefixSmallerThan(const Ip6::Prefix &aFirstPrefix, const Ip6::Prefix &aSecondPrefix);
static bool IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix);
static bool IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix);
/**
* This method evaluate the routing policy depends on prefix and route
* information on Thread Network and infra link. As a result, this
* method May send RA messages on infra link and publish/unpublish
* OMR prefix in the Thread network.
*
* @sa EvaluateOmrPrefix
* @sa EvaluateOnLinkPrefix
* @sa PublishLocalOmrPrefix
* @sa UnpublishLocalOmrPrefix
*
*/
void EvaluateRoutingPolicy(void);
/**
* This method evaluates the OMR prefix for the Thread Network.
*
* @param[out] aNewOmrPrefixes An array of the new OMR prefixes should be advertised.
* MUST not be nullptr.
* @param[in] aMaxOmrPrefixNum The maximum number of OMR prefixes that @p aNewOmrPrefixes can hold.
*
* @returns The number of the new OMR prefixes that should be advertised after this evaluation.
*
*/
uint8_t EvaluateOmrPrefix(Ip6::Prefix *aNewOmrPrefixes, uint8_t aMaxOmrPrefixNum);
/**
* This method evaluates the on-link prefix for the infra link.
*
* @returns A pointer to the new on-link prefix should be advertised.
* nullptr if we should no longer advertise an on-link prefix.
*
*/
const Ip6::Prefix *EvaluateOnLinkPrefix(void) const;
/**
* This method publishes the local OMR prefix in Thread network.
*
*/
otError PublishLocalOmrPrefix(void);
/**
* This method unpublishes the local OMR prefix.
*
*/
void UnpublishLocalOmrPrefix(void);
/**
* This method starts sending Router Solicitations in random delay
* between 0 and kMaxRtrSolicitationDelay.
*
*/
void StartRouterSolicitation(void);
/**
* This method sends Router Solicitation messages to discover on-link
* prefix on infra links.
*
* @sa HandleRouterAdvertisement
*
* @retval OT_ERROR_NONE Successfully sent the message.
* @retval OT_ERROR_FAILED Failed to send the message.
*
*/
otError SendRouterSolicitation(void);
/**
* This method sends Router Advertisement messages to advertise
* on-link prefix and route for OMR prefix.
*
* @param[in] aNewOmrPrefixes A pointer to an array of the new OMR prefixes to be advertised.
* @p aNewOmrPrefixNum must be zero if this argument is nullptr.
* @param[in] aNewOmrPrefixNum The number of the new OMR prefixes to be advertised.
* Zero means we should stop advertising OMR prefixes.
* @param[in] aOnLinkPrefix A pointer to the new on-link prefix to be advertised.
* nullptr means we should stop advertising on-link prefix.
*
*/
void SendRouterAdvertisement(const Ip6::Prefix *aNewOmrPrefixes,
uint8_t aNewOmrPrefixNum,
const Ip6::Prefix *aNewOnLinkPrefix);
static void HandleRouterAdvertisementTimer(Timer &aTimer);
void HandleRouterAdvertisementTimer(void);
static void HandleRouterSolicitTimer(Timer &aTimer);
void HandleRouterSolicitTimer(void);
static void HandleDiscoveredOnLinkPrefixInvalidTimer(Timer &aTimer);
void HandleDiscoveredOnLinkPrefixInvalidTimer(void);
void HandleRouterSolicit(const Ip6::Address &aSrcAddress, const uint8_t *aBuffer, uint16_t aBufferLength);
void HandleRouterAdvertisement(const Ip6::Address &aSrcAddress, const uint8_t *aBuffer, uint16_t aBufferLength);
static bool ContainsPrefix(const Ip6::Prefix &aPrefix, const Ip6::Prefix *aPrefixList, uint8_t aPrefixNum);
static uint32_t GetPrefixExpireDelay(uint32_t aValidLifetime);
bool mIsRunning;
uint32_t mInfraIfIndex;
/**
* The OMR prefix loaded from local persistent storage or randomly generated
* if non is found in persistent storage.
*
*/
Ip6::Prefix mLocalOmrPrefix;
/**
* The advertised OMR prefixes.
*
*/
Ip6::Prefix mAdvertisedOmrPrefixes[kMaxOmrPrefixNum];
uint8_t mAdvertisedOmrPrefixNum;
/**
* The on-link prefix loaded from local persistent storage or randomly generated
* if non is found in persistent storage.
*
*/
Ip6::Prefix mLocalOnLinkPrefix;
/**
* The advertised on-link prefix.
*
* Could only be nullptr or a pointer to mLocalOnLinkPrefix.
*
*/
const Ip6::Prefix *mAdvertisedOnLinkPrefix;
/**
* The on-link prefix we discovered on the infra link.
*
*/
Ip6::Prefix mDiscoveredOnLinkPrefix;
TimerMilli mRouterAdvertisementTimer;
uint32_t mRouterAdvertisementCount;
TimerMilli mRouterSolicitTimer;
uint8_t mRouterSolicitCount;
TimerMilli mDiscoveredOnLinkPrefixInvalidTimer;
};
} // namespace BorderRouter
} // namespace ot
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#endif // ROUTING_MANAGER_HPP_
+3
View File
@@ -93,6 +93,9 @@ Instance::Instance(void)
#if OPENTHREAD_CONFIG_OTNS_ENABLE
, mOtns(*this)
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
, mRoutingManager(*this)
#endif
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
#if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
, mLinkRaw(*this)
+15
View File
@@ -95,6 +95,10 @@
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#include "border_router/routing_manager.hpp"
#endif
#endif // OPENTHREAD_FTD || OPENTHREAD_MTD
#if OPENTHREAD_ENABLE_VENDOR_EXTENSION
#include "common/extension.hpp"
@@ -391,6 +395,10 @@ private:
Utils::Otns mOtns;
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
BorderRouter::RoutingManager mRoutingManager;
#endif
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
#if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
Mac::LinkRaw mLinkRaw;
@@ -847,6 +855,13 @@ template <> inline Utils::Otns &Instance::Get(void)
}
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
template <> inline BorderRouter::RoutingManager &Instance::Get(void)
{
return mRoutingManager;
}
#endif
#endif // OPENTHREAD_MTD || OPENTHREAD_FTD
#if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE
+60
View File
@@ -94,6 +94,7 @@ extern "C" {
#define _OT_REGION_BBR_PREFIX "-BBR-----: "
#define _OT_REGION_MLR_PREFIX "-MLR-----: "
#define _OT_REGION_DUA_PREFIX "-DUA-----: "
#define _OT_REGION_BR_PREFIX "-BR------: "
#else
#define _OT_REGION_API_PREFIX _OT_REGION_SUFFIX
#define _OT_REGION_MLE_PREFIX _OT_REGION_SUFFIX
@@ -114,6 +115,7 @@ extern "C" {
#define _OT_REGION_BBR_PREFIX _OT_REGION_SUFFIX
#define _OT_REGION_MLR_PREFIX _OT_REGION_SUFFIX
#define _OT_REGION_DUA_PREFIX _OT_REGION_SUFFIX
#define _OT_REGION_BR_PREFIX _OT_REGION_SUFFIX
#endif
/**
@@ -329,6 +331,64 @@ void otLogDebg(otLogRegion aRegion, const char *aRegionPrefix, const char *aForm
#define otLogInfoMbedTls(...) otLogInfoMeshCoP(__VA_ARGS__)
#define otLogDebgMbedTls(...) otLogDebgMeshCoP(__VA_ARGS__)
/**
* @def otLogCritBr
*
* This function generates a log with level critical for the BR region.
*
* @param[in] ... Arguments for the format specification.
*
*/
/**
* @def otLogWarnBr
*
* This function generates a log with level warning for the BR region.
*
* @param[in] ... Arguments for the format specification.
*
*/
/**
* @def otLogNoteBr
*
* This function generates a log with level note for the BR region.
*
* @param[in] ... Arguments for the format specification.
*
*/
/**
* @def otLogInfoBr
*
* This function generates a log with level info for the BR region.
*
* @param[in] ... Arguments for the format specification.
*
*/
/**
* @def otLogDebgBr
*
* This function generates a log with level debug for the BR region.
*
* @param[in] ... Arguments for the format specification.
*
*/
#if OPENTHREAD_CONFIG_LOG_BR
#define otLogCritBr(...) otLogCrit(OT_LOG_REGION_BR, _OT_REGION_BR_PREFIX, __VA_ARGS__)
#define otLogWarnBr(...) otLogWarn(OT_LOG_REGION_BR, _OT_REGION_BR_PREFIX, __VA_ARGS__)
#define otLogNoteBr(...) otLogNote(OT_LOG_REGION_BR, _OT_REGION_BR_PREFIX, __VA_ARGS__)
#define otLogInfoBr(...) otLogInfo(OT_LOG_REGION_BR, _OT_REGION_BR_PREFIX, __VA_ARGS__)
#define otLogDebgBr(...) otLogDebg(OT_LOG_REGION_BR, _OT_REGION_BR_PREFIX, __VA_ARGS__)
#else
#define otLogCritBr(...)
#define otLogWarnBr(...)
#define otLogNoteBr(...)
#define otLogInfoBr(...)
#define otLogDebgBr(...)
#endif
/**
* @def otLogCritMle
*
+4
View File
@@ -33,6 +33,7 @@
#include "notifier.hpp"
#include "border_router/routing_manager.hpp"
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/locator-getters.hpp"
@@ -179,6 +180,9 @@ void Notifier::EmitEvents(void)
#if OPENTHREAD_ENABLE_VENDOR_EXTENSION
Get<Extension::ExtensionBase>().HandleNotifierEvents(events);
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
Get<BorderRouter::RoutingManager>().HandleNotifierEvents(events);
#endif
for (ExternalCallback &callback : mExternalCallbacks)
{
+76
View File
@@ -81,6 +81,12 @@ void SettingsBase::LogDadInfo(const char *aAction, const DadInfo &aDadInfo) cons
otLogInfoCore("Non-volatile: %s DadInfo {DadCounter:%2d}", aAction, aDadInfo.GetDadCounter());
}
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
void SettingsBase::LogPrefix(const char *aAction, const char *aPrefixName, const Ip6::Prefix &aOmrPrefix) const
{
otLogInfoCore("Non-volatile: %s %s %s", aAction, aPrefixName, aOmrPrefix.ToString().AsCString());
}
#endif
#endif // #if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO)
@@ -440,6 +446,76 @@ exit:
}
#endif // OPENTHREAD_CONFIG_DUA_ENABLE
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
otError Settings::SaveOmrPrefix(const Ip6::Prefix &aOmrPrefix)
{
otError error = OT_ERROR_NONE;
Ip6::Prefix prevOmrPrefix;
uint16_t length = sizeof(prevOmrPrefix);
if ((Read(kKeyOmrPrefix, &prevOmrPrefix, length) == OT_ERROR_NONE) && (length == sizeof(prevOmrPrefix)) &&
(prevOmrPrefix == aOmrPrefix))
{
LogPrefix("Re-saved", "OMR prefix", aOmrPrefix);
ExitNow();
}
SuccessOrExit(error = Save(kKeyOmrPrefix, &aOmrPrefix, sizeof(aOmrPrefix)));
LogPrefix("Saved", "OMR prefix", aOmrPrefix);
exit:
LogFailure(error, "saving OMR prefix", false);
return error;
}
otError Settings::ReadOmrPrefix(Ip6::Prefix &aOmrPrefix) const
{
otError error;
uint16_t length = sizeof(aOmrPrefix);
aOmrPrefix.Clear();
SuccessOrExit(error = Read(kKeyOmrPrefix, &aOmrPrefix, length));
LogPrefix("Read", "OMR prefix", aOmrPrefix);
exit:
return error;
}
otError Settings::SaveOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix)
{
otError error = OT_ERROR_NONE;
Ip6::Prefix prevOnLinkPrefix;
uint16_t length = sizeof(prevOnLinkPrefix);
if ((Read(kKeyOnLinkPrefix, &prevOnLinkPrefix, length) == OT_ERROR_NONE) && (length == sizeof(prevOnLinkPrefix)) &&
(prevOnLinkPrefix == aOnLinkPrefix))
{
LogPrefix("Re-saved", "on-link prefix", aOnLinkPrefix);
ExitNow();
}
SuccessOrExit(error = Save(kKeyOnLinkPrefix, &aOnLinkPrefix, sizeof(aOnLinkPrefix)));
LogPrefix("Saved", "on-link prefix", aOnLinkPrefix);
exit:
LogFailure(error, "saving on-link prefix", false);
return error;
}
otError Settings::ReadOnLinkPrefix(Ip6::Prefix &aOnLinkPrefix) const
{
otError error;
uint16_t length = sizeof(aOnLinkPrefix);
aOnLinkPrefix.Clear();
SuccessOrExit(error = Read(kKeyOnLinkPrefix, &aOnLinkPrefix, length));
LogPrefix("Read", "on-link prefix", aOnLinkPrefix);
exit:
return error;
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
otError Settings::Read(Key aKey, void *aBuffer, uint16_t &aSize) const
{
return Get<SettingsDriver>().Get(aKey, 0, reinterpret_cast<uint8_t *>(aBuffer), &aSize);
+56
View File
@@ -589,6 +589,8 @@ public:
kKeyReserved = 0x0006, ///< Reserved (previously auto-start)
kKeySlaacIidSecretKey = 0x0007, ///< Secret key used by SLAAC module for generating semantically opaque IID
kKeyDadInfo = 0x0008, ///< Duplicate Address Detection (DAD) information.
kKeyOmrPrefix = 0x0009, ///< Off-mesh routable (OMR) prefix.
kKeyOnLinkPrefix = 0x000a, ///< On-link prefix for infrastructure link.
};
protected:
@@ -604,6 +606,9 @@ protected:
#if OPENTHREAD_CONFIG_DUA_ENABLE
void LogDadInfo(const char *aAction, const DadInfo &aDadInfo) const;
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
void LogPrefix(const char *aAction, const char *aPrefixName, const Ip6::Prefix &aOmrPrefix) const;
#endif
#else // (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_UTIL != 0)
void LogNetworkInfo(const char *, const NetworkInfo &) const {}
void LogParentInfo(const char *, const ParentInfo &) const {}
@@ -611,6 +616,9 @@ protected:
#if OPENTHREAD_CONFIG_DUA_ENABLE
void LogDadInfo(const char *, const DadInfo &) const {}
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
void LogPrefix(const char *, const char *, const Ip6::Prefix &) const {}
#endif
#endif // (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_INFO) && (OPENTHREAD_CONFIG_LOG_UTIL != 0)
#if (OPENTHREAD_CONFIG_LOG_LEVEL >= OT_LOG_LEVEL_WARN) && (OPENTHREAD_CONFIG_LOG_UTIL != 0)
@@ -1000,6 +1008,54 @@ public:
#endif // OPENTHREAD_CONFIG_DUA_ENABLE
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
/**
* This method saves OMR prefix.
*
* @param[in] aOmrPrefix An OMR prefix to be saved.
*
* @retval OT_ERROR_NONE Successfully saved the OMR prefix in settings.
* @retval OT_ERROR_NOT_IMPLEMENTED The platform does not implement settings functionality.
*
*/
otError SaveOmrPrefix(const Ip6::Prefix &aOmrPrefix);
/**
* This method reads OMR prefix.
*
* @param[out] aOmrPrefix A reference to a `Ip6::Prefix` structure to output the OMR prefix.
*
* @retval OT_ERROR_NONE Successfully read the OMR prefix.
* @retval OT_ERROR_NOT_FOUND No corresponding value in the setting store.
* @retval OT_ERROR_NOT_IMPLEMENTED The platform does not implement settings functionality.
*
*/
otError ReadOmrPrefix(Ip6::Prefix &aOmrPrefix) const;
/**
* This method saves on-link prefix.
*
* @param[in] aOnLinkPrefix An on-link prefix to be saved.
*
* @retval OT_ERROR_NONE Successfully saved the on-link prefix in settings.
* @retval OT_ERROR_NOT_IMPLEMENTED The platform does not implement settings functionality.
*
*/
otError SaveOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix);
/**
* This method reads on-link prefix.
*
* @param[out] aOnLinkPrefix A reference to a `Ip6::Prefix` structure to output the on-link prefix.
*
* @retval OT_ERROR_NONE Successfully read the on-link prefix.
* @retval OT_ERROR_NOT_FOUND No corresponding value in the setting store.
* @retval OT_ERROR_NOT_IMPLEMENTED The platform does not implement settings functionality.
*
*/
otError ReadOnLinkPrefix(Ip6::Prefix &aOnLinkPrefix) const;
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
private:
class ChildInfoIteratorBuilder : public InstanceLocator
{
+10
View File
@@ -55,4 +55,14 @@
#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 0
#endif
/**
* @def OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
*
* Define to 1 to enable (Duckhorn) Border Routing support.
*
*/
#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#define OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE 0
#endif
#endif // CONFIG_BORDER_ROUTER_H_
+10
View File
@@ -299,6 +299,16 @@
#define OPENTHREAD_CONFIG_LOG_DUA 1
#endif
/**
* @def OPENTHREAD_CONFIG_LOG_BR
*
* Define to Border Router (BR) region logging.
*
*/
#ifndef OPENTHREAD_CONFIG_LOG_BR
#define OPENTHREAD_CONFIG_LOG_BR 1
#endif
/**
* @def OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL
*
@@ -507,6 +507,17 @@
#endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#if !OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE
#error "OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE is required for OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE"
#endif
#if !OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE
#error "OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE is required for OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE"
#endif
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE
#if (OPENTHREAD_CONFIG_THREAD_VERSION < OT_THREAD_VERSION_1_2)
#error "Thread 1.2 or higher version is required for OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE"
+2 -1
View File
@@ -87,6 +87,8 @@ public:
kTypeParameterProblem = OT_ICMP6_TYPE_PARAMETER_PROBLEM, ///< Parameter Problem
kTypeEchoRequest = OT_ICMP6_TYPE_ECHO_REQUEST, ///< Echo Request
kTypeEchoReply = OT_ICMP6_TYPE_ECHO_REPLY, ///< Echo Reply
kTypeRouterSolicit = OT_ICMP6_TYPE_ROUTER_SOLICIT, ///< Router Solicitation
kTypeRouterAdvert = OT_ICMP6_TYPE_ROUTER_ADVERT, ///< Router Advertisement
};
/**
@@ -195,7 +197,6 @@ public:
*
*/
void SetSequence(uint16_t aSequence) { mData.m16[1] = HostSwap16(aSequence); }
} OT_TOOL_PACKED_END;
/**
+1 -1
View File
@@ -87,7 +87,7 @@ public:
*
*/
OT_TOOL_PACKED_BEGIN
class Prefix : public otIp6Prefix
class Prefix : public otIp6Prefix, public Clearable<Prefix>
{
public:
enum : uint8_t
+1
View File
@@ -692,6 +692,7 @@ enum
SPINEL_NCP_LOG_REGION_OT_BBR = 17,
SPINEL_NCP_LOG_REGION_OT_MLR = 18,
SPINEL_NCP_LOG_REGION_OT_DUA = 19,
SPINEL_NCP_LOG_REGION_OT_BR = 20,
};
enum
+3
View File
@@ -624,6 +624,9 @@ unsigned int NcpBase::ConvertLogRegion(otLogRegion aLogRegion)
case OT_LOG_REGION_DUA:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_DUA;
break;
case OT_LOG_REGION_BR:
spinelLogRegion = SPINEL_NCP_LOG_REGION_OT_BR;
break;
}
return spinelLogRegion;
+1
View File
@@ -64,6 +64,7 @@ add_library(openthread-posix
backbone.cpp
entropy.cpp
hdlc_interface.cpp
infra_if.cpp
logging.cpp
misc.cpp
multicast_routing.cpp
+1
View File
@@ -48,6 +48,7 @@ libopenthread_posix_a_SOURCES = \
backbone.cpp \
entropy.cpp \
hdlc_interface.cpp \
infra_if.cpp \
logging.cpp \
misc.cpp \
multicast_routing.cpp \
+362
View File
@@ -0,0 +1,362 @@
/*
* 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.
*/
/**
* @file
* This file implements the infrastructure interface for posix.
*/
#include "platform-posix.h"
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
#ifdef __APPLE__
#define __APPLE_USE_RFC_3542
#endif
#include <errno.h>
#include <ifaddrs.h>
// clang-format off
#include <netinet/in.h>
#include <netinet/icmp6.h>
// clang-format on
#include <sys/types.h>
#include <unistd.h>
#include <openthread/platform/infra_if.h>
#include "common/code_utils.hpp"
static char sInfraIfName[IFNAMSIZ];
static uint32_t sInfraIfIndex = 0;
static int sInfraIfIcmp6Socket = -1;
static otIp6Address sInfraIfLinkLocalAddr;
otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex,
const otIp6Address *aDestAddress,
const uint8_t * aBuffer,
uint16_t aBufferLength)
{
otError error = OT_ERROR_NONE;
struct iovec iov;
struct in6_pktinfo *packetInfo;
int hopLimit = 255;
uint8_t cmsgBuffer[CMSG_SPACE(sizeof(*packetInfo)) + CMSG_SPACE(sizeof(hopLimit))];
struct msghdr msgHeader;
struct cmsghdr * cmsgPointer;
ssize_t rval;
struct sockaddr_in6 dest;
VerifyOrExit(sInfraIfIcmp6Socket >= 0, error = OT_ERROR_FAILED);
VerifyOrExit(aInfraIfIndex == sInfraIfIndex, error = OT_ERROR_DROP);
memset(cmsgBuffer, 0, sizeof(cmsgBuffer));
// Send the message
memset(&dest, 0, sizeof(dest));
dest.sin6_family = AF_INET6;
memcpy(&dest.sin6_addr, aDestAddress, sizeof(*aDestAddress));
if (IN6_IS_ADDR_LINKLOCAL(&dest.sin6_addr) || IN6_IS_ADDR_MC_LINKLOCAL(&dest.sin6_addr))
{
dest.sin6_scope_id = sInfraIfIndex;
}
iov.iov_base = const_cast<uint8_t *>(aBuffer);
iov.iov_len = aBufferLength;
msgHeader.msg_namelen = sizeof(dest);
msgHeader.msg_name = &dest;
msgHeader.msg_iov = &iov;
msgHeader.msg_iovlen = 1;
msgHeader.msg_control = cmsgBuffer;
msgHeader.msg_controllen = sizeof(cmsgBuffer);
// Specify the interface.
cmsgPointer = CMSG_FIRSTHDR(&msgHeader);
cmsgPointer->cmsg_level = IPPROTO_IPV6;
cmsgPointer->cmsg_type = IPV6_PKTINFO;
cmsgPointer->cmsg_len = CMSG_LEN(sizeof(*packetInfo));
packetInfo = (struct in6_pktinfo *)CMSG_DATA(cmsgPointer);
memset(packetInfo, 0, sizeof(*packetInfo));
packetInfo->ipi6_ifindex = sInfraIfIndex;
// Per section 6.1.2 of RFC 4861, we need to send the ICMPv6 message with IP Hop Limit 255.
cmsgPointer = CMSG_NXTHDR(&msgHeader, cmsgPointer);
cmsgPointer->cmsg_level = IPPROTO_IPV6;
cmsgPointer->cmsg_type = IPV6_HOPLIMIT;
cmsgPointer->cmsg_len = CMSG_LEN(sizeof(hopLimit));
memcpy(CMSG_DATA(cmsgPointer), &hopLimit, sizeof(hopLimit));
rval = sendmsg(sInfraIfIcmp6Socket, &msgHeader, 0);
if (rval < 0)
{
otLogWarnPlat("failed to send ICMPv6 message: %s", strerror(errno));
ExitNow(error = OT_ERROR_FAILED);
}
if (static_cast<size_t>(rval) != iov.iov_len)
{
otLogWarnPlat("failed to send ICMPv6 message: partially sent");
ExitNow(error = OT_ERROR_FAILED);
}
exit:
return error;
}
static void InitLinkLocalAddress(void)
{
struct ifaddrs *ifAddrs = nullptr;
if (getifaddrs(&ifAddrs) < 0)
{
otLogCritPlat("failed to get netif addresses: %s", strerror(errno));
DieNow(OT_EXIT_ERROR_ERRNO);
}
for (struct ifaddrs *addr = ifAddrs; addr != nullptr; addr = addr->ifa_next)
{
struct sockaddr_in6 *ip6Addr;
if (strncmp(addr->ifa_name, sInfraIfName, sizeof(sInfraIfName)) != 0 || addr->ifa_addr->sa_family != AF_INET6)
{
continue;
}
ip6Addr = reinterpret_cast<sockaddr_in6 *>(addr->ifa_addr);
if (IN6_IS_ADDR_LINKLOCAL(&ip6Addr->sin6_addr))
{
memcpy(&sInfraIfLinkLocalAddr, &ip6Addr->sin6_addr, sizeof(sInfraIfLinkLocalAddr));
break;
}
}
freeifaddrs(ifAddrs);
}
void platformInfraIfInit(otInstance *aInstance, const char *aIfName)
{
OT_UNUSED_VARIABLE(aInstance);
int sock;
struct icmp6_filter filter;
ssize_t rval;
const int kEnable = 1;
const int kIpv6ChecksumOffset = 2;
const int kHopLimit = 255;
uint32_t ifIndex = 0;
if (strlen(aIfName) >= sizeof(sInfraIfName))
{
otLogCritPlat("infra interface name '%s' is too long", aIfName);
DieNow(OT_EXIT_INVALID_ARGUMENTS);
}
strcpy(sInfraIfName, aIfName);
// Initializes the infra interface.
ifIndex = if_nametoindex(aIfName);
if (ifIndex == 0)
{
otLogCritPlat("failed to get the index for infra interface %s: %s", aIfName, strerror(errno));
DieNow(OT_EXIT_ERROR_ERRNO);
}
sInfraIfIndex = ifIndex;
// Initializes the ICMPv6 socket.
sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
if (sock < 0)
{
otLogCritPlat("failed to open ICMPv6 socket: %s", strerror(errno));
DieNow(OT_EXIT_ERROR_ERRNO);
}
// Only accept router advertisements and router solicits.
ICMP6_FILTER_SETBLOCKALL(&filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
rval = setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter));
if (rval < 0)
{
otLogCritPlat("Can't set ICMP6_FILTER: %s", strerror(errno));
DieNow(OT_EXIT_ERROR_ERRNO);
}
// We want a source address and interface index.
rval = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &kEnable, sizeof(kEnable));
if (rval < 0)
{
otLogCritPlat("Can't set IPV6_RECVPKTINFO: %s", strerror(errno));
DieNow(OT_EXIT_ERROR_ERRNO);
}
#ifdef __linux__
rval = setsockopt(sock, IPPROTO_RAW, IPV6_CHECKSUM, &kIpv6ChecksumOffset, sizeof(kIpv6ChecksumOffset));
#else
rval = setsockopt(sock, IPPROTO_IPV6, IPV6_CHECKSUM, &kIpv6ChecksumOffset, sizeof(kIpv6ChecksumOffset));
#endif
if (rval < 0)
{
otLogCritPlat("Can't set IPV6_CHECKSUM: %s", strerror(errno));
DieNow(OT_EXIT_ERROR_ERRNO);
}
// We need to be able to reject RAs arriving from off-link.
rval = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &kEnable, sizeof(kEnable));
if (rval < 0)
{
otLogCritPlat("Can't set IPV6_RECVHOPLIMIT: %s", strerror(errno));
DieNow(OT_EXIT_ERROR_ERRNO);
}
rval = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &kHopLimit, sizeof(kHopLimit));
if (rval < 0)
{
otLogCritPlat("Can't set IPV6_UNICAST_HOPS: %s", strerror(errno));
DieNow(OT_EXIT_ERROR_ERRNO);
}
rval = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kHopLimit, sizeof(kHopLimit));
if (rval < 0)
{
otLogCritPlat("Can't set IPV6_MULTICAST_HOPS: %s", strerror(errno));
DieNow(OT_EXIT_ERROR_ERRNO);
}
sInfraIfIcmp6Socket = sock;
InitLinkLocalAddress();
}
void platformInfraIfDeinit(void)
{
if (sInfraIfIcmp6Socket != -1)
{
close(sInfraIfIcmp6Socket);
sInfraIfIcmp6Socket = -1;
}
sInfraIfIndex = 0;
}
void platformInfraIfUpdateFdSet(fd_set &aReadFdSet, int &aMaxFd)
{
VerifyOrExit(sInfraIfIcmp6Socket != -1);
FD_SET(sInfraIfIcmp6Socket, &aReadFdSet);
aMaxFd = OT_MAX(aMaxFd, sInfraIfIcmp6Socket);
exit:
return;
}
void platformInfraIfProcess(otInstance *aInstance, const fd_set &aReadFdSet)
{
otError error = OT_ERROR_DROP;
uint8_t buffer[1500];
uint16_t bufferLength;
ssize_t rval;
struct msghdr msg;
struct iovec bufp;
char cmsgbuf[128];
struct cmsghdr *cmh;
uint32_t ifIndex = 0;
int hopLimit = -1;
struct sockaddr_in6 srcAddr;
struct in6_addr dstAddr;
VerifyOrExit(sInfraIfIcmp6Socket != -1);
VerifyOrExit(FD_ISSET(sInfraIfIcmp6Socket, &aReadFdSet));
memset(&srcAddr, 0, sizeof(srcAddr));
memset(&dstAddr, 0, sizeof(dstAddr));
bufp.iov_base = buffer;
bufp.iov_len = sizeof(buffer);
msg.msg_iov = &bufp;
msg.msg_iovlen = 1;
msg.msg_name = &srcAddr;
msg.msg_namelen = sizeof(srcAddr);
msg.msg_control = cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
rval = recvmsg(sInfraIfIcmp6Socket, &msg, 0);
if (rval < 0)
{
otLogWarnPlat("failed to receive ICMPv6 message: %s", strerror(errno));
ExitNow();
}
bufferLength = static_cast<uint16_t>(rval);
for (cmh = CMSG_FIRSTHDR(&msg); cmh; cmh = CMSG_NXTHDR(&msg, cmh))
{
if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_PKTINFO &&
cmh->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo)))
{
struct in6_pktinfo pktinfo;
memcpy(&pktinfo, CMSG_DATA(cmh), sizeof pktinfo);
ifIndex = pktinfo.ipi6_ifindex;
dstAddr = pktinfo.ipi6_addr;
}
else if (cmh->cmsg_level == IPPROTO_IPV6 && cmh->cmsg_type == IPV6_HOPLIMIT &&
cmh->cmsg_len == CMSG_LEN(sizeof(int)))
{
hopLimit = *(int *)CMSG_DATA(cmh);
}
}
VerifyOrExit(ifIndex == sInfraIfIndex);
// We currently accept only RA & RS messages for the Border Router and it requires that
// the hoplimit must be 255 and the source address must be a link-local address.
VerifyOrExit(hopLimit == 255 && IN6_IS_ADDR_LINKLOCAL(&srcAddr.sin6_addr));
// Drop multicast messages sent by ourselves.
VerifyOrExit(!otIp6IsAddressEqual(&sInfraIfLinkLocalAddr, reinterpret_cast<otIp6Address *>(&srcAddr.sin6_addr)));
otPlatInfraIfRecvIcmp6Nd(aInstance, ifIndex, reinterpret_cast<otIp6Address *>(&srcAddr.sin6_addr), buffer,
bufferLength);
error = OT_ERROR_NONE;
exit:
if (error != OT_ERROR_NONE)
{
otLogDebgPlat("drop ICMPv6 message");
}
}
uint32_t platformInfraIfGetIndex(void)
{
return sInfraIfIndex;
}
#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
+42
View File
@@ -39,6 +39,7 @@
#include <errno.h>
#include <net/if.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>
@@ -498,6 +499,47 @@ extern char gBackboneNetifName[IFNAMSIZ];
*/
extern unsigned int gBackboneNetifIndex;
/**
* This function initializes the infrastructure interface.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aIfName The name of the infrastructure interface.
*
*/
void platformInfraIfInit(otInstance *aInstance, const char *aIfName);
/**
* This function deinitializes the infrastructure interface.
*
*/
void platformInfraIfDeinit(void);
/**
* This function updates the read fd set.
*
* @param[out] aReadFdSet The fd set to be updated.
* @param[out] aMaxFd The maximum fd to be updated.
*
*/
void platformInfraIfUpdateFdSet(fd_set &aReadFdSet, int &aMaxFd);
/**
* This function processes possible events on the infrastructure interface.
*
* @param[in] aInstance The OpenThread instance.
* @param[in] aReadFdSet The fd set which may contain read vents.
*
*/
void platformInfraIfProcess(otInstance *aInstance, const fd_set &aReadFdSet);
/**
* This function returns the index of the infrastructure interface.
*
* @returns The index of the infrastructure interface. 0 indicates invalid index.
*
*/
uint32_t platformInfraIfGetIndex(void);
#ifdef __cplusplus
}
#endif
+20
View File
@@ -38,6 +38,7 @@
#include <assert.h>
#include <openthread-core-config.h>
#include <openthread/border_router.h>
#include <openthread/tasklet.h>
#include <openthread/platform/alarm-milli.h>
#include <openthread/platform/otns.h>
@@ -97,6 +98,11 @@ otInstance *otSysInit(otPlatformConfig *aPlatformConfig)
platformBackboneInit(instance, aPlatformConfig->mBackboneInterfaceName);
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
// Reuse the backbone interface name.
platformInfraIfInit(instance, aPlatformConfig->mBackboneInterfaceName);
#endif
#if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE
platformNetifInit(instance, aPlatformConfig->mInterfaceName);
#elif OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE
@@ -107,6 +113,10 @@ otInstance *otSysInit(otPlatformConfig *aPlatformConfig)
SuccessOrDie(otSetStateChangedCallback(instance, processStateChange, instance));
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
SuccessOrDie(otBorderRoutingInit(instance, platformInfraIfGetIndex()));
#endif
return instance;
}
@@ -123,6 +133,10 @@ void otSysDeinit(void)
platformTrelDeinit();
#endif
IgnoreError(otPlatUartDisable());
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
platformInfraIfDeinit();
#endif
}
#if OPENTHREAD_POSIX_VIRTUAL_TIME
@@ -173,6 +187,9 @@ void otSysMainloopUpdate(otInstance *aInstance, otSysMainloopContext *aMainloop)
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
platformBackboneUpdateFdSet(aMainloop->mReadFdSet, aMainloop->mMaxFd);
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
platformInfraIfUpdateFdSet(aMainloop->mReadFdSet, aMainloop->mMaxFd);
#endif
#if OPENTHREAD_POSIX_VIRTUAL_TIME
virtualTimeUpdateFdSet(&aMainloop->mReadFdSet, &aMainloop->mWriteFdSet, &aMainloop->mErrorFdSet, &aMainloop->mMaxFd,
&aMainloop->mTimeout);
@@ -254,6 +271,9 @@ void otSysMainloopProcess(otInstance *aInstance, const otSysMainloopContext *aMa
#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE
platformBackboneProcess(aMainloop->mReadFdSet);
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
platformInfraIfProcess(aInstance, aMainloop->mReadFdSet);
#endif
}
#if OPENTHREAD_CONFIG_OTNS_ENABLE
@@ -0,0 +1,138 @@
#!/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 logging
import unittest
import config
import thread_cert
# Test description:
# This test verifies that a single OMR and on-link prefix is chosen
# and advertised when there are multiple Border Routers in the same
# Thread and infrastructure network.
#
# Topology:
# ----------------(eth)------------------
# | | |
# BR1 (Leader) ----- BR2 HOST
# |
# ED1
#
BR1 = 1
ROUTER1 = 2
BR2 = 3
HOST = 4
CHANNEL1 = 18
class MultiBorderRouters(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
BR1: {
'name': 'BR_1',
'allowlist': [ROUTER1, BR2],
'is_otbr': True,
'version': '1.2',
'channel': CHANNEL1,
'router_selection_jitter': 2,
},
ROUTER1: {
'name': 'Router_1',
'allowlist': [BR1],
'version': '1.2',
'channel': CHANNEL1,
'router_selection_jitter': 2,
},
BR2: {
'name': 'BR_2',
'allowlist': [BR1],
'is_otbr': True,
'version': '1.2',
'channel': CHANNEL1,
'router_selection_jitter': 2,
},
HOST: {
'name': 'Host',
'is_host': True
},
}
def test(self):
self.nodes[HOST].start(start_radvd=True, prefix=config.ONLINK_PREFIX, slaac=True)
self.simulator.go(5)
self.nodes[BR1].start()
self.simulator.go(5)
self.assertEqual('leader', self.nodes[BR1].get_state())
self.nodes[ROUTER1].start()
self.simulator.go(5)
self.assertEqual('router', self.nodes[ROUTER1].get_state())
self.nodes[BR2].start()
self.simulator.go(5)
self.assertEqual('router', self.nodes[BR2].get_state())
self.simulator.go(10)
self.collect_ipaddrs()
logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs())
logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs())
logging.info("BR2 addrs: %r", self.nodes[BR2].get_addrs())
logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs())
self.assertGreaterEqual(len(self.nodes[HOST].get_addrs()), 2)
self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 1)
self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 1)
self.assertTrue(len(self.nodes[BR2].get_prefixes()) == 1)
self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
self.assertTrue(len(self.nodes[BR2].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
self.assertTrue(len(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)) == 1)
# Router1 and BR2 can ping each other inside the Thread network.
self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[BR2].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]))
self.assertTrue(self.nodes[BR2].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]))
# Both Router1 and BR2 can ping to/from the Host on infra link.
self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
backbone=True))
self.assertTrue(self.nodes[BR2].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
self.assertTrue(self.nodes[HOST].ping(self.nodes[BR2].get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
backbone=True))
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,130 @@
#!/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 logging
import unittest
import config
import thread_cert
# Test description:
# This test verifies bi-directional connectivity accross multiple Thread networks.
#
# Topology:
# -------------(eth)----------------
# | |
# BR1 BR2
# | |
# ED1 ED2
#
# Thread Net1 Thread Net2
#
BR1 = 1
ROUTER1 = 2
BR2 = 3
ROUTER2 = 4
CHANNEL1 = 18
CHANNEL2 = 19
class MultiThreadNetworks(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
BR1: {
'name': 'BR_1',
'allowlist': [ROUTER1],
'is_otbr': True,
'version': '1.2',
'channel': CHANNEL1,
'router_selection_jitter': 1,
},
ROUTER1: {
'name': 'Router_1',
'allowlist': [BR1],
'version': '1.2',
'channel': CHANNEL1,
'router_selection_jitter': 1,
},
BR2: {
'name': 'BR_2',
'allowlist': [ROUTER2],
'is_otbr': True,
'version': '1.2',
'channel': CHANNEL2,
'router_selection_jitter': 1,
},
ROUTER2: {
'name': 'Router_2',
'allowlist': [BR2],
'version': '1.2',
'channel': CHANNEL2,
'router_selection_jitter': 1,
},
}
def test(self):
self.nodes[BR1].start()
self.simulator.go(5)
self.assertEqual('leader', self.nodes[BR1].get_state())
self.nodes[ROUTER1].start()
self.simulator.go(5)
self.assertEqual('router', self.nodes[ROUTER1].get_state())
self.nodes[BR2].start()
self.simulator.go(5)
self.assertEqual('leader', self.nodes[BR2].get_state())
self.nodes[ROUTER2].start()
self.simulator.go(5)
self.assertEqual('router', self.nodes[ROUTER2].get_state())
self.collect_ipaddrs()
logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs())
logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs())
logging.info("BR2 addrs: %r", self.nodes[BR2].get_addrs())
logging.info("ROUTER2 addrs: %r", self.nodes[ROUTER2].get_addrs())
self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 1)
self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 1)
self.assertTrue(len(self.nodes[BR2].get_prefixes()) == 1)
self.assertTrue(len(self.nodes[ROUTER2].get_prefixes()) == 1)
self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
self.assertTrue(len(self.nodes[ROUTER2].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[ROUTER2].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]))
self.assertTrue(self.nodes[ROUTER2].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0]))
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,148 @@
#!/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 logging
import unittest
import config
import thread_cert
# Test description:
# This test verifies bi-directional connectivity between Thread end device
# and infra host.
#
# Topology:
# ----------------(eth)--------------------
# | |
# BR1 (Leader) HOST
# |
# ROUTER1
#
BR1 = 1
ROUTER1 = 2
HOST = 4
CHANNEL1 = 18
# The two prefixes are set small enough that a random-generated OMR prefix is
# very likely greater than them. So that the duckhorn BR will remove the random-generated one.
ON_MESH_PREFIX1 = "fd00:00:00:01::/64"
ON_MESH_PREFIX2 = "fd00:00:00:02::/64"
class SingleBorderRouter(thread_cert.TestCase):
USE_MESSAGE_FACTORY = False
TOPOLOGY = {
BR1: {
'name': 'BR_1',
'allowlist': [ROUTER1],
'is_otbr': True,
'version': '1.2',
'channel': CHANNEL1,
'router_selection_jitter': 2,
},
ROUTER1: {
'name': 'Router_1',
'allowlist': [BR1],
'version': '1.2',
'channel': CHANNEL1,
'router_selection_jitter': 2,
},
HOST: {
'name': 'Host',
'is_host': True
},
}
def test(self):
self.nodes[HOST].start(start_radvd=False)
self.simulator.go(5)
self.nodes[BR1].start()
self.simulator.go(5)
self.assertEqual('leader', self.nodes[BR1].get_state())
self.nodes[ROUTER1].start()
self.simulator.go(5)
self.assertEqual('router', self.nodes[ROUTER1].get_state())
self.simulator.go(10)
self.collect_ipaddrs()
logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs())
logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs())
logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs())
self.assertGreaterEqual(len(self.nodes[HOST].get_addrs()), 2)
self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 1)
self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 1)
self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1)
self.assertTrue(len(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)) == 1)
# Router1 can ping to/from the Host on infra link.
self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
backbone=True))
# Add two on-mesh prefix on BR1, so that
# it will deregister its random-generated OMR prefix.
self.nodes[BR1].add_prefix(ON_MESH_PREFIX1)
self.nodes[BR1].add_prefix(ON_MESH_PREFIX2)
self.nodes[BR1].register_netdata()
self.simulator.go(10)
self.collect_ipaddrs()
logging.info("BR1 addrs: %r", self.nodes[BR1].get_addrs())
logging.info("ROUTER1 addrs: %r", self.nodes[ROUTER1].get_addrs())
logging.info("HOST addrs: %r", self.nodes[HOST].get_addrs())
self.assertGreaterEqual(len(self.nodes[HOST].get_addrs()), 2)
self.assertTrue(len(self.nodes[BR1].get_prefixes()) == 2)
self.assertTrue(len(self.nodes[ROUTER1].get_prefixes()) == 2)
self.assertTrue(len(self.nodes[BR1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 2)
self.assertTrue(len(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)) == 2)
self.assertTrue(len(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)) == 1)
# Router1 can ping to/from the Host on infra link.
self.assertTrue(self.nodes[ROUTER1].ping(self.nodes[HOST].get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0]))
self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[0],
backbone=True))
self.assertTrue(self.nodes[HOST].ping(self.nodes[ROUTER1].get_ip6_address(config.ADDRESS_TYPE.OMR)[1],
backbone=True))
if __name__ == '__main__':
unittest.main()
+7
View File
@@ -78,6 +78,11 @@ ALL_NETWORK_BBRS_ADDRESS = 'ff32:40:fd00:db8:0:0:0:3'
ALL_DOMAIN_BBRS_ADDRESS = 'ff32:40:fd00:7d03:7d03:7d03:0:3'
ALL_DOMAIN_BBRS_ADDRESS_ALTER = 'ff32:40:fd00:7d04:7d04:7d04:0:3'
ONLINK_PREFIX = 'fd00:dead:face::/64'
# Any address starts with 'fd' are considered on-link address.
ONLINK_PREFIX_REGEX_PATTERN = '^fd'
DEFAULT_MASTER_KEY = bytearray([
0x00,
0x11,
@@ -106,6 +111,8 @@ class ADDRESS_TYPE(Enum):
ML_EID = 'ML_EID'
DUA = 'DUA'
BACKBONE_GUA = 'BACKBONE_GUA'
OMR = 'OMR'
ONLINK_ULA = 'ONLINK_ULA'
RSSI = {
+53 -9
View File
@@ -219,6 +219,7 @@ class OtbrDocker:
def _setup_sysctl(self):
self.bash(f'sysctl net.ipv6.conf.{self.ETH_DEV}.accept_ra=2')
self.bash(f'sysctl net.ipv6.conf.{self.ETH_DEV}.accept_ra_rt_info_max_plen=64')
class OtCli:
@@ -1169,6 +1170,17 @@ class NodeImpl:
self.send_command(cmd)
self._expect('Done')
def __getOmrAddress(self):
prefixes = [prefix.split('::')[0] for prefix in self.get_prefixes()]
omr_addrs = []
for addr in self.get_addrs():
for prefix in prefixes:
if (addr.startswith(prefix)):
omr_addrs.append(addr)
break
return omr_addrs
def __getLinkLocalAddress(self):
for ip6Addr in self.get_addrs():
if re.match(config.LINK_LOCAL_REGEX_PATTERN, ip6Addr, re.I):
@@ -1242,6 +1254,8 @@ class NodeImpl:
return self.__getDua()
elif address_type == config.ADDRESS_TYPE.BACKBONE_GUA:
return self._getBackboneGua()
elif address_type == config.ADDRESS_TYPE.OMR:
return self.__getOmrAddress()
else:
return None
@@ -1264,6 +1278,21 @@ class NodeImpl:
self.send_command(cmd)
self._expect('Done')
def get_prefixes(self):
netdata = self.netdata_show()
prefixes = []
for i in range(1, len(netdata)):
if netdata[i].startswith("Routes:"):
break
prefixes.append(netdata[i])
return prefixes
def netdata_show(self):
self.send_command('netdata show')
return self._expect_command_output('netdata show')
def add_route(self, prefix, stable=False, prf='med'):
cmd = 'route add %s ' % prefix
if stable:
@@ -2046,12 +2075,22 @@ class LinuxHost():
return resp_count
def _getBackboneGua(self) -> Optional[str]:
for ip6Addr in self.get_addrs():
if re.match(config.BACKBONE_PREFIX_REGEX_PATTERN, ip6Addr, re.I):
return ip6Addr
for addr in self.get_addrs():
if re.match(config.BACKBONE_PREFIX_REGEX_PATTERN, addr, re.I):
return addr
return None
def _getInfraUla(self) -> Optional[str]:
""" Returns the ULA addresses autoconfigured on the infra link.
"""
addrs = []
for addr in self.get_addrs():
if re.match(config.ONLINK_PREFIX_REGEX_PATTERN, addr, re.I):
addrs.append(addr)
return addrs
def ping(self, *args, **kwargs):
backbone = kwargs.pop('backbone', False)
if backbone:
@@ -2099,9 +2138,12 @@ class HostNode(LinuxHost, OtbrDocker):
self.name = name or ('Host%d' % nodeid)
super().__init__(nodeid, **kwargs)
def start(self):
def start(self, start_radvd=True, prefix=config.DOMAIN_PREFIX, slaac=False):
self._setup_sysctl()
self._service_radvd_start()
if start_radvd:
self._service_radvd_start(prefix, slaac)
else:
self._service_radvd_stop()
def stop(self):
self._service_radvd_stop()
@@ -2121,14 +2163,16 @@ class HostNode(LinuxHost, OtbrDocker):
Returns:
IPv6 address string.
"""
assert address_type == config.ADDRESS_TYPE.BACKBONE_GUA
assert address_type in [config.ADDRESS_TYPE.BACKBONE_GUA, config.ADDRESS_TYPE.ONLINK_ULA]
if address_type == config.ADDRESS_TYPE.BACKBONE_GUA:
return self._getBackboneGua()
if address_type == config.ADDRESS_TYPE.ONLINK_ULA:
return self._getInfraUla()
else:
return None
def _service_radvd_start(self):
def _service_radvd_start(self, prefix, slaac):
self.bash("""cat >/etc/radvd.conf <<EOF
interface eth0
{
@@ -2141,12 +2185,12 @@ interface eth0
prefix %s
{
AdvOnLink on;
AdvAutonomous off;
AdvAutonomous %s;
AdvRouterAddr off;
};
};
EOF
""" % config.DOMAIN_PREFIX)
""" % (prefix, 'on' if slaac else 'off'))
self.bash('service radvd start')
self.bash('service radvd status') # Make sure radvd service is running
+15
View File
@@ -700,4 +700,19 @@ otLinkMetrics otPlatRadioGetEnhAckProbingMetrics(otInstance *aInstance, const ot
}
#endif
#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE
otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex,
const otIp6Address *aDestAddress,
const uint8_t * aBuffer,
uint16_t aBufferLength)
{
OT_UNUSED_VARIABLE(aInfraIfIndex);
OT_UNUSED_VARIABLE(aDestAddress);
OT_UNUSED_VARIABLE(aBuffer);
OT_UNUSED_VARIABLE(aBufferLength);
return OT_ERROR_FAILED;
}
#endif
} // extern "C"