mirror of
https://github.com/espressif/openthread.git
synced 2026-06-06 05:24:51 +00:00
[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:
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_
|
||||
@@ -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
@@ -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)
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
*
|
||||
*/
|
||||
OT_TOOL_PACKED_BEGIN
|
||||
class Prefix : public otIp6Prefix
|
||||
class Prefix : public otIp6Prefix, public Clearable<Prefix>
|
||||
{
|
||||
public:
|
||||
enum : uint8_t
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user