diff --git a/.github/workflows/otbr.yml b/.github/workflows/otbr.yml index e9bc9f3d6..368dc72df 100644 --- a/.github/workflows/otbr.yml +++ b/.github/workflows/otbr.yml @@ -46,7 +46,7 @@ jobs: REFERENCE_DEVICE: 1 VIRTUAL_TIME: 0 PACKET_VERIFICATION: 1 - THREAD_VERSION: 1.2 + THREAD_VERSION: 1.3 INTER_OP: 1 COVERAGE: 1 MULTIPLY: 1 @@ -83,12 +83,12 @@ jobs: sudo -E ./script/test cert_suite ./tests/scripts/thread-cert/backbone/*.py || (sudo chmod a+r *.log *.json *.pcap && false) - uses: actions/upload-artifact@v2 with: - name: cov-thread-1-2-backbone-docker + name: cov-thread-1-3-backbone-docker path: /tmp/coverage/ - uses: actions/upload-artifact@v2 if: ${{ failure() }} with: - name: thread-1-2-backbone-results + name: thread-1-3-backbone-results path: | *.pcap *.json @@ -100,7 +100,7 @@ jobs: ./script/test generate_coverage gcc - uses: actions/upload-artifact@v2 with: - name: cov-thread-1-2-backbone + name: cov-thread-1-3-backbone path: tmp/coverage.info thread-border-router: @@ -144,7 +144,7 @@ jobs: REFERENCE_DEVICE: 1 VIRTUAL_TIME: 0 PACKET_VERIFICATION: ${{ matrix.packet_verification }} - THREAD_VERSION: 1.2 + THREAD_VERSION: 1.3 INTER_OP: 1 COVERAGE: 1 MULTIPLY: 1 @@ -220,7 +220,7 @@ jobs: script/test combine_coverage - name: Upload Coverage continue-on-error: true - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v2 with: files: final.info fail_ci_if_error: true diff --git a/.github/workflows/otci.yml b/.github/workflows/otci.yml index 5023d7c16..a1e3a9b99 100644 --- a/.github/workflows/otci.yml +++ b/.github/workflows/otci.yml @@ -61,7 +61,7 @@ jobs: - name: Build run: | ./bootstrap - make -f examples/Makefile-simulation THREAD_VERSION=1.2 DUA=1 MLR=1 BACKBONE_ROUTER=1 CSL_RECEIVER=1 + make -f examples/Makefile-simulation THREAD_VERSION=1.3 DUA=1 MLR=1 BACKBONE_ROUTER=1 CSL_RECEIVER=1 - name: Install OTCI Python Library run: | (cd tools/otci && python3 setup.py install --user) diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index abe6f0b88..93dd49cc2 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -293,7 +293,7 @@ jobs: run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v2 with: files: final.info fail_ci_if_error: true diff --git a/.github/workflows/simulation-1.1.yml b/.github/workflows/simulation-1.1.yml index a16bf124b..3754c9d3c 100644 --- a/.github/workflows/simulation-1.1.yml +++ b/.github/workflows/simulation-1.1.yml @@ -372,7 +372,7 @@ jobs: run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v2 with: files: final.info fail_ci_if_error: true diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.2.yml index f8bc267ea..26166466d 100644 --- a/.github/workflows/simulation-1.2.yml +++ b/.github/workflows/simulation-1.2.yml @@ -26,7 +26,7 @@ # POSSIBILITY OF SUCH DAMAGE. # -name: Simulation 1.2 +name: Simulation 1.3 on: [push, pull_request] @@ -40,15 +40,15 @@ jobs: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" if: "github.ref != 'refs/heads/main'" - thread-1-2: - name: thread-1-2-${{ matrix.compiler.c }}-${{ matrix.arch }} + thread-1-3: + name: thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }} runs-on: ubuntu-20.04 env: CFLAGS: -${{ matrix.arch }} CXXFLAGS: -${{ matrix.arch }} LDFLAGS: -${{ matrix.arch }} COVERAGE: 1 - THREAD_VERSION: 1.2 + THREAD_VERSION: 1.3 VIRTUAL_TIME: 1 INTER_OP: 1 CC: ${{ matrix.compiler.c }} @@ -87,12 +87,12 @@ jobs: - uses: actions/upload-artifact@v2 if: ${{ failure() }} with: - name: thread-1-2-${{ matrix.compiler.c }}-${{ matrix.arch }}-pcaps + name: thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }}-pcaps path: "*.pcap" - uses: actions/upload-artifact@v2 if: ${{ failure() && env.CRASHED == '1' }} with: - name: core-packet-verification-thread-1-2 + name: core-packet-verification-thread-1-3 path: | ./ot-core-dump/* - name: Generate Coverage @@ -100,7 +100,7 @@ jobs: ./script/test generate_coverage "${{ matrix.compiler.gcov }}" - uses: actions/upload-artifact@v2 with: - name: cov-thread-1-2-${{ matrix.compiler.c }}-${{ matrix.arch }} + name: cov-thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }} path: tmp/coverage.info packet-verification-low-power: @@ -110,7 +110,7 @@ jobs: VIRTUAL_TIME: 1 COVERAGE: 1 PACKET_VERIFICATION: 1 - THREAD_VERSION: 1.2 + THREAD_VERSION: 1.3 MAC_FILTER: 1 INTER_OP: 1 INTER_OP_BBR: 0 @@ -164,13 +164,13 @@ jobs: name: cov-packet-verification-low-power path: tmp/coverage.info - packet-verification-1-1-on-1-2: + packet-verification-1-1-on-1-3: runs-on: ubuntu-20.04 env: REFERENCE_DEVICE: 1 VIRTUAL_TIME: 1 PACKET_VERIFICATION: 1 - THREAD_VERSION: 1.2 + THREAD_VERSION: 1.3 MULTIPLY: 3 steps: - uses: actions/checkout@v2 @@ -193,7 +193,7 @@ jobs: - uses: actions/upload-artifact@v2 if: ${{ failure() }} with: - name: packet-verification-1.1-on-1.2-pcaps + name: packet-verification-1.1-on-1.3-pcaps path: | *.pcap *.json @@ -202,14 +202,14 @@ jobs: ./script/test generate_coverage gcc - uses: actions/upload-artifact@v2 with: - name: cov-packet-verification-1-1-on-1-2 + name: cov-packet-verification-1-1-on-1-3 path: tmp/coverage.info expects: runs-on: ubuntu-20.04 env: COVERAGE: 1 - THREAD_VERSION: 1.2 + THREAD_VERSION: 1.3 VIRTUAL_TIME: 0 steps: - uses: actions/checkout@v2 @@ -232,7 +232,7 @@ jobs: - uses: actions/upload-artifact@v2 if: ${{ failure() && env.CRASHED == '1' }} with: - name: core-expect-1-2 + name: core-expect-1-3 path: | ./ot-core-dump/* - name: Generate Coverage @@ -243,13 +243,13 @@ jobs: name: cov-expects path: tmp/coverage.info - thread-1-2-posix: + thread-1-3-posix: runs-on: ubuntu-20.04 env: COVERAGE: 1 PYTHONUNBUFFERED: 1 READLINE: readline - THREAD_VERSION: 1.2 + THREAD_VERSION: 1.3 OT_NODE_TYPE: rcp USE_MTD: 1 VIRTUAL_TIME: 1 @@ -285,12 +285,12 @@ jobs: - uses: actions/upload-artifact@v2 if: ${{ failure() }} with: - name: thread-1-2-posix-pcaps + name: thread-1-3-posix-pcaps path: "*.pcap" - uses: actions/upload-artifact@v2 if: ${{ failure() && env.CRASHED == '1' }} with: - name: core-thread-1-2-posix + name: core-thread-1-3-posix path: | ./ot-core-dump/* - name: Generate Coverage @@ -298,16 +298,16 @@ jobs: ./script/test generate_coverage gcc - uses: actions/upload-artifact@v2 with: - name: cov-thread-1-2-posix + name: cov-thread-1-3-posix path: tmp/coverage.info upload-coverage: needs: - - thread-1-2 + - thread-1-3 - packet-verification-low-power - - packet-verification-1-1-on-1-2 + - packet-verification-1-1-on-1-3 - expects - - thread-1-2-posix + - thread-1-3-posix runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 @@ -323,7 +323,7 @@ jobs: run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v2 with: files: final.info fail_ci_if_error: true diff --git a/.github/workflows/toranj.yml b/.github/workflows/toranj.yml index 46f9520ef..221913fde 100644 --- a/.github/workflows/toranj.yml +++ b/.github/workflows/toranj.yml @@ -139,7 +139,7 @@ jobs: run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v2 with: files: final.info fail_ci_if_error: true diff --git a/Android.mk b/Android.mk index 4d09fcc91..c1094e6d1 100644 --- a/Android.mk +++ b/Android.mk @@ -220,8 +220,7 @@ 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/infra_if.cpp \ src/core/border_router/routing_manager.cpp \ src/core/coap/coap.cpp \ src/core/coap/coap_message.cpp \ @@ -303,6 +302,7 @@ LOCAL_SRC_FILES := \ src/core/net/ip6_filter.cpp \ src/core/net/ip6_headers.cpp \ src/core/net/ip6_mpl.cpp \ + src/core/net/nd6.cpp \ src/core/net/nd_agent.cpp \ src/core/net/netif.cpp \ src/core/net/sntp_client.cpp \ diff --git a/CMakeLists.txt b/CMakeLists.txt index f311fc921..642332f5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,12 +102,14 @@ if(OT_PACKAGE_VERSION STREQUAL "") endif() message(STATUS "Package Version: ${OT_PACKAGE_VERSION}") -set(OT_THREAD_VERSION "1.2" CACHE STRING "Thread version chosen by the user at configure time") -set_property(CACHE OT_THREAD_VERSION PROPERTY STRINGS "1.1" "1.2") +set(OT_THREAD_VERSION "1.3" CACHE STRING "Thread version chosen by the user at configure time") +set_property(CACHE OT_THREAD_VERSION PROPERTY STRINGS "1.1" "1.2" "1.3") if(${OT_THREAD_VERSION} EQUAL "1.1") target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_1") elseif(${OT_THREAD_VERSION} EQUAL "1.2") target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_2") +elseif(${OT_THREAD_VERSION} EQUAL "1.3") + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_3") else() message(FATAL_ERROR "Thread version unknown: ${OT_THREAD_VERSION}") endif() diff --git a/doc/ot_api_doc.h b/doc/ot_api_doc.h index f0762eb0c..b231833e2 100644 --- a/doc/ot_api_doc.h +++ b/doc/ot_api_doc.h @@ -59,7 +59,16 @@ * @defgroup api-ip6 IPv6 * @defgroup api-srp SRP * @defgroup api-ping-sender Ping Sender + * + * @defgroup api-tcp-group TCP + * + * @{ + * * @defgroup api-tcp TCP + * @defgroup api-tcp-ext TCP Abstractions + * + * @} + * * @defgroup api-udp-group UDP * * @{ diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index 6eec0d623..d35b5d411 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -360,6 +360,11 @@ if (OT_TREL) target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE=1") endif() +option(OT_TX_BEACON_PAYLOAD "enable Thread beacon payload in outgoing beacons") +if (OT_TX_BEACON_PAYLOAD) + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE=1") +endif() + option(OT_UDP_FORWARD "enable UDP forward support") if(OT_UDP_FORWARD) target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE=1") diff --git a/examples/apps/cli/radio.cmake b/examples/apps/cli/radio.cmake index dc3e4e981..4302adca5 100644 --- a/examples/apps/cli/radio.cmake +++ b/examples/apps/cli/radio.cmake @@ -37,13 +37,17 @@ if(NOT DEFINED OT_PLATFORM_LIB_RCP) set(OT_PLATFORM_LIB_RCP ${OT_PLATFORM_LIB}) endif() +if(NOT DEFINED OT_MBEDTLS_RCP) + set(OT_MBEDTLS_RCP ${OT_MBEDTLS}) +endif() + target_link_libraries(ot-cli-radio PRIVATE openthread-cli-radio ${OT_PLATFORM_LIB_RCP} openthread-radio-cli ${OT_PLATFORM_LIB_RCP} openthread-cli-radio - ${OT_MBEDTLS} + ${OT_MBEDTLS_RCP} ot-config ) diff --git a/examples/common-switches.mk b/examples/common-switches.mk index 2e947b0df..ac05ccd6c 100644 --- a/examples/common-switches.mk +++ b/examples/common-switches.mk @@ -86,7 +86,7 @@ SLAAC ?= 1 SNTP_CLIENT ?= 0 SRP_CLIENT ?= 0 SRP_SERVER ?= 0 -THREAD_VERSION ?= 1.2 +THREAD_VERSION ?= 1.3 TIME_SYNC ?= 0 TREL ?= 0 UDP_FORWARD ?= 0 @@ -333,6 +333,8 @@ ifeq ($(THREAD_VERSION),1.1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_THREAD_VERSION=2 else ifeq ($(THREAD_VERSION),1.2) COMMONCFLAGS += -DOPENTHREAD_CONFIG_THREAD_VERSION=3 +else ifeq ($(THREAD_VERSION),1.3) +COMMONCFLAGS += -DOPENTHREAD_CONFIG_THREAD_VERSION=4 endif ifeq ($(TIME_SYNC),1) diff --git a/examples/platforms/simulation/platform-simulation.h b/examples/platforms/simulation/platform-simulation.h index 4e7970b22..9379421d4 100644 --- a/examples/platforms/simulation/platform-simulation.h +++ b/examples/platforms/simulation/platform-simulation.h @@ -163,10 +163,11 @@ void platformRadioReceive(otInstance *aInstance, uint8_t *aBuf, uint16_t aBufLen * * @param[in,out] aReadFdSet A pointer to the read file descriptors. * @param[in,out] aWriteFdSet A pointer to the write file descriptors. + * @param[in,out] aTimeout A pointer to the timeout. * @param[in,out] aMaxFd A pointer to the max file descriptor. * */ -void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd); +void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd); /** * This function performs radio driver processing. diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c index 2faaa2f5f..2b627c2f2 100644 --- a/examples/platforms/simulation/radio.c +++ b/examples/platforms/simulation/radio.c @@ -29,6 +29,7 @@ #include "platform-simulation.h" #include +#include #include #include @@ -47,6 +48,9 @@ // The IPv4 group for receiving packets of radio simulation #define OT_RADIO_GROUP "224.0.0.116" +#define MS_PER_S 1000 +#define US_PER_MS 1000 + enum { IEEE802154_ACK_LENGTH = 5, @@ -75,6 +79,10 @@ static uint16_t sPortOffset = 0; static uint16_t sPort = 0; #endif +static int8_t sEnergyScanResult = OT_RADIO_RSSI_INVALID; +static bool sEnergyScanning = false; +static uint32_t sEnergyScanEndTime = 0; + enum { SIM_RADIO_CHANNEL_MIN = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN, @@ -156,6 +164,13 @@ static otMacKeyMaterial sCurrKey; static otMacKeyMaterial sNextKey; static otRadioKeyType sKeyType; +static int8_t GetRssi(uint16_t aChannel); + +static bool IsTimeAfterOrEqual(uint32_t aTimeA, uint32_t aTimeB) +{ + return (aTimeA - aTimeB) < (1U << 31); +} + static void ReverseExtAddress(otExtAddress *aReversed, const otExtAddress *aOrigin) { for (size_t i = 0; i < sizeof(*aReversed); i++) @@ -493,20 +508,23 @@ otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) int8_t otPlatRadioGetRssi(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); - assert(aInstance != NULL); - int8_t rssi = SIM_LOW_RSSI_SAMPLE; - uint8_t channel = sReceiveFrame.mChannel; + return GetRssi(sReceiveFrame.mChannel); +} + +static int8_t GetRssi(uint16_t aChannel) +{ + int8_t rssi = SIM_LOW_RSSI_SAMPLE; uint32_t probabilityThreshold; - otEXPECT((SIM_RADIO_CHANNEL_MIN <= channel) && channel <= (SIM_RADIO_CHANNEL_MAX)); + otEXPECT((SIM_RADIO_CHANNEL_MIN <= aChannel) && aChannel <= (SIM_RADIO_CHANNEL_MAX)); // To emulate a simple interference model, we return either a high or // a low RSSI value with a fixed probability per each channel. The // probability is increased per channel by a constant. - probabilityThreshold = (channel - SIM_RADIO_CHANNEL_MIN) * SIM_HIGH_RSSI_PROB_INC_PER_CHANNEL; + probabilityThreshold = (aChannel - SIM_RADIO_CHANNEL_MIN) * SIM_HIGH_RSSI_PROB_INC_PER_CHANNEL; if (otRandomNonCryptoGetUint16() < (probabilityThreshold * 0xffff / 100)) { @@ -736,7 +754,7 @@ void platformRadioReceive(otInstance *aInstance, uint8_t *aBuf, uint16_t aBufLen radioReceive(aInstance); } #else -void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMaxFd) +void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd) { if (aReadFdSet != NULL && (sState != OT_RADIO_STATE_TRANSMIT || sTxWait)) { @@ -757,6 +775,25 @@ void platformRadioUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, int *aMax *aMaxFd = sTxFd; } } + + if (sEnergyScanning) + { + struct timeval tv = {0, 0}; + uint32_t now = otPlatAlarmMilliGetNow(); + + if (IsTimeAfterOrEqual(sEnergyScanEndTime, now)) + { + uint32_t remaining = sEnergyScanEndTime - now; + + tv.tv_sec = remaining / MS_PER_S; + tv.tv_usec = (remaining % MS_PER_S) * US_PER_MS; + } + + if (timercmp(&tv, aTimeout, <)) + { + *aTimeout = tv; + } + } } // no need to close in virtual time mode. @@ -811,11 +848,16 @@ void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const } } #endif - if (platformRadioIsTransmitPending()) { radioSendMessage(aInstance); } + + if (sEnergyScanning && IsTimeAfterOrEqual(otPlatAlarmMilliGetNow(), sEnergyScanEndTime)) + { + sEnergyScanning = false; + otPlatRadioEnergyScanDone(aInstance, sEnergyScanResult); + } } void radioTransmit(struct RadioMessage *aMessage, const struct otRadioFrame *aFrame) @@ -982,13 +1024,22 @@ otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint1 { OT_UNUSED_VARIABLE(aInstance); OT_UNUSED_VARIABLE(aScanChannel); - OT_UNUSED_VARIABLE(aScanDuration); + + otError error = OT_ERROR_NONE; assert(aInstance != NULL); assert(aScanChannel >= SIM_RADIO_CHANNEL_MIN && aScanChannel <= SIM_RADIO_CHANNEL_MAX); assert(aScanDuration > 0); - return OT_ERROR_NOT_IMPLEMENTED; + otEXPECT_ACTION((gRadioCaps & OT_RADIO_CAPS_ENERGY_SCAN), error = OT_ERROR_NOT_IMPLEMENTED); + otEXPECT_ACTION(!sEnergyScanning, error = OT_ERROR_BUSY); + + sEnergyScanResult = GetRssi(aScanChannel); + sEnergyScanning = true; + sEnergyScanEndTime = otPlatAlarmMilliGetNow() + aScanDuration; + +exit: + return error; } otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower) diff --git a/examples/platforms/simulation/system.c b/examples/platforms/simulation/system.c index 57bb30cd4..f9282a64a 100644 --- a/examples/platforms/simulation/system.c +++ b/examples/platforms/simulation/system.c @@ -70,10 +70,11 @@ static void handleSignal(int aSignal) */ enum { - OT_SIM_OPT_HELP = 'h', - OT_SIM_OPT_SLEEP_TO_TX = 't', - OT_SIM_OPT_TIME_SPEED = 's', - OT_SIM_OPT_UNKNOWN = '?', + OT_SIM_OPT_HELP = 'h', + OT_SIM_OPT_ENABLE_ENERGY_SCAN = 'E', + OT_SIM_OPT_SLEEP_TO_TX = 't', + OT_SIM_OPT_TIME_SPEED = 's', + OT_SIM_OPT_UNKNOWN = '?', }; static void PrintUsage(const char *aProgramName, int aExitCode) @@ -82,9 +83,10 @@ static void PrintUsage(const char *aProgramName, int aExitCode) "Syntax:\n" " %s [Options] NodeId\n" "Options:\n" - " -h --help Display this usage information.\n" - " -t --sleep-to-tx Let radio support direct transition from sleep to TX with CSMA.\n" - " -s --time-speed=val Speed up the time in simulation.\n", + " -h --help Display this usage information.\n" + " -E --enable-energy-scan Enable energy scan capability.\n" + " -t --sleep-to-tx Let radio support direct transition from sleep to TX with CSMA.\n" + " -s --time-speed=val Speed up the time in simulation.\n", aProgramName); exit(aExitCode); @@ -97,6 +99,7 @@ void otSysInit(int aArgCount, char *aArgVector[]) static const struct option long_options[] = { {"help", no_argument, 0, OT_SIM_OPT_HELP}, + {"enable-energy-scan", no_argument, 0, OT_SIM_OPT_SLEEP_TO_TX}, {"sleep-to-tx", no_argument, 0, OT_SIM_OPT_SLEEP_TO_TX}, {"time-speed", required_argument, 0, OT_SIM_OPT_TIME_SPEED}, {0, 0, 0, 0}, @@ -112,7 +115,7 @@ void otSysInit(int aArgCount, char *aArgVector[]) while (true) { - int c = getopt_long(aArgCount, aArgVector, "hts:", long_options, NULL); + int c = getopt_long(aArgCount, aArgVector, "Ehts:", long_options, NULL); if (c == -1) { @@ -127,6 +130,9 @@ void otSysInit(int aArgCount, char *aArgVector[]) case OT_SIM_OPT_HELP: PrintUsage(aArgVector[0], EXIT_SUCCESS); break; + case OT_SIM_OPT_ENABLE_ENERGY_SCAN: + gRadioCaps |= OT_RADIO_CAPS_ENERGY_SCAN; + break; case OT_SIM_OPT_SLEEP_TO_TX: gRadioCaps |= OT_RADIO_CAPS_SLEEP_TO_TX; break; @@ -197,8 +203,8 @@ void otSysProcessDrivers(otInstance *aInstance) FD_ZERO(&error_fds); platformUartUpdateFdSet(&read_fds, &write_fds, &error_fds, &max_fd); - platformRadioUpdateFdSet(&read_fds, &write_fds, &max_fd); platformAlarmUpdateTimeout(&timeout); + platformRadioUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd); #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE platformTrelUpdateFdSet(&read_fds, &write_fds, &timeout, &max_fd); #endif diff --git a/include/openthread/dataset.h b/include/openthread/dataset.h index a92f3a78c..63e1e26ad 100644 --- a/include/openthread/dataset.h +++ b/include/openthread/dataset.h @@ -213,6 +213,17 @@ typedef struct otOperationalDatasetComponents bool mIsChannelMaskPresent : 1; ///< TRUE if Channel Mask is present, FALSE otherwise. } otOperationalDatasetComponents; +/** + * This structure represents a Thread Dataset timestamp component. + * + */ +typedef struct otTimestamp +{ + uint64_t mSeconds; + uint16_t mTicks; + bool mAuthoritative; +} otTimestamp; + /** * This structure represents an Active or Pending Operational Dataset. * @@ -221,8 +232,8 @@ typedef struct otOperationalDatasetComponents */ typedef struct otOperationalDataset { - uint64_t mActiveTimestamp; ///< Active Timestamp - uint64_t mPendingTimestamp; ///< Pending Timestamp + otTimestamp mActiveTimestamp; ///< Active Timestamp + otTimestamp mPendingTimestamp; ///< Pending Timestamp otNetworkKey mNetworkKey; ///< Network Key otNetworkName mNetworkName; ///< Network Name otExtendedPanId mExtendedPanId; ///< Extended PAN ID diff --git a/include/openthread/dnssd_server.h b/include/openthread/dnssd_server.h index abcac362e..73666981a 100644 --- a/include/openthread/dnssd_server.h +++ b/include/openthread/dnssd_server.h @@ -147,6 +147,22 @@ typedef enum OT_DNSSD_QUERY_TYPE_RESOLVE_HOST = 3, ///< Service type resolve hostname. } otDnssdQueryType; +/** + * This structure contains the counters of DNS-SD server. + * + */ +typedef struct otDnssdCounters +{ + uint32_t mSuccessResponse; ///< The number of successful responses + uint32_t mServerFailureResponse; ///< The number of server failure responses + uint32_t mFormatErrorResponse; ///< The number of format error responses + uint32_t mNameErrorResponse; ///< The number of name error responses + uint32_t mNotImplementedResponse; ///< The number of 'not implemented' responses + uint32_t mOtherResponse; ///< The number of other responses + + uint32_t mResolvedBySrp; ///< The number of queries completely resolved by the local SRP server +} otDnssdCounters; + /** * This function sets DNS-SD server query callbacks. * @@ -219,6 +235,16 @@ const otDnssdQuery *otDnssdGetNextQuery(otInstance *aInstance, const otDnssdQuer */ otDnssdQueryType otDnssdGetQueryTypeAndName(const otDnssdQuery *aQuery, char (*aNameOutput)[OT_DNS_MAX_NAME_SIZE]); +/** + * This function returns the counters of the DNS-SD server. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @returns A pointer to the counters of the DNS-SD server. + * + */ +const otDnssdCounters *otDnssdGetCounters(otInstance *aInstance); + /** * @} * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 1a45a97e2..ab4765afc 100644 --- a/include/openthread/instance.h +++ b/include/openthread/instance.h @@ -53,7 +53,7 @@ extern "C" { * @note This number versions both OpenThread platform and user APIs. * */ -#define OPENTHREAD_API_VERSION (207) +#define OPENTHREAD_API_VERSION (220) /** * @addtogroup api-instance diff --git a/include/openthread/link.h b/include/openthread/link.h index 4be9a79a7..0a7280cee 100644 --- a/include/openthread/link.h +++ b/include/openthread/link.h @@ -391,6 +391,10 @@ typedef struct otActiveScanResult unsigned int mVersion : 4; ///< Version bool mIsNative : 1; ///< Native Commissioner flag bool mDiscover : 1; ///< Result from MLE Discovery + + // Applicable/Required only when beacon payload parsing feature + // (`OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE`) is enabled. + bool mIsJoinable : 1; ///< Joining Permitted flag } otActiveScanResult; /** @@ -486,7 +490,6 @@ bool otLinkIsEnergyScanInProgress(otInstance *aInstance); * @param[in] aInstance A pointer to an OpenThread instance. * * @retval OT_ERROR_NONE Successfully enqueued an IEEE 802.15.4 Data Request message. - * @retval OT_ERROR_ALREADY An IEEE 802.15.4 Data Request message is already enqueued. * @retval OT_ERROR_INVALID_STATE Device is not in rx-off-when-idle mode. * @retval OT_ERROR_NO_BUFS Insufficient message buffers available. * diff --git a/include/openthread/link_raw.h b/include/openthread/link_raw.h index 116fea654..02293e990 100644 --- a/include/openthread/link_raw.h +++ b/include/openthread/link_raw.h @@ -236,6 +236,7 @@ typedef void (*otLinkRawEnergyScanDone)(otInstance *aInstance, int8_t aEnergySca * @param[in] aCallback A pointer to a function called on completion of a scanned channel. * * @retval OT_ERROR_NONE Successfully started scanning the channel. + * @retval OT_ERROR_BUSY The radio is performing enery scanning. * @retval OT_ERROR_NOT_IMPLEMENTED The radio doesn't support energy scanning. * @retval OT_ERROR_INVALID_STATE If the raw link-layer isn't enabled. * diff --git a/include/openthread/netdata_publisher.h b/include/openthread/netdata_publisher.h index fa44686be..8b4af3e8d 100644 --- a/include/openthread/netdata_publisher.h +++ b/include/openthread/netdata_publisher.h @@ -192,12 +192,18 @@ void otNetDataUnpublishDnsSrpService(otInstance *aInstance); * * Only stable entries can be published (i.e.,`aConfig.mStable` MUST be TRUE). * + * A subsequent call to this method will replace a previous request for the same prefix. In particular, if the new call + * only changes the flags (e.g., preference level) and the prefix is already added in the Network Data, the change to + * flags is immediately reflected in the Network Data. This ensures that existing entries in the Network Data are not + * abruptly removed. Note that a change in the preference level can potentially later cause the entry to be removed + * from the Network Data after determining there are other nodes that are publishing the same prefix with the same or + * higher preference. + * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aConfig The on-mesh prefix config to publish (MUST NOT be NULL). * * @retval OT_ERROR_NONE The on-mesh prefix is published successfully. * @retval OT_ERROR_INVALID_ARGS The @p aConfig is not valid (bad prefix, invalid flag combinations, or not stable). - * @retval OT_ERROR_ALREADY An entry with the same prefix is already in the published list. * @retval OT_ERROR_NO_BUFS Could not allocate an entry for the new request. Publisher supports a limited number * of entries (shared between on-mesh prefix and external route) determined by config * `OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES`. @@ -213,12 +219,18 @@ otError otNetDataPublishOnMeshPrefix(otInstance *aInstance, const otBorderRouter * * Only stable entries can be published (i.e.,`aConfig.mStable` MUST be TRUE). * + * A subsequent call to this method will replace a previous request for the same prefix. In particular, if the new call + * only changes the flags (e.g., preference level) and the prefix is already added in the Network Data, the change to + * flags is immediately reflected in the Network Data. This ensures that existing entries in the Network Data are not + * abruptly removed. Note that a change in the preference level can potentially later cause the entry to be removed + * from the Network Data after determining there are other nodes that are publishing the same prefix with the same or + * higher preference. + * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aConfig The external route config to publish (MUST NOT be NULL). * * @retval OT_ERROR_NONE The external route is published successfully. * @retval OT_ERROR_INVALID_ARGS The @p aConfig is not valid (bad prefix, invalid flag combinations, or not stable). - * @retval OT_ERROR_ALREADY An entry with the same prefix is already in the published list. * @retval OT_ERROR_NO_BUFS Could not allocate an entry for the new request. Publisher supports a limited number * of entries (shared between on-mesh prefix and external route) determined by config * `OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES`. diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index ba7314ebb..e3d0dda25 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -857,6 +857,7 @@ int8_t otPlatRadioGetRssi(otInstance *aInstance); * @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned. * * @retval OT_ERROR_NONE Successfully started scanning the channel. + * @retval OT_ERROR_BUSY The radio is performing enery scanning. * @retval OT_ERROR_NOT_IMPLEMENTED The radio doesn't support energy scanning. * */ diff --git a/include/openthread/platform/settings.h b/include/openthread/platform/settings.h index a5d609965..2a4892baa 100644 --- a/include/openthread/platform/settings.h +++ b/include/openthread/platform/settings.h @@ -55,7 +55,8 @@ extern "C" { * This enumeration defines the keys of settings. * * Note: When adding a new settings key, if the settings corresponding to the key contains security sensitive - * information, the developer MUST add the key to the array `kSensitiveKeys`. + * information, the developer MUST add the key to the array `aSensitiveKeys` which is passed in + * `otPlatSettingsInit()`. * */ enum @@ -65,17 +66,20 @@ enum OT_SETTINGS_KEY_NETWORK_INFO = 0x0003, ///< Thread network information. OT_SETTINGS_KEY_PARENT_INFO = 0x0004, ///< Parent information. OT_SETTINGS_KEY_CHILD_INFO = 0x0005, ///< Child information. - OT_SETTINGS_KEY_RESERVED = 0x0006, ///< Reserved (previously auto-start). OT_SETTINGS_KEY_SLAAC_IID_SECRET_KEY = 0x0007, ///< SLAAC key to generate semantically opaque IID. OT_SETTINGS_KEY_DAD_INFO = 0x0008, ///< Duplicate Address Detection (DAD) information. - OT_SETTINGS_KEY_LEGACY_OMR_PREFIX = 0x0009, ///< Reserved. Legacy Off-mesh routable (OMR) prefix. - OT_SETTINGS_KEY_ON_LINK_PREFIX = 0x000a, ///< On-link prefix for infrastructure link. OT_SETTINGS_KEY_SRP_ECDSA_KEY = 0x000b, ///< SRP client ECDSA public/private key pair. OT_SETTINGS_KEY_SRP_CLIENT_INFO = 0x000c, ///< The SRP client info (selected SRP server address). OT_SETTINGS_KEY_SRP_SERVER_INFO = 0x000d, ///< The SRP server info (UDP port). - OT_SETTINGS_KEY_LEGACY_NAT64_PREFIX = 0x000e, ///< Reserved. Legacy NAT64 prefix. OT_SETTINGS_KEY_BR_ULA_PREFIX = 0x000f, ///< BR ULA prefix. + // Deprecated and reserved key values: + // + // 0x0006 previously auto-start. + // 0x0009 previously OMR prefix. + // 0x000a previously on-link prefix. + // 0x000e previously NAT64 prefix. + // Keys in range 0x8000-0xffff are reserved for vendor-specific use. OT_SETTINGS_KEY_VENDOR_RESERVED_MIN = 0x8000, OT_SETTINGS_KEY_VENDOR_RESERVED_MAX = 0xffff, @@ -104,80 +108,100 @@ void otPlatSettingsInit(otInstance *aInstance, const uint16_t *aSensitiveKeys, u */ void otPlatSettingsDeinit(otInstance *aInstance); -/// Fetches the value of a setting -/** This function fetches the value of the setting identified - * by aKey and write it to the memory pointed to by aValue. - * It then writes the length to the integer pointed to by - * aValueLength. The initial value of aValueLength is the - * maximum number of bytes to be written to aValue. +/** + * Fetches the value of a setting. * - * This function can be used to check for the existence of - * a key without fetching the value by setting aValue and - * aValueLength to NULL. You can also check the length of - * the setting without fetching it by setting only aValue - * to NULL. + * This function fetches the value of the setting identified + * by @p aKey and write it to the memory pointed to by aValue. + * It then writes the length to the integer pointed to by + * @p aValueLength. The initial value of @p aValueLength is the + * maximum number of bytes to be written to @p aValue. * - * Note that the underlying storage implementation is not - * required to maintain the order of settings with multiple - * values. The order of such values MAY change after ANY - * write operation to the store. + * This function can be used to check for the existence of + * a key without fetching the value by setting @p aValue and + * @p aValueLength to NULL. You can also check the length of + * the setting without fetching it by setting only aValue + * to NULL. * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aKey The key associated with the requested setting. - * @param[in] aIndex The index of the specific item to get. - * @param[out] aValue A pointer to where the value of the setting should be written. May be set to NULL if - * just testing for the presence or length of a setting. - * @param[in,out] aValueLength A pointer to the length of the value. When called, this pointer should point to an - * integer containing the maximum value size that can be written to aValue. At return, - * the actual length of the setting is written. This may be set to NULL if performing - * a presence check. + * Note that the underlying storage implementation is not + * required to maintain the order of settings with multiple + * values. The order of such values MAY change after ANY + * write operation to the store. * - * @retval OT_ERROR_NONE The given setting was found and fetched successfully. - * @retval OT_ERROR_NOT_FOUND The given setting was not found in the setting store. - * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aKey The key associated with the requested setting. + * @param[in] aIndex The index of the specific item to get. + * @param[out] aValue A pointer to where the value of the setting should be written. May be set to NULL if + * just testing for the presence or length of a setting. + * @param[in,out] aValueLength A pointer to the length of the value. When called, this pointer should point to an + * integer containing the maximum value size that can be written to @p aValue. At return, + * the actual length of the setting is written. This may be set to NULL if performing + * a presence check. + * + * @retval OT_ERROR_NONE The given setting was found and fetched successfully. + * @retval OT_ERROR_NOT_FOUND The given setting was not found in the setting store. + * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. */ otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength); -/// Sets or replaces the value of a setting -/** This function sets or replaces the value of a setting - * identified by aKey. If there was more than one - * value previously associated with aKey, then they are - * all deleted and replaced with this single entry. +/** + * Sets or replaces the value of a setting. * - * Calling this function successfully may cause unrelated - * settings with multiple values to be reordered. + * This function sets or replaces the value of a setting + * identified by @p aKey. * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aKey The key associated with the setting to change. - * @param[in] aValue A pointer to where the new value of the setting should be read from. MUST NOT be NULL if - * aValueLength is non-zero. - * @param[in] aValueLength The length of the data pointed to by aValue. May be zero. + * Calling this function successfully may cause unrelated + * settings with multiple values to be reordered. * - * @retval OT_ERROR_NONE The given setting was changed or staged. - * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. - * @retval OT_ERROR_NO_BUFS No space remaining to store the given setting. + * OpenThread stack guarantees to use `otPlatSettingsSet()` + * method for a @p aKey that was either previously set using + * `otPlatSettingsSet()` (i.e., contains a single value) or + * is empty and/or fully deleted (contains no value). + * + * Platform layer can rely and use this fact for optimizing + * its implementation. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aKey The key associated with the setting to change. + * @param[in] aValue A pointer to where the new value of the setting should be read from. MUST NOT be NULL if + * @p aValueLength is non-zero. + * @param[in] aValueLength The length of the data pointed to by aValue. May be zero. + * + * @retval OT_ERROR_NONE The given setting was changed or staged. + * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. + * @retval OT_ERROR_NO_BUFS No space remaining to store the given setting. */ otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength); -/// Adds a value to a setting -/** This function adds the value to a setting - * identified by aKey, without replacing any existing - * values. +/** + * Adds a value to a setting. * - * Note that the underlying implementation is not required - * to maintain the order of the items associated with a - * specific key. The added value may be added to the end, - * the beginning, or even somewhere in the middle. The order - * of any pre-existing values may also change. + * This function adds the value to a setting + * identified by @p aKey, without replacing any existing + * values. * - * Calling this function successfully may cause unrelated - * settings with multiple values to be reordered. + * Note that the underlying implementation is not required + * to maintain the order of the items associated with a + * specific key. The added value may be added to the end, + * the beginning, or even somewhere in the middle. The order + * of any pre-existing values may also change. + * + * Calling this function successfully may cause unrelated + * settings with multiple values to be reordered. + * + * OpenThread stack guarantees to use `otPlatSettingsAdd()` + * method for a @p aKey that was either previously managed by + * `otPlatSettingsAdd()` (i.e., contains one or more items) or + * is empty and/or fully deleted (contains no value). + * + * Platform layer can rely and use this fact for optimizing + * its implementation. * * @param[in] aInstance The OpenThread instance structure. * @param[in] aKey The key associated with the setting to change. * @param[in] aValue A pointer to where the new value of the setting should be read from. MUST NOT be NULL - * if aValueLength is non-zero. - * @param[in] aValueLength The length of the data pointed to by aValue. May be zero. + * if @p aValueLength is non-zero. + * @param[in] aValueLength The length of the data pointed to by @p aValue. May be zero. * * @retval OT_ERROR_NONE The given setting was added or staged to be added. * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. @@ -185,29 +209,34 @@ otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, const uint8_t *a */ otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength); -/// Removes a setting from the setting store -/** This function deletes a specific value from the - * setting identified by aKey from the settings store. +/** + * Removes a setting from the setting store. * - * Note that the underlying implementation is not required - * to maintain the order of the items associated with a - * specific key. + * This function deletes a specific value from the + * setting identified by aKey from the settings store. * - * @param[in] aInstance The OpenThread instance structure. - * @param[in] aKey The key associated with the requested setting. - * @param[in] aIndex The index of the value to be removed. If set to -1, all values for this aKey will be removed. + * Note that the underlying implementation is not required + * to maintain the order of the items associated with a + * specific key. * - * @retval OT_ERROR_NONE The given key and index was found and removed successfully. - * @retval OT_ERROR_NOT_FOUND The given key or index was not found in the setting store. - * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aKey The key associated with the requested setting. + * @param[in] aIndex The index of the value to be removed. If set to -1, all values for this @p aKey will be + * removed. + * + * @retval OT_ERROR_NONE The given key and index was found and removed successfully. + * @retval OT_ERROR_NOT_FOUND The given key or index was not found in the setting store. + * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented on this platform. */ otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex); -/// Removes all settings from the setting store -/** This function deletes all settings from the settings - * store, resetting it to its initial factory state. +/** + * Removes all settings from the setting store. * - * @param[in] aInstance The OpenThread instance structure. + * This function deletes all settings from the settings + * store, resetting it to its initial factory state. + * + * @param[in] aInstance The OpenThread instance structure. */ void otPlatSettingsWipe(otInstance *aInstance); diff --git a/include/openthread/srp_client.h b/include/openthread/srp_client.h index 72c03610a..bb291d414 100644 --- a/include/openthread/srp_client.h +++ b/include/openthread/srp_client.h @@ -75,8 +75,9 @@ typedef enum typedef struct otSrpClientHostInfo { const char * mName; ///< Host name (label) string (NULL if not yet set). - const otIp6Address * mAddresses; ///< Pointer to an array of host IPv6 addresses (NULL if not yet set). + const otIp6Address * mAddresses; ///< Array of host IPv6 addresses (NULL if not set or auto address is enabled). uint8_t mNumAddresses; ///< Number of IPv6 addresses in `mAddresses` array. + bool mAutoAddress; ///< Indicates whether auto address mode is enabled or not. otSrpClientItemState mState; ///< Host info state. } otSrpClientHostInfo; @@ -315,6 +316,34 @@ void otSrpClientDisableAutoStartMode(otInstance *aInstance); */ bool otSrpClientIsAutoStartModeEnabled(otInstance *aInstance); +/** + * This function gets the TTL value in every record included in SRP update requests. + * + * Note that this is the TTL requested by the SRP client. The server may choose to accept a different TTL. + * + * By default, the TTL will equal the lease interval. Passing 0 or a value larger than the lease interval via + * `otSrpClientSetTtl()` will also cause the TTL to equal the lease interval. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * + * @returns The TTL (in seconds). + * + */ +uint32_t otSrpClientGetTtl(otInstance *aInstance); + +/** + * This function sets the TTL value in every record included in SRP update requests. + * + * Changing the TTL does not impact the TTL of already registered services/host-info. + * It only affects future SRP update messages (i.e., adding new services and/or refreshes of the existing services). + * + * @param[in] aInstance A pointer to the OpenThread instance. + * @param[in] aTtl The TTL (in seconds). If value is zero or greater than lease interval, the TTL is set to the + * lease interval. + * + */ +void otSrpClientSetTtl(otInstance *aInstance, uint32_t aTtl); + /** * This function gets the lease interval used in SRP update requests. * @@ -400,6 +429,27 @@ const otSrpClientHostInfo *otSrpClientGetHostInfo(otInstance *aInstance); */ otError otSrpClientSetHostName(otInstance *aInstance, const char *aName); +/** + * This function enables auto host address mode. + * + * When enabled host IPv6 addresses are automatically set by SRP client using all the unicast addresses on Thread netif + * excluding all link-local and mesh-local addresses. If there is no valid address, then Mesh Local EID address is + * added. The SRP client will automatically re-register when/if addresses on Thread netif are updated (new addresses + * are added or existing addresses are removed). + * + * The auto host address mode can be enabled before start or during operation of SRP client except when the host info + * is being removed (client is busy handling a remove request from an call to `otSrpClientRemoveHostAndServices()` and + * host info still being in either `STATE_TO_REMOVE` or `STATE_REMOVING` states). + * + * After auto host address mode is enabled, it can be disabled by a call to `otSrpClientSetHostAddresses()` which + * then explicitly sets the host addresses. + * + * @retval OT_ERROR_NONE Successfully enabled auto host address mode. + * @retval OT_ERROR_INVALID_STATE Host is being removed and therefore cannot enable auto host address mode. + * + */ +otError otSrpClientEnableAutoHostAddress(otInstance *aInstance); + /** * This function sets/updates the list of host IPv6 address. * @@ -414,6 +464,9 @@ otError otSrpClientSetHostName(otInstance *aInstance, const char *aName); * After a successful call to this function, `otSrpClientCallback` will be called to report the status of the address * registration with SRP server. * + * Calling this function disables auto host address mode if it was previously enabled from a successful call to + * `otSrpClientEnableAutoHostAddress()`. + * * @param[in] aInstance A pointer to the OpenThread instance. * @param[in] aIp6Addresses A pointer to the an array containing the host IPv6 addresses. * @param[in] aNumAddresses The number of addresses in the @p aIp6Addresses array. diff --git a/include/openthread/srp_server.h b/include/openthread/srp_server.h index 1eceb2b4d..fba71749b 100644 --- a/include/openthread/srp_server.h +++ b/include/openthread/srp_server.h @@ -155,6 +155,16 @@ typedef enum otSrpServerAddressMode OT_SRP_SERVER_ADDRESS_MODE_ANYCAST = 1, ///< Anycast address mode. } otSrpServerAddressMode; +/** + * This structure includes SRP server TTL configurations. + * + */ +typedef struct otSrpServerTtlConfig +{ + uint32_t mMinTtl; ///< The minimum TTL in seconds. + uint32_t mMaxTtl; ///< The maximum TTL in seconds. +} otSrpServerTtlConfig; + /** * This structure includes SRP server LEASE and KEY-LEASE configurations. * @@ -167,6 +177,32 @@ typedef struct otSrpServerLeaseConfig uint32_t mMaxKeyLease; ///< The maximum KEY-LEASE interval in seconds. } otSrpServerLeaseConfig; +/** + * This structure includes SRP server lease information of a host/service. + * + */ +typedef struct otSrpServerLeaseInfo +{ + uint32_t mLease; ///< The lease time of a host/service in milliseconds. + uint32_t mKeyLease; ///< The key lease time of a host/service in milliseconds. + uint32_t mRemainingLease; ///< The remaining lease time of the host/service in milliseconds. + uint32_t mRemainingKeyLease; ///< The remaining key lease time of a host/service in milliseconds. +} otSrpServerLeaseInfo; + +/** + * This structure includes the statistics of SRP server responses. + * + */ +typedef struct otSrpServerResponseCounters +{ + uint32_t mSuccess; ///< The number of successful responses. + uint32_t mServerFailure; ///< The number of server failure responses. + uint32_t mFormatError; ///< The number of format error responses. + uint32_t mNameExists; ///< The number of 'name exists' responses. + uint32_t mRefused; ///< The number of refused responses. + uint32_t mOther; ///< The number of other responses. +} otSrpServerResponseCounters; + /** * This function returns the domain authorized to the SRP server. * @@ -207,6 +243,16 @@ otError otSrpServerSetDomain(otInstance *aInstance, const char *aDomain); */ otSrpServerState otSrpServerGetState(otInstance *aInstance); +/** + * This function returns the port the SRP server is listening to. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The port of the SRP server. It returns 0 if the server is not running. + * + */ +uint16_t otSrpServerGetPort(otInstance *aInstance); + /** * This function returns the address mode being used by the SRP server. * @@ -262,6 +308,30 @@ otError otSrpServerSetAnycastModeSequenceNumber(otInstance *aInstance, uint8_t a */ void otSrpServerSetEnabled(otInstance *aInstance, bool aEnabled); +/** + * This function returns SRP server TTL configuration. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[out] aTtlConfig A pointer to an `otSrpServerTtlConfig` instance. + * + */ +void otSrpServerGetTtlConfig(otInstance *aInstance, otSrpServerTtlConfig *aTtlConfig); + +/** + * This function sets SRP server TTL configuration. + * + * The granted TTL will always be no greater than the max lease interval configured via `otSrpServerSetLeaseConfig()`, + * regardless of the minimum and maximum TTL configuration. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aTtlConfig A pointer to an `otSrpServerTtlConfig` instance. + * + * @retval OT_ERROR_NONE Successfully set the TTL configuration. + * @retval OT_ERROR_INVALID_ARGS The TTL configuration is not valid. + * + */ +otError otSrpServerSetTtlConfig(otInstance *aInstance, const otSrpServerTtlConfig *aTtlConfig); + /** * This function returns SRP server LEASE and KEY-LEASE configurations. * @@ -365,6 +435,16 @@ void otSrpServerHandleServiceUpdateResult(otInstance *aInstance, otSrpServerServ */ const otSrpServerHost *otSrpServerGetNextHost(otInstance *aInstance, const otSrpServerHost *aHost); +/** + * This function returns the response counters of the SRP server. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns A pointer to the response counters of the SRP server. + * + */ +const otSrpServerResponseCounters *otSrpServerGetResponseCounters(otInstance *aInstance); + /** * This function tells if the SRP service host has been deleted. * @@ -399,6 +479,15 @@ const char *otSrpServerHostGetFullName(const otSrpServerHost *aHost); */ const otIp6Address *otSrpServerHostGetAddresses(const otSrpServerHost *aHost, uint8_t *aAddressesNum); +/** + * This function returns the LEASE and KEY-LEASE information of a given host. + * + * @param[in] aHost A pointer to the SRP server host. + * @param[out] aLeaseInfo A pointer to where to output the LEASE and KEY-LEASE information. + * + */ +void otSrpServerHostGetLeaseInfo(const otSrpServerHost *aHost, otSrpServerLeaseInfo *aLeaseInfo); + /** * This function returns the next service (excluding any sub-type services) of given host. * @@ -562,6 +651,16 @@ uint16_t otSrpServerServiceGetWeight(const otSrpServerService *aService); */ uint16_t otSrpServerServiceGetPriority(const otSrpServerService *aService); +/** + * This function returns the TTL of the service instance. + * + * @param[in] aService A pointer to the SRP service. + * + * @returns The TTL of the service instance.. + * + */ +uint32_t otSrpServerServiceGetTtl(const otSrpServerService *aService); + /** * This function returns the TXT record data of the service instance. * @@ -583,6 +682,14 @@ const uint8_t *otSrpServerServiceGetTxtData(const otSrpServerService *aService, */ const otSrpServerHost *otSrpServerServiceGetHost(const otSrpServerService *aService); +/** + * This function returns the LEASE and KEY-LEASE information of a given service. + * + * @param[in] aService A pointer to the SRP server service. + * @param[out] aLeaseInfo A pointer to where to output the LEASE and KEY-LEASE information. + * + */ +void otSrpServerServiceGetLeaseInfo(const otSrpServerService *aService, otSrpServerLeaseInfo *aLeaseInfo); /** * @} * diff --git a/include/openthread/thread.h b/include/openthread/thread.h index fe829003c..04cb747f1 100644 --- a/include/openthread/thread.h +++ b/include/openthread/thread.h @@ -194,11 +194,22 @@ typedef struct otThreadParentResponseInfo bool mIsAttached; ///< Is the node receiving parent response attached } otThreadParentResponseInfo; +/** + * This callback informs the application that the detaching process has finished. + * + * @param[in] aContext A pointer to application-specific context. + * + */ +typedef void (*otDetachGracefullyCallback)(void *aContext); + /** * This function starts Thread protocol operation. * * The interface must be up when calling this function. * + * Calling this function with @p aEnabled set to FALSE stops any ongoing processes of detaching started by + * otThreadDetachGracefully(). Its callback will be called. + * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aEnabled TRUE if Thread is enabled, FALSE otherwise. * @@ -1009,6 +1020,21 @@ otError otThreadSendProactiveBackboneNotification(otInstance * aIns otIp6InterfaceIdentifier *aMlIid, uint32_t aTimeSinceLastTransaction); +/** + * This function notifies other nodes in the network (if any) and then stops Thread protocol operation. + * + * It sends an Address Release if it's a router, or sets its child timeout to 0 if it's a child. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aCallback A pointer to a function that is called upon finishing detaching. + * @param[in] aContext A pointer to callback application-specific context. + * + * @retval OT_ERROR_NONE Successfully started detaching. + * @retval OT_ERROR_BUSY Detaching is already in progress. + * + */ +otError otThreadDetachGracefully(otInstance *aInstance, otDetachGracefullyCallback aCallback, void *aContext); + /** * @} * diff --git a/script/check-arm-build-autotools b/script/check-arm-build-autotools index 4390ff8da..e994c2025 100755 --- a/script/check-arm-build-autotools +++ b/script/check-arm-build-autotools @@ -43,7 +43,7 @@ build_cc2538() "DNS_CLIENT=1" "JOINER=1" "SLAAC=1" - # cc2538 does not have enough resources to support Thread 1.2 + # cc2538 does not have enough resources to support Thread 1.3 "THREAD_VERSION=1.1" ) diff --git a/script/check-arm-build-cmake b/script/check-arm-build-cmake index d8210ab1f..3ee62b06e 100755 --- a/script/check-arm-build-cmake +++ b/script/check-arm-build-cmake @@ -51,7 +51,7 @@ reset_source() build_cc2538() { local options=( - # cc2538 does not have enough resources to support Thread 1.2 + # cc2538 does not have enough resources to support Thread 1.3 "-DOT_THREAD_VERSION=1.1" ) diff --git a/script/check-gn-build b/script/check-gn-build index 2a02c3ffe..68b17a7b7 100755 --- a/script/check-gn-build +++ b/script/check-gn-build @@ -43,10 +43,10 @@ main() ninja -C gn-out test -f gn-out/obj/src/core/libopenthread-ftd.a - # Check GN build for OT1.2 + # Check GN build for OT1.3 rm gn-out -r || true mkdir gn-out - echo 'openthread_config_thread_version = "1.2"' >gn-out/args.gn + echo 'openthread_config_thread_version = "1.3"' >gn-out/args.gn gn gen --check gn-out gn args gn-out --list ninja -C gn-out diff --git a/script/check-simulation-build-autotools b/script/check-simulation-build-autotools index 6b16957ac..6605c5209 100755 --- a/script/check-simulation-build-autotools +++ b/script/check-simulation-build-autotools @@ -89,9 +89,11 @@ build_all_features() "-DOPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE=1" "-DOPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE=1" "-DOPENTHREAD_CONFIG_UDP_FORWARD_ENABLE=1" + "-DOPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE=1" + "-DOPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE=1" ) - local options_1_2=( + local options_1_3=( "-DOPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE=1" "-DOPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE=1" "-DOPENTHREAD_CONFIG_DUA_ENABLE=1" @@ -108,15 +110,15 @@ build_all_features() reset_source make -f examples/Makefile-simulation THREAD_VERSION=1.1 FULL_LOGS=1 - # Build Thread 1.2 with full features and logs - export CPPFLAGS="${options[*]} ${options_1_2[*]} -DOPENTHREAD_CONFIG_LOG_OUTPUT=OT_LOG_OUTPUT_NONE" + # Build Thread 1.3 with full features and logs + export CPPFLAGS="${options[*]} ${options_1_3[*]} -DOPENTHREAD_CONFIG_LOG_OUTPUT=OT_LOG_OUTPUT_NONE" reset_source - make -f examples/Makefile-simulation THREAD_VERSION=1.2 + make -f examples/Makefile-simulation THREAD_VERSION=1.3 - # Build Thread 1.2 with full features and full logs - export CPPFLAGS="${options[*]} ${options_1_2[*]}" + # Build Thread 1.3 with full features and full logs + export CPPFLAGS="${options[*]} ${options_1_3[*]}" reset_source - make -f examples/Makefile-simulation THREAD_VERSION=1.2 FULL_LOGS=1 + make -f examples/Makefile-simulation THREAD_VERSION=1.3 FULL_LOGS=1 # Build Thread 1.1 with ASSERT disabled export CPPFLAGS="${options[*]} -DOPENTHREAD_CONFIG_ASSERT_ENABLE=0" diff --git a/script/check-simulation-build-cmake b/script/check-simulation-build-cmake index e9a3b4549..f296ab1ec 100755 --- a/script/check-simulation-build-cmake +++ b/script/check-simulation-build-cmake @@ -53,7 +53,7 @@ build_all_features() -DOT_FTD=OFF \ -DOT_MTD=OFF - # Thread 1.2 options + # Thread 1.3 options local options=( "-DOT_BACKBONE_ROUTER=ON" "-DOT_BORDER_ROUTING=ON" @@ -61,18 +61,18 @@ build_all_features() "-DOT_MLR=ON" "-DOT_OTNS=ON" "-DOT_SIMULATION_VIRTUAL_TIME=ON" - "-DOT_THREAD_VERSION=1.2" + "-DOT_THREAD_VERSION=1.3" ) - # Build Thread 1.2 with full features + # Build Thread 1.3 with full features reset_source "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_DUA=ON - # Build Thread 1.2 Backbone Router without DUA ND Proxying + # Build Thread 1.3 Backbone Router without DUA ND Proxying reset_source "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_BACKBONE_ROUTER_DUA_NDPROXYING=OFF - # Build Thread 1.2 Backbone Router without Multicast Routing + # Build Thread 1.3 Backbone Router without Multicast Routing reset_source "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_BACKBONE_ROUTER_MULTICAST_ROUTING=OFF @@ -82,11 +82,11 @@ build_all_features() -DOT_THREAD_VERSION=1.1 \ -DOT_VENDOR_EXTENSION=../../src/core/common/extension_example.cpp - # Build Thread 1.2 with no additional features + # Build Thread 1.3 with no additional features reset_source - "$(dirname "$0")"/cmake-build simulation -DOT_THREAD_VERSION=1.2 + "$(dirname "$0")"/cmake-build simulation -DOT_THREAD_VERSION=1.3 - # Build Thread 1.2 with full features and OT_ASSERT=OFF + # Build Thread 1.3 with full features and OT_ASSERT=OFF reset_source "$(dirname "$0")"/cmake-build simulation "${options[@]}" -DOT_DUA=ON -DOT_ASSERT=OFF diff --git a/script/check-size b/script/check-size index 2163d0576..e81c0bfaf 100755 --- a/script/check-size +++ b/script/check-size @@ -151,9 +151,9 @@ size_nrf52840_version() local thread_version=$1 - if [[ ${thread_version} == "1.2" ]]; then + if [[ ${thread_version} != "1.1" ]]; then options+=( - "-DOT_THREAD_VERSION=1.2" + "-DOT_THREAD_VERSION=1.3" "-DOT_BACKBONE_ROUTER=ON" "-DOT_DUA=ON" "-DOT_MLR=ON" @@ -243,7 +243,7 @@ size_nrf52840() "${reporter}" init OpenThread size_nrf52840_version 1.1 - size_nrf52840_version 1.2 + size_nrf52840_version 1.3 "${reporter}" post } diff --git a/script/make-pretty b/script/make-pretty index 70984de7c..22201c3f1 100755 --- a/script/make-pretty +++ b/script/make-pretty @@ -121,7 +121,7 @@ readonly OT_CLANG_TIDY_BUILD_OPTS=( '-DOT_SNTP_CLIENT=ON' '-DOT_SRP_CLIENT=ON' '-DOT_SRP_SERVER=ON' - '-DOT_THREAD_VERSION=1.2' + '-DOT_THREAD_VERSION=1.3' '-DOT_TREL=ON' '-DOT_COVERAGE=ON' '-DOT_LOG_LEVEL_DYNAMIC=ON' @@ -177,7 +177,7 @@ do_clang_tidy_fix() (mkdir -p ./build/cmake-tidy \ && cd ./build/cmake-tidy \ - && THREAD_VERSION=1.2 cmake "${OT_CLANG_TIDY_BUILD_OPTS[@]}" ../.. \ + && THREAD_VERSION=1.3 cmake "${OT_CLANG_TIDY_BUILD_OPTS[@]}" ../.. \ && ../../script/clang-tidy -header-filter='.*' -checks="${OT_CLANG_TIDY_CHECKS}" -j"$OT_BUILD_JOBS" "${OT_CLANG_TIDY_FIX_DIRS[@]}" -fix) } @@ -190,7 +190,7 @@ do_clang_tidy_check() ( mkdir -p ./build/cmake-tidy \ && cd ./build/cmake-tidy \ - && THREAD_VERSION=1.2 cmake "${OT_CLANG_TIDY_BUILD_OPTS[@]}" ../.. \ + && THREAD_VERSION=1.3 cmake "${OT_CLANG_TIDY_BUILD_OPTS[@]}" ../.. \ && ../../script/clang-tidy -header-filter='.*' -checks="${OT_CLANG_TIDY_CHECKS}" -j"$OT_BUILD_JOBS" "${OT_CLANG_TIDY_FIX_DIRS[@]}" \ | grep -v -E "third_party" >output.txt if grep -q "warning: \|error: " output.txt; then diff --git a/script/test b/script/test index 2c5226145..631013ad2 100755 --- a/script/test +++ b/script/test @@ -42,7 +42,7 @@ readonly OT_COLOR_NONE='\033[0m' readonly OT_NODE_TYPE="${OT_NODE_TYPE:-cli}" readonly OT_NATIVE_IP="${OT_NATIVE_IP:-0}" -readonly THREAD_VERSION="${THREAD_VERSION:-1.2}" +readonly THREAD_VERSION="${THREAD_VERSION:-1.3}" readonly INTER_OP="${INTER_OP:-0}" readonly VERBOSE="${VERBOSE:-0}" readonly BORDER_ROUTING="${BORDER_ROUTING:-1}" @@ -81,7 +81,7 @@ build_simulation() options+=("-DOT_FULL_LOGS=ON") fi - if [[ ${version} == "1.2" ]]; then + if [[ ${version} != "1.1" ]]; then options+=("-DOT_DUA=ON") options+=("-DOT_MLR=ON") fi @@ -90,7 +90,7 @@ build_simulation() options+=("-DOT_SIMULATION_VIRTUAL_TIME=ON") fi - if [[ ${version} == "1.2" ]]; then + if [[ ${version} != "1.1" ]]; then options+=("-DOT_CSL_RECEIVER=ON") options+=("-DOT_LINK_METRICS_INITIATOR=ON") options+=("-DOT_LINK_METRICS_SUBJECT=ON") @@ -106,7 +106,7 @@ build_simulation() OT_CMAKE_NINJA_TARGET=ot-rcp OT_CMAKE_BUILD_DIR="${OT_BUILDDIR}/openthread-simulation-${version}" "${OT_SRCDIR}"/script/cmake-build simulation "${options[@]}" "-DOT_SIMULATION_VIRTUAL_TIME_UART=ON" fi - if [[ ${version} == "1.2" && ${INTER_OP_BBR} == 1 ]]; then + if [[ ${version} != "1.1" && ${INTER_OP_BBR} == 1 ]]; then options+=("-DOT_BACKBONE_ROUTER=ON") @@ -124,7 +124,7 @@ build_posix() local version="$1" local options=("-DOT_MESSAGE_USE_HEAP=ON" "-DOT_THREAD_VERSION=${version}" "-DBUILD_TESTING=ON") - if [[ ${version} == "1.2" ]]; then + if [[ ${version} != "1.1" ]]; then options+=("-DOT_DUA=ON") options+=("-DOT_MLR=ON") fi @@ -147,7 +147,7 @@ build_posix() OT_CMAKE_BUILD_DIR="${OT_BUILDDIR}/openthread-posix-${version}" "${OT_SRCDIR}"/script/cmake-build posix "${options[@]}" - if [[ ${version} == "1.2" && ${INTER_OP_BBR} == 1 ]]; then + if [[ ${version} != "1.1" && ${INTER_OP_BBR} == 1 ]]; then options+=("-DOT_BACKBONE_ROUTER=ON") @@ -170,7 +170,7 @@ do_build() { build_for_one_version "${THREAD_VERSION}" - if [[ ${THREAD_VERSION} == "1.2" && ${INTER_OP} == "1" ]]; then + if [[ ${THREAD_VERSION} != "1.1" && ${INTER_OP} == "1" ]]; then build_for_one_version 1.1 fi } @@ -201,8 +201,8 @@ do_unit() { do_unit_version "${THREAD_VERSION}" - if [[ ${THREAD_VERSION} == "1.2" && ${INTER_OP_BBR} == 1 ]]; then - do_unit_version "1.2-bbr" + if [[ ${THREAD_VERSION} != "1.1" && ${INTER_OP_BBR} == 1 ]]; then + do_unit_version "1.3-bbr" fi } @@ -220,8 +220,8 @@ do_cert() ;; esac - if [[ ${THREAD_VERSION} == "1.2" ]]; then - export top_builddir_1_2_bbr="${OT_BUILDDIR}/openthread-simulation-1.2-bbr" + if [[ ${THREAD_VERSION} != "1.1" ]]; then + export top_builddir_1_3_bbr="${OT_BUILDDIR}/openthread-simulation-1.3-bbr" if [[ ${INTER_OP} == "1" ]]; then export top_builddir_1_1="${OT_BUILDDIR}/openthread-simulation-1.1" fi @@ -238,8 +238,8 @@ do_cert_suite() { export top_builddir="${OT_BUILDDIR}/openthread-simulation-${THREAD_VERSION}" - if [[ ${THREAD_VERSION} == "1.2" ]]; then - export top_builddir_1_2_bbr="${OT_BUILDDIR}/openthread-simulation-1.2-bbr" + if [[ ${THREAD_VERSION} != "1.1" ]]; then + export top_builddir_1_3_bbr="${OT_BUILDDIR}/openthread-simulation-1.3-bbr" if [[ ${INTER_OP} == "1" ]]; then export top_builddir_1_1="${OT_BUILDDIR}/openthread-simulation-1.1" fi @@ -386,7 +386,7 @@ do_expect() test_patterns=(-name 'tun-*.exp') else test_patterns=(-name 'posix-*.exp' -o -name 'cli-*.exp') - if [[ ${THREAD_VERSION} == "1.2" ]]; then + if [[ ${THREAD_VERSION} != "1.1" ]]; then test_patterns+=(-o -name 'v1_2-*.exp') fi fi @@ -421,9 +421,9 @@ ENVIRONMENTS: VERBOSE 1 to build or test verbosely. The default is 0. VIRTUAL_TIME 1 for virtual time, otherwise real time. The default value is 0 when running expect tests, otherwise default value is 1. - THREAD_VERSION 1.1 for Thread 1.1 stack, 1.2 for Thread 1.2 stack. The default is 1.2. - INTER_OP 1 to build 1.1 together. Only works when THREAD_VERSION is 1.2. The default is 0. - INTER_OP_BBR 1 to build bbr version together. Only works when THREAD_VERSION is 1.2. The default is 1. + THREAD_VERSION 1.1 for Thread 1.1 stack, 1.3 for Thread 1.3 stack. The default is 1.3. + INTER_OP 1 to build 1.1 together. Only works when THREAD_VERSION is 1.3. The default is 0. + INTER_OP_BBR 1 to build bbr version together. Only works when THREAD_VERSION is 1.3. The default is 1. COMMANDS: clean Clean built files to prepare for new build. @@ -455,7 +455,7 @@ EXAMPLES: THREAD_VERSION=1.1 VIRTUAL_TIME=0 $0 clean build cert tests/scripts/thread-cert/Cert_5_1_01_RouterAttach.py THREAD_VERSION=1.1 VIRTUAL_TIME=0 $0 cert tests/scripts/thread-cert/Cert_5_1_02_ChildAddressTimeout.py - # Test Thread 1.2 with real time, use 'INTER_OP=1' when the case needs both versions. + # Test Thread 1.3 with real time, use 'INTER_OP=1' when the case needs both versions. VIRTUAL_TIME=0 $0 clean build cert tests/scripts/thread-cert/v1_2_test_enhanced_keep_alive.py INTER_OP=1 VIRTUAL_TIME=0 $0 clean build cert tests/scripts/thread-cert/v1_2_router_5_1_1.py INTER_OP=1 VIRTUAL_TIME=0 $0 clean build cert_suite tests/scripts/thread-cert/v1_2_* @@ -559,10 +559,10 @@ envsetup() export RADIO_DEVICE="${OT_BUILDDIR}/openthread-simulation-${THREAD_VERSION}/examples/apps/ncp/ot-rcp" export OT_CLI_PATH="${OT_BUILDDIR}/openthread-posix-${THREAD_VERSION}/src/posix/ot-cli" - if [[ ${THREAD_VERSION} == "1.2" ]]; then + if [[ ${THREAD_VERSION} != "1.1" ]]; then export RADIO_DEVICE_1_1="${OT_BUILDDIR}/openthread-simulation-1.1/examples/apps/ncp/ot-rcp" export OT_CLI_PATH_1_1="${OT_BUILDDIR}/openthread-posix-1.1/src/posix/ot-cli" - export OT_CLI_PATH_1_2_BBR="${OT_BUILDDIR}/openthread-posix-1.2-bbr/src/posix/ot-cli" + export OT_CLI_PATH_BBR="${OT_BUILDDIR}/openthread-posix-1.3-bbr/src/posix/ot-cli" fi fi @@ -606,7 +606,7 @@ main() fi [[ ${VIRTUAL_TIME} == 1 ]] && echo "Using virtual time" || echo "Using real time" - [[ ${THREAD_VERSION} == "1.2" ]] && echo "Using Thread 1.2 stack" || echo "Using Thread 1.1 stack" + [[ ${THREAD_VERSION} != "1.1" ]] && echo "Using Thread 1.3 stack" || echo "Using Thread 1.1 stack" while [[ $# != 0 ]]; do case "$1" in diff --git a/src/cli/README.md b/src/cli/README.md index 1dd92e45a..cbb8ad06f 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -62,7 +62,6 @@ Done - [leaderdata](#leaderdata) - [leaderweight](#leaderweight) - [linkmetrics](#linkmetrics-mgmt-ipaddr-enhanced-ack-clear) -- [linkquality](#linkquality-extaddr) - [locate](#locate) - [log](#log-filename-filename) - [mac](#mac-retries-direct) @@ -1627,25 +1626,6 @@ Done - RSSI: -18 (dBm) (Exponential Moving Average) ``` -### linkquality \ - -Get the link quality on the link to a given extended address. - -```bash -> linkquality 36c1dd7a4f5201ff -3 -Done -``` - -### linkquality \ \ - -Set the link quality on the link to a given extended address. - -```bash -> linkquality 36c1dd7a4f5201ff 3 -Done -``` - ### locate Gets the current state (`In Progress` or `Idle`) of anycast locator. diff --git a/src/cli/README_DATASET.md b/src/cli/README_DATASET.md index 1043bbaa0..87cd4d21c 100644 --- a/src/cli/README_DATASET.md +++ b/src/cli/README_DATASET.md @@ -196,7 +196,7 @@ Done Usage: `dataset activetimestamp [timestamp]` -Get active timestamp. +Get active timestamp seconds. ```bash > dataset activetimestamp @@ -204,7 +204,7 @@ Get active timestamp. Done ``` -Set active timestamp. +Set active timestamp seconds. ```bash > dataset activetimestamp 123456789 @@ -457,7 +457,7 @@ Done Usage: `dataset pendingtimestamp [timestamp]` -Get pending timestamp. +Get pending timestamp seconds. ```bash > dataset pendingtimestamp @@ -465,7 +465,7 @@ Get pending timestamp. Done ``` -Set pending timestamp. +Set pending timestamp seconds. ```bash > dataset pendingtimestamp 123456789 diff --git a/src/cli/README_SRP_CLIENT.md b/src/cli/README_SRP_CLIENT.md index 5cbe94fac..ae48fbf1e 100644 --- a/src/cli/README_SRP_CLIENT.md +++ b/src/cli/README_SRP_CLIENT.md @@ -15,6 +15,7 @@ Usage : `srp client [command] ...` - [start](#start) - [state](#state) - [stop](#stop) +- [ttl](#ttl) ## Command Details @@ -36,6 +37,7 @@ service start state stop +ttl Done ``` @@ -137,6 +139,14 @@ name:"dev4312", state:Registered, addrs:[fd00:0:0:0:0:0:0:1234, fd00:0:0:0:0:0:0 Done ``` +When auto host address mode is enabled. + +```bash +srp client host +name:"dev1234", state:Registered, addrs:auto +Done +``` + ### host name Usage: `srp client host name [name]` @@ -158,9 +168,17 @@ Done ### host address -Usage : `srp client host address [
...]` +Usage : `srp client host address [auto |
...]` -Get the list of host addresses. +Indicate auto address mode is enabled. + +```bash +> srp client host address +auto +Done +``` + +Get the list of host addresses (when auto host address is not enabled). ```bash > srp client host address @@ -169,7 +187,14 @@ fd00:0:0:0:0:0:0:beef Done ``` -Set the list of host addresses (can be set while client is running to update the host addresses) +Enable auto host address mode. When enabled client will automatically use all Thread netif unicast addresses excluding all link-local and mesh-local addresses. If there is no valid address, then Mesh Local EID address is added. SRP client will automatically re-register if/when addresses on Thread netif get changed (e.g., new address is added or existing address is removed). + +```bash +> srp client host address auto +Done +``` + +Explicitly set the list of host addresses (can be set while client is running to update the host addresses), also disabled auto host address mode. ```bash > srp client host address fd00::cafe @@ -409,3 +434,23 @@ Stop the SRP client. > srp client stop Done ``` + +### ttl + +Usage: `srp client ttl [value]` + +Get the TTL (in seconds). + +```bash +> srp client ttl +7200 +Done +> +``` + +Set the TTL. + +```bash +> srp client ttl 3600 +Done +``` diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index ef5376ff3..b1f250ffd 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -1453,6 +1453,25 @@ exit: } #endif +template <> otError Interpreter::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + if (aArgs[0] == "async") + { + SuccessOrExit(error = otThreadDetachGracefully(GetInstancePtr(), nullptr, nullptr)); + } + else + { + SuccessOrExit(error = + otThreadDetachGracefully(GetInstancePtr(), &Interpreter::HandleDetachGracefullyResult, this)); + error = OT_ERROR_PENDING; + } + +exit: + return error; +} + template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -4903,6 +4922,17 @@ void Interpreter::OutputChildTableEntry(uint8_t aIndentSize, const otNetworkDiag } #endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE +void Interpreter::HandleDetachGracefullyResult(void *aContext) +{ + static_cast(aContext)->HandleDetachGracefullyResult(); +} + +void Interpreter::HandleDetachGracefullyResult(void) +{ + OutputLine("Finished detaching"); + OutputResult(OT_ERROR_NONE); +} + void Interpreter::HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo &aInfo) { OutputFormat("~ Discovery Request from "); @@ -5017,6 +5047,7 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) #if OPENTHREAD_FTD CmdEntry("delaytimermin"), #endif + CmdEntry("detach"), #endif // OPENTHREAD_FTD || OPENTHREAD_MTD #if OPENTHREAD_CONFIG_DIAG_ENABLE CmdEntry("diag"), diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 90144d196..196703a9e 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -452,6 +452,9 @@ private: const char *LinkMetricsStatusToStr(uint8_t aStatus); #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE + static void HandleDetachGracefullyResult(void *aContext); + void HandleDetachGracefullyResult(void); + static void HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo *aInfo, void *aContext) { static_cast(aContext)->HandleDiscoveryRequest(*aInfo); diff --git a/src/cli/cli_dataset.cpp b/src/cli/cli_dataset.cpp index efade0586..573436aea 100644 --- a/src/cli/cli_dataset.cpp +++ b/src/cli/cli_dataset.cpp @@ -51,12 +51,12 @@ otError Dataset::Print(otOperationalDataset &aDataset) { if (aDataset.mComponents.mIsPendingTimestampPresent) { - OutputLine("Pending Timestamp: %lu", aDataset.mPendingTimestamp); + OutputLine("Pending Timestamp: %lu", aDataset.mPendingTimestamp.mSeconds); } if (aDataset.mComponents.mIsActiveTimestampPresent) { - OutputLine("Active Timestamp: %lu", aDataset.mActiveTimestamp); + OutputLine("Active Timestamp: %lu", aDataset.mActiveTimestamp.mSeconds); } if (aDataset.mComponents.mIsChannelPresent) @@ -205,12 +205,14 @@ template <> otError Dataset::Process(Arg aArgs[]) { if (sDataset.mComponents.mIsActiveTimestampPresent) { - OutputLine("%lu", sDataset.mActiveTimestamp); + OutputLine("%lu", sDataset.mActiveTimestamp.mSeconds); } } else { - SuccessOrExit(error = aArgs[0].ParseAsUint64(sDataset.mActiveTimestamp)); + SuccessOrExit(error = aArgs[0].ParseAsUint64(sDataset.mActiveTimestamp.mSeconds)); + sDataset.mActiveTimestamp.mTicks = 0; + sDataset.mActiveTimestamp.mAuthoritative = false; sDataset.mComponents.mIsActiveTimestampPresent = true; } @@ -423,12 +425,14 @@ template <> otError Dataset::Process(Arg aArgs[]) { if (sDataset.mComponents.mIsPendingTimestampPresent) { - OutputLine("%lu", sDataset.mPendingTimestamp); + OutputLine("%lu", sDataset.mPendingTimestamp.mSeconds); } } else { - SuccessOrExit(error = aArgs[0].ParseAsUint64(sDataset.mPendingTimestamp)); + SuccessOrExit(error = aArgs[0].ParseAsUint64(sDataset.mPendingTimestamp.mSeconds)); + sDataset.mPendingTimestamp.mTicks = 0; + sDataset.mPendingTimestamp.mAuthoritative = false; sDataset.mComponents.mIsPendingTimestampPresent = true; } @@ -450,14 +454,18 @@ template <> otError Dataset::Process(Arg aArgs[]) if (*arg == "activetimestamp") { arg++; + SuccessOrExit(error = arg->ParseAsUint64(dataset.mActiveTimestamp.mSeconds)); + dataset.mActiveTimestamp.mTicks = 0; + dataset.mActiveTimestamp.mAuthoritative = false; dataset.mComponents.mIsActiveTimestampPresent = true; - SuccessOrExit(error = arg->ParseAsUint64(dataset.mActiveTimestamp)); } else if (*arg == "pendingtimestamp") { arg++; + SuccessOrExit(error = arg->ParseAsUint64(dataset.mPendingTimestamp.mSeconds)); + dataset.mPendingTimestamp.mTicks = 0; + dataset.mPendingTimestamp.mAuthoritative = false; dataset.mComponents.mIsPendingTimestampPresent = true; - SuccessOrExit(error = arg->ParseAsUint64(dataset.mPendingTimestamp)); } else if (*arg == "networkkey") { diff --git a/src/cli/cli_srp_client.cpp b/src/cli/cli_srp_client.cpp index fa1b740b1..0732ac2e4 100644 --- a/src/cli/cli_srp_client.cpp +++ b/src/cli/cli_srp_client.cpp @@ -160,10 +160,21 @@ template <> otError SrpClient::Process(Arg aArgs[]) { const otSrpClientHostInfo *hostInfo = otSrpClientGetHostInfo(GetInstancePtr()); - for (uint8_t index = 0; index < hostInfo->mNumAddresses; index++) + if (hostInfo->mAutoAddress) { - OutputIp6AddressLine(hostInfo->mAddresses[index]); + OutputLine("auto"); } + else + { + for (uint8_t index = 0; index < hostInfo->mNumAddresses; index++) + { + OutputIp6AddressLine(hostInfo->mAddresses[index]); + } + } + } + else if (aArgs[1] == "auto") + { + error = otSrpClientEnableAutoHostAddress(GetInstancePtr()); } else { @@ -447,19 +458,28 @@ void SrpClient::OutputHostInfo(uint8_t aIndentSize, const otSrpClientHostInfo &a OutputFormat("(null)"); } - OutputFormat(", state:%s, addrs:[", otSrpClientItemStateToString(aHostInfo.mState)); + OutputFormat(", state:%s, addrs:", otSrpClientItemStateToString(aHostInfo.mState)); - for (uint8_t index = 0; index < aHostInfo.mNumAddresses; index++) + if (aHostInfo.mAutoAddress) { - if (index > 0) + OutputLine("auto"); + } + else + { + OutputFormat("["); + + for (uint8_t index = 0; index < aHostInfo.mNumAddresses; index++) { - OutputFormat(", "); + if (index > 0) + { + OutputFormat(", "); + } + + OutputIp6Address(aHostInfo.mAddresses[index]); } - OutputIp6Address(aHostInfo.mAddresses[index]); + OutputLine("]"); } - - OutputLine("]"); } void SrpClient::OutputServiceList(uint8_t aIndentSize, const otSrpClientService *aServices) @@ -525,6 +545,11 @@ exit: return error; } +template <> otError SrpClient::Process(Arg aArgs[]) +{ + return Interpreter::GetInterpreter().ProcessGetSet(aArgs, otSrpClientGetTtl, otSrpClientSetTtl); +} + void SrpClient::HandleCallback(otError aError, const otSrpClientHostInfo *aHostInfo, const otSrpClientService * aServices, @@ -578,7 +603,7 @@ otError SrpClient::Process(Arg aArgs[]) static constexpr Command kCommands[] = { CmdEntry("autostart"), CmdEntry("callback"), CmdEntry("host"), CmdEntry("keyleaseinterval"), CmdEntry("leaseinterval"), CmdEntry("server"), CmdEntry("service"), CmdEntry("start"), - CmdEntry("state"), CmdEntry("stop"), + CmdEntry("state"), CmdEntry("stop"), CmdEntry("ttl"), }; static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); diff --git a/src/cli/cli_srp_server.cpp b/src/cli/cli_srp_server.cpp index 74930419b..e13b8eca6 100644 --- a/src/cli/cli_srp_server.cpp +++ b/src/cli/cli_srp_server.cpp @@ -149,6 +149,30 @@ otError SrpServer::ProcessDisable(Arg aArgs[]) return OT_ERROR_NONE; } +otError SrpServer::ProcessTtl(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + otSrpServerTtlConfig ttlConfig; + + if (aArgs[0].IsEmpty()) + { + otSrpServerGetTtlConfig(GetInstancePtr(), &ttlConfig); + OutputLine("min ttl: %u", ttlConfig.mMinTtl); + OutputLine("max ttl: %u", ttlConfig.mMaxTtl); + } + else + { + SuccessOrExit(error = aArgs[0].ParseAsUint32(ttlConfig.mMinTtl)); + SuccessOrExit(error = aArgs[1].ParseAsUint32(ttlConfig.mMaxTtl)); + VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + error = otSrpServerSetTtlConfig(GetInstancePtr(), &ttlConfig); + } + +exit: + return error; +} + otError SrpServer::ProcessLease(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -289,6 +313,7 @@ otError SrpServer::ProcessService(Arg aArgs[]) OutputLine(kIndentSize, "port: %hu", otSrpServerServiceGetPort(service)); OutputLine(kIndentSize, "priority: %hu", otSrpServerServiceGetPriority(service)); OutputLine(kIndentSize, "weight: %hu", otSrpServerServiceGetWeight(service)); + OutputLine(kIndentSize, "ttl: %hu", otSrpServerServiceGetTtl(service)); txtData = otSrpServerServiceGetTxtData(service, &txtDataLength); OutputFormat(kIndentSize, "TXT: "); diff --git a/src/cli/cli_srp_server.hpp b/src/cli/cli_srp_server.hpp index 03e5293df..363f59118 100644 --- a/src/cli/cli_srp_server.hpp +++ b/src/cli/cli_srp_server.hpp @@ -90,6 +90,7 @@ private: otError ProcessHost(Arg aArgs[]); otError ProcessService(Arg aArgs[]); otError ProcessSeqNum(Arg aArgs[]); + otError ProcessTtl(Arg aArgs[]); otError ProcessHelp(Arg aArgs[]); void OutputHostAddresses(const otSrpServerHost *aHost); @@ -100,6 +101,7 @@ private: {"help", &SrpServer::ProcessHelp}, {"host", &SrpServer::ProcessHost}, {"lease", &SrpServer::ProcessLease}, {"seqnum", &SrpServer::ProcessSeqNum}, {"service", &SrpServer::ProcessService}, {"state", &SrpServer::ProcessState}, + {"ttl", &SrpServer::ProcessTtl}, }; static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted"); diff --git a/src/cli/radio.cmake b/src/cli/radio.cmake index 9b69a9e6b..3e7354aa6 100644 --- a/src/cli/radio.cmake +++ b/src/cli/radio.cmake @@ -46,10 +46,14 @@ target_sources(openthread-cli-radio cli_output.cpp ) +if(NOT DEFINED OT_MBEDTLS_RCP) + set(OT_MBEDTLS_RCP ${OT_MBEDTLS}) +endif() + target_link_libraries(openthread-cli-radio PUBLIC openthread-radio PRIVATE - ${OT_MBEDTLS} + ${OT_MBEDTLS_RCP} ot-config ) diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 1518546d5..0db992dfb 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -37,6 +37,8 @@ if (openthread_enable_core_config_args) { defines += [ "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_1" ] } else if (openthread_config_thread_version == "1.2") { defines += [ "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_2" ] + } else if (openthread_config_thread_version == "1.3") { + defines += [ "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_3" ] } else if (openthread_config_thread_version != "") { assert(false, "Unrecognized Thread version: ${openthread_config_thread_version}") @@ -363,9 +365,8 @@ 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/infra_if.cpp", + "border_router/infra_if.hpp", "border_router/routing_manager.cpp", "border_router/routing_manager.hpp", "coap/coap.cpp", @@ -557,6 +558,8 @@ openthread_core_files = [ "net/ip6_mpl.cpp", "net/ip6_mpl.hpp", "net/ip6_types.hpp", + "net/nd6.cpp", + "net/nd6.hpp", "net/nd_agent.cpp", "net/nd_agent.hpp", "net/netif.cpp", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e09f7e5f2..3907c4374 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -86,8 +86,7 @@ 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/infra_if.cpp border_router/routing_manager.cpp coap/coap.cpp coap/coap_message.cpp @@ -169,6 +168,7 @@ set(COMMON_SOURCES net/ip6_filter.cpp net/ip6_headers.cpp net/ip6_mpl.cpp + net/nd6.cpp net/nd_agent.cpp net/netif.cpp net/sntp_client.cpp diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 850228d49..13fb8c117 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -176,8 +176,7 @@ 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/infra_if.cpp \ border_router/routing_manager.cpp \ coap/coap.cpp \ coap/coap_message.cpp \ @@ -259,6 +258,7 @@ SOURCES_COMMON = \ net/ip6_filter.cpp \ net/ip6_headers.cpp \ net/ip6_mpl.cpp \ + net/nd6.cpp \ net/nd_agent.cpp \ net/netif.cpp \ net/sntp_client.cpp \ @@ -415,7 +415,7 @@ 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/infra_if.hpp \ border_router/routing_manager.hpp \ coap/coap.hpp \ coap/coap_message.hpp \ @@ -563,6 +563,7 @@ HEADERS_COMMON = \ net/ip6_headers.hpp \ net/ip6_mpl.hpp \ net/ip6_types.hpp \ + net/nd6.hpp \ net/nd_agent.hpp \ net/netif.hpp \ net/sntp_client.hpp \ diff --git a/src/core/api/dns_server_api.cpp b/src/core/api/dns_server_api.cpp index b9e020cf6..5b094487d 100644 --- a/src/core/api/dns_server_api.cpp +++ b/src/core/api/dns_server_api.cpp @@ -81,4 +81,9 @@ otDnssdQueryType otDnssdGetQueryTypeAndName(const otDnssdQuery *aQuery, char (*a return MapEnum(Dns::ServiceDiscovery::Server::GetQueryTypeAndName(aQuery, *aNameOutput)); } +const otDnssdCounters *otDnssdGetCounters(otInstance *aInstance) +{ + return &AsCoreType(aInstance).Get().GetCounters(); +} + #endif // OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE diff --git a/src/core/api/srp_client_api.cpp b/src/core/api/srp_client_api.cpp index 304a2fe2d..3ec356f34 100644 --- a/src/core/api/srp_client_api.cpp +++ b/src/core/api/srp_client_api.cpp @@ -84,6 +84,16 @@ bool otSrpClientIsAutoStartModeEnabled(otInstance *aInstance) } #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE +uint32_t otSrpClientGetTtl(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetTtl(); +} + +void otSrpClientSetTtl(otInstance *aInstance, uint32_t aTtl) +{ + return AsCoreType(aInstance).Get().SetTtl(aTtl); +} + uint32_t otSrpClientGetLeaseInterval(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetLeaseInterval(); @@ -114,6 +124,11 @@ otError otSrpClientSetHostName(otInstance *aInstance, const char *aName) return AsCoreType(aInstance).Get().SetHostName(aName); } +otError otSrpClientEnableAutoHostAddress(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().EnableAutoHostAddress(); +} + otError otSrpClientSetHostAddresses(otInstance *aInstance, const otIp6Address *aIp6Addresses, uint8_t aNumAddresses) { return AsCoreType(aInstance).Get().SetHostAddresses(AsCoreTypePtr(aIp6Addresses), aNumAddresses); diff --git a/src/core/api/srp_server_api.cpp b/src/core/api/srp_server_api.cpp index 8c59319a6..f9d4367e4 100644 --- a/src/core/api/srp_server_api.cpp +++ b/src/core/api/srp_server_api.cpp @@ -57,6 +57,11 @@ otSrpServerState otSrpServerGetState(otInstance *aInstance) return MapEnum(AsCoreType(aInstance).Get().GetState()); } +uint16_t otSrpServerGetPort(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetPort(); +} + otSrpServerAddressMode otSrpServerGetAddressMode(otInstance *aInstance) { return MapEnum(AsCoreType(aInstance).Get().GetAddressMode()); @@ -82,6 +87,16 @@ void otSrpServerSetEnabled(otInstance *aInstance, bool aEnabled) AsCoreType(aInstance).Get().SetEnabled(aEnabled); } +void otSrpServerGetTtlConfig(otInstance *aInstance, otSrpServerTtlConfig *aTtlConfig) +{ + AsCoreType(aInstance).Get().GetTtlConfig(AsCoreType(aTtlConfig)); +} + +otError otSrpServerSetTtlConfig(otInstance *aInstance, const otSrpServerTtlConfig *aTtlConfig) +{ + return AsCoreType(aInstance).Get().SetTtlConfig(AsCoreType(aTtlConfig)); +} + void otSrpServerGetLeaseConfig(otInstance *aInstance, otSrpServerLeaseConfig *aLeaseConfig) { AsCoreType(aInstance).Get().GetLeaseConfig(AsCoreType(aLeaseConfig)); @@ -109,6 +124,11 @@ const otSrpServerHost *otSrpServerGetNextHost(otInstance *aInstance, const otSrp return AsCoreType(aInstance).Get().GetNextHost(AsCoreTypePtr(aHost)); } +const otSrpServerResponseCounters *otSrpServerGetResponseCounters(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetResponseCounters(); +} + bool otSrpServerHostIsDeleted(const otSrpServerHost *aHost) { return AsCoreType(aHost).IsDeleted(); @@ -124,6 +144,16 @@ const otIp6Address *otSrpServerHostGetAddresses(const otSrpServerHost *aHost, ui return AsCoreType(aHost).GetAddresses(*aAddressesNum); } +void otSrpServerHostGetLeaseInfo(const otSrpServerHost *aHost, otSrpServerLeaseInfo *aLeaseInfo) +{ + AsCoreType(aHost).GetLeaseInfo(*aLeaseInfo); +} + +uint32_t otSrpServerHostGetKeyLease(const otSrpServerHost *aHost) +{ + return AsCoreType(aHost).GetKeyLease(); +} + const otSrpServerService *otSrpServerHostGetNextService(const otSrpServerHost * aHost, const otSrpServerService *aService) { @@ -184,6 +214,11 @@ uint16_t otSrpServerServiceGetPriority(const otSrpServerService *aService) return AsCoreType(aService).GetPriority(); } +uint32_t otSrpServerServiceGetTtl(const otSrpServerService *aService) +{ + return AsCoreType(aService).GetTtl(); +} + const uint8_t *otSrpServerServiceGetTxtData(const otSrpServerService *aService, uint16_t *aDataLength) { *aDataLength = AsCoreType(aService).GetTxtDataLength(); @@ -196,4 +231,9 @@ const otSrpServerHost *otSrpServerServiceGetHost(const otSrpServerService *aServ return &AsCoreType(aService).GetHost(); } +void otSrpServerServiceGetLeaseInfo(const otSrpServerService *aService, otSrpServerLeaseInfo *aLeaseInfo) +{ + AsCoreType(aService).GetLeaseInfo(*aLeaseInfo); +} + #endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE diff --git a/src/core/api/thread_api.cpp b/src/core/api/thread_api.cpp index ca38757e8..bec8c0633 100644 --- a/src/core/api/thread_api.cpp +++ b/src/core/api/thread_api.cpp @@ -497,4 +497,9 @@ bool otThreadIsAnycastLocateInProgress(otInstance *aInstance) } #endif +otError otThreadDetachGracefully(otInstance *aInstance, otDetachGracefullyCallback aCallback, void *aContext) +{ + return AsCoreType(aInstance).Get().DetachGracefully(aCallback, aContext); +} + #endif // OPENTHREAD_FTD || OPENTHREAD_MTD diff --git a/src/core/backbone_router/ndproxy_table.cpp b/src/core/backbone_router/ndproxy_table.cpp index 9d6595897..89c044225 100644 --- a/src/core/backbone_router/ndproxy_table.cpp +++ b/src/core/backbone_router/ndproxy_table.cpp @@ -251,7 +251,7 @@ void NdProxyTable::HandleTimer(void) for (NdProxy &proxy : Iterate(kFilterDadInProcess)) { - if (proxy.IsDadAttamptsComplete()) + if (proxy.IsDadAttemptsComplete()) { proxy.mDadFlag = false; NotifyDuaRegistrationOnBackboneLink(proxy, /* aIsRenew */ false); @@ -262,7 +262,7 @@ void NdProxyTable::HandleTimer(void) if (Get().SendBackboneQuery(GetDua(proxy)) == kErrorNone) { - proxy.IncreaseDadAttampts(); + proxy.IncreaseDadAttempts(); } } } diff --git a/src/core/backbone_router/ndproxy_table.hpp b/src/core/backbone_router/ndproxy_table.hpp index b4cf71605..097a6d3a3 100644 --- a/src/core/backbone_router/ndproxy_table.hpp +++ b/src/core/backbone_router/ndproxy_table.hpp @@ -111,8 +111,8 @@ public: uint32_t aTimeSinceLastTransaction); void Update(uint16_t aRloc16, uint32_t aTimeSinceLastTransaction); - void IncreaseDadAttampts(void) { mDadAttempts++; } - bool IsDadAttamptsComplete() const { return mDadAttempts == Mle::kDuaDadRepeats; } + void IncreaseDadAttempts(void) { mDadAttempts++; } + bool IsDadAttemptsComplete() const { return mDadAttempts == Mle::kDuaDadRepeats; } Ip6::InterfaceIdentifier mAddressIid; Ip6::InterfaceIdentifier mMeshLocalIid; diff --git a/src/core/border_router/infra_if.cpp b/src/core/border_router/infra_if.cpp new file mode 100644 index 000000000..7fe20c942 --- /dev/null +++ b/src/core/border_router/infra_if.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2022, 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 network interface. + */ + +#include "infra_if.hpp" + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + +#include "border_router/routing_manager.hpp" +#include "common/as_core_type.hpp" +#include "common/instance.hpp" +#include "common/locator_getters.hpp" +#include "common/logging.hpp" +#include "net/icmp6.hpp" + +namespace ot { +namespace BorderRouter { + +RegisterLogModule("InfraIf"); + +InfraIf::InfraIf(Instance &aInstance) + : InstanceLocator(aInstance) + , mInitialized(false) + , mIsRunning(false) + , mIfIndex(0) +{ +} + +Error InfraIf::Init(uint32_t aIfIndex) +{ + Error error = kErrorNone; + + VerifyOrExit(!mInitialized, error = kErrorInvalidState); + VerifyOrExit(aIfIndex > 0, error = kErrorInvalidArgs); + + mIfIndex = aIfIndex; + mInitialized = true; + + LogInfo("Init %s", ToString().AsCString()); + +exit: + return error; +} + +void InfraIf::Deinit(void) +{ + mInitialized = false; + mIsRunning = false; + mIfIndex = 0; + + LogInfo("Deinit"); +} + +bool InfraIf::HasAddress(const Ip6::Address &aAddress) +{ + OT_ASSERT(mInitialized); + + return otPlatInfraIfHasAddress(mIfIndex, &aAddress); +} + +Error InfraIf::Send(const Icmp6Packet &aPacket, const Ip6::Address &aDestination) +{ + OT_ASSERT(mInitialized); + + return otPlatInfraIfSendIcmp6Nd(mIfIndex, &aDestination, aPacket.GetBytes(), aPacket.GetLength()); +} + +void InfraIf::HandledReceived(uint32_t aIfIndex, const Ip6::Address &aSource, const Icmp6Packet &aPacket) +{ + Error error = kErrorNone; + + VerifyOrExit(mInitialized && mIsRunning, error = kErrorInvalidState); + VerifyOrExit(aIfIndex == mIfIndex, error = kErrorDrop); + VerifyOrExit(aPacket.GetBytes() != nullptr, error = kErrorInvalidArgs); + VerifyOrExit(aPacket.GetLength() >= sizeof(Ip6::Icmp::Header), error = kErrorParse); + + Get().HandleReceived(aPacket, aSource); + +exit: + if (error != kErrorNone) + { + LogDebg("Dropped ICMPv6 message: %s", ErrorToString(error)); + } +} + +Error InfraIf::HandleStateChanged(uint32_t aIfIndex, bool aIsRunning) +{ + Error error = kErrorNone; + + VerifyOrExit(mInitialized, error = kErrorInvalidState); + VerifyOrExit(aIfIndex == mIfIndex, error = kErrorInvalidArgs); + + VerifyOrExit(aIsRunning != mIsRunning); + LogInfo("State changed: %sRUNNING -> %sRUNNING", mIsRunning ? "" : "NOT ", aIsRunning ? "" : "NOT "); + + mIsRunning = aIsRunning; + + Get().HandleInfraIfStateChanged(); + +exit: + return error; +} + +InfraIf::InfoString InfraIf::ToString(void) const +{ + InfoString string; + + string.Append("infra netif %u", mIfIndex); + return string; +} + +//--------------------------------------------------------------------------------------------------------------------- + +extern "C" void otPlatInfraIfRecvIcmp6Nd(otInstance * aInstance, + uint32_t aInfraIfIndex, + const otIp6Address *aSrcAddress, + const uint8_t * aBuffer, + uint16_t aBufferLength) +{ + InfraIf::Icmp6Packet packet; + + packet.Init(aBuffer, aBufferLength); + AsCoreType(aInstance).Get().HandledReceived(aInfraIfIndex, AsCoreType(aSrcAddress), packet); +} + +extern "C" otError otPlatInfraIfStateChanged(otInstance *aInstance, uint32_t aInfraIfIndex, bool aIsRunning) +{ + return AsCoreType(aInstance).Get().HandleStateChanged(aInfraIfIndex, aIsRunning); +} + +} // namespace BorderRouter +} // namespace ot + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE diff --git a/src/core/border_router/infra_if.hpp b/src/core/border_router/infra_if.hpp new file mode 100644 index 000000000..b67009e81 --- /dev/null +++ b/src/core/border_router/infra_if.hpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2022, 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 infrastructure network interface. + * + */ + +#ifndef INFRA_IF_HPP_ +#define INFRA_IF_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + +#include + +#include "common/data.hpp" +#include "common/error.hpp" +#include "common/locator.hpp" +#include "common/string.hpp" +#include "net/ip6.hpp" + +namespace ot { +namespace BorderRouter { + +/** + * This class represents the infrastructure network interface on a border router. + * + */ +class InfraIf : public InstanceLocator +{ +public: + static constexpr uint16_t kInfoStringSize = 20; ///< Max chars for the info string (`ToString()`). + + typedef String InfoString; ///< String type returned from `ToString()`. + typedef Data Icmp6Packet; ///< An IMCPv6 packet (data containing the IP payload) + + /** + * This constructor initializes the `InfraIf`. + * + * @param[in] aInstance A OpenThread instance. + * + */ + explicit InfraIf(Instance &aInstance); + + /** + * This method initializes the `InfraIf`. + * + * @param[in] aIfIndex The infrastructure interface index. + * + * @retval kErrorNone Successfully initialized the `InfraIf`. + * @retval kErrorInvalidArgs The index of the infra interface is not valid. + * @retval kErrorInvalidState The `InfraIf` is already initialized. + * + */ + Error Init(uint32_t aIfIndex); + + /** + * This method deinitilaizes the `InfraIf`. + * + */ + void Deinit(void); + + /** + * This method indicates whether or not the `InfraIf` is initialized. + * + * @retval TRUE The `InfraIf` is initialized. + * @retval FALSE The `InfraIf` is not initialized. + * + */ + bool IsInitialized(void) const { return mInitialized; } + + /** + * This method indicates whether or not the infra interface is running. + * + * @retval TRUE The infrastructure interface is running. + * @retval FALSE The infrastructure interface is not running. + * + */ + bool IsRunning(void) const { return mIsRunning; } + + /** + * This method returns the infrastructure interface index. + * + * @returns The interface index or zero if not initialized. + * + */ + uint32_t GetIfIndex(void) const { return mIfIndex; } + + /** + * This method indicates whether or not the infra interface has the given IPv6 address assigned. + * + * This method MUST be used when interface is initialized. + * + * @param[in] aAddress The IPv6 address. + * + * @retval TRUE The infrastructure interface has @p aAddress. + * @retval FALSE The infrastructure interface does not have @p aAddress. + * + */ + bool HasAddress(const Ip6::Address &aAddress); + + /** + * This method sends an ICMPv6 Neighbor Discovery packet on the infrastructure interface. + * + * This method MUST be used when interface is initialized. + * + * @param[in] aPacket The ICMPv6 packet to send. + * @param[in] aDestination The destination address. + * + * @retval kErrorNone Successfully sent the ICMPv6 message. + * @retval kErrorFailed Failed to send the ICMPv6 message. + * + */ + Error Send(const Icmp6Packet &aPacket, const Ip6::Address &aDestination); + + /** + * This method processes a received ICMPv6 Neighbor Discovery packet from an infrastructure interface. + * + * @param[in] aIfIndex The infrastructure interface index on which the ICMPv6 message is received. + * @param[in] aSource The IPv6 source address. + * @param[in] aPacket The ICMPv6 packet. + * + */ + void HandledReceived(uint32_t aIfIndex, const Ip6::Address &aSource, const Icmp6Packet &aPacket); + + /** + * This method handles infrastructure interface state changes. + * + * @param[in] aIfIndex The infrastructure interface index. + * @param[in] aIsRunning A boolean that indicates whether the infrastructure interface is running. + * + * @retval kErrorNone Successfully updated the infra interface status. + * @retval kErrorInvalidState The `InfraIf` is not initialized. + * @retval kErrorInvalidArgs The @p IfIndex does not match the interface index of `InfraIf`. + * + */ + Error HandleStateChanged(uint32_t aIfIndex, bool aIsRunning); + + /** + * This method converts the `InfraIf` to a human-readable string. + * + * @returns The string representation of `InfraIf`. + * + */ + InfoString ToString(void) const; + +private: + bool mInitialized : 1; + bool mIsRunning : 1; + uint32_t mIfIndex; +}; + +} // namespace BorderRouter +} // namespace ot + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + +#endif // INFRA_IF_HPP_ diff --git a/src/core/border_router/infra_if_platform.cpp b/src/core/border_router/infra_if_platform.cpp deleted file mode 100644 index 33e0c4c7b..000000000 --- a/src/core/border_router/infra_if_platform.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 - -#include "border_router/routing_manager.hpp" -#include "common/as_core_type.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) -{ - AsCoreType(aInstance).Get().RecvIcmp6Message(aInfraIfIndex, AsCoreType(aSrcAddress), - aBuffer, aBufferLength); -} - -extern "C" otError otPlatInfraIfStateChanged(otInstance *aInstance, uint32_t aInfraIfIndex, bool aIsRunning) -{ - return AsCoreType(aInstance).Get().HandleInfraIfStateChanged(aInfraIfIndex, - aIsRunning); -} - -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE diff --git a/src/core/border_router/router_advertisement.cpp b/src/core/border_router/router_advertisement.cpp deleted file mode 100644 index 66dbdd5dd..000000000 --- a/src/core/border_router/router_advertisement.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* - * 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 - -#include "common/as_core_type.hpp" -#include "common/code_utils.hpp" - -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(aCurOption) + aCurOption->GetSize(); - } - - VerifyOrExit(nextOption + sizeof(Option) <= bufferEnd, nextOption = nullptr); - VerifyOrExit(reinterpret_cast(nextOption)->GetSize() > 0, nextOption = nullptr); - VerifyOrExit(nextOption + reinterpret_cast(nextOption)->GetSize() <= bufferEnd, - nextOption = nullptr); - -exit: - return reinterpret_cast(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 = AsCoreType(&aPrefix.mPrefix); -} - -Ip6::Prefix PrefixInfoOption::GetPrefix(void) const -{ - Ip6::Prefix prefix; - - prefix.Set(mPrefix.GetBytes(), mPrefixLength); - return prefix; -} - -RouteInfoOption::RouteInfoOption(void) - : Option(Type::kRouteInfo, 0) - , mPrefixLength(0) - , mReserved(0) - , mRouteLifetime(0) -{ - OT_UNUSED_VARIABLE(mReserved); - - mPrefix.Clear(); -} - -void RouteInfoOption::SetPreference(RoutePreference aPreference) -{ - mReserved &= ~kPreferenceMask; - mReserved |= (NetworkData::RoutePreferenceToValue(aPreference) << kPreferenceOffset) & kPreferenceMask; -} - -RouteInfoOption::RoutePreference RouteInfoOption::GetPreference(void) const -{ - return NetworkData::RoutePreferenceFromValue((mReserved & kPreferenceMask) >> kPreferenceOffset); -} - -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); - - mPrefixLength = aPrefix.mLength; - mPrefix = AsCoreType(&aPrefix.mPrefix); -} - -Ip6::Prefix RouteInfoOption::GetPrefix(void) const -{ - Ip6::Prefix prefix; - - prefix.Set(mPrefix.GetBytes(), mPrefixLength); - return prefix; -} - -bool RouteInfoOption::IsValid(void) const -{ - return (GetLength() == 1 || GetLength() == 2 || GetLength() == 3) && - (mPrefixLength <= OT_IP6_ADDRESS_SIZE * CHAR_BIT) && NetworkData::IsRoutePreferenceValid(GetPreference()); -} - -void RouterAdvMessage::SetToDefault(void) -{ - mHeader.Clear(); - mHeader.SetType(Ip6::Icmp::Header::kTypeRouterAdvert); - mReachableTime = 0; - mRetransTimer = 0; -} - -const RouterAdvMessage &RouterAdvMessage::operator=(const RouterAdvMessage &aOther) -{ - mHeader = aOther.mHeader; - - // Set zero value and let platform do the calculation. - mHeader.SetChecksum(0); - - mReachableTime = aOther.mReachableTime; - mRetransTimer = aOther.mRetransTimer; - - return *this; -} - -bool RouterAdvMessage::operator==(const RouterAdvMessage &aOther) const -{ - return memcmp(&mHeader.mData, &aOther.mHeader.mData, sizeof(mHeader.mData)) == 0 && - mReachableTime == aOther.mReachableTime && mRetransTimer == aOther.mRetransTimer; -} - -RouterSolicitMessage::RouterSolicitMessage(void) -{ - mHeader.Clear(); - mHeader.SetType(Ip6::Icmp::Header::kTypeRouterSolicit); -} - -} // namespace RouterAdv - -} // namespace BorderRouter - -} // namespace ot - -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE diff --git a/src/core/border_router/router_advertisement.hpp b/src/core/border_router/router_advertisement.hpp deleted file mode 100644 index 060383b7b..000000000 --- a/src/core/border_router/router_advertisement.hpp +++ /dev/null @@ -1,511 +0,0 @@ -/* - * 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 - -#include -#include - -#include "common/encoding.hpp" -#include "common/equatable.hpp" -#include "net/icmp6.hpp" -#include "net/ip6.hpp" -#include "thread/network_data_types.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. - }; - - static constexpr uint8_t kLengthUnit = 8; ///< 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 size 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 (aSize + 7) / 8 * 8. - * - * @param[in] aSize The size of the option in unit of 1 byte. - * - */ - void SetSize(uint16_t aSize) { mLength = static_cast((aSize + kLengthUnit - 1) / kLengthUnit); } - - /** - * This method returns the size of the option (in bytes). - * - * @returns The size of the option in unit of 1 byte. - * - */ - uint16_t GetSize(void) const { return mLength * kLengthUnit; } - - /** - * This method sets the length of the option (in unit of 8 bytes). - * - * @param[in] aLength The length of the option in unit of 8 bytes. - * - */ - void SetLength(uint8_t aLength) { mLength = aLength; } - - /** - * This method returns the length of the option (in unit of 8 bytes). - * - * @returns The length of the option in unit of 8 bytes. - * - */ - uint16_t GetLength(void) const { return mLength; } - - /** - * 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 returns the on-link flag. - * - * @returns A boolean which indicates whether the on-link flag is set. - * - */ - bool GetOnLink(void) const { return (mReserved1 & kOnLinkFlagMask) != 0; } - - /** - * 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 returns the autonomous address-configuration (A) flag. - * - * @returns A boolean which indicates whether the A flag is set. - * - */ - bool GetAutoAddrConfig(void) const { return (mReserved1 & kAutoConfigFlagMask) != 0; } - - /** - * 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 returns the preferred lifetime of the prefix in seconds. - * - * @returns The preferred lifetime in seconds. - * - */ - uint32_t GetPreferredLifetime(void) const { return HostSwap32(mPreferredLifetime); } - - /** - * 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. - * - * @returns The IPv6 prefix in this option. - * - */ - Ip6::Prefix GetPrefix(void) const; - - /** - * This method tells whether this option is valid. - * - * @returns A boolean indicates whether this option is valid. - * - */ - bool IsValid(void) const - { - return (GetSize() == sizeof(*this)) && (mPrefixLength <= OT_IP6_ADDRESS_SIZE * CHAR_BIT) && - (GetPreferredLifetime() <= GetValidLifetime()); - } - -private: - static constexpr uint8_t kAutoConfigFlagMask = 0x40; // Bit mask of the Automatic Address Configure flag. - static constexpr uint8_t kOnLinkFlagMask = 0x80; // 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 type represents a route preference. - * - */ - typedef NetworkData::RoutePreference RoutePreference; - - /** - * This constructor initializes this option with zero prefix length. - * - */ - RouteInfoOption(void); - - /** - * This method sets the route preference. - * - * @param[in] aPreference The route preference. - * - */ - void SetPreference(RoutePreference aPreference); - - /** - * This method returns the route preference. - * - * @returns The route preference. - * - */ - RoutePreference GetPreference(void) const; - - /** - * 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 returns Route Lifetime in seconds. - * - * @returns The Route Lifetime in seconds. - * - */ - uint32_t GetRouteLifetime(void) const { return HostSwap32(mRouteLifetime); } - - /** - * 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. - * - * @returns The IPv6 prefix in this option. - * - */ - Ip6::Prefix GetPrefix(void) const; - - /** - * This method tells whether this option is valid. - * - * @returns A boolean indicates whether this option is valid. - * - */ - bool IsValid(void) const; - -private: - static constexpr uint8_t kPreferenceOffset = 3; - static constexpr uint8_t kPreferenceMask = 3 << kPreferenceOffset; - - 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 Unequatable -{ -public: - /** - * This constructor initializes the Router Advertisement message with - * zero router lifetime, reachable time and retransmission timer. - * - */ - RouterAdvMessage(void) { SetToDefault(); } - - /** - * This method sets the RA message to default values. - * - */ - void SetToDefault(void); - - /** - * This method sets the checksum value. - * - * @param[in] aChecksum The checksum value. - * - */ - void SetChecksum(uint16_t aChecksum) { mHeader.SetChecksum(aChecksum); } - - /** - * 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); - } - - /** - * This method returns the Router Lifetime. - * - * Zero Router Lifetime means we are not a default router. - * - * @returns The router lifetime in seconds. - * - */ - uint16_t GetRouterLifetime(void) const { return HostSwap16(mHeader.mData.m16[kRouteLifetimeIdx]); } - - /** - * This method returns the Managed Address Configuration ('m') flag. - * - * @returns A boolean which indicates whether the 'm' flag is set. - * - */ - bool GetManagedAddrConfig(void) const { return (mHeader.mData.m8[kReservedIdx] & kManagedAddressConfigMask) != 0; } - - /** - * This method overloads the assignment operator. - * - */ - const RouterAdvMessage &operator=(const RouterAdvMessage &aOther); - - /** - * This method overloads operator `==` to evaluate whether or not - * two instances of `RouterAdvMessage` are equal. - * - * @param[in] aOther The other `RouterAdvMessage` instance to compare with. - * - * @retval TRUE If the two `RouterAdvMessage` instances are equal. - * @retval FALSE If the two `RouterAdvMessage` instances are not equal. - * - */ - bool operator==(const RouterAdvMessage &aOther) const; - -private: - // The index of Route Lifetime in ICMPv6 Header Data. In unit of 2 octets. - static constexpr uint8_t kRouteLifetimeIdx = 1; - - // The index of Reserved byte in ICMPv6 Header Data. In unit of 1 octet. - static constexpr uint8_t kReservedIdx = 1; - - // The bitmask of the Managed Address Configuration ('m') flag. - static constexpr uint8_t kManagedAddressConfigMask = 0x80; - - 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_ diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index ef1e1ccd5..fe219832b 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -47,6 +47,7 @@ #include "common/log.hpp" #include "common/random.hpp" #include "common/settings.hpp" +#include "meshcop/extended_panid.hpp" #include "net/ip6.hpp" #include "thread/network_data_leader.hpp" #include "thread/network_data_local.hpp" @@ -62,24 +63,22 @@ RoutingManager::RoutingManager(Instance &aInstance) : InstanceLocator(aInstance) , mIsRunning(false) , mIsEnabled(false) - , mInfraIfIsRunning(false) - , mInfraIfIndex(0) + , mInfraIf(aInstance) , mIsAdvertisingLocalOnLinkPrefix(false) , mOnLinkPrefixDeprecateTimer(aInstance, HandleOnLinkPrefixDeprecateTimer) , mIsAdvertisingLocalNat64Prefix(false) + , mDiscoveredPrefixTable(aInstance) , mTimeRouterAdvMessageLastUpdate(TimerMilli::GetNow()) , mLearntRouterAdvMessageFromHost(false) - , mDiscoveredPrefixInvalidTimer(aInstance, HandleDiscoveredPrefixInvalidTimer) , mDiscoveredPrefixStaleTimer(aInstance, HandleDiscoveredPrefixStaleTimer) , mRouterAdvertisementCount(0) , mLastRouterAdvertisementSendTime(TimerMilli::GetNow() - kMinDelayBetweenRtrAdvs) -#if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE - , mVicariousRouterSolicitTimer(aInstance, HandleVicariousRouterSolicitTimer) -#endif , mRouterSolicitTimer(aInstance, HandleRouterSolicitTimer) , mRouterSolicitCount(0) , mRoutingPolicyTimer(aInstance, HandleRoutingPolicyTimer) { + mFavoredDiscoveredOnLinkPrefix.Clear(); + mBrUlaPrefix.Clear(); mLocalOmrPrefix.Clear(); @@ -93,26 +92,23 @@ Error RoutingManager::Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning) { Error error; - VerifyOrExit(!IsInitialized(), error = kErrorInvalidState); - VerifyOrExit(aInfraIfIndex > 0, error = kErrorInvalidArgs); + SuccessOrExit(error = mInfraIf.Init(aInfraIfIndex)); SuccessOrExit(error = LoadOrGenerateRandomBrUlaPrefix()); GenerateOmrPrefix(); #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE GenerateNat64Prefix(); #endif - SuccessOrExit(error = LoadOrGenerateRandomOnLinkPrefix()); + GenerateOnLinkPrefix(); - mInfraIfIndex = aInfraIfIndex; - - // Initialize the infra interface status. - SuccessOrExit(error = HandleInfraIfStateChanged(mInfraIfIndex, aInfraIfIsRunning)); + error = mInfraIf.HandleStateChanged(mInfraIf.GetIfIndex(), aInfraIfIsRunning); exit: if (error != kErrorNone) { - mInfraIfIndex = 0; + mInfraIf.Deinit(); } + return error; } @@ -201,8 +197,6 @@ exit: void RoutingManager::GenerateOmrPrefix(void) { - IgnoreError(Get().Delete()); - mLocalOmrPrefix = mBrUlaPrefix; mLocalOmrPrefix.SetSubnetId(kOmrPrefixSubnetId); mLocalOmrPrefix.SetLength(kOmrPrefixLength); @@ -222,42 +216,23 @@ void RoutingManager::GenerateNat64Prefix(void) } #endif -Error RoutingManager::LoadOrGenerateRandomOnLinkPrefix(void) +void RoutingManager::GenerateOnLinkPrefix(void) { - Error error = kErrorNone; - bool generated = false; + MeshCoP::ExtendedPanId extPanId = Get().GetExtPanId(); - if (Get().Read(mLocalOnLinkPrefix) != kErrorNone || - !mLocalOnLinkPrefix.IsUniqueLocal()) - { - Ip6::NetworkPrefix randomOnLinkPrefix; + mLocalOnLinkPrefix.mPrefix.mFields.m8[0] = 0xfd; + // Global ID: 40 most significant bits of Extended PAN ID + memcpy(mLocalOnLinkPrefix.mPrefix.mFields.m8 + 1, extPanId.m8, 5); + // Subnet ID: 16 least significant bits of Extended PAN ID + memcpy(mLocalOnLinkPrefix.mPrefix.mFields.m8 + 6, extPanId.m8 + 6, 2); + mLocalOnLinkPrefix.SetLength(kOnLinkPrefixLength); - error = randomOnLinkPrefix.GenerateRandomUla(); - if (error != kErrorNone) - { - LogCrit("Failed to generate random on-link prefix"); - ExitNow(); - } - - mLocalOnLinkPrefix.Set(randomOnLinkPrefix); - mLocalOnLinkPrefix.SetSubnetId(0); - - IgnoreError(Get().Save(mLocalOnLinkPrefix)); - generated = true; - } - - OT_UNUSED_VARIABLE(generated); - - LogNote("Local on-link prefix: %s (%s)", mLocalOnLinkPrefix.ToString().AsCString(), - generated ? "generated" : "loaded"); - -exit: - return error; + LogNote("Local on-link prefix: %s", mLocalOnLinkPrefix.ToString().AsCString()); } void RoutingManager::EvaluateState(void) { - if (mIsEnabled && Get().IsAttached() && mInfraIfIsRunning) + if (mIsEnabled && Get().IsAttached() && mInfraIf.IsRunning()) { Start(); } @@ -284,9 +259,11 @@ void RoutingManager::Stop(void) UnpublishLocalOmrPrefix(); + mFavoredDiscoveredOnLinkPrefix.Clear(); + if (mIsAdvertisingLocalOnLinkPrefix) { - RemoveExternalRoute(mLocalOnLinkPrefix); + UnpublishExternalRoute(mLocalOnLinkPrefix); // Start deprecating the local on-link prefix to send a PIO // with zero preferred lifetime in `SendRouterAdvertisement`. @@ -296,27 +273,21 @@ void RoutingManager::Stop(void) #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE if (mIsAdvertisingLocalNat64Prefix) { - RemoveExternalRoute(mLocalNat64Prefix); + UnpublishExternalRoute(mLocalNat64Prefix); mIsAdvertisingLocalNat64Prefix = false; } #endif // Use empty OMR & on-link prefixes to invalidate possible advertised prefixes. - SendRouterAdvertisement(OmrPrefixArray(), nullptr); + SendRouterAdvertisement(OmrPrefixArray()); mAdvertisedOmrPrefixes.Clear(); - mIsAdvertisingLocalOnLinkPrefix = false; mOnLinkPrefixDeprecateTimer.Stop(); - InvalidateAllDiscoveredPrefixes(); - mDiscoveredPrefixes.Clear(); - mDiscoveredPrefixInvalidTimer.Stop(); + mDiscoveredPrefixTable.RemoveAllEntries(); mDiscoveredPrefixStaleTimer.Stop(); mRouterAdvertisementCount = 0; -#if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE - mVicariousRouterSolicitTimer.Stop(); -#endif mRouterSolicitTimer.Stop(); mRouterSolicitCount = 0; @@ -330,55 +301,28 @@ exit: return; } -void RoutingManager::RecvIcmp6Message(uint32_t aInfraIfIndex, - const Ip6::Address &aSrcAddress, - const uint8_t * aBuffer, - uint16_t aBufferLength) +void RoutingManager::HandleReceived(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress) { - Error error = kErrorNone; const Ip6::Icmp::Header *icmp6Header; - VerifyOrExit(IsInitialized() && mIsRunning, error = kErrorDrop); - VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = kErrorDrop); - VerifyOrExit(aBuffer != nullptr && aBufferLength >= sizeof(*icmp6Header), error = kErrorParse); + VerifyOrExit(mIsRunning); - icmp6Header = reinterpret_cast(aBuffer); + icmp6Header = reinterpret_cast(aPacket.GetBytes()); switch (icmp6Header->GetType()) { case Ip6::Icmp::Header::kTypeRouterAdvert: - HandleRouterAdvertisement(aSrcAddress, aBuffer, aBufferLength); + HandleRouterAdvertisement(aPacket, aSrcAddress); break; case Ip6::Icmp::Header::kTypeRouterSolicit: - HandleRouterSolicit(aSrcAddress, aBuffer, aBufferLength); + HandleRouterSolicit(aPacket, aSrcAddress); break; default: break; } exit: - if (error != kErrorNone) - { - LogDebg("Dropped ICMPv6 message: %s", ErrorToString(error)); - } -} - -Error RoutingManager::HandleInfraIfStateChanged(uint32_t aInfraIfIndex, bool aIsRunning) -{ - Error error = kErrorNone; - - VerifyOrExit(IsInitialized(), error = kErrorInvalidState); - VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = kErrorInvalidArgs); - VerifyOrExit(aIsRunning != mInfraIfIsRunning); - - LogInfo("Infra interface (%u) state changed: %sRUNNING -> %sRUNNING", aInfraIfIndex, - (mInfraIfIsRunning ? "" : "NOT "), (aIsRunning ? "" : "NOT ")); - - mInfraIfIsRunning = aIsRunning; - EvaluateState(); - -exit: - return error; + return; } void RoutingManager::HandleNotifierEvents(Events aEvents) @@ -392,11 +336,44 @@ void RoutingManager::HandleNotifierEvents(Events aEvents) if (mIsRunning && aEvents.Contains(kEventThreadNetdataChanged)) { - // Invalidate discovered prefixes because OMR Prefixes in Network Data may change. - InvalidateDiscoveredPrefixes(); + // Remove all OMR prefixes in Network Data from the + // discovered prefix table. + + NetworkData::Iterator iterator = NetworkData::kIteratorInit; + NetworkData::OnMeshPrefixConfig prefixConfig; + + while (Get().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone) + { + if (!IsValidOmrPrefix(prefixConfig)) + { + continue; + } + + mDiscoveredPrefixTable.RemoveRoutePrefix(prefixConfig.GetPrefix(), + DiscoveredPrefixTable::kUnpublishFromNetData); + } + StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter); } + if (aEvents.Contains(kEventThreadExtPanIdChanged)) + { + if (mIsAdvertisingLocalOnLinkPrefix) + { + UnpublishExternalRoute(mLocalOnLinkPrefix); + // TODO: consider deprecating/invalidating existing + // on-link prefix + mIsAdvertisingLocalOnLinkPrefix = false; + } + + GenerateOnLinkPrefix(); + + if (mIsRunning) + { + StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter); + } + } + exit: return; } @@ -405,81 +382,98 @@ void RoutingManager::EvaluateOmrPrefix(OmrPrefixArray &aNewOmrPrefixes) { NetworkData::Iterator iterator = NetworkData::kIteratorInit; NetworkData::OnMeshPrefixConfig onMeshPrefixConfig; - Ip6::Prefix * smallestOmrPrefix = nullptr; - Ip6::Prefix * publishedLocalOmrPrefix = nullptr; + OmrPrefix * favoredOmrEntry = nullptr; + OmrPrefix * localOmrEntry = nullptr; OT_ASSERT(mIsRunning); while (Get().GetNextOnMeshPrefix(iterator, onMeshPrefixConfig) == kErrorNone) { - const Ip6::Prefix &prefix = onMeshPrefixConfig.GetPrefix(); + OmrPrefix *entry; if (!IsValidOmrPrefix(onMeshPrefixConfig)) { continue; } - if (aNewOmrPrefixes.Contains(prefix)) + entry = aNewOmrPrefixes.FindMatching(onMeshPrefixConfig.GetPrefix()); + + if (entry != nullptr) { - // Ignore duplicate prefixes. - continue; + // Update the entry if we find the same prefix with higher + // preference in network data + + if (onMeshPrefixConfig.GetPreference() <= entry->GetPreference()) + { + continue; + } + + entry->SetPreference(onMeshPrefixConfig.GetPreference()); + } + else + { + entry = aNewOmrPrefixes.PushBack(); + + if (entry == nullptr) + { + LogWarn("EvaluateOmrPrefix: Too many OMR prefixes, ignoring prefix %s", + onMeshPrefixConfig.GetPrefix().ToString().AsCString()); + continue; + } + + entry->InitFrom(onMeshPrefixConfig); } - if (aNewOmrPrefixes.PushBack(prefix) != kErrorNone) + if (onMeshPrefixConfig.mPreferred) { - LogWarn("EvaluateOmrPrefix: Too many OMR prefixes, ignoring prefix %s", prefix.ToString().AsCString()); - continue; + if ((favoredOmrEntry == nullptr) || (entry->IsFavoredOver(*favoredOmrEntry))) + { + favoredOmrEntry = entry; + } } - if (smallestOmrPrefix == nullptr || (prefix < *smallestOmrPrefix)) + if (entry->GetPrefix() == mLocalOmrPrefix) { - smallestOmrPrefix = aNewOmrPrefixes.Back(); - } - - if (prefix == mLocalOmrPrefix) - { - publishedLocalOmrPrefix = aNewOmrPrefixes.Back(); + localOmrEntry = entry; } } // Decide if we need to add or remove our local OMR prefix. - if (aNewOmrPrefixes.IsEmpty()) + if (favoredOmrEntry == nullptr) { - LogInfo("EvaluateOmrPrefix: No valid OMR prefixes found in Thread network"); - - if (PublishLocalOmrPrefix() == kErrorNone) - { - IgnoreError(aNewOmrPrefixes.PushBack(mLocalOmrPrefix)); - } + LogInfo("EvaluateOmrPrefix: No preferred OMR prefixes found in Thread network"); // The `aNewOmrPrefixes` remains empty if we fail to publish // the local OMR prefix. + SuccessOrExit(PublishLocalOmrPrefix()); + + localOmrEntry = aNewOmrPrefixes.PushBack(); + VerifyOrExit(localOmrEntry != nullptr); + + localOmrEntry->Init(mLocalOmrPrefix, NetworkData::kRoutePreferenceLow); } - else + else if (favoredOmrEntry == localOmrEntry) { - OT_ASSERT(smallestOmrPrefix != nullptr); + IgnoreError(PublishLocalOmrPrefix()); + } + else if (IsOmrPrefixAddedToLocalNetworkData()) + { + LogInfo("EvaluateOmrPrefix: There is already a preferred OMR prefix %s in the Thread network", + favoredOmrEntry->ToString().AsCString()); - if (*smallestOmrPrefix == mLocalOmrPrefix) + UnpublishLocalOmrPrefix(); + + if (localOmrEntry != nullptr) { - IgnoreError(PublishLocalOmrPrefix()); - } - else if (IsOmrPrefixAddedToLocalNetworkData()) - { - LogInfo("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 element and then popping it from the list. - if (publishedLocalOmrPrefix != nullptr) - { - *publishedLocalOmrPrefix = *aNewOmrPrefixes.Back(); - aNewOmrPrefixes.PopBack(); - } + // with popped last entry in the list. + *localOmrEntry = *aNewOmrPrefixes.PopBack(); } } + +exit: + return; } Error RoutingManager::PublishLocalOmrPrefix(void) @@ -498,7 +492,7 @@ Error RoutingManager::PublishLocalOmrPrefix(void) omrPrefixConfig.mPreferred = true; omrPrefixConfig.mOnMesh = true; omrPrefixConfig.mDefaultRoute = false; - omrPrefixConfig.mPreference = NetworkData::kRoutePreferenceMedium; + omrPrefixConfig.mPreference = NetworkData::kRoutePreferenceLow; error = Get().AddOnMeshPrefix(omrPrefixConfig); if (error != kErrorNone) @@ -542,7 +536,7 @@ bool RoutingManager::IsOmrPrefixAddedToLocalNetworkData(void) const return Get().ContainsOnMeshPrefix(mLocalOmrPrefix); } -Error RoutingManager::AddExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64) +Error RoutingManager::PublishExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64) { Error error; NetworkData::ExternalRouteConfig routeConfig; @@ -555,91 +549,86 @@ Error RoutingManager::AddExternalRoute(const Ip6::Prefix &aPrefix, RoutePreferen routeConfig.mNat64 = aNat64; routeConfig.mPreference = aRoutePreference; - error = Get().AddHasRoutePrefix(routeConfig); + error = Get().PublishExternalRoute(routeConfig); + if (error != kErrorNone) { - LogWarn("Failed to add external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error)); - } - else - { - Get().HandleServerDataUpdated(); - LogInfo("Adding external route %s", aPrefix.ToString().AsCString()); + LogWarn("Failed to publish external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error)); } - return error; + return (error == kErrorAlready) ? kErrorNone : error; } -void RoutingManager::RemoveExternalRoute(const Ip6::Prefix &aPrefix) +void RoutingManager::UnpublishExternalRoute(const Ip6::Prefix &aPrefix) { Error error = kErrorNone; VerifyOrExit(mIsRunning); - SuccessOrExit(error = Get().RemoveHasRoutePrefix(aPrefix)); + error = Get().UnpublishPrefix(aPrefix); - Get().HandleServerDataUpdated(); - LogInfo("Removing external route %s", aPrefix.ToString().AsCString()); - -exit: if (error != kErrorNone) { - LogWarn("Failed to remove external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error)); + LogWarn("Failed to unpublish route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error)); } + +exit: + return; } -const Ip6::Prefix *RoutingManager::EvaluateOnLinkPrefix(void) +void RoutingManager::EvaluateOnLinkPrefix(void) { - const Ip6::Prefix *newOnLinkPrefix = nullptr; - const Ip6::Prefix *smallestOnLinkPrefix = nullptr; + VerifyOrExit(!IsRouterSolicitationInProgress()); - // We don't evaluate on-link prefix if we are doing Router Solicitation. - VerifyOrExit(!IsRouterSolicitationInProgress(), - newOnLinkPrefix = (mIsAdvertisingLocalOnLinkPrefix ? &mLocalOnLinkPrefix : nullptr)); + mDiscoveredPrefixTable.FindFavoredOnLinkPrefix(mFavoredDiscoveredOnLinkPrefix); - for (const ExternalPrefix &prefix : mDiscoveredPrefixes) + if (mFavoredDiscoveredOnLinkPrefix.GetLength() == 0) { - if (!prefix.mIsOnLinkPrefix || prefix.IsDeprecated()) - { - continue; - } - - if (smallestOnLinkPrefix == nullptr || (prefix.mPrefix < *smallestOnLinkPrefix)) - { - smallestOnLinkPrefix = &prefix.mPrefix; - } - } - - // We start advertising our local on-link prefix if there is no existing one. - if (smallestOnLinkPrefix == nullptr) - { - if (mIsAdvertisingLocalOnLinkPrefix || - (AddExternalRoute(mLocalOnLinkPrefix, NetworkData::kRoutePreferenceMedium) == kErrorNone)) - { - newOnLinkPrefix = &mLocalOnLinkPrefix; - } + // We need to advertise our local on-link prefix since there is + // no discovered on-link prefix. mOnLinkPrefixDeprecateTimer.Stop(); + VerifyOrExit(!mIsAdvertisingLocalOnLinkPrefix); + + SuccessOrExit(PublishExternalRoute(mLocalOnLinkPrefix, NetworkData::kRoutePreferenceMedium)); + + mIsAdvertisingLocalOnLinkPrefix = true; + LogInfo("Start advertising on-link prefix %s on %s", mLocalOnLinkPrefix.ToString().AsCString(), + mInfraIf.ToString().AsCString()); + + // We remove the local on-link prefix from discovered prefix + // table, in case it was previously discovered and included in + // the table (now as a deprecating entry). We remove it with + // `kKeepInNetData` flag to ensure that the prefix is not + // unpublished from network data. + // + // Note that `ShouldProcessPrefixInfoOption()` will also check + // not allow the local on-link prefix to be added in the prefix + // table while we are advertising it. + + mDiscoveredPrefixTable.RemoveOnLinkPrefix(mLocalOnLinkPrefix, DiscoveredPrefixTable::kKeepInNetData); } - // When an application-specific on-link prefix is received and it is bigger than the - // advertised prefix, we will not remove the advertised prefix. In this case, there - // will be two on-link prefixes on the infra link. But all BRs will still converge to - // the same smallest on-link prefix and the application-specific prefix is not used. - else if (mIsAdvertisingLocalOnLinkPrefix) + else { - if (mLocalOnLinkPrefix < *smallestOnLinkPrefix) + VerifyOrExit(mIsAdvertisingLocalOnLinkPrefix); + + // When an application-specific on-link prefix is received and + // it is larger than the local prefix, we will not remove the + // advertised local prefix. In this case, there will be two + // on-link prefixes on the infra link. But all BRs will still + // converge to the same smallest/favored on-link prefix and the + // application-specific prefix is not used. + + if (!(mLocalOnLinkPrefix < mFavoredDiscoveredOnLinkPrefix)) { - newOnLinkPrefix = &mLocalOnLinkPrefix; - } - else - { - LogInfo("EvaluateOnLinkPrefix: There is already smaller on-link prefix %s on interface %u", - smallestOnLinkPrefix->ToString().AsCString(), mInfraIfIndex); + LogInfo("EvaluateOnLinkPrefix: There is already favored on-link prefix %s on %s", + mFavoredDiscoveredOnLinkPrefix.ToString().AsCString(), mInfraIf.ToString().AsCString()); DeprecateOnLinkPrefix(); } } exit: - return newOnLinkPrefix; + return; } void RoutingManager::HandleOnLinkPrefixDeprecateTimer(Timer &aTimer) @@ -649,14 +638,22 @@ void RoutingManager::HandleOnLinkPrefixDeprecateTimer(Timer &aTimer) void RoutingManager::HandleOnLinkPrefixDeprecateTimer(void) { + OT_ASSERT(!mIsAdvertisingLocalOnLinkPrefix); + LogInfo("Local on-link prefix %s expired", mLocalOnLinkPrefix.ToString().AsCString()); - RemoveExternalRoute(mLocalOnLinkPrefix); + + if (!mDiscoveredPrefixTable.ContainsOnLinkPrefix(mLocalOnLinkPrefix)) + { + UnpublishExternalRoute(mLocalOnLinkPrefix); + } } void RoutingManager::DeprecateOnLinkPrefix(void) { OT_ASSERT(mIsAdvertisingLocalOnLinkPrefix); + mIsAdvertisingLocalOnLinkPrefix = false; + LogInfo("Deprecate local on-link prefix %s", mLocalOnLinkPrefix.ToString().AsCString()); mOnLinkPrefixDeprecateTimer.StartAt(mTimeAdvertisedOnLinkPrefix, TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime)); @@ -694,7 +691,7 @@ void RoutingManager::EvaluateNat64Prefix(void) // Advertise local NAT64 prefix. if (!mIsAdvertisingLocalNat64Prefix && - AddExternalRoute(mLocalNat64Prefix, NetworkData::kRoutePreferenceLow, /* aNat64= */ true) == kErrorNone) + PublishExternalRoute(mLocalNat64Prefix, NetworkData::kRoutePreferenceLow, /* aNat64= */ true) == kErrorNone) { mIsAdvertisingLocalNat64Prefix = true; } @@ -706,7 +703,7 @@ void RoutingManager::EvaluateNat64Prefix(void) LogNote("Withdrawing local NAT64 prefix since a smaller one %s exists.", smallestNat64Prefix.ToString().AsCString()); - RemoveExternalRoute(mLocalNat64Prefix); + UnpublishExternalRoute(mLocalNat64Prefix); mIsAdvertisingLocalNat64Prefix = false; } } @@ -720,20 +717,19 @@ void RoutingManager::EvaluateRoutingPolicy(void) { OT_ASSERT(mIsRunning); - const Ip6::Prefix *newOnLinkPrefix = nullptr; - OmrPrefixArray newOmrPrefixes; + OmrPrefixArray newOmrPrefixes; LogInfo("Evaluating routing policy"); // 0. Evaluate on-link, OMR and NAT64 prefixes. - newOnLinkPrefix = EvaluateOnLinkPrefix(); + EvaluateOnLinkPrefix(); EvaluateOmrPrefix(newOmrPrefixes); #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE EvaluateNat64Prefix(); #endif // 1. Send Router Advertisement message if necessary. - SendRouterAdvertisement(newOmrPrefixes, newOnLinkPrefix); + SendRouterAdvertisement(newOmrPrefixes); if (newOmrPrefixes.IsEmpty()) { @@ -758,9 +754,8 @@ void RoutingManager::EvaluateRoutingPolicy(void) StartRoutingPolicyEvaluationDelay(Time::SecToMsec(nextSendDelay)); } - // 3. Update advertised on-link & OMR prefixes information. - mIsAdvertisingLocalOnLinkPrefix = (newOnLinkPrefix == &mLocalOnLinkPrefix); - mAdvertisedOmrPrefixes = newOmrPrefixes; + // 3. Update OMR prefixes information. + mAdvertisedOmrPrefixes = newOmrPrefixes; } void RoutingManager::StartRoutingPolicyEvaluationJitter(uint32_t aJitterMilli) @@ -793,10 +788,6 @@ void RoutingManager::StartRouterSolicitationDelay(void) OT_ASSERT(mRouterSolicitCount == 0); -#if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE - mVicariousRouterSolicitTimer.Stop(); -#endif - static_assert(kMaxRtrSolicitationDelay > 0, "invalid maximum Router Solicitation delay"); randomDelay = Random::NonCrypto::GetUint32InRange(0, Time::SecToMsec(kMaxRtrSolicitationDelay)); @@ -815,115 +806,72 @@ bool RoutingManager::IsRouterSolicitationInProgress(void) const Error RoutingManager::SendRouterSolicitation(void) { - Ip6::Address destAddress; - RouterAdv::RouterSolicitMessage routerSolicit; + Ip6::Address destAddress; + Ip6::Nd::RouterSolicitMessage routerSolicit; + InfraIf::Icmp6Packet packet; OT_ASSERT(IsInitialized()); + packet.InitFrom(routerSolicit); destAddress.SetToLinkLocalAllRoutersMulticast(); - return otPlatInfraIfSendIcmp6Nd(mInfraIfIndex, &destAddress, reinterpret_cast(&routerSolicit), - sizeof(routerSolicit)); + + return mInfraIf.Send(packet, destAddress); } -// This method sends Router Advertisement messages to advertise on-link prefix and route for OMR prefix. -// @param[in] aNewOmrPrefixes An array of the new OMR prefixes to be advertised. -// Empty array 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 RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefixes, const Ip6::Prefix *aNewOnLinkPrefix) +void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefixes) { - uint8_t buffer[kMaxRouterAdvMessageLength]; - uint16_t bufferLength = 0; + uint8_t buffer[kMaxRouterAdvMessageLength]; + Ip6::Nd::RouterAdvertMessage raMsg(mRouterAdvertHeader, buffer); - static_assert(sizeof(mRouterAdvMessage) <= sizeof(buffer), "RA buffer too small"); - memcpy(buffer, &mRouterAdvMessage, sizeof(mRouterAdvMessage)); - bufferLength += sizeof(mRouterAdvMessage); + // Append PIO for local on-link prefix. Ensure it is either being + // advertised or deprecated. - if (aNewOnLinkPrefix != nullptr) + if (mIsAdvertisingLocalOnLinkPrefix || mOnLinkPrefixDeprecateTimer.IsRunning()) { - OT_ASSERT(aNewOnLinkPrefix == &mLocalOnLinkPrefix); + uint32_t validLifetime = kDefaultOnLinkPrefixLifetime; + uint32_t preferredLifetime = kDefaultOnLinkPrefixLifetime; - RouterAdv::PrefixInfoOption pio; - - pio.SetOnLink(true); - pio.SetAutoAddrConfig(true); - pio.SetValidLifetime(kDefaultOnLinkPrefixLifetime); - pio.SetPreferredLifetime(kDefaultOnLinkPrefixLifetime); - pio.SetPrefix(*aNewOnLinkPrefix); - - OT_ASSERT(bufferLength + pio.GetSize() <= sizeof(buffer)); - memcpy(buffer + bufferLength, &pio, pio.GetSize()); - bufferLength += pio.GetSize(); - - if (!mIsAdvertisingLocalOnLinkPrefix) + if (mOnLinkPrefixDeprecateTimer.IsRunning()) { - LogInfo("Start advertising new on-link prefix %s on interface %u", aNewOnLinkPrefix->ToString().AsCString(), - mInfraIfIndex); + validLifetime = TimeMilli::MsecToSec(mOnLinkPrefixDeprecateTimer.GetFireTime() - TimerMilli::GetNow()); + preferredLifetime = 0; } - LogInfo("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)", - aNewOnLinkPrefix->ToString().AsCString(), pio.GetPreferredLifetime(), pio.GetValidLifetime()); + SuccessOrAssert(raMsg.AppendPrefixInfoOption(mLocalOnLinkPrefix, validLifetime, preferredLifetime)); - mTimeAdvertisedOnLinkPrefix = TimerMilli::GetNow(); - } - else if (mOnLinkPrefixDeprecateTimer.IsRunning()) - { - RouterAdv::PrefixInfoOption pio; - - pio.SetOnLink(true); - pio.SetAutoAddrConfig(true); - pio.SetValidLifetime(TimeMilli::MsecToSec(mOnLinkPrefixDeprecateTimer.GetFireTime() - TimerMilli::GetNow())); - - // Set zero preferred lifetime to immediately deprecate the advertised on-link prefix. - pio.SetPreferredLifetime(0); - pio.SetPrefix(mLocalOnLinkPrefix); - - OT_ASSERT(bufferLength + pio.GetSize() <= sizeof(buffer)); - memcpy(buffer + bufferLength, &pio, pio.GetSize()); - bufferLength += pio.GetSize(); - - LogInfo("Send on-link prefix %s in PIO (preferred lifetime = %u seconds, valid lifetime = %u seconds)", - mLocalOnLinkPrefix.ToString().AsCString(), pio.GetPreferredLifetime(), pio.GetValidLifetime()); - } - - // Invalidate the advertised OMR prefixes if they are no longer in the new OMR prefix array. - - for (const Ip6::Prefix &advertisedOmrPrefix : mAdvertisedOmrPrefixes) - { - if (!aNewOmrPrefixes.Contains(advertisedOmrPrefix)) + if (mIsAdvertisingLocalOnLinkPrefix) { - RouterAdv::RouteInfoOption rio; + mTimeAdvertisedOnLinkPrefix = TimerMilli::GetNow(); + } - // Set zero route lifetime to immediately invalidate the advertised OMR prefix. - rio.SetRouteLifetime(0); - rio.SetPrefix(advertisedOmrPrefix); + LogInfo("RouterAdvert: Added PIO for %s (valid=%u, preferred=%u)", mLocalOnLinkPrefix.ToString().AsCString(), + validLifetime, preferredLifetime); + } - OT_ASSERT(bufferLength + rio.GetSize() <= sizeof(buffer)); - memcpy(buffer + bufferLength, &rio, rio.GetSize()); - bufferLength += rio.GetSize(); + // Invalidate previously advertised OMR prefixes if they are no + // longer in the new OMR prefix array. - LogInfo("Stop advertising OMR prefix %s on interface %u", advertisedOmrPrefix.ToString().AsCString(), - mInfraIfIndex); + for (const OmrPrefix &omrPrefix : mAdvertisedOmrPrefixes) + { + if (!aNewOmrPrefixes.ContainsMatching(omrPrefix.GetPrefix())) + { + SuccessOrAssert( + raMsg.AppendRouteInfoOption(omrPrefix.GetPrefix(), /* aRouteLifetime */ 0, omrPrefix.GetPreference())); + + LogInfo("RouterAdvert: Added RIO for %s (lifetime=0)", omrPrefix.ToString().AsCString()); } } - for (const Ip6::Prefix &newOmrPrefix : aNewOmrPrefixes) + for (const OmrPrefix &omrPrefix : aNewOmrPrefixes) { - RouterAdv::RouteInfoOption rio; + SuccessOrAssert( + raMsg.AppendRouteInfoOption(omrPrefix.GetPrefix(), kDefaultOmrPrefixLifetime, omrPrefix.GetPreference())); - rio.SetRouteLifetime(kDefaultOmrPrefixLifetime); - rio.SetPrefix(newOmrPrefix); - - OT_ASSERT(bufferLength + rio.GetSize() <= sizeof(buffer)); - memcpy(buffer + bufferLength, &rio, rio.GetSize()); - bufferLength += rio.GetSize(); - - LogInfo("Send OMR prefix %s in RIO (valid lifetime = %u seconds)", newOmrPrefix.ToString().AsCString(), + LogInfo("RouterAdvert: Added RIO for %s (lifetime=%u)", omrPrefix.ToString().AsCString(), kDefaultOmrPrefixLifetime); } - // Send the message only when there are options. - if (bufferLength > sizeof(mRouterAdvMessage)) + if (raMsg.ContainsAnyOptions()) { Error error; Ip6::Address destAddress; @@ -931,17 +879,20 @@ void RoutingManager::SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefix ++mRouterAdvertisementCount; destAddress.SetToLinkLocalAllNodesMulticast(); - error = otPlatInfraIfSendIcmp6Nd(mInfraIfIndex, &destAddress, buffer, bufferLength); + + error = mInfraIf.Send(raMsg.GetAsPacket(), destAddress); if (error == kErrorNone) { mLastRouterAdvertisementSendTime = TimerMilli::GetNow(); - LogInfo("Sent Router Advertisement on interface %u", mInfraIfIndex); - DumpDebg("[BR-CERT] direction=send | type=RA |", buffer, bufferLength); + LogInfo("Sent Router Advertisement on %s", mInfraIf.ToString().AsCString()); + DumpDebg("[BR-CERT] direction=send | type=RA |", raMsg.GetAsPacket().GetBytes(), + raMsg.GetAsPacket().GetLength()); } else { - LogWarn("Failed to send Router Advertisement on interface %u: %s", mInfraIfIndex, ErrorToString(error)); + LogWarn("Failed to send Router Advertisement on %s: %s", mInfraIf.ToString().AsCString(), + ErrorToString(error)); } } } @@ -964,37 +915,21 @@ bool RoutingManager::IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix) (aOmrPrefix.mLength >= 3 && (aOmrPrefix.GetBytes()[0] & 0xE0) == 0x20); } -bool RoutingManager::IsValidOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio) +bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption &aPio) { - return IsValidOnLinkPrefix(aPio.GetPrefix()) && aPio.GetOnLink() && aPio.GetAutoAddrConfig(); + Ip6::Prefix prefix; + + aPio.GetPrefix(prefix); + + return IsValidOnLinkPrefix(prefix) && aPio.IsOnLinkFlagSet() && aPio.IsAutoAddrConfigFlagSet(); } bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix) { - return !aOnLinkPrefix.IsLinkLocal() && !aOnLinkPrefix.IsMulticast(); + return aOnLinkPrefix.IsValid() && (aOnLinkPrefix.GetLength() > 0) && !aOnLinkPrefix.IsLinkLocal() && + !aOnLinkPrefix.IsMulticast(); } -#if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE -void RoutingManager::HandleVicariousRouterSolicitTimer(Timer &aTimer) -{ - aTimer.Get().HandleVicariousRouterSolicitTimer(); -} - -void RoutingManager::HandleVicariousRouterSolicitTimer(void) -{ - LogInfo("Vicarious router solicitation time out"); - - for (const ExternalPrefix &prefix : mDiscoveredPrefixes) - { - if (prefix.mTimeLastUpdate <= mTimeVicariousRouterSolicitStart) - { - StartRouterSolicitationDelay(); - break; - } - } -} -#endif - void RoutingManager::HandleRouterSolicitTimer(Timer &aTimer) { aTimer.Get().HandleRouterSolicitTimer(); @@ -1035,35 +970,20 @@ void RoutingManager::HandleRouterSolicitTimer(void) } else { - // Invalidate/deprecate all OMR/on-link prefixes that are not refreshed during Router Solicitation. - for (ExternalPrefix &prefix : mDiscoveredPrefixes) - { - if (prefix.mTimeLastUpdate <= mTimeRouterSolicitStart) - { - if (prefix.mIsOnLinkPrefix) - { - prefix.mPreferredLifetime = 0; - } - else - { - InvalidateDiscoveredPrefixes(&prefix.mPrefix, prefix.mIsOnLinkPrefix); - } - } - } + // Remove route prefixes and deprecate on-link prefixes that + // are not refreshed during Router Solicitation. + mDiscoveredPrefixTable.RemoveOrDeprecateOldEntries(mTimeRouterSolicitStart); // Invalidate the learned RA message if it is not refreshed during Router Solicitation. if (mTimeRouterAdvMessageLastUpdate <= mTimeRouterSolicitStart) { - UpdateRouterAdvMessage(/* aRouterAdvMessage */ nullptr); + UpdateRouterAdvertHeader(/* aRouterAdvertMessage */ nullptr); } mRouterSolicitCount = 0; // Re-evaluate our routing policy and send Router Advertisement if necessary. StartRoutingPolicyEvaluationDelay(/* aDelayJitter */ 0); - - // Reset prefix stale timer because `mDiscoveredPrefixes` may change. - ResetDiscoveredPrefixStaleTimer(); } } @@ -1078,244 +998,92 @@ void RoutingManager::HandleDiscoveredPrefixStaleTimer(void) StartRouterSolicitationDelay(); } -void RoutingManager::HandleDiscoveredPrefixInvalidTimer(Timer &aTimer) -{ - aTimer.Get().HandleDiscoveredPrefixInvalidTimer(); -} - -void RoutingManager::HandleDiscoveredPrefixInvalidTimer(void) -{ - InvalidateDiscoveredPrefixes(); -} - void RoutingManager::HandleRoutingPolicyTimer(Timer &aTimer) { aTimer.Get().EvaluateRoutingPolicy(); } -void RoutingManager::HandleRouterSolicit(const Ip6::Address &aSrcAddress, - const uint8_t * aBuffer, - uint16_t aBufferLength) +void RoutingManager::HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress) { + OT_UNUSED_VARIABLE(aPacket); OT_UNUSED_VARIABLE(aSrcAddress); - OT_UNUSED_VARIABLE(aBuffer); - OT_UNUSED_VARIABLE(aBufferLength); - LogInfo("Received Router Solicitation from %s on interface %u", aSrcAddress.ToString().AsCString(), mInfraIfIndex); - -#if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE - if (!mVicariousRouterSolicitTimer.IsRunning()) - { - mTimeVicariousRouterSolicitStart = TimerMilli::GetNow(); - mVicariousRouterSolicitTimer.Start(Time::SecToMsec(kVicariousSolicitationTime)); - } -#endif + LogInfo("Received Router Solicitation from %s on %s", aSrcAddress.ToString().AsCString(), + mInfraIf.ToString().AsCString()); // Schedule routing policy evaluation with random jitter to respond with Router Advertisement. StartRoutingPolicyEvaluationJitter(kRaReplyJitter); } -uint32_t RoutingManager::ExternalPrefix::GetPrefixExpireDelay(uint32_t aValidLifetime) +void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress) { - uint32_t delay; + Ip6::Nd::RouterAdvertMessage routerAdvMessage(aPacket); - if (aValidLifetime * static_cast(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_ASSERT(mIsRunning); - OT_UNUSED_VARIABLE(aSrcAddress); - using RouterAdv::Option; - using RouterAdv::PrefixInfoOption; - using RouterAdv::RouteInfoOption; - using RouterAdv::RouterAdvMessage; + VerifyOrExit(routerAdvMessage.IsValid()); - bool needReevaluate = false; - const uint8_t * optionsBegin; - uint16_t optionsLength; - const Option * option; - const RouterAdvMessage *routerAdvMessage; + LogInfo("Received Router Advertisement from %s on %s", aSrcAddress.ToString().AsCString(), + mInfraIf.ToString().AsCString()); + DumpDebg("[BR-CERT] direction=recv | type=RA |", aPacket.GetBytes(), aPacket.GetLength()); - VerifyOrExit(aBufferLength >= sizeof(RouterAdvMessage)); - - LogInfo("Received Router Advertisement from %s on interface %u", aSrcAddress.ToString().AsCString(), mInfraIfIndex); - DumpDebg("[BR-CERT] direction=recv | type=RA |", aBuffer, aBufferLength); - - routerAdvMessage = reinterpret_cast(aBuffer); - optionsBegin = aBuffer + sizeof(RouterAdvMessage); - optionsLength = aBufferLength - sizeof(RouterAdvMessage); - - option = nullptr; - while ((option = Option::GetNextOption(option, optionsBegin, optionsLength)) != nullptr) - { - switch (option->GetType()) - { - case Option::Type::kPrefixInfo: - { - const PrefixInfoOption *pio = static_cast(option); - - if (pio->IsValid()) - { - needReevaluate |= UpdateDiscoveredOnLinkPrefix(*pio); - } - } - break; - - case Option::Type::kRouteInfo: - { - const RouteInfoOption *rio = static_cast(option); - - if (rio->IsValid()) - { - UpdateDiscoveredOmrPrefix(*rio); - } - } - break; - - default: - break; - } - } + mDiscoveredPrefixTable.ProcessRouterAdvertMessage(routerAdvMessage, aSrcAddress); // Remember the header and parameters of RA messages which are // initiated from the infra interface. - if (otPlatInfraIfHasAddress(mInfraIfIndex, &aSrcAddress)) + if (mInfraIf.HasAddress(aSrcAddress)) { - needReevaluate |= UpdateRouterAdvMessage(routerAdvMessage); - } - - if (needReevaluate) - { - StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter); + UpdateRouterAdvertHeader(&routerAdvMessage); } exit: return; } -// Adds or deprecates a discovered on-link prefix (new external routes may be added -// to the Thread network). Returns a boolean which indicates whether we need to do -// routing policy evaluation. -bool RoutingManager::UpdateDiscoveredOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio) +bool RoutingManager::ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix) { - Ip6::Prefix prefix = aPio.GetPrefix(); - bool needReevaluate = false; - ExternalPrefix onLinkPrefix; - ExternalPrefix *existingPrefix = nullptr; + // Indicate whether to process or skip a given prefix + // from a PIO (from received RA message). + + bool shouldProcess = false; + + VerifyOrExit(mIsRunning); if (!IsValidOnLinkPrefix(aPio)) { - LogInfo("Ignore invalid on-link prefix in PIO: %s", prefix.ToString().AsCString()); + LogInfo("Ignore invalid on-link prefix in PIO: %s", aPrefix.ToString().AsCString()); ExitNow(); } - VerifyOrExit(!mIsAdvertisingLocalOnLinkPrefix || prefix != mLocalOnLinkPrefix); - - LogInfo("Discovered on-link prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(), - aPio.GetValidLifetime(), mInfraIfIndex); - - onLinkPrefix.mIsOnLinkPrefix = true; - onLinkPrefix.mPrefix = prefix; - onLinkPrefix.mValidLifetime = aPio.GetValidLifetime(); - onLinkPrefix.mPreferredLifetime = aPio.GetPreferredLifetime(); - onLinkPrefix.mTimeLastUpdate = TimerMilli::GetNow(); - - for (ExternalPrefix &externalPrefix : mDiscoveredPrefixes) + if (mIsAdvertisingLocalOnLinkPrefix) { - if (externalPrefix == onLinkPrefix) - { - existingPrefix = &externalPrefix; - } + VerifyOrExit(aPrefix != mLocalOnLinkPrefix); } - if (existingPrefix == nullptr) - { - if (onLinkPrefix.mValidLifetime == 0) - { - ExitNow(); - } - - if (!mDiscoveredPrefixes.IsFull()) - { - SuccessOrExit(AddExternalRoute(prefix, NetworkData::kRoutePreferenceMedium)); - existingPrefix = mDiscoveredPrefixes.PushBack(); - *existingPrefix = onLinkPrefix; - needReevaluate = true; - } - else - { - LogWarn("Discovered too many prefixes, ignore new on-link prefix %s", prefix.ToString().AsCString()); - ExitNow(); - } - } - else - { - constexpr uint32_t kTwoHoursInSeconds = 2 * 3600; - - // Per RFC 4862 section 5.5.3.e: - // 1. If the received Valid Lifetime is greater than 2 hours or - // greater than RemainingLifetime, set the valid lifetime of the - // corresponding address to the advertised Valid Lifetime. - // 2. If RemainingLifetime is less than or equal to 2 hours, ignore - // the Prefix Information option with regards to the valid - // lifetime, unless ... - // 3. Otherwise, reset the valid lifetime of the corresponding - // address to 2 hours. - - if (onLinkPrefix.mValidLifetime > kTwoHoursInSeconds || - onLinkPrefix.GetExpireTime() > existingPrefix->GetExpireTime()) - { - existingPrefix->mValidLifetime = onLinkPrefix.mValidLifetime; - } - else if (existingPrefix->GetExpireTime() > TimerMilli::GetNow() + TimeMilli::SecToMsec(kTwoHoursInSeconds)) - { - existingPrefix->mValidLifetime = kTwoHoursInSeconds; - } - - // The on-link prefix routing policy may be affected when a - // discovered on-link prefix becomes deprecated or preferred. - needReevaluate = (onLinkPrefix.IsDeprecated() != existingPrefix->IsDeprecated()); - - existingPrefix->mPreferredLifetime = onLinkPrefix.mPreferredLifetime; - existingPrefix->mTimeLastUpdate = onLinkPrefix.mTimeLastUpdate; - } - - mDiscoveredPrefixInvalidTimer.FireAtIfEarlier(existingPrefix->GetExpireTime()); - ResetDiscoveredPrefixStaleTimer(); + shouldProcess = true; exit: - return needReevaluate; + return shouldProcess; } -// Adds or removes a discovered OMR prefix (external route will be added to or removed -// from the Thread network). -void RoutingManager::UpdateDiscoveredOmrPrefix(const RouterAdv::RouteInfoOption &aRio) +bool RoutingManager::ShouldProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, const Ip6::Prefix &aPrefix) { - Ip6::Prefix prefix = aRio.GetPrefix(); - ExternalPrefix omrPrefix; - ExternalPrefix *existingPrefix = nullptr; + // Indicate whether to process or skip a given prefix + // from a RIO (from received RA message). - if (!IsValidOmrPrefix(prefix)) + OT_UNUSED_VARIABLE(aRio); + + bool shouldProcess = false; + + VerifyOrExit(mIsRunning); + + if (!IsValidOmrPrefix(aPrefix)) { - LogInfo("Ignore invalid OMR prefix in RIO: %s", prefix.ToString().AsCString()); + LogInfo("Ignore RIO prefix %s since not a valid OMR prefix", aPrefix.ToString().AsCString()); ExitNow(); } - // Ignore own OMR prefix. - VerifyOrExit(mLocalOmrPrefix != prefix); + VerifyOrExit(mLocalOmrPrefix != aPrefix); // Ignore OMR prefixes advertised by ourselves or in current Thread Network Data. // The `mAdvertisedOmrPrefixes` and the OMR prefix set in Network Data should eventually @@ -1327,116 +1095,39 @@ void RoutingManager::UpdateDiscoveredOmrPrefix(const RouterAdv::RouteInfoOption // messages are usually faster than Thread Network Data propagation). // They are the reasons why we need both the checks. - VerifyOrExit(!mAdvertisedOmrPrefixes.Contains(prefix)); - VerifyOrExit(!NetworkDataContainsOmrPrefix(prefix)); + VerifyOrExit(!mAdvertisedOmrPrefixes.ContainsMatching(aPrefix)); + VerifyOrExit(!Get().NetworkDataContainsOmrPrefix(aPrefix)); - LogInfo("Discovered OMR prefix (%s, %u seconds) from interface %u", prefix.ToString().AsCString(), - aRio.GetRouteLifetime(), mInfraIfIndex); + shouldProcess = true; - if (aRio.GetRouteLifetime() == 0) - { - InvalidateDiscoveredPrefixes(&prefix, /* aIsOnLinkPrefix */ false); - ExitNow(); - } +exit: + return shouldProcess; +} - omrPrefix.mIsOnLinkPrefix = false; - omrPrefix.mPrefix = prefix; - omrPrefix.mValidLifetime = aRio.GetRouteLifetime(); - omrPrefix.mRoutePreference = aRio.GetPreference(); - omrPrefix.mTimeLastUpdate = TimerMilli::GetNow(); +void RoutingManager::HandleDiscoveredPrefixTableChanged(void) +{ + // This is a callback from `mDiscoveredPrefixTable` indicating that + // there has been a change in the table. If the favored on-link + // prefix has changed, we trigger a re-evaluation of the routing + // policy. - for (ExternalPrefix &externalPrefix : mDiscoveredPrefixes) - { - if (externalPrefix == omrPrefix) - { - existingPrefix = &externalPrefix; - } - } + Ip6::Prefix newFavoredPrefix; - if (existingPrefix == nullptr) - { - if (omrPrefix.mValidLifetime == 0) - { - ExitNow(); - } + VerifyOrExit(mIsRunning); - if (!mDiscoveredPrefixes.IsFull()) - { - SuccessOrExit(AddExternalRoute(prefix, omrPrefix.mRoutePreference)); - existingPrefix = mDiscoveredPrefixes.PushBack(); - } - else - { - LogWarn("Discovered too many prefixes, ignore new prefix %s", prefix.ToString().AsCString()); - ExitNow(); - } - } - - *existingPrefix = omrPrefix; - - mDiscoveredPrefixInvalidTimer.FireAtIfEarlier(existingPrefix->GetExpireTime()); ResetDiscoveredPrefixStaleTimer(); + mDiscoveredPrefixTable.FindFavoredOnLinkPrefix(newFavoredPrefix); + + if (newFavoredPrefix != mFavoredDiscoveredOnLinkPrefix) + { + StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter); + } + exit: return; } -void RoutingManager::InvalidateDiscoveredPrefixes(const Ip6::Prefix *aPrefix, bool aIsOnLinkPrefix) -{ - TimeMilli now = TimerMilli::GetNow(); - uint8_t remainingOnLinkPrefixNum = 0; - ExternalPrefixArray remainingPrefixes; - - mDiscoveredPrefixInvalidTimer.Stop(); - - for (const ExternalPrefix &prefix : mDiscoveredPrefixes) - { - if ( - // Invalidate specified prefix - (aPrefix != nullptr && prefix.mPrefix == *aPrefix && prefix.mIsOnLinkPrefix == aIsOnLinkPrefix) || - // Invalidate expired prefix - (prefix.GetExpireTime() <= now) || - // Invalidate Local OMR prefixes - (!prefix.mIsOnLinkPrefix && - (mAdvertisedOmrPrefixes.Contains(prefix.mPrefix) || NetworkDataContainsOmrPrefix(prefix.mPrefix)))) - { - RemoveExternalRoute(prefix.mPrefix); - } - else - { - mDiscoveredPrefixInvalidTimer.FireAtIfEarlier(prefix.GetExpireTime()); - - IgnoreError(remainingPrefixes.PushBack(prefix)); - - if (prefix.mIsOnLinkPrefix) - { - ++remainingOnLinkPrefixNum; - } - } - } - - mDiscoveredPrefixes = remainingPrefixes; - - if (remainingOnLinkPrefixNum == 0 && !mIsAdvertisingLocalOnLinkPrefix) - { - // There are no valid on-link prefixes on infra link now, start Router Solicitation - // To discover more on-link prefixes or timeout to advertise my local on-link prefix. - StartRouterSolicitationDelay(); - } -} - -void RoutingManager::InvalidateAllDiscoveredPrefixes(void) -{ - for (ExternalPrefix &prefix : mDiscoveredPrefixes) - { - prefix.mValidLifetime = 0; - } - - InvalidateDiscoveredPrefixes(); - - OT_ASSERT(mDiscoveredPrefixes.IsEmpty()); -} - bool RoutingManager::NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const { NetworkData::Iterator iterator = NetworkData::kIteratorInit; @@ -1455,77 +1146,61 @@ bool RoutingManager::NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) co return contain; } -// Update the `mRouterAdvMessage` with given Router Advertisement message. -// Returns a boolean which indicates whether there are changes of `mRouterAdvMessage`. -bool RoutingManager::UpdateRouterAdvMessage(const RouterAdv::RouterAdvMessage *aRouterAdvMessage) +void RoutingManager::UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage) { - RouterAdv::RouterAdvMessage oldRouterAdvMessage; + // Updates the `mRouterAdvertHeader` from the given RA message. - oldRouterAdvMessage = mRouterAdvMessage; + Ip6::Nd::RouterAdvertMessage::Header oldHeader; + oldHeader = mRouterAdvertHeader; mTimeRouterAdvMessageLastUpdate = TimerMilli::GetNow(); - if (aRouterAdvMessage == nullptr || aRouterAdvMessage->GetRouterLifetime() == 0) + + if (aRouterAdvertMessage == nullptr || aRouterAdvertMessage->GetHeader().GetRouterLifetime() == 0) { - mRouterAdvMessage.SetToDefault(); + mRouterAdvertHeader.SetToDefault(); mLearntRouterAdvMessageFromHost = false; } else { - mRouterAdvMessage = *aRouterAdvMessage; + // The checksum is set to zero in `mRouterAdvertHeader` + // which indicates to platform that it needs to do the + // calculation and update it. + + mRouterAdvertHeader = aRouterAdvertMessage->GetHeader(); + mRouterAdvertHeader.SetChecksum(0); mLearntRouterAdvMessageFromHost = true; } ResetDiscoveredPrefixStaleTimer(); - return (mRouterAdvMessage != oldRouterAdvMessage); + if (mRouterAdvertHeader != oldHeader) + { + // If there was a change to the header, start timer to + // reevaluate routing policy and send RA message with new + // header. + + StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter); + } } void RoutingManager::ResetDiscoveredPrefixStaleTimer(void) { - TimeMilli now = TimerMilli::GetNow(); - TimeMilli nextStaleTime = now.GetDistantFuture(); - TimeMilli maxOnlinkPrefixStaleTime = now; - bool requireCheckStaleOnlinkPrefix = false; + TimeMilli now = TimerMilli::GetNow(); + TimeMilli nextStaleTime; OT_ASSERT(mIsRunning); - // The stale timer triggers sending RS to check the state of On-Link/OMR prefixes and host RA messages. - // The rules for calculating the next stale time: - // 1. If BR learns RA header from Host daemons, it should send RS when the RA header is stale. - // 2. If BR discovered any on-link prefix, it should send RS when all on-link prefixes are stale. - // 3. If BR discovered any OMR prefix, it should send RS when the first OMR prefix is stale. + // The stale timer triggers sending RS to check the state of + // discovered prefixes and host RA messages. + + nextStaleTime = mDiscoveredPrefixTable.CalculateNextStaleTime(now); // Check for stale Router Advertisement Message if learnt from Host. if (mLearntRouterAdvMessageFromHost) { - TimeMilli routerAdvMessageStaleTime = mTimeRouterAdvMessageLastUpdate + Time::SecToMsec(kRtrAdvStaleTime); + TimeMilli raStaleTime = OT_MAX(now, mTimeRouterAdvMessageLastUpdate + Time::SecToMsec(kRtrAdvStaleTime)); - nextStaleTime = OT_MIN(nextStaleTime, routerAdvMessageStaleTime); - } - - for (ExternalPrefix &externalPrefix : mDiscoveredPrefixes) - { - TimeMilli prefixStaleTime = externalPrefix.GetStaleTime(); - - if (externalPrefix.mIsOnLinkPrefix) - { - if (!externalPrefix.IsDeprecated()) - { - // Check for least recent stale On-Link Prefixes if BR is not advertising local On-Link Prefix. - maxOnlinkPrefixStaleTime = OT_MAX(maxOnlinkPrefixStaleTime, prefixStaleTime); - requireCheckStaleOnlinkPrefix = true; - } - } - else - { - // Check for most recent stale OMR Prefixes - nextStaleTime = OT_MIN(nextStaleTime, prefixStaleTime); - } - } - - if (requireCheckStaleOnlinkPrefix) - { - nextStaleTime = OT_MIN(nextStaleTime, maxOnlinkPrefixStaleTime); + nextStaleTime = OT_MIN(nextStaleTime, raStaleTime); } if (nextStaleTime == now.GetDistantFuture()) @@ -1534,6 +1209,7 @@ void RoutingManager::ResetDiscoveredPrefixStaleTimer(void) { LogDebg("Prefix stale timer stopped"); } + mDiscoveredPrefixStaleTimer.Stop(); } else @@ -1543,6 +1219,461 @@ void RoutingManager::ResetDiscoveredPrefixStaleTimer(void) } } +//--------------------------------------------------------------------------------------------------------------------- +// DiscoveredPrefixTable + +RoutingManager::DiscoveredPrefixTable::DiscoveredPrefixTable(Instance &aInstance) + : InstanceLocator(aInstance) + , mTimer(aInstance, HandleTimer) + , mSignalTask(aInstance, HandleSignalTask) +{ +} + +void RoutingManager::DiscoveredPrefixTable::ProcessRouterAdvertMessage(const Ip6::Nd::RouterAdvertMessage &aRaMessage, + const Ip6::Address & aSrcAddress) +{ + // Process a received RA message and update the prefix table. + + OT_UNUSED_VARIABLE(aSrcAddress); + + for (const Ip6::Nd::Option &option : aRaMessage) + { + switch (option.GetType()) + { + case Ip6::Nd::Option::kTypePrefixInfo: + ProcessPrefixInfoOption(static_cast(option)); + break; + + case Ip6::Nd::Option::kTypeRouteInfo: + ProcessRouteInfoOption(static_cast(option)); + break; + + default: + break; + } + } +} + +void RoutingManager::DiscoveredPrefixTable::ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio) +{ + Ip6::Prefix prefix; + Entry * entry; + + VerifyOrExit(aPio.IsValid()); + aPio.GetPrefix(prefix); + + VerifyOrExit(Get().ShouldProcessPrefixInfoOption(aPio, prefix)); + + LogInfo("Processing PIO (%s, %u seconds)", prefix.ToString().AsCString(), aPio.GetValidLifetime()); + + entry = mEntries.FindMatching(Entry::Matcher(prefix, Entry::kTypeOnLink)); + + if (entry == nullptr) + { + VerifyOrExit(aPio.GetValidLifetime() != 0); + + if (mEntries.IsFull()) + { + LogWarn("Discovered too many prefixes, ignore on-link prefix %s", prefix.ToString().AsCString()); + ExitNow(); + } + + SuccessOrExit(Get().PublishExternalRoute(prefix, NetworkData::kRoutePreferenceMedium)); + entry = mEntries.PushBack(); + entry->InitFrom(aPio); + } + else + { + Entry newEntry; + + newEntry.InitFrom(aPio); + entry->AdoptValidAndPreferredLiftimesFrom(newEntry); + } + + mTimer.FireAtIfEarlier(entry->GetExpireTime()); + SignalTableChanged(); + +exit: + return; +} + +void RoutingManager::DiscoveredPrefixTable::ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio) +{ + Ip6::Prefix prefix; + Entry * entry; + + VerifyOrExit(aRio.IsValid()); + aRio.GetPrefix(prefix); + + VerifyOrExit(Get().ShouldProcessRouteInfoOption(aRio, prefix)); + + LogInfo("Processing RIO (%s, %u seconds)", prefix.ToString().AsCString(), aRio.GetRouteLifetime()); + + entry = mEntries.FindMatching(Entry::Matcher(prefix, Entry::kTypeRoute)); + + if (aRio.GetRouteLifetime() == 0) + { + VerifyOrExit(entry != nullptr); + + Get().UnpublishExternalRoute(entry->GetPrefix()); + mEntries.Remove(*entry); + + ExitNow(); + } + + if (entry == nullptr) + { + if (mEntries.IsFull()) + { + LogWarn("Discovered too many prefixes, ignore new prefix %s", prefix.ToString().AsCString()); + ExitNow(); + } + + SuccessOrExit(Get().PublishExternalRoute(prefix, aRio.GetPreference())); + entry = mEntries.PushBack(); + } + + entry->InitFrom(aRio); + + mTimer.FireAtIfEarlier(entry->GetExpireTime()); + SignalTableChanged(); + +exit: + return; +} + +void RoutingManager::DiscoveredPrefixTable::FindFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const +{ + // Find the smallest preferred on-link prefix entry in the table + // and return it in `aPrefix`. If there is none, `aPrefix` is + // cleared (prefix length is set to zero). + + aPrefix.Clear(); + + for (const Entry &entry : mEntries) + { + if (!entry.IsOnLinkPrefix() || entry.IsDeprecated()) + { + continue; + } + + if ((aPrefix.GetLength() == 0) || (entry.GetPrefix() < aPrefix)) + { + aPrefix = entry.GetPrefix(); + } + } +} + +bool RoutingManager::DiscoveredPrefixTable::ContainsOnLinkPrefix(const Ip6::Prefix &aPrefix) const +{ + return mEntries.ContainsMatching(Entry::Matcher(aPrefix, Entry::kTypeOnLink)); +} + +bool RoutingManager::DiscoveredPrefixTable::ContainsRoutePrefix(const Ip6::Prefix &aPrefix) const +{ + return mEntries.ContainsMatching(Entry::Matcher(aPrefix, Entry::kTypeRoute)); +} + +void RoutingManager::DiscoveredPrefixTable::RemoveOnLinkPrefix(const Ip6::Prefix &aPrefix, NetDataMode aNetDataMode) +{ + RemovePrefix(aPrefix, Entry::kTypeOnLink, aNetDataMode); +} + +void RoutingManager::DiscoveredPrefixTable::RemoveRoutePrefix(const Ip6::Prefix &aPrefix, NetDataMode aNetDataMode) +{ + RemovePrefix(aPrefix, Entry::kTypeRoute, aNetDataMode); +} + +void RoutingManager::DiscoveredPrefixTable::RemovePrefix(const Ip6::Prefix &aPrefix, + Entry::Type aType, + NetDataMode aNetDataMode) +{ + // Remove a prefix of given type from the table if there is any. + // `aNetDataMode` specifies behavior when a match is found and + // removed. It indicates whether or not to unpublish it from + // Network Data. + + Entry *entry = mEntries.FindMatching(Entry::Matcher(aPrefix, aType)); + + VerifyOrExit(entry != nullptr); + + if (aNetDataMode == kUnpublishFromNetData) + { + Get().UnpublishExternalRoute(aPrefix); + } + + mEntries.Remove(*entry); + SignalTableChanged(); + +exit: + return; +} + +void RoutingManager::DiscoveredPrefixTable::RemoveAllEntries(void) +{ + // Remove all entries from the table and unpublish them + // from Network Data. + + Entry *entry; + + while ((entry = mEntries.PopBack()) != nullptr) + { + Get().UnpublishExternalRoute(entry->GetPrefix()); + SignalTableChanged(); + } + + mTimer.Stop(); +} + +void RoutingManager::DiscoveredPrefixTable::RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold) +{ + // Remove route prefix entries and deprecate on-link entries in + // the table that are old (not updated since `aTimeThreshold`). + + for (Entry &entry : mEntries) + { + if (entry.GetLastUpdateTime() <= aTimeThreshold) + { + if (entry.IsOnLinkPrefix()) + { + entry.ClearPreferredLifetime(); + } + else + { + entry.ClearValidLifetime(); + } + + SignalTableChanged(); + } + } + + RemoveExpiredEntries(); +} + +TimeMilli RoutingManager::DiscoveredPrefixTable::CalculateNextStaleTime(TimeMilli aNow) const +{ + TimeMilli onLinkStaleTime = aNow; + TimeMilli routeStaleTime = aNow.GetDistantFuture(); + bool foundOnLink = false; + + // For on-link prefixes, we consider stale time as when all on-link + // prefixes become stale (the latest stale time) but for route + // prefixes we consider the earliest stale time. + + for (const Entry &entry : mEntries) + { + TimeMilli entryStaleTime = OT_MAX(aNow, entry.GetStaleTime()); + + if (entry.IsOnLinkPrefix() && !entry.IsDeprecated()) + { + onLinkStaleTime = OT_MAX(onLinkStaleTime, entryStaleTime); + foundOnLink = true; + } + + if (!entry.IsOnLinkPrefix()) + { + routeStaleTime = OT_MIN(routeStaleTime, entryStaleTime); + } + } + + return foundOnLink ? OT_MIN(onLinkStaleTime, routeStaleTime) : routeStaleTime; +} + +void RoutingManager::DiscoveredPrefixTable::HandleTimer(Timer &aTimer) +{ + aTimer.Get().mDiscoveredPrefixTable.HandleTimer(); +} + +void RoutingManager::DiscoveredPrefixTable::HandleTimer(void) +{ + RemoveExpiredEntries(); +} + +void RoutingManager::DiscoveredPrefixTable::RemoveExpiredEntries(void) +{ + TimeMilli now = TimerMilli::GetNow(); + TimeMilli nextExpireTime = now.GetDistantFuture(); + + for (EntryArray::IndexType index = 0; index < mEntries.GetLength();) + { + Entry &entry = mEntries[index]; + + if (entry.GetExpireTime() <= now) + { + Get().UnpublishExternalRoute(entry.GetPrefix()); + + // Remove the prefix from the array (which replaces it with + // last entry in the array). So in this case, we do not + // increment the `index`. + + mEntries.Remove(entry); + SignalTableChanged(); + } + else + { + nextExpireTime = OT_MIN(nextExpireTime, entry.GetExpireTime()); + index++; + } + } + + if (nextExpireTime != now.GetDistantFuture()) + { + mTimer.FireAt(nextExpireTime); + } +} + +void RoutingManager::DiscoveredPrefixTable::SignalTableChanged(void) +{ + mSignalTask.Post(); +} + +void RoutingManager::DiscoveredPrefixTable::HandleSignalTask(Tasklet &aTasklet) +{ + aTasklet.Get().HandleDiscoveredPrefixTableChanged(); +} + +//--------------------------------------------------------------------------------------------------------------------- +// DiscoveredPrefixTable::Entry + +void RoutingManager::DiscoveredPrefixTable::Entry::InitFrom(const Ip6::Nd::PrefixInfoOption &aPio) +{ + Clear(); + aPio.GetPrefix(mPrefix); + mType = kTypeOnLink; + mValidLifetime = aPio.GetValidLifetime(); + mPreferredLifetime = aPio.GetPreferredLifetime(); + mLastUpdateTime = TimerMilli::GetNow(); +} + +void RoutingManager::DiscoveredPrefixTable::Entry::InitFrom(const Ip6::Nd::RouteInfoOption &aRio) +{ + Clear(); + aRio.GetPrefix(mPrefix); + mType = kTypeRoute; + mValidLifetime = aRio.GetRouteLifetime(); + mRoutePreference = aRio.GetPreference(); + mLastUpdateTime = TimerMilli::GetNow(); +} + +bool RoutingManager::DiscoveredPrefixTable::Entry::operator==(const Entry &aOther) const +{ + return (mType == aOther.mType) && (mPrefix == aOther.mPrefix); +} + +bool RoutingManager::DiscoveredPrefixTable::Entry::Matches(const Matcher &aMatcher) const +{ + return (mType == aMatcher.mType) && (mPrefix == aMatcher.mPrefix); +} + +TimeMilli RoutingManager::DiscoveredPrefixTable::Entry::GetExpireTime(void) const +{ + return mLastUpdateTime + CalculateExpireDelay(mValidLifetime); +} + +TimeMilli RoutingManager::DiscoveredPrefixTable::Entry::GetStaleTime(void) const +{ + uint32_t delay = OT_MIN(kRtrAdvStaleTime, IsOnLinkPrefix() ? mPreferredLifetime : mValidLifetime); + + return mLastUpdateTime + TimeMilli::SecToMsec(delay); +} + +bool RoutingManager::DiscoveredPrefixTable::Entry::IsDeprecated(void) const +{ + OT_ASSERT(IsOnLinkPrefix()); + + return mLastUpdateTime + TimeMilli::SecToMsec(mPreferredLifetime) <= TimerMilli::GetNow(); +} + +void RoutingManager::DiscoveredPrefixTable::Entry::AdoptValidAndPreferredLiftimesFrom(const Entry &aEntry) +{ + constexpr uint32_t kTwoHoursInSeconds = 2 * 3600; + + // Per RFC 4862 section 5.5.3.e: + // + // 1. If the received Valid Lifetime is greater than 2 hours or + // greater than RemainingLifetime, set the valid lifetime of the + // corresponding address to the advertised Valid Lifetime. + // 2. If RemainingLifetime is less than or equal to 2 hours, ignore + // the Prefix Information option with regards to the valid + // lifetime, unless ... + // 3. Otherwise, reset the valid lifetime of the corresponding + // address to 2 hours. + + if (aEntry.mValidLifetime > kTwoHoursInSeconds || aEntry.GetExpireTime() > GetExpireTime()) + { + mValidLifetime = aEntry.mValidLifetime; + } + else if (GetExpireTime() > TimerMilli::GetNow() + TimeMilli::SecToMsec(kTwoHoursInSeconds)) + { + mValidLifetime = kTwoHoursInSeconds; + } + + mPreferredLifetime = aEntry.GetPreferredLifetime(); + mLastUpdateTime = aEntry.GetLastUpdateTime(); +} + +uint32_t RoutingManager::DiscoveredPrefixTable::Entry::CalculateExpireDelay(uint32_t aValidLifetime) +{ + uint32_t delay; + + if (aValidLifetime * static_cast(1000) > Timer::kMaxDelay) + { + delay = Timer::kMaxDelay; + } + else + { + delay = aValidLifetime * 1000; + } + + return delay; +} + +//--------------------------------------------------------------------------------------------------------------------- +// OmrPrefix + +void RoutingManager::OmrPrefix::Init(const Ip6::Prefix &aPrefix, RoutePreference aPreference) +{ + mPrefix = aPrefix; + mPreference = aPreference; +} + +void RoutingManager::OmrPrefix::InitFrom(NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig) +{ + Init(aOnMeshPrefixConfig.GetPrefix(), aOnMeshPrefixConfig.GetPreference()); +} + +bool RoutingManager::OmrPrefix::IsFavoredOver(const OmrPrefix &aOther) const +{ + // This method determines whether this OMR prefix is favored + // over `aOther` prefix. A prefix with higher preference is + // favored. If the preference is the same, then the smaller + // prefix (in the sense defined by `Ip6::Prefix`) is favored. + + return (mPreference > aOther.mPreference) || ((mPreference == aOther.mPreference) && (mPrefix < aOther.mPrefix)); +} + +RoutingManager::OmrPrefix::InfoString RoutingManager::OmrPrefix::ToString(void) const +{ + InfoString string; + + string.Append("%s (prf:", mPrefix.ToString().AsCString()); + + switch (mPreference) + { + case NetworkData::kRoutePreferenceHigh: + string.Append("high)"); + break; + case NetworkData::kRoutePreferenceMedium: + string.Append("med)"); + break; + case NetworkData::kRoutePreferenceLow: + string.Append("low)"); + break; + } + + return string; +} + } // namespace BorderRouter } // namespace ot diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index 0023e9e65..870c8d4da 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -48,15 +48,16 @@ #endif #include -#include -#include "border_router/router_advertisement.hpp" +#include "border_router/infra_if.hpp" #include "common/array.hpp" #include "common/error.hpp" #include "common/locator.hpp" #include "common/notifier.hpp" +#include "common/string.hpp" #include "common/timer.hpp" #include "net/ip6.hpp" +#include "net/nd6.hpp" #include "thread/network_data.hpp" namespace ot { @@ -73,6 +74,7 @@ namespace BorderRouter { class RoutingManager : public InstanceLocator { friend class ot::Notifier; + friend class ot::Instance; public: /** @@ -155,35 +157,21 @@ public: #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE /** - * This method receives an ICMPv6 message on the infrastructure interface. + * This method processes a received ICMPv6 message from the infrastructure interface. * * Malformed or undesired messages are dropped silently. * - * @param[in] aInfraIfIndex The infrastructure interface index. + * @param[in] aPacket The received ICMPv6 packet. * @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); + void HandleReceived(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); /** * This method handles infrastructure interface state changes. * - * @param[in] aInfraIfIndex The index of the infrastructure interface. - * @param[in] aIsRunning A boolean that indicates whether the infrastructure - * interface is running. - * - * @retval kErrorNone Successfully updated the infra interface status. - * @retval kErrorInvalidState The Routing Manager is not initialized. - * @retval kErrorInvalidArgs The @p aInfraIfIndex doesn't match the infra interface - * the Routing Manager is initialized with. - * */ - Error HandleInfraIfStateChanged(uint32_t aInfraIfIndex, bool aIsRunning); + void HandleInfraIfStateChanged(void) { EvaluateState(); } /** * This method checks if the on-mesh prefix configuration is a valid OMR prefix. @@ -250,76 +238,162 @@ private: // The value is chosen in range of [`kMaxRtrAdvInterval` upper bound (1800s), `kDefaultOnLinkPrefixLifetime`]. static constexpr uint32_t kRtrAdvStaleTime = 1800; - // The VICARIOUS_SOLICIT_TIME in seconds. The Routing Manager will consider - // the discovered prefixes invalid if they are not refreshed after receiving - // a Router Solicitation message. - // The value is equal to Router Solicitation timeout. - static constexpr uint32_t kVicariousSolicitationTime = - kRtrSolicitationInterval * (kMaxRtrSolicitations - 1) + kMaxRtrSolicitationDelay; - 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"); static_assert(kRtrAdvStaleTime >= 1800 && kRtrAdvStaleTime <= kDefaultOnLinkPrefixLifetime, "invalid RA STALE time"); - // This struct represents an external prefix which is - // discovered on the infrastructure interface. - struct ExternalPrefix : public Clearable, public Unequatable + class DiscoveredPrefixTable : public InstanceLocator { - Ip6::Prefix mPrefix; - uint32_t mValidLifetime; + // This class maintains the discovered on-link and route prefixes + // from the received RA messages by processing PIO and RIO options + // from the message. It takes care of processing the RA message but + // delegates the decision whether to include or exclude a prefix to + // `RoutingManager` by calling its `ShouldProcessPrefixInfoOption()` + // and `ShouldProcessRouteInfoOption()` methods. + // + // It manages the lifetime of the discovered entries and publishes + // and unpublishes the prefixes in the Network Data (as external + // route) as they are added or removed. + // + // When there is any change in the table (an entry is added, removed, + // or modified), it signals the change to `RoutingManager` by calling + // `HandleDiscoveredPrefixTableChanged()` callback. A `Tasklet` is + // used for signalling which ensures that if there are multiple + // changes within the same flow of execution, the callback is + // invoked after all the changes are processed. - union + public: + enum NetDataMode : uint8_t // Used in `Remove{}` methods { - // Preferred Lifetime of on-link prefix, available - // only when `mIsOnLinkPrefix` is TRUE. - uint32_t mPreferredLifetime; - - // The preference of this route, available - // only when `mIsOnLinkPrefix` is FALSE. - RoutePreference mRoutePreference; + kUnpublishFromNetData, // Unpublish the entry from Network Data if previously published. + kKeepInNetData, // Keep entry in Network Data if previously published. }; - TimeMilli mTimeLastUpdate; - bool mIsOnLinkPrefix; - bool operator==(const ExternalPrefix &aPrefix) const + explicit DiscoveredPrefixTable(Instance &aInstance); + + void ProcessRouterAdvertMessage(const Ip6::Nd::RouterAdvertMessage &aRaMessage, + const Ip6::Address & aSrcAddress); + + void FindFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const; + bool ContainsOnLinkPrefix(const Ip6::Prefix &aPrefix) const; + void RemoveOnLinkPrefix(const Ip6::Prefix &aPrefix, NetDataMode aNetDataMode); + + bool ContainsRoutePrefix(const Ip6::Prefix &aPrefix) const; + void RemoveRoutePrefix(const Ip6::Prefix &aPrefix, NetDataMode aNetDataMode); + + void RemoveAllEntries(void); + void RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold); + + TimeMilli CalculateNextStaleTime(TimeMilli aNow) const; + + private: + static constexpr uint8_t kMaxEntries = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES; + + struct Entry : private Clearable, public Unequatable { - return mPrefix == aPrefix.mPrefix && mIsOnLinkPrefix == aPrefix.mIsOnLinkPrefix; - } + public: + enum Type : uint8_t + { + kTypeOnLink, + kTypeRoute, + }; - bool IsDeprecated(void) const - { - OT_ASSERT(mIsOnLinkPrefix); + struct Matcher + { + Matcher(const Ip6::Prefix &aPrefix, Type aType) + : mPrefix(aPrefix) + , mType(aType) + { + } - return mTimeLastUpdate + TimeMilli::SecToMsec(mPreferredLifetime) <= TimerMilli::GetNow(); - } + const Ip6::Prefix &mPrefix; + bool mType; + }; - TimeMilli GetExpireTime(void) const { return mTimeLastUpdate + GetPrefixExpireDelay(mValidLifetime); } - TimeMilli GetStaleTime(void) const - { - uint32_t delay = OT_MIN(kRtrAdvStaleTime, mIsOnLinkPrefix ? mPreferredLifetime : mValidLifetime); + void InitFrom(const Ip6::Nd::PrefixInfoOption &aPio); + void InitFrom(const Ip6::Nd::RouteInfoOption &aRio); + bool IsOnLinkPrefix(void) const { return (mType == kTypeOnLink); } + const Ip6::Prefix &GetPrefix(void) const { return mPrefix; } + const TimeMilli & GetLastUpdateTime(void) const { return mLastUpdateTime; } + uint32_t GetValidLifetime(void) const { return mValidLifetime; } + void ClearValidLifetime(void) { mValidLifetime = 0; } + TimeMilli GetExpireTime(void) const; + TimeMilli GetStaleTime(void) const; + bool operator==(const Entry &aOther) const; + bool Matches(const Matcher &aMatcher) const; - return mTimeLastUpdate + TimeMilli::SecToMsec(delay); - } + // Methods to use when `IsOnLinkPrefix()` + uint32_t GetPreferredLifetime(void) const { return mPreferredLifetime; } + void ClearPreferredLifetime(void) { mPreferredLifetime = 0; } + bool IsDeprecated(void) const; + void AdoptValidAndPreferredLiftimesFrom(const Entry &aEntry); - static uint32_t GetPrefixExpireDelay(uint32_t aValidLifetime); + // Method to use when `!IsOnlinkPrefix()` + RoutePreference GetRoutePreference(void) const { return mRoutePreference; } + + private: + static uint32_t CalculateExpireDelay(uint32_t aValidLifetime); + + Ip6::Prefix mPrefix; + TimeMilli mLastUpdateTime; + uint32_t mValidLifetime; + uint32_t mPreferredLifetime; // Applicable when prefix is on-link. + RoutePreference mRoutePreference; // Applicable when prefix is not on-link + Type mType; + }; + + typedef Array EntryArray; + + void RemovePrefix(const Ip6::Prefix &aPrefix, Entry::Type aType, NetDataMode aNetDataMode); + + void ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio); + void ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio); + static void HandleTimer(Timer &aTimer); + void HandleTimer(void); + void RemoveExpiredEntries(void); + void SignalTableChanged(void); + static void HandleSignalTask(Tasklet &aTasklet); + + EntryArray mEntries; + TimerMilli mTimer; + Tasklet mSignalTask; }; - typedef Array OmrPrefixArray; - typedef Array ExternalPrefixArray; + class OmrPrefix // An OMR Prefix + { + public: + static constexpr uint16_t kInfoStringSize = 60; + typedef String InfoString; + + void Init(const Ip6::Prefix &aPrefix, RoutePreference aPreference); + void InitFrom(NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig); + const Ip6::Prefix &GetPrefix(void) const { return mPrefix; } + RoutePreference GetPreference(void) const { return mPreference; } + void SetPreference(RoutePreference aPreference) { mPreference = aPreference; } + bool Matches(const Ip6::Prefix &aPrefix) const { return mPrefix == aPrefix; } + bool IsFavoredOver(const OmrPrefix &aOther) const; + InfoString ToString(void) const; + + private: + Ip6::Prefix mPrefix; + RoutePreference mPreference; + }; + + typedef Array OmrPrefixArray; void EvaluateState(void); void Start(void); void Stop(void); void HandleNotifierEvents(Events aEvents); - bool IsInitialized(void) const { return mInfraIfIndex != 0; } + bool IsInitialized(void) const { return mInfraIf.IsInitialized(); } bool IsEnabled(void) const { return mIsEnabled; } Error LoadOrGenerateRandomBrUlaPrefix(void); void GenerateOmrPrefix(void); - Error LoadOrGenerateRandomOnLinkPrefix(void); + void GenerateOnLinkPrefix(void); - const Ip6::Prefix *EvaluateOnLinkPrefix(void); + void EvaluateOnLinkPrefix(void); #if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE void GenerateNat64Prefix(void); @@ -333,17 +407,13 @@ private: Error PublishLocalOmrPrefix(void); void UnpublishLocalOmrPrefix(void); bool IsOmrPrefixAddedToLocalNetworkData(void) const; - Error AddExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64 = false); - void RemoveExternalRoute(const Ip6::Prefix &aPrefix); + Error PublishExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64 = false); + void UnpublishExternalRoute(const Ip6::Prefix &aPrefix); void StartRouterSolicitationDelay(void); Error SendRouterSolicitation(void); - void SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefixes, const Ip6::Prefix *aNewOnLinkPrefix); + void SendRouterAdvertisement(const OmrPrefixArray &aNewOmrPrefixes); bool IsRouterSolicitationInProgress(void) const; -#if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE - static void HandleVicariousRouterSolicitTimer(Timer &aTimer); - void HandleVicariousRouterSolicitTimer(void); -#endif static void HandleRouterSolicitTimer(Timer &aTimer); void HandleRouterSolicitTimer(void); static void HandleDiscoveredPrefixInvalidTimer(Timer &aTimer); @@ -355,18 +425,17 @@ private: static void HandleOnLinkPrefixDeprecateTimer(Timer &aTimer); void DeprecateOnLinkPrefix(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); - bool UpdateDiscoveredOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio); - void UpdateDiscoveredOmrPrefix(const RouterAdv::RouteInfoOption &aRio); - void InvalidateDiscoveredPrefixes(const Ip6::Prefix *aPrefix = nullptr, bool aIsOnLinkPrefix = true); - void InvalidateAllDiscoveredPrefixes(void); + void HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); + void HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); + bool ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix); + bool ShouldProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, const Ip6::Prefix &aPrefix); + void HandleDiscoveredPrefixTableChanged(void); bool NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const; - bool UpdateRouterAdvMessage(const RouterAdv::RouterAdvMessage *aRouterAdvMessage); + void UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage); void ResetDiscoveredPrefixStaleTimer(void); static bool IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix); - static bool IsValidOnLinkPrefix(const RouterAdv::PrefixInfoOption &aPio); + static bool IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption &aPio); static bool IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix); // Indicates whether the Routing Manager is running (started). @@ -376,13 +445,7 @@ private: // Manager will be stopped if we are disabled. bool mIsEnabled; - // Indicates whether the infra interface is running. The Routing - // Manager will be stopped when the Infra interface is not running. - bool mInfraIfIsRunning; - - // The index of the infra interface on which Router Advertisement - // messages will be sent. - uint32_t mInfraIfIndex; + InfraIf mInfraIf; // The /48 BR ULA prefix loaded from local persistent storage or // randomly generated if none is found in persistent storage. @@ -399,6 +462,10 @@ private: // advertised on infra link. OmrPrefixArray mAdvertisedOmrPrefixes; + // The currently favored (smallest) discovered on-link prefix. + // Prefix length of zero indicates there is none. + Ip6::Prefix mFavoredDiscoveredOnLinkPrefix; + // The on-link prefix loaded from local persistent storage or // randomly generated if non is found in persistent storage. Ip6::Prefix mLocalOnLinkPrefix; @@ -416,29 +483,20 @@ private: // True if the local NAT64 prefix is advertised in Thread network. bool mIsAdvertisingLocalNat64Prefix; - // The array of prefixes discovered on the infra link. Those - // prefixes consist of on-link prefix(es) and OMR prefixes - // advertised by BRs in another Thread Network which is connected to - // the same infra link. - ExternalPrefixArray mDiscoveredPrefixes; + DiscoveredPrefixTable mDiscoveredPrefixTable; // The RA header and parameters for the infra interface. // This value is initialized with `RouterAdvMessage::SetToDefault` // and updated with RA messages initiated from infra interface. - RouterAdv::RouterAdvMessage mRouterAdvMessage; - TimeMilli mTimeRouterAdvMessageLastUpdate; - bool mLearntRouterAdvMessageFromHost; + Ip6::Nd::RouterAdvertMessage::Header mRouterAdvertHeader; + TimeMilli mTimeRouterAdvMessageLastUpdate; + bool mLearntRouterAdvMessageFromHost; - TimerMilli mDiscoveredPrefixInvalidTimer; TimerMilli mDiscoveredPrefixStaleTimer; uint32_t mRouterAdvertisementCount; TimeMilli mLastRouterAdvertisementSendTime; -#if OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE - TimerMilli mVicariousRouterSolicitTimer; - TimeMilli mTimeVicariousRouterSolicitStart; -#endif TimerMilli mRouterSolicitTimer; TimeMilli mTimeRouterSolicitStart; uint8_t mRouterSolicitCount; diff --git a/src/core/common/array.hpp b/src/core/common/array.hpp index ded006f93..9683dd619 100644 --- a/src/core/common/array.hpp +++ b/src/core/common/array.hpp @@ -310,6 +310,27 @@ public: */ IndexType IndexOf(const Type &aElement) const { return static_cast(&aElement - &mElements[0]); } + /** + * This method removes an element from the array. + * + * The @p aElement MUST be from the array, otherwise the behavior of this method is undefined. + * + * To remove @p aElement, it is replaced by the last element in array, so the order of items in the array can + * change after a call to this method. + * + * The method uses assignment `=` operator on `Type` to copy the last element in place of @p aElement. + * + */ + void Remove(Type &aElement) + { + Type *lastElement = PopBack(); + + if (lastElement != &aElement) + { + aElement = *lastElement; + } + } + /** * This method finds the first match of a given entry in the array. * @@ -430,6 +451,32 @@ public: return FindMatching(aIndicator) != nullptr; } + /** + * This template method removes the first element in the array matching a given indicator. + * + * This method behaves similar to `Remove()`, i.e., the matched element (if found) is replaced with the last element + * in the array (using `=` operator on `Type`). So the order of items in the array can change after a call to this + * method. + * + * The template type `Indicator` specifies the type of @p aIndicator object which is used to match against elements + * in the array. To check that an element matches the given indicator, the `Matches()` method is invoked on each + * `Type` element in the array. The `Matches()` method should be provided by `Type` class accordingly: + * + * bool Type::Matches(const Indicator &aIndicator) const + * + * @param[in] aIndicator An indicator to match with elements in the array. + * + */ + template void RemoveMatching(const Indicator &aIndicator) + { + Type *entry = FindMatching(aIndicator); + + if (entry != nullptr) + { + Remove(*entry); + } + } + /** * This method overloads assignment `=` operator to copy elements from another array into the array. * diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index caba4dd37..ff0e37243 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -972,6 +972,11 @@ template <> inline BorderRouter::RoutingManager &Instance::Get(void) { return mRoutingManager; } + +template <> inline BorderRouter::InfraIf &Instance::Get(void) +{ + return mRoutingManager.mInfraIf; +} #endif #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE diff --git a/src/core/common/settings.cpp b/src/core/common/settings.cpp index c69b32261..67f76e205 100644 --- a/src/core/common/settings.cpp +++ b/src/core/common/settings.cpp @@ -146,15 +146,15 @@ const char *SettingsBase::KeyToString(Key aKey) "NetworkInfo", // (3) kKeyNetworkInfo "ParentInfo", // (4) kKeyParentInfo "ChildInfo", // (5) kKeyChildInfo - "", // (6) kKeyReserved + "", // (6) Removed (previously auto-start). "SlaacIidSecretKey", // (7) kKeySlaacIidSecretKey "DadInfo", // (8) kKeyDadInfo - "LegacyOmrPrefix", // (9) kKeyLegacyOmrPrefix - "OnLinkPrefix", // (10) kKeyOnLinkPrefix + "", // (9) Removed (previously OMR prefix). + "", // (10) Removed (previously on-link prefix). "SrpEcdsaKey", // (11) kKeySrpEcdsaKey "SrpClientInfo", // (12) kKeySrpClientInfo "SrpServerInfo", // (13) kKeySrpServerInfo - "LegacyNat64Prefix", // (14) kKeyLegacyNat64Prefix + "", // (14) Removed (previously NAT64 prefix) "BrUlaPrefix", // (15) kKeyBrUlaPrefix }; @@ -163,15 +163,11 @@ const char *SettingsBase::KeyToString(Key aKey) static_assert(3 == kKeyNetworkInfo, "kKeyNetworkInfo value is incorrect"); static_assert(4 == kKeyParentInfo, "kKeyParentInfo value is incorrect"); static_assert(5 == kKeyChildInfo, "kKeyChildInfo value is incorrect"); - static_assert(6 == kKeyReserved, "kKeyReserved value is incorrect"); static_assert(7 == kKeySlaacIidSecretKey, "kKeySlaacIidSecretKey value is incorrect"); static_assert(8 == kKeyDadInfo, "kKeyDadInfo value is incorrect"); - static_assert(9 == kKeyLegacyOmrPrefix, "kKeyLegacyOmrPrefix value is incorrect"); - static_assert(10 == kKeyOnLinkPrefix, "kKeyOnLinkPrefix value is incorrect"); static_assert(11 == kKeySrpEcdsaKey, "kKeySrpEcdsaKey value is incorrect"); static_assert(12 == kKeySrpClientInfo, "kKeySrpClientInfo value is incorrect"); static_assert(13 == kKeySrpServerInfo, "kKeySrpServerInfo value is incorrect"); - static_assert(14 == kKeyLegacyNat64Prefix, "kKeyLegacyNat64Prefix value is incorrect"); static_assert(15 == kKeyBrUlaPrefix, "kKeyBrUlaPrefix value is incorrect"); static_assert(kLastKey == kKeyBrUlaPrefix, "kLastKey is not valid"); @@ -435,9 +431,6 @@ void Settings::Log(Action aAction, Error aError, Key aKey, const void *aValue) #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE case kKeyBrUlaPrefix: - case kKeyLegacyOmrPrefix: - case kKeyOnLinkPrefix: - case kKeyLegacyNat64Prefix: LogPrefix(aAction, aKey, *reinterpret_cast(aValue)); break; #endif diff --git a/src/core/common/settings.hpp b/src/core/common/settings.hpp index 1758feba2..6db4a02f2 100644 --- a/src/core/common/settings.hpp +++ b/src/core/common/settings.hpp @@ -112,15 +112,11 @@ public: kKeyNetworkInfo = OT_SETTINGS_KEY_NETWORK_INFO, kKeyParentInfo = OT_SETTINGS_KEY_PARENT_INFO, kKeyChildInfo = OT_SETTINGS_KEY_CHILD_INFO, - kKeyReserved = OT_SETTINGS_KEY_RESERVED, kKeySlaacIidSecretKey = OT_SETTINGS_KEY_SLAAC_IID_SECRET_KEY, kKeyDadInfo = OT_SETTINGS_KEY_DAD_INFO, - kKeyLegacyOmrPrefix = OT_SETTINGS_KEY_LEGACY_OMR_PREFIX, - kKeyOnLinkPrefix = OT_SETTINGS_KEY_ON_LINK_PREFIX, kKeySrpEcdsaKey = OT_SETTINGS_KEY_SRP_ECDSA_KEY, kKeySrpClientInfo = OT_SETTINGS_KEY_SRP_CLIENT_INFO, kKeySrpServerInfo = OT_SETTINGS_KEY_SRP_SERVER_INFO, - kKeyLegacyNat64Prefix = OT_SETTINGS_KEY_LEGACY_NAT64_PREFIX, kKeyBrUlaPrefix = OT_SETTINGS_KEY_BR_ULA_PREFIX, }; @@ -583,37 +579,7 @@ public: private: BrUlaPrefix(void) = default; }; - - /** - * This class defines constants and types for legacy OMR prefix settings. - * - */ - class LegacyOmrPrefix - { - public: - static constexpr Key kKey = kKeyLegacyOmrPrefix; ///< The associated key. - - typedef Ip6::Prefix ValueType; ///< The associated value type. - - private: - LegacyOmrPrefix(void) = default; - }; - - /** - * This class defines constants and types for on-link prefix settings. - * - */ - class OnLinkPrefix - { - public: - static constexpr Key kKey = kKeyOnLinkPrefix; ///< The associated key. - - typedef Ip6::Prefix ValueType; ///< The associated value type. - - private: - OnLinkPrefix(void) = default; - }; -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#endif #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE /** diff --git a/src/core/common/time_ticker.cpp b/src/core/common/time_ticker.cpp index c770275e5..2bfb130a1 100644 --- a/src/core/common/time_ticker.cpp +++ b/src/core/common/time_ticker.cpp @@ -94,6 +94,13 @@ void TimeTicker::HandleTimer(void) Get().HandleTimeTick(); } +#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE + if (mReceivers & Mask(kNetworkDataNotifier)) + { + Get().HandleTimeTick(); + } +#endif + #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE if (mReceivers & Mask(kChildSupervisor)) { diff --git a/src/core/common/time_ticker.hpp b/src/core/common/time_ticker.hpp index 78860c4d6..a89f3e36e 100644 --- a/src/core/common/time_ticker.hpp +++ b/src/core/common/time_ticker.hpp @@ -71,6 +71,7 @@ public: kIp6FragmentReassembler, ///< `Ip6::Ip6` (handling of fragmented messages) kDuaManager, ///< `DuaManager` kMlrManager, ///< `MlrManager` + kNetworkDataNotifier, ///< `NetworkData::Notifier` kNumReceivers, ///< Number of receivers. }; diff --git a/src/core/config/border_router.h b/src/core/config/border_router.h index 5e45b387a..ef5a3a1e3 100644 --- a/src/core/config/border_router.h +++ b/src/core/config/border_router.h @@ -55,6 +55,32 @@ #define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE + * + * Define to 1 to enable mechanism on a Border Router which provides IP connectivity to request router role upgrade. + * + * This config is applicable on an `OPENTHREAD_FTD` build and when `OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE` is also + * enabled. + * + * A Border Router is considered to provide external IP connectivity if at least one of the below conditions hold: + * + * - It has added at least one external route entry. + * - It has added at least one prefix entry with default-route and on-mesh flags set. + * - It has added at least one domain prefix (domain and on-mesh flags set). + * + * A Border Router which provides IP connectivity and is acting as a REED is eligible to request a router role upgrade + * by sending an "Address Solicit" request to leader with status reason `BorderRouterRequest`. This reason is used when + * the number of active routers in the Thread mesh is above the threshold, and only if the number of existing eligible + * BRs (determined from the Thread Network Data) that are acting as router is less than two. This mechanism allows up + * to two eligible Border Routers to request router role upgrade when the number of routers is already above the + * threshold. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE +#define OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE 1 +#endif + /** * @def OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE * @@ -75,16 +101,6 @@ #define OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES 8 #endif -/** - * @def OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE - * - * Define to 1 to enable Border Routing Vicarious Router Solicitation. - * - */ -#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE -#define OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE 1 -#endif - /** * @def OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE * diff --git a/src/core/config/mac.h b/src/core/config/mac.h index 6572dd0e8..d44ff112c 100644 --- a/src/core/config/mac.h +++ b/src/core/config/mac.h @@ -499,4 +499,26 @@ #define OPENTHREAD_CONFIG_MAC_SCAN_DURATION 300 #endif +/** + * @def OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE + * + * This setting configures if the beacon payload parsing needs to be enabled in MAC. This is optional and is disabled by + * default because Thread 1.2.1 has removed support for beacon payloads. + * + */ +#ifndef OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE +#define OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE + * + * This setting configures if the beacon payload needs to be enabled in outgoing beacon frames. This is optional and is + * disabled by default because Thread 1.2.1 has removed support for beacon payloads. + * + */ +#ifndef OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE +#define OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE 0 +#endif + #endif // CONFIG_MAC_H_ diff --git a/src/core/config/netdata_publisher.h b/src/core/config/netdata_publisher.h index 7f9fe0708..bd3ef8f64 100644 --- a/src/core/config/netdata_publisher.h +++ b/src/core/config/netdata_publisher.h @@ -35,6 +35,7 @@ #ifndef CONFIG_NETDATA_PUBLISHER_H_ #define CONFIG_NETDATA_PUBLISHER_H_ +#include "config/border_router.h" #include "config/srp_server.h" /** @@ -48,7 +49,8 @@ * */ #ifndef OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE -#define OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE OPENTHREAD_CONFIG_SRP_SERVER_ENABLE +#define OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE \ + (OPENTHREAD_CONFIG_SRP_SERVER_ENABLE || OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE) #endif /** @@ -149,7 +151,13 @@ * */ #ifndef OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#define OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES \ + (OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES + 5) +#else #define OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES 3 #endif +#endif #endif // CONFIG_NETDATA_PUBLISHER_H_ diff --git a/src/core/config/openthread-core-config-check.h b/src/core/config/openthread-core-config-check.h index ed8495aaa..1536ec2ff 100644 --- a/src/core/config/openthread-core-config-check.h +++ b/src/core/config/openthread-core-config-check.h @@ -623,4 +623,8 @@ #error "OPENTHREAD_CONFIG_PLATFORM_CSL_UNCERT was removed and no longer supported" #endif +#ifdef OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE +#error "OPENTHREAD_CONFIG_BORDER_ROUTING_VICARIOUS_RS_ENABLE was removed and no longer supported" +#endif + #endif // OPENTHREAD_CORE_CONFIG_CHECK_H_ diff --git a/src/core/mac/data_poll_sender.cpp b/src/core/mac/data_poll_sender.cpp index ab1ad0db6..2c5b0899f 100644 --- a/src/core/mac/data_poll_sender.cpp +++ b/src/core/mac/data_poll_sender.cpp @@ -123,11 +123,6 @@ exit: StopPolling(); break; - case kErrorAlready: - LogDebg("Data poll tx requested when a previous data request still in send queue."); - ScheduleNextPoll(kUsePreviousPollPeriod); - break; - default: LogWarn("Unexpected error %s requesting data poll", ErrorToString(error)); ScheduleNextPoll(kRecalculatePollPeriod); diff --git a/src/core/mac/link_raw.hpp b/src/core/mac/link_raw.hpp index 16204b9e6..e0a66c99f 100644 --- a/src/core/mac/link_raw.hpp +++ b/src/core/mac/link_raw.hpp @@ -164,6 +164,7 @@ public: * @param[in] aCallback A pointer to a function called on completion of a scanned channel. * * @retval kErrorNone Successfully started scanning the channel. + * @retval kErrorBusy The radio is performing energy scanning. * @retval kErrorNotImplemented The radio doesn't support energy scanning. * @retval kErrorInvalidState If the raw link-layer isn't enabled. * diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index 7e2f6503c..6c99c573c 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -210,6 +210,11 @@ Error Mac::ConvertBeaconToActiveScanResult(const RxFrame *aBeaconFrame, ActiveSc { Error error = kErrorNone; Address address; +#if OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE + const BeaconPayload *beaconPayload = nullptr; + const Beacon * beacon = nullptr; + uint16_t payloadLength; +#endif memset(&aResult, 0, sizeof(ActiveScanResult)); @@ -229,6 +234,23 @@ Error Mac::ConvertBeaconToActiveScanResult(const RxFrame *aBeaconFrame, ActiveSc aResult.mRssi = aBeaconFrame->GetRssi(); aResult.mLqi = aBeaconFrame->GetLqi(); +#if OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE + payloadLength = aBeaconFrame->GetPayloadLength(); + + beacon = reinterpret_cast(aBeaconFrame->GetPayload()); + beaconPayload = reinterpret_cast(beacon->GetPayload()); + + if ((payloadLength >= (sizeof(*beacon) + sizeof(*beaconPayload))) && beacon->IsValid() && beaconPayload->IsValid()) + { + aResult.mVersion = beaconPayload->GetProtocolVersion(); + aResult.mIsJoinable = beaconPayload->IsJoiningPermitted(); + aResult.mIsNative = beaconPayload->IsNative(); + IgnoreError(AsCoreType(&aResult.mNetworkName).Set(beaconPayload->GetNetworkName())); + VerifyOrExit(IsValidUtf8String(aResult.mNetworkName.m8), error = kErrorParse); + aResult.mExtendedPanId = beaconPayload->GetExtendedPanId(); + } +#endif + LogBeacon("Received"); exit: @@ -482,7 +504,7 @@ Error Mac::RequestDataPollTransmission(void) Error error = kErrorNone; VerifyOrExit(IsEnabled(), error = kErrorInvalidState); - VerifyOrExit(!IsActiveOrPending(kOperationTransmitPoll), error = kErrorAlready); + VerifyOrExit(!IsActiveOrPending(kOperationTransmitPoll)); // We ensure data frame and data poll tx requests are handled in the // order they are requested. So if we have a pending direct data frame @@ -715,6 +737,10 @@ TxFrame *Mac::PrepareBeacon(void) TxFrame *frame; uint16_t fcf; Beacon * beacon = nullptr; +#if OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE + uint8_t beaconLength; + BeaconPayload *beaconPayload = nullptr; +#endif #if OPENTHREAD_CONFIG_MULTI_RADIO OT_ASSERT(!mTxBeaconRadioLinks.IsEmpty()); @@ -732,6 +758,30 @@ TxFrame *Mac::PrepareBeacon(void) beacon = reinterpret_cast(frame->GetPayload()); beacon->Init(); +#if OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE + beaconLength = sizeof(*beacon); + + beaconPayload = reinterpret_cast(beacon->GetPayload()); + + beaconPayload->Init(); + + if (IsJoinable()) + { + beaconPayload->SetJoiningPermitted(); + } + else + { + beaconPayload->ClearJoiningPermitted(); + } + + beaconPayload->SetNetworkName(Get().GetNetworkName().GetAsData()); + beaconPayload->SetExtendedPanId(Get().GetExtPanId()); + + beaconLength += sizeof(*beaconPayload); + + frame->SetPayloadLength(beaconLength); +#endif + LogBeacon("Sending"); return frame; diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index a5929a6d1..940a458bc 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -228,7 +228,6 @@ public: * This method requests transmission of a data poll (MAC Data Request) frame. * * @retval kErrorNone Data poll transmission request is scheduled successfully. - * @retval kErrorAlready MAC is busy sending earlier poll transmission request. * @retval kErrorInvalidState The MAC layer is not enabled. * */ diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index 1c1a1a504..9ffe2d57c 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -43,6 +43,7 @@ #include "common/const_cast.hpp" #include "common/encoding.hpp" #include "mac/mac_types.hpp" +#include "meshcop/network_name.hpp" namespace ot { namespace Mac { @@ -1482,6 +1483,145 @@ private: uint8_t mPendingAddressSpec; } OT_TOOL_PACKED_END; +/** + * This class implements IEEE 802.15.4 Beacon Payload generation and parsing. + * + */ +OT_TOOL_PACKED_BEGIN +class BeaconPayload +{ +public: + static constexpr uint8_t kProtocolId = 3; ///< Thread Protocol ID. + static constexpr uint8_t kProtocolVersion = 2; ///< Thread Protocol version. + static constexpr uint8_t kVersionOffset = 4; ///< Version field bit offset. + static constexpr uint8_t kVersionMask = 0xf << kVersionOffset; ///< Version field mask. + static constexpr uint8_t kNativeFlag = 1 << 3; ///< Native Commissioner flag. + static constexpr uint8_t kJoiningFlag = 1 << 0; ///< Joining Permitted flag. + + /** + * This method initializes the Beacon Payload. + * + */ + void Init(void) + { + mProtocolId = kProtocolId; + mFlags = kProtocolVersion << kVersionOffset; + } + + /** + * This method indicates whether or not the beacon appears to be a valid Thread Beacon Payload. + * + * @retval TRUE If the beacon appears to be a valid Thread Beacon Payload. + * @retval FALSE If the beacon does not appear to be a valid Thread Beacon Payload. + * + */ + bool IsValid(void) const { return (mProtocolId == kProtocolId); } + + /** + * This method returns the Protocol ID value. + * + * @returns the Protocol ID value. + * + */ + uint8_t GetProtocolId(void) const { return mProtocolId; } + + /** + * This method returns the Protocol Version value. + * + * @returns The Protocol Version value. + * + */ + uint8_t GetProtocolVersion(void) const { return mFlags >> kVersionOffset; } + + /** + * This method indicates whether or not the Native Commissioner flag is set. + * + * @retval TRUE If the Native Commissioner flag is set. + * @retval FALSE If the Native Commissioner flag is not set. + * + */ + bool IsNative(void) const { return (mFlags & kNativeFlag) != 0; } + + /** + * This method clears the Native Commissioner flag. + * + */ + void ClearNative(void) { mFlags &= ~kNativeFlag; } + + /** + * This method sets the Native Commissioner flag. + * + */ + void SetNative(void) { mFlags |= kNativeFlag; } + + /** + * This method indicates whether or not the Joining Permitted flag is set. + * + * @retval TRUE If the Joining Permitted flag is set. + * @retval FALSE If the Joining Permitted flag is not set. + * + */ + bool IsJoiningPermitted(void) const { return (mFlags & kJoiningFlag) != 0; } + + /** + * This method clears the Joining Permitted flag. + * + */ + void ClearJoiningPermitted(void) { mFlags &= ~kJoiningFlag; } + + /** + * This method sets the Joining Permitted flag. + * + */ + void SetJoiningPermitted(void) + { + mFlags |= kJoiningFlag; + +#if OPENTHREAD_CONFIG_MAC_JOIN_BEACON_VERSION != 2 // check against kProtocolVersion + mFlags &= ~kVersionMask; + mFlags |= OPENTHREAD_CONFIG_MAC_JOIN_BEACON_VERSION << kVersionOffset; +#endif + } + + /** + * This method gets the Network Name field. + * + * @returns The Network Name field as `NameData`. + * + */ + MeshCoP::NameData GetNetworkName(void) const { return MeshCoP::NameData(mNetworkName, sizeof(mNetworkName)); } + + /** + * This method sets the Network Name field. + * + * @param[in] aNameData The Network Name (as a `NameData`). + * + */ + void SetNetworkName(const MeshCoP::NameData &aNameData) { aNameData.CopyTo(mNetworkName, sizeof(mNetworkName)); } + + /** + * This method returns the Extended PAN ID field. + * + * @returns The Extended PAN ID field. + * + */ + const otExtendedPanId &GetExtendedPanId(void) const { return mExtendedPanId; } + + /** + * This method sets the Extended PAN ID field. + * + * @param[in] aExtPanId An Extended PAN ID. + * + */ + void SetExtendedPanId(const otExtendedPanId &aExtPanId) { mExtendedPanId = aExtPanId; } + +private: + uint8_t mProtocolId; + uint8_t mFlags; + char mNetworkName[MeshCoP::NetworkName::kMaxSize]; + otExtendedPanId mExtendedPanId; +} OT_TOOL_PACKED_END; + /** * This class implements CSL IE data structure. * diff --git a/src/core/mac/mac_links.hpp b/src/core/mac/mac_links.hpp index ff6821cf5..85aa0e54b 100644 --- a/src/core/mac/mac_links.hpp +++ b/src/core/mac/mac_links.hpp @@ -572,6 +572,7 @@ public: * @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned. * * @retval kErrorNone Successfully started scanning the channel. + * @retval kErrorBusy The radio is performing energy scanning. * @retval kErrorInvalidState The radio was disabled or transmitting. * @retval kErrorNotImplemented Energy scan is not supported by radio link. * diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 4e243e97b..da0dbc6ae 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -379,6 +379,7 @@ public: * @param[in] aScanDuration The duration, in milliseconds, for the channel to be scanned. * * @retval kErrorNone Successfully started scanning the channel. + * @retval kErrorBusy The radio is performing energy scanning. * @retval kErrorInvalidState The radio was disabled or transmitting. * @retval kErrorNotImplemented Energy scan is not supported (applicable in link-raw/radio mode only). * diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index 1fb3984fe..e10a79da5 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -588,7 +588,7 @@ void BorderAgent::Start(void) mState = kStateStarted; mUdpProxyPort = 0; - LogInfo("Border Agent start listening on port %d", kBorderAgentUdpPort); + LogInfo("Border Agent start listening on port %u", GetUdpPort()); exit: if (error != kErrorNone) diff --git a/src/core/meshcop/dataset.cpp b/src/core/meshcop/dataset.cpp index 74d9a0919..047808594 100644 --- a/src/core/meshcop/dataset.cpp +++ b/src/core/meshcop/dataset.cpp @@ -43,6 +43,7 @@ #include "common/log.hpp" #include "mac/mac_types.hpp" #include "meshcop/meshcop_tlvs.hpp" +#include "meshcop/timestamp.hpp" #include "thread/mle_tlvs.hpp" namespace ot { @@ -69,10 +70,12 @@ Error Dataset::Info::GenerateRandom(Instance &aInstance) Clear(); - mActiveTimestamp = 1; - mChannel = preferredChannels.ChooseRandomChannel(); - mChannelMask = supportedChannels.GetMask(); - mPanId = Mac::GenerateRandomPanId(); + mActiveTimestamp.mSeconds = 1; + mActiveTimestamp.mTicks = 0; + mActiveTimestamp.mAuthoritative = false; + mChannel = preferredChannels.ChooseRandomChannel(); + mChannelMask = supportedChannels.GetMask(); + mPanId = Mac::GenerateRandomPanId(); AsCoreType(&mSecurityPolicy).SetToDefault(); SuccessOrExit(error = AsCoreType(&mNetworkKey).GenerateRandom()); @@ -193,7 +196,7 @@ void Dataset::ConvertTo(Info &aDatasetInfo) const switch (cur->GetType()) { case Tlv::kActiveTimestamp: - aDatasetInfo.SetActiveTimestamp(As(cur)->GetTimestamp().GetSeconds()); + aDatasetInfo.SetActiveTimestamp(As(cur)->GetTimestamp()); break; case Tlv::kChannel: @@ -237,7 +240,7 @@ void Dataset::ConvertTo(Info &aDatasetInfo) const break; case Tlv::kPendingTimestamp: - aDatasetInfo.SetPendingTimestamp(As(cur)->GetTimestamp().GetSeconds()); + aDatasetInfo.SetPendingTimestamp(As(cur)->GetTimestamp()); break; case Tlv::kPskc: @@ -286,20 +289,18 @@ Error Dataset::SetFrom(const Info &aDatasetInfo) if (aDatasetInfo.IsActiveTimestampPresent()) { - Timestamp timestamp; + Timestamp activeTimestamp; - timestamp.Clear(); - timestamp.SetSeconds(aDatasetInfo.GetActiveTimestamp()); - IgnoreError(SetTlv(Tlv::kActiveTimestamp, timestamp)); + aDatasetInfo.GetActiveTimestamp(activeTimestamp); + IgnoreError(SetTlv(Tlv::kActiveTimestamp, activeTimestamp)); } if (aDatasetInfo.IsPendingTimestampPresent()) { - Timestamp timestamp; + Timestamp pendingTimestamp; - timestamp.Clear(); - timestamp.SetSeconds(aDatasetInfo.GetPendingTimestamp()); - IgnoreError(SetTlv(Tlv::kPendingTimestamp, timestamp)); + aDatasetInfo.GetPendingTimestamp(pendingTimestamp); + IgnoreError(SetTlv(Tlv::kPendingTimestamp, pendingTimestamp)); } if (aDatasetInfo.IsDelayPresent()) diff --git a/src/core/meshcop/dataset.hpp b/src/core/meshcop/dataset.hpp index 4e200d6cf..380951373 100644 --- a/src/core/meshcop/dataset.hpp +++ b/src/core/meshcop/dataset.hpp @@ -203,7 +203,7 @@ public: * @returns The Active Timestamp in the Dataset. * */ - uint64_t GetActiveTimestamp(void) const { return mActiveTimestamp; } + void GetActiveTimestamp(Timestamp &aTimestamp) const { aTimestamp.SetFromTimestamp(mActiveTimestamp); } /** * This method sets the Active Timestamp in the Dataset. @@ -211,9 +211,9 @@ public: * @param[in] aTimestamp A Timestamp value. * */ - void SetActiveTimestamp(uint64_t aTimestamp) + void SetActiveTimestamp(const Timestamp &aTimestamp) { - mActiveTimestamp = aTimestamp; + aTimestamp.ConvertTo(mActiveTimestamp); mComponents.mIsActiveTimestampPresent = true; } @@ -234,7 +234,7 @@ public: * @returns The Pending Timestamp in the Dataset. * */ - uint64_t GetPendingTimestamp(void) const { return mPendingTimestamp; } + void GetPendingTimestamp(Timestamp &aTimestamp) const { aTimestamp.SetFromTimestamp(mPendingTimestamp); } /** * This method sets the Pending Timestamp in the Dataset. @@ -242,9 +242,9 @@ public: * @param[in] aTimestamp A Timestamp value. * */ - void SetPendingTimestamp(uint64_t aTimestamp) + void SetPendingTimestamp(const Timestamp &aTimestamp) { - mPendingTimestamp = aTimestamp; + aTimestamp.ConvertTo(mPendingTimestamp); mComponents.mIsPendingTimestampPresent = true; } diff --git a/src/core/meshcop/dataset_updater.cpp b/src/core/meshcop/dataset_updater.cpp index 00782b227..36d63d7f9 100644 --- a/src/core/meshcop/dataset_updater.cpp +++ b/src/core/meshcop/dataset_updater.cpp @@ -41,6 +41,7 @@ #include "common/locator_getters.hpp" #include "common/log.hpp" #include "common/random.hpp" +#include "meshcop/timestamp.hpp" namespace ot { namespace MeshCoP { @@ -193,9 +194,17 @@ void DatasetUpdater::HandleNotifierEvents(Events aEvents) { Finish(kErrorNone); } - else if (requestedDataset.GetActiveTimestamp() <= dataset.GetActiveTimestamp()) + else { - Finish(kErrorAlready); + Timestamp requestedDatasetTimestamp; + Timestamp activeDatasetTimestamp; + + requestedDataset.GetActiveTimestamp(requestedDatasetTimestamp); + dataset.GetActiveTimestamp(activeDatasetTimestamp); + if (Timestamp::Compare(requestedDatasetTimestamp, activeDatasetTimestamp) <= 0) + { + Finish(kErrorAlready); + } } } diff --git a/src/core/meshcop/timestamp.cpp b/src/core/meshcop/timestamp.cpp index 5d8f59bf6..08ed36b94 100644 --- a/src/core/meshcop/timestamp.cpp +++ b/src/core/meshcop/timestamp.cpp @@ -38,13 +38,23 @@ namespace ot { namespace MeshCoP { +void Timestamp::ConvertTo(otTimestamp &aTimestamp) const +{ + aTimestamp.mSeconds = GetSeconds(); + aTimestamp.mTicks = GetTicks(); + aTimestamp.mAuthoritative = GetAuthoritative(); +} + +void Timestamp::SetFromTimestamp(const otTimestamp &aTimestamp) +{ + SetSeconds(aTimestamp.mSeconds); + SetTicks(aTimestamp.mTicks); + SetAuthoritative(aTimestamp.mAuthoritative); +} + int Timestamp::Compare(const Timestamp *aFirst, const Timestamp *aSecond) { - int rval; - uint64_t firstSeconds; - uint64_t secondSeconds; - uint16_t firstTicks; - uint16_t secondTicks; + int rval; if (aFirst == nullptr) { @@ -62,22 +72,46 @@ int Timestamp::Compare(const Timestamp *aFirst, const Timestamp *aSecond) // Both are non-null. - firstSeconds = aFirst->GetSeconds(); - secondSeconds = aSecond->GetSeconds(); + rval = Compare(*aFirst, *aSecond); + +exit: + return rval; +} + +int Timestamp::Compare(const Timestamp &aFirst, const Timestamp &aSecond) +{ + int rval; + uint64_t firstSeconds; + uint64_t secondSeconds; + uint16_t firstTicks; + uint16_t secondTicks; + bool firstAuthoritative; + bool secondAuthoritative; + + firstSeconds = aFirst.GetSeconds(); + secondSeconds = aSecond.GetSeconds(); if (firstSeconds != secondSeconds) { ExitNow(rval = (firstSeconds > secondSeconds) ? 1 : -1); } - firstTicks = aFirst->GetTicks(); - secondTicks = aSecond->GetTicks(); + firstTicks = aFirst.GetTicks(); + secondTicks = aSecond.GetTicks(); if (firstTicks != secondTicks) { ExitNow(rval = (firstTicks > secondTicks) ? 1 : -1); } + firstAuthoritative = aFirst.GetAuthoritative(); + secondAuthoritative = aSecond.GetAuthoritative(); + + if (firstAuthoritative != secondAuthoritative) + { + ExitNow(rval = firstAuthoritative ? 1 : -1); + } + rval = 0; exit: diff --git a/src/core/meshcop/timestamp.hpp b/src/core/meshcop/timestamp.hpp index ea3b11583..1eace251d 100644 --- a/src/core/meshcop/timestamp.hpp +++ b/src/core/meshcop/timestamp.hpp @@ -39,6 +39,7 @@ #include +#include #include #include "common/clearable.hpp" @@ -59,6 +60,18 @@ OT_TOOL_PACKED_BEGIN class Timestamp : public Clearable { public: + /** + * This method converts the timestamp to `otTimestamp`. + * + */ + void ConvertTo(otTimestamp &aTimestamp) const; + + /** + * This method sets the timestamp from `otTimestamp`. + * + */ + void SetFromTimestamp(const otTimestamp &aTimestamp); + /** * This method returns the Seconds value. * @@ -127,6 +140,15 @@ public: */ void AdvanceRandomTicks(void); + /** + * This method indicates whether the timestamp indicates an MLE Orphan Announce message. + * + * @retval TRUE The timestamp indicates an Orphan Announce message. + * @retval FALSE If the timestamp does not indicate an Orphan Announce message. + * + */ + bool IsOrphanTimestamp(void) const { return GetSeconds() == 0 && GetTicks() == 0 && GetAuthoritative(); } + /** * This static method compares two timestamps. * @@ -143,6 +165,19 @@ public: */ static int Compare(const Timestamp *aFirst, const Timestamp *aSecond); + /** + * This static method compares two timestamps. + * + * @param[in] aFirst A reference to the first timestamp to compare. + * @param[in] aSecond A reference to the second timestamp to compare. + * + * @retval -1 if @p aFirst is less than @p aSecond (`aFirst < aSecond`). + * @retval 0 if @p aFirst is equal to @p aSecond (`aFirst == aSecond`). + * @retval 1 if @p aFirst is greater than @p aSecond (`aFirst > aSecond`). + * + */ + static int Compare(const Timestamp &aFirst, const Timestamp &aSecond); + private: static constexpr uint8_t kTicksOffset = 1; static constexpr uint16_t kTicksMask = 0x7fff << kTicksOffset; diff --git a/src/core/net/dns_dso.cpp b/src/core/net/dns_dso.cpp index 27222baf5..a18b45938 100644 --- a/src/core/net/dns_dso.cpp +++ b/src/core/net/dns_dso.cpp @@ -1409,20 +1409,7 @@ exit: void Dso::Connection::PendingRequests::Remove(MessageId aMessageId) { - Entry *entry = mRequests.FindMatching(aMessageId); - Entry *lastEntry; - - VerifyOrExit(entry != nullptr); - - // Remove last entry from the `mRequests` array, if it is not the - // `entry` we want to remove, replace `entry` with `lastEntry. - - lastEntry = mRequests.PopBack(); - VerifyOrExit(lastEntry != entry); - *entry = *lastEntry; - -exit: - return; + mRequests.RemoveMatching(aMessageId); } bool Dso::Connection::PendingRequests::HasAnyTimedOut(TimeMilli aNow) const diff --git a/src/core/net/dnssd_server.cpp b/src/core/net/dnssd_server.cpp index 40249ce7b..c996c153a 100644 --- a/src/core/net/dnssd_server.cpp +++ b/src/core/net/dnssd_server.cpp @@ -65,6 +65,7 @@ Server::Server(Instance &aInstance) , mQueryUnsubscribe(nullptr) , mTimer(aInstance, Server::HandleTimer) { + mCounters.Clear(); } Error Server::Start(void) @@ -173,11 +174,19 @@ void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage #endif // Resolve the question using query callbacks if SRP server failed to resolve the questions. - if (responseHeader.GetAnswerCount() == 0 && - kErrorNone == ResolveByQueryCallbacks(responseHeader, *responseMessage, compressInfo, aMessageInfo)) + if (responseHeader.GetAnswerCount() == 0) { - resolveByQueryCallbacks = true; + if (kErrorNone == ResolveByQueryCallbacks(responseHeader, *responseMessage, compressInfo, aMessageInfo)) + { + resolveByQueryCallbacks = true; + } } +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE + else + { + ++mCounters.mResolvedBySrp; + } +#endif exit: if (error == kErrorNone && !resolveByQueryCallbacks) @@ -220,6 +229,8 @@ void Server::SendResponse(Header aHeader, { LogInfo("send DNS-SD reply: %s, RCODE=%d", ErrorToString(error), aResponseCode); } + + UpdateResponseCounters(aResponseCode); } Header::Response Server::AddQuestions(const Header & aRequestHeader, @@ -842,7 +853,7 @@ Server::QueryTransaction *Server::NewQuery(const Header & aResponseHead continue; } - query.Init(aResponseHeader, aResponseMessage, aCompressInfo, aMessageInfo); + query.Init(aResponseHeader, aResponseMessage, aCompressInfo, aMessageInfo, GetInstance()); ExitNow(newQuery = &query); } @@ -1194,10 +1205,12 @@ void Server::FinalizeQuery(QueryTransaction &aQuery, Header::Response aResponseC void Server::QueryTransaction::Init(const Header & aResponseHeader, Message & aResponseMessage, const NameCompressInfo &aCompressInfo, - const Ip6::MessageInfo &aMessageInfo) + const Ip6::MessageInfo &aMessageInfo, + Instance & aInstance) { OT_ASSERT(mResponseMessage == nullptr); + InstanceLocatorInit::Init(aInstance); mResponseHeader = aResponseHeader; mResponseMessage = &aResponseMessage; mCompressInfo = aCompressInfo; @@ -1209,10 +1222,35 @@ void Server::QueryTransaction::Finalize(Header::Response aResponseMessage, Ip6:: { OT_ASSERT(mResponseMessage != nullptr); - SendResponse(mResponseHeader, aResponseMessage, *mResponseMessage, mMessageInfo, aSocket); + Get().SendResponse(mResponseHeader, aResponseMessage, *mResponseMessage, mMessageInfo, aSocket); mResponseMessage = nullptr; } +void Server::UpdateResponseCounters(Header::Response aResponseCode) +{ + switch (aResponseCode) + { + case UpdateHeader::kResponseSuccess: + ++mCounters.mSuccessResponse; + break; + case UpdateHeader::kResponseServerFailure: + ++mCounters.mServerFailureResponse; + break; + case UpdateHeader::kResponseFormatError: + ++mCounters.mFormatErrorResponse; + break; + case UpdateHeader::kResponseNameError: + ++mCounters.mNameErrorResponse; + break; + case UpdateHeader::kResponseNotImplemented: + ++mCounters.mNotImplementedResponse; + break; + default: + ++mCounters.mOtherResponse; + break; + } +} + } // namespace ServiceDiscovery } // namespace Dns } // namespace ot diff --git a/src/core/net/dnssd_server.hpp b/src/core/net/dnssd_server.hpp index 2bab3b07e..de16c424e 100644 --- a/src/core/net/dnssd_server.hpp +++ b/src/core/net/dnssd_server.hpp @@ -67,6 +67,14 @@ class Server : public InstanceLocator, private NonCopyable friend class Srp::Server; public: + /** + * This class contains the counters of the DNS-SD server. + * + */ + class Counters : public otDnssdCounters, public Clearable + { + }; + /** * This enumeration specifies a DNS-SD query type. * @@ -155,6 +163,14 @@ public: */ static DnsQueryType GetQueryTypeAndName(const otDnssdQuery *aQuery, char (&aName)[Name::kMaxNameSize]); + /** + * This method returns the counters of the DNS-SD server. + * + * @returns A reference to the `Counters` instance. + * + */ + const Counters &GetCounters(void) const { return mCounters; }; + private: class NameCompressInfo : public Clearable { @@ -273,7 +289,7 @@ private: * This class contains the compress information for a dns packet. * */ - class QueryTransaction + class QueryTransaction : public InstanceLocatorInit { public: explicit QueryTransaction(void) @@ -284,7 +300,8 @@ private: void Init(const Header & aResponseHeader, Message & aResponseMessage, const NameCompressInfo &aCompressInfo, - const Ip6::MessageInfo &aMessageInfo); + const Ip6::MessageInfo &aMessageInfo, + Instance & aInstance); bool IsValid(void) const { return mResponseMessage != nullptr; } const Ip6::MessageInfo &GetMessageInfo(void) const { return mMessageInfo; } const Header & GetResponseHeader(void) const { return mResponseHeader; } @@ -347,7 +364,7 @@ private: static void IncResourceRecordCount(Header &aHeader, bool aAdditional); static Error FindNameComponents(const char *aName, const char *aDomain, NameComponentsOffsetInfo &aInfo); static Error FindPreviousLabel(const char *aName, uint8_t &aStart, uint8_t &aStop); - static void SendResponse(Header aHeader, + void SendResponse(Header aHeader, Header::Response aResponseCode, Message & aMessage, const Ip6::MessageInfo &aMessageInfo, @@ -392,6 +409,8 @@ private: void HandleTimer(void); void ResetTimer(void); + void UpdateResponseCounters(Header::Response aResponseCode); + static const char kDnssdProtocolUdp[]; static const char kDnssdProtocolTcp[]; static const char kDnssdSubTypeLabel[]; @@ -403,6 +422,8 @@ private: otDnssdQuerySubscribeCallback mQuerySubscribe; otDnssdQueryUnsubscribeCallback mQueryUnsubscribe; TimerMilli mTimer; + + Counters mCounters; }; } // namespace ServiceDiscovery diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index 3f5ec0de3..b510fb12c 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -1309,43 +1309,29 @@ exit: bool Ip6::ShouldForwardToThread(const MessageInfo &aMessageInfo, bool aFromHost) const { - OT_UNUSED_VARIABLE(aFromHost); + bool shouldForward = false; - bool rval = false; - - if (aMessageInfo.GetSockAddr().IsMulticast()) + if (aMessageInfo.GetSockAddr().IsMulticast() || aMessageInfo.GetSockAddr().IsLinkLocal()) { - // multicast - ExitNow(rval = true); - } - else if (aMessageInfo.GetSockAddr().IsLinkLocal()) - { - // on-link link-local address - ExitNow(rval = true); + shouldForward = true; } else if (IsOnLink(aMessageInfo.GetSockAddr())) { - // on-link global address #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE - ExitNow(rval = (aFromHost || - !Get().ShouldForwardDuaToBackbone(aMessageInfo.GetSockAddr()))); + shouldForward = + (aFromHost || !Get().ShouldForwardDuaToBackbone(aMessageInfo.GetSockAddr())); #else - ExitNow(rval = true); + OT_UNUSED_VARIABLE(aFromHost); + shouldForward = true; #endif } else if (Get().RouteLookup(aMessageInfo.GetPeerAddr(), aMessageInfo.GetSockAddr(), nullptr) == kErrorNone) { - // route - ExitNow(rval = true); - } - else - { - ExitNow(rval = false); + shouldForward = true; } -exit: - return rval; + return shouldForward; } const Netif::UnicastAddress *Ip6::SelectSourceAddress(MessageInfo &aMessageInfo) diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp index 11dcf9393..5708146a0 100644 --- a/src/core/net/ip6_address.cpp +++ b/src/core/net/ip6_address.cpp @@ -77,13 +77,17 @@ bool Prefix::IsEqual(const uint8_t *aPrefixBytes, uint8_t aPrefixLength) const bool Prefix::operator<(const Prefix &aOther) const { bool isSmaller; + uint8_t minLength; uint8_t matchedLength; - VerifyOrExit(GetLength() == aOther.GetLength(), isSmaller = GetLength() < aOther.GetLength()); + minLength = OT_MIN(GetLength(), aOther.GetLength()); + matchedLength = MatchLength(GetBytes(), aOther.GetBytes(), SizeForLength(minLength)); - matchedLength = MatchLength(GetBytes(), aOther.GetBytes(), GetBytesSize()); - - VerifyOrExit(matchedLength < GetLength(), isSmaller = false); + if (matchedLength >= minLength) + { + isSmaller = (GetLength() < aOther.GetLength()); + ExitNow(); + } isSmaller = GetBytes()[matchedLength / CHAR_BIT] < aOther.GetBytes()[matchedLength / CHAR_BIT]; diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp index c7bf081c0..3c55f2190 100644 --- a/src/core/net/ip6_address.hpp +++ b/src/core/net/ip6_address.hpp @@ -266,8 +266,10 @@ public: /** * This method overloads operator `<` to compare two prefixes. * - * A prefix with shorter length is considered smaller than the one with longer length. If the prefix lengths are - * equal, then the prefix bytes are compared directly. + * If the two prefixes have the same length N, then the bytes are compared directly (as two big-endian N-bit + * numbers). If the two prefix have different lengths, the shorter prefix is padded by `0` bit up to the longer + * prefix length N before the bytes are compared (as big-endian N-bit numbers). If all bytes are equal, the prefix + * with shorter length is considered smaller. * * @param[in] aOther The other prefix to compare against. * diff --git a/src/core/net/nd6.cpp b/src/core/net/nd6.cpp new file mode 100644 index 000000000..5b57e1be9 --- /dev/null +++ b/src/core/net/nd6.cpp @@ -0,0 +1,282 @@ +/* + * 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 IPv6 Neighbor Discovery (ND6). + * + */ + +#include "nd6.hpp" + +#include "common/as_core_type.hpp" +#include "common/code_utils.hpp" + +namespace ot { +namespace Ip6 { +namespace Nd { + +//---------------------------------------------------------------------------------------------------------------------- +// Option::Iterator + +Option::Iterator::Iterator(void) + : mOption(nullptr) + , mEnd(nullptr) +{ + // An empty iterator (used to indicate `end()` of list). +} + +Option::Iterator::Iterator(const void *aStart, const void *aEnd) + : mOption(nullptr) + , mEnd(reinterpret_cast(aEnd)) +{ + // Note that `Validate()` uses `mEnd` so can only be called after + // `mEnd` is set. + + mOption = Validate(reinterpret_cast(aStart)); +} + +const Option *Option::Iterator::Next(const Option *aOption) +{ + return reinterpret_cast(reinterpret_cast(aOption) + aOption->GetSize()); +} + +void Option::Iterator::Advance(void) +{ + mOption = (mOption != nullptr) ? Validate(Next(mOption)) : nullptr; +} + +const Option *Option::Iterator::Validate(const Option *aOption) const +{ + // Check if `aOption` is well-formed and fits in the range + // up to `mEnd`. Returns `aOption` if it is valid, `nullptr` + // otherwise. + + return ((aOption != nullptr) && ((aOption + 1) <= mEnd) && aOption->IsValid() && (Next(aOption) <= mEnd)) ? aOption + : nullptr; +} + +//---------------------------------------------------------------------------------------------------------------------- +// PrefixInfoOption + +void PrefixInfoOption::Init(void) +{ + Clear(); + SetType(kTypePrefixInfo); + SetSize(sizeof(PrefixInfoOption)); + + OT_UNUSED_VARIABLE(mReserved2); +} + +void PrefixInfoOption::SetPrefix(const Prefix &aPrefix) +{ + mPrefixLength = aPrefix.mLength; + mPrefix = AsCoreType(&aPrefix.mPrefix); +} + +void PrefixInfoOption::GetPrefix(Prefix &aPrefix) const +{ + aPrefix.Set(mPrefix.GetBytes(), mPrefixLength); +} + +bool PrefixInfoOption::IsValid(void) const +{ + return (GetSize() >= sizeof(*this)) && (mPrefixLength <= Prefix::kMaxLength) && + (GetPreferredLifetime() <= GetValidLifetime()); +} + +//---------------------------------------------------------------------------------------------------------------------- +// RouteInfoOption + +void RouteInfoOption::Init(void) +{ + Clear(); + SetType(kTypeRouteInfo); +} + +void RouteInfoOption::SetPreference(RoutePreference aPreference) +{ + mResvdPrf &= ~kPreferenceMask; + mResvdPrf |= (NetworkData::RoutePreferenceToValue(aPreference) << kPreferenceOffset) & kPreferenceMask; +} + +RoutePreference RouteInfoOption::GetPreference(void) const +{ + return NetworkData::RoutePreferenceFromValue((mResvdPrf & kPreferenceMask) >> kPreferenceOffset); +} + +void RouteInfoOption::SetPrefix(const Prefix &aPrefix) +{ + SetLength(OptionLengthForPrefix(aPrefix.mLength)); + mPrefixLength = aPrefix.mLength; + memcpy(GetPrefixBytes(), aPrefix.GetBytes(), aPrefix.GetBytesSize()); +} + +void RouteInfoOption::GetPrefix(Prefix &aPrefix) const +{ + aPrefix.Set(GetPrefixBytes(), mPrefixLength); +} + +bool RouteInfoOption::IsValid(void) const +{ + return (GetSize() >= kMinSize) && (mPrefixLength <= Prefix::kMaxLength) && + (GetLength() >= OptionLengthForPrefix(mPrefixLength)) && + NetworkData::IsRoutePreferenceValid(GetPreference()); +} + +uint8_t RouteInfoOption::OptionLengthForPrefix(uint8_t aPrefixLength) +{ + static constexpr uint8_t kMaxPrefixLenForOptionLen1 = 0; + static constexpr uint8_t kMaxPrefixLenForOptionLen2 = 64; + + uint8_t length; + + // The Option Length can be 1, 2, or 3 depending on the prefix + // length + // + // - 1 when prefix len is zero. + // - 2 when prefix len is less then or equal to 64. + // - 3 otherwise. + + if (aPrefixLength == kMaxPrefixLenForOptionLen1) + { + length = 1; + } + else if (aPrefixLength <= kMaxPrefixLenForOptionLen2) + { + length = 2; + } + else + { + length = 3; + } + + return length; +} + +//---------------------------------------------------------------------------------------------------------------------- +// RouterAdverMessage::Header + +void RouterAdvertMessage::Header::SetToDefault(void) +{ + OT_UNUSED_VARIABLE(mCode); + OT_UNUSED_VARIABLE(mCurHopLimit); + OT_UNUSED_VARIABLE(mReachableTime); + OT_UNUSED_VARIABLE(mRetransTimer); + + Clear(); + mType = Icmp::Header::kTypeRouterAdvert; +} + +RoutePreference RouterAdvertMessage::Header::GetDefaultRouterPreference(void) const +{ + return NetworkData::RoutePreferenceFromValue((mFlags & kPreferenceMask) >> kPreferenceOffset); +} + +void RouterAdvertMessage::Header::SetDefaultRouterPreference(RoutePreference aPreference) +{ + mFlags &= ~kPreferenceMask; + mFlags |= (NetworkData::RoutePreferenceToValue(aPreference) << kPreferenceOffset) & kPreferenceMask; +} + +//---------------------------------------------------------------------------------------------------------------------- +// RouterAdverMessage + +Option *RouterAdvertMessage::AppendOption(uint16_t aOptionSize) +{ + // This method appends an option with a given size to the RA + // message by reserving space in the data buffer if there is + // room. On success returns pointer to the option, on failure + // returns `nullptr`. The returned option needs to be + // initialized and populated by the caller. + + Option * option = nullptr; + uint32_t newLength = mData.GetLength(); + + newLength += aOptionSize; + VerifyOrExit(newLength <= mMaxLength); + + option = reinterpret_cast