diff --git a/.clang-format b/.clang-format index dce55a85efc..b384cc6d396 100644 --- a/.clang-format +++ b/.clang-format @@ -11,7 +11,7 @@ AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: InlineOnly +AllowShortFunctionsOnASingleLine: All AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: true AlwaysBreakAfterDefinitionReturnType: None diff --git a/examples/platforms/cc2538/Makefile.platform.am b/.github/dependabot.yml similarity index 78% rename from examples/platforms/cc2538/Makefile.platform.am rename to .github/dependabot.yml index f908b4ed863..b4cb6e3b232 100644 --- a/examples/platforms/cc2538/Makefile.platform.am +++ b/.github/dependabot.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, The OpenThread Authors. +# Copyright (c) 2022, The OpenThread Authors. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -26,14 +26,13 @@ # POSSIBILITY OF SUCH DAMAGE. # -# -# cc2538 platform-specific Makefile -# - -LDADD_COMMON += \ - $(top_builddir)/examples/platforms/cc2538/libopenthread-cc2538.a \ - $(NULL) - -LDFLAGS_COMMON += \ - -T $(top_srcdir)/examples/platforms/cc2538/cc2538.ld \ - $(NULL) +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "github-actions" + rebase-strategy: "disabled" + open-pull-requests-limit: 1 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d158eeaac6f..b385b343178 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,28 +28,38 @@ name: Build -on: [push, pull_request] +on: + push: + branches-ignore: + - 'dependabot/**' + pull_request: + branches: + - 'main' -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +permissions: + contents: read + +jobs: pretty: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update - sudo apt-get --no-install-recommends install -y clang-format-9 clang-tidy-9 shellcheck + sudo apt-get --no-install-recommends install -y clang-format-14 clang-tidy-14 shellcheck python3 -m pip install yapf==0.31.0 sudo snap install shfmt - name: Check @@ -59,23 +69,39 @@ jobs: markdown-lint-check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: gaurav-nelson/github-action-markdown-link-check@v1 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + - uses: gaurav-nelson/github-action-markdown-link-check@5c5dfc0ac2e225883c0e5f03a85311ec2830d368 # v1 with: use-verbose-mode: 'yes' max-depth: 3 cmake-version: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | - sudo pip3 install --system -U cmake==3.10.3 + sudo apt-get --no-install-recommends install -y build-essential ninja-build libreadline-dev libncurses-dev + sudo apt-get remove cmake + sudo apt-get purge --auto-remove cmake + wget http://www.cmake.org/files/v3.10/cmake-3.10.3.tar.gz + tar xf cmake-3.10.3.tar.gz + cd cmake-3.10.3 + ./configure + sudo make install cmake --version | grep 3.10.3 - sudo apt-get --no-install-recommends install -y ninja-build libreadline-dev libncurses-dev - name: Build run: | OT_NODE_TYPE=rcp ./script/test build @@ -97,7 +123,12 @@ jobs: CC: ${{ matrix.compiler_c }} CXX: ${{ matrix.compiler_cpp }} steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -109,33 +140,43 @@ jobs: script/test package scan-build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update - sudo apt-get --no-install-recommends install -y clang-tools-9 ninja-build + sudo apt-get --no-install-recommends install -y clang-tools-14 ninja-build - name: Run run: | script/check-scan-build mbedtls3-build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | sudo apt-get --no-install-recommends install -y ninja-build libreadline-dev libncurses-dev rm -rf third_party/mbedtls/repo - - uses: actions/checkout@v2 + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: repository: ARMmbed/mbedtls - ref: v3.1.0 + ref: v3.2.1 path: third_party/mbedtls/repo - name: Build run: | @@ -143,7 +184,7 @@ jobs: arm-gcc: name: arm-gcc-${{ matrix.gcc_ver }} - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: @@ -163,37 +204,63 @@ jobs: - gcc_ver: 9 gcc_download_url: https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2019q4/RC2.1/gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2 gcc_extract_dir: gcc-arm-none-eabi-9-2019-q4-major + - gcc_ver: 10 + gcc_download_url: https://developer.arm.com/-/media/Files/downloads/gnu-rm/10.3-2021.10/gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 + gcc_extract_dir: gcc-arm-none-eabi-10.3-2021.10 + - gcc_ver: 11 + gcc_download_url: https://developer.arm.com/-/media/Files/downloads/gnu/11.3.rel1/binrel/arm-gnu-toolchain-11.3.rel1-x86_64-arm-none-eabi.tar.xz + gcc_extract_dir: arm-gnu-toolchain-11.3.rel1-x86_64-arm-none-eabi + - gcc_ver: 12 + gcc_download_url: https://developer.arm.com/-/media/Files/downloads/gnu/12.2.rel1/binrel/arm-gnu-toolchain-12.2.rel1-x86_64-arm-none-eabi.tar.xz + gcc_extract_dir: arm-gnu-toolchain-12.2.rel1-x86_64-arm-none-eabi steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | cd /tmp sudo rm /etc/apt/sources.list.d/* && sudo apt-get update - sudo apt-get --no-install-recommends install -y lib32z1 ninja-build gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf - wget --tries 4 --no-check-certificate --quiet ${{ matrix.gcc_download_url }} -O gcc-arm.tar.bz2 - tar xjf gcc-arm.tar.bz2 - # use the minimal required cmake version - sudo pip3 install --system -U cmake==3.10.3 + sudo apt-get --no-install-recommends install -y build-essential lib32z1 ninja-build gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf + wget --tries 4 --no-check-certificate --quiet ${{ matrix.gcc_download_url }} -O gcc-arm + tar xf gcc-arm + sudo apt-get remove cmake + sudo apt-get purge --auto-remove cmake + wget http://www.cmake.org/files/v3.10/cmake-3.10.3.tar.gz + tar xf cmake-3.10.3.tar.gz + cd cmake-3.10.3 + ./configure + sudo make install cmake --version | grep 3.10.3 - name: Build + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" run: | export PATH=/tmp/${{ matrix.gcc_extract_dir }}/bin:$PATH script/check-arm-build gcc: name: gcc-${{ matrix.gcc_ver }} - runs-on: ubuntu-18.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: - gcc_ver: [5, 6, 7, 8, 9, 10, 11] + gcc_ver: [9, 10, 11, 12] env: CC: gcc-${{ matrix.gcc_ver }} CXX: g++-${{ matrix.gcc_ver }} steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -216,12 +283,17 @@ jobs: strategy: fail-fast: false matrix: - clang_ver: ["6.0", "7", "8", "9", "10", "11", "12", "13"] + clang_ver: ["9", "10", "11", "12", "13"] env: CC: clang-${{ matrix.clang_ver }} CXX: clang++-${{ matrix.clang_ver }} steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -246,7 +318,7 @@ jobs: strategy: fail-fast: false matrix: - clang_ver: ["6.0", "7", "8", "9", "10", "11", "12", "13"] + clang_ver: ["9", "10", "11", "12", "13"] env: CC: clang-${{ matrix.clang_ver }} CXX: clang++-${{ matrix.clang_ver }} @@ -254,7 +326,12 @@ jobs: CXXFLAGS: -m32 -Wconversion LDFLAGS: -m32 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -277,7 +354,12 @@ jobs: gn: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -308,27 +390,52 @@ jobs: CC: ${{ matrix.CC }} CXX: ${{ matrix.CXX }} steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | - rm -f '/usr/local/bin/2to3' brew update - brew install automake m4 ninja - [ ${{ matrix.CC }} != clang ] || brew install llvm + brew install automake m4 + wget --tries 4 https://github.com/ninja-build/ninja/releases/download/v1.11.1/ninja-mac.zip + unzip ninja-mac.zip && mv ninja /usr/local/bin/. - name: Build run: | export PATH=$(brew --prefix m4)/bin:$PATH script/check-posix-build script/check-simulation-build - android: - runs-on: ubuntu-20.04 + android-ndk: + name: android-ndk + runs-on: ubuntu-22.04 + container: + image: openthread/environment steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true + - name: Install unzip + run: apt update && apt install -y unzip + - name: Setup NDK + id: setup-ndk + uses: nttld/setup-ndk@v1 + with: + ndk-version: r25c + local-cache: true + - name: Build + env: + NDK: ${{ steps.setup-ndk.outputs.ndk-path }} run: | - docker run --rm -v $PWD:/build/openthread openthread/android-trusty /build/openthread/script/check-android-build + rm -rf build/ && OT_CMAKE_NINJA_TARGET="ot-daemon ot-ctl" script/cmake-build android-ndk + rm -rf build/ && OT_CMAKE_NINJA_TARGET="ot-cli" script/cmake-build android-ndk diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000000..fc4821b2bf2 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,85 @@ +# +# 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. +# + +name: "CodeQL" + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +permissions: # added using https://github.com/step-security/secure-workflows + contents: read + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp', 'python' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - name: Checkout repository + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + + - name: Bootstrap + run: | + sudo apt-get --no-install-recommends install -y ninja-build libreadline-dev libncurses-dev + + - name: Initialize CodeQL + uses: github/codeql-action/init@7df0ce34898d659f95c0c4a09eaa8d4e32ee64db # v2.2.12 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + - run: | + ./script/test build + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@7df0ce34898d659f95c0c4a09eaa8d4e32ee64db # v2.2.12 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 339bb1a09a7..46786d4669b 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -28,17 +28,22 @@ name: Docker -on: [push, pull_request] +on: + push: + branches-ignore: + - 'dependabot/**' + pull_request: + branches: + - 'main' -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +permissions: # added using https://github.com/step-security/secure-workflows + contents: read + +jobs: buildx: name: buildx-${{ matrix.docker_name }} @@ -49,7 +54,12 @@ jobs: include: - docker_name: environment steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true @@ -63,17 +73,17 @@ jobs: TAGS="--tag ${DOCKER_IMAGE}:${VERSION}" - echo ::set-output name=docker_image::${DOCKER_IMAGE} - echo ::set-output name=version::${VERSION} - echo ::set-output name=buildx_args::--platform ${DOCKER_PLATFORMS} \ + echo "docker_image=${DOCKER_IMAGE}" >> $GITHUB_OUTPUT + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "buildx_args=--platform ${DOCKER_PLATFORMS} \ --build-arg OT_GIT_REF=${{ github.sha }} \ --build-arg VERSION=${VERSION} \ --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \ --build-arg VCS_REF=${GITHUB_SHA::8} \ - ${TAGS} --file ${DOCKER_FILE} . + ${TAGS} --file ${DOCKER_FILE} ." >> $GITHUB_OUTPUT - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@4b4e9c3e2d4531116a6f8ba8e71fc6e2cb6e6c8c # v2.5.0 - name: Docker Buildx (build) run: | @@ -81,7 +91,7 @@ jobs: - name: Login to DockerHub if: success() && github.repository == 'openthread/openthread' && github.event_name != 'pull_request' - uses: docker/login-action@v1 + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a # v2.1.0 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 8f549c50514..e1b37fe67b0 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -27,24 +27,41 @@ # name: CIFuzz -on: [pull_request] + +on: + pull_request: + branches: + - 'main' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true + +permissions: # added using https://github.com/step-security/secure-workflows + contents: read + jobs: Fuzzing: runs-on: ubuntu-20.04 steps: + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + - name: Build Fuzzers - uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@c0e4bb8d15a68b7f8cc731ea75523e48a2301bcf # master with: oss-fuzz-project-name: 'openthread' dry-run: false - name: Run Fuzzers - uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@c0e4bb8d15a68b7f8cc731ea75523e48a2301bcf # master with: oss-fuzz-project-name: 'openthread' fuzz-seconds: 1800 dry-run: false - name: Upload Crash - uses: actions/upload-artifact@v1 + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: failure() with: name: artifacts diff --git a/.github/workflows/makefile-check.yml b/.github/workflows/makefile-check.yml index 37e80243cf0..6972074492e 100644 --- a/.github/workflows/makefile-check.yml +++ b/.github/workflows/makefile-check.yml @@ -28,21 +28,31 @@ name: Makefile Check -on: [push, pull_request] +on: + push: + branches-ignore: + - 'dependabot/**' + pull_request: + branches: + - 'main' -jobs: - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true + +permissions: # added using https://github.com/step-security/secure-workflows + contents: read +jobs: makefile-check: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Check diff --git a/.github/workflows/otbr.yml b/.github/workflows/otbr.yml index 6f9412b8665..9cab9ad97fe 100644 --- a/.github/workflows/otbr.yml +++ b/.github/workflows/otbr.yml @@ -28,17 +28,22 @@ name: Border Router -on: [push, pull_request] +on: + push: + branches-ignore: + - 'dependabot/**' + pull_request: + branches: + - 'main' -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +permissions: + contents: read + +jobs: backbone-router: runs-on: ubuntu-20.04 @@ -57,7 +62,7 @@ jobs: # of OMR prefix and Domain prefix is not deterministic. BORDER_ROUTING: 0 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Build OTBR Docker @@ -81,11 +86,11 @@ jobs: export CI_ENV="$(bash <(curl -s https://codecov.io/env)) -e GITHUB_ACTIONS -e COVERAGE" echo "CI_ENV=${CI_ENV}" sudo -E ./script/test cert_suite ./tests/scripts/thread-cert/backbone/*.py || (sudo chmod a+r *.log *.json *.pcap && false) - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-thread-1-3-backbone-docker path: /tmp/coverage/ - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: name: thread-1-3-backbone-results @@ -98,7 +103,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-thread-1-3-backbone path: tmp/coverage.info @@ -138,7 +143,7 @@ jobs: cert_scripts: ./tests/scripts/thread-cert/border_router/nat64/*.py packet_verification: 1 nat64: 1 - description: "nat64" + description: "nat64 openthread" - otbr_mdns: "avahi" otbr_trel: 0 cert_scripts: ./tests/scripts/thread-cert/border_router/*.py @@ -167,7 +172,7 @@ jobs: NAT64: ${{ matrix.nat64 }} MAX_JOBS: 3 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - name: Build OTBR Docker env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" @@ -190,11 +195,11 @@ jobs: export CI_ENV="$(bash <(curl -s https://codecov.io/env)) -e GITHUB_ACTIONS -e COVERAGE" echo "CI_ENV=${CI_ENV}" sudo -E ./script/test cert_suite ${{ matrix.cert_scripts }} || (sudo chmod a+r *.log *.json *.pcap && false) - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-thread-border-router-docker path: /tmp/coverage/ - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: name: thread-border-router-results @@ -207,7 +212,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-thread-border-router path: tmp/coverage.info @@ -218,13 +223,13 @@ jobs: - thread-border-router runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | sudo apt-get --no-install-recommends install -y lcov - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: path: coverage/ - name: Combine Coverage @@ -233,7 +238,7 @@ jobs: script/test combine_coverage - name: Upload Coverage continue-on-error: true - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@894ff025c7b54547a9a2a1e9f228beae737ad3c2 # v3.1.3 with: files: final.info fail_ci_if_error: true @@ -242,7 +247,7 @@ jobs: needs: upload-coverage runs-on: ubuntu-20.04 steps: - - uses: geekyeggo/delete-artifact@1-glob-support + - uses: geekyeggo/delete-artifact@54ab544f12cdb7b71613a16a2b5a37a9ade990af # v2.0.0 with: name: cov-* useGlob: true diff --git a/.github/workflows/otci.yml b/.github/workflows/otci.yml index a1e3a9b99df..46f07450e8c 100644 --- a/.github/workflows/otci.yml +++ b/.github/workflows/otci.yml @@ -28,17 +28,22 @@ name: OTCI -on: [push, pull_request] +on: + push: + branches-ignore: + - 'dependabot/**' + pull_request: + branches: + - 'main' -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +permissions: # added using https://github.com/step-security/secure-workflows + contents: read + +jobs: cli-sim: name: cli-sim VIRTUAL_TIME=${{ matrix.virtual_time }} @@ -48,25 +53,33 @@ jobs: matrix: virtual_time: [0, 1] env: - REFERENCE_DEVICE: 1 VIRTUAL_TIME: ${{ matrix.virtual_time }} REAL_DEVICE: 0 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - name: Bootstrap run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update - sudo apt-get --no-install-recommends install -y g++-multilib python3-setuptools python3-wheel + sudo apt-get --no-install-recommends install -y g++-multilib ninja-build python3-setuptools python3-wheel python3 -m pip install -r tests/scripts/thread-cert/requirements.txt + python3 -m pip install pytype adb-shell + - name: Style check + run: | + PYTHONPATH=./tests/scripts/thread-cert pytype tools/otci - name: Build run: | - ./bootstrap - make -f examples/Makefile-simulation THREAD_VERSION=1.3 DUA=1 MLR=1 BACKBONE_ROUTER=1 CSL_RECEIVER=1 + ./script/cmake-build simulation -DOT_THREAD_VERSION=1.3 -DOT_DUA=ON -DOT_MLR=ON -DOT_BACKBONE_ROUTER=ON \ + -DOT_CSL_RECEIVER=ON -DOT_SIMULATION_VIRTUAL_TIME=${VIRTUAL_TIME} - name: Install OTCI Python Library run: | - (cd tools/otci && python3 setup.py install --user) + (cd tools/otci && python3 -m pip install .) - name: Run run: | export PYTHONPATH=./tests/scripts/thread-cert/ - export OT_CLI=./output/simulation/bin/ot-cli-ftd + export OT_CLI=./build/simulation/examples/apps/cli/ot-cli-ftd python3 tools/otci/tests/test_otci.py diff --git a/.github/workflows/otns.yml b/.github/workflows/otns.yml index 46dc33c65e6..ba09fbdce58 100644 --- a/.github/workflows/otns.yml +++ b/.github/workflows/otns.yml @@ -28,7 +28,17 @@ name: OTNS -on: [push, pull_request] +on: + push: + branches-ignore: + - 'dependabot/**' + pull_request: + branches: + - 'main' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true env: COVERAGE: 1 @@ -38,28 +48,28 @@ env: MAX_NETWORK_SIZE: 999 GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" -jobs: +permissions: # added using https://github.com/step-security/secure-workflows + contents: read - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +jobs: unittests: name: Unittests - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 - - uses: actions/setup-go@v1 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 with: - go-version: '1.14' - - name: Set up Python 3.6 - uses: actions/setup-python@v1 + go-version: "1.20" + - name: Set up Python + uses: actions/setup-python@57ded4d7d5e986d7296eab16560982c6dd7c923b # v4.6.0 with: - python-version: 3.6 + python-version: "3.9" - name: Bootstrap run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update @@ -73,7 +83,7 @@ jobs: cd /tmp/otns ./script/test py-unittests ) - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: name: unittests-pcaps @@ -83,23 +93,23 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-otns-unittests path: tmp/coverage.info examples: name: Examples - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 - - uses: actions/setup-go@v1 + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 with: - go-version: '1.14' - - name: Set up Python 3.6 - uses: actions/setup-python@v1 + go-version: "1.20" + - name: Set up Python + uses: actions/setup-python@57ded4d7d5e986d7296eab16560982c6dd7c923b # v4.6.0 with: - python-version: 3.6 + python-version: "3.9" - name: Bootstrap run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update @@ -113,7 +123,7 @@ jobs: cd /tmp/otns ./script/test py-examples ) - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: name: examples-pcaps @@ -123,14 +133,14 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-otns-examples path: tmp/coverage.info stress-tests: name: Stress ${{ matrix.suite }} - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 strategy: fail-fast: false matrix: @@ -150,14 +160,19 @@ jobs: env: STRESS_LEVEL: ${{ matrix.stress_level }} steps: - - uses: actions/checkout@v2 - - uses: actions/setup-go@v1 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + - uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 with: - go-version: '1.14' - - name: Set up Python 3.6 - uses: actions/setup-python@v1 + go-version: "1.20" + - name: Set up Python + uses: actions/setup-python@57ded4d7d5e986d7296eab16560982c6dd7c923b # v4.6.0 with: - python-version: 3.6 + python-version: "3.9" - name: Bootstrap run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update @@ -171,7 +186,7 @@ jobs: cd /tmp/otns ./script/test stress-tests ${{ matrix.suite }} ) - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: name: stress-tests-${{ matrix.suite }}-pcaps @@ -181,7 +196,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-otns-stress-tests-${{ matrix.suite }} path: tmp/coverage.info @@ -191,13 +206,18 @@ jobs: - unittests - examples - stress-tests - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - name: Bootstrap run: | sudo apt-get --no-install-recommends install -y lcov - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: path: coverage/ - name: Upload Coverage diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index 2ec9fa574f3..ded9e81def7 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -28,25 +28,35 @@ name: POSIX -on: [push, pull_request] +on: + push: + branches-ignore: + - 'dependabot/**' + pull_request: + branches: + - 'main' -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +permissions: + contents: read + +jobs: expects-linux: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 env: CFLAGS: -DCLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER=1 -DOPENTHREAD_CONFIG_MLE_MAX_CHILDREN=15 CXXFLAGS: -DCLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER=1 -DOPENTHREAD_CONFIG_MLE_MAX_CHILDREN=15 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - name: Bootstrap run: | sudo apt-get --no-install-recommends install -y expect ninja-build lcov socat @@ -55,13 +65,17 @@ jobs: ulimit -c unlimited ./script/test prepare_coredump_upload OT_OPTIONS='-DOT_READLINE=OFF -DOT_FULL_LOGS=ON -DOT_LOG_OUTPUT=PLATFORM_DEFINED' VIRTUAL_TIME=0 OT_NODE_TYPE=rcp ./script/test build expect + - name: Run ot-fct + run: | + OT_CMAKE_NINJA_TARGET="ot-fct" script/cmake-build posix + tests/scripts/expect/ot-fct.exp - name: Check Crash if: ${{ failure() }} run: | CRASHED=$(./script/test check_crash | tail -1) [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed." echo "CRASHED_RCP=$CRASHED" >> $GITHUB_ENV - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() && env.CRASHED_RCP == '1' }} with: name: core-expect-rcp @@ -70,19 +84,19 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-expects-linux-1 path: tmp/coverage.info - name: Run TUN Mode run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update - sudo apt-get install --no-install-recommends -y dnsmasq bind9-host ntp - sudo systemctl start dnsmasq ntp - host ipv6.google.com 127.0.0.1 - echo 'listen-address=::1' | sudo tee /etc/dnsmasq.conf echo 0 | sudo tee /proc/sys/net/ipv6/conf/all/disable_ipv6 - sudo systemctl restart dnsmasq + sudo apt-get install --no-install-recommends -y bind9-host ntp socat + sudo systemctl restart ntp + sudo socat 'UDP6-LISTEN:53,fork,reuseaddr,bind=[::1]' UDP:127.0.0.53:53 & + socat 'TCP6-LISTEN:2000,fork,reuseaddr' TCP:127.0.0.53:53 & + host ipv6.google.com 127.0.0.53 host ipv6.google.com ::1 ulimit -c unlimited ./script/test prepare_coredump_upload @@ -93,13 +107,13 @@ jobs: CRASHED=$(./script/test check_crash | tail -1) [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed." echo "CRASHED_TUN=$CRASHED" >> $GITHUB_ENV - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() && env.CRASHED_TUN == '1' }} with: name: core-expect-linux path: | ./ot-core-dump/* - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: name: syslog-expect-linux @@ -107,7 +121,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-expects-linux-2 path: tmp/coverage.info @@ -117,37 +131,37 @@ jobs: env: COVERAGE: 1 PYTHONUNBUFFERED: 1 - READLINE: readline - REFERENCE_DEVICE: 1 THREAD_VERSION: 1.1 VIRTUAL_TIME: 1 - VIRTUAL_TIME_UART: 1 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update - sudo apt-get --no-install-recommends install -y libreadline6-dev python3-setuptools python3-wheel lcov + sudo apt-get --no-install-recommends install -y lcov ninja-build python3-setuptools python3-wheel python3 -m pip install -r tests/scripts/thread-cert/requirements.txt - name: Build run: | - ./bootstrap - make -f examples/Makefile-simulation - make -f src/posix/Makefile-posix + OT_NODE_TYPE=rcp ./script/test build - name: Run run: | - VERBOSE=1 OT_CLI_PATH="$PWD/output/posix/bin/ot-cli -v" RADIO_DEVICE="$PWD/output/simulation/bin/ot-rcp" make -f src/posix/Makefile-posix check - - uses: actions/upload-artifact@v2 + MAX_JOBS=$(getconf _NPROCESSORS_ONLN) ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: name: thread-cert - path: build/posix/tests/scripts/thread-cert + path: ot_testing - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-thread-cert path: tmp/coverage.info @@ -157,7 +171,12 @@ jobs: env: COVERAGE: 1 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -174,7 +193,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-ncp-rcp-migrate path: tmp/coverage.info @@ -191,7 +210,12 @@ jobs: OT_DAEMON: ${{ matrix.OT_DAEMON }} OT_READLINE: 'readline' steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - name: Bootstrap run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update @@ -219,7 +243,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-pty-linux-${{ matrix.DAEMON }} path: tmp/coverage.info @@ -235,10 +259,24 @@ jobs: OT_DAEMON: ${{ matrix.OT_DAEMON }} OT_READLINE: 'off' steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - name: Bootstrap run: | - rm -f '/usr/local/bin/2to3' + rm -f /usr/local/bin/2to3 + rm -f /usr/local/bin/2to3-3.11 + rm -f /usr/local/bin/idle3 + rm -f /usr/local/bin/idle3.11 + rm -f /usr/local/bin/pydoc3 + rm -f /usr/local/bin/pydoc3.11 + rm -f /usr/local/bin/python3 + rm -f /usr/local/bin/python3.11 + rm -f /usr/local/bin/python3-config + rm -f /usr/local/bin/python3.11-config brew update brew install ninja socat - name: Build @@ -251,7 +289,12 @@ jobs: rcp-stack-reset: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - name: Bootstrap env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" @@ -267,7 +310,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-rcp-stack-reset path: tmp/coverage.info @@ -280,20 +323,25 @@ jobs: - thread-cert runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | sudo apt-get --no-install-recommends install -y lcov - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: path: coverage/ - name: Combine Coverage run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@894ff025c7b54547a9a2a1e9f228beae737ad3c2 # v3.1.3 with: files: final.info fail_ci_if_error: true @@ -302,7 +350,12 @@ jobs: needs: upload-coverage runs-on: ubuntu-20.04 steps: - - uses: geekyeggo/delete-artifact@1-glob-support + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: geekyeggo/delete-artifact@54ab544f12cdb7b71613a16a2b5a37a9ade990af # v2.0.0 with: name: cov-* useGlob: true diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml new file mode 100644 index 00000000000..3d982a4325d --- /dev/null +++ b/.github/workflows/scorecards.yml @@ -0,0 +1,100 @@ +# +# 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. +# + +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecards supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '33 12 * * 0' + push: + branches: [ "main" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecards analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read + + steps: + - name: "Checkout code" + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@80e868c13c90f172d68d1f4501dee99e2479f7af # v2.1.3 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecards on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@7df0ce34898d659f95c0c4a09eaa8d4e32ee64db # v2.1.27 + with: + sarif_file: results.sarif diff --git a/.github/workflows/simulation-1.1.yml b/.github/workflows/simulation-1.1.yml index 3754c9d3cc7..d996c0e6320 100644 --- a/.github/workflows/simulation-1.1.yml +++ b/.github/workflows/simulation-1.1.yml @@ -28,17 +28,22 @@ name: Simulation 1.1 -on: [push, pull_request] +on: + push: + branches-ignore: + - 'dependabot/**' + pull_request: + branches: + - 'main' -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +permissions: + contents: read + +jobs: distcheck: runs-on: ubuntu-20.04 @@ -49,7 +54,12 @@ jobs: THREAD_VERSION: 1.1 VIRTUAL_TIME: 1 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -75,7 +85,12 @@ jobs: VIRTUAL_TIME: 1 MULTIPLY: 3 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -92,7 +107,7 @@ jobs: - name: Run run: | ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: name: packet-verification-pcaps @@ -102,7 +117,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-packet-verification path: tmp/coverage.info @@ -118,30 +133,34 @@ jobs: THREAD_VERSION: 1.1 VIRTUAL_TIME: 1 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update - sudo apt-get --no-install-recommends install -y g++-multilib python3-setuptools python3-wheel lcov + sudo apt-get --no-install-recommends install -y lcov ninja-build g++-multilib python3-setuptools python3-wheel python3 -m pip install -r tests/scripts/thread-cert/requirements.txt - name: Build run: | - ./bootstrap - make -f examples/Makefile-simulation + ./script/test build - name: Run run: | - VERBOSE=1 make -f examples/Makefile-simulation check - - uses: actions/upload-artifact@v2 + ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: name: cli-ftd-thread-cert - path: build/simulation/tests/scripts/thread-cert + path: ot_testing - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-cli-ftd path: tmp/coverage.info @@ -164,30 +183,34 @@ jobs: VIRTUAL_TIME: 1 MESSAGE_USE_HEAP: ${{ matrix.message_use_heap }} steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update - sudo apt-get --no-install-recommends install -y g++-multilib python3-setuptools python3-wheel lcov + sudo apt-get --no-install-recommends install -y lcov ninja-build g++-multilib python3-setuptools python3-wheel python3 -m pip install -r tests/scripts/thread-cert/requirements.txt - name: Build run: | - ./bootstrap - make -f examples/Makefile-simulation + ./script/test build - name: Run run: | - VERBOSE=1 make -f examples/Makefile-simulation check - - uses: actions/upload-artifact@v2 + ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: name: cli-mtd-thread-cert - path: build/simulation/tests/scripts/thread-cert + path: ot_testing - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-cli-mtd-${{ matrix.message_use_heap }} path: tmp/coverage.info @@ -201,45 +224,53 @@ jobs: COVERAGE: 1 REFERENCE_DEVICE: 1 THREAD_VERSION: 1.1 - TIME_SYNC: 1 VIRTUAL_TIME: 1 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update - sudo apt-get --no-install-recommends install -y g++-multilib python3-setuptools python3-wheel lcov + sudo apt-get --no-install-recommends install -y g++-multilib lcov ninja-build python3-setuptools python3-wheel python3 -m pip install -r tests/scripts/thread-cert/requirements.txt - name: Build run: | - ./bootstrap - make -f examples/Makefile-simulation + OT_OPTIONS="-DOT_TIME_SYNC=ON" ./script/test build - name: Run run: | - VERBOSE=1 make -f examples/Makefile-simulation check - - uses: actions/upload-artifact@v2 + ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: name: cli-time-sync-thread-cert - path: build/simulation/tests/scripts/thread-cert + path: ot_testing - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-cli-time-sync path: tmp/coverage.info expects: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 env: CFLAGS: -DCLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER=1 -DOPENTHREAD_CONFIG_MLE_MAX_CHILDREN=15 CXXFLAGS: -DCLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER=1 -DOPENTHREAD_CONFIG_MLE_MAX_CHILDREN=15 THREAD_VERSION: 1.1 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - name: Bootstrap run: | sudo apt-get --no-install-recommends install -y expect ninja-build lcov socat @@ -254,7 +285,7 @@ jobs: CRASHED=$(./script/test check_crash | tail -1) [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed." echo "CRASHED_CLI=$CRASHED" >> $GITHUB_ENV - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() && env.CRASHED_CLI == '1' }} with: name: core-expect-cli @@ -263,7 +294,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-expects path: tmp/coverage.info @@ -273,7 +304,12 @@ jobs: env: THREAD_VERSION: 1.1 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -306,7 +342,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-ot-commissioner path: tmp/coverage.info @@ -315,35 +351,37 @@ jobs: runs-on: ubuntu-20.04 env: COVERAGE: 1 - MULTIPLE_INSTANCE: 1 - REFERENCE_DEVICE: 1 THREAD_VERSION: 1.1 VIRTUAL_TIME: 1 CXXFLAGS: "-DOPENTHREAD_CONFIG_LOG_PREPEND_UPTIME=0" steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | - sudo apt-get --no-install-recommends install -y python3-setuptools python3-wheel lcov + sudo apt-get --no-install-recommends install -y lcov ninja-build python3-setuptools python3-wheel python3 -m pip install -r tests/scripts/thread-cert/requirements.txt - name: Build run: | - ./bootstrap - make -f examples/Makefile-simulation + OT_OPTIONS="-DOT_MULTIPLE_INSTANCE=ON" ./script/test build - name: Run run: | - VERBOSE=1 make -f examples/Makefile-simulation check - - uses: actions/upload-artifact@v2 + ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: - name: multiple-instance-thread-cert + name: ot_testing path: build/simulation/tests/scripts/thread-cert - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-multiple-instance path: tmp/coverage.info @@ -359,20 +397,25 @@ jobs: - multiple-instance runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | sudo apt-get --no-install-recommends install -y lcov - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: path: coverage/ - name: Combine Coverage run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@894ff025c7b54547a9a2a1e9f228beae737ad3c2 # v3.1.3 with: files: final.info fail_ci_if_error: true @@ -381,7 +424,12 @@ jobs: needs: upload-coverage runs-on: ubuntu-20.04 steps: - - uses: geekyeggo/delete-artifact@1-glob-support + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: geekyeggo/delete-artifact@54ab544f12cdb7b71613a16a2b5a37a9ade990af # v2.0.0 with: name: cov-* useGlob: true diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.2.yml index 26166466d98..cbf2c9ee132 100644 --- a/.github/workflows/simulation-1.2.yml +++ b/.github/workflows/simulation-1.2.yml @@ -28,17 +28,22 @@ name: Simulation 1.3 -on: [push, pull_request] +on: + push: + branches-ignore: + - 'dependabot/**' + pull_request: + branches: + - 'main' -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +permissions: # added using https://github.com/step-security/secure-workflows + contents: read + +jobs: thread-1-3: name: thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }} @@ -51,6 +56,7 @@ jobs: THREAD_VERSION: 1.3 VIRTUAL_TIME: 1 INTER_OP: 1 + INTER_OP_BBR: 1 CC: ${{ matrix.compiler.c }} CXX: ${{ matrix.compiler.cxx }} strategy: @@ -59,7 +65,12 @@ jobs: compiler: [{c: "gcc", cxx: "g++", gcov: "gcc"}, { c: "clang-10", cxx: "clang++-10", gcov: "llvm"}] arch: ["m32", "m64"] steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -84,12 +95,12 @@ jobs: CRASHED=$(./script/test check_crash | tail -1) [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed." echo "CRASHED=$CRASHED" >> $GITHUB_ENV - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: name: thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }}-pcaps path: "*.pcap" - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() && env.CRASHED == '1' }} with: name: core-packet-verification-thread-1-3 @@ -98,7 +109,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage "${{ matrix.compiler.gcov }}" - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-thread-1-3-${{ matrix.compiler.c }}-${{ matrix.arch }} path: tmp/coverage.info @@ -115,7 +126,12 @@ jobs: INTER_OP: 1 INTER_OP_BBR: 0 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -143,14 +159,14 @@ jobs: CRASHED=$(./script/test check_crash | tail -1) [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed." echo "CRASHED=$CRASHED" >> $GITHUB_ENV - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: name: packet-verification-low-power-pcaps path: | *.pcap *.json - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() && env.CRASHED == '1' }} with: name: core-packet-verification-low-power @@ -159,7 +175,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-packet-verification-low-power path: tmp/coverage.info @@ -171,9 +187,15 @@ jobs: VIRTUAL_TIME: 1 PACKET_VERIFICATION: 1 THREAD_VERSION: 1.3 + INTER_OP_BBR: 1 MULTIPLY: 3 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -190,7 +212,7 @@ jobs: - name: Run run: | ./script/test cert_suite ./tests/scripts/thread-cert/Cert_*.py ./tests/scripts/thread-cert/test_*.py - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: name: packet-verification-1.1-on-1.3-pcaps @@ -200,7 +222,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-packet-verification-1-1-on-1-3 path: tmp/coverage.info @@ -212,7 +234,12 @@ jobs: THREAD_VERSION: 1.3 VIRTUAL_TIME: 0 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -229,7 +256,7 @@ jobs: CRASHED=$(./script/test check_crash | tail -1) [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed." echo "CRASHED=$CRASHED" >> $GITHUB_ENV - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() && env.CRASHED == '1' }} with: name: core-expect-1-3 @@ -238,7 +265,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-expects path: tmp/coverage.info @@ -255,7 +282,12 @@ jobs: VIRTUAL_TIME: 1 INTER_OP: 1 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -282,12 +314,12 @@ jobs: CRASHED=$(./script/test check_crash | tail -1) [[ $CRASHED -eq "1" ]] && echo "Crashed!" || echo "Not crashed." echo "CRASHED=$CRASHED" >> $GITHUB_ENV - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() }} with: name: thread-1-3-posix-pcaps path: "*.pcap" - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: ${{ failure() && env.CRASHED == '1' }} with: name: core-thread-1-3-posix @@ -296,7 +328,7 @@ jobs: - name: Generate Coverage run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 with: name: cov-thread-1-3-posix path: tmp/coverage.info @@ -310,20 +342,25 @@ jobs: - thread-1-3-posix runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | sudo apt-get --no-install-recommends install -y lcov - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: path: coverage/ - name: Combine Coverage run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@894ff025c7b54547a9a2a1e9f228beae737ad3c2 # v3.1.3 with: files: final.info fail_ci_if_error: true @@ -332,7 +369,12 @@ jobs: needs: upload-coverage runs-on: ubuntu-20.04 steps: - - uses: geekyeggo/delete-artifact@1-glob-support + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: geekyeggo/delete-artifact@54ab544f12cdb7b71613a16a2b5a37a9ade990af # v2.0.0 with: name: cov-* useGlob: true diff --git a/.github/workflows/size.yml b/.github/workflows/size.yml index 763fb4584ad..71afb0d2b68 100644 --- a/.github/workflows/size.yml +++ b/.github/workflows/size.yml @@ -28,22 +28,32 @@ name: Size -on: [push, pull_request] +on: + push: + branches-ignore: + - 'dependabot/**' + pull_request: + branches: + - 'main' -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +permissions: + contents: read + +jobs: size-report: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 - name: Bootstrap if: "github.event_name == 'push'" run: | diff --git a/.github/workflows/toranj.yml b/.github/workflows/toranj.yml index 221913fded4..83cf71254f8 100644 --- a/.github/workflows/toranj.yml +++ b/.github/workflows/toranj.yml @@ -28,30 +28,42 @@ name: Toranj -on: [push, pull_request] +on: + push: + branches-ignore: + - 'dependabot/**' + pull_request: + branches: + - 'main' -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +permissions: + contents: read + +jobs: toranj-ncp: name: toranj-ncp-${{ matrix.TORANJ_RADIO }} - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 strategy: fail-fast: false matrix: - TORANJ_RADIO: ['15.4', 'trel', 'multi'] + TORANJ_RADIO: ['15.4'] env: COVERAGE: 1 TORANJ_RADIO : ${{ matrix.TORANJ_RADIO }} + TORANJ_NCP : 1 + TORANJ_EVENT_NAME: ${{ github.event_name }} steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -59,45 +71,28 @@ jobs: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update - sudo apt-get --no-install-recommends install -y dbus libdbus-1-dev - sudo apt-get --no-install-recommends install -y autoconf-archive - sudo apt-get --no-install-recommends install -y bsdtar - sudo apt-get --no-install-recommends install -y libtool - sudo apt-get --no-install-recommends install -y libglib2.0-dev - sudo apt-get --no-install-recommends install -y libboost-dev libboost-signals-dev - sudo apt-get --no-install-recommends install -y lcov - - script/git-tool clone --depth=1 --branch=master https://github.com/openthread/wpantund.git - cd wpantund - ./bootstrap.sh - ./configure - sudo make -j2 - sudo make install - name: Build & Run run: | top_builddir=$(pwd)/build/toranj ./tests/toranj/start.sh - - name: Generate Coverage - if: "matrix.TORANJ_RADIO != 'multi'" - run: | - ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 - if: "matrix.TORANJ_RADIO != 'multi'" - with: - name: cov-toranj-ncp-${{ matrix.TORANJ_RADIO }} - path: tmp/coverage.info + toranj-cli: name: toranj-cli-${{ matrix.TORANJ_RADIO }} - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 strategy: matrix: - TORANJ_RADIO: ['15.4'] + TORANJ_RADIO: ['15.4', 'trel', 'multi'] env: COVERAGE: 1 TORANJ_RADIO : ${{ matrix.TORANJ_RADIO }} TORANJ_CLI: 1 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap @@ -105,7 +100,7 @@ jobs: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" run: | sudo rm /etc/apt/sources.list.d/* && sudo apt-get update - sudo apt-get --no-install-recommends install -y lcov + sudo apt-get --no-install-recommends install -y ninja-build lcov python3 -m pip install -r tests/scripts/thread-cert/requirements.txt - name: Build & Run run: | @@ -114,32 +109,62 @@ jobs: if: "matrix.TORANJ_RADIO != 'multi'" run: | ./script/test generate_coverage gcc - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 if: "matrix.TORANJ_RADIO != 'multi'" with: name: cov-toranj-cli-${{ matrix.TORANJ_RADIO }} path: tmp/coverage.info + toranj-unittest: + name: toranj-unittest + runs-on: ubuntu-20.04 + steps: + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + with: + submodules: true + - name: Bootstrap + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + run: | + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get --no-install-recommends install -y clang-10 clang++-10 ninja-build python3-setuptools python3-wheel llvm lcov + sudo apt-get --no-install-recommends install -y g++-multilib libreadline-dev:i386 libncurses-dev:i386 + python3 -m pip install -r tests/scripts/thread-cert/requirements.txt + - name: Build & Run + run: | + ./tests/toranj/build.sh cmake + ninja test + upload-coverage: needs: - - toranj-ncp - toranj-cli - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Bootstrap run: | sudo apt-get --no-install-recommends install -y lcov - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: path: coverage/ - name: Combine Coverage run: | script/test combine_coverage - name: Upload Coverage - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@894ff025c7b54547a9a2a1e9f228beae737ad3c2 # v3.1.3 with: files: final.info fail_ci_if_error: true @@ -148,7 +173,12 @@ jobs: needs: upload-coverage runs-on: ubuntu-20.04 steps: - - uses: geekyeggo/delete-artifact@1-glob-support + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: geekyeggo/delete-artifact@54ab544f12cdb7b71613a16a2b5a37a9ade990af # v2.0.0 with: name: cov-* useGlob: true diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml new file mode 100644 index 00000000000..a92c629a8ba --- /dev/null +++ b/.github/workflows/unit.yml @@ -0,0 +1,137 @@ +# +# Copyright (c) 2023, 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. +# + +name: Unit + +on: + push: + branches-ignore: + - 'dependabot/**' + pull_request: + branches: + - 'main' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true + +permissions: # added using https://github.com/step-security/secure-workflows + contents: read + +jobs: + + tcplp-buffering: + runs-on: ubuntu-20.04 + steps: + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + with: + submodules: true + - name: Build + run: make -C third_party/tcplp/lib/test/ + - name: Run + run: third_party/tcplp/lib/test/test_all + + unit-tests: + runs-on: ubuntu-20.04 + env: + COVERAGE: 1 + steps: + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + with: + submodules: true + - name: Bootstrap + run: | + sudo rm /etc/apt/sources.list.d/* && sudo apt-get update + sudo apt-get --no-install-recommends install -y ninja-build lcov + - name: Build Simulation + run: ./script/cmake-build simulation + - name: Test Simulation + run: cd build/simulation && ninja test + - name: Build POSIX + run: ./script/cmake-build posix + - name: Test POSIX + run: cd build/posix && ninja test + - name: Generate Coverage + run: | + ./script/test generate_coverage gcc + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2 + with: + name: cov-unit-tests + path: tmp/coverage.info + + + upload-coverage: + needs: unit-tests + runs-on: ubuntu-20.04 + steps: + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 + with: + submodules: true + - name: Bootstrap + run: | + sudo apt-get --no-install-recommends install -y lcov + - uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 + with: + path: coverage/ + - name: Combine Coverage + run: | + script/test combine_coverage + - name: Upload Coverage + uses: codecov/codecov-action@894ff025c7b54547a9a2a1e9f228beae737ad3c2 # v3.1.3 + with: + files: final.info + fail_ci_if_error: true + + delete-coverage-artifacts: + needs: upload-coverage + runs-on: ubuntu-20.04 + steps: + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: geekyeggo/delete-artifact@54ab544f12cdb7b71613a16a2b5a37a9ade990af # v2.0.0 + with: + name: cov-* + useGlob: true diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index 37b110ca8ed..47828d36b46 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -28,21 +28,28 @@ name: API Version -on: [pull_request] +on: + pull_request: + branches: + - 'main' -jobs: - cancel-previous-runs: - runs-on: ubuntu-20.04 - steps: - - uses: rokroskar/workflow-run-cleanup-action@master - env: - GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" - if: "github.ref != 'refs/heads/main'" +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || (github.repository == 'openthread/openthread' && github.run_id) || github.ref }} + cancel-in-progress: true + +permissions: # added using https://github.com/step-security/secure-workflows + contents: read +jobs: api-version: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - name: Harden Runner + uses: step-security/harden-runner@6b3083af2869dc3314a0257a42f4af696cc79ba3 # v2.3.1 + with: + egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs + + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: true - name: Check diff --git a/CMakeLists.txt b/CMakeLists.txt index 642332f5e72..15659e86c5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ file(READ .default-version OT_DEFAULT_VERSION) string(STRIP ${OT_DEFAULT_VERSION} OT_DEFAULT_VERSION) project(openthread VERSION ${OT_DEFAULT_VERSION}) +include(CTest) option(OT_BUILD_EXECUTABLES "Build executables" ON) option(OT_COVERAGE "enable coverage" OFF) @@ -40,6 +41,9 @@ set(OT_EXTERNAL_MBEDTLS "" CACHE STRING "Specify external mbedtls library") option(OT_MBEDTLS_THREADING "enable mbedtls threading" OFF) add_library(ot-config INTERFACE) +add_library(ot-config-ftd INTERFACE) +add_library(ot-config-mtd INTERFACE) +add_library(ot-config-radio INTERFACE) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD 11) set(CMAKE_C_STANDARD 99) @@ -103,13 +107,15 @@ endif() message(STATUS "Package Version: ${OT_PACKAGE_VERSION}") 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") +set_property(CACHE OT_THREAD_VERSION PROPERTY STRINGS "1.1" "1.2" "1.3" "1.3.1") 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") +elseif(${OT_THREAD_VERSION} EQUAL "1.3.1") + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_3_1") else() message(FATAL_ERROR "Thread version unknown: ${OT_THREAD_VERSION}") endif() @@ -169,6 +175,7 @@ list(APPEND OT_PUBLIC_INCLUDES ${PROJECT_SOURCE_DIR}/include) if(OT_PLATFORM STREQUAL "posix") target_include_directories(ot-config INTERFACE ${PROJECT_SOURCE_DIR}/src/posix/platform) + target_compile_definitions(ot-config INTERFACE OPENTHREAD_PLATFORM_POSIX=1) add_subdirectory("${PROJECT_SOURCE_DIR}/src/posix/platform") elseif(OT_PLATFORM STREQUAL "external") # skip in this case @@ -201,11 +208,8 @@ endif() add_subdirectory(src) add_subdirectory(third_party EXCLUDE_FROM_ALL) -if(OT_PLATFORM STREQUAL "simulation") - enable_testing() -endif() - add_subdirectory(tests) +add_subdirectory(tools) add_custom_target(print-ot-config ALL COMMAND ${CMAKE_COMMAND} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2e99d4d4596..ae564f22725 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -109,7 +109,7 @@ This will open up a text editor where you can specify which commits to squash. #### Coding Conventions and Style -OpenThread uses and enforces the [OpenThread Coding Conventions and Style](STYLE_GUIDE.md) on all code, except for code located in [third_party](third_party). Use `script/make-pretty` and `script/make-pretty check` to automatically reformat code and check for code-style compliance, respectively. OpenThread currently requires [clang-format v9.0.0](https://releases.llvm.org/download.html#9.0.0) for C/C++ and [yapf v0.31.0](https://github.com/google/yapf) for Python. +OpenThread uses and enforces the [OpenThread Coding Conventions and Style](STYLE_GUIDE.md) on all code, except for code located in [third_party](third_party). Use `script/make-pretty` and `script/make-pretty check` to automatically reformat code and check for code-style compliance, respectively. OpenThread currently requires [clang-format v14.0.0](https://releases.llvm.org/download.html#14.0.0) for C/C++ and [yapf v0.31.0](https://github.com/google/yapf) for Python. As part of the cleanup process, you should also run `script/make-pretty check` to ensure that your code passes the baseline code style checks. diff --git a/NOTICE b/NOTICE index 94a2d719f96..04cdb0a7d7e 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ -OpenThread is an open source implementation of the Thread 1.2.0 Final Specification. -The Thread 1.2.0 Final Specification is promulgated by the Thread Group. The Thread +OpenThread is an open source implementation of the Thread 1.3.0 Final Specification. +The Thread 1.3.0 Final Specification is promulgated by the Thread Group. The Thread Group is a non-profit organization formed for the purposes of defining one or more specifications, best practices, reference architectures, implementation guidelines and certification programs to promote the availability of compliant @@ -7,10 +7,10 @@ implementations of the Thread protocol. Information on becoming a Member, includ information about the benefits thereof, can be found at http://threadgroup.org. OpenThread is not affiliated with or endorsed by the Thread Group. Implementation -of this OpenThread code does not assure compliance with the Thread 1.2.0 Final +of this OpenThread code does not assure compliance with the Thread 1.3.0 Final Specification and does not convey the right to identify any final product as Thread certified. Members of the Thread Group may hold patents and other intellectual -property rights relating to the Thread 1.2.0 Final Specification, ownership and +property rights relating to the Thread 1.3.0 Final Specification, ownership and licenses of which are subject to the Thread Group’s IP Policies, and not this license. The included copyright to the OpenThread code is subject to the license in the diff --git a/README.md b/README.md index 65745884572..0c4a4f58aec 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![OpenThread][ot-logo]][ot-repo] [![Build][ot-gh-action-build-svg]][ot-gh-action-build] [![Simulation][ot-gh-action-simulation-svg]][ot-gh-action-simulation] [![Docker][ot-gh-action-docker-svg]][ot-gh-action-docker] [![Language grade: C/C++][ot-lgtm-svg]][ot-lgtm] [![Coverage Status][ot-codecov-svg]][ot-codecov] +[![OpenThread][ot-logo]][ot-repo] [![Build][ot-gh-action-build-svg]][ot-gh-action-build] [![Simulation][ot-gh-action-simulation-svg]][ot-gh-action-simulation] [![Docker][ot-gh-action-docker-svg]][ot-gh-action-docker] [![Coverage Status][ot-codecov-svg]][ot-codecov] --- @@ -10,7 +10,7 @@ OpenThread released by Google is... ARMCascodaEspressifGoogleInfineonNordicNXPQorvoQualcommSamsungSilicon LabsSTMicroelectronicsSynopsysTelink SemiconductorTexas InstrumentsZephyr Project +AmazonARMCascodaEeroEspressifGoogleInfineonMMB NetworksNabu CasaNanoleafNordicNXPQorvoQualcommSamsungSilicon LabsSTMicroelectronicsSynopsysTelink SemiconductorTexas InstrumentsZephyr Project # Getting started @@ -54,10 +52,6 @@ We would love for you to contribute to OpenThread and help make it even better t Contributors are required to abide by our [Code of Conduct](https://github.com/openthread/openthread/blob/main/CODE_OF_CONDUCT.md) and [Coding Conventions and Style Guide](https://github.com/openthread/openthread/blob/main/STYLE_GUIDE.md). -# Versioning - -OpenThread follows the [Semantic Versioning guidelines](http://semver.org/) for release cycle transparency and to maintain backwards compatibility. OpenThread's versioning is independent of the Thread protocol specification version but will clearly indicate which version of the specification it currently supports. - # License OpenThread is released under the [BSD 3-Clause license](https://github.com/openthread/openthread/blob/main/LICENSE). See the [`LICENSE`](https://github.com/openthread/openthread/blob/main/LICENSE) file for more information. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..455cf9a4629 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1 @@ +To report a security issue, please use [https://g.co/vulnz](https://g.co/vulnz). We use g.co/vulnz for our intake, and do coordination and disclosure here on GitHub (including using GitHub Security Advisory). The Google Security Team will respond within 5 working days of your report on g.co/vulnz. diff --git a/STYLE_GUIDE.md b/STYLE_GUIDE.md index d8ea62496dd..c409c32bd52 100644 --- a/STYLE_GUIDE.md +++ b/STYLE_GUIDE.md @@ -116,7 +116,7 @@ - OpenThread uses `script/make-pretty` to reformat code and enforce code format and style. `script/make-pretty check` build target is included in OpenThread's continuous integration and must pass before a pull request is merged. -- `script/make-pretty` requires [clang-format v9.0.0](https://releases.llvm.org/download.html#9.0.0) for C/C++ and [yapf v0.31.0](https://github.com/google/yapf) for Python. +- `script/make-pretty` requires [clang-format v14.0.0](https://releases.llvm.org/download.html#14.0.0) for C/C++ and [yapf v0.31.0](https://github.com/google/yapf) for Python. ### File Names diff --git a/configure.ac b/configure.ac index 8c0c7fccacf..dedd85dda0a 100644 --- a/configure.ac +++ b/configure.ac @@ -804,14 +804,14 @@ AC_MSG_CHECKING([whether to build examples]) AC_ARG_WITH(examples, [AS_HELP_STRING([--with-examples=TARGET], - [Build example applications for one of: simulation, cc2538 @<:@default=no@:>@. + [Build example applications for one of: simulation @<:@default=no@:>@. Note that building example applications also builds the associated OpenThread platform libraries and any third_party libraries needed to support the examples.])], [ case "${with_examples}" in no) ;; - simulation|cc2538) + simulation) ;; *) AC_MSG_RESULT(ERROR) @@ -824,7 +824,6 @@ AC_ARG_WITH(examples, AM_CONDITIONAL([OPENTHREAD_ENABLE_EXAMPLES], [test ${with_examples} != "no"]) AM_CONDITIONAL([OPENTHREAD_EXAMPLES_SIMULATION],[test "${with_examples}" = "simulation"]) -AM_CONDITIONAL([OPENTHREAD_EXAMPLES_CC2538], [test "${with_examples}" = "cc2538"]) AM_COND_IF([OPENTHREAD_EXAMPLES_SIMULATION], CPPFLAGS="${CPPFLAGS} -DOPENTHREAD_EXAMPLES_SIMULATION=1", CPPFLAGS="${CPPFLAGS} -DOPENTHREAD_EXAMPLES_SIMULATION=0") @@ -845,11 +844,11 @@ AC_MSG_CHECKING([whether to build platform libraries]) AC_ARG_WITH(platform, [AS_HELP_STRING([--with-platform=TARGET], - [Build OpenThread platform libraries for one of: cc2538, posix, simulation @<:@default=simulation@:>@.])], + [Build OpenThread platform libraries for one of: posix, simulation @<:@default=simulation@:>@.])], [ # Make sure the given target is valid. case "${with_platform}" in - no|cc2538|posix|simulation) + no|posix|simulation) ;; *) AC_MSG_RESULT(ERROR) @@ -880,7 +879,6 @@ AM_CONDITIONAL([OPENTHREAD_ENABLE_PLATFORM], [test ${with_platform} != "no"]) OPENTHREAD_ENABLE_PLATFORM=${with_platform} -AM_CONDITIONAL([OPENTHREAD_PLATFORM_CC2538], [test "${with_platform}" = "cc2538"]) AM_CONDITIONAL([OPENTHREAD_PLATFORM_POSIX], [test "${with_platform}" = "posix"]) AM_CONDITIONAL([OPENTHREAD_PLATFORM_SIMULATION],[test "${with_platform}" = "simulation"]) @@ -1023,7 +1021,6 @@ examples/apps/Makefile examples/apps/cli/Makefile examples/apps/ncp/Makefile examples/platforms/Makefile -examples/platforms/cc2538/Makefile examples/platforms/simulation/Makefile examples/platforms/utils/Makefile tools/Makefile @@ -1032,9 +1029,6 @@ tools/harness-thci/Makefile tools/spi-hdlc-adapter/Makefile tests/Makefile tests/fuzz/Makefile -tests/scripts/Makefile -tests/scripts/thread-cert/Makefile -tests/unit/Makefile doc/Makefile ]) diff --git a/doc/images/ot-contrib-amazon.png b/doc/images/ot-contrib-amazon.png new file mode 100644 index 00000000000..ac236ee757b Binary files /dev/null and b/doc/images/ot-contrib-amazon.png differ diff --git a/doc/images/ot-contrib-eero.png b/doc/images/ot-contrib-eero.png new file mode 100644 index 00000000000..317f20c0bc4 Binary files /dev/null and b/doc/images/ot-contrib-eero.png differ diff --git a/doc/images/ot-contrib-mmb-networks.png b/doc/images/ot-contrib-mmb-networks.png new file mode 100644 index 00000000000..e0f7dd5d4cb Binary files /dev/null and b/doc/images/ot-contrib-mmb-networks.png differ diff --git a/doc/images/ot-contrib-nabu-casa.png b/doc/images/ot-contrib-nabu-casa.png new file mode 100644 index 00000000000..f3b637beb3b Binary files /dev/null and b/doc/images/ot-contrib-nabu-casa.png differ diff --git a/doc/images/ot-contrib-nanoleaf.png b/doc/images/ot-contrib-nanoleaf.png new file mode 100644 index 00000000000..ca18d20aaa1 Binary files /dev/null and b/doc/images/ot-contrib-nanoleaf.png differ diff --git a/doc/images/ot-contrib-qorvo.png b/doc/images/ot-contrib-qorvo.png index f6815fc809f..5a9fc4854ca 100644 Binary files a/doc/images/ot-contrib-qorvo.png and b/doc/images/ot-contrib-qorvo.png differ diff --git a/doc/ot_api_doc.h b/doc/ot_api_doc.h index 3d58b682bbd..58e09a95412 100644 --- a/doc/ot_api_doc.h +++ b/doc/ot_api_doc.h @@ -53,7 +53,7 @@ * @defgroup api-net IPv6 Networking * @{ * - * @defgroup api-dns DNSv6 + * @defgroup api-dns DNS * @defgroup api-dnssd-server DNS-SD Server * @defgroup api-icmp6 ICMPv6 * @defgroup api-ip6 IPv6 @@ -109,6 +109,7 @@ * @brief This module includes functions for all Thread roles. * @defgroup api-joiner Joiner * @defgroup api-operational-dataset Operational Dataset + * @brief Includes functions for the Operational Dataset API. * @defgroup api-thread-router Router/Leader * @brief This module includes functions for Thread Routers and Leaders. * @defgroup api-server Server @@ -138,6 +139,7 @@ * @defgroup api-history-tracker History Tracker * @defgroup api-jam-detection Jam Detection * @defgroup api-logging Logging - Thread Stack + * @defgroup api-mesh-diag Mesh Diagnostics * @defgroup api-ncp Network Co-Processor * @defgroup api-network-time Network Time Synchronization * @defgroup api-random-group Random Number Generator @@ -166,6 +168,7 @@ * * @defgroup plat-alarm Alarm * @defgroup plat-crypto Crypto - Platform + * @defgroup plat-dns DNS - Platform * @defgroup plat-entropy Entropy * @defgroup plat-factory-diagnostics Factory Diagnostics - Platform * @defgroup plat-logging Logging - Platform diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index 521b7fa5ca9..6afef68f928 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -34,57 +34,194 @@ option(OT_FTD "enable FTD" ON) option(OT_MTD "enable MTD" ON) option(OT_RCP "enable RCP" ON) -option(OT_ANYCAST_LOCATOR "enable anycast locator support") -if(OT_ANYCAST_LOCATOR) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE=1") -endif() +option(OT_LINKER_MAP "generate .map files for example apps" ON) + +message(STATUS OT_APP_CLI=${OT_APP_CLI}) +message(STATUS OT_APP_NCP=${OT_APP_NCP}) +message(STATUS OT_APP_RCP=${OT_APP_RCP}) +message(STATUS OT_FTD=${OT_FTD}) +message(STATUS OT_MTD=${OT_MTD}) +message(STATUS OT_RCP=${OT_RCP}) + +set(OT_CONFIG_VALUES + "" + "ON" + "OFF" +) + +macro(ot_option name ot_config description) + # Declare an OT cmake config with `name` mapping to OPENTHREAD_CONFIG + # `ot_config`. Parameter `description` provides the help string for this + # OT cmake config. There is an optional last parameter which if provided + # determines the default value for the cmake config. If not provided + # empty string is used which will be treated as "not specified". In this + # case, the variable `name` would still be false but the related + # OPENTHREAD_CONFIG is not added in `ot-config`. + + if (${ARGC} GREATER 3) + set(${name} ${ARGN} CACHE STRING "enable ${description}") + else() + set(${name} "" CACHE STRING "enable ${description}") + endif() -option(OT_ASSERT "enable assert function OT_ASSERT()" ON) -if(OT_ASSERT) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_ASSERT_ENABLE=1") -else() - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_ASSERT_ENABLE=0") -endif() + set_property(CACHE ${name} PROPERTY STRINGS ${OT_CONFIG_VALUES}) + + string(COMPARE EQUAL "${${name}}" "" is_empty) + if (is_empty) + message(STATUS "${name}=\"\"") + elseif (${name}) + message(STATUS "${name}=ON --> ${ot_config}=1") + target_compile_definitions(ot-config INTERFACE "${ot_config}=1") + else() + message(STATUS "${name}=OFF --> ${ot_config}=0") + target_compile_definitions(ot-config INTERFACE "${ot_config}=0") + endif() +endmacro() + +ot_option(OT_15_4 OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE "802.15.4 radio link") +ot_option(OT_ANDROID_NDK OPENTHREAD_CONFIG_ANDROID_NDK_ENABLE "enable android NDK") +ot_option(OT_ANYCAST_LOCATOR OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE "anycast locator") +ot_option(OT_ASSERT OPENTHREAD_CONFIG_ASSERT_ENABLE "assert function OT_ASSERT()") +ot_option(OT_BACKBONE_ROUTER OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE "backbone router functionality") +ot_option(OT_BACKBONE_ROUTER_DUA_NDPROXYING OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE "BBR DUA ND Proxy") +ot_option(OT_BACKBONE_ROUTER_MULTICAST_ROUTING OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE "BBR MR") +ot_option(OT_BORDER_AGENT OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE "border agent") +ot_option(OT_BORDER_AGENT_ID OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE "create and save border agent ID") +ot_option(OT_BORDER_ROUTER OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE "border router") +ot_option(OT_BORDER_ROUTING OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE "border routing") +ot_option(OT_BORDER_ROUTING_COUNTERS OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE "border routing counters") +ot_option(OT_CHANNEL_MANAGER OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE "channel manager") +ot_option(OT_CHANNEL_MONITOR OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE "channel monitor") +ot_option(OT_COAP OPENTHREAD_CONFIG_COAP_API_ENABLE "coap api") +ot_option(OT_COAP_BLOCK OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE "coap block-wise transfer (RFC7959)") +ot_option(OT_COAP_OBSERVE OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE "coap observe (RFC7641)") +ot_option(OT_COAPS OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE "secure coap") +ot_option(OT_COMMISSIONER OPENTHREAD_CONFIG_COMMISSIONER_ENABLE "commissioner") +ot_option(OT_CSL_AUTO_SYNC OPENTHREAD_CONFIG_MAC_CSL_AUTO_SYNC_ENABLE "data polling based on csl") +ot_option(OT_CSL_DEBUG OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE "csl debug") +ot_option(OT_CSL_RECEIVER OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE "csl receiver") +ot_option(OT_DATASET_UPDATER OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE "dataset updater") +ot_option(OT_DHCP6_CLIENT OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE "DHCP6 client") +ot_option(OT_DHCP6_SERVER OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE "DHCP6 server") +ot_option(OT_DIAGNOSTIC OPENTHREAD_CONFIG_DIAG_ENABLE "diagnostic") +ot_option(OT_DNS_CLIENT OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE "DNS client") +ot_option(OT_DNS_CLIENT_OVER_TCP OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE "Enable dns query over tcp") +ot_option(OT_DNS_DSO OPENTHREAD_CONFIG_DNS_DSO_ENABLE "DNS Stateful Operations (DSO)") +ot_option(OT_DNS_UPSTREAM_QUERY OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE "Allow sending DNS queries to upstream") +ot_option(OT_DNSSD_SERVER OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE "DNS-SD server") +ot_option(OT_DUA OPENTHREAD_CONFIG_DUA_ENABLE "Domain Unicast Address (DUA)") +ot_option(OT_ECDSA OPENTHREAD_CONFIG_ECDSA_ENABLE "ECDSA") +ot_option(OT_EXTERNAL_HEAP OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE "external heap") +ot_option(OT_FIREWALL OPENTHREAD_POSIX_CONFIG_FIREWALL_ENABLE "firewall") +ot_option(OT_HISTORY_TRACKER OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE "history tracker") +ot_option(OT_IP6_FRAGM OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE "ipv6 fragmentation") +ot_option(OT_JAM_DETECTION OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE "jam detection") +ot_option(OT_JOINER OPENTHREAD_CONFIG_JOINER_ENABLE "joiner") +ot_option(OT_LINK_METRICS_INITIATOR OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE "link metrics initiator") +ot_option(OT_LINK_METRICS_SUBJECT OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE "link metrics subject") +ot_option(OT_LINK_RAW OPENTHREAD_CONFIG_LINK_RAW_ENABLE "link raw service") +ot_option(OT_LOG_LEVEL_DYNAMIC OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE "dynamic log level control") +ot_option(OT_MAC_FILTER OPENTHREAD_CONFIG_MAC_FILTER_ENABLE "mac filter") +ot_option(OT_MESH_DIAG OPENTHREAD_CONFIG_MESH_DIAG_ENABLE "mesh diag") +ot_option(OT_MESSAGE_USE_HEAP OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE "heap allocator for message buffers") +ot_option(OT_MLE_LONG_ROUTES OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE "MLE long routes extension (experimental)") +ot_option(OT_MLR OPENTHREAD_CONFIG_MLR_ENABLE "Multicast Listener Registration (MLR)") +ot_option(OT_MULTIPLE_INSTANCE OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE "multiple instances") +ot_option(OT_NAT64_BORDER_ROUTING OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE "border routing NAT64") +ot_option(OT_NAT64_TRANSLATOR OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE "NAT64 translator support") +ot_option(OT_NEIGHBOR_DISCOVERY_AGENT OPENTHREAD_CONFIG_NEIGHBOR_DISCOVERY_AGENT_ENABLE "neighbor discovery agent") +ot_option(OT_NETDATA_PUBLISHER OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE "Network Data publisher") +ot_option(OT_NETDIAG_CLIENT OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE "Network Diagnostic client") +ot_option(OT_OTNS OPENTHREAD_CONFIG_OTNS_ENABLE "OTNS") +ot_option(OT_PING_SENDER OPENTHREAD_CONFIG_PING_SENDER_ENABLE "ping sender" ${OT_APP_CLI}) +ot_option(OT_PLATFORM_NETIF OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE "platform netif") +ot_option(OT_PLATFORM_UDP OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE "platform UDP") +ot_option(OT_REFERENCE_DEVICE OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE "test harness reference device") +ot_option(OT_SERVICE OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE "Network Data service") +ot_option(OT_SETTINGS_RAM OPENTHREAD_SETTINGS_RAM "volatile-only storage of settings") +ot_option(OT_SLAAC OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE "SLAAC address") +ot_option(OT_SNTP_CLIENT OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE "SNTP client") +ot_option(OT_SRP_CLIENT OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE "SRP client") +ot_option(OT_SRP_SERVER OPENTHREAD_CONFIG_SRP_SERVER_ENABLE "SRP server") +ot_option(OT_TCP OPENTHREAD_CONFIG_TCP_ENABLE "TCP") +ot_option(OT_TIME_SYNC OPENTHREAD_CONFIG_TIME_SYNC_ENABLE "time synchronization service") +ot_option(OT_TREL OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE "TREL radio link for Thread over Infrastructure feature") +ot_option(OT_TX_BEACON_PAYLOAD OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE "tx beacon payload") +ot_option(OT_UDP_FORWARD OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE "UDP forward") +ot_option(OT_UPTIME OPENTHREAD_CONFIG_UPTIME_ENABLE "uptime") -option(OT_BACKBONE_ROUTER "enable backbone router functionality") -if(OT_BACKBONE_ROUTER) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE=1") - set(OT_BACKBONE_ROUTER_DUA_NDPROXYING ON CACHE BOOL "Enable DUA NDProxying by default") - set(OT_BACKBONE_ROUTER_MULTICAST_ROUTING ON CACHE BOOL "Enable Multicast Routing by default") -endif() +option(OT_DOC "Build OpenThread documentation") -option(OT_BACKBONE_ROUTER_DUA_NDPROXYING "enable Backbone Router DUA ND Proxying functionality" OFF) -if(OT_BACKBONE_ROUTER_DUA_NDPROXYING) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE=1") -else() - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE=0") +option(OT_FULL_LOGS "enable full logs") +if(OT_FULL_LOGS) + if(NOT OT_LOG_LEVEL) + message(STATUS "OT_FULL_LOGS=ON --> Setting LOG_LEVEL to DEBG") + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_LEVEL=OT_LOG_LEVEL_DEBG") + endif() + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL=1") endif() -option(OT_BACKBONE_ROUTER_MULTICAST_ROUTING "enable Backbone Router Multicast Routing functionality" OFF) -if(OT_BACKBONE_ROUTER_MULTICAST_ROUTING) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE=1") +set(OT_VENDOR_NAME "" CACHE STRING "set the vendor name config") +set_property(CACHE OT_VENDOR_NAME PROPERTY STRINGS ${OT_VENDOR_NAME_VALUES}) +string(COMPARE EQUAL "${OT_VENDOR_NAME}" "" is_empty) +if (is_empty) + message(STATUS "OT_VENDOR_NAME=\"\"") else() - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE=0") + message(STATUS "OT_VENDOR_NAME=\"${OT_VENDOR_NAME}\" --> OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME=\"${OT_VENDOR_NAME}\"") + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME=\"${OT_VENDOR_NAME}\"") endif() -option(OT_BORDER_AGENT "enable border agent support") -if(OT_BORDER_AGENT) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE=1") +set(OT_VENDOR_MODEL "" CACHE STRING "set the vendor model config") +set_property(CACHE OT_VENDOR_MODEL PROPERTY STRINGS ${OT_VENDOR_MODEL_VALUES}) +string(COMPARE EQUAL "${OT_VENDOR_MODEL}" "" is_empty) +if (is_empty) + message(STATUS "OT_VENDOR_MODEL=\"\"") +else() + message(STATUS "OT_VENDOR_MODEL=\"${OT_VENDOR_MODEL}\" --> OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL=\"${OT_VENDOR_MODEL}\"") + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL=\"${OT_VENDOR_MODEL}\"") endif() -option(OT_BORDER_ROUTER "enable border router support") -if(OT_BORDER_ROUTER) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE=1") +set(OT_VENDOR_SW_VERSION "" CACHE STRING "set the vendor sw version config") +set_property(CACHE OT_VENDOR_SW_VERSION PROPERTY STRINGS ${OT_VENDOR_SW_VERSION_VALUES}) +string(COMPARE EQUAL "${OT_VENDOR_SW_VERSION}" "" is_empty) +if (is_empty) + message(STATUS "OT_VENDOR_SW_VERSION=\"\"") +else() + message(STATUS "OT_VENDOR_SW_VERSION=\"${OT_VENDOR_SW_VERSION}\" --> OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION=\"${OT_VENDOR_SW_VERSION}\"") + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION=\"${OT_VENDOR_SW_VERSION}\"") +endif() + +set(OT_POWER_SUPPLY "" CACHE STRING "set the device power supply config") +set(OT_POWER_SUPPLY_VALUES + "" + "BATTERY" + "EXTERNAL" + "EXTERNAL_STABLE" + "EXTERNAL_UNSTABLE" +) +set_property(CACHE OT_POWER_SUPPLY PROPERTY STRINGS ${OT_POWER_SUPPLY_VALUES}) +string(COMPARE EQUAL "${OT_POWER_SUPPLY}" "" is_empty) +if (is_empty) + message(STATUS "OT_POWER_SUPPLY=\"\"") +else() + message(STATUS "OT_POWER_SUPPLY=${OT_POWER_SUPPLY} --> OPENTHREAD_CONFIG_DEVICE_POWER_SUPPLY=OT_POWER_SUPPLY_${OT_POWER_SUPPLY}") + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_DEVICE_POWER_SUPPLY=OT_POWER_SUPPLY_${OT_POWER_SUPPLY}") endif() -option(OT_BORDER_ROUTING "enable border routing support") -if(OT_BORDER_ROUTING) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE=1") +set(OT_MLE_MAX_CHILDREN "" CACHE STRING "set maximum number of children") +if(OT_MLE_MAX_CHILDREN MATCHES "^[0-9]+$") + message(STATUS "OT_MLE_MAX_CHILDREN=${OT_MLE_MAX_CHILDREN}") + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MLE_MAX_CHILDREN=${OT_MLE_MAX_CHILDREN}") +elseif(NOT OT_MLE_MAX_CHILDREN STREQUAL "") + message(FATAL_ERROR "Invalid maximum number of children: ${OT_MLE_MAX_CHILDREN}") endif() -option(OT_BORDER_ROUTING_NAT64 "enable border routing NAT64 support") -if(OT_BORDER_ROUTING_NAT64) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE=1") +set(OT_RCP_RESTORATION_MAX_COUNT "0" CACHE STRING "set max RCP restoration count") +if(OT_RCP_RESTORATION_MAX_COUNT MATCHES "^[0-9]+$") + message(STATUS "OT_RCP_RESTORATION_MAX_COUNT=${OT_RCP_RESTORATION_MAX_COUNT}") + target_compile_definitions(ot-config INTERFACE "OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT=${OT_RCP_RESTORATION_MAX_COUNT}") +else() + message(FATAL_ERROR "Invalid max RCP restoration count: ${OT_RCP_RESTORATION_MAX_COUNT}") endif() if(NOT OT_EXTERNAL_MBEDTLS) @@ -102,307 +239,21 @@ else() target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS_MANAGEMENT=0") endif() -option(OT_CHANNEL_MANAGER "enable channel manager support") -if(OT_CHANNEL_MANAGER) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE=1") -endif() - -option(OT_CHANNEL_MONITOR "enable channel monitor support") -if(OT_CHANNEL_MONITOR) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE=1") -endif() - -option(OT_CHILD_SUPERVISION "enable child supervision support") -if(OT_CHILD_SUPERVISION) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE=1") -endif() - -option(OT_COAP "enable coap api support") -if(OT_COAP) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_COAP_API_ENABLE=1") -endif() - -option(OT_COAPS "enable secure coap api support") -if(OT_COAPS) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE=1") -endif() - -option(OT_COAP_BLOCK "enable coap block-wise transfer (RFC7959) api support") -if(OT_COAP_BLOCK) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE=1") -endif() - -option(OT_COAP_OBSERVE "enable coap observe (RFC7641) api support") -if(OT_COAP_OBSERVE) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE=1") -endif() - -option(OT_COMMISSIONER "enable commissioner support") -if(OT_COMMISSIONER) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_COMMISSIONER_ENABLE=1") -endif() - -option(OT_CSL_RECEIVER "enable csl receiver") -if(OT_CSL_RECEIVER) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE=1") -endif() - -option(OT_CSL_AUTO_SYNC "enable data polling based on csl config" ${OT_CSL_RECEIVER}) -if(OT_CSL_AUTO_SYNC) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MAC_CSL_AUTO_SYNC_ENABLE=1") -else() - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MAC_CSL_AUTO_SYNC_ENABLE=0") -endif() - -option(OT_CSL_DEBUG "enable csl debug") -if(OT_CSL_DEBUG) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE=1") -endif() - -option(OT_DATASET_UPDATER "enable dataset updater support") -if(OT_DATASET_UPDATER) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE=1") -endif() - -option(OT_DHCP6_CLIENT "enable DHCP6 client support") -if(OT_DHCP6_CLIENT) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE=1") -endif() - -option(OT_DHCP6_SERVER "enable DHCP6 server support") -if(OT_DHCP6_SERVER) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE=1") -endif() - -option(OT_DIAGNOSTIC "enable diagnostic support") -if(OT_DIAGNOSTIC) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_DIAG_ENABLE=1") -endif() - -option(OT_DNS_CLIENT "enable DNS client support") -if(OT_DNS_CLIENT) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE=1") -endif() - -option(OT_DNS_DSO "enable DNS Stateful Operations (DSO) support") -if(OT_DNS_DSO) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_DNS_DSO_ENABLE=1") -endif() - -option(OT_DNSSD_SERVER "enable DNS-SD server support") -if(OT_DNSSD_SERVER) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE=1") -endif() - -option(OT_DOC "Build OpenThread documentation") - -option(OT_ECDSA "enable ECDSA support") -if(OT_ECDSA) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_ECDSA_ENABLE=1") -endif() - -option(OT_SRP_CLIENT "enable SRP client support") -if (OT_SRP_CLIENT) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE=1") -endif() - -option(OT_DUA "enable Domain Unicast Address feature for Thread 1.2") -if(OT_DUA) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_DUA_ENABLE=1") -endif() - -option(OT_MESSAGE_USE_HEAP "enable heap allocator for message buffers") -if(OT_MESSAGE_USE_HEAP) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE=1") -endif() - -option(OT_MLR "enable Multicast Listener Registration feature for Thread 1.2") -if(OT_MLR) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MLR_ENABLE=1") -endif() - -option(OT_EXTERNAL_HEAP "enable external heap support") -if(OT_EXTERNAL_HEAP) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE=1") -endif() - -option(OT_HISTORY_TRACKER "enable history tracker support") -if(OT_HISTORY_TRACKER) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE=1") -endif() - -option(OT_IP6_FRAGM "enable ipv6 fragmentation support") -if(OT_IP6_FRAGM) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE=1") -endif() - -option(OT_JAM_DETECTION "enable jam detection support") -if(OT_JAM_DETECTION) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE=1") -endif() - -option(OT_JOINER "enable joiner support") -if(OT_JOINER) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_JOINER_ENABLE=1") -endif() - -option(OT_LEGACY "enable legacy network support") -if(OT_LEGACY) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LEGACY_ENABLE=1") -endif() - -option(OT_LINK_RAW "enable link raw service") -if(OT_LINK_RAW) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LINK_RAW_ENABLE=1") -endif() - -option(OT_LINK_METRICS_INITIATOR "enable link metrics initiator") -if (OT_LINK_METRICS_INITIATOR) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE=1") -endif() - -option(OT_LINK_METRICS_SUBJECT "enable link metrics subject") -if (OT_LINK_METRICS_SUBJECT) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE=1") -endif() - -option(OT_LOG_LEVEL_DYNAMIC "enable dynamic log level control") -if(OT_LOG_LEVEL_DYNAMIC) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE=1") -endif() - -option(OT_MAC_FILTER "enable mac filter support") -if(OT_MAC_FILTER) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MAC_FILTER_ENABLE=1") -endif() - -option(OT_MLE_LONG_ROUTES "enable MLE long routes extension (experimental, breaks Thread conformance)") -if(OT_MLE_LONG_ROUTES) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE=1") -endif() - -option(OT_MTD_NETDIAG "enable TMF network diagnostics on MTDs") -if(OT_MTD_NETDIAG) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE=1") -endif() - -option(OT_MULTIPLE_INSTANCE "enable multiple instances") -if(OT_MULTIPLE_INSTANCE) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE=1") -endif() - -option(OT_NEIGHBOR_DISCOVERY_AGENT "enable neighbor discovery agent support") -if(OT_NEIGHBOR_DISCOVERY_AGENT) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_NEIGHBOR_DISCOVERY_AGENT_ENABLE=1") -endif() - -option(OT_NETDATA_PUBLISHER "enable Thread Network Data publisher") -if(OT_NETDATA_PUBLISHER) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE=1") -endif() - -option(OT_PING_SENDER "enable ping sender support" ${OT_APP_CLI}) -if(OT_PING_SENDER) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_PING_SENDER_ENABLE=1") -endif() - -option(OT_PLATFORM_NETIF "enable platform netif support") -if(OT_PLATFORM_NETIF) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE=1") -endif() - -option(OT_PLATFORM_UDP "enable platform UDP support") -if(OT_PLATFORM_UDP) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE=1") -endif() - if(OT_POSIX_SETTINGS_PATH) target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_POSIX_SETTINGS_PATH=${OT_POSIX_SETTINGS_PATH}") endif() -option(OT_REFERENCE_DEVICE "enable Thread Test Harness reference device support") -if(OT_REFERENCE_DEVICE) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE=1") -endif() - -option(OT_SERVICE "enable support for injecting Service entries into the Thread Network Data") -if(OT_SERVICE) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE=1") -endif() - -option(OT_SETTINGS_RAM "enable volatile-only storage of settings") -if(OT_SETTINGS_RAM) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_SETTINGS_RAM=1") -endif() - -option(OT_SLAAC "enable support for adding of auto-configured SLAAC addresses by OpenThread") -if(OT_SLAAC) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE=1") -endif() - -option(OT_SNTP_CLIENT "enable SNTP Client support") -if(OT_SNTP_CLIENT) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE=1") -endif() - -option(OT_SRP_SERVER "enable SRP server") -if (OT_SRP_SERVER) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_SRP_SERVER_ENABLE=1") -endif() - -option(OT_TIME_SYNC "enable the time synchronization service feature") -if(OT_TIME_SYNC) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_TIME_SYNC_ENABLE=1") -endif() +#----------------------------------------------------------------------------------------------------------------------- +# Check removed/replaced options -option(OT_TREL "enable TREL radio link for Thread over Infrastructure feature") -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") -endif() - -option(OT_UPTIME "enable support for tracking OpenThread instance's uptime") -if(OT_UPTIME) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_UPTIME_ENABLE=1") -endif() - -option(OT_FIREWALL "enable firewall") -if (OT_FIREWALL) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_POSIX_CONFIG_FIREWALL_ENABLE=1") -endif() - -option(OT_FULL_LOGS "enable full logs") -if(OT_FULL_LOGS) - if(NOT OT_LOG_LEVEL) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_LEVEL=OT_LOG_LEVEL_DEBG") +macro(ot_removed_option name error) + # This macro checks for a remove option and emits an error + # if the option is set. + get_property(is_set CACHE ${name} PROPERTY VALUE SET) + if (is_set) + message(FATAL_ERROR "Removed option ${name} is set - ${error}") endif() - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL=1") -endif() - -option(OT_OTNS "enable OTNS support") -if(OT_OTNS) - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_CONFIG_OTNS_ENABLE=1") -endif() +endmacro() -set(OT_RCP_RESTORATION_MAX_COUNT "0" CACHE STRING "set max RCP restoration count") -if(OT_RCP_RESTORATION_MAX_COUNT MATCHES "^[0-9]+$") - target_compile_definitions(ot-config INTERFACE "OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT=${OT_RCP_RESTORATION_MAX_COUNT}") -else() - message(FATAL_ERROR "Invalid max RCP restoration count: ${OT_RCP_RESTORATION_MAX_COUNT}") -endif() - -option(OT_EXCLUDE_TCPLP_LIB "exclude TCPlp library from build") - -# Checks -if(OT_PLATFORM_UDP AND OT_UDP_FORWARD) - message(FATAL_ERROR "OT_PLATFORM_UDP and OT_UDP_FORWARD are exclusive") -endif() +ot_removed_option(OT_MTD_NETDIAG "- Use OT_NETDIAG_CLIENT instead - note that server function is always supported") +ot_removed_option(OT_EXCLUDE_TCPLP_LIB "- Use OT_TCP instead, OT_EXCLUDE_TCPLP_LIB is deprecated") diff --git a/etc/docker/environment/Dockerfile b/etc/docker/environment/Dockerfile index 3c297de3620..a7c38f86904 100644 --- a/etc/docker/environment/Dockerfile +++ b/etc/docker/environment/Dockerfile @@ -1,5 +1,5 @@ # Ubuntu image with tools required to build OpenThread -FROM ubuntu:18.04 +FROM ubuntu:22.04 ENV DEBIAN_FRONTEND noninteractive ENV LANG en_US.UTF-8 @@ -9,6 +9,7 @@ RUN set -x \ && apt-get install -y locales \ && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 \ && apt-get --no-install-recommends install -fy \ + bzip2 \ git \ ninja-build \ python3 \ @@ -20,6 +21,7 @@ RUN set -x \ inetutils-ping \ ca-certificates \ && update-ca-certificates \ + && python3 -m pip install -U pip \ && python3 -m pip install -U cmake \ && python3 -m pip install wheel diff --git a/etc/gn/openthread.gni b/etc/gn/openthread.gni index fe494b08ea6..9bbb543d257 100644 --- a/etc/gn/openthread.gni +++ b/etc/gn/openthread.gni @@ -84,6 +84,9 @@ if (openthread_enable_core_config_args) { # Enable border agent support openthread_config_border_agent_enable = false + # Enable border agent ID + openthread_config_border_agent_id_enable = false + # Enable border router support openthread_config_border_router_enable = false @@ -174,8 +177,8 @@ if (openthread_enable_core_config_args) { # Enable MLE long routes extension (experimental, breaks Thread conformance] openthread_config_mle_long_routes_enable = false - # Enable TMF network diagnostics on MTDs - openthread_config_tmf_network_diag_mtd_enable = false + # Enable TMF network diagnostics client + openthread_config_tmf_netdiag_client_enable = false # Enable multiple instances openthread_config_multiple_instance_enable = false diff --git a/examples/Makefile-cc2538 b/examples/Makefile-cc2538 deleted file mode 100644 index 99d895a53a6..00000000000 --- a/examples/Makefile-cc2538 +++ /dev/null @@ -1,309 +0,0 @@ -# -# Copyright (c) 2016, 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. -# - -.NOTPARALLEL: - -AR = arm-none-eabi-ar -CCAS = arm-none-eabi-as -CPP = arm-none-eabi-cpp -CC = arm-none-eabi-gcc -CXX = arm-none-eabi-g++ -LD = arm-none-eabi-ld -STRIP = arm-none-eabi-strip -NM = arm-none-eabi-nm -RANLIB = arm-none-eabi-ranlib -OBJCOPY = arm-none-eabi-objcopy - -BuildJobs ?= 10 - -configure_OPTIONS = \ - --enable-cli \ - --enable-ftd \ - --enable-mtd \ - --enable-ncp \ - --enable-radio-only \ - --enable-linker-map \ - --with-examples=cc2538 \ - $(NULL) - -TopSourceDir := $(dir $(shell readlink $(firstword $(MAKEFILE_LIST)))).. -AbsTopSourceDir := $(dir $(realpath $(firstword $(MAKEFILE_LIST)))).. - -CC2538_CONFIG_FILE_CPPFLAGS = -DOPENTHREAD_PROJECT_CORE_CONFIG_FILE='\"openthread-core-cc2538-config.h\"' -CC2538_CONFIG_FILE_CPPFLAGS += -DOPENTHREAD_CORE_CONFIG_PLATFORM_CHECK_FILE='\"openthread-core-cc2538-config-check.h\"' -CC2538_CONFIG_FILE_CPPFLAGS += -I$(AbsTopSourceDir)/examples/platforms/cc2538/ - -COMMONCFLAGS := \ - -fdata-sections \ - -ffunction-sections \ - -Os \ - -g \ - $(CC2538_CONFIG_FILE_CPPFLAGS) \ - $(NULL) - -include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/common-switches.mk - -# Optional CC2592 options, first and foremost, whether to enable support for it -# at all. -ifeq ($(CC2592),1) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2538_WITH_CC2592=1 - -# If the PA_EN is on another port C pin, specify it with CC2592_PA_PIN. -ifneq ($(CC2592_PA_EN),) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2592_PA_EN_PIN=$(CC2592_PA_EN) -endif - -# If the LNA_EN is on another port C pin, specify it with CC2592_LNA_PIN. -ifneq ($(CC2592_LNA_EN),) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2592_LNA_EN_PIN=$(CC2592_LNA_EN) -endif - -# If we're not using HGM, set CC2538_USE_HGM to 0. -ifeq ($(CC2592_USE_HGM),0) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2592_USE_HGM=0 -else # CC2592_USE_HGM=1 - -# HGM in use, if not on port D, specify the port here (A, B or C) with CC2592_HGM_PORT. -ifneq ($(CC2592_HGM_PORT),) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2592_HGM_PORT=GPIO_$(CC2592_HGM_PORT)_BASE -endif - -# If HGM is not at pin 2, specify which pin here with CC2592_HGM_PIN. -ifneq ($(CC2592_HGM_PIN),) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2592_HGM_PIN=$(CC2592_HGM_PIN) -endif - -# If we want it off by default, specify CC2592_HGM_DEFAULT_STATE=0 -ifeq ($(CC2592_HGM_DEFAULT_STATE),0) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2592_HGM_DEFAULT_STATE=false -endif - -endif # CC2592_USE_HGM - -endif # CC2592 - -ifneq ($(CC2538_RECEIVE_SENSITIVITY),) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2538_RECEIVE_SENSITIVITY=$(CC2538_RECEIVE_SENSITIVITY) -endif - -ifneq ($(CC2538_RSSI_OFFSET),) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CC2538_RSSI_OFFSET=$(CC2538_RSSI_OFFSET) -endif - -CPPFLAGS += \ - $(COMMONCFLAGS) \ - $(target_CPPFLAGS) \ - $(NULL) - -CFLAGS += \ - $(COMMONCFLAGS) \ - $(target_CFLAGS) \ - $(NULL) - -CXXFLAGS += \ - $(COMMONCFLAGS) \ - $(target_CXXFLAGS) \ - -fno-exceptions \ - -fno-rtti \ - $(NULL) - -LDFLAGS += \ - $(COMMONCFLAGS) \ - $(target_LDFLAGS) \ - -nostartfiles \ - -specs=nano.specs \ - -specs=nosys.specs \ - -Wl,--gc-sections \ - $(NULL) - -ECHO := @echo -MAKE := make -MKDIR_P := mkdir -p -LN_S := ln -s -RM_F := rm -f - -INSTALL := /usr/bin/install -INSTALLFLAGS := -p - -BuildPath = build -TopBuildDir = $(BuildPath) -AbsTopBuildDir = $(PWD)/$(TopBuildDir) - -ResultPath = output -TopResultDir = $(ResultPath) -AbsTopResultDir = $(PWD)/$(TopResultDir) - -TargetTuple = cc2538 - -ARCHS = cortex-m3 - -TopTargetLibDir = $(TopResultDir)/$(TargetTuple)/lib - -ifndef BuildJobs -BuildJobs := $(shell getconf _NPROCESSORS_ONLN) -endif -JOBSFLAG := -j$(BuildJobs) - -# -# configure-arch -# -# Configure OpenThread for the specified architecture. -# -# arch - The architecture to configure. -# -define configure-arch -$(ECHO) " CONFIG $(TargetTuple)..." -(cd $(BuildPath)/$(TargetTuple) && $(AbsTopSourceDir)/configure \ -INSTALL="$(INSTALL) $(INSTALLFLAGS)" \ -CPP="$(CPP)" CC="$(CC)" CXX="$(CXX)" OBJC="$(OBJC)" OBJCXX="$(OBJCXX)" AR="$(AR)" RANLIB="$(RANLIB)" NM="$(NM)" STRIP="$(STRIP)" CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" CXXFLAGS="$(CXXFLAGS)" LDFLAGS="$(LDFLAGS)" \ ---host=arm-none-eabi \ ---prefix=/ \ ---exec-prefix=/$(TargetTuple) \ -$(configure_OPTIONS)) -endef # configure-arch - -# -# build-arch -# -# Build the OpenThread intermediate build products for the specified -# architecture. -# -# arch - The architecture to build. -# -define build-arch -$(ECHO) " BUILD $(TargetTuple)" -$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \ -all -endef # build-arch - -# -# stage-arch -# -# Stage (install) the OpenThread final build products for the specified -# architecture. -# -# arch - The architecture to stage. -# -define stage-arch -$(ECHO) " STAGE $(TargetTuple)" -$(MAKE) $(JOBSFLAG) -C $(BuildPath)/$(TargetTuple) --no-print-directory \ -DESTDIR=$(AbsTopResultDir) \ -install -endef # stage-arch - -# -# ARCH_template -# -# Define macros, targets and rules to configure, build, and stage the -# OpenThread for a single architecture. -# -# arch - The architecture to instantiate the template for. -# -define ARCH_template -CONFIGURE_TARGETS += configure-$(1) -BUILD_TARGETS += do-build-$(1) -STAGE_TARGETS += stage-$(1) -BUILD_DIRS += $(BuildPath)/$(TargetTuple) -DIRECTORIES += $(BuildPath)/$(TargetTuple) - -configure-$(1): target_CPPFLAGS=$($(1)_target_CPPFLAGS) -configure-$(1): target_CFLAGS=$($(1)_target_CFLAGS) -configure-$(1): target_CXXFLAGS=$($(1)_target_CXXFLAGS) -configure-$(1): target_LDFLAGS=$($(1)_target_LDFLAGS) - -configure-$(1): $(BuildPath)/$(TargetTuple)/config.status - -$(BuildPath)/$(TargetTuple)/config.status: | $(BuildPath)/$(TargetTuple) - $$(call configure-arch,$(1)) - -do-build-$(1): configure-$(1) - -do-build-$(1): - +$$(call build-arch,$(1)) - -stage-$(1): do-build-$(1) - -stage-$(1): | $(TopResultDir) - $$(call stage-arch,$(1)) - -$(1): stage-$(1) -endef # ARCH_template - -.DEFAULT_GOAL := all - -all: stage - -# -# cortex-m3 -# - -cortex-m3_target_ABI = cortex-m3 -cortex-m3_target_CPPFLAGS = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb -cortex-m3_target_CFLAGS = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb -cortex-m3_target_CXXFLAGS = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb -cortex-m3_target_LDFLAGS = -mcpu=cortex-m3 -mfloat-abi=soft -mthumb - -# Instantiate an architecture-specific build template for each target -# architecture. - -$(foreach arch,$(ARCHS),$(eval $(call ARCH_template,$(arch)))) - -# -# Common / Finalization -# - -configure: $(CONFIGURE_TARGETS) - -build: $(BUILD_TARGETS) - -stage: $(STAGE_TARGETS) - -DIRECTORIES = $(TopResultDir) $(TopResultDir)/$(TargetTuple)/lib $(BUILD_DIRS) - -CLEAN_DIRS = $(TopResultDir) $(BUILD_DIRS) - -all: stage - -$(DIRECTORIES): - $(ECHO) " MKDIR $@" - @$(MKDIR_P) "$@" - -clean: - $(ECHO) " CLEAN" - @$(RM_F) -r $(CLEAN_DIRS) - -help: - $(ECHO) "Simply type 'make -f $(firstword $(MAKEFILE_LIST))' to build OpenThread for the following " - $(ECHO) "architectures: " - $(ECHO) "" - $(ECHO) " $(ARCHS)" - $(ECHO) "" - $(ECHO) "To build only a particular architecture, specify: " - $(ECHO) "" - $(ECHO) " make -f $(firstword $(MAKEFILE_LIST)) " - $(ECHO) "" diff --git a/examples/Makefile-simulation b/examples/Makefile-simulation index f16ce89736f..72894fb4703 100644 --- a/examples/Makefile-simulation +++ b/examples/Makefile-simulation @@ -45,7 +45,6 @@ COAPS ?= 1 COMMISSIONER ?= 1 CHANNEL_MANAGER ?= 1 CHANNEL_MONITOR ?= 1 -CHILD_SUPERVISION ?= 1 DATASET_UPDATER ?= 1 DHCP6_CLIENT ?= 1 DHCP6_SERVER ?= 1 @@ -58,12 +57,11 @@ HISTORY_TRACKER ?= 1 IP6_FRAGM ?= 1 JAM_DETECTION ?= 1 JOINER ?= 1 -LEGACY ?= 1 LINK_RAW ?= 1 MAC_FILTER ?= 1 -MTD_NETDIAG ?= 1 NEIGHBOR_DISCOVERY_AGENT ?= 1 NETDATA_PUBLISHER ?= 1 +NETDIAG_CLIENT ?= 1 PING_SENDER ?= 1 REFERENCE_DEVICE ?= 1 SERVICE ?= 1 diff --git a/examples/README.md b/examples/README.md index 2d0f5d5efdf..a7d1acd4f90 100644 --- a/examples/README.md +++ b/examples/README.md @@ -6,17 +6,16 @@ This page lists the available common switches with description. Unless stated ot | Makefile switch | CMake switch | Description | | --- | --- | --- | +| | OT_15_4 | Enables 802.15.4 radio link. | | ANYCAST_LOCATOR | OT_ANYCAST_LOCATOR | Enables anycast locator functionality. | | BACKBONE_ROUTER | OT_BACKBONE_ROUTER | Enables Backbone Router functionality for Thread 1.2. | | BIG_ENDIAN | OT_BIG_ENDIAN | Allows the host platform to use big-endian byte order. | | BORDER_AGENT | OT_BORDER_AGENT | Enables support for border agent. In most cases, enable this switch if you are building On-mesh Commissioner or Border Router with External Commissioning support. | | BORDER_ROUTER | OT_BORDER_ROUTER | Enables support for Border Router. This switch is usually combined with the BORDER_AGENT and UDP_FORWARD (or PLATFORM_UDP in case of RCP design) switches to build Border Router device. | | BORDER_ROUTING | OT_BORDER_ROUTING | Enables bi-directional border routing between Thread and Infrastructure networks for Border Router. | -| BORDER_ROUTING_NAT64 | OT_BORDER_ROUTING_NAT64 | Enables NAT64 border routing support for Border Router. | | BUILTIN_MBEDTLS_MANAGEMENT | OT_BUILTIN_MBEDTLS_MANAGEMENT | Enables the built-in mbedTLS management. Enable this switch if the external mbedTLS is used, but mbedTLS memory allocation and debug config should be managed internally by OpenThread. | | CHANNEL_MANAGER | OT_CHANNEL_MANAGER | Enables support for channel manager. Enable this switch on devices that are supposed to request a Thread network channel change. This switch should be used only with an FTD build. | | CHANNEL_MONITOR | OT_CHANNEL_MONITOR | Enables support for channel monitor. Enable this switch on devices that are supposed to determine the cleaner channels. | -| CHILD_SUPERVISION | OT_CHILD_SUPERVISION | Enables support for [child supervision](https://openthread.io/guides/build/features/child-supervision). Enable this switch on a parent or child node with custom OpenThread application that manages the supervision, checks timeout intervals, and verifies connectivity between parent and child. | | COAP | OT_COAP | Enables support for the CoAP API. Enable this switch if you want to control Constrained Application Protocol communication. | | COAP_OBSERVE | OT_COAP_OBSERVE | Enables support for CoAP Observe (RFC7641) API. | | COAPS | OT_COAPS | Enables support for the secure CoAP API. Enable this switch if you want to control Constrained Application Protocol Secure (CoAP over DTLS) communication. | @@ -35,26 +34,27 @@ This page lists the available common switches with description. Unless stated ot | DEBUG_UART_LOG | not implemented | Enables the log output for the debug UART. Requires OPENTHREAD_CONFIG_ENABLE_DEBUG_UART to be enabled. | | DNS_CLIENT | OT_DNS_CLIENT | Enables support for DNS client. Enable this switch on a device that sends a DNS query for AAAA (IPv6) record. | | DNS_DSO | OT_DNS_DSO | Enables support for DNS Stateful Operations (DSO). | +| DNS_UPSTREAM_QUERY | OT_DNS_UPSTREAM_QUERY | Enables forwarding DNS queries to upstream DNS server. | | DNSSD_SERVER | OT_DNSSD_SERVER | Enables support for DNS-SD server. DNS-SD server use service information from local SRP server to resolve DNS-SD query questions. | | DUA | OT_DUA | Enables the Domain Unicast Address feature for Thread 1.2. | | DYNAMIC_LOG_LEVEL | not implemented | Enables the dynamic log level feature. Enable this switch if OpenThread log level is required to be set at runtime. See [Logging guide](https://openthread.io/guides/build/logs) to learn more. | | ECDSA | OT_ECDSA | Enables support for Elliptic Curve Digital Signature Algorithm. Enable this switch if ECDSA digital signature is used by application. | -| EXCLUDE_TCPLP_LIB | OT_EXCLUDE_TCPLP_LIB | Exclude TCPlp library from the build. | | EXTERNAL_HEAP | OT_EXTERNAL_HEAP | Enables support for external heap. Enable this switch if the platform uses its own heap. Make sure to specify the external heap Calloc and Free functions to be used by the OpenThread stack. | | FULL_LOGS | OT_FULL_LOGS | Enables all log levels and regions. This switch sets the log level to OT_LOG_LEVEL_DEBG and turns on all region flags. See [Logging guide](https://openthread.io/guides/build/logs) to learn more. | | HISTORY_TRACKER | OT_HISTORY_TRACKER | Enables support for History Tracker. | | IP6_FRAGM | OT_IP6_FRAGM | Enables support for IPv6 fragmentation. | | JAM_DETECTION | OT_JAM_DETECTION | Enables support for [Jam Detection](https://openthread.io/guides/build/features/jam-detection). Enable this switch if a device requires the ability to detect signal jamming on a specific channel. | | JOINER | OT_JOINER | Enables [support for Joiner](https://openthread.io/reference/group/api-joiner). Enable this switch on a device that has to be commissioned to join the network. | -| LEGACY | OT_LEGACY | Enables support for legacy network. | | LINK_RAW | OT_LINK_RAW | Enables the Link Raw service. | | LOG_OUTPUT | not implemented | Defines if the LOG output is to be created and where it goes. There are several options available: `NONE`, `DEBUG_UART`, `APP`, `PLATFORM_DEFINED` (default). See [Logging guide](https://openthread.io/guides/build/logs) to learn more. | | MAC_FILTER | OT_MAC_FILTER | Enables support for the MAC filter. | | MLE_LONG_ROUTES | OT_MLE_LONG_ROUTES | Enables the MLE long routes extension. **Note: Enabling this feature breaks conformance to the Thread Specification.** | | MLR | OT_MLR | Enables Multicast Listener Registration feature for Thread 1.2. | -| MTD_NETDIAG | OT_MTD_NETDIAG | Enables the TMF network diagnostics on MTDs. | | MULTIPLE_INSTANCE | OT_MULTIPLE_INSTANCE | Enables multiple OpenThread instances. | +| NAT64_BORDER_ROUTING | OT_NAT64_BORDER_ROUTING | Enables NAT64 border routing support for Border Router. | +| NAT64_TRANSLATOR | OT_NAT64_TRANSLATOR | Enables NAT64 translator for Border Router. | | NETDATA_PUBLISHER | OT_NETDATA_PUBLISHER | Enables support for Thread Network Data publisher. | +| NETDIAG_CLIENT | OT_NETDIAG_CLIENT | Enables Network Diagnostics client functionality. | | PING_SENDER | OT_PING_SENDER | Enables support for ping sender. | | OTNS | OT_OTNS | Enables support for [OpenThread Network Simulator](https://github.com/openthread/ot-ns). Enable this switch if you are building OpenThread for OpenThread Network Simulator. | | PLATFORM_UDP | OT_PLATFORM_UDP | Enables platform UDP support. | @@ -66,8 +66,15 @@ This page lists the available common switches with description. Unless stated ot | SPINEL_ENCRYPTER_LIBS | not implemented | Specifies library files (absolute paths) for implementing the NCP Spinel Encrypter. | | SRP_CLIENT | OT_SRP_CLIENT | Enable support for SRP client. | | SRP_SERVER | OT_SRP_SERVER | Enable support for SRP server. | +| TCP | OT_TCP | Enable support for TCP (based on TCPlp). | | THREAD_VERSION | OT_THREAD_VERSION | Enables the chosen Thread version (1.1 / 1.2 (default)). For example, set to `1.1` for Thread 1.1. | -| TIME_SYNC | OT_TIME_SYNC | Enables the time synchronization service feature. **Note: Enabling this feature breaks conformance to the Thread Specification.** | | +| TIME_SYNC | OT_TIME_SYNC | Enables the time synchronization service feature. **Note: Enabling this feature breaks conformance to the Thread Specification.** | | TREL | OT_TREL | Enables TREL radio link for Thread over Infrastructure feature. | -| UDP_FORWARD | OT_UDP_FORWARD | Enables support for UDP forward. | Enable this switch on the Border Router device (running on the NCP design) with External Commissioning support to service Thread Commissioner packets on the NCP side. | +| UDP_FORWARD | OT_UDP_FORWARD | Enables support for UDP forward. Enable this switch on the Border Router device (running on the NCP design) with External Commissioning support to service Thread Commissioner packets on the NCP side. | | UPTIME | OT_UPTIME | Enables support for tracking OpenThread instance's uptime. | + +Removed or replaced switches: + +| Makefile switch | CMake switch | Description | +| --- | --- | --- | +| MTD_NETDIAG | OT_MTD_NETDIAG | Use NEDIAG_CLIENT to enable client functionality. Server functionality is always supported. | diff --git a/examples/apps/cli/cli_uart.cpp b/examples/apps/cli/cli_uart.cpp index b7fa5028d1d..81f05f825ec 100644 --- a/examples/apps/cli/cli_uart.cpp +++ b/examples/apps/cli/cli_uart.cpp @@ -137,7 +137,7 @@ static void ReceiveTask(const uint8_t *aBuf, uint16_t aBufLength) static const char sEraseString[] = {'\b', ' ', '\b'}; static const char CRNL[] = {'\r', '\n'}; static uint8_t sLastChar = '\0'; - const uint8_t * end; + const uint8_t *end; end = aBuf + aBufLength; @@ -368,15 +368,9 @@ static int CliUartOutput(void *aContext, const char *aFormat, va_list aArguments return rval; } -void otPlatUartReceived(const uint8_t *aBuf, uint16_t aBufLength) -{ - ReceiveTask(aBuf, aBufLength); -} +void otPlatUartReceived(const uint8_t *aBuf, uint16_t aBufLength) { ReceiveTask(aBuf, aBufLength); } -void otPlatUartSendDone(void) -{ - SendDoneTask(); -} +void otPlatUartSendDone(void) { SendDoneTask(); } extern "C" void otAppCliInit(otInstance *aInstance) { diff --git a/examples/apps/cli/ftd.cmake b/examples/apps/cli/ftd.cmake index f5a2870f838..9ed9be18c3b 100644 --- a/examples/apps/cli/ftd.cmake +++ b/examples/apps/cli/ftd.cmake @@ -44,8 +44,17 @@ target_link_libraries(ot-cli-ftd PRIVATE ${OT_PLATFORM_LIB_FTD} openthread-cli-ftd ${OT_MBEDTLS} + ot-config-ftd ot-config ) +if(OT_LINKER_MAP) + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") + target_link_libraries(ot-cli-ftd PRIVATE -Wl,-map,ot-cli-ftd.map) + else() + target_link_libraries(ot-cli-ftd PRIVATE -Wl,-Map=ot-cli-ftd.map) + endif() +endif() + install(TARGETS ot-cli-ftd DESTINATION bin) diff --git a/examples/apps/cli/main.c b/examples/apps/cli/main.c index 165f32f9395..cc52d9ab7eb 100644 --- a/examples/apps/cli/main.c +++ b/examples/apps/cli/main.c @@ -50,24 +50,15 @@ extern void otAppCliInit(otInstance *aInstance); #if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE -void *otPlatCAlloc(size_t aNum, size_t aSize) -{ - return calloc(aNum, aSize); -} +OT_TOOL_WEAK void *otPlatCAlloc(size_t aNum, size_t aSize) { return calloc(aNum, aSize); } -void otPlatFree(void *aPtr) -{ - free(aPtr); -} +OT_TOOL_WEAK void otPlatFree(void *aPtr) { free(aPtr); } #endif -void otTaskletsSignalPending(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} +void otTaskletsSignalPending(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); } #if OPENTHREAD_POSIX && !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) -static void ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[]) +static otError ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aContext); OT_UNUSED_VARIABLE(aArgsLength); @@ -75,8 +66,32 @@ static void ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[]) exit(EXIT_SUCCESS); } -static const otCliCommand kCommands[] = {{"exit", ProcessExit}}; + +#if OPENTHREAD_EXAMPLES_SIMULATION +extern otError ProcessNodeIdFilter(void *aContext, uint8_t aArgsLength, char *aArgs[]); +#endif + +static const otCliCommand kCommands[] = { + {"exit", ProcessExit}, +#if OPENTHREAD_EXAMPLES_SIMULATION + /* + * The CLI command `nodeidfilter` only works for simulation in real time. + * + * It can be used either as an allow list or a deny list. Once the filter is cleared, the first `nodeidfilter allow` + * or `nodeidfilter deny` will determine whether it is set up as an allow or deny list. Subsequent calls should + * use the same sub-command to add new node IDs, e.g., if we first call `nodeidfilter allow` (which sets the filter + * up as an allow list), a subsequent `nodeidfilter deny` will result in `InvalidState` error. + * + * The usage of the command `nodeidfilter`: + * - `nodeidfilter deny ` : It denies the connection to a specified node (use as deny-list). + * - `nodeidfilter allow : It allows the connection to a specified node (use as allow-list). + * - `nodeidfilter clear` : It restores the filter state to default. + * - `nodeidfilter` : Outputs filter mode (allow-list or deny-list) and filtered node IDs. + */ + {"nodeidfilter", ProcessNodeIdFilter}, #endif +}; +#endif // OPENTHREAD_POSIX && !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) int main(int argc, char *argv[]) { @@ -111,7 +126,7 @@ int main(int argc, char *argv[]) otAppCliInit(instance); #if OPENTHREAD_POSIX && !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) - otCliSetUserCommands(kCommands, OT_ARRAY_LENGTH(kCommands), instance); + IgnoreError(otCliSetUserCommands(kCommands, OT_ARRAY_LENGTH(kCommands), instance)); #endif while (!otSysPseudoResetWasRequested()) diff --git a/examples/apps/cli/mtd.cmake b/examples/apps/cli/mtd.cmake index 7b71883bb82..786f741610d 100644 --- a/examples/apps/cli/mtd.cmake +++ b/examples/apps/cli/mtd.cmake @@ -44,8 +44,17 @@ target_link_libraries(ot-cli-mtd PRIVATE ${OT_PLATFORM_LIB_MTD} openthread-cli-mtd ${OT_MBEDTLS} + ot-config-mtd ot-config ) +if(OT_LINKER_MAP) + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") + target_link_libraries(ot-cli-mtd PRIVATE -Wl,-map,ot-cli-mtd.map) + else() + target_link_libraries(ot-cli-mtd PRIVATE -Wl,-Map=ot-cli-mtd.map) + endif() +endif() + install(TARGETS ot-cli-mtd DESTINATION bin) diff --git a/examples/apps/cli/radio.cmake b/examples/apps/cli/radio.cmake index 4302adca547..b2fe5d96861 100644 --- a/examples/apps/cli/radio.cmake +++ b/examples/apps/cli/radio.cmake @@ -48,9 +48,18 @@ target_link_libraries(ot-cli-radio PRIVATE ${OT_PLATFORM_LIB_RCP} openthread-cli-radio ${OT_MBEDTLS_RCP} + ot-config-radio ot-config ) +if(OT_LINKER_MAP) + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") + target_link_libraries(ot-cli-radio PRIVATE -Wl,-map,ot-cli-radio.map) + else() + target_link_libraries(ot-cli-radio PRIVATE -Wl,-Map=ot-cli-radio.map) + endif() +endif() + install(TARGETS ot-cli-radio DESTINATION bin ) diff --git a/examples/apps/ncp/ftd.cmake b/examples/apps/ncp/ftd.cmake index c7d0bbf5d1a..a7ffa51ad54 100644 --- a/examples/apps/ncp/ftd.cmake +++ b/examples/apps/ncp/ftd.cmake @@ -44,7 +44,16 @@ target_link_libraries(ot-ncp-ftd PRIVATE ${OT_PLATFORM_LIB_FTD} openthread-ncp-ftd ${OT_MBEDTLS} + ot-config-ftd ot-config ) +if(OT_LINKER_MAP) + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") + target_link_libraries(ot-ncp-ftd PRIVATE -Wl,-map,ot-ncp-ftd.map) + else() + target_link_libraries(ot-ncp-ftd PRIVATE -Wl,-Map=ot-ncp-ftd.map) + endif() +endif() + install(TARGETS ot-ncp-ftd DESTINATION bin) diff --git a/examples/apps/ncp/main.c b/examples/apps/ncp/main.c index af50d145d0c..9c21c57bb90 100644 --- a/examples/apps/ncp/main.c +++ b/examples/apps/ncp/main.c @@ -46,21 +46,12 @@ extern void otAppNcpInit(otInstance *aInstance); #if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE -void *otPlatCAlloc(size_t aNum, size_t aSize) -{ - return calloc(aNum, aSize); -} +OT_TOOL_WEAK void *otPlatCAlloc(size_t aNum, size_t aSize) { return calloc(aNum, aSize); } -void otPlatFree(void *aPtr) -{ - free(aPtr); -} +OT_TOOL_WEAK void otPlatFree(void *aPtr) { free(aPtr); } #endif -void otTaskletsSignalPending(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} +void otTaskletsSignalPending(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); } int main(int argc, char *argv[]) { diff --git a/examples/apps/ncp/mtd.cmake b/examples/apps/ncp/mtd.cmake index 61380c3b55c..2fecbacc0bc 100644 --- a/examples/apps/ncp/mtd.cmake +++ b/examples/apps/ncp/mtd.cmake @@ -44,7 +44,16 @@ target_link_libraries(ot-ncp-mtd PRIVATE ${OT_PLATFORM_LIB_MTD} openthread-ncp-mtd ${OT_MBEDTLS} + ot-config-mtd ot-config ) +if(OT_LINKER_MAP) + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") + target_link_libraries(ot-ncp-mtd PRIVATE -Wl,-map,ot-ncp-mtd.map) + else() + target_link_libraries(ot-ncp-mtd PRIVATE -Wl,-Map=ot-ncp-mtd.map) + endif() +endif() + install(TARGETS ot-ncp-mtd DESTINATION bin) diff --git a/examples/apps/ncp/ncp.c b/examples/apps/ncp/ncp.c index ee7ccf7d175..df6529a3aca 100644 --- a/examples/apps/ncp/ncp.c +++ b/examples/apps/ncp/ncp.c @@ -37,15 +37,9 @@ #if !OPENTHREAD_CONFIG_NCP_SPI_ENABLE #include "utils/uart.h" -void otPlatUartReceived(const uint8_t *aBuf, uint16_t aBufLength) -{ - otNcpHdlcReceive(aBuf, aBufLength); -} +void otPlatUartReceived(const uint8_t *aBuf, uint16_t aBufLength) { otNcpHdlcReceive(aBuf, aBufLength); } -void otPlatUartSendDone(void) -{ - otNcpHdlcSendDone(); -} +void otPlatUartSendDone(void) { otNcpHdlcSendDone(); } #endif #if !OPENTHREAD_ENABLE_NCP_VENDOR_HOOK diff --git a/examples/apps/ncp/rcp.cmake b/examples/apps/ncp/rcp.cmake index 7ba3b97253f..0f8be335ab2 100644 --- a/examples/apps/ncp/rcp.cmake +++ b/examples/apps/ncp/rcp.cmake @@ -43,7 +43,16 @@ target_link_libraries(ot-rcp PRIVATE openthread-radio ${OT_PLATFORM_LIB_RCP} openthread-rcp + ot-config-radio ot-config ) +if(OT_LINKER_MAP) + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") + target_link_libraries(ot-rcp PRIVATE -Wl,-map,ot-rcp.map) + else() + target_link_libraries(ot-rcp PRIVATE -Wl,-Map=ot-rcp.map) + endif() +endif() + install(TARGETS ot-rcp DESTINATION bin) diff --git a/examples/common-switches.mk b/examples/common-switches.mk index ac05ccd6cdf..36ff95726db 100644 --- a/examples/common-switches.mk +++ b/examples/common-switches.mk @@ -34,7 +34,6 @@ BIG_ENDIAN ?= 0 BORDER_AGENT ?= 0 BORDER_ROUTER ?= 0 BORDER_ROUTING ?= 0 -BORDER_ROUTING_NAT64 ?= 0 COAP ?= 0 COAP_BLOCK ?= 0 COAP_OBSERVE ?= 0 @@ -43,7 +42,6 @@ COMMISSIONER ?= 0 COVERAGE ?= 0 CHANNEL_MANAGER ?= 0 CHANNEL_MONITOR ?= 0 -CHILD_SUPERVISION ?= 0 DATASET_UPDATER ?= 0 DEBUG ?= 0 DHCP6_CLIENT ?= 0 @@ -53,6 +51,7 @@ DISABLE_DOC ?= 0 DISABLE_TOOLS ?= 0 DNS_CLIENT ?= 0 DNS_DSO ?= 0 +DNS_UPSTREAM_QUERY ?= 0 DNSSD_SERVER ?= 0 DUA ?= 0 DYNAMIC_LOG_LEVEL ?= 0 @@ -62,19 +61,22 @@ HISTORY_TRACKER ?= 0 IP6_FRAGM ?= 0 JAM_DETECTION ?= 0 JOINER ?= 0 -LEGACY ?= 0 ifeq ($(REFERENCE_DEVICE),1) LOG_OUTPUT ?= APP endif LINK_RAW ?= 0 MAC_FILTER ?= 0 +MESH_DIAG ?= 0 MESSAGE_USE_HEAP ?= 0 MLE_LONG_ROUTES ?= 0 MLR ?= 0 MTD_NETDIAG ?= 0 MULTIPLE_INSTANCE ?= 0 +NAT64_BORDER_ROUTING ?= 0 +NAT64_TRANSLATOR ?= 0 NEIGHBOR_DISCOVERY_AGENT ?= 0 NETDATA_PUBLISHER ?= 0 +NETDIAG_CLIENT ?= 0 OTNS ?= 0 PING_SENDER ?= 1 PLATFORM_UDP ?= 0 @@ -86,6 +88,7 @@ SLAAC ?= 1 SNTP_CLIENT ?= 0 SRP_CLIENT ?= 0 SRP_SERVER ?= 0 +TCP ?= 0 THREAD_VERSION ?= 1.3 TIME_SYNC ?= 0 TREL ?= 0 @@ -117,8 +120,12 @@ ifeq ($(BORDER_ROUTING),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE=1 endif -ifeq ($(BORDER_ROUTING_NAT64),1) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE=1 +ifeq ($(NAT64_BORDER_ROUTING),1) +COMMONCFLAGS += -DOPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE=1 +endif + +ifeq ($(NAT64_TRANSLATOR),1) +COMMONCFLAGS += -DOPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE=1 endif ifeq ($(COAP),1) @@ -153,10 +160,6 @@ ifeq ($(CHANNEL_MONITOR),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE=1 endif -ifeq ($(CHILD_SUPERVISION),1) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE=1 -endif - ifeq ($(CSL_RECEIVER),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE=1 endif @@ -207,6 +210,10 @@ ifeq ($(DNS_DSO),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_DNS_DSO_ENABLE=1 endif +ifeq ($(DNS_UPSTREAM_QUERY), 1) +COMMONCFLAGS += -DOPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE=1 +endif + ifeq ($(DNSSD_SERVER),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE=1 endif @@ -243,10 +250,6 @@ ifeq ($(JOINER),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_JOINER_ENABLE=1 endif -ifeq ($(LEGACY),1) -COMMONCFLAGS += -DOPENTHREAD_CONFIG_LEGACY_ENABLE=1 -endif - ifeq ($(LINK_RAW),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_LINK_RAW_ENABLE=1 endif @@ -267,6 +270,10 @@ ifeq ($(MAC_FILTER),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_MAC_FILTER_ENABLE=1 endif +ifeq ($(MESH_DIAG),1) +COMMONCFLAGS += -DOPENTHREAD_CONFIG_MESH_DIAG_ENABLE=1 +endif + ifeq ($(MESSAGE_USE_HEAP),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE=1 endif @@ -280,6 +287,9 @@ ifeq ($(MLR),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_MLR_ENABLE=1 endif +# This config is removed but we still check and add the +# `OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE` so to +# get an error during build if `MTD_NETDIAG` is used. ifeq ($(MTD_NETDIAG),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE=1 endif @@ -296,6 +306,10 @@ ifeq ($(NETDATA_PUBLISHER),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE=1 endif +ifeq ($(NETDIAG_CLIENT),1) +COMMONCFLAGS += -DOPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE=1 +endif + ifeq ($(PING_SENDER),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_PING_SENDER_ENABLE=1 endif @@ -329,12 +343,18 @@ ifeq ($(SRP_SERVER),1) COMMONCFLAGS += -DOPENTHREAD_CONFIG_SRP_SERVER_ENABLE=1 endif +ifeq ($(TCP),1) +COMMONCFLAGS += -DOPENTHREAD_CONFIG_TCP_ENABLE=1 +endif + 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 +else ifeq ($(THREAD_VERSION),1.3.1) +COMMONCFLAGS += -DOPENTHREAD_CONFIG_THREAD_VERSION=5 endif ifeq ($(TIME_SYNC),1) diff --git a/examples/platforms/Makefile.am b/examples/platforms/Makefile.am index e7bd3ed6c2e..7f86919d855 100644 --- a/examples/platforms/Makefile.am +++ b/examples/platforms/Makefile.am @@ -30,6 +30,7 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am EXTRA_DIST = \ cc1352 \ + cc2538 \ cc2652 \ efr32 \ gp712 \ @@ -45,7 +46,6 @@ EXTRA_DIST = \ # Always package (e.g. for 'make dist') these subdirectories. DIST_SUBDIRS = \ - cc2538 \ simulation \ utils \ $(NULL) @@ -56,10 +56,6 @@ SUBDIRS = \ utils \ $(NULL) -if OPENTHREAD_PLATFORM_CC2538 -SUBDIRS += cc2538 -endif - if OPENTHREAD_PLATFORM_SIMULATION SUBDIRS += simulation endif diff --git a/examples/platforms/Makefile.platform.am b/examples/platforms/Makefile.platform.am index 8ab6520e2d3..71b82b0c2a7 100644 --- a/examples/platforms/Makefile.platform.am +++ b/examples/platforms/Makefile.platform.am @@ -41,10 +41,6 @@ LDFLAGS_COMMON = $(NULL) SOURCES_COMMON = $(NULL) LIBTOOLFLAGS_COMMON = --preserve-dup-deps -if OPENTHREAD_EXAMPLES_CC2538 -include $(top_srcdir)/examples/platforms/cc2538/Makefile.platform.am -endif - if OPENTHREAD_EXAMPLES_SIMULATION include $(top_srcdir)/examples/platforms/simulation/Makefile.platform.am endif diff --git a/examples/platforms/cc2538/Makefile.am b/examples/platforms/cc2538/Makefile.am deleted file mode 100644 index fb802f21183..00000000000 --- a/examples/platforms/cc2538/Makefile.am +++ /dev/null @@ -1,69 +0,0 @@ -# -# Copyright (c) 2016, 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. -# - -include $(abs_top_nlbuild_autotools_dir)/automake/pre.am - -# Do not enable -Wcast-align for this platform -override CFLAGS := $(filter-out -Wcast-align,$(CFLAGS)) -override CXXFLAGS := $(filter-out -Wcast-align,$(CXXFLAGS)) - -lib_LIBRARIES = libopenthread-cc2538.a - -libopenthread_cc2538_a_CPPFLAGS = \ - -I$(top_srcdir)/include \ - -I$(top_srcdir)/examples/platforms \ - -I$(top_srcdir)/src/core \ - $(NULL) - -PLATFORM_SOURCES = \ - alarm.c \ - cc2538-reg.h \ - diag.c \ - entropy.c \ - flash.c \ - misc.c \ - openthread-core-cc2538-config.h \ - openthread-core-cc2538-config-check.h \ - platform-cc2538.h \ - radio.c \ - rom-utility.h \ - startup-gcc.c \ - system.c \ - logging.c \ - uart.c \ - $(NULL) - -libopenthread_cc2538_a_SOURCES = \ - $(PLATFORM_SOURCES) \ - $(NULL) - -Dash = - -libopenthread_cc2538_a_LIBADD = \ - $(shell find $(top_builddir)/examples/platforms/utils $(Dash)type f $(Dash)name "*.o") - -include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/examples/platforms/cc2538/README.md b/examples/platforms/cc2538/README.md index 91d5b7afbbb..d6674dde27d 100644 --- a/examples/platforms/cc2538/README.md +++ b/examples/platforms/cc2538/README.md @@ -1,112 +1 @@ -# OpenThread on CC2538 Example - -This directory contains example platform drivers for the [Texas Instruments CC2538][cc2538]. - -[cc2538]: http://www.ti.com/product/CC2538 - -The example platform drivers are intended to present the minimal code necessary to support OpenThread. As a result, the example platform drivers do not necessarily highlight the platform's full capabilities. - -## Toolchain - -Download and install the [GNU toolchain for ARM Cortex-M][gnu-toolchain]. - -[gnu-toolchain]: https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm - -In a Bash terminal, follow these instructions to install the GNU toolchain and other dependencies. - -```bash -$ cd -$ ./script/bootstrap -``` - -## Building - -In a Bash terminal, follow these instructions to build the cc2538 examples. - -```bash -$ cd -$ ./bootstrap -$ make -f examples/Makefile-cc2538 -``` - -### CC2592 support - -If your board has a CC2592 range extender front-end IC connected to the CC2538 (e.g. the CC2538-CC2592 EM reference design), you need to initialise this part before reception of radio traffic will work. - -Support is enabled in OpenThread by building with `CC2592=1`: - -```bash -$ make -f examples/Makefile-cc2538 CC2592=1 -``` - -The default settings should work for any design following the integration advice given in TI's application report ["AN130 - Using CC2592 Front End With CC2538"](http://www.ti.com/lit/pdf/swra447). - -Additional settings can be customised: - -- `CC2592_PA_EN`: This specifies which pin (on port C of the CC2538) connects to the CC2592's `PA_EN` pin. The default is `3` (PC3). -- `CC2592_LNA_EN`: This specifies which pin (on port C of the CC2538) connects to the CC2592's `LNA_EN` pin. The default is `2` (PC2). -- `CC2592_USE_HGM`: This defines whether the HGM pin of the CC2592 is under GPIO control or not. If not, it is assumed that the HGM pin is tied to a power rail. -- `CC2592_HGM_PORT`: The HGM pin can be connected to any free GPIO. TI recommend using PD2, however if you've used a pin on another GPIO port, you may specify that port (`A`, `B` or `C`) here. -- `CC2592_HGM_PORT`: The HGM pin can be connected to any free GPIO. TI recommend using PD2, however if you've used a pin on another GPIO port, you may specify that port (`A`, `B` or `C`) here. Default is `D`. -- `CC2592_HGM_PIN`: The HGM pin can be connected to any free GPIO. TI recommend using PD2, however if you've used a pin on another GPIO pin, you can specify the pin here. Default is `2`. -- `CC2592_HGM_DEFAULT_STATE`: By default, HGM is enabled at power-on, but you may want to have it default to off, specify `CC2592_HGM_DEFAULT_STATE=0` to do so. -- `CC2538_RECEIVE_SENSITIVITY`: If you have tied the HGM pin to a power rail, this allows you to calibrate the RSSI values according to the new receive sensitivity. This has no effect if `CC2592_USE_HGM=1` (the default). -- `CC2538_RSSI_OFFSET`: If you have tied the HGM pin to a power rail, this allows you to calibrate the RSSI values according to the new RSSI offset. This has no effect if `CC2592_USE_HGM=1` (the default). - -## Flash Binaries - -If the build completed successfully, the `elf` files may be found in `/output/cc2538/bin`. - -To flash the images with [Flash Programmer 2][ti-flash-programmer-2], the files must have the `*.elf` extension. - -```bash -$ cd /output/cc2538/bin -$ cp ot-cli ot-cli.elf -``` - -To load the images with the [serial bootloader][ti-cc2538-bootloader], the images must be converted to `bin`. This is done using `arm-none-eabi-objcopy` - -```bash -$ cd /output/cc2538/bin -$ arm-none-eabi-objcopy -O binary ot-cli ot-cli.bin -``` - -The [cc2538-bsl.py script][cc2538-bsl-tool] provides a convenient method for flashing a CC2538 via the UART. To enter the bootloader backdoor for flashing, hold down SELECT for CC2538DK (corresponds to logic '0') while you press the Reset button. - -[ti-flash-programmer-2]: http://www.ti.com/tool/flash-programmer -[ti-cc2538-bootloader]: http://www.ti.com/lit/an/swra466a/swra466a.pdf -[cc2538-bsl-tool]: https://github.com/JelmerT/cc2538-bsl - -## Interact - -1. Open terminal to `/dev/ttyUSB1` (serial port settings: 115200 8-N-1). -2. Type `help` for list of commands. - -```bash -> help -help -channel -childtimeout -contextreusedelay -extaddr -extpanid -ipaddr -keysequence -leaderweight -mode -netdata register -networkidtimeout -networkkey -networkname -panid -ping -prefix -releaserouterid -rloc16 -route -routerupgradethreshold -scan -start -state -stop -``` +The OpenThread on CC2538 example has moved to https://github.com/openthread/ot-cc2538 diff --git a/examples/platforms/cc2538/alarm.c b/examples/platforms/cc2538/alarm.c deleted file mode 100644 index 7deefda0c79..00000000000 --- a/examples/platforms/cc2538/alarm.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file implements the OpenThread platform abstraction for the alarm. - * - */ - -#include -#include - -#include -#include -#include - -#include "platform-cc2538.h" - -enum -{ - kSystemClock = 32000000, ///< MHz - kTicksPerSec = 1000, ///< Ticks per second -}; - -static uint32_t sCounter = 0; -static uint32_t sAlarmT0 = 0; -static uint32_t sAlarmDt = 0; -static bool sIsRunning = false; - -static uint8_t sTimersIsRunning = 0; -static uint32_t sTimersExpireAt[OT_CC2538_TIMERS_COUNT]; - -extern void cc2538EnergyScanTimerHandler(void); - -void cc2538SetTimer(otCC2538Timer aTimer, uint32_t aDelay) -{ - sTimersIsRunning |= (1 << aTimer); - sTimersExpireAt[aTimer] = sCounter + aDelay; -} - -void cc2538AlarmInit(void) -{ - HWREG(NVIC_ST_RELOAD) = kSystemClock / kTicksPerSec; - HWREG(NVIC_ST_CTRL) = NVIC_ST_CTRL_CLK_SRC | NVIC_ST_CTRL_INTEN | NVIC_ST_CTRL_ENABLE; -} - -uint32_t otPlatAlarmMilliGetNow(void) -{ - return sCounter; -} - -void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t t0, uint32_t dt) -{ - OT_UNUSED_VARIABLE(aInstance); - - sAlarmT0 = t0; - sAlarmDt = dt; - sIsRunning = true; -} - -void otPlatAlarmMilliStop(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - sIsRunning = false; -} - -void cc2538AlarmProcess(otInstance *aInstance) -{ - uint32_t expires; - bool fire = false; - - if (sTimersIsRunning) - { - if ((int32_t)(sTimersExpireAt[OT_CC2538_TIMER_ENERGY_SCAN] - sCounter) < 0) - { - sTimersIsRunning &= ~(1 << OT_CC2538_TIMER_ENERGY_SCAN); - cc2538EnergyScanTimerHandler(); - } - } - - if (sIsRunning) - { - expires = sAlarmT0 + sAlarmDt; - - if (sAlarmT0 <= sCounter) - { - if (expires >= sAlarmT0 && expires <= sCounter) - { - fire = true; - } - } - else - { - if (expires >= sAlarmT0 || expires <= sCounter) - { - fire = true; - } - } - - if (fire) - { - sIsRunning = false; - -#if OPENTHREAD_CONFIG_DIAG_ENABLE - - if (otPlatDiagModeGet()) - { - otPlatDiagAlarmFired(aInstance); - } - else -#endif - { - otPlatAlarmMilliFired(aInstance); - } - } - } -} - -void SysTick_Handler() -{ - sCounter++; -} diff --git a/examples/platforms/cc2538/arm-none-eabi.cmake b/examples/platforms/cc2538/arm-none-eabi.cmake deleted file mode 100644 index 2e39ec11d47..00000000000 --- a/examples/platforms/cc2538/arm-none-eabi.cmake +++ /dev/null @@ -1,42 +0,0 @@ -# -# Copyright (c) 2019, 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. -# - -set(CMAKE_SYSTEM_NAME Generic) -set(CMAKE_SYSTEM_PROCESSOR ARM) - -set(CMAKE_C_COMPILER arm-none-eabi-gcc) -set(CMAKE_CXX_COMPILER arm-none-eabi-g++) -set(CMAKE_ASM_COMPILER arm-none-eabi-as) -set(CMAKE_RANLIB arm-none-eabi-ranlib) - -set(COMMON_C_FLAGS "-mthumb -fno-builtin -Wall -fdata-sections -ffunction-sections -mabi=aapcs -mcpu=cortex-m3 -mfloat-abi=soft") - -set(CMAKE_C_FLAGS_INIT "${COMMON_C_FLAGS} -std=gnu99") -set(CMAKE_CXX_FLAGS_INIT "${COMMON_C_FLAGS} -fno-exceptions -fno-rtti") -set(CMAKE_ASM_FLAGS_INIT "${COMMON_C_FLAGS}") -set(CMAKE_EXE_LINKER_FLAGS_INIT "${COMMON_C_FLAGS} -specs=nano.specs -specs=nosys.specs -nostartfiles") diff --git a/examples/platforms/cc2538/cc2538-reg.h b/examples/platforms/cc2538/cc2538-reg.h deleted file mode 100644 index 54972fbeabe..00000000000 --- a/examples/platforms/cc2538/cc2538-reg.h +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (c) 2016, 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 CC2538 register definitions. - * - */ - -#ifndef CC2538_REG_H_ -#define CC2538_REG_H_ - -#include - -// clang-format off - -#define HWREG(x) (*((volatile uint32_t *)(x))) - -/*! - * For registers that are arrays of 32-bit integers. - * - * @param reg Register address - * @param idx Register array index - */ -#define HWREG_ARR(reg, idx) HWREG((reg) + ((idx) << 2)) - -#define NVIC_ST_CTRL 0xE000E010 // SysTick Control and Status -#define NVIC_ST_RELOAD 0xE000E014 // SysTick Reload Value Register -#define NVIC_EN0 0xE000E100 // Interrupt 0-31 Set Enable - -#define NVIC_ST_CTRL_COUNT 0x00010000 // Count Flag -#define NVIC_ST_CTRL_CLK_SRC 0x00000004 // Clock Source -#define NVIC_ST_CTRL_INTEN 0x00000002 // Interrupt Enable -#define NVIC_ST_CTRL_ENABLE 0x00000001 // Enable - -#define RFCORE_XREG_SRCMATCH_EN 0x00000001 // SRCMATCH.SRC_MATCH_EN(1) -#define RFCORE_XREG_SRCMATCH_AUTOPEND 0x00000002 // SRCMATCH.AUTOPEND(1) -#define RFCORE_XREG_SRCMATCH_PEND_DATAREQ_ONLY 0x00000004 // SRCMATCH.PEND_DATAREQ_ONLY(1) - -#define RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE 3 // Num of register for source match enable status -#define RFCORE_XREG_SRCMATCH_SHORT_ENTRIES 24 // 24 short address entries in maximum -#define RFCORE_XREG_SRCMATCH_EXT_ENTRIES 12 // 12 extended address entries in maximum -#define RFCORE_XREG_SRCMATCH_SHORT_ENTRY_OFFSET 4 // address offset for one short address entry -#define RFCORE_XREG_SRCMATCH_EXT_ENTRY_OFFSET 8 // address offset for one extended address entry - -#define INT_UART0 21 // UART0 Rx and Tx - -#define IEEE_EUI64 0x00280028 // Address of IEEE EUI-64 address - -#define RFCORE_FFSM_SRCADDRESS_TABLE 0x40088400 // Source Address Table - -#define RFCORE_FFSM_SRCEXTPENDEN0 0x40088590 // Enable/Disable automatic pending per extended address -#define RFCORE_FFSM_SRCSHORTPENDEN0 0x4008859C // Enable/Disable automatic pending per short address -#define RFCORE_FFSM_EXT_ADDR0 0x400885A8 // Local address information -#define RFCORE_FFSM_PAN_ID0 0x400885C8 // Local address information -#define RFCORE_FFSM_PAN_ID1 0x400885CC // Local address information -#define RFCORE_FFSM_SHORT_ADDR0 0x400885D0 // Local address information -#define RFCORE_FFSM_SHORT_ADDR1 0x400885D4 // Local address information -#define RFCORE_XREG_FRMFILT0 0x40088600 // The frame filtering function -#define RFCORE_XREG_SRCMATCH 0x40088608 // Source address matching and pending bits -#define RFCORE_XREG_SRCSHORTEN0 0x4008860C // Short address matching -#define RFCORE_XREG_SRCEXTEN0 0x40088618 // Extended address matching - -#define RFCORE_XREG_FRMCTRL0 0x40088624 // Frame handling -#define RFCORE_XREG_FRMCTRL1 0x40088628 // Frame handling -#define RFCORE_XREG_RXENABLE 0x4008862C // RX enabling -#define RFCORE_XREG_FREQCTRL 0x4008863C // Controls the RF frequency -#define RFCORE_XREG_TXPOWER 0x40088640 // Controls the output power -#define RFCORE_XREG_FSMSTAT0 0x40088648 // Radio finite state machine status -#define RFCORE_XREG_FSMSTAT1 0x4008864C // Radio status register -#define RFCORE_XREG_FIFOPCTRL 0x40088650 // FIFOP threshold -#define RFCORE_XREG_CCACTRL0 0x40088658 // CCA threshold -#define RFCORE_XREG_RSSI 0x40088660 // RSSI status register -#define RFCORE_XREG_RSSISTAT 0x40088664 // RSSI valid status register -#define RFCORE_XREG_AGCCTRL1 0x400886C8 // AGC reference level -#define RFCORE_XREG_RFC_OBS_CTRL 0x400887AC // RF Core observable output -#define RFCORE_XREG_TXFILTCFG 0x400887E8 // TX filter configuration -#define RFCORE_XREG_RFRND 0x4008869C // Random data -#define RFCORE_SFR_RFDATA 0x40088828 // The TX FIFO and RX FIFO -#define RFCORE_SFR_RFERRF 0x4008882C // RF error interrupt flags -#define RFCORE_SFR_RFIRQF0 0x40088834 // RF interrupt flags -#define RFCORE_SFR_RFST 0x40088838 // RF CSMA-CA/strobe processor -#define CCTEST_OBSSEL 0x44010014 // CCTEST observable output route - -#define RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN 0x00000001 // Enables frame filtering - -#define RFCORE_XREG_FRMCTRL0_AUTOACK 0x00000020 -#define RFCORE_XREG_FRMCTRL0_ENERGY_SCAN 0x00000010 -#define RFCORE_XREG_FRMCTRL0_AUTOCRC 0x00000040 -#define RFCORE_XREG_FRMCTRL0_INFINITY_RX 0x00000008 - -#define RFCORE_XREG_FRMCTRL1_PENDING_OR 0x00000004 - -#define RFCORE_XREG_RFRND_IRND 0x00000001 - -#define RFCORE_XREG_FSMSTAT0_STATE_MASK 0x0000003F -#define RFCORE_XREG_FSMSTAT0_CAL_DONE 0x00000080 -#define RFCORE_XREG_FSMSTAT0_CAL_RUN 0x00000040 - -#define RFCORE_XREG_FSMSTAT0_STATE_IDLE 0x00000000 -#define RFCORE_XREG_FSMSTAT0_STATE_RX_CAL 0x00000002 -#define RFCORE_XREG_FSMSTAT0_STATE_SFD_WAIT0 0x00000003 -#define RFCORE_XREG_FSMSTAT0_STATE_SFD_WAIT1 0x00000004 -#define RFCORE_XREG_FSMSTAT0_STATE_SFD_WAIT2 0x00000005 -#define RFCORE_XREG_FSMSTAT0_STATE_SFD_WAIT3 0x00000006 -#define RFCORE_XREG_FSMSTAT0_STATE_RX0 0x00000007 -#define RFCORE_XREG_FSMSTAT0_STATE_RX1 0x00000008 -#define RFCORE_XREG_FSMSTAT0_STATE_RX2 0x00000009 -#define RFCORE_XREG_FSMSTAT0_STATE_RX3 0x0000000A -#define RFCORE_XREG_FSMSTAT0_STATE_RX4 0x0000000B -#define RFCORE_XREG_FSMSTAT0_STATE_RX5 0x0000000C -#define RFCORE_XREG_FSMSTAT0_STATE_RX6 0x0000000D -#define RFCORE_XREG_FSMSTAT0_STATE_RX_WAIT 0x0000000E -#define RFCORE_XREG_FSMSTAT0_STATE_RX_FRST 0x00000010 -#define RFCORE_XREG_FSMSTAT0_STATE_RX_OVER 0x00000011 -#define RFCORE_XREG_FSMSTAT0_STATE_TX_CAL 0x00000020 -#define RFCORE_XREG_FSMSTAT0_STATE_TX0 0x00000022 -#define RFCORE_XREG_FSMSTAT0_STATE_TX1 0x00000023 -#define RFCORE_XREG_FSMSTAT0_STATE_TX2 0x00000024 -#define RFCORE_XREG_FSMSTAT0_STATE_TX3 0x00000025 -#define RFCORE_XREG_FSMSTAT0_STATE_TX4 0x00000026 -#define RFCORE_XREG_FSMSTAT0_STATE_TX_FINAL 0x00000027 -#define RFCORE_XREG_FSMSTAT0_STATE_RXTX_TRANS 0x00000028 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK_CAL 0x00000030 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK0 0x00000031 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK1 0x00000032 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK2 0x00000033 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK3 0x00000034 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK4 0x00000035 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK5 0x00000036 -#define RFCORE_XREG_FSMSTAT0_STATE_ACK_DELAY 0x00000037 -#define RFCORE_XREG_FSMSTAT0_STATE_TX_UNDER 0x00000038 -#define RFCORE_XREG_FSMSTAT0_STATE_TX_DOWN0 0x0000001A -#define RFCORE_XREG_FSMSTAT0_STATE_TX_DOWN1 0x0000003A - -#define RFCORE_XREG_FSMSTAT1_RX_ACTIVE 0x00000001 -#define RFCORE_XREG_FSMSTAT1_TX_ACTIVE 0x00000002 -#define RFCORE_XREG_FSMSTAT1_LOCK_STATUS 0x00000004 -#define RFCORE_XREG_FSMSTAT1_SAMPLED_CCA 0x00000008 -#define RFCORE_XREG_FSMSTAT1_CCA 0x00000010 // Clear channel assessment -#define RFCORE_XREG_FSMSTAT1_SFD 0x00000020 -#define RFCORE_XREG_FSMSTAT1_FIFOP 0x00000040 -#define RFCORE_XREG_FSMSTAT1_FIFO 0x00000080 - -#define RFCORE_XREG_RSSISTAT_RSSI_VALID 0x00000001 // RSSI value is valid. - -#define RFCORE_XREG_RFC_OBS_POL_INV 0x00000040 // Invert polarity of OBS signal -#define RFCORE_XREG_RFC_OBS_MUX_ZERO 0x00000000 // Observable = constant zero -#define RFCORE_XREG_RFC_OBS_MUX_ONE 0x00000001 // Observable = constant one -#define RFCORE_XREG_RFC_OBS_MUX_SNIFF_DATA 0x00000008 // RFC sniff data -#define RFCORE_XREG_RFC_OBS_MUX_SNIFF_CLK 0x00000009 // RFC sniff clock -#define RFCORE_XREG_RFC_OBS_MUX_RSSI_VALID 0x0000000c // RSSI valid -#define RFCORE_XREG_RFC_OBS_MUX_DEMOD_CCA 0x0000000d // Clear channel assessment -#define RFCORE_XREG_RFC_OBS_MUX_SAMPLED_CCA 0x0000000e // Sampled CCA signal -#define RFCORE_XREG_RFC_OBS_MUX_SFD_SYNC 0x0000000f // SFD received or transmitted -#define RFCORE_XREG_RFC_OBS_MUX_TX_ACTIVE 0x00000010 // Transmitter is active -#define RFCORE_XREG_RFC_OBS_MUX_RX_ACTIVE 0x00000011 // Receiver is active -#define RFCORE_XREG_RFC_OBS_MUX_FFCTRL_FIFO 0x00000012 // One or more bytes in FIFO -#define RFCORE_XREG_RFC_OBS_MUX_FFCTRL_FIFOP 0x00000013 // One or more frames in FIFO -#define RFCORE_XREG_RFC_OBS_MUX_PACKET_DONE 0x00000014 // Packet received -#define RFCORE_XREG_RFC_OBS_MUX_RFC_XOR_RAND_IQ 0x00000016 // RAND I ^ RAND Q -#define RFCORE_XREG_RFC_OBS_MUX_RFC_RAND_Q 0x00000017 // Random data from Q channel -#define RFCORE_XREG_RFC_OBS_MUX_RFC_RAND_I 0x00000018 // Random data from I channel -#define RFCORE_XREG_RFC_OBS_MUX_LOCK_STATUS 0x00000019 // PLL is in lock -#define RFCORE_XREG_RFC_OBS_MUX_PA_PD 0x00000028 // Power amp power down -#define RFCORE_XREG_RFC_OBS_MUX_LNA_PD 0x0000002a // LNA power down - -#define RFCORE_SFR_RFERRF_NLOCK 0x00000001 // Failed to achieve PLL lock. -#define RFCORE_SFR_RFERRF_RXABO 0x00000002 // RX Aborted. -#define RFCORE_SFR_RFERRF_RXOVERF 0x00000004 // RX FIFO overflowed. -#define RFCORE_SFR_RFERRF_RXUNDERF 0x00000008 // RX FIFO underflowed. -#define RFCORE_SFR_RFERRF_TXOVERF 0x00000010 // TX FIFO overflowed. -#define RFCORE_SFR_RFERRF_TXUNDERF 0x00000020 // TX FIFO underflowed. -#define RFCORE_SFR_RFERRF_STROBEERR 0x00000040 // Command Strobe Error. - -#define RFCORE_SFR_RFST_INSTR_RXON 0xE3 // Instruction set RX on -#define RFCORE_SFR_RFST_INSTR_TXON 0xE9 // Instruction set TX on -#define RFCORE_SFR_RFST_INSTR_RFOFF 0xEF // Instruction set RF off -#define RFCORE_SFR_RFST_INSTR_FLUSHRX 0xED // Instruction set flush rx buffer -#define RFCORE_SFR_RFST_INSTR_FLUSHTX 0xEE // Instruction set flush tx buffer - -#define CCTEST_OBSSEL_EN 0x00000080 // Enable the OBS output on this pin -#define CCTEST_OBSSEL_SEL_OBS0 0x00000000 // Route OBS0 to pin -#define CCTEST_OBSSEL_SEL_OBS1 0x00000001 // Route OBS1 to pin -#define CCTEST_OBSSEL_SEL_OBS2 0x00000002 // Route OBS2 to pin - -#define ANA_REGS_BASE 0x400D6000 // ANA_REGS -#define ANA_REGS_O_IVCTRL 0x00000004 // Analog control register - -#define SYS_CTRL_CLOCK_CTRL 0x400D2000 // The clock control register -#define SYS_CTRL_SYSDIV_32MHZ 0x00000000 // Sys_div for sysclk 32MHz -#define SYS_CTRL_CLOCK_CTRL_AMP_DET 0x00200000 - -#define SYS_CTRL_PWRDBG 0x400D2074 -#define SYS_CTRL_PWRDBG_FORCE_WARM_RESET 0x00000008 - -#define SYS_CTRL_RCGCUART 0x400D2028 -#define SYS_CTRL_SCGCUART 0x400D202C -#define SYS_CTRL_DCGCUART 0x400D2030 -#define SYS_CTRL_I_MAP 0x400D2098 -#define SYS_CTRL_RCGCRFC 0x400D20A8 -#define SYS_CTRL_SCGCRFC 0x400D20AC -#define SYS_CTRL_DCGCRFC 0x400D20B0 -#define SYS_CTRL_EMUOVR 0x400D20B4 - -#define SYS_CTRL_RCGCRFC_RFC0 0x00000001 -#define SYS_CTRL_SCGCRFC_RFC0 0x00000001 -#define SYS_CTRL_DCGCRFC_RFC0 0x00000001 - -#define SYS_CTRL_I_MAP_ALTMAP 0x00000001 - -#define SYS_CTRL_RCGCUART_UART0 0x00000001 -#define SYS_CTRL_SCGCUART_UART0 0x00000001 -#define SYS_CTRL_DCGCUART_UART0 0x00000001 - -#define SYS_CTRL_RCGCUART_UART1 0x00000002 -#define SYS_CTRL_SCGCUART_UART1 0x00000002 -#define SYS_CTRL_DCGCUART_UART1 0x00000002 - -#define IOC_PA0_SEL 0x400D4000 // Peripheral select control -#define IOC_PA1_SEL 0x400D4004 // Peripheral select control -#define IOC_PA2_SEL 0x400D4008 -#define IOC_PA3_SEL 0x400D400C -#define IOC_UARTRXD_UART0 0x400D4100 -#define IOC_UARTRXD_UART1 0x400D4108 - -#define IOC_PA0_OVER 0x400D4080 -#define IOC_PA1_OVER 0x400D4084 -#define IOC_PA2_OVER 0x400D4088 -#define IOC_PA3_OVER 0x400D408C - -#define IOC_MUX_OUT_SEL_UART0_TXD 0x00000000 -#define IOC_MUX_OUT_SEL_UART1_TXD 0x00000002 - -#define IOC_OVERRIDE_OE 0x00000008 // PAD Config Override Output Enable -#define IOC_OVERRIDE_DIS 0x00000000 // PAD Config Override Disabled - -#define IOC_PAD_IN_SEL_PA0 0x00000000 // PA0 -#define IOC_PAD_IN_SEL_PA1 0x00000001 // PA1 -#define IOC_PAD_IN_SEL_PA2 0x00000002 // PA2 -#define IOC_PAD_IN_SEL_PA3 0x00000003 // PA3 - -#define UART0_BASE 0x4000C000 -#define UART1_BASE 0x4000D000 -#define GPIO_A_BASE 0x400D9000 // GPIO A -#define GPIO_B_BASE 0x400DA000 // GPIO B -#define GPIO_C_BASE 0x400DB000 // GPIO C -#define GPIO_D_BASE 0x400DC000 // GPIO D - -#define GPIO_O_DIR 0x00000400 -#define GPIO_O_AFSEL 0x00000420 - -#define GPIO_PIN(x) (1UL << x) // Arbitrary GPIO pin -#define GPIO_PIN_0 0x00000001 // GPIO pin 0 -#define GPIO_PIN_1 0x00000002 // GPIO pin 1 -#define GPIO_PIN_2 0x00000004 // GPIO pin 2 -#define GPIO_PIN_3 0x00000008 // GPIO pin 3 -#define GPIO_PIN_4 0x00000010 // GPIO pin 4 -#define GPIO_PIN_5 0x00000020 // GPIO pin 5 -#define GPIO_PIN_6 0x00000040 // GPIO pin 6 -#define GPIO_PIN_7 0x00000080 // GPIO pin 7 - -#define UART_O_DR 0x00000000 // UART data -#define UART_O_FR 0x00000018 // UART flag -#define UART_O_IBRD 0x00000024 -#define UART_O_FBRD 0x00000028 -#define UART_O_LCRH 0x0000002C -#define UART_O_CTL 0x00000030 // UART control -#define UART_O_IM 0x00000038 // UART interrupt mask -#define UART_O_MIS 0x00000040 // UART masked interrupt status -#define UART_O_ICR 0x00000044 // UART interrupt clear -#define UART_O_CC 0x00000FC8 // UART clock configuration - -#define UART_FR_RXFE 0x00000010 // UART receive FIFO empty -#define UART_FR_TXFF 0x00000020 // UART transmit FIFO full -#define UART_FR_RXFF 0x00000040 // UART receive FIFO full - -#define UART_CONFIG_WLEN_8 0x00000060 // 8 bit data -#define UART_CONFIG_STOP_ONE 0x00000000 // One stop bit -#define UART_CONFIG_PAR_NONE 0x00000000 // No parity - -#define UART_CTL_UARTEN 0x00000001 // UART enable -#define UART_CTL_TXE 0x00000100 // UART transmit enable -#define UART_CTL_RXE 0x00000200 // UART receive enable - -#define UART_IM_RXIM 0x00000010 // UART receive interrupt mask -#define UART_IM_RTIM 0x00000040 // UART receive time-out interrupt - -#define SOC_ADC_ADCCON1 0x400D7000 // ADC Control -#define SOC_ADC_RNDL 0x400D7014 // RNG low data -#define SOC_ADC_RNDH 0x400D7018 // RNG high data - -#define SOC_ADC_ADCCON1_RCTRL0 0x00000004 // ADCCON1 RCTRL bit 0 -#define SOC_ADC_ADCCON1_RCTRL1 0x00000008 // ADCCON1 RCTRL bit 1 - -#define FLASH_CTRL_FCTL 0x400D3008 // Flash control -#define FLASH_CTRL_DIECFG0 0x400D3014 // Flash information - -// clang-format on - -#endif diff --git a/examples/platforms/cc2538/cc2538.ld b/examples/platforms/cc2538/cc2538.ld deleted file mode 100644 index daff1621f24..00000000000 --- a/examples/platforms/cc2538/cc2538.ld +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2016, 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 - * GCC linker script for CC2538. - */ - -_512k_bytes = (512*1024); -_256k_bytes = (256*1024); -_128k_bytes = (128*1024); -_FLASH_page_size = 2048; - -/* - * Change for your chip, default is 512k chips - */ -_FLASH_size_bytes = _512k_bytes; -_FLASH_n_pages = (_FLASH_size_bytes / _FLASH_page_size); -/* reduce the usable size by: the CCA + settings Page A & B, total 3 pages */ -_FLASH_usable_size = (_FLASH_size_bytes - (3 * _FLASH_page_size)); -_FLASH_start = 0x00200000; -_FLASH_end = (_FLASH_start + _FLASH_size_bytes); - -/* - * The CCA (Customer Configuration Area) is always the last page. - * See: http://www.ti.com/lit/ug/swru319c/swru319c.pdf - * table 8-2 for more details. - */ -_FLASH_cca_page = (_FLASH_end - (1 * _FLASH_page_size)); - -/* - * OpenThread NV storage goes in the settings page. - * OpenThread requires at least 2 adjacent pages, call them A and B. - */ -_FLASH_settings_pageB = (_FLASH_end - (2 * _FLASH_page_size)); -_FLASH_settings_pageA = (_FLASH_end - (3 * _FLASH_page_size)); - -MEMORY -{ - /* would like to use SYMBOLS (from above)here but we cannot - * GCC version 4.9 does not support symbolic expressions here. - * But later versions do support the feature. - */ - FLASH (rx) : ORIGIN = 0x00200000, LENGTH = 0x0007c000 - FLASH_CCA (rx) : ORIGIN = 0x0027FFD4, LENGTH = 0x2c - SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 32K -} -/* - * To safty check what would have been the SYMBOL values - * we use these ASSERTS to verify things are still good. - */ -ASSERT( _FLASH_start == 0x00200000, "invalid flash start address for cc2538") -ASSERT( _FLASH_cca_page == 0x0027f800, "invalid cca start address for cc2538") -ASSERT( _FLASH_usable_size == 0x0007e800, "Invalid usable size for this config") - - - -ENTRY(flash_cca_lock_page) -SECTIONS -{ - .text : ALIGN(4) - { - _text = .; - *(.vectors) - *(.text*) - *(.rodata*) - KEEP(*(.init)) - KEEP(*(.fini)) - _etext = .; - } > FLASH= 0 - - .init_array : - { - _init_array = .; - KEEP(*(SORT(.init_array.*))) - KEEP(*(.init_array*)) - _einit_array = .; - } > FLASH - - .ARM.exidx : ALIGN(4) - { - *(.ARM.exidx*) - } > FLASH - - .data : ALIGN(4) - { - _data = .; - *(.data*) - _edata = .; - } > SRAM AT > FLASH - _ldata = LOADADDR(.data); - - .bss : ALIGN(4) - { - _bss = .; - *(.bss*) - *(COMMON) - _ebss = .; - } > SRAM - - _heap = .; - end = .; - - .stack : ALIGN(4) - { - *(.stack) - } > SRAM - - .flashcca : - { - KEEP(*(.flash_cca)) - } > FLASH_CCA -} diff --git a/examples/platforms/cc2538/entropy.c b/examples/platforms/cc2538/entropy.c deleted file mode 100644 index 47a31be04d4..00000000000 --- a/examples/platforms/cc2538/entropy.c +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2019, 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 an entropy source based on ADC. - * - */ - -#include - -#include - -#include "platform-cc2538.h" -#include "utils/code_utils.h" - -static void generateRandom(uint8_t *aOutput, uint16_t aOutputLength) -{ - uint32_t frmctrl0; - - HWREG(SOC_ADC_ADCCON1) &= ~(SOC_ADC_ADCCON1_RCTRL1 | SOC_ADC_ADCCON1_RCTRL0); - HWREG(SYS_CTRL_RCGCRFC) = SYS_CTRL_RCGCRFC_RFC0; - - while (HWREG(SYS_CTRL_RCGCRFC) != SYS_CTRL_RCGCRFC_RFC0) - ; - - frmctrl0 = HWREG(RFCORE_XREG_FRMCTRL0); - HWREG(RFCORE_XREG_FRMCTRL0) = RFCORE_XREG_FRMCTRL0_INFINITY_RX; - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RXON; - - while (!HWREG(RFCORE_XREG_RSSISTAT) & RFCORE_XREG_RSSISTAT_RSSI_VALID) - ; - - for (uint16_t index = 0; index < aOutputLength; index++) - { - aOutput[index] = 0; - - for (uint8_t offset = 0; offset < 8 * sizeof(uint8_t); offset++) - { - aOutput[index] <<= 1; - aOutput[index] |= (HWREG(RFCORE_XREG_RFRND) & RFCORE_XREG_RFRND_IRND); - } - } - - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RFOFF; - HWREG(RFCORE_XREG_FRMCTRL0) = frmctrl0; -} - -void cc2538RandomInit(void) -{ - uint16_t seed = 0; - - while (seed == 0x0000 || seed == 0x8003) - { - generateRandom((uint8_t *)&seed, sizeof(seed)); - } - - HWREG(SOC_ADC_RNDL) = (seed >> 8) & 0xff; - HWREG(SOC_ADC_RNDL) = seed & 0xff; -} - -otError otPlatEntropyGet(uint8_t *aOutput, uint16_t aOutputLength) -{ - otError error = OT_ERROR_NONE; - uint8_t channel = 0; - - otEXPECT_ACTION(aOutput, error = OT_ERROR_INVALID_ARGS); - - if (sInstance && otPlatRadioIsEnabled(sInstance)) - { - channel = 11 + (HWREG(RFCORE_XREG_FREQCTRL) - 11) / 5; - otPlatRadioSleep(sInstance); - otPlatRadioDisable(sInstance); - } - - generateRandom(aOutput, aOutputLength); - - if (channel) - { - cc2538RadioInit(); - otPlatRadioEnable(sInstance); - otPlatRadioReceive(sInstance, channel); - } - -exit: - return error; -} diff --git a/examples/platforms/cc2538/flash.c b/examples/platforms/cc2538/flash.c deleted file mode 100644 index 04c8a655941..00000000000 --- a/examples/platforms/cc2538/flash.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2016, 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. - */ - -#include -#include -#include - -#include "platform-cc2538.h" -#include "rom-utility.h" - -#define FLASH_CTRL_FCTL_BUSY 0x00000080 - -#define FLASH_PAGE_SIZE 2048 -#define FLASH_PAGE_NUM 2 -#define FLASH_SWAP_SIZE (FLASH_PAGE_SIZE * (FLASH_PAGE_NUM / 2)) - -/* The linker script creates this external symbol */ -extern uint8_t _FLASH_settings_pageA[]; - -/* Convert a settings offset to the physical address within the flash settings pages */ -static uint32_t flashPhysAddr(uint8_t aSwapIndex, uint32_t aOffset) -{ - uint32_t address = (uint32_t)(&_FLASH_settings_pageA[0]) + aOffset; - - if (aSwapIndex) - { - address += FLASH_SWAP_SIZE; - } - - return address; -} - -void otPlatFlashInit(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} - -uint32_t otPlatFlashGetSwapSize(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - return FLASH_SWAP_SIZE; -} - -void otPlatFlashErase(otInstance *aInstance, uint8_t aSwapIndex) -{ - OT_UNUSED_VARIABLE(aInstance); - - ROM_PageErase(flashPhysAddr(aSwapIndex, 0), FLASH_PAGE_SIZE); - while (HWREG(FLASH_CTRL_FCTL) & FLASH_CTRL_FCTL_BUSY) - { - } -} - -void otPlatFlashWrite(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, const void *aData, uint32_t aSize) -{ - OT_UNUSED_VARIABLE(aInstance); - - uint32_t *data = (uint32_t *)(aData); - - for (uint32_t size = 0; size < aSize; size += sizeof(uint32_t), aOffset += sizeof(uint32_t), data++) - { - ROM_ProgramFlash(data, flashPhysAddr(aSwapIndex, aOffset), sizeof(uint32_t)); - - while (HWREG(FLASH_CTRL_FCTL) & FLASH_CTRL_FCTL_BUSY) - { - } - } -} - -void otPlatFlashRead(otInstance *aInstance, uint8_t aSwapIndex, uint32_t aOffset, uint8_t *aData, uint32_t aSize) -{ - OT_UNUSED_VARIABLE(aInstance); - - memcpy(aData, (void *)flashPhysAddr(aSwapIndex, aOffset), aSize); -} diff --git a/examples/platforms/cc2538/openthread-core-cc2538-config.h b/examples/platforms/cc2538/openthread-core-cc2538-config.h deleted file mode 100644 index 1f262173e18..00000000000 --- a/examples/platforms/cc2538/openthread-core-cc2538-config.h +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (c) 2016, 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 cc2538 compile-time configuration constants for OpenThread. - */ - -#ifndef OPENTHREAD_CORE_CC2538_CONFIG_H_ -#define OPENTHREAD_CORE_CC2538_CONFIG_H_ - -/** - * @def OPENTHREAD_CONFIG_PLATFORM_INFO - * - * The platform-specific string to insert into the OpenThread version string. - * - */ -#define OPENTHREAD_CONFIG_PLATFORM_INFO "CC2538" - -/** - * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE - * - * Define to 1 if you want to enable software ACK timeout logic. - * - */ -#define OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE 1 - -/** - * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_RETRANSMIT_ENABLE - * - * Define to 1 if you want to enable software retransmission logic. - * - */ -#define OPENTHREAD_CONFIG_MAC_SOFTWARE_RETRANSMIT_ENABLE 1 - -/** - * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE - * - * Define to 1 if you want to enable software CSMA-CA backoff logic. - * - */ -#define OPENTHREAD_CONFIG_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE 1 - -/** - * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE - * - * Define to 1 if you want to enable software transmission security logic. - * - */ -#define OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE 0 - -/** - * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_ENERGY_SCAN_ENABLE - * - * Define to 1 if you want to enable software energy scanning logic. - * - */ -#define OPENTHREAD_CONFIG_MAC_SOFTWARE_ENERGY_SCAN_ENABLE 1 - -/** - * @def OPENTHREAD_CONFIG_NCP_HDLC_ENABLE - * - * Define to 1 to enable NCP HDLC support. - * - */ -#define OPENTHREAD_CONFIG_NCP_HDLC_ENABLE 1 - -/** - * @def OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT - * - * Enable support for using interrupt-driven radio reception. This allows - * for a single frame to be received whilst the CPU is busy processing some - * other code. - * - * To disable interrupts and just rely on polling, set this to 0. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT -#define OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT 1 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2538_WITH_CC2592 - * - * Enable support for the CC2592 range-extender front-end. - * - * This is a feature of the CC2538-CC2592 EM and other peripherals which - * extends the range of the bare CC2538 to over a kilometre line-of-sight. - * The CC2592 needs to be wired up to the RF port on the CC2538 in accordance - * with application note 130 ("Using CC2592 Front End With CC2538", TI doc - * SWRA447). - * - * If you have such a board, change this to 1. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2538_WITH_CC2592 -#define OPENTHREAD_CONFIG_CC2538_WITH_CC2592 0 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2592_PA_EN_PIN - * - * Define the pin (on port C) that connects to the CC2592 PA_EN pin. - * - * One of the 3 observable channels on the CC2538 radio module will be - * configured to take the "PA power down" signal from the radio module itself, - * invert it, and emit it on this GPIO pin. Due to hardware constraints, it - * may only be connected to a pin on GPIO port C. - * - * The default (PC3) is as per TI recommendations in AN130. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2592_PA_EN_PIN -#define OPENTHREAD_CONFIG_CC2592_PA_EN_PIN 3 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2592_LNA_EN_PIN - * - * Define the pin (on port C) that connects to the CC2592 LNA_EN pin. - * - * One of the 3 observable channels on the CC2538 radio module will be - * configured to take the "LNA power down" signal from the radio module itself, - * invert it, and emit it on this GPIO pin. Due to hardware constraints, it - * may only be connected to a pin on GPIO port C. - * - * The default (PC2) is as per TI recommendations in AN130. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2592_LNA_EN_PIN -#define OPENTHREAD_CONFIG_CC2592_LNA_EN_PIN 2 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2592_USE_HGM - * - * Enable control of the high-gain mode signal. - * - * High-gain mode is enabled through the `HGM` pin on the CC2592, which may be - * connected to any free GPIO pin for software control, or may be linked to - * VDD or 0V to hard-wire it to a given state. - * - * Set this to 0 if you have wired this pin to a power rail, or have a - * non-standard way of controlling it. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2592_USE_HGM -#define OPENTHREAD_CONFIG_CC2592_USE_HGM 1 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2538_RECEIVE_SENSITIVITY - * - * Set the CC2538 receive sensitivity. - * - * A bare CC2538 has a receive sensitivity of -88dBm. The CC2592 changes this - * to -85 or -81 depending on whether the HGM pin is high or low. If - * `OPENTHREAD_CONFIG_CC2592_USE_HGM` is 0, then this sets the receive - * sensitivity. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2538_RECEIVE_SENSITIVITY -#define OPENTHREAD_CONFIG_CC2538_RECEIVE_SENSITIVITY -88 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2538_RSSI_OFFSET - * - * Set the CC2538 RSSI offset. This calibrates the RSSI readings received from - * the CC2538 radio module to give a reading in dBm. - * - * For a standard CC2538 (no front-end), the RSSI offset is 73. - * - * For a CC2592 hard-wired in high-gain mode, an offset of 85 should be used; - * or for low-gain mode, 81. If `OPENTHREAD_CONFIG_CC2592_USE_HGM` is 0, then - * this calibrates the RSSI value accordingly. - */ -#ifndef OPENTHREAD_CONFIG_CC2538_RSSI_OFFSET -#define OPENTHREAD_CONFIG_CC2538_RSSI_OFFSET 73 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2592_HGM_PORT - * - * Define the GPIO port that the HGM pin is connected to. It may be - * connected to any available GPIO pin. - * - * The default (GPIO port D) is as per TI recommendations. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2592_HGM_PORT -#define OPENTHREAD_CONFIG_CC2592_HGM_PORT GPIO_D_BASE -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2592_HGM_PIN - * - * Define the pin on the GPIO port that the HGM pin is connected to. It - * may be connected to any available GPIO pin. - * - * The default (PD2) is as per TI recommendations. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2592_HGM_PIN -#define OPENTHREAD_CONFIG_CC2592_HGM_PIN 2 -#endif - -/** - * @def OPENTHREAD_CONFIG_CC2592_HGM_DEFAULT_STATE - * - * Define the default state of the CC2592's HGM pin. - * - * The default is to turn high-gain mode on. - * - */ -#ifndef OPENTHREAD_CONFIG_CC2592_HGM_DEFAULT_STATE -#define OPENTHREAD_CONFIG_CC2592_HGM_DEFAULT_STATE true -#endif - -/** - * @def OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE - * - * Define to 1 to enable otPlatFlash* APIs to support non-volatile storage. - * - * When defined to 1, the platform MUST implement the otPlatFlash* APIs instead of the otPlatSettings* APIs. - * - */ -#define OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE 1 - -#endif // OPENTHREAD_CORE_CC2538_CONFIG_H_ diff --git a/examples/platforms/cc2538/platform-cc2538.h b/examples/platforms/cc2538/platform-cc2538.h deleted file mode 100644 index f89b01bbb85..00000000000 --- a/examples/platforms/cc2538/platform-cc2538.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (c) 2016, 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 the platform-specific initializers. - * - */ - -#ifndef PLATFORM_CC2538_H_ -#define PLATFORM_CC2538_H_ - -#include -#include -#include -#include - -#include "cc2538-reg.h" - -// Global OpenThread instance structure -extern otInstance *sInstance; - -/** - * Initialize the debug uart - */ -void cc2538DebugUartInit(void); - -/** - * This function initializes the alarm service used by OpenThread. - * - */ -void cc2538AlarmInit(void); - -/** - * This function performs alarm driver processing. - * - * @param[in] aInstance The OpenThread instance structure. - * - */ -void cc2538AlarmProcess(otInstance *aInstance); - -/** - * This function initializes the radio service used by OpenThread. - * - */ -void cc2538RadioInit(void); - -/** - * This function performs radio driver processing. - * - * @param[in] aInstance The OpenThread instance structure. - * - */ -void cc2538RadioProcess(otInstance *aInstance); - -/** - * This function initializes the random number service used by OpenThread. - * - */ -void cc2538RandomInit(void); - -/** - * This function performs UART driver processing. - * - */ -void cc2538UartProcess(void); - -#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM -/** - * Change the state of the CC2592 HGM pin. - * - * @param aState Whether or not to enable HGM - */ -void cc2538RadioSetHgm(bool aState); - -/** - * Retrieve the state of the CC2592 HGM pin. - */ -bool cc2538RadioGetHgm(void); -#endif // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM - -typedef enum -{ - OT_CC2538_TIMER_ENERGY_SCAN, ///< Internal timer for energy scan - OT_CC2538_TIMERS_COUNT, ///< Number of internal timers -} otCC2538Timer; - -/** - * This function sets the internal timer. - * - * @param[in] aTimer The timer identifier. - * @param[in] aDelay The delay to trigger the timer, and must be no more than `INT32_MAX`. - * - */ -void cc2538SetTimer(otCC2538Timer aTimer, uint32_t aDelay); - -#endif // PLATFORM_CC2538_H_ diff --git a/examples/platforms/cc2538/radio.c b/examples/platforms/cc2538/radio.c deleted file mode 100644 index 0878f115da0..00000000000 --- a/examples/platforms/cc2538/radio.c +++ /dev/null @@ -1,1273 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file implements the OpenThread platform abstraction for radio communication. - * - */ - -#include -#include -#include -#include -#include - -#include "platform-cc2538.h" -#include "utils/code_utils.h" - -#define RFCORE_XREG_RFIRQM0 0x4008868C // RF interrupt masks -#define RFCORE_XREG_RFIRQM1 0x40088690 // RF interrupt masks -#define RFCORE_XREG_RFERRM 0x40088694 // RF error interrupt mask - -#define RFCORE_SFR_RFIRQF0_RXMASKZERO 0x00000080 // RXENABLE is now completely clear -#define RFCORE_SFR_RFIRQF0_RXPKTDONE 0x00000040 // A complete frame has been received -#define RFCORE_SFR_RFIRQF0_FRAME_ACCEPTED 0x00000020 // Frame has passed frame filtering -#define RFCORE_SFR_RFIRQF0_SRC_MATCH_FOUND 0x00000010 // Source match is found -#define RFCORE_SFR_RFIRQF0_SRC_MATCH_DONE 0x00000008 // Source matching is complete -#define RFCORE_SFR_RFIRQF0_FIFOP 0x00000004 // The number of bytes in the RX fifo is above threshold -#define RFCORE_SFR_RFIRQF0_SFD 0x00000002 // SFD has been received or transmitted -#define RFCORE_SFR_RFIRQF0_ACT_UNUSED 0x00000001 // Reserved - -#define RFCORE_XREG_RFIRQM0_RXMASKZERO 0x00000080 -#define RFCORE_XREG_RFIRQM0_RXPKTDONE 0x00000040 -#define RFCORE_XREG_RFIRQM0_FRAME_ACCEPTED 0x00000020 -#define RFCORE_XREG_RFIRQM0_SRC_MATCH_FOUND 0x00000010 -#define RFCORE_XREG_RFIRQM0_SRC_MATCH_DONE 0x00000008 -#define RFCORE_XREG_RFIRQM0_FIFOP 0x00000004 -#define RFCORE_XREG_RFIRQM0_SFD 0x00000002 -#define RFCORE_XREG_RFIRQM0_ACT_UNUSED 0x00000001 - -#define RFCORE_SFR_RFIRQF1_CSP_WAIT 0x00000020 -#define RFCORE_SFR_RFIRQF1_CSP_STOP 0x00000010 -#define RFCORE_SFR_RFIRQF1_CSP_MANINT 0x00000008 -#define RFCORE_SFR_RFIRQF1_RF_IDLE 0x00000004 -#define RFCORE_SFR_RFIRQF1_TXDONE 0x00000002 -#define RFCORE_SFR_RFIRQF1_TXACKDONE 0x00000001 - -#define RFCORE_XREG_RFIRQM1_CSP_WAIT 0x00000020 -#define RFCORE_XREG_RFIRQM1_CSP_STOP 0x00000010 -#define RFCORE_XREG_RFIRQM1_CSP_MANINT 0x00000008 -#define RFCORE_XREG_RFIRQM1_RF_IDLE 0x00000004 -#define RFCORE_XREG_RFIRQM1_TXDONE 0x00000002 -#define RFCORE_XREG_RFIRQM1_TXACKDONE 0x00000001 - -#define RFCORE_XREG_RFERRM_STROBE_ERR 0x00000040 -#define RFCORE_XREG_RFERRM_TXUNDERF 0x00000020 -#define RFCORE_XREG_RFERRM_TXOVERF 0x00000010 -#define RFCORE_XREG_RFERRM_RXUNDERF 0x00000008 -#define RFCORE_XREG_RFERRM_RXOVERF 0x00000004 -#define RFCORE_XREG_RFERRM_RXABO 0x00000002 -#define RFCORE_XREG_RFERRM_NLOCK 0x00000001 - -enum -{ - IEEE802154_MIN_LENGTH = 5, - IEEE802154_MAX_LENGTH = 127, - IEEE802154_ACK_LENGTH = 5, - IEEE802154_FRAME_TYPE_MASK = 0x7, - IEEE802154_FRAME_TYPE_ACK = 0x2, - IEEE802154_FRAME_PENDING = 1 << 4, - IEEE802154_ACK_REQUEST = 1 << 5, - IEEE802154_DSN_OFFSET = 2, -}; - -enum -{ - CC2538_RSSI_OFFSET = OPENTHREAD_CONFIG_CC2538_RSSI_OFFSET, - // TI AN130 (SWRA447) Table 4 (bottom of page 3) - CC2592_RSSI_OFFSET_HGM = 85, - CC2592_RSSI_OFFSET_LGM = 81, - CC2538_CRC_BIT_MASK = 0x80, - CC2538_LQI_BIT_MASK = 0x7f, -}; - -// All values in dBm -enum -{ - CC2538_RECEIVE_SENSITIVITY = OPENTHREAD_CONFIG_CC2538_RECEIVE_SENSITIVITY, - // TI AN130 (SWRA447) Table 3 (middle of page 3) - CC2592_RECEIVE_SENSITIVITY_LGM = -99, - CC2592_RECEIVE_SENSITIVITY_HGM = -101, -}; - -typedef struct TxPowerTable -{ - int8_t mTxPowerVal; - uint8_t mTxPowerReg; -} TxPowerTable; - -// The transmit power table. -static const TxPowerTable sTxPowerTable[] = { -#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592 - // CC2538 using CC2592 PA - // Values are from AN130 table 6 (page 4) - {22, 0xFF}, // 22.0dBm =~ 158.5mW - {21, 0xD5}, // 20.9dBm =~ 123.0mW - {20, 0xC5}, // 20.1dBm =~ 102.3mW - {19, 0xB0}, // 19.0dBm =~ 79.4mW - {18, 0xA1}, // 17.8dBm =~ 60.3mW - {16, 0x91}, // 16.4dBm =~ 43.7mW - {15, 0x88}, // 14.9dBm =~ 30.9mW - {13, 0x72}, // 13.0dBm =~ 20.0mW - {11, 0x62}, // 11.0dBm =~ 12.6mW - {10, 0x58}, // 9.5dBm =~ 8.9mW - {8, 0x42}, // 7.5dBm =~ 5.6mW -#else - // CC2538 operating "bare foot" - // Values are from SmartRF Studio 2.4.0 - {7, 0xFF}, // - {5, 0xED}, // - {3, 0xD5}, // - {1, 0xC5}, // - {0, 0xB6}, // - {-1, 0xB0}, // - {-3, 0xA1}, // - {-5, 0x91}, // - {-7, 0x88}, // - {-9, 0x72}, // - {-11, 0x62}, // - {-13, 0x58}, // - {-15, 0x42}, // - {-24, 0x00}, // -#endif -}; - -static otRadioFrame sTransmitFrame; -static otRadioFrame sReceiveFrame; -static otError sTransmitError; -static otError sReceiveError; - -static uint8_t sTransmitPsdu[IEEE802154_MAX_LENGTH]; -static uint8_t sReceivePsdu[IEEE802154_MAX_LENGTH]; -static uint8_t sChannel = 0; -static int8_t sTxPower = 0; - -static otRadioState sState = OT_RADIO_STATE_DISABLED; -static bool sIsReceiverEnabled = false; - -#if OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT -// Debugging _and_ logging are enabled, so if there's a dropped frame -// we'll need to store the length here as using snprintf from an interrupt -// handler is not a good idea. -static uint8_t sDroppedFrameLength = 0; -#endif - -static int8_t cc2538RadioGetRssiOffset(void); - -void enableReceiver(void) -{ - if (!sIsReceiverEnabled) - { - otLogInfoPlat("Enabling receiver", NULL); - - // flush rxfifo - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - - // enable receiver - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RXON; - sIsReceiverEnabled = true; - } -} - -void disableReceiver(void) -{ - if (sIsReceiverEnabled) - { - otLogInfoPlat("Disabling receiver", NULL); - - while (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE) - ; - - // flush rxfifo - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - - if (HWREG(RFCORE_XREG_RXENABLE) != 0) - { - // disable receiver - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RFOFF; - } - - sIsReceiverEnabled = false; - } -} - -void setChannel(uint8_t aChannel) -{ - if (sChannel != aChannel) - { - bool enabled = false; - - if (sIsReceiverEnabled) - { - disableReceiver(); - enabled = true; - } - - otLogInfoPlat("Channel=%d", aChannel); - - HWREG(RFCORE_XREG_FREQCTRL) = 11 + (aChannel - 11) * 5; - sChannel = aChannel; - - if (enabled) - { - enableReceiver(); - } - } -} - -void setTxPower(int8_t aTxPower) -{ - uint8_t i = 0; - - if (sTxPower != aTxPower) - { - otLogInfoPlat("TxPower=%d", aTxPower); - - for (i = sizeof(sTxPowerTable) / sizeof(TxPowerTable) - 1; i > 0; i--) - { - if (aTxPower < sTxPowerTable[i].mTxPowerVal) - { - break; - } - } - - HWREG(RFCORE_XREG_TXPOWER) = sTxPowerTable[i].mTxPowerReg; - sTxPower = aTxPower; - } -} - -static bool cc2538SrcMatchEnabled(void) -{ - return (HWREG(RFCORE_XREG_FRMCTRL1) & RFCORE_XREG_FRMCTRL1_PENDING_OR) == 0; -} - -static bool cc2538GetSrcMatchFoundIntFlag(void) -{ - bool flag = (HWREG(RFCORE_SFR_RFIRQF0) & RFCORE_SFR_RFIRQF0_SRC_MATCH_FOUND) != 0; - if (flag) - { - HWREG(RFCORE_SFR_RFIRQF0) &= ~RFCORE_SFR_RFIRQF0_SRC_MATCH_FOUND; - } - return flag; -} - -void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) -{ - OT_UNUSED_VARIABLE(aInstance); - - // EUI64 is in a mixed-endian format. Split in two halves, each 32-bit - // half is in little-endian format (machine endian). However, the - // most significant part of the EUI64 comes first, so we can't cheat - // with a uint64_t! - // - // See https://e2e.ti.com/support/wireless_connectivity/low_power_rf_tools/f/155/p/307344/1072252 - - volatile uint32_t *eui64 = &HWREG(IEEE_EUI64); - - // Read first 32-bits - uint32_t part = eui64[0]; - for (uint8_t i = 0; i < (OT_EXT_ADDRESS_SIZE / 2); i++) - { - aIeeeEui64[3 - i] = part; - part >>= 8; - } - - // Read the last 32-bits - part = eui64[1]; - for (uint8_t i = 0; i < (OT_EXT_ADDRESS_SIZE / 2); i++) - { - aIeeeEui64[7 - i] = part; - part >>= 8; - } -} - -void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanid) -{ - OT_UNUSED_VARIABLE(aInstance); - - otLogInfoPlat("PANID=%X", aPanid); - - HWREG(RFCORE_FFSM_PAN_ID0) = aPanid & 0xFF; - HWREG(RFCORE_FFSM_PAN_ID1) = aPanid >> 8; -} - -void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - otLogInfoPlat("ExtAddr=%X%X%X%X%X%X%X%X", aAddress->m8[7], aAddress->m8[6], aAddress->m8[5], aAddress->m8[4], - aAddress->m8[3], aAddress->m8[2], aAddress->m8[1], aAddress->m8[0]); - - for (int i = 0; i < 8; i++) - { - ((volatile uint32_t *)RFCORE_FFSM_EXT_ADDR0)[i] = aAddress->m8[i]; - } -} - -void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - otLogInfoPlat("ShortAddr=%X", aAddress); - - HWREG(RFCORE_FFSM_SHORT_ADDR0) = aAddress & 0xFF; - HWREG(RFCORE_FFSM_SHORT_ADDR1) = aAddress >> 8; -} - -void cc2538RadioInit(void) -{ - sTransmitFrame.mLength = 0; - sTransmitFrame.mPsdu = sTransmitPsdu; - sReceiveFrame.mLength = 0; - sReceiveFrame.mPsdu = sReceivePsdu; - -#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT - // Enable interrupts for RX/TX, interrupt 26. - // That's NVIC index 0 (26 >> 5) bit 26 (26 & 0x1f). - HWREG(NVIC_EN0 + (0 * 4)) = (1 << 26); - HWREG(RFCORE_XREG_RFIRQM0) |= RFCORE_XREG_RFIRQM0_RXPKTDONE; -#endif - - // enable clock - HWREG(SYS_CTRL_RCGCRFC) = SYS_CTRL_RCGCRFC_RFC0; - HWREG(SYS_CTRL_SCGCRFC) = SYS_CTRL_SCGCRFC_RFC0; - HWREG(SYS_CTRL_DCGCRFC) = SYS_CTRL_DCGCRFC_RFC0; - - // Table 23-7. - HWREG(RFCORE_XREG_AGCCTRL1) = 0x15; - HWREG(RFCORE_XREG_TXFILTCFG) = 0x09; - HWREG(ANA_REGS_BASE + ANA_REGS_O_IVCTRL) = 0x0b; - - HWREG(RFCORE_XREG_CCACTRL0) = 0xf8; - HWREG(RFCORE_XREG_FIFOPCTRL) = IEEE802154_MAX_LENGTH; - - HWREG(RFCORE_XREG_FRMCTRL0) = RFCORE_XREG_FRMCTRL0_AUTOCRC | RFCORE_XREG_FRMCTRL0_AUTOACK; - - // default: SRCMATCH.SRC_MATCH_EN(1), SRCMATCH.AUTOPEND(1), - // SRCMATCH.PEND_DATAREQ_ONLY(1), RFCORE_XREG_FRMCTRL1_PENDING_OR(0) - - HWREG(RFCORE_XREG_TXPOWER) = sTxPowerTable[0].mTxPowerReg; - sTxPower = sTxPowerTable[0].mTxPowerVal; - -#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592 - // PA_EN pin configuration. - // Step 1. make it an output - HWREG(GPIO_C_BASE | GPIO_O_DIR) |= GPIO_PIN(OPENTHREAD_CONFIG_CC2592_PA_EN_PIN); - // Step 2. Route PA_PD to OBS0 and invert it to produce PA_EN - HWREG_ARR(RFCORE_XREG_RFC_OBS_CTRL, 0) = RFCORE_XREG_RFC_OBS_POL_INV // Invert the output - | RFCORE_XREG_RFC_OBS_MUX_PA_PD; // PA "power down" signal - // Step 3. Connect the selected pin to OBS0 and enable OBS0. - HWREG_ARR(CCTEST_OBSSEL, OPENTHREAD_CONFIG_CC2592_PA_EN_PIN) = CCTEST_OBSSEL_EN // Enable the output - | CCTEST_OBSSEL_SEL_OBS0; // Select OBS0 - - // LNA_EN pin configuration. - HWREG(GPIO_C_BASE | GPIO_O_DIR) |= GPIO_PIN(OPENTHREAD_CONFIG_CC2592_LNA_EN_PIN); - HWREG_ARR(RFCORE_XREG_RFC_OBS_CTRL, 1) = RFCORE_XREG_RFC_OBS_POL_INV | RFCORE_XREG_RFC_OBS_MUX_LNA_PD; - HWREG_ARR(CCTEST_OBSSEL, OPENTHREAD_CONFIG_CC2592_LNA_EN_PIN) = CCTEST_OBSSEL_EN | CCTEST_OBSSEL_SEL_OBS1; - -#if OPENTHREAD_CONFIG_CC2592_USE_HGM - // HGM pin configuration. Set the pin state first so we don't glitch. - cc2538RadioSetHgm(OPENTHREAD_CONFIG_CC2592_HGM_DEFAULT_STATE); - HWREG(OPENTHREAD_CONFIG_CC2592_HGM_PORT | GPIO_O_DIR) |= GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN); -#endif // OPENTHREAD_CONFIG_CC2592_USE_HGM -#endif // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 - - otLogInfoPlat("Initialized", NULL); -} - -#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM -void cc2538RadioSetHgm(bool aState) -{ - if (aState) - { - HWREG_ARR(OPENTHREAD_CONFIG_CC2592_HGM_PORT, GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN)) = - GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN); - } - else - { - HWREG_ARR(OPENTHREAD_CONFIG_CC2592_HGM_PORT, GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN)) = 0; - } -} - -bool cc2538RadioGetHgm(void) -{ - if (HWREG_ARR(OPENTHREAD_CONFIG_CC2592_HGM_PORT, GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN)) & - GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN)) - { - return true; - } - else - { - return false; - } -} -#endif // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM - -bool otPlatRadioIsEnabled(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - return (sState != OT_RADIO_STATE_DISABLED) ? true : false; -} - -otError otPlatRadioEnable(otInstance *aInstance) -{ - if (!otPlatRadioIsEnabled(aInstance)) - { - otLogDebgPlat("State=OT_RADIO_STATE_SLEEP", NULL); - sState = OT_RADIO_STATE_SLEEP; - } - - return OT_ERROR_NONE; -} - -otError otPlatRadioDisable(otInstance *aInstance) -{ - if (otPlatRadioIsEnabled(aInstance)) - { - otLogDebgPlat("State=OT_RADIO_STATE_DISABLED", NULL); - sState = OT_RADIO_STATE_DISABLED; - } - - return OT_ERROR_NONE; -} - -otError otPlatRadioSleep(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_INVALID_STATE; - - if (sState == OT_RADIO_STATE_SLEEP || sState == OT_RADIO_STATE_RECEIVE) - { - otLogDebgPlat("State=OT_RADIO_STATE_SLEEP", NULL); - error = OT_ERROR_NONE; - sState = OT_RADIO_STATE_SLEEP; - disableReceiver(); - } - - return error; -} - -otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_INVALID_STATE; - - if (sState != OT_RADIO_STATE_DISABLED) - { - otLogDebgPlat("State=OT_RADIO_STATE_RECEIVE", NULL); - - error = OT_ERROR_NONE; - sState = OT_RADIO_STATE_RECEIVE; - setChannel(aChannel); - sReceiveFrame.mChannel = aChannel; - enableReceiver(); - } - - return error; -} - -static void setupTransmit(otRadioFrame *aFrame) -{ - int i; - - // wait for current TX operation to complete, if any. - while (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE) - ; - - // flush txfifo - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHTX; - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHTX; - - // frame length - HWREG(RFCORE_SFR_RFDATA) = aFrame->mLength; - - // frame data - for (i = 0; i < aFrame->mLength; i++) - { - HWREG(RFCORE_SFR_RFDATA) = aFrame->mPsdu[i]; - } - - setChannel(aFrame->mChannel); -} - -otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_INVALID_STATE; - - if (sState == OT_RADIO_STATE_RECEIVE) - { - int i; - - error = OT_ERROR_NONE; - sState = OT_RADIO_STATE_TRANSMIT; - sTransmitError = OT_ERROR_NONE; - - setupTransmit(aFrame); - - // Set up a counter to inform us if we get stuck. - i = 1000000; - - // Wait for radio to enter receive state. - while ((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_RX_ACTIVE) == 0) - { - // Count down the cycles, and emit a message if we get to zero. - // Ideally, we should never get there! - if (i) - { - i--; - } - else - { - otLogCritPlat("Radio is stuck!!! FSMSTAT0=0x%08x FSMSTAT1=0x%08x RFERRF=0x%08x", - HWREG(RFCORE_XREG_FSMSTAT0), HWREG(RFCORE_XREG_FSMSTAT1), HWREG(RFCORE_SFR_RFERRF)); - i = 1000000; - } - - // Ensure we haven't overflowed the RX buffer in the mean time, as this - // will cause a deadlock here otherwise. Similarly, if we see an aborted - // RX, handle that here too to prevent deadlock. - if (HWREG(RFCORE_SFR_RFERRF) & (RFCORE_SFR_RFERRF_RXOVERF | RFCORE_SFR_RFERRF_RXABO)) - { - if (HWREG(RFCORE_SFR_RFERRF) & RFCORE_SFR_RFERRF_RXOVERF) - { - otLogCritPlat("RX Buffer Overflow detected", NULL); - } - - if (HWREG(RFCORE_SFR_RFERRF) & RFCORE_SFR_RFERRF_RXABO) - { - otLogCritPlat("Aborted RX detected", NULL); - } - - // Flush the RX buffer - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - } - - // Check for idle state. After flushing the RX buffer, we may wind up here. - if (!(HWREG(RFCORE_XREG_FSMSTAT1) & (RFCORE_XREG_FSMSTAT1_TX_ACTIVE | RFCORE_XREG_FSMSTAT1_RX_ACTIVE))) - { - otLogCritPlat("Idle state detected", NULL); - - // In this case, the state of our driver mis-matches our state. So force - // matters by clearing our channel variable and calling setChannel. This - // should bring our radio into the RX state, which should allow us to go - // into TX. - sChannel = 0; - setupTransmit(aFrame); - } - } - - // wait for valid rssi - while ((HWREG(RFCORE_XREG_RSSISTAT) & RFCORE_XREG_RSSISTAT_RSSI_VALID) == 0) - ; - - otEXPECT_ACTION(((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_CCA) && - !((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_SFD))), - sTransmitError = OT_ERROR_CHANNEL_ACCESS_FAILURE); - - // begin transmit - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_TXON; - - otPlatRadioTxStarted(aInstance, aFrame); - - while (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE) - ; - - otLogDebgPlat("Transmitted %d bytes", aFrame->mLength); - } - -exit: - return error; -} - -otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - return &sTransmitFrame; -} - -int8_t otPlatRadioGetRssi(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - int8_t rssi = OT_RADIO_RSSI_INVALID; - - if ((HWREG(RFCORE_XREG_RSSISTAT) & RFCORE_XREG_RSSISTAT_RSSI_VALID) != 0) - { - rssi = HWREG(RFCORE_XREG_RSSI) & 0xff; - - if (rssi > cc2538RadioGetRssiOffset() - 128) - { - rssi -= cc2538RadioGetRssiOffset(); - } - else - { - rssi = -128; - } - } - - return rssi; -} - -otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - return OT_RADIO_CAPS_ENERGY_SCAN; -} - -static bool cc2538RadioGetPromiscuous(void) -{ - return (HWREG(RFCORE_XREG_FRMFILT0) & RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN) == 0; -} - -bool otPlatRadioGetPromiscuous(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - return cc2538RadioGetPromiscuous(); -} - -static int8_t cc2538RadioGetRssiOffset(void) -{ -#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM - if (cc2538RadioGetHgm()) - { - return CC2592_RSSI_OFFSET_HGM; - } - else - { - return CC2592_RSSI_OFFSET_LGM; - } -#else // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM - return CC2538_RSSI_OFFSET; -#endif // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM -} - -void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) -{ - OT_UNUSED_VARIABLE(aInstance); - - otLogInfoPlat("PromiscuousMode=%d", aEnable ? 1 : 0); - - if (aEnable) - { - HWREG(RFCORE_XREG_FRMFILT0) &= ~RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN; - } - else - { - HWREG(RFCORE_XREG_FRMFILT0) |= RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN; - } -} - -static void readFrame(void) -{ - uint8_t length; - uint8_t crcCorr; - int i; - - /* - * There is already a frame present in the buffer, return early so - * we do not overwrite it (hopefully we'll catch it on the next run). - */ - otEXPECT(sReceiveFrame.mLength == 0); - - otEXPECT(sState == OT_RADIO_STATE_RECEIVE || sState == OT_RADIO_STATE_TRANSMIT); - otEXPECT((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFOP) != 0); - - // read length - length = HWREG(RFCORE_SFR_RFDATA); - otEXPECT(IEEE802154_MIN_LENGTH <= length && length <= IEEE802154_MAX_LENGTH); - -#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE -#error Time sync requires the timestamp of SFD rather than that of rx done! -#else - // Timestamp - if (cc2538RadioGetPromiscuous()) -#endif - { - // The current driver only supports milliseconds resolution. - sReceiveFrame.mInfo.mRxInfo.mTimestamp = otPlatAlarmMilliGetNow() * 1000; - } - - // read psdu - for (i = 0; i < length - 2; i++) - { - sReceiveFrame.mPsdu[i] = HWREG(RFCORE_SFR_RFDATA); - } - - sReceiveFrame.mInfo.mRxInfo.mRssi = (int8_t)HWREG(RFCORE_SFR_RFDATA) - cc2538RadioGetRssiOffset(); - crcCorr = HWREG(RFCORE_SFR_RFDATA); - - if (crcCorr & CC2538_CRC_BIT_MASK) - { - sReceiveFrame.mLength = length; - sReceiveFrame.mInfo.mRxInfo.mLqi = crcCorr & CC2538_LQI_BIT_MASK; - - if (length > IEEE802154_ACK_LENGTH) - { - // Set ACK FP flag for the received frame according to whether SRC_MATCH_FOUND was triggered just before - // if SRC MATCH is not enabled, SRC_MATCH_FOUND is not triggered and all ACK FP is always set - sReceiveFrame.mInfo.mRxInfo.mAckedWithFramePending = - cc2538SrcMatchEnabled() ? cc2538GetSrcMatchFoundIntFlag() : true; - } - } - else - { - // resets rxfifo - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; -#if OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT - // Debugging _and_ logging are enabled, it may not be safe to do - // logging if we're in the interrupt context, so just stash the - // length and do the logging later. - sDroppedFrameLength = length; -#else - otLogDebgPlat("Dropping %d received bytes (Invalid CRC)", length); -#endif - } - - // check for rxfifo overflow - if ((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFOP) != 0 && - (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFO) == 0) - { - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; - } - -exit: - return; -} - -void cc2538RadioProcess(otInstance *aInstance) -{ -#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT - // Disable the receive interrupt so that sReceiveFrame doesn't get - // blatted by the interrupt handler while we're polling. - HWREG(RFCORE_XREG_RFIRQM0) &= ~RFCORE_XREG_RFIRQM0_RXPKTDONE; -#endif - - readFrame(); - -#if OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT - if (sDroppedFrameLength != 0) - { - otLogDebgPlat("Dropping %d received bytes (Invalid CRC)", sDroppedFrameLength); - sDroppedFrameLength = 0; - } -#endif - - if ((sState == OT_RADIO_STATE_RECEIVE && sReceiveFrame.mLength > 0) || - (sState == OT_RADIO_STATE_TRANSMIT && sReceiveFrame.mLength > IEEE802154_ACK_LENGTH)) - { -#if OPENTHREAD_CONFIG_DIAG_ENABLE - - if (otPlatDiagModeGet()) - { - otPlatDiagRadioReceiveDone(aInstance, &sReceiveFrame, sReceiveError); - } - else -#endif - { - // signal MAC layer for each received frame if promiscuous is enabled - // otherwise only signal MAC layer for non-ACK frame - if (((HWREG(RFCORE_XREG_FRMFILT0) & RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN) == 0) || - (sReceiveFrame.mLength > IEEE802154_ACK_LENGTH)) - { - otLogDebgPlat("Received %d bytes", sReceiveFrame.mLength); - otPlatRadioReceiveDone(aInstance, &sReceiveFrame, sReceiveError); - } - } - } - - if (sState == OT_RADIO_STATE_TRANSMIT) - { - if (sTransmitError != OT_ERROR_NONE || (sTransmitFrame.mPsdu[0] & IEEE802154_ACK_REQUEST) == 0) - { - if (sTransmitError != OT_ERROR_NONE) - { - otLogDebgPlat("Transmit failed ErrorCode=%d", sTransmitError); - } - - sState = OT_RADIO_STATE_RECEIVE; - -#if OPENTHREAD_CONFIG_DIAG_ENABLE - - if (otPlatDiagModeGet()) - { - otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame, sTransmitError); - } - else -#endif - { - otPlatRadioTxDone(aInstance, &sTransmitFrame, NULL, sTransmitError); - } - } - else if (sReceiveFrame.mLength == IEEE802154_ACK_LENGTH && - (sReceiveFrame.mPsdu[0] & IEEE802154_FRAME_TYPE_MASK) == IEEE802154_FRAME_TYPE_ACK && - (sReceiveFrame.mPsdu[IEEE802154_DSN_OFFSET] == sTransmitFrame.mPsdu[IEEE802154_DSN_OFFSET])) - { - sState = OT_RADIO_STATE_RECEIVE; - - otPlatRadioTxDone(aInstance, &sTransmitFrame, &sReceiveFrame, sTransmitError); - } - } - - sReceiveFrame.mLength = 0; - -#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT - // Turn the receive interrupt handler back on now the buffer is clear. - HWREG(RFCORE_XREG_RFIRQM0) |= RFCORE_XREG_RFIRQM0_RXPKTDONE; -#endif -} - -void RFCoreRxTxIntHandler(void) -{ -#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT - if (HWREG(RFCORE_SFR_RFIRQF0) & RFCORE_SFR_RFIRQF0_RXPKTDONE) - { - readFrame(); - - if (sReceiveFrame.mLength > 0) - { - // A frame has been received, disable the interrupt handler - // until the main loop has dealt with this previous frame, - // otherwise we might overwrite it whilst it is being read. - HWREG(RFCORE_XREG_RFIRQM0) &= ~RFCORE_XREG_RFIRQM0_RXPKTDONE; - } - } -#endif - - HWREG(RFCORE_SFR_RFIRQF0) = 0; -} - -void RFCoreErrIntHandler(void) -{ - HWREG(RFCORE_SFR_RFERRF) = 0; -} - -uint32_t getSrcMatchEntriesEnableStatus(bool aShort) -{ - uint32_t status = 0; - uint32_t *addr = aShort ? (uint32_t *)RFCORE_XREG_SRCSHORTEN0 : (uint32_t *)RFCORE_XREG_SRCEXTEN0; - - for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++) - { - status |= HWREG(addr++) << (i * 8); - } - - return status; -} - -int8_t findSrcMatchShortEntry(uint16_t aShortAddress) -{ - int8_t entry = -1; - uint16_t shortAddr; - uint32_t bitMask; - uint32_t *addr = NULL; - uint32_t status = getSrcMatchEntriesEnableStatus(true); - - for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_SHORT_ENTRIES; i++) - { - bitMask = 0x00000001 << i; - - if ((status & bitMask) == 0) - { - continue; - } - - addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE + (i * RFCORE_XREG_SRCMATCH_SHORT_ENTRY_OFFSET); - - shortAddr = HWREG(addr + 2); - shortAddr |= HWREG(addr + 3) << 8; - - if ((shortAddr == aShortAddress)) - { - entry = i; - break; - } - } - - return entry; -} - -int8_t findSrcMatchExtEntry(const otExtAddress *aExtAddress) -{ - int8_t entry = -1; - uint32_t bitMask; - uint32_t *addr = NULL; - uint32_t status = getSrcMatchEntriesEnableStatus(false); - - for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_EXT_ENTRIES; i++) - { - uint8_t j = 0; - bitMask = 0x00000001 << 2 * i; - - if ((status & bitMask) == 0) - { - continue; - } - - addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE + (i * RFCORE_XREG_SRCMATCH_EXT_ENTRY_OFFSET); - - for (j = 0; j < sizeof(otExtAddress); j++) - { - if (HWREG(addr + j) != aExtAddress->m8[j]) - { - break; - } - } - - if (j == sizeof(otExtAddress)) - { - entry = i; - break; - } - } - - return entry; -} - -void setSrcMatchEntryEnableStatus(bool aShort, uint8_t aEntry, bool aEnable) -{ - uint8_t entry = aShort ? aEntry : (2 * aEntry); - uint8_t index = entry / 8; - uint32_t *addrEn = aShort ? (uint32_t *)RFCORE_XREG_SRCSHORTEN0 : (uint32_t *)RFCORE_XREG_SRCEXTEN0; - uint32_t *addrAutoPendEn = aShort ? (uint32_t *)RFCORE_FFSM_SRCSHORTPENDEN0 : (uint32_t *)RFCORE_FFSM_SRCEXTPENDEN0; - uint32_t bitMask = 0x00000001; - - if (aEnable) - { - HWREG(addrEn + index) |= (bitMask) << (entry % 8); - HWREG(addrAutoPendEn + index) |= (bitMask) << (entry % 8); - } - else - { - HWREG(addrEn + index) &= ~((bitMask) << (entry % 8)); - HWREG(addrAutoPendEn + index) &= ~((bitMask) << (entry % 8)); - } -} - -int8_t findSrcMatchAvailEntry(bool aShort) -{ - int8_t entry = -1; - uint32_t bitMask; - uint32_t shortEnableStatus = getSrcMatchEntriesEnableStatus(true); - uint32_t extEnableStatus = getSrcMatchEntriesEnableStatus(false); - - otLogDebgPlat("Short enable status: 0x%x", shortEnableStatus); - otLogDebgPlat("Ext enable status: 0x%x", extEnableStatus); - - if (aShort) - { - bitMask = 0x00000001; - - for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_SHORT_ENTRIES; i++) - { - if ((extEnableStatus & bitMask) == 0) - { - if ((shortEnableStatus & bitMask) == 0) - { - entry = i; - break; - } - } - - if (i % 2 == 1) - { - extEnableStatus = extEnableStatus >> 2; - } - - shortEnableStatus = shortEnableStatus >> 1; - } - } - else - { - bitMask = 0x00000003; - - for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_EXT_ENTRIES; i++) - { - if (((extEnableStatus | shortEnableStatus) & bitMask) == 0) - { - entry = i; - break; - } - - extEnableStatus = extEnableStatus >> 2; - shortEnableStatus = shortEnableStatus >> 2; - } - } - - return entry; -} - -void cc2538EnergyScanTimerHandler(void) -{ - int8_t rssi = otPlatRadioGetRssi(sInstance); - - disableReceiver(); - - HWREG(RFCORE_XREG_FRMCTRL0) &= ~RFCORE_XREG_FRMCTRL0_ENERGY_SCAN; - HWREG(RFCORE_XREG_FREQCTRL) = 11 + (sChannel - 11) * 5; - - if (sIsReceiverEnabled) - { - enableReceiver(); - } - - otPlatRadioEnergyScanDone(sInstance, rssi); -} - -void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable) -{ - OT_UNUSED_VARIABLE(aInstance); - - otLogInfoPlat("EnableSrcMatch=%d", aEnable ? 1 : 0); - - if (aEnable) - { - // only set FramePending when ack for data poll if there are queued messages - // for entries in the source match table. - HWREG(RFCORE_XREG_FRMCTRL1) &= ~RFCORE_XREG_FRMCTRL1_PENDING_OR; - } - else - { - // set FramePending for all ack. - HWREG(RFCORE_XREG_FRMCTRL1) |= RFCORE_XREG_FRMCTRL1_PENDING_OR; - } -} - -otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, uint16_t aShortAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_NONE; - int8_t entry = findSrcMatchAvailEntry(true); - uint32_t *addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE; - - otLogDebgPlat("Add ShortAddr entry: %d", entry); - - otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_BUFS); - - addr += (entry * RFCORE_XREG_SRCMATCH_SHORT_ENTRY_OFFSET); - - HWREG(addr++) = HWREG(RFCORE_FFSM_PAN_ID0); - HWREG(addr++) = HWREG(RFCORE_FFSM_PAN_ID1); - HWREG(addr++) = aShortAddress & 0xFF; - HWREG(addr++) = aShortAddress >> 8; - - setSrcMatchEntryEnableStatus(true, (uint8_t)(entry), true); - -exit: - return error; -} - -otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_NONE; - int8_t entry = findSrcMatchAvailEntry(false); - uint32_t *addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE; - - otLogDebgPlat("Add ExtAddr entry: %d", entry); - - otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_BUFS); - - addr += (entry * RFCORE_XREG_SRCMATCH_EXT_ENTRY_OFFSET); - - for (uint8_t i = 0; i < sizeof(otExtAddress); i++) - { - HWREG(addr++) = aExtAddress->m8[i]; - } - - setSrcMatchEntryEnableStatus(false, (uint8_t)(entry), true); - -exit: - return error; -} - -otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, uint16_t aShortAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_NONE; - int8_t entry = findSrcMatchShortEntry(aShortAddress); - - otLogDebgPlat("Clear ShortAddr entry: %d", entry); - - otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_ADDRESS); - - setSrcMatchEntryEnableStatus(true, (uint8_t)(entry), false); - -exit: - return error; -} - -otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_NONE; - int8_t entry = findSrcMatchExtEntry(aExtAddress); - - otLogDebgPlat("Clear ExtAddr entry: %d", entry); - - otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_ADDRESS); - - setSrcMatchEntryEnableStatus(false, (uint8_t)(entry), false); - -exit: - return error; -} - -void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - uint32_t *addrEn = (uint32_t *)RFCORE_XREG_SRCSHORTEN0; - uint32_t *addrAutoPendEn = (uint32_t *)RFCORE_FFSM_SRCSHORTPENDEN0; - - otLogDebgPlat("Clear ShortAddr entries", NULL); - - for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++) - { - HWREG(addrEn++) = 0; - HWREG(addrAutoPendEn++) = 0; - } -} - -void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - - uint32_t *addrEn = (uint32_t *)RFCORE_XREG_SRCEXTEN0; - uint32_t *addrAutoPendEn = (uint32_t *)RFCORE_FFSM_SRCEXTPENDEN0; - - otLogDebgPlat("Clear ExtAddr entries", NULL); - - for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++) - { - HWREG(addrEn++) = 0; - HWREG(addrAutoPendEn++) = 0; - } -} - -otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration) -{ - OT_UNUSED_VARIABLE(aInstance); - - otLogInfoPlat("ScanChannel=%d", aScanChannel); - - if (aScanChannel != sChannel) - { - if (sIsReceiverEnabled) - { - disableReceiver(); - } - - HWREG(RFCORE_XREG_FREQCTRL) = 11 + (aScanChannel - 11) * 5; - - enableReceiver(); - } - else if (!sIsReceiverEnabled) - { - enableReceiver(); - } - - // Collect peak signal strength - HWREG(RFCORE_XREG_FRMCTRL0) |= RFCORE_XREG_FRMCTRL0_ENERGY_SCAN; - - cc2538SetTimer(OT_CC2538_TIMER_ENERGY_SCAN, aScanDuration); - - return OT_ERROR_NONE; -} - -otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower) -{ - OT_UNUSED_VARIABLE(aInstance); - - otError error = OT_ERROR_NONE; - - otEXPECT_ACTION(aPower != NULL, error = OT_ERROR_INVALID_ARGS); - *aPower = sTxPower; - -exit: - return error; -} - -otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower) -{ - OT_UNUSED_VARIABLE(aInstance); - - setTxPower(aPower); - return OT_ERROR_NONE; -} - -otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t *aThreshold) -{ - OT_UNUSED_VARIABLE(aInstance); - OT_UNUSED_VARIABLE(aThreshold); - - return OT_ERROR_NOT_IMPLEMENTED; -} - -otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aThreshold) -{ - OT_UNUSED_VARIABLE(aInstance); - OT_UNUSED_VARIABLE(aThreshold); - - return OT_ERROR_NOT_IMPLEMENTED; -} - -int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - -#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM - if (cc2538RadioGetHgm()) - { - return CC2592_RECEIVE_SENSITIVITY_HGM; - } - else - { - return CC2592_RECEIVE_SENSITIVITY_LGM; - } -#else // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM - return CC2538_RECEIVE_SENSITIVITY; -#endif // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM -} diff --git a/examples/platforms/cc2538/rom-utility.h b/examples/platforms/cc2538/rom-utility.h deleted file mode 100644 index d0686efdee7..00000000000 --- a/examples/platforms/cc2538/rom-utility.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2016, 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. - */ - -#ifndef ROM_UTILITY_H_ -#define ROM_UTILITY_H_ - -#define ROM_API_TABLE_ADDR 0x00000048 - -typedef uint32_t (*volatile FPTR_CRC32_T)(uint8_t * /*pData*/, uint32_t /*byteCount*/); -typedef uint32_t (*volatile FPTR_GETFLSIZE_T)(void); -typedef uint32_t (*volatile FPTR_GETCHIPID_T)(void); -typedef int32_t (*volatile FPTR_PAGEERASE_T)(uint32_t /*FlashAddr*/, uint32_t /*Size*/); -typedef int32_t (*volatile FPTR_PROGFLASH_T)(uint32_t * /*pRamData*/, uint32_t /*FlashAdr*/, uint32_t /*ByteCount*/); -typedef void (*volatile FPTR_RESETDEV_T)(void); -typedef void *(*volatile FPTR_MEMSET_T)(void * /*s*/, int32_t /*c*/, uint32_t /*n*/); -typedef void *(*volatile FPTR_MEMCPY_T)(void * /*s1*/, const void * /*s2*/, uint32_t /*n*/); -typedef int32_t (*volatile FPTR_MEMCMP_T)(const void * /*s1*/, const void * /*s2*/, uint32_t /*n*/); -typedef void *(*volatile FPTR_MEMMOVE_T)(void * /*s1*/, const void * /*s2*/, uint32_t /*n*/); - -typedef struct -{ - FPTR_CRC32_T Crc32; - FPTR_GETFLSIZE_T GetFlashSize; - FPTR_GETCHIPID_T GetChipId; - FPTR_PAGEERASE_T PageErase; - FPTR_PROGFLASH_T ProgramFlash; - FPTR_RESETDEV_T ResetDevice; - FPTR_MEMSET_T memset; - FPTR_MEMCPY_T memcpy; - FPTR_MEMCMP_T memcmp; - FPTR_MEMMOVE_T memmove; -} ROM_API_T; - -// clang-format off - -#define P_ROM_API ((ROM_API_T*)ROM_API_TABLE_ADDR) - -#define ROM_Crc32(a,b) P_ROM_API->Crc32(a,b) -#define ROM_GetFlashSize() P_ROM_API->GetFlashSize() -#define ROM_GetChipId() P_ROM_API->GetChipId() -#define ROM_PageErase(a,b) P_ROM_API->PageErase(a,b) -#define ROM_ProgramFlash(a,b,c) P_ROM_API->ProgramFlash(a,b,c) -#define ROM_ResetDevice() P_ROM_API->ResetDevice() -#define ROM_Memset(a,b,c) P_ROM_API->memset(a,b,c) -#define ROM_Memcpy(a,b,c) P_ROM_API->memcpy(a,b,c) -#define ROM_Memcmp(a,b,c) P_ROM_API->memcmp(a,b,c) -#define ROM_Memmove(a,b,c) P_ROM_API->memmove(a,b,c) - -// clang-format on - -#endif // ROM_UTILITY_H_ diff --git a/examples/platforms/cc2538/startup-gcc.c b/examples/platforms/cc2538/startup-gcc.c deleted file mode 100644 index f174bfa099b..00000000000 --- a/examples/platforms/cc2538/startup-gcc.c +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (c) 2016, 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 gcc-specific startup code for the cc2538. - */ - -#include -#include - -#include "cc2538-reg.h" - -extern uint8_t _ldata; -extern uint8_t _data; -extern uint8_t _edata; -extern uint8_t _bss; -extern uint8_t _ebss; -extern uint8_t _init_array; -extern uint8_t _einit_array; - -__extension__ typedef int __guard __attribute__((mode(__DI__))); - -int __cxa_guard_acquire(__guard *g) -{ - return !*(char *)(g); -} - -void __cxa_guard_release(__guard *g) -{ - *(char *)g = 1; -} - -void __cxa_guard_abort(__guard *g) -{ - (void)g; -} - -void __cxa_pure_virtual(void) -{ - while (1) - ; -} - -void IntDefaultHandler(void); -void ResetHandler(void); - -extern void SysTick_Handler(void); -extern void UART0IntHandler(void); -extern void RFCoreRxTxIntHandler(void); -extern void RFCoreErrIntHandler(void); -extern void main(void); - -static uint64_t stack[640] __attribute__((section(".stack"))); - -__attribute__((section(".vectors"), used)) void (*const vectors[])(void) = { - (void (*)(void))((unsigned long)stack + sizeof(stack)), // Initial Stack Pointer - ResetHandler, // 1 The reset handler - ResetHandler, // 2 The NMI handler - IntDefaultHandler, // 3 The hard fault handler - IntDefaultHandler, // 4 The MPU fault handler - IntDefaultHandler, // 5 The bus fault handler - IntDefaultHandler, // 6 The usage fault handler - 0, // 7 Reserved - 0, // 8 Reserved - 0, // 9 Reserved - 0, // 10 Reserved - IntDefaultHandler, // 11 SVCall handler - IntDefaultHandler, // 12 Debug monitor handler - 0, // 13 Reserved - IntDefaultHandler, // 14 The PendSV handler - SysTick_Handler, // 15 The SysTick handler - IntDefaultHandler, // 16 GPIO Port A - IntDefaultHandler, // 17 GPIO Port B - IntDefaultHandler, // 18 GPIO Port C - IntDefaultHandler, // 19 GPIO Port D - 0, // 20 none - UART0IntHandler, // 21 UART0 Rx and Tx - IntDefaultHandler, // 22 UART1 Rx and Tx - IntDefaultHandler, // 23 SSI0 Rx and Tx - IntDefaultHandler, // 24 I2C Master and Slave - 0, // 25 Reserved - 0, // 26 Reserved - 0, // 27 Reserved - 0, // 28 Reserved - 0, // 29 Reserved - IntDefaultHandler, // 30 ADC Sequence 0 - 0, // 31 Reserved - 0, // 32 Reserved - 0, // 33 Reserved - IntDefaultHandler, // 34 Watchdog timer, timer 0 - IntDefaultHandler, // 35 Timer 0 subtimer A - IntDefaultHandler, // 36 Timer 0 subtimer B - IntDefaultHandler, // 37 Timer 1 subtimer A - IntDefaultHandler, // 38 Timer 1 subtimer B - IntDefaultHandler, // 39 Timer 2 subtimer A - IntDefaultHandler, // 40 Timer 2 subtimer B - IntDefaultHandler, // 41 Analog Comparator 0 - RFCoreRxTxIntHandler, // 42 RFCore Rx/Tx - RFCoreErrIntHandler, // 43 RFCore Error - IntDefaultHandler, // 44 IcePick - IntDefaultHandler, // 45 FLASH Control - IntDefaultHandler, // 46 AES - IntDefaultHandler, // 47 PKA - IntDefaultHandler, // 48 Sleep Timer - IntDefaultHandler, // 49 MacTimer - IntDefaultHandler, // 50 SSI1 Rx and Tx - IntDefaultHandler, // 51 Timer 3 subtimer A - IntDefaultHandler, // 52 Timer 3 subtimer B - 0, // 53 Reserved - 0, // 54 Reserved - 0, // 55 Reserved - 0, // 56 Reserved - 0, // 57 Reserved - 0, // 58 Reserved - 0, // 59 Reserved - IntDefaultHandler, // 60 USB 2538 - 0, // 61 Reserved - IntDefaultHandler, // 62 uDMA - IntDefaultHandler, // 63 uDMA Error -}; - -void IntDefaultHandler(void) -{ - while (1) - ; -} - -// clang-format off - -#define FLASH_CCA_BOOTLDR_CFG_DISABLE 0xEFFFFFFF ///< Disable backdoor function -#define FLASH_CCA_BOOTLDR_CFG_ENABLE 0xF0FFFFFF ///< Enable backdoor function -#define FLASH_CCA_BOOTLDR_CFG_ACTIVE_HIGH 0x08000000 ///< Selected pin on pad A active high -#define FLASH_CCA_BOOTLDR_CFG_PORT_A_PIN_M 0x07000000 ///< Selected pin on pad A mask -#define FLASH_CCA_BOOTLDR_CFG_PORT_A_PIN_S 24 ///< Selected pin on pad A shift -#define FLASH_CCA_IMAGE_VALID 0x00000000 ///< Indicates valid image in flash - -#define FLASH_CCA_CONF_BOOTLDR_BACKDOOR_PORT_A_PIN 3 ///< Select Button on SmartRF06 Eval Board - -// clang-format on - -typedef struct -{ - uint32_t ui32BootldrCfg; - uint32_t ui32ImageValid; - uint32_t ui32ImageVectorAddr; - uint8_t ui8lock[32]; -} flash_cca_lock_page_t; - -__attribute__((__section__(".flashcca"), used)) const flash_cca_lock_page_t flash_cca_lock_page = { - FLASH_CCA_BOOTLDR_CFG_ENABLE | (FLASH_CCA_CONF_BOOTLDR_BACKDOOR_PORT_A_PIN << FLASH_CCA_BOOTLDR_CFG_PORT_A_PIN_S), - FLASH_CCA_IMAGE_VALID, - (uint32_t)&vectors, - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}; - -typedef void (*init_fn_t)(void); - -void ResetHandler(void) -{ - HWREG(SYS_CTRL_EMUOVR) = 0xFF; - - // configure clocks - HWREG(SYS_CTRL_CLOCK_CTRL) |= SYS_CTRL_CLOCK_CTRL_AMP_DET; - HWREG(SYS_CTRL_CLOCK_CTRL) = SYS_CTRL_SYSDIV_32MHZ; - - // alternate map - HWREG(SYS_CTRL_I_MAP) |= SYS_CTRL_I_MAP_ALTMAP; - - // copy the data segment initializers from flash to SRAM - memcpy(&_data, &_ldata, &_edata - &_data); - - // zero-fill the bss segment - memset(&_bss, 0, &_ebss - &_bss); - - // C++ runtime initialization (BSS, Data, relocation, etc.) - init_fn_t *fp; - - for (fp = (init_fn_t *)&_init_array; fp < (init_fn_t *)&_einit_array; fp++) - { - (*fp)(); - } - - // call the application's entry point - main(); - - // end here if main() returns - while (1) - ; -} diff --git a/examples/platforms/cc2538/uart.c b/examples/platforms/cc2538/uart.c deleted file mode 100644 index 3325c990427..00000000000 --- a/examples/platforms/cc2538/uart.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright (c) 2016, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file implements the OpenThread platform abstraction for UART communication. - * - */ - -#include -#include - -#include -#include -#include - -#include -#include - -#include "platform-cc2538.h" -#include "utils/code_utils.h" -#include "utils/uart.h" - -enum -{ - kPlatformClock = 32000000, - kBaudRate = 115200, - kReceiveBufferSize = 128, -}; - -extern void UART0IntHandler(void); - -static void processReceive(void); -static void processTransmit(void); - -static const uint8_t *sTransmitBuffer = NULL; -static uint16_t sTransmitLength = 0; - -typedef struct RecvBuffer -{ - // The data buffer - uint8_t mBuffer[kReceiveBufferSize]; - // The offset of the first item written to the list. - uint16_t mHead; - // The offset of the next item to be written to the list. - uint16_t mTail; -} RecvBuffer; - -static RecvBuffer sReceive; - -static void enable_uart_clocks(void) -{ - static int uart_clocks_done = 0; - - if (uart_clocks_done) - { - return; - } - - uart_clocks_done = 1; - -#if OPENTHREAD_CONFIG_ENABLE_DEBUG_UART - HWREG(SYS_CTRL_RCGCUART) = (SYS_CTRL_RCGCUART_UART0 | SYS_CTRL_RCGCUART_UART1); - HWREG(SYS_CTRL_SCGCUART) = (SYS_CTRL_SCGCUART_UART0 | SYS_CTRL_SCGCUART_UART1); - HWREG(SYS_CTRL_DCGCUART) = (SYS_CTRL_DCGCUART_UART0 | SYS_CTRL_DCGCUART_UART1); -#else - HWREG(SYS_CTRL_RCGCUART) = SYS_CTRL_RCGCUART_UART0; - HWREG(SYS_CTRL_SCGCUART) = SYS_CTRL_SCGCUART_UART0; - HWREG(SYS_CTRL_DCGCUART) = SYS_CTRL_DCGCUART_UART0; -#endif -} - -otError otPlatUartEnable(void) -{ - uint32_t div; - - sReceive.mHead = 0; - sReceive.mTail = 0; - - // clock - enable_uart_clocks(); - - HWREG(UART0_BASE + UART_O_CC) = 0; - - // tx pin - HWREG(IOC_PA1_SEL) = IOC_MUX_OUT_SEL_UART0_TXD; - HWREG(IOC_PA1_OVER) = IOC_OVERRIDE_OE; - HWREG(GPIO_A_BASE + GPIO_O_AFSEL) |= GPIO_PIN_1; - - // rx pin - HWREG(IOC_UARTRXD_UART0) = IOC_PAD_IN_SEL_PA0; - HWREG(IOC_PA0_OVER) = IOC_OVERRIDE_DIS; - HWREG(GPIO_A_BASE + GPIO_O_AFSEL) |= GPIO_PIN_0; - - HWREG(UART0_BASE + UART_O_CTL) = 0; - - // baud rate - div = (((kPlatformClock * 8) / kBaudRate) + 1) / 2; - HWREG(UART0_BASE + UART_O_IBRD) = div / 64; - HWREG(UART0_BASE + UART_O_FBRD) = div % 64; - HWREG(UART0_BASE + UART_O_LCRH) = UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE; - - // configure interrupts - HWREG(UART0_BASE + UART_O_IM) |= UART_IM_RXIM | UART_IM_RTIM; - - // enable - HWREG(UART0_BASE + UART_O_CTL) = UART_CTL_UARTEN | UART_CTL_TXE | UART_CTL_RXE; - - // enable interrupts - HWREG(NVIC_EN0) = 1 << ((INT_UART0 - 16) & 31); - - return OT_ERROR_NONE; -} - -otError otPlatUartDisable(void) -{ - return OT_ERROR_NONE; -} - -otError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength) -{ - otError error = OT_ERROR_NONE; - - otEXPECT_ACTION(sTransmitBuffer == NULL, error = OT_ERROR_BUSY); - - sTransmitBuffer = aBuf; - sTransmitLength = aBufLength; - -exit: - return error; -} - -void processReceive(void) -{ - // Copy tail to prevent multiple reads - uint16_t tail = sReceive.mTail; - - // If the data wraps around, process the first part - if (sReceive.mHead > tail) - { - otPlatUartReceived(sReceive.mBuffer + sReceive.mHead, kReceiveBufferSize - sReceive.mHead); - - // Reset the buffer mHead back to zero. - sReceive.mHead = 0; - } - - // For any data remaining, process it - if (sReceive.mHead != tail) - { - otPlatUartReceived(sReceive.mBuffer + sReceive.mHead, tail - sReceive.mHead); - - // Set mHead to the local tail we have cached - sReceive.mHead = tail; - } -} - -otError otPlatUartFlush(void) -{ - otEXPECT(sTransmitBuffer != NULL); - - for (; sTransmitLength > 0; sTransmitLength--) - { - while (HWREG(UART0_BASE + UART_O_FR) & UART_FR_TXFF) - ; - - HWREG(UART0_BASE + UART_O_DR) = *sTransmitBuffer++; - } - - sTransmitBuffer = NULL; - return OT_ERROR_NONE; - -exit: - return OT_ERROR_INVALID_STATE; -} - -void processTransmit(void) -{ - otPlatUartFlush(); - otPlatUartSendDone(); -} - -void cc2538UartProcess(void) -{ - processReceive(); - processTransmit(); -} - -void UART0IntHandler(void) -{ - uint32_t mis; - uint8_t byte; - - mis = HWREG(UART0_BASE + UART_O_MIS); - HWREG(UART0_BASE + UART_O_ICR) = mis; - - if (mis & (UART_IM_RXIM | UART_IM_RTIM)) - { - while (!(HWREG(UART0_BASE + UART_O_FR) & UART_FR_RXFE)) - { - byte = HWREG(UART0_BASE + UART_O_DR); - - // We can only write if incrementing mTail doesn't equal mHead - if (sReceive.mHead != (sReceive.mTail + 1) % kReceiveBufferSize) - { - sReceive.mBuffer[sReceive.mTail] = byte; - sReceive.mTail = (sReceive.mTail + 1) % kReceiveBufferSize; - } - } - } -} - -#if OPENTHREAD_CONFIG_ENABLE_DEBUG_UART - -int otPlatDebugUart_kbhit(void) -{ - uint32_t v; - - /* get flags */ - v = HWREG(UART1_BASE + UART_O_FR); - - /* if FIFO empty we have no data */ - return !(v & UART_FR_RXFE); -} - -int otPlatDebugUart_getc(void) -{ - int v = 1; - - /* if nothing in fifo */ - if (!otPlatDebugUart_kbhit()) - { - return -1; - } - - /* fetch */ - v = (int)HWREG(UART0_BASE + UART_O_DR); - v = (v & 0x0ff); - return v; -} - -void otPlatDebugUart_putchar_raw(int b) -{ - /* wait till not busy */ - while (HWREG(UART1_BASE + UART_O_FR) & UART_FR_TXFF) - ; - - /* write byte */ - HWREG(UART1_BASE + UART_O_DR) = ((uint32_t)(b & 0x0ff)); -} - -void cc2538DebugUartInit(void) -{ - int32_t a, b; - - // clocks - enable_uart_clocks(); - - HWREG(UART1_BASE + UART_O_CC) = 0; - - // UART1 - tx pin - // Using an RF06 Evaluation board - // http://www.ti.com/tool/cc2538dk - // PA3 => is jumper position RF1.14 - // To use these, you will require a "flying-lead" UART adapter - HWREG(IOC_PA3_SEL) = IOC_MUX_OUT_SEL_UART1_TXD; - HWREG(IOC_PA3_OVER) = IOC_OVERRIDE_OE; - HWREG(GPIO_A_BASE + GPIO_O_AFSEL) |= GPIO_PIN_3; - - // UART1 - rx pin we don't really use but we setup anyway - // PA2 => is jumper position RF1.16 - HWREG(IOC_UARTRXD_UART1) = IOC_PAD_IN_SEL_PA2; - HWREG(IOC_PA2_OVER) = IOC_OVERRIDE_DIS; - HWREG(GPIO_A_BASE + GPIO_O_AFSEL) |= GPIO_PIN_2; - - HWREG(UART1_BASE + UART_O_CC) = 0; - - // baud rate - b = (((kPlatformClock * 8) / kBaudRate) + 1) / 2; - a = b / 64; - b = b % 64; - - HWREG(UART1_BASE + UART_O_IBRD) = a; - HWREG(UART1_BASE + UART_O_FBRD) = b; - HWREG(UART1_BASE + UART_O_LCRH) = UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE; - - /* NOTE: - * uart1 is not using IRQs it is tx only - * and we block when writing bytes - */ - HWREG(UART1_BASE + UART_O_CTL) = UART_CTL_UARTEN | UART_CTL_TXE | UART_CTL_RXE; -} - -#endif diff --git a/examples/platforms/simulation/alarm.c b/examples/platforms/simulation/alarm.c index 2795672b694..d826d111d4b 100644 --- a/examples/platforms/simulation/alarm.c +++ b/examples/platforms/simulation/alarm.c @@ -162,10 +162,7 @@ uint64_t platformGetNow(void) } #endif // defined(CLOCK_MONOTONIC_RAW) || defined(CLOCK_MONOTONIC) -uint32_t otPlatAlarmMilliGetNow(void) -{ - return (uint32_t)(platformGetNow() / US_PER_MS); -} +uint32_t otPlatAlarmMilliGetNow(void) { return (uint32_t)(platformGetNow() / US_PER_MS); } void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) { @@ -182,10 +179,7 @@ void otPlatAlarmMilliStop(otInstance *aInstance) sIsMsRunning = false; } -uint32_t otPlatAlarmMicroGetNow(void) -{ - return (uint32_t)platformGetNow(); -} +uint32_t otPlatAlarmMicroGetNow(void) { return (uint32_t)platformGetNow(); } void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) { @@ -303,14 +297,8 @@ void platformAlarmProcess(otInstance *aInstance) #endif } -uint64_t otPlatTimeGet(void) -{ - return platformGetNow(); -} +uint64_t otPlatTimeGet(void) { return platformGetNow(); } -uint16_t otPlatTimeGetXtalAccuracy(void) -{ - return 0; -} +uint16_t otPlatTimeGetXtalAccuracy(void) { return 0; } #endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 diff --git a/examples/platforms/simulation/crypto.c b/examples/platforms/simulation/crypto.c index 36cf89be6d5..b220f7cb58c 100644 --- a/examples/platforms/simulation/crypto.c +++ b/examples/platforms/simulation/crypto.c @@ -38,12 +38,12 @@ // crypto key storage stubs -otError otPlatCryptoImportKey(otCryptoKeyRef * aKeyRef, +otError otPlatCryptoImportKey(otCryptoKeyRef *aKeyRef, otCryptoKeyType aKeyType, otCryptoKeyAlgorithm aKeyAlgorithm, int aKeyUsage, otCryptoKeyStorage aKeyPersistence, - const uint8_t * aKey, + const uint8_t *aKey, size_t aKeyLen) { OT_UNUSED_VARIABLE(aKeyRef); diff --git a/examples/platforms/simulation/diag.c b/examples/platforms/simulation/diag.c index ddb8c265959..dcd9f1d9f7a 100644 --- a/examples/platforms/simulation/diag.c +++ b/examples/platforms/simulation/diag.c @@ -35,8 +35,11 @@ #include #include +#include #include +#include "utils/code_utils.h" + #if OPENTHREAD_CONFIG_DIAG_ENABLE /** @@ -45,36 +48,132 @@ */ static bool sDiagMode = false; -void otPlatDiagModeSet(bool aMode) +enum +{ + SIM_GPIO = 0, +}; + +static otGpioMode sGpioMode = OT_GPIO_MODE_INPUT; +static bool sGpioValue = false; +static uint8_t sRawPowerSetting[OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE]; +static uint16_t sRawPowerSettingLength = 0; + +void otPlatDiagModeSet(bool aMode) { sDiagMode = aMode; } + +bool otPlatDiagModeGet(void) { return sDiagMode; } + +void otPlatDiagChannelSet(uint8_t aChannel) { OT_UNUSED_VARIABLE(aChannel); } + +void otPlatDiagTxPowerSet(int8_t aTxPower) { OT_UNUSED_VARIABLE(aTxPower); } + +void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError) { - sDiagMode = aMode; + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aFrame); + OT_UNUSED_VARIABLE(aError); } -bool otPlatDiagModeGet() +void otPlatDiagAlarmCallback(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); } + +otError otPlatDiagGpioSet(uint32_t aGpio, bool aValue) { - return sDiagMode; + otError error = OT_ERROR_NONE; + + otEXPECT_ACTION(aGpio == SIM_GPIO, error = OT_ERROR_INVALID_ARGS); + sGpioValue = aValue; + +exit: + return error; } -void otPlatDiagChannelSet(uint8_t aChannel) +otError otPlatDiagGpioGet(uint32_t aGpio, bool *aValue) { - OT_UNUSED_VARIABLE(aChannel); + otError error = OT_ERROR_NONE; + + otEXPECT_ACTION((aGpio == SIM_GPIO) && (aValue != NULL), error = OT_ERROR_INVALID_ARGS); + *aValue = sGpioValue; + +exit: + return error; } -void otPlatDiagTxPowerSet(int8_t aTxPower) +otError otPlatDiagGpioSetMode(uint32_t aGpio, otGpioMode aMode) { - OT_UNUSED_VARIABLE(aTxPower); + otError error = OT_ERROR_NONE; + + otEXPECT_ACTION(aGpio == SIM_GPIO, error = OT_ERROR_INVALID_ARGS); + sGpioMode = aMode; + +exit: + return error; } -void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError) +otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode) +{ + otError error = OT_ERROR_NONE; + + otEXPECT_ACTION((aGpio == SIM_GPIO) && (aMode != NULL), error = OT_ERROR_INVALID_ARGS); + *aMode = sGpioMode; + +exit: + return error; +} + +otError otPlatDiagRadioSetRawPowerSetting(otInstance *aInstance, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength) { OT_UNUSED_VARIABLE(aInstance); - OT_UNUSED_VARIABLE(aFrame); - OT_UNUSED_VARIABLE(aError); + otError error = OT_ERROR_NONE; + + otEXPECT_ACTION((aRawPowerSetting != NULL) && (aRawPowerSettingLength <= sizeof(sRawPowerSetting)), + error = OT_ERROR_INVALID_ARGS); + memcpy(sRawPowerSetting, aRawPowerSetting, aRawPowerSettingLength); + sRawPowerSettingLength = aRawPowerSettingLength; + +exit: + return error; } -void otPlatDiagAlarmCallback(otInstance *aInstance) +otError otPlatDiagRadioGetRawPowerSetting(otInstance *aInstance, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) { OT_UNUSED_VARIABLE(aInstance); + otError error = OT_ERROR_NONE; + + otEXPECT_ACTION((aRawPowerSetting != NULL) && (aRawPowerSettingLength != NULL), error = OT_ERROR_INVALID_ARGS); + otEXPECT_ACTION((sRawPowerSettingLength != 0), error = OT_ERROR_NOT_FOUND); + otEXPECT_ACTION((sRawPowerSettingLength <= *aRawPowerSettingLength), error = OT_ERROR_INVALID_ARGS); + + memcpy(aRawPowerSetting, sRawPowerSetting, sRawPowerSettingLength); + *aRawPowerSettingLength = sRawPowerSettingLength; + +exit: + return error; } +otError otPlatDiagRadioRawPowerSettingEnable(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aEnable); + + return OT_ERROR_NONE; +} + +otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aEnable); + + return OT_ERROR_NONE; +} + +otError otPlatDiagRadioTransmitStream(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aEnable); + + return OT_ERROR_NONE; +} #endif // OPENTHREAD_CONFIG_DIAG_ENABLE diff --git a/examples/platforms/simulation/entropy.c b/examples/platforms/simulation/entropy.c index bcb04a42011..b567b0b635c 100644 --- a/examples/platforms/simulation/entropy.c +++ b/examples/platforms/simulation/entropy.c @@ -93,7 +93,7 @@ otError otPlatEntropyGet(uint8_t *aOutput, uint16_t aOutputLength) #if __SANITIZE_ADDRESS__ == 0 - FILE * file = NULL; + FILE *file = NULL; size_t readLength; otEXPECT_ACTION(aOutput && aOutputLength, error = OT_ERROR_INVALID_ARGS); diff --git a/examples/platforms/simulation/infra_if.c b/examples/platforms/simulation/infra_if.c index b62041b8d6c..1904d75d6ac 100644 --- a/examples/platforms/simulation/infra_if.c +++ b/examples/platforms/simulation/infra_if.c @@ -41,7 +41,7 @@ bool otPlatInfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddres otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, const otIp6Address *aDestAddress, - const uint8_t * aBuffer, + const uint8_t *aBuffer, uint16_t aBufferLength) { OT_UNUSED_VARIABLE(aInfraIfIndex); @@ -51,4 +51,11 @@ otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, return OT_ERROR_FAILED; } + +otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex) +{ + OT_UNUSED_VARIABLE(aInfraIfIndex); + + return OT_ERROR_FAILED; +} #endif diff --git a/examples/platforms/simulation/logging.c b/examples/platforms/simulation/logging.c index 8b90c43348a..cce2ad55885 100644 --- a/examples/platforms/simulation/logging.c +++ b/examples/platforms/simulation/logging.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -44,21 +45,82 @@ #include "utils/code_utils.h" #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED) -OT_TOOL_WEAK void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) + +static FILE *sLogFile = NULL; + +void platformLoggingSetFileName(const char *aName) +{ + if (sLogFile != NULL) + { + fclose(sLogFile); + } + + sLogFile = fopen(aName, "wt"); + + if (sLogFile == NULL) + { + fprintf(stderr, "Failed to open log file '%s': %s\r\n", aName, strerror(errno)); + exit(EXIT_FAILURE); + } +} + +void platformLoggingInit(const char *aName) +{ + if (sLogFile == NULL) + { + openlog(aName, LOG_PID, LOG_USER); + setlogmask(setlogmask(0) & LOG_UPTO(LOG_NOTICE)); + } + else + { + fprintf(sLogFile, "OpenThread logs\r\n"); + fprintf(sLogFile, "- Program: %s\r\n", aName); + fprintf(sLogFile, "- Platform: simulation\r\n"); + fprintf(sLogFile, "- Node ID: %lu\r\n", (unsigned long)gNodeId); + fprintf(sLogFile, "\r\n"); + } +} + +void platformLoggingDeinit(void) +{ + if (sLogFile != NULL) + { + fclose(sLogFile); + sLogFile = NULL; + } +} + +void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) { OT_UNUSED_VARIABLE(aLogLevel); OT_UNUSED_VARIABLE(aLogRegion); - char logString[512]; - int offset; va_list args; - offset = snprintf(logString, sizeof(logString), "[%d]", gNodeId); - va_start(args, aFormat); - vsnprintf(&logString[offset], sizeof(logString) - (uint16_t)offset, aFormat, args); - va_end(args); - syslog(LOG_CRIT, "%s", logString); + if (sLogFile == NULL) + { + char logString[512]; + int offset; + + offset = snprintf(logString, sizeof(logString), "[%lu]", (unsigned long)gNodeId); + + vsnprintf(&logString[offset], sizeof(logString) - (uint16_t)offset, aFormat, args); + syslog(LOG_CRIT, "%s", logString); + } + else + { + vfprintf(sLogFile, aFormat, args); + fprintf(sLogFile, "\r\n"); + } + + va_end(args); } -#endif + +#else + +void platformLoggingInit(const char *aName) { OT_UNUSED_VARIABLE(aName); } +void platformLoggingDeinit(void) {} + +#endif // (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED) diff --git a/examples/platforms/simulation/openthread-core-simulation-config.h b/examples/platforms/simulation/openthread-core-simulation-config.h index 879f069bf9e..f71383008a3 100644 --- a/examples/platforms/simulation/openthread-core-simulation-config.h +++ b/examples/platforms/simulation/openthread-core-simulation-config.h @@ -254,4 +254,35 @@ #define OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_MAX_SERVICES 20 #endif +/** + * @def OPENTHREAD_CONFIG_DETERMINISTIC_ECDSA_ENABLE + * + * Define to 1 to generate ECDSA signatures deterministically + * according to RFC 6979 instead of randomly. + * + */ +#ifndef OPENTHREAD_CONFIG_DETERMINISTIC_ECDSA_ENABLE +#define OPENTHREAD_CONFIG_DETERMINISTIC_ECDSA_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE + * + * Define as 1 to enable power calibration support. + * + */ +#ifndef OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE +#define OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + * + * Define as 1 to enable platform power calibration support. + * + */ +#ifndef OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +#define OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE 1 +#endif + #endif // OPENTHREAD_CORE_SIMULATION_CONFIG_H_ diff --git a/examples/platforms/simulation/platform-simulation.h b/examples/platforms/simulation/platform-simulation.h index 9379421d46a..0fa32fddc88 100644 --- a/examples/platforms/simulation/platform-simulation.h +++ b/examples/platforms/simulation/platform-simulation.h @@ -185,6 +185,28 @@ void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const */ void platformRandomInit(void); +/** + * This functions set the file name to use for logging. + * + * @param[in] aName The file name. + * + */ +void platformLoggingSetFileName(const char *aName); + +/** + * This function initializes the platform logging service. + * + * @param[in] aName The log module name to set with syslog. + * + */ +void platformLoggingInit(const char *aName); + +/** + * This function finalizes the platform logging service. + * + */ +void platformLoggingDeinit(void); + /** * This function updates the file descriptor sets with file descriptors used by the UART driver. * @@ -232,6 +254,18 @@ void otSimSendUartWriteEvent(const uint8_t *aData, uint16_t aLength); */ bool platformRadioIsTransmitPending(void); +/** + * This function parses an environment variable as an unsigned 16-bit integer. + * + * If the environment variable does not exist, this function does nothing. + * If it is not a valid integer, this function will terminate the process with an error message. + * + * @param[in] aEnvName The name of the environment variable. + * @param[out] aValue A pointer to the unsigned 16-bit integer. + * + */ +void parseFromEnvAsUint16(const char *aEnvName, uint16_t *aValue); + #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE /** diff --git a/examples/platforms/simulation/radio.c b/examples/platforms/simulation/radio.c index 2b627c2f212..9fe37b66c21 100644 --- a/examples/platforms/simulation/radio.c +++ b/examples/platforms/simulation/radio.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -71,10 +72,12 @@ enum #if OPENTHREAD_SIMULATION_VIRTUAL_TIME extern int sSockFd; +extern uint16_t sPortBase; extern uint16_t sPortOffset; #else static int sTxFd = -1; static int sRxFd = -1; +static uint16_t sPortBase = 9000; static uint16_t sPortOffset = 0; static uint16_t sPort = 0; #endif @@ -166,11 +169,128 @@ static otRadioKeyType sKeyType; static int8_t GetRssi(uint16_t aChannel); -static bool IsTimeAfterOrEqual(uint32_t aTimeA, uint32_t aTimeB) +#if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 + +static enum { + kFilterOff, + kFilterDenyList, + kFilterAllowList, +} sFilterMode = kFilterOff; + +static uint8_t sFilterNodeIdsBitVector[(MAX_NETWORK_SIZE + 7) / 8]; + +static bool FilterContainsId(uint16_t aNodeId) +{ + uint16_t index = aNodeId - 1; + + return (sFilterNodeIdsBitVector[index / 8] & (0x80 >> (index % 8))) != 0; +} + +static bool NodeIdFilterIsConnectable(uint16_t aNodeId) +{ + bool isConnectable = true; + + switch (sFilterMode) + { + case kFilterOff: + break; + case kFilterDenyList: + isConnectable = !FilterContainsId(aNodeId); + break; + case kFilterAllowList: + isConnectable = FilterContainsId(aNodeId); + break; + } + + return isConnectable; +} + +static void AddNodeIdToFilter(uint16_t aNodeId) { - return (aTimeA - aTimeB) < (1U << 31); + uint16_t index = aNodeId - 1; + + sFilterNodeIdsBitVector[index / 8] |= 0x80 >> (index % 8); } +OT_TOOL_WEAK void otCliOutputFormat(const char *aFmt, ...) { OT_UNUSED_VARIABLE(aFmt); } + +otError ProcessNodeIdFilter(void *aContext, uint8_t aArgsLength, char *aArgs[]) +{ + OT_UNUSED_VARIABLE(aContext); + + otError error = OT_ERROR_NONE; + bool deny = false; + + if (aArgsLength == 0) + { + switch (sFilterMode) + { + case kFilterOff: + otCliOutputFormat("off"); + break; + case kFilterDenyList: + otCliOutputFormat("deny-list"); + break; + case kFilterAllowList: + otCliOutputFormat("allow-list"); + break; + } + + for (uint16_t nodeId = 0; nodeId <= MAX_NETWORK_SIZE; nodeId++) + { + if (FilterContainsId(nodeId)) + { + otCliOutputFormat(" %d", nodeId); + } + } + + otCliOutputFormat("\r\n"); + } + else if (!strcmp(aArgs[0], "clear")) + { + otEXPECT_ACTION(aArgsLength == 1, error = OT_ERROR_INVALID_ARGS); + + memset(sFilterNodeIdsBitVector, 0, sizeof(sFilterNodeIdsBitVector)); + sFilterMode = kFilterOff; + } + else if ((deny = !strcmp(aArgs[0], "deny")) || !strcmp(aArgs[0], "allow")) + { + uint16_t nodeId; + char *endptr; + + otEXPECT_ACTION(aArgsLength == 2, error = OT_ERROR_INVALID_ARGS); + + nodeId = (uint16_t)strtol(aArgs[1], &endptr, 0); + + otEXPECT_ACTION(*endptr == '\0', error = OT_ERROR_INVALID_ARGS); + otEXPECT_ACTION(1 <= nodeId && nodeId <= MAX_NETWORK_SIZE, error = OT_ERROR_INVALID_ARGS); + + otEXPECT_ACTION(sFilterMode != (deny ? kFilterAllowList : kFilterDenyList), error = OT_ERROR_INVALID_STATE); + + AddNodeIdToFilter(nodeId); + sFilterMode = deny ? kFilterDenyList : kFilterAllowList; + } + else + { + error = OT_ERROR_INVALID_COMMAND; + } + +exit: + return error; +} +#else +otError ProcessNodeIdFilter(void *aContext, uint8_t aArgsLength, char *aArgs[]) +{ + OT_UNUSED_VARIABLE(aContext); + OT_UNUSED_VARIABLE(aArgsLength); + OT_UNUSED_VARIABLE(aArgs); + + return OT_ERROR_NOT_IMPLEMENTED; +} +#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 + +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++) @@ -298,7 +418,7 @@ static void initFds(void) otEXPECT_ACTION((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1, perror("socket(sTxFd)")); - sPort = (uint16_t)(9000 + sPortOffset + gNodeId); + sPort = (uint16_t)(sPortBase + sPortOffset + gNodeId); sockaddr.sin_family = AF_INET; sockaddr.sin_port = htons(sPort); sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); @@ -337,7 +457,7 @@ static void initFds(void) } sockaddr.sin_family = AF_INET; - sockaddr.sin_port = htons((uint16_t)(9000 + sPortOffset)); + sockaddr.sin_port = htons((uint16_t)(sPortBase + sPortOffset)); sockaddr.sin_addr.s_addr = inet_addr(OT_RADIO_GROUP); otEXPECT_ACTION(bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) != -1, perror("bind(sRxFd)")); @@ -356,24 +476,10 @@ static void initFds(void) void platformRadioInit(void) { #if OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 - char *offset; + parseFromEnvAsUint16("PORT_BASE", &sPortBase); - offset = getenv("PORT_OFFSET"); - - if (offset) - { - char *endptr; - - sPortOffset = (uint16_t)strtol(offset, &endptr, 0); - - if (*endptr != '\0') - { - fprintf(stderr, "Invalid PORT_OFFSET: %s\n", offset); - exit(EXIT_FAILURE); - } - - sPortOffset *= (MAX_NETWORK_SIZE + 1); - } + parseFromEnvAsUint16("PORT_OFFSET", &sPortOffset); + sPortOffset *= (MAX_NETWORK_SIZE + 1); initFds(); #endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 @@ -737,10 +843,7 @@ void radioSendMessage(otInstance *aInstance) return; } -bool platformRadioIsTransmitPending(void) -{ - return sState == OT_RADIO_STATE_TRANSMIT && !sTxWait; -} +bool platformRadioIsTransmitPending(void) { return sState == OT_RADIO_STATE_TRANSMIT && !sTxWait; } #if OPENTHREAD_SIMULATION_VIRTUAL_TIME void platformRadioReceive(otInstance *aInstance, uint8_t *aBuf, uint16_t aBufLength) @@ -829,7 +932,10 @@ void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const if (rval > 0) { - if (sockaddr.sin_port != htons(sPort)) + uint16_t srcPort = ntohs(sockaddr.sin_port); + uint16_t srcNodeId = srcPort - sPortOffset - sPortBase; + + if (NodeIdFilterIsConnectable(srcNodeId) && srcPort != sPort) { sReceiveFrame.mLength = (uint16_t)(rval - 1); @@ -847,7 +953,7 @@ void platformRadioProcess(otInstance *aInstance, const fd_set *aReadFdSet, const exit(EXIT_FAILURE); } } -#endif +#endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME == 0 if (platformRadioIsTransmitPending()) { radioSendMessage(aInstance); @@ -870,7 +976,7 @@ void radioTransmit(struct RadioMessage *aMessage, const struct otRadioFrame *aFr sockaddr.sin_family = AF_INET; inet_pton(AF_INET, OT_RADIO_GROUP, &sockaddr.sin_addr); - sockaddr.sin_port = htons((uint16_t)(9000 + sPortOffset)); + sockaddr.sin_port = htons((uint16_t)(sPortBase + sPortOffset)); rval = sendto(sTxFd, (const char *)aMessage, 1 + aFrame->mLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); @@ -1216,7 +1322,7 @@ static uint8_t generateAckIeData(uint8_t *aLinkMetricsIeData, uint8_t aLinkMetri #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -otError otPlatRadioEnableCsl(otInstance * aInstance, +otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, otShortAddress aShortAddr, const otExtAddress *aExtAddr) @@ -1247,7 +1353,7 @@ uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance) } #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -void otPlatRadioSetMacKey(otInstance * aInstance, +void otPlatRadioSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKeyId, const otMacKeyMaterial *aPrevKey, @@ -1291,10 +1397,10 @@ otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aCh } #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE -otError otPlatRadioConfigureEnhAckProbing(otInstance * aInstance, +otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance, otLinkMetrics aLinkMetrics, const otShortAddress aShortAddress, - const otExtAddress * aExtAddress) + const otExtAddress *aExtAddress) { OT_UNUSED_VARIABLE(aInstance); @@ -1321,3 +1427,21 @@ otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode) exit: return error; } + +void parseFromEnvAsUint16(const char *aEnvName, uint16_t *aValue) +{ + char *env = getenv(aEnvName); + + if (env) + { + char *endptr; + + *aValue = (uint16_t)strtol(env, &endptr, 0); + + if (*endptr != '\0') + { + fprintf(stderr, "Invalid %s: %s\n", aEnvName, env); + exit(EXIT_FAILURE); + } + } +} diff --git a/examples/platforms/simulation/spi-stubs.c b/examples/platforms/simulation/spi-stubs.c index 585bca7b006..2643402fc08 100644 --- a/examples/platforms/simulation/spi-stubs.c +++ b/examples/platforms/simulation/spi-stubs.c @@ -40,7 +40,7 @@ otError otPlatSpiSlaveEnable(otPlatSpiSlaveTransactionCompleteCallback aCompleteCallback, otPlatSpiSlaveTransactionProcessCallback aProcessCallback, - void * aContext) + void *aContext) { OT_UNUSED_VARIABLE(aCompleteCallback); OT_UNUSED_VARIABLE(aProcessCallback); @@ -52,9 +52,7 @@ otError otPlatSpiSlaveEnable(otPlatSpiSlaveTransactionCompleteCallback aComplete return OT_ERROR_NOT_IMPLEMENTED; } -void otPlatSpiSlaveDisable(void) -{ -} +void otPlatSpiSlaveDisable(void) {} otError otPlatSpiSlavePrepareTransaction(uint8_t *aOutputBuf, uint16_t aOutputBufLen, @@ -73,9 +71,7 @@ otError otPlatSpiSlavePrepareTransaction(uint8_t *aOutputBuf, // Uart -void otPlatUartSendDone(void) -{ -} +void otPlatUartSendDone(void) {} void otPlatUartReceived(const uint8_t *aBuf, uint16_t aBufLength) { diff --git a/examples/platforms/simulation/system.c b/examples/platforms/simulation/system.c index f9282a64a20..0e86aaae439 100644 --- a/examples/platforms/simulation/system.c +++ b/examples/platforms/simulation/system.c @@ -44,7 +44,6 @@ #include #include #include -#include #include #include @@ -74,6 +73,7 @@ enum OT_SIM_OPT_ENABLE_ENERGY_SCAN = 'E', OT_SIM_OPT_SLEEP_TO_TX = 't', OT_SIM_OPT_TIME_SPEED = 's', + OT_SIM_OPT_LOG_FILE = 'l', OT_SIM_OPT_UNKNOWN = '?', }; @@ -86,7 +86,11 @@ static void PrintUsage(const char *aProgramName, int aExitCode) " -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", + " -s --time-speed=val Speed up the time in simulation.\n" +#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED) + " -l --log-file=name File name to write logs.\n" +#endif + , aProgramName); exit(aExitCode); @@ -94,17 +98,26 @@ static void PrintUsage(const char *aProgramName, int aExitCode) void otSysInit(int aArgCount, char *aArgVector[]) { - char * endptr; + char *endptr; uint32_t speedUpFactor = 1; 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}, + {"enable-energy-scan", no_argument, 0, OT_SIM_OPT_ENABLE_ENERGY_SCAN}, {"sleep-to-tx", no_argument, 0, OT_SIM_OPT_SLEEP_TO_TX}, {"time-speed", required_argument, 0, OT_SIM_OPT_TIME_SPEED}, +#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED) + {"log-file", required_argument, 0, OT_SIM_OPT_LOG_FILE}, +#endif {0, 0, 0, 0}, }; +#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED) + static const char options[] = "Ehts:l:"; +#else + static const char options[] = "Ehts:"; +#endif + if (gPlatformPseudoResetWasRequested) { gPlatformPseudoResetWasRequested = false; @@ -115,7 +128,7 @@ void otSysInit(int aArgCount, char *aArgVector[]) while (true) { - int c = getopt_long(aArgCount, aArgVector, "Ehts:", long_options, NULL); + int c = getopt_long(aArgCount, aArgVector, options, long_options, NULL); if (c == -1) { @@ -144,6 +157,11 @@ void otSysInit(int aArgCount, char *aArgVector[]) exit(EXIT_FAILURE); } break; +#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED + case OT_SIM_OPT_LOG_FILE: + platformLoggingSetFileName(optarg); + break; +#endif default: break; } @@ -162,12 +180,10 @@ void otSysInit(int aArgCount, char *aArgVector[]) exit(EXIT_FAILURE); } - openlog(basename(aArgVector[0]), LOG_PID, LOG_USER); - setlogmask(setlogmask(0) & LOG_UPTO(LOG_NOTICE)); - signal(SIGTERM, &handleSignal); signal(SIGHUP, &handleSignal); + platformLoggingInit(basename(aArgVector[0])); platformAlarmInit(speedUpFactor); platformRadioInit(); #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE @@ -176,10 +192,7 @@ void otSysInit(int aArgCount, char *aArgVector[]) platformRandomInit(); } -bool otSysPseudoResetWasRequested(void) -{ - return gPlatformPseudoResetWasRequested; -} +bool otSysPseudoResetWasRequested(void) { return gPlatformPseudoResetWasRequested; } void otSysDeinit(void) { @@ -187,6 +200,7 @@ void otSysDeinit(void) #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE platformTrelDeinit(); #endif + platformLoggingDeinit(); } void otSysProcessDrivers(otInstance *aInstance) diff --git a/examples/platforms/simulation/trel.c b/examples/platforms/simulation/trel.c index 20c6b305720..3413c9351b8 100644 --- a/examples/platforms/simulation/trel.c +++ b/examples/platforms/simulation/trel.c @@ -366,8 +366,8 @@ void otPlatTrelRegisterService(otInstance *aInstance, uint16_t aPort, const uint #endif } -void otPlatTrelSend(otInstance * aInstance, - const uint8_t * aUdpPayload, +void otPlatTrelSend(otInstance *aInstance, + const uint8_t *aUdpPayload, uint16_t aUdpPayloadLen, const otSockAddr *aDestSockAddr) { @@ -419,10 +419,7 @@ void platformTrelInit(uint32_t aSpeedUpFactor) OT_UNUSED_VARIABLE(aSpeedUpFactor); } -void platformTrelDeinit(void) -{ - deinitFds(); -} +void platformTrelDeinit(void) { deinitFds(); } void platformTrelUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, struct timeval *aTimeout, int *aMaxFd) { diff --git a/examples/platforms/simulation/uart.c b/examples/platforms/simulation/uart.c index 3bbcface50f..fe3fcebe854 100644 --- a/examples/platforms/simulation/uart.c +++ b/examples/platforms/simulation/uart.c @@ -54,15 +54,9 @@ static int s_out_fd; static struct termios original_stdin_termios; static struct termios original_stdout_termios; -static void restore_stdin_termios(void) -{ - tcsetattr(s_in_fd, TCSAFLUSH, &original_stdin_termios); -} +static void restore_stdin_termios(void) { tcsetattr(s_in_fd, TCSAFLUSH, &original_stdin_termios); } -static void restore_stdout_termios(void) -{ - tcsetattr(s_out_fd, TCSAFLUSH, &original_stdout_termios); -} +static void restore_stdout_termios(void) { tcsetattr(s_out_fd, TCSAFLUSH, &original_stdout_termios); } void platformUartRestore(void) { @@ -241,8 +235,8 @@ void platformUartProcess(void) ssize_t rval; const int error_flags = POLLERR | POLLNVAL | POLLHUP; struct pollfd pollfd[] = { - {s_in_fd, POLLIN | error_flags, 0}, - {s_out_fd, POLLOUT | error_flags, 0}, + {s_in_fd, POLLIN | error_flags, 0}, + {s_out_fd, POLLOUT | error_flags, 0}, }; errno = 0; diff --git a/examples/platforms/simulation/virtual_time/alarm-sim.c b/examples/platforms/simulation/virtual_time/alarm-sim.c index 10ebaa56957..7c2cea1e5e8 100644 --- a/examples/platforms/simulation/virtual_time/alarm-sim.c +++ b/examples/platforms/simulation/virtual_time/alarm-sim.c @@ -55,20 +55,11 @@ void platformAlarmInit(uint32_t aSpeedUpFactor) sNow = 0; } -uint64_t platformAlarmGetNow(void) -{ - return sNow; -} +uint64_t platformAlarmGetNow(void) { return sNow; } -void platformAlarmAdvanceNow(uint64_t aDelta) -{ - sNow += aDelta; -} +void platformAlarmAdvanceNow(uint64_t aDelta) { sNow += aDelta; } -uint32_t otPlatAlarmMilliGetNow(void) -{ - return (uint32_t)(sNow / US_PER_MS); -} +uint32_t otPlatAlarmMilliGetNow(void) { return (uint32_t)(sNow / US_PER_MS); } void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) { @@ -85,10 +76,7 @@ void otPlatAlarmMilliStop(otInstance *aInstance) sIsMsRunning = false; } -uint32_t otPlatAlarmMicroGetNow(void) -{ - return (uint32_t)sNow; -} +uint32_t otPlatAlarmMicroGetNow(void) { return (uint32_t)sNow; } void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) { @@ -186,16 +174,10 @@ void platformAlarmProcess(otInstance *aInstance) #endif // OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE } -uint64_t otPlatTimeGet(void) -{ - return platformAlarmGetNow(); -} +uint64_t otPlatTimeGet(void) { return platformAlarmGetNow(); } #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE -uint16_t otPlatTimeGetXtalAccuracy(void) -{ - return 0; -} +uint16_t otPlatTimeGetXtalAccuracy(void) { return 0; } #endif #endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME diff --git a/examples/platforms/simulation/virtual_time/platform-sim.c b/examples/platforms/simulation/virtual_time/platform-sim.c index fa0dd905599..1c2c8189494 100644 --- a/examples/platforms/simulation/virtual_time/platform-sim.c +++ b/examples/platforms/simulation/virtual_time/platform-sim.c @@ -61,6 +61,7 @@ char **gArguments = NULL; uint64_t sNow = 0; // microseconds int sSockFd; +uint16_t sPortBase = 9000; uint16_t sPortOffset; static void handleSignal(int aSignal) @@ -78,7 +79,7 @@ void otSimSendEvent(const struct Event *aEvent) memset(&sockaddr, 0, sizeof(sockaddr)); sockaddr.sin_family = AF_INET; inet_pton(AF_INET, "127.0.0.1", &sockaddr.sin_addr); - sockaddr.sin_port = htons(9000 + sPortOffset); + sockaddr.sin_port = htons(sPortBase + sPortOffset); rval = sendto(sSockFd, aEvent, offsetof(struct Event, mData) + aEvent->mDataLength, 0, (struct sockaddr *)&sockaddr, sizeof(sockaddr)); @@ -135,19 +136,11 @@ static void platformSendSleepEvent(void) } #if OPENTHREAD_SIMULATION_VIRTUAL_TIME_UART -void platformUartRestore(void) -{ -} +void platformUartRestore(void) {} -otError otPlatUartEnable(void) -{ - return OT_ERROR_NONE; -} +otError otPlatUartEnable(void) { return OT_ERROR_NONE; } -otError otPlatUartDisable(void) -{ - return OT_ERROR_NONE; -} +otError otPlatUartDisable(void) { return OT_ERROR_NONE; } otError otPlatUartSend(const uint8_t *aData, uint16_t aLength) { @@ -167,37 +160,21 @@ otError otPlatUartSend(const uint8_t *aData, uint16_t aLength) return error; } -otError otPlatUartFlush(void) -{ - return OT_ERROR_NONE; -} +otError otPlatUartFlush(void) { return OT_ERROR_NONE; } #endif // OPENTHREAD_SIMULATION_VIRTUAL_TIME_UART static void socket_init(void) { struct sockaddr_in sockaddr; - char * offset; memset(&sockaddr, 0, sizeof(sockaddr)); sockaddr.sin_family = AF_INET; - offset = getenv("PORT_OFFSET"); - - if (offset) - { - char *endptr; - - sPortOffset = (uint16_t)strtol(offset, &endptr, 0); - - if (*endptr != '\0') - { - fprintf(stderr, "Invalid PORT_OFFSET: %s\n", offset); - exit(EXIT_FAILURE); - } + parseFromEnvAsUint16("PORT_BASE", &sPortBase); - sPortOffset *= (MAX_NETWORK_SIZE + 1); - } + parseFromEnvAsUint16("PORT_OFFSET", &sPortOffset); + sPortOffset *= (MAX_NETWORK_SIZE + 1); - sockaddr.sin_port = htons((uint16_t)(9000 + sPortOffset + gNodeId)); + sockaddr.sin_port = htons((uint16_t)(sPortBase + sPortOffset + gNodeId)); sockaddr.sin_addr.s_addr = INADDR_ANY; sSockFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); @@ -254,15 +231,9 @@ void otSysInit(int argc, char *argv[]) signal(SIGHUP, &handleSignal); } -bool otSysPseudoResetWasRequested(void) -{ - return gPlatformPseudoResetWasRequested; -} +bool otSysPseudoResetWasRequested(void) { return gPlatformPseudoResetWasRequested; } -void otSysDeinit(void) -{ - close(sSockFd); -} +void otSysDeinit(void) { close(sSockFd); } void otSysProcessDrivers(otInstance *aInstance) { diff --git a/examples/platforms/utils/debug_uart.c b/examples/platforms/utils/debug_uart.c index e1d10bdc93f..169c65ddef3 100644 --- a/examples/platforms/utils/debug_uart.c +++ b/examples/platforms/utils/debug_uart.c @@ -103,22 +103,13 @@ void otPlatDebugUart_putchar(int c) /* provide WEAK stubs for platforms that do not implement all functions */ OT_TOOL_WEAK -void otPlatDebugUart_putchar_raw(int c) -{ - OT_UNUSED_VARIABLE(c); -} +void otPlatDebugUart_putchar_raw(int c) { OT_UNUSED_VARIABLE(c); } OT_TOOL_WEAK -int otPlatDebugUart_kbhit(void) -{ - return 0; /* nothing */ -} +int otPlatDebugUart_kbhit(void) { return 0; /* nothing */ } OT_TOOL_WEAK -int otPlatDebugUart_getc(void) -{ - return -1; /* nothing */ -} +int otPlatDebugUart_getc(void) { return -1; /* nothing */ } OT_TOOL_WEAK otError otPlatDebugUart_logfile(const char *filename) diff --git a/examples/platforms/utils/link_metrics.cpp b/examples/platforms/utils/link_metrics.cpp index a09fab423b2..84be0d7c377 100644 --- a/examples/platforms/utils/link_metrics.cpp +++ b/examples/platforms/utils/link_metrics.cpp @@ -134,7 +134,7 @@ class LinkMetricsDataInfo : public LinkedListEntry, public otLinkMetrics GetLinkMetrics(void) const { return mLinkMetrics; } private: - uint8_t GetLinkMargin(int8_t aRssi) const { return LinkQualityInfo::ConvertRssToLinkMargin(sNoiseFloor, aRssi); } + uint8_t GetLinkMargin(int8_t aRssi) const { return ComputeLinkMargin(sNoiseFloor, aRssi); } bool Matches(const otShortAddress &aShortAddress) const { return mShortAddress == aShortAddress; }; @@ -177,10 +177,7 @@ static inline bool IsLinkMetricsClear(otLinkMetrics aLinkMetrics) return !aLinkMetrics.mPduCount && !aLinkMetrics.mLqi && !aLinkMetrics.mLinkMargin && !aLinkMetrics.mRssi; } -void otLinkMetricsInit(int8_t aNoiseFloor) -{ - sNoiseFloor = aNoiseFloor; -} +void otLinkMetricsInit(int8_t aNoiseFloor) { sNoiseFloor = aNoiseFloor; } otError otLinkMetricsConfigureEnhAckProbing(otShortAddress aShortAddress, const otExtAddress *aExtAddress, diff --git a/examples/platforms/utils/logging_rtt.c b/examples/platforms/utils/logging_rtt.c index e58db65094f..32d79f5af7c 100644 --- a/examples/platforms/utils/logging_rtt.c +++ b/examples/platforms/utils/logging_rtt.c @@ -136,10 +136,7 @@ void utilsLogRttInit(void) return; } -void utilsLogRttDeinit(void) -{ - sLogInitialized = false; -} +void utilsLogRttDeinit(void) { sLogInitialized = false; } void utilsLogRttOutput(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list ap) { diff --git a/examples/platforms/utils/mac_frame.cpp b/examples/platforms/utils/mac_frame.cpp index de238832ce3..0ae88db3424 100644 --- a/examples/platforms/utils/mac_frame.cpp +++ b/examples/platforms/utils/mac_frame.cpp @@ -68,17 +68,17 @@ bool otMacFrameDoesAddrMatch(const otRadioFrame *aFrame, bool otMacFrameIsAck(const otRadioFrame *aFrame) { - return static_cast(aFrame)->GetType() == Mac::Frame::kFcfFrameAck; + return static_cast(aFrame)->GetType() == Mac::Frame::kTypeAck; } bool otMacFrameIsData(const otRadioFrame *aFrame) { - return static_cast(aFrame)->GetType() == Mac::Frame::kFcfFrameData; + return static_cast(aFrame)->GetType() == Mac::Frame::kTypeData; } bool otMacFrameIsCommand(const otRadioFrame *aFrame) { - return static_cast(aFrame)->GetType() == Mac::Frame::kFcfFrameMacCmd; + return static_cast(aFrame)->GetType() == Mac::Frame::kTypeMacCmd; } bool otMacFrameIsDataRequest(const otRadioFrame *aFrame) @@ -164,9 +164,9 @@ void otMacFrameGenerateImmAck(const otRadioFrame *aFrame, bool aIsFramePending, #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 otError otMacFrameGenerateEnhAck(const otRadioFrame *aFrame, bool aIsFramePending, - const uint8_t * aIeData, + const uint8_t *aIeData, uint8_t aIeLength, - otRadioFrame * aAckFrame) + otRadioFrame *aAckFrame) { assert(aFrame != nullptr && aAckFrame != nullptr); @@ -206,10 +206,7 @@ uint8_t otMacFrameGetKeyId(otRadioFrame *aFrame) return keyId; } -void otMacFrameSetKeyId(otRadioFrame *aFrame, uint8_t aKeyId) -{ - static_cast(aFrame)->SetKeyId(aKeyId); -} +void otMacFrameSetKeyId(otRadioFrame *aFrame, uint8_t aKeyId) { static_cast(aFrame)->SetKeyId(aKeyId); } uint32_t otMacFrameGetFrameCounter(otRadioFrame *aFrame) { diff --git a/examples/platforms/utils/mac_frame.h b/examples/platforms/utils/mac_frame.h index 8f0c6794f5a..1853ed1a185 100644 --- a/examples/platforms/utils/mac_frame.h +++ b/examples/platforms/utils/mac_frame.h @@ -221,9 +221,9 @@ void otMacFrameGenerateImmAck(const otRadioFrame *aFrame, bool aIsFramePending, */ otError otMacFrameGenerateEnhAck(const otRadioFrame *aFrame, bool aIsFramePending, - const uint8_t * aIeData, + const uint8_t *aIeData, uint8_t aIeLength, - otRadioFrame * aAckFrame); + otRadioFrame *aAckFrame); /** * Set CSL IE content into the frame. diff --git a/examples/platforms/utils/otns_utils.cpp b/examples/platforms/utils/otns_utils.cpp index e343503d162..1c8db69c96b 100644 --- a/examples/platforms/utils/otns_utils.cpp +++ b/examples/platforms/utils/otns_utils.cpp @@ -43,9 +43,6 @@ using namespace ot; #if OPENTHREAD_CONFIG_OTNS_ENABLE OT_TOOL_WEAK -void otPlatOtnsStatus(const char *aStatus) -{ - LogAlways("[OTNS] %s", aStatus); -} +void otPlatOtnsStatus(const char *aStatus) { LogAlways("[OTNS] %s", aStatus); } #endif // OPENTHREAD_CONFIG_OTNS_ENABLE diff --git a/examples/platforms/utils/settings_ram.c b/examples/platforms/utils/settings_ram.c index fd4abd9327e..d8e7c0fc079 100644 --- a/examples/platforms/utils/settings_ram.c +++ b/examples/platforms/utils/settings_ram.c @@ -66,10 +66,7 @@ void otPlatSettingsInit(otInstance *aInstance, const uint16_t *aSensitiveKeys, u sSettingsBufLength = 0; } -void otPlatSettingsDeinit(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} +void otPlatSettingsDeinit(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); } otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) { @@ -229,9 +226,6 @@ otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex) return error; } -void otPlatSettingsWipe(otInstance *aInstance) -{ - otPlatSettingsInit(aInstance, NULL, 0); -} +void otPlatSettingsWipe(otInstance *aInstance) { otPlatSettingsInit(aInstance, NULL, 0); } #endif // OPENTHREAD_SETTINGS_RAM diff --git a/examples/platforms/utils/soft_source_match_table.c b/examples/platforms/utils/soft_source_match_table.c index 458d1b5c509..3d8a8a6c190 100644 --- a/examples/platforms/utils/soft_source_match_table.c +++ b/examples/platforms/utils/soft_source_match_table.c @@ -46,10 +46,7 @@ #if RADIO_CONFIG_SRC_MATCH_SHORT_ENTRY_NUM || RADIO_CONFIG_SRC_MATCH_EXT_ENTRY_NUM static uint16_t sPanId = 0; -void utilsSoftSrcMatchSetPanId(uint16_t aPanId) -{ - sPanId = aPanId; -} +void utilsSoftSrcMatchSetPanId(uint16_t aPanId) { sPanId = aPanId; } #endif // RADIO_CONFIG_SRC_MATCH_SHORT_ENTRY_NUM || RADIO_CONFIG_SRC_MATCH_EXT_ENTRY_NUM #if RADIO_CONFIG_SRC_MATCH_SHORT_ENTRY_NUM @@ -148,7 +145,7 @@ void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); - otLogDebgPlat("Clear ShortAddr entries", NULL); + otLogDebgPlat("Clear ShortAddr entries"); memset(srcMatchShortEntry, 0, sizeof(srcMatchShortEntry)); } @@ -260,7 +257,7 @@ void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); - otLogDebgPlat("Clear ExtAddr entries", NULL); + otLogDebgPlat("Clear ExtAddr entries"); memset(srcMatchExtEntry, 0, sizeof(srcMatchExtEntry)); } diff --git a/.lgtm.yml b/examples/platforms/zephyr/CMakeLists.txt similarity index 86% rename from .lgtm.yml rename to examples/platforms/zephyr/CMakeLists.txt index 9051e95d8af..dcdad63b8ce 100644 --- a/.lgtm.yml +++ b/examples/platforms/zephyr/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 2020, The OpenThread Authors. +# Copyright (c) 2022, The OpenThread Authors. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -26,12 +26,5 @@ # POSSIBILITY OF SUCH DAMAGE. # -extraction: - cpp: - prepare: - packages: - - cmake - - ninja-build - index: - build_command: - - THREAD_VERSION=1.2 NODE_MODE=rcp OT_NATIVE_IP=1 ./script/test build +# Intentionally empty, the file is only needed to enable "zephyr" target +# as OT platform for CMake diff --git a/examples/platforms/zephyr/README.md b/examples/platforms/zephyr/README.md new file mode 100644 index 00000000000..b6e9828823d --- /dev/null +++ b/examples/platforms/zephyr/README.md @@ -0,0 +1,3 @@ +The OpenThread stack is integrated with ZephyrOS and nRF Connect SDK. + +See the [Zephyr's OpenThread platform](https://github.com/zephyrproject-rtos/zephyr/tree/main/modules/openthread) and [CLI example](https://github.com/nrfconnect/sdk-nrf/tree/main/samples/openthread/cli) for more information about the integration. diff --git a/include/Makefile.am b/include/Makefile.am index 722775d8e30..6ce6a7dffe0 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -68,6 +68,7 @@ openthread_headers = \ openthread/link_metrics.h \ openthread/link_raw.h \ openthread/logging.h \ + openthread/mesh_diag.h \ openthread/message.h \ openthread/multi_radio.h \ openthread/nat64.h \ @@ -102,6 +103,7 @@ ot_platform_headers = \ openthread/platform/crypto.h \ openthread/platform/debug_uart.h \ openthread/platform/diag.h \ + openthread/platform/dns.h \ openthread/platform/dso_transport.h \ openthread/platform/entropy.h \ openthread/platform/flash.h \ diff --git a/include/openthread/BUILD.gn b/include/openthread/BUILD.gn index 931c7ac1242..fba1dfe9c93 100644 --- a/include/openthread/BUILD.gn +++ b/include/openthread/BUILD.gn @@ -72,6 +72,7 @@ source_set("openthread") { "link_metrics.h", "link_raw.h", "logging.h", + "mesh_diag.h", "message.h", "multi_radio.h", "nat64.h", @@ -86,6 +87,7 @@ source_set("openthread") { "platform/crypto.h", "platform/debug_uart.h", "platform/diag.h", + "platform/dns.h", "platform/dso_transport.h", "platform/entropy.h", "platform/flash.h", diff --git a/include/openthread/backbone_router_ftd.h b/include/openthread/backbone_router_ftd.h index a6196872589..d4041d68af6 100644 --- a/include/openthread/backbone_router_ftd.h +++ b/include/openthread/backbone_router_ftd.h @@ -64,7 +64,14 @@ typedef enum } otBackboneRouterState; /** - * This function enables or disables Backbone functionality. + * Enables or disables Backbone functionality. + * + * If enabled, a Server Data Request message `SRV_DATA.ntf` is triggered for the attached + * device if there is no Backbone Router Service in the Thread Network Data. + * + * If disabled, `SRV_DATA.ntf` is triggered if the Backbone Router is in the Primary state. + * + * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aEnable TRUE to enable Backbone functionality, FALSE otherwise. @@ -78,7 +85,7 @@ typedef enum void otBackboneRouterSetEnabled(otInstance *aInstance, bool aEnable); /** - * This function gets the Backbone Router state. + * Gets the Backbone Router #otBackboneRouterState. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -95,7 +102,9 @@ void otBackboneRouterSetEnabled(otInstance *aInstance, bool aEnable); otBackboneRouterState otBackboneRouterGetState(otInstance *aInstance); /** - * This function gets the local Backbone Router configuration. + * Gets the local Backbone Router configuration. + * + * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[out] aConfig A pointer where to put local Backbone Router configuration. @@ -110,7 +119,12 @@ otBackboneRouterState otBackboneRouterGetState(otInstance *aInstance); void otBackboneRouterGetConfig(otInstance *aInstance, otBackboneRouterConfig *aConfig); /** - * This function sets the local Backbone Router configuration. + * Sets the local Backbone Router configuration #otBackboneRouterConfig. + * + * A Server Data Request message `SRV_DATA.ntf` is initiated automatically if BBR Dataset changes for Primary + * Backbone Router. + * + * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aConfig A pointer to the Backbone Router configuration to take effect. @@ -127,7 +141,11 @@ void otBackboneRouterGetConfig(otInstance *aInstance, otBackboneRouterConfig *aC otError otBackboneRouterSetConfig(otInstance *aInstance, const otBackboneRouterConfig *aConfig); /** - * This function explicitly registers local Backbone Router configuration. + * Explicitly registers local Backbone Router configuration. + * + * A Server Data Request message `SRV_DATA.ntf` is triggered for the attached device. + * + * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -189,15 +207,16 @@ otError otBackboneRouterGetDomainPrefix(otInstance *aInstance, otBorderRouterCon * * */ -void otBackboneRouterConfigNextDuaRegistrationResponse(otInstance * aInstance, +void otBackboneRouterConfigNextDuaRegistrationResponse(otInstance *aInstance, const otIp6InterfaceIdentifier *aMlIid, uint8_t aStatus); /** - * This method configures response status for next Multicast Listener Registration. + * Configures the response status for the next Multicast Listener Registration. * - * Note: available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. - * Only used for test and certification. + * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE`, + * `OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE`, and + * `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` are enabled. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aStatus The status to respond. @@ -223,9 +242,9 @@ typedef enum * @param[in] aAddress The IPv6 multicast address of the Multicast Listener. * */ -typedef void (*otBackboneRouterMulticastListenerCallback)(void * aContext, +typedef void (*otBackboneRouterMulticastListenerCallback)(void *aContext, otBackboneRouterMulticastListenerEvent aEvent, - const otIp6Address * aAddress); + const otIp6Address *aAddress); /** * This method sets the Backbone Router Multicast Listener callback. @@ -235,15 +254,16 @@ typedef void (*otBackboneRouterMulticastListenerCallback)(void * * @param[in] aContext A user context pointer. * */ -void otBackboneRouterSetMulticastListenerCallback(otInstance * aInstance, +void otBackboneRouterSetMulticastListenerCallback(otInstance *aInstance, otBackboneRouterMulticastListenerCallback aCallback, - void * aContext); + void *aContext); /** - * This method clears the Multicast Listeners. + * Clears the Multicast Listeners. * - * Note: available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. - * Only used for test and certification. + * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE`, + * `OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE`, and + * `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` are enabled. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -254,10 +274,13 @@ void otBackboneRouterSetMulticastListenerCallback(otInstance * void otBackboneRouterMulticastListenerClear(otInstance *aInstance); /** - * This method adds a Multicast Listener. + * Adds a Multicast Listener with a timeout value, in seconds. * - * Note: available only when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. - * Only used for test and certification. + * Pass `0` to use the default MLR timeout. + * + * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE`, + * `OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE`, and + * `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` are enabled. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aAddress The Multicast Listener address. @@ -306,9 +329,9 @@ typedef struct otBackboneRouterMulticastListenerInfo * @sa otBackboneRouterMulticastListenerAdd * */ -otError otBackboneRouterMulticastListenerGetNext(otInstance * aInstance, +otError otBackboneRouterMulticastListenerGetNext(otInstance *aInstance, otBackboneRouterMulticastListenerIterator *aIterator, - otBackboneRouterMulticastListenerInfo * aListenerInfo); + otBackboneRouterMulticastListenerInfo *aListenerInfo); /** * Represents the ND Proxy events. @@ -331,9 +354,9 @@ typedef enum * `OT_BACKBONE_ROUTER_NDPROXY_CLEARED`. * */ -typedef void (*otBackboneRouterNdProxyCallback)(void * aContext, +typedef void (*otBackboneRouterNdProxyCallback)(void *aContext, otBackboneRouterNdProxyEvent aEvent, - const otIp6Address * aDua); + const otIp6Address *aDua); /** * This method sets the Backbone Router ND Proxy callback. @@ -343,9 +366,9 @@ typedef void (*otBackboneRouterNdProxyCallback)(void * aCo * @param[in] aContext A user context pointer. * */ -void otBackboneRouterSetNdProxyCallback(otInstance * aInstance, +void otBackboneRouterSetNdProxyCallback(otInstance *aInstance, otBackboneRouterNdProxyCallback aCallback, - void * aContext); + void *aContext); /** * Represents the Backbone Router ND Proxy info. @@ -369,8 +392,8 @@ typedef struct otBackboneRouterNdProxyInfo * @retval OT_ERROR_NOT_FOUND Failed to find the Domain Unicast Address in the ND Proxy table. * */ -otError otBackboneRouterGetNdProxyInfo(otInstance * aInstance, - const otIp6Address * aDua, +otError otBackboneRouterGetNdProxyInfo(otInstance *aInstance, + const otIp6Address *aDua, otBackboneRouterNdProxyInfo *aNdProxyInfo); /** @@ -392,9 +415,9 @@ typedef enum * @param[in] aDomainPrefix The new Domain Prefix if added or changed, nullptr otherwise. * */ -typedef void (*otBackboneRouterDomainPrefixCallback)(void * aContext, +typedef void (*otBackboneRouterDomainPrefixCallback)(void *aContext, otBackboneRouterDomainPrefixEvent aEvent, - const otIp6Prefix * aDomainPrefix); + const otIp6Prefix *aDomainPrefix); /** * This method sets the Backbone Router Domain Prefix callback. * @@ -403,9 +426,9 @@ typedef void (*otBackboneRouterDomainPrefixCallback)(void * * @param[in] aContext A user context pointer. * */ -void otBackboneRouterSetDomainPrefixCallback(otInstance * aInstance, +void otBackboneRouterSetDomainPrefixCallback(otInstance *aInstance, otBackboneRouterDomainPrefixCallback aCallback, - void * aContext); + void *aContext); /** * @} diff --git a/include/openthread/border_agent.h b/include/openthread/border_agent.h index 83babfa142f..e412f6d8eea 100644 --- a/include/openthread/border_agent.h +++ b/include/openthread/border_agent.h @@ -51,6 +51,12 @@ extern "C" { * */ +/** + * The length of Border Agent/Router ID in bytes. + * + */ +#define OT_BORDER_AGENT_ID_LENGTH (16) + /** * This enumeration defines the Border Agent state. * @@ -82,6 +88,24 @@ otBorderAgentState otBorderAgentGetState(otInstance *aInstance); */ uint16_t otBorderAgentGetUdpPort(otInstance *aInstance); +/** + * Gets the randomly generated Border Agent ID. + * + * The ID is saved in persistent storage and survives reboots. The typical use case of the ID is to + * be published in the MeshCoP mDNS service as the `id` TXT value for the client to identify this + * Border Router/Agent device. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[out] aId A pointer to buffer to receive the ID. + * @param[inout] aLength Specifies the length of `aId` when used as input and receives the length + * actual ID data copied to `aId` when used as output. + * + * @retval OT_ERROR_INVALID_ARGS If value of `aLength` if smaller than `OT_BORDER_AGENT_ID_LENGTH`. + * @retval OT_ERROR_NONE If successfully retrieved the Border Agent ID. + * + */ +otError otBorderAgentGetId(otInstance *aInstance, uint8_t *aId, uint16_t *aLength); + /** * @} * diff --git a/include/openthread/border_router.h b/include/openthread/border_router.h index fb7dab06536..a945f91fb29 100644 --- a/include/openthread/border_router.h +++ b/include/openthread/border_router.h @@ -105,9 +105,9 @@ otError otBorderRouterRemoveOnMeshPrefix(otInstance *aInstance, const otIp6Prefi * @retval OT_ERROR_NOT_FOUND No subsequent On Mesh prefix exists in the Thread Network Data. * */ -otError otBorderRouterGetNextOnMeshPrefix(otInstance * aInstance, +otError otBorderRouterGetNextOnMeshPrefix(otInstance *aInstance, otNetworkDataIterator *aIterator, - otBorderRouterConfig * aConfig); + otBorderRouterConfig *aConfig); /** * Add an external route configuration to the local network data. @@ -150,7 +150,7 @@ otError otBorderRouterRemoveRoute(otInstance *aInstance, const otIp6Prefix *aPre * @retval OT_ERROR_NOT_FOUND No subsequent external route entry exists in the Thread Network Data. * */ -otError otBorderRouterGetNextRoute(otInstance * aInstance, +otError otBorderRouterGetNextRoute(otInstance *aInstance, otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig); diff --git a/include/openthread/border_routing.h b/include/openthread/border_routing.h index 8cfec1bdb32..961554ea5aa 100644 --- a/include/openthread/border_routing.h +++ b/include/openthread/border_routing.h @@ -106,6 +106,18 @@ typedef struct otBorderRoutingPrefixTableEntry uint32_t mPreferredLifetime; ///< Preferred lifetime of the on-link prefix when `mIsOnLink` is true. } otBorderRoutingPrefixTableEntry; +/** + * This enumeration represents the state of Border Routing Manager. + * + */ +typedef enum +{ + OT_BORDER_ROUTING_STATE_UNINITIALIZED, ///< Routing Manager is uninitialized. + OT_BORDER_ROUTING_STATE_DISABLED, ///< Routing Manager is initialized but disabled. + OT_BORDER_ROUTING_STATE_STOPPED, ///< Routing Manager in initialized and enabled but currently stopped. + OT_BORDER_ROUTING_STATE_RUNNING, ///< Routing Manager is initialized, enabled, and running. +} otBorderRoutingState; + /** * This method initializes the Border Routing Manager on given infrastructure interface. * @@ -141,23 +153,37 @@ otError otBorderRoutingInit(otInstance *aInstance, uint32_t aInfraIfIndex, bool otError otBorderRoutingSetEnabled(otInstance *aInstance, bool aEnabled); /** - * This function gets the preference used when advertising Route Info Options (e.g., for discovered OMR prefixes) in - * Router Advertisement messages sent over the infrastructure link. + * Gets the current state of Border Routing Manager. * - * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aInstance A pointer to an OpenThread instance. * - * @returns The OMR prefix advertisement preference. + * @returns The current state of Border Routing Manager. + * + */ +otBorderRoutingState otBorderRoutingGetState(otInstance *aInstance); + +/** + * This function gets the current preference used when advertising Route Info Options (RIO) in Router Advertisement + * messages sent over the infrastructure link. + * + * The RIO preference is determined as follows: + * + * - If explicitly set by user by calling `otBorderRoutingSetRouteInfoOptionPreference()`, the given preference is + * used. + * - Otherwise, it is determined based on device's current role: Medium preference when in router/leader role and + * low preference when in child role. + * + * @returns The current Route Info Option preference. * */ otRoutePreference otBorderRoutingGetRouteInfoOptionPreference(otInstance *aInstance); /** - * This function sets the preference to use when advertising Route Info Options in Router Advertisement messages sent - * over the infrastructure link, for example for discovered OMR prefixes. + * This function explicitly sets the preference to use when advertising Route Info Options (RIO) in Router + * Advertisement messages sent over the infrastructure link. * - * By default BR will use `medium` preference level, but this function allows the default value to be changed. As an - * example, it can be set to `low` preference in the case where device is a temporary BR (a mobile BR or a - * battery-powered BR) to indicate that other BRs (if any) should be preferred over this BR on the infrastructure link. + * After a call to this function, BR will use the given preference for all its advertised RIOs. The preference can be + * cleared by calling `otBorderRoutingClearRouteInfoOptionPreference()`. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aPreference The route preference to use. @@ -165,6 +191,17 @@ otRoutePreference otBorderRoutingGetRouteInfoOptionPreference(otInstance *aInsta */ void otBorderRoutingSetRouteInfoOptionPreference(otInstance *aInstance, otRoutePreference aPreference); +/** + * This function clears a previously set preference value for advertised Route Info Options. + * + * After a call to this function, BR will use device's role to determine the RIO preference: Medium preference when + * in router/leader role and low preference when in child role. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + */ +void otBorderRoutingClearRouteInfoOptionPreference(otInstance *aInstance); + /** * Gets the local Off-Mesh-Routable (OMR) Prefix, for example `fdfc:1ff5:1512:5622::/64`. * @@ -190,33 +227,47 @@ otError otBorderRoutingGetOmrPrefix(otInstance *aInstance, otIp6Prefix *aPrefix) * @param[out] aPrefix A pointer to output the favored OMR prefix. * @param[out] aPreference A pointer to output the preference associated the favored prefix. * - * @retval OT_ERROR_INVALID_STATE The Border Routing Manager is not initialized yet. + * @retval OT_ERROR_INVALID_STATE The Border Routing Manager is not running yet. * @retval OT_ERROR_NONE Successfully retrieved the favored OMR prefix. * */ otError otBorderRoutingGetFavoredOmrPrefix(otInstance *aInstance, otIp6Prefix *aPrefix, otRoutePreference *aPreference); /** - * Gets the On-Link Prefix for the adjacent infrastructure link, for example `fd41:2650:a6f5:0::/64`. + * Gets the local On-Link Prefix for the adjacent infrastructure link. * - * An On-Link Prefix is a 64-bit prefix that's advertised on the infrastructure link if there isn't already a usable - * on-link prefix being advertised on the link. + * The local On-Link Prefix is a 64-bit prefix that's advertised on the infrastructure link if there isn't already a + * usable on-link prefix being advertised on the link. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[out] aPrefix A pointer to where the prefix will be output to. * * @retval OT_ERROR_INVALID_STATE The Border Routing Manager is not initialized yet. - * @retval OT_ERROR_NONE Successfully retrieved the on-link prefix. + * @retval OT_ERROR_NONE Successfully retrieved the local on-link prefix. * */ otError otBorderRoutingGetOnLinkPrefix(otInstance *aInstance, otIp6Prefix *aPrefix); +/** + * Gets the currently favored On-Link Prefix. + * + * The favored prefix is either a discovered on-link prefix on the infrastructure link or the local on-link prefix. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[out] aPrefix A pointer to where the prefix will be output to. + * + * @retval OT_ERROR_INVALID_STATE The Border Routing Manager is not initialized yet. + * @retval OT_ERROR_NONE Successfully retrieved the favored on-link prefix. + * + */ +otError otBorderRoutingGetFavoredOnLinkPrefix(otInstance *aInstance, otIp6Prefix *aPrefix); + /** * Gets the local NAT64 Prefix of the Border Router. * * NAT64 Prefix might not be advertised in the Thread network. * - * `OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE` must be enabled. + * `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` must be enabled. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[out] aPrefix A pointer to where the prefix will be output to. @@ -227,6 +278,23 @@ otError otBorderRoutingGetOnLinkPrefix(otInstance *aInstance, otIp6Prefix *aPref */ otError otBorderRoutingGetNat64Prefix(otInstance *aInstance, otIp6Prefix *aPrefix); +/** + * Gets the currently favored NAT64 prefix. + * + * The favored NAT64 prefix can be discovered from infrastructure link or can be this device's local NAT64 prefix. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[out] aPrefix A pointer to output the favored NAT64 prefix. + * @param[out] aPreference A pointer to output the preference associated the favored prefix. + * + * @retval OT_ERROR_INVALID_STATE The Border Routing Manager is not initialized yet. + * @retval OT_ERROR_NONE Successfully retrieved the favored NAT64 prefix. + * + */ +otError otBorderRoutingGetFavoredNat64Prefix(otInstance *aInstance, + otIp6Prefix *aPrefix, + otRoutePreference *aPreference); + /** * This function initializes an `otBorderRoutingPrefixTableIterator`. * @@ -254,9 +322,9 @@ void otBorderRoutingPrefixTableInitIterator(otInstance *aInstance, otBorderRouti * @retval OT_ERROR_NOT_FOUND No more entries in the table. * */ -otError otBorderRoutingGetNextPrefixTableEntry(otInstance * aInstance, +otError otBorderRoutingGetNextPrefixTableEntry(otInstance *aInstance, otBorderRoutingPrefixTableIterator *aIterator, - otBorderRoutingPrefixTableEntry * aEntry); + otBorderRoutingPrefixTableEntry *aEntry); /** * @} diff --git a/include/openthread/channel_manager.h b/include/openthread/channel_manager.h index f7fb8e11c88..73074580680 100644 --- a/include/openthread/channel_manager.h +++ b/include/openthread/channel_manager.h @@ -55,12 +55,12 @@ extern "C" { */ /** - * This function requests a Thread network channel change. + * Requests a Thread network channel change. * - * The network switches to the given channel after a specified delay (see otChannelManagerSetDelay()). The channel + * The network switches to the given channel after a specified delay (see #otChannelManagerSetDelay()). The channel * change is performed by updating the Pending Operational Dataset. * - * A subsequent call to this function will cancel an ongoing previously requested channel change. + * A subsequent call will cancel an ongoing previously requested channel change. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aChannel The new channel for the Thread network. @@ -87,9 +87,9 @@ uint8_t otChannelManagerGetRequestedChannel(otInstance *aInstance); uint16_t otChannelManagerGetDelay(otInstance *aInstance); /** - * This function sets the delay (in seconds) used for a channel change. + * Sets the delay (in seconds) used for a channel change. * - * The delay should preferably be longer than maximum data poll interval used by all sleepy-end-devices within the + * The delay should preferably be longer than the maximum data poll interval used by all sleepy-end-devices within the * Thread network. * * @param[in] aInstance A pointer to an OpenThread instance. @@ -105,7 +105,7 @@ otError otChannelManagerSetDelay(otInstance *aInstance, uint16_t aDelay); * This function requests that `ChannelManager` checks and selects a new channel and starts a channel change. * * Unlike the `otChannelManagerRequestChannelChange()` where the channel must be given as a parameter, this function - * asks the `ChannelManager` to select a channel by itself (based of collected channel quality info). + * asks the `ChannelManager` to select a channel by itself (based on collected channel quality info). * * Once called, the Channel Manager will perform the following 3 steps: * @@ -132,7 +132,7 @@ otError otChannelManagerSetDelay(otInstance *aInstance, uint16_t aDelay); otError otChannelManagerRequestChannelSelect(otInstance *aInstance, bool aSkipQualityCheck); /** - * This function enables/disables the auto-channel-selection functionality. + * Enables or disables the auto-channel-selection functionality. * * When enabled, `ChannelManager` will periodically invoke a `RequestChannelSelect(false)`. The period interval * can be set by `SetAutoChannelSelectionInterval()`. @@ -154,7 +154,7 @@ void otChannelManagerSetAutoChannelSelectionEnabled(otInstance *aInstance, bool bool otChannelManagerGetAutoChannelSelectionEnabled(otInstance *aInstance); /** - * This function sets the period interval (in seconds) used by auto-channel-selection functionality. + * Sets the period interval (in seconds) used by auto-channel-selection functionality. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aInterval The interval in seconds. @@ -176,7 +176,7 @@ otError otChannelManagerSetAutoChannelSelectionInterval(otInstance *aInstance, u uint32_t otChannelManagerGetAutoChannelSelectionInterval(otInstance *aInstance); /** - * This function gets the supported channel mask. + * Gets the supported channel mask. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -186,7 +186,7 @@ uint32_t otChannelManagerGetAutoChannelSelectionInterval(otInstance *aInstance); uint32_t otChannelManagerGetSupportedChannels(otInstance *aInstance); /** - * This function sets the supported channel mask. + * Sets the supported channel mask. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aChannelMask A channel mask. @@ -195,7 +195,7 @@ uint32_t otChannelManagerGetSupportedChannels(otInstance *aInstance); void otChannelManagerSetSupportedChannels(otInstance *aInstance, uint32_t aChannelMask); /** - * This function gets the favored channel mask. + * Gets the favored channel mask. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -205,7 +205,7 @@ void otChannelManagerSetSupportedChannels(otInstance *aInstance, uint32_t aChann uint32_t otChannelManagerGetFavoredChannels(otInstance *aInstance); /** - * This function sets the favored channel mask. + * Sets the favored channel mask. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aChannelMask A channel mask. @@ -214,7 +214,7 @@ uint32_t otChannelManagerGetFavoredChannels(otInstance *aInstance); void otChannelManagerSetFavoredChannels(otInstance *aInstance, uint32_t aChannelMask); /** - * This function gets the CCA failure rate threshold + * Gets the CCA failure rate threshold. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -224,7 +224,7 @@ void otChannelManagerSetFavoredChannels(otInstance *aInstance, uint32_t aChannel uint16_t otChannelManagerGetCcaFailureRateThreshold(otInstance *aInstance); /** - * This function sets the CCA failure rate threshold + * Sets the CCA failure rate threshold. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aThreshold A CCA failure rate threshold. Value 0 maps to 0% and 0xffff maps to 100%. diff --git a/include/openthread/channel_monitor.h b/include/openthread/channel_monitor.h index 0c8929c0de5..12fa92af167 100644 --- a/include/openthread/channel_monitor.h +++ b/include/openthread/channel_monitor.h @@ -64,13 +64,13 @@ extern "C" { */ /** - * This function enables/disables the Channel Monitoring operation. + * Enables or disables the Channel Monitoring operation. * * Once operation starts, any previously collected data is cleared. However, after operation is disabled, the previous * collected data is still valid and can be read. * - * @note OpenThread core internally enables/disables the Channel Monitoring operation when the IPv6 interface is - * brought up/down (i.e., call to `otIp6SetEnabled()`). + * @note OpenThread core internally enables or disables the Channel Monitoring operation when the IPv6 interface is + * brought up or down, for example in a call to `otIp6SetEnabled()`. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aEnabled TRUE to enable/start Channel Monitoring operation, FALSE to disable/stop it. diff --git a/include/openthread/child_supervision.h b/include/openthread/child_supervision.h index 5c01721655d..100e0280c15 100644 --- a/include/openthread/child_supervision.h +++ b/include/openthread/child_supervision.h @@ -29,7 +29,7 @@ /** * @file * @brief - * This file includes the OpenThread API for child supervision feature + * This file includes the OpenThread API for child supervision feature. */ #ifndef OPENTHREAD_CHILD_SUPERVISION_H_ @@ -47,38 +47,35 @@ extern "C" { * @brief * This module includes functions for child supervision feature. * - * The functions in this module are available when child supervision feature - * (`OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE`) is enabled. - * * @{ * */ /** - * Get the child supervision interval (in seconds). + * Gets the child supervision interval (in seconds) on a child. * - * Child supervision feature provides a mechanism for parent to ensure that a message is sent to each sleepy child - * within the supervision interval. If there is no transmission to the child within the supervision interval, - * OpenThread enqueues and sends a supervision message (a data message with empty payload) to the child. + * Child supervision feature provides a mechanism for a sleepy child to ask its parent to ensure to send a message to + * it within the supervision interval. If there is no transmission to the child within the supervision interval, + * parent sends a supervision message (a data message with empty payload) to the child. * * @param[in] aInstance A pointer to an OpenThread instance. * - * @returns The child supervision interval. Zero indicates that child supervision is disabled. + * @returns The child supervision interval. Zero indicates that supervision is disabled. * */ uint16_t otChildSupervisionGetInterval(otInstance *aInstance); /** - * Set the child supervision interval (in seconds). + * Sets the child supervision interval (in seconds) on the child. * * @param[in] aInstance A pointer to an OpenThread instance. - * @param[in] aInterval The supervision interval (in seconds). Zero to disable supervision on parent. + * @param[in] aInterval The supervision interval (in seconds). Zero to disable supervision. * */ void otChildSupervisionSetInterval(otInstance *aInstance, uint16_t aInterval); /** - * Get the supervision check timeout interval (in seconds). + * Gets the supervision check timeout interval (in seconds) on the child. * * If the device is a sleepy child and it does not hear from its parent within the specified check timeout, it initiates * the re-attach process (MLE Child Update Request/Response exchange with its parent). @@ -91,7 +88,7 @@ void otChildSupervisionSetInterval(otInstance *aInstance, uint16_t aInterval); uint16_t otChildSupervisionGetCheckTimeout(otInstance *aInstance); /** - * Set the supervision check timeout interval (in seconds). + * Sets the supervision check timeout interval (in seconds). * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aTimeout The check timeout (in seconds). Zero to disable supervision check on the child. @@ -99,6 +96,21 @@ uint16_t otChildSupervisionGetCheckTimeout(otInstance *aInstance); */ void otChildSupervisionSetCheckTimeout(otInstance *aInstance, uint16_t aTimeout); +/** + * Get the value of supervision check timeout failure counter. + * + * The counter tracks the number of supervision check failures on the child. It is incremented when the child does + * not hear from its parent within the specified check timeout interval. + * + */ +uint16_t otChildSupervisionGetCheckFailureCounter(otInstance *aInstance); + +/** + * Reset the supervision check timeout failure counter to zero. + * + */ +void otChildSupervisionResetCheckFailureCounter(otInstance *aInstance); + /** * @} * diff --git a/include/openthread/cli.h b/include/openthread/cli.h index 3fa5c565337..e0d9e66ee88 100644 --- a/include/openthread/cli.h +++ b/include/openthread/cli.h @@ -52,9 +52,9 @@ extern "C" { typedef struct otCliCommand { const char *mName; ///< A pointer to the command string. - void (*mCommand)(void * aContext, - uint8_t aArgsLength, - char * aArgs[]); ///< A function pointer to process the command. + otError (*mCommand)(void *aContext, + uint8_t aArgsLength, + char *aArgs[]); ///< A function pointer to process the command. } otCliCommand; /** @@ -104,8 +104,10 @@ void otCliInputLine(char *aBuf); * @param[in] aLength @p aUserCommands length. * @param[in] aContext @p The context passed to the handler. * + * @retval OT_ERROR_NONE Successfully updated command table with commands from @p aUserCommands. + * @retval OT_ERROR_FAILED Maximum number of command entries have already been set. */ -void otCliSetUserCommands(const otCliCommand *aUserCommands, uint8_t aLength, void *aContext); +otError otCliSetUserCommands(const otCliCommand *aUserCommands, uint8_t aLength, void *aContext); /** * Write a number of bytes to the CLI console as a hex string. diff --git a/include/openthread/coap.h b/include/openthread/coap.h index c2e036bb203..30faca6f8ad 100644 --- a/include/openthread/coap.h +++ b/include/openthread/coap.h @@ -341,8 +341,8 @@ typedef enum otCoapBlockSzx * @retval OT_ERROR_RESPONSE_TIMEOUT No response or acknowledgment received during timeout period. * */ -typedef void (*otCoapResponseHandler)(void * aContext, - otMessage * aMessage, +typedef void (*otCoapResponseHandler)(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aResult); @@ -375,7 +375,7 @@ typedef void (*otCoapRequestHandler)(void *aContext, otMessage *aMessage, const * @retval OT_ERROR_NO_FRAME_RECEIVED Block segment missing. * */ -typedef otError (*otCoapBlockwiseReceiveHook)(void * aContext, +typedef otError (*otCoapBlockwiseReceiveHook)(void *aContext, const uint8_t *aBlock, uint32_t aPosition, uint16_t aBlockLength, @@ -402,11 +402,11 @@ typedef otError (*otCoapBlockwiseReceiveHook)(void * aContext, * @retval OT_ERROR_INVALID_ARGS Block at @p aPosition does not exist. * */ -typedef otError (*otCoapBlockwiseTransmitHook)(void * aContext, - uint8_t * aBlock, +typedef otError (*otCoapBlockwiseTransmitHook)(void *aContext, + uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, - bool * aMore); + bool *aMore); /** * This structure represents a CoAP resource. @@ -414,9 +414,9 @@ typedef otError (*otCoapBlockwiseTransmitHook)(void * aContext, */ typedef struct otCoapResource { - const char * mUriPath; ///< The URI Path string + const char *mUriPath; ///< The URI Path string otCoapRequestHandler mHandler; ///< The callback for handling a received request - void * mContext; ///< Application-specific context + void *mContext; ///< Application-specific context struct otCoapResource *mNext; ///< The next CoAP resource in the list } otCoapResource; @@ -426,7 +426,7 @@ typedef struct otCoapResource */ typedef struct otCoapBlockwiseResource { - const char * mUriPath; ///< The URI Path string + const char *mUriPath; ///< The URI Path string otCoapRequestHandler mHandler; ///< The callback for handling a received request /** The callback for handling incoming block-wise transfer. @@ -440,7 +440,7 @@ typedef struct otCoapBlockwiseResource * configuration is enabled. */ otCoapBlockwiseTransmitHook mTransmitHook; - void * mContext; ///< Application-specific context + void *mContext; ///< Application-specific context struct otCoapBlockwiseResource *mNext; ///< The next CoAP resource in the list } otCoapBlockwiseResource; @@ -883,11 +883,11 @@ otMessage *otCoapNewMessage(otInstance *aInstance, const otMessageSettings *aSet * @retval OT_ERROR_NO_BUFS Failed to allocate retransmission data. * */ -otError otCoapSendRequestWithParameters(otInstance * aInstance, - otMessage * aMessage, - const otMessageInfo * aMessageInfo, +otError otCoapSendRequestWithParameters(otInstance *aInstance, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, otCoapResponseHandler aHandler, - void * aContext, + void *aContext, const otCoapTxParameters *aTxParameters); /** @@ -913,12 +913,12 @@ otError otCoapSendRequestWithParameters(otInstance * aInstance, * @retval OT_ERROR_NO_BUFS Failed to allocate retransmission data. * */ -otError otCoapSendRequestBlockWiseWithParameters(otInstance * aInstance, - otMessage * aMessage, - const otMessageInfo * aMessageInfo, +otError otCoapSendRequestBlockWiseWithParameters(otInstance *aInstance, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, otCoapResponseHandler aHandler, - void * aContext, - const otCoapTxParameters * aTxParameters, + void *aContext, + const otCoapTxParameters *aTxParameters, otCoapBlockwiseTransmitHook aTransmitHook, otCoapBlockwiseReceiveHook aReceiveHook); @@ -944,11 +944,11 @@ otError otCoapSendRequestBlockWiseWithParameters(otInstance * aIn * @retval OT_ERROR_NO_BUFS Failed to allocate retransmission data. * */ -static inline otError otCoapSendRequestBlockWise(otInstance * aInstance, - otMessage * aMessage, - const otMessageInfo * aMessageInfo, +static inline otError otCoapSendRequestBlockWise(otInstance *aInstance, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, otCoapResponseHandler aHandler, - void * aContext, + void *aContext, otCoapBlockwiseTransmitHook aTransmitHook, otCoapBlockwiseReceiveHook aReceiveHook) { @@ -973,11 +973,11 @@ static inline otError otCoapSendRequestBlockWise(otInstance * aIn * @retval OT_ERROR_NO_BUFS Failed to allocate retransmission data. * */ -static inline otError otCoapSendRequest(otInstance * aInstance, - otMessage * aMessage, - const otMessageInfo * aMessageInfo, +static inline otError otCoapSendRequest(otInstance *aInstance, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, otCoapResponseHandler aHandler, - void * aContext) + void *aContext) { // NOLINTNEXTLINE(modernize-use-nullptr) return otCoapSendRequestWithParameters(aInstance, aMessage, aMessageInfo, aHandler, aContext, NULL); @@ -1063,9 +1063,9 @@ void otCoapSetDefaultHandler(otInstance *aInstance, otCoapRequestHandler aHandle * @retval OT_ERROR_NO_BUFS Insufficient buffers available to send the CoAP response. * */ -otError otCoapSendResponseWithParameters(otInstance * aInstance, - otMessage * aMessage, - const otMessageInfo * aMessageInfo, +otError otCoapSendResponseWithParameters(otInstance *aInstance, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, const otCoapTxParameters *aTxParameters); /** @@ -1085,11 +1085,11 @@ otError otCoapSendResponseWithParameters(otInstance * aInstance, * @retval OT_ERROR_NO_BUFS Insufficient buffers available to send the CoAP response. * */ -otError otCoapSendResponseBlockWiseWithParameters(otInstance * aInstance, - otMessage * aMessage, - const otMessageInfo * aMessageInfo, - const otCoapTxParameters * aTxParameters, - void * aContext, +otError otCoapSendResponseBlockWiseWithParameters(otInstance *aInstance, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, + const otCoapTxParameters *aTxParameters, + void *aContext, otCoapBlockwiseTransmitHook aTransmitHook); /** @@ -1108,10 +1108,10 @@ otError otCoapSendResponseBlockWiseWithParameters(otInstance * aI * @retval OT_ERROR_NO_BUFS Insufficient buffers available to send the CoAP response. * */ -static inline otError otCoapSendResponseBlockWise(otInstance * aInstance, - otMessage * aMessage, - const otMessageInfo * aMessageInfo, - void * aContext, +static inline otError otCoapSendResponseBlockWise(otInstance *aInstance, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, + void *aContext, otCoapBlockwiseTransmitHook aTransmitHook) { // NOLINTNEXTLINE(modernize-use-nullptr) diff --git a/include/openthread/coap_secure.h b/include/openthread/coap_secure.h index 5cb6bd9a02e..6f424096a59 100644 --- a/include/openthread/coap_secure.h +++ b/include/openthread/coap_secure.h @@ -108,7 +108,7 @@ void otCoapSecureStop(otInstance *aInstance); * @param[in] aPskIdLength The PSK Identity Length. * */ -void otCoapSecureSetPsk(otInstance * aInstance, +void otCoapSecureSetPsk(otInstance *aInstance, const uint8_t *aPsk, uint16_t aPskLength, const uint8_t *aPskIdentity, @@ -130,9 +130,9 @@ void otCoapSecureSetPsk(otInstance * aInstance, * @retval OT_ERROR_NO_BUFS Can't allocate memory for certificate. * */ -otError otCoapSecureGetPeerCertificateBase64(otInstance * aInstance, +otError otCoapSecureGetPeerCertificateBase64(otInstance *aInstance, unsigned char *aPeerCert, - size_t * aCertLength, + size_t *aCertLength, size_t aCertBufferSize); /** @@ -160,7 +160,7 @@ void otCoapSecureSetSslAuthMode(otInstance *aInstance, bool aVerifyPeerCertifica * @param[in] aPrivateKeyLength The length of the private key. * */ -void otCoapSecureSetCertificate(otInstance * aInstance, +void otCoapSecureSetCertificate(otInstance *aInstance, const uint8_t *aX509Cert, uint32_t aX509Length, const uint8_t *aPrivateKey, @@ -179,7 +179,7 @@ void otCoapSecureSetCertificate(otInstance * aInstance, * @param[in] aX509CaCertChainLength The length of chain. * */ -void otCoapSecureSetCaCertificateChain(otInstance * aInstance, +void otCoapSecureSetCaCertificateChain(otInstance *aInstance, const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength); @@ -195,10 +195,10 @@ void otCoapSecureSetCaCertificateChain(otInstance * aInstance, * @retval OT_ERROR_NONE Successfully started DTLS connection. * */ -otError otCoapSecureConnect(otInstance * aInstance, - const otSockAddr * aSockAddr, +otError otCoapSecureConnect(otInstance *aInstance, + const otSockAddr *aSockAddr, otHandleCoapSecureClientConnect aHandler, - void * aContext); + void *aContext); /** * This method stops the DTLS connection. @@ -252,10 +252,10 @@ bool otCoapSecureIsConnectionActive(otInstance *aInstance); * @retval OT_ERROR_INVALID_STATE DTLS connection was not initialized. * */ -otError otCoapSecureSendRequestBlockWise(otInstance * aInstance, - otMessage * aMessage, +otError otCoapSecureSendRequestBlockWise(otInstance *aInstance, + otMessage *aMessage, otCoapResponseHandler aHandler, - void * aContext, + void *aContext, otCoapBlockwiseTransmitHook aTransmitHook, otCoapBlockwiseReceiveHook aReceiveHook); @@ -276,10 +276,10 @@ otError otCoapSecureSendRequestBlockWise(otInstance * aInstance, * @retval OT_ERROR_INVALID_STATE DTLS connection was not initialized. * */ -otError otCoapSecureSendRequest(otInstance * aInstance, - otMessage * aMessage, +otError otCoapSecureSendRequest(otInstance *aInstance, + otMessage *aMessage, otCoapResponseHandler aHandler, - void * aContext); + void *aContext); /** * This function adds a resource to the CoAP Secure server. @@ -336,9 +336,9 @@ void otCoapSecureSetDefaultHandler(otInstance *aInstance, otCoapRequestHandler a * @param[in] aContext A pointer to arbitrary context information. May be NULL if not used. * */ -void otCoapSecureSetClientConnectedCallback(otInstance * aInstance, +void otCoapSecureSetClientConnectedCallback(otInstance *aInstance, otHandleCoapSecureClientConnect aHandler, - void * aContext); + void *aContext); /** * This function sends a CoAP response block-wise from the CoAP Secure server. @@ -356,10 +356,10 @@ void otCoapSecureSetClientConnectedCallback(otInstance * aIns * @retval OT_ERROR_NO_BUFS Insufficient buffers available to send the CoAP response. * */ -otError otCoapSecureSendResponseBlockWise(otInstance * aInstance, - otMessage * aMessage, - const otMessageInfo * aMessageInfo, - void * aContext, +otError otCoapSecureSendResponseBlockWise(otInstance *aInstance, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, + void *aContext, otCoapBlockwiseTransmitHook aTransmitHook); /** diff --git a/include/openthread/commissioner.h b/include/openthread/commissioner.h index d632fd0ffd0..9bf09b814b3 100644 --- a/include/openthread/commissioner.h +++ b/include/openthread/commissioner.h @@ -170,9 +170,9 @@ typedef void (*otCommissionerStateCallback)(otCommissionerState aState, void *aC * */ typedef void (*otCommissionerJoinerCallback)(otCommissionerJoinerEvent aEvent, - const otJoinerInfo * aJoinerInfo, - const otExtAddress * aJoinerId, - void * aContext); + const otJoinerInfo *aJoinerInfo, + const otExtAddress *aJoinerId, + void *aContext); /** * This function enables the Thread Commissioner role. @@ -187,10 +187,10 @@ typedef void (*otCommissionerJoinerCallback)(otCommissionerJoinerEvent aEvent, * @retval OT_ERROR_INVALID_STATE Device is not currently attached to a network. * */ -otError otCommissionerStart(otInstance * aInstance, +otError otCommissionerStart(otInstance *aInstance, otCommissionerStateCallback aStateCallback, otCommissionerJoinerCallback aJoinerCallback, - void * aCallbackContext); + void *aCallbackContext); /** * This function disables the Thread Commissioner role. @@ -242,9 +242,9 @@ otError otCommissionerSetId(otInstance *aInstance, const char *aId); * @note Only use this after successfully starting the Commissioner role with otCommissionerStart(). * */ -otError otCommissionerAddJoiner(otInstance * aInstance, +otError otCommissionerAddJoiner(otInstance *aInstance, const otExtAddress *aEui64, - const char * aPskd, + const char *aPskd, uint32_t aTimeout); /** @@ -263,9 +263,9 @@ otError otCommissionerAddJoiner(otInstance * aInstance, * @note Only use this after successfully starting the Commissioner role with otCommissionerStart(). * */ -otError otCommissionerAddJoinerWithDiscerner(otInstance * aInstance, +otError otCommissionerAddJoinerWithDiscerner(otInstance *aInstance, const otJoinerDiscerner *aDiscerner, - const char * aPskd, + const char *aPskd, uint32_t aTimeout); /** @@ -351,7 +351,7 @@ otError otCommissionerSetProvisioningUrl(otInstance *aInstance, const char *aPro * @note Only use this after successfully starting the Commissioner role with otCommissionerStart(). * */ -otError otCommissionerAnnounceBegin(otInstance * aInstance, +otError otCommissionerAnnounceBegin(otInstance *aInstance, uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod, @@ -369,7 +369,7 @@ otError otCommissionerAnnounceBegin(otInstance * aInstance, typedef void (*otCommissionerEnergyReportCallback)(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength, - void * aContext); + void *aContext); /** * This function sends an Energy Scan Query message. @@ -390,14 +390,14 @@ typedef void (*otCommissionerEnergyReportCallback)(uint32_t aChannelMask, * @note Only use this after successfully starting the Commissioner role with otCommissionerStart(). * */ -otError otCommissionerEnergyScan(otInstance * aInstance, +otError otCommissionerEnergyScan(otInstance *aInstance, uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod, uint16_t aScanDuration, - const otIp6Address * aAddress, + const otIp6Address *aAddress, otCommissionerEnergyReportCallback aCallback, - void * aContext); + void *aContext); /** * This function pointer is called when the Commissioner receives a PAN ID Conflict message. @@ -426,12 +426,12 @@ typedef void (*otCommissionerPanIdConflictCallback)(uint16_t aPanId, uint32_t aC * @note Only use this after successfully starting the Commissioner role with otCommissionerStart(). * */ -otError otCommissionerPanIdQuery(otInstance * aInstance, +otError otCommissionerPanIdQuery(otInstance *aInstance, uint16_t aPanId, uint32_t aChannelMask, - const otIp6Address * aAddress, + const otIp6Address *aAddress, otCommissionerPanIdConflictCallback aCallback, - void * aContext); + void *aContext); /** * This function sends MGMT_COMMISSIONER_GET. @@ -460,9 +460,9 @@ otError otCommissionerSendMgmtGet(otInstance *aInstance, const uint8_t *aTlvs, u * @retval OT_ERROR_INVALID_STATE The commissioner is not active. * */ -otError otCommissionerSendMgmtSet(otInstance * aInstance, +otError otCommissionerSendMgmtSet(otInstance *aInstance, const otCommissioningDataset *aDataset, - const uint8_t * aTlvs, + const uint8_t *aTlvs, uint8_t aLength); /** diff --git a/include/openthread/crypto.h b/include/openthread/crypto.h index 3be657bca6a..b2629267e93 100644 --- a/include/openthread/crypto.h +++ b/include/openthread/crypto.h @@ -55,25 +55,13 @@ extern "C" { * */ -#define OT_CRYPTO_SHA256_HASH_SIZE 32 ///< Length of SHA256 hash (in bytes). - /** * @struct otCryptoSha256Hash * * This structure represents a SHA-256 hash. * */ -OT_TOOL_PACKED_BEGIN -struct otCryptoSha256Hash -{ - uint8_t m8[OT_CRYPTO_SHA256_HASH_SIZE]; ///< Hash bytes. -} OT_TOOL_PACKED_END; - -/** - * This structure represents a SHA-256 hash. - * - */ -typedef struct otCryptoSha256Hash otCryptoSha256Hash; +typedef otPlatCryptoSha256Hash otCryptoSha256Hash; /** * This function performs HMAC computation. @@ -107,37 +95,15 @@ void otCryptoHmacSha256(const otCryptoKey *aKey, const uint8_t *aBuf, uint16_t a */ void otCryptoAesCcm(const otCryptoKey *aKey, uint8_t aTagLength, - const void * aNonce, + const void *aNonce, uint8_t aNonceLength, - const void * aHeader, + const void *aHeader, uint32_t aHeaderLength, - void * aPlainText, - void * aCipherText, + void *aPlainText, + void *aCipherText, uint32_t aLength, bool aEncrypt, - void * aTag); - -/** - * This method creates ECDSA sign. - * - * @param[out] aOutput An output buffer where ECDSA sign should be stored. - * @param[in,out] aOutputLength The length of the @p aOutput buffer. - * @param[in] aInputHash An input hash. - * @param[in] aInputHashLength The length of the @p aInputHash buffer. - * @param[in] aPrivateKey A private key in PEM format. - * @param[in] aPrivateKeyLength The length of the @p aPrivateKey buffer. - * - * @retval OT_ERROR_NONE ECDSA sign has been created successfully. - * @retval OT_ERROR_NO_BUFS Output buffer is too small. - * @retval OT_ERROR_INVALID_ARGS Private key is not valid EC Private Key. - * @retval OT_ERROR_FAILED Error during signing. - */ -otError otCryptoEcdsaSign(uint8_t * aOutput, - uint16_t * aOutputLength, - const uint8_t *aInputHash, - uint16_t aInputHashLength, - const uint8_t *aPrivateKey, - uint16_t aPrivateKeyLength); + void *aTag); /** * @} diff --git a/include/openthread/dataset.h b/include/openthread/dataset.h index 63e1e26adc7..efd18bc4b66 100644 --- a/include/openthread/dataset.h +++ b/include/openthread/dataset.h @@ -49,6 +49,8 @@ extern "C" { * * @{ * + * For FTD and MTD builds, the Operational Dataset API includes functions to manage Active and Pending datasets + * and dataset TLVs. */ #define OT_NETWORK_KEY_SIZE 16 ///< Size of the Thread Network Key (bytes) @@ -227,7 +229,7 @@ typedef struct otTimestamp /** * This structure represents an Active or Pending Operational Dataset. * - * Components in Dataset are optional. `mComponets` structure specifies which components are present in the Dataset. + * Components in Dataset are optional. `mComponents` structure specifies which components are present in the Dataset. * */ typedef struct otOperationalDataset @@ -339,7 +341,7 @@ typedef void (*otDatasetMgmtSetCallback)(otError aResult, void *aContext); bool otDatasetIsCommissioned(otInstance *aInstance); /** - * This function gets the Active Operational Dataset. + * Gets the Active Operational Dataset. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[out] aDataset A pointer to where the Active Operational Dataset will be placed. @@ -363,7 +365,7 @@ otError otDatasetGetActive(otInstance *aInstance, otOperationalDataset *aDataset otError otDatasetGetActiveTlvs(otInstance *aInstance, otOperationalDatasetTlvs *aDataset); /** - * This function sets the Active Operational Dataset. + * Sets the Active Operational Dataset. * * If the dataset does not include an Active Timestamp, the dataset is only partially complete. * @@ -439,7 +441,7 @@ otError otDatasetGetPending(otInstance *aInstance, otOperationalDataset *aDatase otError otDatasetGetPendingTlvs(otInstance *aInstance, otOperationalDatasetTlvs *aDataset); /** - * This function sets the Pending Operational Dataset. + * Sets the Pending Operational Dataset. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aDataset A pointer to the Pending Operational Dataset. @@ -465,7 +467,7 @@ otError otDatasetSetPending(otInstance *aInstance, const otOperationalDataset *a otError otDatasetSetPendingTlvs(otInstance *aInstance, const otOperationalDatasetTlvs *aDataset); /** - * This function sends MGMT_ACTIVE_GET. + * Sends MGMT_ACTIVE_GET. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aDatasetComponents A pointer to a Dataset Components structure specifying which components to request. @@ -477,14 +479,14 @@ otError otDatasetSetPendingTlvs(otInstance *aInstance, const otOperationalDatase * @retval OT_ERROR_NO_BUFS Insufficient buffer space to send. * */ -otError otDatasetSendMgmtActiveGet(otInstance * aInstance, +otError otDatasetSendMgmtActiveGet(otInstance *aInstance, const otOperationalDatasetComponents *aDatasetComponents, - const uint8_t * aTlvTypes, + const uint8_t *aTlvTypes, uint8_t aLength, - const otIp6Address * aAddress); + const otIp6Address *aAddress); /** - * This function sends MGMT_ACTIVE_SET. + * Sends MGMT_ACTIVE_SET. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aDataset A pointer to operational dataset. @@ -498,15 +500,15 @@ otError otDatasetSendMgmtActiveGet(otInstance * aInstan * @retval OT_ERROR_BUSY A previous request is ongoing. * */ -otError otDatasetSendMgmtActiveSet(otInstance * aInstance, +otError otDatasetSendMgmtActiveSet(otInstance *aInstance, const otOperationalDataset *aDataset, - const uint8_t * aTlvs, + const uint8_t *aTlvs, uint8_t aLength, otDatasetMgmtSetCallback aCallback, - void * aContext); + void *aContext); /** - * This function sends MGMT_PENDING_GET. + * Sends MGMT_PENDING_GET. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aDatasetComponents A pointer to a Dataset Components structure specifying which components to request. @@ -518,14 +520,14 @@ otError otDatasetSendMgmtActiveSet(otInstance * aInstance, * @retval OT_ERROR_NO_BUFS Insufficient buffer space to send. * */ -otError otDatasetSendMgmtPendingGet(otInstance * aInstance, +otError otDatasetSendMgmtPendingGet(otInstance *aInstance, const otOperationalDatasetComponents *aDatasetComponents, - const uint8_t * aTlvTypes, + const uint8_t *aTlvTypes, uint8_t aLength, - const otIp6Address * aAddress); + const otIp6Address *aAddress); /** - * This function sends MGMT_PENDING_SET. + * Sends MGMT_PENDING_SET. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aDataset A pointer to operational dataset. @@ -539,12 +541,12 @@ otError otDatasetSendMgmtPendingGet(otInstance * aInsta * @retval OT_ERROR_BUSY A previous request is ongoing. * */ -otError otDatasetSendMgmtPendingSet(otInstance * aInstance, +otError otDatasetSendMgmtPendingSet(otInstance *aInstance, const otOperationalDataset *aDataset, - const uint8_t * aTlvs, + const uint8_t *aTlvs, uint8_t aLength, otDatasetMgmtSetCallback aCallback, - void * aContext); + void *aContext); /** * This function generates PSKc from a given pass-phrase, network name, and extended PAN ID. @@ -560,15 +562,15 @@ otError otDatasetSendMgmtPendingSet(otInstance * aInstance, * @retval OT_ERROR_INVALID_ARGS If any of the input arguments is invalid. * */ -otError otDatasetGeneratePskc(const char * aPassPhrase, - const otNetworkName * aNetworkName, +otError otDatasetGeneratePskc(const char *aPassPhrase, + const otNetworkName *aNetworkName, const otExtendedPanId *aExtPanId, - otPskc * aPskc); + otPskc *aPskc); /** - * This function sets an `otNetworkName` instance from a given null terminated C string. + * Sets an `otNetworkName` instance from a given null terminated C string. * - * This function also validates that the given @p aNameString follows UTF-8 encoding and its length is not longer than + * @p aNameString must follow UTF-8 encoding and the Network Name length must not be longer than * `OT_NETWORK_NAME_MAX_SIZE`. * * @param[out] aNetworkName A pointer to the `otNetworkName` to set. @@ -581,7 +583,7 @@ otError otDatasetGeneratePskc(const char * aPassPhrase, otError otNetworkNameFromString(otNetworkName *aNetworkName, const char *aNameString); /** - * This function parses an Operational Dataset from a `otOperationalDatasetTlvs`. + * Parses an Operational Dataset from a given `otOperationalDatasetTlvs`. * * @param[in] aDatasetTlvs A pointer to dataset TLVs. * @param[out] aDataset A pointer to where the dataset will be placed. @@ -592,6 +594,33 @@ otError otNetworkNameFromString(otNetworkName *aNetworkName, const char *aNameSt */ otError otDatasetParseTlvs(const otOperationalDatasetTlvs *aDatasetTlvs, otOperationalDataset *aDataset); +/** + * Converts a given Operational Dataset to `otOperationalDatasetTlvs`. + * + * @param[in] aDataset An Operational dataset to convert to TLVs. + * @param[out] aDatasetTlvs A pointer to dataset TLVs to return the result. + * + * @retval OT_ERROR_NONE Successfully converted @p aDataset and updated @p aDatasetTlvs. + * @retval OT_ERROR_INVALID_ARGS @p aDataset is invalid, does not contain active or pending timestamps. + * + */ +otError otDatasetConvertToTlvs(const otOperationalDataset *aDataset, otOperationalDatasetTlvs *aDatasetTlvs); + +/** + * Updates a given Operational Dataset. + * + * @p aDataset contains the fields to be updated and their new value. + * + * @param[in] aDataset Specifies the set of types and values to update. + * @param[in,out] aDatasetTlvs A pointer to dataset TLVs to update. + * + * @retval OT_ERROR_NONE Successfully updated @p aDatasetTlvs. + * @retval OT_ERROR_INVALID_ARGS @p aDataset contains invalid values. + * @retval OT_ERROR_NO_BUFS Not enough space space in @p aDatasetTlvs to apply the update. + * + */ +otError otDatasetUpdateTlvs(const otOperationalDataset *aDataset, otOperationalDatasetTlvs *aDatasetTlvs); + /** * @} * diff --git a/include/openthread/dataset_ftd.h b/include/openthread/dataset_ftd.h index f1ae621ef6b..05e37a1b2bf 100644 --- a/include/openthread/dataset_ftd.h +++ b/include/openthread/dataset_ftd.h @@ -50,7 +50,7 @@ extern "C" { */ /** - * This method creates a new Operational Dataset to use when forming a new network. + * For FTD only, creates a new Operational Dataset to use when forming a new network. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[out] aDataset The Operational Dataset. @@ -62,7 +62,7 @@ extern "C" { otError otDatasetCreateNewNetwork(otInstance *aInstance, otOperationalDataset *aDataset); /** - * Get minimal delay timer. + * For FTD only, gets a minimal delay timer. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -72,7 +72,7 @@ otError otDatasetCreateNewNetwork(otInstance *aInstance, otOperationalDataset *a uint32_t otDatasetGetDelayTimerMinimal(otInstance *aInstance); /** - * Set minimal delay timer. + * For FTD only, sets a minimal delay timer. * * @note This API is reserved for testing and demo purposes only. Changing settings with * this API will render a production application non-compliant with the Thread Specification. diff --git a/include/openthread/dataset_updater.h b/include/openthread/dataset_updater.h index 627ddb390b8..cb16c33ac3c 100644 --- a/include/openthread/dataset_updater.h +++ b/include/openthread/dataset_updater.h @@ -45,20 +45,18 @@ extern "C" { /** * @addtogroup api-operational-dataset * - * @brief - * This module includes functions for Dataset Updater. - * - * The functions in this module are available when Dataset Updater feature is enabled (i.e. - * `OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE` is set to 1). Further this feature is available only on an FTD build. - * * @{ * + * For FTD builds only, Dataset Updater includes functions to manage dataset updates. + * */ /** * This callback function pointer is called when a Dataset update request finishes, reporting success or failure status * of the Dataset update request. * + * Available when `OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE` is enabled. + * * @param[in] aError The error status. * OT_ERROR_NONE indicates successful Dataset update. * OT_ERROR_INVALID_STATE indicates failure due invalid state (MLE being disabled). @@ -73,6 +71,8 @@ typedef void (*otDatasetUpdaterCallback)(otError aError, void *aContext); /** * This function requests an update to Operational Dataset. * + * Available when `OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE` is enabled. + * * @p aDataset should contain the fields to be updated and their new value. It must not contain Active or Pending * Timestamp fields. The Delay field is optional, if not provided a default value (1000 ms) would be used. * @@ -88,14 +88,16 @@ typedef void (*otDatasetUpdaterCallback)(otError aError, void *aContext); * @retval OT_ERROR_NO_BUFS Could not allocated buffer to save Dataset. * */ -otError otDatasetUpdaterRequestUpdate(otInstance * aInstance, +otError otDatasetUpdaterRequestUpdate(otInstance *aInstance, const otOperationalDataset *aDataset, otDatasetUpdaterCallback aCallback, - void * aContext); + void *aContext); /** * This function cancels an ongoing (if any) Operational Dataset update request. * + * Available when `OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE` is enabled. + * * @param[in] aInstance A pointer to an OpenThread instance. * */ @@ -104,6 +106,8 @@ void otDatasetUpdaterCancelUpdate(otInstance *aInstance); /** * This function indicates whether there is an ongoing Operation Dataset update request. * + * Available when `OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE` is enabled. + * * @param[in] aInstance A pointer to an OpenThread instance. * * @retval TRUE There is an ongoing update. diff --git a/include/openthread/diag.h b/include/openthread/diag.h index 96571f9c037..98c708e3425 100644 --- a/include/openthread/diag.h +++ b/include/openthread/diag.h @@ -70,8 +70,8 @@ extern "C" { */ otError otDiagProcessCmd(otInstance *aInstance, uint8_t aArgsLength, - char * aArgs[], - char * aOutput, + char *aArgs[], + char *aOutput, size_t aOutputMaxLen); /** @@ -85,8 +85,13 @@ otError otDiagProcessCmd(otInstance *aInstance, * @param[out] aOutput The diagnostics execution result. * @param[in] aOutputMaxLen The output buffer size. * + * @retval OT_ERROR_NONE The command is successfully process. + * @retval OT_ERROR_INVALID_ARGS The command is supported but invalid arguments provided. + * @retval OT_ERROR_NOT_IMPLEMENTED The command is not supported. + * @retval OT_ERROR_NO_BUFS The command string is too long. + * */ -void otDiagProcessCmdLine(otInstance *aInstance, const char *aString, char *aOutput, size_t aOutputMaxLen); +otError otDiagProcessCmdLine(otInstance *aInstance, const char *aString, char *aOutput, size_t aOutputMaxLen); /** * This function indicates whether or not the factory diagnostics mode is enabled. diff --git a/include/openthread/dns.h b/include/openthread/dns.h index 2a5dd13734d..22f3fc2b74f 100644 --- a/include/openthread/dns.h +++ b/include/openthread/dns.h @@ -89,7 +89,7 @@ typedef struct otDnsTxtEntry * DNS message. * */ - const char * mKey; + const char *mKey; const uint8_t *mValue; ///< The TXT record value or already encoded TXT-DATA (depending on `mKey`). uint16_t mValueLength; ///< Number of bytes in `mValue` buffer. } otDnsTxtEntry; diff --git a/include/openthread/dns_client.h b/include/openthread/dns_client.h index 17817c1472d..c790bc18c00 100644 --- a/include/openthread/dns_client.h +++ b/include/openthread/dns_client.h @@ -80,6 +80,36 @@ typedef enum OT_DNS_NAT64_DISALLOW = 2, ///< Do not allow NAT64 address translation during DNS client address resolution. } otDnsNat64Mode; +/** + * This enumeration type represents the service resolution mode in an `otDnsQueryConfig`. + * + * This is only used during DNS client service resolution `otDnsClientResolveService()`. It determines which + * record types to query. + * + */ +typedef enum +{ + OT_DNS_SERVICE_MODE_UNSPECIFIED = 0, ///< Mode is not specified. Use default service mode. + OT_DNS_SERVICE_MODE_SRV = 1, ///< Query for SRV record only. + OT_DNS_SERVICE_MODE_TXT = 2, ///< Query for TXT record only. + OT_DNS_SERVICE_MODE_SRV_TXT = 3, ///< Query for both SRV and TXT records in same message. + OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE = 4, ///< Query in parallel for SRV and TXT using separate messages. + OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE = 5, ///< Query for TXT/SRV together first, if fails then query separately. +} otDnsServiceMode; + +/** + * This enumeration type represents the DNS transport protocol in an `otDnsQueryConfig`. + * + * This `OT_DNS_TRANSPORT_TCP` is only supported when `OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE` is enabled. + * + */ +typedef enum +{ + OT_DNS_TRANSPORT_UNSPECIFIED = 0, /// DNS transport is unspecified. + OT_DNS_TRANSPORT_UDP = 1, /// DNS query should be sent via UDP. + OT_DNS_TRANSPORT_TCP = 2, /// DNS query should be sent via TCP. +} otDnsTransportProto; + /** * This structure represents a DNS query configuration. * @@ -89,11 +119,13 @@ typedef enum */ typedef struct otDnsQueryConfig { - otSockAddr mServerSockAddr; ///< Server address (IPv6 address/port). All zero or zero port for unspecified. - uint32_t mResponseTimeout; ///< Wait time (in msec) to rx response. Zero indicates unspecified value. - uint8_t mMaxTxAttempts; ///< Maximum tx attempts before reporting failure. Zero for unspecified value. - otDnsRecursionFlag mRecursionFlag; ///< Indicates whether the server can resolve the query recursively or not. - otDnsNat64Mode mNat64Mode; ///< Allow/Disallow NAT64 address translation during address resolution. + otSockAddr mServerSockAddr; ///< Server address (IPv6 addr/port). All zero or zero port for unspecified. + uint32_t mResponseTimeout; ///< Wait time (in msec) to rx response. Zero indicates unspecified value. + uint8_t mMaxTxAttempts; ///< Maximum tx attempts before reporting failure. Zero for unspecified value. + otDnsRecursionFlag mRecursionFlag; ///< Indicates whether the server can resolve the query recursively or not. + otDnsNat64Mode mNat64Mode; ///< Allow/Disallow NAT64 address translation during address resolution. + otDnsServiceMode mServiceMode; ///< Determines which records to query during service resolution. + otDnsTransportProto mTransportProto; ///< Select default transport protocol. } otDnsQueryConfig; /** @@ -203,10 +235,10 @@ typedef void (*otDnsAddressCallback)(otError aError, const otDnsAddressResponse * @retval OT_ERROR_INVALID_STATE Cannot send query since Thread interface is not up. * */ -otError otDnsClientResolveAddress(otInstance * aInstance, - const char * aHostName, +otError otDnsClientResolveAddress(otInstance *aInstance, + const char *aHostName, otDnsAddressCallback aCallback, - void * aContext, + void *aContext, const otDnsQueryConfig *aConfig); /** @@ -233,10 +265,10 @@ otError otDnsClientResolveAddress(otInstance * aInstance, * @retval OT_ERROR_INVALID_STATE Cannot send query since Thread interface is not up. * */ -otError otDnsClientResolveIp4Address(otInstance * aInstance, - const char * aHostName, +otError otDnsClientResolveIp4Address(otInstance *aInstance, + const char *aHostName, otDnsAddressCallback aCallback, - void * aContext, + void *aContext, const otDnsQueryConfig *aConfig); /** @@ -253,7 +285,7 @@ otError otDnsClientResolveIp4Address(otInstance * aInstance, * */ otError otDnsAddressResponseGetHostName(const otDnsAddressResponse *aResponse, - char * aNameBuffer, + char *aNameBuffer, uint16_t aNameBufferSize); /** @@ -279,8 +311,8 @@ otError otDnsAddressResponseGetHostName(const otDnsAddressResponse *aResponse, */ otError otDnsAddressResponseGetAddress(const otDnsAddressResponse *aResponse, uint16_t aIndex, - otIp6Address * aAddress, - uint32_t * aTtl); + otIp6Address *aAddress, + uint32_t *aTtl); /** * This type is an opaque representation of a response to a browse (service instance enumeration) DNS query. @@ -318,12 +350,13 @@ typedef struct otDnsServiceInfo uint16_t mPort; ///< Service port number. uint16_t mPriority; ///< Service priority. uint16_t mWeight; ///< Service weight. - char * mHostNameBuffer; ///< Buffer to output the service host name (can be NULL if not needed). + char *mHostNameBuffer; ///< Buffer to output the service host name (can be NULL if not needed). uint16_t mHostNameBufferSize; ///< Size of `mHostNameBuffer`. otIp6Address mHostAddress; ///< The host IPv6 address. Set to all zero if not available. uint32_t mHostAddressTtl; ///< The host address TTL. - uint8_t * mTxtData; ///< Buffer to output TXT data (can be NULL if not needed). - uint16_t mTxtDataSize; ///< On input, size of `mTxtData` buffer. On output `mTxtData` length. + uint8_t *mTxtData; ///< Buffer to output TXT data (can be NULL if not needed). + uint16_t mTxtDataSize; ///< On input, size of `mTxtData` buffer. On output number bytes written. + bool mTxtDataTruncated; ///< Indicates if TXT data could not fit in `mTxtDataSize` and was truncated. uint32_t mTxtDataTtl; ///< The TXT data TTL. } otDnsServiceInfo; @@ -346,10 +379,10 @@ typedef struct otDnsServiceInfo * @retval OT_ERROR_NO_BUFS Insufficient buffer to prepare and send query. * */ -otError otDnsClientBrowse(otInstance * aInstance, - const char * aServiceName, +otError otDnsClientBrowse(otInstance *aInstance, + const char *aServiceName, otDnsBrowseCallback aCallback, - void * aContext, + void *aContext, const otDnsQueryConfig *aConfig); /** @@ -366,7 +399,7 @@ otError otDnsClientBrowse(otInstance * aInstance, * */ otError otDnsBrowseResponseGetServiceName(const otDnsBrowseResponse *aResponse, - char * aNameBuffer, + char *aNameBuffer, uint16_t aNameBufferSize); /** @@ -393,7 +426,7 @@ otError otDnsBrowseResponseGetServiceName(const otDnsBrowseResponse *aResponse, */ otError otDnsBrowseResponseGetServiceInstance(const otDnsBrowseResponse *aResponse, uint16_t aIndex, - char * aLabelBuffer, + char *aLabelBuffer, uint8_t aLabelBufferSize); /** @@ -401,13 +434,15 @@ otError otDnsBrowseResponseGetServiceInstance(const otDnsBrowseResponse *aRespon * * This function MUST only be used from `otDnsBrowseCallback`. * - * A browse DNS response should include the SRV, TXT, and AAAA records for the service instances that are enumerated - * (note that it is a SHOULD and not a MUST requirement). This function tries to retrieve this info for a given service - * instance when available. + * A browse DNS response can include SRV, TXT, and AAAA records for the service instances that are enumerated. This is + * a SHOULD and not a MUST requirement, and servers/resolvers are not required to provide this. This function attempts + * to retrieve this info for a given service instance when available. * - * - If no matching SRV record is found in @p aResponse, `OT_ERROR_NOT_FOUND` is returned. + * - If no matching SRV record is found in @p aResponse, `OT_ERROR_NOT_FOUND` is returned. In this case, no additional + * records (no TXT and/or AAAA) are read. * - If a matching SRV record is found in @p aResponse, @p aServiceInfo is updated and `OT_ERROR_NONE` is returned. * - If no matching TXT record is found in @p aResponse, `mTxtDataSize` in @p aServiceInfo is set to zero. + * - If TXT data length is greater than `mTxtDataSize`, it is read partially and `mTxtDataTruncated` is set to true. * - If no matching AAAA record is found in @p aResponse, `mHostAddress is set to all zero or unspecified address. * - If there are multiple AAAA records for the host name in @p aResponse, `mHostAddress` is set to the first one. The * other addresses can be retrieved using `otDnsBrowseResponseGetHostAddress()`. @@ -423,8 +458,8 @@ otError otDnsBrowseResponseGetServiceInstance(const otDnsBrowseResponse *aRespon * */ otError otDnsBrowseResponseGetServiceInfo(const otDnsBrowseResponse *aResponse, - const char * aInstanceLabel, - otDnsServiceInfo * aServiceInfo); + const char *aInstanceLabel, + otDnsServiceInfo *aServiceInfo); /** * This function gets the host IPv6 address from a DNS browse (service instance enumeration) response. @@ -448,10 +483,10 @@ otError otDnsBrowseResponseGetServiceInfo(const otDnsBrowseResponse *aResponse, * */ otError otDnsBrowseResponseGetHostAddress(const otDnsBrowseResponse *aResponse, - const char * aHostName, + const char *aHostName, uint16_t aIndex, - otIp6Address * aAddress, - uint32_t * aTtl); + otIp6Address *aAddress, + uint32_t *aTtl); /** * This type is an opaque representation of a response to a service instance resolution DNS query. @@ -488,6 +523,18 @@ typedef void (*otDnsServiceCallback)(otError aError, const otDnsServiceResponse * the config for this query. In a non-NULL @p aConfig, some of the fields can be left unspecified (value zero). The * unspecified fields are then replaced by the values from the default config. * + * The function sends queries for SRV and/or TXT records for the given service instance. The `mServiceMode` field in + * `otDnsQueryConfig` determines which records to query (SRV only, TXT only, or both SRV and TXT) and how to perform + * the query (together in the same message, separately in parallel, or in optimized mode where client will try in the + * same message first and then separately if it fails to get a response). + * + * The SRV record provides information about service port, priority, and weight along with the host name associated + * with the service instance. This function DOES NOT perform address resolution for the host name discovered from SRV + * record. The server/resolver may provide AAAA/A record(s) for the host name in the Additional Data section of the + * response to SRV/TXT query and this information can be retrieved using `otDnsServiceResponseGetServiceInfo()` in + * `otDnsServiceCallback`. Users of this API MUST NOT assume that host address will always be available from + * `otDnsServiceResponseGetServiceInfo()`. + * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aInstanceLabel The service instance label. * @param[in] aServiceName The service name (together with @p aInstanceLabel form full instance name). @@ -500,11 +547,11 @@ typedef void (*otDnsServiceCallback)(otError aError, const otDnsServiceResponse * @retval OT_ERROR_INVALID_ARGS @p aInstanceLabel is NULL. * */ -otError otDnsClientResolveService(otInstance * aInstance, - const char * aInstanceLabel, - const char * aServiceName, +otError otDnsClientResolveService(otInstance *aInstance, + const char *aInstanceLabel, + const char *aServiceName, otDnsServiceCallback aCallback, - void * aContext, + void *aContext, const otDnsQueryConfig *aConfig); /** @@ -524,9 +571,9 @@ otError otDnsClientResolveService(otInstance * aInstance, * */ otError otDnsServiceResponseGetServiceName(const otDnsServiceResponse *aResponse, - char * aLabelBuffer, + char *aLabelBuffer, uint8_t aLabelBufferSize, - char * aNameBuffer, + char *aNameBuffer, uint16_t aNameBufferSize); /** @@ -534,9 +581,18 @@ otError otDnsServiceResponseGetServiceName(const otDnsServiceResponse *aResponse * * This function MUST only be used from `otDnsServiceCallback`. * - * - If no matching SRV record is found in @p aResponse, `OT_ERROR_NOT_FOUND` is returned. - * - If a matching SRV record is found in @p aResponse, @p aServiceInfo is updated and `OT_ERROR_NONE` is returned. + * A service resolution DNS response may include AAAA records in its Additional Data section for host name associated + * with the service instance that is resolved. This is a SHOULD and not a MUST requirement so servers/resolvers are + * not required to provide this. This function attempts to retrieve AAAA record(s) if included in the response. If it + * is not included `mHostAddress` is set to all zero (unspecified address). If the caller wants to resolve the host + * address it can call `otDnsClientResolveAddress()` with the host name to start an address resolution query. + * + * - If a matching SRV record is found in @p aResponse, @p aServiceInfo is updated. + * - If no matching SRV record is found, `OT_ERROR_NOT_FOUND` is returned unless the query config for this query + * used `OT_DNS_SERVICE_MODE_TXT` for `mServiceMode` (meaning the request was only for TXT record). In this case, we + * still try to parse the SRV record from Additional Data Section of response (in case server provided the info). * - If no matching TXT record is found in @p aResponse, `mTxtDataSize` in @p aServiceInfo is set to zero. + * - If TXT data length is greater than `mTxtDataSize`, it is read partially and `mTxtDataTruncated` is set to true. * - If no matching AAAA record is found in @p aResponse, `mHostAddress is set to all zero or unspecified address. * - If there are multiple AAAA records for the host name in @p aResponse, `mHostAddress` is set to the first one. The * other addresses can be retrieved using `otDnsServiceResponseGetHostAddress()`. @@ -545,7 +601,7 @@ otError otDnsServiceResponseGetServiceName(const otDnsServiceResponse *aResponse * @param[out] aServiceInfo A `ServiceInfo` to output the service instance information (MUST NOT be NULL). * * @retval OT_ERROR_NONE The service instance info was read. @p aServiceInfo is updated. - * @retval OT_ERROR_NOT_FOUND Could not find a matching SRV record in @p aResponse. + * @retval OT_ERROR_NOT_FOUND Could not find a required record in @p aResponse. * @retval OT_ERROR_NO_BUFS The host name and/or TXT data could not fit in the given buffers. * @retval OT_ERROR_PARSE Could not parse the records in the @p aResponse. * @@ -574,10 +630,10 @@ otError otDnsServiceResponseGetServiceInfo(const otDnsServiceResponse *aResponse * */ otError otDnsServiceResponseGetHostAddress(const otDnsServiceResponse *aResponse, - const char * aHostName, + const char *aHostName, uint16_t aIndex, - otIp6Address * aAddress, - uint32_t * aTtl); + otIp6Address *aAddress, + uint32_t *aTtl); /** * @} diff --git a/include/openthread/dnssd_server.h b/include/openthread/dnssd_server.h index 73666981ac4..cd6463c2355 100644 --- a/include/openthread/dnssd_server.h +++ b/include/openthread/dnssd_server.h @@ -112,15 +112,15 @@ typedef void otDnssdQuery; */ typedef struct otDnssdServiceInstanceInfo { - const char * mFullName; ///< Full instance name (e.g. "OpenThread._ipps._tcp.default.service.arpa."). - const char * mHostName; ///< Host name (e.g. "ot-host.default.service.arpa."). + const char *mFullName; ///< Full instance name (e.g. "OpenThread._ipps._tcp.default.service.arpa."). + const char *mHostName; ///< Host name (e.g. "ot-host.default.service.arpa."). uint8_t mAddressNum; ///< Number of host IPv6 addresses. const otIp6Address *mAddresses; ///< Host IPv6 addresses. uint16_t mPort; ///< Service port. uint16_t mPriority; ///< Service priority. uint16_t mWeight; ///< Service weight. uint16_t mTxtLength; ///< Service TXT RDATA length. - const uint8_t * mTxtData; ///< Service TXT RDATA. + const uint8_t *mTxtData; ///< Service TXT RDATA. uint32_t mTtl; ///< Service TTL (in seconds). } otDnssdServiceInstanceInfo; @@ -177,10 +177,10 @@ typedef struct otDnssdCounters * @param[in] aContext A pointer to the application-specific context. * */ -void otDnssdQuerySetCallbacks(otInstance * aInstance, +void otDnssdQuerySetCallbacks(otInstance *aInstance, otDnssdQuerySubscribeCallback aSubscribe, otDnssdQueryUnsubscribeCallback aUnsubscribe, - void * aContext); + void *aContext); /** * This function notifies a discovered service instance. @@ -195,8 +195,8 @@ void otDnssdQuerySetCallbacks(otInstance * aInstance, * @param[in] aInstanceInfo A pointer to the discovered service instance information. * */ -void otDnssdQueryHandleDiscoveredServiceInstance(otInstance * aInstance, - const char * aServiceFullName, +void otDnssdQueryHandleDiscoveredServiceInstance(otInstance *aInstance, + const char *aServiceFullName, otDnssdServiceInstanceInfo *aInstanceInfo); /** * This function notifies a discovered host. @@ -245,6 +245,35 @@ otDnssdQueryType otDnssdGetQueryTypeAndName(const otDnssdQuery *aQuery, char (*a */ const otDnssdCounters *otDnssdGetCounters(otInstance *aInstance); +/** + * Enable or disable forwarding DNS queries to platform DNS upstream API. + * + * Available when `OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE` is enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aEnabled A boolean to enable/disable forwarding DNS queries to upstream. + * + * @sa otPlatDnsStartUpstreamQuery + * @sa otPlatDnsCancelUpstreamQuery + * @sa otPlatDnsUpstreamQueryDone + * + */ +void otDnssdUpstreamQuerySetEnabled(otInstance *aInstance, bool aEnabled); + +/** + * Returns whether the DNSSD server will forward DNS queries to the platform DNS upstream API. + * + * Available when `OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE` is enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @retval TRUE If the DNSSD server will forward DNS queries. + * @retval FALSE If the DNSSD server will not forward DNS queries. + * + * @sa otDnssdUpstreamQuerySetEnabled + * + */ +bool otDnssdUpstreamQueryIsEnabled(otInstance *aInstance); + /** * @} * diff --git a/include/openthread/history_tracker.h b/include/openthread/history_tracker.h index a8dc7c30150..79a255a77ac 100644 --- a/include/openthread/history_tracker.h +++ b/include/openthread/history_tracker.h @@ -43,7 +43,7 @@ extern "C" { * Records the history of different events, for example RX and TX messages or network info changes. All tracked * entries are timestamped. * - * The functions in this module are available when `OPENTHREAD_CONFIG_HISTOR_TRACKER_ENABLE` is enabled. + * The functions in this module are available when `OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE` is enabled. * * @{ * @@ -152,7 +152,7 @@ typedef struct otHistoryTrackerMessageInfo uint16_t mChecksum; ///< Message checksum (valid only for UDP/TCP/ICMP6). uint8_t mIpProto; ///< IP Protocol number (`OT_IP6_PROTO_*` enumeration). uint8_t mIcmp6Type; ///< ICMP6 type if msg is ICMP6, zero otherwise (`OT_ICMP6_TYPE_*` enumeration). - int8_t mAveRxRss; ///< RSS of received message or OT_RADIO_INVALI_RSSI if not known. + int8_t mAveRxRss; ///< RSS of received message or OT_RADIO_INVALID_RSSI if not known. bool mLinkSecurity : 1; ///< Indicates whether msg used link security. bool mTxSuccess : 1; ///< Indicates TX success (e.g., ack received). Applicable for TX msg only. uint8_t mPriority : 2; ///< Message priority (`OT_HISTORY_TRACKER_MSG_PRIORITY_*` enumeration). @@ -192,6 +192,35 @@ typedef struct otHistoryTrackerNeighborInfo bool mIsChild : 1; ///< Indicates whether or not the neighbor is a child. } otHistoryTrackerNeighborInfo; +/** + * This enumeration defines the events in a router info (i.e. whether router is added, removed, or changed). + * + */ +typedef enum +{ + OT_HISTORY_TRACKER_ROUTER_EVENT_ADDED = 0, ///< Router is added (router ID allocated). + OT_HISTORY_TRACKER_ROUTER_EVENT_REMOVED = 1, ///< Router entry is removed (router ID released). + OT_HISTORY_TRACKER_ROUTER_EVENT_NEXT_HOP_CHANGED = 2, ///< Router entry next hop and cost changed. + OT_HISTORY_TRACKER_ROUTER_EVENT_COST_CHANGED = 3, ///< Router entry path cost changed (next hop as before). +} otHistoryTrackerRouterEvent; + +#define OT_HISTORY_TRACKER_NO_NEXT_HOP 63 ///< No next hop - For `mNextHop` in `otHistoryTrackerRouterInfo`. + +#define OT_HISTORY_TRACKER_INFINITE_PATH_COST 0 ///< Infinite path cost - used in `otHistoryTrackerRouterInfo`. + +/** + * This structure represents a router table entry event. + * + */ +typedef struct otHistoryTrackerRouterInfo +{ + uint8_t mEvent : 2; ///< Router entry event (`OT_HISTORY_TRACKER_ROUTER_EVENT_*` enumeration). + uint8_t mRouterId : 6; ///< Router ID. + uint8_t mNextHop; ///< Next Hop Router ID - `OT_HISTORY_TRACKER_NO_NEXT_HOP` if no next hop. + uint8_t mOldPathCost : 4; ///< Old path cost - `OT_HISTORY_TRACKER_INFINITE_PATH_COST` if infinite or unknown. + uint8_t mPathCost : 4; ///< New path cost - `OT_HISTORY_TRACKER_INFINITE_PATH_COST` if infinite or unknown. +} otHistoryTrackerRouterInfo; + /** * This enumeration defines the events for a Network Data entry (i.e., whether an entry is added or removed). * @@ -251,9 +280,9 @@ void otHistoryTrackerInitIterator(otHistoryTrackerIterator *aIterator); * @returns A pointer to `otHistoryTrackerNetworkInfo` entry or `NULL` if no more entries in the list. * */ -const otHistoryTrackerNetworkInfo *otHistoryTrackerIterateNetInfoHistory(otInstance * aInstance, +const otHistoryTrackerNetworkInfo *otHistoryTrackerIterateNetInfoHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge); + uint32_t *aEntryAge); /** * This function iterates over the entries in the unicast address history list. @@ -269,9 +298,9 @@ const otHistoryTrackerNetworkInfo *otHistoryTrackerIterateNetInfoHistory(otInsta * */ const otHistoryTrackerUnicastAddressInfo *otHistoryTrackerIterateUnicastAddressHistory( - otInstance * aInstance, + otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge); + uint32_t *aEntryAge); /** * This function iterates over the entries in the multicast address history list. @@ -287,9 +316,9 @@ const otHistoryTrackerUnicastAddressInfo *otHistoryTrackerIterateUnicastAddressH * */ const otHistoryTrackerMulticastAddressInfo *otHistoryTrackerIterateMulticastAddressHistory( - otInstance * aInstance, + otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge); + uint32_t *aEntryAge); /** * This function iterates over the entries in the RX message history list. @@ -304,9 +333,9 @@ const otHistoryTrackerMulticastAddressInfo *otHistoryTrackerIterateMulticastAddr * @returns The `otHistoryTrackerMessageInfo` entry or `NULL` if no more entries in the list. * */ -const otHistoryTrackerMessageInfo *otHistoryTrackerIterateRxHistory(otInstance * aInstance, +const otHistoryTrackerMessageInfo *otHistoryTrackerIterateRxHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge); + uint32_t *aEntryAge); /** * This function iterates over the entries in the TX message history list. @@ -321,9 +350,9 @@ const otHistoryTrackerMessageInfo *otHistoryTrackerIterateRxHistory(otInstance * * @returns The `otHistoryTrackerMessageInfo` entry or `NULL` if no more entries in the list. * */ -const otHistoryTrackerMessageInfo *otHistoryTrackerIterateTxHistory(otInstance * aInstance, +const otHistoryTrackerMessageInfo *otHistoryTrackerIterateTxHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge); + uint32_t *aEntryAge); /** * This function iterates over the entries in the neighbor history list. @@ -338,9 +367,26 @@ const otHistoryTrackerMessageInfo *otHistoryTrackerIterateTxHistory(otInstance * * @returns The `otHistoryTrackerNeighborInfo` entry or `NULL` if no more entries in the list. * */ -const otHistoryTrackerNeighborInfo *otHistoryTrackerIterateNeighborHistory(otInstance * aInstance, +const otHistoryTrackerNeighborInfo *otHistoryTrackerIterateNeighborHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge); + uint32_t *aEntryAge); + +/** + * This function iterates over the entries in the router history list. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * @param[in,out] aIterator A pointer to an iterator. MUST be initialized or the behavior is undefined. + * @param[out] aEntryAge A pointer to a variable to output the entry's age. MUST NOT be NULL. + * Age is provided as the duration (in milliseconds) from when entry was recorded to + * @p aIterator initialization time. It is set to `OT_HISTORY_TRACKER_MAX_AGE` for entries + * older than max age. + * + * @returns The `otHistoryTrackerRouterInfo` entry or `NULL` if no more entries in the list. + * + */ +const otHistoryTrackerRouterInfo *otHistoryTrackerIterateRouterHistory(otInstance *aInstance, + otHistoryTrackerIterator *aIterator, + uint32_t *aEntryAge); /** * This function iterates over the entries in the Network Data on mesh prefix entry history list. @@ -355,9 +401,9 @@ const otHistoryTrackerNeighborInfo *otHistoryTrackerIterateNeighborHistory(otIns * @returns The `otHistoryTrackerOnMeshPrefixInfo` entry or `NULL` if no more entries in the list. * */ -const otHistoryTrackerOnMeshPrefixInfo *otHistoryTrackerIterateOnMeshPrefixHistory(otInstance * aInstance, +const otHistoryTrackerOnMeshPrefixInfo *otHistoryTrackerIterateOnMeshPrefixHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge); + uint32_t *aEntryAge); /** * This function iterates over the entries in the Network Data external route entry history list. @@ -373,9 +419,9 @@ const otHistoryTrackerOnMeshPrefixInfo *otHistoryTrackerIterateOnMeshPrefixHisto * */ const otHistoryTrackerExternalRouteInfo *otHistoryTrackerIterateExternalRouteHistory( - otInstance * aInstance, + otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge); + uint32_t *aEntryAge); /** * This function converts a given entry age to a human-readable string. diff --git a/include/openthread/icmp6.h b/include/openthread/icmp6.h index cefe596e4f6..38bd6a6a166 100644 --- a/include/openthread/icmp6.h +++ b/include/openthread/icmp6.h @@ -66,6 +66,8 @@ typedef enum otIcmp6Type OT_ICMP6_TYPE_ECHO_REPLY = 129, ///< Echo Reply OT_ICMP6_TYPE_ROUTER_SOLICIT = 133, ///< Router Solicitation OT_ICMP6_TYPE_ROUTER_ADVERT = 134, ///< Router Advertisement + OT_ICMP6_TYPE_NEIGHBOR_SOLICIT = 135, ///< Neighbor Solicitation + OT_ICMP6_TYPE_NEIGHBOR_ADVERT = 136, ///< Neighbor Advertisement } otIcmp6Type; /** @@ -115,8 +117,8 @@ typedef struct otIcmp6Header otIcmp6Header; * @param[in] aIcmpHeader A pointer to the received ICMPv6 header. * */ -typedef void (*otIcmp6ReceiveCallback)(void * aContext, - otMessage * aMessage, +typedef void (*otIcmp6ReceiveCallback)(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, const otIcmp6Header *aIcmpHeader); @@ -127,7 +129,7 @@ typedef void (*otIcmp6ReceiveCallback)(void * aContext, typedef struct otIcmp6Handler { otIcmp6ReceiveCallback mReceiveCallback; ///< The ICMPv6 received callback - void * mContext; ///< A pointer to arbitrary context information. + void *mContext; ///< A pointer to arbitrary context information. struct otIcmp6Handler *mNext; ///< A pointer to the next handler in the list. } otIcmp6Handler; @@ -188,8 +190,8 @@ otError otIcmp6RegisterHandler(otInstance *aInstance, otIcmp6Handler *aHandler); * May be zero. * */ -otError otIcmp6SendEchoRequest(otInstance * aInstance, - otMessage * aMessage, +otError otIcmp6SendEchoRequest(otInstance *aInstance, + otMessage *aMessage, const otMessageInfo *aMessageInfo, uint16_t aIdentifier); diff --git a/include/openthread/instance.h b/include/openthread/instance.h index db89b62bfe6..616a1d9da40 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 (230) +#define OPENTHREAD_API_VERSION (321) /** * @addtogroup api-instance @@ -196,6 +196,7 @@ enum OT_CHANGED_JOINER_STATE = 1 << 27, ///< Joiner state changed OT_CHANGED_ACTIVE_DATASET = 1 << 28, ///< Active Operational Dataset changed OT_CHANGED_PENDING_DATASET = 1 << 29, ///< Pending Operational Dataset changed + OT_CHANGED_NAT64_TRANSLATOR_STATE = 1 << 30, ///< The state of NAT64 translator changed }; /** @@ -250,7 +251,7 @@ void otRemoveStateChangeCallback(otInstance *aInstance, otStateChangedCallback a void otInstanceReset(otInstance *aInstance); /** - * This method deletes all the settings stored on non-volatile memory, and then triggers platform reset. + * Deletes all the settings stored on non-volatile memory, and then triggers a platform reset. * * @param[in] aInstance A pointer to an OpenThread instance. * diff --git a/include/openthread/ip6.h b/include/openthread/ip6.h index f624b426ee0..c04bdc2cacd 100644 --- a/include/openthread/ip6.h +++ b/include/openthread/ip6.h @@ -229,7 +229,7 @@ typedef struct otMessageInfo otIp6Address mPeerAddr; ///< The peer IPv6 address. uint16_t mSockPort; ///< The local transport-layer port. uint16_t mPeerPort; ///< The peer transport-layer port. - const void * mLinkInfo; ///< A pointer to link-specific information. + const void *mLinkInfo; ///< A pointer to link-specific information. uint8_t mHopLimit; ///< The IPv6 Hop Limit value. Only applies if `mAllowZeroHopLimit` is FALSE. ///< If `0`, IPv6 Hop Limit is default value `OPENTHREAD_CONFIG_IP6_HOP_LIMIT_DEFAULT`. ///< Otherwise, specifies the IPv6 Hop Limit. @@ -257,9 +257,9 @@ enum }; /** - * This function brings up/down the IPv6 interface. + * Brings the IPv6 interface up or down. * - * Call this function to enable/disable IPv6 communication. + * Call this to enable or disable IPv6 communication. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aEnabled TRUE to enable IPv6, FALSE otherwise. @@ -272,7 +272,7 @@ enum otError otIp6SetEnabled(otInstance *aInstance, bool aEnabled); /** - * This function indicates whether or not the IPv6 interface is up. + * Indicates whether or not the IPv6 interface is up. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -283,10 +283,10 @@ otError otIp6SetEnabled(otInstance *aInstance, bool aEnabled); bool otIp6IsEnabled(otInstance *aInstance); /** - * Add a Network Interface Address to the Thread interface. + * Adds a Network Interface Address to the Thread interface. * * The passed-in instance @p aAddress is copied by the Thread interface. The Thread interface only - * supports a fixed number of externally added unicast addresses. See OPENTHREAD_CONFIG_IP6_MAX_EXT_UCAST_ADDRS. + * supports a fixed number of externally added unicast addresses. See `OPENTHREAD_CONFIG_IP6_MAX_EXT_UCAST_ADDRS`. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aAddress A pointer to a Network Interface Address. @@ -298,7 +298,7 @@ bool otIp6IsEnabled(otInstance *aInstance); otError otIp6AddUnicastAddress(otInstance *aInstance, const otNetifAddress *aAddress); /** - * Remove a Network Interface Address from the Thread interface. + * Removes a Network Interface Address from the Thread interface. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aAddress A pointer to an IP Address. @@ -310,7 +310,7 @@ otError otIp6AddUnicastAddress(otInstance *aInstance, const otNetifAddress *aAdd otError otIp6RemoveUnicastAddress(otInstance *aInstance, const otIp6Address *aAddress); /** - * Get the list of IPv6 addresses assigned to the Thread interface. + * Gets the list of IPv6 addresses assigned to the Thread interface. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -319,10 +319,10 @@ otError otIp6RemoveUnicastAddress(otInstance *aInstance, const otIp6Address *aAd const otNetifAddress *otIp6GetUnicastAddresses(otInstance *aInstance); /** - * Subscribe the Thread interface to a Network Interface Multicast Address. + * Subscribes the Thread interface to a Network Interface Multicast Address. * * The passed in instance @p aAddress will be copied by the Thread interface. The Thread interface only - * supports a fixed number of externally added multicast addresses. See OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS. + * supports a fixed number of externally added multicast addresses. See `OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS`. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aAddress A pointer to an IP Address. @@ -338,7 +338,7 @@ const otNetifAddress *otIp6GetUnicastAddresses(otInstance *aInstance); otError otIp6SubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress); /** - * Unsubscribe the Thread interface to a Network Interface Multicast Address. + * Unsubscribes the Thread interface to a Network Interface Multicast Address. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aAddress A pointer to an IP Address. @@ -351,7 +351,7 @@ otError otIp6SubscribeMulticastAddress(otInstance *aInstance, const otIp6Address otError otIp6UnsubscribeMulticastAddress(otInstance *aInstance, const otIp6Address *aAddress); /** - * Get the list of IPv6 multicast addresses subscribed to the Thread interface. + * Gets the list of IPv6 multicast addresses subscribed to the Thread interface. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -361,7 +361,7 @@ otError otIp6UnsubscribeMulticastAddress(otInstance *aInstance, const otIp6Addre const otNetifMulticastAddress *otIp6GetMulticastAddresses(otInstance *aInstance); /** - * Check if multicast promiscuous mode is enabled on the Thread interface. + * Checks if multicast promiscuous mode is enabled on the Thread interface. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -371,7 +371,7 @@ const otNetifMulticastAddress *otIp6GetMulticastAddresses(otInstance *aInstance) bool otIp6IsMulticastPromiscuousEnabled(otInstance *aInstance); /** - * Enable multicast promiscuous mode on the Thread interface. + * Enables or disables multicast promiscuous mode on the Thread interface. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aEnabled TRUE to enable Multicast Promiscuous mode, FALSE otherwise. @@ -414,8 +414,8 @@ otMessage *otIp6NewMessage(otInstance *aInstance, const otMessageSettings *aSett * @sa otMessageFree * */ -otMessage *otIp6NewMessageFromBuffer(otInstance * aInstance, - const uint8_t * aData, +otMessage *otIp6NewMessageFromBuffer(otInstance *aInstance, + const uint8_t *aData, uint16_t aDataLength, const otMessageSettings *aSettings); @@ -610,12 +610,27 @@ bool otIp6ArePrefixesEqual(const otIp6Prefix *aFirst, const otIp6Prefix *aSecond * @param[in] aString A pointer to a NULL-terminated string. * @param[out] aAddress A pointer to an IPv6 address. * - * @retval OT_ERROR_NONE Successfully parsed the string. - * @retval OT_ERROR_INVALID_ARGS Failed to parse the string. + * @retval OT_ERROR_NONE Successfully parsed @p aString and updated @p aAddress. + * @retval OT_ERROR_PARSE Failed to parse @p aString as an IPv6 address. * */ otError otIp6AddressFromString(const char *aString, otIp6Address *aAddress); +/** + * This function converts a human-readable IPv6 prefix string into a binary representation. + * + * The @p aString parameter should be a string in the format "
/", where `
` is an IPv6 + * address and `` is a prefix length. + * + * @param[in] aString A pointer to a NULL-terminated string. + * @param[out] aPrefix A pointer to an IPv6 prefix. + * + * @retval OT_ERROR_NONE Successfully parsed the string as an IPv6 prefix and updated @p aPrefix. + * @retval OT_ERROR_PARSE Failed to parse @p aString as an IPv6 prefix. + * + */ +otError otIp6PrefixFromString(const char *aString, otIp6Prefix *aPrefix); + #define OT_IP6_ADDRESS_STRING_SIZE 40 ///< Recommended size for string representation of an IPv6 address. /** @@ -638,8 +653,9 @@ void otIp6AddressToString(const otIp6Address *aAddress, char *aBuffer, uint16_t /** * This function converts a given IPv6 socket address to a human-readable string. * - * The IPv6 socket address string is formatted as "[
]:" where `
is shown as 16 hex values - * separated by ':' and `` is the port number in decimal format (i.e., "[%x:%x:...:%x]:%u") + * The IPv6 socket address string is formatted as [`address`]:`port` where `address` is shown + * as 16 hex values separated by `:` and `port` is the port number in decimal format, + * for example "[%x:%x:...:%x]:%u". * * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be truncated * but the outputted string is always null-terminated. @@ -679,6 +695,16 @@ void otIp6PrefixToString(const otIp6Prefix *aPrefix, char *aBuffer, uint16_t aSi */ uint8_t otIp6PrefixMatch(const otIp6Address *aFirst, const otIp6Address *aSecond); +/** + * This method gets a prefix with @p aLength from @p aAddress. + * + * @param[in] aAddress A pointer to an IPv6 address. + * @param[in] aLength The length of prefix in bits. + * @param[out] aPrefix A pointer to output the IPv6 prefix. + * + */ +void otIp6GetPrefix(const otIp6Address *aAddress, uint8_t aLength, otIp6Prefix *aPrefix); + /** * This function indicates whether or not a given IPv6 address is the Unspecified Address. * @@ -775,7 +801,7 @@ void otIp6SetSlaacPrefixFilter(otInstance *aInstance, otIp6SlaacPrefixFilter aFi * @sa otIp6RegisterMulticastListeners * */ -typedef void (*otIp6RegisterMulticastListenersCallback)(void * aContext, +typedef void (*otIp6RegisterMulticastListenersCallback)(void *aContext, otError aError, uint8_t aMlrStatus, const otIp6Address *aFailedAddresses, @@ -809,12 +835,12 @@ typedef void (*otIp6RegisterMulticastListenersCallback)(void * aCon * @sa otIp6RegisterMulticastListenersCallback * */ -otError otIp6RegisterMulticastListeners(otInstance * aInstance, - const otIp6Address * aAddresses, +otError otIp6RegisterMulticastListeners(otInstance *aInstance, + const otIp6Address *aAddresses, uint8_t aAddressNum, - const uint32_t * aTimeout, + const uint32_t *aTimeout, otIp6RegisterMulticastListenersCallback aCallback, - void * aContext); + void *aContext); /** * This function sets the Mesh Local IID (for test purpose). @@ -840,6 +866,54 @@ otError otIp6SetMeshLocalIid(otInstance *aInstance, const otIp6InterfaceIdentifi */ const char *otIp6ProtoToString(uint8_t aIpProto); +/** + * This structure represents the counters for packets and bytes. + * + */ +typedef struct otPacketsAndBytes +{ + uint64_t mPackets; ///< The number of packets. + uint64_t mBytes; ///< The number of bytes. +} otPacketsAndBytes; + +/** + * This structure represents the counters of packets forwarded via Border Routing. + * + */ +typedef struct otBorderRoutingCounters +{ + otPacketsAndBytes mInboundUnicast; ///< The counters for inbound unicast. + otPacketsAndBytes mInboundMulticast; ///< The counters for inbound multicast. + otPacketsAndBytes mOutboundUnicast; ///< The counters for outbound unicast. + otPacketsAndBytes mOutboundMulticast; ///< The counters for outbound multicast. + uint32_t mRaRx; ///< The number of received RA packets. + uint32_t mRaTxSuccess; ///< The number of RA packets successfully transmitted. + uint32_t mRaTxFailure; ///< The number of RA packets failed to transmit. + uint32_t mRsRx; ///< The number of received RS packets. + uint32_t mRsTxSuccess; ///< The number of RS packets successfully transmitted. + uint32_t mRsTxFailure; ///< The number of RS packets failed to transmit. +} otBorderRoutingCounters; + +/** + * Gets the Border Routing counters. + * + * This function requires the build-time feature `OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE` to be enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns A pointer to the Border Routing counters. + * + */ +const otBorderRoutingCounters *otIp6GetBorderRoutingCounters(otInstance *aInstance); + +/** + * Resets the Border Routing counters. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + */ +void otIp6ResetBorderRoutingCounters(otInstance *aInstance); + /** * @} * diff --git a/include/openthread/joiner.h b/include/openthread/joiner.h index a1873f74cb2..586e88f5486 100644 --- a/include/openthread/joiner.h +++ b/include/openthread/joiner.h @@ -112,15 +112,15 @@ typedef void (*otJoinerCallback)(otError aError, void *aContext); * @retval OT_ERROR_INVALID_STATE The IPv6 stack is not enabled or Thread stack is fully enabled. * */ -otError otJoinerStart(otInstance * aInstance, - const char * aPskd, - const char * aProvisioningUrl, - const char * aVendorName, - const char * aVendorModel, - const char * aVendorSwVersion, - const char * aVendorData, +otError otJoinerStart(otInstance *aInstance, + const char *aPskd, + const char *aProvisioningUrl, + const char *aVendorName, + const char *aVendorModel, + const char *aVendorSwVersion, + const char *aVendorData, otJoinerCallback aCallback, - void * aContext); + void *aContext); /** * Disables the Thread Joiner role. diff --git a/include/openthread/link.h b/include/openthread/link.h index 0a7280cee66..289cf858ba7 100644 --- a/include/openthread/link.h +++ b/include/openthread/link.h @@ -52,7 +52,7 @@ extern "C" { * @{ * */ -#define OT_US_PER_TEN_SYMBOLS 160 ///< The microseconds per 10 symbols. +#define OT_US_PER_TEN_SYMBOLS OT_RADIO_TEN_SYMBOLS_TIME ///< Time for 10 symbols in units of microseconds /** * This structure represents link-specific information for messages received from the Thread radio. @@ -430,11 +430,11 @@ typedef void (*otHandleActiveScanResult)(otActiveScanResult *aResult, void *aCon * @retval OT_ERROR_BUSY Already performing an Active Scan. * */ -otError otLinkActiveScan(otInstance * aInstance, +otError otLinkActiveScan(otInstance *aInstance, uint32_t aScanChannels, uint16_t aScanDuration, otHandleActiveScanResult aCallback, - void * aCallbackContext); + void *aCallbackContext); /** * This function indicates whether or not an IEEE 802.15.4 Active Scan is currently in progress. @@ -468,11 +468,11 @@ typedef void (*otHandleEnergyScanResult)(otEnergyScanResult *aResult, void *aCon * @retval OT_ERROR_BUSY Could not start the energy scan. * */ -otError otLinkEnergyScan(otInstance * aInstance, +otError otLinkEnergyScan(otInstance *aInstance, uint32_t aScanChannels, uint16_t aScanDuration, otHandleEnergyScanResult aCallback, - void * aCallbackContext); + void *aCallbackContext); /** * This function indicates whether or not an IEEE 802.15.4 Energy Scan is currently in progress. @@ -565,7 +565,7 @@ uint32_t otLinkGetSupportedChannelMask(otInstance *aInstance); otError otLinkSetSupportedChannelMask(otInstance *aInstance, uint32_t aChannelMask); /** - * Get the IEEE 802.15.4 Extended Address. + * Gets the IEEE 802.15.4 Extended Address. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -575,9 +575,9 @@ otError otLinkSetSupportedChannelMask(otInstance *aInstance, uint32_t aChannelMa const otExtAddress *otLinkGetExtendedAddress(otInstance *aInstance); /** - * This function sets the IEEE 802.15.4 Extended Address. + * Sets the IEEE 802.15.4 Extended Address. * - * This function succeeds only when Thread protocols are disabled. + * @note Only succeeds when Thread protocols are disabled. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aExtAddress A pointer to the IEEE 802.15.4 Extended Address. @@ -965,7 +965,7 @@ void otLinkResetTxRetrySuccessHistogram(otInstance *aInstance); const otMacCounters *otLinkGetCounters(otInstance *aInstance); /** - * Reset the MAC layer counters. + * Resets the MAC layer counters. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -1034,7 +1034,7 @@ otError otLinkSetPromiscuous(otInstance *aInstance, bool aPromiscuous); uint8_t otLinkCslGetChannel(otInstance *aInstance); /** - * This function sets the CSL channel. + * Sets the CSL channel. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aChannel The CSL sample channel. Channel value should be `0` (Set CSL Channel unspecified) or @@ -1057,7 +1057,7 @@ otError otLinkCslSetChannel(otInstance *aInstance, uint8_t aChannel); uint16_t otLinkCslGetPeriod(otInstance *aInstance); /** - * This function sets the CSL period. + * Sets the CSL period in units of 10 symbols. Disable CSL by setting this parameter to `0`. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aPeriod The CSL period in units of 10 symbols. @@ -1079,7 +1079,7 @@ otError otLinkCslSetPeriod(otInstance *aInstance, uint16_t aPeriod); uint32_t otLinkCslGetTimeout(otInstance *aInstance); /** - * This function sets the CSL timeout. + * Sets the CSL timeout in seconds. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aTimeout The CSL timeout in seconds. diff --git a/include/openthread/link_metrics.h b/include/openthread/link_metrics.h index 0b039b57ab4..fc9a7f00912 100644 --- a/include/openthread/link_metrics.h +++ b/include/openthread/link_metrics.h @@ -116,10 +116,10 @@ typedef enum otLinkMetricsStatus * @param[in] aContext A pointer to application-specific context. * */ -typedef void (*otLinkMetricsReportCallback)(const otIp6Address * aSource, +typedef void (*otLinkMetricsReportCallback)(const otIp6Address *aSource, const otLinkMetricsValues *aMetricsValues, uint8_t aStatus, - void * aContext); + void *aContext); /** * This function pointer is called when a Link Metrics Management Response is received. * @@ -140,9 +140,9 @@ typedef void (*otLinkMetricsMgmtResponseCallback)(const otIp6Address *aSource, u * */ typedef void (*otLinkMetricsEnhAckProbingIeReportCallback)(otShortAddress aShortAddress, - const otExtAddress * aExtAddress, + const otExtAddress *aExtAddress, const otLinkMetricsValues *aMetricsValues, - void * aContext); + void *aContext); /** * This function sends an MLE Data Request to query Link Metrics. @@ -162,15 +162,15 @@ typedef void (*otLinkMetricsEnhAckProbingIeReportCallback)(otShortAddress * @retval OT_ERROR_NOT_CAPABLE The neighbor is not a Thread 1.2 device and does not support Link Metrics. * */ -otError otLinkMetricsQuery(otInstance * aInstance, - const otIp6Address * aDestination, +otError otLinkMetricsQuery(otInstance *aInstance, + const otIp6Address *aDestination, uint8_t aSeriesId, - const otLinkMetrics * aLinkMetricsFlags, + const otLinkMetrics *aLinkMetricsFlags, otLinkMetricsReportCallback aCallback, - void * aCallbackContext); + void *aCallbackContext); /** - * This function sends an MLE Link Metrics Management Request to configure/clear a Forward Tracking Series. + * Sends an MLE Link Metrics Management Request to configure or clear a Forward Tracking Series. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aDestination A pointer to the destination address. @@ -189,13 +189,13 @@ otError otLinkMetricsQuery(otInstance * aInstance, * @retval OT_ERROR_NOT_CAPABLE The neighbor is not a Thread 1.2 device and does not support Link Metrics. * */ -otError otLinkMetricsConfigForwardTrackingSeries(otInstance * aInstance, - const otIp6Address * aDestination, +otError otLinkMetricsConfigForwardTrackingSeries(otInstance *aInstance, + const otIp6Address *aDestination, uint8_t aSeriesId, otLinkMetricsSeriesFlags aSeriesFlags, - const otLinkMetrics * aLinkMetricsFlags, + const otLinkMetrics *aLinkMetricsFlags, otLinkMetricsMgmtResponseCallback aCallback, - void * aCallbackContext); + void *aCallbackContext); /** * This function sends an MLE Link Metrics Management Request to configure/clear an Enhanced-ACK Based Probing. @@ -218,17 +218,17 @@ otError otLinkMetricsConfigForwardTrackingSeries(otInstance * * @retval OT_ERROR_NOT_CAPABLE The neighbor is not a Thread 1.2 device and does not support Link Metrics. * */ -otError otLinkMetricsConfigEnhAckProbing(otInstance * aInstance, - const otIp6Address * aDestination, +otError otLinkMetricsConfigEnhAckProbing(otInstance *aInstance, + const otIp6Address *aDestination, otLinkMetricsEnhAckFlags aEnhAckFlags, - const otLinkMetrics * aLinkMetricsFlags, + const otLinkMetrics *aLinkMetricsFlags, otLinkMetricsMgmtResponseCallback aCallback, - void * aCallbackContext, + void *aCallbackContext, otLinkMetricsEnhAckProbingIeReportCallback aEnhAckCallback, - void * aEnhAckCallbackContext); + void *aEnhAckCallbackContext); /** - * This function sends an MLE Link Probe message. + * Sends an MLE Link Probe message. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aDestination A pointer to the destination address. @@ -242,7 +242,7 @@ otError otLinkMetricsConfigEnhAckProbing(otInstance * * @retval OT_ERROR_NOT_CAPABLE The neighbor is not a Thread 1.2 device and does not support Link Metrics. * */ -otError otLinkMetricsSendLinkProbe(otInstance * aInstance, +otError otLinkMetricsSendLinkProbe(otInstance *aInstance, const otIp6Address *aDestination, uint8_t aSeriesId, uint8_t aLength); diff --git a/include/openthread/link_raw.h b/include/openthread/link_raw.h index 02293e990df..1f43a0578bd 100644 --- a/include/openthread/link_raw.h +++ b/include/openthread/link_raw.h @@ -174,7 +174,7 @@ otRadioFrame *otLinkRawGetTransmitBuffer(otInstance *aInstance); * OT_ERROR_ABORT when transmission was aborted for other reasons. * */ -typedef void (*otLinkRawTransmitDone)(otInstance * aInstance, +typedef void (*otLinkRawTransmitDone)(otInstance *aInstance, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError); @@ -236,12 +236,12 @@ 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_BUSY The radio is performing energy 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. * */ -otError otLinkRawEnergyScan(otInstance * aInstance, +otError otLinkRawEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration, otLinkRawEnergyScanDone aCallback); @@ -346,7 +346,7 @@ otError otLinkRawSrcMatchClearExtEntries(otInstance *aInstance); * @retval OT_ERROR_INVALID_STATE If the raw link-layer isn't enabled. * */ -otError otLinkRawSetMacKey(otInstance * aInstance, +otError otLinkRawSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKeyId, const otMacKey *aPrevKey, @@ -356,6 +356,9 @@ otError otLinkRawSetMacKey(otInstance * aInstance, /** * Sets the current MAC frame counter value. * + * This function always sets the MAC counter to the new given value @p aMacFrameCounter independent of the current + * value. + * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aMacFrameCounter The MAC frame counter value. * @@ -365,6 +368,18 @@ otError otLinkRawSetMacKey(otInstance * aInstance, */ otError otLinkRawSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter); +/** + * Sets the current MAC frame counter value only if the new value is larger than the current one. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aMacFrameCounter The MAC frame counter value. + * + * @retval OT_ERROR_NONE If successful. + * @retval OT_ERROR_INVALID_STATE If the raw link-layer isn't enabled. + * + */ +otError otLinkRawSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacFrameCounter); + /** * Get current platform time (64bits width) of the radio chip. * diff --git a/include/openthread/logging.h b/include/openthread/logging.h index b07e6436755..b581b27021e 100644 --- a/include/openthread/logging.h +++ b/include/openthread/logging.h @@ -86,7 +86,7 @@ otError otLoggingSetLevel(otLogLevel aLogLevel); * @param[in] ... Arguments for the format specification. * */ -void otLogCritPlat(const char *aFormat, ...); +void otLogCritPlat(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2); /** * This function emits a log message at warning log level. @@ -98,7 +98,7 @@ void otLogCritPlat(const char *aFormat, ...); * @param[in] ... Arguments for the format specification. * */ -void otLogWarnPlat(const char *aFormat, ...); +void otLogWarnPlat(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2); /** * This function emits a log message at note log level. @@ -110,7 +110,7 @@ void otLogWarnPlat(const char *aFormat, ...); * @param[in] ... Arguments for the format specification. * */ -void otLogNotePlat(const char *aFormat, ...); +void otLogNotePlat(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2); /** * This function emits a log message at info log level. @@ -122,7 +122,7 @@ void otLogNotePlat(const char *aFormat, ...); * @param[in] ... Arguments for the format specification. * */ -void otLogInfoPlat(const char *aFormat, ...); +void otLogInfoPlat(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2); /** * This function emits a log message at debug log level. @@ -134,7 +134,7 @@ void otLogInfoPlat(const char *aFormat, ...); * @param[in] ... Arguments for the format specification. * */ -void otLogDebgPlat(const char *aFormat, ...); +void otLogDebgPlat(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(1, 2); /** * This function generates a memory dump at critical log level. @@ -212,7 +212,7 @@ void otDumpDebgPlat(const char *aText, const void *aData, uint16_t aDataLength); * @param[in] ... Arguments for the format specification. * */ -void otLogCli(otLogLevel aLogLevel, const char *aFormat, ...); +void otLogCli(otLogLevel aLogLevel, const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3); /** * @} diff --git a/include/openthread/mesh_diag.h b/include/openthread/mesh_diag.h new file mode 100644 index 00000000000..dd613d1fa6e --- /dev/null +++ b/include/openthread/mesh_diag.h @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2023, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief + * This file defines the OpenThread Mesh Diagnostic APIs. + */ + +#ifndef OPENTHREAD_MESH_DIAG_H_ +#define OPENTHREAD_MESH_DIAG_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup api-mesh-diag + * + * @brief + * This module includes definitions and functions for Mesh Diagnostics. + * + * The Mesh Diagnostics APIs require `OPENTHREAD_CONFIG_MESH_DIAG_ENABLE` and `OPENTHREAD_FTD`. + * + * @{ + * + */ + +/** + * This structure represents the set of configurations used when discovering mesh topology indicating which items to + * discover. + * + */ +typedef struct otMeshDiagDiscoverConfig +{ + bool mDiscoverIp6Addresses : 1; ///< Whether or not to discover IPv6 addresses of every router. + bool mDiscoverChildTable : 1; ///< Whether or not to discover children of every router. +} otMeshDiagDiscoverConfig; + +/** + * This type is an opaque iterator to iterate over list of IPv6 addresses of a router. + * + * Pointers to instance of this type are provided in `otMeshDiagRouterInfo`. + * + */ +typedef struct otMeshDiagIp6AddrIterator otMeshDiagIp6AddrIterator; + +/** + * This type is an opaque iterator to iterate over list of children of a router. + * + * Pointers to instance of this type are provided in `otMeshDiagRouterInfo`. + * + */ +typedef struct otMeshDiagChildIterator otMeshDiagChildIterator; + +/** + * This constant indicates that Thread Version is unknown. + * + * This is used in `otMeshDiagRouterInfo` for `mVersion` property when device does not provide its version. This + * indicates that device is likely running 1.3.0 (version value 4) or earlier. + * + */ +#define OT_MESH_DIAG_VERSION_UNKNOWN 0xffff + +/** + * This type represents information about a router in Thread mesh. + * + */ +typedef struct otMeshDiagRouterInfo +{ + otExtAddress mExtAddress; ///< Extended MAC address. + uint16_t mRloc16; ///< RLOC16. + uint8_t mRouterId; ///< Router ID. + uint16_t mVersion; ///< Thread Version. `OT_MESH_DIAG_VERSION_UNKNOWN` if unknown. + bool mIsThisDevice : 1; ///< Whether router is this device itself. + bool mIsThisDeviceParent : 1; ///< Whether router is parent of this device (when device is a child). + bool mIsLeader : 1; ///< Whether router is leader. + bool mIsBorderRouter : 1; ///< Whether router acts as a border router providing ext connectivity. + + /** + * This array provides the link quality from this router to other routers, also indicating whether a link is + * established between the routers. + * + * The array is indexed based on Router ID. `mLinkQualities[routerId]` indicates the incoming link quality, the + * router sees to the router with `routerId`. Link quality is a value in [0, 3]. Value zero indicates no link. + * Larger value indicate better link quality (as defined by Thread specification). + * + */ + uint8_t mLinkQualities[OT_NETWORK_MAX_ROUTER_ID + 1]; + + /** + * A pointer to an iterator to go through the list of IPv6 addresses of the router. + * + * The pointer is valid only while `otMeshDiagRouterInfo` is valid. It can be used in `otMeshDiagGetNextIp6Address` + * to iterate through the IPv6 addresses. + * + * The pointer can be NULL when there was no request to discover IPv6 addresses (in `otMeshDiagDiscoverConfig`) or + * if the router did not provide the list. + * + */ + otMeshDiagIp6AddrIterator *mIp6AddrIterator; + + /** + * A pointer to an iterator to go through the list of children of the router. + * + * The pointer is valid only while `otMeshDiagRouterInfo` is valid. It can be used in `otMeshDiagGetNextChildInfo` + * to iterate through the children of the router. + * + * The pointer can be NULL when there was no request to discover children (in `otMeshDiagDiscoverConfig`) or + * if the router did not provide the list. + * + */ + otMeshDiagChildIterator *mChildIterator; +} otMeshDiagRouterInfo; + +/** + * This type represents information about a discovered child in Thread mesh. + * + */ +typedef struct otMeshDiagChildInfo +{ + uint16_t mRloc16; ///< RLOC16. + otLinkModeConfig mMode; ///< Device mode. + uint8_t mLinkQuality; ///< Incoming link quality to child from parent. + bool mIsThisDevice : 1; ///< Whether child is this device itself. + bool mIsBorderRouter : 1; ///< Whether child acts as a border router providing ext connectivity. +} otMeshDiagChildInfo; + +/** + * This function pointer type represents the callback used by `otMeshDiagDiscoverTopology()` to provide information + * about a discovered router. + * + * When @p aError is `OT_ERROR_PENDING`, it indicates that the discovery is not yet finished and there will be more + * routers to discover and the callback will be invoked again. + * + * @param[in] aError OT_ERROR_PENDING Indicates there are more routers to be discovered. + * OT_ERROR_NONE Indicates this is the last router and mesh discovery is done. + * OT_ERROR_RESPONSE_TIMEOUT Timed out waiting for response from one or more routers. + * @param[in] aRouterInfo The discovered router info (can be null if `aError` is OT_ERROR_RESPONSE_TIMEOUT). + * @param[in] aContext Application-specific context. + * + */ +typedef void (*otMeshDiagDiscoverCallback)(otError aError, otMeshDiagRouterInfo *aRouterInfo, void *aContext); + +/** + * This function starts network topology discovery. + * + * @param[in] aInstance The OpenThread instance. + * @param[in] aConfig The configuration to use for discovery (e.g., which items to discover). + * @param[in] aCallback The callback to report the discovered routers. + * @param[in] aContext A context to pass in @p aCallback. + * + * @retval OT_ERROR_NONE The network topology discovery started successfully. + * @retval OT_ERROR_BUSY A previous discovery request is still ongoing. + * @retval OT_ERROR_INVALID_STATE Device is not attached. + * @retval OT_ERROR_NO_BUFS Could not allocate buffer to send discovery messages. + * + */ +otError otMeshDiagDiscoverTopology(otInstance *aInstance, + const otMeshDiagDiscoverConfig *aConfig, + otMeshDiagDiscoverCallback aCallback, + void *aContext); + +/** + * This function cancels an ongoing topology discovery if there is one, otherwise no action. + * + * When ongoing discovery is cancelled, the callback from `otMeshDiagDiscoverTopology()` will not be called anymore. + * + */ +void otMeshDiagCancel(otInstance *aInstance); + +/** + * This function iterates through the discovered IPv6 address of a router. + * + * @param[in,out] aIterator The address iterator to use. + * @param[out] aIp6Address A pointer to return the next IPv6 address (if any). + * + * @retval OT_ERROR_NONE Successfully retrieved the next address. @p aIp6Address and @p aIterator are updated. + * @retval OT_ERROR_NOT_FOUND No more address. Reached the end of the list. + * + */ +otError otMeshDiagGetNextIp6Address(otMeshDiagIp6AddrIterator *aIterator, otIp6Address *aIp6Address); + +/** + * This function iterates through the discovered children of a router. + * + * @param[in,out] aIterator The address iterator to use. + * @param[out] aChildInfo A pointer to return the child info (if any). + * + * @retval OT_ERROR_NONE Successfully retrieved the next child. @p aChildInfo and @p aIterator are updated. + * @retval OT_ERROR_NOT_FOUND No more child. Reached the end of the list. + * + */ +otError otMeshDiagGetNextChildInfo(otMeshDiagChildIterator *aIterator, otMeshDiagChildInfo *aChildInfo); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // OPENTHREAD_MESH_DIAG_H_ diff --git a/include/openthread/message.h b/include/openthread/message.h index d94c176e4aa..0826ddb3177 100644 --- a/include/openthread/message.h +++ b/include/openthread/message.h @@ -287,8 +287,16 @@ typedef struct otMessageQueueInfo */ typedef struct otBufferInfo { - uint16_t mTotalBuffers; ///< The total number of buffers in the messages pool (0xffff if unknown). - uint16_t mFreeBuffers; ///< The number of free buffers (0xffff if unknown). + uint16_t mTotalBuffers; ///< The total number of buffers in the messages pool (0xffff if unknown). + uint16_t mFreeBuffers; ///< The number of free buffers (0xffff if unknown). + + /** + * The maximum number of used buffers at the same time since OT stack initialization or last call to + * `otMessageResetBufferInfo()`. + * + */ + uint16_t mMaxUsedBuffers; + otMessageQueueInfo m6loSendQueue; ///< Info about 6LoWPAN send queue. otMessageQueueInfo m6loReassemblyQueue; ///< Info about 6LoWPAN reassembly queue. otMessageQueueInfo mIp6Queue; ///< Info about IPv6 send queue. @@ -369,6 +377,16 @@ otMessage *otMessageQueueGetNext(otMessageQueue *aQueue, const otMessage *aMessa */ void otMessageGetBufferInfo(otInstance *aInstance, otBufferInfo *aBufferInfo); +/** + * Reset the Message Buffer information counter tracking the maximum number buffers in use at the same time. + * + * This resets `mMaxUsedBuffers` in `otBufferInfo`. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * + */ +void otMessageResetBufferInfo(otInstance *aInstance); + /** * @} * diff --git a/include/openthread/multi_radio.h b/include/openthread/multi_radio.h index 744b1c080b0..13930f58c5f 100644 --- a/include/openthread/multi_radio.h +++ b/include/openthread/multi_radio.h @@ -85,8 +85,8 @@ typedef struct otMultiRadioNeighborInfo * @retval OT_ERROR_NOT_FOUND Could not find a neighbor with @p aExtAddress. * */ -otError otMultiRadioGetNeighborInfo(otInstance * aInstance, - const otExtAddress * aExtAddress, +otError otMultiRadioGetNeighborInfo(otInstance *aInstance, + const otExtAddress *aExtAddress, otMultiRadioNeighborInfo *aNeighborInfo); /** diff --git a/include/openthread/nat64.h b/include/openthread/nat64.h index 55bef4df9f8..cd1eec2996e 100644 --- a/include/openthread/nat64.h +++ b/include/openthread/nat64.h @@ -35,6 +35,7 @@ #ifndef OPENTHREAD_NAT64_H_ #define OPENTHREAD_NAT64_H_ +#include #include #ifdef __cplusplus @@ -45,7 +46,7 @@ extern "C" { * @addtogroup api-nat64 * * @brief This module includes functions and structs for the NAT64 function on the border router. These functions are - * only available when `OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE` is enabled. + * only available when `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled. * * @{ * @@ -87,6 +88,407 @@ typedef struct otIp4Cidr uint8_t mLength; } otIp4Cidr; +/** + * Represents the counters for NAT64. + * + */ +typedef struct otNat64Counters +{ + uint64_t m4To6Packets; ///< Number of packets translated from IPv4 to IPv6. + uint64_t m4To6Bytes; ///< Sum of size of packets translated from IPv4 to IPv6. + uint64_t m6To4Packets; ///< Number of packets translated from IPv6 to IPv4. + uint64_t m6To4Bytes; ///< Sum of size of packets translated from IPv6 to IPv4. +} otNat64Counters; + +/** + * Represents the counters for the protocols supported by NAT64. + * + */ +typedef struct otNat64ProtocolCounters +{ + otNat64Counters mTotal; ///< Counters for sum of all protocols. + otNat64Counters mIcmp; ///< Counters for ICMP and ICMPv6. + otNat64Counters mUdp; ///< Counters for UDP. + otNat64Counters mTcp; ///< Counters for TCP. +} otNat64ProtocolCounters; + +/** + * Packet drop reasons. + * + */ +typedef enum otNat64DropReason +{ + OT_NAT64_DROP_REASON_UNKNOWN = 0, ///< Packet drop for unknown reasons. + OT_NAT64_DROP_REASON_ILLEGAL_PACKET, ///< Packet drop due to failed to parse the datagram. + OT_NAT64_DROP_REASON_UNSUPPORTED_PROTO, ///< Packet drop due to unsupported IP protocol. + OT_NAT64_DROP_REASON_NO_MAPPING, ///< Packet drop due to no mappings found or mapping pool exhausted. + //--- + OT_NAT64_DROP_REASON_COUNT, +} otNat64DropReason; + +/** + * Represents the counters of dropped packets due to errors when handling NAT64 packets. + * + */ +typedef struct otNat64ErrorCounters +{ + uint64_t mCount4To6[OT_NAT64_DROP_REASON_COUNT]; ///< Errors translating IPv4 packets. + uint64_t mCount6To4[OT_NAT64_DROP_REASON_COUNT]; ///< Errors translating IPv6 packets. +} otNat64ErrorCounters; + +/** + * Gets NAT64 translator counters. + * + * The counter is counted since the instance initialized. + * + * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[out] aCounters A pointer to an `otNat64Counters` where the counters of NAT64 translator will be placed. + * + */ +void otNat64GetCounters(otInstance *aInstance, otNat64ProtocolCounters *aCounters); + +/** + * Gets the NAT64 translator error counters. + * + * The counters are initialized to zero when the OpenThread instance is initialized. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[out] aCounters A pointer to an `otNat64Counters` where the counters of NAT64 translator will be placed. + * + */ +void otNat64GetErrorCounters(otInstance *aInstance, otNat64ErrorCounters *aCounters); + +/** + * Represents an address mapping record for NAT64. + * + * @note The counters will be reset for each mapping session even for the same address pair. Applications can use `mId` + * to identify different sessions to calculate the packets correctly. + * + */ +typedef struct otNat64AddressMapping +{ + uint64_t mId; ///< The unique id for a mapping session. + + otIp4Address mIp4; ///< The IPv4 address of the mapping. + otIp6Address mIp6; ///< The IPv6 address of the mapping. + uint32_t mRemainingTimeMs; ///< Remaining time before expiry in milliseconds. + + otNat64ProtocolCounters mCounters; +} otNat64AddressMapping; + +/** + * Used to iterate through NAT64 address mappings. + * + * The fields in this type are opaque (intended for use by OpenThread core only) and therefore should not be + * accessed or used by caller. + * + * Before using an iterator, it MUST be initialized using `otNat64AddressMappingIteratorInit()`. + * + */ +typedef struct otNat64AddressMappingIterator +{ + void *mPtr; +} otNat64AddressMappingIterator; + +/** + * Initializes an `otNat64AddressMappingIterator`. + * + * An iterator MUST be initialized before it is used. + * + * An iterator can be initialized again to restart from the beginning of the mapping info. + * + * @param[in] aInstance The OpenThread instance. + * @param[out] aIterator A pointer to the iterator to initialize. + * + */ +void otNat64InitAddressMappingIterator(otInstance *aInstance, otNat64AddressMappingIterator *aIterator); + +/** + * Gets the next AddressMapping info (using an iterator). + * + * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in,out] aIterator A pointer to the iterator. On success the iterator will be updated to point to next + * NAT64 address mapping record. To get the first entry the iterator should be set to + * OT_NAT64_ADDRESS_MAPPING_ITERATOR_INIT. + * @param[out] aMapping A pointer to an `otNat64AddressMapping` where information of next NAT64 address + * mapping record is placed (on success). + * + * @retval OT_ERROR_NONE Successfully found the next NAT64 address mapping info (@p aMapping was successfully + * updated). + * @retval OT_ERROR_NOT_FOUND No subsequent NAT64 address mapping info was found. + * + */ +otError otNat64GetNextAddressMapping(otInstance *aInstance, + otNat64AddressMappingIterator *aIterator, + otNat64AddressMapping *aMapping); + +/** + * States of NAT64. + * + */ +typedef enum +{ + OT_NAT64_STATE_DISABLED = 0, ///< NAT64 is disabled. + OT_NAT64_STATE_NOT_RUNNING, ///< NAT64 is enabled, but one or more dependencies of NAT64 are not running. + OT_NAT64_STATE_IDLE, ///< NAT64 is enabled, but this BR is not an active NAT64 BR. + OT_NAT64_STATE_ACTIVE, ///< The BR is publishing a NAT64 prefix and/or translating packets. +} otNat64State; + +/** + * Gets the state of NAT64 translator. + * + * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @retval OT_NAT64_STATE_DISABLED NAT64 translator is disabled. + * @retval OT_NAT64_STATE_NOT_RUNNING NAT64 translator is enabled, but the translator is not configured with a valid + * NAT64 prefix and a CIDR. + * @retval OT_NAT64_STATE_ACTIVE NAT64 translator is enabled, and is translating packets. + * + */ +otNat64State otNat64GetTranslatorState(otInstance *aInstance); + +/** + * Gets the state of NAT64 prefix manager. + * + * Available when `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @retval OT_NAT64_STATE_DISABLED NAT64 prefix manager is disabled. + * @retval OT_NAT64_STATE_NOT_RUNNING NAT64 prefix manager is enabled, but is not running (because the routing manager + * is not running). + * @retval OT_NAT64_STATE_IDLE NAT64 prefix manager is enabled, but is not publishing a NAT64 prefix. Usually + * when there is another border router publishing a NAT64 prefix with higher + * priority. + * @retval OT_NAT64_STATE_ACTIVE NAT64 prefix manager is enabled, and is publishing NAT64 prefix to the Thread + * network. + * + */ +otNat64State otNat64GetPrefixManagerState(otInstance *aInstance); + +/** + * Enable or disable NAT64 functions. + * + * Note: This includes the NAT64 Translator (when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled) and the NAT64 + * Prefix Manager (when `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled). + * + * When `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled, setting disabled to true resets the + * mapping table in the translator. + * + * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` or `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is + * enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aEnabled A boolean to enable/disable the NAT64 functions + * + * @sa otNat64GetTranslatorState + * @sa otNat64GetPrefixManagerState + * + */ +void otNat64SetEnabled(otInstance *aInstance, bool aEnable); + +/** + * Allocate a new message buffer for sending an IPv4 message to the NAT64 translator. + * + * Message buffers allocated by this function will have 20 bytes (difference between the size of IPv6 headers + * and IPv4 header sizes) reserved. + * + * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled. + * + * @note If @p aSettings is `NULL`, the link layer security is enabled and the message priority is set to + * OT_MESSAGE_PRIORITY_NORMAL by default. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aSettings A pointer to the message settings or NULL to set default settings. + * + * @returns A pointer to the message buffer or NULL if no message buffers are available or parameters are invalid. + * + * @sa otNat64Send + * + */ +otMessage *otIp4NewMessage(otInstance *aInstance, const otMessageSettings *aSettings); + +/** + * Sets the CIDR used when setting the source address of the outgoing translated IPv4 packets. + * + * This function is available only when OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE is enabled. + * + * @note A valid CIDR must have a non-zero prefix length. The actual addresses pool is limited by the size of the + * mapping pool and the number of addresses available in the CIDR block. + * + * @note This function can be called at any time, but the NAT64 translator will be reset and all existing sessions will + * be expired when updating the configured CIDR. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aCidr A pointer to an otIp4Cidr for the IPv4 CIDR block for NAT64. + * + * @retval OT_ERROR_INVALID_ARGS The given CIDR is not a valid IPv4 CIDR for NAT64. + * @retval OT_ERROR_NONE Successfully set the CIDR for NAT64. + * + * @sa otBorderRouterSend + * @sa otBorderRouterSetReceiveCallback + * + */ +otError otNat64SetIp4Cidr(otInstance *aInstance, const otIp4Cidr *aCidr); + +/** + * Translates an IPv4 datagram to an IPv6 datagram and sends via the Thread interface. + * + * The caller transfers ownership of @p aMessage when making this call. OpenThread will free @p aMessage when + * processing is complete, including when a value other than `OT_ERROR_NONE` is returned. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aMessage A pointer to the message buffer containing the IPv4 datagram. + * + * @retval OT_ERROR_NONE Successfully processed the message. + * @retval OT_ERROR_DROP Message was well-formed but not fully processed due to packet processing + * rules. + * @retval OT_ERROR_NO_BUFS Could not allocate necessary message buffers when processing the datagram. + * @retval OT_ERROR_NO_ROUTE No route to host. + * @retval OT_ERROR_INVALID_SOURCE_ADDRESS Source address is invalid, e.g. an anycast address or a multicast address. + * @retval OT_ERROR_PARSE Encountered a malformed header when processing the message. + * + */ +otError otNat64Send(otInstance *aInstance, otMessage *aMessage); + +/** + * This function pointer is called when an IPv4 datagram (translated by NAT64 translator) is received. + * + * @param[in] aMessage A pointer to the message buffer containing the received IPv6 datagram. This function transfers + * the ownership of the @p aMessage to the receiver of the callback. The message should be + * freed by the receiver of the callback after it is processed. + * @param[in] aContext A pointer to application-specific context. + * + */ +typedef void (*otNat64ReceiveIp4Callback)(otMessage *aMessage, void *aContext); + +/** + * Registers a callback to provide received IPv4 datagrams. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aCallback A pointer to a function that is called when an IPv4 datagram is received or + * NULL to disable the callback. + * @param[in] aCallbackContext A pointer to application-specific context. + * + */ +void otNat64SetReceiveIp4Callback(otInstance *aInstance, otNat64ReceiveIp4Callback aCallback, void *aContext); + +/** + * Gets the IPv4 CIDR configured in the NAT64 translator. + * + * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[out] aCidr A pointer to an otIp4Cidr. Where the CIDR will be filled. + * + */ +otError otNat64GetCidr(otInstance *aInstance, otIp4Cidr *aCidr); + +/** + * Test if two IPv4 addresses are the same. + * + * @param[in] aFirst A pointer to the first IPv4 address to compare. + * @param[in] aSecond A pointer to the second IPv4 address to compare. + * + * @retval TRUE The two IPv4 addresses are the same. + * @retval FALSE The two IPv4 addresses are not the same. + * + */ +bool otIp4IsAddressEqual(const otIp4Address *aFirst, const otIp4Address *aSecond); + +/** + * Set @p aIp4Address by performing NAT64 address translation from @p aIp6Address as specified + * in RFC 6052. + * + * The NAT64 @p aPrefixLength MUST be one of the following values: 32, 40, 48, 56, 64, or 96, otherwise the behavior + * of this method is undefined. + * + * @param[in] aPrefixLength The prefix length to use for IPv4/IPv6 translation. + * @param[in] aIp6Address A pointer to an IPv6 address. + * @param[out] aIp4Address A pointer to output the IPv4 address. + * + */ +void otIp4ExtractFromIp6Address(uint8_t aPrefixLength, const otIp6Address *aIp6Address, otIp4Address *aIp4Address); + +#define OT_IP4_ADDRESS_STRING_SIZE 17 ///< Length of 000.000.000.000 plus a suffix NUL + +/** + * Converts the address to a string. + * + * The string format uses quad-dotted notation of four bytes in the address (e.g., "127.0.0.1"). + * + * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be + * truncated but the outputted string is always null-terminated. + * + * @param[in] aAddress A pointer to an IPv4 address (MUST NOT be NULL). + * @param[out] aBuffer A pointer to a char array to output the string (MUST NOT be `nullptr`). + * @param[in] aSize The size of @p aBuffer (in bytes). + * + */ +void otIp4AddressToString(const otIp4Address *aAddress, char *aBuffer, uint16_t aSize); + +#define OT_IP4_CIDR_STRING_SIZE 20 ///< Length of 000.000.000.000/00 plus a suffix NUL + +/** + * This function converts a human-readable IPv4 CIDR string into a binary representation. + * + * @param[in] aString A pointer to a NULL-terminated string. + * @param[out] aCidr A pointer to an IPv4 CIDR. + * + * @retval OT_ERROR_NONE Successfully parsed the string. + * @retval OT_ERROR_INVALID_ARGS Failed to parse the string. + * + */ +otError otIp4CidrFromString(const char *aString, otIp4Cidr *aCidr); + +/** + * Converts the IPv4 CIDR to a string. + * + * The string format uses quad-dotted notation of four bytes in the address with the length of prefix (e.g., + * "127.0.0.1/32"). + * + * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be + * truncated but the outputted string is always null-terminated. + * + * @param[in] aCidr A pointer to an IPv4 CIDR (MUST NOT be NULL). + * @param[out] aBuffer A pointer to a char array to output the string (MUST NOT be `nullptr`). + * @param[in] aSize The size of @p aBuffer (in bytes). + * + */ +void otIp4CidrToString(const otIp4Cidr *aCidr, char *aBuffer, uint16_t aSize); + +/** + * Converts a human-readable IPv4 address string into a binary representation. + * + * @param[in] aString A pointer to a NULL-terminated string. + * @param[out] aAddress A pointer to an IPv4 address. + * + * @retval OT_ERROR_NONE Successfully parsed the string. + * @retval OT_ERROR_INVALID_ARGS Failed to parse the string. + * + */ +otError otIp4AddressFromString(const char *aString, otIp4Address *aAddress); + +/** + * Sets the IPv6 address by performing NAT64 address translation from the preferred NAT64 prefix and the given IPv4 + * address as specified in RFC 6052. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aIp4Address A pointer to the IPv4 address to translate to IPv6. + * @param[out] aIp6Address A pointer to the synthesized IPv6 address. + * + * @returns OT_ERROR_NONE Successfully synthesized the IPv6 address from NAT64 prefix and IPv4 address. + * @returns OT_ERROR_INVALID_STATE No valid NAT64 prefix in the network data. + * + */ +otError otNat64SynthesizeIp6Address(otInstance *aInstance, const otIp4Address *aIp4Address, otIp6Address *aIp6Address); + /** * @} * diff --git a/include/openthread/ncp.h b/include/openthread/ncp.h index 72896723c68..4576c7fd330 100644 --- a/include/openthread/ncp.h +++ b/include/openthread/ncp.h @@ -158,94 +158,9 @@ typedef bool (*otNcpDelegateAllowPeekPoke)(uint32_t aAddress, uint16_t aCount); * @param[in] aAllowPokeDelegate Delegate function pointer for poke operation. * */ -void otNcpRegisterPeekPokeDelagates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate, +void otNcpRegisterPeekPokeDelegates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate, otNcpDelegateAllowPeekPoke aAllowPokeDelegate); -//----------------------------------------------------------------------------------------- -// Legacy network APIs - -#define OT_NCP_LEGACY_ULA_PREFIX_LENGTH 8 ///< Legacy ULA size (in bytes) - -/** - * Defines handler (function pointer) type for starting legacy network - * - * Invoked to start the legacy network. - * - */ -typedef void (*otNcpHandlerStartLegacy)(void); - -/** - * Defines handler (function pointer) type for stopping legacy network - * - * Invoked to stop the legacy network. - * - */ -typedef void (*otNcpHandlerStopLegacy)(void); - -/** - * Defines handler (function pointer) type for initiating joining process. - * - * @param[in] aExtAddress A pointer to the extended address for the node to join - * or NULL if desired to join any neighboring node. - * - * Invoked to initiate a legacy join procedure to any or a specific node. - * - */ -typedef void (*otNcpHandlerJoinLegacyNode)(const otExtAddress *aExtAddress); - -/** - * Defines handler (function pointer) type for setting the legacy ULA prefix. - * - * @param[in] aUlaPrefix A pointer to buffer containing the legacy ULA prefix. - * - * Invoked to set the legacy ULA prefix. - * - */ -typedef void (*otNcpHandlerSetLegacyUlaPrefix)(const uint8_t *aUlaPrefix); - -/** - * Defines a struct containing all the legacy handlers (function pointers). - * - */ -typedef struct otNcpLegacyHandlers -{ - otNcpHandlerStartLegacy mStartLegacy; ///< Start handler - otNcpHandlerStopLegacy mStopLegacy; ///< Stop handler - otNcpHandlerJoinLegacyNode mJoinLegacyNode; ///< Join handler - otNcpHandlerSetLegacyUlaPrefix mSetLegacyUlaPrefix; ///< Set ULA handler -} otNcpLegacyHandlers; - -/** - * This callback is invoked by the legacy stack to notify that a new - * legacy node did join the network. - * - * @param[in] aExtAddr A pointer to the extended address of the joined node. - * - */ -void otNcpHandleLegacyNodeDidJoin(const otExtAddress *aExtAddr); - -/** - * This callback is invoked by the legacy stack to notify that the - * legacy ULA prefix has changed. - * - * @param[in] aUlaPrefix A pointer to the received ULA prefix. - * - */ -void otNcpHandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix); - -/** - * This method registers a set of legacy handlers with NCP. - * - * The set of handlers provided by the struct @p aHandlers are used by - * NCP code to start/stop legacy network. - * The @p aHandlers can be NULL to disable legacy support on NCP. - * Individual handlers in the given handlers struct can also be NULL. - * - * @param[in] aHandlers A pointer to a handler struct. - * - */ -void otNcpRegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers); - /** * @} * diff --git a/include/openthread/netdata.h b/include/openthread/netdata.h index 74b92e89d87..ba999211495 100644 --- a/include/openthread/netdata.h +++ b/include/openthread/netdata.h @@ -71,6 +71,17 @@ typedef struct otBorderRouterConfig uint16_t mRloc16; ///< The border router's RLOC16 (value ignored on config add). } otBorderRouterConfig; +/** + * This structure represents 6LoWPAN Context ID information associated with a prefix in Network Data. + * + */ +typedef struct otLowpanContextInfo +{ + uint8_t mContextId; ///< The 6LoWPAN Context ID. + bool mCompressFlag; ///< The compress flag. + otIp6Prefix mPrefix; ///< The associated IPv6 prefix. +} otLowpanContextInfo; + /** * This structure represents an External Route configuration. * @@ -125,7 +136,7 @@ typedef struct otServiceConfig } otServiceConfig; /** - * This method provides a full or stable copy of the Partition's Thread Network Data. + * Provide full or stable copy of the Partition's Thread Network Data. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aStable TRUE when copying the stable version, FALSE when copying the full version. @@ -133,11 +144,45 @@ typedef struct otServiceConfig * @param[in,out] aDataLength On entry, size of the data buffer pointed to by @p aData. * On exit, number of copied bytes. * + * @retval OT_ERROR_NONE Successfully copied the Thread Network Data into @p aData and updated @p aDataLength. + * @retval OT_ERROR_NO_BUFS Not enough space in @p aData to fully copy the Thread Network Data. + * */ otError otNetDataGet(otInstance *aInstance, bool aStable, uint8_t *aData, uint8_t *aDataLength); /** - * This function gets the next On Mesh Prefix in the partition's Network Data. + * Get the current length (number of bytes) of Partition's Thread Network Data. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @return The length of the Network Data. + * + */ +uint8_t otNetDataGetLength(otInstance *aInstance); + +/** + * Get the maximum observed length of the Thread Network Data since OT stack initialization or since the last call to + * `otNetDataResetMaxLength()`. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @return The maximum length of the Network Data (high water mark for Network Data length). + * + */ +uint8_t otNetDataGetMaxLength(otInstance *aInstance); + +/** + * Reset the tracked maximum length of the Thread Network Data. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @sa otNetDataGetMaxLength + * + */ +void otNetDataResetMaxLength(otInstance *aInstance); + +/** + * Get the next On Mesh Prefix in the partition's Network Data. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in,out] aIterator A pointer to the Network Data iterator context. To get the first on-mesh entry @@ -148,12 +193,12 @@ otError otNetDataGet(otInstance *aInstance, bool aStable, uint8_t *aData, uint8_ * @retval OT_ERROR_NOT_FOUND No subsequent On Mesh prefix exists in the Thread Network Data. * */ -otError otNetDataGetNextOnMeshPrefix(otInstance * aInstance, +otError otNetDataGetNextOnMeshPrefix(otInstance *aInstance, otNetworkDataIterator *aIterator, - otBorderRouterConfig * aConfig); + otBorderRouterConfig *aConfig); /** - * This function gets the next external route in the partition's Network Data. + * Get the next external route in the partition's Network Data. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in,out] aIterator A pointer to the Network Data iterator context. To get the first external route entry @@ -167,7 +212,7 @@ otError otNetDataGetNextOnMeshPrefix(otInstance * aInstance, otError otNetDataGetNextRoute(otInstance *aInstance, otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig); /** - * This function gets the next service in the partition's Network Data. + * Get the next service in the partition's Network Data. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in,out] aIterator A pointer to the Network Data iterator context. To get the first service entry @@ -180,6 +225,22 @@ otError otNetDataGetNextRoute(otInstance *aInstance, otNetworkDataIterator *aIte */ otError otNetDataGetNextService(otInstance *aInstance, otNetworkDataIterator *aIterator, otServiceConfig *aConfig); +/** + * Get the next 6LoWPAN Context ID info in the partition's Network Data. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in,out] aIterator A pointer to the Network Data iterator. To get the first service entry + it should be set to OT_NETWORK_DATA_ITERATOR_INIT. + * @param[out] aContextInfo A pointer to where the retrieved 6LoWPAN Context ID information will be placed. + * + * @retval OT_ERROR_NONE Successfully found the next 6LoWPAN Context ID info. + * @retval OT_ERROR_NOT_FOUND No subsequent 6LoWPAN Context info exists in the partition's Network Data. + * + */ +otError otNetDataGetNextLowpanContextInfo(otInstance *aInstance, + otNetworkDataIterator *aIterator, + otLowpanContextInfo *aContextInfo); + /** * Get the Network Data Version. * @@ -227,12 +288,11 @@ struct otJoinerDiscerner; * @retval OT_ERROR_NOT_FOUND @p aDiscerner is not included in the steering data. * */ -otError otNetDataSteeringDataCheckJoinerWithDiscerner(otInstance * aInstance, +otError otNetDataSteeringDataCheckJoinerWithDiscerner(otInstance *aInstance, const struct otJoinerDiscerner *aDiscerner); /** - * This function checks whether a given Prefix can act as a valid OMR prefix and also the Leader's Network Data contains - * this prefix. + * Check whether a given Prefix can act as a valid OMR prefix and also the Leader's Network Data contains this prefix. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aPrefix A pointer to the IPv6 prefix. diff --git a/include/openthread/netdata_publisher.h b/include/openthread/netdata_publisher.h index 2c38e15137f..10346c25494 100644 --- a/include/openthread/netdata_publisher.h +++ b/include/openthread/netdata_publisher.h @@ -91,8 +91,8 @@ typedef void (*otNetDataDnsSrpServicePublisherCallback)(otNetDataPublisherEvent * */ typedef void (*otNetDataPrefixPublisherCallback)(otNetDataPublisherEvent aEvent, - const otIp6Prefix * aPrefix, - void * aContext); + const otIp6Prefix *aPrefix, + void *aContext); /** * This function requests "DNS/SRP Service Anycast Address" to be published in the Thread Network Data. @@ -170,9 +170,9 @@ bool otNetDataIsDnsSrpServiceAdded(otInstance *aInstance); * @param[in] aContext A pointer to application-specific context (used when @p aCallback is invoked). * */ -void otNetDataSetDnsSrpServicePublisherCallback(otInstance * aInstance, +void otNetDataSetDnsSrpServicePublisherCallback(otInstance *aInstance, otNetDataDnsSrpServicePublisherCallback aCallback, - void * aContext); + void *aContext); /** * Unpublishes any previously added DNS/SRP (Anycast or Unicast) Service entry from the Thread Network @@ -237,6 +237,42 @@ otError otNetDataPublishOnMeshPrefix(otInstance *aInstance, const otBorderRouter */ otError otNetDataPublishExternalRoute(otInstance *aInstance, const otExternalRouteConfig *aConfig); +/** + * This function replaces a previously published external route in the Thread Network Data. + * + * This function requires the feature `OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE` to be enabled. + * + * If there is no previously published external route matching @p aPrefix, this function behaves similarly to + * `otNetDataPublishExternalRoute()`, i.e., it will start the process of publishing @a aConfig as an external route in + * the Thread Network Data. + * + * If there is a previously published route entry matching @p aPrefix, it will be replaced with the new prefix from + * @p aConfig. + * + * - If the @p aPrefix was already added in the Network Data, the change to the new prefix in @p aConfig is immediately + * reflected in the Network Data. This ensures that route entries in the Network Data are not abruptly removed and + * the transition from aPrefix to the new prefix is smooth. + * + * - If the old published @p aPrefix was not added in the Network Data, it will be replaced with the new @p aConfig + * prefix but it will not be immediately added. Instead, it will start the process of publishing it in the Network + * Data (monitoring the Network Data to determine when/if to add the prefix, depending on the number of similar + * prefixes present in the Network Data). + * + * @param[in] aPrefix The previously published external route prefix to replace. + * @param[in] aConfig The external route config to publish. + * @param[in] aRequester The requester (`kFromUser` or `kFromRoutingManager` module). + * + * @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_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`. + * + */ +otError otNetDataReplacePublishedExternalRoute(otInstance *aInstance, + const otIp6Prefix *aPrefix, + const otExternalRouteConfig *aConfig); + /** * This function indicates whether or not currently a published prefix entry (on-mesh or external route) is added to * the Thread Network Data. @@ -265,9 +301,9 @@ bool otNetDataIsPrefixAdded(otInstance *aInstance, const otIp6Prefix *aPrefix); * @param[in] aContext A pointer to application-specific context (used when @p aCallback is invoked). * */ -void otNetDataSetPrefixPublisherCallback(otInstance * aInstance, +void otNetDataSetPrefixPublisherCallback(otInstance *aInstance, otNetDataPrefixPublisherCallback aCallback, - void * aContext); + void *aContext); /** * Unpublishes a previously published On-Mesh or External Route Prefix. diff --git a/include/openthread/netdiag.h b/include/openthread/netdiag.h index d40238340e4..76e523bcd2b 100644 --- a/include/openthread/netdiag.h +++ b/include/openthread/netdiag.h @@ -66,24 +66,34 @@ extern "C" { enum { - OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS = 0, ///< MAC Extended Address TLV - OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS = 1, ///< Address16 TLV - OT_NETWORK_DIAGNOSTIC_TLV_MODE = 2, ///< Mode TLV - OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT = 3, ///< Timeout TLV (the maximum polling time period for SEDs) - OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY = 4, ///< Connectivity TLV - OT_NETWORK_DIAGNOSTIC_TLV_ROUTE = 5, ///< Route64 TLV - OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA = 6, ///< Leader Data TLV - OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA = 7, ///< Network Data TLV - OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST = 8, ///< IPv6 Address List TLV - OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS = 9, ///< MAC Counters TLV - OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL = 14, ///< Battery Level TLV - OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE = 15, ///< Supply Voltage TLV - OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE = 16, ///< Child Table TLV - OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES = 17, ///< Channel Pages TLV - OT_NETWORK_DIAGNOSTIC_TLV_TYPE_LIST = 18, ///< Type List TLV - OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT = 19, ///< Max Child Timeout TLV + OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS = 0, ///< MAC Extended Address TLV + OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS = 1, ///< Address16 TLV + OT_NETWORK_DIAGNOSTIC_TLV_MODE = 2, ///< Mode TLV + OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT = 3, ///< Timeout TLV (the maximum polling time period for SEDs) + OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY = 4, ///< Connectivity TLV + OT_NETWORK_DIAGNOSTIC_TLV_ROUTE = 5, ///< Route64 TLV + OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA = 6, ///< Leader Data TLV + OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA = 7, ///< Network Data TLV + OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST = 8, ///< IPv6 Address List TLV + OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS = 9, ///< MAC Counters TLV + OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL = 14, ///< Battery Level TLV + OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE = 15, ///< Supply Voltage TLV + OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE = 16, ///< Child Table TLV + OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES = 17, ///< Channel Pages TLV + OT_NETWORK_DIAGNOSTIC_TLV_TYPE_LIST = 18, ///< Type List TLV + OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT = 19, ///< Max Child Timeout TLV + OT_NETWORK_DIAGNOSTIC_TLV_VERSION = 24, ///< Version TLV + OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME = 25, ///< Vendor Name TLV + OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL = 26, ///< Vendor Model TLV + OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION = 27, ///< Vendor SW Version TLV + OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION = 28, ///< Thread Stack Version TLV }; +#define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_NAME_TLV_LENGTH 32 ///< Max length of Vendor Name TLV. +#define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_MODEL_TLV_LENGTH 32 ///< Max length of Vendor Model TLV. +#define OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_SW_VERSION_TLV_LENGTH 16 ///< Max length of Vendor SW Version TLV. +#define OT_NETWORK_DIAGNOSTIC_MAX_THREAD_STACK_VERSION_TLV_LENGTH 64 ///< Max length of Thread Stack Version TLV. + typedef uint16_t otNetworkDiagIterator; ///< Used to iterate through Network Diagnostic TLV. /** @@ -202,6 +212,14 @@ typedef struct otNetworkDiagChildEntry */ uint16_t mTimeout : 5; + /** + * Link Quality In value in [0,3]. + * + * Value 0 indicates that sender does not support the feature to provide link quality info. + * + */ + uint8_t mLinkQuality : 2; + /** * Child ID from which an RLOC can be generated. */ @@ -237,6 +255,11 @@ typedef struct otNetworkDiagTlv uint8_t mBatteryLevel; uint16_t mSupplyVoltage; uint32_t mMaxChildTimeout; + uint16_t mVersion; + char mVendorName[OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_NAME_TLV_LENGTH + 1]; + char mVendorModel[OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_MODEL_TLV_LENGTH + 1]; + char mVendorSwVersion[OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_SW_VERSION_TLV_LENGTH + 1]; + char mThreadStackVersion[OT_NETWORK_DIAGNOSTIC_MAX_THREAD_STACK_VERSION_TLV_LENGTH + 1]; struct { uint8_t mCount; @@ -264,6 +287,8 @@ typedef struct otNetworkDiagTlv /** * This function gets the next Network Diagnostic TLV in the message. * + * Requires `OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE`. + * * @param[in] aMessage A pointer to a message. * @param[in,out] aIterator A pointer to the Network Diagnostic iterator context. To get the first * Network Diagnostic TLV it should be set to OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT. @@ -276,9 +301,9 @@ typedef struct otNetworkDiagTlv * @Note A subsequent call to this function is allowed only when current return value is OT_ERROR_NONE. * */ -otError otThreadGetNextDiagnosticTlv(const otMessage * aMessage, +otError otThreadGetNextDiagnosticTlv(const otMessage *aMessage, otNetworkDiagIterator *aIterator, - otNetworkDiagTlv * aNetworkDiagTlv); + otNetworkDiagTlv *aNetworkDiagTlv); /** * This function pointer is called when Network Diagnostic Get response is received. @@ -292,13 +317,15 @@ otError otThreadGetNextDiagnosticTlv(const otMessage * aMessage, * */ typedef void (*otReceiveDiagnosticGetCallback)(otError aError, - otMessage * aMessage, + otMessage *aMessage, const otMessageInfo *aMessageInfo, - void * aContext); + void *aContext); /** * Send a Network Diagnostic Get request. * + * Requires `OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE`. + * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aDestination A pointer to destination address. * @param[in] aTlvTypes An array of Network Diagnostic TLV types. @@ -311,16 +338,18 @@ typedef void (*otReceiveDiagnosticGetCallback)(otError aError, * @retval OT_ERROR_NO_BUFS Insufficient message buffers available to send DIAG_GET.req. * */ -otError otThreadSendDiagnosticGet(otInstance * aInstance, - const otIp6Address * aDestination, +otError otThreadSendDiagnosticGet(otInstance *aInstance, + const otIp6Address *aDestination, const uint8_t aTlvTypes[], uint8_t aCount, otReceiveDiagnosticGetCallback aCallback, - void * aCallbackContext); + void *aCallbackContext); /** * Send a Network Diagnostic Reset request. * + * Requires `OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE`. + * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aDestination A pointer to destination address. * @param[in] aTlvTypes An array of Network Diagnostic TLV types. Currently only Type 9 is allowed. @@ -330,11 +359,92 @@ otError otThreadSendDiagnosticGet(otInstance * aInstance, * @retval OT_ERROR_NO_BUFS Insufficient message buffers available to send DIAG_RST.ntf. * */ -otError otThreadSendDiagnosticReset(otInstance * aInstance, +otError otThreadSendDiagnosticReset(otInstance *aInstance, const otIp6Address *aDestination, const uint8_t aTlvTypes[], uint8_t aCount); +/** + * Get the vendor name string. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The vendor name string. + * + */ +const char *otThreadGetVendorName(otInstance *aInstance); + +/** + * Get the vendor model string. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The vendor model string. + * + */ +const char *otThreadGetVendorModel(otInstance *aInstance); + +/** + * Get the vendor sw version string. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The vendor sw version string. + * + */ +const char *otThreadGetVendorSwVersion(otInstance *aInstance); + +/** + * Set the vendor name string. + * + * Requires `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE`. + * + * @p aVendorName should be UTF8 with max length of 32 chars (`MAX_VENDOR_NAME_TLV_LENGTH`). Maximum length does not + * include the null `\0` character. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aVendorName The vendor name string. + * + * @retval OT_ERROR_NONE Successfully set the vendor name. + * @retval OT_ERROR_INVALID_ARGS @p aVendorName is not valid (too long or not UTF8). + * + */ +otError otThreadSetVendorName(otInstance *aInstance, const char *aVendorName); + +/** + * Set the vendor model string. + * + * Requires `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE`. + * + * @p aVendorModel should be UTF8 with max length of 32 chars (`MAX_VENDOR_MODEL_TLV_LENGTH`). Maximum length does not + * include the null `\0` character. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aVendorModel The vendor model string. + * + * @retval OT_ERROR_NONE Successfully set the vendor model. + * @retval OT_ERROR_INVALID_ARGS @p aVendorModel is not valid (too long or not UTF8). + * + */ +otError otThreadSetVendorModel(otInstance *aInstance, const char *aVendorModel); + +/** + * Set the vendor software version string. + * + * Requires `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE`. + * + * @p aVendorSwVersion should be UTF8 with max length of 16 chars(`MAX_VENDOR_SW_VERSION_TLV_LENGTH`). Maximum length + * does not include the null `\0` character. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aVendorSwVersion The vendor software version string. + * + * @retval OT_ERROR_NONE Successfully set the vendor software version. + * @retval OT_ERROR_INVALID_ARGS @p aVendorSwVersion is not valid (too long or not UTF8). + * + */ +otError otThreadSetVendorSwVersion(otInstance *aInstance, const char *aVendorSwVersion); + /** * @} * diff --git a/include/openthread/network_time.h b/include/openthread/network_time.h index 5e772317300..e20a9d5a34a 100644 --- a/include/openthread/network_time.h +++ b/include/openthread/network_time.h @@ -145,9 +145,9 @@ uint16_t otNetworkTimeGetXtalThreshold(otInstance *aInstance); * @param[in] aCallbackContext The context to be passed to the callback function upon invocation * */ -void otNetworkTimeSyncSetCallback(otInstance * aInstance, +void otNetworkTimeSyncSetCallback(otInstance *aInstance, otNetworkTimeSyncCallbackFn aCallbackFn, - void * aCallbackContext); + void *aCallbackContext); /** * @} diff --git a/include/openthread/ping_sender.h b/include/openthread/ping_sender.h index 07e5c03ce5e..a0e30ca9aea 100644 --- a/include/openthread/ping_sender.h +++ b/include/openthread/ping_sender.h @@ -112,7 +112,7 @@ typedef struct otPingSenderConfig otPingSenderReplyCallback mReplyCallback; ///< Callback function to report replies (can be NULL if not needed). otPingSenderStatisticsCallback mStatisticsCallback; ///< Callback function to report statistics (can be NULL if not needed). - void * mCallbackContext; ///< A pointer to the callback application-specific context. + void *mCallbackContext; ///< A pointer to the callback application-specific context. uint16_t mSize; ///< Data size (# of bytes) excludes IPv6/ICMPv6 header. Zero for default. uint16_t mCount; ///< Number of ping messages to send. Zero to use default. uint32_t mInterval; ///< Ping tx interval in milliseconds. Zero to use default. diff --git a/include/openthread/platform/alarm-micro.h b/include/openthread/platform/alarm-micro.h index e61640740ca..c165a40331a 100644 --- a/include/openthread/platform/alarm-micro.h +++ b/include/openthread/platform/alarm-micro.h @@ -53,6 +53,8 @@ extern "C" { /** * Set the alarm to fire at @p aDt microseconds after @p aT0. * + * For both @p aT0 and @p aDt, the platform MUST support all values in [0, 2^32-1]. + * * @param[in] aInstance The OpenThread instance structure. * @param[in] aT0 The reference time. * @param[in] aDt The time delay in microseconds from @p aT0. @@ -71,6 +73,9 @@ void otPlatAlarmMicroStop(otInstance *aInstance); /** * Get the current time. * + * The current time MUST represent a free-running timer. When maintaining current time, the time value MUST utilize the + * entire range [0, 2^32-1] and MUST NOT wrap before 2^32. + * * @returns The current time in microseconds. * */ diff --git a/include/openthread/platform/alarm-milli.h b/include/openthread/platform/alarm-milli.h index dd72201633d..0ddd9180bf5 100644 --- a/include/openthread/platform/alarm-milli.h +++ b/include/openthread/platform/alarm-milli.h @@ -56,6 +56,8 @@ extern "C" { /** * Set the alarm to fire at @p aDt milliseconds after @p aT0. * + * For both @p aT0 and @p aDt, the platform MUST support all values in [0, 2^32-1]. + * * @param[in] aInstance The OpenThread instance structure. * @param[in] aT0 The reference time. * @param[in] aDt The time delay in milliseconds from @p aT0. @@ -72,6 +74,9 @@ void otPlatAlarmMilliStop(otInstance *aInstance); /** * Get the current time. * + * The current time MUST represent a free-running timer. When maintaining current time, the time value MUST utilize the + * entire range [0, 2^32-1] and MUST NOT wrap before 2^32. + * * @returns The current time in milliseconds. */ uint32_t otPlatAlarmMilliGetNow(void); diff --git a/include/openthread/platform/crypto.h b/include/openthread/platform/crypto.h index 7cadc8e793f..0bccc38b866 100644 --- a/include/openthread/platform/crypto.h +++ b/include/openthread/platform/crypto.h @@ -60,9 +60,10 @@ extern "C" { */ typedef enum { - OT_CRYPTO_KEY_TYPE_RAW, ///< Key Type: Raw Data. - OT_CRYPTO_KEY_TYPE_AES, ///< Key Type: AES. - OT_CRYPTO_KEY_TYPE_HMAC, ///< Key Type: HMAC. + OT_CRYPTO_KEY_TYPE_RAW, ///< Key Type: Raw Data. + OT_CRYPTO_KEY_TYPE_AES, ///< Key Type: AES. + OT_CRYPTO_KEY_TYPE_HMAC, ///< Key Type: HMAC. + OT_CRYPTO_KEY_TYPE_ECDSA, ///< Key Type: ECDSA. } otCryptoKeyType; /** @@ -74,6 +75,7 @@ typedef enum OT_CRYPTO_KEY_ALG_VENDOR, ///< Key Algorithm: Vendor Defined. OT_CRYPTO_KEY_ALG_AES_ECB, ///< Key Algorithm: AES ECB. OT_CRYPTO_KEY_ALG_HMAC_SHA_256, ///< Key Algorithm: HMAC SHA-256. + OT_CRYPTO_KEY_ALG_ECDSA, ///< Key Algorithm: ECDSA. } otCryptoKeyAlgorithm; /** @@ -82,11 +84,12 @@ typedef enum */ enum { - OT_CRYPTO_KEY_USAGE_NONE = 0, ///< Key Usage: Key Usage is empty. - OT_CRYPTO_KEY_USAGE_EXPORT = 1 << 0, ///< Key Usage: Key can be exported. - OT_CRYPTO_KEY_USAGE_ENCRYPT = 1 << 1, ///< Key Usage: Encryption (vendor defined). - OT_CRYPTO_KEY_USAGE_DECRYPT = 1 << 2, ///< Key Usage: AES ECB. - OT_CRYPTO_KEY_USAGE_SIGN_HASH = 1 << 3, ///< Key Usage: HMAC SHA-256. + OT_CRYPTO_KEY_USAGE_NONE = 0, ///< Key Usage: Key Usage is empty. + OT_CRYPTO_KEY_USAGE_EXPORT = 1 << 0, ///< Key Usage: Key can be exported. + OT_CRYPTO_KEY_USAGE_ENCRYPT = 1 << 1, ///< Key Usage: Encryption (vendor defined). + OT_CRYPTO_KEY_USAGE_DECRYPT = 1 << 2, ///< Key Usage: AES ECB. + OT_CRYPTO_KEY_USAGE_SIGN_HASH = 1 << 3, ///< Key Usage: Sign Hash. + OT_CRYPTO_KEY_USAGE_VERIFY_HASH = 1 << 4, ///< Key Usage: Verify Hash. }; /** @@ -126,10 +129,105 @@ typedef struct otCryptoKey */ typedef struct otCryptoContext { - void * mContext; ///< Pointer to the context. + void *mContext; ///< Pointer to the context. uint16_t mContextSize; ///< The length of the context in bytes. } otCryptoContext; +/** + * Length of SHA256 hash (in bytes). + * + */ +#define OT_CRYPTO_SHA256_HASH_SIZE 32 + +/** + * @struct otPlatCryptoSha256Hash + * + * This structure represents a SHA-256 hash. + * + */ +OT_TOOL_PACKED_BEGIN +struct otPlatCryptoSha256Hash +{ + uint8_t m8[OT_CRYPTO_SHA256_HASH_SIZE]; ///< Hash bytes. +} OT_TOOL_PACKED_END; + +/** + * This structure represents a SHA-256 hash. + * + */ +typedef struct otPlatCryptoSha256Hash otPlatCryptoSha256Hash; + +/** + * Max buffer size (in bytes) for representing the EDCSA key-pair in DER format. + * + */ +#define OT_CRYPTO_ECDSA_MAX_DER_SIZE 125 + +/** + * @struct otPlatCryptoEcdsaKeyPair + * + * This structure represents an ECDSA key pair (public and private keys). + * + * The key pair is stored using Distinguished Encoding Rules (DER) format (per RFC 5915). + * + */ +typedef struct otPlatCryptoEcdsaKeyPair +{ + uint8_t mDerBytes[OT_CRYPTO_ECDSA_MAX_DER_SIZE]; + uint8_t mDerLength; +} otPlatCryptoEcdsaKeyPair; + +/** + * Buffer size (in bytes) for representing the EDCSA public key. + * + */ +#define OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE 64 + +/** + * @struct otPlatCryptoEcdsaPublicKey + * + * This struct represents a ECDSA public key. + * + * The public key is stored as a byte sequence representation of an uncompressed curve point (RFC 6605 - sec 4). + * + */ +OT_TOOL_PACKED_BEGIN +struct otPlatCryptoEcdsaPublicKey +{ + uint8_t m8[OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE]; +} OT_TOOL_PACKED_END; + +typedef struct otPlatCryptoEcdsaPublicKey otPlatCryptoEcdsaPublicKey; + +/** + * Buffer size (in bytes) for representing the EDCSA signature. + * + */ +#define OT_CRYPTO_ECDSA_SIGNATURE_SIZE 64 + +/** + * @struct otPlatCryptoEcdsaSignature + * + * This struct represents an ECDSA signature. + * + * The signature is encoded as the concatenated binary representation of two MPIs `r` and `s` which are calculated + * during signing (RFC 6605 - section 4). + * + */ +OT_TOOL_PACKED_BEGIN +struct otPlatCryptoEcdsaSignature +{ + uint8_t m8[OT_CRYPTO_ECDSA_SIGNATURE_SIZE]; +} OT_TOOL_PACKED_END; + +typedef struct otPlatCryptoEcdsaSignature otPlatCryptoEcdsaSignature; + +/** + * Max PBKDF2 SALT length: salt prefix (6) + extended panid (8) + network name (16) + * + */ +#define OT_CRYPTO_PBDKF2_MAX_SALT_SIZE 30 + /** * Initialize the Crypto module. * @@ -160,12 +258,12 @@ void otPlatCryptoInit(void); * This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled. * */ -otError otPlatCryptoImportKey(otCryptoKeyRef * aKeyRef, +otError otPlatCryptoImportKey(otCryptoKeyRef *aKeyRef, otCryptoKeyType aKeyType, otCryptoKeyAlgorithm aKeyAlgorithm, int aKeyUsage, otCryptoKeyStorage aKeyPersistence, - const uint8_t * aKey, + const uint8_t *aKey, size_t aKeyLen); /** @@ -242,7 +340,7 @@ otError otPlatCryptoHmacSha256Deinit(otCryptoContext *aContext); * Start HMAC operation. * * @param[in] aContext Context for HMAC operation. - * @param[in] aKey Key material to be used for for HMAC operation. + * @param[in] aKey Key material to be used for HMAC operation. * * @retval OT_ERROR_NONE Successfully started HMAC operation. * @retval OT_ERROR_FAILED Failed to start HMAC operation. @@ -364,9 +462,9 @@ otError otPlatCryptoHkdfInit(otCryptoContext *aContext); * */ otError otPlatCryptoHkdfExpand(otCryptoContext *aContext, - const uint8_t * aInfo, + const uint8_t *aInfo, uint16_t aInfoLength, - uint8_t * aOutputKey, + uint8_t *aOutputKey, uint16_t aOutputKeyLength); /** @@ -381,8 +479,8 @@ otError otPlatCryptoHkdfExpand(otCryptoContext *aContext, * @retval OT_ERROR_FAILED HKDF Extract failed. * */ -otError otPlatCryptoHkdfExtract(otCryptoContext * aContext, - const uint8_t * aSalt, +otError otPlatCryptoHkdfExtract(otCryptoContext *aContext, + const uint8_t *aSalt, uint16_t aSaltLength, const otCryptoKey *aInputKey); @@ -480,15 +578,180 @@ void otPlatCryptoRandomDeinit(void); /** * Fills a given buffer with cryptographically secure random bytes. * - * @param[out] aBuffer A pointer to a buffer to fill with the random bytes. - * @param[in] aSize Size of buffer (number of bytes to fill). + * @param[out] aBuffer A pointer to a buffer to fill with the random bytes. + * @param[in] aSize Size of buffer (number of bytes to fill). * - * @retval OT_ERROR_NONE Successfully filled buffer with random values. - * @retval OT_ERROR_FAILED Operation failed. + * @retval OT_ERROR_NONE Successfully filled buffer with random values. + * @retval OT_ERROR_FAILED Operation failed. * */ otError otPlatCryptoRandomGet(uint8_t *aBuffer, uint16_t aSize); +/** + * Generate and populate the output buffer with a new ECDSA key-pair. + * + * @param[out] aKeyPair A pointer to an ECDSA key-pair structure to store the generated key-pair. + * + * @retval OT_ERROR_NONE A new key-pair was generated successfully. + * @retval OT_ERROR_NO_BUFS Failed to allocate buffer for key generation. + * @retval OT_ERROR_NOT_CAPABLE Feature not supported. + * @retval OT_ERROR_FAILED Failed to generate key-pair. + * + */ +otError otPlatCryptoEcdsaGenerateKey(otPlatCryptoEcdsaKeyPair *aKeyPair); + +/** + * Get the associated public key from the input context. + * + * @param[in] aKeyPair A pointer to an ECDSA key-pair structure where the key-pair is stored. + * @param[out] aPublicKey A pointer to an ECDSA public key structure to store the public key. + * + * @retval OT_ERROR_NONE Public key was retrieved successfully, and @p aBuffer is updated. + * @retval OT_ERROR_PARSE The key-pair DER format could not be parsed (invalid format). + * @retval OT_ERROR_INVALID_ARGS The @p aContext is NULL. + * + */ +otError otPlatCryptoEcdsaGetPublicKey(const otPlatCryptoEcdsaKeyPair *aKeyPair, otPlatCryptoEcdsaPublicKey *aPublicKey); + +/** + * Calculate the ECDSA signature for a hashed message using the private key from the input context. + * + * This method uses the deterministic digital signature generation procedure from RFC 6979. + * + * @param[in] aKeyPair A pointer to an ECDSA key-pair structure where the key-pair is stored. + * @param[in] aHash A pointer to a SHA-256 hash structure where the hash value for signature calculation + * is stored. + * @param[out] aSignature A pointer to an ECDSA signature structure to output the calculated signature. + * + * @retval OT_ERROR_NONE The signature was calculated successfully, @p aSignature was updated. + * @retval OT_ERROR_PARSE The key-pair DER format could not be parsed (invalid format). + * @retval OT_ERROR_NO_BUFS Failed to allocate buffer for signature calculation. + * @retval OT_ERROR_INVALID_ARGS The @p aContext is NULL. + * + */ +otError otPlatCryptoEcdsaSign(const otPlatCryptoEcdsaKeyPair *aKeyPair, + const otPlatCryptoSha256Hash *aHash, + otPlatCryptoEcdsaSignature *aSignature); + +/** + * Use the key from the input context to verify the ECDSA signature of a hashed message. + * + * @param[in] aPublicKey A pointer to an ECDSA public key structure where the public key for signature + * verification is stored. + * @param[in] aHash A pointer to a SHA-256 hash structure where the hash value for signature verification + * is stored. + * @param[in] aSignature A pointer to an ECDSA signature structure where the signature value to be verified is + * stored. + * + * @retval OT_ERROR_NONE The signature was verified successfully. + * @retval OT_ERROR_SECURITY The signature is invalid. + * @retval OT_ERROR_INVALID_ARGS The key or hash is invalid. + * @retval OT_ERROR_NO_BUFS Failed to allocate buffer for signature verification. + * + */ +otError otPlatCryptoEcdsaVerify(const otPlatCryptoEcdsaPublicKey *aPublicKey, + const otPlatCryptoSha256Hash *aHash, + const otPlatCryptoEcdsaSignature *aSignature); + +/** + * Calculate the ECDSA signature for a hashed message using the Key reference passed. + * + * This method uses the deterministic digital signature generation procedure from RFC 6979. + * + * @param[in] aKeyRef Key Reference to the slot where the key-pair is stored. + * @param[in] aHash A pointer to a SHA-256 hash structure where the hash value for signature calculation + * is stored. + * @param[out] aSignature A pointer to an ECDSA signature structure to output the calculated signature. + * + * @retval OT_ERROR_NONE The signature was calculated successfully, @p aSignature was updated. + * @retval OT_ERROR_PARSE The key-pair DER format could not be parsed (invalid format). + * @retval OT_ERROR_NO_BUFS Failed to allocate buffer for signature calculation. + * @retval OT_ERROR_INVALID_ARGS The @p aContext is NULL. + * + * @note This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled. + * + */ +otError otPlatCryptoEcdsaSignUsingKeyRef(otCryptoKeyRef aKeyRef, + const otPlatCryptoSha256Hash *aHash, + otPlatCryptoEcdsaSignature *aSignature); + +/** + * Get the associated public key from the key reference passed. + * + * The public key is stored differently depending on the crypto backend library being used + * (OPENTHREAD_CONFIG_CRYPTO_LIB). + * + * This API must make sure to return the public key as a byte sequence representation of an + * uncompressed curve point (RFC 6605 - sec 4) + * + * @param[in] aKeyRef Key Reference to the slot where the key-pair is stored. + * @param[out] aPublicKey A pointer to an ECDSA public key structure to store the public key. + * + * @retval OT_ERROR_NONE Public key was retrieved successfully, and @p aBuffer is updated. + * @retval OT_ERROR_PARSE The key-pair DER format could not be parsed (invalid format). + * @retval OT_ERROR_INVALID_ARGS The @p aContext is NULL. + * + * @note This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled. + * + */ +otError otPlatCryptoEcdsaExportPublicKey(otCryptoKeyRef aKeyRef, otPlatCryptoEcdsaPublicKey *aPublicKey); + +/** + * Generate and import a new ECDSA key-pair at reference passed. + * + * @param[in] aKeyRef Key Reference to the slot where the key-pair is stored. + * + * @retval OT_ERROR_NONE A new key-pair was generated successfully. + * @retval OT_ERROR_NO_BUFS Failed to allocate buffer for key generation. + * @retval OT_ERROR_NOT_CAPABLE Feature not supported. + * @retval OT_ERROR_FAILED Failed to generate key-pair. + * + * @note This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled. + * + */ +otError otPlatCryptoEcdsaGenerateAndImportKey(otCryptoKeyRef aKeyRef); + +/** + * Use the keyref to verify the ECDSA signature of a hashed message. + * + * @param[in] aKeyRef Key Reference to the slot where the key-pair is stored. + * @param[in] aHash A pointer to a SHA-256 hash structure where the hash value for signature verification + * is stored. + * @param[in] aSignature A pointer to an ECDSA signature structure where the signature value to be verified is + * stored. + * + * @retval OT_ERROR_NONE The signature was verified successfully. + * @retval OT_ERROR_SECURITY The signature is invalid. + * @retval OT_ERROR_INVALID_ARGS The key or hash is invalid. + * @retval OT_ERROR_NO_BUFS Failed to allocate buffer for signature verification. + * + * @note This API is only used by OT core when `OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is enabled. + * + */ +otError otPlatCryptoEcdsaVerifyUsingKeyRef(otCryptoKeyRef aKeyRef, + const otPlatCryptoSha256Hash *aHash, + const otPlatCryptoEcdsaSignature *aSignature); + +/** + * Perform PKCS#5 PBKDF2 using CMAC (AES-CMAC-PRF-128). + * + * @param[in] aPassword Password to use when generating key. + * @param[in] aPasswordLen Length of password. + * @param[in] aSalt Salt to use when generating key. + * @param[in] aSaltLen Length of salt. + * @param[in] aIterationCounter Iteration count. + * @param[in] aKeyLen Length of generated key in bytes. + * @param[out] aKey A pointer to the generated key. + * + */ +void otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword, + uint16_t aPasswordLen, + const uint8_t *aSalt, + uint16_t aSaltLen, + uint32_t aIterationCounter, + uint16_t aKeyLen, + uint8_t *aKey); + /** * @} * diff --git a/include/openthread/platform/diag.h b/include/openthread/platform/diag.h index 9dee346c284..6f65f8234fb 100644 --- a/include/openthread/platform/diag.h +++ b/include/openthread/platform/diag.h @@ -56,6 +56,16 @@ extern "C" { * */ +/** + * This enumeration defines the gpio modes. + * + */ +typedef enum +{ + OT_GPIO_MODE_INPUT = 0, ///< Input mode without pull resistor. + OT_GPIO_MODE_OUTPUT = 1, ///< Output mode. +} otGpioMode; + /** * This function processes a factory diagnostics command line. * @@ -75,8 +85,8 @@ extern "C" { */ otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, - char * aArgs[], - char * aOutput, + char *aArgs[], + char *aOutput, size_t aOutputMaxLen); /** @@ -129,6 +139,165 @@ void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otErro */ void otPlatDiagAlarmCallback(otInstance *aInstance); +/** + * This function sets the gpio value. + * + * @param[in] aGpio The gpio number. + * @param[in] aValue true to set the gpio to high level, or false otherwise. + * + * @retval OT_ERROR_NONE Successfully set the gpio. + * @retval OT_ERROR_FAILED A platform error occurred while setting the gpio. + * @retval OT_ERROR_INVALID_ARGS @p aGpio is not supported. + * @retval OT_ERROR_INVALID_STATE Diagnostic mode was not enabled or @p aGpio is not configured as output. + * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented or configured on the platform. + * + */ +otError otPlatDiagGpioSet(uint32_t aGpio, bool aValue); + +/** + * This function gets the gpio value. + * + * @param[in] aGpio The gpio number. + * @param[out] aValue A pointer where to put gpio value. + * + * @retval OT_ERROR_NONE Successfully got the gpio value. + * @retval OT_ERROR_FAILED A platform error occurred while getting the gpio value. + * @retval OT_ERROR_INVALID_ARGS @p aGpio is not supported or @p aValue is NULL. + * @retval OT_ERROR_INVALID_STATE Diagnostic mode was not enabled or @p aGpio is not configured as input. + * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented or configured on the platform. + * + */ +otError otPlatDiagGpioGet(uint32_t aGpio, bool *aValue); + +/** + * This function sets the gpio mode. + * + * @param[in] aGpio The gpio number. + * @param[out] aMode The gpio mode. + * + * @retval OT_ERROR_NONE Successfully set the gpio mode. + * @retval OT_ERROR_FAILED A platform error occurred while setting the gpio mode. + * @retval OT_ERROR_INVALID_ARGS @p aGpio or @p aMode is not supported. + * @retval OT_ERROR_INVALID_STATE Diagnostic mode was not enabled. + * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented or configured on the platform. + * + */ +otError otPlatDiagGpioSetMode(uint32_t aGpio, otGpioMode aMode); + +/** + * This function gets the gpio mode. + * + * @param[in] aGpio The gpio number. + * @param[out] aMode A pointer where to put gpio mode. + * + * @retval OT_ERROR_NONE Successfully got the gpio mode. + * @retval OT_ERROR_FAILED Mode returned by the platform is not implemented in OpenThread or a platform error + * occurred while getting the gpio mode. + * @retval OT_ERROR_INVALID_ARGS @p aGpio is not supported or @p aMode is NULL. + * @retval OT_ERROR_INVALID_STATE Diagnostic mode was not enabled. + * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented or configured on the platform. + * + */ +otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode); + +/** + * Set the radio raw power setting for diagnostics module. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aRawPowerSetting A pointer to the raw power setting byte array. + * @param[in] aRawPowerSettingLength The length of the @p aRawPowerSetting. + * + * @retval OT_ERROR_NONE Successfully set the raw power setting. + * @retval OT_ERROR_INVALID_ARGS The @p aRawPowerSetting is NULL or the @p aRawPowerSettingLength is too long. + * @retval OT_ERROR_NOT_IMPLEMENTED This method is not implemented. + * + */ +otError otPlatDiagRadioSetRawPowerSetting(otInstance *aInstance, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength); + +/** + * Get the radio raw power setting for diagnostics module. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[out] aRawPowerSetting A pointer to the raw power setting byte array. + * @param[in,out] aRawPowerSettingLength On input, a pointer to the size of @p aRawPowerSetting. + * On output, a pointer to the length of the raw power setting data. + * + * @retval OT_ERROR_NONE Successfully set the raw power setting. + * @retval OT_ERROR_INVALID_ARGS The @p aRawPowerSetting or @p aRawPowerSettingLength is NULL or + * @aRawPowerSettingLength is too short. + * @retval OT_ERROR_NOT_FOUND The raw power setting is not set. + * @retval OT_ERROR_NOT_IMPLEMENTED This method is not implemented. + * + */ +otError otPlatDiagRadioGetRawPowerSetting(otInstance *aInstance, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength); + +/** + * Enable/disable the platform layer to use the raw power setting set by `otPlatDiagRadioSetRawPowerSetting()`. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aEnable TRUE to enable or FALSE to disable the raw power setting. + * + * @retval OT_ERROR_NONE Successfully enabled/disabled the raw power setting. + * @retval OT_ERROR_NOT_IMPLEMENTED This method is not implemented. + * + */ +otError otPlatDiagRadioRawPowerSettingEnable(otInstance *aInstance, bool aEnable); + +/** + * Start/stop the platform layer to transmit continuous carrier wave. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aEnable TRUE to enable or FALSE to disable the platform layer to transmit continuous carrier wave. + * + * @retval OT_ERROR_NONE Successfully enabled/disabled . + * @retval OT_ERROR_INVALID_STATE The radio was not in the Receive state. + * @retval OT_ERROR_NOT_IMPLEMENTED This method is not implemented. + * + */ +otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable); + +/** + * Start/stop the platform layer to transmit stream of characters. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aEnable TRUE to enable or FALSE to disable the platform layer to transmit stream. + * + * @retval OT_ERROR_NONE Successfully enabled/disabled. + * @retval OT_ERROR_INVALID_STATE The radio was not in the Receive state. + * @retval OT_ERROR_NOT_IMPLEMENTED This function is not implemented. + * + */ +otError otPlatDiagRadioTransmitStream(otInstance *aInstance, bool aEnable); + +/** + * Get the power settings for the given channel. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aChannel The radio channel. + * @param[out] aTargetPower The target power in 0.01 dBm. + * @param[out] aActualPower The actual power in 0.01 dBm. + * @param[out] aRawPowerSetting A pointer to the raw power setting byte array. + * @param[in,out] aRawPowerSettingLength On input, a pointer to the size of @p aRawPowerSetting. + * On output, a pointer to the length of the raw power setting data. + * + * @retval OT_ERROR_NONE Successfully got the target power. + * @retval OT_ERROR_INVALID_ARGS The @p aChannel is invalid, @aTargetPower, @p aActualPower, @p aRawPowerSetting or + * @p aRawPowerSettingLength is NULL or @aRawPowerSettingLength is too short. + * @retval OT_ERROR_NOT_FOUND The power settings for the @p aChannel was not found. + * @retval OT_ERROR_NOT_IMPLEMENTED This method is not implemented. + * + */ +otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance, + uint8_t aChannel, + int16_t *aTargetPower, + int16_t *aActualPower, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength); + /** * @} * diff --git a/include/openthread/platform/dns.h b/include/openthread/platform/dns.h new file mode 100644 index 00000000000..9e84fbe43f3 --- /dev/null +++ b/include/openthread/platform/dns.h @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2023, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * @brief + * This file defines the platform DNS interface. + * + */ + +#ifndef OPENTHREAD_PLATFORM_DNS_H_ +#define OPENTHREAD_PLATFORM_DNS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup plat-dns + * + * @brief + * This module includes the platform abstraction for sending recursive DNS query to upstream DNS servers. + * + * @{ + * + */ + +/** + * This opaque type represents an upstream DNS query transaction. + * + */ +typedef struct otPlatDnsUpstreamQuery otPlatDnsUpstreamQuery; + +/** + * Starts an upstream query transaction. + * + * - In success case (and errors represented by DNS protocol messages), the platform is expected to call + * `otPlatDnsUpstreamQueryDone`. + * - The OpenThread core may cancel a (possibly timeout) query transaction by calling + * `otPlatDnsCancelUpstreamQuery`, the platform must not call `otPlatDnsUpstreamQueryDone` on a + * cancelled transaction. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aTxn A pointer to the opaque DNS query transaction object. + * @param[in] aQuery A message buffer of the DNS payload that should be sent to upstream DNS server. + * + */ +void otPlatDnsStartUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn, const otMessage *aQuery); + +/** + * Cancels a transaction of upstream query. + * + * The platform must call `otPlatDnsUpstreamQueryDone` to release the resources. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aTxn A pointer to the opaque DNS query transaction object. + * + */ +void otPlatDnsCancelUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn); + +/** + * The platform calls this function to finish DNS query. + * + * The transaction will be released, so the platform must not call on the same transaction twice. This function passes + * the ownership of `aResponse` to OpenThread stack. + * + * Platform can pass a nullptr to close a transaction without a response. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aTxn A pointer to the opaque DNS query transaction object. + * @param[in] aResponse A message buffer of the DNS response payload or `nullptr` to close a transaction without a + * response. + * + */ +extern void otPlatDnsUpstreamQueryDone(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn, otMessage *aResponse); + +/** + * @} + * + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/openthread/platform/infra_if.h b/include/openthread/platform/infra_if.h index 9242213ef92..5d2c50f1504 100644 --- a/include/openthread/platform/infra_if.h +++ b/include/openthread/platform/infra_if.h @@ -87,7 +87,7 @@ bool otPlatInfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddres */ otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, const otIp6Address *aDestAddress, - const uint8_t * aBuffer, + const uint8_t *aBuffer, uint16_t aBufferLength); /** @@ -106,10 +106,10 @@ otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, * address and the IP Hop Limit MUST be 255. * */ -extern void otPlatInfraIfRecvIcmp6Nd(otInstance * aInstance, +extern void otPlatInfraIfRecvIcmp6Nd(otInstance *aInstance, uint32_t aInfraIfIndex, const otIp6Address *aSrcAddress, - const uint8_t * aBuffer, + const uint8_t *aBuffer, uint16_t aBufferLength); /** @@ -133,6 +133,35 @@ extern void otPlatInfraIfRecvIcmp6Nd(otInstance * aInstance, */ extern otError otPlatInfraIfStateChanged(otInstance *aInstance, uint32_t aInfraIfIndex, bool aIsRunning); +/** + * Send a request to discover the NAT64 prefix on the infrastructure interface with @p aInfraIfIndex. + * + * OpenThread will call this method periodically to monitor the presence or change of NAT64 prefix. + * + * @param[in] aInfraIfIndex The index of the infrastructure interface to discover the NAT64 prefix. + * + * @retval OT_ERROR_NONE Successfully request NAT64 prefix discovery. + * @retval OT_ERROR_FAILED Failed to request NAT64 prefix discovery. + * + */ +otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex); + +/** + * The infra interface driver calls this method to notify OpenThread that + * the discovery of NAT64 prefix is done. + * + * This method is expected to be invoked after calling otPlatInfraIfDiscoverNat64Prefix. + * If no NAT64 prefix is discovered, @p aIp6Prefix shall point to an empty prefix with zero length. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aInfraIfIndex The index of the infrastructure interface on which the NAT64 prefix is discovered. + * @param[in] aIp6Prefix A pointer to NAT64 prefix. + * + */ +extern void otPlatInfraIfDiscoverNat64PrefixDone(otInstance *aInstance, + uint32_t aInfraIfIndex, + const otIp6Prefix *aIp6Prefix); + /** * @} * diff --git a/include/openthread/platform/radio.h b/include/openthread/platform/radio.h index b95acd54c9f..89e17be88c3 100644 --- a/include/openthread/platform/radio.h +++ b/include/openthread/platform/radio.h @@ -68,13 +68,20 @@ extern "C" { enum { - OT_RADIO_FRAME_MAX_SIZE = 127, ///< aMaxPHYPacketSize (IEEE 802.15.4-2006) - OT_RADIO_FRAME_MIN_SIZE = 3, ///< Minimal size of frame FCS + CONTROL + OT_RADIO_FRAME_MAX_SIZE = 127, ///< aMaxPHYPacketSize (IEEE 802.15.4-2006) + OT_RADIO_FRAME_MIN_SIZE = 3, ///< Minimal size of frame FCS + CONTROL + OT_RADIO_SYMBOLS_PER_OCTET = 2, ///< 2.4 GHz IEEE 802.15.4-2006 OT_RADIO_BIT_RATE = 250000, ///< 2.4 GHz IEEE 802.15.4 (bits per second) OT_RADIO_BITS_PER_OCTET = 8, ///< Number of bits per octet - OT_RADIO_SYMBOL_TIME = ((OT_RADIO_BITS_PER_OCTET / OT_RADIO_SYMBOLS_PER_OCTET) * 1000000) / OT_RADIO_BIT_RATE, + // Per IEEE 802.15.4-2015, 12.3.3 Symbol rate: + // The O-QPSK PHY symbol rate shall be 25 ksymbol/s when operating in the 868 MHz band and 62.5 ksymbol/s when + // operating in the 780 MHz, 915 MHz, 2380 MHz, or 2450 MHz band + OT_RADIO_SYMBOL_RATE = 62500, ///< The O-QPSK PHY symbol rate when operating in the 780MHz, 915MHz, 2380MHz, 2450MHz + OT_RADIO_SYMBOL_TIME = 1000000 * 1 / OT_RADIO_SYMBOL_RATE, ///< Symbol duration time in unit of microseconds + OT_RADIO_TEN_SYMBOLS_TIME = 10 * OT_RADIO_SYMBOL_TIME, ///< Time for 10 symbols in unit of microseconds + OT_RADIO_LQI_NONE = 0, ///< LQI measurement not supported OT_RADIO_RSSI_INVALID = 127, ///< Invalid or unknown RSSI value OT_RADIO_POWER_INVALID = 127, ///< Invalid or unknown power value @@ -264,12 +271,33 @@ typedef struct otRadioFrame struct { const otMacKeyMaterial *mAesKey; ///< The key material used for AES-CCM frame security. - otRadioIeInfo * mIeInfo; ///< The pointer to the Header IE(s) related information. + otRadioIeInfo *mIeInfo; ///< The pointer to the Header IE(s) related information. uint32_t mTxDelay; ///< The delay time for this transmission (based on `mTxDelayBaseTime`). uint32_t mTxDelayBaseTime; ///< The base time for the transmission delay. uint8_t mMaxCsmaBackoffs; ///< Maximum number of backoffs attempts before declaring CCA failure. uint8_t mMaxFrameRetries; ///< Maximum number of retries allowed after a transmission failure. + /** + * The RX channel after frame TX is done (after all frame retries - ack received, or timeout, or abort). + * + * Radio platforms can choose to fully ignore this. OT stack will make sure to call `otPlatRadioReceive()` + * with the desired RX channel after a frame TX is done and signaled in `otPlatRadioTxDone()` callback. + * Radio platforms that don't provide `OT_RADIO_CAPS_TRANSMIT_RETRIES` must always ignore this. + * + * This is intended for situations where there may be delay in interactions between OT stack and radio, as + * an example this is used in RCP/host architecture to make sure RCP switches to PAN channel more quickly. + * In particular, this can help with CSL tx to a sleepy child, where the child may use a different channel + * for CSL than the PAN channel. After frame tx, we want the radio/RCP to go back to the PAN channel + * quickly to ensure that parent does not miss tx from child afterwards, e.g., child responding to the + * earlier CSL transmitted frame from parent using PAN channel while radio still staying on CSL channel. + * + * The switch to the RX channel MUST happen after the frame TX is fully done, i.e., after all retries and + * when ack is received (when "Ack Request" flag is set on the TX frame) or ack timeout. Note that ack is + * expected on the same channel that frame is sent on. + * + */ + uint8_t mRxChannelAfterTxDone; + /** * Indicates whether frame counter and CSL IEs are properly updated in the header. * @@ -306,8 +334,7 @@ typedef struct otRadioFrame /** * The timestamp when the frame was received in microseconds. * - * The value SHALL be the time when the SFD was received when TIME_SYNC or CSL is enabled. - * Otherwise, the time when the MAC frame was fully received is also acceptable. + * The value SHALL be the time when the SFD was received. * */ uint64_t mTimestamp; @@ -536,7 +563,7 @@ otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t *aT otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aThreshold); /** - * Get the external FEM's Rx LNA gain in dBm. + * Gets the external FEM's Rx LNA gain in dBm. * * @param[in] aInstance The OpenThread instance structure. * @param[out] aGain The external FEM's Rx LNA gain in dBm. @@ -549,7 +576,7 @@ otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aTh otError otPlatRadioGetFemLnaGain(otInstance *aInstance, int8_t *aGain); /** - * Set the external FEM's Rx LNA gain in dBm. + * Sets the external FEM's Rx LNA gain in dBm. * * @param[in] aInstance The OpenThread instance structure. * @param[in] aGain The external FEM's Rx LNA gain in dBm. @@ -594,7 +621,7 @@ void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable); * @param[in] aKeyType Key Type used. * */ -void otPlatRadioSetMacKey(otInstance * aInstance, +void otPlatRadioSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKeyId, const otMacKeyMaterial *aPrevKey, @@ -613,6 +640,17 @@ void otPlatRadioSetMacKey(otInstance * aInstance, */ void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter); +/** + * This method sets the current MAC frame counter value only if the new given value is larger than the current value. + * + * This function is used when radio provides `OT_RADIO_CAPS_TRANSMIT_SEC` capability. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aMacFrameCounter The MAC frame counter value. + * + */ +void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacFrameCounter); + /** * Get the current estimated time (in microseconds) of the radio chip. * @@ -969,7 +1007,7 @@ void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance); uint32_t otPlatRadioGetSupportedChannelMask(otInstance *aInstance); /** - * Get the radio preferred channel mask that the device prefers to form on. + * Gets the radio preferred channel mask that the device prefers to form on. * * @param[in] aInstance The OpenThread instance structure. * @@ -1032,7 +1070,7 @@ otError otPlatRadioGetCoexMetrics(otInstance *aInstance, otRadioCoexMetrics *aCo * @retval kErrorNone Successfully enabled or disabled CSL. * */ -otError otPlatRadioEnableCsl(otInstance * aInstance, +otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, otShortAddress aShortAddr, const otExtAddress *aExtAddr); @@ -1096,6 +1134,7 @@ otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aCh * * @retval OT_ERROR_FAILED Other platform specific errors. * @retval OT_ERROR_NONE Successfully set region code. + * @retval OT_ERROR_NOT_IMPLEMENTED The feature is not implemented. * */ otError otPlatRadioSetRegion(otInstance *aInstance, uint16_t aRegionCode); @@ -1112,6 +1151,7 @@ otError otPlatRadioSetRegion(otInstance *aInstance, uint16_t aRegionCode); * @retval OT_ERROR_INVALID_ARGS @p aRegionCode is nullptr. * @retval OT_ERROR_FAILED Other platform specific errors. * @retval OT_ERROR_NONE Successfully got region code. + * @retval OT_ERROR_NOT_IMPLEMENTED The feature is not implemented. * */ otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode); @@ -1134,13 +1174,108 @@ otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode); * @retval OT_ERROR_INVALID_ARGS @p aExtAddress is `NULL`. * @retval OT_ERROR_NOT_FOUND The Initiator indicated by @p aShortAddress is not found when trying to clear. * @retval OT_ERROR_NO_BUFS No more Initiator can be supported. + * @retval OT_ERROR_NOT_IMPLEMENTED The feature is not implemented. * */ -otError otPlatRadioConfigureEnhAckProbing(otInstance * aInstance, +otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance, otLinkMetrics aLinkMetrics, otShortAddress aShortAddress, const otExtAddress *aExtAddress); +/** + * Add a calibrated power of the specified channel to the power calibration table. + * + * @note This API is an optional radio platform API. It's up to the platform layer to implement it. + * + * The @p aActualPower is the actual measured output power when the parameters of the radio hardware modules + * are set to the @p aRawPowerSetting. + * + * The raw power setting is an opaque byte array. OpenThread doesn't define the format of the raw power setting. + * Its format is radio hardware related and it should be defined by the developers in the platform radio driver. + * For example, if the radio hardware contains both the radio chip and the FEM chip, the raw power setting can be + * a combination of the radio power register and the FEM gain value. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aChannel The radio channel. + * @param[in] aActualPower The actual power in 0.01dBm. + * @param[in] aRawPowerSetting A pointer to the raw power setting byte array. + * @param[in] aRawPowerSettingLength The length of the @p aRawPowerSetting. + * + * @retval OT_ERROR_NONE Successfully added the calibrated power to the power calibration table. + * @retval OT_ERROR_NO_BUFS No available entry in the power calibration table. + * @retval OT_ERROR_INVALID_ARGS The @p aChannel, @p aActualPower or @p aRawPowerSetting is invalid or the + * @p aActualPower already exists in the power calibration table. + * @retval OT_ERROR_NOT_IMPLEMENTED This feature is not implemented. + * + */ +otError otPlatRadioAddCalibratedPower(otInstance *aInstance, + uint8_t aChannel, + int16_t aActualPower, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength); + +/** + * Clear all calibrated powers from the power calibration table. + * + * @note This API is an optional radio platform API. It's up to the platform layer to implement it. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @retval OT_ERROR_NONE Successfully cleared all calibrated powers from the power calibration table. + * @retval OT_ERROR_NOT_IMPLEMENTED This feature is not implemented. + * + */ +otError otPlatRadioClearCalibratedPowers(otInstance *aInstance); + +/** + * Set the target power for the given channel. + * + * @note This API is an optional radio platform API. It's up to the platform layer to implement it. + * If this API is implemented, the function `otPlatRadioSetTransmitPower()` should be disabled. + * + * The radio driver should set the actual output power to be less than or equal to the target power and as close + * as possible to the target power. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aChannel The radio channel. + * @param[in] aTargetPower The target power in 0.01dBm. Passing `INT16_MAX` will disable this channel to use the + * target power. + * + * @retval OT_ERROR_NONE Successfully set the target power. + * @retval OT_ERROR_INVALID_ARGS The @p aChannel or @p aTargetPower is invalid. + * @retval OT_ERROR_NOT_IMPLEMENTED The feature is not implemented. + * + */ +otError otPlatRadioSetChannelTargetPower(otInstance *aInstance, uint8_t aChannel, int16_t aTargetPower); + +/** + * Get the raw power setting for the given channel. + * + * @note OpenThread `src/core/utils` implements a default implementation of the API `otPlatRadioAddCalibratedPower()`, + * `otPlatRadioClearCalibratedPowers()` and `otPlatRadioSetChannelTargetPower()`. This API is provided by + * the default implementation to get the raw power setting for the given channel. If the platform doesn't + * use the default implementation, it can ignore this API. + * + * Platform radio layer should parse the raw power setting based on the radio layer defined format and set the + * parameters of each radio hardware module. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aChannel The radio channel. + * @param[out] aRawPowerSetting A pointer to the raw power setting byte array. + * @param[in,out] aRawPowerSettingLength On input, a pointer to the size of @p aRawPowerSetting. + * On output, a pointer to the length of the raw power setting data. + * + * @retval OT_ERROR_NONE Successfully got the target power. + * @retval OT_ERROR_INVALID_ARGS The @p aChannel is invalid, @p aRawPowerSetting or @p aRawPowerSettingLength is NULL + * or @aRawPowerSettingLength is too short. + * @retval OT_ERROR_NOT_FOUND The raw power setting for the @p aChannel was not found. + * + */ +extern otError otPlatRadioGetRawPowerSetting(otInstance *aInstance, + uint8_t aChannel, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength); + /** * @} * diff --git a/include/openthread/platform/settings.h b/include/openthread/platform/settings.h index 2a4892baa97..a9ca05de4e0 100644 --- a/include/openthread/platform/settings.h +++ b/include/openthread/platform/settings.h @@ -72,6 +72,8 @@ enum 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_BR_ULA_PREFIX = 0x000f, ///< BR ULA prefix. + OT_SETTINGS_KEY_BR_ON_LINK_PREFIXES = 0x0010, ///< BR local on-link prefixes. + OT_SETTINGS_KEY_BORDER_AGENT_ID = 0x0011, ///< Unique Border Agent/Router ID. // Deprecated and reserved key values: // diff --git a/include/openthread/platform/spi-slave.h b/include/openthread/platform/spi-slave.h index 3b846673e74..551d7016350 100644 --- a/include/openthread/platform/spi-slave.h +++ b/include/openthread/platform/spi-slave.h @@ -79,7 +79,7 @@ extern "C" { * @returns TRUE if after this call returns the platform should invoke the process callback `aProcessCallback`, * FALSE if there is nothing to process and no need to invoke the process callback. */ -typedef bool (*otPlatSpiSlaveTransactionCompleteCallback)(void * aContext, +typedef bool (*otPlatSpiSlaveTransactionCompleteCallback)(void *aContext, uint8_t *aOutputBuf, uint16_t aOutputBufLen, uint8_t *aInputBuf, @@ -115,7 +115,7 @@ typedef void (*otPlatSpiSlaveTransactionProcessCallback)(void *aContext); */ otError otPlatSpiSlaveEnable(otPlatSpiSlaveTransactionCompleteCallback aCompleteCallback, otPlatSpiSlaveTransactionProcessCallback aProcessCallback, - void * aContext); + void *aContext); /** * Shutdown and disable the SPI slave interface. diff --git a/include/openthread/platform/toolchain.h b/include/openthread/platform/toolchain.h index 56109852da0..d8b0308ef87 100644 --- a/include/openthread/platform/toolchain.h +++ b/include/openthread/platform/toolchain.h @@ -110,6 +110,24 @@ extern "C" { * */ +/** + * @def OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK + * + * This macro specifies that a function or method takes `printf` style arguments and should be type-checked against + * a format string. + * + * This macro must be added after the function/method declaration. For example: + * + * `void MyPrintf(void *aObject, const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3);` + * + * The two argument index values indicate format string and first argument to check against it. They start at index 1 + * for the first parameter in a function and at index 2 for the first parameter in a method. + * + * @param[in] aFmtIndex The argument index of the format string. + * @param[in] aStartIndex The argument index of the first argument to check against the format string. + * + */ + // =========== TOOLCHAIN SELECTION : START =========== #if defined(__GNUC__) || defined(__clang__) || defined(__CC_ARM) || defined(__TI_ARM__) @@ -122,6 +140,9 @@ extern "C" { #define OT_TOOL_PACKED_END __attribute__((packed)) #define OT_TOOL_WEAK __attribute__((weak)) +#define OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(aFmtIndex, aStartIndex) \ + __attribute__((format(printf, aFmtIndex, aStartIndex))) + #elif defined(__ICCARM__) || defined(__ICC8051__) // http://supp.iar.com/FilesPublic/UPDINFO/004916/arm/doc/EWARM_DevelopmentGuide.ENU.pdf @@ -133,6 +154,8 @@ extern "C" { #define OT_TOOL_PACKED_END #define OT_TOOL_WEAK __weak +#define OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(aFmtIndex, aStartIndex) + #elif defined(__SDCC) // Structures are packed by default in sdcc, as it primarily targets 8-bit MCUs. @@ -142,6 +165,8 @@ extern "C" { #define OT_TOOL_PACKED_END #define OT_TOOL_WEAK +#define OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(aFmtIndex, aStartIndex) + #else #error "Error: No valid Toolchain specified" @@ -153,6 +178,8 @@ extern "C" { #define OT_TOOL_PACKED_END #define OT_TOOL_WEAK +#define OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(aFmtIndex, aStartIndex) + #endif // =========== TOOLCHAIN SELECTION : END =========== diff --git a/include/openthread/platform/trel.h b/include/openthread/platform/trel.h index d4e0475a60a..6515b98d591 100644 --- a/include/openthread/platform/trel.h +++ b/include/openthread/platform/trel.h @@ -178,8 +178,8 @@ void otPlatTrelRegisterService(otInstance *aInstance, uint16_t aPort, const uint * @param[in] aDestSockAddr The destination socket address. * */ -void otPlatTrelSend(otInstance * aInstance, - const uint8_t * aUdpPayload, +void otPlatTrelSend(otInstance *aInstance, + const uint8_t *aUdpPayload, uint16_t aUdpPayloadLen, const otSockAddr *aDestSockAddr); diff --git a/include/openthread/platform/udp.h b/include/openthread/platform/udp.h index 345f1596297..b1dae45c799 100644 --- a/include/openthread/platform/udp.h +++ b/include/openthread/platform/udp.h @@ -68,7 +68,7 @@ otError otPlatUdpClose(otUdpSocket *aUdpSocket); * * @param[in] aUdpSocket A pointer to the UDP socket. * - * @retval OT_ERROR_NONE Successfully binded UDP socket by platform. + * @retval OT_ERROR_NONE Successfully bound UDP socket by platform. * @retval OT_ERROR_FAILED Failed to bind UDP socket. * */ @@ -107,7 +107,7 @@ otError otPlatUdpConnect(otUdpSocket *aUdpSocket); * @param[in] aMessageInfo A pointer to the message info associated with @p aMessage. * * @retval OT_ERROR_NONE Successfully sent by platform, and @p aMessage is freed. - * @retval OT_ERROR_FAILED Failed to binded UDP socket. + * @retval OT_ERROR_FAILED Failed to bind UDP socket. * */ otError otPlatUdpSend(otUdpSocket *aUdpSocket, otMessage *aMessage, const otMessageInfo *aMessageInfo); @@ -125,7 +125,7 @@ otError otPlatUdpSend(otUdpSocket *aUdpSocket, otMessage *aMessage, const otMess * @retval OT_ERROR_FAILED Failed to join the multicast group. * */ -otError otPlatUdpJoinMulticastGroup(otUdpSocket * aUdpSocket, +otError otPlatUdpJoinMulticastGroup(otUdpSocket *aUdpSocket, otNetifIdentifier aNetifIdentifier, const otIp6Address *aAddress); @@ -142,7 +142,7 @@ otError otPlatUdpJoinMulticastGroup(otUdpSocket * aUdpSocket, * @retval OT_ERROR_FAILED Failed to leave the multicast group. * */ -otError otPlatUdpLeaveMulticastGroup(otUdpSocket * aUdpSocket, +otError otPlatUdpLeaveMulticastGroup(otUdpSocket *aUdpSocket, otNetifIdentifier aNetifIdentifier, const otIp6Address *aAddress); diff --git a/include/openthread/server.h b/include/openthread/server.h index 0d177c70127..3ce11170523 100644 --- a/include/openthread/server.h +++ b/include/openthread/server.h @@ -94,7 +94,7 @@ otError otServerAddService(otInstance *aInstance, const otServiceConfig *aConfig * @sa otServerRegister * */ -otError otServerRemoveService(otInstance * aInstance, +otError otServerRemoveService(otInstance *aInstance, uint32_t aEnterpriseNumber, const uint8_t *aServiceData, uint8_t aServiceDataLength); diff --git a/include/openthread/sntp.h b/include/openthread/sntp.h index ca61f0a599a..34b26c28c67 100644 --- a/include/openthread/sntp.h +++ b/include/openthread/sntp.h @@ -93,10 +93,10 @@ typedef void (*otSntpResponseHandler)(void *aContext, uint64_t aTime, otError aR * @param[in] aContext A pointer to arbitrary context information. * */ -otError otSntpClientQuery(otInstance * aInstance, - const otSntpQuery * aQuery, +otError otSntpClientQuery(otInstance *aInstance, + const otSntpQuery *aQuery, otSntpResponseHandler aHandler, - void * aContext); + void *aContext); /** * This function sets the unix era number. diff --git a/include/openthread/srp_client.h b/include/openthread/srp_client.h index bb291d414dd..beaa062250a 100644 --- a/include/openthread/srp_client.h +++ b/include/openthread/srp_client.h @@ -74,8 +74,8 @@ typedef enum */ typedef struct otSrpClientHostInfo { - const char * mName; ///< Host name (label) string (NULL if not yet set). - const otIp6Address * mAddresses; ///< Array of host IPv6 addresses (NULL if not set or auto address is enabled). + const char *mName; ///< Host name (label) string (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. @@ -88,28 +88,34 @@ typedef struct otSrpClientHostInfo * and stay constant after an instance of this structure is passed to OpenThread from `otSrpClientAddService()` or * `otSrpClientRemoveService()`. * + * The `mState`, `mData`, `mNext` fields are used/managed by OT core only. Their value is ignored when an instance of + * `otSrpClientService` is passed in `otSrpClientAddService()` or `otSrpClientRemoveService()` or other functions. The + * caller does not need to set these fields. + * + * The `mLease` and `mKeyLease` fields specify the desired lease and key lease intervals for this service. Zero value + * indicates that the interval is unspecified and then the default lease or key lease intervals from + * `otSrpClientGetLeaseInterval()` and `otSrpClientGetKeyLeaseInterval()` are used for this service. If the key lease + * interval (whether set explicitly or determined from the default) is shorter than the lease interval for a service, + * SRP client will re-use the lease interval value for key lease interval as well. For example, if in service `mLease` + * is explicitly set to 2 days and `mKeyLease` is set to zero and default key lease is set to 1 day, then when + * registering this service, the requested key lease for this service is also set to 2 days. + * */ typedef struct otSrpClientService { - const char * mName; ///< The service name labels (e.g., "_chip._udp", not the full domain name). - const char * mInstanceName; ///< The service instance name label (not the full name). - const char *const * mSubTypeLabels; ///< Array of service sub-type labels (must end with `NULL` or can be `NULL`). - const otDnsTxtEntry *mTxtEntries; ///< Array of TXT entries (number of entries is given by `mNumTxtEntries`). - uint16_t mPort; ///< The service port number. - uint16_t mPriority; ///< The service priority. - uint16_t mWeight; ///< The service weight. - uint8_t mNumTxtEntries; ///< Number of entries in the `mTxtEntries` array. - - /** - * @note The following fields are used/managed by OT core only. Their values do not matter and are ignored when an - * instance of `otSrpClientService` is passed in `otSrpClientAddService()` or `otSrpClientRemoveService()`. The - * user should not modify these fields. - * - */ - - otSrpClientItemState mState; ///< Service state (managed by OT core). - uint32_t mData; ///< Internal data (used by OT core). - struct otSrpClientService *mNext; ///< Pointer to next entry in a linked-list (managed by OT core). + const char *mName; ///< The service labels (e.g., "_mt._udp", not the full domain name). + const char *mInstanceName; ///< The service instance name label (not the full name). + const char *const *mSubTypeLabels; ///< Array of sub-type labels (must end with `NULL` or can be `NULL`). + const otDnsTxtEntry *mTxtEntries; ///< Array of TXT entries (`mNumTxtEntries` gives num of entries). + uint16_t mPort; ///< The service port number. + uint16_t mPriority; ///< The service priority. + uint16_t mWeight; ///< The service weight. + uint8_t mNumTxtEntries; ///< Number of entries in the `mTxtEntries` array. + otSrpClientItemState mState; ///< Service state (managed by OT core). + uint32_t mData; ///< Internal data (used by OT core). + struct otSrpClientService *mNext; ///< Pointer to next entry in a linked-list (managed by OT core). + uint32_t mLease; ///< Desired lease interval in sec - zero to use default. + uint32_t mKeyLease; ///< Desired key lease interval in sec - zero to use default. } otSrpClientService; /** @@ -169,9 +175,9 @@ typedef struct otSrpClientService */ typedef void (*otSrpClientCallback)(otError aError, const otSrpClientHostInfo *aHostInfo, - const otSrpClientService * aServices, - const otSrpClientService * aRemovedServices, - void * aContext); + const otSrpClientService *aServices, + const otSrpClientService *aRemovedServices, + void *aContext); /** * This function pointer type defines the callback used by SRP client to notify user when it is auto-started or stopped. @@ -270,12 +276,26 @@ void otSrpClientSetCallback(otInstance *aInstance, otSrpClientCallback aCallback * Config option `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE` specifies the default auto-start mode (whether * it is enabled or disabled at the start of OT stack). * - * When auto-start is enabled, the SRP client will monitor the Thread Network Data for SRP Server Service entries - * and automatically start and stop the client when an SRP server is detected. + * When auto-start is enabled, the SRP client will monitor the Thread Network Data to discover SRP servers and select + * the preferred server and automatically start and stop the client when an SRP server is detected. + * + * There are three categories of Network Data entries indicating presence of SRP sever. They are preferred in the + * following order: + * + * 1) Preferred unicast entries where server address is included in the service data. If there are multiple options, + * the one with numerically lowest IPv6 address is preferred. + * + * 2) Anycast entries each having a seq number. A larger sequence number in the sense specified by Serial Number + * Arithmetic logic in RFC-1982 is considered more recent and therefore preferred. The largest seq number using + * serial number arithmetic is preferred if it is well-defined (i.e., the seq number is larger than all other + * seq numbers). If it is not well-defined, then the numerically largest seq number is preferred. * - * If multiple SRP servers are found, a random one will be selected. If the selected SRP server is no longer - * detected (not longer present in the Thread Network Data), the SRP client will be stopped and then it may switch - * to another SRP server (if available). + * 3) Unicast entries where the server address info is included in server data. If there are multiple options, the + * one with numerically lowest IPv6 address is preferred. + * + * When there is a change in the Network Data entries, client will check that the currently selected server is still + * present in the Network Data and is still the preferred one. Otherwise the client will switch to the new preferred + * server or stop if there is none. * * When the SRP client is explicitly started through a successful call to `otSrpClientStart()`, the given SRP server * address in `otSrpClientStart()` will continue to be used regardless of the state of auto-start mode and whether the @@ -345,7 +365,9 @@ uint32_t otSrpClientGetTtl(otInstance *aInstance); void otSrpClientSetTtl(otInstance *aInstance, uint32_t aTtl); /** - * This function gets the lease interval used in SRP update requests. + * This function gets the default lease interval used in SRP update requests. + * + * The default interval is used only for `otSrpClientService` instances with `mLease` set to zero. * * Note that this is the lease duration requested by the SRP client. The server may choose to accept a different lease * interval. @@ -358,7 +380,9 @@ void otSrpClientSetTtl(otInstance *aInstance, uint32_t aTtl); uint32_t otSrpClientGetLeaseInterval(otInstance *aInstance); /** - * This function sets the lease interval used in SRP update requests. + * This function sets the default lease interval used in SRP update requests. + * + * The default interval is used only for `otSrpClientService` instances with `mLease` set to zero. * * Changing the lease interval does not impact the accepted lease interval of already registered services/host-info. * It only affects any future SRP update messages (i.e., adding new services and/or refreshes of the existing services). @@ -371,7 +395,9 @@ uint32_t otSrpClientGetLeaseInterval(otInstance *aInstance); void otSrpClientSetLeaseInterval(otInstance *aInstance, uint32_t aInterval); /** - * This function gets the key lease interval used in SRP update requests. + * This function gets the default key lease interval used in SRP update requests. + * + * The default interval is used only for `otSrpClientService` instances with `mKeyLease` set to zero. * * Note that this is the lease duration requested by the SRP client. The server may choose to accept a different lease * interval. @@ -384,7 +410,9 @@ void otSrpClientSetLeaseInterval(otInstance *aInstance, uint32_t aInterval); uint32_t otSrpClientGetKeyLeaseInterval(otInstance *aInstance); /** - * This function sets the key lease interval used in SRP update requests. + * This function sets the default key lease interval used in SRP update requests. + * + * The default interval is used only for `otSrpClientService` instances with `mKeyLease` set to zero. * * Changing the lease interval does not impact the accepted lease interval of already registered services/host-info. * It only affects any future SRP update messages (i.e., adding new services and/or refreshes of existing services). diff --git a/include/openthread/srp_server.h b/include/openthread/srp_server.h index fba71749b03..c3dd36b2f76 100644 --- a/include/openthread/srp_server.h +++ b/include/openthread/srp_server.h @@ -132,14 +132,14 @@ enum }; /** - * Represents the state of an SRP server + * This enumeration represents the state of the SRP server. * */ typedef enum { OT_SRP_SERVER_STATE_DISABLED = 0, ///< The SRP server is disabled. - OT_SRP_SERVER_STATE_RUNNING = 1, ///< The SRP server is running. - OT_SRP_SERVER_STATE_STOPPED = 2, ///< The SRP server is stopped. + OT_SRP_SERVER_STATE_RUNNING = 1, ///< The SRP server is enabled and running. + OT_SRP_SERVER_STATE_STOPPED = 2, ///< The SRP server is enabled but stopped. } otSrpServerState; /** @@ -302,12 +302,49 @@ otError otSrpServerSetAnycastModeSequenceNumber(otInstance *aInstance, uint8_t a /** * This function enables/disables the SRP server. * + * On a Border Router, it is recommended to use `otSrpServerSetAutoEnableMode()` instead. + * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aEnabled A boolean to enable/disable the SRP server. * */ void otSrpServerSetEnabled(otInstance *aInstance, bool aEnabled); +/** + * This function enables/disables the auto-enable mode on SRP server. + * + * This function requires `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` feature. + * + * When this mode is enabled, the Border Routing Manager controls if/when to enable or disable the SRP server. + * SRP sever is auto-enabled if/when Border Routing is started and it is done with the initial prefix and route + * configurations (when the OMR and on-link prefixes are determined, advertised in emitted Router Advertisement message + * on infrastructure side and published in the Thread Network Data). The SRP server is auto-disabled if/when BR is + * stopped (e.g., if the infrastructure network interface is brought down or if BR gets detached). + * + * This mode can be disabled by a `otSrpServerSetAutoEnableMode()` call with @p aEnabled set to `false` or if the SRP + * server is explicitly enabled or disabled by a call to `otSrpServerSetEnabled()` function. Disabling auto-enable mode + * using `otSrpServerSetAutoEnableMode(false)` will not change the current state of SRP sever (e.g., if it is enabled + * it stays enabled). + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aEnabled A boolean to enable/disable the auto-enable mode. + * + */ +void otSrpServerSetAutoEnableMode(otInstance *aInstance, bool aEnabled); + +/** + * This function indicates whether the auto-enable mode is enabled or disabled. + * + * This function requires `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` feature. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @retval TRUE The auto-enable mode is enabled. + * @retval FALSE The auto-enable mode is disabled. + * + */ +bool otSrpServerIsAutoEnableMode(otInstance *aInstance); + /** * This function returns SRP server TTL configuration. * @@ -392,9 +429,9 @@ otError otSrpServerSetLeaseConfig(otInstance *aInstance, const otSrpServerLeaseC * */ typedef void (*otSrpServerServiceUpdateHandler)(otSrpServerServiceUpdateId aId, - const otSrpServerHost * aHost, + const otSrpServerHost *aHost, uint32_t aTimeout, - void * aContext); + void *aContext); /** * This function sets the SRP service updates handler on SRP server. @@ -405,9 +442,9 @@ typedef void (*otSrpServerServiceUpdateHandler)(otSrpServerServiceUpdateId aId, * May be NULL if not used. * */ -void otSrpServerSetServiceUpdateHandler(otInstance * aInstance, +void otSrpServerSetServiceUpdateHandler(otInstance *aInstance, otSrpServerServiceUpdateHandler aServiceHandler, - void * aContext); + void *aContext); /** * This function reports the result of processing a SRP update to the SRP server. @@ -500,7 +537,7 @@ void otSrpServerHostGetLeaseInfo(const otSrpServerHost *aHost, otSrpServerLeaseI * @returns A pointer to the next service or NULL if there is no more services. * */ -const otSrpServerService *otSrpServerHostGetNextService(const otSrpServerHost * aHost, +const otSrpServerService *otSrpServerHostGetNextService(const otSrpServerHost *aHost, const otSrpServerService *aService); /** @@ -535,11 +572,11 @@ const otSrpServerService *otSrpServerHostGetNextService(const otSrpServerHost * * @returns A pointer to the next matching service or NULL if no matching service could be found. * */ -const otSrpServerService *otSrpServerHostFindNextService(const otSrpServerHost * aHost, +const otSrpServerService *otSrpServerHostFindNextService(const otSrpServerHost *aHost, const otSrpServerService *aPrevService, otSrpServerServiceFlags aFlags, - const char * aServiceName, - const char * aInstanceName); + const char *aServiceName, + const char *aInstanceName); /** * This function indicates whether or not the SRP service has been deleted. diff --git a/include/openthread/tcp.h b/include/openthread/tcp.h index 6cc11f79495..3b63bc359c5 100644 --- a/include/openthread/tcp.h +++ b/include/openthread/tcp.h @@ -63,7 +63,7 @@ extern "C" { typedef struct otLinkedBuffer { struct otLinkedBuffer *mNext; ///< Pointer to the next linked buffer in the chain, or NULL if it is the end. - const uint8_t * mData; ///< Pointer to data referenced by this linked buffer. + const uint8_t *mData; ///< Pointer to data referenced by this linked buffer. size_t mLength; ///< Length of this linked buffer (number of bytes). } otLinkedBuffer; @@ -249,7 +249,7 @@ struct otTcpEndpoint } mTcb; struct otTcpEndpoint *mNext; ///< A pointer to the next TCP endpoint (internal use only) - void * mContext; ///< A pointer to application-specific context + void *mContext; ///< A pointer to application-specific context otTcpEstablished mEstablishedCallback; ///< "Established" callback function otTcpSendDone mSendDoneCallback; ///< "Send done" callback function @@ -279,7 +279,7 @@ typedef struct otTcpEndpointInitializeArgs otTcpReceiveAvailable mReceiveAvailableCallback; ///< "Receive available" callback function otTcpDisconnected mDisconnectedCallback; ///< "Disconnected" callback function - void * mReceiveBuffer; ///< Pointer to memory provided to the system for the TCP receive buffer + void *mReceiveBuffer; ///< Pointer to memory provided to the system for the TCP receive buffer size_t mReceiveBufferSize; ///< Size of memory provided to the system for the TCP receive buffer } otTcpEndpointInitializeArgs; @@ -294,7 +294,7 @@ typedef struct otTcpEndpointInitializeArgs * select a smaller buffer size. * */ -#define OT_TCP_RECEIVE_BUFFER_SIZE_FEW_HOPS 2599 +#define OT_TCP_RECEIVE_BUFFER_SIZE_FEW_HOPS 2598 /** * @def OT_TCP_RECEIVE_BUFFER_SIZE_MANY_HOPS @@ -306,7 +306,7 @@ typedef struct otTcpEndpointInitializeArgs * so), then it may be advisable to select a large buffer size manually. * */ -#define OT_TCP_RECEIVE_BUFFER_SIZE_MANY_HOPS 4158 +#define OT_TCP_RECEIVE_BUFFER_SIZE_MANY_HOPS 4157 /** * Initializes a TCP endpoint. @@ -325,8 +325,8 @@ typedef struct otTcpEndpointInitializeArgs * @retval OT_ERROR_FAILED Failed to open the TCP endpoint. * */ -otError otTcpEndpointInitialize(otInstance * aInstance, - otTcpEndpoint * aEndpoint, +otError otTcpEndpointInitialize(otInstance *aInstance, + otTcpEndpoint *aEndpoint, const otTcpEndpointInitializeArgs *aArgs); /** @@ -401,11 +401,11 @@ enum /** * Records the remote host and port for this connection. * - * By default TCP Fast Open is used. This means that this function merely - * records the remote host and port, and that the TCP connection establishment - * handshake only happens on the first call to otTcpSendByReference(). TCP Fast - * Open can be explicitly disabled using @p aFlags, in which case the TCP - * connection establishment handshake is initiated immediately. + * Caller must wait for `otTcpEstablished` callback indicating that TCP + * connection establishment handshake is done before it can start sending data + * e.g., calling `otTcpSendByReference()`. + * + * The TCP Fast Open is not yet supported and @p aFlags is ignored. * * @param[in] aEndpoint A pointer to the TCP endpoint structure to connect. * @param[in] aSockName The IP address and port of the host to which to connect. @@ -624,9 +624,9 @@ typedef enum otTcpIncomingConnectionAction * @returns Description of how to handle the incoming connection. * */ -typedef otTcpIncomingConnectionAction (*otTcpAcceptReady)(otTcpListener * aListener, +typedef otTcpIncomingConnectionAction (*otTcpAcceptReady)(otTcpListener *aListener, const otSockAddr *aPeer, - otTcpEndpoint ** aAcceptInto); + otTcpEndpoint **aAcceptInto); /** * This callback indicates that the TCP connection is now ready for two-way @@ -670,11 +670,11 @@ struct otTcpListener union { uint8_t mSize[OT_TCP_LISTENER_TCB_SIZE_BASE + OT_TCP_LISTENER_TCB_NUM_PTR * sizeof(void *)]; - void * mAlign; + void *mAlign; } mTcbListen; struct otTcpListener *mNext; ///< A pointer to the next TCP listener (internal use only) - void * mContext; ///< A pointer to application-specific context + void *mContext; ///< A pointer to application-specific context otTcpAcceptReady mAcceptReadyCallback; ///< "Accept ready" callback function otTcpAcceptDone mAcceptDoneCallback; ///< "Accept done" callback function @@ -709,8 +709,8 @@ typedef struct otTcpListenerInitializeArgs * @retval OT_ERROR_FAILED Failed to open the TCP listener. * */ -otError otTcpListenerInitialize(otInstance * aInstance, - otTcpListener * aListener, +otError otTcpListenerInitialize(otInstance *aInstance, + otTcpListener *aListener, const otTcpListenerInitializeArgs *aArgs); /** diff --git a/include/openthread/tcp_ext.h b/include/openthread/tcp_ext.h index 5c0ddc678e5..0258ac28bf8 100644 --- a/include/openthread/tcp_ext.h +++ b/include/openthread/tcp_ext.h @@ -88,10 +88,10 @@ extern "C" { */ typedef struct otTcpCircularSendBuffer { - const uint8_t *mDataBuffer; ///< Pointer to data in the circular send buffer - size_t mCapacity; ///< Length of the circular send buffer - size_t mStartIndex; ///< Index of the first valid byte in the send buffer - size_t mCapacityUsed; ///< Number of bytes stored in the send buffer + uint8_t *mDataBuffer; ///< Pointer to data in the circular send buffer + size_t mCapacity; ///< Length of the circular send buffer + size_t mStartIndex; ///< Index of the first valid byte in the send buffer + size_t mCapacityUsed; ///< Number of bytes stored in the send buffer otLinkedBuffer mSendLinks[2]; uint8_t mFirstSendLinkIndex; @@ -107,6 +107,15 @@ typedef struct otTcpCircularSendBuffer */ void otTcpCircularSendBufferInitialize(otTcpCircularSendBuffer *aSendBuffer, void *aDataBuffer, size_t aCapacity); +/** + * This enumeration defines flags passed to @p otTcpCircularSendBufferWrite. + * + */ +enum +{ + OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME = 1 << 0, +}; + /** * Sends out data on a TCP endpoint, using the provided TCP circular send * buffer to manage buffering. @@ -136,15 +145,17 @@ void otTcpCircularSendBufferInitialize(otTcpCircularSendBuffer *aSendBuffer, voi * @param[in] aLength The length of the data pointed to by @p aData to copy into the TCP circular send buffer. * @param[out] aWritten Populated with the amount of data copied into the send buffer, which might be less than * @p aLength if the send buffer reaches capacity. + * @param[in] aFlags Flags specifying options for this operation (see enumeration above). * - * @returns OT_ERROR_NONE Successfully copied data into the send buffer and sent it on the TCP endpoint. - * @returns OT_ERROR_FAILED Failed to send out data on the TCP endpoint. + * @retval OT_ERROR_NONE Successfully copied data into the send buffer and sent it on the TCP endpoint. + * @retval OT_ERROR_FAILED Failed to send out data on the TCP endpoint. */ -otError otTcpCircularSendBufferWrite(otTcpEndpoint * aEndpoint, +otError otTcpCircularSendBufferWrite(otTcpEndpoint *aEndpoint, otTcpCircularSendBuffer *aSendBuffer, - void * aData, + const void *aData, size_t aLength, - size_t * aWritten); + size_t *aWritten, + uint32_t aFlags); /** * Performs circular-send-buffer-specific handling in the otTcpForwardProgress @@ -173,9 +184,9 @@ void otTcpCircularSendBufferHandleForwardProgress(otTcpCircularSendBuffer *aSend * * @param[in] aSendBuffer A pointer to the TCP circular send buffer whose amount of free space to return. * - * @return The amount of free space in the send buffer. + * @returns The amount of free space in the send buffer. */ -size_t otTcpCircularSendBufferFreeSpace(otTcpCircularSendBuffer *aSendBuffer); +size_t otTcpCircularSendBufferGetFreeSpace(const otTcpCircularSendBuffer *aSendBuffer); /** * Forcibly discards all data in the circular send buffer. @@ -204,6 +215,37 @@ void otTcpCircularSendBufferForceDiscardAll(otTcpCircularSendBuffer *aSendBuffer */ otError otTcpCircularSendBufferDeinitialize(otTcpCircularSendBuffer *aSendBuffer); +/** + * Context structure to use with mbedtls_ssl_set_bio. + */ +typedef struct otTcpEndpointAndCircularSendBuffer +{ + otTcpEndpoint *mEndpoint; + otTcpCircularSendBuffer *mSendBuffer; +} otTcpEndpointAndCircularSendBuffer; + +/** + * Non-blocking send callback to pass to mbedtls_ssl_set_bio. + * + * @param[in] aCtx A pointer to an otTcpEndpointAndCircularSendBuffer. + * @param[in] aBuf The data to add to the send buffer. + * @param[in] aLen The amount of data to add to the send buffer. + * + * @returns The number of bytes sent, or an mbedtls error code. + */ +int otTcpMbedTlsSslSendCallback(void *aCtx, const unsigned char *aBuf, size_t aLen); + +/** + * Non-blocking receive callback to pass to mbedtls_ssl_set_bio. + * + * @param[in] aCtx A pointer to an otTcpEndpointAndCircularSendBuffer. + * @param[out] aBuf The buffer into which to receive data. + * @param[in] aLen The maximum amount of data that can be received. + * + * @returns The number of bytes received, or an mbedtls error code. + */ +int otTcpMbedTlsSslRecvCallback(void *aCtx, unsigned char *aBuf, size_t aLen); + /** * @} * diff --git a/include/openthread/thread.h b/include/openthread/thread.h index 04cb747f118..4bde02c140d 100644 --- a/include/openthread/thread.h +++ b/include/openthread/thread.h @@ -90,15 +90,18 @@ typedef struct otLinkModeConfig typedef struct { otExtAddress mExtAddress; ///< IEEE 802.15.4 Extended Address - uint32_t mAge; ///< Time last heard + uint32_t mAge; ///< Seconds since last heard + uint32_t mConnectionTime; ///< Seconds since link establishment (requires `CONFIG_UPTIME_ENABLE`) uint16_t mRloc16; ///< RLOC16 uint32_t mLinkFrameCounter; ///< Link Frame Counter uint32_t mMleFrameCounter; ///< MLE Frame Counter uint8_t mLinkQualityIn; ///< Link Quality In int8_t mAverageRssi; ///< Average RSSI int8_t mLastRssi; ///< Last observed RSSI + uint8_t mLinkMargin; ///< Link Margin uint16_t mFrameErrorRate; ///< Frame error rate (0xffff->100%). Requires error tracking feature. uint16_t mMessageErrorRate; ///< (IPv6) msg error rate (0xffff->100%). Requires error tracking feature. + uint16_t mVersion; ///< Thread version of the neighbor bool mRxOnWhenIdle : 1; ///< rx-on-when-idle bool mFullThreadDevice : 1; ///< Full Thread Device bool mFullNetworkData : 1; ///< Full Network Data @@ -138,6 +141,14 @@ typedef struct uint8_t mAge; ///< Time last heard bool mAllocated : 1; ///< Router ID allocated or not bool mLinkEstablished : 1; ///< Link established with Router ID or not + uint8_t mVersion; ///< Thread version + + /** + * Parent CSL parameters are only relevant when OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE is enabled. + * + */ + uint8_t mCslClockAccuracy; ///< CSL clock accuracy, in ± ppm + uint8_t mCslUncertainty; ///< CSL uncertainty, in ±10 us } otRouterInfo; /** @@ -167,6 +178,19 @@ typedef struct otMleCounters uint16_t mPartitionIdChanges; ///< Number of changes to partition ID. uint16_t mBetterPartitionAttachAttempts; ///< Number of attempts to attach to a better partition. + /** + * Role time tracking. + * + * When uptime feature is enabled (OPENTHREAD_CONFIG_UPTIME_ENABLE = 1) time spent in each MLE role is tracked. + * + */ + uint64_t mDisabledTime; ///< Number of milliseconds device has been in OT_DEVICE_ROLE_DISABLED role. + uint64_t mDetachedTime; ///< Number of milliseconds device has been in OT_DEVICE_ROLE_DETACHED role. + uint64_t mChildTime; ///< Number of milliseconds device has been in OT_DEVICE_ROLE_CHILD role. + uint64_t mRouterTime; ///< Number of milliseconds device has been in OT_DEVICE_ROLE_ROUTER role. + uint64_t mLeaderTime; ///< Number of milliseconds device has been in OT_DEVICE_ROLE_LEADER role. + uint64_t mTrackedTime; ///< Number of milliseconds tracked by previous counters. + /** * Number of times device changed its parent. * @@ -241,6 +265,8 @@ bool otThreadIsSingleton(otInstance *aInstance); /** * This function starts a Thread Discovery scan. * + * @note A successful call to this function enables the rx-on-when-idle mode for the entire scan procedure. + * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aScanChannels A bit vector indicating which channels to scan (e.g. OT_CHANNEL_11_MASK). * @param[in] aPanId The PAN ID filter (set to Broadcast PAN to disable filter). @@ -256,13 +282,13 @@ bool otThreadIsSingleton(otInstance *aInstance); * @retval OT_ERROR_BUSY Thread Discovery Scan is already in progress. * */ -otError otThreadDiscover(otInstance * aInstance, +otError otThreadDiscover(otInstance *aInstance, uint32_t aScanChannels, uint16_t aPanId, bool aJoiner, bool aEnableEui64Filtering, otHandleActiveScanResult aCallback, - void * aCallbackContext); + void *aCallbackContext); /** * This function determines if an MLE Thread Discovery is currently in progress. @@ -289,7 +315,7 @@ bool otThreadIsDiscoverInProgress(otInstance *aInstance); * @retval OT_ERROR_INVALID_ARGS Invalid AdvData. * */ -otError otThreadSetJoinerAdvertisement(otInstance * aInstance, +otError otThreadSetJoinerAdvertisement(otInstance *aInstance, uint32_t aOui, const uint8_t *aAdvData, uint8_t aAdvDataLength); @@ -297,7 +323,7 @@ otError otThreadSetJoinerAdvertisement(otInstance * aInstance, #define OT_JOINER_ADVDATA_MAX_LENGTH 64 ///< Maximum AdvData Length of Joiner Advertisement /** - * Get the Thread Child Timeout used when operating in the Child role. + * Gets the Thread Child Timeout (in seconds) used when operating in the Child role. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -309,7 +335,7 @@ otError otThreadSetJoinerAdvertisement(otInstance * aInstance, uint32_t otThreadGetChildTimeout(otInstance *aInstance); /** - * Set the Thread Child Timeout used when operating in the Child role. + * Sets the Thread Child Timeout (in seconds) used when operating in the Child role. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aTimeout The timeout value in seconds. @@ -320,7 +346,7 @@ uint32_t otThreadGetChildTimeout(otInstance *aInstance); void otThreadSetChildTimeout(otInstance *aInstance, uint32_t aTimeout); /** - * Get the IEEE 802.15.4 Extended PAN ID. + * Gets the IEEE 802.15.4 Extended PAN ID. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -332,9 +358,9 @@ void otThreadSetChildTimeout(otInstance *aInstance, uint32_t aTimeout); const otExtendedPanId *otThreadGetExtendedPanId(otInstance *aInstance); /** - * Set the IEEE 802.15.4 Extended PAN ID. + * Sets the IEEE 802.15.4 Extended PAN ID. * - * This function can only be called while Thread protocols are disabled. A successful + * @note Can only be called while Thread protocols are disabled. A successful * call to this function invalidates the Active and Pending Operational Datasets in * non-volatile memory. * @@ -391,7 +417,7 @@ otError otThreadSetLinkMode(otInstance *aInstance, otLinkModeConfig aConfig); * Get the Thread Network Key. * * @param[in] aInstance A pointer to an OpenThread instance. - * @param[out] aNetworkKey A pointer to an `otNetworkkey` to return the Thread Network Key. + * @param[out] aNetworkKey A pointer to an `otNetworkKey` to return the Thread Network Key. * * @sa otThreadSetNetworkKey * @@ -451,7 +477,7 @@ otError otThreadSetNetworkKey(otInstance *aInstance, const otNetworkKey *aKey); otError otThreadSetNetworkKeyRef(otInstance *aInstance, otNetworkKeyRef aKeyRef); /** - * This function returns a pointer to the Thread Routing Locator (RLOC) address. + * Gets the Thread Routing Locator (RLOC) address. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -461,7 +487,7 @@ otError otThreadSetNetworkKeyRef(otInstance *aInstance, otNetworkKeyRef aKeyRef) const otIp6Address *otThreadGetRloc(otInstance *aInstance); /** - * This function returns a pointer to the Mesh Local EID address. + * Gets the Mesh Local EID address. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -497,7 +523,7 @@ const otMeshLocalPrefix *otThreadGetMeshLocalPrefix(otInstance *aInstance); otError otThreadSetMeshLocalPrefix(otInstance *aInstance, const otMeshLocalPrefix *aMeshLocalPrefix); /** - * This function returns the Thread link-local IPv6 address. + * Gets the Thread link-local IPv6 address. * * The Thread link local address is derived using IEEE802.15.4 Extended Address as Interface Identifier. * @@ -509,9 +535,9 @@ otError otThreadSetMeshLocalPrefix(otInstance *aInstance, const otMeshLocalPrefi const otIp6Address *otThreadGetLinkLocalIp6Address(otInstance *aInstance); /** - * This function returns the Thread Link-Local All Thread Nodes multicast address. + * Gets the Thread Link-Local All Thread Nodes multicast address. * - * The address is a link-local Unicast Prefix-Based Multcast Address [RFC 3306], with: + * The address is a link-local Unicast Prefix-Based Multicast Address [RFC 3306], with: * - flgs set to 3 (P = 1 and T = 1) * - scop set to 2 * - plen set to 64 @@ -526,9 +552,9 @@ const otIp6Address *otThreadGetLinkLocalIp6Address(otInstance *aInstance); const otIp6Address *otThreadGetLinkLocalAllThreadNodesMulticastAddress(otInstance *aInstance); /** - * This function returns the Thread Realm-Local All Thread Nodes multicast address. + * Gets the Thread Realm-Local All Thread Nodes multicast address. * - * The address is a realm-local Unicast Prefix-Based Multcast Address [RFC 3306], with: + * The address is a realm-local Unicast Prefix-Based Multicast Address [RFC 3306], with: * - flgs set to 3 (P = 1 and T = 1) * - scop set to 3 * - plen set to 64 @@ -585,9 +611,9 @@ const char *otThreadGetNetworkName(otInstance *aInstance); otError otThreadSetNetworkName(otInstance *aInstance, const char *aNetworkName); /** - * Get the Thread Domain Name. + * Gets the Thread Domain Name. * - * This function is only available since Thread 1.2. + * @note Available since Thread 1.2. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -599,10 +625,9 @@ otError otThreadSetNetworkName(otInstance *aInstance, const char *aNetworkName); const char *otThreadGetDomainName(otInstance *aInstance); /** - * Set the Thread Domain Name. + * Sets the Thread Domain Name. Only succeeds when Thread protocols are disabled. * - * This function is only available since Thread 1.2. - * This function succeeds only when Thread protocols are disabled. + * @note Available since Thread 1.2. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aDomainName A pointer to the Thread Domain Name. @@ -616,9 +641,11 @@ const char *otThreadGetDomainName(otInstance *aInstance); otError otThreadSetDomainName(otInstance *aInstance, const char *aDomainName); /** - * Set/Clear the Interface Identifier manually specified for the Thread Domain Unicast Address. + * Sets or clears the Interface Identifier manually specified for the Thread Domain Unicast Address. * - * This function is only available since Thread 1.2 when `OPENTHREAD_CONFIG_DUA_ENABLE` is enabled. + * Available when `OPENTHREAD_CONFIG_DUA_ENABLE` is enabled. + * + * @note Only available since Thread 1.2. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aIid A pointer to the Interface Identifier to set or NULL to clear. @@ -631,9 +658,11 @@ otError otThreadSetDomainName(otInstance *aInstance, const char *aDomainName); otError otThreadSetFixedDuaInterfaceIdentifier(otInstance *aInstance, const otIp6InterfaceIdentifier *aIid); /** - * Get the Interface Identifier manually specified for the Thread Domain Unicast Address. + * Gets the Interface Identifier manually specified for the Thread Domain Unicast Address. + * + * Available when `OPENTHREAD_CONFIG_DUA_ENABLE` is enabled. * - * This function is only available since Thread 1.2 when `OPENTHREAD_CONFIG_DUA_ENABLE` is enabled. + * @note Only available since Thread 1.2. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -645,7 +674,7 @@ otError otThreadSetFixedDuaInterfaceIdentifier(otInstance *aInstance, const otIp const otIp6InterfaceIdentifier *otThreadGetFixedDuaInterfaceIdentifier(otInstance *aInstance); /** - * Get the thrKeySequenceCounter. + * Gets the thrKeySequenceCounter. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -657,7 +686,7 @@ const otIp6InterfaceIdentifier *otThreadGetFixedDuaInterfaceIdentifier(otInstanc uint32_t otThreadGetKeySequenceCounter(otInstance *aInstance); /** - * Set the thrKeySequenceCounter. + * Sets the thrKeySequenceCounter. * * @note This API is reserved for testing and demo purposes only. Changing settings with * this API will render a production application non-compliant with the Thread Specification. @@ -671,7 +700,7 @@ uint32_t otThreadGetKeySequenceCounter(otInstance *aInstance); void otThreadSetKeySequenceCounter(otInstance *aInstance, uint32_t aKeySequenceCounter); /** - * Get the thrKeySwitchGuardTime + * Gets the thrKeySwitchGuardTime (in hours). * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -683,7 +712,7 @@ void otThreadSetKeySequenceCounter(otInstance *aInstance, uint32_t aKeySequenceC uint32_t otThreadGetKeySwitchGuardTime(otInstance *aInstance); /** - * Set the thrKeySwitchGuardTime + * Sets the thrKeySwitchGuardTime (in hours). * * @note This API is reserved for testing and demo purposes only. Changing settings with * this API will render a production application non-compliant with the Thread Specification. @@ -845,7 +874,18 @@ otError otThreadGetParentAverageRssi(otInstance *aInstance, int8_t *aParentRssi) otError otThreadGetParentLastRssi(otInstance *aInstance, int8_t *aLastRssi); /** - * Get the IPv6 counters. + * Starts the process for child to search for a better parent while staying attached to its current parent. + * + * Must be used when device is attached as a child. + * + * @retval OT_ERROR_NONE Successfully started the process to search for a better parent. + * @retval OT_ERROR_INVALID_STATE Device role is not child. + * + */ +otError otThreadSearchForBetterParent(otInstance *aInstance); + +/** + * Gets the IPv6 counters. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -855,7 +895,7 @@ otError otThreadGetParentLastRssi(otInstance *aInstance, int8_t *aLastRssi); const otIpCounters *otThreadGetIp6Counters(otInstance *aInstance); /** - * Reset the IPv6 counters. + * Resets the IPv6 counters. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -863,7 +903,7 @@ const otIpCounters *otThreadGetIp6Counters(otInstance *aInstance); void otThreadResetIp6Counters(otInstance *aInstance); /** - * Get the Thread MLE counters. + * Gets the Thread MLE counters. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -873,7 +913,7 @@ void otThreadResetIp6Counters(otInstance *aInstance); const otMleCounters *otThreadGetMleCounters(otInstance *aInstance); /** - * Reset the Thread MLE counters. + * Resets the Thread MLE counters. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -883,6 +923,8 @@ void otThreadResetMleCounters(otInstance *aInstance); /** * This function pointer is called every time an MLE Parent Response message is received. * + * This is used in `otThreadRegisterParentResponseCallback()`. + * * @param[in] aInfo A pointer to a location on stack holding the stats data. * @param[in] aContext A pointer to callback client-specific context. * @@ -892,14 +934,16 @@ typedef void (*otThreadParentResponseCallback)(otThreadParentResponseInfo *aInfo /** * This function registers a callback to receive MLE Parent Response data. * + * This function requires `OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE`. + * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aCallback A pointer to a function that is called upon receiving an MLE Parent Response message. * @param[in] aContext A pointer to callback client-specific context. * */ -void otThreadRegisterParentResponseCallback(otInstance * aInstance, +void otThreadRegisterParentResponseCallback(otInstance *aInstance, otThreadParentResponseCallback aCallback, - void * aContext); + void *aContext); /** * This structure represents the Thread Discovery Request data. @@ -929,9 +973,9 @@ typedef void (*otThreadDiscoveryRequestCallback)(const otThreadDiscoveryRequestI * @param[in] aContext A pointer to callback application-specific context. * */ -void otThreadSetDiscoveryRequestCallback(otInstance * aInstance, +void otThreadSetDiscoveryRequestCallback(otInstance *aInstance, otThreadDiscoveryRequestCallback aCallback, - void * aContext); + void *aContext); /** * This function pointer type defines the callback to notify the outcome of a `otThreadLocateAnycastDestination()` @@ -946,7 +990,7 @@ void otThreadSetDiscoveryRequestCallback(otInstance * aInsta * @param[in] aRloc16 The RLOC16 of the destination if found, otherwise invalid RLOC16 (0xfffe). * */ -typedef void (*otThreadAnycastLocatorCallback)(void * aContext, +typedef void (*otThreadAnycastLocatorCallback)(void *aContext, otError aError, const otIp6Address *aMeshLocalAddress, uint16_t aRloc16); @@ -968,10 +1012,10 @@ typedef void (*otThreadAnycastLocatorCallback)(void * aContext, * @retval OT_ERROR_NO_BUFS Out of buffer to prepare and send the request message. * */ -otError otThreadLocateAnycastDestination(otInstance * aInstance, - const otIp6Address * aAnycastAddress, +otError otThreadLocateAnycastDestination(otInstance *aInstance, + const otIp6Address *aAnycastAddress, otThreadAnycastLocatorCallback aCallback, - void * aContext); + void *aContext); /** * This function indicates whether an anycast locate request is currently in progress. @@ -996,9 +1040,9 @@ bool otThreadIsAnycastLocateInProgress(otInstance *aInstance); * @param[in] aMlIid The ML-IID of the ADDR_NTF.ntf message. * */ -void otThreadSendAddressNotification(otInstance * aInstance, - otIp6Address * aDestination, - otIp6Address * aTarget, +void otThreadSendAddressNotification(otInstance *aInstance, + otIp6Address *aDestination, + otIp6Address *aTarget, otIp6InterfaceIdentifier *aMlIid); /** @@ -1015,8 +1059,8 @@ void otThreadSendAddressNotification(otInstance * aInstance, * @retval OT_ERROR_NO_BUFS If insufficient message buffers available. * */ -otError otThreadSendProactiveBackboneNotification(otInstance * aInstance, - otIp6Address * aTarget, +otError otThreadSendProactiveBackboneNotification(otInstance *aInstance, + otIp6Address *aTarget, otIp6InterfaceIdentifier *aMlIid, uint32_t aTimeSinceLastTransaction); @@ -1035,6 +1079,28 @@ otError otThreadSendProactiveBackboneNotification(otInstance * aIns */ otError otThreadDetachGracefully(otInstance *aInstance, otDetachGracefullyCallback aCallback, void *aContext); +#define OT_DURATION_STRING_SIZE 21 ///< Recommended size for string representation of `uint32_t` duration in seconds. + +/** + * This function converts an `uint32_t` duration (in seconds) to a human-readable string. + * + * This function requires `OPENTHREAD_CONFIG_UPTIME_ENABLE` to be enabled. + * + * The string follows the format "::" for hours, minutes, seconds (if duration is shorter than one day) or + * "
d.::" (if longer than a day). + * + * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be truncated + * but the outputted string is always null-terminated. + * + * This function is intended for use with `mAge` or `mConnectionTime` in `otNeighborInfo` or `otChildInfo` structures. + * + * @param[in] aDuration A duration interval in seconds. + * @param[out] aBuffer A pointer to a char array to output the string. + * @param[in] aSize The size of @p aBuffer (in bytes). Recommended to use `OT_DURATION_STRING_SIZE`. + * + */ +void otConvertDurationInSecondsToString(uint32_t aDuration, char *aBuffer, uint16_t aSize); + /** * @} * diff --git a/include/openthread/thread_ftd.h b/include/openthread/thread_ftd.h index ea052deed39..f2b0db5d958 100644 --- a/include/openthread/thread_ftd.h +++ b/include/openthread/thread_ftd.h @@ -58,7 +58,8 @@ typedef struct { otExtAddress mExtAddress; ///< IEEE 802.15.4 Extended Address uint32_t mTimeout; ///< Timeout - uint32_t mAge; ///< Time last heard + uint32_t mAge; ///< Seconds since last heard + uint64_t mConnectionTime; ///< Seconds since attach (requires `OPENTHREAD_CONFIG_UPTIME_ENABLE`) uint16_t mRloc16; ///< RLOC16 uint16_t mChildId; ///< Child ID uint8_t mNetworkDataVersion; ///< Network Data Version @@ -68,6 +69,7 @@ typedef struct uint16_t mFrameErrorRate; ///< Frame error rate (0xffff->100%). Requires error tracking feature. uint16_t mMessageErrorRate; ///< (IPv6) msg error rate (0xffff->100%). Requires error tracking feature. uint16_t mQueuedMessageCnt; ///< Number of queued messages for the child. + uint16_t mSupervisionInterval; ///< Supervision interval (in seconds). uint8_t mVersion; ///< MLE version bool mRxOnWhenIdle : 1; ///< rx-on-when-idle bool mFullThreadDevice : 1; ///< Full Thread Device @@ -122,7 +124,7 @@ typedef struct otCacheEntryIterator } otCacheEntryIterator; /** - * Get the maximum number of children currently allowed. + * Gets the maximum number of children currently allowed. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -134,7 +136,7 @@ typedef struct otCacheEntryIterator uint16_t otThreadGetMaxAllowedChildren(otInstance *aInstance); /** - * Set the maximum number of children currently allowed. + * Sets the maximum number of children currently allowed. * * This parameter can only be set when Thread protocol operation has been stopped. * @@ -196,19 +198,76 @@ otError otThreadSetRouterEligible(otInstance *aInstance, bool aEligible); otError otThreadSetPreferredRouterId(otInstance *aInstance, uint8_t aRouterId); /** - * Get the Thread Leader Weight used when operating in the Leader role. + * This enumeration represents the power supply property on a device. + * + * This is used as a property in `otDeviceProperties` to calculate the leader weight. + * + */ +typedef enum +{ + OT_POWER_SUPPLY_BATTERY = 0, ///< Battery powered. + OT_POWER_SUPPLY_EXTERNAL = 1, ///< Externally powered (mains-powered). + OT_POWER_SUPPLY_EXTERNAL_STABLE = 2, ///< Stable external power with a battery backup or UPS. + OT_POWER_SUPPLY_EXTERNAL_UNSTABLE = 3, ///< Potentially unstable ext power (e.g. light bulb powered via a switch). +} otPowerSupply; + +/** + * This structure represents the device properties which are used for calculating the local leader weight on a + * device. + * + * The parameters are set based on device's capability, whether acting as border router, its power supply config, etc. + * + * `mIsUnstable` indicates operational stability of device and is determined via a vendor specific mechanism. It can + * include the following cases: + * - Device internally detects that it loses external power supply more often than usual. What is usual is + * determined by the vendor. + * - Device internally detects that it reboots more often than usual. What is usual is determined by the vendor. + * + */ +typedef struct otDeviceProperties +{ + otPowerSupply mPowerSupply; ///< Power supply config. + bool mIsBorderRouter : 1; ///< Whether device is a border router. + bool mSupportsCcm : 1; ///< Whether device supports CCM (can act as a CCM border router). + bool mIsUnstable : 1; ///< Operational stability of device (vendor specific). + int8_t mLeaderWeightAdjustment; ///< Weight adjustment. Should be -16 to +16 (clamped otherwise). +} otDeviceProperties; + +/** + * Get the current device properties. + * + * @returns The device properties `otDeviceProperties`. + * + */ +const otDeviceProperties *otThreadGetDeviceProperties(otInstance *aInstance); + +/** + * Set the device properties which are then used to determine and set the Leader Weight. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aDeviceProperties The device properties. + * + */ +void otThreadSetDeviceProperties(otInstance *aInstance, const otDeviceProperties *aDeviceProperties); + +/** + * Gets the Thread Leader Weight used when operating in the Leader role. * * @param[in] aInstance A pointer to an OpenThread instance. * * @returns The Thread Leader Weight value. * * @sa otThreadSetLeaderWeight + * @sa otThreadSetDeviceProperties * */ uint8_t otThreadGetLocalLeaderWeight(otInstance *aInstance); /** - * Set the Thread Leader Weight used when operating in the Leader role. + * Sets the Thread Leader Weight used when operating in the Leader role. + * + * This function directly sets the Leader Weight to the new value, replacing its previous value (which may have been + * determined from the current `otDeviceProperties`). * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aWeight The Thread Leader Weight value. @@ -238,7 +297,7 @@ uint32_t otThreadGetPreferredLeaderPartitionId(otInstance *aInstance); void otThreadSetPreferredLeaderPartitionId(otInstance *aInstance, uint32_t aPartitionId); /** - * Get the Joiner UDP Port. + * Gets the Joiner UDP Port. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -250,7 +309,7 @@ void otThreadSetPreferredLeaderPartitionId(otInstance *aInstance, uint32_t aPart uint16_t otThreadGetJoinerUdpPort(otInstance *aInstance); /** - * Set the Joiner UDP Port. + * Sets the Joiner UDP Port. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aJoinerUdpPort The Joiner UDP Port number. @@ -355,6 +414,35 @@ uint8_t otThreadGetRouterUpgradeThreshold(otInstance *aInstance); */ void otThreadSetRouterUpgradeThreshold(otInstance *aInstance, uint8_t aThreshold); +/** + * Get the MLE_CHILD_ROUTER_LINKS parameter used in the REED role. + * + * This parameter specifies the max number of neighboring routers with which the device (as an FED) + * will try to establish link. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The MLE_CHILD_ROUTER_LINKS value. + * + * @sa otThreadSetChildRouterLinks + * + */ +uint8_t otThreadGetChildRouterLinks(otInstance *aInstance); + +/** + * Set the MLE_CHILD_ROUTER_LINKS parameter used in the REED role. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aChildRouterLinks The MLE_CHILD_ROUTER_LINKS value. + * + * @retval OT_ERROR_NONE Successfully set the value. + * @retval OT_ERROR_INVALID_STATE Thread protocols are enabled. + * + * @sa otThreadGetChildRouterLinks + * + */ +otError otThreadSetChildRouterLinks(otInstance *aInstance, uint8_t aChildRouterLinks); + /** * Release a Router ID that has been allocated by the device in the Leader role. * @@ -451,7 +539,7 @@ uint8_t otThreadGetRouterSelectionJitter(otInstance *aInstance); void otThreadSetRouterSelectionJitter(otInstance *aInstance, uint8_t aRouterJitter); /** - * The function retains diagnostic information for an attached Child by its Child ID or RLOC16. + * Gets diagnostic information for an attached Child by its Child ID or RLOC16. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aChildId The Child ID or RLOC16 for the attached child. @@ -498,10 +586,10 @@ otError otThreadGetChildInfoByIndex(otInstance *aInstance, uint16_t aChildIndex, * @sa otThreadGetChildInfoByIndex * */ -otError otThreadGetChildNextIp6Address(otInstance * aInstance, +otError otThreadGetChildNextIp6Address(otInstance *aInstance, uint16_t aChildIndex, otChildIp6AddressIterator *aIterator, - otIp6Address * aAddress); + otIp6Address *aAddress); /** * Get the current Router ID Sequence. @@ -645,7 +733,7 @@ int8_t otThreadGetParentPriority(otInstance *aInstance); otError otThreadSetParentPriority(otInstance *aInstance, int8_t aParentPriority); /** - * This function gets the maximum number of IP addresses that each MTD child may register with this device as parent. + * Gets the maximum number of IP addresses that each MTD child may register with this device as parent. * * @param[in] aInstance A pointer to an OpenThread instance. * @@ -657,11 +745,15 @@ otError otThreadSetParentPriority(otInstance *aInstance, int8_t aParentPriority) uint8_t otThreadGetMaxChildIpAddresses(otInstance *aInstance); /** - * This function sets/restores the maximum number of IP addresses that each MTD child may register with this + * Sets or restores the maximum number of IP addresses that each MTD child may register with this * device as parent. * - * @note This API requires `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE`, and is only used by Thread Test Harness - * to limit the address registrations of the reference parent in order to test the MTD DUT reaction. + * Pass `0` to clear the setting and restore the default. + * + * Available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. + * + * @note Only used by Thread Test Harness to limit the address registrations of the reference + * parent in order to test the MTD DUT reaction. * * @param[in] aInstance A pointer to an OpenThread instance. * @param[in] aMaxIpAddresses The maximum number of IP addresses that each MTD child may register with this @@ -783,6 +875,36 @@ void otThreadGetRouterIdRange(otInstance *aInstance, uint8_t *aMinRouterId, uint * */ otError otThreadSetRouterIdRange(otInstance *aInstance, uint8_t aMinRouterId, uint8_t aMaxRouterId); + +/** + * This function indicates whether or not a Router ID is currently allocated. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aRouterId The router ID to check. + * + * @retval TRUE The @p aRouterId is allocated. + * @retval FALSE The @p aRouterId is not allocated. + * + */ +bool otThreadIsRouterIdAllocated(otInstance *aInstance, uint8_t aRouterId); + +/** + * This function gets the next hop and path cost towards a given RLOC16 destination. + * + * This function can be used with either @p aNextHopRloc16 or @p aPathCost being NULL indicating caller does not want + * to get the value. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aDesRloct16 The RLOC16 of destination. + * @param[out] aNextHopRloc16 A pointer to return RLOC16 of next hop, 0xfffe if no next hop. + * @param[out] aPathCost A pointer to return path cost towards destination. + * + */ +void otThreadGetNextHopAndPathCost(otInstance *aInstance, + uint16_t aDestRloc16, + uint16_t *aNextHopRloc16, + uint8_t *aPathCost); + /** * @} * diff --git a/include/openthread/trel.h b/include/openthread/trel.h index e2dcbdbe4b5..26373501c42 100644 --- a/include/openthread/trel.h +++ b/include/openthread/trel.h @@ -74,30 +74,23 @@ typedef struct otTrelPeer typedef uint16_t otTrelPeerIterator; /** - * This function enables TREL operation. + * Enables or disables TREL operation. * - * This function initiates an ongoing DNS-SD browse on the service name "_trel._udp" within the local browsing domain - * to discover other devices supporting TREL. Device also registers a new service to be advertised using DNS-SD, - * with the service name is "_trel._udp" indicating its support for TREL. Device is then ready to receive TREL messages - * from peers. + * When @p aEnable is true, this function initiates an ongoing DNS-SD browse on the service name "_trel._udp" within the + * local browsing domain to discover other devices supporting TREL. Device also registers a new service to be advertised + * using DNS-SD, with the service name is "_trel._udp" indicating its support for TREL. Device is then ready to receive + * TREL messages from peers. * - * @note By default the OpenThread stack enables the TREL operation on start. - * - * @param[in] aInstance The OpenThread instance. + * When @p aEnable is false, this function stops the DNS-SD browse on the service name "_trel._udp", stops advertising + * TREL DNS-SD service, and clears the TREL peer table. * - */ -void otTrelEnable(otInstance *aInstance); - -/** - * This function disables TREL operation. - * - * This function stops the DNS-SD browse on the service name "_trel._udp", stops advertising TREL DNS-SD service, and - * clears the TREL peer table. + * @note By default the OpenThread stack enables the TREL operation on start. * - * @param[in] aInstance The OpenThread instance. + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aEnable A boolean to enable/disable the TREL operation. * */ -void otTrelDisable(otInstance *aInstance); +void otTrelSetEnabled(otInstance *aInstance, bool aEnable); /** * This function indicates whether the TREL operation is enabled. diff --git a/include/openthread/udp.h b/include/openthread/udp.h index 3b2ffcebac4..5a2681320bd 100644 --- a/include/openthread/udp.h +++ b/include/openthread/udp.h @@ -70,7 +70,7 @@ typedef struct otUdpReceiver { struct otUdpReceiver *mNext; ///< A pointer to the next UDP receiver (internal use only). otUdpHandler mHandler; ///< A function pointer to the receiver callback. - void * mContext; ///< A pointer to application-specific context. + void *mContext; ///< A pointer to application-specific context. } otUdpReceiver; /** @@ -125,8 +125,8 @@ typedef struct otUdpSocket otSockAddr mSockName; ///< The local IPv6 socket address. otSockAddr mPeerName; ///< The peer IPv6 socket address. otUdpReceive mHandler; ///< A function pointer to the application callback. - void * mContext; ///< A pointer to application-specific context. - void * mHandle; ///< A handle to platform's UDP. + void *mContext; ///< A pointer to application-specific context. + void *mHandle; ///< A handle to platform's UDP. struct otUdpSocket *mNext; ///< A pointer to the next UDP socket (internal use only). } otUdpSocket; @@ -278,11 +278,11 @@ otUdpSocket *otUdpGetSockets(otInstance *aInstance); * @param[in] aContext A pointer to application-specific context. * */ -typedef void (*otUdpForwarder)(otMessage * aMessage, +typedef void (*otUdpForwarder)(otMessage *aMessage, uint16_t aPeerPort, otIp6Address *aPeerAddr, uint16_t aSockPort, - void * aContext); + void *aContext); /** * Set UDP forward callback to deliver UDP packets to host. @@ -306,8 +306,8 @@ void otUdpForwardSetForwarder(otInstance *aInstance, otUdpForwarder aForwarder, * @warning No matter the call success or fail, the message is freed. * */ -void otUdpForwardReceive(otInstance * aInstance, - otMessage * aMessage, +void otUdpForwardReceive(otInstance *aInstance, + otMessage *aMessage, uint16_t aPeerPort, const otIp6Address *aPeerAddr, uint16_t aSockPort); diff --git a/script/bootstrap b/script/bootstrap index 9a4479c3521..45f2f75978d 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -38,7 +38,7 @@ install_packages_pretty_format() echo 'Installing pretty tools useful for code contributions...' # add clang-format and clang-tidy for pretty - sudo apt-get --no-install-recommends install -y clang-format-9 clang-tidy-9 || echo 'WARNING: could not install clang-format-9 and clang-tidy-9, which is useful if you plan to contribute C/C++ code to the OpenThread project.' + sudo apt-get --no-install-recommends install -y clang-format-14 clang-tidy-14 || echo 'WARNING: could not install clang-format-14 and clang-tidy-14, which is useful if you plan to contribute C/C++ code to the OpenThread project.' # add yapf for pretty python3 -m pip install yapf==0.31.0 || echo 'WARNING: could not install yapf, which is useful if you plan to contribute python code to the OpenThread project.' @@ -46,8 +46,8 @@ install_packages_pretty_format() # add mdv for local size report python3 -m pip install mdv || echo 'WARNING: could not install mdv, which is required to post markdown size report for OpenThread.' - # add shfmt for shell pretty, try brew only because snap does not support home directory not being /home and doesn't work in docker. - command -v shfmt || brew install shfmt || echo 'WARNING: could not install shfmt, which is useful if you plan to contribute shell scripts to the OpenThread project.' + # add shfmt for shell pretty + command -v shfmt || sudo apt-get install shfmt || echo 'WARNING: could not install shfmt, which is useful if you plan to contribute shell scripts to the OpenThread project.' } install_packages_apt() @@ -66,7 +66,7 @@ install_packages_apt() if [ "$PLATFORM" = "Raspbian" ]; then sudo apt-get --no-install-recommends install -y binutils-arm-none-eabi gcc-arm-none-eabi gdb-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib elif [ "$PLATFORM" = "Ubuntu" ]; then - sudo apt-get --no-install-recommends install -y ca-certificates wget + sudo apt-get --no-install-recommends install -y bzip2 ca-certificates wget (cd /tmp \ && wget --tries 4 --no-check-certificate --quiet -c https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2020q2/gcc-arm-none-eabi-9-2020-q2-update-"$ARCH"-linux.tar.bz2 \ && sudo tar xjf gcc-arm-none-eabi-9-2020-q2-update-"$ARCH"-linux.tar.bz2 -C /opt \ @@ -111,11 +111,14 @@ install_packages_brew() echo 'Installing pretty tools useful for code contributions...' # add clang-format for pretty - CLANG_FORMAT_VERSION="clang-format version 9" - command -v clang-format-9 || (command -v clang-format && (clang-format --version | grep -q "${CLANG_FORMAT_VERSION}")) || { - brew install llvm@9 - sudo ln -s "$(brew --prefix llvm@9)/bin/clang-format" /usr/local/bin/clang-format-9 - } || echo 'WARNING: could not install llvm@9, which is useful if you plan to contribute C/C++ code to the OpenThread project.' + CLANG_FORMAT_VERSION="clang-format version 14" + command -v clang-format-14 || (command -v clang-format && (clang-format --version | grep -q "${CLANG_FORMAT_VERSION}")) || { + brew install llvm@14 + sudo ln -s "$(brew --prefix llvm@14)/bin/clang-format" /usr/local/bin/clang-format-14 + sudo ln -s "$(brew --prefix llvm@14)/bin/clang-tidy" /usr/local/bin/clang-tidy-14 + sudo ln -s "$(brew --prefix llvm@14)/bin/clang-apply-replacements" /usr/local/bin/clang-apply-replacements-14 + sudo ln -s "$(brew --prefix llvm@14)/bin/run-clang-tidy" /usr/local/bin/run-clang-tidy-14 + } || echo 'WARNING: could not install llvm@14, which is useful if you plan to contribute C/C++ code to the OpenThread project.' # add yapf for pretty python3 -m pip install yapf || echo 'Failed to install python code formatter yapf. Install it manually if you need.' diff --git a/script/check-android-build b/script/check-android-build deleted file mode 100755 index 6647b9f1514..00000000000 --- a/script/check-android-build +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash -# -# Copyright (c) 2018, 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. -# - -# -# Run this command on parent directory of openthread -# - -set -euxo pipefail - -check_targets() -{ - for target in "$@"; do - make showcommands "${target}" - test -x "out/target/product/generic/system/bin/${target}" - done - - for target in "$@"; do - make "clean-${target}" || true - done -} - -check_datetime() -{ - local datetime - - datetime="$(date)" - cat >openthread-config-datetime.h <"$RADIO_PTY" <"$RADIO_PTY" & # Cover setting a valid network interface name. - readonly VALID_NETIF_NAME="wan$(date +%H%M%S)" + VALID_NETIF_NAME="wan$(date +%H%M%S)" + readonly VALID_NETIF_NAME RADIO_URL="spinel+hdlc+uart://${CORE_PTY}?region=US&max-power-table=11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26" @@ -140,7 +141,8 @@ do_check() # sleep a while for daemon ready sleep 2 - readonly OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH=640 + OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH=640 + readonly OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH local -r kMaxStringLength="$((OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH - 1))" # verify success if command length doesn't exceed the limit @@ -160,7 +162,8 @@ do_check() # Cover setting a too long(max is 15 characters) network interface name. # Expect exit code to be 2(OT_EXIT_INVALID_ARGUMENTS). - readonly INVALID_NETIF_NAME="wan0123456789123" + INVALID_NETIF_NAME="wan0123456789123" + readonly INVALID_NETIF_NAME sudo "${OT_CLI}" -I "${INVALID_NETIF_NAME}" -n "${RADIO_URL}" || test $? = 2 OT_CLI_CMD="$PWD/build/posix/src/posix/ot-cli ${RADIO_URL}" diff --git a/script/check-scan-build b/script/check-scan-build index 06329d9ca7d..ef723faad8a 100755 --- a/script/check-scan-build +++ b/script/check-scan-build @@ -29,15 +29,16 @@ set -euxo pipefail -readonly OT_SRCDIR="$(pwd)" +OT_SRCDIR="$(pwd)" +readonly OT_SRCDIR -readonly OT_BUILD_OPTIONS=( +OT_BUILD_OPTIONS=( + "-DBUILD_TESTING=OFF" "-DOT_ANYCAST_LOCATOR=ON" "-DOT_BUILD_EXECUTABLES=OFF" "-DOT_BORDER_AGENT=ON" "-DOT_BORDER_ROUTER=ON" "-DOT_BORDER_ROUTING=ON" - "-DOT_BORDER_ROUTING_NAT64=ON" "-DOT_COAP=ON" "-DOT_COAP_BLOCK=ON" "-DOT_COAP_OBSERVE=ON" @@ -47,7 +48,6 @@ readonly OT_BUILD_OPTIONS=( "-DOT_COVERAGE=ON" "-DOT_CHANNEL_MANAGER=ON" "-DOT_CHANNEL_MONITOR=ON" - "-DOT_CHILD_SUPERVISION=ON" "-DOT_DATASET_UPDATER=ON" "-DOT_DHCP6_CLIENT=ON" "-DOT_DHCP6_SERVER=ON" @@ -59,11 +59,13 @@ readonly OT_BUILD_OPTIONS=( "-DOT_IP6_FRAGM=ON" "-DOT_JAM_DETECTION=ON" "-DOT_JOINER=ON" - "-DOT_LEGACY=ON" "-DOT_LOG_LEVEL_DYNAMIC=ON" "-DOT_MAC_FILTER=ON" - "-DOT_MTD_NETDIAG=ON" + "-DOT_MESH_DIAG=ON" + "-DOT_NAT64_BORDER_ROUTING=ON" + "-DOT_NAT64_TRANSLATOR=ON" "-DOT_NEIGHBOR_DISCOVERY_AGENT=ON" + "-DOT_NETDIAG_CLIENT=ON" "-DOT_PING_SENDER=ON" "-DOT_PLATFORM=external" "-DOT_RCP_RESTORATION_MAX_COUNT=2" @@ -73,22 +75,27 @@ readonly OT_BUILD_OPTIONS=( "-DOT_SNTP_CLIENT=ON" "-DOT_SRP_CLIENT=ON" "-DOT_SRP_SERVER=ON" + "-DOT_VENDOR_NAME=OpenThread" + "-DOT_VENDOR_MODEL=Scan-build" + "-DOT_VENDOR_SW_VERSION=OT" ) +readonly OT_BUILD_OPTIONS -readonly OT_CFLAGS=( +OT_CFLAGS=( "-DMBEDTLS_DEBUG_C" "-I$(pwd)/third_party/mbedtls" "-I$(pwd)/third_party/mbedtls/repo/include" '-DMBEDTLS_CONFIG_FILE=\"mbedtls-config.h\"' ) +readonly OT_CFLAGS main() { mkdir -p build cd build - scan-build-9 cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DOT_COMPILE_WARNING_AS_ERROR=ON -DCMAKE_C_FLAGS="${OT_CFLAGS[*]}" -DCMAKE_CXX_FLAGS="${OT_CFLAGS[*]}" "${OT_BUILD_OPTIONS[@]}" "${OT_SRCDIR}" - scan-build-9 --status-bugs -analyze-headers -v ninja + scan-build-14 cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DOT_COMPILE_WARNING_AS_ERROR=ON -DCMAKE_C_FLAGS="${OT_CFLAGS[*]}" -DCMAKE_CXX_FLAGS="${OT_CFLAGS[*]}" "${OT_BUILD_OPTIONS[@]}" "${OT_SRCDIR}" + scan-build-14 --status-bugs -analyze-headers -v ninja cd "${OT_SRCDIR}" } diff --git a/script/check-simulation-build-autotools b/script/check-simulation-build-autotools index aa6f6316b81..1e3ef250f4d 100755 --- a/script/check-simulation-build-autotools +++ b/script/check-simulation-build-autotools @@ -29,7 +29,8 @@ set -euxo pipefail -readonly OT_BUILD_JOBS=$(getconf _NPROCESSORS_ONLN) +OT_BUILD_JOBS=$(getconf _NPROCESSORS_ONLN) +readonly OT_BUILD_JOBS reset_source() { @@ -46,7 +47,6 @@ build_all_features() "-DOPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE=1" "-DOPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE=1" "-DOPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE=1" - "-DOPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE=1" "-DOPENTHREAD_CONFIG_COAP_API_ENABLE=1" "-DOPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE=1" "-DOPENTHREAD_CONFIG_COMMISSIONER_ENABLE=1" @@ -63,7 +63,6 @@ build_all_features() "-DOPENTHREAD_CONFIG_IP6_SLAAC_ENABLE=1" "-DOPENTHREAD_CONFIG_JAM_DETECTION_ENABLE=1" "-DOPENTHREAD_CONFIG_JOINER_ENABLE=1" - "-DOPENTHREAD_CONFIG_LEGACY_ENABLE=1" "-DOPENTHREAD_CONFIG_LINK_RAW_ENABLE=1" "-DOPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE=1" "-DOPENTHREAD_CONFIG_MAC_BEACON_RSP_WHEN_JOINABLE_ENABLE=1" @@ -87,7 +86,7 @@ build_all_features() "-DOPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE=1" "-DOPENTHREAD_CONFIG_SRP_CLIENT_ENABLE=1" "-DOPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE=1" - "-DOPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE=1" + "-DOPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE=1" "-DOPENTHREAD_CONFIG_UDP_FORWARD_ENABLE=1" "-DOPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE=1" "-DOPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE=1" @@ -95,6 +94,8 @@ build_all_features() local options_1_3=( "-DOPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE=1" + "-DOPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE=1" + "-DOPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE=1" "-DOPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE=1" "-DOPENTHREAD_CONFIG_DUA_ENABLE=1" "-DOPENTHREAD_CONFIG_MLR_ENABLE=1" @@ -142,11 +143,9 @@ build_nest_common() "-DOPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE=1" "-DOPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE=1" "-DOPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE=1" - "-DOPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE=1" "-DOPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE=1" "-DOPENTHREAD_CONFIG_DIAG_ENABLE=1" "-DOPENTHREAD_CONFIG_JAM_DETECTION_ENABLE=1" - "-DOPENTHREAD_CONFIG_LEGACY_ENABLE=1" "-DOPENTHREAD_CONFIG_MAC_FILTER_ENABLE=1" "-DOPENTHREAD_CONFIG_PING_SENDER_ENABLE=1" "-DOPENTHREAD_CONFIG_NCP_SPI_ENABLE=1" diff --git a/script/check-simulation-build-cmake b/script/check-simulation-build-cmake index f296ab1ec84..3a0800a2bb0 100755 --- a/script/check-simulation-build-cmake +++ b/script/check-simulation-build-cmake @@ -29,7 +29,8 @@ set -euxo pipefail -readonly OT_BUILDDIR="$(pwd)/build" +OT_BUILDDIR="$(pwd)/build" +readonly OT_BUILDDIR reset_source() { @@ -57,6 +58,8 @@ build_all_features() local options=( "-DOT_BACKBONE_ROUTER=ON" "-DOT_BORDER_ROUTING=ON" + "-DOT_NAT64_BORDER_ROUTING=ON" + "-DOT_NAT64_TRANSLATOR=ON" "-DOT_CSL_RECEIVER=ON" "-DOT_MLR=ON" "-DOT_OTNS=ON" diff --git a/script/check-size b/script/check-size index e81c0bfafa9..9076626984e 100755 --- a/script/check-size +++ b/script/check-size @@ -29,10 +29,17 @@ set -euo pipefail -readonly OT_TMP_DIR=/tmp/ot-size-report -readonly OT_SHA_NEW=${GITHUB_SHA:-$(git rev-parse HEAD)} -readonly OT_SHA_OLD="$(git cat-file -p "${OT_SHA_NEW}" | grep 'parent ' | head -n1 | cut -d' ' -f2)" -readonly OT_REPORT_FILE=/tmp/size_report +OT_TMP_DIR=/tmp/ot-size-report +readonly OT_TMP_DIR + +OT_SHA_NEW=${GITHUB_SHA:-$(git rev-parse HEAD)} +readonly OT_SHA_NEW + +OT_SHA_OLD="$(git cat-file -p "${OT_SHA_NEW}" | grep 'parent ' | head -n1 | cut -d' ' -f2)" +readonly OT_SHA_OLD + +OT_REPORT_FILE=/tmp/size_report +readonly OT_REPORT_FILE setup_arm_gcc_7() { @@ -119,7 +126,6 @@ size_nrf52840_version() "-DOT_BORDER_ROUTER=ON" "-DOT_CHANNEL_MANAGER=ON" "-DOT_CHANNEL_MONITOR=ON" - "-DOT_CHILD_SUPERVISION=ON" "-DOT_COAP=ON" "-DOT_COAPS=ON" "-DOT_COMMISSIONER=ON" @@ -136,7 +142,6 @@ size_nrf52840_version() "-DOT_LINK_RAW=ON" "-DOT_MAC_FILTER=ON" "-DOT_MESSAGE_USE_HEAP=ON" - "-DOT_MTD_NETDIAG=ON" "-DOT_NETDATA_PUBLISHER=ON" "-DOT_PING_SENDER=ON" "-DOT_SERVICE=ON" diff --git a/script/clang-format b/script/clang-format index 01b9535e0c2..e31466d22b5 100755 --- a/script/clang-format +++ b/script/clang-format @@ -27,7 +27,7 @@ # POSSIBILITY OF SUCH DAMAGE. # -CLANG_FORMAT_VERSION="clang-format version 9.0" +CLANG_FORMAT_VERSION="clang-format version 14.0" die() { @@ -39,36 +39,41 @@ die() # expand_aliases shell option is set using shopt. shopt -s expand_aliases -if command -v clang-format-9 >/dev/null; then - alias clang-format=clang-format-9 +if command -v clang-format-14 >/dev/null; then + alias clang-format=clang-format-14 elif command -v clang-format >/dev/null; then case "$(clang-format --version)" in "$CLANG_FORMAT_VERSION"*) ;; *) - die "$(clang-format --version); clang-format 9.0 required" + die "$(clang-format --version); clang-format 14.0 required" ;; esac else - die "clang-format 9.0 required" + die "clang-format 14.0 required" fi clang-format "$@" || die # ensure EOF newline REPLACE=no +FILES=() for arg; do case $arg in -i) REPLACE=yes ;; + -*) ;; + *) + FILES+=("$arg") + ;; esac done -file=$arg - [ $REPLACE != yes ] || { - [ -n "$(tail -c1 "$file")" ] && echo >>"$file" + for file in "${FILES[@]}"; do + [ -n "$(tail -c1 "$file")" ] && echo >>"$file" + done } exit 0 diff --git a/script/clang-tidy b/script/clang-tidy index b2dfb4d4d8e..c4608092626 100755 --- a/script/clang-tidy +++ b/script/clang-tidy @@ -27,8 +27,8 @@ # POSSIBILITY OF SUCH DAMAGE. # -CLANG_TIDY_VERSION="LLVM version 9.0" -CLANG_APPLY_REPLACEMENTS_VERSION="clang-apply-replacements version 9.0" +CLANG_TIDY_VERSION="LLVM version 14.0" +CLANG_APPLY_REPLACEMENTS_VERSION="clang-apply-replacements version 14.0" die() { @@ -36,9 +36,9 @@ die() exit 1 } -# Search for clang-tidy-9 -if command -v clang-tidy-9 >/dev/null; then - clang_tidy=$(command -v clang-tidy-9) +# Search for clang-tidy-14 +if command -v clang-tidy-14 >/dev/null; then + clang_tidy=$(command -v clang-tidy-14) elif command -v clang-tidy >/dev/null; then clang_tidy=$(command -v clang-tidy) case "$($clang_tidy --version)" in @@ -49,12 +49,12 @@ elif command -v clang-tidy >/dev/null; then ;; esac else - die "clang-tidy 9.0 required" + die "clang-tidy 14.0 required" fi -# Search for clang-apply-replacements-9 -if command -v clang-apply-replacements-9 >/dev/null; then - clang_apply_replacements=$(command -v clang-apply-replacements-9) +# Search for clang-apply-replacements-14 +if command -v clang-apply-replacements-14 >/dev/null; then + clang_apply_replacements=$(command -v clang-apply-replacements-14) elif command -v clang-apply-replacements >/dev/null; then clang_apply_replacements=$(command -v clang-apply-replacements) case "$($clang_apply_replacements --version)" in @@ -65,20 +65,20 @@ elif command -v clang-apply-replacements >/dev/null; then ;; esac else - die "clang-apply-replacements 9.0 required" + die "clang-apply-replacements 14.0 required" fi -# Search for run-clang-tidy-9.py -if command -v run-clang-tidy-9.py >/dev/null; then - run_clang_tidy=$(command -v run-clang-tidy-9.py) -elif command -v run-clang-tidy-9 >/dev/null; then - run_clang_tidy=$(command -v run-clang-tidy-9) +# Search for run-clang-tidy-14.py +if command -v run-clang-tidy-14.py >/dev/null; then + run_clang_tidy=$(command -v run-clang-tidy-14.py) +elif command -v run-clang-tidy-14 >/dev/null; then + run_clang_tidy=$(command -v run-clang-tidy-14) elif command -v run-clang-tidy.py >/dev/null; then run_clang_tidy=$(command -v run-clang-tidy.py) elif command -v run-clang-tidy >/dev/null; then run_clang_tidy=$(command -v run-clang-tidy) else - die "run-clang-tidy.py 9.0 required" + die "run-clang-tidy.py 14.0 required" fi $run_clang_tidy -clang-tidy-binary "$clang_tidy" -clang-apply-replacements-binary "$clang_apply_replacements" "$@" || die diff --git a/script/cmake-build b/script/cmake-build index b91b7be0959..475728644e3 100755 --- a/script/cmake-build +++ b/script/cmake-build @@ -45,6 +45,7 @@ # Compile with the specified ninja build target: # # OT_CMAKE_NINJA_TARGET="ot-cli-ftd" script/cmake-build ${platform} +# OT_CMAKE_NINJA_TARGET="ot-cli-ftd ot-cli-mtd" script/cmake-build ${platform} # # Compile with the specified build directory: # @@ -61,50 +62,55 @@ set -euxo pipefail -OT_CMAKE_NINJA_TARGET=${OT_CMAKE_NINJA_TARGET:-} +OT_CMAKE_NINJA_TARGET=${OT_CMAKE_NINJA_TARGET-} OT_SRCDIR="$(cd "$(dirname "$0")"/.. && pwd)" - readonly OT_SRCDIR -readonly OT_PLATFORMS=(cc2538 simulation posix) -readonly OT_POSIX_SIM_COMMON_OPTIONS=( + +OT_PLATFORMS=(simulation posix android-ndk) +readonly OT_PLATFORMS + +OT_POSIX_SIM_COMMON_OPTIONS=( "-DOT_ANYCAST_LOCATOR=ON" "-DOT_BORDER_AGENT=ON" + "-DOT_BORDER_AGENT_ID=ON" "-DOT_BORDER_ROUTER=ON" + "-DOT_CHANNEL_MANAGER=ON" + "-DOT_CHANNEL_MONITOR=ON" + "-DOT_CHILD_SUPERVISION=ON" "-DOT_COAP=ON" + "-DOT_COAPS=ON" "-DOT_COAP_BLOCK=ON" "-DOT_COAP_OBSERVE=ON" - "-DOT_COAPS=ON" "-DOT_COMMISSIONER=ON" - "-DOT_CHANNEL_MANAGER=ON" - "-DOT_CHANNEL_MONITOR=ON" - "-DOT_CHILD_SUPERVISION=ON" + "-DOT_COMPILE_WARNING_AS_ERROR=ON" + "-DOT_COVERAGE=ON" "-DOT_DATASET_UPDATER=ON" "-DOT_DHCP6_CLIENT=ON" "-DOT_DHCP6_SERVER=ON" "-DOT_DIAGNOSTIC=ON" + "-DOT_DNSSD_SERVER=ON" "-DOT_DNS_CLIENT=ON" "-DOT_ECDSA=ON" "-DOT_HISTORY_TRACKER=ON" "-DOT_IP6_FRAGM=ON" "-DOT_JAM_DETECTION=ON" "-DOT_JOINER=ON" - "-DOT_LEGACY=ON" + "-DOT_LOG_LEVEL_DYNAMIC=ON" "-DOT_MAC_FILTER=ON" - "-DOT_MTD_NETDIAG=ON" "-DOT_NEIGHBOR_DISCOVERY_AGENT=ON" "-DOT_NETDATA_PUBLISHER=ON" + "-DOT_NETDIAG_CLIENT=ON" "-DOT_PING_SENDER=ON" + "-DOT_RCP_RESTORATION_MAX_COUNT=2" "-DOT_REFERENCE_DEVICE=ON" "-DOT_SERVICE=ON" "-DOT_SNTP_CLIENT=ON" "-DOT_SRP_CLIENT=ON" - "-DOT_COVERAGE=ON" - "-DOT_LOG_LEVEL_DYNAMIC=ON" - "-DOT_COMPILE_WARNING_AS_ERROR=ON" - "-DOT_RCP_RESTORATION_MAX_COUNT=2" + "-DOT_SRP_SERVER=ON" "-DOT_UPTIME=ON" ) +readonly OT_POSIX_SIM_COMMON_OPTIONS die() { @@ -122,11 +128,11 @@ build() cd "${builddir}" cmake -GNinja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DOT_COMPILE_WARNING_AS_ERROR=ON "$@" "${OT_SRCDIR}" - - if [[ -n ${OT_CMAKE_NINJA_TARGET[*]} ]]; then - ninja "${OT_CMAKE_NINJA_TARGET[@]}" - else + if [[ -z ${OT_CMAKE_NINJA_TARGET// /} ]]; then ninja + else + IFS=' ' read -r -a OT_CMAKE_NINJA_TARGET <<<"${OT_CMAKE_NINJA_TARGET}" + ninja "${OT_CMAKE_NINJA_TARGET[@]}" fi cd "${OT_SRCDIR}" @@ -146,30 +152,87 @@ main() shift local local_options=() local options=( - "-DOT_PLATFORM=${platform}" "-DOT_SLAAC=ON" ) case "${platform}" in + android-ndk) + if [ -z "${NDK-}" ]; then + echo " +The 'NDK' environment variable needs to point to the Android NDK toolchain. +Please ensure the NDK is downloaded and extracted then try to run this script again + +For example: + NDK=/opt/android-ndk-r25c ./script/cmake-build-android + +You can download the NDK at https://developer.android.com/ndk/downloads + + " + exit 1 + fi + + NDK_CMAKE_TOOLCHAIN_FILE="${NDK?}/build/cmake/android.toolchain.cmake" + if [ ! -f "${NDK_CMAKE_TOOLCHAIN_FILE}" ]; then + echo " +Could not fild the Android NDK CMake toolchain file +- NDK=${NDK} +- NDK_CMAKE_TOOLCHAIN_FILE=${NDK_CMAKE_TOOLCHAIN_FILE} + + " + exit 2 + fi + local_options+=( + "-DOT_LOG_OUTPUT=PLATFORM_DEFINED" + + # Add Android NDK flags + "-DOT_ANDROID_NDK=1" + "-DCMAKE_TOOLCHAIN_FILE=${NDK?}/build/cmake/android.toolchain.cmake" + + # Android API needs to be >= android-24 for `getifsaddrs()` + "-DANDROID_PLATFORM=android-24" + + # Store thread settings in the CWD when executing ot-cli or ot-daemon + '-DOT_POSIX_SETTINGS_PATH="./thread"' + ) + + # Rewrite platform to posix + platform="posix" + + # Check if OT_DAEMON or OT_APP_CLI flags are needed + if [[ ${OT_CMAKE_NINJA_TARGET[*]} =~ "ot-daemon" ]] || [[ ${OT_CMAKE_NINJA_TARGET[*]} =~ "ot-ctl" ]]; then + local_options+=("-DOT_DAEMON=ON") + elif [[ ${OT_CMAKE_NINJA_TARGET[*]} =~ "ot-cli" ]]; then + local_options+=("-DOT_APP_CLI=ON") + fi + + options+=("${local_options[@]}") + ;; + posix) local_options+=( + "-DOT_TCP=OFF" "-DOT_LOG_OUTPUT=PLATFORM_DEFINED" "-DOT_POSIX_MAX_POWER_TABLE=ON" ) options+=("${OT_POSIX_SIM_COMMON_OPTIONS[@]}" "${local_options[@]}") ;; simulation) - local_options=("-DOT_LINK_RAW=ON") + local_options+=( + "-DOT_LINK_RAW=ON" + "-DOT_DNS_DSO=ON" + "-DOT_DNS_CLIENT_OVER_TCP=ON" + "-DOT_UDP_FORWARD=ON" + ) options+=("${OT_POSIX_SIM_COMMON_OPTIONS[@]}" "${local_options[@]}") ;; - cc2538) - options+=("-DCMAKE_TOOLCHAIN_FILE=examples/platforms/${platform}/arm-none-eabi.cmake" "-DCMAKE_BUILD_TYPE=MinSizeRel") - ;; *) options+=("-DCMAKE_TOOLCHAIN_FILE=examples/platforms/${platform}/arm-none-eabi.cmake") ;; esac + options+=( + "-DOT_PLATFORM=${platform}" + ) options+=("$@") build "${platform}" "${options[@]}" } diff --git a/script/gcda-tool b/script/gcda-tool index d5bf2035be8..1b757cce6d1 100755 --- a/script/gcda-tool +++ b/script/gcda-tool @@ -29,8 +29,11 @@ set -euxo pipefail -readonly OT_MERGED_PROFILES=merged_profiles -readonly OT_GCOV_PREFIX_BASE=ot-run +OT_MERGED_PROFILES=merged_profiles +readonly OT_MERGED_PROFILES + +OT_GCOV_PREFIX_BASE=ot-run +readonly OT_GCOV_PREFIX_BASE merge_profiles() { diff --git a/script/make-pretty b/script/make-pretty index 22201c3f1a1..28abdf01461 100755 --- a/script/make-pretty +++ b/script/make-pretty @@ -64,15 +64,25 @@ set -euo pipefail -readonly OT_BUILD_JOBS=$(getconf _NPROCESSORS_ONLN) -readonly OT_EXCLUDE_DIRS=(third_party doc/site) +OT_BUILD_JOBS=$(getconf _NPROCESSORS_ONLN) +readonly OT_BUILD_JOBS -readonly OT_CLANG_SOURCES=('*.c' '*.cc' '*.cpp' '*.h' '*.hpp') -readonly OT_MARKDOWN_SOURCES=('*.md') -readonly OT_PYTHON_SOURCES=('*.py') +OT_EXCLUDE_DIRS=(third_party doc/site) +readonly OT_EXCLUDE_DIRS -readonly OT_CLANG_TIDY_FIX_DIRS=('examples' 'include' 'src' 'tests') -readonly OT_CLANG_TIDY_BUILD_OPTS=( +OT_CLANG_SOURCES=('*.c' '*.cc' '*.cpp' '*.h' '*.hpp') +readonly OT_CLANG_SOURCES + +OT_MARKDOWN_SOURCES=('*.md') +readonly OT_MARKDOWN_SOURCES + +OT_PYTHON_SOURCES=('*.py') +readonly OT_PYTHON_SOURCES + +OT_CLANG_TIDY_FIX_DIRS=('examples' 'include' 'src' 'tests') +readonly OT_CLANG_TIDY_FIX_DIRS + +OT_CLANG_TIDY_BUILD_OPTS=( '-DCMAKE_EXPORT_COMPILE_COMMANDS=ON' '-DOT_ANYCAST_LOCATOR=ON' '-DOT_APP_RCP=OFF' @@ -83,10 +93,8 @@ readonly OT_CLANG_TIDY_BUILD_OPTS=( '-DOT_BORDER_AGENT=ON' '-DOT_BORDER_ROUTER=ON' '-DOT_BORDER_ROUTING=ON' - '-DOT_BORDER_ROUTING_NAT64=ON' '-DOT_CHANNEL_MANAGER=ON' '-DOT_CHANNEL_MONITOR=ON' - '-DOT_CHILD_SUPERVISION=ON' '-DOT_COAP=ON' '-DOT_COAP_BLOCK=ON' '-DOT_COAP_OBSERVE=ON' @@ -99,6 +107,7 @@ readonly OT_CLANG_TIDY_BUILD_OPTS=( '-DOT_DIAGNOSTIC=ON' '-DOT_DNS_CLIENT=ON' '-DOT_DNS_DSO=ON' + '-DOT_DNS_UPSTREAM_QUERY=ON' '-DOT_DNSSD_SERVER=ON' '-DOT_DUA=ON' '-DOT_MLR=ON' @@ -107,13 +116,15 @@ readonly OT_CLANG_TIDY_BUILD_OPTS=( '-DOT_IP6_FRAGM=ON' '-DOT_JAM_DETECTION=ON' '-DOT_JOINER=ON' - '-DOT_LEGACY=ON' '-DOT_LINK_RAW=ON' '-DOT_LINK_METRICS_INITIATOR=ON' '-DOT_LINK_METRICS_SUBJECT=ON' '-DOT_MAC_FILTER=ON' - '-DOT_MTD_NETDIAG=ON' + '-DOT_MESH_DIAG=ON' + '-DOT_NAT64_BORDER_ROUTING=ON' + '-DOT_NAT64_TRANSLATOR=ON' '-DOT_NETDATA_PUBLISHER=ON' + '-DOT_NETDIAG_CLIENT=ON' '-DOT_PING_SENDER=ON' '-DOT_REFERENCE_DEVICE=ON' '-DOT_SERVICE=ON' @@ -128,12 +139,14 @@ readonly OT_CLANG_TIDY_BUILD_OPTS=( '-DOT_COMPILE_WARNING_AS_ERROR=ON' '-DOT_UPTIME=ON' ) +readonly OT_CLANG_TIDY_BUILD_OPTS -readonly OT_CLANG_TIDY_CHECKS="\ +OT_CLANG_TIDY_CHECKS="\ -*,\ google-explicit-constructor,\ google-readability-casting,\ misc-unused-using-decls,\ +modernize-loop-convert,\ modernize-use-bool-literals,\ modernize-use-equals-default,\ modernize-use-equals-delete,\ @@ -142,12 +155,12 @@ readability-avoid-const-params-in-decls,\ readability-else-after-return,\ readability-inconsistent-declaration-parameter-name,\ readability-make-member-function-const,\ +readability-redundant-control-flow,\ readability-redundant-member-init,\ readability-simplify-boolean-expr,\ readability-static-accessed-through-instance,\ " - -#performance-for-range-copy\ +readonly OT_CLANG_TIDY_CHECKS do_clang_format() { diff --git a/script/package b/script/package index 4eaafdf3bdb..2cd8eef9458 100755 --- a/script/package +++ b/script/package @@ -32,12 +32,14 @@ set -euo pipefail -readonly OT_BUILDDIR="${OT_BUILDDIR:-${PWD}/build}" +OT_BUILDDIR="${OT_BUILDDIR:-${PWD}/build}" +readonly OT_BUILDDIR main() { local builddir local options=( + "-DBUILD_TESTING=OFF" "-DCMAKE_BUILD_TYPE=Release" "-DOT_COVERAGE=OFF" "-DOT_LOG_LEVEL=INFO" diff --git a/script/test b/script/test index be16f85bd30..7fc0c3ed214 100755 --- a/script/test +++ b/script/test @@ -32,27 +32,62 @@ set -euo pipefail -readonly OT_BUILDDIR="${OT_BUILDDIR:-${PWD}/build}" -readonly OT_SRCDIR="${PWD}" - -readonly OT_COLOR_PASS='\033[0;32m' -readonly OT_COLOR_FAIL='\033[0;31m' -readonly OT_COLOR_SKIP='\033[0;33m' -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.3}" -readonly INTER_OP="${INTER_OP:-0}" -readonly VERBOSE="${VERBOSE:-0}" -readonly BORDER_ROUTING="${BORDER_ROUTING:-1}" -readonly NAT64="${NAT64:-0}" -readonly INTER_OP_BBR="${INTER_OP_BBR:-1}" - -readonly OT_COREDUMP_DIR="${PWD}/ot-core-dump" -readonly FULL_LOGS=${FULL_LOGS:-0} -readonly TREL=${TREL:-0} -readonly LOCAL_OTBR_DIR=${LOCAL_OTBR_DIR:-""} +OT_BUILDDIR="${OT_BUILDDIR:-${PWD}/build}" +readonly OT_BUILDDIR + +OT_SRCDIR="${PWD}" +readonly OT_SRCDIR + +OT_COLOR_PASS='\033[0;32m' +readonly OT_COLOR_PASS + +OT_COLOR_FAIL='\033[0;31m' +readonly OT_COLOR_FAIL + +OT_COLOR_SKIP='\033[0;33m' +readonly OT_COLOR_SKIP + +OT_COLOR_NONE='\033[0m' +readonly OT_COLOR_NONE + +OT_NODE_TYPE="${OT_NODE_TYPE:-cli}" +readonly OT_NODE_TYPE + +OT_NATIVE_IP="${OT_NATIVE_IP:-0}" +readonly OT_NATIVE_IP + +THREAD_VERSION="${THREAD_VERSION:-1.3}" +readonly THREAD_VERSION + +INTER_OP="${INTER_OP:-0}" +readonly INTER_OP + +VERBOSE="${VERBOSE:-0}" +readonly VERBOSE + +BORDER_ROUTING="${BORDER_ROUTING:-1}" +readonly BORDER_ROUTING + +NAT64="${NAT64:-0}" +readonly NAT64 + +NAT64_SERVICE="${NAT64_SERVICE:-openthread}" +readonly NAT64_SERVICE + +INTER_OP_BBR="${INTER_OP_BBR:-0}" +readonly INTER_OP_BBR + +OT_COREDUMP_DIR="${PWD}/ot-core-dump" +readonly OT_COREDUMP_DIR + +FULL_LOGS=${FULL_LOGS:-0} +readonly FULL_LOGS + +TREL=${TREL:-0} +readonly TREL + +LOCAL_OTBR_DIR=${LOCAL_OTBR_DIR:-""} +readonly LOCAL_OTBR_DIR build_simulation() { @@ -237,6 +272,7 @@ do_cert() do_cert_suite() { export top_builddir="${OT_BUILDDIR}/openthread-simulation-${THREAD_VERSION}" + export top_srcdir="${OT_SRCDIR}" if [[ ${THREAD_VERSION} != "1.1" ]]; then export top_builddir_1_3_bbr="${OT_BUILDDIR}/openthread-simulation-1.3-bbr" @@ -250,7 +286,8 @@ do_cert_suite() sudo modprobe ip6table_filter - python3 tests/scripts/thread-cert/run_cert_suite.py --multiply "${MULTIPLY:-1}" "$@" + mkdir -p ot_testing + ./tests/scripts/thread-cert/run_cert_suite.py --run-directory ot_testing --multiply "${MULTIPLY:-1}" "$@" exit 0 } @@ -286,9 +323,27 @@ do_build_otbr_docker() "-DOT_SRP_CLIENT=ON" "-DOT_FULL_LOGS=ON" "-DOT_UPTIME=ON" + "-DOTBR_DNS_UPSTREAM_QUERY=ON" "-DOTBR_DUA_ROUTING=ON" - "-DCMAKE_CXX_FLAGS='-DOPENTHREAD_CONFIG_DNSSD_SERVER_BIND_UNSPECIFIED_NETIF=1'" ) + local args=( + "BORDER_ROUTING=${BORDER_ROUTING}" + "INFRA_IF_NAME=eth0" + "BACKBONE_ROUTER=1" + "REFERENCE_DEVICE=1" + "OT_BACKBONE_CI=1" + "NAT64=${NAT64}" + "NAT64_SERVICE=${NAT64_SERVICE}" + "DNS64=${NAT64}" + "REST_API=0" + "WEB_GUI=0" + "MDNS=${OTBR_MDNS:-mDNSResponder}" + ) + + if [[ ${NAT64} != 1 ]]; then + # We are testing upstream DNS forwarding in the NAT64 tests, and OPENTHREAD_CONFIG_DNSSD_SERVER_BIND_UNSPECIFIED_NETIF will block OpenThread's DNSSD server since we already have bind9 running. + otbr_options+=("-DCMAKE_CXX_FLAGS='-DOPENTHREAD_CONFIG_DNSSD_SERVER_BIND_UNSPECIFIED_NETIF=1'") + fi if [[ ${TREL} == 1 ]]; then otbr_options+=("-DOTBR_TREL=ON") @@ -296,13 +351,12 @@ do_build_otbr_docker() otbr_options+=("-DOTBR_TREL=OFF") fi - if [[ ${NAT64} == 1 ]]; then - otbr_options+=("-DOTBR_BORDER_ROUTING_NAT64=ON") - else - otbr_options+=("-DOTBR_BORDER_ROUTING_NAT64=OFF") - fi - local otbr_docker_image=${OTBR_DOCKER_IMAGE:-otbr-ot12-backbone-ci} + local docker_build_args=() + + for arg in "${args[@]}"; do + docker_build_args+=("--build-arg" "$arg") + done otbrdir=$(mktemp -d -t otbr_XXXXXX) otdir=$(pwd) @@ -311,23 +365,24 @@ do_build_otbr_docker() if [[ -z ${LOCAL_OTBR_DIR} ]]; then ./script/git-tool clone https://github.com/openthread/ot-br-posix.git --depth 1 "${otbrdir}" else - cp -r "${LOCAL_OTBR_DIR}"/* "${otbrdir}" - rm -rf "${otbrdir}"/build + rsync -r \ + --exclude=third_party/openthread/repo \ + --exclude=.git \ + --exclude=build \ + "${LOCAL_OTBR_DIR}/." \ + "${otbrdir}" fi + cd "${otbrdir}" rm -rf third_party/openthread/repo - cp -r "${otdir}" third_party/openthread/repo + rsync -r \ + --exclude=build \ + "${otdir}/." \ + third_party/openthread/repo rm -rf .git + docker build -t "${otbr_docker_image}" -f etc/docker/Dockerfile . \ - --build-arg BORDER_ROUTING="${BORDER_ROUTING}" \ - --build-arg INFRA_IF_NAME=eth0 \ - --build-arg BACKBONE_ROUTER=1 \ - --build-arg REFERENCE_DEVICE=1 \ - --build-arg OT_BACKBONE_CI=1 \ - --build-arg NAT64="${NAT64}" \ - --build-arg REST_API=0 \ - --build-arg WEB_GUI=0 \ - --build-arg MDNS="${OTBR_MDNS:-mDNSResponder}" \ + "${docker_build_args[@]}" \ --build-arg OTBR_OPTIONS="${otbr_options[*]}" ) @@ -336,7 +391,7 @@ do_build_otbr_docker() do_pktverify() { - python3 ./tests/scripts/thread-cert/pktverify/verify.py "$1" + ./tests/scripts/thread-cert/pktverify/verify.py "$1" } ot_exec_expect_script() diff --git a/script/update-makefiles.py b/script/update-makefiles.py index 2cd45d3b811..bbaeca43ecc 100755 --- a/script/update-makefiles.py +++ b/script/update-makefiles.py @@ -147,18 +147,6 @@ def update_build_file(file_name, start_string, end_string, new_list, search_stri print("Updated " + include_build_gn_file) -#---------------------------------------------------------------------------------------------- -# Update Android.mk file - -android_mk_file = "./Android.mk" - -formatted_list = [" {:<63} \\\n".format(file_name) for file_name in core_cpp_files] -start_string = "LOCAL_SRC_FILES := \\\n" -end_string = " src/lib/hdlc/hdlc.cpp" -update_build_file(android_mk_file, start_string, end_string, formatted_list) - -print("Updated " + android_mk_file) - #---------------------------------------------------------------------------------------------- # Update Makefile.am files diff --git a/src/cli/BUILD.gn b/src/cli/BUILD.gn index 2d4e8f6a019..86e06af7666 100644 --- a/src/cli/BUILD.gn +++ b/src/cli/BUILD.gn @@ -30,6 +30,8 @@ import("../../etc/gn/openthread.gni") openthread_cli_sources = [ "cli.cpp", "cli.hpp", + "cli_br.cpp", + "cli_br.hpp", "cli_coap.cpp", "cli_coap.hpp", "cli_coap_secure.cpp", diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index 67c8b01b833..32c9d7b34a3 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -33,6 +33,7 @@ set(COMMON_INCLUDES set(COMMON_SOURCES cli.cpp + cli_br.cpp cli_coap.cpp cli_coap_secure.cpp cli_commissioner.cpp diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am index f6e32d02dea..6df2c08e1be 100644 --- a/src/cli/Makefile.am +++ b/src/cli/Makefile.am @@ -65,23 +65,23 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am # # Thus, the existing(previous) way *THIS* library was compiled is # exactly the FTD path. Meaning the "cli" library always sees -# the FTD varients of various headers/classes. +# the FTD variants of various headers/classes. # # The same is true of the "ncp" library. # -# HOWEVER there are two varients of the CLI application, CLI-MTD -# and CLI-FTD (and likewise, two varients of the ncp application) +# HOWEVER there are two variants of the CLI application, CLI-MTD +# and CLI-FTD (and likewise, two variants of the ncp application) # These applications link against two different OpenThread libraries. # # Which flavor, you get depends upon which library: "mtd" or "ftd" is linked. # # Which on the surface appear to link fine against the MTD/FTD library. # -# In this description/example we focus on the "nework_data_leader" -# header file. The FTD varient has many private variables, functions +# In this description/example we focus on the "network_data_leader" +# header file. The FTD variant has many private variables, functions # and other things of "FTD" (ie: full) implementation items. # -# In contrast the MTD is generaly stubbed out with stub-functions +# In contrast the MTD is generally stubbed out with stub-functions # inlined in the header that return "error not implemented" or similar. # # Thus it works... here ... With this file and this example. @@ -92,20 +92,20 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am # Is this true always? Is this robust? # Or is there a hidden "got-ya" that will snag the next person? # -# This also fails static analisys, checks. +# This also fails static analysis, checks. # Application - with MTD vrs FTD class. # Library #1 (cli-lib) with FTD selected. # Library #2 (openthread) with two different class flavors. # -# The static analisys tools will say: "NOPE" different classes! +# The static analysis tools will say: "NOPE" different classes! # Perhaps this will change if/when nothing is implemented in the 'mtd-header' # # Additionally, tools that perform "whole program optimization" will -# throw errors becuase the data structures differ greatly. +# throw errors because the data structures differ greatly. # # Hence, CLI library (and NCP) must exist in two flavors. # -# Unless and until these libraries do not "accidently" suck in +# Unless and until these libraries do not "accidentally" suck in # a "flavored" header file somewhere. lib_LIBRARIES = $(NULL) @@ -153,6 +153,7 @@ libopenthread_cli_radio_a_CPPFLAGS = \ SOURCES_COMMON = \ cli.cpp \ + cli_br.cpp \ cli_coap.cpp \ cli_coap_secure.cpp \ cli_commissioner.cpp \ @@ -182,6 +183,7 @@ libopenthread_cli_radio_a_SOURCES = \ noinst_HEADERS = \ cli.hpp \ + cli_br.hpp \ cli_coap.hpp \ cli_coap_secure.hpp \ cli_commissioner.hpp \ diff --git a/src/cli/README.md b/src/cli/README.md index c088da47494..bdc405b437b 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -23,7 +23,7 @@ Done - [ba](#ba) - [bbr](#bbr) -- [br](#br) +- [br](README_BR.md) - [bufferinfo](#bufferinfo) - [ccathreshold](#ccathreshold) - [channel](#channel) @@ -34,12 +34,14 @@ Done - [childtimeout](#childtimeout) - [coap](README_COAP.md) - [coaps](README_COAPS.md) +- [coex](#coex) - [commissioner](README_COMMISSIONER.md) - [contextreusedelay](#contextreusedelay) - [counters](#counters) - [csl](#csl) - [dataset](README_DATASET.md) - [delaytimermin](#delaytimermin) +- [deviceprops](#deviceprops) - [diag](#diag) - [discover](#discover-channel) - [dns](#dns-config) @@ -66,10 +68,12 @@ Done - [log](#log-filename-filename) - [mac](#mac-retries-direct) - [macfilter](#macfilter) +- [meshdiag](#meshdiag-topology) - [mliid](#mliid-iid) - [mlr](#mlr-reg-ipaddr--timeout) - [mode](#mode) - [multiradio](#multiradio) +- [nat64](#nat64-cidr) - [neighbor](#neighbor-list) - [netdata](README_NETDATA.md) - [netstat](#netstat) @@ -82,12 +86,14 @@ Done - [parent](#parent) - [parentpriority](#parentpriority) - [partitionid](#partitionid) -- [ping](#ping--i-source-ipaddr-size-count-interval-hoplimit-timeout) +- [ping](#ping-async--i-source-ipaddr-size-count-interval-hoplimit-timeout) +- [platform](#platform) - [pollperiod](#pollperiod-pollperiod) - [preferrouterid](#preferrouterid-routerid) - [prefix](#prefix) - [promiscuous](#promiscuous) -- [pskc](#pskc--p-keypassphrase) +- [pskc](#pskc) +- [pskcref](#pskcref) - [radiofilter](#radiofilter) - [rcp](#rcp) - [region](#region) @@ -100,6 +106,7 @@ Done - [routereligible](#routereligible) - [routerselectionjitter](#routerselectionjitter) - [routerupgradethreshold](#routerupgradethreshold) +- [childrouterlinks](#childrouterlinks) - [scan](#scan-channel) - [service](#service) - [singleton](#singleton) @@ -114,6 +121,7 @@ Done - [udp](README_UDP.md) - [unsecureport](#unsecureport-add-port) - [uptime](#uptime) +- [vendor](#vendor-name) - [version](#version) ## OpenThread Command Details @@ -247,7 +255,7 @@ Done ### bbr enable -Enable Backbone Router Service for Thread 1.2 FTD. `SRV_DATA.ntf` would be triggerred for attached device if there is no Backbone Router Service in Thread Network Data. +Enable Backbone Router Service for Thread 1.2 FTD. `SRV_DATA.ntf` would be triggered for attached device if there is no Backbone Router Service in Thread Network Data. `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is required. @@ -258,7 +266,7 @@ Done ### bbr disable -Disable Backbone Router Service for Thread 1.2 FTD. `SRV_DATA.ntf` would be triggerred if Backbone Router is Primary state. o `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is required. +Disable Backbone Router Service for Thread 1.2 FTD. `SRV_DATA.ntf` would be triggered if Backbone Router is Primary state. o `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is required. ```bash > bbr disable @@ -267,7 +275,7 @@ Done ### bbr register -Register Backbone Router Service for Thread 1.2 FTD. `SRV_DATA.ntf` would be triggerred for attached device. +Register Backbone Router Service for Thread 1.2 FTD. `SRV_DATA.ntf` would be triggered for attached device. `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is required. @@ -348,77 +356,13 @@ Started Done ``` -### br - -Enbale/disable the Border Routing functionality. - -```bash -> br enable -Done -``` - -```bash -> br disable -Done -``` - -### br omrprefix - -Get the randomly generated off-mesh-routable prefix of the Border Router. - -```bash -> br omrprefix -fdfc:1ff5:1512:5622::/64 -Done -``` - -### br onlinkprefix - -Get the randomly generated on-link prefix of the Border Router. - -```bash -> br onlinkprefix -fd41:2650:a6f5:0::/64 -Done -``` - -### br nat64prefix - -Get the local NAT64 prefix of the Border Router. - -`OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE` is required. - -```bash -> br nat64prefix -fd14:1078:b3d5:b0b0:0:0::/96 -Done -``` - -### br rioprf - -Get the preference used when advertising Route Info Options (e.g., for discovered OMR prefixes) in emitted Router Advertisement message. - -```bash -> br rioprf -med -Done -``` - -### br rioprf \ - -Set the preference (which may be 'high', 'med', or 'low') to use when advertising Route Info Options (e.g., for discovered OMR prefixes) in emitted Router Advertisement message. - -```bash -> br rioprf low -Done -``` - ### bufferinfo Show the current message buffer information. - The `total` shows total number of message buffers in pool. - The `free` shows the number of free message buffers. +- The `max-used` shows the maximum number of used buffers at the same time since OT stack initialization or last `bufferinfo reset`. - This is then followed by info about different queues used by OpenThread stack, each line representing info about a queue. - The first number shows number messages in the queue. - The second number shows number of buffers used by all messages in the queue. @@ -428,6 +372,7 @@ Show the current message buffer information. > bufferinfo total: 40 free: 40 +max-used: 5 6lo send: 0 0 0 6lo reas: 0 0 0 ip6: 0 0 0 @@ -439,6 +384,15 @@ application coap: 0 0 0 Done ``` +### bufferinfo reset + +Reset the message buffer counter tracking maximum number buffers in use at the same time. + +```bash +> bufferinfo reset +Done +``` + ### ccathreshold Get the CCA threshold in dBm measured at antenna connector per IEEE 802.15.4 - 2015 section 10.1.4. @@ -672,10 +626,10 @@ Print table of attached children. ```bash > child table -| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt| Extended MAC | -+-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+------------------+ -| 1 | 0xc801 | 240 | 24 | 3 | 131 |1|0|0| 3| 0 | 0 | 4ecede68435358ac | -| 2 | 0xc802 | 240 | 2 | 3 | 131 |0|0|0| 3| 1 | 0 | a672a601d2ce37d8 | +| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt|Suprvsn| Extended MAC | ++-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+-------+------------------+ +| 1 | 0xc801 | 240 | 24 | 3 | 131 |1|0|0| 3| 0 | 0 | 129 | 4ecede68435358ac | +| 2 | 0xc802 | 240 | 2 | 3 | 131 |0|0|0| 3| 1 | 0 | 0 | a672a601d2ce37d8 | Done ``` @@ -689,11 +643,13 @@ Child ID: 1 Rloc: 9c01 Ext Addr: e2b3540590b0fd87 Mode: rn +CSL Synchronized: 1 Net Data: 184 Timeout: 100 Age: 0 Link Quality In: 3 RSSI: -20 +Supervision Interval: 129 Done ``` @@ -789,6 +745,27 @@ Set the Child Supervision Check Timeout value. Done ``` +### childsupervision failcounter + +Get the current value of supervision check timeout failure counter. + +The counter tracks the number of supervision check failures on the child. It is incremented when the child does not hear from its parent within the specified check timeout interval. + +```bash +> childsupervision failcounter +0 +Done +``` + +### childsupervision failcounter reset + +Reset the supervision check timeout failure counter to zero. + +```bash +> childsupervision failcounter reset +Done +``` + ### childtimeout Get the Thread Child Timeout value. @@ -808,6 +785,72 @@ Set the Thread Child Timeout value. Done ``` +### coex + +Get the coex status. + +`OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE` is required. + +```bash +> coex +Enabled +Done +``` + +### coex disable + +Disable coex. + +`OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE` is required. + +```bash +> coex disable +Done +``` + +### coex enable + +Enable coex. + +`OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE` is required. + +```bash +> coex enable +Done +``` + +### coex metrics + +Show coex metrics. + +`OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE` is required. + +```bash +> coex metrics +Stopped: false +Grant Glitch: 0 +Transmit metrics + Request: 0 + Grant Immediate: 0 + Grant Wait: 0 + Grant Wait Activated: 0 + Grant Wait Timeout: 0 + Grant Deactivated During Request: 0 + Delayed Grant: 0 + Average Request To Grant Time: 0 +Receive metrics + Request: 0 + Grant Immediate: 0 + Grant Wait: 0 + Grant Wait Activated: 0 + Grant Wait Timeout: 0 + Grant Deactivated During Request: 0 + Delayed Grant: 0 + Average Request To Grant Time: 0 + Grant None: 0 +Done +``` + ### contextreusedelay Get the CONTEXT_ID_REUSE_DELAY value. @@ -833,6 +876,7 @@ Get the supported counter names. ```bash > counters +br ip mac mle @@ -843,6 +887,11 @@ Done Get the counter value. +Note: + +- `OPENTHREAD_CONFIG_UPTIME_ENABLE` is required for MLE role time tracking in `counters mle` +- `OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE` is required for `counters br` + ```bash > counters mac TxTotal: 10 @@ -887,6 +936,12 @@ Attach Attempts: 1 Partition Id Changes: 1 Better Partition Attach Attempts: 0 Parent Changes: 0 +Time Disabled Milli: 10026 +Time Detached Milli: 6852 +Time Child Milli: 0 +Time Router Milli: 0 +Time Leader Milli: 16195 +Time Tracked Milli: 33073 Done > counters ip TxSuccess: 10 @@ -894,6 +949,18 @@ TxFailed: 0 RxSuccess: 5 RxFailed: 0 Done +> counters br +Inbound Unicast: Packets 4 Bytes 320 +Inbound Multicast: Packets 0 Bytes 0 +Outbound Unicast: Packets 2 Bytes 160 +Outbound Multicast: Packets 0 Bytes 0 +RA Rx: 4 +RA TxSuccess: 2 +RA TxFailed: 0 +RS Rx: 0 +RS TxSuccess: 2 +RS TxFailed: 0 +Done ``` ### counters \ reset @@ -991,6 +1058,44 @@ Set the minimal delay timer (in seconds). Done ``` +### deviceprops + +Get the current device properties. + +```bash +> deviceprops +PowerSupply : external +IsBorderRouter : yes +SupportsCcm : no +IsUnstable : no +WeightAdjustment : 0 +Done +``` + +### deviceprops \ \ \ \ \ + +Set the device properties which are then used to determine and set the Leader Weight. + +- power-supply: `battery`, `external`, `external-stable`, or `external-unstable`. +- weight-adjustment: Valid range is from -16 to +16. Clamped if not within the range. + +```bash +> deviceprops battery 0 0 0 -5 +Done + +> deviceprops +PowerSupply : battery +IsBorderRouter : no +SupportsCcm : no +IsUnstable : no +WeightAdjustment : -5 +Done + +> leaderweight +51 +Done +``` + ### discover \[channel\] Perform an MLE Discovery operation. @@ -1009,7 +1114,20 @@ Done Get the default query config used by DNS client. -The config includes the server IPv6 address and port, response timeout in msec (wait time to rx response), maximum tx attempts before reporting failure, boolean flag to indicate whether the server can resolve the query recursively or not. +The config includes + +- Server IPv6 address and port +- Response timeout in msec (wait time to rx response) +- Maximum tx attempts before reporting failure +- Boolean flag to indicate whether the server can resolve the query recursively or not. +- Service resolution mode which specifies which records to query. Possible options are: + - `srv` : Query for SRV record only. + - `txt` : Query for TXT record only. + - `srv_txt` : Query for both SRV and TXT records in the same message. + - `srv_txt_sep`: Query in parallel for SRV and TXT using separate messages. + - `srv_txt_opt`: Query for TXT/SRV together first, if it fails then query separately. +- Whether to allow/disallow NAT64 address translation during address resolution (requires `OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE`) +- Transport protocol UDP or TCP (requires `OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE`) ```bash > dns config @@ -1017,16 +1135,30 @@ Server: [fd00:0:0:0:0:0:0:1]:1234 ResponseTimeout: 5000 ms MaxTxAttempts: 2 RecursionDesired: no +ServiceMode: srv_txt_opt +Nat64Mode: allow +TransportProtocol: udp Done > ``` -### dns config \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\] +### dns config \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\] \[service mode] Set the default query config. +Service mode specifies which records to query. Possible options are: + +- `def` : Use default option. +- `srv` : Query for SRV record only. +- `txt` : Query for TXT record only. +- `srv_txt` : Query for both SRV and TXT records in the same message. +- `srv_txt_sep`: Query in parallel for SRV and TXT using separate messages. +- `srv_txt_opt`: Query for TXT/SRV together first, if it fails then query separately. + +To set protocol effectively to tcp `OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE` is required. + ```bash -> dns config fd00::1 1234 5000 2 0 +> dns config fd00::1 1234 5000 2 0 srv_txt_sep tcp Done > dns config @@ -1034,6 +1166,9 @@ Server: [fd00:0:0:0:0:0:0:1]:1234 ResponseTimeout: 5000 ms MaxTxAttempts: 2 RecursionDesired: no +ServiceMode: srv_txt_sep +Nat64Mode: allow +TransportProtocol: tcp Done ``` @@ -1051,17 +1186,47 @@ RecursionDesired: yes Done ``` -### dns resolve \ \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\] +### dns resolve \ \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\] \[transport protocol\] Send DNS Query to obtain IPv6 address for given hostname. The parameters after `hostname` are optional. Any unspecified (or zero) value for these optional parameters is replaced by the value from the current default config (`dns config`). +To use tcp, `OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE` is required. + ```bash > dns resolve ipv6.google.com > DNS response for ipv6.google.com - 2a00:1450:401b:801:0:0:0:200e TTL: 300 ``` +The DNS server IP can be an IPv4 address, which will be synthesized to an IPv6 address using the preferred NAT64 prefix from the network data. + +> Note: The command will return `InvalidState` when the DNS server IP is an IPv4 address but the preferred NAT64 prefix is unavailable. + +```bash +> dns resolve example.com 8.8.8.8 +Synthesized IPv6 DNS server address: fdde:ad00:beef:2:0:0:808:808 +DNS response for example.com. - fd4c:9574:3720:2:0:0:5db8:d822 TTL:20456 +Done +``` + +### dns resolve4 \ \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\] + +Send DNS query to obtain IPv4 address for a given hostname and provide the NAT64 synthesized IPv6 addresses for the IPv4 addresses from the query response. + +Requires `OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE`. + +The parameters after `hostname` are optional. Any unspecified (or zero) value for these optional parameters is replaced by the value from the current default config (`dns config`). + +This command requires a NAT64 prefix to be configured and present in Thread Network Data. + +For example, if a NAT64 prefix of `2001:db8:122:344::/96` is used within the Thread mesh, the outputted IPv6 address corresponds to an IPv4 address of `142.250.191.78` for the `ipv4.google.com` host: + +```bash +> dns resolve4 ipv4.google.com +> DNS response for ipv4.google.com - 2001:db8:122:344:0:0:8efa:bf4e TTL: 20456 +``` + ### dns browse \ \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\] Send a browse (service instance enumeration) DNS query to get the list of services for given service-name. @@ -1084,12 +1249,26 @@ instance2 Done ``` +```bash +> dns browse _airplay._tcp.default.service.arpa +DNS browse response for _airplay._tcp.default.service.arpa. +Gabe's Mac mini + Port:7000, Priority:0, Weight:0, TTL:10 + Host:Gabes-Mac-mini.default.service.arpa. + HostAddress:fd97:739d:386a:1:1c2e:d83c:fcbe:9cf4 TTL:10 +Done +``` + +> Note: The DNS server IP can be an IPv4 address, which will be synthesized to an IPv6 address using the preferred NAT64 prefix from the network data. The command will return `InvalidState` when the DNS server IP is an IPv4 address but the preferred NAT64 prefix is unavailable. When testing DNS-SD discovery proxy, the zone is not `local` and instead should be `default.service.arpa`. + ### dns service \ \ \[DNS server IP\] \[DNS server port\] \[response timeout (ms)\] \[max tx attempts\] \[recursion desired (boolean)\] Send a service instance resolution DNS query for a given service instance. Service instance label is provided first, followed by the service name (note that service instance label can contain dot '.' character). The parameters after `service-name` are optional. Any unspecified (or zero) value for these optional parameters is replaced by the value from the current default config (`dns config`). +> Note: The DNS server IP can be an IPv4 address, which will be synthesized to an IPv6 address using the preferred NAT64 prefix from the network data. The command will return `InvalidState` when the DNS server IP is an IPv4 address but the preferred NAT64 prefix is unavailable. + ### dns compression \[enable|disable\] Enable/Disable the "DNS name compression" mode. @@ -1725,6 +1904,141 @@ Set the log level. Done ``` +### meshdiag topology [ip6-addrs][children] + +Discover network topology (list of routers and their connections). + +This command requires `OPENTHREAD_CONFIG_MESH_DIAG_ENABLE` and `OPENTHREAD_FTD`. + +Parameters are optional and indicate additional items to discover. Can be added in any order. + +- `ip6-addrs` to discover the list of IPv6 addresses of every router. +- `children` to discover the child table of every router. + +Output lists all discovered routers. Information per router: + +- Router ID +- RLOC16 +- Extended MAC address +- Thread Version (if known). +- Whether the router is this device is itself (`me`) +- Whether the router is the parent of this device when device is a child (`parent`) +- Whether the router is `leader` +- Whether the router acts as a border router providing external connectivity (`br`) +- List of routers to which this router has a link: + - `3-links`: Router IDs to which this router has a incoming link with link quality 3 + - `2-links`: Router IDs to which this router has a incoming link with link quality 2 + - `1-links`: Router IDs to which this router has a incoming link with link quality 1 + - If a list if empty, it is omitted in the out. +- If `ip6-addrs`, list of IPv6 addresses of the router +- If `children`, list of all children of the router. Information per child: + - RLOC16 + - Incoming Link Quality from perspective of parent to child (zero indicates unknown) + - Child Device mode (`r` rx-on-when-idle, `d` Full Thread Device, `n` Full Network Data, `-` no flags set) + - Whether the child is this device itself (`me`) + - Whether the child acts as a border router providing external connectivity (`br`) + +Discover network topology: + +```bash +> meshdiag topology +id:02 rloc16:0x0800 ext-addr:8aa57d2c603fe16c ver:4 - me - leader + 3-links:{ 46 } +id:46 rloc16:0xb800 ext-addr:fe109d277e0175cc ver:4 + 3-links:{ 02 51 57 } +id:33 rloc16:0x8400 ext-addr:d2e511a146b9e54d ver:4 + 3-links:{ 51 57 } +id:51 rloc16:0xcc00 ext-addr:9aab43ababf05352 ver:4 + 3-links:{ 33 57 } + 2-links:{ 46 } +id:57 rloc16:0xe400 ext-addr:dae9c4c0e9da55ff ver:4 + 3-links:{ 46 51 } + 1-links:{ 33 } +Done +``` + +Discover network topology with router's IPv6 addresses and children: + +```bash +> meshdiag topology children ip6-addrs +id:62 rloc16:0xf800 ext-addr:ce349873897233a5 ver:4 - me - br + 3-links:{ 46 } + ip6-addrs: + fdde:ad00:beef:0:0:ff:fe00:f800 + fdde:ad00:beef:0:211d:39e9:6b2e:4ad1 + fe80:0:0:0:cc34:9873:8972:33a5 + children: none +id:02 rloc16:0x0800 ext-addr:8aa57d2c603fe16c ver:4 - leader - br + 3-links:{ 46 51 } + ip6-addrs: + fdde:ad00:beef:0:0:ff:fe00:fc00 + fdde:ad00:beef:0:0:ff:fe00:800 + fdde:ad00:beef:0:8a36:a3eb:47ae:a9b0 + fe80:0:0:0:88a5:7d2c:603f:e16c + children: + rloc16:0x0803 lq:3, mode:rn + rloc16:0x0804 lq:3, mode:rdn +id:33 rloc16:0x8400 ext-addr:d2e511a146b9e54d ver:4 + 3-links:{ 57 } + ip6-addrs: + fdde:ad00:beef:0:0:ff:fe00:8400 + fdde:ad00:beef:0:824:a126:cf19:a9f4 + fe80:0:0:0:d0e5:11a1:46b9:e54d + children: none +id:51 rloc16:0xcc00 ext-addr:9aab43ababf05352 ver:4 + 3-links:{ 02 46 57 } + ip6-addrs: + fdde:ad00:beef:0:0:ff:fe00:cc00 + fdde:ad00:beef:0:2986:bba3:12d0:1dd2 + fe80:0:0:0:98ab:43ab:abf0:5352 + children: none +id:57 rloc16:0xe400 ext-addr:dae9c4c0e9da55ff ver:4 + 3-links:{ 33 51 } + ip6-addrs: + fdde:ad00:beef:0:0:ff:fe00:e400 + fdde:ad00:beef:0:87d0:550:bc18:9920 + fe80:0:0:0:d8e9:c4c0:e9da:55ff + children: + rloc16:0xe402 lq:3, mode:rn - br + rloc16:0xe403 lq:3, mode:rn +id:46 rloc16:0xb800 ext-addr:fe109d277e0175cc ver:4 + 3-links:{ 02 51 62 } + ip6-addrs: + fdde:ad00:beef:0:0:ff:fe00:b800 + fdde:ad00:beef:0:df4d:2994:d85c:c337 + fe80:0:0:0:fc10:9d27:7e01:75cc + children: none +Done +``` + +Discover network topology with children: + +```bash +> meshdiag topology children +id:02 rloc16:0x0800 ext-addr:8aa57d2c603fe16c ver:4 - parent - leader - br + 3-links:{ 46 51 } + children: + rloc16:0x0803 lq:0, mode:rn + rloc16:0x0804 lq:0, mode:rdn - me +id:46 rloc16:0xb800 ext-addr:fe109d277e0175cc ver:4 + 3-links:{ 02 51 62 } + children: none +id:33 rloc16:0x8400 ext-addr:d2e511a146b9e54d ver:4 + 3-links:{ 57 } + children: none +id:51 rloc16:0xcc00 ext-addr:9aab43ababf05352 ver:4 + 3-links:{ 02 46 57 } + children: none +id:57 rloc16:0xe400 ext-addr:dae9c4c0e9da55ff ver:4 + 3-links:{ 33 51 } + children: + rloc16:0xe402 lq:3, mode:rn - br + rloc16:0xe403 lq:3, mode:rn +id:62 rloc16:0xf800 ext-addr:ce349873897233a5 ver:4 - br + 3-links:{ 46 } + children: none +``` + ### mliid \ Set the Mesh Local IID. @@ -1835,6 +2149,136 @@ This command is only available when device supports more than one radio link. Done ``` +### nat64 cidr + +Gets the IPv4 configured CIDR in the NAT64 translator. + +`OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is required. + +```bash +> nat64 cidr +192.168.255.0/24 +Done +``` + +### nat64 disable + +Disable NAT64 functions, including the translator and the prefix publishing. + +This command will reset the mapping table in the translator (if NAT64 translator is enabled in the build). + +`OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` or `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` are required. + +```bash +> nat64 disable +Done +``` + +### nat64 enable + +Enable NAT64 functions, including the translator and the prefix publishing. + +This command can be called anytime. + +`OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` or `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` are required. + +```bash +> nat64 enable +Done +``` + +### nat64 state + +Gets the state of NAT64 functions. + +Possible results for prefix manager are (`OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is required): + +- `Disabled`: NAT64 prefix manager is disabled. +- `NotRunning`: NAT64 prefix manager is enabled, but is not running, probably bacause the routing manager is disabled. +- `Idle`: NAT64 prefix manager is enabled and is running, but is not publishing a NAT64 prefix. Usually when there is another border router publishing a NAT64 prefix with higher priority. +- `Active`: NAT64 prefix manager is enabled, running and publishing a NAT64 prefix. + +Possible results for NAT64 translator are (`OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is required): + +- `Disabled`: NAT64 translator is disabled. +- `NotRunning`: NAT64 translator is enabled, but is not translating packets, probably bacause it is not configued with a NAT64 prefix or a CIDR for NAT64. +- `Active`: NAT64 translator is enabled and is translating packets. + +`OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` or `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` are required. + +```bash +> nat64 state +PrefixManager: NotRunning +Translator: NotRunning +Done + +> nat64 state +PrefixManager: Idle +Translator: NotRunning +Done + +> nat64 state +PrefixManager: Active +Translator: Active +Done +``` + +### nat64 mappings + +Get the NAT64 translator mappings. + +`OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is required. + +```bash +> nat64 mappings +| | Address | | 4 to 6 | 6 to 4 | ++----------+---------------------------+--------+--------------+--------------+ +| ID | IPv6 | IPv4 | Expiry | Pkts | Bytes | Pkts | Bytes | ++----------+------------+--------------+--------+------+-------+------+-------+ +| 00021cb9 | fdc7::df79 | 192.168.64.2 | 7196s | 6 | 456 | 11 | 1928 | +| | TCP | 0 | 0 | 0 | 0 | +| | UDP | 1 | 136 | 16 | 1608 | +| | ICMP | 5 | 320 | 5 | 320 | +``` + +### nat64 counters + +Get the NAT64 translator packet and error counters. + +`OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is required. + +```bash +> nat64 counters +| | 4 to 6 | 6 to 4 | ++---------------+-------------------------+-------------------------+ +| Protocol | Pkts | Bytes | Pkts | Bytes | ++---------------+----------+--------------+----------+--------------+ +| Total | 11 | 704 | 11 | 704 | +| TCP | 0 | 0 | 0 | 0 | +| UDP | 0 | 0 | 0 | 0 | +| ICMP | 11 | 704 | 11 | 704 | +| Errors | Pkts | Pkts | ++---------------+-------------------------+-------------------------+ +| Total | 8 | 4 | +| Illegal Pkt | 0 | 0 | +| Unsup Proto | 0 | 0 | +| No Mapping | 2 | 0 | +Done +``` + +### neighbor linkquality + +Print link quality info for all neighbors. + +```bash +> neighbor linkquality +| RLOC16 | Extended MAC | Frame Error | Msg Error | Avg RSS | Last RSS | Age | ++--------+------------------+-------------+-----------+---------+----------+-------+ +| 0xe800 | 9e2fa4e1b84f92db | 0.00 % | 0.00 % | -46 | -48 | 1 | +| 0xc001 | 0ad7ed6beaa6016d | 4.67 % | 0.08 % | -68 | -72 | 10 | +Done +``` + ### neighbor list List RLOC16 of neighbors. @@ -1859,6 +2303,43 @@ Print table of neighbors. Done ``` +### neighbor conntime + +Print connection time and age of neighbors. + +The table provides the following info per neighbor: + +- RLOC16 +- Extended MAC address +- Age (seconds since last heard from neighbor) +- Connection time (seconds since link establishment with neighbor) + +Duration intervals are formatted as `::` for hours, minutes, and seconds if the duration is less than one day. If the duration is longer than one day, the format is `
d.::`. + +```bash +> neighbor conntime +| RLOC16 | Extended MAC | Last Heard (Age) | Connection Time | ++--------+------------------+------------------+------------------+ +| 0x8401 | 1a28be396a14a318 | 00:00:13 | 00:07:59 | +| 0x5c00 | 723ebf0d9eba3264 | 00:00:03 | 00:11:27 | +| 0xe800 | ce53628a1e3f5b3c | 00:00:02 | 00:00:15 | +Done +``` + +### neighbor conntime list + +Print connection time and age of neighbors. + +This command is similar to `neighbor conntime`, but it displays the information in a list format. The age and connection time are both displayed in seconds. + +```bash +> neighbor conntime list +0x8401 1a28be396a14a318 age:63 conn-time:644 +0x5c00 723ebf0d9eba3264 age:23 conn-time:852 +0xe800 ce53628a1e3f5b3c age:23 conn-time:180 +Done +``` + ### netstat List all UDP sockets. @@ -1995,7 +2476,7 @@ Done Get the diagnostic information for a Thread Router as parent. -Note: When operating as a Thread Router, this command will return the cached information from when the device was previously attached as a Thread Child. Returning cached information is necessary to support the Thread Test Harness - Test Scenario 8.2.x requests the former parent (i.e. Joiner Router's) MAC address even if the device has already promoted to a router. +Note: When operating as a Thread Router when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled, this command will return the cached information from when the device was previously attached as a Thread Child. Returning cached information is necessary to support the Thread Test Harness - Test Scenario 8.2.x requests the former parent (i.e. Joiner Router's) MAC address even if the device has already promoted to a router. ```bash > parent @@ -2004,9 +2485,17 @@ Rloc: 5c00 Link Quality In: 3 Link Quality Out: 3 Age: 20 +Version: 4 Done ``` +Note: When `OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE` is enabled, this command will return two extra lines with information relevant for CSL Receiver operation. + +```bash +CSL clock accuracy: 20 +CSL uncertainty: 5 +``` + ### parentpriority Get the assigned parent priority value, -2 means not assigned. @@ -2059,10 +2548,11 @@ Set the preferred Thread Leader Partition ID. Done ``` -### ping \[-I source\] \ \[size\] \[count\] \[interval\] \[hoplimit\] \[timeout\] +### ping \[async\] \[-I source\] \ \[size\] \[count\] \[interval\] \[hoplimit\] \[timeout\] Send an ICMPv6 Echo Request. +- async: Use the non-blocking mode. New commands are allowed before the ping process terminates. - source: The source IPv6 address of the echo request. - size: The number of data bytes to be sent. - count: The number of ICMPv6 Echo Requests to be sent. @@ -2082,6 +2572,18 @@ Done Done ``` +The address can be an IPv4 address, which will be synthesized to an IPv6 address using the preferred NAT64 prefix from the network data. + +> Note: The command will return `InvalidState` when the preferred NAT64 prefix is unavailable. + +```bash +> ping 172.17.0.1 +Pinging synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1 +> 16 bytes from fdde:ad00:beef:2:0:0:ac11:1: icmp_seq=5 hlim=64 time=0ms +1 packets transmitted, 1 packets received. Packet loss = 0.0%. Round-trip min/avg/max = 0/0.0/0 ms. +Done +``` + ### ping stop Stop sending ICMPv6 Echo Requests. @@ -2091,9 +2593,19 @@ Stop sending ICMPv6 Echo Requests. Done ``` +### platform + +Print the current platform + +```bash +> platform +NRF52840 +Done +``` + ### pollperiod -Get the customized data poll period of sleepy end device (milliseconds). Only for certification test +Get the customized data poll period of sleepy end device (milliseconds). Only for certification test. ```bash > pollperiod @@ -2103,13 +2615,23 @@ Done ### pollperiod \ -Set the customized data poll period for sleepy end device (milliseconds >= 10ms). Only for certification test +Set the customized data poll period for sleepy end device (milliseconds >= 10ms). Only for certification test. ```bash > pollperiod 10 Done ``` +### pskc + +Get pskc in hex format. + +```bash +> pskc +00000000000000000000000000000000 +Done +``` + ### pskc [-p] \|\ With `-p` generate pskc from \ (UTF-8 encoded) together with **current** network name and extended PAN ID, otherwise set pskc as \ (hex format). @@ -2121,6 +2643,29 @@ Done Done ``` +### pskcref + +Get pskc key reference. + +`OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is required. + +```bash +> pskcref +0x80000000 +Done +``` + +### pskcref \ + +Set pskc key reference as \. + +`OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE` is required. + +```bash +> pskcref 0x20017 +Done +``` + ### preferrouterid \ Prefer a Router ID when solicit router id from Leader. @@ -2487,6 +3032,25 @@ Set the ROUTER_UPGRADE_THRESHOLD value. Done ``` +### childrouterlinks + +Get the MLE_CHILD_ROUTER_LINKS value. + +```bash +> childrouterlinks +16 +Done +``` + +### childrouterlinks \ + +Set the MLE_CHILD_ROUTER_LINKS value. + +```bash +> childrouterlinks 16 +Done +``` + ### scan \[channel\] Perform an IEEE 802.15.4 Active Scan. @@ -2834,6 +3398,63 @@ Done > ``` +### vendor name + +This command requires `OPENTHREAD_FTD` or `OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE`. + +Get the vendor name. + +```bash +> vendor name +nest +Done +``` + +Set the vendor name (requires `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE`). + +```bash +> vendor name nest +Done +``` + +### vendor model + +This command requires `OPENTHREAD_FTD` or `OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE`. + +Get the vendor model. + +```bash +> vendor model +Hub Max +Done +``` + +Set the vendor model (requires `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE`). + +```bash +> vendor model Hub\ Max +Done +``` + +### vendor swversion + +This command requires `OPENTHREAD_FTD` or `OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE`. + +Get the vendor SW version. + +```bash +> vendor swversion +Marble3.5.1 +Done +``` + +Set the vendor SW version (requires `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE`). + +```bash +> vendor swversion Marble3.5.1 +Done +``` + ### version Print the build version information. diff --git a/src/cli/README_BR.md b/src/cli/README_BR.md new file mode 100644 index 00000000000..90a50b6197d --- /dev/null +++ b/src/cli/README_BR.md @@ -0,0 +1,208 @@ +# OpenThread CLI - Border Router (BR) + +## Command List + +Usage : `br [command] ...` + +- [counters](#counters) +- [disable](#disable) +- [enable](#enable) +- [help](#help) +- [nat64prefix](#nat64prefix) +- [omrprefix](#omrprefix) +- [onlinkprefix](#onlinkprefix) +- [prefixtable](#prefixtable) +- [rioprf](#rioprf) +- [state](#state) + +## Command Details + +### help + +Usage: `br help` + +Print BR command help menu. + +```bash +> br help +counters +disable +enable +omrprefix +onlinkprefix +prefixtable +rioprf +state +Done +``` + +### enable + +Usage: `br enable` + +Enable the Border Routing functionality. + +```bash +> br enable +Done +``` + +### disable + +Usage: `br disable` + +Disable the Border Routing functionality. + +```bash +> br disable +Done +``` + +### state + +Usage: `br state` + +Get the Border Routing state: + +- `uninitialized`: Routing Manager is uninitialized. +- `disabled`: Routing Manager is initialized but disabled. +- `stopped`: Routing Manager in initialized and enabled but currently stopped. +- `running`: Routing Manager is initialized, enabled, and running. + +```bash +> br state +running +``` + +### counters + +Usage : `br counters` + +Get the Border Router counter. + +```bash +> br counters +Inbound Unicast: Packets 4 Bytes 320 +Inbound Multicast: Packets 0 Bytes 0 +Outbound Unicast: Packets 2 Bytes 160 +Outbound Multicast: Packets 0 Bytes 0 +RA Rx: 4 +RA TxSuccess: 2 +RA TxFailed: 0 +RS Rx: 0 +RS TxSuccess: 2 +RS TxFailed: 0 +Done +``` + +### omrprefix + +Usage: `br omrprefix [local|favored]` + +Get local or favored or both off-mesh-routable prefixes of the Border Router. + +```bash +> br omrprefix +Local: fdfc:1ff5:1512:5622::/64 +Favored: fdfc:1ff5:1512:5622::/64 prf:low +Done + +> br omrprefix favored +fdfc:1ff5:1512:5622::/64 prf:low +Done + +> br omrprefix local +fdfc:1ff5:1512:5622::/64 +Done +``` + +### onlinkprefix + +Usage: `br onlinkprefix [local|favored]` + +Get local or favored or both on-link prefixes of the Border Router. + +```bash +> br onlinkprefix +Local: fd41:2650:a6f5:0::/64 +Favored: 2600::0:1234:da12::/64 +Done + +> br onlinkprefix favored +2600::0:1234:da12::/64 +Done + +> br onlinkprefix local +fd41:2650:a6f5:0::/64 +Done +``` + +### nat64prefix + +Usage: `br nat64prefix [local|favored]` + +Get local or favored or both NAT64 prefixes of the Border Router. + +`OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is required. + +```bash +> br nat64prefix +Local: fd14:1078:b3d5:b0b0:0:0::/96 +Favored: fd14:1078:b3d5:b0b0:0:0::/96 prf:low +Done + +> br nat64prefix favored +fd14:1078:b3d5:b0b0:0:0::/96 prf:low +Done + +> br nat64prefix +fd14:1078:b3d5:b0b0:0:0::/96 +Done +``` + +### prefixtable + +Usage: `br prefixtable` + +Get the discovered prefixes by Border Routing Manager on the infrastructure link. + +```bash +> br prefixtable +prefix:fd00:1234:5678:0::/64, on-link:no, ms-since-rx:29526, lifetime:1800, route-prf:med, router:ff02:0:0:0:0:0:0:1 +prefix:1200:abba:baba:0::/64, on-link:yes, ms-since-rx:29527, lifetime:1800, preferred:1800, router:ff02:0:0:0:0:0:0:1 +Done +``` + +### rioprf + +Usage: `br rioprf` + +Get the preference used when advertising Route Info Options (e.g., for discovered OMR prefixes) in emitted Router Advertisement message. + +```bash +> br rioprf +med +Done +``` + +### rioprf \ + +Usage: `br rioprf high|med|low` + +Set the preference (which may be 'high', 'med', or 'low') to use when advertising Route Info Options (e.g., for discovered OMR prefixes) in emitted Router Advertisement message. + +```bash +> br rioprf low +Done +``` + +### rioprf clear + +Usage: `br rioprf clear` + +Clear a previously set preference value for advertising Route Info Options (e.g., for discovered OMR prefixes) in emitted Router Advertisement message. When cleared BR will use device's role to determine the RIO preference: Medium preference when in router/leader role and low preference when in child role. + +```bash +> br rioprf clear +Done +``` diff --git a/src/cli/README_DATASET.md b/src/cli/README_DATASET.md index 87cd4d21c08..df1b274c8a1 100644 --- a/src/cli/README_DATASET.md +++ b/src/cli/README_DATASET.md @@ -36,15 +36,15 @@ The Pending Operational Dataset is used to communicate changes to the Active Ope Done > dataset Active Timestamp: 1 - Channel: 13 + Channel: 15 Channel Mask: 0x07fff800 - Ext PAN ID: d63e8e3e495ebbc3 - Mesh Local Prefix: fd3d:b50b:f96d:722d::/64 - Network Key: dfd34f0f05cad978ec4e32b0413038ff - Network Name: OpenThread-8f28 - PAN ID: 0x8f28 - PSKc: c23a76e98f1a6483639b1ac1271e2e27 - Security Policy: 0, onrc + Ext PAN ID: 39758ec8144b07fb + Mesh Local Prefix: fdf1:f1ad:d079:7dc0::/64 + Network Key: f366cec7a446bab978d90d27abe38f23 + Network Name: OpenThread-5938 + PAN ID: 0x5938 + PSKc: 3ca67c969efb0d0c74a4d8ee923b576c + Security Policy: 672 onrc Done ``` @@ -95,15 +95,15 @@ After the device successfully attaches to a Thread network, the device will retr ```bash > dataset active Active Timestamp: 1 - Channel: 13 + Channel: 15 Channel Mask: 0x07fff800 - Ext PAN ID: d63e8e3e495ebbc3 - Mesh Local Prefix: fd3d:b50b:f96d:722d::/64 - Network Key: dfd34f0f05cad978ec4e32b0413038ff - Network Name: OpenThread-8f28 - PAN ID: 0x8f28 - PSKc: c23a76e98f1a6483639b1ac1271e2e27 - Security Policy: 0, onrc + Ext PAN ID: 39758ec8144b07fb + Mesh Local Prefix: fdf1:f1ad:d079:7dc0::/64 + Network Key: f366cec7a446bab978d90d27abe38f23 + Network Name: OpenThread-5938 + PAN ID: 0x5938 + PSKc: 3ca67c969efb0d0c74a4d8ee923b576c + Security Policy: 672 onrc Done ``` @@ -129,6 +129,7 @@ After the device successfully attaches to a Thread network, the device will retr - [pendingtimestamp](#pendingtimestamp) - [pskc](#pskc) - [securitypolicy](#securitypolicy) +- [tlvs](#tlvs) ## Command Details @@ -160,6 +161,8 @@ pending pendingtimestamp pskc securitypolicy +set +tlvs Done ``` @@ -172,15 +175,15 @@ Print Active Operational Dataset in human-readable form. ```bash > dataset active Active Timestamp: 1 -Channel: 13 +Channel: 15 Channel Mask: 0x07fff800 -Ext PAN ID: d63e8e3e495ebbc3 -Mesh Local Prefix: fd3d:b50b:f96d:722d::/64 -Network Key: dfd34f0f05cad978ec4e32b0413038ff -Network Name: OpenThread-8f28 -PAN ID: 0x8f28 -PSKc: c23a76e98f1a6483639b1ac1271e2e27 -Security Policy: 0, onrc +Ext PAN ID: 39758ec8144b07fb +Mesh Local Prefix: fdf1:f1ad:d079:7dc0::/64 +Network Key: f366cec7a446bab978d90d27abe38f23 +Network Name: OpenThread-5938 +PAN ID: 0x5938 +PSKc: 3ca67c969efb0d0c74a4d8ee923b576c +Security Policy: 672 onrc Done ``` @@ -188,7 +191,7 @@ Print Active Operational Dataset as hex-encoded TLVs. ```bash > dataset active -x -0e080000000000010000000300001035060004001fffe002084eb74ab03c56e6d00708fdc7fe165c83a67805108e2104f183e698da87e96efc1e45aa51030f4f70656e5468726561642d383631310102861104108d6273023d82c841eff0e68db86f35740c030000ff +0e080000000000010000000300000f35060004001fffe0020839758ec8144b07fb0708fdf1f1add0797dc00510f366cec7a446bab978d90d27abe38f23030f4f70656e5468726561642d353933380102593804103ca67c969efb0d0c74a4d8ee923b576c0c0402a0f7f8 Done ``` @@ -431,17 +434,17 @@ Print Pending Operational Dataset in human-readable form. ```bash > dataset pending Pending Timestamp: 2 -Active Timestamp: 15 -Channel: 16 +Active Timestamp: 1 +Channel: 26 Channel Mask: 0x07fff800 Delay: 58706 -Ext PAN ID: d63e8e3e495ebbc3 -Mesh Local Prefix: fd3d:b50b:f96d:722d::/64 -Network Key: dfd34f0f05cad978ec4e32b0413038ff -Network Name: OpenThread-8f28 -PAN ID: 0x8f28 -PSKc: c23a76e98f1a6483639b1ac1271e2e27 -Security Policy: 0, onrc +Ext PAN ID: a74182f4d3f4de41 +Mesh Local Prefix: fd46:c1b9:e159:5574::/64 +Network Key: ed916e454d96fd00184f10a6f5c9e1d3 +Network Name: OpenThread-bff8 +PAN ID: 0xbff8 +PSKc: 264f78414adc683191863d968f72d1b7 +Security Policy: 672 onrc Done ``` @@ -449,7 +452,7 @@ Print Pending Operational Dataset as hex-encoded TLVs. ```bash > dataset pending -x -0e080000000000010000000300001035060004001fffe002084eb74ab03c56e6d00708fdc7fe165c83a67805108e2104f183e698da87e96efc1e45aa51030f4f70656e5468726561642d383631310102861104108d6273023d82c841eff0e68db86f35740c030000ff +0e0800000000000100003308000000000002000034040000b512000300001a35060004001fffe00208a74182f4d3f4de410708fd46c1b9e15955740510ed916e454d96fd00184f10a6f5c9e1d3030f4f70656e5468726561642d626666380102bff80410264f78414adc683191863d968f72d1b70c0402a0f7f8 Done ``` @@ -530,13 +533,38 @@ Usage: `dataset set ` Set the Active Operational Dataset using hex-encoded TLVs. ```bash -dataset set active 0e080000000000010000000300001035060004001fffe002084eb74ab03c56e6d00708fdc7fe165c83a67805108e2104f183e698da87e96efc1e45aa51030f4f70656e5468726561642d383631310102861104108d6273023d82c841eff0e68db86f35740c030000ff +> dataset set active 0e080000000000010000000300000f35060004001fffe0020839758ec8144b07fb0708fdf1f1add0797dc00510f366cec7a446bab978d90d27abe38f23030f4f70656e5468726561642d353933380102593804103ca67c969efb0d0c74a4d8ee923b576c0c0402a0f7f8 Done ``` Set the Pending Operational Dataset using hex-encoded TLVs. ```bash -dataset set pending 0e080000000000010000000300001035060004001fffe002084eb74ab03c56e6d00708fdc7fe165c83a67805108e2104f183e698da87e96efc1e45aa51030f4f70656e5468726561642d383631310102861104108d6273023d82c841eff0e68db86f35740c030000ff +> dataset set pending 0e0800000000000100003308000000000002000034040000b512000300001a35060004001fffe00208a74182f4d3f4de410708fd46c1b9e15955740510ed916e454d96fd00184f10a6f5c9e1d3030f4f70656e5468726561642d626666380102bff80410264f78414adc683191863d968f72d1b70c0402a0f7f8 +Done +``` + +### tlvs + +Usage: `dataset tlvs` + +Convert the Operational Dataset to hex-encoded TLVs. + +```bash +> dataset +Active Timestamp: 1 +Channel: 22 +Channel Mask: 0x07fff800 +Ext PAN ID: d196fa2040e973b6 +Mesh Local Prefix: fdbb:c310:c48f:3a39::/64 +Network Key: 9929154dbc363218bcd22f907caf5c15 +Network Name: OpenThread-de2b +PAN ID: 0xde2b +PSKc: 15b2c16f7ba92ed4bc7b1ee054f1553f +Security Policy: 672 onrc +Done + +> dataset tlvs +0e080000000000010000000300001635060004001fffe00208d196fa2040e973b60708fdbbc310c48f3a3905109929154dbc363218bcd22f907caf5c15030f4f70656e5468726561642d646532620102de2b041015b2c16f7ba92ed4bc7b1ee054f1553f0c0402a0f7f8 Done ``` diff --git a/src/cli/README_HISTORY.md b/src/cli/README_HISTORY.md index e3ea55dc801..268cf7d242d 100644 --- a/src/cli/README_HISTORY.md +++ b/src/cli/README_HISTORY.md @@ -17,6 +17,7 @@ Usage : `history [command] ...` - [netinfo](#netinfo) - [prefix](#prefix) - [route](#route) +- [router](#router) - [rx](#rx) - [rxtx](#rxtx) - [tx](#tx) @@ -56,6 +57,7 @@ neighbor netinfo prefix route +router rx rxtx tx @@ -298,7 +300,7 @@ Print the history as a list. 00:06:01.711 -> event:Added prefix:fd00:dead:beef:1::/64 flags:paros pref:med rloc16:0x8800 ``` -### prefix +### route Usage `history route [list] []` @@ -337,6 +339,82 @@ Print the history as a list (last two entries). Done ``` +### router + +Usage `history router [list] []` + +Print the route table history. Each item provides: + +- Event (`Added`, `Removed`, `NextHopChnaged`, `CostChanged`) +- Router ID and RLOC16 of router +- Next Hop (Router ID and RLOC16) - `none` if no next hop. +- Path cost (old `->` new) - `inf` to indicate infinite path cost. + +Print the history as a table. + +```bash +> history router +| Age | Event | ID (RLOC16) | Next Hop | Path Cost | ++----------------------+----------------+-------------+-------------+------------+ +| 00:00:05.258 | NextHopChanged | 7 (0x1c00) | 34 (0x8800) | inf -> 3 | +| 00:00:08.604 | NextHopChanged | 34 (0x8800) | 34 (0x8800) | inf -> 2 | +| 00:00:08.604 | Added | 7 (0x1c00) | none | inf -> inf | +| 00:00:11.931 | Added | 34 (0x8800) | none | inf -> inf | +| 00:00:14.948 | Removed | 59 (0xec00) | none | inf -> inf | +| 00:00:14.948 | Removed | 54 (0xd800) | none | inf -> inf | +| 00:00:14.948 | Removed | 34 (0x8800) | none | inf -> inf | +| 00:00:14.948 | Removed | 7 (0x1c00) | none | inf -> inf | +| 00:00:54.795 | NextHopChanged | 59 (0xec00) | 34 (0x8800) | 1 -> 5 | +| 00:02:33.735 | NextHopChanged | 54 (0xd800) | none | 15 -> inf | +| 00:03:10.915 | CostChanged | 54 (0xd800) | 34 (0x8800) | 13 -> 15 | +| 00:03:45.716 | NextHopChanged | 54 (0xd800) | 34 (0x8800) | 15 -> 13 | +| 00:03:46.188 | CostChanged | 54 (0xd800) | 59 (0xec00) | 13 -> 15 | +| 00:04:19.124 | CostChanged | 54 (0xd800) | 59 (0xec00) | 11 -> 13 | +| 00:04:52.008 | CostChanged | 54 (0xd800) | 59 (0xec00) | 9 -> 11 | +| 00:05:23.176 | CostChanged | 54 (0xd800) | 59 (0xec00) | 7 -> 9 | +| 00:05:51.081 | CostChanged | 54 (0xd800) | 59 (0xec00) | 5 -> 7 | +| 00:06:48.721 | CostChanged | 54 (0xd800) | 59 (0xec00) | 3 -> 5 | +| 00:07:13.792 | NextHopChanged | 54 (0xd800) | 59 (0xec00) | 1 -> 3 | +| 00:09:28.681 | NextHopChanged | 7 (0x1c00) | 34 (0x8800) | inf -> 3 | +| 00:09:31.882 | Added | 7 (0x1c00) | none | inf -> inf | +| 00:09:51.240 | NextHopChanged | 54 (0xd800) | 54 (0xd800) | inf -> 1 | +| 00:09:54.204 | Added | 54 (0xd800) | none | inf -> inf | +| 00:10:20.645 | NextHopChanged | 34 (0x8800) | 34 (0x8800) | inf -> 2 | +| 00:10:24.242 | NextHopChanged | 59 (0xec00) | 59 (0xec00) | inf -> 1 | +| 00:10:24.242 | Added | 34 (0x8800) | none | inf -> inf | +| 00:10:41.900 | NextHopChanged | 59 (0xec00) | none | 1 -> inf | +| 00:10:42.480 | Added | 3 (0x0c00) | 3 (0x0c00) | inf -> inf | +| 00:10:43.614 | Added | 59 (0xec00) | 59 (0xec00) | inf -> 1 | +Done +``` + +Print the history as a list (last 20 entries). + +```bash +> history router list 20 +00:00:06.959 -> event:NextHopChanged router:7(0x1c00) nexthop:34(0x8800) old-cost:inf new-cost:3 +00:00:10.305 -> event:NextHopChanged router:34(0x8800) nexthop:34(0x8800) old-cost:inf new-cost:2 +00:00:10.305 -> event:Added router:7(0x1c00) nexthop:none old-cost:inf new-cost:inf +00:00:13.632 -> event:Added router:34(0x8800) nexthop:none old-cost:inf new-cost:inf +00:00:16.649 -> event:Removed router:59(0xec00) nexthop:none old-cost:inf new-cost:inf +00:00:16.649 -> event:Removed router:54(0xd800) nexthop:none old-cost:inf new-cost:inf +00:00:16.649 -> event:Removed router:34(0x8800) nexthop:none old-cost:inf new-cost:inf +00:00:16.649 -> event:Removed router:7(0x1c00) nexthop:none old-cost:inf new-cost:inf +00:00:56.496 -> event:NextHopChanged router:59(0xec00) nexthop:34(0x8800) old-cost:1 new-cost:5 +00:02:35.436 -> event:NextHopChanged router:54(0xd800) nexthop:none old-cost:15 new-cost:inf +00:03:12.616 -> event:CostChanged router:54(0xd800) nexthop:34(0x8800) old-cost:13 new-cost:15 +00:03:47.417 -> event:NextHopChanged router:54(0xd800) nexthop:34(0x8800) old-cost:15 new-cost:13 +00:03:47.889 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:13 new-cost:15 +00:04:20.825 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:11 new-cost:13 +00:04:53.709 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:9 new-cost:11 +00:05:24.877 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:7 new-cost:9 +00:05:52.782 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:5 new-cost:7 +00:06:50.422 -> event:CostChanged router:54(0xd800) nexthop:59(0xec00) old-cost:3 new-cost:5 +00:07:15.493 -> event:NextHopChanged router:54(0xd800) nexthop:59(0xec00) old-cost:1 new-cost:3 +00:09:30.382 -> event:NextHopChanged router:7(0x1c00) nexthop:34(0x8800) old-cost:inf new-cost:3 +Done +``` + ### rx Usage `history rx [list] []` diff --git a/src/cli/README_NETDATA.md b/src/cli/README_NETDATA.md index 68c3b43f78e..5fff19f09ea 100644 --- a/src/cli/README_NETDATA.md +++ b/src/cli/README_NETDATA.md @@ -142,6 +142,8 @@ After the device successfully attaches to a Thread network, the device will retr ## Command List - [help](#help) +- [length](#length) +- [maxlength](#maxlength) - [publish](#publish) - [register](#register) - [show](#show) @@ -158,7 +160,8 @@ Print netdata help menu. ```bash > netdata help -help +length +maxlength publish register show @@ -167,6 +170,41 @@ unpublish Done ``` +### length + +Usage: `netdata length` + +Get the current length of (number of bytes) Partition's Thread Network Data. + +```bash +> netdata length +23 +Done +``` + +### maxlength + +Usage: `netdata maxlength` + +Get the maximum observed length of the Thread Network Data since OT stack initialization or since the last call to `netdata maxlength reset`. + +```bash +> netdata maxlength +40 +Done +``` + +### maxlength reset + +Usage: `netdata maxlength reset` + +Reset the tracked maximum length of the Thread Network Data. + +```bash +> netdata maxlength reset +Done +``` + ### publish The Network Data Publisher provides mechanisms to limit the number of similar Service and/or Prefix (on-mesh prefix or external route) entries in the Thread Network Data by monitoring the Network Data and managing if or when to add or remove entries. @@ -231,6 +269,21 @@ Publish an external route entry. Done ``` +### publish replace \ \ [sn][prf] + +Replace a previously published external route entry. + +If there is no previously published external route matching old prefix, this command behaves similarly to `netdata publish route`. If there is a previously published route entry, it will be replaced with the new prefix. In particular, if the old prefix was already added in the Network Data, the change to the new prefix is immediately reflected in the Network Data (i.e., old prefix is removed and the new prefix is added in the same Network Data registration request to leader). This ensures that route entries in the Network Data are not abruptly removed. + +- s: Stable flag +- n: NAT64 flag +- prf: Preference, which may be: 'high', 'med', or 'low'. + +```bash +> netdata publish replace ::/0 fd00:1234:5678::/64 s high +Done +``` + ### register Usage: `netdata register` @@ -246,14 +299,61 @@ Done Usage: `netdata show [local] [-x]` +Print entries in Network Data, on-mesh prefixes, external routes, services, and 6LoWPAN context information. + +On-mesh prefixes are listed under `Prefixes` header: + +- The on-mesh prefix +- Flags + - p: Preferred flag + - a: Stateless IPv6 Address Autoconfiguration flag + - d: DHCPv6 IPv6 Address Configuration flag + - c: DHCPv6 Other Configuration flag + - r: Default Route flag + - o: On Mesh flag + - s: Stable flag + - n: Nd Dns flag + - D: Domain Prefix flag (only available for Thread 1.2). +- Preference `high`, `med`, or `low` +- RLOC16 of device which added the on-mesh prefix + +External Routes are listed under `Routes` header: + +- The route prefix +- Flags + - s: Stable flag + - n: NAT64 flag +- Preference `high`, `med`, or `low` +- RLOC16 of device which added the route prefix + +Service entries are listed under `Services` header: + +- Enterprise number +- Service data (as hex bytes) +- Server data (as hex bytes) +- Flags + - s: Stable flag +- RLOC16 of devices which added the service entry + +6LoWPAN Context IDs are listed under `Contexts` header: + +- The prefix +- Context ID +- Compress flag (`c` if marked or `-` otherwise). + Print Network Data received from the Leader. ```bash > netdata show Prefixes: -fd00:dead:beef:cafe::/64 paros med dc00 +fd00:dead:beef:cafe::/64 paros med a000 Routes: +fd00:1234:0:0::/64 s med a000 +fd00:4567:0:0::/64 s med 8000 Services: +44970 5d fddead00beef00007bad0069ce45948504d2 s a000 +Contexts: +fd00:dead:beef:cafe::/64 1 c Done ``` diff --git a/src/cli/README_SRP.md b/src/cli/README_SRP.md index a0ab5ba81e4..ce2139fb7b1 100644 --- a/src/cli/README_SRP.md +++ b/src/cli/README_SRP.md @@ -131,7 +131,10 @@ my-service._ipps._tcp.default.service.arpa. port: 12345 priority: 0 weight: 0 - TXT: 00 + ttl: 7200 + lease: 7200 + key-lease: 1209600 + TXT: [] host: my-host.default.service.arpa. addresses: [fded:5114:8263:1fe1:44f9:cc06:4a2d:534] Done diff --git a/src/cli/README_SRP_SERVER.md b/src/cli/README_SRP_SERVER.md index 05cb9bc7629..ad87182590f 100644 --- a/src/cli/README_SRP_SERVER.md +++ b/src/cli/README_SRP_SERVER.md @@ -170,6 +170,8 @@ Usage: `srp server service` Print information of all registered services. +The TXT record is displayed as an array of entries. If an entry has a key, the key will be printed in ASCII format. The value portion will always be printed as hex bytes. + ```bash > srp server service srp-api-test-1._ipps._tcp.default.service.arpa. @@ -178,7 +180,10 @@ srp-api-test-1._ipps._tcp.default.service.arpa. port: 49152 priority: 0 weight: 0 - TXT: 0130 + ttl: 7200 + lease: 7200 + key-lease: 1209600 + TXT: [616263, xyz=585960] host: srp-api-test-1.default.service.arpa. addresses: [fdde:ad00:beef:0:0:ff:fe00:fc10] srp-api-test-0._ipps._tcp.default.service.arpa. @@ -187,7 +192,10 @@ srp-api-test-0._ipps._tcp.default.service.arpa. port: 49152 priority: 0 weight: 0 - TXT: 0130 + ttl: 3600 + lease: 3600 + key-lease: 1209600 + TXT: [616263, xyz=585960] host: srp-api-test-0.default.service.arpa. addresses: [fdde:ad00:beef:0:0:ff:fe00:fc10] Done diff --git a/src/cli/README_TCP.md b/src/cli/README_TCP.md index c1aba71d05c..3869570b814 100644 --- a/src/cli/README_TCP.md +++ b/src/cli/README_TCP.md @@ -109,7 +109,7 @@ Establishes a connection with the specified peer. If the connection establishment is successful, the resulting TCP connection is associated with the example TCP endpoint. -- ip: the peer's IPv6 address. +- ip: the peer's IP address. - port: the peer's TCP port. ```bash @@ -118,6 +118,16 @@ Done TCP: Connection established ``` +The address can be an IPv4 address, which will be synthesized to an IPv6 address using the preferred NAT64 prefix from the network data. + +> Note: The command will return `InvalidState` when the preferred NAT64 prefix is unavailable. + +```bash +> tcp connect 172.17.0.1 1234 +Connecting to synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1 +Done +``` + ### deinit Deinitializes the example TCP listener and the example TCP endpoint. @@ -147,14 +157,19 @@ stoplistening Done ``` -### init [\] +### init [\] [\] Initializes the example TCP listener and the example TCP endpoint. +- mode: this specifies the buffering strategy and whether to use TLS. The possible values are "linked", "circular" (default), and "tls". - size: the size of the receive buffer to associate with the example TCP endpoint. If left unspecified, the maximum size is used. +If "tls" is used, then the TLS protocol will be used for the connection (on top of TCP). When communicating over TCP between two nodes, either both should use TLS or neither should (a non-TLS endpoint cannot communicate with a TLS endpoint). The first two options, "linked" and "circular", specify that TLS should not be used and specify a buffering strategy to use with TCP; two endpoints of a TCP connection may use different buffering strategies. + +The behaviors of "linked" and "circular" buffering are identical, but the option is provided so that users of TCP can inspect the code to see an example of using the two buffering strategies. + ```bash -> tcp init +> tcp init tls Done ``` diff --git a/src/cli/README_UDP.md b/src/cli/README_UDP.md index 51cd8cc5b1f..14444cdaf14 100644 --- a/src/cli/README_UDP.md +++ b/src/cli/README_UDP.md @@ -96,7 +96,7 @@ Done Specifies the peer with which the socket is to be associated. -- ip: the peer's IPv6 address. +- ip: the peer's IP address. - port: the peer's UDP port. ```bash @@ -104,6 +104,16 @@ Specifies the peer with which the socket is to be associated. Done ``` +The address can be an IPv4 address, which will be synthesized to an IPv6 address using the preferred NAT64 prefix from the network data. + +> Note: The command will return `InvalidState` when the preferred NAT64 prefix is unavailable. + +```bash +> udp connect 172.17.0.1 1234 +Connecting to synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1 +Done +``` + ### linksecurity Indicates whether the link security is enabled or disabled. @@ -145,7 +155,7 @@ Done Send a UDP message. -- ip: the IPv6 destination address. +- ip: the destination address. - port: the UDP destination port. - message: the message to send. @@ -154,6 +164,16 @@ Send a UDP message. Done ``` +The address can be an IPv4 address, which will be synthesized to an IPv6 address using the preferred NAT64 prefix from the network data. + +> Note: The command will return `InvalidState` when the preferred NAT64 prefix is unavailable. + +```bash +> udp send 172.17.0.1 1234 +Sending to synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1 +Done +``` + ### send \ \ \ \ Send a few bytes over UDP. diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index 71785276b56..6d1102358e2 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include "common/num_utils.hpp" #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE #include #endif @@ -57,9 +59,6 @@ #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE #include #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE -#include -#endif #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE #include #endif @@ -84,6 +83,9 @@ #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE #include #endif +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE +#include +#endif #include "common/new.hpp" #include "common/string.hpp" @@ -96,50 +98,56 @@ Interpreter *Interpreter::sInterpreter = nullptr; static OT_DEFINE_ALIGNED_VAR(sInterpreterRaw, sizeof(Interpreter), uint64_t); Interpreter::Interpreter(Instance *aInstance, otCliOutputCallback aCallback, void *aContext) - : Output(aInstance, aCallback, aContext) - , mUserCommands(nullptr) - , mUserCommandsLength(0) + : OutputImplementer(aCallback, aContext) + , Output(aInstance, *this) , mCommandIsPending(false) , mTimer(*aInstance, HandleTimer, this) #if OPENTHREAD_FTD || OPENTHREAD_MTD #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE , mSntpQueryingInProgress(false) #endif - , mDataset(*this) - , mNetworkData(*this) - , mUdp(*this) + , mDataset(aInstance, *this) + , mNetworkData(aInstance, *this) + , mUdp(aInstance, *this) +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + , mBr(aInstance, *this) +#endif #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE - , mTcp(*this) + , mTcp(aInstance, *this) #endif #if OPENTHREAD_CONFIG_COAP_API_ENABLE - , mCoap(*this) + , mCoap(aInstance, *this) #endif #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE - , mCoapSecure(*this) + , mCoapSecure(aInstance, *this) #endif #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD - , mCommissioner(*this) + , mCommissioner(aInstance, *this) #endif #if OPENTHREAD_CONFIG_JOINER_ENABLE - , mJoiner(*this) + , mJoiner(aInstance, *this) #endif #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE - , mSrpClient(*this) + , mSrpClient(aInstance, *this) #endif #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE - , mSrpServer(*this) + , mSrpServer(aInstance, *this) #endif #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE - , mHistory(*this) + , mHistory(aInstance, *this) #endif #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE , mLocateInProgress(false) #endif +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE + , mLinkMetricsQueryInProgress(false) +#endif #endif // OPENTHREAD_FTD || OPENTHREAD_MTD { -#if OPENTHREAD_FTD - otThreadSetDiscoveryRequestCallback(GetInstancePtr(), &Interpreter::HandleDiscoveryRequest, this); +#if (OPENTHREAD_FTD || OPENTHREAD_MTD) && OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK + otIp6SetReceiveCallback(GetInstancePtr(), &Interpreter::HandleIp6Receive, this); #endif + memset(&mUserCommands, 0, sizeof(mUserCommands)); OutputPrompt(); } @@ -156,7 +164,7 @@ void Interpreter::OutputResult(otError aError) } else { - OutputLine("Error %d: %s", aError, otThreadErrorToString(aError)); + OutputLine("Error %u: %s", aError, otThreadErrorToString(aError)); } mCommandIsPending = false; @@ -200,7 +208,7 @@ const char *Interpreter::LinkModeToString(const otLinkModeConfig &aLinkMode, cha template <> otError Interpreter::Process(Arg aArgs[]) { otError error; - char * args[kMaxArgs]; + char *args[kMaxArgs]; char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; // all diagnostics related features are processed within diagnostics module @@ -224,7 +232,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) } else if (aArgs[0] == "api") { - OutputLine("%d", OPENTHREAD_API_VERSION); + OutputLine("%u", OPENTHREAD_API_VERSION); } else { @@ -286,27 +294,42 @@ otError Interpreter::ProcessUserCommands(Arg aArgs[]) { otError error = OT_ERROR_INVALID_COMMAND; - for (uint8_t i = 0; i < mUserCommandsLength; i++) + for (const UserCommandsEntry &entry : mUserCommands) { - if (aArgs[0] == mUserCommands[i].mName) + for (uint8_t i = 0; i < entry.mLength; i++) { - char *args[kMaxArgs]; + if (aArgs[0] == entry.mCommands[i].mName) + { + char *args[kMaxArgs]; - Arg::CopyArgsToStringArray(aArgs, args); - mUserCommands[i].mCommand(mUserCommandsContext, Arg::GetArgsLength(aArgs) - 1, args + 1); - error = OT_ERROR_NONE; - break; + Arg::CopyArgsToStringArray(aArgs, args); + error = entry.mCommands[i].mCommand(entry.mContext, Arg::GetArgsLength(aArgs) - 1, args + 1); + break; + } } } return error; } -void Interpreter::SetUserCommands(const otCliCommand *aCommands, uint8_t aLength, void *aContext) +otError Interpreter::SetUserCommands(const otCliCommand *aCommands, uint8_t aLength, void *aContext) { - mUserCommands = aCommands; - mUserCommandsLength = aLength; - mUserCommandsContext = aContext; + otError error = OT_ERROR_FAILED; + + for (UserCommandsEntry &entry : mUserCommands) + { + if (entry.mCommands == nullptr) + { + entry.mCommands = aCommands; + entry.mLength = aLength; + entry.mContext = aContext; + + error = OT_ERROR_NONE; + break; + } + } + + return error; } #if OPENTHREAD_FTD || OPENTHREAD_MTD @@ -333,7 +356,7 @@ otError Interpreter::ParseEnableOrDisable(const Arg &aArg, bool &aEnable) otError Interpreter::ParseJoinerDiscerner(Arg &aArg, otJoinerDiscerner &aDiscerner) { otError error; - char * separator; + char *separator; VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS); @@ -355,7 +378,7 @@ otError Interpreter::ParseJoinerDiscerner(Arg &aArg, otJoinerDiscerner &aDiscern otError Interpreter::ParsePingInterval(const Arg &aArg, uint32_t &aInterval) { otError error = OT_ERROR_NONE; - const char * string = aArg.GetCString(); + const char *string = aArg.GetCString(); const uint32_t msFactor = 1000; uint32_t factor = msFactor; @@ -450,11 +473,33 @@ const char *Interpreter::PreferenceToString(signed int aPreference) return str; } -#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE -template <> otError Interpreter::Process(Arg aArgs[]) +otError Interpreter::ParseToIp6Address(otInstance *aInstance, + const Arg &aArg, + otIp6Address &aAddress, + bool &aSynthesized) { - return mHistory.Process(aArgs); + Error error = kErrorNone; + + VerifyOrExit(!aArg.IsEmpty(), error = OT_ERROR_INVALID_ARGS); + error = aArg.ParseAsIp6Address(aAddress); + aSynthesized = false; + if (error != kErrorNone) + { + // It might be an IPv4 address, let's have a try. + otIp4Address ip4Address; + + // Do not touch the error value if we failed to parse it as an IPv4 address. + SuccessOrExit(aArg.ParseAsIp4Address(ip4Address)); + SuccessOrExit(error = otNat64SynthesizeIp6Address(aInstance, &ip4Address, &aAddress)); + aSynthesized = true; + } + +exit: + return error; } + +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE +template <> otError Interpreter::Process(Arg aArgs[]) { return mHistory.Process(aArgs); } #endif #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE @@ -510,181 +555,304 @@ template <> otError Interpreter::Process(Arg aArgs[]) #endif // OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE -template <> otError Interpreter::Process(Arg aArgs[]) +template <> otError Interpreter::Process(Arg aArgs[]) { return mBr.Process(aArgs); } +#endif + +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE +template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; bool enable; - /** - * @cli br (enable,disable) - * @code - * br enable - * Done - * @endcode - * @code - * br disable - * Done - * @endcode - * @par api_copy - * #otBorderRoutingSetEnabled - */ - if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE) + if (aArgs[0].IsEmpty()) { - SuccessOrExit(error = otBorderRoutingSetEnabled(GetInstancePtr(), enable)); + ExitNow(error = OT_ERROR_INVALID_COMMAND); } /** - * @cli br omrprefix + * @cli nat64 (enable,disable) * @code - * br omrprefix - * fdfc:1ff5:1512:5622::/64 + * nat64 enable * Done * @endcode - * @par api_copy - * #otBorderRoutingGetOmrPrefix - */ - else if (aArgs[0] == "omrprefix") - { - otIp6Prefix omrPrefix; - - SuccessOrExit(error = otBorderRoutingGetOmrPrefix(GetInstancePtr(), &omrPrefix)); - OutputIp6PrefixLine(omrPrefix); - } - /** - * @cli br favoredomrprefix * @code - * br favoredomrprefix - * fdfc:1ff5:1512:5622::/64 prf:low + * nat64 disable * Done * @endcode + * @cparam nat64 @ca{enable|disable} * @par api_copy - * #otBorderRoutingGetFavoredOmrPrefix + * #otNat64SetEnabled + * */ - else if (aArgs[0] == "favoredomrprefix") + if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE) { - otIp6Prefix prefix; - otRoutePreference preference; - - SuccessOrExit(error = otBorderRoutingGetFavoredOmrPrefix(GetInstancePtr(), &prefix, &preference)); - OutputIp6Prefix(prefix); - OutputLine(" prf:%s", PreferenceToString(preference)); + otNat64SetEnabled(GetInstancePtr(), enable); } /** - * @cli br onlinkprefix + * @cli nat64 state * @code - * br onlinkprefix - * fd41:2650:a6f5:0::/64 + * nat64 state + * PrefixManager: Active + * Translator: Active * Done * @endcode - * @par api_copy - * #otBorderRoutingGetOnLinkPrefix + * @par + * Gets the state of NAT64 functions. + * @par + * `PrefixManager` state is available when `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled. + * `Translator` state is available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled. + * @par + * When `OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE` is enabled, `PrefixManager` returns one of the following + * states: + * - `Disabled`: NAT64 prefix manager is disabled. + * - `NotRunning`: NAT64 prefix manager is enabled, but is not running. This could mean that the routing manager is + * disabled. + * - `Idle`: NAT64 prefix manager is enabled and is running, but is not publishing a NAT64 prefix. This can happen + * when there is another border router publishing a NAT64 prefix with a higher priority. + * - `Active`: NAT64 prefix manager is enabled, running, and publishing a NAT64 prefix. + * @par + * When `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled, `Translator` returns one of the following states: + * - `Disabled`: NAT64 translator is disabled. + * - `NotRunning`: NAT64 translator is enabled, but is not translating packets. This could mean that the Translator + * is not configured with a NAT64 prefix or a CIDR for NAT64. + * - `Active`: NAT64 translator is enabled and is translating packets. + * @sa otNat64GetPrefixManagerState + * @sa otNat64GetTranslatorState + * */ - else if (aArgs[0] == "onlinkprefix") + else if (aArgs[0] == "state") { - otIp6Prefix onLinkPrefix; + static const char *const kNat64State[] = {"Disabled", "NotRunning", "Idle", "Active"}; - SuccessOrExit(error = otBorderRoutingGetOnLinkPrefix(GetInstancePtr(), &onLinkPrefix)); - OutputIp6PrefixLine(onLinkPrefix); + static_assert(0 == OT_NAT64_STATE_DISABLED, "OT_NAT64_STATE_DISABLED value is incorrect"); + static_assert(1 == OT_NAT64_STATE_NOT_RUNNING, "OT_NAT64_STATE_NOT_RUNNING value is incorrect"); + static_assert(2 == OT_NAT64_STATE_IDLE, "OT_NAT64_STATE_IDLE value is incorrect"); + static_assert(3 == OT_NAT64_STATE_ACTIVE, "OT_NAT64_STATE_ACTIVE value is incorrect"); + +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + OutputLine("PrefixManager: %s", kNat64State[otNat64GetPrefixManagerState(GetInstancePtr())]); +#endif +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + OutputLine("Translator: %s", kNat64State[otNat64GetTranslatorState(GetInstancePtr())]); +#endif } -#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE /** - * @cli br nat64prefix + * @cli nat64 cidr * @code - * br nat64prefix - * fd14:1078:b3d5:b0b0:0:0::/96 + * nat64 cidr + * 192.168.255.0/24 * Done * @endcode * @par api_copy - * #otBorderRoutingGetNat64Prefix + * #otNat64GetCidr + * */ - else if (aArgs[0] == "nat64prefix") + else if (aArgs[0] == "cidr") { - otIp6Prefix nat64Prefix; + otIp4Cidr cidr; + char cidrString[OT_IP4_CIDR_STRING_SIZE]; - SuccessOrExit(error = otBorderRoutingGetNat64Prefix(GetInstancePtr(), &nat64Prefix)); - OutputIp6PrefixLine(nat64Prefix); + SuccessOrExit(error = otNat64GetCidr(GetInstancePtr(), &cidr)); + otIp4CidrToString(&cidr, cidrString, sizeof(cidrString)); + OutputLine("%s", cidrString); } -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE /** - * @cli br rioprf (high,med,low) - * @code - * br rioprf - * med - * Done - * @endcode + * @cli nat64 mappings * @code - * br rioprf low - * Done + * nat64 mappings + * | | Address | | 4 to 6 | 6 to 4 | + * +----------+---------------------------+--------+--------------+--------------+ + * | ID | IPv6 | IPv4 | Expiry | Pkts | Bytes | Pkts | Bytes | + * +----------+------------+--------------+--------+------+-------+------+-------+ + * | 00021cb9 | fdc7::df79 | 192.168.64.2 | 7196s | 6 | 456 | 11 | 1928 | + * | | TCP | 0 | 0 | 0 | 0 | + * | | UDP | 1 | 136 | 16 | 1608 | + * | | ICMP | 5 | 320 | 5 | 320 | * @endcode - * @cparam br rioprf [@ca{high}|@ca{med}|@ca{low}] * @par api_copy - * #otBorderRoutingSetRouteInfoOptionPreference + * #otNat64GetNextAddressMapping * */ - else if (aArgs[0] == "rioprf") + else if (aArgs[0] == "mappings") { - if (aArgs[1].IsEmpty()) - { - OutputLine("%s", PreferenceToString(otBorderRoutingGetRouteInfoOptionPreference(GetInstancePtr()))); - } - else - { - otRoutePreference preference; + otNat64AddressMappingIterator iterator; + otNat64AddressMapping mapping; + + static const char *const kNat64StatusLevel1Title[] = {"", "Address", "", "4 to 6", "6 to 4"}; - SuccessOrExit(error = ParsePreference(aArgs[1], preference)); - otBorderRoutingSetRouteInfoOptionPreference(GetInstancePtr(), preference); + static const uint8_t kNat64StatusLevel1ColumnWidths[] = { + 18, 61, 8, 25, 25, + }; + + static const char *const kNat64StatusTableHeader[] = { + "ID", "IPv6", "IPv4", "Expiry", "Pkts", "Bytes", "Pkts", "Bytes", + }; + + static const uint8_t kNat64StatusTableColumnWidths[] = { + 18, 42, 18, 8, 10, 14, 10, 14, + }; + + OutputTableHeader(kNat64StatusLevel1Title, kNat64StatusLevel1ColumnWidths); + OutputTableHeader(kNat64StatusTableHeader, kNat64StatusTableColumnWidths); + + otNat64InitAddressMappingIterator(GetInstancePtr(), &iterator); + while (otNat64GetNextAddressMapping(GetInstancePtr(), &iterator, &mapping) == OT_ERROR_NONE) + { + char ip4AddressString[OT_IP4_ADDRESS_STRING_SIZE]; + char ip6AddressString[OT_IP6_PREFIX_STRING_SIZE]; + Uint64StringBuffer u64StringBuffer; + + otIp6AddressToString(&mapping.mIp6, ip6AddressString, sizeof(ip6AddressString)); + otIp4AddressToString(&mapping.mIp4, ip4AddressString, sizeof(ip4AddressString)); + + OutputFormat("| %08lx%08lx ", ToUlong(static_cast(mapping.mId >> 32)), + ToUlong(static_cast(mapping.mId & 0xffffffff))); + OutputFormat("| %40s ", ip6AddressString); + OutputFormat("| %16s ", ip4AddressString); + OutputFormat("| %5lus ", ToUlong(mapping.mRemainingTimeMs / 1000)); + OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mTotal.m4To6Packets, u64StringBuffer)); + OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mTotal.m4To6Bytes, u64StringBuffer)); + OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mTotal.m6To4Packets, u64StringBuffer)); + OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mTotal.m6To4Bytes, u64StringBuffer)); + + OutputLine("|"); + + OutputFormat("| %16s ", ""); + OutputFormat("| %68s ", "TCP"); + OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mTcp.m4To6Packets, u64StringBuffer)); + OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mTcp.m4To6Bytes, u64StringBuffer)); + OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mTcp.m6To4Packets, u64StringBuffer)); + OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mTcp.m6To4Bytes, u64StringBuffer)); + OutputLine("|"); + + OutputFormat("| %16s ", ""); + OutputFormat("| %68s ", "UDP"); + OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mUdp.m4To6Packets, u64StringBuffer)); + OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mUdp.m4To6Bytes, u64StringBuffer)); + OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mUdp.m6To4Packets, u64StringBuffer)); + OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mUdp.m6To4Bytes, u64StringBuffer)); + OutputLine("|"); + + OutputFormat("| %16s ", ""); + OutputFormat("| %68s ", "ICMP"); + OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mIcmp.m4To6Packets, u64StringBuffer)); + OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mIcmp.m4To6Bytes, u64StringBuffer)); + OutputFormat("| %8s ", Uint64ToString(mapping.mCounters.mIcmp.m6To4Packets, u64StringBuffer)); + OutputFormat("| %12s ", Uint64ToString(mapping.mCounters.mIcmp.m6To4Bytes, u64StringBuffer)); + OutputLine("|"); } } /** - * @cli br prefixtable + * @cli nat64 counters * @code - * br prefixtable - * prefix:fd00:1234:5678:0::/64, on-link:no, ms-since-rx:29526, lifetime:1800, route-prf:med, - * router:ff02:0:0:0:0:0:0:1 - * prefix:1200:abba:baba:0::/64, on-link:yes, ms-since-rx:29527, lifetime:1800, preferred:1800, - * router:ff02:0:0:0:0:0:0:1 + * nat64 counters + * | | 4 to 6 | 6 to 4 | + * +---------------+-------------------------+-------------------------+ + * | Protocol | Pkts | Bytes | Pkts | Bytes | + * +---------------+----------+--------------+----------+--------------+ + * | Total | 11 | 704 | 11 | 704 | + * | TCP | 0 | 0 | 0 | 0 | + * | UDP | 0 | 0 | 0 | 0 | + * | ICMP | 11 | 704 | 11 | 704 | + * | Errors | Pkts | Pkts | + * +---------------+-------------------------+-------------------------+ + * | Total | 8 | 4 | + * | Illegal Pkt | 0 | 0 | + * | Unsup Proto | 0 | 0 | + * | No Mapping | 2 | 0 | * Done * @endcode - * @par api_copy - * #otBorderRoutingGetNextPrefixTableEntry + * @par + * Gets the NAT64 translator packet and error counters. + * @par + * Available when `OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE` is enabled. + * @sa otNat64GetCounters + * @sa otNat64GetErrorCounters * */ - else if (aArgs[0] == "prefixtable") + else if (aArgs[0] == "counters") { - otBorderRoutingPrefixTableIterator iterator; - otBorderRoutingPrefixTableEntry entry; + static const char *const kNat64CounterTableHeader[] = { + "", + "4 to 6", + "6 to 4", + }; + static const uint8_t kNat64CounterTableHeaderColumns[] = {15, 25, 25}; + static const char *const kNat64CounterTableSubHeader[] = { + "Protocol", "Pkts", "Bytes", "Pkts", "Bytes", + }; + static const uint8_t kNat64CounterTableSubHeaderColumns[] = { + 15, 10, 14, 10, 14, + }; + static const char *const kNat64CounterTableErrorSubHeader[] = { + "Errors", + "Pkts", + "Pkts", + }; + static const uint8_t kNat64CounterTableErrorSubHeaderColumns[] = { + 15, + 25, + 25, + }; + static const char *const kNat64CounterErrorType[] = { + "Unknown", + "Illegal Pkt", + "Unsup Proto", + "No Mapping", + }; - otBorderRoutingPrefixTableInitIterator(GetInstancePtr(), &iterator); + otNat64ProtocolCounters counters; + otNat64ErrorCounters errorCounters; + Uint64StringBuffer u64StringBuffer; - while (otBorderRoutingGetNextPrefixTableEntry(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE) - { - char string[OT_IP6_PREFIX_STRING_SIZE]; + OutputTableHeader(kNat64CounterTableHeader, kNat64CounterTableHeaderColumns); + OutputTableHeader(kNat64CounterTableSubHeader, kNat64CounterTableSubHeaderColumns); - otIp6PrefixToString(&entry.mPrefix, string, sizeof(string)); - OutputFormat("prefix:%s, on-link:%s, ms-since-rx:%u, lifetime:%u, ", string, entry.mIsOnLink ? "yes" : "no", - entry.mMsecSinceLastUpdate, entry.mValidLifetime); + otNat64GetCounters(GetInstancePtr(), &counters); + otNat64GetErrorCounters(GetInstancePtr(), &errorCounters); - if (entry.mIsOnLink) - { - OutputFormat("preferred:%u, ", entry.mPreferredLifetime); - } - else - { - OutputFormat("route-prf:%s, ", PreferenceToString(entry.mRoutePreference)); - } + OutputFormat("| %13s ", "Total"); + OutputFormat("| %8s ", Uint64ToString(counters.mTotal.m4To6Packets, u64StringBuffer)); + OutputFormat("| %12s ", Uint64ToString(counters.mTotal.m4To6Bytes, u64StringBuffer)); + OutputFormat("| %8s ", Uint64ToString(counters.mTotal.m6To4Packets, u64StringBuffer)); + OutputLine("| %12s |", Uint64ToString(counters.mTotal.m6To4Bytes, u64StringBuffer)); + + OutputFormat("| %13s ", "TCP"); + OutputFormat("| %8s ", Uint64ToString(counters.mTcp.m4To6Packets, u64StringBuffer)); + OutputFormat("| %12s ", Uint64ToString(counters.mTcp.m4To6Bytes, u64StringBuffer)); + OutputFormat("| %8s ", Uint64ToString(counters.mTcp.m6To4Packets, u64StringBuffer)); + OutputLine("| %12s |", Uint64ToString(counters.mTcp.m6To4Bytes, u64StringBuffer)); + + OutputFormat("| %13s ", "UDP"); + OutputFormat("| %8s ", Uint64ToString(counters.mUdp.m4To6Packets, u64StringBuffer)); + OutputFormat("| %12s ", Uint64ToString(counters.mUdp.m4To6Bytes, u64StringBuffer)); + OutputFormat("| %8s ", Uint64ToString(counters.mUdp.m6To4Packets, u64StringBuffer)); + OutputLine("| %12s |", Uint64ToString(counters.mUdp.m6To4Bytes, u64StringBuffer)); + + OutputFormat("| %13s ", "ICMP"); + OutputFormat("| %8s ", Uint64ToString(counters.mIcmp.m4To6Packets, u64StringBuffer)); + OutputFormat("| %12s ", Uint64ToString(counters.mIcmp.m4To6Bytes, u64StringBuffer)); + OutputFormat("| %8s ", Uint64ToString(counters.mIcmp.m6To4Packets, u64StringBuffer)); + OutputLine("| %12s |", Uint64ToString(counters.mIcmp.m6To4Bytes, u64StringBuffer)); - otIp6AddressToString(&entry.mRouterAddress, string, sizeof(string)); - OutputLine("router:%s", string); + OutputTableHeader(kNat64CounterTableErrorSubHeader, kNat64CounterTableErrorSubHeaderColumns); + for (uint8_t i = 0; i < OT_NAT64_DROP_REASON_COUNT; i++) + { + OutputFormat("| %13s | %23s ", kNat64CounterErrorType[i], + Uint64ToString(errorCounters.mCount4To6[i], u64StringBuffer)); + OutputLine("| %23s |", Uint64ToString(errorCounters.mCount6To4[i], u64StringBuffer)); } } +#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE else { - error = OT_ERROR_INVALID_COMMAND; + ExitNow(error = OT_ERROR_INVALID_COMMAND); } exit: return error; } -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) template <> otError Interpreter::Process(Arg aArgs[]) @@ -692,15 +860,34 @@ template <> otError Interpreter::Process(Arg aArgs[]) otError error = OT_ERROR_INVALID_COMMAND; otBackboneRouterConfig config; + /** + * @cli bbr + * @code + * bbr + * BBR Primary: + * server16: 0xE400 + * seqno: 10 + * delay: 120 secs + * timeout: 300 secs + * Done + * @endcode + * @code + * bbr + * BBR Primary: None + * Done + * @endcode + * @par + * Returns the current Primary Backbone Router information for the Thread device. + */ if (aArgs[0].IsEmpty()) { if (otBackboneRouterGetPrimary(GetInstancePtr(), &config) == OT_ERROR_NONE) { OutputLine("BBR Primary:"); OutputLine("server16: 0x%04X", config.mServer16); - OutputLine("seqno: %d", config.mSequenceNumber); - OutputLine("delay: %d secs", config.mReregistrationDelay); - OutputLine("timeout: %d secs", config.mMlrTimeout); + OutputLine("seqno: %u", config.mSequenceNumber); + OutputLine("delay: %u secs", config.mReregistrationDelay); + OutputLine("timeout: %lu secs", ToUlong(config.mMlrTimeout)); } else { @@ -719,6 +906,34 @@ template <> otError Interpreter::Process(Arg aArgs[]) ExitNow(error = OT_ERROR_INVALID_COMMAND); } #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE && OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + /** + * @cli bbr mgmt dua + * @code + * bbr mgmt dua 1 2f7c235e5025a2fd + * Done + * @endcode + * @code + * bbr mgmt dua 160 + * Done + * @endcode + * @cparam bbr mgmt dua @ca{status|coap-code} [@ca{meshLocalIid}] + * For `status` or `coap-code`, use: + * * 0: ST_DUA_SUCCESS + * * 1: ST_DUA_REREGISTER + * * 2: ST_DUA_INVALID + * * 3: ST_DUA_DUPLICATE + * * 4: ST_DUA_NO_RESOURCES + * * 5: ST_DUA_BBR_NOT_PRIMARY + * * 6: ST_DUA_GENERAL_FAILURE + * * 160: COAP code 5.00 + * @par + * With the `meshLocalIid` included, this command configures the response status + * for the next DUA registration. Without `meshLocalIid`, respond to the next + * DUA.req with the specified `status` or `coap-code`. + * @par + * Available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. + * @sa otBackboneRouterConfigNextDuaRegistrationResponse + */ else if (aArgs[1] == "dua") { uint8_t status; @@ -761,6 +976,22 @@ otError Interpreter::ProcessBackboneRouterMgmtMlr(Arg aArgs[]) { otError error = OT_ERROR_INVALID_COMMAND; + /** + * @cli bbr mgmt mlr listener + * @code + * bbr mgmt mlr listener + * ff04:0:0:0:0:0:0:abcd 3534000 + * ff04:0:0:0:0:0:0:eeee 3537610 + * Done + * @endcode + * @par + * Returns the Multicast Listeners with the #otBackboneRouterMulticastListenerInfo + * `mTimeout` in seconds. + * @par + * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` and + * `OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE` are enabled. + * @sa otBackboneRouterMulticastListenerGetNext + */ if (aArgs[0] == "listener") { if (aArgs[1].IsEmpty()) @@ -770,11 +1001,34 @@ otError Interpreter::ProcessBackboneRouterMgmtMlr(Arg aArgs[]) } #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + /** + * @cli bbr mgmt mlr listener clear + * @code + * bbr mgmt mlr listener clear + * Done + * @endcode + * @par api_copy + * #otBackboneRouterMulticastListenerClear + */ if (aArgs[1] == "clear") { otBackboneRouterMulticastListenerClear(GetInstancePtr()); error = OT_ERROR_NONE; } + /** + * @cli bbr mgmt mlr listener add + * @code + * bbr mgmt mlr listener add ff04::1 + * Done + * @endcode + * @code + * bbr mgmt mlr listener add ff04::2 300 + * Done + * @endcode + * @cparam bbr mgmt mlr listener add @ca{ipaddress} [@ca{timeout-seconds}] + * @par api_copy + * #otBackboneRouterMulticastListenerAdd + */ else if (aArgs[1] == "add") { otIp6Address address; @@ -791,6 +1045,23 @@ otError Interpreter::ProcessBackboneRouterMgmtMlr(Arg aArgs[]) error = otBackboneRouterMulticastListenerAdd(GetInstancePtr(), &address, timeout); } } + /** + * @cli bbr mgmt mlr response + * @code + * bbr mgmt mlr response 2 + * Done + * @endcode + * @cparam bbr mgmt mlr response @ca{status-code} + * For `status-code`, use: + * * 0: ST_MLR_SUCCESS + * * 2: ST_MLR_INVALID + * * 3: ST_MLR_NO_PERSISTENT + * * 4: ST_MLR_NO_RESOURCES + * * 5: ST_MLR_BBR_NOT_PRIMARY + * * 6: ST_MLR_GENERAL_FAILURE + * @par api_copy + * #otBackboneRouterConfigNextMulticastListenerRegistrationResponse + */ else if (aArgs[0] == "response") { error = ProcessSet(aArgs + 1, otBackboneRouterConfigNextMulticastListenerRegistrationResponse); @@ -809,7 +1080,7 @@ void Interpreter::PrintMulticastListenersTable(void) while (otBackboneRouterMulticastListenerGetNext(GetInstancePtr(), &iter, &listenerInfo) == OT_ERROR_NONE) { OutputIp6Address(listenerInfo.mAddress); - OutputLine(" %u", listenerInfo.mTimeout); + OutputLine(" %lu", ToUlong(listenerInfo.mTimeout)); } } @@ -821,18 +1092,81 @@ otError Interpreter::ProcessBackboneRouterLocal(Arg aArgs[]) otBackboneRouterConfig config; bool enable; + /** + * @cli bbr (enable,disable) + * @code + * bbr enable + * Done + * @endcode + * @code + * bbr disable + * Done + * @endcode + * @par api_copy + * #otBackboneRouterSetEnabled + */ if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE) { otBackboneRouterSetEnabled(GetInstancePtr(), enable); } + /** + * @cli bbr jitter (get,set) + * @code + * bbr jitter + * 20 + * Done + * @endcode + * @code + * bbr jitter 10 + * Done + * @endcode + * @cparam bbr jitter [@ca{jitter}] + * @par + * Gets or sets jitter (in seconds) for Backbone Router registration. + * @par + * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled. + * @sa otBackboneRouterGetRegistrationJitter + * @sa otBackboneRouterSetRegistrationJitter + */ else if (aArgs[0] == "jitter") { error = ProcessGetSet(aArgs + 1, otBackboneRouterGetRegistrationJitter, otBackboneRouterSetRegistrationJitter); } + /** + * @cli bbr register + * @code + * bbr register + * Done + * @endcode + * @par api_copy + * #otBackboneRouterRegister + */ else if (aArgs[0] == "register") { SuccessOrExit(error = otBackboneRouterRegister(GetInstancePtr())); } + /** + * @cli bbr state + * @code + * bbr state + * Disabled + * Done + * @endcode + * @code + * bbr state + * Primary + * Done + * @endcode + * @code + * bbr state + * Secondary + * Done + * @endcode + * @par + * Available when `OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE` is enabled. + * @par api_copy + * #otBackboneRouterGetState + */ else if (aArgs[0] == "state") { static const char *const kStateStrings[] = { @@ -847,19 +1181,44 @@ otError Interpreter::ProcessBackboneRouterLocal(Arg aArgs[]) OutputLine("%s", Stringify(otBackboneRouterGetState(GetInstancePtr()), kStateStrings)); } + /** + * @cli bbr config + * @code + * bbr config + * seqno: 10 + * delay: 120 secs + * timeout: 300 secs + * Done + * @endcode + * @par api_copy + * #otBackboneRouterGetConfig + */ else if (aArgs[0] == "config") { otBackboneRouterGetConfig(GetInstancePtr(), &config); if (aArgs[1].IsEmpty()) { - OutputLine("seqno: %d", config.mSequenceNumber); - OutputLine("delay: %d secs", config.mReregistrationDelay); - OutputLine("timeout: %d secs", config.mMlrTimeout); + OutputLine("seqno: %u", config.mSequenceNumber); + OutputLine("delay: %u secs", config.mReregistrationDelay); + OutputLine("timeout: %lu secs", ToUlong(config.mMlrTimeout)); } else { // Set local Backbone Router configuration. + /** + * @cli bbr config (set) + * @code + * bbr config seqno 20 delay 30 + * Done + * @endcode + * @cparam bbr config [seqno @ca{seqno}] [delay @ca{delay}] [timeout @ca{timeout}] + * @par + * `bbr register` should be issued explicitly to register Backbone Router service to Leader + * for Secondary Backbone Router. + * @par api_copy + * #otBackboneRouterSetConfig + */ for (Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++) { if (*arg == "seqno") @@ -896,21 +1255,30 @@ otError Interpreter::ProcessBackboneRouterLocal(Arg aArgs[]) } #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE +/** + * @cli domainname + * @code + * domainname + * Thread + * Done + * @endcode + * @par api_copy + * #otThreadGetDomainName + */ template <> otError Interpreter::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; - - if (aArgs[0].IsEmpty()) - { - OutputLine("%s", otThreadGetDomainName(GetInstancePtr())); - } - else - { - SuccessOrExit(error = otThreadSetDomainName(GetInstancePtr(), aArgs[0].GetCString())); - } - -exit: - return error; + /** + * @cli domainname (set) + * @code + * domainname Test\ Thread + * Done + * @endcode + * @cparam domainname @ca{name} + * Use a `backslash` to escape spaces. + * @par api_copy + * #otThreadSetDomainName + */ + return ProcessGetSet(aArgs, otThreadGetDomainName, otThreadSetDomainName); } #if OPENTHREAD_CONFIG_DUA_ENABLE @@ -918,6 +1286,16 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; + /** + * @cli dua iid + * @code + * dua iid + * 0004000300020001 + * Done + * @endcode + * @par api_copy + * #otThreadGetFixedDuaInterfaceIdentifier + */ if (aArgs[0] == "iid") { if (aArgs[1].IsEmpty()) @@ -929,6 +1307,22 @@ template <> otError Interpreter::Process(Arg aArgs[]) OutputBytesLine(iid->mFields.m8); } } + /** + * @cli dua iid (set,clear) + * @code + * dua iid 0004000300020001 + * Done + * @endcode + * @code + * dua iid clear + * Done + * @endcode + * @cparam dua iid @ca{iid|clear} + * `dua iid clear` passes a `nullptr` to #otThreadSetFixedDuaInterfaceIdentifier. + * Otherwise, you can pass the `iid`. + * @par api_copy + * #otThreadSetFixedDuaInterfaceIdentifier + */ else if (aArgs[1] == "clear") { error = otThreadSetFixedDuaInterfaceIdentifier(GetInstancePtr(), nullptr); @@ -953,14 +1347,43 @@ template <> otError Interpreter::Process(Arg aArgs[]) #endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) +/** + * @cli bufferinfo + * @code + * bufferinfo + * total: 40 + * free: 40 + * max-used: 5 + * 6lo send: 0 0 0 + * 6lo reas: 0 0 0 + * ip6: 0 0 0 + * mpl: 0 0 0 + * mle: 0 0 0 + * coap: 0 0 0 + * coap secure: 0 0 0 + * application coap: 0 0 0 + * Done + * @endcode + * @par + * Gets the current message buffer information. + * * `total` displays the total number of message buffers in pool. + * * `free` displays the number of free message buffers. + * * `max-used` displays max number of used buffers at the same time since OT stack + * initialization or last `bufferinfo reset`. + * @par + * Next, the CLI displays info about different queues used by the OpenThread stack, + * for example `6lo send`. Each line after the queue represents info about a queue: + * * The first number shows number messages in the queue. + * * The second number shows number of buffers used by all messages in the queue. + * * The third number shows total number of bytes of all messages in the queue. + * @sa otMessageGetBufferInfo + */ template <> otError Interpreter::Process(Arg aArgs[]) { - OT_UNUSED_VARIABLE(aArgs); - struct BufferInfoName { const otMessageQueueInfo otBufferInfo::*mQueuePtr; - const char * mName; + const char *mName; }; static const BufferInfoName kBufferInfoNames[] = { @@ -974,22 +1397,64 @@ template <> otError Interpreter::Process(Arg aArgs[]) {&otBufferInfo::mApplicationCoapQueue, "application coap"}, }; - otBufferInfo bufferInfo; + otError error = OT_ERROR_NONE; + + if (aArgs[0].IsEmpty()) + { + otBufferInfo bufferInfo; - otMessageGetBufferInfo(GetInstancePtr(), &bufferInfo); + otMessageGetBufferInfo(GetInstancePtr(), &bufferInfo); - OutputLine("total: %d", bufferInfo.mTotalBuffers); - OutputLine("free: %d", bufferInfo.mFreeBuffers); + OutputLine("total: %u", bufferInfo.mTotalBuffers); + OutputLine("free: %u", bufferInfo.mFreeBuffers); + OutputLine("max-used: %u", bufferInfo.mMaxUsedBuffers); - for (const BufferInfoName &info : kBufferInfoNames) + for (const BufferInfoName &info : kBufferInfoNames) + { + OutputLine("%s: %u %u %lu", info.mName, (bufferInfo.*info.mQueuePtr).mNumMessages, + (bufferInfo.*info.mQueuePtr).mNumBuffers, ToUlong((bufferInfo.*info.mQueuePtr).mTotalBytes)); + } + } + /** + * @cli bufferinfo reset + * @code + * bufferinfo reset + * Done + * @endcode + * @par api_copy + * #otMessageResetBufferInfo + */ + else if (aArgs[0] == "reset") + { + otMessageResetBufferInfo(GetInstancePtr()); + } + else { - OutputLine("%s: %u %u %u", info.mName, (bufferInfo.*info.mQueuePtr).mNumMessages, - (bufferInfo.*info.mQueuePtr).mNumBuffers, (bufferInfo.*info.mQueuePtr).mTotalBytes); + error = OT_ERROR_INVALID_ARGS; } - return OT_ERROR_NONE; + return error; } +/** + * @cli ccathreshold (get,set) + * @code + * ccathreshold + * -75 dBm + * Done + * @endcode + * @code + * ccathreshold -62 + * Done + * @endcode + * @cparam ccathreshold [@ca{CCA-threshold-dBm}] + * Use the optional `CCA-threshold-dBm` argument to set the CCA threshold. + * @par + * Gets or sets the CCA threshold in dBm measured at the antenna connector per + * IEEE 802.15.4 - 2015 section 10.1.4. + * @sa otPlatRadioGetCcaEnergyDetectThreshold + * @sa otPlatRadioSetCcaEnergyDetectThreshold + */ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -1041,20 +1506,88 @@ template <> otError Interpreter::Process(Arg aArgs[]) #endif +/** + * @cli channel (get,set) + * @code + * channel + * 11 + * Done + * @endcode + * @code + * channel 11 + * Done + * @endcode + * @cparam channel [@ca{channel-num}] + * Use `channel-num` to set the channel. + * @par + * Gets or sets the IEEE 802.15.4 Channel value. + */ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; + /** + * @cli channel supported + * @code + * channel supported + * 0x7fff800 + * Done + * @endcode + * @par api_copy + * #otPlatRadioGetSupportedChannelMask + */ if (aArgs[0] == "supported") { - OutputLine("0x%x", otPlatRadioGetSupportedChannelMask(GetInstancePtr())); - } - else if (aArgs[0] == "preferred") - { - OutputLine("0x%x", otPlatRadioGetPreferredChannelMask(GetInstancePtr())); + OutputLine("0x%lx", ToUlong(otPlatRadioGetSupportedChannelMask(GetInstancePtr()))); } -#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE - else if (aArgs[0] == "monitor") + /** + * @cli channel preferred + * @code + * channel preferred + * 0x7fff800 + * Done + * @endcode + * @par api_copy + * #otPlatRadioGetPreferredChannelMask + */ + else if (aArgs[0] == "preferred") + { + OutputLine("0x%lx", ToUlong(otPlatRadioGetPreferredChannelMask(GetInstancePtr()))); + } +#if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE + /** + * @cli channel monitor + * @code + * channel monitor + * enabled: 1 + * interval: 41000 + * threshold: -75 + * window: 960 + * count: 10552 + * occupancies: + * ch 11 (0x0cb7) 4.96% busy + * ch 12 (0x2e2b) 18.03% busy + * ch 13 (0x2f54) 18.48% busy + * ch 14 (0x0fef) 6.22% busy + * ch 15 (0x1536) 8.28% busy + * ch 16 (0x1746) 9.09% busy + * ch 17 (0x0b8b) 4.50% busy + * ch 18 (0x60a7) 37.75% busy + * ch 19 (0x0810) 3.14% busy + * ch 20 (0x0c2a) 4.75% busy + * ch 21 (0x08dc) 3.46% busy + * ch 22 (0x101d) 6.29% busy + * ch 23 (0x0092) 0.22% busy + * ch 24 (0x0028) 0.06% busy + * ch 25 (0x0063) 0.15% busy + * ch 26 (0x058c) 2.16% busy + * Done + * @endcode + * @par + * Get the current channel monitor state and channel occupancy. + * `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` is required. + */ + else if (aArgs[0] == "monitor") { if (aArgs[1].IsEmpty()) { @@ -1064,15 +1597,17 @@ template <> otError Interpreter::Process(Arg aArgs[]) uint32_t channelMask = otLinkGetSupportedChannelMask(GetInstancePtr()); uint8_t channelNum = sizeof(channelMask) * CHAR_BIT; - OutputLine("interval: %u", otChannelMonitorGetSampleInterval(GetInstancePtr())); + OutputLine("interval: %lu", ToUlong(otChannelMonitorGetSampleInterval(GetInstancePtr()))); OutputLine("threshold: %d", otChannelMonitorGetRssiThreshold(GetInstancePtr())); - OutputLine("window: %u", otChannelMonitorGetSampleWindow(GetInstancePtr())); - OutputLine("count: %u", otChannelMonitorGetSampleCount(GetInstancePtr())); + OutputLine("window: %lu", ToUlong(otChannelMonitorGetSampleWindow(GetInstancePtr()))); + OutputLine("count: %lu", ToUlong(otChannelMonitorGetSampleCount(GetInstancePtr()))); OutputLine("occupancies:"); + for (uint8_t channel = 0; channel < channelNum; channel++) { - uint32_t occupancy = 0; + uint16_t occupancy; + PercentageStringBuffer stringBuffer; if (!((1UL << channel) & channelMask)) { @@ -1081,17 +1616,43 @@ template <> otError Interpreter::Process(Arg aArgs[]) occupancy = otChannelMonitorGetChannelOccupancy(GetInstancePtr(), channel); - OutputFormat("ch %d (0x%04x) ", channel, occupancy); - occupancy = (occupancy * 10000) / 0xffff; - OutputLine("%2d.%02d%% busy", occupancy / 100, occupancy % 100); + OutputLine("ch %u (0x%04x) %6s%% busy", channel, occupancy, + PercentageToString(occupancy, stringBuffer)); } - OutputLine(""); + + OutputNewLine(); } } + /** + * @cli channel monitor start + * @code + * channel monitor start + * channel monitor start + * Done + * @endcode + * @par + * Start the channel monitor. + * OT CLI sends a boolean value of `true` to #otChannelMonitorSetEnabled. + * `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` is required. + * @sa otChannelMonitorSetEnabled + */ else if (aArgs[1] == "start") { error = otChannelMonitorSetEnabled(GetInstancePtr(), true); } + /** + * @cli channel monitor stop + * @code + * channel monitor stop + * channel monitor stop + * Done + * @endcode + * @par + * Stop the channel monitor. + * OT CLI sends a boolean value of `false` to #otChannelMonitorSetEnabled. + * `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` is required. + * @sa otChannelMonitorSetEnabled + */ else if (aArgs[1] == "stop") { error = otChannelMonitorSetEnabled(GetInstancePtr(), false); @@ -1105,9 +1666,26 @@ template <> otError Interpreter::Process(Arg aArgs[]) #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD else if (aArgs[0] == "manager") { + /** + * @cli channel manager + * @code + * channel manager + * channel: 11 + * auto: 1 + * delay: 120 + * interval: 10800 + * supported: { 11-26} + * favored: { 11-26} + * Done + * @endcode + * @par + * Get the channel manager state. + * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` is required. + * @sa otChannelManagerGetRequestedChannel + */ if (aArgs[1].IsEmpty()) { - OutputLine("channel: %d", otChannelManagerGetRequestedChannel(GetInstancePtr())); + OutputLine("channel: %u", otChannelManagerGetRequestedChannel(GetInstancePtr())); OutputLine("auto: %d", otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr())); if (otChannelManagerGetAutoChannelSelectionEnabled(GetInstancePtr())) @@ -1115,18 +1693,45 @@ template <> otError Interpreter::Process(Arg aArgs[]) Mac::ChannelMask supportedMask(otChannelManagerGetSupportedChannels(GetInstancePtr())); Mac::ChannelMask favoredMask(otChannelManagerGetFavoredChannels(GetInstancePtr())); - OutputLine("delay: %d", otChannelManagerGetDelay(GetInstancePtr())); - OutputLine("interval: %u", otChannelManagerGetAutoChannelSelectionInterval(GetInstancePtr())); + OutputLine("delay: %u", otChannelManagerGetDelay(GetInstancePtr())); + OutputLine("interval: %lu", ToUlong(otChannelManagerGetAutoChannelSelectionInterval(GetInstancePtr()))); OutputLine("cca threshold: 0x%04x", otChannelManagerGetCcaFailureRateThreshold(GetInstancePtr())); OutputLine("supported: %s", supportedMask.ToString().AsCString()); - OutputLine("favored: %s", supportedMask.ToString().AsCString()); + OutputLine("favored: %s", favoredMask.ToString().AsCString()); } } + /** + * @cli channel manager change + * @code + * channel manager change 11 + * channel manager change 11 + * Done + * @endcode + * @cparam channel manager change @ca{channel-num} + * @par + * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` is required. + * @par api_copy + * #otChannelManagerRequestChannelChange + */ else if (aArgs[1] == "change") { error = ProcessSet(aArgs + 2, otChannelManagerRequestChannelChange); } #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE + /** + * @cli channel manager select + * @code + * channel manager select 1 + * channel manager select 1 + * Done + * @endcode + * @cparam channel manager select @ca{skip-quality-check} + * Use a `1` or `0` for the boolean `skip-quality-check`. + * @par + * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required. + * @par api_copy + * #otChannelManagerRequestChannelSelect + */ else if (aArgs[1] == "select") { bool enable; @@ -1135,6 +1740,20 @@ template <> otError Interpreter::Process(Arg aArgs[]) error = otChannelManagerRequestChannelSelect(GetInstancePtr(), enable); } #endif + /** + * @cli channel manager auto + * @code + * channel manager auto 1 + * channel manager auto 1 + * Done + * @endcode + * @cparam channel manager auto @ca{enable} + * `1` is a boolean to `enable`. + * @par + * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required. + * @par api_copy + * #otChannelManagerSetAutoChannelSelectionEnabled + */ else if (aArgs[1] == "auto") { bool enable; @@ -1142,22 +1761,88 @@ template <> otError Interpreter::Process(Arg aArgs[]) SuccessOrExit(error = aArgs[2].ParseAsBool(enable)); otChannelManagerSetAutoChannelSelectionEnabled(GetInstancePtr(), enable); } + /** + * @cli channel manager delay + * @code + * channel manager delay 120 + * channel manager delay 120 + * Done + * @endcode + * @cparam channel manager delay @ca{delay-seconds} + * @par + * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required. + * @par api_copy + * #otChannelManagerSetDelay + */ else if (aArgs[1] == "delay") { - error = ProcessSet(aArgs + 2, otChannelManagerSetDelay); - } + error = ProcessGetSet(aArgs + 2, otChannelManagerGetDelay, otChannelManagerSetDelay); + } + /** + * @cli channel manager interval + * @code + * channel manager interval 10800 + * channel manager interval 10800 + * Done + * @endcode + * @cparam channel manager interval @ca{interval-seconds} + * @par + * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required. + * @par api_copy + * #otChannelManagerSetAutoChannelSelectionInterval + */ else if (aArgs[1] == "interval") { error = ProcessSet(aArgs + 2, otChannelManagerSetAutoChannelSelectionInterval); } + /** + * @cli channel manager supported + * @code + * channel manager supported 0x7fffc00 + * channel manager supported 0x7fffc00 + * Done + * @endcode + * @cparam channel manager supported @ca{mask} + * @par + * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required. + * @par api_copy + * #otChannelManagerSetSupportedChannels + */ else if (aArgs[1] == "supported") { error = ProcessSet(aArgs + 2, otChannelManagerSetSupportedChannels); } + /** + * @cli channel manager favored + * @code + * channel manager favored 0x7fffc00 + * channel manager favored 0x7fffc00 + * Done + * @endcode + * @cparam channel manager favored @ca{mask} + * @par + * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required. + * @par api_copy + * #otChannelManagerSetFavoredChannels + */ else if (aArgs[1] == "favored") { error = ProcessSet(aArgs + 2, otChannelManagerSetFavoredChannels); } + /** + * @cli channel manager threshold + * @code + * channel manager threshold 0xffff + * channel manager threshold 0xffff + * Done + * @endcode + * @cparam channel manager threshold @ca{threshold-percent} + * Use a hex value for `threshold-percent`. `0` maps to 0% and `0xffff` maps to 100%. + * @par + * `OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE` and `OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE` are required. + * @par api_copy + * #otChannelManagerSetCcaFailureRateThreshold + */ else if (aArgs[1] == "threshold") { error = ProcessSet(aArgs + 2, otChannelManagerSetCcaFailureRateThreshold); @@ -1193,15 +1878,29 @@ template <> otError Interpreter::Process(Arg aArgs[]) { uint16_t maxChildren; + /** + * @cli child table + * @code + * child table + * | ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt| Extended MAC | + * +-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+------------------+ + * | 1 | 0xc801 | 240 | 24 | 3 | 131 |1|0|0| 3| 0 | 0 | 4ecede68435358ac | + * | 2 | 0xc802 | 240 | 2 | 3 | 131 |0|0|0| 3| 1 | 0 | a672a601d2ce37d8 | + * Done + * @endcode + * @par + * Prints a table of the attached children. + * @sa otThreadGetChildInfoByIndex + */ if (isTable) { static const char *const kChildTableTitles[] = { - "ID", "RLOC16", "Timeout", "Age", "LQ In", "C_VN", "R", - "D", "N", "Ver", "CSL", "QMsgCnt", "Extended MAC", + "ID", "RLOC16", "Timeout", "Age", "LQ In", "C_VN", "R", + "D", "N", "Ver", "CSL", "QMsgCnt", "Suprvsn", "Extended MAC", }; static const uint8_t kChildTableColumnWidths[] = { - 5, 8, 12, 12, 7, 6, 1, 1, 1, 3, 3, 7, 18, + 5, 8, 12, 12, 7, 6, 1, 1, 1, 3, 3, 7, 7, 18, }; OutputTableHeader(kChildTableTitles, kChildTableColumnWidths); @@ -1219,36 +1918,68 @@ template <> otError Interpreter::Process(Arg aArgs[]) if (isTable) { - OutputFormat("| %3d ", childInfo.mChildId); + OutputFormat("| %3u ", childInfo.mChildId); OutputFormat("| 0x%04x ", childInfo.mRloc16); - OutputFormat("| %10d ", childInfo.mTimeout); - OutputFormat("| %10d ", childInfo.mAge); - OutputFormat("| %5d ", childInfo.mLinkQualityIn); - OutputFormat("| %4d ", childInfo.mNetworkDataVersion); + OutputFormat("| %10lu ", ToUlong(childInfo.mTimeout)); + OutputFormat("| %10lu ", ToUlong(childInfo.mAge)); + OutputFormat("| %5u ", childInfo.mLinkQualityIn); + OutputFormat("| %4u ", childInfo.mNetworkDataVersion); OutputFormat("|%1d", childInfo.mRxOnWhenIdle); OutputFormat("|%1d", childInfo.mFullThreadDevice); OutputFormat("|%1d", childInfo.mFullNetworkData); - OutputFormat("|%3d", childInfo.mVersion); + OutputFormat("|%3u", childInfo.mVersion); OutputFormat("| %1d ", childInfo.mIsCslSynced); - OutputFormat("| %5d ", childInfo.mQueuedMessageCnt); + OutputFormat("| %5u ", childInfo.mQueuedMessageCnt); + OutputFormat("| %5u ", childInfo.mSupervisionInterval); OutputFormat("| "); OutputExtAddress(childInfo.mExtAddress); OutputLine(" |"); } + /** + * @cli child list + * @code + * child list + * 1 2 3 6 7 8 + * Done + * @endcode + * @par + * Returns a list of attached Child IDs. + * @sa otThreadGetChildInfoByIndex + */ else { - OutputFormat("%d ", childInfo.mChildId); + OutputFormat("%u ", childInfo.mChildId); } } - OutputLine(""); + OutputNewLine(); ExitNow(); } SuccessOrExit(error = aArgs[0].ParseAsUint16(childId)); SuccessOrExit(error = otThreadGetChildInfoById(GetInstancePtr(), childId, &childInfo)); - OutputLine("Child ID: %d", childInfo.mChildId); + /** + * @cli child (id) + * @code + * child 1 + * Child ID: 1 + * Rloc: 9c01 + * Ext Addr: e2b3540590b0fd87 + * Mode: rn + * CSL Synchronized: 1 + * Net Data: 184 + * Timeout: 100 + * Age: 0 + * Link Quality In: 3 + * RSSI: -20 + * Done + * @endcode + * @cparam child @ca{child-id} + * @par api_copy + * #otThreadGetChildInfoById + */ + OutputLine("Child ID: %u", childInfo.mChildId); OutputLine("Rloc: %04x", childInfo.mRloc16); OutputFormat("Ext Addr: "); OutputExtAddressLine(childInfo.mExtAddress); @@ -1256,11 +1987,13 @@ template <> otError Interpreter::Process(Arg aArgs[]) linkMode.mDeviceType = childInfo.mFullThreadDevice; linkMode.mNetworkData = childInfo.mFullThreadDevice; OutputLine("Mode: %s", LinkModeToString(linkMode, linkModeString)); - OutputLine("Net Data: %d", childInfo.mNetworkDataVersion); - OutputLine("Timeout: %d", childInfo.mTimeout); - OutputLine("Age: %d", childInfo.mAge); - OutputLine("Link Quality In: %d", childInfo.mLinkQualityIn); + OutputLine("CSL Synchronized: %d ", childInfo.mIsCslSynced); + OutputLine("Net Data: %u", childInfo.mNetworkDataVersion); + OutputLine("Timeout: %lu", ToUlong(childInfo.mTimeout)); + OutputLine("Age: %lu", ToUlong(childInfo.mAge)); + OutputLine("Link Quality In: %u", childInfo.mLinkQualityIn); OutputLine("RSSI: %d", childInfo.mAverageRssi); + OutputLine("Supervision Interval: %d", childInfo.mSupervisionInterval); exit: return error; @@ -1270,6 +2003,17 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; + /** + * @cli childip + * @code + * childip + * 3401: fdde:ad00:beef:0:3037:3e03:8c5f:bc0c + * Done + * @endcode + * @par + * Gets a list of IP addresses stored for MTD children. + * @sa otThreadGetChildNextIp6Address + */ if (aArgs[0].IsEmpty()) { uint16_t maxChildren = otThreadGetMaxAllowedChildren(GetInstancePtr()); @@ -1296,11 +2040,31 @@ template <> otError Interpreter::Process(Arg aArgs[]) } } } + /** + * @cli childip max + * @code + * childip max + * 4 + * Done + * @endcode + * @par api_copy + * #otThreadGetMaxChildIpAddresses + */ else if (aArgs[0] == "max") { #if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE error = ProcessGet(aArgs + 1, otThreadGetMaxChildIpAddresses); #else + /** + * @cli childip max (set) + * @code + * childip max 2 + * Done + * @endcode + * @cparam childip max @ca{count} + * @par api_copy + * #otThreadSetMaxChildIpAddresses + */ error = ProcessGetSet(aArgs + 1, otThreadGetMaxChildIpAddresses, otThreadSetMaxChildIpAddresses); #endif } @@ -1312,49 +2076,129 @@ template <> otError Interpreter::Process(Arg aArgs[]) return error; } +/** + * @cli childmax + * @code + * childmax + * 5 + * Done + * @endcode + * @par api_copy + * #otThreadGetMaxAllowedChildren + */ template <> otError Interpreter::Process(Arg aArgs[]) { + /** + * @cli childmax (set) + * @code + * childmax 2 + * Done + * @endcode + * @cparam childmax @ca{count} + * @par api_copy + * #otThreadSetMaxAllowedChildren + */ return ProcessGetSet(aArgs, otThreadGetMaxAllowedChildren, otThreadSetMaxAllowedChildren); } #endif // OPENTHREAD_FTD -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_INVALID_ARGS; + /** + * @cli childsupervision checktimeout + * @code + * childsupervision checktimeout + * 30 + * Done + * @endcode + * @par api_copy + * #otChildSupervisionGetCheckTimeout + */ if (aArgs[0] == "checktimeout") { + /** @cli childsupervision checktimeout (set) + * @code + * childsupervision checktimeout 30 + * Done + * @endcode + * @cparam childsupervision checktimeout @ca{timeout-seconds} + * @par api_copy + * #otChildSupervisionSetCheckTimeout + */ error = ProcessGetSet(aArgs + 1, otChildSupervisionGetCheckTimeout, otChildSupervisionSetCheckTimeout); } -#if OPENTHREAD_FTD + /** + * @cli childsupervision interval + * @code + * childsupervision interval + * 30 + * Done + * @endcode + * @par api_copy + * #otChildSupervisionGetInterval + */ else if (aArgs[0] == "interval") { + /** + * @cli childsupervision interval (set) + * @code + * childsupervision interval 30 + * Done + * @endcode + * @cparam childsupervision interval @ca{interval-seconds} + * @par api_copy + * #otChildSupervisionSetInterval + */ error = ProcessGetSet(aArgs + 1, otChildSupervisionGetInterval, otChildSupervisionSetInterval); } -#endif + else if (aArgs[0] == "failcounter") + { + if (aArgs[1].IsEmpty()) + { + OutputLine("%u", otChildSupervisionGetCheckFailureCounter(GetInstancePtr())); + error = OT_ERROR_NONE; + } + else if (aArgs[1] == "reset") + { + otChildSupervisionResetCheckFailureCounter(GetInstancePtr()); + error = OT_ERROR_NONE; + } + } return error; } -#endif // OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE +/** @cli childtimeout + * @code + * childtimeout + * 300 + * Done + * @endcode + * @par api_copy + * #otThreadGetChildTimeout + */ template <> otError Interpreter::Process(Arg aArgs[]) { + /** @cli childtimeout (set) + * @code + * childtimeout 300 + * Done + * @endcode + * @cparam childtimeout @ca{timeout-seconds} + * @par api_copy + * #otThreadSetChildTimeout + */ return ProcessGetSet(aArgs, otThreadGetChildTimeout, otThreadSetChildTimeout); } #if OPENTHREAD_CONFIG_COAP_API_ENABLE -template <> otError Interpreter::Process(Arg aArgs[]) -{ - return mCoap.Process(aArgs); -} +template <> otError Interpreter::Process(Arg aArgs[]) { return mCoap.Process(aArgs); } #endif #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE -template <> otError Interpreter::Process(Arg aArgs[]) -{ - return mCoapSecure.Process(aArgs); -} +template <> otError Interpreter::Process(Arg aArgs[]) { return mCoapSecure.Process(aArgs); } #endif #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE @@ -1376,7 +2220,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) struct RadioCoexMetricName { const uint32_t otRadioCoexMetrics::*mValuePtr; - const char * mName; + const char *mName; }; static const RadioCoexMetricName kTxMetricNames[] = { @@ -1407,19 +2251,19 @@ template <> otError Interpreter::Process(Arg aArgs[]) SuccessOrExit(error = otPlatRadioGetCoexMetrics(GetInstancePtr(), &metrics)); OutputLine("Stopped: %s", metrics.mStopped ? "true" : "false"); - OutputLine("Grant Glitch: %u", metrics.mNumGrantGlitch); + OutputLine("Grant Glitch: %lu", ToUlong(metrics.mNumGrantGlitch)); OutputLine("Transmit metrics"); for (const RadioCoexMetricName &metric : kTxMetricNames) { - OutputLine(kIndentSize, "%s: %u", metric.mName, metrics.*metric.mValuePtr); + OutputLine(kIndentSize, "%s: %lu", metric.mName, ToUlong(metrics.*metric.mValuePtr)); } OutputLine("Receive metrics"); for (const RadioCoexMetricName &metric : kRxMetricNames) { - OutputLine(kIndentSize, "%s: %u", metric.mName, metrics.*metric.mValuePtr); + OutputLine(kIndentSize, "%s: %lu", metric.mName, ToUlong(metrics.*metric.mValuePtr)); } } else @@ -1433,22 +2277,177 @@ template <> otError Interpreter::Process(Arg aArgs[]) #endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE #if OPENTHREAD_FTD +/** + * @cli contextreusedelay (get,set) + * @code + * contextreusedelay + * 11 + * Done + * @endcode + * @code + * contextreusedelay 11 + * Done + * @endcode + * @cparam contextreusedelay @ca{delay} + * Use the optional `delay` argument to set the `CONTEXT_ID_REUSE_DELAY`. + * @par + * Gets or sets the `CONTEXT_ID_REUSE_DELAY` value. + * @sa otThreadGetContextIdReuseDelay + * @sa otThreadSetContextIdReuseDelay + */ template <> otError Interpreter::Process(Arg aArgs[]) { return ProcessGetSet(aArgs, otThreadGetContextIdReuseDelay, otThreadSetContextIdReuseDelay); } #endif +#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE +void Interpreter::OutputBorderRouterCounters(void) +{ + struct BrCounterName + { + const otPacketsAndBytes otBorderRoutingCounters::*mPacketsAndBytes; + const char *mName; + }; + + static const BrCounterName kCounterNames[] = { + {&otBorderRoutingCounters::mInboundUnicast, "Inbound Unicast"}, + {&otBorderRoutingCounters::mInboundMulticast, "Inbound Multicast"}, + {&otBorderRoutingCounters::mOutboundUnicast, "Outbound Unicast"}, + {&otBorderRoutingCounters::mOutboundMulticast, "Outbound Multicast"}, + }; + + const otBorderRoutingCounters *brCounters = otIp6GetBorderRoutingCounters(GetInstancePtr()); + Uint64StringBuffer uint64StringBuffer; + + for (const BrCounterName &counter : kCounterNames) + { + OutputFormat("%s:", counter.mName); + OutputFormat(" Packets %s", + Uint64ToString((brCounters->*counter.mPacketsAndBytes).mPackets, uint64StringBuffer)); + OutputLine(" Bytes %s", Uint64ToString((brCounters->*counter.mPacketsAndBytes).mBytes, uint64StringBuffer)); + } + + OutputLine("RA Rx: %lu", ToUlong(brCounters->mRaRx)); + OutputLine("RA TxSuccess: %lu", ToUlong(brCounters->mRaTxSuccess)); + OutputLine("RA TxFailed: %lu", ToUlong(brCounters->mRaTxFailure)); + OutputLine("RS Rx: %lu", ToUlong(brCounters->mRsRx)); + OutputLine("RS TxSuccess: %lu", ToUlong(brCounters->mRsTxSuccess)); + OutputLine("RS TxFailed: %lu", ToUlong(brCounters->mRsTxFailure)); +} +#endif // OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; + /** + * @cli counters + * @code + * counters + * ip + * mac + * mle + * Done + * @endcode + * @par + * Gets the supported counter names. + */ if (aArgs[0].IsEmpty()) { +#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + OutputLine("br"); +#endif OutputLine("ip"); OutputLine("mac"); OutputLine("mle"); } +#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + /** + * @cli counters br + * @code + * counters br + * Inbound Unicast: Packets 4 Bytes 320 + * Inbound Multicast: Packets 0 Bytes 0 + * Outbound Unicast: Packets 2 Bytes 160 + * Outbound Multicast: Packets 0 Bytes 0 + * RA Rx: 4 + * RA TxSuccess: 2 + * RA TxFailed: 0 + * RS Rx: 0 + * RS TxSuccess: 2 + * RS TxFailed: 0 + * Done + * @endcode + * @par api_copy + * #otIp6GetBorderRoutingCounters + */ + else if (aArgs[0] == "br") + { + if (aArgs[1].IsEmpty()) + { + OutputBorderRouterCounters(); + } + /** + * @cli counters br reset + * @code + * counters br reset + * Done + * @endcode + * @par api_copy + * #otIp6ResetBorderRoutingCounters + */ + else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty()) + { + otIp6ResetBorderRoutingCounters(GetInstancePtr()); + } + else + { + error = OT_ERROR_INVALID_ARGS; + } + } +#endif + /** + * @cli counters (mac) + * @code + * counters mac + * TxTotal: 10 + * TxUnicast: 3 + * TxBroadcast: 7 + * TxAckRequested: 3 + * TxAcked: 3 + * TxNoAckRequested: 7 + * TxData: 10 + * TxDataPoll: 0 + * TxBeacon: 0 + * TxBeaconRequest: 0 + * TxOther: 0 + * TxRetry: 0 + * TxErrCca: 0 + * TxErrBusyChannel: 0 + * RxTotal: 2 + * RxUnicast: 1 + * RxBroadcast: 1 + * RxData: 2 + * RxDataPoll: 0 + * RxBeacon: 0 + * RxBeaconRequest: 0 + * RxOther: 0 + * RxAddressFiltered: 0 + * RxDestAddrFiltered: 0 + * RxDuplicated: 0 + * RxErrNoFrame: 0 + * RxErrNoUnknownNeighbor: 0 + * RxErrInvalidSrcAddr: 0 + * RxErrSec: 0 + * RxErrFcs: 0 + * RxErrOther: 0 + * Done + * @endcode + * @cparam counters @ca{mac} + * @par api_copy + * #otLinkGetCounters + */ else if (aArgs[0] == "mac") { if (aArgs[1].IsEmpty()) @@ -1456,7 +2455,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) struct MacCounterName { const uint32_t otMacCounters::*mValuePtr; - const char * mName; + const char *mName; }; static const MacCounterName kTxCounterNames[] = { @@ -1473,6 +2472,9 @@ template <> otError Interpreter::Process(Arg aArgs[]) {&otMacCounters::mTxRetry, "TxRetry"}, {&otMacCounters::mTxErrCca, "TxErrCca"}, {&otMacCounters::mTxErrBusyChannel, "TxErrBusyChannel"}, + {&otMacCounters::mTxErrAbort, "TxErrAbort"}, + {&otMacCounters::mTxDirectMaxRetryExpiry, "TxDirectMaxRetryExpiry"}, + {&otMacCounters::mTxIndirectMaxRetryExpiry, "TxIndirectMaxRetryExpiry"}, }; static const MacCounterName kRxCounterNames[] = { @@ -1496,20 +2498,30 @@ template <> otError Interpreter::Process(Arg aArgs[]) const otMacCounters *macCounters = otLinkGetCounters(GetInstancePtr()); - OutputLine("TxTotal: %d", macCounters->mTxTotal); + OutputLine("TxTotal: %lu", ToUlong(macCounters->mTxTotal)); for (const MacCounterName &counter : kTxCounterNames) { - OutputLine(kIndentSize, "%s: %u", counter.mName, macCounters->*counter.mValuePtr); + OutputLine(kIndentSize, "%s: %lu", counter.mName, ToUlong(macCounters->*counter.mValuePtr)); } - OutputLine("RxTotal: %d", macCounters->mRxTotal); + OutputLine("RxTotal: %lu", ToUlong(macCounters->mRxTotal)); for (const MacCounterName &counter : kRxCounterNames) { - OutputLine(kIndentSize, "%s: %u", counter.mName, macCounters->*counter.mValuePtr); + OutputLine(kIndentSize, "%s: %lu", counter.mName, ToUlong(macCounters->*counter.mValuePtr)); } } + /** + * @cli counters mac reset + * @code + * counters mac reset + * Done + * @endcode + * @cparam counters @ca{mac} reset + * @par api_copy + * #otLinkResetCounters + */ else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty()) { otLinkResetCounters(GetInstancePtr()); @@ -1519,6 +2531,25 @@ template <> otError Interpreter::Process(Arg aArgs[]) error = OT_ERROR_INVALID_ARGS; } } + /** + * @cli counters (mle) + * @code + * counters mle + * Role Disabled: 0 + * Role Detached: 1 + * Role Child: 0 + * Role Router: 0 + * Role Leader: 1 + * Attach Attempts: 1 + * Partition Id Changes: 1 + * Better Partition Attach Attempts: 0 + * Parent Changes: 0 + * Done + * @endcode + * @cparam counters @ca{mle} + * @par api_copy + * #otThreadGetMleCounters + */ else if (aArgs[0] == "mle") { if (aArgs[1].IsEmpty()) @@ -1526,7 +2557,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) struct MleCounterName { const uint16_t otMleCounters::*mValuePtr; - const char * mName; + const char *mName; }; static const MleCounterName kCounterNames[] = { @@ -1545,9 +2576,43 @@ template <> otError Interpreter::Process(Arg aArgs[]) for (const MleCounterName &counter : kCounterNames) { - OutputLine("%s: %d", counter.mName, mleCounters->*counter.mValuePtr); + OutputLine("%s: %u", counter.mName, mleCounters->*counter.mValuePtr); } +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + { + struct MleTimeCounterName + { + const uint64_t otMleCounters::*mValuePtr; + const char *mName; + }; + + static const MleTimeCounterName kTimeCounterNames[] = { + {&otMleCounters::mDisabledTime, "Disabled"}, {&otMleCounters::mDetachedTime, "Detached"}, + {&otMleCounters::mChildTime, "Child"}, {&otMleCounters::mRouterTime, "Router"}, + {&otMleCounters::mLeaderTime, "Leader"}, + }; + + for (const MleTimeCounterName &counter : kTimeCounterNames) + { + OutputFormat("Time %s Milli: ", counter.mName); + OutputUint64Line(mleCounters->*counter.mValuePtr); + } + + OutputFormat("Time Tracked Milli: "); + OutputUint64Line(mleCounters->mTrackedTime); + } +#endif } + /** + * @cli counters mle reset + * @code + * counters mle reset + * Done + * @endcode + * @cparam counters @ca{mle} reset + * @par api_copy + * #otThreadResetMleCounters + */ else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty()) { otThreadResetMleCounters(GetInstancePtr()); @@ -1557,6 +2622,20 @@ template <> otError Interpreter::Process(Arg aArgs[]) error = OT_ERROR_INVALID_ARGS; } } + /** + * @cli counters ip + * @code + * counters ip + * TxSuccess: 10 + * TxFailed: 0 + * RxSuccess: 5 + * RxFailed: 0 + * Done + * @endcode + * @cparam counters @ca{ip} + * @par api_copy + * #otThreadGetIp6Counters + */ else if (aArgs[0] == "ip") { if (aArgs[1].IsEmpty()) @@ -1564,7 +2643,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) struct IpCounterName { const uint32_t otIpCounters::*mValuePtr; - const char * mName; + const char *mName; }; static const IpCounterName kCounterNames[] = { @@ -1578,9 +2657,19 @@ template <> otError Interpreter::Process(Arg aArgs[]) for (const IpCounterName &counter : kCounterNames) { - OutputLine("%s: %d", counter.mName, ipCounters->*counter.mValuePtr); + OutputLine("%s: %lu", counter.mName, ToUlong(ipCounters->*counter.mValuePtr)); } } + /** + * @cli counters ip reset + * @code + * counters ip reset + * Done + * @endcode + * @cparam counters @ca{ip} reset + * @par api_copy + * #otThreadResetIp6Counters + */ else if ((aArgs[1] == "reset") && aArgs[2].IsEmpty()) { otThreadResetIp6Counters(GetInstancePtr()); @@ -1603,21 +2692,67 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; + /** + * @cli csl + * @code + * csl + * Channel: 11 + * Period: 1000 (in units of 10 symbols), 160ms + * Timeout: 1000s + * Done + * @endcode + * @par + * Gets the CSL configuration. + * @sa otLinkCslGetChannel + * @sa otLinkCslGetPeriod + * @sa otLinkCslGetPeriod + * @sa otLinkCslGetTimeout + */ if (aArgs[0].IsEmpty()) { OutputLine("Channel: %u", otLinkCslGetChannel(GetInstancePtr())); - OutputLine("Period: %u(in units of 10 symbols), %ums", otLinkCslGetPeriod(GetInstancePtr()), - otLinkCslGetPeriod(GetInstancePtr()) * kUsPerTenSymbols / 1000); - OutputLine("Timeout: %us", otLinkCslGetTimeout(GetInstancePtr())); + OutputLine("Period: %u(in units of 10 symbols), %lums", otLinkCslGetPeriod(GetInstancePtr()), + ToUlong(otLinkCslGetPeriod(GetInstancePtr()) * kUsPerTenSymbols / 1000)); + OutputLine("Timeout: %lus", ToUlong(otLinkCslGetTimeout(GetInstancePtr()))); } + /** + * @cli csl channel + * @code + * csl channel 20 + * Done + * @endcode + * @cparam csl channel @ca{channel} + * @par api_copy + * #otLinkCslSetChannel + */ else if (aArgs[0] == "channel") { error = ProcessSet(aArgs + 1, otLinkCslSetChannel); } + /** + * @cli csl period + * @code + * csl period 3000 + * Done + * @endcode + * @cparam csl period @ca{period} + * @par api_copy + * #otLinkCslSetPeriod + */ else if (aArgs[0] == "period") { error = ProcessSet(aArgs + 1, otLinkCslSetPeriod); } + /** + * @cli csl timeout + * @code + * cls timeout 10 + * Done + * @endcode + * @cparam csl timeout @ca{timeout} + * @par api_copy + * #otLinkCslSetTimeout + */ else if (aArgs[0] == "timeout") { error = ProcessSet(aArgs + 1, otLinkCslSetTimeout); @@ -1636,10 +2771,32 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; + /** + * @cli delaytimermin + * @code + * delaytimermin + * 30 + * Done + * @endcode + * @par + * Get the minimal delay timer (in seconds). + * @sa otDatasetGetDelayTimerMinimal + */ if (aArgs[0].IsEmpty()) { - OutputLine("%d", (otDatasetGetDelayTimerMinimal(GetInstancePtr()) / 1000)); + OutputLine("%lu", ToUlong((otDatasetGetDelayTimerMinimal(GetInstancePtr()) / 1000))); } + /** + * @cli delaytimermin (set) + * @code + * delaytimermin 60 + * Done + * @endcode + * @cparam delaytimermin @ca{delaytimermin} + * @par + * Sets the minimal delay timer (in seconds). + * @sa otDatasetSetDelayTimerMinimal + */ else if (aArgs[1].IsEmpty()) { uint32_t delay; @@ -1675,11 +2832,57 @@ template <> otError Interpreter::Process(Arg aArgs[]) return error; } +/** + * @cli discover + * @code + * discover + * | J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI | + * +---+------------------+------------------+------+------------------+----+-----+-----+ + * | 0 | OpenThread | dead00beef00cafe | ffff | f1d92a82c8d8fe43 | 11 | -20 | 0 | + * Done + * @endcode + * @cparam discover [@ca{channel}] + * `channel`: The channel to discover on. If no channel is provided, the discovery will cover all + * valid channels. + * @par + * Perform an MLE Discovery operation. + * @sa otThreadDiscover + */ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; uint32_t scanChannels = 0; +#if OPENTHREAD_FTD + /** + * @cli discover reqcallback (enable,disable) + * @code + * discover reqcallback enable + * Done + * @endcode + * @cparam discover reqcallback @ca{enable|disable} + * @par api_copy + * #otThreadSetDiscoveryRequestCallback + */ + if (aArgs[0] == "reqcallback") + { + bool enable; + otThreadDiscoveryRequestCallback callback = nullptr; + void *context = nullptr; + + SuccessOrExit(error = ParseEnableOrDisable(aArgs[1], enable)); + + if (enable) + { + callback = &Interpreter::HandleDiscoveryRequest; + context = this; + } + + otThreadSetDiscoveryRequestCallback(GetInstancePtr(), callback, context); + ExitNow(); + } +#endif // OPENTHREAD_FTD + if (!aArgs[0].IsEmpty()) { uint8_t channel; @@ -1722,9 +2925,49 @@ template <> otError Interpreter::Process(Arg aArgs[]) { error = OT_ERROR_INVALID_ARGS; } + /** + * @cli dns compression + * @code + * dns compression + * Enabled + * @endcode + * @cparam dns compression [@ca{enable|disable}] + * @par api_copy + * #otDnsIsNameCompressionEnabled + * @par + * By default DNS name compression is enabled. When disabled, + * DNS names are appended as full and never compressed. This + * is applicable to OpenThread's DNS and SRP client/server + * modules." + * 'OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE' is required. + */ #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE else if (aArgs[0] == "compression") { + /** + * @cli dns compression (enable,disable) + * @code + * dns compression enable + * Enabled + * @endcode + * @code + * dns compression disable + * Done + * dns compression + * Disabled + * Done + * @endcode + * @cparam dns compression [@ca{enable|disable}] + * @par + * Set the "DNS name compression" mode. + * @par + * By default DNS name compression is enabled. When disabled, + * DNS names are appended as full and never compressed. This + * is applicable to OpenThread's DNS and SRP client/server + * modules." + * 'OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE' is required. + * @sa otDnsSetNameCompressionEnabled + */ if (aArgs[1].IsEmpty()) { OutputEnabledDisabledStatus(otDnsIsNameCompressionEnabled()); @@ -1739,25 +2982,124 @@ template <> otError Interpreter::Process(Arg aArgs[]) } #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE + else if (aArgs[0] == "config") { + /** + * @cli dns config + * @code + * dns config + * Server: [fd00:0:0:0:0:0:0:1]:1234 + * ResponseTimeout: 5000 ms + * MaxTxAttempts: 2 + * RecursionDesired: no + * ServiceMode: srv + * Nat64Mode: allow + * Done + * @endcode + * @par api_copy + * #otDnsClientGetDefaultConfig + * @par + * The config includes the server IPv6 address and port, response + * timeout in msec (wait time to rx response), maximum tx attempts + * before reporting failure, boolean flag to indicate whether the server + * can resolve the query recursively or not. + * 'OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE' is required. + */ if (aArgs[1].IsEmpty()) { const otDnsQueryConfig *defaultConfig = otDnsClientGetDefaultConfig(GetInstancePtr()); OutputFormat("Server: "); OutputSockAddrLine(defaultConfig->mServerSockAddr); - OutputLine("ResponseTimeout: %u ms", defaultConfig->mResponseTimeout); + OutputLine("ResponseTimeout: %lu ms", ToUlong(defaultConfig->mResponseTimeout)); OutputLine("MaxTxAttempts: %u", defaultConfig->mMaxTxAttempts); OutputLine("RecursionDesired: %s", (defaultConfig->mRecursionFlag == OT_DNS_FLAG_RECURSION_DESIRED) ? "yes" : "no"); + OutputLine("ServiceMode: %s", DnsConfigServiceModeToString(defaultConfig->mServiceMode)); +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + OutputLine("Nat64Mode: %s", (defaultConfig->mNat64Mode == OT_DNS_NAT64_ALLOW) ? "allow" : "disallow"); +#endif +#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE + OutputLine("TransportProtocol: %s", + (defaultConfig->mTransportProto == OT_DNS_TRANSPORT_UDP) ? "udp" : "tcp"); +#endif } + /** + * @cli dns config (set) + * @code + * dns config fd00::1 1234 5000 2 0 + * Done + * @endcode + * @code + * dns config + * Server: [fd00:0:0:0:0:0:0:1]:1234 + * ResponseTimeout: 5000 ms + * MaxTxAttempts: 2 + * RecursionDesired: no + * Done + * @endcode + * @code + * dns config fd00::2 + * Done + * @endcode + * @code + * dns config + * Server: [fd00:0:0:0:0:0:0:2]:53 + * ResponseTimeout: 3000 ms + * MaxTxAttempts: 3 + * RecursionDesired: yes + * Done + * @endcode + * @par api_copy + * #otDnsClientSetDefaultConfig + * @cparam dns config [@ca{dns-server-IP}] [@ca{dns-server-port}] [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] [@ca{recursion-desired-boolean}] [@ca{service-mode}] + * @par + * We can leave some of the fields as unspecified (or use value zero). The + * unspecified fields are replaced by the corresponding OT config option + * definitions OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT to form the default + * query config. + * 'OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE' is required. + */ else { SuccessOrExit(error = GetDnsConfig(aArgs + 1, config)); otDnsClientSetDefaultConfig(GetInstancePtr(), config); } } + /** + * @cli dns resolve + * @code + * dns resolve ipv6.google.com + * DNS response for ipv6.google.com - 2a00:1450:401b:801:0:0:0:200e TTL: 300 + * @endcode + * @code + * dns resolve example.com 8.8.8.8 + * Synthesized IPv6 DNS server address: fdde:ad00:beef:2:0:0:808:808 + * DNS response for example.com. - fd4c:9574:3720:2:0:0:5db8:d822 TTL:20456 + * Done + * @endcode + * @cparam dns resolve @ca{hostname} [@ca{dns-server-IP}] [@ca{dns-server-port}] [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] [@ca{recursion-desired-boolean}] + * @par api_copy + * #otDnsClientResolveAddress + * @par + * Send DNS Query to obtain IPv6 address for given hostname. + * @par + * The parameters after hostname are optional. Any unspecified (or zero) value + * for these optional parameters is replaced by the value from the current default + * config (dns config). + * @par + * The DNS server IP can be an IPv4 address, which will be synthesized to an + * IPv6 address using the preferred NAT64 prefix from the network data. + * @par + * Note: The command will return InvalidState when the DNS server IP is an IPv4 + * address but the preferred NAT64 prefix is unavailable. + * 'OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE' is required. + */ else if (aArgs[0] == "resolve") { VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); @@ -1777,6 +3119,51 @@ template <> otError Interpreter::Process(Arg aArgs[]) } #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE + /** + * @cli dns browse + * @code + * dns browse _service._udp.example.com + * DNS browse response for _service._udp.example.com. + * inst1 + * Port:1234, Priority:1, Weight:2, TTL:7200 + * Host:host.example.com. + * HostAddress:fd00:0:0:0:0:0:0:abcd TTL:7200 + * TXT:[a=6531, b=6c12] TTL:7300 + * instance2 + * Port:1234, Priority:1, Weight:2, TTL:7200 + * Host:host.example.com. + * HostAddress:fd00:0:0:0:0:0:0:abcd TTL:7200 + * TXT:[a=1234] TTL:7300 + * Done + * @endcode + * @code + * dns browse _airplay._tcp.default.service.arpa + * DNS browse response for _airplay._tcp.default.service.arpa. + * Mac mini + * Port:7000, Priority:0, Weight:0, TTL:10 + * Host:Mac-mini.default.service.arpa. + * HostAddress:fd97:739d:386a:1:1c2e:d83c:fcbe:9cf4 TTL:10 + * Done + * @endcode + * @cparam dns browse @ca{service-name} [@ca{dns-server-IP}] [@ca{dns-server-port}] [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] [@ca{recursion-desired-boolean}] + * @sa otDnsClientBrowse + * @par + * Send a browse (service instance enumeration) DNS query to get the list of services for + * given service-name + * @par + * The parameters after `service-name` are optional. Any unspecified (or zero) value + * for these optional parameters is replaced by the value from the current default + * config (`dns config`). + * @par + * Note: The DNS server IP can be an IPv4 address, which will be synthesized to an IPv6 + * address using the preferred NAT64 prefix from the network data. The command will return + * `InvalidState` when the DNS server IP is an IPv4 address but the preferred NAT64 prefix + * is unavailable. When testing DNS-SD discovery proxy, the zone is not `local` and + * instead should be `default.service.arpa`. + * 'OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE' is required. + */ else if (aArgs[0] == "browse") { VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); @@ -1785,16 +3172,88 @@ template <> otError Interpreter::Process(Arg aArgs[]) &Interpreter::HandleDnsBrowseResponse, this, config)); error = OT_ERROR_PENDING; } - else if (aArgs[0] == "service") - { - VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); - SuccessOrExit(error = GetDnsConfig(aArgs + 3, config)); + /** + * @cli dns service + * @cparam dns service @ca{service-instance-label} @ca{service-name} [@ca{DNS-server-IP}] [@ca{DNS-server-port}] [@ca{response-timeout-ms}] [@ca{max-tx-attempts}] [@ca{recursion-desired-boolean}] + * @par api_copy + * #otDnsClientResolveService + * @par + * Send a service instance resolution DNS query for a given service instance. + * Service instance label is provided first, followed by the service name + * (note that service instance label can contain dot '.' character). + * @par + * The parameters after `service-name` are optional. Any unspecified (or zero) + * value for these optional parameters is replaced by the value from the + * current default config (`dns config`). + * @par + * Note: The DNS server IP can be an IPv4 address, which will be synthesized + * to an IPv6 address using the preferred NAT64 prefix from the network data. + * The command will return `InvalidState` when the DNS * server IP is an IPv4 + * address but the preferred NAT64 prefix is unavailable. + * 'OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE' is required. + */ + else if (aArgs[0] == "service") + { + VerifyOrExit(!aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + SuccessOrExit(error = GetDnsConfig(aArgs + 3, config)); SuccessOrExit(error = otDnsClientResolveService(GetInstancePtr(), aArgs[1].GetCString(), aArgs[2].GetCString(), &Interpreter::HandleDnsServiceResponse, this, config)); error = OT_ERROR_PENDING; } #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE #endif // OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE +#if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE + else if (aArgs[0] == "server") + { + if (aArgs[1].IsEmpty()) + { + error = OT_ERROR_INVALID_ARGS; + } +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + else if (aArgs[1] == "upstream") + { + /** + * @cli dns server upstream + * @code + * dns server upstream + * Enabled + * Done + * @endcode + * @par api_copy + * #otDnssdUpstreamQueryIsEnabled + */ + if (aArgs[2].IsEmpty()) + { + OutputEnabledDisabledStatus(otDnssdUpstreamQueryIsEnabled(GetInstancePtr())); + } + /** + * @cli dns server upstream {enable|disable} + * @code + * dns server upstream enable + * Done + * @endcode + * @cparam dns server upstream @ca{enable|disable} + * @par api_copy + * #otDnssdUpstreamQuerySetEnabled + */ + else + { + bool enable; + + SuccessOrExit(error = ParseEnableOrDisable(aArgs[2], enable)); + otDnssdUpstreamQuerySetEnabled(GetInstancePtr(), enable); + } + } +#endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + else + { + ExitNow(error = OT_ERROR_INVALID_COMMAND); + } + } +#endif // OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE else { ExitNow(error = OT_ERROR_INVALID_COMMAND); @@ -1806,20 +3265,85 @@ template <> otError Interpreter::Process(Arg aArgs[]) #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE +const char *Interpreter::DnsConfigServiceModeToString(otDnsServiceMode aMode) const +{ + static const char *const kServiceModeStrings[] = { + "unspec", // OT_DNS_SERVICE_MODE_UNSPECIFIED (0) + "srv", // OT_DNS_SERVICE_MODE_SRV (1) + "txt", // OT_DNS_SERVICE_MODE_TXT (2) + "srv_txt", // OT_DNS_SERVICE_MODE_SRV_TXT (3) + "srv_txt_sep", // OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE (4) + "srv_txt_opt", // OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE (5) + }; + + static_assert(OT_DNS_SERVICE_MODE_UNSPECIFIED == 0, "OT_DNS_SERVICE_MODE_UNSPECIFIED value is incorrect"); + static_assert(OT_DNS_SERVICE_MODE_SRV == 1, "OT_DNS_SERVICE_MODE_SRV value is incorrect"); + static_assert(OT_DNS_SERVICE_MODE_TXT == 2, "OT_DNS_SERVICE_MODE_TXT value is incorrect"); + static_assert(OT_DNS_SERVICE_MODE_SRV_TXT == 3, "OT_DNS_SERVICE_MODE_SRV_TXT value is incorrect"); + static_assert(OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE == 4, "OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE value is incorrect"); + static_assert(OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE == 5, "OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE value is incorrect"); + + return Stringify(aMode, kServiceModeStrings); +} + +otError Interpreter::ParseDnsServiceMode(const Arg &aArg, otDnsServiceMode &aMode) const +{ + otError error = OT_ERROR_NONE; + + if (aArg == "def") + { + aMode = OT_DNS_SERVICE_MODE_UNSPECIFIED; + } + else if (aArg == "srv") + { + aMode = OT_DNS_SERVICE_MODE_SRV; + } + else if (aArg == "txt") + { + aMode = OT_DNS_SERVICE_MODE_TXT; + } + else if (aArg == "srv_txt") + { + aMode = OT_DNS_SERVICE_MODE_SRV_TXT; + } + else if (aArg == "srv_txt_sep") + { + aMode = OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE; + } + else if (aArg == "srv_txt_opt") + { + aMode = OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE; + } + else + { + error = OT_ERROR_INVALID_ARGS; + } + + return error; +} + otError Interpreter::GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig) { // This method gets the optional DNS config from `aArgs[]`. - // The format: `[server IPv6 address] [server port] [timeout] - // [max tx attempt] [recursion desired]`. + // The format: `[server IP address] [server port] [timeout] + // [max tx attempt] [recursion desired] [service mode] + // [transport]` otError error = OT_ERROR_NONE; bool recursionDesired; + bool nat64SynthesizedAddress; memset(aConfig, 0, sizeof(otDnsQueryConfig)); VerifyOrExit(!aArgs[0].IsEmpty(), aConfig = nullptr); - SuccessOrExit(error = aArgs[0].ParseAsIp6Address(aConfig->mServerSockAddr.mAddress)); + SuccessOrExit(error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], aConfig->mServerSockAddr.mAddress, + nat64SynthesizedAddress)); + if (nat64SynthesizedAddress) + { + OutputFormat("Synthesized IPv6 DNS server address: "); + OutputIp6AddressLine(aConfig->mServerSockAddr.mAddress); + } VerifyOrExit(!aArgs[1].IsEmpty()); SuccessOrExit(error = aArgs[1].ParseAsUint16(aConfig->mServerSockAddr.mPort)); @@ -1834,6 +3358,24 @@ otError Interpreter::GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig) SuccessOrExit(error = aArgs[4].ParseAsBool(recursionDesired)); aConfig->mRecursionFlag = recursionDesired ? OT_DNS_FLAG_RECURSION_DESIRED : OT_DNS_FLAG_NO_RECURSION; + VerifyOrExit(!aArgs[5].IsEmpty()); + SuccessOrExit(error = ParseDnsServiceMode(aArgs[5], aConfig->mServiceMode)); + + VerifyOrExit(!aArgs[6].IsEmpty()); + + if (aArgs[6] == "tcp") + { + aConfig->mTransportProto = OT_DNS_TRANSPORT_TCP; + } + else if (aArgs[6] == "udp") + { + aConfig->mTransportProto = OT_DNS_TRANSPORT_UDP; + } + else + { + error = OT_ERROR_INVALID_ARGS; + } + exit: return error; } @@ -1860,12 +3402,12 @@ void Interpreter::HandleDnsAddressResponse(otError aError, const otDnsAddressRes while (otDnsAddressResponseGetAddress(aResponse, index, &address, &ttl) == OT_ERROR_NONE) { OutputIp6Address(address); - OutputFormat(" TTL:%u ", ttl); + OutputFormat(" TTL:%lu ", ToUlong(ttl)); index++; } } - OutputLine(""); + OutputNewLine(); OutputResult(aError); } @@ -1873,15 +3415,26 @@ void Interpreter::HandleDnsAddressResponse(otError aError, const otDnsAddressRes void Interpreter::OutputDnsServiceInfo(uint8_t aIndentSize, const otDnsServiceInfo &aServiceInfo) { - OutputLine(aIndentSize, "Port:%d, Priority:%d, Weight:%d, TTL:%u", aServiceInfo.mPort, aServiceInfo.mPriority, - aServiceInfo.mWeight, aServiceInfo.mTtl); + OutputLine(aIndentSize, "Port:%d, Priority:%d, Weight:%d, TTL:%lu", aServiceInfo.mPort, aServiceInfo.mPriority, + aServiceInfo.mWeight, ToUlong(aServiceInfo.mTtl)); OutputLine(aIndentSize, "Host:%s", aServiceInfo.mHostNameBuffer); OutputFormat(aIndentSize, "HostAddress:"); OutputIp6Address(aServiceInfo.mHostAddress); - OutputLine(" TTL:%u", aServiceInfo.mHostAddressTtl); + OutputLine(" TTL:%lu", ToUlong(aServiceInfo.mHostAddressTtl)); OutputFormat(aIndentSize, "TXT:"); - OutputDnsTxtData(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize); - OutputLine(" TTL:%u", aServiceInfo.mTxtDataTtl); + + if (!aServiceInfo.mTxtDataTruncated) + { + OutputDnsTxtData(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize); + } + else + { + OutputFormat("["); + OutputBytes(aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize); + OutputFormat("...]"); + } + + OutputLine(" TTL:%lu", ToUlong(aServiceInfo.mTxtDataTtl)); } void Interpreter::HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse, void *aContext) @@ -1893,7 +3446,7 @@ void Interpreter::HandleDnsBrowseResponse(otError aError, const otDnsBrowseRespo { char name[OT_DNS_MAX_NAME_SIZE]; char label[OT_DNS_MAX_LABEL_SIZE]; - uint8_t txtBuffer[255]; + uint8_t txtBuffer[kMaxTxtDataSize]; otDnsServiceInfo serviceInfo; IgnoreError(otDnsBrowseResponseGetServiceName(aResponse, name, sizeof(name))); @@ -1919,7 +3472,7 @@ void Interpreter::HandleDnsBrowseResponse(otError aError, const otDnsBrowseRespo OutputDnsServiceInfo(kIndentSize, serviceInfo); } - OutputLine(""); + OutputNewLine(); } } @@ -1935,7 +3488,7 @@ void Interpreter::HandleDnsServiceResponse(otError aError, const otDnsServiceRes { char name[OT_DNS_MAX_NAME_SIZE]; char label[OT_DNS_MAX_LABEL_SIZE]; - uint8_t txtBuffer[255]; + uint8_t txtBuffer[kMaxTxtDataSize]; otDnsServiceInfo serviceInfo; IgnoreError(otDnsServiceResponseGetServiceName(aResponse, label, sizeof(label), name, sizeof(name))); @@ -1951,8 +3504,8 @@ void Interpreter::HandleDnsServiceResponse(otError aError, const otDnsServiceRes if (otDnsServiceResponseGetServiceInfo(aResponse, &serviceInfo) == OT_ERROR_NONE) { - OutputDnsServiceInfo(/* aIndetSize */ 0, serviceInfo); - OutputLine(""); + OutputDnsServiceInfo(/* aIndentSize */ 0, serviceInfo); + OutputNewLine(); } } @@ -1986,7 +3539,7 @@ void Interpreter::OutputEidCacheEntry(const otCacheEntryInfo &aEntry) { if (aEntry.mValidLastTrans) { - OutputFormat(" transTime=%u eid=", aEntry.mLastTransTime); + OutputFormat(" transTime=%lu eid=", ToUlong(aEntry.mLastTransTime)); OutputIp6Address(aEntry.mMeshLocalEid); } } @@ -2000,9 +3553,21 @@ void Interpreter::OutputEidCacheEntry(const otCacheEntryInfo &aEntry) OutputFormat(" retryDelay=%u", aEntry.mRetryDelay); } - OutputLine(""); + OutputNewLine(); } +/** + * @cli eidcache + * @code + * eidcache + * fd49:caf4:a29f:dc0e:97fc:69dd:3c16:df7d 2000 cache canEvict=1 transTime=0 eid=fd49:caf4:a29f:dc0e:97fc:69dd:3c16:df7d + * fd49:caf4:a29f:dc0e:97fc:69dd:3c16:df7f fffe retry canEvict=1 timeout=10 retryDelay=30 + * Done + * @endcode + * @par + * Returns the EID-to-RLOC cache entries. + * @sa otThreadGetNextCacheEntry + */ template <> otError Interpreter::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); @@ -2012,7 +3577,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) memset(&iterator, 0, sizeof(iterator)); - for (uint8_t i = 0;; i++) + while (true) { SuccessOrExit(otThreadGetNextCacheEntry(GetInstancePtr(), &entry, &iterator)); OutputEidCacheEntry(entry); @@ -2053,10 +3618,31 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; + /** + * @cli extaddr + * @code + * extaddr + * dead00beef00cafe + * Done + * @endcode + * @par api_copy + * #otLinkGetExtendedAddress + */ if (aArgs[0].IsEmpty()) { OutputExtAddressLine(*otLinkGetExtendedAddress(GetInstancePtr())); } + /** + * @cli extaddr (set) + * @code + * extaddr dead00beef00cafe + * dead00beef00cafe + * Done + * @endcode + * @cparam extaddr @ca{extaddr} + * @par api_copy + * #otLinkSetExtendedAddress + */ else { otExtAddress extAddress; @@ -2095,7 +3681,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) #if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_DEBUG_UART) && OPENTHREAD_POSIX else if (aArgs[0] == "filename") { - VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); SuccessOrExit(error = otPlatDebugUart_logfile(aArgs[1].GetCString())); } #endif @@ -2112,10 +3698,33 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; + /** + * @cli extpanid + * @code + * extpanid + * dead00beef00cafe + * Done + * @endcode + * @par api_copy + * #otThreadGetExtendedPanId + */ if (aArgs[0].IsEmpty()) { OutputBytesLine(otThreadGetExtendedPanId(GetInstancePtr())->m8); } + /** + * @cli extpanid (set) + * @code + * extpanid dead00beef00cafe + * Done + * @endcode + * @cparam extpanid @ca{extpanid} + * @par + * @note The current commissioning credential becomes stale after changing this value. + * Use `pskc` to reset. + * @par api_copy + * #otThreadSetExtendedPanId + */ else { otExtendedPanId extPanId; @@ -2128,6 +3737,14 @@ template <> otError Interpreter::Process(Arg aArgs[]) return error; } +/** + * @cli factoryreset + * @code + * factoryreset + * @endcode + * @par api_copy + * #otInstanceFactoryReset + */ template <> otError Interpreter::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); @@ -2142,6 +3759,19 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_INVALID_COMMAND; + /** + * @cli fake (a,an) + * @code + * fake /a/an fdde:ad00:beef:0:0:ff:fe00:a800 fd00:7d03:7d03:7d03:55f2:bb6a:7a43:a03b 1111222233334444 + * Done + * @endcode + * @cparam fake /a/an @ca{dst-ipaddr} @ca{target} @ca{meshLocalIid} + * @par + * Sends fake Thread messages. + * @par + * Available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. + * @sa otThreadSendAddressNotification + */ if (aArgs[0] == "/a/an") { otIp6Address destination, target; @@ -2176,6 +3806,17 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; + /** + * @cli fem + * @code + * fem + * LNA gain 11 dBm + * Done + * @endcode + * @par + * Gets external FEM parameters. + * @sa otPlatRadioGetFemLnaGain + */ if (aArgs[0].IsEmpty()) { int8_t lnaGain; @@ -2183,6 +3824,16 @@ template <> otError Interpreter::Process(Arg aArgs[]) SuccessOrExit(error = otPlatRadioGetFemLnaGain(GetInstancePtr(), &lnaGain)); OutputLine("LNA gain %d dBm", lnaGain); } + /** + * @cli fem lnagain (get) + * @code + * fem lnagain + * 11 + * Done + * @endcode + * @par api_copy + * #otPlatRadioGetFemLnaGain + */ else if (aArgs[0] == "lnagain") { if (aArgs[1].IsEmpty()) @@ -2192,6 +3843,15 @@ template <> otError Interpreter::Process(Arg aArgs[]) SuccessOrExit(error = otPlatRadioGetFemLnaGain(GetInstancePtr(), &lnaGain)); OutputLine("%d", lnaGain); } + /** + * @cli fem lnagain (set) + * @code + * fem lnagain 8 + * Done + * @endcode + * @par api_copy + * #otPlatRadioSetFemLnaGain + */ else { int8_t lnaGain; @@ -2213,6 +3873,21 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; + /** + * @cli ifconfig + * @code + * ifconfig + * down + * Done + * @endcode + * @code + * ifconfig + * up + * Done + * @endcode + * @par api_copy + * #otIp6IsEnabled + */ if (aArgs[0].IsEmpty()) { if (otIp6IsEnabled(GetInstancePtr())) @@ -2224,6 +3899,20 @@ template <> otError Interpreter::Process(Arg aArgs[]) OutputLine("down"); } } + /** + * @cli ifconfig (up,down) + * @code + * ifconfig up + * Done + * @endcode + * @code + * ifconfig down + * Done + * @endcode + * @cparam ifconfig @ca{up|down} + * @par api_copy + * #otIp6SetEnabled + */ else if (aArgs[0] == "up") { SuccessOrExit(error = otIp6SetEnabled(GetInstancePtr(), true)); @@ -2269,6 +3958,27 @@ template <> otError Interpreter::Process(Arg aArgs[]) verbose = true; } + /** + * @cli ipaddr + * @code + * ipaddr + * fdde:ad00:beef:0:0:ff:fe00:0 + * fdde:ad00:beef:0:558:f56b:d688:799 + * fe80:0:0:0:f3d9:2a82:c8d8:fe43 + * Done + * @endcode + * @code + * ipaddr -v + * fdde:ad00:beef:0:0:ff:fe00:0 origin:thread + * fdde:ad00:beef:0:558:f56b:d688:799 origin:thread + * fe80:0:0:0:f3d9:2a82:c8d8:fe43 origin:thread + * Done + * @endcode + * @cparam ipaddr [@ca{-v}] + * Use `-v` to get verbose IP Address information. + * @par api_copy + * #otIp6GetUnicastAddresses + */ if (aArgs[0].IsEmpty()) { const otNetifAddress *unicastAddrs = otIp6GetUnicastAddresses(GetInstancePtr()); @@ -2282,9 +3992,19 @@ template <> otError Interpreter::Process(Arg aArgs[]) OutputFormat(" origin:%s", AddressOriginToString(addr->mAddressOrigin)); } - OutputLine(""); + OutputNewLine(); } } + /** + * @cli ipaddr add + * @code + * ipaddr add 2001::dead:beef:cafe + * Done + * @endcode + * @cparam ipaddr add @ca{aAddress} + * @par api_copy + * #otIp6AddUnicastAddress + */ else if (aArgs[0] == "add") { otNetifAddress address; @@ -2297,6 +4017,16 @@ template <> otError Interpreter::Process(Arg aArgs[]) error = otIp6AddUnicastAddress(GetInstancePtr(), &address); } + /** + * @cli ipaddr del + * @code + * ipaddr del 2001::dead:beef:cafe + * Done + * @endcode + * @cparam ipaddr del @ca{aAddress} + * @par api_copy + * #otIp6RemoveUnicastAddress + */ else if (aArgs[0] == "del") { otIp6Address address; @@ -2304,14 +4034,44 @@ template <> otError Interpreter::Process(Arg aArgs[]) SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address)); error = otIp6RemoveUnicastAddress(GetInstancePtr(), &address); } + /** + * @cli ipaddr linklocal + * @code + * ipaddr linklocal + * fe80:0:0:0:f3d9:2a82:c8d8:fe43 + * Done + * @endcode + * @par api_copy + * #otThreadGetLinkLocalIp6Address + */ else if (aArgs[0] == "linklocal") { OutputIp6AddressLine(*otThreadGetLinkLocalIp6Address(GetInstancePtr())); } + /** + * @cli ipaddr rloc + * @code + * ipaddr rloc + * fdde:ad00:beef:0:0:ff:fe00:0 + * Done + * @endcode + * @par api_copy + * #otThreadGetRloc + */ else if (aArgs[0] == "rloc") { OutputIp6AddressLine(*otThreadGetRloc(GetInstancePtr())); } + /** + * @cli ipaddr mleid + * @code + * ipaddr mleid + * fdde:ad00:beef:0:558:f56b:d688:799 + * Done + * @endcode + * @par api_copy + * #otThreadGetMeshLocalEid + */ else if (aArgs[0] == "mleid") { OutputIp6AddressLine(*otThreadGetMeshLocalEid(GetInstancePtr())); @@ -2329,6 +4089,18 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; + /** + * @cli ipmaddr + * @code + * ipmaddr + * ff05:0:0:0:0:0:0:1 + * ff33:40:fdde:ad00:beef:0:0:1 + * ff32:40:fdde:ad00:beef:0:0:1 + * Done + * @endcode + * @par api_copy + * #otIp6GetMulticastAddresses + */ if (aArgs[0].IsEmpty()) { for (const otNetifMulticastAddress *addr = otIp6GetMulticastAddresses(GetInstancePtr()); addr; @@ -2337,6 +4109,16 @@ template <> otError Interpreter::Process(Arg aArgs[]) OutputIp6AddressLine(addr->mAddress); } } + /** + * @cli ipmaddr add + * @code + * ipmaddr add ff05::1 + * Done + * @endcode + * @cparam ipmaddr add @ca{aAddress} + * @par api_copy + * #otIp6SubscribeMulticastAddress + */ else if (aArgs[0] == "add") { otIp6Address address; @@ -2354,6 +4136,16 @@ template <> otError Interpreter::Process(Arg aArgs[]) while (false); #endif } + /** + * @cli ipmaddr del + * @code + * ipmaddr del ff05::1 + * Done + * @endcode + * @cparam ipmaddr del @ca{aAddress} + * @par api_copy + * #otIp6UnsubscribeMulticastAddress + */ else if (aArgs[0] == "del") { otIp6Address address; @@ -2361,12 +4153,36 @@ template <> otError Interpreter::Process(Arg aArgs[]) SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address)); error = otIp6UnsubscribeMulticastAddress(GetInstancePtr(), &address); } + /** + * @cli ipmaddr promiscuous + * @code + * ipmaddr promiscuous + * Disabled + * Done + * @endcode + * @par api_copy + * #otIp6IsMulticastPromiscuousEnabled + */ else if (aArgs[0] == "promiscuous") { if (aArgs[1].IsEmpty()) { OutputEnabledDisabledStatus(otIp6IsMulticastPromiscuousEnabled(GetInstancePtr())); } + /** + * @cli ipmaddr promiscuous (enable,disable) + * @code + * ipmaddr promiscuous enable + * Done + * @endcode + * @code + * ipmaddr promiscuous disable + * Done + * @endcode + * @cparam ipmaddr promiscuous @ca{enable|disable} + * @par api_copy + * #otIp6SetMulticastPromiscuousEnabled + */ else { bool enable; @@ -2375,10 +4191,30 @@ template <> otError Interpreter::Process(Arg aArgs[]) otIp6SetMulticastPromiscuousEnabled(GetInstancePtr(), enable); } } + /** + * @cli ipmaddr llatn + * @code + * ipmaddr llatn + * ff32:40:fdde:ad00:beef:0:0:1 + * Done + * @endcode + * @par api_copy + * #otThreadGetLinkLocalAllThreadNodesMulticastAddress + */ else if (aArgs[0] == "llatn") { OutputIp6AddressLine(*otThreadGetLinkLocalAllThreadNodesMulticastAddress(GetInstancePtr())); } + /** + * @cli ipmaddr rlatn + * @code + * ipmaddr rlatn + * ff33:40:fdde:ad00:beef:0:0:1 + * Done + * @endcode + * @par api_copy + * #otThreadGetRealmLocalAllThreadNodesMulticastAddress + */ else if (aArgs[0] == "rlatn") { OutputIp6AddressLine(*otThreadGetRealmLocalAllThreadNodesMulticastAddress(GetInstancePtr())); @@ -2396,18 +4232,74 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_INVALID_ARGS; + /** + * @cli keysequence counter + * @code + * keysequence counter + * 10 + * Done + * @endcode + * @par api_copy + * #otThreadGetKeySequenceCounter + */ if (aArgs[0] == "counter") { + /** + * @cli keysequence counter (set) + * @code + * keysequence counter 10 + * Done + * @endcode + * @cparam keysequence counter @ca{counter} + * @par api_copy + * #otThreadSetKeySequenceCounter + */ error = ProcessGetSet(aArgs + 1, otThreadGetKeySequenceCounter, otThreadSetKeySequenceCounter); } + /** + * @cli keysequence guardtime + * @code + * keysequence guardtime + * 0 + * Done + * @endcode + * @par api_copy + * #otThreadGetKeySwitchGuardTime + */ else if (aArgs[0] == "guardtime") { + /** + * @cli keysequence guardtime (set) + * @code + * keysequence guardtime 0 + * Done + * @endcode + * @cparam keysequence guardtime @ca{guardtime-hours} + * Use `0` to `Thread Key Switch` immediately if there's a key index match. + * @par api_copy + * #otThreadSetKeySwitchGuardTime + */ error = ProcessGetSet(aArgs + 1, otThreadGetKeySwitchGuardTime, otThreadSetKeySwitchGuardTime); } return error; } +/** + * @cli leaderdata + * @code + * leaderdata + * Partition ID: 1077744240 + * Weighting: 64 + * Data Version: 109 + * Stable Data Version: 211 + * Leader Router ID: 60 + * Done + * @endcode + * @par + * Gets the Thread Leader Data. + * @sa otThreadGetLeaderData + */ template <> otError Interpreter::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); @@ -2417,11 +4309,11 @@ template <> otError Interpreter::Process(Arg aArgs[]) SuccessOrExit(error = otThreadGetLeaderData(GetInstancePtr(), &leaderData)); - OutputLine("Partition ID: %u", leaderData.mPartitionId); - OutputLine("Weighting: %d", leaderData.mWeighting); - OutputLine("Data Version: %d", leaderData.mDataVersion); - OutputLine("Stable Data Version: %d", leaderData.mStableDataVersion); - OutputLine("Leader Router ID: %d", leaderData.mLeaderRouterId); + OutputLine("Partition ID: %lu", ToUlong(leaderData.mPartitionId)); + OutputLine("Weighting: %u", leaderData.mWeighting); + OutputLine("Data Version: %u", leaderData.mDataVersion); + OutputLine("Stable Data Version: %u", leaderData.mStableDataVersion); + OutputLine("Leader Router ID: %u", leaderData.mLeaderRouterId); exit: return error; @@ -2432,54 +4324,201 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_INVALID_COMMAND; + /** + * @cli partitionid + * @code + * partitionid + * 4294967295 + * Done + * @endcode + * @par + * Get the Thread Network Partition ID. + * @sa otThreadGetPartitionId + */ if (aArgs[0].IsEmpty()) { - OutputLine("%u", otThreadGetPartitionId(GetInstancePtr())); + OutputLine("%lu", ToUlong(otThreadGetPartitionId(GetInstancePtr()))); error = OT_ERROR_NONE; } #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE - else if (aArgs[0] == "preferred") - { - error = ProcessGetSet(aArgs + 1, otThreadGetPreferredLeaderPartitionId, otThreadSetPreferredLeaderPartitionId); - } -#endif - - return error; -} + /** + * @cli partitionid preferred (get,set) + * @code + * partitionid preferred + * 4294967295 + * Done + * @endcode + * @code + * partitionid preferred 0xffffffff + * Done + * @endcode + * @cparam partitionid preferred @ca{partitionid} + * @sa otThreadGetPreferredLeaderPartitionId + * @sa otThreadSetPreferredLeaderPartitionId + * @par + * `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is required. + */ + else if (aArgs[0] == "preferred") + { + error = ProcessGetSet(aArgs + 1, otThreadGetPreferredLeaderPartitionId, otThreadSetPreferredLeaderPartitionId); + } +#endif + + return error; +} +/** + * @cli leaderweight + * @code + * leaderweight + * 128 + * Done + * @endcode + * @par api_copy + * #otThreadGetLocalLeaderWeight + */ template <> otError Interpreter::Process(Arg aArgs[]) { + /** + * @cli leaderweight (set) + * @code + * leaderweight 128 + * Done + * @endcode + * @cparam leaderweight @ca{weight} + * @par api_copy + * #otThreadSetLocalLeaderWeight + */ return ProcessGetSet(aArgs, otThreadGetLocalLeaderWeight, otThreadSetLocalLeaderWeight); } + +template <> otError Interpreter::Process(Arg aArgs[]) +{ + static const char *const kPowerSupplyStrings[4] = { + "battery", // (0) OT_POWER_SUPPLY_BATTERY + "external", // (1) OT_POWER_SUPPLY_EXTERNAL + "external-stable", // (2) OT_POWER_SUPPLY_EXTERNAL_STABLE + "external-unstable", // (3) OT_POWER_SUPPLY_EXTERNAL_UNSTABLE + }; + + static_assert(0 == OT_POWER_SUPPLY_BATTERY, "OT_POWER_SUPPLY_BATTERY value is incorrect"); + static_assert(1 == OT_POWER_SUPPLY_EXTERNAL, "OT_POWER_SUPPLY_EXTERNAL value is incorrect"); + static_assert(2 == OT_POWER_SUPPLY_EXTERNAL_STABLE, "OT_POWER_SUPPLY_EXTERNAL_STABLE value is incorrect"); + static_assert(3 == OT_POWER_SUPPLY_EXTERNAL_UNSTABLE, "OT_POWER_SUPPLY_EXTERNAL_UNSTABLE value is incorrect"); + + otError error = OT_ERROR_NONE; + + /** + * @cli deviceprops + * @code + * deviceprops + * PowerSupply : external + * IsBorderRouter : yes + * SupportsCcm : no + * IsUnstable : no + * WeightAdjustment : 0 + * Done + * @endcode + * @par api_copy + * #otThreadGetDeviceProperties + */ + if (aArgs[0].IsEmpty()) + { + const otDeviceProperties *props = otThreadGetDeviceProperties(GetInstancePtr()); + + OutputLine("PowerSupply : %s", Stringify(props->mPowerSupply, kPowerSupplyStrings)); + OutputLine("IsBorderRouter : %s", props->mIsBorderRouter ? "yes" : "no"); + OutputLine("SupportsCcm : %s", props->mSupportsCcm ? "yes" : "no"); + OutputLine("IsUnstable : %s", props->mIsUnstable ? "yes" : "no"); + OutputLine("WeightAdjustment : %d", props->mLeaderWeightAdjustment); + } + /** + * @cli deviceprops (set) + * @code + * deviceprops battery 0 0 0 -5 + * Done + * @endcode + * @code + * deviceprops + * PowerSupply : battery + * IsBorderRouter : no + * SupportsCcm : no + * IsUnstable : no + * WeightAdjustment : -5 + * Done + * @endcode + * @cparam deviceprops @ca{powerSupply} @ca{isBr} @ca{supportsCcm} @ca{isUnstable} @ca{weightAdjustment} + * `powerSupply`: should be 'battery', 'external', 'external-stable', 'external-unstable'. + * @par + * Sets the device properties. + * @csa{leaderweight} + * @csa{leaderweight (set)} + * @sa #otThreadSetDeviceProperties + */ + else + { + otDeviceProperties props; + bool value; + uint8_t index; + + for (index = 0; index < OT_ARRAY_LENGTH(kPowerSupplyStrings); index++) + { + if (aArgs[0] == kPowerSupplyStrings[index]) + { + props.mPowerSupply = static_cast(index); + break; + } + } + + VerifyOrExit(index < OT_ARRAY_LENGTH(kPowerSupplyStrings), error = OT_ERROR_INVALID_ARGS); + + SuccessOrExit(error = aArgs[1].ParseAsBool(value)); + props.mIsBorderRouter = value; + + SuccessOrExit(error = aArgs[2].ParseAsBool(value)); + props.mSupportsCcm = value; + + SuccessOrExit(error = aArgs[3].ParseAsBool(value)); + props.mIsUnstable = value; + + SuccessOrExit(error = aArgs[4].ParseAsInt8(props.mLeaderWeightAdjustment)); + + VerifyOrExit(aArgs[5].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + otThreadSetDeviceProperties(GetInstancePtr(), &props); + } + +exit: + return error; +} + #endif // OPENTHREAD_FTD #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE -void Interpreter::HandleLinkMetricsReport(const otIp6Address * aAddress, +void Interpreter::HandleLinkMetricsReport(const otIp6Address *aAddress, const otLinkMetricsValues *aMetricsValues, uint8_t aStatus, - void * aContext) + void *aContext) { static_cast(aContext)->HandleLinkMetricsReport(aAddress, aMetricsValues, aStatus); } void Interpreter::PrintLinkMetricsValue(const otLinkMetricsValues *aMetricsValues) { - const char kLinkMetricsTypeCount[] = "(Count/Summation)"; - const char kLinkMetricsTypeAverage[] = "(Exponential Moving Average)"; + static const char kLinkMetricsTypeAverage[] = "(Exponential Moving Average)"; if (aMetricsValues->mMetrics.mPduCount) { - OutputLine(" - PDU Counter: %d %s", aMetricsValues->mPduCountValue, kLinkMetricsTypeCount); + OutputLine(" - PDU Counter: %lu (Count/Summation)", ToUlong(aMetricsValues->mPduCountValue)); } if (aMetricsValues->mMetrics.mLqi) { - OutputLine(" - LQI: %d %s", aMetricsValues->mLqiValue, kLinkMetricsTypeAverage); + OutputLine(" - LQI: %u %s", aMetricsValues->mLqiValue, kLinkMetricsTypeAverage); } if (aMetricsValues->mMetrics.mLinkMargin) { - OutputLine(" - Margin: %d (dB) %s", aMetricsValues->mLinkMarginValue, kLinkMetricsTypeAverage); + OutputLine(" - Margin: %u (dB) %s", aMetricsValues->mLinkMarginValue, kLinkMetricsTypeAverage); } if (aMetricsValues->mMetrics.mRssi) @@ -2488,7 +4527,7 @@ void Interpreter::PrintLinkMetricsValue(const otLinkMetricsValues *aMetricsValue } } -void Interpreter::HandleLinkMetricsReport(const otIp6Address * aAddress, +void Interpreter::HandleLinkMetricsReport(const otIp6Address *aAddress, const otLinkMetricsValues *aMetricsValues, uint8_t aStatus) { @@ -2503,6 +4542,12 @@ void Interpreter::HandleLinkMetricsReport(const otIp6Address * aAddress, { OutputLine("Link Metrics Report, status: %s", LinkMetricsStatusToStr(aStatus)); } + + if (mLinkMetricsQueryInProgress) + { + mLinkMetricsQueryInProgress = false; + OutputResult(OT_ERROR_NONE); + } } void Interpreter::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, uint8_t aStatus, void *aContext) @@ -2519,15 +4564,15 @@ void Interpreter::HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, ui } void Interpreter::HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress, - const otExtAddress * aExtAddress, + const otExtAddress *aExtAddress, const otLinkMetricsValues *aMetricsValues, - void * aContext) + void *aContext) { static_cast(aContext)->HandleLinkMetricsEnhAckProbingIe(aShortAddress, aExtAddress, aMetricsValues); } void Interpreter::HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress, - const otExtAddress * aExtAddress, + const otExtAddress *aExtAddress, const otLinkMetricsValues *aMetricsValues) { OutputFormat("Received Link Metrics data in Enh Ack from neighbor, short address:0x%02x , extended address:", @@ -2577,34 +4622,95 @@ template <> otError Interpreter::Process(Arg aArgs[]) if (aArgs[0] == "query") { otIp6Address address; + bool isSingle; + bool blocking; + uint8_t seriesId; otLinkMetrics linkMetrics; SuccessOrExit(error = aArgs[1].ParseAsIp6Address(address)); + /** + * @cli linkmetrics query single + * @code + * linkmetrics query fe80:0:0:0:3092:f334:1455:1ad2 single qmr + * Done + * > Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2 + * - LQI: 76 (Exponential Moving Average) + * - Margin: 82 (dB) (Exponential Moving Average) + * - RSSI: -18 (dBm) (Exponential Moving Average) + * @endcode + * @cparam linkmetrics query @ca{peer-ipaddr} single [@ca{pqmr}] + * - `peer-ipaddr`: Peer address. + * - [`p`, `q`, `m`, and `r`] map to #otLinkMetrics. + * - `p`: Layer 2 Number of PDUs received. + * - `q`: Layer 2 LQI. + * - `m`: Link Margin. + * - `r`: RSSI. + * @par + * Perform a Link Metrics query (Single Probe). + * @sa otLinkMetricsQuery + */ if (aArgs[2] == "single") { - VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + isSingle = true; SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3])); - error = otLinkMetricsQuery(GetInstancePtr(), &address, /* aSeriesId */ 0, &linkMetrics, - &Interpreter::HandleLinkMetricsReport, this); } + /** + * @cli linkmetrics query forward + * @code + * linkmetrics query fe80:0:0:0:3092:f334:1455:1ad2 forward 1 + * Done + * > Received Link Metrics Report from: fe80:0:0:0:3092:f334:1455:1ad2 + * - PDU Counter: 2 (Count/Summation) + * - LQI: 76 (Exponential Moving Average) + * - Margin: 82 (dB) (Exponential Moving Average) + * - RSSI: -18 (dBm) (Exponential Moving Average) + * @endcode + * @cparam linkmetrics query @ca{peer-ipaddr} forward @ca{series-id} + * - `peer-ipaddr`: Peer address. + * - `series-id`: The Series ID. + * @par + * Perform a Link Metrics query (Forward Tracking Series). + * @sa otLinkMetricsQuery + */ else if (aArgs[2] == "forward") { - uint8_t seriesId; - + isSingle = false; SuccessOrExit(error = aArgs[3].ParseAsUint8(seriesId)); - error = otLinkMetricsQuery(GetInstancePtr(), &address, seriesId, nullptr, - &Interpreter::HandleLinkMetricsReport, this); } else { - error = OT_ERROR_INVALID_ARGS; + ExitNow(error = OT_ERROR_INVALID_ARGS); + } + + blocking = (aArgs[4] == "block"); + + SuccessOrExit(error = otLinkMetricsQuery(GetInstancePtr(), &address, isSingle ? 0 : seriesId, + isSingle ? &linkMetrics : nullptr, HandleLinkMetricsReport, this)); + + if (blocking) + { + mLinkMetricsQueryInProgress = true; + error = OT_ERROR_PENDING; } } else if (aArgs[0] == "mgmt") { error = ProcessLinkMetricsMgmt(aArgs + 1); } + /** + * @cli linkmetrics probe + * @code + * linkmetrics probe fe80:0:0:0:3092:f334:1455:1ad2 1 10 + * Done + * @endcode + * @cparam linkmetrics probe @ca{peer-ipaddr} @ca{series-id} @ca{length} + * - `peer-ipaddr`: Peer address. + * - `series-id`: The Series ID for which this Probe message targets. + * - `length`: The length of the Probe message. A valid range is [0, 64]. + * @par api_copy + * #otLinkMetricsSendLinkProbe + */ else if (aArgs[0] == "probe") { otIp6Address address; @@ -2626,6 +4732,8 @@ otError Interpreter::ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, const Ar { otError error = OT_ERROR_NONE; + VerifyOrExit(!aFlags.IsEmpty(), error = OT_ERROR_INVALID_ARGS); + memset(&aLinkMetrics, 0, sizeof(aLinkMetrics)); for (const char *arg = aFlags.GetCString(); *arg != '\0'; arg++) @@ -2668,6 +4776,32 @@ otError Interpreter::ProcessLinkMetricsMgmt(Arg aArgs[]) memset(&seriesFlags, 0, sizeof(otLinkMetricsSeriesFlags)); + /** + * @cli linkmetrics mgmt forward + * @code + * linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 forward 1 dra pqmr + * Done + * > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 + * Status: SUCCESS + * @endcode + * @cparam linkmetrics mgmt @ca{peer-ipaddr} forward @ca{series-id} [@ca{ldraX}][@ca{pqmr}] + * - `peer-ipaddr`: Peer address. + * - `series-id`: The Series ID. + * - [`l`, `d`, `r`, and `a`] map to #otLinkMetricsSeriesFlags. `X` represents none of the + * `otLinkMetricsSeriesFlags`, and stops the accounting and removes the series. + * - `l`: MLE Link Probe. + * - `d`: MAC Data. + * - `r`: MAC Data Request. + * - `a`: MAC Ack. + * - `X`: Can only be used without any other flags. + * - [`p`, `q`, `m`, and `r`] map to #otLinkMetricsValues. + * - `p`: Layer 2 Number of PDUs received. + * - `q`: Layer 2 LQI. + * - `m`: Link Margin. + * - `r`: RSSI. + * @par api_copy + * #otLinkMetricsConfigForwardTrackingSeries + */ if (aArgs[1] == "forward") { uint8_t seriesId; @@ -2710,8 +4844,8 @@ otError Interpreter::ProcessLinkMetricsMgmt(Arg aArgs[]) if (!clear) { - VerifyOrExit(!aArgs[4].IsEmpty() && aArgs[5].IsEmpty(), error = OT_ERROR_INVALID_ARGS); SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[4])); + VerifyOrExit(aArgs[5].IsEmpty(), error = OT_ERROR_INVALID_ARGS); } error = otLinkMetricsConfigForwardTrackingSeries(GetInstancePtr(), &address, seriesId, seriesFlags, @@ -2722,17 +4856,59 @@ otError Interpreter::ProcessLinkMetricsMgmt(Arg aArgs[]) { otLinkMetricsEnhAckFlags enhAckFlags; otLinkMetrics linkMetrics; - otLinkMetrics * pLinkMetrics = &linkMetrics; - + otLinkMetrics *pLinkMetrics = &linkMetrics; + + /** + * @cli linkmetrics mgmt enhanced-ack clear + * @code + * linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack clear + * Done + * > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 + * Status: Success + * @endcode + * @cparam linkmetrics mgmt @ca{peer-ipaddr} enhanced-ack clear + * `peer-ipaddr` should be the Link Local address of the neighboring device. + * @par + * Sends a Link Metrics Management Request to clear an Enhanced-ACK Based Probing. + * @sa otLinkMetricsConfigEnhAckProbing + */ if (aArgs[2] == "clear") { enhAckFlags = OT_LINK_METRICS_ENH_ACK_CLEAR; pLinkMetrics = nullptr; } + /** + * @cli linkmetrics mgmt enhanced-ack register + * @code + * linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm + * Done + * > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 + * Status: Success + * @endcode + * @code + * > linkmetrics mgmt fe80:0:0:0:3092:f334:1455:1ad2 enhanced-ack register qm r + * Done + * > Received Link Metrics Management Response from: fe80:0:0:0:3092:f334:1455:1ad2 + * Status: Cannot support new series + * @endcode + * @cparam linkmetrics mgmt @ca{peer-ipaddr} enhanced-ack register [@ca{qmr}][@ca{r}] + * [`q`, `m`, and `r`] map to #otLinkMetricsValues. Per spec 4.11.3.4.4.6, you can + * only use a maximum of two options at once, for example `q`, or `qm`. + * - `q`: Layer 2 LQI. + * - `m`: Link Margin. + * - `r`: RSSI. + * . + * The additional `r` is optional and only used for reference devices. When this option + * is specified, Type/Average Enum of each Type Id Flags is set to reserved. This is + * used to verify that the Probing Subject correctly handles invalid Type Id Flags, and + * only available when `OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE` is enabled. + * @par + * Sends a Link Metrics Management Request to register an Enhanced-ACK Based Probing. + * @sa otLinkMetricsConfigEnhAckProbing + */ else if (aArgs[2] == "register") { enhAckFlags = OT_LINK_METRICS_ENH_ACK_REGISTER; - VerifyOrExit(!aArgs[3].IsEmpty(), error = OT_ERROR_INVALID_ARGS); SuccessOrExit(error = ParseLinkMetricsFlags(linkMetrics, aArgs[3])); #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE if (aArgs[4] == "r") @@ -2785,7 +4961,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) return error; } -void Interpreter::HandleLocateResult(void * aContext, +void Interpreter::HandleLocateResult(void *aContext, otError aError, const otIp6Address *aMeshLocalAddress, uint16_t aRloc16) @@ -2856,7 +5032,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) if (aArgs[0].IsEmpty()) { - OutputLine("0x%04x", otThreadGetPskcRef(GetInstancePtr())); + OutputLine("0x%08lx", ToUlong(otThreadGetPskcRef(GetInstancePtr()))); } else { @@ -2941,7 +5117,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) return error; } -void Interpreter::HandleMlrRegResult(void * aContext, +void Interpreter::HandleMlrRegResult(void *aContext, otError aError, uint8_t aMlrStatus, const otIp6Address *aFailedAddresses, @@ -3089,13 +5265,13 @@ void Interpreter::OutputMultiRadioInfo(const otMultiRadioNeighborInfo &aMultiRad if (aMultiRadioInfo.mSupportsIeee802154) { - OutputFormat("15.4(%d)", aMultiRadioInfo.mIeee802154Info.mPreference); + OutputFormat("15.4(%u)", aMultiRadioInfo.mIeee802154Info.mPreference); isFirst = false; } if (aMultiRadioInfo.mSupportsTrelUdp6) { - OutputFormat("%sTREL(%d)", isFirst ? "" : ", ", aMultiRadioInfo.mTrelUdp6Info.mPreference); + OutputFormat("%sTREL(%u)", isFirst ? "" : ", ", aMultiRadioInfo.mTrelUdp6Info.mPreference); } OutputLine("]"); @@ -3117,11 +5293,11 @@ template <> otError Interpreter::Process(Arg aArgs[]) if (isTable) { static const char *const kNeighborTableTitles[] = { - "Role", "RLOC16", "Age", "Avg RSSI", "Last RSSI", "R", "D", "N", "Extended MAC", + "Role", "RLOC16", "Age", "Avg RSSI", "Last RSSI", "R", "D", "N", "Extended MAC", "Version", }; static const uint8_t kNeighborTableColumnWidths[] = { - 6, 8, 5, 10, 11, 1, 1, 1, 18, + 6, 8, 5, 10, 11, 1, 1, 1, 18, 9, }; OutputTableHeader(kNeighborTableTitles, kNeighborTableColumnWidths); @@ -3133,7 +5309,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) { OutputFormat("| %3c ", neighborInfo.mIsChild ? 'C' : 'R'); OutputFormat("| 0x%04x ", neighborInfo.mRloc16); - OutputFormat("| %3d ", neighborInfo.mAge); + OutputFormat("| %3lu ", ToUlong(neighborInfo.mAge)); OutputFormat("| %8d ", neighborInfo.mAverageRssi); OutputFormat("| %9d ", neighborInfo.mLastRssi); OutputFormat("|%1d", neighborInfo.mRxOnWhenIdle); @@ -3141,7 +5317,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) OutputFormat("|%1d", neighborInfo.mFullNetworkData); OutputFormat("| "); OutputExtAddress(neighborInfo.mExtAddress); - OutputLine(" |"); + OutputLine(" | %7d |", neighborInfo.mVersion); } else { @@ -3149,8 +5325,111 @@ template <> otError Interpreter::Process(Arg aArgs[]) } } - OutputLine(""); + OutputNewLine(); } + else if (aArgs[0] == "linkquality") + { + static const char *const kLinkQualityTableTitles[] = { + "RLOC16", "Extended MAC", "Frame Error", "Msg Error", "Avg RSS", "Last RSS", "Age", + }; + + static const uint8_t kLinkQualityTableColumnWidths[] = { + 8, 18, 13, 11, 9, 10, 7, + }; + + OutputTableHeader(kLinkQualityTableTitles, kLinkQualityTableColumnWidths); + + while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighborInfo) == OT_ERROR_NONE) + { + PercentageStringBuffer stringBuffer; + + OutputFormat("| 0x%04x | ", neighborInfo.mRloc16); + OutputExtAddress(neighborInfo.mExtAddress); + OutputFormat(" | %9s %% ", PercentageToString(neighborInfo.mFrameErrorRate, stringBuffer)); + OutputFormat("| %7s %% ", PercentageToString(neighborInfo.mMessageErrorRate, stringBuffer)); + OutputFormat("| %7d ", neighborInfo.mAverageRssi); + OutputFormat("| %8d ", neighborInfo.mLastRssi); + OutputLine("| %5lu |", ToUlong(neighborInfo.mAge)); + } + } +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + /** + * @cli neighbor conntime + * @code + * neighbor conntime + * | RLOC16 | Extended MAC | Last Heard (Age) | Connection Time | + * +--------+------------------+------------------+------------------+ + * | 0x8401 | 1a28be396a14a318 | 00:00:13 | 00:07:59 | + * | 0x5c00 | 723ebf0d9eba3264 | 00:00:03 | 00:11:27 | + * | 0xe800 | ce53628a1e3f5b3c | 00:00:02 | 00:00:15 | + * Done + * @endcode + * @par + * Print the connection time and age of neighbors. Info per neighbor: + * - RLOC16 + * - Extended MAC address + * - Last Heard (seconds since last heard from neighbor) + * - Connection time (seconds since link establishment with neighbor) + * Duration intervals are formatted as `{hh}:{mm}:{ss}` for hours, minutes, and seconds if the duration is less + * than one day. If the duration is longer than one day, the format is `{dd}d.{hh}:{mm}:{ss}`. + */ + else if (aArgs[0] == "conntime") + { + /** + * @cli neighbor conntime list + * @code + * neighbor conntime list + * 0x8401 1a28be396a14a318 age:63 conn-time:644 + * 0x5c00 723ebf0d9eba3264 age:23 conn-time:852 + * 0xe800 ce53628a1e3f5b3c age:23 conn-time:180 + * Done + * @endcode + * @par + * Print connection time and age of neighbors. + * This command is similar to `neighbor conntime`, but it displays the information in a list format. The age + * and connection time are both displayed in seconds. + */ + if (aArgs[1] == "list") + { + isTable = false; + } + else + { + static const char *const kConnTimeTableTitles[] = { + "RLOC16", + "Extended MAC", + "Last Heard (Age)", + "Connection Time", + }; + + static const uint8_t kConnTimeTableColumnWidths[] = {8, 18, 18, 18}; + + isTable = true; + OutputTableHeader(kConnTimeTableTitles, kConnTimeTableColumnWidths); + } + + while (otThreadGetNextNeighborInfo(GetInstancePtr(), &iterator, &neighborInfo) == OT_ERROR_NONE) + { + if (isTable) + { + char string[OT_DURATION_STRING_SIZE]; + + OutputFormat("| 0x%04x | ", neighborInfo.mRloc16); + OutputExtAddress(neighborInfo.mExtAddress); + otConvertDurationInSecondsToString(neighborInfo.mAge, string, sizeof(string)); + OutputFormat(" | %16s", string); + otConvertDurationInSecondsToString(neighborInfo.mConnectionTime, string, sizeof(string)); + OutputLine(" | %16s |", string); + } + else + { + OutputFormat("0x%04x ", neighborInfo.mRloc16); + OutputExtAddress(neighborInfo.mExtAddress); + OutputLine(" age:%lu conn-time:%lu", ToUlong(neighborInfo.mAge), ToUlong(neighborInfo.mConnectionTime)); + } + } + } +#endif else { error = OT_ERROR_INVALID_ARGS; @@ -3234,10 +5513,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) } #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE -template <> otError Interpreter::Process(Arg aArgs[]) -{ - return mNetworkData.Process(aArgs); -} +template <> otError Interpreter::Process(Arg aArgs[]) { return mNetworkData.Process(aArgs); } #if OPENTHREAD_FTD template <> otError Interpreter::Process(Arg aArgs[]) @@ -3250,6 +5526,16 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; + /** + * @cli networkkey + * @code + * networkkey + * 00112233445566778899aabbccddeeff + * Done + * @endcode + * @par api_copy + * #otThreadGetNetworkKey + */ if (aArgs[0].IsEmpty()) { otNetworkKey networkKey; @@ -3257,6 +5543,16 @@ template <> otError Interpreter::Process(Arg aArgs[]) otThreadGetNetworkKey(GetInstancePtr(), &networkKey); OutputBytesLine(networkKey.m8); } + /** + * @cli networkkey (key) + * @code + * networkkey 00112233445566778899aabbccddeeff + * Done + * @endcode + * @par api_copy + * #otThreadSetNetworkKey + * @cparam networkkey @ca{key} + */ else { otNetworkKey key; @@ -3276,7 +5572,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) if (aArgs[0].IsEmpty()) { - OutputLine("0x%04x", otThreadGetNetworkKeyRef(GetInstancePtr())); + OutputLine("0x%08lx", ToUlong(otThreadGetNetworkKeyRef(GetInstancePtr()))); } else { @@ -3291,21 +5587,31 @@ template <> otError Interpreter::Process(Arg aArgs[]) } #endif +/** + * @cli networkname + * @code + * networkname + * OpenThread + * Done + * @endcode + * @par api_copy + * #otThreadGetNetworkName + */ template <> otError Interpreter::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; - - if (aArgs[0].IsEmpty()) - { - OutputLine("%s", otThreadGetNetworkName(GetInstancePtr())); - } - else - { - SuccessOrExit(error = otThreadSetNetworkName(GetInstancePtr(), aArgs[0].GetCString())); - } - -exit: - return error; + /** + * @cli networkname (name) + * @code + * networkname OpenThread + * Done + * @endcode + * @par api_copy + * #otThreadSetNetworkName + * @cparam networkname @ca{name} + * @par + * Note: The current commissioning credential becomes stale after changing this value. Use `pskc` to reset. + */ + return ProcessGetSet(aArgs, otThreadGetNetworkName, otThreadSetNetworkName); } #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE @@ -3313,6 +5619,21 @@ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; + /** + * @cli networktime + * @code + * networktime + * Network Time: 21084154us (synchronized) + * Time Sync Period: 100s + * XTAL Threshold: 300ppm + * Done + * @endcode + * @par + * Gets the Thread network time and the time sync parameters. + * @sa otNetworkTimeGet + * @sa otNetworkTimeGetSyncPeriod + * @sa otNetworkTimeGetXtalThreshold + */ if (aArgs[0].IsEmpty()) { uint64_t time; @@ -3320,7 +5641,9 @@ template <> otError Interpreter::Process(Arg aArgs[]) networkTimeStatus = otNetworkTimeGet(GetInstancePtr(), &time); - OutputFormat("Network Time: %luus", time); + OutputFormat("Network Time: "); + OutputUint64(time); + OutputFormat("us"); switch (networkTimeStatus) { @@ -3340,9 +5663,23 @@ template <> otError Interpreter::Process(Arg aArgs[]) break; } - OutputLine("Time Sync Period: %ds", otNetworkTimeGetSyncPeriod(GetInstancePtr())); - OutputLine("XTAL Threshold: %dppm", otNetworkTimeGetXtalThreshold(GetInstancePtr())); + OutputLine("Time Sync Period: %us", otNetworkTimeGetSyncPeriod(GetInstancePtr())); + OutputLine("XTAL Threshold: %uppm", otNetworkTimeGetXtalThreshold(GetInstancePtr())); } + /** + * @cli networktime (set) + * @code + * networktime 100 300 + * Done + * @endcode + * @cparam networktime @ca{timesyncperiod} @ca{xtalthreshold} + * @par + * Sets the time sync parameters. + * * `timesyncperiod`: The time synchronization period, in seconds. + * * `xtalthreshold`: The XTAL accuracy threshold for a device to become Router-Capable device, in PPM. + * @sa otNetworkTimeSetSyncPeriod + * @sa otNetworkTimeSetXtalThreshold + */ else { uint16_t period; @@ -3359,14 +5696,347 @@ template <> otError Interpreter::Process(Arg aArgs[]) } #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE -template <> otError Interpreter::Process(Arg aArgs[]) +#if OPENTHREAD_FTD +template <> otError Interpreter::Process(Arg aArgs[]) +{ + constexpr uint8_t kRouterIdOffset = 10; // Bit offset of Router ID in RLOC16 + constexpr uint16_t kInvalidRloc16 = 0xfffe; + + otError error = OT_ERROR_NONE; + uint16_t destRloc16; + uint16_t nextHopRloc16; + uint8_t pathCost; + + /** + * @cli nexthop + * @code + * nexthop + * | ID |NxtHop| Cost | + * +------+------+------+ + * | 9 | 9 | 1 | + * | 25 | 25 | 0 | + * | 30 | 30 | 1 | + * | 46 | - | - | + * | 50 | 30 | 3 | + * | 60 | 30 | 2 | + * Done + * @endcode + * @par + * Output table of allocated Router IDs and current next hop and path + * cost for each router. + * @sa otThreadGetNextHopAndPathCost + * @sa otThreadIsRouterIdAllocated + */ + if (aArgs[0].IsEmpty()) + { + static const char *const kNextHopTableTitles[] = { + "ID", + "NxtHop", + "Cost", + }; + + static const uint8_t kNextHopTableColumnWidths[] = { + 6, + 6, + 6, + }; + + OutputTableHeader(kNextHopTableTitles, kNextHopTableColumnWidths); + + for (uint8_t routerId = 0; routerId <= OT_NETWORK_MAX_ROUTER_ID; routerId++) + { + if (!otThreadIsRouterIdAllocated(GetInstancePtr(), routerId)) + { + continue; + } + + destRloc16 = routerId; + destRloc16 <<= kRouterIdOffset; + + otThreadGetNextHopAndPathCost(GetInstancePtr(), destRloc16, &nextHopRloc16, &pathCost); + + OutputFormat("| %4u | ", routerId); + + if (nextHopRloc16 != kInvalidRloc16) + { + OutputLine("%4u | %4u |", nextHopRloc16 >> kRouterIdOffset, pathCost); + } + else + { + OutputLine("%4s | %4s |", "-", "-"); + } + } + } + /** + * @cli nexthop (get) + * @code + * nexthop 0xc000 + * 0xc000 cost:0 + * Done + * @endcode + * @code + * nexthop 0x8001 + * 0x2000 cost:3 + * Done + * @endcode + * @cparam nexthop @ca{rloc16} + * @par api_copy + * #otThreadGetNextHopAndPathCost + */ + else + { + SuccessOrExit(error = aArgs[0].ParseAsUint16(destRloc16)); + otThreadGetNextHopAndPathCost(GetInstancePtr(), destRloc16, &nextHopRloc16, &pathCost); + OutputLine("0x%04x cost:%u", nextHopRloc16, pathCost); + } + +exit: + return error; +} + +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE + +template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; + /** + * @cli meshdiag topology + * @code + * meshdiag topology + * id:02 rloc16:0x0800 ext-addr:8aa57d2c603fe16c ver:4 - me - leader + * 3-links:{ 46 } + * id:46 rloc16:0xb800 ext-addr:fe109d277e0175cc ver:4 + * 3-links:{ 02 51 57 } + * id:33 rloc16:0x8400 ext-addr:d2e511a146b9e54d ver:4 + * 3-links:{ 51 57 } + * id:51 rloc16:0xcc00 ext-addr:9aab43ababf05352 ver:4 + * 3-links:{ 33 57 } + * 2-links:{ 46 } + * id:57 rloc16:0xe400 ext-addr:dae9c4c0e9da55ff ver:4 + * 3-links:{ 46 51 } + * 1-links:{ 33 } + * Done + * @endcode + * @par + * Discover network topology (list of routers and their connections). + * Parameters are optional and indicate additional items to discover. Can be added in any order. + * * `ip6-addrs` to discover the list of IPv6 addresses of every router. + * * `children` to discover the child table of every router. + * @par + * Information per router: + * * Router ID + * * RLOC16 + * * Extended MAC address + * * Thread Version (if known) + * * Whether the router is this device is itself (`me`) + * * Whether the router is the parent of this device when device is a child (`parent`) + * * Whether the router is `leader` + * * Whether the router acts as a border router providing external connectivity (`br`) + * * List of routers to which this router has a link: + * * `3-links`: Router IDs to which this router has a incoming link with link quality 3 + * * `2-links`: Router IDs to which this router has a incoming link with link quality 2 + * * `1-links`: Router IDs to which this router has a incoming link with link quality 1 + * * If a list if empty, it is omitted in the out. + * * If `ip6-addrs`, list of IPv6 addresses of the router + * * If `children`, list of all children of the router. Information per child: + * * RLOC16 + * * Incoming Link Quality from perspective of parent to child (zero indicates unknown) + * * Child Device mode (`r` rx-on-when-idle, `d` Full Thread Device, `n` Full Network Data, `-` no flags set) + * * Whether the child is this device itself (`me`) + * * Whether the child acts as a border router providing external connectivity (`br`) + * @cparam meshdiag topology [@ca{ip6-addrs}] [@ca{children}] + * @sa otMeshDiagDiscoverTopology + */ + if (aArgs[0] == "topology") + { + otMeshDiagDiscoverConfig config; + + config.mDiscoverIp6Addresses = false; + config.mDiscoverChildTable = false; + + aArgs++; + + for (; !aArgs->IsEmpty(); aArgs++) + { + if (*aArgs == "ip6-addrs") + { + config.mDiscoverIp6Addresses = true; + } + else if (*aArgs == "children") + { + config.mDiscoverChildTable = true; + } + else + { + ExitNow(error = OT_ERROR_INVALID_ARGS); + } + } + + SuccessOrExit(error = otMeshDiagDiscoverTopology(GetInstancePtr(), &config, HandleMeshDiagDiscoverDone, this)); + error = OT_ERROR_PENDING; + } + else + { + error = OT_ERROR_INVALID_COMMAND; + } + +exit: + return error; +} + +void Interpreter::HandleMeshDiagDiscoverDone(otError aError, otMeshDiagRouterInfo *aRouterInfo, void *aContext) +{ + reinterpret_cast(aContext)->HandleMeshDiagDiscoverDone(aError, aRouterInfo); +} + +void Interpreter::HandleMeshDiagDiscoverDone(otError aError, otMeshDiagRouterInfo *aRouterInfo) +{ + VerifyOrExit(aRouterInfo != nullptr); + + OutputFormat("id:%02u rloc16:0x%04x ext-addr:", aRouterInfo->mRouterId, aRouterInfo->mRloc16); + OutputExtAddress(aRouterInfo->mExtAddress); + + if (aRouterInfo->mVersion != OT_MESH_DIAG_VERSION_UNKNOWN) + { + OutputFormat(" ver:%u", aRouterInfo->mVersion); + } + + if (aRouterInfo->mIsThisDevice) + { + OutputFormat(" - me"); + } + + if (aRouterInfo->mIsThisDeviceParent) + { + OutputFormat(" - parent"); + } + + if (aRouterInfo->mIsLeader) + { + OutputFormat(" - leader"); + } + + if (aRouterInfo->mIsBorderRouter) + { + OutputFormat(" - br"); + } + + OutputNewLine(); + + for (uint8_t linkQuality = 3; linkQuality > 0; linkQuality--) + { + bool hasLinkQuality = false; + + for (uint8_t entryQuality : aRouterInfo->mLinkQualities) + { + if (entryQuality == linkQuality) + { + hasLinkQuality = true; + break; + } + } + + if (hasLinkQuality) + { + OutputFormat(kIndentSize, "%u-links:{ ", linkQuality); + + for (uint8_t id = 0; id < OT_ARRAY_LENGTH(aRouterInfo->mLinkQualities); id++) + { + if (aRouterInfo->mLinkQualities[id] == linkQuality) + { + OutputFormat("%02u ", id); + } + } + + OutputLine("}"); + } + } + + if (aRouterInfo->mIp6AddrIterator != nullptr) + { + otIp6Address ip6Address; + + OutputLine(kIndentSize, "ip6-addrs:"); + + while (otMeshDiagGetNextIp6Address(aRouterInfo->mIp6AddrIterator, &ip6Address) == OT_ERROR_NONE) + { + OutputSpaces(kIndentSize * 2); + OutputIp6AddressLine(ip6Address); + } + } + + if (aRouterInfo->mChildIterator != nullptr) + { + otMeshDiagChildInfo childInfo; + char linkModeString[kLinkModeStringSize]; + bool isFirst = true; + + while (otMeshDiagGetNextChildInfo(aRouterInfo->mChildIterator, &childInfo) == OT_ERROR_NONE) + { + if (isFirst) + { + OutputLine(kIndentSize, "children:"); + isFirst = false; + } + + OutputFormat(kIndentSize * 2, "rloc16:0x%04x lq:%u, mode:%s", childInfo.mRloc16, childInfo.mLinkQuality, + LinkModeToString(childInfo.mMode, linkModeString)); + + if (childInfo.mIsThisDevice) + { + OutputFormat(" - me"); + } + + if (childInfo.mIsBorderRouter) + { + OutputFormat(" - br"); + } + + OutputNewLine(); + } + + if (isFirst) + { + OutputLine(kIndentSize, "children: none"); + } + } + +exit: + OutputResult(aError); +} + +#endif // OPENTHREAD_CONFIG_MESH_DIAG_ENABLE + +#endif // OPENTHREAD_FTD + +template <> otError Interpreter::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + /** + * @cli panid + * @code + * panid + * 0xdead + * Done + * @endcode + * @par api_copy + * #otLinkGetPanId + */ if (aArgs[0].IsEmpty()) { OutputLine("0x%04x", otLinkGetPanId(GetInstancePtr())); } + /** + * @cli panid (panid) + * @code + * panid 0xdead + * Done + * @endcode + * @par api_copy + * #otLinkSetPanId + * @cparam panid @ca{panid} + */ else { error = ProcessSet(aArgs, otLinkSetPanId); @@ -3377,24 +6047,88 @@ template <> otError Interpreter::Process(Arg aArgs[]) template <> otError Interpreter::Process(Arg aArgs[]) { - OT_UNUSED_VARIABLE(aArgs); - - otError error = OT_ERROR_NONE; - otRouterInfo parentInfo; + otError error = OT_ERROR_NONE; + /** + * @cli parent + * @code + * parent + * Ext Addr: be1857c6c21dce55 + * Rloc: 5c00 + * Link Quality In: 3 + * Link Quality Out: 3 + * Age: 20 + * Version: 4 + * Done + * @endcode + * @sa otThreadGetParentInfo + * @par + * Get the diagnostic information for a Thread Router as parent. + * @par + * When operating as a Thread Router when OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE is enabled, this command + * will return the cached information from when the device was previously attached as a Thread Child. Returning + * cached information is necessary to support the Thread Test Harness - Test Scenario 8.2.x requests the former + * parent (i.e. %Joiner Router's) MAC address even if the device has already promoted to a router. + * @par + * Note: When OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE is enabled, this command will return two extra lines with + * information relevant for CSL Receiver operation. + */ + if (aArgs[0].IsEmpty()) + { + otRouterInfo parentInfo; - SuccessOrExit(error = otThreadGetParentInfo(GetInstancePtr(), &parentInfo)); - OutputFormat("Ext Addr: "); - OutputExtAddressLine(parentInfo.mExtAddress); - OutputLine("Rloc: %x", parentInfo.mRloc16); - OutputLine("Link Quality In: %d", parentInfo.mLinkQualityIn); - OutputLine("Link Quality Out: %d", parentInfo.mLinkQualityOut); - OutputLine("Age: %d", parentInfo.mAge); + SuccessOrExit(error = otThreadGetParentInfo(GetInstancePtr(), &parentInfo)); + OutputFormat("Ext Addr: "); + OutputExtAddressLine(parentInfo.mExtAddress); + OutputLine("Rloc: %x", parentInfo.mRloc16); + OutputLine("Link Quality In: %u", parentInfo.mLinkQualityIn); + OutputLine("Link Quality Out: %u", parentInfo.mLinkQualityOut); + OutputLine("Age: %lu", ToUlong(parentInfo.mAge)); + OutputLine("Version: %u", parentInfo.mVersion); +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + OutputLine("CSL clock accuracy: %u", parentInfo.mCslClockAccuracy); + OutputLine("CSL uncertainty: %u", parentInfo.mCslUncertainty); +#endif + } + /** + * @cli parent search + * @code + * parent search + * Done + * @endcode + * @par api_copy + * #otThreadSearchForBetterParent + */ + else if (aArgs[0] == "search") + { + error = otThreadSearchForBetterParent(GetInstancePtr()); + } + else + { + error = OT_ERROR_INVALID_ARGS; + } exit: return error; } #if OPENTHREAD_FTD +/** + * @cli parentpriority (get,set) + * @code + * parentpriority + * 1 + * Done + * @endcode + * @code + * parentpriority 1 + * Done + * @endcode + * @cparam parentpriority [@ca{parentpriority}] + * @par + * Gets or sets the assigned parent priority value: 1, 0, -1 or -2. -2 means not assigned. + * @sa otThreadGetParentPriority + * @sa otThreadSetParentPriority + */ template <> otError Interpreter::Process(Arg aArgs[]) { return ProcessGetSet(aArgs, otThreadGetParentPriority, otThreadSetParentPriority); @@ -3411,7 +6145,7 @@ template <> otError Interpreter::Process(Arg *aArgs) if (aArgs[0].IsEmpty()) { otThreadGetRouterIdRange(GetInstancePtr(), &minRouterId, &maxRouterId); - OutputLine("%d %d", minRouterId, maxRouterId); + OutputLine("%u %u", minRouterId, maxRouterId); } else { @@ -3437,7 +6171,7 @@ void Interpreter::HandlePingReply(const otPingSenderReply *aReply) { OutputFormat("%u bytes from ", static_cast(aReply->mSize + sizeof(otIcmp6Header))); OutputIp6Address(aReply->mSenderAddress); - OutputLine(": icmp_seq=%d hlim=%d time=%dms", aReply->mSequenceNumber, aReply->mHopLimit, aReply->mRoundTripTime); + OutputLine(": icmp_seq=%u hlim=%u time=%ums", aReply->mSequenceNumber, aReply->mHopLimit, aReply->mRoundTripTime); } void Interpreter::HandlePingStatistics(const otPingSenderStatistics *aStatistics, void *aContext) @@ -3454,17 +6188,21 @@ void Interpreter::HandlePingStatistics(const otPingSenderStatistics *aStatistics { uint32_t packetLossRate = 1000 * (aStatistics->mSentCount - aStatistics->mReceivedCount) / aStatistics->mSentCount; - OutputFormat(" Packet loss = %u.%u%%.", packetLossRate / 10, packetLossRate % 10); + + OutputFormat(" Packet loss = %lu.%u%%.", ToUlong(packetLossRate / 10), + static_cast(packetLossRate % 10)); } if (aStatistics->mReceivedCount != 0) { uint32_t avgRoundTripTime = 1000 * aStatistics->mTotalRoundTripTime / aStatistics->mReceivedCount; + OutputFormat(" Round-trip min/avg/max = %u/%u.%u/%u ms.", aStatistics->mMinRoundTripTime, - avgRoundTripTime / 1000, avgRoundTripTime % 1000, aStatistics->mMaxRoundTripTime); + static_cast(avgRoundTripTime / 1000), static_cast(avgRoundTripTime % 1000), + aStatistics->mMaxRoundTripTime); } - OutputLine(""); + OutputNewLine(); if (!mPingIsAsync) { @@ -3472,12 +6210,55 @@ void Interpreter::HandlePingStatistics(const otPingSenderStatistics *aStatistics } } +/** + * @cli ping + * @code + * ping fd00:db8:0:0:76b:6a05:3ae9:a61a + * 16 bytes from fd00:db8:0:0:76b:6a05:3ae9:a61a: icmp_seq=5 hlim=64 time=0ms + * 1 packets transmitted, 1 packets received. Packet loss = 0.0%. Round-trip min/avg/max = 0/0.0/0 ms. + * Done + * @endcode + * @code + * ping -I fd00:db8:0:0:76b:6a05:3ae9:a61a ff02::1 100 1 1 1 + * 108 bytes from fd00:db8:0:0:f605:fb4b:d429:d59a: icmp_seq=4 hlim=64 time=7ms + * 1 packets transmitted, 1 packets received. Round-trip min/avg/max = 7/7.0/7 ms. + * Done + * @endcode + * @code + * ping 172.17.0.1 + * Pinging synthesized IPv6 address: fdde:ad00:beef:2:0:0:ac11:1 + * 16 bytes from fdde:ad00:beef:2:0:0:ac11:1: icmp_seq=5 hlim=64 time=0ms + * 1 packets transmitted, 1 packets received. Packet loss = 0.0%. Round-trip min/avg/max = 0/0.0/0 ms. + * Done + * @endcode + * @cparam ping [@ca{async}] [@ca{-I source}] @ca{ipaddrc} [@ca{size}] [@ca{count}] [@ca{interval}] [@ca{hoplimit}] [@ca{timeout}] + * @par + * Send an ICMPv6 Echo Request. + * @par + * The address can be an IPv4 address, which will be synthesized to an IPv6 address using the preferred NAT64 prefix + * from the network data. + * @par + * Note: The command will return InvalidState when the preferred NAT64 prefix is unavailable. + * @sa otPingSenderPing + */ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; otPingSenderConfig config; bool async = false; + bool nat64SynthesizedAddress; + /** + * @cli ping stop + * @code + * ping stop + * Done + * @endcode + * @par + * Stop sending ICMPv6 Echo Requests. + * @sa otPingSenderStop + */ if (aArgs[0] == "stop") { otPingSenderStop(GetInstancePtr()); @@ -3516,7 +6297,12 @@ template <> otError Interpreter::Process(Arg aArgs[]) aArgs += 2; } - SuccessOrExit(error = aArgs[0].ParseAsIp6Address(config.mDestination)); + SuccessOrExit(error = ParseToIp6Address(GetInstancePtr(), aArgs[0], config.mDestination, nat64SynthesizedAddress)); + if (nat64SynthesizedAddress) + { + OutputFormat("Pinging synthesized IPv6 address: "); + OutputIp6AddressLine(config.mDestination); + } if (!aArgs[1].IsEmpty()) { @@ -3569,6 +6355,39 @@ template <> otError Interpreter::Process(Arg aArgs[]) #endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE +/** + * @cli platform + * @code + * platform + * NRF52840 + * Done + * @endcode + * @par + * Print the current platform + */ +template <> otError Interpreter::Process(Arg aArgs[]) +{ + OT_UNUSED_VARIABLE(aArgs); + OutputLine("%s", OPENTHREAD_CONFIG_PLATFORM_INFO); + return OT_ERROR_NONE; +} + +/** + * @cli pollperiod (get,set) + * @code + * pollperiod + * 0 + * Done + * @endcode + * @code + * pollperiod 10 + * Done + * @endcode + * @sa otLinkGetPollPeriod + * @sa otLinkSetPollPeriod + * @par + * Get or set the customized data poll period of sleepy end device (milliseconds). Only for certification test. + */ template <> otError Interpreter::Process(Arg aArgs[]) { return ProcessGetSet(aArgs, otLinkGetPollPeriod, otLinkSetPollPeriod); @@ -3615,7 +6434,7 @@ void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx) { OT_UNUSED_VARIABLE(aIsTx); - OutputLine(""); + OutputNewLine(); for (size_t i = 0; i < 44; i++) { @@ -3629,7 +6448,7 @@ void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx) OutputFormat("="); } - OutputLine(""); + OutputNewLine(); for (size_t i = 0; i < aFrame->mLength; i += 16) { @@ -3676,7 +6495,7 @@ void Interpreter::HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx) OutputFormat("-"); } - OutputLine(""); + OutputNewLine(); } #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE @@ -3740,6 +6559,9 @@ otError Interpreter::ParsePrefix(Arg aArgs[], otBorderRouterConfig &aConfig) aConfig.mDp = true; break; #endif + case '-': + break; + default: ExitNow(error = OT_ERROR_INVALID_ARGS); } @@ -3901,11 +6723,21 @@ template <> otError Interpreter::Process(Arg aArgs[]) const char *version = otPlatRadioGetVersionString(GetInstancePtr()); VerifyOrExit(version != otGetVersionString(), error = OT_ERROR_NOT_IMPLEMENTED); - + /** + * @cli rcp version + * @code + * rcp version + * OPENTHREAD/20191113-00825-g82053cc9d-dirty; SIMULATION; Jun 4 2020 17:53:16 + * Done + * @endcode + * @par api_copy + * #otPlatRadioGetVersionString + */ if (aArgs[0] == "version") { OutputLine("%s", version); } + else { error = OT_ERROR_INVALID_ARGS; @@ -3914,7 +6746,16 @@ template <> otError Interpreter::Process(Arg aArgs[]) exit: return error; } - +/** + * @cli region + * @code + * region + * US + * Done + * @endcode + * @par api_copy + * #otPlatRadioGetRegion + */ template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -3925,7 +6766,19 @@ template <> otError Interpreter::Process(Arg aArgs[]) SuccessOrExit(error = otPlatRadioGetRegion(GetInstancePtr(), ®ionCode)); OutputLine("%c%c", regionCode >> 8, regionCode & 0xff); } + /** + * @cli region (set) + * @code + * region US + * Done + * @endcode + * @par api_copy + * #otPlatRadioSetRegion + * @par + * Changing this can affect the transmit power limit. + */ else + { VerifyOrExit(aArgs[0].GetLength() == 2, error = OT_ERROR_INVALID_ARGS); @@ -3939,13 +6792,34 @@ template <> otError Interpreter::Process(Arg aArgs[]) } #if OPENTHREAD_FTD +/** + * @cli releaserouterid (routerid) + * @code + * releaserouterid 16 + * Done + * @endcode + * @cparam releaserouterid [@ca{routerid}] + * @par api_copy + * #otThreadReleaseRouterId + */ template <> otError Interpreter::Process(Arg aArgs[]) + { return ProcessSet(aArgs, otThreadReleaseRouterId); } #endif - +/** + * @cli rloc16 + * @code + * rloc16 + * 0xdead + * Done + * @endcode + * @par api_copy + * #otThreadGetRloc16 + */ template <> otError Interpreter::Process(Arg aArgs[]) + { OT_UNUSED_VARIABLE(aArgs); @@ -3968,21 +6842,31 @@ otError Interpreter::ParseRoute(Arg aArgs[], otExternalRouteConfig &aConfig) { otRoutePreference preference; - if (*aArgs == "s") - { - aConfig.mStable = true; - } - else if (*aArgs == "n") - { - aConfig.mNat64 = true; - } - else if (ParsePreference(*aArgs, preference) == OT_ERROR_NONE) + if (ParsePreference(*aArgs, preference) == OT_ERROR_NONE) { aConfig.mPreference = preference; } else { - ExitNow(error = OT_ERROR_INVALID_ARGS); + for (char *arg = aArgs->GetCString(); *arg != '\0'; arg++) + { + switch (*arg) + { + case 's': + aConfig.mStable = true; + break; + + case 'n': + aConfig.mNat64 = true; + break; + + case '-': + break; + + default: + ExitNow(error = OT_ERROR_INVALID_ARGS); + } + } } } @@ -3993,7 +6877,17 @@ otError Interpreter::ParseRoute(Arg aArgs[], otExternalRouteConfig &aConfig) template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; - + /** + * @cli route + * @code + * route + * 2001:dead:beef:cafe::/64 s med + * Done + * @endcode + * @sa otBorderRouterGetNextRoute + * @par + * Get the external route list in the local Network Data. + */ if (aArgs[0].IsEmpty()) { otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; @@ -4004,6 +6898,23 @@ template <> otError Interpreter::Process(Arg aArgs[]) mNetworkData.OutputRoute(config); } } + /** + * @cli route add + * @code + * route add 2001:dead:beef:cafe::/64 s med + * Done + * @endcode + * @par + * For parameters, use: + * * s: Stable flag + * * n: NAT64 flag + * * prf: Default Router Preference, [high, med, low]. + * @cparam route add @ca{prefix} [@ca{sn}] [@ca{high}|@ca{med}|@ca{low}] + * @par api_copy + * #otExternalRouteConfig + * @par + * Add a valid external route to the Network Data. + */ else if (aArgs[0] == "add") { otExternalRouteConfig config; @@ -4011,6 +6922,16 @@ template <> otError Interpreter::Process(Arg aArgs[]) SuccessOrExit(error = ParseRoute(aArgs + 1, config)); error = otBorderRouterAddRoute(GetInstancePtr(), &config); } + /** + * @cli route remove + * @code + * route remove 2001:dead:beef:cafe::/64 + * Done + * @endcode + * @cparam route remove [@ca{prefix}] + * @par api_copy + * #otBorderRouterRemoveRoute + */ else if (aArgs[0] == "remove") { otIp6Prefix prefix; @@ -4035,9 +6956,32 @@ template <> otError Interpreter::Process(Arg aArgs[]) otRouterInfo routerInfo; uint16_t routerId; bool isTable; - + /** + * @cli router table + * @code + * router table + * | ID | RLOC16 | Next Hop | Path Cost | LQ In | LQ Out | Age | Extended MAC | Link | + * +----+--------+----------+-----------+-------+--------+-----+------------------+------+ + * | 22 | 0x5800 | 63 | 0 | 0 | 0 | 0 | 0aeb8196c9f61658 | 0 | + * | 49 | 0xc400 | 63 | 0 | 3 | 3 | 0 | faa1c03908e2dbf2 | 1 | + * Done + * @endcode + * @sa otThreadGetRouterInfo + * @par + * Prints a list of routers in a table format. + */ isTable = (aArgs[0] == "table"); - + /** + * @cli router list + * @code + * router list + * 8 24 50 + * Done + * @endcode + * @sa otThreadGetRouterInfo + * @par + * List allocated Router IDs. + */ if (isTable || (aArgs[0] == "list")) { uint8_t maxRouterId; @@ -4066,27 +7010,63 @@ template <> otError Interpreter::Process(Arg aArgs[]) if (isTable) { - OutputFormat("| %2d ", routerInfo.mRouterId); + OutputFormat("| %2u ", routerInfo.mRouterId); OutputFormat("| 0x%04x ", routerInfo.mRloc16); - OutputFormat("| %8d ", routerInfo.mNextHop); - OutputFormat("| %9d ", routerInfo.mPathCost); - OutputFormat("| %5d ", routerInfo.mLinkQualityIn); - OutputFormat("| %6d ", routerInfo.mLinkQualityOut); - OutputFormat("| %3d ", routerInfo.mAge); + OutputFormat("| %8u ", routerInfo.mNextHop); + OutputFormat("| %9u ", routerInfo.mPathCost); + OutputFormat("| %5u ", routerInfo.mLinkQualityIn); + OutputFormat("| %6u ", routerInfo.mLinkQualityOut); + OutputFormat("| %3u ", routerInfo.mAge); OutputFormat("| "); OutputExtAddress(routerInfo.mExtAddress); OutputLine(" | %4d |", routerInfo.mLinkEstablished); } else { - OutputFormat("%d ", i); + OutputFormat("%u ", i); } } - OutputLine(""); + OutputNewLine(); ExitNow(); } - + /** + * @cli router (id) + * @code + * router 50 + * Alloc: 1 + * Router ID: 50 + * Rloc: c800 + * Next Hop: c800 + * Link: 1 + * Ext Addr: e2b3540590b0fd87 + * Cost: 0 + * Link Quality In: 3 + * Link Quality Out: 3 + * Age: 3 + * Done + * @endcode + * @code + * router 0xc800 + * Alloc: 1 + * Router ID: 50 + * Rloc: c800 + * Next Hop: c800 + * Link: 1 + * Ext Addr: e2b3540590b0fd87 + * Cost: 0 + * Link Quality In: 3 + * Link Quality Out: 3 + * Age: 7 + * Done + * @endcode + * @cparam router [@ca{id}] + * @par api_copy + * #otThreadGetRouterInfo + * @par + * Print diagnostic information for a Thread Router. The id may be a Router ID or + * an RLOC16. + */ SuccessOrExit(error = aArgs[0].ParseAsUint16(routerId)); SuccessOrExit(error = otThreadGetRouterInfo(GetInstancePtr(), routerId, &routerInfo)); @@ -4094,7 +7074,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) if (routerInfo.mAllocated) { - OutputLine("Router ID: %d", routerInfo.mRouterId); + OutputLine("Router ID: %u", routerInfo.mRouterId); OutputLine("Rloc: %04x", routerInfo.mRloc16); OutputLine("Next Hop: %04x", static_cast(routerInfo.mNextHop) << 10); OutputLine("Link: %d", routerInfo.mLinkEstablished); @@ -4103,17 +7083,31 @@ template <> otError Interpreter::Process(Arg aArgs[]) { OutputFormat("Ext Addr: "); OutputExtAddressLine(routerInfo.mExtAddress); - OutputLine("Cost: %d", routerInfo.mPathCost); - OutputLine("Link Quality In: %d", routerInfo.mLinkQualityIn); - OutputLine("Link Quality Out: %d", routerInfo.mLinkQualityOut); - OutputLine("Age: %d", routerInfo.mAge); + OutputLine("Cost: %u", routerInfo.mPathCost); + OutputLine("Link Quality In: %u", routerInfo.mLinkQualityIn); + OutputLine("Link Quality Out: %u", routerInfo.mLinkQualityOut); + OutputLine("Age: %u", routerInfo.mAge); } } exit: return error; } - +/** + * @cli routerdowngradethreshold (get,set) + * @code routerdowngradethreshold + * 23 + * Done + * @endcode + * @code routerdowngradethreshold 23 + * Done + * @endcode + * @cparam routerdowngradethreshold [@ca{threshold}] + * @par + * Gets or sets the ROUTER_DOWNGRADE_THRESHOLD value. + * @sa otThreadGetRouterDowngradeThreshold + * @sa otThreadSetRouterDowngradeThreshold + */ template <> otError Interpreter::Process(Arg aArgs[]) { return ProcessGetSet(aArgs, otThreadGetRouterDowngradeThreshold, otThreadSetRouterDowngradeThreshold); @@ -4122,11 +7116,36 @@ template <> otError Interpreter::Process(Arg aA template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; - + /** + * @cli routereligible + * @code + * routereligible + * Enabled + * Done + * @endcode + * @sa otThreadIsRouterEligible + * @par + * Indicates whether the router role is enabled or disabled. + */ if (aArgs[0].IsEmpty()) { OutputEnabledDisabledStatus(otThreadIsRouterEligible(GetInstancePtr())); } + /** + * @cli routereligible (enable,disable) + * @code + * routereligible enable + * Done + * @endcode + * @code + * routereligible disable + * Done + * @endcode + * @cparam routereligible [@ca{enable|disable}] + * @sa otThreadSetRouterEligible + * @par + * Enables or disables the router role. + */ else { bool enable; @@ -4138,16 +7157,69 @@ template <> otError Interpreter::Process(Arg aArgs[]) exit: return error; } - +/** + * @cli routerselectionjitter + * @code + * routerselectionjitter + * 120 + * Done + * @endcode + * @code + * routerselectionjitter 120 + * Done + * @endcode + * @cparam routerselectionjitter [@ca{jitter}] + * @par + * Gets or sets the ROUTER_SELECTION_JITTER value. + * @sa otThreadGetRouterSelectionJitter + * @sa otThreadSetRouterSelectionJitter + */ template <> otError Interpreter::Process(Arg aArgs[]) { return ProcessGetSet(aArgs, otThreadGetRouterSelectionJitter, otThreadSetRouterSelectionJitter); } - +/** + * @cli routerupgradethreshold (get,set) + * @code + * routerupgradethreshold + * 16 + * Done + * @endcode + * @code + * routerupgradethreshold 16 + * Done + * @endcode + * @cparam routerupgradethreshold [@ca{threshold}] + * @par + * Gets or sets the ROUTER_UPGRADE_THRESHOLD value. + * @sa otThreadGetRouterUpgradeThreshold + * @sa otThreadSetRouterUpgradeThreshold + */ template <> otError Interpreter::Process(Arg aArgs[]) { return ProcessGetSet(aArgs, otThreadGetRouterUpgradeThreshold, otThreadSetRouterUpgradeThreshold); } +/** + * @cli childrouterlinks (get,set) + * @code + * childrouterlinks + * 16 + * Done + * @endcode + * @code + * childrouterlinks 16 + * Done + * @endcode + * @cparam childrouterlinks [@ca{links}] + * @par + * Gets or sets the MLE_CHILD_ROUTER_LINKS value. + * @sa otThreadGetChildRouterLinks + * @sa otThreadSetChildRouterLinks + */ +template <> otError Interpreter::Process(Arg aArgs[]) +{ + return ProcessGetSet(aArgs, otThreadGetChildRouterLinks, otThreadSetChildRouterLinks); +} #endif // OPENTHREAD_FTD template <> otError Interpreter::Process(Arg aArgs[]) @@ -4228,9 +7300,9 @@ void Interpreter::HandleActiveScanResult(otActiveScanResult *aResult) OutputFormat("| %04x | ", aResult->mPanId); OutputExtAddress(aResult->mExtAddress); - OutputFormat(" | %2d ", aResult->mChannel); + OutputFormat(" | %2u ", aResult->mChannel); OutputFormat("| %3d ", aResult->mRssi); - OutputLine("| %3d |", aResult->mLqi); + OutputLine("| %3u |", aResult->mLqi); exit: return; @@ -4249,7 +7321,7 @@ void Interpreter::HandleEnergyScanResult(otEnergyScanResult *aResult) ExitNow(); } - OutputLine("| %2d | %4d |", aResult->mChannel, aResult->mMaxRssi); + OutputLine("| %2u | %4d |", aResult->mChannel, aResult->mMaxRssi); exit: return; @@ -4320,8 +7392,8 @@ void Interpreter::HandleSntpResponse(uint64_t aTime, otError aResult) { // Some Embedded C libraries do not support printing of 64-bit unsigned integers. // To simplify, unix epoch time and era number are printed separately. - OutputLine("SNTP response - Unix time: %u (era: %u)", static_cast(aTime), - static_cast(aTime >> 32)); + OutputLine("SNTP response - Unix time: %lu (era: %lu)", ToUlong(static_cast(aTime)), + ToUlong(static_cast(aTime >> 32))); } else { @@ -4428,10 +7500,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) return error; } -template <> otError Interpreter::Process(Arg aArgs[]) -{ - return mDataset.Process(aArgs); -} +template <> otError Interpreter::Process(Arg aArgs[]) { return mDataset.Process(aArgs); } template <> otError Interpreter::Process(Arg aArgs[]) { @@ -4454,16 +7523,10 @@ template <> otError Interpreter::Process(Arg aArgs[]) } #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE -template <> otError Interpreter::Process(Arg aArgs[]) -{ - return mTcp.Process(aArgs); -} +template <> otError Interpreter::Process(Arg aArgs[]) { return mTcp.Process(aArgs); } #endif -template <> otError Interpreter::Process(Arg aArgs[]) -{ - return mUdp.Process(aArgs); -} +template <> otError Interpreter::Process(Arg aArgs[]) { return mUdp.Process(aArgs); } template <> otError Interpreter::Process(Arg aArgs[]) { @@ -4495,11 +7558,11 @@ template <> otError Interpreter::Process(Arg aArgs[]) { for (uint8_t i = 0; i < numPorts; i++) { - OutputFormat("%d ", ports[i]); + OutputFormat("%u ", ports[i]); } } - OutputLine(""); + OutputNewLine(); } else { @@ -4523,7 +7586,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) } else if (aArgs[0] == "ms") { - OutputLine("%lu", otInstanceGetUptime(GetInstancePtr())); + OutputUint64Line(otInstanceGetUptime(GetInstancePtr())); } else { @@ -4535,22 +7598,36 @@ template <> otError Interpreter::Process(Arg aArgs[]) #endif #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD -template <> otError Interpreter::Process(Arg aArgs[]) -{ - return mCommissioner.Process(aArgs); -} +template <> otError Interpreter::Process(Arg aArgs[]) { return mCommissioner.Process(aArgs); } #endif #if OPENTHREAD_CONFIG_JOINER_ENABLE -template <> otError Interpreter::Process(Arg aArgs[]) -{ - return mJoiner.Process(aArgs); -} +template <> otError Interpreter::Process(Arg aArgs[]) { return mJoiner.Process(aArgs); } #endif #if OPENTHREAD_FTD +/** + * @cli joinerport + * @code + * joinerport + * 1000 + * Done + * @endcode + * @par api_copy + * #otThreadGetJoinerUdpPort + */ template <> otError Interpreter::Process(Arg aArgs[]) { + /** + * @cli joinerport (set) + * @code + * joinerport 1000 + * Done + * @endcode + * @cparam joinerport @ca{udp-port} + * @par api_copy + * #otThreadSetJoinerUdpPort + */ return ProcessGetSet(aArgs, otThreadGetJoinerUdpPort, otThreadSetJoinerUdpPort); } #endif @@ -4609,7 +7686,7 @@ void Interpreter::PrintMacFilter(void) if (i == OT_EXT_ADDRESS_SIZE) { - OutputLine("Default rss : %d (lqi %d)", entry.mRssIn, + OutputLine("Default rss : %d (lqi %u)", entry.mRssIn, otLinkConvertRssToLinkQuality(GetInstancePtr(), entry.mRssIn)); } else @@ -4708,7 +7785,7 @@ otError Interpreter::ProcessMacFilterRss(Arg aArgs[]) if (i == OT_EXT_ADDRESS_SIZE) { - OutputLine("Default rss: %d (lqi %d)", entry.mRssIn, + OutputLine("Default rss: %d (lqi %u)", entry.mRssIn, otLinkConvertRssToLinkQuality(GetInstancePtr(), entry.mRssIn)); } else @@ -4784,7 +7861,7 @@ void Interpreter::OutputMacFilterEntry(const otMacFilterEntry &aEntry) otLinkConvertRssToLinkQuality(GetInstancePtr(), aEntry.mRssIn)); } - OutputLine(""); + OutputNewLine(); } const char *Interpreter::MacFilterAddressModeToString(otMacFilterAddressMode aMode) @@ -4866,14 +7943,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) } else if (ParseEnableOrDisable(aArgs[0], enable) == OT_ERROR_NONE) { - if (enable) - { - otTrelEnable(GetInstancePtr()); - } - else - { - otTrelDisable(GetInstancePtr()); - } + otTrelSetEnabled(GetInstancePtr(), enable); } else if (aArgs[0] == "filter") { @@ -4891,7 +7961,7 @@ template <> otError Interpreter::Process(Arg aArgs[]) { uint16_t index = 0; otTrelPeerIterator iterator; - const otTrelPeer * peer; + const otTrelPeer *peer; bool isTable = true; if (aArgs[1] == "list") @@ -4949,7 +8019,106 @@ template <> otError Interpreter::Process(Arg aArgs[]) } #endif -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE +template <> otError Interpreter::Process(Arg aArgs[]) +{ + Error error = OT_ERROR_INVALID_ARGS; + + /** + * @cli vendor name + * @code + * vendor name + * nest + * Done + * @endcode + * @par api_copy + * #otThreadGetVendorName + */ + if (aArgs[0] == "name") + { + aArgs++; + +#if !OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE + error = ProcessGet(aArgs, otThreadGetVendorName); +#else + /** + * @cli vendor name (name) + * @code + * vendor name nest + * Done + * @endcode + * @par api_copy + * #otThreadSetVendorName + * @cparam vendor name @ca{name} + */ + error = ProcessGetSet(aArgs, otThreadGetVendorName, otThreadSetVendorName); +#endif + } + /** + * @cli vendor model + * @code + * vendor model + * Hub Max + * Done + * @endcode + * @par api_copy + * #otThreadGetVendorModel + */ + else if (aArgs[0] == "model") + { + aArgs++; + +#if !OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE + error = ProcessGet(aArgs, otThreadGetVendorModel); +#else + /** + * @cli vendor model (name) + * @code + * vendor model Hub\ Max + * Done + * @endcode + * @par api_copy + * #otThreadSetVendorModel + * @cparam vendor model @ca{name} + */ + error = ProcessGetSet(aArgs, otThreadGetVendorModel, otThreadSetVendorModel); +#endif + } + /** + * @cli vendor swversion + * @code + * vendor swversion + * Marble3.5.1 + * Done + * @endcode + * @par api_copy + * #otThreadGetVendorSwVersion + */ + else if (aArgs[0] == "swversion") + { + aArgs++; + +#if !OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE + error = ProcessGet(aArgs, otThreadGetVendorSwVersion); +#else + /** + * @cli vendor swversion (version) + * @code + * vendor swversion Marble3.5.1 + * Done + * @endcode + * @par api_copy + * #otThreadSetVendorSwVersion + * @cparam vendor swversion @ca{version} + */ + error = ProcessGetSet(aArgs, otThreadGetVendorSwVersion, otThreadSetVendorSwVersion); +#endif + } + + return error; +} + +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE + template <> otError Interpreter::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -4986,16 +8155,16 @@ template <> otError Interpreter::Process(Arg aArgs[]) } void Interpreter::HandleDiagnosticGetResponse(otError aError, - otMessage * aMessage, + otMessage *aMessage, const otMessageInfo *aMessageInfo, - void * aContext) + void *aContext) { static_cast(aContext)->HandleDiagnosticGetResponse( aError, aMessage, static_cast(aMessageInfo)); } void Interpreter::HandleDiagnosticGetResponse(otError aError, - const otMessage * aMessage, + const otMessage *aMessage, const Ip6::MessageInfo *aMessageInfo) { uint8_t buf[16]; @@ -5015,7 +8184,7 @@ void Interpreter::HandleDiagnosticGetResponse(otError aError, while (length > 0) { - bytesToPrint = (length < sizeof(buf)) ? length : sizeof(buf); + bytesToPrint = Min(length, static_cast(sizeof(buf))); otMessageRead(aMessage, otMessageGetOffset(aMessage) + bytesPrinted, buf, bytesToPrint); OutputBytes(buf, static_cast(bytesToPrint)); @@ -5024,7 +8193,7 @@ void Interpreter::HandleDiagnosticGetResponse(otError aError, bytesPrinted += bytesToPrint; } - OutputLine(""); + OutputNewLine(); // Output Network Diagnostic TLV values in standard YAML format. while (otThreadGetNextDiagnosticTlv(aMessage, &iterator, &diagTlv) == OT_ERROR_NONE) @@ -5043,7 +8212,7 @@ void Interpreter::HandleDiagnosticGetResponse(otError aError, OutputMode(kIndentSize, diagTlv.mData.mMode); break; case OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT: - OutputLine("Timeout: %u", diagTlv.mData.mTimeout); + OutputLine("Timeout: %lu", ToUlong(diagTlv.mData.mTimeout)); break; case OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY: OutputLine("Connectivity:"); @@ -5093,7 +8262,21 @@ void Interpreter::HandleDiagnosticGetResponse(otError aError, OutputLine("'"); break; case OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT: - OutputLine("Max Child Timeout: %u", diagTlv.mData.mMaxChildTimeout); + OutputLine("Max Child Timeout: %lu", ToUlong(diagTlv.mData.mMaxChildTimeout)); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME: + OutputLine("Vendor Name: %s", diagTlv.mData.mVendorName); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL: + OutputLine("Vendor Model: %s", diagTlv.mData.mVendorModel); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION: + OutputLine("Vendor SW Version: %s", diagTlv.mData.mVendorSwVersion); + break; + case OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION: + OutputLine("Thread Stack Version: %s", diagTlv.mData.mThreadStackVersion); + break; + default: break; } } @@ -5145,7 +8328,7 @@ void Interpreter::OutputRouteData(uint8_t aIndentSize, const otNetworkDiagRouteD void Interpreter::OutputLeaderData(uint8_t aIndentSize, const otLeaderData &aLeaderData) { - OutputLine(aIndentSize, "PartitionId: 0x%08x", aLeaderData.mPartitionId); + OutputLine(aIndentSize, "PartitionId: 0x%08lx", ToUlong(aLeaderData.mPartitionId)); OutputLine(aIndentSize, "Weighting: %u", aLeaderData.mWeighting); OutputLine(aIndentSize, "DataVersion: %u", aLeaderData.mDataVersion); OutputLine(aIndentSize, "StableDataVersion: %u", aLeaderData.mStableDataVersion); @@ -5154,15 +8337,15 @@ void Interpreter::OutputLeaderData(uint8_t aIndentSize, const otLeaderData &aLea void Interpreter::OutputNetworkDiagMacCounters(uint8_t aIndentSize, const otNetworkDiagMacCounters &aMacCounters) { - OutputLine(aIndentSize, "IfInUnknownProtos: %u", aMacCounters.mIfInUnknownProtos); - OutputLine(aIndentSize, "IfInErrors: %u", aMacCounters.mIfInErrors); - OutputLine(aIndentSize, "IfOutErrors: %u", aMacCounters.mIfOutErrors); - OutputLine(aIndentSize, "IfInUcastPkts: %u", aMacCounters.mIfInUcastPkts); - OutputLine(aIndentSize, "IfInBroadcastPkts: %u", aMacCounters.mIfInBroadcastPkts); - OutputLine(aIndentSize, "IfInDiscards: %u", aMacCounters.mIfInDiscards); - OutputLine(aIndentSize, "IfOutUcastPkts: %u", aMacCounters.mIfOutUcastPkts); - OutputLine(aIndentSize, "IfOutBroadcastPkts: %u", aMacCounters.mIfOutBroadcastPkts); - OutputLine(aIndentSize, "IfOutDiscards: %u", aMacCounters.mIfOutDiscards); + OutputLine(aIndentSize, "IfInUnknownProtos: %lu", ToUlong(aMacCounters.mIfInUnknownProtos)); + OutputLine(aIndentSize, "IfInErrors: %lu", ToUlong(aMacCounters.mIfInErrors)); + OutputLine(aIndentSize, "IfOutErrors: %lu", ToUlong(aMacCounters.mIfOutErrors)); + OutputLine(aIndentSize, "IfInUcastPkts: %lu", ToUlong(aMacCounters.mIfInUcastPkts)); + OutputLine(aIndentSize, "IfInBroadcastPkts: %lu", ToUlong(aMacCounters.mIfInBroadcastPkts)); + OutputLine(aIndentSize, "IfInDiscards: %lu", ToUlong(aMacCounters.mIfInDiscards)); + OutputLine(aIndentSize, "IfOutUcastPkts: %lu", ToUlong(aMacCounters.mIfOutUcastPkts)); + OutputLine(aIndentSize, "IfOutBroadcastPkts: %lu", ToUlong(aMacCounters.mIfOutBroadcastPkts)); + OutputLine(aIndentSize, "IfOutDiscards: %lu", ToUlong(aMacCounters.mIfOutDiscards)); } void Interpreter::OutputChildTableEntry(uint8_t aIndentSize, const otNetworkDiagChildEntry &aChildEntry) @@ -5170,10 +8353,11 @@ void Interpreter::OutputChildTableEntry(uint8_t aIndentSize, const otNetworkDiag OutputLine("ChildId: 0x%04x", aChildEntry.mChildId); OutputLine(aIndentSize, "Timeout: %u", aChildEntry.mTimeout); + OutputLine(aIndentSize, "Link Quality: %u", aChildEntry.mLinkQuality); OutputLine(aIndentSize, "Mode:"); OutputMode(aIndentSize + kIndentSize, aChildEntry.mMode); } -#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE +#endif // OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE void Interpreter::HandleDetachGracefullyResult(void *aContext) { @@ -5186,12 +8370,28 @@ void Interpreter::HandleDetachGracefullyResult(void) OutputResult(OT_ERROR_NONE); } +#if OPENTHREAD_FTD +void Interpreter::HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo *aInfo, void *aContext) +{ + static_cast(aContext)->HandleDiscoveryRequest(*aInfo); +} + void Interpreter::HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo &aInfo) { OutputFormat("~ Discovery Request from "); OutputExtAddress(aInfo.mExtAddress); OutputLine(": version=%u,joiner=%d", aInfo.mVersion, aInfo.mIsJoiner); } +#endif + +#if OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK +void Interpreter::HandleIp6Receive(otMessage *aMessage, void *aContext) +{ + OT_UNUSED_VARIABLE(aContext); + + otMessageFree(aMessage); +} +#endif #endif // OPENTHREAD_FTD || OPENTHREAD_MTD @@ -5272,10 +8472,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) CmdEntry("child"), CmdEntry("childip"), CmdEntry("childmax"), + CmdEntry("childrouterlinks"), #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE CmdEntry("childsupervision"), -#endif CmdEntry("childtimeout"), #if OPENTHREAD_CONFIG_COAP_API_ENABLE CmdEntry("coap"), @@ -5302,6 +8501,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) #endif CmdEntry("detach"), #endif // OPENTHREAD_FTD || OPENTHREAD_MTD +#if OPENTHREAD_FTD + CmdEntry("deviceprops"), +#endif #if OPENTHREAD_CONFIG_DIAG_ENABLE CmdEntry("diag"), #endif @@ -5355,6 +8557,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE CmdEntry("macfilter"), #endif +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD + CmdEntry("meshdiag"), +#endif #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE CmdEntry("mliid"), #endif @@ -5363,12 +8568,15 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) #endif CmdEntry("mode"), CmdEntry("multiradio"), +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + CmdEntry("nat64"), +#endif #if OPENTHREAD_FTD CmdEntry("neighbor"), #endif CmdEntry("netdata"), CmdEntry("netstat"), -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE CmdEntry("networkdiagnostic"), #endif #if OPENTHREAD_FTD @@ -5381,6 +8589,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) CmdEntry("networkname"), #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE CmdEntry("networktime"), +#endif +#if OPENTHREAD_FTD + CmdEntry("nexthop"), #endif CmdEntry("panid"), CmdEntry("parent"), @@ -5391,6 +8602,7 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE CmdEntry("ping"), #endif + CmdEntry("platform"), CmdEntry("pollperiod"), #if OPENTHREAD_FTD CmdEntry("preferrouterid"), @@ -5458,6 +8670,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) #if OPENTHREAD_CONFIG_UPTIME_ENABLE CmdEntry("uptime"), #endif +#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE + CmdEntry("vendor"), +#endif #endif // OPENTHREAD_FTD || OPENTHREAD_MTD CmdEntry("version"), }; @@ -5477,9 +8692,12 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) { OutputCommandTable(kCommands); - for (uint8_t i = 0; i < mUserCommandsLength; i++) + for (const UserCommandsEntry &entry : mUserCommands) { - OutputLine("%s", mUserCommands[i].mName); + for (uint8_t i = 0; i < entry.mLength; i++) + { + OutputLine("%s", entry.mCommands[i].mName); + } } } else @@ -5495,14 +8713,11 @@ extern "C" void otCliInit(otInstance *aInstance, otCliOutputCallback aCallback, Interpreter::Initialize(aInstance, aCallback, aContext); } -extern "C" void otCliInputLine(char *aBuf) -{ - Interpreter::GetInterpreter().ProcessLine(aBuf); -} +extern "C" void otCliInputLine(char *aBuf) { Interpreter::GetInterpreter().ProcessLine(aBuf); } -extern "C" void otCliSetUserCommands(const otCliCommand *aUserCommands, uint8_t aLength, void *aContext) +extern "C" otError otCliSetUserCommands(const otCliCommand *aUserCommands, uint8_t aLength, void *aContext) { - Interpreter::GetInterpreter().SetUserCommands(aUserCommands, aLength, aContext); + return Interpreter::GetInterpreter().SetUserCommands(aUserCommands, aLength, aContext); } extern "C" void otCliOutputBytes(const uint8_t *aBytes, uint8_t aLength) @@ -5518,10 +8733,7 @@ extern "C" void otCliOutputFormat(const char *aFmt, ...) va_end(aAp); } -extern "C" void otCliAppendResult(otError aError) -{ - Interpreter::GetInterpreter().OutputResult(aError); -} +extern "C" void otCliAppendResult(otError aError) { Interpreter::GetInterpreter().OutputResult(aError); } extern "C" void otCliPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, va_list aArgs) { @@ -5534,7 +8746,7 @@ extern "C" void otCliPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, cons // `EmittingCommandOutput` to false indicate this. Interpreter::GetInterpreter().SetEmittingCommandOutput(false); Interpreter::GetInterpreter().OutputFormatV(aFormat, aArgs); - Interpreter::GetInterpreter().OutputLine(""); + Interpreter::GetInterpreter().OutputNewLine(); Interpreter::GetInterpreter().SetEmittingCommandOutput(true); exit: @@ -5543,20 +8755,3 @@ extern "C" void otCliPlatLogv(otLogLevel aLogLevel, otLogRegion aLogRegion, cons } // namespace Cli } // namespace ot - -#if OPENTHREAD_CONFIG_LEGACY_ENABLE -OT_TOOL_WEAK void otNcpRegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers) -{ - OT_UNUSED_VARIABLE(aHandlers); -} - -OT_TOOL_WEAK void otNcpHandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix) -{ - OT_UNUSED_VARIABLE(aUlaPrefix); -} - -OT_TOOL_WEAK void otNcpHandleLegacyNodeDidJoin(const otExtAddress *aExtAddr) -{ - OT_UNUSED_VARIABLE(aExtAddr); -} -#endif diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index f4aadbbe8c6..6c6cbbd8a78 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ #include #include +#include "cli/cli_br.hpp" #include "cli/cli_commissioner.hpp" #include "cli/cli_dataset.hpp" #include "cli/cli_history.hpp" @@ -74,6 +76,7 @@ #include "cli/cli_coap_secure.hpp" #endif +#include "common/array.hpp" #include "common/code_utils.hpp" #include "common/debug.hpp" #include "common/instance.hpp" @@ -99,9 +102,10 @@ extern "C" void otCliOutputFormat(const char *aFmt, ...); * This class implements the CLI interpreter. * */ -class Interpreter : public Output +class Interpreter : public OutputImplementer, public Output { #if OPENTHREAD_FTD || OPENTHREAD_MTD + friend class Br; friend class Commissioner; friend class Joiner; friend class NetworkData; @@ -177,14 +181,16 @@ class Interpreter : public Output static otError ParseEnableOrDisable(const Arg &aArg, bool &aEnable); /** - * This method sets the user command table. + * This method adds commands to the user command table. * * @param[in] aCommands A pointer to an array with user commands. * @param[in] aLength @p aUserCommands length. * @param[in] aContext @p aUserCommands length. * + * @retval OT_ERROR_NONE Successfully updated command table with commands from @p aCommands. + * @retval OT_ERROR_FAILED No available UserCommandsEntry to register requested user commands. */ - void SetUserCommands(const otCliCommand *aCommands, uint8_t aLength, void *aContext); + otError SetUserCommands(const otCliCommand *aCommands, uint8_t aLength, void *aContext); static constexpr uint8_t kLinkModeStringSize = sizeof("rdn"); ///< Size of string buffer for a MLE Link Mode. @@ -235,21 +241,45 @@ class Interpreter : public Output */ static const char *PreferenceToString(signed int aPreference); + /** + * This method parses the argument as an IP address. + * + * If the argument string is an IPv4 address, this method will try to synthesize an IPv6 address using preferred + * NAT64 prefix in the network data. + * + * @param[in] aInstance A pointer to openthread instance. + * @param[in] aArg The argument string to parse. + * @param[out] aAddress A reference to an `otIp6Address` to output the parsed IPv6 address. + * @param[out] aSynthesized Whether @p aAddress is synthesized from an IPv4 address. + * + * @retval OT_ERROR_NONE The argument was parsed successfully. + * @retval OT_ERROR_INVALID_ARGS The argument is empty or does not contain valid IP address. + * @retval OT_ERROR_INVALID_STATE No valid NAT64 prefix in the network data. + * + */ + static otError ParseToIp6Address(otInstance *aInstance, + const Arg &aArg, + otIp6Address &aAddress, + bool &aSynthesized); + protected: static Interpreter *sInterpreter; private: enum { - kIndentSize = 4, - kMaxArgs = 32, - kMaxAutoAddresses = 8, - kMaxLineLength = OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH, + kIndentSize = 4, + kMaxArgs = 32, + kMaxAutoAddresses = 8, + kMaxLineLength = OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH, + kMaxUserCommandEntries = OPENTHREAD_CONFIG_CLI_MAX_USER_CMD_ENTRIES, }; static constexpr uint32_t kNetworkDiagnosticTimeoutMsecs = 5000; static constexpr uint32_t kLocateTimeoutMsecs = 2500; + static constexpr uint16_t kMaxTxtDataSize = OPENTHREAD_CONFIG_CLI_TXT_RECORD_MAX_SIZE; + using Command = CommandEntry; template using GetHandler = ValueType (&)(otInstance *); @@ -259,13 +289,15 @@ class Interpreter : public Output // Returns format string to output a `ValueType` (e.g., "%u" for `uint16_t`). template static constexpr const char *FormatStringFor(void); + // General template implementation. + // Specializations for `uint32_t` and `int32_t` are added at the end. template otError ProcessGet(Arg aArgs[], GetHandler aGetHandler) { static_assert( TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || - TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue, - "ValueType must be an 8, 16, or 32 bit `int` or `uint` type"); + TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || + TypeTraits::IsSame::kValue, + "ValueType must be an 8, 16 `int` or `uint` type, or a `const char *`"); otError error = OT_ERROR_NONE; @@ -339,6 +371,9 @@ class Interpreter : public Output static otError ParsePrefix(Arg aArgs[], otBorderRouterConfig &aConfig); static otError ParseRoute(Arg aArgs[], otExternalRouteConfig &aConfig); #endif +#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + void OutputBorderRouterCounters(void); +#endif otError ProcessCommand(Arg aArgs[]); @@ -368,14 +403,18 @@ class Interpreter : public Output otError ParseLinkMetricsFlags(otLinkMetrics &aLinkMetrics, const Arg &aFlags); #endif #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE - static void HandleLocateResult(void * aContext, + static void HandleLocateResult(void *aContext, otError aError, const otIp6Address *aMeshLocalAddress, uint16_t aRloc16); void HandleLocateResult(otError aError, const otIp6Address *aMeshLocalAddress, uint16_t aRloc16); #endif +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD + static void HandleMeshDiagDiscoverDone(otError aError, otMeshDiagRouterInfo *aRouterInfo, void *aContext); + void HandleMeshDiagDiscoverDone(otError aError, otMeshDiagRouterInfo *aRouterInfo); +#endif #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE - static void HandleMlrRegResult(void * aContext, + static void HandleMlrRegResult(void *aContext, otError aError, uint8_t aMlrStatus, const otIp6Address *aFailedAddresses, @@ -405,12 +444,12 @@ class Interpreter : public Output static void HandleEnergyScanResult(otEnergyScanResult *aResult, void *aContext); static void HandleLinkPcapReceive(const otRadioFrame *aFrame, bool aIsTx, void *aContext); -#if OPENTHREAD_FTD || (OPENTHREAD_MTD && OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE) +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE void HandleDiagnosticGetResponse(otError aError, const otMessage *aMessage, const Ip6::MessageInfo *aMessageInfo); static void HandleDiagnosticGetResponse(otError aError, - otMessage * aMessage, + otMessage *aMessage, const otMessageInfo *aMessageInfo, - void * aContext); + void *aContext); void OutputMode(uint8_t aIndentSize, const otLinkModeConfig &aMode); void OutputConnectivity(uint8_t aIndentSize, const otNetworkDiagConnectivity &aConnectivity); @@ -425,6 +464,8 @@ class Interpreter : public Output otError GetDnsConfig(Arg aArgs[], otDnsQueryConfig *&aConfig); static void HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse, void *aContext); void HandleDnsAddressResponse(otError aError, const otDnsAddressResponse *aResponse); + const char *DnsConfigServiceModeToString(otDnsServiceMode aMode) const; + otError ParseDnsServiceMode(const Arg &aArg, otDnsServiceMode &aMode) const; #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE void OutputDnsServiceInfo(uint8_t aIndentSize, const otDnsServiceInfo &aServiceInfo); static void HandleDnsBrowseResponse(otError aError, const otDnsBrowseResponse *aResponse, void *aContext); @@ -451,12 +492,12 @@ class Interpreter : public Output #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE void PrintLinkMetricsValue(const otLinkMetricsValues *aMetricsValues); - static void HandleLinkMetricsReport(const otIp6Address * aAddress, + static void HandleLinkMetricsReport(const otIp6Address *aAddress, const otLinkMetricsValues *aMetricsValues, uint8_t aStatus, - void * aContext); + void *aContext); - void HandleLinkMetricsReport(const otIp6Address * aAddress, + void HandleLinkMetricsReport(const otIp6Address *aAddress, const otLinkMetricsValues *aMetricsValues, uint8_t aStatus); @@ -465,12 +506,12 @@ class Interpreter : public Output void HandleLinkMetricsMgmtResponse(const otIp6Address *aAddress, uint8_t aStatus); static void HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress, - const otExtAddress * aExtAddress, + const otExtAddress *aExtAddress, const otLinkMetricsValues *aMetricsValues, - void * aContext); + void *aContext); void HandleLinkMetricsEnhAckProbingIe(otShortAddress aShortAddress, - const otExtAddress * aExtAddress, + const otExtAddress *aExtAddress, const otLinkMetricsValues *aMetricsValues); const char *LinkMetricsStatusToStr(uint8_t aStatus); @@ -479,11 +520,14 @@ class Interpreter : public Output static void HandleDetachGracefullyResult(void *aContext); void HandleDetachGracefullyResult(void); - static void HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo *aInfo, void *aContext) - { - static_cast(aContext)->HandleDiscoveryRequest(*aInfo); - } - void HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo &aInfo); +#if OPENTHREAD_FTD + static void HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo *aInfo, void *aContext); + void HandleDiscoveryRequest(const otThreadDiscoveryRequestInfo &aInfo); +#endif + +#if OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK + static void HandleIp6Receive(otMessage *aMessage, void *aContext); +#endif #endif // OPENTHREAD_FTD || OPENTHREAD_MTD @@ -492,10 +536,15 @@ class Interpreter : public Output static void HandleTimer(Timer &aTimer); void HandleTimer(void); - const otCliCommand *mUserCommands; - uint8_t mUserCommandsLength; - void * mUserCommandsContext; - bool mCommandIsPending; + struct UserCommandsEntry + { + const otCliCommand *mCommands; + uint8_t mLength; + void *mContext; + }; + + UserCommandsEntry mUserCommands[kMaxUserCommandEntries]; + bool mCommandIsPending; TimerMilliContext mTimer; @@ -508,6 +557,10 @@ class Interpreter : public Output NetworkData mNetworkData; UdpExample mUdp; +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + Br mBr; +#endif + #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE TcpExample mTcp; #endif @@ -547,38 +600,50 @@ class Interpreter : public Output #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE bool mLocateInProgress : 1; #endif + +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE + bool mLinkMetricsQueryInProgress : 1; +#endif }; // Specializations of `FormatStringFor()` -template <> inline constexpr const char *Interpreter::FormatStringFor(void) -{ - return "%u"; -} +template <> inline constexpr const char *Interpreter::FormatStringFor(void) { return "%u"; } -template <> inline constexpr const char *Interpreter::FormatStringFor(void) -{ - return "%u"; -} +template <> inline constexpr const char *Interpreter::FormatStringFor(void) { return "%u"; } -template <> inline constexpr const char *Interpreter::FormatStringFor(void) -{ - return "%u"; -} +template <> inline constexpr const char *Interpreter::FormatStringFor(void) { return "%lu"; } -template <> inline constexpr const char *Interpreter::FormatStringFor(void) -{ - return "%d"; -} +template <> inline constexpr const char *Interpreter::FormatStringFor(void) { return "%d"; } + +template <> inline constexpr const char *Interpreter::FormatStringFor(void) { return "%d"; } + +template <> inline constexpr const char *Interpreter::FormatStringFor(void) { return "%ld"; } + +template <> inline constexpr const char *Interpreter::FormatStringFor(void) { return "%s"; } -template <> inline constexpr const char *Interpreter::FormatStringFor(void) +// Specialization of ProcessGet<> for `uint32_t` and `int32_t` + +template <> inline otError Interpreter::ProcessGet(Arg aArgs[], GetHandler aGetHandler) { - return "%d"; + otError error = OT_ERROR_NONE; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + OutputLine(FormatStringFor(), ToUlong(aGetHandler(GetInstancePtr()))); + +exit: + return error; } -template <> inline constexpr const char *Interpreter::FormatStringFor(void) +template <> inline otError Interpreter::ProcessGet(Arg aArgs[], GetHandler aGetHandler) { - return "%d"; + otError error = OT_ERROR_NONE; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + OutputLine(FormatStringFor(), static_cast(aGetHandler(GetInstancePtr()))); + +exit: + return error; } } // namespace Cli diff --git a/src/cli/cli_br.cpp b/src/cli/cli_br.cpp new file mode 100644 index 00000000000..a3e2152e54e --- /dev/null +++ b/src/cli/cli_br.cpp @@ -0,0 +1,537 @@ +/* + * Copyright (c) 2023, 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 CLI for Border Router. + */ + +#include "cli_br.hpp" + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + +#include + +#include + +#include "cli/cli.hpp" + +namespace ot { +namespace Cli { + +/** + * @cli br enable + * @code + * br enable + * Done + * @endcode + * @par + * Enables the Border Routing Manager. + * @sa otBorderRoutingSetEnabled + */ +template <> otError Br::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + error = otBorderRoutingSetEnabled(GetInstancePtr(), true); + +exit: + return error; +} + +/** + * @cli br disable + * @code + * br disable + * Done + * @endcode + * @par + * Disables the Border Routing Manager. + * @sa otBorderRoutingSetEnabled + */ +template <> otError Br::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + error = otBorderRoutingSetEnabled(GetInstancePtr(), false); + +exit: + return error; +} + +/** + * @cli br state + * @code + * br state + * running + * @endcode + * @par api_copy + * #otBorderRoutingGetState + */ +template <> otError Br::Process(Arg aArgs[]) +{ + static const char *const kStateStrings[] = { + + "uninitialized", // (0) OT_BORDER_ROUTING_STATE_UNINITIALIZED + "disabled", // (1) OT_BORDER_ROUTING_STATE_DISABLED + "stopped", // (2) OT_BORDER_ROUTING_STATE_STOPPED + "running", // (3) OT_BORDER_ROUTING_STATE_RUNNING + }; + + otError error = OT_ERROR_NONE; + + static_assert(0 == OT_BORDER_ROUTING_STATE_UNINITIALIZED, "STATE_UNINITIALIZED value is incorrect"); + static_assert(1 == OT_BORDER_ROUTING_STATE_DISABLED, "STATE_DISABLED value is incorrect"); + static_assert(2 == OT_BORDER_ROUTING_STATE_STOPPED, "STATE_STOPPED value is incorrect"); + static_assert(3 == OT_BORDER_ROUTING_STATE_RUNNING, "STATE_RUNNING value is incorrect"); + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + OutputLine("%s", Stringify(otBorderRoutingGetState(GetInstancePtr()), kStateStrings)); + +exit: + return error; +} + +otError Br::ParsePrefixTypeArgs(Arg aArgs[], PrefixType &aFlags) +{ + otError error = OT_ERROR_NONE; + + aFlags = 0; + + if (aArgs[0].IsEmpty()) + { + aFlags = kPrefixTypeFavored | kPrefixTypeLocal; + ExitNow(); + } + + if (aArgs[0] == "local") + { + aFlags = kPrefixTypeLocal; + } + else if (aArgs[0] == "favored") + { + aFlags = kPrefixTypeFavored; + } + else + { + ExitNow(error = OT_ERROR_INVALID_ARGS); + } + + VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + +exit: + return error; +} + +/** + * @cli br omrprefix + * @code + * br omrprefix + * Local: fdfc:1ff5:1512:5622::/64 + * Favored: fdfc:1ff5:1512:5622::/64 prf:low + * Done + * @endcode + * @par + * Outputs both local and favored OMR prefix. + * @sa otBorderRoutingGetOmrPrefix + * @sa otBorderRoutingGetFavoredOmrPrefix + */ +template <> otError Br::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + PrefixType outputPrefixTypes; + + SuccessOrExit(error = ParsePrefixTypeArgs(aArgs, outputPrefixTypes)); + + /** + * @cli br omrprefix local + * @code + * br omrprefix local + * fdfc:1ff5:1512:5622::/64 + * Done + * @endcode + * @par api_copy + * #otBorderRoutingGetOmrPrefix + */ + if (outputPrefixTypes & kPrefixTypeLocal) + { + otIp6Prefix local; + + SuccessOrExit(error = otBorderRoutingGetOmrPrefix(GetInstancePtr(), &local)); + + OutputFormat("%s", outputPrefixTypes == kPrefixTypeLocal ? "" : "Local: "); + OutputIp6PrefixLine(local); + } + + /** + * @cli br omrprefix favored + * @code + * br omrprefix favored + * fdfc:1ff5:1512:5622::/64 prf:low + * Done + * @endcode + * @par api_copy + * #otBorderRoutingGetFavoredOmrPrefix + */ + if (outputPrefixTypes & kPrefixTypeFavored) + { + otIp6Prefix favored; + otRoutePreference preference; + + SuccessOrExit(error = otBorderRoutingGetFavoredOmrPrefix(GetInstancePtr(), &favored, &preference)); + + OutputFormat("%s", outputPrefixTypes == kPrefixTypeFavored ? "" : "Favored: "); + OutputIp6Prefix(favored); + OutputLine(" prf:%s", Interpreter::PreferenceToString(preference)); + } + +exit: + return error; +} + +/** + * @cli br onlinkprefix + * @code + * br onlinkprefix + * Local: fd41:2650:a6f5:0::/64 + * Favored: 2600::0:1234:da12::/64 + * Done + * @endcode + * @par + * Outputs both local and favored on-link prefixes. + * @sa otBorderRoutingGetOnLinkPrefix + * @sa otBorderRoutingGetFavoredOnLinkPrefix + */ +template <> otError Br::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + PrefixType outputPrefixTypes; + + SuccessOrExit(error = ParsePrefixTypeArgs(aArgs, outputPrefixTypes)); + + /** + * @cli br onlinkprefix local + * @code + * br onlinkprefix local + * fd41:2650:a6f5:0::/64 + * Done + * @endcode + * @par api_copy + * #otBorderRoutingGetOnLinkPrefix + */ + if (outputPrefixTypes & kPrefixTypeLocal) + { + otIp6Prefix local; + + SuccessOrExit(error = otBorderRoutingGetOnLinkPrefix(GetInstancePtr(), &local)); + + OutputFormat("%s", outputPrefixTypes == kPrefixTypeLocal ? "" : "Local: "); + OutputIp6PrefixLine(local); + } + + /** + * @cli br onlinkprefix favored + * @code + * br onlinkprefix favored + * 2600::0:1234:da12::/64 + * Done + * @endcode + * @par api_copy + * #otBorderRoutingGetFavoredOnLinkPrefix + */ + if (outputPrefixTypes & kPrefixTypeFavored) + { + otIp6Prefix favored; + + SuccessOrExit(error = otBorderRoutingGetFavoredOnLinkPrefix(GetInstancePtr(), &favored)); + + OutputFormat("%s", outputPrefixTypes == kPrefixTypeFavored ? "" : "Favored: "); + OutputIp6PrefixLine(favored); + } + +exit: + return error; +} + +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + +/** + * @cli br nat64prefix + * @code + * br nat64prefix + * Local: fd14:1078:b3d5:b0b0:0:0::/96 + * Favored: fd14:1078:b3d5:b0b0:0:0::/96 prf:low + * Done + * @endcode + * @par + * Outputs both local and favored NAT64 prefixes. + * @sa otBorderRoutingGetNat64Prefix + * @sa otBorderRoutingGetFavoredNat64Prefix + */ +template <> otError Br::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + PrefixType outputPrefixTypes; + + SuccessOrExit(error = ParsePrefixTypeArgs(aArgs, outputPrefixTypes)); + + /** + * @cli br nat64prefix local + * @code + * br nat64prefix local + * fd14:1078:b3d5:b0b0:0:0::/96 + * Done + * @endcode + * @par api_copy + * #otBorderRoutingGetNat64Prefix + */ + if (outputPrefixTypes & kPrefixTypeLocal) + { + otIp6Prefix local; + + SuccessOrExit(error = otBorderRoutingGetNat64Prefix(GetInstancePtr(), &local)); + + OutputFormat("%s", outputPrefixTypes == kPrefixTypeLocal ? "" : "Local: "); + OutputIp6PrefixLine(local); + } + + /** + * @cli br nat64prefix favored + * @code + * br nat64prefix favored + * fd14:1078:b3d5:b0b0:0:0::/96 prf:low + * Done + * @endcode + * @par api_copy + * #otBorderRoutingGetFavoredNat64Prefix + */ + if (outputPrefixTypes & kPrefixTypeFavored) + { + otIp6Prefix favored; + otRoutePreference preference; + + SuccessOrExit(error = otBorderRoutingGetFavoredNat64Prefix(GetInstancePtr(), &favored, &preference)); + + OutputFormat("%s", outputPrefixTypes == kPrefixTypeFavored ? "" : "Favored: "); + OutputIp6Prefix(favored); + OutputLine(" prf:%s", Interpreter::PreferenceToString(preference)); + } + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + +/** + * @cli br prefixtable + * @code + * br prefixtable + * prefix:fd00:1234:5678:0::/64, on-link:no, ms-since-rx:29526, lifetime:1800, route-prf:med, + * router:ff02:0:0:0:0:0:0:1 + * prefix:1200:abba:baba:0::/64, on-link:yes, ms-since-rx:29527, lifetime:1800, preferred:1800, + * router:ff02:0:0:0:0:0:0:1 + * Done + * @endcode + * @par api_copy + * #otBorderRoutingGetNextPrefixTableEntry + */ +template <> otError Br::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + otBorderRoutingPrefixTableIterator iterator; + otBorderRoutingPrefixTableEntry entry; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + otBorderRoutingPrefixTableInitIterator(GetInstancePtr(), &iterator); + + while (otBorderRoutingGetNextPrefixTableEntry(GetInstancePtr(), &iterator, &entry) == OT_ERROR_NONE) + { + char string[OT_IP6_PREFIX_STRING_SIZE]; + + otIp6PrefixToString(&entry.mPrefix, string, sizeof(string)); + OutputFormat("prefix:%s, on-link:%s, ms-since-rx:%lu, lifetime:%lu, ", string, entry.mIsOnLink ? "yes" : "no", + ToUlong(entry.mMsecSinceLastUpdate), ToUlong(entry.mValidLifetime)); + + if (entry.mIsOnLink) + { + OutputFormat("preferred:%lu, ", ToUlong(entry.mPreferredLifetime)); + } + else + { + OutputFormat("route-prf:%s, ", Interpreter::PreferenceToString(entry.mRoutePreference)); + } + + otIp6AddressToString(&entry.mRouterAddress, string, sizeof(string)); + OutputLine("router:%s", string); + } + +exit: + return error; +} + +template <> otError Br::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + /** + * @cli br rioprf + * @code + * br rioprf + * med + * Done + * @endcode + * @par api_copy + * #otBorderRoutingGetRouteInfoOptionPreference + */ + if (aArgs[0].IsEmpty()) + { + OutputLine("%s", + Interpreter::PreferenceToString(otBorderRoutingGetRouteInfoOptionPreference(GetInstancePtr()))); + } + /** + * @cli br rioprf clear + * @code + * br rioprf clear + * Done + * @endcode + * @par api_copy + * #otBorderRoutingClearRouteInfoOptionPreference + */ + else if (aArgs[0] == "clear") + { + otBorderRoutingClearRouteInfoOptionPreference(GetInstancePtr()); + } + /** + * @cli br rioprf (high,med,low) + * @code + * br rioprf low + * Done + * @endcode + * @cparam br rioprf [@ca{high}|@ca{med}|@ca{low}] + * @par api_copy + * #otBorderRoutingSetRouteInfoOptionPreference + */ + else + { + otRoutePreference preference; + + SuccessOrExit(error = Interpreter::ParsePreference(aArgs[0], preference)); + otBorderRoutingSetRouteInfoOptionPreference(GetInstancePtr(), preference); + } + +exit: + return error; +} + +#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + +/** + * @cli br counters + * @code + * br counters + * Inbound Unicast: Packets 4 Bytes 320 + * Inbound Multicast: Packets 0 Bytes 0 + * Outbound Unicast: Packets 2 Bytes 160 + * Outbound Multicast: Packets 0 Bytes 0 + * RA Rx: 4 + * RA TxSuccess: 2 + * RA TxFailed: 0 + * RS Rx: 0 + * RS TxSuccess: 2 + * RS TxFailed: 0 + * Done + * @endcode + * @par api_copy + * #otIp6GetBorderRoutingCounters + */ +template <> otError Br::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + Interpreter::GetInterpreter().OutputBorderRouterCounters(); + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + +otError Br::Process(Arg aArgs[]) +{ +#define CmdEntry(aCommandString) \ + { \ + aCommandString, &Br::Process \ + } + + static constexpr Command kCommands[] = { +#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + CmdEntry("counters"), +#endif + CmdEntry("disable"), + CmdEntry("enable"), +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + CmdEntry("nat64prefix"), +#endif + CmdEntry("omrprefix"), + CmdEntry("onlinkprefix"), + CmdEntry("prefixtable"), + CmdEntry("rioprf"), + CmdEntry("state"), + }; + +#undef CmdEntry + + static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); + + otError error = OT_ERROR_INVALID_COMMAND; + const Command *command; + + if (aArgs[0].IsEmpty() || (aArgs[0] == "help")) + { + OutputCommandTable(kCommands); + ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE); + } + + command = BinarySearch::Find(aArgs[0].GetCString(), kCommands); + VerifyOrExit(command != nullptr); + + error = (this->*command->mHandler)(aArgs + 1); + +exit: + return error; +} + +} // namespace Cli +} // namespace ot + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE diff --git a/src/core/crypto/pbkdf2_cmac.hpp b/src/cli/cli_br.hpp similarity index 56% rename from src/core/crypto/pbkdf2_cmac.hpp rename to src/cli/cli_br.hpp index d98e33d2afe..794f8741f39 100644 --- a/src/core/crypto/pbkdf2_cmac.hpp +++ b/src/cli/cli_br.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The OpenThread Authors. + * Copyright (c) 2023, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,57 +28,69 @@ /** * @file - * @brief - * This file includes definitions for performing Password-Based Key Derivation Function 2 (PBKDF2) using CMAC. + * This file contains definitions for CLI to Border Router. */ -#ifndef PBKDF2_CMAC_HPP_ -#define PBKDF2_CMAC_HPP_ +#ifndef CLI_BR_HPP_ +#define CLI_BR_HPP_ #include "openthread-core-config.h" -#include +#include "cli/cli_config.h" +#include "cli/cli_output.hpp" + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE namespace ot { -namespace Crypto { -namespace Pbkdf2 { +namespace Cli { /** - * @addtogroup core-security - * - * @{ + * This class implements the Border Router CLI interpreter. * */ +class Br : private Output +{ +public: + typedef Utils::CmdLineParser::Arg Arg; -constexpr uint16_t kMaxSaltLength = 30; ///< Max SALT length: salt prefix (6) + extended panid (8) + network name (16) + /** + * Constructor + * + * @param[in] aInstance The OpenThread Instance. + * @param[in] aOutputImplementer An `OutputImplementer`. + * + */ + Br(otInstance *aInstance, OutputImplementer &aOutputImplementer) + : Output(aInstance, aOutputImplementer) + { + } -/** - * This function performs PKCS#5 PBKDF2 using CMAC (AES-CMAC-PRF-128). - * - * @param[in] aPassword Password to use when generating key. - * @param[in] aPasswordLen Length of password. - * @param[in] aSalt Salt to use when generating key. - * @param[in] aSaltLen Length of salt. - * @param[in] aIterationCounter Iteration count. - * @param[in] aKeyLen Length of generated key in bytes. - * @param[out] aKey A pointer to the generated key. - * - */ -void GenerateKey(const uint8_t *aPassword, - uint16_t aPasswordLen, - const uint8_t *aSalt, - uint16_t aSaltLen, - uint32_t aIterationCounter, - uint16_t aKeyLen, - uint8_t * aKey); + /** + * This method interprets a list of CLI arguments. + * + * @param[in] aArgs A pointer an array of command line arguments. + * + */ + otError Process(Arg aArgs[]); -/** - * @} - * - */ +private: + using Command = CommandEntry
; + + using PrefixType = uint8_t; + enum : PrefixType + { + kPrefixTypeLocal = 1u << 0, + kPrefixTypeFavored = 1u << 1, + }; -} // namespace Pbkdf2 -} // namespace Crypto + template otError Process(Arg aArgs[]); + + otError ParsePrefixTypeArgs(Arg aArgs[], PrefixType &aFlags); +}; + +} // namespace Cli } // namespace ot -#endif // PBKDF2_CMAC_HPP_ +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + +#endif // CLI_BR_HPP_ diff --git a/src/cli/cli_coap.cpp b/src/cli/cli_coap.cpp index 5d7c2609812..7f71781510c 100644 --- a/src/cli/cli_coap.cpp +++ b/src/cli/cli_coap.cpp @@ -44,8 +44,8 @@ namespace ot { namespace Cli { -Coap::Coap(Output &aOutput) - : OutputWrapper(aOutput) +Coap::Coap(otInstance *aInstance, OutputImplementer &aOutputImplementer) + : Output(aInstance, aOutputImplementer) , mUseDefaultRequestTxParameters(true) , mUseDefaultResponseTxParameters(true) #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE @@ -75,7 +75,7 @@ Coap::Coap(Output &aOutput) otError Coap::CancelResourceSubscription(void) { otError error = OT_ERROR_NONE; - otMessage * message = nullptr; + otMessage *message = nullptr; otMessageInfo messageInfo; memset(&messageInfo, 0, sizeof(messageInfo)); @@ -128,7 +128,7 @@ void Coap::PrintPayload(otMessage *aMessage) while (length > 0) { - bytesToPrint = (length < sizeof(buf)) ? length : sizeof(buf); + bytesToPrint = Min(length, static_cast(sizeof(buf))); otMessageRead(aMessage, otMessageGetOffset(aMessage) + bytesPrinted, buf, bytesToPrint); OutputBytes(buf, static_cast(bytesToPrint)); @@ -138,7 +138,7 @@ void Coap::PrintPayload(otMessage *aMessage) } } - OutputLine(""); + OutputNewLine(); } #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE @@ -192,7 +192,7 @@ template <> otError Coap::Process(Arg aArgs[]) template <> otError Coap::Process(Arg aArgs[]) { #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE - otMessage * notificationMessage = nullptr; + otMessage *notificationMessage = nullptr; otMessageInfo messageInfo; #endif otError error = OT_ERROR_NONE; @@ -273,7 +273,7 @@ template <> otError Coap::Process(Arg aArgs[]) template <> otError Coap::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; - bool * defaultTxParameters; + bool *defaultTxParameters; otCoapTxParameters *txParameters; if (aArgs[0] == "request") @@ -319,7 +319,7 @@ template <> otError Coap::Process(Arg aArgs[]) } else { - OutputLine("ACK_TIMEOUT=%u ms, ACK_RANDOM_FACTOR=%u/%u, MAX_RETRANSMIT=%u", txParameters->mAckTimeout, + OutputLine("ACK_TIMEOUT=%lu ms, ACK_RANDOM_FACTOR=%u/%u, MAX_RETRANSMIT=%u", ToUlong(txParameters->mAckTimeout), txParameters->mAckRandomFactorNumerator, txParameters->mAckRandomFactorDenominator, txParameters->mMaxRetransmit); } @@ -328,25 +328,13 @@ template <> otError Coap::Process(Arg aArgs[]) return error; } -template <> otError Coap::Process(Arg aArgs[]) -{ - return ProcessRequest(aArgs, OT_COAP_CODE_GET); -} +template <> otError Coap::Process(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_GET); } -template <> otError Coap::Process(Arg aArgs[]) -{ - return ProcessRequest(aArgs, OT_COAP_CODE_POST); -} +template <> otError Coap::Process(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_POST); } -template <> otError Coap::Process(Arg aArgs[]) -{ - return ProcessRequest(aArgs, OT_COAP_CODE_PUT); -} +template <> otError Coap::Process(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_PUT); } -template <> otError Coap::Process(Arg aArgs[]) -{ - return ProcessRequest(aArgs, OT_COAP_CODE_DELETE); -} +template <> otError Coap::Process(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_DELETE); } #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE template <> otError Coap::Process(Arg aArgs[]) @@ -362,7 +350,7 @@ otError Coap::ProcessRequest(Arg aArgs[], otCoapCode aCoapCode) #endif { otError error = OT_ERROR_NONE; - otMessage * message = nullptr; + otMessage *message = nullptr; otMessageInfo messageInfo; uint16_t payloadLength = 0; @@ -647,7 +635,8 @@ void Coap::HandleRequest(otMessage *aMessage, const otMessageInfo *aMessageInfo) SuccessOrExit(error = otCoapOptionIteratorGetOptionUintValue(&iterator, &observe)); observePresent = true; - OutputFormat(" OBS=%lu", static_cast(observe)); + OutputFormat(" OBS="); + OutputUint64(observe); } #endif #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE @@ -799,8 +788,8 @@ void Coap::HandleRequest(otMessage *aMessage, const otMessageInfo *aMessageInfo) } #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE -void Coap::HandleNotificationResponse(void * aContext, - otMessage * aMessage, +void Coap::HandleNotificationResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError) { @@ -862,7 +851,8 @@ void Coap::HandleResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo if (error == OT_ERROR_NONE) { - OutputFormat(" OBS=%u", observeVal); + OutputFormat(" OBS="); + OutputUint64(observeVal); } } } @@ -872,7 +862,7 @@ void Coap::HandleResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo } #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE -otError Coap::BlockwiseReceiveHook(void * aContext, +otError Coap::BlockwiseReceiveHook(void *aContext, const uint8_t *aBlock, uint32_t aPosition, uint16_t aBlockLength, @@ -901,11 +891,11 @@ otError Coap::BlockwiseReceiveHook(const uint8_t *aBlock, return OT_ERROR_NONE; } -otError Coap::BlockwiseTransmitHook(void * aContext, - uint8_t * aBlock, +otError Coap::BlockwiseTransmitHook(void *aContext, + uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, - bool * aMore) + bool *aMore) { return static_cast(aContext)->BlockwiseTransmitHook(aBlock, aPosition, aBlockLength, aMore); } diff --git a/src/cli/cli_coap.hpp b/src/cli/cli_coap.hpp index afa56bfca2c..041e7ae9451 100644 --- a/src/cli/cli_coap.hpp +++ b/src/cli/cli_coap.hpp @@ -49,7 +49,7 @@ namespace Cli { * This class implements the CLI CoAP server and client. * */ -class Coap : private OutputWrapper +class Coap : private Output { public: typedef Utils::CmdLineParser::Arg Arg; @@ -57,10 +57,11 @@ class Coap : private OutputWrapper /** * Constructor * - * @param[in] aOutput The CLI console output context + * @param[in] aInstance The OpenThread Instance. + * @param[in] aOutputImplementer An `OutputImplementer`. * */ - explicit Coap(Output &aOutput); + Coap(otInstance *aInstance, OutputImplementer &aOutputImplementer); /** * This method interprets a list of CLI arguments. @@ -105,8 +106,8 @@ class Coap : private OutputWrapper void HandleRequest(otMessage *aMessage, const otMessageInfo *aMessageInfo); #if OPENTHREAD_CONFIG_COAP_OBSERVE_API_ENABLE - static void HandleNotificationResponse(void * aContext, - otMessage * aMessage, + static void HandleNotificationResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError); void HandleNotificationResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError); @@ -117,7 +118,7 @@ class Coap : private OutputWrapper #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE - static otError BlockwiseReceiveHook(void * aContext, + static otError BlockwiseReceiveHook(void *aContext, const uint8_t *aBlock, uint32_t aPosition, uint16_t aBlockLength, @@ -128,11 +129,11 @@ class Coap : private OutputWrapper uint16_t aBlockLength, bool aMore, uint32_t aTotalLength); - static otError BlockwiseTransmitHook(void * aContext, - uint8_t * aBlock, + static otError BlockwiseTransmitHook(void *aContext, + uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, - bool * aMore); + bool *aMore); otError BlockwiseTransmitHook(uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, bool *aMore); #endif diff --git a/src/cli/cli_coap_secure.cpp b/src/cli/cli_coap_secure.cpp index aae479829a7..518e9075d53 100644 --- a/src/cli/cli_coap_secure.cpp +++ b/src/cli/cli_coap_secure.cpp @@ -46,10 +46,8 @@ namespace ot { namespace Cli { -constexpr CoapSecure::Command CoapSecure::sCommands[]; - -CoapSecure::CoapSecure(Output &aOutput) - : OutputWrapper(aOutput) +CoapSecure::CoapSecure(otInstance *aInstance, OutputImplementer &aOutputImplementer) + : Output(aInstance, aOutputImplementer) , mShutdownFlag(false) , mUseCertificate(false) , mPskLength(0) @@ -79,7 +77,7 @@ void CoapSecure::PrintPayload(otMessage *aMessage) while (length > 0) { - bytesToPrint = (length < sizeof(buf)) ? length : sizeof(buf); + bytesToPrint = Min(length, static_cast(sizeof(buf))); otMessageRead(aMessage, otMessageGetOffset(aMessage) + bytesPrinted, buf, bytesToPrint); OutputBytes(buf, static_cast(bytesToPrint)); @@ -89,22 +87,10 @@ void CoapSecure::PrintPayload(otMessage *aMessage) } } - OutputLine(""); + OutputNewLine(); } -otError CoapSecure::ProcessHelp(Arg aArgs[]) -{ - OT_UNUSED_VARIABLE(aArgs); - - for (const Command &command : sCommands) - { - OutputLine(command.mName); - } - - return OT_ERROR_NONE; -} - -otError CoapSecure::ProcessResource(Arg aArgs[]) +template <> otError CoapSecure::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -142,7 +128,7 @@ otError CoapSecure::ProcessResource(Arg aArgs[]) return error; } -otError CoapSecure::ProcessSet(Arg aArgs[]) +template <> otError CoapSecure::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -161,7 +147,7 @@ otError CoapSecure::ProcessSet(Arg aArgs[]) return error; } -otError CoapSecure::ProcessStart(Arg aArgs[]) +template <> otError CoapSecure::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; bool verifyPeerCert = true; @@ -191,7 +177,7 @@ otError CoapSecure::ProcessStart(Arg aArgs[]) return error; } -otError CoapSecure::ProcessStop(Arg aArgs[]) +template <> otError CoapSecure::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); @@ -214,22 +200,13 @@ otError CoapSecure::ProcessStop(Arg aArgs[]) return OT_ERROR_NONE; } -otError CoapSecure::ProcessGet(Arg aArgs[]) -{ - return ProcessRequest(aArgs, OT_COAP_CODE_GET); -} +template <> otError CoapSecure::Process(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_GET); } -otError CoapSecure::ProcessPost(Arg aArgs[]) -{ - return ProcessRequest(aArgs, OT_COAP_CODE_POST); -} +template <> otError CoapSecure::Process(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_POST); } -otError CoapSecure::ProcessPut(Arg aArgs[]) -{ - return ProcessRequest(aArgs, OT_COAP_CODE_PUT); -} +template <> otError CoapSecure::Process(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_PUT); } -otError CoapSecure::ProcessDelete(Arg aArgs[]) +template <> otError CoapSecure::Process(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_DELETE); } @@ -384,7 +361,7 @@ otError CoapSecure::ProcessRequest(Arg aArgs[], otCoapCode aCoapCode) return error; } -otError CoapSecure::ProcessConnect(Arg aArgs[]) +template <> otError CoapSecure::Process(Arg aArgs[]) { otError error; otSockAddr sockaddr; @@ -404,7 +381,7 @@ otError CoapSecure::ProcessConnect(Arg aArgs[]) return error; } -otError CoapSecure::ProcessDisconnect(Arg aArgs[]) +template <> otError CoapSecure::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); @@ -414,7 +391,7 @@ otError CoapSecure::ProcessDisconnect(Arg aArgs[]) } #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED -otError CoapSecure::ProcessPsk(Arg aArgs[]) +template <> otError CoapSecure::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; uint16_t length; @@ -440,7 +417,7 @@ otError CoapSecure::ProcessPsk(Arg aArgs[]) #endif // MBEDTLS_KEY_EXCHANGE_PSK_ENABLED #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED -otError CoapSecure::ProcessX509(Arg aArgs[]) +template <> otError CoapSecure::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); @@ -459,17 +436,37 @@ otError CoapSecure::ProcessX509(Arg aArgs[]) otError CoapSecure::Process(Arg aArgs[]) { - otError error = OT_ERROR_INVALID_ARGS; +#define CmdEntry(aCommandString) \ + { \ + aCommandString, &CoapSecure::Process \ + } + + static constexpr Command kCommands[] = { + CmdEntry("connect"), CmdEntry("delete"), CmdEntry("disconnect"), CmdEntry("get"), CmdEntry("post"), +#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + CmdEntry("psk"), +#endif + CmdEntry("put"), CmdEntry("resource"), CmdEntry("set"), CmdEntry("start"), CmdEntry("stop"), +#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + CmdEntry("x509"), +#endif + }; + +#undef CmdEntry + + static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); + + otError error = OT_ERROR_INVALID_COMMAND; const Command *command; - if (aArgs[0].IsEmpty()) + if (aArgs[0].IsEmpty() || (aArgs[0] == "help")) { - IgnoreError(ProcessHelp(aArgs)); - ExitNow(); + OutputCommandTable(kCommands); + ExitNow(error = aArgs[0].IsEmpty() ? OT_ERROR_INVALID_ARGS : OT_ERROR_NONE); } - command = BinarySearch::Find(aArgs[0].GetCString(), sCommands); - VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND); + command = BinarySearch::Find(aArgs[0].GetCString(), kCommands); + VerifyOrExit(command != nullptr); error = (this->*command->mHandler)(aArgs + 1); @@ -688,7 +685,7 @@ void CoapSecure::DefaultHandler(otMessage *aMessage, const otMessageInfo *aMessa #endif // CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE -otError CoapSecure::BlockwiseReceiveHook(void * aContext, +otError CoapSecure::BlockwiseReceiveHook(void *aContext, const uint8_t *aBlock, uint32_t aPosition, uint16_t aBlockLength, @@ -718,11 +715,11 @@ otError CoapSecure::BlockwiseReceiveHook(const uint8_t *aBlock, return OT_ERROR_NONE; } -otError CoapSecure::BlockwiseTransmitHook(void * aContext, - uint8_t * aBlock, +otError CoapSecure::BlockwiseTransmitHook(void *aContext, + uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, - bool * aMore) + bool *aMore) { return static_cast(aContext)->BlockwiseTransmitHook(aBlock, aPosition, aBlockLength, aMore); } diff --git a/src/cli/cli_coap_secure.hpp b/src/cli/cli_coap_secure.hpp index 3bd48729082..9bdc63135ea 100644 --- a/src/cli/cli_coap_secure.hpp +++ b/src/cli/cli_coap_secure.hpp @@ -55,7 +55,7 @@ namespace Cli { * This class implements the CLI CoAP Secure server and client. * */ -class CoapSecure : private OutputWrapper +class CoapSecure : private Output { public: typedef Utils::CmdLineParser::Arg Arg; @@ -63,10 +63,11 @@ class CoapSecure : private OutputWrapper /** * Constructor * - * @param[in] aOutput The CLI console output context + * @param[in] aInstance The OpenThread Instance. + * @param[in] aOutputImplementer An `OutputImplementer`. * */ - explicit CoapSecure(Output &aOutput); + CoapSecure(otInstance *aInstance, OutputImplementer &aOutputImplementer); /** * This method interprets a list of CLI arguments. @@ -96,19 +97,7 @@ class CoapSecure : private OutputWrapper void PrintPayload(otMessage *aMessage); - otError ProcessConnect(Arg aArgs[]); - otError ProcessDelete(Arg aArgs[]); - otError ProcessDisconnect(Arg aArgs[]); - otError ProcessGet(Arg aArgs[]); - otError ProcessHelp(Arg aArgs[]); - otError ProcessPost(Arg aArgs[]); - otError ProcessPsk(Arg aArgs[]); - otError ProcessPut(Arg aArgs[]); - otError ProcessResource(Arg aArgs[]); - otError ProcessSet(Arg aArgs[]); - otError ProcessStart(Arg aArgs[]); - otError ProcessStop(Arg aArgs[]); - otError ProcessX509(Arg aArgs[]); + template otError Process(Arg aArgs[]); otError ProcessRequest(Arg aArgs[], otCoapCode aCoapCode); @@ -122,7 +111,7 @@ class CoapSecure : private OutputWrapper #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE - static otError BlockwiseReceiveHook(void * aContext, + static otError BlockwiseReceiveHook(void *aContext, const uint8_t *aBlock, uint32_t aPosition, uint16_t aBlockLength, @@ -133,11 +122,11 @@ class CoapSecure : private OutputWrapper uint16_t aBlockLength, bool aMore, uint32_t aTotalLength); - static otError BlockwiseTransmitHook(void * aContext, - uint8_t * aBlock, + static otError BlockwiseTransmitHook(void *aContext, + uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, - bool * aMore); + bool *aMore); otError BlockwiseTransmitHook(uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, bool *aMore); #endif @@ -149,28 +138,6 @@ class CoapSecure : private OutputWrapper static void HandleConnected(bool aConnected, void *aContext); void HandleConnected(bool aConnected); - static constexpr Command sCommands[] = { - {"connect", &CoapSecure::ProcessConnect}, - {"delete", &CoapSecure::ProcessDelete}, - {"disconnect", &CoapSecure::ProcessDisconnect}, - {"get", &CoapSecure::ProcessGet}, - {"help", &CoapSecure::ProcessHelp}, - {"post", &CoapSecure::ProcessPost}, -#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED - {"psk", &CoapSecure::ProcessPsk}, -#endif - {"put", &CoapSecure::ProcessPut}, - {"resource", &CoapSecure::ProcessResource}, - {"set", &CoapSecure::ProcessSet}, - {"start", &CoapSecure::ProcessStart}, - {"stop", &CoapSecure::ProcessStop}, -#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED - {"x509", &CoapSecure::ProcessX509}, -#endif - }; - - static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted"); - #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE otCoapBlockwiseResource mResource; #else diff --git a/src/cli/cli_commissioner.cpp b/src/cli/cli_commissioner.cpp index bbbcfceb272..d8fa14a099d 100644 --- a/src/cli/cli_commissioner.cpp +++ b/src/cli/cli_commissioner.cpp @@ -117,13 +117,15 @@ template <> otError Commissioner::Process(Arg aArgs[]) break; case OT_JOINER_INFO_TYPE_DISCERNER: - OutputFormat("| 0x%016llx/%2u", static_cast(joinerInfo.mSharedId.mDiscerner.mValue), + OutputFormat("| 0x%08lx%08lx/%2u", + static_cast(joinerInfo.mSharedId.mDiscerner.mValue >> 32), + static_cast(joinerInfo.mSharedId.mDiscerner.mValue & 0xffffffff), joinerInfo.mSharedId.mDiscerner.mLength); break; } - OutputFormat(" | %32s | %10d |", joinerInfo.mPskd.m8, joinerInfo.mExpirationTime); - OutputLine(""); + OutputFormat(" | %32s | %10lu |", joinerInfo.mPskd.m8, ToUlong(joinerInfo.mExpirationTime)); + OutputNewLine(); } ExitNow(error = OT_ERROR_NONE); @@ -383,16 +385,16 @@ const char *Commissioner::StateToString(otCommissionerState aState) } void Commissioner::HandleJoinerEvent(otCommissionerJoinerEvent aEvent, - const otJoinerInfo * aJoinerInfo, - const otExtAddress * aJoinerId, - void * aContext) + const otJoinerInfo *aJoinerInfo, + const otExtAddress *aJoinerId, + void *aContext) { static_cast(aContext)->HandleJoinerEvent(aEvent, aJoinerInfo, aJoinerId); } void Commissioner::HandleJoinerEvent(otCommissionerJoinerEvent aEvent, - const otJoinerInfo * aJoinerInfo, - const otExtAddress * aJoinerId) + const otJoinerInfo *aJoinerInfo, + const otExtAddress *aJoinerId) { static const char *const kEventStrings[] = { "start", // (0) OT_COMMISSIONER_JOINER_START @@ -417,7 +419,7 @@ void Commissioner::HandleJoinerEvent(otCommissionerJoinerEvent aEvent, OutputExtAddress(*aJoinerId); } - OutputLine(""); + OutputNewLine(); } template <> otError Commissioner::Process(Arg aArgs[]) @@ -431,7 +433,7 @@ template <> otError Commissioner::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); - OutputLine(StateToString(otCommissionerGetState(GetInstancePtr()))); + OutputLine("%s", StateToString(otCommissionerGetState(GetInstancePtr()))); return OT_ERROR_NONE; } @@ -474,21 +476,21 @@ otError Commissioner::Process(Arg aArgs[]) void Commissioner::HandleEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength, - void * aContext) + void *aContext) { static_cast(aContext)->HandleEnergyReport(aChannelMask, aEnergyList, aEnergyListLength); } void Commissioner::HandleEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength) { - OutputFormat("Energy: %08x ", aChannelMask); + OutputFormat("Energy: %08lx ", ToUlong(aChannelMask)); for (uint8_t i = 0; i < aEnergyListLength; i++) { OutputFormat("%d ", static_cast(aEnergyList[i])); } - OutputLine(""); + OutputNewLine(); } void Commissioner::HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask, void *aContext) @@ -498,7 +500,7 @@ void Commissioner::HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask, v void Commissioner::HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask) { - OutputLine("Conflict: %04x, %08x", aPanId, aChannelMask); + OutputLine("Conflict: %04x, %08lx", aPanId, ToUlong(aChannelMask)); } } // namespace Cli diff --git a/src/cli/cli_commissioner.hpp b/src/cli/cli_commissioner.hpp index d63089d3d17..7b63cb0d2b4 100644 --- a/src/cli/cli_commissioner.hpp +++ b/src/cli/cli_commissioner.hpp @@ -49,7 +49,7 @@ namespace Cli { * This class implements the Commissioner CLI interpreter. * */ -class Commissioner : private OutputWrapper +class Commissioner : private Output { public: typedef Utils::CmdLineParser::Arg Arg; @@ -57,11 +57,12 @@ class Commissioner : private OutputWrapper /** * Constructor * - * @param[in] aOutput The CLI console output context + * @param[in] aInstance The OpenThread Instance. + * @param[in] aOutputImplementer An `OutputImplementer`. * */ - explicit Commissioner(Output &aOutput) - : OutputWrapper(aOutput) + Commissioner(otInstance *aInstance, OutputImplementer &aOutputImplementer) + : Output(aInstance, aOutputImplementer) { } @@ -87,17 +88,17 @@ class Commissioner : private OutputWrapper void HandleStateChanged(otCommissionerState aState); static void HandleJoinerEvent(otCommissionerJoinerEvent aEvent, - const otJoinerInfo * aJoinerInfo, - const otExtAddress * aJoinerId, - void * aContext); + const otJoinerInfo *aJoinerInfo, + const otExtAddress *aJoinerId, + void *aContext); void HandleJoinerEvent(otCommissionerJoinerEvent aEvent, - const otJoinerInfo * aJoinerInfo, - const otExtAddress * aJoinerId); + const otJoinerInfo *aJoinerInfo, + const otExtAddress *aJoinerId); static void HandleEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength, - void * aContext); + void *aContext); void HandleEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyList, uint8_t aEnergyListLength); static void HandlePanIdConflict(uint16_t aPanId, uint32_t aChannelMask, void *aContext); diff --git a/src/cli/cli_config.h b/src/cli/cli_config.h index 36aedc6443e..4e03052594c 100644 --- a/src/cli/cli_config.h +++ b/src/cli/cli_config.h @@ -87,6 +87,16 @@ #define OPENTHREAD_CONFIG_CLI_TCP_RECEIVE_BUFFER_SIZE OT_TCP_RECEIVE_BUFFER_SIZE_FEW_HOPS #endif +/** + * @def OPENTHREAD_CONFIG_CLI_MAX_USER_CMD_ENTRIES + * + * The maximum number of user CLI command lists that can be registered by the interpreter. + * + */ +#ifndef OPENTHREAD_CONFIG_CLI_MAX_USER_CMD_ENTRIES +#define OPENTHREAD_CONFIG_CLI_MAX_USER_CMD_ENTRIES 1 +#endif + /** * @def OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE * @@ -101,6 +111,18 @@ (OPENTHREAD_POSIX && (OPENTHREAD_CONFIG_LOG_OUTPUT != OPENTHREAD_CONFIG_LOG_OUTPUT_APP)) #endif +/** + * @def OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL + * + * Defines the log level to use when CLI emits its command input/output to the logs. + * + * This is used only when `OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE` is enabled. + * + */ +#ifndef OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL +#define OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL OT_LOG_LEVEL_DEBG +#endif + /** * @def OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LOG_STRING_SIZE * @@ -126,4 +148,30 @@ #define OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE 1 #endif +/** + * @def OPENTHREAD_CONFIG_CLI_TXT_RECORD_MAX_SIZE + * + * Specifies the max TXT record data length to use when performing DNS queries. + * + * If the service TXT record data length is greater than the specified value, it will be read partially (up to the given + * size) and output as a sequence of raw hex bytes `[{hex-bytes}...]` + * + */ +#ifndef OPENTHREAD_CONFIG_CLI_TXT_RECORD_MAX_SIZE +#define OPENTHREAD_CONFIG_CLI_TXT_RECORD_MAX_SIZE 512 +#endif + +/** + * @def OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK + * + * Define as 1 to have CLI register an IPv6 receive callback using `otIp6SetReceiveCallback()`. + * + * This is intended for testing only. Receive callback should be registered for the `otIp6GetBorderRoutingCounters()` + * to count the messages being passed to the callback. + * + */ +#ifndef OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK +#define OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK 0 +#endif + #endif // CONFIG_CLI_H_ diff --git a/src/cli/cli_dataset.cpp b/src/cli/cli_dataset.cpp index 573436aea67..633fa5b1226 100644 --- a/src/cli/cli_dataset.cpp +++ b/src/cli/cli_dataset.cpp @@ -45,130 +45,182 @@ namespace ot { namespace Cli { -otOperationalDataset Dataset::sDataset; +otOperationalDatasetTlvs Dataset::sDatasetTlvs; -otError Dataset::Print(otOperationalDataset &aDataset) +otError Dataset::Print(otOperationalDatasetTlvs &aDatasetTlvs) { - if (aDataset.mComponents.mIsPendingTimestampPresent) + otError error; + otOperationalDataset dataset; + + SuccessOrExit(error = otDatasetParseTlvs(&aDatasetTlvs, &dataset)); + + if (dataset.mComponents.mIsPendingTimestampPresent) { - OutputLine("Pending Timestamp: %lu", aDataset.mPendingTimestamp.mSeconds); + OutputFormat("Pending Timestamp: "); + OutputUint64Line(dataset.mPendingTimestamp.mSeconds); } - if (aDataset.mComponents.mIsActiveTimestampPresent) + if (dataset.mComponents.mIsActiveTimestampPresent) { - OutputLine("Active Timestamp: %lu", aDataset.mActiveTimestamp.mSeconds); + OutputFormat("Active Timestamp: "); + OutputUint64Line(dataset.mActiveTimestamp.mSeconds); } - if (aDataset.mComponents.mIsChannelPresent) + if (dataset.mComponents.mIsChannelPresent) { - OutputLine("Channel: %d", aDataset.mChannel); + OutputLine("Channel: %d", dataset.mChannel); } - if (aDataset.mComponents.mIsChannelMaskPresent) + if (dataset.mComponents.mIsChannelMaskPresent) { - OutputLine("Channel Mask: 0x%08x", aDataset.mChannelMask); + OutputLine("Channel Mask: 0x%08lx", ToUlong(dataset.mChannelMask)); } - if (aDataset.mComponents.mIsDelayPresent) + if (dataset.mComponents.mIsDelayPresent) { - OutputLine("Delay: %d", aDataset.mDelay); + OutputLine("Delay: %lu", ToUlong(dataset.mDelay)); } - if (aDataset.mComponents.mIsExtendedPanIdPresent) + if (dataset.mComponents.mIsExtendedPanIdPresent) { OutputFormat("Ext PAN ID: "); - OutputBytesLine(aDataset.mExtendedPanId.m8); + OutputBytesLine(dataset.mExtendedPanId.m8); } - if (aDataset.mComponents.mIsMeshLocalPrefixPresent) + if (dataset.mComponents.mIsMeshLocalPrefixPresent) { OutputFormat("Mesh Local Prefix: "); - OutputIp6PrefixLine(aDataset.mMeshLocalPrefix); + OutputIp6PrefixLine(dataset.mMeshLocalPrefix); } - if (aDataset.mComponents.mIsNetworkKeyPresent) + if (dataset.mComponents.mIsNetworkKeyPresent) { OutputFormat("Network Key: "); - OutputBytesLine(aDataset.mNetworkKey.m8); + OutputBytesLine(dataset.mNetworkKey.m8); } - if (aDataset.mComponents.mIsNetworkNamePresent) + if (dataset.mComponents.mIsNetworkNamePresent) { OutputFormat("Network Name: "); - OutputLine("%s", aDataset.mNetworkName.m8); + OutputLine("%s", dataset.mNetworkName.m8); } - if (aDataset.mComponents.mIsPanIdPresent) + if (dataset.mComponents.mIsPanIdPresent) { - OutputLine("PAN ID: 0x%04x", aDataset.mPanId); + OutputLine("PAN ID: 0x%04x", dataset.mPanId); } - if (aDataset.mComponents.mIsPskcPresent) + if (dataset.mComponents.mIsPskcPresent) { OutputFormat("PSKc: "); - OutputBytesLine(aDataset.mPskc.m8); + OutputBytesLine(dataset.mPskc.m8); } - if (aDataset.mComponents.mIsSecurityPolicyPresent) + if (dataset.mComponents.mIsSecurityPolicyPresent) { OutputFormat("Security Policy: "); - OutputSecurityPolicy(aDataset.mSecurityPolicy); + OutputSecurityPolicy(dataset.mSecurityPolicy); } - return OT_ERROR_NONE; +exit: + return error; } +/** + * @cli dataset init (active,new,pending,tlvs) + * @code + * dataset init new + * Done + * @endcode + * @cparam dataset init {@ca{active}|@ca{new}|@ca{pending}|@ca{tlvs}} [@ca{hex-encoded-tlvs}] + * Use `new` to initialize a new dataset, then enter the command `dataset commit active`. + * Use `tlvs` for hex-encoded TLVs. + * @par + * OT CLI checks for `active`, `pending`, or `tlvs` and returns the corresponding values. Otherwise, + * OT CLI creates a new, random network and returns a new dataset. + * @csa{dataset commit active} + * @csa{dataset active} + */ template <> otError Dataset::Process(Arg aArgs[]) { otError error = OT_ERROR_INVALID_ARGS; if (aArgs[0] == "active") { - error = otDatasetGetActive(GetInstancePtr(), &sDataset); + error = otDatasetGetActiveTlvs(GetInstancePtr(), &sDatasetTlvs); } else if (aArgs[0] == "pending") { - error = otDatasetGetPending(GetInstancePtr(), &sDataset); + error = otDatasetGetPendingTlvs(GetInstancePtr(), &sDatasetTlvs); } #if OPENTHREAD_FTD else if (aArgs[0] == "new") { - error = otDatasetCreateNewNetwork(GetInstancePtr(), &sDataset); + otOperationalDataset dataset; + + SuccessOrExit(error = otDatasetCreateNewNetwork(GetInstancePtr(), &dataset)); + SuccessOrExit(error = otDatasetConvertToTlvs(&dataset, &sDatasetTlvs)); } #endif else if (aArgs[0] == "tlvs") { - otOperationalDatasetTlvs datasetTlvs; - uint16_t size = sizeof(datasetTlvs.mTlvs); - - SuccessOrExit(error = aArgs[1].ParseAsHexString(size, datasetTlvs.mTlvs)); - datasetTlvs.mLength = static_cast(size); + uint16_t size = sizeof(sDatasetTlvs.mTlvs); - SuccessOrExit(error = otDatasetParseTlvs(&datasetTlvs, &sDataset)); + SuccessOrExit(error = aArgs[1].ParseAsHexString(size, sDatasetTlvs.mTlvs)); + sDatasetTlvs.mLength = static_cast(size); } exit: return error; } +/** + * @cli dataset active + * @code + * dataset active + * Active Timestamp: 1 + * Channel: 13 + * Channel Mask: 0x07fff800 + * Ext PAN ID: d63e8e3e495ebbc3 + * Mesh Local Prefix: fd3d:b50b:f96d:722d::/64 + * Network Key: dfd34f0f05cad978ec4e32b0413038ff + * Network Name: OpenThread-8f28 + * PAN ID: 0x8f28 + * PSKc: c23a76e98f1a6483639b1ac1271e2e27 + * Security Policy: 0, onrcb + * Done + * @endcode + * @code + * dataset active -x + * 0e08000000000001000000030000103506000...3023d82c841eff0e68db86f35740c030000ff + * Done + * @endcode + * @cparam dataset active [-x] + * The optional `-x` argument prints the Active Operational %Dataset values as hex-encoded TLVs. + * @par api_copy + * #otDatasetGetActive + * @par + * OT CLI uses #otOperationalDataset members to return dataset values to the console. + */ template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_INVALID_ARGS; + otError error; + otOperationalDatasetTlvs dataset; + + SuccessOrExit(error = otDatasetGetActiveTlvs(GetInstancePtr(), &dataset)); if (aArgs[0].IsEmpty()) { - otOperationalDataset dataset; - - SuccessOrExit(error = otDatasetGetActive(GetInstancePtr(), &dataset)); error = Print(dataset); } else if (aArgs[0] == "-x") { - otOperationalDatasetTlvs dataset; - - SuccessOrExit(error = otDatasetGetActiveTlvs(GetInstancePtr(), &dataset)); OutputBytesLine(dataset.mTlvs, dataset.mLength); } + else + { + error = OT_ERROR_INVALID_ARGS; + } exit: return error; @@ -176,97 +228,167 @@ template <> otError Dataset::Process(Arg aArgs[]) template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_INVALID_ARGS; + otError error; + otOperationalDatasetTlvs datasetTlvs; + + SuccessOrExit(error = otDatasetGetPendingTlvs(GetInstancePtr(), &datasetTlvs)); if (aArgs[0].IsEmpty()) { - otOperationalDataset dataset; - - SuccessOrExit(error = otDatasetGetPending(GetInstancePtr(), &dataset)); - error = Print(dataset); + error = Print(datasetTlvs); } else if (aArgs[0] == "-x") { - otOperationalDatasetTlvs dataset; - - SuccessOrExit(error = otDatasetGetPendingTlvs(GetInstancePtr(), &dataset)); - OutputBytesLine(dataset.mTlvs, dataset.mLength); + OutputBytesLine(datasetTlvs.mTlvs, datasetTlvs.mLength); + } + else + { + error = OT_ERROR_INVALID_ARGS; } exit: return error; } +/** + * @cli dataset activetimestamp (get, set) + * @code + * dataset activetimestamp + * 123456789 + * Done + * @endcode + * @code + * dataset activetimestamp 123456789 + * Done + * @endcode + * @cparam dataset activetimestamp [@ca{timestamp}] + * Pass the optional `timestamp` argument to set the active timestamp. + * @par + * Gets or sets #otOperationalDataset::mActiveTimestamp. + */ template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; if (aArgs[0].IsEmpty()) { - if (sDataset.mComponents.mIsActiveTimestampPresent) + SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset)); + if (dataset.mComponents.mIsActiveTimestampPresent) { - OutputLine("%lu", sDataset.mActiveTimestamp.mSeconds); + OutputUint64Line(dataset.mActiveTimestamp.mSeconds); } } else { - SuccessOrExit(error = aArgs[0].ParseAsUint64(sDataset.mActiveTimestamp.mSeconds)); - sDataset.mActiveTimestamp.mTicks = 0; - sDataset.mActiveTimestamp.mAuthoritative = false; - sDataset.mComponents.mIsActiveTimestampPresent = true; + memset(&dataset, 0, sizeof(dataset)); + SuccessOrExit(error = aArgs[0].ParseAsUint64(dataset.mActiveTimestamp.mSeconds)); + dataset.mActiveTimestamp.mTicks = 0; + dataset.mActiveTimestamp.mAuthoritative = false; + dataset.mComponents.mIsActiveTimestampPresent = true; + SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs)); } exit: return error; } +/** + * @cli dataset channel (get,set) + * @code + * dataset channel + * 12 + * Done + * @endcode + * @code + * dataset channel 12 + * Done + * @endcode + * @cparam dataset channel [@ca{channel-num}] + * Use the optional `channel-num` argument to set the channel. + * @par + * Gets or sets #otOperationalDataset::mChannel. + */ template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; if (aArgs[0].IsEmpty()) { - if (sDataset.mComponents.mIsChannelPresent) + SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset)); + if (dataset.mComponents.mIsChannelPresent) { - OutputLine("%d", sDataset.mChannel); + OutputLine("%d", dataset.mChannel); } } else { - SuccessOrExit(error = aArgs[0].ParseAsUint16(sDataset.mChannel)); - sDataset.mComponents.mIsChannelPresent = true; + memset(&dataset, 0, sizeof(dataset)); + SuccessOrExit(error = aArgs[0].ParseAsUint16(dataset.mChannel)); + dataset.mComponents.mIsChannelPresent = true; + SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs)); } exit: return error; } +/** + * @cli dataset channelmask (get,set) + * @code + * dataset channelmask + * 0x07fff800 + * Done + * @endcode + * @code + * dataset channelmask 0x07fff800 + * Done + * @endcode + * @cparam dataset channelmask [@ca{channel-mask}] + * Use the optional `channel-mask` argument to set the channel mask. + * @par + * Gets or sets #otOperationalDataset::mChannelMask + */ template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; if (aArgs[0].IsEmpty()) { - if (sDataset.mComponents.mIsChannelMaskPresent) + SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset)); + if (dataset.mComponents.mIsChannelMaskPresent) { - OutputLine("0x%08x", sDataset.mChannelMask); + OutputLine("0x%08lx", ToUlong(dataset.mChannelMask)); } } else { - SuccessOrExit(error = aArgs[0].ParseAsUint32(sDataset.mChannelMask)); - sDataset.mComponents.mIsChannelMaskPresent = true; + memset(&dataset, 0, sizeof(dataset)); + SuccessOrExit(error = aArgs[0].ParseAsUint32(dataset.mChannelMask)); + dataset.mComponents.mIsChannelMaskPresent = true; + SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs)); } exit: return error; } +/** + * @cli dataset clear + * @code + * dataset clear + * Done + * @endcode + * @par + * Reset the Operational %Dataset buffer. + */ template <> otError Dataset::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); - memset(&sDataset, 0, sizeof(sDataset)); + memset(&sDatasetTlvs, 0, sizeof(sDatasetTlvs)); return OT_ERROR_NONE; } @@ -274,70 +396,154 @@ template <> otError Dataset::Process(Arg aArgs[]) { otError error = OT_ERROR_INVALID_ARGS; + /** + * @cli dataset commit active + * @code + * dataset commit active + * Done + * @endcode + * @par + * Commit the Operational %Dataset buffer to Active Operational %Dataset. + * @csa{dataset commit pending} + * @sa #otDatasetSetPending + */ if (aArgs[0] == "active") { - error = otDatasetSetActive(GetInstancePtr(), &sDataset); - } + error = otDatasetSetActiveTlvs(GetInstancePtr(), &sDatasetTlvs); + } + /** + * @cli dataset commit pending + * @code + * dataset commit pending + * Done + * @endcode + * @par + * Commit the Operational %Dataset buffer to Pending Operational %Dataset. + * @csa{dataset commit active} + * @sa #otDatasetSetActive + */ else if (aArgs[0] == "pending") { - error = otDatasetSetPending(GetInstancePtr(), &sDataset); + error = otDatasetSetPendingTlvs(GetInstancePtr(), &sDatasetTlvs); } return error; } +/** + * @cli dataset delay (get,set) + * @code + * dataset delay + * 1000 + * Done + * @endcode + * @code + * dataset delay 1000 + * Done + * @endcode + * @cparam dataset delay [@ca{delay}] + * Use the optional `delay` argument to set the delay timer value. + * @par + * Gets or sets #otOperationalDataset::mDelay. + * @sa otDatasetSetDelayTimerMinimal + */ template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; if (aArgs[0].IsEmpty()) { - if (sDataset.mComponents.mIsDelayPresent) + SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset)); + if (dataset.mComponents.mIsDelayPresent) { - OutputLine("%d", sDataset.mDelay); + OutputLine("%lu", ToUlong(dataset.mDelay)); } } else { - SuccessOrExit(error = aArgs[0].ParseAsUint32(sDataset.mDelay)); - sDataset.mComponents.mIsDelayPresent = true; + memset(&dataset, 0, sizeof(dataset)); + SuccessOrExit(error = aArgs[0].ParseAsUint32(dataset.mDelay)); + dataset.mComponents.mIsDelayPresent = true; + SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs)); } exit: return error; } +/** + * @cli dataset extpanid (get,set) + * @code + * dataset extpanid + * 000db80123456789 + * Done + * @endcode + * @code + * dataset extpanid 000db80123456789 + * Done + * @endcode + * @cparam dataset extpanid [@ca{extpanid}] + * Use the optional `extpanid` argument to set the Extended Personal Area Network ID. + * @par + * Gets or sets #otOperationalDataset::mExtendedPanId. + * @note The commissioning credential in the dataset buffer becomes stale after changing + * this value. Use `dataset pskc` to reset. + * @csa{dataset pskc (get,set)} + */ template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; if (aArgs[0].IsEmpty()) { - if (sDataset.mComponents.mIsExtendedPanIdPresent) + SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset)); + if (dataset.mComponents.mIsExtendedPanIdPresent) { - OutputBytesLine(sDataset.mExtendedPanId.m8); + OutputBytesLine(dataset.mExtendedPanId.m8); } } else { - SuccessOrExit(error = aArgs[0].ParseAsHexString(sDataset.mExtendedPanId.m8)); - sDataset.mComponents.mIsExtendedPanIdPresent = true; + memset(&dataset, 0, sizeof(dataset)); + SuccessOrExit(error = aArgs[0].ParseAsHexString(dataset.mExtendedPanId.m8)); + dataset.mComponents.mIsExtendedPanIdPresent = true; + SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs)); } exit: return error; } +/** + * @cli dataset meshlocalprefix (get,set) + * @code + * dataset meshlocalprefix + * fd00:db8:0:0::/64 + * Done + * @endcode + * @code + * dataset meshlocalprefix fd00:db8:0:0::/64 + * Done + * @endcode + * @cparam dataset meshlocalprefix [@ca{meshlocalprefix}] + * Use the optional `meshlocalprefix` argument to set the Mesh-Local Prefix. + * @par + * Gets or sets #otOperationalDataset::mMeshLocalPrefix. + */ template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; if (aArgs[0].IsEmpty()) { - if (sDataset.mComponents.mIsMeshLocalPrefixPresent) + SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset)); + if (dataset.mComponents.mIsMeshLocalPrefixPresent) { OutputFormat("Mesh Local Prefix: "); - OutputIp6PrefixLine(sDataset.mMeshLocalPrefix); + OutputIp6PrefixLine(dataset.mMeshLocalPrefix); } } else @@ -346,94 +552,179 @@ template <> otError Dataset::Process(Arg aArgs[]) SuccessOrExit(error = aArgs[0].ParseAsIp6Address(prefix)); - memcpy(sDataset.mMeshLocalPrefix.m8, prefix.mFields.m8, sizeof(sDataset.mMeshLocalPrefix.m8)); - sDataset.mComponents.mIsMeshLocalPrefixPresent = true; + memset(&dataset, 0, sizeof(dataset)); + memcpy(dataset.mMeshLocalPrefix.m8, prefix.mFields.m8, sizeof(dataset.mMeshLocalPrefix.m8)); + dataset.mComponents.mIsMeshLocalPrefixPresent = true; + SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs)); } exit: return error; } +/** + * @cli dataset networkkey (get,set) + * @code + * dataset networkkey + * 00112233445566778899aabbccddeeff + * Done + * @endcode + * @code + * dataset networkkey 00112233445566778899aabbccddeeff + * Done + * @endcode + * @cparam dataset networkkey [@ca{key}] + * Use the optional `key` argument to set the Network Key. + * @par + * Gets or sets #otOperationalDataset::mNetworkKey. + */ template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; if (aArgs[0].IsEmpty()) { - if (sDataset.mComponents.mIsNetworkKeyPresent) + SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset)); + if (dataset.mComponents.mIsNetworkKeyPresent) { - OutputBytesLine(sDataset.mNetworkKey.m8); + OutputBytesLine(dataset.mNetworkKey.m8); } } else { - SuccessOrExit(error = aArgs[0].ParseAsHexString(sDataset.mNetworkKey.m8)); - sDataset.mComponents.mIsNetworkKeyPresent = true; + memset(&dataset, 0, sizeof(dataset)); + SuccessOrExit(error = aArgs[0].ParseAsHexString(dataset.mNetworkKey.m8)); + dataset.mComponents.mIsNetworkKeyPresent = true; + SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs)); } exit: return error; } +/** + * @cli dataset networkname (get,set) + * @code + * dataset networkname + * OpenThread + * Done + * @endcode + * @code + * dataset networkname OpenThread + * Done + * @endcode + * @cparam dataset networkname [@ca{name}] + * Use the optional `name` argument to set the Network Name. + * @par + * Gets or sets #otOperationalDataset::mNetworkName. + * @note The Commissioning Credential in the dataset buffer becomes stale after changing this value. + * Use `dataset pskc` to reset. + * @csa{dataset pskc (get,set)} + */ template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; if (aArgs[0].IsEmpty()) { - if (sDataset.mComponents.mIsNetworkNamePresent) + SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset)); + if (dataset.mComponents.mIsNetworkNamePresent) { - OutputLine("%s", sDataset.mNetworkName.m8); + OutputLine("%s", dataset.mNetworkName.m8); } } else { - SuccessOrExit(error = otNetworkNameFromString(&sDataset.mNetworkName, aArgs[0].GetCString())); - sDataset.mComponents.mIsNetworkNamePresent = true; + memset(&dataset, 0, sizeof(dataset)); + SuccessOrExit(error = otNetworkNameFromString(&dataset.mNetworkName, aArgs[0].GetCString())); + dataset.mComponents.mIsNetworkNamePresent = true; + SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs)); } exit: return error; } +/** + * @cli dataset panid (get,set) + * @code + * dataset panid + * 0x1234 + * Done + * @endcode + * @code + * dataset panid 0x1234 + * Done + * @endcode + * @cparam dataset panid [@ca{panid}] + * Use the optional `panid` argument to set the PAN ID. + * @par + * Gets or sets #otOperationalDataset::mPanId. + */ template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; if (aArgs[0].IsEmpty()) { - if (sDataset.mComponents.mIsPanIdPresent) + SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset)); + if (dataset.mComponents.mIsPanIdPresent) { - OutputLine("0x%04x", sDataset.mPanId); + OutputLine("0x%04x", dataset.mPanId); } } else { - SuccessOrExit(error = aArgs[0].ParseAsUint16(sDataset.mPanId)); - sDataset.mComponents.mIsPanIdPresent = true; + memset(&dataset, 0, sizeof(dataset)); + SuccessOrExit(error = aArgs[0].ParseAsUint16(dataset.mPanId)); + dataset.mComponents.mIsPanIdPresent = true; + SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs)); } exit: return error; } +/** + * @cli dataset pendingtimestamp (get,set) + * @code + * dataset pendingtimestamp + * 123456789 + * Done + * @endcode + * @code + * dataset pendingtimestamp 123456789 + * Done + * @endcode + * @cparam dataset pendingtimestamp [@ca{timestamp}] + * Use the optional `timestamp` argument to set the pending timestamp seconds. + * @par + * Gets or sets #otOperationalDataset::mPendingTimestamp. + */ template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; if (aArgs[0].IsEmpty()) { - if (sDataset.mComponents.mIsPendingTimestampPresent) + SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset)); + if (dataset.mComponents.mIsPendingTimestampPresent) { - OutputLine("%lu", sDataset.mPendingTimestamp.mSeconds); + OutputUint64Line(dataset.mPendingTimestamp.mSeconds); } } else { - SuccessOrExit(error = aArgs[0].ParseAsUint64(sDataset.mPendingTimestamp.mSeconds)); - sDataset.mPendingTimestamp.mTicks = 0; - sDataset.mPendingTimestamp.mAuthoritative = false; - sDataset.mComponents.mIsPendingTimestampPresent = true; + memset(&dataset, 0, sizeof(dataset)); + SuccessOrExit(error = aArgs[0].ParseAsUint64(dataset.mPendingTimestamp.mSeconds)); + dataset.mPendingTimestamp.mTicks = 0; + dataset.mPendingTimestamp.mAuthoritative = false; + dataset.mComponents.mIsPendingTimestampPresent = true; + SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs)); } exit: @@ -540,11 +831,43 @@ template <> otError Dataset::Process(Arg aArgs[]) } } + /** + * @cli dataset mgmtsetcommand active + * @code + * dataset mgmtsetcommand active activetimestamp 123 securitypolicy 1 onrcb + * Done + * @endcode + * @cparam dataset mgmtsetcommand active [@ca{dataset-components}] [-x @ca{tlv-list}] + * To learn more about these parameters and argument mappings, refer to @dataset. + * @par + * @note This command is primarily used for testing only. + * @par api_copy + * #otDatasetSendMgmtActiveSet + * @csa{dataset mgmtgetcommand active} + * @csa{dataset mgmtgetcommand pending} + * @csa{dataset mgmtsetcommand pending} + */ if (aArgs[0] == "active") { error = otDatasetSendMgmtActiveSet(GetInstancePtr(), &dataset, tlvs, tlvsLength, /* aCallback */ nullptr, /* aContext */ nullptr); } + /** + * @cli dataset mgmtsetcommand pending + * @code + * dataset mgmtsetcommand pending activetimestamp 123 securitypolicy 1 onrcb + * Done + * @endcode + * @cparam dataset mgmtsetcommand pending [@ca{dataset-components}] [-x @ca{tlv-list}] + * To learn more about these parameters and argument mappings, refer to @dataset. + * @par + * @note This command is primarily used for testing only. + * @par api_copy + * #otDatasetSendMgmtPendingSet + * @csa{dataset mgmtgetcommand active} + * @csa{dataset mgmtgetcommand pending} + * @csa{dataset mgmtsetcommand active} + */ else if (aArgs[0] == "pending") { error = otDatasetSendMgmtPendingSet(GetInstancePtr(), &dataset, tlvs, tlvsLength, /* aCallback */ nullptr, @@ -633,11 +956,56 @@ template <> otError Dataset::Process(Arg aArgs[]) } } + /** + * @cli dataset mgmtgetcommand active + * @code + * dataset mgmtgetcommand active address fdde:ad00:beef:0:558:f56b:d688:799 activetimestamp securitypolicy + * Done + * @endcode + * @code + * dataset mgmtgetcommand active networkname + * Done + * @endcode + * @cparam dataset mgmtgetcommand active [address @ca{leader-address}] [@ca{dataset-components}] [-x @ca{tlv-list}] + * * Use `address` to specify the IPv6 destination; otherwise, the Leader ALOC is used as default. + * * For `dataset-components`, you can pass any combination of #otOperationalDatasetComponents, for + * example `activetimestamp`, `pendingtimestamp`, or `networkkey`. + * * The optional `-x` argument specifies raw TLVs to be requested. + * @par + * OT CLI sends a MGMT_ACTIVE_GET with the relevant arguments. + * To learn more about these parameters and argument mappings, refer to @dataset. + * @note This command is primarily used for testing only. + * @par api_copy + * #otDatasetSendMgmtActiveGet + * @csa{dataset mgmtgetcommand pending} + * @csa{dataset mgmtsetcommand active} + * @csa{dataset mgmtsetcommand pending} + */ if (aArgs[0] == "active") { error = otDatasetSendMgmtActiveGet(GetInstancePtr(), &datasetComponents, tlvs, tlvsLength, destAddrSpecified ? &address : nullptr); } + /** + * @cli dataset mgmtgetcommand pending + * @code + * dataset mgmtgetcommand pending address fdde:ad00:beef:0:558:f56b:d688:799 activetimestamp securitypolicy + * Done + * @endcode + * @code + * dataset mgmtgetcommand pending networkname + * Done + * @endcode + * @cparam dataset mgmtgetcommand pending [address @ca{leader-address}] [@ca{dataset-components}] [-x @ca{tlv-list}] + * To learn more about these parameters and argument mappings, refer to @dataset. + * @par + * @note This command is primarily used for testing only. + * @par api_copy + * #otDatasetSendMgmtPendingGet + * @csa{dataset mgmtgetcommand active} + * @csa{dataset mgmtsetcommand active} + * @csa{dataset mgmtsetcommand pending} + */ else if (aArgs[0] == "pending") { error = otDatasetSendMgmtPendingGet(GetInstancePtr(), &datasetComponents, tlvs, tlvsLength, @@ -652,21 +1020,48 @@ template <> otError Dataset::Process(Arg aArgs[]) return error; } +/** + * @cli dataset pskc (get,set) + * @code + * dataset pskc + * 67c0c203aa0b042bfb5381c47aef4d9e + * Done + * @endcode + * @code + * dataset pskc -p 123456 + * Done + * @endcode + * @code + * dataset pskc 67c0c203aa0b042bfb5381c47aef4d9e + * Done + * @endcode + * @cparam dataset pskc [@ca{-p} @ca{passphrase}] | [@ca{key}] + * For FTD only, use `-p` with the `passphrase` argument. `-p` generates a pskc from + * the UTF-8 encoded `passphrase` that you provide, together with + * the network name and extended PAN ID. If set, `-p` uses the dataset buffer; + * otherwise, it uses the current stack. + * Alternatively, you can set pskc as `key` (hex format). + * @par + * Gets or sets #otOperationalDataset::mPskc. + */ template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; if (aArgs[0].IsEmpty()) { - // sDataset holds the key as a literal string, we don't + SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset)); + // dataset holds the key as a literal string, we don't // need to export it from PSA ITS. - if (sDataset.mComponents.mIsPskcPresent) + if (dataset.mComponents.mIsPskcPresent) { - OutputBytesLine(sDataset.mPskc.m8); + OutputBytesLine(dataset.mPskc.m8); } } else { + memset(&dataset, 0, sizeof(dataset)); #if OPENTHREAD_FTD if (aArgs[0] == "-p") { @@ -675,20 +1070,21 @@ template <> otError Dataset::Process(Arg aArgs[]) SuccessOrExit( error = otDatasetGeneratePskc( aArgs[1].GetCString(), - (sDataset.mComponents.mIsNetworkNamePresent - ? &sDataset.mNetworkName + (dataset.mComponents.mIsNetworkNamePresent + ? &dataset.mNetworkName : reinterpret_cast(otThreadGetNetworkName(GetInstancePtr()))), - (sDataset.mComponents.mIsExtendedPanIdPresent ? &sDataset.mExtendedPanId - : otThreadGetExtendedPanId(GetInstancePtr())), - &sDataset.mPskc)); + (dataset.mComponents.mIsExtendedPanIdPresent ? &dataset.mExtendedPanId + : otThreadGetExtendedPanId(GetInstancePtr())), + &dataset.mPskc)); } else #endif { - SuccessOrExit(error = aArgs[0].ParseAsHexString(sDataset.mPskc.m8)); + SuccessOrExit(error = aArgs[0].ParseAsHexString(dataset.mPskc.m8)); } - sDataset.mComponents.mIsPskcPresent = true; + dataset.mComponents.mIsPskcPresent = true; + SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs)); } exit: @@ -739,7 +1135,7 @@ void Dataset::OutputSecurityPolicy(const otSecurityPolicy &aSecurityPolicy) OutputFormat("R"); } - OutputLine(""); + OutputNewLine(); } otError Dataset::ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, Arg *&aArgs) @@ -806,29 +1202,69 @@ otError Dataset::ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, Arg *&aA return error; } +/** + * @cli dataset securitypolicy (get,set) + * @code + * dataset securitypolicy + * 672 onrc + * Done + * @endcode + * @code + * dataset securitypolicy 672 onrc + * Done + * @endcode + * @cparam dataset securitypolicy [@ca{rotationtime} [@ca{onrcCepR}]] + * * Use `rotationtime` for `thrKeyRotation`, in units of hours. + * * Security Policy commands use the `onrcCepR` argument mappings to get and set + * #otSecurityPolicy members, for example `o` represents + * #otSecurityPolicy::mObtainNetworkKeyEnabled. + * @moreinfo{@dataset}. + * @par + * Gets or sets the %Dataset security policy. + */ template <> otError Dataset::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; + otError error = OT_ERROR_NONE; + otOperationalDataset dataset; if (aArgs[0].IsEmpty()) { - if (sDataset.mComponents.mIsSecurityPolicyPresent) + SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset)); + if (dataset.mComponents.mIsSecurityPolicyPresent) { - OutputSecurityPolicy(sDataset.mSecurityPolicy); + OutputSecurityPolicy(dataset.mSecurityPolicy); } } else { Arg *arg = &aArgs[0]; - SuccessOrExit(error = ParseSecurityPolicy(sDataset.mSecurityPolicy, arg)); - sDataset.mComponents.mIsSecurityPolicyPresent = true; + memset(&dataset, 0, sizeof(dataset)); + SuccessOrExit(error = ParseSecurityPolicy(dataset.mSecurityPolicy, arg)); + dataset.mComponents.mIsSecurityPolicyPresent = true; + SuccessOrExit(error = otDatasetUpdateTlvs(&dataset, &sDatasetTlvs)); } exit: return error; } +/** + * @cli dataset set (active,pending) + * @code + * dataset set active 0e08000000000001000000030000103506000...3023d82c841eff0e68db86f35740c030000ff + * Done + * @endcode + * @code + * dataset set pending 0e08000000000001000000030000103506000...3023d82c841eff0e68db86f35740c030000ff + * Done + * @endcode + * @cparam dataset set {active|pending} @ca{tlvs} + * @par + * The CLI `dataset set` command sets the Active Operational %Dataset using hex-encoded TLVs. + * @par api_copy + * #otDatasetSetActive + */ template <> otError Dataset::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -848,22 +1284,22 @@ template <> otError Dataset::Process(Arg aArgs[]) } { - MeshCoP::Dataset dataset; - MeshCoP::Dataset::Info datasetInfo; - uint16_t tlvsLength = MeshCoP::Dataset::kMaxSize; + otOperationalDataset dataset; + otOperationalDatasetTlvs datasetTlvs; + uint16_t tlvsLength = MeshCoP::Dataset::kMaxSize; + + SuccessOrExit(error = aArgs[1].ParseAsHexString(tlvsLength, datasetTlvs.mTlvs)); + datasetTlvs.mLength = static_cast(tlvsLength); - SuccessOrExit(error = aArgs[1].ParseAsHexString(tlvsLength, dataset.GetBytes())); - dataset.SetSize(tlvsLength); - VerifyOrExit(dataset.IsValid(), error = OT_ERROR_INVALID_ARGS); - dataset.ConvertTo(datasetInfo); + SuccessOrExit(error = otDatasetParseTlvs(&datasetTlvs, &dataset)); switch (datasetType) { case MeshCoP::Dataset::Type::kActive: - SuccessOrExit(error = otDatasetSetActive(GetInstancePtr(), &datasetInfo)); + SuccessOrExit(error = otDatasetSetActiveTlvs(GetInstancePtr(), &datasetTlvs)); break; case MeshCoP::Dataset::Type::kPending: - SuccessOrExit(error = otDatasetSetPending(GetInstancePtr(), &datasetInfo)); + SuccessOrExit(error = otDatasetSetPendingTlvs(GetInstancePtr(), &datasetTlvs)); break; } } @@ -872,6 +1308,27 @@ template <> otError Dataset::Process(Arg aArgs[]) return error; } +/** + * @cli dataset tlvs + * @code + * dataset tlvs + * 0e080000000000010000000300001635060004001fffe0020...f7f8 + * Done + * @endcode + * @par api_copy + * #otDatasetConvertToTlvs + */ +template <> otError Dataset::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + OutputBytesLine(sDatasetTlvs.mTlvs, sDatasetTlvs.mLength); + +exit: + return error; +} + #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD template <> otError Dataset::Process(Arg aArgs[]) @@ -884,7 +1341,11 @@ template <> otError Dataset::Process(Arg aArgs[]) } else if (aArgs[0] == "start") { - error = otDatasetUpdaterRequestUpdate(GetInstancePtr(), &sDataset, &Dataset::HandleDatasetUpdater, this); + otOperationalDataset dataset; + + SuccessOrExit(error = otDatasetParseTlvs(&sDatasetTlvs, &dataset)); + SuccessOrExit( + error = otDatasetUpdaterRequestUpdate(GetInstancePtr(), &dataset, &Dataset::HandleDatasetUpdater, this)); } else if (aArgs[0] == "cancel") { @@ -895,6 +1356,7 @@ template <> otError Dataset::Process(Arg aArgs[]) error = OT_ERROR_INVALID_ARGS; } +exit: return error; } @@ -938,6 +1400,7 @@ otError Dataset::Process(Arg aArgs[]) CmdEntry("pskc"), CmdEntry("securitypolicy"), CmdEntry("set"), + CmdEntry("tlvs"), #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD CmdEntry("updater"), #endif @@ -952,9 +1415,40 @@ otError Dataset::Process(Arg aArgs[]) if (aArgs[0].IsEmpty()) { - ExitNow(error = Print(sDataset)); - } - + ExitNow(error = Print(sDatasetTlvs)); + } + + /** + * @cli dataset help + * @code + * dataset help + * help + * active + * activetimestamp + * channel + * channelmask + * clear + * commit + * delay + * extpanid + * init + * meshlocalprefix + * mgmtgetcommand + * mgmtsetcommand + * networkkey + * networkname + * panid + * pending + * pendingtimestamp + * pskc + * securitypolicy + * set + * tlvs + * Done + * @endcode + * @par + * Gets a list of `dataset` CLI commands. @moreinfo{@dataset}. + */ if (aArgs[0] == "help") { OutputCommandTable(kCommands); diff --git a/src/cli/cli_dataset.hpp b/src/cli/cli_dataset.hpp index 980bb5d108b..7f11ec3027a 100644 --- a/src/cli/cli_dataset.hpp +++ b/src/cli/cli_dataset.hpp @@ -49,13 +49,13 @@ namespace Cli { * This class implements the Dataset CLI interpreter. * */ -class Dataset : private OutputWrapper +class Dataset : private Output { public: typedef Utils::CmdLineParser::Arg Arg; - explicit Dataset(Output &aOutput) - : OutputWrapper(aOutput) + Dataset(otInstance *aInstance, OutputImplementer &aOutputImplementer) + : Output(aInstance, aOutputImplementer) { } @@ -72,7 +72,7 @@ class Dataset : private OutputWrapper template otError Process(Arg aArgs[]); - otError Print(otOperationalDataset &aDataset); + otError Print(otOperationalDatasetTlvs &aDatasetTlvs); #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE && OPENTHREAD_FTD otError ProcessUpdater(Arg aArgs[]); @@ -83,7 +83,7 @@ class Dataset : private OutputWrapper void OutputSecurityPolicy(const otSecurityPolicy &aSecurityPolicy); otError ParseSecurityPolicy(otSecurityPolicy &aSecurityPolicy, Arg *&aArgs); - static otOperationalDataset sDataset; + static otOperationalDatasetTlvs sDatasetTlvs; }; } // namespace Cli diff --git a/src/cli/cli_history.cpp b/src/cli/cli_history.cpp index 3dc856b0a50..0ea5ebf694e 100644 --- a/src/cli/cli_history.cpp +++ b/src/cli/cli_history.cpp @@ -43,7 +43,7 @@ namespace ot { namespace Cli { static const char *const kSimpleEventStrings[] = { - "Added", // (0) OT_HISTORY_TRACKER_{NET_DATA_ENTRY/ADDRESSS_EVENT}_ADDED + "Added", // (0) OT_HISTORY_TRACKER_{NET_DATA_ENTRY/ADDRESS_EVENT}_ADDED "Removed" // (1) OT_HISTORY_TRACKER_{NET_DATA_ENTRY/ADDRESS_EVENT}_REMOVED }; @@ -256,6 +256,92 @@ template <> otError History::Process(Arg aArgs[]) return error; } +template <> otError History::Process(Arg aArgs[]) +{ + static const char *const kEventString[] = { + /* (0) OT_HISTORY_TRACKER_ROUTER_EVENT_ADDED -> */ "Added", + /* (1) OT_HISTORY_TRACKER_ROUTER_EVENT_REMOVED -> */ "Removed", + /* (2) OT_HISTORY_TRACKER_ROUTER_EVENT_NEXT_HOP_CHANGED -> */ "NextHopChanged", + /* (3) OT_HISTORY_TRACKER_ROUTER_EVENT_COST_CHANGED -> */ "CostChanged", + }; + + constexpr uint8_t kRouterIdOffset = 10; // Bit offset of Router ID in RLOC16 + + otError error; + bool isList; + uint16_t numEntries; + otHistoryTrackerIterator iterator; + const otHistoryTrackerRouterInfo *info; + uint32_t entryAge; + char ageString[OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE]; + + static_assert(0 == OT_HISTORY_TRACKER_ROUTER_EVENT_ADDED, "EVENT_ADDED is incorrect"); + static_assert(1 == OT_HISTORY_TRACKER_ROUTER_EVENT_REMOVED, "EVENT_REMOVED is incorrect"); + static_assert(2 == OT_HISTORY_TRACKER_ROUTER_EVENT_NEXT_HOP_CHANGED, "EVENT_NEXT_HOP_CHANGED is incorrect"); + static_assert(3 == OT_HISTORY_TRACKER_ROUTER_EVENT_COST_CHANGED, "EVENT_COST_CHANGED is incorrect"); + + SuccessOrExit(error = ParseArgs(aArgs, isList, numEntries)); + + if (!isList) + { + // | Age | Event | ID (RlOC16) | Next Hop | Path Cost | + // +----------------------+----------------+-------------+------------+-------------+ + + static const char *const kRouterInfoTitles[] = { + "Age", "Event", "ID (RLOC16)", "Next Hop", "Path Cost", + }; + + static const uint8_t kRouterInfoColumnWidths[] = {22, 16, 13, 13, 12}; + + OutputTableHeader(kRouterInfoTitles, kRouterInfoColumnWidths); + } + + otHistoryTrackerInitIterator(&iterator); + + for (uint16_t index = 0; (numEntries == 0) || (index < numEntries); index++) + { + info = otHistoryTrackerIterateRouterHistory(GetInstancePtr(), &iterator, &entryAge); + VerifyOrExit(info != nullptr); + + otHistoryTrackerEntryAgeToString(entryAge, ageString, sizeof(ageString)); + + OutputFormat(isList ? "%s -> event:%s router:%u(0x%04x) nexthop:" : "| %20s | %-14s | %2u (0x%04x) | ", + ageString, kEventString[info->mEvent], info->mRouterId, + static_cast(info->mRouterId) << kRouterIdOffset); + + if (info->mNextHop != OT_HISTORY_TRACKER_NO_NEXT_HOP) + { + OutputFormat(isList ? "%u(0x%04x)" : "%2u (0x%04x)", info->mNextHop, + static_cast(info->mNextHop) << kRouterIdOffset); + } + else + { + OutputFormat(isList ? "%s" : "%11s", "none"); + } + + if (info->mOldPathCost != OT_HISTORY_TRACKER_INFINITE_PATH_COST) + { + OutputFormat(isList ? " old-cost:%u" : " | %3u ->", info->mOldPathCost); + } + else + { + OutputFormat(isList ? " old-cost:inf" : " | inf ->"); + } + + if (info->mPathCost != OT_HISTORY_TRACKER_INFINITE_PATH_COST) + { + OutputLine(isList ? " new-cost:%u" : " %3u |", info->mPathCost); + } + else + { + OutputLine(isList ? " new-cost:inf" : " inf |"); + } + } + +exit: + return error; +} + template <> otError History::Process(Arg aArgs[]) { otError error; @@ -299,20 +385,11 @@ template <> otError History::Process(Arg aArgs[]) return error; } -template <> otError History::Process(Arg aArgs[]) -{ - return ProcessRxTxHistory(kRx, aArgs); -} +template <> otError History::Process(Arg aArgs[]) { return ProcessRxTxHistory(kRx, aArgs); } -template <> otError History::Process(Arg aArgs[]) -{ - return ProcessRxTxHistory(kRxTx, aArgs); -} +template <> otError History::Process(Arg aArgs[]) { return ProcessRxTxHistory(kRxTx, aArgs); } -template <> otError History::Process(Arg aArgs[]) -{ - return ProcessRxTxHistory(kTx, aArgs); -} +template <> otError History::Process(Arg aArgs[]) { return ProcessRxTxHistory(kTx, aArgs); } const char *History::MessagePriorityToString(uint8_t aPriority) { @@ -668,7 +745,7 @@ otError History::Process(Arg aArgs[]) static constexpr Command kCommands[] = { CmdEntry("ipaddr"), CmdEntry("ipmaddr"), CmdEntry("neighbor"), CmdEntry("netinfo"), CmdEntry("prefix"), - CmdEntry("route"), CmdEntry("rx"), CmdEntry("rxtx"), CmdEntry("tx"), + CmdEntry("route"), CmdEntry("router"), CmdEntry("rx"), CmdEntry("rxtx"), CmdEntry("tx"), }; #undef CmdEntry diff --git a/src/cli/cli_history.hpp b/src/cli/cli_history.hpp index a17a667cda5..cab6d79eb4a 100644 --- a/src/cli/cli_history.hpp +++ b/src/cli/cli_history.hpp @@ -50,7 +50,7 @@ namespace Cli { * This class implements the History Tracker CLI interpreter. * */ -class History : private OutputWrapper +class History : private Output { public: typedef Utils::CmdLineParser::Arg Arg; @@ -58,11 +58,12 @@ class History : private OutputWrapper /** * Constructor * - * @param[in] aOutput The CLI console output context + * @param[in] aInstance The OpenThread Instance. + * @param[in] aOutputImplementer An `OutputImplementer`. * */ - explicit History(Output &aOutput) - : OutputWrapper(aOutput) + History(otInstance *aInstance, OutputImplementer &aOutputImplementer) + : Output(aInstance, aOutputImplementer) { } diff --git a/src/cli/cli_joiner.cpp b/src/cli/cli_joiner.cpp index 82d0d3765c0..cf84968edaf 100644 --- a/src/cli/cli_joiner.cpp +++ b/src/cli/cli_joiner.cpp @@ -62,7 +62,16 @@ template <> otError Joiner::Process(Arg aArgs[]) VerifyOrExit(discerner != nullptr, error = OT_ERROR_NOT_FOUND); - OutputLine("0x%llx/%u", static_cast(discerner->mValue), discerner->mLength); + if (discerner->mValue <= 0xffffffff) + { + OutputLine("0x%lx/%u", static_cast(discerner->mValue & 0xffffffff), discerner->mLength); + } + else + { + OutputLine("0x%lx%08lx/%u", static_cast(discerner->mValue >> 32), + static_cast(discerner->mValue & 0xffffffff), discerner->mLength); + } + error = OT_ERROR_NONE; } else @@ -252,10 +261,7 @@ otError Joiner::Process(Arg aArgs[]) return error; } -void Joiner::HandleCallback(otError aError, void *aContext) -{ - static_cast(aContext)->HandleCallback(aError); -} +void Joiner::HandleCallback(otError aError, void *aContext) { static_cast(aContext)->HandleCallback(aError); } void Joiner::HandleCallback(otError aError) { diff --git a/src/cli/cli_joiner.hpp b/src/cli/cli_joiner.hpp index d2bc640cb7d..cc3ddac2c55 100644 --- a/src/cli/cli_joiner.hpp +++ b/src/cli/cli_joiner.hpp @@ -49,7 +49,7 @@ namespace Cli { * This class implements the Joiner CLI interpreter. * */ -class Joiner : private OutputWrapper +class Joiner : private Output { public: typedef Utils::CmdLineParser::Arg Arg; @@ -57,11 +57,12 @@ class Joiner : private OutputWrapper /** * Constructor * - * @param[in] aOutput The CLI console output context + * @param[in] aInstance The OpenThread Instance. + * @param[in] aOutputImplementer An `OutputImplementer`. * */ - explicit Joiner(Output &aOutput) - : OutputWrapper(aOutput) + Joiner(otInstance *aInstance, OutputImplementer &aOutputImplementer) + : Output(aInstance, aOutputImplementer) { } diff --git a/src/cli/cli_network_data.cpp b/src/cli/cli_network_data.cpp index 1c3de9071d9..36fcf3de59b 100644 --- a/src/cli/cli_network_data.cpp +++ b/src/cli/cli_network_data.cpp @@ -146,7 +146,7 @@ void NetworkData::OutputRoute(const otExternalRouteConfig &aConfig) void NetworkData::OutputService(const otServiceConfig &aConfig) { - OutputFormat("%u ", aConfig.mEnterpriseNumber); + OutputFormat("%lu ", ToUlong(aConfig.mEnterpriseNumber)); OutputBytes(aConfig.mServiceData, aConfig.mServiceDataLength); OutputFormat(" "); OutputBytes(aConfig.mServerConfig.mServerData, aConfig.mServerConfig.mServerDataLength); @@ -159,6 +159,66 @@ void NetworkData::OutputService(const otServiceConfig &aConfig) OutputLine(" %04x", aConfig.mServerConfig.mRloc16); } +/** + * @cli netdata length + * @code + * netdata length + * 23 + * Done + * @endcode + * @par api_copy + * #otNetDataGetLength + */ +template <> otError NetworkData::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + OutputLine("%u", otNetDataGetLength(GetInstancePtr())); + +exit: + return error; +} + +template <> otError NetworkData::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + /** + * @cli netdata maxlength + * @code + * netdata maxlength + * 40 + * Done + * @endcode + * @par api_copy + * #otNetDataGetMaxLength + */ + if (aArgs[0].IsEmpty()) + { + OutputLine("%u", otNetDataGetMaxLength(GetInstancePtr())); + } + /** + * @cli netdata maxlength reset + * @code + * netdata maxlength reset + * Done + * @endcode + * @par api_copy + * #otNetDataResetMaxLength + */ + else if (aArgs[0] == "reset") + { + otNetDataResetMaxLength(GetInstancePtr()); + } + else + { + error = OT_ERROR_INVALID_ARGS; + } + + return error; +} + #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE template <> otError NetworkData::Process(Arg aArgs[]) { @@ -294,6 +354,29 @@ template <> otError NetworkData::Process(Arg aArgs[]) error = otNetDataPublishExternalRoute(GetInstancePtr(), &config); ExitNow(); } + + /** + * @cli netdata publish replace + * @code + * netdata publish replace ::/0 fd00:1234:5678::/64 s high + * Done + * @endcode + * @cparam netdata publish replace @ca{oldprefix} @ca{prefix} [@ca{sn}] [@ca{high}|@ca{med}|@ca{low}] + * OT CLI uses mapped arguments to configure #otExternalRouteConfig values. @moreinfo{the @overview}. + * @par + * Replaces a previously published external route entry. @moreinfo{@netdata}. + * @sa otNetDataReplacePublishedExternalRoute + */ + if (aArgs[0] == "replace") + { + otIp6Prefix prefix; + otExternalRouteConfig config; + + SuccessOrExit(error = aArgs[1].ParseAsIp6Prefix(prefix)); + SuccessOrExit(error = Interpreter::ParseRoute(aArgs + 2, config)); + error = otNetDataReplacePublishedExternalRoute(GetInstancePtr(), &prefix, &config); + ExitNow(); + } #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE error = OT_ERROR_INVALID_ARGS; @@ -546,6 +629,25 @@ void NetworkData::OutputServices(bool aLocal) } } +void NetworkData::OutputLowpanContexts(bool aLocal) +{ + otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; + otLowpanContextInfo info; + + VerifyOrExit(!aLocal); + + OutputLine("Contexts:"); + + while (otNetDataGetNextLowpanContextInfo(GetInstancePtr(), &iterator, &info) == OT_ERROR_NONE) + { + OutputIp6Prefix(info.mPrefix); + OutputLine(" %u %c", info.mContextId, info.mCompressFlag ? 'c' : '-'); + } + +exit: + return; +} + otError NetworkData::OutputBinary(bool aLocal) { otError error; @@ -583,6 +685,8 @@ otError NetworkData::OutputBinary(bool aLocal) * Services: * 44970 5d c000 s 4000 * 44970 01 9a04b000000e10 s 4000 + * Contexts: + * fd00:dead:beef:cafe::/64 1 c * Done * @endcode * @code @@ -595,7 +699,43 @@ otError NetworkData::OutputBinary(bool aLocal) * @par * `netdata show` from OT CLI gets full Network Data received from the Leader. This command uses several * API functions to combine prefixes, routes, and services, including #otNetDataGetNextOnMeshPrefix, - * #otNetDataGetNextRoute, and #otNetDataGetNextService. + * #otNetDataGetNextRoute, #otNetDataGetNextService and #otNetDataGetNextLowpanContextInfo. + * @par + * On-mesh prefixes are listed under `Prefixes` header: + * * The on-mesh prefix + * * Flags + * * p: Preferred flag + * * a: Stateless IPv6 Address Autoconfiguration flag + * * d: DHCPv6 IPv6 Address Configuration flag + * * c: DHCPv6 Other Configuration flag + * * r: Default Route flag + * * o: On Mesh flag + * * s: Stable flag + * * n: Nd Dns flag + * * D: Domain Prefix flag (only available for Thread 1.2). + * * Preference `high`, `med`, or `low` + * * RLOC16 of device which added the on-mesh prefix + * @par + * External Routes are listed under `Routes` header: + * * The route prefix + * * Flags + * * s: Stable flag + * * n: NAT64 flag + * * Preference `high`, `med`, or `low` + * * RLOC16 of device which added the route prefix + * @par + * Service entries are listed under `Services` header: + * * Enterprise number + * * Service data (as hex bytes) + * * Server data (as hex bytes) + * * Flags + * * s: Stable flag + * * RLOC16 of devices which added the service entry + * @par + * 6LoWPAN Context IDs are listed under `Contexts` header: + * * The prefix + * * Context ID + * * Compress flag (`c` if marked or `-` otherwise). * @par * @moreinfo{@netdata}. * @csa{br omrprefix} @@ -654,6 +794,7 @@ template <> otError NetworkData::Process(Arg aArgs[]) OutputPrefixes(local); OutputRoutes(local); OutputServices(local); + OutputLowpanContexts(local); error = OT_ERROR_NONE; } @@ -669,6 +810,8 @@ otError NetworkData::Process(Arg aArgs[]) } static constexpr Command kCommands[] = { + CmdEntry("length"), + CmdEntry("maxlength"), #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE CmdEntry("publish"), #endif @@ -693,7 +836,8 @@ otError NetworkData::Process(Arg aArgs[]) * @cli netdata help * @code * netdata help - * help + * length + * maxlength * publish * register * show diff --git a/src/cli/cli_network_data.hpp b/src/cli/cli_network_data.hpp index d46ba9ecdaa..d751c458761 100644 --- a/src/cli/cli_network_data.hpp +++ b/src/cli/cli_network_data.hpp @@ -47,7 +47,7 @@ namespace Cli { * This class implements the Network Data CLI. * */ -class NetworkData : private OutputWrapper +class NetworkData : private Output { public: typedef Utils::CmdLineParser::Arg Arg; @@ -67,11 +67,12 @@ class NetworkData : private OutputWrapper /** * Constructor * - * @param[in] aOutput The CLI console output context + * @param[in] aInstance The OpenThread Instance. + * @param[in] aOutputImplementer An `OutputImplementer`. * */ - explicit NetworkData(Output &aOutput) - : OutputWrapper(aOutput) + NetworkData(otInstance *aInstance, OutputImplementer &aOutputImplementer) + : Output(aInstance, aOutputImplementer) { } @@ -138,6 +139,7 @@ class NetworkData : private OutputWrapper void OutputPrefixes(bool aLocal); void OutputRoutes(bool aLocal); void OutputServices(bool aLocal); + void OutputLowpanContexts(bool aLocal); }; } // namespace Cli diff --git a/src/cli/cli_output.cpp b/src/cli/cli_output.cpp index bd61c259dfb..c70eacdd2f6 100644 --- a/src/cli/cli_output.cpp +++ b/src/cli/cli_output.cpp @@ -42,16 +42,16 @@ #endif #include +#include "cli/cli.hpp" #include "common/string.hpp" namespace ot { namespace Cli { -const char OutputBase::kUnknownString[] = "unknown"; +const char Output::kUnknownString[] = "unknown"; -Output::Output(otInstance *aInstance, otCliOutputCallback aCallback, void *aCallbackContext) - : mInstance(aInstance) - , mCallback(aCallback) +OutputImplementer::OutputImplementer(otCliOutputCallback aCallback, void *aCallbackContext) + : mCallback(aCallback) , mCallbackContext(aCallbackContext) #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE , mOutputLength(0) @@ -88,7 +88,7 @@ void Output::OutputLine(const char *aFormat, ...) OutputFormatV(aFormat, args); va_end(args); - OutputFormat("\r\n"); + OutputNewLine(); } void Output::OutputLine(uint8_t aIndentSize, const char *aFormat, ...) @@ -101,17 +101,12 @@ void Output::OutputLine(uint8_t aIndentSize, const char *aFormat, ...) OutputFormatV(aFormat, args); va_end(args); - OutputFormat("\r\n"); + OutputNewLine(); } -void Output::OutputSpaces(uint8_t aCount) -{ - char format[sizeof("%256s")]; - - snprintf(format, sizeof(format), "%%%us", aCount); +void Output::OutputNewLine(void) { OutputFormat("\r\n"); } - OutputFormat(format, ""); -} +void Output::OutputSpaces(uint8_t aCount) { OutputFormat("%*s", aCount, ""); } void Output::OutputBytes(const uint8_t *aBytes, uint16_t aLength) { @@ -124,14 +119,45 @@ void Output::OutputBytes(const uint8_t *aBytes, uint16_t aLength) void Output::OutputBytesLine(const uint8_t *aBytes, uint16_t aLength) { OutputBytes(aBytes, aLength); - OutputLine(""); + OutputNewLine(); +} + +const char *Output::Uint64ToString(uint64_t aUint64, Uint64StringBuffer &aBuffer) +{ + char *cur = &aBuffer.mChars[Uint64StringBuffer::kSize - 1]; + + *cur = '\0'; + + if (aUint64 == 0) + { + *(--cur) = '0'; + } + else + { + for (; aUint64 != 0; aUint64 /= 10) + { + *(--cur) = static_cast('0' + static_cast(aUint64 % 10)); + } + } + + return cur; } -void Output::OutputEnabledDisabledStatus(bool aEnabled) +void Output::OutputUint64(uint64_t aUint64) { - OutputLine(aEnabled ? "Enabled" : "Disabled"); + Uint64StringBuffer buffer; + + OutputFormat("%s", Uint64ToString(aUint64, buffer)); } +void Output::OutputUint64Line(uint64_t aUint64) +{ + OutputUint64(aUint64); + OutputNewLine(); +} + +void Output::OutputEnabledDisabledStatus(bool aEnabled) { OutputLine(aEnabled ? "Enabled" : "Disabled"); } + #if OPENTHREAD_FTD || OPENTHREAD_MTD void Output::OutputIp6Address(const otIp6Address &aAddress) @@ -146,7 +172,7 @@ void Output::OutputIp6Address(const otIp6Address &aAddress) void Output::OutputIp6AddressLine(const otIp6Address &aAddress) { OutputIp6Address(aAddress); - OutputLine(""); + OutputNewLine(); } void Output::OutputIp6Prefix(const otIp6Prefix &aPrefix) @@ -161,7 +187,7 @@ void Output::OutputIp6Prefix(const otIp6Prefix &aPrefix) void Output::OutputIp6PrefixLine(const otIp6Prefix &aPrefix) { OutputIp6Prefix(aPrefix); - OutputLine(""); + OutputNewLine(); } void Output::OutputIp6Prefix(const otIp6NetworkPrefix &aPrefix) @@ -173,7 +199,7 @@ void Output::OutputIp6Prefix(const otIp6NetworkPrefix &aPrefix) void Output::OutputIp6PrefixLine(const otIp6NetworkPrefix &aPrefix) { OutputIp6Prefix(aPrefix); - OutputLine(""); + OutputNewLine(); } void Output::OutputSockAddr(const otSockAddr &aSockAddr) @@ -188,7 +214,7 @@ void Output::OutputSockAddr(const otSockAddr &aSockAddr) void Output::OutputSockAddrLine(const otSockAddr &aSockAddr) { OutputSockAddr(aSockAddr); - OutputLine(""); + OutputNewLine(); } void Output::OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength) @@ -235,9 +261,23 @@ void Output::OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength) OutputFormat("]"); } + +const char *Output::PercentageToString(uint16_t aValue, PercentageStringBuffer &aBuffer) +{ + uint32_t scaledValue = aValue; + StringWriter writer(aBuffer.mChars, sizeof(aBuffer.mChars)); + + scaledValue = (scaledValue * 10000) / 0xffff; + writer.Append("%u.%02u", static_cast(scaledValue / 100), static_cast(scaledValue % 100)); + + return aBuffer.mChars; +} + #endif // OPENTHREAD_FTD || OPENTHREAD_MTD -void Output::OutputFormatV(const char *aFormat, va_list aArguments) +void Output::OutputFormatV(const char *aFormat, va_list aArguments) { mImplementer.OutputV(aFormat, aArguments); } + +void OutputImplementer::OutputV(const char *aFormat, va_list aArguments) { #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE va_list args; @@ -279,7 +319,7 @@ void Output::OutputFormatV(const char *aFormat, va_list aArguments) if (lineEnd > mOutputString) { - otLogCli(OT_LOG_LEVEL_DEBG, "Output: %s", mOutputString); + otLogCli(OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL, "Output: %s", mOutputString); } lineEnd++; @@ -320,7 +360,7 @@ void Output::OutputFormatV(const char *aFormat, va_list aArguments) if (truncated) { - otLogCli(OT_LOG_LEVEL_DEBG, "Output: %s ...", mOutputString); + otLogCli(OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL, "Output: %s ...", mOutputString); mOutputLength = 0; } @@ -339,7 +379,7 @@ void Output::LogInput(const Arg *aArgs) inputString.Append(isFirst ? "%s" : " %s", aArgs->GetCString()); } - otLogCli(OT_LOG_LEVEL_DEBG, "Input: %s", inputString.AsCString()); + otLogCli(OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL, "Input: %s", inputString.AsCString()); } #endif diff --git a/src/cli/cli_output.hpp b/src/cli/cli_output.hpp index 61e8997a736..1c46935dca4 100644 --- a/src/cli/cli_output.hpp +++ b/src/cli/cli_output.hpp @@ -43,6 +43,7 @@ #include "cli_config.h" #include "common/binary_search.hpp" +#include "common/num_utils.hpp" #include "common/string.hpp" #include "utils/parse_cmdline.hpp" @@ -68,11 +69,51 @@ constexpr static CommandId Cmd(const char *aString) return (aString[0] == '\0') ? 0 : (static_cast(aString[0]) + Cmd(aString + 1) * 255u); } +class Output; + +/** + * This class implements the basic output functions. + * + */ +class OutputImplementer +{ + friend class Output; + +public: + /** + * This constructor initializes the `OutputImplementer` object. + * + * @param[in] aCallback A pointer to an `otCliOutputCallback` to deliver strings to the CLI console. + * @param[in] aCallbackContext An arbitrary context to pass in when invoking @p aCallback. + * + */ + OutputImplementer(otCliOutputCallback aCallback, void *aCallbackContext); + +#if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE + void SetEmittingCommandOutput(bool aEmittingOutput) { mEmittingCommandOutput = aEmittingOutput; } +#else + void SetEmittingCommandOutput(bool) {} +#endif + +private: + static constexpr uint16_t kInputOutputLogStringSize = OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LOG_STRING_SIZE; + + void OutputV(const char *aFormat, va_list aArguments); + + otCliOutputCallback mCallback; + void *mCallbackContext; +#if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE + char mOutputString[kInputOutputLogStringSize]; + uint16_t mOutputLength; + bool mEmittingCommandOutput; +#endif +}; + /** - * This class is the base class for `Output` and `OutputWrapper` providing common helper methods. + * This class provides CLI output helper methods. * */ -class OutputBase +class Output { public: typedef Utils::CmdLineParser::Arg Arg; ///< An argument @@ -141,26 +182,18 @@ class OutputBase return (static_cast(aEnum) < kLength) ? aTable[static_cast(aEnum)] : aNotFound; } -protected: - OutputBase(void) = default; -}; - -/** - * This class provides CLI output helper methods. - * - */ -class Output : public OutputBase -{ -public: /** * This constructor initializes the `Output` object. * * @param[in] aInstance A pointer to OpenThread instance. - * @param[in] aCallback A pointer to an `otCliOutputCallback` to deliver strings to the CLI console. - * @param[in] aCallbackContext An arbitrary context to pass in when invoking @p aCallback. + * @param[in] aImplementer An `OutputImplementer`. * */ - Output(otInstance *aInstance, otCliOutputCallback aCallback, void *aCallbackContext); + Output(otInstance *aInstance, OutputImplementer &aImplementer) + : mInstance(aInstance) + , mImplementer(aImplementer) + { + } /** * This method returns the pointer to OpenThread instance. @@ -170,6 +203,28 @@ class Output : public OutputBase */ otInstance *GetInstancePtr(void) { return mInstance; } + /** + * This structure represents a buffer which is used when converting a `uint64` value to string in decimal format. + * + */ + struct Uint64StringBuffer + { + static constexpr uint16_t kSize = 21; ///< Size of a buffer + + char mChars[kSize]; ///< Char array (do not access the array directly). + }; + + /** + * This static method converts a `uint64_t` value to a decimal format string. + * + * @param[in] aUint64 The `uint64_t` value to convert. + * @param[in] aBuffer A buffer to allocate the string from. + * + * @returns A pointer to the start of the string (null-terminated) representation of @p aUint64. + * + */ + static const char *Uint64ToString(uint64_t aUint64, Uint64StringBuffer &aBuffer); + /** * This method delivers a formatted output string to the CLI console. * @@ -177,7 +232,7 @@ class Output : public OutputBase * @param[in] ... A variable list of arguments to format. * */ - void OutputFormat(const char *aFormat, ...); + void OutputFormat(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3); /** * This method delivers a formatted output string to the CLI console (to which it prepends a given number @@ -188,7 +243,7 @@ class Output : public OutputBase * @param[in] ... A variable list of arguments to format. * */ - void OutputFormat(uint8_t aIndentSize, const char *aFormat, ...); + void OutputFormat(uint8_t aIndentSize, const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(3, 4); /** * This method delivers a formatted output string to the CLI console (to which it also appends newline "\r\n"). @@ -197,7 +252,7 @@ class Output : public OutputBase * @param[in] ... A variable list of arguments to format. * */ - void OutputLine(const char *aFormat, ...); + void OutputLine(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3); /** * This method delivers a formatted output string to the CLI console (to which it prepends a given number @@ -208,7 +263,13 @@ class Output : public OutputBase * @param[in] ... A variable list of arguments to format. * */ - void OutputLine(uint8_t aIndentSize, const char *aFormat, ...); + void OutputLine(uint8_t aIndentSize, const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(3, 4); + + /** + * This method delivered newline "\r\n" to the CLI console. + * + */ + void OutputNewLine(void); /** * This method outputs a given number of space chars to the CLI console. @@ -280,6 +341,22 @@ class Output : public OutputBase */ void OutputExtAddressLine(const otExtAddress &aExtAddress) { OutputBytesLine(aExtAddress.m8); } + /** + * This method outputs a `uint64_t` value in decimal format. + * + * @param[in] aUint64 The `uint64_t` value to output. + * + */ + void OutputUint64(uint64_t aUint64); + + /** + * This method outputs a `uint64_t` value in decimal format and at the end it also outputs newline "\r\n". + * + * @param[in] aUint64 The `uint64_t` value to output. + * + */ + void OutputUint64Line(uint64_t aUint64); + /** * This method outputs "Enabled" or "Disabled" status to the CLI console (it also appends newline "\r\n"). * @@ -363,6 +440,32 @@ class Output : public OutputBase */ void OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength); + /** + * This structure represents a buffer which is used when converting an encoded rate value to percentage string. + * + */ + struct PercentageStringBuffer + { + static constexpr uint16_t kSize = 7; ///< Size of a buffer + + char mChars[kSize]; ///< Char array (do not access the array directly). + }; + + /** + * This static method converts an encoded value to a percentage representation. + * + * The encoded @p aValue is assumed to be linearly scaled such that `0` maps to 0% and `0xffff` maps to 100%. + * + * The resulting string provides two decimal accuracy, e.g., "100.00", "0.00", "75.37". + * + * @param[in] aValue The encoded percentage value to convert. + * @param[in] aBuffer A buffer to allocate the string from. + * + * @returns A pointer to the start of the string (null-terminated) representation of @p aValue. + * + */ + static const char *PercentageToString(uint16_t aValue, PercentageStringBuffer &aBuffer); + #endif // OPENTHREAD_FTD || OPENTHREAD_MTD /** @@ -429,10 +532,8 @@ class Output : public OutputBase #if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE void LogInput(const Arg *aArgs); - void SetEmittingCommandOutput(bool aEmittingOutput) { mEmittingCommandOutput = aEmittingOutput; } #else void LogInput(const Arg *) {} - void SetEmittingCommandOutput(bool) {} #endif private: @@ -441,96 +542,8 @@ class Output : public OutputBase void OutputTableHeader(uint8_t aNumColumns, const char *const aTitles[], const uint8_t aWidths[]); void OutputTableSeparator(uint8_t aNumColumns, const uint8_t aWidths[]); - otInstance * mInstance; - otCliOutputCallback mCallback; - void * mCallbackContext; -#if OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE - char mOutputString[kInputOutputLogStringSize]; - uint16_t mOutputLength; - bool mEmittingCommandOutput; -#endif -}; - -class OutputWrapper : public OutputBase -{ -protected: - explicit OutputWrapper(Output &aOutput) - : mOutput(aOutput) - { - } - - otInstance *GetInstancePtr(void) { return mOutput.GetInstancePtr(); } - - template void OutputFormat(const char *aFormat, Args... aArgs) - { - mOutput.OutputFormat(aFormat, aArgs...); - } - - template void OutputFormat(uint8_t aIndentSize, const char *aFormat, Args... aArgs) - { - mOutput.OutputFormat(aIndentSize, aFormat, aArgs...); - } - - template void OutputLine(const char *aFormat, Args... aArgs) - { - return mOutput.OutputLine(aFormat, aArgs...); - } - - template void OutputLine(uint8_t aIndentSize, const char *aFormat, Args... aArgs) - { - return mOutput.OutputLine(aIndentSize, aFormat, aArgs...); - } - - template void OutputBytes(const uint8_t (&aBytes)[kBytesLength]) - { - mOutput.OutputBytes(aBytes, kBytesLength); - } - - template void OutputBytesLine(const uint8_t (&aBytes)[kBytesLength]) - { - mOutput.OutputBytesLine(aBytes, kBytesLength); - } - - void OutputSpaces(uint8_t aCount) { return mOutput.OutputSpaces(aCount); } - void OutputBytes(const uint8_t *aBytes, uint16_t aLength) { return mOutput.OutputBytes(aBytes, aLength); } - void OutputBytesLine(const uint8_t *aBytes, uint16_t aLength) { return mOutput.OutputBytesLine(aBytes, aLength); } - void OutputExtAddress(const otExtAddress &aExtAddress) { mOutput.OutputExtAddress(aExtAddress); } - void OutputExtAddressLine(const otExtAddress &aExtAddress) { mOutput.OutputExtAddressLine(aExtAddress); } - void OutputEnabledDisabledStatus(bool aEnabled) { mOutput.OutputEnabledDisabledStatus(aEnabled); } - -#if OPENTHREAD_FTD || OPENTHREAD_MTD - void OutputIp6Address(const otIp6Address &aAddress) { mOutput.OutputIp6Address(aAddress); } - void OutputIp6AddressLine(const otIp6Address &aAddress) { mOutput.OutputIp6AddressLine(aAddress); } - void OutputIp6Prefix(const otIp6Prefix &aPrefix) { mOutput.OutputIp6Prefix(aPrefix); } - void OutputIp6PrefixLine(const otIp6Prefix &aPrefix) { mOutput.OutputIp6PrefixLine(aPrefix); } - void OutputIp6Prefix(const otIp6NetworkPrefix &aPrefix) { mOutput.OutputIp6Prefix(aPrefix); } - void OutputIp6PrefixLine(const otIp6NetworkPrefix &aPrefix) { mOutput.OutputIp6PrefixLine(aPrefix); } - void OutputSockAddr(const otSockAddr &aSockAddr) { mOutput.OutputSockAddr(aSockAddr); } - void OutputSockAddrLine(const otSockAddr &aSockAddr) { mOutput.OutputSockAddrLine(aSockAddr); } - void OutputDnsTxtData(const uint8_t *aTxtData, uint16_t aTxtDataLength) - { - mOutput.OutputDnsTxtData(aTxtData, aTxtDataLength); - } -#endif - - template - void OutputTableHeader(const char *const (&aTitles)[kTableNumColumns], const uint8_t (&aWidths)[kTableNumColumns]) - { - mOutput.OutputTableHeader(aTitles, aWidths); - } - - template void OutputTableSeparator(const uint8_t (&aWidths)[kTableNumColumns]) - { - mOutput.OutputTableSeparator(aWidths); - } - - template void OutputCommandTable(const CommandEntry (&aCommandTable)[kLength]) - { - mOutput.OutputCommandTable(aCommandTable); - } - -private: - Output &mOutput; + otInstance *mInstance; + OutputImplementer &mImplementer; }; } // namespace Cli diff --git a/src/cli/cli_srp_client.cpp b/src/cli/cli_srp_client.cpp index 0732ac2e4ce..0294c5306d5 100644 --- a/src/cli/cli_srp_client.cpp +++ b/src/cli/cli_srp_client.cpp @@ -57,8 +57,8 @@ static otError CopyString(char *aDest, uint16_t aDestSize, const char *aSource) return error; } -SrpClient::SrpClient(Output &aOutput) - : OutputWrapper(aOutput) +SrpClient::SrpClient(otInstance *aInstance, OutputImplementer &aOutputImplementer) + : Output(aInstance, aOutputImplementer) , mCallbackEnabled(false) { otSrpClientSetCallback(GetInstancePtr(), SrpClient::HandleCallback, this); @@ -129,7 +129,7 @@ template <> otError SrpClient::Process(Arg aArgs[]) { uint16_t len; uint16_t size; - char * hostName; + char *hostName; VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); hostName = otSrpClientBuffersGetHostNameString(GetInstancePtr(), &size); @@ -355,13 +355,13 @@ template <> otError SrpClient::Process(Arg aArgs[]) otError SrpClient::ProcessServiceAdd(Arg aArgs[]) { - // `add` [priority] [weight] [txt] + // `add` [priority] [weight] [txt] [lease] [key-lease] otSrpClientBuffersServiceEntry *entry = nullptr; uint16_t size; - char * string; + char *string; otError error; - char * label; + char *label; entry = otSrpClientBuffersAllocateService(GetInstancePtr()); @@ -417,7 +417,7 @@ otError SrpClient::ProcessServiceAdd(Arg aArgs[]) SuccessOrExit(error = aArgs[5].ParseAsUint16(entry->mService.mWeight)); } - if (!aArgs[6].IsEmpty()) + if (!aArgs[6].IsEmpty() && (aArgs[6] != "-")) { uint8_t *txtBuffer; @@ -425,13 +425,23 @@ otError SrpClient::ProcessServiceAdd(Arg aArgs[]) entry->mTxtEntry.mValueLength = size; SuccessOrExit(error = aArgs[6].ParseAsHexString(entry->mTxtEntry.mValueLength, txtBuffer)); - VerifyOrExit(aArgs[7].IsEmpty(), error = OT_ERROR_INVALID_ARGS); } else { entry->mService.mNumTxtEntries = 0; } + if (!aArgs[7].IsEmpty()) + { + SuccessOrExit(error = aArgs[7].ParseAsUint32(entry->mService.mLease)); + } + + if (!aArgs[8].IsEmpty()) + { + SuccessOrExit(error = aArgs[8].ParseAsUint32(entry->mService.mKeyLease)); + VerifyOrExit(aArgs[9].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + } + SuccessOrExit(error = otSrpClientAddService(GetInstancePtr(), &entry->mService)); entry = nullptr; @@ -552,17 +562,17 @@ template <> otError SrpClient::Process(Arg aArgs[]) void SrpClient::HandleCallback(otError aError, const otSrpClientHostInfo *aHostInfo, - const otSrpClientService * aServices, - const otSrpClientService * aRemovedServices, - void * aContext) + const otSrpClientService *aServices, + const otSrpClientService *aRemovedServices, + void *aContext) { static_cast(aContext)->HandleCallback(aError, aHostInfo, aServices, aRemovedServices); } void SrpClient::HandleCallback(otError aError, const otSrpClientHostInfo *aHostInfo, - const otSrpClientService * aServices, - const otSrpClientService * aRemovedServices) + const otSrpClientService *aServices, + const otSrpClientService *aRemovedServices) { otSrpClientService *next; diff --git a/src/cli/cli_srp_client.hpp b/src/cli/cli_srp_client.hpp index b868e6ff842..9b9ea4237ec 100644 --- a/src/cli/cli_srp_client.hpp +++ b/src/cli/cli_srp_client.hpp @@ -51,7 +51,7 @@ namespace Cli { * This class implements the SRP Client CLI interpreter. * */ -class SrpClient : private OutputWrapper +class SrpClient : private Output { public: typedef Utils::CmdLineParser::Arg Arg; @@ -59,10 +59,11 @@ class SrpClient : private OutputWrapper /** * Constructor * - * @param[in] aOutput The CLI console output context. + * @param[in] aInstance The OpenThread Instance. + * @param[in] aOutputImplementer An `OutputImplementer`. * */ - explicit SrpClient(Output &aOutput); + SrpClient(otInstance *aInstance, OutputImplementer &aOutputImplementer); /** * This method interprets a list of CLI arguments. @@ -91,13 +92,13 @@ class SrpClient : private OutputWrapper static void HandleCallback(otError aError, const otSrpClientHostInfo *aHostInfo, - const otSrpClientService * aServices, - const otSrpClientService * aRemovedServices, - void * aContext); + const otSrpClientService *aServices, + const otSrpClientService *aRemovedServices, + void *aContext); void HandleCallback(otError aError, const otSrpClientHostInfo *aHostInfo, - const otSrpClientService * aServices, - const otSrpClientService * aRemovedServices); + const otSrpClientService *aServices, + const otSrpClientService *aRemovedServices); bool mCallbackEnabled; }; diff --git a/src/cli/cli_srp_server.cpp b/src/cli/cli_srp_server.cpp index e13b8eca68d..6a0a4027600 100644 --- a/src/cli/cli_srp_server.cpp +++ b/src/cli/cli_srp_server.cpp @@ -43,29 +43,7 @@ namespace ot { namespace Cli { -constexpr SrpServer::Command SrpServer::sCommands[]; - -otError SrpServer::Process(Arg aArgs[]) -{ - otError error = OT_ERROR_INVALID_COMMAND; - const Command *command; - - if (aArgs[0].IsEmpty()) - { - IgnoreError(ProcessHelp(aArgs)); - ExitNow(); - } - - command = BinarySearch::Find(aArgs[0].GetCString(), sCommands); - VerifyOrExit(command != nullptr); - - error = (this->*command->mHandler)(aArgs + 1); - -exit: - return error; -} - -otError SrpServer::ProcessAddrMode(Arg aArgs[]) +template <> otError SrpServer::Process(Arg aArgs[]) { otError error = OT_ERROR_INVALID_ARGS; @@ -96,7 +74,48 @@ otError SrpServer::ProcessAddrMode(Arg aArgs[]) return error; } -otError SrpServer::ProcessDomain(Arg aArgs[]) +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +template <> otError SrpServer::Process(Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + /** + * @cli srp server auto + * @code + * srp server auto + * Disabled + * Done + * @endcode + * @par api_copy + * #otSrpServerIsAutoEnableMode + */ + if (aArgs[0].IsEmpty()) + { + OutputEnabledDisabledStatus(otSrpServerIsAutoEnableMode(GetInstancePtr())); + } + /** + * @cli srp server auto enable + * @code + * srp server auto enable + * Done + * @endcode + * @par api_copy + * #otSrpServerSetAutoEnableMode + */ + else + { + bool enable; + + SuccessOrExit(error = Interpreter::ParseEnableOrDisable(aArgs[0], enable)); + otSrpServerSetAutoEnableMode(GetInstancePtr(), enable); + } + +exit: + return error; +} +#endif + +template <> otError SrpServer::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -112,7 +131,7 @@ otError SrpServer::ProcessDomain(Arg aArgs[]) return error; } -otError SrpServer::ProcessState(Arg aArgs[]) +template <> otError SrpServer::Process(Arg aArgs[]) { static const char *const kStateStrings[] = { "disabled", // (0) OT_SRP_SERVER_STATE_DISABLED @@ -131,7 +150,7 @@ otError SrpServer::ProcessState(Arg aArgs[]) return OT_ERROR_NONE; } -otError SrpServer::ProcessEnable(Arg aArgs[]) +template <> otError SrpServer::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); @@ -140,7 +159,7 @@ otError SrpServer::ProcessEnable(Arg aArgs[]) return OT_ERROR_NONE; } -otError SrpServer::ProcessDisable(Arg aArgs[]) +template <> otError SrpServer::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); @@ -149,7 +168,7 @@ otError SrpServer::ProcessDisable(Arg aArgs[]) return OT_ERROR_NONE; } -otError SrpServer::ProcessTtl(Arg aArgs[]) +template <> otError SrpServer::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; otSrpServerTtlConfig ttlConfig; @@ -157,8 +176,8 @@ otError SrpServer::ProcessTtl(Arg aArgs[]) if (aArgs[0].IsEmpty()) { otSrpServerGetTtlConfig(GetInstancePtr(), &ttlConfig); - OutputLine("min ttl: %u", ttlConfig.mMinTtl); - OutputLine("max ttl: %u", ttlConfig.mMaxTtl); + OutputLine("min ttl: %lu", ToUlong(ttlConfig.mMinTtl)); + OutputLine("max ttl: %lu", ToUlong(ttlConfig.mMaxTtl)); } else { @@ -173,7 +192,7 @@ otError SrpServer::ProcessTtl(Arg aArgs[]) return error; } -otError SrpServer::ProcessLease(Arg aArgs[]) +template <> otError SrpServer::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; otSrpServerLeaseConfig leaseConfig; @@ -181,10 +200,10 @@ otError SrpServer::ProcessLease(Arg aArgs[]) if (aArgs[0].IsEmpty()) { otSrpServerGetLeaseConfig(GetInstancePtr(), &leaseConfig); - OutputLine("min lease: %u", leaseConfig.mMinLease); - OutputLine("max lease: %u", leaseConfig.mMaxLease); - OutputLine("min key-lease: %u", leaseConfig.mMinKeyLease); - OutputLine("max key-lease: %u", leaseConfig.mMaxKeyLease); + OutputLine("min lease: %lu", ToUlong(leaseConfig.mMinLease)); + OutputLine("max lease: %lu", ToUlong(leaseConfig.mMaxLease)); + OutputLine("min key-lease: %lu", ToUlong(leaseConfig.mMinKeyLease)); + OutputLine("max key-lease: %lu", ToUlong(leaseConfig.mMaxKeyLease)); } else { @@ -201,7 +220,7 @@ otError SrpServer::ProcessLease(Arg aArgs[]) return error; } -otError SrpServer::ProcessHost(Arg aArgs[]) +template <> otError SrpServer::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; const otSrpServerHost *host; @@ -263,7 +282,7 @@ void SrpServer::OutputHostAddresses(const otSrpServerHost *aHost) OutputFormat("]"); } -otError SrpServer::ProcessService(Arg aArgs[]) +template <> otError SrpServer::Process(Arg aArgs[]) { static constexpr char *kAnyServiceName = nullptr; static constexpr char *kAnyInstanceName = nullptr; @@ -281,11 +300,12 @@ otError SrpServer::ProcessService(Arg aArgs[]) kAnyServiceName, kAnyInstanceName)) != nullptr) { bool isDeleted = otSrpServerServiceIsDeleted(service); - const char * instanceName = otSrpServerServiceGetInstanceName(service); + const char *instanceName = otSrpServerServiceGetInstanceName(service); const otSrpServerService *subService = nullptr; - const uint8_t * txtData; + const uint8_t *txtData; uint16_t txtDataLength; bool hasSubType = false; + otSrpServerLeaseInfo leaseInfo; OutputLine("%s", instanceName); OutputLine(kIndentSize, "deleted: %s", isDeleted ? "true" : "false"); @@ -295,6 +315,8 @@ otError SrpServer::ProcessService(Arg aArgs[]) continue; } + otSrpServerServiceGetLeaseInfo(service, &leaseInfo); + OutputFormat(kIndentSize, "subtypes: "); while ((subService = otSrpServerHostFindNextService( @@ -310,21 +332,23 @@ otError SrpServer::ProcessService(Arg aArgs[]) OutputLine(hasSubType ? "" : "(null)"); - OutputLine(kIndentSize, "port: %hu", otSrpServerServiceGetPort(service)); - OutputLine(kIndentSize, "priority: %hu", otSrpServerServiceGetPriority(service)); - OutputLine(kIndentSize, "weight: %hu", otSrpServerServiceGetWeight(service)); - OutputLine(kIndentSize, "ttl: %hu", otSrpServerServiceGetTtl(service)); + OutputLine(kIndentSize, "port: %u", otSrpServerServiceGetPort(service)); + OutputLine(kIndentSize, "priority: %u", otSrpServerServiceGetPriority(service)); + OutputLine(kIndentSize, "weight: %u", otSrpServerServiceGetWeight(service)); + OutputLine(kIndentSize, "ttl: %lu", ToUlong(otSrpServerServiceGetTtl(service))); + OutputLine(kIndentSize, "lease: %lu", ToUlong(leaseInfo.mLease / 1000)); + OutputLine(kIndentSize, "key-lease: %lu", ToUlong(leaseInfo.mKeyLease / 1000)); txtData = otSrpServerServiceGetTxtData(service, &txtDataLength); OutputFormat(kIndentSize, "TXT: "); OutputDnsTxtData(txtData, txtDataLength); - OutputLine(""); + OutputNewLine(); OutputLine(kIndentSize, "host: %s", otSrpServerHostGetFullName(host)); OutputFormat(kIndentSize, "addresses: "); OutputHostAddresses(host); - OutputLine(""); + OutputNewLine(); } } @@ -332,7 +356,7 @@ otError SrpServer::ProcessService(Arg aArgs[]) return error; } -otError SrpServer::ProcessSeqNum(Arg aArgs[]) +template <> otError SrpServer::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -352,16 +376,47 @@ otError SrpServer::ProcessSeqNum(Arg aArgs[]) return error; } -otError SrpServer::ProcessHelp(Arg aArgs[]) +otError SrpServer::Process(Arg aArgs[]) { - OT_UNUSED_VARIABLE(aArgs); +#define CmdEntry(aCommandString) \ + { \ + aCommandString, &SrpServer::Process \ + } - for (const Command &command : sCommands) + static constexpr Command kCommands[] = { + CmdEntry("addrmode"), +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + CmdEntry("auto"), +#endif + CmdEntry("disable"), + CmdEntry("domain"), + CmdEntry("enable"), + CmdEntry("host"), + CmdEntry("lease"), + CmdEntry("seqnum"), + CmdEntry("service"), + CmdEntry("state"), + CmdEntry("ttl"), + }; + + static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); + + otError error = OT_ERROR_INVALID_COMMAND; + const Command *command; + + if (aArgs[0].IsEmpty() || (aArgs[0] == "help")) { - OutputLine(command.mName); + OutputCommandTable(kCommands); + ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE); } - return OT_ERROR_NONE; + command = BinarySearch::Find(aArgs[0].GetCString(), kCommands); + VerifyOrExit(command != nullptr); + + error = (this->*command->mHandler)(aArgs + 1); + +exit: + return error; } } // namespace Cli diff --git a/src/cli/cli_srp_server.hpp b/src/cli/cli_srp_server.hpp index 363f5911863..6d179d2a9af 100644 --- a/src/cli/cli_srp_server.hpp +++ b/src/cli/cli_srp_server.hpp @@ -49,7 +49,7 @@ namespace Cli { * This class implements the SRP Server CLI interpreter. * */ -class SrpServer : private OutputWrapper +class SrpServer : private Output { public: typedef Utils::CmdLineParser::Arg Arg; @@ -57,11 +57,12 @@ class SrpServer : private OutputWrapper /** * Constructor * - * @param[in] aOutput The CLI console output context. + * @param[in] aInstance The OpenThread Instance. + * @param[in] aOutputImplementer An `OutputImplementer`. * */ - explicit SrpServer(Output &aOutput) - : OutputWrapper(aOutput) + SrpServer(otInstance *aInstance, OutputImplementer &aOutputImplementer) + : Output(aInstance, aOutputImplementer) { } @@ -81,30 +82,9 @@ class SrpServer : private OutputWrapper using Command = CommandEntry; - otError ProcessAddrMode(Arg aArgs[]); - otError ProcessDomain(Arg aArgs[]); - otError ProcessState(Arg aArgs[]); - otError ProcessEnable(Arg aArgs[]); - otError ProcessDisable(Arg aArgs[]); - otError ProcessLease(Arg aArgs[]); - otError ProcessHost(Arg aArgs[]); - otError ProcessService(Arg aArgs[]); - otError ProcessSeqNum(Arg aArgs[]); - otError ProcessTtl(Arg aArgs[]); - otError ProcessHelp(Arg aArgs[]); + template otError Process(Arg aArgs[]); void OutputHostAddresses(const otSrpServerHost *aHost); - - static constexpr Command sCommands[] = { - {"addrmode", &SrpServer::ProcessAddrMode}, {"disable", &SrpServer::ProcessDisable}, - {"domain", &SrpServer::ProcessDomain}, {"enable", &SrpServer::ProcessEnable}, - {"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"); }; } // namespace Cli diff --git a/src/cli/cli_tcp.cpp b/src/cli/cli_tcp.cpp index 97076e9dfd9..3e1cc82ff11 100644 --- a/src/cli/cli_tcp.cpp +++ b/src/cli/cli_tcp.cpp @@ -39,40 +39,51 @@ #include "cli_tcp.hpp" +#include #include #include "cli/cli.hpp" #include "common/encoding.hpp" #include "common/timer.hpp" +#if OPENTHREAD_CONFIG_TLS_ENABLE +#include +#include +#include "crypto/mbedtls.hpp" +#endif + namespace ot { namespace Cli { -constexpr TcpExample::Command TcpExample::sCommands[]; +#if OPENTHREAD_CONFIG_TLS_ENABLE +const int TcpExample::sCipherSuites[] = {MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8, + MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, 0}; +#endif -TcpExample::TcpExample(Output &aOutput) - : OutputWrapper(aOutput) +TcpExample::TcpExample(otInstance *aInstance, OutputImplementer &aOutputImplementer) + : Output(aInstance, aOutputImplementer) , mInitialized(false) , mEndpointConnected(false) , mSendBusy(false) + , mUseCircularSendBuffer(true) + , mUseTls(false) + , mTlsHandshakeComplete(false) , mBenchmarkBytesTotal(0) - , mBenchmarkLinksLeft(0) + , mBenchmarkBytesUnsent(0) { + mEndpointAndCircularSendBuffer.mEndpoint = &mEndpoint; + mEndpointAndCircularSendBuffer.mSendBuffer = &mSendBuffer; } -otError TcpExample::ProcessHelp(Arg aArgs[]) +#if OPENTHREAD_CONFIG_TLS_ENABLE +void TcpExample::MbedTlsDebugOutput(void *ctx, int level, const char *file, int line, const char *str) { - OT_UNUSED_VARIABLE(aArgs); - - for (const Command &command : sCommands) - { - OutputLine(command.mName); - } - - return OT_ERROR_NONE; + TcpExample &tcpExample = *static_cast(ctx); + tcpExample.OutputLine("%s:%d:%d: %s", file, line, level, str); } +#endif // OPENTHREAD_CONFIG_TLS_ENABLE -otError TcpExample::ProcessInit(Arg aArgs[]) +template <> otError TcpExample::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; size_t receiveBufferSize; @@ -81,30 +92,119 @@ otError TcpExample::ProcessInit(Arg aArgs[]) if (aArgs[0].IsEmpty()) { - receiveBufferSize = sizeof(mReceiveBuffer); + mUseCircularSendBuffer = true; + mUseTls = false; + receiveBufferSize = sizeof(mReceiveBufferBytes); } else { - uint32_t windowSize; + if (aArgs[0] == "linked") + { + mUseCircularSendBuffer = false; + mUseTls = false; + } + else if (aArgs[0] == "circular") + { + mUseCircularSendBuffer = true; + mUseTls = false; + } +#if OPENTHREAD_CONFIG_TLS_ENABLE + else if (aArgs[0] == "tls") + { + mUseCircularSendBuffer = true; + mUseTls = true; + + // mbedtls_debug_set_threshold(0); + + otPlatCryptoRandomInit(); + mbedtls_x509_crt_init(&mSrvCert); + mbedtls_pk_init(&mPKey); + + mbedtls_ssl_init(&mSslContext); + mbedtls_ssl_config_init(&mSslConfig); + mbedtls_ssl_conf_rng(&mSslConfig, Crypto::MbedTls::CryptoSecurePrng, nullptr); + // mbedtls_ssl_conf_dbg(&mSslConfig, MbedTlsDebugOutput, this); + mbedtls_ssl_conf_authmode(&mSslConfig, MBEDTLS_SSL_VERIFY_NONE); + mbedtls_ssl_conf_ciphersuites(&mSslConfig, sCipherSuites); + +#if (MBEDTLS_VERSION_NUMBER >= 0x03020000) + mbedtls_ssl_conf_min_tls_version(&mSslConfig, MBEDTLS_SSL_VERSION_TLS1_2); + mbedtls_ssl_conf_max_tls_version(&mSslConfig, MBEDTLS_SSL_VERSION_TLS1_2); +#else + mbedtls_ssl_conf_min_version(&mSslConfig, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); + mbedtls_ssl_conf_max_version(&mSslConfig, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); +#endif + +#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) +#include "crypto/mbedtls.hpp" + int rv = mbedtls_pk_parse_key(&mPKey, reinterpret_cast(sSrvKey), sSrvKeyLength, + nullptr, 0, Crypto::MbedTls::CryptoSecurePrng, nullptr); +#else + int rv = mbedtls_pk_parse_key(&mPKey, reinterpret_cast(sSrvKey), sSrvKeyLength, + nullptr, 0); +#endif + if (rv != 0) + { + OutputLine("mbedtls_pk_parse_key returned %d", rv); + } + + rv = mbedtls_x509_crt_parse(&mSrvCert, reinterpret_cast(sSrvPem), sSrvPemLength); + if (rv != 0) + { + OutputLine("mbedtls_x509_crt_parse (1) returned %d", rv); + } + rv = mbedtls_x509_crt_parse(&mSrvCert, reinterpret_cast(sCasPem), sCasPemLength); + if (rv != 0) + { + OutputLine("mbedtls_x509_crt_parse (2) returned %d", rv); + } + rv = mbedtls_ssl_setup(&mSslContext, &mSslConfig); + if (rv != 0) + { + OutputLine("mbedtls_ssl_setup returned %d", rv); + } + } +#endif // OPENTHREAD_CONFIG_TLS_ENABLE + else + { + ExitNow(error = OT_ERROR_INVALID_ARGS); + } + + if (aArgs[1].IsEmpty()) + { + receiveBufferSize = sizeof(mReceiveBufferBytes); + } + else + { + uint32_t windowSize; - SuccessOrExit(error = aArgs[0].ParseAsUint32(windowSize)); - VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + SuccessOrExit(error = aArgs[1].ParseAsUint32(windowSize)); - receiveBufferSize = windowSize + ((windowSize + 7) >> 3); - VerifyOrExit(receiveBufferSize <= sizeof(mReceiveBuffer) && receiveBufferSize != 0, - error = OT_ERROR_INVALID_ARGS); + receiveBufferSize = windowSize + ((windowSize + 7) >> 3); + VerifyOrExit(receiveBufferSize <= sizeof(mReceiveBufferBytes) && receiveBufferSize != 0, + error = OT_ERROR_INVALID_ARGS); + } } + otTcpCircularSendBufferInitialize(&mSendBuffer, mSendBufferBytes, sizeof(mSendBufferBytes)); + { otTcpEndpointInitializeArgs endpointArgs; memset(&endpointArgs, 0x00, sizeof(endpointArgs)); - endpointArgs.mEstablishedCallback = HandleTcpEstablishedCallback; - endpointArgs.mSendDoneCallback = HandleTcpSendDoneCallback; + endpointArgs.mEstablishedCallback = HandleTcpEstablishedCallback; + if (mUseCircularSendBuffer) + { + endpointArgs.mForwardProgressCallback = HandleTcpForwardProgressCallback; + } + else + { + endpointArgs.mSendDoneCallback = HandleTcpSendDoneCallback; + } endpointArgs.mReceiveAvailableCallback = HandleTcpReceiveAvailableCallback; endpointArgs.mDisconnectedCallback = HandleTcpDisconnectedCallback; endpointArgs.mContext = this; - endpointArgs.mReceiveBuffer = mReceiveBuffer; + endpointArgs.mReceiveBuffer = mReceiveBufferBytes; endpointArgs.mReceiveBufferSize = receiveBufferSize; SuccessOrExit(error = otTcpEndpointInitialize(GetInstancePtr(), &mEndpoint, &endpointArgs)); @@ -132,29 +232,46 @@ otError TcpExample::ProcessInit(Arg aArgs[]) return error; } -otError TcpExample::ProcessDeinit(Arg aArgs[]) +template <> otError TcpExample::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; otError endpointError; + otError bufferError; otError listenerError; VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE); +#if OPENTHREAD_CONFIG_TLS_ENABLE + if (mUseTls) + { + otPlatCryptoRandomDeinit(); + mbedtls_ssl_config_free(&mSslConfig); + mbedtls_ssl_free(&mSslContext); + + mbedtls_pk_free(&mPKey); + mbedtls_x509_crt_free(&mSrvCert); + } +#endif // OPENTHREAD_CONFIG_TLS_ENABLE + endpointError = otTcpEndpointDeinitialize(&mEndpoint); mSendBusy = false; + otTcpCircularSendBufferForceDiscardAll(&mSendBuffer); + bufferError = otTcpCircularSendBufferDeinitialize(&mSendBuffer); + listenerError = otTcpListenerDeinitialize(&mListener); mInitialized = false; SuccessOrExit(error = endpointError); + SuccessOrExit(error = bufferError); SuccessOrExit(error = listenerError); exit: return error; } -otError TcpExample::ProcessBind(Arg aArgs[]) +template <> otError TcpExample::Process(Arg aArgs[]) { otError error; otSockAddr sockaddr; @@ -171,50 +288,94 @@ otError TcpExample::ProcessBind(Arg aArgs[]) return error; } -otError TcpExample::ProcessConnect(Arg aArgs[]) +template <> otError TcpExample::Process(Arg aArgs[]) { otError error; otSockAddr sockaddr; + bool nat64SynthesizedAddress; VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE); - SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress)); + SuccessOrExit( + error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], sockaddr.mAddress, nat64SynthesizedAddress)); + if (nat64SynthesizedAddress) + { + OutputFormat("Connecting to synthesized IPv6 address: "); + OutputIp6AddressLine(sockaddr.mAddress); + } + SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort)); VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); +#if OPENTHREAD_CONFIG_TLS_ENABLE + if (mUseTls) + { + int rv = mbedtls_ssl_config_defaults(&mSslConfig, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (rv != 0) + { + OutputLine("mbedtls_ssl_config_defaults returned %d", rv); + } + } +#endif // OPENTHREAD_CONFIG_TLS_ENABLE + SuccessOrExit(error = otTcpConnect(&mEndpoint, &sockaddr, OT_TCP_CONNECT_NO_FAST_OPEN)); - mEndpointConnected = false; + mEndpointConnected = true; exit: return error; } -otError TcpExample::ProcessSend(Arg aArgs[]) +template <> otError TcpExample::Process(Arg aArgs[]) { otError error; VerifyOrExit(mInitialized, error = OT_ERROR_INVALID_STATE); - VerifyOrExit(!mSendBusy, error = OT_ERROR_BUSY); VerifyOrExit(mBenchmarkBytesTotal == 0, error = OT_ERROR_BUSY); - - mSendLink.mNext = nullptr; - mSendLink.mData = mSendBuffer; VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); - mSendLink.mLength = OT_MIN(aArgs[0].GetLength(), sizeof(mSendBuffer)); - memcpy(mSendBuffer, aArgs[0].GetCString(), mSendLink.mLength); VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); - SuccessOrExit(error = otTcpSendByReference(&mEndpoint, &mSendLink, 0)); - mSendBusy = true; + if (mUseCircularSendBuffer) + { +#if OPENTHREAD_CONFIG_TLS_ENABLE + if (mUseTls) + { + int rv = mbedtls_ssl_write(&mSslContext, reinterpret_cast(aArgs[0].GetCString()), + aArgs[0].GetLength()); + if (rv < 0 && rv != MBEDTLS_ERR_SSL_WANT_WRITE && rv != MBEDTLS_ERR_SSL_WANT_READ) + { + ExitNow(error = kErrorFailed); + } + error = kErrorNone; + } + else +#endif // OPENTHREAD_CONFIG_TLS_ENABLE + { + size_t written; + SuccessOrExit(error = otTcpCircularSendBufferWrite(&mEndpoint, &mSendBuffer, aArgs[0].GetCString(), + aArgs[0].GetLength(), &written, 0)); + } + } + else + { + VerifyOrExit(!mSendBusy, error = OT_ERROR_BUSY); + + mSendLink.mNext = nullptr; + mSendLink.mData = mSendBufferBytes; + mSendLink.mLength = OT_MIN(aArgs[0].GetLength(), sizeof(mSendBufferBytes)); + memcpy(mSendBufferBytes, aArgs[0].GetCString(), mSendLink.mLength); + + SuccessOrExit(error = otTcpSendByReference(&mEndpoint, &mSendLink, 0)); + mSendBusy = true; + } exit: return error; } -otError TcpExample::ProcessBenchmark(Arg aArgs[]) +template <> otError TcpExample::Process(Arg aArgs[]) { - otError error = OT_ERROR_NONE; - uint32_t toSendOut; + otError error = OT_ERROR_NONE; VerifyOrExit(!mSendBusy, error = OT_ERROR_BUSY); VerifyOrExit(mBenchmarkBytesTotal == 0, error = OT_ERROR_BUSY); @@ -230,34 +391,41 @@ otError TcpExample::ProcessBenchmark(Arg aArgs[]) } VerifyOrExit(aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); - memset(mSendBuffer, 'a', sizeof(mSendBuffer)); + mBenchmarkStart = TimerMilli::GetNow(); + mBenchmarkBytesUnsent = mBenchmarkBytesTotal; - mBenchmarkLinksLeft = (mBenchmarkBytesTotal + sizeof(mSendBuffer) - 1) / sizeof(mSendBuffer); - toSendOut = OT_MIN(OT_ARRAY_LENGTH(mBenchmarkLinks), mBenchmarkLinksLeft); - mBenchmarkStart = TimerMilli::GetNow(); - for (uint32_t i = 0; i != toSendOut; i++) + if (mUseCircularSendBuffer) { - mBenchmarkLinks[i].mNext = nullptr; - mBenchmarkLinks[i].mData = mSendBuffer; - mBenchmarkLinks[i].mLength = sizeof(mSendBuffer); - if (i == 0 && mBenchmarkBytesTotal % sizeof(mSendBuffer) != 0) + SuccessOrExit(error = ContinueBenchmarkCircularSend()); + } + else + { + uint32_t benchmarkLinksLeft = (mBenchmarkBytesTotal + sizeof(mSendBufferBytes) - 1) / sizeof(mSendBufferBytes); + uint32_t toSendOut = OT_MIN(OT_ARRAY_LENGTH(mBenchmarkLinks), benchmarkLinksLeft); + + /* We could also point the linked buffers directly to sBenchmarkData. */ + memset(mSendBufferBytes, 'a', sizeof(mSendBufferBytes)); + + for (uint32_t i = 0; i != toSendOut; i++) { - mBenchmarkLinks[i].mLength = mBenchmarkBytesTotal % sizeof(mSendBuffer); + mBenchmarkLinks[i].mNext = nullptr; + mBenchmarkLinks[i].mData = mSendBufferBytes; + mBenchmarkLinks[i].mLength = sizeof(mSendBufferBytes); + if (i == 0 && mBenchmarkBytesTotal % sizeof(mSendBufferBytes) != 0) + { + mBenchmarkLinks[i].mLength = mBenchmarkBytesTotal % sizeof(mSendBufferBytes); + } + error = otTcpSendByReference(&mEndpoint, &mBenchmarkLinks[i], + i == toSendOut - 1 ? 0 : OT_TCP_SEND_MORE_TO_COME); + VerifyOrExit(error == OT_ERROR_NONE, mBenchmarkBytesTotal = 0); } - SuccessOrExit(error = otTcpSendByReference(&mEndpoint, &mBenchmarkLinks[i], - i == toSendOut - 1 ? 0 : OT_TCP_SEND_MORE_TO_COME)); } exit: - if (error != OT_ERROR_NONE) - { - mBenchmarkBytesTotal = 0; - mBenchmarkLinksLeft = 0; - } return error; } -otError TcpExample::ProcessSendEnd(Arg aArgs[]) +template <> otError TcpExample::Process(Arg aArgs[]) { otError error; @@ -270,7 +438,7 @@ otError TcpExample::ProcessSendEnd(Arg aArgs[]) return error; } -otError TcpExample::ProcessAbort(Arg aArgs[]) +template <> otError TcpExample::Process(Arg aArgs[]) { otError error; @@ -284,7 +452,7 @@ otError TcpExample::ProcessAbort(Arg aArgs[]) return error; } -otError TcpExample::ProcessListen(Arg aArgs[]) +template <> otError TcpExample::Process(Arg aArgs[]) { otError error; otSockAddr sockaddr; @@ -302,7 +470,7 @@ otError TcpExample::ProcessListen(Arg aArgs[]) return error; } -otError TcpExample::ProcessStopListening(Arg aArgs[]) +template <> otError TcpExample::Process(Arg aArgs[]) { otError error; @@ -317,13 +485,29 @@ otError TcpExample::ProcessStopListening(Arg aArgs[]) otError TcpExample::Process(Arg aArgs[]) { - otError error = OT_ERROR_INVALID_ARGS; +#define CmdEntry(aCommandString) \ + { \ + aCommandString, &TcpExample::Process \ + } + + static constexpr Command kCommands[] = { + CmdEntry("abort"), CmdEntry("benchmark"), CmdEntry("bind"), CmdEntry("connect"), CmdEntry("deinit"), + CmdEntry("init"), CmdEntry("listen"), CmdEntry("send"), CmdEntry("sendend"), CmdEntry("stoplistening"), + }; + + static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); + + otError error = OT_ERROR_INVALID_COMMAND; const Command *command; - VerifyOrExit(!aArgs[0].IsEmpty(), IgnoreError(ProcessHelp(nullptr))); + if (aArgs[0].IsEmpty() || (aArgs[0] == "help")) + { + OutputCommandTable(kCommands); + ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE); + } - command = BinarySearch::Find(aArgs[0].GetCString(), sCommands); - VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND); + command = BinarySearch::Find(aArgs[0].GetCString(), kCommands); + VerifyOrExit(command != nullptr); error = (this->*command->mHandler)(aArgs + 1); @@ -341,6 +525,12 @@ void TcpExample::HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuf static_cast(otTcpEndpointGetContext(aEndpoint))->HandleTcpSendDone(aEndpoint, aData); } +void TcpExample::HandleTcpForwardProgressCallback(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog) +{ + static_cast(otTcpEndpointGetContext(aEndpoint)) + ->HandleTcpForwardProgress(aEndpoint, aInSendBuffer, aBacklog); +} + void TcpExample::HandleTcpReceiveAvailableCallback(otTcpEndpoint *aEndpoint, size_t aBytesAvailable, bool aEndOfStream, @@ -355,16 +545,16 @@ void TcpExample::HandleTcpDisconnectedCallback(otTcpEndpoint *aEndpoint, otTcpDi static_cast(otTcpEndpointGetContext(aEndpoint))->HandleTcpDisconnected(aEndpoint, aReason); } -otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReadyCallback(otTcpListener * aListener, +otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReadyCallback(otTcpListener *aListener, const otSockAddr *aPeer, - otTcpEndpoint ** aAcceptInto) + otTcpEndpoint **aAcceptInto) { return static_cast(otTcpListenerGetContext(aListener)) ->HandleTcpAcceptReady(aListener, aPeer, aAcceptInto); } -void TcpExample::HandleTcpAcceptDoneCallback(otTcpListener * aListener, - otTcpEndpoint * aEndpoint, +void TcpExample::HandleTcpAcceptDoneCallback(otTcpListener *aListener, + otTcpEndpoint *aEndpoint, const otSockAddr *aPeer) { static_cast(otTcpListenerGetContext(aListener))->HandleTcpAcceptDone(aListener, aEndpoint, aPeer); @@ -374,11 +564,33 @@ void TcpExample::HandleTcpEstablished(otTcpEndpoint *aEndpoint) { OT_UNUSED_VARIABLE(aEndpoint); OutputLine("TCP: Connection established"); +#if OPENTHREAD_CONFIG_TLS_ENABLE + if (mUseTls) + { + int rv; + rv = mbedtls_ssl_set_hostname(&mSslContext, "localhost"); + if (rv != 0) + { + OutputLine("mbedtls_ssl_set_hostname returned %d", rv); + } + rv = mbedtls_ssl_set_hs_ecjpake_password( + &mSslContext, reinterpret_cast(sEcjpakePassword), sEcjpakePasswordLength); + if (rv != 0) + { + OutputLine("mbedtls_ssl_set_hs_ecjpake_password returned %d", rv); + } + mbedtls_ssl_set_bio(&mSslContext, &mEndpointAndCircularSendBuffer, otTcpMbedTlsSslSendCallback, + otTcpMbedTlsSslRecvCallback, nullptr); + mTlsHandshakeComplete = false; + ContinueTLSHandshake(); + } +#endif // OPENTHREAD_CONFIG_TLS_ENABLE } void TcpExample::HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData) { OT_UNUSED_VARIABLE(aEndpoint); + OT_ASSERT(!mUseCircularSendBuffer); // this callback is not used when using the circular send buffer if (mBenchmarkBytesTotal == 0) { @@ -393,25 +605,51 @@ void TcpExample::HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aDa else { OT_ASSERT(aData != &mSendLink); - mBenchmarkLinksLeft--; - if (mBenchmarkLinksLeft >= OT_ARRAY_LENGTH(mBenchmarkLinks)) + OT_ASSERT(mBenchmarkBytesUnsent >= aData->mLength); + mBenchmarkBytesUnsent -= aData->mLength; // could be less than sizeof(mSendBufferBytes) for the first link + if (mBenchmarkBytesUnsent >= OT_ARRAY_LENGTH(mBenchmarkLinks) * sizeof(mSendBufferBytes)) { - aData->mLength = sizeof(mSendBuffer); + aData->mLength = sizeof(mSendBufferBytes); if (otTcpSendByReference(&mEndpoint, aData, 0) != OT_ERROR_NONE) { OutputLine("TCP Benchmark Failed"); mBenchmarkBytesTotal = 0; } } - else if (mBenchmarkLinksLeft == 0) + else if (mBenchmarkBytesUnsent == 0) { - uint32_t milliseconds = TimerMilli::GetNow() - mBenchmarkStart; - uint32_t thousandTimesGoodput = (1000 * (mBenchmarkBytesTotal << 3) + (milliseconds >> 1)) / milliseconds; + CompleteBenchmark(); + } + } +} - OutputLine("TCP Benchmark Complete: Transferred %u bytes in %u milliseconds", - static_cast(mBenchmarkBytesTotal), static_cast(milliseconds)); - OutputLine("TCP Goodput: %u.%03u kb/s", thousandTimesGoodput / 1000, thousandTimesGoodput % 1000); - mBenchmarkBytesTotal = 0; +void TcpExample::HandleTcpForwardProgress(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog) +{ + OT_UNUSED_VARIABLE(aEndpoint); + OT_UNUSED_VARIABLE(aBacklog); + OT_ASSERT(mUseCircularSendBuffer); // this callback is only used when using the circular send buffer + + otTcpCircularSendBufferHandleForwardProgress(&mSendBuffer, aInSendBuffer); + +#if OPENTHREAD_CONFIG_TLS_ENABLE + if (mUseTls) + { + ContinueTLSHandshake(); + } +#endif + + /* Handle case where we're in a benchmark. */ + if (mBenchmarkBytesTotal != 0) + { + if (mBenchmarkBytesUnsent != 0) + { + /* Continue sending out data if there's data we haven't sent. */ + IgnoreError(ContinueBenchmarkCircularSend()); + } + else if (aInSendBuffer == 0) + { + /* Handle case where all data is sent out and the send buffer has drained. */ + CompleteBenchmark(); } } } @@ -424,20 +662,53 @@ void TcpExample::HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint, OT_UNUSED_VARIABLE(aBytesRemaining); OT_ASSERT(aEndpoint == &mEndpoint); - if (aBytesAvailable > 0) +#if OPENTHREAD_CONFIG_TLS_ENABLE + if (mUseTls && ContinueTLSHandshake()) { - const otLinkedBuffer *data; - size_t totalReceived = 0; + return; + } +#endif - IgnoreError(otTcpReceiveByReference(aEndpoint, &data)); - for (; data != nullptr; data = data->mNext) + if ((mTlsHandshakeComplete || !mUseTls) && aBytesAvailable > 0) + { +#if OPENTHREAD_CONFIG_TLS_ENABLE + if (mUseTls) { - OutputLine("TCP: Received %u bytes: %.*s", static_cast(data->mLength), data->mLength, - reinterpret_cast(data->mData)); - totalReceived += data->mLength; + uint8_t buffer[500]; + for (;;) + { + int rv = mbedtls_ssl_read(&mSslContext, buffer, sizeof(buffer)); + if (rv < 0) + { + if (rv == MBEDTLS_ERR_SSL_WANT_READ) + { + break; + } + OutputLine("TLS receive failure: %d", rv); + } + else + { + OutputLine("TLS: Received %u bytes: %.*s", static_cast(rv), rv, + reinterpret_cast(buffer)); + } + } + OutputLine("(TCP: Received %u bytes)", static_cast(aBytesAvailable)); + } + else +#endif // OPENTHREAD_CONFIG_TLS_ENABLE + { + const otLinkedBuffer *data; + size_t totalReceived = 0; + IgnoreError(otTcpReceiveByReference(aEndpoint, &data)); + for (; data != nullptr; data = data->mNext) + { + OutputLine("TCP: Received %u bytes: %.*s", static_cast(data->mLength), + static_cast(data->mLength), reinterpret_cast(data->mData)); + totalReceived += data->mLength; + } + OT_ASSERT(aBytesAvailable == totalReceived); + IgnoreReturnValue(otTcpCommitReceive(aEndpoint, totalReceived, 0)); } - OT_ASSERT(aBytesAvailable == totalReceived); - IgnoreReturnValue(otTcpCommitReceive(aEndpoint, totalReceived, 0)); } if (aEndOfStream) @@ -466,6 +737,13 @@ void TcpExample::HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnect OutputLine("TCP: %s", Stringify(aReason, kReasonStrings)); +#if OPENTHREAD_CONFIG_TLS_ENABLE + if (mUseTls) + { + mbedtls_ssl_session_reset(&mSslContext); + } +#endif + // We set this to false even for the TIME-WAIT state, so that we can reuse // the active socket if an incoming connection comes in instead of waiting // for the 2MSL timeout. @@ -473,17 +751,18 @@ void TcpExample::HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnect mSendBusy = false; // Mark the benchmark as inactive if the connection was disconnected. - if (mBenchmarkBytesTotal != 0) - { - mBenchmarkBytesTotal = 0; - mBenchmarkLinksLeft = 0; - } + mBenchmarkBytesTotal = 0; + mBenchmarkBytesUnsent = 0; + + otTcpCircularSendBufferForceDiscardAll(&mSendBuffer); } -otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReady(otTcpListener * aListener, +otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReady(otTcpListener *aListener, const otSockAddr *aPeer, - otTcpEndpoint ** aAcceptInto) + otTcpEndpoint **aAcceptInto) { + otTcpIncomingConnectionAction action; + OT_UNUSED_VARIABLE(aListener); if (mEndpointConnected) @@ -492,11 +771,14 @@ otTcpIncomingConnectionAction TcpExample::HandleTcpAcceptReady(otTcpListener * OutputSockAddr(*aPeer); OutputLine(" (active socket is busy)"); - return OT_TCP_INCOMING_CONNECTION_ACTION_DEFER; + ExitNow(action = OT_TCP_INCOMING_CONNECTION_ACTION_DEFER); } *aAcceptInto = &mEndpoint; - return OT_TCP_INCOMING_CONNECTION_ACTION_ACCEPT; + action = OT_TCP_INCOMING_CONNECTION_ACTION_ACCEPT; + +exit: + return action; } void TcpExample::HandleTcpAcceptDone(otTcpListener *aListener, otTcpEndpoint *aEndpoint, const otSockAddr *aPeer) @@ -504,9 +786,116 @@ void TcpExample::HandleTcpAcceptDone(otTcpListener *aListener, otTcpEndpoint *aE OT_UNUSED_VARIABLE(aListener); OT_UNUSED_VARIABLE(aEndpoint); + mEndpointConnected = true; OutputFormat("Accepted connection from "); OutputSockAddrLine(*aPeer); + +#if OPENTHREAD_CONFIG_TLS_ENABLE + if (mUseTls) + { + int rv; + + rv = mbedtls_ssl_config_defaults(&mSslConfig, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT); + if (rv != 0) + { + OutputLine("mbedtls_ssl_config_defaults returned %d", rv); + } + mbedtls_ssl_conf_ca_chain(&mSslConfig, mSrvCert.next, nullptr); + rv = mbedtls_ssl_conf_own_cert(&mSslConfig, &mSrvCert, &mPKey); + if (rv != 0) + { + OutputLine("mbedtls_ssl_conf_own_cert returned %d", rv); + } + } +#endif // OPENTHREAD_CONFIG_TLS_ENABLE +} + +otError TcpExample::ContinueBenchmarkCircularSend(void) +{ + otError error = OT_ERROR_NONE; + size_t freeSpace; + + while (mBenchmarkBytesUnsent != 0 && (freeSpace = otTcpCircularSendBufferGetFreeSpace(&mSendBuffer)) != 0) + { + size_t toSendThisIteration = OT_MIN(mBenchmarkBytesUnsent, sBenchmarkDataLength); + uint32_t flag = (toSendThisIteration < freeSpace && toSendThisIteration < mBenchmarkBytesUnsent) + ? OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME + : 0; + size_t written = 0; + +#if OPENTHREAD_CONFIG_TLS_ENABLE + if (mUseTls) + { + int rv = mbedtls_ssl_write(&mSslContext, reinterpret_cast(sBenchmarkData), + toSendThisIteration); + if (rv > 0) + { + written = static_cast(rv); + OT_ASSERT(written <= mBenchmarkBytesUnsent); + } + else if (rv != MBEDTLS_ERR_SSL_WANT_WRITE && rv != MBEDTLS_ERR_SSL_WANT_READ) + { + ExitNow(error = kErrorFailed); + } + error = kErrorNone; + } + else +#endif // OPENTHREAD_CONFIG_TLS_ENABLE + { + SuccessOrExit(error = otTcpCircularSendBufferWrite(&mEndpoint, &mSendBuffer, sBenchmarkData, + toSendThisIteration, &written, flag)); + } + mBenchmarkBytesUnsent -= written; + } + +exit: + if (error != OT_ERROR_NONE) + { + OutputLine("TCP Benchmark Failed"); + mBenchmarkBytesTotal = 0; + mBenchmarkBytesUnsent = 0; + } + + return error; +} + +void TcpExample::CompleteBenchmark(void) +{ + uint32_t milliseconds = TimerMilli::GetNow() - mBenchmarkStart; + uint32_t thousandTimesGoodput = (1000 * (mBenchmarkBytesTotal << 3) + (milliseconds >> 1)) / milliseconds; + + OutputLine("TCP Benchmark Complete: Transferred %lu bytes in %lu milliseconds", ToUlong(mBenchmarkBytesTotal), + ToUlong(milliseconds)); + OutputLine("TCP Goodput: %lu.%03u kb/s", ToUlong(thousandTimesGoodput / 1000), + static_cast(thousandTimesGoodput % 1000)); + mBenchmarkBytesTotal = 0; +} + +#if OPENTHREAD_CONFIG_TLS_ENABLE +bool TcpExample::ContinueTLSHandshake(void) +{ + bool wasNotAlreadyDone = false; + int rv; + + if (!mTlsHandshakeComplete) + { + rv = mbedtls_ssl_handshake(&mSslContext); + if (rv == 0) + { + OutputLine("TLS Handshake Complete"); + mTlsHandshakeComplete = true; + } + else if (rv != MBEDTLS_ERR_SSL_WANT_READ && rv != MBEDTLS_ERR_SSL_WANT_WRITE) + { + OutputLine("TLS Handshake Failed: %d", rv); + } + wasNotAlreadyDone = true; + } + + return wasNotAlreadyDone; } +#endif // OPENTHREAD_CONFIG_TLS_ENABLE } // namespace Cli } // namespace ot diff --git a/src/cli/cli_tcp.hpp b/src/cli/cli_tcp.hpp index 4bebc963222..9af459dbed3 100644 --- a/src/cli/cli_tcp.hpp +++ b/src/cli/cli_tcp.hpp @@ -37,6 +37,16 @@ #include "openthread-core-config.h" #include +#include + +#if OPENTHREAD_CONFIG_TLS_ENABLE + +#include +#include +#include +#include + +#endif #include "cli/cli_config.h" #include "cli/cli_output.hpp" @@ -49,7 +59,7 @@ namespace Cli { * This class implements a CLI-based TCP example. * */ -class TcpExample : private OutputWrapper +class TcpExample : private Output { public: using Arg = Utils::CmdLineParser::Arg; @@ -57,10 +67,11 @@ class TcpExample : private OutputWrapper /** * Constructor * - * @param[in] aOutput The CLI console output context. + * @param[in] aInstance The OpenThread Instance. + * @param[in] aOutputImplementer An `OutputImplementer`. * */ - explicit TcpExample(Output &aOutput); + TcpExample(otInstance *aInstance, OutputImplementer &aOutputImplementer); /** * This method interprets a list of CLI arguments. @@ -73,59 +84,46 @@ class TcpExample : private OutputWrapper private: using Command = CommandEntry; - otError ProcessHelp(Arg aArgs[]); - otError ProcessInit(Arg aArgs[]); - otError ProcessDeinit(Arg aArgs[]); - otError ProcessBind(Arg aArgs[]); - otError ProcessConnect(Arg aArgs[]); - otError ProcessSend(Arg aArgs[]); - otError ProcessBenchmark(Arg aArgs[]); - otError ProcessSendEnd(Arg aArgs[]); - otError ProcessAbort(Arg aArgs[]); - otError ProcessListen(Arg aArgs[]); - otError ProcessStopListening(Arg aArgs[]); + template otError Process(Arg aArgs[]); + + otError ContinueBenchmarkCircularSend(void); + void CompleteBenchmark(void); + +#if OPENTHREAD_CONFIG_TLS_ENABLE + bool ContinueTLSHandshake(void); +#endif static void HandleTcpEstablishedCallback(otTcpEndpoint *aEndpoint); static void HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData); + static void HandleTcpForwardProgressCallback(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog); static void HandleTcpReceiveAvailableCallback(otTcpEndpoint *aEndpoint, size_t aBytesAvailable, bool aEndOfStream, size_t aBytesRemaining); static void HandleTcpDisconnectedCallback(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason); - static otTcpIncomingConnectionAction HandleTcpAcceptReadyCallback(otTcpListener * aListener, + static otTcpIncomingConnectionAction HandleTcpAcceptReadyCallback(otTcpListener *aListener, const otSockAddr *aPeer, - otTcpEndpoint ** aAcceptInto); - static void HandleTcpAcceptDoneCallback(otTcpListener * aListener, - otTcpEndpoint * aEndpoint, + otTcpEndpoint **aAcceptInto); + static void HandleTcpAcceptDoneCallback(otTcpListener *aListener, + otTcpEndpoint *aEndpoint, const otSockAddr *aPeer); - void HandleTcpEstablished(otTcpEndpoint *aEndpoint); - void HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData); - void HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint, - size_t aBytesAvailable, - bool aEndOfStream, - size_t aBytesRemaining); - void HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason); - otTcpIncomingConnectionAction HandleTcpAcceptReady(otTcpListener * aListener, + void HandleTcpEstablished(otTcpEndpoint *aEndpoint); + void HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData); + void HandleTcpForwardProgress(otTcpEndpoint *aEndpoint, size_t aInSendBuffer, size_t aBacklog); + void HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint, + size_t aBytesAvailable, + bool aEndOfStream, + size_t aBytesRemaining); + void HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason); + otTcpIncomingConnectionAction HandleTcpAcceptReady(otTcpListener *aListener, const otSockAddr *aPeer, - otTcpEndpoint ** aAcceptInto); + otTcpEndpoint **aAcceptInto); void HandleTcpAcceptDone(otTcpListener *aListener, otTcpEndpoint *aEndpoint, const otSockAddr *aPeer); - static constexpr Command sCommands[] = { - {"abort", &TcpExample::ProcessAbort}, - {"benchmark", &TcpExample::ProcessBenchmark}, - {"bind", &TcpExample::ProcessBind}, - {"connect", &TcpExample::ProcessConnect}, - {"deinit", &TcpExample::ProcessDeinit}, - {"help", &TcpExample::ProcessHelp}, - {"init", &TcpExample::ProcessInit}, - {"listen", &TcpExample::ProcessListen}, - {"send", &TcpExample::ProcessSend}, - {"sendend", &TcpExample::ProcessSendEnd}, - {"stoplistening", &TcpExample::ProcessStopListening}, - }; - - static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted"); +#if OPENTHREAD_CONFIG_TLS_ENABLE + static void MbedTlsDebugOutput(void *ctx, int level, const char *file, int line, const char *str); +#endif otTcpEndpoint mEndpoint; otTcpListener mListener; @@ -133,15 +131,86 @@ class TcpExample : private OutputWrapper bool mInitialized; bool mEndpointConnected; bool mSendBusy; - - otLinkedBuffer mSendLink; - uint8_t mSendBuffer[OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH]; - uint8_t mReceiveBuffer[OPENTHREAD_CONFIG_CLI_TCP_RECEIVE_BUFFER_SIZE]; - - otLinkedBuffer mBenchmarkLinks[(sizeof(mReceiveBuffer) + sizeof(mSendBuffer) - 1) / sizeof(mSendBuffer)]; - uint32_t mBenchmarkBytesTotal; - uint32_t mBenchmarkLinksLeft; - TimeMilli mBenchmarkStart; + bool mUseCircularSendBuffer; + bool mUseTls; + bool mTlsHandshakeComplete; + + otTcpCircularSendBuffer mSendBuffer; + otLinkedBuffer mSendLink; + uint8_t mSendBufferBytes[OPENTHREAD_CONFIG_CLI_TCP_RECEIVE_BUFFER_SIZE]; + uint8_t mReceiveBufferBytes[OPENTHREAD_CONFIG_CLI_TCP_RECEIVE_BUFFER_SIZE]; + + otLinkedBuffer + mBenchmarkLinks[(sizeof(mReceiveBufferBytes) + sizeof(mSendBufferBytes) - 1) / sizeof(mSendBufferBytes)]; + uint32_t mBenchmarkBytesTotal; + uint32_t mBenchmarkBytesUnsent; + TimeMilli mBenchmarkStart; + + otTcpEndpointAndCircularSendBuffer mEndpointAndCircularSendBuffer; + +#if OPENTHREAD_CONFIG_TLS_ENABLE + mbedtls_ssl_context mSslContext; + mbedtls_ssl_config mSslConfig; + mbedtls_x509_crt mSrvCert; + mbedtls_pk_context mPKey; + mbedtls_entropy_context mEntropy; +#endif // OPENTHREAD_CONFIG_TLS_ENABLE + + static constexpr const char *sBenchmarkData = + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + static constexpr const size_t sBenchmarkDataLength = 1040; + +#if OPENTHREAD_CONFIG_TLS_ENABLE + static constexpr const char *sCasPem = "-----BEGIN CERTIFICATE-----\r\n" + "MIIBtDCCATqgAwIBAgIBTTAKBggqhkjOPQQDAjBLMQswCQYDVQQGEwJOTDERMA8G\r\n" + "A1UEChMIUG9sYXJTU0wxKTAnBgNVBAMTIFBvbGFyU1NMIFRlc3QgSW50ZXJtZWRp\r\n" + "YXRlIEVDIENBMB4XDTE1MDkwMTE0MDg0M1oXDTI1MDgyOTE0MDg0M1owSjELMAkG\r\n" + "A1UEBhMCVUsxETAPBgNVBAoTCG1iZWQgVExTMSgwJgYDVQQDEx9tYmVkIFRMUyBU\r\n" + "ZXN0IGludGVybWVkaWF0ZSBDQSAzMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\r\n" + "732fWHLNPMPsP1U1ibXvb55erlEVMlpXBGsj+KYwVqU1XCmW9Z9hhP7X/5js/DX9\r\n" + "2J/utoHyjUtVpQOzdTrbsaMQMA4wDAYDVR0TBAUwAwEB/zAKBggqhkjOPQQDAgNo\r\n" + "ADBlAjAJRxbGRas3NBmk9MnGWXg7PT1xnRELHRWWIvfLdVQt06l1/xFg3ZuPdQdt\r\n" + "Qh7CK80CMQD7wa1o1a8qyDKBfLN636uKmKGga0E+vYXBeFCy9oARBangGCB0B2vt\r\n" + "pz590JvGWfM=\r\n" + "-----END CERTIFICATE-----\r\n"; + static constexpr const size_t sCasPemLength = 665; // includes NUL byte + + static constexpr const char *sSrvPem = "-----BEGIN CERTIFICATE-----\r\n" + "MIICHzCCAaWgAwIBAgIBCTAKBggqhkjOPQQDAjA+MQswCQYDVQQGEwJOTDERMA8G\r\n" + "A1UEChMIUG9sYXJTU0wxHDAaBgNVBAMTE1BvbGFyc3NsIFRlc3QgRUMgQ0EwHhcN\r\n" + "MTMwOTI0MTU1MjA0WhcNMjMwOTIyMTU1MjA0WjA0MQswCQYDVQQGEwJOTDERMA8G\r\n" + "A1UEChMIUG9sYXJTU0wxEjAQBgNVBAMTCWxvY2FsaG9zdDBZMBMGByqGSM49AgEG\r\n" + "CCqGSM49AwEHA0IABDfMVtl2CR5acj7HWS3/IG7ufPkGkXTQrRS192giWWKSTuUA\r\n" + "2CMR/+ov0jRdXRa9iojCa3cNVc2KKg76Aci07f+jgZ0wgZowCQYDVR0TBAIwADAd\r\n" + "BgNVHQ4EFgQUUGGlj9QH2deCAQzlZX+MY0anE74wbgYDVR0jBGcwZYAUnW0gJEkB\r\n" + "PyvLeLUZvH4kydv7NnyhQqRAMD4xCzAJBgNVBAYTAk5MMREwDwYDVQQKEwhQb2xh\r\n" + "clNTTDEcMBoGA1UEAxMTUG9sYXJzc2wgVGVzdCBFQyBDQYIJAMFD4n5iQ8zoMAoG\r\n" + "CCqGSM49BAMCA2gAMGUCMQCaLFzXptui5WQN8LlO3ddh1hMxx6tzgLvT03MTVK2S\r\n" + "C12r0Lz3ri/moSEpNZWqPjkCMCE2f53GXcYLqyfyJR078c/xNSUU5+Xxl7VZ414V\r\n" + "fGa5kHvHARBPc8YAIVIqDvHH1Q==\r\n" + "-----END CERTIFICATE-----\r\n"; + static constexpr const size_t sSrvPemLength = 813; // includes NUL byte + + static constexpr const char *sSrvKey = "-----BEGIN EC PRIVATE KEY-----\r\n" + "MHcCAQEEIPEqEyB2AnCoPL/9U/YDHvdqXYbIogTywwyp6/UfDw6noAoGCCqGSM49\r\n" + "AwEHoUQDQgAEN8xW2XYJHlpyPsdZLf8gbu58+QaRdNCtFLX3aCJZYpJO5QDYIxH/\r\n" + "6i/SNF1dFr2KiMJrdw1VzYoqDvoByLTt/w==\r\n" + "-----END EC PRIVATE KEY-----\r\n"; + static constexpr const size_t sSrvKeyLength = 233; // includes NUL byte + + static constexpr const char *sEcjpakePassword = "TLS-over-TCPlp"; + static constexpr const size_t sEcjpakePasswordLength = 14; + static const int sCipherSuites[]; +#endif // OPENTHREAD_CONFIG_TLS_ENABLE }; } // namespace Cli diff --git a/src/cli/cli_udp.cpp b/src/cli/cli_udp.cpp index 62ad05790ad..d5f37928855 100644 --- a/src/cli/cli_udp.cpp +++ b/src/cli/cli_udp.cpp @@ -34,6 +34,7 @@ #include "cli_udp.hpp" #include +#include #include #include "cli/cli.hpp" @@ -42,28 +43,14 @@ namespace ot { namespace Cli { -constexpr UdpExample::Command UdpExample::sCommands[]; - -UdpExample::UdpExample(Output &aOutput) - : OutputWrapper(aOutput) +UdpExample::UdpExample(otInstance *aInstance, OutputImplementer &aOutputImplementer) + : Output(aInstance, aOutputImplementer) , mLinkSecurityEnabled(true) { memset(&mSocket, 0, sizeof(mSocket)); } -otError UdpExample::ProcessHelp(Arg aArgs[]) -{ - OT_UNUSED_VARIABLE(aArgs); - - for (const Command &command : sCommands) - { - OutputLine(command.mName); - } - - return OT_ERROR_NONE; -} - -otError UdpExample::ProcessBind(Arg aArgs[]) +template <> otError UdpExample::Process(Arg aArgs[]) { otError error; otSockAddr sockaddr; @@ -90,12 +77,20 @@ otError UdpExample::ProcessBind(Arg aArgs[]) return error; } -otError UdpExample::ProcessConnect(Arg aArgs[]) +template <> otError UdpExample::Process(Arg aArgs[]) { otError error; otSockAddr sockaddr; + bool nat64SynthesizedAddress; + + SuccessOrExit( + error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], sockaddr.mAddress, nat64SynthesizedAddress)); + if (nat64SynthesizedAddress) + { + OutputFormat("Connecting to synthesized IPv6 address: "); + OutputIp6AddressLine(sockaddr.mAddress); + } - SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress)); SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort)); VerifyOrExit(aArgs[2].IsEmpty(), error = OT_ERROR_INVALID_ARGS); @@ -105,14 +100,14 @@ otError UdpExample::ProcessConnect(Arg aArgs[]) return error; } -otError UdpExample::ProcessClose(Arg aArgs[]) +template <> otError UdpExample::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); return otUdpClose(GetInstancePtr(), &mSocket); } -otError UdpExample::ProcessOpen(Arg aArgs[]) +template <> otError UdpExample::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); @@ -125,10 +120,10 @@ otError UdpExample::ProcessOpen(Arg aArgs[]) return error; } -otError UdpExample::ProcessSend(Arg aArgs[]) +template <> otError UdpExample::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; - otMessage * message = nullptr; + otMessage *message = nullptr; otMessageInfo messageInfo; otMessageSettings messageSettings = {mLinkSecurityEnabled, OT_MESSAGE_PRIORITY_NORMAL}; @@ -143,7 +138,16 @@ otError UdpExample::ProcessSend(Arg aArgs[]) if (!aArgs[2].IsEmpty()) { - SuccessOrExit(error = aArgs[0].ParseAsIp6Address(messageInfo.mPeerAddr)); + bool nat64SynthesizedAddress; + + SuccessOrExit(error = Interpreter::ParseToIp6Address(GetInstancePtr(), aArgs[0], messageInfo.mPeerAddr, + nat64SynthesizedAddress)); + if (nat64SynthesizedAddress) + { + OutputFormat("Sending to synthesized IPv6 address: "); + OutputIp6AddressLine(messageInfo.mPeerAddr); + } + SuccessOrExit(error = aArgs[1].ParseAsUint16(messageInfo.mPeerPort)); aArgs += 2; } @@ -165,7 +169,7 @@ otError UdpExample::ProcessSend(Arg aArgs[]) // Binary hex data payload VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS); - SuccessOrExit(error = PrepareHexStringPaylod(*message, aArgs[1].GetCString())); + SuccessOrExit(error = PrepareHexStringPayload(*message, aArgs[1].GetCString())); } else { @@ -193,7 +197,7 @@ otError UdpExample::ProcessSend(Arg aArgs[]) return error; } -otError UdpExample::ProcessLinkSecurity(Arg aArgs[]) +template <> otError UdpExample::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -239,7 +243,7 @@ otError UdpExample::PrepareAutoGeneratedPayload(otMessage &aMessage, uint16_t aP return error; } -otError UdpExample::PrepareHexStringPaylod(otMessage &aMessage, const char *aHexString) +otError UdpExample::PrepareHexStringPayload(otMessage &aMessage, const char *aHexString) { enum : uint8_t { @@ -268,17 +272,29 @@ otError UdpExample::PrepareHexStringPaylod(otMessage &aMessage, const char *aHex otError UdpExample::Process(Arg aArgs[]) { - otError error = OT_ERROR_INVALID_ARGS; +#define CmdEntry(aCommandString) \ + { \ + aCommandString, &UdpExample::Process \ + } + + static constexpr Command kCommands[] = { + CmdEntry("bind"), CmdEntry("close"), CmdEntry("connect"), + CmdEntry("linksecurity"), CmdEntry("open"), CmdEntry("send"), + }; + + static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); + + otError error = OT_ERROR_INVALID_COMMAND; const Command *command; - if (aArgs[0].IsEmpty()) + if (aArgs[0].IsEmpty() || (aArgs[0] == "help")) { - IgnoreError(ProcessHelp(aArgs)); - ExitNow(); + OutputCommandTable(kCommands); + ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE); } - command = BinarySearch::Find(aArgs[0].GetCString(), sCommands); - VerifyOrExit(command != nullptr, error = OT_ERROR_INVALID_COMMAND); + command = BinarySearch::Find(aArgs[0].GetCString(), kCommands); + VerifyOrExit(command != nullptr); error = (this->*command->mHandler)(aArgs + 1); diff --git a/src/cli/cli_udp.hpp b/src/cli/cli_udp.hpp index bd8ba5462c8..55996c7ec82 100644 --- a/src/cli/cli_udp.hpp +++ b/src/cli/cli_udp.hpp @@ -47,7 +47,7 @@ namespace Cli { * This class implements a CLI-based UDP example. * */ -class UdpExample : private OutputWrapper +class UdpExample : private Output { public: typedef Utils::CmdLineParser::Arg Arg; @@ -55,10 +55,11 @@ class UdpExample : private OutputWrapper /** * Constructor * - * @param[in] aOutput The CLI console output context. + * @param[in] aInstance The OpenThread Instance. + * @param[in] aOutputImplementer An `OutputImplementer`. * */ - explicit UdpExample(Output &aOutput); + UdpExample(otInstance *aInstance, OutputImplementer &aOutputImplementer); /** * This method interprets a list of CLI arguments. @@ -71,32 +72,14 @@ class UdpExample : private OutputWrapper private: using Command = CommandEntry; - otError ProcessHelp(Arg aArgs[]); - otError ProcessBind(Arg aArgs[]); - otError ProcessClose(Arg aArgs[]); - otError ProcessConnect(Arg aArgs[]); - otError ProcessOpen(Arg aArgs[]); - otError ProcessSend(Arg aArgs[]); - otError ProcessLinkSecurity(Arg aArgs[]); + template otError Process(Arg aArgs[]); static otError PrepareAutoGeneratedPayload(otMessage &aMessage, uint16_t aPayloadLength); - static otError PrepareHexStringPaylod(otMessage &aMessage, const char *aHexString); + static otError PrepareHexStringPayload(otMessage &aMessage, const char *aHexString); static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); void HandleUdpReceive(otMessage *aMessage, const otMessageInfo *aMessageInfo); - static constexpr Command sCommands[] = { - {"bind", &UdpExample::ProcessBind}, - {"close", &UdpExample::ProcessClose}, - {"connect", &UdpExample::ProcessConnect}, - {"help", &UdpExample::ProcessHelp}, - {"linksecurity", &UdpExample::ProcessLinkSecurity}, - {"open", &UdpExample::ProcessOpen}, - {"send", &UdpExample::ProcessSend}, - }; - - static_assert(BinarySearch::IsSorted(sCommands), "Command Table is not sorted"); - bool mLinkSecurityEnabled; otUdpSocket mSocket; }; diff --git a/src/cli/ftd.cmake b/src/cli/ftd.cmake index a101da342ed..22f1e11f682 100644 --- a/src/cli/ftd.cmake +++ b/src/cli/ftd.cmake @@ -46,5 +46,6 @@ target_link_libraries(openthread-cli-ftd openthread-ftd PRIVATE ${OT_MBEDTLS} + ot-config-ftd ot-config ) diff --git a/src/cli/mtd.cmake b/src/cli/mtd.cmake index c8c42305db7..3a875c39d45 100644 --- a/src/cli/mtd.cmake +++ b/src/cli/mtd.cmake @@ -46,5 +46,6 @@ target_link_libraries(openthread-cli-mtd openthread-mtd PRIVATE ${OT_MBEDTLS} + ot-config-mtd ot-config ) diff --git a/src/cli/radio.cmake b/src/cli/radio.cmake index 3e7354aa693..6fa0b9144e4 100644 --- a/src/cli/radio.cmake +++ b/src/cli/radio.cmake @@ -55,5 +55,6 @@ target_link_libraries(openthread-cli-radio openthread-radio PRIVATE ${OT_MBEDTLS_RCP} + ot-config-radio ot-config ) diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 299059f39c9..d7f9f63a54d 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -39,6 +39,8 @@ if (openthread_enable_core_config_args) { 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 == "1.3.1") { + defines += [ "OPENTHREAD_CONFIG_THREAD_VERSION=OT_THREAD_VERSION_1_3_1" ] } else if (openthread_config_thread_version != "") { assert(false, "Unrecognized Thread version: ${openthread_config_thread_version}") @@ -98,10 +100,6 @@ if (openthread_enable_core_config_args) { defines += [ "OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE=1" ] } - if (openthread_config_child_supervision_enable) { - defines += [ "OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE=1" ] - } - if (openthread_config_coap_api_enable) { defines += [ "OPENTHREAD_CONFIG_COAP_API_ENABLE=1" ] } @@ -170,10 +168,6 @@ if (openthread_enable_core_config_args) { defines += [ "OPENTHREAD_CONFIG_JOINER_ENABLE=1" ] } - if (openthread_config_legacy_enable) { - defines += [ "OPENTHREAD_CONFIG_LEGACY_ENABLE=1" ] - } - if (openthread_config_link_metrics_initiator_enable) { defines += [ "DOPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE=1" ] } @@ -202,18 +196,19 @@ if (openthread_enable_core_config_args) { defines += [ "OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE=1" ] } - if (openthread_config_tmf_network_diag_mtd_enable) { - defines += [ "OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE=1" ] - } - if (openthread_config_multiple_instance_enable) { defines += [ "OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE=1" ] } + if (openthread_config_tmf_netdiag_client_enable) { + defines += [ "OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE=1" ] + } + if (openthread_config_platform_netif_enable) { defines += [ "OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE=1" ] } + if (openthread_config_platform_udp_enable) { defines += [ "OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE=1" ] } @@ -334,8 +329,10 @@ openthread_core_files = [ "api/link_metrics_api.cpp", "api/link_raw_api.cpp", "api/logging_api.cpp", + "api/mesh_diag_api.cpp", "api/message_api.cpp", "api/multi_radio_api.cpp", + "api/nat64_api.cpp", "api/netdata_api.cpp", "api/netdata_publisher_api.cpp", "api/netdiag_api.cpp", @@ -350,6 +347,7 @@ openthread_core_files = [ "api/srp_server_api.cpp", "api/tasklet_api.cpp", "api/tcp_api.cpp", + "api/tcp_ext_api.cpp", "api/thread_api.cpp", "api/thread_ftd_api.cpp", "api/trel_api.cpp", @@ -384,6 +382,7 @@ openthread_core_files = [ "common/binary_search.cpp", "common/binary_search.hpp", "common/bit_vector.hpp", + "common/callback.hpp", "common/clearable.hpp", "common/code_utils.hpp", "common/const_cast.hpp", @@ -424,10 +423,13 @@ openthread_core_files = [ "common/non_copyable.hpp", "common/notifier.cpp", "common/notifier.hpp", + "common/num_utils.hpp", "common/numeric_limits.hpp", "common/owned_ptr.hpp", "common/owning_list.hpp", "common/pool.hpp", + "common/preference.cpp", + "common/preference.hpp", "common/ptr_wrapper.hpp", "common/random.cpp", "common/random.hpp", @@ -458,17 +460,13 @@ openthread_core_files = [ "crypto/aes_ecb.hpp", "crypto/context_size.hpp", "crypto/crypto_platform.cpp", - "crypto/ecdsa.cpp", "crypto/ecdsa.hpp", - "crypto/ecdsa_tinycrypt.cpp", "crypto/hkdf_sha256.cpp", "crypto/hkdf_sha256.hpp", "crypto/hmac_sha256.cpp", "crypto/hmac_sha256.hpp", "crypto/mbedtls.cpp", "crypto/mbedtls.hpp", - "crypto/pbkdf2_cmac.cpp", - "crypto/pbkdf2_cmac.hpp", "crypto/sha256.cpp", "crypto/sha256.hpp", "crypto/storage.cpp", @@ -544,6 +542,7 @@ openthread_core_files = [ "net/dns_client.hpp", "net/dns_dso.cpp", "net/dns_dso.hpp", + "net/dns_platform.cpp", "net/dns_types.cpp", "net/dns_types.hpp", "net/dnssd_server.cpp", @@ -563,6 +562,8 @@ openthread_core_files = [ "net/ip6_mpl.cpp", "net/ip6_mpl.hpp", "net/ip6_types.hpp", + "net/nat64_translator.cpp", + "net/nat64_translator.hpp", "net/nd6.cpp", "net/nd6.hpp", "net/nd_agent.cpp", @@ -579,6 +580,8 @@ openthread_core_files = [ "net/srp_server.hpp", "net/tcp6.cpp", "net/tcp6.hpp", + "net/tcp6_ext.cpp", + "net/tcp6_ext.hpp", "net/udp6.cpp", "net/udp6.hpp", "radio/max_power_table.hpp", @@ -601,6 +604,8 @@ openthread_core_files = [ "thread/anycast_locator.cpp", "thread/anycast_locator.hpp", "thread/child_mask.hpp", + "thread/child_supervision.cpp", + "thread/child_supervision.hpp", "thread/child_table.cpp", "thread/child_table.hpp", "thread/csl_tx_scheduler.cpp", @@ -619,6 +624,8 @@ openthread_core_files = [ "thread/link_metrics.cpp", "thread/link_metrics.hpp", "thread/link_metrics_tlvs.hpp", + "thread/link_metrics_types.cpp", + "thread/link_metrics_types.hpp", "thread/link_quality.cpp", "thread/link_quality.hpp", "thread/lowpan.cpp", @@ -631,6 +638,7 @@ openthread_core_files = [ "thread/mle.hpp", "thread/mle_router.cpp", "thread/mle_router.hpp", + "thread/mle_tlvs.cpp", "thread/mle_tlvs.hpp", "thread/mle_types.cpp", "thread/mle_types.hpp", @@ -679,12 +687,11 @@ openthread_core_files = [ "thread/topology.hpp", "thread/uri_paths.cpp", "thread/uri_paths.hpp", + "thread/version.hpp", "utils/channel_manager.cpp", "utils/channel_manager.hpp", "utils/channel_monitor.cpp", "utils/channel_monitor.hpp", - "utils/child_supervision.cpp", - "utils/child_supervision.hpp", "utils/flash.cpp", "utils/flash.hpp", "utils/heap.cpp", @@ -693,12 +700,16 @@ openthread_core_files = [ "utils/history_tracker.hpp", "utils/jam_detector.cpp", "utils/jam_detector.hpp", + "utils/mesh_diag.cpp", + "utils/mesh_diag.hpp", "utils/otns.cpp", "utils/otns.hpp", "utils/parse_cmdline.cpp", "utils/parse_cmdline.hpp", "utils/ping_sender.cpp", "utils/ping_sender.hpp", + "utils/power_calibration.cpp", + "utils/power_calibration.hpp", "utils/slaac_address.cpp", "utils/slaac_address.hpp", "utils/srp_client_buffers.cpp", @@ -716,6 +727,8 @@ openthread_radio_sources = [ "common/binary_search.cpp", "common/binary_search.hpp", "common/error.hpp", + "common/frame_builder.cpp", + "common/frame_builder.hpp", "common/instance.cpp", "common/log.cpp", "common/random.cpp", @@ -738,6 +751,7 @@ openthread_radio_sources = [ "radio/radio_platform.cpp", "thread/link_quality.cpp", "utils/parse_cmdline.cpp", + "utils/power_calibration.cpp", ] header_pattern = [ @@ -752,7 +766,9 @@ source_set("libopenthread_core_config") { public = [ "config/announce_sender.h", "config/backbone_router.h", + "config/border_agent.h", "config/border_router.h", + "config/border_routing.h", "config/channel_manager.h", "config/channel_monitor.h", "config/child_supervision.h", @@ -774,13 +790,17 @@ source_set("libopenthread_core_config") { "config/link_raw.h", "config/logging.h", "config/mac.h", + "config/mesh_diag.h", "config/misc.h", "config/mle.h", + "config/nat64.h", "config/netdata_publisher.h", + "config/network_diagnostic.h", "config/openthread-core-config-check.h", "config/parent_search.h", "config/ping_sender.h", "config/platform.h", + "config/power_calibration.h", "config/radio_link.h", "config/sntp_client.h", "config/srp_client.h", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ce36689649a..eb1279f8ac1 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -61,8 +61,10 @@ set(COMMON_SOURCES api/link_metrics_api.cpp api/link_raw_api.cpp api/logging_api.cpp + api/mesh_diag_api.cpp api/message_api.cpp api/multi_radio_api.cpp + api/nat64_api.cpp api/netdata_api.cpp api/netdata_publisher_api.cpp api/netdiag_api.cpp @@ -77,6 +79,7 @@ set(COMMON_SOURCES api/srp_server_api.cpp api/tasklet_api.cpp api/tcp_api.cpp + api/tcp_ext_api.cpp api/thread_api.cpp api/thread_ftd_api.cpp api/trel_api.cpp @@ -106,6 +109,7 @@ set(COMMON_SOURCES common/log.cpp common/message.cpp common/notifier.cpp + common/preference.cpp common/random.cpp common/settings.cpp common/string.cpp @@ -118,12 +122,9 @@ set(COMMON_SOURCES crypto/aes_ccm.cpp crypto/aes_ecb.cpp crypto/crypto_platform.cpp - crypto/ecdsa.cpp - crypto/ecdsa_tinycrypt.cpp crypto/hkdf_sha256.cpp crypto/hmac_sha256.cpp crypto/mbedtls.cpp - crypto/pbkdf2_cmac.cpp crypto/sha256.cpp crypto/storage.cpp diags/factory_diags.cpp @@ -162,6 +163,7 @@ set(COMMON_SOURCES net/dhcp6_server.cpp net/dns_client.cpp net/dns_dso.cpp + net/dns_platform.cpp net/dns_types.cpp net/dnssd_server.cpp net/icmp6.cpp @@ -171,6 +173,7 @@ set(COMMON_SOURCES net/ip6_filter.cpp net/ip6_headers.cpp net/ip6_mpl.cpp + net/nat64_translator.cpp net/nd6.cpp net/nd_agent.cpp net/netif.cpp @@ -179,6 +182,7 @@ set(COMMON_SOURCES net/srp_client.cpp net/srp_server.cpp net/tcp6.cpp + net/tcp6_ext.cpp net/udp6.cpp radio/radio.cpp radio/radio_callbacks.cpp @@ -190,6 +194,7 @@ set(COMMON_SOURCES thread/announce_begin_server.cpp thread/announce_sender.cpp thread/anycast_locator.cpp + thread/child_supervision.cpp thread/child_table.cpp thread/csl_tx_scheduler.cpp thread/discover_scanner.cpp @@ -198,6 +203,7 @@ set(COMMON_SOURCES thread/indirect_sender.cpp thread/key_manager.cpp thread/link_metrics.cpp + thread/link_metrics_types.cpp thread/link_quality.cpp thread/lowpan.cpp thread/mesh_forwarder.cpp @@ -205,6 +211,7 @@ set(COMMON_SOURCES thread/mesh_forwarder_mtd.cpp thread/mle.cpp thread/mle_router.cpp + thread/mle_tlvs.cpp thread/mle_types.cpp thread/mlr_manager.cpp thread/neighbor_table.cpp @@ -229,14 +236,15 @@ set(COMMON_SOURCES thread/uri_paths.cpp utils/channel_manager.cpp utils/channel_monitor.cpp - utils/child_supervision.cpp utils/flash.cpp utils/heap.cpp utils/history_tracker.cpp utils/jam_detector.cpp + utils/mesh_diag.cpp utils/otns.cpp utils/parse_cmdline.cpp utils/ping_sender.cpp + utils/power_calibration.cpp utils/slaac_address.cpp utils/srp_client_buffers.cpp ) @@ -251,6 +259,7 @@ set(RADIO_COMMON_SOURCES api/tasklet_api.cpp common/binary_search.cpp common/error.cpp + common/frame_builder.cpp common/instance.cpp common/log.cpp common/random.cpp @@ -273,6 +282,7 @@ set(RADIO_COMMON_SOURCES radio/radio_platform.cpp thread/link_quality.cpp utils/parse_cmdline.cpp + utils/power_calibration.cpp ) set(OT_VENDOR_EXTENSION "" CACHE STRING "specify a C++ source file built as part of OpenThread core library") diff --git a/src/core/Makefile.am b/src/core/Makefile.am index 7b180eac549..92c2bf63470 100644 --- a/src/core/Makefile.am +++ b/src/core/Makefile.am @@ -151,8 +151,10 @@ SOURCES_COMMON = \ api/link_metrics_api.cpp \ api/link_raw_api.cpp \ api/logging_api.cpp \ + api/mesh_diag_api.cpp \ api/message_api.cpp \ api/multi_radio_api.cpp \ + api/nat64_api.cpp \ api/netdata_api.cpp \ api/netdata_publisher_api.cpp \ api/netdiag_api.cpp \ @@ -167,6 +169,7 @@ SOURCES_COMMON = \ api/srp_server_api.cpp \ api/tasklet_api.cpp \ api/tcp_api.cpp \ + api/tcp_ext_api.cpp \ api/thread_api.cpp \ api/thread_ftd_api.cpp \ api/trel_api.cpp \ @@ -196,6 +199,7 @@ SOURCES_COMMON = \ common/log.cpp \ common/message.cpp \ common/notifier.cpp \ + common/preference.cpp \ common/random.cpp \ common/settings.cpp \ common/string.cpp \ @@ -208,12 +212,9 @@ SOURCES_COMMON = \ crypto/aes_ccm.cpp \ crypto/aes_ecb.cpp \ crypto/crypto_platform.cpp \ - crypto/ecdsa.cpp \ - crypto/ecdsa_tinycrypt.cpp \ crypto/hkdf_sha256.cpp \ crypto/hmac_sha256.cpp \ crypto/mbedtls.cpp \ - crypto/pbkdf2_cmac.cpp \ crypto/sha256.cpp \ crypto/storage.cpp \ diags/factory_diags.cpp \ @@ -252,6 +253,7 @@ SOURCES_COMMON = \ net/dhcp6_server.cpp \ net/dns_client.cpp \ net/dns_dso.cpp \ + net/dns_platform.cpp \ net/dns_types.cpp \ net/dnssd_server.cpp \ net/icmp6.cpp \ @@ -261,6 +263,7 @@ SOURCES_COMMON = \ net/ip6_filter.cpp \ net/ip6_headers.cpp \ net/ip6_mpl.cpp \ + net/nat64_translator.cpp \ net/nd6.cpp \ net/nd_agent.cpp \ net/netif.cpp \ @@ -269,6 +272,7 @@ SOURCES_COMMON = \ net/srp_client.cpp \ net/srp_server.cpp \ net/tcp6.cpp \ + net/tcp6_ext.cpp \ net/udp6.cpp \ radio/radio.cpp \ radio/radio_callbacks.cpp \ @@ -280,6 +284,7 @@ SOURCES_COMMON = \ thread/announce_begin_server.cpp \ thread/announce_sender.cpp \ thread/anycast_locator.cpp \ + thread/child_supervision.cpp \ thread/child_table.cpp \ thread/csl_tx_scheduler.cpp \ thread/discover_scanner.cpp \ @@ -288,6 +293,7 @@ SOURCES_COMMON = \ thread/indirect_sender.cpp \ thread/key_manager.cpp \ thread/link_metrics.cpp \ + thread/link_metrics_types.cpp \ thread/link_quality.cpp \ thread/lowpan.cpp \ thread/mesh_forwarder.cpp \ @@ -295,6 +301,7 @@ SOURCES_COMMON = \ thread/mesh_forwarder_mtd.cpp \ thread/mle.cpp \ thread/mle_router.cpp \ + thread/mle_tlvs.cpp \ thread/mle_types.cpp \ thread/mlr_manager.cpp \ thread/neighbor_table.cpp \ @@ -319,14 +326,15 @@ SOURCES_COMMON = \ thread/uri_paths.cpp \ utils/channel_manager.cpp \ utils/channel_monitor.cpp \ - utils/child_supervision.cpp \ utils/flash.cpp \ utils/heap.cpp \ utils/history_tracker.cpp \ utils/jam_detector.cpp \ + utils/mesh_diag.cpp \ utils/otns.cpp \ utils/parse_cmdline.cpp \ utils/ping_sender.cpp \ + utils/power_calibration.cpp \ utils/slaac_address.cpp \ utils/srp_client_buffers.cpp \ $(NULL) @@ -341,6 +349,7 @@ RADIO_SOURCES_COMMON = \ api/tasklet_api.cpp \ common/binary_search.cpp \ common/error.cpp \ + common/frame_builder.cpp \ common/instance.cpp \ common/log.cpp \ common/random.cpp \ @@ -363,6 +372,7 @@ RADIO_SOURCES_COMMON = \ radio/radio_platform.cpp \ thread/link_quality.cpp \ utils/parse_cmdline.cpp \ + utils/power_calibration.cpp \ $(NULL) EXTRA_DIST = \ @@ -429,6 +439,7 @@ HEADERS_COMMON = \ common/as_core_type.hpp \ common/binary_search.hpp \ common/bit_vector.hpp \ + common/callback.hpp \ common/clearable.hpp \ common/code_utils.hpp \ common/const_cast.hpp \ @@ -457,10 +468,12 @@ HEADERS_COMMON = \ common/new.hpp \ common/non_copyable.hpp \ common/notifier.hpp \ + common/num_utils.hpp \ common/numeric_limits.hpp \ common/owned_ptr.hpp \ common/owning_list.hpp \ common/pool.hpp \ + common/preference.hpp \ common/ptr_wrapper.hpp \ common/random.hpp \ common/retain_ptr.hpp \ @@ -478,7 +491,9 @@ HEADERS_COMMON = \ common/uptime.hpp \ config/announce_sender.h \ config/backbone_router.h \ + config/border_agent.h \ config/border_router.h \ + config/border_routing.h \ config/channel_manager.h \ config/channel_monitor.h \ config/child_supervision.h \ @@ -500,13 +515,17 @@ HEADERS_COMMON = \ config/link_raw.h \ config/logging.h \ config/mac.h \ + config/mesh_diag.h \ config/misc.h \ config/mle.h \ + config/nat64.h \ config/netdata_publisher.h \ + config/network_diagnostic.h \ config/openthread-core-config-check.h \ config/parent_search.h \ config/ping_sender.h \ config/platform.h \ + config/power_calibration.h \ config/radio_link.h \ config/sntp_client.h \ config/srp_client.h \ @@ -520,7 +539,6 @@ HEADERS_COMMON = \ crypto/hkdf_sha256.hpp \ crypto/hmac_sha256.hpp \ crypto/mbedtls.hpp \ - crypto/pbkdf2_cmac.hpp \ crypto/sha256.hpp \ crypto/storage.hpp \ diags/factory_diags.hpp \ @@ -568,6 +586,7 @@ HEADERS_COMMON = \ net/ip6_headers.hpp \ net/ip6_mpl.hpp \ net/ip6_types.hpp \ + net/nat64_translator.hpp \ net/nd6.hpp \ net/nd_agent.hpp \ net/netif.hpp \ @@ -576,6 +595,7 @@ HEADERS_COMMON = \ net/srp_client.hpp \ net/srp_server.hpp \ net/tcp6.hpp \ + net/tcp6_ext.hpp \ net/udp6.hpp \ openthread-core-config.h \ radio/max_power_table.hpp \ @@ -588,6 +608,7 @@ HEADERS_COMMON = \ thread/announce_sender.hpp \ thread/anycast_locator.hpp \ thread/child_mask.hpp \ + thread/child_supervision.hpp \ thread/child_table.hpp \ thread/csl_tx_scheduler.hpp \ thread/discover_scanner.hpp \ @@ -598,6 +619,7 @@ HEADERS_COMMON = \ thread/key_manager.hpp \ thread/link_metrics.hpp \ thread/link_metrics_tlvs.hpp \ + thread/link_metrics_types.hpp \ thread/link_quality.hpp \ thread/lowpan.hpp \ thread/mesh_forwarder.hpp \ @@ -629,16 +651,18 @@ HEADERS_COMMON = \ thread/tmf.hpp \ thread/topology.hpp \ thread/uri_paths.hpp \ + thread/version.hpp \ utils/channel_manager.hpp \ utils/channel_monitor.hpp \ - utils/child_supervision.hpp \ utils/flash.hpp \ utils/heap.hpp \ utils/history_tracker.hpp \ utils/jam_detector.hpp \ + utils/mesh_diag.hpp \ utils/otns.hpp \ utils/parse_cmdline.hpp \ utils/ping_sender.hpp \ + utils/power_calibration.hpp \ utils/slaac_address.hpp \ utils/srp_client_buffers.hpp \ $(NULL) diff --git a/src/core/api/backbone_router_api.cpp b/src/core/api/backbone_router_api.cpp index 179249a0c54..cedb3785b1b 100644 --- a/src/core/api/backbone_router_api.cpp +++ b/src/core/api/backbone_router_api.cpp @@ -44,7 +44,7 @@ using namespace ot; otError otBackboneRouterGetPrimary(otInstance *aInstance, otBackboneRouterConfig *aConfig) { - OT_ASSERT(aConfig != nullptr); + AssertPointerIsNotNull(aConfig); return AsCoreType(aInstance).Get().GetConfig(*aConfig); } diff --git a/src/core/api/backbone_router_ftd_api.cpp b/src/core/api/backbone_router_ftd_api.cpp index d8e5169058a..2f3e4b3d507 100644 --- a/src/core/api/backbone_router_ftd_api.cpp +++ b/src/core/api/backbone_router_ftd_api.cpp @@ -50,19 +50,19 @@ void otBackboneRouterSetEnabled(otInstance *aInstance, bool aEnabled) otBackboneRouterState otBackboneRouterGetState(otInstance *aInstance) { - return AsCoreType(aInstance).Get().GetState(); + return MapEnum(AsCoreType(aInstance).Get().GetState()); } void otBackboneRouterGetConfig(otInstance *aInstance, otBackboneRouterConfig *aConfig) { - OT_ASSERT(aConfig != nullptr); + AssertPointerIsNotNull(aConfig); AsCoreType(aInstance).Get().GetConfig(*aConfig); } otError otBackboneRouterSetConfig(otInstance *aInstance, const otBackboneRouterConfig *aConfig) { - OT_ASSERT(aConfig != nullptr); + AssertPointerIsNotNull(aConfig); return AsCoreType(aInstance).Get().SetConfig(*aConfig); } @@ -84,49 +84,49 @@ void otBackboneRouterSetRegistrationJitter(otInstance *aInstance, uint8_t aJitte otError otBackboneRouterGetDomainPrefix(otInstance *aInstance, otBorderRouterConfig *aConfig) { - OT_ASSERT(aConfig != nullptr); - return AsCoreType(aInstance).Get().GetDomainPrefix(AsCoreType(aConfig)); } -void otBackboneRouterSetDomainPrefixCallback(otInstance * aInstance, +void otBackboneRouterSetDomainPrefixCallback(otInstance *aInstance, otBackboneRouterDomainPrefixCallback aCallback, - void * aContext) + void *aContext) { return AsCoreType(aInstance).Get().SetDomainPrefixCallback(aCallback, aContext); } #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE -void otBackboneRouterSetNdProxyCallback(otInstance * aInstance, +void otBackboneRouterSetNdProxyCallback(otInstance *aInstance, otBackboneRouterNdProxyCallback aCallback, - void * aContext) + void *aContext) { AsCoreType(aInstance).Get().SetCallback(aCallback, aContext); } -otError otBackboneRouterGetNdProxyInfo(otInstance * aInstance, - const otIp6Address * aDua, +otError otBackboneRouterGetNdProxyInfo(otInstance *aInstance, + const otIp6Address *aDua, otBackboneRouterNdProxyInfo *aNdProxyInfo) { + AssertPointerIsNotNull(aNdProxyInfo); + return AsCoreType(aInstance).Get().GetInfo( reinterpret_cast(*aDua), *aNdProxyInfo); } #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE -void otBackboneRouterSetMulticastListenerCallback(otInstance * aInstance, +void otBackboneRouterSetMulticastListenerCallback(otInstance *aInstance, otBackboneRouterMulticastListenerCallback aCallback, - void * aContext) + void *aContext) { AsCoreType(aInstance).Get().SetCallback(aCallback, aContext); } -otError otBackboneRouterMulticastListenerGetNext(otInstance * aInstance, - otChildIp6AddressIterator * aIterator, +otError otBackboneRouterMulticastListenerGetNext(otInstance *aInstance, + otChildIp6AddressIterator *aIterator, otBackboneRouterMulticastListenerInfo *aListenerInfo) { - OT_ASSERT(aIterator != nullptr); - OT_ASSERT(aListenerInfo != nullptr); + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aListenerInfo); return AsCoreType(aInstance).Get().GetNext(*aIterator, *aListenerInfo); } @@ -134,7 +134,7 @@ otError otBackboneRouterMulticastListenerGetNext(otInstance * #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE -void otBackboneRouterConfigNextDuaRegistrationResponse(otInstance * aInstance, +void otBackboneRouterConfigNextDuaRegistrationResponse(otInstance *aInstance, const otIp6InterfaceIdentifier *aMlIid, uint8_t aStatus) { @@ -159,17 +159,14 @@ void otBackboneRouterMulticastListenerClear(otInstance *aInstance) otError otBackboneRouterMulticastListenerAdd(otInstance *aInstance, const otIp6Address *aAddress, uint32_t aTimeout) { - OT_ASSERT(aAddress != nullptr); - if (aTimeout == 0) { - BackboneRouter::BackboneRouterConfig config; + BackboneRouter::Config config; AsCoreType(aInstance).Get().GetConfig(config); aTimeout = config.mMlrTimeout; } - aTimeout = - aTimeout > static_cast(Mle::kMlrTimeoutMax) ? static_cast(Mle::kMlrTimeoutMax) : aTimeout; + aTimeout = Min(aTimeout, Mle::kMlrTimeoutMax); aTimeout = Time::SecToMsec(aTimeout); return AsCoreType(aInstance).Get().Add(AsCoreType(aAddress), diff --git a/src/core/api/border_agent_api.cpp b/src/core/api/border_agent_api.cpp index a6ccea1a970..70f39baef9d 100644 --- a/src/core/api/border_agent_api.cpp +++ b/src/core/api/border_agent_api.cpp @@ -42,6 +42,13 @@ using namespace ot; +#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE +otError otBorderAgentGetId(otInstance *aInstance, uint8_t *aId, uint16_t *aLength) +{ + return AsCoreType(aInstance).Get().GetId(aId, *aLength); +} +#endif + otBorderAgentState otBorderAgentGetState(otInstance *aInstance) { return MapEnum(AsCoreType(aInstance).Get().GetState()); diff --git a/src/core/api/border_router_api.cpp b/src/core/api/border_router_api.cpp index 2ef3c4d9139..c3dd9ded27b 100644 --- a/src/core/api/border_router_api.cpp +++ b/src/core/api/border_router_api.cpp @@ -53,8 +53,6 @@ otError otBorderRouterAddOnMeshPrefix(otInstance *aInstance, const otBorderRoute { Error error = kErrorNone; - OT_ASSERT(aConfig != nullptr); - #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE if (aConfig->mDp) { @@ -73,8 +71,6 @@ otError otBorderRouterRemoveOnMeshPrefix(otInstance *aInstance, const otIp6Prefi { Error error = kErrorNone; - OT_ASSERT(aPrefix != nullptr); - #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE error = AsCoreType(aInstance).Get().RemoveDomainPrefix(AsCoreType(aPrefix)); @@ -87,34 +83,30 @@ otError otBorderRouterRemoveOnMeshPrefix(otInstance *aInstance, const otIp6Prefi return error; } -otError otBorderRouterGetNextOnMeshPrefix(otInstance * aInstance, +otError otBorderRouterGetNextOnMeshPrefix(otInstance *aInstance, otNetworkDataIterator *aIterator, - otBorderRouterConfig * aConfig) + otBorderRouterConfig *aConfig) { - OT_ASSERT(aIterator != nullptr && aConfig != nullptr); + AssertPointerIsNotNull(aIterator); return AsCoreType(aInstance).Get().GetNextOnMeshPrefix(*aIterator, AsCoreType(aConfig)); } otError otBorderRouterAddRoute(otInstance *aInstance, const otExternalRouteConfig *aConfig) { - OT_ASSERT(aConfig != nullptr); - return AsCoreType(aInstance).Get().AddHasRoutePrefix(AsCoreType(aConfig)); } otError otBorderRouterRemoveRoute(otInstance *aInstance, const otIp6Prefix *aPrefix) { - OT_ASSERT(aPrefix != nullptr); - return AsCoreType(aInstance).Get().RemoveHasRoutePrefix(AsCoreType(aPrefix)); } -otError otBorderRouterGetNextRoute(otInstance * aInstance, +otError otBorderRouterGetNextRoute(otInstance *aInstance, otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig) { - OT_ASSERT(aIterator != nullptr && aConfig != nullptr); + AssertPointerIsNotNull(aIterator); return AsCoreType(aInstance).Get().GetNextExternalRoute(*aIterator, AsCoreType(aConfig)); } diff --git a/src/core/api/border_routing_api.cpp b/src/core/api/border_routing_api.cpp index 1d942c5b87d..2731e02c0cf 100644 --- a/src/core/api/border_routing_api.cpp +++ b/src/core/api/border_routing_api.cpp @@ -52,6 +52,11 @@ otError otBorderRoutingSetEnabled(otInstance *aInstance, bool aEnabled) return AsCoreType(aInstance).Get().SetEnabled(aEnabled); } +otBorderRoutingState otBorderRoutingGetState(otInstance *aInstance) +{ + return MapEnum(AsCoreType(aInstance).Get().GetState()); +} + otRoutePreference otBorderRoutingGetRouteInfoOptionPreference(otInstance *aInstance) { return static_cast( @@ -64,6 +69,11 @@ void otBorderRoutingSetRouteInfoOptionPreference(otInstance *aInstance, otRouteP static_cast(aPreference)); } +void otBorderRoutingClearRouteInfoOptionPreference(otInstance *aInstance) +{ + AsCoreType(aInstance).Get().ClearRouteInfoOptionPreference(); +} + otError otBorderRoutingGetOmrPrefix(otInstance *aInstance, otIp6Prefix *aPrefix) { return AsCoreType(aInstance).Get().GetOmrPrefix(AsCoreType(aPrefix)); @@ -74,6 +84,8 @@ otError otBorderRoutingGetFavoredOmrPrefix(otInstance *aInstance, otIp6Prefix *a otError error; BorderRouter::RoutingManager::RoutePreference preference; + AssertPointerIsNotNull(aPreference); + SuccessOrExit(error = AsCoreType(aInstance).Get().GetFavoredOmrPrefix( AsCoreType(aPrefix), preference)); *aPreference = static_cast(preference); @@ -87,22 +99,49 @@ otError otBorderRoutingGetOnLinkPrefix(otInstance *aInstance, otIp6Prefix *aPref return AsCoreType(aInstance).Get().GetOnLinkPrefix(AsCoreType(aPrefix)); } -#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE +otError otBorderRoutingGetFavoredOnLinkPrefix(otInstance *aInstance, otIp6Prefix *aPrefix) +{ + return AsCoreType(aInstance).Get().GetFavoredOnLinkPrefix(AsCoreType(aPrefix)); +} + +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE otError otBorderRoutingGetNat64Prefix(otInstance *aInstance, otIp6Prefix *aPrefix) { return AsCoreType(aInstance).Get().GetNat64Prefix(AsCoreType(aPrefix)); } + +otError otBorderRoutingGetFavoredNat64Prefix(otInstance *aInstance, + otIp6Prefix *aPrefix, + otRoutePreference *aPreference) +{ + otError error; + BorderRouter::RoutingManager::RoutePreference preference; + + AssertPointerIsNotNull(aPreference); + + SuccessOrExit(error = AsCoreType(aInstance).Get().GetFavoredNat64Prefix( + AsCoreType(aPrefix), preference)); + *aPreference = static_cast(preference); + +exit: + return error; +} #endif void otBorderRoutingPrefixTableInitIterator(otInstance *aInstance, otBorderRoutingPrefixTableIterator *aIterator) { + AssertPointerIsNotNull(aIterator); + AsCoreType(aInstance).Get().InitPrefixTableIterator(*aIterator); } -otError otBorderRoutingGetNextPrefixTableEntry(otInstance * aInstance, +otError otBorderRoutingGetNextPrefixTableEntry(otInstance *aInstance, otBorderRoutingPrefixTableIterator *aIterator, - otBorderRoutingPrefixTableEntry * aEntry) + otBorderRoutingPrefixTableEntry *aEntry) { + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aEntry); + return AsCoreType(aInstance).Get().GetNextPrefixTableEntry(*aIterator, *aEntry); } diff --git a/src/core/api/child_supervision_api.cpp b/src/core/api/child_supervision_api.cpp index ab2dcecc737..d24a17d33b3 100644 --- a/src/core/api/child_supervision_api.cpp +++ b/src/core/api/child_supervision_api.cpp @@ -33,8 +33,6 @@ #include "openthread-core-config.h" -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - #include #include "common/as_core_type.hpp" @@ -42,28 +40,32 @@ using namespace ot; -#if OPENTHREAD_FTD - uint16_t otChildSupervisionGetInterval(otInstance *aInstance) { - return AsCoreType(aInstance).Get().GetSupervisionInterval(); + return AsCoreType(aInstance).Get().GetInterval(); } void otChildSupervisionSetInterval(otInstance *aInstance, uint16_t aInterval) { - AsCoreType(aInstance).Get().SetSupervisionInterval(aInterval); + AsCoreType(aInstance).Get().SetInterval(aInterval); } -#endif - uint16_t otChildSupervisionGetCheckTimeout(otInstance *aInstance) { - return AsCoreType(aInstance).Get().GetTimeout(); + return AsCoreType(aInstance).Get().GetTimeout(); } void otChildSupervisionSetCheckTimeout(otInstance *aInstance, uint16_t aTimeout) { - AsCoreType(aInstance).Get().SetTimeout(aTimeout); + AsCoreType(aInstance).Get().SetTimeout(aTimeout); +} + +uint16_t otChildSupervisionGetCheckFailureCounter(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetCounter(); } -#endif // OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE +void otChildSupervisionResetCheckFailureCounter(otInstance *aInstance) +{ + AsCoreType(aInstance).Get().ResetCounter(); +} diff --git a/src/core/api/coap_api.cpp b/src/core/api/coap_api.cpp index 4dfb35edfe2..6c52cd2e0e2 100644 --- a/src/core/api/coap_api.cpp +++ b/src/core/api/coap_api.cpp @@ -55,7 +55,7 @@ void otCoapMessageInit(otMessage *aMessage, otCoapType aType, otCoapCode aCode) otError otCoapMessageInitResponse(otMessage *aResponse, const otMessage *aRequest, otCoapType aType, otCoapCode aCode) { - Coap::Message & response = AsCoapMessage(aResponse); + Coap::Message &response = AsCoapMessage(aResponse); const Coap::Message &request = AsCoapMessage(aRequest); response.Init(MapEnum(aType), MapEnum(aCode)); @@ -129,10 +129,7 @@ otError otCoapMessageAppendUriQueryOption(otMessage *aMessage, const char *aUriQ return AsCoapMessage(aMessage).AppendUriQueryOption(aUriQuery); } -otError otCoapMessageSetPayloadMarker(otMessage *aMessage) -{ - return AsCoapMessage(aMessage).SetPayloadMarker(); -} +otError otCoapMessageSetPayloadMarker(otMessage *aMessage) { return AsCoapMessage(aMessage).SetPayloadMarker(); } otCoapType otCoapMessageGetType(const otMessage *aMessage) { @@ -144,30 +141,15 @@ otCoapCode otCoapMessageGetCode(const otMessage *aMessage) return static_cast(AsCoapMessage(aMessage).GetCode()); } -void otCoapMessageSetCode(otMessage *aMessage, otCoapCode aCode) -{ - AsCoapMessage(aMessage).SetCode(MapEnum(aCode)); -} +void otCoapMessageSetCode(otMessage *aMessage, otCoapCode aCode) { AsCoapMessage(aMessage).SetCode(MapEnum(aCode)); } -const char *otCoapMessageCodeToString(const otMessage *aMessage) -{ - return AsCoapMessage(aMessage).CodeToString(); -} +const char *otCoapMessageCodeToString(const otMessage *aMessage) { return AsCoapMessage(aMessage).CodeToString(); } -uint16_t otCoapMessageGetMessageId(const otMessage *aMessage) -{ - return AsCoapMessage(aMessage).GetMessageId(); -} +uint16_t otCoapMessageGetMessageId(const otMessage *aMessage) { return AsCoapMessage(aMessage).GetMessageId(); } -uint8_t otCoapMessageGetTokenLength(const otMessage *aMessage) -{ - return AsCoapMessage(aMessage).GetTokenLength(); -} +uint8_t otCoapMessageGetTokenLength(const otMessage *aMessage) { return AsCoapMessage(aMessage).GetTokenLength(); } -const uint8_t *otCoapMessageGetToken(const otMessage *aMessage) -{ - return AsCoapMessage(aMessage).GetToken(); -} +const uint8_t *otCoapMessageGetToken(const otMessage *aMessage) { return AsCoapMessage(aMessage).GetToken(); } otError otCoapOptionIteratorInit(otCoapOptionIterator *aIterator, const otMessage *aMessage) { @@ -217,12 +199,12 @@ otError otCoapOptionIteratorGetOptionValue(otCoapOptionIterator *aIterator, void } #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE -otError otCoapSendRequestBlockWiseWithParameters(otInstance * aInstance, - otMessage * aMessage, - const otMessageInfo * aMessageInfo, +otError otCoapSendRequestBlockWiseWithParameters(otInstance *aInstance, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, otCoapResponseHandler aHandler, - void * aContext, - const otCoapTxParameters * aTxParameters, + void *aContext, + const otCoapTxParameters *aTxParameters, otCoapBlockwiseTransmitHook aTransmitHook, otCoapBlockwiseReceiveHook aReceiveHook) { @@ -243,11 +225,11 @@ otError otCoapSendRequestBlockWiseWithParameters(otInstance * aIn } #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE -otError otCoapSendRequestWithParameters(otInstance * aInstance, - otMessage * aMessage, - const otMessageInfo * aMessageInfo, +otError otCoapSendRequestWithParameters(otInstance *aInstance, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, otCoapResponseHandler aHandler, - void * aContext, + void *aContext, const otCoapTxParameters *aTxParameters) { Error error; @@ -271,10 +253,7 @@ otError otCoapStart(otInstance *aInstance, uint16_t aPort) return AsCoreType(aInstance).GetApplicationCoap().Start(aPort); } -otError otCoapStop(otInstance *aInstance) -{ - return AsCoreType(aInstance).GetApplicationCoap().Stop(); -} +otError otCoapStop(otInstance *aInstance) { return AsCoreType(aInstance).GetApplicationCoap().Stop(); } #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE void otCoapAddBlockWiseResource(otInstance *aInstance, otCoapBlockwiseResource *aResource) @@ -304,11 +283,11 @@ void otCoapSetDefaultHandler(otInstance *aInstance, otCoapRequestHandler aHandle } #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE -otError otCoapSendResponseBlockWiseWithParameters(otInstance * aInstance, - otMessage * aMessage, - const otMessageInfo * aMessageInfo, - const otCoapTxParameters * aTxParameters, - void * aContext, +otError otCoapSendResponseBlockWiseWithParameters(otInstance *aInstance, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, + const otCoapTxParameters *aTxParameters, + void *aContext, otCoapBlockwiseTransmitHook aTransmitHook) { return AsCoreType(aInstance).GetApplicationCoap().SendMessage(AsCoapMessage(aMessage), AsCoreType(aMessageInfo), @@ -317,9 +296,9 @@ otError otCoapSendResponseBlockWiseWithParameters(otInstance * aI } #endif -otError otCoapSendResponseWithParameters(otInstance * aInstance, - otMessage * aMessage, - const otMessageInfo * aMessageInfo, +otError otCoapSendResponseWithParameters(otInstance *aInstance, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, const otCoapTxParameters *aTxParameters) { return AsCoreType(aInstance).GetApplicationCoap().SendMessage( diff --git a/src/core/api/coap_secure_api.cpp b/src/core/api/coap_secure_api.cpp index 0a0c6aac2a8..86f76b4c948 100644 --- a/src/core/api/coap_secure_api.cpp +++ b/src/core/api/coap_secure_api.cpp @@ -51,48 +51,47 @@ otError otCoapSecureStart(otInstance *aInstance, uint16_t aPort) } #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED -void otCoapSecureSetCertificate(otInstance * aInstance, +void otCoapSecureSetCertificate(otInstance *aInstance, const uint8_t *aX509Cert, uint32_t aX509Length, const uint8_t *aPrivateKey, uint32_t aPrivateKeyLength) { - OT_ASSERT(aX509Cert != nullptr && aX509Length != 0 && aPrivateKey != nullptr && aPrivateKeyLength != 0); - AsCoreType(aInstance).GetApplicationCoapSecure().SetCertificate(aX509Cert, aX509Length, aPrivateKey, aPrivateKeyLength); } -void otCoapSecureSetCaCertificateChain(otInstance * aInstance, +void otCoapSecureSetCaCertificateChain(otInstance *aInstance, const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength) { - OT_ASSERT(aX509CaCertificateChain != nullptr && aX509CaCertChainLength != 0); - AsCoreType(aInstance).GetApplicationCoapSecure().SetCaCertificateChain(aX509CaCertificateChain, aX509CaCertChainLength); } #endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED -void otCoapSecureSetPsk(otInstance * aInstance, +void otCoapSecureSetPsk(otInstance *aInstance, const uint8_t *aPsk, uint16_t aPskLength, const uint8_t *aPskIdentity, uint16_t aPskIdLength) { - OT_ASSERT(aPsk != nullptr && aPskLength != 0 && aPskIdentity != nullptr && aPskIdLength != 0); + AssertPointerIsNotNull(aPsk); + AssertPointerIsNotNull(aPskIdentity); AsCoreType(aInstance).GetApplicationCoapSecure().SetPreSharedKey(aPsk, aPskLength, aPskIdentity, aPskIdLength); } #endif // MBEDTLS_KEY_EXCHANGE_PSK_ENABLED #if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) -otError otCoapSecureGetPeerCertificateBase64(otInstance * aInstance, +otError otCoapSecureGetPeerCertificateBase64(otInstance *aInstance, unsigned char *aPeerCert, - size_t * aCertLength, + size_t *aCertLength, size_t aCertBufferSize) { + AssertPointerIsNotNull(aPeerCert); + return AsCoreType(aInstance).GetApplicationCoapSecure().GetPeerCertificateBase64(aPeerCert, aCertLength, aCertBufferSize); } @@ -103,18 +102,15 @@ void otCoapSecureSetSslAuthMode(otInstance *aInstance, bool aVerifyPeerCertifica AsCoreType(aInstance).GetApplicationCoapSecure().SetSslAuthMode(aVerifyPeerCertificate); } -otError otCoapSecureConnect(otInstance * aInstance, - const otSockAddr * aSockAddr, +otError otCoapSecureConnect(otInstance *aInstance, + const otSockAddr *aSockAddr, otHandleCoapSecureClientConnect aHandler, - void * aContext) + void *aContext) { return AsCoreType(aInstance).GetApplicationCoapSecure().Connect(AsCoreType(aSockAddr), aHandler, aContext); } -void otCoapSecureDisconnect(otInstance *aInstance) -{ - AsCoreType(aInstance).GetApplicationCoapSecure().Disconnect(); -} +void otCoapSecureDisconnect(otInstance *aInstance) { AsCoreType(aInstance).GetApplicationCoapSecure().Disconnect(); } bool otCoapSecureIsConnected(otInstance *aInstance) { @@ -126,16 +122,13 @@ bool otCoapSecureIsConnectionActive(otInstance *aInstance) return AsCoreType(aInstance).GetApplicationCoapSecure().IsConnectionActive(); } -void otCoapSecureStop(otInstance *aInstance) -{ - AsCoreType(aInstance).GetApplicationCoapSecure().Stop(); -} +void otCoapSecureStop(otInstance *aInstance) { AsCoreType(aInstance).GetApplicationCoapSecure().Stop(); } #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE -otError otCoapSecureSendRequestBlockWise(otInstance * aInstance, - otMessage * aMessage, +otError otCoapSecureSendRequestBlockWise(otInstance *aInstance, + otMessage *aMessage, otCoapResponseHandler aHandler, - void * aContext, + void *aContext, otCoapBlockwiseTransmitHook aTransmitHook, otCoapBlockwiseReceiveHook aReceiveHook) { @@ -144,10 +137,10 @@ otError otCoapSecureSendRequestBlockWise(otInstance * aInstance, } #endif -otError otCoapSecureSendRequest(otInstance * aInstance, - otMessage * aMessage, +otError otCoapSecureSendRequest(otInstance *aInstance, + otMessage *aMessage, otCoapResponseHandler aHandler, - void * aContext) + void *aContext) { return AsCoreType(aInstance).GetApplicationCoapSecure().SendMessage(AsCoapMessage(aMessage), aHandler, aContext); } @@ -174,9 +167,9 @@ void otCoapSecureRemoveResource(otInstance *aInstance, otCoapResource *aResource AsCoreType(aInstance).GetApplicationCoapSecure().RemoveResource(AsCoreType(aResource)); } -void otCoapSecureSetClientConnectedCallback(otInstance * aInstance, +void otCoapSecureSetClientConnectedCallback(otInstance *aInstance, otHandleCoapSecureClientConnect aHandler, - void * aContext) + void *aContext) { AsCoreType(aInstance).GetApplicationCoapSecure().SetClientConnectedCallback(aHandler, aContext); } @@ -187,10 +180,10 @@ void otCoapSecureSetDefaultHandler(otInstance *aInstance, otCoapRequestHandler a } #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE -otError otCoapSecureSendResponseBlockWise(otInstance * aInstance, - otMessage * aMessage, - const otMessageInfo * aMessageInfo, - void * aContext, +otError otCoapSecureSendResponseBlockWise(otInstance *aInstance, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, + void *aContext, otCoapBlockwiseTransmitHook aTransmitHook) { return AsCoreType(aInstance).GetApplicationCoapSecure().SendMessage( diff --git a/src/core/api/commissioner_api.cpp b/src/core/api/commissioner_api.cpp index 9c5b5759009..6831c87ef60 100644 --- a/src/core/api/commissioner_api.cpp +++ b/src/core/api/commissioner_api.cpp @@ -42,10 +42,10 @@ using namespace ot; -otError otCommissionerStart(otInstance * aInstance, +otError otCommissionerStart(otInstance *aInstance, otCommissionerStateCallback aStateCallback, otCommissionerJoinerCallback aJoinerCallback, - void * aCallbackContext) + void *aCallbackContext) { return AsCoreType(aInstance).Get().Start(aStateCallback, aJoinerCallback, aCallbackContext); } @@ -60,10 +60,7 @@ otError otCommissionerSetId(otInstance *aInstance, const char *aId) return AsCoreType(aInstance).Get().SetId(aId); } -otError otCommissionerStop(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().Stop(); -} +otError otCommissionerStop(otInstance *aInstance) { return AsCoreType(aInstance).Get().Stop(); } otError otCommissionerAddJoiner(otInstance *aInstance, const otExtAddress *aEui64, const char *aPskd, uint32_t aTimeout) { @@ -82,9 +79,9 @@ otError otCommissionerAddJoiner(otInstance *aInstance, const otExtAddress *aEui6 return error; } -otError otCommissionerAddJoinerWithDiscerner(otInstance * aInstance, +otError otCommissionerAddJoinerWithDiscerner(otInstance *aInstance, const otJoinerDiscerner *aDiscerner, - const char * aPskd, + const char *aPskd, uint32_t aTimeout) { return AsCoreType(aInstance).Get().AddJoiner(AsCoreType(aDiscerner), aPskd, aTimeout); @@ -127,7 +124,7 @@ const char *otCommissionerGetProvisioningUrl(otInstance *aInstance) return AsCoreType(aInstance).Get().GetProvisioningUrl(); } -otError otCommissionerAnnounceBegin(otInstance * aInstance, +otError otCommissionerAnnounceBegin(otInstance *aInstance, uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod, @@ -137,25 +134,25 @@ otError otCommissionerAnnounceBegin(otInstance * aInstance, aChannelMask, aCount, aPeriod, AsCoreType(aAddress)); } -otError otCommissionerEnergyScan(otInstance * aInstance, +otError otCommissionerEnergyScan(otInstance *aInstance, uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod, uint16_t aScanDuration, - const otIp6Address * aAddress, + const otIp6Address *aAddress, otCommissionerEnergyReportCallback aCallback, - void * aContext) + void *aContext) { return AsCoreType(aInstance).Get().GetEnergyScanClient().SendQuery( aChannelMask, aCount, aPeriod, aScanDuration, AsCoreType(aAddress), aCallback, aContext); } -otError otCommissionerPanIdQuery(otInstance * aInstance, +otError otCommissionerPanIdQuery(otInstance *aInstance, uint16_t aPanId, uint32_t aChannelMask, - const otIp6Address * aAddress, + const otIp6Address *aAddress, otCommissionerPanIdConflictCallback aCallback, - void * aContext) + void *aContext) { return AsCoreType(aInstance).Get().GetPanIdQueryClient().SendQuery( aPanId, aChannelMask, AsCoreType(aAddress), aCallback, aContext); @@ -166,9 +163,9 @@ otError otCommissionerSendMgmtGet(otInstance *aInstance, const uint8_t *aTlvs, u return AsCoreType(aInstance).Get().SendMgmtCommissionerGetRequest(aTlvs, aLength); } -otError otCommissionerSendMgmtSet(otInstance * aInstance, +otError otCommissionerSendMgmtSet(otInstance *aInstance, const otCommissioningDataset *aDataset, - const uint8_t * aTlvs, + const uint8_t *aTlvs, uint8_t aLength) { return AsCoreType(aInstance).Get().SendMgmtCommissionerSetRequest(AsCoreType(aDataset), diff --git a/src/core/api/crypto_api.cpp b/src/core/api/crypto_api.cpp index 8a96d466a84..069596f53e0 100644 --- a/src/core/api/crypto_api.cpp +++ b/src/core/api/crypto_api.cpp @@ -51,27 +51,31 @@ void otCryptoHmacSha256(const otCryptoKey *aKey, const uint8_t *aBuf, uint16_t a { HmacSha256 hmac; - OT_ASSERT((aKey != nullptr) && (aBuf != nullptr) && (aHash != nullptr)); + AssertPointerIsNotNull(aBuf); hmac.Start(AsCoreType(aKey)); hmac.Update(aBuf, aBufLength); - hmac.Finish(ot::AsCoreType(aHash)); + hmac.Finish(AsCoreType(aHash)); } void otCryptoAesCcm(const otCryptoKey *aKey, uint8_t aTagLength, - const void * aNonce, + const void *aNonce, uint8_t aNonceLength, - const void * aHeader, + const void *aHeader, uint32_t aHeaderLength, - void * aPlainText, - void * aCipherText, + void *aPlainText, + void *aCipherText, uint32_t aLength, bool aEncrypt, - void * aTag) + void *aTag) { AesCcm aesCcm; - OT_ASSERT((aNonce != nullptr) && (aPlainText != nullptr) && (aCipherText != nullptr) && (aTag != nullptr)); + + AssertPointerIsNotNull(aNonce); + AssertPointerIsNotNull(aPlainText); + AssertPointerIsNotNull(aCipherText); + AssertPointerIsNotNull(aTag); aesCcm.SetKey(AsCoreType(aKey)); aesCcm.Init(aHeaderLength, aLength, aTagLength, aNonce, aNonceLength); @@ -85,17 +89,3 @@ void otCryptoAesCcm(const otCryptoKey *aKey, aesCcm.Payload(aPlainText, aCipherText, aLength, aEncrypt ? AesCcm::kEncrypt : AesCcm::kDecrypt); aesCcm.Finalize(aTag); } - -#if OPENTHREAD_CONFIG_ECDSA_ENABLE - -otError otCryptoEcdsaSign(uint8_t * aOutput, - uint16_t * aOutputLength, - const uint8_t *aInputHash, - uint16_t aInputHashLength, - const uint8_t *aPrivateKey, - uint16_t aPrivateKeyLength) -{ - return Ecdsa::Sign(aOutput, *aOutputLength, aInputHash, aInputHashLength, aPrivateKey, aPrivateKeyLength); -} - -#endif // OPENTHREAD_CONFIG_ECDSA_ENABLE diff --git a/src/core/api/dataset_api.cpp b/src/core/api/dataset_api.cpp index 457c7835019..7e4c0d42db5 100644 --- a/src/core/api/dataset_api.cpp +++ b/src/core/api/dataset_api.cpp @@ -49,111 +49,103 @@ bool otDatasetIsCommissioned(otInstance *aInstance) otError otDatasetGetActive(otInstance *aInstance, otOperationalDataset *aDataset) { - OT_ASSERT(aDataset != nullptr); - return AsCoreType(aInstance).Get().Read(AsCoreType(aDataset)); } otError otDatasetGetActiveTlvs(otInstance *aInstance, otOperationalDatasetTlvs *aDataset) { - OT_ASSERT(aDataset != nullptr); + AssertPointerIsNotNull(aDataset); return AsCoreType(aInstance).Get().Read(*aDataset); } otError otDatasetSetActive(otInstance *aInstance, const otOperationalDataset *aDataset) { - OT_ASSERT(aDataset != nullptr); - return AsCoreType(aInstance).Get().Save(AsCoreType(aDataset)); } otError otDatasetSetActiveTlvs(otInstance *aInstance, const otOperationalDatasetTlvs *aDataset) { - OT_ASSERT(aDataset != nullptr); + AssertPointerIsNotNull(aDataset); return AsCoreType(aInstance).Get().Save(*aDataset); } otError otDatasetGetPending(otInstance *aInstance, otOperationalDataset *aDataset) { - OT_ASSERT(aDataset != nullptr); - return AsCoreType(aInstance).Get().Read(AsCoreType(aDataset)); } otError otDatasetGetPendingTlvs(otInstance *aInstance, otOperationalDatasetTlvs *aDataset) { - OT_ASSERT(aDataset != nullptr); + AssertPointerIsNotNull(aDataset); return AsCoreType(aInstance).Get().Read(*aDataset); } otError otDatasetSetPending(otInstance *aInstance, const otOperationalDataset *aDataset) { - OT_ASSERT(aDataset != nullptr); - return AsCoreType(aInstance).Get().Save(AsCoreType(aDataset)); } otError otDatasetSetPendingTlvs(otInstance *aInstance, const otOperationalDatasetTlvs *aDataset) { - OT_ASSERT(aDataset != nullptr); + AssertPointerIsNotNull(aDataset); return AsCoreType(aInstance).Get().Save(*aDataset); } -otError otDatasetSendMgmtActiveGet(otInstance * aInstance, +otError otDatasetSendMgmtActiveGet(otInstance *aInstance, const otOperationalDatasetComponents *aDatasetComponents, - const uint8_t * aTlvTypes, + const uint8_t *aTlvTypes, uint8_t aLength, - const otIp6Address * aAddress) + const otIp6Address *aAddress) { return AsCoreType(aInstance).Get().SendGetRequest(AsCoreType(aDatasetComponents), aTlvTypes, aLength, aAddress); } -otError otDatasetSendMgmtActiveSet(otInstance * aInstance, +otError otDatasetSendMgmtActiveSet(otInstance *aInstance, const otOperationalDataset *aDataset, - const uint8_t * aTlvs, + const uint8_t *aTlvs, uint8_t aLength, otDatasetMgmtSetCallback aCallback, - void * aContext) + void *aContext) { return AsCoreType(aInstance).Get().SendSetRequest(AsCoreType(aDataset), aTlvs, aLength, aCallback, aContext); } -otError otDatasetSendMgmtPendingGet(otInstance * aInstance, +otError otDatasetSendMgmtPendingGet(otInstance *aInstance, const otOperationalDatasetComponents *aDatasetComponents, - const uint8_t * aTlvTypes, + const uint8_t *aTlvTypes, uint8_t aLength, - const otIp6Address * aAddress) + const otIp6Address *aAddress) { return AsCoreType(aInstance).Get().SendGetRequest(AsCoreType(aDatasetComponents), aTlvTypes, aLength, aAddress); } -otError otDatasetSendMgmtPendingSet(otInstance * aInstance, +otError otDatasetSendMgmtPendingSet(otInstance *aInstance, const otOperationalDataset *aDataset, - const uint8_t * aTlvs, + const uint8_t *aTlvs, uint8_t aLength, otDatasetMgmtSetCallback aCallback, - void * aContext) + void *aContext) { return AsCoreType(aInstance).Get().SendSetRequest(AsCoreType(aDataset), aTlvs, aLength, aCallback, aContext); } #if OPENTHREAD_FTD -otError otDatasetGeneratePskc(const char * aPassPhrase, - const otNetworkName * aNetworkName, +otError otDatasetGeneratePskc(const char *aPassPhrase, + const otNetworkName *aNetworkName, const otExtendedPanId *aExtPanId, - otPskc * aPskc) + otPskc *aPskc) { return MeshCoP::GeneratePskc(aPassPhrase, AsCoreType(aNetworkName), AsCoreType(aExtPanId), AsCoreType(aPskc)); } -#endif // OPENTHREAD_FTD +#endif otError otNetworkNameFromString(otNetworkName *aNetworkName, const char *aNameString) { @@ -167,6 +159,8 @@ otError otDatasetParseTlvs(const otOperationalDatasetTlvs *aDatasetTlvs, otOpera Error error = kErrorNone; MeshCoP::Dataset dataset; + AssertPointerIsNotNull(aDatasetTlvs); + dataset.SetFrom(*aDatasetTlvs); VerifyOrExit(dataset.IsValid(), error = kErrorInvalidArgs); dataset.ConvertTo(AsCoreType(aDataset)); @@ -174,3 +168,32 @@ otError otDatasetParseTlvs(const otOperationalDatasetTlvs *aDatasetTlvs, otOpera exit: return error; } + +otError otDatasetConvertToTlvs(const otOperationalDataset *aDataset, otOperationalDatasetTlvs *aDatasetTlvs) +{ + Error error = kErrorNone; + MeshCoP::Dataset dataset; + + AssertPointerIsNotNull(aDatasetTlvs); + + SuccessOrExit(error = dataset.SetFrom(AsCoreType(aDataset))); + dataset.ConvertTo(*aDatasetTlvs); + +exit: + return error; +} + +otError otDatasetUpdateTlvs(const otOperationalDataset *aDataset, otOperationalDatasetTlvs *aDatasetTlvs) +{ + Error error = kErrorNone; + MeshCoP::Dataset dataset; + + AssertPointerIsNotNull(aDatasetTlvs); + + dataset.SetFrom(*aDatasetTlvs); + SuccessOrExit(error = dataset.SetFrom(AsCoreType(aDataset))); + dataset.ConvertTo(*aDatasetTlvs); + +exit: + return error; +} diff --git a/src/core/api/dataset_updater_api.cpp b/src/core/api/dataset_updater_api.cpp index 8cd64812c99..b21577a6c64 100644 --- a/src/core/api/dataset_updater_api.cpp +++ b/src/core/api/dataset_updater_api.cpp @@ -43,10 +43,10 @@ using namespace ot; -otError otDatasetUpdaterRequestUpdate(otInstance * aInstance, +otError otDatasetUpdaterRequestUpdate(otInstance *aInstance, const otOperationalDataset *aDataset, otDatasetUpdaterCallback aCallback, - void * aContext) + void *aContext) { return AsCoreType(aInstance).Get().RequestUpdate(AsCoreType(aDataset), aCallback, aContext); diff --git a/src/core/api/diags_api.cpp b/src/core/api/diags_api.cpp index a012d1adcde..a87bde58135 100644 --- a/src/core/api/diags_api.cpp +++ b/src/core/api/diags_api.cpp @@ -42,9 +42,11 @@ using namespace ot; -void otDiagProcessCmdLine(otInstance *aInstance, const char *aString, char *aOutput, size_t aOutputMaxLen) +otError otDiagProcessCmdLine(otInstance *aInstance, const char *aString, char *aOutput, size_t aOutputMaxLen) { - AsCoreType(aInstance).Get().ProcessLine(aString, aOutput, aOutputMaxLen); + AssertPointerIsNotNull(aString); + + return AsCoreType(aInstance).Get().ProcessLine(aString, aOutput, aOutputMaxLen); } otError otDiagProcessCmd(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) @@ -52,9 +54,6 @@ otError otDiagProcessCmd(otInstance *aInstance, uint8_t aArgsLength, char *aArgs return AsCoreType(aInstance).Get().ProcessCmd(aArgsLength, aArgs, aOutput, aOutputMaxLen); } -bool otDiagIsEnabled(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().IsEnabled(); -} +bool otDiagIsEnabled(otInstance *aInstance) { return AsCoreType(aInstance).Get().IsEnabled(); } #endif // OPENTHREAD_CONFIG_DIAG_ENABLE diff --git a/src/core/api/dns_api.cpp b/src/core/api/dns_api.cpp index d9bdda70004..f1020ec5898 100644 --- a/src/core/api/dns_api.cpp +++ b/src/core/api/dns_api.cpp @@ -51,15 +51,9 @@ otError otDnsGetNextTxtEntry(otDnsTxtEntryIterator *aIterator, otDnsTxtEntry *aE } #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE -void otDnsSetNameCompressionEnabled(bool aEnabled) -{ - Instance::SetDnsNameCompressionEnabled(aEnabled); -} +void otDnsSetNameCompressionEnabled(bool aEnabled) { Instance::SetDnsNameCompressionEnabled(aEnabled); } -bool otDnsIsNameCompressionEnabled(void) -{ - return Instance::IsDnsNameCompressionEnabled(); -} +bool otDnsIsNameCompressionEnabled(void) { return Instance::IsDnsNameCompressionEnabled(); } #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE @@ -81,39 +75,45 @@ void otDnsClientSetDefaultConfig(otInstance *aInstance, const otDnsQueryConfig * } } -otError otDnsClientResolveAddress(otInstance * aInstance, - const char * aHostName, +otError otDnsClientResolveAddress(otInstance *aInstance, + const char *aHostName, otDnsAddressCallback aCallback, - void * aContext, + void *aContext, const otDnsQueryConfig *aConfig) { + AssertPointerIsNotNull(aHostName); + return AsCoreType(aInstance).Get().ResolveAddress(aHostName, aCallback, aContext, AsCoreTypePtr(aConfig)); } #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE -otError otDnsClientResolveIp4Address(otInstance * aInstance, - const char * aHostName, +otError otDnsClientResolveIp4Address(otInstance *aInstance, + const char *aHostName, otDnsAddressCallback aCallback, - void * aContext, + void *aContext, const otDnsQueryConfig *aConfig) { + AssertPointerIsNotNull(aHostName); + return AsCoreType(aInstance).Get().ResolveIp4Address(aHostName, aCallback, aContext, AsCoreTypePtr(aConfig)); } #endif otError otDnsAddressResponseGetHostName(const otDnsAddressResponse *aResponse, - char * aNameBuffer, + char *aNameBuffer, uint16_t aNameBufferSize) { + AssertPointerIsNotNull(aNameBuffer); + return AsCoreType(aResponse).GetHostName(aNameBuffer, aNameBufferSize); } otError otDnsAddressResponseGetAddress(const otDnsAddressResponse *aResponse, uint16_t aIndex, - otIp6Address * aAddress, - uint32_t * aTtl) + otIp6Address *aAddress, + uint32_t *aTtl) { uint32_t ttl; @@ -122,65 +122,81 @@ otError otDnsAddressResponseGetAddress(const otDnsAddressResponse *aResponse, #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE -otError otDnsClientBrowse(otInstance * aInstance, - const char * aServiceName, +otError otDnsClientBrowse(otInstance *aInstance, + const char *aServiceName, otDnsBrowseCallback aCallback, - void * aContext, + void *aContext, const otDnsQueryConfig *aConfig) { + AssertPointerIsNotNull(aServiceName); + return AsCoreType(aInstance).Get().Browse(aServiceName, aCallback, aContext, AsCoreTypePtr(aConfig)); } otError otDnsBrowseResponseGetServiceName(const otDnsBrowseResponse *aResponse, - char * aNameBuffer, + char *aNameBuffer, uint16_t aNameBufferSize) { + AssertPointerIsNotNull(aNameBuffer); + return AsCoreType(aResponse).GetServiceName(aNameBuffer, aNameBufferSize); } otError otDnsBrowseResponseGetServiceInstance(const otDnsBrowseResponse *aResponse, uint16_t aIndex, - char * aLabelBuffer, + char *aLabelBuffer, uint8_t aLabelBufferSize) { + AssertPointerIsNotNull(aLabelBuffer); + return AsCoreType(aResponse).GetServiceInstance(aIndex, aLabelBuffer, aLabelBufferSize); } otError otDnsBrowseResponseGetServiceInfo(const otDnsBrowseResponse *aResponse, - const char * aInstanceLabel, - otDnsServiceInfo * aServiceInfo) + const char *aInstanceLabel, + otDnsServiceInfo *aServiceInfo) { + AssertPointerIsNotNull(aInstanceLabel); + return AsCoreType(aResponse).GetServiceInfo(aInstanceLabel, AsCoreType(aServiceInfo)); } otError otDnsBrowseResponseGetHostAddress(const otDnsBrowseResponse *aResponse, - const char * aHostName, + const char *aHostName, uint16_t aIndex, - otIp6Address * aAddress, - uint32_t * aTtl) + otIp6Address *aAddress, + uint32_t *aTtl) { uint32_t ttl; + AssertPointerIsNotNull(aHostName); + return AsCoreType(aResponse).GetHostAddress(aHostName, aIndex, AsCoreType(aAddress), aTtl != nullptr ? *aTtl : ttl); } -otError otDnsClientResolveService(otInstance * aInstance, - const char * aInstanceLabel, - const char * aServiceName, +otError otDnsClientResolveService(otInstance *aInstance, + const char *aInstanceLabel, + const char *aServiceName, otDnsServiceCallback aCallback, - void * aContext, + void *aContext, const otDnsQueryConfig *aConfig) { + AssertPointerIsNotNull(aInstanceLabel); + AssertPointerIsNotNull(aServiceName); + return AsCoreType(aInstance).Get().ResolveService(aInstanceLabel, aServiceName, aCallback, aContext, AsCoreTypePtr(aConfig)); } otError otDnsServiceResponseGetServiceName(const otDnsServiceResponse *aResponse, - char * aLabelBuffer, + char *aLabelBuffer, uint8_t aLabelBufferSize, - char * aNameBuffer, + char *aNameBuffer, uint16_t aNameBufferSize) { + AssertPointerIsNotNull(aLabelBuffer); + AssertPointerIsNotNull(aNameBuffer); + return AsCoreType(aResponse).GetServiceName(aLabelBuffer, aLabelBufferSize, aNameBuffer, aNameBufferSize); } @@ -190,13 +206,15 @@ otError otDnsServiceResponseGetServiceInfo(const otDnsServiceResponse *aResponse } otError otDnsServiceResponseGetHostAddress(const otDnsServiceResponse *aResponse, - const char * aHostName, + const char *aHostName, uint16_t aIndex, - otIp6Address * aAddress, - uint32_t * aTtl) + otIp6Address *aAddress, + uint32_t *aTtl) { uint32_t ttl; + AssertPointerIsNotNull(aHostName); + return AsCoreType(aResponse).GetHostAddress(aHostName, aIndex, AsCoreType(aAddress), (aTtl != nullptr) ? *aTtl : ttl); } diff --git a/src/core/api/dns_server_api.cpp b/src/core/api/dns_server_api.cpp index 5b094487d70..62e69fc17ec 100644 --- a/src/core/api/dns_server_api.cpp +++ b/src/core/api/dns_server_api.cpp @@ -41,20 +41,20 @@ using namespace ot; #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE -void otDnssdQuerySetCallbacks(otInstance * aInstance, +void otDnssdQuerySetCallbacks(otInstance *aInstance, otDnssdQuerySubscribeCallback aSubscribe, otDnssdQueryUnsubscribeCallback aUnsubscribe, - void * aContext) + void *aContext) { AsCoreType(aInstance).Get().SetQueryCallbacks(aSubscribe, aUnsubscribe, aContext); } -void otDnssdQueryHandleDiscoveredServiceInstance(otInstance * aInstance, - const char * aServiceFullName, +void otDnssdQueryHandleDiscoveredServiceInstance(otInstance *aInstance, + const char *aServiceFullName, otDnssdServiceInstanceInfo *aInstanceInfo) { - OT_ASSERT(aServiceFullName != nullptr); - OT_ASSERT(aInstanceInfo != nullptr); + AssertPointerIsNotNull(aServiceFullName); + AssertPointerIsNotNull(aInstanceInfo); AsCoreType(aInstance).Get().HandleDiscoveredServiceInstance(aServiceFullName, *aInstanceInfo); @@ -62,8 +62,8 @@ void otDnssdQueryHandleDiscoveredServiceInstance(otInstance * aIn void otDnssdQueryHandleDiscoveredHost(otInstance *aInstance, const char *aHostFullName, otDnssdHostInfo *aHostInfo) { - OT_ASSERT(aHostFullName != nullptr); - OT_ASSERT(aHostInfo != nullptr); + AssertPointerIsNotNull(aHostFullName); + AssertPointerIsNotNull(aHostInfo); AsCoreType(aInstance).Get().HandleDiscoveredHost(aHostFullName, *aHostInfo); } @@ -75,8 +75,8 @@ const otDnssdQuery *otDnssdGetNextQuery(otInstance *aInstance, const otDnssdQuer otDnssdQueryType otDnssdGetQueryTypeAndName(const otDnssdQuery *aQuery, char (*aNameOutput)[OT_DNS_MAX_NAME_SIZE]) { - OT_ASSERT(aQuery != nullptr); - OT_ASSERT(aNameOutput != nullptr); + AssertPointerIsNotNull(aQuery); + AssertPointerIsNotNull(aNameOutput); return MapEnum(Dns::ServiceDiscovery::Server::GetQueryTypeAndName(aQuery, *aNameOutput)); } @@ -86,4 +86,16 @@ const otDnssdCounters *otDnssdGetCounters(otInstance *aInstance) return &AsCoreType(aInstance).Get().GetCounters(); } +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE +bool otDnssdUpstreamQueryIsEnabled(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().IsUpstreamQueryEnabled(); +} + +void otDnssdUpstreamQuerySetEnabled(otInstance *aInstance, bool aEnabled) +{ + return AsCoreType(aInstance).Get().SetUpstreamQueryEnabled(aEnabled); +} +#endif + #endif // OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE diff --git a/src/core/api/error_api.cpp b/src/core/api/error_api.cpp index cc2b18d50b0..a884150ecc9 100644 --- a/src/core/api/error_api.cpp +++ b/src/core/api/error_api.cpp @@ -37,7 +37,4 @@ using namespace ot; -const char *otThreadErrorToString(otError aError) -{ - return ErrorToString(aError); -} +const char *otThreadErrorToString(otError aError) { return ErrorToString(aError); } diff --git a/src/core/api/heap_api.cpp b/src/core/api/heap_api.cpp index e306c61ec8d..5946212eab5 100644 --- a/src/core/api/heap_api.cpp +++ b/src/core/api/heap_api.cpp @@ -60,13 +60,7 @@ void otHeapFree(void *aPointer) } #else // OPENTHREAD_RADIO -void *otHeapCAlloc(size_t aCount, size_t aSize) -{ - return ot::Heap::CAlloc(aCount, aSize); -} +void *otHeapCAlloc(size_t aCount, size_t aSize) { return ot::Heap::CAlloc(aCount, aSize); } -void otHeapFree(void *aPointer) -{ - ot::Heap::Free(aPointer); -} +void otHeapFree(void *aPointer) { ot::Heap::Free(aPointer); } #endif // OPENTHREAD_RADIO diff --git a/src/core/api/history_tracker_api.cpp b/src/core/api/history_tracker_api.cpp index 5e52b36a793..f3d0357c358 100644 --- a/src/core/api/history_tracker_api.cpp +++ b/src/core/api/history_tracker_api.cpp @@ -43,70 +43,92 @@ using namespace ot; -void otHistoryTrackerInitIterator(otHistoryTrackerIterator *aIterator) -{ - AsCoreType(aIterator).Init(); -} +void otHistoryTrackerInitIterator(otHistoryTrackerIterator *aIterator) { AsCoreType(aIterator).Init(); } -const otHistoryTrackerNetworkInfo *otHistoryTrackerIterateNetInfoHistory(otInstance * aInstance, +const otHistoryTrackerNetworkInfo *otHistoryTrackerIterateNetInfoHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge) + uint32_t *aEntryAge) { + AssertPointerIsNotNull(aEntryAge); + return AsCoreType(aInstance).Get().IterateNetInfoHistory(AsCoreType(aIterator), *aEntryAge); } const otHistoryTrackerUnicastAddressInfo *otHistoryTrackerIterateUnicastAddressHistory( - otInstance * aInstance, + otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge) + uint32_t *aEntryAge) { + AssertPointerIsNotNull(aEntryAge); + return AsCoreType(aInstance).Get().IterateUnicastAddressHistory(AsCoreType(aIterator), *aEntryAge); } const otHistoryTrackerMulticastAddressInfo *otHistoryTrackerIterateMulticastAddressHistory( - otInstance * aInstance, + otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge) + uint32_t *aEntryAge) { + AssertPointerIsNotNull(aEntryAge); + return AsCoreType(aInstance).Get().IterateMulticastAddressHistory(AsCoreType(aIterator), *aEntryAge); } -const otHistoryTrackerMessageInfo *otHistoryTrackerIterateRxHistory(otInstance * aInstance, +const otHistoryTrackerMessageInfo *otHistoryTrackerIterateRxHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge) + uint32_t *aEntryAge) { + AssertPointerIsNotNull(aEntryAge); + return AsCoreType(aInstance).Get().IterateRxHistory(AsCoreType(aIterator), *aEntryAge); } -const otHistoryTrackerMessageInfo *otHistoryTrackerIterateTxHistory(otInstance * aInstance, +const otHistoryTrackerMessageInfo *otHistoryTrackerIterateTxHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge) + uint32_t *aEntryAge) { + AssertPointerIsNotNull(aEntryAge); + return AsCoreType(aInstance).Get().IterateTxHistory(AsCoreType(aIterator), *aEntryAge); } -const otHistoryTrackerNeighborInfo *otHistoryTrackerIterateNeighborHistory(otInstance * aInstance, +const otHistoryTrackerNeighborInfo *otHistoryTrackerIterateNeighborHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge) + uint32_t *aEntryAge) { + AssertPointerIsNotNull(aEntryAge); + return AsCoreType(aInstance).Get().IterateNeighborHistory(AsCoreType(aIterator), *aEntryAge); } -const otHistoryTrackerOnMeshPrefixInfo *otHistoryTrackerIterateOnMeshPrefixHistory(otInstance * aInstance, +const otHistoryTrackerRouterInfo *otHistoryTrackerIterateRouterHistory(otInstance *aInstance, + otHistoryTrackerIterator *aIterator, + uint32_t *aEntryAge) +{ + AssertPointerIsNotNull(aEntryAge); + + return AsCoreType(aInstance).Get().IterateRouterHistory(AsCoreType(aIterator), *aEntryAge); +} + +const otHistoryTrackerOnMeshPrefixInfo *otHistoryTrackerIterateOnMeshPrefixHistory(otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge) + uint32_t *aEntryAge) { + AssertPointerIsNotNull(aEntryAge); + return AsCoreType(aInstance).Get().IterateOnMeshPrefixHistory(AsCoreType(aIterator), *aEntryAge); } const otHistoryTrackerExternalRouteInfo *otHistoryTrackerIterateExternalRouteHistory( - otInstance * aInstance, + otInstance *aInstance, otHistoryTrackerIterator *aIterator, - uint32_t * aEntryAge) + uint32_t *aEntryAge) { + AssertPointerIsNotNull(aEntryAge); + return AsCoreType(aInstance).Get().IterateExternalRouteHistory(AsCoreType(aIterator), *aEntryAge); } diff --git a/src/core/api/icmp6_api.cpp b/src/core/api/icmp6_api.cpp index 55ed192813e..c9df55d64b0 100644 --- a/src/core/api/icmp6_api.cpp +++ b/src/core/api/icmp6_api.cpp @@ -55,8 +55,8 @@ otError otIcmp6RegisterHandler(otInstance *aInstance, otIcmp6Handler *aHandler) return AsCoreType(aInstance).Get().RegisterHandler(AsCoreType(aHandler)); } -otError otIcmp6SendEchoRequest(otInstance * aInstance, - otMessage * aMessage, +otError otIcmp6SendEchoRequest(otInstance *aInstance, + otMessage *aMessage, const otMessageInfo *aMessageInfo, uint16_t aIdentifier) { diff --git a/src/core/api/instance_api.cpp b/src/core/api/instance_api.cpp index 5557f687f59..06db73ce399 100644 --- a/src/core/api/instance_api.cpp +++ b/src/core/api/instance_api.cpp @@ -43,7 +43,7 @@ #if !defined(OPENTHREAD_BUILD_DATETIME) #ifdef __ANDROID__ -#ifdef OPENTHREAD_ENABLE_ANDROID_NDK +#ifdef OPENTHREAD_CONFIG_ANDROID_NDK_ENABLE #include #else #include @@ -67,10 +67,7 @@ otInstance *otInstanceInit(void *aInstanceBuffer, size_t *aInstanceBufferSize) return instance; } #else -otInstance *otInstanceInitSingle(void) -{ - return &Instance::InitSingle(); -} +otInstance *otInstanceInitSingle(void) { return &Instance::InitSingle(); } #endif // #if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE bool otInstanceIsInitialized(otInstance *aInstance) @@ -83,24 +80,17 @@ bool otInstanceIsInitialized(otInstance *aInstance) #endif // OPENTHREAD_MTD || OPENTHREAD_FTD } -void otInstanceFinalize(otInstance *aInstance) -{ - AsCoreType(aInstance).Finalize(); -} +void otInstanceFinalize(otInstance *aInstance) { AsCoreType(aInstance).Finalize(); } -void otInstanceReset(otInstance *aInstance) -{ - AsCoreType(aInstance).Reset(); -} +void otInstanceReset(otInstance *aInstance) { AsCoreType(aInstance).Reset(); } #if OPENTHREAD_CONFIG_UPTIME_ENABLE -uint64_t otInstanceGetUptime(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().GetUptime(); -} +uint64_t otInstanceGetUptime(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetUptime(); } void otInstanceGetUptimeAsString(otInstance *aInstance, char *aBuffer, uint16_t aSize) { + AssertPointerIsNotNull(aBuffer); + AsCoreType(aInstance).Get().GetUptime(aBuffer, aSize); } #endif @@ -116,22 +106,13 @@ void otRemoveStateChangeCallback(otInstance *aInstance, otStateChangedCallback a AsCoreType(aInstance).Get().RemoveCallback(aCallback, aContext); } -void otInstanceFactoryReset(otInstance *aInstance) -{ - AsCoreType(aInstance).FactoryReset(); -} +void otInstanceFactoryReset(otInstance *aInstance) { AsCoreType(aInstance).FactoryReset(); } -otError otInstanceErasePersistentInfo(otInstance *aInstance) -{ - return AsCoreType(aInstance).ErasePersistentInfo(); -} +otError otInstanceErasePersistentInfo(otInstance *aInstance) { return AsCoreType(aInstance).ErasePersistentInfo(); } #endif // OPENTHREAD_MTD || OPENTHREAD_FTD #if OPENTHREAD_RADIO -void otInstanceResetRadioStack(otInstance *aInstance) -{ - AsCoreType(aInstance).ResetRadioStack(); -} +void otInstanceResetRadioStack(otInstance *aInstance) { AsCoreType(aInstance).ResetRadioStack(); } #endif const char *otGetVersionString(void) @@ -154,7 +135,7 @@ const char *otGetVersionString(void) #if !defined(OPENTHREAD_BUILD_DATETIME) && defined(__ANDROID__) -#ifdef OPENTHREAD_ENABLE_ANDROID_NDK +#ifdef OPENTHREAD_CONFIG_ANDROID_NDK_ENABLE static char sVersion[100 + PROP_VALUE_MAX]; char dateTime[PROP_VALUE_MAX]; diff --git a/src/core/api/ip6_api.cpp b/src/core/api/ip6_api.cpp index b12b35ec52f..4ff7d229ce7 100644 --- a/src/core/api/ip6_api.cpp +++ b/src/core/api/ip6_api.cpp @@ -37,6 +37,9 @@ #include "common/as_core_type.hpp" #include "common/locator_getters.hpp" +#include "net/ip4_types.hpp" +#include "net/ip6_headers.hpp" +#include "thread/network_data_leader.hpp" #include "utils/slaac_address.hpp" using namespace ot; @@ -65,10 +68,7 @@ otError otIp6SetEnabled(otInstance *aInstance, bool aEnabled) return error; } -bool otIp6IsEnabled(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().IsUp(); -} +bool otIp6IsEnabled(otInstance *aInstance) { return AsCoreType(aInstance).Get().IsUp(); } const otNetifAddress *otIp6GetUnicastAddresses(otInstance *aInstance) { @@ -133,7 +133,7 @@ void otIp6SetReceiveFilterEnabled(otInstance *aInstance, bool aEnabled) otError otIp6Send(otInstance *aInstance, otMessage *aMessage) { return AsCoreType(aInstance).Get().SendRaw(AsCoreType(aMessage), - !OPENTHREAD_CONFIG_IP6_ALLOW_LOOP_BACK_HOST_DATAGRAMS); + OPENTHREAD_CONFIG_IP6_ALLOW_LOOP_BACK_HOST_DATAGRAMS); } otMessage *otIp6NewMessage(otInstance *aInstance, const otMessageSettings *aSettings) @@ -141,14 +141,13 @@ otMessage *otIp6NewMessage(otInstance *aInstance, const otMessageSettings *aSett return AsCoreType(aInstance).Get().NewMessage(0, Message::Settings::From(aSettings)); } -otMessage *otIp6NewMessageFromBuffer(otInstance * aInstance, - const uint8_t * aData, +otMessage *otIp6NewMessageFromBuffer(otInstance *aInstance, + const uint8_t *aData, uint16_t aDataLength, const otMessageSettings *aSettings) { - return (aSettings != nullptr) - ? AsCoreType(aInstance).Get().NewMessage(aData, aDataLength, AsCoreType(aSettings)) - : AsCoreType(aInstance).Get().NewMessage(aData, aDataLength); + return AsCoreType(aInstance).Get().NewMessageFromData(aData, aDataLength, + Message::Settings::From(aSettings)); } otError otIp6AddUnsecurePort(otInstance *aInstance, uint16_t aPort) @@ -168,6 +167,8 @@ void otIp6RemoveAllUnsecurePorts(otInstance *aInstance) const uint16_t *otIp6GetUnsecurePorts(otInstance *aInstance, uint8_t *aNumEntries) { + AssertPointerIsNotNull(aNumEntries); + return AsCoreType(aInstance).Get().GetUnsecurePorts(*aNumEntries); } @@ -186,53 +187,56 @@ otError otIp6AddressFromString(const char *aString, otIp6Address *aAddress) return AsCoreType(aAddress).FromString(aString); } +otError otIp6PrefixFromString(const char *aString, otIp6Prefix *aPrefix) +{ + return AsCoreType(aPrefix).FromString(aString); +} + void otIp6AddressToString(const otIp6Address *aAddress, char *aBuffer, uint16_t aSize) { + AssertPointerIsNotNull(aBuffer); + AsCoreType(aAddress).ToString(aBuffer, aSize); } void otIp6SockAddrToString(const otSockAddr *aSockAddr, char *aBuffer, uint16_t aSize) { + AssertPointerIsNotNull(aBuffer); + AsCoreType(aSockAddr).ToString(aBuffer, aSize); } void otIp6PrefixToString(const otIp6Prefix *aPrefix, char *aBuffer, uint16_t aSize) { + AssertPointerIsNotNull(aBuffer); + AsCoreType(aPrefix).ToString(aBuffer, aSize); } uint8_t otIp6PrefixMatch(const otIp6Address *aFirst, const otIp6Address *aSecond) { - OT_ASSERT(aFirst != nullptr && aSecond != nullptr); - return AsCoreType(aFirst).PrefixMatch(AsCoreType(aSecond)); } -bool otIp6IsAddressUnspecified(const otIp6Address *aAddress) +void otIp6GetPrefix(const otIp6Address *aAddress, uint8_t aLength, otIp6Prefix *aPrefix) { - return AsCoreType(aAddress).IsUnspecified(); + AsCoreType(aAddress).GetPrefix(aLength, AsCoreType(aPrefix)); } +bool otIp6IsAddressUnspecified(const otIp6Address *aAddress) { return AsCoreType(aAddress).IsUnspecified(); } + otError otIp6SelectSourceAddress(otInstance *aInstance, otMessageInfo *aMessageInfo) { - Error error = kErrorNone; - const Ip6::Netif::UnicastAddress *netifAddr; - - netifAddr = AsCoreType(aInstance).Get().SelectSourceAddress(AsCoreType(aMessageInfo)); - VerifyOrExit(netifAddr != nullptr, error = kErrorNotFound); - aMessageInfo->mSockAddr = netifAddr->GetAddress(); - -exit: - return error; + return AsCoreType(aInstance).Get().SelectSourceAddress(AsCoreType(aMessageInfo)); } #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE -otError otIp6RegisterMulticastListeners(otInstance * aInstance, - const otIp6Address * aAddresses, +otError otIp6RegisterMulticastListeners(otInstance *aInstance, + const otIp6Address *aAddresses, uint8_t aAddressNum, - const uint32_t * aTimeout, + const uint32_t *aTimeout, otIp6RegisterMulticastListenersCallback aCallback, - void * aContext) + void *aContext) { return AsCoreType(aInstance).Get().RegisterMulticastListeners(aAddresses, aAddressNum, aTimeout, aCallback, aContext); @@ -241,10 +245,7 @@ otError otIp6RegisterMulticastListeners(otInstance * #if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE -bool otIp6IsSlaacEnabled(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().IsEnabled(); -} +bool otIp6IsSlaacEnabled(otInstance *aInstance) { return AsCoreType(aInstance).Get().IsEnabled(); } void otIp6SetSlaacEnabled(otInstance *aInstance, bool aEnabled) { @@ -276,7 +277,16 @@ otError otIp6SetMeshLocalIid(otInstance *aInstance, const otIp6InterfaceIdentifi #endif -const char *otIp6ProtoToString(uint8_t aIpProto) +const char *otIp6ProtoToString(uint8_t aIpProto) { return Ip6::Ip6::IpProtoToString(aIpProto); } + +#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE +const otBorderRoutingCounters *otIp6GetBorderRoutingCounters(otInstance *aInstance) { - return Ip6::Ip6::IpProtoToString(aIpProto); + return &AsCoreType(aInstance).Get().GetBorderRoutingCounters(); } + +void otIp6ResetBorderRoutingCounters(otInstance *aInstance) +{ + AsCoreType(aInstance).Get().ResetBorderRoutingCounters(); +} +#endif diff --git a/src/core/api/jam_detection_api.cpp b/src/core/api/jam_detection_api.cpp index d108bdd0898..277f64cde9f 100644 --- a/src/core/api/jam_detection_api.cpp +++ b/src/core/api/jam_detection_api.cpp @@ -79,10 +79,7 @@ otError otJamDetectionStart(otInstance *aInstance, otJamDetectionCallback aCallb return AsCoreType(aInstance).Get().Start(aCallback, aContext); } -otError otJamDetectionStop(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().Stop(); -} +otError otJamDetectionStop(otInstance *aInstance) { return AsCoreType(aInstance).Get().Stop(); } bool otJamDetectionIsEnabled(otInstance *aInstance) { diff --git a/src/core/api/joiner_api.cpp b/src/core/api/joiner_api.cpp index 2ee2ac3a43e..ceee3945098 100644 --- a/src/core/api/joiner_api.cpp +++ b/src/core/api/joiner_api.cpp @@ -43,24 +43,21 @@ using namespace ot; -otError otJoinerStart(otInstance * aInstance, - const char * aPskd, - const char * aProvisioningUrl, - const char * aVendorName, - const char * aVendorModel, - const char * aVendorSwVersion, - const char * aVendorData, +otError otJoinerStart(otInstance *aInstance, + const char *aPskd, + const char *aProvisioningUrl, + const char *aVendorName, + const char *aVendorModel, + const char *aVendorSwVersion, + const char *aVendorData, otJoinerCallback aCallback, - void * aContext) + void *aContext) { return AsCoreType(aInstance).Get().Start(aPskd, aProvisioningUrl, aVendorName, aVendorModel, aVendorSwVersion, aVendorData, aCallback, aContext); } -void otJoinerStop(otInstance *aInstance) -{ - AsCoreType(aInstance).Get().Stop(); -} +void otJoinerStop(otInstance *aInstance) { AsCoreType(aInstance).Get().Stop(); } otJoinerState otJoinerGetState(otInstance *aInstance) { diff --git a/src/core/api/link_api.cpp b/src/core/api/link_api.cpp index ff5922deae2..56ca54921be 100644 --- a/src/core/api/link_api.cpp +++ b/src/core/api/link_api.cpp @@ -112,7 +112,6 @@ otError otLinkSetExtendedAddress(otInstance *aInstance, const otExtAddress *aExt Error error = kErrorNone; Instance &instance = AsCoreType(aInstance); - OT_ASSERT(aExtAddress != nullptr); VerifyOrExit(instance.Get().IsDisabled(), error = kErrorInvalidState); instance.Get().SetExtAddress(AsCoreType(aExtAddress)); @@ -128,10 +127,7 @@ void otLinkGetFactoryAssignedIeeeEui64(otInstance *aInstance, otExtAddress *aEui AsCoreType(aInstance).Get().GetIeeeEui64(AsCoreType(aEui64)); } -otPanId otLinkGetPanId(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().GetPanId(); -} +otPanId otLinkGetPanId(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetPanId(); } otError otLinkSetPanId(otInstance *aInstance, otPanId aPanId) { @@ -206,15 +202,11 @@ void otLinkFilterSetAddressMode(otInstance *aInstance, otMacFilterAddressMode aM otError otLinkFilterAddAddress(otInstance *aInstance, const otExtAddress *aExtAddress) { - OT_ASSERT(aExtAddress != nullptr); - return AsCoreType(aInstance).Get().AddAddress(AsCoreType(aExtAddress)); } void otLinkFilterRemoveAddress(otInstance *aInstance, const otExtAddress *aExtAddress) { - OT_ASSERT(aExtAddress != nullptr); - AsCoreType(aInstance).Get().RemoveAddress(AsCoreType(aExtAddress)); } @@ -225,22 +217,19 @@ void otLinkFilterClearAddresses(otInstance *aInstance) otError otLinkFilterGetNextAddress(otInstance *aInstance, otMacFilterIterator *aIterator, otMacFilterEntry *aEntry) { - OT_ASSERT(aIterator != nullptr && aEntry != nullptr); + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aEntry); return AsCoreType(aInstance).Get().GetNextAddress(*aIterator, *aEntry); } otError otLinkFilterAddRssIn(otInstance *aInstance, const otExtAddress *aExtAddress, int8_t aRss) { - OT_ASSERT(aExtAddress != nullptr); - return AsCoreType(aInstance).Get().AddRssIn(AsCoreType(aExtAddress), aRss); } void otLinkFilterRemoveRssIn(otInstance *aInstance, const otExtAddress *aExtAddress) { - OT_ASSERT(aExtAddress != nullptr); - AsCoreType(aInstance).Get().RemoveRssIn(AsCoreType(aExtAddress)); } @@ -254,14 +243,12 @@ void otLinkFilterClearDefaultRssIn(otInstance *aInstance) AsCoreType(aInstance).Get().ClearDefaultRssIn(); } -void otLinkFilterClearAllRssIn(otInstance *aInstance) -{ - AsCoreType(aInstance).Get().ClearAllRssIn(); -} +void otLinkFilterClearAllRssIn(otInstance *aInstance) { AsCoreType(aInstance).Get().ClearAllRssIn(); } otError otLinkFilterGetNextRssIn(otInstance *aInstance, otMacFilterIterator *aIterator, otMacFilterEntry *aEntry) { - OT_ASSERT(aIterator != nullptr && aEntry != nullptr); + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aEntry); return AsCoreType(aInstance).Get().GetNextRssIn(*aIterator, *aEntry); } @@ -282,18 +269,20 @@ bool otLinkIsRadioFilterEnabled(otInstance *aInstance) uint8_t otLinkConvertRssToLinkQuality(otInstance *aInstance, int8_t aRss) { - return LinkQualityInfo::ConvertRssToLinkQuality(AsCoreType(aInstance).Get().GetNoiseFloor(), aRss); + return LinkQualityForLinkMargin(AsCoreType(aInstance).Get().ComputeLinkMargin(aRss)); } int8_t otLinkConvertLinkQualityToRss(otInstance *aInstance, uint8_t aLinkQuality) { - return LinkQualityInfo::ConvertLinkQualityToRss(AsCoreType(aInstance).Get().GetNoiseFloor(), - static_cast(aLinkQuality)); + return GetTypicalRssForLinkQuality(AsCoreType(aInstance).Get().GetNoiseFloor(), + static_cast(aLinkQuality)); } #if OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE const uint32_t *otLinkGetTxDirectRetrySuccessHistogram(otInstance *aInstance, uint8_t *aNumberOfEntries) { + AssertPointerIsNotNull(aNumberOfEntries); + return AsCoreType(aInstance).Get().GetDirectRetrySuccessHistogram(*aNumberOfEntries); } @@ -301,6 +290,8 @@ const uint32_t *otLinkGetTxIndirectRetrySuccessHistogram(otInstance *aInstance, { const uint32_t *histogram = nullptr; + AssertPointerIsNotNull(aNumberOfEntries); + #if OPENTHREAD_FTD histogram = AsCoreType(aInstance).Get().GetIndirectRetrySuccessHistogram(*aNumberOfEntries); #else @@ -322,10 +313,7 @@ void otLinkSetPcapCallback(otInstance *aInstance, otLinkPcapCallback aPcapCallba AsCoreType(aInstance).Get().SetPcapCallback(aPcapCallback, aCallbackContext); } -bool otLinkIsPromiscuous(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().IsPromiscuous(); -} +bool otLinkIsPromiscuous(otInstance *aInstance) { return AsCoreType(aInstance).Get().IsPromiscuous(); } otError otLinkSetPromiscuous(otInstance *aInstance, bool aPromiscuous) { @@ -355,26 +343,20 @@ otError otLinkSetEnabled(otInstance *aInstance, bool aEnable) return error; } -bool otLinkIsEnabled(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().IsEnabled(); -} +bool otLinkIsEnabled(otInstance *aInstance) { return AsCoreType(aInstance).Get().IsEnabled(); } const otMacCounters *otLinkGetCounters(otInstance *aInstance) { return &AsCoreType(aInstance).Get().GetCounters(); } -void otLinkResetCounters(otInstance *aInstance) -{ - AsCoreType(aInstance).Get().ResetCounters(); -} +void otLinkResetCounters(otInstance *aInstance) { AsCoreType(aInstance).Get().ResetCounters(); } -otError otLinkActiveScan(otInstance * aInstance, +otError otLinkActiveScan(otInstance *aInstance, uint32_t aScanChannels, uint16_t aScanDuration, otHandleActiveScanResult aCallback, - void * aCallbackContext) + void *aCallbackContext) { return AsCoreType(aInstance).Get().ActiveScan(aScanChannels, aScanDuration, aCallback, aCallbackContext); } @@ -384,11 +366,11 @@ bool otLinkIsActiveScanInProgress(otInstance *aInstance) return AsCoreType(aInstance).Get().IsActiveScanInProgress(); } -otError otLinkEnergyScan(otInstance * aInstance, +otError otLinkEnergyScan(otInstance *aInstance, uint32_t aScanChannels, uint16_t aScanDuration, otHandleEnergyScanResult aCallback, - void * aCallbackContext) + void *aCallbackContext) { return AsCoreType(aInstance).Get().EnergyScan(aScanChannels, aScanDuration, aCallback, aCallbackContext); } @@ -409,10 +391,7 @@ uint16_t otLinkGetCcaFailureRate(otInstance *aInstance) } #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -uint8_t otLinkCslGetChannel(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().GetCslChannel(); -} +uint8_t otLinkCslGetChannel(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetCslChannel(); } otError otLinkCslSetChannel(otInstance *aInstance, uint8_t aChannel) { @@ -426,10 +405,7 @@ otError otLinkCslSetChannel(otInstance *aInstance, uint8_t aChannel) return error; } -uint16_t otLinkCslGetPeriod(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().GetCslPeriod(); -} +uint16_t otLinkCslGetPeriod(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetCslPeriod(); } otError otLinkCslSetPeriod(otInstance *aInstance, uint16_t aPeriod) { diff --git a/src/core/api/link_metrics_api.cpp b/src/core/api/link_metrics_api.cpp index b14a915a08d..548876d279d 100644 --- a/src/core/api/link_metrics_api.cpp +++ b/src/core/api/link_metrics_api.cpp @@ -33,8 +33,7 @@ #include "openthread-core-config.h" -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE - +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE #include #include "common/as_core_type.hpp" @@ -42,70 +41,63 @@ using namespace ot; -otError otLinkMetricsQuery(otInstance * aInstance, - const otIp6Address * aDestination, +otError otLinkMetricsQuery(otInstance *aInstance, + const otIp6Address *aDestination, uint8_t aSeriesId, - const otLinkMetrics * aLinkMetricsFlags, + const otLinkMetrics *aLinkMetricsFlags, otLinkMetricsReportCallback aCallback, - void * aCallbackContext) + void *aCallbackContext) { - OT_ASSERT(aDestination != nullptr); + AsCoreType(aInstance).Get().SetReportCallback(aCallback, aCallbackContext); - AsCoreType(aInstance).Get().SetReportCallback(aCallback, aCallbackContext); - - return AsCoreType(aInstance).Get().Query(AsCoreType(aDestination), aSeriesId, - AsCoreTypePtr(aLinkMetricsFlags)); + return AsCoreType(aInstance).Get().Query(AsCoreType(aDestination), aSeriesId, + AsCoreTypePtr(aLinkMetricsFlags)); } -otError otLinkMetricsConfigForwardTrackingSeries(otInstance * aInstance, - const otIp6Address * aDestination, +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE +otError otLinkMetricsConfigForwardTrackingSeries(otInstance *aInstance, + const otIp6Address *aDestination, uint8_t aSeriesId, const otLinkMetricsSeriesFlags aSeriesFlags, - const otLinkMetrics * aLinkMetricsFlags, + const otLinkMetrics *aLinkMetricsFlags, otLinkMetricsMgmtResponseCallback aCallback, - void * aCallbackContext) + void *aCallbackContext) { - OT_ASSERT(aDestination != nullptr); + LinkMetrics::Initiator &initiator = AsCoreType(aInstance).Get(); - LinkMetrics::LinkMetrics &linkMetrics = AsCoreType(aInstance).Get(); + initiator.SetMgmtResponseCallback(aCallback, aCallbackContext); - linkMetrics.SetMgmtResponseCallback(aCallback, aCallbackContext); - - return linkMetrics.SendMgmtRequestForwardTrackingSeries(AsCoreType(aDestination), aSeriesId, aSeriesFlags, - AsCoreTypePtr(aLinkMetricsFlags)); + return initiator.SendMgmtRequestForwardTrackingSeries(AsCoreType(aDestination), aSeriesId, + AsCoreType(&aSeriesFlags), AsCoreTypePtr(aLinkMetricsFlags)); } -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE -otError otLinkMetricsConfigEnhAckProbing(otInstance * aInstance, - const otIp6Address * aDestination, +otError otLinkMetricsConfigEnhAckProbing(otInstance *aInstance, + const otIp6Address *aDestination, otLinkMetricsEnhAckFlags aEnhAckFlags, - const otLinkMetrics * aLinkMetricsFlags, + const otLinkMetrics *aLinkMetricsFlags, otLinkMetricsMgmtResponseCallback aCallback, - void * aCallbackContext, + void *aCallbackContext, otLinkMetricsEnhAckProbingIeReportCallback aEnhAckCallback, - void * aEnhAckCallbackContext) + void *aEnhAckCallbackContext) { - OT_ASSERT(aDestination != nullptr); - - LinkMetrics::LinkMetrics &linkMetrics = AsCoreType(aInstance).Get(); + LinkMetrics::Initiator &initiator = AsCoreType(aInstance).Get(); - linkMetrics.SetMgmtResponseCallback(aCallback, aCallbackContext); - linkMetrics.SetEnhAckProbingCallback(aEnhAckCallback, aEnhAckCallbackContext); + initiator.SetMgmtResponseCallback(aCallback, aCallbackContext); + initiator.SetEnhAckProbingCallback(aEnhAckCallback, aEnhAckCallbackContext); - return linkMetrics.SendMgmtRequestEnhAckProbing(AsCoreType(aDestination), MapEnum(aEnhAckFlags), - AsCoreTypePtr(aLinkMetricsFlags)); + return initiator.SendMgmtRequestEnhAckProbing(AsCoreType(aDestination), MapEnum(aEnhAckFlags), + AsCoreTypePtr(aLinkMetricsFlags)); } -otError otLinkMetricsSendLinkProbe(otInstance * aInstance, +otError otLinkMetricsSendLinkProbe(otInstance *aInstance, const otIp6Address *aDestination, uint8_t aSeriesId, uint8_t aLength) { - OT_ASSERT(aDestination != nullptr); - LinkMetrics::LinkMetrics &linkMetrics = AsCoreType(aInstance).Get(); + LinkMetrics::Initiator &initiator = AsCoreType(aInstance).Get(); - return linkMetrics.SendLinkProbe(AsCoreType(aDestination), aSeriesId, aLength); + return initiator.SendLinkProbe(AsCoreType(aDestination), aSeriesId, aLength); } #endif -#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE diff --git a/src/core/api/link_raw_api.cpp b/src/core/api/link_raw_api.cpp index fafe69c3801..12a76d53d3a 100644 --- a/src/core/api/link_raw_api.cpp +++ b/src/core/api/link_raw_api.cpp @@ -54,20 +54,14 @@ otError otLinkRawSetReceiveDone(otInstance *aInstance, otLinkRawReceiveDone aCal return AsCoreType(aInstance).Get().SetReceiveDone(aCallback); } -bool otLinkRawIsEnabled(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().IsEnabled(); -} +bool otLinkRawIsEnabled(otInstance *aInstance) { return AsCoreType(aInstance).Get().IsEnabled(); } otError otLinkRawSetShortAddress(otInstance *aInstance, uint16_t aShortAddress) { return AsCoreType(aInstance).Get().SetShortAddress(aShortAddress); } -bool otLinkRawGetPromiscuous(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().GetPromiscuous(); -} +bool otLinkRawGetPromiscuous(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetPromiscuous(); } otError otLinkRawSetPromiscuous(otInstance *aInstance, bool aEnable) { @@ -94,10 +88,7 @@ otError otLinkRawSleep(otInstance *aInstance) return error; } -otError otLinkRawReceive(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().Receive(); -} +otError otLinkRawReceive(otInstance *aInstance) { return AsCoreType(aInstance).Get().Receive(); } otRadioFrame *otLinkRawGetTransmitBuffer(otInstance *aInstance) { @@ -109,17 +100,11 @@ otError otLinkRawTransmit(otInstance *aInstance, otLinkRawTransmitDone aCallback return AsCoreType(aInstance).Get().Transmit(aCallback); } -int8_t otLinkRawGetRssi(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().GetRssi(); -} +int8_t otLinkRawGetRssi(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetRssi(); } -otRadioCaps otLinkRawGetCaps(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().GetCaps(); -} +otRadioCaps otLinkRawGetCaps(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetCaps(); } -otError otLinkRawEnergyScan(otInstance * aInstance, +otError otLinkRawEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration, otLinkRawEnergyScanDone aCallback) @@ -157,7 +142,9 @@ otError otLinkRawSrcMatchAddExtEntry(otInstance *aInstance, const otExtAddress * { Mac::ExtAddress address; Error error = kErrorNone; - Instance & instance = AsCoreType(aInstance); + Instance &instance = AsCoreType(aInstance); + + AssertPointerIsNotNull(aExtAddress); VerifyOrExit(instance.Get().IsEnabled(), error = kErrorInvalidState); @@ -184,7 +171,9 @@ otError otLinkRawSrcMatchClearExtEntry(otInstance *aInstance, const otExtAddress { Mac::ExtAddress address; Error error = kErrorNone; - Instance & instance = AsCoreType(aInstance); + Instance &instance = AsCoreType(aInstance); + + AssertPointerIsNotNull(aExtAddress); VerifyOrExit(instance.Get().IsEnabled(), error = kErrorInvalidState); @@ -221,7 +210,7 @@ otError otLinkRawSrcMatchClearExtEntries(otInstance *aInstance) return error; } -otError otLinkRawSetMacKey(otInstance * aInstance, +otError otLinkRawSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKeyId, const otMacKey *aPrevKey, @@ -234,7 +223,12 @@ otError otLinkRawSetMacKey(otInstance * aInstance, otError otLinkRawSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter) { - return AsCoreType(aInstance).Get().SetMacFrameCounter(aMacFrameCounter); + return AsCoreType(aInstance).Get().SetMacFrameCounter(aMacFrameCounter, /* aSetIfLarger */ false); +} + +otError otLinkRawSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacFrameCounter) +{ + return AsCoreType(aInstance).Get().SetMacFrameCounter(aMacFrameCounter, /* aSetIfLarger */ true); } uint64_t otLinkRawGetRadioTime(otInstance *aInstance) @@ -251,20 +245,14 @@ otDeviceRole otThreadGetDeviceRole(otInstance *aInstance) return OT_DEVICE_ROLE_DISABLED; } -uint8_t otLinkGetChannel(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().GetChannel(); -} +uint8_t otLinkGetChannel(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetChannel(); } otError otLinkSetChannel(otInstance *aInstance, uint8_t aChannel) { return AsCoreType(aInstance).Get().SetChannel(aChannel); } -otPanId otLinkGetPanId(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().GetPanId(); -} +otPanId otLinkGetPanId(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetPanId(); } otError otLinkSetPanId(otInstance *aInstance, uint16_t aPanId) { @@ -288,6 +276,8 @@ uint16_t otLinkGetShortAddress(otInstance *aInstance) void otLinkGetFactoryAssignedIeeeEui64(otInstance *aInstance, otExtAddress *aEui64) { + AssertPointerIsNotNull(aEui64); + otPlatRadioGetIeeeEui64(aInstance, aEui64->m8); } diff --git a/src/core/api/logging_api.cpp b/src/core/api/logging_api.cpp index 3218837087d..edce6b9201b 100644 --- a/src/core/api/logging_api.cpp +++ b/src/core/api/logging_api.cpp @@ -41,10 +41,7 @@ using namespace ot; -otLogLevel otLoggingGetLevel(void) -{ - return static_cast(Instance::GetLogLevel()); -} +otLogLevel otLoggingGetLevel(void) { return static_cast(Instance::GetLogLevel()); } #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE otError otLoggingSetLevel(otLogLevel aLogLevel) diff --git a/src/core/api/mesh_diag_api.cpp b/src/core/api/mesh_diag_api.cpp new file mode 100644 index 00000000000..888476a1a37 --- /dev/null +++ b/src/core/api/mesh_diag_api.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements the Mesh Diagnostics public APIs. + */ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD + +#include + +#include "common/as_core_type.hpp" +#include "common/locator_getters.hpp" +#include "utils/mesh_diag.hpp" + +using namespace ot; + +otError otMeshDiagDiscoverTopology(otInstance *aInstance, + const otMeshDiagDiscoverConfig *aConfig, + otMeshDiagDiscoverCallback aCallback, + void *aContext) +{ + AssertPointerIsNotNull(aConfig); + return AsCoreType(aInstance).Get().DiscoverTopology(*aConfig, aCallback, aContext); +} + +void otMeshDiagCancel(otInstance *aInstance) { AsCoreType(aInstance).Get().Cancel(); } + +otError otMeshDiagGetNextIp6Address(otMeshDiagIp6AddrIterator *aIterator, otIp6Address *aIp6Address) +{ + return AsCoreType(aIterator).GetNextAddress(AsCoreType(aIp6Address)); +} + +otError otMeshDiagGetNextChildInfo(otMeshDiagChildIterator *aIterator, otMeshDiagChildInfo *aChildInfo) +{ + return AsCoreType(aIterator).GetNextChildInfo(AsCoreType(aChildInfo)); +} + +#endif // OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD diff --git a/src/core/api/message_api.cpp b/src/core/api/message_api.cpp index ecc3bfc4d83..eafc1d2c729 100644 --- a/src/core/api/message_api.cpp +++ b/src/core/api/message_api.cpp @@ -40,35 +40,17 @@ using namespace ot; -void otMessageFree(otMessage *aMessage) -{ - AsCoreType(aMessage).Free(); -} +void otMessageFree(otMessage *aMessage) { AsCoreType(aMessage).Free(); } -uint16_t otMessageGetLength(const otMessage *aMessage) -{ - return AsCoreType(aMessage).GetLength(); -} +uint16_t otMessageGetLength(const otMessage *aMessage) { return AsCoreType(aMessage).GetLength(); } -otError otMessageSetLength(otMessage *aMessage, uint16_t aLength) -{ - return AsCoreType(aMessage).SetLength(aLength); -} +otError otMessageSetLength(otMessage *aMessage, uint16_t aLength) { return AsCoreType(aMessage).SetLength(aLength); } -uint16_t otMessageGetOffset(const otMessage *aMessage) -{ - return AsCoreType(aMessage).GetOffset(); -} +uint16_t otMessageGetOffset(const otMessage *aMessage) { return AsCoreType(aMessage).GetOffset(); } -void otMessageSetOffset(otMessage *aMessage, uint16_t aOffset) -{ - AsCoreType(aMessage).SetOffset(aOffset); -} +void otMessageSetOffset(otMessage *aMessage, uint16_t aOffset) { AsCoreType(aMessage).SetOffset(aOffset); } -bool otMessageIsLinkSecurityEnabled(const otMessage *aMessage) -{ - return AsCoreType(aMessage).IsLinkSecurityEnabled(); -} +bool otMessageIsLinkSecurityEnabled(const otMessage *aMessage) { return AsCoreType(aMessage).IsLinkSecurityEnabled(); } void otMessageSetDirectTransmission(otMessage *aMessage, bool aEnabled) { @@ -82,23 +64,26 @@ void otMessageSetDirectTransmission(otMessage *aMessage, bool aEnabled) } } -int8_t otMessageGetRss(const otMessage *aMessage) -{ - return AsCoreType(aMessage).GetAverageRss(); -} +int8_t otMessageGetRss(const otMessage *aMessage) { return AsCoreType(aMessage).GetAverageRss(); } otError otMessageAppend(otMessage *aMessage, const void *aBuf, uint16_t aLength) { + AssertPointerIsNotNull(aBuf); + return AsCoreType(aMessage).AppendBytes(aBuf, aLength); } uint16_t otMessageRead(const otMessage *aMessage, uint16_t aOffset, void *aBuf, uint16_t aLength) { + AssertPointerIsNotNull(aBuf); + return AsCoreType(aMessage).ReadBytes(aOffset, aBuf, aLength); } int otMessageWrite(otMessage *aMessage, uint16_t aOffset, const void *aBuf, uint16_t aLength) { + AssertPointerIsNotNull(aBuf); + AsCoreType(aMessage).WriteBytes(aOffset, aBuf, aLength); return aLength; @@ -106,6 +91,8 @@ int otMessageWrite(otMessage *aMessage, uint16_t aOffset, const void *aBuf, uint void otMessageQueueInit(otMessageQueue *aQueue) { + AssertPointerIsNotNull(aQueue); + aQueue->mData = nullptr; } @@ -124,10 +111,7 @@ void otMessageQueueDequeue(otMessageQueue *aQueue, otMessage *aMessage) AsCoreType(aQueue).Dequeue(AsCoreType(aMessage)); } -otMessage *otMessageQueueGetHead(otMessageQueue *aQueue) -{ - return AsCoreType(aQueue).GetHead(); -} +otMessage *otMessageQueueGetHead(otMessageQueue *aQueue) { return AsCoreType(aQueue).GetHead(); } otMessage *otMessageQueueGetNext(otMessageQueue *aQueue, const otMessage *aMessage) { @@ -147,4 +131,6 @@ void otMessageGetBufferInfo(otInstance *aInstance, otBufferInfo *aBufferInfo) { AsCoreType(aInstance).GetBufferInfo(AsCoreType(aBufferInfo)); } + +void otMessageResetBufferInfo(otInstance *aInstance) { AsCoreType(aInstance).ResetBufferInfo(); } #endif // OPENTHREAD_MTD || OPENTHREAD_FTD diff --git a/src/core/api/multi_radio_api.cpp b/src/core/api/multi_radio_api.cpp index 10bd547c455..830456a6d88 100644 --- a/src/core/api/multi_radio_api.cpp +++ b/src/core/api/multi_radio_api.cpp @@ -44,8 +44,8 @@ using namespace ot; -otError otMultiRadioGetNeighborInfo(otInstance * aInstance, - const otExtAddress * aExtAddress, +otError otMultiRadioGetNeighborInfo(otInstance *aInstance, + const otExtAddress *aExtAddress, otMultiRadioNeighborInfo *aNeighborInfo) { Error error = kErrorNone; diff --git a/src/core/api/nat64_api.cpp b/src/core/api/nat64_api.cpp new file mode 100644 index 00000000000..7405b9d6cd8 --- /dev/null +++ b/src/core/api/nat64_api.cpp @@ -0,0 +1,176 @@ +/* + * 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 the OpenThread APIs for handling IPv4 (NAT64) messages + */ + +#include "openthread-core-config.h" + +#include +#include +#include + +#include "border_router/routing_manager.hpp" +#include "common/debug.hpp" +#include "common/instance.hpp" +#include "net/ip4_types.hpp" +#include "net/ip6_headers.hpp" +#include "net/nat64_translator.hpp" + +using namespace ot; + +// Note: We support the following scenarios: +// - Using OpenThread's routing manager, while using external NAT64 translator (like tayga). +// - Using OpenThread's NAT64 translator, while using external routing manager. +// So OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE translator and OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE are two +// separate build flags and they are not depending on each other. + +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE +otError otNat64SetIp4Cidr(otInstance *aInstance, const otIp4Cidr *aCidr) +{ + return AsCoreType(aInstance).Get().SetIp4Cidr(AsCoreType(aCidr)); +} + +otMessage *otIp4NewMessage(otInstance *aInstance, const otMessageSettings *aSettings) +{ + return AsCoreType(aInstance).Get().NewIp4Message(Message::Settings::From(aSettings)); +} + +otError otNat64Send(otInstance *aInstance, otMessage *aMessage) +{ + return AsCoreType(aInstance).Get().SendMessage(AsCoreType(aMessage)); +} + +void otNat64SetReceiveIp4Callback(otInstance *aInstance, otNat64ReceiveIp4Callback aCallback, void *aContext) +{ + AsCoreType(aInstance).Get().SetNat64ReceiveIp4DatagramCallback(aCallback, aContext); +} + +void otNat64InitAddressMappingIterator(otInstance *aInstance, otNat64AddressMappingIterator *aIterator) +{ + AssertPointerIsNotNull(aIterator); + + AsCoreType(aInstance).Get().InitAddressMappingIterator(*aIterator); +} + +otError otNat64GetNextAddressMapping(otInstance *aInstance, + otNat64AddressMappingIterator *aIterator, + otNat64AddressMapping *aMapping) +{ + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aMapping); + + return AsCoreType(aInstance).Get().GetNextAddressMapping(*aIterator, *aMapping); +} + +void otNat64GetCounters(otInstance *aInstance, otNat64ProtocolCounters *aCounters) +{ + AsCoreType(aInstance).Get().GetCounters(AsCoreType(aCounters)); +} + +void otNat64GetErrorCounters(otInstance *aInstance, otNat64ErrorCounters *aCounters) +{ + AsCoreType(aInstance).Get().GetErrorCounters(AsCoreType(aCounters)); +} + +otError otNat64GetCidr(otInstance *aInstance, otIp4Cidr *aCidr) +{ + return AsCoreType(aInstance).Get().GetIp4Cidr(AsCoreType(aCidr)); +} + +otNat64State otNat64GetTranslatorState(otInstance *aInstance) +{ + return MapEnum(AsCoreType(aInstance).Get().GetState()); +} +#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE +otNat64State otNat64GetPrefixManagerState(otInstance *aInstance) +{ + return MapEnum(AsCoreType(aInstance).Get().GetNat64PrefixManagerState()); +} +#endif + +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE +void otNat64SetEnabled(otInstance *aInstance, bool aEnabled) +{ +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + AsCoreType(aInstance).Get().SetNat64PrefixManagerEnabled(aEnabled); +#endif +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + AsCoreType(aInstance).Get().SetEnabled(aEnabled); +#endif +} +#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE || OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + +bool otIp4IsAddressEqual(const otIp4Address *aFirst, const otIp4Address *aSecond) +{ + return AsCoreType(aFirst) == AsCoreType(aSecond); +} + +void otIp4ExtractFromIp6Address(uint8_t aPrefixLength, const otIp6Address *aIp6Address, otIp4Address *aIp4Address) +{ + AsCoreType(aIp4Address).ExtractFromIp6Address(aPrefixLength, AsCoreType(aIp6Address)); +} + +otError otIp4AddressFromString(const char *aString, otIp4Address *aAddress) +{ + AssertPointerIsNotNull(aString); + return AsCoreType(aAddress).FromString(aString); +} + +otError otNat64SynthesizeIp6Address(otInstance *aInstance, const otIp4Address *aIp4Address, otIp6Address *aIp6Address) +{ + otError err = OT_ERROR_NONE; + NetworkData::ExternalRouteConfig nat64Prefix; + + VerifyOrExit(AsCoreType(aInstance).Get().GetPreferredNat64Prefix(nat64Prefix) == OT_ERROR_NONE, + err = OT_ERROR_INVALID_STATE); + AsCoreType(aIp6Address).SynthesizeFromIp4Address(nat64Prefix.GetPrefix(), AsCoreType(aIp4Address)); + +exit: + return err; +} + +void otIp4AddressToString(const otIp4Address *aAddress, char *aBuffer, uint16_t aSize) +{ + AssertPointerIsNotNull(aBuffer); + + AsCoreType(aAddress).ToString(aBuffer, aSize); +} + +otError otIp4CidrFromString(const char *aString, otIp4Cidr *aCidr) { return AsCoreType(aCidr).FromString(aString); } + +void otIp4CidrToString(const otIp4Cidr *aCidr, char *aBuffer, uint16_t aSize) +{ + AssertPointerIsNotNull(aBuffer); + + AsCoreType(aCidr).ToString(aBuffer, aSize); +} diff --git a/src/core/api/netdata_api.cpp b/src/core/api/netdata_api.cpp index c28b9872741..28e06c7f5b4 100644 --- a/src/core/api/netdata_api.cpp +++ b/src/core/api/netdata_api.cpp @@ -42,22 +42,35 @@ using namespace ot; otError otNetDataGet(otInstance *aInstance, bool aStable, uint8_t *aData, uint8_t *aDataLength) { + AssertPointerIsNotNull(aData); + AssertPointerIsNotNull(aDataLength); + return AsCoreType(aInstance).Get().CopyNetworkData( aStable ? NetworkData::kStableSubset : NetworkData::kFullSet, aData, *aDataLength); } -otError otNetDataGetNextOnMeshPrefix(otInstance * aInstance, - otNetworkDataIterator *aIterator, - otBorderRouterConfig * aConfig) +uint8_t otNetDataGetLength(otInstance *aInstance) { - Error error = kErrorNone; + return AsCoreType(aInstance).Get().GetLength(); +} - VerifyOrExit(aIterator && aConfig, error = kErrorInvalidArgs); +uint8_t otNetDataGetMaxLength(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetMaxLength(); +} + +void otNetDataResetMaxLength(otInstance *aInstance) +{ + AsCoreType(aInstance).Get().ResetMaxLength(); +} - error = AsCoreType(aInstance).Get().GetNextOnMeshPrefix(*aIterator, AsCoreType(aConfig)); +otError otNetDataGetNextOnMeshPrefix(otInstance *aInstance, + otNetworkDataIterator *aIterator, + otBorderRouterConfig *aConfig) +{ + AssertPointerIsNotNull(aIterator); -exit: - return error; + return AsCoreType(aInstance).Get().GetNextOnMeshPrefix(*aIterator, AsCoreType(aConfig)); } #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE @@ -69,26 +82,26 @@ bool otNetDataContainsOmrPrefix(otInstance *aInstance, const otIp6Prefix *aPrefi otError otNetDataGetNextRoute(otInstance *aInstance, otNetworkDataIterator *aIterator, otExternalRouteConfig *aConfig) { - Error error = kErrorNone; - - VerifyOrExit(aIterator && aConfig, error = kErrorInvalidArgs); + AssertPointerIsNotNull(aIterator); - error = AsCoreType(aInstance).Get().GetNextExternalRoute(*aIterator, AsCoreType(aConfig)); - -exit: - return error; + return AsCoreType(aInstance).Get().GetNextExternalRoute(*aIterator, AsCoreType(aConfig)); } otError otNetDataGetNextService(otInstance *aInstance, otNetworkDataIterator *aIterator, otServiceConfig *aConfig) { - Error error = kErrorNone; + AssertPointerIsNotNull(aIterator); - VerifyOrExit(aIterator && aConfig, error = kErrorInvalidArgs); + return AsCoreType(aInstance).Get().GetNextService(*aIterator, AsCoreType(aConfig)); +} - error = AsCoreType(aInstance).Get().GetNextService(*aIterator, AsCoreType(aConfig)); +otError otNetDataGetNextLowpanContextInfo(otInstance *aInstance, + otNetworkDataIterator *aIterator, + otLowpanContextInfo *aContextInfo) +{ + AssertPointerIsNotNull(aIterator); -exit: - return error; + return AsCoreType(aInstance).Get().GetNextLowpanContextInfo(*aIterator, + AsCoreType(aContextInfo)); } uint8_t otNetDataGetVersion(otInstance *aInstance) diff --git a/src/core/api/netdata_publisher_api.cpp b/src/core/api/netdata_publisher_api.cpp index 559734be2d7..9101b0a293c 100644 --- a/src/core/api/netdata_publisher_api.cpp +++ b/src/core/api/netdata_publisher_api.cpp @@ -64,9 +64,9 @@ bool otNetDataIsDnsSrpServiceAdded(otInstance *aInstance) return AsCoreType(aInstance).Get().IsDnsSrpServiceAdded(); } -void otNetDataSetDnsSrpServicePublisherCallback(otInstance * aInstance, +void otNetDataSetDnsSrpServicePublisherCallback(otInstance *aInstance, otNetDataDnsSrpServicePublisherCallback aCallback, - void * aContext) + void *aContext) { AsCoreType(aInstance).Get().SetDnsSrpServiceCallback(aCallback, aContext); } @@ -82,12 +82,22 @@ void otNetDataUnpublishDnsSrpService(otInstance *aInstance) otError otNetDataPublishOnMeshPrefix(otInstance *aInstance, const otBorderRouterConfig *aConfig) { - return AsCoreType(aInstance).Get().PublishOnMeshPrefix(AsCoreType(aConfig)); + return AsCoreType(aInstance).Get().PublishOnMeshPrefix(AsCoreType(aConfig), + NetworkData::Publisher::kFromUser); } otError otNetDataPublishExternalRoute(otInstance *aInstance, const otExternalRouteConfig *aConfig) { - return AsCoreType(aInstance).Get().PublishExternalRoute(AsCoreType(aConfig)); + return AsCoreType(aInstance).Get().PublishExternalRoute(AsCoreType(aConfig), + NetworkData::Publisher::kFromUser); +} + +otError otNetDataReplacePublishedExternalRoute(otInstance *aInstance, + const otIp6Prefix *aPrefix, + const otExternalRouteConfig *aConfig) +{ + return AsCoreType(aInstance).Get().ReplacePublishedExternalRoute( + AsCoreType(aPrefix), AsCoreType(aConfig), NetworkData::Publisher::kFromUser); } bool otNetDataIsPrefixAdded(otInstance *aInstance, const otIp6Prefix *aPrefix) @@ -95,9 +105,9 @@ bool otNetDataIsPrefixAdded(otInstance *aInstance, const otIp6Prefix *aPrefix) return AsCoreType(aInstance).Get().IsPrefixAdded(AsCoreType(aPrefix)); } -void otNetDataSetPrefixPublisherCallback(otInstance * aInstance, +void otNetDataSetPrefixPublisherCallback(otInstance *aInstance, otNetDataPrefixPublisherCallback aCallback, - void * aContext) + void *aContext) { return AsCoreType(aInstance).Get().SetPrefixCallback(aCallback, aContext); } diff --git a/src/core/api/netdiag_api.cpp b/src/core/api/netdiag_api.cpp index ff09c8e8f78..ffcc4c12114 100644 --- a/src/core/api/netdiag_api.cpp +++ b/src/core/api/netdiag_api.cpp @@ -33,8 +33,6 @@ #include "openthread-core-config.h" -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE - #include #include "common/as_core_type.hpp" @@ -42,31 +40,68 @@ using namespace ot; -otError otThreadGetNextDiagnosticTlv(const otMessage * aMessage, +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE + +otError otThreadGetNextDiagnosticTlv(const otMessage *aMessage, otNetworkDiagIterator *aIterator, - otNetworkDiagTlv * aNetworkDiagTlv) + otNetworkDiagTlv *aNetworkDiagTlv) { - return NetworkDiagnostic::NetworkDiagnostic::GetNextDiagTlv(AsCoapMessage(aMessage), *aIterator, *aNetworkDiagTlv); + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aNetworkDiagTlv); + + return NetworkDiagnostic::Client::GetNextDiagTlv(AsCoapMessage(aMessage), *aIterator, *aNetworkDiagTlv); } -otError otThreadSendDiagnosticGet(otInstance * aInstance, - const otIp6Address * aDestination, +otError otThreadSendDiagnosticGet(otInstance *aInstance, + const otIp6Address *aDestination, const uint8_t aTlvTypes[], uint8_t aCount, otReceiveDiagnosticGetCallback aCallback, - void * aCallbackContext) + void *aCallbackContext) { - return AsCoreType(aInstance).Get().SendDiagnosticGet( + return AsCoreType(aInstance).Get().SendDiagnosticGet( AsCoreType(aDestination), aTlvTypes, aCount, aCallback, aCallbackContext); } -otError otThreadSendDiagnosticReset(otInstance * aInstance, +otError otThreadSendDiagnosticReset(otInstance *aInstance, const otIp6Address *aDestination, const uint8_t aTlvTypes[], uint8_t aCount) { - return AsCoreType(aInstance).Get().SendDiagnosticReset( - AsCoreType(aDestination), aTlvTypes, aCount); + return AsCoreType(aInstance).Get().SendDiagnosticReset(AsCoreType(aDestination), + aTlvTypes, aCount); } -#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE +#endif // OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE + +const char *otThreadGetVendorName(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetVendorName(); +} + +const char *otThreadGetVendorModel(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetVendorModel(); +} + +const char *otThreadGetVendorSwVersion(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetVendorSwVersion(); +} + +#if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE +otError otThreadSetVendorName(otInstance *aInstance, const char *aVendorName) +{ + return AsCoreType(aInstance).Get().SetVendorName(aVendorName); +} + +otError otThreadSetVendorModel(otInstance *aInstance, const char *aVendorModel) +{ + return AsCoreType(aInstance).Get().SetVendorModel(aVendorModel); +} + +otError otThreadSetVendorSwVersion(otInstance *aInstance, const char *aVendorSwVersion) +{ + return AsCoreType(aInstance).Get().SetVendorSwVersion(aVendorSwVersion); +} +#endif diff --git a/src/core/api/network_time_api.cpp b/src/core/api/network_time_api.cpp index 128aff47888..903ed8661f9 100644 --- a/src/core/api/network_time_api.cpp +++ b/src/core/api/network_time_api.cpp @@ -44,7 +44,9 @@ using namespace ot; otNetworkTimeStatus otNetworkTimeGet(otInstance *aInstance, uint64_t *aNetworkTime) { - return AsCoreType(aInstance).Get().GetTime(*aNetworkTime); + AssertPointerIsNotNull(aNetworkTime); + + return MapEnum(AsCoreType(aInstance).Get().GetTime(*aNetworkTime)); } otError otNetworkTimeSetSyncPeriod(otInstance *aInstance, uint16_t aTimeSyncPeriod) diff --git a/src/core/api/ping_sender_api.cpp b/src/core/api/ping_sender_api.cpp index 9172ed4f099..331930608f0 100644 --- a/src/core/api/ping_sender_api.cpp +++ b/src/core/api/ping_sender_api.cpp @@ -47,9 +47,6 @@ otError otPingSenderPing(otInstance *aInstance, const otPingSenderConfig *aConfi return AsCoreType(aInstance).Get().Ping(AsCoreType(aConfig)); } -void otPingSenderStop(otInstance *aInstance) -{ - AsCoreType(aInstance).Get().Stop(); -} +void otPingSenderStop(otInstance *aInstance) { AsCoreType(aInstance).Get().Stop(); } #endif // OPENTHREAD_CONFIG_PING_SENDER_ENABLE diff --git a/src/core/api/random_noncrypto_api.cpp b/src/core/api/random_noncrypto_api.cpp index 51fb14178bb..6a2d18ba37a 100644 --- a/src/core/api/random_noncrypto_api.cpp +++ b/src/core/api/random_noncrypto_api.cpp @@ -37,20 +37,11 @@ using namespace ot; -uint32_t otRandomNonCryptoGetUint32(void) -{ - return Random::NonCrypto::GetUint32(); -} +uint32_t otRandomNonCryptoGetUint32(void) { return Random::NonCrypto::GetUint32(); } -uint8_t otRandomNonCryptoGetUint8(void) -{ - return Random::NonCrypto::GetUint8(); -} +uint8_t otRandomNonCryptoGetUint8(void) { return Random::NonCrypto::GetUint8(); } -uint16_t otRandomNonCryptoGetUint16(void) -{ - return Random::NonCrypto::GetUint16(); -} +uint16_t otRandomNonCryptoGetUint16(void) { return Random::NonCrypto::GetUint16(); } uint8_t otRandomNonCryptoGetUint8InRange(uint8_t aMin, uint8_t aMax) { @@ -67,10 +58,7 @@ uint32_t otRandomNonCryptoGetUint32InRange(uint32_t aMin, uint32_t aMax) return Random::NonCrypto::GetUint32InRange(aMin, aMax); } -void otRandomNonCryptoFillBuffer(uint8_t *aBuffer, uint16_t aSize) -{ - Random::NonCrypto::FillBuffer(aBuffer, aSize); -} +void otRandomNonCryptoFillBuffer(uint8_t *aBuffer, uint16_t aSize) { Random::NonCrypto::FillBuffer(aBuffer, aSize); } uint32_t otRandomNonCryptoAddJitter(uint32_t aValue, uint16_t aJitter) { diff --git a/src/core/api/server_api.cpp b/src/core/api/server_api.cpp index b1712f10399..9c44ab889d6 100644 --- a/src/core/api/server_api.cpp +++ b/src/core/api/server_api.cpp @@ -60,7 +60,7 @@ otError otServerAddService(otInstance *aInstance, const otServiceConfig *aConfig aConfig->mServerConfig.mStable, serverData); } -otError otServerRemoveService(otInstance * aInstance, +otError otServerRemoveService(otInstance *aInstance, uint32_t aEnterpriseNumber, const uint8_t *aServiceData, uint8_t aServiceDataLength) diff --git a/src/core/api/sntp_api.cpp b/src/core/api/sntp_api.cpp index 007fc9294ce..1651c3b0e34 100644 --- a/src/core/api/sntp_api.cpp +++ b/src/core/api/sntp_api.cpp @@ -42,10 +42,10 @@ using namespace ot; -otError otSntpClientQuery(otInstance * aInstance, - const otSntpQuery * aQuery, +otError otSntpClientQuery(otInstance *aInstance, + const otSntpQuery *aQuery, otSntpResponseHandler aHandler, - void * aContext) + void *aContext) { return AsCoreType(aInstance).Get().Query(aQuery, aHandler, aContext); } diff --git a/src/core/api/srp_client_api.cpp b/src/core/api/srp_client_api.cpp index 3ec356f3432..7078c2083f5 100644 --- a/src/core/api/srp_client_api.cpp +++ b/src/core/api/srp_client_api.cpp @@ -47,15 +47,9 @@ otError otSrpClientStart(otInstance *aInstance, const otSockAddr *aServerSockAdd return AsCoreType(aInstance).Get().Start(AsCoreType(aServerSockAddr)); } -void otSrpClientStop(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().Stop(); -} +void otSrpClientStop(otInstance *aInstance) { return AsCoreType(aInstance).Get().Stop(); } -bool otSrpClientIsRunning(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().IsRunning(); -} +bool otSrpClientIsRunning(otInstance *aInstance) { return AsCoreType(aInstance).Get().IsRunning(); } const otSockAddr *otSrpClientGetServerAddress(otInstance *aInstance) { @@ -84,10 +78,7 @@ bool otSrpClientIsAutoStartModeEnabled(otInstance *aInstance) } #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE -uint32_t otSrpClientGetTtl(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().GetTtl(); -} +uint32_t otSrpClientGetTtl(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetTtl(); } void otSrpClientSetTtl(otInstance *aInstance, uint32_t aTtl) { diff --git a/src/core/api/srp_client_buffers_api.cpp b/src/core/api/srp_client_buffers_api.cpp index 51c91a86aa2..35b26054c94 100644 --- a/src/core/api/srp_client_buffers_api.cpp +++ b/src/core/api/srp_client_buffers_api.cpp @@ -44,11 +44,15 @@ using namespace ot; char *otSrpClientBuffersGetHostNameString(otInstance *aInstance, uint16_t *aSize) { + AssertPointerIsNotNull(aSize); + return AsCoreType(aInstance).Get().GetHostNameString(*aSize); } otIp6Address *otSrpClientBuffersGetHostAddressesArray(otInstance *aInstance, uint8_t *aArrayLength) { + AssertPointerIsNotNull(aArrayLength); + return AsCoreType(aInstance).Get().GetHostAddressesArray(*aArrayLength); } @@ -69,21 +73,29 @@ void otSrpClientBuffersFreeAllServices(otInstance *aInstance) char *otSrpClientBuffersGetServiceEntryServiceNameString(otSrpClientBuffersServiceEntry *aEntry, uint16_t *aSize) { + AssertPointerIsNotNull(aSize); + return AsCoreType(aEntry).GetServiceNameString(*aSize); } char *otSrpClientBuffersGetServiceEntryInstanceNameString(otSrpClientBuffersServiceEntry *aEntry, uint16_t *aSize) { + AssertPointerIsNotNull(aSize); + return AsCoreType(aEntry).GetInstanceNameString(*aSize); } uint8_t *otSrpClientBuffersGetServiceEntryTxtBuffer(otSrpClientBuffersServiceEntry *aEntry, uint16_t *aSize) { + AssertPointerIsNotNull(aSize); + return AsCoreType(aEntry).GetTxtBuffer(*aSize); } const char **otSrpClientBuffersGetSubTypeLabelsArray(otSrpClientBuffersServiceEntry *aEntry, uint16_t *aArrayLength) { + AssertPointerIsNotNull(aArrayLength); + return AsCoreType(aEntry).GetSubTypeLabelsArray(*aArrayLength); } diff --git a/src/core/api/srp_server_api.cpp b/src/core/api/srp_server_api.cpp index f9d4367e41d..a6a3ca7ca3a 100644 --- a/src/core/api/srp_server_api.cpp +++ b/src/core/api/srp_server_api.cpp @@ -42,10 +42,7 @@ using namespace ot; -const char *otSrpServerGetDomain(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().GetDomain(); -} +const char *otSrpServerGetDomain(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetDomain(); } otError otSrpServerSetDomain(otInstance *aInstance, const char *aDomain) { @@ -57,10 +54,7 @@ otSrpServerState otSrpServerGetState(otInstance *aInstance) return MapEnum(AsCoreType(aInstance).Get().GetState()); } -uint16_t otSrpServerGetPort(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().GetPort(); -} +uint16_t otSrpServerGetPort(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetPort(); } otSrpServerAddressMode otSrpServerGetAddressMode(otInstance *aInstance) { @@ -87,6 +81,18 @@ void otSrpServerSetEnabled(otInstance *aInstance, bool aEnabled) AsCoreType(aInstance).Get().SetEnabled(aEnabled); } +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +void otSrpServerSetAutoEnableMode(otInstance *aInstance, bool aEnabled) +{ + AsCoreType(aInstance).Get().SetAutoEnableMode(aEnabled); +} + +bool otSrpServerIsAutoEnableMode(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().IsAutoEnableMode(); +} +#endif + void otSrpServerGetTtlConfig(otInstance *aInstance, otSrpServerTtlConfig *aTtlConfig) { AsCoreType(aInstance).Get().GetTtlConfig(AsCoreType(aTtlConfig)); @@ -107,9 +113,9 @@ otError otSrpServerSetLeaseConfig(otInstance *aInstance, const otSrpServerLeaseC return AsCoreType(aInstance).Get().SetLeaseConfig(AsCoreType(aLeaseConfig)); } -void otSrpServerSetServiceUpdateHandler(otInstance * aInstance, +void otSrpServerSetServiceUpdateHandler(otInstance *aInstance, otSrpServerServiceUpdateHandler aServiceHandler, - void * aContext) + void *aContext) { AsCoreType(aInstance).Get().SetServiceHandler(aServiceHandler, aContext); } @@ -129,15 +135,9 @@ const otSrpServerResponseCounters *otSrpServerGetResponseCounters(otInstance *aI return AsCoreType(aInstance).Get().GetResponseCounters(); } -bool otSrpServerHostIsDeleted(const otSrpServerHost *aHost) -{ - return AsCoreType(aHost).IsDeleted(); -} +bool otSrpServerHostIsDeleted(const otSrpServerHost *aHost) { return AsCoreType(aHost).IsDeleted(); } -const char *otSrpServerHostGetFullName(const otSrpServerHost *aHost) -{ - return AsCoreType(aHost).GetFullName(); -} +const char *otSrpServerHostGetFullName(const otSrpServerHost *aHost) { return AsCoreType(aHost).GetFullName(); } const otIp6Address *otSrpServerHostGetAddresses(const otSrpServerHost *aHost, uint8_t *aAddressesNum) { @@ -149,35 +149,26 @@ void otSrpServerHostGetLeaseInfo(const otSrpServerHost *aHost, otSrpServerLeaseI AsCoreType(aHost).GetLeaseInfo(*aLeaseInfo); } -uint32_t otSrpServerHostGetKeyLease(const otSrpServerHost *aHost) -{ - return AsCoreType(aHost).GetKeyLease(); -} +uint32_t otSrpServerHostGetKeyLease(const otSrpServerHost *aHost) { return AsCoreType(aHost).GetKeyLease(); } -const otSrpServerService *otSrpServerHostGetNextService(const otSrpServerHost * aHost, +const otSrpServerService *otSrpServerHostGetNextService(const otSrpServerHost *aHost, const otSrpServerService *aService) { return AsCoreType(aHost).FindNextService(AsCoreTypePtr(aService), Srp::Server::kFlagsBaseTypeServiceOnly); } -const otSrpServerService *otSrpServerHostFindNextService(const otSrpServerHost * aHost, +const otSrpServerService *otSrpServerHostFindNextService(const otSrpServerHost *aHost, const otSrpServerService *aPrevService, otSrpServerServiceFlags aFlags, - const char * aServiceName, - const char * aInstanceName) + const char *aServiceName, + const char *aInstanceName) { return AsCoreType(aHost).FindNextService(AsCoreTypePtr(aPrevService), aFlags, aServiceName, aInstanceName); } -bool otSrpServerServiceIsDeleted(const otSrpServerService *aService) -{ - return AsCoreType(aService).IsDeleted(); -} +bool otSrpServerServiceIsDeleted(const otSrpServerService *aService) { return AsCoreType(aService).IsDeleted(); } -bool otSrpServerServiceIsSubType(const otSrpServerService *aService) -{ - return AsCoreType(aService).IsSubType(); -} +bool otSrpServerServiceIsSubType(const otSrpServerService *aService) { return AsCoreType(aService).IsSubType(); } const char *otSrpServerServiceGetFullName(const otSrpServerService *aService) { @@ -199,25 +190,16 @@ otError otSrpServerServiceGetServiceSubTypeLabel(const otSrpServerService *aServ return AsCoreType(aService).GetServiceSubTypeLabel(aLabel, aMaxSize); } -uint16_t otSrpServerServiceGetPort(const otSrpServerService *aService) -{ - return AsCoreType(aService).GetPort(); -} +uint16_t otSrpServerServiceGetPort(const otSrpServerService *aService) { return AsCoreType(aService).GetPort(); } -uint16_t otSrpServerServiceGetWeight(const otSrpServerService *aService) -{ - return AsCoreType(aService).GetWeight(); -} +uint16_t otSrpServerServiceGetWeight(const otSrpServerService *aService) { return AsCoreType(aService).GetWeight(); } uint16_t otSrpServerServiceGetPriority(const otSrpServerService *aService) { return AsCoreType(aService).GetPriority(); } -uint32_t otSrpServerServiceGetTtl(const otSrpServerService *aService) -{ - return AsCoreType(aService).GetTtl(); -} +uint32_t otSrpServerServiceGetTtl(const otSrpServerService *aService) { return AsCoreType(aService).GetTtl(); } const uint8_t *otSrpServerServiceGetTxtData(const otSrpServerService *aService, uint16_t *aDataLength) { diff --git a/src/core/api/tasklet_api.cpp b/src/core/api/tasklet_api.cpp index e5213c8161e..bcda24a97ae 100644 --- a/src/core/api/tasklet_api.cpp +++ b/src/core/api/tasklet_api.cpp @@ -60,6 +60,4 @@ bool otTaskletsArePending(otInstance *aInstance) return retval; } -OT_TOOL_WEAK void otTaskletsSignalPending(otInstance *) -{ -} +OT_TOOL_WEAK void otTaskletsSignalPending(otInstance *) {} diff --git a/src/core/api/tcp_api.cpp b/src/core/api/tcp_api.cpp index 896aac6a33c..b779d4815b1 100644 --- a/src/core/api/tcp_api.cpp +++ b/src/core/api/tcp_api.cpp @@ -42,22 +42,16 @@ using namespace ot; -otError otTcpEndpointInitialize(otInstance * aInstance, - otTcpEndpoint * aEndpoint, +otError otTcpEndpointInitialize(otInstance *aInstance, + otTcpEndpoint *aEndpoint, const otTcpEndpointInitializeArgs *aArgs) { return AsCoreType(aEndpoint).Initialize(AsCoreType(aInstance), *aArgs); } -otInstance *otTcpEndpointGetInstance(otTcpEndpoint *aEndpoint) -{ - return &AsCoreType(aEndpoint).GetInstance(); -} +otInstance *otTcpEndpointGetInstance(otTcpEndpoint *aEndpoint) { return &AsCoreType(aEndpoint).GetInstance(); } -void *otTcpEndpointGetContext(otTcpEndpoint *aEndpoint) -{ - return AsCoreType(aEndpoint).GetContext(); -} +void *otTcpEndpointGetContext(otTcpEndpoint *aEndpoint) { return AsCoreType(aEndpoint).GetContext(); } const otSockAddr *otTcpGetLocalAddress(const otTcpEndpoint *aEndpoint) { @@ -94,61 +88,37 @@ otError otTcpReceiveByReference(otTcpEndpoint *aEndpoint, const otLinkedBuffer * return AsCoreType(aEndpoint).ReceiveByReference(*aBuffer); } -otError otTcpReceiveContiguify(otTcpEndpoint *aEndpoint) -{ - return AsCoreType(aEndpoint).ReceiveContiguify(); -} +otError otTcpReceiveContiguify(otTcpEndpoint *aEndpoint) { return AsCoreType(aEndpoint).ReceiveContiguify(); } otError otTcpCommitReceive(otTcpEndpoint *aEndpoint, size_t aNumBytes, uint32_t aFlags) { return AsCoreType(aEndpoint).CommitReceive(aNumBytes, aFlags); } -otError otTcpSendEndOfStream(otTcpEndpoint *aEndpoint) -{ - return AsCoreType(aEndpoint).SendEndOfStream(); -} +otError otTcpSendEndOfStream(otTcpEndpoint *aEndpoint) { return AsCoreType(aEndpoint).SendEndOfStream(); } -otError otTcpAbort(otTcpEndpoint *aEndpoint) -{ - return AsCoreType(aEndpoint).Abort(); -} +otError otTcpAbort(otTcpEndpoint *aEndpoint) { return AsCoreType(aEndpoint).Abort(); } -otError otTcpEndpointDeinitialize(otTcpEndpoint *aEndpoint) -{ - return AsCoreType(aEndpoint).Deinitialize(); -} +otError otTcpEndpointDeinitialize(otTcpEndpoint *aEndpoint) { return AsCoreType(aEndpoint).Deinitialize(); } -otError otTcpListenerInitialize(otInstance * aInstance, - otTcpListener * aListener, +otError otTcpListenerInitialize(otInstance *aInstance, + otTcpListener *aListener, const otTcpListenerInitializeArgs *aArgs) { return AsCoreType(aListener).Initialize(AsCoreType(aInstance), *aArgs); } -otInstance *otTcpListenerGetInstance(otTcpListener *aListener) -{ - return &AsCoreType(aListener).GetInstance(); -} +otInstance *otTcpListenerGetInstance(otTcpListener *aListener) { return &AsCoreType(aListener).GetInstance(); } -void *otTcpListenerGetContext(otTcpListener *aListener) -{ - return AsCoreType(aListener).GetContext(); -} +void *otTcpListenerGetContext(otTcpListener *aListener) { return AsCoreType(aListener).GetContext(); } otError otTcpListen(otTcpListener *aListener, const otSockAddr *aSockName) { return AsCoreType(aListener).Listen(AsCoreType(aSockName)); } -otError otTcpStopListening(otTcpListener *aListener) -{ - return AsCoreType(aListener).StopListening(); -} +otError otTcpStopListening(otTcpListener *aListener) { return AsCoreType(aListener).StopListening(); } -otError otTcpListenerDeinitialize(otTcpListener *aListener) -{ - return AsCoreType(aListener).Deinitialize(); -} +otError otTcpListenerDeinitialize(otTcpListener *aListener) { return AsCoreType(aListener).Deinitialize(); } #endif // OPENTHREAD_CONFIG_TCP_ENABLE diff --git a/src/core/api/tcp_ext_api.cpp b/src/core/api/tcp_ext_api.cpp new file mode 100644 index 00000000000..c6540662ccd --- /dev/null +++ b/src/core/api/tcp_ext_api.cpp @@ -0,0 +1,136 @@ +/* + * 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 extensions to the OpenThread TCP API. + */ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_TCP_ENABLE + +#include + +#include "common/as_core_type.hpp" +#include "common/locator_getters.hpp" +#include "net/tcp6_ext.hpp" + +#if OPENTHREAD_CONFIG_TLS_ENABLE + +#include + +#endif + +using namespace ot; + +void otTcpCircularSendBufferInitialize(otTcpCircularSendBuffer *aSendBuffer, void *aDataBuffer, size_t aCapacity) +{ + AsCoreType(aSendBuffer).Initialize(aDataBuffer, aCapacity); +} + +otError otTcpCircularSendBufferWrite(otTcpEndpoint *aEndpoint, + otTcpCircularSendBuffer *aSendBuffer, + const void *aData, + size_t aLength, + size_t *aWritten, + uint32_t aFlags) +{ + AssertPointerIsNotNull(aWritten); + return AsCoreType(aSendBuffer).Write(AsCoreType(aEndpoint), aData, aLength, *aWritten, aFlags); +} + +void otTcpCircularSendBufferHandleForwardProgress(otTcpCircularSendBuffer *aSendBuffer, size_t aInSendBuffer) +{ + AsCoreType(aSendBuffer).HandleForwardProgress(aInSendBuffer); +} + +size_t otTcpCircularSendBufferGetFreeSpace(const otTcpCircularSendBuffer *aSendBuffer) +{ + return AsCoreType(aSendBuffer).GetFreeSpace(); +} + +void otTcpCircularSendBufferForceDiscardAll(otTcpCircularSendBuffer *aSendBuffer) +{ + AsCoreType(aSendBuffer).ForceDiscardAll(); +} + +otError otTcpCircularSendBufferDeinitialize(otTcpCircularSendBuffer *aSendBuffer) +{ + return AsCoreType(aSendBuffer).Deinitialize(); +} + +#if OPENTHREAD_CONFIG_TLS_ENABLE + +int otTcpMbedTlsSslSendCallback(void *aCtx, const unsigned char *aBuf, size_t aLen) +{ + otTcpEndpointAndCircularSendBuffer *pair = static_cast(aCtx); + otTcpEndpoint *endpoint = pair->mEndpoint; + otTcpCircularSendBuffer *sendBuffer = pair->mSendBuffer; + size_t bytes_written; + int result; + otError error; + + error = otTcpCircularSendBufferWrite(endpoint, sendBuffer, aBuf, aLen, &bytes_written, 0); + VerifyOrExit(error == OT_ERROR_NONE, result = MBEDTLS_ERR_SSL_INTERNAL_ERROR); + VerifyOrExit(aLen == 0 || bytes_written != 0, result = MBEDTLS_ERR_SSL_WANT_WRITE); + result = static_cast(bytes_written); + +exit: + return result; +} + +int otTcpMbedTlsSslRecvCallback(void *aCtx, unsigned char *aBuf, size_t aLen) +{ + otTcpEndpointAndCircularSendBuffer *pair = static_cast(aCtx); + otTcpEndpoint *endpoint = pair->mEndpoint; + size_t bytes_read = 0; + const otLinkedBuffer *buffer; + int result; + otError error; + + error = otTcpReceiveByReference(endpoint, &buffer); + VerifyOrExit(error == OT_ERROR_NONE, result = MBEDTLS_ERR_SSL_INTERNAL_ERROR); + while (bytes_read != aLen && buffer != nullptr) + { + size_t to_copy = OT_MIN(aLen - bytes_read, buffer->mLength); + memcpy(&aBuf[bytes_read], buffer->mData, to_copy); + bytes_read += to_copy; + buffer = buffer->mNext; + } + VerifyOrExit(aLen == 0 || bytes_read != 0, result = MBEDTLS_ERR_SSL_WANT_READ); + IgnoreReturnValue(otTcpCommitReceive(endpoint, bytes_read, 0)); + result = static_cast(bytes_read); + +exit: + return result; +} + +#endif // OPENTHREAD_CONFIG_TLS_ENABLE + +#endif // OPENTHREAD_CONFIG_TCP_ENABLE diff --git a/src/core/api/thread_api.cpp b/src/core/api/thread_api.cpp index bec8c0633ea..778a663c8dc 100644 --- a/src/core/api/thread_api.cpp +++ b/src/core/api/thread_api.cpp @@ -40,6 +40,8 @@ #include "common/as_core_type.hpp" #include "common/debug.hpp" #include "common/locator_getters.hpp" +#include "common/uptime.hpp" +#include "thread/version.hpp" using namespace ot; @@ -61,7 +63,7 @@ const otExtendedPanId *otThreadGetExtendedPanId(otInstance *aInstance) otError otThreadSetExtendedPanId(otInstance *aInstance, const otExtendedPanId *aExtendedPanId) { Error error = kErrorNone; - Instance & instance = AsCoreType(aInstance); + Instance &instance = AsCoreType(aInstance); const MeshCoP::ExtendedPanId &extPanId = AsCoreType(aExtendedPanId); VerifyOrExit(instance.Get().IsDisabled(), error = kErrorInvalidState); @@ -77,8 +79,6 @@ otError otThreadSetExtendedPanId(otInstance *aInstance, const otExtendedPanId *a otError otThreadGetLeaderRloc(otInstance *aInstance, otIp6Address *aLeaderRloc) { - OT_ASSERT(aLeaderRloc != nullptr); - return AsCoreType(aInstance).Get().GetLeaderAddress(AsCoreType(aLeaderRloc)); } @@ -113,8 +113,6 @@ otError otThreadSetNetworkKey(otInstance *aInstance, const otNetworkKey *aKey) Error error = kErrorNone; Instance &instance = AsCoreType(aInstance); - OT_ASSERT(aKey != nullptr); - VerifyOrExit(instance.Get().IsDisabled(), error = kErrorInvalidState); instance.Get().SetNetworkKey(AsCoreType(aKey)); @@ -205,6 +203,12 @@ otError otThreadSetNetworkName(otInstance *aInstance, const char *aNetworkName) VerifyOrExit(AsCoreType(aInstance).Get().IsDisabled(), error = kErrorInvalidState); +#if !OPENTHREAD_CONFIG_ALLOW_EMPTY_NETWORK_NAME + // Thread interfaces support a zero length name internally for backwards compatibility, but new names + // must be at least one valid character long. + VerifyOrExit(nullptr != aNetworkName && aNetworkName[0] != '\0', error = kErrorInvalidArgs); +#endif + error = AsCoreType(aInstance).Get().SetNetworkName(aNetworkName); AsCoreType(aInstance).Get().Clear(); AsCoreType(aInstance).Get().Clear(); @@ -250,7 +254,7 @@ otError otThreadSetFixedDuaInterfaceIdentifier(otInstance *aInstance, const otIp const otIp6InterfaceIdentifier *otThreadGetFixedDuaInterfaceIdentifier(otInstance *aInstance) { - Instance & instance = AsCoreType(aInstance); + Instance &instance = AsCoreType(aInstance); const otIp6InterfaceIdentifier *iid = nullptr; if (instance.Get().IsFixedDuaInterfaceIdentifierSet()) @@ -289,14 +293,11 @@ otError otThreadBecomeDetached(otInstance *aInstance) return AsCoreType(aInstance).Get().BecomeDetached(); } -otError otThreadBecomeChild(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().BecomeChild(); -} +otError otThreadBecomeChild(otInstance *aInstance) { return AsCoreType(aInstance).Get().BecomeChild(); } otError otThreadGetNextNeighborInfo(otInstance *aInstance, otNeighborInfoIterator *aIterator, otNeighborInfo *aInfo) { - OT_ASSERT((aInfo != nullptr) && (aIterator != nullptr)); + AssertPointerIsNotNull(aIterator); return AsCoreType(aInstance).Get().GetNextNeighborInfo(*aIterator, AsCoreType(aInfo)); } @@ -306,16 +307,13 @@ otDeviceRole otThreadGetDeviceRole(otInstance *aInstance) return MapEnum(AsCoreType(aInstance).Get().GetRole()); } -const char *otThreadDeviceRoleToString(otDeviceRole aRole) -{ - return Mle::Mle::RoleToString(MapEnum(aRole)); -} +const char *otThreadDeviceRoleToString(otDeviceRole aRole) { return Mle::RoleToString(MapEnum(aRole)); } otError otThreadGetLeaderData(otInstance *aInstance, otLeaderData *aLeaderData) { Error error = kErrorNone; - OT_ASSERT(aLeaderData != nullptr); + AssertPointerIsNotNull(aLeaderData); VerifyOrExit(AsCoreType(aInstance).Get().IsAttached(), error = kErrorDetached); *aLeaderData = AsCoreType(aInstance).Get().GetLeaderData(); @@ -339,51 +337,22 @@ uint32_t otThreadGetPartitionId(otInstance *aInstance) return AsCoreType(aInstance).Get().GetLeaderData().GetPartitionId(); } -uint16_t otThreadGetRloc16(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().GetRloc16(); -} +uint16_t otThreadGetRloc16(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetRloc16(); } otError otThreadGetParentInfo(otInstance *aInstance, otRouterInfo *aParentInfo) { - Error error = kErrorNone; - Router *parent; - - OT_ASSERT(aParentInfo != nullptr); - - // Reference device needs get the original parent's info even after the node state changed. -#if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE - VerifyOrExit(AsCoreType(aInstance).Get().IsChild(), error = kErrorInvalidState); -#endif - - parent = &AsCoreType(aInstance).Get().GetParent(); - - aParentInfo->mExtAddress = parent->GetExtAddress(); - aParentInfo->mRloc16 = parent->GetRloc16(); - aParentInfo->mRouterId = Mle::Mle::RouterIdFromRloc16(parent->GetRloc16()); - aParentInfo->mNextHop = parent->GetNextHop(); - aParentInfo->mPathCost = parent->GetCost(); - aParentInfo->mLinkQualityIn = parent->GetLinkInfo().GetLinkQuality(); - aParentInfo->mLinkQualityOut = parent->GetLinkQualityOut(); - aParentInfo->mAge = static_cast(Time::MsecToSec(TimerMilli::GetNow() - parent->GetLastHeard())); - aParentInfo->mAllocated = true; - aParentInfo->mLinkEstablished = parent->IsStateValid(); - -#if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE -exit: -#endif - return error; + return AsCoreType(aInstance).Get().GetParentInfo(AsCoreType(aParentInfo)); } otError otThreadGetParentAverageRssi(otInstance *aInstance, int8_t *aParentRssi) { Error error = kErrorNone; - OT_ASSERT(aParentRssi != nullptr); + AssertPointerIsNotNull(aParentRssi); *aParentRssi = AsCoreType(aInstance).Get().GetParent().GetLinkInfo().GetAverageRss(); - VerifyOrExit(*aParentRssi != OT_RADIO_RSSI_INVALID, error = kErrorFailed); + VerifyOrExit(*aParentRssi != Radio::kInvalidRssi, error = kErrorFailed); exit: return error; @@ -393,16 +362,21 @@ otError otThreadGetParentLastRssi(otInstance *aInstance, int8_t *aLastRssi) { Error error = kErrorNone; - OT_ASSERT(aLastRssi != nullptr); + AssertPointerIsNotNull(aLastRssi); *aLastRssi = AsCoreType(aInstance).Get().GetParent().GetLinkInfo().GetLastRss(); - VerifyOrExit(*aLastRssi != OT_RADIO_RSSI_INVALID, error = kErrorFailed); + VerifyOrExit(*aLastRssi != Radio::kInvalidRssi, error = kErrorFailed); exit: return error; } +otError otThreadSearchForBetterParent(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().SearchForBetterParent(); +} + otError otThreadSetEnabled(otInstance *aInstance, bool aEnabled) { Error error = kErrorNone; @@ -419,30 +393,24 @@ otError otThreadSetEnabled(otInstance *aInstance, bool aEnabled) return error; } -uint16_t otThreadGetVersion(void) -{ - return OPENTHREAD_CONFIG_THREAD_VERSION; -} +uint16_t otThreadGetVersion(void) { return kThreadVersion; } -bool otThreadIsSingleton(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().IsSingleton(); -} +bool otThreadIsSingleton(otInstance *aInstance) { return AsCoreType(aInstance).Get().IsSingleton(); } -otError otThreadDiscover(otInstance * aInstance, +otError otThreadDiscover(otInstance *aInstance, uint32_t aScanChannels, uint16_t aPanId, bool aJoiner, bool aEnableEui64Filtering, otHandleActiveScanResult aCallback, - void * aCallbackContext) + void *aCallbackContext) { return AsCoreType(aInstance).Get().Discover( Mac::ChannelMask(aScanChannels), aPanId, aJoiner, aEnableEui64Filtering, /* aFilterIndexes (use hash of factory EUI64) */ nullptr, aCallback, aCallbackContext); } -otError otThreadSetJoinerAdvertisement(otInstance * aInstance, +otError otThreadSetJoinerAdvertisement(otInstance *aInstance, uint32_t aOui, const uint8_t *aAdvData, uint8_t aAdvDataLength) @@ -460,33 +428,29 @@ const otIpCounters *otThreadGetIp6Counters(otInstance *aInstance) return &AsCoreType(aInstance).Get().GetCounters(); } -void otThreadResetIp6Counters(otInstance *aInstance) -{ - AsCoreType(aInstance).Get().ResetCounters(); -} +void otThreadResetIp6Counters(otInstance *aInstance) { AsCoreType(aInstance).Get().ResetCounters(); } const otMleCounters *otThreadGetMleCounters(otInstance *aInstance) { return &AsCoreType(aInstance).Get().GetCounters(); } -void otThreadResetMleCounters(otInstance *aInstance) -{ - AsCoreType(aInstance).Get().ResetCounters(); -} +void otThreadResetMleCounters(otInstance *aInstance) { AsCoreType(aInstance).Get().ResetCounters(); } -void otThreadRegisterParentResponseCallback(otInstance * aInstance, +#if OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE +void otThreadRegisterParentResponseCallback(otInstance *aInstance, otThreadParentResponseCallback aCallback, - void * aContext) + void *aContext) { AsCoreType(aInstance).Get().RegisterParentResponseStatsCallback(aCallback, aContext); } +#endif #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE -otError otThreadLocateAnycastDestination(otInstance * aInstance, - const otIp6Address * aAnycastAddress, +otError otThreadLocateAnycastDestination(otInstance *aInstance, + const otIp6Address *aAnycastAddress, otThreadAnycastLocatorCallback aCallback, - void * aContext) + void *aContext) { return AsCoreType(aInstance).Get().Locate(AsCoreType(aAnycastAddress), aCallback, aContext); } @@ -503,3 +467,12 @@ otError otThreadDetachGracefully(otInstance *aInstance, otDetachGracefullyCallba } #endif // OPENTHREAD_FTD || OPENTHREAD_MTD + +#if OPENTHREAD_CONFIG_UPTIME_ENABLE +void otConvertDurationInSecondsToString(uint32_t aDuration, char *aBuffer, uint16_t aSize) +{ + StringWriter writer(aBuffer, aSize); + + Uptime::UptimeToString(Uptime::SecToMsec(aDuration), writer, /* aIncludeMsec */ false); +} +#endif diff --git a/src/core/api/thread_ftd_api.cpp b/src/core/api/thread_ftd_api.cpp index e8d3a4b8e2e..96fb8903a2c 100644 --- a/src/core/api/thread_ftd_api.cpp +++ b/src/core/api/thread_ftd_api.cpp @@ -79,6 +79,16 @@ otError otThreadSetPreferredRouterId(otInstance *aInstance, uint8_t aRouterId) return AsCoreType(aInstance).Get().SetPreferredRouterId(aRouterId); } +const otDeviceProperties *otThreadGetDeviceProperties(otInstance *aInstance) +{ + return &AsCoreType(aInstance).Get().GetDeviceProperties(); +} + +void otThreadSetDeviceProperties(otInstance *aInstance, const otDeviceProperties *aDeviceProperties) +{ + AsCoreType(aInstance).Get().SetDeviceProperties(AsCoreType(aDeviceProperties)); +} + uint8_t otThreadGetLocalLeaderWeight(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetLeaderWeight(); @@ -143,6 +153,16 @@ void otThreadSetRouterUpgradeThreshold(otInstance *aInstance, uint8_t aThreshold AsCoreType(aInstance).Get().SetRouterUpgradeThreshold(aThreshold); } +uint8_t otThreadGetChildRouterLinks(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().GetChildRouterLinks(); +} + +otError otThreadSetChildRouterLinks(otInstance *aInstance, uint8_t aChildRouterLinks) +{ + return AsCoreType(aInstance).Get().SetChildRouterLinks(aChildRouterLinks); +} + otError otThreadReleaseRouterId(otInstance *aInstance, uint8_t aRouterId) { Error error = kErrorNone; @@ -205,27 +225,24 @@ void otThreadSetRouterSelectionJitter(otInstance *aInstance, uint8_t aRouterJitt otError otThreadGetChildInfoById(otInstance *aInstance, uint16_t aChildId, otChildInfo *aChildInfo) { - OT_ASSERT(aChildInfo != nullptr); - return AsCoreType(aInstance).Get().GetChildInfoById(aChildId, AsCoreType(aChildInfo)); } otError otThreadGetChildInfoByIndex(otInstance *aInstance, uint16_t aChildIndex, otChildInfo *aChildInfo) { - OT_ASSERT(aChildInfo != nullptr); - return AsCoreType(aInstance).Get().GetChildInfoByIndex(aChildIndex, AsCoreType(aChildInfo)); } -otError otThreadGetChildNextIp6Address(otInstance * aInstance, +otError otThreadGetChildNextIp6Address(otInstance *aInstance, uint16_t aChildIndex, otChildIp6AddressIterator *aIterator, - otIp6Address * aAddress) + otIp6Address *aAddress) { Error error = kErrorNone; const Child *child; - OT_ASSERT(aIterator != nullptr && aAddress != nullptr); + AssertPointerIsNotNull(aIterator); + AssertPointerIsNotNull(aAddress); child = AsCoreType(aInstance).Get().GetChildAtIndex(aChildIndex); VerifyOrExit(child != nullptr, error = kErrorInvalidArgs); @@ -258,16 +275,13 @@ uint8_t otThreadGetMaxRouterId(otInstance *aInstance) otError otThreadGetRouterInfo(otInstance *aInstance, uint16_t aRouterId, otRouterInfo *aRouterInfo) { - OT_ASSERT(aRouterInfo != nullptr); - return AsCoreType(aInstance).Get().GetRouterInfo(aRouterId, AsCoreType(aRouterInfo)); } otError otThreadGetNextCacheEntry(otInstance *aInstance, otCacheEntryInfo *aEntryInfo, otCacheEntryIterator *aIterator) { - OT_ASSERT((aIterator != nullptr) && (aEntryInfo != nullptr)); - - return AsCoreType(aInstance).Get().GetNextCacheEntry(*aEntryInfo, *aIterator); + return AsCoreType(aInstance).Get().GetNextCacheEntry(AsCoreType(aEntryInfo), + AsCoreType(aIterator)); } #if OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE @@ -283,10 +297,7 @@ void otThreadGetPskc(otInstance *aInstance, otPskc *aPskc) } #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE -otPskcRef otThreadGetPskcRef(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().GetPskcRef(); -} +otPskcRef otThreadGetPskcRef(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetPskcRef(); } #endif otError otThreadSetPskc(otInstance *aInstance, const otPskc *aPskc) @@ -336,17 +347,17 @@ void otThreadRegisterNeighborTableCallback(otInstance *aInstance, otNeighborTabl AsCoreType(aInstance).Get().RegisterCallback(aCallback); } -void otThreadSetDiscoveryRequestCallback(otInstance * aInstance, +void otThreadSetDiscoveryRequestCallback(otInstance *aInstance, otThreadDiscoveryRequestCallback aCallback, - void * aContext) + void *aContext) { AsCoreType(aInstance).Get().SetDiscoveryRequestCallback(aCallback, aContext); } #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE -void otThreadSendAddressNotification(otInstance * aInstance, - otIp6Address * aDestination, - otIp6Address * aTarget, +void otThreadSendAddressNotification(otInstance *aInstance, + otIp6Address *aDestination, + otIp6Address *aTarget, otIp6InterfaceIdentifier *aMlIid) { AsCoreType(aInstance).Get().SendAddressQueryResponse(AsCoreType(aTarget), AsCoreType(aMlIid), @@ -354,8 +365,8 @@ void otThreadSendAddressNotification(otInstance * aInstance, } #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE -otError otThreadSendProactiveBackboneNotification(otInstance * aInstance, - otIp6Address * aTarget, +otError otThreadSendProactiveBackboneNotification(otInstance *aInstance, + otIp6Address *aTarget, otIp6InterfaceIdentifier *aMlIid, uint32_t aTimeSinceLastTransaction) { @@ -378,6 +389,9 @@ void otThreadSetThreadVersionCheckEnabled(otInstance *aInstance, bool aEnabled) #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE void otThreadGetRouterIdRange(otInstance *aInstance, uint8_t *aMinRouterId, uint8_t *aMaxRouterId) { + AssertPointerIsNotNull(aMinRouterId); + AssertPointerIsNotNull(aMaxRouterId); + AsCoreType(aInstance).Get().GetRouterIdRange(*aMinRouterId, *aMaxRouterId); } @@ -387,4 +401,22 @@ otError otThreadSetRouterIdRange(otInstance *aInstance, uint8_t aMinRouterId, ui } #endif +bool otThreadIsRouterIdAllocated(otInstance *aInstance, uint8_t aRouterId) +{ + return AsCoreType(aInstance).Get().IsAllocated(aRouterId); +} + +void otThreadGetNextHopAndPathCost(otInstance *aInstance, + uint16_t aDestRloc16, + uint16_t *aNextHopRloc16, + uint8_t *aPathCost) +{ + uint8_t pathcost; + uint16_t nextHopRloc16; + + AsCoreType(aInstance).Get().GetNextHopAndPathCost( + aDestRloc16, (aNextHopRloc16 != nullptr) ? *aNextHopRloc16 : nextHopRloc16, + (aPathCost != nullptr) ? *aPathCost : pathcost); +} + #endif // OPENTHREAD_FTD diff --git a/src/core/api/trel_api.cpp b/src/core/api/trel_api.cpp index 01feb25a4b6..29c1563a654 100644 --- a/src/core/api/trel_api.cpp +++ b/src/core/api/trel_api.cpp @@ -43,20 +43,12 @@ using namespace ot; -void otTrelEnable(otInstance *aInstance) +void otTrelSetEnabled(otInstance *aInstance, bool aEnable) { - AsCoreType(aInstance).Get().Enable(); + AsCoreType(aInstance).Get().SetEnabled(aEnable); } -void otTrelDisable(otInstance *aInstance) -{ - AsCoreType(aInstance).Get().Disable(); -} - -bool otTrelIsEnabled(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().IsEnabled(); -} +bool otTrelIsEnabled(otInstance *aInstance) { return AsCoreType(aInstance).Get().IsEnabled(); } void otTrelInitPeerIterator(otInstance *aInstance, otTrelPeerIterator *aIterator) { diff --git a/src/core/api/udp_api.cpp b/src/core/api/udp_api.cpp index fad774bd31e..16817b62945 100644 --- a/src/core/api/udp_api.cpp +++ b/src/core/api/udp_api.cpp @@ -62,7 +62,7 @@ otError otUdpClose(otInstance *aInstance, otUdpSocket *aSocket) otError otUdpBind(otInstance *aInstance, otUdpSocket *aSocket, const otSockAddr *aSockName, otNetifIdentifier aNetif) { - return AsCoreType(aInstance).Get().Bind(AsCoreType(aSocket), AsCoreType(aSockName), aNetif); + return AsCoreType(aInstance).Get().Bind(AsCoreType(aSocket), AsCoreType(aSockName), MapEnum(aNetif)); } otError otUdpConnect(otInstance *aInstance, otUdpSocket *aSocket, const otSockAddr *aSockName) @@ -76,10 +76,7 @@ otError otUdpSend(otInstance *aInstance, otUdpSocket *aSocket, otMessage *aMessa AsCoreType(aMessageInfo)); } -otUdpSocket *otUdpGetSockets(otInstance *aInstance) -{ - return AsCoreType(aInstance).Get().GetUdpSockets(); -} +otUdpSocket *otUdpGetSockets(otInstance *aInstance) { return AsCoreType(aInstance).Get().GetUdpSockets(); } #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE void otUdpForwardSetForwarder(otInstance *aInstance, otUdpForwarder aForwarder, void *aContext) @@ -87,16 +84,14 @@ void otUdpForwardSetForwarder(otInstance *aInstance, otUdpForwarder aForwarder, AsCoreType(aInstance).Get().SetUdpForwarder(aForwarder, aContext); } -void otUdpForwardReceive(otInstance * aInstance, - otMessage * aMessage, +void otUdpForwardReceive(otInstance *aInstance, + otMessage *aMessage, uint16_t aPeerPort, const otIp6Address *aPeerAddr, uint16_t aSockPort) { Ip6::MessageInfo messageInfo; - OT_ASSERT(aMessage != nullptr && aPeerAddr != nullptr); - messageInfo.SetSockAddr(AsCoreType(aInstance).Get().GetMeshLocal16()); messageInfo.SetSockPort(aSockPort); messageInfo.SetPeerAddr(AsCoreType(aPeerAddr)); diff --git a/src/core/backbone_router/backbone_tmf.cpp b/src/core/backbone_router/backbone_tmf.cpp index 293793f045a..d49e31c626c 100644 --- a/src/core/backbone_router/backbone_tmf.cpp +++ b/src/core/backbone_router/backbone_tmf.cpp @@ -43,17 +43,65 @@ namespace BackboneRouter { RegisterLogModule("Bbr"); +BackboneTmfAgent::BackboneTmfAgent(Instance &aInstance) + : Coap::Coap(aInstance) +{ + SetInterceptor(&Filter, this); + SetResourceHandler(&HandleResource); +} + Error BackboneTmfAgent::Start(void) { Error error = kErrorNone; - SuccessOrExit(error = Coap::Start(kBackboneUdpPort, OT_NETIF_BACKBONE)); + SuccessOrExit(error = Coap::Start(kBackboneUdpPort, Ip6::kNetifBackbone)); + LogInfo("Start listening on port %u", kBackboneUdpPort); SubscribeMulticast(Get().GetAllNetworkBackboneRoutersAddress()); exit: return error; } +bool BackboneTmfAgent::HandleResource(CoapBase &aCoapBase, + const char *aUriPath, + ot::Coap::Message &aMessage, + const Ip6::MessageInfo &aMessageInfo) +{ + return static_cast(aCoapBase).HandleResource(aUriPath, aMessage, aMessageInfo); +} + +bool BackboneTmfAgent::HandleResource(const char *aUriPath, + ot::Coap::Message &aMessage, + const Ip6::MessageInfo &aMessageInfo) +{ + OT_UNUSED_VARIABLE(aMessage); + OT_UNUSED_VARIABLE(aMessageInfo); + + bool didHandle = true; + Uri uri = UriFromPath(aUriPath); + +#define Case(kUri, Type) \ + case kUri: \ + Get().HandleTmf(aMessage, aMessageInfo); \ + break + + switch (uri) + { +#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE + Case(kUriBackboneQuery, Manager); + Case(kUriBackboneAnswer, Manager); +#endif + + default: + didHandle = false; + break; + } + +#undef Case + + return didHandle; +} + Error BackboneTmfAgent::Filter(const ot::Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, void *aContext) { OT_UNUSED_VARIABLE(aMessage); @@ -79,14 +127,14 @@ bool BackboneTmfAgent::IsBackboneTmfMessage(const Ip6::MessageInfo &aMessageInfo void BackboneTmfAgent::SubscribeMulticast(const Ip6::Address &aAddress) { - Error error = mSocket.JoinNetifMulticastGroup(OT_NETIF_BACKBONE, aAddress); + Error error = mSocket.JoinNetifMulticastGroup(Ip6::kNetifBackbone, aAddress); LogError("Backbone TMF subscribes", aAddress, error); } void BackboneTmfAgent::UnsubscribeMulticast(const Ip6::Address &aAddress) { - Error error = mSocket.LeaveNetifMulticastGroup(OT_NETIF_BACKBONE, aAddress); + Error error = mSocket.LeaveNetifMulticastGroup(Ip6::kNetifBackbone, aAddress); LogError("Backbone TMF unsubscribes", aAddress, error); } diff --git a/src/core/backbone_router/backbone_tmf.hpp b/src/core/backbone_router/backbone_tmf.hpp index 0720f05b574..73b92ad1bd8 100644 --- a/src/core/backbone_router/backbone_tmf.hpp +++ b/src/core/backbone_router/backbone_tmf.hpp @@ -39,6 +39,7 @@ #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE #include "coap/coap.hpp" +#include "thread/tmf.hpp" namespace ot { namespace BackboneRouter { @@ -58,11 +59,7 @@ class BackboneTmfAgent : public Coap::Coap * @param[in] aInstance A reference to the OpenThread instance. * */ - explicit BackboneTmfAgent(Instance &aInstance) - : Coap::Coap(aInstance) - { - SetInterceptor(&Filter, this); - } + explicit BackboneTmfAgent(Instance &aInstance); /** * This method starts the Backbone TMF agent. @@ -99,7 +96,12 @@ class BackboneTmfAgent : public Coap::Coap void UnsubscribeMulticast(const Ip6::Address &aAddress); private: - void LogError(const char *aText, const Ip6::Address &aAddress, Error aError) const; + static bool HandleResource(CoapBase &aCoapBase, + const char *aUriPath, + ot::Coap::Message &aMessage, + const Ip6::MessageInfo &aMessageInfo); + bool HandleResource(const char *aUriPath, ot::Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + void LogError(const char *aText, const Ip6::Address &aAddress, Error aError) const; static Error Filter(const ot::Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, void *aContext); }; diff --git a/src/core/backbone_router/bbr_leader.cpp b/src/core/backbone_router/bbr_leader.cpp index 4b64fffe3fe..30330dee714 100644 --- a/src/core/backbone_router/bbr_leader.cpp +++ b/src/core/backbone_router/bbr_leader.cpp @@ -58,7 +58,7 @@ void Leader::Reset(void) mDomainPrefix.SetLength(0); } -Error Leader::GetConfig(BackboneRouterConfig &aConfig) const +Error Leader::GetConfig(Config &aConfig) const { Error error = kErrorNone; @@ -84,7 +84,7 @@ Error Leader::GetServiceId(uint8_t &aServiceId) const #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) -void Leader::LogBackboneRouterPrimary(State aState, const BackboneRouterConfig &aConfig) const +void Leader::LogBackboneRouterPrimary(State aState, const Config &aConfig) const { OT_UNUSED_VARIABLE(aConfig); @@ -92,8 +92,8 @@ void Leader::LogBackboneRouterPrimary(State aState, const BackboneRouterConfig & if (aState != kStateRemoved && aState != kStateNone) { - LogInfo("Rloc16: 0x%4X, seqno: %d, delay: %d, timeout %d", aConfig.mServer16, aConfig.mSequenceNumber, - aConfig.mReregistrationDelay, aConfig.mMlrTimeout); + LogInfo("Rloc16:0x%4x, seqno:%u, delay:%u, timeout:%lu", aConfig.mServer16, aConfig.mSequenceNumber, + aConfig.mReregistrationDelay, ToUlong(aConfig.mMlrTimeout)); } } @@ -152,9 +152,9 @@ void Leader::Update(void) void Leader::UpdateBackboneRouterPrimary(void) { - BackboneRouterConfig config; - State state; - uint32_t origMlrTimeout; + Config config; + State state; + uint32_t origMlrTimeout; Get().GetBackboneRouterPrimary(config); @@ -205,7 +205,8 @@ void Leader::UpdateBackboneRouterPrimary(void) if (config.mMlrTimeout != origMlrTimeout) { - LogNote("Leader MLR Timeout is normalized from %u to %u", origMlrTimeout, config.mMlrTimeout); + LogNote("Leader MLR Timeout is normalized from %lu to %lu", ToUlong(origMlrTimeout), + ToUlong(config.mMlrTimeout)); } } diff --git a/src/core/backbone_router/bbr_leader.hpp b/src/core/backbone_router/bbr_leader.hpp index 6266d026e14..605283933c8 100644 --- a/src/core/backbone_router/bbr_leader.hpp +++ b/src/core/backbone_router/bbr_leader.hpp @@ -52,7 +52,7 @@ namespace ot { namespace BackboneRouter { -typedef otBackboneRouterConfig BackboneRouterConfig; +typedef otBackboneRouterConfig Config; /** * This class implements the basic Primary Backbone Router service operations. @@ -112,7 +112,7 @@ class Leader : public InstanceLocator, private NonCopyable * @retval kErrorNotFound No Backbone Router in the Thread Network. * */ - Error GetConfig(BackboneRouterConfig &aConfig) const; + Error GetConfig(Config &aConfig) const; /** * This method gets the Backbone Router Service ID. @@ -177,17 +177,17 @@ class Leader : public InstanceLocator, private NonCopyable void UpdateBackboneRouterPrimary(void); void UpdateDomainPrefixConfig(void); #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) - void LogBackboneRouterPrimary(State aState, const BackboneRouterConfig &aConfig) const; + void LogBackboneRouterPrimary(State aState, const Config &aConfig) const; void LogDomainPrefix(DomainPrefixState aState, const Ip6::Prefix &aPrefix) const; static const char *StateToString(State aState); static const char *DomainPrefixStateToString(DomainPrefixState aState); #else - void LogBackboneRouterPrimary(State, const BackboneRouterConfig &) const {} + void LogBackboneRouterPrimary(State, const Config &) const {} void LogDomainPrefix(DomainPrefixState, const Ip6::Prefix &) const {} #endif - BackboneRouterConfig mConfig; ///< Primary Backbone Router information. - Ip6::Prefix mDomainPrefix; ///< Domain Prefix in the Thread network. + Config mConfig; ///< Primary Backbone Router information. + Ip6::Prefix mDomainPrefix; ///< Domain Prefix in the Thread network. }; } // namespace BackboneRouter diff --git a/src/core/backbone_router/bbr_local.cpp b/src/core/backbone_router/bbr_local.cpp index 4c7945a44c0..ee20b2b3f8d 100644 --- a/src/core/backbone_router/bbr_local.cpp +++ b/src/core/backbone_router/bbr_local.cpp @@ -51,14 +51,12 @@ RegisterLogModule("BbrLocal"); Local::Local(Instance &aInstance) : InstanceLocator(aInstance) - , mState(OT_BACKBONE_ROUTER_STATE_DISABLED) + , mState(kStateDisabled) , mMlrTimeout(Mle::kMlrTimeoutDefault) , mReregistrationDelay(Mle::kRegistrationDelayDefault) , mSequenceNumber(Random::NonCrypto::GetUint8() % 127) , mRegistrationJitter(Mle::kBackboneRouterRegistrationJitter) , mIsServiceAdded(false) - , mDomainPrefixCallback(nullptr) - , mDomainPrefixCallbackContext(nullptr) { mDomainPrefixConfig.GetPrefix().SetLength(0); @@ -83,11 +81,11 @@ Local::Local(Instance &aInstance) void Local::SetEnabled(bool aEnable) { - VerifyOrExit(aEnable == (mState == OT_BACKBONE_ROUTER_STATE_DISABLED)); + VerifyOrExit(aEnable != IsEnabled()); if (aEnable) { - SetState(OT_BACKBONE_ROUTER_STATE_SECONDARY); + SetState(kStateSecondary); AddDomainPrefixToNetworkData(); IgnoreError(AddService()); } @@ -95,7 +93,7 @@ void Local::SetEnabled(bool aEnable) { RemoveDomainPrefixFromNetworkData(); RemoveService(); - SetState(OT_BACKBONE_ROUTER_STATE_DISABLED); + SetState(kStateDisabled); } exit: @@ -104,30 +102,30 @@ void Local::SetEnabled(bool aEnable) void Local::Reset(void) { - VerifyOrExit(mState != OT_BACKBONE_ROUTER_STATE_DISABLED); + VerifyOrExit(mState != kStateDisabled); RemoveService(); - if (mState == OT_BACKBONE_ROUTER_STATE_PRIMARY) + if (mState == kStatePrimary) { // Increase sequence number when changing from Primary to Secondary. SequenceNumberIncrease(); Get().Signal(kEventThreadBackboneRouterLocalChanged); - SetState(OT_BACKBONE_ROUTER_STATE_SECONDARY); + SetState(kStateSecondary); } exit: return; } -void Local::GetConfig(BackboneRouterConfig &aConfig) const +void Local::GetConfig(Config &aConfig) const { aConfig.mSequenceNumber = mSequenceNumber; aConfig.mReregistrationDelay = mReregistrationDelay; aConfig.mMlrTimeout = mMlrTimeout; } -Error Local::SetConfig(const BackboneRouterConfig &aConfig) +Error Local::SetConfig(const Config &aConfig) { Error error = kErrorNone; bool update = false; @@ -178,7 +176,7 @@ Error Local::AddService(bool aForce) Error error = kErrorInvalidState; NetworkData::Service::BackboneRouter::ServerData serverData; - VerifyOrExit(mState != OT_BACKBONE_ROUTER_STATE_DISABLED && Get().IsAttached()); + VerifyOrExit(mState != kStateDisabled && Get().IsAttached()); VerifyOrExit(aForce /* if register by force */ || !Get().HasPrimary() /* if no available Backbone Router service */ || @@ -212,21 +210,21 @@ void Local::RemoveService(void) LogBackboneRouterService("Remove", error); } -void Local::SetState(BackboneRouterState aState) +void Local::SetState(State aState) { VerifyOrExit(mState != aState); - if (mState == OT_BACKBONE_ROUTER_STATE_DISABLED) + if (mState == kStateDisabled) { // Update All Network Backbone Routers Multicast Address for both Secondary and Primary state. mAllNetworkBackboneRouters.SetMulticastNetworkPrefix(Get().GetMeshLocalPrefix()); } - if (mState == OT_BACKBONE_ROUTER_STATE_PRIMARY) + if (mState == kStatePrimary) { Get().RemoveUnicastAddress(mBackboneRouterPrimaryAloc); } - else if (aState == OT_BACKBONE_ROUTER_STATE_PRIMARY) + else if (aState == kStatePrimary) { // Add Primary Backbone Router Aloc for Primary Backbone Router. mBackboneRouterPrimaryAloc.GetAddress().SetPrefix(Get().GetMeshLocalPrefix()); @@ -241,11 +239,11 @@ void Local::SetState(BackboneRouterState aState) return; } -void Local::HandleBackboneRouterPrimaryUpdate(Leader::State aState, const BackboneRouterConfig &aConfig) +void Local::HandleBackboneRouterPrimaryUpdate(Leader::State aState, const Config &aConfig) { OT_UNUSED_VARIABLE(aState); - VerifyOrExit(mState != OT_BACKBONE_ROUTER_STATE_DISABLED && Get().IsAttached()); + VerifyOrExit(IsEnabled() && Get().IsAttached()); // Wait some jitter before trying to Register. if (aConfig.mServer16 == Mac::kShortAddrInvalid) @@ -281,7 +279,7 @@ void Local::HandleBackboneRouterPrimaryUpdate(Leader::State aState, const Backbo } else { - SetState(OT_BACKBONE_ROUTER_STATE_PRIMARY); + SetState(kStatePrimary); } exit: @@ -378,21 +376,18 @@ void Local::HandleDomainPrefixUpdate(Leader::DomainPrefixState aState) Get().SubscribeMulticast(mAllDomainBackboneRouters); } - if (mDomainPrefixCallback != nullptr) + if (mDomainPrefixCallback.IsSet()) { switch (aState) { case Leader::kDomainPrefixAdded: - mDomainPrefixCallback(mDomainPrefixCallbackContext, OT_BACKBONE_ROUTER_DOMAIN_PREFIX_ADDED, - Get().GetDomainPrefix()); + mDomainPrefixCallback.Invoke(OT_BACKBONE_ROUTER_DOMAIN_PREFIX_ADDED, Get().GetDomainPrefix()); break; case Leader::kDomainPrefixRemoved: - mDomainPrefixCallback(mDomainPrefixCallbackContext, OT_BACKBONE_ROUTER_DOMAIN_PREFIX_REMOVED, - Get().GetDomainPrefix()); + mDomainPrefixCallback.Invoke(OT_BACKBONE_ROUTER_DOMAIN_PREFIX_REMOVED, Get().GetDomainPrefix()); break; case Leader::kDomainPrefixRefreshed: - mDomainPrefixCallback(mDomainPrefixCallbackContext, OT_BACKBONE_ROUTER_DOMAIN_PREFIX_CHANGED, - Get().GetDomainPrefix()); + mDomainPrefixCallback.Invoke(OT_BACKBONE_ROUTER_DOMAIN_PREFIX_CHANGED, Get().GetDomainPrefix()); break; default: break; @@ -454,17 +449,11 @@ void Local::LogDomainPrefix(const char *aAction, Error aError) void Local::LogBackboneRouterService(const char *aAction, Error aError) { - LogInfo("%s BBR Service: seqno (%d), delay (%ds), timeout (%ds), %s", aAction, mSequenceNumber, - mReregistrationDelay, mMlrTimeout, ErrorToString(aError)); + LogInfo("%s BBR Service: seqno (%u), delay (%us), timeout (%lus), %s", aAction, mSequenceNumber, + mReregistrationDelay, ToUlong(mMlrTimeout), ErrorToString(aError)); } #endif -void Local::SetDomainPrefixCallback(otBackboneRouterDomainPrefixCallback aCallback, void *aContext) -{ - mDomainPrefixCallback = aCallback; - mDomainPrefixCallbackContext = aContext; -} - } // namespace BackboneRouter } // namespace ot diff --git a/src/core/backbone_router/bbr_local.hpp b/src/core/backbone_router/bbr_local.hpp index b7a76f36630..42b1dd1047e 100644 --- a/src/core/backbone_router/bbr_local.hpp +++ b/src/core/backbone_router/bbr_local.hpp @@ -54,6 +54,8 @@ #include #include "backbone_router/bbr_leader.hpp" +#include "common/as_core_type.hpp" +#include "common/callback.hpp" #include "common/locator.hpp" #include "common/log.hpp" #include "common/non_copyable.hpp" @@ -71,7 +73,16 @@ namespace BackboneRouter { class Local : public InstanceLocator, private NonCopyable { public: - typedef otBackboneRouterState BackboneRouterState; + /** + * This enumeration represents Backbone Router state. + * + */ + enum State : uint8_t + { + kStateDisabled = OT_BACKBONE_ROUTER_STATE_DISABLED, ///< Backbone function is disabled. + kStateSecondary = OT_BACKBONE_ROUTER_STATE_SECONDARY, ///< Secondary Backbone Router. + kStatePrimary = OT_BACKBONE_ROUTER_STATE_PRIMARY, ///< The Primary Backbone Router. + }; /** * This constructor initializes the local Backbone Router. @@ -93,12 +104,10 @@ class Local : public InstanceLocator, private NonCopyable * This method retrieves the Backbone Router state. * * - * @retval OT_BACKBONE_ROUTER_STATE_DISABLED Backbone function is disabled. - * @retval OT_BACKBONE_ROUTER_STATE_SECONDARY Secondary Backbone Router. - * @retval OT_BACKBONE_ROUTER_STATE_PRIMARY Primary Backbone Router. + * @returns The current state of Backbone Router. * */ - BackboneRouterState GetState(void) const { return mState; } + State GetState(void) const { return mState; } /** * This method resets the local Thread Network Data. @@ -112,7 +121,7 @@ class Local : public InstanceLocator, private NonCopyable * @param[out] aConfig The local Backbone Router configuration. * */ - void GetConfig(BackboneRouterConfig &aConfig) const; + void GetConfig(Config &aConfig) const; /** * This method sets local Backbone Router configuration. @@ -123,13 +132,13 @@ class Local : public InstanceLocator, private NonCopyable * @retval kErrorInvalidArgs The configuration in @p aConfig is invalid. * */ - Error SetConfig(const BackboneRouterConfig &aConfig); + Error SetConfig(const Config &aConfig); /** * This method registers Backbone Router Dataset to Leader. * - * @param[in] aForce True to force registration regardless of current BackboneRouterState. - * False to decide based on current BackboneRouterState. + * @param[in] aForce True to force registration regardless of current state. + * False to decide based on current state. * * * @retval kErrorNone Successfully added the Service entry. @@ -146,7 +155,7 @@ class Local : public InstanceLocator, private NonCopyable * @retval False if the Backbone Router is not Primary. * */ - bool IsPrimary(void) const { return mState == OT_BACKBONE_ROUTER_STATE_PRIMARY; } + bool IsPrimary(void) const { return mState == kStatePrimary; } /** * This method indicates whether or not the Backbone Router is enabled. @@ -155,7 +164,7 @@ class Local : public InstanceLocator, private NonCopyable * @retval False if the Backbone Router is not enabled. * */ - bool IsEnabled(void) const { return mState != OT_BACKBONE_ROUTER_STATE_DISABLED; } + bool IsEnabled(void) const { return mState != kStateDisabled; } /** * This method sets the Backbone Router registration jitter value. @@ -180,7 +189,7 @@ class Local : public InstanceLocator, private NonCopyable * @param[in] aConfig The Primary Backbone Router service. * */ - void HandleBackboneRouterPrimaryUpdate(Leader::State aState, const BackboneRouterConfig &aConfig); + void HandleBackboneRouterPrimaryUpdate(Leader::State aState, const Config &aConfig); /** * This method gets the Domain Prefix configuration. @@ -253,10 +262,13 @@ class Local : public InstanceLocator, private NonCopyable * @param[in] aContext A user context pointer. * */ - void SetDomainPrefixCallback(otBackboneRouterDomainPrefixCallback aCallback, void *aContext); + void SetDomainPrefixCallback(otBackboneRouterDomainPrefixCallback aCallback, void *aContext) + { + mDomainPrefixCallback.Set(aCallback, aContext); + } private: - void SetState(BackboneRouterState aState); + void SetState(State aState); void RemoveService(void); void AddDomainPrefixToNetworkData(void); void RemoveDomainPrefixFromNetworkData(void); @@ -269,11 +281,11 @@ class Local : public InstanceLocator, private NonCopyable void LogDomainPrefix(const char *, Error) {} #endif - BackboneRouterState mState; - uint32_t mMlrTimeout; - uint16_t mReregistrationDelay; - uint8_t mSequenceNumber; - uint8_t mRegistrationJitter; + State mState; + uint32_t mMlrTimeout; + uint16_t mReregistrationDelay; + uint8_t mSequenceNumber; + uint8_t mRegistrationJitter; // Indicates whether or not already add Backbone Router Service to local server data. // Used to check whether or not in restore stage after reset or whether to remove @@ -282,15 +294,16 @@ class Local : public InstanceLocator, private NonCopyable NetworkData::OnMeshPrefixConfig mDomainPrefixConfig; - Ip6::Netif::UnicastAddress mBackboneRouterPrimaryAloc; - Ip6::Address mAllNetworkBackboneRouters; - Ip6::Address mAllDomainBackboneRouters; - otBackboneRouterDomainPrefixCallback mDomainPrefixCallback; - void * mDomainPrefixCallbackContext; + Ip6::Netif::UnicastAddress mBackboneRouterPrimaryAloc; + Ip6::Address mAllNetworkBackboneRouters; + Ip6::Address mAllDomainBackboneRouters; + Callback mDomainPrefixCallback; }; } // namespace BackboneRouter +DefineMapEnum(otBackboneRouterState, BackboneRouter::Local::State); + /** * @} */ diff --git a/src/core/backbone_router/bbr_manager.cpp b/src/core/backbone_router/bbr_manager.cpp index 2de2f6f8240..fd04b12a852 100644 --- a/src/core/backbone_router/bbr_manager.cpp +++ b/src/core/backbone_router/bbr_manager.cpp @@ -40,6 +40,7 @@ #include "common/instance.hpp" #include "common/locator_getters.hpp" #include "common/log.hpp" +#include "common/num_utils.hpp" #include "common/random.hpp" #include "thread/mle_types.hpp" #include "thread/thread_netif.hpp" @@ -54,19 +55,13 @@ RegisterLogModule("BbrManager"); Manager::Manager(Instance &aInstance) : InstanceLocator(aInstance) -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE - , mMulticastListenerRegistration(UriPath::kMlr, Manager::HandleMulticastListenerRegistration, this) -#endif #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE - , mDuaRegistration(UriPath::kDuaRegistrationRequest, Manager::HandleDuaRegistration, this) - , mBackboneQuery(UriPath::kBackboneQuery, Manager::HandleBackboneQuery, this) - , mBackboneAnswer(UriPath::kBackboneAnswer, Manager::HandleBackboneAnswer, this) , mNdProxyTable(aInstance) #endif #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE , mMulticastListenersTable(aInstance) #endif - , mTimer(aInstance, Manager::HandleTimer) + , mTimer(aInstance) , mBackboneTmfAgent(aInstance) #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE @@ -83,10 +78,6 @@ Manager::Manager(Instance &aInstance) #endif #endif { -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE - mBackboneTmfAgent.AddResource(mBackboneQuery); - mBackboneTmfAgent.AddResource(mBackboneAnswer); -#endif } void Manager::HandleNotifierEvents(Events aEvents) @@ -95,14 +86,10 @@ void Manager::HandleNotifierEvents(Events aEvents) if (aEvents.Contains(kEventThreadBackboneRouterStateChanged)) { - if (Get().GetState() == OT_BACKBONE_ROUTER_STATE_DISABLED) + if (!Get().IsEnabled()) { #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE - Get().RemoveResource(mMulticastListenerRegistration); mMulticastListenersTable.Clear(); -#endif -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE - Get().RemoveResource(mDuaRegistration); #endif mTimer.Stop(); @@ -119,12 +106,6 @@ void Manager::HandleNotifierEvents(Events aEvents) } else { -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE - Get().AddResource(mMulticastListenerRegistration); -#endif -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE - Get().AddResource(mDuaRegistration); -#endif if (!mTimer.IsRunning()) { mTimer.Start(kTimerInterval); @@ -137,11 +118,6 @@ void Manager::HandleNotifierEvents(Events aEvents) } } -void Manager::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void Manager::HandleTimer(void) { #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE @@ -156,12 +132,21 @@ void Manager::HandleTimer(void) } #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE +template <> void Manager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + VerifyOrExit(Get().IsEnabled()); + HandleMulticastListenerRegistration(aMessage, aMessageInfo); + +exit: + return; +} + void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error = kErrorNone; bool isPrimary = Get().IsPrimary(); ThreadStatusTlv::MlrStatus status = ThreadStatusTlv::kMlrSuccess; - BackboneRouterConfig config; + Config config; uint16_t addressesOffset, addressesLength; Ip6::Address address; @@ -224,11 +209,11 @@ void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage, { uint32_t origTimeout = timeout; - timeout = OT_MIN(timeout, static_cast(Mle::kMlrTimeoutMax)); + timeout = Min(timeout, Mle::kMlrTimeoutMax); if (timeout != origTimeout) { - LogNote("MLR.req: MLR timeout is normalized from %u to %u", origTimeout, timeout); + LogNote("MLR.req: MLR timeout is normalized from %lu to %lu", ToUlong(origTimeout), ToUlong(timeout)); } } } @@ -296,10 +281,10 @@ void Manager::HandleMulticastListenerRegistration(const Coap::Message &aMessage, } } -void Manager::SendMulticastListenerRegistrationResponse(const Coap::Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, +void Manager::SendMulticastListenerRegistrationResponse(const Coap::Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, ThreadStatusTlv::MlrStatus aStatus, - Ip6::Address * aFailedAddresses, + Ip6::Address *aFailedAddresses, uint8_t aFailedAddressNum) { Error error = kErrorNone; @@ -336,14 +321,14 @@ void Manager::SendBackboneMulticastListenerRegistration(const Ip6::Address *aAdd uint32_t aTimeout) { Error error = kErrorNone; - Coap::Message * message = nullptr; + Coap::Message *message = nullptr; Ip6::MessageInfo messageInfo; Ip6AddressesTlv addressesTlv; BackboneTmfAgent &backboneTmf = Get(); OT_ASSERT(aAddressNum >= Ip6AddressesTlv::kMinAddresses && aAddressNum <= Ip6AddressesTlv::kMaxAddresses); - message = backboneTmf.NewNonConfirmablePostMessage(UriPath::kBackboneMlr); + message = backboneTmf.NewNonConfirmablePostMessage(kUriBackboneMlr); VerifyOrExit(message != nullptr, error = kErrorNoBufs); addressesTlv.Init(); @@ -368,6 +353,17 @@ void Manager::SendBackboneMulticastListenerRegistration(const Ip6::Address *aAdd #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE + +template <> +void Manager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + VerifyOrExit(Get().IsEnabled()); + HandleDuaRegistration(aMessage, aMessageInfo); + +exit: + return; +} + void Manager::HandleDuaRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error = kErrorNone; @@ -445,9 +441,9 @@ void Manager::HandleDuaRegistration(const Coap::Message &aMessage, const Ip6::Me } } -void Manager::SendDuaRegistrationResponse(const Coap::Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, - const Ip6::Address & aTarget, +void Manager::SendDuaRegistrationResponse(const Coap::Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, + const Ip6::Address &aTarget, ThreadStatusTlv::DuaStatus aStatus) { Error error = kErrorNone; @@ -496,10 +492,7 @@ void Manager::ConfigNextMulticastListenerRegistrationResponse(ThreadStatusTlv::M #endif #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE -NdProxyTable &Manager::GetNdProxyTable(void) -{ - return mNdProxyTable; -} +NdProxyTable &Manager::GetNdProxyTable(void) { return mNdProxyTable; } bool Manager::ShouldForwardDuaToBackbone(const Ip6::Address &aAddress) { @@ -524,12 +517,12 @@ bool Manager::ShouldForwardDuaToBackbone(const Ip6::Address &aAddress) Error Manager::SendBackboneQuery(const Ip6::Address &aDua, uint16_t aRloc16) { Error error = kErrorNone; - Coap::Message * message = nullptr; + Coap::Message *message = nullptr; Ip6::MessageInfo messageInfo; VerifyOrExit(Get().IsPrimary(), error = kErrorInvalidState); - message = mBackboneTmfAgent.NewPriorityNonConfirmablePostMessage(UriPath::kBackboneQuery); + message = mBackboneTmfAgent.NewPriorityNonConfirmablePostMessage(kUriBackboneQuery); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = Tlv::Append(*message, aDua)); @@ -553,12 +546,7 @@ Error Manager::SendBackboneQuery(const Ip6::Address &aDua, uint16_t aRloc16) return error; } -void Manager::HandleBackboneQuery(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleBackboneQuery(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void Manager::HandleBackboneQuery(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> void Manager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error = kErrorNone; Ip6::Address dua; @@ -587,12 +575,7 @@ void Manager::HandleBackboneQuery(const Coap::Message &aMessage, const Ip6::Mess LogInfo("HandleBackboneQuery: %s", ErrorToString(error)); } -void Manager::HandleBackboneAnswer(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleBackboneAnswer(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void Manager::HandleBackboneAnswer(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> void Manager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error = kErrorNone; bool proactive; @@ -638,7 +621,7 @@ void Manager::HandleBackboneAnswer(const Coap::Message &aMessage, const Ip6::Mes LogInfo("HandleBackboneAnswer: %s", ErrorToString(error)); } -Error Manager::SendProactiveBackboneNotification(const Ip6::Address & aDua, +Error Manager::SendProactiveBackboneNotification(const Ip6::Address &aDua, const Ip6::InterfaceIdentifier &aMeshLocalIid, uint32_t aTimeSinceLastTransaction) { @@ -646,8 +629,8 @@ Error Manager::SendProactiveBackboneNotification(const Ip6::Address & aTimeSinceLastTransaction, Mac::kShortAddrInvalid); } -Error Manager::SendBackboneAnswer(const Ip6::MessageInfo & aQueryMessageInfo, - const Ip6::Address & aDua, +Error Manager::SendBackboneAnswer(const Ip6::MessageInfo &aQueryMessageInfo, + const Ip6::Address &aDua, uint16_t aSrcRloc16, const NdProxyTable::NdProxy &aNdProxy) { @@ -655,21 +638,21 @@ Error Manager::SendBackboneAnswer(const Ip6::MessageInfo & aQueryMessageInfo aNdProxy.GetTimeSinceLastTransaction(), aSrcRloc16); } -Error Manager::SendBackboneAnswer(const Ip6::Address & aDstAddr, - const Ip6::Address & aDua, +Error Manager::SendBackboneAnswer(const Ip6::Address &aDstAddr, + const Ip6::Address &aDua, const Ip6::InterfaceIdentifier &aMeshLocalIid, uint32_t aTimeSinceLastTransaction, uint16_t aSrcRloc16) { Error error = kErrorNone; - Coap::Message * message = nullptr; + Coap::Message *message = nullptr; Ip6::MessageInfo messageInfo; bool proactive = aDstAddr.IsMulticast(); VerifyOrExit((message = mBackboneTmfAgent.NewPriorityMessage()) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->Init(proactive ? Coap::kTypeNonConfirmable : Coap::kTypeConfirmable, Coap::kCodePost, - UriPath::kBackboneAnswer)); + kUriBackboneAnswer)); SuccessOrExit(error = message->SetPayloadMarker()); SuccessOrExit(error = Tlv::Append(*message, aDua)); @@ -678,11 +661,8 @@ Error Manager::SendBackboneAnswer(const Ip6::Address & aDstAddr, SuccessOrExit(error = Tlv::Append(*message, aTimeSinceLastTransaction)); - { - const MeshCoP::NameData nameData = Get().GetNetworkName().GetAsData(); - - SuccessOrExit(error = Tlv::Append(*message, nameData.GetBuffer(), nameData.GetLength())); - } + SuccessOrExit(error = Tlv::Append( + *message, Get().GetNetworkName().GetAsCString())); if (aSrcRloc16 != Mac::kShortAddrInvalid) { @@ -732,7 +712,7 @@ void Manager::HandleDadBackboneAnswer(const Ip6::Address &aDua, const Ip6::Inter aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), duplicate ? "Y" : "N"); } -void Manager::HandleExtendedBackboneAnswer(const Ip6::Address & aDua, +void Manager::HandleExtendedBackboneAnswer(const Ip6::Address &aDua, const Ip6::InterfaceIdentifier &aMeshLocalIid, uint32_t aTimeSinceLastTransaction, uint16_t aSrcRloc16) @@ -742,11 +722,11 @@ void Manager::HandleExtendedBackboneAnswer(const Ip6::Address & aDua, dest.SetToRoutingLocator(Get().GetMeshLocalPrefix(), aSrcRloc16); Get().SendAddressQueryResponse(aDua, aMeshLocalIid, &aTimeSinceLastTransaction, dest); - LogInfo("HandleExtendedBackboneAnswer: target=%s, mliid=%s, LTT=%lds, rloc16=%04x", aDua.ToString().AsCString(), - aMeshLocalIid.ToString().AsCString(), aTimeSinceLastTransaction, aSrcRloc16); + LogInfo("HandleExtendedBackboneAnswer: target=%s, mliid=%s, LTT=%lus, rloc16=%04x", aDua.ToString().AsCString(), + aMeshLocalIid.ToString().AsCString(), ToUlong(aTimeSinceLastTransaction), aSrcRloc16); } -void Manager::HandleProactiveBackboneNotification(const Ip6::Address & aDua, +void Manager::HandleProactiveBackboneNotification(const Ip6::Address &aDua, const Ip6::InterfaceIdentifier &aMeshLocalIid, uint32_t aTimeSinceLastTransaction) { @@ -779,8 +759,8 @@ void Manager::HandleProactiveBackboneNotification(const Ip6::Address & } exit: - LogInfo("HandleProactiveBackboneNotification: %s, target=%s, mliid=%s, LTT=%lds", ErrorToString(error), - aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), aTimeSinceLastTransaction); + LogInfo("HandleProactiveBackboneNotification: %s, target=%s, mliid=%s, LTT=%lus", ErrorToString(error), + aDua.ToString().AsCString(), aMeshLocalIid.ToString().AsCString(), ToUlong(aTimeSinceLastTransaction)); } #endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE diff --git a/src/core/backbone_router/bbr_manager.hpp b/src/core/backbone_router/bbr_manager.hpp index e5f17056812..da5434d78f1 100644 --- a/src/core/backbone_router/bbr_manager.hpp +++ b/src/core/backbone_router/bbr_manager.hpp @@ -49,6 +49,7 @@ #include "common/non_copyable.hpp" #include "net/netif.hpp" #include "thread/network_data.hpp" +#include "thread/tmf.hpp" namespace ot { @@ -61,6 +62,8 @@ namespace BackboneRouter { class Manager : public InstanceLocator, private NonCopyable { friend class ot::Notifier; + friend class Tmf::Agent; + friend class BackboneTmfAgent; public: /** @@ -164,27 +167,22 @@ class Manager : public InstanceLocator, private NonCopyable * @retval kErrorNoBufs If insufficient message buffers available. * */ - Error SendProactiveBackboneNotification(const Ip6::Address & aDua, + Error SendProactiveBackboneNotification(const Ip6::Address &aDua, const Ip6::InterfaceIdentifier &aMeshLocalIid, uint32_t aTimeSinceLastTransaction); private: static constexpr uint32_t kTimerInterval = 1000; + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE - static void HandleMulticastListenerRegistration(void * aContext, - otMessage * aMessage, - const otMessageInfo *aMessageInfo) - { - static_cast(aContext)->HandleMulticastListenerRegistration( - *static_cast(aMessage), *static_cast(aMessageInfo)); - } void HandleMulticastListenerRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - void SendMulticastListenerRegistrationResponse(const Coap::Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, + void SendMulticastListenerRegistrationResponse(const Coap::Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, ThreadStatusTlv::MlrStatus aStatus, - Ip6::Address * aFailedAddresses, + Ip6::Address *aFailedAddresses, uint8_t aFailedAddressNum); void SendBackboneMulticastListenerRegistration(const Ip6::Address *aAddresses, uint8_t aAddressNum, @@ -192,59 +190,45 @@ class Manager : public InstanceLocator, private NonCopyable #endif #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE - static void HandleDuaRegistration(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) - { - static_cast(aContext)->HandleDuaRegistration(*static_cast(aMessage), - *static_cast(aMessageInfo)); - } - void HandleDuaRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleBackboneQuery(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleBackboneQuery(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleBackboneAnswer(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleBackboneAnswer(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - Error SendBackboneAnswer(const Ip6::MessageInfo & aQueryMessageInfo, - const Ip6::Address & aDua, - uint16_t aSrcRloc16, - const NdProxyTable::NdProxy &aNdProxy); - Error SendBackboneAnswer(const Ip6::Address & aDstAddr, - const Ip6::Address & aDua, - const Ip6::InterfaceIdentifier &aMeshLocalIid, - uint32_t aTimeSinceLastTransaction, - uint16_t aSrcRloc16); - void HandleDadBackboneAnswer(const Ip6::Address &aDua, const Ip6::InterfaceIdentifier &aMeshLocalIid); - void HandleExtendedBackboneAnswer(const Ip6::Address & aDua, - const Ip6::InterfaceIdentifier &aMeshLocalIid, - uint32_t aTimeSinceLastTransaction, - uint16_t aSrcRloc16); - void HandleProactiveBackboneNotification(const Ip6::Address & aDua, - const Ip6::InterfaceIdentifier &aMeshLocalIid, - uint32_t aTimeSinceLastTransaction); - void SendDuaRegistrationResponse(const Coap::Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, - const Ip6::Address & aTarget, - ThreadStatusTlv::DuaStatus aStatus); + void HandleDuaRegistration(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + Error SendBackboneAnswer(const Ip6::MessageInfo &aQueryMessageInfo, + const Ip6::Address &aDua, + uint16_t aSrcRloc16, + const NdProxyTable::NdProxy &aNdProxy); + Error SendBackboneAnswer(const Ip6::Address &aDstAddr, + const Ip6::Address &aDua, + const Ip6::InterfaceIdentifier &aMeshLocalIid, + uint32_t aTimeSinceLastTransaction, + uint16_t aSrcRloc16); + void HandleDadBackboneAnswer(const Ip6::Address &aDua, const Ip6::InterfaceIdentifier &aMeshLocalIid); + void HandleExtendedBackboneAnswer(const Ip6::Address &aDua, + const Ip6::InterfaceIdentifier &aMeshLocalIid, + uint32_t aTimeSinceLastTransaction, + uint16_t aSrcRloc16); + void HandleProactiveBackboneNotification(const Ip6::Address &aDua, + const Ip6::InterfaceIdentifier &aMeshLocalIid, + uint32_t aTimeSinceLastTransaction); + void SendDuaRegistrationResponse(const Coap::Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, + const Ip6::Address &aTarget, + ThreadStatusTlv::DuaStatus aStatus); #endif void HandleNotifierEvents(Events aEvents); - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); + void HandleTimer(void); void LogError(const char *aText, Error aError) const; -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE - Coap::Resource mMulticastListenerRegistration; -#endif + using BbrTimer = TimerMilliIn; + #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE - Coap::Resource mDuaRegistration; - Coap::Resource mBackboneQuery; - Coap::Resource mBackboneAnswer; - NdProxyTable mNdProxyTable; + NdProxyTable mNdProxyTable; #endif #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE MulticastListenersTable mMulticastListenersTable; #endif - TimerMilli mTimer; + BbrTimer mTimer; BackboneTmfAgent mBackboneTmfAgent; @@ -265,6 +249,15 @@ class Manager : public InstanceLocator, private NonCopyable #endif }; +#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE +DeclareTmfHandler(Manager, kUriMlr); +#endif +#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE +DeclareTmfHandler(Manager, kUriDuaRegistrationRequest); +DeclareTmfHandler(Manager, kUriBackboneQuery); +DeclareTmfHandler(Manager, kUriBackboneAnswer); +#endif + } // namespace BackboneRouter /** diff --git a/src/core/backbone_router/multicast_listeners_table.cpp b/src/core/backbone_router/multicast_listeners_table.cpp index 35a49726383..4f477cdee10 100644 --- a/src/core/backbone_router/multicast_listeners_table.cpp +++ b/src/core/backbone_router/multicast_listeners_table.cpp @@ -77,13 +77,10 @@ Error MulticastListenersTable::Add(const Ip6::Address &aAddress, Time aExpireTim FixHeap(mNumValidListeners - 1); - if (mCallback != nullptr) - { - mCallback(mCallbackContext, OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ADDED, &aAddress); - } + mCallback.InvokeIfSet(MapEnum(Listener::kEventAdded), &aAddress); exit: - LogMulticastListenersTable("Add", aAddress, aExpireTime, error); + Log(kAdd, aAddress, aExpireTime, error); CheckInvariants(); return error; } @@ -106,17 +103,14 @@ void MulticastListenersTable::Remove(const Ip6::Address &aAddress) FixHeap(i); } - if (mCallback != nullptr) - { - mCallback(mCallbackContext, OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED, &aAddress); - } + mCallback.InvokeIfSet(MapEnum(Listener::kEventRemoved), &aAddress); ExitNow(error = kErrorNone); } } exit: - LogMulticastListenersTable("Remove", aAddress, TimeMilli(0), error); + Log(kRemove, aAddress, TimeMilli(0), error); CheckInvariants(); } @@ -127,7 +121,7 @@ void MulticastListenersTable::Expire(void) while (mNumValidListeners > 0 && now >= mListeners[0].GetExpireTime()) { - LogMulticastListenersTable("Expire", mListeners[0].GetAddress(), mListeners[0].GetExpireTime(), kErrorNone); + Log(kExpire, mListeners[0].GetAddress(), mListeners[0].GetExpireTime(), kErrorNone); address = mListeners[0].GetAddress(); mNumValidListeners--; @@ -138,28 +132,34 @@ void MulticastListenersTable::Expire(void) FixHeap(0); } - if (mCallback != nullptr) - { - mCallback(mCallbackContext, OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED, &address); - } + mCallback.InvokeIfSet(MapEnum(Listener::kEventRemoved), &address); } CheckInvariants(); } -void MulticastListenersTable::LogMulticastListenersTable(const char * aAction, - const Ip6::Address &aAddress, - TimeMilli aExpireTime, - Error aError) +#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG) +void MulticastListenersTable::Log(Action aAction, + const Ip6::Address &aAddress, + TimeMilli aExpireTime, + Error aError) const { - OT_UNUSED_VARIABLE(aAction); - OT_UNUSED_VARIABLE(aAddress); - OT_UNUSED_VARIABLE(aExpireTime); - OT_UNUSED_VARIABLE(aError); - - LogDebg("%s %s expire %u: %s", aAction, aAddress.ToString().AsCString(), aExpireTime.GetValue(), - ErrorToString(aError)); + static const char *const kActionStrings[] = { + "Add", // (0) kAdd + "Remove", // (1) kRemove + "Expire", // (2) kExpire + }; + + static_assert(0 == kAdd, "kAdd value is incorrect"); + static_assert(1 == kRemove, "kRemove value is incorrect"); + static_assert(2 == kExpire, "kExpire value is incorrect"); + + LogDebg("%s %s expire %lu: %s", kActionStrings[aAction], aAddress.ToString().AsCString(), + ToUlong(aExpireTime.GetValue()), ErrorToString(aError)); } +#else +void MulticastListenersTable::Log(Action, const Ip6::Address &, TimeMilli, Error) const {} +#endif void MulticastListenersTable::FixHeap(uint16_t aIndex) { @@ -263,11 +263,11 @@ MulticastListenersTable::Listener *MulticastListenersTable::IteratorBuilder::end void MulticastListenersTable::Clear(void) { - if (mCallback != nullptr) + if (mCallback.IsSet()) { for (uint16_t i = 0; i < mNumValidListeners; i++) { - mCallback(mCallbackContext, OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED, &mListeners[i].GetAddress()); + mCallback.Invoke(MapEnum(Listener::kEventRemoved), &mListeners[i].GetAddress()); } } @@ -276,22 +276,20 @@ void MulticastListenersTable::Clear(void) CheckInvariants(); } -void MulticastListenersTable::SetCallback(otBackboneRouterMulticastListenerCallback aCallback, void *aContext) +void MulticastListenersTable::SetCallback(Listener::Callback aCallback, void *aContext) { - mCallback = aCallback; - mCallbackContext = aContext; + mCallback.Set(aCallback, aContext); - if (mCallback != nullptr) + if (mCallback.IsSet()) { for (uint16_t i = 0; i < mNumValidListeners; i++) { - mCallback(mCallbackContext, OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ADDED, &mListeners[i].GetAddress()); + mCallback.Invoke(MapEnum(Listener::kEventAdded), &mListeners[i].GetAddress()); } } } -Error MulticastListenersTable::GetNext(otBackboneRouterMulticastListenerIterator &aIterator, - otBackboneRouterMulticastListenerInfo & aListenerInfo) +Error MulticastListenersTable::GetNext(Listener::Iterator &aIterator, Listener::Info &aInfo) { Error error = kErrorNone; TimeMilli now; @@ -300,8 +298,8 @@ Error MulticastListenersTable::GetNext(otBackboneRouterMulticastListenerIterator now = TimerMilli::GetNow(); - aListenerInfo.mAddress = mListeners[aIterator].mAddress; - aListenerInfo.mTimeout = + aInfo.mAddress = mListeners[aIterator].mAddress; + aInfo.mTimeout = Time::MsecToSec(mListeners[aIterator].mExpireTime > now ? mListeners[aIterator].mExpireTime - now : 0); aIterator++; diff --git a/src/core/backbone_router/multicast_listeners_table.hpp b/src/core/backbone_router/multicast_listeners_table.hpp index b7ae80cd05f..7565f94d32c 100644 --- a/src/core/backbone_router/multicast_listeners_table.hpp +++ b/src/core/backbone_router/multicast_listeners_table.hpp @@ -40,6 +40,8 @@ #include +#include "common/as_core_type.hpp" +#include "common/callback.hpp" #include "common/non_copyable.hpp" #include "common/notifier.hpp" #include "common/time.hpp" @@ -67,6 +69,16 @@ class MulticastListenersTable : public InstanceLocator, private NonCopyable friend class MulticastListenersTable; public: + typedef otBackboneRouterMulticastListenerCallback Callback; ///< Listener callback. + typedef otBackboneRouterMulticastListenerIterator Iterator; ///< Iterator to go over Listener entries. + typedef otBackboneRouterMulticastListenerInfo Info; ///< Listener info. + + enum Event : uint8_t ///< Listener Event + { + kEventAdded = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_ADDED, ///< Listener was added. + kEventRemoved = OT_BACKBONE_ROUTER_MULTICAST_LISTENER_REMOVED, ///< Listener was removed. + }; + /** * This constructor initializes the `Listener` object. * @@ -108,8 +120,6 @@ class MulticastListenersTable : public InstanceLocator, private NonCopyable explicit MulticastListenersTable(Instance &aInstance) : InstanceLocator(aInstance) , mNumValidListeners(0) - , mCallback(nullptr) - , mCallbackContext(nullptr) { } @@ -173,27 +183,24 @@ class MulticastListenersTable : public InstanceLocator, private NonCopyable * @param[in] aContext A user context pointer. * */ - void SetCallback(otBackboneRouterMulticastListenerCallback aCallback, void *aContext); + void SetCallback(Listener::Callback aCallback, void *aContext); /** * This method gets the next Multicast Listener. * - * @param[in] aIterator A pointer to the Multicast Listener Iterator. - * @param[out] aListenerInfo A pointer to where the Multicast Listener info is placed. + * @param[in] aIterator A pointer to the Multicast Listener Iterator. + * @param[out] aInfo A reference to output the Multicast Listener info. * * @retval kErrorNone Successfully found the next Multicast Listener info. * @retval kErrorNotFound No subsequent Multicast Listener was found. * */ - Error GetNext(otBackboneRouterMulticastListenerIterator &aIterator, - otBackboneRouterMulticastListenerInfo & aListenerInfo); + Error GetNext(Listener::Iterator &aIterator, Listener::Info &aInfo); private: - static constexpr uint16_t kMulticastListenersTableSize = OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS; + static constexpr uint16_t kTableSize = OPENTHREAD_CONFIG_MAX_MULTICAST_LISTENERS; - static_assert( - kMulticastListenersTableSize >= 75, - "Thread 1.2 Conformance requires the Multicast Listener Table size to be larger than or equal to 75."); + static_assert(kTableSize >= 75, "Thread 1.2 Conformance requires table size of at least 75 listeners."); class IteratorBuilder : InstanceLocator { @@ -207,25 +214,29 @@ class MulticastListenersTable : public InstanceLocator, private NonCopyable Listener *end(void); }; - void LogMulticastListenersTable(const char * aAction, - const Ip6::Address &aAddress, - TimeMilli aExpireTime, - Error aError); + enum Action : uint8_t + { + kAdd, + kRemove, + kExpire, + }; + + void Log(Action aAction, const Ip6::Address &aAddress, TimeMilli aExpireTime, Error aError) const; void FixHeap(uint16_t aIndex); bool SiftHeapElemDown(uint16_t aIndex); void SiftHeapElemUp(uint16_t aIndex); void CheckInvariants(void) const; - Listener mListeners[kMulticastListenersTableSize]; - uint16_t mNumValidListeners; - - otBackboneRouterMulticastListenerCallback mCallback; - void * mCallbackContext; + Listener mListeners[kTableSize]; + uint16_t mNumValidListeners; + Callback mCallback; }; } // namespace BackboneRouter +DefineMapEnum(otBackboneRouterMulticastListenerEvent, BackboneRouter::MulticastListenersTable::Listener::Event); + /** * @} */ diff --git a/src/core/backbone_router/ndproxy_table.cpp b/src/core/backbone_router/ndproxy_table.cpp index 89c0442255a..787a8a930ed 100644 --- a/src/core/backbone_router/ndproxy_table.cpp +++ b/src/core/backbone_router/ndproxy_table.cpp @@ -38,6 +38,7 @@ #include "common/array.hpp" #include "common/locator_getters.hpp" #include "common/log.hpp" +#include "common/num_utils.hpp" namespace ot { @@ -66,10 +67,9 @@ void NdProxyTable::NdProxy::Update(uint16_t aRloc16, uint32_t aTimeSinceLastTran { OT_ASSERT(mValid); - mRloc16 = aRloc16; - aTimeSinceLastTransaction = - OT_MIN(aTimeSinceLastTransaction, static_cast(Mle::kTimeSinceLastTransactionMax)); - mLastRegistrationTime = TimerMilli::GetNow() - TimeMilli::SecToMsec(aTimeSinceLastTransaction); + mRloc16 = aRloc16; + aTimeSinceLastTransaction = Min(aTimeSinceLastTransaction, Mle::kTimeSinceLastTransactionMax); + mLastRegistrationTime = TimerMilli::GetNow() - TimeMilli::SecToMsec(aTimeSinceLastTransaction); } bool NdProxyTable::MatchesFilter(const NdProxy &aProxy, Filter aFilter) @@ -123,10 +123,7 @@ void NdProxyTable::Iterator::Advance(void) } while (mItem < GetArrayEnd(table.mProxies) && !MatchesFilter(*mItem, mFilter)); } -void NdProxyTable::Erase(NdProxy &aNdProxy) -{ - aNdProxy.mValid = false; -} +void NdProxyTable::Erase(NdProxy &aNdProxy) { aNdProxy.mValid = false; } void NdProxyTable::HandleDomainPrefixUpdate(Leader::DomainPrefixState aState) { @@ -144,10 +141,7 @@ void NdProxyTable::Clear(void) proxy.Clear(); } - if (mCallback != nullptr) - { - mCallback(mCallbackContext, OT_BACKBONE_ROUTER_NDPROXY_CLEARED, nullptr); - } + mCallback.InvokeIfSet(MapEnum(NdProxy::kCleared), nullptr); LogInfo("NdProxyTable::Clear!"); } @@ -155,7 +149,7 @@ void NdProxyTable::Clear(void) Error NdProxyTable::Register(const Ip6::InterfaceIdentifier &aAddressIid, const Ip6::InterfaceIdentifier &aMeshLocalIid, uint16_t aRloc16, - const uint32_t * aTimeSinceLastTransaction) + const uint32_t *aTimeSinceLastTransaction) { Error error = kErrorNone; NdProxy *proxy = FindByAddressIid(aAddressIid); @@ -173,7 +167,7 @@ Error NdProxyTable::Register(const Ip6::InterfaceIdentifier &aAddressIid, proxy = FindByMeshLocalIid(aMeshLocalIid); if (proxy != nullptr) { - TriggerCallback(OT_BACKBONE_ROUTER_NDPROXY_REMOVED, proxy->mAddressIid); + TriggerCallback(NdProxy::kRemoved, proxy->mAddressIid); Erase(*proxy); } else @@ -188,8 +182,8 @@ Error NdProxyTable::Register(const Ip6::InterfaceIdentifier &aAddressIid, mIsAnyDadInProcess = true; exit: - LogInfo("NdProxyTable::Register %s MLIID %s RLOC16 %04x LTT %u => %s", aAddressIid.ToString().AsCString(), - aMeshLocalIid.ToString().AsCString(), aRloc16, timeSinceLastTransaction, ErrorToString(error)); + LogInfo("NdProxyTable::Register %s MLIID %s RLOC16 %04x LTT %lu => %s", aAddressIid.ToString().AsCString(), + aMeshLocalIid.ToString().AsCString(), aRloc16, ToUlong(timeSinceLastTransaction), ErrorToString(error)); return error; } @@ -271,26 +265,19 @@ void NdProxyTable::HandleTimer(void) return; } -void NdProxyTable::SetCallback(otBackboneRouterNdProxyCallback aCallback, void *aContext) -{ - mCallback = aCallback; - mCallbackContext = aContext; -} - -void NdProxyTable::TriggerCallback(otBackboneRouterNdProxyEvent aEvent, - const Ip6::InterfaceIdentifier &aAddressIid) const +void NdProxyTable::TriggerCallback(NdProxy::Event aEvent, const Ip6::InterfaceIdentifier &aAddressIid) const { Ip6::Address dua; const Ip6::Prefix *prefix = Get().GetDomainPrefix(); - VerifyOrExit(mCallback != nullptr); + VerifyOrExit(mCallback.IsSet()); OT_ASSERT(prefix != nullptr); dua.SetPrefix(*prefix); dua.SetIid(aAddressIid); - mCallback(mCallbackContext, aEvent, &dua); + mCallback.Invoke(MapEnum(aEvent), &dua); exit: return; @@ -330,8 +317,7 @@ void NdProxyTable::NotifyDuaRegistrationOnBackboneLink(NdProxyTable::NdProxy &aN { if (!aNdProxy.mDadFlag) { - TriggerCallback(aIsRenew ? OT_BACKBONE_ROUTER_NDPROXY_RENEWED : OT_BACKBONE_ROUTER_NDPROXY_ADDED, - aNdProxy.mAddressIid); + TriggerCallback(aIsRenew ? NdProxy::kRenewed : NdProxy::kAdded, aNdProxy.mAddressIid); IgnoreError(Get().SendProactiveBackboneNotification( GetDua(aNdProxy), aNdProxy.GetMeshLocalIid(), aNdProxy.GetTimeSinceLastTransaction())); diff --git a/src/core/backbone_router/ndproxy_table.hpp b/src/core/backbone_router/ndproxy_table.hpp index 097a6d3a3a5..8c12db1a8ad 100644 --- a/src/core/backbone_router/ndproxy_table.hpp +++ b/src/core/backbone_router/ndproxy_table.hpp @@ -40,6 +40,8 @@ #include #include "backbone_router/bbr_leader.hpp" +#include "common/as_core_type.hpp" +#include "common/callback.hpp" #include "common/iterator_utils.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" @@ -65,8 +67,23 @@ class NdProxyTable : public InstanceLocator, private NonCopyable class NdProxy : private Clearable { friend class NdProxyTable; + friend class Clearable; public: + typedef otBackboneRouterNdProxyCallback Callback; ///< ND Proxy callback. + + /** + * This type represents the ND Proxy events. + * + */ + enum Event + { + kAdded = OT_BACKBONE_ROUTER_NDPROXY_ADDED, ///< ND Proxy was added. + kRemoved = OT_BACKBONE_ROUTER_NDPROXY_REMOVED, ///< ND Proxy was removed. + kRenewed = OT_BACKBONE_ROUTER_NDPROXY_RENEWED, ///< ND Proxy was renewed. + kCleared = OT_BACKBONE_ROUTER_NDPROXY_CLEARED, ///< All ND Proxies were cleared. + }; + /** * This method gets the Mesh-Local IID of the ND Proxy. * @@ -133,8 +150,6 @@ class NdProxyTable : public InstanceLocator, private NonCopyable */ explicit NdProxyTable(Instance &aInstance) : InstanceLocator(aInstance) - , mCallback(nullptr) - , mCallbackContext(nullptr) , mIsAnyDadInProcess(false) { } @@ -155,7 +170,7 @@ class NdProxyTable : public InstanceLocator, private NonCopyable Error Register(const Ip6::InterfaceIdentifier &aAddressIid, const Ip6::InterfaceIdentifier &aMeshLocalIid, uint16_t aRloc16, - const uint32_t * aTimeSinceLastTransaction); + const uint32_t *aTimeSinceLastTransaction); /** * This method checks if a given IPv6 address IID was registered. @@ -216,7 +231,7 @@ class NdProxyTable : public InstanceLocator, private NonCopyable * @param[in] aContext A user context pointer. * */ - void SetCallback(otBackboneRouterNdProxyCallback aCallback, void *aContext); + void SetCallback(NdProxy::Callback aCallback, void *aContext) { mCallback.Set(aCallback, aContext); } /** * This method retrieves the ND Proxy info of the Domain Unicast Address. @@ -284,21 +299,22 @@ class NdProxyTable : public InstanceLocator, private NonCopyable IteratorBuilder Iterate(Filter aFilter) { return IteratorBuilder(GetInstance(), aFilter); } void Clear(void); static bool MatchesFilter(const NdProxy &aProxy, Filter aFilter); - NdProxy * FindByAddressIid(const Ip6::InterfaceIdentifier &aAddressIid); - NdProxy * FindByMeshLocalIid(const Ip6::InterfaceIdentifier &aMeshLocalIid); - NdProxy * FindInvalid(void); + NdProxy *FindByAddressIid(const Ip6::InterfaceIdentifier &aAddressIid); + NdProxy *FindByMeshLocalIid(const Ip6::InterfaceIdentifier &aMeshLocalIid); + NdProxy *FindInvalid(void); Ip6::Address GetDua(NdProxy &aNdProxy); void NotifyDuaRegistrationOnBackboneLink(NdProxy &aNdProxy, bool aIsRenew); - void TriggerCallback(otBackboneRouterNdProxyEvent aEvent, const Ip6::InterfaceIdentifier &aAddressIid) const; + void TriggerCallback(NdProxy::Event aEvent, const Ip6::InterfaceIdentifier &aAddressIid) const; - NdProxy mProxies[kMaxNdProxyNum]; - otBackboneRouterNdProxyCallback mCallback; - void * mCallbackContext; - bool mIsAnyDadInProcess : 1; + NdProxy mProxies[kMaxNdProxyNum]; + Callback mCallback; + bool mIsAnyDadInProcess : 1; }; } // namespace BackboneRouter +DefineMapEnum(otBackboneRouterNdProxyEvent, BackboneRouter::NdProxyTable::NdProxy::Event); + } // namespace ot #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE diff --git a/src/core/border_router/infra_if.cpp b/src/core/border_router/infra_if.cpp index 7fe20c942d9..acc3049a370 100644 --- a/src/core/border_router/infra_if.cpp +++ b/src/core/border_router/infra_if.cpp @@ -31,6 +31,7 @@ */ #include "infra_if.hpp" +#include "common/num_utils.hpp" #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE @@ -79,14 +80,14 @@ void InfraIf::Deinit(void) LogInfo("Deinit"); } -bool InfraIf::HasAddress(const Ip6::Address &aAddress) +bool InfraIf::HasAddress(const Ip6::Address &aAddress) const { OT_ASSERT(mInitialized); return otPlatInfraIfHasAddress(mIfIndex, &aAddress); } -Error InfraIf::Send(const Icmp6Packet &aPacket, const Ip6::Address &aDestination) +Error InfraIf::Send(const Icmp6Packet &aPacket, const Ip6::Address &aDestination) const { OT_ASSERT(mInitialized); @@ -111,6 +112,33 @@ void InfraIf::HandledReceived(uint32_t aIfIndex, const Ip6::Address &aSource, co } } +Error InfraIf::DiscoverNat64Prefix(void) const +{ + OT_ASSERT(mInitialized); + + return otPlatInfraIfDiscoverNat64Prefix(mIfIndex); +} + +void InfraIf::DiscoverNat64PrefixDone(uint32_t aIfIndex, const Ip6::Prefix &aPrefix) +{ + Error error = kErrorNone; + + OT_UNUSED_VARIABLE(aPrefix); + + VerifyOrExit(mInitialized && mIsRunning, error = kErrorInvalidState); + VerifyOrExit(aIfIndex == mIfIndex, error = kErrorInvalidArgs); + +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + Get().HandleDiscoverNat64PrefixDone(aPrefix); +#endif + +exit: + if (error != kErrorNone) + { + LogDebg("Failed to handle discovered NAT64 synthetic addresses: %s", ErrorToString(error)); + } +} + Error InfraIf::HandleStateChanged(uint32_t aIfIndex, bool aIsRunning) { Error error = kErrorNone; @@ -133,16 +161,16 @@ InfraIf::InfoString InfraIf::ToString(void) const { InfoString string; - string.Append("infra netif %u", mIfIndex); + string.Append("infra netif %lu", ToUlong(mIfIndex)); return string; } //--------------------------------------------------------------------------------------------------------------------- -extern "C" void otPlatInfraIfRecvIcmp6Nd(otInstance * aInstance, +extern "C" void otPlatInfraIfRecvIcmp6Nd(otInstance *aInstance, uint32_t aInfraIfIndex, const otIp6Address *aSrcAddress, - const uint8_t * aBuffer, + const uint8_t *aBuffer, uint16_t aBufferLength) { InfraIf::Icmp6Packet packet; @@ -156,6 +184,13 @@ extern "C" otError otPlatInfraIfStateChanged(otInstance *aInstance, uint32_t aIn return AsCoreType(aInstance).Get().HandleStateChanged(aInfraIfIndex, aIsRunning); } +extern "C" void otPlatInfraIfDiscoverNat64PrefixDone(otInstance *aInstance, + uint32_t aInfraIfIndex, + const otIp6Prefix *aIp6Prefix) +{ + AsCoreType(aInstance).Get().DiscoverNat64PrefixDone(aInfraIfIndex, AsCoreType(aIp6Prefix)); +} + } // namespace BorderRouter } // namespace ot diff --git a/src/core/border_router/infra_if.hpp b/src/core/border_router/infra_if.hpp index b67009e8191..ec4c5480d5e 100644 --- a/src/core/border_router/infra_if.hpp +++ b/src/core/border_router/infra_if.hpp @@ -125,7 +125,7 @@ class InfraIf : public InstanceLocator * @retval FALSE The infrastructure interface does not have @p aAddress. * */ - bool HasAddress(const Ip6::Address &aAddress); + bool HasAddress(const Ip6::Address &aAddress) const; /** * This method sends an ICMPv6 Neighbor Discovery packet on the infrastructure interface. @@ -139,7 +139,7 @@ class InfraIf : public InstanceLocator * @retval kErrorFailed Failed to send the ICMPv6 message. * */ - Error Send(const Icmp6Packet &aPacket, const Ip6::Address &aDestination); + Error Send(const Icmp6Packet &aPacket, const Ip6::Address &aDestination) const; /** * This method processes a received ICMPv6 Neighbor Discovery packet from an infrastructure interface. @@ -151,6 +151,26 @@ class InfraIf : public InstanceLocator */ void HandledReceived(uint32_t aIfIndex, const Ip6::Address &aSource, const Icmp6Packet &aPacket); + /** + * This method sends a request to discover the NAT64 prefix on the infrastructure interface. + * + * @note This method MUST be used when interface is initialized. + * + * @retval kErrorNone Successfully request NAT64 prefix discovery. + * @retval kErrorFailed Failed to request NAT64 prefix discovery. + * + */ + Error DiscoverNat64Prefix(void) const; + + /** + * This method processes the discovered NAT64 prefix. + * + * @param[in] aIfIndex The infrastructure interface index on which the host address is received. + * @param[in] aPrefix The NAT64 prefix on the infrastructure link. + * + */ + void DiscoverNat64PrefixDone(uint32_t aIfIndex, const Ip6::Prefix &aPrefix); + /** * This method handles infrastructure interface state changes. * diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index c7f213ff48f..24669f5bf66 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -38,6 +38,7 @@ #include +#include #include #include "common/code_utils.hpp" @@ -45,10 +46,12 @@ #include "common/instance.hpp" #include "common/locator_getters.hpp" #include "common/log.hpp" +#include "common/num_utils.hpp" #include "common/random.hpp" #include "common/settings.hpp" #include "meshcop/extended_panid.hpp" #include "net/ip6.hpp" +#include "net/nat64_translator.hpp" #include "thread/network_data_leader.hpp" #include "thread/network_data_local.hpp" #include "thread/network_data_notifier.hpp" @@ -65,27 +68,19 @@ RoutingManager::RoutingManager(Instance &aInstance) , mIsEnabled(false) , mInfraIf(aInstance) , mLocalOmrPrefix(aInstance) - , mRouteInfoOptionPreference(NetworkData::kRoutePreferenceMedium) - , mIsAdvertisingLocalOnLinkPrefix(false) - , mOnLinkPrefixDeprecateTimer(aInstance, HandleOnLinkPrefixDeprecateTimer) - , mIsAdvertisingLocalNat64Prefix(false) + , mRioPreference(NetworkData::kRoutePreferenceLow) + , mUserSetRioPreference(false) + , mOnLinkPrefixManager(aInstance) , mDiscoveredPrefixTable(aInstance) - , mTimeRouterAdvMessageLastUpdate(TimerMilli::GetNow()) - , mLearntRouterAdvMessageFromHost(false) - , mDiscoveredPrefixStaleTimer(aInstance, HandleDiscoveredPrefixStaleTimer) - , mRouterAdvertisementCount(0) - , mLastRouterAdvertisementSendTime(TimerMilli::GetNow() - kMinDelayBetweenRtrAdvs) - , mRouterSolicitTimer(aInstance, HandleRouterSolicitTimer) - , mRouterSolicitCount(0) - , mRoutingPolicyTimer(aInstance, HandleRoutingPolicyTimer) + , mRoutePublisher(aInstance) +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + , mNat64PrefixManager(aInstance) +#endif + , mRsSender(aInstance) + , mDiscoveredPrefixStaleTimer(aInstance) + , mRoutingPolicyTimer(aInstance) { - mFavoredDiscoveredOnLinkPrefix.Clear(); - mBrUlaPrefix.Clear(); - - mLocalOnLinkPrefix.Clear(); - - mLocalNat64Prefix.Clear(); } Error RoutingManager::Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning) @@ -96,10 +91,10 @@ Error RoutingManager::Init(uint32_t aInfraIfIndex, bool aInfraIfIsRunning) SuccessOrExit(error = LoadOrGenerateRandomBrUlaPrefix()); mLocalOmrPrefix.GenerateFrom(mBrUlaPrefix); -#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE - GenerateNat64Prefix(); +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + mNat64PrefixManager.GenerateLocalPrefix(mBrUlaPrefix); #endif - GenerateOnLinkPrefix(); + mOnLinkPrefixManager.Init(); error = mInfraIf.HandleStateChanged(mInfraIf.GetIfIndex(), aInfraIfIsRunning); @@ -127,20 +122,60 @@ Error RoutingManager::SetEnabled(bool aEnabled) return error; } +RoutingManager::State RoutingManager::GetState(void) const +{ + State state = kStateUninitialized; + + VerifyOrExit(IsInitialized()); + VerifyOrExit(IsEnabled(), state = kStateDisabled); + + state = IsRunning() ? kStateRunning : kStateStopped; + +exit: + return state; +} + void RoutingManager::SetRouteInfoOptionPreference(RoutePreference aPreference) { - VerifyOrExit(mRouteInfoOptionPreference != aPreference); + LogInfo("User explicitly set RIO Preference to %s", RoutePreferenceToString(aPreference)); + mUserSetRioPreference = true; + UpdateRioPreference(aPreference); +} + +void RoutingManager::ClearRouteInfoOptionPreference(void) +{ + VerifyOrExit(mUserSetRioPreference); + + LogInfo("User cleared explicitly set RIO Preference"); + mUserSetRioPreference = false; + SetRioPreferenceBasedOnRole(); + +exit: + return; +} + +void RoutingManager::SetRioPreferenceBasedOnRole(void) +{ + UpdateRioPreference(Get().IsRouterOrLeader() ? NetworkData::kRoutePreferenceMedium + : NetworkData::kRoutePreferenceLow); +} + +void RoutingManager::UpdateRioPreference(RoutePreference aPreference) +{ + VerifyOrExit(mRioPreference != aPreference); - mRouteInfoOptionPreference = aPreference; + LogInfo("RIO Preference changed: %s -> %s", RoutePreferenceToString(mRioPreference), + RoutePreferenceToString(aPreference)); + mRioPreference = aPreference; VerifyOrExit(mIsRunning); - StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter); + ScheduleRoutingPolicyEvaluation(kAfterRandomDelay); exit: return; } -Error RoutingManager::GetOmrPrefix(Ip6::Prefix &aPrefix) +Error RoutingManager::GetOmrPrefix(Ip6::Prefix &aPrefix) const { Error error = kErrorNone; @@ -151,11 +186,11 @@ Error RoutingManager::GetOmrPrefix(Ip6::Prefix &aPrefix) return error; } -Error RoutingManager::GetFavoredOmrPrefix(Ip6::Prefix &aPrefix, RoutePreference &aPreference) +Error RoutingManager::GetFavoredOmrPrefix(Ip6::Prefix &aPrefix, RoutePreference &aPreference) const { Error error = kErrorNone; - VerifyOrExit(IsInitialized(), error = kErrorInvalidState); + VerifyOrExit(IsRunning(), error = kErrorInvalidState); aPrefix = mFavoredOmrPrefix.GetPrefix(); aPreference = mFavoredOmrPrefix.GetPreference(); @@ -163,24 +198,57 @@ Error RoutingManager::GetFavoredOmrPrefix(Ip6::Prefix &aPrefix, RoutePreference return error; } -Error RoutingManager::GetOnLinkPrefix(Ip6::Prefix &aPrefix) +Error RoutingManager::GetOnLinkPrefix(Ip6::Prefix &aPrefix) const +{ + Error error = kErrorNone; + + VerifyOrExit(IsInitialized(), error = kErrorInvalidState); + aPrefix = mOnLinkPrefixManager.GetLocalPrefix(); + +exit: + return error; +} + +Error RoutingManager::GetFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const { Error error = kErrorNone; VerifyOrExit(IsInitialized(), error = kErrorInvalidState); - aPrefix = mLocalOnLinkPrefix; + aPrefix = mOnLinkPrefixManager.GetFavoredDiscoveredPrefix(); + + if (aPrefix.GetLength() == 0) + { + aPrefix = mOnLinkPrefixManager.GetLocalPrefix(); + } exit: return error; } -#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE +void RoutingManager::SetNat64PrefixManagerEnabled(bool aEnabled) +{ + // PrefixManager will start itself if routing manager is running. + mNat64PrefixManager.SetEnabled(aEnabled); +} + Error RoutingManager::GetNat64Prefix(Ip6::Prefix &aPrefix) { Error error = kErrorNone; VerifyOrExit(IsInitialized(), error = kErrorInvalidState); - aPrefix = mLocalNat64Prefix; + aPrefix = mNat64PrefixManager.GetLocalPrefix(); + +exit: + return error; +} + +Error RoutingManager::GetFavoredNat64Prefix(Ip6::Prefix &aPrefix, RoutePreference &aRoutePreference) +{ + Error error = kErrorNone; + + VerifyOrExit(IsInitialized(), error = kErrorInvalidState); + aPrefix = mNat64PrefixManager.GetFavoredPrefix(aRoutePreference); exit: return error; @@ -220,32 +288,6 @@ Error RoutingManager::LoadOrGenerateRandomBrUlaPrefix(void) return error; } -#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE -void RoutingManager::GenerateNat64Prefix(void) -{ - mLocalNat64Prefix = mBrUlaPrefix; - mLocalNat64Prefix.SetSubnetId(kNat64PrefixSubnetId); - mLocalNat64Prefix.mPrefix.mFields.m32[2] = 0; - mLocalNat64Prefix.SetLength(kNat64PrefixLength); - - LogInfo("Generated NAT64 prefix: %s", mLocalNat64Prefix.ToString().AsCString()); -} -#endif - -void RoutingManager::GenerateOnLinkPrefix(void) -{ - MeshCoP::ExtendedPanId extPanId = Get().GetExtPanId(); - - 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); - - LogNote("Local on-link prefix: %s", mLocalOnLinkPrefix.ToString().AsCString()); -} - void RoutingManager::EvaluateState(void) { if (mIsEnabled && Get().IsAttached() && mInfraIf.IsRunning()) @@ -266,7 +308,13 @@ void RoutingManager::Start(void) mIsRunning = true; UpdateDiscoveredPrefixTableOnNetDataChange(); - StartRouterSolicitationDelay(); + mOnLinkPrefixManager.Start(); + DetermineFavoredOmrPrefix(); + mRoutePublisher.Start(); + mRsSender.Start(); +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + mNat64PrefixManager.Start(); +#endif } } @@ -277,46 +325,60 @@ void RoutingManager::Stop(void) mLocalOmrPrefix.RemoveFromNetData(); mFavoredOmrPrefix.Clear(); - mFavoredDiscoveredOnLinkPrefix.Clear(); - - if (mIsAdvertisingLocalOnLinkPrefix) - { - UnpublishExternalRoute(mLocalOnLinkPrefix); - - // Start deprecating the local on-link prefix to send a PIO - // with zero preferred lifetime in `SendRouterAdvertisement`. - DeprecateOnLinkPrefix(); - } + mOnLinkPrefixManager.Stop(); -#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE - if (mIsAdvertisingLocalNat64Prefix) - { - UnpublishExternalRoute(mLocalNat64Prefix); - mIsAdvertisingLocalNat64Prefix = false; - } +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + mNat64PrefixManager.Stop(); #endif + SendRouterAdvertisement(kInvalidateAllPrevPrefixes); mAdvertisedPrefixes.Clear(); - mOnLinkPrefixDeprecateTimer.Stop(); mDiscoveredPrefixTable.RemoveAllEntries(); mDiscoveredPrefixStaleTimer.Stop(); - mRouterAdvertisementCount = 0; + mRaInfo.mTxCount = 0; - mRouterSolicitTimer.Stop(); - mRouterSolicitCount = 0; + mRsSender.Stop(); mRoutingPolicyTimer.Stop(); + mRoutePublisher.Stop(); + LogInfo("Border Routing manager stopped"); mIsRunning = false; +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE + if (Get().IsAutoEnableMode()) + { + Get().Disable(); + } +#endif + +exit: + return; +} + +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE +void RoutingManager::HandleSrpServerAutoEnableMode(void) +{ + VerifyOrExit(Get().IsAutoEnableMode()); + + if (IsInitalPolicyEvaluationDone()) + { + Get().Enable(); + } + else + { + Get().Disable(); + } + exit: return; } +#endif void RoutingManager::HandleReceived(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress) { @@ -334,6 +396,9 @@ void RoutingManager::HandleReceived(const InfraIf::Icmp6Packet &aPacket, const I case Ip6::Icmp::Header::kTypeRouterSolicit: HandleRouterSolicit(aPacket, aSrcAddress); break; + case Ip6::Icmp::Header::kTypeNeighborAdvert: + HandleNeighborAdvertisement(aPacket); + break; default: break; } @@ -344,6 +409,12 @@ void RoutingManager::HandleReceived(const InfraIf::Icmp6Packet &aPacket, const I void RoutingManager::HandleNotifierEvents(Events aEvents) { + if (aEvents.Contains(kEventThreadRoleChanged) && !mUserSetRioPreference) + { + SetRioPreferenceBasedOnRole(); + mRoutePublisher.HandleRoleChanged(); + } + VerifyOrExit(IsInitialized() && IsEnabled()); if (aEvents.Contains(kEventThreadRoleChanged)) @@ -354,25 +425,13 @@ void RoutingManager::HandleNotifierEvents(Events aEvents) if (mIsRunning && aEvents.Contains(kEventThreadNetdataChanged)) { UpdateDiscoveredPrefixTableOnNetDataChange(); - StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter); + mOnLinkPrefixManager.HandleNetDataChange(); + ScheduleRoutingPolicyEvaluation(kAfterRandomDelay); } if (aEvents.Contains(kEventThreadExtPanIdChanged)) { - if (mIsAdvertisingLocalOnLinkPrefix) - { - UnpublishExternalRoute(mLocalOnLinkPrefix); - // TODO: consider deprecating/invalidating existing - // on-link prefix - mIsAdvertisingLocalOnLinkPrefix = false; - } - - GenerateOnLinkPrefix(); - - if (mIsRunning) - { - StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter); - } + mOnLinkPrefixManager.HandleExtPanIdChange(); } exit: @@ -383,7 +442,6 @@ void RoutingManager::UpdateDiscoveredPrefixTableOnNetDataChange(void) { NetworkData::Iterator iterator = NetworkData::kIteratorInit; NetworkData::OnMeshPrefixConfig prefixConfig; - bool foundDefRouteOmrPrefix = false; // Remove all OMR prefixes in Network Data from the // discovered prefix table. Also check if we have @@ -396,35 +454,17 @@ void RoutingManager::UpdateDiscoveredPrefixTableOnNetDataChange(void) continue; } - mDiscoveredPrefixTable.RemoveRoutePrefix(prefixConfig.GetPrefix(), - DiscoveredPrefixTable::kUnpublishFromNetData); - - if (prefixConfig.mDefaultRoute) - { - foundDefRouteOmrPrefix = true; - } + mDiscoveredPrefixTable.RemoveRoutePrefix(prefixConfig.GetPrefix()); } - - // If we find an OMR prefix with default route flag, it indicates - // that this prefix can be used with default route (routable beyond - // infra link). - // - // `DiscoveredPrefixTable` will always track which routers provide - // default route when processing received RA messages, but only - // if we see an OMR prefix with default route flag, we allow it - // to publish the discovered default route (as ::/0 external - // route) in Network Data. - - mDiscoveredPrefixTable.SetAllowDefaultRouteInNetData(foundDefRouteOmrPrefix); } -void RoutingManager::EvaluateOmrPrefix(void) +void RoutingManager::DetermineFavoredOmrPrefix(void) { + // Determine the favored OMR prefix present in Network Data. + NetworkData::Iterator iterator = NetworkData::kIteratorInit; NetworkData::OnMeshPrefixConfig onMeshPrefixConfig; - OT_ASSERT(mIsRunning); - mFavoredOmrPrefix.Clear(); while (Get().GetNextOnMeshPrefix(iterator, onMeshPrefixConfig) == kErrorNone) @@ -439,6 +479,13 @@ void RoutingManager::EvaluateOmrPrefix(void) mFavoredOmrPrefix.SetFrom(onMeshPrefixConfig); } } +} + +void RoutingManager::EvaluateOmrPrefix(void) +{ + OT_ASSERT(mIsRunning); + + DetermineFavoredOmrPrefix(); // Decide if we need to add or remove our local OMR prefix. @@ -446,7 +493,7 @@ void RoutingManager::EvaluateOmrPrefix(void) { LogInfo("EvaluateOmrPrefix: No preferred OMR prefix found in Thread network"); - // The `aNewPrefixes` remains empty if we fail to publish + // The `mFavoredOmrPrefix` remains empty if we fail to publish // the local OMR prefix. SuccessOrExit(mLocalOmrPrefix.AddToNetData()); @@ -468,375 +515,181 @@ void RoutingManager::EvaluateOmrPrefix(void) return; } -Error RoutingManager::PublishExternalRoute(const Ip6::Prefix &aPrefix, RoutePreference aRoutePreference, bool aNat64) +// This method evaluate the routing policy depends on prefix and route +// information on Thread Network and infra link. As a result, this +// method May send RA messages on infra link and publish/unpublish +// OMR and NAT64 prefix in the Thread network. +void RoutingManager::EvaluateRoutingPolicy(void) { - Error error; - NetworkData::ExternalRouteConfig routeConfig; - OT_ASSERT(mIsRunning); - routeConfig.Clear(); - routeConfig.SetPrefix(aPrefix); - routeConfig.mStable = true; - routeConfig.mNat64 = aNat64; - routeConfig.mPreference = aRoutePreference; + LogInfo("Evaluating routing policy"); - error = Get().PublishExternalRoute(routeConfig); + mOnLinkPrefixManager.Evaluate(); + EvaluateOmrPrefix(); + mRoutePublisher.Evaluate(); +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + mNat64PrefixManager.Evaluate(); +#endif - if (error != kErrorNone) + SendRouterAdvertisement(kAdvPrefixesFromNetData); + +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE + if (Get().IsAutoEnableMode() && IsInitalPolicyEvaluationDone()) { - LogWarn("Failed to publish external route %s: %s", aPrefix.ToString().AsCString(), ErrorToString(error)); + // If SRP server uses the auto-enable mode, we enable the SRP + // server on the first RA transmission after we are done with + // initial prefix/route configurations. Note that if SRP server + // is already enabled, calling `Enable()` again does nothing. + + Get().Enable(); } +#endif - return error; + ScheduleRoutingPolicyEvaluation(kForNextRa); } -void RoutingManager::UnpublishExternalRoute(const Ip6::Prefix &aPrefix) +bool RoutingManager::IsInitalPolicyEvaluationDone(void) const { - VerifyOrExit(mIsRunning); - IgnoreError(Get().UnpublishPrefix(aPrefix)); + // This method indicates whether or not we are done with the + // initial policy evaluation and prefix and route setup, i.e., + // the OMR and on-link prefixes are determined, advertised in + // the emitted Router Advert message on infrastructure side + // and published in the Thread Network Data. -exit: - return; + return mIsRunning && !mFavoredOmrPrefix.IsEmpty() && mOnLinkPrefixManager.IsInitalEvaluationDone(); } -void RoutingManager::EvaluateOnLinkPrefix(void) +void RoutingManager::ScheduleRoutingPolicyEvaluation(ScheduleMode aMode) { - VerifyOrExit(!IsRouterSolicitationInProgress()); - - mDiscoveredPrefixTable.FindFavoredOnLinkPrefix(mFavoredDiscoveredOnLinkPrefix); - - if (mFavoredDiscoveredOnLinkPrefix.GetLength() == 0) - { - // 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. + TimeMilli now = TimerMilli::GetNow(); + uint32_t delay = 0; + TimeMilli evaluateTime; - mDiscoveredPrefixTable.RemoveOnLinkPrefix(mLocalOnLinkPrefix, DiscoveredPrefixTable::kKeepInNetData); - } - else + switch (aMode) { - VerifyOrExit(mIsAdvertisingLocalOnLinkPrefix); + case kImmediately: + break; - // 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. + case kForNextRa: + delay = Random::NonCrypto::GetUint32InRange(Time::SecToMsec(kMinRtrAdvInterval), + Time::SecToMsec(kMaxRtrAdvInterval)); - if (!(mLocalOnLinkPrefix < mFavoredDiscoveredOnLinkPrefix)) + if (mRaInfo.mTxCount <= kMaxInitRtrAdvertisements && delay > Time::SecToMsec(kMaxInitRtrAdvInterval)) { - LogInfo("EvaluateOnLinkPrefix: There is already favored on-link prefix %s on %s", - mFavoredDiscoveredOnLinkPrefix.ToString().AsCString(), mInfraIf.ToString().AsCString()); - DeprecateOnLinkPrefix(); + delay = Time::SecToMsec(kMaxInitRtrAdvInterval); } - } + break; -exit: - return; -} + case kAfterRandomDelay: + delay = Random::NonCrypto::GetUint32InRange(kPolicyEvaluationMinDelay, kPolicyEvaluationMaxDelay); + break; -void RoutingManager::HandleOnLinkPrefixDeprecateTimer(Timer &aTimer) -{ - aTimer.Get().HandleOnLinkPrefixDeprecateTimer(); -} + case kToReplyToRs: + delay = Random::NonCrypto::GetUint32InRange(0, kRaReplyJitter); + break; + } -void RoutingManager::HandleOnLinkPrefixDeprecateTimer(void) -{ - OT_ASSERT(!mIsAdvertisingLocalOnLinkPrefix); + // Ensure we wait a min delay after last RA tx + evaluateTime = Max(now + delay, mRaInfo.mLastTxTime + kMinDelayBetweenRtrAdvs); - LogInfo("Local on-link prefix %s expired", mLocalOnLinkPrefix.ToString().AsCString()); + LogInfo("Start evaluating routing policy, scheduled in %lu milliseconds", ToUlong(evaluateTime - now)); - if (!mDiscoveredPrefixTable.ContainsOnLinkPrefix(mLocalOnLinkPrefix)) - { - UnpublishExternalRoute(mLocalOnLinkPrefix); - } + mRoutingPolicyTimer.FireAtIfEarlier(evaluateTime); } -void RoutingManager::DeprecateOnLinkPrefix(void) +void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode) { - OT_ASSERT(mIsAdvertisingLocalOnLinkPrefix); + // RA message max length is derived to accommodate: + // + // - The RA header. + // - One PIO for current local on-link prefix. + // - At most `kMaxOldPrefixes` for old deprecating on-link prefixes. + // - At most twice `kMaxOnMeshPrefixes` RIO for on-mesh prefixes. + // Factor two is used for RIO to account for entries invalidating + // previous prefixes while adding new ones. - mIsAdvertisingLocalOnLinkPrefix = false; + static constexpr uint16_t kMaxRaLength = + sizeof(Ip6::Nd::RouterAdvertMessage::Header) + sizeof(Ip6::Nd::PrefixInfoOption) + + sizeof(Ip6::Nd::PrefixInfoOption) * OnLinkPrefixManager::kMaxOldPrefixes + + 2 * kMaxOnMeshPrefixes * (sizeof(Ip6::Nd::RouteInfoOption) + sizeof(Ip6::Prefix)); - LogInfo("Deprecate local on-link prefix %s", mLocalOnLinkPrefix.ToString().AsCString()); - mOnLinkPrefixDeprecateTimer.StartAt(mTimeAdvertisedOnLinkPrefix, - TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime)); -} + uint8_t buffer[kMaxRaLength]; + Ip6::Nd::RouterAdvertMessage raMsg(mRaInfo.mHeader, buffer); + NetworkData::Iterator iterator; + NetworkData::OnMeshPrefixConfig prefixConfig; -#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE -void RoutingManager::EvaluateNat64Prefix(void) -{ - OT_ASSERT(mIsRunning); + // Append PIO for local on-link prefix if is either being + // advertised or deprecated and for old prefix if is being + // deprecated. - NetworkData::Iterator iterator = NetworkData::kIteratorInit; - NetworkData::ExternalRouteConfig config; - Ip6::Prefix smallestNat64Prefix; + mOnLinkPrefixManager.AppendAsPiosTo(raMsg); - LogInfo("Evaluating NAT64 prefix"); + // Determine which previously advertised prefixes need to be + // invalidated. Under `kInvalidateAllPrevPrefixes` mode we need + // to invalidate all. Under `kAdvPrefixesFromNetData` mode, we + // check Network Data entries and invalidate any previously + // advertised prefix that is no longer present in the Network + // Data. We go through all Network Data prefixes and mark the + // ones we find in `mAdvertisedPrefixes` as deleted by setting + // the prefix length to zero). By the end, the remaining entries + // in the array with a non-zero prefix length are invalidated. - smallestNat64Prefix.Clear(); - while (Get().GetNextExternalRoute(iterator, config) == kErrorNone) + if (aRaTxMode != kInvalidateAllPrevPrefixes) { - const Ip6::Prefix &prefix = config.GetPrefix(); + iterator = NetworkData::kIteratorInit; - if (config.mNat64 && prefix.IsValidNat64()) + while (Get().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone) { - if (smallestNat64Prefix.GetLength() == 0 || prefix < smallestNat64Prefix) + if (!prefixConfig.mOnMesh || prefixConfig.mDp || (prefixConfig.GetPrefix() == mLocalOmrPrefix.GetPrefix())) { - smallestNat64Prefix = prefix; + continue; } - } - } - if (smallestNat64Prefix.GetLength() == 0 || smallestNat64Prefix == mLocalNat64Prefix) - { - LogInfo("No NAT64 prefix in Network Data is smaller than the local NAT64 prefix %s", - mLocalNat64Prefix.ToString().AsCString()); + mAdvertisedPrefixes.MarkAsDeleted(prefixConfig.GetPrefix()); + } - // Advertise local NAT64 prefix. - if (!mIsAdvertisingLocalNat64Prefix && - PublishExternalRoute(mLocalNat64Prefix, NetworkData::kRoutePreferenceLow, /* aNat64= */ true) == kErrorNone) + if (mLocalOmrPrefix.IsAddedInNetData()) { - mIsAdvertisingLocalNat64Prefix = true; + mAdvertisedPrefixes.MarkAsDeleted(mLocalOmrPrefix.GetPrefix()); } } - else if (mIsAdvertisingLocalNat64Prefix && smallestNat64Prefix < mLocalNat64Prefix) - { - // Withdraw local NAT64 prefix if it's not the smallest one in Network Data. - // TODO: remove the prefix with lower preference after discovering upstream NAT64 prefix is supported - LogNote("Withdrawing local NAT64 prefix since a smaller one %s exists.", - smallestNat64Prefix.ToString().AsCString()); - UnpublishExternalRoute(mLocalNat64Prefix); - mIsAdvertisingLocalNat64Prefix = false; + for (const OnMeshPrefix &prefix : mAdvertisedPrefixes) + { + if (prefix.GetLength() != 0) + { + SuccessOrAssert(raMsg.AppendRouteInfoOption(prefix, /* aRouteLifetime */ 0, mRioPreference)); + LogInfo("RouterAdvert: Added RIO for %s (lifetime=0)", prefix.ToString().AsCString()); + } } -} -#endif - -// This method evaluate the routing policy depends on prefix and route -// information on Thread Network and infra link. As a result, this -// method May send RA messages on infra link and publish/unpublish -// OMR and NAT64 prefix in the Thread network. -void RoutingManager::EvaluateRoutingPolicy(void) -{ - OT_ASSERT(mIsRunning); - - LogInfo("Evaluating routing policy"); - // 0. Evaluate on-link, OMR and NAT64 prefixes. - EvaluateOnLinkPrefix(); - EvaluateOmrPrefix(); -#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE - EvaluateNat64Prefix(); -#endif + // Discover and add prefixes from Network Data to advertise as + // RIO in the Router Advertisement message. - // 1. Send Router Advertisement message if necessary. - SendRouterAdvertisement(kAdvPrefixesFromNetData); + mAdvertisedPrefixes.Clear(); - // 2. Schedule routing policy timer with random interval for the next Router Advertisement. + if (aRaTxMode == kAdvPrefixesFromNetData) { - uint32_t nextSendDelay; + // `mAdvertisedPrefixes` array has a limited size. We add more + // important prefixes first in the array to ensure they are + // advertised in the RA message. Note that `Add()` method + // will ensure to add a prefix only once (will check if + // prefix is already present in the array). - nextSendDelay = Random::NonCrypto::GetUint32InRange(kMinRtrAdvInterval, kMaxRtrAdvInterval); + // (1) Local OMR prefix. - if (mRouterAdvertisementCount <= kMaxInitRtrAdvertisements && nextSendDelay > kMaxInitRtrAdvInterval) + if (mLocalOmrPrefix.IsAddedInNetData()) { - nextSendDelay = kMaxInitRtrAdvInterval; + mAdvertisedPrefixes.Add(mLocalOmrPrefix.GetPrefix()); } - StartRoutingPolicyEvaluationDelay(Time::SecToMsec(nextSendDelay)); - } -} + // (2) Favored OMR prefix. -void RoutingManager::StartRoutingPolicyEvaluationJitter(uint32_t aJitterMilli) -{ - OT_ASSERT(mIsRunning); - - StartRoutingPolicyEvaluationDelay(Random::NonCrypto::GetUint32InRange(0, aJitterMilli)); -} - -void RoutingManager::StartRoutingPolicyEvaluationDelay(uint32_t aDelayMilli) -{ - TimeMilli now = TimerMilli::GetNow(); - TimeMilli evaluateTime = now + aDelayMilli; - TimeMilli earliestTime = mLastRouterAdvertisementSendTime + kMinDelayBetweenRtrAdvs; - - evaluateTime = OT_MAX(evaluateTime, earliestTime); - - LogInfo("Start evaluating routing policy, scheduled in %u milliseconds", evaluateTime - now); - - mRoutingPolicyTimer.FireAtIfEarlier(evaluateTime); -} - -// starts sending Router Solicitations in random delay -// between 0 and kMaxRtrSolicitationDelay. -void RoutingManager::StartRouterSolicitationDelay(void) -{ - uint32_t randomDelay; - - VerifyOrExit(!IsRouterSolicitationInProgress()); - - OT_ASSERT(mRouterSolicitCount == 0); - - static_assert(kMaxRtrSolicitationDelay > 0, "invalid maximum Router Solicitation delay"); - randomDelay = Random::NonCrypto::GetUint32InRange(0, Time::SecToMsec(kMaxRtrSolicitationDelay)); - - LogInfo("Start Router Solicitation, scheduled in %u milliseconds", randomDelay); - mTimeRouterSolicitStart = TimerMilli::GetNow(); - mRouterSolicitTimer.Start(randomDelay); - -exit: - return; -} - -bool RoutingManager::IsRouterSolicitationInProgress(void) const -{ - return mRouterSolicitTimer.IsRunning() || mRouterSolicitCount > 0; -} - -Error RoutingManager::SendRouterSolicitation(void) -{ - Ip6::Address destAddress; - Ip6::Nd::RouterSolicitMessage routerSolicit; - InfraIf::Icmp6Packet packet; - - OT_ASSERT(IsInitialized()); - - packet.InitFrom(routerSolicit); - destAddress.SetToLinkLocalAllRoutersMulticast(); - - return mInfraIf.Send(packet, destAddress); -} - -void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode) -{ - // RA message max length is derived to accommodate: - // - // - The RA header, - // - At most one PIO (for local on-link prefix), - // - At most twice `kMaxOnMeshPrefixes` RIO for on-mesh prefixes. - // Factor two is used for RIO to account for entries invalidating - // previous prefixes while adding new ones. - - static constexpr uint16_t kMaxRaLength = - sizeof(Ip6::Nd::RouterAdvertMessage::Header) + sizeof(Ip6::Nd::PrefixInfoOption) + - 2 * kMaxOnMeshPrefixes * (sizeof(Ip6::Nd::RouteInfoOption) + sizeof(Ip6::Prefix)); - - uint8_t buffer[kMaxRaLength]; - Ip6::Nd::RouterAdvertMessage raMsg(mRouterAdvertHeader, buffer); - NetworkData::Iterator iterator; - NetworkData::OnMeshPrefixConfig prefixConfig; - - // Append PIO for local on-link prefix. Ensure it is either being - // advertised or deprecated. - - if (mIsAdvertisingLocalOnLinkPrefix || mOnLinkPrefixDeprecateTimer.IsRunning()) - { - uint32_t validLifetime = kDefaultOnLinkPrefixLifetime; - uint32_t preferredLifetime = kDefaultOnLinkPrefixLifetime; - - if (mOnLinkPrefixDeprecateTimer.IsRunning()) - { - validLifetime = TimeMilli::MsecToSec(mOnLinkPrefixDeprecateTimer.GetFireTime() - TimerMilli::GetNow()); - preferredLifetime = 0; - } - - SuccessOrAssert(raMsg.AppendPrefixInfoOption(mLocalOnLinkPrefix, validLifetime, preferredLifetime)); - - if (mIsAdvertisingLocalOnLinkPrefix) - { - mTimeAdvertisedOnLinkPrefix = TimerMilli::GetNow(); - } - - LogInfo("RouterAdvert: Added PIO for %s (valid=%u, preferred=%u)", mLocalOnLinkPrefix.ToString().AsCString(), - validLifetime, preferredLifetime); - } - - // Determine which previously advertised prefixes need to be - // invalidated. Under `kInvalidateAllPrevPrefixes` mode we need - // to invalidate all. Under `kAdvPrefixesFromNetData` mode, we - // check Network Data entries and invalidate any previously - // advertised prefix that is no longer present in the Network - // Data. We go through all Network Data prefixes and mark the - // ones we find in `mAdvertisedPrefixes` as deleted by setting - // the prefix length to zero). By the end, the remaining entries - // in the array with a non-zero prefix length are invalidated. - - if (aRaTxMode != kInvalidateAllPrevPrefixes) - { - iterator = NetworkData::kIteratorInit; - - while (Get().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNone) - { - if (!prefixConfig.mOnMesh || prefixConfig.mDp || (prefixConfig.GetPrefix() == mLocalOmrPrefix.GetPrefix())) - { - continue; - } - - mAdvertisedPrefixes.MarkAsDeleted(prefixConfig.GetPrefix()); - } - - if (mLocalOmrPrefix.IsAddedInNetData()) - { - mAdvertisedPrefixes.MarkAsDeleted(mLocalOmrPrefix.GetPrefix()); - } - } - - for (const OnMeshPrefix &prefix : mAdvertisedPrefixes) - { - if (prefix.GetLength() != 0) - { - SuccessOrAssert(raMsg.AppendRouteInfoOption(prefix, /* aRouteLifetime */ 0, mRouteInfoOptionPreference)); - LogInfo("RouterAdvert: Added RIO for %s (lifetime=0)", prefix.ToString().AsCString()); - } - } - - // Discover and add prefixes from Network Data to advertise as - // RIO in the Router Advertisement message. - - mAdvertisedPrefixes.Clear(); - - if (aRaTxMode == kAdvPrefixesFromNetData) - { - // `mAdvertisedPrefixes` array has a limited size. We add more - // important prefixes first in the array to ensure they are - // advertised in the RA message. Note that `Add()` method - // will ensure to add a prefix only once (will check if - // prefix is already present in the array). - - // (1) Local OMR prefix. - - if (mLocalOmrPrefix.IsAddedInNetData()) - { - mAdvertisedPrefixes.Add(mLocalOmrPrefix.GetPrefix()); - } - - // (2) Favored OMR prefix. - - if (!mFavoredOmrPrefix.IsEmpty()) - { - mAdvertisedPrefixes.Add(mFavoredOmrPrefix.GetPrefix()); - } + if (!mFavoredOmrPrefix.IsEmpty() && !mFavoredOmrPrefix.IsDomainPrefix()) + { + mAdvertisedPrefixes.Add(mFavoredOmrPrefix.GetPrefix()); + } // (3) All other OMR prefixes. @@ -855,6 +708,11 @@ void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode) // leader and can take some time to be updated in Network // Data. + if (prefixConfig.mDp) + { + continue; + } + if (IsValidOmrPrefix(prefixConfig) && (prefixConfig.GetPrefix() != mLocalOmrPrefix.GetPrefix())) { mAdvertisedPrefixes.Add(prefixConfig.GetPrefix()); @@ -875,9 +733,9 @@ void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode) for (const OnMeshPrefix &prefix : mAdvertisedPrefixes) { - SuccessOrAssert(raMsg.AppendRouteInfoOption(prefix, kDefaultOmrPrefixLifetime, mRouteInfoOptionPreference)); - LogInfo("RouterAdvert: Added RIO for %s (lifetime=%u)", prefix.ToString().AsCString(), - kDefaultOmrPrefixLifetime); + SuccessOrAssert(raMsg.AppendRouteInfoOption(prefix, kDefaultOmrPrefixLifetime, mRioPreference)); + LogInfo("RouterAdvert: Added RIO for %s (lifetime=%lu)", prefix.ToString().AsCString(), + ToUlong(kDefaultOmrPrefixLifetime)); } } @@ -886,7 +744,7 @@ void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode) Error error; Ip6::Address destAddress; - ++mRouterAdvertisementCount; + ++mRaInfo.mTxCount; destAddress.SetToLinkLocalAllNodesMulticast(); @@ -894,13 +752,15 @@ void RoutingManager::SendRouterAdvertisement(RouterAdvTxMode aRaTxMode) if (error == kErrorNone) { - mLastRouterAdvertisementSendTime = TimerMilli::GetNow(); + mRaInfo.mLastTxTime = TimerMilli::GetNow(); + Get().GetBorderRoutingCounters().mRaTxSuccess++; LogInfo("Sent Router Advertisement on %s", mInfraIf.ToString().AsCString()); DumpDebg("[BR-CERT] direction=send | type=RA |", raMsg.GetAsPacket().GetBytes(), raMsg.GetAsPacket().GetLength()); } else { + Get().GetBorderRoutingCounters().mRaTxFailure++; LogWarn("Failed to send Router Advertisement on %s: %s", mInfraIf.ToString().AsCString(), ErrorToString(error)); } @@ -924,14 +784,19 @@ bool RoutingManager::IsReceivedRouterAdvertFromManager(const Ip6::Nd::RouterAdve { case Ip6::Nd::Option::kTypePrefixInfo: { - // PIO should match `mLocalOnLinkPrefix`. - const Ip6::Nd::PrefixInfoOption &pio = static_cast(option); VerifyOrExit(pio.IsValid()); pio.GetPrefix(prefix); - VerifyOrExit(prefix == mLocalOnLinkPrefix); + // If it is a non-deprecated PIO, it should match the + // local on-link prefix. + + if (pio.GetPreferredLifetime() > 0) + { + VerifyOrExit(prefix == mOnLinkPrefixManager.GetLocalPrefix()); + } + break; } @@ -977,14 +842,13 @@ bool RoutingManager::IsValidBrUlaPrefix(const Ip6::Prefix &aBrUlaPrefix) bool RoutingManager::IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig) { return IsValidOmrPrefix(aOnMeshPrefixConfig.GetPrefix()) && aOnMeshPrefixConfig.mOnMesh && - aOnMeshPrefixConfig.mSlaac && aOnMeshPrefixConfig.mStable && !aOnMeshPrefixConfig.mDp; + aOnMeshPrefixConfig.mSlaac && aOnMeshPrefixConfig.mStable; } -bool RoutingManager::IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix) +bool RoutingManager::IsValidOmrPrefix(const Ip6::Prefix &aPrefix) { - // Accept ULA prefix with length of 64 bits and GUA prefix. - return (aOmrPrefix.mLength == kOmrPrefixLength && aOmrPrefix.mPrefix.mFields.m8[0] == 0xfd) || - (aOmrPrefix.mLength >= 3 && (aOmrPrefix.GetBytes()[0] & 0xE0) == 0x20); + // Accept ULA/GUA prefixes with 64-bit length. + return (aPrefix.GetLength() == kOmrPrefixLength) && !aPrefix.IsLinkLocal() && !aPrefix.IsMulticast(); } bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption &aPio) @@ -1002,77 +866,31 @@ bool RoutingManager::IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix) !aOnLinkPrefix.IsMulticast(); } -void RoutingManager::HandleRouterSolicitTimer(Timer &aTimer) -{ - aTimer.Get().HandleRouterSolicitTimer(); -} - -void RoutingManager::HandleRouterSolicitTimer(void) +void RoutingManager::HandleRsSenderFinished(TimeMilli aStartTime) { - LogInfo("Router solicitation times out"); - - if (mRouterSolicitCount < kMaxRtrSolicitations) - { - uint32_t nextSolicitationDelay; - Error error; - - error = SendRouterSolicitation(); + // This is a callback from `RsSender` and is invoked when it + // finishes a cycle of sending Router Solicitations. `aStartTime` + // specifies the start time of the RS transmission cycle. + // + // We remove or deprecate old entries in discovered table that are + // not refreshed during Router Solicitation. We also invalidate + // the learned RA header if it is not refreshed during Router + // Solicitation. - if (error == kErrorNone) - { - LogDebg("Successfully sent %uth Router Solicitation", mRouterSolicitCount); - ++mRouterSolicitCount; - nextSolicitationDelay = - (mRouterSolicitCount == kMaxRtrSolicitations) ? kMaxRtrSolicitationDelay : kRtrSolicitationInterval; - } - else - { - LogCrit("Failed to send %uth Router Solicitation: %s", mRouterSolicitCount, ErrorToString(error)); - - // It's unexpected that RS will fail and we will retry sending RS messages in 60 seconds. - // Notice that `mRouterSolicitCount` is not incremented for failed RS and thus we will - // not start configuring on-link prefixes before `kMaxRtrSolicitations` successful RS - // messages have been sent. - nextSolicitationDelay = kRtrSolicitationRetryDelay; - mRouterSolicitCount = 0; - } + mDiscoveredPrefixTable.RemoveOrDeprecateOldEntries(aStartTime); - LogDebg("Router solicitation timer scheduled in %u seconds", nextSolicitationDelay); - mRouterSolicitTimer.Start(Time::SecToMsec(nextSolicitationDelay)); - } - else + if (mRaInfo.mHeaderUpdateTime <= aStartTime) { - // 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) - { - UpdateRouterAdvertHeader(/* aRouterAdvertMessage */ nullptr); - } - - mRouterSolicitCount = 0; - - // Re-evaluate our routing policy and send Router Advertisement if necessary. - StartRoutingPolicyEvaluationDelay(/* aDelayJitter */ 0); + UpdateRouterAdvertHeader(/* aRouterAdvertMessage */ nullptr); } -} -void RoutingManager::HandleDiscoveredPrefixStaleTimer(Timer &aTimer) -{ - aTimer.Get().HandleDiscoveredPrefixStaleTimer(); + ScheduleRoutingPolicyEvaluation(kImmediately); } void RoutingManager::HandleDiscoveredPrefixStaleTimer(void) { LogInfo("Stale On-Link or OMR Prefixes or RA messages are detected"); - StartRouterSolicitationDelay(); -} - -void RoutingManager::HandleRoutingPolicyTimer(Timer &aTimer) -{ - aTimer.Get().EvaluateRoutingPolicy(); + mRsSender.Start(); } void RoutingManager::HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress) @@ -1080,11 +898,24 @@ void RoutingManager::HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, co OT_UNUSED_VARIABLE(aPacket); OT_UNUSED_VARIABLE(aSrcAddress); + Get().GetBorderRoutingCounters().mRsRx++; 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); + ScheduleRoutingPolicyEvaluation(kToReplyToRs); +} + +void RoutingManager::HandleNeighborAdvertisement(const InfraIf::Icmp6Packet &aPacket) +{ + const Ip6::Nd::NeighborAdvertMessage *naMsg; + + VerifyOrExit(aPacket.GetLength() >= sizeof(naMsg)); + naMsg = reinterpret_cast(aPacket.GetBytes()); + + mDiscoveredPrefixTable.ProcessNeighborAdvertMessage(*naMsg); + +exit: + return; } void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress) @@ -1095,6 +926,7 @@ void RoutingManager::HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPack VerifyOrExit(routerAdvMessage.IsValid()); + Get().GetBorderRoutingCounters().mRaRx++; 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()); @@ -1127,9 +959,9 @@ bool RoutingManager::ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOpti ExitNow(); } - if (mIsAdvertisingLocalOnLinkPrefix) + if (mOnLinkPrefixManager.IsPublishingOrAdvertising()) { - VerifyOrExit(aPrefix != mLocalOnLinkPrefix); + VerifyOrExit(aPrefix != mOnLinkPrefixManager.GetLocalPrefix()); } shouldProcess = true; @@ -1185,22 +1017,13 @@ bool RoutingManager::ShouldProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption 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. - - Ip6::Prefix newFavoredPrefix; + // there has been a change in the table. VerifyOrExit(mIsRunning); ResetDiscoveredPrefixStaleTimer(); - - mDiscoveredPrefixTable.FindFavoredOnLinkPrefix(newFavoredPrefix); - - if (newFavoredPrefix != mFavoredDiscoveredOnLinkPrefix) - { - StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter); - } + mOnLinkPrefixManager.HandleDiscoveredPrefixTableChanged(); + mRoutePublisher.Evaluate(); exit: return; @@ -1210,23 +1033,45 @@ bool RoutingManager::NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) co { NetworkData::Iterator iterator = NetworkData::kIteratorInit; NetworkData::OnMeshPrefixConfig onMeshPrefixConfig; - bool contain = false; + bool contains = false; while (Get().GetNextOnMeshPrefix(iterator, onMeshPrefixConfig) == kErrorNone) { if (IsValidOmrPrefix(onMeshPrefixConfig) && onMeshPrefixConfig.GetPrefix() == aPrefix) { - contain = true; + contains = true; + break; + } + } + + return contains; +} + +bool RoutingManager::NetworkDataContainsUlaRoute(void) const +{ + // Determine whether leader Network Data contains a route + // prefix which is either the ULA prefix `fc00::/7` or + // a sub-prefix of it (e.g., default route). + + NetworkData::Iterator iterator = NetworkData::kIteratorInit; + NetworkData::ExternalRouteConfig routeConfig; + bool contains = false; + + while (Get().GetNextExternalRoute(iterator, routeConfig) == kErrorNone) + { + if (routeConfig.mStable && RoutePublisher::GetUlaPrefix().ContainsPrefix(routeConfig.GetPrefix())) + { + contains = true; break; } } - return contain; + return contains; } void RoutingManager::UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage) { - // Updates the `mRouterAdvertHeader` from the given RA message. + // Updates the `mRaInfo` from the given RA message. Ip6::Nd::RouterAdvertMessage::Header oldHeader; @@ -1238,34 +1083,34 @@ void RoutingManager::UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage VerifyOrExit(!IsReceivedRouterAdvertFromManager(*aRouterAdvertMessage)); } - oldHeader = mRouterAdvertHeader; - mTimeRouterAdvMessageLastUpdate = TimerMilli::GetNow(); + oldHeader = mRaInfo.mHeader; + mRaInfo.mHeaderUpdateTime = TimerMilli::GetNow(); if (aRouterAdvertMessage == nullptr || aRouterAdvertMessage->GetHeader().GetRouterLifetime() == 0) { - mRouterAdvertHeader.SetToDefault(); - mLearntRouterAdvMessageFromHost = false; + mRaInfo.mHeader.SetToDefault(); + mRaInfo.mIsHeaderFromHost = false; } else { - // The checksum is set to zero in `mRouterAdvertHeader` + // The checksum is set to zero in `mRaInfo.mHeader` // which indicates to platform that it needs to do the // calculation and update it. - mRouterAdvertHeader = aRouterAdvertMessage->GetHeader(); - mRouterAdvertHeader.SetChecksum(0); - mLearntRouterAdvMessageFromHost = true; + mRaInfo.mHeader = aRouterAdvertMessage->GetHeader(); + mRaInfo.mHeader.SetChecksum(0); + mRaInfo.mIsHeaderFromHost = true; } ResetDiscoveredPrefixStaleTimer(); - if (mRouterAdvertHeader != oldHeader) + if (mRaInfo.mHeader != oldHeader) { // If there was a change to the header, start timer to // reevaluate routing policy and send RA message with new // header. - StartRoutingPolicyEvaluationJitter(kRoutingPolicyEvaluationJitter); + ScheduleRoutingPolicyEvaluation(kAfterRandomDelay); } exit: @@ -1285,11 +1130,11 @@ void RoutingManager::ResetDiscoveredPrefixStaleTimer(void) nextStaleTime = mDiscoveredPrefixTable.CalculateNextStaleTime(now); // Check for stale Router Advertisement Message if learnt from Host. - if (mLearntRouterAdvMessageFromHost) + if (mRaInfo.mIsHeaderFromHost) { - TimeMilli raStaleTime = OT_MAX(now, mTimeRouterAdvMessageLastUpdate + Time::SecToMsec(kRtrAdvStaleTime)); + TimeMilli raStaleTime = Max(now, mRaInfo.mHeaderUpdateTime + Time::SecToMsec(kRtrAdvStaleTime)); - nextStaleTime = OT_MIN(nextStaleTime, raStaleTime); + nextStaleTime = Min(nextStaleTime, raStaleTime); } if (nextStaleTime == now.GetDistantFuture()) @@ -1304,7 +1149,7 @@ void RoutingManager::ResetDiscoveredPrefixStaleTimer(void) else { mDiscoveredPrefixStaleTimer.FireAt(nextStaleTime); - LogDebg("Prefix stale timer scheduled in %lu ms", nextStaleTime - now); + LogDebg("Prefix stale timer scheduled in %lu ms", ToUlong(nextStaleTime - now)); } } @@ -1313,14 +1158,14 @@ void RoutingManager::ResetDiscoveredPrefixStaleTimer(void) RoutingManager::DiscoveredPrefixTable::DiscoveredPrefixTable(Instance &aInstance) : InstanceLocator(aInstance) - , mTimer(aInstance, HandleTimer) - , mSignalTask(aInstance, HandleSignalTask) - , mAllowDefaultRouteInNetData(false) + , mEntryTimer(aInstance) + , mRouterTimer(aInstance) + , mSignalTask(aInstance) { } void RoutingManager::DiscoveredPrefixTable::ProcessRouterAdvertMessage(const Ip6::Nd::RouterAdvertMessage &aRaMessage, - const Ip6::Address & aSrcAddress) + const Ip6::Address &aSrcAddress) { // Process a received RA message and update the prefix table. @@ -1365,6 +1210,8 @@ void RoutingManager::DiscoveredPrefixTable::ProcessRouterAdvertMessage(const Ip6 } } + UpdateRouterOnRx(*router); + RemoveRoutersWithNoEntries(); exit: @@ -1372,9 +1219,9 @@ void RoutingManager::DiscoveredPrefixTable::ProcessRouterAdvertMessage(const Ip6 } void RoutingManager::DiscoveredPrefixTable::ProcessDefaultRoute(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader, - Router & aRouter) + Router &aRouter) { - Entry * entry; + Entry *entry; Ip6::Prefix prefix; prefix.Clear(); @@ -1400,8 +1247,8 @@ void RoutingManager::DiscoveredPrefixTable::ProcessDefaultRoute(const Ip6::Nd::R entry->SetFrom(aRaHeader); } - UpdateNetworkDataOnChangeTo(*entry); - mTimer.FireAtIfEarlier(entry->GetExpireTime()); + mEntryTimer.FireAtIfEarlier(entry->GetExpireTime()); + SignalTableChanged(); exit: @@ -1409,17 +1256,17 @@ void RoutingManager::DiscoveredPrefixTable::ProcessDefaultRoute(const Ip6::Nd::R } void RoutingManager::DiscoveredPrefixTable::ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, - Router & aRouter) + Router &aRouter) { Ip6::Prefix prefix; - Entry * entry; + Entry *entry; VerifyOrExit(aPio.IsValid()); aPio.GetPrefix(prefix); VerifyOrExit(Get().ShouldProcessPrefixInfoOption(aPio, prefix)); - LogInfo("Processing PIO (%s, %u seconds)", prefix.ToString().AsCString(), aPio.GetValidLifetime()); + LogInfo("Processing PIO (%s, %lu seconds)", prefix.ToString().AsCString(), ToUlong(aPio.GetValidLifetime())); entry = aRouter.mEntries.FindMatching(Entry::Matcher(prefix, Entry::kTypeOnLink)); @@ -1443,11 +1290,11 @@ void RoutingManager::DiscoveredPrefixTable::ProcessPrefixInfoOption(const Ip6::N Entry newEntry; newEntry.SetFrom(aPio); - entry->AdoptValidAndPreferredLiftimesFrom(newEntry); + entry->AdoptValidAndPreferredLifetimesFrom(newEntry); } - UpdateNetworkDataOnChangeTo(*entry); - mTimer.FireAtIfEarlier(entry->GetExpireTime()); + mEntryTimer.FireAtIfEarlier(entry->GetExpireTime()); + SignalTableChanged(); exit: @@ -1455,17 +1302,17 @@ void RoutingManager::DiscoveredPrefixTable::ProcessPrefixInfoOption(const Ip6::N } void RoutingManager::DiscoveredPrefixTable::ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, - Router & aRouter) + Router &aRouter) { Ip6::Prefix prefix; - Entry * entry; + Entry *entry; VerifyOrExit(aRio.IsValid()); aRio.GetPrefix(prefix); VerifyOrExit(Get().ShouldProcessRouteInfoOption(aRio, prefix)); - LogInfo("Processing RIO (%s, %u seconds)", prefix.ToString().AsCString(), aRio.GetRouteLifetime()); + LogInfo("Processing RIO (%s, %lu seconds)", prefix.ToString().AsCString(), ToUlong(aRio.GetRouteLifetime())); entry = aRouter.mEntries.FindMatching(Entry::Matcher(prefix, Entry::kTypeRoute)); @@ -1489,40 +1336,43 @@ void RoutingManager::DiscoveredPrefixTable::ProcessRouteInfoOption(const Ip6::Nd entry->SetFrom(aRio); } - UpdateNetworkDataOnChangeTo(*entry); - mTimer.FireAtIfEarlier(entry->GetExpireTime()); + mEntryTimer.FireAtIfEarlier(entry->GetExpireTime()); + SignalTableChanged(); exit: return; } -void RoutingManager::DiscoveredPrefixTable::SetAllowDefaultRouteInNetData(bool aAllow) +bool RoutingManager::DiscoveredPrefixTable::Contains(const Entry::Checker &aChecker) const { - Entry * favoredEntry; - Ip6::Prefix prefix; - - VerifyOrExit(aAllow != mAllowDefaultRouteInNetData); + bool contains = false; - LogInfo("Allow default route in netdata: %s -> %s", ToYesNo(mAllowDefaultRouteInNetData), ToYesNo(aAllow)); + for (const Router &router : mRouters) + { + if (router.mEntries.ContainsMatching(aChecker)) + { + contains = true; + break; + } + } - mAllowDefaultRouteInNetData = aAllow; + return contains; +} - prefix.Clear(); - favoredEntry = FindFavoredEntryToPublish(prefix); - VerifyOrExit(favoredEntry != nullptr); +bool RoutingManager::DiscoveredPrefixTable::ContainsDefaultOrNonUlaRoutePrefix(void) const +{ + return Contains(Entry::Checker(Entry::Checker::kIsNotUla, Entry::kTypeRoute)); +} - if (mAllowDefaultRouteInNetData) - { - PublishEntry(*favoredEntry); - } - else - { - UnpublishEntry(*favoredEntry); - } +bool RoutingManager::DiscoveredPrefixTable::ContainsNonUlaOnLinkPrefix(void) const +{ + return Contains(Entry::Checker(Entry::Checker::kIsNotUla, Entry::kTypeOnLink)); +} -exit: - return; +bool RoutingManager::DiscoveredPrefixTable::ContainsUlaOnLinkPrefix(void) const +{ + return Contains(Entry::Checker(Entry::Checker::kIsUla, Entry::kTypeOnLink)); } void RoutingManager::DiscoveredPrefixTable::FindFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const @@ -1550,48 +1400,19 @@ void RoutingManager::DiscoveredPrefixTable::FindFavoredOnLinkPrefix(Ip6::Prefix } } -bool RoutingManager::DiscoveredPrefixTable::ContainsOnLinkPrefix(const Ip6::Prefix &aPrefix) const -{ - return ContainsPrefix(Entry::Matcher(aPrefix, Entry::kTypeOnLink)); -} - -bool RoutingManager::DiscoveredPrefixTable::ContainsRoutePrefix(const Ip6::Prefix &aPrefix) const -{ - return ContainsPrefix(Entry::Matcher(aPrefix, Entry::kTypeRoute)); -} - -bool RoutingManager::DiscoveredPrefixTable::ContainsPrefix(const Entry::Matcher &aMatcher) const -{ - bool contains = false; - - for (const Router &router : mRouters) - { - if (router.mEntries.ContainsMatching(aMatcher)) - { - contains = true; - break; - } - } - - return contains; -} - -void RoutingManager::DiscoveredPrefixTable::RemoveOnLinkPrefix(const Ip6::Prefix &aPrefix, NetDataMode aNetDataMode) +void RoutingManager::DiscoveredPrefixTable::RemoveOnLinkPrefix(const Ip6::Prefix &aPrefix) { - RemovePrefix(Entry::Matcher(aPrefix, Entry::kTypeOnLink), aNetDataMode); + RemovePrefix(Entry::Matcher(aPrefix, Entry::kTypeOnLink)); } -void RoutingManager::DiscoveredPrefixTable::RemoveRoutePrefix(const Ip6::Prefix &aPrefix, NetDataMode aNetDataMode) +void RoutingManager::DiscoveredPrefixTable::RemoveRoutePrefix(const Ip6::Prefix &aPrefix) { - RemovePrefix(Entry::Matcher(aPrefix, Entry::kTypeRoute), aNetDataMode); + RemovePrefix(Entry::Matcher(aPrefix, Entry::kTypeRoute)); } -void RoutingManager::DiscoveredPrefixTable::RemovePrefix(const Entry::Matcher &aMatcher, NetDataMode aNetDataMode) +void RoutingManager::DiscoveredPrefixTable::RemovePrefix(const Entry::Matcher &aMatcher) { // Removes all entries matching a given prefix from the table. - // `aNetDataMode` specifies behavior when a match is found and - // removed. It indicates whether or not to unpublish it from - // Network Data. LinkedList removedEntries; @@ -1602,11 +1423,6 @@ void RoutingManager::DiscoveredPrefixTable::RemovePrefix(const Entry::Matcher &a VerifyOrExit(!removedEntries.IsEmpty()); - if (aNetDataMode == kUnpublishFromNetData) - { - UnpublishEntry(*removedEntries.GetHead()); - } - FreeEntries(removedEntries); RemoveRoutersWithNoEntries(); @@ -1627,14 +1443,13 @@ void RoutingManager::DiscoveredPrefixTable::RemoveAllEntries(void) while ((entry = router.mEntries.Pop()) != nullptr) { - UnpublishEntry(*entry); FreeEntry(*entry); SignalTableChanged(); } } RemoveRoutersWithNoEntries(); - mTimer.Stop(); + mEntryTimer.Stop(); } void RoutingManager::DiscoveredPrefixTable::RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold) @@ -1665,36 +1480,66 @@ void RoutingManager::DiscoveredPrefixTable::RemoveOrDeprecateOldEntries(TimeMill RemoveExpiredEntries(); } -TimeMilli RoutingManager::DiscoveredPrefixTable::CalculateNextStaleTime(TimeMilli aNow) const +void RoutingManager::DiscoveredPrefixTable::RemoveOrDeprecateEntriesFromInactiveRouters(void) { - 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. + // Remove route prefix entries and deprecate on-link prefix entries + // in the table for routers that have reached the max NS probe + // attempts and considered as inactive. - for (const Router &router : mRouters) + for (Router &router : mRouters) { - for (const Entry &entry : router.mEntries) + if (router.mNsProbeCount <= Router::kMaxNsProbes) { - TimeMilli entryStaleTime = OT_MAX(aNow, entry.GetStaleTime()); + continue; + } + + for (Entry &entry : router.mEntries) + { + if (entry.IsOnLinkPrefix() && !entry.IsDeprecated()) + { + entry.ClearPreferredLifetime(); + SignalTableChanged(); + } + else + { + entry.ClearValidLifetime(); + } + } + } + + 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 Router &router : mRouters) + { + for (const Entry &entry : router.mEntries) + { + TimeMilli entryStaleTime = Max(aNow, entry.GetStaleTime()); if (entry.IsOnLinkPrefix() && !entry.IsDeprecated()) { - onLinkStaleTime = OT_MAX(onLinkStaleTime, entryStaleTime); + onLinkStaleTime = Max(onLinkStaleTime, entryStaleTime); foundOnLink = true; } if (!entry.IsOnLinkPrefix()) { - routeStaleTime = OT_MIN(routeStaleTime, entryStaleTime); + routeStaleTime = Min(routeStaleTime, entryStaleTime); } } } - return foundOnLink ? OT_MIN(onLinkStaleTime, routeStaleTime) : routeStaleTime; + return foundOnLink ? Min(onLinkStaleTime, routeStaleTime) : routeStaleTime; } void RoutingManager::DiscoveredPrefixTable::RemoveRoutersWithNoEntries(void) @@ -1715,8 +1560,8 @@ void RoutingManager::DiscoveredPrefixTable::FreeEntries(LinkedList &aEntr } } -RoutingManager::DiscoveredPrefixTable::Entry *RoutingManager::DiscoveredPrefixTable::FindFavoredEntryToPublish( - const Ip6::Prefix &aPrefix) +const RoutingManager::DiscoveredPrefixTable::Entry *RoutingManager::DiscoveredPrefixTable::FindFavoredEntryToPublish( + const Ip6::Prefix &aPrefix) const { // Finds the favored entry matching a given `aPrefix` in the table // to publish in the Network Data. We can have multiple entries @@ -1725,11 +1570,11 @@ RoutingManager::DiscoveredPrefixTable::Entry *RoutingManager::DiscoveredPrefixTa // select the one with the highest preference as the favored // entry to publish. - Entry *favoredEntry = nullptr; + const Entry *favoredEntry = nullptr; - for (Router &router : mRouters) + for (const Router &router : mRouters) { - for (Entry &entry : router.mEntries) + for (const Entry &entry : router.mEntries) { if (entry.GetPrefix() != aPrefix) { @@ -1746,50 +1591,7 @@ RoutingManager::DiscoveredPrefixTable::Entry *RoutingManager::DiscoveredPrefixTa return favoredEntry; } -void RoutingManager::DiscoveredPrefixTable::UpdateNetworkDataOnChangeTo(Entry &aEntry) -{ - // Updates Network Data when there is a change to `aEntry` which - // can be a newly added entry or an existing entry that is - // modified due to processing of a received RA message. - - Entry *favoredEntry; - - if (aEntry.GetPrefix().GetLength() == 0) - { - // If the change is to default route ::/0 prefix, make sure we - // are allowed to publish default route in Network Data. - - VerifyOrExit(mAllowDefaultRouteInNetData); - } - - favoredEntry = FindFavoredEntryToPublish(aEntry.GetPrefix()); - - OT_ASSERT(favoredEntry != nullptr); - PublishEntry(*favoredEntry); - -exit: - return; -} - -void RoutingManager::DiscoveredPrefixTable::PublishEntry(const Entry &aEntry) -{ - IgnoreError(Get().PublishExternalRoute(aEntry.GetPrefix(), aEntry.GetPreference())); -} - -void RoutingManager::DiscoveredPrefixTable::UnpublishEntry(const Entry &aEntry) -{ - Get().UnpublishExternalRoute(aEntry.GetPrefix()); -} - -void RoutingManager::DiscoveredPrefixTable::HandleTimer(Timer &aTimer) -{ - aTimer.Get().mDiscoveredPrefixTable.HandleTimer(); -} - -void RoutingManager::DiscoveredPrefixTable::HandleTimer(void) -{ - RemoveExpiredEntries(); -} +void RoutingManager::DiscoveredPrefixTable::HandleEntryTimer(void) { RemoveExpiredEntries(); } void RoutingManager::DiscoveredPrefixTable::RemoveExpiredEntries(void) { @@ -1804,23 +1606,6 @@ void RoutingManager::DiscoveredPrefixTable::RemoveExpiredEntries(void) RemoveRoutersWithNoEntries(); - // Determine if we need to publish/unpublish any prefixes in - // the Network Data. - - for (const Entry &expiredEntry : expiredEntries) - { - Entry *favoredEntry = FindFavoredEntryToPublish(expiredEntry.GetPrefix()); - - if (favoredEntry == nullptr) - { - UnpublishEntry(expiredEntry); - } - else - { - PublishEntry(*favoredEntry); - } - } - if (!expiredEntries.IsEmpty()) { SignalTableChanged(); @@ -1834,24 +1619,113 @@ void RoutingManager::DiscoveredPrefixTable::RemoveExpiredEntries(void) { for (const Entry &entry : router.mEntries) { - nextExpireTime = OT_MIN(nextExpireTime, entry.GetExpireTime()); + nextExpireTime = Min(nextExpireTime, entry.GetExpireTime()); } } if (nextExpireTime != now.GetDistantFuture()) { - mTimer.FireAt(nextExpireTime); + mEntryTimer.FireAt(nextExpireTime); } } -void RoutingManager::DiscoveredPrefixTable::SignalTableChanged(void) +void RoutingManager::DiscoveredPrefixTable::SignalTableChanged(void) { mSignalTask.Post(); } + +void RoutingManager::DiscoveredPrefixTable::ProcessNeighborAdvertMessage( + const Ip6::Nd::NeighborAdvertMessage &aNaMessage) +{ + Router *router; + + VerifyOrExit(aNaMessage.IsValid()); + + router = mRouters.FindMatching(aNaMessage.GetTargetAddress()); + VerifyOrExit(router != nullptr); + + LogInfo("Received NA from router %s", router->mAddress.ToString().AsCString()); + + UpdateRouterOnRx(*router); + +exit: + return; +} + +void RoutingManager::DiscoveredPrefixTable::UpdateRouterOnRx(Router &aRouter) +{ + aRouter.mNsProbeCount = 0; + aRouter.mTimeout = TimerMilli::GetNow() + Random::NonCrypto::AddJitter(Router::kActiveTimeout, Router::kJitter); + + mRouterTimer.FireAtIfEarlier(aRouter.mTimeout); +} + +void RoutingManager::DiscoveredPrefixTable::HandleRouterTimer(void) { - mSignalTask.Post(); + TimeMilli now = TimerMilli::GetNow(); + TimeMilli nextTime = now.GetDistantFuture(); + + for (Router &router : mRouters) + { + if (router.mNsProbeCount > Router::kMaxNsProbes) + { + continue; + } + + // If the `router` emitting RA has an address belonging to + // infra interface, it indicates that the RAs are from + // same device. In this case we skip performing NS probes. + // This addresses situation where platform may not be + // be able to receive and pass the NA message response + // from device itself. + + if (Get().mInfraIf.HasAddress(router.mAddress)) + { + continue; + } + + if (router.mTimeout <= now) + { + router.mNsProbeCount++; + + if (router.mNsProbeCount > Router::kMaxNsProbes) + { + LogInfo("No response to all Neighbor Solicitations attempts from router %s", + router.mAddress.ToString().AsCString()); + continue; + } + + router.mTimeout = now + ((router.mNsProbeCount < Router::kMaxNsProbes) ? Router::kNsProbeRetryInterval + : Router::kNsProbeTimeout); + + SendNeighborSolicitToRouter(router); + } + + nextTime = Min(nextTime, router.mTimeout); + } + + RemoveOrDeprecateEntriesFromInactiveRouters(); + + if (nextTime != now.GetDistantFuture()) + { + mRouterTimer.FireAtIfEarlier(nextTime); + } } -void RoutingManager::DiscoveredPrefixTable::HandleSignalTask(Tasklet &aTasklet) +void RoutingManager::DiscoveredPrefixTable::SendNeighborSolicitToRouter(const Router &aRouter) { - aTasklet.Get().HandleDiscoveredPrefixTableChanged(); + InfraIf::Icmp6Packet packet; + Ip6::Nd::NeighborSolicitMessage neighborSolicitMsg; + + VerifyOrExit(!Get().mRsSender.IsInProgress()); + + neighborSolicitMsg.SetTargetAddress(aRouter.mAddress); + packet.InitFrom(neighborSolicitMsg); + + IgnoreError(Get().mInfraIf.Send(packet, aRouter.mAddress)); + + LogInfo("Sent Neighbor Solicitation to %s - attempt:%u/%u", aRouter.mAddress.ToString().AsCString(), + aRouter.mNsProbeCount, Router::kMaxNsProbes); + +exit: + return; } void RoutingManager::DiscoveredPrefixTable::InitIterator(PrefixTableIterator &aIterator) const @@ -1864,7 +1738,7 @@ void RoutingManager::DiscoveredPrefixTable::InitIterator(PrefixTableIterator &aI } Error RoutingManager::DiscoveredPrefixTable::GetNextEntry(PrefixTableIterator &aIterator, - PrefixTableEntry & aEntry) const + PrefixTableEntry &aEntry) const { Error error = kErrorNone; Iterator &iterator = static_cast(aIterator); @@ -1941,9 +1815,14 @@ bool RoutingManager::DiscoveredPrefixTable::Entry::Matches(const Matcher &aMatch return (mType == aMatcher.mType) && (mPrefix == aMatcher.mPrefix); } -bool RoutingManager::DiscoveredPrefixTable::Entry::Matches(const ExpirationChecker &aCheker) const +bool RoutingManager::DiscoveredPrefixTable::Entry::Matches(const Checker &aChecker) const +{ + return (mType == aChecker.mType) && (mPrefix.IsUniqueLocal() == (aChecker.mMode == Checker::kIsUla)); +} + +bool RoutingManager::DiscoveredPrefixTable::Entry::Matches(const ExpirationChecker &aChecker) const { - return GetExpireTime() <= aCheker.mNow; + return GetExpireTime() <= aChecker.mNow; } TimeMilli RoutingManager::DiscoveredPrefixTable::Entry::GetExpireTime(void) const @@ -1953,7 +1832,7 @@ TimeMilli RoutingManager::DiscoveredPrefixTable::Entry::GetExpireTime(void) cons TimeMilli RoutingManager::DiscoveredPrefixTable::Entry::GetStaleTime(void) const { - uint32_t delay = OT_MIN(kRtrAdvStaleTime, IsOnLinkPrefix() ? GetPreferredLifetime() : mValidLifetime); + uint32_t delay = Min(kRtrAdvStaleTime, IsOnLinkPrefix() ? GetPreferredLifetime() : mValidLifetime); return mLastUpdateTime + TimeMilli::SecToMsec(delay); } @@ -1973,7 +1852,7 @@ RoutingManager::RoutePreference RoutingManager::DiscoveredPrefixTable::Entry::Ge return IsOnLinkPrefix() ? NetworkData::kRoutePreferenceMedium : GetRoutePreference(); } -void RoutingManager::DiscoveredPrefixTable::Entry::AdoptValidAndPreferredLiftimesFrom(const Entry &aEntry) +void RoutingManager::DiscoveredPrefixTable::Entry::AdoptValidAndPreferredLifetimesFrom(const Entry &aEntry) { constexpr uint32_t kTwoHoursInSeconds = 2 * 3600; @@ -2020,16 +1899,27 @@ uint32_t RoutingManager::DiscoveredPrefixTable::Entry::CalculateExpireDelay(uint //--------------------------------------------------------------------------------------------------------------------- // OmrPrefix +bool RoutingManager::OmrPrefix::IsInfrastructureDerived(void) const +{ + // Indicate whether the OMR prefix is infrastructure-derived which + // can be identified as a valid OMR prefix with preference of + // medium or higher. + + return !IsEmpty() && (mPreference >= NetworkData::kRoutePreferenceMedium); +} + void RoutingManager::OmrPrefix::SetFrom(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig) { - mPrefix = aOnMeshPrefixConfig.GetPrefix(); - mPreference = aOnMeshPrefixConfig.GetPreference(); + mPrefix = aOnMeshPrefixConfig.GetPrefix(); + mPreference = aOnMeshPrefixConfig.GetPreference(); + mIsDomainPrefix = aOnMeshPrefixConfig.mDp; } void RoutingManager::OmrPrefix::SetFrom(const LocalOmrPrefix &aLocalOmrPrefix) { - mPrefix = aLocalOmrPrefix.GetPrefix(); - mPreference = aLocalOmrPrefix.GetPreference(); + mPrefix = aLocalOmrPrefix.GetPrefix(); + mPreference = aLocalOmrPrefix.GetPreference(); + mIsDomainPrefix = false; } bool RoutingManager::OmrPrefix::IsFavoredOver(const NetworkData::OnMeshPrefixConfig &aOmrPrefixConfig) const @@ -2057,6 +1947,7 @@ bool RoutingManager::OmrPrefix::IsFavoredOver(const NetworkData::OnMeshPrefixCon RoutingManager::LocalOmrPrefix::LocalOmrPrefix(Instance &aInstance) : InstanceLocator(aInstance) , mIsAddedInNetData(false) + , mDefaultRoute(false) { } @@ -2071,10 +1962,23 @@ void RoutingManager::LocalOmrPrefix::GenerateFrom(const Ip6::Prefix &aBrUlaPrefi Error RoutingManager::LocalOmrPrefix::AddToNetData(void) { - Error error = kErrorNone; - NetworkData::OnMeshPrefixConfig config; + Error error = kErrorNone; VerifyOrExit(!mIsAddedInNetData); + SuccessOrExit(error = AddOrUpdate()); + mIsAddedInNetData = true; + +exit: + return error; +} + +Error RoutingManager::LocalOmrPrefix::AddOrUpdate(void) +{ + // Add the local OMR prefix in Thread Network Data or update it + // (e.g., change default route flag) if it is already added. + + Error error; + NetworkData::OnMeshPrefixConfig config; config.Clear(); config.mPrefix = mPrefix; @@ -2082,21 +1986,21 @@ Error RoutingManager::LocalOmrPrefix::AddToNetData(void) config.mSlaac = true; config.mPreferred = true; config.mOnMesh = true; - config.mDefaultRoute = false; + config.mDefaultRoute = mDefaultRoute; config.mPreference = GetPreference(); error = Get().AddOnMeshPrefix(config); if (error != kErrorNone) { - LogWarn("Failed to add local OMR prefix %s in Thread Network Data: %s", mPrefix.ToString().AsCString(), - ErrorToString(error)); + LogWarn("Failed to %s %s in Thread Network Data: %s", !mIsAddedInNetData ? "add" : "update", + ToString().AsCString(), ErrorToString(error)); ExitNow(); } - mIsAddedInNetData = true; Get().HandleServerDataUpdated(); - LogInfo("Added local OMR prefix %s in Thread Network Data", mPrefix.ToString().AsCString()); + + LogInfo("%s %s in Thread Network Data", !mIsAddedInNetData ? "Added" : "Updated", ToString().AsCString()); exit: return error; @@ -2112,53 +2016,1087 @@ void RoutingManager::LocalOmrPrefix::RemoveFromNetData(void) if (error != kErrorNone) { - LogWarn("Failed to remove local OMR prefix %s from Thread Network Data: %s", mPrefix.ToString().AsCString(), - ErrorToString(error)); + LogWarn("Failed to remove %s from Thread Network Data: %s", ToString().AsCString(), ErrorToString(error)); ExitNow(); } mIsAddedInNetData = false; Get().HandleServerDataUpdated(); - LogInfo("Removed local OMR prefix %s from Thread Network Data", mPrefix.ToString().AsCString()); + LogInfo("Removed %s from Thread Network Data", ToString().AsCString()); + +exit: + return; +} + +void RoutingManager::LocalOmrPrefix::UpdateDefaultRouteFlag(bool aDefaultRoute) +{ + VerifyOrExit(aDefaultRoute != mDefaultRoute); + + mDefaultRoute = aDefaultRoute; + + VerifyOrExit(mIsAddedInNetData); + IgnoreError(AddOrUpdate()); exit: return; } +RoutingManager::LocalOmrPrefix::InfoString RoutingManager::LocalOmrPrefix::ToString(void) const +{ + InfoString string; + + string.Append("local OMR prefix %s (def-route:%s)", mPrefix.ToString().AsCString(), ToYesNo(mDefaultRoute)); + return string; +} + //--------------------------------------------------------------------------------------------------------------------- -// OnMeshPrefixArray +// OnLinkPrefixManager -void RoutingManager::OnMeshPrefixArray::Add(const OnMeshPrefix &aPrefix) +RoutingManager::OnLinkPrefixManager::OnLinkPrefixManager(Instance &aInstance) + : InstanceLocator(aInstance) + , mState(kIdle) + , mTimer(aInstance) { - // Checks if `aPrefix` is already present in the array and if not - // adds it as new entry. + mLocalPrefix.Clear(); + mFavoredDiscoveredPrefix.Clear(); + mOldLocalPrefixes.Clear(); +} - Error error; +void RoutingManager::OnLinkPrefixManager::Init(void) +{ + TimeMilli now = TimerMilli::GetNow(); + Settings::BrOnLinkPrefix savedPrefix; + bool refreshStoredPrefixes = false; - VerifyOrExit(!Contains(aPrefix)); + // Restore old prefixes from `Settings` - error = PushBack(aPrefix); + for (int index = 0; Get().ReadBrOnLinkPrefix(index, savedPrefix) == kErrorNone; index++) + { + uint32_t lifetime; + OldPrefix *entry; - if (error != kErrorNone) + if (mOldLocalPrefixes.ContainsMatching(savedPrefix.GetPrefix())) + { + // We should not see duplicate entries in `Settings` + // but if we do we refresh the stored prefixes to make + // it consistent. + refreshStoredPrefixes = true; + continue; + } + + entry = mOldLocalPrefixes.PushBack(); + + if (entry == nullptr) + { + // If there are more stored prefixes, we refresh the + // prefixes in `Settings` to remove the ones we cannot + // handle. + + refreshStoredPrefixes = true; + break; + } + + lifetime = Min(savedPrefix.GetLifetime(), Time::MsecToSec(TimerMilli::kMaxDelay)); + + entry->mPrefix = savedPrefix.GetPrefix(); + entry->mExpireTime = now + Time::SecToMsec(lifetime); + + LogInfo("Restored old prefix %s, lifetime:%lu", entry->mPrefix.ToString().AsCString(), ToUlong(lifetime)); + + mTimer.FireAtIfEarlier(entry->mExpireTime); + } + + if (refreshStoredPrefixes) { - LogWarn("Too many on-mesh prefixes in net data, ignoring prefix %s", aPrefix.ToString().AsCString()); + // We clear the entries in `Settings` and re-write the entries + // from `mOldLocalPrefixes` array. + + IgnoreError(Get().DeleteAllBrOnLinkPrefixes()); + + for (OldPrefix &oldPrefix : mOldLocalPrefixes) + { + SavePrefix(oldPrefix.mPrefix, oldPrefix.mExpireTime); + } + } + + GenerateLocalPrefix(); +} + +void RoutingManager::OnLinkPrefixManager::GenerateLocalPrefix(void) +{ + const MeshCoP::ExtendedPanId &extPanId = Get().GetExtPanId(); + OldPrefix *entry; + Ip6::Prefix oldLocalPrefix = mLocalPrefix; + + // Global ID: 40 most significant bits of Extended PAN ID + // Subnet ID: 16 least significant bits of Extended PAN ID + + mLocalPrefix.mPrefix.mFields.m8[0] = 0xfd; + memcpy(mLocalPrefix.mPrefix.mFields.m8 + 1, extPanId.m8, 5); + memcpy(mLocalPrefix.mPrefix.mFields.m8 + 6, extPanId.m8 + 6, 2); + + mLocalPrefix.SetLength(kOnLinkPrefixLength); + + // We ensure that the local prefix did change, since not all the + // bytes in Extended PAN ID are used in derivation of the local prefix. + + VerifyOrExit(mLocalPrefix != oldLocalPrefix); + + LogNote("Local on-link prefix: %s", mLocalPrefix.ToString().AsCString()); + + // Check if the new local prefix happens to be in `mOldLocalPrefixes` array. + // If so, we remove it from the array and set `mState` accordingly. + + entry = mOldLocalPrefixes.FindMatching(mLocalPrefix); + + if (entry != nullptr) + { + mState = kDeprecating; + mExpireTime = entry->mExpireTime; + mOldLocalPrefixes.Remove(*entry); + } + else + { + mState = kIdle; } exit: return; } -void RoutingManager::OnMeshPrefixArray::MarkAsDeleted(const OnMeshPrefix &aPrefix) +void RoutingManager::OnLinkPrefixManager::Start(void) {} + +void RoutingManager::OnLinkPrefixManager::Stop(void) { - // Searches for a matching entry to `aPrefix` and if found marks - // it as deleted by setting prefix length to zero. + mFavoredDiscoveredPrefix.Clear(); - OnMeshPrefix *entry = Find(aPrefix); + switch (mState) + { + case kIdle: + break; - if (entry != nullptr) + case kPublishing: + case kAdvertising: + case kDeprecating: + mState = kDeprecating; + break; + } +} + +void RoutingManager::OnLinkPrefixManager::Evaluate(void) +{ + VerifyOrExit(!Get().mRsSender.IsInProgress()); + + Get().mDiscoveredPrefixTable.FindFavoredOnLinkPrefix(mFavoredDiscoveredPrefix); + + if ((mFavoredDiscoveredPrefix.GetLength() == 0) || (mFavoredDiscoveredPrefix == mLocalPrefix)) { - entry->SetLength(0); + // We need to advertise our local on-link prefix when there is + // no discovered on-link prefix. If the favored discovered + // prefix is the same as our local on-link prefix we also + // start advertising the local prefix to add redundancy. Note + // that local on-link prefix is derived from extended PAN ID + // and therefore is the same for all BRs on the same Thread + // mesh. + + PublishAndAdvertise(); + + // 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. + + Get().mDiscoveredPrefixTable.RemoveOnLinkPrefix(mLocalPrefix); + + mFavoredDiscoveredPrefix.Clear(); + } + else if (IsPublishingOrAdvertising()) + { + // 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 (!(mLocalPrefix < mFavoredDiscoveredPrefix)) + { + LogInfo("EvaluateOnLinkPrefix: There is already favored on-link prefix %s", + mFavoredDiscoveredPrefix.ToString().AsCString()); + Deprecate(); + } } + +exit: + return; +} + +bool RoutingManager::OnLinkPrefixManager::IsInitalEvaluationDone(void) const +{ + // This method indicates whether or not we are done with the + // initial policy evaluation of the on-link prefixes, i.e., either + // we have discovered a favored on-link prefix (being advertised by + // another router on infra link) or we are advertising our local + // on-link prefix. + + return (mFavoredDiscoveredPrefix.GetLength() != 0 || IsPublishingOrAdvertising()); +} + +void RoutingManager::OnLinkPrefixManager::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. + + Ip6::Prefix newFavoredPrefix; + + Get().mDiscoveredPrefixTable.FindFavoredOnLinkPrefix(newFavoredPrefix); + + if (newFavoredPrefix != mFavoredDiscoveredPrefix) + { + Get().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay); + } +} + +void RoutingManager::OnLinkPrefixManager::PublishAndAdvertise(void) +{ + // Start publishing and advertising the local on-link prefix if + // not already. + + switch (mState) + { + case kIdle: + case kDeprecating: + break; + + case kPublishing: + case kAdvertising: + ExitNow(); + } + + mState = kPublishing; + ResetExpireTime(TimerMilli::GetNow()); + + // We wait for the ULA `fc00::/7` route or a sub-prefix of it (e.g., + // default route) to be added in Network Data before + // starting to advertise the local on-link prefix in RAs. + // However, if it is already present in Network Data (e.g., + // added by another BR on the same Thread mesh), we can + // immediately start advertising it. + + if (Get().NetworkDataContainsUlaRoute()) + { + EnterAdvertisingState(); + } + +exit: + return; +} + +void RoutingManager::OnLinkPrefixManager::Deprecate(void) +{ + // Deprecate the local on-link prefix if it was being advertised + // before. While depreciating the prefix, we wait for the lifetime + // timer to expire before unpublishing the prefix from the Network + // Data. We also continue to include it as a PIO in the RA message + // with zero preferred lifetime and the remaining valid lifetime + // until the timer expires. + + switch (mState) + { + case kPublishing: + case kAdvertising: + mState = kDeprecating; + LogInfo("Deprecate local on-link prefix %s", mLocalPrefix.ToString().AsCString()); + break; + + case kIdle: + case kDeprecating: + break; + } +} + +bool RoutingManager::OnLinkPrefixManager::ShouldPublishUlaRoute(void) const +{ + // Determine whether or not we should publish ULA prefix. We need + // to publish if we are in any of `kPublishing`, `kAdvertising`, + // or `kDeprecating` states, or if there is at least one old local + // prefix being deprecated. + + return (mState != kIdle) || !mOldLocalPrefixes.IsEmpty(); +} + +void RoutingManager::OnLinkPrefixManager::ResetExpireTime(TimeMilli aNow) +{ + mExpireTime = aNow + TimeMilli::SecToMsec(kDefaultOnLinkPrefixLifetime); + mTimer.FireAtIfEarlier(mExpireTime); + SavePrefix(mLocalPrefix, mExpireTime); +} + +void RoutingManager::OnLinkPrefixManager::EnterAdvertisingState(void) +{ + mState = kAdvertising; + LogInfo("Start advertising local on-link prefix %s", mLocalPrefix.ToString().AsCString()); +} + +bool RoutingManager::OnLinkPrefixManager::IsPublishingOrAdvertising(void) const +{ + return (mState == kPublishing) || (mState == kAdvertising); +} + +void RoutingManager::OnLinkPrefixManager::AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage) +{ + AppendCurPrefix(aRaMessage); + AppendOldPrefixes(aRaMessage); +} + +void RoutingManager::OnLinkPrefixManager::AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage) +{ + // Append the local on-link prefix to the `aRaMessage` as a PIO + // only if it is being advertised or deprecated. + // + // If in `kAdvertising` state, we reset the expire time. + // If in `kDeprecating` state, we include it as PIO with zero + // preferred lifetime and the remaining valid lifetime. + + uint32_t validLifetime = kDefaultOnLinkPrefixLifetime; + uint32_t preferredLifetime = kDefaultOnLinkPrefixLifetime; + TimeMilli now = TimerMilli::GetNow(); + + switch (mState) + { + case kAdvertising: + ResetExpireTime(now); + break; + + case kDeprecating: + VerifyOrExit(mExpireTime > now); + validLifetime = TimeMilli::MsecToSec(mExpireTime - now); + preferredLifetime = 0; + break; + + case kIdle: + case kPublishing: + ExitNow(); + } + + SuccessOrAssert(aRaMessage.AppendPrefixInfoOption(mLocalPrefix, validLifetime, preferredLifetime)); + + LogInfo("RouterAdvert: Added PIO for %s (valid=%lu, preferred=%lu)", mLocalPrefix.ToString().AsCString(), + ToUlong(validLifetime), ToUlong(preferredLifetime)); + +exit: + return; +} + +void RoutingManager::OnLinkPrefixManager::AppendOldPrefixes(Ip6::Nd::RouterAdvertMessage &aRaMessage) +{ + TimeMilli now = TimerMilli::GetNow(); + uint32_t validLifetime; + + for (const OldPrefix &oldPrefix : mOldLocalPrefixes) + { + if (oldPrefix.mExpireTime < now) + { + continue; + } + + validLifetime = TimeMilli::MsecToSec(oldPrefix.mExpireTime - now); + SuccessOrAssert(aRaMessage.AppendPrefixInfoOption(oldPrefix.mPrefix, validLifetime, 0)); + + LogInfo("RouterAdvert: Added PIO for %s (valid=%lu, preferred=0)", oldPrefix.mPrefix.ToString().AsCString(), + ToUlong(validLifetime)); + } +} + +void RoutingManager::OnLinkPrefixManager::HandleNetDataChange(void) +{ + VerifyOrExit(mState == kPublishing); + + if (Get().NetworkDataContainsUlaRoute()) + { + EnterAdvertisingState(); + Get().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay); + } + +exit: + return; +} + +void RoutingManager::OnLinkPrefixManager::HandleExtPanIdChange(void) +{ + // If the current local prefix is being advertised or deprecated, + // we save it in `mOldLocalPrefixes` and keep deprecating it. It will + // be included in emitted RAs as PIO with zero preferred lifetime. + // It will still be present in Network Data until its expire time + // so to allow Thread nodes to continue to communicate with `InfraIf` + // device using addresses based on this prefix. + + uint16_t oldState = mState; + Ip6::Prefix oldPrefix = mLocalPrefix; + + GenerateLocalPrefix(); + + VerifyOrExit(oldPrefix != mLocalPrefix); + + switch (oldState) + { + case kIdle: + case kPublishing: + break; + + case kAdvertising: + case kDeprecating: + DeprecateOldPrefix(oldPrefix, mExpireTime); + break; + } + + if (Get().mIsRunning) + { + Get().mRoutePublisher.Evaluate(); + Get().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay); + } + +exit: + return; +} + +void RoutingManager::OnLinkPrefixManager::DeprecateOldPrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime) +{ + OldPrefix *entry = nullptr; + Ip6::Prefix removedPrefix; + + removedPrefix.Clear(); + + VerifyOrExit(!mOldLocalPrefixes.ContainsMatching(aPrefix)); + + LogInfo("Deprecating old on-link prefix %s", aPrefix.ToString().AsCString()); + + if (!mOldLocalPrefixes.IsFull()) + { + entry = mOldLocalPrefixes.PushBack(); + } + else + { + // If there is no more room in `mOldLocalPrefixes` array + // we evict the entry with the earliest expiration time. + + entry = &mOldLocalPrefixes[0]; + + for (OldPrefix &oldPrefix : mOldLocalPrefixes) + { + if ((oldPrefix.mExpireTime < entry->mExpireTime)) + { + entry = &oldPrefix; + } + } + + removedPrefix = entry->mPrefix; + + IgnoreError(Get().RemoveBrOnLinkPrefix(removedPrefix)); + } + + entry->mPrefix = aPrefix; + entry->mExpireTime = aExpireTime; + mTimer.FireAtIfEarlier(aExpireTime); + + SavePrefix(aPrefix, aExpireTime); + +exit: + return; +} + +void RoutingManager::OnLinkPrefixManager::SavePrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime) +{ + Settings::BrOnLinkPrefix savedPrefix; + + savedPrefix.SetPrefix(aPrefix); + savedPrefix.SetLifetime(TimeMilli::MsecToSec(aExpireTime - TimerMilli::GetNow())); + IgnoreError(Get().AddOrUpdateBrOnLinkPrefix(savedPrefix)); +} + +void RoutingManager::OnLinkPrefixManager::HandleTimer(void) +{ + TimeMilli now = TimerMilli::GetNow(); + TimeMilli nextExpireTime = now.GetDistantFuture(); + Array expiredPrefixes; + + switch (mState) + { + case kIdle: + break; + case kPublishing: + case kAdvertising: + case kDeprecating: + if (now >= mExpireTime) + { + LogInfo("Local on-link prefix %s expired", mLocalPrefix.ToString().AsCString()); + IgnoreError(Get().RemoveBrOnLinkPrefix(mLocalPrefix)); + mState = kIdle; + } + else + { + nextExpireTime = mExpireTime; + } + break; + } + + for (OldPrefix &entry : mOldLocalPrefixes) + { + if (now >= entry.mExpireTime) + { + SuccessOrAssert(expiredPrefixes.PushBack(entry.mPrefix)); + } + else + { + nextExpireTime = Min(nextExpireTime, entry.mExpireTime); + } + } + + for (const Ip6::Prefix &prefix : expiredPrefixes) + { + LogInfo("Old local on-link prefix %s expired", prefix.ToString().AsCString()); + IgnoreError(Get().RemoveBrOnLinkPrefix(prefix)); + mOldLocalPrefixes.RemoveMatching(prefix); + } + + if (nextExpireTime != now.GetDistantFuture()) + { + mTimer.FireAtIfEarlier(nextExpireTime); + } + + Get().mRoutePublisher.Evaluate(); +} + +//--------------------------------------------------------------------------------------------------------------------- +// OnMeshPrefixArray + +void RoutingManager::OnMeshPrefixArray::Add(const OnMeshPrefix &aPrefix) +{ + // Checks if `aPrefix` is already present in the array and if not + // adds it as new entry. + + Error error; + + VerifyOrExit(!Contains(aPrefix)); + + error = PushBack(aPrefix); + + if (error != kErrorNone) + { + LogWarn("Too many on-mesh prefixes in net data, ignoring prefix %s", aPrefix.ToString().AsCString()); + } + +exit: + return; +} + +void RoutingManager::OnMeshPrefixArray::MarkAsDeleted(const OnMeshPrefix &aPrefix) +{ + // Searches for a matching entry to `aPrefix` and if found marks + // it as deleted by setting prefix length to zero. + + OnMeshPrefix *entry = Find(aPrefix); + + if (entry != nullptr) + { + entry->SetLength(0); + } +} + +//--------------------------------------------------------------------------------------------------------------------- +// RoutePublisher + +const otIp6Prefix RoutingManager::RoutePublisher::kUlaPrefix = { + {{{0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + 7, +}; + +RoutingManager::RoutePublisher::RoutePublisher(Instance &aInstance) + : InstanceLocator(aInstance) + , mState(kDoNotPublish) + , mPreference(NetworkData::kRoutePreferenceMedium) + , mUserSetPreference(false) +{ +} + +void RoutingManager::RoutePublisher::Evaluate(void) +{ + State newState = kDoNotPublish; + + VerifyOrExit(Get().IsRunning()); + + if (Get().mFavoredOmrPrefix.IsInfrastructureDerived() && + Get().mDiscoveredPrefixTable.ContainsDefaultOrNonUlaRoutePrefix()) + { + newState = kPublishDefault; + } + else if (Get().mDiscoveredPrefixTable.ContainsNonUlaOnLinkPrefix()) + { + newState = kPublishDefault; + } + else if (Get().mDiscoveredPrefixTable.ContainsUlaOnLinkPrefix() || + Get().mOnLinkPrefixManager.ShouldPublishUlaRoute()) + { + newState = kPublishUla; + } + +exit: + if (newState != mState) + { + LogInfo("RoutePublisher state: %s -> %s", StateToString(mState), StateToString(newState)); + UpdatePublishedRoute(newState); + Get().mLocalOmrPrefix.UpdateDefaultRouteFlag(newState == kPublishDefault); + } +} + +void RoutingManager::RoutePublisher::DeterminePrefixFor(State aState, Ip6::Prefix &aPrefix) const +{ + aPrefix.Clear(); + + switch (aState) + { + case kDoNotPublish: + case kPublishDefault: + // `Clear()` will set the prefix `::/0`. + break; + case kPublishUla: + aPrefix = GetUlaPrefix(); + break; + } +} + +void RoutingManager::RoutePublisher::UpdatePublishedRoute(State aNewState) +{ + // Updates the published route entry in Network Data, transitioning + // from current `mState` to new `aNewState`. This method can be used + // when there is no change to `mState` but a change to `mPreference`. + + Ip6::Prefix oldPrefix; + NetworkData::ExternalRouteConfig routeConfig; + + DeterminePrefixFor(mState, oldPrefix); + + if (aNewState == kDoNotPublish) + { + VerifyOrExit(mState != kDoNotPublish); + IgnoreError(Get().UnpublishPrefix(oldPrefix)); + ExitNow(); + } + + routeConfig.Clear(); + routeConfig.mPreference = mPreference; + routeConfig.mStable = true; + DeterminePrefixFor(aNewState, routeConfig.GetPrefix()); + + // If we were not publishing a route prefix before, publish the new + // `routeConfig`. Otherwise, use `ReplacePublishedExternalRoute()` to + // replace the previously published prefix entry. This ensures that we do + // not have a situation where the previous route is removed while the new + // one is not yet added in the Network Data. + + if (mState == kDoNotPublish) + { + SuccessOrAssert(Get().PublishExternalRoute( + routeConfig, NetworkData::Publisher::kFromRoutingManager)); + } + else + { + SuccessOrAssert(Get().ReplacePublishedExternalRoute( + oldPrefix, routeConfig, NetworkData::Publisher::kFromRoutingManager)); + } + +exit: + mState = aNewState; +} + +void RoutingManager::RoutePublisher::Unpublish(void) +{ + // Unpublish the previously published route based on `mState` + // and update `mState`. + + Ip6::Prefix prefix; + + VerifyOrExit(mState != kDoNotPublish); + DeterminePrefixFor(mState, prefix); + IgnoreError(Get().UnpublishPrefix(prefix)); + mState = kDoNotPublish; + +exit: + return; +} + +void RoutingManager::RoutePublisher::SetPreference(RoutePreference aPreference) +{ + LogInfo("User explicitly set published route preference to %s", RoutePreferenceToString(aPreference)); + mUserSetPreference = true; + UpdatePreference(aPreference); +} + +void RoutingManager::RoutePublisher::ClearPreference(void) +{ + VerifyOrExit(mUserSetPreference); + + LogInfo("User cleared explicitly set published route preference - set based on role"); + mUserSetPreference = false; + SetPreferenceBasedOnRole(); + +exit: + return; +} + +void RoutingManager::RoutePublisher::SetPreferenceBasedOnRole(void) +{ + UpdatePreference(Get().IsRouterOrLeader() ? NetworkData::kRoutePreferenceMedium + : NetworkData::kRoutePreferenceLow); +} + +void RoutingManager::RoutePublisher::HandleRoleChanged(void) +{ + if (!mUserSetPreference) + { + SetPreferenceBasedOnRole(); + } +} + +void RoutingManager::RoutePublisher::UpdatePreference(RoutePreference aPreference) +{ + VerifyOrExit(mPreference != aPreference); + + LogInfo("Published route preference changed: %s -> %s", RoutePreferenceToString(mPreference), + RoutePreferenceToString(aPreference)); + mPreference = aPreference; + UpdatePublishedRoute(mState); + +exit: + return; +} + +const char *RoutingManager::RoutePublisher::StateToString(State aState) +{ + static const char *const kStateStrings[] = { + "none", // (0) kDoNotPublish + "def-route", // (1) kPublishDefault + "ula", // (2) kPublishUla + }; + + static_assert(0 == kDoNotPublish, "kDoNotPublish value is incorrect"); + static_assert(1 == kPublishDefault, "kPublishDefault value is incorrect"); + static_assert(2 == kPublishUla, "kPublishUla value is incorrect"); + + return kStateStrings[aState]; +} + +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + +//--------------------------------------------------------------------------------------------------------------------- +// Nat64PrefixManager + +RoutingManager::Nat64PrefixManager::Nat64PrefixManager(Instance &aInstance) + : InstanceLocator(aInstance) + , mEnabled(false) + , mTimer(aInstance) +{ + mInfraIfPrefix.Clear(); + mLocalPrefix.Clear(); + mPublishedPrefix.Clear(); +} + +void RoutingManager::Nat64PrefixManager::SetEnabled(bool aEnabled) +{ + VerifyOrExit(mEnabled != aEnabled); + mEnabled = aEnabled; + + if (aEnabled) + { + if (Get().IsRunning()) + { + Start(); + } + } + else + { + Stop(); + } + +exit: + return; +} + +void RoutingManager::Nat64PrefixManager::Start(void) +{ + VerifyOrExit(mEnabled); + LogInfo("Starting Nat64PrefixManager"); + mTimer.Start(0); + +exit: + return; +} + +void RoutingManager::Nat64PrefixManager::Stop(void) +{ + LogInfo("Stopping Nat64PrefixManager"); + + if (mPublishedPrefix.IsValidNat64()) + { + IgnoreError(Get().UnpublishPrefix(mPublishedPrefix)); + } + + mPublishedPrefix.Clear(); + mInfraIfPrefix.Clear(); + mTimer.Stop(); + +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + Get().ClearNat64Prefix(); +#endif +} + +void RoutingManager::Nat64PrefixManager::GenerateLocalPrefix(const Ip6::Prefix &aBrUlaPrefix) +{ + mLocalPrefix = aBrUlaPrefix; + mLocalPrefix.SetSubnetId(kNat64PrefixSubnetId); + mLocalPrefix.mPrefix.mFields.m32[2] = 0; + mLocalPrefix.SetLength(kNat64PrefixLength); + + LogInfo("Generated local NAT64 prefix: %s", mLocalPrefix.ToString().AsCString()); +} + +const Ip6::Prefix &RoutingManager::Nat64PrefixManager::GetFavoredPrefix(RoutePreference &aPreference) const +{ + const Ip6::Prefix *favoredPrefix = &mInfraIfPrefix; + + if (mInfraIfPrefix.IsValidNat64()) + { + aPreference = NetworkData::kRoutePreferenceMedium; + } + else + { + favoredPrefix = &mLocalPrefix; + aPreference = NetworkData::kRoutePreferenceLow; + } + + return *favoredPrefix; +} + +void RoutingManager::Nat64PrefixManager::Evaluate(void) +{ + Error error; + Ip6::Prefix prefix; + RoutePreference preference; + NetworkData::ExternalRouteConfig netdataPrefixConfig; + bool shouldPublish; + + VerifyOrExit(mEnabled); + + LogInfo("Evaluating NAT64 prefix"); + + prefix = GetFavoredPrefix(preference); + + error = Get().GetPreferredNat64Prefix(netdataPrefixConfig); + + // NAT64 prefix is expected to be published from this BR + // when one of the following is true: + // + // - No NAT64 prefix in Network Data. + // - The preferred NAT64 prefix in Network Data has lower + // preference than this BR's prefix. + // - The preferred NAT64 prefix in Network Data was published + // by this BR. + // - The preferred NAT64 prefix in Network Data is same as the + // discovered infrastructure prefix. + // + // TODO: change to check RLOC16 to determine if the NAT64 prefix + // was published by this BR. + + shouldPublish = + ((error == kErrorNotFound) || (netdataPrefixConfig.mPreference < preference) || + (netdataPrefixConfig.GetPrefix() == mPublishedPrefix) || (netdataPrefixConfig.GetPrefix() == mInfraIfPrefix)); + + if (mPublishedPrefix.IsValidNat64() && (!shouldPublish || (prefix != mPublishedPrefix))) + { + IgnoreError(Get().UnpublishPrefix(mPublishedPrefix)); + mPublishedPrefix.Clear(); + } + + if (shouldPublish && ((prefix != mPublishedPrefix) || (preference != mPublishedPreference))) + { + mPublishedPrefix = prefix; + mPublishedPreference = preference; + Publish(); + } + +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + // When there is an prefix other than mLocalPrefix, means there is an external translator available. So we bypass + // the NAT64 translator by clearing the NAT64 prefix in the translator. + if (mPublishedPrefix == mLocalPrefix) + { + Get().SetNat64Prefix(mLocalPrefix); + } + else + { + Get().ClearNat64Prefix(); + } +#endif + +exit: + return; +} + +void RoutingManager::Nat64PrefixManager::Publish(void) +{ + NetworkData::ExternalRouteConfig routeConfig; + + routeConfig.Clear(); + routeConfig.SetPrefix(mPublishedPrefix); + routeConfig.mPreference = mPublishedPreference; + routeConfig.mStable = true; + routeConfig.mNat64 = true; + + SuccessOrAssert( + Get().PublishExternalRoute(routeConfig, NetworkData::Publisher::kFromRoutingManager)); +} + +void RoutingManager::Nat64PrefixManager::HandleTimer(void) +{ + OT_ASSERT(mEnabled); + + Discover(); + + mTimer.Start(TimeMilli::SecToMsec(kDefaultNat64PrefixLifetime)); + LogInfo("NAT64 prefix timer scheduled in %lu seconds", ToUlong(kDefaultNat64PrefixLifetime)); +} + +void RoutingManager::Nat64PrefixManager::Discover(void) +{ + Error error = Get().mInfraIf.DiscoverNat64Prefix(); + + if (error == kErrorNone) + { + LogInfo("Discovering infraif NAT64 prefix"); + } + else + { + LogWarn("Failed to discover infraif NAT64 prefix: %s", ErrorToString(error)); + } +} + +void RoutingManager::Nat64PrefixManager::HandleDiscoverDone(const Ip6::Prefix &aPrefix) +{ + mInfraIfPrefix = aPrefix; + + LogInfo("Infraif NAT64 prefix: %s", mInfraIfPrefix.IsValidNat64() ? mInfraIfPrefix.ToString().AsCString() : "none"); + + if (Get().mIsRunning) + { + Get().ScheduleRoutingPolicyEvaluation(kAfterRandomDelay); + } +} + +Nat64::State RoutingManager::Nat64PrefixManager::GetState(void) const +{ + Nat64::State state = Nat64::kStateDisabled; + + VerifyOrExit(mEnabled); + VerifyOrExit(Get().IsRunning(), state = Nat64::kStateNotRunning); + VerifyOrExit(mPublishedPrefix.IsValidNat64(), state = Nat64::kStateIdle); + state = Nat64::kStateActive; + +exit: + return state; +} + +#endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + +//--------------------------------------------------------------------------------------------------------------------- +// RsSender + +RoutingManager::RsSender::RsSender(Instance &aInstance) + : InstanceLocator(aInstance) + , mTxCount(0) + , mTimer(aInstance) +{ +} + +void RoutingManager::RsSender::Start(void) +{ + uint32_t delay; + + VerifyOrExit(!IsInProgress()); + + delay = Random::NonCrypto::GetUint32InRange(0, kMaxStartDelay); + LogInfo("Scheduled Router Solicitation in %lu milliseconds", ToUlong(delay)); + + mTxCount = 0; + mStartTime = TimerMilli::GetNow(); + mTimer.Start(delay); + +exit: + return; +} + +void RoutingManager::RsSender::Stop(void) { mTimer.Stop(); } + +Error RoutingManager::RsSender::SendRs(void) +{ + Ip6::Address destAddress; + Ip6::Nd::RouterSolicitMessage routerSolicit; + InfraIf::Icmp6Packet packet; + Error error; + + packet.InitFrom(routerSolicit); + destAddress.SetToLinkLocalAllRoutersMulticast(); + + error = Get().mInfraIf.Send(packet, destAddress); + + if (error == kErrorNone) + { + Get().GetBorderRoutingCounters().mRsTxSuccess++; + } + else + { + Get().GetBorderRoutingCounters().mRsTxFailure++; + } + return error; +} + +void RoutingManager::RsSender::HandleTimer(void) +{ + Error error; + uint32_t delay; + + if (mTxCount >= kMaxTxCount) + { + Get().HandleRsSenderFinished(mStartTime); + ExitNow(); + } + + error = SendRs(); + + if (error == kErrorNone) + { + mTxCount++; + LogInfo("Successfully sent RS %u/%u", mTxCount, kMaxTxCount); + delay = (mTxCount == kMaxTxCount) ? kWaitOnLastAttempt : kTxInterval; + } + else + { + LogCrit("Failed to send RS %u, error:%s", mTxCount + 1, ErrorToString(error)); + + // Note that `mTxCount` is intentionally not incremented + // if the tx fails. + delay = kRetryDelay; + } + + mTimer.Start(delay); + +exit: + return; } } // namespace BorderRouter diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index fcc07c448e1..65a01fff30d 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -47,6 +47,7 @@ #error "OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE is required for OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE." #endif +#include #include #include "border_router/infra_if.hpp" @@ -59,6 +60,7 @@ #include "common/string.hpp" #include "common/timer.hpp" #include "net/ip6.hpp" +#include "net/nat64_translator.hpp" #include "net/nd6.hpp" #include "thread/network_data.hpp" @@ -83,6 +85,32 @@ class RoutingManager : public InstanceLocator typedef otBorderRoutingPrefixTableIterator PrefixTableIterator; ///< Prefix Table Iterator. typedef otBorderRoutingPrefixTableEntry PrefixTableEntry; ///< Prefix Table Entry. + /** + * This constant specifies the maximum number of route prefixes that may be published by `RoutingManager` + * in Thread Network Data. + * + * This is used by `NetworkData::Publisher` to reserve entries for use by `RoutingManager`. + * + * The number of published entries accounts for: + * - Route prefix `fc00::/7` or `::/0` + * - One entry for NAT64 published prefix. + * - One extra entry for transitions. + * + */ + static constexpr uint16_t kMaxPublishedPrefixes = 3; + + /** + * This enumeration represents the states of `RoutingManager`. + * + */ + enum State : uint8_t + { + kStateUninitialized = OT_BORDER_ROUTING_STATE_UNINITIALIZED, ///< Uninitialized. + kStateDisabled = OT_BORDER_ROUTING_STATE_DISABLED, ///< Initialized but disabled. + kStateStopped = OT_BORDER_ROUTING_STATE_STOPPED, ///< Initialized & enabled, but currently stopped. + kStateRunning = OT_BORDER_ROUTING_STATE_RUNNING, ///< Initialized, enabled, and running. + }; + /** * This constructor initializes the routing manager. * @@ -118,28 +146,76 @@ class RoutingManager : public InstanceLocator Error SetEnabled(bool aEnabled); /** - * This method gets the preference used when advertising Route Info Options (e.g., for discovered OMR prefixes) in - * Router Advertisement messages sent over the infrastructure link. + * This method indicates whether or not it is currently running. + * + * In order for the `RoutingManager` to be running it needs to be initialized and enabled, and device being + * attached. + * + * @retval TRUE The RoutingManager is currently running. + * @retval FALSE The RoutingManager is not running. + * + */ + bool IsRunning(void) const { return mIsRunning; } + + /** + * This method gets the state of `RoutingManager`. + * + * @returns The current state of `RoutingManager`. + * + */ + State GetState(void) const; + + /** + * This method requests the Border Routing Manager to stop. * - * @returns The Route Info Option preference. + * If Border Routing Manager is running, calling this method immediately stops it and triggers the preparation + * and sending of a final Router Advertisement (RA) message on infrastructure interface which deprecates and/or + * removes any previously advertised PIO/RIO prefixes. If Routing Manager is not running (or not enabled), no + * action is taken. + * + * Note that this method does not change whether the Routing Manager is enabled or disabled (see `SetEnabled()`). + * It stops the Routing Manager temporarily. After calling this method if the device role gets changes (device + * gets attached) and/or the infra interface state gets changed, the Routing Manager may be started again. + * + */ + void RequestStop(void) { Stop(); } + + /** + * This method gets the current preference used when advertising Route Info Options (RIO) in Router Advertisement + * messages sent over the infrastructure link. + * + * The RIO preference is determined as follows: + * + * - If explicitly set by user by calling `SetRouteInfoOptionPreference()`, the given preference is used. + * - Otherwise, it is determined based on device's role: Medium preference when in router/leader role and low + * preference when in child role. + * + * @returns The current Route Info Option preference. * */ - RoutePreference GetRouteInfoOptionPreference(void) const { return mRouteInfoOptionPreference; } + RoutePreference GetRouteInfoOptionPreference(void) const { return mRioPreference; } /** - * This method sets the preference to use when advertising Route Info Options (e.g., for discovered OMR prefixes) - * in Router Advertisement messages sent over the infrastructure link. + * This method explicitly sets the preference to use when advertising Route Info Options (RIO) in Router + * Advertisement messages sent over the infrastructure link. * - * By default BR will use 'medium' preference level but this method allows the default value to be changed. As an - * example, it can be set to 'low' preference in the case where device is a temporary BR (a mobile BR or a - * battery-powered BR) to indicate that other BRs (if any) should be preferred over this BR on the infrastructure - * link. + * After a call to this method, BR will use the given preference for all its advertised RIOs. The preference can be + * cleared by calling `ClearRouteInfoOptionPreference`()`. * * @param[in] aPreference The route preference to use. * */ void SetRouteInfoOptionPreference(RoutePreference aPreference); + /** + * This method clears a previously set preference value for advertised Route Info Options. + * + * After a call to this method, BR will use device role to determine the RIO preference: Medium preference when + * in router/leader role and low preference when in child role. + * + */ + void ClearRouteInfoOptionPreference(void); + /** * This method returns the local off-mesh-routable (OMR) prefix. * @@ -152,7 +228,7 @@ class RoutingManager : public InstanceLocator * @retval kErrorNone Successfully retrieved the OMR prefix. * */ - Error GetOmrPrefix(Ip6::Prefix &aPrefix); + Error GetOmrPrefix(Ip6::Prefix &aPrefix) const; /** * This method returns the currently favored off-mesh-routable (OMR) prefix. @@ -165,14 +241,14 @@ class RoutingManager : public InstanceLocator * @param[out] aPrefix A reference to output the favored prefix. * @param[out] aPreference A reference to output the preference associated with the favored OMR prefix. * - * @retval kErrorInvalidState The Border Routing Manager is not initialized yet. + * @retval kErrorInvalidState The Border Routing Manager is not running yet. * @retval kErrorNone Successfully retrieved the OMR prefix. * */ - Error GetFavoredOmrPrefix(Ip6::Prefix &aPrefix, RoutePreference &aPreference); + Error GetFavoredOmrPrefix(Ip6::Prefix &aPrefix, RoutePreference &aPreference) const; /** - * This method returns the on-link prefix for the adjacent infrastructure link. + * This method returns the on-link prefix for the adjacent infrastructure link. * * The randomly generated 64-bit prefix will be advertised * on the infrastructure link if there isn't already a usable @@ -181,17 +257,48 @@ class RoutingManager : public InstanceLocator * @param[out] aPrefix A reference to where the prefix will be output to. * * @retval kErrorInvalidState The Border Routing Manager is not initialized yet. - * @retval kErrorNone Successfully retrieved the on-link prefix. + * @retval kErrorNone Successfully retrieved the local on-link prefix. * */ - Error GetOnLinkPrefix(Ip6::Prefix &aPrefix); + Error GetOnLinkPrefix(Ip6::Prefix &aPrefix) const; -#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE /** - * This method returns the local NAT64 prefix. + * This method returns the favored on-link prefix for the adjacent infrastructure link. * - * The local NAT64 prefix will be published in the Thread network - * if none exists. + * The favored prefix is either a discovered prefix on the infrastructure link or the local on-link prefix. + * + * @param[out] aPrefix A reference to where the prefix will be output to. + * + * @retval kErrorInvalidState The Border Routing Manager is not initialized yet. + * @retval kErrorNone Successfully retrieved the favored on-link prefix. + * + */ + Error GetFavoredOnLinkPrefix(Ip6::Prefix &aPrefix) const; + +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + /** + * Gets the state of NAT64 prefix publishing. + * + * @retval kStateDisabled NAT64 is disabled. + * @retval kStateNotRunning NAT64 is enabled, but is not running since routing manager is not running. + * @retval kStateIdle NAT64 is enabled, but the border router is not publishing a NAT64 prefix. Usually + * when there is another border router publishing a NAT64 prefix with higher + * priority. + * @retval kStateActive The Border router is publishing a NAT64 prefix. + * + */ + Nat64::State GetNat64PrefixManagerState(void) const { return mNat64PrefixManager.GetState(); } + + /** + * Enable or disable NAT64 prefix publishing. + * + * @param[in] aEnabled A boolean to enable/disable NAT64 prefix publishing. + * + */ + void SetNat64PrefixManagerEnabled(bool aEnabled); + + /** + * This method returns the local NAT64 prefix. * * @param[out] aPrefix A reference to where the prefix will be output to. * @@ -200,7 +307,31 @@ class RoutingManager : public InstanceLocator * */ Error GetNat64Prefix(Ip6::Prefix &aPrefix); -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE + + /** + * This method returns the currently favored NAT64 prefix. + * + * The favored NAT64 prefix can be discovered from infrastructure link or can be the local NAT64 prefix. + * + * @param[out] aPrefix A reference to output the favored prefix. + * @param[out] aPreference A reference to output the preference associated with the favored prefix. + * + * @retval kErrorInvalidState The Border Routing Manager is not initialized yet. + * @retval kErrorNone Successfully retrieved the NAT64 prefix. + * + */ + Error GetFavoredNat64Prefix(Ip6::Prefix &aPrefix, RoutePreference &aRoutePreference); + + /** + * This method informs `RoutingManager` of the result of the discovery request of NAT64 prefix on infrastructure + * interface (`InfraIf::DiscoverNat64Prefix()`). + * + * @param[in] aPrefix The discovered NAT64 prefix on `InfraIf`. + * + */ + void HandleDiscoverNat64PrefixDone(const Ip6::Prefix &aPrefix) { mNat64PrefixManager.HandleDiscoverDone(aPrefix); } + +#endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE /** * This method processes a received ICMPv6 message from the infrastructure interface. @@ -220,23 +351,26 @@ class RoutingManager : public InstanceLocator void HandleInfraIfStateChanged(void) { EvaluateState(); } /** - * This method checks if the on-mesh prefix configuration is a valid OMR prefix. + * This method checks whether the on-mesh prefix configuration is a valid OMR prefix. * * @param[in] aOnMeshPrefixConfig The on-mesh prefix configuration to check. * - * @returns Whether the on-mesh prefix configuration is a valid OMR prefix. + * @retval TRUE The prefix is a valid OMR prefix. + * @retval FALSE The prefix is not a valid OMR prefix. * */ static bool IsValidOmrPrefix(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig); /** - * This method checks if the OMR prefix is valid (i.e. GUA/ULA prefix with length being 64). + * This method checks whether a given prefix is a valid OMR prefix. + * + * @param[in] aPrefix The prefix to check. * - * @param[in] aOmrPrefix The OMR prefix to check. - * @returns Whether the OMR prefix is valid. + * @retval TRUE The prefix is a valid OMR prefix. + * @retval FALSE The prefix is not a valid OMR prefix. * */ - static bool IsValidOmrPrefix(const Ip6::Prefix &aOmrPrefix); + static bool IsValidOmrPrefix(const Ip6::Prefix &aPrefix); /** * This method initializes a `PrefixTableIterator`. @@ -269,6 +403,16 @@ class RoutingManager : public InstanceLocator return mDiscoveredPrefixTable.GetNextEntry(aIterator, aEntry); } +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE + /** + * This method determines whether to enable/disable SRP server when the auto-enable mode is changed on SRP server. + * + * This should be called from `Srp::Server` when auto-enable mode is changed. + * + */ + void HandleSrpServerAutoEnableMode(void); +#endif + private: static constexpr uint8_t kMaxOnMeshPrefixes = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_ON_MESH_PREFIXES; @@ -283,21 +427,16 @@ class RoutingManager : public InstanceLocator // The maximum number of initial Router Advertisements. static constexpr uint32_t kMaxInitRtrAdvertisements = 3; - // The maximum number of Router Solicitations before sending Router Advertisements. - static constexpr uint32_t kMaxRtrSolicitations = 3; - static constexpr uint32_t kDefaultOmrPrefixLifetime = 1800; // The default OMR prefix valid lifetime. In sec. static constexpr uint32_t kDefaultOnLinkPrefixLifetime = 1800; // The default on-link prefix valid lifetime. In sec. + static constexpr uint32_t kDefaultNat64PrefixLifetime = 300; // The default NAT64 prefix valid lifetime. In sec. static constexpr uint32_t kMaxRtrAdvInterval = 600; // Max Router Advertisement Interval. In sec. static constexpr uint32_t kMinRtrAdvInterval = kMaxRtrAdvInterval / 3; // Min RA Interval. In sec. static constexpr uint32_t kMaxInitRtrAdvInterval = 16; // Max Initial RA Interval. In sec. - static constexpr uint32_t kRaReplyJitter = 500; // Jitter for sending RA after rx RS. In msec. - static constexpr uint32_t kRtrSolicitationInterval = 4; // Interval between RSs. In sec. - static constexpr uint32_t kMaxRtrSolicitationDelay = 1; // Max delay for initial solicitation. In sec. - static constexpr uint32_t kRoutingPolicyEvaluationJitter = 1000; // Jitter for routing policy evaluation. In msec. - static constexpr uint32_t kRtrSolicitationRetryDelay = - kRtrSolicitationInterval; // The delay before retrying failed RS tx. In Sec. - static constexpr uint32_t kMinDelayBetweenRtrAdvs = 3000; // Min delay (msec) between consecutive RAs. + static constexpr uint32_t kRaReplyJitter = 500; // Jitter for sending RA after rx RS. In msec. + static constexpr uint32_t kPolicyEvaluationMinDelay = 2000; // Min delay for policy evaluation. In msec. + static constexpr uint32_t kPolicyEvaluationMaxDelay = 4000; // Max delay for policy evaluation. In msec. + static constexpr uint32_t kMinDelayBetweenRtrAdvs = 3000; // Min delay (msec) between consecutive RAs. // The STALE_RA_TIME in seconds. The Routing Manager will consider the prefixes // and learned RA parameters STALE when they are not refreshed in STALE_RA_TIME @@ -312,6 +451,8 @@ class RoutingManager : public InstanceLocator static_assert(kDefaultOnLinkPrefixLifetime >= kMaxRtrAdvInterval, "invalid default on-link prefix lifetime"); static_assert(kRtrAdvStaleTime >= 1800 && kRtrAdvStaleTime <= kDefaultOnLinkPrefixLifetime, "invalid RA STALE time"); + static_assert(kPolicyEvaluationMaxDelay > kPolicyEvaluationMinDelay, + "kPolicyEvaluationMaxDelay must be larger than kPolicyEvaluationMinDelay"); enum RouterAdvTxMode : uint8_t // Used in `SendRouterAdvertisement()` { @@ -319,6 +460,18 @@ class RoutingManager : public InstanceLocator kAdvPrefixesFromNetData, }; + enum ScheduleMode : uint8_t // Used in `ScheduleRoutingPolicyEvaluation()` + { + kImmediately, + kForNextRa, + kAfterRandomDelay, + kToReplyToRs, + }; + + void HandleDiscoveredPrefixTableChanged(void); // Declare early so we can use in `mSignalTask` + void HandleDiscoveredPrefixTableEntryTimer(void) { mDiscoveredPrefixTable.HandleEntryTimer(); } + void HandleDiscoveredPrefixTableRouterTimer(void) { mDiscoveredPrefixTable.HandleRouterTimer(); } + class DiscoveredPrefixTable : public InstanceLocator { // This class maintains the discovered on-link and route prefixes @@ -340,25 +493,20 @@ class RoutingManager : public InstanceLocator // invoked after all the changes are processed. public: - enum NetDataMode : uint8_t // Used in `Remove{}` methods - { - kUnpublishFromNetData, // Unpublish the entry from Network Data if previously published. - kKeepInNetData, // Keep entry in Network Data if previously published. - }; - explicit DiscoveredPrefixTable(Instance &aInstance); void ProcessRouterAdvertMessage(const Ip6::Nd::RouterAdvertMessage &aRaMessage, - const Ip6::Address & aSrcAddress); + const Ip6::Address &aSrcAddress); + void ProcessNeighborAdvertMessage(const Ip6::Nd::NeighborAdvertMessage &aNaMessage); - void SetAllowDefaultRouteInNetData(bool aAllow); + bool ContainsDefaultOrNonUlaRoutePrefix(void) const; + bool ContainsNonUlaOnLinkPrefix(void) const; + bool ContainsUlaOnLinkPrefix(void) const; 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 RemoveOnLinkPrefix(const Ip6::Prefix &aPrefix); + void RemoveRoutePrefix(const Ip6::Prefix &aPrefix); void RemoveAllEntries(void); void RemoveOrDeprecateOldEntries(TimeMilli aTimeThreshold); @@ -368,6 +516,9 @@ class RoutingManager : public InstanceLocator void InitIterator(PrefixTableIterator &aIterator) const; Error GetNextEntry(PrefixTableIterator &aIterator, PrefixTableEntry &aEntry) const; + void HandleEntryTimer(void); + void HandleRouterTimer(void); + private: static constexpr uint16_t kMaxRouters = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_ROUTERS; static constexpr uint16_t kMaxEntries = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES; @@ -375,6 +526,7 @@ class RoutingManager : public InstanceLocator class Entry : public LinkedListEntry, public Unequatable, private Clearable { friend class LinkedListEntry; + friend class Clearable; public: enum Type : uint8_t @@ -392,7 +544,26 @@ class RoutingManager : public InstanceLocator } const Ip6::Prefix &mPrefix; - bool mType; + Type mType; + }; + + struct Checker + { + enum Mode : uint8_t + { + kIsUla, + kIsNotUla, + }; + + Checker(Mode aMode, Type aType) + : mMode(aMode) + , mType(aType) + + { + } + + Mode mMode; + Type mType; }; struct ExpirationChecker @@ -410,8 +581,9 @@ class RoutingManager : public InstanceLocator void SetFrom(const Ip6::Nd::RouteInfoOption &aRio); Type GetType(void) const { return mType; } bool IsOnLinkPrefix(void) const { return (mType == kTypeOnLink); } + bool IsRoutePrefix(void) const { return (mType == kTypeRoute); } const Ip6::Prefix &GetPrefix(void) const { return mPrefix; } - const TimeMilli & GetLastUpdateTime(void) const { return mLastUpdateTime; } + const TimeMilli &GetLastUpdateTime(void) const { return mLastUpdateTime; } uint32_t GetValidLifetime(void) const { return mValidLifetime; } void ClearValidLifetime(void) { mValidLifetime = 0; } TimeMilli GetExpireTime(void) const; @@ -419,13 +591,14 @@ class RoutingManager : public InstanceLocator RoutePreference GetPreference(void) const; bool operator==(const Entry &aOther) const; bool Matches(const Matcher &aMatcher) const; - bool Matches(const ExpirationChecker &aCheker) const; + bool Matches(const Checker &aChecker) const; + bool Matches(const ExpirationChecker &aChecker) const; // Methods to use when `IsOnLinkPrefix()` uint32_t GetPreferredLifetime(void) const { return mShared.mPreferredLifetime; } void ClearPreferredLifetime(void) { mShared.mPreferredLifetime = 0; } bool IsDeprecated(void) const; - void AdoptValidAndPreferredLiftimesFrom(const Entry &aEntry); + void AdoptValidAndPreferredLifetimesFrom(const Entry &aEntry); // Method to use when `!IsOnlinkPrefix()` RoutePreference GetRoutePreference(void) const { return mShared.mRoutePreference; } @@ -433,7 +606,7 @@ class RoutingManager : public InstanceLocator private: static uint32_t CalculateExpireDelay(uint32_t aValidLifetime); - Entry * mNext; + Entry *mNext; Ip6::Prefix mPrefix; Type mType; TimeMilli mLastUpdateTime; @@ -447,6 +620,17 @@ class RoutingManager : public InstanceLocator struct Router { + // The timeout (in msec) for router staying in active state + // before starting the Neighbor Solicitation (NS) probes. + static constexpr uint32_t kActiveTimeout = OPENTHREAD_CONFIG_BORDER_ROUTING_ROUTER_ACTIVE_CHECK_TIMEOUT; + + static constexpr uint8_t kMaxNsProbes = 5; // Max number of NS probe attempts. + static constexpr uint32_t kNsProbeRetryInterval = 1000; // In msec. Time between NS probe attempts. + static constexpr uint32_t kNsProbeTimeout = 2000; // In msec. Max Wait time after last NS probe. + static constexpr uint32_t kJitter = 2000; // In msec. Jitter to randomize probe starts. + + static_assert(kMaxNsProbes < 255, "kMaxNsProbes MUST not be 255"); + enum EmptyChecker : uint8_t { kContainsNoEntries @@ -457,6 +641,8 @@ class RoutingManager : public InstanceLocator Ip6::Address mAddress; LinkedList mEntries; + TimeMilli mTimeout; + uint8_t mNsProbeCount; }; class Iterator : public PrefixTableIterator @@ -464,36 +650,38 @@ class RoutingManager : public InstanceLocator public: const Router *GetRouter(void) const { return static_cast(mPtr1); } void SetRouter(const Router *aRouter) { mPtr1 = aRouter; } - const Entry * GetEntry(void) const { return static_cast(mPtr2); } + const Entry *GetEntry(void) const { return static_cast(mPtr2); } void SetEntry(const Entry *aEntry) { mPtr2 = aEntry; } TimeMilli GetInitTime(void) const { return TimeMilli(mData32); } void SetInitTime(void) { mData32 = TimerMilli::GetNow().GetValue(); } }; - void ProcessDefaultRoute(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader, Router &aRouter); - void ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, Router &aRouter); - void ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, Router &aRouter); - bool ContainsPrefix(const Entry::Matcher &aMatcher) const; - void RemovePrefix(const Entry::Matcher &aMatcher, NetDataMode aNetDataMode); - void RemoveRoutersWithNoEntries(void); - Entry * AllocateEntry(void) { return mEntryPool.Allocate(); } - void FreeEntry(Entry &aEntry) { mEntryPool.Free(aEntry); } - void FreeEntries(LinkedList &aEntries); - void UpdateNetworkDataOnChangeTo(Entry &aEntry); - Entry * FindFavoredEntryToPublish(const Ip6::Prefix &aPrefix); - void PublishEntry(const Entry &aEntry); - void UnpublishEntry(const Entry &aEntry); - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); - void RemoveExpiredEntries(void); - void SignalTableChanged(void); - static void HandleSignalTask(Tasklet &aTasklet); + void ProcessDefaultRoute(const Ip6::Nd::RouterAdvertMessage::Header &aRaHeader, Router &aRouter); + void ProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, Router &aRouter); + void ProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, Router &aRouter); + bool Contains(const Entry::Checker &aChecker) const; + void RemovePrefix(const Entry::Matcher &aMatcher); + void RemoveOrDeprecateEntriesFromInactiveRouters(void); + void RemoveRoutersWithNoEntries(void); + Entry *AllocateEntry(void) { return mEntryPool.Allocate(); } + void FreeEntry(Entry &aEntry) { mEntryPool.Free(aEntry); } + void FreeEntries(LinkedList &aEntries); + void UpdateNetworkDataOnChangeTo(Entry &aEntry); + const Entry *FindFavoredEntryToPublish(const Ip6::Prefix &aPrefix) const; + void RemoveExpiredEntries(void); + void SignalTableChanged(void); + void UpdateRouterOnRx(Router &aRouter); + void SendNeighborSolicitToRouter(const Router &aRouter); + + using SignalTask = TaskletIn; + using EntryTimer = TimerMilliIn; + using RouterTimer = TimerMilliIn; Array mRouters; Pool mEntryPool; - TimerMilli mTimer; - Tasklet mSignalTask; - bool mAllowDefaultRouteInNetData; + EntryTimer mEntryTimer; + RouterTimer mRouterTimer; + SignalTask mSignalTask; }; class LocalOmrPrefix; @@ -504,18 +692,21 @@ class RoutingManager : public InstanceLocator OmrPrefix(void) { Clear(); } bool IsEmpty(void) const { return (mPrefix.GetLength() == 0); } + bool IsInfrastructureDerived(void) const; void SetFrom(const NetworkData::OnMeshPrefixConfig &aOnMeshPrefixConfig); void SetFrom(const LocalOmrPrefix &aLocalOmrPrefix); const Ip6::Prefix &GetPrefix(void) const { return mPrefix; } RoutePreference GetPreference(void) const { return mPreference; } bool IsFavoredOver(const NetworkData::OnMeshPrefixConfig &aOmrPrefixConfig) const; + bool IsDomainPrefix(void) const { return mIsDomainPrefix; } private: Ip6::Prefix mPrefix; RoutePreference mPreference; + bool mIsDomainPrefix; }; - class LocalOmrPrefix : InstanceLocator + class LocalOmrPrefix : public InstanceLocator { public: explicit LocalOmrPrefix(Instance &aInstance); @@ -525,10 +716,81 @@ class RoutingManager : public InstanceLocator Error AddToNetData(void); void RemoveFromNetData(void); bool IsAddedInNetData(void) const { return mIsAddedInNetData; } + void UpdateDefaultRouteFlag(bool aDefaultRoute); private: + static constexpr uint16_t kInfoStringSize = 85; + + typedef String InfoString; + + Error AddOrUpdate(void); + InfoString ToString(void) const; + Ip6::Prefix mPrefix; bool mIsAddedInNetData; + bool mDefaultRoute; + }; + + void HandleOnLinkPrefixManagerTimer(void) { mOnLinkPrefixManager.HandleTimer(); } + + class OnLinkPrefixManager : public InstanceLocator + { + public: + explicit OnLinkPrefixManager(Instance &aInstance); + + // Max number of old on-link prefixes to retain to deprecate. + static constexpr uint16_t kMaxOldPrefixes = OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_OLD_ON_LINK_PREFIXES; + + void Init(void); + void Start(void); + void Stop(void); + void Evaluate(void); + const Ip6::Prefix &GetLocalPrefix(void) const { return mLocalPrefix; } + const Ip6::Prefix &GetFavoredDiscoveredPrefix(void) const { return mFavoredDiscoveredPrefix; } + bool IsInitalEvaluationDone(void) const; + void HandleDiscoveredPrefixTableChanged(void); + bool ShouldPublishUlaRoute(void) const; + void AppendAsPiosTo(Ip6::Nd::RouterAdvertMessage &aRaMessage); + bool IsPublishingOrAdvertising(void) const; + void HandleNetDataChange(void); + void HandleExtPanIdChange(void); + void HandleTimer(void); + + private: + enum State : uint8_t // State of `mLocalPrefix` + { + kIdle, + kPublishing, + kAdvertising, + kDeprecating, + }; + + struct OldPrefix + { + bool Matches(const Ip6::Prefix &aPrefix) const { return mPrefix == aPrefix; } + + Ip6::Prefix mPrefix; + TimeMilli mExpireTime; + }; + + void GenerateLocalPrefix(void); + void PublishAndAdvertise(void); + void Deprecate(void); + void ResetExpireTime(TimeMilli aNow); + void EnterAdvertisingState(void); + void AppendCurPrefix(Ip6::Nd::RouterAdvertMessage &aRaMessage); + void AppendOldPrefixes(Ip6::Nd::RouterAdvertMessage &aRaMessage); + void DeprecateOldPrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime); + void SavePrefix(const Ip6::Prefix &aPrefix, TimeMilli aExpireTime); + + using ExpireTimer = TimerMilliIn; + + Ip6::Prefix mLocalPrefix; + State mState; + TimeMilli mExpireTime; + Ip6::Prefix mFavoredDiscoveredPrefix; + Array mOldLocalPrefixes; + ExpireTimer mTimer; }; typedef Ip6::Prefix OnMeshPrefix; @@ -540,51 +802,180 @@ class RoutingManager : public InstanceLocator void MarkAsDeleted(const OnMeshPrefix &aPrefix); }; +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + void HandleNat64PrefixManagerTimer(void) { mNat64PrefixManager.HandleTimer(); } + + class Nat64PrefixManager : public InstanceLocator + { + public: + // This class manages the NAT64 related functions including + // generation of local NAT64 prefix, discovery of infra + // interface prefix, maintaining the discovered prefix + // lifetime, and selection of the NAT64 prefix to publish in + // Network Data. + // + // Calling methods except GenerateLocalPrefix and SetEnabled + // when disabled becomes no-op. + + explicit Nat64PrefixManager(Instance &aInstance); + + void SetEnabled(bool aEnabled); + Nat64::State GetState(void) const; + + void Start(void); + void Stop(void); + + void GenerateLocalPrefix(const Ip6::Prefix &aBrUlaPrefix); + const Ip6::Prefix &GetLocalPrefix(void) const { return mLocalPrefix; } + const Ip6::Prefix &GetFavoredPrefix(RoutePreference &aPreference) const; + void Evaluate(void); + void HandleDiscoverDone(const Ip6::Prefix &aPrefix); + void HandleTimer(void); + + private: + void Discover(void); + void Publish(void); + + using Nat64Timer = TimerMilliIn; + + bool mEnabled; + + Ip6::Prefix mInfraIfPrefix; // The latest NAT64 prefix discovered on the infrastructure interface. + Ip6::Prefix mLocalPrefix; // The local prefix (from BR ULA prefix). + Ip6::Prefix mPublishedPrefix; // The prefix to publish in Net Data (empty or local or from infra-if). + RoutePreference mPublishedPreference; // The published prefix preference. + Nat64Timer mTimer; + }; +#endif // OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + + class RoutePublisher : public InstanceLocator // Manages the routes that are published in net data + { + public: + explicit RoutePublisher(Instance &aInstance); + + void Start(void) { Evaluate(); } + void Stop(void) { Unpublish(); } + void Evaluate(void); + + RoutePreference GetPreference(void) const { return mPreference; } + void SetPreference(RoutePreference aPreference); + void ClearPreference(void); + + void HandleRoleChanged(void); + + static const Ip6::Prefix &GetUlaPrefix(void) { return AsCoreType(&kUlaPrefix); } + + private: + static const otIp6Prefix kUlaPrefix; + + enum State : uint8_t + { + kDoNotPublish, // Do not publish any routes in network data. + kPublishDefault, // Publish "::/0" route in network data. + kPublishUla, // Publish "fc00::/7" route in network data. + }; + + void DeterminePrefixFor(State aState, Ip6::Prefix &aPrefix) const; + void UpdatePublishedRoute(State aNewState); + void Unpublish(void); + void SetPreferenceBasedOnRole(void); + void UpdatePreference(RoutePreference aPreference); + + static const char *StateToString(State aState); + + State mState; + RoutePreference mPreference; + bool mUserSetPreference; + }; + + struct RaInfo + { + // Tracks info about emitted RA messages: Number of RAs sent, + // last tx time, header to use and whether the header is + // discovered from receiving RAs from the host itself. This + // ensures that if an entity on host is advertising certain + // info in its RA header (e.g., a default route), the RAs we + // emit from `RoutingManager` also include the same header. + + RaInfo(void) + : mHeaderUpdateTime(TimerMilli::GetNow()) + , mIsHeaderFromHost(false) + , mTxCount(0) + , mLastTxTime(TimerMilli::GetNow() - kMinDelayBetweenRtrAdvs) + { + } + + Ip6::Nd::RouterAdvertMessage::Header mHeader; + TimeMilli mHeaderUpdateTime; + bool mIsHeaderFromHost; + uint32_t mTxCount; + TimeMilli mLastTxTime; + }; + + void HandleRsSenderTimer(void) { mRsSender.HandleTimer(); } + + class RsSender : public InstanceLocator + { + public: + // This class implements tx of Router Solicitation (RS) + // messages to discover other routers. `Start()` schedules + // a cycle of RS transmissions of `kMaxTxCount` separated + // by `kTxInterval`. At the end of cycle the callback + // `HandleRsSenderFinished()` is invoked to inform end of + // the cycle to `RoutingManager`. + + explicit RsSender(Instance &aInstance); + + bool IsInProgress(void) const { return mTimer.IsRunning(); } + void Start(void); + void Stop(void); + void HandleTimer(void); + + private: + // All time intervals are in msec. + static constexpr uint32_t kMaxStartDelay = 1000; // Max random delay to send the first RS. + static constexpr uint32_t kTxInterval = 4000; // Interval between RS tx. + static constexpr uint32_t kRetryDelay = kTxInterval; // Interval to wait to retry a failed RS tx. + static constexpr uint32_t kWaitOnLastAttempt = 1000; // Wait interval after last RS tx. + static constexpr uint8_t kMaxTxCount = 3; // Number of RS tx in one cycle. + + Error SendRs(void); + + using RsTimer = TimerMilliIn; + + uint8_t mTxCount; + RsTimer mTimer; + TimeMilli mStartTime; + }; + void EvaluateState(void); void Start(void); void Stop(void); void HandleNotifierEvents(Events aEvents); bool IsInitialized(void) const { return mInfraIf.IsInitialized(); } bool IsEnabled(void) const { return mIsEnabled; } + void SetRioPreferenceBasedOnRole(void); + void UpdateRioPreference(RoutePreference aPreference); Error LoadOrGenerateRandomBrUlaPrefix(void); - void GenerateOnLinkPrefix(void); - void EvaluateOnLinkPrefix(void); + void EvaluateRoutingPolicy(void); + bool IsInitalPolicyEvaluationDone(void) const; + void ScheduleRoutingPolicyEvaluation(ScheduleMode aMode); + void DetermineFavoredOmrPrefix(void); + void EvaluateOmrPrefix(void); + void HandleRsSenderFinished(TimeMilli aStartTime); + void SendRouterAdvertisement(RouterAdvTxMode aRaTxMode); -#if OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE - void GenerateNat64Prefix(void); - void EvaluateNat64Prefix(void); -#endif + void HandleDiscoveredPrefixStaleTimer(void); - void EvaluateRoutingPolicy(void); - void StartRoutingPolicyEvaluationJitter(uint32_t aJitterMilli); - void StartRoutingPolicyEvaluationDelay(uint32_t aDelayMilli); - void EvaluateOmrPrefix(void); - 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(RouterAdvTxMode aRaTxMode); - bool IsRouterSolicitationInProgress(void) const; - - static void HandleRouterSolicitTimer(Timer &aTimer); - void HandleRouterSolicitTimer(void); - static void HandleDiscoveredPrefixInvalidTimer(Timer &aTimer); - void HandleDiscoveredPrefixInvalidTimer(void); - static void HandleDiscoveredPrefixStaleTimer(Timer &aTimer); - void HandleDiscoveredPrefixStaleTimer(void); - static void HandleRoutingPolicyTimer(Timer &aTimer); - void HandleOnLinkPrefixDeprecateTimer(void); - static void HandleOnLinkPrefixDeprecateTimer(Timer &aTimer); - - void DeprecateOnLinkPrefix(void); - void HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); void HandleRouterAdvertisement(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); + void HandleRouterSolicit(const InfraIf::Icmp6Packet &aPacket, const Ip6::Address &aSrcAddress); + void HandleNeighborAdvertisement(const InfraIf::Icmp6Packet &aPacket); bool ShouldProcessPrefixInfoOption(const Ip6::Nd::PrefixInfoOption &aPio, const Ip6::Prefix &aPrefix); bool ShouldProcessRouteInfoOption(const Ip6::Nd::RouteInfoOption &aRio, const Ip6::Prefix &aPrefix); void UpdateDiscoveredPrefixTableOnNetDataChange(void); - void HandleDiscoveredPrefixTableChanged(void); bool NetworkDataContainsOmrPrefix(const Ip6::Prefix &aPrefix) const; + bool NetworkDataContainsUlaRoute(void) const; void UpdateRouterAdvertHeader(const Ip6::Nd::RouterAdvertMessage *aRouterAdvertMessage); bool IsReceivedRouterAdvertFromManager(const Ip6::Nd::RouterAdvertMessage &aRaMessage) const; void ResetDiscoveredPrefixStaleTimer(void); @@ -593,6 +984,9 @@ class RoutingManager : public InstanceLocator static bool IsValidOnLinkPrefix(const Ip6::Nd::PrefixInfoOption &aPio); static bool IsValidOnLinkPrefix(const Ip6::Prefix &aOnLinkPrefix); + using RoutingPolicyTimer = TimerMilliIn; + using DiscoveredPrefixStaleTimer = TimerMilliIn; + // Indicates whether the Routing Manager is running (started). bool mIsRunning; @@ -613,52 +1007,30 @@ class RoutingManager : public InstanceLocator // were advertised as RIO in the last sent RA message. OnMeshPrefixArray mAdvertisedPrefixes; - RoutePreference mRouteInfoOptionPreference; - - // 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; - - bool mIsAdvertisingLocalOnLinkPrefix; + RoutePreference mRioPreference; + bool mUserSetRioPreference; - // The last time when the on-link prefix is advertised with - // non-zero preferred lifetime. - TimeMilli mTimeAdvertisedOnLinkPrefix; - TimerMilli mOnLinkPrefixDeprecateTimer; - - // The NAT64 prefix allocated from the /48 BR ULA prefix. - Ip6::Prefix mLocalNat64Prefix; - - // True if the local NAT64 prefix is advertised in Thread network. - bool mIsAdvertisingLocalNat64Prefix; + OnLinkPrefixManager mOnLinkPrefixManager; 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. - Ip6::Nd::RouterAdvertMessage::Header mRouterAdvertHeader; - TimeMilli mTimeRouterAdvMessageLastUpdate; - bool mLearntRouterAdvMessageFromHost; - - TimerMilli mDiscoveredPrefixStaleTimer; + RoutePublisher mRoutePublisher; - uint32_t mRouterAdvertisementCount; - TimeMilli mLastRouterAdvertisementSendTime; +#if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + Nat64PrefixManager mNat64PrefixManager; +#endif - TimerMilli mRouterSolicitTimer; - TimeMilli mTimeRouterSolicitStart; - uint8_t mRouterSolicitCount; + RaInfo mRaInfo; + RsSender mRsSender; - TimerMilli mRoutingPolicyTimer; + DiscoveredPrefixStaleTimer mDiscoveredPrefixStaleTimer; + RoutingPolicyTimer mRoutingPolicyTimer; }; } // namespace BorderRouter +DefineMapEnum(otBorderRoutingState, BorderRouter::RoutingManager::State); + } // namespace ot #endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE diff --git a/src/core/coap/coap.cpp b/src/core/coap/coap.cpp index f0b966daae6..38e5618581b 100644 --- a/src/core/coap/coap.cpp +++ b/src/core/coap/coap.cpp @@ -54,11 +54,8 @@ CoapBase::CoapBase(Instance &aInstance, Sender aSender) : InstanceLocator(aInstance) , mMessageId(Random::NonCrypto::GetUint16()) , mRetransmissionTimer(aInstance, Coap::HandleRetransmissionTimer, this) - , mContext(nullptr) - , mInterceptor(nullptr) , mResponsesQueue(aInstance) - , mDefaultHandler(nullptr) - , mDefaultHandlerContext(nullptr) + , mResourceHandler(nullptr) , mSender(aSender) #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE , mLastResponse(nullptr) @@ -72,10 +69,7 @@ void CoapBase::ClearRequestsAndResponses(void) mResponsesQueue.DequeueAllResponses(); } -void CoapBase::ClearRequests(const Ip6::Address &aAddress) -{ - ClearRequests(&aAddress); -} +void CoapBase::ClearRequests(const Ip6::Address &aAddress) { ClearRequests(&aAddress); } void CoapBase::ClearRequests(const Ip6::Address *aAddress) { @@ -93,10 +87,7 @@ void CoapBase::ClearRequests(const Ip6::Address *aAddress) } #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE -void CoapBase::AddBlockWiseResource(ResourceBlockWise &aResource) -{ - IgnoreError(mBlockWiseResources.Add(aResource)); -} +void CoapBase::AddBlockWiseResource(ResourceBlockWise &aResource) { IgnoreError(mBlockWiseResources.Add(aResource)); } void CoapBase::RemoveBlockWiseResource(ResourceBlockWise &aResource) { @@ -105,10 +96,7 @@ void CoapBase::RemoveBlockWiseResource(ResourceBlockWise &aResource) } #endif -void CoapBase::AddResource(Resource &aResource) -{ - IgnoreError(mResources.Add(aResource)); -} +void CoapBase::AddResource(Resource &aResource) { IgnoreError(mResources.Add(aResource)); } void CoapBase::RemoveResource(Resource &aResource) { @@ -116,18 +104,6 @@ void CoapBase::RemoveResource(Resource &aResource) aResource.SetNext(nullptr); } -void CoapBase::SetDefaultHandler(RequestHandler aHandler, void *aContext) -{ - mDefaultHandler = aHandler; - mDefaultHandlerContext = aContext; -} - -void CoapBase::SetInterceptor(Interceptor aInterceptor, void *aContext) -{ - mInterceptor = aInterceptor; - mContext = aContext; -} - Message *CoapBase::NewMessage(const Message::Settings &aSettings) { Message *message = nullptr; @@ -139,24 +115,28 @@ Message *CoapBase::NewMessage(const Message::Settings &aSettings) return message; } -Message *CoapBase::NewPriorityConfirmablePostMessage(const char *aUriPath) +Message *CoapBase::NewMessage(void) { return NewMessage(Message::Settings::GetDefault()); } + +Message *CoapBase::NewPriorityMessage(void) { - return InitMessage(NewPriorityMessage(), kTypeConfirmable, aUriPath); + return NewMessage(Message::Settings(Message::kWithLinkSecurity, Message::kPriorityNet)); } -Message *CoapBase::NewConfirmablePostMessage(const char *aUriPath) +Message *CoapBase::NewPriorityConfirmablePostMessage(Uri aUri) { - return InitMessage(NewMessage(), kTypeConfirmable, aUriPath); + return InitMessage(NewPriorityMessage(), kTypeConfirmable, aUri); } -Message *CoapBase::NewPriorityNonConfirmablePostMessage(const char *aUriPath) +Message *CoapBase::NewConfirmablePostMessage(Uri aUri) { return InitMessage(NewMessage(), kTypeConfirmable, aUri); } + +Message *CoapBase::NewPriorityNonConfirmablePostMessage(Uri aUri) { - return InitMessage(NewPriorityMessage(), kTypeNonConfirmable, aUriPath); + return InitMessage(NewPriorityMessage(), kTypeNonConfirmable, aUri); } -Message *CoapBase::NewNonConfirmablePostMessage(const char *aUriPath) +Message *CoapBase::NewNonConfirmablePostMessage(Uri aUri) { - return InitMessage(NewMessage(), kTypeNonConfirmable, aUriPath); + return InitMessage(NewMessage(), kTypeNonConfirmable, aUri); } Message *CoapBase::NewPriorityResponseMessage(const Message &aRequest) @@ -164,18 +144,15 @@ Message *CoapBase::NewPriorityResponseMessage(const Message &aRequest) return InitResponse(NewPriorityMessage(), aRequest); } -Message *CoapBase::NewResponseMessage(const Message &aRequest) -{ - return InitResponse(NewMessage(), aRequest); -} +Message *CoapBase::NewResponseMessage(const Message &aRequest) { return InitResponse(NewMessage(), aRequest); } -Message *CoapBase::InitMessage(Message *aMessage, Type aType, const char *aUriPath) +Message *CoapBase::InitMessage(Message *aMessage, Type aType, Uri aUri) { Error error = kErrorNone; VerifyOrExit(aMessage != nullptr); - SuccessOrExit(error = aMessage->Init(aType, kCodePost, aUriPath)); + SuccessOrExit(error = aMessage->Init(aType, kCodePost, aUri)); SuccessOrExit(error = aMessage->SetPayloadMarker()); exit: @@ -217,19 +194,19 @@ Error CoapBase::Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo } #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE -Error CoapBase::SendMessage(Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, - const TxParameters & aTxParameters, +Error CoapBase::SendMessage(Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, + const TxParameters &aTxParameters, ResponseHandler aHandler, - void * aContext, + void *aContext, otCoapBlockwiseTransmitHook aTransmitHook, otCoapBlockwiseReceiveHook aReceiveHook) #else -Error CoapBase::SendMessage(Message & aMessage, +Error CoapBase::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, - const TxParameters & aTxParameters, + const TxParameters &aTxParameters, ResponseHandler aHandler, - void * aContext) + void *aContext) #endif { Error error; @@ -381,10 +358,15 @@ Error CoapBase::SendMessage(Message & aMessage, return error; } -Error CoapBase::SendMessage(Message & aMessage, +Error CoapBase::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const TxParameters &aTxParameters) +{ + return SendMessage(aMessage, aMessageInfo, aTxParameters, nullptr, nullptr); +} + +Error CoapBase::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, ResponseHandler aHandler, - void * aContext) + void *aContext) { #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE return SendMessage(aMessage, aMessageInfo, TxParameters::GetDefault(), aHandler, aContext, nullptr, nullptr); @@ -393,6 +375,11 @@ Error CoapBase::SendMessage(Message & aMessage, #endif } +Error CoapBase::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + return SendMessage(aMessage, aMessageInfo, nullptr, nullptr); +} + Error CoapBase::SendReset(Message &aRequest, const Ip6::MessageInfo &aMessageInfo) { return SendEmptyMessage(kTypeReset, aRequest, aMessageInfo); @@ -408,6 +395,11 @@ Error CoapBase::SendEmptyAck(const Message &aRequest, const Ip6::MessageInfo &aM return (aRequest.IsConfirmable() ? SendHeaderResponse(aCode, aRequest, aMessageInfo) : kErrorInvalidArgs); } +Error CoapBase::SendEmptyAck(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo) +{ + return SendEmptyAck(aRequest, aMessageInfo, kCodeChanged); +} + Error CoapBase::SendNotFound(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo) { return SendHeaderResponse(kCodeNotFound, aRequest, aMessageInfo); @@ -454,7 +446,6 @@ Error CoapBase::SendHeaderResponse(Message::Code aCode, const Message &aRequest, default: ExitNow(error = kErrorInvalidArgs); - OT_UNREACHABLE_CODE(break); } SuccessOrExit(error = message->SetTokenFromMessage(aRequest)); @@ -521,10 +512,7 @@ void CoapBase::HandleRetransmissionTimer(void) } } - if (nextTime > metadata.mNextTimerShot) - { - nextTime = metadata.mNextTimerShot; - } + nextTime = Min(nextTime, metadata.mNextTimerShot); } if (nextTime < now.GetDistantFuture()) @@ -533,9 +521,9 @@ void CoapBase::HandleRetransmissionTimer(void) } } -void CoapBase::FinalizeCoapTransaction(Message & aRequest, - const Metadata & aMetadata, - Message * aResponse, +void CoapBase::FinalizeCoapTransaction(Message &aRequest, + const Metadata &aMetadata, + Message *aResponse, const Ip6::MessageInfo *aMessageInfo, Error aResult) { @@ -625,9 +613,9 @@ Error CoapBase::CacheLastBlockResponse(Message *aResponse) Error CoapBase::PrepareNextBlockRequest(Message::BlockType aType, bool aMoreBlocks, - Message & aRequestOld, - Message & aRequest, - Message & aMessage) + Message &aRequestOld, + Message &aRequest, + Message &aMessage) { Error error = kErrorNone; bool isOptionSet = false; @@ -685,10 +673,10 @@ Error CoapBase::PrepareNextBlockRequest(Message::BlockType aType, return error; } -Error CoapBase::SendNextBlock1Request(Message & aRequest, - Message & aMessage, +Error CoapBase::SendNextBlock1Request(Message &aRequest, + Message &aMessage, const Ip6::MessageInfo &aMessageInfo, - const Metadata & aCoapMetadata) + const Metadata &aCoapMetadata) { Error error = kErrorNone; Message *request = nullptr; @@ -742,10 +730,10 @@ Error CoapBase::SendNextBlock1Request(Message & aRequest, return error; } -Error CoapBase::SendNextBlock2Request(Message & aRequest, - Message & aMessage, +Error CoapBase::SendNextBlock2Request(Message &aRequest, + Message &aMessage, const Ip6::MessageInfo &aMessageInfo, - const Metadata & aCoapMetadata, + const Metadata &aCoapMetadata, uint32_t aTotalLength, bool aBeginBlock1Transfer) { @@ -804,8 +792,8 @@ Error CoapBase::SendNextBlock2Request(Message & aRequest, return error; } -Error CoapBase::ProcessBlock1Request(Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, +Error CoapBase::ProcessBlock1Request(Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, const ResourceBlockWise &aResource, uint32_t aTotalLength) { @@ -865,12 +853,12 @@ Error CoapBase::ProcessBlock1Request(Message & aMessage, return error; } -Error CoapBase::ProcessBlock2Request(Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, +Error CoapBase::ProcessBlock2Request(Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, const ResourceBlockWise &aResource) { Error error = kErrorNone; - Message * response = nullptr; + Message *response = nullptr; uint8_t buf[kMaxBlockLength] = {0}; uint16_t bufLen = kMaxBlockLength; bool moreBlocks = false; @@ -893,6 +881,8 @@ Error CoapBase::ProcessBlock2Request(Message & aMessage, response->Init(kTypeAck, kCodeContent); response->SetMessageId(aMessage.GetMessageId()); + SuccessOrExit(error = response->SetTokenFromMessage(aMessage)); + VerifyOrExit((bufLen = otCoapBlockSizeFromExponent(aMessage.GetBlockWiseBlockSize())) <= kMaxBlockLength, error = kErrorNoBufs); SuccessOrExit(error = aResource.HandleBlockTransmit(buf, @@ -1011,9 +1001,9 @@ void CoapBase::SendCopy(const Message &aMessage, const Ip6::MessageInfo &aMessag } } -Message *CoapBase::FindRelatedRequest(const Message & aResponse, +Message *CoapBase::FindRelatedRequest(const Message &aResponse, const Ip6::MessageInfo &aMessageInfo, - Metadata & aMetadata) + Metadata &aMetadata) { Message *request = nullptr; @@ -1091,8 +1081,8 @@ void CoapBase::ProcessReceivedResponse(Message &aMessage, const Ip6::MessageInfo bool responseObserve = false; #endif #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE - uint8_t blockOptionType = 0; - uint32_t totalTransfereSize = 0; + uint8_t blockOptionType = 0; + uint32_t totalTransferSize = 0; #endif request = FindRelatedRequest(aMessage, aMessageInfo, metadata); @@ -1191,7 +1181,7 @@ void CoapBase::ProcessReceivedResponse(Message &aMessage, const Ip6::MessageInfo case kOptionSize2: // ToDo: wait for method to read uint option values - totalTransfereSize = 0; + totalTransferSize = 0; break; default: @@ -1222,8 +1212,8 @@ void CoapBase::ProcessReceivedResponse(Message &aMessage, const Ip6::MessageInfo case 2: // Block2 option if (aMessage.GetCode() < kCodeBadRequest && metadata.mBlockwiseReceiveHook != nullptr) { - error = SendNextBlock2Request(*request, aMessage, aMessageInfo, metadata, totalTransfereSize, - false); + error = + SendNextBlock2Request(*request, aMessage, aMessageInfo, metadata, totalTransferSize, false); } if (aMessage.GetCode() >= kCodeBadRequest || metadata.mBlockwiseReceiveHook == nullptr || @@ -1236,7 +1226,7 @@ void CoapBase::ProcessReceivedResponse(Message &aMessage, const Ip6::MessageInfo if (aMessage.GetCode() < kCodeBadRequest && metadata.mBlockwiseReceiveHook != nullptr) { error = - SendNextBlock2Request(*request, aMessage, aMessageInfo, metadata, totalTransfereSize, true); + SendNextBlock2Request(*request, aMessage, aMessageInfo, metadata, totalTransferSize, true); } FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, error); @@ -1249,7 +1239,7 @@ void CoapBase::ProcessReceivedResponse(Message &aMessage, const Ip6::MessageInfo } #else // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE { - FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, kErrorNone); + FinalizeCoapTransaction(*request, metadata, &aMessage, &aMessageInfo, kErrorNone); } #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE } @@ -1302,17 +1292,17 @@ void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo { char uriPath[Message::kMaxReceivedUriPath + 1]; Message *cachedResponse = nullptr; - Error error = kErrorNotFound; + Error error = kErrorNone; #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE Option::Iterator iterator; - char * curUriPath = uriPath; - uint8_t blockOptionType = 0; - uint32_t totalTransfereSize = 0; + char *curUriPath = uriPath; + uint8_t blockOptionType = 0; + uint32_t totalTransferSize = 0; #endif - if (mInterceptor != nullptr) + if (mInterceptor.IsSet()) { - SuccessOrExit(error = mInterceptor(aMessage, aMessageInfo, mContext)); + SuccessOrExit(error = mInterceptor.Invoke(aMessage, aMessageInfo)); } switch (mResponsesQueue.GetMatchedResponseCopy(aMessage, aMessageInfo, &cachedResponse)) @@ -1320,10 +1310,10 @@ void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo case kErrorNone: cachedResponse->Finish(); error = Send(*cachedResponse, aMessageInfo); - - OT_FALL_THROUGH; + ExitNow(); case kErrorNoBufs: + error = kErrorNoBufs; ExitNow(); case kErrorNotFound: @@ -1360,7 +1350,7 @@ void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo case kOptionSize1: // ToDo: wait for method to read uint option values - totalTransfereSize = 0; + totalTransferSize = 0; break; default: @@ -1386,7 +1376,7 @@ void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo case 1: if (resource.mReceiveHook != nullptr) { - switch (ProcessBlock1Request(aMessage, aMessageInfo, resource, totalTransfereSize)) + switch (ProcessBlock1Request(aMessage, aMessageInfo, resource, totalTransferSize)) { case kErrorNone: resource.HandleRequest(aMessage, aMessageInfo); @@ -1433,6 +1423,12 @@ void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo SuccessOrExit(error = aMessage.ReadUriPathOptions(uriPath)); #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE + if ((mResourceHandler != nullptr) && mResourceHandler(*this, uriPath, aMessage, aMessageInfo)) + { + error = kErrorNone; + ExitNow(); + } + for (const Resource &resource : mResources) { if (strcmp(resource.mUriPath, uriPath) == 0) @@ -1443,12 +1439,15 @@ void CoapBase::ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo } } - if (mDefaultHandler) + if (mDefaultHandler.IsSet()) { - mDefaultHandler(mDefaultHandlerContext, &aMessage, &aMessageInfo); + mDefaultHandler.Invoke(&aMessage, &aMessageInfo); error = kErrorNone; + ExitNow(); } + error = kErrorNotFound; + exit: if (error != kErrorNone) @@ -1482,9 +1481,9 @@ ResponsesQueue::ResponsesQueue(Instance &aInstance) { } -Error ResponsesQueue::GetMatchedResponseCopy(const Message & aRequest, +Error ResponsesQueue::GetMatchedResponseCopy(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo, - Message ** aResponse) + Message **aResponse) { Error error = kErrorNone; const Message *cacheResponse; @@ -1523,11 +1522,11 @@ const Message *ResponsesQueue::FindMatchedResponse(const Message &aRequest, cons return response; } -void ResponsesQueue::EnqueueResponse(Message & aMessage, +void ResponsesQueue::EnqueueResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, - const TxParameters & aTxParameters) + const TxParameters &aTxParameters) { - Message * responseCopy; + Message *responseCopy; ResponseMetadata metadata; metadata.mDequeueTime = TimerMilli::GetNow() + aTxParameters.CalculateExchangeLifetime(); @@ -1552,7 +1551,7 @@ void ResponsesQueue::EnqueueResponse(Message & aMessage, void ResponsesQueue::UpdateQueue(void) { uint16_t msgCount = 0; - Message * earliestMsg = nullptr; + Message *earliestMsg = nullptr; TimeMilli earliestDequeueTime(0); // Check the number of messages in the queue and if number is at @@ -1580,15 +1579,9 @@ void ResponsesQueue::UpdateQueue(void) } } -void ResponsesQueue::DequeueResponse(Message &aMessage) -{ - mQueue.DequeueAndFree(aMessage); -} +void ResponsesQueue::DequeueResponse(Message &aMessage) { mQueue.DequeueAndFree(aMessage); } -void ResponsesQueue::DequeueAllResponses(void) -{ - mQueue.DequeueAndFreeAll(); -} +void ResponsesQueue::DequeueAllResponses(void) { mQueue.DequeueAndFreeAll(); } void ResponsesQueue::HandleTimer(Timer &aTimer) { @@ -1612,10 +1605,7 @@ void ResponsesQueue::HandleTimer(void) continue; } - if (metadata.mDequeueTime < nextDequeueTime) - { - nextDequeueTime = metadata.mDequeueTime; - } + nextDequeueTime = Min(nextDequeueTime, metadata.mDequeueTime); } if (nextDequeueTime < now.GetDistantFuture()) @@ -1653,7 +1643,7 @@ bool TxParameters::IsValid(void) const if ((mAckRandomFactorDenominator > 0) && (mAckRandomFactorNumerator >= mAckRandomFactorDenominator) && (mAckTimeout >= OT_COAP_MIN_ACK_TIMEOUT) && (mMaxRetransmit <= OT_COAP_MAX_RETRANSMIT)) { - // Calulate exchange lifetime step by step and verify no overflow. + // Calculate exchange lifetime step by step and verify no overflow. uint32_t tmp = Multiply(mAckTimeout, (1U << (mMaxRetransmit + 1)) - 1); tmp = Multiply(tmp, mAckRandomFactorNumerator); @@ -1677,10 +1667,7 @@ uint32_t TxParameters::CalculateExchangeLifetime(void) const return CalculateSpan(mMaxRetransmit) + 2 * kDefaultMaxLatency + mAckTimeout; } -uint32_t TxParameters::CalculateMaxTransmitWait(void) const -{ - return CalculateSpan(mMaxRetransmit + 1); -} +uint32_t TxParameters::CalculateMaxTransmitWait(void) const { return CalculateSpan(mMaxRetransmit + 1); } uint32_t TxParameters::CalculateSpan(uint8_t aMaxRetx) const { @@ -1695,13 +1682,30 @@ const otCoapTxParameters TxParameters::kDefaultTxParameters = { kDefaultMaxRetransmit, }; +//---------------------------------------------------------------------------------------------------------------------- + +Resource::Resource(const char *aUriPath, RequestHandler aHandler, void *aContext) +{ + mUriPath = aUriPath; + mHandler = aHandler; + mContext = aContext; + mNext = nullptr; +} + +Resource::Resource(Uri aUri, RequestHandler aHandler, void *aContext) + : Resource(PathForUri(aUri), aHandler, aContext) +{ +} + +//---------------------------------------------------------------------------------------------------------------------- + Coap::Coap(Instance &aInstance) : CoapBase(aInstance, &Coap::Send) , mSocket(aInstance) { } -Error Coap::Start(uint16_t aPort, otNetifIdentifier aNetifIdentifier) +Error Coap::Start(uint16_t aPort, Ip6::NetifIdentifier aNetifIdentifier) { Error error = kErrorNone; bool socketOpened = false; diff --git a/src/core/coap/coap.hpp b/src/core/coap/coap.hpp index aea240eac84..b5d05a669fb 100644 --- a/src/core/coap/coap.hpp +++ b/src/core/coap/coap.hpp @@ -35,6 +35,7 @@ #include "coap/coap_message.hpp" #include "common/as_core_type.hpp" +#include "common/callback.hpp" #include "common/debug.hpp" #include "common/linked_list.hpp" #include "common/locator.hpp" @@ -44,6 +45,7 @@ #include "net/ip6.hpp" #include "net/netif.hpp" #include "net/udp6.hpp" +#include "thread/uri_paths.hpp" /** * @file @@ -150,14 +152,19 @@ class Resource : public otCoapResource, public LinkedListEntry * @param[in] aUriPath A pointer to a null-terminated string for the URI path. * @param[in] aHandler A function pointer that is called when receiving a CoAP message for @p aUriPath. * @param[in] aContext A pointer to arbitrary context information. + * */ - Resource(const char *aUriPath, RequestHandler aHandler, void *aContext) - { - mUriPath = aUriPath; - mHandler = aHandler; - mContext = aContext; - mNext = nullptr; - } + Resource(const char *aUriPath, RequestHandler aHandler, void *aContext); + + /** + * This constructor initializes the resource. + * + * @param[in] aUri A Thread URI. + * @param[in] aHandler A function pointer that is called when receiving a CoAP message for the URI. + * @param[in] aContext A pointer to arbitrary context information. + * + */ + Resource(Uri aUri, RequestHandler aHandler, void *aContext); /** * This method returns a pointer to the URI path. @@ -195,9 +202,9 @@ class ResourceBlockWise : public otCoapBlockwiseResource * @param[in] aTransmitHook A function pointer that is called when transmitting a CoAP block message from @p * aUriPath. */ - ResourceBlockWise(const char * aUriPath, + ResourceBlockWise(const char *aUriPath, otCoapRequestHandler aHandler, - void * aContext, + void *aContext, otCoapBlockwiseReceiveHook aReceiveHook, otCoapBlockwiseTransmitHook aTransmitHook) { @@ -432,7 +439,7 @@ class CoapBase : public InstanceLocator, private NonCopyable * @param[in] aContext A pointer to arbitrary context information. May be `nullptr` if not used. * */ - void SetDefaultHandler(RequestHandler aHandler, void *aContext); + void SetDefaultHandler(RequestHandler aHandler, void *aContext) { mDefaultHandler.Set(aHandler, aContext); } /** * This method allocates a new message with a CoAP header. @@ -442,7 +449,15 @@ class CoapBase : public InstanceLocator, private NonCopyable * @returns A pointer to the message or `nullptr` if failed to allocate message. * */ - Message *NewMessage(const Message::Settings &aSettings = Message::Settings::GetDefault()); + Message *NewMessage(const Message::Settings &aSettings); + + /** + * This method allocates a new message with a CoAP header with default settings. + * + * @returns A pointer to the message or `nullptr` if failed to allocate message. + * + */ + Message *NewMessage(void); /** * This method allocates a new message with a CoAP header that has Network Control priority level. @@ -450,10 +465,7 @@ class CoapBase : public InstanceLocator, private NonCopyable * @returns A pointer to the message or `nullptr` if failed to allocate message. * */ - Message *NewPriorityMessage(void) - { - return NewMessage(Message::Settings(Message::kWithLinkSecurity, Message::kPriorityNet)); - } + Message *NewPriorityMessage(void); /** * This method allocates and initializes a new CoAP Confirmable Post message with Network Control priority level. @@ -463,58 +475,58 @@ class CoapBase : public InstanceLocator, private NonCopyable * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and * remove the payload marker when there is no payload. * - * @param[in] aUriPath The URI path string. + * @param[in] aUri The URI. * * @returns A pointer to the message or `nullptr` if failed to allocate message. * */ - Message *NewPriorityConfirmablePostMessage(const char *aUriPath); + Message *NewPriorityConfirmablePostMessage(Uri aUri); /** * This method allocates and initializes a new CoAP Confirmable Post message with normal priority level. * - * The CoAP header is initialized as `kTypeConfirmable` and `kCodePost` with a given URI path and a randomly + * The CoAP header is initialized as `kTypeConfirmable` and `kCodePost` with a given URI and a randomly * generated token (of default length). This method also sets the payload marker (calling `SetPayloadMarker()`). * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and * remove the payload marker when there is no payload. * - * @param[in] aUriPath The URI path string. + * @param[in] aUri The URI. * * @returns A pointer to the message or `nullptr` if failed to allocate message. * */ - Message *NewConfirmablePostMessage(const char *aUriPath); + Message *NewConfirmablePostMessage(Uri aUri); /** * This method allocates and initializes a new CoAP Non-confirmable Post message with Network Control priority * level. * - * The CoAP header is initialized as `kTypeNonConfirmable` and `kCodePost` with a given URI path and a randomly + * The CoAP header is initialized as `kTypeNonConfirmable` and `kCodePost` with a given URI and a randomly * generated token (of default length). This method also sets the payload marker (calling `SetPayloadMarker()`). * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and * remove the payload marker when there is no payload. * - * @param[in] aUriPath The URI path string. + * @param[in] aUri The URI. * * @returns A pointer to the message or `nullptr` if failed to allocate message. * */ - Message *NewPriorityNonConfirmablePostMessage(const char *aUriPath); + Message *NewPriorityNonConfirmablePostMessage(Uri aUri); /** * This method allocates and initializes a new CoAP Non-confirmable Post message with normal priority level. * - * The CoAP header is initialized as `kTypeNonConfirmable` and `kCodePost` with a given URI path and a randomly + * The CoAP header is initialized as `kTypeNonConfirmable` and `kCodePost` with a given URI and a randomly * generated token (of default length). This method also sets the payload marker (calling `SetPayloadMarker()`). * Even if message has no payload, calling `SetPayloadMarker()` is harmless, since `SendMessage()` will check and * remove the payload marker when there is no payload. * - * @param[in] aUriPath The URI path string. + * @param[in] aUri The URI. * * @returns A pointer to the message or `nullptr` if failed to allocate message. * */ - Message *NewNonConfirmablePostMessage(const char *aUriPath); + Message *NewNonConfirmablePostMessage(Uri aUri); /** * This method allocates and initializes a new CoAP response message with Network Control priority level for a @@ -550,7 +562,7 @@ class CoapBase : public InstanceLocator, private NonCopyable * * If a response for a request is expected, respective function and context information should be provided. * If no response is expected, these arguments should be NULL pointers. - * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message. + * If Message ID was not set in the header (equal to 0), this method will assign unique Message ID to the message. * * @param[in] aMessage A reference to the message to send. * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. @@ -564,11 +576,11 @@ class CoapBase : public InstanceLocator, private NonCopyable * @retval kErrorNoBufs Failed to allocate retransmission data. * */ - Error SendMessage(Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, - const TxParameters & aTxParameters, + Error SendMessage(Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, + const TxParameters &aTxParameters, otCoapResponseHandler aHandler = nullptr, - void * aContext = nullptr, + void *aContext = nullptr, otCoapBlockwiseTransmitHook aTransmitHook = nullptr, otCoapBlockwiseReceiveHook aReceiveHook = nullptr); #else // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE @@ -578,7 +590,7 @@ class CoapBase : public InstanceLocator, private NonCopyable * * If a response for a request is expected, respective function and context information should be provided. * If no response is expected, these arguments should be `nullptr` pointers. - * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message. + * If Message ID was not set in the header (equal to 0), this method will assign unique Message ID to the message. * * @param[in] aMessage A reference to the message to send. * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. @@ -590,19 +602,31 @@ class CoapBase : public InstanceLocator, private NonCopyable * @retval kErrorNoBufs Insufficient buffers available to send the CoAP message. * */ - Error SendMessage(Message & aMessage, + Error SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, - const TxParameters & aTxParameters, - ResponseHandler aHandler = nullptr, - void * aContext = nullptr); + const TxParameters &aTxParameters, + ResponseHandler aHandler, + void *aContext); #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE + /** + * This method sends a CoAP message with custom transmission parameters. + * + * If Message ID was not set in the header (equal to 0), this method will assign unique Message ID to the message. + * + * @param[in] aMessage A reference to the message to send. + * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. + * @param[in] aTxParameters A reference to transmission parameters for this message. + * + * @retval kErrorNone Successfully sent CoAP message. + * @retval kErrorNoBufs Insufficient buffers available to send the CoAP message. + * + */ + Error SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, const TxParameters &aTxParameters); /** * This method sends a CoAP message with default transmission parameters. * - * If a response for a request is expected, respective function and context information should be provided. - * If no response is expected, these arguments should be `nullptr` pointers. - * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message. + * If Message ID was not set in the header (equal to 0), this method will assign unique Message ID to the message. * * @param[in] aMessage A reference to the message to send. * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. @@ -613,10 +637,24 @@ class CoapBase : public InstanceLocator, private NonCopyable * @retval kErrorNoBufs Insufficient buffers available to send the CoAP response. * */ - Error SendMessage(Message & aMessage, + Error SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, - ResponseHandler aHandler = nullptr, - void * aContext = nullptr); + ResponseHandler aHandler, + void *aContext); + + /** + * This method sends a CoAP message with default transmission parameters. + * + * If Message ID was not set in the header (equal to 0), this method will assign unique Message ID to the message. + * + * @param[in] aMessage A reference to the message to send. + * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. + * + * @retval kErrorNone Successfully sent CoAP message. + * @retval kErrorNoBufs Insufficient buffers available to send the CoAP response. + * + */ + Error SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); /** * This method sends a CoAP reset message. @@ -670,7 +708,20 @@ class CoapBase : public InstanceLocator, private NonCopyable * @retval kErrorInvalidArgs The @p aRequest header is not of confirmable type. * */ - Error SendEmptyAck(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo, Code aCode = kCodeChanged); + Error SendEmptyAck(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo, Code aCode); + + /** + * This method sends a CoAP ACK message on which a dummy CoAP response is piggybacked. + * + * @param[in] aRequest A reference to the CoAP Message that was used in CoAP request. + * @param[in] aMessageInfo The message info corresponding to the CoAP request. + * + * @retval kErrorNone Successfully enqueued the CoAP response message. + * @retval kErrorNoBufs Insufficient buffers available to send the CoAP response. + * @retval kErrorInvalidArgs The @p aRequest header is not of confirmable type. + * + */ + Error SendEmptyAck(const Message &aRequest, const Ip6::MessageInfo &aMessageInfo); /** * This method sends a header-only CoAP message to indicate no resource matched for the request. @@ -723,7 +774,7 @@ class CoapBase : public InstanceLocator, private NonCopyable * @param[in] aContext A pointer to arbitrary context information. * */ - void SetInterceptor(Interceptor aInterceptor, void *aContext); + void SetInterceptor(Interceptor aInterceptor, void *aContext) { mInterceptor.Set(aInterceptor, aContext); } /** * This method returns a reference to the request message list. @@ -742,6 +793,26 @@ class CoapBase : public InstanceLocator, private NonCopyable const MessageQueue &GetCachedResponses(void) const { return mResponsesQueue.GetResponses(); } protected: + /** + * This type defines function pointer to handle a CoAP resource. + * + * When processing a received request, this handler is called first with the URI path before checking the list of + * added `Resource` entries to match against the URI path. + * + * @param[in] aCoapBase A reference the CoAP agent. + * @param[in] aUriPath The URI Path string. + * @param[in] aMessage The received message. + * @param[in] aMessageInfo The message info associated with @p aMessage. + * + * @retval TRUE Indicates that the URI path was known and the message was processed by the handler. + * @retval FALSE Indicates that URI path was not known and the message was not processed by the handler. + * + */ + typedef bool (*ResourceHandler)(CoapBase &aCoapBase, + const char *aUriPath, + Message &aMessage, + const Ip6::MessageInfo &aMessageInfo); + /** * This function pointer is called to send a CoAP message. * @@ -774,6 +845,14 @@ class CoapBase : public InstanceLocator, private NonCopyable */ void Receive(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + /** + * This method sets the resource handler function. + * + * @param[in] aResourceHandler The resource handler function pointer. + * + */ + void SetResourceHandler(ResourceHandler aHandler) { mResourceHandler = aHandler; } + private: struct Metadata { @@ -785,7 +864,7 @@ class CoapBase : public InstanceLocator, private NonCopyable Ip6::Address mDestinationAddress; // IPv6 address of the message destination. uint16_t mDestinationPort; // UDP port of the message destination. ResponseHandler mResponseHandler; // A function pointer that is called on response reception. - void * mResponseContext; // A pointer to arbitrary context information. + void *mResponseContext; // A pointer to arbitrary context information. TimeMilli mNextTimerShot; // Time when the timer should shoot for this message. uint32_t mRetransmissionTimeout; // Delay that is applied to next retransmission. uint8_t mRetransmissionsRemaining; // Number of retransmissions remaining. @@ -807,7 +886,7 @@ class CoapBase : public InstanceLocator, private NonCopyable #endif }; - Message *InitMessage(Message *aMessage, Type aType, const char *aUriPath); + Message *InitMessage(Message *aMessage, Type aType, Uri aUri); Message *InitResponse(Message *aMessage, const Message &aResponse); static void HandleRetransmissionTimer(Timer &aTimer); @@ -817,9 +896,9 @@ class CoapBase : public InstanceLocator, private NonCopyable Message *CopyAndEnqueueMessage(const Message &aMessage, uint16_t aCopyLength, const Metadata &aMetadata); void DequeueMessage(Message &aMessage); Message *FindRelatedRequest(const Message &aResponse, const Ip6::MessageInfo &aMessageInfo, Metadata &aMetadata); - void FinalizeCoapTransaction(Message & aRequest, - const Metadata & aMetadata, - Message * aResponse, + void FinalizeCoapTransaction(Message &aRequest, + const Metadata &aMetadata, + Message *aResponse, const Ip6::MessageInfo *aMessageInfo, Error aResult); @@ -829,29 +908,29 @@ class CoapBase : public InstanceLocator, private NonCopyable Error PrepareNextBlockRequest(Message::BlockType aType, bool aMoreBlocks, - Message & aRequestOld, - Message & aRequest, - Message & aMessage); - Error ProcessBlock1Request(Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, + Message &aRequestOld, + Message &aRequest, + Message &aMessage); + Error ProcessBlock1Request(Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, const ResourceBlockWise &aResource, uint32_t aTotalLength); - Error ProcessBlock2Request(Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, + Error ProcessBlock2Request(Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, const ResourceBlockWise &aResource); #endif void ProcessReceivedRequest(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void ProcessReceivedResponse(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE - Error SendNextBlock1Request(Message & aRequest, - Message & aMessage, + Error SendNextBlock1Request(Message &aRequest, + Message &aMessage, const Ip6::MessageInfo &aMessageInfo, - const Metadata & aCoapMetadata); - Error SendNextBlock2Request(Message & aRequest, - Message & aMessage, + const Metadata &aCoapMetadata); + Error SendNextBlock2Request(Message &aRequest, + Message &aMessage, const Ip6::MessageInfo &aMessageInfo, - const Metadata & aCoapMetadata, + const Metadata &aCoapMetadata, uint32_t aTotalLength, bool aBeginBlock1Transfer); #endif @@ -866,18 +945,18 @@ class CoapBase : public InstanceLocator, private NonCopyable LinkedList mResources; - void * mContext; - Interceptor mInterceptor; - ResponsesQueue mResponsesQueue; + Callback mInterceptor; + ResponsesQueue mResponsesQueue; + + Callback mDefaultHandler; - RequestHandler mDefaultHandler; - void * mDefaultHandlerContext; + ResourceHandler mResourceHandler; const Sender mSender; #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE LinkedList mBlockWiseResources; - Message * mLastResponse; + Message *mLastResponse; #endif }; @@ -906,7 +985,7 @@ class Coap : public CoapBase * @retval kErrorFailed Failed to start CoAP agent. * */ - Error Start(uint16_t aPort, otNetifIdentifier aNetifIdentifier = OT_NETIF_UNSPECIFIED); + Error Start(uint16_t aPort, Ip6::NetifIdentifier aNetifIdentifier = Ip6::kNetifUnspecified); /** * This method stops the CoAP service. diff --git a/src/core/coap/coap_message.cpp b/src/core/coap/coap_message.cpp index bd1c152b563..e4ef990880a 100644 --- a/src/core/coap/coap_message.cpp +++ b/src/core/coap/coap_message.cpp @@ -67,32 +67,26 @@ void Message::Init(Type aType, Code aCode) SetCode(aCode); } -Error Message::Init(Type aType, Code aCode, const char *aUriPath) +Error Message::Init(Type aType, Code aCode, Uri aUri) { Error error; Init(aType, aCode); SuccessOrExit(error = GenerateRandomToken(kDefaultTokenLength)); - SuccessOrExit(error = AppendUriPathOptions(aUriPath)); + SuccessOrExit(error = AppendUriPathOptions(PathForUri(aUri))); exit: return error; } -Error Message::InitAsPost(const Ip6::Address &aDestination, const char *aUriPath) +Error Message::InitAsPost(const Ip6::Address &aDestination, Uri aUri) { - return Init(aDestination.IsMulticast() ? kTypeNonConfirmable : kTypeConfirmable, kCodePost, aUriPath); + return Init(aDestination.IsMulticast() ? kTypeNonConfirmable : kTypeConfirmable, kCodePost, aUri); } -bool Message::IsConfirmablePostRequest(void) const -{ - return IsConfirmable() && IsPostRequest(); -} +bool Message::IsConfirmablePostRequest(void) const { return IsConfirmable() && IsPostRequest(); } -bool Message::IsNonConfirmablePostRequest(void) const -{ - return IsNonConfirmable() && IsPostRequest(); -} +bool Message::IsNonConfirmablePostRequest(void) const { return IsNonConfirmable() && IsPostRequest(); } void Message::Finish(void) { @@ -225,7 +219,7 @@ Error Message::AppendUriPathOptions(const char *aUriPath) Error Message::ReadUriPathOptions(char (&aUriPath)[kMaxReceivedUriPath + 1]) const { - char * curUriPath = aUriPath; + char *curUriPath = aUriPath; Error error = kErrorNone; Option::Iterator iterator; @@ -456,15 +450,9 @@ const char *Message::CodeToString(void) const } #endif // OPENTHREAD_CONFIG_COAP_API_ENABLE -Message::Iterator MessageQueue::begin(void) -{ - return Message::Iterator(GetHead()); -} +Message::Iterator MessageQueue::begin(void) { return Message::Iterator(GetHead()); } -Message::ConstIterator MessageQueue::begin(void) const -{ - return Message::ConstIterator(GetHead()); -} +Message::ConstIterator MessageQueue::begin(void) const { return Message::ConstIterator(GetHead()); } Error Option::Iterator::Init(const Message &aMessage) { diff --git a/src/core/coap/coap_message.hpp b/src/core/coap/coap_message.hpp index 1f4a86b06e5..40c9c97854e 100644 --- a/src/core/coap/coap_message.hpp +++ b/src/core/coap/coap_message.hpp @@ -47,6 +47,7 @@ #include "net/ip6.hpp" #include "net/ip6_address.hpp" #include "net/udp6.hpp" +#include "thread/uri_paths.hpp" namespace ot { @@ -208,13 +209,13 @@ class Message : public ot::Message * * @param[in] aType The Type value. * @param[in] aCode The Code value. - * @param[in] aUriPath A pointer to a null-terminated string. + * @param[in] aUri The URI. * * @retval kErrorNone Successfully appended the option. * @retval kErrorNoBufs The option length exceeds the buffer size. * */ - Error Init(Type aType, Code aCode, const char *aUriPath); + Error Init(Type aType, Code aCode, Uri aUri); /** * This method initializes the CoAP header as `kCodePost` with a given URI Path with its type determined from a @@ -222,13 +223,13 @@ class Message : public ot::Message * * @param[in] aDestination The message destination IPv6 address used to determine the CoAP type, * `kTypeNonConfirmable` if multicast address, `kTypeConfirmable` otherwise. - * @param[in] aUriPath A pointer to a null-terminated string. + * @param[in] aUri The URI. * * @retval kErrorNone Successfully appended the option. * @retval kErrorNoBufs The option length exceeds the buffer size. * */ - Error InitAsPost(const Ip6::Address &aDestination, const char *aUriPath); + Error InitAsPost(const Ip6::Address &aDestination, Uri aUri); /** * This method writes header to the message. This must be called before sending the message. @@ -633,7 +634,7 @@ class Message : public ot::Message void SetBlockWiseBlockNumber(uint32_t aBlockNumber) { GetHelpData().mBlockWiseData.mBlockNumber = aBlockNumber; } /** - * This method sets the More Blocks falg in the message HelpData. + * This method sets the More Blocks flag in the message HelpData. * * @param[in] aMoreBlocks TRUE or FALSE. * @@ -944,14 +945,14 @@ class Message : public ot::Message Message *operator->(void) { return static_cast(ot::Message::Iterator::operator->()); } }; - static_assert(sizeof(HelpData) <= sizeof(Ip6::Header) + sizeof(Ip6::HopByHopHeader) + sizeof(Ip6::OptionMpl) + + static_assert(sizeof(HelpData) <= sizeof(Ip6::Header) + sizeof(Ip6::HopByHopHeader) + sizeof(Ip6::MplOption) + sizeof(Ip6::Udp::Header), "HelpData size exceeds the size of the reserved region in the message"); const HelpData &GetHelpData(void) const { static_assert(sizeof(HelpData) + kHelpDataAlignment <= kHeadBufferDataSize, - "Insufficient buffer size for CoAP processing!"); + "Insufficient buffer size for CoAP processing! Increase OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE."); return *static_cast(OT_ALIGN(GetFirstData(), kHelpDataAlignment)); } @@ -1240,10 +1241,7 @@ DefineMapEnum(otCoapCode, Coap::Code); * @returns A reference to `Coap::Message` matching @p aMessage. * */ -inline Coap::Message &AsCoapMessage(otMessage *aMessage) -{ - return *static_cast(aMessage); -} +inline Coap::Message &AsCoapMessage(otMessage *aMessage) { return *static_cast(aMessage); } /** * This method casts an `otMessage` pointer to a `Coap::Message` reference. @@ -1253,10 +1251,7 @@ inline Coap::Message &AsCoapMessage(otMessage *aMessage) * @returns A reference to `Coap::Message` matching @p aMessage. * */ -inline Coap::Message *AsCoapMessagePtr(otMessage *aMessage) -{ - return static_cast(aMessage); -} +inline Coap::Message *AsCoapMessagePtr(otMessage *aMessage) { return static_cast(aMessage); } /** * This method casts an `otMessage` pointer to a `Coap::Message` pointer. diff --git a/src/core/coap/coap_secure.cpp b/src/core/coap/coap_secure.cpp index d15873147f5..3f5730afb1c 100644 --- a/src/core/coap/coap_secure.cpp +++ b/src/core/coap/coap_secure.cpp @@ -50,8 +50,6 @@ RegisterLogModule("CoapSecure"); CoapSecure::CoapSecure(Instance &aInstance, bool aLayerTwoSecurity) : CoapBase(aInstance, &CoapSecure::Send) , mDtls(aInstance, aLayerTwoSecurity) - , mConnectedCallback(nullptr) - , mConnectedContext(nullptr) , mTransmitTask(aInstance, CoapSecure::HandleTransmit, this) { } @@ -60,8 +58,7 @@ Error CoapSecure::Start(uint16_t aPort) { Error error = kErrorNone; - mConnectedCallback = nullptr; - mConnectedContext = nullptr; + mConnectedCallback.Clear(); SuccessOrExit(error = mDtls.Open(&CoapSecure::HandleDtlsReceive, &CoapSecure::HandleDtlsConnected, this)); SuccessOrExit(error = mDtls.Bind(aPort)); @@ -74,8 +71,7 @@ Error CoapSecure::Start(MeshCoP::Dtls::TransportCallback aCallback, void *aConte { Error error = kErrorNone; - mConnectedCallback = nullptr; - mConnectedContext = nullptr; + mConnectedCallback.Clear(); SuccessOrExit(error = mDtls.Open(&CoapSecure::HandleDtlsReceive, &CoapSecure::HandleDtlsConnected, this)); SuccessOrExit(error = mDtls.Bind(aCallback, aContext)); @@ -94,8 +90,7 @@ void CoapSecure::Stop(void) Error CoapSecure::Connect(const Ip6::SockAddr &aSockAddr, ConnectedCallback aCallback, void *aContext) { - mConnectedCallback = aCallback; - mConnectedContext = aContext; + mConnectedCallback.Set(aCallback, aContext); return mDtls.Connect(aSockAddr); } @@ -110,9 +105,9 @@ void CoapSecure::SetPsk(const MeshCoP::JoinerPskd &aPskd) } #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE -Error CoapSecure::SendMessage(Message & aMessage, +Error CoapSecure::SendMessage(Message &aMessage, ResponseHandler aHandler, - void * aContext, + void *aContext, otCoapBlockwiseTransmitHook aTransmitHook, otCoapBlockwiseReceiveHook aReceiveHook) { @@ -127,10 +122,10 @@ Error CoapSecure::SendMessage(Message & aMessage, return error; } -Error CoapSecure::SendMessage(Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, +Error CoapSecure::SendMessage(Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, ResponseHandler aHandler, - void * aContext, + void *aContext, otCoapBlockwiseTransmitHook aTransmitHook, otCoapBlockwiseReceiveHook aReceiveHook) { @@ -150,10 +145,10 @@ Error CoapSecure::SendMessage(Message &aMessage, ResponseHandler aHandler, void return error; } -Error CoapSecure::SendMessage(Message & aMessage, +Error CoapSecure::SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, ResponseHandler aHandler, - void * aContext) + void *aContext) { return CoapBase::SendMessage(aMessage, aMessageInfo, aHandler, aContext); } @@ -174,13 +169,7 @@ void CoapSecure::HandleDtlsConnected(void *aContext, bool aConnected) return static_cast(aContext)->HandleDtlsConnected(aConnected); } -void CoapSecure::HandleDtlsConnected(bool aConnected) -{ - if (mConnectedCallback != nullptr) - { - mConnectedCallback(aConnected, mConnectedContext); - } -} +void CoapSecure::HandleDtlsConnected(bool aConnected) { mConnectedCallback.InvokeIfSet(aConnected); } void CoapSecure::HandleDtlsReceive(void *aContext, uint8_t *aBuf, uint16_t aLength) { diff --git a/src/core/coap/coap_secure.hpp b/src/core/coap/coap_secure.hpp index be8caf67b6d..43b2d02eef5 100644 --- a/src/core/coap/coap_secure.hpp +++ b/src/core/coap/coap_secure.hpp @@ -34,6 +34,7 @@ #if OPENTHREAD_CONFIG_DTLS_ENABLE #include "coap/coap.hpp" +#include "common/callback.hpp" #include "meshcop/dtls.hpp" #include "meshcop/meshcop.hpp" @@ -101,8 +102,7 @@ class CoapSecure : public CoapBase */ void SetConnectedCallback(ConnectedCallback aCallback, void *aContext) { - mConnectedCallback = aCallback; - mConnectedContext = aContext; + mConnectedCallback.Set(aCallback, aContext); } /** @@ -267,8 +267,7 @@ class CoapSecure : public CoapBase */ void SetClientConnectedCallback(ConnectedCallback aCallback, void *aContext) { - mConnectedCallback = aCallback; - mConnectedContext = aContext; + mConnectedCallback.Set(aCallback, aContext); } /** @@ -301,9 +300,9 @@ class CoapSecure : public CoapBase * @retval kErrorInvalidState DTLS connection was not initialized. * */ - Error SendMessage(Message & aMessage, + Error SendMessage(Message &aMessage, ResponseHandler aHandler = nullptr, - void * aContext = nullptr, + void *aContext = nullptr, otCoapBlockwiseTransmitHook aTransmitHook = nullptr, otCoapBlockwiseReceiveHook aReceiveHook = nullptr); @@ -326,10 +325,10 @@ class CoapSecure : public CoapBase * @retval kErrorInvalidState DTLS connection was not initialized. * */ - Error SendMessage(Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, + Error SendMessage(Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, ResponseHandler aHandler = nullptr, - void * aContext = nullptr, + void *aContext = nullptr, otCoapBlockwiseTransmitHook aTransmitHook = nullptr, otCoapBlockwiseReceiveHook aReceiveHook = nullptr); #else // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE @@ -368,10 +367,10 @@ class CoapSecure : public CoapBase * @retval kErrorInvalidState DTLS connection was not initialized. * */ - Error SendMessage(Message & aMessage, + Error SendMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo, ResponseHandler aHandler = nullptr, - void * aContext = nullptr); + void *aContext = nullptr); #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE /** @@ -410,11 +409,10 @@ class CoapSecure : public CoapBase static void HandleTransmit(Tasklet &aTasklet); void HandleTransmit(void); - MeshCoP::Dtls mDtls; - ConnectedCallback mConnectedCallback; - void * mConnectedContext; - ot::MessageQueue mTransmitQueue; - TaskletContext mTransmitTask; + MeshCoP::Dtls mDtls; + Callback mConnectedCallback; + ot::MessageQueue mTransmitQueue; + TaskletContext mTransmitTask; }; } // namespace Coap diff --git a/src/core/common/appender.cpp b/src/core/common/appender.cpp index caa46e09182..150429496f6 100644 --- a/src/core/common/appender.cpp +++ b/src/core/common/appender.cpp @@ -84,7 +84,7 @@ uint16_t Appender::GetAppendedLength(void) const return length; } -void Appender::GetAsData(Data &aData) +void Appender::GetAsData(Data &aData) const { aData.Init(mShared.mFrameBuilder.GetBytes(), mShared.mFrameBuilder.GetLength()); } diff --git a/src/core/common/appender.hpp b/src/core/common/appender.hpp index 9154c5339da..5260ba83564 100644 --- a/src/core/common/appender.hpp +++ b/src/core/common/appender.hpp @@ -142,7 +142,7 @@ class Appender * @returns The `Message` instance associated with `Appender`. * */ - Message &GetMessage(void) { return *mShared.mMessage.mMessage; } + Message &GetMessage(void) const { return *mShared.mMessage.mMessage; } /** * This method returns a pointer to the start of the data buffer associated with `Appender`. @@ -152,7 +152,7 @@ class Appender * @returns A pointer to the start of the data buffer associated with `Appender`. * */ - uint8_t *GetBufferStart(void) { return AsNonConst(mShared.mFrameBuilder.GetBytes()); } + uint8_t *GetBufferStart(void) const { return AsNonConst(mShared.mFrameBuilder.GetBytes()); } /** * This method gets the data buffer associated with `Appender` as a `Data`. @@ -162,7 +162,7 @@ class Appender * @pram[out] aData A reference to a `Data` to output the data buffer. * */ - void GetAsData(Data &aData); + void GetAsData(Data &aData) const; private: Type mType; diff --git a/src/core/common/array.hpp b/src/core/common/array.hpp index 2afb4fda5d6..4a65073f108 100644 --- a/src/core/common/array.hpp +++ b/src/core/common/array.hpp @@ -39,6 +39,7 @@ #include "common/code_utils.hpp" #include "common/const_cast.hpp" #include "common/error.hpp" +#include "common/locator.hpp" #include "common/numeric_limits.hpp" #include "common/type_traits.hpp" @@ -146,6 +147,24 @@ class Array */ Array(const Array &aOtherArray) { *this = aOtherArray; } + /** + * This constructor initializes the array as empty and initializes its elements by calling `Init(Instance &)` + * method on every element. + * + * This constructor uses method `Init(Instance &aInstance)` on `Type`. + * + * @param[in] aInstance The OpenThread instance. + * + */ + explicit Array(Instance &aInstance) + : mLength(0) + { + for (Type &element : mElements) + { + element.Init(aInstance); + } + } + /** * This method clears the array. * @@ -186,6 +205,30 @@ class Array */ IndexType GetLength(void) const { return mLength; } + /** + * This methods sets the current length (number of elements) of the array. + * + * @param[in] aLength The array length. + * + */ + void SetLength(IndexType aLength) { mLength = aLength; } + + /** + * This method returns the pointer to the start of underlying C array buffer serving as `Array` storage. + * + * @return The pointer to start of underlying C array buffer. + * + */ + Type *GetArrayBuffer(void) { return mElements; } + + /** + * This method returns the pointer to the start of underlying C array buffer serving as `Array` storage. + * + * @return The pointer to start of underlying C array buffer. + * + */ + const Type *GetArrayBuffer(void) const { return mElements; } + /** * This method overloads the `[]` operator to get the element at a given index. * @@ -534,12 +577,29 @@ class Array return *this; } + /** + * This method indicates whether a given entry pointer is from the array buffer. + * + * This method does not check the current length of array and only checks that @p aEntry is pointing to an address + * contained within underlying C array buffer. + * + * @param[in] aEntry A pointer to an entry to check. + * + * @retval TRUE The @p aEntry is from the array. + * @retval FALSE The @p aEntry is not from the array. + * + */ + bool IsInArrayBuffer(const Type *aEntry) const + { + return (&mElements[0] <= aEntry) && (aEntry < GetArrayEnd(mElements)); + } + // The following methods are intended to support range-based `for` // loop iteration over the array elements and should not be used // directly. - Type * begin(void) { return &mElements[0]; } - Type * end(void) { return &mElements[mLength]; } + Type *begin(void) { return &mElements[0]; } + Type *end(void) { return &mElements[mLength]; } const Type *begin(void) const { return &mElements[0]; } const Type *end(void) const { return &mElements[mLength]; } diff --git a/src/core/common/as_core_type.hpp b/src/core/common/as_core_type.hpp index e5ad9bc8e08..521cabbc4a0 100644 --- a/src/core/common/as_core_type.hpp +++ b/src/core/common/as_core_type.hpp @@ -36,6 +36,8 @@ #include "openthread-core-config.h" +#include "common/debug.hpp" + namespace ot { /** @@ -63,6 +65,8 @@ template struct CoreType; */ template typename CoreType::Type &AsCoreType(Type *aObject) { + AssertPointerIsNotNull(aObject); + return *static_cast::Type *>(aObject); } @@ -78,6 +82,8 @@ template typename CoreType::Type &AsCoreType(Type *aObject */ template const typename CoreType::Type &AsCoreType(const Type *aObject) { + AssertPointerIsNotNull(aObject); + return *static_cast::Type *>(aObject); } diff --git a/src/core/common/callback.hpp b/src/core/common/callback.hpp new file mode 100644 index 00000000000..02e4cf33f09 --- /dev/null +++ b/src/core/common/callback.hpp @@ -0,0 +1,243 @@ +/* + * 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 defines OpenThread `Callback` class. + */ + +#ifndef CALLBACK_HPP_ +#define CALLBACK_HPP_ + +#include "openthread-core-config.h" + +#include + +#include "common/type_traits.hpp" + +namespace ot { + +/** + * This enumeration specifies the context argument position in a callback function pointer. + * + */ +enum CallbackContextPosition : uint8_t +{ + kContextAsFirstArg, ///< Context is the first argument. + kContextAsLastArg, ///< Context is the last argument. +}; + +/** + * This class is the base class for `Callback` (a function pointer handler and a `void *` context). + * + * @tparam HandlerType The handler function pointer type. + * + */ +template class CallbackBase +{ +public: + /** + * This method clears the `Callback` by setting the handler function pointer to `nullptr`. + * + */ + void Clear(void) { mHandler = nullptr; } + + /** + * This method sets the callback handler function pointer and its associated context. + * + * @param[in] aHandler The handler function pointer. + * @param[in] aContext The context associated with handler. + * + */ + void Set(HandlerType aHandler, void *aContext) + { + mHandler = aHandler; + mContext = aContext; + } + + /** + * This method indicates whether or not the callback is set (not `nullptr`). + * + * @retval TRUE The handler is set. + * @retval FALSE The handler is not set. + * + */ + bool IsSet(void) const { return (mHandler != nullptr); } + + /** + * This method returns the handler function pointer. + * + * @returns The handler function pointer. + * + */ + HandlerType GetHandler(void) const { return mHandler; } + + /** + * This method returns the context associated with callback. + * + * @returns The context. + * + */ + void *GetContext(void) const { return mContext; } + + /** + * This method indicates whether the callback matches a given handler function pointer and context. + * + * @param[in] aHandler The handler function pointer to compare with. + * @param[in] aContext The context associated with handler. + * + * @retval TRUE The callback matches @p aHandler and @p aContext. + * @retval FALSE The callback does not match @p aHandler and @p aContext. + * + */ + bool Matches(HandlerType aHandler, void *aContext) const + { + return (mHandler == aHandler) && (mContext == aContext); + } + +protected: + CallbackBase(void) + : mHandler(nullptr) + , mContext(nullptr) + { + } + + HandlerType mHandler; + void *mContext; +}; + +/** + * This class represents a `Callback` (a function pointer handler and a `void *` context). + * + * The context is passed as one of the arguments to the function pointer handler when invoked. + * + * The `Callback` provides two specializations based on `CallbackContextPosition` in the function pointer, i.e., whether + * it is passed as the first argument or as the last argument. + * + * The `CallbackContextPosition` template parameter is automatically determined at compile-time based on the given + * `HandlerType`. So user can simply use `Callback`. The `Invoke()` method will properly pass the context + * to the function handler. + * + * @tparam HandlerType The function pointer handler type. + * @tparam CallbackContextPosition Context position (first or last). Automatically determined at compile-time. + * + */ +template ::Type, void *>::kValue + ? kContextAsFirstArg + : kContextAsLastArg)> +class Callback +{ +}; + +// Specialization for `kContextAsLastArg` +template class Callback : public CallbackBase +{ + using CallbackBase::mHandler; + using CallbackBase::mContext; + +public: + using ReturnType = typename TypeTraits::ReturnTypeOf::Type; ///< Return type of `HandlerType`. + + static constexpr CallbackContextPosition kContextPosition = kContextAsLastArg; ///< Context position. + + /** + * This constructor initializes `Callback` as empty (`nullptr` handler function pointer). + * + */ + Callback(void) = default; + + /** + * This method invokes the callback handler. + * + * The caller MUST ensure that callback is set (`IsSet()` returns `true`) before calling this method. + * + * @param[in] aArgs The args to pass to the callback handler. + * + * @returns The return value from handler. + * + */ + template ReturnType Invoke(Args &&...aArgs) const + { + return mHandler(static_cast(aArgs)..., mContext); + } + + /** + * This method invokes the callback handler if it is set. + * + * The method MUST be used when the handler function returns `void`. + * + * @param[in] aArgs The args to pass to the callback handler. + * + */ + template void InvokeIfSet(Args &&...aArgs) const + { + static_assert(TypeTraits::IsSame::kValue, + "InvokeIfSet() MUST be used with `void` returning handler"); + + if (mHandler != nullptr) + { + Invoke(static_cast(aArgs)...); + } + } +}; + +// Specialization for `kContextAsFirstArg` +template class Callback : public CallbackBase +{ + using CallbackBase::mHandler; + using CallbackBase::mContext; + +public: + using ReturnType = typename TypeTraits::ReturnTypeOf::Type; + + static constexpr CallbackContextPosition kContextPosition = kContextAsFirstArg; + + Callback(void) = default; + + template ReturnType Invoke(Args &&...aArgs) const + { + return mHandler(mContext, static_cast(aArgs)...); + } + + template void InvokeIfSet(Args &&...aArgs) const + { + static_assert(TypeTraits::IsSame::kValue, + "InvokeIfSet() MUST be used with `void` returning handler"); + + if (mHandler != nullptr) + { + Invoke(static_cast(aArgs)...); + } + } +}; + +} // namespace ot + +#endif // CALLBACK_HPP_ diff --git a/src/core/common/clearable.hpp b/src/core/common/clearable.hpp index 2cef0918ebe..b47bb196067 100644 --- a/src/core/common/clearable.hpp +++ b/src/core/common/clearable.hpp @@ -41,7 +41,7 @@ namespace ot { /** - * This template class defines a Clearable object which provides `Clear()` method. + * This template class defines a `Clearable` object which provides `Clear()` method. * * The `Clear` implementation simply sets all the bytes of a `Type` instance to zero. * @@ -52,7 +52,7 @@ namespace ot { template class Clearable { public: - void Clear(void) { memset(reinterpret_cast(this), 0, sizeof(Type)); } + void Clear(void) { memset(reinterpret_cast(static_cast(this)), 0, sizeof(Type)); } }; } // namespace ot diff --git a/src/core/common/code_utils.hpp b/src/core/common/code_utils.hpp index 78e01c61f1c..379c951cd5f 100644 --- a/src/core/common/code_utils.hpp +++ b/src/core/common/code_utils.hpp @@ -170,9 +170,6 @@ * @param[in] aError The error to be ignored. * */ -static inline void IgnoreError(otError aError) -{ - OT_UNUSED_VARIABLE(aError); -} +static inline void IgnoreError(otError aError) { OT_UNUSED_VARIABLE(aError); } #endif // CODE_UTILS_HPP_ diff --git a/src/core/common/const_cast.hpp b/src/core/common/const_cast.hpp index 54fd2ff0b32..2927df31a5b 100644 --- a/src/core/common/const_cast.hpp +++ b/src/core/common/const_cast.hpp @@ -48,10 +48,7 @@ namespace ot { * @returns A const reference to @p aObject reference. * */ -template const Type &AsConst(Type &aObject) -{ - return const_cast(aObject); -} +template const Type &AsConst(Type &aObject) { return const_cast(aObject); } /** * This template method casts a given non-const pointer to a const pointer. @@ -63,10 +60,7 @@ template const Type &AsConst(Type &aObject) * @returns A const pointer to @p aPointer pointer. * */ -template const Type *AsConst(Type *aPointer) -{ - return const_cast(aPointer); -} +template const Type *AsConst(Type *aPointer) { return const_cast(aPointer); } /** * This template method casts a given const reference to a non-const reference. @@ -78,10 +72,7 @@ template const Type *AsConst(Type *aPointer) * @returns A non-const reference to @p aObject reference. * */ -template Type &AsNonConst(const Type &aObject) -{ - return const_cast(aObject); -} +template Type &AsNonConst(const Type &aObject) { return const_cast(aObject); } /** * This template method casts a given const pointer to a non-const pointer. @@ -93,10 +84,7 @@ template Type &AsNonConst(const Type &aObject) * @returns A non-const pointer to @p aPointer pointer. * */ -template Type *AsNonConst(const Type *aPointer) -{ - return const_cast(aPointer); -} +template Type *AsNonConst(const Type *aPointer) { return const_cast(aPointer); } } // namespace ot diff --git a/src/core/common/data.hpp b/src/core/common/data.hpp index 59b37a3a33d..404e6a3f84c 100644 --- a/src/core/common/data.hpp +++ b/src/core/common/data.hpp @@ -44,6 +44,7 @@ #include "common/const_cast.hpp" #include "common/equatable.hpp" #include "common/error.hpp" +#include "common/num_utils.hpp" #include "common/type_traits.hpp" namespace ot { @@ -360,7 +361,7 @@ template class MutableData : public Data= aLength) ? kErrorNone : kErrorNoBufs; - mLength = OT_MIN(mLength, aLength); + mLength = Min(mLength, aLength); memcpy(AsNonConst(mBuffer), aBuffer, mLength); return error; diff --git a/src/core/common/debug.hpp b/src/core/common/debug.hpp index 4febe3442e6..8bb21997a7d 100644 --- a/src/core/common/debug.hpp +++ b/src/core/common/debug.hpp @@ -108,4 +108,19 @@ } \ } while (false) +/** + * @def AssertPointerIsNotNull + * + * This macro asserts that a given pointer (API input parameter) is not `nullptr`. This macro checks the pointer only + * when `OPENTHREAD_CONFIG_ASSERT_CHECK_API_POINTER_PARAM_FOR_NULL` is enabled. Otherwise it is an empty macro. + * + * @param[in] aPointer The pointer variable (API input parameter) to check. + * + */ +#if OPENTHREAD_CONFIG_ASSERT_CHECK_API_POINTER_PARAM_FOR_NULL +#define AssertPointerIsNotNull(aPointer) OT_ASSERT((aPointer) != nullptr) +#else +#define AssertPointerIsNotNull(aPointer) +#endif + #endif // DEBUG_HPP_ diff --git a/src/core/common/encoding.hpp b/src/core/common/encoding.hpp index b67d77999ea..29e66d5448e 100644 --- a/src/core/common/encoding.hpp +++ b/src/core/common/encoding.hpp @@ -51,10 +51,7 @@ namespace ot { namespace Encoding { -inline uint16_t Swap16(uint16_t v) -{ - return (((v & 0x00ffU) << 8) & 0xff00) | (((v & 0xff00U) >> 8) & 0x00ff); -} +inline uint16_t Swap16(uint16_t v) { return (((v & 0x00ffU) << 8) & 0xff00) | (((v & 0xff00U) >> 8) & 0x00ff); } inline uint32_t Swap32(uint32_t v) { @@ -91,33 +88,15 @@ namespace BigEndian { #if BYTE_ORDER_BIG_ENDIAN -inline uint16_t HostSwap16(uint16_t v) -{ - return v; -} -inline uint32_t HostSwap32(uint32_t v) -{ - return v; -} -inline uint64_t HostSwap64(uint64_t v) -{ - return v; -} +inline uint16_t HostSwap16(uint16_t v) { return v; } +inline uint32_t HostSwap32(uint32_t v) { return v; } +inline uint64_t HostSwap64(uint64_t v) { return v; } #else /* BYTE_ORDER_LITTLE_ENDIAN */ -inline uint16_t HostSwap16(uint16_t v) -{ - return Swap16(v); -} -inline uint32_t HostSwap32(uint32_t v) -{ - return Swap32(v); -} -inline uint64_t HostSwap64(uint64_t v) -{ - return Swap64(v); -} +inline uint16_t HostSwap16(uint16_t v) { return Swap16(v); } +inline uint32_t HostSwap32(uint32_t v) { return Swap32(v); } +inline uint64_t HostSwap64(uint64_t v) { return Swap64(v); } #endif // LITTLE_ENDIAN @@ -133,22 +112,10 @@ inline uint64_t HostSwap64(uint64_t v) */ template UintType HostSwap(UintType aValue); -template <> inline uint8_t HostSwap(uint8_t aValue) -{ - return aValue; -} -template <> inline uint16_t HostSwap(uint16_t aValue) -{ - return HostSwap16(aValue); -} -template <> inline uint32_t HostSwap(uint32_t aValue) -{ - return HostSwap32(aValue); -} -template <> inline uint64_t HostSwap(uint64_t aValue) -{ - return HostSwap64(aValue); -} +template <> inline uint8_t HostSwap(uint8_t aValue) { return aValue; } +template <> inline uint16_t HostSwap(uint16_t aValue) { return HostSwap16(aValue); } +template <> inline uint32_t HostSwap(uint32_t aValue) { return HostSwap32(aValue); } +template <> inline uint64_t HostSwap(uint64_t aValue) { return HostSwap64(aValue); } /** * This function reads a `uint16_t` value from a given buffer assuming big-endian encoding. @@ -158,10 +125,7 @@ template <> inline uint64_t HostSwap(uint64_t aValue) * @returns The `uint16_t` value read from buffer. * */ -inline uint16_t ReadUint16(const uint8_t *aBuffer) -{ - return static_cast((aBuffer[0] << 8) | aBuffer[1]); -} +inline uint16_t ReadUint16(const uint8_t *aBuffer) { return static_cast((aBuffer[0] << 8) | aBuffer[1]); } /** * This function reads a `uint32_t` value from a given buffer assuming big-endian encoding. @@ -274,33 +238,15 @@ namespace LittleEndian { #if BYTE_ORDER_BIG_ENDIAN -inline uint16_t HostSwap16(uint16_t v) -{ - return Swap16(v); -} -inline uint32_t HostSwap32(uint32_t v) -{ - return Swap32(v); -} -inline uint64_t HostSwap64(uint64_t v) -{ - return Swap64(v); -} +inline uint16_t HostSwap16(uint16_t v) { return Swap16(v); } +inline uint32_t HostSwap32(uint32_t v) { return Swap32(v); } +inline uint64_t HostSwap64(uint64_t v) { return Swap64(v); } #else /* BYTE_ORDER_LITTLE_ENDIAN */ -inline uint16_t HostSwap16(uint16_t v) -{ - return v; -} -inline uint32_t HostSwap32(uint32_t v) -{ - return v; -} -inline uint64_t HostSwap64(uint64_t v) -{ - return v; -} +inline uint16_t HostSwap16(uint16_t v) { return v; } +inline uint32_t HostSwap32(uint32_t v) { return v; } +inline uint64_t HostSwap64(uint64_t v) { return v; } #endif @@ -316,22 +262,10 @@ inline uint64_t HostSwap64(uint64_t v) */ template UintType HostSwap(UintType aValue); -template <> inline uint8_t HostSwap(uint8_t aValue) -{ - return aValue; -} -template <> inline uint16_t HostSwap(uint16_t aValue) -{ - return HostSwap16(aValue); -} -template <> inline uint32_t HostSwap(uint32_t aValue) -{ - return HostSwap32(aValue); -} -template <> inline uint64_t HostSwap(uint64_t aValue) -{ - return HostSwap64(aValue); -} +template <> inline uint8_t HostSwap(uint8_t aValue) { return aValue; } +template <> inline uint16_t HostSwap(uint16_t aValue) { return HostSwap16(aValue); } +template <> inline uint32_t HostSwap(uint32_t aValue) { return HostSwap32(aValue); } +template <> inline uint64_t HostSwap(uint64_t aValue) { return HostSwap64(aValue); } /** * This function reads a `uint16_t` value from a given buffer assuming little-endian encoding. @@ -341,10 +275,7 @@ template <> inline uint64_t HostSwap(uint64_t aValue) * @returns The `uint16_t` value read from buffer. * */ -inline uint16_t ReadUint16(const uint8_t *aBuffer) -{ - return static_cast(aBuffer[0] | (aBuffer[1] << 8)); -} +inline uint16_t ReadUint16(const uint8_t *aBuffer) { return static_cast(aBuffer[0] | (aBuffer[1] << 8)); } /** * This function reads a 24-bit integer value from a given buffer assuming little-endian encoding. diff --git a/src/core/common/equatable.hpp b/src/core/common/equatable.hpp index 06fdda993ef..d9130150fd0 100644 --- a/src/core/common/equatable.hpp +++ b/src/core/common/equatable.hpp @@ -87,7 +87,10 @@ template class Equatable : public Unequatable * @retval FALSE If the two `Type` instances are not equal. * */ - bool operator==(const Type &aOther) const { return memcmp(this, &aOther, sizeof(Type)) == 0; } + bool operator==(const Type &aOther) const + { + return memcmp(static_cast(this), &aOther, sizeof(Type)) == 0; + } }; } // namespace ot diff --git a/src/core/common/frame_builder.cpp b/src/core/common/frame_builder.cpp index 6d95d1afb95..88247ca6720 100644 --- a/src/core/common/frame_builder.cpp +++ b/src/core/common/frame_builder.cpp @@ -33,6 +33,16 @@ #include "frame_builder.hpp" +#include + +#include "common/code_utils.hpp" +#include "common/debug.hpp" +#include "common/encoding.hpp" + +#if OPENTHREAD_FTD || OPENTHREAD_MTD +#include "common/message.hpp" +#endif + namespace ot { void FrameBuilder::Init(void *aBuffer, uint16_t aLength) @@ -42,10 +52,7 @@ void FrameBuilder::Init(void *aBuffer, uint16_t aLength) mMaxLength = aLength; } -Error FrameBuilder::AppendUint8(uint8_t aUint8) -{ - return Append(aUint8); -} +Error FrameBuilder::AppendUint8(uint8_t aUint8) { return Append(aUint8); } Error FrameBuilder::AppendBigEndianUint16(uint16_t aUint16) { @@ -79,6 +86,31 @@ Error FrameBuilder::AppendBytes(const void *aBuffer, uint16_t aLength) return error; } +Error FrameBuilder::AppendMacAddress(const Mac::Address &aMacAddress) +{ + Error error = kErrorNone; + + switch (aMacAddress.GetType()) + { + case Mac::Address::kTypeNone: + break; + + case Mac::Address::kTypeShort: + error = AppendLittleEndianUint16(aMacAddress.GetShort()); + break; + + case Mac::Address::kTypeExtended: + VerifyOrExit(CanAppend(sizeof(Mac::ExtAddress)), error = kErrorNoBufs); + aMacAddress.GetExtended().CopyTo(mBuffer + mLength, Mac::ExtAddress::kReverseByteOrder); + mLength += sizeof(Mac::ExtAddress); + break; + } + +exit: + return error; +} + +#if OPENTHREAD_FTD || OPENTHREAD_MTD Error FrameBuilder::AppendBytesFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength) { Error error = kErrorNone; @@ -90,10 +122,33 @@ Error FrameBuilder::AppendBytesFromMessage(const Message &aMessage, uint16_t aOf exit: return error; } +#endif void FrameBuilder::WriteBytes(uint16_t aOffset, const void *aBuffer, uint16_t aLength) { memcpy(mBuffer + aOffset, aBuffer, aLength); } +Error FrameBuilder::InsertBytes(uint16_t aOffset, const void *aBuffer, uint16_t aLength) +{ + Error error = kErrorNone; + + OT_ASSERT(aOffset <= mLength); + + VerifyOrExit(CanAppend(aLength), error = kErrorNoBufs); + + memmove(mBuffer + aOffset + aLength, mBuffer + aOffset, mLength - aOffset); + memcpy(mBuffer + aOffset, aBuffer, aLength); + mLength += aLength; + +exit: + return error; +} + +void FrameBuilder::RemoveBytes(uint16_t aOffset, uint16_t aLength) +{ + memmove(mBuffer + aOffset, mBuffer + aOffset + aLength, mLength - aOffset - aLength); + mLength -= aLength; +} + } // namespace ot diff --git a/src/core/common/frame_builder.hpp b/src/core/common/frame_builder.hpp index 81150452f60..bbbb73154e4 100644 --- a/src/core/common/frame_builder.hpp +++ b/src/core/common/frame_builder.hpp @@ -37,10 +37,11 @@ #include "openthread-core-config.h" #include "common/error.hpp" -#include "common/message.hpp" #include "common/type_traits.hpp" +#include "mac/mac_types.hpp" namespace ot { +class Message; /** * The `FrameBuilder` can be used to construct frame content in a given data buffer. @@ -77,13 +78,32 @@ class FrameBuilder uint16_t GetLength(void) const { return mLength; } /** - * This method returns the maximum length of frame. + * This method returns the maximum length of the frame. * * @returns The maximum frame length (max number of bytes in the frame buffer). * */ uint16_t GetMaxLength(void) const { return mMaxLength; } + /** + * This method sets the maximum length of the frame. + * + * This method does not perform any checks on the new given length. The caller MUST ensure that the specified max + * length is valid for the frame buffer. + * + * @param[in] aLength The maximum frame length. + * + */ + void SetMaxLength(uint16_t aLength) { mMaxLength = aLength; } + + /** + * This method returns the remaining length (number of bytes that can be appended) in the frame. + * + * @returns The remaining length. + * + */ + uint16_t GetRemainingLength(void) const { return mMaxLength - mLength; } + /** * This method indicates whether or not there are enough bytes remaining in the `FrameBuilder` buffer to append a * given number of bytes. @@ -163,6 +183,18 @@ class FrameBuilder */ Error AppendBytes(const void *aBuffer, uint16_t aLength); + /** + * This method appends a given `Mac::Address` to the `FrameBuilder`. + * + * @param[in] aMacAddress A `Mac::Address` to append. + * + * @retval kErrorNone Successfully appended the address. + * @retval kErrorNoBufs Insufficient available buffers. + * + */ + Error AppendMacAddress(const Mac::Address &aMacAddress); + +#if OPENTHREAD_FTD || OPENTHREAD_MTD /** * This method appends bytes read from a given message to the `FrameBuilder`. * @@ -176,6 +208,7 @@ class FrameBuilder * */ Error AppendBytesFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength); +#endif /** * This method appends an object to the `FrameBuilder`. @@ -227,6 +260,57 @@ class FrameBuilder WriteBytes(aOffset, &aObject, sizeof(ObjectType)); } + /** + * This method inserts bytes in `FrameBuilder` at a given offset, moving previous content forward. + * + * The caller MUST ensure that @p aOffset is within the current frame length (from 0 up to and including + * `GetLength()`). Otherwise the behavior of this method is undefined. + * + * @param[in] aOffset The offset to insert bytes. + * @param[in] aBuffer A pointer to a data buffer to insert. + * @param[in] aLength Number of bytes in @p aBuffer. + * + * @retval kErrorNone Successfully inserted the bytes. + * @retval kErrorNoBufs Insufficient available buffers to insert the bytes. + * + */ + Error InsertBytes(uint16_t aOffset, const void *aBuffer, uint16_t aLength); + + /** + * This method inserts an object in `FrameBuilder` at a given offset, moving previous content forward. + * + * The caller MUST ensure that @p aOffset is within the current frame length (from 0 up to and including + * `GetLength()`). Otherwise the behavior of this method is undefined. + * + * @tparam ObjectType The object type to insert. + * + * @param[in] aOffset The offset to insert bytes. + * @param[in] aObject A reference to the object to insert. + * + * @retval kErrorNone Successfully inserted the bytes. + * @retval kErrorNoBufs Insufficient available buffers to insert the bytes. + * + */ + template Error Insert(uint16_t aOffset, const ObjectType &aObject) + { + static_assert(!TypeTraits::IsPointer::kValue, "ObjectType must not be a pointer"); + + return InsertBytes(aOffset, &aObject, sizeof(ObjectType)); + } + + /** + * This method removes a given number of bytes in `FrameBuilder` at a given offset, moving existing content + * after removed bytes backward. + * + * This method does not perform any bound checks. The caller MUST ensure that the given length and offset fits + * within the previously appended content. Otherwise the behavior of this method is undefined. + * + * @param[in] aOffset The offset to remove bytes from. + * @param[in] aLength The number of bytes to remove. + * + */ + void RemoveBytes(uint16_t aOffset, uint16_t aLength); + private: uint8_t *mBuffer; uint16_t mLength; diff --git a/src/core/common/frame_data.cpp b/src/core/common/frame_data.cpp index 760704186b7..04d2aa94c07 100644 --- a/src/core/common/frame_data.cpp +++ b/src/core/common/frame_data.cpp @@ -38,10 +38,7 @@ namespace ot { -Error FrameData::ReadUint8(uint8_t &aUint8) -{ - return ReadBytes(&aUint8, sizeof(uint8_t)); -} +Error FrameData::ReadUint8(uint8_t &aUint8) { return ReadBytes(&aUint8, sizeof(uint8_t)); } Error FrameData::ReadBigEndianUint16(uint16_t &aUint16) { @@ -99,9 +96,6 @@ Error FrameData::ReadBytes(void *aBuffer, uint16_t aLength) return error; } -void FrameData::SkipOver(uint16_t aLength) -{ - Init(GetBytes() + aLength, GetLength() - aLength); -} +void FrameData::SkipOver(uint16_t aLength) { Init(GetBytes() + aLength, GetLength() - aLength); } } // namespace ot diff --git a/src/core/common/heap.cpp b/src/core/common/heap.cpp index 77a171ae9bb..7b431ee308d 100644 --- a/src/core/common/heap.cpp +++ b/src/core/common/heap.cpp @@ -40,27 +40,15 @@ namespace Heap { #if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE -void *CAlloc(size_t aCount, size_t aSize) -{ - return otPlatCAlloc(aCount, aSize); -} +void *CAlloc(size_t aCount, size_t aSize) { return otPlatCAlloc(aCount, aSize); } -void Free(void *aPointer) -{ - otPlatFree(aPointer); -} +void Free(void *aPointer) { otPlatFree(aPointer); } #else -void *CAlloc(size_t aCount, size_t aSize) -{ - return Instance::GetHeap().CAlloc(aCount, aSize); -} +void *CAlloc(size_t aCount, size_t aSize) { return Instance::GetHeap().CAlloc(aCount, aSize); } -void Free(void *aPointer) -{ - Instance::GetHeap().Free(aPointer); -} +void Free(void *aPointer) { Instance::GetHeap().Free(aPointer); } #endif // OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE diff --git a/src/core/common/heap_allocatable.hpp b/src/core/common/heap_allocatable.hpp index 1b88016311f..e9073ed70a9 100644 --- a/src/core/common/heap_allocatable.hpp +++ b/src/core/common/heap_allocatable.hpp @@ -70,7 +70,7 @@ template class Allocatable * @returns A pointer to the newly allocated instance or `nullptr` if it fails to allocate. * */ - template static Type *Allocate(Args &&... aArgs) + template static Type *Allocate(Args &&...aArgs) { void *buf = Heap::CAlloc(1, sizeof(Type)); @@ -89,7 +89,7 @@ template class Allocatable * @returns A pointer to the newly allocated instance or `nullptr` if it fails to allocate or initialize. * */ - template static Type *AllocateAndInit(Args &&... aArgs) + template static Type *AllocateAndInit(Args &&...aArgs) { void *buf = Heap::CAlloc(1, sizeof(Type)); Type *object = nullptr; diff --git a/src/core/common/heap_array.hpp b/src/core/common/heap_array.hpp index a4ede9027ca..29645f6fb7b 100644 --- a/src/core/common/heap_array.hpp +++ b/src/core/common/heap_array.hpp @@ -506,12 +506,12 @@ template class Array // loop iteration over the array elements and should not be used // directly. - Type * begin(void) { return (mLength > 0) ? mArray : nullptr; } - Type * end(void) { return (mLength > 0) ? &mArray[mLength] : nullptr; } + Type *begin(void) { return (mLength > 0) ? mArray : nullptr; } + Type *end(void) { return (mLength > 0) ? &mArray[mLength] : nullptr; } const Type *begin(void) const { return (mLength > 0) ? mArray : nullptr; } const Type *end(void) const { return (mLength > 0) ? &mArray[mLength] : nullptr; } - Array(const Array &) = delete; + Array(const Array &) = delete; Array &operator=(const Array &) = delete; private: @@ -538,7 +538,7 @@ template class Array return error; } - Type * mArray; + Type *mArray; IndexType mLength; IndexType mCapacity; }; diff --git a/src/core/common/heap_data.hpp b/src/core/common/heap_data.hpp index 041f5397a9a..20545dae0ff 100644 --- a/src/core/common/heap_data.hpp +++ b/src/core/common/heap_data.hpp @@ -94,7 +94,7 @@ class Data /** * This method returns the `Heap::Data` length. * - * @returns The data length (number of bytes) or zero if the `HeadpData` is null. + * @returns The data length (number of bytes) or zero if the `HeapData` is null. * */ uint16_t GetLength(void) const { return mData.GetLength(); } @@ -178,7 +178,7 @@ class Data */ void Free(void); - Data(const Data &) = delete; + Data(const Data &) = delete; Data &operator=(const Data &) = delete; private: diff --git a/src/core/common/heap_string.hpp b/src/core/common/heap_string.hpp index c9a27fdc0f8..2f737bb1599 100644 --- a/src/core/common/heap_string.hpp +++ b/src/core/common/heap_string.hpp @@ -175,7 +175,7 @@ class String : public Unequatable */ bool operator==(const String &aString) const { return (*this == aString.AsCString()); } - String(const String &) = delete; + String(const String &) = delete; String &operator=(const String &) = delete; private: diff --git a/src/core/common/instance.cpp b/src/core/common/instance.cpp index 6aa10759049..d0aebecd378 100644 --- a/src/core/common/instance.cpp +++ b/src/core/common/instance.cpp @@ -50,7 +50,8 @@ OT_DEFINE_ALIGNED_VAR(gInstanceRaw, sizeof(Instance), uint64_t); #if OPENTHREAD_MTD || OPENTHREAD_FTD #if !OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE -Utils::Heap Instance::sHeap; +OT_DEFINE_ALIGNED_VAR(sHeapRaw, sizeof(Utils::Heap), uint64_t); +Utils::Heap *Instance::sHeap{nullptr}; #endif #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE bool Instance::sDnsNameCompressionEnabled = true; @@ -135,8 +136,9 @@ Instance::Instance(void) , mNetworkDataPublisher(*this) #endif , mNetworkDataServiceManager(*this) -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE - , mNetworkDiagnostic(*this) + , mNetworkDiagnosticServer(*this) +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE + , mNetworkDiagnosticClient(*this) #endif #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE , mBorderAgent(*this) @@ -145,7 +147,7 @@ Instance::Instance(void) , mCommissioner(*this) #endif #if OPENTHREAD_CONFIG_DTLS_ENABLE - , mCoapSecure(*this) + , mTmfSecureAgent(*this) #endif #if OPENTHREAD_CONFIG_JOINER_ENABLE , mJoiner(*this) @@ -174,13 +176,10 @@ Instance::Instance(void) #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE , mSrpServer(*this) #endif - -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE #if OPENTHREAD_FTD , mChildSupervisor(*this) #endif , mSupervisionListener(*this) -#endif , mAnnounceBegin(*this) , mPanIdQuery(*this) , mEnergyScan(*this) @@ -190,8 +189,11 @@ Instance::Instance(void) #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE , mTimeSync(*this) #endif -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE - , mLinkMetrics(*this) +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE + , mInitiator(*this) +#endif +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE + , mSubject(*this) #endif #if OPENTHREAD_CONFIG_COAP_API_ENABLE , mApplicationCoap(*this) @@ -208,6 +210,9 @@ Instance::Instance(void) #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD , mChannelManager(*this) #endif +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD + , mMeshDiag(*this) +#endif #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE , mHistoryTracker(*this) #endif @@ -223,6 +228,9 @@ Instance::Instance(void) #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE , mRoutingManager(*this) #endif +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + , mNat64Translator(*this) +#endif #endif // OPENTHREAD_MTD || OPENTHREAD_FTD #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE , mLinkRaw(*this) @@ -232,11 +240,26 @@ Instance::Instance(void) #endif #if OPENTHREAD_CONFIG_DIAG_ENABLE , mDiags(*this) +#endif +#if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + , mPowerCalibration(*this) #endif , mIsInitialized(false) { } +#if (OPENTHREAD_MTD || OPENTHREAD_FTD) && !OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE +Utils::Heap &Instance::GetHeap(void) +{ + if (nullptr == sHeap) + { + sHeap = new (&sHeapRaw) Utils::Heap(); + } + + return *sHeap; +} +#endif + #if !OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE Instance &Instance::InitSingle(void) @@ -283,10 +306,7 @@ Instance *Instance::Init(void *aBuffer, size_t *aBufferSize) #endif // OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE -void Instance::Reset(void) -{ - otPlatReset(this); -} +void Instance::Reset(void) { otPlatReset(this); } #if OPENTHREAD_RADIO void Instance::ResetRadioStack(void) @@ -328,6 +348,10 @@ void Instance::Finalize(void) IgnoreError(otIp6SetEnabled(this, false)); IgnoreError(otLinkSetEnabled(this, false)); +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + Get().DestroyTemporaryKeys(); +#endif + Get().Deinit(); #endif @@ -352,6 +376,10 @@ void Instance::Finalize(void) void Instance::FactoryReset(void) { Get().Wipe(); +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + Get().DestroyTemporaryKeys(); + Get().DestroyPersistentKeys(); +#endif otPlatReset(this); } @@ -361,6 +389,10 @@ Error Instance::ErasePersistentInfo(void) VerifyOrExit(Get().IsDisabled(), error = kErrorInvalidState); Get().Wipe(); +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + Get().DestroyTemporaryKeys(); + Get().DestroyPersistentKeys(); +#endif exit: return error; @@ -370,8 +402,9 @@ void Instance::GetBufferInfo(BufferInfo &aInfo) { aInfo.Clear(); - aInfo.mTotalBuffers = Get().GetTotalBufferCount(); - aInfo.mFreeBuffers = Get().GetFreeBufferCount(); + aInfo.mTotalBuffers = Get().GetTotalBufferCount(); + aInfo.mFreeBuffers = Get().GetFreeBufferCount(); + aInfo.mMaxUsedBuffers = Get().GetMaxUsedBufferCount(); Get().GetSendQueue().GetInfo(aInfo.m6loSendQueue); Get().GetReassemblyQueue().GetInfo(aInfo.m6loReassemblyQueue); @@ -387,8 +420,8 @@ void Instance::GetBufferInfo(BufferInfo &aInfo) Get().GetCachedResponses().GetInfo(aInfo.mCoapQueue); #if OPENTHREAD_CONFIG_DTLS_ENABLE - Get().GetRequestMessages().GetInfo(aInfo.mCoapSecureQueue); - Get().GetCachedResponses().GetInfo(aInfo.mCoapSecureQueue); + Get().GetRequestMessages().GetInfo(aInfo.mCoapSecureQueue); + Get().GetCachedResponses().GetInfo(aInfo.mCoapSecureQueue); #endif #if OPENTHREAD_CONFIG_COAP_API_ENABLE @@ -397,6 +430,8 @@ void Instance::GetBufferInfo(BufferInfo &aInfo) #endif } +void Instance::ResetBufferInfo(void) { Get().ResetMaxUsedBufferCount(); } + #endif // OPENTHREAD_MTD || OPENTHREAD_FTD #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE @@ -410,10 +445,7 @@ void Instance::SetLogLevel(LogLevel aLogLevel) } } -extern "C" OT_TOOL_WEAK void otPlatLogHandleLevelChanged(otLogLevel aLogLevel) -{ - OT_UNUSED_VARIABLE(aLogLevel); -} +extern "C" OT_TOOL_WEAK void otPlatLogHandleLevelChanged(otLogLevel aLogLevel) { OT_UNUSED_VARIABLE(aLogLevel); } #endif diff --git a/src/core/common/instance.hpp b/src/core/common/instance.hpp index dd3e4e6d454..8ce6eba09ef 100644 --- a/src/core/common/instance.hpp +++ b/src/core/common/instance.hpp @@ -60,6 +60,7 @@ #include "mac/link_raw.hpp" #include "radio/radio.hpp" #include "utils/otns.hpp" +#include "utils/power_calibration.hpp" #if OPENTHREAD_FTD || OPENTHREAD_MTD #include "backbone_router/backbone_tmf.hpp" @@ -90,6 +91,7 @@ #include "net/dnssd_server.hpp" #include "net/ip6.hpp" #include "net/ip6_filter.hpp" +#include "net/nat64_translator.hpp" #include "net/nd_agent.hpp" #include "net/netif.hpp" #include "net/sntp_client.hpp" @@ -99,6 +101,7 @@ #include "thread/announce_begin_server.hpp" #include "thread/announce_sender.hpp" #include "thread/anycast_locator.hpp" +#include "thread/child_supervision.hpp" #include "thread/discover_scanner.hpp" #include "thread/dua_manager.hpp" #include "thread/energy_scan_server.hpp" @@ -121,10 +124,10 @@ #include "thread/tmf.hpp" #include "utils/channel_manager.hpp" #include "utils/channel_monitor.hpp" -#include "utils/child_supervision.hpp" #include "utils/heap.hpp" #include "utils/history_tracker.hpp" #include "utils/jam_detector.hpp" +#include "utils/mesh_diag.hpp" #include "utils/ping_sender.hpp" #include "utils/slaac_address.hpp" #include "utils/srp_client_buffers.hpp" @@ -289,7 +292,7 @@ class Instance : public otInstance, private NonCopyable * @returns A reference to the Heap object. * */ - static Utils::Heap &GetHeap(void) { return sHeap; } + static Utils::Heap &GetHeap(void); #endif #if OPENTHREAD_CONFIG_COAP_API_ENABLE @@ -343,6 +346,15 @@ class Instance : public otInstance, private NonCopyable */ void GetBufferInfo(BufferInfo &aInfo); + /** + * This method resets the Message Buffer information counter tracking maximum number buffers in use at the same + * time. + * + * This method resets `mMaxUsedBuffers` in `BufferInfo`. + * + */ + void ResetBufferInfo(void); + #endif // OPENTHREAD_MTD || OPENTHREAD_FTD /** @@ -380,7 +392,7 @@ class Instance : public otInstance, private NonCopyable // Random::Manager is initialized before other objects. Note that it // requires MbedTls which itself may use Heap. #if !OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE - static Utils::Heap sHeap; + static Utils::Heap *sHeap; #endif Crypto::MbedTls mMbedTls; #endif // OPENTHREAD_MTD || OPENTHREAD_FTD @@ -483,8 +495,9 @@ class Instance : public otInstance, private NonCopyable NetworkData::Service::Manager mNetworkDataServiceManager; -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE - NetworkDiagnostic::NetworkDiagnostic mNetworkDiagnostic; + NetworkDiagnostic::Server mNetworkDiagnosticServer; +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE + NetworkDiagnostic::Client mNetworkDiagnosticClient; #endif #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE @@ -496,7 +509,7 @@ class Instance : public otInstance, private NonCopyable #endif #if OPENTHREAD_CONFIG_DTLS_ENABLE - Coap::CoapSecure mCoapSecure; + Tmf::SecureAgent mTmfSecureAgent; #endif #if OPENTHREAD_CONFIG_JOINER_ENABLE @@ -533,12 +546,10 @@ class Instance : public otInstance, private NonCopyable Srp::Server mSrpServer; #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE #if OPENTHREAD_FTD - Utils::ChildSupervisor mChildSupervisor; -#endif - Utils::SupervisionListener mSupervisionListener; + ChildSupervisor mChildSupervisor; #endif + SupervisionListener mSupervisionListener; AnnounceBeginServer mAnnounceBegin; PanIdQueryServer mPanIdQuery; @@ -552,8 +563,12 @@ class Instance : public otInstance, private NonCopyable TimeSync mTimeSync; #endif -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE - LinkMetrics::LinkMetrics mLinkMetrics; +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE + LinkMetrics::Initiator mInitiator; +#endif + +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE + LinkMetrics::Subject mSubject; #endif #if OPENTHREAD_CONFIG_COAP_API_ENABLE @@ -576,6 +591,10 @@ class Instance : public otInstance, private NonCopyable Utils::ChannelManager mChannelManager; #endif +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD + Utils::MeshDiag mMeshDiag; +#endif + #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE Utils::HistoryTracker mHistoryTracker; #endif @@ -596,6 +615,10 @@ class Instance : public otInstance, private NonCopyable BorderRouter::RoutingManager mRoutingManager; #endif +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + Nat64::Translator mNat64Translator; +#endif + #endif // OPENTHREAD_MTD || OPENTHREAD_FTD #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE @@ -613,6 +636,9 @@ class Instance : public otInstance, private NonCopyable #if OPENTHREAD_CONFIG_DIAG_ENABLE FactoryDiags::Diags mDiags; #endif +#if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + Utils::PowerCalibration mPowerCalibration; +#endif bool mIsInitialized; @@ -626,492 +652,259 @@ DefineCoreType(otBufferInfo, Instance::BufferInfo); // Specializations of the `Get()` method. -template <> inline Instance &Instance::Get(void) -{ - return *this; -} +template <> inline Instance &Instance::Get(void) { return *this; } -template <> inline Radio &Instance::Get(void) -{ - return mRadio; -} +template <> inline Radio &Instance::Get(void) { return mRadio; } -template <> inline Radio::Callbacks &Instance::Get(void) -{ - return mRadio.mCallbacks; -} +template <> inline Radio::Callbacks &Instance::Get(void) { return mRadio.mCallbacks; } #if OPENTHREAD_CONFIG_UPTIME_ENABLE -template <> inline Uptime &Instance::Get(void) -{ - return mUptime; -} +template <> inline Uptime &Instance::Get(void) { return mUptime; } #endif #if OPENTHREAD_MTD || OPENTHREAD_FTD -template <> inline Notifier &Instance::Get(void) -{ - return mNotifier; -} +template <> inline Notifier &Instance::Get(void) { return mNotifier; } -template <> inline TimeTicker &Instance::Get(void) -{ - return mTimeTicker; -} +template <> inline TimeTicker &Instance::Get(void) { return mTimeTicker; } -template <> inline Settings &Instance::Get(void) -{ - return mSettings; -} +template <> inline Settings &Instance::Get(void) { return mSettings; } -template <> inline SettingsDriver &Instance::Get(void) -{ - return mSettingsDriver; -} +template <> inline SettingsDriver &Instance::Get(void) { return mSettingsDriver; } -template <> inline MeshForwarder &Instance::Get(void) -{ - return mMeshForwarder; -} +template <> inline MeshForwarder &Instance::Get(void) { return mMeshForwarder; } #if OPENTHREAD_CONFIG_MULTI_RADIO -template <> inline RadioSelector &Instance::Get(void) -{ - return mRadioSelector; -} +template <> inline RadioSelector &Instance::Get(void) { return mRadioSelector; } #endif -template <> inline Mle::Mle &Instance::Get(void) -{ - return mMleRouter; -} +template <> inline Mle::Mle &Instance::Get(void) { return mMleRouter; } -template <> inline Mle::MleRouter &Instance::Get(void) -{ - return mMleRouter; -} +template <> inline Mle::MleRouter &Instance::Get(void) { return mMleRouter; } -template <> inline Mle::DiscoverScanner &Instance::Get(void) -{ - return mDiscoverScanner; -} +template <> inline Mle::DiscoverScanner &Instance::Get(void) { return mDiscoverScanner; } -template <> inline NeighborTable &Instance::Get(void) -{ - return mMleRouter.mNeighborTable; -} +template <> inline NeighborTable &Instance::Get(void) { return mMleRouter.mNeighborTable; } #if OPENTHREAD_FTD -template <> inline ChildTable &Instance::Get(void) -{ - return mMleRouter.mChildTable; -} +template <> inline ChildTable &Instance::Get(void) { return mMleRouter.mChildTable; } -template <> inline RouterTable &Instance::Get(void) -{ - return mMleRouter.mRouterTable; -} +template <> inline RouterTable &Instance::Get(void) { return mMleRouter.mRouterTable; } #endif -template <> inline Ip6::Netif &Instance::Get(void) -{ - return mThreadNetif; -} +template <> inline Ip6::Netif &Instance::Get(void) { return mThreadNetif; } -template <> inline ThreadNetif &Instance::Get(void) -{ - return mThreadNetif; -} +template <> inline ThreadNetif &Instance::Get(void) { return mThreadNetif; } -template <> inline Ip6::Ip6 &Instance::Get(void) -{ - return mIp6; -} +template <> inline Ip6::Ip6 &Instance::Get(void) { return mIp6; } -template <> inline Mac::Mac &Instance::Get(void) -{ - return mMac; -} +template <> inline Mac::Mac &Instance::Get(void) { return mMac; } -template <> inline Mac::SubMac &Instance::Get(void) -{ - return mMac.mLinks.mSubMac; -} +template <> inline Mac::SubMac &Instance::Get(void) { return mMac.mLinks.mSubMac; } #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE -template <> inline Trel::Link &Instance::Get(void) -{ - return mMac.mLinks.mTrel; -} +template <> inline Trel::Link &Instance::Get(void) { return mMac.mLinks.mTrel; } -template <> inline Trel::Interface &Instance::Get(void) -{ - return mMac.mLinks.mTrel.mInterface; -} +template <> inline Trel::Interface &Instance::Get(void) { return mMac.mLinks.mTrel.mInterface; } #endif #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE -template <> inline Mac::Filter &Instance::Get(void) -{ - return mMac.mFilter; -} +template <> inline Mac::Filter &Instance::Get(void) { return mMac.mFilter; } #endif -template <> inline Lowpan::Lowpan &Instance::Get(void) -{ - return mLowpan; -} +template <> inline Lowpan::Lowpan &Instance::Get(void) { return mLowpan; } -template <> inline KeyManager &Instance::Get(void) -{ - return mKeyManager; -} +template <> inline KeyManager &Instance::Get(void) { return mKeyManager; } -template <> inline Ip6::Filter &Instance::Get(void) -{ - return mIp6Filter; -} +template <> inline Ip6::Filter &Instance::Get(void) { return mIp6Filter; } -template <> inline AddressResolver &Instance::Get(void) -{ - return mAddressResolver; -} +template <> inline AddressResolver &Instance::Get(void) { return mAddressResolver; } #if OPENTHREAD_FTD -template <> inline IndirectSender &Instance::Get(void) -{ - return mMeshForwarder.mIndirectSender; -} +template <> inline IndirectSender &Instance::Get(void) { return mMeshForwarder.mIndirectSender; } template <> inline SourceMatchController &Instance::Get(void) { return mMeshForwarder.mIndirectSender.mSourceMatchController; } -template <> inline DataPollHandler &Instance::Get(void) -{ - return mMeshForwarder.mIndirectSender.mDataPollHandler; -} +template <> inline DataPollHandler &Instance::Get(void) { return mMeshForwarder.mIndirectSender.mDataPollHandler; } #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE -template <> inline CslTxScheduler &Instance::Get(void) -{ - return mMeshForwarder.mIndirectSender.mCslTxScheduler; -} +template <> inline CslTxScheduler &Instance::Get(void) { return mMeshForwarder.mIndirectSender.mCslTxScheduler; } #endif -template <> inline MeshCoP::Leader &Instance::Get(void) -{ - return mLeader; -} +template <> inline MeshCoP::Leader &Instance::Get(void) { return mLeader; } -template <> inline MeshCoP::JoinerRouter &Instance::Get(void) -{ - return mJoinerRouter; -} +template <> inline MeshCoP::JoinerRouter &Instance::Get(void) { return mJoinerRouter; } #endif // OPENTHREAD_FTD -template <> inline AnnounceBeginServer &Instance::Get(void) -{ - return mAnnounceBegin; -} +template <> inline AnnounceBeginServer &Instance::Get(void) { return mAnnounceBegin; } -template <> inline DataPollSender &Instance::Get(void) -{ - return mMeshForwarder.mDataPollSender; -} +template <> inline DataPollSender &Instance::Get(void) { return mMeshForwarder.mDataPollSender; } -template <> inline EnergyScanServer &Instance::Get(void) -{ - return mEnergyScan; -} +template <> inline EnergyScanServer &Instance::Get(void) { return mEnergyScan; } -template <> inline PanIdQueryServer &Instance::Get(void) -{ - return mPanIdQuery; -} +template <> inline PanIdQueryServer &Instance::Get(void) { return mPanIdQuery; } #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE -template <> inline AnycastLocator &Instance::Get(void) -{ - return mAnycastLocator; -} +template <> inline AnycastLocator &Instance::Get(void) { return mAnycastLocator; } #endif #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE -template <> inline NetworkData::Local &Instance::Get(void) -{ - return mNetworkDataLocal; -} +template <> inline NetworkData::Local &Instance::Get(void) { return mNetworkDataLocal; } #endif -template <> inline NetworkData::Leader &Instance::Get(void) -{ - return mNetworkDataLeader; -} +template <> inline NetworkData::Leader &Instance::Get(void) { return mNetworkDataLeader; } #if OPENTHREAD_FTD || OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE -template <> inline NetworkData::Notifier &Instance::Get(void) -{ - return mNetworkDataNotifier; -} +template <> inline NetworkData::Notifier &Instance::Get(void) { return mNetworkDataNotifier; } #endif #if OPENTHREAD_CONFIG_NETDATA_PUBLISHER_ENABLE -template <> inline NetworkData::Publisher &Instance::Get(void) -{ - return mNetworkDataPublisher; -} +template <> inline NetworkData::Publisher &Instance::Get(void) { return mNetworkDataPublisher; } #endif -template <> inline NetworkData::Service::Manager &Instance::Get(void) -{ - return mNetworkDataServiceManager; -} +template <> inline NetworkData::Service::Manager &Instance::Get(void) { return mNetworkDataServiceManager; } #if OPENTHREAD_CONFIG_TCP_ENABLE -template <> inline Ip6::Tcp &Instance::Get(void) -{ - return mIp6.mTcp; -} +template <> inline Ip6::Tcp &Instance::Get(void) { return mIp6.mTcp; } #endif -template <> inline Ip6::Udp &Instance::Get(void) -{ - return mIp6.mUdp; -} +template <> inline Ip6::Udp &Instance::Get(void) { return mIp6.mUdp; } -template <> inline Ip6::Icmp &Instance::Get(void) -{ - return mIp6.mIcmp; -} +template <> inline Ip6::Icmp &Instance::Get(void) { return mIp6.mIcmp; } -template <> inline Ip6::Mpl &Instance::Get(void) -{ - return mIp6.mMpl; -} +template <> inline Ip6::Mpl &Instance::Get(void) { return mIp6.mMpl; } -template <> inline Tmf::Agent &Instance::Get(void) -{ - return mTmfAgent; -} +template <> inline Tmf::Agent &Instance::Get(void) { return mTmfAgent; } #if OPENTHREAD_CONFIG_DTLS_ENABLE -template <> inline Coap::CoapSecure &Instance::Get(void) -{ - return mCoapSecure; -} +template <> inline Tmf::SecureAgent &Instance::Get(void) { return mTmfSecureAgent; } #endif -template <> inline MeshCoP::ExtendedPanIdManager &Instance::Get(void) -{ - return mExtendedPanIdManager; -} +template <> inline MeshCoP::ExtendedPanIdManager &Instance::Get(void) { return mExtendedPanIdManager; } -template <> inline MeshCoP::NetworkNameManager &Instance::Get(void) -{ - return mNetworkNameManager; -} +template <> inline MeshCoP::NetworkNameManager &Instance::Get(void) { return mNetworkNameManager; } -template <> inline MeshCoP::ActiveDatasetManager &Instance::Get(void) -{ - return mActiveDataset; -} +template <> inline MeshCoP::ActiveDatasetManager &Instance::Get(void) { return mActiveDataset; } -template <> inline MeshCoP::PendingDatasetManager &Instance::Get(void) -{ - return mPendingDataset; -} +template <> inline MeshCoP::PendingDatasetManager &Instance::Get(void) { return mPendingDataset; } #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE -template <> inline TimeSync &Instance::Get(void) -{ - return mTimeSync; -} +template <> inline TimeSync &Instance::Get(void) { return mTimeSync; } #endif #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD -template <> inline MeshCoP::Commissioner &Instance::Get(void) -{ - return mCommissioner; -} +template <> inline MeshCoP::Commissioner &Instance::Get(void) { return mCommissioner; } + +template <> inline AnnounceBeginClient &Instance::Get(void) { return mCommissioner.GetAnnounceBeginClient(); } + +template <> inline EnergyScanClient &Instance::Get(void) { return mCommissioner.GetEnergyScanClient(); } + +template <> inline PanIdQueryClient &Instance::Get(void) { return mCommissioner.GetPanIdQueryClient(); } #endif #if OPENTHREAD_CONFIG_JOINER_ENABLE -template <> inline MeshCoP::Joiner &Instance::Get(void) -{ - return mJoiner; -} +template <> inline MeshCoP::Joiner &Instance::Get(void) { return mJoiner; } #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE -template <> inline Dns::Client &Instance::Get(void) -{ - return mDnsClient; -} +template <> inline Dns::Client &Instance::Get(void) { return mDnsClient; } #endif #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE -template <> inline Srp::Client &Instance::Get(void) -{ - return mSrpClient; -} +template <> inline Srp::Client &Instance::Get(void) { return mSrpClient; } #endif #if OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_ENABLE -template <> inline Utils::SrpClientBuffers &Instance::Get(void) -{ - return mSrpClientBuffers; -} +template <> inline Utils::SrpClientBuffers &Instance::Get(void) { return mSrpClientBuffers; } #endif #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE -template <> inline Dns::ServiceDiscovery::Server &Instance::Get(void) -{ - return mDnssdServer; -} +template <> inline Dns::ServiceDiscovery::Server &Instance::Get(void) { return mDnssdServer; } #endif #if OPENTHREAD_CONFIG_DNS_DSO_ENABLE -template <> inline Dns::Dso &Instance::Get(void) -{ - return mDnsDso; -} +template <> inline Dns::Dso &Instance::Get(void) { return mDnsDso; } #endif -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE -template <> inline NetworkDiagnostic::NetworkDiagnostic &Instance::Get(void) -{ - return mNetworkDiagnostic; -} +template <> inline NetworkDiagnostic::Server &Instance::Get(void) { return mNetworkDiagnosticServer; } + +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE +template <> inline NetworkDiagnostic::Client &Instance::Get(void) { return mNetworkDiagnosticClient; } #endif #if OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE -template <> inline Dhcp6::Client &Instance::Get(void) -{ - return mDhcp6Client; -} +template <> inline Dhcp6::Client &Instance::Get(void) { return mDhcp6Client; } #endif #if OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE -template <> inline Dhcp6::Server &Instance::Get(void) -{ - return mDhcp6Server; -} +template <> inline Dhcp6::Server &Instance::Get(void) { return mDhcp6Server; } #endif #if OPENTHREAD_CONFIG_NEIGHBOR_DISCOVERY_AGENT_ENABLE -template <> inline NeighborDiscovery::Agent &Instance::Get(void) -{ - return mNeighborDiscoveryAgent; -} +template <> inline NeighborDiscovery::Agent &Instance::Get(void) { return mNeighborDiscoveryAgent; } #endif #if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE -template <> inline Utils::Slaac &Instance::Get(void) -{ - return mSlaac; -} +template <> inline Utils::Slaac &Instance::Get(void) { return mSlaac; } #endif #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE -template <> inline Utils::JamDetector &Instance::Get(void) -{ - return mJamDetector; -} +template <> inline Utils::JamDetector &Instance::Get(void) { return mJamDetector; } #endif #if OPENTHREAD_CONFIG_SNTP_CLIENT_ENABLE -template <> inline Sntp::Client &Instance::Get(void) -{ - return mSntpClient; -} +template <> inline Sntp::Client &Instance::Get(void) { return mSntpClient; } #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE #if OPENTHREAD_FTD -template <> inline Utils::ChildSupervisor &Instance::Get(void) -{ - return mChildSupervisor; -} -#endif -template <> inline Utils::SupervisionListener &Instance::Get(void) -{ - return mSupervisionListener; -} +template <> inline ChildSupervisor &Instance::Get(void) { return mChildSupervisor; } #endif +template <> inline SupervisionListener &Instance::Get(void) { return mSupervisionListener; } #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE -template <> inline Utils::PingSender &Instance::Get(void) -{ - return mPingSender; -} +template <> inline Utils::PingSender &Instance::Get(void) { return mPingSender; } #endif #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE -template <> inline Utils::ChannelMonitor &Instance::Get(void) -{ - return mChannelMonitor; -} +template <> inline Utils::ChannelMonitor &Instance::Get(void) { return mChannelMonitor; } #endif #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE && OPENTHREAD_FTD -template <> inline Utils::ChannelManager &Instance::Get(void) -{ - return mChannelManager; -} +template <> inline Utils::ChannelManager &Instance::Get(void) { return mChannelManager; } +#endif + +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD +template <> inline Utils::MeshDiag &Instance::Get(void) { return mMeshDiag; } #endif #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE -template <> inline Utils::HistoryTracker &Instance::Get(void) -{ - return mHistoryTracker; -} +template <> inline Utils::HistoryTracker &Instance::Get(void) { return mHistoryTracker; } #endif #if (OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE || OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE) && OPENTHREAD_FTD -template <> inline MeshCoP::DatasetUpdater &Instance::Get(void) -{ - return mDatasetUpdater; -} +template <> inline MeshCoP::DatasetUpdater &Instance::Get(void) { return mDatasetUpdater; } #endif #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE -template <> inline MeshCoP::BorderAgent &Instance::Get(void) -{ - return mBorderAgent; -} +template <> inline MeshCoP::BorderAgent &Instance::Get(void) { return mBorderAgent; } #endif #if OPENTHREAD_CONFIG_ANNOUNCE_SENDER_ENABLE -template <> inline AnnounceSender &Instance::Get(void) -{ - return mAnnounceSender; -} +template <> inline AnnounceSender &Instance::Get(void) { return mAnnounceSender; } #endif -template <> inline MessagePool &Instance::Get(void) -{ - return mMessagePool; -} +template <> inline MessagePool &Instance::Get(void) { return mMessagePool; } #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) -template <> inline BackboneRouter::Leader &Instance::Get(void) -{ - return mBackboneRouterLeader; -} +template <> inline BackboneRouter::Leader &Instance::Get(void) { return mBackboneRouterLeader; } #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE -template <> inline BackboneRouter::Local &Instance::Get(void) -{ - return mBackboneRouterLocal; -} -template <> inline BackboneRouter::Manager &Instance::Get(void) -{ - return mBackboneRouterManager; -} +template <> inline BackboneRouter::Local &Instance::Get(void) { return mBackboneRouterLocal; } +template <> inline BackboneRouter::Manager &Instance::Get(void) { return mBackboneRouterManager; } #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE template <> inline BackboneRouter::MulticastListenersTable &Instance::Get(void) @@ -1134,100 +927,70 @@ template <> inline BackboneRouter::BackboneTmfAgent &Instance::Get(void) #endif #if OPENTHREAD_CONFIG_MLR_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) -template <> inline MlrManager &Instance::Get(void) -{ - return mMlrManager; -} +template <> inline MlrManager &Instance::Get(void) { return mMlrManager; } #endif #if OPENTHREAD_CONFIG_DUA_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE) -template <> inline DuaManager &Instance::Get(void) -{ - return mDuaManager; -} +template <> inline DuaManager &Instance::Get(void) { return mDuaManager; } #endif -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE -template <> inline LinkMetrics::LinkMetrics &Instance::Get(void) -{ - return mLinkMetrics; -} +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE +template <> inline LinkMetrics::Initiator &Instance::Get(void) { return mInitiator; } +#endif + +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +template <> inline LinkMetrics::Subject &Instance::Get(void) { return mSubject; } #endif #endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) #if OPENTHREAD_CONFIG_OTNS_ENABLE -template <> inline Utils::Otns &Instance::Get(void) -{ - return mOtns; -} +template <> inline Utils::Otns &Instance::Get(void) { return mOtns; } #endif #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE -template <> inline BorderRouter::RoutingManager &Instance::Get(void) -{ - return mRoutingManager; -} +template <> inline BorderRouter::RoutingManager &Instance::Get(void) { return mRoutingManager; } -template <> inline BorderRouter::InfraIf &Instance::Get(void) -{ - return mRoutingManager.mInfraIf; -} +template <> inline BorderRouter::InfraIf &Instance::Get(void) { return mRoutingManager.mInfraIf; } +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE +template <> inline Nat64::Translator &Instance::Get(void) { return mNat64Translator; } #endif #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE -template <> inline Srp::Server &Instance::Get(void) -{ - return mSrpServer; -} +template <> inline Srp::Server &Instance::Get(void) { return mSrpServer; } #endif #endif // OPENTHREAD_MTD || OPENTHREAD_FTD #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE -template <> inline Mac::LinkRaw &Instance::Get(void) -{ - return mLinkRaw; -} +template <> inline Mac::LinkRaw &Instance::Get(void) { return mLinkRaw; } #if OPENTHREAD_RADIO -template <> inline Mac::SubMac &Instance::Get(void) -{ - return mLinkRaw.mSubMac; -} +template <> inline Mac::SubMac &Instance::Get(void) { return mLinkRaw.mSubMac; } #endif #endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE -template <> inline Tasklet::Scheduler &Instance::Get(void) -{ - return mTaskletScheduler; -} +template <> inline Tasklet::Scheduler &Instance::Get(void) { return mTaskletScheduler; } -template <> inline TimerMilli::Scheduler &Instance::Get(void) -{ - return mTimerMilliScheduler; -} +template <> inline TimerMilli::Scheduler &Instance::Get(void) { return mTimerMilliScheduler; } #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE -template <> inline TimerMicro::Scheduler &Instance::Get(void) -{ - return mTimerMicroScheduler; -} +template <> inline TimerMicro::Scheduler &Instance::Get(void) { return mTimerMicroScheduler; } #endif #if OPENTHREAD_ENABLE_VENDOR_EXTENSION -template <> inline Extension::ExtensionBase &Instance::Get(void) -{ - return mExtension; -} +template <> inline Extension::ExtensionBase &Instance::Get(void) { return mExtension; } #endif #if OPENTHREAD_CONFIG_DIAG_ENABLE -template <> inline FactoryDiags::Diags &Instance::Get(void) -{ - return mDiags; -} +template <> inline FactoryDiags::Diags &Instance::Get(void) { return mDiags; } +#endif + +#if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +template <> inline Utils::PowerCalibration &Instance::Get(void) { return mPowerCalibration; } #endif /** diff --git a/src/core/common/linked_list.hpp b/src/core/common/linked_list.hpp index b9248771077..f79ac91d078 100644 --- a/src/core/common/linked_list.hpp +++ b/src/core/common/linked_list.hpp @@ -458,10 +458,10 @@ template class LinkedList * */ template - const Type *FindMatching(const Type * aBegin, - const Type * aEnd, + const Type *FindMatching(const Type *aBegin, + const Type *aEnd, const Indicator &aIndicator, - const Type *& aPrevEntry) const + const Type *&aPrevEntry) const { const Type *entry; diff --git a/src/core/common/locator_getters.hpp b/src/core/common/locator_getters.hpp index e1c68855c86..990b8c6d622 100644 --- a/src/core/common/locator_getters.hpp +++ b/src/core/common/locator_getters.hpp @@ -39,6 +39,7 @@ #include "common/instance.hpp" #include "common/locator.hpp" +#include "common/tasklet.hpp" namespace ot { @@ -49,6 +50,26 @@ inline Type &GetProvider::Get(void) const return static_cast(this)->GetInstance().template Get(); } +template +void TaskletIn::HandleTasklet(Tasklet &aTasklet) +{ + (aTasklet.Get().*HandleTaskletPtr)(); +} + +template +void TimerMilliIn::HandleTimer(Timer &aTimer) +{ + (aTimer.Get().*HandleTimertPtr)(); +} + +#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE +template +void TimerMicroIn::HandleTimer(Timer &aTimer) +{ + (aTimer.Get().*HandleTimertPtr)(); +} +#endif + } // namespace ot #endif // LOCATOR_GETTERS_HPP_ diff --git a/src/core/common/log.cpp b/src/core/common/log.cpp index a0a126a661e..04c74bbb2e8 100644 --- a/src/core/common/log.cpp +++ b/src/core/common/log.cpp @@ -39,6 +39,7 @@ #include "common/code_utils.hpp" #include "common/instance.hpp" +#include "common/num_utils.hpp" #include "common/string.hpp" /* @@ -97,7 +98,7 @@ void Logger::LogVarArgs(const char *aModuleName, LogLevel aLogLevel, const char static_assert(sizeof(kModuleNamePadding) == kMaxLogModuleNameLength + 1, "Padding string is not correct"); #if OPENTHREAD_CONFIG_LOG_PREPEND_UPTIME - ot::Uptime::UptimeToString(ot::Instance::Get().Get().GetUptime(), logString); + ot::Uptime::UptimeToString(ot::Instance::Get().Get().GetUptime(), logString, /* aInlcudeMsec */ true); logString.Append(" "); #endif @@ -244,7 +245,7 @@ void Logger::DumpInModule(const char *aModuleName, for (uint16_t i = 0; i < aDataLength; i += kDumpBytesPerLine) { DumpLine(aModuleName, aLogLevel, static_cast(aData) + i, - OT_MIN((aDataLength - i), kDumpBytesPerLine)); + Min(static_cast(aDataLength - i), kDumpBytesPerLine)); } string.Clear(); diff --git a/src/core/common/log.hpp b/src/core/common/log.hpp index 0870e0685a4..90fec6c2fd1 100644 --- a/src/core/common/log.hpp +++ b/src/core/common/log.hpp @@ -107,7 +107,7 @@ constexpr uint8_t kMaxLogModuleNameLength = 14; ///< Maximum module name length * @param[in] ... Arguments for the format specification. * */ -#define LogCrit(...) Logger::Log(__VA_ARGS__) +#define LogCrit(...) Logger::LogAtLevel(kLogModuleName, __VA_ARGS__) #else #define LogCrit(...) #endif @@ -119,7 +119,7 @@ constexpr uint8_t kMaxLogModuleNameLength = 14; ///< Maximum module name length * @param[in] ... Arguments for the format specification. * */ -#define LogWarn(...) Logger::Log(__VA_ARGS__) +#define LogWarn(...) Logger::LogAtLevel(kLogModuleName, __VA_ARGS__) #else #define LogWarn(...) #endif @@ -131,7 +131,7 @@ constexpr uint8_t kMaxLogModuleNameLength = 14; ///< Maximum module name length * @param[in] ... Arguments for the format specification. * */ -#define LogNote(...) Logger::Log(__VA_ARGS__) +#define LogNote(...) Logger::LogAtLevel(kLogModuleName, __VA_ARGS__) #else #define LogNote(...) #endif @@ -143,7 +143,7 @@ constexpr uint8_t kMaxLogModuleNameLength = 14; ///< Maximum module name length * @param[in] ... Arguments for the format specification. * */ -#define LogInfo(...) Logger::Log(__VA_ARGS__) +#define LogInfo(...) Logger::LogAtLevel(kLogModuleName, __VA_ARGS__) #else #define LogInfo(...) #endif @@ -155,7 +155,7 @@ constexpr uint8_t kMaxLogModuleNameLength = 14; ///< Maximum module name length * @param[in] ... Arguments for the format specification. * */ -#define LogDebg(...) Logger::Log(__VA_ARGS__) +#define LogDebg(...) Logger::LogAtLevel(kLogModuleName, __VA_ARGS__) #else #define LogDebg(...) #endif @@ -305,15 +305,13 @@ class Logger // and instead the logging macros should be used. public: - template - static void Log(const char *aFormat, Args... aArgs) - { - LogAtLevel(kModuleName, aFormat, aArgs...); - } + static void LogInModule(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, ...) + OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(3, 4); - static void LogInModule(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, ...); + template + static void LogAtLevel(const char *aModuleName, const char *aFormat, ...) + OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3); - template static void LogAtLevel(const char *aModuleName, const char *aFormat, ...); static void LogVarArgs(const char *aModuleName, LogLevel aLogLevel, const char *aFormat, va_list aArgs); #if OPENTHREAD_CONFIG_LOG_PKT_DUMP diff --git a/src/core/common/message.cpp b/src/core/common/message.cpp index 4847aac95f1..e7727e21f93 100644 --- a/src/core/common/message.cpp +++ b/src/core/common/message.cpp @@ -40,6 +40,7 @@ #include "common/instance.hpp" #include "common/locator_getters.hpp" #include "common/log.hpp" +#include "common/num_utils.hpp" #include "common/numeric_limits.hpp" #include "net/checksum.hpp" #include "net/ip6.hpp" @@ -63,9 +64,8 @@ RegisterLogModule("Message"); MessagePool::MessagePool(Instance &aInstance) : InstanceLocator(aInstance) -#if !OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT && !OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE - , mNumFreeBuffers(kNumBuffers) -#endif + , mNumAllocated(0) + , mMaxAllocated(0) { #if OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT otPlatMessagePoolInit(&GetInstance(), kNumBuffers, sizeof(Buffer)); @@ -98,6 +98,13 @@ Message *MessagePool::Allocate(Message::Type aType, uint16_t aReserveHeader, con return message; } +Message *MessagePool::Allocate(Message::Type aType) { return Allocate(aType, 0, Message::Settings::GetDefault()); } + +Message *MessagePool::Allocate(Message::Type aType, uint16_t aReserveHeader) +{ + return Allocate(aType, aReserveHeader, Message::Settings::GetDefault()); +} + void MessagePool::Free(Message *aMessage) { OT_ASSERT(aMessage->Next() == nullptr && aMessage->Prev() == nullptr); @@ -122,9 +129,8 @@ Buffer *MessagePool::NewBuffer(Message::Priority aPriority) SuccessOrExit(ReclaimBuffers(aPriority)); } -#if !OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT && !OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE - mNumFreeBuffers--; -#endif + mNumAllocated++; + mMaxAllocated = Max(mMaxAllocated, mNumAllocated); buffer->SetNextBuffer(nullptr); @@ -148,16 +154,14 @@ void MessagePool::FreeBuffers(Buffer *aBuffer) otPlatMessagePoolFree(&GetInstance(), aBuffer); #else mBufferPool.Free(*aBuffer); - mNumFreeBuffers++; #endif + mNumAllocated--; + aBuffer = next; } } -Error MessagePool::ReclaimBuffers(Message::Priority aPriority) -{ - return Get().EvictMessage(aPriority); -} +Error MessagePool::ReclaimBuffers(Message::Priority aPriority) { return Get().EvictMessage(aPriority); } uint16_t MessagePool::GetFreeBufferCount(void) const { @@ -172,7 +176,7 @@ uint16_t MessagePool::GetFreeBufferCount(void) const #elif OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT rval = otPlatMessagePoolNumFreeBuffers(&GetInstance()); #else - rval = mNumFreeBuffers; + rval = kNumBuffers - mNumAllocated; #endif return rval; @@ -229,8 +233,8 @@ Error Message::ResizeMessage(uint16_t aLength) // requested length. Error error = kErrorNone; - Buffer * curBuffer = this; - Buffer * lastBuffer; + Buffer *curBuffer = this; + Buffer *lastBuffer; uint16_t curLength = kHeadBufferDataSize; while (curLength < aLength) @@ -255,10 +259,7 @@ Error Message::ResizeMessage(uint16_t aLength) return error; } -void Message::Free(void) -{ - GetMessagePool()->Free(this); -} +void Message::Free(void) { GetMessagePool()->Free(this); } Message *Message::GetNext(void) const { @@ -487,6 +488,67 @@ void Message::RemoveHeader(uint16_t aLength) } } +void Message::RemoveHeader(uint16_t aOffset, uint16_t aLength) +{ + // To shrink the header, we copy the header byte before `aOffset` + // forward. Starting at offset `aLength`, we write bytes we read + // from offset `0` onward and copy a total of `aOffset` bytes. + // Then remove the first `aLength` bytes from message. + // + // + // 0 aOffset aOffset + aLength + // +-----------------------+---------+------------------------+ + // | / / / / / / / / / / / | x x x x | | + // +-----------------------+---------+------------------------+ + // + // 0 aLength aOffset + aLength + // +---------+-----------------------+------------------------+ + // | | / / / / / / / / / / / | | + // +---------+-----------------------+------------------------+ + // + // 0 aOffset + // +-----------------------+------------------------+ + // | / / / / / / / / / / / | | + // +-----------------------+------------------------+ + // + + WriteBytesFromMessage(/* aWriteOffset */ aLength, *this, /* aReadOffset */ 0, /* aLength */ aOffset); + RemoveHeader(aLength); +} + +Error Message::InsertHeader(uint16_t aOffset, uint16_t aLength) +{ + Error error; + + // To make space in header at `aOffset`, we first prepend + // `aLength` bytes at front. Then copy the existing bytes + // backwards. Starting at offset `0`, we write bytes we read + // from offset `aLength` onward and copy a total of `aOffset` + // bytes. + // + // 0 aOffset + // +-----------------------+------------------------+ + // | / / / / / / / / / / / | | + // +-----------------------+------------------------+ + // + // 0 aLength aOffset + aLength + // +---------+-----------------------+------------------------+ + // | | / / / / / / / / / / / | | + // +---------+-----------------------+------------------------+ + // + // 0 aOffset aOffset + aLength + // +-----------------------+---------+------------------------+ + // | / / / / / / / / / / / | N E W | | + // +-----------------------+---------+------------------------+ + // + + SuccessOrExit(error = PrependBytes(nullptr, aLength)); + WriteBytesFromMessage(/* aWriteOffset */ 0, *this, /* aReadOffset */ aLength, /* aLength */ aOffset); + +exit: + return error; +} + void Message::GetFirstChunk(uint16_t aOffset, uint16_t &aLength, Chunk &aChunk) const { // This method gets the first message chunk (contiguous data @@ -652,29 +714,49 @@ void Message::WriteBytes(uint16_t aOffset, const void *aBuf, uint16_t aLength) } } -uint16_t Message::CopyTo(uint16_t aSourceOffset, uint16_t aDestinationOffset, uint16_t aLength, Message &aMessage) const +void Message::WriteBytesFromMessage(uint16_t aWriteOffset, + const Message &aMessage, + uint16_t aReadOffset, + uint16_t aLength) { - uint16_t bytesCopied = 0; - Chunk chunk; + if ((&aMessage != this) || (aReadOffset >= aWriteOffset)) + { + Chunk chunk; - // This implementing can potentially overwrite the data when bytes are - // being copied forward within the same message, i.e., source and - // destination messages are the same, and source offset is smaller than - // the destination offset. We assert not allowing such a use. + aMessage.GetFirstChunk(aReadOffset, aLength, chunk); - OT_ASSERT((&aMessage != this) || (aSourceOffset >= aDestinationOffset)); + while (chunk.GetLength() > 0) + { + WriteBytes(aWriteOffset, chunk.GetBytes(), chunk.GetLength()); + aWriteOffset += chunk.GetLength(); + aMessage.GetNextChunk(aLength, chunk); + } + } + else + { + // We are copying bytes within the same message forward. + // To ensure copy forward works, we read and write from + // end of range and move backwards. - GetFirstChunk(aSourceOffset, aLength, chunk); + static constexpr uint16_t kBufSize = 32; - while (chunk.GetLength() > 0) - { - aMessage.WriteBytes(aDestinationOffset, chunk.GetBytes(), chunk.GetLength()); - aDestinationOffset += chunk.GetLength(); - bytesCopied += chunk.GetLength(); - GetNextChunk(aLength, chunk); - } + uint8_t buf[kBufSize]; + + aWriteOffset += aLength; + aReadOffset += aLength; + + while (aLength > 0) + { + uint16_t copyLength = Min(kBufSize, aLength); + + aLength -= copyLength; + aReadOffset -= copyLength; + aWriteOffset -= copyLength; - return bytesCopied; + ReadBytes(aReadOffset, buf, copyLength); + WriteBytes(aWriteOffset, buf, copyLength); + } + } } Message *Message::Clone(uint16_t aLength) const @@ -684,13 +766,13 @@ Message *Message::Clone(uint16_t aLength) const Settings settings(IsLinkSecurityEnabled() ? kWithLinkSecurity : kNoLinkSecurity, GetPriority()); uint16_t offset; + aLength = Min(GetLength(), aLength); messageCopy = GetMessagePool()->Allocate(GetType(), GetReserved(), settings); VerifyOrExit(messageCopy != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = messageCopy->SetLength(aLength)); - CopyTo(0, 0, aLength, *messageCopy); + SuccessOrExit(error = messageCopy->AppendBytesFromMessage(*this, 0, aLength)); // Copy selected message information. - offset = GetOffset() < aLength ? GetOffset() : aLength; + offset = Min(GetOffset(), aLength); messageCopy->SetOffset(offset); messageCopy->SetSubType(GetSubType()); @@ -703,25 +785,13 @@ Message *Message::Clone(uint16_t aLength) const return messageCopy; } -bool Message::GetChildMask(uint16_t aChildIndex) const -{ - return GetMetadata().mChildMask.Get(aChildIndex); -} +bool Message::GetChildMask(uint16_t aChildIndex) const { return GetMetadata().mChildMask.Get(aChildIndex); } -void Message::ClearChildMask(uint16_t aChildIndex) -{ - GetMetadata().mChildMask.Set(aChildIndex, false); -} +void Message::ClearChildMask(uint16_t aChildIndex) { GetMetadata().mChildMask.Set(aChildIndex, false); } -void Message::SetChildMask(uint16_t aChildIndex) -{ - GetMetadata().mChildMask.Set(aChildIndex, true); -} +void Message::SetChildMask(uint16_t aChildIndex) { GetMetadata().mChildMask.Set(aChildIndex, true); } -bool Message::IsChildPending(void) const -{ - return GetMetadata().mChildMask.HasAny(); -} +bool Message::IsChildPending(void) const { return GetMetadata().mChildMask.HasAny(); } void Message::SetLinkInfo(const ThreadLinkInfo &aLinkInfo) { @@ -835,15 +905,9 @@ void MessageQueue::DequeueAndFreeAll(void) } } -Message::Iterator MessageQueue::begin(void) -{ - return Message::Iterator(GetHead()); -} +Message::Iterator MessageQueue::begin(void) { return Message::Iterator(GetHead()); } -Message::ConstIterator MessageQueue::begin(void) const -{ - return Message::ConstIterator(GetHead()); -} +Message::ConstIterator MessageQueue::begin(void) const { return Message::ConstIterator(GetHead()); } void MessageQueue::GetInfo(Info &aInfo) const { @@ -909,16 +973,13 @@ const Message *PriorityQueue::GetHeadForPriority(Message::Priority aPriority) co return head; } -const Message *PriorityQueue::GetTail(void) const -{ - return FindFirstNonNullTail(Message::kPriorityLow); -} +const Message *PriorityQueue::GetTail(void) const { return FindFirstNonNullTail(Message::kPriorityLow); } void PriorityQueue::Enqueue(Message &aMessage) { Message::Priority priority; - Message * tail; - Message * next; + Message *tail; + Message *next; OT_ASSERT(!aMessage.IsInAQueue()); @@ -949,7 +1010,7 @@ void PriorityQueue::Enqueue(Message &aMessage) void PriorityQueue::Dequeue(Message &aMessage) { Message::Priority priority; - Message * tail; + Message *tail; OT_ASSERT(aMessage.GetPriorityQueue() == this); @@ -993,15 +1054,9 @@ void PriorityQueue::DequeueAndFreeAll(void) } } -Message::Iterator PriorityQueue::begin(void) -{ - return Message::Iterator(GetHead()); -} +Message::Iterator PriorityQueue::begin(void) { return Message::Iterator(GetHead()); } -Message::ConstIterator PriorityQueue::begin(void) const -{ - return Message::ConstIterator(GetHead()); -} +Message::ConstIterator PriorityQueue::begin(void) const { return Message::ConstIterator(GetHead()); } void PriorityQueue::GetInfo(Info &aInfo) const { diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index 78adcee486c..307383724e7 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -39,6 +39,7 @@ #include #include +#include #include #include "common/as_core_type.hpp" @@ -186,10 +187,10 @@ class Buffer : public otMessageBuffer, public LinkedListEntry protected: struct Metadata { - Message * mNext; // Next message in a doubly linked list. - Message * mPrev; // Previous message in a doubly linked list. + Message *mNext; // Next message in a doubly linked list. + Message *mPrev; // Previous message in a doubly linked list. MessagePool *mMessagePool; // Message pool for this message. - void * mQueue; // The queue where message is queued (if any). Queue type from `mInPriorityQ`. + void *mQueue; // The queue where message is queued (if any). Queue type from `mInPriorityQ`. uint32_t mDatagramTag; // The datagram tag used for 6LoWPAN frags or IPv6fragmentation. TimeMilli mTimestamp; // The message timestamp. uint16_t mReserved; // Number of reserved bytes (for header). @@ -231,13 +232,13 @@ class Buffer : public otMessageBuffer, public LinkedListEntry static constexpr uint16_t kBufferDataSize = kBufferSize - sizeof(otMessageBuffer); static constexpr uint16_t kHeadBufferDataSize = kBufferDataSize - sizeof(Metadata); - Metadata & GetMetadata(void) { return mBuffer.mHead.mMetadata; } + Metadata &GetMetadata(void) { return mBuffer.mHead.mMetadata; } const Metadata &GetMetadata(void) const { return mBuffer.mHead.mMetadata; } - uint8_t * GetFirstData(void) { return mBuffer.mHead.mData; } + uint8_t *GetFirstData(void) { return mBuffer.mHead.mData; } const uint8_t *GetFirstData(void) const { return mBuffer.mHead.mData; } - uint8_t * GetData(void) { return mBuffer.mData; } + uint8_t *GetData(void) { return mBuffer.mData; } const uint8_t *GetData(void) const { return mBuffer.mData; } private: @@ -252,7 +253,8 @@ class Buffer : public otMessageBuffer, public LinkedListEntry } mBuffer; }; -static_assert(sizeof(Buffer) >= kBufferSize, "Buffer size if not valid"); +static_assert(sizeof(Buffer) >= kBufferSize, + "Buffer size is not valid. Increase OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE."); /** * This class represents a message. @@ -279,7 +281,8 @@ class Message : public otMessage, public Buffer, public GetProvider kType6lowpan = 1, ///< A 6lowpan frame kTypeSupervision = 2, ///< A child supervision frame. kTypeMacEmptyData = 3, ///< An empty MAC data frame. - kTypeOther = 4, ///< Other (data) message. + kTypeIp4 = 4, ///< A full uncompressed IPv4 packet, for NAT64. + kTypeOther = 5, ///< Other (data) message. }; /** @@ -605,13 +608,48 @@ class Message : public otMessage, public Buffer, public GetProvider } /** - * This method removes header bytes from the message. + * This method removes header bytes from the message at start of message. * - * @param[in] aLength Number of header bytes to remove. + * The caller MUST ensure that message contains the bytes to be removed, i.e. `aOffset` is smaller than the message + * length. + * + * @param[in] aLength Number of header bytes to remove from start of `Message`. * */ void RemoveHeader(uint16_t aLength); + /** + * This method removes header bytes from the message at a given offset. + * + * This method shrinks the message. The existing header bytes before @p aOffset are copied forward and replace the + * removed bytes. + * + * The caller MUST ensure that message contains the bytes to be removed, i.e. `aOffset + aLength` is smaller than + * the message length. + * + * @param[in] aOffset The offset to start removing. + * @param[in] aLength Number of header bytes to remove. + * + */ + void RemoveHeader(uint16_t aOffset, uint16_t aLength); + + /** + * This method grows the message to make space for new header bytes at a given offset. + * + * This method grows the message header (similar to `PrependBytes()`). The existing header bytes from start to + * `aOffset + aLength` are then copied backward to make room for the new header bytes. Note that this method does + * not change the bytes from @p aOffset up @p aLength (the new inserted header range). Caller can write to this + * range to update the bytes after successful return from this method. + * + * @param[in] aOffset The offset at which to insert the header bytes + * @param[in] aLength Number of header bytes to insert. + * + * @retval kErrorNone Successfully grown the message and copied the existing header bytes. + * @retval kErrorNoBufs Insufficient available buffers to grow the message. + * + */ + Error InsertHeader(uint16_t aOffset, uint16_t aLength); + /** * This method appends bytes to the end of the message. * @@ -806,6 +844,23 @@ class Message : public otMessage, public Buffer, public GetProvider */ void WriteBytes(uint16_t aOffset, const void *aBuf, uint16_t aLength); + /** + * This method writes bytes read from another or potentially the same message to the message at a given offset. + * + * This method will not resize the message. The bytes to write (with @p aLength) MUST fit within the existing + * message buffer (from the given @p aWriteOffset up to the message's length). + * + * This method can be used to copy bytes within the same message in either direction, i.e., copy forward where + * `aWriteOffset > aReadOffset` or copy backward where `aWriteOffset < aReadOffset`. + * + * @param[in] aWriteOffset Byte offset within this message to begin writing. + * @param[in] aMessage The message to read the bytes from. + * @param[in] aReadOffset The offset in @p aMessage to start reading the bytes from. + * @param[in] aLength The number of bytes to read from @p aMessage and write. + * + */ + void WriteBytesFromMessage(uint16_t aWriteOffset, const Message &aMessage, uint16_t aReadOffset, uint16_t aLength); + /** * This methods writes an object to the message. * @@ -842,23 +897,6 @@ class Message : public otMessage, public Buffer, public GetProvider WriteBytes(aOffset, aData.GetBytes(), aData.GetLength()); } - /** - * This method copies bytes from one message to another. - * - * If source and destination messages are the same, `CopyTo()` can be used to perform a backward copy, but - * it MUST not be used to forward copy within the same message (i.e., when source and destination messages are the - * same and source offset is smaller than the destination offset). - * - * @param[in] aSourceOffset Byte offset within the source message to begin reading. - * @param[in] aDestinationOffset Byte offset within the destination message to begin writing. - * @param[in] aLength Number of bytes to copy. - * @param[in] aMessage Message to copy to. - * - * @returns The number of bytes copied. - * - */ - uint16_t CopyTo(uint16_t aSourceOffset, uint16_t aDestinationOffset, uint16_t aLength, Message &aMessage) const; - /** * This method creates a copy of the message. * @@ -1123,7 +1161,7 @@ class Message : public otMessage, public Buffer, public GetProvider /** * This method returns the average RSS (Received Signal Strength) associated with the message. * - * @returns The current average RSS value (in dBm) or OT_RADIO_RSSI_INVALID if no average is available. + * @returns The current average RSS value (in dBm) or `Radio::kInvalidRssi` if no average is available. * */ int8_t GetAverageRss(void) const { return GetMetadata().mRssAverager.GetAverage(); } @@ -1370,11 +1408,11 @@ class Message : public otMessage, public Buffer, public GetProvider void SetMessageQueue(MessageQueue *aMessageQueue); void SetPriorityQueue(PriorityQueue *aPriorityQueue); - Message *& Next(void) { return GetMetadata().mNext; } + Message *&Next(void) { return GetMetadata().mNext; } Message *const &Next(void) const { return GetMetadata().mNext; } - Message *& Prev(void) { return GetMetadata().mPrev; } + Message *&Prev(void) { return GetMetadata().mPrev; } - static Message * NextOf(Message *aMessage) { return (aMessage != nullptr) ? aMessage->Next() : nullptr; } + static Message *NextOf(Message *aMessage) { return (aMessage != nullptr) ? aMessage->Next() : nullptr; } static const Message *NextOf(const Message *aMessage) { return (aMessage != nullptr) ? aMessage->Next() : nullptr; } Error ResizeMessage(uint16_t aLength); @@ -1489,7 +1527,7 @@ class MessageQueue : public otMessageQueue Message::ConstIterator end(void) const { return Message::ConstIterator(); } private: - Message * GetTail(void) { return static_cast(mData); } + Message *GetTail(void) { return static_cast(mData); } const Message *GetTail(void) const { return static_cast(mData); } void SetTail(Message *aMessage) { mData = aMessage; } }; @@ -1503,6 +1541,7 @@ class PriorityQueue : private Clearable friend class Message; friend class MessageQueue; friend class MessagePool; + friend class Clearable; public: typedef otMessageQueueInfo Info; ///< This struct represents info (number of messages/buffers) about a queue. @@ -1667,9 +1706,28 @@ class MessagePool : public InstanceLocator, private NonCopyable * @returns A pointer to the message or `nullptr` if no message buffers are available. * */ - Message *Allocate(Message::Type aType, - uint16_t aReserveHeader = 0, - const Message::Settings &aSettings = Message::Settings::GetDefault()); + Message *Allocate(Message::Type aType, uint16_t aReserveHeader, const Message::Settings &aSettings); + + /** + * This method allocates a new message of a given type using default settings. + * + * @param[in] aType The message type. + * + * @returns A pointer to the message or `nullptr` if no message buffers are available. + * + */ + Message *Allocate(Message::Type aType); + + /** + * This method allocates a new message with a given type and reserved length using default settings. + * + * @param[in] aType The message type. + * @param[in] aReserveHeader The number of header bytes to reserve. + * + * @returns A pointer to the message or `nullptr` if no message buffers are available. + * + */ + Message *Allocate(Message::Type aType, uint16_t aReserveHeader); /** * This method is used to free a message and return all message buffers to the buffer pool. @@ -1695,21 +1753,36 @@ class MessagePool : public InstanceLocator, private NonCopyable */ uint16_t GetTotalBufferCount(void) const; + /** + * This method returns the maximum number of buffers in use at the same time since OT stack initialization or + * since last call to `ResetMaxUsedBufferCount()`. + * + * @returns The maximum number of buffers in use at the same time so far (buffer allocation watermark). + * + */ + uint16_t GetMaxUsedBufferCount(void) const { return mMaxAllocated; } + + /** + * This method resets the tracked maximum number of buffers in use. + * + * @sa GetMaxUsedBufferCount + * + */ + void ResetMaxUsedBufferCount(void) { mMaxAllocated = mNumAllocated; } + private: Buffer *NewBuffer(Message::Priority aPriority); void FreeBuffers(Buffer *aBuffer); Error ReclaimBuffers(Message::Priority aPriority); #if !OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT && !OPENTHREAD_CONFIG_MESSAGE_USE_HEAP_ENABLE - uint16_t mNumFreeBuffers; Pool mBufferPool; #endif + uint16_t mNumAllocated; + uint16_t mMaxAllocated; }; -inline Instance &Message::GetInstance(void) const -{ - return GetMessagePool()->GetInstance(); -} +inline Instance &Message::GetInstance(void) const { return GetMessagePool()->GetInstance(); } /** * @} diff --git a/src/core/common/new.hpp b/src/core/common/new.hpp index ab437b18b37..76d2f5e170e 100644 --- a/src/core/common/new.hpp +++ b/src/core/common/new.hpp @@ -31,8 +31,8 @@ * This file defines the new operator used by OpenThread. */ -#ifndef NEW_HPP_ -#define NEW_HPP_ +#ifndef OT_CORE_COMMON_NEW_HPP_ +#define OT_CORE_COMMON_NEW_HPP_ #include "openthread-core-config.h" @@ -40,9 +40,6 @@ #include -inline void *operator new(size_t, void *p) throw() -{ - return p; -} +inline void *operator new(size_t, void *p) throw() { return p; } -#endif // NEW_HPP_ +#endif // OT_CORE_COMMON_NEW_HPP_ diff --git a/src/core/common/non_copyable.hpp b/src/core/common/non_copyable.hpp index d2bd9f90e7d..b6e44feada1 100644 --- a/src/core/common/non_copyable.hpp +++ b/src/core/common/non_copyable.hpp @@ -43,7 +43,7 @@ namespace ot { class NonCopyable { public: - NonCopyable(const NonCopyable &) = delete; + NonCopyable(const NonCopyable &) = delete; NonCopyable &operator=(const NonCopyable &) = delete; protected: diff --git a/src/core/common/notifier.cpp b/src/core/common/notifier.cpp index 7fd2831a67e..4cc5a9634cb 100644 --- a/src/core/common/notifier.cpp +++ b/src/core/common/notifier.cpp @@ -46,12 +46,11 @@ RegisterLogModule("Notifier"); Notifier::Notifier(Instance &aInstance) : InstanceLocator(aInstance) - , mTask(aInstance, Notifier::EmitEvents) + , mTask(aInstance) { for (ExternalCallback &callback : mExternalCallbacks) { - callback.mHandler = nullptr; - callback.mContext = nullptr; + callback.Clear(); } } @@ -64,23 +63,17 @@ Error Notifier::RegisterCallback(otStateChangedCallback aCallback, void *aContex for (ExternalCallback &callback : mExternalCallbacks) { - if (callback.mHandler == nullptr) - { - if (unusedCallback == nullptr) - { - unusedCallback = &callback; - } + VerifyOrExit(!callback.Matches(aCallback, aContext), error = kErrorAlready); - continue; + if (!callback.IsSet() && (unusedCallback == nullptr)) + { + unusedCallback = &callback; } - - VerifyOrExit((callback.mHandler != aCallback) || (callback.mContext != aContext), error = kErrorAlready); } VerifyOrExit(unusedCallback != nullptr, error = kErrorNoBufs); - unusedCallback->mHandler = aCallback; - unusedCallback->mContext = aContext; + unusedCallback->Set(aCallback, aContext); exit: return error; @@ -92,10 +85,9 @@ void Notifier::RemoveCallback(otStateChangedCallback aCallback, void *aContext) for (ExternalCallback &callback : mExternalCallbacks) { - if ((callback.mHandler == aCallback) && (callback.mContext == aContext)) + if (callback.Matches(aCallback, aContext)) { - callback.mHandler = nullptr; - callback.mContext = nullptr; + callback.Clear(); } } @@ -118,11 +110,6 @@ void Notifier::SignalIfFirst(Event aEvent) } } -void Notifier::EmitEvents(Tasklet &aTasklet) -{ - aTasklet.Get().EmitEvents(); -} - void Notifier::EmitEvents(void) { Events events; @@ -146,9 +133,7 @@ void Notifier::EmitEvents(void) #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE Get().HandleNotifierEvents(events); #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - Get().HandleNotifierEvents(events); -#endif + Get().HandleNotifierEvents(events); #if OPENTHREAD_CONFIG_DATASET_UPDATER_ENABLE || OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE Get().HandleNotifierEvents(events); #endif @@ -204,10 +189,7 @@ void Notifier::EmitEvents(void) for (ExternalCallback &callback : mExternalCallbacks) { - if (callback.mHandler != nullptr) - { - callback.mHandler(events.GetAsFlags(), callback.mContext); - } + callback.InvokeIfSet(events.GetAsFlags()); } exit: @@ -233,7 +215,7 @@ void Notifier::LogEvents(Events aEvents) const { if (string.GetLength() >= kFlagsStringLineLimit) { - LogInfo("StateChanged (0x%08x) %s%s ...", aEvents.GetAsFlags(), didLog ? "... " : "[", + LogInfo("StateChanged (0x%08lx) %s%s ...", ToUlong(aEvents.GetAsFlags()), didLog ? "... " : "[", string.AsCString()); string.Clear(); didLog = true; @@ -248,7 +230,7 @@ void Notifier::LogEvents(Events aEvents) const } exit: - LogInfo("StateChanged (0x%08x) %s%s]", aEvents.GetAsFlags(), didLog ? "... " : "[", string.AsCString()); + LogInfo("StateChanged (0x%08lx) %s%s]", ToUlong(aEvents.GetAsFlags()), didLog ? "... " : "[", string.AsCString()); } const char *Notifier::EventToString(Event aEvent) const @@ -305,14 +287,9 @@ const char *Notifier::EventToString(Event aEvent) const #else // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_INFO) -void Notifier::LogEvents(Events) const -{ -} +void Notifier::LogEvents(Events) const {} -const char *Notifier::EventToString(Event) const -{ - return ""; -} +const char *Notifier::EventToString(Event) const { return ""; } #endif // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_INFO) diff --git a/src/core/common/notifier.hpp b/src/core/common/notifier.hpp index 8887671aff4..cd05f2dc538 100644 --- a/src/core/common/notifier.hpp +++ b/src/core/common/notifier.hpp @@ -42,6 +42,7 @@ #include #include +#include "common/callback.hpp" #include "common/error.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" @@ -95,6 +96,7 @@ enum Event : uint32_t kEventJoinerStateChanged = OT_CHANGED_JOINER_STATE, ///< Joiner state changed kEventActiveDatasetChanged = OT_CHANGED_ACTIVE_DATASET, ///< Active Dataset changed kEventPendingDatasetChanged = OT_CHANGED_PENDING_DATASET, ///< Pending Dataset changed + kEventNat64TranslatorStateChanged = OT_CHANGED_NAT64_TRANSLATOR_STATE, ///< Nat64Translator state changed }; /** @@ -307,21 +309,18 @@ class Notifier : public InstanceLocator, private NonCopyable static constexpr uint16_t kFlagsStringBufferSize = kFlagsStringLineLimit + kMaxFlagNameLength; - struct ExternalCallback - { - otStateChangedCallback mHandler; - void * mContext; - }; + typedef Callback ExternalCallback; - static void EmitEvents(Tasklet &aTasklet); - void EmitEvents(void); + void EmitEvents(void); void LogEvents(Events aEvents) const; const char *EventToString(Event aEvent) const; + using EmitEventsTask = TaskletIn; + Events mEventsToSignal; Events mSignaledEvents; - Tasklet mTask; + EmitEventsTask mTask; ExternalCallback mExternalCallbacks[kMaxExternalHandlers]; }; diff --git a/src/core/common/num_utils.hpp b/src/core/common/num_utils.hpp new file mode 100644 index 00000000000..dff0ce11355 --- /dev/null +++ b/src/core/common/num_utils.hpp @@ -0,0 +1,223 @@ +/* + * 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 generic number utility functions (min, max, clamp). + */ + +#ifndef NUM_UTILS_HPP_ +#define NUM_UTILS_HPP_ + +#include "common/numeric_limits.hpp" +#include "common/type_traits.hpp" + +namespace ot { + +/** + * This template function returns the minimum of two given values. + * + * Uses `operator<` to compare the values. + * + * @tparam Type The value type. + * + * @param[in] aFirst The first value. + * @param[in] aSecond The second value. + * + * @returns The minimum of @p aFirst and @p aSecond. + * + */ +template Type Min(Type aFirst, Type aSecond) { return (aFirst < aSecond) ? aFirst : aSecond; } + +/** + * This template function returns the maximum of two given values. + * + * Uses `operator<` to compare the values. + * + * @tparam Type The value type. + * + * @param[in] aFirst The first value. + * @param[in] aSecond The second value. + * + * @returns The maximum of @p aFirst and @p aSecond. + * + */ +template Type Max(Type aFirst, Type aSecond) { return (aFirst < aSecond) ? aSecond : aFirst; } + +/** + * This template function returns clamped version of a given value to a given closed range [min, max]. + * + * Uses `operator<` to compare the values. The behavior is undefined if the value of @p aMin is greater than @p aMax. + * + * @tparam Type The value type. + * + * @param[in] aValue The value to clamp. + * @param[in] aMin The minimum value. + * @param[in] aMax The maximum value. + * + * @returns The clamped version of @aValue to the closed range [@p aMin, @p aMax]. + * + */ +template Type Clamp(Type aValue, Type aMin, Type aMax) +{ + Type value = Max(aValue, aMin); + + return Min(value, aMax); +} + +/** + * This template function returns a clamped version of given integer to a `uint8_t`. + * + * If @p aValue is greater than max value of a `uint8_t`, the max value is returned. + * + * @tparam UintType The value type (MUST be `uint16_t`, `uint32_t`, or `uint64_t`). + * + * @param[in] aValue The value to clamp. + * + * @returns The clamped version of @p aValue to `uint8_t`. + * + */ +template uint8_t ClampToUint8(UintType aValue) +{ + static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || + TypeTraits::IsSame::kValue, + "UintType must be `uint16_t, `uint32_t`, or `uint64_t`"); + + return static_cast(Min(aValue, static_cast(NumericLimits::kMax))); +} + +/** + * This template function returns a clamped version of given integer to a `uint16_t`. + * + * If @p aValue is greater than max value of a `uint16_t`, the max value is returned. + * + * @tparam UintType The value type (MUST be `uint32_t`, or `uint64_t`). + * + * @param[in] aValue The value to clamp. + * + * @returns The clamped version of @p aValue to `uint16_t`. + * + */ +template uint16_t ClampToUint16(UintType aValue) +{ + static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue, + "UintType must be `uint32_t` or `uint64_t`"); + + return static_cast(Min(aValue, static_cast(NumericLimits::kMax))); +} + +/** + * This template function performs a three-way comparison between two values. + * + * @tparam Type The value type. + * + * @param[in] aFirst The first value. + * @param[in] aSecond The second value. + * + * @retval 1 If @p aFirst > @p aSecond. + * @retval 0 If @p aFirst == @p aSecond. + * @retval -1 If @p aFirst < @p aSecond. + * + */ +template int ThreeWayCompare(Type aFirst, Type aSecond) +{ + return (aFirst == aSecond) ? 0 : ((aFirst > aSecond) ? 1 : -1); +} + +/** + * This is template specialization of three-way comparison between two boolean values. + * + * @param[in] aFirst The first boolean value. + * @param[in] aSecond The second boolean value. + * + * @retval 1 If @p aFirst is true and @p aSecond is false (true > false). + * @retval 0 If both @p aFirst and @p aSecond are true, or both are false (they are equal). + * @retval -1 If @p aFirst is false and @p aSecond is true (false < true). + * + */ +template <> inline int ThreeWayCompare(bool aFirst, bool aSecond) +{ + return (aFirst == aSecond) ? 0 : (aFirst ? 1 : -1); +} + +/** + * This template function divides two numbers and rounds the result to the closest integer. + * + * @tparam IntType The integer type. + * + * @param[in] aDividend The dividend value. + * @param[in] aDivisor The divisor value. + * + * @return The result of division and rounding to the closest integer. + * + */ +template inline IntType DivideAndRoundToClosest(IntType aDividend, IntType aDivisor) +{ + return (aDividend + (aDivisor / 2)) / aDivisor; +} + +/** + * This function casts a given `uint32_t` to `unsigned long`. + * + * @param[in] aUint32 A `uint32_t` value. + * + * @returns The @p aUint32 value as `unsigned long`. + * + */ +inline unsigned long ToUlong(uint32_t aUint32) { return static_cast(aUint32); } + +/** + * This function counts the number of `1` bits in the binary representation of a given unsigned int bit-mask value. + * + * @tparam UintType The unsigned int type (MUST be `uint8_t`, uint16_t`, uint32_t`, or `uint64_t`). + * + * @param[in] aMask A bit mask. + * + * @returns The number of `1` bits in @p aMask. + * + */ +template uint8_t CountBitsInMask(UintType aMask) +{ + static_assert(TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue || + TypeTraits::IsSame::kValue || TypeTraits::IsSame::kValue, + "UintType must be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`"); + + uint8_t count = 0; + + while (aMask != 0) + { + aMask &= aMask - 1; + count++; + } + + return count; +} + +} // namespace ot + +#endif // NUM_UTILS_HPP_ diff --git a/src/core/common/owned_ptr.hpp b/src/core/common/owned_ptr.hpp index ab98fd3f912..c1dde762bb4 100644 --- a/src/core/common/owned_ptr.hpp +++ b/src/core/common/owned_ptr.hpp @@ -171,8 +171,8 @@ template class OwnedPtr : public Ptr return *this; } - OwnedPtr(const OwnedPtr &) = delete; - OwnedPtr(OwnedPtr &) = delete; + OwnedPtr(const OwnedPtr &) = delete; + OwnedPtr(OwnedPtr &) = delete; OwnedPtr &operator=(const OwnedPtr &) = delete; private: diff --git a/src/core/common/pool.hpp b/src/core/common/pool.hpp index 279655f14f5..4a0f0e54289 100644 --- a/src/core/common/pool.hpp +++ b/src/core/common/pool.hpp @@ -82,7 +82,7 @@ template class Pool : private NonCopyable * This constructor initializes the pool. * * This constructor version requires the `Type` class to provide method `void Init(Instance &)` to initialize - * each `Type` entry object. This can be realized by the `Type` class inheriting from `InstaceLocatorInit()`. + * each `Type` entry object. This can be realized by the `Type` class inheriting from `InstanceLocatorInit()`. * * @param[in] aInstance A reference to the OpenThread instance. * diff --git a/src/core/common/preference.cpp b/src/core/common/preference.cpp new file mode 100644 index 00000000000..44aeb522f29 --- /dev/null +++ b/src/core/common/preference.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023, 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 methods for a signed preference value and its 2-bit unsigned representation. + * + */ + +#include "preference.hpp" + +namespace ot { + +uint8_t Preference::To2BitUint(int8_t aPrf) { return (aPrf == 0) ? k2BitMedium : ((aPrf > 0) ? k2BitHigh : k2BitLow); } + +int8_t Preference::From2BitUint(uint8_t a2BitUint) +{ + static const int8_t kPreferences[] = { + /* 0 (00) -> */ kMedium, + /* 1 (01) -> */ kHigh, + /* 2 (10) -> */ kMedium, // Per RFC-4191, the reserved value (10) MUST be treated as (00) + /* 3 (11) -> */ kLow, + }; + + return kPreferences[a2BitUint & k2BitMask]; +} + +bool Preference::IsValid(int8_t aPrf) { return (aPrf == kHigh) || (aPrf == kMedium) || (aPrf == kLow); } + +const char *Preference::ToString(int8_t aPrf) { return (aPrf == 0) ? "medium" : ((aPrf > 0) ? "high" : "low"); } + +} // namespace ot diff --git a/src/core/common/preference.hpp b/src/core/common/preference.hpp new file mode 100644 index 00000000000..cf0657dd76c --- /dev/null +++ b/src/core/common/preference.hpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2023, 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 a signed preference value and its 2-bit unsigned representation used by + * Route Preference in RFC-4191. + */ + +#ifndef PREFERENCE_HPP_ +#define PREFERENCE_HPP_ + +#include "openthread-core-config.h" + +#include "common/error.hpp" + +namespace ot { + +/** + * This class provides constants and static methods to convert between `int8_t` preference and its 2-bit unsigned + * representation. + * + */ +class Preference +{ +public: + static constexpr int8_t kHigh = 1; ///< High preference. + static constexpr int8_t kMedium = 0; ///< Medium preference. + static constexpr int8_t kLow = -1; ///< Low preference. + + /** + * This static method converts a signed preference value to its corresponding 2-bit `uint8_t` value. + * + * A positive @p aPrf is mapped to "High Preference", a negative @p aPrf is mapped to "Low Preference", and + * zero @p aPrf is mapped to "Medium Preference". + * + * @param[in] aPrf The preference to convert to `uint8_t`. + * + * @returns The 2-bit unsigned value representing @p aPrf. + * + */ + static uint8_t To2BitUint(int8_t aPrf); + + /** + * This static method converts a 2-bit `uint8_t` value to a signed preference value `kHigh`, `kMedium`, and `kLow`. + * + * Only the first two bits (LSB) of @p a2BitUint are used and the rest of the bits are ignored. + * + * - `0b01` (or 1) is mapped to `kHigh`. + * - `0b00` (or 0) is mapped to `kMedium`. + * - `0b11` (or 3) is mapped to `kLow`. + * - `0b10` (or 2) is reserved for future and is also mapped to `kMedium` (this complies with RFC-4191 where + * the reserved value `0b10` MUST be treated as `0b00` for Route Preference). + * + * @param[in] a2BitUint The 2-bit unsigned value to convert from. Only two LSB bits are used and the reset are + * ignored. + * + * @returns The signed preference `kHigh`, `kMedium`, or `kLow` corresponding to @p a2BitUint. + * + */ + static int8_t From2BitUint(uint8_t a2BitUint); + + /** + * This static method indicates whether a given `int8_t` preference value is valid, i.e., whether it has of the + * three values `kHigh`, `kMedium`, or `kLow`. + * + * @param[in] aPrf The signed preference value to check. + * + * @retval TRUE if @p aPrf is valid. + * @retval FALSE if @p aPrf is not valid + * + */ + static bool IsValid(int8_t aPrf); + + /** + * This static method indicates whether a given 2-bit `uint8_t` preference value is valid. + * + * @param[in] a2BitUint The 2-bit unsigned value to convert from. Only two LSB bits are used and the reset are + * ignored. + * + * @retval TRUE if the first 2 bits of @p a2BitUint are `0b00`, `0b01`, or `0b11`. + * @retval FALSE if the first 2 bits of @p a2BitUint are `0b01`. + * + */ + static bool Is2BitUintValid(uint8_t a2BitUint) { return ((a2BitUint & k2BitMask) != k2BitReserved); } + + /** + * This static method converts a given preference to a human-readable string. + * + * @param[in] aPrf The preference to convert. + * + * @returns The string representation of @p aPrf. + * + */ + static const char *ToString(int8_t aPrf); + + Preference(void) = delete; + +private: + static constexpr uint8_t k2BitMask = 3; + + static constexpr uint8_t k2BitHigh = 1; // 0b01 + static constexpr uint8_t k2BitMedium = 0; // 0b00 + static constexpr uint8_t k2BitLow = 3; // 0b11 + static constexpr uint8_t k2BitReserved = 2; // 0b10 +}; + +} // namespace ot + +#endif // PREFERENCE_HPP_ diff --git a/src/core/common/ptr_wrapper.hpp b/src/core/common/ptr_wrapper.hpp index 1a655432b6d..3704d76ceb0 100644 --- a/src/core/common/ptr_wrapper.hpp +++ b/src/core/common/ptr_wrapper.hpp @@ -111,7 +111,7 @@ template class Ptr * @returns The wrapped pointer. * */ - const Type *operator->(void)const { return mPointer; } + const Type *operator->(void) const { return mPointer; } /** * This method overloads the `*` dereference operator and returns a reference to the pointed object. @@ -131,7 +131,7 @@ template class Ptr * @returns A reference to the pointed object. * */ - const Type &operator*(void)const { return *mPointer; } + const Type &operator*(void) const { return *mPointer; } /** * This method overloads the operator `==` to compare the `Ptr` with a given pointer. diff --git a/src/core/common/random.hpp b/src/core/common/random.hpp index d3b75d0f2a7..01fb982bfc4 100644 --- a/src/core/common/random.hpp +++ b/src/core/common/random.hpp @@ -110,10 +110,7 @@ namespace NonCrypto { * @returns A random `uint32_t` value. * */ -inline uint32_t GetUint32(void) -{ - return Manager::NonCryptoGetUint32(); -} +inline uint32_t GetUint32(void) { return Manager::NonCryptoGetUint32(); } /** * This function generates and returns a random byte. @@ -121,10 +118,7 @@ inline uint32_t GetUint32(void) * @returns A random `uint8_t` value. * */ -inline uint8_t GetUint8(void) -{ - return static_cast(GetUint32() & 0xff); -} +inline uint8_t GetUint8(void) { return static_cast(GetUint32() & 0xff); } /** * This function generates and returns a random `uint16_t` value. @@ -132,10 +126,7 @@ inline uint8_t GetUint8(void) * @returns A random `uint16_t` value. * */ -inline uint16_t GetUint16(void) -{ - return static_cast(GetUint32() & 0xffff); -} +inline uint16_t GetUint16(void) { return static_cast(GetUint32() & 0xffff); } /** * This function generates and returns a random `uint8_t` value within a given range `[aMin, aMax)`. @@ -209,10 +200,7 @@ namespace Crypto { * @retval kErrorNone Successfully filled buffer with random values. * */ -inline Error FillBuffer(uint8_t *aBuffer, uint16_t aSize) -{ - return Manager::CryptoFillBuffer(aBuffer, aSize); -} +inline Error FillBuffer(uint8_t *aBuffer, uint16_t aSize) { return Manager::CryptoFillBuffer(aBuffer, aSize); } } // namespace Crypto diff --git a/src/core/common/settings.cpp b/src/core/common/settings.cpp index 67f76e20556..18f17d8872e 100644 --- a/src/core/common/settings.cpp +++ b/src/core/common/settings.cpp @@ -37,6 +37,7 @@ #include "common/code_utils.hpp" #include "common/instance.hpp" #include "common/locator_getters.hpp" +#include "common/num_utils.hpp" #include "meshcop/dataset.hpp" #include "thread/mle.hpp" @@ -53,26 +54,26 @@ RegisterLogModule("Settings"); void SettingsBase::NetworkInfo::Log(Action aAction) const { - LogInfo("%s NetworkInfo {rloc:0x%04x, extaddr:%s, role:%s, mode:0x%02x, version:%hu, keyseq:0x%x, ...", + LogInfo("%s NetworkInfo {rloc:0x%04x, extaddr:%s, role:%s, mode:0x%02x, version:%u, keyseq:0x%lx, ...", ActionToString(aAction), GetRloc16(), GetExtAddress().ToString().AsCString(), - Mle::Mle::RoleToString(static_cast(GetRole())), GetDeviceMode(), GetVersion(), - GetKeySequence()); + Mle::RoleToString(static_cast(GetRole())), GetDeviceMode(), GetVersion(), + ToUlong(GetKeySequence())); - LogInfo("... pid:0x%x, mlecntr:0x%x, maccntr:0x%x, mliid:%s}", GetPreviousPartitionId(), GetMleFrameCounter(), - GetMacFrameCounter(), GetMeshLocalIid().ToString().AsCString()); + LogInfo("... pid:0x%lx, mlecntr:0x%lx, maccntr:0x%lx, mliid:%s}", ToUlong(GetPreviousPartitionId()), + ToUlong(GetMleFrameCounter()), ToUlong(GetMacFrameCounter()), GetMeshLocalIid().ToString().AsCString()); } void SettingsBase::ParentInfo::Log(Action aAction) const { - LogInfo("%s ParentInfo {extaddr:%s, version:%hu}", ActionToString(aAction), GetExtAddress().ToString().AsCString(), + LogInfo("%s ParentInfo {extaddr:%s, version:%u}", ActionToString(aAction), GetExtAddress().ToString().AsCString(), GetVersion()); } #if OPENTHREAD_FTD void SettingsBase::ChildInfo::Log(Action aAction) const { - LogInfo("%s ChildInfo {rloc:0x%04x, extaddr:%s, timeout:%u, mode:0x%02x, version:%hu}", ActionToString(aAction), - GetRloc16(), GetExtAddress().ToString().AsCString(), GetTimeout(), GetMode(), GetVersion()); + LogInfo("%s ChildInfo {rloc:0x%04x, extaddr:%s, timeout:%lu, mode:0x%02x, version:%u}", ActionToString(aAction), + GetRloc16(), GetExtAddress().ToString().AsCString(), ToUlong(GetTimeout()), GetMode(), GetVersion()); } #endif @@ -105,6 +106,28 @@ void SettingsBase::SrpServerInfo::Log(Action aAction) const } #endif +#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE +Error SettingsBase::BorderAgentId::SetId(const uint8_t *aId, uint16_t aLength) +{ + Error error = kErrorNone; + + VerifyOrExit(aLength == sizeof(mId), error = kErrorInvalidArgs); + memcpy(mId, aId, aLength); + +exit: + return error; +} + +void SettingsBase::BorderAgentId::Log(Action aAction) const +{ + char buffer[sizeof(BorderAgentId) * 2 + 1]; + StringWriter sw(buffer, sizeof(buffer)); + + sw.AppendHexBytes(GetId(), sizeof(BorderAgentId)); + LogInfo("%s BorderAgentId {id:%s}", ActionToString(aAction), buffer); +} +#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE + #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) @@ -156,6 +179,8 @@ const char *SettingsBase::KeyToString(Key aKey) "SrpServerInfo", // (13) kKeySrpServerInfo "", // (14) Removed (previously NAT64 prefix) "BrUlaPrefix", // (15) kKeyBrUlaPrefix + "BrOnLinkPrefixes", // (16) kKeyBrOnLinkPrefixes + "BorderAgentId" // (17) kKeyBorderAgentId }; static_assert(1 == kKeyActiveDataset, "kKeyActiveDataset value is incorrect"); @@ -169,8 +194,10 @@ const char *SettingsBase::KeyToString(Key aKey) static_assert(12 == kKeySrpClientInfo, "kKeySrpClientInfo value is incorrect"); static_assert(13 == kKeySrpServerInfo, "kKeySrpServerInfo value is incorrect"); static_assert(15 == kKeyBrUlaPrefix, "kKeyBrUlaPrefix value is incorrect"); + static_assert(16 == kKeyBrOnLinkPrefixes, "kKeyBrOnLinkPrefixes is incorrect"); + static_assert(17 == kKeyBorderAgentId, "kKeyBorderAgentId is incorrect"); - static_assert(kLastKey == kKeyBrUlaPrefix, "kLastKey is not valid"); + static_assert(kLastKey == kKeyBorderAgentId, "kLastKey is not valid"); OT_ASSERT(aKey <= kLastKey); @@ -190,15 +217,9 @@ const uint16_t Settings::kSensitiveKeys[] = { SettingsBase::kKeySrpEcdsaKey, }; -void Settings::Init(void) -{ - Get().Init(kSensitiveKeys, GetArrayLength(kSensitiveKeys)); -} +void Settings::Init(void) { Get().Init(kSensitiveKeys, GetArrayLength(kSensitiveKeys)); } -void Settings::Deinit(void) -{ - Get().Deinit(); -} +void Settings::Deinit(void) { Get().Deinit(); } void Settings::Wipe(void) { @@ -308,6 +329,80 @@ void Settings::ChildInfoIterator::Read(void) } #endif // OPENTHREAD_FTD +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +Error Settings::AddOrUpdateBrOnLinkPrefix(const BrOnLinkPrefix &aBrOnLinkPrefix) +{ + Error error = kErrorNone; + int index = 0; + BrOnLinkPrefix brPrefix; + bool didUpdate = false; + + while (ReadBrOnLinkPrefix(index, brPrefix) == kErrorNone) + { + if (brPrefix.GetPrefix() == aBrOnLinkPrefix.GetPrefix()) + { + if (brPrefix.GetLifetime() == aBrOnLinkPrefix.GetLifetime()) + { + // Existing entry fully matches `aBrOnLinkPrefix`. + // No need to make any changes. + ExitNow(); + } + + SuccessOrExit(error = Get().Delete(kKeyBrOnLinkPrefixes, index)); + didUpdate = true; + break; + } + + index++; + } + + SuccessOrExit(error = Get().Add(kKeyBrOnLinkPrefixes, &aBrOnLinkPrefix, sizeof(BrOnLinkPrefix))); + brPrefix.Log(didUpdate ? "Updated" : "Added"); + +exit: + return error; +} + +Error Settings::RemoveBrOnLinkPrefix(const Ip6::Prefix &aPrefix) +{ + Error error = kErrorNotFound; + BrOnLinkPrefix brPrefix; + + for (int index = 0; ReadBrOnLinkPrefix(index, brPrefix) == kErrorNone; index++) + { + if (brPrefix.GetPrefix() == aPrefix) + { + SuccessOrExit(error = Get().Delete(kKeyBrOnLinkPrefixes, index)); + brPrefix.Log("Removed"); + break; + } + } + +exit: + return error; +} + +Error Settings::DeleteAllBrOnLinkPrefixes(void) { return Get().Delete(kKeyBrOnLinkPrefixes); } + +Error Settings::ReadBrOnLinkPrefix(int aIndex, BrOnLinkPrefix &aBrOnLinkPrefix) +{ + uint16_t length = sizeof(BrOnLinkPrefix); + + aBrOnLinkPrefix.Init(); + + return Get().Get(kKeyBrOnLinkPrefixes, aIndex, &aBrOnLinkPrefix, &length); +} + +void Settings::BrOnLinkPrefix::Log(const char *aActionText) const +{ + OT_UNUSED_VARIABLE(aActionText); + + LogInfo("%s %s entry {prefix:%s,lifetime:%lu}", aActionText, KeyToString(kKeyBrOnLinkPrefixes), + GetPrefix().ToString().AsCString(), ToUlong(GetLifetime())); +} + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + Error Settings::ReadEntry(Key aKey, void *aValue, uint16_t aMaxLength) const { Error error; @@ -447,6 +542,12 @@ void Settings::Log(Action aAction, Error aError, Key aKey, const void *aValue) break; #endif +#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE + case kKeyBorderAgentId: + reinterpret_cast(aValue)->Log(aAction); + break; +#endif + default: // For any other keys, we do not want to include the value // in the log, so even if it is given we set `aValue` to diff --git a/src/core/common/settings.hpp b/src/core/common/settings.hpp index 6db4a02f2f0..4b307d01c2b 100644 --- a/src/core/common/settings.hpp +++ b/src/core/common/settings.hpp @@ -36,6 +36,7 @@ #include "openthread-core-config.h" +#include #include #include "common/clearable.hpp" @@ -49,6 +50,7 @@ #include "mac/mac_types.hpp" #include "meshcop/dataset.hpp" #include "net/ip6_address.hpp" +#include "thread/version.hpp" #include "utils/flash.hpp" #include "utils/slaac_address.hpp" @@ -118,9 +120,12 @@ class SettingsBase : public InstanceLocator kKeySrpClientInfo = OT_SETTINGS_KEY_SRP_CLIENT_INFO, kKeySrpServerInfo = OT_SETTINGS_KEY_SRP_SERVER_INFO, kKeyBrUlaPrefix = OT_SETTINGS_KEY_BR_ULA_PREFIX, + kKeyBrOnLinkPrefixes = OT_SETTINGS_KEY_BR_ON_LINK_PREFIXES, + kKeyBorderAgentId = OT_SETTINGS_KEY_BORDER_AGENT_ID, }; - static constexpr Key kLastKey = kKeyBrUlaPrefix; ///< The last (numerically) enumerator value in `Key`. + static constexpr Key kLastKey = kKeyBorderAgentId; ///< The last (numerically) enumerator value in `Key`. + static_assert(static_cast(kLastKey) < static_cast(OT_SETTINGS_KEY_VENDOR_RESERVED_MIN), "Core settings keys overlap with vendor reserved keys"); @@ -132,6 +137,7 @@ class SettingsBase : public InstanceLocator class NetworkInfo : private Clearable { friend class Settings; + friend class Clearable; public: static constexpr Key kKey = kKeyNetworkInfo; ///< The associated key. @@ -143,7 +149,7 @@ class SettingsBase : public InstanceLocator void Init(void) { Clear(); - SetVersion(OT_THREAD_VERSION_1_1); + SetVersion(kThreadVersion1p1); } /** @@ -338,6 +344,7 @@ class SettingsBase : public InstanceLocator class ParentInfo : private Clearable { friend class Settings; + friend class Clearable; public: static constexpr Key kKey = kKeyParentInfo; ///< The associated key. @@ -349,7 +356,7 @@ class SettingsBase : public InstanceLocator void Init(void) { Clear(); - SetVersion(OT_THREAD_VERSION_1_1); + SetVersion(kThreadVersion1p1); } /** @@ -411,7 +418,7 @@ class SettingsBase : public InstanceLocator void Init(void) { memset(this, 0, sizeof(*this)); - SetVersion(OT_THREAD_VERSION_1_1); + SetVersion(kThreadVersion1p1); } /** @@ -531,6 +538,7 @@ class SettingsBase : public InstanceLocator class DadInfo : private Clearable { friend class Settings; + friend class Clearable; public: static constexpr Key kKey = kKeyDadInfo; ///< The associated key. @@ -579,7 +587,65 @@ class SettingsBase : public InstanceLocator private: BrUlaPrefix(void) = default; }; -#endif + + /** + * This class represents a BR on-link prefix entry for settings storage. + * + */ + OT_TOOL_PACKED_BEGIN + class BrOnLinkPrefix : public Clearable + { + friend class Settings; + + public: + static constexpr Key kKey = kKeyBrOnLinkPrefixes; ///< The associated key. + + /** + * This method initializes the `BrOnLinkPrefix` object. + * + */ + void Init(void) { Clear(); } + + /** + * This method gets the prefix. + * + * @returns The prefix. + * + */ + const Ip6::Prefix &GetPrefix(void) const { return mPrefix; } + + /** + * This method set the prefix. + * + * @param[in] aPrefix The prefix. + * + */ + void SetPrefix(const Ip6::Prefix &aPrefix) { mPrefix = aPrefix; } + + /** + * This method gets the remaining prefix lifetime in seconds. + * + * @returns The prefix lifetime in seconds. + * + */ + uint32_t GetLifetime(void) const { return mLifetime; } + + /** + * This method sets the the prefix lifetime. + * + * @param[in] aLifetime The prefix lifetime in seconds. + * + */ + void SetLifetime(uint32_t aLifetime) { mLifetime = aLifetime; } + + private: + void Log(const char *aActionText) const; + + Ip6::Prefix mPrefix; + uint32_t mLifetime; + } OT_TOOL_PACKED_END; + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE /** @@ -606,6 +672,7 @@ class SettingsBase : public InstanceLocator class SrpClientInfo : private Clearable { friend class Settings; + friend class Clearable; public: static constexpr Key kKey = kKeySrpClientInfo; ///< The associated key. @@ -666,6 +733,7 @@ class SettingsBase : public InstanceLocator class SrpServerInfo : private Clearable { friend class Settings; + friend class Clearable; public: static constexpr Key kKey = kKeySrpServerInfo; ///< The associated key. @@ -699,6 +767,59 @@ class SettingsBase : public InstanceLocator } OT_TOOL_PACKED_END; #endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE && OPENTHREAD_CONFIG_SRP_SERVER_PORT_SWITCH_ENABLE +#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE + /** + * This structure represents the Border Agent ID. + * + */ + OT_TOOL_PACKED_BEGIN + class BorderAgentId : private Clearable + { + friend class Settings; + friend class Clearable; + + public: + static constexpr Key kKey = kKeyBorderAgentId; ///< The associated key. + static constexpr uint8_t kLength = OT_BORDER_AGENT_ID_LENGTH; + + /** + * This method initializes the `BorderAgentId` object. + * + */ + void Init(void) { Clear(); } + + /** + * This method returns the Border Agent ID. + * + * @returns The Border Agent ID. + * + */ + const uint8_t *GetId(void) const { return mId; } + + /** + * This method returns the Border Agent ID. + * + * @returns The Border Agent ID. + * + */ + uint8_t *GetId(void) { return mId; } + + /** + * This method sets the Border Agent ID. + * + * @retval kErrorInvalidArgs If `aLength` doesn't equal to `OT_BORDER_AGENT_ID_LENGTH`. + * @retval kErrorNone If success. + * + */ + Error SetId(const uint8_t *aId, uint16_t aLength); + + private: + void Log(Action aAction) const; + + uint8_t mId[kLength]; + } OT_TOOL_PACKED_END; +#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE + protected: explicit SettingsBase(Instance &aInstance) : InstanceLocator(aInstance) @@ -1024,7 +1145,7 @@ class Settings : public SettingsBase, private NonCopyable * @returns A reference to the `ChildInfo` entry currently pointed by the iterator. * */ - const ChildInfo &operator*(void)const { return mChildInfo; } + const ChildInfo &operator*(void) const { return mChildInfo; } /** * This method overloads operator `==` to evaluate whether or not two iterator instances are equal. @@ -1062,6 +1183,56 @@ class Settings : public SettingsBase, private NonCopyable }; #endif // OPENTHREAD_FTD +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + /** + * This method adds or updates an on-link prefix. + * + * If there is no matching entry (matching the same `GetPrefix()`) saved in `Settings`, the new entry will be added. + * If there is matching entry, it will be updated to the new @p aPrefix. + * + * @param[in] aBrOnLinkPrefix The on-link prefix to save (add or updated). + * + * @retval kErrorNone Successfully added or updated the entry in settings. + * @retval kErrorNotImplemented The platform does not implement settings functionality. + * + */ + Error AddOrUpdateBrOnLinkPrefix(const BrOnLinkPrefix &aBrOnLinkPrefix); + + /** + * This method removes an on-link prefix entry matching a given prefix. + * + * @param[in] aPrefix The prefix to remove + * + * @retval kErrorNone Successfully removed the matching entry in settings. + * @retval kErrorNotImplemented The platform does not implement settings functionality. + * + */ + Error RemoveBrOnLinkPrefix(const Ip6::Prefix &aPrefix); + + /** + * This method deletes all on-link prefix entries from the settings. + * + * @retval kErrorNone Successfully deleted the entries. + * @retval kErrorNotImplemented The platform does not implement settings functionality. + * + */ + Error DeleteAllBrOnLinkPrefixes(void); + + /** + * This method retrieves an entry from on-link prefixes list at a given index. + * + * @param[in] aIndex The index to read. + * @param[out] aBrOnLinkPrefix A reference to `BrOnLinkPrefix` to output the read value. + * + * @retval kErrorNone Successfully read the value. + * @retval kErrorNotFound No corresponding value in the setting store. + * @retval kErrorNotImplemented The platform does not implement settings functionality. + * + */ + Error ReadBrOnLinkPrefix(int aIndex, BrOnLinkPrefix &aBrOnLinkPrefix); + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + private: #if OPENTHREAD_FTD class ChildInfoIteratorBuilder : public InstanceLocator diff --git a/src/core/common/string.cpp b/src/core/common/string.cpp index 4940076fc53..c7aa7218a1f 100644 --- a/src/core/common/string.cpp +++ b/src/core/common/string.cpp @@ -160,6 +160,32 @@ bool StringMatch(const char *aFirstString, const char *aSecondString, StringMatc return Match(aFirstString, aSecondString, aMode) == kFullMatch; } +Error StringParseUint8(const char *&aString, uint8_t &aUint8) +{ + return StringParseUint8(aString, aUint8, NumericLimits::kMax); +} + +Error StringParseUint8(const char *&aString, uint8_t &aUint8, uint8_t aMaxValue) +{ + Error error = kErrorParse; + const char *cur = aString; + uint16_t value = 0; + + for (; (*cur >= '0') && (*cur <= '9'); cur++) + { + value *= 10; + value += static_cast(*cur - '0'); + VerifyOrExit(value <= aMaxValue, error = kErrorParse); + error = kErrorNone; + } + + aString = cur; + aUint8 = static_cast(value); + +exit: + return error; +} + void StringConvertToLowercase(char *aString) { for (; *aString != kNullChar; aString++) @@ -255,10 +281,7 @@ StringWriter &StringWriter::AppendHexBytes(const uint8_t *aBytes, uint16_t aLeng return *this; } -bool IsValidUtf8String(const char *aString) -{ - return IsValidUtf8String(aString, strlen(aString)); -} +bool IsValidUtf8String(const char *aString) { return IsValidUtf8String(aString, strlen(aString)); } bool IsValidUtf8String(const char *aString, size_t aLength) { diff --git a/src/core/common/string.hpp b/src/core/common/string.hpp index a2ab1aa85d2..fbbaca7ee21 100644 --- a/src/core/common/string.hpp +++ b/src/core/common/string.hpp @@ -43,6 +43,7 @@ #include "common/binary_search.hpp" #include "common/code_utils.hpp" #include "common/error.hpp" +#include "common/num_utils.hpp" namespace ot { @@ -143,7 +144,7 @@ bool StringEndsWith(const char *aString, char aChar); bool StringEndsWith(const char *aString, const char *aSubString, StringMatchMode aMode = kStringExactMatch); /** - * This method checks whether or not two null-terminated strings match. + * This function checks whether or not two null-terminated strings match. * * @param[in] aFirstString A pointer to the first string. * @param[in] aSecondString A pointer to the second string. @@ -155,6 +156,45 @@ bool StringEndsWith(const char *aString, const char *aSubString, StringMatchMode */ bool StringMatch(const char *aFirstString, const char *aSecondString, StringMatchMode aMode = kStringExactMatch); +/** + * This function parses a decimal number from a string as `uint8_t` and skips over the parsed characters. + * + * If the string does not start with a digit, `kErrorParse` is returned. + * + * All the digit characters in the string are parsed until reaching a non-digit character. The pointer `aString` is + * updated to point to the first non-digit character after the parsed digits. + * + * If the parsed number value is larger than @p aMaxValue, `kErrorParse` is returned. + * + * @param[in,out] aString A reference to a pointer to string to parse. + * @param[out] aUint8 A reference to return the parsed value. + * @param[in] aMaxValue Maximum allowed value for the parsed number. + * + * @retval kErrorNone Successfully parsed the number from string. @p aString and @p aUint8 are updated. + * @retval kErrorParse Failed to parse the number from @p aString, or parsed number is larger than @p aMaxValue. + * + */ +Error StringParseUint8(const char *&aString, uint8_t &aUint8, uint8_t aMaxValue); + +/** + * This function parses a decimal number from a string as `uint8_t` and skips over the parsed characters. + * + * If the string does not start with a digit, `kErrorParse` is returned. + * + * All the digit characters in the string are parsed until reaching a non-digit character. The pointer `aString` is + * updated to point to the first non-digit character after the parsed digits. + * + * If the parsed number value is larger than maximum `uint8_t` value, `kErrorParse` is returned. + * + * @param[in,out] aString A reference to a pointer to string to parse. + * @param[out] aUint8 A reference to return the parsed value. + * + * @retval kErrorNone Successfully parsed the number from string. @p aString and @p aUint8 are updated. + * @retval kErrorParse Failed to parse the number from @p aString, or parsed number is out of range. + * + */ +Error StringParseUint8(const char *&aString, uint8_t &aUint8); + /** * This function converts all uppercase letter characters in a given string to lowercase. * @@ -312,7 +352,7 @@ class StringWriter * @returns The string writer. * */ - StringWriter &Append(const char *aFormat, ...); + StringWriter &Append(const char *aFormat, ...) OT_TOOL_PRINTF_STYLE_FORMAT_ARG_CHECK(2, 3); /** * This method appends `printf()` style formatted data to the buffer. @@ -349,7 +389,7 @@ class StringWriter void ConvertToUppercase(void) { StringConvertToUppercase(mBuffer); } private: - char * mBuffer; + char *mBuffer; uint16_t mLength; const uint16_t mSize; }; @@ -405,7 +445,7 @@ class Stringify : public BinarySearch const char *mString; ///< The associated string. private: - int Compare(uint16_t aKey) const { return (aKey == mKey) ? 0 : ((aKey > mKey) ? 1 : -1); } + int Compare(uint16_t aKey) const { return ThreeWayCompare(aKey, mKey); } constexpr static bool AreInOrder(const Entry &aFirst, const Entry &aSecond) { diff --git a/src/core/common/tasklet.hpp b/src/core/common/tasklet.hpp index 6899e57d7be..3694eab8310 100644 --- a/src/core/common/tasklet.hpp +++ b/src/core/common/tasklet.hpp @@ -149,6 +149,33 @@ class Tasklet : public InstanceLocator Tasklet *mNext; }; +/** + * This template class defines a tasklet owned by specific type and using a method on owner type as the callback. + * + * @tparam Owner The type of owner of this tasklet. + * @tparam HandleTaskletPtr A pointer to a non-static member method of `Owner` to use as tasklet handler. + * + * The `Owner` MUST be a type that is accessible using `InstanceLocator::Get()`. + * + */ +template class TaskletIn : public Tasklet +{ +public: + /** + * This constructor initializes the tasklet. + * + * @param[in] aInstance The OpenThread instance. + * + */ + explicit TaskletIn(Instance &aInstance) + : Tasklet(aInstance, HandleTasklet) + { + } + +private: + static void HandleTasklet(Tasklet &aTasklet); // Implemented in `locator_getters.hpp` +}; + /** * This class defines a tasklet that also maintains a user context pointer. * diff --git a/src/core/common/time_ticker.cpp b/src/core/common/time_ticker.cpp index 2bfb130a13c..21845c803be 100644 --- a/src/core/common/time_ticker.cpp +++ b/src/core/common/time_ticker.cpp @@ -45,7 +45,7 @@ namespace ot { TimeTicker::TimeTicker(Instance &aInstance) : InstanceLocator(aInstance) , mReceivers(0) - , mTimer(aInstance, HandleTimer) + , mTimer(aInstance) { } @@ -69,11 +69,6 @@ void TimeTicker::UnregisterReceiver(Receiver aReceiver) } } -void TimeTicker::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void TimeTicker::HandleTimer(void) { mTimer.FireAt(mTimer.GetFireTime() + Random::NonCrypto::AddJitter(kTickInterval, kRestartJitter)); @@ -101,12 +96,10 @@ void TimeTicker::HandleTimer(void) } #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE if (mReceivers & Mask(kChildSupervisor)) { - Get().HandleTimeTick(); + Get().HandleTimeTick(); } -#endif #endif // OPENTHREAD_FTD #if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE @@ -129,6 +122,11 @@ void TimeTicker::HandleTimer(void) Get().HandleTimeTick(); } #endif + + if (mReceivers & Mask(kIp6Mpl)) + { + Get().HandleTimeTick(); + } } } // namespace ot diff --git a/src/core/common/time_ticker.hpp b/src/core/common/time_ticker.hpp index a89f3e36e2d..0db6df2385b 100644 --- a/src/core/common/time_ticker.hpp +++ b/src/core/common/time_ticker.hpp @@ -67,11 +67,12 @@ class TimeTicker : public InstanceLocator, private NonCopyable kMeshForwarder, ///< `MeshForwarder` kMleRouter, ///< `Mle::MleRouter` kAddressResolver, ///< `AddressResolver` - kChildSupervisor, ///< `Utils::ChildSupervisor` + kChildSupervisor, ///< `ChildSupervisor` kIp6FragmentReassembler, ///< `Ip6::Ip6` (handling of fragmented messages) kDuaManager, ///< `DuaManager` kMlrManager, ///< `MlrManager` kNetworkDataNotifier, ///< `NetworkData::Notifier` + kIp6Mpl, ///< `Ip6::Mpl` kNumReceivers, ///< Number of receivers. }; @@ -115,11 +116,12 @@ class TimeTicker : public InstanceLocator, private NonCopyable constexpr static uint32_t Mask(Receiver aReceiver) { return static_cast(1U) << aReceiver; } - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); + void HandleTimer(void); - uint32_t mReceivers; - TimerMilli mTimer; + using TickerTimer = TimerMilliIn; + + uint32_t mReceivers; + TickerTimer mTimer; static_assert(kNumReceivers < sizeof(mReceivers) * CHAR_BIT, "Too many `Receiver`s - does not fit in a bit mask"); }; diff --git a/src/core/common/timer.cpp b/src/core/common/timer.cpp index 66de06d999b..7722cae3f9f 100644 --- a/src/core/common/timer.cpp +++ b/src/core/common/timer.cpp @@ -77,10 +77,7 @@ bool Timer::DoesFireBefore(const Timer &aSecondTimer, Time aNow) const return retval; } -void TimerMilli::Start(uint32_t aDelay) -{ - StartAt(GetNow(), aDelay); -} +void TimerMilli::Start(uint32_t aDelay) { StartAt(GetNow(), aDelay); } void TimerMilli::StartAt(TimeMilli aStartTime, uint32_t aDelay) { @@ -102,15 +99,9 @@ void TimerMilli::FireAtIfEarlier(TimeMilli aFireTime) } } -void TimerMilli::Stop(void) -{ - Get().Remove(*this); -} +void TimerMilli::Stop(void) { Get().Remove(*this); } -void TimerMilli::RemoveAll(Instance &aInstance) -{ - aInstance.Get().RemoveAll(); -} +void TimerMilli::RemoveAll(Instance &aInstance) { aInstance.Get().RemoveAll(); } void Timer::Scheduler::Add(Timer &aTimer, const AlarmApi &aAlarmApi) { @@ -168,7 +159,7 @@ void Timer::Scheduler::SetAlarm(const AlarmApi &aAlarmApi) } else { - Timer * timer = mTimerList.GetHead(); + Timer *timer = mTimerList.GetHead(); Time now(aAlarmApi.AlarmGetNow()); uint32_t remaining; @@ -228,10 +219,7 @@ const Timer::Scheduler::AlarmApi TimerMicro::Scheduler::sAlarmMicroApi = { &otPlatAlarmMicroGetNow, }; -void TimerMicro::Start(uint32_t aDelay) -{ - StartAt(GetNow(), aDelay); -} +void TimerMicro::Start(uint32_t aDelay) { StartAt(GetNow(), aDelay); } void TimerMicro::StartAt(TimeMicro aStartTime, uint32_t aDelay) { @@ -245,15 +233,9 @@ void TimerMicro::FireAt(TimeMicro aFireTime) Get().Add(*this); } -void TimerMicro::Stop(void) -{ - Get().Remove(*this); -} +void TimerMicro::Stop(void) { Get().Remove(*this); } -void TimerMicro::RemoveAll(Instance &aInstance) -{ - aInstance.Get().RemoveAll(); -} +void TimerMicro::RemoveAll(Instance &aInstance) { aInstance.Get().RemoveAll(); } extern "C" void otPlatAlarmMicroFired(otInstance *aInstance) { diff --git a/src/core/common/timer.hpp b/src/core/common/timer.hpp index 3faf09f0019..34e60ec2c42 100644 --- a/src/core/common/timer.hpp +++ b/src/core/common/timer.hpp @@ -140,7 +140,7 @@ class Timer : public InstanceLocator, public LinkedListEntry Handler mHandler; Time mFireTime; - Timer * mNext; + Timer *mNext; }; extern "C" void otPlatAlarmMilliFired(otInstance *aInstance); @@ -246,6 +246,33 @@ class TimerMilli : public Timer static void RemoveAll(Instance &aInstance); }; +/** + * This template class defines a timer owned by a specific type and using a method on owner type as the callback. + * + * @tparam Owner The type of the owner of this timer. + * @tparam HandleTimerPtr A pointer to a non-static member method of `Owner` to use as timer handler. + * + * The `Owner` MUST be a type that is accessible using `InstanceLocator::Get()`. + * + */ +template class TimerMilliIn : public TimerMilli +{ +public: + /** + * This constructor initializes the timer. + * + * @param[in] aInstance The OpenThread instance. + * + */ + explicit TimerMilliIn(Instance &aInstance) + : TimerMilli(aInstance, HandleTimer) + { + } + +private: + static void HandleTimer(Timer &aTimer); // Implemented in `locator_getters.hpp` +}; + /** * This class implements a millisecond timer that also maintains a user context pointer. * @@ -379,6 +406,34 @@ class TimerMicro : public Timer protected: static void RemoveAll(Instance &aInstance); }; + +/** + * This template class defines a timer owned by a specific type and using a method on owner type as the callback. + * + * @tparam Owner The type of the owner of this timer. + * @tparam HandleTimerPtr A pointer to a non-static member method of `Owner` to use as timer handler. + * + * The `Owner` MUST be a type that is accessible using `InstanceLocator::Get()`. + * + */ +template class TimerMicroIn : public TimerMicro +{ +public: + /** + * This constructor initializes the timer. + * + * @param[in] aInstance The OpenThread instance. + * + */ + explicit TimerMicroIn(Instance &aInstance) + : TimerMicro(aInstance, HandleTimer) + { + } + +private: + static void HandleTimer(Timer &aTimer); // Implemented in `locator_getters.hpp` +}; + #endif // OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE /** diff --git a/src/core/common/tlvs.cpp b/src/core/common/tlvs.cpp index c045db6cd1b..2d4f018a0b0 100644 --- a/src/core/common/tlvs.cpp +++ b/src/core/common/tlvs.cpp @@ -54,25 +54,21 @@ const uint8_t *Tlv::GetValue(void) const return reinterpret_cast(this) + (IsExtended() ? sizeof(ExtendedTlv) : sizeof(Tlv)); } -Error Tlv::AppendTo(Message &aMessage) const -{ - return aMessage.AppendBytes(this, static_cast(GetSize())); -} +Error Tlv::AppendTo(Message &aMessage) const { return aMessage.AppendBytes(this, static_cast(GetSize())); } Error Tlv::FindTlv(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tlv &aTlv) { - Error error; - uint16_t offset; - uint16_t size; + Error error; + ParsedInfo info; - SuccessOrExit(error = Find(aMessage, aType, &offset, &size, nullptr)); + SuccessOrExit(error = info.FindIn(aMessage, aType)); - if (aMaxSize > size) + if (aMaxSize > info.mSize) { - aMaxSize = size; + aMaxSize = info.mSize; } - aMessage.ReadBytes(offset, &aTlv, aMaxSize); + aMessage.ReadBytes(info.mOffset, &aTlv, aMaxSize); exit: return error; @@ -80,97 +76,124 @@ Error Tlv::FindTlv(const Message &aMessage, uint8_t aType, uint16_t aMaxSize, Tl Error Tlv::FindTlvOffset(const Message &aMessage, uint8_t aType, uint16_t &aOffset) { - return Find(aMessage, aType, &aOffset, nullptr, nullptr); + Error error; + ParsedInfo info; + + SuccessOrExit(error = info.FindIn(aMessage, aType)); + aOffset = info.mOffset; + +exit: + return error; } Error Tlv::FindTlvValueOffset(const Message &aMessage, uint8_t aType, uint16_t &aValueOffset, uint16_t &aLength) { - Error error; - uint16_t offset; - uint16_t size; - bool isExtendedTlv; + Error error; + ParsedInfo info; + + SuccessOrExit(error = info.FindIn(aMessage, aType)); + + aValueOffset = info.mValueOffset; + aLength = info.mLength; + +exit: + return error; +} + +Error Tlv::ParsedInfo::ParseFrom(const Message &aMessage, uint16_t aOffset) +{ + // This method reads and parses the TLV info from `aMessage` at + // `aOffset`. This can be used independent of whether the TLV is + // extended or not. It validates that the entire TLV can be read + // from `aMessage`. Returns `kErrorNone` when successfully parsed, + // otherwise `kErrorParse`. + + Error error; + Tlv tlv; + ExtendedTlv extTlv; + uint16_t headerSize; - SuccessOrExit(error = Find(aMessage, aType, &offset, &size, &isExtendedTlv)); + SuccessOrExit(error = aMessage.Read(aOffset, tlv)); - if (!isExtendedTlv) + if (!tlv.IsExtended()) { - aValueOffset = offset + sizeof(Tlv); - aLength = size - sizeof(Tlv); + mType = tlv.GetType(); + mLength = tlv.GetLength(); + headerSize = sizeof(Tlv); } else { - aValueOffset = offset + sizeof(ExtendedTlv); - aLength = size - sizeof(ExtendedTlv); + SuccessOrExit(error = aMessage.Read(aOffset, extTlv)); + + mType = extTlv.GetType(); + mLength = extTlv.GetLength(); + headerSize = sizeof(ExtendedTlv); } + // We know that we could successfully read `tlv` or `extTlv` + // (`headerSize` bytes) from the message, so the calculation of the + // remaining length as `aMessage.GetLength() - aOffset - headerSize` + // cannot underflow. + + VerifyOrExit(mLength <= aMessage.GetLength() - aOffset - headerSize, error = kErrorParse); + + // Now that we know the entire TLV is contained within the + // `aMessage`, we can safely calculate `mValueOffset` and `mSize` + // as `uint16_t` and know that there will be no overflow. + + mType = tlv.GetType(); + mOffset = aOffset; + mValueOffset = aOffset + headerSize; + mSize = mLength + headerSize; + exit: return error; } -Error Tlv::Find(const Message &aMessage, uint8_t aType, uint16_t *aOffset, uint16_t *aSize, bool *aIsExtendedTlv) +Error Tlv::ParsedInfo::FindIn(const Message &aMessage, uint8_t aType) { - // This static method searches within a `aMessage` for a TLV type - // `aType` and outputs the TLV offset, size, and whether or not it - // is an Extended TLV. - // - // A `nullptr` pointer can be used for output parameters `aOffset`, - // `aSize`, or `aIsExtendedTlv` if the parameter is not required. - // - // Returns `kErrorNone` when found, otherwise `kErrorNotFound`. - - Error error = kErrorNotFound; - uint16_t offset = aMessage.GetOffset(); - uint16_t remainingLen = aMessage.GetLength(); - Tlv tlv; - uint32_t size; - - VerifyOrExit(offset <= remainingLen); - remainingLen -= offset; + // This method searches within `aMessage` starting from + // `aMessage.GetOffset()` for a TLV type `aType` and parsed its + // info and validates that the entire TLV can be read from + // `aMessage`. Returns `kErrorNone` when found, otherwise + // `kErrorNotFound`. + + Error error = kErrorNotFound; + uint16_t offset = aMessage.GetOffset(); while (true) { - SuccessOrExit(aMessage.Read(offset, tlv)); + SuccessOrExit(ParseFrom(aMessage, offset)); - if (tlv.mLength != kExtendedLength) + if (mType == aType) { - size = tlv.GetSize(); + error = kErrorNone; + ExitNow(); } - else - { - ExtendedTlv extTlv; - SuccessOrExit(aMessage.Read(offset, extTlv)); + // `ParseFrom()` already validated that `offset + mSize` is + // less than `aMessage.GetLength()` and therefore we can not + // have an overflow here. - VerifyOrExit(extTlv.GetLength() <= (remainingLen - sizeof(ExtendedTlv))); - size = extTlv.GetSize(); - } + offset += mSize; + } - VerifyOrExit(size <= remainingLen); +exit: + return error; +} - if (tlv.GetType() == aType) - { - if (aOffset != nullptr) - { - *aOffset = offset; - } +Error Tlv::ReadStringTlv(const Message &aMessage, uint16_t aOffset, uint8_t aMaxStringLength, char *aValue) +{ + Error error = kErrorNone; + ParsedInfo info; + uint16_t length; - if (aSize != nullptr) - { - *aSize = static_cast(size); - } + SuccessOrExit(error = info.ParseFrom(aMessage, aOffset)); - if (aIsExtendedTlv != nullptr) - { - *aIsExtendedTlv = (tlv.mLength == kExtendedLength); - } + length = Min(info.mLength, static_cast(aMaxStringLength)); - error = kErrorNone; - ExitNow(); - } - - offset += size; - remainingLen -= size; - } + aMessage.ReadBytes(info.mValueOffset, aValue, length); + aValue[length] = '\0'; exit: return error; @@ -180,7 +203,7 @@ template Error Tlv::ReadUintTlv(const Message &aMessage, uin { Error error; - SuccessOrExit(error = ReadTlv(aMessage, aOffset, &aValue, sizeof(aValue))); + SuccessOrExit(error = ReadTlvValue(aMessage, aOffset, &aValue, sizeof(aValue))); aValue = Encoding::BigEndian::HostSwap(aValue); exit: @@ -192,16 +215,28 @@ template Error Tlv::ReadUintTlv(const Message &aMessage, uint16_t aOffs template Error Tlv::ReadUintTlv(const Message &aMessage, uint16_t aOffset, uint16_t &aValue); template Error Tlv::ReadUintTlv(const Message &aMessage, uint16_t aOffset, uint32_t &aValue); -Error Tlv::ReadTlv(const Message &aMessage, uint16_t aOffset, void *aValue, uint8_t aMinLength) +Error Tlv::ReadTlvValue(const Message &aMessage, uint16_t aOffset, void *aValue, uint8_t aMinLength) { - Error error = kErrorNone; - Tlv tlv; + Error error; + ParsedInfo info; - SuccessOrExit(error = aMessage.Read(aOffset, tlv)); - VerifyOrExit(!tlv.IsExtended() && (tlv.GetLength() >= aMinLength), error = kErrorParse); - VerifyOrExit(tlv.GetSize() + aOffset <= aMessage.GetLength(), error = kErrorParse); + SuccessOrExit(error = info.ParseFrom(aMessage, aOffset)); + + VerifyOrExit(info.mLength >= aMinLength, error = kErrorParse); - aMessage.ReadBytes(aOffset + sizeof(Tlv), aValue, aMinLength); + aMessage.ReadBytes(info.mValueOffset, aValue, aMinLength); + +exit: + return error; +} + +Error Tlv::FindStringTlv(const Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, char *aValue) +{ + Error error = kErrorNone; + uint16_t offset; + + SuccessOrExit(error = FindTlvOffset(aMessage, aType, offset)); + error = ReadStringTlv(aMessage, offset, aMaxStringLength, aValue); exit: return error; @@ -238,6 +273,13 @@ Error Tlv::FindTlv(const Message &aMessage, uint8_t aType, void *aValue, uint8_t return error; } +Error Tlv::AppendStringTlv(Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, const char *aValue) +{ + uint16_t length = (aValue == nullptr) ? 0 : StringLength(aValue, aMaxStringLength); + + return AppendTlv(aMessage, aType, aValue, static_cast(length)); +} + template Error Tlv::AppendUintTlv(Message &aMessage, uint8_t aType, UintType aValue) { UintType value = Encoding::BigEndian::HostSwap(aValue); diff --git a/src/core/common/tlvs.hpp b/src/core/common/tlvs.hpp index 4dd55f4d61c..577ce4af86a 100644 --- a/src/core/common/tlvs.hpp +++ b/src/core/common/tlvs.hpp @@ -175,7 +175,9 @@ class Tlv Error AppendTo(Message &aMessage) const; /** - * This static method reads a TLV in a message at a given offset expecting a minimum length for the value. + * This static method reads a TLV's value in a message at a given offset expecting a minimum length for the value. + * + * This method can be used independent of whether the read TLV (from the message) is an Extended TLV or not. * * @param[in] aMessage The message to read from. * @param[in] aOffset The offset into the message pointing to the start of the TLV. @@ -186,7 +188,7 @@ class Tlv * @retval kErrorParse The TLV was not well-formed and could not be parsed. * */ - static Error ReadTlv(const Message &aMessage, uint16_t aOffset, void *aValue, uint8_t aMinLength); + static Error ReadTlvValue(const Message &aMessage, uint16_t aOffset, void *aValue, uint8_t aMinLength); /** * This static method reads a simple TLV with a single non-integral value in a message at a given offset. @@ -204,7 +206,7 @@ class Tlv template static Error Read(const Message &aMessage, uint16_t aOffset, typename SimpleTlvType::ValueType &aValue) { - return ReadTlv(aMessage, aOffset, &aValue, sizeof(aValue)); + return ReadTlvValue(aMessage, aOffset, &aValue, sizeof(aValue)); } /** @@ -226,6 +228,25 @@ class Tlv return ReadUintTlv(aMessage, aOffset, aValue); } + /** + * This static method reads a simple TLV with a UTF-8 string value in a message at a given offset. + * + * @tparam StringTlvType The simple TLV type to read (must be a sub-class of `StringTlvInfo`). + * + * @param[in] aMessage The message to read from. + * @param[in] aOffset The offset into the message pointing to the start of the TLV. + * @param[out] aValue A reference to the string buffer to output the read value. + * + * @retval kErrorNone Successfully read the TLV and updated the @p aValue. + * @retval kErrorParse The TLV was not well-formed and could not be parsed. + * + */ + template + static Error Read(const Message &aMessage, uint16_t aOffset, typename StringTlvType::StringType &aValue) + { + return ReadStringTlv(aMessage, aOffset, StringTlvType::kMaxStringLength, aValue); + } + /** * This static method searches for and reads a requested TLV out of a given message. * @@ -367,6 +388,32 @@ class Tlv return FindUintTlv(aMessage, UintTlvType::kType, aValue); } + /** + * This static method searches for a simple TLV with a UTF-8 string value in a message, and then reads its value + * into a given string buffer. + * + * If the TLV length is longer than maximum string length specified by `StringTlvType::kMaxStringLength` then + * only up to maximum length is read and returned. In this case `kErrorNone` is returned. + * + * The returned string in @p aValue is always null terminated.`StringTlvType::StringType` MUST have at least + * `kMaxStringLength + 1` chars. + * + * @tparam StringTlvType The simple TLV type to find (must be a sub-class of `StringTlvInfo`) + * + * @param[in] aMessage A reference to the message. + * @param[out] aValue A reference to a string buffer to output the TLV's value. + * + * @retval kErrorNone The TLV was found and read successfully. @p aValue is updated. + * @retval kErrorNotFound Could not find the TLV with Type @p aType. + * @retval kErrorParse TLV was found but it was not well-formed and could not be parsed. + * + */ + template + static Error Find(const Message &aMessage, typename StringTlvType::StringType &aValue) + { + return FindStringTlv(aMessage, StringTlvType::kType, StringTlvType::kMaxStringLength, aValue); + } + /** * This static method appends a TLV with a given type and value to a message. * @@ -426,13 +473,51 @@ class Tlv return AppendUintTlv(aMessage, UintTlvType::kType, aValue); } + /** + * This static method appends a simple TLV with a single UTF-8 string value to a message. + * + * On success this method grows the message by the size of the TLV. + * + * If the passed in @p aValue string length is longer than the maximum allowed length for the TLV as specified by + * `StringTlvType::kMaxStringLength`, the first maximum length chars are appended. + * + * The @p aValue can be `nullptr` in which case it is treated as an empty string. + * + * @tparam StringTlvType The simple TLV type to append (must be a sub-class of `StringTlvInfo`) + * + * @param[in] aMessage A reference to the message to append to. + * @param[in] aValue A pointer to a C string to append as TLV's value. + * + * @retval kErrorNone Successfully appended the TLV to the message. + * @retval kErrorNoBufs Insufficient available buffers to grow the message. + * + */ + template static Error Append(Message &aMessage, const char *aValue) + { + return AppendStringTlv(aMessage, StringTlvType::kType, StringTlvType::kMaxStringLength, aValue); + } + protected: static const uint8_t kExtendedLength = 255; // Extended Length value. private: - static Error Find(const Message &aMessage, uint8_t aType, uint16_t *aOffset, uint16_t *aSize, bool *aIsExtendedTlv); + struct ParsedInfo + { + Error ParseFrom(const Message &aMessage, uint16_t aOffset); + Error FindIn(const Message &aMessage, uint8_t aType); + + uint8_t mType; + uint16_t mLength; + uint16_t mOffset; + uint16_t mValueOffset; + uint16_t mSize; + }; + static Error FindTlv(const Message &aMessage, uint8_t aType, void *aValue, uint8_t aLength); static Error AppendTlv(Message &aMessage, uint8_t aType, const void *aValue, uint8_t aLength); + static Error ReadStringTlv(const Message &aMessage, uint16_t aOffset, uint8_t aMaxStringLength, char *aValue); + static Error FindStringTlv(const Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, char *aValue); + static Error AppendStringTlv(Message &aMessage, uint8_t aType, uint8_t aMaxStringLength, const char *aValue); template static Error ReadUintTlv(const Message &aMessage, uint16_t aOffset, UintType &aValue); template static Error FindUintTlv(const Message &aMessage, uint8_t aType, UintType &aValue); template static Error AppendUintTlv(Message &aMessage, uint8_t aType, UintType aValue); @@ -477,10 +562,7 @@ class ExtendedTlv : public Tlv * @returns A `TlvType` pointer to `aTlv`. * */ -template TlvType *As(Tlv *aTlv) -{ - return static_cast(aTlv); -} +template TlvType *As(Tlv *aTlv) { return static_cast(aTlv); } /** * This template method casts a `Tlv` pointer to a given subclass `TlvType` pointer. @@ -492,10 +574,7 @@ template TlvType *As(Tlv *aTlv) * @returns A `TlvType` pointer to `aTlv`. * */ -template const TlvType *As(const Tlv *aTlv) -{ - return static_cast(aTlv); -} +template const TlvType *As(const Tlv *aTlv) { return static_cast(aTlv); } /** * This template method casts a `Tlv` reference to a given subclass `TlvType` reference. @@ -507,10 +586,7 @@ template const TlvType *As(const Tlv *aTlv) * @returns A `TlvType` reference to `aTlv`. * */ -template TlvType &As(Tlv &aTlv) -{ - return static_cast(aTlv); -} +template TlvType &As(Tlv &aTlv) { return static_cast(aTlv); } /** * This template method casts a `Tlv` reference to a given subclass `TlvType` reference. @@ -522,10 +598,7 @@ template TlvType &As(Tlv &aTlv) * @returns A `TlvType` reference to `aTlv`. * */ -template const TlvType &As(const Tlv &aTlv) -{ - return static_cast(aTlv); -} +template const TlvType &As(const Tlv &aTlv) { return static_cast(aTlv); } /** * This class defines constants for a TLV. @@ -583,6 +656,24 @@ template class SimpleTlvInfo : pu typedef TlvValueType ValueType; ///< The TLV Value type. }; +/** + * This class defines constants and types for a simple TLV with a UTF-8 string value. + * + * This class and its sub-classes are intended to be used as the template type in `Tlv::Append()`, + * and the related `Tlv::Find()` and `Tlv::Read()`. + * + * @tparam kTlvTypeValue The TLV Type value. + * @tparam kTlvMaxValueLength The maximum allowed string length (as TLV value). + * + */ +template class StringTlvInfo : public TlvInfo +{ +public: + static constexpr uint8_t kMaxStringLength = kTlvMaxValueLength; ///< Maximum string length. + + typedef char StringType[kMaxStringLength + 1]; ///< String buffer for TLV value. +}; + } // namespace ot #endif // TLVS_HPP_ diff --git a/src/core/common/trickle_timer.cpp b/src/core/common/trickle_timer.cpp index bfa3aecce78..7258cd27e7c 100644 --- a/src/core/common/trickle_timer.cpp +++ b/src/core/common/trickle_timer.cpp @@ -115,10 +115,7 @@ void TrickleTimer::StartNewInterval(void) TimerMilli::Start(mTimeInInterval); } -void TrickleTimer::HandleTimer(Timer &aTimer) -{ - static_cast(&aTimer)->HandleTimer(); -} +void TrickleTimer::HandleTimer(Timer &aTimer) { static_cast(&aTimer)->HandleTimer(); } void TrickleTimer::HandleTimer(void) { @@ -161,7 +158,7 @@ void TrickleTimer::HandleTimer(void) } StartNewInterval(); - ExitNow(); // Exit so to not call `mHanlder` + ExitNow(); // Exit so to not call `mHandler` } break; diff --git a/src/core/common/type_traits.hpp b/src/core/common/type_traits.hpp index 4feb52a2a93..a6081464bd9 100644 --- a/src/core/common/type_traits.hpp +++ b/src/core/common/type_traits.hpp @@ -125,6 +125,41 @@ template struct Conditional::Type` would be `Error`. + * + * @tparam HandlerType The function pointer type. + * + */ +template struct ReturnTypeOf; + +template struct ReturnTypeOf +{ + typedef RetType Type; ///< The return type. +}; + +/** + * This type determines the type of the first argument of a given function pointer type. + * + * It provides member type named `Type` which gives the first argument type of `HandlerType` function pointer. + * + * For example, `ReturnTypeOf::Type` would be `void *`. + * + * @tparam HandlerType The function pointer type. + * + */ +template struct FirstArgTypeOf; + +template +struct FirstArgTypeOf +{ + typedef FirstArgType Type; ///< The first argument type. +}; + } // namespace TypeTraits } // namespace ot diff --git a/src/core/common/uptime.cpp b/src/core/common/uptime.cpp index 1c3c74ca28c..4cf0a34c78d 100644 --- a/src/core/common/uptime.cpp +++ b/src/core/common/uptime.cpp @@ -46,7 +46,7 @@ Uptime::Uptime(Instance &aInstance) : InstanceLocator(aInstance) , mStartTime(TimerMilli::GetNow()) , mOverflowCount(0) - , mTimer(aInstance, HandleTimer) + , mTimer(aInstance) { mTimer.FireAt(mStartTime + kTimerInterval); } @@ -85,12 +85,7 @@ void Uptime::GetUptime(char *aBuffer, uint16_t aSize) const { StringWriter writer(aBuffer, aSize); - UptimeToString(GetUptime(), writer); -} - -void Uptime::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); + UptimeToString(GetUptime(), writer, /* aIncludeMsec */ true); } void Uptime::HandleTimer(void) @@ -103,7 +98,7 @@ void Uptime::HandleTimer(void) mTimer.FireAt(mTimer.GetFireTime() + kTimerInterval); } -static uint32_t DivideAndGetRemainder(uint32_t &aDividend, uint32_t aDivisor) +static uint16_t DivideAndGetRemainder(uint32_t &aDividend, uint32_t aDivisor) { // Returns the quotient of division `aDividend / aDivisor` and updates // `aDividend` to returns the remainder @@ -112,20 +107,20 @@ static uint32_t DivideAndGetRemainder(uint32_t &aDividend, uint32_t aDivisor) aDividend -= quotient * aDivisor; - return quotient; + return static_cast(quotient); } -void Uptime::UptimeToString(uint64_t aUptime, StringWriter &aWriter) +void Uptime::UptimeToString(uint64_t aUptime, StringWriter &aWriter, bool aIncludeMsec) { uint64_t days = aUptime / Time::kOneDayInMsec; uint32_t remainder; - uint32_t hours; - uint32_t minutes; - uint32_t seconds; + uint16_t hours; + uint16_t minutes; + uint16_t seconds; if (days > 0) { - aWriter.Append("%lud.", days); + aWriter.Append("%lud.", static_cast(days)); aUptime -= days * Time::kOneDayInMsec; } @@ -134,7 +129,12 @@ void Uptime::UptimeToString(uint64_t aUptime, StringWriter &aWriter) minutes = DivideAndGetRemainder(remainder, Time::kOneMinuteInMsec); seconds = DivideAndGetRemainder(remainder, Time::kOneSecondInMsec); - aWriter.Append("%02u:%02u:%02u.%03u", hours, minutes, seconds, remainder); + aWriter.Append("%02u:%02u:%02u", hours, minutes, seconds); + + if (aIncludeMsec) + { + aWriter.Append(".%03u", static_cast(remainder)); + } } } // namespace ot diff --git a/src/core/common/uptime.hpp b/src/core/common/uptime.hpp index d6cab3ce22c..670b02343d5 100644 --- a/src/core/common/uptime.hpp +++ b/src/core/common/uptime.hpp @@ -90,25 +90,51 @@ class Uptime : public InstanceLocator, private NonCopyable * This method converts an uptime value (number of milliseconds) to a human-readable string. * * The string follows the format "::." for hours, minutes, seconds and millisecond (if uptime is - * shorter than one day) or "
d.::." (if longer than a day). + * shorter than one day) or "
d.::." (if longer than a day). @p aIncludeMsec can be used + * to determine whether `.` milliseconds is included or omitted in the resulting string. + * + * @param[in] aUptime The uptime to convert. + * @param[in,out] aWriter A `StringWriter` to append the converted string to. + * @param[in] aIncludeMsec Whether to include `.` milliseconds in the string. + * + */ + static void UptimeToString(uint64_t aUptime, StringWriter &aWriter, bool aIncludeMsec); + + /** + * This static method converts a given uptime as number of milliseconds to number of seconds. + * + * @param[in] aUptimeInMilliseconds Uptime in milliseconds (as `uint64_t`). * - * @param[in] aUptime The uptime to convert. - * @param[in,out] aWriter A `StringWriter` to append the converted string to. + * @returns The converted @p aUptimeInMilliseconds to seconds (as `uint32_t`). * */ - static void UptimeToString(uint64_t aUptime, StringWriter &aWriter); + static uint32_t MsecToSec(uint64_t aUptimeInMilliseconds) + { + return static_cast(aUptimeInMilliseconds / 1000u); + } + + /** + * This static method converts a given uptime as number of seconds to number of milliseconds. + * + * @param[in] aUptimeInSeconds Uptime in seconds (as `uint32_t`). + * + * @returns The converted @p aUptimeInSeconds to milliseconds (as `uint64_t`). + * + */ + static uint64_t SecToMsec(uint32_t aUptimeInSeconds) { return static_cast(aUptimeInSeconds) * 1000u; } private: static constexpr uint32_t kTimerInterval = (1 << 30); static_assert(static_cast(4 * kTimerInterval) == 0, "kTimerInterval is not correct"); - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); + void HandleTimer(void); + + using UptimeTimer = TimerMilliIn; - TimeMilli mStartTime; - uint32_t mOverflowCount; - TimerMilli mTimer; + TimeMilli mStartTime; + uint32_t mOverflowCount; + UptimeTimer mTimer; }; } // namespace ot diff --git a/src/core/config/border_agent.h b/src/core/config/border_agent.h new file mode 100644 index 00000000000..7e0abc19b8d --- /dev/null +++ b/src/core/config/border_agent.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2019, 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 compile-time configurations for Border Agent. + * + */ + +#ifndef CONFIG_BORDER_AGENT_H_ +#define CONFIG_BORDER_AGENT_H_ + +/** + * @def OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE + * + * Define to 1 to enable Border Agent support. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE +#define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_BORDER_AGENT_UDP_PORT + * + * Specifies the Border Agent UDP port, and use 0 for ephemeral port. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_AGENT_UDP_PORT +#define OPENTHREAD_CONFIG_BORDER_AGENT_UDP_PORT 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE + * + * Define ro 1 to enable Border Agent ID support. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE +#define OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE 0 +#endif + +#endif // CONFIG_BORDER_AGENT_H_ diff --git a/src/core/config/border_router.h b/src/core/config/border_router.h index ec1792a1b07..01ddfe9aa2e 100644 --- a/src/core/config/border_router.h +++ b/src/core/config/border_router.h @@ -35,16 +35,6 @@ #ifndef CONFIG_BORDER_ROUTER_H_ #define CONFIG_BORDER_ROUTER_H_ -/** - * @def OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE - * - * Define to 1 to enable Border Agent support. - * - */ -#ifndef OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE -#define OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE 0 -#endif - /** * @def OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE * @@ -81,65 +71,4 @@ #define OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE 1 #endif -/** - * @def OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE - * - * Define to 1 to enable Border Routing support. - * - */ -#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE -#define OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE 0 -#endif - -/** - * @def OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_ROUTERS - * - * Specifies maximum number of routers (on infra link) to track by routing manager. - * - */ -#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_ROUTERS -#define OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_ROUTERS 16 -#endif - -/** - * @def OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES - * - * Specifies maximum number of discovered prefixes (on-link prefixes on the infra link) maintained by routing manager. - * - */ -#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES -#define OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES 64 -#endif - -/** - * @def OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_ON_MESH_PREFIXES - * - * Specified maximum number of on-mesh prefixes (discovered from Thread Network Data) that are included as Route Info - * Option in emitted Router Advertisement messages. - * - */ -#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_ON_MESH_PREFIXES -#define OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_ON_MESH_PREFIXES 16 -#endif - -/** - * @def OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE - * - * Define to 1 to enable Border Routing NAT64 support. - * - */ -#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE -#define OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE 0 -#endif - -/** - * @def OPENTHREAD_CONFIG_BORDER_AGENT_UDP_PORT - * - * Specifies the Border Agent UDP port, and use 0 for ephemeral port. - * - */ -#ifndef OPENTHREAD_CONFIG_BORDER_AGENT_UDP_PORT -#define OPENTHREAD_CONFIG_BORDER_AGENT_UDP_PORT 0 -#endif - #endif // CONFIG_BORDER_ROUTER_H_ diff --git a/src/core/config/border_routing.h b/src/core/config/border_routing.h new file mode 100644 index 00000000000..1327901ea06 --- /dev/null +++ b/src/core/config/border_routing.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2021-22, 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 compile-time configurations for Border Routing Manager. + * + */ + +#ifndef CONFIG_BORDER_ROUTING_H_ +#define CONFIG_BORDER_ROUTING_H_ + +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + * + * Define to 1 to enable Border Routing Manager feature. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#define OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_ROUTERS + * + * Specifies maximum number of routers (on infra link) to track by routing manager. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_ROUTERS +#define OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_ROUTERS 16 +#endif + +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES + * + * Specifies maximum number of discovered prefixes (on-link prefixes on the infra link) maintained by routing manager. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES +#define OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES 64 +#endif + +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_ON_MESH_PREFIXES + * + * Specifies maximum number of on-mesh prefixes (discovered from Thread Network Data) that are included as Route Info + * Option in emitted Router Advertisement messages. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_ON_MESH_PREFIXES +#define OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_ON_MESH_PREFIXES 16 +#endif + +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_OLD_ON_LINK_PREFIXES + * + * Specifies maximum number of old local on-link prefixes (being deprecated) maintained by routing manager. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_OLD_ON_LINK_PREFIXES +#define OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_OLD_ON_LINK_PREFIXES 3 +#endif + +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTING_ROUTER_ACTIVE_CHECK_TIMEOUT + * + * Specifies the timeout in msec for a discovered router on infra link side. + * + * This parameter is related to mechanism to check that a discovered router is still active. + * + * After this timeout elapses since the last received message (a Router or Neighbor Advertisement) from the router, + * routing manager will start sending Neighbor Solidification (NS) probes to the router to check that it is still + * active. + * + * This parameter can be considered to large value to practically disable this behavior. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTING_ROUTER_ACTIVE_CHECK_TIMEOUT +#define OPENTHREAD_CONFIG_BORDER_ROUTING_ROUTER_ACTIVE_CHECK_TIMEOUT (60 * 1000) // (in msec). +#endif + +#endif // CONFIG_BORDER_ROUTING_H_ diff --git a/src/core/config/child_supervision.h b/src/core/config/child_supervision.h index 282e5a0f36e..94e8ec63a83 100644 --- a/src/core/config/child_supervision.h +++ b/src/core/config/child_supervision.h @@ -35,24 +35,12 @@ #ifndef CONFIG_CHILD_SUPERVISION_H_ #define CONFIG_CHILD_SUPERVISION_H_ -/** - * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - * - * Define to 1 to enable Child Supervision support. - * - */ -#ifndef OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE -#define OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE 0 -#endif - /** * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL * - * The default supervision interval in seconds used by parent. Set to zero to disable the supervision process on the - * parent. + * The default supervision interval in seconds to use when in child state. Zero indicates no supervision needed. * - * Applicable only if child supervision feature is enabled (i.e., - * `OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE ` is set). + * The current supervision interval can be changed using `otChildSupervisionSetInterval()`. * * Child supervision feature provides a mechanism for parent to ensure that a message is sent to each sleepy child * within the supervision interval. If there is no transmission to the child within the supervision interval, child @@ -69,7 +57,7 @@ * The default supervision check timeout interval (in seconds) used by a device in child state. Set to zero to disable * the supervision check process on the child. * - * Applicable only if child supervision feature is enabled (i.e., `OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE` is set). + * The check timeout interval can be changed using `otChildSupervisionSetCheckTimeout()`. * * If the sleepy child does not hear from its parent within the specified timeout interval, it initiates the re-attach * process (MLE Child Update Request/Response exchange with its parent). @@ -80,15 +68,22 @@ #endif /** - * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_MSG_NO_ACK_REQUEST + * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_OLDER_VERSION_CHILD_DEFAULT_INTERVAL + * + * Specifies the default supervision interval to use on parent for children that do not explicitly indicate their + * desired supervision internal (do not include a "Supervision Interval TLV") and are running older Thread versions + * (version <= 1.3.0). * - * Define as 1 to clear/disable 15.4 ack request in the MAC header of a supervision message. + * This config is added to allow backward compatibility on parent with SED children that used Child Supervision + * feature in OT stack before adoption of it by Thread specification and addition of the "Supervision Interval TLV" as + * the mechanism for child to inform the parent of its desired supervision interval. * - * Applicable only if child supervision feature is enabled (i.e., `OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE` is set). + * The config can be set to zero to effectively disable it, i.e., if a child does not provide "Supervision Interval TLV" + * it indicates that it does not want to be supervised and then parent will use zero interval for the child. * */ -#ifndef OPENTHREAD_CONFIG_CHILD_SUPERVISION_MSG_NO_ACK_REQUEST -#define OPENTHREAD_CONFIG_CHILD_SUPERVISION_MSG_NO_ACK_REQUEST 0 +#ifndef OPENTHREAD_CONFIG_CHILD_SUPERVISION_OLDER_VERSION_CHILD_DEFAULT_INTERVAL +#define OPENTHREAD_CONFIG_CHILD_SUPERVISION_OLDER_VERSION_CHILD_DEFAULT_INTERVAL 129 #endif #endif // CONFIG_CHILD_SUPERVISION_H_ diff --git a/src/core/config/commissioner.h b/src/core/config/commissioner.h index a875d8d2352..ff43adb363c 100644 --- a/src/core/config/commissioner.h +++ b/src/core/config/commissioner.h @@ -55,4 +55,15 @@ #define OPENTHREAD_CONFIG_COMMISSIONER_MAX_JOINER_ENTRIES 2 #endif +/** + * @def OPENTHREAD_CONFIG_COMMISSIONER_JOINER_SESSION_TIMEOUT + * + * The timeout for the Joiner's session, in seconds. After this timeout, + * the Commissioner tears down the session. + * + */ +#ifndef OPENTHREAD_CONFIG_COMMISSIONER_JOINER_SESSION_TIMEOUT +#define OPENTHREAD_CONFIG_COMMISSIONER_JOINER_SESSION_TIMEOUT 30 +#endif + #endif // CONFIG_COMMISSIONER_H_ diff --git a/src/core/config/dns_client.h b/src/core/config/dns_client.h index e0a9ec89415..d727261d8fe 100644 --- a/src/core/config/dns_client.h +++ b/src/core/config/dns_client.h @@ -35,6 +35,7 @@ #ifndef CONFIG_DNS_CLIENT_H_ #define CONFIG_DNS_CLIENT_H_ +#include "config/ip6.h" #include "config/srp_client.h" /** @@ -150,4 +151,34 @@ #define OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_RECURSION_DESIRED_FLAG 1 #endif +/** + * @def OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVICE_MODE + * + * Specifies the default `otDnsServiceMode` to use. The value MUST be from `otDnsServiceMode` enumeration. + * + */ +#ifndef OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVICE_MODE +#define OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVICE_MODE OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE +#endif + +/** + * @def OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE + * + * Enables support for sending DNS Queries over TCP. + * + */ +#ifndef OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE +#define OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE + * + * Specifies size of receive and transmit buffers of TCP sockets for DNS query over TCP. + * + */ +#ifndef OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE +#define OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE 1024 +#endif + #endif // CONFIG_DNS_CLIENT_H_ diff --git a/src/core/config/dnssd_server.h b/src/core/config/dnssd_server.h index 70834937c22..3edc4097f25 100644 --- a/src/core/config/dnssd_server.h +++ b/src/core/config/dnssd_server.h @@ -75,4 +75,15 @@ #define OPENTHREAD_CONFIG_DNSSD_QUERY_TIMEOUT 6000 #endif +/** + * @def OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + * + * Define to 1 to enable upstream forwarding support. The platform MUST implement `otPlatDnsStartUpstreamQuery` and + * `otPlatDnsCancelUpstreamQuery`. + * + */ +#ifndef OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE +#define OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE 0 +#endif + #endif // CONFIG_DNSSD_SERVER_H_ diff --git a/src/core/config/dtls.h b/src/core/config/dtls.h index be6077d9913..09e833788bc 100644 --- a/src/core/config/dtls.h +++ b/src/core/config/dtls.h @@ -35,7 +35,7 @@ #ifndef CONFIG_DTLS_H_ #define CONFIG_DTLS_H_ -#include "config/border_router.h" +#include "config/border_agent.h" #include "config/coap.h" #include "config/commissioner.h" #include "config/joiner.h" @@ -50,11 +50,16 @@ #define OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN MBEDTLS_SSL_IN_CONTENT_LEN #endif -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE || OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE || \ - OPENTHREAD_CONFIG_COMMISSIONER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE -#define OPENTHREAD_CONFIG_DTLS_ENABLE 1 -#else -#define OPENTHREAD_CONFIG_DTLS_ENABLE 0 +/** + * @def OPENTHREAD_CONFIG_DTLS_ENABLE + * + * Define to 1 to enable DTLS. + * + */ +#ifndef OPENTHREAD_CONFIG_DTLS_ENABLE +#define OPENTHREAD_CONFIG_DTLS_ENABLE \ + (OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE || OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE || \ + OPENTHREAD_CONFIG_COMMISSIONER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE) #endif #endif // CONFIG_DTLS_H_ diff --git a/src/core/config/history_tracker.h b/src/core/config/history_tracker.h index 32da285aa1e..591474a3ffd 100644 --- a/src/core/config/history_tracker.h +++ b/src/core/config/history_tracker.h @@ -127,6 +127,18 @@ #define OPENTHREAD_CONFIG_HISTORY_TRACKER_NEIGHBOR_LIST_SIZE 64 #endif +/** + * @def OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE + * + * Specifies the maximum number of entries in router table history list. + * + * Can be set to zero to configure History Tracker module not to collect any router table history. + * + */ +#ifndef OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE +#define OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE 256 +#endif + /** * @def OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_MESH_PREFIX_LIST_SIZE * diff --git a/src/core/config/ip6.h b/src/core/config/ip6.h index b84918fecf2..75976e1a163 100644 --- a/src/core/config/ip6.h +++ b/src/core/config/ip6.h @@ -35,6 +35,8 @@ #ifndef CONFIG_IP6_H_ #define CONFIG_IP6_H_ +#include "config/border_routing.h" + /** * @def OPENTHREAD_CONFIG_IP6_MAX_EXT_UCAST_ADDRS * @@ -134,7 +136,7 @@ * */ #ifndef OPENTHREAD_CONFIG_MPL_SEED_SET_ENTRIES -#define OPENTHREAD_CONFIG_MPL_SEED_SET_ENTRIES 32 +#define OPENTHREAD_CONFIG_MPL_SEED_SET_ENTRIES 35 #endif /** @@ -170,6 +172,16 @@ #define OPENTHREAD_CONFIG_TCP_ENABLE 1 #endif +/** + * @def OPENTHREAD_CONFIG_TLS_ENABLE + * + * Define as 1 to enable support for TLS over TCP. + * + */ +#if OPENTHREAD_CONFIG_TCP_ENABLE && !defined(OPENTHREAD_CONFIG_TLS_ENABLE) +#define OPENTHREAD_CONFIG_TLS_ENABLE 1 +#endif + /** * @def OPENTHREAD_CONFIG_IP6_ALLOW_LOOP_BACK_HOST_DATAGRAMS * @@ -180,4 +192,14 @@ #define OPENTHREAD_CONFIG_IP6_ALLOW_LOOP_BACK_HOST_DATAGRAMS 1 #endif +/** + * @def OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + * + * Define as 1 to enable IPv6 Border Routing counters. + * + */ +#ifndef OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE +#define OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#endif + #endif // CONFIG_IP6_H_ diff --git a/src/core/config/mac.h b/src/core/config/mac.h index d44ff112c5b..0358b22ebda 100644 --- a/src/core/config/mac.h +++ b/src/core/config/mac.h @@ -466,6 +466,18 @@ #define OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_CSL_TRANSMIT_TIME_AHEAD + * + * Transmission scheduling and ramp up time needed for the CSL transmitter to be ready, in units of microseconds. + * This time must include at least the radio's turnaround time between end of CCA and start of preamble transmission. + * To avoid early CSL transmission it also must not be configured higher than the actual scheduling and ramp up time. + * + */ +#ifndef OPENTHREAD_CONFIG_CSL_TRANSMIT_TIME_AHEAD +#define OPENTHREAD_CONFIG_CSL_TRANSMIT_TIME_AHEAD 40 +#endif + /** * @def OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD * @@ -521,4 +533,15 @@ #define OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_MAC_DATA_POLL_TIMEOUT + * + * This setting specifies the timeout for receiving the Data Frame (in msec) - after an ACK with FP bit set was + * received. + * + */ +#ifndef OPENTHREAD_CONFIG_MAC_DATA_POLL_TIMEOUT +#define OPENTHREAD_CONFIG_MAC_DATA_POLL_TIMEOUT 100 +#endif + #endif // CONFIG_MAC_H_ diff --git a/examples/platforms/cc2538/misc.c b/src/core/config/mesh_diag.h similarity index 63% rename from examples/platforms/cc2538/misc.c rename to src/core/config/mesh_diag.h index 94dca3dd010..4049255feb7 100644 --- a/examples/platforms/cc2538/misc.c +++ b/src/core/config/mesh_diag.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The OpenThread Authors. + * Copyright (c) 2023, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,24 +26,37 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include +/** + * @file + * This file includes compile-time configurations for Mesh Diagnostic module. + * + */ + +#ifndef CONFIG_MESH_DIAG_H_ +#define CONFIG_MESH_DIAG_H_ -#include "platform-cc2538.h" +#include "config/border_routing.h" -void otPlatReset(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - HWREG(SYS_CTRL_PWRDBG) = SYS_CTRL_PWRDBG_FORCE_WARM_RESET; -} +/** + * @def OPENTHREAD_CONFIG_MESH_DIAG_ENABLE + * + * Define to 1 to enable Mesh Diagnostic module. + * + * By default this feature is enabled if device is configured to act as Border Router. + * + */ +#ifndef OPENTHREAD_CONFIG_MESH_DIAG_ENABLE +#define OPENTHREAD_CONFIG_MESH_DIAG_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#endif -otPlatResetReason otPlatGetResetReason(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); - // TODO: Write me! - return OT_PLAT_RESET_REASON_POWER_ON; -} +/** + * @def OPENTHREAD_CONFIG_MESH_DIAG_RESPONSE_TIMEOUT + * + * Specifies the timeout interval in milliseconds waiting for response from router during discover. + * + */ +#ifndef OPENTHREAD_CONFIG_MESH_DIAG_RESPONSE_TIMEOUT +#define OPENTHREAD_CONFIG_MESH_DIAG_RESPONSE_TIMEOUT 5000 +#endif -void otPlatWakeHost(void) -{ - // TODO: implement an operation to wake the host from sleep state. -} +#endif // CONFIG_MESH_DIAG_H_ diff --git a/src/core/config/misc.h b/src/core/config/misc.h index 3070bb07c88..c857850d18c 100644 --- a/src/core/config/misc.h +++ b/src/core/config/misc.h @@ -77,6 +77,19 @@ #define OPENTHREAD_CONFIG_STACK_VERSION_MINOR 1 #endif +/** + * @def OPENTHREAD_CONFIG_DEVICE_POWER_SUPPLY + * + * Specifies the default device power supply config. This config MUST use values from `otPowerSupply` enumeration. + * + * Device manufacturer can use this config to set the power supply config used by the device. This is then used as part + * of default `otDeviceProperties` to determine the Leader Weight used by the device. + * + */ +#ifndef OPENTHREAD_CONFIG_DEVICE_POWER_SUPPLY +#define OPENTHREAD_CONFIG_DEVICE_POWER_SUPPLY OT_POWER_SUPPLY_EXTERNAL +#endif + /** * @def OPENTHREAD_CONFIG_ECDSA_ENABLE * @@ -87,6 +100,17 @@ #define OPENTHREAD_CONFIG_ECDSA_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_DETERMINISTIC_ECDSA_ENABLE + * + * Define to 1 to generate ECDSA signatures deterministically + * according to RFC 6979 instead of randomly. + * + */ +#ifndef OPENTHREAD_CONFIG_DETERMINISTIC_ECDSA_ENABLE +#define OPENTHREAD_CONFIG_DETERMINISTIC_ECDSA_ENABLE 1 +#endif + /** * @def OPENTHREAD_CONFIG_UPTIME_ENABLE * @@ -94,7 +118,7 @@ * */ #ifndef OPENTHREAD_CONFIG_UPTIME_ENABLE -#define OPENTHREAD_CONFIG_UPTIME_ENABLE 0 +#define OPENTHREAD_CONFIG_UPTIME_ENABLE OPENTHREAD_FTD #endif /** @@ -170,6 +194,9 @@ * to that on 32bit system. As a result, the first message always have some * bytes left for small packets. * + * Some configuration options can increase the buffer size requirements, including + * OPENTHREAD_CONFIG_MLE_MAX_CHILDREN and OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE. + * */ #ifndef OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE #define OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE (sizeof(void *) * 32) @@ -244,9 +271,9 @@ /** * @def OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS * - * Define as 1 to enable bultin-mbedtls. + * Define as 1 to enable builtin-mbedtls. * - * Note that the OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS determines whether to use bultin-mbedtls as well as + * Note that the OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS determines whether to use builtin-mbedtls as well as * whether to manage mbedTLS internally, such as memory allocation and debug. * */ @@ -257,7 +284,7 @@ /** * @def OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS_MANAGEMENT * - * Define as 1 to enable bultin mbedtls management. + * Define as 1 to enable builtin mbedtls management. * * OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS_MANAGEMENT determines whether to manage mbedTLS memory * allocation and debug config internally. If not configured, the default is to enable builtin @@ -332,6 +359,19 @@ #define OPENTHREAD_CONFIG_ASSERT_ENABLE 1 #endif +/** + * @def OPENTHREAD_CONFIG_ASSERT_CHECK_API_POINTER_PARAM_FOR_NULL + * + * Define as 1 to enable assert check of pointer-type API input parameters against null. + * + * Enabling this feature can increase code-size significantly due to many assert checks added for all API pointer + * parameters. It is recommended to enable and use this feature during debugging only. + * + */ +#ifndef OPENTHREAD_CONFIG_ASSERT_CHECK_API_POINTER_PARAM_FOR_NULL +#define OPENTHREAD_CONFIG_ASSERT_CHECK_API_POINTER_PARAM_FOR_NULL 0 +#endif + /** * @def OPENTHREAD_CONFIG_ENABLE_DEBUG_UART * @@ -554,16 +594,6 @@ #endif // OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT #endif // OPENTHREAD_CONFIG_DEFAULT_CHANNEL -/** - * @def OPENTHREAD_CONFIG_LEGACY_ENABLE - * - * Define to 1 to enable legacy network support. - * - */ -#ifndef OPENTHREAD_CONFIG_LEGACY_ENABLE -#define OPENTHREAD_CONFIG_LEGACY_ENABLE 0 -#endif - /** * @def OPENTHREAD_CONFIG_OTNS_ENABLE * @@ -604,4 +634,14 @@ #define OPENTHREAD_CONFIG_NEIGHBOR_DISCOVERY_AGENT_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_ALLOW_EMPTY_NETWORK_NAME + * + * Define as 1 to enable support for an empty network name (zero-length: "") + * + */ +#ifndef OPENTHREAD_CONFIG_ALLOW_EMPTY_NETWORK_NAME +#define OPENTHREAD_CONFIG_ALLOW_EMPTY_NETWORK_NAME 0 +#endif + #endif // CONFIG_MISC_H_ diff --git a/src/core/config/mle.h b/src/core/config/mle.h index 872e1a7fe3d..57e1e507eb9 100644 --- a/src/core/config/mle.h +++ b/src/core/config/mle.h @@ -88,6 +88,18 @@ #define OPENTHREAD_CONFIG_MLE_IP_ADDRS_TO_REGISTER (OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD) #endif +/** + * @def OPENTHREAD_CONFIG_MLE_DEFAULT_LEADER_WEIGHT_ADJUSTMENT + * + * Specifies the default value for `mLeaderWeightAdjustment` in `otDeviceProperties`. MUST be from -16 up to +16. + * + * This value is used to adjust the calculated Leader Weight from `otDeviceProperties`. + * + */ +#ifndef OPENTHREAD_CONFIG_MLE_DEFAULT_LEADER_WEIGHT_ADJUSTMENT +#define OPENTHREAD_CONFIG_MLE_DEFAULT_LEADER_WEIGHT_ADJUSTMENT 0 +#endif + /** * @def OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE * @@ -257,7 +269,19 @@ * */ #ifndef OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH -#define OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH 0 +#define OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE + * + * Define as 1 to support `otThreadRegisterParentResponseCallback()` API which registers a callback to notify user + * of received Parent Response message(s) during attach. This API is mainly intended for debugging and therefore is + * disabled by default. + * + */ +#ifndef OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE +#define OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE 0 #endif /** diff --git a/src/core/config/nat64.h b/src/core/config/nat64.h new file mode 100644 index 00000000000..fff5883b072 --- /dev/null +++ b/src/core/config/nat64.h @@ -0,0 +1,78 @@ +/* + * 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 compile-time configurations for NAT64. + * + */ + +#ifndef CONFIG_NAT64_H_ +#define CONFIG_NAT64_H_ + +/** + * @def OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + * + * Define to 1 to enable the internal NAT64 translator. + * + */ +#ifndef OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE +#define OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_NAT64_MAX_MAPPINGS + * + * Specifies maximum number of active mappings for NAT64. + * + */ +#ifndef OPENTHREAD_CONFIG_NAT64_MAX_MAPPINGS +#define OPENTHREAD_CONFIG_NAT64_MAX_MAPPINGS 254 +#endif + +/** + * @def OPENTHREAD_CONFIG_NAT64_IDLE_TIMEOUT_SECONDS + * + * Specifies timeout in seconds before removing an inactive address mapping. + * + */ +#ifndef OPENTHREAD_CONFIG_NAT64_IDLE_TIMEOUT_SECONDS +#define OPENTHREAD_CONFIG_NAT64_IDLE_TIMEOUT_SECONDS 7200 +#endif + +/** + * @def OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE + * + * Define to 1 to enable NAT64 support in Border Routing Manager. + * + */ +#ifndef OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE +#define OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE 0 +#endif + +#endif diff --git a/src/core/config/netdata_publisher.h b/src/core/config/netdata_publisher.h index bd3ef8f641e..ee8fe583a79 100644 --- a/src/core/config/netdata_publisher.h +++ b/src/core/config/netdata_publisher.h @@ -36,6 +36,7 @@ #define CONFIG_NETDATA_PUBLISHER_H_ #include "config/border_router.h" +#include "config/border_routing.h" #include "config/srp_server.h" /** @@ -147,17 +148,12 @@ /** * @def OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES * - * Specifies maximum number of prefix (on-mesh prefix or external route) entries supported by Publisher. + * Specifies maximum number of prefix (on-mesh prefix or external route) entries reserved by Publisher for use by + * user (through OT public APIs). * */ #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/network_diagnostic.h b/src/core/config/network_diagnostic.h new file mode 100644 index 00000000000..2aaf26018a7 --- /dev/null +++ b/src/core/config/network_diagnostic.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023, 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 compile-time configurations for the Network Diagnostics. + * + */ + +#ifndef CONFIG_NETWORK_DIAGNOSTIC_H_ +#define CONFIG_NETWORK_DIAGNOSTIC_H_ + +/** + * @def OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME + * + * Specifies the default Vendor Name string. + * + */ +#ifndef OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME +#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME "" +#endif + +/** + * @def OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL + * + * Specifies the default Vendor Model string. + * + */ +#ifndef OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL +#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL "" +#endif + +/** + * @def OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION + * + * Specifies the default Vendor SW Version string. + * + */ +#ifndef OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION +#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION "" +#endif + +/** + * @def OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE + * + * Define as 1 to add APIs to allow Vendor Name, Model, SW Version to change at run-time. + * + * It is recommended that Vendor Name, Model, and SW Version are set at build time using the OpenThread configurations + * `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_*`. This way they are treated as constants and won't consume RAM. + * + * However, for situations where the OpenThread stack is integrated as a library into different projects/products, this + * config can be used to add API to change Vendor Name, Model, and SW Version at run-time. In this case, the strings in + * `OPENTHREAD_CONFIG_NET_DIAG_VENDOR_*` are treated as the default values (used when OT stack is initialized). + * + */ +#ifndef OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE +#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE 0 +#endif + +#endif // CONFIG_NETWORK_DIAGNOSTIC_H_ diff --git a/src/core/config/openthread-core-config-check.h b/src/core/config/openthread-core-config-check.h index 4cf71217890..67976e149fb 100644 --- a/src/core/config/openthread-core-config-check.h +++ b/src/core/config/openthread-core-config-check.h @@ -65,6 +65,10 @@ #error "OPENTHREAD_CONFIG_ENABLE_AUTO_START_SUPPORT was removed." #endif +#ifdef OPENTHREAD_ENABLE_ANDROID_NDK +#error "OPENTHREAD_ENABLE_ANDROID_NDK was replaced by OPENTHREAD_CONFIG_ANDROID_NDK_ENABLE." +#endif + #ifdef OPENTHREAD_ENABLE_CERT_LOG #error "OPENTHREAD_ENABLE_CERT_LOG was replaced by OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE." #endif @@ -145,6 +149,10 @@ #error "OPENTHREAD_ENABLE_LEGACY was replaced by OPENTHREAD_CONFIG_LEGACY_ENABLE." #endif +#ifdef OPENTHREAD_CONFIG_LEGACY_ENABLE +#error "OPENTHREAD_CONFIG_LEGACY_ENABLE was removed." +#endif + #ifdef OPENTHREAD_ENABLE_CHILD_SUPERVISION #error "OPENTHREAD_ENABLE_CHILD_SUPERVISION was replaced by OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE." #endif @@ -501,7 +509,7 @@ #ifdef OPENTHREAD_CONFIG_SRP_SERVER_SERVICE_NUMBER #error "OPENTHREAD_CONFIG_SRP_SERVER_SERVICE_NUMBER was removed. "\ - "Service numbers are defined in `network_data_servcie.hpp` per spec" + "Service numbers are defined in `network_data_service.hpp` per spec" #endif #ifdef OPENTHREAD_CONFIG_SRP_SERVER_UDP_PORT @@ -632,4 +640,32 @@ "OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MIN_DELAY and OPENTHREAD_CONFIG_SRP_CLIENT_UPDATE_TX_MAX_DELAY" #endif +#ifdef OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE +#error "OPENTHREAD_CONFIG_BORDER_ROUTING_NAT64_ENABLE was replaced by OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE" +#endif + +#ifdef OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE +#error "OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE is removed. The feature is now supported by default (on 1.3.0)" +#endif + +#ifdef OPENTHREAD_CONFIG_CHILD_SUPERVISION_MSG_NO_ACK_REQUEST +#error "OPENTHREAD_CONFIG_CHILD_SUPERVISION_MSG_NO_ACK_REQUEST is removed". +#endif + +#ifdef OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE +#error "OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE is removed. "\ + "Use OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE to enable client functionality."\ + "Netdiag server functionality is always supported." +#endif + +#ifdef OPENTHREAD_CONFIG_PING_SENDER_DEFAULT_INTEVRAL +#error "OPENTHREAD_CONFIG_PING_SENDER_DEFAULT_INTEVRAL was replaced by "\ + "OPENTHREAD_CONFIG_PING_SENDER_DEFAULT_INTERVAL." +#endif + +#ifdef OPENTHREAD_CONFIG_SRP_SERVER_DEFAULT_ADDDRESS_MODE +#error "OPENTHREAD_CONFIG_SRP_SERVER_DEFAULT_ADDDRESS_MODE was replaced by "\ + "OPENTHREAD_CONFIG_SRP_SERVER_DEFAULT_ADDRESS_MODE." +#endif + #endif // OPENTHREAD_CORE_CONFIG_CHECK_H_ diff --git a/src/core/config/ping_sender.h b/src/core/config/ping_sender.h index 2143389c8fc..d655b1dc837 100644 --- a/src/core/config/ping_sender.h +++ b/src/core/config/ping_sender.h @@ -48,13 +48,13 @@ #endif /** - * @def OPENTHREAD_CONFIG_PING_SENDER_DEFAULT_INTEVRAL + * @def OPENTHREAD_CONFIG_PING_SENDER_DEFAULT_INTERVAL * * Specifies the default ping interval (time between sending echo requests) in milliseconds. * */ -#ifndef OPENTHREAD_CONFIG_PING_SENDER_DEFAULT_INTEVRAL -#define OPENTHREAD_CONFIG_PING_SENDER_DEFAULT_INTEVRAL 1000 +#ifndef OPENTHREAD_CONFIG_PING_SENDER_DEFAULT_INTERVAL +#define OPENTHREAD_CONFIG_PING_SENDER_DEFAULT_INTERVAL 1000 #endif /** diff --git a/src/core/config/platform.h b/src/core/config/platform.h index ca132dbae33..9fa475bdf62 100644 --- a/src/core/config/platform.h +++ b/src/core/config/platform.h @@ -158,6 +158,16 @@ #define OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + * + * Define as 1 to enable platform power calibration support. + * + */ +#ifndef OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +#define OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE 0 +#endif + #if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT #if (!defined(OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE) || \ !defined(OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN) || \ diff --git a/examples/platforms/cc2538/diag.c b/src/core/config/power_calibration.h similarity index 59% rename from examples/platforms/cc2538/diag.c rename to src/core/config/power_calibration.h index 579c1e7948c..56e9467e2c5 100644 --- a/examples/platforms/cc2538/diag.c +++ b/src/core/config/power_calibration.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The OpenThread Authors. + * Copyright (c) 2022, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,55 +26,43 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include -#include -#include -#include - -#include -#include -#include - -#include "platform-cc2538.h" - -#if OPENTHREAD_CONFIG_DIAG_ENABLE - /** - * Diagnostics mode variables. + * @file + * This file includes compile-time configurations for power calibration module. * */ -static bool sDiagMode = false; - -void otPlatDiagModeSet(bool aMode) -{ - sDiagMode = aMode; -} -bool otPlatDiagModeGet() -{ - return sDiagMode; -} +#ifndef CONFIG_POWER_CALIBRATION_H_ +#define CONFIG_POWER_CALIBRATION_H_ -void otPlatDiagChannelSet(uint8_t aChannel) -{ - OT_UNUSED_VARIABLE(aChannel); -} - -void otPlatDiagTxPowerSet(int8_t aTxPower) -{ - OT_UNUSED_VARIABLE(aTxPower); -} +/** + * @def OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE + * + * Define as 1 to enable power calibration support. + * + */ +#ifndef OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE +#define OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE 0 +#endif -void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError) -{ - OT_UNUSED_VARIABLE(aInstance); - OT_UNUSED_VARIABLE(aFrame); - OT_UNUSED_VARIABLE(aError); -} +/** + * @def OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE + * + * The size of the raw power setting byte array. + * + */ +#ifndef OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE +#define OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE 16 +#endif -void otPlatDiagAlarmCallback(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} +/** + * @def OPENTHREAD_CONFIG_POWER_CALIBRATION_NUM_CALIBRATED_POWER_ENTRIES + * + * The number of the calibrated power entries for each channel. + * + */ +#ifndef OPENTHREAD_CONFIG_POWER_CALIBRATION_NUM_CALIBRATED_POWER_ENTRIES +#define OPENTHREAD_CONFIG_POWER_CALIBRATION_NUM_CALIBRATED_POWER_ENTRIES 6 +#endif -#endif // OPENTHREAD_CONFIG_DIAG_ENABLE +#endif // CONFIG_POWER_CALIBRATION_H_ diff --git a/src/core/config/srp_client.h b/src/core/config/srp_client.h index 4bef258edd7..ca609abeb43 100644 --- a/src/core/config/srp_client.h +++ b/src/core/config/srp_client.h @@ -35,6 +35,8 @@ #ifndef CONFIG_SRP_CLIENT_H_ #define CONFIG_SRP_CLIENT_H_ +#include "config/misc.h" + /** * @def OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE * @@ -332,8 +334,12 @@ * */ #ifndef OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_MAX_HOST_ADDRESSES +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE +#define OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_MAX_HOST_ADDRESSES 10 +#else #define OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_MAX_HOST_ADDRESSES 2 #endif +#endif /** * @def OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_HOST_NAME_SIZE diff --git a/src/core/config/srp_server.h b/src/core/config/srp_server.h index aeffbdf3148..5b76ec3ee5e 100644 --- a/src/core/config/srp_server.h +++ b/src/core/config/srp_server.h @@ -46,7 +46,7 @@ #endif /** - * @def OPENTHREAD_CONFIG_SRP_SERVER_DEFAULT_ADDDRESS_MODE + * @def OPENTHREAD_CONFIG_SRP_SERVER_DEFAULT_ADDRESS_MODE * * Specifies the default address mode used by the SRP server. * @@ -56,8 +56,8 @@ * The value of this configuration should be from `otSrpServerAddressMode` enumeration. * */ -#ifndef OPENTHREAD_CONFIG_SRP_SERVER_DEFAULT_ADDDRESS_MODE -#define OPENTHREAD_CONFIG_SRP_SERVER_DEFAULT_ADDDRESS_MODE OT_SRP_SERVER_ADDRESS_MODE_UNICAST +#ifndef OPENTHREAD_CONFIG_SRP_SERVER_DEFAULT_ADDRESS_MODE +#define OPENTHREAD_CONFIG_SRP_SERVER_DEFAULT_ADDRESS_MODE OT_SRP_SERVER_ADDRESS_MODE_UNICAST #endif /** diff --git a/src/core/config/tmf.h b/src/core/config/tmf.h index 538ccaf25fb..653ed75e4b3 100644 --- a/src/core/config/tmf.h +++ b/src/core/config/tmf.h @@ -42,7 +42,7 @@ * */ #ifndef OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_ENTRIES -#define OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_ENTRIES 10 +#define OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_ENTRIES 32 #endif /** @@ -106,6 +106,16 @@ #define OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_MAX_RETRY_DELAY 120 #endif +/** + * @def OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES + * + * Define as 1 to allow address resolution of on-mesh addresses using Thread Network Data DNS/SRP Service entries. + * + */ +#ifndef OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES +#define OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES 1 +#endif + /** * @def OPENTHREAD_CONFIG_TMF_PENDING_DATASET_MINIMUM_DELAY * @@ -163,13 +173,16 @@ #endif /** - * @def OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE + * @def OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE + * + * Define to 1 to enable TMF network diagnostics client. * - * Define to 1 to enable TMF network diagnostics on MTDs. + * The network diagnostic client add API to send diagnostic requests and queries to other node and process the response. + * It is enabled by default on Border Routers. * */ -#ifndef OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE -#define OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE 0 +#ifndef OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE +#define OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE #endif /** @@ -215,7 +228,7 @@ /** * @def OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE * - * This setting configures the Multicast Listener Registration parent proxing in Thread 1.2. + * This setting configures the Multicast Listener Registration parent proxying in Thread 1.2. * */ #ifndef OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE diff --git a/src/core/crypto/aes_ccm.cpp b/src/core/crypto/aes_ccm.cpp index 49c42f2acaa..bb9bec12510 100644 --- a/src/core/crypto/aes_ccm.cpp +++ b/src/core/crypto/aes_ccm.cpp @@ -280,7 +280,7 @@ void AesCcm::Finalize(void *aTag) void AesCcm::GenerateNonce(const Mac::ExtAddress &aAddress, uint32_t aFrameCounter, uint8_t aSecurityLevel, - uint8_t * aNonce) + uint8_t *aNonce) { memcpy(aNonce, aAddress.m8, sizeof(Mac::ExtAddress)); aNonce += sizeof(Mac::ExtAddress); diff --git a/src/core/crypto/aes_ccm.hpp b/src/core/crypto/aes_ccm.hpp index 0234243a27f..8ae9ccc93f5 100644 --- a/src/core/crypto/aes_ccm.hpp +++ b/src/core/crypto/aes_ccm.hpp @@ -196,7 +196,7 @@ class AesCcm static void GenerateNonce(const Mac::ExtAddress &aAddress, uint32_t aFrameCounter, uint8_t aSecurityLevel, - uint8_t * aNonce); + uint8_t *aNonce); private: AesEcb mEcb; diff --git a/src/core/crypto/aes_ecb.cpp b/src/core/crypto/aes_ecb.cpp index f13e0501afc..70966e5accf 100644 --- a/src/core/crypto/aes_ecb.cpp +++ b/src/core/crypto/aes_ecb.cpp @@ -45,20 +45,14 @@ AesEcb::AesEcb(void) SuccessOrAssert(otPlatCryptoAesInit(&mContext)); } -void AesEcb::SetKey(const Key &aKey) -{ - SuccessOrAssert(otPlatCryptoAesSetKey(&mContext, &aKey)); -} +void AesEcb::SetKey(const Key &aKey) { SuccessOrAssert(otPlatCryptoAesSetKey(&mContext, &aKey)); } void AesEcb::Encrypt(const uint8_t aInput[kBlockSize], uint8_t aOutput[kBlockSize]) { SuccessOrAssert(otPlatCryptoAesEncrypt(&mContext, aInput, aOutput)); } -AesEcb::~AesEcb(void) -{ - SuccessOrAssert(otPlatCryptoAesFree(&mContext)); -} +AesEcb::~AesEcb(void) { SuccessOrAssert(otPlatCryptoAesFree(&mContext)); } } // namespace Crypto } // namespace ot diff --git a/src/core/crypto/aes_ecb.hpp b/src/core/crypto/aes_ecb.hpp index 3e30f5f48ea..61680ad2a76 100644 --- a/src/core/crypto/aes_ecb.hpp +++ b/src/core/crypto/aes_ecb.hpp @@ -62,13 +62,13 @@ class AesEcb static constexpr uint8_t kBlockSize = 16; ///< AES-128 block size (bytes). /** - * Constructor to initialize the mbedtls_aes_context. + * Constructor to initialize the AES operation. * */ AesEcb(void); /** - * Destructor to free the mbedtls_aes_context. + * Destructor to free the AES context. * */ ~AesEcb(void); diff --git a/src/core/crypto/crypto_platform.cpp b/src/core/crypto/crypto_platform.cpp index 7b3a77070e9..a8da82e95d5 100644 --- a/src/core/crypto/crypto_platform.cpp +++ b/src/core/crypto/crypto_platform.cpp @@ -32,11 +32,17 @@ #include "openthread-core-config.h" +#include + #include +#include #include +#include #include #include +#include #include +#include #include #include @@ -48,6 +54,7 @@ #include "common/instance.hpp" #include "common/new.hpp" #include "config/crypto.h" +#include "crypto/ecdsa.hpp" #include "crypto/hmac_sha256.hpp" #include "crypto/storage.hpp" @@ -147,7 +154,7 @@ OT_TOOL_WEAK otError otPlatCryptoHmacSha256Init(otCryptoContext *aContext) { Error error = kErrorNone; const mbedtls_md_info_t *mdInfo = nullptr; - mbedtls_md_context_t * context; + mbedtls_md_context_t *context; VerifyOrExit(aContext != nullptr, error = kErrorInvalidArgs); VerifyOrExit(aContext->mContextSize >= sizeof(mbedtls_md_context_t), error = kErrorFailed); @@ -239,9 +246,9 @@ otError otPlatCryptoHkdfInit(otCryptoContext *aContext) } OT_TOOL_WEAK otError otPlatCryptoHkdfExpand(otCryptoContext *aContext, - const uint8_t * aInfo, + const uint8_t *aInfo, uint16_t aInfoLength, - uint8_t * aOutputKey, + uint8_t *aOutputKey, uint16_t aOutputKeyLength) { Error error = kErrorNone; @@ -287,7 +294,7 @@ OT_TOOL_WEAK otError otPlatCryptoHkdfExpand(otCryptoContext *aContext, hmac.Update(iter); hmac.Finish(hash); - copyLength = (aOutputKeyLength > sizeof(hash)) ? sizeof(hash) : aOutputKeyLength; + copyLength = Min(aOutputKeyLength, static_cast(sizeof(hash))); memcpy(aOutputKey, hash.GetBytes(), copyLength); aOutputKey += copyLength; @@ -298,8 +305,8 @@ OT_TOOL_WEAK otError otPlatCryptoHkdfExpand(otCryptoContext *aContext, return error; } -OT_TOOL_WEAK otError otPlatCryptoHkdfExtract(otCryptoContext * aContext, - const uint8_t * aSalt, +OT_TOOL_WEAK otError otPlatCryptoHkdfExtract(otCryptoContext *aContext, + const uint8_t *aSalt, uint16_t aSaltLength, const otCryptoKey *aInputKey) { @@ -484,6 +491,300 @@ OT_TOOL_WEAK otError otPlatCryptoRandomGet(uint8_t *aBuffer, uint16_t aSize) mbedtls_ctr_drbg_random(&sCtrDrbgContext, static_cast(aBuffer), static_cast(aSize))); } +#if OPENTHREAD_CONFIG_ECDSA_ENABLE + +OT_TOOL_WEAK otError otPlatCryptoEcdsaGenerateKey(otPlatCryptoEcdsaKeyPair *aKeyPair) +{ + mbedtls_pk_context pk; + int ret; + + mbedtls_pk_init(&pk); + + ret = mbedtls_pk_setup(&pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)); + VerifyOrExit(ret == 0); + + ret = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(pk), MbedTls::CryptoSecurePrng, nullptr); + VerifyOrExit(ret == 0); + + ret = mbedtls_pk_write_key_der(&pk, aKeyPair->mDerBytes, OT_CRYPTO_ECDSA_MAX_DER_SIZE); + VerifyOrExit(ret > 0); + + aKeyPair->mDerLength = static_cast(ret); + + memmove(aKeyPair->mDerBytes, aKeyPair->mDerBytes + OT_CRYPTO_ECDSA_MAX_DER_SIZE - aKeyPair->mDerLength, + aKeyPair->mDerLength); + +exit: + mbedtls_pk_free(&pk); + + return (ret >= 0) ? kErrorNone : MbedTls::MapError(ret); +} + +OT_TOOL_WEAK otError otPlatCryptoEcdsaGetPublicKey(const otPlatCryptoEcdsaKeyPair *aKeyPair, + otPlatCryptoEcdsaPublicKey *aPublicKey) +{ + Error error = kErrorNone; + mbedtls_pk_context pk; + mbedtls_ecp_keypair *keyPair; + int ret; + + mbedtls_pk_init(&pk); + + VerifyOrExit(mbedtls_pk_setup(&pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)) == 0, error = kErrorFailed); + +#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) + VerifyOrExit(mbedtls_pk_parse_key(&pk, aKeyPair->mDerBytes, aKeyPair->mDerLength, nullptr, 0, + MbedTls::CryptoSecurePrng, nullptr) == 0, + error = kErrorParse); +#else + VerifyOrExit(mbedtls_pk_parse_key(&pk, aKeyPair->mDerBytes, aKeyPair->mDerLength, nullptr, 0) == 0, + error = kErrorParse); +#endif + + keyPair = mbedtls_pk_ec(pk); + + ret = mbedtls_mpi_write_binary(&keyPair->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), aPublicKey->m8, + Ecdsa::P256::kMpiSize); + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); + + ret = mbedtls_mpi_write_binary(&keyPair->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), + aPublicKey->m8 + Ecdsa::P256::kMpiSize, Ecdsa::P256::kMpiSize); + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); + +exit: + mbedtls_pk_free(&pk); + return error; +} + +OT_TOOL_WEAK otError otPlatCryptoEcdsaSign(const otPlatCryptoEcdsaKeyPair *aKeyPair, + const otPlatCryptoSha256Hash *aHash, + otPlatCryptoEcdsaSignature *aSignature) +{ + Error error = kErrorNone; + mbedtls_pk_context pk; + mbedtls_ecp_keypair *keypair; + mbedtls_ecdsa_context ecdsa; + mbedtls_mpi r; + mbedtls_mpi s; + int ret; + + mbedtls_pk_init(&pk); + mbedtls_ecdsa_init(&ecdsa); + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + + VerifyOrExit(mbedtls_pk_setup(&pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)) == 0, error = kErrorFailed); + +#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) + VerifyOrExit(mbedtls_pk_parse_key(&pk, aKeyPair->mDerBytes, aKeyPair->mDerLength, nullptr, 0, + MbedTls::CryptoSecurePrng, nullptr) == 0, + error = kErrorParse); +#else + VerifyOrExit(mbedtls_pk_parse_key(&pk, aKeyPair->mDerBytes, aKeyPair->mDerLength, nullptr, 0) == 0, + error = kErrorParse); +#endif + + keypair = mbedtls_pk_ec(pk); + + ret = mbedtls_ecdsa_from_keypair(&ecdsa, keypair); + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); + +#if (MBEDTLS_VERSION_NUMBER >= 0x02130000) + ret = mbedtls_ecdsa_sign_det_ext(&ecdsa.MBEDTLS_PRIVATE(grp), &r, &s, &ecdsa.MBEDTLS_PRIVATE(d), aHash->m8, + Sha256::Hash::kSize, MBEDTLS_MD_SHA256, MbedTls::CryptoSecurePrng, nullptr); +#else + ret = mbedtls_ecdsa_sign_det(&ecdsa.MBEDTLS_PRIVATE(grp), &r, &s, &ecdsa.MBEDTLS_PRIVATE(d), aHash->m8, + Sha256::Hash::kSize, MBEDTLS_MD_SHA256); +#endif + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); + + OT_ASSERT(mbedtls_mpi_size(&r) <= Ecdsa::P256::kMpiSize); + + ret = mbedtls_mpi_write_binary(&r, aSignature->m8, Ecdsa::P256::kMpiSize); + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); + + ret = mbedtls_mpi_write_binary(&s, aSignature->m8 + Ecdsa::P256::kMpiSize, Ecdsa::P256::kMpiSize); + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); + +exit: + mbedtls_pk_free(&pk); + mbedtls_mpi_free(&s); + mbedtls_mpi_free(&r); + mbedtls_ecdsa_free(&ecdsa); + + return error; +} + +OT_TOOL_WEAK otError otPlatCryptoEcdsaVerify(const otPlatCryptoEcdsaPublicKey *aPublicKey, + const otPlatCryptoSha256Hash *aHash, + const otPlatCryptoEcdsaSignature *aSignature) +{ + Error error = kErrorNone; + mbedtls_ecdsa_context ecdsa; + mbedtls_mpi r; + mbedtls_mpi s; + int ret; + + mbedtls_ecdsa_init(&ecdsa); + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&s); + + ret = mbedtls_ecp_group_load(&ecdsa.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_SECP256R1); + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); + + ret = mbedtls_mpi_read_binary(&ecdsa.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), aPublicKey->m8, Ecdsa::P256::kMpiSize); + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); + ret = mbedtls_mpi_read_binary(&ecdsa.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), aPublicKey->m8 + Ecdsa::P256::kMpiSize, + Ecdsa::P256::kMpiSize); + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); + ret = mbedtls_mpi_lset(&ecdsa.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z), 1); + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); + + ret = mbedtls_mpi_read_binary(&r, aSignature->m8, Ecdsa::P256::kMpiSize); + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); + + ret = mbedtls_mpi_read_binary(&s, aSignature->m8 + Ecdsa::P256::kMpiSize, Ecdsa::P256::kMpiSize); + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); + + ret = mbedtls_ecdsa_verify(&ecdsa.MBEDTLS_PRIVATE(grp), aHash->m8, Sha256::Hash::kSize, &ecdsa.MBEDTLS_PRIVATE(Q), + &r, &s); + VerifyOrExit(ret == 0, error = kErrorSecurity); + +exit: + mbedtls_mpi_free(&s); + mbedtls_mpi_free(&r); + mbedtls_ecdsa_free(&ecdsa); + + return error; +} + +#endif // #if OPENTHREAD_CONFIG_ECDSA_ENABLE + +#endif // #if !OPENTHREAD_RADIO + +#elif OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_PSA + +#if !OPENTHREAD_RADIO +#if OPENTHREAD_CONFIG_ECDSA_ENABLE + +OT_TOOL_WEAK otError otPlatCryptoEcdsaGenerateKey(otPlatCryptoEcdsaKeyPair *aKeyPair) +{ + OT_UNUSED_VARIABLE(aKeyPair); + + return OT_ERROR_NOT_CAPABLE; +} + +OT_TOOL_WEAK otError otPlatCryptoEcdsaGetPublicKey(const otPlatCryptoEcdsaKeyPair *aKeyPair, + otPlatCryptoEcdsaPublicKey *aPublicKey) +{ + OT_UNUSED_VARIABLE(aKeyPair); + OT_UNUSED_VARIABLE(aPublicKey); + + return OT_ERROR_NOT_CAPABLE; +} + +OT_TOOL_WEAK otError otPlatCryptoEcdsaSign(const otPlatCryptoEcdsaKeyPair *aKeyPair, + const otPlatCryptoSha256Hash *aHash, + otPlatCryptoEcdsaSignature *aSignature) +{ + OT_UNUSED_VARIABLE(aKeyPair); + OT_UNUSED_VARIABLE(aHash); + OT_UNUSED_VARIABLE(aSignature); + + return OT_ERROR_NOT_CAPABLE; +} + +OT_TOOL_WEAK otError otPlatCryptoEcdsaVerify(const otPlatCryptoEcdsaPublicKey *aPublicKey, + const otPlatCryptoSha256Hash *aHash, + const otPlatCryptoEcdsaSignature *aSignature) + +{ + OT_UNUSED_VARIABLE(aPublicKey); + OT_UNUSED_VARIABLE(aHash); + OT_UNUSED_VARIABLE(aSignature); + + return OT_ERROR_NOT_CAPABLE; +} +#endif // #if OPENTHREAD_CONFIG_ECDSA_ENABLE + #endif // #if !OPENTHREAD_RADIO #endif // #if OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_MBEDTLS + +//--------------------------------------------------------------------------------------------------------------------- +// APIs to be used in "hybrid" mode by every OPENTHREAD_CONFIG_CRYPTO_LIB variant until full PSA support is ready + +#if OPENTHREAD_FTD + +OT_TOOL_WEAK void otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword, + uint16_t aPasswordLen, + const uint8_t *aSalt, + uint16_t aSaltLen, + uint32_t aIterationCounter, + uint16_t aKeyLen, + uint8_t *aKey) +{ + const size_t kBlockSize = MBEDTLS_CIPHER_BLKSIZE_MAX; + uint8_t prfInput[OT_CRYPTO_PBDKF2_MAX_SALT_SIZE + 4]; // Salt || INT(), for U1 calculation + long prfOne[kBlockSize / sizeof(long)]; + long prfTwo[kBlockSize / sizeof(long)]; + long keyBlock[kBlockSize / sizeof(long)]; + uint32_t blockCounter = 0; + uint8_t *key = aKey; + uint16_t keyLen = aKeyLen; + uint16_t useLen = 0; + + OT_ASSERT(aSaltLen <= sizeof(prfInput)); + memcpy(prfInput, aSalt, aSaltLen); + OT_ASSERT(aIterationCounter % 2 == 0); + aIterationCounter /= 2; + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + // limit iterations to avoid OSS-Fuzz timeouts + aIterationCounter = 2; +#endif + + while (keyLen) + { + ++blockCounter; + prfInput[aSaltLen + 0] = static_cast(blockCounter >> 24); + prfInput[aSaltLen + 1] = static_cast(blockCounter >> 16); + prfInput[aSaltLen + 2] = static_cast(blockCounter >> 8); + prfInput[aSaltLen + 3] = static_cast(blockCounter); + + // Calculate U_1 + mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, prfInput, aSaltLen + 4, + reinterpret_cast(keyBlock)); + + // Calculate U_2 + mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast(keyBlock), kBlockSize, + reinterpret_cast(prfOne)); + + for (uint32_t j = 0; j < kBlockSize / sizeof(long); ++j) + { + keyBlock[j] ^= prfOne[j]; + } + + for (uint32_t i = 1; i < aIterationCounter; ++i) + { + // Calculate U_{2 * i - 1} + mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast(prfOne), kBlockSize, + reinterpret_cast(prfTwo)); + // Calculate U_{2 * i} + mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast(prfTwo), kBlockSize, + reinterpret_cast(prfOne)); + + for (uint32_t j = 0; j < kBlockSize / sizeof(long); ++j) + { + keyBlock[j] ^= prfOne[j] ^ prfTwo[j]; + } + } + + useLen = Min(keyLen, static_cast(kBlockSize)); + memcpy(key, keyBlock, useLen); + key += useLen; + keyLen -= useLen; + } +} + +#endif // #if OPENTHREAD_FTD diff --git a/src/core/crypto/ecdsa.cpp b/src/core/crypto/ecdsa.cpp deleted file mode 100644 index b91e4e18961..00000000000 --- a/src/core/crypto/ecdsa.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/* - * Copyright (c) 2018, 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 ECDSA signing. - */ - -#include "ecdsa.hpp" - -#if OPENTHREAD_CONFIG_ECDSA_ENABLE - -#ifndef MBEDTLS_USE_TINYCRYPT - -#include - -#include -#include -#include -#include - -#include "common/code_utils.hpp" -#include "common/debug.hpp" -#include "common/random.hpp" -#include "crypto/mbedtls.hpp" - -namespace ot { -namespace Crypto { -namespace Ecdsa { - -Error P256::KeyPair::Generate(void) -{ - mbedtls_pk_context pk; - int ret; - - mbedtls_pk_init(&pk); - - ret = mbedtls_pk_setup(&pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)); - VerifyOrExit(ret == 0); - - ret = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(pk), MbedTls::CryptoSecurePrng, nullptr); - VerifyOrExit(ret == 0); - - ret = mbedtls_pk_write_key_der(&pk, mDerBytes, sizeof(mDerBytes)); - VerifyOrExit(ret > 0); - - mDerLength = static_cast(ret); - - memmove(mDerBytes, mDerBytes + sizeof(mDerBytes) - mDerLength, mDerLength); - -exit: - mbedtls_pk_free(&pk); - - return (ret >= 0) ? kErrorNone : MbedTls::MapError(ret); -} - -Error P256::KeyPair::Parse(void *aContext) const -{ - Error error = kErrorNone; - mbedtls_pk_context *pk = reinterpret_cast(aContext); - - mbedtls_pk_init(pk); - - VerifyOrExit(mbedtls_pk_setup(pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)) == 0, error = kErrorFailed); -#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) - VerifyOrExit(mbedtls_pk_parse_key(pk, mDerBytes, mDerLength, nullptr, 0, MbedTls::CryptoSecurePrng, nullptr) == 0, - error = kErrorParse); -#else - VerifyOrExit(mbedtls_pk_parse_key(pk, mDerBytes, mDerLength, nullptr, 0) == 0, error = kErrorParse); -#endif - -exit: - return error; -} - -Error P256::KeyPair::GetPublicKey(PublicKey &aPublicKey) const -{ - Error error; - mbedtls_pk_context pk; - mbedtls_ecp_keypair *keyPair; - int ret; - - SuccessOrExit(error = Parse(&pk)); - - keyPair = mbedtls_pk_ec(pk); - - ret = mbedtls_mpi_write_binary(&keyPair->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), aPublicKey.mData, kMpiSize); - VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); - - ret = mbedtls_mpi_write_binary(&keyPair->MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), aPublicKey.mData + kMpiSize, - kMpiSize); - VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); - -exit: - mbedtls_pk_free(&pk); - return error; -} - -Error P256::KeyPair::Sign(const Sha256::Hash &aHash, Signature &aSignature) const -{ - Error error; - mbedtls_pk_context pk; - mbedtls_ecp_keypair * keypair; - mbedtls_ecdsa_context ecdsa; - mbedtls_mpi r; - mbedtls_mpi s; - int ret; - - mbedtls_ecdsa_init(&ecdsa); - mbedtls_mpi_init(&r); - mbedtls_mpi_init(&s); - - SuccessOrExit(error = Parse(&pk)); - - keypair = mbedtls_pk_ec(pk); - - ret = mbedtls_ecdsa_from_keypair(&ecdsa, keypair); - VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); - -#if (MBEDTLS_VERSION_NUMBER >= 0x02130000) - ret = mbedtls_ecdsa_sign_det_ext(&ecdsa.MBEDTLS_PRIVATE(grp), &r, &s, &ecdsa.MBEDTLS_PRIVATE(d), aHash.GetBytes(), - Sha256::Hash::kSize, MBEDTLS_MD_SHA256, MbedTls::CryptoSecurePrng, nullptr); -#else - ret = mbedtls_ecdsa_sign_det(&ecdsa.MBEDTLS_PRIVATE(grp), &r, &s, &ecdsa.MBEDTLS_PRIVATE(d), aHash.GetBytes(), - Sha256::Hash::kSize, MBEDTLS_MD_SHA256); -#endif - VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); - - OT_ASSERT(mbedtls_mpi_size(&r) <= kMpiSize); - - ret = mbedtls_mpi_write_binary(&r, aSignature.mShared.mMpis.mR, kMpiSize); - VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); - - ret = mbedtls_mpi_write_binary(&s, aSignature.mShared.mMpis.mS, kMpiSize); - VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); - -exit: - mbedtls_pk_free(&pk); - mbedtls_mpi_free(&s); - mbedtls_mpi_free(&r); - mbedtls_ecdsa_free(&ecdsa); - - return error; -} - -Error P256::PublicKey::Verify(const Sha256::Hash &aHash, const Signature &aSignature) const -{ - Error error = kErrorNone; - mbedtls_ecdsa_context ecdsa; - mbedtls_mpi r; - mbedtls_mpi s; - int ret; - - mbedtls_ecdsa_init(&ecdsa); - mbedtls_mpi_init(&r); - mbedtls_mpi_init(&s); - - ret = mbedtls_ecp_group_load(&ecdsa.MBEDTLS_PRIVATE(grp), MBEDTLS_ECP_DP_SECP256R1); - VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); - - ret = mbedtls_mpi_read_binary(&ecdsa.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(X), GetBytes(), kMpiSize); - VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); - ret = mbedtls_mpi_read_binary(&ecdsa.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Y), GetBytes() + kMpiSize, kMpiSize); - VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); - ret = mbedtls_mpi_lset(&ecdsa.MBEDTLS_PRIVATE(Q).MBEDTLS_PRIVATE(Z), 1); - VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); - - ret = mbedtls_mpi_read_binary(&r, aSignature.mShared.mMpis.mR, kMpiSize); - VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); - - ret = mbedtls_mpi_read_binary(&s, aSignature.mShared.mMpis.mS, kMpiSize); - VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); - - ret = mbedtls_ecdsa_verify(&ecdsa.MBEDTLS_PRIVATE(grp), aHash.GetBytes(), Sha256::Hash::kSize, - &ecdsa.MBEDTLS_PRIVATE(Q), &r, &s); - VerifyOrExit(ret == 0, error = kErrorSecurity); - -exit: - mbedtls_mpi_free(&s); - mbedtls_mpi_free(&r); - mbedtls_ecdsa_free(&ecdsa); - - return error; -} - -Error Sign(uint8_t * aOutput, - uint16_t & aOutputLength, - const uint8_t *aInputHash, - uint16_t aInputHashLength, - const uint8_t *aPrivateKey, - uint16_t aPrivateKeyLength) -{ - Error error = kErrorNone; - mbedtls_ecdsa_context ctx; - mbedtls_pk_context pkCtx; - mbedtls_ecp_keypair * keypair; - mbedtls_mpi rMpi; - mbedtls_mpi sMpi; - - mbedtls_pk_init(&pkCtx); - mbedtls_ecdsa_init(&ctx); - mbedtls_mpi_init(&rMpi); - mbedtls_mpi_init(&sMpi); - - // Parse a private key in PEM format. -#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) - VerifyOrExit(mbedtls_pk_parse_key(&pkCtx, aPrivateKey, aPrivateKeyLength, nullptr, 0, MbedTls::CryptoSecurePrng, - nullptr) == 0, - error = kErrorInvalidArgs); -#else - VerifyOrExit(mbedtls_pk_parse_key(&pkCtx, aPrivateKey, aPrivateKeyLength, nullptr, 0) == 0, - error = kErrorInvalidArgs); -#endif - VerifyOrExit(mbedtls_pk_get_type(&pkCtx) == MBEDTLS_PK_ECKEY, error = kErrorInvalidArgs); - - keypair = mbedtls_pk_ec(pkCtx); - OT_ASSERT(keypair != nullptr); - - VerifyOrExit(mbedtls_ecdsa_from_keypair(&ctx, keypair) == 0, error = kErrorFailed); - - // Sign using ECDSA. - VerifyOrExit(mbedtls_ecdsa_sign(&ctx.MBEDTLS_PRIVATE(grp), &rMpi, &sMpi, &ctx.MBEDTLS_PRIVATE(d), aInputHash, - aInputHashLength, MbedTls::CryptoSecurePrng, nullptr) == 0, - error = kErrorFailed); - VerifyOrExit(mbedtls_mpi_size(&rMpi) + mbedtls_mpi_size(&sMpi) <= aOutputLength, error = kErrorNoBufs); - - // Concatenate the two octet sequences in the order R and then S. - VerifyOrExit(mbedtls_mpi_write_binary(&rMpi, aOutput, mbedtls_mpi_size(&rMpi)) == 0, error = kErrorFailed); - aOutputLength = static_cast(mbedtls_mpi_size(&rMpi)); - - VerifyOrExit(mbedtls_mpi_write_binary(&sMpi, aOutput + aOutputLength, mbedtls_mpi_size(&sMpi)) == 0, - error = kErrorFailed); - aOutputLength += mbedtls_mpi_size(&sMpi); - -exit: - mbedtls_mpi_free(&rMpi); - mbedtls_mpi_free(&sMpi); - mbedtls_ecdsa_free(&ctx); - mbedtls_pk_free(&pkCtx); - - return error; -} - -} // namespace Ecdsa -} // namespace Crypto -} // namespace ot - -#endif // MBEDTLS_USE_TINYCRYPT -#endif // OPENTHREAD_CONFIG_ECDSA_ENABLE diff --git a/src/core/crypto/ecdsa.hpp b/src/core/crypto/ecdsa.hpp index 98b439b814d..41a693d7dda 100644 --- a/src/core/crypto/ecdsa.hpp +++ b/src/core/crypto/ecdsa.hpp @@ -41,8 +41,12 @@ #include #include +#include +#include + #include "common/error.hpp" #include "crypto/sha256.hpp" +#include "crypto/storage.hpp" namespace ot { namespace Crypto { @@ -74,6 +78,9 @@ class P256 class PublicKey; class KeyPair; +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + class KeyPairAsRef; +#endif /** * This class represents an ECDSA signature. @@ -83,13 +90,16 @@ class P256 * */ OT_TOOL_PACKED_BEGIN - class Signature + class Signature : public otPlatCryptoEcdsaSignature { friend class KeyPair; friend class PublicKey; +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + friend class KeyPairAsRef; +#endif public: - static constexpr uint8_t kSize = 2 * kMpiSize; ///< Signature size in bytes (two times the curve MPI size). + static constexpr uint8_t kSize = OT_CRYPTO_ECDSA_SIGNATURE_SIZE; ///< Signature size in bytes. /** * This method returns the signature as a byte array. @@ -97,21 +107,7 @@ class P256 * @returns A pointer to the byte array containing the signature. * */ - const uint8_t *GetBytes(void) const { return mShared.mKey; } - - private: - OT_TOOL_PACKED_BEGIN - struct Mpis - { - uint8_t mR[kMpiSize]; - uint8_t mS[kMpiSize]; - } OT_TOOL_PACKED_END; - - union OT_TOOL_PACKED_FIELD - { - Mpis mMpis; - uint8_t mKey[kSize]; - } mShared; + const uint8_t *GetBytes(void) const { return m8; } } OT_TOOL_PACKED_END; /** @@ -120,23 +116,20 @@ class P256 * The key pair is stored using Distinguished Encoding Rules (DER) format (per RFC 5915). * */ - class KeyPair + class KeyPair : public otPlatCryptoEcdsaKeyPair { public: /** * Max buffer size (in bytes) for representing the key-pair in DER format. * */ - static constexpr uint8_t kMaxDerSize = 125; + static constexpr uint8_t kMaxDerSize = OT_CRYPTO_ECDSA_MAX_DER_SIZE; /** * This constructor initializes a `KeyPair` as empty (no key). * */ - KeyPair(void) - : mDerLength(0) - { - } + KeyPair(void) { mDerLength = 0; } /** * This method generates and populates the `KeyPair` with a new public/private keys. @@ -147,7 +140,7 @@ class P256 * @retval kErrorFailed Failed to generate key. * */ - Error Generate(void); + Error Generate(void) { return otPlatCryptoEcdsaGenerateKey(this); } /** * This method gets the associated public key from the `KeyPair`. @@ -158,7 +151,7 @@ class P256 * @retval kErrorParse The key-pair DER format could not be parsed (invalid format). * */ - Error GetPublicKey(PublicKey &aPublicKey) const; + Error GetPublicKey(PublicKey &aPublicKey) const { return otPlatCryptoEcdsaGetPublicKey(this, &aPublicKey); } /** * This method gets the pointer to start of the buffer containing the key-pair info in DER format. @@ -212,14 +205,110 @@ class P256 * @retval kErrorNoBufs Failed to allocate buffer for signature calculation. * */ - Error Sign(const Sha256::Hash &aHash, Signature &aSignature) const; + Error Sign(const Sha256::Hash &aHash, Signature &aSignature) const + { + return otPlatCryptoEcdsaSign(this, &aHash, &aSignature); + } + }; + +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + /** + * This class represents a key pair (public and private keys) as a PSA KeyRef. + * + */ + class KeyPairAsRef + { + public: + /** + * This constructor initializes a `KeyPairAsRef`. + * + * @param[in] aKeyRef PSA key reference to use while using the keypair. + */ + explicit KeyPairAsRef(otCryptoKeyRef aKeyRef = 0) { mKeyRef = aKeyRef; } - private: - Error Parse(void *aContext) const; + /** + * This method generates a new keypair and imports it into PSA ITS. + * + * @retval kErrorNone A new key pair was generated successfully. + * @retval kErrorNoBufs Failed to allocate buffer for key generation. + * @retval kErrorNotCapable Feature not supported. + * @retval kErrorFailed Failed to generate key. + * + */ + Error Generate(void) const { return otPlatCryptoEcdsaGenerateAndImportKey(mKeyRef); } + + /** + * This method imports a new keypair into PSA ITS. + * + * @param[in] aKeyPair KeyPair to be imported in DER format. + * + * @retval kErrorNone A key pair was imported successfully. + * @retval kErrorNotCapable Feature not supported. + * @retval kErrorFailed Failed to import the key. + * + */ + Error ImportKeyPair(const KeyPair &aKeyPair) + { + return Crypto::Storage::ImportKey(mKeyRef, Storage::kKeyTypeEcdsa, Storage::kKeyAlgorithmEcdsa, + (Storage::kUsageSignHash | Storage::kUsageVerifyHash), + Storage::kTypePersistent, aKeyPair.GetDerBytes(), + aKeyPair.GetDerLength()); + } + + /** + * This method gets the associated public key from the keypair referenced by mKeyRef. + * + * @param[out] aPublicKey A reference to a `PublicKey` to output the value. + * + * @retval kErrorNone Public key was retrieved successfully, and @p aPublicKey is updated. + * @retval kErrorFailed There was a error exporting the public key from PSA. + * + */ + Error GetPublicKey(PublicKey &aPublicKey) const + { + return otPlatCryptoEcdsaExportPublicKey(mKeyRef, &aPublicKey); + } + + /** + * This method calculates the ECDSA signature for a hashed message using the private key from keypair + * referenced by mKeyRef. + * + * This method uses the deterministic digital signature generation procedure from RFC 6979. + * + * @param[in] aHash The SHA-256 hash value of the message to use for signature calculation. + * @param[out] aSignature A reference to a `Signature` to output the calculated signature value. + * + * @retval kErrorNone The signature was calculated successfully and @p aSignature was updated. + * @retval kErrorParse The key-pair DER format could not be parsed (invalid format). + * @retval kErrorInvalidArgs The @p aHash is invalid. + * @retval kErrorNoBufs Failed to allocate buffer for signature calculation. + * + */ + Error Sign(const Sha256::Hash &aHash, Signature &aSignature) const + { + return otPlatCryptoEcdsaSignUsingKeyRef(mKeyRef, &aHash, &aSignature); + } - uint8_t mDerBytes[kMaxDerSize]; - uint8_t mDerLength; + /** + * This method gets the Key reference for the keypair stored in the PSA. + * + * @returns The PSA key ref. + * + */ + otCryptoKeyRef GetKeyRef(void) const { return mKeyRef; } + + /** + * This method sets the Key reference. + * + * @param[in] aKeyRef PSA key reference to use while using the keypair. + * + */ + void SetKeyRef(otCryptoKeyRef aKeyRef) { mKeyRef = aKeyRef; } + + private: + otCryptoKeyRef mKeyRef; }; +#endif /** * This class represents a public key. @@ -228,12 +317,15 @@ class P256 * */ OT_TOOL_PACKED_BEGIN - class PublicKey : public Equatable + class PublicKey : public otPlatCryptoEcdsaPublicKey, public Equatable { friend class KeyPair; +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + friend class KeyPairAsRef; +#endif public: - static constexpr uint8_t kSize = kMpiSize * 2; ///< Size of the public key in bytes. + static constexpr uint8_t kSize = OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE; ///< Size of the public key in bytes. /** * This method gets the pointer to the buffer containing the public key (as an uncompressed curve point). @@ -241,7 +333,7 @@ class P256 * @return The pointer to the buffer containing the public key (with `kSize` bytes). * */ - const uint8_t *GetBytes(void) const { return mData; } + const uint8_t *GetBytes(void) const { return m8; } /** * This method uses the `PublicKey` to verify the ECDSA signature of a hashed message. @@ -255,36 +347,14 @@ class P256 * @retval kErrorNoBufs Failed to allocate buffer for signature verification * */ - Error Verify(const Sha256::Hash &aHash, const Signature &aSignature) const; + Error Verify(const Sha256::Hash &aHash, const Signature &aSignature) const + { + return otPlatCryptoEcdsaVerify(this, &aHash, &aSignature); + } - private: - uint8_t mData[kSize]; } OT_TOOL_PACKED_END; }; -/** - * This function creates an ECDSA signature. - * - * @param[out] aOutput An output buffer where ECDSA sign should be stored. - * @param[in,out] aOutputLength The length of the @p aOutput buffer. - * @param[in] aInputHash An input hash. - * @param[in] aInputHashLength The length of the @p aInputHash buffer. - * @param[in] aPrivateKey A private key in PEM format. - * @param[in] aPrivateKeyLength The length of the @p aPrivateKey buffer. - * - * @retval kErrorNone ECDSA sign has been created successfully. - * @retval kErrorNoBufs Output buffer is too small. - * @retval kErrorInvalidArgs Private key is not valid EC Private Key. - * @retval kErrorFailed Error during signing. - * - */ -Error Sign(uint8_t * aOutput, - uint16_t & aOutputLength, - const uint8_t *aInputHash, - uint16_t aInputHashLength, - const uint8_t *aPrivateKey, - uint16_t aPrivateKeyLength); - /** * @} * @@ -292,6 +362,11 @@ Error Sign(uint8_t * aOutput, } // namespace Ecdsa } // namespace Crypto + +DefineCoreType(otPlatCryptoEcdsaSignature, Crypto::Ecdsa::P256::Signature); +DefineCoreType(otPlatCryptoEcdsaKeyPair, Crypto::Ecdsa::P256::KeyPair); +DefineCoreType(otPlatCryptoEcdsaPublicKey, Crypto::Ecdsa::P256::PublicKey); + } // namespace ot #endif // OPENTHREAD_CONFIG_ECDSA_ENABLE diff --git a/src/core/crypto/ecdsa_tinycrypt.cpp b/src/core/crypto/ecdsa_tinycrypt.cpp deleted file mode 100644 index ea75a2d8150..00000000000 --- a/src/core/crypto/ecdsa_tinycrypt.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/* - * 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 ECDSA signing using TinyCrypt library. - */ - -#include "ecdsa.hpp" - -#if OPENTHREAD_CONFIG_ECDSA_ENABLE - -#ifdef MBEDTLS_USE_TINYCRYPT - -#include - -#include -#include - -#include -#include -#include - -#include "common/code_utils.hpp" -#include "common/debug.hpp" -#include "common/random.hpp" -#include "crypto/mbedtls.hpp" - -namespace ot { -namespace Crypto { -namespace Ecdsa { - -Error P256::KeyPair::Generate(void) -{ - mbedtls_pk_context pk; - mbedtls_uecc_keypair *keypair; - int ret; - - mbedtls_pk_init(&pk); - - ret = mbedtls_pk_setup(&pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)); - VerifyOrExit(ret == 0); - - keypair = mbedtls_pk_uecc(pk); - - ret = uECC_make_key(keypair->public_key, keypair->private_key); - VerifyOrExit(ret == UECC_SUCCESS); - - ret = mbedtls_pk_write_key_der(&pk, mDerBytes, sizeof(mDerBytes)); - VerifyOrExit(ret > 0); - - mDerLength = static_cast(ret); - - memmove(mDerBytes, mDerBytes + sizeof(mDerBytes) - mDerLength, mDerLength); - -exit: - mbedtls_pk_free(&pk); - - return (ret >= 0) ? kErrorNone : MbedTls::MapError(ret); -} - -Error P256::KeyPair::Parse(void *aContext) const -{ - Error error = kErrorNone; - mbedtls_pk_context *pk = reinterpret_cast(aContext); - - mbedtls_pk_init(pk); - - VerifyOrExit(mbedtls_pk_setup(pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY)) == 0, error = kErrorFailed); -#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) - VerifyOrExit(mbedtls_pk_parse_key(pk, mDerBytes, mDerLength, nullptr, 0, MbedTls::CryptoSecurePrng, nullptr) == 0, - error = kErrorParse); -#else - VerifyOrExit(mbedtls_pk_parse_key(pk, mDerBytes, mDerLength, nullptr, 0) == 0, error = kErrorParse); -#endif - -exit: - return error; -} - -Error P256::KeyPair::GetPublicKey(PublicKey &aPublicKey) const -{ - Error error; - mbedtls_pk_context pk; - mbedtls_uecc_keypair *keyPair; - int ret; - - SuccessOrExit(error = Parse(&pk)); - - keyPair = mbedtls_pk_uecc(pk); - - memcpy(aPublicKey.mData, keyPair->public_key, kMpiSize); - memcpy(aPublicKey.mData + kMpiSize, keyPair->public_key + kMpiSize, kMpiSize); - -exit: - mbedtls_pk_free(&pk); - - return error; -} - -Error P256::KeyPair::Sign(const Sha256::Hash &aHash, Signature &aSignature) const -{ - Error error; - mbedtls_pk_context pk; - mbedtls_uecc_keypair *keypair; - int ret; - uint8_t sig[2 * kMpiSize]; - - SuccessOrExit(error = Parse(&pk)); - - keypair = mbedtls_pk_uecc(pk); - - ret = uECC_sign(keypair->private_key, aHash.GetBytes(), Sha256::Hash::kSize, sig); - VerifyOrExit(ret == UECC_SUCCESS, error = MbedTls::MapError(ret)); - - memcpy(aSignature.mShared.mMpis.mR, sig, kMpiSize); - memcpy(aSignature.mShared.mMpis.mS, sig + kMpiSize, kMpiSize); - -exit: - mbedtls_pk_free(&pk); - - return error; -} - -Error P256::PublicKey::Verify(const Sha256::Hash &aHash, const Signature &aSignature) const -{ - Error error = kErrorNone; - int ret; - uint8_t public_key[2 * kMpiSize]; - uint8_t sig[2 * kMpiSize]; - - memcpy(public_key, GetBytes(), 2 * kMpiSize); - - memcpy(sig, aSignature.mShared.mMpis.mR, kMpiSize); - memcpy(sig + kMpiSize, aSignature.mShared.mMpis.mS, kMpiSize); - - ret = uECC_verify(public_key, aHash.GetBytes(), Sha256::Hash::kSize, sig); - VerifyOrExit(ret == UECC_SUCCESS, error = kErrorSecurity); - -exit: - return error; -} - -Error Sign(uint8_t * aOutput, - uint16_t & aOutputLength, - const uint8_t *aInputHash, - uint16_t aInputHashLength, - const uint8_t *aPrivateKey, - uint16_t aPrivateKeyLength) -{ - Error error = kErrorNone; - mbedtls_pk_context pkCtx; - mbedtls_uecc_keypair *keypair; - uint8_t sig[2 * NUM_ECC_BYTES]; - - mbedtls_pk_init(&pkCtx); - - // Parse a private key in PEM format. - VerifyOrExit(mbedtls_pk_parse_key(&pkCtx, aPrivateKey, aPrivateKeyLength, nullptr, 0) == 0, - error = kErrorInvalidArgs); - VerifyOrExit(mbedtls_pk_get_type(&pkCtx) == MBEDTLS_PK_ECKEY, error = kErrorInvalidArgs); - - keypair = mbedtls_pk_uecc(pkCtx); - OT_ASSERT(keypair != nullptr); - - // Sign using ECDSA. - VerifyOrExit(uECC_sign(keypair->private_key, aInputHash, aInputHashLength, sig) == UECC_SUCCESS, - error = kErrorFailed); - VerifyOrExit(2 * NUM_ECC_BYTES <= aOutputLength, error = kErrorNoBufs); - - // Concatenate the two octet sequences in the order R and then S. - memcpy(aOutput, sig, 2 * NUM_ECC_BYTES); - aOutputLength = 2 * NUM_ECC_BYTES; - -exit: - mbedtls_pk_free(&pkCtx); - - return error; -} - -} // namespace Ecdsa -} // namespace Crypto -} // namespace ot - -#endif // MBEDTLS_USE_TINYCRYPT -#endif // OPENTHREAD_CONFIG_ECDSA_ENABLE diff --git a/src/core/crypto/hkdf_sha256.cpp b/src/core/crypto/hkdf_sha256.cpp index 7c195e69e4c..16ca2085d61 100644 --- a/src/core/crypto/hkdf_sha256.cpp +++ b/src/core/crypto/hkdf_sha256.cpp @@ -50,10 +50,7 @@ HkdfSha256::HkdfSha256(void) SuccessOrAssert(otPlatCryptoHkdfInit(&mContext)); } -HkdfSha256::~HkdfSha256(void) -{ - SuccessOrAssert(otPlatCryptoHkdfDeinit(&mContext)); -} +HkdfSha256::~HkdfSha256(void) { SuccessOrAssert(otPlatCryptoHkdfDeinit(&mContext)); } void HkdfSha256::Extract(const uint8_t *aSalt, uint16_t aSaltLength, const Key &aInputKey) { diff --git a/src/core/crypto/hmac_sha256.cpp b/src/core/crypto/hmac_sha256.cpp index 2cedbac20d2..0f49da04988 100644 --- a/src/core/crypto/hmac_sha256.cpp +++ b/src/core/crypto/hmac_sha256.cpp @@ -47,15 +47,9 @@ HmacSha256::HmacSha256(void) SuccessOrAssert(otPlatCryptoHmacSha256Init(&mContext)); } -HmacSha256::~HmacSha256(void) -{ - SuccessOrAssert(otPlatCryptoHmacSha256Deinit(&mContext)); -} +HmacSha256::~HmacSha256(void) { SuccessOrAssert(otPlatCryptoHmacSha256Deinit(&mContext)); } -void HmacSha256::Start(const Key &aKey) -{ - SuccessOrAssert(otPlatCryptoHmacSha256Start(&mContext, &aKey)); -} +void HmacSha256::Start(const Key &aKey) { SuccessOrAssert(otPlatCryptoHmacSha256Start(&mContext, &aKey)); } void HmacSha256::Update(const void *aBuf, uint16_t aBufLength) { diff --git a/src/core/crypto/pbkdf2_cmac.cpp b/src/core/crypto/pbkdf2_cmac.cpp deleted file mode 100644 index 988a5770650..00000000000 --- a/src/core/crypto/pbkdf2_cmac.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2016, 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 PBKDF2 using AES-CMAC-PRF-128 - */ - -#include "pbkdf2_cmac.hpp" - -#include -#include - -#include "common/debug.hpp" - -namespace ot { -namespace Crypto { -namespace Pbkdf2 { - -#if OPENTHREAD_FTD - -void GenerateKey(const uint8_t *aPassword, - uint16_t aPasswordLen, - const uint8_t *aSalt, - uint16_t aSaltLen, - uint32_t aIterationCounter, - uint16_t aKeyLen, - uint8_t * aKey) -{ - const size_t kBlockSize = MBEDTLS_CIPHER_BLKSIZE_MAX; - uint8_t prfInput[kMaxSaltLength + 4]; // Salt || INT(), for U1 calculation - long prfOne[kBlockSize / sizeof(long)]; - long prfTwo[kBlockSize / sizeof(long)]; - long keyBlock[kBlockSize / sizeof(long)]; - uint32_t blockCounter = 0; - uint8_t * key = aKey; - uint16_t keyLen = aKeyLen; - uint16_t useLen = 0; - - memcpy(prfInput, aSalt, aSaltLen); - OT_ASSERT(aIterationCounter % 2 == 0); - aIterationCounter /= 2; - -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - // limit iterations to avoid OSS-Fuzz timeouts - aIterationCounter = 2; -#endif - - while (keyLen) - { - ++blockCounter; - prfInput[aSaltLen + 0] = static_cast(blockCounter >> 24); - prfInput[aSaltLen + 1] = static_cast(blockCounter >> 16); - prfInput[aSaltLen + 2] = static_cast(blockCounter >> 8); - prfInput[aSaltLen + 3] = static_cast(blockCounter); - - // Calculate U_1 - mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, prfInput, aSaltLen + 4, - reinterpret_cast(keyBlock)); - - // Calculate U_2 - mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast(keyBlock), kBlockSize, - reinterpret_cast(prfOne)); - - for (uint32_t j = 0; j < kBlockSize / sizeof(long); ++j) - { - keyBlock[j] ^= prfOne[j]; - } - - for (uint32_t i = 1; i < aIterationCounter; ++i) - { - // Calculate U_{2 * i - 1} - mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast(prfOne), kBlockSize, - reinterpret_cast(prfTwo)); - // Calculate U_{2 * i} - mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast(prfTwo), kBlockSize, - reinterpret_cast(prfOne)); - - for (uint32_t j = 0; j < kBlockSize / sizeof(long); ++j) - { - keyBlock[j] ^= prfOne[j] ^ prfTwo[j]; - } - } - - useLen = (keyLen < kBlockSize) ? keyLen : kBlockSize; - memcpy(key, keyBlock, useLen); - key += useLen; - keyLen -= useLen; - } -} - -#endif // OPENTHREAD_FTD - -} // namespace Pbkdf2 -} // namespace Crypto -} // namespace ot diff --git a/src/core/crypto/sha256.cpp b/src/core/crypto/sha256.cpp index b649994992c..c4c5225da87 100644 --- a/src/core/crypto/sha256.cpp +++ b/src/core/crypto/sha256.cpp @@ -47,15 +47,9 @@ Sha256::Sha256(void) SuccessOrAssert(otPlatCryptoSha256Init(&mContext)); } -Sha256::~Sha256(void) -{ - SuccessOrAssert(otPlatCryptoSha256Deinit(&mContext)); -} +Sha256::~Sha256(void) { SuccessOrAssert(otPlatCryptoSha256Deinit(&mContext)); } -void Sha256::Start(void) -{ - SuccessOrAssert(otPlatCryptoSha256Start(&mContext)); -} +void Sha256::Start(void) { SuccessOrAssert(otPlatCryptoSha256Start(&mContext)); } void Sha256::Update(const void *aBuf, uint16_t aBufLength) { @@ -75,10 +69,7 @@ void Sha256::Update(const Message &aMessage, uint16_t aOffset, uint16_t aLength) } } -void Sha256::Finish(Hash &aHash) -{ - SuccessOrAssert(otPlatCryptoSha256Finish(&mContext, aHash.m8, Hash::kSize)); -} +void Sha256::Finish(Hash &aHash) { SuccessOrAssert(otPlatCryptoSha256Finish(&mContext, aHash.m8, Hash::kSize)); } } // namespace Crypto } // namespace ot diff --git a/src/core/crypto/storage.cpp b/src/core/crypto/storage.cpp index b9301fc35c2..726daa930a3 100644 --- a/src/core/crypto/storage.cpp +++ b/src/core/crypto/storage.cpp @@ -41,7 +41,7 @@ namespace Crypto { #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE Error Key::ExtractKey(uint8_t *aKeyBuffer, uint16_t &aKeyLength) const { - Error error; + Error error = kErrorNone; size_t readKeyLength; OT_ASSERT(IsKeyRef()); @@ -55,6 +55,17 @@ Error Key::ExtractKey(uint8_t *aKeyBuffer, uint16_t &aKeyLength) const exit: return error; } + +void Storage::DestroyPersistentKeys(void) +{ + DestroyKey(kNetworkKeyRef); + DestroyKey(kPskcRef); + DestroyKey(kActiveDatasetNetworkKeyRef); + DestroyKey(kActiveDatasetPskcRef); + DestroyKey(kPendingDatasetNetworkKeyRef); + DestroyKey(kPendingDatasetPskcRef); + DestroyKey(kEcdsaRef); +} #endif LiteralKey::LiteralKey(const Key &aKey) diff --git a/src/core/crypto/storage.hpp b/src/core/crypto/storage.hpp index 725ab27553a..8193943fb6a 100644 --- a/src/core/crypto/storage.hpp +++ b/src/core/crypto/storage.hpp @@ -57,9 +57,10 @@ namespace Storage { */ enum KeyType : uint8_t { - kKeyTypeRaw = OT_CRYPTO_KEY_TYPE_RAW, ///< Key Type: Raw Data. - kKeyTypeAes = OT_CRYPTO_KEY_TYPE_AES, ///< Key Type: AES. - kKeyTypeHmac = OT_CRYPTO_KEY_TYPE_HMAC, ///< Key Type: HMAC. + kKeyTypeRaw = OT_CRYPTO_KEY_TYPE_RAW, ///< Key Type: Raw Data. + kKeyTypeAes = OT_CRYPTO_KEY_TYPE_AES, ///< Key Type: AES. + kKeyTypeHmac = OT_CRYPTO_KEY_TYPE_HMAC, ///< Key Type: HMAC. + kKeyTypeEcdsa = OT_CRYPTO_KEY_TYPE_ECDSA, ///< Key Type: ECDSA. }; /** @@ -71,13 +72,15 @@ enum KeyAlgorithm : uint8_t kKeyAlgorithmVendor = OT_CRYPTO_KEY_ALG_VENDOR, ///< Key Algorithm: Vendor Defined. kKeyAlgorithmAesEcb = OT_CRYPTO_KEY_ALG_AES_ECB, ///< Key Algorithm: AES ECB. kKeyAlgorithmHmacSha256 = OT_CRYPTO_KEY_ALG_HMAC_SHA_256, ///< Key Algorithm: HMAC SHA-256. + kKeyAlgorithmEcdsa = OT_CRYPTO_KEY_ALG_ECDSA, ///< Key Algorithm: ECDSA. }; -constexpr uint8_t kUsageNone = OT_CRYPTO_KEY_USAGE_NONE; ///< Key Usage: Key Usage is empty. -constexpr uint8_t kUsageExport = OT_CRYPTO_KEY_USAGE_EXPORT; ///< Key Usage: Key can be exported. -constexpr uint8_t kUsageEncrypt = OT_CRYPTO_KEY_USAGE_ENCRYPT; ///< Key Usage: Encrypt (vendor defined). -constexpr uint8_t kUsageDecrypt = OT_CRYPTO_KEY_USAGE_DECRYPT; ///< Key Usage: AES ECB. -constexpr uint8_t kUsageSignHash = OT_CRYPTO_KEY_USAGE_SIGN_HASH; ///< Key Usage: HMAC SHA-256. +constexpr uint8_t kUsageNone = OT_CRYPTO_KEY_USAGE_NONE; ///< Key Usage: Key Usage is empty. +constexpr uint8_t kUsageExport = OT_CRYPTO_KEY_USAGE_EXPORT; ///< Key Usage: Key can be exported. +constexpr uint8_t kUsageEncrypt = OT_CRYPTO_KEY_USAGE_ENCRYPT; ///< Key Usage: Encrypt (vendor defined). +constexpr uint8_t kUsageDecrypt = OT_CRYPTO_KEY_USAGE_DECRYPT; ///< Key Usage: AES ECB. +constexpr uint8_t kUsageSignHash = OT_CRYPTO_KEY_USAGE_SIGN_HASH; ///< Key Usage: Sign Hash. +constexpr uint8_t kUsageVerifyHash = OT_CRYPTO_KEY_USAGE_VERIFY_HASH; ///< Key Usage: Verify Hash. /** * This enumeration defines the key storage types. @@ -102,6 +105,7 @@ constexpr KeyRef kActiveDatasetNetworkKeyRef = OPENTHREAD_CONFIG_PSA_ITS_NVM_OF constexpr KeyRef kActiveDatasetPskcRef = OPENTHREAD_CONFIG_PSA_ITS_NVM_OFFSET + 4; constexpr KeyRef kPendingDatasetNetworkKeyRef = OPENTHREAD_CONFIG_PSA_ITS_NVM_OFFSET + 5; constexpr KeyRef kPendingDatasetPskcRef = OPENTHREAD_CONFIG_PSA_ITS_NVM_OFFSET + 6; +constexpr KeyRef kEcdsaRef = OPENTHREAD_CONFIG_PSA_ITS_NVM_OFFSET + 7; /** * Determine if a given `KeyRef` is valid or not. @@ -112,10 +116,7 @@ constexpr KeyRef kPendingDatasetPskcRef = OPENTHREAD_CONFIG_PSA_ITS_NVM_OF * @retval FALSE If @p aKeyRef is not valid. * */ -inline bool IsKeyRefValid(KeyRef aKeyRef) -{ - return (aKeyRef < kInvalidKeyRef); -} +inline bool IsKeyRefValid(KeyRef aKeyRef) { return (aKeyRef < kInvalidKeyRef); } /** * Import a key into PSA ITS. @@ -133,7 +134,7 @@ inline bool IsKeyRefValid(KeyRef aKeyRef) * @retval kErrorInvalidArgs @p aKey was set to `nullptr`. * */ -inline Error ImportKey(KeyRef & aKeyRef, +inline Error ImportKey(KeyRef &aKeyRef, KeyType aKeyType, KeyAlgorithm aKeyAlgorithm, int aKeyUsage, @@ -187,10 +188,13 @@ inline void DestroyKey(KeyRef aKeyRef) * @retval false Key Ref passed is invalid and has no key associated in PSA. * */ -inline bool HasKey(KeyRef aKeyRef) -{ - return otPlatCryptoHasKey(aKeyRef); -} +inline bool HasKey(KeyRef aKeyRef) { return otPlatCryptoHasKey(aKeyRef); } + +/** + * Delete all the persistent keys stored in PSA ITS. + * + */ +void DestroyPersistentKeys(void); } // namespace Storage @@ -210,7 +214,7 @@ class Key : public otCryptoKey, public Clearable * This method sets the `Key` as a literal key from a given byte array and length. * * @param[in] aKeyBytes A pointer to buffer containing the key. - * @param[in] aKeyLength The key length (number of bytes in @p akeyBytes). + * @param[in] aKeyLength The key length (number of bytes in @p aKeyBytes). * */ void Set(const uint8_t *aKeyBytes, uint16_t aKeyLength) diff --git a/src/core/diags/README.md b/src/core/diags/README.md index 090aa03762b..2ddb658ac15 100644 --- a/src/core/diags/README.md +++ b/src/core/diags/README.md @@ -9,11 +9,16 @@ The diagnostics module supports common diagnostics features that are listed belo - [diag](#diag) - [diag start](#diag-start) - [diag channel](#diag-channel) +- [diag cw](#diag-cw-start) +- [diag stream](#diag-stream-start) - [diag power](#diag-power) +- [diag powersettings](#diag-powersettings) - [diag send](#diag-send-packets-length) - [diag repeat](#diag-repeat-delay-length) - [diag radio](#diag-radio-sleep) +- [diag rawpowersetting](#diag-rawpowersetting) - [diag stats](#diag-stats) +- [diag gpio](#diag-gpio-get-gpio) - [diag stop](#diag-stop) ### diag @@ -54,6 +59,42 @@ set channel to 11 status 0x00 ``` +### diag cw start + +Start transmitting continuous carrier wave. + +```bash +> diag cw start +Done +``` + +### diag cw stop + +Stop transmitting continuous carrier wave. + +```bash +> diag cw stop +Done +``` + +### diag stream start + +Start transmitting a stream of characters. + +```bash +> diag stream start +Done +``` + +### diag stream stop + +Stop transmitting a stream of characters. + +```bash +> diag stream stop +Done +``` + ### diag power Get the tx power value(dBm) for diagnostics module. @@ -73,6 +114,35 @@ set tx power to -10 dBm status 0x00 ``` +### diag powersettings + +Show the currently used power settings table. + +- Note: The unit of `TargetPower` and `ActualPower` is 0.01dBm. + +```bash +> diag powersettings +| StartCh | EndCh | TargetPower | ActualPower | RawPowerSetting | ++---------+-------+-------------+-------------+-----------------+ +| 11 | 14 | 1700 | 1000 | 223344 | +| 15 | 24 | 2000 | 1900 | 112233 | +| 25 | 25 | 1600 | 1000 | 223344 | +| 26 | 26 | 1600 | 1500 | 334455 | +Done +``` + +### diag powersettings \ + +Show the currently used power settings for the given channel. + +```bash +> diag powersettings 11 +TargetPower(0.01dBm): 1700 +ActualPower(0.01dBm): 1000 +RawPowerSetting: 223344 +Done +``` + ### diag send \ \ Transmit a fixed number of packets with fixed length. @@ -136,6 +206,43 @@ Return the state of the radio. sleep ``` +### diag rawpowersetting + +Show the raw power setting for diagnostics module. + +```bash +> diag rawpowersetting +112233 +Done +``` + +### diag rawpowersetting \ + +Set the raw power setting for diagnostics module. + +```bash +> diag rawpowersetting 112233 +Done +``` + +### diag rawpowersetting enable + +Enable the platform layer to use the value set by the command `diag rawpowersetting \`. + +```bash +> diag rawpowersetting enable +Done +``` + +### diag rawpowersetting disable + +Disable the platform layer to use the value set by the command `diag rawpowersetting \`. + +```bash +> diag rawpowersetting disable +Done +``` + ### diag stats Print statistics during diagnostics mode. @@ -157,6 +264,55 @@ Clear statistics during diagnostics mode. stats cleared ``` +### diag gpio get \ + +Get the gpio value. + +```bash +> diag gpio get 0 +1 +Done +``` + +### diag gpio set \ \ + +Set the gpio value. + +The parameter `value` has to be `0` or `1`. + +```bash +> diag gpio set 0 1 +Done +``` + +### diag gpio mode \ + +Get the gpio mode. + +```bash +> diag gpio mode 1 +in +Done +``` + +### diag gpio mode \ in + +Sets the given gpio to the input mode without pull resistor. + +```bash +> diag gpio mode 1 in +Done +``` + +### diag gpio mode \ out + +Sets the given gpio to the output mode. + +```bash +> diag gpio mode 1 out +Done +``` + ### diag stop Stop diagnostics mode and print statistics. diff --git a/src/core/diags/factory_diags.cpp b/src/core/diags/factory_diags.cpp index 6410421f764..5eed151f394 100644 --- a/src/core/diags/factory_diags.cpp +++ b/src/core/diags/factory_diags.cpp @@ -51,8 +51,8 @@ OT_TOOL_WEAK otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, - char * aArgs[], - char * aOutput, + char *aArgs[], + char *aOutput, size_t aOutputMaxLen) { OT_UNUSED_VARIABLE(aArgsLength); @@ -70,8 +70,16 @@ namespace FactoryDiags { #if OPENTHREAD_RADIO && !OPENTHREAD_RADIO_CLI const struct Diags::Command Diags::sCommands[] = { - {"channel", &Diags::ProcessChannel}, {"echo", &Diags::ProcessEcho}, {"power", &Diags::ProcessPower}, - {"start", &Diags::ProcessStart}, {"stop", &Diags::ProcessStop}, + {"channel", &Diags::ProcessChannel}, + {"cw", &Diags::ProcessContinuousWave}, + {"echo", &Diags::ProcessEcho}, + {"gpio", &Diags::ProcessGpio}, + {"power", &Diags::ProcessPower}, + {"powersettings", &Diags::ProcessPowerSettings}, + {"rawpowersetting", &Diags::ProcessRawPowerSetting}, + {"start", &Diags::ProcessStart}, + {"stop", &Diags::ProcessStop}, + {"stream", &Diags::ProcessStream}, }; Diags::Diags(Instance &aInstance) @@ -129,8 +137,7 @@ Error Diags::ProcessEcho(uint8_t aArgsLength, char *aArgs[], char *aOutput, size uint32_t number; SuccessOrExit(error = ParseLong(aArgs[1], value)); - number = static_cast(value); - number = (number < outputMaxLen) ? number : outputMaxLen; + number = Min(static_cast(value), outputMaxLen); for (i = 0; i < number; i++) { @@ -173,17 +180,24 @@ Error Diags::ProcessStop(uint8_t aArgsLength, char *aArgs[], char *aOutput, size return kErrorNone; } -extern "C" void otPlatDiagAlarmFired(otInstance *aInstance) -{ - otPlatDiagAlarmCallback(aInstance); -} +extern "C" void otPlatDiagAlarmFired(otInstance *aInstance) { otPlatDiagAlarmCallback(aInstance); } #else // OPENTHREAD_RADIO && !OPENTHREAD_RADIO_CLI // For OPENTHREAD_FTD, OPENTHREAD_MTD, OPENTHREAD_RADIO_CLI const struct Diags::Command Diags::sCommands[] = { - {"channel", &Diags::ProcessChannel}, {"power", &Diags::ProcessPower}, {"radio", &Diags::ProcessRadio}, - {"repeat", &Diags::ProcessRepeat}, {"send", &Diags::ProcessSend}, {"start", &Diags::ProcessStart}, - {"stats", &Diags::ProcessStats}, {"stop", &Diags::ProcessStop}, + {"channel", &Diags::ProcessChannel}, + {"cw", &Diags::ProcessContinuousWave}, + {"gpio", &Diags::ProcessGpio}, + {"power", &Diags::ProcessPower}, + {"powersettings", &Diags::ProcessPowerSettings}, + {"rawpowersetting", &Diags::ProcessRawPowerSetting}, + {"radio", &Diags::ProcessRadio}, + {"repeat", &Diags::ProcessRepeat}, + {"send", &Diags::ProcessSend}, + {"start", &Diags::ProcessStart}, + {"stats", &Diags::ProcessStats}, + {"stop", &Diags::ProcessStop}, + {"stream", &Diags::ProcessStream}, }; Diags::Diags(Instance &aInstance) @@ -475,10 +489,7 @@ Error Diags::ProcessRadio(uint8_t aArgsLength, char *aArgs[], char *aOutput, siz return error; } -extern "C" void otPlatDiagAlarmFired(otInstance *aInstance) -{ - AsCoreType(aInstance).Get().AlarmFired(); -} +extern "C" void otPlatDiagAlarmFired(otInstance *aInstance) { AsCoreType(aInstance).Get().AlarmFired(); } void Diags::AlarmFired(void) { @@ -543,6 +554,212 @@ void Diags::TransmitDone(Error aError) #endif // OPENTHREAD_RADIO +Error Diags::ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +{ + Error error = kErrorInvalidArgs; + + VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState); + VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs); + + if (strcmp(aArgs[0], "start") == 0) + { + SuccessOrExit(error = otPlatDiagRadioTransmitCarrier(&GetInstance(), true)); + } + else if (strcmp(aArgs[0], "stop") == 0) + { + SuccessOrExit(error = otPlatDiagRadioTransmitCarrier(&GetInstance(), false)); + } + +exit: + AppendErrorResult(error, aOutput, aOutputMaxLen); + return error; +} + +Error Diags::ProcessStream(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +{ + Error error = kErrorInvalidArgs; + + VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState); + VerifyOrExit(aArgsLength > 0, error = kErrorInvalidArgs); + + if (strcmp(aArgs[0], "start") == 0) + { + error = otPlatDiagRadioTransmitStream(&GetInstance(), true); + } + else if (strcmp(aArgs[0], "stop") == 0) + { + error = otPlatDiagRadioTransmitStream(&GetInstance(), false); + } + +exit: + AppendErrorResult(error, aOutput, aOutputMaxLen); + return error; +} + +Error Diags::GetPowerSettings(uint8_t aChannel, PowerSettings &aPowerSettings) +{ + aPowerSettings.mRawPowerSetting.mLength = RawPowerSetting::kMaxDataSize; + return otPlatDiagRadioGetPowerSettings(&GetInstance(), aChannel, &aPowerSettings.mTargetPower, + &aPowerSettings.mActualPower, aPowerSettings.mRawPowerSetting.mData, + &aPowerSettings.mRawPowerSetting.mLength); +} + +Error Diags::ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +{ + Error error = kErrorInvalidArgs; + uint8_t channel; + PowerSettings powerSettings; + + VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState); + + if (aArgsLength == 0) + { + bool isPrePowerSettingsValid = false; + uint8_t preChannel = 0; + PowerSettings prePowerSettings; + int n; + + n = snprintf(aOutput, aOutputMaxLen, + "| StartCh | EndCh | TargetPower | ActualPower | RawPowerSetting |\r\n" + "+---------+-------+-------------+-------------+-----------------+\r\n"); + VerifyOrExit((n > 0) && (n < static_cast(aOutputMaxLen)), error = kErrorNoBufs); + aOutput += n; + aOutputMaxLen -= static_cast(n); + + for (channel = Radio::kChannelMin; channel <= Radio::kChannelMax + 1; channel++) + { + error = (channel == Radio::kChannelMax + 1) ? kErrorNotFound : GetPowerSettings(channel, powerSettings); + + if (isPrePowerSettingsValid && ((powerSettings != prePowerSettings) || (error != kErrorNone))) + { + n = snprintf(aOutput, aOutputMaxLen, "| %7u | %5u | %11d | %11d | %15s |\r\n", preChannel, channel - 1, + prePowerSettings.mTargetPower, prePowerSettings.mActualPower, + prePowerSettings.mRawPowerSetting.ToString().AsCString()); + VerifyOrExit((n > 0) && (n < static_cast(aOutputMaxLen)), error = kErrorNoBufs); + aOutput += n; + aOutputMaxLen -= static_cast(n); + isPrePowerSettingsValid = false; + } + + if ((error == kErrorNone) && (!isPrePowerSettingsValid)) + { + preChannel = channel; + prePowerSettings = powerSettings; + isPrePowerSettingsValid = true; + } + } + + error = kErrorNone; + } + else if (aArgsLength == 1) + { + SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(aArgs[0], channel)); + VerifyOrExit(channel >= Radio::kChannelMin && channel <= Radio::kChannelMax, error = kErrorInvalidArgs); + + SuccessOrExit(error = GetPowerSettings(channel, powerSettings)); + snprintf(aOutput, aOutputMaxLen, + "TargetPower(0.01dBm): %d\r\nActualPower(0.01dBm): %d\r\nRawPowerSetting: %s\r\n", + powerSettings.mTargetPower, powerSettings.mActualPower, + powerSettings.mRawPowerSetting.ToString().AsCString()); + } + +exit: + AppendErrorResult(error, aOutput, aOutputMaxLen); + return error; +} + +Error Diags::GetRawPowerSetting(RawPowerSetting &aRawPowerSetting) +{ + aRawPowerSetting.mLength = RawPowerSetting::kMaxDataSize; + return otPlatDiagRadioGetRawPowerSetting(&GetInstance(), aRawPowerSetting.mData, &aRawPowerSetting.mLength); +} + +Error Diags::ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +{ + Error error = kErrorInvalidArgs; + RawPowerSetting setting; + + VerifyOrExit(otPlatDiagModeGet(), error = kErrorInvalidState); + + if (aArgsLength == 0) + { + SuccessOrExit(error = GetRawPowerSetting(setting)); + snprintf(aOutput, aOutputMaxLen, "%s\r\n", setting.ToString().AsCString()); + } + else if (strcmp(aArgs[0], "enable") == 0) + { + SuccessOrExit(error = otPlatDiagRadioRawPowerSettingEnable(&GetInstance(), true)); + } + else if (strcmp(aArgs[0], "disable") == 0) + { + SuccessOrExit(error = otPlatDiagRadioRawPowerSettingEnable(&GetInstance(), false)); + } + else + { + setting.mLength = RawPowerSetting::kMaxDataSize; + SuccessOrExit(error = Utils::CmdLineParser::ParseAsHexString(aArgs[0], setting.mLength, setting.mData)); + SuccessOrExit(error = otPlatDiagRadioSetRawPowerSetting(&GetInstance(), setting.mData, setting.mLength)); + } + +exit: + AppendErrorResult(error, aOutput, aOutputMaxLen); + return error; +} + +Error Diags::ProcessGpio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) +{ + Error error = kErrorInvalidArgs; + long value; + uint32_t gpio; + bool level; + otGpioMode mode; + + if ((aArgsLength == 2) && (strcmp(aArgs[0], "get") == 0)) + { + SuccessOrExit(error = ParseLong(aArgs[1], value)); + gpio = static_cast(value); + SuccessOrExit(error = otPlatDiagGpioGet(gpio, &level)); + snprintf(aOutput, aOutputMaxLen, "%d\r\n", level); + } + else if ((aArgsLength == 3) && (strcmp(aArgs[0], "set") == 0)) + { + SuccessOrExit(error = ParseLong(aArgs[1], value)); + gpio = static_cast(value); + SuccessOrExit(error = ParseBool(aArgs[2], level)); + SuccessOrExit(error = otPlatDiagGpioSet(gpio, level)); + } + else if ((aArgsLength >= 2) && (strcmp(aArgs[0], "mode") == 0)) + { + SuccessOrExit(error = ParseLong(aArgs[1], value)); + gpio = static_cast(value); + + if (aArgsLength == 2) + { + SuccessOrExit(error = otPlatDiagGpioGetMode(gpio, &mode)); + if (mode == OT_GPIO_MODE_INPUT) + { + snprintf(aOutput, aOutputMaxLen, "in\r\n"); + } + else if (mode == OT_GPIO_MODE_OUTPUT) + { + snprintf(aOutput, aOutputMaxLen, "out\r\n"); + } + } + else if ((aArgsLength == 3) && (strcmp(aArgs[2], "in") == 0)) + { + SuccessOrExit(error = otPlatDiagGpioSetMode(gpio, OT_GPIO_MODE_INPUT)); + } + else if ((aArgsLength == 3) && (strcmp(aArgs[2], "out") == 0)) + { + SuccessOrExit(error = otPlatDiagGpioSetMode(gpio, OT_GPIO_MODE_OUTPUT)); + } + } + +exit: + AppendErrorResult(error, aOutput, aOutputMaxLen); + return error; +} + void Diags::AppendErrorResult(Error aError, char *aOutput, size_t aOutputMaxLen) { if (aError != kErrorNone) @@ -558,6 +775,19 @@ Error Diags::ParseLong(char *aString, long &aLong) return (*endptr == '\0') ? kErrorNone : kErrorParse; } +Error Diags::ParseBool(char *aString, bool &aBool) +{ + Error error; + long value; + + SuccessOrExit(error = ParseLong(aString, value)); + VerifyOrExit((value == 0) || (value == 1), error = kErrorParse); + aBool = static_cast(value); + +exit: + return error; +} + Error Diags::ParseCmd(char *aString, uint8_t &aArgsLength, char *aArgs[]) { Error error; @@ -571,13 +801,13 @@ Error Diags::ParseCmd(char *aString, uint8_t &aArgsLength, char *aArgs[]) return error; } -void Diags::ProcessLine(const char *aString, char *aOutput, size_t aOutputMaxLen) +Error Diags::ProcessLine(const char *aString, char *aOutput, size_t aOutputMaxLen) { constexpr uint16_t kMaxCommandBuffer = OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE; Error error = kErrorNone; char buffer[kMaxCommandBuffer]; - char * args[kMaxArgs]; + char *args[kMaxArgs]; uint8_t argCount = 0; VerifyOrExit(StringLength(aString, kMaxCommandBuffer) < kMaxCommandBuffer, error = kErrorNoBufs); @@ -591,7 +821,7 @@ void Diags::ProcessLine(const char *aString, char *aOutput, size_t aOutputMaxLen { case kErrorNone: aOutput[0] = '\0'; // In case there is no output. - IgnoreError(ProcessCmd(argCount, &args[0], aOutput, aOutputMaxLen)); + error = ProcessCmd(argCount, &args[0], aOutput, aOutputMaxLen); break; case kErrorNoBufs: @@ -606,6 +836,8 @@ void Diags::ProcessLine(const char *aString, char *aOutput, size_t aOutputMaxLen snprintf(aOutput, aOutputMaxLen, "failed to parse command string\r\n"); break; } + + return error; } Error Diags::ProcessCmd(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen) @@ -655,12 +887,103 @@ Error Diags::ProcessCmd(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_ return error; } -bool Diags::IsEnabled(void) -{ - return otPlatDiagModeGet(); -} +bool Diags::IsEnabled(void) { return otPlatDiagModeGet(); } } // namespace FactoryDiags } // namespace ot +OT_TOOL_WEAK otError otPlatDiagGpioSet(uint32_t aGpio, bool aValue) +{ + OT_UNUSED_VARIABLE(aGpio); + OT_UNUSED_VARIABLE(aValue); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +OT_TOOL_WEAK otError otPlatDiagGpioGet(uint32_t aGpio, bool *aValue) +{ + OT_UNUSED_VARIABLE(aGpio); + OT_UNUSED_VARIABLE(aValue); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +OT_TOOL_WEAK otError otPlatDiagGpioSetMode(uint32_t aGpio, otGpioMode aMode) +{ + OT_UNUSED_VARIABLE(aGpio); + OT_UNUSED_VARIABLE(aMode); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +OT_TOOL_WEAK otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode) +{ + OT_UNUSED_VARIABLE(aGpio); + OT_UNUSED_VARIABLE(aMode); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +OT_TOOL_WEAK otError otPlatDiagRadioSetRawPowerSetting(otInstance *aInstance, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aRawPowerSetting); + OT_UNUSED_VARIABLE(aRawPowerSettingLength); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +OT_TOOL_WEAK otError otPlatDiagRadioGetRawPowerSetting(otInstance *aInstance, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aRawPowerSetting); + OT_UNUSED_VARIABLE(aRawPowerSettingLength); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +OT_TOOL_WEAK otError otPlatDiagRadioRawPowerSettingEnable(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aEnable); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +OT_TOOL_WEAK otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aEnable); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +OT_TOOL_WEAK otError otPlatDiagRadioTransmitStream(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aEnable); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +OT_TOOL_WEAK otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance, + uint8_t aChannel, + int16_t *aTargetPower, + int16_t *aActualPower, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aChannel); + OT_UNUSED_VARIABLE(aTargetPower); + OT_UNUSED_VARIABLE(aActualPower); + OT_UNUSED_VARIABLE(aRawPowerSetting); + OT_UNUSED_VARIABLE(aRawPowerSettingLength); + + return OT_ERROR_NOT_IMPLEMENTED; +} #endif // OPENTHREAD_CONFIG_DIAG_ENABLE diff --git a/src/core/diags/factory_diags.hpp b/src/core/diags/factory_diags.hpp index 26ccd9e8383..d7ac648f929 100644 --- a/src/core/diags/factory_diags.hpp +++ b/src/core/diags/factory_diags.hpp @@ -45,6 +45,7 @@ #include "common/error.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" +#include "common/string.hpp" namespace ot { namespace FactoryDiags { @@ -68,7 +69,7 @@ class Diags : public InstanceLocator, private NonCopyable * @param[in] aOutputMaxLen The output buffer size. * */ - void ProcessLine(const char *aString, char *aOutput, size_t aOutputMaxLen); + Error ProcessLine(const char *aString, char *aOutput, size_t aOutputMaxLen); /** * This method processes a factory diagnostics command line. @@ -142,23 +143,69 @@ class Diags : public InstanceLocator, private NonCopyable uint8_t mLastLqi; }; + struct RawPowerSetting + { + static constexpr uint16_t kMaxDataSize = OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE; + static constexpr uint16_t kInfoStringSize = kMaxDataSize * 2 + 1; + typedef String InfoString; + + InfoString ToString(void) const + { + InfoString string; + + string.AppendHexBytes(mData, mLength); + + return string; + } + + bool operator!=(const RawPowerSetting &aOther) const + { + return (mLength != aOther.mLength) || (memcmp(mData, aOther.mData, mLength) != 0); + } + + uint8_t mData[kMaxDataSize]; + uint16_t mLength; + }; + + struct PowerSettings + { + bool operator!=(const PowerSettings &aOther) const + { + return (mTargetPower != aOther.mTargetPower) || (mActualPower != aOther.mActualPower) || + (mRawPowerSetting != aOther.mRawPowerSetting); + } + + int16_t mTargetPower; + int16_t mActualPower; + RawPowerSetting mRawPowerSetting; + }; + Error ParseCmd(char *aString, uint8_t &aArgsLength, char *aArgs[]); Error ProcessChannel(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error ProcessContinuousWave(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error ProcessGpio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); Error ProcessPower(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); Error ProcessRadio(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); Error ProcessRepeat(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error ProcessPowerSettings(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error ProcessRawPowerSetting(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); Error ProcessSend(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); Error ProcessStart(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); Error ProcessStats(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); Error ProcessStop(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); + Error ProcessStream(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); #if OPENTHREAD_RADIO && !OPENTHREAD_RADIO_CLI Error ProcessEcho(uint8_t aArgsLength, char *aArgs[], char *aOutput, size_t aOutputMaxLen); #endif + Error GetRawPowerSetting(RawPowerSetting &aRawPowerSetting); + Error GetPowerSettings(uint8_t aChannel, PowerSettings &aPowerSettings); + void TransmitPacket(void); static void AppendErrorResult(Error aError, char *aOutput, size_t aOutputMaxLen); static Error ParseLong(char *aString, long &aLong); + static Error ParseBool(char *aString, bool &aBool); static const struct Command sCommands[]; diff --git a/src/core/ftd.cmake b/src/core/ftd.cmake index 0100a1ffd6b..dd6dae2f49e 100644 --- a/src/core/ftd.cmake +++ b/src/core/ftd.cmake @@ -43,9 +43,8 @@ target_sources(openthread-ftd PRIVATE ${COMMON_SOURCES}) target_link_libraries(openthread-ftd PRIVATE ${OT_MBEDTLS} + ot-config-ftd ot-config ) -if(NOT OT_EXCLUDE_TCPLP_LIB) - target_link_libraries(openthread-ftd PRIVATE tcplp) -endif() +target_link_libraries(openthread-ftd PRIVATE tcplp-ftd) diff --git a/src/core/mac/data_poll_handler.cpp b/src/core/mac/data_poll_handler.cpp index c1d78e3273c..b5033180345 100644 --- a/src/core/mac/data_poll_handler.cpp +++ b/src/core/mac/data_poll_handler.cpp @@ -51,7 +51,7 @@ DataPollHandler::Callbacks::Callbacks(Instance &aInstance) inline Error DataPollHandler::Callbacks::PrepareFrameForChild(Mac::TxFrame &aFrame, FrameContext &aContext, - Child & aChild) + Child &aChild) { return Get().PrepareFrameForChild(aFrame, aContext, aChild); } @@ -59,7 +59,7 @@ inline Error DataPollHandler::Callbacks::PrepareFrameForChild(Mac::TxFrame &aFra inline void DataPollHandler::Callbacks::HandleSentFrameToChild(const Mac::TxFrame &aFrame, const FrameContext &aContext, Error aError, - Child & aChild) + Child &aChild) { Get().HandleSentFrameToChild(aFrame, aContext, aError, aChild); } @@ -128,7 +128,7 @@ void DataPollHandler::RequestFrameChange(FrameChange aChange, Child &aChild) void DataPollHandler::HandleDataPoll(Mac::RxFrame &aFrame) { Mac::Address macSource; - Child * child; + Child *child; uint16_t indirectMsgCount; VerifyOrExit(aFrame.GetSecurityEnabled()); @@ -188,7 +188,11 @@ Mac::TxFrame *DataPollHandler::HandleFrameRequest(Mac::TxFrames &aTxFrames) VerifyOrExit(mCallbacks.PrepareFrameForChild(*frame, mFrameContext, *mIndirectTxChild) == kErrorNone, frame = nullptr); +#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + if ((mIndirectTxChild->GetIndirectTxAttempts() > 0) || (mIndirectTxChild->GetCslTxAttempts() > 0)) +#else if (mIndirectTxChild->GetIndirectTxAttempts() > 0) +#endif { // For a re-transmission of an indirect frame to a sleepy // child, we ensure to use the same frame counter, key id, and @@ -240,6 +244,9 @@ void DataPollHandler::HandleSentFrame(const Mac::TxFrame &aFrame, Error aError, { case kErrorNone: aChild.ResetIndirectTxAttempts(); +#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + aChild.ResetCslTxAttempts(); +#endif aChild.SetFrameReplacePending(false); break; @@ -291,7 +298,6 @@ void DataPollHandler::HandleSentFrame(const Mac::TxFrame &aFrame, Error aError, default: OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); } mCallbacks.HandleSentFrameToChild(aFrame, mFrameContext, aError, aChild); diff --git a/src/core/mac/data_poll_handler.hpp b/src/core/mac/data_poll_handler.hpp index 37532c49cf0..11cb531bbeb 100644 --- a/src/core/mac/data_poll_handler.hpp +++ b/src/core/mac/data_poll_handler.hpp @@ -191,7 +191,7 @@ class DataPollHandler : public InstanceLocator, private NonCopyable void HandleSentFrameToChild(const Mac::TxFrame &aFrame, const FrameContext &aContext, Error aError, - Child & aChild); + Child &aChild); /** * This callback method notifies that a requested frame change from `RequestFrameChange()` is processed. @@ -282,7 +282,7 @@ class DataPollHandler : public InstanceLocator, private NonCopyable // indicates no active indirect tx). `mFrameContext` tracks the // context for the prepared frame for the current indirect tx. - Child * mIndirectTxChild; + Child *mIndirectTxChild; Callbacks::FrameContext mFrameContext; Callbacks mCallbacks; }; diff --git a/src/core/mac/data_poll_sender.cpp b/src/core/mac/data_poll_sender.cpp index 2c5b0899f4b..acc968c5c9f 100644 --- a/src/core/mac/data_poll_sender.cpp +++ b/src/core/mac/data_poll_sender.cpp @@ -38,6 +38,7 @@ #include "common/locator_getters.hpp" #include "common/log.hpp" #include "common/message.hpp" +#include "common/num_utils.hpp" #include "net/ip6.hpp" #include "net/netif.hpp" #include "thread/mesh_forwarder.hpp" @@ -54,7 +55,7 @@ DataPollSender::DataPollSender(Instance &aInstance) , mPollPeriod(0) , mExternalPollPeriod(0) , mFastPollsUsers(0) - , mTimer(aInstance, DataPollSender::HandlePollTimer) + , mTimer(aInstance) , mEnabled(false) , mAttachMode(false) , mRetxMode(false) @@ -197,7 +198,7 @@ uint32_t DataPollSender::GetKeepAlivePollPeriod(void) const if (mExternalPollPeriod != 0) { - period = OT_MIN(period, mExternalPollPeriod); + period = Min(period, mExternalPollPeriod); } return period; @@ -506,22 +507,29 @@ uint32_t DataPollSender::CalculatePollPeriod(void) const if (mAttachMode) { - period = OT_MIN(period, kAttachDataPollPeriod); + period = Min(period, kAttachDataPollPeriod); } if (mRetxMode) { - period = OT_MIN(period, kRetxPollPeriod); + period = Min(period, kRetxPollPeriod); + +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + if (Get().GetCslPeriodMs() > 0) + { + period = Min(period, Get().GetCslPeriodMs()); + } +#endif } if (mRemainingFastPolls != 0) { - period = OT_MIN(period, kFastPollPeriod); + period = Min(period, kFastPollPeriod); } if (mExternalPollPeriod != 0) { - period = OT_MIN(period, mExternalPollPeriod); + period = Min(period, mExternalPollPeriod); } if (period == 0) @@ -532,20 +540,17 @@ uint32_t DataPollSender::CalculatePollPeriod(void) const return period; } -void DataPollSender::HandlePollTimer(Timer &aTimer) -{ - IgnoreError(aTimer.Get().SendDataPoll()); -} - uint32_t DataPollSender::GetDefaultPollPeriod(void) const { - uint32_t period = Time::SecToMsec(Get().GetTimeout()); uint32_t pollAhead = static_cast(kRetxPollPeriod) * kMaxPollRetxAttempts; + uint32_t period; + + period = Time::SecToMsec(Min(Get().GetTimeout(), Time::MsecToSec(TimerMilli::kMaxDelay))); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE && OPENTHREAD_CONFIG_MAC_CSL_AUTO_SYNC_ENABLE if (Get().IsCslEnabled()) { - period = OT_MIN(period, Time::SecToMsec(Get().GetCslTimeout())); + period = Min(period, Time::SecToMsec(Get().GetCslTimeout())); pollAhead = static_cast(kRetxPollPeriod); } #endif @@ -560,66 +565,41 @@ uint32_t DataPollSender::GetDefaultPollPeriod(void) const Mac::TxFrame *DataPollSender::PrepareDataRequest(Mac::TxFrames &aTxFrames) { - Mac::TxFrame *frame = nullptr; - Mac::Address src, dst; - uint16_t fcf; - bool iePresent; + Mac::TxFrame *frame = nullptr; + Mac::Addresses addresses; + Mac::PanIds panIds; #if OPENTHREAD_CONFIG_MULTI_RADIO Mac::RadioType radio; - SuccessOrExit(GetPollDestinationAddress(dst, radio)); + SuccessOrExit(GetPollDestinationAddress(addresses.mDestination, radio)); frame = &aTxFrames.GetTxFrame(radio); #else - SuccessOrExit(GetPollDestinationAddress(dst)); + SuccessOrExit(GetPollDestinationAddress(addresses.mDestination)); frame = &aTxFrames.GetTxFrame(); #endif - fcf = Mac::Frame::kFcfFrameMacCmd | Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfAckRequest | - Mac::Frame::kFcfSecurityEnabled; - - iePresent = Get().CalcIePresent(nullptr); - - if (iePresent) + if (addresses.mDestination.IsExtended()) { - fcf |= Mac::Frame::kFcfIePresent; - } - - fcf |= Get().CalcFrameVersion(Get().FindNeighbor(dst), iePresent); - - if (dst.IsExtended()) - { - fcf |= Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrExt; - src.SetExtended(Get().GetExtAddress()); + addresses.mSource.SetExtended(Get().GetExtAddress()); } else { - fcf |= Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort; - src.SetShort(Get().GetShortAddress()); + addresses.mSource.SetShort(Get().GetShortAddress()); } - frame->InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32); + panIds.mSource = Get().GetPanId(); + panIds.mDestination = Get().GetPanId(); - if (frame->IsDstPanIdPresent()) - { - frame->SetDstPanId(Get().GetPanId()); - } + Get().PrepareMacHeaders(*frame, Mac::Frame::kTypeMacCmd, addresses, panIds, + Mac::Frame::kSecurityEncMic32, Mac::Frame::kKeyIdMode1, nullptr); - frame->SetSrcAddr(src); - frame->SetDstAddr(dst); -#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT - if (iePresent) - { - Get().AppendHeaderIe(nullptr, *frame); - } - -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE +#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE if (frame->GetHeaderIe(Mac::CslIe::kHeaderIeId) != nullptr) { // Disable frame retransmission when the data poll has CSL IE included aTxFrames.SetMaxFrameRetries(0); } -#endif #endif IgnoreError(frame->SetCommandId(Mac::Frame::kMacCmdDataRequest)); diff --git a/src/core/mac/data_poll_sender.hpp b/src/core/mac/data_poll_sender.hpp index a30f56362a2..2327bd9ad92 100644 --- a/src/core/mac/data_poll_sender.hpp +++ b/src/core/mac/data_poll_sender.hpp @@ -276,24 +276,26 @@ class DataPollSender : public InstanceLocator, private NonCopyable void ScheduleNextPoll(PollPeriodSelector aPollPeriodSelector); uint32_t CalculatePollPeriod(void) const; const Neighbor &GetParent(void) const; - static void HandlePollTimer(Timer &aTimer); + void HandlePollTimer(void) { IgnoreError(SendDataPoll()); } #if OPENTHREAD_CONFIG_MULTI_RADIO Error GetPollDestinationAddress(Mac::Address &aDest, Mac::RadioType &aRadioType) const; #else Error GetPollDestinationAddress(Mac::Address &aDest) const; #endif + using PollTimer = TimerMilliIn; + TimeMilli mTimerStartTime; uint32_t mPollPeriod; uint32_t mExternalPollPeriod : 26; // In milliseconds. uint8_t mFastPollsUsers : 6; // Number of callers which request fast polls. - TimerMilli mTimer; + PollTimer mTimer; bool mEnabled : 1; // Indicates whether data polling is enabled/started. bool mAttachMode : 1; // Indicates whether in attach mode (to use attach poll period). bool mRetxMode : 1; // Indicates whether last poll tx failed at mac/radio layer (poll retx mode). - uint8_t mPollTimeoutCounter : 4; // Poll timeouts counter (0 to `kQuickPollsAfterTimout`). + uint8_t mPollTimeoutCounter : 4; // Poll timeouts counter (0 to `kQuickPollsAfterTimeout`). uint8_t mPollTxFailureCounter : 4; // Poll tx failure counter (0 to `kMaxPollRetxAttempts`). uint8_t mRemainingFastPolls : 4; // Number of remaining fast polls when in transient fast polling mode. }; diff --git a/src/core/mac/link_raw.cpp b/src/core/mac/link_raw.cpp index bbce9f2acab..ca87f414700 100644 --- a/src/core/mac/link_raw.cpp +++ b/src/core/mac/link_raw.cpp @@ -258,12 +258,12 @@ Error LinkRaw::SetMacKey(uint8_t aKeyIdMode, return error; } -Error LinkRaw::SetMacFrameCounter(uint32_t aMacFrameCounter) +Error LinkRaw::SetMacFrameCounter(uint32_t aFrameCounter, bool aSetIfLarger) { Error error = kErrorNone; VerifyOrExit(IsEnabled(), error = kErrorInvalidState); - mSubMac.SetFrameCounter(aMacFrameCounter); + mSubMac.SetFrameCounter(aFrameCounter, aSetIfLarger); exit: return error; @@ -274,7 +274,7 @@ Error LinkRaw::SetMacFrameCounter(uint32_t aMacFrameCounter) #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) void LinkRaw::RecordFrameTransmitStatus(const TxFrame &aFrame, - const RxFrame *aAckFrame, + RxFrame *aAckFrame, Error aError, uint8_t aRetryCount, bool aWillRetx) diff --git a/src/core/mac/link_raw.hpp b/src/core/mac/link_raw.hpp index e0a66c99ff7..d630ebe6370 100644 --- a/src/core/mac/link_raw.hpp +++ b/src/core/mac/link_raw.hpp @@ -271,13 +271,15 @@ class LinkRaw : public InstanceLocator, private NonCopyable /** * This method sets the current MAC frame counter value. * - * @param[in] aMacFrameCounter The MAC frame counter value. + * @param[in] aFrameCounter The MAC frame counter value. + * @param[in] aSetIfLarger If `true`, set only if the new value @p aFrameCounter is larger than current value. + * If `false`, set the new value independent of the current value. * * @retval kErrorNone If successful. * @retval kErrorInvalidState If the raw link-layer isn't enabled. * */ - Error SetMacFrameCounter(uint32_t aMacFrameCounter); + Error SetMacFrameCounter(uint32_t aFrameCounter, bool aSetIfLarger); #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) /** @@ -298,12 +300,12 @@ class LinkRaw : public InstanceLocator, private NonCopyable * */ void RecordFrameTransmitStatus(const TxFrame &aFrame, - const RxFrame *aAckFrame, + RxFrame *aAckFrame, Error aError, uint8_t aRetryCount, bool aWillRetx); #else - void RecordFrameTransmitStatus(const TxFrame &, const RxFrame *, Error, uint8_t, bool) {} + void RecordFrameTransmitStatus(const TxFrame &, RxFrame *, Error, uint8_t, bool) {} #endif private: diff --git a/src/core/mac/mac.cpp b/src/core/mac/mac.cpp index 8ad0518fe22..a29ee3fc534 100644 --- a/src/core/mac/mac.cpp +++ b/src/core/mac/mac.cpp @@ -92,16 +92,16 @@ Mac::Mac(Instance &aInstance) #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE , mCslTxFireTime(TimeMilli::kMaxDuration) #endif +#endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE , mCslChannel(0) , mCslPeriod(0) -#endif #endif , mActiveScanHandler(nullptr) // Initialize `mActiveScanHandler` and `mEnergyScanHandler` union , mScanHandlerContext(nullptr) , mLinks(aInstance) - , mOperationTask(aInstance, Mac::HandleOperationTask) - , mTimer(aInstance, Mac::HandleTimer) + , mOperationTask(aInstance) + , mTimer(aInstance) , mKeyIdMode2FrameCounter(0) , mCcaSampleCount(0) #if OPENTHREAD_CONFIG_MULTI_RADIO @@ -216,7 +216,7 @@ Error Mac::ConvertBeaconToActiveScanResult(const RxFrame *aBeaconFrame, ActiveSc Address address; #if OPENTHREAD_CONFIG_MAC_BEACON_PAYLOAD_PARSING_ENABLE const BeaconPayload *beaconPayload = nullptr; - const Beacon * beacon = nullptr; + const Beacon *beacon = nullptr; uint16_t payloadLength; #endif @@ -224,7 +224,7 @@ Error Mac::ConvertBeaconToActiveScanResult(const RxFrame *aBeaconFrame, ActiveSc VerifyOrExit(aBeaconFrame != nullptr, error = kErrorInvalidArgs); - VerifyOrExit(aBeaconFrame->GetType() == Frame::kFcfFrameBeacon, error = kErrorParse); + VerifyOrExit(aBeaconFrame->GetType() == Frame::kTypeBeacon, error = kErrorParse); SuccessOrExit(error = aBeaconFrame->GetSrcAddr(address)); VerifyOrExit(address.IsExtended(), error = kErrorParse); aResult.mExtAddress = address.GetExtended(); @@ -348,7 +348,7 @@ void Mac::ReportEnergyScanResult(int8_t aRssi) { EnergyScanResult result; - VerifyOrExit((mEnergyScanHandler != nullptr) && (aRssi != kInvalidRssiValue)); + VerifyOrExit((mEnergyScanHandler != nullptr) && (aRssi != Radio::kInvalidRssi)); result.mChannel = mScanChannel; result.mMaxRssi = aRssi; @@ -571,17 +571,14 @@ void Mac::UpdateIdleMode(void) else { mLinks.Receive(mRadioChannel); - LogDebg("Idle mode: Radio receiving on channel %d", mRadioChannel); + LogDebg("Idle mode: Radio receiving on channel %u", mRadioChannel); } exit: return; } -bool Mac::IsActiveOrPending(Operation aOperation) const -{ - return (mOperation == aOperation) || IsPending(aOperation); -} +bool Mac::IsActiveOrPending(Operation aOperation) const { return (mOperation == aOperation) || IsPending(aOperation); } void Mac::StartOperation(Operation aOperation) { @@ -608,11 +605,6 @@ void Mac::StartOperation(Operation aOperation) } } -void Mac::HandleOperationTask(Tasklet &aTasklet) -{ - aTasklet.Get().PerformNextOperation(); -} - void Mac::PerformNextOperation(void) { VerifyOrExit(mOperation == kOperationIdle); @@ -727,12 +719,16 @@ void Mac::FinishOperation(void) TxFrame *Mac::PrepareBeaconRequest(void) { - TxFrame &frame = mLinks.GetTxFrames().GetBroadcastTxFrame(); - uint16_t fcf = Frame::kFcfFrameMacCmd | Frame::kFcfDstAddrShort | Frame::kFcfSrcAddrNone; + TxFrame &frame = mLinks.GetTxFrames().GetBroadcastTxFrame(); + Addresses addrs; + PanIds panIds; + + addrs.mSource.SetNone(); + addrs.mDestination.SetShort(kShortAddrBroadcast); + panIds.mDestination = kShortAddrBroadcast; + + frame.InitMacHeader(Frame::kTypeMacCmd, Frame::kVersion2003, addrs, panIds, Frame::kSecurityNone); - frame.InitMacHeader(fcf, Frame::kSecNone); - frame.SetDstPanId(kShortAddrBroadcast); - frame.SetDstAddr(kShortAddrBroadcast); IgnoreError(frame.SetCommandId(Frame::kMacCmdBeaconRequest)); LogInfo("Sending Beacon Request"); @@ -742,9 +738,10 @@ TxFrame *Mac::PrepareBeaconRequest(void) TxFrame *Mac::PrepareBeacon(void) { - TxFrame *frame; - uint16_t fcf; - Beacon * beacon = nullptr; + TxFrame *frame; + Beacon *beacon = nullptr; + Addresses addrs; + PanIds panIds; #if OPENTHREAD_CONFIG_MAC_OUTGOING_BEACON_PAYLOAD_ENABLE uint8_t beaconLength; BeaconPayload *beaconPayload = nullptr; @@ -758,10 +755,11 @@ TxFrame *Mac::PrepareBeacon(void) frame = &mLinks.GetTxFrames().GetBroadcastTxFrame(); #endif - fcf = Frame::kFcfFrameBeacon | Frame::kFcfDstAddrNone | Frame::kFcfSrcAddrExt; - frame->InitMacHeader(fcf, Frame::kSecNone); - IgnoreError(frame->SetSrcPanId(mPanId)); - frame->SetSrcAddr(GetExtAddress()); + addrs.mSource.SetExtended(GetExtAddress()); + panIds.mSource = mPanId; + addrs.mDestination.SetNone(); + + frame->InitMacHeader(Frame::kTypeBeacon, Frame::kVersion2003, addrs, panIds, Frame::kSecurityNone); beacon = reinterpret_cast(frame->GetPayload()); beacon->Init(); @@ -832,7 +830,7 @@ bool Mac::IsJoinable(void) const void Mac::ProcessTransmitSecurity(TxFrame &aFrame) { - KeyManager & keyManager = Get(); + KeyManager &keyManager = Get(); uint8_t keyIdMode; const ExtAddress *extAddress = nullptr; @@ -920,7 +918,7 @@ void Mac::ProcessTransmitSecurity(TxFrame &aFrame) void Mac::BeginTransmit(void) { - TxFrame * frame = nullptr; + TxFrame *frame = nullptr; TxFrames &txFrames = mLinks.GetTxFrames(); Address dstAddr; @@ -1033,10 +1031,8 @@ void Mac::BeginTransmit(void) // copy the frame into correct `TxFrame` for each radio type // (if it is not already prepared). - for (uint8_t index = 0; index < GetArrayLength(RadioTypes::kAllRadioTypes); index++) + for (RadioType radio : RadioTypes::kAllRadioTypes) { - RadioType radio = RadioTypes::kAllRadioTypes[index]; - if (txFrames.GetSelectedRadioTypes().Contains(radio)) { TxFrame &txFrame = txFrames.GetTxFrame(radio); @@ -1052,10 +1048,8 @@ void Mac::BeginTransmit(void) // process security for each radio type separately. This // allows radio links to handle security differently, e.g., // with different keys or link frame counters. - for (uint8_t index = 0; index < GetArrayLength(RadioTypes::kAllRadioTypes); index++) + for (RadioType radio : RadioTypes::kAllRadioTypes) { - RadioType radio = RadioTypes::kAllRadioTypes[index]; - if (txFrames.GetSelectedRadioTypes().Contains(radio)) { ProcessTransmitSecurity(txFrames.GetTxFrame(radio)); @@ -1136,7 +1130,7 @@ void Mac::RecordCcaStatus(bool aCcaSuccess, uint8_t aChannel) } void Mac::RecordFrameTransmitStatus(const TxFrame &aFrame, - const RxFrame *aAckFrame, + RxFrame *aAckFrame, Error aError, uint8_t aRetryCount, bool aWillRetx) @@ -1199,14 +1193,20 @@ void Mac::RecordFrameTransmitStatus(const TxFrame &aFrame, if ((aError == kErrorNone) && ackRequested && (aAckFrame != nullptr) && (neighbor != nullptr)) { +#if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE + SuccessOrExit(mFilter.ApplyToRxFrame(*aAckFrame, neighbor->GetExtAddress(), neighbor)); +#endif + neighbor->GetLinkInfo().AddRss(aAckFrame->GetRssi()); #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE neighbor->AggregateLinkMetrics(/* aSeriesId */ 0, aAckFrame->GetType(), aAckFrame->GetLqi(), aAckFrame->GetRssi()); +#endif +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE ProcessEnhAckProbing(*aAckFrame, *neighbor); #endif #if OPENTHREAD_FTD - if (aAckFrame->GetVersion() == Frame::kFcfFrameVersion2015) + if (aAckFrame->GetVersion() == Frame::kVersion2015) { #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE ProcessCsl(*aAckFrame, dstAddr); @@ -1307,11 +1307,11 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError) if (!aFrame.IsEmpty()) { RadioType radio = aFrame.GetRadioType(); - RadioTypes requriedRadios = mLinks.GetTxFrames().GetRequiredRadioTypes(); + RadioTypes requiredRadios = mLinks.GetTxFrames().GetRequiredRadioTypes(); Get().UpdateOnSendDone(aFrame, aError); - if (requriedRadios.IsEmpty()) + if (requiredRadios.IsEmpty()) { // If the "required radio type set" is empty, successful // tx over any radio link is sufficient for overall tx to @@ -1332,7 +1332,7 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError) // `mTxError` starts as `kErrorNone` and we update it // if tx over any link in the set fails. - if (requriedRadios.Contains(radio) && (aError != kErrorNone)) + if (requiredRadios.Contains(radio) && (aError != kErrorNone)) { LogDebg("Frame tx failed on required radio link %s with error %s", RadioTypeToString(radio), ErrorToString(aError)); @@ -1453,11 +1453,6 @@ void Mac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError) return; } -void Mac::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void Mac::HandleTimer(void) { switch (mOperation) @@ -1500,7 +1495,7 @@ void Mac::HandleTimer(void) Error Mac::ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Neighbor *aNeighbor) { - KeyManager & keyManager = Get(); + KeyManager &keyManager = Get(); Error error = kErrorSecurity; uint8_t securityLevel; uint8_t keyIdMode; @@ -1508,15 +1503,15 @@ Error Mac::ProcessReceiveSecurity(RxFrame &aFrame, const Address &aSrcAddr, Neig uint8_t keyid; uint32_t keySequence = 0; const KeyMaterial *macKey; - const ExtAddress * extAddress; + const ExtAddress *extAddress; VerifyOrExit(aFrame.GetSecurityEnabled(), error = kErrorNone); IgnoreError(aFrame.GetSecurityLevel(securityLevel)); - VerifyOrExit(securityLevel == Frame::kSecEncMic32); + VerifyOrExit(securityLevel == Frame::kSecurityEncMic32); IgnoreError(aFrame.GetFrameCounter(frameCounter)); - LogDebg("Rx security - frame counter %u", frameCounter); + LogDebg("Rx security - frame counter %lu", ToUlong(frameCounter)); IgnoreError(aFrame.GetKeyIdMode(keyIdMode)); @@ -1643,15 +1638,15 @@ Error Mac::ProcessEnhAckSecurity(TxFrame &aTxFrame, RxFrame &aAckFrame) uint32_t frameCounter; Address srcAddr; Address dstAddr; - Neighbor * neighbor = nullptr; - KeyManager & keyManager = Get(); + Neighbor *neighbor = nullptr; + KeyManager &keyManager = Get(); const KeyMaterial *macKey; VerifyOrExit(aAckFrame.GetSecurityEnabled(), error = kErrorNone); VerifyOrExit(aAckFrame.IsVersion2015()); IgnoreError(aAckFrame.GetSecurityLevel(securityLevel)); - VerifyOrExit(securityLevel == Frame::kSecEncMic32); + VerifyOrExit(securityLevel == Frame::kSecurityEncMic32); IgnoreError(aAckFrame.GetKeyIdMode(keyIdMode)); VerifyOrExit(keyIdMode == Frame::kKeyIdMode1, error = kErrorNone); @@ -1662,7 +1657,7 @@ Error Mac::ProcessEnhAckSecurity(TxFrame &aTxFrame, RxFrame &aAckFrame) VerifyOrExit(txKeyId == ackKeyId); IgnoreError(aAckFrame.GetFrameCounter(frameCounter)); - LogDebg("Rx security - Ack frame counter %u", frameCounter); + LogDebg("Rx security - Ack frame counter %lu", ToUlong(frameCounter)); IgnoreError(aAckFrame.GetSrcAddr(srcAddr)); @@ -1750,7 +1745,7 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, Error aError) IgnoreError(aFrame->GetSrcAddr(srcaddr)); IgnoreError(aFrame->GetDstAddr(dstaddr)); - neighbor = Get().FindNeighbor(srcaddr); + neighbor = !srcaddr.IsNone() ? Get().FindNeighbor(srcaddr) : nullptr; // Destination Address Filtering switch (dstaddr.GetType()) @@ -1804,23 +1799,7 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, Error aError) VerifyOrExit(srcaddr.GetExtended() != GetExtAddress(), error = kErrorInvalidSourceAddress); #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE - { - int8_t fixedRss; - - SuccessOrExit(error = mFilter.Apply(srcaddr.GetExtended(), fixedRss)); - - if (fixedRss != Filter::kFixedRssDisabled) - { - aFrame->SetRssi(fixedRss); - - // Clear any previous link info to ensure the fixed RSSI - // value takes effect quickly. - if (neighbor != nullptr) - { - neighbor->GetLinkInfo().Clear(); - } - } - } + SuccessOrExit(error = mFilter.ApplyToRxFrame(*aFrame, srcaddr.GetExtended(), neighbor)); #endif break; @@ -1866,7 +1845,7 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, Error aError) } #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE - if (aFrame->GetVersion() == Frame::kFcfFrameVersion2015) + if (aFrame->GetVersion() == Frame::kVersion2015) { ProcessCsl(*aFrame, srcaddr); } @@ -1907,7 +1886,7 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, Error aError) #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 && OPENTHREAD_FTD // From Thread 1.2, MAC Data Frame can also act as keep-alive message if child supports - if (aFrame->GetType() == Frame::kFcfFrameData && !neighbor->IsRxOnWhenIdle() && + if (aFrame->GetType() == Frame::kTypeData && !neighbor->IsRxOnWhenIdle() && neighbor->IsEnhancedKeepAliveSupported()) { neighbor->SetLastHeard(TimerMilli::GetNow()); @@ -1925,7 +1904,7 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, Error aError) { case kOperationActiveScan: - if (aFrame->GetType() == Frame::kFcfFrameBeacon) + if (aFrame->GetType() == Frame::kTypeBeacon) { mCounters.mRxBeacon++; ReportActiveScanResult(aFrame); @@ -1970,7 +1949,7 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, Error aError) switch (aFrame->GetType()) { - case Frame::kFcfFrameMacCmd: + case Frame::kTypeMacCmd: if (HandleMacCommand(*aFrame)) // returns `true` when handled { ExitNow(error = kErrorNone); @@ -1978,11 +1957,11 @@ void Mac::HandleReceivedFrame(RxFrame *aFrame, Error aError) break; - case Frame::kFcfFrameBeacon: + case Frame::kTypeBeacon: mCounters.mRxBeacon++; break; - case Frame::kFcfFrameData: + case Frame::kTypeData: mCounters.mRxData++; break; @@ -2128,12 +2107,11 @@ const uint32_t *Mac::GetIndirectRetrySuccessHistogram(uint8_t &aNumberOfEntries) } #endif -void Mac::ResetRetrySuccessHistogram() -{ - memset(&mRetryHistogram, 0, sizeof(mRetryHistogram)); -} +void Mac::ResetRetrySuccessHistogram() { memset(&mRetryHistogram, 0, sizeof(mRetryHistogram)); } #endif // OPENTHREAD_CONFIG_MAC_RETRY_SUCCESS_HISTOGRAM_ENABLE +uint8_t Mac::ComputeLinkMargin(int8_t aRss) const { return ot::ComputeLinkMargin(GetNoiseFloor(), aRss); } + // LCOV_EXCL_START #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) @@ -2214,7 +2192,7 @@ void Mac::LogFrameTxFailure(const TxFrame &aFrame, Error aError, uint8_t aRetryC uint8_t maxAttempts = aFrame.GetMaxFrameRetries() + 1; uint8_t curAttempt = aWillRetx ? (aRetryCount + 1) : maxAttempts; - LogInfo("Frame tx attempt %d/%d failed, error:%s, %s", curAttempt, maxAttempts, ErrorToString(aError), + LogInfo("Frame tx attempt %u/%u failed, error:%s, %s", curAttempt, maxAttempts, ErrorToString(aError), aFrame.ToInfoString().AsCString()); } else @@ -2223,24 +2201,15 @@ void Mac::LogFrameTxFailure(const TxFrame &aFrame, Error aError, uint8_t aRetryC } } -void Mac::LogBeacon(const char *aActionText) const -{ - LogInfo("%s Beacon", aActionText); -} +void Mac::LogBeacon(const char *aActionText) const { LogInfo("%s Beacon", aActionText); } #else // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) -void Mac::LogFrameRxFailure(const RxFrame *, Error) const -{ -} +void Mac::LogFrameRxFailure(const RxFrame *, Error) const {} -void Mac::LogBeacon(const char *) const -{ -} +void Mac::LogBeacon(const char *) const {} -void Mac::LogFrameTxFailure(const TxFrame &, Error, uint8_t, bool) const -{ -} +void Mac::LogFrameTxFailure(const TxFrame &, Error, uint8_t, bool) const {} #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) @@ -2299,15 +2268,9 @@ void Mac::SetCslPeriod(uint16_t aPeriod) UpdateCsl(); } -bool Mac::IsCslEnabled(void) const -{ - return !Get().IsRxOnWhenIdle() && IsCslCapable(); -} +bool Mac::IsCslEnabled(void) const { return !Get().IsRxOnWhenIdle() && IsCslCapable(); } -bool Mac::IsCslCapable(void) const -{ - return (GetCslPeriod() > 0) && IsCslSupported(); -} +bool Mac::IsCslCapable(void) const { return (GetCslPeriod() > 0) && IsCslSupported(); } bool Mac::IsCslSupported(void) const { @@ -2319,8 +2282,8 @@ bool Mac::IsCslSupported(void) const void Mac::ProcessCsl(const RxFrame &aFrame, const Address &aSrcAddr) { const uint8_t *cur = aFrame.GetHeaderIe(CslIe::kHeaderIeId); - Child * child = Get().FindChild(aSrcAddr, Child::kInStateAnyExceptInvalid); - const CslIe * csl; + Child *child = Get().FindChild(aSrcAddr, Child::kInStateAnyExceptInvalid); + const CslIe *csl; VerifyOrExit(cur != nullptr && child != nullptr && aFrame.GetSecurityEnabled()); @@ -2332,9 +2295,9 @@ void Mac::ProcessCsl(const RxFrame &aFrame, const Address &aSrcAddr) child->SetCslSynchronized(true); child->SetCslLastHeard(TimerMilli::GetNow()); child->SetLastRxTimestamp(aFrame.GetTimestamp()); - LogDebg("Timestamp=%u Sequence=%u CslPeriod=%hu CslPhase=%hu TransmitPhase=%hu", - static_cast(aFrame.GetTimestamp()), aFrame.GetSequence(), csl->GetPeriod(), csl->GetPhase(), - child->GetCslPhase()); + LogDebg("Timestamp=%lu Sequence=%u CslPeriod=%u CslPhase=%u TransmitPhase=%u", + ToUlong(static_cast(aFrame.GetTimestamp())), aFrame.GetSequence(), csl->GetPeriod(), + csl->GetPhase(), child->GetCslPhase()); Get().Update(); @@ -2343,7 +2306,7 @@ void Mac::ProcessCsl(const RxFrame &aFrame, const Address &aSrcAddr) } #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE void Mac::ProcessEnhAckProbing(const RxFrame &aFrame, const Neighbor &aNeighbor) { constexpr uint8_t kEnhAckProbingIeMaxLen = 2; @@ -2359,11 +2322,11 @@ void Mac::ProcessEnhAckProbing(const RxFrame &aFrame, const Neighbor &aNeighbor) dataLen = enhAckProbingIe->GetLength() - sizeof(VendorIeHeader); VerifyOrExit(dataLen <= kEnhAckProbingIeMaxLen); - Get().ProcessEnhAckIeData(data, dataLen, aNeighbor); + Get().ProcessEnhAckIeData(data, dataLen, aNeighbor); exit: return; } -#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE void Mac::SetRadioFilterEnabled(bool aFilterEnabled) diff --git a/src/core/mac/mac.hpp b/src/core/mac/mac.hpp index babdbbf351e..3566109418d 100644 --- a/src/core/mac/mac.hpp +++ b/src/core/mac/mac.hpp @@ -71,8 +71,9 @@ class Neighbor; namespace Mac { -constexpr uint32_t kDataPollTimeout = 100; ///< Timeout for receiving Data Frame (in msec). -constexpr uint32_t kSleepDelay = 300; ///< Max sleep delay when frame is pending (in msec). +constexpr uint32_t kDataPollTimeout = + OPENTHREAD_CONFIG_MAC_DATA_POLL_TIMEOUT; ///< Timeout for receiving Data Frame (in msec). +constexpr uint32_t kSleepDelay = 300; ///< Max sleep delay when frame is pending (in msec) constexpr uint16_t kScanDurationDefault = OPENTHREAD_CONFIG_MAC_SCAN_DURATION; ///< Duration per channel (in msec). @@ -412,7 +413,7 @@ class Mac : public InstanceLocator, private NonCopyable * */ void RecordFrameTransmitStatus(const TxFrame &aFrame, - const RxFrame *aAckFrame, + RxFrame *aAckFrame, Error aError, uint8_t aRetryCount, bool aWillRetx); @@ -546,7 +547,17 @@ class Mac : public InstanceLocator, private NonCopyable * @returns The noise floor value in dBm. * */ - int8_t GetNoiseFloor(void) { return mLinks.GetNoiseFloor(); } + int8_t GetNoiseFloor(void) const { return mLinks.GetNoiseFloor(); } + + /** + * This method computes the link margin for a given a received signal strength value using noise floor. + * + * @param[in] aRss The received signal strength in dBm. + * + * @returns The link margin for @p aRss in dB based on noise floor. + * + */ + uint8_t ComputeLinkMargin(int8_t aRss) const; /** * This method returns the current CCA (Clear Channel Assessment) failure rate. @@ -576,6 +587,12 @@ class Mac : public InstanceLocator, private NonCopyable */ bool IsEnabled(void) const { return mEnabled; } + /** + * This method clears the Mode2Key stored in PSA ITS. + * + */ + void ClearMode2Key(void) { mMode2KeyMaterial.Clear(); } + #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE /** * This method gets the CSL channel. @@ -607,6 +624,14 @@ class Mac : public InstanceLocator, private NonCopyable */ uint16_t GetCslPeriod(void) const { return mCslPeriod; } + /** + * This method gets the CSL period. + * + * @returns CSL period in milliseconds. + * + */ + uint32_t GetCslPeriodMs(void) const { return mCslPeriod * kUsPerTenSymbols / 1000; } + /** * This method sets the CSL period. * @@ -643,42 +668,24 @@ class Mac : public InstanceLocator, private NonCopyable bool IsCslSupported(void) const; /** - * This method returns CSL parent clock accuracy, in ± ppm. + * This method returns parent CSL accuracy (clock accuracy and uncertainty). * - * @retval CSL parent clock accuracy, in ± ppm. + * @returns The parent CSL accuracy. * */ - uint8_t GetCslParentClockAccuracy(void) const { return mLinks.GetSubMac().GetCslParentClockAccuracy(); } + const CslAccuracy &GetCslParentAccuracy(void) const { return mLinks.GetSubMac().GetCslParentAccuracy(); } /** - * This method sets CSL parent clock accuracy, in ± ppm. + * This method sets parent CSL accuracy. * - * @param[in] aCslParentAccuracy CSL parent clock accuracy, in ± ppm. + * @param[in] aCslAccuracy The parent CSL accuracy. * */ - void SetCslParentClockAccuracy(uint8_t aCslParentAccuracy) + void SetCslParentAccuracy(const CslAccuracy &aCslAccuracy) { - mLinks.GetSubMac().SetCslParentClockAccuracy(aCslParentAccuracy); + mLinks.GetSubMac().SetCslParentAccuracy(aCslAccuracy); } - /** - * This method returns CSL parent uncertainty, in ±10 us units. - * - * @retval CSL parent uncertainty, in ±10 us units. - * - */ - uint8_t GetCslParentUncertainty(void) const { return mLinks.GetSubMac().GetCslParentUncertainty(); } - - /** - * This method returns CSL parent uncertainty, in ±10 us units. - * - * @param[in] aCslParentUncert CSL parent uncertainty, in ±10 us units. - * - */ - void SetCslParentUncertainty(uint8_t aCslParentUncert) - { - mLinks.GetSubMac().SetCslParentUncertainty(aCslParentUncert); - } #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE && OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE @@ -705,7 +712,6 @@ class Mac : public InstanceLocator, private NonCopyable #endif private: - static constexpr int8_t kInvalidRssiValue = SubMac::kInvalidRssiValue; static constexpr uint16_t kMaxCcaSampleCount = OPENTHREAD_CONFIG_CCA_FAILURE_RATE_AVERAGING_WINDOW; enum Operation : uint8_t @@ -768,10 +774,7 @@ class Mac : public InstanceLocator, private NonCopyable bool IsJoinable(void) const; void BeginTransmit(void); bool HandleMacCommand(RxFrame &aFrame); - - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); - static void HandleOperationTask(Tasklet &aTasklet); + void HandleTimer(void); void Scan(Operation aScanOperation, uint32_t aScanChannels, uint16_t aScanDuration); Error UpdateScanChannel(void); @@ -792,11 +795,14 @@ class Mac : public InstanceLocator, private NonCopyable #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE void ProcessCsl(const RxFrame &aFrame, const Address &aSrcAddr); #endif -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE void ProcessEnhAckProbing(const RxFrame &aFrame, const Neighbor &aNeighbor); #endif static const char *OperationToString(Operation aOperation); + using OperationTask = TaskletIn; + using MacTimer = TimerMilliIn; + static const otExtAddress sMode2ExtAddress; bool mEnabled : 1; @@ -843,8 +849,8 @@ class Mac : public InstanceLocator, private NonCopyable void *mScanHandlerContext; Links mLinks; - Tasklet mOperationTask; - TimerMilli mTimer; + OperationTask mOperationTask; + MacTimer mTimer; otMacCounters mCounters; uint32_t mKeyIdMode2FrameCounter; SuccessRateTracker mCcaSuccessRateTracker; diff --git a/src/core/mac/mac_filter.cpp b/src/core/mac/mac_filter.cpp index c119c425b23..97604a33c9f 100644 --- a/src/core/mac/mac_filter.cpp +++ b/src/core/mac/mac_filter.cpp @@ -38,6 +38,7 @@ #include "common/array.hpp" #include "common/as_core_type.hpp" #include "common/code_utils.hpp" +#include "thread/topology.hpp" namespace ot { namespace Mac { @@ -53,11 +54,11 @@ Filter::Filter(void) } } -Filter::FilterEntry *Filter::FindEntry(const ExtAddress &aExtAddress) +const Filter::FilterEntry *Filter::FindEntry(const ExtAddress &aExtAddress) const { - FilterEntry *rval = nullptr; + const FilterEntry *rval = nullptr; - for (FilterEntry &entry : mFilterEntries) + for (const FilterEntry &entry : mFilterEntries) { if (entry.IsInUse() && (aExtAddress == entry.mExtAddress)) { @@ -182,13 +183,13 @@ void Filter::ClearAllRssIn(void) mDefaultRssIn = kFixedRssDisabled; } -Error Filter::GetNextRssIn(Iterator &aIterator, Entry &aEntry) +Error Filter::GetNextRssIn(Iterator &aIterator, Entry &aEntry) const { Error error = kErrorNotFound; for (; aIterator < GetArrayLength(mFilterEntries); aIterator++) { - FilterEntry &entry = mFilterEntries[aIterator]; + const FilterEntry &entry = mFilterEntries[aIterator]; if (entry.mRssIn != kFixedRssDisabled) { @@ -213,11 +214,11 @@ Error Filter::GetNextRssIn(Iterator &aIterator, Entry &aEntry) return error; } -Error Filter::Apply(const ExtAddress &aExtAddress, int8_t &aRss) +Error Filter::Apply(const ExtAddress &aExtAddress, int8_t &aRss) const { - Error error = kErrorNone; - FilterEntry *entry = FindEntry(aExtAddress); - bool isInFilterList; + Error error = kErrorNone; + const FilterEntry *entry = FindEntry(aExtAddress); + bool isInFilterList; // Use the default RssIn setting for all receiving messages first. aRss = mDefaultRssIn; @@ -250,6 +251,28 @@ Error Filter::Apply(const ExtAddress &aExtAddress, int8_t &aRss) return error; } +Error Filter::ApplyToRxFrame(RxFrame &aRxFrame, const ExtAddress &aExtAddress, Neighbor *aNeighbor) const +{ + Error error; + int8_t fixedRss; + + SuccessOrExit(error = Apply(aExtAddress, fixedRss)); + + VerifyOrExit(fixedRss != kFixedRssDisabled); + + aRxFrame.SetRssi(fixedRss); + + if (aNeighbor != nullptr) + { + // Clear the previous RSS average to ensure the fixed RSS + // value takes effect quickly. + aNeighbor->GetLinkInfo().ClearAverageRss(); + } + +exit: + return error; +} + } // namespace Mac } // namespace ot diff --git a/src/core/mac/mac_filter.hpp b/src/core/mac/mac_filter.hpp index 2eaef71aab8..d5ff8bba20d 100644 --- a/src/core/mac/mac_filter.hpp +++ b/src/core/mac/mac_filter.hpp @@ -41,10 +41,14 @@ #include #include "common/as_core_type.hpp" +#include "common/const_cast.hpp" #include "common/non_copyable.hpp" #include "mac/mac_frame.hpp" namespace ot { + +class Neighbor; + namespace Mac { /** @@ -208,7 +212,7 @@ class Filter : private NonCopyable * @retval kErrorNotFound No subsequent entry exists. * */ - Error GetNextRssIn(Iterator &aIterator, Entry &aEntry); + Error GetNextRssIn(Iterator &aIterator, Entry &aEntry) const; /** * This method applies the filter rules on a given Extended Address. @@ -220,7 +224,24 @@ class Filter : private NonCopyable * @retval kErrorAddressFiltered Address filter (allowlist or denylist) is enabled and @p aExtAddress is filtered. * */ - Error Apply(const ExtAddress &aExtAddress, int8_t &aRss); + Error Apply(const ExtAddress &aExtAddress, int8_t &aRss) const; + + /** + * This method applies the filter rules to a received frame from a given Extended Address. + * + * This method can potentially update the signal strength value on the received frame @p aRxFrame. If @p aNeighbor + * is not `nullptr` and filter applies a fixed RSS to the @p aRxFrame, this method will also clear the current RSS + * average on @p aNeighbor to ensure that the new fixed RSS takes effect quickly. + * + * @param[out] aRxFrame The received frame. + * @param[in] aExtAddress The extended address from which @p aRxFrame was received. + * @param[in] aNeighbor A pointer to the neighbor (can be `nullptr` if not known). + * + * @retval kErrorNone Successfully applied the filter, @p aRxFrame RSS may be updated. + * @retval kErrorAddressFiltered Address filter (allowlist or denylist) is enabled and @p aExtAddress is filtered. + * + */ + Error ApplyToRxFrame(RxFrame &aRxFrame, const ExtAddress &aExtAddress, Neighbor *aNeighbor = nullptr) const; private: static constexpr uint16_t kMaxEntries = OPENTHREAD_CONFIG_MAC_FILTER_SIZE; @@ -234,8 +255,9 @@ class Filter : private NonCopyable bool IsInUse(void) const { return mFiltered || (mRssIn != kFixedRssDisabled); } }; - FilterEntry *FindAvailableEntry(void); - FilterEntry *FindEntry(const ExtAddress &aExtAddress); + FilterEntry *FindAvailableEntry(void); + const FilterEntry *FindEntry(const ExtAddress &aExtAddress) const; + FilterEntry *FindEntry(const ExtAddress &aExtAddress) { return AsNonConst(AsConst(this)->FindEntry(aExtAddress)); } Mode mMode; int8_t mDefaultRssIn; diff --git a/src/core/mac/mac_frame.cpp b/src/core/mac/mac_frame.cpp index 2f0b1d18c7f..a31350b1911 100644 --- a/src/core/mac/mac_frame.cpp +++ b/src/core/mac/mac_frame.cpp @@ -37,6 +37,7 @@ #include "common/code_utils.hpp" #include "common/debug.hpp" +#include "common/frame_builder.hpp" #include "common/log.hpp" #include "radio/trel_link.hpp" #if !OPENTHREAD_RADIO || OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE @@ -58,23 +59,106 @@ void HeaderIe::Init(uint16_t aId, uint8_t aLen) SetLength(aLen); } -void Frame::InitMacHeader(uint16_t aFcf, uint8_t aSecurityControl) +void Frame::InitMacHeader(Type aType, + Version aVersion, + const Addresses &aAddrs, + const PanIds &aPanIds, + SecurityLevel aSecurityLevel, + KeyIdMode aKeyIdMode) { - mLength = CalculateAddrFieldSize(aFcf); + uint16_t fcf; + FrameBuilder builder; - OT_ASSERT(mLength != kInvalidSize); + fcf = static_cast(aType) | static_cast(aVersion); - WriteUint16(aFcf, mPsdu); + switch (aAddrs.mSource.GetType()) + { + case Address::kTypeNone: + fcf |= kFcfSrcAddrNone; + break; + case Address::kTypeShort: + fcf |= kFcfSrcAddrShort; + break; + case Address::kTypeExtended: + fcf |= kFcfSrcAddrExt; + break; + } + + switch (aAddrs.mDestination.GetType()) + { + case Address::kTypeNone: + fcf |= kFcfDstAddrNone; + break; + case Address::kTypeShort: + fcf |= kFcfDstAddrShort; + fcf |= ((aAddrs.mDestination.GetShort() == kShortAddrBroadcast) ? 0 : kFcfAckRequest); + break; + case Address::kTypeExtended: + fcf |= (kFcfDstAddrExt | kFcfAckRequest); + break; + } + + fcf |= (aSecurityLevel != kSecurityNone) ? kFcfSecurityEnabled : 0; + + // When we have both source and destination addresses we check PAN + // IDs to determine whether to include `kFcfPanidCompression`. + + if (!aAddrs.mSource.IsNone() && !aAddrs.mDestination.IsNone() && (aPanIds.mSource == aPanIds.mDestination)) + { + switch (aVersion) + { + case kVersion2015: + // Special case for a IEEE 802.15.4-2015 frame: When both + // addresses are extended, the PAN ID compression is set + // to one to indicate that no PAN ID is in the frame, + // while setting the PAN ID Compression to zero indicates + // the presence of the destination PAN ID in the frame. + + if (aAddrs.mSource.IsExtended() && aAddrs.mDestination.IsExtended()) + { + break; + } + + OT_FALL_THROUGH; + + case kVersion2003: + case kVersion2006: + fcf |= kFcfPanidCompression; + break; + } + } + + builder.Init(mPsdu, GetMtu()); + IgnoreError(builder.AppendLittleEndianUint16(fcf)); + IgnoreError(builder.AppendUint8(0)); // Seq number + + if (IsDstPanIdPresent(fcf)) + { + IgnoreError(builder.AppendLittleEndianUint16(aPanIds.mDestination)); + } + + IgnoreError(builder.AppendMacAddress(aAddrs.mDestination)); + + if (IsSrcPanIdPresent(fcf)) + { + IgnoreError(builder.AppendLittleEndianUint16(aPanIds.mSource)); + } + + IgnoreError(builder.AppendMacAddress(aAddrs.mSource)); - if (aFcf & kFcfSecurityEnabled) + mLength = builder.GetLength(); + + if (aSecurityLevel != kSecurityNone) { - mPsdu[mLength] = aSecurityControl; + uint8_t secCtl = static_cast(aSecurityLevel) | static_cast(aKeyIdMode); + + IgnoreError(builder.AppendUint8(secCtl)); - mLength += CalculateSecurityHeaderSize(aSecurityControl); - mLength += CalculateMicSize(aSecurityControl); + mLength += CalculateSecurityHeaderSize(secCtl); + mLength += CalculateMicSize(secCtl); } - if ((aFcf & kFcfFrameTypeMask) == kFcfFrameMacCmd) + if (aType == kTypeMacCmd) { mLength += kCommandIdSize; } @@ -82,10 +166,7 @@ void Frame::InitMacHeader(uint16_t aFcf, uint8_t aSecurityControl) mLength += GetFcsSize(); } -uint16_t Frame::GetFrameControlField(void) const -{ - return ReadUint16(mPsdu); -} +uint16_t Frame::GetFrameControlField(void) const { return ReadUint16(mPsdu); } Error Frame::ValidatePsdu(void) const { @@ -139,7 +220,6 @@ bool Frame::IsDstPanIdPresent(uint16_t aFcf) { bool present = true; -#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT if (IsVersion2015(aFcf)) { switch (aFcf & (kFcfDstAddrMask | kFcfSrcAddrMask | kFcfPanidCompression)) @@ -159,7 +239,6 @@ bool Frame::IsDstPanIdPresent(uint16_t aFcf) } } else -#endif { present = IsDstAddrPresent(aFcf); } @@ -187,10 +266,7 @@ void Frame::SetDstPanId(PanId aPanId) WriteUint16(aPanId, &mPsdu[index]); } -uint8_t Frame::FindDstAddrIndex(void) const -{ - return kFcfSize + kDsnSize + (IsDstPanIdPresent() ? sizeof(PanId) : 0); -} +uint8_t Frame::FindDstAddrIndex(void) const { return kFcfSize + kDsnSize + (IsDstPanIdPresent() ? sizeof(PanId) : 0); } Error Frame::GetDstAddr(Address &aAddress) const { @@ -283,26 +359,21 @@ uint8_t Frame::FindSrcPanIdIndex(void) const bool Frame::IsSrcPanIdPresent(uint16_t aFcf) { - bool srcPanIdPresent = false; + bool present = IsSrcAddrPresent(aFcf) && ((aFcf & kFcfPanidCompression) == 0); + + // Special case for a IEEE 802.15.4-2015 frame: When both + // addresses are extended, then the source PAN iD is not present + // independent of PAN ID Compression. In this case, if the PAN ID + // compression is set, it indicates that no PAN ID is in the + // frame, while if the PAN ID Compression is zero, it indicates + // the presence of the destination PAN ID in the frame. - if ((aFcf & kFcfSrcAddrMask) != kFcfSrcAddrNone && (aFcf & kFcfPanidCompression) == 0) + if (IsVersion2015(aFcf) && ((aFcf & (kFcfDstAddrMask | kFcfSrcAddrMask)) == (kFcfDstAddrExt | kFcfSrcAddrExt))) { -#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT - // Handle a special case in IEEE 802.15.4-2015, when Pan ID Compression is 0, but Src Pan ID is not present: - // Dest Address: Extended - // Source Address: Extended - // Dest Pan ID: Present - // Src Pan ID: Not Present - // Pan ID Compression: 0 - if (!IsVersion2015(aFcf) || (aFcf & kFcfDstAddrMask) != kFcfDstAddrExt || - (aFcf & kFcfSrcAddrMask) != kFcfSrcAddrExt) -#endif - { - srcPanIdPresent = true; - } + present = false; } - return srcPanIdPresent; + return present; } Error Frame::GetSrcPanId(PanId &aPanId) const @@ -378,9 +449,14 @@ Error Frame::GetSrcAddr(Address &aAddress) const aAddress.SetExtended(&mPsdu[index], ExtAddress::kReverseByteOrder); break; - default: + case kFcfSrcAddrNone: aAddress.SetNone(); break; + + default: + // reserved value + error = kErrorParse; + break; } exit: @@ -620,7 +696,7 @@ bool Frame::IsDataRequestCommand(void) const bool isDataRequest = false; uint8_t commandId; - VerifyOrExit(GetType() == kFcfFrameMacCmd); + VerifyOrExit(GetType() == kTypeMacCmd); SuccessOrExit(GetCommandId(commandId)); isDataRequest = (commandId == kMacCmdDataRequest); @@ -628,10 +704,7 @@ bool Frame::IsDataRequestCommand(void) const return isDataRequest; } -uint8_t Frame::GetHeaderLength(void) const -{ - return static_cast(GetPayload() - mPsdu); -} +uint8_t Frame::GetHeaderLength(void) const { return static_cast(GetPayload() - mPsdu); } uint8_t Frame::GetFooterLength(void) const { @@ -651,23 +724,23 @@ uint8_t Frame::CalculateMicSize(uint8_t aSecurityControl) switch (aSecurityControl & kSecLevelMask) { - case kSecNone: - case kSecEnc: + case kSecurityNone: + case kSecurityEnc: micSize = kMic0Size; break; - case kSecMic32: - case kSecEncMic32: + case kSecurityMic32: + case kSecurityEncMic32: micSize = kMic32Size; break; - case kSecMic64: - case kSecEncMic64: + case kSecurityMic64: + case kSecurityEncMic64: micSize = kMic64Size; break; - case kSecMic128: - case kSecEncMic128: + case kSecurityMic128: + case kSecurityEncMic128: micSize = kMic128Size; break; } @@ -675,20 +748,11 @@ uint8_t Frame::CalculateMicSize(uint8_t aSecurityControl) return micSize; } -uint16_t Frame::GetMaxPayloadLength(void) const -{ - return GetMtu() - (GetHeaderLength() + GetFooterLength()); -} +uint16_t Frame::GetMaxPayloadLength(void) const { return GetMtu() - (GetHeaderLength() + GetFooterLength()); } -uint16_t Frame::GetPayloadLength(void) const -{ - return mLength - (GetHeaderLength() + GetFooterLength()); -} +uint16_t Frame::GetPayloadLength(void) const { return mLength - (GetHeaderLength() + GetFooterLength()); } -void Frame::SetPayloadLength(uint16_t aLength) -{ - mLength = GetHeaderLength() + GetFooterLength() + aLength; -} +void Frame::SetPayloadLength(uint16_t aLength) { mLength = GetHeaderLength() + GetFooterLength() + aLength; } uint8_t Frame::SkipSecurityHeaderIndex(void) const { @@ -720,7 +784,7 @@ uint8_t Frame::CalculateSecurityHeaderSize(uint8_t aSecurityControl) { uint8_t size = kSecurityControlSize + kFrameCounterSize; - VerifyOrExit((aSecurityControl & kSecLevelMask) != kSecNone, size = kInvalidSize); + VerifyOrExit((aSecurityControl & kSecLevelMask) != kSecurityNone, size = kInvalidSize); switch (aSecurityControl & kKeyIdModeMask) { @@ -858,7 +922,7 @@ uint8_t Frame::FindPayloadIndex(void) const } #endif // OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT - if (!IsVersion2015() && (GetFrameControlField() & kFcfFrameTypeMask) == kFcfFrameMacCmd) + if (!IsVersion2015() && (GetFrameControlField() & kFcfFrameTypeMask) == kTypeMacCmd) { index += kCommandIdSize; } @@ -879,10 +943,7 @@ const uint8_t *Frame::GetPayload(void) const return payload; } -const uint8_t *Frame::GetFooter(void) const -{ - return mPsdu + mLength - GetFooterLength(); -} +const uint8_t *Frame::GetFooter(void) const { return mPsdu + mLength - GetFooterLength(); } #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT uint8_t Frame::FindHeaderIeIndex(void) const @@ -914,6 +975,8 @@ Error Frame::InitIeHeaderAt(uint8_t &aIndex, uint8_t ieId, uint8_t ieContentSize { Error error = kErrorNone; + WriteUint16(GetFrameControlField() | kFcfIePresent, mPsdu); + if (aIndex == 0) { aIndex = FindHeaderIeIndex(); @@ -938,16 +1001,10 @@ template <> void Frame::InitIeContentAt(uint8_t &aIndex) #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -template <> void Frame::InitIeContentAt(uint8_t &aIndex) -{ - aIndex += sizeof(CslIe); -} +template <> void Frame::InitIeContentAt(uint8_t &aIndex) { aIndex += sizeof(CslIe); } #endif -template <> void Frame::InitIeContentAt(uint8_t &aIndex) -{ - OT_UNUSED_VARIABLE(aIndex); -} +template <> void Frame::InitIeContentAt(uint8_t &aIndex) { OT_UNUSED_VARIABLE(aIndex); } #endif // OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT const uint8_t *Frame::GetHeaderIe(uint8_t aIeId) const @@ -1018,7 +1075,7 @@ const uint8_t *Frame::GetThreadIe(uint8_t aSubType) const void Frame::SetCslIe(uint16_t aCslPeriod, uint16_t aCslPhase) { uint8_t *cur = GetHeaderIe(CslIe::kHeaderIeId); - CslIe * csl; + CslIe *csl; VerifyOrExit(cur != nullptr); @@ -1044,7 +1101,7 @@ void Frame::SetEnhAckProbingIe(const uint8_t *aValue, uint8_t aLen) #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE const TimeIe *Frame::GetTimeIe(void) const { - const TimeIe * timeIe = nullptr; + const TimeIe *timeIe = nullptr; const uint8_t *cur = nullptr; cur = GetHeaderIe(VendorIeHeader::kHeaderIeId); @@ -1107,15 +1164,9 @@ uint8_t Frame::GetFcsSize(void) const } #elif OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE -uint16_t Frame::GetMtu(void) const -{ - return Trel::Link::kMtuSize; -} +uint16_t Frame::GetMtu(void) const { return Trel::Link::kMtuSize; } -uint8_t Frame::GetFcsSize(void) const -{ - return Trel::Link::kFcsSize; -} +uint8_t Frame::GetFcsSize(void) const { return Trel::Link::kFcsSize; } #endif // Explicit instantiation @@ -1131,7 +1182,7 @@ template Error Frame::AppendHeaderIeAt(uint8_t &aIndex); void TxFrame::CopyFrom(const TxFrame &aFromFrame) { - uint8_t * psduBuffer = mPsdu; + uint8_t *psduBuffer = mPsdu; otRadioIeInfo *ieInfoBuffer = mInfo.mTxInfo.mIeInfo; #if OPENTHREAD_CONFIG_MULTI_RADIO uint8_t radioType = mRadioType; @@ -1203,7 +1254,7 @@ void TxFrame::ProcessTransmitAesCcm(const ExtAddress &aExtAddress) void TxFrame::GenerateImmAck(const RxFrame &aFrame, bool aIsFramePending) { - uint16_t fcf = kFcfFrameAck | aFrame.GetVersion(); + uint16_t fcf = static_cast(kTypeAck) | aFrame.GetVersion(); mChannel = aFrame.mChannel; memset(&mInfo.mTxInfo, 0, sizeof(mInfo.mTxInfo)); @@ -1224,13 +1275,15 @@ Error TxFrame::GenerateEnhAck(const RxFrame &aFrame, bool aIsFramePending, const { Error error = kErrorNone; - uint16_t fcf = kFcfFrameAck | kFcfFrameVersion2015 | kFcfSrcAddrNone; + uint16_t fcf; Address address; PanId panId; uint8_t footerLength; uint8_t securityControlField; uint8_t keyId; + fcf = static_cast(kTypeAck) | static_cast(kVersion2015) | kFcfSrcAddrNone; + mChannel = aFrame.mChannel; memset(&mInfo.mTxInfo, 0, sizeof(mInfo.mTxInfo)); @@ -1394,19 +1447,19 @@ Frame::InfoString Frame::ToInfoString(void) const switch (type) { - case kFcfFrameBeacon: + case kTypeBeacon: string.Append("Beacon"); break; - case kFcfFrameData: + case kTypeData: string.Append("Data"); break; - case kFcfFrameAck: + case kTypeAck: string.Append("Ack"); break; - case kFcfFrameMacCmd: + case kTypeMacCmd: if (GetCommandId(commandId) != kErrorNone) { commandId = 0xff; diff --git a/src/core/mac/mac_frame.hpp b/src/core/mac/mac_frame.hpp index 9ffe2d57c8f..78924bfc2c2 100644 --- a/src/core/mac/mac_frame.hpp +++ b/src/core/mac/mac_frame.hpp @@ -275,75 +275,81 @@ class ThreadIe class Frame : public otRadioFrame { public: - static constexpr uint8_t kFcfSize = sizeof(uint16_t); - static constexpr uint8_t kDsnSize = sizeof(uint8_t); - static constexpr uint8_t kSecurityControlSize = sizeof(uint8_t); - static constexpr uint8_t kFrameCounterSize = sizeof(uint32_t); - static constexpr uint8_t kCommandIdSize = sizeof(uint8_t); - static constexpr uint8_t k154FcsSize = sizeof(uint16_t); - - static constexpr uint16_t kFcfFrameBeacon = 0 << 0; - static constexpr uint16_t kFcfFrameData = 1 << 0; - static constexpr uint16_t kFcfFrameAck = 2 << 0; - static constexpr uint16_t kFcfFrameMacCmd = 3 << 0; - static constexpr uint16_t kFcfFrameTypeMask = 7 << 0; - static constexpr uint16_t kFcfSecurityEnabled = 1 << 3; - static constexpr uint16_t kFcfFramePending = 1 << 4; - static constexpr uint16_t kFcfAckRequest = 1 << 5; - static constexpr uint16_t kFcfPanidCompression = 1 << 6; - static constexpr uint16_t kFcfIePresent = 1 << 9; - static constexpr uint16_t kFcfDstAddrNone = 0 << 10; - static constexpr uint16_t kFcfDstAddrShort = 2 << 10; - static constexpr uint16_t kFcfDstAddrExt = 3 << 10; - static constexpr uint16_t kFcfDstAddrMask = 3 << 10; - static constexpr uint16_t kFcfFrameVersion2006 = 1 << 12; - static constexpr uint16_t kFcfFrameVersion2015 = 2 << 12; - static constexpr uint16_t kFcfFrameVersionMask = 3 << 12; - static constexpr uint16_t kFcfSrcAddrNone = 0 << 14; - static constexpr uint16_t kFcfSrcAddrShort = 2 << 14; - static constexpr uint16_t kFcfSrcAddrExt = 3 << 14; - static constexpr uint16_t kFcfSrcAddrMask = 3 << 14; - - static constexpr uint8_t kSecNone = 0 << 0; - static constexpr uint8_t kSecMic32 = 1 << 0; - static constexpr uint8_t kSecMic64 = 2 << 0; - static constexpr uint8_t kSecMic128 = 3 << 0; - static constexpr uint8_t kSecEnc = 4 << 0; - static constexpr uint8_t kSecEncMic32 = 5 << 0; - static constexpr uint8_t kSecEncMic64 = 6 << 0; - static constexpr uint8_t kSecEncMic128 = 7 << 0; - static constexpr uint8_t kSecLevelMask = 7 << 0; - - static constexpr uint8_t kMic0Size = 0; - static constexpr uint8_t kMic32Size = 32 / CHAR_BIT; - static constexpr uint8_t kMic64Size = 64 / CHAR_BIT; - static constexpr uint8_t kMic128Size = 128 / CHAR_BIT; - static constexpr uint8_t kMaxMicSize = kMic128Size; - - static constexpr uint8_t kKeyIdMode0 = 0 << 3; - static constexpr uint8_t kKeyIdMode1 = 1 << 3; - static constexpr uint8_t kKeyIdMode2 = 2 << 3; - static constexpr uint8_t kKeyIdMode3 = 3 << 3; - static constexpr uint8_t kKeyIdModeMask = 3 << 3; + /** + * This enumeration represents the MAC frame type. + * + * Values match the Frame Type field in Frame Control Field (FCF) as an `uint16_t`. + * + */ + enum Type : uint16_t + { + kTypeBeacon = 0, ///< Beacon Frame Type. + kTypeData = 1, ///< Data Frame Type. + kTypeAck = 2, ///< Ack Frame Type. + kTypeMacCmd = 3, ///< MAC Command Frame Type. + }; - static constexpr uint8_t kKeySourceSizeMode0 = 0; - static constexpr uint8_t kKeySourceSizeMode1 = 0; - static constexpr uint8_t kKeySourceSizeMode2 = 4; - static constexpr uint8_t kKeySourceSizeMode3 = 8; + /** + * This enumeration represents the MAC frame version. + * + * Values match the Version field in Frame Control Field (FCF) as an `uint16_t`. + * + */ + enum Version : uint16_t + { + kVersion2003 = 0 << 12, ///< 2003 Frame Version. + kVersion2006 = 1 << 12, ///< 2006 Frame Version. + kVersion2015 = 2 << 12, ///< 2015 Frame Version. + }; - static constexpr uint8_t kKeyIndexSize = sizeof(uint8_t); + /** + * This enumeration represents the MAC frame security level. + * + * Values match the Security Level field in Security Control Field as an `uint8_t`. + * + */ + enum SecurityLevel : uint8_t + { + kSecurityNone = 0, ///< No security. + kSecurityMic32 = 1, ///< No encryption, MIC-32 authentication. + kSecurityMic64 = 2, ///< No encryption, MIC-64 authentication. + kSecurityMic128 = 3, ///< No encryption, MIC-128 authentication. + kSecurityEnc = 4, ///< Encryption, no authentication + kSecurityEncMic32 = 5, ///< Encryption with MIC-32 authentication. + kSecurityEncMic64 = 6, ///< Encryption with MIC-64 authentication. + kSecurityEncMic128 = 7, ///< Encryption with MIC-128 authentication. + }; - static constexpr uint8_t kMacCmdAssociationRequest = 1; - static constexpr uint8_t kMacCmdAssociationResponse = 2; - static constexpr uint8_t kMacCmdDisassociationNotification = 3; - static constexpr uint8_t kMacCmdDataRequest = 4; - static constexpr uint8_t kMacCmdPanidConflictNotification = 5; - static constexpr uint8_t kMacCmdOrphanNotification = 6; - static constexpr uint8_t kMacCmdBeaconRequest = 7; - static constexpr uint8_t kMacCmdCoordinatorRealignment = 8; - static constexpr uint8_t kMacCmdGtsRequest = 9; + /** + * This enumeration represents the MAC frame security key identifier mode. + * + * Values match the Key Identifier Mode field in Security Control Field as an `uint8_t`. + * + */ + enum KeyIdMode : uint8_t + { + kKeyIdMode0 = 0 << 3, ///< Key ID Mode 0 - Key is determined implicitly. + kKeyIdMode1 = 1 << 3, ///< Key ID Mode 1 - Key is determined from Key Index field. + kKeyIdMode2 = 2 << 3, ///< Key ID Mode 2 - Key is determined from 4-bytes Key Source and Index fields. + kKeyIdMode3 = 3 << 3, ///< Key ID Mode 3 - Key is determined from 8-bytes Key Source and Index fields. + }; - static constexpr uint8_t kImmAckLength = kFcfSize + kDsnSize + k154FcsSize; + /** + * This enumeration represents a subset of MAC Command Identifiers. + * + */ + enum CommandId : uint8_t + { + kMacCmdAssociationRequest = 1, + kMacCmdAssociationResponse = 2, + kMacCmdDisassociationNotification = 3, + kMacCmdDataRequest = 4, + kMacCmdPanidConflictNotification = 5, + kMacCmdOrphanNotification = 6, + kMacCmdBeaconRequest = 7, + kMacCmdCoordinatorRealignment = 8, + kMacCmdGtsRequest = 9, + }; static constexpr uint16_t kInfoStringSize = 128; ///< Max chars for `InfoString` (ToInfoString()). @@ -365,11 +371,26 @@ class Frame : public otRadioFrame /** * This method initializes the MAC header. * - * @param[in] aFcf The Frame Control field. - * @param[in] aSecurityControl The Security Control field. + * This method determines and writes the Frame Control Field (FCF) and Security Control in the frame along with + * given source and destination addresses and PAN IDs. + * + * The Ack Request bit in FCF is set if there is destination and it is not broadcast. The Frame Pending and IE + * Present bits are not set. + * + * @param[in] aType Frame type. + * @param[in] aVerion Frame version. + * @param[in] aAddrs Frame source and destination addresses (each can be none, short, or extended). + * @param[in] aPanIds Source and destination PAN IDs. + * @param[in] aSecurityLevel Frame security level. + * @param[in] aKeyIdMode Frame security key ID mode. * */ - void InitMacHeader(uint16_t aFcf, uint8_t aSecurityControl); + void InitMacHeader(Type aType, + Version aVersion, + const Addresses &aAddrs, + const PanIds &aPanIds, + SecurityLevel aSecurityLevel, + KeyIdMode aKeyIdMode = kKeyIdMode0); /** * This method validates the frame. @@ -395,7 +416,7 @@ class Frame : public otRadioFrame * @retval FALSE If this is not an Ack. * */ - bool IsAck(void) const { return GetType() == kFcfFrameAck; } + bool IsAck(void) const { return GetType() == kTypeAck; } /** * This method returns the IEEE 802.15.4 Frame Version. @@ -815,14 +836,6 @@ class Frame : public otRadioFrame */ uint8_t GetChannel(void) const { return mChannel; } - /** - * This method sets the IEEE 802.15.4 channel used for transmission or reception. - * - * @param[in] aChannel The IEEE 802.15.4 channel used for transmission or reception. - * - */ - void SetChannel(uint8_t aChannel) { mChannel = aChannel; } - /** * This method returns the IEEE 802.15.4 PSDU length. * @@ -918,6 +931,8 @@ class Frame : public otRadioFrame /** * This template method appends an Header IE at specified index in this frame. * + * This method also sets the IE present bit in the Frame Control Field (FCF). + * * @param[in,out] aIndex The index to append IE. If `aIndex` is `0` on input, this method finds the index * for the first IE and appends the IE at that position. If the position is not found * successfully, `aIndex` will be set to `kInvalidIndex`. Otherwise the IE will be @@ -1066,6 +1081,46 @@ class Frame : public otRadioFrame uint16_t GetFrameControlField(void) const; protected: + static constexpr uint8_t kFcfSize = sizeof(uint16_t); + static constexpr uint8_t kDsnSize = sizeof(uint8_t); + static constexpr uint8_t kSecurityControlSize = sizeof(uint8_t); + static constexpr uint8_t kFrameCounterSize = sizeof(uint32_t); + static constexpr uint8_t kCommandIdSize = sizeof(uint8_t); + static constexpr uint8_t k154FcsSize = sizeof(uint16_t); + static constexpr uint8_t kKeyIndexSize = sizeof(uint8_t); + + static constexpr uint16_t kFcfFrameTypeMask = 7 << 0; + static constexpr uint16_t kFcfSecurityEnabled = 1 << 3; + static constexpr uint16_t kFcfFramePending = 1 << 4; + static constexpr uint16_t kFcfAckRequest = 1 << 5; + static constexpr uint16_t kFcfPanidCompression = 1 << 6; + static constexpr uint16_t kFcfIePresent = 1 << 9; + static constexpr uint16_t kFcfDstAddrNone = 0 << 10; + static constexpr uint16_t kFcfDstAddrShort = 2 << 10; + static constexpr uint16_t kFcfDstAddrExt = 3 << 10; + static constexpr uint16_t kFcfDstAddrMask = 3 << 10; + static constexpr uint16_t kFcfFrameVersionMask = 3 << 12; + static constexpr uint16_t kFcfSrcAddrNone = 0 << 14; + static constexpr uint16_t kFcfSrcAddrShort = 2 << 14; + static constexpr uint16_t kFcfSrcAddrExt = 3 << 14; + static constexpr uint16_t kFcfSrcAddrMask = 3 << 14; + + static constexpr uint8_t kSecLevelMask = 7 << 0; + static constexpr uint8_t kKeyIdModeMask = 3 << 3; + + static constexpr uint8_t kMic0Size = 0; + static constexpr uint8_t kMic32Size = 32 / CHAR_BIT; + static constexpr uint8_t kMic64Size = 64 / CHAR_BIT; + static constexpr uint8_t kMic128Size = 128 / CHAR_BIT; + static constexpr uint8_t kMaxMicSize = kMic128Size; + + static constexpr uint8_t kKeySourceSizeMode0 = 0; + static constexpr uint8_t kKeySourceSizeMode1 = 0; + static constexpr uint8_t kKeySourceSizeMode2 = 4; + static constexpr uint8_t kKeySourceSizeMode3 = 8; + + static constexpr uint8_t kImmAckLength = kFcfSize + kDsnSize + k154FcsSize; + static constexpr uint8_t kInvalidIndex = 0xff; static constexpr uint8_t kInvalidSize = kInvalidIndex; static constexpr uint8_t kMaxPsduSize = kInvalidSize - 1; @@ -1092,7 +1147,7 @@ class Frame : public otRadioFrame static bool IsDstPanIdPresent(uint16_t aFcf); static bool IsSrcAddrPresent(uint16_t aFcf) { return (aFcf & kFcfSrcAddrMask) != kFcfSrcAddrNone; } static bool IsSrcPanIdPresent(uint16_t aFcf); - static bool IsVersion2015(uint16_t aFcf) { return (aFcf & kFcfFrameVersionMask) == kFcfFrameVersion2015; } + static bool IsVersion2015(uint16_t aFcf) { return (aFcf & kFcfFrameVersionMask) == kVersion2015; } static uint8_t CalculateAddrFieldSize(uint16_t aFcf); static uint8_t CalculateSecurityHeaderSize(uint8_t aSecurityControl); @@ -1151,8 +1206,9 @@ class RxFrame : public Frame /** * This method returns the timestamp when the frame was received. + * The timestamp marks the frame detection time: the end of the last symbol of SFD. * - * @returns The timestamp when the frame was received, in microseconds. + * @returns The timestamp when the frame SFD was received, in microseconds. * */ const uint64_t &GetTimestamp(void) const { return mInfo.mRxInfo.mTimestamp; } @@ -1199,6 +1255,36 @@ class RxFrame : public Frame class TxFrame : public Frame { public: + /** + * This method sets the channel on which to send the frame. + * + * It also sets the `RxChannelAfterTxDone` to the same channel. + * + * @param[in] aChannel The channel used for transmission. + * + */ + void SetChannel(uint8_t aChannel) + { + mChannel = aChannel; + SetRxChannelAfterTxDone(aChannel); + } + + /** + * This method gets the RX channel after frame TX is done. + * + * @returns The RX channel after frame TX is done. + * + */ + uint8_t GetRxChannelAfterTxDone(void) const { return mInfo.mTxInfo.mRxChannelAfterTxDone; } + + /** + * This method sets the RX channel after frame TX is done. + * + * @param[in] aChannel The RX channel after frame TX is done. + * + */ + void SetRxChannelAfterTxDone(uint8_t aChannel) { mInfo.mTxInfo.mRxChannelAfterTxDone = aChannel; } + /** * This method returns the maximum number of backoffs the CSMA-CA algorithm will attempt before declaring a channel * access failure. diff --git a/src/core/mac/mac_links.hpp b/src/core/mac/mac_links.hpp index 99406f7ad84..cc30ef0cd1a 100644 --- a/src/core/mac/mac_links.hpp +++ b/src/core/mac/mac_links.hpp @@ -41,6 +41,7 @@ #include "mac/mac_frame.hpp" #include "mac/mac_types.hpp" #include "mac/sub_mac.hpp" +#include "radio/radio.hpp" #include "radio/trel_link.hpp" namespace ot { @@ -291,8 +292,6 @@ class Links : public InstanceLocator friend class ot::Instance; public: - static const int8_t kInvalidRssiValue = SubMac::kInvalidRssiValue; ///< Invalid RSSI value. - /** * This constructor initializes the `Links` object. * @@ -576,7 +575,7 @@ class Links : public InstanceLocator /** * This method gets the most recent RSSI measurement from radio link. * - * @returns The RSSI in dBm when it is valid. `kInvalidRssiValue` when RSSI is invalid. + * @returns The RSSI in dBm when it is valid. `Radio::kInvalidRssi` when RSSI is invalid. * */ int8_t GetRssi(void) const @@ -585,7 +584,7 @@ class Links : public InstanceLocator #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE mSubMac.GetRssi(); #else - kInvalidRssiValue; + Radio::kInvalidRssi; #endif } @@ -620,7 +619,7 @@ class Links : public InstanceLocator * @returns The noise floor value in dBm. * */ - int8_t GetNoiseFloor(void) + int8_t GetNoiseFloor(void) const { return #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE @@ -682,7 +681,7 @@ class Links : public InstanceLocator #endif private: - static constexpr int8_t kDefaultNoiseFloor = -100; + static constexpr int8_t kDefaultNoiseFloor = Radio::kDefaultReceiveSensitivity; SubMac mSubMac; #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE diff --git a/src/core/mac/mac_types.cpp b/src/core/mac/mac_types.cpp index 83fab4ba99d..7ed4b1b31b6 100644 --- a/src/core/mac/mac_types.cpp +++ b/src/core/mac/mac_types.cpp @@ -299,7 +299,7 @@ void KeyMaterial::SetFrom(const Key &aKey, bool aIsExportable) #endif } -void KeyMaterial::ExtractKey(Key &aKey) +void KeyMaterial::ExtractKey(Key &aKey) const { #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE aKey.Clear(); diff --git a/src/core/mac/mac_types.hpp b/src/core/mac/mac_types.hpp index 483a5b97623..6a73244d2b6 100644 --- a/src/core/mac/mac_types.hpp +++ b/src/core/mac/mac_types.hpp @@ -411,6 +411,26 @@ class Address Type mType; ///< The address type (Short, Extended, or none). }; +/** + * This structure represents two MAC addresses corresponding to source and destination. + * + */ +struct Addresses +{ + Address mSource; ///< Source address. + Address mDestination; ///< Destination address. +}; + +/** + * This structure represents two PAN IDs corresponding to source and destination. + * + */ +struct PanIds +{ + PanId mSource; ///< Source PAN ID. + PanId mDestination; ///< Destination PAN ID. +}; + /** * This class represents a MAC key. * @@ -520,7 +540,7 @@ class KeyMaterial : public otMacKeyMaterial, public Unequatable * @param[out] aKey A reference to the output the key. * */ - void ExtractKey(Key &aKey); + void ExtractKey(Key &aKey) const; /** * This method converts `KeyMaterial` to a `Crypto::Key`. @@ -856,6 +876,73 @@ class LinkFrameCounters #endif }; +/** + * This class represents CSL accuracy. + * + */ +class CslAccuracy +{ +public: + static constexpr uint8_t kWorstClockAccuracy = 255; ///< Worst possible crystal accuracy, in units of ± ppm. + static constexpr uint8_t kWorstUncertainty = 255; ///< Worst possible uncertainty, in units of 10 microseconds. + + /** + * This method initializes the CSL accuracy using `kWorstClockAccuracy` and `kWorstUncertainty` values. + * + */ + void Init(void) + { + mClockAccuracy = kWorstClockAccuracy; + mUncertainty = kWorstUncertainty; + } + + /** + * This method returns the CSL clock accuracy. + * + * @returns The CSL clock accuracy in ± ppm. + * + */ + uint8_t GetClockAccuracy(void) const { return mClockAccuracy; } + + /** + * This method sets the CSL clock accuracy. + * + * @param[in] aClockAccuracy The CSL clock accuracy in ± ppm. + * + */ + void SetClockAccuracy(uint8_t aClockAccuracy) { mClockAccuracy = aClockAccuracy; } + + /** + * This method returns the CSL uncertainty. + * + * @returns The uncertainty in units 10 microseconds. + * + */ + uint8_t GetUncertainty(void) const { return mUncertainty; } + + /** + * This method gets the CLS uncertainty in microseconds. + * + * @returns the CLS uncertainty in microseconds. + * + */ + uint16_t GetUncertaintyInMicrosec(void) const { return static_cast(mUncertainty) * kUsPerUncertUnit; } + + /** + * This method sets the CSL uncertainty. + * + * @param[in] aUncertainty The CSL uncertainty in units 10 microseconds. + * + */ + void SetUncertainty(uint8_t aUncertainty) { mUncertainty = aUncertainty; } + +private: + static constexpr uint8_t kUsPerUncertUnit = 10; + + uint8_t mClockAccuracy; + uint8_t mUncertainty; +}; + /** * @} * diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index 7d5cccdc941..7d7ac175083 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -42,6 +42,7 @@ #include "common/instance.hpp" #include "common/locator_getters.hpp" #include "common/log.hpp" +#include "common/num_utils.hpp" #include "common/random.hpp" #include "common/time.hpp" #include "mac/mac_frame.hpp" @@ -56,15 +57,15 @@ SubMac::SubMac(Instance &aInstance) , mRadioCaps(Get().GetCaps()) , mTransmitFrame(Get().GetTransmitBuffer()) , mCallbacks(aInstance) - , mPcapCallback(nullptr) - , mPcapCallbackContext(nullptr) - , mTimer(aInstance, SubMac::HandleTimer) + , mTimer(aInstance) #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - , mCslParentAccuracy(kCslWorstCrystalPpm) - , mCslParentUncert(kCslWorstUncertainty) , mCslTimer(aInstance, SubMac::HandleCslTimer) #endif { +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + mCslParentAccuracy.Init(); +#endif + Init(); } @@ -76,7 +77,7 @@ void SubMac::Init(void) mShortAddress = kShortAddrInvalid; mExtAddress.Clear(); mRxOnWhenBackoff = true; - mEnergyScanMaxRssi = kInvalidRssiValue; + mEnergyScanMaxRssi = Radio::kInvalidRssi; mEnergyScanEndTime = Time{0}; #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY mRetxDelayBackOffExponent = kRetxDelayMinBackoffExponent; @@ -175,12 +176,6 @@ void SubMac::SetExtAddress(const ExtAddress &aExtAddress) LogDebg("RadioExtAddress: %s", mExtAddress.ToString().AsCString()); } -void SubMac::SetPcapCallback(otLinkPcapCallback aPcapCallback, void *aCallbackContext) -{ - mPcapCallback = aPcapCallback; - mPcapCallbackContext = aCallbackContext; -} - Error SubMac::Enable(void) { Error error = kErrorNone; @@ -283,14 +278,14 @@ void SubMac::CslSample(void) void SubMac::HandleReceiveDone(RxFrame *aFrame, Error aError) { - if (mPcapCallback && (aFrame != nullptr) && (aError == kErrorNone)) + if (mPcapCallback.IsSet() && (aFrame != nullptr) && (aError == kErrorNone)) { - mPcapCallback(aFrame, false, mPcapCallbackContext); + mPcapCallback.Invoke(aFrame, false); } if (!ShouldHandleTransmitSecurity() && aFrame != nullptr && aFrame->mInfo.mRxInfo.mAckedWithSecEnhAck) { - SignalFrameCounterUsed(aFrame->mInfo.mRxInfo.mAckFrameCounter); + SignalFrameCounterUsed(aFrame->mInfo.mRxInfo.mAckFrameCounter, aFrame->mInfo.mRxInfo.mAckKeyId); } #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE @@ -304,10 +299,11 @@ void SubMac::HandleReceiveDone(RxFrame *aFrame, Error aError) #if OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE // Split the log into two lines for RTT to output - LogDebg("Received frame in state (SubMac %s, CSL %s), timestamp %u", StateToString(mState), - mIsCslSampling ? "CslSample" : "CslSleep", static_cast(aFrame->mInfo.mRxInfo.mTimestamp)); - LogDebg("Target sample start time %u, time drift %d", mCslSampleTime.GetValue(), - static_cast(aFrame->mInfo.mRxInfo.mTimestamp) - mCslSampleTime.GetValue()); + LogDebg("Received frame in state (SubMac %s, CSL %s), timestamp %lu", StateToString(mState), + mIsCslSampling ? "CslSample" : "CslSleep", + ToUlong(static_cast(aFrame->mInfo.mRxInfo.mTimestamp))); + LogDebg("Target sample start time %lu, time drift %lu", ToUlong(mCslSampleTime.GetValue()), + ToUlong(static_cast(aFrame->mInfo.mRxInfo.mTimestamp) - mCslSampleTime.GetValue())); #endif } #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE @@ -335,16 +331,15 @@ Error SubMac::Send(void) #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY case kStateDelayBeforeRetx: #endif - case kStateEnergyScan: - ExitNow(error = kErrorInvalidState); - OT_UNREACHABLE_CODE(break); - case kStateSleep: case kStateReceive: #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE case kStateCslSample: #endif break; + + case kStateEnergyScan: + ExitNow(error = kErrorInvalidState); } #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE @@ -395,7 +390,7 @@ void SubMac::ProcessTransmitSecurity(void) uint32_t frameCounter = GetFrameCounter(); mTransmitFrame.SetFrameCounter(frameCounter); - SignalFrameCounterUsed(frameCounter); + SignalFrameCounterUsed(frameCounter, mKeyId); } extAddress = &GetExtAddress(); @@ -424,9 +419,10 @@ void SubMac::StartCsmaBackoff(void) { if (Time(static_cast(otPlatRadioGetNow(&GetInstance()))) < Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime) + mTransmitFrame.mInfo.mTxInfo.mTxDelay - - kCcaSampleInterval) + kCcaSampleInterval - kCslTransmitTimeAhead) { - mTimer.StartAt(Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime) - kCcaSampleInterval, + mTimer.StartAt(Time(mTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime) - kCcaSampleInterval - + kCslTransmitTimeAhead, mTransmitFrame.mInfo.mTxInfo.mTxDelay); } else // Transmit without delay @@ -483,7 +479,7 @@ void SubMac::StartTimerForBackoff(uint8_t aBackoffExponent) #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY if (mState == kStateDelayBeforeRetx) { - LogDebg("Delaying retx for %u usec (be=%d)", backoff, aBackoffExponent); + LogDebg("Delaying retx for %lu usec (be=%u)", ToUlong(backoff), aBackoffExponent); } #endif } @@ -505,9 +501,9 @@ void SubMac::BeginTransmit(void) SetState(kStateTransmit); - if (mPcapCallback) + if (mPcapCallback.IsSet()) { - mPcapCallback(&mTransmitFrame, true, mPcapCallbackContext); + mPcapCallback.Invoke(&mTransmitFrame, true); } error = Get().Transmit(mTransmitFrame); @@ -612,7 +608,8 @@ void SubMac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aErro { SetState(kStateDelayBeforeRetx); StartTimerForBackoff(mRetxDelayBackOffExponent); - mRetxDelayBackOffExponent = OT_MIN(mRetxDelayBackOffExponent + 1, kRetxDelayMaxBackoffExponent); + mRetxDelayBackOffExponent = + Min(static_cast(mRetxDelayBackOffExponent + 1), kRetxDelayMaxBackoffExponent); ExitNow(); } #endif @@ -623,6 +620,19 @@ void SubMac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aErro SetState(kStateReceive); +#if OPENTHREAD_RADIO + if (aFrame.GetChannel() != aFrame.GetRxChannelAfterTxDone()) + { + // On RCP build, we switch immediately to the specified RX + // channel if it is different from the channel on which frame + // was sent. On FTD or MTD builds we don't need to do + // the same as the `Mac` will switch the channel from the + // `mCallbacks.TransmitDone()`. + + IgnoreError(Get().Receive(aFrame.GetRxChannelAfterTxDone())); + } +#endif + mCallbacks.TransmitDone(aFrame, aAckFrame, aError); exit: @@ -632,6 +642,7 @@ void SubMac::HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aErro void SubMac::SignalFrameCounterUsedOnTxDone(const TxFrame &aFrame) { uint8_t keyIdMode; + uint8_t keyId; uint32_t frameCounter; bool allowError = false; @@ -656,7 +667,9 @@ void SubMac::SignalFrameCounterUsedOnTxDone(const TxFrame &aFrame) VerifyOrExit(keyIdMode == Frame::kKeyIdMode1); VerifyOrExit(aFrame.GetFrameCounter(frameCounter) == kErrorNone, OT_ASSERT(allowError)); - SignalFrameCounterUsed(frameCounter); + VerifyOrExit(aFrame.GetKeyId(keyId) == kErrorNone, OT_ASSERT(allowError)); + + SignalFrameCounterUsed(frameCounter, keyId); exit: return; @@ -669,7 +682,7 @@ int8_t SubMac::GetRssi(void) const #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE if (mRadioFilterEnabled) { - rssi = kInvalidRssiValue; + rssi = Radio::kInvalidRssi; } else #endif @@ -680,10 +693,7 @@ int8_t SubMac::GetRssi(void) const return rssi; } -int8_t SubMac::GetNoiseFloor(void) -{ - return Get().GetReceiveSensitivity(); -} +int8_t SubMac::GetNoiseFloor(void) const { return Get().GetReceiveSensitivity(); } Error SubMac::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration) { @@ -712,7 +722,7 @@ Error SubMac::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration) } #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE - VerifyOrExit(!mRadioFilterEnabled, HandleEnergyScanDone(kInvalidRssiValue)); + VerifyOrExit(!mRadioFilterEnabled, HandleEnergyScanDone(Radio::kInvalidRssi)); #endif if (RadioSupportsEnergyScan()) @@ -725,7 +735,7 @@ Error SubMac::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration) SuccessOrAssert(Get().Receive(aScanChannel)); SetState(kStateEnergyScan); - mEnergyScanMaxRssi = kInvalidRssiValue; + mEnergyScanMaxRssi = Radio::kInvalidRssi; mEnergyScanEndTime = TimerMilli::GetNow() + static_cast(aScanDuration); mTimer.Start(0); } @@ -744,9 +754,9 @@ void SubMac::SampleRssi(void) int8_t rssi = GetRssi(); - if (rssi != kInvalidRssiValue) + if (rssi != Radio::kInvalidRssi) { - if ((mEnergyScanMaxRssi == kInvalidRssiValue) || (rssi > mEnergyScanMaxRssi)) + if ((mEnergyScanMaxRssi == Radio::kInvalidRssi) || (rssi > mEnergyScanMaxRssi)) { mEnergyScanMaxRssi = rssi; } @@ -772,11 +782,6 @@ void SubMac::HandleEnergyScanDone(int8_t aMaxRssi) mCallbacks.EnergyScanDone(aMaxRssi); } -void SubMac::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void SubMac::HandleTimer(void) { switch (mState) @@ -959,8 +964,10 @@ void SubMac::SetMacKey(uint8_t aKeyIdMode, return; } -void SubMac::SignalFrameCounterUsed(uint32_t aFrameCounter) +void SubMac::SignalFrameCounterUsed(uint32_t aFrameCounter, uint8_t aKeyId) { + VerifyOrExit(aKeyId == mKeyId); + mCallbacks.FrameCounterUsed(aFrameCounter); // It not always guaranteed that this method is invoked in order @@ -978,13 +985,23 @@ void SubMac::SignalFrameCounterUsed(uint32_t aFrameCounter) return; } -void SubMac::SetFrameCounter(uint32_t aFrameCounter) +void SubMac::SetFrameCounter(uint32_t aFrameCounter, bool aSetIfLarger) { - mFrameCounter = aFrameCounter; + if (!aSetIfLarger || (aFrameCounter > mFrameCounter)) + { + mFrameCounter = aFrameCounter; + } VerifyOrExit(!ShouldHandleTransmitSecurity()); - Get().SetMacFrameCounter(aFrameCounter); + if (aSetIfLarger) + { + Get().SetMacFrameCounterIfLarger(aFrameCounter); + } + else + { + Get().SetMacFrameCounter(aFrameCounter); + } exit: return; @@ -1075,10 +1092,7 @@ bool SubMac::UpdateCsl(uint16_t aPeriod, uint8_t aChannel, otShortAddress aShort return retval; } -void SubMac::HandleCslTimer(Timer &aTimer) -{ - aTimer.Get().HandleCslTimer(); -} +void SubMac::HandleCslTimer(Timer &aTimer) { aTimer.Get().HandleCslTimer(); } void SubMac::HandleCslTimer(void) { @@ -1090,6 +1104,32 @@ void SubMac::HandleCslTimer(void) * | | | | | | | | * ---|-----------|------------|-----------|-----------|------------|------------|----------//------------|--- * -timeAhead CslPhase +timeAfter -timeAhead + * + * The handler works in different ways when the radio supports receive-timing and doesn't. + * + * When the radio supports receive-timing: + * The handler will be called once per CSL period. When the handler is called, it will set the timer to + * fire at the next CSL sample time and call `Radio::ReceiveAt` to start sampling for the current CSL period. + * The timer fires some time before the actual sample time. After `Radio::ReceiveAt` is called, the radio will + * remain in sleep state until the actual sample time. + * Note that it never call `Radio::Sleep` explicitly. The radio will fall into sleep after `ReceiveAt` ends. This + * will be done by the platform as part of the `otPlatRadioReceiveAt` API. + * + * Timer fires Timer fires + * ^ ^ + * x-|------------|-------------------------------------x-|------------|---------------------------------------| + * sample sleep sample sleep + * + * When the radio doesn't support receive-timing: + * The handler will be called twice per CSL period: at the beginning of sample and sleep. When the handler is + * called, it will explicitly change the radio state due to the current state by calling `Radio::Receive` or + * `Radio::Sleep`. + * + * Timer fires Timer fires Timer fires Timer fires + * ^ ^ ^ ^ + * |------------|---------------------------------------|------------|---------------------------------------| + * sample sleep sample sleep + * */ uint32_t periodUs = mCslPeriod * kUsPerTenSymbols; uint32_t timeAhead, timeAfter; @@ -1105,7 +1145,7 @@ void SubMac::HandleCslTimer(void) #if !OPENTHREAD_CONFIG_MAC_CSL_DEBUG_ENABLE IgnoreError(Get().Sleep()); // Don't actually sleep for debugging #endif - LogDebg("CSL sleep %u", mCslTimer.GetNow().GetValue()); + LogDebg("CSL sleep %lu", ToUlong(mCslTimer.GetNow().GetValue())); } } else @@ -1125,7 +1165,9 @@ void SubMac::HandleCslTimer(void) Get().UpdateCslSampleTime(mCslSampleTime.GetValue()); - if (RadioSupportsReceiveTiming() && (mState != kStateDisabled)) + // Schedule reception window for any state except RX - so that CSL RX Window has lower priority + // than scanning or RX after the data poll. + if (RadioSupportsReceiveTiming() && (mState != kStateDisabled) && (mState != kStateReceive)) { IgnoreError(Get().ReceiveAt(mCslChannel, mCslSampleTime.GetValue() - periodUs - timeAhead, timeAhead + timeAfter)); @@ -1133,7 +1175,8 @@ void SubMac::HandleCslTimer(void) else if (mState == kStateCslSample) { IgnoreError(Get().Receive(mCslChannel)); - LogDebg("CSL sample %u, duration %u", mCslTimer.GetNow().GetValue(), timeAhead + timeAfter); + LogDebg("CSL sample %lu, duration %lu", ToUlong(mCslTimer.GetNow().GetValue()), + ToUlong(timeAhead + timeAfter)); } } } @@ -1147,12 +1190,13 @@ void SubMac::GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter) elapsed = curTime - mCslLastSync.GetValue(); - semiWindow = static_cast(static_cast(elapsed) * - (Get().GetCslAccuracy() + mCslParentAccuracy) / 1000000); - semiWindow += mCslParentUncert * kUsPerUncertUnit; + semiWindow = + static_cast(static_cast(elapsed) * + (Get().GetCslAccuracy() + mCslParentAccuracy.GetClockAccuracy()) / 1000000); + semiWindow += mCslParentAccuracy.GetUncertaintyInMicrosec() + Get().GetCslUncertainty() * 10; - aAhead = (semiWindow + kCslReceiveTimeAhead > semiPeriod) ? semiPeriod : semiWindow + kCslReceiveTimeAhead; - aAfter = (semiWindow + kMinCslWindow > semiPeriod) ? semiPeriod : semiWindow + kMinCslWindow; + aAhead = Min(semiPeriod, semiWindow + kCslReceiveTimeAhead); + aAfter = Min(semiPeriod, semiWindow + kMinCslWindow); } #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE diff --git a/src/core/mac/sub_mac.hpp b/src/core/mac/sub_mac.hpp index 8eccfe9f58b..e5632105844 100644 --- a/src/core/mac/sub_mac.hpp +++ b/src/core/mac/sub_mac.hpp @@ -40,6 +40,7 @@ #include +#include "common/callback.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" #include "common/timer.hpp" @@ -108,8 +109,6 @@ class SubMac : public InstanceLocator, private NonCopyable friend class LinkRaw; public: - static constexpr int8_t kInvalidRssiValue = 127; ///< Invalid Received Signal Strength Indicator (RSSI) value. - /** * This class defines the callbacks notifying `SubMac` user of changes and events. * @@ -167,7 +166,7 @@ class SubMac : public InstanceLocator, private NonCopyable * */ void RecordFrameTransmitStatus(const TxFrame &aFrame, - const RxFrame *aAckFrame, + RxFrame *aAckFrame, Error aError, uint8_t aRetryCount, bool aWillRetx); @@ -189,7 +188,7 @@ class SubMac : public InstanceLocator, private NonCopyable /** * This method notifies user of `SubMac` that energy scan is complete. * - * @param[in] aMaxRssi Maximum RSSI seen on the channel, or `SubMac::kInvalidRssiValue` if failed. + * @param[in] aMaxRssi Maximum RSSI seen on the channel, or `Radio::kInvalidRssi` if failed. * */ void EnergyScanDone(int8_t aMaxRssi); @@ -278,7 +277,10 @@ class SubMac : public InstanceLocator, private NonCopyable * @param[in] aCallbackContext A pointer to application-specific context. * */ - void SetPcapCallback(otLinkPcapCallback aPcapCallback, void *aCallbackContext); + void SetPcapCallback(otLinkPcapCallback aPcapCallback, void *aCallbackContext) + { + mPcapCallback.Set(aPcapCallback, aCallbackContext); + } /** * This method indicates whether radio should stay in Receive or Sleep during CSMA backoff. @@ -367,7 +369,7 @@ class SubMac : public InstanceLocator, private NonCopyable /** * This method gets the most recent RSSI measurement. * - * @returns The RSSI in dBm when it is valid. `kInvalidRssiValue` when RSSI is invalid. + * @returns The RSSI in dBm when it is valid. `Radio::kInvalidRssi` when RSSI is invalid. * */ int8_t GetRssi(void) const; @@ -392,7 +394,7 @@ class SubMac : public InstanceLocator, private NonCopyable * @returns The noise floor value in dBm. * */ - int8_t GetNoiseFloor(void); + int8_t GetNoiseFloor(void) const; #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE /** @@ -418,36 +420,20 @@ class SubMac : public InstanceLocator, private NonCopyable void CslSample(void); /** - * This method returns CSL parent clock accuracy, in ± ppm. - * - * @retval CSL parent clock accuracy. - * - */ - uint8_t GetCslParentClockAccuracy(void) const { return mCslParentAccuracy; } - - /** - * This method sets CSL parent clock accuracy, in ± ppm. + * This method returns parent CSL accuracy (clock accuracy and uncertainty). * - * @param[in] aCslParentAccuracy CSL parent clock accuracy, in ± ppm. + * @returns The parent CSL accuracy. * */ - void SetCslParentClockAccuracy(uint8_t aCslParentAccuracy) { mCslParentAccuracy = aCslParentAccuracy; } + const CslAccuracy &GetCslParentAccuracy(void) const { return mCslParentAccuracy; } /** - * This method sets CSL parent uncertainty, in ±10 us units. + * This method sets parent CSL accuracy. * - * @retval CSL parent uncertainty, in ±10 us units. + * @param[in] aCslAccuracy The parent CSL accuracy. * */ - uint8_t GetCslParentUncertainty(void) const { return mCslParentUncert; } - - /** - * This method returns CSL parent uncertainty, in ±10 us units. - * - * @param[in] aCslParentUncert CSL parent uncertainty, in ±10 us units. - * - */ - void SetCslParentUncertainty(uint8_t aCslParentUncert) { mCslParentUncert = aCslParentUncert; } + void SetCslParentAccuracy(const CslAccuracy &aCslAccuracy) { mCslParentAccuracy = aCslAccuracy; } #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE @@ -491,6 +477,17 @@ class SubMac : public InstanceLocator, private NonCopyable */ const KeyMaterial &GetNextMacKey(void) const { return mNextKey; } + /** + * This method clears the stored MAC keys. + * + */ + void ClearMacKeys(void) + { + mPrevKey.Clear(); + mCurrKey.Clear(); + mNextKey.Clear(); + } + /** * This method returns the current MAC frame counter value. * @@ -503,9 +500,11 @@ class SubMac : public InstanceLocator, private NonCopyable * This method sets the current MAC Frame Counter value. * * @param[in] aFrameCounter The MAC Frame Counter value. + * @param[in] aSetIfLarger If `true`, set only if the new value @p aFrameCounter is larger than the current value. + * If `false`, set the new value independent of the current value. * */ - void SetFrameCounter(uint32_t aFrameCounter); + void SetFrameCounter(uint32_t aFrameCounter, bool aSetIfLarger); #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE /** @@ -583,6 +582,13 @@ class SubMac : public InstanceLocator, private NonCopyable static constexpr uint32_t kCslReceiveTimeAhead = OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD; #endif +#if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE + // CSL transmitter would schedule delayed transmission `kCslTransmitTimeAhead` earlier + // than expected delayed transmit time. The value is in usec. + // Only for radios not supporting OT_RADIO_CAPS_TRANSMIT_TIMING. + static constexpr uint32_t kCslTransmitTimeAhead = OPENTHREAD_CONFIG_CSL_TRANSMIT_TIME_AHEAD; +#endif + /** * This method initializes the states of the sub-MAC layer. * @@ -609,7 +615,7 @@ class SubMac : public InstanceLocator, private NonCopyable bool ShouldHandleTransmitTargetTime(void) const; void ProcessTransmitSecurity(void); - void SignalFrameCounterUsed(uint32_t aFrameCounter); + void SignalFrameCounterUsed(uint32_t aFrameCounter, uint8_t aKeyId); void StartCsmaBackoff(void); void StartTimerForBackoff(uint8_t aBackoffExponent); void BeginTransmit(void); @@ -620,13 +626,18 @@ class SubMac : public InstanceLocator, private NonCopyable void HandleTransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error aError); void SignalFrameCounterUsedOnTxDone(const TxFrame &aFrame); void HandleEnergyScanDone(int8_t aMaxRssi); - - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); + void HandleTimer(void); void SetState(State aState); static const char *StateToString(State aState); + using SubMacTimer = +#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE + TimerMicroIn; +#else + TimerMilliIn; +#endif + otRadioCaps mRadioCaps; State mState; uint8_t mCsmaBackoffs; @@ -637,37 +648,31 @@ class SubMac : public InstanceLocator, private NonCopyable #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE bool mRadioFilterEnabled : 1; #endif - int8_t mEnergyScanMaxRssi; - TimeMilli mEnergyScanEndTime; - TxFrame & mTransmitFrame; - Callbacks mCallbacks; - otLinkPcapCallback mPcapCallback; - void * mPcapCallbackContext; - KeyMaterial mPrevKey; - KeyMaterial mCurrKey; - KeyMaterial mNextKey; - uint32_t mFrameCounter; - uint8_t mKeyId; + int8_t mEnergyScanMaxRssi; + TimeMilli mEnergyScanEndTime; + TxFrame &mTransmitFrame; + Callbacks mCallbacks; + Callback mPcapCallback; + KeyMaterial mPrevKey; + KeyMaterial mCurrKey; + KeyMaterial mNextKey; + uint32_t mFrameCounter; + uint8_t mKeyId; #if OPENTHREAD_CONFIG_MAC_ADD_DELAY_ON_NO_ACK_ERROR_BEFORE_RETRY uint8_t mRetxDelayBackOffExponent; #endif -#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE - TimerMicro mTimer; -#else - TimerMilli mTimer; -#endif + SubMacTimer mTimer; #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE uint16_t mCslPeriod; // The CSL sample period, in units of 10 symbols (160 microseconds). uint8_t mCslChannel : 7; // The CSL sample channel. bool mIsCslSampling : 1; // Indicates that the radio is receiving in CSL state for platforms not supporting delayed // reception. - uint16_t mCslPeerShort; // The CSL peer short address. - TimeMicro mCslSampleTime; // The CSL sample time of the current period. - TimeMicro mCslLastSync; // The timestamp of the last successful CSL synchronization. - uint8_t mCslParentAccuracy; // Drift of timer used for scheduling CSL tx by the parent, in ± ppm. - uint8_t mCslParentUncert; // Uncertainty of the scheduling CSL of tx by the parent, in ±10 us units. - TimerMicro mCslTimer; + uint16_t mCslPeerShort; // The CSL peer short address. + TimeMicro mCslSampleTime; // The CSL sample time of the current period. + TimeMicro mCslLastSync; // The timestamp of the last successful CSL synchronization. + CslAccuracy mCslParentAccuracy; // The parent's CSL accuracy (clock accuracy and uncertainty). + TimerMicro mCslTimer; #endif }; diff --git a/src/core/mac/sub_mac_callbacks.cpp b/src/core/mac/sub_mac_callbacks.cpp index b61ad283f1d..4a3053dd292 100644 --- a/src/core/mac/sub_mac_callbacks.cpp +++ b/src/core/mac/sub_mac_callbacks.cpp @@ -71,7 +71,7 @@ void SubMac::Callbacks::RecordCcaStatus(bool aCcaSuccess, uint8_t aChannel) } void SubMac::Callbacks::RecordFrameTransmitStatus(const TxFrame &aFrame, - const RxFrame *aAckFrame, + RxFrame *aAckFrame, Error aError, uint8_t aRetryCount, bool aWillRetx) @@ -114,17 +114,12 @@ void SubMac::Callbacks::FrameCounterUsed(uint32_t aFrameCounter) #elif OPENTHREAD_RADIO -void SubMac::Callbacks::ReceiveDone(RxFrame *aFrame, Error aError) -{ - Get().InvokeReceiveDone(aFrame, aError); -} +void SubMac::Callbacks::ReceiveDone(RxFrame *aFrame, Error aError) { Get().InvokeReceiveDone(aFrame, aError); } -void SubMac::Callbacks::RecordCcaStatus(bool, uint8_t) -{ -} +void SubMac::Callbacks::RecordCcaStatus(bool, uint8_t) {} void SubMac::Callbacks::RecordFrameTransmitStatus(const TxFrame &aFrame, - const RxFrame *aAckFrame, + RxFrame *aAckFrame, Error aError, uint8_t aRetryCount, bool aWillRetx) @@ -137,15 +132,9 @@ void SubMac::Callbacks::TransmitDone(TxFrame &aFrame, RxFrame *aAckFrame, Error Get().InvokeTransmitDone(aFrame, aAckFrame, aError); } -void SubMac::Callbacks::EnergyScanDone(int8_t aMaxRssi) -{ - Get().InvokeEnergyScanDone(aMaxRssi); -} +void SubMac::Callbacks::EnergyScanDone(int8_t aMaxRssi) { Get().InvokeEnergyScanDone(aMaxRssi); } -void SubMac::Callbacks::FrameCounterUsed(uint32_t aFrameCounter) -{ - OT_UNUSED_VARIABLE(aFrameCounter); -} +void SubMac::Callbacks::FrameCounterUsed(uint32_t aFrameCounter) { OT_UNUSED_VARIABLE(aFrameCounter); } #endif // OPENTHREAD_RADIO diff --git a/src/core/meshcop/announce_begin_client.cpp b/src/core/meshcop/announce_begin_client.cpp index b1bb699995c..f0cde456fda 100644 --- a/src/core/meshcop/announce_begin_client.cpp +++ b/src/core/meshcop/announce_begin_client.cpp @@ -63,12 +63,12 @@ Error AnnounceBeginClient::SendRequest(uint32_t aChannelMask, Error error = kErrorNone; MeshCoP::ChannelMaskTlv channelMask; Tmf::MessageInfo messageInfo(GetInstance()); - Coap::Message * message = nullptr; + Coap::Message *message = nullptr; VerifyOrExit(Get().IsActive(), error = kErrorInvalidState); VerifyOrExit((message = Get().NewPriorityMessage()) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = message->InitAsPost(aAddress, UriPath::kAnnounceBegin)); + SuccessOrExit(error = message->InitAsPost(aAddress, kUriAnnounceBegin)); SuccessOrExit(error = message->SetPayloadMarker()); SuccessOrExit( @@ -85,7 +85,7 @@ Error AnnounceBeginClient::SendRequest(uint32_t aChannelMask, SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("sent announce begin query"); + LogInfo("Sent %s", UriToString()); exit: FreeMessageOnError(message, error); diff --git a/src/core/meshcop/border_agent.cpp b/src/core/meshcop/border_agent.cpp index e10a79da519..9081751673a 100644 --- a/src/core/meshcop/border_agent.cpp +++ b/src/core/meshcop/border_agent.cpp @@ -56,7 +56,7 @@ namespace { constexpr uint16_t kBorderAgentUdpPort = OPENTHREAD_CONFIG_BORDER_AGENT_UDP_PORT; ///< UDP port of border agent service. } -void BorderAgent::ForwardContext::Init(Instance & aInstance, +void BorderAgent::ForwardContext::Init(Instance &aInstance, const Coap::Message &aMessage, bool aPetition, bool aSeparate) @@ -113,13 +113,12 @@ Coap::Message::Code BorderAgent::CoapCodeFromError(Error aError) void BorderAgent::SendErrorMessage(ForwardContext &aForwardContext, Error aError) { - Error error = kErrorNone; - Coap::CoapSecure &coaps = Get(); - Coap::Message * message = nullptr; + Error error = kErrorNone; + Coap::Message *message = nullptr; - VerifyOrExit((message = coaps.NewPriorityMessage()) != nullptr, error = kErrorNoBufs); + VerifyOrExit((message = Get().NewPriorityMessage()) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = aForwardContext.ToHeader(*message, CoapCodeFromError(aError))); - SuccessOrExit(error = coaps.SendMessage(*message, coaps.GetMessageInfo())); + SuccessOrExit(error = Get().SendMessage(*message, Get().GetMessageInfo())); exit: FreeMessageOnError(message, error); @@ -128,11 +127,10 @@ void BorderAgent::SendErrorMessage(ForwardContext &aForwardContext, Error aError void BorderAgent::SendErrorMessage(const Coap::Message &aRequest, bool aSeparate, Error aError) { - Error error = kErrorNone; - Coap::CoapSecure &coaps = Get(); - Coap::Message * message = nullptr; + Error error = kErrorNone; + Coap::Message *message = nullptr; - VerifyOrExit((message = coaps.NewPriorityMessage()) != nullptr, error = kErrorNoBufs); + VerifyOrExit((message = Get().NewPriorityMessage()) != nullptr, error = kErrorNoBufs); if (aRequest.IsNonConfirmable() || aSeparate) { @@ -150,15 +148,15 @@ void BorderAgent::SendErrorMessage(const Coap::Message &aRequest, bool aSeparate SuccessOrExit(error = message->SetTokenFromMessage(aRequest)); - SuccessOrExit(error = coaps.SendMessage(*message, coaps.GetMessageInfo())); + SuccessOrExit(error = Get().SendMessage(*message, Get().GetMessageInfo())); exit: FreeMessageOnError(message, error); LogError("send error CoAP message", error); } -void BorderAgent::HandleCoapResponse(void * aContext, - otMessage * aMessage, +void BorderAgent::HandleCoapResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult) { @@ -175,7 +173,7 @@ void BorderAgent::HandleCoapResponse(ForwardContext &aForwardContext, const Coap Error error; SuccessOrExit(error = aResult); - VerifyOrExit((message = Get().NewPriorityMessage()) != nullptr, error = kErrorNoBufs); + VerifyOrExit((message = Get().NewPriorityMessage()) != nullptr, error = kErrorNoBufs); if (aForwardContext.IsPetition() && aResponse->GetCode() == Coap::kCodeChanged) { @@ -213,7 +211,7 @@ void BorderAgent::HandleCoapResponse(ForwardContext &aForwardContext, const Coap { FreeMessage(message); - LogWarn("Commissioner request[%hu] failed: %s", aForwardContext.GetMessageId(), ErrorToString(error)); + LogWarn("Commissioner request[%u] failed: %s", aForwardContext.GetMessageId(), ErrorToString(error)); SendErrorMessage(aForwardContext, error); } @@ -221,82 +219,44 @@ void BorderAgent::HandleCoapResponse(ForwardContext &aForwardContext, const Coap Heap::Free(&aForwardContext); } -template -void BorderAgent::HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +BorderAgent::BorderAgent(Instance &aInstance) + : InstanceLocator(aInstance) + , mUdpReceiver(BorderAgent::HandleUdpReceive, this) + , mTimer(aInstance) + , mState(kStateStopped) + , mUdpProxyPort(0) +#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE + , mIdInitialized(false) +#endif { - IgnoreError(static_cast(aContext)->ForwardToLeader( - AsCoapMessage(aMessage), AsCoreType(aMessageInfo), - (static_cast(aContext)->*aResource).GetUriPath(), false, false)); + mCommissionerAloc.InitAsThreadOriginRealmLocalScope(); } -template <> -void BorderAgent::HandleRequest<&BorderAgent::mCommissionerPetition>(void * aContext, - otMessage * aMessage, - const otMessageInfo *aMessageInfo) +#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE +Error BorderAgent::GetId(uint8_t *aId, uint16_t &aLength) { - IgnoreError(static_cast(aContext)->ForwardToLeader(AsCoapMessage(aMessage), AsCoreType(aMessageInfo), - UriPath::kLeaderPetition, true, true)); -} + Error error = kErrorNone; -template <> -void BorderAgent::HandleRequest<&BorderAgent::mCommissionerKeepAlive>(void * aContext, - otMessage * aMessage, - const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleKeepAlive(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} + VerifyOrExit(aLength >= sizeof(mId), error = kErrorInvalidArgs); + VerifyOrExit(!mIdInitialized, error = kErrorNone); -template <> -void BorderAgent::HandleRequest<&BorderAgent::mRelayTransmit>(void * aContext, - otMessage * aMessage, - const otMessageInfo *aMessageInfo) -{ - OT_UNUSED_VARIABLE(aMessageInfo); - static_cast(aContext)->HandleRelayTransmit(AsCoapMessage(aMessage)); -} - -template <> -void BorderAgent::HandleRequest<&BorderAgent::mRelayReceive>(void * aContext, - otMessage * aMessage, - const otMessageInfo *aMessageInfo) -{ - OT_UNUSED_VARIABLE(aMessageInfo); - static_cast(aContext)->HandleRelayReceive(AsCoapMessage(aMessage)); -} + if (Get().Read(mId) != kErrorNone) + { + Random::NonCrypto::FillBuffer(mId.GetId(), sizeof(mId)); + SuccessOrExit(error = Get().Save(mId)); + } -template <> -void BorderAgent::HandleRequest<&BorderAgent::mProxyTransmit>(void * aContext, - otMessage * aMessage, - const otMessageInfo *aMessageInfo) -{ - OT_UNUSED_VARIABLE(aMessageInfo); - static_cast(aContext)->HandleProxyTransmit(AsCoapMessage(aMessage)); -} + mIdInitialized = true; -BorderAgent::BorderAgent(Instance &aInstance) - : InstanceLocator(aInstance) - , mCommissionerPetition(UriPath::kCommissionerPetition, - BorderAgent::HandleRequest<&BorderAgent::mCommissionerPetition>, - this) - , mCommissionerKeepAlive(UriPath::kCommissionerKeepAlive, - BorderAgent::HandleRequest<&BorderAgent::mCommissionerKeepAlive>, - this) - , mRelayTransmit(UriPath::kRelayTx, BorderAgent::HandleRequest<&BorderAgent::mRelayTransmit>, this) - , mRelayReceive(UriPath::kRelayRx, BorderAgent::HandleRequest<&BorderAgent::mRelayReceive>, this) - , mCommissionerGet(UriPath::kCommissionerGet, BorderAgent::HandleRequest<&BorderAgent::mCommissionerGet>, this) - , mCommissionerSet(UriPath::kCommissionerSet, BorderAgent::HandleRequest<&BorderAgent::mCommissionerSet>, this) - , mActiveGet(UriPath::kActiveGet, BorderAgent::HandleRequest<&BorderAgent::mActiveGet>, this) - , mActiveSet(UriPath::kActiveSet, BorderAgent::HandleRequest<&BorderAgent::mActiveSet>, this) - , mPendingGet(UriPath::kPendingGet, BorderAgent::HandleRequest<&BorderAgent::mPendingGet>, this) - , mPendingSet(UriPath::kPendingSet, BorderAgent::HandleRequest<&BorderAgent::mPendingSet>, this) - , mProxyTransmit(UriPath::kProxyTx, BorderAgent::HandleRequest<&BorderAgent::mProxyTransmit>, this) - , mUdpReceiver(BorderAgent::HandleUdpReceive, this) - , mTimer(aInstance, HandleTimeout) - , mState(kStateStopped) - , mUdpProxyPort(0) -{ - mCommissionerAloc.InitAsThreadOriginRealmLocalScope(); +exit: + if (error == kErrorNone) + { + memcpy(aId, mId.GetId(), sizeof(mId)); + aLength = static_cast(sizeof(mId)); + } + return error; } +#endif // OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE void BorderAgent::HandleNotifierEvents(Events aEvents) { @@ -319,31 +279,38 @@ void BorderAgent::HandleNotifierEvents(Events aEvents) return; } -void BorderAgent::HandleProxyTransmit(const Coap::Message &aMessage) +template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - Message * message = nullptr; - Ip6::MessageInfo messageInfo; - uint16_t offset; - Error error; - UdpEncapsulationTlv tlv; + OT_UNUSED_VARIABLE(aMessageInfo); + + Error error = kErrorNone; + Message *message = nullptr; + Ip6::MessageInfo messageInfo; + uint16_t offset; + uint16_t length; + UdpEncapsulationTlvHeader udpEncapHeader; - SuccessOrExit(error = Tlv::FindTlvOffset(aMessage, Tlv::kUdpEncapsulation, offset)); - SuccessOrExit(error = aMessage.Read(offset, tlv)); + VerifyOrExit(mState != kStateStopped); - VerifyOrExit((message = Get().NewMessage(0)) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = message->SetLength(tlv.GetUdpLength())); - aMessage.CopyTo(offset + sizeof(tlv), 0, tlv.GetUdpLength(), *message); + SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Tlv::kUdpEncapsulation, offset, length)); - VerifyOrExit(tlv.GetSourcePort() > 0 && tlv.GetDestinationPort() > 0, error = kErrorDrop); + SuccessOrExit(error = aMessage.Read(offset, udpEncapHeader)); + offset += sizeof(UdpEncapsulationTlvHeader); + length -= sizeof(UdpEncapsulationTlvHeader); - messageInfo.SetSockPort(tlv.GetSourcePort()); + VerifyOrExit(udpEncapHeader.GetSourcePort() > 0 && udpEncapHeader.GetDestinationPort() > 0, error = kErrorDrop); + + VerifyOrExit((message = Get().NewMessage()) != nullptr, error = kErrorNoBufs); + SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offset, length)); + + messageInfo.SetSockPort(udpEncapHeader.GetSourcePort()); messageInfo.SetSockAddr(mCommissionerAloc.GetAddress()); - messageInfo.SetPeerPort(tlv.GetDestinationPort()); + messageInfo.SetPeerPort(udpEncapHeader.GetDestinationPort()); SuccessOrExit(error = Tlv::Find(aMessage, messageInfo.GetPeerAddr())); SuccessOrExit(error = Get().SendDatagram(*message, messageInfo, Ip6::kProtoUdp)); - mUdpProxyPort = tlv.GetSourcePort(); + mUdpProxyPort = udpEncapHeader.GetSourcePort(); LogInfo("Proxy transmit sent to %s", messageInfo.GetPeerAddr().ToString().AsCString()); @@ -357,55 +324,66 @@ bool BorderAgent::HandleUdpReceive(const Message &aMessage, const Ip6::MessageIn Error error; Coap::Message *message = nullptr; - VerifyOrExit(aMessageInfo.GetSockAddr() == mCommissionerAloc.GetAddress(), - error = kErrorDestinationAddressFiltered); + if (aMessageInfo.GetSockAddr() != mCommissionerAloc.GetAddress()) + { + LogDebg("Filtered out message for commissioner: dest %s != %s (ALOC)", + aMessageInfo.GetSockAddr().ToString().AsCString(), + mCommissionerAloc.GetAddress().ToString().AsCString()); + ExitNow(error = kErrorDestinationAddressFiltered); + } VerifyOrExit(aMessage.GetLength() > 0, error = kErrorNone); - message = Get().NewPriorityNonConfirmablePostMessage(UriPath::kProxyRx); + message = Get().NewPriorityNonConfirmablePostMessage(kUriProxyRx); VerifyOrExit(message != nullptr, error = kErrorNoBufs); { - UdpEncapsulationTlv tlv; - uint16_t offset; - uint16_t udpLength = aMessage.GetLength() - aMessage.GetOffset(); - - tlv.Init(); - tlv.SetSourcePort(aMessageInfo.GetPeerPort()); - tlv.SetDestinationPort(aMessageInfo.GetSockPort()); - tlv.SetUdpLength(udpLength); - SuccessOrExit(error = message->Append(tlv)); - - offset = message->GetLength(); - SuccessOrExit(error = message->SetLength(offset + udpLength)); - aMessage.CopyTo(aMessage.GetOffset(), offset, udpLength, *message); + ExtendedTlv extTlv; + UdpEncapsulationTlvHeader udpEncapHeader; + uint16_t udpLength = aMessage.GetLength() - aMessage.GetOffset(); + + extTlv.SetType(Tlv::kUdpEncapsulation); + extTlv.SetLength(sizeof(UdpEncapsulationTlvHeader) + udpLength); + SuccessOrExit(error = message->Append(extTlv)); + + udpEncapHeader.SetSourcePort(aMessageInfo.GetPeerPort()); + udpEncapHeader.SetDestinationPort(aMessageInfo.GetSockPort()); + SuccessOrExit(error = message->Append(udpEncapHeader)); + SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, aMessage.GetOffset(), udpLength)); } SuccessOrExit(error = Tlv::Append(*message, aMessageInfo.GetPeerAddr())); - SuccessOrExit(error = Get().SendMessage(*message, Get().GetMessageInfo())); + SuccessOrExit(error = Get().SendMessage(*message, Get().GetMessageInfo())); - LogInfo("Sent to commissioner on %s", UriPath::kProxyRx); + LogInfo("Sent to commissioner on ProxyRx (c/ur)"); exit: FreeMessageOnError(message, error); - LogError("notify commissioner on ProxyRx (c/ur)", error); + if (error != kErrorDestinationAddressFiltered) + { + LogError("Notify commissioner on ProxyRx (c/ur)", error); + } return error != kErrorDestinationAddressFiltered; } -void BorderAgent::HandleRelayReceive(const Coap::Message &aMessage) +template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { + OT_UNUSED_VARIABLE(aMessageInfo); + Coap::Message *message = nullptr; - Error error; + Error error = kErrorNone; + + VerifyOrExit(mState != kStateStopped); VerifyOrExit(aMessage.IsNonConfirmablePostRequest(), error = kErrorDrop); - message = Get().NewPriorityNonConfirmablePostMessage(UriPath::kRelayRx); + message = Get().NewPriorityNonConfirmablePostMessage(kUriRelayRx); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = ForwardToCommissioner(*message, aMessage)); - LogInfo("Sent to commissioner on %s", UriPath::kRelayRx); + LogInfo("Sent to commissioner on RelayRx (c/rx)"); exit: FreeMessageOnError(message, error); @@ -413,15 +391,12 @@ void BorderAgent::HandleRelayReceive(const Coap::Message &aMessage) Error BorderAgent::ForwardToCommissioner(Coap::Message &aForwardMessage, const Message &aMessage) { - Error error = kErrorNone; - uint16_t offset = 0; - - offset = aForwardMessage.GetLength(); - SuccessOrExit(error = aForwardMessage.SetLength(offset + aMessage.GetLength() - aMessage.GetOffset())); - aMessage.CopyTo(aMessage.GetOffset(), offset, aMessage.GetLength() - aMessage.GetOffset(), aForwardMessage); + Error error; + SuccessOrExit(error = aForwardMessage.AppendBytesFromMessage(aMessage, aMessage.GetOffset(), + aMessage.GetLength() - aMessage.GetOffset())); SuccessOrExit(error = - Get().SendMessage(aForwardMessage, Get().GetMessageInfo())); + Get().SendMessage(aForwardMessage, Get().GetMessageInfo())); LogInfo("Sent to commissioner"); @@ -430,77 +405,128 @@ Error BorderAgent::ForwardToCommissioner(Coap::Message &aForwardMessage, const M return error; } -void BorderAgent::HandleKeepAlive(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> +void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - Error error; + IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriLeaderPetition)); +} - error = ForwardToLeader(aMessage, aMessageInfo, UriPath::kLeaderKeepAlive, false, true); +template <> +void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriCommissionerGet)); +} - if (error == kErrorNone) - { - mTimer.Start(kKeepAliveTimeout); - } +template <> +void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriCommissionerSet)); } -void BorderAgent::HandleRelayTransmit(const Coap::Message &aMessage) +template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { + IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriActiveGet)); +} + +template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriActiveSet)); +} + +template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriPendingGet)); +} + +template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + IgnoreError(ForwardToLeader(aMessage, aMessageInfo, kUriPendingSet)); +} + +template <> +void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + VerifyOrExit(mState != kStateStopped); + + SuccessOrExit(ForwardToLeader(aMessage, aMessageInfo, kUriLeaderKeepAlive)); + mTimer.Start(kKeepAliveTimeout); + +exit: + return; +} + +template <> void BorderAgent::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + OT_UNUSED_VARIABLE(aMessageInfo); + Error error = kErrorNone; uint16_t joinerRouterRloc; - Coap::Message * message = nullptr; + Coap::Message *message = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); - uint16_t offset = 0; + + VerifyOrExit(mState != kStateStopped); VerifyOrExit(aMessage.IsNonConfirmablePostRequest()); SuccessOrExit(error = Tlv::Find(aMessage, joinerRouterRloc)); - message = Get().NewPriorityNonConfirmablePostMessage(UriPath::kRelayTx); + message = Get().NewPriorityNonConfirmablePostMessage(kUriRelayTx); VerifyOrExit(message != nullptr, error = kErrorNoBufs); - offset = message->GetLength(); - SuccessOrExit(error = message->SetLength(offset + aMessage.GetLength() - aMessage.GetOffset())); - aMessage.CopyTo(aMessage.GetOffset(), offset, aMessage.GetLength() - aMessage.GetOffset(), *message); + SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, aMessage.GetOffset(), + aMessage.GetLength() - aMessage.GetOffset())); messageInfo.SetSockAddrToRlocPeerAddrTo(joinerRouterRloc); messageInfo.SetSockPortToTmf(); SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("Sent to joiner router request on %s", UriPath::kRelayTx); + LogInfo("Sent to joiner router request on RelayTx (c/tx)"); exit: FreeMessageOnError(message, error); LogError("send to joiner router request RelayTx (c/tx)", error); } -Error BorderAgent::ForwardToLeader(const Coap::Message & aMessage, - const Ip6::MessageInfo &aMessageInfo, - const char * aPath, - bool aPetition, - bool aSeparate) +Error BorderAgent::ForwardToLeader(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Uri aUri) { Error error = kErrorNone; - ForwardContext * forwardContext = nullptr; + ForwardContext *forwardContext = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); - Coap::Message * message = nullptr; - uint16_t offset = 0; + Coap::Message *message = nullptr; + bool petition = false; + bool separate = false; + + VerifyOrExit(mState != kStateStopped); + + switch (aUri) + { + case kUriLeaderPetition: + petition = true; + separate = true; + break; + case kUriLeaderKeepAlive: + separate = true; + break; + default: + break; + } - if (aSeparate) + if (separate) { - SuccessOrExit(error = Get().SendAck(aMessage, aMessageInfo)); + SuccessOrExit(error = Get().SendAck(aMessage, aMessageInfo)); } forwardContext = static_cast(Heap::CAlloc(1, sizeof(ForwardContext))); VerifyOrExit(forwardContext != nullptr, error = kErrorNoBufs); - forwardContext->Init(GetInstance(), aMessage, aPetition, aSeparate); + forwardContext->Init(GetInstance(), aMessage, petition, separate); - message = Get().NewPriorityConfirmablePostMessage(aPath); + message = Get().NewPriorityConfirmablePostMessage(aUri); VerifyOrExit(message != nullptr, error = kErrorNoBufs); - offset = message->GetLength(); - SuccessOrExit(error = message->SetLength(offset + aMessage.GetLength() - aMessage.GetOffset())); - aMessage.CopyTo(aMessage.GetOffset(), offset, aMessage.GetLength() - aMessage.GetOffset(), *message); + SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, aMessage.GetOffset(), + aMessage.GetLength() - aMessage.GetOffset())); SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); messageInfo.SetSockPortToTmf(); @@ -510,7 +536,7 @@ Error BorderAgent::ForwardToLeader(const Coap::Message & aMessage, // HandleCoapResponse is responsible to free this forward context. forwardContext = nullptr; - LogInfo("Forwarded request to leader on %s", aPath); + LogInfo("Forwarded request to leader on %s", PathForUri(aUri)); exit: LogError("forward to leader", error); @@ -523,7 +549,7 @@ Error BorderAgent::ForwardToLeader(const Coap::Message & aMessage, } FreeMessage(message); - SendErrorMessage(aMessage, aSeparate, error); + SendErrorMessage(aMessage, separate, error); } return error; @@ -552,38 +578,21 @@ void BorderAgent::HandleConnected(bool aConnected) } } -uint16_t BorderAgent::GetUdpPort(void) const -{ - return Get().GetUdpPort(); -} +uint16_t BorderAgent::GetUdpPort(void) const { return Get().GetUdpPort(); } void BorderAgent::Start(void) { - Error error; - Coap::CoapSecure &coaps = Get(); - Pskc pskc; + Error error; + Pskc pskc; VerifyOrExit(mState == kStateStopped, error = kErrorNone); Get().GetPskc(pskc); - SuccessOrExit(error = coaps.Start(kBorderAgentUdpPort)); - SuccessOrExit(error = coaps.SetPsk(pskc.m8, Pskc::kSize)); + SuccessOrExit(error = Get().Start(kBorderAgentUdpPort)); + SuccessOrExit(error = Get().SetPsk(pskc.m8, Pskc::kSize)); pskc.Clear(); - coaps.SetConnectedCallback(HandleConnected, this); - - coaps.AddResource(mActiveGet); - coaps.AddResource(mActiveSet); - coaps.AddResource(mPendingGet); - coaps.AddResource(mPendingSet); - coaps.AddResource(mCommissionerPetition); - coaps.AddResource(mCommissionerKeepAlive); - coaps.AddResource(mCommissionerSet); - coaps.AddResource(mCommissionerGet); - coaps.AddResource(mProxyTransmit); - coaps.AddResource(mRelayTransmit); - - Get().AddResource(mRelayReceive); + Get().SetConnectedCallback(HandleConnected, this); mState = kStateStarted; mUdpProxyPort = 0; @@ -597,42 +606,21 @@ void BorderAgent::Start(void) } } -void BorderAgent::HandleTimeout(Timer &aTimer) -{ - aTimer.Get().HandleTimeout(); -} - void BorderAgent::HandleTimeout(void) { - if (Get().IsConnected()) + if (Get().IsConnected()) { - Get().Disconnect(); + Get().Disconnect(); LogWarn("Reset commissioner session"); } } void BorderAgent::Stop(void) { - Coap::CoapSecure &coaps = Get(); - VerifyOrExit(mState != kStateStopped); mTimer.Stop(); - - coaps.RemoveResource(mCommissionerPetition); - coaps.RemoveResource(mCommissionerKeepAlive); - coaps.RemoveResource(mCommissionerSet); - coaps.RemoveResource(mCommissionerGet); - coaps.RemoveResource(mActiveGet); - coaps.RemoveResource(mActiveSet); - coaps.RemoveResource(mPendingGet); - coaps.RemoveResource(mPendingSet); - coaps.RemoveResource(mProxyTransmit); - coaps.RemoveResource(mRelayTransmit); - - Get().RemoveResource(mRelayReceive); - - coaps.Stop(); + Get().Stop(); mState = kStateStopped; mUdpProxyPort = 0; diff --git a/src/core/meshcop/border_agent.hpp b/src/core/meshcop/border_agent.hpp index 6f6bc45b541..4a047a5e27a 100644 --- a/src/core/meshcop/border_agent.hpp +++ b/src/core/meshcop/border_agent.hpp @@ -40,12 +40,14 @@ #include -#include "coap/coap.hpp" #include "common/as_core_type.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" #include "common/notifier.hpp" +#include "common/settings.hpp" #include "net/udp6.hpp" +#include "thread/tmf.hpp" +#include "thread/uri_paths.hpp" namespace ot { @@ -54,6 +56,8 @@ namespace MeshCoP { class BorderAgent : public InstanceLocator, private NonCopyable { friend class ot::Notifier; + friend class Tmf::Agent; + friend class Tmf::SecureAgent; public: /** @@ -75,6 +79,25 @@ class BorderAgent : public InstanceLocator, private NonCopyable */ explicit BorderAgent(Instance &aInstance); +#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE + /** + * Gets the randomly generated Border Agent ID. + * + * The ID is saved in persistent storage and survives reboots. The typical use case of the ID is to + * be published in the MeshCoP mDNS service as the `id` TXT value for the client to identify this + * Border Router/Agent device. + * + * @param[out] aId A pointer to buffer to receive the ID. + * @param[inout] aLength Specifies the length of `aId` when used as input and receives the length + * actual ID data copied to `aId` when used as output. + * + * @retval OT_ERROR_INVALID_ARGS If value of `aLength` if smaller than `OT_BORDER_AGENT_ID_LENGTH`. + * @retval OT_ERROR_NONE If successfully retrieved the Border Agent ID. + * + */ + Error GetId(uint8_t *aId, uint16_t &aLength); +#endif + /** * This method gets the UDP port of this service. * @@ -145,28 +168,18 @@ class BorderAgent : public InstanceLocator, private NonCopyable static void HandleConnected(bool aConnected, void *aContext); void HandleConnected(bool aConnected); - template - static void HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleTimeout(Timer &aTimer); - void HandleTimeout(void); + void HandleTimeout(void); - static void HandleCoapResponse(void * aContext, - otMessage * aMessage, + static void HandleCoapResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); void HandleCoapResponse(ForwardContext &aForwardContext, const Coap::Message *aResponse, Error aResult); - Error ForwardToLeader(const Coap::Message & aMessage, - const Ip6::MessageInfo &aMessageInfo, - const char * aPath, - bool aPetition, - bool aSeparate); + Error ForwardToLeader(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo, Uri aUri); Error ForwardToCommissioner(Coap::Message &aForwardMessage, const Message &aMessage); - void HandleKeepAlive(const Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - void HandleRelayTransmit(const Coap::Message &aMessage); - void HandleRelayReceive(const Coap::Message &aMessage); - void HandleProxyTransmit(const Coap::Message &aMessage); static bool HandleUdpReceive(void *aContext, const otMessage *aMessage, const otMessageInfo *aMessageInfo) { return static_cast(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo)); @@ -175,28 +188,34 @@ class BorderAgent : public InstanceLocator, private NonCopyable static constexpr uint32_t kKeepAliveTimeout = 50 * 1000; // Timeout to reject a commissioner. - Ip6::MessageInfo mMessageInfo; + using TimeoutTimer = TimerMilliIn; - Coap::Resource mCommissionerPetition; - Coap::Resource mCommissionerKeepAlive; - Coap::Resource mRelayTransmit; - Coap::Resource mRelayReceive; - Coap::Resource mCommissionerGet; - Coap::Resource mCommissionerSet; - Coap::Resource mActiveGet; - Coap::Resource mActiveSet; - Coap::Resource mPendingGet; - Coap::Resource mPendingSet; - Coap::Resource mProxyTransmit; + Ip6::MessageInfo mMessageInfo; Ip6::Udp::Receiver mUdpReceiver; ///< The UDP receiver to receive packets from external commissioner Ip6::Netif::UnicastAddress mCommissionerAloc; - TimerMilli mTimer; - State mState; - uint16_t mUdpProxyPort; + TimeoutTimer mTimer; + State mState; + uint16_t mUdpProxyPort; +#if OPENTHREAD_CONFIG_BORDER_AGENT_ID_ENABLE + Settings::BorderAgentId mId; + bool mIdInitialized; +#endif }; +DeclareTmfHandler(BorderAgent, kUriRelayRx); +DeclareTmfHandler(BorderAgent, kUriCommissionerPetition); +DeclareTmfHandler(BorderAgent, kUriCommissionerKeepAlive); +DeclareTmfHandler(BorderAgent, kUriRelayTx); +DeclareTmfHandler(BorderAgent, kUriCommissionerGet); +DeclareTmfHandler(BorderAgent, kUriCommissionerSet); +DeclareTmfHandler(BorderAgent, kUriActiveGet); +DeclareTmfHandler(BorderAgent, kUriActiveSet); +DeclareTmfHandler(BorderAgent, kUriPendingGet); +DeclareTmfHandler(BorderAgent, kUriPendingSet); +DeclareTmfHandler(BorderAgent, kUriProxyTx); + } // namespace MeshCoP DefineMapEnum(otBorderAgentState, MeshCoP::BorderAgent::State); diff --git a/src/core/meshcop/commissioner.cpp b/src/core/meshcop/commissioner.cpp index c1c73eb7e07..0bac4320f2a 100644 --- a/src/core/meshcop/commissioner.cpp +++ b/src/core/meshcop/commissioner.cpp @@ -64,18 +64,13 @@ Commissioner::Commissioner(Instance &aInstance) , mJoinerRloc(0) , mSessionId(0) , mTransmitAttempts(0) - , mJoinerExpirationTimer(aInstance, HandleJoinerExpirationTimer) - , mTimer(aInstance, HandleTimer) - , mRelayReceive(UriPath::kRelayRx, &Commissioner::HandleRelayReceive, this) - , mDatasetChanged(UriPath::kDatasetChanged, &Commissioner::HandleDatasetChanged, this) - , mJoinerFinalize(UriPath::kJoinerFinalize, &Commissioner::HandleJoinerFinalize, this) + , mJoinerExpirationTimer(aInstance) + , mTimer(aInstance) + , mJoinerSessionTimer(aInstance) , mAnnounceBegin(aInstance) , mEnergyScan(aInstance) , mPanIdQuery(aInstance) , mState(kStateDisabled) - , mStateCallback(nullptr) - , mJoinerCallback(nullptr) - , mCallbackContext(nullptr) { memset(reinterpret_cast(mJoiners), 0, sizeof(mJoiners)); @@ -101,10 +96,7 @@ void Commissioner::SetState(State aState) LogInfo("State: %s -> %s", StateToString(oldState), StateToString(aState)); - if (mStateCallback) - { - mStateCallback(MapEnum(mState), mCallbackContext); - } + mStateCallback.InvokeIfSet(MapEnum(mState)); exit: return; @@ -116,7 +108,7 @@ void Commissioner::SignalJoinerEvent(JoinerEvent aEvent, const Joiner *aJoiner) Mac::ExtAddress joinerId; bool noJoinerId = false; - VerifyOrExit((mJoinerCallback != nullptr) && (aJoiner != nullptr)); + VerifyOrExit(mJoinerCallback.IsSet() && (aJoiner != nullptr)); aJoiner->CopyToJoinerInfo(joinerInfo); @@ -133,33 +125,24 @@ void Commissioner::SignalJoinerEvent(JoinerEvent aEvent, const Joiner *aJoiner) noJoinerId = true; } - mJoinerCallback(MapEnum(aEvent), &joinerInfo, noJoinerId ? nullptr : &joinerId, mCallbackContext); + mJoinerCallback.Invoke(MapEnum(aEvent), &joinerInfo, noJoinerId ? nullptr : &joinerId); exit: return; } -void Commissioner::AddCoapResources(void) +void Commissioner::HandleSecureAgentConnected(bool aConnected, void *aContext) { - Get().AddResource(mRelayReceive); - Get().AddResource(mDatasetChanged); - Get().AddResource(mJoinerFinalize); + static_cast(aContext)->HandleSecureAgentConnected(aConnected); } -void Commissioner::RemoveCoapResources(void) +void Commissioner::HandleSecureAgentConnected(bool aConnected) { - Get().RemoveResource(mRelayReceive); - Get().RemoveResource(mDatasetChanged); - Get().RemoveResource(mJoinerFinalize); -} - -void Commissioner::HandleCoapsConnected(bool aConnected, void *aContext) -{ - static_cast(aContext)->HandleCoapsConnected(aConnected); -} + if (!aConnected) + { + mJoinerSessionTimer.Stop(); + } -void Commissioner::HandleCoapsConnected(bool aConnected) -{ SignalJoinerEvent(aConnected ? kJoinerEventConnected : kJoinerEventEnd, mActiveJoiner); } @@ -229,7 +212,7 @@ Commissioner::Joiner *Commissioner::FindJoinerEntry(const JoinerDiscerner &aDisc Commissioner::Joiner *Commissioner::FindBestMatchingJoinerEntry(const Mac::ExtAddress &aReceivedJoinerId) { - Joiner * best = nullptr; + Joiner *best = nullptr; Mac::ExtAddress joinerId; // Prefer a full Joiner ID match, if not found use the entry @@ -309,12 +292,11 @@ Error Commissioner::Start(StateCallback aStateCallback, JoinerCallback aJoinerCa Get().Stop(); #endif - SuccessOrExit(error = Get().Start(SendRelayTransmit, this)); - Get().SetConnectedCallback(&Commissioner::HandleCoapsConnected, this); + SuccessOrExit(error = Get().Start(SendRelayTransmit, this)); + Get().SetConnectedCallback(&Commissioner::HandleSecureAgentConnected, this); - mStateCallback = aStateCallback; - mJoinerCallback = aJoinerCallback; - mCallbackContext = aCallbackContext; + mStateCallback.Set(aStateCallback, aCallbackContext); + mJoinerCallback.Set(aJoinerCallback, aCallbackContext); mTransmitAttempts = 0; SuccessOrExit(error = SendPetition()); @@ -325,7 +307,7 @@ Error Commissioner::Start(StateCallback aStateCallback, JoinerCallback aJoinerCa exit: if ((error != kErrorNone) && (error != kErrorAlready)) { - Get().Stop(); + Get().Stop(); } LogError("start commissioner", error); @@ -339,12 +321,12 @@ Error Commissioner::Stop(ResignMode aResignMode) VerifyOrExit(mState != kStateDisabled, error = kErrorAlready); - Get().Stop(); + mJoinerSessionTimer.Stop(); + Get().Stop(); if (mState == kStateActive) { Get().RemoveUnicastAddress(mCommissionerAloc); - RemoveCoapResources(); ClearJoiners(); needResign = true; } @@ -455,7 +437,7 @@ void Commissioner::ClearJoiners(void) Error Commissioner::AddJoiner(const Mac::ExtAddress *aEui64, const JoinerDiscerner *aDiscerner, - const char * aPskd, + const char *aPskd, uint32_t aTimeout) { Error error = kErrorNone; @@ -628,11 +610,6 @@ Error Commissioner::SetProvisioningUrl(const char *aProvisioningUrl) return error; } -void Commissioner::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void Commissioner::HandleTimer(void) { switch (mState) @@ -650,11 +627,6 @@ void Commissioner::HandleTimer(void) } } -void Commissioner::HandleJoinerExpirationTimer(Timer &aTimer) -{ - aTimer.Get().HandleJoinerExpirationTimer(); -} - void Commissioner::HandleJoinerExpirationTimer(void) { TimeMilli now = TimerMilli::GetNow(); @@ -688,14 +660,7 @@ void Commissioner::UpdateJoinerExpirationTimer(void) continue; } - if (joiner.mExpirationTime <= now) - { - next = now; - } - else if (joiner.mExpirationTime < next) - { - next = joiner.mExpirationTime; - } + next = Min(next, Max(now, joiner.mExpirationTime)); } if (next < now.GetDistantFuture()) @@ -711,11 +676,11 @@ void Commissioner::UpdateJoinerExpirationTimer(void) Error Commissioner::SendMgmtCommissionerGetRequest(const uint8_t *aTlvs, uint8_t aLength) { Error error = kErrorNone; - Coap::Message * message; + Coap::Message *message; Tmf::MessageInfo messageInfo(GetInstance()); Tlv tlv; - message = Get().NewPriorityConfirmablePostMessage(UriPath::kCommissionerGet); + message = Get().NewPriorityConfirmablePostMessage(kUriCommissionerGet); VerifyOrExit(message != nullptr, error = kErrorNoBufs); if (aLength > 0) @@ -730,15 +695,15 @@ Error Commissioner::SendMgmtCommissionerGetRequest(const uint8_t *aTlvs, uint8_t SuccessOrExit(error = Get().SendMessage(*message, messageInfo, Commissioner::HandleMgmtCommissionerGetResponse, this)); - LogInfo("sent MGMT_COMMISSIONER_GET.req to leader"); + LogInfo("Sent %s to leader", UriToString()); exit: FreeMessageOnError(message, error); return error; } -void Commissioner::HandleMgmtCommissionerGetResponse(void * aContext, - otMessage * aMessage, +void Commissioner::HandleMgmtCommissionerGetResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult) { @@ -746,14 +711,14 @@ void Commissioner::HandleMgmtCommissionerGetResponse(void * aConte AsCoreTypePtr(aMessageInfo), aResult); } -void Commissioner::HandleMgmtCommissionerGetResponse(Coap::Message * aMessage, +void Commissioner::HandleMgmtCommissionerGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult) { OT_UNUSED_VARIABLE(aMessageInfo); VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged); - LogInfo("received MGMT_COMMISSIONER_GET response"); + LogInfo("Received %s response", UriToString()); exit: return; @@ -762,10 +727,10 @@ void Commissioner::HandleMgmtCommissionerGetResponse(Coap::Message * aMe Error Commissioner::SendMgmtCommissionerSetRequest(const Dataset &aDataset, const uint8_t *aTlvs, uint8_t aLength) { Error error = kErrorNone; - Coap::Message * message; + Coap::Message *message; Tmf::MessageInfo messageInfo(GetInstance()); - message = Get().NewPriorityConfirmablePostMessage(UriPath::kCommissionerSet); + message = Get().NewPriorityConfirmablePostMessage(kUriCommissionerSet); VerifyOrExit(message != nullptr, error = kErrorNoBufs); if (aDataset.IsLocatorSet()) @@ -799,15 +764,15 @@ Error Commissioner::SendMgmtCommissionerSetRequest(const Dataset &aDataset, cons SuccessOrExit(error = Get().SendMessage(*message, messageInfo, Commissioner::HandleMgmtCommissionerSetResponse, this)); - LogInfo("sent MGMT_COMMISSIONER_SET.req to leader"); + LogInfo("Sent %s to leader", UriToString()); exit: FreeMessageOnError(message, error); return error; } -void Commissioner::HandleMgmtCommissionerSetResponse(void * aContext, - otMessage * aMessage, +void Commissioner::HandleMgmtCommissionerSetResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult) { @@ -815,14 +780,14 @@ void Commissioner::HandleMgmtCommissionerSetResponse(void * aConte AsCoreTypePtr(aMessageInfo), aResult); } -void Commissioner::HandleMgmtCommissionerSetResponse(Coap::Message * aMessage, +void Commissioner::HandleMgmtCommissionerSetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult) { OT_UNUSED_VARIABLE(aMessageInfo); VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged); - LogInfo("received MGMT_COMMISSIONER_SET response"); + LogInfo("Received %s response", UriToString()); exit: return; @@ -831,13 +796,13 @@ void Commissioner::HandleMgmtCommissionerSetResponse(Coap::Message * aMe Error Commissioner::SendPetition(void) { Error error = kErrorNone; - Coap::Message * message = nullptr; + Coap::Message *message = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); CommissionerIdTlv commissionerIdTlv; mTransmitAttempts++; - message = Get().NewPriorityConfirmablePostMessage(UriPath::kLeaderPetition); + message = Get().NewPriorityConfirmablePostMessage(kUriLeaderPetition); VerifyOrExit(message != nullptr, error = kErrorNoBufs); commissionerIdTlv.Init(); @@ -848,15 +813,15 @@ Error Commissioner::SendPetition(void) SuccessOrExit( error = Get().SendMessage(*message, messageInfo, Commissioner::HandleLeaderPetitionResponse, this)); - LogInfo("sent petition"); + LogInfo("Sent %s", UriToString()); exit: FreeMessageOnError(message, error); return error; } -void Commissioner::HandleLeaderPetitionResponse(void * aContext, - otMessage * aMessage, +void Commissioner::HandleLeaderPetitionResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult) { @@ -864,7 +829,7 @@ void Commissioner::HandleLeaderPetitionResponse(void * aContext, AsCoreTypePtr(aMessageInfo), aResult); } -void Commissioner::HandleLeaderPetitionResponse(Coap::Message * aMessage, +void Commissioner::HandleLeaderPetitionResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult) { @@ -877,7 +842,7 @@ void Commissioner::HandleLeaderPetitionResponse(Coap::Message * aMessage VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged, retransmit = (mState == kStatePetition)); - LogInfo("received Leader Petition response"); + LogInfo("Received %s response", UriToString()); SuccessOrExit(Tlv::Find(*aMessage, state)); VerifyOrExit(state == StateTlv::kAccept, IgnoreError(Stop(kDoNotSendKeepAlive))); @@ -895,7 +860,6 @@ void Commissioner::HandleLeaderPetitionResponse(Coap::Message * aMessage IgnoreError(Get().GetCommissionerAloc(mCommissionerAloc.GetAddress(), mSessionId)); Get().AddUnicastAddress(mCommissionerAloc); - AddCoapResources(); SetState(kStateActive); mTransmitAttempts = 0; @@ -916,18 +880,15 @@ void Commissioner::HandleLeaderPetitionResponse(Coap::Message * aMessage } } -void Commissioner::SendKeepAlive(void) -{ - SendKeepAlive(mSessionId); -} +void Commissioner::SendKeepAlive(void) { SendKeepAlive(mSessionId); } void Commissioner::SendKeepAlive(uint16_t aSessionId) { Error error = kErrorNone; - Coap::Message * message = nullptr; + Coap::Message *message = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); - message = Get().NewPriorityConfirmablePostMessage(UriPath::kLeaderKeepAlive); + message = Get().NewPriorityConfirmablePostMessage(kUriLeaderKeepAlive); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit( @@ -939,15 +900,15 @@ void Commissioner::SendKeepAlive(uint16_t aSessionId) SuccessOrExit(error = Get().SendMessage(*message, messageInfo, Commissioner::HandleLeaderKeepAliveResponse, this)); - LogInfo("sent keep alive"); + LogInfo("Sent %s", UriToString()); exit: FreeMessageOnError(message, error); LogError("send keep alive", error); } -void Commissioner::HandleLeaderKeepAliveResponse(void * aContext, - otMessage * aMessage, +void Commissioner::HandleLeaderKeepAliveResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult) { @@ -955,7 +916,7 @@ void Commissioner::HandleLeaderKeepAliveResponse(void * aContext, AsCoreTypePtr(aMessageInfo), aResult); } -void Commissioner::HandleLeaderKeepAliveResponse(Coap::Message * aMessage, +void Commissioner::HandleLeaderKeepAliveResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult) { @@ -967,7 +928,7 @@ void Commissioner::HandleLeaderKeepAliveResponse(Coap::Message * aMessag VerifyOrExit(aResult == kErrorNone && aMessage->GetCode() == Coap::kCodeChanged, IgnoreError(Stop(kDoNotSendKeepAlive))); - LogInfo("received Leader keep-alive response"); + LogInfo("Received %s response", UriToString()); SuccessOrExit(Tlv::Find(*aMessage, state)); VerifyOrExit(state == StateTlv::kAccept, IgnoreError(Stop(kDoNotSendKeepAlive))); @@ -978,12 +939,7 @@ void Commissioner::HandleLeaderKeepAliveResponse(Coap::Message * aMessag return; } -void Commissioner::HandleRelayReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleRelayReceive(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void Commissioner::HandleRelayReceive(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> void Commissioner::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { OT_UNUSED_VARIABLE(aMessageInfo); @@ -1006,10 +962,10 @@ void Commissioner::HandleRelayReceive(Coap::Message &aMessage, const Ip6::Messag SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Tlv::kJoinerDtlsEncapsulation, offset, length)); VerifyOrExit(length <= aMessage.GetLength() - offset, error = kErrorParse); - if (!Get().IsConnectionActive()) + if (!Get().IsConnectionActive()) { Mac::ExtAddress receivedId; - Joiner * joiner; + Joiner *joiner; mJoinerIid = joinerIid; mJoinerIid.ConvertToExtAddress(receivedId); @@ -1017,21 +973,29 @@ void Commissioner::HandleRelayReceive(Coap::Message &aMessage, const Ip6::Messag joiner = FindBestMatchingJoinerEntry(receivedId); VerifyOrExit(joiner != nullptr); - Get().SetPsk(joiner->mPskd); + Get().SetPsk(joiner->mPskd); mActiveJoiner = joiner; + mJoinerSessionTimer.Start(kJoinerSessionTimeoutMillis); + LogJoinerEntry("Starting new session with", *joiner); SignalJoinerEvent(kJoinerEventStart, joiner); } else { - VerifyOrExit(mJoinerIid == joinerIid); + if (mJoinerIid != joinerIid) + { + LogNote("Ignore %s (%s, 0x%04x), session in progress with (%s, 0x%04x)", UriToString(), + joinerIid.ToString().AsCString(), joinerRloc, mJoinerIid.ToString().AsCString(), mJoinerRloc); + + ExitNow(); + } } mJoinerPort = joinerPort; mJoinerRloc = joinerRloc; - LogInfo("Received Relay Receive (%s, 0x%04x)", mJoinerIid.ToString().AsCString(), mJoinerRloc); + LogInfo("Received %s (%s, 0x%04x)", UriToString(), mJoinerIid.ToString().AsCString(), mJoinerRloc); aMessage.SetOffset(offset); SuccessOrExit(error = aMessage.SetLength(offset + length)); @@ -1040,54 +1004,64 @@ void Commissioner::HandleRelayReceive(Coap::Message &aMessage, const Ip6::Messag joinerMessageInfo.GetPeerAddr().SetIid(mJoinerIid); joinerMessageInfo.SetPeerPort(mJoinerPort); - Get().HandleUdpReceive(aMessage, joinerMessageInfo); + Get().HandleUdpReceive(aMessage, joinerMessageInfo); exit: return; } -void Commissioner::HandleDatasetChanged(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +void Commissioner::HandleJoinerSessionTimer(void) { - static_cast(aContext)->HandleDatasetChanged(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); + if (mActiveJoiner != nullptr) + { + LogJoinerEntry("Timed out session with", *mActiveJoiner); + } + + Get().Disconnect(); } -void Commissioner::HandleDatasetChanged(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> +void Commissioner::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { + VerifyOrExit(mState == kStateActive); VerifyOrExit(aMessage.IsConfirmablePostRequest()); - LogInfo("received dataset changed"); + LogInfo("Received %s", UriToString()); SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); - LogInfo("sent dataset changed acknowledgment"); + LogInfo("Sent %s ack", UriToString()); exit: return; } -void Commissioner::HandleJoinerFinalize(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleJoinerFinalize(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void Commissioner::HandleJoinerFinalize(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> +void Commissioner::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { OT_UNUSED_VARIABLE(aMessageInfo); - StateTlv::State state = StateTlv::kAccept; - ProvisioningUrlTlv provisioningUrl; + StateTlv::State state = StateTlv::kAccept; + ProvisioningUrlTlv::StringType provisioningUrl; - LogInfo("received joiner finalize"); + VerifyOrExit(mState == kStateActive); - if (Tlv::FindTlv(aMessage, provisioningUrl) == kErrorNone) - { - uint8_t len = static_cast(StringLength(mProvisioningUrl, sizeof(mProvisioningUrl))); + LogInfo("Received %s", UriToString()); - if ((provisioningUrl.GetProvisioningUrlLength() != len) || - !memcmp(provisioningUrl.GetProvisioningUrl(), mProvisioningUrl, len)) + switch (Tlv::Find(aMessage, provisioningUrl)) + { + case kErrorNone: + if (!StringMatch(provisioningUrl, mProvisioningUrl)) { state = StateTlv::kReject; } + break; + + case kErrorNotFound: + break; + + default: + ExitNow(); } #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE @@ -1101,15 +1075,18 @@ void Commissioner::HandleJoinerFinalize(Coap::Message &aMessage, const Ip6::Mess #endif SendJoinFinalizeResponse(aMessage, state); + +exit: + return; } void Commissioner::SendJoinFinalizeResponse(const Coap::Message &aRequest, StateTlv::State aState) { Error error = kErrorNone; Ip6::MessageInfo joinerMessageInfo; - Coap::Message * message; + Coap::Message *message; - message = Get().NewPriorityResponseMessage(aRequest); + message = Get().NewPriorityResponseMessage(aRequest); VerifyOrExit(message != nullptr, error = kErrorNoBufs); message->SetOffset(message->GetLength()); @@ -1129,7 +1106,7 @@ void Commissioner::SendJoinFinalizeResponse(const Coap::Message &aRequest, State DumpCert("[THCI] direction=send | type=JOIN_FIN.rsp |", buf, message->GetLength() - message->GetOffset()); #endif - SuccessOrExit(error = Get().SendMessage(*message, joinerMessageInfo)); + SuccessOrExit(error = Get().SendMessage(*message, joinerMessageInfo)); SignalJoinerEvent(kJoinerEventFinalize, mActiveJoiner); @@ -1139,7 +1116,7 @@ void Commissioner::SendJoinFinalizeResponse(const Coap::Message &aRequest, State RemoveJoiner(*mActiveJoiner, kRemoveJoinerDelay); } - LogInfo("sent joiner finalize response"); + LogInfo("Sent %s response", UriToString()); exit: FreeMessageOnError(message, error); @@ -1156,14 +1133,13 @@ Error Commissioner::SendRelayTransmit(Message &aMessage, const Ip6::MessageInfo Error error = kErrorNone; ExtendedTlv tlv; - Coap::Message * message; - uint16_t offset; + Coap::Message *message; Tmf::MessageInfo messageInfo(GetInstance()); Kek kek; Get().ExtractKek(kek); - message = Get().NewPriorityNonConfirmablePostMessage(UriPath::kRelayTx); + message = Get().NewPriorityNonConfirmablePostMessage(kUriRelayTx); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = Tlv::Append(*message, mJoinerPort)); @@ -1178,9 +1154,7 @@ Error Commissioner::SendRelayTransmit(Message &aMessage, const Ip6::MessageInfo tlv.SetType(Tlv::kJoinerDtlsEncapsulation); tlv.SetLength(aMessage.GetLength()); SuccessOrExit(error = message->Append(tlv)); - offset = message->GetLength(); - SuccessOrExit(error = message->SetLength(offset + aMessage.GetLength())); - aMessage.CopyTo(0, offset, aMessage.GetLength(), *message); + SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, 0, aMessage.GetLength())); messageInfo.SetSockAddrToRlocPeerAddrTo(mJoinerRloc); @@ -1249,9 +1223,7 @@ void Commissioner::LogJoinerEntry(const char *aAction, const Joiner &aJoiner) co #else -void Commissioner::LogJoinerEntry(const char *, const Joiner &) const -{ -} +void Commissioner::LogJoinerEntry(const char *, const Joiner &) const {} #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) diff --git a/src/core/meshcop/commissioner.hpp b/src/core/meshcop/commissioner.hpp index 4c1d1904631..22e061fb5c0 100644 --- a/src/core/meshcop/commissioner.hpp +++ b/src/core/meshcop/commissioner.hpp @@ -40,9 +40,9 @@ #include -#include "coap/coap.hpp" #include "coap/coap_secure.hpp" #include "common/as_core_type.hpp" +#include "common/callback.hpp" #include "common/clearable.hpp" #include "common/locator.hpp" #include "common/log.hpp" @@ -57,6 +57,7 @@ #include "net/udp6.hpp" #include "thread/key_manager.hpp" #include "thread/mle.hpp" +#include "thread/tmf.hpp" namespace ot { @@ -64,6 +65,9 @@ namespace MeshCoP { class Commissioner : public InstanceLocator, private NonCopyable { + friend class Tmf::Agent; + friend class Tmf::SecureAgent; + public: /** * This enumeration type represents the Commissioner State. @@ -495,6 +499,9 @@ class Commissioner : public InstanceLocator, private NonCopyable static constexpr uint32_t kKeepAliveTimeout = 50; // TIMEOUT_COMM_PET (seconds) static constexpr uint32_t kRemoveJoinerDelay = 20; // Delay to remove successfully joined joiner + static constexpr uint32_t kJoinerSessionTimeoutMillis = + 1000 * OPENTHREAD_CONFIG_COMMISSIONER_JOINER_SESSION_TIMEOUT; // Expiration time for active Joiner session + enum ResignMode : uint8_t { kSendKeepAliveToResign, @@ -534,58 +541,49 @@ class Commissioner : public InstanceLocator, private NonCopyable Error AddJoiner(const Mac::ExtAddress *aEui64, const JoinerDiscerner *aDiscerner, - const char * aPskd, + const char *aPskd, uint32_t aTimeout); Error RemoveJoiner(const Mac::ExtAddress *aEui64, const JoinerDiscerner *aDiscerner, uint32_t aDelay); void RemoveJoiner(Joiner &aJoiner, uint32_t aDelay); - void AddCoapResources(void); - void RemoveCoapResources(void); - - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); - - static void HandleJoinerExpirationTimer(Timer &aTimer); - void HandleJoinerExpirationTimer(void); + void HandleTimer(void); + void HandleJoinerExpirationTimer(void); void UpdateJoinerExpirationTimer(void); - static void HandleMgmtCommissionerSetResponse(void * aContext, - otMessage * aMessage, + static void HandleMgmtCommissionerSetResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); - void HandleMgmtCommissionerSetResponse(Coap::Message * aMessage, + void HandleMgmtCommissionerSetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); - static void HandleMgmtCommissionerGetResponse(void * aContext, - otMessage * aMessage, + static void HandleMgmtCommissionerGetResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); - void HandleMgmtCommissionerGetResponse(Coap::Message * aMessage, + void HandleMgmtCommissionerGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); - static void HandleLeaderPetitionResponse(void * aContext, - otMessage * aMessage, + static void HandleLeaderPetitionResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); void HandleLeaderPetitionResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); - static void HandleLeaderKeepAliveResponse(void * aContext, - otMessage * aMessage, + static void HandleLeaderKeepAliveResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); void HandleLeaderKeepAliveResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); - static void HandleCoapsConnected(bool aConnected, void *aContext); - void HandleCoapsConnected(bool aConnected); + static void HandleSecureAgentConnected(bool aConnected, void *aContext); + void HandleSecureAgentConnected(bool aConnected); - static void HandleRelayReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleRelayReceive(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleDatasetChanged(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleDatasetChanged(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + void HandleRelayReceive(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleJoinerFinalize(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleJoinerFinalize(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + void HandleJoinerSessionTimer(void); void SendJoinFinalizeResponse(const Coap::Message &aRequest, StateTlv::State aState); @@ -604,20 +602,21 @@ class Commissioner : public InstanceLocator, private NonCopyable static const char *StateToString(State aState); + using JoinerExpirationTimer = TimerMilliIn; + using CommissionerTimer = TimerMilliIn; + using JoinerSessionTimer = TimerMilliIn; + Joiner mJoiners[OPENTHREAD_CONFIG_COMMISSIONER_MAX_JOINER_ENTRIES]; - Joiner * mActiveJoiner; + Joiner *mActiveJoiner; Ip6::InterfaceIdentifier mJoinerIid; uint16_t mJoinerPort; uint16_t mJoinerRloc; uint16_t mSessionId; uint8_t mTransmitAttempts; - TimerMilli mJoinerExpirationTimer; - TimerMilli mTimer; - - Coap::Resource mRelayReceive; - Coap::Resource mDatasetChanged; - Coap::Resource mJoinerFinalize; + JoinerExpirationTimer mJoinerExpirationTimer; + CommissionerTimer mTimer; + JoinerSessionTimer mJoinerSessionTimer; AnnounceBeginClient mAnnounceBegin; EnergyScanClient mEnergyScan; @@ -630,11 +629,14 @@ class Commissioner : public InstanceLocator, private NonCopyable State mState; - StateCallback mStateCallback; - JoinerCallback mJoinerCallback; - void * mCallbackContext; + Callback mStateCallback; + Callback mJoinerCallback; }; +DeclareTmfHandler(Commissioner, kUriDatasetChanged); +DeclareTmfHandler(Commissioner, kUriRelayRx); +DeclareTmfHandler(Commissioner, kUriJoinerFinalize); + } // namespace MeshCoP DefineMapEnum(otCommissionerState, MeshCoP::Commissioner::State); diff --git a/src/core/meshcop/dataset.cpp b/src/core/meshcop/dataset.cpp index 04780859455..5d9a02877de 100644 --- a/src/core/meshcop/dataset.cpp +++ b/src/core/meshcop/dataset.cpp @@ -83,7 +83,7 @@ Error Dataset::Info::GenerateRandom(Instance &aInstance) SuccessOrExit(error = Random::Crypto::FillBuffer(mExtendedPanId.m8, sizeof(mExtendedPanId.m8))); SuccessOrExit(error = AsCoreType(&mMeshLocalPrefix).GenerateRandomUla()); - snprintf(mNetworkName.m8, sizeof(mNetworkName), "OpenThread-%04x", mPanId); + snprintf(mNetworkName.m8, sizeof(mNetworkName), "%s-%04x", NetworkName::kNetworkNameInit, mPanId); mComponents.mIsActiveTimestampPresent = true; mComponents.mIsNetworkKeyPresent = true; @@ -162,10 +162,7 @@ Dataset::Dataset(void) memset(mTlvs, 0, sizeof(mTlvs)); } -void Dataset::Clear(void) -{ - mLength = 0; -} +void Dataset::Clear(void) { mLength = 0; } bool Dataset::IsValid(void) const { @@ -182,10 +179,7 @@ bool Dataset::IsValid(void) const return rval; } -const Tlv *Dataset::GetTlv(Tlv::Type aType) const -{ - return Tlv::FindTlv(mTlvs, mLength, aType); -} +const Tlv *Dataset::GetTlv(Tlv::Type aType) const { return Tlv::FindTlv(mTlvs, mLength, aType); } void Dataset::ConvertTo(Info &aDatasetInfo) const { @@ -402,7 +396,7 @@ Error Dataset::SetTlv(Tlv::Type aType, const void *aValue, uint8_t aLength) { Error error = kErrorNone; uint16_t bytesAvailable = sizeof(mTlvs) - mLength; - Tlv * old = GetTlv(aType); + Tlv *old = GetTlv(aType); Tlv tlv; if (old != nullptr) @@ -431,15 +425,14 @@ Error Dataset::SetTlv(Tlv::Type aType, const void *aValue, uint8_t aLength) return error; } -Error Dataset::SetTlv(const Tlv &aTlv) -{ - return SetTlv(aTlv.GetType(), aTlv.GetValue(), aTlv.GetLength()); -} +Error Dataset::SetTlv(const Tlv &aTlv) { return SetTlv(aTlv.GetType(), aTlv.GetValue(), aTlv.GetLength()); } -Error Dataset::ReadFromMessage(const Message &aMessage, uint16_t aOffset, uint8_t aLength) +Error Dataset::ReadFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength) { Error error = kErrorParse; + VerifyOrExit(aLength <= kMaxSize); + SuccessOrExit(aMessage.Read(aOffset, mTlvs, aLength)); mLength = aLength; @@ -521,7 +514,7 @@ void Dataset::RemoveTlv(Tlv *aTlv) Error Dataset::ApplyConfiguration(Instance &aInstance, bool *aIsNetworkKeyUpdated) const { - Mac::Mac & mac = aInstance.Get(); + Mac::Mac &mac = aInstance.Get(); KeyManager &keyManager = aInstance.Get(); Error error = kErrorNone; @@ -610,10 +603,7 @@ void Dataset::ConvertToActive(void) RemoveTlv(Tlv::kDelayTimer); } -const char *Dataset::TypeToString(Type aType) -{ - return (aType == kActive) ? "Active" : "Pending"; -} +const char *Dataset::TypeToString(Type aType) { return (aType == kActive) ? "Active" : "Pending"; } } // namespace MeshCoP } // namespace ot diff --git a/src/core/meshcop/dataset.hpp b/src/core/meshcop/dataset.hpp index 3809513736f..d45ea027866 100644 --- a/src/core/meshcop/dataset.hpp +++ b/src/core/meshcop/dataset.hpp @@ -795,7 +795,7 @@ class Dataset * @retval kErrorParse Could not read or parse the dataset from @p aMessage. * */ - Error ReadFromMessage(const Message &aMessage, uint16_t aOffset, uint8_t aLength); + Error ReadFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength); /** * This method sets the Dataset using an existing Dataset. diff --git a/src/core/meshcop/dataset_local.cpp b/src/core/meshcop/dataset_local.cpp index dfcb077138a..58370c3fbb7 100644 --- a/src/core/meshcop/dataset_local.cpp +++ b/src/core/meshcop/dataset_local.cpp @@ -237,7 +237,7 @@ void DatasetLocal::MoveKeysToSecureStorage(Dataset &aDataset) const KeyRef networkKeyRef = IsActive() ? kActiveDatasetNetworkKeyRef : kPendingDatasetNetworkKeyRef; KeyRef pskcRef = IsActive() ? kActiveDatasetPskcRef : kPendingDatasetPskcRef; NetworkKeyTlv *networkKeyTlv = aDataset.GetTlv(); - PskcTlv * pskcTlv = aDataset.GetTlv(); + PskcTlv *pskcTlv = aDataset.GetTlv(); if (networkKeyTlv != nullptr) { @@ -269,7 +269,7 @@ void DatasetLocal::EmplaceSecurelyStoredKeys(Dataset &aDataset) const KeyRef networkKeyRef = IsActive() ? kActiveDatasetNetworkKeyRef : kPendingDatasetNetworkKeyRef; KeyRef pskcRef = IsActive() ? kActiveDatasetPskcRef : kPendingDatasetPskcRef; NetworkKeyTlv *networkKeyTlv = aDataset.GetTlv(); - PskcTlv * pskcTlv = aDataset.GetTlv(); + PskcTlv *pskcTlv = aDataset.GetTlv(); bool moveKeys = false; size_t keyLen; Error error; diff --git a/src/core/meshcop/dataset_manager.cpp b/src/core/meshcop/dataset_manager.cpp index 355c4b0a525..7dcef3da2ad 100644 --- a/src/core/meshcop/dataset_manager.cpp +++ b/src/core/meshcop/dataset_manager.cpp @@ -59,16 +59,11 @@ DatasetManager::DatasetManager(Instance &aInstance, Dataset::Type aType, Timer:: , mTimestampValid(false) , mMgmtPending(false) , mTimer(aInstance, aTimerHandler) - , mMgmtSetCallback(nullptr) - , mMgmtSetCallbackContext(nullptr) { mTimestamp.Clear(); } -const Timestamp *DatasetManager::GetTimestamp(void) const -{ - return mTimestampValid ? &mTimestamp : nullptr; -} +const Timestamp *DatasetManager::GetTimestamp(void) const { return mTimestampValid ? &mTimestamp : nullptr; } Error DatasetManager::Restore(void) { @@ -115,16 +110,13 @@ void DatasetManager::Clear(void) SignalDatasetChange(); } -void DatasetManager::HandleDetach(void) -{ - IgnoreError(Restore()); -} +void DatasetManager::HandleDetach(void) { IgnoreError(Restore()); } Error DatasetManager::Save(const Dataset &aDataset) { Error error = kErrorNone; int compare; - bool isNetworkkeyUpdated = false; + bool isNetworkKeyUpdated = false; if (aDataset.GetTimestamp(GetType(), mTimestamp) == kErrorNone) { @@ -132,13 +124,13 @@ Error DatasetManager::Save(const Dataset &aDataset) if (IsActiveDataset()) { - SuccessOrExit(error = aDataset.ApplyConfiguration(GetInstance(), &isNetworkkeyUpdated)); + SuccessOrExit(error = aDataset.ApplyConfiguration(GetInstance(), &isNetworkKeyUpdated)); } } compare = Timestamp::Compare(mTimestampValid ? &mTimestamp : nullptr, mLocal.GetTimestamp()); - if (isNetworkkeyUpdated || compare > 0) + if (isNetworkKeyUpdated || compare > 0) { SuccessOrExit(error = mLocal.Save(aDataset)); @@ -246,15 +238,12 @@ Error DatasetManager::GetChannelMask(Mac::ChannelMask &aChannelMask) const return error; } -void DatasetManager::HandleTimer(void) -{ - SendSet(); -} +void DatasetManager::HandleTimer(void) { SendSet(); } void DatasetManager::SendSet(void) { Error error; - Coap::Message * message = nullptr; + Coap::Message *message = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); Dataset dataset; @@ -278,8 +267,7 @@ void DatasetManager::SendSet(void) } } - message = Get().NewPriorityConfirmablePostMessage(IsActiveDataset() ? UriPath::kActiveSet - : UriPath::kPendingSet); + message = Get().NewPriorityConfirmablePostMessage(IsActiveDataset() ? kUriActiveSet : kUriPendingSet); VerifyOrExit(message != nullptr, error = kErrorNoBufs); IgnoreError(Read(dataset)); @@ -310,8 +298,8 @@ void DatasetManager::SendSet(void) } } -void DatasetManager::HandleMgmtSetResponse(void * aContext, - otMessage * aMessage, +void DatasetManager::HandleMgmtSetResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aError) { @@ -323,13 +311,13 @@ void DatasetManager::HandleMgmtSetResponse(Coap::Message *aMessage, const Ip6::M { OT_UNUSED_VARIABLE(aMessageInfo); - Error error; - StateTlv stateTlv; + Error error; + uint8_t state; SuccessOrExit(error = aError); - VerifyOrExit(Tlv::FindTlv(*aMessage, stateTlv) == kErrorNone, error = kErrorParse); + VerifyOrExit(Tlv::Find(*aMessage, state) == kErrorNone, error = kErrorParse); - switch (stateTlv.GetState()) + switch (state) { case StateTlv::kReject: error = kErrorRejected; @@ -347,15 +335,12 @@ void DatasetManager::HandleMgmtSetResponse(Coap::Message *aMessage, const Ip6::M mMgmtPending = false; - if (mMgmtSetCallback != nullptr) + if (mMgmtSetCallback.IsSet()) { - otDatasetMgmtSetCallback callback = mMgmtSetCallback; - void * context = mMgmtSetCallbackContext; + Callback callbackCopy = mMgmtSetCallback; - mMgmtSetCallback = nullptr; - mMgmtSetCallbackContext = nullptr; - - callback(error, context); + mMgmtSetCallback.Clear(); + callbackCopy.Invoke(error); } mTimer.Start(kSendSetDelay); @@ -406,9 +391,9 @@ void DatasetManager::HandleGet(const Coap::Message &aMessage, const Ip6::Message SendGetResponse(aMessage, aMessageInfo, tlvs, length); } -void DatasetManager::SendGetResponse(const Coap::Message & aRequest, +void DatasetManager::SendGetResponse(const Coap::Message &aRequest, const Ip6::MessageInfo &aMessageInfo, - uint8_t * aTlvs, + uint8_t *aTlvs, uint8_t aLength) const { Error error = kErrorNone; @@ -469,20 +454,19 @@ Error DatasetManager::AppendDatasetToMessage(const Dataset::Info &aDatasetInfo, return error; } -Error DatasetManager::SendSetRequest(const Dataset::Info & aDatasetInfo, - const uint8_t * aTlvs, +Error DatasetManager::SendSetRequest(const Dataset::Info &aDatasetInfo, + const uint8_t *aTlvs, uint8_t aLength, otDatasetMgmtSetCallback aCallback, - void * aContext) + void *aContext) { Error error = kErrorNone; - Coap::Message * message = nullptr; + Coap::Message *message = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); VerifyOrExit(!mMgmtPending, error = kErrorBusy); - message = Get().NewPriorityConfirmablePostMessage(IsActiveDataset() ? UriPath::kActiveSet - : UriPath::kPendingSet); + message = Get().NewPriorityConfirmablePostMessage(IsActiveDataset() ? kUriActiveSet : kUriPendingSet); VerifyOrExit(message != nullptr, error = kErrorNoBufs); #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD @@ -521,9 +505,8 @@ Error DatasetManager::SendSetRequest(const Dataset::Info & aDatasetInfo, IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); SuccessOrExit(error = Get().SendMessage(*message, messageInfo, HandleMgmtSetResponse, this)); - mMgmtSetCallback = aCallback; - mMgmtSetCallbackContext = aContext; - mMgmtPending = true; + mMgmtSetCallback.Set(aCallback, aContext); + mMgmtPending = true; LogInfo("sent dataset set request to leader"); @@ -533,12 +516,12 @@ Error DatasetManager::SendSetRequest(const Dataset::Info & aDatasetInfo, } Error DatasetManager::SendGetRequest(const Dataset::Components &aDatasetComponents, - const uint8_t * aTlvTypes, + const uint8_t *aTlvTypes, uint8_t aLength, - const otIp6Address * aAddress) const + const otIp6Address *aAddress) const { Error error = kErrorNone; - Coap::Message * message; + Coap::Message *message; Tmf::MessageInfo messageInfo(GetInstance()); Tlv tlv; uint8_t datasetTlvs[kMaxDatasetTlvs]; @@ -606,8 +589,7 @@ Error DatasetManager::SendGetRequest(const Dataset::Components &aDatasetComponen datasetTlvs[length++] = Tlv::kChannelMask; } - message = Get().NewPriorityConfirmablePostMessage(IsActiveDataset() ? UriPath::kActiveGet - : UriPath::kPendingGet); + message = Get().NewPriorityConfirmablePostMessage(IsActiveDataset() ? kUriActiveGet : kUriPendingGet); VerifyOrExit(message != nullptr, error = kErrorNoBufs); if (aLength + length > 0) @@ -646,18 +628,10 @@ Error DatasetManager::SendGetRequest(const Dataset::Components &aDatasetComponen ActiveDatasetManager::ActiveDatasetManager(Instance &aInstance) : DatasetManager(aInstance, Dataset::kActive, ActiveDatasetManager::HandleTimer) - , mResourceGet(UriPath::kActiveGet, &ActiveDatasetManager::HandleGet, this) -#if OPENTHREAD_FTD - , mResourceSet(UriPath::kActiveSet, &ActiveDatasetManager::HandleSet, this) -#endif { - Get().AddResource(mResourceGet); } -bool ActiveDatasetManager::IsPartiallyComplete(void) const -{ - return mLocal.IsSaved() && !mTimestampValid; -} +bool ActiveDatasetManager::IsPartiallyComplete(void) const { return mLocal.IsSaved() && !mTimestampValid; } bool ActiveDatasetManager::IsCommissioned(void) const { @@ -674,9 +648,9 @@ bool ActiveDatasetManager::IsCommissioned(void) const } Error ActiveDatasetManager::Save(const Timestamp &aTimestamp, - const Message & aMessage, + const Message &aMessage, uint16_t aOffset, - uint8_t aLength) + uint16_t aLength) { Error error = kErrorNone; Dataset dataset; @@ -689,30 +663,18 @@ Error ActiveDatasetManager::Save(const Timestamp &aTimestamp, return error; } -void ActiveDatasetManager::HandleGet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleGet(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void ActiveDatasetManager::HandleGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const +template <> +void ActiveDatasetManager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { DatasetManager::HandleGet(aMessage, aMessageInfo); } -void ActiveDatasetManager::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} +void ActiveDatasetManager::HandleTimer(Timer &aTimer) { aTimer.Get().HandleTimer(); } PendingDatasetManager::PendingDatasetManager(Instance &aInstance) : DatasetManager(aInstance, Dataset::kPending, PendingDatasetManager::HandleTimer) - , mDelayTimer(aInstance, PendingDatasetManager::HandleDelayTimer) - , mResourceGet(UriPath::kPendingGet, &PendingDatasetManager::HandleGet, this) -#if OPENTHREAD_FTD - , mResourceSet(UriPath::kPendingSet, &PendingDatasetManager::HandleSet, this) -#endif + , mDelayTimer(aInstance) { - Get().AddResource(mResourceGet); } void PendingDatasetManager::Clear(void) @@ -764,9 +726,9 @@ Error PendingDatasetManager::Save(const Dataset &aDataset) } Error PendingDatasetManager::Save(const Timestamp &aTimestamp, - const Message & aMessage, + const Message &aMessage, uint16_t aOffset, - uint8_t aLength) + uint16_t aLength) { Error error = kErrorNone; Dataset dataset; @@ -800,15 +762,10 @@ void PendingDatasetManager::StartDelayTimer(void) } mDelayTimer.StartAt(dataset.GetUpdateTime(), delay); - LogInfo("delay timer started %d", delay); + LogInfo("delay timer started %lu", ToUlong(delay)); } } -void PendingDatasetManager::HandleDelayTimer(Timer &aTimer) -{ - aTimer.Get().HandleDelayTimer(); -} - void PendingDatasetManager::HandleDelayTimer(void) { DelayTimerTlv *delayTimer; @@ -842,20 +799,13 @@ void PendingDatasetManager::HandleDelayTimer(void) return; } -void PendingDatasetManager::HandleGet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleGet(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void PendingDatasetManager::HandleGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const +template <> +void PendingDatasetManager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { DatasetManager::HandleGet(aMessage, aMessageInfo); } -void PendingDatasetManager::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} +void PendingDatasetManager::HandleTimer(Timer &aTimer) { aTimer.Get().HandleTimer(); } } // namespace MeshCoP } // namespace ot diff --git a/src/core/meshcop/dataset_manager.hpp b/src/core/meshcop/dataset_manager.hpp index 9d54874296d..d6e1bbf7f85 100644 --- a/src/core/meshcop/dataset_manager.hpp +++ b/src/core/meshcop/dataset_manager.hpp @@ -37,7 +37,7 @@ #include "openthread-core-config.h" -#include "coap/coap.hpp" +#include "common/callback.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" #include "common/timer.hpp" @@ -45,6 +45,7 @@ #include "meshcop/dataset.hpp" #include "meshcop/dataset_local.hpp" #include "net/udp6.hpp" +#include "thread/tmf.hpp" namespace ot { @@ -145,11 +146,11 @@ class DatasetManager : public InstanceLocator * @retval kErrorBusy A previous request is ongoing. * */ - Error SendSetRequest(const Dataset::Info & aDatasetInfo, - const uint8_t * aTlvs, + Error SendSetRequest(const Dataset::Info &aDatasetInfo, + const uint8_t *aTlvs, uint8_t aLength, otDatasetMgmtSetCallback aCallback, - void * aContext); + void *aContext); /** * This method sends a MGMT_GET request. @@ -164,9 +165,9 @@ class DatasetManager : public InstanceLocator * */ Error SendGetRequest(const Dataset::Components &aDatasetComponents, - const uint8_t * aTlvTypes, + const uint8_t *aTlvTypes, uint8_t aLength, - const otIp6Address * aAddress) const; + const otIp6Address *aAddress) const; #if OPENTHREAD_FTD /** * This method appends the MLE Dataset TLV but excluding MeshCoP Sub Timestamp TLV. @@ -332,8 +333,8 @@ class DatasetManager : public InstanceLocator bool mTimestampValid : 1; private: - static void HandleMgmtSetResponse(void * aContext, - otMessage * aMessage, + static void HandleMgmtSetResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aError); void HandleMgmtSetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aError); @@ -344,9 +345,9 @@ class DatasetManager : public InstanceLocator void HandleDatasetUpdated(void); Error AppendDatasetToMessage(const Dataset::Info &aDatasetInfo, Message &aMessage) const; void SendSet(void); - void SendGetResponse(const Coap::Message & aRequest, + void SendGetResponse(const Coap::Message &aRequest, const Ip6::MessageInfo &aMessageInfo, - uint8_t * aTlvs, + uint8_t *aTlvs, uint8_t aLength) const; #if OPENTHREAD_FTD @@ -359,12 +360,13 @@ class DatasetManager : public InstanceLocator bool mMgmtPending : 1; TimerMilli mTimer; - otDatasetMgmtSetCallback mMgmtSetCallback; - void * mMgmtSetCallbackContext; + Callback mMgmtSetCallback; }; class ActiveDatasetManager : public DatasetManager, private NonCopyable { + friend class Tmf::Agent; + public: /** * This constructor initializes the ActiveDatasetManager object. @@ -426,7 +428,7 @@ class ActiveDatasetManager : public DatasetManager, private NonCopyable * @retval kErrorParse Could not parse the Dataset from @p aMessage. * */ - Error Save(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint8_t aLength); + Error Save(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint16_t aLength); /** * This method sets the Operational Dataset in non-volatile memory. @@ -469,12 +471,6 @@ class ActiveDatasetManager : public DatasetManager, private NonCopyable */ void StartLeader(void); - /** - * This method stops the Leader functions for maintaining the Active Operational Dataset. - * - */ - void StopLeader(void); - /** * This method generate a default Active Operational Dataset. * @@ -487,26 +483,21 @@ class ActiveDatasetManager : public DatasetManager, private NonCopyable #endif private: + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + static void HandleTimer(Timer &aTimer); void HandleTimer(void) { DatasetManager::HandleTimer(); } +}; - static void HandleGet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const; - -#if OPENTHREAD_FTD - static void HandleSet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); -#endif - - Coap::Resource mResourceGet; - +DeclareTmfHandler(ActiveDatasetManager, kUriActiveGet); #if OPENTHREAD_FTD - Coap::Resource mResourceSet; +DeclareTmfHandler(ActiveDatasetManager, kUriActiveSet); #endif -}; class PendingDatasetManager : public DatasetManager, private NonCopyable { + friend class Tmf::Agent; + public: /** * This constructor initializes the PendingDatasetManager object. @@ -571,7 +562,7 @@ class PendingDatasetManager : public DatasetManager, private NonCopyable * @param[in] aLength The length of the Operational Dataset. * */ - Error Save(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint8_t aLength); + Error Save(const Timestamp &aTimestamp, const Message &aMessage, uint16_t aOffset, uint16_t aLength); /** * This method saves the Operational Dataset in non-volatile memory. @@ -591,12 +582,6 @@ class PendingDatasetManager : public DatasetManager, private NonCopyable */ void StartLeader(void); - /** - * This method stops the Leader functions for maintaining the Active Operational Dataset. - * - */ - void StopLeader(void); - /** * This method generates a Pending Dataset from an Active Dataset. * @@ -613,25 +598,18 @@ class PendingDatasetManager : public DatasetManager, private NonCopyable static void HandleTimer(Timer &aTimer); void HandleTimer(void) { DatasetManager::HandleTimer(); } - static void HandleDelayTimer(Timer &aTimer); - void HandleDelayTimer(void); + void HandleDelayTimer(void); + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleGet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) const; + using DelayTimer = TimerMilliIn; -#if OPENTHREAD_FTD - static void HandleSet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); -#endif - - TimerMilli mDelayTimer; - - Coap::Resource mResourceGet; + DelayTimer mDelayTimer; +}; +DeclareTmfHandler(PendingDatasetManager, kUriPendingGet); #if OPENTHREAD_FTD - Coap::Resource mResourceSet; +DeclareTmfHandler(PendingDatasetManager, kUriPendingSet); #endif -}; } // namespace MeshCoP } // namespace ot diff --git a/src/core/meshcop/dataset_manager_ftd.cpp b/src/core/meshcop/dataset_manager_ftd.cpp index 4face84c56b..32644e6af93 100644 --- a/src/core/meshcop/dataset_manager_ftd.cpp +++ b/src/core/meshcop/dataset_manager_ftd.cpp @@ -263,7 +263,7 @@ Error DatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo return (state == StateTlv::kAccept) ? kErrorNone : kErrorDrop; } -void DatasetManager::SendSetResponse(const Coap::Message & aRequest, +void DatasetManager::SendSetResponse(const Coap::Message &aRequest, const Ip6::MessageInfo &aMessageInfo, StateTlv::State aState) { @@ -394,24 +394,12 @@ Error ActiveDatasetManager::GenerateLocal(void) return error; } -void ActiveDatasetManager::StartLeader(void) -{ - IgnoreError(GenerateLocal()); - Get().AddResource(mResourceSet); -} - -void ActiveDatasetManager::StopLeader(void) -{ - Get().RemoveResource(mResourceSet); -} +void ActiveDatasetManager::StartLeader(void) { IgnoreError(GenerateLocal()); } -void ActiveDatasetManager::HandleSet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleSet(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void ActiveDatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> +void ActiveDatasetManager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { + VerifyOrExit(Get().IsLeader()); SuccessOrExit(DatasetManager::HandleSet(aMessage, aMessageInfo)); IgnoreError(ApplyConfiguration()); @@ -419,24 +407,12 @@ void ActiveDatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::Message return; } -void PendingDatasetManager::StartLeader(void) -{ - StartDelayTimer(); - Get().AddResource(mResourceSet); -} - -void PendingDatasetManager::StopLeader(void) -{ - Get().RemoveResource(mResourceSet); -} - -void PendingDatasetManager::HandleSet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleSet(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} +void PendingDatasetManager::StartLeader(void) { StartDelayTimer(); } -void PendingDatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> +void PendingDatasetManager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { + VerifyOrExit(Get().IsLeader()); SuccessOrExit(DatasetManager::HandleSet(aMessage, aMessageInfo)); StartDelayTimer(); diff --git a/src/core/meshcop/dataset_updater.cpp b/src/core/meshcop/dataset_updater.cpp index 36d63d7f906..114bae5506b 100644 --- a/src/core/meshcop/dataset_updater.cpp +++ b/src/core/meshcop/dataset_updater.cpp @@ -48,14 +48,12 @@ namespace MeshCoP { DatasetUpdater::DatasetUpdater(Instance &aInstance) : InstanceLocator(aInstance) - , mCallback(nullptr) - , mCallbackContext(nullptr) - , mTimer(aInstance, DatasetUpdater::HandleTimer) + , mTimer(aInstance) , mDataset(nullptr) { } -Error DatasetUpdater::RequestUpdate(const Dataset::Info &aDataset, Callback aCallback, void *aContext) +Error DatasetUpdater::RequestUpdate(const Dataset::Info &aDataset, UpdaterCallback aCallback, void *aContext) { Error error = kErrorNone; Message *message = nullptr; @@ -71,9 +69,8 @@ Error DatasetUpdater::RequestUpdate(const Dataset::Info &aDataset, Callback aCal SuccessOrExit(error = message->Append(aDataset)); - mCallback = aCallback; - mCallbackContext = aContext; - mDataset = message; + mCallback.Set(aCallback, aContext); + mDataset = message; mTimer.Start(1); @@ -94,15 +91,7 @@ void DatasetUpdater::CancelUpdate(void) return; } -void DatasetUpdater::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - -void DatasetUpdater::HandleTimer(void) -{ - PreparePendingDataset(); -} +void DatasetUpdater::HandleTimer(void) { PreparePendingDataset(); } void DatasetUpdater::PreparePendingDataset(void) { @@ -171,10 +160,7 @@ void DatasetUpdater::Finish(Error aError) FreeMessage(mDataset); mDataset = nullptr; - if (mCallback != nullptr) - { - mCallback(aError, mCallbackContext); - } + mCallback.InvokeIfSet(aError); } void DatasetUpdater::HandleNotifierEvents(Events aEvents) diff --git a/src/core/meshcop/dataset_updater.hpp b/src/core/meshcop/dataset_updater.hpp index b3b94c0c609..ecc998d7086 100644 --- a/src/core/meshcop/dataset_updater.hpp +++ b/src/core/meshcop/dataset_updater.hpp @@ -40,6 +40,7 @@ #include +#include "common/callback.hpp" #include "common/locator.hpp" #include "common/message.hpp" #include "common/non_copyable.hpp" @@ -72,10 +73,10 @@ class DatasetUpdater : public InstanceLocator, private NonCopyable * This type represents the callback function pointer which is called when a Dataset update request finishes, * reporting success or failure status of the request. * - * The function pointer has the syntax `void (*Callback)(Error aError, void *aContext)`. + * The function pointer has the syntax `void (*UpdaterCallback)(Error aError, void *aContext)`. * */ - typedef otDatasetUpdaterCallback Callback; + typedef otDatasetUpdaterCallback UpdaterCallback; /** * This method requests an update to Operational Dataset. @@ -94,7 +95,7 @@ class DatasetUpdater : public InstanceLocator, private NonCopyable * @retval kErrorNoBufs Could not allocated buffer to save Dataset. * */ - Error RequestUpdate(const Dataset::Info &aDataset, Callback aCallback, void *aContext); + Error RequestUpdate(const Dataset::Info &aDataset, UpdaterCallback aCallback, void *aContext); /** * This method cancels an ongoing (if any) Operational Dataset update request. @@ -118,16 +119,16 @@ class DatasetUpdater : public InstanceLocator, private NonCopyable // Retry interval (in ms) when preparing and/or sending Pending Dataset fails. static constexpr uint32_t kRetryInterval = 1000; - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); - void PreparePendingDataset(void); - void Finish(Error aError); - void HandleNotifierEvents(Events aEvents); + void HandleTimer(void); + void PreparePendingDataset(void); + void Finish(Error aError); + void HandleNotifierEvents(Events aEvents); - Callback mCallback; - void * mCallbackContext; - TimerMilli mTimer; - Message * mDataset; + using UpdaterTimer = TimerMilliIn; + + Callback mCallback; + UpdaterTimer mTimer; + Message *mDataset; }; } // namespace MeshCoP diff --git a/src/core/meshcop/dtls.cpp b/src/core/meshcop/dtls.cpp index 7a558aa3d57..28337a62e69 100644 --- a/src/core/meshcop/dtls.cpp +++ b/src/core/meshcop/dtls.cpp @@ -66,8 +66,12 @@ const mbedtls_ecp_group_id Dtls::sCurves[] = {MBEDTLS_ECP_DP_SECP256R1, MBEDTLS_ #endif #if defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED) || defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) +#if (MBEDTLS_VERSION_NUMBER >= 0x03020000) +const uint16_t Dtls::sSignatures[] = {MBEDTLS_TLS1_3_SIG_ECDSA_SECP256R1_SHA256, MBEDTLS_TLS1_3_SIG_NONE}; +#else const int Dtls::sHashes[] = {MBEDTLS_MD_SHA256, MBEDTLS_MD_NONE}; #endif +#endif Dtls::Dtls(Instance &aInstance, bool aLayerTwoSecurity) : InstanceLocator(aInstance) @@ -79,12 +83,7 @@ Dtls::Dtls(Instance &aInstance, bool aLayerTwoSecurity) , mTimerSet(false) , mLayerTwoSecurity(aLayerTwoSecurity) , mReceiveMessage(nullptr) - , mConnectedHandler(nullptr) - , mReceiveHandler(nullptr) - , mContext(nullptr) , mSocket(aInstance) - , mTransportCallback(nullptr) - , mTransportContext(nullptr) , mMessageSubType(Message::kSubTypeNone) , mMessageDefaultSubType(Message::kSubTypeNone) { @@ -143,10 +142,9 @@ Error Dtls::Open(ReceiveHandler aReceiveHandler, ConnectedHandler aConnectedHand SuccessOrExit(error = mSocket.Open(&Dtls::HandleUdpReceive, this)); - mReceiveHandler = aReceiveHandler; - mConnectedHandler = aConnectedHandler; - mContext = aContext; - mState = kStateOpen; + mConnectedCallback.Set(aConnectedHandler, aContext); + mReceiveCallback.Set(aReceiveHandler, aContext); + mState = kStateOpen; exit: return error; @@ -180,17 +178,11 @@ void Dtls::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageI ExitNow(); case Dtls::kStateOpen: - IgnoreError(mSocket.Connect(Ip6::SockAddr(aMessageInfo.GetPeerAddr(), aMessageInfo.GetPeerPort()))); - mMessageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr()); mMessageInfo.SetPeerPort(aMessageInfo.GetPeerPort()); mMessageInfo.SetIsHostInterface(aMessageInfo.IsHostInterface()); - if (Get().HasUnicastAddress(aMessageInfo.GetSockAddr())) - { - mMessageInfo.SetSockAddr(aMessageInfo.GetSockAddr()); - } - + mMessageInfo.SetSockAddr(aMessageInfo.GetSockAddr()); mMessageInfo.SetSockPort(aMessageInfo.GetSockPort()); SuccessOrExit(Setup(false)); @@ -216,19 +208,16 @@ void Dtls::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageI return; } -uint16_t Dtls::GetUdpPort(void) const -{ - return mSocket.GetSockName().GetPort(); -} +uint16_t Dtls::GetUdpPort(void) const { return mSocket.GetSockName().GetPort(); } Error Dtls::Bind(uint16_t aPort) { Error error; VerifyOrExit(mState == kStateOpen, error = kErrorInvalidState); - VerifyOrExit(mTransportCallback == nullptr, error = kErrorAlready); + VerifyOrExit(!mTransportCallback.IsSet(), error = kErrorAlready); - SuccessOrExit(error = mSocket.Bind(aPort, OT_NETIF_UNSPECIFIED)); + SuccessOrExit(error = mSocket.Bind(aPort, Ip6::kNetifUnspecified)); exit: return error; @@ -240,10 +229,9 @@ Error Dtls::Bind(TransportCallback aCallback, void *aContext) VerifyOrExit(mState == kStateOpen, error = kErrorInvalidState); VerifyOrExit(!mSocket.IsBound(), error = kErrorAlready); - VerifyOrExit(mTransportCallback == nullptr, error = kErrorAlready); + VerifyOrExit(!mTransportCallback.IsSet(), error = kErrorAlready); - mTransportCallback = aCallback; - mTransportContext = aContext; + mTransportCallback.Set(aCallback, aContext); exit: return error; @@ -289,8 +277,13 @@ Error Dtls::Setup(bool aClient) #endif mbedtls_ssl_conf_rng(&mConf, Crypto::MbedTls::CryptoSecurePrng, nullptr); +#if (MBEDTLS_VERSION_NUMBER >= 0x03020000) + mbedtls_ssl_conf_min_tls_version(&mConf, MBEDTLS_SSL_VERSION_TLS1_2); + mbedtls_ssl_conf_max_tls_version(&mConf, MBEDTLS_SSL_VERSION_TLS1_2); +#else mbedtls_ssl_conf_min_version(&mConf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); mbedtls_ssl_conf_max_version(&mConf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3); +#endif OT_ASSERT(mCipherSuites[1] == 0); mbedtls_ssl_conf_ciphersuites(&mConf, mCipherSuites); @@ -302,7 +295,11 @@ Error Dtls::Setup(bool aClient) mbedtls_ssl_conf_curves(&mConf, sCurves); #endif #if defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED) || defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) +#if (MBEDTLS_VERSION_NUMBER >= 0x03020000) + mbedtls_ssl_conf_sig_algs(&mConf, sSignatures); +#else mbedtls_ssl_conf_sig_hashes(&mConf, sHashes); +#endif #endif } @@ -435,10 +432,9 @@ void Dtls::Close(void) { Disconnect(); - mState = kStateClosed; - mTransportCallback = nullptr; - mTransportContext = nullptr; - mTimerSet = false; + mState = kStateClosed; + mTimerSet = false; + mTransportCallback.Clear(); IgnoreError(mSocket.Close()); mTimer.Stop(); @@ -676,10 +672,7 @@ int Dtls::HandleMbedtlsReceive(unsigned char *aBuf, size_t aLength) return rval; } -int Dtls::HandleMbedtlsGetTimer(void *aContext) -{ - return static_cast(aContext)->HandleMbedtlsGetTimer(); -} +int Dtls::HandleMbedtlsGetTimer(void *aContext) { return static_cast(aContext)->HandleMbedtlsGetTimer(); } int Dtls::HandleMbedtlsGetTimer(void) { @@ -749,9 +742,9 @@ void Dtls::HandleMbedtlsSetTimer(uint32_t aIntermediate, uint32_t aFinish) #if (MBEDTLS_VERSION_NUMBER >= 0x03000000) -void Dtls::HandleMbedtlsExportKeys(void * aContext, +void Dtls::HandleMbedtlsExportKeys(void *aContext, mbedtls_ssl_key_export_type aType, - const unsigned char * aMasterSecret, + const unsigned char *aMasterSecret, size_t aMasterSecretLen, const unsigned char aClientRandom[32], const unsigned char aServerRandom[32], @@ -762,7 +755,7 @@ void Dtls::HandleMbedtlsExportKeys(void * aContext, } void Dtls::HandleMbedtlsExportKeys(mbedtls_ssl_key_export_type aType, - const unsigned char * aMasterSecret, + const unsigned char *aMasterSecret, size_t aMasterSecretLen, const unsigned char aClientRandom[32], const unsigned char aServerRandom[32], @@ -796,7 +789,7 @@ void Dtls::HandleMbedtlsExportKeys(mbedtls_ssl_key_export_type aType, #else -int Dtls::HandleMbedtlsExportKeys(void * aContext, +int Dtls::HandleMbedtlsExportKeys(void *aContext, const unsigned char *aMasterSecret, const unsigned char *aKeyBlock, size_t aMacLength, @@ -850,11 +843,7 @@ void Dtls::HandleTimer(void) case kStateCloseNotify: mState = kStateOpen; mTimer.Stop(); - - if (mConnectedHandler != nullptr) - { - mConnectedHandler(mContext, false); - } + mConnectedCallback.InvokeIfSet(false); break; default: @@ -878,11 +867,7 @@ void Dtls::Process(void) if (mSsl.MBEDTLS_PRIVATE(state) == MBEDTLS_SSL_HANDSHAKE_OVER) { mState = kStateConnected; - - if (mConnectedHandler != nullptr) - { - mConnectedHandler(mContext, true); - } + mConnectedCallback.InvokeIfSet(true); } } else @@ -892,10 +877,7 @@ void Dtls::Process(void) if (rval > 0) { - if (mReceiveHandler != nullptr) - { - mReceiveHandler(mContext, buf, static_cast(rval)); - } + mReceiveCallback.InvokeIfSet(buf, static_cast(rval)); } else if (rval == 0 || rval == MBEDTLS_ERR_SSL_WANT_READ || rval == MBEDTLS_ERR_SSL_WANT_WRITE) { @@ -970,20 +952,20 @@ void Dtls::HandleMbedtlsDebug(int aLevel, const char *aFile, int aLine, const ch switch (aLevel) { case 1: - LogCrit("[%hu] %s", mSocket.GetSockName().mPort, aStr); + LogCrit("[%u] %s", mSocket.GetSockName().mPort, aStr); break; case 2: - LogWarn("[%hu] %s", mSocket.GetSockName().mPort, aStr); + LogWarn("[%u] %s", mSocket.GetSockName().mPort, aStr); break; case 3: - LogInfo("[%hu] %s", mSocket.GetSockName().mPort, aStr); + LogInfo("[%u] %s", mSocket.GetSockName().mPort, aStr); break; case 4: default: - LogDebg("[%hu] %s", mSocket.GetSockName().mPort, aStr); + LogDebg("[%u] %s", mSocket.GetSockName().mPort, aStr); break; } } @@ -993,7 +975,7 @@ Error Dtls::HandleDtlsSend(const uint8_t *aBuf, uint16_t aLength, Message::SubTy Error error = kErrorNone; ot::Message *message = nullptr; - VerifyOrExit((message = mSocket.NewMessage(0)) != nullptr, error = kErrorNoBufs); + VerifyOrExit((message = mSocket.NewMessage()) != nullptr, error = kErrorNoBufs); message->SetSubType(aMessageSubType); message->SetLinkSecurityEnabled(mLayerTwoSecurity); @@ -1005,9 +987,9 @@ Error Dtls::HandleDtlsSend(const uint8_t *aBuf, uint16_t aLength, Message::SubTy message->SetSubType(aMessageSubType); } - if (mTransportCallback) + if (mTransportCallback.IsSet()) { - SuccessOrExit(error = mTransportCallback(mTransportContext, *message, mMessageInfo)); + SuccessOrExit(error = mTransportCallback.Invoke(*message, mMessageInfo)); } else { diff --git a/src/core/meshcop/dtls.hpp b/src/core/meshcop/dtls.hpp index 0f809de75e4..129ed3c7767 100644 --- a/src/core/meshcop/dtls.hpp +++ b/src/core/meshcop/dtls.hpp @@ -51,6 +51,7 @@ #endif #endif +#include "common/callback.hpp" #include "common/locator.hpp" #include "common/message.hpp" #include "common/random.hpp" @@ -394,16 +395,16 @@ class Dtls : public InstanceLocator #ifdef MBEDTLS_SSL_EXPORT_KEYS #if (MBEDTLS_VERSION_NUMBER >= 0x03000000) - static void HandleMbedtlsExportKeys(void * aContext, + static void HandleMbedtlsExportKeys(void *aContext, mbedtls_ssl_key_export_type aType, - const unsigned char * aMasterSecret, + const unsigned char *aMasterSecret, size_t aMasterSecretLen, const unsigned char aClientRandom[32], const unsigned char aServerRandom[32], mbedtls_tls_prf_types aTlsPrfType); void HandleMbedtlsExportKeys(mbedtls_ssl_key_export_type aType, - const unsigned char * aMasterSecret, + const unsigned char *aMasterSecret, size_t aMasterSecretLen, const unsigned char aClientRandom[32], const unsigned char aServerRandom[32], @@ -411,17 +412,17 @@ class Dtls : public InstanceLocator #else - static int HandleMbedtlsExportKeys(void * aContext, - const unsigned char *aMasterSecret, - const unsigned char *aKeyBlock, - size_t aMacLength, - size_t aKeyLength, - size_t aIvLength); - int HandleMbedtlsExportKeys(const unsigned char *aMasterSecret, - const unsigned char *aKeyBlock, - size_t aMacLength, - size_t aKeyLength, - size_t aIvLength); + static int HandleMbedtlsExportKeys(void *aContext, + const unsigned char *aMasterSecret, + const unsigned char *aKeyBlock, + size_t aMacLength, + size_t aKeyLength, + size_t aIvLength); + int HandleMbedtlsExportKeys(const unsigned char *aMasterSecret, + const unsigned char *aKeyBlock, + size_t aMacLength, + size_t aKeyLength, + size_t aIvLength); #endif // (MBEDTLS_VERSION_NUMBER >= 0x03000000) #endif // MBEDTLS_SSL_EXPORT_KEYS @@ -449,16 +450,20 @@ class Dtls : public InstanceLocator #endif #if defined(MBEDTLS_KEY_EXCHANGE__WITH_CERT__ENABLED) || defined(MBEDTLS_KEY_EXCHANGE_WITH_CERT_ENABLED) +#if (MBEDTLS_VERSION_NUMBER >= 0x03020000) + static const uint16_t sSignatures[]; +#else static const int sHashes[]; #endif +#endif #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED - const uint8_t * mCaChainSrc; + const uint8_t *mCaChainSrc; uint32_t mCaChainLength; - const uint8_t * mOwnCertSrc; + const uint8_t *mOwnCertSrc; uint32_t mOwnCertLength; - const uint8_t * mPrivateKeySrc; + const uint8_t *mPrivateKeySrc; uint32_t mPrivateKeyLength; mbedtls_x509_crt mCaChain; mbedtls_x509_crt mOwnCert; @@ -490,15 +495,13 @@ class Dtls : public InstanceLocator Message *mReceiveMessage; - ConnectedHandler mConnectedHandler; - ReceiveHandler mReceiveHandler; - void * mContext; + Callback mConnectedCallback; + Callback mReceiveCallback; Ip6::MessageInfo mMessageInfo; Ip6::Udp::Socket mSocket; - TransportCallback mTransportCallback; - void * mTransportContext; + Callback mTransportCallback; Message::SubType mMessageSubType; Message::SubType mMessageDefaultSubType; diff --git a/src/core/meshcop/energy_scan_client.cpp b/src/core/meshcop/energy_scan_client.cpp index 09e8fbdd194..905d8a66f28 100644 --- a/src/core/meshcop/energy_scan_client.cpp +++ b/src/core/meshcop/energy_scan_client.cpp @@ -43,6 +43,7 @@ #include "common/instance.hpp" #include "common/locator_getters.hpp" #include "common/log.hpp" +#include "common/num_utils.hpp" #include "meshcop/meshcop.hpp" #include "meshcop/meshcop_tlvs.hpp" #include "thread/thread_netif.hpp" @@ -54,30 +55,26 @@ RegisterLogModule("EnergyScanClnt"); EnergyScanClient::EnergyScanClient(Instance &aInstance) : InstanceLocator(aInstance) - , mCallback(nullptr) - , mContext(nullptr) - , mEnergyScan(UriPath::kEnergyReport, &EnergyScanClient::HandleReport, this) { - Get().AddResource(mEnergyScan); } Error EnergyScanClient::SendQuery(uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod, uint16_t aScanDuration, - const Ip6::Address & aAddress, + const Ip6::Address &aAddress, otCommissionerEnergyReportCallback aCallback, - void * aContext) + void *aContext) { Error error = kErrorNone; MeshCoP::ChannelMaskTlv channelMask; Tmf::MessageInfo messageInfo(GetInstance()); - Coap::Message * message = nullptr; + Coap::Message *message = nullptr; VerifyOrExit(Get().IsActive(), error = kErrorInvalidState); VerifyOrExit((message = Get().NewPriorityMessage()) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = message->InitAsPost(aAddress, UriPath::kEnergyScan)); + SuccessOrExit(error = message->InitAsPost(aAddress, kUriEnergyScan)); SuccessOrExit(error = message->SetPayloadMarker()); SuccessOrExit( @@ -94,49 +91,34 @@ Error EnergyScanClient::SendQuery(uint32_t aChannelMas messageInfo.SetSockAddrToRlocPeerAddrTo(aAddress); SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("sent query"); + LogInfo("Sent %s", UriToString()); - mCallback = aCallback; - mContext = aContext; + mCallback.Set(aCallback, aContext); exit: FreeMessageOnError(message, error); return error; } -void EnergyScanClient::HandleReport(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +template <> +void EnergyScanClient::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - static_cast(aContext)->HandleReport(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void EnergyScanClient::HandleReport(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) -{ - uint32_t mask; - - OT_TOOL_PACKED_BEGIN - struct - { - MeshCoP::EnergyListTlv tlv; - uint8_t list[OPENTHREAD_CONFIG_TMF_ENERGY_SCAN_MAX_RESULTS]; - } OT_TOOL_PACKED_END energyList; + uint32_t mask; + MeshCoP::EnergyListTlv energyListTlv; VerifyOrExit(aMessage.IsConfirmablePostRequest()); - LogInfo("received report"); + LogInfo("Received %s", UriToString()); VerifyOrExit((mask = MeshCoP::ChannelMaskTlv::GetChannelMask(aMessage)) != 0); - SuccessOrExit(MeshCoP::Tlv::FindTlv(aMessage, MeshCoP::Tlv::kEnergyList, sizeof(energyList), energyList.tlv)); - VerifyOrExit(energyList.tlv.IsValid()); + SuccessOrExit(MeshCoP::Tlv::FindTlv(aMessage, MeshCoP::Tlv::kEnergyList, sizeof(energyListTlv), energyListTlv)); - if (mCallback != nullptr) - { - mCallback(mask, energyList.list, energyList.tlv.GetLength(), mContext); - } + mCallback.InvokeIfSet(mask, energyListTlv.GetEnergyList(), energyListTlv.GetEnergyListLength()); SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); - LogInfo("sent report response"); + LogInfo("Sent %s ack", UriToString()); exit: return; diff --git a/src/core/meshcop/energy_scan_client.hpp b/src/core/meshcop/energy_scan_client.hpp index 238344cbef9..97d9873bb9b 100644 --- a/src/core/meshcop/energy_scan_client.hpp +++ b/src/core/meshcop/energy_scan_client.hpp @@ -41,9 +41,11 @@ #include #include "coap/coap.hpp" +#include "common/callback.hpp" #include "common/locator.hpp" #include "net/ip6_address.hpp" #include "net/udp6.hpp" +#include "thread/tmf.hpp" namespace ot { @@ -53,6 +55,8 @@ namespace ot { */ class EnergyScanClient : public InstanceLocator { + friend class Tmf::Agent; + public: /** * This constructor initializes the object. @@ -79,20 +83,18 @@ class EnergyScanClient : public InstanceLocator uint8_t aCount, uint16_t aPeriod, uint16_t aScanDuration, - const Ip6::Address & aAddress, + const Ip6::Address &aAddress, otCommissionerEnergyReportCallback aCallback, - void * aContext); + void *aContext); private: - static void HandleReport(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleReport(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - - otCommissionerEnergyReportCallback mCallback; - void * mContext; + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - Coap::Resource mEnergyScan; + Callback mCallback; }; +DeclareTmfHandler(EnergyScanClient, kUriEnergyReport); + /** * @} */ diff --git a/src/core/meshcop/joiner.cpp b/src/core/meshcop/joiner.cpp index 4c483164997..84b7d556501 100644 --- a/src/core/meshcop/joiner.cpp +++ b/src/core/meshcop/joiner.cpp @@ -62,17 +62,13 @@ Joiner::Joiner(Instance &aInstance) , mId() , mDiscerner() , mState(kStateIdle) - , mCallback(nullptr) - , mContext(nullptr) , mJoinerRouterIndex(0) , mFinalizeMessage(nullptr) - , mTimer(aInstance, Joiner::HandleTimer) - , mJoinerEntrust(UriPath::kJoinerEntrust, &Joiner::HandleJoinerEntrust, this) + , mTimer(aInstance) { SetIdFromIeeeEui64(); mDiscerner.Clear(); memset(mJoinerRouters, 0, sizeof(mJoinerRouters)); - Get().AddResource(mJoinerEntrust); } void Joiner::SetIdFromIeeeEui64(void) @@ -83,10 +79,7 @@ void Joiner::SetIdFromIeeeEui64(void) ComputeJoinerId(eui64, mId); } -const JoinerDiscerner *Joiner::GetDiscerner(void) const -{ - return mDiscerner.IsEmpty() ? nullptr : &mDiscerner; -} +const JoinerDiscerner *Joiner::GetDiscerner(void) const { return mDiscerner.IsEmpty() ? nullptr : &mDiscerner; } Error Joiner::SetDiscerner(const JoinerDiscerner &aDiscerner) { @@ -128,14 +121,14 @@ void Joiner::SetState(State aState) return; } -Error Joiner::Start(const char * aPskd, - const char * aProvisioningUrl, - const char * aVendorName, - const char * aVendorModel, - const char * aVendorSwVersion, - const char * aVendorData, +Error Joiner::Start(const char *aPskd, + const char *aProvisioningUrl, + const char *aVendorName, + const char *aVendorModel, + const char *aVendorSwVersion, + const char *aVendorData, otJoinerCallback aCallback, - void * aContext) + void *aContext) { Error error; JoinerPskd joinerPskd; @@ -159,8 +152,8 @@ Error Joiner::Start(const char * aPskd, Get().SetExtAddress(randomAddress); Get().UpdateLinkLocalAddress(); - SuccessOrExit(error = Get().Start(kJoinerUdpPort)); - Get().SetPsk(joinerPskd); + SuccessOrExit(error = Get().Start(kJoinerUdpPort)); + Get().SetPsk(joinerPskd); for (JoinerRouter &router : mJoinerRouters) { @@ -182,9 +175,8 @@ Error Joiner::Start(const char * aPskd, SuccessOrExit(error = Get().Discover(Mac::ChannelMask(0), Get().GetPanId(), /* aJoiner */ true, /* aEnableFiltering */ true, &filterIndexes, HandleDiscoverResult, this)); - mCallback = aCallback; - mContext = aContext; + mCallback.Set(aCallback, aContext); SetState(kStateDiscover); exit: @@ -202,7 +194,7 @@ void Joiner::Stop(void) LogInfo("Joiner stopped"); // Callback is set to `nullptr` to skip calling it from `Finish()` - mCallback = nullptr; + mCallback.Clear(); Finish(kErrorAbort); } @@ -217,24 +209,21 @@ void Joiner::Finish(Error aError) case kStateConnected: case kStateEntrust: case kStateJoined: - Get().Disconnect(); + Get().Disconnect(); IgnoreError(Get().RemoveUnsecurePort(kJoinerUdpPort)); mTimer.Stop(); OT_FALL_THROUGH; case kStateDiscover: - Get().Stop(); + Get().Stop(); break; } SetState(kStateIdle); FreeJoinerFinalizeMessage(); - if (mCallback) - { - mCallback(aError, mContext); - } + mCallback.InvokeIfSet(aError); exit: return; @@ -244,25 +233,13 @@ uint8_t Joiner::CalculatePriority(int8_t aRssi, bool aSteeringDataAllowsAny) { int16_t priority; - if (aRssi == OT_RADIO_RSSI_INVALID) + if (aRssi == Radio::kInvalidRssi) { aRssi = -127; } - // Limit the RSSI to range (-128, 0), i.e. -128 < aRssi < 0. - - if (aRssi <= -128) - { - priority = -127; - } - else if (aRssi >= 0) - { - priority = -1; - } - else - { - priority = aRssi; - } + // Clamp the RSSI to range [-127, -1] + priority = Clamp(aRssi, -127, -1); // Assign higher priority to networks with an exact match of Joiner // ID in the Steering Data (128 < priority < 256) compared to ones @@ -396,7 +373,7 @@ Error Joiner::Connect(JoinerRouter &aRouter) sockAddr.GetAddress().SetToLinkLocalAddress(aRouter.mExtAddr); - SuccessOrExit(error = Get().Connect(sockAddr, Joiner::HandleSecureCoapClientConnect, this)); + SuccessOrExit(error = Get().Connect(sockAddr, Joiner::HandleSecureCoapClientConnect, this)); SetState(kStateConnect); @@ -418,7 +395,7 @@ void Joiner::HandleSecureCoapClientConnect(bool aConnected) { SetState(kStateConnected); SendJoinerFinalize(); - mTimer.Start(kReponseTimeout); + mTimer.Start(kResponseTimeout); } else { @@ -436,30 +413,18 @@ Error Joiner::PrepareJoinerFinalizeMessage(const char *aProvisioningUrl, const char *aVendorData) { Error error = kErrorNone; - VendorNameTlv vendorNameTlv; - VendorModelTlv vendorModelTlv; - VendorSwVersionTlv vendorSwVersionTlv; VendorStackVersionTlv vendorStackVersionTlv; - ProvisioningUrlTlv provisioningUrlTlv; - mFinalizeMessage = Get().NewPriorityConfirmablePostMessage(UriPath::kJoinerFinalize); + mFinalizeMessage = Get().NewPriorityConfirmablePostMessage(kUriJoinerFinalize); VerifyOrExit(mFinalizeMessage != nullptr, error = kErrorNoBufs); mFinalizeMessage->SetOffset(mFinalizeMessage->GetLength()); SuccessOrExit(error = Tlv::Append(*mFinalizeMessage, StateTlv::kAccept)); - vendorNameTlv.Init(); - vendorNameTlv.SetVendorName(aVendorName); - SuccessOrExit(error = vendorNameTlv.AppendTo(*mFinalizeMessage)); - - vendorModelTlv.Init(); - vendorModelTlv.SetVendorModel(aVendorModel); - SuccessOrExit(error = vendorModelTlv.AppendTo(*mFinalizeMessage)); - - vendorSwVersionTlv.Init(); - vendorSwVersionTlv.SetVendorSwVersion(aVendorSwVersion); - SuccessOrExit(error = vendorSwVersionTlv.AppendTo(*mFinalizeMessage)); + SuccessOrExit(error = Tlv::Append(*mFinalizeMessage, aVendorName)); + SuccessOrExit(error = Tlv::Append(*mFinalizeMessage, aVendorModel)); + SuccessOrExit(error = Tlv::Append(*mFinalizeMessage, aVendorSwVersion)); vendorStackVersionTlv.Init(); vendorStackVersionTlv.SetOui(OPENTHREAD_CONFIG_STACK_VENDOR_OUI); @@ -470,18 +435,12 @@ Error Joiner::PrepareJoinerFinalizeMessage(const char *aProvisioningUrl, if (aVendorData != nullptr) { - VendorDataTlv vendorDataTlv; - vendorDataTlv.Init(); - vendorDataTlv.SetVendorData(aVendorData); - SuccessOrExit(error = vendorDataTlv.AppendTo(*mFinalizeMessage)); + SuccessOrExit(error = Tlv::Append(*mFinalizeMessage, aVendorData)); } - provisioningUrlTlv.Init(); - provisioningUrlTlv.SetProvisioningUrl(aProvisioningUrl); - - if (provisioningUrlTlv.GetLength() > 0) + if (aProvisioningUrl != nullptr) { - SuccessOrExit(error = provisioningUrlTlv.AppendTo(*mFinalizeMessage)); + SuccessOrExit(error = Tlv::Append(*mFinalizeMessage, aProvisioningUrl)); } exit: @@ -512,17 +471,17 @@ void Joiner::SendJoinerFinalize(void) LogCertMessage("[THCI] direction=send | type=JOIN_FIN.req |", *mFinalizeMessage); #endif - SuccessOrExit(Get().SendMessage(*mFinalizeMessage, Joiner::HandleJoinerFinalizeResponse, this)); + SuccessOrExit(Get().SendMessage(*mFinalizeMessage, Joiner::HandleJoinerFinalizeResponse, this)); mFinalizeMessage = nullptr; - LogInfo("Joiner sent finalize"); + LogInfo("Sent %s", UriToString()); exit: return; } -void Joiner::HandleJoinerFinalizeResponse(void * aContext, - otMessage * aMessage, +void Joiner::HandleJoinerFinalizeResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult) { @@ -544,32 +503,27 @@ void Joiner::HandleJoinerFinalizeResponse(Coap::Message *aMessage, const Ip6::Me SuccessOrExit(Tlv::Find(*aMessage, state)); SetState(kStateEntrust); - mTimer.Start(kReponseTimeout); + mTimer.Start(kResponseTimeout); - LogInfo("Joiner received finalize response %d", state); + LogInfo("Received %s %d", UriToString(), state); #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE LogCertMessage("[THCI] direction=recv | type=JOIN_FIN.rsp |", *aMessage); #endif exit: - Get().Disconnect(); + Get().Disconnect(); IgnoreError(Get().RemoveUnsecurePort(kJoinerUdpPort)); } -void Joiner::HandleJoinerEntrust(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleJoinerEntrust(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void Joiner::HandleJoinerEntrust(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> void Joiner::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error; Dataset::Info datasetInfo; VerifyOrExit(mState == kStateEntrust && aMessage.IsConfirmablePostRequest(), error = kErrorDrop); - LogInfo("Joiner received entrust"); + LogInfo("Received %s", UriToString()); LogCert("[THCI] direction=recv | type=JOIN_ENT.ntf"); datasetInfo.Clear(); @@ -595,7 +549,7 @@ void Joiner::HandleJoinerEntrust(Coap::Message &aMessage, const Ip6::MessageInfo void Joiner::SendJoinerEntrustResponse(const Coap::Message &aRequest, const Ip6::MessageInfo &aRequestInfo) { Error error = kErrorNone; - Coap::Message * message; + Coap::Message *message; Ip6::MessageInfo responseInfo(aRequestInfo); message = Get().NewPriorityResponseMessage(aRequest); @@ -608,30 +562,19 @@ void Joiner::SendJoinerEntrustResponse(const Coap::Message &aRequest, const Ip6: SetState(kStateJoined); - LogInfo("Joiner sent entrust response"); + LogInfo("Sent %s response", UriToString()); LogCert("[THCI] direction=send | type=JOIN_ENT.rsp"); exit: FreeMessageOnError(message, error); } -void Joiner::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void Joiner::HandleTimer(void) { Error error = kErrorNone; switch (mState) { - case kStateIdle: - case kStateDiscover: - case kStateConnect: - OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); - case kStateConnected: case kStateEntrust: error = kErrorResponseTimeout; @@ -646,6 +589,11 @@ void Joiner::HandleTimer(void) error = kErrorNone; break; + + case kStateIdle: + case kStateDiscover: + case kStateConnect: + OT_ASSERT(false); } Finish(error); diff --git a/src/core/meshcop/joiner.hpp b/src/core/meshcop/joiner.hpp index ac894e97c48..81e4b2bbbd1 100644 --- a/src/core/meshcop/joiner.hpp +++ b/src/core/meshcop/joiner.hpp @@ -40,10 +40,10 @@ #include -#include "coap/coap.hpp" #include "coap/coap_message.hpp" #include "coap/coap_secure.hpp" #include "common/as_core_type.hpp" +#include "common/callback.hpp" #include "common/locator.hpp" #include "common/log.hpp" #include "common/message.hpp" @@ -53,6 +53,7 @@ #include "meshcop/meshcop.hpp" #include "meshcop/meshcop_tlvs.hpp" #include "thread/discover_scanner.hpp" +#include "thread/tmf.hpp" namespace ot { @@ -60,6 +61,8 @@ namespace MeshCoP { class Joiner : public InstanceLocator, private NonCopyable { + friend class Tmf::Agent; + public: /** * This enumeration type defines the Joiner State. @@ -100,14 +103,14 @@ class Joiner : public InstanceLocator, private NonCopyable * @retval kErrorInvalidState The IPv6 stack is not enabled or Thread stack is fully enabled. * */ - Error Start(const char * aPskd, - const char * aProvisioningUrl, - const char * aVendorName, - const char * aVendorModel, - const char * aVendorSwVersion, - const char * aVendorData, + Error Start(const char *aPskd, + const char *aProvisioningUrl, + const char *aVendorName, + const char *aVendorModel, + const char *aVendorSwVersion, + const char *aVendorData, otJoinerCallback aCallback, - void * aContext); + void *aContext); /** * This method stops the Joiner service. @@ -182,7 +185,7 @@ class Joiner : public InstanceLocator, private NonCopyable static constexpr uint16_t kJoinerUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT; static constexpr uint32_t kConfigExtAddressDelay = 100; // in msec. - static constexpr uint32_t kReponseTimeout = 4000; ///< Max wait time to receive response (in msec). + static constexpr uint32_t kResponseTimeout = 4000; ///< Max wait time to receive response (in msec). struct JoinerRouter { @@ -199,17 +202,15 @@ class Joiner : public InstanceLocator, private NonCopyable static void HandleSecureCoapClientConnect(bool aConnected, void *aContext); void HandleSecureCoapClientConnect(bool aConnected); - static void HandleJoinerFinalizeResponse(void * aContext, - otMessage * aMessage, + static void HandleJoinerFinalizeResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); void HandleJoinerFinalizeResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); - static void HandleJoinerEntrust(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleJoinerEntrust(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); + void HandleTimer(void); void SetState(State aState); void SetIdFromIeeeEui64(void); @@ -232,23 +233,25 @@ class Joiner : public InstanceLocator, private NonCopyable void LogCertMessage(const char *aText, const Coap::Message &aMessage) const; #endif + using JoinerTimer = TimerMilliIn; + Mac::ExtAddress mId; JoinerDiscerner mDiscerner; State mState; - otJoinerCallback mCallback; - void * mContext; + Callback mCallback; JoinerRouter mJoinerRouters[OPENTHREAD_CONFIG_JOINER_MAX_CANDIDATES]; uint16_t mJoinerRouterIndex; Coap::Message *mFinalizeMessage; - TimerMilli mTimer; - Coap::Resource mJoinerEntrust; + JoinerTimer mTimer; }; +DeclareTmfHandler(Joiner, kUriJoinerEntrust); + } // namespace MeshCoP DefineMapEnum(otJoinerState, MeshCoP::Joiner::State); diff --git a/src/core/meshcop/joiner_router.cpp b/src/core/meshcop/joiner_router.cpp index 679ae817fb3..09616023c39 100644 --- a/src/core/meshcop/joiner_router.cpp +++ b/src/core/meshcop/joiner_router.cpp @@ -57,12 +57,10 @@ RegisterLogModule("JoinerRouter"); JoinerRouter::JoinerRouter(Instance &aInstance) : InstanceLocator(aInstance) , mSocket(aInstance) - , mRelayTransmit(UriPath::kRelayTx, &JoinerRouter::HandleRelayTransmit, this) - , mTimer(aInstance, JoinerRouter::HandleTimer) + , mTimer(aInstance) , mJoinerUdpPort(0) , mIsJoinerPortConfigured(false) { - Get().AddResource(mRelayTransmit); } void JoinerRouter::HandleNotifierEvents(Events aEvents) @@ -132,17 +130,16 @@ void JoinerRouter::HandleUdpReceive(void *aContext, otMessage *aMessage, const o void JoinerRouter::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error; - Coap::Message * message = nullptr; + Coap::Message *message = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); ExtendedTlv tlv; uint16_t borderAgentRloc; - uint16_t offset; LogInfo("JoinerRouter::HandleUdpReceive"); SuccessOrExit(error = GetBorderAgentRloc(Get(), borderAgentRloc)); - message = Get().NewPriorityNonConfirmablePostMessage(UriPath::kRelayRx); + message = Get().NewPriorityNonConfirmablePostMessage(kUriRelayRx); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = Tlv::Append(*message, aMessageInfo.GetPeerPort())); @@ -152,26 +149,19 @@ void JoinerRouter::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &a tlv.SetType(Tlv::kJoinerDtlsEncapsulation); tlv.SetLength(aMessage.GetLength() - aMessage.GetOffset()); SuccessOrExit(error = message->Append(tlv)); - offset = message->GetLength(); - SuccessOrExit(error = message->SetLength(offset + tlv.GetLength())); - aMessage.CopyTo(aMessage.GetOffset(), offset, tlv.GetLength(), *message); + SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, aMessage.GetOffset(), tlv.GetLength())); messageInfo.SetSockAddrToRlocPeerAddrTo(borderAgentRloc); SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("Sent relay rx"); + LogInfo("Sent %s", UriToString()); exit: FreeMessageOnError(message, error); } -void JoinerRouter::HandleRelayTransmit(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleRelayTransmit(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void JoinerRouter::HandleRelayTransmit(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> void JoinerRouter::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { OT_UNUSED_VARIABLE(aMessageInfo); @@ -181,13 +171,13 @@ void JoinerRouter::HandleRelayTransmit(Coap::Message &aMessage, const Ip6::Messa Kek kek; uint16_t offset; uint16_t length; - Message * message = nullptr; + Message *message = nullptr; Message::Settings settings(Message::kNoLinkSecurity, Message::kPriorityNet); Ip6::MessageInfo messageInfo; VerifyOrExit(aMessage.IsNonConfirmablePostRequest(), error = kErrorDrop); - LogInfo("Received relay transmit"); + LogInfo("Received %s", UriToString()); SuccessOrExit(error = Tlv::Find(aMessage, joinerPort)); SuccessOrExit(error = Tlv::Find(aMessage, joinerIid)); @@ -196,8 +186,7 @@ void JoinerRouter::HandleRelayTransmit(Coap::Message &aMessage, const Ip6::Messa VerifyOrExit((message = mSocket.NewMessage(0, settings)) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = message->SetLength(length)); - aMessage.CopyTo(offset, 0, length, *message); + SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, offset, length)); messageInfo.GetPeerAddr().SetToLinkLocalAddress(joinerIid); messageInfo.SetPeerPort(joinerPort); @@ -218,7 +207,7 @@ void JoinerRouter::HandleRelayTransmit(Coap::Message &aMessage, const Ip6::Messa void JoinerRouter::DelaySendingJoinerEntrust(const Ip6::MessageInfo &aMessageInfo, const Kek &aKek) { Error error = kErrorNone; - Message * message = Get().Allocate(Message::kTypeOther); + Message *message = Get().Allocate(Message::kTypeOther); JoinerEntrustMetadata metadata; VerifyOrExit(message != nullptr, error = kErrorNoBufs); @@ -242,20 +231,12 @@ void JoinerRouter::DelaySendingJoinerEntrust(const Ip6::MessageInfo &aMessageInf LogError("schedule joiner entrust", error); } -void JoinerRouter::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - -void JoinerRouter::HandleTimer(void) -{ - SendDelayedJoinerEntrust(); -} +void JoinerRouter::HandleTimer(void) { SendDelayedJoinerEntrust(); } void JoinerRouter::SendDelayedJoinerEntrust(void) { JoinerEntrustMetadata metadata; - Message * message = mDelayedJoinEnts.GetHead(); + Message *message = mDelayedJoinEnts.GetHead(); VerifyOrExit(message != nullptr); VerifyOrExit(!mTimer.IsRunning()); @@ -292,11 +273,10 @@ Error JoinerRouter::SendJoinerEntrust(const Ip6::MessageInfo &aMessageInfo) IgnoreError(Get().AbortTransaction(&JoinerRouter::HandleJoinerEntrustResponse, this)); - LogInfo("Sending JOIN_ENT.ntf"); SuccessOrExit(error = Get().SendMessage(*message, aMessageInfo, &JoinerRouter::HandleJoinerEntrustResponse, this)); - LogInfo("Sent joiner entrust length = %d", message->GetLength()); + LogInfo("Sent %s (len= %d)", UriToString(), message->GetLength()); LogCert("[THCI] direction=send | type=JOIN_ENT.ntf"); exit: @@ -310,10 +290,10 @@ Coap::Message *JoinerRouter::PrepareJoinerEntrustMessage(void) Coap::Message *message = nullptr; Dataset dataset; NetworkNameTlv networkName; - const Tlv * tlv; + const Tlv *tlv; NetworkKey networkKey; - message = Get().NewPriorityConfirmablePostMessage(UriPath::kJoinerEntrust); + message = Get().NewPriorityConfirmablePostMessage(kUriJoinerEntrust); VerifyOrExit(message != nullptr, error = kErrorNoBufs); message->SetSubType(Message::kSubTypeJoinerEntrust); @@ -380,8 +360,8 @@ Coap::Message *JoinerRouter::PrepareJoinerEntrustMessage(void) return message; } -void JoinerRouter::HandleJoinerEntrustResponse(void * aContext, - otMessage * aMessage, +void JoinerRouter::HandleJoinerEntrustResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult) { @@ -389,7 +369,7 @@ void JoinerRouter::HandleJoinerEntrustResponse(void * aContext, AsCoreTypePtr(aMessageInfo), aResult); } -void JoinerRouter::HandleJoinerEntrustResponse(Coap::Message * aMessage, +void JoinerRouter::HandleJoinerEntrustResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult) { @@ -401,7 +381,7 @@ void JoinerRouter::HandleJoinerEntrustResponse(Coap::Message * aMessage, VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged); - LogInfo("Receive joiner entrust response"); + LogInfo("Receive %s response", UriToString()); LogCert("[THCI] direction=recv | type=JOIN_ENT.rsp"); exit: diff --git a/src/core/meshcop/joiner_router.hpp b/src/core/meshcop/joiner_router.hpp index e8c61167a58..4831c2f2bf1 100644 --- a/src/core/meshcop/joiner_router.hpp +++ b/src/core/meshcop/joiner_router.hpp @@ -38,7 +38,6 @@ #if OPENTHREAD_FTD -#include "coap/coap.hpp" #include "coap/coap_message.hpp" #include "common/locator.hpp" #include "common/message.hpp" @@ -49,6 +48,7 @@ #include "meshcop/meshcop_tlvs.hpp" #include "net/udp6.hpp" #include "thread/key_manager.hpp" +#include "thread/tmf.hpp" namespace ot { @@ -57,6 +57,7 @@ namespace MeshCoP { class JoinerRouter : public InstanceLocator, private NonCopyable { friend class ot::Notifier; + friend class Tmf::Agent; public: /** @@ -101,17 +102,15 @@ class JoinerRouter : public InstanceLocator, private NonCopyable static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleRelayTransmit(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleRelayTransmit(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleJoinerEntrustResponse(void * aContext, - otMessage * aMessage, + static void HandleJoinerEntrustResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); void HandleJoinerEntrustResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); + void HandleTimer(void); void Start(void); void DelaySendingJoinerEntrust(const Ip6::MessageInfo &aMessageInfo, const Kek &aKek); @@ -119,17 +118,20 @@ class JoinerRouter : public InstanceLocator, private NonCopyable Error SendJoinerEntrust(const Ip6::MessageInfo &aMessageInfo); Coap::Message *PrepareJoinerEntrustMessage(void); + using JoinerRouterTimer = TimerMilliIn; + Ip6::Udp::Socket mSocket; - Coap::Resource mRelayTransmit; - TimerMilli mTimer; - MessageQueue mDelayedJoinEnts; + JoinerRouterTimer mTimer; + MessageQueue mDelayedJoinEnts; uint16_t mJoinerUdpPort; bool mIsJoinerPortConfigured : 1; }; +DeclareTmfHandler(JoinerRouter, kUriRelayTx); + } // namespace MeshCoP } // namespace ot diff --git a/src/core/meshcop/meshcop.cpp b/src/core/meshcop/meshcop.cpp index a61b0ae31fe..6fb17b02012 100644 --- a/src/core/meshcop/meshcop.cpp +++ b/src/core/meshcop/meshcop.cpp @@ -37,7 +37,6 @@ #include "common/debug.hpp" #include "common/locator_getters.hpp" #include "common/string.hpp" -#include "crypto/pbkdf2_cmac.hpp" #include "crypto/sha256.hpp" #include "mac/mac_types.hpp" #include "thread/thread_netif.hpp" @@ -174,11 +173,12 @@ JoinerDiscerner::InfoString JoinerDiscerner::ToString(void) const } else if (mLength <= sizeof(uint32_t) * CHAR_BIT) { - string.Append("0x%08x", static_cast(mValue)); + string.Append("0x%08lx", ToUlong(static_cast(mValue))); } else { - string.Append("0x%x-%08x", static_cast(mValue >> 32), static_cast(mValue)); + string.Append("0x%lx-%08lx", ToUlong(static_cast(mValue >> 32)), + ToUlong(static_cast(mValue))); } string.Append("/len:%d", mLength); @@ -316,14 +316,14 @@ Error GetBorderAgentRloc(ThreadNetif &aNetif, uint16_t &aRloc) } #if OPENTHREAD_FTD -Error GeneratePskc(const char * aPassPhrase, - const NetworkName & aNetworkName, +Error GeneratePskc(const char *aPassPhrase, + const NetworkName &aNetworkName, const ExtendedPanId &aExtPanId, - Pskc & aPskc) + Pskc &aPskc) { Error error = kErrorNone; const char saltPrefix[] = "Thread"; - uint8_t salt[Crypto::Pbkdf2::kMaxSaltLength]; + uint8_t salt[OT_CRYPTO_PBDKF2_MAX_SALT_SIZE]; uint16_t saltLen = 0; uint16_t passphraseLen; uint8_t networkNameLen; @@ -348,8 +348,8 @@ Error GeneratePskc(const char * aPassPhrase, memcpy(salt + saltLen, aNetworkName.GetAsCString(), networkNameLen); saltLen += networkNameLen; - Crypto::Pbkdf2::GenerateKey(reinterpret_cast(aPassPhrase), passphraseLen, salt, saltLen, 16384, - OT_PSKC_MAX_SIZE, aPskc.m8); + otPlatCryptoPbkdf2GenerateKey(reinterpret_cast(aPassPhrase), passphraseLen, salt, saltLen, 16384, + OT_PSKC_MAX_SIZE, aPskc.m8); exit: return error; diff --git a/src/core/meshcop/meshcop.hpp b/src/core/meshcop/meshcop.hpp index 0de1047029d..a1485dbca53 100644 --- a/src/core/meshcop/meshcop.hpp +++ b/src/core/meshcop/meshcop.hpp @@ -420,10 +420,10 @@ class SteeringData : public otSteeringData * @retval kErrorInvalidArgs If the length of passphrase is out of range. * */ -Error GeneratePskc(const char * aPassPhrase, - const NetworkName & aNetworkName, +Error GeneratePskc(const char *aPassPhrase, + const NetworkName &aNetworkName, const ExtendedPanId &aExtPanId, - Pskc & aPskc); + Pskc &aPskc); /** * This function computes the Joiner ID from a factory-assigned IEEE EUI-64. @@ -459,9 +459,7 @@ Error GetBorderAgentRloc(ThreadNetif &aNetIf, uint16_t &aRloc); */ void LogError(const char *aActionText, Error aError); #else -inline void LogError(const char *, Error) -{ -} +inline void LogError(const char *, Error) {} #endif } // namespace MeshCoP diff --git a/src/core/meshcop/meshcop_leader.cpp b/src/core/meshcop/meshcop_leader.cpp index a0720262456..5f97185d4fa 100644 --- a/src/core/meshcop/meshcop_leader.cpp +++ b/src/core/meshcop/meshcop_leader.cpp @@ -57,22 +57,13 @@ RegisterLogModule("MeshCoPLeader"); Leader::Leader(Instance &aInstance) : InstanceLocator(aInstance) - , mPetition(UriPath::kLeaderPetition, Leader::HandlePetition, this) - , mKeepAlive(UriPath::kLeaderKeepAlive, Leader::HandleKeepAlive, this) - , mTimer(aInstance, HandleTimer) + , mTimer(aInstance) , mDelayTimerMinimal(DelayTimerTlv::kDelayTimerMinimal) , mSessionId(Random::NonCrypto::GetUint16()) { - Get().AddResource(mPetition); - Get().AddResource(mKeepAlive); } -void Leader::HandlePetition(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandlePetition(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void Leader::HandlePetition(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> void Leader::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { OT_UNUSED_VARIABLE(aMessageInfo); @@ -80,7 +71,7 @@ void Leader::HandlePetition(Coap::Message &aMessage, const Ip6::MessageInfo &aMe CommissionerIdTlv commissionerId; StateTlv::State state = StateTlv::kReject; - LogInfo("received petition"); + LogInfo("Received %s", UriToString()); VerifyOrExit(Get().IsRoutingLocator(aMessageInfo.GetPeerAddr())); SuccessOrExit(Tlv::FindTlv(aMessage, commissionerId)); @@ -121,7 +112,7 @@ void Leader::HandlePetition(Coap::Message &aMessage, const Ip6::MessageInfo &aMe SendPetitionResponse(aMessage, aMessageInfo, state); } -void Leader::SendPetitionResponse(const Coap::Message & aRequest, +void Leader::SendPetitionResponse(const Coap::Message &aRequest, const Ip6::MessageInfo &aMessageInfo, StateTlv::State aState) { @@ -145,26 +136,21 @@ void Leader::SendPetitionResponse(const Coap::Message & aRequest, SuccessOrExit(error = Get().SendMessage(*message, aMessageInfo)); - LogInfo("sent petition response"); + LogInfo("Sent %s response", UriToString()); exit: FreeMessageOnError(message, error); LogError("send petition response", error); } -void Leader::HandleKeepAlive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleKeepAlive(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void Leader::HandleKeepAlive(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> void Leader::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { uint8_t state; uint16_t sessionId; BorderAgentLocatorTlv *borderAgentLocator; StateTlv::State responseState; - LogInfo("received keep alive"); + LogInfo("Received %s", UriToString()); SuccessOrExit(Tlv::Find(aMessage, state)); @@ -202,7 +188,7 @@ void Leader::HandleKeepAlive(Coap::Message &aMessage, const Ip6::MessageInfo &aM return; } -void Leader::SendKeepAliveResponse(const Coap::Message & aRequest, +void Leader::SendKeepAliveResponse(const Coap::Message &aRequest, const Ip6::MessageInfo &aMessageInfo, StateTlv::State aState) { @@ -216,7 +202,7 @@ void Leader::SendKeepAliveResponse(const Coap::Message & aRequest, SuccessOrExit(error = Get().SendMessage(*message, aMessageInfo)); - LogInfo("sent keep alive response"); + LogInfo("Sent %s response", UriToString()); exit: FreeMessageOnError(message, error); @@ -227,15 +213,15 @@ void Leader::SendDatasetChanged(const Ip6::Address &aAddress) { Error error = kErrorNone; Tmf::MessageInfo messageInfo(GetInstance()); - Coap::Message * message; + Coap::Message *message; - message = Get().NewPriorityConfirmablePostMessage(UriPath::kDatasetChanged); + message = Get().NewPriorityConfirmablePostMessage(kUriDatasetChanged); VerifyOrExit(message != nullptr, error = kErrorNoBufs); messageInfo.SetSockAddrToRlocPeerAddrTo(aAddress); SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("sent dataset changed"); + LogInfo("Sent %s", UriToString()); exit: FreeMessageOnError(message, error); @@ -253,15 +239,7 @@ Error Leader::SetDelayTimerMinimal(uint32_t aDelayTimerMinimal) return error; } -uint32_t Leader::GetDelayTimerMinimal(void) const -{ - return mDelayTimerMinimal; -} - -void Leader::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} +uint32_t Leader::GetDelayTimerMinimal(void) const { return mDelayTimerMinimal; } void Leader::HandleTimer(void) { diff --git a/src/core/meshcop/meshcop_leader.hpp b/src/core/meshcop/meshcop_leader.hpp index 75f0187d750..fa2a288d78a 100644 --- a/src/core/meshcop/meshcop_leader.hpp +++ b/src/core/meshcop/meshcop_leader.hpp @@ -38,13 +38,13 @@ #if OPENTHREAD_FTD -#include "coap/coap.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" #include "common/timer.hpp" #include "meshcop/meshcop_tlvs.hpp" #include "net/udp6.hpp" #include "thread/mle.hpp" +#include "thread/tmf.hpp" namespace ot { namespace MeshCoP { @@ -66,6 +66,8 @@ class CommissioningData class Leader : public InstanceLocator, private NonCopyable { + friend class Tmf::Agent; + public: /** * This constructor initializes the Leader object. @@ -111,28 +113,25 @@ class Leader : public InstanceLocator, private NonCopyable private: static constexpr uint32_t kTimeoutLeaderPetition = 50; // TIMEOUT_LEAD_PET (seconds) - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); + void HandleTimer(void); + + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandlePetition(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandlePetition(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - void SendPetitionResponse(const Coap::Message & aRequest, - const Ip6::MessageInfo &aMessageInfo, - StateTlv::State aState); + void SendPetitionResponse(const Coap::Message &aRequest, + const Ip6::MessageInfo &aMessageInfo, + StateTlv::State aState); - static void HandleKeepAlive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleKeepAlive(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - void SendKeepAliveResponse(const Coap::Message & aRequest, - const Ip6::MessageInfo &aMessageInfo, - StateTlv::State aState); + void SendKeepAliveResponse(const Coap::Message &aRequest, + const Ip6::MessageInfo &aMessageInfo, + StateTlv::State aState); static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); void ResignCommissioner(void); - Coap::Resource mPetition; - Coap::Resource mKeepAlive; - TimerMilli mTimer; + using LeaderTimer = TimerMilliIn; + + LeaderTimer mTimer; uint32_t mDelayTimerMinimal; @@ -140,6 +139,9 @@ class Leader : public InstanceLocator, private NonCopyable uint16_t mSessionId; }; +DeclareTmfHandler(Leader, kUriLeaderPetition); +DeclareTmfHandler(Leader, kUriLeaderKeepAlive); + } // namespace MeshCoP } // namespace ot diff --git a/src/core/meshcop/meshcop_tlvs.cpp b/src/core/meshcop/meshcop_tlvs.cpp index c047ed9f44b..8a0e3ee0f2f 100644 --- a/src/core/meshcop/meshcop_tlvs.cpp +++ b/src/core/meshcop/meshcop_tlvs.cpp @@ -35,6 +35,7 @@ #include "common/const_cast.hpp" #include "common/debug.hpp" +#include "common/num_utils.hpp" #include "common/string.hpp" #include "meshcop/meshcop.hpp" @@ -135,10 +136,7 @@ void NetworkNameTlv::SetNetworkName(const NameData &aNameData) SetLength(len); } -bool NetworkNameTlv::IsValid(void) const -{ - return GetLength() >= 1 && IsValidUtf8String(mNetworkName, GetLength()); -} +bool NetworkNameTlv::IsValid(void) const { return IsValidUtf8String(mNetworkName, GetLength()); } void SteeringDataTlv::CopyTo(SteeringData &aSteeringData) const { @@ -154,7 +152,7 @@ bool SecurityPolicyTlv::IsValid(void) const SecurityPolicy SecurityPolicyTlv::GetSecurityPolicy(void) const { SecurityPolicy securityPolicy; - uint8_t length = OT_MIN(static_cast(sizeof(mFlags)), GetFlagsLength()); + uint8_t length = Min(static_cast(sizeof(mFlags)), GetFlagsLength()); securityPolicy.mRotationTime = GetRotationTime(); securityPolicy.SetFlags(mFlags, length); @@ -260,10 +258,7 @@ const ChannelMaskEntryBase *ChannelMaskBaseTlv::GetFirstEntry(void) const return entry; } -ChannelMaskEntryBase *ChannelMaskBaseTlv::GetFirstEntry(void) -{ - return AsNonConst(AsConst(this)->GetFirstEntry()); -} +ChannelMaskEntryBase *ChannelMaskBaseTlv::GetFirstEntry(void) { return AsNonConst(AsConst(this)->GetFirstEntry()); } void ChannelMaskTlv::SetChannelMask(uint32_t aChannelMask) { diff --git a/src/core/meshcop/meshcop_tlvs.hpp b/src/core/meshcop/meshcop_tlvs.hpp index c21e799841d..1cd2755059c 100644 --- a/src/core/meshcop/meshcop_tlvs.hpp +++ b/src/core/meshcop/meshcop_tlvs.hpp @@ -44,6 +44,7 @@ #include "common/const_cast.hpp" #include "common/encoding.hpp" #include "common/message.hpp" +#include "common/num_utils.hpp" #include "common/string.hpp" #include "common/tlvs.hpp" #include "mac/mac_types.hpp" @@ -119,6 +120,17 @@ class Tlv : public ot::Tlv kJoinerAdvertisement = OT_MESHCOP_TLV_JOINERADVERTISEMENT, ///< Joiner Advertisement TLV }; + /** + * Max length of Provisioning URL TLV. + * + */ + static constexpr uint8_t kMaxProvisioningUrlLength = OT_PROVISIONING_URL_MAX_SIZE; + + static constexpr uint8_t kMaxVendorNameLength = 32; ///< Max length of Vendor Name TLV. + static constexpr uint8_t kMaxVendorModelLength = 32; ///< Max length of Vendor Model TLV. + static constexpr uint8_t kMaxVendorSwVersionLength = 16; ///< Max length of Vendor SW Version TLV. + static constexpr uint8_t kMaxVendorDataLength = 64; ///< Max length of Vendor Data TLV. + /** * This method returns the Type value. * @@ -1060,28 +1072,10 @@ class ActiveTimestampTlv : public Tlv, public SimpleTlvInfo +class StateTlv : public UintTlvInfo { public: - /** - * This method initializes the TLV. - * - */ - void Init(void) - { - SetType(kState); - SetLength(sizeof(*this) - sizeof(Tlv)); - } - - /** - * This method indicates whether or not the TLV appears to be well-formed. - * - * @retval TRUE If the TLV appears to be well-formed. - * @retval FALSE If the TLV does not appear to be well-formed. - * - */ - bool IsValid(void) const { return GetLength() >= sizeof(*this) - sizeof(Tlv); } + StateTlv(void) = delete; /** * State values. @@ -1093,26 +1087,7 @@ class StateTlv : public Tlv, public UintTlvInfo kPending = 0, ///< Pending kAccept = 1, ///< Accept }; - - /** - * This method returns the State value. - * - * @returns The State value. - * - */ - State GetState(void) const { return static_cast(mState); } - - /** - * This method sets the State value. - * - * @param[in] aState The State value. - * - */ - void SetState(State aState) { mState = static_cast(aState); } - -private: - uint8_t mState; -} OT_TOOL_PACKED_END; +}; /** * This class implements Joiner UDP Port TLV generation and parsing. @@ -1557,328 +1532,58 @@ class EnergyListTlv : public Tlv, public TlvInfo * */ bool IsValid(void) const { return true; } -} OT_TOOL_PACKED_END; - -/** - * This class implements Provisioning URL TLV generation and parsing. - * - */ -OT_TOOL_PACKED_BEGIN -class ProvisioningUrlTlv : public Tlv, public TlvInfo -{ -public: - /** - * Maximum number of chars in the Provisioning URL string. - * - */ - static constexpr uint16_t kMaxLength = OT_PROVISIONING_URL_MAX_SIZE; /** - * This method initializes the TLV. - * - */ - void Init(void) - { - SetType(kProvisioningUrl); - SetLength(0); - } - - /* - * This method returns the Provisioning URL length. + * This method returns a pointer to the start of energy measurement list. * - * @returns The Provisioning URL length. + * @returns A pointer to the start start of energy energy measurement list. * */ - uint8_t GetProvisioningUrlLength(void) const - { - return GetLength() <= sizeof(mProvisioningUrl) ? GetLength() : sizeof(mProvisioningUrl); - } + const uint8_t *GetEnergyList(void) const { return mEnergyList; } /** - * This method indicates whether or not the TLV appears to be well-formed. + * This method returns the length of energy measurement list. * - * @retval TRUE If the TLV appears to be well-formed. - * @retval FALSE If the TLV does not appear to be well-formed. + * @returns The length of energy measurement list. * */ - bool IsValid(void) const - { - return GetType() == kProvisioningUrl && mProvisioningUrl[GetProvisioningUrlLength()] == '\0'; - } - - /** - * This method returns the Provisioning URL value. - * - * @returns The Provisioning URL value. - * - */ - const char *GetProvisioningUrl(void) const { return mProvisioningUrl; } - - /** - * This method sets the Provisioning URL value. - * - * @param[in] aProvisioningUrl A pointer to the Provisioning URL value. - * - */ - void SetProvisioningUrl(const char *aProvisioningUrl) - { - uint16_t len = aProvisioningUrl ? StringLength(aProvisioningUrl, kMaxLength) : 0; - - SetLength(static_cast(len)); - - if (len > 0) - { - memcpy(mProvisioningUrl, aProvisioningUrl, len); - } - } + uint8_t GetEnergyListLength(void) const { return Min(kMaxListLength, GetLength()); } private: - char mProvisioningUrl[kMaxLength]; + static constexpr uint8_t kMaxListLength = OPENTHREAD_CONFIG_TMF_ENERGY_SCAN_MAX_RESULTS; + + uint8_t mEnergyList[kMaxListLength]; } OT_TOOL_PACKED_END; /** - * This class implements Vendor Name TLV generation and parsing. + * This class defines Provisioning TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class VendorNameTlv : public Tlv, public TlvInfo -{ -public: - /** - * This method initializes the TLV. - * - */ - void Init(void) - { - SetType(kVendorName); - SetLength(0); - } - - /** - * This method returns the Vendor Name length. - * - * @returns The Vendor Name length. - * - */ - uint8_t GetVendorNameLength(void) const - { - return GetLength() <= sizeof(mVendorName) ? GetLength() : sizeof(mVendorName); - } - - /** - * This method returns the Vendor Name value. - * - * @returns The Vendor Name value. - * - */ - const char *GetVendorName(void) const { return mVendorName; } - - /** - * This method sets the Vendor Name value. - * - * @param[in] aVendorName A pointer to the Vendor Name value. - * - */ - void SetVendorName(const char *aVendorName) - { - uint16_t len = (aVendorName == nullptr) ? 0 : StringLength(aVendorName, sizeof(mVendorName)); - - SetLength(static_cast(len)); - - if (len > 0) - { - memcpy(mVendorName, aVendorName, len); - } - } - -private: - static constexpr uint8_t kMaxLength = 32; - - char mVendorName[kMaxLength]; -} OT_TOOL_PACKED_END; +typedef StringTlvInfo ProvisioningUrlTlv; /** - * This class implements Vendor Model TLV generation and parsing. + * This class defines Vendor Name TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class VendorModelTlv : public Tlv, public TlvInfo -{ -public: - /** - * This method initializes the TLV. - * - */ - void Init(void) - { - SetType(kVendorModel); - SetLength(0); - } - - /** - * This method returns the Vendor Model length. - * - * @returns The Vendor Model length. - * - */ - uint8_t GetVendorModelLength(void) const - { - return GetLength() <= sizeof(mVendorModel) ? GetLength() : sizeof(mVendorModel); - } - - /** - * This method returns the Vendor Model value. - * - * @returns The Vendor Model value. - * - */ - const char *GetVendorModel(void) const { return mVendorModel; } - - /** - * This method sets the Vendor Model value. - * - * @param[in] aVendorModel A pointer to the Vendor Model value. - * - */ - void SetVendorModel(const char *aVendorModel) - { - uint16_t len = (aVendorModel == nullptr) ? 0 : StringLength(aVendorModel, sizeof(mVendorModel)); - - SetLength(static_cast(len)); - - if (len > 0) - { - memcpy(mVendorModel, aVendorModel, len); - } - } - -private: - static constexpr uint8_t kMaxLength = 32; - - char mVendorModel[kMaxLength]; -} OT_TOOL_PACKED_END; +typedef StringTlvInfo VendorNameTlv; /** - * This class implements Vendor SW Version TLV generation and parsing. + * This class defines Vendor Model TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class VendorSwVersionTlv : public Tlv, public TlvInfo -{ -public: - /** - * This method initializes the TLV. - * - */ - void Init(void) - { - SetType(kVendorSwVersion); - SetLength(0); - } - - /** - * This method returns the Vendor SW Version length. - * - * @returns The Vendor SW Version length. - * - */ - uint8_t GetVendorSwVersionLength(void) const - { - return GetLength() <= sizeof(mVendorSwVersion) ? GetLength() : sizeof(mVendorSwVersion); - } - - /** - * This method returns the Vendor SW Version value. - * - * @returns The Vendor SW Version value. - * - */ - const char *GetVendorSwVersion(void) const { return mVendorSwVersion; } - - /** - * This method sets the Vendor SW Version value. - * - * @param[in] aVendorSwVersion A pointer to the Vendor SW Version value. - * - */ - void SetVendorSwVersion(const char *aVendorSwVersion) - { - uint16_t len = (aVendorSwVersion == nullptr) ? 0 : StringLength(aVendorSwVersion, sizeof(mVendorSwVersion)); - - SetLength(static_cast(len)); - - if (len > 0) - { - memcpy(mVendorSwVersion, aVendorSwVersion, len); - } - } - -private: - static constexpr uint8_t kMaxLength = 16; - - char mVendorSwVersion[kMaxLength]; -} OT_TOOL_PACKED_END; +typedef StringTlvInfo VendorModelTlv; /** - * This class implements Vendor Data TLV generation and parsing. + * This class defines Vendor SW Version TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class VendorDataTlv : public Tlv, public TlvInfo -{ -public: - /** - * This method initializes the TLV. - * - */ - void Init(void) - { - SetType(kVendorData); - SetLength(0); - } - - /** - * This method returns the Vendor Data length. - * - * @returns The Vendor Data length. - * - */ - uint8_t GetVendorDataLength(void) const - { - return GetLength() <= sizeof(mVendorData) ? GetLength() : sizeof(mVendorData); - } - - /** - * This method returns the Vendor Data value. - * - * @returns The Vendor Data value. - * - */ - const char *GetVendorData(void) const { return mVendorData; } - - /** - * This method sets the Vendor Data value. - * - * @param[in] aVendorData A pointer to the Vendor Data value. - * - */ - void SetVendorData(const char *aVendorData) - { - uint16_t len = (aVendorData == nullptr) ? 0 : StringLength(aVendorData, sizeof(mVendorData)); - - SetLength(static_cast(len)); +typedef StringTlvInfo VendorSwVersionTlv; - if (len > 0) - { - memcpy(mVendorData, aVendorData, len); - } - } - -private: - static constexpr uint8_t kMaxLength = 64; - - char mVendorData[kMaxLength]; -} OT_TOOL_PACKED_END; +/** + * This class defines Vendor Data TLV constants and types. + * + */ +typedef StringTlvInfo VendorDataTlv; /** * This class implements Vendor Stack Version TLV generation and parsing. @@ -2029,32 +1734,19 @@ class VendorStackVersionTlv : public Tlv, public TlvInfo UdpEncapsulationTlv; + +/** + * This class represents UDP Encapsulation TLV value header (source and destination ports). * */ OT_TOOL_PACKED_BEGIN -class UdpEncapsulationTlv : public ExtendedTlv, public TlvInfo +class UdpEncapsulationTlvHeader { public: - /** - * This method initializes the TLV. - * - */ - void Init(void) - { - SetType(MeshCoP::Tlv::kUdpEncapsulation); - SetLength(sizeof(*this) - sizeof(ExtendedTlv)); - } - - /** - * This method indicates whether or not the TLV appears to be well-formed. - * - * @retval TRUE If the TLV appears to be well-formed. - * @retval FALSE If the TLV does not appear to be well-formed. - * - */ - bool IsValid(void) const { return GetLength() >= sizeof(*this) - sizeof(ExtendedTlv); } - /** * This method returns the source port. * @@ -2087,25 +1779,10 @@ class UdpEncapsulationTlv : public ExtendedTlv, public TlvInfo= OT_THREAD_VERSION_1_2) -const char NetworkNameManager::sDomainNameInit[] = "DefaultDomain"; -#endif - uint8_t NameData::CopyTo(char *aBuffer, uint8_t aMaxSize) const { MutableData destData; @@ -71,6 +65,9 @@ Error NetworkName::Set(const char *aNameString) // chars. The `+ 1` ensures that a `aNameString` with length // longer than `kMaxSize` is correctly rejected (returning error // `kErrorInvalidArgs`). + // Additionally, no minimum length is verified in order to ensure + // backwards compatibility with previous versions that allowed + // a zero-length name. Error error; NameData data(aNameString, kMaxSize + 1); @@ -89,7 +86,7 @@ Error NetworkName::Set(const NameData &aNameData) NameData data = aNameData; uint8_t newLen = static_cast(StringLength(data.GetBuffer(), data.GetLength())); - VerifyOrExit((0 < newLen) && (newLen <= kMaxSize), error = kErrorInvalidArgs); + VerifyOrExit(newLen <= kMaxSize, error = kErrorInvalidArgs); data.SetLength(newLen); @@ -106,18 +103,15 @@ Error NetworkName::Set(const NameData &aNameData) return error; } -bool NetworkName::operator==(const NetworkName &aOther) const -{ - return GetAsData() == aOther.GetAsData(); -} +bool NetworkName::operator==(const NetworkName &aOther) const { return GetAsData() == aOther.GetAsData(); } NetworkNameManager::NetworkNameManager(Instance &aInstance) : InstanceLocator(aInstance) { - IgnoreError(SetNetworkName(sNetworkNameInit)); + IgnoreError(SetNetworkName(NetworkName::kNetworkNameInit)); #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) - IgnoreError(SetDomainName(sDomainNameInit)); + IgnoreError(SetDomainName(NetworkName::kDomainNameInit)); #endif } diff --git a/src/core/meshcop/network_name.hpp b/src/core/meshcop/network_name.hpp index 1f6d84786d3..d376fc5cd71 100644 --- a/src/core/meshcop/network_name.hpp +++ b/src/core/meshcop/network_name.hpp @@ -107,6 +107,9 @@ class NameData : private Data class NetworkName : public otNetworkName, public Unequatable { public: + static constexpr const char *kNetworkNameInit = "OpenThread"; + static constexpr const char *kDomainNameInit = "DefaultDomain"; + /** * This constant specified the maximum number of chars in Network Name (excludes null char). * @@ -262,9 +265,6 @@ class NetworkNameManager : public InstanceLocator, private NonCopyable private: Error SignalNetworkNameChange(Error aError); - static const char sNetworkNameInit[]; - static const char sDomainNameInit[]; - NetworkName mNetworkName; #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) diff --git a/src/core/meshcop/panid_query_client.cpp b/src/core/meshcop/panid_query_client.cpp index 7bacc80c087..8c6a004ef44 100644 --- a/src/core/meshcop/panid_query_client.cpp +++ b/src/core/meshcop/panid_query_client.cpp @@ -53,28 +53,24 @@ RegisterLogModule("PanIdQueryClnt"); PanIdQueryClient::PanIdQueryClient(Instance &aInstance) : InstanceLocator(aInstance) - , mCallback(nullptr) - , mContext(nullptr) - , mPanIdQuery(UriPath::kPanIdConflict, &PanIdQueryClient::HandleConflict, this) { - Get().AddResource(mPanIdQuery); } Error PanIdQueryClient::SendQuery(uint16_t aPanId, uint32_t aChannelMask, - const Ip6::Address & aAddress, + const Ip6::Address &aAddress, otCommissionerPanIdConflictCallback aCallback, - void * aContext) + void *aContext) { Error error = kErrorNone; MeshCoP::ChannelMaskTlv channelMask; Tmf::MessageInfo messageInfo(GetInstance()); - Coap::Message * message = nullptr; + Coap::Message *message = nullptr; VerifyOrExit(Get().IsActive(), error = kErrorInvalidState); VerifyOrExit((message = Get().NewPriorityMessage()) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = message->InitAsPost(aAddress, UriPath::kPanIdQuery)); + SuccessOrExit(error = message->InitAsPost(aAddress, kUriPanIdQuery)); SuccessOrExit(error = message->SetPayloadMarker()); SuccessOrExit( @@ -89,43 +85,34 @@ Error PanIdQueryClient::SendQuery(uint16_t aPanId, messageInfo.SetSockAddrToRlocPeerAddrTo(aAddress); SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("sent panid query"); + LogInfo("Sent %s", UriToString()); - mCallback = aCallback; - mContext = aContext; + mCallback.Set(aCallback, aContext); exit: FreeMessageOnError(message, error); return error; } -void PanIdQueryClient::HandleConflict(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +template <> +void PanIdQueryClient::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - static_cast(aContext)->HandleConflict(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void PanIdQueryClient::HandleConflict(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) -{ - uint16_t panId; - Ip6::MessageInfo responseInfo(aMessageInfo); - uint32_t mask; + uint16_t panId; + uint32_t mask; VerifyOrExit(aMessage.IsConfirmablePostRequest()); - LogInfo("received panid conflict"); + LogInfo("Received %s", UriToString()); SuccessOrExit(Tlv::Find(aMessage, panId)); VerifyOrExit((mask = MeshCoP::ChannelMaskTlv::GetChannelMask(aMessage)) != 0); - if (mCallback != nullptr) - { - mCallback(panId, mask, mContext); - } + mCallback.InvokeIfSet(panId, mask); - SuccessOrExit(Get().SendEmptyAck(aMessage, responseInfo)); + SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); - LogInfo("sent panid query conflict response"); + LogInfo("Sent %s response", UriToString()); exit: return; diff --git a/src/core/meshcop/panid_query_client.hpp b/src/core/meshcop/panid_query_client.hpp index 35071508f86..46b1ff78415 100644 --- a/src/core/meshcop/panid_query_client.hpp +++ b/src/core/meshcop/panid_query_client.hpp @@ -40,10 +40,11 @@ #include -#include "coap/coap.hpp" +#include "common/callback.hpp" #include "common/locator.hpp" #include "net/ip6_address.hpp" #include "net/udp6.hpp" +#include "thread/tmf.hpp" namespace ot { @@ -53,6 +54,8 @@ namespace ot { */ class PanIdQueryClient : public InstanceLocator { + friend class Tmf::Agent; + public: /** * This constructor initializes the object. @@ -75,20 +78,18 @@ class PanIdQueryClient : public InstanceLocator */ Error SendQuery(uint16_t aPanId, uint32_t aChannelMask, - const Ip6::Address & aAddress, + const Ip6::Address &aAddress, otCommissionerPanIdConflictCallback aCallback, - void * aContext); + void *aContext); private: - static void HandleConflict(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleConflict(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - - otCommissionerPanIdConflictCallback mCallback; - void * mContext; + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - Coap::Resource mPanIdQuery; + Callback mCallback; }; +DeclareTmfHandler(PanIdQueryClient, kUriPanIdConflict); + /** * @} */ diff --git a/src/core/meshcop/timestamp.cpp b/src/core/meshcop/timestamp.cpp index 08ed36b94c1..c0f356eb2c8 100644 --- a/src/core/meshcop/timestamp.cpp +++ b/src/core/meshcop/timestamp.cpp @@ -34,6 +34,7 @@ #include "timestamp.hpp" #include "common/code_utils.hpp" +#include "common/num_utils.hpp" namespace ot { namespace MeshCoP { @@ -80,39 +81,15 @@ int Timestamp::Compare(const Timestamp *aFirst, const Timestamp *aSecond) 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(); - - if (firstTicks != secondTicks) - { - ExitNow(rval = (firstTicks > secondTicks) ? 1 : -1); - } + int rval; - firstAuthoritative = aFirst.GetAuthoritative(); - secondAuthoritative = aSecond.GetAuthoritative(); + rval = ThreeWayCompare(aFirst.GetSeconds(), aSecond.GetSeconds()); + VerifyOrExit(rval == 0); - if (firstAuthoritative != secondAuthoritative) - { - ExitNow(rval = firstAuthoritative ? 1 : -1); - } + rval = ThreeWayCompare(aFirst.GetTicks(), aSecond.GetTicks()); + VerifyOrExit(rval == 0); - rval = 0; + rval = ThreeWayCompare(aFirst.GetAuthoritative(), aSecond.GetAuthoritative()); exit: return rval; diff --git a/src/core/mtd.cmake b/src/core/mtd.cmake index 7d6f0ef8b80..e9b831c24d2 100644 --- a/src/core/mtd.cmake +++ b/src/core/mtd.cmake @@ -43,9 +43,8 @@ target_sources(openthread-mtd PRIVATE ${COMMON_SOURCES}) target_link_libraries(openthread-mtd PRIVATE ${OT_MBEDTLS} + ot-config-mtd ot-config ) -if(NOT OT_EXCLUDE_TCPLP_LIB) - target_link_libraries(openthread-mtd PRIVATE tcplp) -endif() +target_link_libraries(openthread-mtd PRIVATE tcplp-mtd) diff --git a/src/core/net/checksum.cpp b/src/core/net/checksum.cpp index 16860cbdfbc..df761d42921 100644 --- a/src/core/net/checksum.cpp +++ b/src/core/net/checksum.cpp @@ -93,7 +93,7 @@ void Checksum::WriteToMessage(uint16_t aOffset, Message &aMessage) const void Checksum::Calculate(const Ip6::Address &aSource, const Ip6::Address &aDestination, uint8_t aIpProto, - const Message & aMessage) + const Message &aMessage) { Message::Chunk chunk; uint16_t length = aMessage.GetLength() - aMessage.GetOffset(); @@ -119,13 +119,13 @@ void Checksum::Calculate(const Ip6::Address &aSource, void Checksum::Calculate(const Ip4::Address &aSource, const Ip4::Address &aDestination, uint8_t aIpProto, - const Message & aMessage) + const Message &aMessage) { Message::Chunk chunk; uint16_t length = aMessage.GetLength() - aMessage.GetOffset(); // Pseudo-header for checksum calculation (RFC-768/792/793). - // Note: ICMP checksum won't count the presudo header like TCP and UDP. + // Note: ICMP checksum won't count the pseudo header like TCP and UDP. if (aIpProto != Ip4::kProtoIcmp) { AddData(aSource.GetBytes(), sizeof(Ip4::Address)); @@ -154,7 +154,7 @@ Error Checksum::VerifyMessageChecksum(const Message &aMessage, const Ip6::Messag return (checksum.GetValue() == kValidRxChecksum) ? kErrorNone : kErrorDrop; } -void Checksum::UpdateMessageChecksum(Message & aMessage, +void Checksum::UpdateMessageChecksum(Message &aMessage, const Ip6::Address &aSource, const Ip6::Address &aDestination, uint8_t aIpProto) @@ -189,7 +189,7 @@ void Checksum::UpdateMessageChecksum(Message & aMessage, return; } -void Checksum::UpdateMessageChecksum(Message & aMessage, +void Checksum::UpdateMessageChecksum(Message &aMessage, const Ip4::Address &aSource, const Ip4::Address &aDestination, uint8_t aIpProto) diff --git a/src/core/net/checksum.hpp b/src/core/net/checksum.hpp index f335785ce7b..06d6e69014d 100644 --- a/src/core/net/checksum.hpp +++ b/src/core/net/checksum.hpp @@ -80,7 +80,7 @@ class Checksum * @param[in] aIpProto The Internet Protocol value. * */ - static void UpdateMessageChecksum(Message & aMessage, + static void UpdateMessageChecksum(Message &aMessage, const Ip6::Address &aSource, const Ip6::Address &aDestination, uint8_t aIpProto); @@ -96,7 +96,7 @@ class Checksum * @param[in] aIpProto The Internet Protocol value. * */ - static void UpdateMessageChecksum(Message & aMessage, + static void UpdateMessageChecksum(Message &aMessage, const Ip4::Address &aSource, const Ip4::Address &aDestination, uint8_t aIpProto); @@ -124,11 +124,11 @@ class Checksum void Calculate(const Ip6::Address &aSource, const Ip6::Address &aDestination, uint8_t aIpProto, - const Message & aMessage); + const Message &aMessage); void Calculate(const Ip4::Address &aSource, const Ip4::Address &aDestination, uint8_t aIpProto, - const Message & aMessage); + const Message &aMessage); static constexpr uint16_t kValidRxChecksum = 0xffff; diff --git a/src/core/net/dhcp6.hpp b/src/core/net/dhcp6.hpp index 6a1f3dac5c1..5d65f2163f0 100644 --- a/src/core/net/dhcp6.hpp +++ b/src/core/net/dhcp6.hpp @@ -480,7 +480,7 @@ class IaAddress : public Option { public: static constexpr uint32_t kDefaultPreferredLifetime = 0xffffffffU; ///< Default preferred lifetime. - static constexpr uint32_t kDefaultValidLiftetime = 0xffffffffU; ///< Default valid lifetime. + static constexpr uint32_t kDefaultValidLifetime = 0xffffffffU; ///< Default valid lifetime. /** * This method initializes the DHCPv6 Option. diff --git a/src/core/net/dhcp6_client.cpp b/src/core/net/dhcp6_client.cpp index 9c3e77278a7..db671357aea 100644 --- a/src/core/net/dhcp6_client.cpp +++ b/src/core/net/dhcp6_client.cpp @@ -218,10 +218,7 @@ bool Client::ProcessNextIdentityAssociation(void) return rval; } -void Client::HandleTrickleTimer(TrickleTimer &aTrickleTimer) -{ - aTrickleTimer.Get().HandleTrickleTimer(); -} +void Client::HandleTrickleTimer(TrickleTimer &aTrickleTimer) { aTrickleTimer.Get().HandleTrickleTimer(); } void Client::HandleTrickleTimer(void) { @@ -263,10 +260,10 @@ void Client::HandleTrickleTimer(void) void Client::Solicit(uint16_t aRloc16) { Error error = kErrorNone; - Message * message; + Message *message; Ip6::MessageInfo messageInfo; - VerifyOrExit((message = mSocket.NewMessage(0)) != nullptr, error = kErrorNoBufs); + VerifyOrExit((message = mSocket.NewMessage()) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = AppendHeader(*message)); SuccessOrExit(error = AppendElapsedTime(*message)); diff --git a/src/core/net/dhcp6_client.hpp b/src/core/net/dhcp6_client.hpp index 7004cef96f3..533ded73f2b 100644 --- a/src/core/net/dhcp6_client.hpp +++ b/src/core/net/dhcp6_client.hpp @@ -110,7 +110,7 @@ class Client : public InstanceLocator, private NonCopyable void Stop(void); static bool MatchNetifAddressWithPrefix(const Ip6::Netif::UnicastAddress &aNetifAddress, - const Ip6::Prefix & aIp6Prefix); + const Ip6::Prefix &aIp6Prefix); void Solicit(uint16_t aRloc16); diff --git a/src/core/net/dhcp6_server.cpp b/src/core/net/dhcp6_server.cpp index eaab1b4dd92..826d5fefcdd 100644 --- a/src/core/net/dhcp6_server.cpp +++ b/src/core/net/dhcp6_server.cpp @@ -145,10 +145,7 @@ void Server::Start(void) return; } -void Server::Stop(void) -{ - IgnoreError(mSocket.Close()); -} +void Server::Stop(void) { IgnoreError(mSocket.Close()); } void Server::AddPrefixAgent(const Ip6::Prefix &aIp6Prefix, const Lowpan::Context &aContext) { @@ -333,16 +330,16 @@ Error Server::ProcessIaAddress(Message &aMessage, uint16_t aOffset) return error; } -Error Server::SendReply(const Ip6::Address & aDst, +Error Server::SendReply(const Ip6::Address &aDst, const TransactionId &aTransactionId, - ClientIdentifier & aClientId, - IaNa & aIaNa) + ClientIdentifier &aClientId, + IaNa &aIaNa) { Error error = kErrorNone; Ip6::MessageInfo messageInfo; - Message * message; + Message *message; - VerifyOrExit((message = mSocket.NewMessage(0)) != nullptr, error = kErrorNoBufs); + VerifyOrExit((message = mSocket.NewMessage()) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = AppendHeader(*message, aTransactionId)); SuccessOrExit(error = AppendServerIdentifier(*message)); SuccessOrExit(error = AppendClientIdentifier(*message, aClientId)); @@ -473,7 +470,7 @@ Error Server::AddIaAddress(Message &aMessage, const Ip6::Address &aPrefix, Clien option.GetAddress().SetPrefix(aPrefix.mFields.m8, OT_IP6_PREFIX_BITSIZE); option.GetAddress().GetIid().SetFromExtAddress(aClientId.GetDuidLinkLayerAddress()); option.SetPreferredLifetime(IaAddress::kDefaultPreferredLifetime); - option.SetValidLifetime(IaAddress::kDefaultValidLiftetime); + option.SetValidLifetime(IaAddress::kDefaultValidLifetime); SuccessOrExit(error = aMessage.Append(option)); exit: diff --git a/src/core/net/dhcp6_server.hpp b/src/core/net/dhcp6_server.hpp index 8fa7db30a77..fa14e8bd991 100644 --- a/src/core/net/dhcp6_server.hpp +++ b/src/core/net/dhcp6_server.hpp @@ -209,10 +209,10 @@ class Server : public InstanceLocator, private NonCopyable Error ProcessIaAddress(Message &aMessage, uint16_t aOffset); Error ProcessElapsedTime(Message &aMessage, uint16_t aOffset); - Error SendReply(const Ip6::Address & aDst, + Error SendReply(const Ip6::Address &aDst, const TransactionId &aTransactionId, - ClientIdentifier & aClientId, - IaNa & aIaNa); + ClientIdentifier &aClientId, + IaNa &aIaNa); Ip6::Udp::Socket mSocket; diff --git a/src/core/net/dns_client.cpp b/src/core/net/dns_client.cpp index bb33d70721c..da1672535e4 100644 --- a/src/core/net/dns_client.cpp +++ b/src/core/net/dns_client.cpp @@ -49,6 +49,11 @@ namespace ot { namespace Dns { +#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE +using ot::Encoding::BigEndian::ReadUint16; +using ot::Encoding::BigEndian::WriteUint16; +#endif + RegisterLogModule("DnsClient"); //--------------------------------------------------------------------------------------------------------------------- @@ -65,18 +70,27 @@ Client::QueryConfig::QueryConfig(InitMode aMode) SetResponseTimeout(kDefaultResponseTimeout); SetMaxTxAttempts(kDefaultMaxTxAttempts); SetRecursionFlag(kDefaultRecursionDesired ? kFlagRecursionDesired : kFlagNoRecursion); + SetServiceMode(kDefaultServiceMode); #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE SetNat64Mode(kDefaultNat64Allowed ? kNat64Allow : kNat64Disallow); #endif + SetTransportProto(kDnsTransportUdp); } -void Client::QueryConfig::SetFrom(const QueryConfig &aConfig, const QueryConfig &aDefaultConfig) +void Client::QueryConfig::SetFrom(const QueryConfig *aConfig, const QueryConfig &aDefaultConfig) { // This method sets the config from `aConfig` replacing any // unspecified fields (value zero) with the fields from - // `aDefaultConfig`. + // `aDefaultConfig`. If `aConfig` is `nullptr` then + // `aDefaultConfig` is used. + + if (aConfig == nullptr) + { + *this = aDefaultConfig; + ExitNow(); + } - *this = aConfig; + *this = *aConfig; if (GetServerSockAddr().GetAddress().IsUnspecified()) { @@ -109,6 +123,19 @@ void Client::QueryConfig::SetFrom(const QueryConfig &aConfig, const QueryConfig SetNat64Mode(aDefaultConfig.GetNat64Mode()); } #endif + + if (GetServiceMode() == kServiceModeUnspecified) + { + SetServiceMode(aDefaultConfig.GetServiceMode()); + } + + if (GetTransportProto() == kDnsTransportUnspecified) + { + SetTransportProto(aDefaultConfig.GetTransportProto()); + } + +exit: + return; } //--------------------------------------------------------------------------------------------------------------------- @@ -176,10 +203,10 @@ Error Client::Response::CheckForHostNameAlias(Section aSection, Name &aHostName) } Error Client::Response::FindHostAddress(Section aSection, - const Name & aHostName, + const Name &aHostName, uint16_t aIndex, Ip6::Address &aAddress, - uint32_t & aTtl) const + uint32_t &aTtl) const { Error error; uint16_t offset; @@ -220,22 +247,42 @@ Error Client::Response::FindARecord(Section aSection, const Name &aHostName, uin #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE -Error Client::Response::FindServiceInfo(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const +void Client::Response::InitServiceInfo(ServiceInfo &aServiceInfo) const { - // This method searches for SRV and TXT records in the given - // section matching the record name against `aName`, and updates - // the `aServiceInfo` accordingly. It also searches for AAAA - // record for host name associated with the service (from SRV - // record). The search for AAAA record is always performed in - // Additional Data section (independent of the value given in - // `aSection`). + // This method initializes `aServiceInfo` setting all + // TTLs to zero and host name to empty string. - Error error; + aServiceInfo.mTtl = 0; + aServiceInfo.mHostAddressTtl = 0; + aServiceInfo.mTxtDataTtl = 0; + aServiceInfo.mTxtDataTruncated = false; + + AsCoreType(&aServiceInfo.mHostAddress).Clear(); + + if ((aServiceInfo.mHostNameBuffer != nullptr) && (aServiceInfo.mHostNameBufferSize > 0)) + { + aServiceInfo.mHostNameBuffer[0] = '\0'; + } +} + +Error Client::Response::ReadServiceInfo(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const +{ + // This method searches for SRV record in the given `aSection` + // matching the record name against `aName`, and updates the + // `aServiceInfo` accordingly. It also searches for AAAA record + // for host name associated with the service (from SRV record). + // The search for AAAA record is always performed in Additional + // Data section (independent of the value given in `aSection`). + + Error error = kErrorNone; uint16_t offset; uint16_t numRecords; Name hostName; SrvRecord srvRecord; - TxtRecord txtRecord; + + // A non-zero `mTtl` indicates that SRV record is already found + // and parsed from a previous response. + VerifyOrExit(aServiceInfo.mTtl == 0); VerifyOrExit(mMessage != nullptr, error = kErrorNotFound); @@ -267,46 +314,97 @@ Error Client::Response::FindServiceInfo(Section aSection, const Name &aName, Ser if (error == kErrorNotFound) { - AsCoreType(&aServiceInfo.mHostAddress).Clear(); - aServiceInfo.mHostAddressTtl = 0; - } - else - { - SuccessOrExit(error); + error = kErrorNone; } - // A null `mTxtData` indicates that caller does not want to retrieve TXT data. +exit: + return error; +} + +Error Client::Response::ReadTxtRecord(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const +{ + // This method searches a TXT record in the given `aSection` + // matching the record name against `aName` and updates the TXT + // related properties in `aServicesInfo`. + // + // If no match is found `mTxtDataTtl` (which is initialized to zero) + // remains unchanged to indicate this. In this case this method still + // returns `kErrorNone`. + + Error error = kErrorNone; + uint16_t offset; + uint16_t numRecords; + TxtRecord txtRecord; + + // A non-zero `mTxtDataTtl` indicates that TXT record is already + // found and parsed from a previous response. + VerifyOrExit(aServiceInfo.mTxtDataTtl == 0); + + // A null `mTxtData` indicates that caller does not want to retrieve + // TXT data. VerifyOrExit(aServiceInfo.mTxtData != nullptr); - // Search for a matching TXT record. If not found, indicate this by - // setting `aServiceInfo.mTxtDataSize` to zero. + VerifyOrExit(mMessage != nullptr, error = kErrorNotFound); SelectSection(aSection, offset, numRecords); - error = ResourceRecord::FindRecord(*mMessage, offset, numRecords, /* aIndex */ 0, aName, txtRecord); - switch (error) - { - case kErrorNone: - SuccessOrExit(error = - txtRecord.ReadTxtData(*mMessage, offset, aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize)); - aServiceInfo.mTxtDataTtl = txtRecord.GetTtl(); - break; + aServiceInfo.mTxtDataTruncated = false; - case kErrorNotFound: - aServiceInfo.mTxtDataSize = 0; - aServiceInfo.mTxtDataTtl = 0; - break; + SuccessOrExit(error = ResourceRecord::FindRecord(*mMessage, offset, numRecords, /* aIndex */ 0, aName, txtRecord)); - default: - ExitNow(); + error = txtRecord.ReadTxtData(*mMessage, offset, aServiceInfo.mTxtData, aServiceInfo.mTxtDataSize); + + if (error == kErrorNoBufs) + { + error = kErrorNone; + + // Mark `mTxtDataTruncated` to indicate that we could not read + // the full TXT record into the given `mTxtData` buffer. + aServiceInfo.mTxtDataTruncated = true; } + SuccessOrExit(error); + aServiceInfo.mTxtDataTtl = txtRecord.GetTtl(); + exit: + if (error == kErrorNotFound) + { + error = kErrorNone; + } + return error; } #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE +void Client::Response::PopulateFrom(const Message &aMessage) +{ + // Populate `Response` with info from `aMessage`. + + uint16_t offset = aMessage.GetOffset(); + Header header; + + mMessage = &aMessage; + + IgnoreError(aMessage.Read(offset, header)); + offset += sizeof(Header); + + for (uint16_t num = 0; num < header.GetQuestionCount(); num++) + { + IgnoreError(Name::ParseName(aMessage, offset)); + offset += sizeof(Question); + } + + mAnswerOffset = offset; + IgnoreError(ResourceRecord::ParseRecords(aMessage, offset, header.GetAnswerCount())); + IgnoreError(ResourceRecord::ParseRecords(aMessage, offset, header.GetAuthorityRecordCount())); + mAdditionalOffset = offset; + IgnoreError(ResourceRecord::ParseRecords(aMessage, offset, header.GetAdditionalRecordCount())); + + mAnswerRecordCount = header.GetAnswerCount(); + mAdditionalRecordCount = header.GetAdditionalRecordCount(); +} + //--------------------------------------------------------------------------------------------------------------------- // Client::AddressResponse @@ -380,21 +478,29 @@ Error Client::BrowseResponse::GetServiceInfo(const char *aInstanceLabel, Service Error error; Name instanceName; - // Find a matching PTR record for the service instance label. - // Then search and read SRV, TXT and AAAA records in Additional Data section - // matching the same name to populate `aServiceInfo`. + // Find a matching PTR record for the service instance label. Then + // search and read SRV, TXT and AAAA records in Additional Data + // section matching the same name to populate `aServiceInfo`. SuccessOrExit(error = FindPtrRecord(aInstanceLabel, instanceName)); - error = FindServiceInfo(kAdditionalDataSection, instanceName, aServiceInfo); + + InitServiceInfo(aServiceInfo); + SuccessOrExit(error = ReadServiceInfo(kAdditionalDataSection, instanceName, aServiceInfo)); + SuccessOrExit(error = ReadTxtRecord(kAdditionalDataSection, instanceName, aServiceInfo)); + + if (aServiceInfo.mTxtDataTtl == 0) + { + aServiceInfo.mTxtDataSize = 0; + } exit: return error; } -Error Client::BrowseResponse::GetHostAddress(const char * aHostName, +Error Client::BrowseResponse::GetHostAddress(const char *aHostName, uint16_t aIndex, Ip6::Address &aAddress, - uint32_t & aTtl) const + uint32_t &aTtl) const { return FindHostAddress(kAdditionalDataSection, Name(aHostName), aIndex, aAddress, aTtl); } @@ -458,9 +564,9 @@ Error Client::BrowseResponse::FindPtrRecord(const char *aInstanceLabel, Name &aI //--------------------------------------------------------------------------------------------------------------------- // Client::ServiceResponse -Error Client::ServiceResponse::GetServiceName(char * aLabelBuffer, +Error Client::ServiceResponse::GetServiceName(char *aLabelBuffer, uint8_t aLabelBufferSize, - char * aNameBuffer, + char *aNameBuffer, uint16_t aNameBufferSize) const { Error error; @@ -477,18 +583,71 @@ Error Client::ServiceResponse::GetServiceName(char * aLabelBuffer, Error Client::ServiceResponse::GetServiceInfo(ServiceInfo &aServiceInfo) const { - // Search and read SRV, TXT records in Answer Section - // matching name from query. + // Search and read SRV, TXT records matching name from query. + + Error error = kErrorNotFound; + + InitServiceInfo(aServiceInfo); + + for (const Response *response = this; response != nullptr; response = response->mNext) + { + Name name(*response->mQuery, kNameOffsetInQuery); + QueryInfo info; + Section srvSection; + Section txtSection; + + info.ReadFrom(*response->mQuery); + + // Determine from which section we should try to read the SRV and + // TXT records based on the query type. + // + // In `kServiceQuerySrv` or `kServiceQueryTxt` we expect to see + // only one record (SRV or TXT) in the answer section, but we + // still try to read the other records from additional data + // section in case server provided them. + + srvSection = (info.mQueryType != kServiceQueryTxt) ? kAnswerSection : kAdditionalDataSection; + txtSection = (info.mQueryType != kServiceQuerySrv) ? kAnswerSection : kAdditionalDataSection; + + error = response->ReadServiceInfo(srvSection, name, aServiceInfo); + + if ((srvSection == kAdditionalDataSection) && (error == kErrorNotFound)) + { + error = kErrorNone; + } - return FindServiceInfo(kAnswerSection, Name(*mQuery, kNameOffsetInQuery), aServiceInfo); + SuccessOrExit(error); + + SuccessOrExit(error = response->ReadTxtRecord(txtSection, name, aServiceInfo)); + } + + if (aServiceInfo.mTxtDataTtl == 0) + { + aServiceInfo.mTxtDataSize = 0; + } + +exit: + return error; } -Error Client::ServiceResponse::GetHostAddress(const char * aHostName, +Error Client::ServiceResponse::GetHostAddress(const char *aHostName, uint16_t aIndex, Ip6::Address &aAddress, - uint32_t & aTtl) const + uint32_t &aTtl) const { - return FindHostAddress(kAdditionalDataSection, Name(aHostName), aIndex, aAddress, aTtl); + Error error = kErrorNotFound; + + for (const Response *response = this; response != nullptr; response = response->mNext) + { + error = response->FindHostAddress(kAdditionalDataSection, Name(aHostName), aIndex, aAddress, aTtl); + + if (error == kErrorNone) + { + break; + } + } + + return error; } #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE @@ -506,31 +665,39 @@ const uint16_t Client::kServiceQueryRecordTypes[] = {ResourceRecord::kTypeSrv, R #endif const uint8_t Client::kQuestionCount[] = { - /* kIp6AddressQuery -> */ GetArrayLength(kIp6AddressQueryRecordTypes), // AAAA records + /* kIp6AddressQuery -> */ GetArrayLength(kIp6AddressQueryRecordTypes), // AAAA record #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE - /* kIp4AddressQuery -> */ GetArrayLength(kIp4AddressQueryRecordTypes), // A records + /* kIp4AddressQuery -> */ GetArrayLength(kIp4AddressQueryRecordTypes), // A record #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE - /* kBrowseQuery -> */ GetArrayLength(kBrowseQueryRecordTypes), // PTR records - /* kServiceQuery -> */ GetArrayLength(kServiceQueryRecordTypes), // SRV and TXT records + /* kBrowseQuery -> */ GetArrayLength(kBrowseQueryRecordTypes), // PTR record + /* kServiceQuerySrvTxt -> */ GetArrayLength(kServiceQueryRecordTypes), // SRV and TXT records + /* kServiceQuerySrv -> */ 1, // SRV record only + /* kServiceQueryTxt -> */ 1, // TXT record only #endif }; -const uint16_t *Client::kQuestionRecordTypes[] = { +const uint16_t *const Client::kQuestionRecordTypes[] = { /* kIp6AddressQuery -> */ kIp6AddressQueryRecordTypes, #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE /* kIp4AddressQuery -> */ kIp4AddressQueryRecordTypes, #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE /* kBrowseQuery -> */ kBrowseQueryRecordTypes, - /* kServiceQuery -> */ kServiceQueryRecordTypes, + /* kServiceQuerySrvTxt -> */ kServiceQueryRecordTypes, + /* kServiceQuerySrv -> */ &kServiceQueryRecordTypes[0], + /* kServiceQueryTxt -> */ &kServiceQueryRecordTypes[1], + #endif }; Client::Client(Instance &aInstance) : InstanceLocator(aInstance) , mSocket(aInstance) - , mTimer(aInstance, Client::HandleTimer) +#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE + , mTcpState(kTcpUninitialized) +#endif + , mTimer(aInstance) , mDefaultConfig(QueryConfig::kInitFromDefaults) #if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE , mUserDidSetDefaultAddress(false) @@ -541,11 +708,15 @@ Client::Client(Instance &aInstance) static_assert(kIp4AddressQuery == 1, "kIp4AddressQuery value is not correct"); #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE static_assert(kBrowseQuery == 2, "kBrowseQuery value is not correct"); - static_assert(kServiceQuery == 3, "kServiceQuery value is not correct"); + static_assert(kServiceQuerySrvTxt == 3, "kServiceQuerySrvTxt value is not correct"); + static_assert(kServiceQuerySrv == 4, "kServiceQuerySrv value is not correct"); + static_assert(kServiceQueryTxt == 5, "kServiceQueryTxt value is not correct"); #endif #elif OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE static_assert(kBrowseQuery == 1, "kBrowseQuery value is not correct"); - static_assert(kServiceQuery == 2, "kServiceQuery value is not correct"); + static_assert(kServiceQuerySrvTxt == 2, "kServiceQuerySrvTxt value is not correct"); + static_assert(kServiceQuerySrv == 3, "kServiceQuerySrv value is not correct"); + static_assert(kServiceQueryTxt == 4, "kServiceQuerySrv value is not correct"); #endif } @@ -554,7 +725,7 @@ Error Client::Start(void) Error error; SuccessOrExit(error = mSocket.Open(&Client::HandleUdpReceive, this)); - SuccessOrExit(error = mSocket.Bind(0, OT_NETIF_UNSPECIFIED)); + SuccessOrExit(error = mSocket.Bind(0, Ip6::kNetifUnspecified)); exit: return error; @@ -564,19 +735,50 @@ void Client::Stop(void) { Query *query; - while ((query = mQueries.GetHead()) != nullptr) + while ((query = mMainQueries.GetHead()) != nullptr) { FinalizeQuery(*query, kErrorAbort); } IgnoreError(mSocket.Close()); +#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE + if (mTcpState != kTcpUninitialized) + { + IgnoreError(mEndpoint.Deinitialize()); + } +#endif +} + +#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE +Error Client::InitTcpSocket(void) +{ + Error error; + otTcpEndpointInitializeArgs endpointArgs; + + memset(&endpointArgs, 0x00, sizeof(endpointArgs)); + endpointArgs.mSendDoneCallback = HandleTcpSendDoneCallback; + endpointArgs.mEstablishedCallback = HandleTcpEstablishedCallback; + endpointArgs.mReceiveAvailableCallback = HandleTcpReceiveAvailableCallback; + endpointArgs.mDisconnectedCallback = HandleTcpDisconnectedCallback; + endpointArgs.mContext = this; + endpointArgs.mReceiveBuffer = mReceiveBufferBytes; + endpointArgs.mReceiveBufferSize = sizeof(mReceiveBufferBytes); + + mSendLink.mNext = nullptr; + mSendLink.mData = mSendBufferBytes; + mSendLink.mLength = 0; + + SuccessOrExit(error = mEndpoint.Initialize(Get(), endpointArgs)); +exit: + return error; } +#endif void Client::SetDefaultConfig(const QueryConfig &aQueryConfig) { QueryConfig startingDefault(QueryConfig::kInitFromDefaults); - mDefaultConfig.SetFrom(aQueryConfig, startingDefault); + mDefaultConfig.SetFrom(&aQueryConfig, startingDefault); #if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE mUserDidSetDefaultAddress = !aQueryConfig.GetServerSockAddr().GetAddress().IsUnspecified(); @@ -607,33 +809,37 @@ void Client::UpdateDefaultConfigAddress(void) } #endif -Error Client::ResolveAddress(const char * aHostName, +Error Client::ResolveAddress(const char *aHostName, AddressCallback aCallback, - void * aContext, + void *aContext, const QueryConfig *aConfig) { QueryInfo info; info.Clear(); - info.mQueryType = kIp6AddressQuery; + info.mQueryType = kIp6AddressQuery; + info.mConfig.SetFrom(aConfig, mDefaultConfig); info.mCallback.mAddressCallback = aCallback; + info.mCallbackContext = aContext; - return StartQuery(info, aConfig, nullptr, aHostName, aContext); + return StartQuery(info, nullptr, aHostName); } #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE -Error Client::ResolveIp4Address(const char * aHostName, +Error Client::ResolveIp4Address(const char *aHostName, AddressCallback aCallback, - void * aContext, + void *aContext, const QueryConfig *aConfig) { QueryInfo info; info.Clear(); - info.mQueryType = kIp4AddressQuery; + info.mQueryType = kIp4AddressQuery; + info.mConfig.SetFrom(aConfig, mDefaultConfig); info.mCallback.mAddressCallback = aCallback; + info.mCallbackContext = aContext; - return StartQuery(info, aConfig, nullptr, aHostName, aContext); + return StartQuery(info, nullptr, aHostName); } #endif @@ -644,28 +850,56 @@ Error Client::Browse(const char *aServiceName, BrowseCallback aCallback, void *a QueryInfo info; info.Clear(); - info.mQueryType = kBrowseQuery; + info.mQueryType = kBrowseQuery; + info.mConfig.SetFrom(aConfig, mDefaultConfig); info.mCallback.mBrowseCallback = aCallback; + info.mCallbackContext = aContext; - return StartQuery(info, aConfig, nullptr, aServiceName, aContext); + return StartQuery(info, nullptr, aServiceName); } -Error Client::ResolveService(const char * aInstanceLabel, - const char * aServiceName, +Error Client::ResolveService(const char *aInstanceLabel, + const char *aServiceName, ServiceCallback aCallback, - void * aContext, + void *aContext, const QueryConfig *aConfig) { QueryInfo info; Error error; + QueryType secondQueryType = kNoQuery; VerifyOrExit(aInstanceLabel != nullptr, error = kErrorInvalidArgs); info.Clear(); - info.mQueryType = kServiceQuery; + + info.mConfig.SetFrom(aConfig, mDefaultConfig); + + switch (info.mConfig.GetServiceMode()) + { + case QueryConfig::kServiceModeSrvTxtSeparate: + secondQueryType = kServiceQueryTxt; + + OT_FALL_THROUGH; + + case QueryConfig::kServiceModeSrv: + info.mQueryType = kServiceQuerySrv; + break; + + case QueryConfig::kServiceModeTxt: + info.mQueryType = kServiceQueryTxt; + break; + + case QueryConfig::kServiceModeSrvTxt: + case QueryConfig::kServiceModeSrvTxtOptimize: + default: + info.mQueryType = kServiceQuerySrvTxt; + break; + } + info.mCallback.mServiceCallback = aCallback; + info.mCallbackContext = aContext; - error = StartQuery(info, aConfig, aInstanceLabel, aServiceName, aContext); + error = StartQuery(info, aInstanceLabel, aServiceName, secondQueryType); exit: return error; @@ -673,37 +907,17 @@ Error Client::ResolveService(const char * aInstanceLabel, #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE -Error Client::StartQuery(QueryInfo & aInfo, - const QueryConfig *aConfig, - const char * aLabel, - const char * aName, - void * aContext) +Error Client::StartQuery(QueryInfo &aInfo, const char *aLabel, const char *aName, QueryType aSecondType) { - // This method assumes that `mQueryType` and `mCallback` to be - // already set by caller on `aInfo`. The `aLabel` can be `nullptr` - // and then `aName` provides the full name, otherwise the name is - // appended as `{aLabel}.{aName}`. + // The `aLabel` can be `nullptr` and then `aName` provides the + // full name, otherwise the name is appended as `{aLabel}. + // {aName}`. Error error; Query *query; VerifyOrExit(mSocket.IsBound(), error = kErrorInvalidState); - if (aConfig == nullptr) - { - aInfo.mConfig = mDefaultConfig; - } - else - { - // To form the config for this query, replace any unspecified - // fields (zero value) in the given `aConfig` with the fields - // from `mDefaultConfig`. - - aInfo.mConfig.SetFrom(*aConfig, mDefaultConfig); - } - - aInfo.mCallbackContext = aContext; - #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE if (aInfo.mQueryType == kIp4AddressQuery) { @@ -716,9 +930,34 @@ Error Client::StartQuery(QueryInfo & aInfo, #endif SuccessOrExit(error = AllocateQuery(aInfo, aLabel, aName, query)); - mQueries.Enqueue(*query); - SendQuery(*query, aInfo, /* aUpdateTimer */ true); + mMainQueries.Enqueue(*query); + + error = SendQuery(*query, aInfo, /* aUpdateTimer */ true); + VerifyOrExit(error == kErrorNone, FreeQuery(*query)); + + if (aSecondType != kNoQuery) + { + Query *secondQuery; + + aInfo.mQueryType = aSecondType; + aInfo.mMessageId = 0; + aInfo.mTransmissionCount = 0; + aInfo.mMainQuery = query; + + // We intentionally do not use `error` here so in the unlikely + // case where we cannot allocate the second query we can proceed + // with the first one. + SuccessOrExit(AllocateQuery(aInfo, aLabel, aName, secondQuery)); + + IgnoreError(SendQuery(*secondQuery, aInfo, /* aUpdateTimer */ true)); + + // Update first query to link to second one by updating + // its `mNextQuery`. + aInfo.ReadFrom(*query); + aInfo.mNextQuery = secondQuery; + UpdateQuery(*query, aInfo); + } exit: return error; @@ -728,6 +967,10 @@ Error Client::AllocateQuery(const QueryInfo &aInfo, const char *aLabel, const ch { Error error = kErrorNone; + aQuery = nullptr; + + VerifyOrExit(aInfo.mConfig.GetResponseTimeout() <= TimerMilli::kMaxDelay, error = kErrorInvalidArgs); + aQuery = Get().Allocate(Message::kTypeOther); VerifyOrExit(aQuery != nullptr, error = kErrorNoBufs); @@ -745,12 +988,31 @@ Error Client::AllocateQuery(const QueryInfo &aInfo, const char *aLabel, const ch return error; } +Client::Query &Client::FindMainQuery(Query &aQuery) +{ + QueryInfo info; + + info.ReadFrom(aQuery); + + return (info.mMainQuery == nullptr) ? aQuery : *info.mMainQuery; +} + void Client::FreeQuery(Query &aQuery) { - mQueries.DequeueAndFree(aQuery); + Query &mainQuery = FindMainQuery(aQuery); + QueryInfo info; + + mMainQueries.Dequeue(mainQuery); + + for (Query *query = &mainQuery; query != nullptr; query = info.mNextQuery) + { + info.ReadFrom(*query); + FreeMessage(info.mSavedResponse); + query->Free(); + } } -void Client::SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer) +Error Client::SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer) { // This method prepares and sends a query message represented by // `aQuery` and `aInfo`. This method updates `aInfo` (e.g., sets @@ -760,9 +1022,10 @@ void Client::SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer) // is handled by caller). Error error = kErrorNone; - Message * message = nullptr; + Message *message = nullptr; Header header; Ip6::MessageInfo messageInfo; + uint16_t length = 0; aInfo.mTransmissionCount++; aInfo.mRetransmissionTime = TimerMilli::GetNow() + aInfo.mConfig.GetResponseTimeout(); @@ -791,7 +1054,7 @@ void Client::SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer) header.SetQuestionCount(kQuestionCount[aInfo.mQueryType]); - message = mSocket.NewMessage(0); + message = mSocket.NewMessage(); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->Append(header)); @@ -804,64 +1067,106 @@ void Client::SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer) SuccessOrExit(error = message->Append(Question(kQuestionRecordTypes[aInfo.mQueryType][num]))); } - messageInfo.SetPeerAddr(aInfo.mConfig.GetServerSockAddr().GetAddress()); - messageInfo.SetPeerPort(aInfo.mConfig.GetServerSockAddr().GetPort()); + length = message->GetLength() - message->GetOffset(); - SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); + if (aInfo.mConfig.GetTransportProto() == QueryConfig::kDnsTransportTcp) +#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE + { + // Check if query will fit into tcp buffer if not return error. + VerifyOrExit(length + sizeof(uint16_t) + mSendLink.mLength <= + OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE, + error = kErrorNoBufs); -exit: - FreeMessageOnError(message, error); + // In case of initialized connection check if connected peer and new query have the same address. + if (mTcpState != kTcpUninitialized) + { + VerifyOrExit(mEndpoint.GetPeerAddress() == AsCoreType(&aInfo.mConfig.mServerSockAddr), + error = kErrorFailed); + } - UpdateQuery(aQuery, aInfo); + switch (mTcpState) + { + case kTcpUninitialized: + SuccessOrExit(error = InitTcpSocket()); + SuccessOrExit( + error = mEndpoint.Connect(AsCoreType(&aInfo.mConfig.mServerSockAddr), OT_TCP_CONNECT_NO_FAST_OPEN)); + mTcpState = kTcpConnecting; + PrepareTcpMessage(*message); + break; + case kTcpConnectedIdle: + PrepareTcpMessage(*message); + SuccessOrExit(error = mEndpoint.SendByReference(mSendLink, /* aFlags */ 0)); + mTcpState = kTcpConnectedSending; + break; + case kTcpConnecting: + PrepareTcpMessage(*message); + break; + case kTcpConnectedSending: + WriteUint16(length, mSendBufferBytes + mSendLink.mLength); + SuccessOrAssert(error = message->Read(message->GetOffset(), + (mSendBufferBytes + sizeof(uint16_t) + mSendLink.mLength), length)); + IgnoreError(mEndpoint.SendByExtension(length + sizeof(uint16_t), /* aFlags */ 0)); + break; + } + message->Free(); + message = nullptr; + } +#else + { + error = kErrorInvalidArgs; + LogWarn("DNS query over TCP not supported."); + ExitNow(); + } +#endif + else + { + VerifyOrExit(length <= kUdpQueryMaxSize, error = kErrorInvalidArgs); + messageInfo.SetPeerAddr(aInfo.mConfig.GetServerSockAddr().GetAddress()); + messageInfo.SetPeerPort(aInfo.mConfig.GetServerSockAddr().GetPort()); + SuccessOrExit(error = mSocket.SendTo(*message, messageInfo)); + } + +exit: + FreeMessageOnError(message, error); if (aUpdateTimer) { mTimer.FireAtIfEarlier(aInfo.mRetransmissionTime); } + + UpdateQuery(aQuery, aInfo); + + return error; } Error Client::AppendNameFromQuery(const Query &aQuery, Message &aMessage) { - Error error = kErrorNone; - uint16_t offset; - uint16_t length; - - // The name is encoded and included after the `Info` in `aQuery`. We - // first calculate the encoded length of the name, then grow the - // message, and finally copy the encoded name bytes from `aQuery` - // into `aMessage`. - - length = aQuery.GetLength() - kNameOffsetInQuery; + // The name is encoded and included after the `Info` in `aQuery` + // starting at `kNameOffsetInQuery`. - offset = aMessage.GetLength(); - SuccessOrExit(error = aMessage.SetLength(offset + length)); - - aQuery.CopyTo(/* aSourceOffset */ kNameOffsetInQuery, /* aDestOffset */ offset, length, aMessage); - -exit: - return error; + return aMessage.AppendBytesFromMessage(aQuery, kNameOffsetInQuery, aQuery.GetLength() - kNameOffsetInQuery); } void Client::FinalizeQuery(Query &aQuery, Error aError) { - Response response; - QueryInfo info; + Response response; + Query &mainQuery = FindMainQuery(aQuery); response.mInstance = &Get(); - response.mQuery = &aQuery; - info.ReadFrom(aQuery); + response.mQuery = &mainQuery; - FinalizeQuery(response, info.mQueryType, aError); + FinalizeQuery(response, aError); } -void Client::FinalizeQuery(Response &aResponse, QueryType aType, Error aError) +void Client::FinalizeQuery(Response &aResponse, Error aError) { - Callback callback; - void * context; + QueryType type; + Callback callback; + void *context; - GetCallback(*aResponse.mQuery, callback, context); + GetQueryTypeAndCallback(*aResponse.mQuery, type, callback, context); - switch (aType) + switch (type) { case kIp6AddressQuery: #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE @@ -881,44 +1186,53 @@ void Client::FinalizeQuery(Response &aResponse, QueryType aType, Error aError) } break; - case kServiceQuery: + case kServiceQuerySrvTxt: + case kServiceQuerySrv: + case kServiceQueryTxt: if (callback.mServiceCallback != nullptr) { callback.mServiceCallback(aError, &aResponse, context); } break; #endif + case kNoQuery: + break; } FreeQuery(*aResponse.mQuery); } -void Client::GetCallback(const Query &aQuery, Callback &aCallback, void *&aContext) +void Client::GetQueryTypeAndCallback(const Query &aQuery, QueryType &aType, Callback &aCallback, void *&aContext) { QueryInfo info; info.ReadFrom(aQuery); + aType = info.mQueryType; aCallback = info.mCallback; aContext = info.mCallbackContext; } Client::Query *Client::FindQueryById(uint16_t aMessageId) { - Query * matchedQuery = nullptr; + Query *matchedQuery = nullptr; QueryInfo info; - for (Query &query : mQueries) + for (Query &mainQuery : mMainQueries) { - info.ReadFrom(query); - - if (info.mMessageId == aMessageId) + for (Query *query = &mainQuery; query != nullptr; query = info.mNextQuery) { - matchedQuery = &query; - break; + info.ReadFrom(*query); + + if (info.mMessageId == aMessageId) + { + matchedQuery = query; + ExitNow(); + } } } +exit: return matchedQuery; } @@ -929,58 +1243,78 @@ void Client::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessa static_cast(aContext)->ProcessResponse(AsCoreType(aMessage)); } -void Client::ProcessResponse(const Message &aMessage) +void Client::ProcessResponse(const Message &aResponseMessage) { - Response response; - QueryType type; - Error responseError; + Error responseError; + Query *query; - response.mInstance = &Get(); - response.mMessage = &aMessage; + SuccessOrExit(ParseResponse(aResponseMessage, query, responseError)); + + if (responseError != kErrorNone) + { + // Received an error from server, check if we can replace + // the query. - // We intentionally parse the response in a separate method - // `ParseResponse()` to free all the stack allocated variables - // (e.g., `QueryInfo`) used during parsing of the message before - // finalizing the query and invoking the user's callback. +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + if (ReplaceWithIp4Query(*query) == kErrorNone) + { + ExitNow(); + } +#endif +#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE + if (ReplaceWithSeparateSrvTxtQueries(*query) == kErrorNone) + { + ExitNow(); + } +#endif + + FinalizeQuery(*query, responseError); + ExitNow(); + } - SuccessOrExit(ParseResponse(response, type, responseError)); - FinalizeQuery(response, type, responseError); + // Received successful response from server. + + if (!CanFinalizeQuery(*query)) + { + SaveQueryResponse(*query, aResponseMessage); + ExitNow(); + } + + PrepareResponseAndFinalize(FindMainQuery(*query), aResponseMessage, nullptr); exit: return; } -Error Client::ParseResponse(Response &aResponse, QueryType &aType, Error &aResponseError) +Error Client::ParseResponse(const Message &aResponseMessage, Query *&aQuery, Error &aResponseError) { - Error error = kErrorNone; - const Message &message = *aResponse.mMessage; - uint16_t offset = message.GetOffset(); - Header header; - QueryInfo info; - Name queryName; - - SuccessOrExit(error = message.Read(offset, header)); + Error error = kErrorNone; + uint16_t offset = aResponseMessage.GetOffset(); + Header header; + QueryInfo info; + Name queryName; + + SuccessOrExit(error = aResponseMessage.Read(offset, header)); offset += sizeof(Header); VerifyOrExit((header.GetType() == Header::kTypeResponse) && (header.GetQueryType() == Header::kQueryTypeStandard) && !header.IsTruncationFlagSet(), error = kErrorDrop); - aResponse.mQuery = FindQueryById(header.GetMessageId()); - VerifyOrExit(aResponse.mQuery != nullptr, error = kErrorNotFound); + aQuery = FindQueryById(header.GetMessageId()); + VerifyOrExit(aQuery != nullptr, error = kErrorNotFound); - info.ReadFrom(*aResponse.mQuery); - aType = info.mQueryType; + info.ReadFrom(*aQuery); - queryName.SetFromMessage(*aResponse.mQuery, kNameOffsetInQuery); + queryName.SetFromMessage(*aQuery, kNameOffsetInQuery); // Check the Question Section - if (header.GetQuestionCount() == kQuestionCount[aType]) + if (header.GetQuestionCount() == kQuestionCount[info.mQueryType]) { - for (uint8_t num = 0; num < kQuestionCount[aType]; num++) + for (uint8_t num = 0; num < kQuestionCount[info.mQueryType]; num++) { - SuccessOrExit(error = Name::CompareName(message, offset, queryName)); + SuccessOrExit(error = Name::CompareName(aResponseMessage, offset, queryName)); offset += sizeof(Question); } } @@ -992,79 +1326,103 @@ Error Client::ParseResponse(Response &aResponse, QueryType &aType, Error &aRespo // Check the answer, authority and additional record sections - aResponse.mAnswerOffset = offset; - SuccessOrExit(error = ResourceRecord::ParseRecords(message, offset, header.GetAnswerCount())); - SuccessOrExit(error = ResourceRecord::ParseRecords(message, offset, header.GetAuthorityRecordCount())); - aResponse.mAdditionalOffset = offset; - SuccessOrExit(error = ResourceRecord::ParseRecords(message, offset, header.GetAdditionalRecordCount())); + SuccessOrExit(error = ResourceRecord::ParseRecords(aResponseMessage, offset, header.GetAnswerCount())); + SuccessOrExit(error = ResourceRecord::ParseRecords(aResponseMessage, offset, header.GetAuthorityRecordCount())); + SuccessOrExit(error = ResourceRecord::ParseRecords(aResponseMessage, offset, header.GetAdditionalRecordCount())); - aResponse.mAnswerRecordCount = header.GetAnswerCount(); - aResponse.mAdditionalRecordCount = header.GetAdditionalRecordCount(); - - // Check the response code from server + // Read the response code aResponseError = Header::ResponseCodeToError(header.GetResponseCode()); -#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE +exit: + return error; +} - if (aType == kIp6AddressQuery) - { - Ip6::Address ip6ddress; - uint32_t ttl; - ARecord aRecord; +bool Client::CanFinalizeQuery(Query &aQuery) +{ + // Determines whether we can finalize a main query by checking if + // we have received and saved responses for all other related + // queries associated with `aQuery`. Note that this method is + // called when we receive a response for `aQuery`, so no need to + // check for a saved response for `aQuery` itself. - // If the response does not contain an answer for the IPv6 address - // resolution query and if NAT64 is allowed for this query, we can - // perform IPv4 to IPv6 address translation. + bool canFinalize = true; + QueryInfo info; - VerifyOrExit(aResponse.FindHostAddress(Response::kAnswerSection, queryName, /* aIndex */ 0, ip6ddress, ttl) != - kErrorNone); - VerifyOrExit(info.mConfig.GetNat64Mode() == QueryConfig::kNat64Allow); + for (Query *query = &FindMainQuery(aQuery); query != nullptr; query = info.mNextQuery) + { + info.ReadFrom(*query); - // First, we check if the response already contains an A record - // (IPv4 address) for the query name. + if (query == &aQuery) + { + continue; + } - if (aResponse.FindARecord(Response::kAdditionalDataSection, queryName, /* aIndex */ 0, aRecord) == kErrorNone) + if (info.mSavedResponse == nullptr) { - aResponse.mIp6QueryResponseRequiresNat64 = true; - aResponseError = kErrorNone; + canFinalize = false; ExitNow(); } + } - // Otherwise, we send a new query for IPv4 address resolution - // for the same host name. We reuse the existing `query` - // instance and keep all the info but clear `mTransmissionCount` - // and `mMessageId` (so that a new random message ID is - // selected). The new `info` will be saved in the query in - // `SendQuery()`. Note that the current query is still in the - // `mQueries` list when `SendQuery()` selects a new random - // message ID, so the existing message ID for this query will - // not be reused. Since the query is not yet resolved, we - // return `kErrorPending`. +exit: + return canFinalize; +} - info.mQueryType = kIp4AddressQuery; - info.mMessageId = 0; - info.mTransmissionCount = 0; +void Client::SaveQueryResponse(Query &aQuery, const Message &aResponseMessage) +{ + QueryInfo info; - SendQuery(*aResponse.mQuery, info, /* aUpdateTimer */ true); + info.ReadFrom(aQuery); + VerifyOrExit(info.mSavedResponse == nullptr); - error = kErrorPending; - } + // If `Clone()` fails we let retry or timeout handle the error. + info.mSavedResponse = aResponseMessage.Clone(); -#endif // OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + UpdateQuery(aQuery, info); exit: - if (error != kErrorNone) - { - LogInfo("Failed to parse response %s", ErrorToString(error)); - } + return; +} - return error; +Client::Query *Client::PopulateResponse(Response &aResponse, Query &aQuery, const Message &aResponseMessage) +{ + // Populate `aResponse` for `aQuery`. If there is a saved response + // message for `aQuery` we use it, otherwise, we use + // `aResponseMessage`. + + QueryInfo info; + + info.ReadFrom(aQuery); + + aResponse.mInstance = &Get(); + aResponse.mQuery = &aQuery; + aResponse.PopulateFrom((info.mSavedResponse == nullptr) ? aResponseMessage : *info.mSavedResponse); + + return info.mNextQuery; } -void Client::HandleTimer(Timer &aTimer) +void Client::PrepareResponseAndFinalize(Query &aQuery, const Message &aResponseMessage, Response *aPrevResponse) { - aTimer.Get().HandleTimer(); + // This method prepares a list of chained `Response` instances + // corresponding to all related (chained) queries. It uses + // recursion to go through the queries and construct the + // `Response` chain. + + Response response; + Query *nextQuery; + + nextQuery = PopulateResponse(response, aQuery, aResponseMessage); + response.mNext = aPrevResponse; + + if (nextQuery != nullptr) + { + PrepareResponseAndFinalize(*nextQuery, aResponseMessage, &response); + } + else + { + FinalizeQuery(response, kErrorNone); + } } void Client::HandleTimer(void) @@ -1072,25 +1430,43 @@ void Client::HandleTimer(void) TimeMilli now = TimerMilli::GetNow(); TimeMilli nextTime = now.GetDistantFuture(); QueryInfo info; +#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE + bool hasTcpQuery = false; +#endif - for (Query &query : mQueries) + for (Query &mainQuery : mMainQueries) { - info.ReadFrom(query); - - if (now >= info.mRetransmissionTime) + for (Query *query = &mainQuery; query != nullptr; query = info.mNextQuery) { - if (info.mTransmissionCount >= info.mConfig.GetMaxTxAttempts()) + info.ReadFrom(*query); + + if (info.mSavedResponse != nullptr) { - FinalizeQuery(query, kErrorResponseTimeout); continue; } - SendQuery(query, info, /* aUpdateTimer */ false); - } + if (now >= info.mRetransmissionTime) + { + if (info.mTransmissionCount >= info.mConfig.GetMaxTxAttempts()) + { + FinalizeQuery(*query, kErrorResponseTimeout); + break; + } - if (nextTime > info.mRetransmissionTime) - { - nextTime = info.mRetransmissionTime; + IgnoreError(SendQuery(*query, info, /* aUpdateTimer */ false)); + } + + if (nextTime > info.mRetransmissionTime) + { + nextTime = info.mRetransmissionTime; + } + +#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE + if (info.mConfig.GetTransportProto() == QueryConfig::kDnsTransportTcp) + { + hasTcpQuery = true; + } +#endif } } @@ -1098,8 +1474,260 @@ void Client::HandleTimer(void) { mTimer.FireAt(nextTime); } + +#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE + if (!hasTcpQuery && mTcpState != kTcpUninitialized) + { + IgnoreError(mEndpoint.SendEndOfStream()); + } +#endif } +#if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + +Error Client::ReplaceWithIp4Query(Query &aQuery) +{ + Error error = kErrorFailed; + QueryInfo info; + + info.ReadFrom(aQuery); + + VerifyOrExit(info.mQueryType == kIp4AddressQuery); + VerifyOrExit(info.mConfig.GetNat64Mode() == QueryConfig::kNat64Allow); + + // We send a new query for IPv4 address resolution + // for the same host name. We reuse the existing `aQuery` + // instance and keep all the info but clear `mTransmissionCount` + // and `mMessageId` (so that a new random message ID is + // selected). The new `info` will be saved in the query in + // `SendQuery()`. Note that the current query is still in the + // `mMainQueries` list when `SendQuery()` selects a new random + // message ID, so the existing message ID for this query will + // not be reused. + + info.mQueryType = kIp4AddressQuery; + info.mMessageId = 0; + info.mTransmissionCount = 0; + + IgnoreError(SendQuery(aQuery, info, /* aUpdateTimer */ true)); + error = kErrorNone; + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE + +#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE + +Error Client::ReplaceWithSeparateSrvTxtQueries(Query &aQuery) +{ + Error error = kErrorFailed; + QueryInfo info; + Query *secondQuery; + + info.ReadFrom(aQuery); + + VerifyOrExit(info.mQueryType == kServiceQuerySrvTxt); + VerifyOrExit(info.mConfig.GetServiceMode() == QueryConfig::kServiceModeSrvTxtOptimize); + + secondQuery = aQuery.Clone(); + VerifyOrExit(secondQuery != nullptr); + + info.mQueryType = kServiceQueryTxt; + info.mMessageId = 0; + info.mTransmissionCount = 0; + info.mMainQuery = &aQuery; + IgnoreError(SendQuery(*secondQuery, info, /* aUpdateTimer */ true)); + + info.mQueryType = kServiceQuerySrv; + info.mMessageId = 0; + info.mTransmissionCount = 0; + info.mNextQuery = secondQuery; + IgnoreError(SendQuery(aQuery, info, /* aUpdateTimer */ true)); + error = kErrorNone; + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE + +#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE +void Client::PrepareTcpMessage(Message &aMessage) +{ + uint16_t length = aMessage.GetLength() - aMessage.GetOffset(); + + // Prepending the DNS query with length of the packet according to RFC1035. + WriteUint16(length, mSendBufferBytes + mSendLink.mLength); + SuccessOrAssert( + aMessage.Read(aMessage.GetOffset(), (mSendBufferBytes + sizeof(uint16_t) + mSendLink.mLength), length)); + mSendLink.mLength += length + sizeof(uint16_t); +} + +void Client::HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData) +{ + OT_UNUSED_VARIABLE(aEndpoint); + OT_UNUSED_VARIABLE(aData); + OT_ASSERT(mTcpState == kTcpConnectedSending); + + mSendLink.mLength = 0; + mTcpState = kTcpConnectedIdle; +} + +void Client::HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData) +{ + static_cast(otTcpEndpointGetContext(aEndpoint))->HandleTcpSendDone(aEndpoint, aData); +} + +void Client::HandleTcpEstablished(otTcpEndpoint *aEndpoint) +{ + OT_UNUSED_VARIABLE(aEndpoint); + IgnoreError(mEndpoint.SendByReference(mSendLink, /* aFlags */ 0)); + mTcpState = kTcpConnectedSending; +} + +void Client::HandleTcpEstablishedCallback(otTcpEndpoint *aEndpoint) +{ + static_cast(otTcpEndpointGetContext(aEndpoint))->HandleTcpEstablished(aEndpoint); +} + +Error Client::ReadFromLinkBuffer(const otLinkedBuffer *&aLinkedBuffer, + size_t &aOffset, + Message &aMessage, + uint16_t aLength) +{ + // Read `aLength` bytes from `aLinkedBuffer` starting at `aOffset` + // and copy the content into `aMessage`. As we read we can move + // to the next `aLinkedBuffer` and update `aOffset`. + // Returns: + // - `kErrorNone` if `aLength` bytes are successfully read and + // `aOffset` and `aLinkedBuffer` are updated. + // - `kErrorNotFound` is not enough bytes available to read + // from `aLinkedBuffer`. + // - `kErrorNotBufs` if cannot grow `aMessage` to append bytes. + + Error error = kErrorNone; + + while (aLength > 0) + { + uint16_t bytesToRead = aLength; + + VerifyOrExit(aLinkedBuffer != nullptr, error = kErrorNotFound); + + if (bytesToRead > aLinkedBuffer->mLength - aOffset) + { + bytesToRead = static_cast(aLinkedBuffer->mLength - aOffset); + } + + SuccessOrExit(error = aMessage.AppendBytes(&aLinkedBuffer->mData[aOffset], bytesToRead)); + + aLength -= bytesToRead; + aOffset += bytesToRead; + + if (aOffset == aLinkedBuffer->mLength) + { + aLinkedBuffer = aLinkedBuffer->mNext; + aOffset = 0; + } + } + +exit: + return error; +} + +void Client::HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint, + size_t aBytesAvailable, + bool aEndOfStream, + size_t aBytesRemaining) +{ + OT_UNUSED_VARIABLE(aEndpoint); + OT_UNUSED_VARIABLE(aBytesRemaining); + + Message *message = nullptr; + size_t totalRead = 0; + size_t offset = 0; + const otLinkedBuffer *data; + + if (aEndOfStream) + { + // Cleanup is done in disconnected callback. + IgnoreError(mEndpoint.SendEndOfStream()); + } + + SuccessOrExit(mEndpoint.ReceiveByReference(data)); + VerifyOrExit(data != nullptr); + + message = mSocket.NewMessage(); + VerifyOrExit(message != nullptr); + + while (aBytesAvailable > totalRead) + { + uint16_t length; + + // Read the `length` field. + SuccessOrExit(ReadFromLinkBuffer(data, offset, *message, sizeof(uint16_t))); + + IgnoreError(message->Read(/* aOffset */ 0, length)); + length = HostSwap16(length); + + // Try to read `length` bytes. + IgnoreError(message->SetLength(0)); + SuccessOrExit(ReadFromLinkBuffer(data, offset, *message, length)); + + totalRead += length + sizeof(uint16_t); + + // Now process the read message as query response. + ProcessResponse(*message); + + IgnoreError(message->SetLength(0)); + + // Loop again to see if we can read another response. + } + +exit: + // Inform `mEndPoint` about the total read and processed bytes + IgnoreError(mEndpoint.CommitReceive(totalRead, /* aFlags */ 0)); + FreeMessage(message); +} + +void Client::HandleTcpReceiveAvailableCallback(otTcpEndpoint *aEndpoint, + size_t aBytesAvailable, + bool aEndOfStream, + size_t aBytesRemaining) +{ + static_cast(otTcpEndpointGetContext(aEndpoint)) + ->HandleTcpReceiveAvailable(aEndpoint, aBytesAvailable, aEndOfStream, aBytesRemaining); +} + +void Client::HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason) +{ + OT_UNUSED_VARIABLE(aEndpoint); + OT_UNUSED_VARIABLE(aReason); + QueryInfo info; + + IgnoreError(mEndpoint.Deinitialize()); + mTcpState = kTcpUninitialized; + + // Abort queries in case of connection failures + for (Query &mainQuery : mMainQueries) + { + info.ReadFrom(mainQuery); + + if (info.mConfig.GetTransportProto() == QueryConfig::kDnsTransportTcp) + { + FinalizeQuery(mainQuery, kErrorAbort); + } + } +} + +void Client::HandleTcpDisconnectedCallback(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason) +{ + static_cast(otTcpEndpointGetContext(aEndpoint))->HandleTcpDisconnected(aEndpoint, aReason); +} + +#endif // OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE + } // namespace Dns } // namespace ot diff --git a/src/core/net/dns_client.hpp b/src/core/net/dns_client.hpp index de9afca3d22..12c63b9a9f5 100644 --- a/src/core/net/dns_client.hpp +++ b/src/core/net/dns_client.hpp @@ -61,6 +61,10 @@ #endif +#if !OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE +#error "OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE requires OPENTHREAD_CONFIG_TCP_ENABLE" +#endif + /** * This struct represents an opaque (and empty) type for a response to an address resolution DNS query. * @@ -136,11 +140,36 @@ class Client : public InstanceLocator, private NonCopyable enum Nat64Mode : uint8_t { kNat64Unspecified = OT_DNS_NAT64_UNSPECIFIED, ///< NAT64 mode is not specified. Use default NAT64 mode. - kNat64Allow = OT_DNS_NAT64_ALLOW, ///< Allow NAT64 address translation + kNat64Allow = OT_DNS_NAT64_ALLOW, ///< Allow NAT64 address translation. kNat64Disallow = OT_DNS_NAT64_DISALLOW, ///< Disallow NAT64 address translation. }; #endif + /** + * This enumeration type represents the service resolution mode. + * + */ + enum ServiceMode : uint8_t + { + kServiceModeUnspecified = OT_DNS_SERVICE_MODE_UNSPECIFIED, ///< Unspecified. Use default. + kServiceModeSrv = OT_DNS_SERVICE_MODE_SRV, ///< SRV record only. + kServiceModeTxt = OT_DNS_SERVICE_MODE_TXT, ///< TXT record only. + kServiceModeSrvTxt = OT_DNS_SERVICE_MODE_SRV_TXT, ///< SRV and TXT same msg. + kServiceModeSrvTxtSeparate = OT_DNS_SERVICE_MODE_SRV_TXT_SEPARATE, ///< SRV and TXT separate msgs. + kServiceModeSrvTxtOptimize = OT_DNS_SERVICE_MODE_SRV_TXT_OPTIMIZE, ///< Same msg first, if fail separate. + }; + + /** + * This enumeration type represents the DNS transport protocol selection. + * + */ + enum TransportProto : uint8_t + { + kDnsTransportUnspecified = OT_DNS_TRANSPORT_UNSPECIFIED, /// Dns transport is unspecified. + kDnsTransportUdp = OT_DNS_TRANSPORT_UDP, /// Dns query should be sent via UDP. + kDnsTransportTcp = OT_DNS_TRANSPORT_TCP, /// Dns query should be sent via TCP. + }; + /** * This is the default constructor for `QueryConfig` object. * @@ -191,12 +220,31 @@ class Client : public InstanceLocator, private NonCopyable */ Nat64Mode GetNat64Mode(void) const { return static_cast(mNat64Mode); } #endif + /** + * This method gets the service resolution mode. + * + * @returns The service resolution mode. + * + */ + ServiceMode GetServiceMode(void) const { return static_cast(mServiceMode); } + + /** + * This method gets the transport protocol. + * + * @returns The transport protocol. + * + */ + TransportProto GetTransportProto(void) const { return static_cast(mTransportProto); }; private: static constexpr uint32_t kDefaultResponseTimeout = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_RESPONSE_TIMEOUT; static constexpr uint16_t kDefaultServerPort = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_PORT; static constexpr uint8_t kDefaultMaxTxAttempts = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_MAX_TX_ATTEMPTS; static constexpr bool kDefaultRecursionDesired = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_RECURSION_DESIRED_FLAG; + static constexpr ServiceMode kDefaultServiceMode = + static_cast(OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVICE_MODE); + + static_assert(kDefaultServiceMode != kServiceModeUnspecified, "Invalid default service mode"); #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE static constexpr bool kDefaultNat64Allowed = OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_NAT64_ALLOWED; @@ -216,11 +264,16 @@ class Client : public InstanceLocator, private NonCopyable void SetResponseTimeout(uint32_t aResponseTimeout) { mResponseTimeout = aResponseTimeout; } void SetMaxTxAttempts(uint8_t aMaxTxAttempts) { mMaxTxAttempts = aMaxTxAttempts; } void SetRecursionFlag(RecursionFlag aFlag) { mRecursionFlag = static_cast(aFlag); } + void SetServiceMode(ServiceMode aMode) { mServiceMode = static_cast(aMode); } #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE void SetNat64Mode(Nat64Mode aMode) { mNat64Mode = static_cast(aMode); } #endif + void SetTransportProto(TransportProto aTransportProto) + { + mTransportProto = static_cast(aTransportProto); + } - void SetFrom(const QueryConfig &aConfig, const QueryConfig &aDefaultConfig); + void SetFrom(const QueryConfig *aConfig, const QueryConfig &aDefaultConfig); }; #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE @@ -257,21 +310,25 @@ class Client : public InstanceLocator, private NonCopyable void SelectSection(Section aSection, uint16_t &aOffset, uint16_t &aNumRecord) const; Error CheckForHostNameAlias(Section aSection, Name &aHostName) const; Error FindHostAddress(Section aSection, - const Name & aHostName, + const Name &aHostName, uint16_t aIndex, Ip6::Address &aAddress, - uint32_t & aTtl) const; + uint32_t &aTtl) const; #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE Error FindARecord(Section aSection, const Name &aHostName, uint16_t aIndex, ARecord &aARecord) const; #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE - Error FindServiceInfo(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const; + void InitServiceInfo(ServiceInfo &aServiceInfo) const; + Error ReadServiceInfo(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const; + Error ReadTxtRecord(Section aSection, const Name &aName, ServiceInfo &aServiceInfo) const; #endif + void PopulateFrom(const Message &aMessage); - Instance * mInstance; // The OpenThread instance. - Query * mQuery; // The associated query. + Instance *mInstance; // The OpenThread instance. + Query *mQuery; // The associated query. const Message *mMessage; // The response message. + Response *mNext; // The next response when we have related queries. uint16_t mAnswerOffset; // Answer section offset in `mMessage`. uint16_t mAnswerRecordCount; // Number of records in answer section. uint16_t mAdditionalOffset; // Additional data section offset in `mMessage`. @@ -486,9 +543,9 @@ class Client : public InstanceLocator, private NonCopyable * @retval kErrorNoBufs Either the label or name does not fit in the given buffers. * */ - Error GetServiceName(char * aLabelBuffer, + Error GetServiceName(char *aLabelBuffer, uint8_t aLabelBufferSize, - char * aNameBuffer, + char *aNameBuffer, uint16_t aNameBufferSize) const; /** @@ -604,9 +661,9 @@ class Client : public InstanceLocator, private NonCopyable * @retval kErrorInvalidState Cannot send query since Thread interface is not up. * */ - Error ResolveAddress(const char * aHostName, + Error ResolveAddress(const char *aHostName, AddressCallback aCallback, - void * aContext, + void *aContext, const QueryConfig *aConfig = nullptr); #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE @@ -631,9 +688,9 @@ class Client : public InstanceLocator, private NonCopyable * @retval kErrorInvalidState Cannot send query since Thread interface is not up, or there is no NAT64 prefix. * */ - Error ResolveIp4Address(const char * aHostName, + Error ResolveIp4Address(const char *aHostName, AddressCallback aCallback, - void * aContext, + void *aContext, const QueryConfig *aConfig = nullptr); #endif @@ -655,9 +712,9 @@ class Client : public InstanceLocator, private NonCopyable * @retval kErrorNoBufs Insufficient buffer to prepare and send query. * */ - Error Browse(const char * aServiceName, + Error Browse(const char *aServiceName, BrowseCallback aCallback, - void * aContext, + void *aContext, const QueryConfig *aConfig = nullptr); /** @@ -678,11 +735,11 @@ class Client : public InstanceLocator, private NonCopyable * @retval kErrorInvalidArgs @p aInstanceLabel is `nullptr`. * */ - Error ResolveService(const char * aInstanceLabel, - const char * aServiceName, + Error ResolveService(const char *aInstanceLabel, + const char *aServiceName, otDnsServiceCallback aCallback, - void * aContext, - const QueryConfig * aConfig = nullptr); + void *aContext, + const QueryConfig *aConfig = nullptr); #endif // OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE @@ -694,11 +751,24 @@ class Client : public InstanceLocator, private NonCopyable kIp4AddressQuery, // IPv4 Address resolution #endif #if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE - kBrowseQuery, // Browse (service instance enumeration). - kServiceQuery, // Service instance resolution. + kBrowseQuery, // Browse (service instance enumeration). + kServiceQuerySrvTxt, // Service instance resolution both SRV and TXT records. + kServiceQuerySrv, // Service instance resolution SRV record only. + kServiceQueryTxt, // Service instance resolution TXT record only. #endif + kNoQuery, }; +#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE + enum TcpState : uint8_t + { + kTcpUninitialized = 0, + kTcpConnecting, + kTcpConnectedIdle, + kTcpConnectedSending, + }; +#endif + union Callback { AddressCallback mAddressCallback; @@ -717,43 +787,75 @@ class Client : public InstanceLocator, private NonCopyable QueryType mQueryType; uint16_t mMessageId; Callback mCallback; - void * mCallbackContext; + void *mCallbackContext; TimeMilli mRetransmissionTime; QueryConfig mConfig; uint8_t mTransmissionCount; + Query *mMainQuery; + Query *mNextQuery; + Message *mSavedResponse; // Followed by the name (service, host, instance) encoded as a `Dns::Name`. }; static constexpr uint16_t kNameOffsetInQuery = sizeof(QueryInfo); - Error StartQuery(QueryInfo & aInfo, - const QueryConfig *aConfig, - const char * aLabel, - const char * aName, - void * aContext); + Error StartQuery(QueryInfo &aInfo, const char *aLabel, const char *aName, QueryType aSecondType = kNoQuery); Error AllocateQuery(const QueryInfo &aInfo, const char *aLabel, const char *aName, Query *&aQuery); void FreeQuery(Query &aQuery); void UpdateQuery(Query &aQuery, const QueryInfo &aInfo) { aQuery.Write(0, aInfo); } - void SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer); + Query &FindMainQuery(Query &aQuery); + Error SendQuery(Query &aQuery, QueryInfo &aInfo, bool aUpdateTimer); void FinalizeQuery(Query &aQuery, Error aError); - void FinalizeQuery(Response &Response, QueryType aType, Error aError); - static void GetCallback(const Query &aQuery, Callback &aCallback, void *&aContext); + void FinalizeQuery(Response &Response, Error aError); + static void GetQueryTypeAndCallback(const Query &aQuery, QueryType &aType, Callback &aCallback, void *&aContext); Error AppendNameFromQuery(const Query &aQuery, Message &aMessage); - Query * FindQueryById(uint16_t aMessageId); + Query *FindQueryById(uint16_t aMessageId); static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMsgInfo); - void ProcessResponse(const Message &aMessage); - Error ParseResponse(Response &aResponse, QueryType &aType, Error &aResponseError); - static void HandleTimer(Timer &aTimer); + void ProcessResponse(const Message &aResponseMessage); + Error ParseResponse(const Message &aResponseMessage, Query *&aQuery, Error &aResponseError); + bool CanFinalizeQuery(Query &aQuery); + void SaveQueryResponse(Query &aQuery, const Message &aResponseMessage); + Query *PopulateResponse(Response &aResponse, Query &aQuery, const Message &aResponseMessage); + void PrepareResponseAndFinalize(Query &aQuery, const Message &aResponseMessage, Response *aPrevResponse); void HandleTimer(void); + #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE - Error CheckAddressResponse(Response &aResponse, Error aResponseError) const; + Error ReplaceWithIp4Query(Query &aQuery); #endif +#if OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE + Error ReplaceWithSeparateSrvTxtQueries(Query &aQuery); +#endif + #if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE void UpdateDefaultConfigAddress(void); #endif - static const uint8_t kQuestionCount[]; - static const uint16_t *kQuestionRecordTypes[]; +#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE + static void HandleTcpEstablishedCallback(otTcpEndpoint *aEndpoint); + static void HandleTcpSendDoneCallback(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData); + static void HandleTcpDisconnectedCallback(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason); + static void HandleTcpReceiveAvailableCallback(otTcpEndpoint *aEndpoint, + size_t aBytesAvailable, + bool aEndOfStream, + size_t aBytesRemaining); + + void HandleTcpEstablished(otTcpEndpoint *aEndpoint); + void HandleTcpSendDone(otTcpEndpoint *aEndpoint, otLinkedBuffer *aData); + void HandleTcpDisconnected(otTcpEndpoint *aEndpoint, otTcpDisconnectedReason aReason); + void HandleTcpReceiveAvailable(otTcpEndpoint *aEndpoint, + size_t aBytesAvailable, + bool aEndOfStream, + size_t aBytesRemaining); + Error InitTcpSocket(void); + Error ReadFromLinkBuffer(const otLinkedBuffer *&aLinkedBuffer, + size_t &aOffset, + Message &aMessage, + uint16_t aLength); + void PrepareTcpMessage(Message &aMessage); +#endif // OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE + + static const uint8_t kQuestionCount[]; + static const uint16_t *const kQuestionRecordTypes[]; static const uint16_t kIp6AddressQueryRecordTypes[]; #if OPENTHREAD_CONFIG_DNS_CLIENT_NAT64_ENABLE @@ -764,10 +866,25 @@ class Client : public InstanceLocator, private NonCopyable static const uint16_t kServiceQueryRecordTypes[]; #endif + static constexpr uint16_t kUdpQueryMaxSize = 512; + + using RetryTimer = TimerMilliIn; + Ip6::Udp::Socket mSocket; - QueryList mQueries; - TimerMilli mTimer; - QueryConfig mDefaultConfig; + +#if OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_ENABLE + Ip6::Tcp::Endpoint mEndpoint; + + otLinkedBuffer mSendLink; + uint8_t mSendBufferBytes[OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE]; + uint8_t mReceiveBufferBytes[OPENTHREAD_CONFIG_DNS_CLIENT_OVER_TCP_QUERY_MAX_SIZE]; + + TcpState mTcpState; +#endif + + QueryList mMainQueries; + RetryTimer mTimer; + QueryConfig mDefaultConfig; #if OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE bool mUserDidSetDefaultAddress; #endif diff --git a/src/core/net/dns_dso.cpp b/src/core/net/dns_dso.cpp index a18b4593801..82ee2915a16 100644 --- a/src/core/net/dns_dso.cpp +++ b/src/core/net/dns_dso.cpp @@ -37,6 +37,7 @@ #include "common/instance.hpp" #include "common/locator_getters.hpp" #include "common/log.hpp" +#include "common/num_utils.hpp" #include "common/random.hpp" /** @@ -80,9 +81,9 @@ extern "C" void otPlatDsoHandleDisconnected(otPlatDsoConnection *aConnection, ot //--------------------------------------------------------------------------------------------------------------------- // Dso::Connection -Dso::Connection::Connection(Instance & aInstance, +Dso::Connection::Connection(Instance &aInstance, const Ip6::SockAddr &aPeerSockAddr, - Callbacks & aCallbacks, + Callbacks &aCallbacks, uint32_t aInactivityTimeout, uint32_t aKeepAliveInterval) : InstanceLocator(aInstance) @@ -321,7 +322,7 @@ void Dso::Connection::SetLongLivedOperation(bool aLongLivedOperation) Error Dso::Connection::SendRetryDelayMessage(uint32_t aDelay, Dns::Header::Response aResponseCode) { Error error = kErrorNone; - Message * message = nullptr; + Message *message = nullptr; RetryDelayTlv retryDelayTlv; MessageId messageId; @@ -411,7 +412,7 @@ Error Dso::Connection::SendKeepAliveMessage(MessageType aMessageType, MessageId // `kResponseMessage`. Error error = kErrorNone; - Message * message = nullptr; + Message *message = nullptr; KeepAliveTlv keepAliveTlv; switch (mState) @@ -479,9 +480,9 @@ Error Dso::Connection::SendKeepAliveMessage(MessageType aMessageType, MessageId return error; } -Error Dso::Connection::SendMessage(Message & aMessage, +Error Dso::Connection::SendMessage(Message &aMessage, MessageType aMessageType, - MessageId & aMessageId, + MessageId &aMessageId, Dns::Header::Response aResponseCode, uint32_t aResponseTimeout) { @@ -787,7 +788,7 @@ Error Dso::Connection::ReadPrimaryTlv(const Message &aMessage, Tlv::Type &aPrima } Error Dso::Connection::ProcessRequestOrUnidirectionalMessage(const Dns::Header &aHeader, - const Message & aMessage, + const Message &aMessage, Tlv::Type aPrimaryTlvType) { Error error = kErrorAbort; @@ -846,7 +847,7 @@ Error Dso::Connection::ProcessRequestOrUnidirectionalMessage(const Dns::Header & } Error Dso::Connection::ProcessResponseMessage(const Dns::Header &aHeader, - const Message & aMessage, + const Message &aMessage, Tlv::Type aPrimaryTlvType) { Error error = kErrorAbort; @@ -1029,7 +1030,7 @@ Error Dso::Connection::ProcessRetryDelayMessage(const Dns::Header &aHeader, cons void Dso::Connection::SendErrorResponse(const Dns::Header &aHeader, Dns::Header::Response aResponseCode) { - Message * response = NewMessage(); + Message *response = NewMessage(); Dns::Header header; VerifyOrExit(response != nullptr); @@ -1125,7 +1126,7 @@ void Dso::Connection::AdjustInactivityTimeout(uint32_t aNewTimeout) // five seconds or one quarter of the new inactivity // timeout, whichever is greater [RFC 8490 - 7.1.1]. - newExpiration = now + OT_MAX(kMinServerInactivityWaitTime, aNewTimeout / 4); + newExpiration = now + Max(kMinServerInactivityWaitTime, aNewTimeout / 4); } } @@ -1143,7 +1144,7 @@ uint32_t Dso::Connection::CalculateServerInactivityWaitTime(void) const OT_ASSERT(mInactivity.IsUsed()); - return OT_MAX(mInactivity.GetInterval() * 2, kMinServerInactivityWaitTime); + return Max(mInactivity.GetInterval() * 2, kMinServerInactivityWaitTime); } void Dso::Connection::ResetTimeouts(bool aIsKeepAliveMessage) @@ -1219,12 +1220,12 @@ TimeMilli Dso::Connection::GetNextFireTime(TimeMilli aNow) const case kStateConnectedButSessionless: case kStateEstablishingSession: case kStateSessionEstablished: - nextTime = OT_MIN(nextTime, mPendingRequests.GetNextFireTime(aNow)); + nextTime = Min(nextTime, mPendingRequests.GetNextFireTime(aNow)); if (mKeepAlive.IsUsed()) { VerifyOrExit(mKeepAlive.GetExpirationTime() > aNow, nextTime = aNow); - nextTime = OT_MIN(nextTime, mKeepAlive.GetExpirationTime()); + nextTime = Min(nextTime, mKeepAlive.GetExpirationTime()); } if (mInactivity.IsUsed() && mPendingRequests.IsEmpty() && !mLongLivedOperation) @@ -1234,7 +1235,7 @@ TimeMilli Dso::Connection::GetNextFireTime(TimeMilli aNow) const // active long-lived operation. VerifyOrExit(mInactivity.GetExpirationTime() > aNow, nextTime = aNow); - nextTime = OT_MIN(nextTime, mInactivity.GetExpirationTime()); + nextTime = Min(nextTime, mInactivity.GetExpirationTime()); } break; @@ -1311,7 +1312,7 @@ void Dso::Connection::HandleTimer(TimeMilli aNow, TimeMilli &aNextTime) } exit: - aNextTime = OT_MIN(aNextTime, GetNextFireTime(aNow)); + aNextTime = Min(aNextTime, GetNextFireTime(aNow)); SignalAnyStateChange(); } @@ -1407,10 +1408,7 @@ Error Dso::Connection::PendingRequests::Add(MessageId aMessageId, Tlv::Type aPri return error; } -void Dso::Connection::PendingRequests::Remove(MessageId aMessageId) -{ - mRequests.RemoveMatching(aMessageId); -} +void Dso::Connection::PendingRequests::Remove(MessageId aMessageId) { mRequests.RemoveMatching(aMessageId); } bool Dso::Connection::PendingRequests::HasAnyTimedOut(TimeMilli aNow) const { @@ -1435,7 +1433,7 @@ TimeMilli Dso::Connection::PendingRequests::GetNextFireTime(TimeMilli aNow) cons for (const Entry &entry : mRequests) { VerifyOrExit(entry.mTimeout > aNow, nextTime = aNow); - nextTime = OT_MIN(entry.mTimeout, nextTime); + nextTime = Min(entry.mTimeout, nextTime); } exit: @@ -1448,7 +1446,7 @@ TimeMilli Dso::Connection::PendingRequests::GetNextFireTime(TimeMilli aNow) cons Dso::Dso(Instance &aInstance) : InstanceLocator(aInstance) , mAcceptHandler(nullptr) - , mTimer(aInstance, HandleTimer) + , mTimer(aInstance) { } @@ -1458,10 +1456,7 @@ void Dso::StartListening(AcceptHandler aAcceptHandler) otPlatDsoEnableListening(&GetInstance(), true); } -void Dso::StopListening(void) -{ - otPlatDsoEnableListening(&GetInstance(), false); -} +void Dso::StopListening(void) { otPlatDsoEnableListening(&GetInstance(), false); } Dso::Connection *Dso::FindClientConnection(const Ip6::SockAddr &aPeerSockAddr) { @@ -1487,11 +1482,6 @@ Dso::Connection *Dso::AcceptConnection(const Ip6::SockAddr &aPeerSockAddr) return connection; } -void Dso::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void Dso::HandleTimer(void) { TimeMilli now = TimerMilli::GetNow(); diff --git a/src/core/net/dns_dso.hpp b/src/core/net/dns_dso.hpp index d9ff458cd3d..6ab6800ce18 100644 --- a/src/core/net/dns_dso.hpp +++ b/src/core/net/dns_dso.hpp @@ -43,6 +43,7 @@ #include "common/locator.hpp" #include "common/message.hpp" #include "common/non_copyable.hpp" +#include "common/num_utils.hpp" #include "common/timer.hpp" #include "net/dns_types.hpp" #include "net/socket.hpp" @@ -305,7 +306,7 @@ class Dso : public InstanceLocator, private NonCopyable * @retval kErrorAbort Fatal error (misbehavior by peer). This triggers aborting of the connection. * */ - typedef Error (&ProcessRequestMessage)(Connection & aConnection, + typedef Error (&ProcessRequestMessage)(Connection &aConnection, MessageId aMessageId, const Message &aMessage, Tlv::Type aPrimaryTlvType); @@ -328,7 +329,7 @@ class Dso : public InstanceLocator, private NonCopyable * @p aPrimaryTlvType is not known in a unidirectional message, it is a fatal error. * */ - typedef Error (&ProcessUnidirectionalMessage)(Connection & aConnection, + typedef Error (&ProcessUnidirectionalMessage)(Connection &aConnection, const Message &aMessage, Tlv::Type aPrimaryTlvType); @@ -358,9 +359,9 @@ class Dso : public InstanceLocator, private NonCopyable * @retval kErrorAbort Fatal error (misbehavior by peer). This triggers aborting of the connection. * */ - typedef Error (&ProcessResponseMessage)(Connection & aConnection, + typedef Error (&ProcessResponseMessage)(Connection &aConnection, const Dns::Header &aHeader, - const Message & aMessage, + const Message &aMessage, Tlv::Type aResponseTlvType, Tlv::Type aRequestTlvType); /** @@ -411,9 +412,9 @@ class Dso : public InstanceLocator, private NonCopyable * @param[in] aKeepAliveInterval The Keep Alive timeout interval (in msec). * */ - Connection(Instance & aInstance, + Connection(Instance &aInstance, const Ip6::SockAddr &aPeerSockAddr, - Callbacks & aCallbacks, + Callbacks &aCallbacks, uint32_t aInactivityTimeout = kDefaultTimeout, uint32_t aKeepAliveInterval = kDefaultTimeout); @@ -547,7 +548,7 @@ class Dso : public InstanceLocator, private NonCopyable * @retval kErrorNoBufs Failed to allocate new buffer to prepare the message (append header or padding). * */ - Error SendRequestMessage(Message & aMessage, + Error SendRequestMessage(Message &aMessage, MessageId &aMessageId, uint32_t aResponseTimeout = kResponseTimeout); @@ -773,7 +774,7 @@ class Dso : public InstanceLocator, private NonCopyable // If it is not infinite, limit the interval to `kMaxInterval`. // The max limit ensures that even twice the interval is less // than max OpenThread timer duration. - return (aInterval == kInfinite) ? aInterval : OT_MIN(aInterval, kMaxInterval); + return (aInterval == kInfinite) ? aInterval : Min(aInterval, kMaxInterval); } uint32_t mInterval; @@ -791,15 +792,15 @@ class Dso : public InstanceLocator, private NonCopyable void MarkAsDisconnected(void); Error SendKeepAliveMessage(MessageType aMessageType, MessageId aResponseId); - Error SendMessage(Message & aMessage, + Error SendMessage(Message &aMessage, MessageType aMessageType, - MessageId & aMessageId, + MessageId &aMessageId, Dns::Header::Response aResponseCode = Dns::Header::kResponseSuccess, uint32_t aResponseTimeout = kResponseTimeout); void HandleReceive(Message &aMessage); Error ReadPrimaryTlv(const Message &aMessage, Tlv::Type &aPrimaryTlvType) const; Error ProcessRequestOrUnidirectionalMessage(const Dns::Header &aHeader, - const Message & aMessage, + const Message &aMessage, Tlv::Type aPrimaryTlvType); Error ProcessResponseMessage(const Dns::Header &aHeader, const Message &aMessage, Tlv::Type aPrimaryTlvType); Error ProcessKeepAliveMessage(const Dns::Header &aHeader, const Message &aMessage); @@ -819,8 +820,8 @@ class Dso : public InstanceLocator, private NonCopyable static const char *MessageTypeToString(MessageType aMessageType); static const char *DisconnectReasonToString(DisconnectReason aReason); - Connection * mNext; - Callbacks & mCallbacks; + Connection *mNext; + Callbacks &mCallbacks; Ip6::SockAddr mPeerSockAddr; State mState; MessageId mNextMessageId; @@ -952,13 +953,14 @@ class Dso : public InstanceLocator, private NonCopyable Connection *AcceptConnection(const Ip6::SockAddr &aPeerSockAddr); - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); + void HandleTimer(void); + + using DsoTimer = TimerMilliIn; AcceptHandler mAcceptHandler; LinkedList mClientConnections; LinkedList mServerConnections; - TimerMilli mTimer; + DsoTimer mTimer; }; } // namespace Dns diff --git a/examples/platforms/cc2538/system.c b/src/core/net/dns_platform.cpp similarity index 68% rename from examples/platforms/cc2538/system.c rename to src/core/net/dns_platform.cpp index 0d1cd63de79..ef48077b6df 100644 --- a/examples/platforms/cc2538/system.c +++ b/src/core/net/dns_platform.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The OpenThread Authors. + * Copyright (c) 2023, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,39 +28,25 @@ /** * @file - * @brief - * This file includes the platform-specific initializers. + * This file implements the DNS platform callbacks into OpenThread. */ -#include "platform-cc2538.h" -#include -otInstance *sInstance; +#include "openthread-core-config.h" -void otSysInit(int argc, char *argv[]) -{ - OT_UNUSED_VARIABLE(argc); - OT_UNUSED_VARIABLE(argv); +#include +#include -#if OPENTHREAD_CONFIG_ENABLE_DEBUG_UART - cc2538DebugUartInit(); -#endif - cc2538AlarmInit(); - cc2538RandomInit(); - cc2538RadioInit(); -} +#include "common/code_utils.hpp" +#include "common/instance.hpp" +#include "common/message.hpp" -bool otSysPseudoResetWasRequested(void) -{ - return false; -} +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE -void otSysProcessDrivers(otInstance *aInstance) -{ - sInstance = aInstance; - - // should sleep and wait for interrupts here +using namespace ot; - cc2538UartProcess(); - cc2538RadioProcess(aInstance); - cc2538AlarmProcess(aInstance); +void otPlatDnsUpstreamQueryDone(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn, otMessage *aResponse) +{ + return AsCoreType(aInstance).Get().OnUpstreamQueryDone(AsCoreType(aTxn), + AsCoreTypePtr(aResponse)); } +#endif diff --git a/src/core/net/dns_types.cpp b/src/core/net/dns_types.cpp index e65cd035a82..7c3241e95cb 100644 --- a/src/core/net/dns_types.cpp +++ b/src/core/net/dns_types.cpp @@ -36,6 +36,7 @@ #include "common/code_utils.hpp" #include "common/debug.hpp" #include "common/instance.hpp" +#include "common/num_utils.hpp" #include "common/random.hpp" #include "common/string.hpp" @@ -182,7 +183,7 @@ Error Name::AppendMultipleLabels(const char *aLabels, uint8_t aLength, Message & { ch = index < aLength ? aLabels[index] : static_cast(kNullChar); - if ((ch == kNullChar) || (ch == kLabelSeperatorChar)) + if ((ch == kNullChar) || (ch == kLabelSeparatorChar)) { uint8_t labelLength = static_cast(index - labelStartIndex); @@ -327,14 +328,14 @@ Error Name::ReadName(const Message &aMessage, uint16_t &aOffset, char *aNameBuff if (!firstLabel) { - *aNameBuffer++ = kLabelSeperatorChar; + *aNameBuffer++ = kLabelSeparatorChar; aNameBufferSize--; // No need to check if we have reached end of the name buffer // here since `iterator.ReadLabel()` would verify it. } - labelLength = static_cast(OT_MIN(static_cast(kMaxLabelSize), aNameBufferSize)); + labelLength = static_cast(Min(static_cast(kMaxLabelSize), aNameBufferSize)); SuccessOrExit(error = iterator.ReadLabel(aNameBuffer, labelLength, /* aAllowDotCharInLabel */ false)); aNameBuffer += labelLength; aNameBufferSize -= labelLength; @@ -344,7 +345,7 @@ Error Name::ReadName(const Message &aMessage, uint16_t &aOffset, char *aNameBuff case kErrorNotFound: // We reach the end of name successfully. Always add a terminating dot // at the end. - *aNameBuffer++ = kLabelSeperatorChar; + *aNameBuffer++ = kLabelSeparatorChar; aNameBufferSize--; VerifyOrExit(aNameBufferSize >= sizeof(uint8_t), error = kErrorNoBufs); *aNameBuffer = kNullChar; @@ -381,7 +382,7 @@ Error Name::CompareName(const Message &aMessage, uint16_t &aOffset, const char * LabelIterator iterator(aMessage, aOffset); bool matches = true; - if (*aName == kLabelSeperatorChar) + if (*aName == kLabelSeparatorChar) { aName++; VerifyOrExit(*aName == kNullChar, error = kErrorInvalidArgs); @@ -521,6 +522,7 @@ Error Name::LabelIterator::GetNextLabel(void) // specify an offset value from the start of the DNS header. uint16_t pointerValue; + uint16_t nextLabelOffset; SuccessOrExit(error = mMessage.Read(mNextLabelOffset, pointerValue)); @@ -531,7 +533,9 @@ Error Name::LabelIterator::GetNextLabel(void) // `mMessage.GetOffset()` must point to the start of the // DNS header. - mNextLabelOffset = mMessage.GetOffset() + (HostSwap16(pointerValue) & kPointerLabelOffsetMask); + nextLabelOffset = mMessage.GetOffset() + (HostSwap16(pointerValue) & kPointerLabelOffsetMask); + VerifyOrExit(nextLabelOffset < mNextLabelOffset, error = kErrorParse); + mNextLabelOffset = nextLabelOffset; // Go back through the `while(true)` loop to get the next label. } @@ -557,7 +561,7 @@ Error Name::LabelIterator::ReadLabel(char *aLabelBuffer, uint8_t &aLabelLength, if (!aAllowDotCharInLabel) { - VerifyOrExit(StringFind(aLabelBuffer, kLabelSeperatorChar) == nullptr, error = kErrorParse); + VerifyOrExit(StringFind(aLabelBuffer, kLabelSeparatorChar) == nullptr, error = kErrorParse); } exit: @@ -594,7 +598,7 @@ bool Name::LabelIterator::CompareLabel(const char *&aName, bool aIsSingleLabel) matches = (*aName == kNullChar); - if (!aIsSingleLabel && (*aName == kLabelSeperatorChar)) + if (!aIsSingleLabel && (*aName == kLabelSeparatorChar)) { matches = true; aName++; @@ -637,13 +641,13 @@ bool Name::IsSubDomainOf(const char *aName, const char *aDomain) uint16_t nameLength = StringLength(aName, kMaxNameLength); uint16_t domainLength = StringLength(aDomain, kMaxNameLength); - if (nameLength > 0 && aName[nameLength - 1] == kLabelSeperatorChar) + if (nameLength > 0 && aName[nameLength - 1] == kLabelSeparatorChar) { nameEndsWithDot = true; --nameLength; } - if (domainLength > 0 && aDomain[domainLength - 1] == kLabelSeperatorChar) + if (domainLength > 0 && aDomain[domainLength - 1] == kLabelSeparatorChar) { domainEndsWithDot = true; --domainLength; @@ -655,7 +659,7 @@ bool Name::IsSubDomainOf(const char *aName, const char *aDomain) if (nameLength > domainLength) { - VerifyOrExit(aName[-1] == kLabelSeperatorChar); + VerifyOrExit(aName[-1] == kLabelSeparatorChar); } // This method allows either `aName` or `aDomain` to include or @@ -683,6 +687,11 @@ bool Name::IsSubDomainOf(const char *aName, const char *aDomain) return match; } +bool Name::IsSameDomain(const char *aDomain1, const char *aDomain2) +{ + return IsSubDomainOf(aDomain1, aDomain2) && IsSubDomainOf(aDomain2, aDomain1); +} + Error ResourceRecord::ParseRecords(const Message &aMessage, uint16_t &aOffset, uint16_t aNumRecords) { Error error = kErrorNone; @@ -735,11 +744,11 @@ Error ResourceRecord::FindRecord(const Message &aMessage, uint16_t &aOffset, uin return error; } -Error ResourceRecord::FindRecord(const Message & aMessage, - uint16_t & aOffset, +Error ResourceRecord::FindRecord(const Message &aMessage, + uint16_t &aOffset, uint16_t aNumRecords, uint16_t aIndex, - const Name & aName, + const Name &aName, uint16_t aType, ResourceRecord &aRecord, uint16_t aMinRecordSize) @@ -794,8 +803,8 @@ Error ResourceRecord::FindRecord(const Message & aMessage, return error; } -Error ResourceRecord::ReadRecord(const Message & aMessage, - uint16_t & aOffset, +Error ResourceRecord::ReadRecord(const Message &aMessage, + uint16_t &aOffset, uint16_t aType, ResourceRecord &aRecord, uint16_t aMinRecordSize) @@ -828,9 +837,9 @@ Error ResourceRecord::ReadRecord(const Message & aMessage, } Error ResourceRecord::ReadName(const Message &aMessage, - uint16_t & aOffset, + uint16_t &aOffset, uint16_t aStartOffset, - char * aNameBuffer, + char *aNameBuffer, uint16_t aNameBufferSize, bool aSkipRecord) const { @@ -918,7 +927,7 @@ Error TxtEntry::Iterator::GetNextEntry(TxtEntry &aEntry) uint8_t length; uint8_t index; const char *cur; - char * keyBuffer = GetKeyBuffer(); + char *keyBuffer = GetKeyBuffer(); static_assert(sizeof(mChar) == TxtEntry::kMaxKeyLength + 1, "KeyBuffer cannot fit the max key length"); @@ -1070,10 +1079,7 @@ bool AaaaRecord::IsValid(void) const return GetType() == Dns::ResourceRecord::kTypeAaaa && GetSize() == sizeof(*this); } -bool KeyRecord::IsValid(void) const -{ - return GetType() == Dns::ResourceRecord::kTypeKey; -} +bool KeyRecord::IsValid(void) const { return GetType() == Dns::ResourceRecord::kTypeKey; } #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE void Ecdsa256KeyRecord::Init(void) @@ -1094,16 +1100,75 @@ bool SigRecord::IsValid(void) const return GetType() == Dns::ResourceRecord::kTypeSig && GetLength() >= sizeof(*this) - sizeof(ResourceRecord); } +void LeaseOption::InitAsShortVariant(uint32_t aLeaseInterval) +{ + SetOptionCode(kUpdateLease); + SetOptionLength(kShortLength); + SetLeaseInterval(aLeaseInterval); +} + +void LeaseOption::InitAsLongVariant(uint32_t aLeaseInterval, uint32_t aKeyLeaseInterval) +{ + SetOptionCode(kUpdateLease); + SetOptionLength(kLongLength); + SetLeaseInterval(aLeaseInterval); + SetKeyLeaseInterval(aKeyLeaseInterval); +} + bool LeaseOption::IsValid(void) const { - return GetLeaseInterval() <= GetKeyLeaseInterval(); + bool isValid = false; + + VerifyOrExit((GetOptionLength() == kShortLength) || (GetOptionLength() >= kLongLength)); + isValid = (GetLeaseInterval() <= GetKeyLeaseInterval()); + +exit: + return isValid; +} + +Error LeaseOption::ReadFrom(const Message &aMessage, uint16_t aOffset, uint16_t aLength) +{ + Error error = kErrorNone; + uint16_t endOffset; + + VerifyOrExit(static_cast(aOffset) + aLength <= aMessage.GetLength(), error = kErrorParse); + + endOffset = aOffset + aLength; + + while (aOffset < endOffset) + { + uint16_t size; + + SuccessOrExit(error = aMessage.Read(aOffset, this, sizeof(Option))); + + VerifyOrExit(aOffset + GetSize() <= endOffset, error = kErrorParse); + + size = static_cast(GetSize()); + + if (GetOptionCode() == kUpdateLease) + { + VerifyOrExit(GetOptionLength() >= kShortLength, error = kErrorParse); + + IgnoreError(aMessage.Read(aOffset, this, Min(size, static_cast(sizeof(LeaseOption))))); + VerifyOrExit(IsValid(), error = kErrorParse); + + ExitNow(); + } + + aOffset += size; + } + + error = kErrorNotFound; + +exit: + return error; } Error PtrRecord::ReadPtrName(const Message &aMessage, - uint16_t & aOffset, - char * aLabelBuffer, + uint16_t &aOffset, + char *aLabelBuffer, uint8_t aLabelBufferSize, - char * aNameBuffer, + char *aNameBuffer, uint16_t aNameBufferSize) const { Error error = kErrorNone; @@ -1129,17 +1194,18 @@ Error PtrRecord::ReadPtrName(const Message &aMessage, } Error TxtRecord::ReadTxtData(const Message &aMessage, - uint16_t & aOffset, - uint8_t * aTxtBuffer, - uint16_t & aTxtBufferSize) const + uint16_t &aOffset, + uint8_t *aTxtBuffer, + uint16_t &aTxtBufferSize) const { Error error = kErrorNone; + SuccessOrExit(error = aMessage.Read(aOffset, aTxtBuffer, Min(GetLength(), aTxtBufferSize))); + aOffset += GetLength(); + VerifyOrExit(GetLength() <= aTxtBufferSize, error = kErrorNoBufs); - SuccessOrExit(error = aMessage.Read(aOffset, aTxtBuffer, GetLength())); - VerifyOrExit(VerifyTxtData(aTxtBuffer, GetLength(), /* aAllowEmpty */ true), error = kErrorParse); aTxtBufferSize = GetLength(); - aOffset += GetLength(); + VerifyOrExit(VerifyTxtData(aTxtBuffer, aTxtBufferSize, /* aAllowEmpty */ true), error = kErrorParse); exit: return error; diff --git a/src/core/net/dns_types.hpp b/src/core/net/dns_types.hpp index 2fa3f1a7b20..19b9061ea0b 100644 --- a/src/core/net/dns_types.hpp +++ b/src/core/net/dns_types.hpp @@ -514,7 +514,7 @@ class Name : public Clearable */ static constexpr uint8_t kMaxLabelLength = kMaxLabelSize - 1; - static constexpr char kLabelSeperatorChar = '.'; + static constexpr char kLabelSeparatorChar = '.'; /** * This enumeration represents the name type. @@ -990,6 +990,20 @@ class Name : public Clearable */ static bool IsSubDomainOf(const char *aName, const char *aDomain); + /** + * This static method tests if the two DNS name are the same domain. + * + * Both @p aDomain1 and @p aDomain2 can end without dot ('.'). + * + * @param[in] aDomain1 The dot-separated name. + * @param[in] aDomain2 The dot-separated domain. + * + * @retval TRUE If the two DNS names are the same domain. + * @retval FALSE If the two DNS names are not the same domain. + * + */ + static bool IsSameDomain(const char *aDomain1, const char *aDomain2); + private: // The first 2 bits of the encoded label specifies label type. // @@ -1006,7 +1020,7 @@ class Name : public Clearable static constexpr uint16_t kPointerLabelTypeUint16 = 0xc000; // Pointer label type mask (first 2 bits). static constexpr uint16_t kPointerLabelOffsetMask = 0x3fff; // Mask for offset in a pointer label (lower 14 bits). - static constexpr bool kIsSingleLabel = true; // Used in `LabelIterator::CompareLable()`. + static constexpr bool kIsSingleLabel = true; // Used in `LabelIterator::CompareLabel()`. struct LabelIterator { @@ -1042,7 +1056,7 @@ class Name : public Clearable { } - const char * mString; // String containing the name or `nullptr` if name is not from string. + const char *mString; // String containing the name or `nullptr` if name is not from string. const Message *mMessage; // Message containing the encoded name, or `nullptr` if `Name` is not from message. uint16_t mOffset; // Offset in `mMessage` to the start of name (used when name is from `mMessage`). }; @@ -1119,7 +1133,7 @@ class TxtEntry : public otDnsTxtEntry uint16_t GetTxtDataPosition(void) const { return mData[kIndexTxtPosition]; } void SetTxtDataPosition(uint16_t aValue) { mData[kIndexTxtPosition] = aValue; } void IncreaseTxtDataPosition(uint16_t aIncrement) { mData[kIndexTxtPosition] += aIncrement; } - char * GetKeyBuffer(void) { return mChar; } + char *GetKeyBuffer(void) { return mChar; } const char *GetTxtDataEnd(void) const { return GetTxtData() + GetTxtDataLength(); } }; @@ -1255,7 +1269,7 @@ class ResourceRecord * @returns TRUE if the resources records matches @p aType and @p aClass, FALSE otherwise. * */ - bool Matches(uint16_t aType, uint16_t aClass = kClassInternet) + bool Matches(uint16_t aType, uint16_t aClass = kClassInternet) const { return (mType == HostSwap16(aType)) && (mClass == HostSwap16(aClass)); } @@ -1405,11 +1419,11 @@ class ResourceRecord */ template static Error FindRecord(const Message &aMessage, - uint16_t & aOffset, + uint16_t &aOffset, uint16_t aNumRecords, uint16_t aIndex, - const Name & aName, - RecordType & aRecord) + const Name &aName, + RecordType &aRecord) { return FindRecord(aMessage, aOffset, aNumRecords, aIndex, aName, RecordType::kType, aRecord, sizeof(RecordType)); @@ -1456,9 +1470,9 @@ class ResourceRecord protected: Error ReadName(const Message &aMessage, - uint16_t & aOffset, + uint16_t &aOffset, uint16_t aStartOffset, - char * aNameBuffer, + char *aNameBuffer, uint16_t aNameBufferSize, bool aSkipRecord) const; Error SkipRecord(const Message &aMessage, uint16_t &aOffset) const; @@ -1466,17 +1480,17 @@ class ResourceRecord private: static constexpr uint16_t kType = kTypeAny; // This is intended for used by `ReadRecord()` only. - static Error FindRecord(const Message & aMessage, - uint16_t & aOffset, + static Error FindRecord(const Message &aMessage, + uint16_t &aOffset, uint16_t aNumRecords, uint16_t aIndex, - const Name & aName, + const Name &aName, uint16_t aType, ResourceRecord &aRecord, uint16_t aMinRecordSize); - static Error ReadRecord(const Message & aMessage, - uint16_t & aOffset, + static Error ReadRecord(const Message &aMessage, + uint16_t &aOffset, uint16_t aType, ResourceRecord &aRecord, uint16_t aMinRecordSize); @@ -1574,8 +1588,8 @@ class CnameRecord : public ResourceRecord * */ Error ReadCanonicalName(const Message &aMessage, - uint16_t & aOffset, - char * aNameBuffer, + uint16_t &aOffset, + char *aNameBuffer, uint16_t aNameBufferSize) const { return ResourceRecord::ReadName(aMessage, aOffset, /* aStartOffset */ aOffset - sizeof(CnameRecord), @@ -1661,10 +1675,10 @@ class PtrRecord : public ResourceRecord * */ Error ReadPtrName(const Message &aMessage, - uint16_t & aOffset, - char * aLabelBuffer, + uint16_t &aOffset, + char *aLabelBuffer, uint8_t aLabelBufferSize, - char * aNameBuffer, + char *aNameBuffer, uint16_t aNameBufferSize) const; } OT_TOOL_PACKED_END; @@ -1692,7 +1706,8 @@ class TxtRecord : public ResourceRecord /** * This method parses and reads the TXT record data from a message. * - * This method also checks if the TXT data is well-formed by calling `VerifyTxtData()`. + * This method also checks if the TXT data is well-formed by calling `VerifyTxtData()` when it is successfully + * read. * * @param[in] aMessage The message to read from. * @param[in,out] aOffset On input, the offset in @p aMessage to start of TXT record data. @@ -1705,7 +1720,9 @@ class TxtRecord : public ResourceRecord * @retval kErrorNone The TXT data was read successfully. @p aOffset, @p aTxtBuffer and @p aTxtBufferSize * are updated. * @retval kErrorParse The TXT record in @p aMessage could not be parsed (invalid format). - * @retval kErrorNoBufs TXT data could not fit in @p aTxtBufferSize bytes. + * @retval kErrorNoBufs TXT data could not fit in @p aTxtBufferSize bytes. TXT data is still partially read + * into @p aTxtBuffer up to its size and @p aOffset is updated to skip over the full + * TXT record. * */ Error ReadTxtData(const Message &aMessage, uint16_t &aOffset, uint8_t *aTxtBuffer, uint16_t &aTxtBufferSize) const; @@ -1863,8 +1880,8 @@ class SrvRecord : public ResourceRecord * */ Error ReadTargetHostName(const Message &aMessage, - uint16_t & aOffset, - char * aNameBuffer, + uint16_t &aOffset, + char *aNameBuffer, uint16_t aNameBufferSize) const { return ResourceRecord::ReadName(aMessage, aOffset, /* aStartOffset */ aOffset - sizeof(SrvRecord), aNameBuffer, @@ -2344,7 +2361,7 @@ class OptRecord : public ResourceRecord * @param[in] aExtendedResponse The upper 8-bit of the extended 12-bit Response Code. * */ - void SetExtnededResponseCode(uint8_t aExtendedResponse) { GetTtlByteAt(kExtRCodeByteIndex) = aExtendedResponse; } + void SetExtendedResponseCode(uint8_t aExtendedResponse) { GetTtlByteAt(kExtRCodeByteIndex) = aExtendedResponse; } /** * This method gets the Version field. @@ -2475,23 +2492,40 @@ OT_TOOL_PACKED_BEGIN class LeaseOption : public Option { public: - static constexpr uint16_t kOptionLength = sizeof(uint32_t) + sizeof(uint32_t); ///< lease and key lease values + /** + * This method initializes the Update Lease Option using the short variant format which contains lease interval + * only. + * + * @param[in] aLeaseInterval The lease interval in seconds. + * + */ + void InitAsShortVariant(uint32_t aLeaseInterval); /** - * This method initialize the Update Lease Option by setting the Option Code and Option Length. + * This method initializes the Update Lease Option using the long variant format which contains both lease and + * key lease intervals. * - * The lease and key lease intervals remain unchanged/uninitialized. + * @param[in] aLeaseInterval The lease interval in seconds. + * @param[in] aKeyLeaseInterval The key lease interval in seconds. * */ - void Init(void) - { - SetOptionCode(kUpdateLease); - SetOptionLength(kOptionLength); - } + void InitAsLongVariant(uint32_t aLeaseInterval, uint32_t aKeyLeaseInterval); + + /** + * This method indicates whether or not the Update Lease Option follows the short variant format which contains + * only the lease interval. + * + * @retval TRUE The Update Lease Option follows the short variant format. + * @retval FALSE The Update Lease Option follows the long variant format. + * + */ + bool IsShortVariant(void) const { return (GetOptionLength() == kShortLength); } /** * This method tells whether this is a valid Lease Option. * + * This method validates that option follows either short or long variant format. + * * @returns TRUE if this is a valid Lease Option, FALSE if not a valid Lease Option. * */ @@ -2505,31 +2539,43 @@ class LeaseOption : public Option */ uint32_t GetLeaseInterval(void) const { return HostSwap32(mLeaseInterval); } - /** - * This method sets the Update Lease OPT record's lease interval value. - * - * @param[in] aLeaseInterval The lease interval value. - * - */ - void SetLeaseInterval(uint32_t aLeaseInterval) { mLeaseInterval = HostSwap32(aLeaseInterval); } - /** * This method returns the Update Lease OPT record's key lease interval value. * + * If the Update Lease Option follows the short variant format the lease interval is returned as key lease interval. + * * @returns The key lease interval value (in seconds). * */ - uint32_t GetKeyLeaseInterval(void) const { return HostSwap32(mKeyLeaseInterval); } + uint32_t GetKeyLeaseInterval(void) const + { + return IsShortVariant() ? GetLeaseInterval() : HostSwap32(mKeyLeaseInterval); + } /** - * This method sets the Update Lease OPT record's key lease interval value. + * This method searches among the Options is a given message and reads and validates the Update Lease Option if + * found. + * + * This method reads the Update Lease Option whether it follows the short or long variant formats. * - * @param[in] aKeyLeaseInterval The key lease interval value (in seconds). + * @param[in] aMessage The message to read the Option from. + * @param[in] aOffset Offset in @p aMessage to the start of Options (start of OPT Record data). + * @param[in] aLength Length of Option data in OPT record. + * + * @retval kErrorNone Successfully read and validated the Update Lease Option from @p aMessage. + * @retval kErrorNotFound Did not find any Update Lease Option. + * @retval kErrorParse Failed to parse the Options. * */ - void SetKeyLeaseInterval(uint32_t aKeyLeaseInterval) { mKeyLeaseInterval = HostSwap32(aKeyLeaseInterval); } + Error ReadFrom(const Message &aMessage, uint16_t aOffset, uint16_t aLength); private: + static constexpr uint16_t kShortLength = sizeof(uint32_t); // lease only. + static constexpr uint16_t kLongLength = sizeof(uint32_t) + sizeof(uint32_t); // lease and key lease values + + void SetLeaseInterval(uint32_t aLeaseInterval) { mLeaseInterval = HostSwap32(aLeaseInterval); } + void SetKeyLeaseInterval(uint32_t aKeyLeaseInterval) { mKeyLeaseInterval = HostSwap32(aKeyLeaseInterval); } + uint32_t mLeaseInterval; uint32_t mKeyLeaseInterval; } OT_TOOL_PACKED_END; diff --git a/src/core/net/dnssd_server.cpp b/src/core/net/dnssd_server.cpp index c996c153ab9..8f93096ae42 100644 --- a/src/core/net/dnssd_server.cpp +++ b/src/core/net/dnssd_server.cpp @@ -35,6 +35,8 @@ #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE +#include + #include "common/array.hpp" #include "common/as_core_type.hpp" #include "common/code_utils.hpp" @@ -52,10 +54,11 @@ namespace ServiceDiscovery { RegisterLogModule("DnssdServer"); -const char Server::kDnssdProtocolUdp[] = "_udp"; -const char Server::kDnssdProtocolTcp[] = "_tcp"; -const char Server::kDnssdSubTypeLabel[] = "._sub."; -const char Server::kDefaultDomainName[] = "default.service.arpa."; +const char Server::kDnssdProtocolUdp[] = "_udp"; +const char Server::kDnssdProtocolTcp[] = "_tcp"; +const char Server::kDnssdSubTypeLabel[] = "._sub."; +const char Server::kDefaultDomainName[] = "default.service.arpa."; +const char *Server::kBlockedDomains[] = {"ipv4only.arpa."}; Server::Server(Instance &aInstance) : InstanceLocator(aInstance) @@ -63,7 +66,11 @@ Server::Server(Instance &aInstance) , mQueryCallbackContext(nullptr) , mQuerySubscribe(nullptr) , mQueryUnsubscribe(nullptr) - , mTimer(aInstance, Server::HandleTimer) +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + , mEnableUpstreamQuery(false) +#endif + , mTimer(aInstance) + , mTestMode(kTestModeDisabled) { mCounters.Clear(); } @@ -75,7 +82,7 @@ Error Server::Start(void) VerifyOrExit(!IsRunning()); SuccessOrExit(error = mSocket.Open(&Server::HandleUdpReceive, this)); - SuccessOrExit(error = mSocket.Bind(kPort, kBindUnspecifiedNetif ? OT_NETIF_UNSPECIFIED : OT_NETIF_THREAD)); + SuccessOrExit(error = mSocket.Bind(kPort, kBindUnspecifiedNetif ? Ip6::kNetifUnspecified : Ip6::kNetifThread)); #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE Get().HandleDnssdServerStateChange(); @@ -103,6 +110,16 @@ void Server::Stop(void) } } +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + for (UpstreamQueryTransaction &txn : mUpstreamQueryTransactions) + { + if (txn.IsValid()) + { + ResetUpstreamQueryTransaction(txn, kErrorFailed); + } + } +#endif + mTimer.Stop(); IgnoreError(mSocket.Close()); @@ -142,13 +159,27 @@ void Server::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessag void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage, const Ip6::MessageInfo &aMessageInfo) { Error error = kErrorNone; - Message * responseMessage = nullptr; + Message *responseMessage = nullptr; Header responseHeader; NameCompressInfo compressInfo(kDefaultDomainName); - Header::Response response = Header::kResponseSuccess; - bool resolveByQueryCallbacks = false; + Header::Response response = Header::kResponseSuccess; + bool shouldSendResponse = true; + +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + if (mEnableUpstreamQuery && ShouldForwardToUpstream(aRequestHeader, aRequestMessage)) + { + error = ResolveByUpstream(aRequestMessage, aMessageInfo); + if (error == kErrorNone) + { + shouldSendResponse = false; + ExitNow(); + } + response = Header::kResponseServerFailure; + LogWarn("Failed to forward DNS query to upstream: %s", ErrorToString(error)); + } +#endif - responseMessage = mSocket.NewMessage(0); + responseMessage = mSocket.NewMessage(); VerifyOrExit(responseMessage != nullptr, error = kErrorNoBufs); // Allocate space for DNS header @@ -158,6 +189,14 @@ void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage responseHeader.Clear(); responseHeader.SetType(Header::kTypeResponse); responseHeader.SetMessageId(aRequestHeader.GetMessageId()); + responseHeader.SetQueryType(aRequestHeader.GetQueryType()); + if (aRequestHeader.IsRecursionDesiredFlagSet()) + { + responseHeader.SetRecursionDesiredFlag(); + } + + // We may met errors when forwarding the query to the upstream + VerifyOrExit(response == Header::kResponseSuccess); // Validate the query VerifyOrExit(aRequestHeader.GetQueryType() == Header::kQueryTypeStandard, @@ -165,6 +204,15 @@ void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage VerifyOrExit(!aRequestHeader.IsTruncationFlagSet(), response = Header::kResponseFormatError); VerifyOrExit(aRequestHeader.GetQuestionCount() > 0, response = Header::kResponseFormatError); + switch (mTestMode) + { + case kTestModeDisabled: + break; + case kTestModeSingleQuestionOnly: + VerifyOrExit(aRequestHeader.GetQuestionCount() == 1, response = Header::kResponseFormatError); + break; + } + response = AddQuestions(aRequestHeader, aRequestMessage, responseHeader, *responseMessage, compressInfo); VerifyOrExit(response == Header::kResponseSuccess); @@ -172,13 +220,11 @@ void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage // Answer the questions response = ResolveBySrp(responseHeader, *responseMessage, compressInfo); #endif - - // Resolve the question using query callbacks if SRP server failed to resolve the questions. if (responseHeader.GetAnswerCount() == 0) { if (kErrorNone == ResolveByQueryCallbacks(responseHeader, *responseMessage, compressInfo, aMessageInfo)) { - resolveByQueryCallbacks = true; + shouldSendResponse = false; } } #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE @@ -189,7 +235,7 @@ void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage #endif exit: - if (error == kErrorNone && !resolveByQueryCallbacks) + if (error == kErrorNone && shouldSendResponse) { SendResponse(responseHeader, response, *responseMessage, aMessageInfo, mSocket); } @@ -199,9 +245,9 @@ void Server::ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage void Server::SendResponse(Header aHeader, Header::Response aResponseCode, - Message & aMessage, + Message &aMessage, const Ip6::MessageInfo &aMessageInfo, - Ip6::Udp::Socket & aSocket) + Ip6::Udp::Socket &aSocket) { Error error; @@ -219,10 +265,10 @@ void Server::SendResponse(Header aHeader, error = aSocket.SendTo(aMessage, aMessageInfo); - FreeMessageOnError(&aMessage, error); - if (error != kErrorNone) { + // do not use `FreeMessageOnError()` to avoid null check on nonnull pointer + aMessage.Free(); LogWarn("failed to send DNS-SD reply: %s", ErrorToString(error)); } else @@ -233,10 +279,10 @@ void Server::SendResponse(Header aHeader, UpdateResponseCounters(aResponseCode); } -Header::Response Server::AddQuestions(const Header & aRequestHeader, - const Message & aRequestMessage, - Header & aResponseHeader, - Message & aResponseMessage, +Header::Response Server::AddQuestions(const Header &aRequestHeader, + const Message &aRequestMessage, + Header &aResponseHeader, + Message &aResponseMessage, NameCompressInfo &aCompressInfo) { Question question; @@ -294,9 +340,9 @@ Header::Response Server::AddQuestions(const Header & aRequestHeader, return response; } -Error Server::AppendQuestion(const char * aName, - const Question & aQuestion, - Message & aMessage, +Error Server::AppendQuestion(const char *aName, + const Question &aQuestion, + Message &aMessage, NameCompressInfo &aCompressInfo) { Error error = kErrorNone; @@ -323,9 +369,9 @@ Error Server::AppendQuestion(const char * aName, return error; } -Error Server::AppendPtrRecord(Message & aMessage, - const char * aServiceName, - const char * aInstanceName, +Error Server::AppendPtrRecord(Message &aMessage, + const char *aServiceName, + const char *aInstanceName, uint32_t aTtl, NameCompressInfo &aCompressInfo) { @@ -350,9 +396,9 @@ Error Server::AppendPtrRecord(Message & aMessage, return error; } -Error Server::AppendSrvRecord(Message & aMessage, - const char * aInstanceName, - const char * aHostName, +Error Server::AppendSrvRecord(Message &aMessage, + const char *aInstanceName, + const char *aHostName, uint32_t aTtl, uint16_t aPriority, uint16_t aWeight, @@ -383,11 +429,11 @@ Error Server::AppendSrvRecord(Message & aMessage, return error; } -Error Server::AppendAaaaRecord(Message & aMessage, - const char * aHostName, +Error Server::AppendAaaaRecord(Message &aMessage, + const char *aHostName, const Ip6::Address &aAddress, uint32_t aTtl, - NameCompressInfo & aCompressInfo) + NameCompressInfo &aCompressInfo) { AaaaRecord aaaaRecord; Error error; @@ -496,9 +542,9 @@ Error Server::AppendInstanceName(Message &aMessage, const char *aName, NameCompr return error; } -Error Server::AppendTxtRecord(Message & aMessage, - const char * aInstanceName, - const void * aTxtData, +Error Server::AppendTxtRecord(Message &aMessage, + const char *aInstanceName, + const void *aTxtData, uint16_t aTxtLength, uint32_t aTtl, NameCompressInfo &aCompressInfo) @@ -640,10 +686,10 @@ Error Server::FindPreviousLabel(const char *aName, uint8_t &aStart, uint8_t &aSt uint8_t end; VerifyOrExit(start > 0, error = kErrorNotFound); - VerifyOrExit(aName[--start] == Name::kLabelSeperatorChar, error = kErrorInvalidArgs); + VerifyOrExit(aName[--start] == Name::kLabelSeparatorChar, error = kErrorInvalidArgs); end = start; - while (start > 0 && aName[start - 1] != Name::kLabelSeperatorChar) + while (start > 0 && aName[start - 1] != Name::kLabelSeparatorChar) { start--; } @@ -658,8 +704,8 @@ Error Server::FindPreviousLabel(const char *aName, uint8_t &aStart, uint8_t &aSt } #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE -Header::Response Server::ResolveBySrp(Header & aResponseHeader, - Message & aResponseMessage, +Header::Response Server::ResolveBySrp(Header &aResponseHeader, + Message &aResponseMessage, Server::NameCompressInfo &aCompressInfo) { Question question; @@ -703,10 +749,10 @@ Header::Response Server::ResolveBySrp(Header & aResponseHeader, return response; } -Header::Response Server::ResolveQuestionBySrp(const char * aName, - const Question & aQuestion, - Header & aResponseHeader, - Message & aResponseMessage, +Header::Response Server::ResolveQuestionBySrp(const char *aName, + const Question &aQuestion, + Header &aResponseHeader, + Message &aResponseMessage, NameCompressInfo &aCompressInfo, bool aAdditional) { @@ -807,16 +853,16 @@ const Srp::Server::Host *Server::GetNextSrpHost(const Srp::Server::Host *aHost) return host; } -const Srp::Server::Service *Server::GetNextSrpService(const Srp::Server::Host & aHost, +const Srp::Server::Service *Server::GetNextSrpService(const Srp::Server::Host &aHost, const Srp::Server::Service *aService) { return aHost.FindNextService(aService, Srp::Server::kFlagsAnyTypeActiveService); } #endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE -Error Server::ResolveByQueryCallbacks(Header & aResponseHeader, - Message & aResponseMessage, - NameCompressInfo & aCompressInfo, +Error Server::ResolveByQueryCallbacks(Header &aResponseHeader, + Message &aResponseMessage, + NameCompressInfo &aCompressInfo, const Ip6::MessageInfo &aMessageInfo) { QueryTransaction *query = nullptr; @@ -839,8 +885,89 @@ Error Server::ResolveByQueryCallbacks(Header & aResponseHeader, return error; } -Server::QueryTransaction *Server::NewQuery(const Header & aResponseHeader, - Message & aResponseMessage, +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE +bool Server::ShouldForwardToUpstream(const Header &aRequestHeader, const Message &aRequestMessage) +{ + bool ret = true; + uint16_t readOffset; + char name[Name::kMaxNameSize]; + + VerifyOrExit(aRequestHeader.IsRecursionDesiredFlagSet(), ret = false); + readOffset = sizeof(Header); + + for (uint16_t i = 0; i < aRequestHeader.GetQuestionCount(); i++) + { + VerifyOrExit(kErrorNone == Name::ReadName(aRequestMessage, readOffset, name, sizeof(name)), ret = false); + readOffset += sizeof(Question); + + VerifyOrExit(!Name::IsSubDomainOf(name, kDefaultDomainName), ret = false); + for (const char *blockedDomain : kBlockedDomains) + { + VerifyOrExit(!Name::IsSameDomain(name, blockedDomain), ret = false); + } + } + +exit: + return ret; +} + +void Server::OnUpstreamQueryDone(UpstreamQueryTransaction &aQueryTransaction, Message *aResponseMessage) +{ + Error error = kErrorNone; + + VerifyOrExit(aQueryTransaction.IsValid(), error = kErrorInvalidArgs); + + if (aResponseMessage != nullptr) + { + error = mSocket.SendTo(*aResponseMessage, aQueryTransaction.GetMessageInfo()); + } + ResetUpstreamQueryTransaction(aQueryTransaction, error); + ResetTimer(); + +exit: + FreeMessageOnError(aResponseMessage, error); +} + +Server::UpstreamQueryTransaction *Server::AllocateUpstreamQueryTransaction(const Ip6::MessageInfo &aMessageInfo) +{ + UpstreamQueryTransaction *ret = nullptr; + + for (UpstreamQueryTransaction &txn : mUpstreamQueryTransactions) + { + if (!txn.IsValid()) + { + ret = &txn; + txn.Init(aMessageInfo); + break; + } + } + + if (ret != nullptr) + { + LogInfo("Upstream query transaction %d initialized.", static_cast(ret - mUpstreamQueryTransactions)); + mTimer.FireAtIfEarlier(ret->GetExpireTime()); + } + + return ret; +} + +Error Server::ResolveByUpstream(const Message &aRequestMessage, const Ip6::MessageInfo &aMessageInfo) +{ + Error error = kErrorNone; + UpstreamQueryTransaction *txn = nullptr; + + txn = AllocateUpstreamQueryTransaction(aMessageInfo); + VerifyOrExit(txn != nullptr, error = kErrorNoBufs); + + otPlatDnsStartUpstreamQuery(&GetInstance(), txn, &aRequestMessage); + +exit: + return error; +} +#endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + +Server::QueryTransaction *Server::NewQuery(const Header &aResponseHeader, + Message &aResponseMessage, const NameCompressInfo &aCompressInfo, const Ip6::MessageInfo &aMessageInfo) { @@ -866,8 +993,8 @@ Server::QueryTransaction *Server::NewQuery(const Header & aResponseHead return newQuery; } -bool Server::CanAnswerQuery(const QueryTransaction & aQuery, - const char * aServiceFullName, +bool Server::CanAnswerQuery(const QueryTransaction &aQuery, + const char *aServiceFullName, const otDnssdServiceInstanceInfo &aInstanceInfo) { char name[Name::kMaxNameSize]; @@ -900,12 +1027,12 @@ bool Server::CanAnswerQuery(const Server::QueryTransaction &aQuery, const char * return (sdType == kDnsQueryResolveHost) && StringMatch(name, aHostFullName, kStringCaseInsensitiveMatch); } -void Server::AnswerQuery(QueryTransaction & aQuery, - const char * aServiceFullName, +void Server::AnswerQuery(QueryTransaction &aQuery, + const char *aServiceFullName, const otDnssdServiceInstanceInfo &aInstanceInfo) { - Header & responseHeader = aQuery.GetResponseHeader(); - Message & responseMessage = aQuery.GetResponseMessage(); + Header &responseHeader = aQuery.GetResponseHeader(); + Message &responseMessage = aQuery.GetResponseMessage(); Error error = kErrorNone; NameCompressInfo &compressInfo = aQuery.GetNameCompressInfo(); @@ -960,8 +1087,8 @@ void Server::AnswerQuery(QueryTransaction & aQuery, void Server::AnswerQuery(QueryTransaction &aQuery, const char *aHostFullName, const otDnssdHostInfo &aHostInfo) { - Header & responseHeader = aQuery.GetResponseHeader(); - Message & responseMessage = aQuery.GetResponseMessage(); + Header &responseHeader = aQuery.GetResponseHeader(); + Message &responseMessage = aQuery.GetResponseMessage(); Error error = kErrorNone; NameCompressInfo &compressInfo = aQuery.GetNameCompressInfo(); @@ -987,7 +1114,7 @@ void Server::AnswerQuery(QueryTransaction &aQuery, const char *aHostFullName, co void Server::SetQueryCallbacks(otDnssdQuerySubscribeCallback aSubscribe, otDnssdQueryUnsubscribeCallback aUnsubscribe, - void * aContext) + void *aContext) { OT_ASSERT((aSubscribe == nullptr) == (aUnsubscribe == nullptr)); @@ -996,12 +1123,12 @@ void Server::SetQueryCallbacks(otDnssdQuerySubscribeCallback aSubscribe, mQueryCallbackContext = aContext; } -void Server::HandleDiscoveredServiceInstance(const char * aServiceFullName, +void Server::HandleDiscoveredServiceInstance(const char *aServiceFullName, const otDnssdServiceInstanceInfo &aInstanceInfo) { - OT_ASSERT(StringEndsWith(aServiceFullName, Name::kLabelSeperatorChar)); - OT_ASSERT(StringEndsWith(aInstanceInfo.mFullName, Name::kLabelSeperatorChar)); - OT_ASSERT(StringEndsWith(aInstanceInfo.mHostName, Name::kLabelSeperatorChar)); + OT_ASSERT(StringEndsWith(aServiceFullName, Name::kLabelSeparatorChar)); + OT_ASSERT(StringEndsWith(aInstanceInfo.mFullName, Name::kLabelSeparatorChar)); + OT_ASSERT(StringEndsWith(aInstanceInfo.mHostName, Name::kLabelSeparatorChar)); for (QueryTransaction &query : mQueryTransactions) { @@ -1014,7 +1141,7 @@ void Server::HandleDiscoveredServiceInstance(const char * a void Server::HandleDiscoveredHost(const char *aHostFullName, const otDnssdHostInfo &aHostInfo) { - OT_ASSERT(StringEndsWith(aHostFullName, Name::kLabelSeperatorChar)); + OT_ASSERT(StringEndsWith(aHostFullName, Name::kLabelSeparatorChar)); for (QueryTransaction &query : mQueryTransactions) { @@ -1056,7 +1183,7 @@ Server::DnsQueryType Server::GetQueryTypeAndName(const otDnssdQuery *aQuery, cha return GetQueryTypeAndName(query->GetResponseHeader(), query->GetResponseMessage(), aName); } -Server::DnsQueryType Server::GetQueryTypeAndName(const Header & aHeader, +Server::DnsQueryType Server::GetQueryTypeAndName(const Header &aHeader, const Message &aMessage, char (&aName)[Name::kMaxNameSize]) { @@ -1123,11 +1250,6 @@ bool Server::HasQuestion(const Header &aHeader, const Message &aMessage, const c return found; } -void Server::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void Server::HandleTimer(void) { TimeMilli now = TimerMilli::GetNow(); @@ -1148,6 +1270,21 @@ void Server::HandleTimer(void) } } +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + for (UpstreamQueryTransaction &query : mUpstreamQueryTransactions) + { + if (!query.IsValid()) + { + continue; + } + + if (query.GetExpireTime() <= now) + { + otPlatDnsCancelUpstreamQuery(&GetInstance(), &query); + } + } +#endif + ResetTimer(); } @@ -1158,23 +1295,25 @@ void Server::ResetTimer(void) for (QueryTransaction &query : mQueryTransactions) { - TimeMilli expire; - if (!query.IsValid()) { continue; } - expire = query.GetStartTime() + kQueryTimeout; - if (expire <= now) - { - nextExpire = now; - } - else if (expire < nextExpire) + nextExpire = Min(nextExpire, Max(now, query.GetStartTime() + kQueryTimeout)); + } + +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + for (UpstreamQueryTransaction &query : mUpstreamQueryTransactions) + { + if (!query.IsValid()) { - nextExpire = expire; + continue; } + + nextExpire = Min(nextExpire, Max(now, query.GetExpireTime())); } +#endif if (nextExpire < now.GetDistantFuture()) { @@ -1202,11 +1341,11 @@ void Server::FinalizeQuery(QueryTransaction &aQuery, Header::Response aResponseC aQuery.Finalize(aResponseCode, mSocket); } -void Server::QueryTransaction::Init(const Header & aResponseHeader, - Message & aResponseMessage, +void Server::QueryTransaction::Init(const Header &aResponseHeader, + Message &aResponseMessage, const NameCompressInfo &aCompressInfo, const Ip6::MessageInfo &aMessageInfo, - Instance & aInstance) + Instance &aInstance) { OT_ASSERT(mResponseMessage == nullptr); @@ -1251,6 +1390,32 @@ void Server::UpdateResponseCounters(Header::Response aResponseCode) } } +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE +void Server::UpstreamQueryTransaction::Init(const Ip6::MessageInfo &aMessageInfo) +{ + mMessageInfo = aMessageInfo; + mValid = true; + mExpireTime = TimerMilli::GetNow() + kQueryTimeout; +} + +void Server::ResetUpstreamQueryTransaction(UpstreamQueryTransaction &aTxn, Error aError) +{ + int index = static_cast(&aTxn - mUpstreamQueryTransactions); + + // Avoid the warnings when info / warn logging is disabled. + OT_UNUSED_VARIABLE(index); + if (aError == kErrorNone) + { + LogInfo("Upstream query transaction %d completed.", index); + } + else + { + LogWarn("Upstream query transaction %d closed: %s.", index, ErrorToString(aError)); + } + aTxn.Reset(); +} +#endif + } // namespace ServiceDiscovery } // namespace Dns } // namespace ot diff --git a/src/core/net/dnssd_server.hpp b/src/core/net/dnssd_server.hpp index de16c424e52..bebcf41c503 100644 --- a/src/core/net/dnssd_server.hpp +++ b/src/core/net/dnssd_server.hpp @@ -49,6 +49,10 @@ * This file includes definitions for the DNS-SD server. */ +struct otPlatDnsUpstreamQuery +{ +}; + namespace ot { namespace Srp { @@ -75,6 +79,62 @@ class Server : public InstanceLocator, private NonCopyable { }; +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + /** + * This class represents an upstream query transaction. The methods should only be used by + * `Dns::ServiceDiscovery::Server`. + * + */ + class UpstreamQueryTransaction : public otPlatDnsUpstreamQuery + { + public: + /** + * This method returns whether the transaction is valid. + * + * @retval TRUE The transaction is valid. + * @retval FALSE The transaction is not valid. + * + */ + bool IsValid(void) const { return mValid; } + + /** + * This method returns the time when the transaction expires. + * + * @returns The expire time of the transaction. + * + */ + TimeMilli GetExpireTime(void) const { return mExpireTime; } + + /** + * This method resets the transaction with a reason. The transaction will be invalid and can be reused for + * another upstream query after this call. + * + */ + void Reset(void) { mValid = false; } + + /** + * This method initializes the transaction. + * + * @param[in] aMessageInfo The IP message info of the query. + * + */ + void Init(const Ip6::MessageInfo &aMessageInfo); + + /** + * This method returns the message info of the query. + * + * @returns The message info of the query. + * + */ + const Ip6::MessageInfo &GetMessageInfo(void) const { return mMessageInfo; } + + private: + Ip6::MessageInfo mMessageInfo; + TimeMilli mExpireTime; + bool mValid; + }; +#endif + /** * This enumeration specifies a DNS-SD query type. * @@ -122,7 +182,7 @@ class Server : public InstanceLocator, private NonCopyable */ void SetQueryCallbacks(otDnssdQuerySubscribeCallback aSubscribe, otDnssdQueryUnsubscribeCallback aUnsubscribe, - void * aContext); + void *aContext); /** * This method notifies a discovered service instance. @@ -133,6 +193,37 @@ class Server : public InstanceLocator, private NonCopyable */ void HandleDiscoveredServiceInstance(const char *aServiceFullName, const otDnssdServiceInstanceInfo &aInstanceInfo); +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + /** + * This method notifies an answer of an upstream DNS query. + * + * The Transaction will be released. + * + * @param[in] aQueryTransaction A reference to upstream DNS query transaction. + * @param[in] aResponseMessage A pointer to response UDP message, should be allocated from Udp::NewMessage. + * Passing a nullptr means close the transaction without a response. + * + */ + void OnUpstreamQueryDone(UpstreamQueryTransaction &aQueryTransaction, Message *aResponseMessage); + + /** + * This method indicates whether the server will forward DNS queries to platform DNS upstream API. + * + * @retval TRUE If the server will forward DNS queries. + * @retval FALSE If the server will not forward DNS queries. + * + */ + bool IsUpstreamQueryEnabled(void) const { return mEnableUpstreamQuery; } + + /** + * This method enables or disables forwarding DNS queries to platform DNS upstream API. + * + * @param[in] aEnabled A boolean to enable/disable forwarding DNS queries to upstream. + * + */ + void SetUpstreamQueryEnabled(bool aEnabled) { mEnableUpstreamQuery = aEnabled; } +#endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + /** * This method notifies a discovered host. * @@ -171,6 +262,27 @@ class Server : public InstanceLocator, private NonCopyable */ const Counters &GetCounters(void) const { return mCounters; }; + /** + * This enumeration represents different test modes. + * + * The test mode is intended for testing the client by having server behave in certain ways, e.g., reject messages + * with certain format (e.g., more than one question in query). + * + */ + enum TestMode : uint8_t + { + kTestModeDisabled, ///< Test mode is disabled. + kTestModeSingleQuestionOnly, ///< Allow single question in query message, send `FormatError` for two or more. + }; + + /** + * This method sets the test mode for `Server`. + * + * @param[in] aTestMode The new test mode. + * + */ + void SetTestMode(TestMode aTestMode) { mTestMode = aTestMode; } + private: class NameCompressInfo : public Clearable { @@ -250,10 +362,11 @@ class Server : public InstanceLocator, private NonCopyable uint16_t mHostNameOffset; // Offset of host name serialization into the response message. }; - static constexpr bool kBindUnspecifiedNetif = OPENTHREAD_CONFIG_DNSSD_SERVER_BIND_UNSPECIFIED_NETIF; - static constexpr uint8_t kProtocolLabelLength = 4; - static constexpr uint8_t kSubTypeLabelLength = 4; - static constexpr uint16_t kMaxConcurrentQueries = 32; + static constexpr bool kBindUnspecifiedNetif = OPENTHREAD_CONFIG_DNSSD_SERVER_BIND_UNSPECIFIED_NETIF; + static constexpr uint8_t kProtocolLabelLength = 4; + static constexpr uint8_t kSubTypeLabelLength = 4; + static constexpr uint16_t kMaxConcurrentQueries = 32; + static constexpr uint16_t kMaxConcurrentUpstreamQueries = 32; // This structure represents the splitting information of a full name. struct NameComponentsOffsetInfo @@ -297,23 +410,23 @@ class Server : public InstanceLocator, private NonCopyable { } - void Init(const Header & aResponseHeader, - Message & aResponseMessage, + void Init(const Header &aResponseHeader, + Message &aResponseMessage, const NameCompressInfo &aCompressInfo, const Ip6::MessageInfo &aMessageInfo, - Instance & aInstance); + Instance &aInstance); bool IsValid(void) const { return mResponseMessage != nullptr; } const Ip6::MessageInfo &GetMessageInfo(void) const { return mMessageInfo; } - const Header & GetResponseHeader(void) const { return mResponseHeader; } - Header & GetResponseHeader(void) { return mResponseHeader; } - const Message & GetResponseMessage(void) const { return *mResponseMessage; } - Message & GetResponseMessage(void) { return *mResponseMessage; } + const Header &GetResponseHeader(void) const { return mResponseHeader; } + Header &GetResponseHeader(void) { return mResponseHeader; } + const Message &GetResponseMessage(void) const { return *mResponseMessage; } + Message &GetResponseMessage(void) { return const_cast(*mResponseMessage); } TimeMilli GetStartTime(void) const { return mStartTime; } - NameCompressInfo & GetNameCompressInfo(void) { return mCompressInfo; }; + NameCompressInfo &GetNameCompressInfo(void) { return mCompressInfo; }; void Finalize(Header::Response aResponseMessage, Ip6::Udp::Socket &aSocket); Header mResponseHeader; - Message * mResponseMessage; + Message *mResponseMessage; NameCompressInfo mCompressInfo; Ip6::MessageInfo mMessageInfo; TimeMilli mStartTime; @@ -325,39 +438,39 @@ class Server : public InstanceLocator, private NonCopyable static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void ProcessQuery(const Header &aRequestHeader, Message &aRequestMessage, const Ip6::MessageInfo &aMessageInfo); - static Header::Response AddQuestions(const Header & aRequestHeader, - const Message & aRequestMessage, - Header & aResponseHeader, - Message & aResponseMessage, + static Header::Response AddQuestions(const Header &aRequestHeader, + const Message &aRequestMessage, + Header &aResponseHeader, + Message &aResponseMessage, NameCompressInfo &aCompressInfo); - static Error AppendQuestion(const char * aName, - const Question & aQuestion, - Message & aMessage, + static Error AppendQuestion(const char *aName, + const Question &aQuestion, + Message &aMessage, NameCompressInfo &aCompressInfo); - static Error AppendPtrRecord(Message & aMessage, - const char * aServiceName, - const char * aInstanceName, + static Error AppendPtrRecord(Message &aMessage, + const char *aServiceName, + const char *aInstanceName, uint32_t aTtl, NameCompressInfo &aCompressInfo); - static Error AppendSrvRecord(Message & aMessage, - const char * aInstanceName, - const char * aHostName, + static Error AppendSrvRecord(Message &aMessage, + const char *aInstanceName, + const char *aHostName, uint32_t aTtl, uint16_t aPriority, uint16_t aWeight, uint16_t aPort, NameCompressInfo &aCompressInfo); - static Error AppendTxtRecord(Message & aMessage, - const char * aInstanceName, - const void * aTxtData, + static Error AppendTxtRecord(Message &aMessage, + const char *aInstanceName, + const void *aTxtData, uint16_t aTxtLength, uint32_t aTtl, NameCompressInfo &aCompressInfo); - static Error AppendAaaaRecord(Message & aMessage, - const char * aHostName, + static Error AppendAaaaRecord(Message &aMessage, + const char *aHostName, const Ip6::Address &aAddress, uint32_t aTtl, - NameCompressInfo & aCompressInfo); + NameCompressInfo &aCompressInfo); static Error AppendServiceName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo); static Error AppendInstanceName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo); static Error AppendHostName(Message &aMessage, const char *aName, NameCompressInfo &aCompressInfo); @@ -366,70 +479,90 @@ class Server : public InstanceLocator, private NonCopyable static Error FindPreviousLabel(const char *aName, uint8_t &aStart, uint8_t &aStop); void SendResponse(Header aHeader, Header::Response aResponseCode, - Message & aMessage, + Message &aMessage, const Ip6::MessageInfo &aMessageInfo, - Ip6::Udp::Socket & aSocket); + Ip6::Udp::Socket &aSocket); #if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE - Header::Response ResolveBySrp(Header & aResponseHeader, - Message & aResponseMessage, + Header::Response ResolveBySrp(Header &aResponseHeader, + Message &aResponseMessage, Server::NameCompressInfo &aCompressInfo); - Header::Response ResolveQuestionBySrp(const char * aName, - const Question & aQuestion, - Header & aResponseHeader, - Message & aResponseMessage, + Header::Response ResolveQuestionBySrp(const char *aName, + const Question &aQuestion, + Header &aResponseHeader, + Message &aResponseMessage, NameCompressInfo &aCompressInfo, bool aAdditional); - const Srp::Server::Host * GetNextSrpHost(const Srp::Server::Host *aHost); - static const Srp::Server::Service *GetNextSrpService(const Srp::Server::Host & aHost, + const Srp::Server::Host *GetNextSrpHost(const Srp::Server::Host *aHost); + static const Srp::Server::Service *GetNextSrpService(const Srp::Server::Host &aHost, const Srp::Server::Service *aService); #endif - Error ResolveByQueryCallbacks(Header & aResponseHeader, - Message & aResponseMessage, - NameCompressInfo & aCompressInfo, +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + static bool ShouldForwardToUpstream(const Header &aRequestHeader, const Message &aRequestMessage); + UpstreamQueryTransaction *AllocateUpstreamQueryTransaction(const Ip6::MessageInfo &aMessageInfo); + void ResetUpstreamQueryTransaction(UpstreamQueryTransaction &aTxn, Error aError); + Error ResolveByUpstream(const Message &aRequestMessage, const Ip6::MessageInfo &aMessageInfo); +#endif + + Error ResolveByQueryCallbacks(Header &aResponseHeader, + Message &aResponseMessage, + NameCompressInfo &aCompressInfo, const Ip6::MessageInfo &aMessageInfo); - QueryTransaction *NewQuery(const Header & aResponseHeader, - Message & aResponseMessage, + QueryTransaction *NewQuery(const Header &aResponseHeader, + Message &aResponseMessage, const NameCompressInfo &aCompressInfo, const Ip6::MessageInfo &aMessageInfo); - static bool CanAnswerQuery(const QueryTransaction & aQuery, - const char * aServiceFullName, + static bool CanAnswerQuery(const QueryTransaction &aQuery, + const char *aServiceFullName, const otDnssdServiceInstanceInfo &aInstanceInfo); - void AnswerQuery(QueryTransaction & aQuery, - const char * aServiceFullName, + void AnswerQuery(QueryTransaction &aQuery, + const char *aServiceFullName, const otDnssdServiceInstanceInfo &aInstanceInfo); static bool CanAnswerQuery(const Server::QueryTransaction &aQuery, const char *aHostFullName); void AnswerQuery(QueryTransaction &aQuery, const char *aHostFullName, const otDnssdHostInfo &aHostInfo); void FinalizeQuery(QueryTransaction &aQuery, Header::Response aResponseCode); - static DnsQueryType GetQueryTypeAndName(const Header & aHeader, + static DnsQueryType GetQueryTypeAndName(const Header &aHeader, const Message &aMessage, char (&aName)[Name::kMaxNameSize]); static bool HasQuestion(const Header &aHeader, const Message &aMessage, const char *aName, uint16_t aQuestionType); - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); - void ResetTimer(void); + + void HandleTimer(void); + void ResetTimer(void); void UpdateResponseCounters(Header::Response aResponseCode); + using ServerTimer = TimerMilliIn; + static const char kDnssdProtocolUdp[]; static const char kDnssdProtocolTcp[]; static const char kDnssdSubTypeLabel[]; static const char kDefaultDomainName[]; - Ip6::Udp::Socket mSocket; + + Ip6::Udp::Socket mSocket; QueryTransaction mQueryTransactions[kMaxConcurrentQueries]; - void * mQueryCallbackContext; + void *mQueryCallbackContext; otDnssdQuerySubscribeCallback mQuerySubscribe; otDnssdQueryUnsubscribeCallback mQueryUnsubscribe; - TimerMilli mTimer; - Counters mCounters; + static const char *kBlockedDomains[]; +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + bool mEnableUpstreamQuery; + UpstreamQueryTransaction mUpstreamQueryTransactions[kMaxConcurrentUpstreamQueries]; +#endif + + ServerTimer mTimer; + Counters mCounters; + TestMode mTestMode; }; } // namespace ServiceDiscovery } // namespace Dns DefineMapEnum(otDnssdQueryType, Dns::ServiceDiscovery::Server::DnsQueryType); +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE +DefineCoreType(otPlatDnsUpstreamQuery, Dns::ServiceDiscovery::Server::UpstreamQueryTransaction); +#endif } // namespace ot diff --git a/src/core/net/icmp6.cpp b/src/core/net/icmp6.cpp index 93542f688cf..2f195b0aba3 100644 --- a/src/core/net/icmp6.cpp +++ b/src/core/net/icmp6.cpp @@ -54,15 +54,9 @@ Icmp::Icmp(Instance &aInstance) { } -Message *Icmp::NewMessage(uint16_t aReserved) -{ - return Get().NewMessage(sizeof(Header) + aReserved); -} +Message *Icmp::NewMessage(void) { return Get().NewMessage(sizeof(Header)); } -Error Icmp::RegisterHandler(Handler &aHandler) -{ - return mHandlers.Add(aHandler); -} +Error Icmp::RegisterHandler(Handler &aHandler) { return mHandlers.Add(aHandler); } Error Icmp::SendEchoRequest(Message &aMessage, const MessageInfo &aMessageInfo, uint16_t aIdentifier) { @@ -103,7 +97,7 @@ Error Icmp::SendError(Header::Type aType, Header::Code aCode, const MessageInfo { Error error = kErrorNone; MessageInfo messageInfoLocal; - Message * message = nullptr; + Message *message = nullptr; Header icmp6Header; Message::Settings settings(Message::kWithLinkSecurity, Message::kPriorityNet); @@ -186,9 +180,9 @@ Error Icmp::HandleEchoRequest(Message &aRequestMessage, const MessageInfo &aMess { Error error = kErrorNone; Header icmp6Header; - Message * replyMessage = nullptr; + Message *replyMessage = nullptr; MessageInfo replyMessageInfo; - uint16_t payloadLength; + uint16_t dataOffset; // always handle Echo Request destined for RLOC or ALOC VerifyOrExit(ShouldHandleEchoRequest(aMessageInfo) || aMessageInfo.GetSockAddr().GetIid().IsLocator()); @@ -204,12 +198,11 @@ Error Icmp::HandleEchoRequest(Message &aRequestMessage, const MessageInfo &aMess ExitNow(); } - payloadLength = aRequestMessage.GetLength() - aRequestMessage.GetOffset() - Header::kDataFieldOffset; - SuccessOrExit(error = replyMessage->SetLength(Header::kDataFieldOffset + payloadLength)); + dataOffset = aRequestMessage.GetOffset() + Header::kDataFieldOffset; - replyMessage->WriteBytes(0, &icmp6Header, Header::kDataFieldOffset); - aRequestMessage.CopyTo(aRequestMessage.GetOffset() + Header::kDataFieldOffset, Header::kDataFieldOffset, - payloadLength, *replyMessage); + SuccessOrExit(error = replyMessage->AppendBytes(&icmp6Header, Header::kDataFieldOffset)); + SuccessOrExit(error = replyMessage->AppendBytesFromMessage(aRequestMessage, dataOffset, + aRequestMessage.GetLength() - dataOffset)); replyMessageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr()); diff --git a/src/core/net/icmp6.hpp b/src/core/net/icmp6.hpp index 594da57b18b..b054b569256 100644 --- a/src/core/net/icmp6.hpp +++ b/src/core/net/icmp6.hpp @@ -92,6 +92,8 @@ class Icmp : public InstanceLocator, private NonCopyable kTypeEchoReply = OT_ICMP6_TYPE_ECHO_REPLY, ///< Echo Reply kTypeRouterSolicit = OT_ICMP6_TYPE_ROUTER_SOLICIT, ///< Router Solicitation kTypeRouterAdvert = OT_ICMP6_TYPE_ROUTER_ADVERT, ///< Router Advertisement + kTypeNeighborSolicit = OT_ICMP6_TYPE_NEIGHBOR_SOLICIT, ///< Neighbor Solicitation + kTypeNeighborAdvert = OT_ICMP6_TYPE_NEIGHBOR_ADVERT, ///< Neighbor Advertisement }; /** @@ -240,12 +242,10 @@ class Icmp : public InstanceLocator, private NonCopyable /** * This method returns a new ICMP message with sufficient header space reserved. * - * @param[in] aReserved The number of header bytes to reserve after the ICMP header. - * * @returns A pointer to the message or `nullptr` if no buffers are available. * */ - Message *NewMessage(uint16_t aReserved); + Message *NewMessage(void); /** * This method registers ICMPv6 handler. diff --git a/src/core/net/ip4_types.cpp b/src/core/net/ip4_types.cpp index a23f51e3325..bbe80f1982b 100644 --- a/src/core/net/ip4_types.cpp +++ b/src/core/net/ip4_types.cpp @@ -37,44 +37,27 @@ namespace ot { namespace Ip4 { -Error Address::FromString(const char *aString) +Error Address::FromString(const char *aString, char aTerminatorChar) { - constexpr char kSeperatorChar = '.'; - constexpr char kNullChar = '\0'; + constexpr char kSeparatorChar = '.'; - Error error = kErrorParse; + Error error = kErrorParse; + const char *cur = aString; for (uint8_t index = 0;; index++) { - uint16_t value = 0; - uint8_t hasFirstDigit = false; - - for (char digitChar = *aString;; ++aString, digitChar = *aString) - { - if ((digitChar < '0') || (digitChar > '9')) - { - break; - } - - value = static_cast((value * 10) + static_cast(digitChar - '0')); - VerifyOrExit(value <= NumericLimits::kMax); - hasFirstDigit = true; - } - - VerifyOrExit(hasFirstDigit); - - mFields.m8[index] = static_cast(value); + SuccessOrExit(StringParseUint8(cur, mFields.m8[index])); if (index == sizeof(Address) - 1) { break; } - VerifyOrExit(*aString == kSeperatorChar); - aString++; + VerifyOrExit(*cur == kSeparatorChar); + cur++; } - VerifyOrExit(*aString == kNullChar); + VerifyOrExit(*cur == aTerminatorChar); error = kErrorNone; exit: @@ -85,7 +68,7 @@ void Address::ExtractFromIp6Address(uint8_t aPrefixLength, const Ip6::Address &a { // The prefix length must be 32, 40, 48, 56, 64, 96. IPv4 bytes are added // after the prefix, skipping over the bits 64 to 71 (byte at `kSkipIndex`) - // which must be set to zero. The suffix is set to zero (per RFC 6502). + // which must be set to zero. The suffix is set to zero (per RFC 6052). // // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ // |PL| 0-------------32--40--48--56--64--72--80--88--96--104---------| @@ -111,14 +94,14 @@ void Address::ExtractFromIp6Address(uint8_t aPrefixLength, const Ip6::Address &a ip6Index = aPrefixLength / CHAR_BIT; - for (uint8_t i = 0; i < Ip4::Address::kSize; i++) + for (uint8_t &i : mFields.m8) { if (ip6Index == kSkipIndex) { ip6Index++; } - mFields.m8[i] = aIp6Address.GetBytes()[ip6Index++]; + i = aIp6Address.GetBytes()[ip6Index++]; } } @@ -127,20 +110,67 @@ void Address::SynthesizeFromCidrAndHost(const Cidr &aCidr, const uint32_t aHost) mFields.m32 = (aCidr.mAddress.mFields.m32 & aCidr.SubnetMask()) | (HostSwap32(aHost) & aCidr.HostMask()); } +void Address::ToString(StringWriter &aWriter) const +{ + aWriter.Append("%d.%d.%d.%d", mFields.m8[0], mFields.m8[1], mFields.m8[2], mFields.m8[3]); +} + +void Address::ToString(char *aBuffer, uint16_t aSize) const +{ + StringWriter writer(aBuffer, aSize); + + ToString(writer); +} + Address::InfoString Address::ToString(void) const { InfoString string; - string.Append("%d.%d.%d.%d", mFields.m8[0], mFields.m8[1], mFields.m8[2], mFields.m8[3]); + ToString(string); return string; } +Error Cidr::FromString(const char *aString) +{ + constexpr char kSlashChar = '/'; + constexpr uint16_t kMaxCidrLength = 32; + + Error error = kErrorParse; + const char *cur; + + SuccessOrExit(AsCoreType(&mAddress).FromString(aString, kSlashChar)); + + cur = StringFind(aString, kSlashChar); + VerifyOrExit(cur != nullptr); + cur++; + + SuccessOrExit(StringParseUint8(cur, mLength, kMaxCidrLength)); + VerifyOrExit(*cur == kNullChar); + + error = kErrorNone; + +exit: + return error; +} + +void Cidr::ToString(StringWriter &aWriter) const +{ + aWriter.Append("%s/%d", AsCoreType(&mAddress).ToString().AsCString(), mLength); +} + +void Cidr::ToString(char *aBuffer, uint16_t aSize) const +{ + StringWriter writer(aBuffer, aSize); + + ToString(writer); +} + Cidr::InfoString Cidr::ToString(void) const { InfoString string; - string.Append("%s/%d", AsCoreType(&mAddress).ToString().AsCString(), mLength); + ToString(string); return string; } diff --git a/src/core/net/ip4_types.hpp b/src/core/net/ip4_types.hpp index 042e4ca4980..6d2ba0c3922 100644 --- a/src/core/net/ip4_types.hpp +++ b/src/core/net/ip4_types.hpp @@ -148,7 +148,7 @@ class Address : public otIp4Address, public Equatable
, public Clearable void SynthesizeFromCidrAndHost(const Cidr &aCidr, uint32_t aHost); /** - * This method parses an IPv4 address string. + * This method parses an IPv4 address string terminated by `aTerminatorChar`. * * The string MUST follow the quad-dotted notation of four decimal values (ranging from 0 to 255 each). For * example, "127.0.0.1" @@ -159,7 +159,21 @@ class Address : public otIp4Address, public Equatable
, public Clearable * @retval kErrorParse Failed to parse the IPv4 address string. * */ - Error FromString(const char *aString); + Error FromString(const char *aString, char aTerminatorChar = kNullChar); + + /** + * This method converts the address to a string. + * + * The string format uses quad-dotted notation of four bytes in the address (e.g., "127.0.0.1"). + * + * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be + * truncated but the outputted string is always null-terminated. + * + * @param[out] aBuffer A pointer to a char array to output the string (MUST NOT be `nullptr`). + * @param[in] aSize The size of @p aBuffer (in bytes). + * + */ + void ToString(char *aBuffer, uint16_t aSize) const; /** * This method converts the IPv4 address to a string. @@ -170,6 +184,9 @@ class Address : public otIp4Address, public Equatable
, public Clearable * */ InfoString ToString(void) const; + +private: + void ToString(StringWriter &aWriter) const; } OT_TOOL_PACKED_END; /** @@ -189,6 +206,35 @@ class Cidr : public otIp4Cidr, public Unequatable, public Clearable InfoString; + /** + * This method converts the IPv4 CIDR string to binary. + * + * The string format uses quad-dotted notation of four bytes in the address with the length of prefix (e.g., + * "127.0.0.1/32"). + * + * @param[in] aString A pointer to the null-terminated string. + * + * @retval kErrorNone Successfully parsed the IPv4 CIDR string. + * @retval kErrorParse Failed to parse the IPv4 CIDR string. + * + */ + Error FromString(const char *aString); + + /** + * This method converts the IPv4 CIDR to a string. + * + * The string format uses quad-dotted notation of four bytes in the address with the length of prefix (e.g., + * "127.0.0.1/32"). + * + * If the resulting string does not fit in @p aBuffer (within its @p aSize characters), the string will be + * truncated but the outputted string is always null-terminated. + * + * @param[out] aBuffer A pointer to a char array to output the string (MUST NOT be `nullptr`). + * @param[in] aSize The size of @p aBuffer (in bytes). + * + */ + void ToString(char *aBuffer, uint16_t aSize) const; + /** * This method converts the IPv4 CIDR to a string. * @@ -237,6 +283,8 @@ class Cidr : public otIp4Cidr, public Unequatable, public Clearable static constexpr uint8_t kVersionIhlOffset = 0; static constexpr uint8_t kTrafficClassOffset = 1; static constexpr uint8_t kTotalLengthOffset = 2; - static constexpr uint8_t kIdenficationOffset = 4; + static constexpr uint8_t kIdentificationOffset = 4; static constexpr uint8_t kFlagsFragmentOffset = 6; static constexpr uint8_t kTtlOffset = 8; static constexpr uint8_t kProtocolOffset = 9; @@ -479,7 +527,7 @@ class Header : public Clearable
* @returns Whether don't fragment flag is set. * */ - bool GetDf(void) const { return HostSwap16(mFlagsFargmentOffset) & kFlagsDf; } + bool GetDf(void) const { return HostSwap16(mFlagsFragmentOffset) & kFlagsDf; } /** * This method returns the Mf flag in the IPv4 header. @@ -487,7 +535,7 @@ class Header : public Clearable
* @returns Whether more fragments flag is set. * */ - bool GetMf(void) const { return HostSwap16(mFlagsFargmentOffset) & kFlagsMf; } + bool GetMf(void) const { return HostSwap16(mFlagsFragmentOffset) & kFlagsMf; } /** * This method returns the fragment offset in the IPv4 header. @@ -495,7 +543,7 @@ class Header : public Clearable
* @returns The fragment offset of the IPv4 packet. * */ - uint16_t GetFragmentOffset(void) const { return HostSwap16(mFlagsFargmentOffset) & kFragmentOffsetMask; } + uint16_t GetFragmentOffset(void) const { return HostSwap16(mFlagsFragmentOffset) & kFragmentOffsetMask; } private: // IPv4 header @@ -527,7 +575,7 @@ class Header : public Clearable
uint8_t mDscpEcn; uint16_t mTotalLength; uint16_t mIdentification; - uint16_t mFlagsFargmentOffset; + uint16_t mFlagsFragmentOffset; uint8_t mTtl; uint8_t mProtocol; uint16_t mHeaderChecksum; @@ -537,7 +585,7 @@ class Header : public Clearable
/** * This class implements ICMP(v4). - * Note: ICMP(v4) messages will only be generated / handled by NAT64. So only header defination is required. + * Note: ICMP(v4) messages will only be generated / handled by NAT64. So only header definition is required. * */ class Icmp @@ -636,9 +684,9 @@ class Icmp * @param[in] aRestOfHeader The rest of header field in the ICMP message. The buffer should have 4 octets. * */ - void SetRestOfHeader(const uint8_t *aRestOfheader) + void SetRestOfHeader(const uint8_t *aRestOfHeader) { - memcpy(mRestOfHeader, aRestOfheader, sizeof(mRestOfHeader)); + memcpy(mRestOfHeader, aRestOfHeader, sizeof(mRestOfHeader)); } private: diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index bb3096fd6b6..b6693d2af82 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -47,6 +47,7 @@ #include "net/icmp6.hpp" #include "net/ip6_address.hpp" #include "net/ip6_filter.hpp" +#include "net/nat64_translator.hpp" #include "net/netif.hpp" #include "net/udp6.hpp" #include "openthread/ip6.h" @@ -66,11 +67,8 @@ RegisterLogModule("Ip6"); Ip6::Ip6(Instance &aInstance) : InstanceLocator(aInstance) - , mForwardingEnabled(false) , mIsReceiveIp6FilterEnabled(false) - , mReceiveIp6DatagramCallback(nullptr) - , mReceiveIp6DatagramCallbackContext(nullptr) - , mSendQueueTask(aInstance, Ip6::HandleSendQueue) + , mSendQueueTask(aInstance) , mIcmp(aInstance) , mUdp(aInstance) , mMpl(aInstance) @@ -78,17 +76,36 @@ Ip6::Ip6(Instance &aInstance) , mTcp(aInstance) #endif { +#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + ResetBorderRoutingCounters(); +#endif } +Message *Ip6::NewMessage(void) { return NewMessage(0); } + +Message *Ip6::NewMessage(uint16_t aReserved) { return NewMessage(aReserved, Message::Settings::GetDefault()); } + Message *Ip6::NewMessage(uint16_t aReserved, const Message::Settings &aSettings) { return Get().Allocate( - Message::kTypeIp6, sizeof(Header) + sizeof(HopByHopHeader) + sizeof(OptionMpl) + aReserved, aSettings); + Message::kTypeIp6, sizeof(Header) + sizeof(HopByHopHeader) + sizeof(MplOption) + aReserved, aSettings); } -Message *Ip6::NewMessage(const uint8_t *aData, uint16_t aDataLength, const Message::Settings &aSettings) +Message *Ip6::NewMessageFromData(const uint8_t *aData, uint16_t aDataLength, const Message::Settings &aSettings) { - Message *message = Get().Allocate(Message::kTypeIp6, /* aReserveHeader */ 0, aSettings); + Message *message = nullptr; + Message::Settings settings = aSettings; + const Header *header; + + VerifyOrExit((aData != nullptr) && (aDataLength >= sizeof(Header))); + + // Determine priority from IPv6 header + header = reinterpret_cast(aData); + VerifyOrExit(header->IsValid()); + VerifyOrExit(sizeof(Header) + header->GetPayloadLength() == aDataLength); + settings.mPriority = DscpToPriority(header->GetDscp()); + + message = Get().Allocate(Message::kTypeIp6, /* aReserveHeader */ 0, settings); VerifyOrExit(message != nullptr); @@ -102,18 +119,6 @@ Message *Ip6::NewMessage(const uint8_t *aData, uint16_t aDataLength, const Messa return message; } -Message *Ip6::NewMessage(const uint8_t *aData, uint16_t aDataLength) -{ - Message * message = nullptr; - Message::Priority priority; - - SuccessOrExit(GetDatagramPriority(aData, aDataLength, priority)); - message = NewMessage(aData, aDataLength, Message::Settings(Message::kWithLinkSecurity, priority)); - -exit: - return message; -} - Message::Priority Ip6::DscpToPriority(uint8_t aDscp) { Message::Priority priority; @@ -169,48 +174,24 @@ uint8_t Ip6::PriorityToDscp(Message::Priority aPriority) return dscp; } -Error Ip6::GetDatagramPriority(const uint8_t *aData, uint16_t aDataLen, Message::Priority &aPriority) -{ - Error error = kErrorNone; - const Header *header; - - VerifyOrExit((aData != nullptr) && (aDataLen >= sizeof(Header)), error = kErrorInvalidArgs); - - header = reinterpret_cast(aData); - VerifyOrExit(header->IsValid(), error = kErrorParse); - VerifyOrExit(sizeof(Header) + header->GetPayloadLength() == aDataLen, error = kErrorParse); - - aPriority = DscpToPriority(header->GetDscp()); - -exit: - return error; -} - -void Ip6::SetReceiveDatagramCallback(otIp6ReceiveCallback aCallback, void *aCallbackContext) -{ - mReceiveIp6DatagramCallback = aCallback; - mReceiveIp6DatagramCallbackContext = aCallbackContext; -} - Error Ip6::AddMplOption(Message &aMessage, Header &aHeader) { Error error = kErrorNone; HopByHopHeader hbhHeader; - OptionMpl mplOption; - OptionPadN padOption; + MplOption mplOption; + PadOption padOption; hbhHeader.SetNextHeader(aHeader.GetNextHeader()); hbhHeader.SetLength(0); mMpl.InitOption(mplOption, aHeader.GetSource()); - // Mpl option may require two bytes padding. - if ((mplOption.GetTotalLength() + sizeof(hbhHeader)) % 8) + // Check if MPL option may require padding + if (padOption.InitToPadHeaderWithSize(sizeof(HopByHopHeader) + mplOption.GetSize()) == kErrorNone) { - padOption.Init(2); - SuccessOrExit(error = aMessage.PrependBytes(&padOption, padOption.GetTotalLength())); + SuccessOrExit(error = aMessage.PrependBytes(&padOption, padOption.GetSize())); } - SuccessOrExit(error = aMessage.PrependBytes(&mplOption, mplOption.GetTotalLength())); + SuccessOrExit(error = aMessage.PrependBytes(&mplOption, mplOption.GetSize())); SuccessOrExit(error = aMessage.Prepend(hbhHeader)); aHeader.SetPayloadLength(aHeader.GetPayloadLength() + sizeof(hbhHeader) + sizeof(mplOption)); aHeader.SetNextHeader(kProtoHopOpts); @@ -219,25 +200,23 @@ Error Ip6::AddMplOption(Message &aMessage, Header &aHeader) return error; } -Error Ip6::AddTunneledMplOption(Message &aMessage, Header &aHeader, MessageInfo &aMessageInfo) +Error Ip6::AddTunneledMplOption(Message &aMessage, Header &aHeader) { - Error error = kErrorNone; - Header tunnelHeader; - const Netif::UnicastAddress *source; - MessageInfo messageInfo(aMessageInfo); + Error error = kErrorNone; + Header tunnelHeader; + const Address *source; // Use IP-in-IP encapsulation (RFC2473) and ALL_MPL_FORWARDERS address. - messageInfo.GetPeerAddr().SetToRealmLocalAllMplForwarders(); - tunnelHeader.InitVersionTrafficClassFlow(); tunnelHeader.SetHopLimit(static_cast(kDefaultHopLimit)); tunnelHeader.SetPayloadLength(aHeader.GetPayloadLength() + sizeof(tunnelHeader)); - tunnelHeader.SetDestination(messageInfo.GetPeerAddr()); + tunnelHeader.GetDestination().SetToRealmLocalAllMplForwarders(); tunnelHeader.SetNextHeader(kProtoIp6); - VerifyOrExit((source = SelectSourceAddress(messageInfo)) != nullptr, error = kErrorInvalidSourceAddress); + source = SelectSourceAddress(tunnelHeader.GetDestination()); + VerifyOrExit(source != nullptr, error = kErrorInvalidSourceAddress); - tunnelHeader.SetSource(source->GetAddress()); + tunnelHeader.SetSource(*source); SuccessOrExit(error = AddMplOption(aMessage, tunnelHeader)); SuccessOrExit(error = aMessage.Prepend(tunnelHeader)); @@ -246,7 +225,7 @@ Error Ip6::AddTunneledMplOption(Message &aMessage, Header &aHeader, MessageInfo return error; } -Error Ip6::InsertMplOption(Message &aMessage, Header &aHeader, MessageInfo &aMessageInfo) +Error Ip6::InsertMplOption(Message &aMessage, Header &aHeader) { Error error = kErrorNone; @@ -260,37 +239,37 @@ Error Ip6::InsertMplOption(Message &aMessage, Header &aHeader, MessageInfo &aMes if (aHeader.GetNextHeader() == kProtoHopOpts) { HopByHopHeader hbh; - uint16_t hbhLength = 0; - OptionMpl mplOption; + uint16_t hbhSize; + MplOption mplOption; + PadOption padOption; - // read existing hop-by-hop option header + // Read existing hop-by-hop option header SuccessOrExit(error = aMessage.Read(0, hbh)); - hbhLength = (hbh.GetLength() + 1) * 8; + hbhSize = hbh.GetSize(); - VerifyOrExit(hbhLength <= aHeader.GetPayloadLength(), error = kErrorParse); + VerifyOrExit(hbhSize <= aHeader.GetPayloadLength(), error = kErrorParse); - // increase existing hop-by-hop option header length by 8 bytes + // Increment hop-by-hop option header length by one which + // increases its total size by 8 bytes. hbh.SetLength(hbh.GetLength() + 1); aMessage.Write(0, hbh); - // make space for MPL Option + padding by shifting hop-by-hop option header - SuccessOrExit(error = aMessage.PrependBytes(nullptr, 8)); - aMessage.CopyTo(8, 0, hbhLength, aMessage); + // Make space for MPL Option + padding (8 bytes) at the end + // of hop-by-hop header + SuccessOrExit(error = aMessage.InsertHeader(hbhSize, ExtensionHeader::kLengthUnitSize)); - // insert MPL Option + // Insert MPL Option mMpl.InitOption(mplOption, aHeader.GetSource()); - aMessage.WriteBytes(hbhLength, &mplOption, mplOption.GetTotalLength()); + aMessage.WriteBytes(hbhSize, &mplOption, mplOption.GetSize()); - // insert Pad Option (if needed) - if (mplOption.GetTotalLength() % 8) + // Insert Pad Option (if needed) + if (padOption.InitToPadHeaderWithSize(mplOption.GetSize()) == kErrorNone) { - OptionPadN padOption; - padOption.Init(8 - (mplOption.GetTotalLength() % 8)); - aMessage.WriteBytes(hbhLength + mplOption.GetTotalLength(), &padOption, padOption.GetTotalLength()); + aMessage.WriteBytes(hbhSize + mplOption.GetSize(), &padOption, padOption.GetSize()); } - // increase IPv6 Payload Length - aHeader.SetPayloadLength(aHeader.GetPayloadLength() + 8); + // Update IPv6 Payload Length + aHeader.SetPayloadLength(aHeader.GetPayloadLength() + ExtensionHeader::kLengthUnitSize); } else { @@ -309,7 +288,7 @@ Error Ip6::InsertMplOption(Message &aMessage, Header &aHeader, MessageInfo &aMes if ((messageCopy = aMessage.Clone()) != nullptr) { - IgnoreError(HandleDatagram(*messageCopy, nullptr, nullptr, /* aFromHost */ true)); + IgnoreError(HandleDatagram(*messageCopy, kFromHostDisallowLoopBack)); LogInfo("Message copy for indirect transmission to sleepy children"); } else @@ -319,7 +298,7 @@ Error Ip6::InsertMplOption(Message &aMessage, Header &aHeader, MessageInfo &aMes } #endif - SuccessOrExit(error = AddTunneledMplOption(aMessage, aHeader, aMessageInfo)); + SuccessOrExit(error = AddTunneledMplOption(aMessage, aHeader)); } exit: @@ -331,6 +310,7 @@ Error Ip6::RemoveMplOption(Message &aMessage) Error error = kErrorNone; Header ip6Header; HopByHopHeader hbh; + Option option; uint16_t offset; uint16_t endOffset; uint16_t mplOffset = 0; @@ -343,55 +323,46 @@ Error Ip6::RemoveMplOption(Message &aMessage) VerifyOrExit(ip6Header.GetNextHeader() == kProtoHopOpts); IgnoreError(aMessage.Read(offset, hbh)); - endOffset = offset + (hbh.GetLength() + 1) * 8; + endOffset = offset + hbh.GetSize(); VerifyOrExit(aMessage.GetLength() >= endOffset, error = kErrorParse); offset += sizeof(hbh); - while (offset < endOffset) + for (; offset < endOffset; offset += option.GetSize()) { - OptionHeader option; + IgnoreError(option.ParseFrom(aMessage, offset, endOffset)); - IgnoreError(aMessage.Read(offset, option)); + if (option.IsPadding()) + { + continue; + } - switch (option.GetType()) + if (option.GetType() == MplOption::kType) { - case OptionMpl::kType: - // if multiple MPL options exist, discard packet + // If multiple MPL options exist, discard packet VerifyOrExit(mplOffset == 0, error = kErrorParse); mplOffset = offset; mplLength = option.GetLength(); - VerifyOrExit(mplLength <= sizeof(OptionMpl) - sizeof(OptionHeader), error = kErrorParse); + VerifyOrExit(mplLength <= sizeof(MplOption) - sizeof(Option), error = kErrorParse); if (mplOffset == sizeof(ip6Header) + sizeof(hbh) && hbh.GetLength() == 0) { - // first and only IPv6 Option, remove IPv6 HBH Option header + // First and only IPv6 Option, remove IPv6 HBH Option header remove = true; } - else if (mplOffset + 8 == endOffset) + else if (mplOffset + ExtensionHeader::kLengthUnitSize == endOffset) { - // last IPv6 Option, remove last 8 bytes + // Last IPv6 Option, remove the last 8 bytes remove = true; } - - offset += sizeof(option) + option.GetLength(); - break; - - case OptionPad1::kType: - offset += sizeof(OptionPad1); - break; - - case OptionPadN::kType: - offset += sizeof(option) + option.GetLength(); - break; - - default: - // encountered another option, now just replace MPL Option with PadN + } + else + { + // Encountered another option, now just replace + // MPL Option with Pad Option remove = false; - offset += sizeof(option) + option.GetLength(); - break; } } @@ -400,42 +371,34 @@ Error Ip6::RemoveMplOption(Message &aMessage) if (remove) { - // last IPv6 Option, shrink HBH Option header - uint8_t buf[8]; - - offset = endOffset - sizeof(buf); - - while (offset >= sizeof(buf)) - { - IgnoreError(aMessage.Read(offset - sizeof(buf), buf)); - aMessage.Write(offset, buf); - offset -= sizeof(buf); - } - - aMessage.RemoveHeader(sizeof(buf)); + // Last IPv6 Option, shrink HBH Option header by + // 8 bytes (`kLengthUnitSize`) + aMessage.RemoveHeader(endOffset - ExtensionHeader::kLengthUnitSize, ExtensionHeader::kLengthUnitSize); if (mplOffset == sizeof(ip6Header) + sizeof(hbh)) { - // remove entire HBH header + // Remove entire HBH header ip6Header.SetNextHeader(hbh.GetNextHeader()); } else { - // update HBH header length + // Update HBH header length, decrement by one + // which decreases its total size by 8 bytes. + hbh.SetLength(hbh.GetLength() - 1); aMessage.Write(sizeof(ip6Header), hbh); } - ip6Header.SetPayloadLength(ip6Header.GetPayloadLength() - sizeof(buf)); + ip6Header.SetPayloadLength(ip6Header.GetPayloadLength() - ExtensionHeader::kLengthUnitSize); aMessage.Write(0, ip6Header); } else if (mplOffset != 0) { - // replace MPL Option with PadN Option - OptionPadN padOption; + // Replace MPL Option with Pad Option + PadOption padOption; - padOption.Init(sizeof(OptionHeader) + mplLength); - aMessage.WriteBytes(mplOffset, &padOption, padOption.GetTotalLength()); + padOption.InitForPadSize(sizeof(Option) + mplLength); + aMessage.WriteBytes(mplOffset, &padOption, padOption.GetSize()); } exit: @@ -452,10 +415,22 @@ Error Ip6::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aI { Error error = kErrorNone; Header header; + uint8_t dscp; uint16_t payloadLength = aMessage.GetLength(); + if ((aIpProto == kProtoUdp) && + Get().IsTmfMessage(aMessageInfo.GetSockAddr(), aMessageInfo.GetPeerAddr(), + aMessageInfo.GetPeerPort())) + { + dscp = Tmf::Agent::PriorityToDscp(aMessage.GetPriority()); + } + else + { + dscp = PriorityToDscp(aMessage.GetPriority()); + } + header.InitVersionTrafficClassFlow(); - header.SetDscp(PriorityToDscp(aMessage.GetPriority())); + header.SetDscp(dscp); header.SetEcn(aMessageInfo.GetEcn()); header.SetPayloadLength(payloadLength); header.SetNextHeader(aIpProto); @@ -471,10 +446,10 @@ Error Ip6::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aI if (aMessageInfo.GetSockAddr().IsUnspecified() || aMessageInfo.GetSockAddr().IsMulticast()) { - const Netif::UnicastAddress *source = SelectSourceAddress(aMessageInfo); + const Address *source = SelectSourceAddress(aMessageInfo.GetPeerAddr()); VerifyOrExit(source != nullptr, error = kErrorInvalidSourceAddress); - header.SetSource(source->GetAddress()); + header.SetSource(*source); } else { @@ -511,7 +486,7 @@ Error Ip6::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aI } #endif - SuccessOrExit(error = AddTunneledMplOption(aMessage, header, aMessageInfo)); + SuccessOrExit(error = AddTunneledMplOption(aMessage, header)); } aMessage.SetMulticastLoop(aMessageInfo.GetMulticastLoop()); @@ -530,11 +505,6 @@ Error Ip6::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aI return error; } -void Ip6::HandleSendQueue(Tasklet &aTasklet) -{ - aTasklet.Get().HandleSendQueue(); -} - void Ip6::HandleSendQueue(void) { Message *message; @@ -542,7 +512,7 @@ void Ip6::HandleSendQueue(void) while ((message = mSendQueue.GetHead()) != nullptr) { mSendQueue.Dequeue(*message); - IgnoreError(HandleDatagram(*message, nullptr, nullptr, /* aFromHost */ false)); + IgnoreError(HandleDatagram(*message, kFromHostAllowLoopBack)); } } @@ -550,59 +520,37 @@ Error Ip6::HandleOptions(Message &aMessage, Header &aHeader, bool aIsOutbound, b { Error error = kErrorNone; HopByHopHeader hbhHeader; - OptionHeader optionHeader; + Option option; + uint16_t offset = aMessage.GetOffset(); uint16_t endOffset; - SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), hbhHeader)); - endOffset = aMessage.GetOffset() + (hbhHeader.GetLength() + 1) * 8; + SuccessOrExit(error = aMessage.Read(offset, hbhHeader)); + endOffset = offset + hbhHeader.GetSize(); VerifyOrExit(endOffset <= aMessage.GetLength(), error = kErrorParse); - aMessage.MoveOffset(sizeof(optionHeader)); + offset += sizeof(HopByHopHeader); - while (aMessage.GetOffset() < endOffset) + for (; offset < endOffset; offset += option.GetSize()) { - SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), optionHeader)); + SuccessOrExit(error = option.ParseFrom(aMessage, offset, endOffset)); - if (optionHeader.GetType() == OptionPad1::kType) + if (option.IsPadding()) { - aMessage.MoveOffset(sizeof(OptionPad1)); continue; } - VerifyOrExit(aMessage.GetOffset() + sizeof(optionHeader) + optionHeader.GetLength() <= endOffset, - error = kErrorParse); - - switch (optionHeader.GetType()) + if (option.GetType() == MplOption::kType) { - case OptionMpl::kType: - SuccessOrExit(error = mMpl.ProcessOption(aMessage, aHeader.GetSource(), aIsOutbound, aReceive)); - break; - - default: - switch (optionHeader.GetAction()) - { - case OptionHeader::kActionSkip: - break; - - case OptionHeader::kActionDiscard: - ExitNow(error = kErrorDrop); - - case OptionHeader::kActionForceIcmp: - // TODO: send icmp error - ExitNow(error = kErrorDrop); - - case OptionHeader::kActionIcmp: - // TODO: send icmp error - ExitNow(error = kErrorDrop); - } - - break; + SuccessOrExit(error = mMpl.ProcessOption(aMessage, offset, aHeader.GetSource(), aIsOutbound, aReceive)); + continue; } - aMessage.MoveOffset(sizeof(optionHeader) + optionHeader.GetLength()); + VerifyOrExit(option.GetAction() == Option::kActionSkip, error = kErrorDrop); } + aMessage.SetOffset(offset); + exit: return error; } @@ -613,7 +561,7 @@ Error Ip6::FragmentDatagram(Message &aMessage, uint8_t aIpProto) Error error = kErrorNone; Header header; FragmentHeader fragmentHeader; - Message * fragment = nullptr; + Message *fragment = nullptr; uint16_t fragmentCnt = 0; uint16_t payloadFragment = 0; uint16_t offset = 0; @@ -650,7 +598,7 @@ Error Ip6::FragmentDatagram(Message &aMessage, uint8_t aIpProto) offset = fragmentCnt * FragmentHeader::BytesToFragmentOffset(maxPayloadFragment); fragmentHeader.SetOffset(offset); - VerifyOrExit((fragment = NewMessage(0)) != nullptr, error = kErrorNoBufs); + VerifyOrExit((fragment = NewMessage()) != nullptr, error = kErrorNoBufs); IgnoreError(fragment->SetPriority(aMessage.GetPriority())); SuccessOrExit(error = fragment->SetLength(aMessage.GetOffset() + sizeof(fragmentHeader) + payloadFragment)); @@ -660,10 +608,10 @@ Error Ip6::FragmentDatagram(Message &aMessage, uint8_t aIpProto) fragment->SetOffset(aMessage.GetOffset()); fragment->Write(aMessage.GetOffset(), fragmentHeader); - VerifyOrExit(aMessage.CopyTo(aMessage.GetOffset() + FragmentHeader::FragmentOffsetToBytes(offset), - aMessage.GetOffset() + sizeof(fragmentHeader), payloadFragment, - *fragment) == static_cast(payloadFragment), - error = kErrorNoBufs); + fragment->WriteBytesFromMessage( + /* aWriteOffset */ aMessage.GetOffset() + sizeof(fragmentHeader), aMessage, + /* aReadOffset */ aMessage.GetOffset() + FragmentHeader::FragmentOffsetToBytes(offset), + /* aLength */ payloadFragment); EnqueueDatagram(*fragment); @@ -686,19 +634,16 @@ Error Ip6::FragmentDatagram(Message &aMessage, uint8_t aIpProto) return error; } -Error Ip6::HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessageInfo, bool aFromHost) +Error Ip6::HandleFragment(Message &aMessage, MessageOrigin aOrigin, MessageInfo &aMessageInfo) { Error error = kErrorNone; Header header, headerBuffer; FragmentHeader fragmentHeader; - Message * message = nullptr; + Message *message = nullptr; uint16_t offset = 0; uint16_t payloadFragment = 0; - int assertValue = 0; bool isFragmented = true; - OT_UNUSED_VARIABLE(assertValue); - SuccessOrExit(error = aMessage.Read(0, header)); SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), fragmentHeader)); @@ -736,17 +681,15 @@ Error Ip6::HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessag if (message == nullptr) { LogDebg("start reassembly"); - VerifyOrExit((message = NewMessage(0)) != nullptr, error = kErrorNoBufs); + VerifyOrExit((message = NewMessage()) != nullptr, error = kErrorNoBufs); mReassemblyList.Enqueue(*message); - SuccessOrExit(error = message->SetLength(aMessage.GetOffset())); message->SetTimestampToNow(); message->SetOffset(0); message->SetDatagramTag(fragmentHeader.GetIdentification()); // copying the non-fragmentable header to the fragmentation buffer - assertValue = aMessage.CopyTo(0, 0, aMessage.GetOffset(), *message); - OT_ASSERT(assertValue == aMessage.GetOffset()); + SuccessOrExit(error = message->AppendBytesFromMessage(aMessage, 0, aMessage.GetOffset())); Get().RegisterReceiver(TimeTicker::kIp6FragmentReassembler); } @@ -758,9 +701,9 @@ Error Ip6::HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessag } // copy the fragment payload into the message buffer - assertValue = aMessage.CopyTo(aMessage.GetOffset() + sizeof(fragmentHeader), aMessage.GetOffset() + offset, - payloadFragment, *message); - OT_ASSERT(assertValue == static_cast(payloadFragment)); + message->WriteBytesFromMessage( + /* aWriteOffset */ aMessage.GetOffset() + offset, aMessage, + /* aReadOffset */ aMessage.GetOffset() + sizeof(fragmentHeader), /* aLength */ payloadFragment); // check if it is the last frame if (!fragmentHeader.IsMoreFlagSet()) @@ -778,7 +721,7 @@ Error Ip6::HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessag mReassemblyList.Dequeue(*message); - IgnoreError(HandleDatagram(*message, aNetif, aMessageInfo.mLinkInfo, aFromHost)); + IgnoreError(HandleDatagram(*message, aOrigin, aMessageInfo.mLinkInfo, /* aIsReassembled */ true)); } exit: @@ -801,10 +744,7 @@ Error Ip6::HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessag return error; } -void Ip6::CleanupFragmentationBuffer(void) -{ - mReassemblyList.DequeueAndFreeAll(); -} +void Ip6::CleanupFragmentationBuffer(void) { mReassemblyList.DequeueAndFreeAll(); } void Ip6::HandleTimeTick(void) { @@ -865,11 +805,10 @@ Error Ip6::FragmentDatagram(Message &aMessage, uint8_t aIpProto) return kErrorNone; } -Error Ip6::HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessageInfo, bool aFromHost) +Error Ip6::HandleFragment(Message &aMessage, MessageOrigin aOrigin, MessageInfo &aMessageInfo) { - OT_UNUSED_VARIABLE(aNetif); + OT_UNUSED_VARIABLE(aOrigin); OT_UNUSED_VARIABLE(aMessageInfo); - OT_UNUSED_VARIABLE(aFromHost); Error error = kErrorNone; FragmentHeader fragmentHeader; @@ -885,16 +824,15 @@ Error Ip6::HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessag } #endif // OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE -Error Ip6::HandleExtensionHeaders(Message & aMessage, - Netif * aNetif, - MessageInfo &aMessageInfo, - Header & aHeader, - uint8_t & aNextHeader, - bool aIsOutbound, - bool aFromHost, - bool & aReceive) +Error Ip6::HandleExtensionHeaders(Message &aMessage, + MessageOrigin aOrigin, + MessageInfo &aMessageInfo, + Header &aHeader, + uint8_t &aNextHeader, + bool &aReceive) { - Error error = kErrorNone; + Error error = kErrorNone; + bool isOutbound = (aOrigin != kFromThreadNetif); ExtensionHeader extHeader; while (aReceive || aNextHeader == kProtoHopOpts) @@ -904,19 +842,17 @@ Error Ip6::HandleExtensionHeaders(Message & aMessage, switch (aNextHeader) { case kProtoHopOpts: - SuccessOrExit(error = HandleOptions(aMessage, aHeader, aIsOutbound, aReceive)); + SuccessOrExit(error = HandleOptions(aMessage, aHeader, isOutbound, aReceive)); break; case kProtoFragment: -#if !OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE - IgnoreError(ProcessReceiveCallback(aMessage, aMessageInfo, aNextHeader, aFromHost, - /* aAllowReceiveFilter */ false, Message::kCopyToUse)); -#endif - SuccessOrExit(error = HandleFragment(aMessage, aNetif, aMessageInfo, aFromHost)); + IgnoreError(PassToHost(aMessage, aOrigin, aMessageInfo, aNextHeader, + /* aApplyFilter */ false, Message::kCopyToUse)); + SuccessOrExit(error = HandleFragment(aMessage, aOrigin, aMessageInfo)); break; case kProtoDstOpts: - SuccessOrExit(error = HandleOptions(aMessage, aHeader, aIsOutbound, aReceive)); + SuccessOrExit(error = HandleOptions(aMessage, aHeader, isOutbound, aReceive)); break; case kProtoIp6: @@ -937,9 +873,9 @@ Error Ip6::HandleExtensionHeaders(Message & aMessage, return error; } -Error Ip6::HandlePayload(Header & aIp6Header, - Message & aMessage, - MessageInfo & aMessageInfo, +Error Ip6::HandlePayload(Header &aIp6Header, + Message &aMessage, + MessageInfo &aMessageInfo, uint8_t aIpProto, Message::Ownership aMessageOwnership) { @@ -950,7 +886,18 @@ Error Ip6::HandlePayload(Header & aIp6Header, Error error = kErrorNone; Message *message = (aMessageOwnership == Message::kTakeCustody) ? &aMessage : nullptr; - VerifyOrExit(aIpProto == kProtoTcp || aIpProto == kProtoUdp || aIpProto == kProtoIcmp6); + switch (aIpProto) + { + case kProtoUdp: + case kProtoIcmp6: + break; +#if OPENTHREAD_CONFIG_TCP_ENABLE + case kProtoTcp: + break; +#endif + default: + ExitNow(); + } if (aMessageOwnership == Message::kCopyToUse) { @@ -995,26 +942,47 @@ Error Ip6::HandlePayload(Header & aIp6Header, return error; } -Error Ip6::ProcessReceiveCallback(Message & aMessage, - const MessageInfo &aMessageInfo, - uint8_t aIpProto, - bool aFromHost, - bool aAllowReceiveFilter, - Message::Ownership aMessageOwnership) +Error Ip6::PassToHost(Message &aMessage, + MessageOrigin aOrigin, + const MessageInfo &aMessageInfo, + uint8_t aIpProto, + bool aApplyFilter, + Message::Ownership aMessageOwnership) { + // This method passes the message to host by invoking the + // registered IPv6 receive callback. When NAT64 is enabled, it + // may also perform translation and invoke IPv4 receive + // callback. + Error error = kErrorNone; - Message *message = &aMessage; + Message *message = nullptr; + + // `message` points to the `Message` instance we own in this + // method. If we can take ownership of `aMessage`, we use it as + // `message`. Otherwise, we may create a clone of it and use as + // `message`. `message` variable will be set to `nullptr` if the + // message ownership is transferred to an invoked callback. At + // the end of this method we free `message` if it is not `nullptr` + // indicating it was not passed to a callback. - VerifyOrExit(!aFromHost, error = kErrorNoRoute); - VerifyOrExit(mReceiveIp6DatagramCallback != nullptr, error = kErrorNoRoute); + if (aMessageOwnership == Message::kTakeCustody) + { + message = &aMessage; + } + + VerifyOrExit(aOrigin != kFromHostDisallowLoopBack, error = kErrorNoRoute); + + VerifyOrExit(mReceiveIp6DatagramCallback.IsSet(), error = kErrorNoRoute); - // Do not forward reassembled IPv6 packets. + // Do not pass IPv6 packets that exceed kMinimalMtu. VerifyOrExit(aMessage.GetLength() <= kMinimalMtu, error = kErrorDrop); - if (mIsReceiveIp6FilterEnabled && aAllowReceiveFilter) + if (mIsReceiveIp6FilterEnabled && aApplyFilter) { #if !OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE - // do not pass messages sent to an RLOC/ALOC, except Service Locator + // Do not pass messages sent to an RLOC/ALOC, except + // Service Locator + bool isLocator = Get().IsMeshLocalAddress(aMessageInfo.GetSockAddr()) && aMessageInfo.GetSockAddr().GetIid().IsLocator(); @@ -1028,9 +996,8 @@ Error Ip6::ProcessReceiveCallback(Message & aMessage, if (mIcmp.ShouldHandleEchoRequest(aMessageInfo)) { Icmp::Header icmp; - IgnoreError(aMessage.Read(aMessage.GetOffset(), icmp)); - // do not pass ICMP Echo Request messages + IgnoreError(aMessage.Read(aMessage.GetOffset(), icmp)); VerifyOrExit(icmp.GetType() != Icmp::Header::kTypeEchoRequest, error = kErrorDrop); } @@ -1047,6 +1014,14 @@ Error Ip6::ProcessReceiveCallback(Message & aMessage, break; } +#if OPENTHREAD_CONFIG_TCP_ENABLE + // Do not pass TCP message to avoid dual processing from both + // OpenThread and POSIX TCP stacks. + case kProtoTcp: + error = kErrorNoRoute; + ExitNow(); +#endif + default: break; } @@ -1070,40 +1045,64 @@ Error Ip6::ProcessReceiveCallback(Message & aMessage, } IgnoreError(RemoveMplOption(*message)); - mReceiveIp6DatagramCallback(message, mReceiveIp6DatagramCallbackContext); -exit: +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + switch (Get().TranslateFromIp6(aMessage)) + { + case Nat64::Translator::kNotTranslated: + break; + + case Nat64::Translator::kDrop: + ExitNow(error = kErrorDrop); + + case Nat64::Translator::kForward: + VerifyOrExit(mReceiveIp4DatagramCallback.IsSet(), error = kErrorNoRoute); + // Pass message to callback transferring its ownership. + mReceiveIp4DatagramCallback.Invoke(message); + message = nullptr; + ExitNow(); + } +#endif - if ((error != kErrorNone) && (aMessageOwnership == Message::kTakeCustody)) + // Pass message to callback transferring its ownership. + mReceiveIp6DatagramCallback.Invoke(message); + message = nullptr; + +#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE { - aMessage.Free(); + Header header; + + IgnoreError(header.ParseFrom(aMessage)); + UpdateBorderRoutingCounters(header, aMessage.GetLength(), /* aIsInbound */ false); } +#endif +exit: + FreeMessage(message); return error; } -Error Ip6::SendRaw(Message &aMessage, bool aFromHost) +Error Ip6::SendRaw(Message &aMessage, bool aAllowLoopBackToHost) { - Error error = kErrorNone; - Header header; - MessageInfo messageInfo; - bool freed = false; + Error error = kErrorNone; + Header header; + bool freed = false; SuccessOrExit(error = header.ParseFrom(aMessage)); VerifyOrExit(!header.GetSource().IsMulticast(), error = kErrorInvalidSourceAddress); - messageInfo.SetPeerAddr(header.GetSource()); - messageInfo.SetSockAddr(header.GetDestination()); - messageInfo.SetHopLimit(header.GetHopLimit()); - if (header.GetDestination().IsMulticast()) { - SuccessOrExit(error = InsertMplOption(aMessage, header, messageInfo)); + SuccessOrExit(error = InsertMplOption(aMessage, header)); } - error = HandleDatagram(aMessage, nullptr, nullptr, aFromHost); + error = HandleDatagram(aMessage, aAllowLoopBackToHost ? kFromHostAllowLoopBack : kFromHostDisallowLoopBack); freed = true; +#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + UpdateBorderRoutingCounters(header, aMessage.GetLength(), /* aIsInbound */ true); +#endif + exit: if (!freed) @@ -1114,7 +1113,7 @@ Error Ip6::SendRaw(Message &aMessage, bool aFromHost) return error; } -Error Ip6::HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkMessageInfo, bool aFromHost) +Error Ip6::HandleDatagram(Message &aMessage, MessageOrigin aOrigin, const void *aLinkMessageInfo, bool aIsReassembled) { Error error; MessageInfo messageInfo; @@ -1140,100 +1139,99 @@ Error Ip6::HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkMes messageInfo.SetEcn(header.GetEcn()); messageInfo.SetLinkInfo(aLinkMessageInfo); - // determine destination of packet + // Determine `forwardThread`, `forwardHost` and `receive` + // based on the destination address. + if (header.GetDestination().IsMulticast()) { - Netif *netif; + // Destination is multicast - if (aNetif != nullptr) - { -#if OPENTHREAD_FTD - if (header.GetDestination().IsMulticastLargerThanRealmLocal() && - Get().HasSleepyChildWithAddress(header.GetDestination())) - { - forwardThread = true; - } -#endif + forwardThread = (aOrigin != kFromThreadNetif); - netif = aNetif; - } - else +#if OPENTHREAD_FTD + if ((aOrigin == kFromThreadNetif) && header.GetDestination().IsMulticastLargerThanRealmLocal() && + Get().HasSleepyChildWithAddress(header.GetDestination())) { forwardThread = true; - - netif = &Get(); } +#endif forwardHost = header.GetDestination().IsMulticastLargerThanRealmLocal(); - if ((aNetif != nullptr || aMessage.GetMulticastLoop()) && netif->IsMulticastSubscribed(header.GetDestination())) + if (((aOrigin == kFromThreadNetif) || aMessage.GetMulticastLoop()) && + Get().IsMulticastSubscribed(header.GetDestination())) { receive = true; } - else if (netif->IsMulticastPromiscuousEnabled()) + else if (Get().IsMulticastPromiscuousEnabled()) { forwardHost = true; } } else { - // unicast + // Destination is unicast + if (Get().HasUnicastAddress(header.GetDestination())) { receive = true; } - else if (!header.GetDestination().IsLinkLocal()) + else if ((aOrigin != kFromThreadNetif) || !header.GetDestination().IsLinkLocal()) { - forwardThread = true; - } - else if (aNetif == nullptr) - { - forwardThread = true; - } + if (header.GetDestination().IsLinkLocal()) + { + forwardThread = true; + } + else if (IsOnLink(header.GetDestination())) + { +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE + forwardThread = ((aOrigin == kFromHostDisallowLoopBack) || + !Get().ShouldForwardDuaToBackbone(header.GetDestination())); +#else + forwardThread = true; +#endif + } + else if (RouteLookup(header.GetSource(), header.GetDestination()) == kErrorNone) + { + forwardThread = true; + } - if (forwardThread && !ShouldForwardToThread(messageInfo, aFromHost)) - { - forwardThread = false; - forwardHost = true; + forwardHost = !forwardThread; } } aMessage.SetOffset(sizeof(header)); - // process IPv6 Extension Headers + // Process IPv6 Extension Headers nextHeader = static_cast(header.GetNextHeader()); - SuccessOrExit(error = HandleExtensionHeaders(aMessage, aNetif, messageInfo, header, nextHeader, aNetif == nullptr, - aFromHost, receive)); + SuccessOrExit(error = HandleExtensionHeaders(aMessage, aOrigin, messageInfo, header, nextHeader, receive)); - // process IPv6 Payload - if (receive) + if (receive && (nextHeader == kProtoIp6)) { - if (nextHeader == kProtoIp6) - { - // Remove encapsulating header and start over. - aMessage.RemoveHeader(aMessage.GetOffset()); - Get().LogMessage(MeshForwarder::kMessageReceive, aMessage); - goto start; - } - - error = ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromHost, - /* aAllowReceiveFilter */ !forwardHost, Message::kCopyToUse); + // Remove encapsulating header and start over. + aMessage.RemoveHeader(aMessage.GetOffset()); + Get().LogMessage(MeshForwarder::kMessageReceive, aMessage); + goto start; + } - if ((error == kErrorNone || error == kErrorNoRoute) && forwardHost) - { - forwardHost = false; - } + if ((forwardHost || receive) && !aIsReassembled) + { + error = PassToHost(aMessage, aOrigin, messageInfo, nextHeader, + /* aApplyFilter */ !forwardHost, + (receive || forwardThread) ? Message::kCopyToUse : Message::kTakeCustody); - error = HandlePayload(header, aMessage, messageInfo, nextHeader, - (forwardThread || forwardHost ? Message::kCopyToUse : Message::kTakeCustody)); - shouldFreeMessage = forwardThread || forwardHost; + // Need to free the message if we did not pass its + // ownership in the call to `PassToHost()` + shouldFreeMessage = (receive || forwardThread); } - if (forwardHost) + if (receive) { - // try passing to host - error = ProcessReceiveCallback(aMessage, messageInfo, nextHeader, aFromHost, /* aAllowReceiveFilter */ false, - forwardThread ? Message::kCopyToUse : Message::kTakeCustody); + error = HandlePayload(header, aMessage, messageInfo, nextHeader, + forwardThread ? Message::kCopyToUse : Message::kTakeCustody); + + // Need to free the message if we did not pass its + // ownership in the call to `HandlePayload()` shouldFreeMessage = forwardThread; } @@ -1241,9 +1239,9 @@ Error Ip6::HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkMes { uint8_t hopLimit; - if (aNetif != nullptr) + if (aOrigin == kFromThreadNetif) { - VerifyOrExit(mForwardingEnabled); + VerifyOrExit(Get().IsRouterOrLeader()); header.SetHopLimit(header.GetHopLimit() - 1); } @@ -1270,7 +1268,7 @@ Error Ip6::HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkMes } #if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE - if (aFromHost && (nextHeader == kProtoUdp)) + if ((aOrigin == kFromHostDisallowLoopBack) && (nextHeader == kProtoUdp)) { uint16_t destPort; @@ -1293,7 +1291,7 @@ Error Ip6::HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkMes #endif // `SendMessage()` takes custody of message in the success case - SuccessOrExit(error = Get().SendMessage(aMessage)); + SuccessOrExit(error = Get().SendMessage(aMessage)); shouldFreeMessage = false; } @@ -1307,122 +1305,105 @@ Error Ip6::HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkMes return error; } -bool Ip6::ShouldForwardToThread(const MessageInfo &aMessageInfo, bool aFromHost) const +Error Ip6::SelectSourceAddress(MessageInfo &aMessageInfo) const { - bool shouldForward = false; + Error error = kErrorNone; + const Address *source; - if (aMessageInfo.GetSockAddr().IsMulticast() || aMessageInfo.GetSockAddr().IsLinkLocal()) - { - shouldForward = true; - } - else if (IsOnLink(aMessageInfo.GetSockAddr())) - { -#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE - shouldForward = - (aFromHost || !Get().ShouldForwardDuaToBackbone(aMessageInfo.GetSockAddr())); -#else - OT_UNUSED_VARIABLE(aFromHost); - shouldForward = true; -#endif - } - else if (Get().RouteLookup(aMessageInfo.GetPeerAddr(), aMessageInfo.GetSockAddr(), nullptr) == - kErrorNone) - { - shouldForward = true; - } + source = SelectSourceAddress(aMessageInfo.GetPeerAddr()); + VerifyOrExit(source != nullptr, error = kErrorNotFound); + aMessageInfo.SetSockAddr(*source); - return shouldForward; +exit: + return error; } -const Netif::UnicastAddress *Ip6::SelectSourceAddress(MessageInfo &aMessageInfo) +const Address *Ip6::SelectSourceAddress(const Address &aDestination) const { - Address * destination = &aMessageInfo.GetPeerAddr(); - uint8_t destinationScope = destination->GetScope(); - const bool destinationIsRoutingLocator = Get().IsRoutingLocator(*destination); - const Netif::UnicastAddress *rvalAddr = nullptr; - uint8_t rvalPrefixMatched = 0; + uint8_t destScope = aDestination.GetScope(); + bool destIsRloc = Get().IsRoutingLocator(aDestination); + const Netif::UnicastAddress *bestAddr = nullptr; + uint8_t bestMatchLen = 0; for (const Netif::UnicastAddress &addr : Get().GetUnicastAddresses()) { - const Address *candidateAddr = &addr.GetAddress(); - uint8_t candidatePrefixMatched; - uint8_t overrideScope; + uint8_t matchLen; + uint8_t overrideScope; - if (Get().IsAnycastLocator(*candidateAddr)) + if (Get().IsAnycastLocator(addr.GetAddress())) { // Don't use anycast address as source address. continue; } - candidatePrefixMatched = destination->PrefixMatch(*candidateAddr); + matchLen = aDestination.PrefixMatch(addr.GetAddress()); - if (candidatePrefixMatched >= addr.mPrefixLength) + if (matchLen >= addr.mPrefixLength) { - candidatePrefixMatched = addr.mPrefixLength; - overrideScope = addr.GetScope(); + matchLen = addr.mPrefixLength; + overrideScope = addr.GetScope(); } else { - overrideScope = destinationScope; + overrideScope = destScope; } - if (rvalAddr == nullptr) + if (bestAddr == nullptr) { // Rule 0: Prefer any address - rvalAddr = &addr; - rvalPrefixMatched = candidatePrefixMatched; + bestAddr = &addr; + bestMatchLen = matchLen; } - else if (*candidateAddr == *destination) + else if (addr.GetAddress() == aDestination) { // Rule 1: Prefer same address - rvalAddr = &addr; + bestAddr = &addr; ExitNow(); } - else if (addr.GetScope() < rvalAddr->GetScope()) + else if (addr.GetScope() < bestAddr->GetScope()) { // Rule 2: Prefer appropriate scope if (addr.GetScope() >= overrideScope) { - rvalAddr = &addr; - rvalPrefixMatched = candidatePrefixMatched; + bestAddr = &addr; + bestMatchLen = matchLen; } else { continue; } } - else if (addr.GetScope() > rvalAddr->GetScope()) + else if (addr.GetScope() > bestAddr->GetScope()) { - if (rvalAddr->GetScope() < overrideScope) + if (bestAddr->GetScope() < overrideScope) { - rvalAddr = &addr; - rvalPrefixMatched = candidatePrefixMatched; + bestAddr = &addr; + bestMatchLen = matchLen; } else { continue; } } - else if (addr.mPreferred && !rvalAddr->mPreferred) + else if (addr.mPreferred && !bestAddr->mPreferred) { // Rule 3: Avoid deprecated addresses - rvalAddr = &addr; - rvalPrefixMatched = candidatePrefixMatched; + bestAddr = &addr; + bestMatchLen = matchLen; } - else if (candidatePrefixMatched > rvalPrefixMatched) + else if (matchLen > bestMatchLen) { // Rule 6: Prefer matching label // Rule 7: Prefer public address // Rule 8: Use longest prefix matching - rvalAddr = &addr; - rvalPrefixMatched = candidatePrefixMatched; + bestAddr = &addr; + bestMatchLen = matchLen; } - else if ((candidatePrefixMatched == rvalPrefixMatched) && - (destinationIsRoutingLocator == Get().IsRoutingLocator(*candidateAddr))) + else if ((matchLen == bestMatchLen) && (destIsRloc == Get().IsRoutingLocator(addr.GetAddress()))) { // Additional rule: Prefer RLOC source for RLOC destination, EID source for anything else - rvalAddr = &addr; - rvalPrefixMatched = candidatePrefixMatched; + bestAddr = &addr; + bestMatchLen = matchLen; } else { @@ -1430,37 +1411,108 @@ const Netif::UnicastAddress *Ip6::SelectSourceAddress(MessageInfo &aMessageInfo) } // infer destination scope based on prefix match - if (rvalPrefixMatched >= rvalAddr->mPrefixLength) + if (bestMatchLen >= bestAddr->mPrefixLength) { - destinationScope = rvalAddr->GetScope(); + destScope = bestAddr->GetScope(); } } exit: - return rvalAddr; + return (bestAddr != nullptr) ? &bestAddr->GetAddress() : nullptr; } bool Ip6::IsOnLink(const Address &aAddress) const { - bool rval = false; + bool isOnLink = false; - if (Get().IsOnMesh(aAddress)) + if (Get().IsOnMesh(aAddress)) { - ExitNow(rval = true); + ExitNow(isOnLink = true); } - for (const Netif::UnicastAddress &cur : Get().GetUnicastAddresses()) + for (const Netif::UnicastAddress &unicastAddr : Get().GetUnicastAddresses()) { - if (cur.GetAddress().PrefixMatch(aAddress) >= cur.mPrefixLength) + if (unicastAddr.GetAddress().PrefixMatch(aAddress) >= unicastAddr.mPrefixLength) { - ExitNow(rval = true); + ExitNow(isOnLink = true); } } exit: - return rval; + return isOnLink; +} + +Error Ip6::RouteLookup(const Address &aSource, const Address &aDestination) const +{ + Error error; + uint16_t rloc; + + error = Get().RouteLookup(aSource, aDestination, rloc); + + if (error == kErrorNone) + { + if (rloc == Get().GetRloc16()) + { + error = kErrorNoRoute; + } + } + else + { + LogNote("Failed to find valid route for: %s", aDestination.ToString().AsCString()); + } + + return error; } +#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE +void Ip6::UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLength, bool aIsInbound) +{ + otPacketsAndBytes *counter = nullptr; + + VerifyOrExit(!aHeader.GetSource().IsLinkLocal()); + VerifyOrExit(!aHeader.GetDestination().IsLinkLocal()); + VerifyOrExit(aHeader.GetSource().GetPrefix() != Get().GetMeshLocalPrefix()); + VerifyOrExit(aHeader.GetDestination().GetPrefix() != Get().GetMeshLocalPrefix()); + + if (aIsInbound) + { + VerifyOrExit(!Get().HasUnicastAddress(aHeader.GetSource())); + + if (aHeader.GetDestination().IsMulticast()) + { + VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal()); + counter = &mBorderRoutingCounters.mInboundMulticast; + } + else + { + counter = &mBorderRoutingCounters.mInboundUnicast; + } + } + else + { + VerifyOrExit(!Get().HasUnicastAddress(aHeader.GetDestination())); + + if (aHeader.GetDestination().IsMulticast()) + { + VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal()); + counter = &mBorderRoutingCounters.mOutboundMulticast; + } + else + { + counter = &mBorderRoutingCounters.mOutboundUnicast; + } + } + +exit: + + if (counter) + { + counter->mPackets += 1; + counter->mBytes += aMessageLength; + } +} +#endif + // LCOV_EXCL_START const char *Ip6::IpProtoToString(uint8_t aIpProto) @@ -1527,12 +1579,9 @@ Error Headers::ParseFrom(const Message &aMessage) return error; } -Error Headers::DecompressFrom(const Message & aMessage, - uint16_t aOffset, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest) +Error Headers::DecompressFrom(const Message &aMessage, uint16_t aOffset, const Mac::Addresses &aMacAddrs) { - static constexpr uint16_t kReadLength = Lowpan::FragmentHeader::kSubsequentFragmentHeaderSize + sizeof(Headers); + static constexpr uint16_t kReadLength = sizeof(Lowpan::FragmentHeader::NextFrag) + sizeof(Headers); uint8_t frameBuffer[kReadLength]; uint16_t frameLength; @@ -1541,13 +1590,10 @@ Error Headers::DecompressFrom(const Message & aMessage, frameLength = aMessage.ReadBytes(aOffset, frameBuffer, sizeof(frameBuffer)); frameData.Init(frameBuffer, frameLength); - return DecompressFrom(frameData, aMacSource, aMacDest, aMessage.GetInstance()); + return DecompressFrom(frameData, aMacAddrs, aMessage.GetInstance()); } -Error Headers::DecompressFrom(const FrameData & aFrameData, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - Instance & aInstance) +Error Headers::DecompressFrom(const FrameData &aFrameData, const Mac::Addresses &aMacAddrs, Instance &aInstance) { Error error = kErrorNone; FrameData frameData = aFrameData; @@ -1563,7 +1609,7 @@ Error Headers::DecompressFrom(const FrameData & aFrameData, VerifyOrExit(Lowpan::Lowpan::IsLowpanHc(frameData), error = kErrorNotFound); SuccessOrExit(error = aInstance.Get().DecompressBaseHeader(mIp6Header, nextHeaderCompressed, - aMacSource, aMacDest, frameData)); + aMacAddrs, frameData)); switch (mIp6Header.GetNextHeader()) { diff --git a/src/core/net/ip6.hpp b/src/core/net/ip6.hpp index 5e695ccafce..aafb09ce856 100644 --- a/src/core/net/ip6.hpp +++ b/src/core/net/ip6.hpp @@ -39,8 +39,10 @@ #include #include +#include #include +#include "common/callback.hpp" #include "common/encoding.hpp" #include "common/frame_data.hpp" #include "common/locator.hpp" @@ -111,6 +113,20 @@ class Ip6 : public InstanceLocator, private NonCopyable friend class Mpl; public: + /** + * This enumeration represents an IPv6 message origin. + * + * In case the message is originating from host, it also indicates whether or not it is allowed to passed back the + * message to the host. + * + */ + enum MessageOrigin : uint8_t + { + kFromThreadNetif, ///< Message originates from Thread Netif. + kFromHostDisallowLoopBack, ///< Message originates from host and should not be passed back to host. + kFromHostAllowLoopBack, ///< Message originates from host and can be passed back to host. + }; + /** * This constructor initializes the object. * @@ -120,42 +136,51 @@ class Ip6 : public InstanceLocator, private NonCopyable explicit Ip6(Instance &aInstance); /** - * This method allocates a new message buffer from the buffer pool. + * This method allocates a new message buffer from the buffer pool with default settings (link security + * enabled and `kPriorityMedium`). + * + * @returns A pointer to the message or `nullptr` if insufficient message buffers are available. + * + */ + Message *NewMessage(void); + + /** + * This method allocates a new message buffer from the buffer pool with default settings (link security + * enabled and `kPriorityMedium`). * * @param[in] aReserved The number of header bytes to reserve following the IPv6 header. - * @param[in] aSettings The message settings. * * @returns A pointer to the message or `nullptr` if insufficient message buffers are available. * */ - Message *NewMessage(uint16_t aReserved, const Message::Settings &aSettings = Message::Settings::GetDefault()); + Message *NewMessage(uint16_t aReserved); /** - * This method allocates a new message buffer from the buffer pool and writes the IPv6 datagram to the message. + * This method allocates a new message buffer from the buffer pool. * - * @param[in] aData A pointer to the IPv6 datagram buffer. - * @param[in] aDataLength The size of the IPV6 datagram buffer pointed by @p aData. - * @param[in] aSettings The message settings. + * @param[in] aReserved The number of header bytes to reserve following the IPv6 header. + * @param[in] aSettings The message settings. * - * @returns A pointer to the message or `nullptr` if malformed IPv6 header or insufficient message buffers are - * available. + * @returns A pointer to the message or `nullptr` if insufficient message buffers are available. * */ - Message *NewMessage(const uint8_t *aData, uint16_t aDataLength, const Message::Settings &aSettings); + Message *NewMessage(uint16_t aReserved, const Message::Settings &aSettings); /** * This method allocates a new message buffer from the buffer pool and writes the IPv6 datagram to the message. * - * @note The link layer security is enabled and the message priority is obtained from IPv6 message itself. + * The message priority is always determined from IPv6 message itself (@p aData) and the priority included in + * @p aSetting is ignored. * * @param[in] aData A pointer to the IPv6 datagram buffer. * @param[in] aDataLength The size of the IPV6 datagram buffer pointed by @p aData. + * @param[in] aSettings The message settings. * * @returns A pointer to the message or `nullptr` if malformed IPv6 header or insufficient message buffers are - * available. + * available. * */ - Message *NewMessage(const uint8_t *aData, uint16_t aDataLength); + Message *NewMessageFromData(const uint8_t *aData, uint16_t aDataLength, const Message::Settings &aSettings); /** * This method converts the IPv6 DSCP value to message priority level. @@ -186,8 +211,8 @@ class Ip6 : public InstanceLocator, private NonCopyable * The caller transfers ownership of @p aMessage when making this call. OpenThread will free @p aMessage when * processing is complete, including when a value other than `kErrorNone` is returned. * - * @param[in] aMessage A reference to the message. - * @param[in] aFromHost TRUE if the message is originated from the host, FALSE otherwise. + * @param[in] aMessage A reference to the message. + * @param[in] aAllowLoopBackToHost Indicate whether or not the message is allowed to be passed back to host. * * @retval kErrorNone Successfully processed the message. * @retval kErrorDrop Message was well-formed but not fully processed due to packet processing rules. @@ -196,15 +221,14 @@ class Ip6 : public InstanceLocator, private NonCopyable * @retval kErrorParse Encountered a malformed header when processing the message. * */ - Error SendRaw(Message &aMessage, bool aFromHost); + Error SendRaw(Message &aMessage, bool aAllowLoopBackToHost); /** * This method processes a received IPv6 datagram. * * @param[in] aMessage A reference to the message. - * @param[in] aNetif A pointer to the network interface that received the message. + * @param[in] aOrigin The message oirgin. * @param[in] aLinkMessageInfo A pointer to link-specific message information. - * @param[in] aFromHost TRUE if the message is originated from the host, FALSE otherwise. * * @retval kErrorNone Successfully processed the message. * @retval kErrorDrop Message was well-formed but not fully processed due to packet processing rules. @@ -213,7 +237,10 @@ class Ip6 : public InstanceLocator, private NonCopyable * @retval kErrorParse Encountered a malformed header when processing the message. * */ - Error HandleDatagram(Message &aMessage, Netif *aNetif, const void *aLinkMessageInfo, bool aFromHost); + Error HandleDatagram(Message &aMessage, + MessageOrigin aOrigin, + const void *aLinkMessageInfo = nullptr, + bool aIsReassembled = false); /** * This method registers a callback to provide received raw IPv6 datagrams. @@ -229,7 +256,27 @@ class Ip6 : public InstanceLocator, private NonCopyable * @sa SetReceiveIp6FilterEnabled * */ - void SetReceiveDatagramCallback(otIp6ReceiveCallback aCallback, void *aCallbackContext); + void SetReceiveDatagramCallback(otIp6ReceiveCallback aCallback, void *aCallbackContext) + { + mReceiveIp6DatagramCallback.Set(aCallback, aCallbackContext); + } + +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + /** + * This method registers a callback to provide received translated IPv4 datagrams. + * + * @param[in] aCallback A pointer to a function that is called when a translated IPv4 datagram is received + * or `nullptr` to disable the callback. + * @param[in] aCallbackContext A pointer to application-specific context. + * + * @sa SetReceiveDatagramCallback + * + */ + void SetNat64ReceiveIp4DatagramCallback(otNat64ReceiveIp4Callback aCallback, void *aCallbackContext) + { + mReceiveIp4DatagramCallback.Set(aCallback, aCallbackContext); + } +#endif /** * This method indicates whether or not Thread control traffic is filtered out when delivering IPv6 datagrams @@ -256,30 +303,25 @@ class Ip6 : public InstanceLocator, private NonCopyable void SetReceiveIp6FilterEnabled(bool aEnabled) { mIsReceiveIp6FilterEnabled = aEnabled; } /** - * This method indicates whether or not IPv6 forwarding is enabled. - * - * @returns TRUE if IPv6 forwarding is enabled, FALSE otherwise. + * This method performs default source address selection. * - */ - bool IsForwardingEnabled(void) const { return mForwardingEnabled; } - - /** - * This method enables/disables IPv6 forwarding. + * @param[in,out] aMessageInfo A reference to the message information. * - * @param[in] aEnable TRUE to enable IPv6 forwarding, FALSE otherwise. + * @retval kErrorNone Found a source address and updated SockAddr of @p aMessageInfo. + * @retval kErrorNotFound No source address was found and @p aMessageInfo is unchanged. * */ - void SetForwardingEnabled(bool aEnable) { mForwardingEnabled = aEnable; } + Error SelectSourceAddress(MessageInfo &aMessageInfo) const; /** - * This method perform default source address selection. + * This method performs default source address selection. * - * @param[in] aMessageInfo A reference to the message information. + * @param[in] aDestination The destination address. * * @returns A pointer to the selected IPv6 source address or `nullptr` if no source address was found. * */ - const Netif::UnicastAddress *SelectSourceAddress(MessageInfo &aMessageInfo); + const Address *SelectSourceAddress(const Address &aDestination) const; /** * This method returns a reference to the send queue. @@ -309,35 +351,55 @@ class Ip6 : public InstanceLocator, private NonCopyable */ static const char *EcnToString(Ecn aEcn); +#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + /** + * This method returns a reference to the Border Routing counters. + * + * @returns A reference to the Border Routing counters. + * + */ + const otBorderRoutingCounters &GetBorderRoutingCounters(void) const { return mBorderRoutingCounters; } + + /** + * This method returns a reference to the Border Routing counters. + * + * @returns A reference to the Border Routing counters. + * + */ + otBorderRoutingCounters &GetBorderRoutingCounters(void) { return mBorderRoutingCounters; } + + /** + * This method resets the Border Routing counters. + * + */ + void ResetBorderRoutingCounters(void) { memset(&mBorderRoutingCounters, 0, sizeof(mBorderRoutingCounters)); } +#endif + private: static constexpr uint8_t kDefaultHopLimit = OPENTHREAD_CONFIG_IP6_HOP_LIMIT_DEFAULT; static constexpr uint8_t kIp6ReassemblyTimeout = OPENTHREAD_CONFIG_IP6_REASSEMBLY_TIMEOUT; static constexpr uint16_t kMinimalMtu = 1280; - static void HandleSendQueue(Tasklet &aTasklet); - void HandleSendQueue(void); + void HandleSendQueue(void); static uint8_t PriorityToDscp(Message::Priority aPriority); - static Error GetDatagramPriority(const uint8_t *aData, uint16_t aDataLen, Message::Priority &aPriority); void EnqueueDatagram(Message &aMessage); - Error ProcessReceiveCallback(Message & aMessage, - const MessageInfo &aMessageInfo, - uint8_t aIpProto, - bool aFromHost, - bool aAllowReceiveFilter, - Message::Ownership aMessageOwnership); - Error HandleExtensionHeaders(Message & aMessage, - Netif * aNetif, - MessageInfo &aMessageInfo, - Header & aHeader, - uint8_t & aNextHeader, - bool aIsOutbound, - bool aFromHost, - bool & aReceive); + Error PassToHost(Message &aMessage, + MessageOrigin aOrigin, + const MessageInfo &aMessageInfo, + uint8_t aIpProto, + bool aApplyFilter, + Message::Ownership aMessageOwnership); + Error HandleExtensionHeaders(Message &aMessage, + MessageOrigin aOrigin, + MessageInfo &aMessageInfo, + Header &aHeader, + uint8_t &aNextHeader, + bool &aReceive); Error FragmentDatagram(Message &aMessage, uint8_t aIpProto); - Error HandleFragment(Message &aMessage, Netif *aNetif, MessageInfo &aMessageInfo, bool aFromHost); + Error HandleFragment(Message &aMessage, MessageOrigin aOrigin, MessageInfo &aMessageInfo); #if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE void CleanupFragmentationBuffer(void); void HandleTimeTick(void); @@ -345,25 +407,33 @@ class Ip6 : public InstanceLocator, private NonCopyable void SendIcmpError(Message &aMessage, Icmp::Header::Type aIcmpType, Icmp::Header::Code aIcmpCode); #endif Error AddMplOption(Message &aMessage, Header &aHeader); - Error AddTunneledMplOption(Message &aMessage, Header &aHeader, MessageInfo &aMessageInfo); - Error InsertMplOption(Message &aMessage, Header &aHeader, MessageInfo &aMessageInfo); + Error AddTunneledMplOption(Message &aMessage, Header &aHeader); + Error InsertMplOption(Message &aMessage, Header &aHeader); Error RemoveMplOption(Message &aMessage); Error HandleOptions(Message &aMessage, Header &aHeader, bool aIsOutbound, bool &aReceive); - Error HandlePayload(Header & aIp6Header, - Message & aMessage, - MessageInfo & aMessageInfo, + Error HandlePayload(Header &aIp6Header, + Message &aMessage, + MessageInfo &aMessageInfo, uint8_t aIpProto, Message::Ownership aMessageOwnership); - bool ShouldForwardToThread(const MessageInfo &aMessageInfo, bool aFromHost) const; bool IsOnLink(const Address &aAddress) const; + Error RouteLookup(const Address &aSource, const Address &aDestination) const; +#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + void UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLength, bool aIsInbound); +#endif + + using SendQueueTask = TaskletIn; + + bool mIsReceiveIp6FilterEnabled; + + Callback mReceiveIp6DatagramCallback; - bool mForwardingEnabled; - bool mIsReceiveIp6FilterEnabled; - otIp6ReceiveCallback mReceiveIp6DatagramCallback; - void * mReceiveIp6DatagramCallbackContext; +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + Callback mReceiveIp4DatagramCallback; +#endif PriorityQueue mSendQueue; - Tasklet mSendQueueTask; + SendQueueTask mSendQueueTask; Icmp mIcmp; Udp mUdp; @@ -376,6 +446,10 @@ class Ip6 : public InstanceLocator, private NonCopyable #if OPENTHREAD_CONFIG_IP6_FRAGMENTATION_ENABLE MessageQueue mReassemblyList; #endif + +#if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + otBorderRoutingCounters mBorderRoutingCounters; +#endif }; /** @@ -384,6 +458,8 @@ class Ip6 : public InstanceLocator, private NonCopyable */ class Headers : private Clearable { + friend class Clearable; + public: /** * This method parses the IPv6 and UDP/TCP/ICMP6 headers from a given message. @@ -401,25 +477,20 @@ class Headers : private Clearable * * @param[in] aMessage The message from which to read the lowpan frame. * @param[in] aOffset The offset in @p aMessage to start reading the frame. - * @param[in] aMacSource The MAC source address. - * @param[in] aMacDest The MAC destination address. + * @param[in] aMacAddrs The MAC source and destination addresses. * * @retval kErrorNone Successfully decompressed and parsed IPv6 and UDP/TCP/ICMP6 headers. * @retval kErrorNotFound Lowpan frame is a next fragment and does not contain IPv6 headers. * @retval kErrorParse Failed to parse the headers. * */ - Error DecompressFrom(const Message & aMessage, - uint16_t aOffset, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest); + Error DecompressFrom(const Message &aMessage, uint16_t aOffset, const Mac::Addresses &aMacAddrs); /** * This method decompresses lowpan frame and parses the IPv6 and UDP/TCP/ICMP6 headers. * * @param[in] aFrameData The lowpan frame data. - * @param[in] aMacSource The MAC source address. - * @param[in] aMacDest The MAC destination address. + * @param[in] aMacAddrs The MAC source and destination addresses. * @param[in] aInstance The OpenThread instance. * * @retval kErrorNone Successfully decompressed and parsed IPv6 and UDP/TCP/ICMP6 headers. @@ -427,10 +498,7 @@ class Headers : private Clearable * @retval kErrorParse Failed to parse the headers. * */ - Error DecompressFrom(const FrameData & aFrameData, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - Instance & aInstance); + Error DecompressFrom(const FrameData &aFrameData, const Mac::Addresses &aMacAddrs, Instance &aInstance); /** * This method returns the IPv6 header. diff --git a/src/core/net/ip6_address.cpp b/src/core/net/ip6_address.cpp index 7f50c941c10..58ac92d7c51 100644 --- a/src/core/net/ip6_address.cpp +++ b/src/core/net/ip6_address.cpp @@ -40,6 +40,7 @@ #include "common/code_utils.hpp" #include "common/encoding.hpp" #include "common/instance.hpp" +#include "common/num_utils.hpp" #include "common/numeric_limits.hpp" #include "common/random.hpp" #include "net/ip4_types.hpp" @@ -69,18 +70,44 @@ void Prefix::Set(const uint8_t *aPrefix, uint8_t aLength) mLength = aLength; } +bool Prefix::IsLinkLocal(void) const +{ + return (mLength >= 10) && ((mPrefix.mFields.m16[0] & HostSwap16(0xffc0)) == HostSwap16(0xfe80)); +} + +bool Prefix::IsMulticast(void) const { return (mLength >= 8) && (mPrefix.mFields.m8[0] == 0xff); } + +bool Prefix::IsUniqueLocal(void) const { return (mLength >= 7) && ((mPrefix.mFields.m8[0] & 0xfe) == 0xfc); } + bool Prefix::IsEqual(const uint8_t *aPrefixBytes, uint8_t aPrefixLength) const { return (mLength == aPrefixLength) && (MatchLength(GetBytes(), aPrefixBytes, GetBytesSize()) >= mLength); } +bool Prefix::ContainsPrefix(const Prefix &aSubPrefix) const +{ + return (mLength >= aSubPrefix.mLength) && + (MatchLength(GetBytes(), aSubPrefix.GetBytes(), aSubPrefix.GetBytesSize()) >= aSubPrefix.GetLength()); +} + +bool Prefix::ContainsPrefix(const NetworkPrefix &aSubPrefix) const +{ + return (mLength >= NetworkPrefix::kLength) && + (MatchLength(GetBytes(), aSubPrefix.m8, NetworkPrefix::kSize) >= NetworkPrefix::kLength); +} + +bool Prefix::operator==(const Prefix &aOther) const +{ + return (mLength == aOther.mLength) && (MatchLength(GetBytes(), aOther.GetBytes(), GetBytesSize()) >= GetLength()); +} + bool Prefix::operator<(const Prefix &aOther) const { bool isSmaller; uint8_t minLength; uint8_t matchedLength; - minLength = OT_MIN(GetLength(), aOther.GetLength()); + minLength = Min(GetLength(), aOther.GetLength()); matchedLength = MatchLength(GetBytes(), aOther.GetBytes(), SizeForLength(minLength)); if (matchedLength >= minLength) @@ -130,6 +157,31 @@ bool Prefix::IsValidNat64PrefixLength(uint8_t aLength) (aLength == 96); } +Error Prefix::FromString(const char *aString) +{ + constexpr char kSlashChar = '/'; + constexpr char kNullChar = '\0'; + + Error error = kErrorParse; + const char *cur; + + VerifyOrExit(aString != nullptr); + + cur = StringFind(aString, kSlashChar); + VerifyOrExit(cur != nullptr); + + SuccessOrExit(AsCoreType(&mPrefix).ParseFrom(aString, kSlashChar)); + + cur++; + SuccessOrExit(StringParseUint8(cur, mLength, kMaxLength)); + VerifyOrExit(*cur == kNullChar); + + error = kErrorNone; + +exit: + return error; +} + Prefix::InfoString Prefix::ToString(void) const { InfoString string; @@ -163,20 +215,14 @@ void Prefix::ToString(StringWriter &aWriter) const //--------------------------------------------------------------------------------------------------------------------- // InterfaceIdentifier methods -bool InterfaceIdentifier::IsUnspecified(void) const -{ - return (mFields.m32[0] == 0) && (mFields.m32[1] == 0); -} +bool InterfaceIdentifier::IsUnspecified(void) const { return (mFields.m32[0] == 0) && (mFields.m32[1] == 0); } bool InterfaceIdentifier::IsReserved(void) const { return IsSubnetRouterAnycast() || IsReservedSubnetAnycast() || IsAnycastLocator(); } -bool InterfaceIdentifier::IsSubnetRouterAnycast(void) const -{ - return (mFields.m32[0] == 0) && (mFields.m32[1] == 0); -} +bool InterfaceIdentifier::IsSubnetRouterAnycast(void) const { return (mFields.m32[0] == 0) && (mFields.m32[1] == 0); } bool InterfaceIdentifier::IsReservedSubnetAnycast(void) const { @@ -191,15 +237,9 @@ bool InterfaceIdentifier::IsReservedSubnetAnycast(void) const mFields.m8[7] >= 0x80); } -void InterfaceIdentifier::GenerateRandom(void) -{ - SuccessOrAssert(Random::Crypto::FillBuffer(mFields.m8, kSize)); -} +void InterfaceIdentifier::GenerateRandom(void) { SuccessOrAssert(Random::Crypto::FillBuffer(mFields.m8, kSize)); } -void InterfaceIdentifier::SetBytes(const uint8_t *aBuffer) -{ - memcpy(mFields.m8, aBuffer, kSize); -} +void InterfaceIdentifier::SetBytes(const uint8_t *aBuffer) { memcpy(mFields.m8, aBuffer, kSize); } void InterfaceIdentifier::SetFromExtAddress(const Mac::ExtAddress &aExtAddress) { @@ -254,6 +294,15 @@ bool InterfaceIdentifier::IsAnycastServiceLocator(void) const return (IsLocator() && (locator >= Mle::kAloc16ServiceStart) && (locator <= Mle::kAloc16ServiceEnd)); } +void InterfaceIdentifier::ApplyPrefix(const Prefix &aPrefix) +{ + if (aPrefix.GetLength() > NetworkPrefix::kLength) + { + Address::CopyBits(mFields.m8, aPrefix.GetBytes() + NetworkPrefix::kSize, + aPrefix.GetLength() - NetworkPrefix::kLength); + } +} + InterfaceIdentifier::InfoString InterfaceIdentifier::ToString(void) const { InfoString string; @@ -276,10 +325,7 @@ bool Address::IsLoopback(void) const return (mFields.m32[0] == 0 && mFields.m32[1] == 0 && mFields.m32[2] == 0 && mFields.m32[3] == HostSwap32(1)); } -bool Address::IsLinkLocal(void) const -{ - return (mFields.m16[0] & HostSwap16(0xffc0)) == HostSwap16(0xfe80); -} +bool Address::IsLinkLocal(void) const { return (mFields.m16[0] & HostSwap16(0xffc0)) == HostSwap16(0xfe80); } void Address::SetToLinkLocalAddress(const Mac::ExtAddress &aExtAddress) { @@ -295,70 +341,31 @@ void Address::SetToLinkLocalAddress(const InterfaceIdentifier &aIid) SetIid(aIid); } -bool Address::IsLinkLocalMulticast(void) const -{ - return IsMulticast() && (GetScope() == kLinkLocalScope); -} +bool Address::IsLinkLocalMulticast(void) const { return IsMulticast() && (GetScope() == kLinkLocalScope); } -bool Address::IsLinkLocalAllNodesMulticast(void) const -{ - return (*this == GetLinkLocalAllNodesMulticast()); -} +bool Address::IsLinkLocalAllNodesMulticast(void) const { return (*this == GetLinkLocalAllNodesMulticast()); } -void Address::SetToLinkLocalAllNodesMulticast(void) -{ - *this = GetLinkLocalAllNodesMulticast(); -} +void Address::SetToLinkLocalAllNodesMulticast(void) { *this = GetLinkLocalAllNodesMulticast(); } -bool Address::IsLinkLocalAllRoutersMulticast(void) const -{ - return (*this == GetLinkLocalAllRoutersMulticast()); -} +bool Address::IsLinkLocalAllRoutersMulticast(void) const { return (*this == GetLinkLocalAllRoutersMulticast()); } -void Address::SetToLinkLocalAllRoutersMulticast(void) -{ - *this = GetLinkLocalAllRoutersMulticast(); -} +void Address::SetToLinkLocalAllRoutersMulticast(void) { *this = GetLinkLocalAllRoutersMulticast(); } -bool Address::IsRealmLocalMulticast(void) const -{ - return IsMulticast() && (GetScope() == kRealmLocalScope); -} +bool Address::IsRealmLocalMulticast(void) const { return IsMulticast() && (GetScope() == kRealmLocalScope); } -bool Address::IsMulticastLargerThanRealmLocal(void) const -{ - return IsMulticast() && (GetScope() > kRealmLocalScope); -} +bool Address::IsMulticastLargerThanRealmLocal(void) const { return IsMulticast() && (GetScope() > kRealmLocalScope); } -bool Address::IsRealmLocalAllNodesMulticast(void) const -{ - return (*this == GetRealmLocalAllNodesMulticast()); -} +bool Address::IsRealmLocalAllNodesMulticast(void) const { return (*this == GetRealmLocalAllNodesMulticast()); } -void Address::SetToRealmLocalAllNodesMulticast(void) -{ - *this = GetRealmLocalAllNodesMulticast(); -} +void Address::SetToRealmLocalAllNodesMulticast(void) { *this = GetRealmLocalAllNodesMulticast(); } -bool Address::IsRealmLocalAllRoutersMulticast(void) const -{ - return (*this == GetRealmLocalAllRoutersMulticast()); -} +bool Address::IsRealmLocalAllRoutersMulticast(void) const { return (*this == GetRealmLocalAllRoutersMulticast()); } -void Address::SetToRealmLocalAllRoutersMulticast(void) -{ - *this = GetRealmLocalAllRoutersMulticast(); -} +void Address::SetToRealmLocalAllRoutersMulticast(void) { *this = GetRealmLocalAllRoutersMulticast(); } -bool Address::IsRealmLocalAllMplForwarders(void) const -{ - return (*this == GetRealmLocalAllMplForwarders()); -} +bool Address::IsRealmLocalAllMplForwarders(void) const { return (*this == GetRealmLocalAllMplForwarders()); } -void Address::SetToRealmLocalAllMplForwarders(void) -{ - *this = GetRealmLocalAllMplForwarders(); -} +void Address::SetToRealmLocalAllMplForwarders(void) { *this = GetRealmLocalAllMplForwarders(); } bool Address::MatchesPrefix(const Prefix &aPrefix) const { @@ -370,42 +377,37 @@ bool Address::MatchesPrefix(const uint8_t *aPrefix, uint8_t aPrefixLength) const return Prefix::MatchLength(mFields.m8, aPrefix, Prefix::SizeForLength(aPrefixLength)) >= aPrefixLength; } -void Address::SetPrefix(const NetworkPrefix &aNetworkPrefix) -{ - mFields.mComponents.mNetworkPrefix = aNetworkPrefix; -} +void Address::SetPrefix(const NetworkPrefix &aNetworkPrefix) { mFields.mComponents.mNetworkPrefix = aNetworkPrefix; } -void Address::SetPrefix(const Prefix &aPrefix) -{ - SetPrefix(0, aPrefix.GetBytes(), aPrefix.GetLength()); -} +void Address::SetPrefix(const Prefix &aPrefix) { CopyBits(mFields.m8, aPrefix.GetBytes(), aPrefix.GetLength()); } -void Address::SetPrefix(uint8_t aOffset, const uint8_t *aPrefix, uint8_t aPrefixLength) +void Address::CopyBits(uint8_t *aDst, const uint8_t *aSrc, uint8_t aNumBits) { - uint8_t bytes = aPrefixLength / CHAR_BIT; - uint8_t extraBits = aPrefixLength % CHAR_BIT; + // This method copies `aNumBits` from `aSrc` into `aDst` handling + // the case where `aNumBits` may not be a multiple of 8. It leaves the + // remaining bits beyond `aNumBits` in `aDst` unchanged. - OT_ASSERT(aPrefixLength <= (sizeof(Address) - aOffset) * CHAR_BIT); + uint8_t numBytes = aNumBits / CHAR_BIT; + uint8_t extraBits = aNumBits % CHAR_BIT; - memcpy(mFields.m8 + aOffset, aPrefix, bytes); + memcpy(aDst, aSrc, numBytes); if (extraBits > 0) { - uint8_t index = aOffset + bytes; - uint8_t mask = ((0x80 >> (extraBits - 1)) - 1); + uint8_t mask = ((0x80 >> (extraBits - 1)) - 1); // `mask` has its higher (msb) `extraBits` bits as `0` and the remaining as `1`. // Example with `extraBits` = 3: // ((0x80 >> 2) - 1) = (0b0010_0000 - 1) = 0b0001_1111 - mFields.m8[index] &= mask; - mFields.m8[index] |= (aPrefix[index] & ~mask); + aDst[numBytes] &= mask; + aDst[numBytes] |= (aSrc[numBytes] & ~mask); } } void Address::SetMulticastNetworkPrefix(const uint8_t *aPrefix, uint8_t aPrefixLength) { - SetPrefix(kMulticastNetworkPrefixOffset, aPrefix, aPrefixLength); + CopyBits(&mFields.m8[kMulticastNetworkPrefixOffset], aPrefix, aPrefixLength); mFields.m8[kMulticastNetworkPrefixLengthOffset] = aPrefixLength; } @@ -473,7 +475,7 @@ void Address::SynthesizeFromIp4Address(const Prefix &aPrefix, const Ip4::Address { // The prefix length must be 32, 40, 48, 56, 64, 96. IPv4 bytes are added // after the prefix, skipping over the bits 64 to 71 (byte at `kSkipIndex`) - // which must be set to zero. The suffix is set to zero (per RFC 6502). + // which must be set to zero. The suffix is set to zero (per RFC 6052). // // +--+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ // |PL| 0-------------32--40--48--56--64--72--80--88--96--104---------| @@ -514,11 +516,17 @@ void Address::SynthesizeFromIp4Address(const Prefix &aPrefix, const Ip4::Address } Error Address::FromString(const char *aString) +{ + constexpr char kNullChar = '\0'; + + return ParseFrom(aString, kNullChar); +} + +Error Address::ParseFrom(const char *aString, char aTerminatorChar) { constexpr uint8_t kInvalidIndex = 0xff; constexpr char kColonChar = ':'; constexpr char kDotChar = '.'; - constexpr char kNullChar = '\0'; Error error = kErrorParse; uint8_t index = 0; @@ -536,7 +544,7 @@ Error Address::FromString(const char *aString) colonIndex = index; } - while (*aString != kNullChar) + while (*aString != aTerminatorChar) { const char *start = aString; uint32_t value = 0; @@ -583,7 +591,7 @@ Error Address::FromString(const char *aString) break; } - VerifyOrExit((*aString == kColonChar) || (*aString == kNullChar)); + VerifyOrExit((*aString == kColonChar) || (*aString == aTerminatorChar)); VerifyOrExit(index < endIndex); mFields.m16[index++] = HostSwap16(static_cast(value)); @@ -617,7 +625,7 @@ Error Address::FromString(const char *aString) { Ip4::Address ip4Addr; - SuccessOrExit(error = ip4Addr.FromString(aString)); + SuccessOrExit(error = ip4Addr.FromString(aString, aTerminatorChar)); memcpy(GetArrayEnd(mFields.m8) - Ip4::Address::kSize, ip4Addr.GetBytes(), Ip4::Address::kSize); } diff --git a/src/core/net/ip6_address.hpp b/src/core/net/ip6_address.hpp index 064e282b6df..3323690fba0 100644 --- a/src/core/net/ip6_address.hpp +++ b/src/core/net/ip6_address.hpp @@ -188,10 +188,7 @@ class Prefix : public otIp6Prefix, public Clearable, public Unequatable< * @retval FALSE The prefix is not a Link-Local prefix. * */ - bool IsLinkLocal(void) const - { - return mLength >= 10 && mPrefix.mFields.m8[0] == 0xfe && (mPrefix.mFields.m8[1] & 0xc0) == 0x80; - } + bool IsLinkLocal(void) const; /** * This method indicates whether the prefix is a Multicast prefix. @@ -200,7 +197,7 @@ class Prefix : public otIp6Prefix, public Clearable, public Unequatable< * @retval FALSE The prefix is not a Multicast prefix. * */ - bool IsMulticast(void) const { return mLength >= 8 && mPrefix.mFields.m8[0] == 0xff; } + bool IsMulticast(void) const; /** * This method indicates whether the prefix is a Unique-Local prefix. @@ -209,7 +206,7 @@ class Prefix : public otIp6Prefix, public Clearable, public Unequatable< * @retval FALSE The prefix is not a Unique-Local prefix. * */ - bool IsUniqueLocal(void) const { return mLength >= 7 && (mPrefix.mFields.m8[0] & 0xfe) == 0xfc; } + bool IsUniqueLocal(void) const; /** * This method indicates whether the prefix is equal to a given prefix. @@ -232,11 +229,7 @@ class Prefix : public otIp6Prefix, public Clearable, public Unequatable< * @retval FALSE The prefix does not contains the @p aSubPrefix. * */ - bool ContainsPrefix(const Prefix &aSubPrefix) const - { - return (mLength >= aSubPrefix.mLength) && - (MatchLength(GetBytes(), aSubPrefix.GetBytes(), aSubPrefix.GetBytesSize()) >= aSubPrefix.GetLength()); - } + bool ContainsPrefix(const Prefix &aSubPrefix) const; /** * This method indicates whether the prefix contains a sub-prefix (given as a `NetworkPrefix`). @@ -247,11 +240,7 @@ class Prefix : public otIp6Prefix, public Clearable, public Unequatable< * @retval FALSE The prefix does not contains the @p aSubPrefix. * */ - bool ContainsPrefix(const NetworkPrefix &aSubPrefix) const - { - return (mLength >= NetworkPrefix::kLength) && - (MatchLength(GetBytes(), aSubPrefix.m8, NetworkPrefix::kSize) >= NetworkPrefix::kLength); - } + bool ContainsPrefix(const NetworkPrefix &aSubPrefix) const; /** * This method overloads operator `==` to evaluate whether or not two prefixes are equal. @@ -262,11 +251,7 @@ class Prefix : public otIp6Prefix, public Clearable, public Unequatable< * @retval FALSE If the two prefixes are not equal. * */ - bool operator==(const Prefix &aOther) const - { - return (mLength == aOther.mLength) && - (MatchLength(GetBytes(), aOther.GetBytes(), GetBytesSize()) >= GetLength()); - } + bool operator==(const Prefix &aOther) const; /** * This method overloads operator `<` to compare two prefixes. @@ -309,7 +294,7 @@ class Prefix : public otIp6Prefix, public Clearable, public Unequatable< /** * This method indicates whether or not a given prefix length is valid for use as a NAT64 prefix. * - * A NAT64 prefix must have one of the following lengths: 32, 40, 48, 56, 64, or 96 (per RFC 6502). + * A NAT64 prefix must have one of the following lengths: 32, 40, 48, 56, 64, or 96 (per RFC 6052). * * @param[in] aLength The length of the prefix. * @@ -322,7 +307,7 @@ class Prefix : public otIp6Prefix, public Clearable, public Unequatable< /** * This method indicates whether or not the prefix has a valid length for use as a NAT64 prefix. * - * A NAT64 prefix must have one of the following lengths: 32, 40, 48, 56, 64, or 96 (per RFC 6502). + * A NAT64 prefix must have one of the following lengths: 32, 40, 48, 56, 64, or 96 (per RFC 6052). * * @retval TRUE If the prefix has a valid length for use as a NAT64 prefix. * @retval FALSE If the prefix does not have a valid length for use as a NAT64 prefix. @@ -330,6 +315,17 @@ class Prefix : public otIp6Prefix, public Clearable, public Unequatable< */ bool IsValidNat64(void) const { return IsValidNat64PrefixLength(mLength); } + /** + * This method parses a given IPv6 prefix string and sets the prefix. + * + * @param[in] aString A null-terminated string, with format "/" + * + * @retval kErrorNone Successfully parsed the IPv6 prefix from @p aString. + * @retval kErrorParse Failed to parse the IPv6 prefix from @p aString. + * + */ + Error FromString(const char *aString); + /** * This method converts the prefix to a string. * @@ -539,6 +535,17 @@ class InterfaceIdentifier : public otIp6InterfaceIdentifier, */ void SetLocator(uint16_t aLocator) { mFields.m16[3] = HostSwap16(aLocator); } + /** + * This method applies a prefix to IID. + * + * If the prefix length is longer than 64 bits, the prefix bits after 64 are written into the IID. This method only + * changes the bits in IID up the prefix length and keeps the rest of the bits in IID as before. + * + * @param[in] aPrefix An IPv6 prefix. + * + */ + void ApplyPrefix(const Prefix &aPrefix); + /** * This method converts an Interface Identifier to a string. * @@ -561,6 +568,7 @@ OT_TOOL_PACKED_BEGIN class Address : public otIp6Address, public Equatable
, public Clearable
{ friend class Prefix; + friend class InterfaceIdentifier; public: static constexpr uint8_t kAloc16Mask = InterfaceIdentifier::kAloc16Mask; ///< The mask for ALOC16. @@ -806,6 +814,15 @@ class Address : public otIp6Address, public Equatable
, public Clearable return static_cast(mFields.mComponents.mNetworkPrefix); } + /** + * This method gets a prefix of the IPv6 address with a given length. + * + * @param[in] aLength The length of prefix in bits. + * @param[out] aPrefix A reference to a prefix to output the fetched prefix. + * + */ + void GetPrefix(uint8_t aLength, Prefix &aPrefix) const { aPrefix.Set(mFields.m8, aLength); } + /** * This method indicates whether the IPv6 address matches a given prefix. * @@ -839,7 +856,7 @@ class Address : public otIp6Address, public Equatable
, public Clearable * @param[in] aPrefixLength The prefix length (in bits). * */ - void SetPrefix(const uint8_t *aPrefix, uint8_t aPrefixLength) { SetPrefix(0, aPrefix, aPrefixLength); } + void SetPrefix(const uint8_t *aPrefix, uint8_t aPrefixLength) { CopyBits(mFields.m8, aPrefix, aPrefixLength); } /** * This method sets the IPv6 address prefix to the given Network Prefix. @@ -1007,7 +1024,9 @@ class Address : public otIp6Address, public Equatable
, public Clearable bool operator<(const Address &aOther) const { return memcmp(mFields.m8, aOther.mFields.m8, sizeof(Address)) < 0; } private: - void SetPrefix(uint8_t aOffset, const uint8_t *aPrefix, uint8_t aPrefixLength); + static constexpr uint8_t kMulticastNetworkPrefixLengthOffset = 3; // Prefix-Based Multicast Address (RFC3306) + static constexpr uint8_t kMulticastNetworkPrefixOffset = 4; // Prefix-Based Multicast Address (RFC3306) + void SetToLocator(const NetworkPrefix &aNetworkPrefix, uint16_t aLocator); void ToString(StringWriter &aWriter) const; void AppendHexWords(StringWriter &aWriter, uint8_t aLength) const; @@ -1018,8 +1037,10 @@ class Address : public otIp6Address, public Equatable
, public Clearable static const Address &GetRealmLocalAllRoutersMulticast(void); static const Address &GetRealmLocalAllMplForwarders(void); - static constexpr uint8_t kMulticastNetworkPrefixLengthOffset = 3; // Prefix-Based Multicast Address (RFC3306). - static constexpr uint8_t kMulticastNetworkPrefixOffset = 4; // Prefix-Based Multicast Address (RFC3306). + static void CopyBits(uint8_t *aDst, const uint8_t *aSrc, uint8_t aNumBits); + + Error ParseFrom(const char *aString, char aTerminatorChar); + } OT_TOOL_PACKED_END; /** diff --git a/src/core/net/ip6_headers.cpp b/src/core/net/ip6_headers.cpp index be72fb6012d..ff9585f6f53 100644 --- a/src/core/net/ip6_headers.cpp +++ b/src/core/net/ip6_headers.cpp @@ -38,6 +38,9 @@ namespace ot { namespace Ip6 { +//--------------------------------------------------------------------------------------------------------------------- +// Header + Error Header::ParseFrom(const Message &aMessage) { Error error = kErrorParse; @@ -63,5 +66,68 @@ bool Header::IsValid(void) const return IsVersion6() && ((sizeof(Header) + GetPayloadLength()) <= kMaxLength); } +//--------------------------------------------------------------------------------------------------------------------- +// Option + +Error Option::ParseFrom(const Message &aMessage, uint16_t aOffset, uint16_t aEndOffset) +{ + Error error; + + // Read the Type first to check for the Pad1 Option. + // If it is not, then we read the full `Option` header. + + SuccessOrExit(error = aMessage.Read(aOffset, this, sizeof(mType))); + + if (mType == kTypePad1) + { + SetLength(0); + ExitNow(); + } + + SuccessOrExit(error = aMessage.Read(aOffset, *this)); + + VerifyOrExit(aOffset + GetSize() <= aEndOffset, error = kErrorParse); + +exit: + return error; +} + +uint16_t Option::GetSize(void) const +{ + return (mType == kTypePad1) ? sizeof(mType) : static_cast(mLength) + sizeof(Option); +} + +//--------------------------------------------------------------------------------------------------------------------- +// PadOption + +void PadOption::InitForPadSize(uint8_t aPadSize) +{ + OT_UNUSED_VARIABLE(mPads); + + Clear(); + + if (aPadSize == 1) + { + SetType(kTypePad1); + } + else + { + SetType(kTypePadN); + SetLength(aPadSize - sizeof(Option)); + } +} + +Error PadOption::InitToPadHeaderWithSize(uint16_t aHeaderSize) +{ + Error error = kErrorNone; + uint8_t size = static_cast(aHeaderSize % ExtensionHeader::kLengthUnitSize); + + VerifyOrExit(size != 0, error = kErrorAlready); + InitForPadSize(ExtensionHeader::kLengthUnitSize - size); + +exit: + return error; +} + } // namespace Ip6 } // namespace ot diff --git a/src/core/net/ip6_headers.hpp b/src/core/net/ip6_headers.hpp index c6846a7fc58..bb2ba1065c9 100644 --- a/src/core/net/ip6_headers.hpp +++ b/src/core/net/ip6_headers.hpp @@ -377,6 +377,14 @@ OT_TOOL_PACKED_BEGIN class ExtensionHeader { public: + /** + * This constant defines the size of Length unit in bytes. + * + * The Length field is in 8-bytes unit. The total size of `ExtensionHeader` MUST be a multiple of 8. + * + */ + static constexpr uint16_t kLengthUnitSize = 8; + /** * This method returns the IPv6 Next Header value. * @@ -396,6 +404,8 @@ class ExtensionHeader /** * This method returns the IPv6 Header Extension Length value. * + * The Length is in 8-byte units and does not include the first 8 bytes. + * * @returns The IPv6 Header Extension Length value. * */ @@ -404,12 +414,27 @@ class ExtensionHeader /** * This method sets the IPv6 Header Extension Length value. * + * The Length is in 8-byte units and does not include the first 8 bytes. + * * @param[in] aLength The IPv6 Header Extension Length value. * */ void SetLength(uint8_t aLength) { mLength = aLength; } + /** + * This method returns the size (number of bytes) of the Extension Header including Next Header and Length fields. + * + * @returns The size (number of bytes) of the Extension Header. + * + */ + uint16_t GetSize(void) const { return kLengthUnitSize * (mLength + 1); } + private: + // | m8[0] | m8[1] | m8[2] | m8[3] | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Next Header | Header Length | . . . | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + uint8_t mNextHeader; uint8_t mLength; } OT_TOOL_PACKED_END; @@ -428,18 +453,20 @@ class HopByHopHeader : public ExtensionHeader * */ OT_TOOL_PACKED_BEGIN -class OptionHeader +class Option { public: /** - * Default constructor. + * IPv6 Option Type actions for unrecognized IPv6 Options. * */ - OptionHeader(void) - : mType(0) - , mLength(0) + enum Action : uint8_t { - } + kActionSkip = 0x00, ///< Skip over this option and continue processing the header. + kActionDiscard = 0x40, ///< Discard the packet. + kActionForceIcmp = 0x80, ///< Discard the packet and forcibly send an ICMP Parameter Problem. + kActionIcmp = 0xc0, ///< Discard packet and conditionally send an ICMP Parameter Problem. + }; /** * This method returns the IPv6 Option Type value. @@ -450,24 +477,13 @@ class OptionHeader uint8_t GetType(void) const { return mType; } /** - * This method sets the IPv6 Option Type value. + * This method indicates whether IPv6 Option is padding (either Pad1 or PadN). * - * @param[in] aType The IPv6 Option Type value. - * - */ - void SetType(uint8_t aType) { mType = aType; } - - /** - * IPv6 Option Type actions for unrecognized IPv6 Options. + * @retval TRUE The Option is padding. + * @retval FALSE The Option is not padding. * */ - enum Action : uint8_t - { - kActionSkip = 0x00, ///< skip over this option and continue processing the header - kActionDiscard = 0x40, ///< discard the packet - kActionForceIcmp = 0x80, ///< discard the packet and forcibly send an ICMP Parameter Problem - kActionIcmp = 0xc0, ///< discard packet and conditionally send an ICMP Parameter Problem - }; + bool IsPadding(void) const { return (mType == kTypePad1) || (mType == kTypePadN); } /** * This method returns the IPv6 Option action for unrecognized IPv6 Options. @@ -485,6 +501,46 @@ class OptionHeader */ uint8_t GetLength(void) const { return mLength; } + /** + * This method returns the size (number of bytes) of the IPv6 Option. + * + * This method returns the proper size of the Option independent of its type, particularly if Option is Pad1 (which + * does not follow the common Option header structure and has only Type field with no Length field). For other + * Option types, the returned size includes the Type and Length fields. + * + * @returns The size of the Option. + * + */ + uint16_t GetSize(void) const; + + /** + * This method parses and validates the IPv6 Option from a given message. + * + * The Option is read from @p aOffset in @p aMessage. This method then checks that the entire Option is present + * in @p aMessage before the @p aEndOffset. + * + * @param[in] aMessage The IPv6 message. + * @param[in] aOffset The offset in @p aMessage to read the IPv6 Option. + * @param[in] aEndOffset The end offset in @p aMessage. + * + * @retval kErrorNone Successfully parsed the IPv6 option from @p aMessage. + * @retval kErrorParse Malformed IPv6 Option or Option is not contained within @p aMessage by @p aEndOffset. + * + */ + Error ParseFrom(const Message &aMessage, uint16_t aOffset, uint16_t aEndOffset); + +protected: + static constexpr uint8_t kTypePad1 = 0x00; ///< Pad1 Option Type. + static constexpr uint8_t kTypePadN = 0x01; ///< PanN Option Type. + + /** + * This method sets the IPv6 Option Type value. + * + * @param[in] aType The IPv6 Option Type value. + * + */ + void SetType(uint8_t aType) { mType = aType; } + /** * This method sets the IPv6 Option Length value. * @@ -501,62 +557,45 @@ class OptionHeader } OT_TOOL_PACKED_END; /** - * This class implements IPv6 PadN Option generation and parsing. + * This class implements IPv6 Pad Options (Pad1 or PadN) generation. * */ OT_TOOL_PACKED_BEGIN -class OptionPadN : public OptionHeader +class PadOption : public Option, private Clearable { -public: - static constexpr uint8_t kType = 0x01; ///< PadN type - static constexpr uint8_t kData = 0x00; ///< PadN specific data - static constexpr uint8_t kMaxLength = 0x05; ///< Maximum length of PadN option data + friend class Clearable; +public: /** - * This method initializes the PadN header. + * This method initializes the Pad Option for a given total Pad size. + * + * The @p aPadSize MUST be from range 1-7. Otherwise the behavior of this method is undefined. * - * @param[in] aPadLength The length of needed padding. Allowed value from - * range 2-7. + * @param[in] aPadSize The total number of needed padding bytes. * */ - void Init(uint8_t aPadLength) - { - SetType(kType); - SetLength(aPadLength - sizeof(OptionHeader)); - memset(mPad, kData, aPadLength - sizeof(OptionHeader)); - } + void InitForPadSize(uint8_t aPadSize); /** - * This method returns the total IPv6 Option Length value including option - * header. + * This method initializes the Pad Option for padding an IPv6 Extension header with a given current size. * - * @returns The total IPv6 Option Length. + * The Extension Header Length is in 8-bytes unit, so the total size should be a multiple of 8. This method + * determines the Pad Option size needed for appending to Extension Header based on it current size @p aHeaderSize + * so to make it a multiple of 8. This method returns `kErrorAlready` when the @p aHeaderSize is already + * a multiple of 8 (i.e., no padding is needed). * - */ - uint8_t GetTotalLength(void) const { return GetLength() + sizeof(OptionHeader); } - -private: - uint8_t mPad[kMaxLength]; -} OT_TOOL_PACKED_END; - -/** - * This class implements IPv6 Pad1 Option generation and parsing. Pad1 does not follow default option header structure. - * - */ -OT_TOOL_PACKED_BEGIN -class OptionPad1 -{ -public: - static constexpr uint8_t kType = 0x00; - - /** - * This method initializes the Pad1 header. + * @param[in] aHeaderSize The current IPv6 Extension header size (in bytes). + * + * @retval kErrorNone The Pad Option is successfully initialized. + * @retval kErrorAlready The @p aHeaderSize is already a multiple of 8 and no padding is needed. * */ - void Init(void) { mType = kType; } + Error InitToPadHeaderWithSize(uint16_t aHeaderSize); private: - uint8_t mType; + static constexpr uint8_t kMaxLength = 5; + + uint8_t mPads[kMaxLength]; } OT_TOOL_PACKED_END; /** diff --git a/src/core/net/ip6_mpl.cpp b/src/core/net/ip6_mpl.cpp index 3e0156ae6db..f39d83300cb 100644 --- a/src/core/net/ip6_mpl.cpp +++ b/src/core/net/ip6_mpl.cpp @@ -34,6 +34,7 @@ #include "ip6_mpl.hpp" #include "common/code_utils.hpp" +#include "common/debug.hpp" #include "common/instance.hpp" #include "common/locator_getters.hpp" #include "common/message.hpp" @@ -46,52 +47,73 @@ namespace Ip6 { Mpl::Mpl(Instance &aInstance) : InstanceLocator(aInstance) - , mMatchingAddress(nullptr) - , mSeedSetTimer(aInstance, Mpl::HandleSeedSetTimer) - , mSeedId(0) , mSequence(0) #if OPENTHREAD_FTD - , mRetransmissionTimer(aInstance, Mpl::HandleRetransmissionTimer) - , mTimerExpirations(0) + , mRetransmissionTimer(aInstance) #endif { memset(mSeedSet, 0, sizeof(mSeedSet)); } -void Mpl::InitOption(OptionMpl &aOption, const Address &aAddress) +void MplOption::Init(SeedIdLength aSeedIdLength) { - aOption.Init(); - aOption.SetSequence(mSequence++); + SetType(kType); - // Check if Seed Id can be elided. - if (mMatchingAddress && aAddress == *mMatchingAddress) + switch (aSeedIdLength) { - aOption.SetSeedIdLength(OptionMpl::kSeedIdLength0); + case kSeedIdLength0: + SetLength(sizeof(*this) - sizeof(Option) - sizeof(mSeedId)); + break; + case kSeedIdLength2: + SetLength(sizeof(*this) - sizeof(Option)); + break; + default: + OT_ASSERT(false); + } + + mControl = aSeedIdLength; +} - // Decrease default option length. - aOption.SetLength(aOption.GetLength() - sizeof(mSeedId)); +void Mpl::InitOption(MplOption &aOption, const Address &aAddress) +{ + if (aAddress == Get().GetMeshLocal16()) + { + // Seed ID can be elided when `aAddress` is RLOC. + aOption.Init(MplOption::kSeedIdLength0); } else { - aOption.SetSeedIdLength(OptionMpl::kSeedIdLength2); - aOption.SetSeedId(mSeedId); + aOption.Init(MplOption::kSeedIdLength2); + aOption.SetSeedId(Get().GetRloc16()); } + + aOption.SetSequence(mSequence++); } -Error Mpl::ProcessOption(Message &aMessage, const Address &aAddress, bool aIsOutbound, bool &aReceive) +Error Mpl::ProcessOption(Message &aMessage, uint16_t aOffset, const Address &aAddress, bool aIsOutbound, bool &aReceive) { Error error; - OptionMpl option; + MplOption option; - VerifyOrExit(aMessage.ReadBytes(aMessage.GetOffset(), &option, sizeof(option)) >= OptionMpl::kMinLength && - (option.GetSeedIdLength() == OptionMpl::kSeedIdLength0 || - option.GetSeedIdLength() == OptionMpl::kSeedIdLength2), - error = kErrorParse); + // Read the min size bytes first, then check the expected + // `SeedIdLength` and read the full `MplOption` if needed. + SuccessOrExit(error = aMessage.Read(aOffset, &option, MplOption::kMinSize)); - if (option.GetSeedIdLength() == OptionMpl::kSeedIdLength0) + switch (option.GetSeedIdLength()) { - // Retrieve MPL Seed Id from the IPv6 Source Address. - option.SetSeedId(HostSwap16(aAddress.mFields.m16[7])); + case MplOption::kSeedIdLength0: + // Retrieve Seed ID from the IPv6 Source Address RLOC. + VerifyOrExit(aAddress.GetIid().IsLocator(), error = kErrorDrop); + option.SetSeedId(aAddress.GetIid().GetLocator()); + break; + + case MplOption::kSeedIdLength2: + SuccessOrExit(error = aMessage.Read(aOffset, option)); + break; + + case MplOption::kSeedIdLength8: + case MplOption::kSeedIdLength16: + ExitNow(error = kErrorParse); } // Check if the MPL Data Message is new. @@ -191,6 +213,8 @@ Error Mpl::UpdateSeedSet(uint16_t aSeedId, uint8_t aSequence) if (aSequence == mSeedSet[i].mSequence) { // already received, drop message + + mSeedSet[i].mLifetime = kSeedEntryLifetime; ExitNow(error = kErrorDrop); } else if (insert == nullptr && SerialNumber::IsLess(aSequence, mSeedSet[i].mSequence)) @@ -252,24 +276,16 @@ Error Mpl::UpdateSeedSet(uint16_t aSeedId, uint8_t aSequence) insert->mSequence = aSequence; insert->mLifetime = kSeedEntryLifetime; - if (!mSeedSetTimer.IsRunning()) - { - mSeedSetTimer.Start(kSeedEntryLifetimeDt); - } + Get().RegisterReceiver(TimeTicker::kIp6Mpl); exit: return error; } -void Mpl::HandleSeedSetTimer(Timer &aTimer) -{ - aTimer.Get().HandleSeedSetTimer(); -} - -void Mpl::HandleSeedSetTimer(void) +void Mpl::HandleTimeTick(void) { - bool startTimer = false; - int j = 0; + bool continueRxingTicks = false; + int j = 0; for (int i = 0; i < kNumSeedEntries && mSeedSet[i].mLifetime; i++) { @@ -277,8 +293,8 @@ void Mpl::HandleSeedSetTimer(void) if (mSeedSet[i].mLifetime > 0) { - mSeedSet[j++] = mSeedSet[i]; - startTimer = true; + mSeedSet[j++] = mSeedSet[i]; + continueRxingTicks = true; } } @@ -287,14 +303,37 @@ void Mpl::HandleSeedSetTimer(void) mSeedSet[j].mLifetime = 0; } - if (startTimer) + if (!continueRxingTicks) { - mSeedSetTimer.Start(kSeedEntryLifetimeDt); + Get().UnregisterReceiver(TimeTicker::kIp6Mpl); } } #if OPENTHREAD_FTD +uint8_t Mpl::GetTimerExpirations(void) const +{ + uint8_t timerExpirations = 0; + + switch (Get().GetRole()) + { + case Mle::kRoleDisabled: + case Mle::kRoleDetached: + break; + + case Mle::kRoleChild: + timerExpirations = kChildTimerExpirations; + break; + + case Mle::kRoleRouter: + case Mle::kRoleLeader: + timerExpirations = kRouterTimerExpirations; + break; + } + + return timerExpirations; +} + void Mpl::AddBufferedMessage(Message &aMessage, uint16_t aSeedId, uint8_t aSequence, bool aIsOutbound) { Error error = kErrorNone; @@ -334,11 +373,6 @@ void Mpl::AddBufferedMessage(Message &aMessage, uint16_t aSeedId, uint8_t aSeque FreeMessageOnError(messageCopy, error); } -void Mpl::HandleRetransmissionTimer(Timer &aTimer) -{ - aTimer.Get().HandleRetransmissionTimer(); -} - void Mpl::HandleRetransmissionTimer(void) { TimeMilli now = TimerMilli::GetNow(); @@ -351,17 +385,16 @@ void Mpl::HandleRetransmissionTimer(void) if (now < metadata.mTransmissionTime) { - if (nextTime > metadata.mTransmissionTime) - { - nextTime = metadata.mTransmissionTime; - } + nextTime = Min(nextTime, metadata.mTransmissionTime); } else { + uint8_t timerExpirations = GetTimerExpirations(); + // Update the number of transmission timer expirations. metadata.mTransmissionCount++; - if (metadata.mTransmissionCount < GetTimerExpirations()) + if (metadata.mTransmissionCount < timerExpirations) { Message *messageCopy = message.Clone(message.GetLength() - sizeof(Metadata)); @@ -378,16 +411,13 @@ void Mpl::HandleRetransmissionTimer(void) metadata.GenerateNextTransmissionTime(now, kDataMessageInterval); metadata.UpdateIn(message); - if (nextTime > metadata.mTransmissionTime) - { - nextTime = metadata.mTransmissionTime; - } + nextTime = Min(nextTime, metadata.mTransmissionTime); } else { mBufferedMessageSet.Dequeue(message); - if (metadata.mTransmissionCount == GetTimerExpirations()) + if (metadata.mTransmissionCount == timerExpirations) { if (metadata.mTransmissionCount > 1) { @@ -425,10 +455,7 @@ void Mpl::Metadata::RemoveFrom(Message &aMessage) const SuccessOrAssert(aMessage.SetLength(aMessage.GetLength() - sizeof(*this))); } -void Mpl::Metadata::UpdateIn(Message &aMessage) const -{ - aMessage.Write(aMessage.GetLength() - sizeof(*this), *this); -} +void Mpl::Metadata::UpdateIn(Message &aMessage) const { aMessage.Write(aMessage.GetLength() - sizeof(*this), *this); } void Mpl::Metadata::GenerateNextTransmissionTime(TimeMilli aCurrentTime, uint8_t aInterval) { diff --git a/src/core/net/ip6_mpl.hpp b/src/core/net/ip6_mpl.hpp index d6cdfac25b7..60fd6686f31 100644 --- a/src/core/net/ip6_mpl.hpp +++ b/src/core/net/ip6_mpl.hpp @@ -39,6 +39,7 @@ #include "common/locator.hpp" #include "common/message.hpp" #include "common/non_copyable.hpp" +#include "common/time_ticker.hpp" #include "common/timer.hpp" #include "net/ip6_headers.hpp" @@ -60,34 +61,14 @@ namespace Ip6 { * */ OT_TOOL_PACKED_BEGIN -class OptionMpl : public OptionHeader +class MplOption : public Option { public: - static constexpr uint8_t kType = 0x6d; // 01 1 01101 - static constexpr uint8_t kMinLength = 2; + static constexpr uint8_t kType = 0x6d; ///< MPL option type - 01 1 01101 + static constexpr uint8_t kMinSize = (2 + sizeof(Option)); ///< Minimum size (num of bytes) of `MplOption` /** - * This method initializes the MPL header. - * - */ - void Init(void) - { - OptionHeader::SetType(kType); - OptionHeader::SetLength(sizeof(*this) - sizeof(OptionHeader)); - mControl = 0; - } - - /** - * This method returns the total MPL Option length value including option - * header. - * - * @returns The total IPv6 Option Length. - * - */ - uint8_t GetTotalLength(void) const { return OptionHeader::GetLength() + sizeof(OptionHeader); } - - /** - * MPL Seed Id lengths. + * MPL Seed Id Lengths. * */ enum SeedIdLength : uint8_t @@ -99,23 +80,22 @@ class OptionMpl : public OptionHeader }; /** - * This method returns the MPL Seed Id Length value. + * This method initializes the MPL Option. * - * @returns The MPL Seed Id Length value. + * The @p aSeedIdLength MUST be either `kSeedIdLength0` or `kSeedIdLength2`. Other values are not supported. + * + * @param[in] aSeedIdLength The MPL Seed Id Length. * */ - SeedIdLength GetSeedIdLength(void) const { return static_cast(mControl & kSeedIdLengthMask); } + void Init(SeedIdLength aSeedIdLength); /** - * This method sets the MPL Seed Id Length value. + * This method returns the MPL Seed Id Length value. * - * @param[in] aSeedIdLength The MPL Seed Length. + * @returns The MPL Seed Id Length value. * */ - void SetSeedIdLength(SeedIdLength aSeedIdLength) - { - mControl = static_cast((mControl & ~kSeedIdLengthMask) | aSeedIdLength); - } + SeedIdLength GetSeedIdLength(void) const { return static_cast(mControl & kSeedIdLengthMask); } /** * This method indicates whether or not the MPL M flag is set. @@ -185,6 +165,8 @@ class OptionMpl : public OptionHeader */ class Mpl : public InstanceLocator, private NonCopyable { + friend class ot::TimeTicker; + public: /** * This constructor initializes the MPL object. @@ -201,7 +183,7 @@ class Mpl : public InstanceLocator, private NonCopyable * @param[in] aAddress A reference to the IPv6 Source Address. * */ - void InitOption(OptionMpl &aOption, const Address &aAddress); + void InitOption(MplOption &aOption, const Address &aAddress); /** * This method processes an MPL option. When the MPL module acts as an MPL Forwarder @@ -210,6 +192,7 @@ class Mpl : public InstanceLocator, private NonCopyable * timer expirations for subsequent retransmissions. * * @param[in] aMessage A reference to the message. + * @param[in] aOffset The offset in @p aMessage to read the MPL option. * @param[in] aAddress A reference to the IPv6 Source Address. * @param[in] aIsOutbound TRUE if this message was locally generated, FALSE otherwise. * @param[out] aReceive Set to FALSE if the MPL message is a duplicate and must not @@ -219,51 +202,9 @@ class Mpl : public InstanceLocator, private NonCopyable * @retval kErrorDrop The MPL message is a duplicate and should be dropped. * */ - Error ProcessOption(Message &aMessage, const Address &aAddress, bool aIsOutbound, bool &aReceive); - - /** - * This method returns the MPL Seed Id value. - * - * @returns The MPL Seed Id value. - * - */ - uint16_t GetSeedId(void) const { return mSeedId; } - - /** - * This method sets the MPL Seed Id value. - * - * @param[in] aSeedId The MPL Seed Id value. - * - */ - void SetSeedId(uint16_t aSeedId) { mSeedId = aSeedId; } - - /** - * This method sets the IPv6 matching address, that allows to elide MPL Seed Id. - * - * @param[in] aAddress The reference to the IPv6 matching address. - * - */ - void SetMatchingAddress(const Address &aAddress) { mMatchingAddress = &aAddress; } + Error ProcessOption(Message &aMessage, uint16_t aOffset, const Address &aAddress, bool aIsOutbound, bool &aReceive); #if OPENTHREAD_FTD - /** - * This method gets the MPL number of Trickle timer expirations that occur before - * terminating the Trickle algorithm's retransmission of a given MPL Data Message. - * - * @returns The MPL number of Trickle timer expirations. - * - */ - uint8_t GetTimerExpirations(void) const { return mTimerExpirations; } - - /** - * This method sets the MPL number of Trickle timer expirations that occur before - * terminating the Trickle algorithm's retransmission of a given MPL Data Message. - * - * @param[in] aTimerExpirations The number of Trickle timer expirations. - * - */ - void SetTimerExpirations(uint8_t aTimerExpirations) { mTimerExpirations = aTimerExpirations; } - /** * This method returns a reference to the buffered message set. * @@ -271,7 +212,7 @@ class Mpl : public InstanceLocator, private NonCopyable * */ const MessageQueue &GetBufferedMessageSet(void) const { return mBufferedMessageSet; } -#endif // OPENTHREAD_FTD +#endif private: static constexpr uint16_t kNumSeedEntries = OPENTHREAD_CONFIG_MPL_SEED_SET_ENTRIES; @@ -286,18 +227,16 @@ class Mpl : public InstanceLocator, private NonCopyable uint8_t mLifetime; }; - static void HandleSeedSetTimer(Timer &aTimer); - void HandleSeedSetTimer(void); - + void HandleTimeTick(void); Error UpdateSeedSet(uint16_t aSeedId, uint8_t aSequence); - SeedEntry mSeedSet[kNumSeedEntries]; - const Address *mMatchingAddress; - TimerMilli mSeedSetTimer; - uint16_t mSeedId; - uint8_t mSequence; + SeedEntry mSeedSet[kNumSeedEntries]; + uint8_t mSequence; #if OPENTHREAD_FTD + static constexpr uint8_t kChildTimerExpirations = 0; // MPL retransmissions for Children. + static constexpr uint8_t kRouterTimerExpirations = 2; // MPL retransmissions for Routers. + struct Metadata { Error AppendTo(Message &aMessage) const { return aMessage.Append(*this); } @@ -313,14 +252,14 @@ class Mpl : public InstanceLocator, private NonCopyable uint8_t mIntervalOffset; }; - static void HandleRetransmissionTimer(Timer &aTimer); - void HandleRetransmissionTimer(void); + uint8_t GetTimerExpirations(void) const; + void HandleRetransmissionTimer(void); + void AddBufferedMessage(Message &aMessage, uint16_t aSeedId, uint8_t aSequence, bool aIsOutbound); - void AddBufferedMessage(Message &aMessage, uint16_t aSeedId, uint8_t aSequence, bool aIsOutbound); + using RetxTimer = TimerMilliIn; MessageQueue mBufferedMessageSet; - TimerMilli mRetransmissionTimer; - uint8_t mTimerExpirations; + RetxTimer mRetransmissionTimer; #endif // OPENTHREAD_FTD }; diff --git a/src/core/net/ip6_types.hpp b/src/core/net/ip6_types.hpp index 06af1266008..8e1f9c34137 100644 --- a/src/core/net/ip6_types.hpp +++ b/src/core/net/ip6_types.hpp @@ -89,7 +89,13 @@ enum IpDscpCs : uint8_t kDscpCs5 = 40, ///< Class selector codepoint 40 kDscpCs6 = 48, ///< Class selector codepoint 48 kDscpCs7 = 56, ///< Class selector codepoint 56 - kDscpCsMask = 0x38, ///< Class selector mask + kDscpCsMask = 0x38, ///< Class selector mask (0b111000) + + // DSCP values to use within Thread mesh (from local codepoint space 0bxxxx11 [RFC 2474 - section 6]). + + kDscpTmfNetPriority = 0x07, ///< TMF network priority (0b000111). + kDscpTmfNormalPriority = 0x0f, ///< TMF normal priority (0b001111). + kDscpTmfLowPriority = 0x17, ///< TMF low priority (0b010111). }; /** diff --git a/src/core/net/nat64_translator.cpp b/src/core/net/nat64_translator.cpp new file mode 100644 index 00000000000..ee9cd2d70cd --- /dev/null +++ b/src/core/net/nat64_translator.cpp @@ -0,0 +1,680 @@ +/* + * 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 implementation for the NAT64 translator. + * + */ + +#include "nat64_translator.hpp" + +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + +#include "common/code_utils.hpp" +#include "common/locator_getters.hpp" +#include "common/log.hpp" +#include "net/checksum.hpp" +#include "net/ip4_types.hpp" +#include "net/ip6.hpp" + +namespace ot { +namespace Nat64 { + +RegisterLogModule("Nat64"); + +const char *StateToString(State aState) +{ + static const char *const kStateString[] = { + "Disabled", + "NotRunning", + "Idle", + "Active", + }; + + static_assert(0 == kStateDisabled, "kStateDisabled value is incorrect"); + static_assert(1 == kStateNotRunning, "kStateNotRunning value is incorrect"); + static_assert(2 == kStateIdle, "kStateIdle value is incorrect"); + static_assert(3 == kStateActive, "kStateActive value is incorrect"); + + return kStateString[aState]; +} + +Translator::Translator(Instance &aInstance) + : InstanceLocator(aInstance) + , mState(State::kStateDisabled) + , mMappingExpirerTimer(aInstance) +{ + Random::NonCrypto::FillBuffer(reinterpret_cast(&mNextMappingId), sizeof(mNextMappingId)); + + mNat64Prefix.Clear(); + mIp4Cidr.Clear(); + mMappingExpirerTimer.Start(kAddressMappingIdleTimeoutMsec); +} + +Message *Translator::NewIp4Message(const Message::Settings &aSettings) +{ + Message *message = Get().NewMessage(sizeof(Ip6::Header) - sizeof(Ip4::Header), aSettings); + + if (message != nullptr) + { + message->SetType(Message::kTypeIp4); + } + + return message; +} + +Error Translator::SendMessage(Message &aMessage) +{ + bool freed = false; + Error error = kErrorDrop; + Result result = TranslateToIp6(aMessage); + + VerifyOrExit(result == kForward); + + error = Get().SendRaw(aMessage, !OPENTHREAD_CONFIG_IP6_ALLOW_LOOP_BACK_HOST_DATAGRAMS); + freed = true; + +exit: + if (!freed) + { + aMessage.Free(); + } + + return error; +} + +Translator::Result Translator::TranslateFromIp6(Message &aMessage) +{ + Result res = kDrop; + ErrorCounters::Reason dropReason = ErrorCounters::kUnknown; + Ip6::Header ip6Header; + Ip4::Header ip4Header; + AddressMapping *mapping = nullptr; + + if (mIp4Cidr.mLength == 0 || !mNat64Prefix.IsValidNat64()) + { + ExitNow(res = kNotTranslated); + } + + // ParseFrom will do basic checks for the message, including the message length and IP protocol version. + if (ip6Header.ParseFrom(aMessage) != kErrorNone) + { + LogWarn("outgoing datagram is not a valid IPv6 datagram, drop"); + dropReason = ErrorCounters::Reason::kIllegalPacket; + ExitNow(res = kDrop); + } + + if (!ip6Header.GetDestination().MatchesPrefix(mNat64Prefix)) + { + ExitNow(res = kNotTranslated); + } + + mapping = FindOrAllocateMapping(ip6Header.GetSource()); + if (mapping == nullptr) + { + LogWarn("failed to get a mapping for %s (mapping pool full?)", ip6Header.GetSource().ToString().AsCString()); + dropReason = ErrorCounters::Reason::kNoMapping; + ExitNow(res = kDrop); + } + + aMessage.RemoveHeader(sizeof(Ip6::Header)); + + ip4Header.Clear(); + ip4Header.InitVersionIhl(); + ip4Header.SetSource(mapping->mIp4); + ip4Header.GetDestination().ExtractFromIp6Address(mNat64Prefix.mLength, ip6Header.GetDestination()); + ip4Header.SetTtl(ip6Header.GetHopLimit()); + ip4Header.SetIdentification(0); + + switch (ip6Header.GetNextHeader()) + { + case Ip6::kProtoUdp: + ip4Header.SetProtocol(Ip4::kProtoUdp); + res = kForward; + break; + case Ip6::kProtoTcp: + ip4Header.SetProtocol(Ip4::kProtoTcp); + res = kForward; + break; + case Ip6::kProtoIcmp6: + ip4Header.SetProtocol(Ip4::kProtoIcmp); + SuccessOrExit(TranslateIcmp6(aMessage)); + res = kForward; + break; + default: + dropReason = ErrorCounters::Reason::kUnsupportedProto; + ExitNow(res = kDrop); + } + + // res here must be kForward based on the switch above. + // TODO: Implement the logic for replying ICMP messages. + ip4Header.SetTotalLength(sizeof(Ip4::Header) + aMessage.GetLength() - aMessage.GetOffset()); + Checksum::UpdateMessageChecksum(aMessage, ip4Header.GetSource(), ip4Header.GetDestination(), + ip4Header.GetProtocol()); + Checksum::UpdateIp4HeaderChecksum(ip4Header); + if (aMessage.Prepend(ip4Header) != kErrorNone) + { + // This should never happen since the IPv4 header is shorter than the IPv6 header. + LogCrit("failed to prepend IPv4 head to translated message"); + ExitNow(res = kDrop); + } + aMessage.SetType(Message::kTypeIp4); + mCounters.Count6To4Packet(ip6Header.GetNextHeader(), ip6Header.GetPayloadLength()); + mapping->mCounters.Count6To4Packet(ip6Header.GetNextHeader(), ip6Header.GetPayloadLength()); + +exit: + if (res == Result::kDrop) + { + mErrorCounters.Count6To4(dropReason); + } + return res; +} + +Translator::Result Translator::TranslateToIp6(Message &aMessage) +{ + Result res = Result::kDrop; + ErrorCounters::Reason dropReason = ErrorCounters::kUnknown; + Ip6::Header ip6Header; + Ip4::Header ip4Header; + AddressMapping *mapping = nullptr; + + // Ip6::Header::ParseFrom may return an error value when the incoming message is an IPv4 datagram. + // If the message is already an IPv6 datagram, forward it directly. + VerifyOrExit(ip6Header.ParseFrom(aMessage) != kErrorNone, res = kNotTranslated); + + if (mIp4Cidr.mLength == 0) + { + // The NAT64 translation is bypassed (will be handled externally) + LogWarn("incoming message is an IPv4 datagram but no IPv4 CIDR for NAT64 configured, drop"); + ExitNow(res = kForward); + } + + if (!mNat64Prefix.IsValidNat64()) + { + LogWarn("incoming message is an IPv4 datagram but no NAT64 prefix configured, drop"); + ExitNow(res = kDrop); + } + + if (ip4Header.ParseFrom(aMessage) != kErrorNone) + { + LogWarn("incoming message is neither IPv4 nor an IPv6 datagram, drop"); + dropReason = ErrorCounters::Reason::kIllegalPacket; + ExitNow(res = kDrop); + } + + mapping = FindMapping(ip4Header.GetDestination()); + if (mapping == nullptr) + { + LogWarn("no mapping found for the IPv4 address"); + dropReason = ErrorCounters::Reason::kNoMapping; + ExitNow(res = kDrop); + } + + aMessage.RemoveHeader(sizeof(Ip4::Header)); + + ip6Header.Clear(); + ip6Header.InitVersionTrafficClassFlow(); + ip6Header.GetSource().SynthesizeFromIp4Address(mNat64Prefix, ip4Header.GetSource()); + ip6Header.SetDestination(mapping->mIp6); + ip6Header.SetFlow(0); + ip6Header.SetHopLimit(ip4Header.GetTtl()); + + // Note: TCP and UDP are the same for both IPv4 and IPv6 except for the checksum calculation, we will update the + // checksum in the payload later. However, we need to translate ICMPv6 messages to ICMP messages in IPv4. + switch (ip4Header.GetProtocol()) + { + case Ip4::kProtoUdp: + ip6Header.SetNextHeader(Ip6::kProtoUdp); + res = kForward; + break; + case Ip4::kProtoTcp: + ip6Header.SetNextHeader(Ip6::kProtoTcp); + res = kForward; + break; + case Ip4::kProtoIcmp: + ip6Header.SetNextHeader(Ip6::kProtoIcmp6); + SuccessOrExit(TranslateIcmp4(aMessage)); + res = kForward; + break; + default: + dropReason = ErrorCounters::Reason::kUnsupportedProto; + ExitNow(res = kDrop); + } + + // res here must be kForward based on the switch above. + // TODO: Implement the logic for replying ICMP datagrams. + ip6Header.SetPayloadLength(aMessage.GetLength() - aMessage.GetOffset()); + Checksum::UpdateMessageChecksum(aMessage, ip6Header.GetSource(), ip6Header.GetDestination(), + ip6Header.GetNextHeader()); + if (aMessage.Prepend(ip6Header) != kErrorNone) + { + // This might happen when the platform failed to reserve enough space before the original IPv4 datagram. + LogWarn("failed to prepend IPv6 head to translated message"); + ExitNow(res = kDrop); + } + aMessage.SetType(Message::kTypeIp6); + mCounters.Count4To6Packet(ip4Header.GetProtocol(), ip4Header.GetTotalLength() - sizeof(ip4Header)); + mapping->mCounters.Count4To6Packet(ip4Header.GetProtocol(), ip4Header.GetTotalLength() - sizeof(ip4Header)); + +exit: + if (res == Result::kDrop) + { + mErrorCounters.Count4To6(dropReason); + } + + return res; +} + +Translator::AddressMapping::InfoString Translator::AddressMapping::ToString(void) const +{ + InfoString string; + + string.Append("%s -> %s", mIp6.ToString().AsCString(), mIp4.ToString().AsCString()); + + return string; +} + +void Translator::AddressMapping::CopyTo(otNat64AddressMapping &aMapping, TimeMilli aNow) const +{ + aMapping.mId = mId; + aMapping.mIp4 = mIp4; + aMapping.mIp6 = mIp6; + aMapping.mCounters = mCounters; + + // We are removing expired mappings lazily, and an expired mapping might become active again before actually + // removed. Report the mapping to be "just expired" to avoid confusion. + if (mExpiry < aNow) + { + aMapping.mRemainingTimeMs = 0; + } + else + { + aMapping.mRemainingTimeMs = mExpiry - aNow; + } +} + +void Translator::ReleaseMapping(AddressMapping &aMapping) +{ + IgnoreError(mIp4AddressPool.PushBack(aMapping.mIp4)); + mAddressMappingPool.Free(aMapping); + LogInfo("mapping removed: %s", aMapping.ToString().AsCString()); +} + +uint16_t Translator::ReleaseMappings(LinkedList &aMappings) +{ + uint16_t numRemoved = 0; + + for (AddressMapping *mapping = aMappings.Pop(); mapping != nullptr; mapping = aMappings.Pop()) + { + numRemoved++; + ReleaseMapping(*mapping); + } + + return numRemoved; +} + +uint16_t Translator::ReleaseExpiredMappings(void) +{ + LinkedList idleMappings; + + mActiveAddressMappings.RemoveAllMatching(TimerMilli::GetNow(), idleMappings); + + return ReleaseMappings(idleMappings); +} + +Translator::AddressMapping *Translator::AllocateMapping(const Ip6::Address &aIp6Addr) +{ + AddressMapping *mapping = nullptr; + + // The address pool will be no larger than the mapping pool, so checking the address pool is enough. + if (mIp4AddressPool.IsEmpty()) + { + // ReleaseExpiredMappings returns the number of mappings removed. + VerifyOrExit(ReleaseExpiredMappings() > 0); + } + + mapping = mAddressMappingPool.Allocate(); + // We should get a valid item since address pool is no larger than the mapping pool, and the address pool is not + // empty. + VerifyOrExit(mapping != nullptr); + + mActiveAddressMappings.Push(*mapping); + mapping->mId = ++mNextMappingId; + mapping->mIp6 = aIp6Addr; + // PopBack must return a valid address since it is not empty. + mapping->mIp4 = *mIp4AddressPool.PopBack(); + mapping->Touch(TimerMilli::GetNow()); + LogInfo("mapping created: %s", mapping->ToString().AsCString()); + +exit: + return mapping; +} + +Translator::AddressMapping *Translator::FindOrAllocateMapping(const Ip6::Address &aIp6Addr) +{ + AddressMapping *mapping = mActiveAddressMappings.FindMatching(aIp6Addr); + + // Exit if we found a valid mapping. + VerifyOrExit(mapping == nullptr); + + mapping = AllocateMapping(aIp6Addr); + +exit: + return mapping; +} + +Translator::AddressMapping *Translator::FindMapping(const Ip4::Address &aIp4Addr) +{ + AddressMapping *mapping = mActiveAddressMappings.FindMatching(aIp4Addr); + + if (mapping != nullptr) + { + mapping->Touch(TimerMilli::GetNow()); + } + return mapping; +} + +Error Translator::TranslateIcmp4(Message &aMessage) +{ + Error err = kErrorNone; + Ip4::Icmp::Header icmp4Header; + Ip6::Icmp::Header icmp6Header; + + // TODO: Implement the translation of other ICMP messages. + + // Note: The caller consumed the IP header, so the ICMP header is at offset 0. + SuccessOrExit(err = aMessage.Read(0, icmp4Header)); + switch (icmp4Header.GetType()) + { + case Ip4::Icmp::Header::Type::kTypeEchoReply: + { + // The only difference between ICMPv6 echo and ICMP4 echo is the message type field, so we can reinterpret it as + // ICMP6 header and set the message type. + SuccessOrExit(err = aMessage.Read(0, icmp6Header)); + icmp6Header.SetType(Ip6::Icmp::Header::Type::kTypeEchoReply); + aMessage.Write(0, icmp6Header); + break; + } + default: + err = kErrorInvalidArgs; + break; + } + +exit: + return err; +} + +Error Translator::TranslateIcmp6(Message &aMessage) +{ + Error err = kErrorNone; + Ip4::Icmp::Header icmp4Header; + Ip6::Icmp::Header icmp6Header; + + // TODO: Implement the translation of other ICMP messages. + + // Note: The caller have consumed the IP header, so the ICMP header is at offset 0. + SuccessOrExit(err = aMessage.Read(0, icmp6Header)); + switch (icmp6Header.GetType()) + { + case Ip6::Icmp::Header::Type::kTypeEchoRequest: + { + // The only difference between ICMPv6 echo and ICMP4 echo is the message type field, so we can reinterpret it as + // ICMP6 header and set the message type. + SuccessOrExit(err = aMessage.Read(0, icmp4Header)); + icmp4Header.SetType(Ip4::Icmp::Header::Type::kTypeEchoRequest); + aMessage.Write(0, icmp4Header); + break; + } + default: + err = kErrorInvalidArgs; + break; + } + +exit: + return err; +} + +Error Translator::SetIp4Cidr(const Ip4::Cidr &aCidr) +{ + Error err = kErrorNone; + + uint32_t numberOfHosts; + uint32_t hostIdBegin; + + VerifyOrExit(aCidr.mLength > 0 && aCidr.mLength <= 32, err = kErrorInvalidArgs); + + VerifyOrExit(mIp4Cidr != aCidr); + + // Avoid using the 0s and 1s in the host id of an address, but what if the user provides us with /32 or /31 + // addresses? + if (aCidr.mLength == 32) + { + hostIdBegin = 0; + numberOfHosts = 1; + } + else if (aCidr.mLength == 31) + { + hostIdBegin = 0; + numberOfHosts = 2; + } + else + { + hostIdBegin = 1; + numberOfHosts = static_cast((1 << (Ip4::Address::kSize * 8 - aCidr.mLength)) - 2); + } + numberOfHosts = OT_MIN(numberOfHosts, kAddressMappingPoolSize); + + mAddressMappingPool.FreeAll(); + mActiveAddressMappings.Clear(); + mIp4AddressPool.Clear(); + + for (uint32_t i = 0; i < numberOfHosts; i++) + { + Ip4::Address addr; + + addr.SynthesizeFromCidrAndHost(aCidr, i + hostIdBegin); + IgnoreError(mIp4AddressPool.PushBack(addr)); + } + + LogInfo("IPv4 CIDR for NAT64: %s (actual address pool: %s - %s, %u addresses)", aCidr.ToString().AsCString(), + mIp4AddressPool.Front()->ToString().AsCString(), mIp4AddressPool.Back()->ToString().AsCString(), + numberOfHosts); + mIp4Cidr = aCidr; + + UpdateState(); + +exit: + return err; +} + +void Translator::SetNat64Prefix(const Ip6::Prefix &aNat64Prefix) +{ + if (aNat64Prefix.GetLength() == 0) + { + ClearNat64Prefix(); + } + else if (mNat64Prefix != aNat64Prefix) + { + LogInfo("IPv6 Prefix for NAT64 updated to %s", aNat64Prefix.ToString().AsCString()); + mNat64Prefix = aNat64Prefix; + UpdateState(); + } +} + +void Translator::ClearNat64Prefix(void) +{ + VerifyOrExit(mNat64Prefix.GetLength() != 0); + mNat64Prefix.Clear(); + LogInfo("IPv6 Prefix for NAT64 cleared"); + UpdateState(); + +exit: + return; +} + +void Translator::HandleMappingExpirerTimer(void) +{ + LogInfo("Released %d expired mappings", ReleaseExpiredMappings()); + mMappingExpirerTimer.Start(kAddressMappingIdleTimeoutMsec); +} + +void Translator::InitAddressMappingIterator(AddressMappingIterator &aIterator) +{ + aIterator.mPtr = mActiveAddressMappings.GetHead(); +} + +Error Translator::GetNextAddressMapping(AddressMappingIterator &aIterator, otNat64AddressMapping &aMapping) +{ + Error err = kErrorNotFound; + TimeMilli now = TimerMilli::GetNow(); + AddressMapping *item = static_cast(aIterator.mPtr); + + VerifyOrExit(item != nullptr); + + item->CopyTo(aMapping, now); + aIterator.mPtr = item->GetNext(); + err = kErrorNone; + +exit: + return err; +} + +Error Translator::GetIp4Cidr(Ip4::Cidr &aCidr) +{ + Error err = kErrorNone; + + VerifyOrExit(mIp4Cidr.mLength > 0, err = kErrorNotFound); + aCidr = mIp4Cidr; + +exit: + return err; +} + +Error Translator::GetIp6Prefix(Ip6::Prefix &aPrefix) +{ + Error err = kErrorNone; + + VerifyOrExit(mNat64Prefix.mLength > 0, err = kErrorNotFound); + aPrefix = mNat64Prefix; + +exit: + return err; +} + +void Translator::ProtocolCounters::Count6To4Packet(uint8_t aProtocol, uint64_t aPacketSize) +{ + switch (aProtocol) + { + case Ip6::kProtoUdp: + mUdp.m6To4Packets++; + mUdp.m6To4Bytes += aPacketSize; + break; + case Ip6::kProtoTcp: + mTcp.m6To4Packets++; + mTcp.m6To4Bytes += aPacketSize; + break; + case Ip6::kProtoIcmp6: + mIcmp.m6To4Packets++; + mIcmp.m6To4Bytes += aPacketSize; + break; + } + + mTotal.m6To4Packets++; + mTotal.m6To4Bytes += aPacketSize; +} + +void Translator::ProtocolCounters::Count4To6Packet(uint8_t aProtocol, uint64_t aPacketSize) +{ + switch (aProtocol) + { + case Ip4::kProtoUdp: + mUdp.m4To6Packets++; + mUdp.m4To6Bytes += aPacketSize; + break; + case Ip4::kProtoTcp: + mTcp.m4To6Packets++; + mTcp.m4To6Bytes += aPacketSize; + break; + case Ip4::kProtoIcmp: + mIcmp.m4To6Packets++; + mIcmp.m4To6Bytes += aPacketSize; + break; + } + + mTotal.m4To6Packets++; + mTotal.m4To6Bytes += aPacketSize; +} + +void Translator::UpdateState(void) +{ + State newState; + + if (mEnabled) + { + if (mIp4Cidr.mLength > 0 && mNat64Prefix.IsValidNat64()) + { + newState = kStateActive; + } + else + { + newState = kStateNotRunning; + } + } + else + { + newState = kStateDisabled; + } + + SuccessOrExit(Get().Update(mState, newState, kEventNat64TranslatorStateChanged)); + LogInfo("NAT64 translator is now %s", StateToString(mState)); + +exit: + return; +} + +void Translator::SetEnabled(bool aEnabled) +{ + VerifyOrExit(mEnabled != aEnabled); + mEnabled = aEnabled; + + if (!aEnabled) + { + ReleaseMappings(mActiveAddressMappings); + } + + UpdateState(); + +exit: + return; +} + +} // namespace Nat64 +} // namespace ot + +#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE diff --git a/src/core/net/nat64_translator.hpp b/src/core/net/nat64_translator.hpp new file mode 100644 index 00000000000..1ec16a4a6a4 --- /dev/null +++ b/src/core/net/nat64_translator.hpp @@ -0,0 +1,414 @@ +/* + * 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 the NAT64 translator. + * + */ + +#ifndef NAT64_TRANSLATOR_HPP_ +#define NAT64_TRANSLATOR_HPP_ + +#include "openthread-core-config.h" + +#include "common/array.hpp" +#include "common/linked_list.hpp" +#include "common/locator.hpp" +#include "common/pool.hpp" +#include "common/timer.hpp" +#include "net/ip4_types.hpp" +#include "net/ip6.hpp" + +namespace ot { +namespace Nat64 { + +enum State : uint8_t +{ + kStateDisabled = OT_NAT64_STATE_DISABLED, ///< The component is disabled. + kStateNotRunning = OT_NAT64_STATE_NOT_RUNNING, ///< The component is enabled, but is not running. + kStateIdle = OT_NAT64_STATE_IDLE, ///< NAT64 is enabled, but this BR is not an active NAT64 BR. + kStateActive = OT_NAT64_STATE_ACTIVE, ///< The component is running. +}; + +/** + * This function converts a `State` into a string. + * + * @param[in] aState A state. + * + * @returns A string representation of @p aState. + * + */ +const char *StateToString(State aState); + +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + +/** + * This class implements the NAT64 translator. + * + */ +class Translator : public InstanceLocator, private NonCopyable +{ +public: + static constexpr uint32_t kAddressMappingIdleTimeoutMsec = + OPENTHREAD_CONFIG_NAT64_IDLE_TIMEOUT_SECONDS * Time::kOneSecondInMsec; + static constexpr uint32_t kAddressMappingPoolSize = OPENTHREAD_CONFIG_NAT64_MAX_MAPPINGS; + + typedef otNat64AddressMappingIterator AddressMappingIterator; ///< Address mapping Iterator. + + /** + * The possible results of NAT64 translation. + * + */ + enum Result : uint8_t + { + kNotTranslated, ///< The message is not translated, it might be sending to an non-nat64 prefix (for outgoing + ///< datagrams), or it is already an IPv6 message (for incoming datagrams). + kForward, ///< Message is successfully translated, the caller should continue forwarding the translated + ///< datagram. + kDrop, ///< The caller should drop the datagram silently. + }; + + /** + * Represents the counters for the protocols supported by NAT64. + * + */ + class ProtocolCounters : public otNat64ProtocolCounters, public Clearable + { + public: + /** + * Adds the packet to the counter for the given IPv6 protocol. + * + * @param[in] aProtocol The protocol of the packet. + * @param[in] aPacketSize The size of the packet. + * + */ + void Count6To4Packet(uint8_t aProtocol, uint64_t aPacketSize); + + /** + * Adds the packet to the counter for the given IPv4 protocol. + * + * @param[in] aProtocol The protocol of the packet. + * @param[in] aPacketSize The size of the packet. + * + */ + void Count4To6Packet(uint8_t aProtocol, uint64_t aPacketSize); + }; + + /** + * Represents the counters of dropped packets due to errors when handling NAT64 packets. + * + */ + class ErrorCounters : public otNat64ErrorCounters, public Clearable + { + public: + enum Reason : uint8_t + { + kUnknown = OT_NAT64_DROP_REASON_UNKNOWN, + kIllegalPacket = OT_NAT64_DROP_REASON_ILLEGAL_PACKET, + kUnsupportedProto = OT_NAT64_DROP_REASON_UNSUPPORTED_PROTO, + kNoMapping = OT_NAT64_DROP_REASON_NO_MAPPING, + }; + + /** + * Adds the counter for the given reason when translating an IPv4 datagram. + * + * @param[in] aReason The reason of packet drop. + * + */ + void Count4To6(Reason aReason) { mCount4To6[aReason]++; } + + /** + * Adds the counter for the given reason when translating an IPv6 datagram. + * + * @param[in] aReason The reason of packet drop. + * + */ + void Count6To4(Reason aReason) { mCount6To4[aReason]++; } + }; + + /** + * This constructor initializes the NAT64 translator. + * + */ + explicit Translator(Instance &aInstance); + + /** + * Set the state of NAT64 translator. + * + * Note: Disabling the translator will invalidate all address mappings. + * + * @param[in] aEnabled A boolean to enable/disable NAT64 translator. + * + */ + void SetEnabled(bool aEnabled); + + /** + * Gets the state of NAT64 translator. + * + * @retval kNat64StateDisabled The translator is disabled. + * @retval kNat64StateIdle The translator is not configured with a valid NAT64 prefix and a CIDR. + * @retval kNat64StateActive The translator is translating packets. + * + */ + State GetState(void) const { return mState; } + + /** + * This method translates an IPv4 datagram to an IPv6 datagram and sends it via Thread interface. + * + * The caller transfers ownership of @p aMessage when making this call. OpenThread will free @p aMessage when + * processing is complete, including when a value other than `kErrorNone` is returned. + * + * @param[in] aMessage A reference to the message. + * + * @retval kErrorNone Successfully processed the message. + * @retval kErrorDrop Message was well-formed but not fully processed due to datagram processing rules. + * @retval kErrorNoBufs Could not allocate necessary message buffers when processing the datagram. + * @retval kErrorNoRoute No route to host. + * @retval kErrorParse Encountered a malformed header when processing the message. + * + */ + Error SendMessage(Message &aMessage); + + /** + * Allocate a new message buffer for sending an IPv4 message (which will be translated into an IPv6 datagram by + * NAT64 later). Message buffers allocated by this function will have 20 bytes (The differences between the size of + * IPv6 headers and the size of IPv4 headers) reserved. + * + * @param[in] aSettings The message settings. + * + * @returns A pointer to the message buffer or NULL if no message buffers are available or parameters are invalid. + * + */ + Message *NewIp4Message(const Message::Settings &aSettings); + + /** + * Translates an IPv4 datagram to IPv6 datagram. Note the datagram and datagramLength might be adjusted. + * Note the message can have 20 bytes reserved before the message to avoid potential copy operations. If the message + * is already an IPv6 datagram, `Result::kNotTranslated` will be returned and @p aMessage won't be modified. + * + * @param[in,out] aMessage the message to be processed. + * + * @retval kNotTranslated The message is already an IPv6 datagram. @p aMessage is not updated. + * @retval kForward The caller should continue forwarding the datagram. + * @retval kDrop The caller should drop the datagram silently. + * + */ + Result TranslateToIp6(Message &message); + + /** + * Translates an IPv6 datagram to IPv4 datagram. Note the datagram and datagramLength might be adjusted. + * If the message is not targeted to NAT64-mapped address, `Result::kNotTranslated` will be returned and @p aMessage + * won't be modified. + * + * @param[in,out] aMessage the message to be processed. + * + * @retval kNotTranslated The datagram is not sending to the configured NAT64 prefix. + * @retval kForward The caller should continue forwarding the datagram. + * @retval kDrop The caller should drop the datagram silently. + * + */ + Result TranslateFromIp6(Message &aMessage); + + /** + * Sets the CIDR used when setting the source address of the outgoing translated IPv4 datagrams. A valid CIDR must + * have a non-zero prefix length. + * + * @note The actual addresses pool is limited by the size of the mapping pool and the number of addresses available + * in the CIDR block. If the provided is a valid IPv4 CIDR for NAT64, and it is different from the one already + * configured, the NAT64 translator will be reset and all existing sessions will be expired. + * + * @param[in] aCidr the CIDR for the sources of the translated datagrams. + * + * @retval kErrorInvalidArgs The the given CIDR a valid CIDR for NAT64. + * @retval kErrorNone Successfully enabled/disabled the NAT64 translator. + * + */ + Error SetIp4Cidr(const Ip4::Cidr &aCidr); + + /** + * Sets the prefix of NAT64-mapped addresses in the thread network. The address mapping table will not be cleared. + * This function equals to `ClearNat64Prefix` when an empty prefix is provided. + * + * @param[in] aNat64Prefix The prefix of the NAT64-mapped addresses. + * + */ + void SetNat64Prefix(const Ip6::Prefix &aNat64Prefix); + + /** + * Clear the prefix of NAT64-mapped addresses in the thread network. The address mapping table will not be cleared. + * The translator will return kNotTranslated for all IPv6 datagrams and kDrop for all IPv4 datagrams. + * + */ + void ClearNat64Prefix(void); + + /** + * Initializes an `otNat64AddressMappingIterator`. + * + * An iterator MUST be initialized before it is used. + * + * An iterator can be initialized again to restart from the beginning of the mapping info. + * + * @param[out] aIterator An iterator to initialize. + * + */ + void InitAddressMappingIterator(AddressMappingIterator &aIterator); + + /** + * Gets the next AddressMapping info (using an iterator). + * + * @param[in,out] aIterator The iterator. On success the iterator will be updated to point to next NAT64 + * address mapping record. To get the first entry the iterator should be set to + * OT_NAT64_ADDRESS_MAPPING_ITERATOR_INIT. + * @param[out] aMapping An `otNat64AddressMapping` where information of next NAT64 address mapping record + * is placed (on success). + * + * @retval kErrorNone Successfully found the next NAT64 address mapping info (@p aMapping was successfully + * updated). + * @retval kErrorNotFound No subsequent NAT64 address mapping info was found. + * + */ + Error GetNextAddressMapping(AddressMappingIterator &aIterator, otNat64AddressMapping &aMapping); + + /** + * Gets the NAT64 translator counters. + * + * The counters are initialized to zero when the OpenThread instance is initialized. + * + * @param[out] aCounters A `ProtocolCounters` where the counters of NAT64 translator will be placed. + * + */ + void GetCounters(ProtocolCounters &aCounters) const { aCounters = mCounters; } + + /** + * Gets the NAT64 translator error counters. + * + * The counters are initialized to zero when the OpenThread instance is initialized. + * + * @param[out] aCounters An `ErrorCounters` where the counters of NAT64 translator will be placed. + * + */ + void GetErrorCounters(ErrorCounters &aCounters) const { aCounters = mErrorCounters; } + + /** + * Gets the configured CIDR in the NAT64 translator. + * + * @param[out] aCidr The `Ip4::Cidr` Where the configured CIDR will be placed. + * + * @retval kErrorNone @p aCidr is set to the configured CIDR. + * @retval kErrorNotFound The translator is not configured with an IPv4 CIDR. + * + */ + Error GetIp4Cidr(Ip4::Cidr &aCidr); + + /** + * Gets the configured IPv6 prefix in the NAT64 translator. + * + * @param[out] aPrefix The `Ip6::Prefix` where the configured NAT64 prefix will be placed. + * + * @retval kErrorNone @p aPrefix is set to the configured prefix. + * @retval kErrorNotFound The translator is not configured with an IPv6 prefix. + * + */ + Error GetIp6Prefix(Ip6::Prefix &aPrefix); + +private: + class AddressMapping : public LinkedListEntry + { + public: + friend class LinkedListEntry; + friend class LinkedList; + + typedef String InfoString; + + void Touch(TimeMilli aNow) { mExpiry = aNow + kAddressMappingIdleTimeoutMsec; } + InfoString ToString(void) const; + void CopyTo(otNat64AddressMapping &aMapping, TimeMilli aNow) const; + + uint64_t mId; // The unique id for a mapping session. + + Ip4::Address mIp4; + Ip6::Address mIp6; + TimeMilli mExpiry; // The timestamp when this mapping expires, in milliseconds. + + ProtocolCounters mCounters; + + private: + bool Matches(const Ip4::Address &aIp4) const { return mIp4 == aIp4; } + bool Matches(const Ip6::Address &aIp6) const { return mIp6 == aIp6; } + bool Matches(const TimeMilli aNow) const { return mExpiry < aNow; } + + AddressMapping *mNext; + }; + + Error TranslateIcmp4(Message &aMessage); + Error TranslateIcmp6(Message &aMessage); + + uint16_t ReleaseMappings(LinkedList &aMappings); + void ReleaseMapping(AddressMapping &aMapping); + uint16_t ReleaseExpiredMappings(void); + AddressMapping *AllocateMapping(const Ip6::Address &aIp6Addr); + AddressMapping *FindOrAllocateMapping(const Ip6::Address &aIp6Addr); + AddressMapping *FindMapping(const Ip4::Address &aIp4Addr); + + void HandleMappingExpirerTimer(void); + + using MappingTimer = TimerMilliIn; + + void UpdateState(void); + + bool mEnabled; + State mState; + + uint64_t mNextMappingId; + + Array mIp4AddressPool; + Pool mAddressMappingPool; + LinkedList mActiveAddressMappings; + + Ip6::Prefix mNat64Prefix; + Ip4::Cidr mIp4Cidr; + + MappingTimer mMappingExpirerTimer; + + ProtocolCounters mCounters; + ErrorCounters mErrorCounters; +}; +#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + +} // namespace Nat64 + +DefineMapEnum(otNat64State, Nat64::State); +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE +DefineCoreType(otNat64ProtocolCounters, Nat64::Translator::ProtocolCounters); +DefineCoreType(otNat64ErrorCounters, Nat64::Translator::ErrorCounters); +#endif + +} // namespace ot + +#endif // NAT64_TRANSLATOR_HPP_ diff --git a/src/core/net/nd6.cpp b/src/core/net/nd6.cpp index 5b57e1be9b3..cc5e6bfb0cc 100644 --- a/src/core/net/nd6.cpp +++ b/src/core/net/nd6.cpp @@ -66,10 +66,7 @@ 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; -} +void Option::Iterator::Advance(void) { mOption = (mOption != nullptr) ? Validate(Next(mOption)) : nullptr; } const Option *Option::Iterator::Validate(const Option *aOption) const { @@ -99,10 +96,7 @@ void PrefixInfoOption::SetPrefix(const Prefix &aPrefix) mPrefix = AsCoreType(&aPrefix.mPrefix); } -void PrefixInfoOption::GetPrefix(Prefix &aPrefix) const -{ - aPrefix.Set(mPrefix.GetBytes(), mPrefixLength); -} +void PrefixInfoOption::GetPrefix(Prefix &aPrefix) const { aPrefix.Set(mPrefix.GetBytes(), mPrefixLength); } bool PrefixInfoOption::IsValid(void) const { @@ -137,10 +131,7 @@ void RouteInfoOption::SetPrefix(const Prefix &aPrefix) memcpy(GetPrefixBytes(), aPrefix.GetBytes(), aPrefix.GetBytesSize()); } -void RouteInfoOption::GetPrefix(Prefix &aPrefix) const -{ - aPrefix.Set(GetPrefixBytes(), mPrefixLength); -} +void RouteInfoOption::GetPrefix(Prefix &aPrefix) const { aPrefix.Set(GetPrefixBytes(), mPrefixLength); } bool RouteInfoOption::IsValid(void) const { @@ -215,7 +206,7 @@ Option *RouterAdvertMessage::AppendOption(uint16_t aOptionSize) // returns `nullptr`. The returned option needs to be // initialized and populated by the caller. - Option * option = nullptr; + Option *option = nullptr; uint32_t newLength = mData.GetLength(); newLength += aOptionSize; @@ -249,7 +240,7 @@ Error RouterAdvertMessage::AppendPrefixInfoOption(const Prefix &aPrefix, return error; } -Error RouterAdvertMessage::AppendRouteInfoOption(const Prefix & aPrefix, +Error RouterAdvertMessage::AppendRouteInfoOption(const Prefix &aPrefix, uint32_t aRouteLifetime, RoutePreference aPreference) { @@ -269,7 +260,7 @@ Error RouterAdvertMessage::AppendRouteInfoOption(const Prefix & aPrefix, } //---------------------------------------------------------------------------------------------------------------------- -// RouterAdvMessage +// RouterSolicitMessage RouterSolicitMessage::RouterSolicitMessage(void) { @@ -277,6 +268,30 @@ RouterSolicitMessage::RouterSolicitMessage(void) mHeader.SetType(Icmp::Header::kTypeRouterSolicit); } +//---------------------------------------------------------------------------------------------------------------------- +// NeighborSolicitMessage + +NeighborSolicitMessage::NeighborSolicitMessage(void) +{ + OT_UNUSED_VARIABLE(mChecksum); + OT_UNUSED_VARIABLE(mReserved); + + Clear(); + mType = Icmp::Header::kTypeNeighborSolicit; +} + +//---------------------------------------------------------------------------------------------------------------------- +// NeighborAdvertMessage + +NeighborAdvertMessage::NeighborAdvertMessage(void) +{ + OT_UNUSED_VARIABLE(mChecksum); + OT_UNUSED_VARIABLE(mReserved); + + Clear(); + mType = Icmp::Header::kTypeNeighborAdvert; +} + } // namespace Nd } // namespace Ip6 } // namespace ot diff --git a/src/core/net/nd6.hpp b/src/core/net/nd6.hpp index 0ec6146da01..8a084a917b5 100644 --- a/src/core/net/nd6.hpp +++ b/src/core/net/nd6.hpp @@ -156,7 +156,7 @@ class Option private: static const Option *Next(const Option *aOption); void Advance(void); - const Option * Validate(const Option *aOption) const; + const Option *Validate(const Option *aOption) const; const Option *mOption; const Option *mEnd; @@ -175,6 +175,8 @@ class Option OT_TOOL_PACKED_BEGIN class PrefixInfoOption : public Option, private Clearable { + friend class Clearable; + public: static constexpr Type kType = kTypePrefixInfo; ///< Prefix Information Option Type. @@ -330,6 +332,8 @@ static_assert(sizeof(PrefixInfoOption) == 32, "invalid PrefixInfoOption structur OT_TOOL_PACKED_BEGIN class RouteInfoOption : public Option, private Clearable { + friend class Clearable; + public: static constexpr uint16_t kMinSize = kLengthUnit; ///< Minimum size (in bytes) of a Route Info Option static constexpr Type kType = kTypeRouteInfo; ///< Route Information Option Type. @@ -442,7 +446,7 @@ class RouteInfoOption : public Option, private Clearable static constexpr uint8_t kPreferenceOffset = 3; static constexpr uint8_t kPreferenceMask = 3 << kPreferenceOffset; - uint8_t * GetPrefixBytes(void) { return AsNonConst(AsConst(this)->GetPrefixBytes()); } + uint8_t *GetPrefixBytes(void) { return AsNonConst(AsConst(this)->GetPrefixBytes()); } const uint8_t *GetPrefixBytes(void) const { return reinterpret_cast(this) + sizeof(*this); } uint8_t mPrefixLength; // The prefix length in bits. @@ -470,6 +474,8 @@ class RouterAdvertMessage OT_TOOL_PACKED_BEGIN class Header : public Equatable
, private Clearable
{ + friend class Clearable
; + public: /** * This constructor initializes the Router Advertisement message with @@ -663,7 +669,7 @@ class RouterAdvertMessage private: const uint8_t *GetOptionStart(void) const { return (mData.GetBytes() + sizeof(Header)); } const uint8_t *GetDataEnd(void) const { return mData.GetBytes() + mData.GetLength(); } - Option * AppendOption(uint16_t aOptionSize); + Option *AppendOption(uint16_t aOptionSize); Data mData; uint16_t mMaxLength; @@ -692,6 +698,190 @@ class RouterSolicitMessage static_assert(sizeof(RouterSolicitMessage) == 8, "invalid RouterSolicitMessage structure"); +/** + * This class represents a Neighbor Solicitation (NS) message. + * + */ +OT_TOOL_PACKED_BEGIN +class NeighborSolicitMessage : public Clearable +{ +public: + /** + * This constructor initializes the Neighbor Solicitation message. + * + */ + NeighborSolicitMessage(void); + + /** + * This method indicates whether the Neighbor Solicitation message is valid (proper Type and Code). + * + * @retval TRUE If the message is valid. + * @retval FALSE If the message is not valid. + * + */ + bool IsValid(void) const { return (mType == Icmp::Header::kTypeNeighborSolicit) && (mCode == 0); } + + /** + * This method gets the Target Address field. + * + * @returns The Target Address. + * + */ + const Address &GetTargetAddress(void) const { return mTargetAddress; } + + /** + * This method sets the Target Address field. + * + * @param[in] aTargetAddress The Target Address. + * + */ + void SetTargetAddress(const Address &aTargetAddress) { mTargetAddress = aTargetAddress; } + +private: + // Neighbor Solicitation Message (RFC 4861) + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Type | Code | Checksum | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Reserved | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // + + + // | | + // + Target Address + + // | | + // + + + // | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Options ... + // +-+-+-+-+-+-+-+-+-+-+-+- + + uint8_t mType; + uint8_t mCode; + uint16_t mChecksum; + uint32_t mReserved; + Address mTargetAddress; +} OT_TOOL_PACKED_END; + +static_assert(sizeof(NeighborSolicitMessage) == 24, "Invalid NeighborSolicitMessage definition"); + +/** + * This class represents a Neighbor Advertisement (NA) message. + * + */ +OT_TOOL_PACKED_BEGIN +class NeighborAdvertMessage : public Clearable +{ +public: + NeighborAdvertMessage(void); + + /** + * This method indicates whether the Neighbor Advertisement message is valid (proper Type and Code). + * + * @retval TRUE If the message is valid. + * @retval FALSE If the message is not valid. + * + */ + bool IsValid(void) const { return (mType == Icmp::Header::kTypeNeighborAdvert) && (mCode == 0); } + + /** + * This method indicates whether or not the Router Flag is set in the NA message. + * + * @retval TRUE The Router Flag is set. + * @retval FALSE The Router Flag is not set. + * + */ + bool IsRouterFlagSet(void) const { return (mFlags & kRouterFlag) != 0; } + + /** + * This method sets the Router Flag in the NA message. + * + */ + void SetRouterFlag(void) { mFlags |= kRouterFlag; } + + /** + * This method indicates whether or not the Solicited Flag is set in the NA message. + * + * @retval TRUE The Solicited Flag is set. + * @retval FALSE The Solicited Flag is not set. + * + */ + bool IsSolicitedFlagSet(void) const { return (mFlags & kSolicitedFlag) != 0; } + + /** + * This method sets the Solicited Flag in the NA message. + * + */ + void SetSolicitedFlag(void) { mFlags |= kSolicitedFlag; } + + /** + * This method indicates whether or not the Override Flag is set in the NA message. + * + * @retval TRUE The Override Flag is set. + * @retval FALSE The Override Flag is not set. + * + */ + bool IsOverrideFlagSet(void) const { return (mFlags & kOverrideFlag) != 0; } + + /** + * This method sets the Override Flag in the NA message. + * + */ + void SetOverrideFlag(void) { mFlags |= kOverrideFlag; } + + /** + * This method gets the Target Address field. + * + * @returns The Target Address. + * + */ + const Address &GetTargetAddress(void) const { return mTargetAddress; } + + /** + * This method sets the Target Address field. + * + * @param[in] aTargetAddress The Target Address. + * + */ + void SetTargetAddress(const Address &aTargetAddress) { mTargetAddress = aTargetAddress; } + +private: + // Neighbor Advertisement Message (RFC 4861) + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Type | Code | Checksum | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |R|S|O| Reserved | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // + + + // | | + // + Target Address + + // | | + // + + + // | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Options ... + // +-+-+-+-+-+-+-+-+-+-+-+- + + static constexpr uint8_t kRouterFlag = (1 << 7); + static constexpr uint8_t kSolicitedFlag = (1 << 6); + static constexpr uint8_t kOverrideFlag = (1 << 5); + + uint8_t mType; + uint8_t mCode; + uint16_t mChecksum; + uint8_t mFlags; + uint8_t mReserved[3]; + Address mTargetAddress; +} OT_TOOL_PACKED_END; + +static_assert(sizeof(NeighborAdvertMessage) == 24, "Invalid NeighborAdvertMessage definition"); + } // namespace Nd } // namespace Ip6 } // namespace ot diff --git a/src/core/net/netif.cpp b/src/core/net/netif.cpp index a55914f83db..009feaa9a39 100644 --- a/src/core/net/netif.cpp +++ b/src/core/net/netif.cpp @@ -110,8 +110,6 @@ const otNetifMulticastAddress Netif::kLinkLocalAllRoutersMulticastAddress = { Netif::Netif(Instance &aInstance) : InstanceLocator(aInstance) , mMulticastPromiscuous(false) - , mAddressCallback(nullptr) - , mAddressCallbackContext(nullptr) { } @@ -143,25 +141,7 @@ void Netif::SubscribeAllNodesMulticast(void) tail->SetNext(&linkLocalAllNodesAddress); } - Get().Signal(kEventIp6MulticastSubscribed); - -#if !OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE - VerifyOrExit(mAddressCallback != nullptr); -#endif - - for (const MulticastAddress *entry = &linkLocalAllNodesAddress; entry; entry = entry->GetNext()) - { -#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE - Get().RecordAddressEvent(kAddressAdded, *entry, kOriginThread); - - if (mAddressCallback != nullptr) -#endif - { - AddressInfo addressInfo(*entry); - - mAddressCallback(&addressInfo, kAddressAdded, mAddressCallbackContext); - } - } + SignalMulticastAddressChange(kAddressAdded, &linkLocalAllNodesAddress, nullptr); exit: return; @@ -169,7 +149,7 @@ void Netif::SubscribeAllNodesMulticast(void) void Netif::UnsubscribeAllNodesMulticast(void) { - MulticastAddress * prev; + MulticastAddress *prev; const MulticastAddress &linkLocalAllNodesAddress = AsCoreType(&AsNonConst(kLinkLocalAllNodesMulticastAddress)); // The tail of multicast address linked list contains the @@ -199,25 +179,7 @@ void Netif::UnsubscribeAllNodesMulticast(void) prev->SetNext(nullptr); } - Get().Signal(kEventIp6MulticastUnsubscribed); - -#if !OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE - VerifyOrExit(mAddressCallback != nullptr); -#endif - - for (const MulticastAddress *entry = &linkLocalAllNodesAddress; entry; entry = entry->GetNext()) - { -#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE - Get().RecordAddressEvent(kAddressRemoved, *entry, kOriginThread); - - if (mAddressCallback != nullptr) -#endif - { - AddressInfo addressInfo(*entry); - - mAddressCallback(&addressInfo, kAddressRemoved, mAddressCallbackContext); - } - } + SignalMulticastAddressChange(kAddressRemoved, &linkLocalAllNodesAddress, nullptr); exit: return; @@ -260,26 +222,7 @@ void Netif::SubscribeAllRoutersMulticast(void) prev->SetNext(&linkLocalAllRoutersAddress); } - Get().Signal(kEventIp6MulticastSubscribed); - -#if !OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE - VerifyOrExit(mAddressCallback != nullptr); -#endif - - for (const MulticastAddress *entry = &linkLocalAllRoutersAddress; entry != &linkLocalAllNodesAddress; - entry = entry->GetNext()) - { -#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE - Get().RecordAddressEvent(kAddressAdded, *entry, kOriginThread); - - if (mAddressCallback != nullptr) -#endif - { - AddressInfo addressInfo(*entry); - - mAddressCallback(&addressInfo, kAddressAdded, mAddressCallbackContext); - } - } + SignalMulticastAddressChange(kAddressAdded, &linkLocalAllRoutersAddress, &linkLocalAllNodesAddress); exit: return; @@ -312,27 +255,43 @@ void Netif::UnsubscribeAllRoutersMulticast(void) prev->SetNext(&linkLocalAllNodesAddress); } - Get().Signal(kEventIp6MulticastUnsubscribed); + SignalMulticastAddressChange(kAddressRemoved, &linkLocalAllRoutersAddress, &linkLocalAllNodesAddress); + +exit: + return; +} + +void Netif::SignalMulticastAddressChange(AddressEvent aAddressEvent, + const MulticastAddress *aStart, + const MulticastAddress *aEnd) +{ + // Signal changes to fixed multicast addresses from `aStart` up to + // (not including) `aEnd`. `aAddressEvent` indicates whether + // addresses were subscribed or unsubscribed. + + Get().Signal(aAddressEvent == kAddressAdded ? kEventIp6MulticastSubscribed + : kEventIp6MulticastUnsubscribed); #if !OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE - VerifyOrExit(mAddressCallback != nullptr); + VerifyOrExit(mAddressCallback.IsSet()); #endif - for (const MulticastAddress *entry = &linkLocalAllRoutersAddress; entry != &linkLocalAllNodesAddress; - entry = entry->GetNext()) + for (const MulticastAddress *entry = aStart; entry != aEnd; entry = entry->GetNext()) { #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE - Get().RecordAddressEvent(kAddressRemoved, *entry, kOriginThread); + Get().RecordAddressEvent(aAddressEvent, *entry, kOriginThread); - if (mAddressCallback != nullptr) + if (mAddressCallback.IsSet()) #endif { AddressInfo addressInfo(*entry); - mAddressCallback(&addressInfo, kAddressRemoved, mAddressCallbackContext); + mAddressCallback.Invoke(&addressInfo, aAddressEvent); } } + ExitNow(); + exit: return; } @@ -352,11 +311,11 @@ void Netif::SubscribeMulticast(MulticastAddress &aAddress) Get().RecordAddressEvent(kAddressAdded, aAddress, kOriginThread); #endif - if (mAddressCallback != nullptr) + if (mAddressCallback.IsSet()) { AddressInfo addressInfo(aAddress); - mAddressCallback(&addressInfo, kAddressAdded, mAddressCallbackContext); + mAddressCallback.Invoke(&addressInfo, kAddressAdded); } exit: @@ -373,11 +332,11 @@ void Netif::UnsubscribeMulticast(const MulticastAddress &aAddress) Get().RecordAddressEvent(kAddressRemoved, aAddress, kOriginThread); #endif - if (mAddressCallback != nullptr) + if (mAddressCallback.IsSet()) { AddressInfo addressInfo(aAddress); - mAddressCallback(&addressInfo, kAddressRemoved, mAddressCallbackContext); + mAddressCallback.Invoke(&addressInfo, kAddressRemoved); } exit: @@ -461,12 +420,6 @@ void Netif::UnsubscribeAllExternalMulticastAddresses(void) } } -void Netif::SetAddressCallback(otIp6AddressCallback aCallback, void *aCallbackContext) -{ - mAddressCallback = aCallback; - mAddressCallbackContext = aCallbackContext; -} - void Netif::AddUnicastAddress(UnicastAddress &aAddress) { SuccessOrExit(mUnicastAddresses.Add(aAddress)); @@ -477,11 +430,11 @@ void Netif::AddUnicastAddress(UnicastAddress &aAddress) Get().RecordAddressEvent(kAddressAdded, aAddress); #endif - if (mAddressCallback != nullptr) + if (mAddressCallback.IsSet()) { AddressInfo addressInfo(aAddress); - mAddressCallback(&addressInfo, kAddressAdded, mAddressCallbackContext); + mAddressCallback.Invoke(&addressInfo, kAddressAdded); } exit: @@ -498,11 +451,11 @@ void Netif::RemoveUnicastAddress(const UnicastAddress &aAddress) Get().RecordAddressEvent(kAddressRemoved, aAddress); #endif - if (mAddressCallback != nullptr) + if (mAddressCallback.IsSet()) { AddressInfo addressInfo(aAddress); - mAddressCallback(&addressInfo, kAddressRemoved, mAddressCallbackContext); + mAddressCallback.Invoke(&addressInfo, kAddressRemoved); } exit: @@ -586,10 +539,7 @@ void Netif::RemoveAllExternalUnicastAddresses(void) } } -bool Netif::HasUnicastAddress(const Address &aAddress) const -{ - return mUnicastAddresses.ContainsMatching(aAddress); -} +bool Netif::HasUnicastAddress(const Address &aAddress) const { return mUnicastAddresses.ContainsMatching(aAddress); } bool Netif::IsUnicastAddressExternal(const UnicastAddress &aAddress) const { diff --git a/src/core/net/netif.hpp b/src/core/net/netif.hpp index 65013559363..29f3b61240b 100644 --- a/src/core/net/netif.hpp +++ b/src/core/net/netif.hpp @@ -37,6 +37,7 @@ #include "openthread-core-config.h" #include "common/as_core_type.hpp" +#include "common/callback.hpp" #include "common/clearable.hpp" #include "common/code_utils.hpp" #include "common/const_cast.hpp" @@ -305,7 +306,7 @@ class Netif : public InstanceLocator, private NonCopyable Iterator end(void) { return Iterator(mNetif, Iterator::kEndIterator); } private: - const Netif & mNetif; + const Netif &mNetif; Address::TypeFilter mFilter; }; @@ -322,7 +323,7 @@ class Netif : public InstanceLocator, private NonCopyable void AdvanceFrom(const MulticastAddress *aAddr); void Advance(void) { AdvanceFrom(mItem->GetNext()); } - const Netif & mNetif; + const Netif &mNetif; Address::TypeFilter mFilter; }; @@ -367,7 +368,10 @@ class Netif : public InstanceLocator, private NonCopyable * @param[in] aCallbackContext A pointer to application-specific context. * */ - void SetAddressCallback(otIp6AddressCallback aCallback, void *aCallbackContext); + void SetAddressCallback(otIp6AddressCallback aCallback, void *aCallbackContext) + { + mAddressCallback.Set(aCallback, aCallbackContext); + } /** * This method returns the linked list of unicast addresses. @@ -644,12 +648,15 @@ class Netif : public InstanceLocator, private NonCopyable void UnsubscribeAllNodesMulticast(void); private: + void SignalMulticastAddressChange(AddressEvent aAddressEvent, + const MulticastAddress *aStart, + const MulticastAddress *aEnd); + LinkedList mUnicastAddresses; LinkedList mMulticastAddresses; bool mMulticastPromiscuous; - otIp6AddressCallback mAddressCallback; - void * mAddressCallbackContext; + Callback mAddressCallback; Pool mExtUnicastAddressPool; Pool mExtMulticastAddressPool; diff --git a/src/core/net/sntp_client.cpp b/src/core/net/sntp_client.cpp index b2e82f2003b..e93effa7c2f 100644 --- a/src/core/net/sntp_client.cpp +++ b/src/core/net/sntp_client.cpp @@ -95,7 +95,7 @@ QueryMetadata::QueryMetadata(otSntpResponseHandler aHandler, void *aContext) Client::Client(Instance &aInstance) : mSocket(aInstance) - , mRetransmissionTimer(aInstance, Client::HandleRetransmissionTimer) + , mRetransmissionTimer(aInstance) , mUnixEra(0) { } @@ -105,7 +105,7 @@ Error Client::Start(void) Error error; SuccessOrExit(error = mSocket.Open(&Client::HandleUdpReceive, this)); - SuccessOrExit(error = mSocket.Bind(0, OT_NETIF_UNSPECIFIED)); + SuccessOrExit(error = mSocket.Bind(0, Ip6::kNetifUnspecified)); exit: return error; @@ -128,8 +128,8 @@ Error Client::Query(const otSntpQuery *aQuery, otSntpResponseHandler aHandler, v { Error error; QueryMetadata queryMetadata(aHandler, aContext); - Message * message = nullptr; - Message * messageCopy = nullptr; + Message *message = nullptr; + Message *messageCopy = nullptr; Header header; const Ip6::MessageInfo *messageInfo; @@ -256,7 +256,7 @@ Message *Client::FindRelatedQuery(const Header &aResponseHeader, QueryMetadata & return matchedMessage; } -void Client::FinalizeSntpTransaction(Message & aQuery, +void Client::FinalizeSntpTransaction(Message &aQuery, const QueryMetadata &aQueryMetadata, uint64_t aTime, Error aResult) @@ -269,11 +269,6 @@ void Client::FinalizeSntpTransaction(Message & aQuery, } } -void Client::HandleRetransmissionTimer(Timer &aTimer) -{ - aTimer.Get().HandleRetransmissionTimer(); -} - void Client::HandleRetransmissionTimer(void) { TimeMilli now = TimerMilli::GetNow(); @@ -307,10 +302,7 @@ void Client::HandleRetransmissionTimer(void) SendCopy(message, messageInfo); } - if (nextTime > queryMetadata.mTransmissionTime) - { - nextTime = queryMetadata.mTransmissionTime; - } + nextTime = Min(nextTime, queryMetadata.mTransmissionTime); } if (nextTime < now.GetDistantFuture()) @@ -331,7 +323,7 @@ void Client::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessag Error error = kErrorNone; Header responseHeader; QueryMetadata queryMetadata; - Message * message = nullptr; + Message *message = nullptr; uint64_t unixTime = 0; SuccessOrExit(aMessage.Read(aMessage.GetOffset(), responseHeader)); diff --git a/src/core/net/sntp_client.hpp b/src/core/net/sntp_client.hpp index 1a9ec4f4dad..f8338d58cba 100644 --- a/src/core/net/sntp_client.hpp +++ b/src/core/net/sntp_client.hpp @@ -440,7 +440,7 @@ class QueryMetadata private: uint32_t mTransmitTimestamp; ///< Time at the client when the request departed for the server. otSntpResponseHandler mResponseHandler; ///< A function pointer that is called on response reception. - void * mResponseContext; ///< A pointer to arbitrary context information. + void *mResponseContext; ///< A pointer to arbitrary context information. TimeMilli mTransmissionTime; ///< Time when the timer should shoot for this message. Ip6::Address mSourceAddress; ///< IPv6 address of the message source. Ip6::Address mDestinationAddress; ///< IPv6 address of the message destination. @@ -524,16 +524,17 @@ class Client : private NonCopyable Message *FindRelatedQuery(const Header &aResponseHeader, QueryMetadata &aQueryMetadata); void FinalizeSntpTransaction(Message &aQuery, const QueryMetadata &aQueryMetadata, uint64_t aTime, Error aResult); - static void HandleRetransmissionTimer(Timer &aTimer); - void HandleRetransmissionTimer(void); + void HandleRetransmissionTimer(void); static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + using RetxTimer = TimerMilliIn; + Ip6::Udp::Socket mSocket; MessageQueue mPendingQueries; - TimerMilli mRetransmissionTimer; + RetxTimer mRetransmissionTimer; uint32_t mUnixEra; }; diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index 5bf507be13c..d9647319dc3 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -35,6 +35,7 @@ #include "common/debug.hpp" #include "common/instance.hpp" #include "common/locator_getters.hpp" +#include "common/num_utils.hpp" #include "common/random.hpp" #include "common/settings.hpp" #include "common/string.hpp" @@ -82,7 +83,7 @@ void Client::HostInfo::EnableAutoAddress(void) mNumAddresses = 0; mAutoAddress = true; - LogInfo("HostInfo enabled auto address", GetNumAddresses()); + LogInfo("HostInfo enabled auto address"); } void Client::HostInfo::SetAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddresses) @@ -113,6 +114,9 @@ Error Client::Service::Init(void) // to avoid logging. mState = OT_SRP_CLIENT_ITEM_STATE_REMOVED; + mLease = Min(mLease, kMaxLease); + mKeyLease = Min(mKeyLease, kMaxLease); + exit: return error; } @@ -202,18 +206,9 @@ void Client::AutoStart::SetState(State aState) } } -void Client::AutoStart::SetCallback(AutoStartCallback aCallback, void *aContext) -{ - mCallback = aCallback; - mContext = aContext; -} - void Client::AutoStart::InvokeCallback(const Ip6::SockAddr *aServerSockAddr) const { - if (mCallback != nullptr) - { - mCallback(aServerSockAddr, mContext); - } + mCallback.InvokeIfSet(aServerSockAddr); } #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) @@ -250,20 +245,21 @@ Client::Client(Instance &aInstance) , mTxFailureRetryCount(0) , mShouldRemoveKeyLease(false) , mAutoHostAddressAddedMeshLocal(false) + , mSingleServiceMode(false) #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE , mServiceKeyRecordEnabled(false) + , mUseShortLeaseOption(false) #endif , mUpdateMessageId(0) , mRetryWaitInterval(kMinRetryWaitInterval) - , mAcceptedLeaseInterval(0) , mTtl(0) - , mLeaseInterval(kDefaultLease) - , mKeyLeaseInterval(kDefaultKeyLease) + , mLease(0) + , mKeyLease(0) + , mDefaultLease(kDefaultLease) + , mDefaultKeyLease(kDefaultKeyLease) , mSocket(aInstance) - , mCallback(nullptr) - , mCallbackContext(nullptr) , mDomainName(kDefaultDomainName) - , mTimer(aInstance, Client::HandleTimer) + , mTimer(aInstance) { mHostInfo.Init(); @@ -335,7 +331,7 @@ void Client::Stop(Requester aRequester, StopMode aMode) VerifyOrExit(GetState() != kStateStopped); - mSingleServiceMode.Disable(); + mSingleServiceMode = false; // State changes: // kAdding -> kToRefresh @@ -343,7 +339,7 @@ void Client::Stop(Requester aRequester, StopMode aMode) // kRemoving -> kToRemove // kRegistered -> kToRefresh - ChangeHostAndServiceStates(kNewStateOnStop); + ChangeHostAndServiceStates(kNewStateOnStop, kForAllServices); IgnoreError(mSocket.Close()); @@ -359,7 +355,7 @@ void Client::Stop(Requester aRequester, StopMode aMode) #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE - mAutoStart.ResetTimoutFailureCount(); + mAutoStart.ResetTimeoutFailureCount(); #endif if (aRequester == kRequesterAuto) { @@ -376,12 +372,6 @@ void Client::Stop(Requester aRequester, StopMode aMode) #endif } -void Client::SetCallback(Callback aCallback, void *aContext) -{ - mCallback = aCallback; - mCallbackContext = aContext; -} - void Client::Resume(void) { SetState(kStateUpdated); @@ -405,14 +395,14 @@ void Client::Pause(void) /* (7) kRemoved -> */ kRemoved, }; - mSingleServiceMode.Disable(); + mSingleServiceMode = false; // State changes: // kAdding -> kToRefresh // kRefreshing -> kToRefresh // kRemoving -> kToRemove - ChangeHostAndServiceStates(kNewStateOnPause); + ChangeHostAndServiceStates(kNewStateOnPause, kForAllServices); SetState(kStatePaused); } @@ -569,16 +559,6 @@ Error Client::RemoveService(Service &aService) VerifyOrExit(mServices.Contains(aService), error = kErrorNotFound); UpdateServiceStateToRemove(aService); - - // Check if the service was removed immediately, if so - // invoke the callback to report the removed service. - GetRemovedServices(removedServices); - - if (!removedServices.IsEmpty()) - { - InvokeCallback(kErrorNone, mHostInfo, removedServices.GetHead()); - } - UpdateState(); exit: @@ -587,12 +567,7 @@ Error Client::RemoveService(Service &aService) void Client::UpdateServiceStateToRemove(Service &aService) { - if (aService.GetState() == kToAdd) - { - // If the service has not been added yet, we can remove it immediately. - aService.SetState(kRemoved); - } - else if (aService.GetState() != kRemoving) + if (aService.GetState() != kRemoving) { aService.SetState(kToRemove); } @@ -704,7 +679,7 @@ void Client::SetState(State aState) return; } -void Client::ChangeHostAndServiceStates(const ItemState *aNewStates) +void Client::ChangeHostAndServiceStates(const ItemState *aNewStates, ServiceStateChangeMode aMode) { #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE ItemState oldHostState = mHostInfo.GetState(); @@ -714,7 +689,7 @@ void Client::ChangeHostAndServiceStates(const ItemState *aNewStates) for (Service &service : mServices) { - if (mSingleServiceMode.IsEnabled() && mSingleServiceMode.GetService() != &service) + if ((aMode == kForServicesAppendedInMessage) && !service.IsAppendedInMessage()) { continue; } @@ -748,18 +723,11 @@ void Client::ChangeHostAndServiceStates(const ItemState *aNewStates) #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SAVE_SELECTED_SERVER_ENABLE } -void Client::InvokeCallback(Error aError) const -{ - InvokeCallback(aError, mHostInfo, nullptr); -} +void Client::InvokeCallback(Error aError) const { InvokeCallback(aError, mHostInfo, nullptr); } void Client::InvokeCallback(Error aError, const HostInfo &aHostInfo, const Service *aRemovedServices) const { - VerifyOrExit(mCallback != nullptr); - mCallback(aError, &aHostInfo, mServices.GetHead(), aRemovedServices, mCallbackContext); - -exit: - return; + mCallback.InvokeIfSet(aError, &aHostInfo, mServices.GetHead(), aRemovedServices); } void Client::SendUpdate(void) @@ -776,7 +744,7 @@ void Client::SendUpdate(void) }; Error error = kErrorNone; - Message *message = mSocket.NewMessage(0); + Message *message = mSocket.NewMessage(); uint32_t length; VerifyOrExit(message != nullptr, error = kErrorNoBufs); @@ -786,8 +754,8 @@ void Client::SendUpdate(void) if (length >= Ip6::kMaxDatagramLength) { - LogInfo("Msg len %u is larger than MTU, enabling single service mode", length); - mSingleServiceMode.Enable(); + LogInfo("Msg len %lu is larger than MTU, enabling single service mode", ToUlong(length)); + mSingleServiceMode = true; IgnoreError(message->SetLength(0)); SuccessOrExit(error = PrepareUpdateMessage(*message)); } @@ -801,7 +769,7 @@ void Client::SendUpdate(void) // kToRefresh -> kRefreshing // kToRemove -> kRemoving - ChangeHostAndServiceStates(kNewStateOnMessageTx); + ChangeHostAndServiceStates(kNewStateOnMessageTx, kForServicesAppendedInMessage); // Remember the update message tx time to use later to determine the // lease renew time. @@ -829,7 +797,7 @@ void Client::SendUpdate(void) LogInfo("Failed to send update: %s", ErrorToString(error)); - mSingleServiceMode.Disable(); + mSingleServiceMode = false; FreeMessage(message); SetState(kStateToRetry); @@ -842,7 +810,7 @@ void Client::SendUpdate(void) interval = Random::NonCrypto::AddJitter(kTxFailureRetryInterval, kTxFailureRetryJitter); mTimer.Start(interval); - LogInfo("Quick retry %d in %u msec", mTxFailureRetryCount, interval); + LogInfo("Quick retry %u in %lu msec", mTxFailureRetryCount, ToUlong(interval)); // Do not report message preparation errors to user // until `kMaxTxFailureRetries` are exhausted. @@ -867,7 +835,12 @@ Error Client::PrepareUpdateMessage(Message &aMessage) info.Clear(); +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + info.mKeyRef.SetKeyRef(kSrpEcdsaKeyRef); + SuccessOrExit(error = ReadOrGenerateKey(info.mKeyRef)); +#else SuccessOrExit(error = ReadOrGenerateKey(info.mKeyPair)); +#endif // Generate random Message ID and ensure it is different from last one do @@ -899,19 +872,7 @@ Error Client::PrepareUpdateMessage(Message &aMessage) // Prepare Update section - if ((mHostInfo.GetState() != kToRemove) && (mHostInfo.GetState() != kRemoving)) - { - for (Service &service : mServices) - { - SuccessOrExit(error = AppendServiceInstructions(service, aMessage, info)); - - if (mSingleServiceMode.IsEnabled() && (mSingleServiceMode.GetService() != nullptr)) - { - break; - } - } - } - + SuccessOrExit(error = AppendServiceInstructions(aMessage, info)); SuccessOrExit(error = AppendHostDescriptionInstruction(aMessage, info)); header.SetUpdateRecordCount(info.mRecordCount); @@ -929,6 +890,34 @@ Error Client::PrepareUpdateMessage(Message &aMessage) return error; } +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE +Error Client::ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPairAsRef &aKeyRef) +{ + Error error = kErrorNone; + Crypto::Ecdsa::P256::KeyPair keyPair; + + VerifyOrExit(!Crypto::Storage::HasKey(aKeyRef.GetKeyRef())); + error = Get().Read(keyPair); + + if (error == kErrorNone) + { + Crypto::Ecdsa::P256::PublicKey publicKey; + + if (keyPair.GetPublicKey(publicKey) == kErrorNone) + { + SuccessOrExit(error = aKeyRef.ImportKeyPair(keyPair)); + IgnoreError(Get().Delete()); + ExitNow(); + } + IgnoreError(Get().Delete()); + } + + error = aKeyRef.Generate(); + +exit: + return error; +} +#else Error Client::ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPair &aKeyPair) { Error error; @@ -951,29 +940,181 @@ Error Client::ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPair &aKeyPair) exit: return error; } +#endif // OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE -Error Client::AppendServiceInstructions(Service &aService, Message &aMessage, Info &aInfo) +Error Client::AppendServiceInstructions(Message &aMessage, Info &aInfo) { - Error error = kErrorNone; + Error error = kErrorNone; + + if ((mHostInfo.GetState() == kToRemove) || (mHostInfo.GetState() == kRemoving)) + { + // When host is being removed, there is no need to include + // services in the message (server is expected to remove any + // previously registered services by this client). However, we + // still mark all services as if they are appended in the message + // so to ensure to update their state after sending the message. + + for (Service &service : mServices) + { + service.MarkAsAppendedInMessage(); + } + + mLease = 0; + mKeyLease = mShouldRemoveKeyLease ? 0 : mDefaultKeyLease; + ExitNow(); + } + + mLease = kUnspecifiedInterval; + mKeyLease = kUnspecifiedInterval; + + // We first go through all services which are being updated (in any + // of `...ing` states) and determine the lease and key lease intervals + // associated with them. By the end of the loop either of `mLease` or + // `mKeyLease` may be set or may still remain `kUnspecifiedInterval`. + + for (Service &service : mServices) + { + uint32_t lease = DetermineLeaseInterval(service.GetLease(), mDefaultLease); + uint32_t keyLease = Max(DetermineLeaseInterval(service.GetKeyLease(), mDefaultKeyLease), lease); + + service.ClearAppendedInMessageFlag(); + + switch (service.GetState()) + { + case kAdding: + case kRefreshing: + OT_ASSERT((mLease == kUnspecifiedInterval) || (mLease == lease)); + mLease = lease; + + OT_FALL_THROUGH; + + case kRemoving: + OT_ASSERT((mKeyLease == kUnspecifiedInterval) || (mKeyLease == keyLease)); + mKeyLease = keyLease; + break; + + case kToAdd: + case kToRefresh: + case kToRemove: + case kRegistered: + case kRemoved: + break; + } + } + + // We go through all services again and append the services that + // match the selected `mLease` and `mKeyLease`. If the lease intervals + // are not yet set, the first appended service will determine them. + + for (Service &service : mServices) + { + // Skip over services that are already registered in this loop. + // They may be added from the loop below once the lease intervals + // are determined. + + if ((service.GetState() != kRegistered) && CanAppendService(service)) + { + SuccessOrExit(error = AppendServiceInstruction(service, aMessage, aInfo)); + + if (mSingleServiceMode) + { + // In "single service mode", we allow only one service + // to be appended in the message. + break; + } + } + } + + if (!mSingleServiceMode) + { + for (Service &service : mServices) + { + if ((service.GetState() == kRegistered) && CanAppendService(service) && ShouldRenewEarly(service)) + { + // If the lease needs to be renewed or if we are close to the + // renewal time of a registered service, we refresh the service + // early and include it in this update. This helps put more + // services on the same lease refresh schedule. + + service.SetState(kToRefresh); + SuccessOrExit(error = AppendServiceInstruction(service, aMessage, aInfo)); + } + } + } + + // `mLease` or `mKeylease` may be determined from the set of + // services included in the message. If they are not yet set we + // use the default intervals. + + mLease = DetermineLeaseInterval(mLease, mDefaultLease); + mKeyLease = DetermineLeaseInterval(mKeyLease, mDefaultKeyLease); + + // When message only contains removal of a previously registered + // service, then `mKeyLease` is set but `mLease` remains unspecified. + // In such a case, we end up using `mDefaultLease` but then we need + // to make sure it is not greater than the selected `mKeyLease`. + + if (mLease > mKeyLease) + { + mLease = mKeyLease; + } + +exit: + return error; +} + +bool Client::CanAppendService(const Service &aService) +{ + // Check the lease intervals associated with `aService` to see if + // it can be included in this message. When removing a service, + // only key lease interval should match. In all other cases, both + // lease and key lease should match. The `mLease` and/or `mKeyLease` + // may be updated if they were unspecified. + + bool canAppend = false; + uint32_t lease = DetermineLeaseInterval(aService.GetLease(), mDefaultLease); + uint32_t keyLease = Max(DetermineLeaseInterval(aService.GetKeyLease(), mDefaultKeyLease), lease); + + switch (aService.GetState()) + { + case kToAdd: + case kAdding: + case kToRefresh: + case kRefreshing: + case kRegistered: + VerifyOrExit((mLease == kUnspecifiedInterval) || (mLease == lease)); + VerifyOrExit((mKeyLease == kUnspecifiedInterval) || (mKeyLease == keyLease)); + mLease = lease; + mKeyLease = keyLease; + canAppend = true; + break; + + case kToRemove: + case kRemoving: + VerifyOrExit((mKeyLease == kUnspecifiedInterval) || (mKeyLease == keyLease)); + mKeyLease = keyLease; + canAppend = true; + break; + + case kRemoved: + break; + } + +exit: + return canAppend; +} + +Error Client::AppendServiceInstruction(Service &aService, Message &aMessage, Info &aInfo) +{ + Error error = kErrorNone; + bool removing = ((aService.GetState() == kToRemove) || (aService.GetState() == kRemoving)); Dns::ResourceRecord rr; Dns::SrvRecord srv; - bool removing; uint16_t serviceNameOffset; uint16_t instanceNameOffset; uint16_t offset; - if (aService.GetState() == kRegistered) - { - // If the lease needs to be renewed or if we are close to the - // renewal time of a registered service, we refresh the service - // early and include it in this update. This helps put more - // services on the same lease refresh schedule. - - VerifyOrExit(ShouldRenewEarly(aService)); - aService.SetState(kToRefresh); - } - - removing = ((aService.GetState() == kToRemove) || (aService.GetState() == kRemoving)); + aService.MarkAsAppendedInMessage(); //---------------------------------- // Service Discovery Instruction @@ -989,7 +1130,7 @@ Error Client::AppendServiceInstructions(Service &aService, Message &aMessage, In // to NONE and TTL to zero (RFC 2136 - section 2.5.4). rr.Init(Dns::ResourceRecord::kTypePtr, removing ? Dns::PtrRecord::kClassNone : Dns::PtrRecord::kClassInternet); - rr.SetTtl(removing ? 0 : GetTtl()); + rr.SetTtl(removing ? 0 : DetermineTtl()); offset = aMessage.GetLength(); SuccessOrExit(error = aMessage.Append(rr)); @@ -1048,7 +1189,7 @@ Error Client::AppendServiceInstructions(Service &aService, Message &aMessage, In SuccessOrExit(error = Dns::Name::AppendPointerLabel(instanceNameOffset, aMessage)); srv.Init(); - srv.SetTtl(GetTtl()); + srv.SetTtl(DetermineTtl()); srv.SetPriority(aService.GetPriority()); srv.SetWeight(aService.GetWeight()); srv.SetPort(aService.GetPort()); @@ -1081,11 +1222,6 @@ Error Client::AppendServiceInstructions(Service &aService, Message &aMessage, In } #endif - if (mSingleServiceMode.IsEnabled()) - { - mSingleServiceMode.SetService(aService); - } - exit: return error; } @@ -1153,7 +1289,7 @@ Error Client::AppendAaaaRecord(const Ip6::Address &aAddress, Message &aMessage, Dns::ResourceRecord rr; rr.Init(Dns::ResourceRecord::kTypeAaaa); - rr.SetTtl(GetTtl()); + rr.SetTtl(DetermineTtl()); rr.SetLength(sizeof(Ip6::Address)); SuccessOrExit(error = AppendHostName(aMessage, aInfo)); @@ -1172,14 +1308,18 @@ Error Client::AppendKeyRecord(Message &aMessage, Info &aInfo) const Crypto::Ecdsa::P256::PublicKey publicKey; key.Init(); - key.SetTtl(GetTtl()); + key.SetTtl(DetermineTtl()); key.SetFlags(Dns::KeyRecord::kAuthConfidPermitted, Dns::KeyRecord::kOwnerNonZone, Dns::KeyRecord::kSignatoryFlagGeneral); key.SetProtocol(Dns::KeyRecord::kProtocolDnsSec); key.SetAlgorithm(Dns::KeyRecord::kAlgorithmEcdsaP256Sha256); key.SetLength(sizeof(Dns::KeyRecord) - sizeof(Dns::ResourceRecord) + sizeof(Crypto::Ecdsa::P256::PublicKey)); SuccessOrExit(error = aMessage.Append(key)); +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + SuccessOrExit(error = aInfo.mKeyRef.GetPublicKey(publicKey)); +#else SuccessOrExit(error = aInfo.mKeyPair.GetPublicKey(publicKey)); +#endif SuccessOrExit(error = aMessage.Append(publicKey)); aInfo.mRecordCount++; @@ -1231,11 +1371,12 @@ Error Client::AppendHostName(Message &aMessage, Info &aInfo, bool aDoNotCompress return error; } -Error Client::AppendUpdateLeaseOptRecord(Message &aMessage) const +Error Client::AppendUpdateLeaseOptRecord(Message &aMessage) { Error error; Dns::OptRecord optRecord; Dns::LeaseOption leaseOption; + uint16_t optionSize; // Append empty (root domain) as OPT RR name. SuccessOrExit(error = Dns::Name::AppendTerminator(aMessage)); @@ -1245,24 +1386,26 @@ Error Client::AppendUpdateLeaseOptRecord(Message &aMessage) const optRecord.Init(); optRecord.SetUdpPayloadSize(kUdpPayloadSize); optRecord.SetDnsSecurityFlag(); - optRecord.SetLength(sizeof(Dns::LeaseOption)); - - SuccessOrExit(error = aMessage.Append(optRecord)); - leaseOption.Init(); - - if ((mHostInfo.GetState() == kToRemove) || (mHostInfo.GetState() == kRemoving)) +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + if (mUseShortLeaseOption) { - leaseOption.SetLeaseInterval(0); - leaseOption.SetKeyLeaseInterval(mShouldRemoveKeyLease ? 0 : mKeyLeaseInterval); + LogInfo("Test mode - appending short variant of Lease Option"); + mKeyLease = mLease; + leaseOption.InitAsShortVariant(mLease); } else +#endif { - leaseOption.SetLeaseInterval(mLeaseInterval); - leaseOption.SetKeyLeaseInterval(mKeyLeaseInterval); + leaseOption.InitAsLongVariant(mLease, mKeyLease); } - error = aMessage.Append(leaseOption); + optionSize = static_cast(leaseOption.GetSize()); + + optRecord.SetLength(optionSize); + + SuccessOrExit(error = aMessage.Append(optRecord)); + error = aMessage.AppendBytes(&leaseOption, optionSize); exit: return error; @@ -1313,7 +1456,11 @@ Error Client::AppendSignature(Message &aMessage, Info &aInfo) sha256.Update(aMessage, 0, offset); sha256.Finish(hash); +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + SuccessOrExit(error = aInfo.mKeyRef.Sign(hash, signature)); +#else SuccessOrExit(error = aInfo.mKeyPair.Sign(hash, signature)); +#endif // Move back in message and append SIG RR now with compressed host // name (as signer's name) along with the calculated signature. @@ -1389,7 +1536,7 @@ void Client::ProcessResponse(Message &aMessage) LogInfo("Received response"); #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE - mAutoStart.ResetTimoutFailureCount(); + mAutoStart.ResetTimeoutFailureCount(); #endif error = Dns::Header::ResponseCodeToError(header.GetResponseCode()); @@ -1457,7 +1604,6 @@ void Client::ProcessResponse(Message &aMessage) // interval accepted by server. If not present, then use the // transmitted lease interval from the update request message. - mAcceptedLeaseInterval = mLeaseInterval; recordCount = header.GetPrerequisiteRecordCount() + header.GetUpdateRecordCount() + header.GetAdditionalRecordCount(); @@ -1482,13 +1628,13 @@ void Client::ProcessResponse(Message &aMessage) // lease interval is too short (shorter than the guard time) we // just use half of the accepted lease interval. - if (mAcceptedLeaseInterval > kLeaseRenewGuardInterval) + if (mLease > kLeaseRenewGuardInterval) { - mLeaseRenewTime += Time::SecToMsec(mAcceptedLeaseInterval - kLeaseRenewGuardInterval); + mLeaseRenewTime += Time::SecToMsec(mLease - kLeaseRenewGuardInterval); } else { - mLeaseRenewTime += Time::SecToMsec(mAcceptedLeaseInterval) / 2; + mLeaseRenewTime += Time::SecToMsec(mLease) / 2; } for (Service &service : mServices) @@ -1504,8 +1650,7 @@ void Client::ProcessResponse(Message &aMessage) // kRefreshing -> kRegistered // kRemoving -> kRemoved - ChangeHostAndServiceStates(kNewStateOnUpdateDone); - mSingleServiceMode.Disable(); + ChangeHostAndServiceStates(kNewStateOnUpdateDone, kForServicesAppendedInMessage); HandleUpdateDone(); UpdateState(); @@ -1561,40 +1706,27 @@ Error Client::ProcessOptRecord(const Message &aMessage, uint16_t aOffset, const // Read and process all options (in an OPT RR) from a message. // The `aOffset` points to beginning of record in `aMessage`. - Error error = kErrorNone; - uint16_t len; + Error error = kErrorNone; + Dns::LeaseOption leaseOption; IgnoreError(Dns::Name::ParseName(aMessage, aOffset)); aOffset += sizeof(Dns::OptRecord); - len = aOptRecord.GetLength(); - - while (len > 0) + switch (error = leaseOption.ReadFrom(aMessage, aOffset, aOptRecord.GetLength())) { - Dns::LeaseOption leaseOption; - Dns::Option & option = leaseOption; - uint16_t size; - - SuccessOrExit(error = aMessage.Read(aOffset, option)); - - VerifyOrExit(aOffset + option.GetSize() <= aMessage.GetLength(), error = kErrorParse); - - if ((option.GetOptionCode() == Dns::Option::kUpdateLease) && - (option.GetOptionLength() >= Dns::LeaseOption::kOptionLength)) - { - SuccessOrExit(error = aMessage.Read(aOffset, leaseOption)); - - mAcceptedLeaseInterval = leaseOption.GetLeaseInterval(); + case kErrorNone: + mLease = Min(leaseOption.GetLeaseInterval(), kMaxLease); + mKeyLease = Min(leaseOption.GetKeyLeaseInterval(), kMaxLease); + break; - if (mAcceptedLeaseInterval > kMaxLease) - { - mAcceptedLeaseInterval = kMaxLease; - } - } + case kErrorNotFound: + // If server does not include a lease option in its response, it + // indicates that it accepted what we requested. + error = kErrorNone; + break; - size = static_cast(option.GetSize()); - aOffset += size; - len -= size; + default: + ExitNow(); } exit: @@ -1677,9 +1809,9 @@ void Client::UpdateState(void) service.SetState(kToRefresh); shouldUpdate = true; } - else if (service.GetLeaseRenewTime() < earliestRenewTime) + else { - earliestRenewTime = service.GetLeaseRenewTime(); + earliestRenewTime = Min(earliestRenewTime, service.GetLeaseRenewTime()); } break; @@ -1719,33 +1851,47 @@ void Client::GrowRetryWaitInterval(void) } } -uint32_t Client::GetBoundedLeaseInterval(uint32_t aInterval, uint32_t aDefaultInterval) const +uint32_t Client::DetermineLeaseInterval(uint32_t aInterval, uint32_t aDefaultInterval) const { - uint32_t boundedInterval = aDefaultInterval; + // Determine the lease or key lease interval. + // + // We use `aInterval` if it is non-zero, otherwise, use the + // `aDefaultInterval`. We also ensure that the returned value is + // never greater than `kMaxLease`. The `kMaxLease` is selected + // such the lease intervals in msec can still fit in a `uint32_t` + // `Time` variable (`kMaxLease` is ~ 24.8 days). - if (aInterval != 0) - { - boundedInterval = OT_MIN(aInterval, static_cast(kMaxLease)); - } + return Min(kMaxLease, (aInterval != kUnspecifiedInterval) ? aInterval : aDefaultInterval); +} + +uint32_t Client::DetermineTtl(void) const +{ + // Determine the TTL to use based on current `mLease`. + // If `mLease == 0`, it indicates we are removing host + // and so we use `mDefaultLease` instead. - return boundedInterval; + uint32_t lease = (mLease == 0) ? mDefaultLease : mLease; + + return (mTtl == kUnspecifiedInterval) ? lease : Min(mTtl, lease); } bool Client::ShouldRenewEarly(const Service &aService) const { // Check if we reached the service renew time or close to it. The // "early renew interval" is used to allow early refresh. It is - // calculated as a factor of the `mAcceptedLeaseInterval`. The - // "early lease renew factor" is given as a fraction (numerator and - // denominator). If the denominator is set to zero (i.e., factor is - // set to infinity), then service is always included in all SRP + // calculated as a factor of the service requested lease interval. + // The "early lease renew factor" is given as a fraction (numerator + // and denominator). If the denominator is set to zero (i.e., factor + // is set to infinity), then service is always included in all SRP // update messages. bool shouldRenew; #if OPENTHREAD_CONFIG_SRP_CLIENT_EARLY_LEASE_RENEW_FACTOR_DENOMINATOR != 0 - uint32_t earlyRenewInterval = - Time::SecToMsec(mAcceptedLeaseInterval) / kEarlyLeaseRenewFactorDenominator * kEarlyLeaseRenewFactorNumerator; + uint32_t earlyRenewInterval; + + earlyRenewInterval = Time::SecToMsec(DetermineLeaseInterval(aService.GetLease(), mDefaultLease)); + earlyRenewInterval = earlyRenewInterval / kEarlyLeaseRenewFactorDenominator * kEarlyLeaseRenewFactorNumerator; shouldRenew = (aService.GetLeaseRenewTime() <= TimerMilli::GetNow() + earlyRenewInterval); #else @@ -1756,11 +1902,6 @@ bool Client::ShouldRenewEarly(const Service &aService) const return shouldRenew; } -void Client::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void Client::HandleTimer(void) { switch (GetState()) @@ -1775,7 +1916,7 @@ void Client::HandleTimer(void) break; case kStateUpdating: - mSingleServiceMode.Disable(); + mSingleServiceMode = false; LogRetryWaitInterval(); LogInfo("Timed out, no response"); GrowRetryWaitInterval(); @@ -1789,9 +1930,9 @@ void Client::HandleTimer(void) // callback. It works correctly due to the guard check at the // top of `SelectNextServer()`. - mAutoStart.IncrementTimoutFailureCount(); + mAutoStart.IncrementTimeoutFailureCount(); - if (mAutoStart.GetTimoutFailureCount() >= kMaxTimeoutFailuresToSwitchServer) + if (mAutoStart.GetTimeoutFailureCount() >= kMaxTimeoutFailuresToSwitchServer) { SelectNextServer(kDisallowSwitchOnRegisteredHost); } @@ -2101,7 +2242,7 @@ void Client::LogRetryWaitInterval(void) const uint32_t interval = GetRetryWaitInterval(); - LogInfo("Retry interval %u %s", (interval < kLogInMsecLimit) ? interval : Time::MsecToSec(interval), + LogInfo("Retry interval %lu %s", ToUlong((interval < kLogInMsecLimit) ? interval : Time::MsecToSec(interval)), (interval < kLogInMsecLimit) ? "ms" : "sec"); } diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp index 46bdc33694c..992940cfd40 100644 --- a/src/core/net/srp_client.hpp +++ b/src/core/net/srp_client.hpp @@ -36,6 +36,7 @@ #include #include "common/as_core_type.hpp" +#include "common/callback.hpp" #include "common/clearable.hpp" #include "common/linked_list.hpp" #include "common/locator.hpp" @@ -97,7 +98,7 @@ class Client : public InstanceLocator, private NonCopyable * Please see `otSrpClientCallback` for more details. * */ - typedef otSrpClientCallback Callback; + typedef otSrpClientCallback ClientCallback; /** * This type represents an SRP client host info. @@ -106,6 +107,7 @@ class Client : public InstanceLocator, private NonCopyable class HostInfo : public otSrpClientHostInfo, private Clearable { friend class Client; + friend class Clearable; public: /** @@ -268,17 +270,45 @@ class Client : public InstanceLocator, private NonCopyable uint8_t GetNumTxtEntries(void) const { return mNumTxtEntries; } /** - * This method get the state of service. + * This method gets the state of service. * * @returns The service state. * */ ItemState GetState(void) const { return static_cast(mState); } + /** + * This method gets the desired lease interval to request when registering this service. + * + * @returns The desired lease interval in sec. Zero indicates to use default. + * + */ + uint32_t GetLease(void) const { return (mLease & kLeaseMask); } + + /** + * This method gets the desired key lease interval to request when registering this service. + * + * @returns The desired lease interval in sec. Zero indicates to use default. + * + */ + uint32_t GetKeyLease(void) const { return mKeyLease; } + private: + // We use the high (MSB) bit of `mLease` as flag to indicate + // whether or not the service is appended in the message. + // This is then used when updating the service state. Note that + // we guarantee that `mLease` is not greater than `kMaxLease` + // which ensures that the last bit is unused. + + static constexpr uint32_t kAppendedInMsgFlag = (1U << 31); + static constexpr uint32_t kLeaseMask = ~kAppendedInMsgFlag; + void SetState(ItemState aState); TimeMilli GetLeaseRenewTime(void) const { return TimeMilli(mData); } void SetLeaseRenewTime(TimeMilli aTime) { mData = aTime.GetValue(); } + bool IsAppendedInMessage(void) const { return mLease & kAppendedInMsgFlag; } + void MarkAsAppendedInMessage(void) { mLease |= kAppendedInMsgFlag; } + void ClearAppendedInMessageFlag(void) { mLease &= ~kAppendedInMsgFlag; } bool Matches(const Service &aOther) const; bool Matches(ItemState aState) const { return GetState() == aState; } }; @@ -343,12 +373,26 @@ class Client : public InstanceLocator, private NonCopyable * Config option `OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_DEFAULT_MODE` specifies the default auto-start mode * (whether it is enabled or disabled at the start of OT stack). * - * When auto-start is enabled, the SRP client will monitor the Thread Network Data for SRP Server Service entries - * and automatically start and stop the client when an SRP server is detected. + * When auto-start is enabled, the SRP client will monitor the Thread Network Data to discover SRP servers and + * select the preferred server and automatically start and stop the client when an SRP server is detected. + * + * There are three categories of Network Data entries indicating presence of SRP sever. They are preferred in the + * following order: + * + * 1) Preferred unicast entries where server address is included in the service data. If there are multiple + * options, the one with numerically lowest IPv6 address is preferred. + * + * 2) Anycast entries each having a seq number. A larger sequence number in the sense specified by Serial Number + * Arithmetic logic in RFC-1982 is considered more recent and therefore preferred. The largest seq number + * using serial number arithmetic is preferred if it is well-defined (i.e., the seq number is larger than all + * other seq numbers). If it is not well-defined, then the numerically largest seq number is preferred. + * + * 3) Unicast entries where the server address info is included in server data. If there are multiple options, + * the one with numerically lowest IPv6 address is preferred. * - * If multiple SRP servers are found, a random one will be selected. If the selected SRP server is no longer - * detected (not longer present in the Thread Network Data), the SRP client will be stopped and then it may switch - * to another SRP server (if available). + * When there is a change in the Network Data entries, client will check that the currently selected server is + * still present in the Network Data and is still the preferred one. Otherwise the client will switch to the new + * preferred server or stop if there is none. * * When the SRP client is explicitly started through a successful call to `Start()`, the given SRP server address * in `Start()` will continue to be used regardless of the state of auto-start mode and whether the same SRP @@ -418,7 +462,7 @@ class Client : public InstanceLocator, private NonCopyable * @param[in] aContext An arbitrary context used with @p aCallback. * */ - void SetCallback(Callback aCallback, void *aContext); + void SetCallback(ClientCallback aCallback, void *aContext) { mCallback.Set(aCallback, aContext); } /** * This method gets the TTL used in SRP update requests. @@ -431,7 +475,7 @@ class Client : public InstanceLocator, private NonCopyable * @returns The TTL (in seconds). * */ - uint32_t GetTtl(void) const { return (0 < mTtl && mTtl < mLeaseInterval) ? mTtl : mLeaseInterval; } + uint32_t GetTtl(void) const { return mTtl; } /** * This method sets the TTL used in SRP update requests. @@ -454,7 +498,7 @@ class Client : public InstanceLocator, private NonCopyable * @returns The lease interval (in seconds). * */ - uint32_t GetLeaseInterval(void) const { return mLeaseInterval; } + uint32_t GetLeaseInterval(void) const { return mDefaultLease; } /** * This method sets the lease interval used in SRP update requests. @@ -465,7 +509,7 @@ class Client : public InstanceLocator, private NonCopyable * @param[in] aInterval The lease interval (in seconds). If zero, the default value `kDefaultLease` would be used. * */ - void SetLeaseInterval(uint32_t aInterval) { mLeaseInterval = GetBoundedLeaseInterval(aInterval, kDefaultLease); } + void SetLeaseInterval(uint32_t aInterval) { mDefaultLease = DetermineLeaseInterval(aInterval, kDefaultLease); } /** * This method gets the key lease interval used in SRP update requests. @@ -473,7 +517,7 @@ class Client : public InstanceLocator, private NonCopyable * @returns The key lease interval (in seconds). * */ - uint32_t GetKeyLeaseInterval(void) const { return mKeyLeaseInterval; } + uint32_t GetKeyLeaseInterval(void) const { return mDefaultKeyLease; } /** * This method sets the key lease interval used in SRP update requests. @@ -487,7 +531,7 @@ class Client : public InstanceLocator, private NonCopyable */ void SetKeyLeaseInterval(uint32_t aInterval) { - mKeyLeaseInterval = GetBoundedLeaseInterval(aInterval, kDefaultKeyLease); + mDefaultKeyLease = DetermineLeaseInterval(aInterval, kDefaultKeyLease); } /** @@ -722,12 +766,38 @@ class Client : public InstanceLocator, private NonCopyable * */ bool IsServiceKeyRecordEnabled(void) const { return mServiceKeyRecordEnabled; } + + /** + * This method enables/disables "use short Update Lease Option" behavior. + * + * When enabled, the SRP client will use the short variant format of Update Lease Option in its message. The short + * format only includes the lease interval. + * + * This method is added under `REFERENCE_DEVICE` config and is intended to override the default behavior for + * testing only. + * + * @param[in] aUseShort TRUE to enable, FALSE to disable the "use short Update Lease Option" mode. + * + */ + void SetUseShortLeaseOption(bool aUseShort) { mUseShortLeaseOption = aUseShort; } + + /** + * This method gets the current "use short Update Lease Option" mode. + * + * @returns TRUE if "use short Update Lease Option" mode is enabled, FALSE otherwise. + * + */ + bool GetUseShortLeaseOption(void) const { return mUseShortLeaseOption; } #endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE private: // Number of fast data polls after SRP Update tx (11x 188ms = ~2 seconds) static constexpr uint8_t kFastPollsAfterUpdateTx = 11; +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + static constexpr uint32_t kSrpEcdsaKeyRef = Crypto::Storage::kEcdsaRef; +#endif + #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE static constexpr uint8_t kMaxTimeoutFailuresToSwitchServer = OPENTHREAD_CONFIG_SRP_CLIENT_MAX_TIMEOUT_FAILURES_TO_SWITCH_SERVER; @@ -836,6 +906,8 @@ class Client : public InstanceLocator, private NonCopyable // Port number to use when server is discovered using "network data anycast service". static constexpr uint16_t kAnycastServerPort = 53; + static constexpr uint32_t kUnspecifiedInterval = 0; // Used for lease/key-lease intervals. + // This enumeration type is used by the private `Start()` and // `Stop()` methods to indicate whether it is being requested by the // user or by the auto-start feature. @@ -855,28 +927,15 @@ class Client : public InstanceLocator, private NonCopyable kKeepRetryInterval, }; - class SingleServiceMode + // Used in `ChangeHostAndServiceStates()` + enum ServiceStateChangeMode : uint8_t { - public: - SingleServiceMode(void) - : mEnabled(false) - , mService(nullptr) - { - } - - void Enable(void) { mEnabled = true, mService = nullptr; } - void Disable(void) { mEnabled = false; } - bool IsEnabled(void) const { return mEnabled; } - Service *GetService(void) { return mService; } - void SetService(Service &aService) { mService = &aService; } - - private: - bool mEnabled; - Service *mService; + kForAllServices, + kForServicesAppendedInMessage, }; #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE - class AutoStart : Clearable + class AutoStart : public Clearable { public: enum State : uint8_t{ @@ -893,17 +952,17 @@ class Client : public InstanceLocator, private NonCopyable void SetState(State aState); uint8_t GetAnycastSeqNum(void) const { return mAnycastSeqNum; } void SetAnycastSeqNum(uint8_t aAnycastSeqNum) { mAnycastSeqNum = aAnycastSeqNum; } - void SetCallback(AutoStartCallback aCallback, void *aContext); + void SetCallback(AutoStartCallback aCallback, void *aContext) { mCallback.Set(aCallback, aContext); } void InvokeCallback(const Ip6::SockAddr *aServerSockAddr) const; #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE - uint8_t GetTimoutFailureCount(void) const { return mTimoutFailureCount; } - void ResetTimoutFailureCount(void) { mTimoutFailureCount = 0; } - void IncrementTimoutFailureCount(void) + uint8_t GetTimeoutFailureCount(void) const { return mTimeoutFailureCount; } + void ResetTimeoutFailureCount(void) { mTimeoutFailureCount = 0; } + void IncrementTimeoutFailureCount(void) { - if (mTimoutFailureCount < NumericLimits::kMax) + if (mTimeoutFailureCount < NumericLimits::kMax) { - mTimoutFailureCount++; + mTimeoutFailureCount++; } } #endif @@ -913,12 +972,11 @@ class Client : public InstanceLocator, private NonCopyable static const char *StateToString(State aState); - AutoStartCallback mCallback; - void * mContext; - State mState; - uint8_t mAnycastSeqNum; + Callback mCallback; + State mState; + uint8_t mAnycastSeqNum; #if OPENTHREAD_CONFIG_SRP_CLIENT_SWITCH_SERVER_ON_FAILURE - uint8_t mTimoutFailureCount; // Number of no-response timeout failures with the currently selected server. + uint8_t mTimeoutFailureCount; // Number of no-response timeout failures with the currently selected server. #endif }; #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE @@ -927,36 +985,46 @@ class Client : public InstanceLocator, private NonCopyable { static constexpr uint16_t kUnknownOffset = 0; // Unknown offset value (used when offset is not yet set). - uint16_t mDomainNameOffset; // Offset of domain name serialization - uint16_t mHostNameOffset; // Offset of host name serialization. - uint16_t mRecordCount; // Number of resource records in Update section. - Crypto::Ecdsa::P256::KeyPair mKeyPair; // The ECDSA key pair. + uint16_t mDomainNameOffset; // Offset of domain name serialization + uint16_t mHostNameOffset; // Offset of host name serialization. + uint16_t mRecordCount; // Number of resource records in Update section. +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + Crypto::Ecdsa::P256::KeyPairAsRef mKeyRef; // The ECDSA key ref for key-pair. +#else + Crypto::Ecdsa::P256::KeyPair mKeyPair; // The ECDSA key pair. +#endif }; - Error Start(const Ip6::SockAddr &aServerSockAddr, Requester aRequester); - void Stop(Requester aRequester, StopMode aMode); - void Resume(void); - void Pause(void); - void HandleNotifierEvents(Events aEvents); - void HandleRoleChanged(void); - Error UpdateHostInfoStateOnAddressChange(void); - void UpdateServiceStateToRemove(Service &aService); - State GetState(void) const { return mState; } - void SetState(State aState); - void ChangeHostAndServiceStates(const ItemState *aNewStates); - void InvokeCallback(Error aError) const; - void InvokeCallback(Error aError, const HostInfo &aHostInfo, const Service *aRemovedServices) const; - void HandleHostInfoOrServiceChange(void); - void SendUpdate(void); - Error PrepareUpdateMessage(Message &aMessage); - Error ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPair &aKeyPair); - Error AppendServiceInstructions(Service &aService, Message &aMessage, Info &aInfo); + Error Start(const Ip6::SockAddr &aServerSockAddr, Requester aRequester); + void Stop(Requester aRequester, StopMode aMode); + void Resume(void); + void Pause(void); + void HandleNotifierEvents(Events aEvents); + void HandleRoleChanged(void); + Error UpdateHostInfoStateOnAddressChange(void); + void UpdateServiceStateToRemove(Service &aService); + State GetState(void) const { return mState; } + void SetState(State aState); + void ChangeHostAndServiceStates(const ItemState *aNewStates, ServiceStateChangeMode aMode); + void InvokeCallback(Error aError) const; + void InvokeCallback(Error aError, const HostInfo &aHostInfo, const Service *aRemovedServices) const; + void HandleHostInfoOrServiceChange(void); + void SendUpdate(void); + Error PrepareUpdateMessage(Message &aMessage); +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + Error ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPairAsRef &aKeyRef); +#else + Error ReadOrGenerateKey(Crypto::Ecdsa::P256::KeyPair &aKeyPair); +#endif + Error AppendServiceInstructions(Message &aMessage, Info &aInfo); + bool CanAppendService(const Service &aService); + Error AppendServiceInstruction(Service &aService, Message &aMessage, Info &aInfo); Error AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo); Error AppendKeyRecord(Message &aMessage, Info &aInfo) const; Error AppendDeleteAllRrsets(Message &aMessage) const; Error AppendHostName(Message &aMessage, Info &aInfo, bool aDoNotCompress = false) const; Error AppendAaaaRecord(const Ip6::Address &aAddress, Message &aMessage, Info &aInfo) const; - Error AppendUpdateLeaseOptRecord(Message &aMessage) const; + Error AppendUpdateLeaseOptRecord(Message &aMessage); Error AppendSignature(Message &aMessage, Info &aInfo); void UpdateRecordLengthInMessage(Dns::ResourceRecord &aRecord, uint16_t aOffset, Message &aMessage) const; static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); @@ -969,9 +1037,9 @@ class Client : public InstanceLocator, private NonCopyable uint32_t GetRetryWaitInterval(void) const { return mRetryWaitInterval; } void ResetRetryWaitInterval(void) { mRetryWaitInterval = kMinRetryWaitInterval; } void GrowRetryWaitInterval(void); - uint32_t GetBoundedLeaseInterval(uint32_t aInterval, uint32_t aDefaultInterval) const; + uint32_t DetermineLeaseInterval(uint32_t aInterval, uint32_t aDefaultInterval) const; + uint32_t DetermineTtl(void) const; bool ShouldRenewEarly(const Service &aService) const; - static void HandleTimer(Timer &aTimer); void HandleTimer(void); #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE void ProcessAutoStart(void); @@ -992,32 +1060,35 @@ class Client : public InstanceLocator, private NonCopyable static_assert(kMaxTxFailureRetries < 16, "kMaxTxFailureRetries exceed the range of mTxFailureRetryCount (4-bit)"); + using DelayTimer = TimerMilliIn; + State mState; uint8_t mTxFailureRetryCount : 4; bool mShouldRemoveKeyLease : 1; bool mAutoHostAddressAddedMeshLocal : 1; + bool mSingleServiceMode : 1; #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE bool mServiceKeyRecordEnabled : 1; + bool mUseShortLeaseOption : 1; #endif uint16_t mUpdateMessageId; uint32_t mRetryWaitInterval; TimeMilli mLeaseRenewTime; - uint32_t mAcceptedLeaseInterval; uint32_t mTtl; - uint32_t mLeaseInterval; - uint32_t mKeyLeaseInterval; + uint32_t mLease; + uint32_t mKeyLease; + uint32_t mDefaultLease; + uint32_t mDefaultKeyLease; Ip6::Udp::Socket mSocket; - Callback mCallback; - void * mCallbackContext; - const char * mDomainName; - HostInfo mHostInfo; - LinkedList mServices; - SingleServiceMode mSingleServiceMode; - TimerMilli mTimer; + Callback mCallback; + const char *mDomainName; + HostInfo mHostInfo; + LinkedList mServices; + DelayTimer mTimer; #if OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE AutoStart mAutoStart; #endif diff --git a/src/core/net/srp_server.cpp b/src/core/net/srp_server.cpp index cdcee311feb..7125238fae4 100644 --- a/src/core/net/srp_server.cpp +++ b/src/core/net/srp_server.cpp @@ -41,6 +41,7 @@ #include "common/locator_getters.hpp" #include "common/log.hpp" #include "common/new.hpp" +#include "common/num_utils.hpp" #include "common/random.hpp" #include "net/dns_types.hpp" #include "thread/thread_netif.hpp" @@ -85,26 +86,21 @@ static Dns::UpdateHeader::Response ErrorToDnsResponseCode(Error aError) Server::Server(Instance &aInstance) : InstanceLocator(aInstance) , mSocket(aInstance) - , mServiceUpdateHandler(nullptr) - , mServiceUpdateHandlerContext(nullptr) - , mLeaseTimer(aInstance, HandleLeaseTimer) - , mOutstandingUpdatesTimer(aInstance, HandleOutstandingUpdatesTimer) + , mLeaseTimer(aInstance) + , mOutstandingUpdatesTimer(aInstance) , mServiceUpdateId(Random::NonCrypto::GetUint32()) , mPort(kUdpPortMin) , mState(kStateDisabled) , mAddressMode(kDefaultAddressMode) , mAnycastSequenceNumber(0) , mHasRegisteredAnyService(false) +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + , mAutoEnable(false) +#endif { IgnoreError(SetDomain(kDefaultDomain)); } -void Server::SetServiceHandler(otSrpServerServiceUpdateHandler aServiceHandler, void *aServiceHandlerContext) -{ - mServiceUpdateHandler = aServiceHandler; - mServiceUpdateHandlerContext = aServiceHandlerContext; -} - Error Server::SetAddressMode(AddressMode aMode) { Error error = kErrorNone; @@ -133,41 +129,71 @@ Error Server::SetAnycastModeSequenceNumber(uint8_t aSequenceNumber) void Server::SetEnabled(bool aEnabled) { +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + mAutoEnable = false; +#endif + if (aEnabled) { - VerifyOrExit(mState == kStateDisabled); - mState = kStateStopped; + Enable(); + } + else + { + Disable(); + } +} - // Request publishing of "DNS/SRP Address Service" entry in the - // Thread Network Data based of `mAddressMode`. Then wait for - // callback `HandleNetDataPublisherEntryChange()` from the - // `Publisher` to start the SRP server. +void Server::Enable(void) +{ + VerifyOrExit(mState == kStateDisabled); + mState = kStateStopped; - switch (mAddressMode) - { - case kAddressModeUnicast: - SelectPort(); - Get().PublishDnsSrpServiceUnicast(mPort); - break; + // Request publishing of "DNS/SRP Address Service" entry in the + // Thread Network Data based of `mAddressMode`. Then wait for + // callback `HandleNetDataPublisherEntryChange()` from the + // `Publisher` to start the SRP server. - case kAddressModeAnycast: - mPort = kAnycastAddressModePort; - Get().PublishDnsSrpServiceAnycast(mAnycastSequenceNumber); - break; - } - } - else + switch (mAddressMode) { - VerifyOrExit(mState != kStateDisabled); - Get().UnpublishDnsSrpService(); - Stop(); - mState = kStateDisabled; + case kAddressModeUnicast: + SelectPort(); + Get().PublishDnsSrpServiceUnicast(mPort); + break; + + case kAddressModeAnycast: + mPort = kAnycastAddressModePort; + Get().PublishDnsSrpServiceAnycast(mAnycastSequenceNumber); + break; } exit: return; } +void Server::Disable(void) +{ + VerifyOrExit(mState != kStateDisabled); + Get().UnpublishDnsSrpService(); + Stop(); + mState = kStateDisabled; + +exit: + return; +} + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +void Server::SetAutoEnableMode(bool aEnabled) +{ + VerifyOrExit(mAutoEnable != aEnabled); + mAutoEnable = aEnabled; + + Get().HandleSrpServerAutoEnableMode(); + +exit: + return; +} +#endif + Server::TtlConfig::TtlConfig(void) { mMinTtl = kDefaultMinTtl; @@ -189,7 +215,7 @@ uint32_t Server::TtlConfig::GrantTtl(uint32_t aLease, uint32_t aTtl) const { OT_ASSERT(mMinTtl <= mMaxTtl); - return OT_MAX(mMinTtl, OT_MIN(OT_MIN(mMaxTtl, aLease), aTtl)); + return Clamp(Min(aTtl, aLease), mMinTtl, mMaxTtl); } Server::LeaseConfig::LeaseConfig(void) @@ -222,14 +248,14 @@ uint32_t Server::LeaseConfig::GrantLease(uint32_t aLease) const { OT_ASSERT(mMinLease <= mMaxLease); - return (aLease == 0) ? 0 : OT_MAX(mMinLease, OT_MIN(mMaxLease, aLease)); + return (aLease == 0) ? 0 : Clamp(aLease, mMinLease, mMaxLease); } uint32_t Server::LeaseConfig::GrantKeyLease(uint32_t aKeyLease) const { OT_ASSERT(mMinKeyLease <= mMaxKeyLease); - return (aKeyLease == 0) ? 0 : OT_MAX(mMinKeyLease, OT_MIN(mMaxKeyLease, aKeyLease)); + return (aKeyLease == 0) ? 0 : Clamp(aKeyLease, mMinKeyLease, mMaxKeyLease); } Error Server::SetLeaseConfig(const LeaseConfig &aLeaseConfig) @@ -308,12 +334,12 @@ void Server::RemoveHost(Host *aHost, RetainName aRetainName, NotifyMode aNotifyS LogInfo("Fully remove host %s", aHost->GetFullName()); } - if (aNotifyServiceHandler && mServiceUpdateHandler != nullptr) + if (aNotifyServiceHandler && mServiceUpdateHandler.IsSet()) { uint32_t updateId = AllocateId(); - LogInfo("SRP update handler is notified (updatedId = %u)", updateId); - mServiceUpdateHandler(updateId, aHost, kDefaultEventsHandlerTimeout, mServiceUpdateHandlerContext); + LogInfo("SRP update handler is notified (updatedId = %lu)", ToUlong(updateId)); + mServiceUpdateHandler.Invoke(updateId, aHost, static_cast(kDefaultEventsHandlerTimeout)); // We don't wait for the reply from the service update handler, // but always remove the host (and its services) regardless of // host/service update result. Because removing a host should fail @@ -372,13 +398,14 @@ void Server::HandleServiceUpdateResult(ServiceUpdateId aId, Error aError) } else { - LogInfo("Delayed SRP host update result, the SRP update has been committed (updateId = %u)", aId); + LogInfo("Delayed SRP host update result, the SRP update has been committed (updateId = %lu)", ToUlong(aId)); } } void Server::HandleServiceUpdateResult(UpdateMetadata *aUpdate, Error aError) { - LogInfo("Handler result of SRP update (id = %u) is received: %s", aUpdate->GetId(), ErrorToString(aError)); + LogInfo("Handler result of SRP update (id = %lu) is received: %s", ToUlong(aUpdate->GetId()), + ErrorToString(aError)); IgnoreError(mOutstandingUpdates.Remove(*aUpdate)); CommitSrpUpdate(aError, *aUpdate); @@ -408,26 +435,26 @@ void Server::CommitSrpUpdate(Error aError, UpdateMetadata &aUpdateMetadata) } void Server::CommitSrpUpdate(Error aError, - Host & aHost, + Host &aHost, const Dns::UpdateHeader &aDnsHeader, - const Ip6::MessageInfo * aMessageInfo, - const TtlConfig & aTtlConfig, - const LeaseConfig & aLeaseConfig) -{ - Host * existingHost; - uint32_t hostLease; - uint32_t hostKeyLease; - uint32_t grantedLease; - uint32_t grantedKeyLease; + const Ip6::MessageInfo *aMessageInfo, + const TtlConfig &aTtlConfig, + const LeaseConfig &aLeaseConfig) +{ + Host *existingHost; uint32_t grantedTtl; - bool shouldFreeHost = true; + uint32_t hostLease = 0; + uint32_t hostKeyLease = 0; + uint32_t grantedLease = 0; + uint32_t grantedKeyLease = 0; + bool shouldFreeHost = true; SuccessOrExit(aError); hostLease = aHost.GetLease(); hostKeyLease = aHost.GetKeyLease(); grantedLease = aLeaseConfig.GrantLease(hostLease); - grantedKeyLease = aLeaseConfig.GrantKeyLease(hostKeyLease); + grantedKeyLease = aHost.ShouldUseShortLeaseOption() ? grantedLease : aLeaseConfig.GrantKeyLease(hostKeyLease); grantedTtl = aTtlConfig.GrantTtl(grantedLease, aHost.GetTtl()); aHost.SetLease(grantedLease); @@ -496,7 +523,7 @@ void Server::CommitSrpUpdate(Error aError, { if (aError == kErrorNone && !(grantedLease == hostLease && grantedKeyLease == hostKeyLease)) { - SendResponse(aDnsHeader, grantedLease, grantedKeyLease, *aMessageInfo); + SendResponse(aDnsHeader, grantedLease, grantedKeyLease, aHost.ShouldUseShortLeaseOption(), *aMessageInfo); } else { @@ -564,7 +591,7 @@ void Server::PrepareSocket(void) VerifyOrExit(!mSocket.IsOpen()); SuccessOrExit(error = mSocket.Open(HandleUdpReceive, this)); - error = mSocket.Bind(mPort, OT_NETIF_THREAD); + error = mSocket.Bind(mPort, Ip6::kNetifThread); exit: if (error != kErrorNone) @@ -700,7 +727,7 @@ void Server::ProcessDnsUpdate(Message &aMessage, MessageMetadata &aMetadata) if (FindOutstandingUpdate(aMetadata) != nullptr) { - LogInfo("Drop duplicated SRP update request: MessageId=%hu", aMetadata.mDnsHeader.GetMessageId()); + LogInfo("Drop duplicated SRP update request: MessageId=%u", aMetadata.mDnsHeader.GetMessageId()); // Silently drop duplicate requests. // This could rarely happen, because the outstanding SRP update timer should @@ -793,11 +820,11 @@ Error Server::ProcessUpdateSection(Host &aHost, const Message &aMessage, Message return error; } -Error Server::ProcessHostDescriptionInstruction(Host & aHost, - const Message & aMessage, +Error Server::ProcessHostDescriptionInstruction(Host &aHost, + const Message &aMessage, const MessageMetadata &aMetadata) const { - Error error; + Error error = kErrorNone; uint16_t offset = aMetadata.mOffset; OT_ASSERT(aHost.GetFullName() == nullptr); @@ -879,8 +906,8 @@ Error Server::ProcessHostDescriptionInstruction(Host & aHost, return error; } -Error Server::ProcessServiceDiscoveryInstructions(Host & aHost, - const Message & aMessage, +Error Server::ProcessServiceDiscoveryInstructions(Host &aHost, + const Message &aMessage, const MessageMetadata &aMetadata) const { Error error = kErrorNone; @@ -891,8 +918,8 @@ Error Server::ProcessServiceDiscoveryInstructions(Host & aHost, char serviceName[Dns::Name::kMaxNameSize]; char instanceName[Dns::Name::kMaxNameSize]; Dns::PtrRecord ptrRecord; - const char * subServiceName; - Service * service; + const char *subServiceName; + Service *service; bool isSubType; SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, serviceName, sizeof(serviceName))); @@ -930,10 +957,8 @@ Error Server::ProcessServiceDiscoveryInstructions(Host & aHost, } // Verify that instance name and service name are related. - - VerifyOrExit( - StringEndsWith(instanceName, isSubType ? subServiceName : serviceName, kStringCaseInsensitiveMatch), - error = kErrorFailed); + VerifyOrExit(Dns::Name::IsSubDomainOf(instanceName, isSubType ? subServiceName : serviceName), + error = kErrorFailed); // Ensure the same service does not exist already. VerifyOrExit(aHost.FindService(serviceName, instanceName) == nullptr, error = kErrorFailed); @@ -959,8 +984,8 @@ Error Server::ProcessServiceDiscoveryInstructions(Host & aHost, return error; } -Error Server::ProcessServiceDescriptionInstructions(Host & aHost, - const Message & aMessage, +Error Server::ProcessServiceDescriptionInstructions(Host &aHost, + const Message &aMessage, MessageMetadata &aMetadata) const { Error error = kErrorNone; @@ -1091,15 +1116,18 @@ Error Server::ProcessAdditionalSection(Host *aHost, const Message &aMessage, Mes SuccessOrExit(error = Dns::Name::ReadName(aMessage, offset, name, sizeof(name))); SuccessOrExit(error = aMessage.Read(offset, optRecord)); - SuccessOrExit(error = aMessage.Read(offset + sizeof(optRecord), leaseOption)); - VerifyOrExit(leaseOption.IsValid(), error = kErrorFailed); - VerifyOrExit(optRecord.GetSize() == sizeof(optRecord) + sizeof(leaseOption), error = kErrorParse); + + SuccessOrExit(error = leaseOption.ReadFrom(aMessage, offset + sizeof(optRecord), optRecord.GetLength())); offset += optRecord.GetSize(); aHost->SetLease(leaseOption.GetLeaseInterval()); aHost->SetKeyLease(leaseOption.GetKeyLeaseInterval()); + // If the client included the short variant of Lease Option, + // server must also use the short variant in its response. + aHost->SetUseShortLeaseOption(leaseOption.IsShortVariant()); + if (aHost->GetLease() > 0) { uint8_t hostAddressesNum; @@ -1150,12 +1178,12 @@ Error Server::ProcessAdditionalSection(Host *aHost, const Message &aMessage, Mes } Error Server::VerifySignature(const Dns::Ecdsa256KeyRecord &aKeyRecord, - const Message & aMessage, + const Message &aMessage, Dns::UpdateHeader aDnsHeader, uint16_t aSigOffset, uint16_t aSigRdataOffset, uint16_t aSigRdataLength, - const char * aSignerName) const + const char *aSignerName) const { Error error; uint16_t offset = aMessage.GetOffset(); @@ -1163,7 +1191,7 @@ Error Server::VerifySignature(const Dns::Ecdsa256KeyRecord &aKeyRecord, Crypto::Sha256 sha256; Crypto::Sha256::Hash hash; Crypto::Ecdsa::P256::Signature signature; - Message * signerNameMessage = nullptr; + Message *signerNameMessage = nullptr; VerifyOrExit(aSigRdataLength >= Crypto::Ecdsa::P256::Signature::kSize, error = kErrorInvalidArgs); @@ -1174,7 +1202,7 @@ Error Server::VerifySignature(const Dns::Ecdsa256KeyRecord &aKeyRecord, // The uncompressed (canonical) form of the signer name should be used for signature // verification. See https://tools.ietf.org/html/rfc2931#section-3.1 for details. - signerNameMessage = Get().NewMessage(0); + signerNameMessage = Get().NewMessage(); VerifyOrExit(signerNameMessage != nullptr, error = kErrorNoBufs); SuccessOrExit(error = Dns::Name::AppendName(aSignerName, *signerNameMessage)); sha256.Update(*signerNameMessage, signerNameMessage->GetOffset(), signerNameMessage->GetLength()); @@ -1297,7 +1325,50 @@ void Server::HandleUpdate(Host &aHost, const MessageMetadata &aMetadata) void Server::InformUpdateHandlerOrCommit(Error aError, Host &aHost, const MessageMetadata &aMetadata) { - if ((aError == kErrorNone) && (mServiceUpdateHandler != nullptr)) +#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) + if (aError == kErrorNone) + { + uint8_t numAddrs; + const Ip6::Address *addrs; + + LogInfo("Processed DNS update info"); + LogInfo(" Host:%s", aHost.GetFullName()); + LogInfo(" Lease:%lu, key-lease:%lu, ttl:%lu", ToUlong(aHost.GetLease()), ToUlong(aHost.GetKeyLease()), + ToUlong(aHost.GetTtl())); + + addrs = aHost.GetAddresses(numAddrs); + + if (numAddrs == 0) + { + LogInfo(" No host address"); + } + else + { + LogInfo(" %d host address(es):", numAddrs); + + for (; numAddrs > 0; addrs++, numAddrs--) + { + LogInfo(" %s", addrs->ToString().AsCString()); + } + } + + for (const Service &service : aHost.GetServices()) + { + char subLabel[Dns::Name::kMaxLabelSize]; + + IgnoreError(service.GetServiceSubTypeLabel(subLabel, sizeof(subLabel))); + + LogInfo(" %s service '%s'%s%s", service.IsDeleted() ? "Deleting" : "Adding", service.GetInstanceName(), + service.IsSubType() ? " subtype:" : "", subLabel); + } + } + else + { + LogInfo("Error %s processing received DNS update", ErrorToString(aError)); + } +#endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) + + if ((aError == kErrorNone) && mServiceUpdateHandler.IsSet()) { UpdateMetadata *update = UpdateMetadata::Allocate(GetInstance(), aHost, aMetadata); @@ -1306,8 +1377,8 @@ void Server::InformUpdateHandlerOrCommit(Error aError, Host &aHost, const Messag mOutstandingUpdates.Push(*update); mOutstandingUpdatesTimer.FireAtIfEarlier(update->GetExpireTime()); - LogInfo("SRP update handler is notified (updatedId = %u)", update->GetId()); - mServiceUpdateHandler(update->GetId(), &aHost, kDefaultEventsHandlerTimeout, mServiceUpdateHandlerContext); + LogInfo("SRP update handler is notified (updatedId = %lu)", ToUlong(update->GetId())); + mServiceUpdateHandler.Invoke(update->GetId(), &aHost, static_cast(kDefaultEventsHandlerTimeout)); ExitNow(); } @@ -1320,15 +1391,15 @@ void Server::InformUpdateHandlerOrCommit(Error aError, Host &aHost, const Messag return; } -void Server::SendResponse(const Dns::UpdateHeader & aHeader, +void Server::SendResponse(const Dns::UpdateHeader &aHeader, Dns::UpdateHeader::Response aResponseCode, - const Ip6::MessageInfo & aMessageInfo) + const Ip6::MessageInfo &aMessageInfo) { Error error; - Message * response = nullptr; + Message *response = nullptr; Dns::UpdateHeader header; - response = GetSocket().NewMessage(0); + response = GetSocket().NewMessage(); VerifyOrExit(response != nullptr, error = kErrorNoBufs); header.SetMessageId(aHeader.GetMessageId()); @@ -1361,15 +1432,17 @@ void Server::SendResponse(const Dns::UpdateHeader & aHeader, void Server::SendResponse(const Dns::UpdateHeader &aHeader, uint32_t aLease, uint32_t aKeyLease, - const Ip6::MessageInfo & aMessageInfo) + bool mUseShortLeaseOption, + const Ip6::MessageInfo &aMessageInfo) { Error error; - Message * response = nullptr; + Message *response = nullptr; Dns::UpdateHeader header; Dns::OptRecord optRecord; Dns::LeaseOption leaseOption; + uint16_t optionSize; - response = GetSocket().NewMessage(0); + response = GetSocket().NewMessage(); VerifyOrExit(response != nullptr, error = kErrorNoBufs); header.SetMessageId(aHeader.GetMessageId()); @@ -1385,17 +1458,25 @@ void Server::SendResponse(const Dns::UpdateHeader &aHeader, optRecord.Init(); optRecord.SetUdpPayloadSize(kUdpPayloadSize); optRecord.SetDnsSecurityFlag(); - optRecord.SetLength(sizeof(Dns::LeaseOption)); - SuccessOrExit(error = response->Append(optRecord)); - leaseOption.Init(); - leaseOption.SetLeaseInterval(aLease); - leaseOption.SetKeyLeaseInterval(aKeyLease); - SuccessOrExit(error = response->Append(leaseOption)); + if (mUseShortLeaseOption) + { + leaseOption.InitAsShortVariant(aLease); + } + else + { + leaseOption.InitAsLongVariant(aLease, aKeyLease); + } + + optionSize = static_cast(leaseOption.GetSize()); + optRecord.SetLength(optionSize); + + SuccessOrExit(error = response->Append(optRecord)); + SuccessOrExit(error = response->AppendBytes(&leaseOption, optionSize)); SuccessOrExit(error = GetSocket().SendTo(*response, aMessageInfo)); - LogInfo("Send success response with granted lease: %u and key lease: %u", aLease, aKeyLease); + LogInfo("Send success response with granted lease: %lu and key lease: %lu", ToUlong(aLease), ToUlong(aKeyLease)); UpdateResponseCounters(Dns::UpdateHeader::kResponseSuccess); @@ -1427,10 +1508,10 @@ Error Server::ProcessMessage(Message &aMessage, const Ip6::MessageInfo &aMessage return ProcessMessage(aMessage, TimerMilli::GetNow(), mTtlConfig, mLeaseConfig, &aMessageInfo); } -Error Server::ProcessMessage(Message & aMessage, +Error Server::ProcessMessage(Message &aMessage, TimeMilli aRxTime, - const TtlConfig & aTtlConfig, - const LeaseConfig & aLeaseConfig, + const TtlConfig &aTtlConfig, + const LeaseConfig &aLeaseConfig, const Ip6::MessageInfo *aMessageInfo) { Error error; @@ -1454,16 +1535,11 @@ Error Server::ProcessMessage(Message & aMessage, return error; } -void Server::HandleLeaseTimer(Timer &aTimer) -{ - aTimer.Get().HandleLeaseTimer(); -} - void Server::HandleLeaseTimer(void) { TimeMilli now = TimerMilli::GetNow(); TimeMilli earliestExpireTime = now.GetDistantFuture(); - Host * nextHost; + Host *nextHost; for (Host *host = mHosts.GetHead(); host != nullptr; host = nextHost) { @@ -1482,7 +1558,7 @@ void Server::HandleLeaseTimer(void) Service *next; - earliestExpireTime = OT_MIN(earliestExpireTime, host->GetKeyExpireTime()); + earliestExpireTime = Min(earliestExpireTime, host->GetKeyExpireTime()); // Check if any service instance name expired. for (Service *service = host->mServices.GetHead(); service != nullptr; service = next) @@ -1498,7 +1574,7 @@ void Server::HandleLeaseTimer(void) } else { - earliestExpireTime = OT_MIN(earliestExpireTime, service->GetKeyExpireTime()); + earliestExpireTime = Min(earliestExpireTime, service->GetKeyExpireTime()); } } } @@ -1515,7 +1591,7 @@ void Server::HandleLeaseTimer(void) RemoveHost(host, kRetainName, kNotifyServiceHandler); - earliestExpireTime = OT_MIN(earliestExpireTime, host->GetKeyExpireTime()); + earliestExpireTime = Min(earliestExpireTime, host->GetKeyExpireTime()); } else { @@ -1525,7 +1601,7 @@ void Server::HandleLeaseTimer(void) OT_ASSERT(!host->IsDeleted()); - earliestExpireTime = OT_MIN(earliestExpireTime, host->GetExpireTime()); + earliestExpireTime = Min(earliestExpireTime, host->GetExpireTime()); for (Service *service = host->mServices.GetHead(); service != nullptr; service = next) { @@ -1539,7 +1615,7 @@ void Server::HandleLeaseTimer(void) else if (service->mIsDeleted) { // The service has been deleted but the name retains. - earliestExpireTime = OT_MIN(earliestExpireTime, service->GetKeyExpireTime()); + earliestExpireTime = Min(earliestExpireTime, service->GetKeyExpireTime()); } else if (service->GetExpireTime() <= now) { @@ -1547,11 +1623,11 @@ void Server::HandleLeaseTimer(void) // The service is expired, delete it. host->RemoveService(service, kRetainName, kNotifyServiceHandler); - earliestExpireTime = OT_MIN(earliestExpireTime, service->GetKeyExpireTime()); + earliestExpireTime = Min(earliestExpireTime, service->GetKeyExpireTime()); } else { - earliestExpireTime = OT_MIN(earliestExpireTime, service->GetExpireTime()); + earliestExpireTime = Min(earliestExpireTime, service->GetExpireTime()); } } } @@ -1562,7 +1638,7 @@ void Server::HandleLeaseTimer(void) OT_ASSERT(earliestExpireTime >= now); if (!mLeaseTimer.IsRunning() || earliestExpireTime <= mLeaseTimer.GetFireTime()) { - LogInfo("Lease timer is scheduled for %u seconds", Time::MsecToSec(earliestExpireTime - now)); + LogInfo("Lease timer is scheduled for %lu seconds", ToUlong(Time::MsecToSec(earliestExpireTime - now))); mLeaseTimer.StartAt(earliestExpireTime, 0); } } @@ -1573,16 +1649,11 @@ void Server::HandleLeaseTimer(void) } } -void Server::HandleOutstandingUpdatesTimer(Timer &aTimer) -{ - aTimer.Get().HandleOutstandingUpdatesTimer(); -} - void Server::HandleOutstandingUpdatesTimer(void) { while (!mOutstandingUpdates.IsEmpty() && mOutstandingUpdates.GetTail()->GetExpireTime() <= TimerMilli::GetNow()) { - LogInfo("Outstanding service update timeout (updateId = %u)", mOutstandingUpdates.GetTail()->GetId()); + LogInfo("Outstanding service update timeout (updateId = %lu)", ToUlong(mOutstandingUpdates.GetTail()->GetId())); HandleServiceUpdateResult(mOutstandingUpdates.GetTail(), kErrorResponseTimeout); } } @@ -1686,13 +1757,22 @@ TimeMilli Server::Service::GetKeyExpireTime(void) const void Server::Service::GetLeaseInfo(LeaseInfo &aLeaseInfo) const { TimeMilli now = TimerMilli::GetNow(); - TimeMilli expireTime = GetExpireTime(); TimeMilli keyExpireTime = GetKeyExpireTime(); aLeaseInfo.mLease = Time::SecToMsec(GetLease()); aLeaseInfo.mKeyLease = Time::SecToMsec(GetKeyLease()); - aLeaseInfo.mRemainingLease = (now <= expireTime) ? (expireTime - now) : 0; aLeaseInfo.mRemainingKeyLease = (now <= keyExpireTime) ? (keyExpireTime - now) : 0; + + if (!mIsDeleted) + { + TimeMilli expireTime = GetExpireTime(); + + aLeaseInfo.mRemainingLease = (now <= expireTime) ? (expireTime - now) : 0; + } + else + { + aLeaseInfo.mRemainingLease = 0; + } } bool Server::Service::MatchesInstanceName(const char *aInstanceName) const @@ -1741,7 +1821,7 @@ void Server::Service::Log(Action aAction) const "Update existing", // (1) kUpdateExisting "Remove but retain name of", // (2) kRemoveButRetainName "Fully remove", // (3) kFullyRemove - "LEASE expired for ", // (4) kLeaseExpired + "LEASE expired for", // (4) kLeaseExpired "KEY LEASE expired for", // (5) kKeyLeaseExpired }; @@ -1768,9 +1848,7 @@ void Server::Service::Log(Action aAction) const } } #else -void Server::Service::Log(Action) const -{ -} +void Server::Service::Log(Action) const {} #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) //--------------------------------------------------------------------------------------------------------------------- @@ -1848,10 +1926,7 @@ Server::Host::Host(Instance &aInstance, TimeMilli aUpdateTime) mKeyRecord.Clear(); } -Server::Host::~Host(void) -{ - FreeAllServices(); -} +Server::Host::~Host(void) { FreeAllServices(); } Error Server::Host::SetFullName(const char *aFullName) { @@ -1892,21 +1967,27 @@ TimeMilli Server::Host::GetExpireTime(void) const return mUpdateTime + Time::SecToMsec(mLease); } -TimeMilli Server::Host::GetKeyExpireTime(void) const -{ - return mUpdateTime + Time::SecToMsec(mKeyLease); -} +TimeMilli Server::Host::GetKeyExpireTime(void) const { return mUpdateTime + Time::SecToMsec(mKeyLease); } void Server::Host::GetLeaseInfo(LeaseInfo &aLeaseInfo) const { TimeMilli now = TimerMilli::GetNow(); - TimeMilli expireTime = GetExpireTime(); TimeMilli keyExpireTime = GetKeyExpireTime(); aLeaseInfo.mLease = Time::SecToMsec(GetLease()); aLeaseInfo.mKeyLease = Time::SecToMsec(GetKeyLease()); - aLeaseInfo.mRemainingLease = (now <= expireTime) ? (expireTime - now) : 0; aLeaseInfo.mRemainingKeyLease = (now <= keyExpireTime) ? (keyExpireTime - now) : 0; + + if (!IsDeleted()) + { + TimeMilli expireTime = GetExpireTime(); + + aLeaseInfo.mRemainingLease = (now <= expireTime) ? (expireTime - now) : 0; + } + else + { + aLeaseInfo.mRemainingLease = 0; + } } Error Server::Host::ProcessTtl(uint32_t aTtl) @@ -1931,8 +2012,8 @@ Error Server::Host::ProcessTtl(uint32_t aTtl) const Server::Service *Server::Host::FindNextService(const Service *aPrevService, Service::Flags aFlags, - const char * aServiceName, - const char * aInstanceName) const + const char *aServiceName, + const char *aInstanceName) const { const Service *service = (aPrevService == nullptr) ? GetServices().GetHead() : aPrevService->GetNext(); @@ -1964,7 +2045,7 @@ Server::Service *Server::Host::AddNewService(const char *aServiceName, bool aIsSubType, TimeMilli aUpdateTime) { - Service * service = nullptr; + Service *service = nullptr; RetainPtr desc(FindServiceDescription(aInstanceName)); if (desc == nullptr) @@ -1992,12 +2073,12 @@ void Server::Host::RemoveService(Service *aService, RetainName aRetainName, Noti aService->Log(aRetainName ? Service::kRemoveButRetainName : Service::kFullyRemove); - if (aNotifyServiceHandler && server.mServiceUpdateHandler != nullptr) + if (aNotifyServiceHandler && server.mServiceUpdateHandler.IsSet()) { uint32_t updateId = server.AllocateId(); - LogInfo("SRP update handler is notified (updatedId = %u)", updateId); - server.mServiceUpdateHandler(updateId, this, kDefaultEventsHandlerTimeout, server.mServiceUpdateHandlerContext); + LogInfo("SRP update handler is notified (updatedId = %lu)", ToUlong(updateId)); + server.mServiceUpdateHandler.Invoke(updateId, this, static_cast(kDefaultEventsHandlerTimeout)); // We don't wait for the reply from the service update handler, // but always remove the service regardless of service update result. // Because removing a service should fail only when there is system @@ -2042,10 +2123,7 @@ void Server::Host::FreeAllServices(void) } } -void Server::Host::ClearResources(void) -{ - mAddresses.Free(); -} +void Server::Host::ClearResources(void) { mAddresses.Free(); } Error Server::Host::MergeServicesAndResourcesFrom(Host &aHost) { @@ -2071,7 +2149,7 @@ Error Server::Host::MergeServicesAndResourcesFrom(Host &aHost) if (service.mIsDeleted) { - // `RemoveService()` does nothing if `exitsingService` is `nullptr`. + // `RemoveService()` does nothing if `existingService` is `nullptr`. RemoveService(existingService, kRetainName, kDoNotNotifyServiceHandler); continue; } diff --git a/src/core/net/srp_server.hpp b/src/core/net/srp_server.hpp index b2c371688f2..b88b136568c 100644 --- a/src/core/net/srp_server.hpp +++ b/src/core/net/srp_server.hpp @@ -55,6 +55,7 @@ #include "common/array.hpp" #include "common/as_core_type.hpp" +#include "common/callback.hpp" #include "common/clearable.hpp" #include "common/heap.hpp" #include "common/heap_allocatable.hpp" @@ -65,6 +66,7 @@ #include "common/locator.hpp" #include "common/non_copyable.hpp" #include "common/notifier.hpp" +#include "common/num_utils.hpp" #include "common/numeric_limits.hpp" #include "common/retain_ptr.hpp" #include "common/timer.hpp" @@ -91,6 +93,12 @@ class Server; } } // namespace Dns +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +namespace BorderRouter { +class RoutingManager; +} +#endif + namespace Srp { /** @@ -104,6 +112,9 @@ class Server : public InstanceLocator, private NonCopyable friend class Service; friend class Host; friend class Dns::ServiceDiscovery::Server; +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + friend class BorderRouter::RoutingManager; +#endif enum RetainName : bool { @@ -150,11 +161,15 @@ class Server : public InstanceLocator, private NonCopyable class Host; + /** + * This enumeration represents the state of SRP server. + * + */ enum State : uint8_t { - kStateDisabled = OT_SRP_SERVER_STATE_DISABLED, - kStateRunning = OT_SRP_SERVER_STATE_RUNNING, - kStateStopped = OT_SRP_SERVER_STATE_STOPPED, + kStateDisabled = OT_SRP_SERVER_STATE_DISABLED, ///< Server is disabled. + kStateRunning = OT_SRP_SERVER_STATE_RUNNING, ///< Server is enabled and running. + kStateStopped = OT_SRP_SERVER_STATE_STOPPED, ///< Server is enabled but stopped. }; /** @@ -393,7 +408,7 @@ class Server : public InstanceLocator, private NonCopyable Description *mNext; Heap::String mInstanceName; - Host * mHost; + Host *mHost; Heap::Data mTxtData; uint16_t mPriority; uint16_t mWeight; @@ -421,7 +436,7 @@ class Server : public InstanceLocator, private NonCopyable Heap::String mServiceName; RetainPtr mDescription; - Service * mNext; + Service *mNext; TimeMilli mUpdateTime; bool mIsDeleted : 1; bool mIsSubType : 1; @@ -472,7 +487,8 @@ class Server : public InstanceLocator, private NonCopyable */ const Ip6::Address *GetAddresses(uint8_t &aAddressesNum) const { - aAddressesNum = static_cast(OT_MIN(mAddresses.GetLength(), NumericLimits::kMax)); + aAddressesNum = ClampToUint8(mAddresses.GetLength()); + return mAddresses.AsCArray(); } @@ -555,8 +571,8 @@ class Server : public InstanceLocator, private NonCopyable */ const Service *FindNextService(const Service *aPrevService, Service::Flags aFlags = kFlagsAnyService, - const char * aServiceName = nullptr, - const char * aInstanceName = nullptr) const; + const char *aServiceName = nullptr, + const char *aInstanceName = nullptr) const; /** * This method tells whether the host matches a given full name. @@ -577,10 +593,12 @@ class Server : public InstanceLocator, private NonCopyable void SetTtl(uint32_t aTtl) { mTtl = aTtl; } void SetLease(uint32_t aLease) { mLease = aLease; } void SetKeyLease(uint32_t aKeyLease) { mKeyLease = aKeyLease; } + void SetUseShortLeaseOption(bool aUse) { mUseShortLeaseOption = aUse; } + bool ShouldUseShortLeaseOption(void) const { return mUseShortLeaseOption; } Error ProcessTtl(uint32_t aTtl); LinkedList &GetServices(void) { return mServices; } - Service * AddNewService(const char *aServiceName, + Service *AddNewService(const char *aServiceName, const char *aInstanceName, bool aIsSubType, TimeMilli aUpdateTime); @@ -593,12 +611,12 @@ class Server : public InstanceLocator, private NonCopyable bool HasServiceInstance(const char *aInstanceName) const; RetainPtr FindServiceDescription(const char *aInstanceName); const RetainPtr FindServiceDescription(const char *aInstanceName) const; - Service * FindService(const char *aServiceName, const char *aInstanceName); - const Service * FindService(const char *aServiceName, const char *aInstanceName) const; - Service * FindBaseService(const char *aInstanceName); - const Service * FindBaseService(const char *aInstanceName) const; + Service *FindService(const char *aServiceName, const char *aInstanceName); + const Service *FindService(const char *aServiceName, const char *aInstanceName) const; + Service *FindBaseService(const char *aInstanceName); + const Service *FindBaseService(const char *aInstanceName) const; - Host * mNext; + Host *mNext; Heap::String mFullName; Heap::Array mAddresses; @@ -610,6 +628,7 @@ class Server : public InstanceLocator, private NonCopyable uint32_t mKeyLease; // The KEY-LEASE time in seconds. TimeMilli mUpdateTime; LinkedList mServices; + bool mUseShortLeaseOption; // Use short lease option (lease only - 4 byte) when responding. }; /** @@ -703,7 +722,10 @@ class Server : public InstanceLocator, private NonCopyable * @sa HandleServiceUpdateResult * */ - void SetServiceHandler(otSrpServerServiceUpdateHandler aServiceHandler, void *aServiceHandlerContext); + void SetServiceHandler(otSrpServerServiceUpdateHandler aServiceHandler, void *aServiceHandlerContext) + { + mServiceUpdateHandler.Set(aServiceHandler, aServiceHandlerContext); + } /** * This method returns the domain authorized to the SRP server. @@ -773,17 +795,9 @@ class Server : public InstanceLocator, private NonCopyable Error SetAnycastModeSequenceNumber(uint8_t aSequenceNumber); /** - * This method tells whether the SRP server is currently running. + * This method returns the state of the SRP server. * - * @returns A boolean that indicates whether the server is running. - * - */ - bool IsRunning(void) const { return (mState == kStateRunning); } - - /** - * This method tells the state of the SRP server. - * - * @returns An enum that represents the state of the server. + * @returns The state of the server. * */ State GetState(void) const { return mState; } @@ -791,10 +805,10 @@ class Server : public InstanceLocator, private NonCopyable /** * This method tells the port the SRP server is listening to. * - * @returns An integer that represents the port of the server. It returns 0 if the SRP server is not running. + * @returns The port of the server or 0 if the SRP server is not running. * */ - uint16_t GetPort(void) const { return IsRunning() ? mPort : 0; } + uint16_t GetPort(void) const { return (mState == kStateRunning) ? mPort : 0; } /** * This method enables/disables the SRP server. @@ -804,6 +818,35 @@ class Server : public InstanceLocator, private NonCopyable */ void SetEnabled(bool aEnabled); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + /** + * This method enables/disables the auto-enable mode on SRP server. + * + * When this mode is enabled, the Border Routing Manager controls if/when to enable or disable the SRP server. + * SRP sever is auto-enabled if/when Border Routing is started it is done with the initial prefix and route + * configurations (when the OMR and on-link prefixes are determined, advertised in emitted Router Advert message on + * infrastructure side and published in the Thread Network Data). The SRP server is auto-disabled when BR is + * stopped (e.g., if the infrastructure network interface is brought down or if BR gets detached). + * + * This mode can be disabled by a `SetAutoEnableMode(false)` call or if the SRP server is explicitly enabled or + * disabled by a call to `SetEnabled()` method. Disabling auto-enable mode using `SetAutoEnableMode(false` call + * will not change the current state of SRP sever (e.g., if it is enabled it stays enabled). + * + * @param[in] aEnabled A boolean to enable/disable the auto-enable mode. + * + */ + void SetAutoEnableMode(bool aEnabled); + + /** + * This method indicates whether the auto-enable mode is enabled or disabled. + * + * @retval TRUE The auto-enable mode is enabled. + * @retval FALSE The auto-enable mode is disabled. + * + */ + bool IsAutoEnableMode(void) const { return mAutoEnable; } +#endif + /** * This method returns the TTL configuration. * @@ -877,20 +920,20 @@ class Server : public InstanceLocator, private NonCopyable private: static constexpr uint16_t kUdpPayloadSize = Ip6::kMaxDatagramLength - sizeof(Ip6::Udp::Header); - static constexpr uint32_t kDefaultMinTtl = 60u; // 1 min (in seconds). - static constexpr uint32_t kDefaultMaxTtl = 3600u * 2; // 2 hours (in seconds). - static constexpr uint32_t kDefaultMinLease = 60u * 30; // 30 min (in seconds). - static constexpr uint32_t kDefaultMaxLease = 3600u * 2; // 2 hours (in seconds). - static constexpr uint32_t kDefaultMinKeyLease = 3600u * 24; // 1 day (in seconds). - static constexpr uint32_t kDefaultMaxKeyLease = 3600u * 24 * 14; // 14 days (in seconds). + static constexpr uint32_t kDefaultMinLease = 30; // 30 seconds. + static constexpr uint32_t kDefaultMaxLease = 27u * 3600; // 27 hours (in seconds). + static constexpr uint32_t kDefaultMinKeyLease = 30; // 30 seconds. + static constexpr uint32_t kDefaultMaxKeyLease = 189u * 3600; // 189 hours (in seconds). + static constexpr uint32_t kDefaultMinTtl = kDefaultMinLease; + static constexpr uint32_t kDefaultMaxTtl = kDefaultMaxLease; static constexpr uint32_t kDefaultEventsHandlerTimeout = OPENTHREAD_CONFIG_SRP_SERVER_SERVICE_UPDATE_TIMEOUT; static constexpr AddressMode kDefaultAddressMode = - static_cast(OPENTHREAD_CONFIG_SRP_SERVER_DEFAULT_ADDDRESS_MODE); + static_cast(OPENTHREAD_CONFIG_SRP_SERVER_DEFAULT_ADDRESS_MODE); static constexpr uint16_t kAnycastAddressModePort = 53; - // Metdata for a received SRP Update message. + // Metadata for a received SRP Update message. struct MessageMetadata { // Indicates whether the `Message` is received directly from a @@ -919,27 +962,29 @@ class Server : public InstanceLocator, private NonCopyable TimeMilli GetExpireTime(void) const { return mExpireTime; } const Dns::UpdateHeader &GetDnsHeader(void) const { return mDnsHeader; } ServiceUpdateId GetId(void) const { return mId; } - const TtlConfig & GetTtlConfig(void) const { return mTtlConfig; } - const LeaseConfig & GetLeaseConfig(void) const { return mLeaseConfig; } - Host & GetHost(void) { return mHost; } - const Ip6::MessageInfo & GetMessageInfo(void) const { return mMessageInfo; } + const TtlConfig &GetTtlConfig(void) const { return mTtlConfig; } + const LeaseConfig &GetLeaseConfig(void) const { return mLeaseConfig; } + Host &GetHost(void) { return mHost; } + const Ip6::MessageInfo &GetMessageInfo(void) const { return mMessageInfo; } bool IsDirectRxFromClient(void) const { return mIsDirectRxFromClient; } bool Matches(ServiceUpdateId aId) const { return mId == aId; } private: UpdateMetadata(Instance &aInstance, Host &aHost, const MessageMetadata &aMessageMetadata); - UpdateMetadata * mNext; + UpdateMetadata *mNext; TimeMilli mExpireTime; Dns::UpdateHeader mDnsHeader; ServiceUpdateId mId; // The ID of this service update transaction. TtlConfig mTtlConfig; // TTL config to use when processing the message. LeaseConfig mLeaseConfig; // Lease config to use when processing the message. - Host & mHost; // The `UpdateMetadata` has no ownership of this host. + Host &mHost; // The `UpdateMetadata` has no ownership of this host. Ip6::MessageInfo mMessageInfo; // Valid when `mIsDirectRxFromClient` is true. bool mIsDirectRxFromClient; }; + void Enable(void); + void Disable(void); void Start(void); void Stop(void); void SelectPort(void); @@ -959,34 +1004,34 @@ class Server : public InstanceLocator, private NonCopyable void CommitSrpUpdate(Error aError, Host &aHost, const MessageMetadata &aMessageMetadata); void CommitSrpUpdate(Error aError, UpdateMetadata &aUpdateMetadata); void CommitSrpUpdate(Error aError, - Host & aHost, + Host &aHost, const Dns::UpdateHeader &aDnsHeader, - const Ip6::MessageInfo * aMessageInfo, - const TtlConfig & aTtlConfig, - const LeaseConfig & aLeaseConfig); + const Ip6::MessageInfo *aMessageInfo, + const TtlConfig &aTtlConfig, + const LeaseConfig &aLeaseConfig); Error ProcessMessage(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - Error ProcessMessage(Message & aMessage, + Error ProcessMessage(Message &aMessage, TimeMilli aRxTime, - const TtlConfig & aTtlConfig, - const LeaseConfig & aLeaseConfig, + const TtlConfig &aTtlConfig, + const LeaseConfig &aLeaseConfig, const Ip6::MessageInfo *aMessageInfo); void ProcessDnsUpdate(Message &aMessage, MessageMetadata &aMetadata); Error ProcessUpdateSection(Host &aHost, const Message &aMessage, MessageMetadata &aMetadata) const; Error ProcessAdditionalSection(Host *aHost, const Message &aMessage, MessageMetadata &aMetadata) const; Error VerifySignature(const Dns::Ecdsa256KeyRecord &aKeyRecord, - const Message & aMessage, + const Message &aMessage, Dns::UpdateHeader aDnsHeader, uint16_t aSigOffset, uint16_t aSigRdataOffset, uint16_t aSigRdataLength, - const char * aSignerName) const; + const char *aSignerName) const; Error ValidateServiceSubTypes(Host &aHost, const MessageMetadata &aMetadata); Error ProcessZoneSection(const Message &aMessage, MessageMetadata &aMetadata) const; - Error ProcessHostDescriptionInstruction(Host & aHost, - const Message & aMessage, + Error ProcessHostDescriptionInstruction(Host &aHost, + const Message &aMessage, const MessageMetadata &aMetadata) const; - Error ProcessServiceDiscoveryInstructions(Host & aHost, - const Message & aMessage, + Error ProcessServiceDiscoveryInstructions(Host &aHost, + const Message &aMessage, const MessageMetadata &aMetadata) const; Error ProcessServiceDescriptionInstructions(Host &aHost, const Message &aMessage, MessageMetadata &aMetadata) const; @@ -996,29 +1041,32 @@ class Server : public InstanceLocator, private NonCopyable void AddHost(Host &aHost); void RemoveHost(Host *aHost, RetainName aRetainName, NotifyMode aNotifyServiceHandler); bool HasNameConflictsWith(Host &aHost) const; - void SendResponse(const Dns::UpdateHeader & aHeader, + void SendResponse(const Dns::UpdateHeader &aHeader, Dns::UpdateHeader::Response aResponseCode, - const Ip6::MessageInfo & aMessageInfo); + const Ip6::MessageInfo &aMessageInfo); void SendResponse(const Dns::UpdateHeader &aHeader, uint32_t aLease, uint32_t aKeyLease, - const Ip6::MessageInfo & aMessageInfo); + bool mUseShortLeaseOption, + const Ip6::MessageInfo &aMessageInfo); static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleLeaseTimer(Timer &aTimer); void HandleLeaseTimer(void); static void HandleOutstandingUpdatesTimer(Timer &aTimer); void HandleOutstandingUpdatesTimer(void); void HandleServiceUpdateResult(UpdateMetadata *aUpdate, Error aError); const UpdateMetadata *FindOutstandingUpdate(const MessageMetadata &aMessageMetadata) const; - static const char * AddressModeToString(AddressMode aMode); + static const char *AddressModeToString(AddressMode aMode); void UpdateResponseCounters(Dns::Header::Response aResponseCode); - Ip6::Udp::Socket mSocket; - otSrpServerServiceUpdateHandler mServiceUpdateHandler; - void * mServiceUpdateHandlerContext; + using LeaseTimer = TimerMilliIn; + using UpdateTimer = TimerMilliIn; + + Ip6::Udp::Socket mSocket; + + Callback mServiceUpdateHandler; Heap::String mDomain; @@ -1026,9 +1074,9 @@ class Server : public InstanceLocator, private NonCopyable LeaseConfig mLeaseConfig; LinkedList mHosts; - TimerMilli mLeaseTimer; + LeaseTimer mLeaseTimer; - TimerMilli mOutstandingUpdatesTimer; + UpdateTimer mOutstandingUpdatesTimer; LinkedList mOutstandingUpdates; ServiceUpdateId mServiceUpdateId; @@ -1037,6 +1085,9 @@ class Server : public InstanceLocator, private NonCopyable AddressMode mAddressMode; uint8_t mAnycastSequenceNumber; bool mHasRegisteredAnyService : 1; +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + bool mAutoEnable : 1; +#endif otSrpServerResponseCounters mResponseCounters; }; diff --git a/src/core/net/tcp6.cpp b/src/core/net/tcp6.cpp index 9b18a06dda5..47c50278ed8 100644 --- a/src/core/net/tcp6.cpp +++ b/src/core/net/tcp6.cpp @@ -43,6 +43,7 @@ #include "common/instance.hpp" #include "common/locator_getters.hpp" #include "common/log.hpp" +#include "common/num_utils.hpp" #include "common/random.hpp" #include "net/checksum.hpp" #include "net/ip6.hpp" @@ -71,8 +72,8 @@ static_assert(offsetof(Tcp::Listener, mTcbListen) == 0, "mTcbListen field in otT Tcp::Tcp(Instance &aInstance) : InstanceLocator(aInstance) - , mTimer(aInstance, Tcp::HandleTimer) - , mTasklet(aInstance, Tcp::HandleTasklet) + , mTimer(aInstance) + , mTasklet(aInstance) , mEphemeralPort(kDynamicPortMin) { OT_UNUSED_VARIABLE(mEphemeralPort); @@ -123,10 +124,7 @@ Error Tcp::Endpoint::Initialize(Instance &aInstance, const otTcpEndpointInitiali return error; } -Instance &Tcp::Endpoint::GetInstance(void) const -{ - return AsNonConst(AsCoreType(GetTcb().instance)); -} +Instance &Tcp::Endpoint::GetInstance(void) const { return AsNonConst(AsCoreType(GetTcb().instance)); } const SockAddr &Tcp::Endpoint::GetLocalAddress(void) const { @@ -171,7 +169,7 @@ Error Tcp::Endpoint::Bind(const SockAddr &aSockName) Error Tcp::Endpoint::Connect(const SockAddr &aSockName, uint32_t aFlags) { Error error = kErrorNone; - struct tcpcb & tp = GetTcb(); + struct tcpcb &tp = GetTcb(); struct sockaddr_in6 sin6p; OT_UNUSED_VARIABLE(aFlags); @@ -233,7 +231,11 @@ Error Tcp::Endpoint::ReceiveByReference(const otLinkedBuffer *&aBuffer) Error Tcp::Endpoint::ReceiveContiguify(void) { - return kErrorNotImplemented; + struct tcpcb &tp = GetTcb(); + + cbuf_contiguify(&tp.recvbuf, tp.reassbmp); + + return kErrorNone; } Error Tcp::Endpoint::CommitReceive(size_t aNumBytes, uint32_t aFlags) @@ -282,10 +284,7 @@ Error Tcp::Endpoint::Deinitialize(void) return error; } -bool Tcp::Endpoint::IsClosed(void) const -{ - return GetTcb().t_state == TCP6S_CLOSED; -} +bool Tcp::Endpoint::IsClosed(void) const { return GetTcb().t_state == TCP6S_CLOSED; } uint8_t Tcp::Endpoint::TimerFlagToIndex(uint8_t aTimerFlag) { @@ -431,7 +430,7 @@ bool Tcp::Endpoint::FirePendingTimers(TimeMilli aNow, bool &aHasFutureTimer, Tim else { aHasFutureTimer = true; - aEarliestFutureExpiry = OT_MIN(aEarliestFutureExpiry, expiry); + aEarliestFutureExpiry = Min(aEarliestFutureExpiry, expiry); } } } @@ -478,25 +477,16 @@ size_t Tcp::Endpoint::GetInFlightBytes(void) const return tp.snd_max - tp.snd_una; } -size_t Tcp::Endpoint::GetBacklogBytes(void) const -{ - return GetSendBufferBytes() - GetInFlightBytes(); -} +size_t Tcp::Endpoint::GetBacklogBytes(void) const { return GetSendBufferBytes() - GetInFlightBytes(); } -Address &Tcp::Endpoint::GetLocalIp6Address(void) -{ - return *reinterpret_cast
(&GetTcb().laddr); -} +Address &Tcp::Endpoint::GetLocalIp6Address(void) { return *reinterpret_cast
(&GetTcb().laddr); } const Address &Tcp::Endpoint::GetLocalIp6Address(void) const { return *reinterpret_cast(&GetTcb().laddr); } -Address &Tcp::Endpoint::GetForeignIp6Address(void) -{ - return *reinterpret_cast
(&GetTcb().faddr); -} +Address &Tcp::Endpoint::GetForeignIp6Address(void) { return *reinterpret_cast
(&GetTcb().faddr); } const Address &Tcp::Endpoint::GetForeignIp6Address(void) const { @@ -538,10 +528,7 @@ Error Tcp::Listener::Initialize(Instance &aInstance, const otTcpListenerInitiali return error; } -Instance &Tcp::Listener::GetInstance(void) const -{ - return AsNonConst(AsCoreType(GetTcbListen().instance)); -} +Instance &Tcp::Listener::GetInstance(void) const { return AsNonConst(AsCoreType(GetTcbListen().instance)); } Error Tcp::Listener::Listen(const SockAddr &aSockName) { @@ -581,15 +568,9 @@ Error Tcp::Listener::Deinitialize(void) return error; } -bool Tcp::Listener::IsClosed(void) const -{ - return GetTcbListen().t_state == TCP6S_CLOSED; -} +bool Tcp::Listener::IsClosed(void) const { return GetTcbListen().t_state == TCP6S_CLOSED; } -Address &Tcp::Listener::GetLocalIp6Address(void) -{ - return *reinterpret_cast
(&GetTcbListen().laddr); -} +Address &Tcp::Listener::GetLocalIp6Address(void) { return *reinterpret_cast
(&GetTcbListen().laddr); } const Address &Tcp::Listener::GetLocalIp6Address(void) const { @@ -625,7 +606,7 @@ Error Tcp::HandleMessage(ot::Ip6::Header &aIp6Header, Message &aMessage, Message uint8_t headerSize; struct ip6_hdr *ip6Header; - struct tcphdr * tcpHeader; + struct tcphdr *tcpHeader; Endpoint *endpoint; Endpoint *endpointPrev; @@ -655,7 +636,7 @@ Error Tcp::HandleMessage(ot::Ip6::Header &aIp6Header, Message &aMessage, Message { struct tcplp_signals sig; int nextAction; - struct tcpcb * tp = &endpoint->GetTcb(); + struct tcpcb *tp = &endpoint->GetTcb(); otLinkedBuffer *priorHead = lbuf_head(&tp->sendbuf); size_t priorBacklog = endpoint->GetSendBufferBytes() - endpoint->GetInFlightBytes(); @@ -686,10 +667,10 @@ Error Tcp::HandleMessage(ot::Ip6::Header &aIp6Header, Message &aMessage, Message return error; } -void Tcp::ProcessSignals(Endpoint & aEndpoint, - otLinkedBuffer * aPriorHead, +void Tcp::ProcessSignals(Endpoint &aEndpoint, + otLinkedBuffer *aPriorHead, size_t aPriorBacklog, - struct tcplp_signals &aSignals) + struct tcplp_signals &aSignals) const { VerifyOrExit(IsInitialized(aEndpoint) && !aEndpoint.IsClosed()); if (aSignals.conn_established && aEndpoint.mEstablishedCallback != nullptr) @@ -799,14 +780,11 @@ bool Tcp::AutoBind(const SockAddr &aPeer, SockAddr &aToBind, bool aBindAddress, if (aBindAddress) { - MessageInfo peerInfo; - const Netif::UnicastAddress *netifAddress; - - peerInfo.Clear(); - peerInfo.SetPeerAddr(aPeer.GetAddress()); - netifAddress = Get().SelectSourceAddress(peerInfo); - VerifyOrExit(netifAddress != nullptr, success = false); - aToBind.GetAddress() = netifAddress->GetAddress(); + const Address *source; + + source = Get().SelectSourceAddress(aPeer.GetAddress()); + VerifyOrExit(source != nullptr, success = false); + aToBind.SetAddress(*source); } if (aBindPort) @@ -844,20 +822,13 @@ bool Tcp::AutoBind(const SockAddr &aPeer, SockAddr &aToBind, bool aBindAddress, return success; } -void Tcp::HandleTimer(Timer &aTimer) -{ - OT_ASSERT(&aTimer == &aTimer.Get().mTimer); - LogDebg("Main TCP timer expired"); - aTimer.Get().ProcessTimers(); -} - -void Tcp::ProcessTimers(void) +void Tcp::HandleTimer(void) { TimeMilli now = TimerMilli::GetNow(); bool pendingTimer; TimeMilli earliestPendingTimerExpiry; - OT_ASSERT(!mTimer.IsRunning()); + LogDebg("Main TCP timer expired"); /* * The timer callbacks could potentially set/reset/cancel timers. @@ -913,13 +884,6 @@ void Tcp::ProcessTimers(void) } } -void Tcp::HandleTasklet(Tasklet &aTasklet) -{ - OT_ASSERT(&aTasklet == &aTasklet.Get().mTasklet); - LogDebg("TCP tasklet invoked"); - aTasklet.Get().ProcessCallbacks(); -} - void Tcp::ProcessCallbacks(void) { for (Endpoint &endpoint : mEndpoints) @@ -953,7 +917,7 @@ extern "C" { otMessage *tcplp_sys_new_message(otInstance *aInstance) { Instance &instance = AsCoreType(aInstance); - Message * message = instance.Get().NewMessage(0); + Message *message = instance.Get().NewMessage(0); if (message) { @@ -972,8 +936,8 @@ void tcplp_sys_free_message(otInstance *aInstance, otMessage *aMessage) void tcplp_sys_send_message(otInstance *aInstance, otMessage *aMessage, otMessageInfo *aMessageInfo) { - Instance & instance = AsCoreType(aInstance); - Message & message = AsCoreType(aMessage); + Instance &instance = AsCoreType(aInstance); + Message &message = AsCoreType(aMessage); MessageInfo &info = AsCoreType(aMessageInfo); LogDebg("Sending TCP segment: payload_size = %d", static_cast(message.GetLength())); @@ -981,15 +945,9 @@ void tcplp_sys_send_message(otInstance *aInstance, otMessage *aMessage, otMessag IgnoreError(instance.Get().SendDatagram(message, info, kProtoTcp)); } -uint32_t tcplp_sys_get_ticks(void) -{ - return TimerMilli::GetNow().GetValue(); -} +uint32_t tcplp_sys_get_ticks(void) { return TimerMilli::GetNow().GetValue(); } -uint32_t tcplp_sys_get_millis(void) -{ - return TimerMilli::GetNow().GetValue(); -} +uint32_t tcplp_sys_get_millis(void) { return TimerMilli::GetNow().GetValue(); } void tcplp_sys_set_timer(struct tcpcb *aTcb, uint8_t aTimerFlag, uint32_t aDelay) { @@ -1005,11 +963,11 @@ void tcplp_sys_stop_timer(struct tcpcb *aTcb, uint8_t aTimerFlag) struct tcpcb *tcplp_sys_accept_ready(struct tcpcb_listen *aTcbListen, struct in6_addr *aAddr, uint16_t aPort) { - Tcp::Listener & listener = Tcp::Listener::FromTcbListen(*aTcbListen); - Tcp & tcp = listener.Get(); - struct tcpcb * rv = (struct tcpcb *)-1; + Tcp::Listener &listener = Tcp::Listener::FromTcbListen(*aTcbListen); + Tcp &tcp = listener.Get(); + struct tcpcb *rv = (struct tcpcb *)-1; otSockAddr addr; - otTcpEndpoint * endpointPtr; + otTcpEndpoint *endpointPtr; otTcpIncomingConnectionAction action; VerifyOrExit(listener.mAcceptReadyCallback != nullptr); @@ -1051,13 +1009,13 @@ struct tcpcb *tcplp_sys_accept_ready(struct tcpcb_listen *aTcbListen, struct in6 } bool tcplp_sys_accepted_connection(struct tcpcb_listen *aTcbListen, - struct tcpcb * aAccepted, - struct in6_addr * aAddr, + struct tcpcb *aAccepted, + struct in6_addr *aAddr, uint16_t aPort) { Tcp::Listener &listener = Tcp::Listener::FromTcbListen(*aTcbListen); Tcp::Endpoint &endpoint = Tcp::Endpoint::FromTcb(*aAccepted); - Tcp & tcp = endpoint.Get(); + Tcp &tcp = endpoint.Get(); bool accepted = true; if (listener.mAcceptDoneCallback != nullptr) @@ -1125,7 +1083,7 @@ void tcplp_sys_log(const char *aFormat, ...) vsnprintf(buffer, sizeof(buffer), aFormat, args); va_end(args); - LogDebg(buffer); + LogDebg("%s", buffer); } void tcplp_sys_panic(const char *aFormat, ...) @@ -1141,9 +1099,9 @@ void tcplp_sys_panic(const char *aFormat, ...) OT_ASSERT(false); } -bool tcplp_sys_autobind(otInstance * aInstance, +bool tcplp_sys_autobind(otInstance *aInstance, const otSockAddr *aPeer, - otSockAddr * aToBind, + otSockAddr *aToBind, bool aBindAddress, bool aBindPort) { @@ -1160,15 +1118,9 @@ uint32_t tcplp_sys_generate_isn() return isn; } -uint16_t tcplp_sys_hostswap16(uint16_t aHostPort) -{ - return HostSwap16(aHostPort); -} +uint16_t tcplp_sys_hostswap16(uint16_t aHostPort) { return HostSwap16(aHostPort); } -uint32_t tcplp_sys_hostswap32(uint32_t aHostPort) -{ - return HostSwap32(aHostPort); -} +uint32_t tcplp_sys_hostswap32(uint32_t aHostPort) { return HostSwap32(aHostPort); } } #endif // OPENTHREAD_CONFIG_TCP_ENABLE diff --git a/src/core/net/tcp6.hpp b/src/core/net/tcp6.hpp index 501fd5ae1c0..ec407deaded 100644 --- a/src/core/net/tcp6.hpp +++ b/src/core/net/tcp6.hpp @@ -55,7 +55,16 @@ extern "C" { struct tcpcb; struct tcpcb_listen; struct tcplp_signals; + +/* + * The next two declarations intentionally change argument names from the + * original declarations in TCPlp, in order to comply with OpenThread's format. + */ + +// NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) void tcplp_sys_set_timer(struct tcpcb *aTcb, uint8_t aTimerFlag, uint32_t aDelay); + +// NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) void tcplp_sys_stop_timer(struct tcpcb *aTcb, uint8_t aTimerFlag); } @@ -391,9 +400,9 @@ class Tcp : public InstanceLocator, private NonCopyable size_t GetInFlightBytes(void) const; size_t GetBacklogBytes(void) const; - Address & GetLocalIp6Address(void); + Address &GetLocalIp6Address(void); const Address &GetLocalIp6Address(void) const; - Address & GetForeignIp6Address(void); + Address &GetForeignIp6Address(void); const Address &GetForeignIp6Address(void) const; bool Matches(const MessageInfo &aMessageInfo) const; }; @@ -520,7 +529,7 @@ class Tcp : public InstanceLocator, private NonCopyable bool IsClosed(void) const; private: - Address & GetLocalIp6Address(void); + Address &GetLocalIp6Address(void); const Address &GetLocalIp6Address(void) const; bool Matches(const MessageInfo &aMessageInfo) const; }; @@ -669,22 +678,23 @@ class Tcp : public InstanceLocator, private NonCopyable static constexpr uint8_t kReceiveAvailableCallbackFlag = (1 << 3); static constexpr uint8_t kDisconnectedCallbackFlag = (1 << 4); - void ProcessSignals(Endpoint & aEndpoint, - otLinkedBuffer * aPriorHead, + void ProcessSignals(Endpoint &aEndpoint, + otLinkedBuffer *aPriorHead, size_t aPriorBacklog, - struct tcplp_signals &aSignals); + struct tcplp_signals &aSignals) const; static Error BsdErrorToOtError(int aBsdError); bool CanBind(const SockAddr &aSockName); - static void HandleTimer(Timer &aTimer); - void ProcessTimers(void); + void HandleTimer(void); + + void ProcessCallbacks(void); - static void HandleTasklet(Tasklet &aTasklet); - void ProcessCallbacks(void); + using TcpTasklet = TaskletIn; + using TcpTimer = TimerMilliIn; - TimerMilli mTimer; - Tasklet mTasklet; + TcpTimer mTimer; + TcpTasklet mTasklet; LinkedList mEndpoints; LinkedList mListeners; diff --git a/src/core/net/tcp6_ext.cpp b/src/core/net/tcp6_ext.cpp new file mode 100644 index 00000000000..c7601e8d36d --- /dev/null +++ b/src/core/net/tcp6_ext.cpp @@ -0,0 +1,225 @@ +/* + * 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 TCP/IPv6 socket extensions. + */ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_TCP_ENABLE + +#include "tcp6_ext.hpp" + +#include "common/code_utils.hpp" +#include "common/error.hpp" +#include "common/locator_getters.hpp" +#include "common/log.hpp" + +namespace ot { +namespace Ip6 { + +RegisterLogModule("TcpExt"); + +void TcpCircularSendBuffer::Initialize(void *aDataBuffer, size_t aCapacity) +{ + mDataBuffer = static_cast(aDataBuffer); + mCapacity = aCapacity; + ForceDiscardAll(); +} + +Error TcpCircularSendBuffer::Write(Tcp::Endpoint &aEndpoint, + const void *aData, + size_t aLength, + size_t &aWritten, + uint32_t aFlags) +{ + Error error = kErrorNone; + size_t bytesFree = GetFreeSpace(); + size_t writeIndex; + uint32_t flags = 0; + size_t bytesUntilWrap; + + /* + * Handle the case where we don't have enough space to accommodate all of the + * provided data. + */ + aLength = Min(aLength, bytesFree); + VerifyOrExit(aLength != 0); + + /* + * This is a "simplifying" if statement the removes an edge case from the logic + * below. It guarantees that a write to an empty buffer will never wrap. + */ + if (mCapacityUsed == 0) + { + mStartIndex = 0; + } + + writeIndex = GetIndex(mStartIndex, mCapacityUsed); + + if ((aFlags & OT_TCP_CIRCULAR_SEND_BUFFER_WRITE_MORE_TO_COME) != 0 && aLength < bytesFree) + { + flags |= OT_TCP_SEND_MORE_TO_COME; + } + + bytesUntilWrap = mCapacity - writeIndex; + if (aLength <= bytesUntilWrap) + { + memcpy(&mDataBuffer[writeIndex], aData, aLength); + if (writeIndex == 0) + { + /* + * mCapacityUsed == 0 corresponds to the case where we're writing + * to an empty buffer. mCapacityUsed != 0 && writeIndex == 0 + * corresponds to the case where the buffer is not empty and this is + * writing the first bytes that wrap. + */ + uint8_t linkIndex; + if (mCapacityUsed == 0) + { + linkIndex = mFirstSendLinkIndex; + } + else + { + linkIndex = 1 - mFirstSendLinkIndex; + } + { + otLinkedBuffer &dataSendLink = mSendLinks[linkIndex]; + + dataSendLink.mNext = nullptr; + dataSendLink.mData = &mDataBuffer[writeIndex]; + dataSendLink.mLength = aLength; + + LogDebg("Appending link %u (points to index %u, length %u)", static_cast(linkIndex), + static_cast(writeIndex), static_cast(aLength)); + error = aEndpoint.SendByReference(dataSendLink, flags); + } + } + else + { + LogDebg("Extending tail link by length %u", static_cast(aLength)); + error = aEndpoint.SendByExtension(aLength, flags); + } + VerifyOrExit(error == kErrorNone, aLength = 0); + } + else + { + const uint8_t *dataIndexable = static_cast(aData); + size_t bytesWrapped = aLength - bytesUntilWrap; + + memcpy(&mDataBuffer[writeIndex], &dataIndexable[0], bytesUntilWrap); + memcpy(&mDataBuffer[0], &dataIndexable[bytesUntilWrap], bytesWrapped); + + /* + * Because of the "simplifying" if statement at the top, we don't + * have to worry about starting from an empty buffer in this case. + */ + LogDebg("Extending tail link by length %u (wrapping)", static_cast(bytesUntilWrap)); + error = aEndpoint.SendByExtension(bytesUntilWrap, flags | OT_TCP_SEND_MORE_TO_COME); + VerifyOrExit(error == kErrorNone, aLength = 0); + + { + otLinkedBuffer &wrappedDataSendLink = mSendLinks[1 - mFirstSendLinkIndex]; + + wrappedDataSendLink.mNext = nullptr; + wrappedDataSendLink.mData = &mDataBuffer[0]; + wrappedDataSendLink.mLength = bytesWrapped; + + LogDebg("Appending link %u (wrapping)", static_cast(1 - mFirstSendLinkIndex)); + error = aEndpoint.SendByReference(wrappedDataSendLink, flags); + VerifyOrExit(error == kErrorNone, aLength = bytesUntilWrap); + } + } + +exit: + mCapacityUsed += aLength; + aWritten = aLength; + return error; +} + +void TcpCircularSendBuffer::HandleForwardProgress(size_t aInSendBuffer) +{ + size_t bytesRemoved; + size_t bytesUntilWrap; + + OT_ASSERT(aInSendBuffer <= mCapacityUsed); + LogDebg("Forward progress: %u bytes in send buffer\n", static_cast(aInSendBuffer)); + bytesRemoved = mCapacityUsed - aInSendBuffer; + bytesUntilWrap = mCapacity - mStartIndex; + + if (bytesRemoved < bytesUntilWrap) + { + mStartIndex += bytesRemoved; + } + else + { + mStartIndex = bytesRemoved - bytesUntilWrap; + /* The otLinkedBuffer for the pre-wrap data is now empty. */ + LogDebg("Pre-wrap linked buffer now empty: switching first link index from %u to %u\n", + static_cast(mFirstSendLinkIndex), static_cast(1 - mFirstSendLinkIndex)); + mFirstSendLinkIndex = 1 - mFirstSendLinkIndex; + } + mCapacityUsed = aInSendBuffer; +} + +size_t TcpCircularSendBuffer::GetFreeSpace(void) const { return mCapacity - mCapacityUsed; } + +void TcpCircularSendBuffer::ForceDiscardAll(void) +{ + mStartIndex = 0; + mCapacityUsed = 0; + mFirstSendLinkIndex = 0; +} + +Error TcpCircularSendBuffer::Deinitialize(void) { return (mCapacityUsed != 0) ? kErrorBusy : kErrorNone; } + +size_t TcpCircularSendBuffer::GetIndex(size_t aStart, size_t aOffsetFromStart) const +{ + size_t bytesUntilWrap; + size_t index; + + OT_ASSERT(aStart < mCapacity); + bytesUntilWrap = mCapacity - aStart; + if (aOffsetFromStart < bytesUntilWrap) + { + index = aStart + aOffsetFromStart; + } + else + { + index = aOffsetFromStart - bytesUntilWrap; + } + + return index; +} + +} // namespace Ip6 +} // namespace ot + +#endif // OPENTHREAD_CONFIG_TCP_ENABLE diff --git a/src/core/net/tcp6_ext.hpp b/src/core/net/tcp6_ext.hpp new file mode 100644 index 00000000000..b40663aedb2 --- /dev/null +++ b/src/core/net/tcp6_ext.hpp @@ -0,0 +1,136 @@ +/* + * 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 TCP/IPv6 socket extensions. + */ + +#ifndef TCP6_EXT_HPP_ +#define TCP6_EXT_HPP_ + +#include "openthread-core-config.h" + +#include + +#include "net/tcp6.hpp" + +namespace ot { +namespace Ip6 { + +/** + * @addtogroup core-tcp-ext + * + * @brief + * This module includes definitions for TCP/IPv6 socket extensions. + * + * @{ + * + */ + +/** + * This class represents a TCP circular send buffer. + * + */ +class TcpCircularSendBuffer : public otTcpCircularSendBuffer +{ +public: + /** + * Initializes a TCP circular send buffer. + * + * @sa otTcpCircularSendBufferInitialize + * + * @param[in] aDataBuffer A pointer to memory to use to store data in the TCP circular send buffer. + * @param[in] aCapacity The capacity, in bytes, of the TCP circular send buffer, which must equal the size + * of the memory pointed to by @p aDataBuffer . + */ + void Initialize(void *aDataBuffer, size_t aCapacity); + + /** + * Sends out data on a TCP endpoint, using this TCP circular send buffer to manage buffering. + * + * @sa otTcpCircularSendBufferWrite, particularly for guidance on how @p aEndpoint must be chosen. + * + * @param[in] aEndpoint The TCP endpoint on which to send out data. + * @param[in] aData A pointer to data to copy into the TCP circular send buffer. + * @param[in] aLength The length of the data pointed to by @p aData to copy into the TCP circular send buffer. + * @param[out] aWritten Populated with the amount of data copied into the send buffer, which might be less than + * @p aLength if the send buffer reaches capacity. + * @param[in] aFlags Flags specifying options for this operation. + * + * @retval kErrorNone on success, or the error returned by the TCP endpoint on failure. + */ + Error Write(Tcp::Endpoint &aEndpoint, const void *aData, size_t aLength, size_t &aWritten, uint32_t aFlags); + + /** + * Performs circular-send-buffer-specific handling in the otTcpForwardProgress callback. + * + * @sa otTcpCircularSendBufferHandleForwardProgress + * + * @param[in] aInSendBuffer Value of @p aInSendBuffer passed to the otTcpForwardProgress() callback. + */ + void HandleForwardProgress(size_t aInSendBuffer); + + /** + * Returns the amount of free space in this TCP circular send buffer. + * + * @sa otTcpCircularSendBufferFreeSpace + * + * @return The amount of free space in the send buffer. + */ + size_t GetFreeSpace(void) const; + + /** + * Forcibly discards all data in this TCP circular send buffer. + * + * @sa otTcpCircularSendBufferForceDiscardAll + * + */ + void ForceDiscardAll(void); + + /** + * Deinitializes this TCP circular send buffer. + * + * @sa otTcpCircularSendBufferDeinitialize + * + * @retval kErrorNone Successfully deinitialized this TCP circular send buffer. + * @retval kErrorFailed Failed to deinitialize the TCP circular send buffer. + */ + Error Deinitialize(void); + +private: + size_t GetIndex(size_t aStart, size_t aOffsetFromStart) const; +}; + +} // namespace Ip6 + +DefineCoreType(otTcpCircularSendBuffer, Ip6::TcpCircularSendBuffer); + +} // namespace ot + +#endif // TCP6_HPP_ diff --git a/src/core/net/udp6.cpp b/src/core/net/udp6.cpp index 1dea10e0557..3ddda61cd14 100644 --- a/src/core/net/udp6.cpp +++ b/src/core/net/udp6.cpp @@ -77,45 +77,34 @@ Udp::Socket::Socket(Instance &aInstance) Clear(); } +Message *Udp::Socket::NewMessage(void) { return NewMessage(0); } + +Message *Udp::Socket::NewMessage(uint16_t aReserved) { return NewMessage(aReserved, Message::Settings::GetDefault()); } + Message *Udp::Socket::NewMessage(uint16_t aReserved, const Message::Settings &aSettings) { return Get().NewMessage(aReserved, aSettings); } -Error Udp::Socket::Open(otUdpReceive aHandler, void *aContext) -{ - return Get().Open(*this, aHandler, aContext); -} +Error Udp::Socket::Open(otUdpReceive aHandler, void *aContext) { return Get().Open(*this, aHandler, aContext); } -bool Udp::Socket::IsOpen(void) const -{ - return Get().IsOpen(*this); -} +bool Udp::Socket::IsOpen(void) const { return Get().IsOpen(*this); } -Error Udp::Socket::Bind(const SockAddr &aSockAddr, otNetifIdentifier aNetifIdentifier) +Error Udp::Socket::Bind(const SockAddr &aSockAddr, NetifIdentifier aNetifIdentifier) { return Get().Bind(*this, aSockAddr, aNetifIdentifier); } -Error Udp::Socket::Bind(uint16_t aPort, otNetifIdentifier aNetifIdentifier) +Error Udp::Socket::Bind(uint16_t aPort, NetifIdentifier aNetifIdentifier) { return Bind(SockAddr(aPort), aNetifIdentifier); } -Error Udp::Socket::Connect(const SockAddr &aSockAddr) -{ - return Get().Connect(*this, aSockAddr); -} +Error Udp::Socket::Connect(const SockAddr &aSockAddr) { return Get().Connect(*this, aSockAddr); } -Error Udp::Socket::Connect(uint16_t aPort) -{ - return Connect(SockAddr(aPort)); -} +Error Udp::Socket::Connect(uint16_t aPort) { return Connect(SockAddr(aPort)); } -Error Udp::Socket::Close(void) -{ - return Get().Close(*this); -} +Error Udp::Socket::Close(void) { return Get().Close(*this); } Error Udp::Socket::SendTo(Message &aMessage, const MessageInfo &aMessageInfo) { @@ -123,7 +112,7 @@ Error Udp::Socket::SendTo(Message &aMessage, const MessageInfo &aMessageInfo) } #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE -Error Udp::Socket::JoinNetifMulticastGroup(otNetifIdentifier aNetifIdentifier, const Address &aAddress) +Error Udp::Socket::JoinNetifMulticastGroup(NetifIdentifier aNetifIdentifier, const Address &aAddress) { OT_UNUSED_VARIABLE(aNetifIdentifier); OT_UNUSED_VARIABLE(aAddress); @@ -133,14 +122,14 @@ Error Udp::Socket::JoinNetifMulticastGroup(otNetifIdentifier aNetifIdentifier, c VerifyOrExit(aAddress.IsMulticast(), error = kErrorInvalidArgs); #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE - error = otPlatUdpJoinMulticastGroup(this, aNetifIdentifier, &aAddress); + error = otPlatUdpJoinMulticastGroup(this, MapEnum(aNetifIdentifier), &aAddress); #endif exit: return error; } -Error Udp::Socket::LeaveNetifMulticastGroup(otNetifIdentifier aNetifIdentifier, const Address &aAddress) +Error Udp::Socket::LeaveNetifMulticastGroup(NetifIdentifier aNetifIdentifier, const Address &aAddress) { OT_UNUSED_VARIABLE(aNetifIdentifier); OT_UNUSED_VARIABLE(aAddress); @@ -150,7 +139,7 @@ Error Udp::Socket::LeaveNetifMulticastGroup(otNetifIdentifier aNetifIdentifier, VerifyOrExit(aAddress.IsMulticast(), error = kErrorInvalidArgs); #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE - error = otPlatUdpLeaveMulticastGroup(this, aNetifIdentifier, &aAddress); + error = otPlatUdpLeaveMulticastGroup(this, MapEnum(aNetifIdentifier), &aAddress); #endif exit: @@ -164,17 +153,10 @@ Udp::Udp(Instance &aInstance) #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE , mPrevBackboneSockets(nullptr) #endif -#if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE - , mUdpForwarderContext(nullptr) - , mUdpForwarder(nullptr) -#endif { } -Error Udp::AddReceiver(Receiver &aReceiver) -{ - return mReceivers.Add(aReceiver); -} +Error Udp::AddReceiver(Receiver &aReceiver) { return mReceivers.Add(aReceiver); } Error Udp::RemoveReceiver(Receiver &aReceiver) { @@ -209,18 +191,18 @@ Error Udp::Open(SocketHandle &aSocket, otUdpReceive aHandler, void *aContext) return error; } -Error Udp::Bind(SocketHandle &aSocket, const SockAddr &aSockAddr, otNetifIdentifier aNetifIdentifier) +Error Udp::Bind(SocketHandle &aSocket, const SockAddr &aSockAddr, NetifIdentifier aNetifIdentifier) { OT_UNUSED_VARIABLE(aNetifIdentifier); Error error = kErrorNone; #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE - SuccessOrExit(error = otPlatUdpBindToNetif(&aSocket, aNetifIdentifier)); + SuccessOrExit(error = otPlatUdpBindToNetif(&aSocket, MapEnum(aNetifIdentifier))); #endif #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE - if (aNetifIdentifier == OT_NETIF_BACKBONE) + if (aNetifIdentifier == kNetifBackbone) { SetBackboneSocket(aSocket); } @@ -297,7 +279,7 @@ Error Udp::Connect(SocketHandle &aSocket, const SockAddr &aSockAddr) if (!aSocket.IsBound()) { - SuccessOrExit(error = Bind(aSocket, aSocket.GetSockName(), OT_NETIF_THREAD)); + SuccessOrExit(error = Bind(aSocket, aSocket.GetSockName(), kNetifThread)); } #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE @@ -360,7 +342,7 @@ Error Udp::SendTo(SocketHandle &aSocket, Message &aMessage, const MessageInfo &a if (!aSocket.IsBound()) { - SuccessOrExit(error = Bind(aSocket, aSocket.GetSockName(), OT_NETIF_THREAD)); + SuccessOrExit(error = Bind(aSocket, aSocket.GetSockName(), kNetifThread)); } messageInfoLocal.SetSockPort(aSocket.GetSockName().mPort); @@ -436,6 +418,10 @@ uint16_t Udp::GetEphemeralPort(void) return mEphemeralPort; } +Message *Udp::NewMessage(void) { return NewMessage(0); } + +Message *Udp::NewMessage(uint16_t aReserved) { return NewMessage(aReserved, Message::Settings::GetDefault()); } + Message *Udp::NewMessage(uint16_t aReserved, const Message::Settings &aSettings) { return Get().NewMessage(sizeof(Header) + aReserved, aSettings); @@ -448,9 +434,8 @@ Error Udp::SendDatagram(Message &aMessage, MessageInfo &aMessageInfo, uint8_t aI #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE if (aMessageInfo.IsHostInterface()) { - VerifyOrExit(mUdpForwarder != nullptr, error = kErrorNoRoute); - mUdpForwarder(&aMessage, aMessageInfo.mPeerPort, &aMessageInfo.GetPeerAddr(), aMessageInfo.mSockPort, - mUdpForwarderContext); + VerifyOrExit(mUdpForwarder.IsSet(), error = kErrorNoRoute); + mUdpForwarder.Invoke(&aMessage, aMessageInfo.mPeerPort, &aMessageInfo.GetPeerAddr(), aMessageInfo.mSockPort); // message is consumed by the callback } else diff --git a/src/core/net/udp6.hpp b/src/core/net/udp6.hpp index 6471c4bdfb5..6648f00728a 100644 --- a/src/core/net/udp6.hpp +++ b/src/core/net/udp6.hpp @@ -40,6 +40,7 @@ #include #include "common/as_core_type.hpp" +#include "common/callback.hpp" #include "common/clearable.hpp" #include "common/linked_list.hpp" #include "common/locator.hpp" @@ -65,6 +66,17 @@ class Udp; #error "OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE and OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE must not both be set." #endif +/** + * This enumeration defines the network interface identifiers. + * + */ +enum NetifIdentifier : uint8_t +{ + kNetifUnspecified = OT_NETIF_UNSPECIFIED, ///< Unspecified network interface. + kNetifThread = OT_NETIF_THREAD, ///< The Thread interface. + kNetifBackbone = OT_NETIF_BACKBONE, ///< The Backbone interface. +}; + /** * This class implements core UDP message handling. * @@ -149,6 +161,24 @@ class Udp : public InstanceLocator, private NonCopyable */ explicit Socket(Instance &aInstance); + /** + * This method returns a new UDP message with default settings (link security enabled and `kPriorityNormal`) + * + * @returns A pointer to the message or `nullptr` if no buffers are available. + * + */ + Message *NewMessage(void); + + /** + * This method returns a new UDP message with default settings (link security enabled and `kPriorityNormal`) + * + * @param[in] aReserved The number of header bytes to reserve after the UDP header. + * + * @returns A pointer to the message or `nullptr` if no buffers are available. + * + */ + Message *NewMessage(uint16_t aReserved); + /** * This method returns a new UDP message with sufficient header space reserved. * @@ -158,7 +188,7 @@ class Udp : public InstanceLocator, private NonCopyable * @returns A pointer to the message or `nullptr` if no buffers are available. * */ - Message *NewMessage(uint16_t aReserved, const Message::Settings &aSettings = Message::Settings::GetDefault()); + Message *NewMessage(uint16_t aReserved, const Message::Settings &aSettings); /** * This method opens the UDP socket. @@ -191,7 +221,7 @@ class Udp : public InstanceLocator, private NonCopyable * @retval kErrorFailed Failed to bind UDP Socket. * */ - Error Bind(const SockAddr &aSockAddr, otNetifIdentifier aNetifIdentifier = OT_NETIF_THREAD); + Error Bind(const SockAddr &aSockAddr, NetifIdentifier aNetifIdentifier = kNetifThread); /** * This method binds the UDP socket. @@ -203,7 +233,7 @@ class Udp : public InstanceLocator, private NonCopyable * @retval kErrorFailed Failed to bind UDP Socket. * */ - Error Bind(uint16_t aPort, otNetifIdentifier aNetifIdentifier = OT_NETIF_THREAD); + Error Bind(uint16_t aPort, NetifIdentifier aNetifIdentifier = kNetifThread); /** * This method binds the UDP socket. @@ -269,7 +299,7 @@ class Udp : public InstanceLocator, private NonCopyable #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE /** - * This method configures the UDP socket to join a mutlicast group on a Host network interface. + * This method configures the UDP socket to join a multicast group on a Host network interface. * * @param[in] aNetifIdentifier The network interface identifier. * @param[in] aAddress The multicast group address. @@ -278,7 +308,7 @@ class Udp : public InstanceLocator, private NonCopyable * @retval kErrorFailed Failed to join the multicast group. * */ - Error JoinNetifMulticastGroup(otNetifIdentifier aNetifIdentifier, const Address &aAddress); + Error JoinNetifMulticastGroup(NetifIdentifier aNetifIdentifier, const Address &aAddress); /** * This method configures the UDP socket to leave a multicast group on a Host network interface. @@ -290,7 +320,7 @@ class Udp : public InstanceLocator, private NonCopyable * @retval kErrorFailed Failed to leave the multicast group. * */ - Error LeaveNetifMulticastGroup(otNetifIdentifier aNetifIdentifier, const Address &aAddress); + Error LeaveNetifMulticastGroup(NetifIdentifier aNetifIdentifier, const Address &aAddress); #endif }; @@ -474,7 +504,7 @@ class Udp : public InstanceLocator, private NonCopyable * @retval kErrorFailed Failed to bind UDP Socket. * */ - Error Bind(SocketHandle &aSocket, const SockAddr &aSockAddr, otNetifIdentifier aNetifIdentifier); + Error Bind(SocketHandle &aSocket, const SockAddr &aSockAddr, NetifIdentifier aNetifIdentifier); /** * This method connects a UDP socket. @@ -521,6 +551,24 @@ class Udp : public InstanceLocator, private NonCopyable */ uint16_t GetEphemeralPort(void); + /** + * This method returns a new UDP message with default settings (link security enabled and `kPriorityNormal`) + * + * @returns A pointer to the message or `nullptr` if no buffers are available. + * + */ + Message *NewMessage(void); + + /** + * This method returns a new UDP message with default settings (link security enabled and `kPriorityNormal`) + * + * @param[in] aReserved The number of header bytes to reserve after the UDP header. + * + * @returns A pointer to the message or `nullptr` if no buffers are available. + * + */ + Message *NewMessage(uint16_t aReserved); + /** * This method returns a new UDP message with sufficient header space reserved. * @@ -530,7 +578,7 @@ class Udp : public InstanceLocator, private NonCopyable * @returns A pointer to the message or `nullptr` if no buffers are available. * */ - Message *NewMessage(uint16_t aReserved, const Message::Settings &aSettings = Message::Settings::GetDefault()); + Message *NewMessage(uint16_t aReserved, const Message::Settings &aSettings); /** * This method sends an IPv6 datagram. @@ -582,11 +630,7 @@ class Udp : public InstanceLocator, private NonCopyable * @param[in] aContext A pointer to arbitrary context information. * */ - void SetUdpForwarder(otUdpForwarder aForwarder, void *aContext) - { - mUdpForwarder = aForwarder; - mUdpForwarderContext = aContext; - } + void SetUdpForwarder(otUdpForwarder aForwarder, void *aContext) { mUdpForwarder.Set(aForwarder, aContext); } #endif /** @@ -641,8 +685,7 @@ class Udp : public InstanceLocator, private NonCopyable SocketHandle *mPrevBackboneSockets; #endif #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE - void * mUdpForwarderContext; - otUdpForwarder mUdpForwarder; + Callback mUdpForwarder; #endif }; @@ -655,6 +698,7 @@ class Udp : public InstanceLocator, private NonCopyable DefineCoreType(otUdpSocket, Ip6::Udp::SocketHandle); DefineCoreType(otUdpReceiver, Ip6::Udp::Receiver); +DefineMapEnum(otNetifIdentifier, Ip6::NetifIdentifier); } // namespace ot diff --git a/src/core/openthread-core-config.h b/src/core/openthread-core-config.h index da7e18565ba..2b4127dccbf 100644 --- a/src/core/openthread-core-config.h +++ b/src/core/openthread-core-config.h @@ -41,6 +41,7 @@ #define OT_THREAD_VERSION_1_1 2 #define OT_THREAD_VERSION_1_2 3 #define OT_THREAD_VERSION_1_3 4 +#define OT_THREAD_VERSION_1_3_1 5 #define OPENTHREAD_CORE_CONFIG_H_IN @@ -58,7 +59,9 @@ #include "config/announce_sender.h" #include "config/backbone_router.h" +#include "config/border_agent.h" #include "config/border_router.h" +#include "config/border_routing.h" #include "config/channel_manager.h" #include "config/channel_monitor.h" #include "config/child_supervision.h" @@ -80,12 +83,16 @@ #include "config/link_raw.h" #include "config/logging.h" #include "config/mac.h" +#include "config/mesh_diag.h" #include "config/misc.h" #include "config/mle.h" +#include "config/nat64.h" #include "config/netdata_publisher.h" +#include "config/network_diagnostic.h" #include "config/parent_search.h" #include "config/ping_sender.h" #include "config/platform.h" +#include "config/power_calibration.h" #include "config/radio_link.h" #include "config/sntp_client.h" #include "config/srp_client.h" diff --git a/src/core/radio.cmake b/src/core/radio.cmake index f70a8dd36a7..95ae9153320 100644 --- a/src/core/radio.cmake +++ b/src/core/radio.cmake @@ -54,5 +54,6 @@ endif() target_link_libraries(openthread-radio PRIVATE ${OT_MBEDTLS_RCP} + ot-config-radio ot-config ) diff --git a/src/core/radio/radio.hpp b/src/core/radio/radio.hpp index aa7233e18fc..bbe8544d5cb 100644 --- a/src/core/radio/radio.hpp +++ b/src/core/radio/radio.hpp @@ -45,7 +45,8 @@ namespace ot { -static constexpr uint32_t kUsPerTenSymbols = OT_US_PER_TEN_SYMBOLS; ///< The microseconds per 10 symbols. +static constexpr uint32_t kUsPerTenSymbols = OT_US_PER_TEN_SYMBOLS; ///< Time for 10 symbols in units of microseconds +static constexpr uint32_t kRadioHeaderShrDuration = 160; ///< Duration of SHR in us #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE /** @@ -56,12 +57,6 @@ static constexpr uint64_t kMinCslPeriod = OPENTHREAD_CONFIG_MAC_CSL_MIN_PERIOD static constexpr uint64_t kMaxCslTimeout = OPENTHREAD_CONFIG_MAC_CSL_MAX_TIMEOUT; #endif -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -static constexpr uint8_t kCslWorstCrystalPpm = 255; ///< Worst possible crystal accuracy, in units of ± ppm. -static constexpr uint8_t kCslWorstUncertainty = 255; ///< Worst possible scheduling uncertainty, in units of 10 us. -static constexpr uint8_t kUsPerUncertUnit = 10; ///< Number of microseconds by uncertainty unit. -#endif - /** * @addtogroup core-radio * @@ -109,6 +104,10 @@ class Radio : public InstanceLocator, private NonCopyable static constexpr uint32_t kSupportedChannelPages = (1 << OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE); #endif + static constexpr int8_t kInvalidRssi = OT_RADIO_RSSI_INVALID; ///< Invalid RSSI value. + + static constexpr int8_t kDefaultReceiveSensitivity = -110; ///< Default receive sensitivity (in dBm). + static_assert((OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT || OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT || OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT), "OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT " @@ -242,7 +241,7 @@ class Radio : public InstanceLocator, private NonCopyable * @returns The radio receive sensitivity value in dBm. * */ - int8_t GetReceiveSensitivity(void); + int8_t GetReceiveSensitivity(void) const; #if OPENTHREAD_RADIO /** @@ -303,6 +302,18 @@ class Radio : public InstanceLocator, private NonCopyable otPlatRadioSetMacFrameCounter(GetInstancePtr(), aMacFrameCounter); } + /** + * This method sets the current MAC Frame Counter value only if the new given value is larger than the current + * value. + * + * @param[in] aMacFrameCounter The MAC Frame Counter value. + * + */ + void SetMacFrameCounterIfLarger(uint32_t aMacFrameCounter) + { + otPlatRadioSetMacFrameCounterIfLarger(GetInstancePtr(), aMacFrameCounter); + } + /** * This method gets the radio's transmit power in dBm. * @@ -473,7 +484,7 @@ class Radio : public InstanceLocator, private NonCopyable uint8_t GetCslAccuracy(void); /** - * Get the fixed uncertainty of the Device for scheduling CSL Transmissions in units of 10 microseconds. + * Get the fixed uncertainty of the Device for scheduling CSL operations in units of 10 microseconds. * * @returns The CSL Uncertainty in units of 10 us. * @@ -642,7 +653,7 @@ class Radio : public InstanceLocator, private NonCopyable */ Error ConfigureEnhAckProbing(otLinkMetrics aLinkMetrics, const Mac::ShortAddress &aShortAddress, - const Mac::ExtAddress & aExtAddress) + const Mac::ExtAddress &aExtAddress) { return otPlatRadioConfigureEnhAckProbing(GetInstancePtr(), aLinkMetrics, aShortAddress, &aExtAddress); } @@ -662,7 +673,7 @@ class Radio : public InstanceLocator, private NonCopyable } private: - otInstance *GetInstancePtr(void) { return reinterpret_cast(&InstanceLocator::GetInstance()); } + otInstance *GetInstancePtr(void) const { return reinterpret_cast(&InstanceLocator::GetInstance()); } Callbacks mCallbacks; }; @@ -670,25 +681,16 @@ class Radio : public InstanceLocator, private NonCopyable //--------------------------------------------------------------------------------------------------------------------- // Radio APIs that are always mapped to the same `otPlatRadio` function (independent of the link type) -inline const char *Radio::GetVersionString(void) -{ - return otPlatRadioGetVersionString(GetInstancePtr()); -} +inline const char *Radio::GetVersionString(void) { return otPlatRadioGetVersionString(GetInstancePtr()); } inline void Radio::GetIeeeEui64(Mac::ExtAddress &aIeeeEui64) { otPlatRadioGetIeeeEui64(GetInstancePtr(), aIeeeEui64.m8); } -inline uint32_t Radio::GetSupportedChannelMask(void) -{ - return otPlatRadioGetSupportedChannelMask(GetInstancePtr()); -} +inline uint32_t Radio::GetSupportedChannelMask(void) { return otPlatRadioGetSupportedChannelMask(GetInstancePtr()); } -inline uint32_t Radio::GetPreferredChannelMask(void) -{ - return otPlatRadioGetPreferredChannelMask(GetInstancePtr()); -} +inline uint32_t Radio::GetPreferredChannelMask(void) { return otPlatRadioGetPreferredChannelMask(GetInstancePtr()); } //--------------------------------------------------------------------------------------------------------------------- // If IEEE 802.15.4 is among supported radio links, provide inline @@ -696,20 +698,11 @@ inline uint32_t Radio::GetPreferredChannelMask(void) #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE -inline otRadioCaps Radio::GetCaps(void) -{ - return otPlatRadioGetCaps(GetInstancePtr()); -} +inline otRadioCaps Radio::GetCaps(void) { return otPlatRadioGetCaps(GetInstancePtr()); } -inline int8_t Radio::GetReceiveSensitivity(void) -{ - return otPlatRadioGetReceiveSensitivity(GetInstancePtr()); -} +inline int8_t Radio::GetReceiveSensitivity(void) const { return otPlatRadioGetReceiveSensitivity(GetInstancePtr()); } -inline void Radio::SetPanId(Mac::PanId aPanId) -{ - otPlatRadioSetPanId(GetInstancePtr(), aPanId); -} +inline void Radio::SetPanId(Mac::PanId aPanId) { otPlatRadioSetPanId(GetInstancePtr(), aPanId); } inline void Radio::SetMacKey(uint8_t aKeyIdMode, uint8_t aKeyId, @@ -728,15 +721,9 @@ inline void Radio::SetMacKey(uint8_t aKeyIdMode, otPlatRadioSetMacKey(GetInstancePtr(), aKeyIdMode, aKeyId, &aPrevKey, &aCurrKey, &aNextKey, aKeyType); } -inline Error Radio::GetTransmitPower(int8_t &aPower) -{ - return otPlatRadioGetTransmitPower(GetInstancePtr(), &aPower); -} +inline Error Radio::GetTransmitPower(int8_t &aPower) { return otPlatRadioGetTransmitPower(GetInstancePtr(), &aPower); } -inline Error Radio::SetTransmitPower(int8_t aPower) -{ - return otPlatRadioSetTransmitPower(GetInstancePtr(), aPower); -} +inline Error Radio::SetTransmitPower(int8_t aPower) { return otPlatRadioSetTransmitPower(GetInstancePtr(), aPower); } inline Error Radio::GetCcaEnergyDetectThreshold(int8_t &aThreshold) { @@ -748,45 +735,21 @@ inline Error Radio::SetCcaEnergyDetectThreshold(int8_t aThreshold) return otPlatRadioSetCcaEnergyDetectThreshold(GetInstancePtr(), aThreshold); } -inline bool Radio::GetPromiscuous(void) -{ - return otPlatRadioGetPromiscuous(GetInstancePtr()); -} +inline bool Radio::GetPromiscuous(void) { return otPlatRadioGetPromiscuous(GetInstancePtr()); } -inline void Radio::SetPromiscuous(bool aEnable) -{ - otPlatRadioSetPromiscuous(GetInstancePtr(), aEnable); -} +inline void Radio::SetPromiscuous(bool aEnable) { otPlatRadioSetPromiscuous(GetInstancePtr(), aEnable); } -inline otRadioState Radio::GetState(void) -{ - return otPlatRadioGetState(GetInstancePtr()); -} +inline otRadioState Radio::GetState(void) { return otPlatRadioGetState(GetInstancePtr()); } -inline Error Radio::Enable(void) -{ - return otPlatRadioEnable(GetInstancePtr()); -} +inline Error Radio::Enable(void) { return otPlatRadioEnable(GetInstancePtr()); } -inline Error Radio::Disable(void) -{ - return otPlatRadioDisable(GetInstancePtr()); -} +inline Error Radio::Disable(void) { return otPlatRadioDisable(GetInstancePtr()); } -inline bool Radio::IsEnabled(void) -{ - return otPlatRadioIsEnabled(GetInstancePtr()); -} +inline bool Radio::IsEnabled(void) { return otPlatRadioIsEnabled(GetInstancePtr()); } -inline Error Radio::Sleep(void) -{ - return otPlatRadioSleep(GetInstancePtr()); -} +inline Error Radio::Sleep(void) { return otPlatRadioSleep(GetInstancePtr()); } -inline Error Radio::Receive(uint8_t aChannel) -{ - return otPlatRadioReceive(GetInstancePtr(), aChannel); -} +inline Error Radio::Receive(uint8_t aChannel) { return otPlatRadioReceive(GetInstancePtr(), aChannel); } #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE inline void Radio::UpdateCslSampleTime(uint32_t aCslSampleTime) @@ -806,17 +769,11 @@ inline Error Radio::EnableCsl(uint32_t aCslPeriod, otShortAddress aShortAddr, co #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE -inline uint8_t Radio::GetCslAccuracy(void) -{ - return otPlatRadioGetCslAccuracy(GetInstancePtr()); -} +inline uint8_t Radio::GetCslAccuracy(void) { return otPlatRadioGetCslAccuracy(GetInstancePtr()); } #endif #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE -inline uint8_t Radio::GetCslUncertainty(void) -{ - return otPlatRadioGetCslUncertainty(GetInstancePtr()); -} +inline uint8_t Radio::GetCslUncertainty(void) { return otPlatRadioGetCslUncertainty(GetInstancePtr()); } #endif inline Mac::TxFrame &Radio::GetTransmitBuffer(void) @@ -824,20 +781,14 @@ inline Mac::TxFrame &Radio::GetTransmitBuffer(void) return *static_cast(otPlatRadioGetTransmitBuffer(GetInstancePtr())); } -inline int8_t Radio::GetRssi(void) -{ - return otPlatRadioGetRssi(GetInstancePtr()); -} +inline int8_t Radio::GetRssi(void) { return otPlatRadioGetRssi(GetInstancePtr()); } inline Error Radio::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration) { return otPlatRadioEnergyScan(GetInstancePtr(), aScanChannel, aScanDuration); } -inline void Radio::EnableSrcMatch(bool aEnable) -{ - otPlatRadioEnableSrcMatch(GetInstancePtr(), aEnable); -} +inline void Radio::EnableSrcMatch(bool aEnable) { otPlatRadioEnableSrcMatch(GetInstancePtr(), aEnable); } inline Error Radio::AddSrcMatchShortEntry(Mac::ShortAddress aShortAddress) { @@ -859,15 +810,9 @@ inline Error Radio::ClearSrcMatchExtEntry(const Mac::ExtAddress &aExtAddress) return otPlatRadioClearSrcMatchExtEntry(GetInstancePtr(), &aExtAddress); } -inline void Radio::ClearSrcMatchShortEntries(void) -{ - otPlatRadioClearSrcMatchShortEntries(GetInstancePtr()); -} +inline void Radio::ClearSrcMatchShortEntries(void) { otPlatRadioClearSrcMatchShortEntries(GetInstancePtr()); } -inline void Radio::ClearSrcMatchExtEntries(void) -{ - otPlatRadioClearSrcMatchExtEntries(GetInstancePtr()); -} +inline void Radio::ClearSrcMatchExtEntries(void) { otPlatRadioClearSrcMatchExtEntries(GetInstancePtr()); } #else //---------------------------------------------------------------------------------------------------------------- @@ -876,22 +821,13 @@ inline otRadioCaps Radio::GetCaps(void) return OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_CSMA_BACKOFF | OT_RADIO_CAPS_TRANSMIT_RETRIES; } -inline int8_t Radio::GetReceiveSensitivity(void) -{ - return -110; -} +inline int8_t Radio::GetReceiveSensitivity(void) const { return kDefaultReceiveSensitivity; } -inline void Radio::SetPanId(Mac::PanId) -{ -} +inline void Radio::SetPanId(Mac::PanId) {} -inline void Radio::SetExtendedAddress(const Mac::ExtAddress &) -{ -} +inline void Radio::SetExtendedAddress(const Mac::ExtAddress &) {} -inline void Radio::SetShortAddress(Mac::ShortAddress) -{ -} +inline void Radio::SetShortAddress(Mac::ShortAddress) {} inline void Radio::SetMacKey(uint8_t, uint8_t, @@ -901,74 +837,34 @@ inline void Radio::SetMacKey(uint8_t, { } -inline Error Radio::GetTransmitPower(int8_t &) -{ - return kErrorNotImplemented; -} +inline Error Radio::GetTransmitPower(int8_t &) { return kErrorNotImplemented; } -inline Error Radio::SetTransmitPower(int8_t) -{ - return kErrorNotImplemented; -} +inline Error Radio::SetTransmitPower(int8_t) { return kErrorNotImplemented; } -inline Error Radio::GetCcaEnergyDetectThreshold(int8_t &) -{ - return kErrorNotImplemented; -} +inline Error Radio::GetCcaEnergyDetectThreshold(int8_t &) { return kErrorNotImplemented; } -inline Error Radio::SetCcaEnergyDetectThreshold(int8_t) -{ - return kErrorNotImplemented; -} +inline Error Radio::SetCcaEnergyDetectThreshold(int8_t) { return kErrorNotImplemented; } -inline bool Radio::GetPromiscuous(void) -{ - return false; -} +inline bool Radio::GetPromiscuous(void) { return false; } -inline void Radio::SetPromiscuous(bool) -{ -} +inline void Radio::SetPromiscuous(bool) {} -inline otRadioState Radio::GetState(void) -{ - return OT_RADIO_STATE_DISABLED; -} +inline otRadioState Radio::GetState(void) { return OT_RADIO_STATE_DISABLED; } -inline Error Radio::Enable(void) -{ - return kErrorNone; -} +inline Error Radio::Enable(void) { return kErrorNone; } -inline Error Radio::Disable(void) -{ - return kErrorInvalidState; -} +inline Error Radio::Disable(void) { return kErrorInvalidState; } -inline bool Radio::IsEnabled(void) -{ - return true; -} +inline bool Radio::IsEnabled(void) { return true; } -inline Error Radio::Sleep(void) -{ - return kErrorNone; -} +inline Error Radio::Sleep(void) { return kErrorNone; } -inline Error Radio::Receive(uint8_t) -{ - return kErrorNone; -} +inline Error Radio::Receive(uint8_t) { return kErrorNone; } #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -inline void Radio::UpdateCslSampleTime(uint32_t) -{ -} +inline void Radio::UpdateCslSampleTime(uint32_t) {} -inline Error Radio::ReceiveAt(uint8_t, uint32_t, uint32_t) -{ - return kErrorNone; -} +inline Error Radio::ReceiveAt(uint8_t, uint32_t, uint32_t) { return kErrorNone; } inline Error Radio::EnableCsl(uint32_t, otShortAddress aShortAddr, const otExtAddress *) { @@ -977,15 +873,9 @@ inline Error Radio::EnableCsl(uint32_t, otShortAddress aShortAddr, const otExtAd #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE -inline uint8_t Radio::GetCslAccuracy(void) -{ - return UINT8_MAX; -} +inline uint8_t Radio::GetCslAccuracy(void) { return UINT8_MAX; } -inline uint8_t Radio::GetCslUncertainty(void) -{ - return UINT8_MAX; -} +inline uint8_t Radio::GetCslUncertainty(void) { return UINT8_MAX; } #endif inline Mac::TxFrame &Radio::GetTransmitBuffer(void) @@ -993,52 +883,25 @@ inline Mac::TxFrame &Radio::GetTransmitBuffer(void) return *static_cast(otPlatRadioGetTransmitBuffer(GetInstancePtr())); } -inline Error Radio::Transmit(Mac::TxFrame &) -{ - return kErrorAbort; -} +inline Error Radio::Transmit(Mac::TxFrame &) { return kErrorAbort; } -inline int8_t Radio::GetRssi(void) -{ - return OT_RADIO_RSSI_INVALID; -} +inline int8_t Radio::GetRssi(void) { return kInvalidRssi; } -inline Error Radio::EnergyScan(uint8_t, uint16_t) -{ - return kErrorNotImplemented; -} +inline Error Radio::EnergyScan(uint8_t, uint16_t) { return kErrorNotImplemented; } -inline void Radio::EnableSrcMatch(bool) -{ -} +inline void Radio::EnableSrcMatch(bool) {} -inline Error Radio::AddSrcMatchShortEntry(Mac::ShortAddress) -{ - return kErrorNone; -} +inline Error Radio::AddSrcMatchShortEntry(Mac::ShortAddress) { return kErrorNone; } -inline Error Radio::AddSrcMatchExtEntry(const Mac::ExtAddress &) -{ - return kErrorNone; -} +inline Error Radio::AddSrcMatchExtEntry(const Mac::ExtAddress &) { return kErrorNone; } -inline Error Radio::ClearSrcMatchShortEntry(Mac::ShortAddress) -{ - return kErrorNone; -} +inline Error Radio::ClearSrcMatchShortEntry(Mac::ShortAddress) { return kErrorNone; } -inline Error Radio::ClearSrcMatchExtEntry(const Mac::ExtAddress &) -{ - return kErrorNone; -} +inline Error Radio::ClearSrcMatchExtEntry(const Mac::ExtAddress &) { return kErrorNone; } -inline void Radio::ClearSrcMatchShortEntries(void) -{ -} +inline void Radio::ClearSrcMatchShortEntries(void) {} -inline void Radio::ClearSrcMatchExtEntries(void) -{ -} +inline void Radio::ClearSrcMatchExtEntries(void) {} #endif // #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE diff --git a/src/core/radio/radio_callbacks.cpp b/src/core/radio/radio_callbacks.cpp index 041d3f47f34..d23dddc2448 100644 --- a/src/core/radio/radio_callbacks.cpp +++ b/src/core/radio/radio_callbacks.cpp @@ -43,20 +43,14 @@ void Radio::Callbacks::HandleReceiveDone(Mac::RxFrame *aFrame, Error aError) Get().HandleReceiveDone(aFrame, aError); } -void Radio::Callbacks::HandleTransmitStarted(Mac::TxFrame &aFrame) -{ - Get().HandleTransmitStarted(aFrame); -} +void Radio::Callbacks::HandleTransmitStarted(Mac::TxFrame &aFrame) { Get().HandleTransmitStarted(aFrame); } void Radio::Callbacks::HandleTransmitDone(Mac::TxFrame &aFrame, Mac::RxFrame *aAckFrame, Error aError) { Get().HandleTransmitDone(aFrame, aAckFrame, aError); } -void Radio::Callbacks::HandleEnergyScanDone(int8_t aMaxRssi) -{ - Get().HandleEnergyScanDone(aMaxRssi); -} +void Radio::Callbacks::HandleEnergyScanDone(int8_t aMaxRssi) { Get().HandleEnergyScanDone(aMaxRssi); } #if OPENTHREAD_CONFIG_DIAG_ENABLE void Radio::Callbacks::HandleDiagsReceiveDone(Mac::RxFrame *aFrame, Error aError) diff --git a/src/core/radio/radio_platform.cpp b/src/core/radio/radio_platform.cpp index fe994c1ec73..7840a867cfe 100644 --- a/src/core/radio/radio_platform.cpp +++ b/src/core/radio/radio_platform.cpp @@ -47,7 +47,7 @@ using namespace ot; extern "C" void otPlatRadioReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError) { - Instance & instance = AsCoreType(aInstance); + Instance &instance = AsCoreType(aInstance); Mac::RxFrame *rxFrame = static_cast(aFrame); VerifyOrExit(instance.IsInitialized()); @@ -67,7 +67,7 @@ extern "C" void otPlatRadioReceiveDone(otInstance *aInstance, otRadioFrame *aFra extern "C" void otPlatRadioTxStarted(otInstance *aInstance, otRadioFrame *aFrame) { - Instance & instance = AsCoreType(aInstance); + Instance &instance = AsCoreType(aInstance); Mac::TxFrame &txFrame = *static_cast(aFrame); VerifyOrExit(instance.IsInitialized()); @@ -84,7 +84,7 @@ extern "C" void otPlatRadioTxStarted(otInstance *aInstance, otRadioFrame *aFrame extern "C" void otPlatRadioTxDone(otInstance *aInstance, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError) { - Instance & instance = AsCoreType(aInstance); + Instance &instance = AsCoreType(aInstance); Mac::TxFrame &txFrame = *static_cast(aFrame); Mac::RxFrame *ackFrame = static_cast(aAckFrame); @@ -145,30 +145,18 @@ extern "C" void otPlatDiagRadioTransmitDone(otInstance *aInstance, otRadioFrame #else // #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE -extern "C" void otPlatRadioReceiveDone(otInstance *, otRadioFrame *, otError) -{ -} +extern "C" void otPlatRadioReceiveDone(otInstance *, otRadioFrame *, otError) {} -extern "C" void otPlatRadioTxStarted(otInstance *, otRadioFrame *) -{ -} +extern "C" void otPlatRadioTxStarted(otInstance *, otRadioFrame *) {} -extern "C" void otPlatRadioTxDone(otInstance *, otRadioFrame *, otRadioFrame *, otError) -{ -} +extern "C" void otPlatRadioTxDone(otInstance *, otRadioFrame *, otRadioFrame *, otError) {} -extern "C" void otPlatRadioEnergyScanDone(otInstance *, int8_t) -{ -} +extern "C" void otPlatRadioEnergyScanDone(otInstance *, int8_t) {} #if OPENTHREAD_CONFIG_DIAG_ENABLE -extern "C" void otPlatDiagRadioReceiveDone(otInstance *, otRadioFrame *, otError) -{ -} +extern "C" void otPlatDiagRadioReceiveDone(otInstance *, otRadioFrame *, otError) {} -extern "C" void otPlatDiagRadioTransmitDone(otInstance *, otRadioFrame *, otError) -{ -} +extern "C" void otPlatDiagRadioTransmitDone(otInstance *, otRadioFrame *, otError) {} #endif #endif // // #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE @@ -201,7 +189,7 @@ OT_TOOL_WEAK otRadioState otPlatRadioGetState(otInstance *aInstance) return OT_RADIO_STATE_INVALID; } -OT_TOOL_WEAK void otPlatRadioSetMacKey(otInstance * aInstance, +OT_TOOL_WEAK void otPlatRadioSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKeyId, const otMacKeyMaterial *aPrevKey, @@ -224,11 +212,30 @@ OT_TOOL_WEAK void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t OT_UNUSED_VARIABLE(aMacFrameCounter); } -OT_TOOL_WEAK uint64_t otPlatTimeGet(void) +OT_TOOL_WEAK void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacFrameCounter) { - return UINT64_MAX; + // Radio platforms that support `OT_RADIO_CAPS_TRANSMIT_SEC` should + // provide this radio platform function. + // + // This function helps address an edge-case where OT stack may not + // yet know the latest frame counter values used by radio platform + // (e.g., due to enhanced acks processed by radio platform directly + // or due to delay between RCP and host) and then setting the value + // from OT stack may cause the counter value on radio to move back + // (OT stack will set the counter after appending it in + // `LinkFrameCounterTlv` on all radios when multi-radio links + // feature is enabled). + // + // The weak implementation here is intended as a solution to ensure + // temporary compatibility with radio platforms that may not yet + // implement it. If this weak implementation is used, the edge-case + // above may still happen. + + otPlatRadioSetMacFrameCounter(aInstance, aMacFrameCounter); } +OT_TOOL_WEAK uint64_t otPlatTimeGet(void) { return UINT64_MAX; } + OT_TOOL_WEAK uint64_t otPlatRadioGetNow(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); diff --git a/src/core/radio/trel_interface.cpp b/src/core/radio/trel_interface.cpp index 68825e9c460..7fa190767d9 100644 --- a/src/core/radio/trel_interface.cpp +++ b/src/core/radio/trel_interface.cpp @@ -59,7 +59,7 @@ Interface::Interface(Instance &aInstance) , mInitialized(false) , mEnabled(false) , mFiltered(false) - , mRegisterServiceTask(aInstance, HandleRegisterServiceTask) + , mRegisterServiceTask(aInstance) { } @@ -76,6 +76,18 @@ void Interface::Init(void) } } +void Interface::SetEnabled(bool aEnable) +{ + if (aEnable) + { + Enable(); + } + else + { + Disable(); + } +} + void Interface::Enable(void) { VerifyOrExit(!mEnabled); @@ -127,11 +139,6 @@ void Interface::HandleExtPanIdChange(void) return; } -void Interface::HandleRegisterServiceTask(Tasklet &aTasklet) -{ - aTasklet.Get().RegisterService(); -} - void Interface::RegisterService(void) { // TXT data consists of two entries: the length fields, the @@ -178,7 +185,7 @@ extern "C" void otPlatTrelHandleDiscoveredPeerInfo(otInstance *aInstance, const void Interface::HandleDiscoveredPeerInfo(const Peer::Info &aInfo) { - Peer * entry; + Peer *entry; Mac::ExtAddress extAddress; MeshCoP::ExtendedPanId extPanId; bool isNew = false; @@ -239,8 +246,8 @@ void Interface::HandleDiscoveredPeerInfo(const Peer::Info &aInfo) return; } -Error Interface::ParsePeerInfoTxtData(const Peer::Info & aInfo, - Mac::ExtAddress & aExtAddress, +Error Interface::ParsePeerInfoTxtData(const Peer::Info &aInfo, + Mac::ExtAddress &aExtAddress, MeshCoP::ExtendedPanId &aExtPanId) const { Error error; @@ -255,6 +262,15 @@ Error Interface::ParsePeerInfoTxtData(const Peer::Info & aInfo, while ((error = iterator.GetNextEntry(entry)) == kErrorNone) { + // If the TXT data happens to have entries with key longer + // than `kMaxKeyLength`, `mKey` would be `nullptr` and full + // entry would be placed in `mValue`. We skip over such + // entries. + if (entry.mKey == nullptr) + { + continue; + } + if (strcmp(entry.mKey, kTxtRecordExtAddressKey) == 0) { VerifyOrExit(!parsedExtAddress, error = kErrorParse); diff --git a/src/core/radio/trel_interface.hpp b/src/core/radio/trel_interface.hpp index b80bf1b7805..1822d4739b9 100644 --- a/src/core/radio/trel_interface.hpp +++ b/src/core/radio/trel_interface.hpp @@ -134,7 +134,7 @@ class Interface : public InstanceLocator { public: bool IsRemoved(void) const { return mRemoved; } - const uint8_t * GetTxtData(void) const { return mTxtData; } + const uint8_t *GetTxtData(void) const { return mTxtData; } uint16_t GetTxtLength(void) const { return mTxtLength; } const Ip6::SockAddr &GetSockAddr(void) const { return static_cast(mSockAddr); } }; @@ -151,6 +151,13 @@ class Interface : public InstanceLocator */ typedef otTrelPeerIterator PeerIterator; + /** + * This method enables or disables the TREL interface. + * + * @param[in] aEnable A boolean to enable/disable the TREL interface. + */ + void SetEnabled(bool aEnable); + /** * This method enables the TREL interface. * @@ -242,21 +249,22 @@ class Interface : public InstanceLocator void HandleReceived(uint8_t *aBuffer, uint16_t aLength); void HandleDiscoveredPeerInfo(const Peer::Info &aInfo); - static void HandleRegisterServiceTask(Tasklet &aTasklet); - void RegisterService(void); - Error ParsePeerInfoTxtData(const Peer::Info & aInfo, - Mac::ExtAddress & aExtAddress, - MeshCoP::ExtendedPanId &aExtPanId) const; - Peer * GetNewPeerEntry(void); - void RemovePeerEntry(Peer &aEntry); - - bool mInitialized : 1; - bool mEnabled : 1; - bool mFiltered : 1; - Tasklet mRegisterServiceTask; - uint16_t mUdpPort; - Packet mRxPacket; - PeerTable mPeerTable; + void RegisterService(void); + Error ParsePeerInfoTxtData(const Peer::Info &aInfo, + Mac::ExtAddress &aExtAddress, + MeshCoP::ExtendedPanId &aExtPanId) const; + Peer *GetNewPeerEntry(void); + void RemovePeerEntry(Peer &aEntry); + + using RegisterServiceTask = TaskletIn; + + bool mInitialized : 1; + bool mEnabled : 1; + bool mFiltered : 1; + RegisterServiceTask mRegisterServiceTask; + uint16_t mUdpPort; + Packet mRxPacket; + PeerTable mPeerTable; }; } // namespace Trel diff --git a/src/core/radio/trel_link.cpp b/src/core/radio/trel_link.cpp index 549633b0ce8..8eb2b9e88e7 100644 --- a/src/core/radio/trel_link.cpp +++ b/src/core/radio/trel_link.cpp @@ -51,8 +51,8 @@ Link::Link(Instance &aInstance) , mRxChannel(0) , mPanId(Mac::kPanIdBroadcast) , mTxPacketNumber(0) - , mTxTasklet(aInstance, HandleTxTasklet) - , mTimer(aInstance, HandleTimer) + , mTxTasklet(aInstance) + , mTimer(aInstance) , mInterface(aInstance) { memset(&mTxFrame, 0, sizeof(mTxFrame)); @@ -70,10 +70,7 @@ Link::Link(Instance &aInstance) mTimer.Start(kAckWaitWindow); } -void Link::AfterInit(void) -{ - mInterface.Init(); -} +void Link::AfterInit(void) { mInterface.Init(); } void Link::Enable(void) { @@ -116,15 +113,7 @@ void Link::Send(void) mTxTasklet.Post(); } -void Link::HandleTxTasklet(Tasklet &aTasklet) -{ - aTasklet.Get().HandleTxTasklet(); -} - -void Link::HandleTxTasklet(void) -{ - BeginTransmit(); -} +void Link::HandleTxTasklet(void) { BeginTransmit(); } void Link::BeginTransmit(void) { @@ -132,9 +121,9 @@ void Link::BeginTransmit(void) Mac::PanId destPanId; Header::Type type; Packet txPacket; - Neighbor * neighbor = nullptr; - Mac::RxFrame *ackFrame = nullptr; - bool isDisovery = false; + Neighbor *neighbor = nullptr; + Mac::RxFrame *ackFrame = nullptr; + bool isDiscovery = false; VerifyOrExit(mState == kStateTransmit); @@ -181,14 +170,14 @@ void Link::BeginTransmit(void) if (!mTxFrame.GetSecurityEnabled()) { - isDisovery = true; + isDiscovery = true; } else { uint8_t keyIdMode; IgnoreError(mTxFrame.GetKeyIdMode(keyIdMode)); - isDisovery = (keyIdMode == Mac::Frame::kKeyIdMode2); + isDiscovery = (keyIdMode == Mac::Frame::kKeyIdMode2); } } @@ -223,15 +212,15 @@ void Link::BeginTransmit(void) LogDebg("BeginTransmit() [%s] plen:%d", txPacket.GetHeader().ToString().AsCString(), txPacket.GetPayloadLength()); - VerifyOrExit(mInterface.Send(txPacket, isDisovery) == kErrorNone, InvokeSendDone(kErrorAbort)); + VerifyOrExit(mInterface.Send(txPacket, isDiscovery) == kErrorNone, InvokeSendDone(kErrorAbort)); if (mTxFrame.GetAckRequest()) { - uint16_t fcf = Mac::Frame::kFcfFrameAck; + uint16_t fcf = Mac::Frame::kTypeAck; if (!Get().IsRxOnWhenIdle()) { - fcf |= Mac::Frame::kFcfFramePending; + fcf |= kFcfFramePending; } // Prepare the ack frame (FCF followed by sequence number) @@ -245,7 +234,7 @@ void Link::BeginTransmit(void) mRxFrame.mRadioType = Mac::kRadioTypeTrel; #endif mRxFrame.mInfo.mRxInfo.mTimestamp = 0; - mRxFrame.mInfo.mRxInfo.mRssi = OT_RADIO_RSSI_INVALID; + mRxFrame.mInfo.mRxInfo.mRssi = Radio::kInvalidRssi; mRxFrame.mInfo.mRxInfo.mLqi = OT_RADIO_LQI_NONE; mRxFrame.mInfo.mRxInfo.mAckedWithFramePending = false; @@ -266,11 +255,6 @@ void Link::InvokeSendDone(Error aError, Mac::RxFrame *aAckFrame) Get().HandleTransmitDone(mTxFrame, aAckFrame, aError); } -void Link::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void Link::HandleTimer(void) { mTimer.Start(kAckWaitWindow); @@ -281,7 +265,7 @@ void Link::HandleTimer(void) HandleTimer(child); } - for (Router &router : Get().Iterate()) + for (Router &router : Get()) { HandleTimer(router); } @@ -401,7 +385,7 @@ void Link::HandleAck(Packet &aAckPacket) { Error ackError; Mac::Address srcAddress; - Neighbor * neighbor; + Neighbor *neighbor; uint32_t ackNumber; LogDebg("HandleAck() [%s]", aAckPacket.GetHeader().ToString().AsCString()); diff --git a/src/core/radio/trel_link.hpp b/src/core/radio/trel_link.hpp index fd00ac5017d..f19b0bbaee3 100644 --- a/src/core/radio/trel_link.hpp +++ b/src/core/radio/trel_link.hpp @@ -153,6 +153,7 @@ class Link : public InstanceLocator static constexpr uint16_t k154AckFrameSize = 3 + kFcsSize; static constexpr int8_t kRxRssi = -20; // The RSSI value used for received frames on TREL radio link. static constexpr uint32_t kAckWaitWindow = 750; // (in msec) + static constexpr uint16_t kFcfFramePending = 1 << 4; enum State : uint8_t { @@ -173,21 +174,20 @@ class Link : public InstanceLocator void ReportDeferredAckStatus(Neighbor &aNeighbor, Error aError); void HandleTimer(Neighbor &aNeighbor); void HandleNotifierEvents(Events aEvents); - - static void HandleTxTasklet(Tasklet &aTasklet); - void HandleTxTasklet(void); - - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); + void HandleTxTasklet(void); + void HandleTimer(void); static const char *StateToString(State aState); + using TxTasklet = TaskletIn; + using TimeoutTimer = TimerMilliIn; + State mState; uint8_t mRxChannel; Mac::PanId mPanId; uint32_t mTxPacketNumber; - Tasklet mTxTasklet; - TimerMilli mTimer; + TxTasklet mTxTasklet; + TimeoutTimer mTimer; Interface mInterface; Mac::RxFrame mRxFrame; Mac::TxFrame mTxFrame; diff --git a/src/core/radio/trel_packet.cpp b/src/core/radio/trel_packet.cpp index 9b5cabdfdd1..2847b6001cf 100644 --- a/src/core/radio/trel_packet.cpp +++ b/src/core/radio/trel_packet.cpp @@ -94,7 +94,8 @@ Header::InfoString Header::ToString(void) const break; } - string.Append(" panid:%04x num:%lu src:%s", GetPanId(), GetPacketNumber(), GetSource().ToString().AsCString()); + string.Append(" panid:%04x num:%lu src:%s", GetPanId(), ToUlong(GetPacketNumber()), + GetSource().ToString().AsCString()); if ((type == kTypeUnicast) || (type == kTypeAck)) { diff --git a/src/core/radio_cli.cmake b/src/core/radio_cli.cmake index b4e26fe1811..31e7b6dae3c 100644 --- a/src/core/radio_cli.cmake +++ b/src/core/radio_cli.cmake @@ -54,5 +54,6 @@ endif() target_link_libraries(openthread-radio-cli PRIVATE ${OT_MBEDTLS_RCP} + ot-config-radio ot-config ) diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp index 9876e938a5e..16bf8e7a471 100644 --- a/src/core/thread/address_resolver.cpp +++ b/src/core/thread/address_resolver.cpp @@ -54,19 +54,12 @@ RegisterLogModule("AddrResolver"); AddressResolver::AddressResolver(Instance &aInstance) : InstanceLocator(aInstance) - , mAddressError(UriPath::kAddressError, &AddressResolver::HandleAddressError, this) #if OPENTHREAD_FTD - , mAddressQuery(UriPath::kAddressQuery, &AddressResolver::HandleAddressQuery, this) - , mAddressNotification(UriPath::kAddressNotify, &AddressResolver::HandleAddressNotification, this) , mCacheEntryPool(aInstance) , mIcmpHandler(&AddressResolver::HandleIcmpReceive, this) #endif { - Get().AddResource(mAddressError); #if OPENTHREAD_FTD - Get().AddResource(mAddressQuery); - Get().AddResource(mAddressNotification); - IgnoreError(Get().RegisterHandler(mIcmpHandler)); #endif } @@ -96,11 +89,8 @@ void AddressResolver::Clear(void) Error AddressResolver::GetNextCacheEntry(EntryInfo &aInfo, Iterator &aIterator) const { Error error = kErrorNone; - const CacheEntryList *list; - const CacheEntry * entry; - - list = reinterpret_cast(aIterator.mData[kIteratorListIndex]); - entry = reinterpret_cast(aIterator.mData[kIteratorEntryIndex]); + const CacheEntryList *list = aIterator.GetList(); + const CacheEntry *entry = aIterator.GetEntry(); while (entry == nullptr) { @@ -130,16 +120,16 @@ Error AddressResolver::GetNextCacheEntry(EntryInfo &aInfo, Iterator &aIterator) // Update the iterator then populate the `aInfo`. - aIterator.mData[kIteratorEntryIndex] = entry->GetNext(); - aIterator.mData[kIteratorListIndex] = list; + aIterator.SetEntry(entry->GetNext()); + aIterator.SetList(list); - memset(&aInfo, 0, sizeof(aInfo)); + aInfo.Clear(); aInfo.mTarget = entry->GetTarget(); aInfo.mRloc16 = entry->GetRloc16(); if (list == &mCachedList) { - aInfo.mState = OT_CACHE_ENTRY_STATE_CACHED; + aInfo.mState = MapEnum(EntryInfo::kStateCached); aInfo.mCanEvict = true; aInfo.mValidLastTrans = entry->IsLastTransactionTimeValid(); @@ -154,15 +144,15 @@ Error AddressResolver::GetNextCacheEntry(EntryInfo &aInfo, Iterator &aIterator) if (list == &mSnoopedList) { - aInfo.mState = OT_CACHE_ENTRY_STATE_SNOOPED; + aInfo.mState = MapEnum(EntryInfo::kStateSnooped); } else if (list == &mQueryList) { - aInfo.mState = OT_CACHE_ENTRY_STATE_QUERY; + aInfo.mState = MapEnum(EntryInfo::kStateQuery); } else { - aInfo.mState = OT_CACHE_ENTRY_STATE_RETRY_QUERY; + aInfo.mState = MapEnum(EntryInfo::kStateRetryQuery); } aInfo.mCanEvict = entry->CanEvict(); @@ -173,15 +163,12 @@ Error AddressResolver::GetNextCacheEntry(EntryInfo &aInfo, Iterator &aIterator) return error; } -void AddressResolver::Remove(uint8_t aRouterId) +void AddressResolver::RemoveEntriesForRouterId(uint8_t aRouterId) { - Remove(Mle::Mle::Rloc16FromRouterId(aRouterId), /* aMatchRouterId */ true); + Remove(Mle::Rloc16FromRouterId(aRouterId), /* aMatchRouterId */ true); } -void AddressResolver::Remove(uint16_t aRloc16) -{ - Remove(aRloc16, /* aMatchRouterId */ false); -} +void AddressResolver::RemoveEntriesForRloc16(uint16_t aRloc16) { Remove(aRloc16, /* aMatchRouterId */ false); } AddressResolver::CacheEntry *AddressResolver::GetEntryAfter(CacheEntry *aPrev, CacheEntryList &aList) { @@ -199,7 +186,7 @@ void AddressResolver::Remove(Mac::ShortAddress aRloc16, bool aMatchRouterId) while ((entry = GetEntryAfter(prev, *list)) != nullptr) { - if ((aMatchRouterId && Mle::Mle::RouterIdMatch(entry->GetRloc16(), aRloc16)) || + if ((aMatchRouterId && Mle::RouterIdMatch(entry->GetRloc16(), aRloc16)) || (!aMatchRouterId && (entry->GetRloc16() == aRloc16))) { RemoveCacheEntry(*entry, *list, prev, aMatchRouterId ? kReasonRemovingRouterId : kReasonRemovingRloc16); @@ -217,10 +204,10 @@ void AddressResolver::Remove(Mac::ShortAddress aRloc16, bool aMatchRouterId) } AddressResolver::CacheEntry *AddressResolver::FindCacheEntry(const Ip6::Address &aEid, - CacheEntryList *& aList, - CacheEntry *& aPrevEntry) + CacheEntryList *&aList, + CacheEntry *&aPrevEntry) { - CacheEntry * entry = nullptr; + CacheEntry *entry = nullptr; CacheEntryList *lists[] = {&mCachedList, &mSnoopedList, &mQueryList, &mQueryRetryList}; for (CacheEntryList *list : lists) @@ -234,15 +221,12 @@ AddressResolver::CacheEntry *AddressResolver::FindCacheEntry(const Ip6::Address return entry; } -void AddressResolver::Remove(const Ip6::Address &aEid) -{ - Remove(aEid, kReasonRemovingEid); -} +void AddressResolver::RemoveEntryForAddress(const Ip6::Address &aEid) { Remove(aEid, kReasonRemovingEid); } void AddressResolver::Remove(const Ip6::Address &aEid, Reason aReason) { - CacheEntry * entry; - CacheEntry * prev; + CacheEntry *entry; + CacheEntry *prev; CacheEntryList *list; entry = FindCacheEntry(aEid, list, prev); @@ -255,10 +239,26 @@ void AddressResolver::Remove(const Ip6::Address &aEid, Reason aReason) return; } +void AddressResolver::ReplaceEntriesForRloc16(uint16_t aOldRloc16, uint16_t aNewRloc16) +{ + CacheEntryList *lists[] = {&mCachedList, &mSnoopedList}; + + for (CacheEntryList *list : lists) + { + for (CacheEntry &entry : *list) + { + if (entry.GetRloc16() == aOldRloc16) + { + entry.SetRloc16(aNewRloc16); + } + } + } +} + AddressResolver::CacheEntry *AddressResolver::NewCacheEntry(bool aSnoopedEntry) { - CacheEntry * newEntry = nullptr; - CacheEntry * prevEntry = nullptr; + CacheEntry *newEntry = nullptr; + CacheEntry *prevEntry = nullptr; CacheEntryList *lists[] = {&mSnoopedList, &mQueryRetryList, &mQueryList, &mCachedList}; // The following order is used when trying to allocate a new cache @@ -321,9 +321,9 @@ AddressResolver::CacheEntry *AddressResolver::NewCacheEntry(bool aSnoopedEntry) return newEntry; } -void AddressResolver::RemoveCacheEntry(CacheEntry & aEntry, +void AddressResolver::RemoveCacheEntry(CacheEntry &aEntry, CacheEntryList &aList, - CacheEntry * aPrevEntry, + CacheEntry *aPrevEntry, Reason aReason) { aList.PopAfter(aPrevEntry); @@ -344,8 +344,8 @@ Error AddressResolver::UpdateCacheEntry(const Ip6::Address &aEid, Mac::ShortAddr Error error = kErrorNone; CacheEntryList *list; - CacheEntry * entry; - CacheEntry * prev; + CacheEntry *entry; + CacheEntry *prev; entry = FindCacheEntry(aEid, list, prev); VerifyOrExit(entry != nullptr, error = kErrorNotFound); @@ -381,11 +381,15 @@ void AddressResolver::UpdateSnoopedCacheEntry(const Ip6::Address &aEid, Mac::ShortAddress aDest) { uint16_t numNonEvictable = 0; - CacheEntry * entry; + CacheEntry *entry; Mac::ShortAddress macAddress; VerifyOrExit(Get().IsFullThreadDevice()); +#if OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES + VerifyOrExit(ResolveUsingNetDataServices(aEid, macAddress) != kErrorNone); +#endif + VerifyOrExit(UpdateCacheEntry(aEid, aRloc16) != kErrorNone); // Skip if the `aRloc16` (i.e., the source of the snooped message) @@ -476,10 +480,14 @@ Mac::ShortAddress AddressResolver::LookUp(const Ip6::Address &aEid) Error AddressResolver::Resolve(const Ip6::Address &aEid, Mac::ShortAddress &aRloc16, bool aAllowAddressQuery) { Error error = kErrorNone; - CacheEntry * entry; - CacheEntry * prev = nullptr; + CacheEntry *entry; + CacheEntry *prev = nullptr; CacheEntryList *list; +#if OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES + VerifyOrExit(ResolveUsingNetDataServices(aEid, aRloc16) != kErrorNone); +#endif + entry = FindCacheEntry(aEid, list, prev); if (entry == nullptr) @@ -558,13 +566,48 @@ Error AddressResolver::Resolve(const Ip6::Address &aEid, Mac::ShortAddress &aRlo return error; } +#if OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES + +Error AddressResolver::ResolveUsingNetDataServices(const Ip6::Address &aEid, Mac::ShortAddress &aRloc16) +{ + // Tries to resolve `aEid` Network Data DNS/SRP Unicast address + // service entries. Returns `kErrorNone` and updates `aRloc16` + // if successful, otherwise returns `kErrorNotFound`. + + Error error = kErrorNotFound; + NetworkData::Service::Manager::Iterator iterator; + NetworkData::Service::DnsSrpUnicast::Info unicastInfo; + + VerifyOrExit(Get().GetDeviceMode().GetNetworkDataType() == NetworkData::kFullSet); + + while (Get().GetNextDnsSrpUnicastInfo(iterator, unicastInfo) == kErrorNone) + { + if (unicastInfo.mOrigin != NetworkData::Service::DnsSrpUnicast::kFromServerData) + { + continue; + } + + if (aEid == unicastInfo.mSockAddr.GetAddress()) + { + aRloc16 = unicastInfo.mRloc16; + error = kErrorNone; + ExitNow(); + } + } + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES + Error AddressResolver::SendAddressQuery(const Ip6::Address &aEid) { Error error; - Coap::Message * message; + Coap::Message *message; Tmf::MessageInfo messageInfo(GetInstance()); - message = Get().NewPriorityNonConfirmablePostMessage(UriPath::kAddressQuery); + message = Get().NewPriorityNonConfirmablePostMessage(kUriAddressQuery); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = Tlv::Append(*message, aEid)); @@ -573,7 +616,7 @@ Error AddressResolver::SendAddressQuery(const Ip6::Address &aEid) SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("Sending address query for %s", aEid.ToString().AsCString()); + LogInfo("Sent %s for %s", UriToString(), aEid.ToString().AsCString()); exit: @@ -585,8 +628,8 @@ Error AddressResolver::SendAddressQuery(const Ip6::Address &aEid) { uint16_t selfRloc16 = Get().GetRloc16(); - LogInfo("Extending ADDR.qry to BB.qry for target=%s, rloc16=%04x(self)", aEid.ToString().AsCString(), - selfRloc16); + LogInfo("Extending %s to %s for target %s, rloc16=%04x(self)", UriToString(), + UriToString(), aEid.ToString().AsCString(), selfRloc16); IgnoreError(Get().SendBackboneQuery(aEid, selfRloc16)); } #endif @@ -594,21 +637,16 @@ Error AddressResolver::SendAddressQuery(const Ip6::Address &aEid) return error; } -void AddressResolver::HandleAddressNotification(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleAddressNotification(AsCoapMessage(aMessage), - AsCoreType(aMessageInfo)); -} - -void AddressResolver::HandleAddressNotification(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> +void AddressResolver::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Ip6::Address target; Ip6::InterfaceIdentifier meshLocalIid; uint16_t rloc16; uint32_t lastTransactionTime; - CacheEntryList * list; - CacheEntry * entry; - CacheEntry * prev; + CacheEntryList *list; + CacheEntry *entry; + CacheEntry *prev; VerifyOrExit(aMessage.IsConfirmablePostRequest()); @@ -627,7 +665,7 @@ void AddressResolver::HandleAddressNotification(Coap::Message &aMessage, const I ExitNow(); } - LogInfo("Received address notification from 0x%04x for %s to 0x%04x", + LogInfo("Received %s from 0x%04x for %s to 0x%04x", UriToString(), aMessageInfo.GetPeerAddr().GetIid().GetLocator(), target.ToString().AsCString(), rloc16); entry = FindCacheEntry(target, list, prev); @@ -659,7 +697,7 @@ void AddressResolver::HandleAddressNotification(Coap::Message &aMessage, const I if (Get().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone) { - LogInfo("Sending address notification acknowledgment"); + LogInfo("Sent %s ack", UriToString()); } Get().HandleResolved(target, kErrorNone); @@ -668,18 +706,18 @@ void AddressResolver::HandleAddressNotification(Coap::Message &aMessage, const I return; } -void AddressResolver::SendAddressError(const Ip6::Address & aTarget, +void AddressResolver::SendAddressError(const Ip6::Address &aTarget, const Ip6::InterfaceIdentifier &aMeshLocalIid, - const Ip6::Address * aDestination) + const Ip6::Address *aDestination) { Error error; - Coap::Message * message; + Coap::Message *message; Tmf::MessageInfo messageInfo(GetInstance()); VerifyOrExit((message = Get().NewMessage()) != nullptr, error = kErrorNoBufs); message->Init(aDestination == nullptr ? Coap::kTypeNonConfirmable : Coap::kTypeConfirmable, Coap::kCodePost); - SuccessOrExit(error = message->AppendUriPathOptions(UriPath::kAddressError)); + SuccessOrExit(error = message->AppendUriPathOptions(PathForUri(kUriAddressError))); SuccessOrExit(error = message->SetPayloadMarker()); SuccessOrExit(error = Tlv::Append(*message, aTarget)); @@ -696,25 +734,21 @@ void AddressResolver::SendAddressError(const Ip6::Address & aTarget, SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("Sending address error for target %s", aTarget.ToString().AsCString()); + LogInfo("Sent %s for target %s", UriToString(), aTarget.ToString().AsCString()); exit: if (error != kErrorNone) { FreeMessage(message); - LogInfo("Failed to send address error: %s", ErrorToString(error)); + LogInfo("Failed to send %s: %s", UriToString(), ErrorToString(error)); } } #endif // OPENTHREAD_FTD -void AddressResolver::HandleAddressError(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleAddressError(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void AddressResolver::HandleAddressError(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> +void AddressResolver::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error = kErrorNone; Ip6::Address target; @@ -726,13 +760,13 @@ void AddressResolver::HandleAddressError(Coap::Message &aMessage, const Ip6::Mes VerifyOrExit(aMessage.IsPostRequest(), error = kErrorDrop); - LogInfo("Received address error notification"); + LogInfo("Received %s", UriToString()); if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast()) { if (Get().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone) { - LogInfo("Sent address error notification acknowledgment"); + LogInfo("Sent %s ack", UriToString()); } } @@ -789,18 +823,14 @@ void AddressResolver::HandleAddressError(Coap::Message &aMessage, const Ip6::Mes if (error != kErrorNone) { - LogWarn("Error while processing address error notification: %s", ErrorToString(error)); + LogWarn("Error %s when processing %s", ErrorToString(error), UriToString()); } } #if OPENTHREAD_FTD -void AddressResolver::HandleAddressQuery(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleAddressQuery(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void AddressResolver::HandleAddressQuery(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> +void AddressResolver::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Ip6::Address target; uint32_t lastTransactionTime; @@ -809,8 +839,8 @@ void AddressResolver::HandleAddressQuery(Coap::Message &aMessage, const Ip6::Mes SuccessOrExit(Tlv::Find(aMessage, target)); - LogInfo("Received address query from 0x%04x for target %s", aMessageInfo.GetPeerAddr().GetIid().GetLocator(), - target.ToString().AsCString()); + LogInfo("Received %s from 0x%04x for target %s", UriToString(), + aMessageInfo.GetPeerAddr().GetIid().GetLocator(), target.ToString().AsCString()); if (Get().HasUnicastAddress(target)) { @@ -839,7 +869,8 @@ void AddressResolver::HandleAddressQuery(Coap::Message &aMessage, const Ip6::Mes { uint16_t srcRloc16 = aMessageInfo.GetPeerAddr().GetIid().GetLocator(); - LogInfo("Extending ADDR.qry to BB.qry for target=%s, rloc16=%04x", target.ToString().AsCString(), srcRloc16); + LogInfo("Extending %s to %s for target %s rloc16=%04x", UriToString(), + UriToString(), target.ToString().AsCString(), srcRloc16); IgnoreError(Get().SendBackboneQuery(target, srcRloc16)); } #endif @@ -848,16 +879,16 @@ void AddressResolver::HandleAddressQuery(Coap::Message &aMessage, const Ip6::Mes return; } -void AddressResolver::SendAddressQueryResponse(const Ip6::Address & aTarget, +void AddressResolver::SendAddressQueryResponse(const Ip6::Address &aTarget, const Ip6::InterfaceIdentifier &aMeshLocalIid, - const uint32_t * aLastTransactionTime, - const Ip6::Address & aDestination) + const uint32_t *aLastTransactionTime, + const Ip6::Address &aDestination) { Error error; - Coap::Message * message; + Coap::Message *message; Tmf::MessageInfo messageInfo(GetInstance()); - message = Get().NewPriorityConfirmablePostMessage(UriPath::kAddressNotify); + message = Get().NewPriorityConfirmablePostMessage(kUriAddressNotify); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = Tlv::Append(*message, aTarget)); @@ -873,7 +904,7 @@ void AddressResolver::SendAddressQueryResponse(const Ip6::Address & a SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("Sending address notification for target %s", aTarget.ToString().AsCString()); + LogInfo("Sent %s for target %s", UriToString(), aTarget.ToString().AsCString()); exit: FreeMessageOnError(message, error); @@ -941,7 +972,7 @@ void AddressResolver::HandleTimeTick(void) mQueryList.PopAfter(prev); mQueryRetryList.Push(*entry); - LogInfo("Timed out waiting for address notification for %s, retry: %d", + LogInfo("Timed out waiting for %s for %s, retry: %d", UriToString(), entry->GetTarget().ToString().AsCString(), entry->GetTimeout()); Get().HandleResolved(entry->GetTarget(), kErrorDrop); @@ -962,8 +993,8 @@ void AddressResolver::HandleTimeTick(void) } } -void AddressResolver::HandleIcmpReceive(void * aContext, - otMessage * aMessage, +void AddressResolver::HandleIcmpReceive(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, const otIcmp6Header *aIcmpHeader) { @@ -973,8 +1004,8 @@ void AddressResolver::HandleIcmpReceive(void * aContext, AsCoreType(aIcmpHeader)); } -void AddressResolver::HandleIcmpReceive(Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, +void AddressResolver::HandleIcmpReceive(Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, const Ip6::Icmp::Header &aIcmpHeader) { OT_UNUSED_VARIABLE(aMessageInfo); @@ -998,7 +1029,7 @@ void AddressResolver::HandleIcmpReceive(Message & aMessage, void AddressResolver::LogCacheEntryChange(EntryChange aChange, Reason aReason, const CacheEntry &aEntry, - CacheEntryList * aList) + CacheEntryList *aList) { static const char *const kChangeStrings[] = { "added", // (0) kEntryAdded @@ -1049,9 +1080,7 @@ const char *AddressResolver::ListToString(const CacheEntryList *aList) const #else // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) -void AddressResolver::LogCacheEntryChange(EntryChange, Reason, const CacheEntry &, CacheEntryList *) -{ -} +void AddressResolver::LogCacheEntryChange(EntryChange, Reason, const CacheEntry &, CacheEntryList *) {} #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE) diff --git a/src/core/thread/address_resolver.hpp b/src/core/thread/address_resolver.hpp index b65fc604dcb..81ee36be57e 100644 --- a/src/core/thread/address_resolver.hpp +++ b/src/core/thread/address_resolver.hpp @@ -37,6 +37,7 @@ #include "openthread-core-config.h" #include "coap/coap.hpp" +#include "common/as_core_type.hpp" #include "common/linked_list.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" @@ -46,6 +47,7 @@ #include "net/icmp6.hpp" #include "net/udp6.hpp" #include "thread/thread_tlvs.hpp" +#include "thread/tmf.hpp" namespace ot { @@ -65,19 +67,44 @@ namespace ot { class AddressResolver : public InstanceLocator, private NonCopyable { friend class TimeTicker; + friend class Tmf::Agent; + + class CacheEntry; + class CacheEntryList; public: /** * This type represents an iterator used for iterating through the EID cache table entries. * */ - typedef otCacheEntryIterator Iterator; + class Iterator : public otCacheEntryIterator, public Clearable + { + friend class AddressResolver; + + static constexpr uint8_t kListIndex = 0; + static constexpr uint8_t kEntryIndex = 1; + + const CacheEntry *GetEntry(void) const { return static_cast(mData[kEntryIndex]); } + void SetEntry(const CacheEntry *aEntry) { mData[kEntryIndex] = aEntry; } + const CacheEntryList *GetList(void) const { return static_cast(mData[kListIndex]); } + void SetList(const CacheEntryList *aList) { mData[kListIndex] = aList; } + }; /** * This type represents an EID cache entry. * */ - typedef otCacheEntryInfo EntryInfo; + class EntryInfo : public otCacheEntryInfo, public Clearable + { + public: + enum State : uint8_t ///< Entry state. + { + kStateCached = OT_CACHE_ENTRY_STATE_CACHED, ///< Cached and in-use. + kStateSnooped = OT_CACHE_ENTRY_STATE_SNOOPED, ///< Created by snoop optimization. + kStateQuery = OT_CACHE_ENTRY_STATE_QUERY, ///< Ongoing query for the EID. + kStateRetryQuery = OT_CACHE_ENTRY_STATE_RETRY_QUERY, ///< In retry wait mode. + }; + }; /** * This constructor initializes the object. @@ -112,7 +139,7 @@ class AddressResolver : public InstanceLocator, private NonCopyable * @param[in] aRloc16 The RLOC16 address. * */ - void Remove(Mac::ShortAddress aRloc16); + void RemoveEntriesForRloc16(Mac::ShortAddress aRloc16); /** * This method removes all EID-to-RLOC cache entries associated with a Router ID. @@ -120,7 +147,7 @@ class AddressResolver : public InstanceLocator, private NonCopyable * @param[in] aRouterId The Router ID. * */ - void Remove(uint8_t aRouterId); + void RemoveEntriesForRouterId(uint8_t aRouterId); /** * This method removes the cache entry for the EID. @@ -128,7 +155,16 @@ class AddressResolver : public InstanceLocator, private NonCopyable * @param[in] aEid A reference to the EID. * */ - void Remove(const Ip6::Address &aEid); + void RemoveEntryForAddress(const Ip6::Address &aEid); + + /** + * This method replaces all EID-to-RLOC cache entries corresponding to an old RLOC16 with a new RLOC16. + * + * @param[in] aOldRloc16 The old RLOC16. + * @param[in] aNewRloc16 The new RLOC16. + * + */ + void ReplaceEntriesForRloc16(uint16_t aOldRloc16, uint16_t aNewRloc16); /** * This method updates an existing entry or adds a snooped cache entry for a given EID. @@ -188,10 +224,10 @@ class AddressResolver : public InstanceLocator, private NonCopyable * @param[in] aDestination The destination to send the ADDR_NTF.ans message. * */ - void SendAddressQueryResponse(const Ip6::Address & aTarget, + void SendAddressQueryResponse(const Ip6::Address &aTarget, const Ip6::InterfaceIdentifier &aMeshLocalIid, - const uint32_t * aLastTransactionTimeTlv, - const Ip6::Address & aDestination); + const uint32_t *aLastTransactionTimeTlv, + const Ip6::Address &aDestination); /** * This method sends an Address Error Notification (ADDR_ERR.ntf) message. @@ -201,9 +237,9 @@ class AddressResolver : public InstanceLocator, private NonCopyable * @param aDestination The destination to send the ADDR_ERR.ntf message. * */ - void SendAddressError(const Ip6::Address & aTarget, + void SendAddressError(const Ip6::Address &aTarget, const Ip6::InterfaceIdentifier &aMeshLocalIid, - const Ip6::Address * aDestination); + const Ip6::Address *aDestination); private: static constexpr uint16_t kCacheEntries = OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_ENTRIES; @@ -215,15 +251,12 @@ class AddressResolver : public InstanceLocator, private NonCopyable static constexpr uint16_t kAddressQueryMaxRetryDelay = OPENTHREAD_CONFIG_TMF_ADDRESS_QUERY_MAX_RETRY_DELAY; static constexpr uint16_t kSnoopBlockEvictionTimeout = OPENTHREAD_CONFIG_TMF_SNOOP_CACHE_ENTRY_TIMEOUT; - static constexpr uint8_t kIteratorListIndex = 0; - static constexpr uint8_t kIteratorEntryIndex = 1; - class CacheEntry : public InstanceLocatorInit { public: void Init(Instance &aInstance); - CacheEntry * GetNext(void); + CacheEntry *GetNext(void); const CacheEntry *GetNext(void) const; void SetNext(CacheEntry *aEntry); @@ -281,7 +314,10 @@ class AddressResolver : public InstanceLocator, private NonCopyable }; typedef Pool CacheEntryPool; - typedef LinkedList CacheEntryList; + + class CacheEntryList : public LinkedList + { + }; enum EntryChange : uint8_t { @@ -311,62 +347,60 @@ class AddressResolver : public InstanceLocator, private NonCopyable CacheEntry *NewCacheEntry(bool aSnoopedEntry); void RemoveCacheEntry(CacheEntry &aEntry, CacheEntryList &aList, CacheEntry *aPrevEntry, Reason aReason); Error UpdateCacheEntry(const Ip6::Address &aEid, Mac::ShortAddress aRloc16); - - Error SendAddressQuery(const Ip6::Address &aEid); + Error SendAddressQuery(const Ip6::Address &aEid); +#if OPENTHREAD_CONFIG_TMF_ALLOW_ADDRESS_RESOLUTION_USING_NET_DATA_SERVICES + Error ResolveUsingNetDataServices(const Ip6::Address &aEid, Mac::ShortAddress &aRloc16); +#endif static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); #endif // OPENTHREAD_FTD - static void HandleAddressError(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleAddressError(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); #if OPENTHREAD_FTD - static void HandleAddressQuery(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleAddressQuery(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - - static void HandleAddressNotification(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleAddressNotification(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleIcmpReceive(void * aContext, - otMessage * aMessage, + static void HandleIcmpReceive(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, const otIcmp6Header *aIcmpHeader); - void HandleIcmpReceive(Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, + void HandleIcmpReceive(Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, const Ip6::Icmp::Header &aIcmpHeader); - void HandleTimeTick(void); - - void LogCacheEntryChange(EntryChange aChange, - Reason aReason, - const CacheEntry &aEntry, - CacheEntryList * aList = nullptr); - + void HandleTimeTick(void); + void LogCacheEntryChange(EntryChange aChange, + Reason aReason, + const CacheEntry &aEntry, + CacheEntryList *aList = nullptr); const char *ListToString(const CacheEntryList *aList) const; static AddressResolver::CacheEntry *GetEntryAfter(CacheEntry *aPrev, CacheEntryList &aList); -#endif // OPENTHREAD_FTD - Coap::Resource mAddressError; -#if OPENTHREAD_FTD - Coap::Resource mAddressQuery; - Coap::Resource mAddressNotification; - - CacheEntryPool mCacheEntryPool; - CacheEntryList mCachedList; - CacheEntryList mSnoopedList; - CacheEntryList mQueryList; - CacheEntryList mQueryRetryList; - + CacheEntryPool mCacheEntryPool; + CacheEntryList mCachedList; + CacheEntryList mSnoopedList; + CacheEntryList mQueryList; + CacheEntryList mQueryRetryList; Ip6::Icmp::Handler mIcmpHandler; -#endif // OPENTHREAD_FTD + +#endif // OPENTHREAD_FTD }; +DeclareTmfHandler(AddressResolver, kUriAddressError); +#if OPENTHREAD_FTD +DeclareTmfHandler(AddressResolver, kUriAddressQuery); +DeclareTmfHandler(AddressResolver, kUriAddressNotify); +#endif + /** * @} */ +DefineCoreType(otCacheEntryIterator, AddressResolver::Iterator); +DefineCoreType(otCacheEntryInfo, AddressResolver::EntryInfo); +DefineMapEnum(otCacheEntryState, AddressResolver::EntryInfo::State); + } // namespace ot #endif // ADDRESS_RESOLVER_HPP_ diff --git a/src/core/thread/announce_begin_server.cpp b/src/core/thread/announce_begin_server.cpp index ddac782fa02..1c904e358a9 100644 --- a/src/core/thread/announce_begin_server.cpp +++ b/src/core/thread/announce_begin_server.cpp @@ -52,9 +52,7 @@ RegisterLogModule("MeshCoP"); AnnounceBeginServer::AnnounceBeginServer(Instance &aInstance) : AnnounceSenderBase(aInstance, AnnounceBeginServer::HandleTimer) - , mAnnounceBegin(UriPath::kAnnounceBegin, &AnnounceBeginServer::HandleRequest, this) { - Get().AddResource(mAnnounceBegin); } void AnnounceBeginServer::SendAnnounce(uint32_t aChannelMask, uint8_t aCount, uint16_t aPeriod) @@ -65,17 +63,12 @@ void AnnounceBeginServer::SendAnnounce(uint32_t aChannelMask, uint8_t aCount, ui AnnounceSenderBase::SendAnnounce(aCount); } -void AnnounceBeginServer::HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +template <> +void AnnounceBeginServer::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - static_cast(aContext)->HandleRequest(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void AnnounceBeginServer::HandleRequest(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) -{ - uint32_t mask; - uint8_t count; - uint16_t period; - Ip6::MessageInfo responseInfo(aMessageInfo); + uint32_t mask; + uint8_t count; + uint16_t period; VerifyOrExit(aMessage.IsPostRequest()); VerifyOrExit((mask = MeshCoP::ChannelMaskTlv::GetChannelMask(aMessage)) != 0); @@ -87,8 +80,8 @@ void AnnounceBeginServer::HandleRequest(Coap::Message &aMessage, const Ip6::Mess if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast()) { - SuccessOrExit(Get().SendEmptyAck(aMessage, responseInfo)); - LogInfo("Sent announce begin response"); + SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); + LogInfo("Sent %s response", UriToString()); } exit: diff --git a/src/core/thread/announce_begin_server.hpp b/src/core/thread/announce_begin_server.hpp index aef72e2d4a4..a5aafd1f3f6 100644 --- a/src/core/thread/announce_begin_server.hpp +++ b/src/core/thread/announce_begin_server.hpp @@ -36,11 +36,11 @@ #include "openthread-core-config.h" -#include "coap/coap.hpp" #include "common/locator.hpp" #include "common/timer.hpp" #include "net/ip6_address.hpp" #include "thread/announce_sender.hpp" +#include "thread/tmf.hpp" namespace ot { @@ -50,6 +50,8 @@ namespace ot { */ class AnnounceBeginServer : public AnnounceSenderBase { + friend class Tmf::Agent; + public: /** * This constructor initializes the object. @@ -72,14 +74,13 @@ class AnnounceBeginServer : public AnnounceSenderBase static constexpr uint16_t kDefaultPeriod = 1000; static constexpr uint16_t kDefaultJitter = 0; - static void HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleRequest(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); static void HandleTimer(Timer &aTimer); - - Coap::Resource mAnnounceBegin; }; +DeclareTmfHandler(AnnounceBeginServer, kUriAnnounceBegin); + /** * @} */ diff --git a/src/core/thread/announce_sender.cpp b/src/core/thread/announce_sender.cpp index c5005761a58..810939e4cc4 100644 --- a/src/core/thread/announce_sender.cpp +++ b/src/core/thread/announce_sender.cpp @@ -161,10 +161,7 @@ AnnounceSender::AnnounceSender(Instance &aInstance) SetJitter(kMaxJitter); } -void AnnounceSender::UpdateOnReceivedAnnounce(void) -{ - mTrickleTimer.IndicateConsistent(); -} +void AnnounceSender::UpdateOnReceivedAnnounce(void) { mTrickleTimer.IndicateConsistent(); } void AnnounceSender::Stop(void) { @@ -173,15 +170,9 @@ void AnnounceSender::Stop(void) LogInfo("Stopped"); } -void AnnounceSender::HandleTimer(Timer &aTimer) -{ - aTimer.Get().AnnounceSenderBase::HandleTimer(); -} +void AnnounceSender::HandleTimer(Timer &aTimer) { aTimer.Get().AnnounceSenderBase::HandleTimer(); } -void AnnounceSender::HandleTrickleTimer(TrickleTimer &aTimer) -{ - aTimer.Get().HandleTrickleTimer(); -} +void AnnounceSender::HandleTrickleTimer(TrickleTimer &aTimer) { aTimer.Get().HandleTrickleTimer(); } void AnnounceSender::HandleTrickleTimer(void) { @@ -259,7 +250,7 @@ void AnnounceSender::HandleActiveDatasetChanged(void) SetChannelMask(channelMask); SetPeriod(kTxInterval / channelMask.GetNumberOfChannels()); - LogInfo("ChannelMask:%s, period:%u", GetChannelMask().ToString().AsCString(), GetPeriod()); + LogInfo("ChannelMask:%s, period:%lu", GetChannelMask().ToString().AsCString(), ToUlong(GetPeriod())); // When channel mask is changed, we also check and update the PAN // channel. This handles the case where `ThreadChannelChanged` event diff --git a/src/core/thread/anycast_locator.cpp b/src/core/thread/anycast_locator.cpp index a8989cdbfa0..88a02d4e188 100644 --- a/src/core/thread/anycast_locator.cpp +++ b/src/core/thread/anycast_locator.cpp @@ -46,31 +46,23 @@ namespace ot { AnycastLocator::AnycastLocator(Instance &aInstance) : InstanceLocator(aInstance) - , mCallback(nullptr) - , mContext(nullptr) -#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_SEND_RESPONSE - , mAnycastLocate(UriPath::kAnycastLocate, HandleAnycastLocate, this) -#endif { -#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_SEND_RESPONSE - Get().AddResource(mAnycastLocate); -#endif } -Error AnycastLocator::Locate(const Ip6::Address &aAnycastAddress, Callback aCallback, void *aContext) +Error AnycastLocator::Locate(const Ip6::Address &aAnycastAddress, LocatorCallback aCallback, void *aContext) { Error error = kErrorNone; - Coap::Message * message = nullptr; + Coap::Message *message = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); VerifyOrExit((aCallback != nullptr) && Get().IsAnycastLocator(aAnycastAddress), error = kErrorInvalidArgs); - message = Get().NewConfirmablePostMessage(UriPath::kAnycastLocate); + message = Get().NewConfirmablePostMessage(kUriAnycastLocate); VerifyOrExit(message != nullptr, error = kErrorNoBufs); - if (mCallback != nullptr) + if (mCallback.IsSet()) { IgnoreError(Get().AbortTransaction(HandleResponse, this)); } @@ -79,16 +71,15 @@ Error AnycastLocator::Locate(const Ip6::Address &aAnycastAddress, Callback aCall SuccessOrExit(error = Get().SendMessage(*message, messageInfo, HandleResponse, this)); - mCallback = aCallback; - mContext = aContext; + mCallback.Set(aCallback, aContext); exit: FreeMessageOnError(message, error); return error; } -void AnycastLocator::HandleResponse(void * aContext, - otMessage * aMessage, +void AnycastLocator::HandleResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aError) { @@ -118,29 +109,25 @@ void AnycastLocator::HandleResponse(Coap::Message *aMessage, const Ip6::MessageI address = &meshLocalAddress; exit: - if (mCallback != nullptr) + if (mCallback.IsSet()) { - Callback callback = mCallback; + Callback callbackCopy = mCallback; - mCallback = nullptr; - callback(mContext, aError, address, rloc16); + mCallback.Clear(); + callbackCopy.Invoke(aError, address, rloc16); } } #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_SEND_RESPONSE -void AnycastLocator::HandleAnycastLocate(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleAnycastLocate(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void AnycastLocator::HandleAnycastLocate(const Coap::Message &aRequest, const Ip6::MessageInfo &aMessageInfo) +template <> +void AnycastLocator::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Coap::Message *message = nullptr; - VerifyOrExit(aRequest.IsConfirmablePostRequest()); + VerifyOrExit(aMessage.IsConfirmablePostRequest()); - message = Get().NewResponseMessage(aRequest); + message = Get().NewResponseMessage(aMessage); VerifyOrExit(message != nullptr); SuccessOrExit(Tlv::Append(*message, Get().GetMeshLocal64().GetIid())); diff --git a/src/core/thread/anycast_locator.hpp b/src/core/thread/anycast_locator.hpp index 5ae937f45a3..cba969d2e38 100644 --- a/src/core/thread/anycast_locator.hpp +++ b/src/core/thread/anycast_locator.hpp @@ -38,10 +38,11 @@ #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE -#include "coap/coap.hpp" +#include "common/callback.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" #include "net/ip6_address.hpp" +#include "thread/tmf.hpp" namespace ot { @@ -54,12 +55,14 @@ namespace ot { */ class AnycastLocator : public InstanceLocator, private NonCopyable { + friend class Tmf::Agent; + public: /** * This function pointer type defines the callback to notify the outcome of a request. * */ - typedef otThreadAnycastLocatorCallback Callback; + typedef otThreadAnycastLocatorCallback LocatorCallback; /** * This constructor initializes the `AnycastLocator` object. @@ -84,7 +87,7 @@ class AnycastLocator : public InstanceLocator, private NonCopyable * @retval kErrorInvalidArgs The @p aAnycastAddress is not a valid anycast address or @p aCallback is `nullptr`. * */ - Error Locate(const Ip6::Address &aAnycastAddress, Callback aCallback, void *aContext); + Error Locate(const Ip6::Address &aAnycastAddress, LocatorCallback aCallback, void *aContext); /** * This method indicates whether an earlier request is in progress. @@ -92,24 +95,21 @@ class AnycastLocator : public InstanceLocator, private NonCopyable * @returns TRUE if an earlier request is in progress, FALSE otherwise. * */ - bool IsInProgress(void) const { return (mCallback != nullptr); } + bool IsInProgress(void) const { return mCallback.IsSet(); } private: static void HandleResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aError); void HandleResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aError); -#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_SEND_RESPONSE - static void HandleAnycastLocate(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleAnycastLocate(const Coap::Message &aRequest, const Ip6::MessageInfo &aMessageInfo); -#endif + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + + Callback mCallback; +}; - Callback mCallback; - void * mContext; #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_SEND_RESPONSE - Coap::Resource mAnycastLocate; +DeclareTmfHandler(AnycastLocator, kUriAnycastLocate); #endif -}; } // namespace ot diff --git a/src/core/utils/child_supervision.cpp b/src/core/thread/child_supervision.cpp similarity index 80% rename from src/core/utils/child_supervision.cpp rename to src/core/thread/child_supervision.cpp index b0f45bdd148..02ba64ada89 100644 --- a/src/core/utils/child_supervision.cpp +++ b/src/core/thread/child_supervision.cpp @@ -41,29 +41,19 @@ #include "thread/thread_netif.hpp" namespace ot { -namespace Utils { RegisterLogModule("ChildSupervsn"); -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - #if OPENTHREAD_FTD ChildSupervisor::ChildSupervisor(Instance &aInstance) : InstanceLocator(aInstance) - , mSupervisionInterval(kDefaultSupervisionInterval) -{ -} - -void ChildSupervisor::SetSupervisionInterval(uint16_t aInterval) { - mSupervisionInterval = aInterval; - CheckState(); } Child *ChildSupervisor::GetDestination(const Message &aMessage) const { - Child * child = nullptr; + Child *child = nullptr; uint16_t childIndex; VerifyOrExit(aMessage.GetType() == Message::kTypeSupervision); @@ -93,7 +83,7 @@ void ChildSupervisor::SendMessage(Child &aChild) childIndex = Get().GetChildIndex(aChild); SuccessOrExit(message->Append(childIndex)); - SuccessOrExit(Get().SendMessage(*message)); + SuccessOrExit(Get().SendMessage(*message)); message = nullptr; LogInfo("Sending supervision message to child 0x%04x", aChild.GetRloc16()); @@ -102,18 +92,20 @@ void ChildSupervisor::SendMessage(Child &aChild) FreeMessage(message); } -void ChildSupervisor::UpdateOnSend(Child &aChild) -{ - aChild.ResetSecondsSinceLastSupervision(); -} +void ChildSupervisor::UpdateOnSend(Child &aChild) { aChild.ResetSecondsSinceLastSupervision(); } void ChildSupervisor::HandleTimeTick(void) { for (Child &child : Get().Iterate(Child::kInStateValid)) { + if (child.IsRxOnWhenIdle() || (child.GetSupervisionInterval() == 0)) + { + continue; + } + child.IncrementSecondsSinceLastSupervision(); - if ((child.GetSecondsSinceLastSupervision() >= mSupervisionInterval) && !child.IsRxOnWhenIdle()) + if (child.GetSecondsSinceLastSupervision() >= child.GetSupervisionInterval()) { SendMessage(child); } @@ -122,14 +114,11 @@ void ChildSupervisor::HandleTimeTick(void) void ChildSupervisor::CheckState(void) { - bool shouldRun = false; + // Child Supervision should run if Thread MLE operation is + // enabled, and there is at least one "valid" child in the + // child table. - // Child Supervision should run if `mSupervisionInterval` is not - // zero, Thread MLE operation is enabled, and there is at least one - // "valid" child in the child table. - - shouldRun = ((mSupervisionInterval != 0) && !Get().IsDisabled() && - Get().HasChildren(Child::kInStateValid)); + bool shouldRun = (!Get().IsDisabled() && Get().HasChildren(Child::kInStateValid)); if (shouldRun && !Get().IsReceiverRegistered(TimeTicker::kChildSupervisor)) { @@ -157,25 +146,39 @@ void ChildSupervisor::HandleNotifierEvents(Events aEvents) SupervisionListener::SupervisionListener(Instance &aInstance) : InstanceLocator(aInstance) , mTimeout(0) - , mTimer(aInstance, SupervisionListener::HandleTimer) + , mInterval(kDefaultInterval) + , mTimer(aInstance) { SetTimeout(kDefaultTimeout); } -void SupervisionListener::Start(void) -{ - RestartTimer(); -} +void SupervisionListener::Start(void) { RestartTimer(); } + +void SupervisionListener::Stop(void) { mTimer.Stop(); } -void SupervisionListener::Stop(void) +void SupervisionListener::SetInterval(uint16_t aInterval) { - mTimer.Stop(); + VerifyOrExit(mInterval != aInterval); + + LogInfo("Interval: %u -> %u", mInterval, aInterval); + + mInterval = aInterval; + + if (Get().IsChild()) + { + IgnoreError(Get().SendChildUpdateRequest()); + } + +exit: + return; } void SupervisionListener::SetTimeout(uint16_t aTimeout) { if (mTimeout != aTimeout) { + LogInfo("Timeout: %u -> %u", mTimeout, aTimeout); + mTimeout = aTimeout; RestartTimer(); } @@ -206,16 +209,12 @@ void SupervisionListener::RestartTimer(void) } } -void SupervisionListener::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void SupervisionListener::HandleTimer(void) { VerifyOrExit(Get().IsChild() && !Get().GetRxOnWhenIdle()); - LogWarn("Supervision timeout. No frame from parent in %d sec", mTimeout); + LogWarn("Supervision timeout. No frame from parent in %u sec", mTimeout); + mCounter++; IgnoreError(Get().SendChildUpdateRequest()); @@ -223,7 +222,4 @@ void SupervisionListener::HandleTimer(void) RestartTimer(); } -#endif // #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - -} // namespace Utils } // namespace ot diff --git a/src/core/utils/child_supervision.hpp b/src/core/thread/child_supervision.hpp similarity index 87% rename from src/core/utils/child_supervision.hpp rename to src/core/thread/child_supervision.hpp index 30b26f1f3fe..c56291f69c9 100644 --- a/src/core/utils/child_supervision.hpp +++ b/src/core/thread/child_supervision.hpp @@ -53,8 +53,6 @@ namespace ot { class ThreadNetif; class Child; -namespace Utils { - /** * * Child supervision feature provides a mechanism for parent @@ -85,8 +83,6 @@ namespace Utils { * */ -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - #if OPENTHREAD_FTD /** @@ -119,25 +115,6 @@ class ChildSupervisor : public InstanceLocator, private NonCopyable */ void Stop(void); - /** - * This method sets the supervision interval. - * - * Setting the supervision interval to a non-zero value will ensure to start the supervision process (if not - * already started). - * - * @param[in] aInterval If non-zero, the desired supervision interval (in seconds), zero to disable supervision. - * - */ - void SetSupervisionInterval(uint16_t aInterval); - - /** - * This method returns the supervision interval. - * - * @returns The current supervision interval (seconds), or zero if supervision is disabled. - * - */ - uint16_t GetSupervisionInterval(void) const { return mSupervisionInterval; } - /** * This method returns the destination for a supervision message. * @@ -165,8 +142,6 @@ class ChildSupervisor : public InstanceLocator, private NonCopyable void CheckState(void); void HandleTimeTick(void); void HandleNotifierEvents(Events aEvents); - - uint16_t mSupervisionInterval; }; #endif // #if OPENTHREAD_FTD @@ -198,6 +173,22 @@ class SupervisionListener : public InstanceLocator, private NonCopyable */ void Stop(void); + /** + * This method sets the supervision interval. + * + * @param[in] aInterval If non-zero, the desired supervision interval (in seconds), zero to disable supervision. + * + */ + void SetInterval(uint16_t aInterval); + + /** + * This method returns the supervision interval. + * + * @returns The current supervision interval (seconds), or zero if supervision is disabled. + * + */ + uint16_t GetInterval(void) const { return mInterval; } + /** * This method sets the supervision check timeout (in seconds). * @@ -221,6 +212,21 @@ class SupervisionListener : public InstanceLocator, private NonCopyable */ uint16_t GetTimeout(void) const { return mTimeout; } + /** + * This method returns the value of supervision check timeout failure counter. + * + * The counter tracks the number of supervision check failures on the child. It is incremented when the child does + * not hear from its parent within the specified check timeout interval. + * + */ + uint16_t GetCounter(void) const { return mCounter; } + + /** + * This method reset the supervision check timeout failure counter. + * + */ + void ResetCounter(void) { mCounter = 0; } + /** * This method updates the supervision listener state. It informs the listener of a received frame. * @@ -231,24 +237,20 @@ class SupervisionListener : public InstanceLocator, private NonCopyable void UpdateOnReceive(const Mac::Address &aSourceAddress, bool aIsSecure); private: - static constexpr uint16_t kDefaultTimeout = OPENTHREAD_CONFIG_CHILD_SUPERVISION_CHECK_TIMEOUT; // (seconds) + static constexpr uint16_t kDefaultTimeout = OPENTHREAD_CONFIG_CHILD_SUPERVISION_CHECK_TIMEOUT; // (seconds) + static constexpr uint16_t kDefaultInterval = OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL; // (seconds) - void RestartTimer(void); - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); + void RestartTimer(void); + void HandleTimer(void); - uint16_t mTimeout; - TimerMilli mTimer; -}; + using ListenerTimer = TimerMilliIn; -#endif // #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - -/** - * @} - * - */ + uint16_t mTimeout; + uint16_t mInterval; + uint16_t mCounter; + ListenerTimer mTimer; +}; -} // namespace Utils } // namespace ot #endif // CHILD_SUPERVISION_HPP_ diff --git a/src/core/thread/child_table.cpp b/src/core/thread/child_table.cpp index 9eca6955f92..e5ec2093665 100644 --- a/src/core/thread/child_table.cpp +++ b/src/core/thread/child_table.cpp @@ -184,12 +184,12 @@ Error ChildTable::SetMaxChildrenAllowed(uint16_t aMaxChildren) Error ChildTable::GetChildInfoById(uint16_t aChildId, Child::Info &aChildInfo) { Error error = kErrorNone; - Child * child; + Child *child; uint16_t rloc16; if ((aChildId & ~Mle::kMaxChildId) != 0) { - aChildId = Mle::Mle::ChildIdFromRloc16(aChildId); + aChildId = Mle::ChildIdFromRloc16(aChildId); } rloc16 = Get().GetShortAddress() | aChildId; @@ -246,7 +246,7 @@ void ChildTable::Restore(void) child->SetDeviceMode(Mle::DeviceMode(childInfo.GetMode())); child->SetState(Neighbor::kStateRestored); child->SetLastHeard(TimerMilli::GetNow()); - child->SetVersion(static_cast(childInfo.GetVersion())); + child->SetVersion(childInfo.GetVersion()); Get().SetChildUseShortAddress(*child, true); Get().Signal(NeighborTable::kChildAdded, *child); numChildren++; diff --git a/src/core/thread/csl_tx_scheduler.cpp b/src/core/thread/csl_tx_scheduler.cpp index 15beb59b94a..9d86408cc76 100644 --- a/src/core/thread/csl_tx_scheduler.cpp +++ b/src/core/thread/csl_tx_scheduler.cpp @@ -46,7 +46,7 @@ CslTxScheduler::Callbacks::Callbacks(Instance &aInstance) inline Error CslTxScheduler::Callbacks::PrepareFrameForChild(Mac::TxFrame &aFrame, FrameContext &aContext, - Child & aChild) + Child &aChild) { return Get().PrepareFrameForChild(aFrame, aContext, aChild); } @@ -54,7 +54,7 @@ inline Error CslTxScheduler::Callbacks::PrepareFrameForChild(Mac::TxFrame &aFram inline void CslTxScheduler::Callbacks::HandleSentFrameToChild(const Mac::TxFrame &aFrame, const FrameContext &aContext, Error aError, - Child & aChild) + Child &aChild) { Get().HandleSentFrameToChild(aFrame, aContext, aError, aChild); } @@ -90,6 +90,7 @@ void CslTxScheduler::Update(void) { // `Mac` has already started the CSL tx, so wait for tx done callback // to call `RescheduleCslTx` + mCslTxChild->ResetCslTxAttempts(); mCslTxChild = nullptr; mFrameContext.mMessageNextOffset = 0; } @@ -122,7 +123,7 @@ void CslTxScheduler::Clear(void) void CslTxScheduler::RescheduleCslTx(void) { uint32_t minDelayTime = Time::kMaxDuration; - Child * bestChild = nullptr; + Child *bestChild = nullptr; for (Child &child : Get().Iterate(Child::kInStateAnyExceptInvalid)) { @@ -134,7 +135,7 @@ void CslTxScheduler::RescheduleCslTx(void) continue; } - delay = GetNextCslTransmissionDelay(child, cslTxDelay); + delay = GetNextCslTransmissionDelay(child, cslTxDelay, mCslFrameRequestAheadUs); if (delay < minDelayTime) { @@ -151,18 +152,24 @@ void CslTxScheduler::RescheduleCslTx(void) mCslTxChild = bestChild; } -uint32_t CslTxScheduler::GetNextCslTransmissionDelay(const Child &aChild, uint32_t &aDelayFromLastRx) const +uint32_t CslTxScheduler::GetNextCslTransmissionDelay(const Child &aChild, + uint32_t &aDelayFromLastRx, + uint32_t aAheadUs) const { - uint64_t radioNow = otPlatRadioGetNow(&GetInstance()); - uint32_t periodInUs = aChild.GetCslPeriod() * kUsPerTenSymbols; - uint64_t firstTxWindow = aChild.GetLastRxTimestamp() + aChild.GetCslPhase() * kUsPerTenSymbols; - uint64_t nextTxWindow = radioNow - (radioNow % periodInUs) + (firstTxWindow % periodInUs); + uint64_t radioNow = otPlatRadioGetNow(&GetInstance()); + uint32_t periodInUs = aChild.GetCslPeriod() * kUsPerTenSymbols; + uint64_t firstTxWindow = + aChild.GetLastRxTimestamp() - kRadioHeaderShrDuration + aChild.GetCslPhase() * kUsPerTenSymbols; + uint64_t nextTxWindow = radioNow - (radioNow % periodInUs) + (firstTxWindow % periodInUs); - while (nextTxWindow < radioNow + mCslFrameRequestAheadUs) nextTxWindow += periodInUs; + while (nextTxWindow < radioNow + aAheadUs) + { + nextTxWindow += periodInUs; + } aDelayFromLastRx = static_cast(nextTxWindow - aChild.GetLastRxTimestamp()); - return static_cast(nextTxWindow - radioNow - mCslFrameRequestAheadUs); + return static_cast(nextTxWindow - radioNow - aAheadUs); } #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE @@ -171,6 +178,7 @@ Mac::TxFrame *CslTxScheduler::HandleFrameRequest(Mac::TxFrames &aTxFrames) { Mac::TxFrame *frame = nullptr; uint32_t txDelay; + uint32_t delay; VerifyOrExit(mCslTxChild != nullptr); @@ -207,7 +215,30 @@ Mac::TxFrame *CslTxScheduler::HandleFrameRequest(Mac::TxFrames &aTxFrames) frame->SetChannel(mCslTxChild->GetCslChannel() == 0 ? Get().GetPanChannel() : mCslTxChild->GetCslChannel()); - GetNextCslTransmissionDelay(*mCslTxChild, txDelay); + if (frame->GetChannel() != Get().GetPanChannel()) + { + frame->SetRxChannelAfterTxDone(Get().GetPanChannel()); + } + + delay = GetNextCslTransmissionDelay(*mCslTxChild, txDelay, /* aAheadUs */ 0); + + // We make sure that delay is less than `mCslFrameRequestAheadUs` + // plus some guard time. Note that we used `mCslFrameRequestAheadUs` + // in `RescheduleCslTx()` when determining the next CSL delay to + // schedule CSL tx with `Mac` but here we calculate the delay with + // zero `aAheadUs`. All the timings are in usec but when passing + // delay to `Mac` we divide by `1000` (to covert to msec) which + // can round the value down and cause `Mac` to start operation a + // bit (some usec) earlier. This is covered by adding the guard + // time `kFramePreparationGuardInterval`. + // + // In general this check handles the case where `Mac` is busy with + // other operations and therefore late to start the CSL tx operation + // and by the time `HandleFrameRequest()` is invoked, we miss the + // current CSL window and move to the next window. + + VerifyOrExit(delay <= mCslFrameRequestAheadUs + kFramePreparationGuardInterval, frame = nullptr); + frame->SetTxDelay(txDelay); frame->SetTxDelayBaseTime( static_cast(mCslTxChild->GetLastRxTimestamp())); // Only LSB part of the time is required. @@ -219,10 +250,7 @@ Mac::TxFrame *CslTxScheduler::HandleFrameRequest(Mac::TxFrames &aTxFrames) #else // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE -Mac::TxFrame *CslTxScheduler::HandleFrameRequest(Mac::TxFrames &) -{ - return nullptr; -} +Mac::TxFrame *CslTxScheduler::HandleFrameRequest(Mac::TxFrames &) { return nullptr; } #endif // OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE @@ -239,7 +267,7 @@ void CslTxScheduler::HandleSentFrame(const Mac::TxFrame &aFrame, Error aError) HandleSentFrame(aFrame, aError, *child); exit: - return; + RescheduleCslTx(); } void CslTxScheduler::HandleSentFrame(const Mac::TxFrame &aFrame, Error aError, Child &aChild) @@ -291,7 +319,6 @@ void CslTxScheduler::HandleSentFrame(const Mac::TxFrame &aFrame, Error aError, C } } - RescheduleCslTx(); ExitNow(); default: diff --git a/src/core/thread/csl_tx_scheduler.hpp b/src/core/thread/csl_tx_scheduler.hpp index d8e03681c8d..e0378b22857 100644 --- a/src/core/thread/csl_tx_scheduler.hpp +++ b/src/core/thread/csl_tx_scheduler.hpp @@ -97,8 +97,8 @@ class CslTxScheduler : public InstanceLocator, private NonCopyable TimeMilli GetCslLastHeard(void) const { return mCslLastHeard; } void SetCslLastHeard(TimeMilli aCslLastHeard) { mCslLastHeard = aCslLastHeard; } - uint64_t GetLastRxTimestamp(void) const { return mLastRxTimstamp; } - void SetLastRxTimestamp(uint64_t aLastRxTimestamp) { mLastRxTimstamp = aLastRxTimestamp; } + uint64_t GetLastRxTimestamp(void) const { return mLastRxTimestamp; } + void SetLastRxTimestamp(uint64_t aLastRxTimestamp) { mLastRxTimestamp = aLastRxTimestamp; } private: uint8_t mCslTxAttempts : 7; ///< Number of CSL triggered tx attempts. @@ -108,7 +108,7 @@ class CslTxScheduler : public InstanceLocator, private NonCopyable uint16_t mCslPeriod; ///< CSL sampled listening period in units of 10 symbols (160 microseconds). uint16_t mCslPhase; ///< The time when the next CSL sample will start. TimeMilli mCslLastHeard; ///< Time when last frame containing CSL IE was heard. - uint64_t mLastRxTimstamp; ///< Time when last frame containing CSL IE was received, in microseconds. + uint64_t mLastRxTimestamp; ///< Time when last frame containing CSL IE was received, in microseconds. static_assert(kMaxCslTriggeredTxAttempts < (1 << 7), "mCslTxAttempts cannot fit max!"); }; @@ -160,7 +160,7 @@ class CslTxScheduler : public InstanceLocator, private NonCopyable void HandleSentFrameToChild(const Mac::TxFrame &aFrame, const FrameContext &aContext, Error aError, - Child & aChild); + Child &aChild); }; /** * This constructor initializes the CSL tx scheduler object. @@ -187,10 +187,13 @@ class CslTxScheduler : public InstanceLocator, private NonCopyable void Clear(void); private: + // Guard time in usec to add when checking delay while preparing the CSL frame for tx. + static constexpr uint32_t kFramePreparationGuardInterval = 1500; + void InitFrameRequestAhead(void); void RescheduleCslTx(void); - uint32_t GetNextCslTransmissionDelay(const Child &aChild, uint32_t &aDelayFromLastRx) const; + uint32_t GetNextCslTransmissionDelay(const Child &aChild, uint32_t &aDelayFromLastRx, uint32_t aAheadUs) const; // Callbacks from `Mac` Mac::TxFrame *HandleFrameRequest(Mac::TxFrames &aTxFrames); @@ -199,8 +202,8 @@ class CslTxScheduler : public InstanceLocator, private NonCopyable void HandleSentFrame(const Mac::TxFrame &aFrame, Error aError, Child &aChild); uint32_t mCslFrameRequestAheadUs; - Child * mCslTxChild; - Message * mCslTxMessage; + Child *mCslTxChild; + Message *mCslTxMessage; Callbacks::FrameContext mFrameContext; Callbacks mCallbacks; }; diff --git a/src/core/thread/discover_scanner.cpp b/src/core/thread/discover_scanner.cpp index bf6d60f4405..288bbe7c98d 100644 --- a/src/core/thread/discover_scanner.cpp +++ b/src/core/thread/discover_scanner.cpp @@ -40,15 +40,15 @@ #include "thread/mesh_forwarder.hpp" #include "thread/mle.hpp" #include "thread/mle_router.hpp" +#include "thread/version.hpp" namespace ot { namespace Mle { DiscoverScanner::DiscoverScanner(Instance &aInstance) : InstanceLocator(aInstance) - , mHandler(nullptr) - , mHandlerContext(nullptr) - , mTimer(aInstance, DiscoverScanner::HandleTimer) + , mScanDoneTask(aInstance) + , mTimer(aInstance) , mFilterIndexes() , mState(kStateIdle) , mScanChannel(0) @@ -62,12 +62,12 @@ Error DiscoverScanner::Discover(const Mac::ChannelMask &aScanChannels, uint16_t aPanId, bool aJoiner, bool aEnableFiltering, - const FilterIndexes * aFilterIndexes, + const FilterIndexes *aFilterIndexes, Handler aCallback, - void * aContext) + void *aContext) { Error error = kErrorNone; - Mle::TxMessage * message = nullptr; + Mle::TxMessage *message = nullptr; Tlv tlv; Ip6::Address destination; MeshCoP::DiscoveryRequestTlv discoveryRequest; @@ -95,8 +95,7 @@ Error DiscoverScanner::Discover(const Mac::ChannelMask &aScanChannels, } } - mHandler = aCallback; - mHandlerContext = aContext; + mCallback.Set(aCallback, aContext); mShouldRestorePanId = false; mScanChannels = Get().GetSupportedChannelMask(); @@ -153,6 +152,12 @@ Error DiscoverScanner::Discover(const Mac::ChannelMask &aScanChannels, mScanChannel = Mac::ChannelMask::kChannelIteratorFirst; mState = (mScanChannels.GetNextChannel(mScanChannel) == kErrorNone) ? kStateScanning : kStateScanDone; + // For rx-off-when-idle device, temporarily enable receiver during discovery procedure. + if (!Get().IsDisabled() && !Get().IsRxOnWhenIdle()) + { + Get().SetRxOnWhenIdle(true); + } + Mle::Log(Mle::kMessageSend, Mle::kTypeDiscoveryRequest, destination); exit: @@ -226,6 +231,12 @@ void DiscoverScanner::HandleDiscoveryRequestFrameTxDone(Message &aMessage) void DiscoverScanner::HandleDiscoverComplete(void) { + // Restore Data Polling or CSL for rx-off-when-idle device. + if (!Get().IsDisabled() && !Get().IsRxOnWhenIdle()) + { + Get().SetRxOnWhenIdle(false); + } + switch (mState) { case kStateIdle: @@ -246,20 +257,18 @@ void DiscoverScanner::HandleDiscoverComplete(void) mShouldRestorePanId = false; } - mState = kStateIdle; - - if (mHandler) - { - mHandler(nullptr, mHandlerContext); - } - + // Post the tasklet to change `mState` and invoke handler + // callback. This allows users to safely call OT APIs from + // the callback. + mScanDoneTask.Post(); break; } } -void DiscoverScanner::HandleTimer(Timer &aTimer) +void DiscoverScanner::HandleScanDoneTask(void) { - aTimer.Get().HandleTimer(); + mState = kStateIdle; + mCallback.InvokeIfSet(nullptr); } void DiscoverScanner::HandleTimer(void) @@ -287,13 +296,13 @@ void DiscoverScanner::HandleTimer(void) void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const { Error error = kErrorNone; - const ThreadLinkInfo * linkInfo = aRxInfo.mMessageInfo.GetThreadLinkInfo(); - Tlv tlv; + const ThreadLinkInfo *linkInfo = aRxInfo.mMessageInfo.GetThreadLinkInfo(); MeshCoP::Tlv meshcopTlv; MeshCoP::DiscoveryResponseTlv discoveryResponse; MeshCoP::NetworkNameTlv networkName; ScanResult result; uint16_t offset; + uint16_t length; uint16_t end; bool didCheckSteeringData = false; @@ -302,11 +311,8 @@ void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const VerifyOrExit(mState == kStateScanning, error = kErrorDrop); // Find MLE Discovery TLV - VerifyOrExit(Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kDiscovery, offset) == kErrorNone, error = kErrorParse); - IgnoreError(aRxInfo.mMessage.Read(offset, tlv)); - - offset += sizeof(tlv); - end = offset + tlv.GetLength(); + SuccessOrExit(error = Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kDiscovery, offset, length)); + end = offset + length; memset(&result, 0, sizeof(result)); result.mDiscover = true; @@ -357,7 +363,7 @@ void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const steeringData.Init(dataLength); - SuccessOrExit(error = Tlv::ReadTlv(aRxInfo.mMessage, offset, steeringData.GetData(), dataLength)); + SuccessOrExit(error = Tlv::ReadTlvValue(aRxInfo.mMessage, offset, steeringData.GetData(), dataLength)); if (mEnableFiltering) { @@ -382,10 +388,7 @@ void DiscoverScanner::HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const VerifyOrExit(!mEnableFiltering || didCheckSteeringData); - if (mHandler) - { - mHandler(&result, mHandlerContext); - } + mCallback.InvokeIfSet(&result); exit: Mle::LogProcessError(Mle::kTypeDiscoveryResponse, error); diff --git a/src/core/thread/discover_scanner.hpp b/src/core/thread/discover_scanner.hpp index 061aa2f6633..ac5ac523ea0 100644 --- a/src/core/thread/discover_scanner.hpp +++ b/src/core/thread/discover_scanner.hpp @@ -36,8 +36,10 @@ #include "openthread-core-config.h" +#include "common/callback.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" +#include "common/tasklet.hpp" #include "common/timer.hpp" #include "mac/channel_mask.hpp" #include "mac/mac.hpp" @@ -126,9 +128,9 @@ class DiscoverScanner : public InstanceLocator, private NonCopyable Mac::PanId aPanId, bool aJoiner, bool aEnableFiltering, - const FilterIndexes * aFilterIndexes, + const FilterIndexes *aFilterIndexes, Handler aCallback, - void * aContext); + void *aContext); /** * This method indicates whether or not an MLE Thread Discovery Scan is currently in progress. @@ -169,22 +171,25 @@ class DiscoverScanner : public InstanceLocator, private NonCopyable // Methods used from `Mle` void HandleDiscoveryResponse(Mle::RxInfo &aRxInfo) const; - void HandleDiscoverComplete(void); - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); - - Handler mHandler; - void * mHandlerContext; - TimerMilli mTimer; - FilterIndexes mFilterIndexes; - Mac::ChannelMask mScanChannels; - State mState; - uint32_t mOui; - uint8_t mScanChannel; - uint8_t mAdvDataLength; - uint8_t mAdvData[MeshCoP::JoinerAdvertisementTlv::kAdvDataMaxLength]; - bool mEnableFiltering : 1; - bool mShouldRestorePanId : 1; + void HandleDiscoverComplete(void); + void HandleScanDoneTask(void); + void HandleTimer(void); + + using ScanTimer = TimerMilliIn; + using ScanDoneTask = TaskletIn; + + Callback mCallback; + ScanDoneTask mScanDoneTask; + ScanTimer mTimer; + FilterIndexes mFilterIndexes; + Mac::ChannelMask mScanChannels; + State mState; + uint32_t mOui; + uint8_t mScanChannel; + uint8_t mAdvDataLength; + uint8_t mAdvData[MeshCoP::JoinerAdvertisementTlv::kAdvDataMaxLength]; + bool mEnableFiltering : 1; + bool mShouldRestorePanId : 1; }; } // namespace Mle diff --git a/src/core/thread/dua_manager.cpp b/src/core/thread/dua_manager.cpp index 108f07a7357..53faa17f8ea 100644 --- a/src/core/thread/dua_manager.cpp +++ b/src/core/thread/dua_manager.cpp @@ -54,8 +54,7 @@ RegisterLogModule("DuaManager"); DuaManager::DuaManager(Instance &aInstance) : InstanceLocator(aInstance) - , mRegistrationTask(aInstance, DuaManager::HandleRegistrationTask) - , mDuaNotification(UriPath::kDuaRegistrationNotify, &DuaManager::HandleDuaNotification, this) + , mRegistrationTask(aInstance) , mIsDuaPending(false) #if OPENTHREAD_CONFIG_DUA_ENABLE , mDuaState(kNotExist) @@ -77,8 +76,6 @@ DuaManager::DuaManager(Instance &aInstance) mChildDuaMask.Clear(); mChildDuaRegisteredMask.Clear(); #endif - - Get().AddResource(mDuaNotification); } void DuaManager::HandleDomainPrefixUpdate(BackboneRouter::Leader::DomainPrefixState aState) @@ -108,7 +105,7 @@ void DuaManager::HandleDomainPrefixUpdate(BackboneRouter::Leader::DomainPrefixSt switch (aState) { case BackboneRouter::Leader::kDomainPrefixUnchanged: - // In case removed for some reason e.g. the kDuaInvalid response from PBBR forcely + // In case removed for some reason e.g. the kDuaInvalid response from PBBR forcefully VerifyOrExit(!Get().HasUnicastAddress(GetDomainUnicastAddress())); OT_FALL_THROUGH; @@ -276,7 +273,7 @@ void DuaManager::NotifyDuplicateDomainUnicastAddress(void) void DuaManager::UpdateReregistrationDelay(void) { uint16_t delay = 0; - otBackboneRouterConfig config; + BackboneRouter::Config config; VerifyOrExit(Get().GetConfig(config) == kErrorNone); @@ -308,6 +305,19 @@ void DuaManager::HandleNotifierEvents(Events aEvents) { Mle::MleRouter &mle = Get(); +#if OPENTHREAD_CONFIG_DUA_ENABLE + if (aEvents.Contains(kEventThreadNetdataChanged)) + { + Lowpan::Context context; + // Remove a stale DUA address if any. + if (Get().HasUnicastAddress(Get().GetDomainUnicastAddress()) && + (Get().GetContext(Get().GetDomainUnicastAddress(), context) != kErrorNone)) + { + RemoveDomainUnicastAddress(); + } + } +#endif + VerifyOrExit(mle.IsAttached(), mDelay.mValue = 0); if (aEvents.Contains(kEventThreadRoleChanged)) @@ -341,14 +351,19 @@ void DuaManager::HandleNotifierEvents(Events aEvents) return; } -void DuaManager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState, - const BackboneRouter::BackboneRouterConfig &aConfig) +void DuaManager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState, + const BackboneRouter::Config &aConfig) { OT_UNUSED_VARIABLE(aConfig); if (aState == BackboneRouter::Leader::kStateAdded || aState == BackboneRouter::Leader::kStateToTriggerRereg) { - UpdateReregistrationDelay(); +#if OPENTHREAD_CONFIG_DUA_ENABLE + if (Get().IsFullThreadDevice() || Get().GetParent().IsThreadVersion1p1()) +#endif + { + UpdateReregistrationDelay(); + } } } @@ -402,11 +417,6 @@ void DuaManager::HandleTimeTick(void) UpdateTimeTickerRegistration(); } -void DuaManager::HandleRegistrationTask(Tasklet &aTasklet) -{ - aTasklet.Get().PerformNextRegistration(); -} - void DuaManager::UpdateTimeTickerRegistration(void) { if (mDelay.mValue == 0) @@ -422,8 +432,8 @@ void DuaManager::UpdateTimeTickerRegistration(void) void DuaManager::PerformNextRegistration(void) { Error error = kErrorNone; - Mle::MleRouter & mle = Get(); - Coap::Message * message = nullptr; + Mle::MleRouter &mle = Get(); + Coap::Message *message = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); Ip6::Address dua; @@ -455,7 +465,7 @@ void DuaManager::PerformNextRegistration(void) } // Prepare DUA.req - message = Get().NewPriorityConfirmablePostMessage(UriPath::kDuaRegistrationRequest); + message = Get().NewPriorityConfirmablePostMessage(kUriDuaRegistrationRequest); VerifyOrExit(message != nullptr, error = kErrorNoBufs); #if OPENTHREAD_CONFIG_DUA_ENABLE @@ -473,7 +483,7 @@ void DuaManager::PerformNextRegistration(void) #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE uint32_t lastTransactionTime; const Ip6::Address *duaPtr = nullptr; - Child * child = nullptr; + Child *child = nullptr; OT_ASSERT(mChildIndexDuaRegistering == Mle::kMaxChildren); @@ -531,7 +541,7 @@ void DuaManager::PerformNextRegistration(void) Get().SendFastPolls(); } - LogInfo("Sent DUA.req for DUA %s", dua.ToString().AsCString()); + LogInfo("Sent %s for DUA %s", UriToString(), dua.ToString().AsCString()); exit: if (error == kErrorNoBufs) @@ -543,8 +553,8 @@ void DuaManager::PerformNextRegistration(void) FreeMessageOnError(message, error); } -void DuaManager::HandleDuaResponse(void * aContext, - otMessage * aMessage, +void DuaManager::HandleDuaResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult) { @@ -582,14 +592,11 @@ void DuaManager::HandleDuaResponse(Coap::Message *aMessage, const Ip6::MessageIn mRegistrationTask.Post(); } - LogInfo("Received DUA.rsp: %s", ErrorToString(error)); + LogInfo("Received %s response: %s", UriToString(), ErrorToString(error)); } -void DuaManager::HandleDuaNotification(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleDuaNotification(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} -void DuaManager::HandleDuaNotification(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> +void DuaManager::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { OT_UNUSED_VARIABLE(aMessageInfo); @@ -599,14 +606,14 @@ void DuaManager::HandleDuaNotification(Coap::Message &aMessage, const Ip6::Messa if (aMessage.IsConfirmable() && Get().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone) { - LogInfo("Sent DUA.ntf acknowledgment"); + LogInfo("Sent %s ack", UriToString()); } error = ProcessDuaResponse(aMessage); exit: OT_UNUSED_VARIABLE(error); - LogInfo("Received DUA.ntf: %d", ErrorToString(error)); + LogInfo("Received %s: %s", UriToString(), ErrorToString(error)); } Error DuaManager::ProcessDuaResponse(Coap::Message &aMessage) @@ -662,7 +669,7 @@ Error DuaManager::ProcessDuaResponse(Coap::Message &aMessage) #endif #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE { - Child * child = nullptr; + Child *child = nullptr; uint16_t childIndex; for (Child &iter : Get().Iterate(Child::kInStateValid)) @@ -718,15 +725,15 @@ Error DuaManager::ProcessDuaResponse(Coap::Message &aMessage) } #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE -void DuaManager::SendAddressNotification(Ip6::Address & aAddress, +void DuaManager::SendAddressNotification(Ip6::Address &aAddress, ThreadStatusTlv::DuaStatus aStatus, - const Child & aChild) + const Child &aChild) { - Coap::Message * message = nullptr; + Coap::Message *message = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); Error error; - message = Get().NewPriorityConfirmablePostMessage(UriPath::kDuaRegistrationNotify); + message = Get().NewPriorityConfirmablePostMessage(kUriDuaRegistrationNotify); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = Tlv::Append(*message, aStatus)); @@ -736,7 +743,8 @@ void DuaManager::SendAddressNotification(Ip6::Address & aAddress, SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("Sent ADDR_NTF for child %04x DUA %s", aChild.GetRloc16(), aAddress.ToString().AsCString()); + LogInfo("Sent %s for child %04x DUA %s", UriToString(), aChild.GetRloc16(), + aAddress.ToString().AsCString()); exit: @@ -745,8 +753,8 @@ void DuaManager::SendAddressNotification(Ip6::Address & aAddress, FreeMessage(message); // TODO: (DUA) (P4) may enhance to guarantee the delivery of DUA.ntf - LogWarn("Sent ADDR_NTF for child %04x DUA %s Error %s", aChild.GetRloc16(), aAddress.ToString().AsCString(), - ErrorToString(error)); + LogWarn("Sent %s for child %04x DUA %s Error %s", UriToString(), aChild.GetRloc16(), + aAddress.ToString().AsCString(), ErrorToString(error)); } } @@ -778,8 +786,6 @@ void DuaManager::UpdateChildDomainUnicastAddress(const Child &aChild, Mle::Child mChildDuaMask.Set(childIndex, true); mChildDuaRegisteredMask.Set(childIndex, false); } - - return; } #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE diff --git a/src/core/thread/dua_manager.hpp b/src/core/thread/dua_manager.hpp index 45d780797dc..d8df6c672be 100644 --- a/src/core/thread/dua_manager.hpp +++ b/src/core/thread/dua_manager.hpp @@ -47,7 +47,6 @@ #endif #include "backbone_router/bbr_leader.hpp" -#include "coap/coap.hpp" #include "coap/coap_message.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" @@ -58,6 +57,7 @@ #include "common/timer.hpp" #include "net/netif.hpp" #include "thread/thread_tlvs.hpp" +#include "thread/tmf.hpp" #include "thread/topology.hpp" namespace ot { @@ -84,6 +84,7 @@ class DuaManager : public InstanceLocator, private NonCopyable { friend class ot::Notifier; friend class ot::TimeTicker; + friend class Tmf::Agent; public: /** @@ -109,8 +110,7 @@ class DuaManager : public InstanceLocator, private NonCopyable * @param[in] aConfig The Primary Backbone Router service. * */ - void HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState, - const BackboneRouter::BackboneRouterConfig &aConfig); + void HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState, const BackboneRouter::Config &aConfig); #if OPENTHREAD_CONFIG_DUA_ENABLE @@ -195,18 +195,15 @@ class DuaManager : public InstanceLocator, private NonCopyable void HandleTimeTick(void); - static void HandleRegistrationTask(Tasklet &aTasklet); - void UpdateTimeTickerRegistration(void); - static void HandleDuaResponse(void * aContext, - otMessage * aMessage, + static void HandleDuaResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); void HandleDuaResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); - static void HandleDuaNotification(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleDuaNotification(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); Error ProcessDuaResponse(Coap::Message &aMessage); @@ -214,10 +211,11 @@ class DuaManager : public InstanceLocator, private NonCopyable void UpdateReregistrationDelay(void); void UpdateCheckDelay(uint8_t aDelay); - Tasklet mRegistrationTask; - Coap::Resource mDuaNotification; - Ip6::Address mRegisteringDua; - bool mIsDuaPending : 1; + using RegistrationTask = TaskletIn; + + RegistrationTask mRegistrationTask; + Ip6::Address mRegisteringDua; + bool mIsDuaPending : 1; #if OPENTHREAD_CONFIG_DUA_ENABLE enum DuaState : uint8_t @@ -258,6 +256,8 @@ class DuaManager : public InstanceLocator, private NonCopyable #endif }; +DeclareTmfHandler(DuaManager, kUriDuaRegistrationNotify); + } // namespace ot #endif // OPENTHREAD_CONFIG_DUA_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE) diff --git a/src/core/thread/energy_scan_server.cpp b/src/core/thread/energy_scan_server.cpp index 9c6b9135ec1..c207ef9f3ad 100644 --- a/src/core/thread/energy_scan_server.cpp +++ b/src/core/thread/energy_scan_server.cpp @@ -43,7 +43,6 @@ #include "meshcop/meshcop.hpp" #include "meshcop/meshcop_tlvs.hpp" #include "thread/thread_netif.hpp" -#include "thread/uri_paths.hpp" namespace ot { @@ -56,26 +55,20 @@ EnergyScanServer::EnergyScanServer(Instance &aInstance) , mPeriod(0) , mScanDuration(0) , mCount(0) - , mActive(false) - , mScanResultsLength(0) - , mTimer(aInstance, EnergyScanServer::HandleTimer) - , mEnergyScan(UriPath::kEnergyScan, &EnergyScanServer::HandleRequest, this) + , mReportMessage(nullptr) + , mTimer(aInstance) { - Get().AddResource(mEnergyScan); } -void EnergyScanServer::HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +template <> +void EnergyScanServer::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - static_cast(aContext)->HandleRequest(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void EnergyScanServer::HandleRequest(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) -{ - uint8_t count; - uint16_t period; - uint16_t scanDuration; - Ip6::MessageInfo responseInfo(aMessageInfo); - uint32_t mask; + uint8_t count; + uint16_t period; + uint16_t scanDuration; + uint32_t mask; + MeshCoP::Tlv tlv; + MeshCoP::ChannelMaskTlv channelMaskTlv; VerifyOrExit(aMessage.IsPostRequest()); @@ -85,40 +78,46 @@ void EnergyScanServer::HandleRequest(Coap::Message &aMessage, const Ip6::Message VerifyOrExit((mask = MeshCoP::ChannelMaskTlv::GetChannelMask(aMessage)) != 0); + FreeMessage(mReportMessage); + mReportMessage = Get().NewPriorityConfirmablePostMessage(kUriEnergyReport); + VerifyOrExit(mReportMessage != nullptr); + + channelMaskTlv.Init(); + channelMaskTlv.SetChannelMask(mask); + SuccessOrExit(channelMaskTlv.AppendTo(*mReportMessage)); + + tlv.SetType(MeshCoP::Tlv::kEnergyList); + SuccessOrExit(mReportMessage->Append(tlv)); + + mNumScanResults = 0; mChannelMask = mask; mChannelMaskCurrent = mChannelMask; mCount = count; mPeriod = period; mScanDuration = scanDuration; - mScanResultsLength = 0; - mActive = true; mTimer.Start(kScanDelay); mCommissioner = aMessageInfo.GetPeerAddr(); if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast()) { - SuccessOrExit(Get().SendEmptyAck(aMessage, responseInfo)); - LogInfo("sent energy scan query response"); + SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); + LogInfo("Sent %s ack", UriToString()); } exit: return; } -void EnergyScanServer::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void EnergyScanServer::HandleTimer(void) { - VerifyOrExit(mActive); + VerifyOrExit(mReportMessage != nullptr); if (mCount) { // grab the lowest channel to scan uint32_t channelMask = mChannelMaskCurrent & ~(mChannelMaskCurrent - 1); + IgnoreError(Get().EnergyScan(channelMask, mScanDuration, HandleScanResult, this)); } else @@ -137,16 +136,31 @@ void EnergyScanServer::HandleScanResult(Mac::EnergyScanResult *aResult, void *aC void EnergyScanServer::HandleScanResult(Mac::EnergyScanResult *aResult) { - VerifyOrExit(mActive); + VerifyOrExit(mReportMessage != nullptr); if (aResult) { - VerifyOrExit(mScanResultsLength < OPENTHREAD_CONFIG_TMF_ENERGY_SCAN_MAX_RESULTS); - mScanResults[mScanResultsLength++] = aResult->mMaxRssi; + if (mReportMessage->Append(aResult->mMaxRssi) != kErrorNone) + { + FreeMessage(mReportMessage); + mReportMessage = nullptr; + ExitNow(); + } + + mNumScanResults++; + + if (mNumScanResults == NumericLimits::kMax) + { + // If we reach the max length that fit in the Energy List + // TLV we send the current set of energy scan data. + + mCount = 0; + mTimer.Start(kReportDelay); + } } else { - // clear the lowest channel to scan + // Clear the lowest channel to scan mChannelMaskCurrent &= mChannelMaskCurrent - 1; if (mChannelMaskCurrent == 0) @@ -171,42 +185,33 @@ void EnergyScanServer::HandleScanResult(Mac::EnergyScanResult *aResult) void EnergyScanServer::SendReport(void) { - Error error = kErrorNone; - MeshCoP::ChannelMaskTlv channelMask; - MeshCoP::EnergyListTlv energyList; - Tmf::MessageInfo messageInfo(GetInstance()); - Coap::Message * message; - - message = Get().NewPriorityConfirmablePostMessage(UriPath::kEnergyReport); - VerifyOrExit(message != nullptr, error = kErrorNoBufs); - - channelMask.Init(); - channelMask.SetChannelMask(mChannelMask); - SuccessOrExit(error = channelMask.AppendTo(*message)); + Error error = kErrorNone; + Tmf::MessageInfo messageInfo(GetInstance()); + uint16_t offset; - energyList.Init(); - energyList.SetLength(mScanResultsLength); - SuccessOrExit(error = message->Append(energyList)); - SuccessOrExit(error = message->AppendBytes(mScanResults, mScanResultsLength)); + // Update the Energy List TLV length in Report message + offset = mReportMessage->GetLength() - mNumScanResults - sizeof(uint8_t); + mReportMessage->Write(offset, mNumScanResults); messageInfo.SetSockAddrToRlocPeerAddrTo(mCommissioner); - SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); + SuccessOrExit(error = Get().SendMessage(*mReportMessage, messageInfo)); - LogInfo("sent scan results"); + LogInfo("Sent %s", UriToString()); exit: - FreeMessageOnError(message, error); + FreeMessageOnError(mReportMessage, error); MeshCoP::LogError("send scan results", error); - mActive = false; + mReportMessage = nullptr; } void EnergyScanServer::HandleNotifierEvents(Events aEvents) { - if (aEvents.Contains(kEventThreadNetdataChanged) && !mActive && + if (aEvents.Contains(kEventThreadNetdataChanged) && (mReportMessage != nullptr) && Get().GetCommissioningData() == nullptr) { - mActive = false; + mReportMessage->Free(); + mReportMessage = nullptr; mTimer.Stop(); } } diff --git a/src/core/thread/energy_scan_server.hpp b/src/core/thread/energy_scan_server.hpp index 1f168d18326..ca7c7a50675 100644 --- a/src/core/thread/energy_scan_server.hpp +++ b/src/core/thread/energy_scan_server.hpp @@ -36,7 +36,6 @@ #include "openthread-core-config.h" -#include "coap/coap.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" #include "common/notifier.hpp" @@ -44,6 +43,7 @@ #include "net/ip6_address.hpp" #include "net/udp6.hpp" #include "thread/thread_tlvs.hpp" +#include "thread/tmf.hpp" namespace ot { @@ -54,6 +54,7 @@ namespace ot { class EnergyScanServer : public InstanceLocator, private NonCopyable { friend class ot::Notifier; + friend class Tmf::Agent; public: /** @@ -63,38 +64,35 @@ class EnergyScanServer : public InstanceLocator, private NonCopyable explicit EnergyScanServer(Instance &aInstance); private: - static constexpr uint32_t kScanDelay = 1000; ///< SCAN_DELAY (milliseconds) - static constexpr uint32_t kReportDelay = 500; ///< Delay before sending a report (milliseconds) + static constexpr uint32_t kScanDelay = 1000; // SCAN_DELAY (milliseconds) + static constexpr uint32_t kReportDelay = 500; // Delay before sending a report (milliseconds) - static void HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleRequest(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); static void HandleScanResult(Mac::EnergyScanResult *aResult, void *aContext); void HandleScanResult(Mac::EnergyScanResult *aResult); - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); + void HandleTimer(void); void HandleNotifierEvents(Events aEvents); void SendReport(void); - Ip6::Address mCommissioner; - uint32_t mChannelMask; - uint32_t mChannelMaskCurrent; - uint16_t mPeriod; - uint16_t mScanDuration; - uint8_t mCount; - bool mActive; - - int8_t mScanResults[OPENTHREAD_CONFIG_TMF_ENERGY_SCAN_MAX_RESULTS]; - uint8_t mScanResultsLength; - - TimerMilli mTimer; - - Coap::Resource mEnergyScan; + using ScanTimer = TimerMilliIn; + + Ip6::Address mCommissioner; + uint32_t mChannelMask; + uint32_t mChannelMaskCurrent; + uint16_t mPeriod; + uint16_t mScanDuration; + uint8_t mCount; + uint8_t mNumScanResults; + Coap::Message *mReportMessage; + ScanTimer mTimer; }; +DeclareTmfHandler(EnergyScanServer, kUriEnergyScan); + /** * @} */ diff --git a/src/core/thread/indirect_sender.cpp b/src/core/thread/indirect_sender.cpp index 6b4e68c8ef8..697ce35d1a3 100644 --- a/src/core/thread/indirect_sender.cpp +++ b/src/core/thread/indirect_sender.cpp @@ -345,13 +345,12 @@ Error IndirectSender::PrepareFrameForChild(Mac::TxFrame &aFrame, FrameContext &a break; case Message::kTypeSupervision: - PrepareEmptyFrame(aFrame, aChild, kSupervisionMsgAckRequest); + PrepareEmptyFrame(aFrame, aChild, /* aAckRequest */ true); aContext.mMessageNextOffset = message->GetLength(); break; default: OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); } exit: @@ -360,24 +359,24 @@ Error IndirectSender::PrepareFrameForChild(Mac::TxFrame &aFrame, FrameContext &a uint16_t IndirectSender::PrepareDataFrame(Mac::TxFrame &aFrame, Child &aChild, Message &aMessage) { - Ip6::Header ip6Header; - Mac::Address macSource, macDest; - uint16_t directTxOffset; - uint16_t nextOffset; + Ip6::Header ip6Header; + Mac::Addresses macAddrs; + uint16_t directTxOffset; + uint16_t nextOffset; // Determine the MAC source and destination addresses. IgnoreError(aMessage.Read(0, ip6Header)); - Get().GetMacSourceAddress(ip6Header.GetSource(), macSource); + Get().GetMacSourceAddress(ip6Header.GetSource(), macAddrs.mSource); if (ip6Header.GetDestination().IsLinkLocal()) { - Get().GetMacDestinationAddress(ip6Header.GetDestination(), macDest); + Get().GetMacDestinationAddress(ip6Header.GetDestination(), macAddrs.mDestination); } else { - aChild.GetMacAddress(macDest); + aChild.GetMacAddress(macAddrs.mDestination); } // Prepare the data frame from previous child's indirect offset. @@ -385,7 +384,7 @@ uint16_t IndirectSender::PrepareDataFrame(Mac::TxFrame &aFrame, Child &aChild, M directTxOffset = aMessage.GetOffset(); aMessage.SetOffset(aChild.GetIndirectFragmentOffset()); - nextOffset = Get().PrepareDataFrame(aFrame, aMessage, macSource, macDest); + nextOffset = Get().PrepareDataFrame(aFrame, aMessage, macAddrs); aMessage.SetOffset(directTxOffset); @@ -412,19 +411,17 @@ void IndirectSender::PrepareEmptyFrame(Mac::TxFrame &aFrame, Child &aChild, bool void IndirectSender::HandleSentFrameToChild(const Mac::TxFrame &aFrame, const FrameContext &aContext, Error aError, - Child & aChild) + Child &aChild) { Message *message = aChild.GetIndirectMessage(); uint16_t nextOffset = aContext.mMessageNextOffset; VerifyOrExit(mEnabled); -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE if (aError == kErrorNone) { - Get().UpdateOnSend(aChild); + Get().UpdateOnSend(aChild); } -#endif // A zero `nextOffset` indicates that the sent frame is an empty // frame generated by `PrepareFrameForChild()` when there was no @@ -465,7 +462,6 @@ void IndirectSender::HandleSentFrameToChild(const Mac::TxFrame &aFrame, default: OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); } if ((message != nullptr) && (nextOffset < message->GetLength())) diff --git a/src/core/thread/indirect_sender.hpp b/src/core/thread/indirect_sender.hpp index 4e5fdb9fc03..f04b54f1ce4 100644 --- a/src/core/thread/indirect_sender.hpp +++ b/src/core/thread/indirect_sender.hpp @@ -203,12 +203,6 @@ class IndirectSender : public InstanceLocator, public IndirectSenderBase, privat void HandleChildModeChange(Child &aChild, Mle::DeviceMode aOldMode); private: - /** - * Indicates whether to set/enable 15.4 ack request in the MAC header of a supervision message. - * - */ - static constexpr bool kSupervisionMsgAckRequest = (OPENTHREAD_CONFIG_CHILD_SUPERVISION_MSG_NO_ACK_REQUEST == 0); - // Callbacks from DataPollHandler Error PrepareFrameForChild(Mac::TxFrame &aFrame, FrameContext &aContext, Child &aChild); void HandleSentFrameToChild(const Mac::TxFrame &aFrame, const FrameContext &aContext, Error aError, Child &aChild); diff --git a/src/core/thread/indirect_sender_frame_context.hpp b/src/core/thread/indirect_sender_frame_context.hpp index 13d0c0d439a..9e14348b3f8 100644 --- a/src/core/thread/indirect_sender_frame_context.hpp +++ b/src/core/thread/indirect_sender_frame_context.hpp @@ -31,8 +31,8 @@ * This file includes definitions of frame context used for indirect transmission. */ -#ifndef INDIRECT_SENDER_FRAME_CONTETX_HPP_ -#define INDIRECT_SENDER_FRAME_CONTETX_HPP_ +#ifndef INDIRECT_SENDER_FRAME_CONTEXT_HPP_ +#define INDIRECT_SENDER_FRAME_CONTEXT_HPP_ #include "openthread-core-config.h" @@ -87,4 +87,4 @@ class IndirectSenderBase } // namespace ot -#endif // INDIRECT_SENDER_FRAME_CONTETX_HPP_ +#endif // INDIRECT_SENDER_FRAME_CONTEXT_HPP_ diff --git a/src/core/thread/key_manager.cpp b/src/core/thread/key_manager.cpp index 7563972ed66..ddcbf74ef0b 100644 --- a/src/core/thread/key_manager.cpp +++ b/src/core/thread/key_manager.cpp @@ -172,7 +172,7 @@ KeyManager::KeyManager(Instance &aInstance) , mHoursSinceKeyRotation(0) , mKeySwitchGuardTime(kDefaultKeySwitchGuardTime) , mKeySwitchGuardEnabled(false) - , mKeyRotationTimer(aInstance, KeyManager::HandleKeyRotationTimer) + , mKeyRotationTimer(aInstance) , mKekFrameCounter(0) , mIsPskcSet(false) { @@ -202,10 +202,7 @@ void KeyManager::Start(void) StartKeyRotationTimer(); } -void KeyManager::Stop(void) -{ - mKeyRotationTimer.Stop(); -} +void KeyManager::Stop(void) { mKeyRotationTimer.Stop(); } void KeyManager::SetPskc(const Pskc &aPskc) { @@ -241,7 +238,7 @@ void KeyManager::ResetFrameCounters(void) #if OPENTHREAD_FTD // reset router frame counters - for (Router &router : Get().Iterate()) + for (Router &router : Get()) { router.SetKeySequence(0); router.GetLinkFrameCounters().Reset(); @@ -287,7 +284,7 @@ void KeyManager::SetNetworkKey(const NetworkKey &aNetworkKey) return; } -void KeyManager::ComputeKeys(uint32_t aKeySequence, HashKeys &aHashKeys) +void KeyManager::ComputeKeys(uint32_t aKeySequence, HashKeys &aHashKeys) const { Crypto::HmacSha256 hmac; uint8_t keySequenceBytes[sizeof(uint32_t)]; @@ -309,7 +306,7 @@ void KeyManager::ComputeKeys(uint32_t aKeySequence, HashKeys &aHashKeys) } #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE -void KeyManager::ComputeTrelKey(uint32_t aKeySequence, Mac::Key &aKey) +void KeyManager::ComputeTrelKey(uint32_t aKeySequence, Mac::Key &aKey) const { Crypto::HkdfSha256 hkdf; uint8_t salt[sizeof(uint32_t) + sizeof(kHkdfExtractSaltString)]; @@ -384,7 +381,7 @@ void KeyManager::SetCurrentKeySequence(uint32_t aKeySequence) mKeySequence = aKeySequence; UpdateKeyMaterial(); - SetAllMacFrameCounters(0); + SetAllMacFrameCounters(0, /* aSetIfLarger */ false); mMleFrameCounter = 0; Get().Signal(kEventThreadKeySeqCounterChanged); @@ -415,12 +412,14 @@ const Mac::KeyMaterial &KeyManager::GetTemporaryTrelMacKey(uint32_t aKeySequence } #endif -void KeyManager::SetAllMacFrameCounters(uint32_t aMacFrameCounter) +void KeyManager::SetAllMacFrameCounters(uint32_t aFrameCounter, bool aSetIfLarger) { - mMacFrameCounters.SetAll(aMacFrameCounter); + mMacFrameCounters.SetAll(aFrameCounter); #if OPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE - Get().SetFrameCounter(aMacFrameCounter); + Get().SetFrameCounter(aFrameCounter, aSetIfLarger); +#else + OT_UNUSED_VARIABLE(aSetIfLarger); #endif } @@ -444,9 +443,7 @@ void KeyManager::MacFrameCounterUsed(uint32_t aMacFrameCounter) return; } #else -void KeyManager::MacFrameCounterUsed(uint32_t) -{ -} +void KeyManager::MacFrameCounterUsed(uint32_t) {} #endif #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE @@ -497,11 +494,6 @@ void KeyManager::StartKeyRotationTimer(void) mKeyRotationTimer.Start(kOneHourIntervalInMsec); } -void KeyManager::HandleKeyRotationTimer(Timer &aTimer) -{ - aTimer.Get().HandleKeyRotationTimer(); -} - void KeyManager::HandleKeyRotationTimer(void) { mHoursSinceKeyRotation++; @@ -642,6 +634,15 @@ void KeyManager::SetNetworkKeyRef(otNetworkKeyRef aKeyRef) return; } +void KeyManager::DestroyTemporaryKeys(void) +{ + mMleKey.Clear(); + mKek.Clear(); + Get().ClearMacKeys(); + Get().ClearMode2Key(); +} + +void KeyManager::DestroyPersistentKeys(void) { Crypto::Storage::DestroyPersistentKeys(); } #endif // OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE } // namespace ot diff --git a/src/core/thread/key_manager.hpp b/src/core/thread/key_manager.hpp index a8e2e32d801..7428301c973 100644 --- a/src/core/thread/key_manager.hpp +++ b/src/core/thread/key_manager.hpp @@ -254,7 +254,7 @@ class KeyManager : public InstanceLocator, private NonCopyable * @returns A key reference to the Thread Network Key. * */ - NetworkKeyRef GetNetworkKeyRef(void) { return mNetworkKeyRef; } + NetworkKeyRef GetNetworkKeyRef(void) const { return mNetworkKeyRef; } /** * This method sets the Thread Network Key using Key Reference. @@ -299,7 +299,7 @@ class KeyManager : public InstanceLocator, private NonCopyable * @returns A key reference to the PSKc. * */ - const PskcRef &GetPskcRef(void) { return mPskcRef; } + const PskcRef &GetPskcRef(void) const { return mPskcRef; } /** * This method sets the PSKc as a Key reference. @@ -401,10 +401,12 @@ class KeyManager : public InstanceLocator, private NonCopyable /** * This method sets the current MAC Frame Counter value for all radio links. * - * @param[in] aMacFrameCounter The MAC Frame Counter value. - * + * @param[in] aFrameCounter The MAC Frame Counter value. + * @param[in] aSetIfLarger If `true`, set only if the new value @p aFrameCounter is larger than current value. + * If `false`, set the new value independent of the current value. + */ - void SetAllMacFrameCounters(uint32_t aMacFrameCounter); + void SetAllMacFrameCounters(uint32_t aFrameCounter, bool aSetIfLarger); /** * This method sets the MAC Frame Counter value which is stored in non-volatile memory. @@ -445,7 +447,7 @@ class KeyManager : public InstanceLocator, private NonCopyable void IncrementMleFrameCounter(void); /** - * This method returns the KEK as `KekKeyMaterail` + * This method returns the KEK as `KekKeyMaterial` * * @returns The KEK as `KekKeyMaterial`. * @@ -543,11 +545,25 @@ class KeyManager : public InstanceLocator, private NonCopyable * * This is called to indicate the @p aMacFrameCounter value is now used. * - * @param[in] aMacFrameCounter The 15.4 link MAC frame counter value. + * @param[in] aMacFrameCounter The 15.4 link MAC frame counter value. * */ void MacFrameCounterUsed(uint32_t aMacFrameCounter); +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + /** + * This method destroys all the volatile mac keys stored in PSA ITS. + * + */ + void DestroyTemporaryKeys(void); + + /** + * This method destroys all the persistent keys stored in PSA ITS. + * + */ + void DestroyPersistentKeys(void); +#endif + private: static constexpr uint32_t kDefaultKeySwitchGuardTime = 624; static constexpr uint32_t kOneHourIntervalInMsec = 3600u * 1000u; @@ -569,15 +585,14 @@ class KeyManager : public InstanceLocator, private NonCopyable const Mac::Key &GetMacKey(void) const { return mKeys.mMacKey; } }; - void ComputeKeys(uint32_t aKeySequence, HashKeys &aHashKeys); + void ComputeKeys(uint32_t aKeySequence, HashKeys &aHashKeys) const; #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE - void ComputeTrelKey(uint32_t aKeySequence, Mac::Key &aKey); + void ComputeTrelKey(uint32_t aKeySequence, Mac::Key &aKey) const; #endif - void StartKeyRotationTimer(void); - static void HandleKeyRotationTimer(Timer &aTimer); - void HandleKeyRotationTimer(void); + void StartKeyRotationTimer(void); + void HandleKeyRotationTimer(void); #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE void StoreNetworkKey(const NetworkKey &aNetworkKey, bool aOverWriteExisting); @@ -586,6 +601,8 @@ class KeyManager : public InstanceLocator, private NonCopyable void ResetFrameCounters(void); + using RotationTimer = TimerMilliIn; + static const uint8_t kThreadString[]; #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE @@ -613,10 +630,10 @@ class KeyManager : public InstanceLocator, private NonCopyable uint32_t mStoredMacFrameCounter; uint32_t mStoredMleFrameCounter; - uint32_t mHoursSinceKeyRotation; - uint32_t mKeySwitchGuardTime; - bool mKeySwitchGuardEnabled; - TimerMilli mKeyRotationTimer; + uint32_t mHoursSinceKeyRotation; + uint32_t mKeySwitchGuardTime; + bool mKeySwitchGuardEnabled; + RotationTimer mKeyRotationTimer; #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE PskcRef mPskcRef; diff --git a/src/core/thread/link_metrics.cpp b/src/core/thread/link_metrics.cpp index c9c917bdd3d..cfe784c4ffe 100644 --- a/src/core/thread/link_metrics.cpp +++ b/src/core/thread/link_metrics.cpp @@ -40,6 +40,8 @@ #include "common/instance.hpp" #include "common/locator_getters.hpp" #include "common/log.hpp" +#include "common/num_utils.hpp" +#include "common/numeric_limits.hpp" #include "mac/mac.hpp" #include "thread/link_metrics_tlvs.hpp" #include "thread/neighbor_table.hpp" @@ -49,160 +51,239 @@ namespace LinkMetrics { RegisterLogModule("LinkMetrics"); -using ot::Encoding::BigEndian::HostSwap32; +static constexpr uint8_t kQueryIdSingleProbe = 0; // This query ID represents Single Probe. +static constexpr uint8_t kSeriesIdAllSeries = 255; // This series ID represents all series. -void SeriesInfo::Init(uint8_t aSeriesId, const SeriesFlags &aSeriesFlags, const Metrics &aMetrics) +// Constants for scaling Link Margin and RSSI to raw value +static constexpr uint8_t kMaxLinkMargin = 130; +static constexpr int32_t kMinRssi = -130; +static constexpr int32_t kMaxRssi = 0; + +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE + +Initiator::Initiator(Instance &aInstance) + : InstanceLocator(aInstance) { - mSeriesId = aSeriesId; - mSeriesFlags = aSeriesFlags; - mMetrics = aMetrics; - mRssAverager.Clear(); - mLqiAverager.Clear(); - mPduCount = 0; } -void SeriesInfo::AggregateLinkMetrics(uint8_t aFrameType, uint8_t aLqi, int8_t aRss) +Error Initiator::Query(const Ip6::Address &aDestination, uint8_t aSeriesId, const Metrics *aMetrics) { - if (IsFrameTypeMatch(aFrameType)) + Error error; + Neighbor *neighbor; + QueryInfo info; + + SuccessOrExit(error = FindNeighbor(aDestination, neighbor)); + + info.Clear(); + info.mSeriesId = aSeriesId; + + if (aMetrics != nullptr) { - mPduCount++; - mLqiAverager.Add(aLqi); - IgnoreError(mRssAverager.Add(aRss)); + info.mTypeIdCount = aMetrics->ConvertToTypeIds(info.mTypeIds); } -} -bool SeriesInfo::IsFrameTypeMatch(uint8_t aFrameType) const -{ - bool match = false; - - switch (aFrameType) + if (aSeriesId != 0) { - case kSeriesTypeLinkProbe: - VerifyOrExit(!mSeriesFlags.IsMacDataFlagSet()); // Ignore this when Mac Data is accounted - match = mSeriesFlags.IsLinkProbeFlagSet(); - break; - case Mac::Frame::kFcfFrameData: - match = mSeriesFlags.IsMacDataFlagSet(); - break; - case Mac::Frame::kFcfFrameMacCmd: - match = mSeriesFlags.IsMacDataRequestFlagSet(); - break; - case Mac::Frame::kFcfFrameAck: - match = mSeriesFlags.IsMacAckFlagSet(); - break; - default: - break; + VerifyOrExit(info.mTypeIdCount == 0, error = kErrorInvalidArgs); } + error = Get().SendDataRequestForLinkMetricsReport(aDestination, info); + exit: - return match; + return error; } -LinkMetrics::LinkMetrics(Instance &aInstance) - : InstanceLocator(aInstance) - , mReportCallback(nullptr) - , mReportCallbackContext(nullptr) - , mMgmtResponseCallback(nullptr) - , mMgmtResponseCallbackContext(nullptr) - , mEnhAckProbingIeReportCallback(nullptr) - , mEnhAckProbingIeReportCallbackContext(nullptr) +Error Initiator::AppendLinkMetricsQueryTlv(Message &aMessage, const QueryInfo &aInfo) { -} + Error error = kErrorNone; + Tlv tlv; -Error LinkMetrics::Query(const Ip6::Address &aDestination, uint8_t aSeriesId, const Metrics *aMetrics) -{ - Error error; - TypeIdFlags typeIdFlags[kMaxTypeIdFlags]; - uint8_t typeIdFlagsCount = 0; - Neighbor * neighbor = GetNeighborFromLinkLocalAddr(aDestination); + // The MLE Link Metrics Query TLV has two sub-TLVs: + // - Query ID sub-TLV with series ID as value. + // - Query Options sub-TLV with Type IDs as value. - VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor); - VerifyOrExit(neighbor->IsThreadVersion1p2OrHigher(), error = kErrorNotCapable); + tlv.SetType(Mle::Tlv::kLinkMetricsQuery); + tlv.SetLength(sizeof(Tlv) + sizeof(uint8_t) + ((aInfo.mTypeIdCount == 0) ? 0 : (sizeof(Tlv) + aInfo.mTypeIdCount))); - if (aMetrics != nullptr) - { - typeIdFlagsCount = TypeIdFlagsFromMetrics(typeIdFlags, *aMetrics); - } + SuccessOrExit(error = aMessage.Append(tlv)); - if (aSeriesId != 0) + SuccessOrExit(error = Tlv::Append(aMessage, aInfo.mSeriesId)); + + if (aInfo.mTypeIdCount != 0) { - VerifyOrExit(typeIdFlagsCount == 0, error = kErrorInvalidArgs); - } + QueryOptionsSubTlv queryOptionsTlv; - error = SendLinkMetricsQuery(aDestination, aSeriesId, typeIdFlags, typeIdFlagsCount); + queryOptionsTlv.Init(); + queryOptionsTlv.SetLength(aInfo.mTypeIdCount); + SuccessOrExit(error = aMessage.Append(queryOptionsTlv)); + SuccessOrExit(error = aMessage.AppendBytes(aInfo.mTypeIds, aInfo.mTypeIdCount)); + } exit: return error; } -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE -Error LinkMetrics::SendMgmtRequestForwardTrackingSeries(const Ip6::Address & aDestination, - uint8_t aSeriesId, - const SeriesFlags::Info &aSeriesFlags, - const Metrics * aMetrics) +void Initiator::HandleReport(const Message &aMessage, uint16_t aOffset, uint16_t aLength, const Ip6::Address &aAddress) { - Error error = kErrorNone; - uint8_t subTlvs[sizeof(Tlv) + sizeof(uint8_t) * 2 + sizeof(TypeIdFlags) * kMaxTypeIdFlags]; - Tlv * fwdProbingSubTlv = reinterpret_cast(subTlvs); - SeriesFlags *seriesFlags = reinterpret_cast(subTlvs + sizeof(Tlv) + sizeof(aSeriesId)); - uint8_t typeIdFlagsOffset = sizeof(Tlv) + sizeof(uint8_t) * 2; - uint8_t typeIdFlagsCount = 0; - Neighbor * neighbor = GetNeighborFromLinkLocalAddr(aDestination); + Error error = kErrorNone; + uint16_t offset = aOffset; + uint16_t endOffset = aOffset + aLength; + bool hasStatus = false; + bool hasReport = false; + Tlv tlv; + ReportSubTlv reportTlv; + MetricsValues values; + uint8_t status; + uint8_t typeId; - VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor); - VerifyOrExit(neighbor->IsThreadVersion1p2OrHigher(), error = kErrorNotCapable); + OT_UNUSED_VARIABLE(error); - // Directly transform `aMetrics` into TypeIdFlags and put them into `subTlvs` - if (aMetrics != nullptr) + VerifyOrExit(mReportCallback.IsSet()); + + values.Clear(); + + while (offset < endOffset) { - typeIdFlagsCount = - TypeIdFlagsFromMetrics(reinterpret_cast(subTlvs + typeIdFlagsOffset), *aMetrics); + SuccessOrExit(error = aMessage.Read(offset, tlv)); + + VerifyOrExit(offset + sizeof(Tlv) + tlv.GetLength() <= endOffset, error = kErrorParse); + + // The report must contain either: + // - One or more Report Sub-TLVs (in case of success), or + // - A single Status Sub-TLV (in case of failure). + + switch (tlv.GetType()) + { + case StatusSubTlv::kType: + VerifyOrExit(!hasStatus && !hasReport, error = kErrorDrop); + SuccessOrExit(error = Tlv::Read(aMessage, offset, status)); + hasStatus = true; + break; + + case ReportSubTlv::kType: + VerifyOrExit(!hasStatus, error = kErrorDrop); + + // Read the report sub-TLV assuming minimum length + SuccessOrExit(error = aMessage.Read(offset, &reportTlv, sizeof(Tlv) + ReportSubTlv::kMinLength)); + VerifyOrExit(reportTlv.IsValid(), error = kErrorParse); + hasReport = true; + + typeId = reportTlv.GetMetricsTypeId(); + + if (TypeId::IsExtended(typeId)) + { + // Skip the sub-TLV if `E` flag is set. + break; + } + + if (TypeId::GetValueLength(typeId) > sizeof(uint8_t)) + { + // If Type ID indicates metric value has 4 bytes length, we + // read the full `reportTlv`. + SuccessOrExit(error = aMessage.Read(offset, reportTlv)); + } + + switch (typeId) + { + case TypeId::kPdu: + values.mMetrics.mPduCount = true; + values.mPduCountValue = reportTlv.GetMetricsValue32(); + LogDebg(" - PDU Counter: %lu (Count/Summation)", ToUlong(values.mPduCountValue)); + break; + + case TypeId::kLqi: + values.mMetrics.mLqi = true; + values.mLqiValue = reportTlv.GetMetricsValue8(); + LogDebg(" - LQI: %u (Exponential Moving Average)", values.mLqiValue); + break; + + case TypeId::kLinkMargin: + values.mMetrics.mLinkMargin = true; + values.mLinkMarginValue = ScaleRawValueToLinkMargin(reportTlv.GetMetricsValue8()); + LogDebg(" - Margin: %u (dB) (Exponential Moving Average)", values.mLinkMarginValue); + break; + + case TypeId::kRssi: + values.mMetrics.mRssi = true; + values.mRssiValue = ScaleRawValueToRssi(reportTlv.GetMetricsValue8()); + LogDebg(" - RSSI: %u (dBm) (Exponential Moving Average)", values.mRssiValue); + break; + } + + break; + } + + offset += sizeof(Tlv) + tlv.GetLength(); } - VerifyOrExit(aSeriesId > kQueryIdSingleProbe, error = kErrorInvalidArgs); + VerifyOrExit(hasStatus || hasReport); + + mReportCallback.Invoke(&aAddress, hasStatus ? nullptr : &values, + hasStatus ? static_cast(status) : kStatusSuccess); + +exit: + LogDebg("HandleReport, error:%s", ErrorToString(error)); +} + +Error Initiator::SendMgmtRequestForwardTrackingSeries(const Ip6::Address &aDestination, + uint8_t aSeriesId, + const SeriesFlags &aSeriesFlags, + const Metrics *aMetrics) +{ + Error error; + Neighbor *neighbor; + uint8_t typeIdCount = 0; + FwdProbingRegSubTlv fwdProbingSubTlv; + + SuccessOrExit(error = FindNeighbor(aDestination, neighbor)); - fwdProbingSubTlv->SetType(SubTlv::kFwdProbingReg); + VerifyOrExit(aSeriesId > kQueryIdSingleProbe, error = kErrorInvalidArgs); - // SeriesId + SeriesFlags + typeIdFlagsCount * TypeIdFlags - fwdProbingSubTlv->SetLength(sizeof(uint8_t) * 2 + sizeof(TypeIdFlags) * typeIdFlagsCount); + fwdProbingSubTlv.Init(); + fwdProbingSubTlv.SetSeriesId(aSeriesId); + fwdProbingSubTlv.SetSeriesFlagsMask(aSeriesFlags.ConvertToMask()); - memcpy(subTlvs + sizeof(Tlv), &aSeriesId, sizeof(aSeriesId)); + if (aMetrics != nullptr) + { + typeIdCount = aMetrics->ConvertToTypeIds(fwdProbingSubTlv.GetTypeIds()); + } - seriesFlags->SetFrom(aSeriesFlags); + fwdProbingSubTlv.SetLength(sizeof(aSeriesId) + sizeof(uint8_t) + typeIdCount); - error = Get().SendLinkMetricsManagementRequest(aDestination, subTlvs, fwdProbingSubTlv->GetSize()); + error = Get().SendLinkMetricsManagementRequest(aDestination, fwdProbingSubTlv); exit: LogDebg("SendMgmtRequestForwardTrackingSeries, error:%s, Series ID:%u", ErrorToString(error), aSeriesId); return error; } -Error LinkMetrics::SendMgmtRequestEnhAckProbing(const Ip6::Address &aDestination, - const EnhAckFlags aEnhAckFlags, - const Metrics * aMetrics) +Error Initiator::SendMgmtRequestEnhAckProbing(const Ip6::Address &aDestination, + EnhAckFlags aEnhAckFlags, + const Metrics *aMetrics) { - Error error = kErrorNone; + Error error; + Neighbor *neighbor; + uint8_t typeIdCount = 0; EnhAckConfigSubTlv enhAckConfigSubTlv; - Mac::Address macAddress; - Neighbor * neighbor = GetNeighborFromLinkLocalAddr(aDestination); - VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor); - VerifyOrExit(neighbor->IsThreadVersion1p2OrHigher(), error = kErrorNotCapable); + SuccessOrExit(error = FindNeighbor(aDestination, neighbor)); if (aEnhAckFlags == kEnhAckClear) { VerifyOrExit(aMetrics == nullptr, error = kErrorInvalidArgs); } + enhAckConfigSubTlv.Init(); enhAckConfigSubTlv.SetEnhAckFlags(aEnhAckFlags); if (aMetrics != nullptr) { - enhAckConfigSubTlv.SetTypeIdFlags(*aMetrics); + typeIdCount = aMetrics->ConvertToTypeIds(enhAckConfigSubTlv.GetTypeIds()); } - error = Get().SendLinkMetricsManagementRequest( - aDestination, reinterpret_cast(&enhAckConfigSubTlv), enhAckConfigSubTlv.GetSize()); + enhAckConfigSubTlv.SetLength(EnhAckConfigSubTlv::kMinLength + typeIdCount); + + error = Get().SendLinkMetricsManagementRequest(aDestination, enhAckConfigSubTlv); if (aMetrics != nullptr) { @@ -220,45 +301,142 @@ Error LinkMetrics::SendMgmtRequestEnhAckProbing(const Ip6::Address &aDestination return error; } -Error LinkMetrics::SendLinkProbe(const Ip6::Address &aDestination, uint8_t aSeriesId, uint8_t aLength) +Error Initiator::HandleManagementResponse(const Message &aMessage, const Ip6::Address &aAddress) +{ + Error error = kErrorNone; + uint16_t offset; + uint16_t endOffset; + uint16_t length; + uint8_t status; + bool hasStatus = false; + + VerifyOrExit(mMgmtResponseCallback.IsSet()); + + SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Mle::Tlv::Type::kLinkMetricsManagement, offset, length)); + endOffset = offset + length; + + while (offset < endOffset) + { + Tlv tlv; + + SuccessOrExit(error = aMessage.Read(offset, tlv)); + + switch (tlv.GetType()) + { + case StatusSubTlv::kType: + VerifyOrExit(!hasStatus, error = kErrorParse); + SuccessOrExit(error = Tlv::Read(aMessage, offset, status)); + hasStatus = true; + break; + + default: + break; + } + + offset += sizeof(Tlv) + tlv.GetLength(); + } + + VerifyOrExit(hasStatus, error = kErrorParse); + + mMgmtResponseCallback.Invoke(&aAddress, status); + +exit: + return error; +} + +Error Initiator::SendLinkProbe(const Ip6::Address &aDestination, uint8_t aSeriesId, uint8_t aLength) { - Error error = kErrorNone; + Error error; uint8_t buf[kLinkProbeMaxLen]; - Neighbor *neighbor = GetNeighborFromLinkLocalAddr(aDestination); + Neighbor *neighbor; - VerifyOrExit(neighbor != nullptr, error = kErrorUnknownNeighbor); - VerifyOrExit(neighbor->IsThreadVersion1p2OrHigher(), error = kErrorNotCapable); + SuccessOrExit(error = FindNeighbor(aDestination, neighbor)); - VerifyOrExit(aLength <= LinkMetrics::kLinkProbeMaxLen && aSeriesId != kQueryIdSingleProbe && - aSeriesId != kSeriesIdAllSeries, + VerifyOrExit(aLength <= kLinkProbeMaxLen && aSeriesId != kQueryIdSingleProbe && aSeriesId != kSeriesIdAllSeries, error = kErrorInvalidArgs); - error = Get().SendLinkProbe(aDestination, aSeriesId, buf, aLength); + error = Get().SendLinkProbe(aDestination, aSeriesId, buf, aLength); exit: LogDebg("SendLinkProbe, error:%s, Series ID:%u", ErrorToString(error), aSeriesId); return error; } + +void Initiator::ProcessEnhAckIeData(const uint8_t *aData, uint8_t aLength, const Neighbor &aNeighbor) +{ + MetricsValues values; + uint8_t idx = 0; + + VerifyOrExit(mEnhAckProbingIeReportCallback.IsSet()); + + values.SetMetrics(aNeighbor.GetEnhAckProbingMetrics()); + + if (values.GetMetrics().mLqi && idx < aLength) + { + values.mLqiValue = aData[idx++]; + } + if (values.GetMetrics().mLinkMargin && idx < aLength) + { + values.mLinkMarginValue = ScaleRawValueToLinkMargin(aData[idx++]); + } + if (values.GetMetrics().mRssi && idx < aLength) + { + values.mRssiValue = ScaleRawValueToRssi(aData[idx++]); + } + + mEnhAckProbingIeReportCallback.Invoke(aNeighbor.GetRloc16(), &aNeighbor.GetExtAddress(), &values); + +exit: + return; +} + +Error Initiator::FindNeighbor(const Ip6::Address &aDestination, Neighbor *&aNeighbor) +{ + Error error = kErrorUnknownNeighbor; + Mac::Address macAddress; + + aNeighbor = nullptr; + + VerifyOrExit(aDestination.IsLinkLocal()); + aDestination.GetIid().ConvertToMacAddress(macAddress); + + aNeighbor = Get().FindNeighbor(macAddress); + VerifyOrExit(aNeighbor != nullptr); + + VerifyOrExit(aNeighbor->GetVersion() >= kThreadVersion1p2, error = kErrorNotCapable); + error = kErrorNone; + +exit: + return error; +} + #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE -Error LinkMetrics::AppendReport(Message &aMessage, const Message &aRequestMessage, Neighbor &aNeighbor) +Subject::Subject(Instance &aInstance) + : InstanceLocator(aInstance) +{ +} + +Error Subject::AppendReport(Message &aMessage, const Message &aRequestMessage, Neighbor &aNeighbor) { Error error = kErrorNone; Tlv tlv; uint8_t queryId; - bool hasQueryId = false; - uint8_t length = 0; - uint16_t startOffset = aMessage.GetLength(); + bool hasQueryId = false; + uint16_t length; uint16_t offset; uint16_t endOffset; MetricsValues values; values.Clear(); - SuccessOrExit(error = Tlv::FindTlvValueOffset(aRequestMessage, Mle::Tlv::Type::kLinkMetricsQuery, offset, - endOffset)); // `endOffset` is used to store tlv length here + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Parse MLE Link Metrics Query TLV and its sub-TLVs from + // `aRequestMessage`. - endOffset = offset + endOffset; + SuccessOrExit(error = Tlv::FindTlvValueOffset(aRequestMessage, Mle::Tlv::Type::kLinkMetricsQuery, offset, length)); + + endOffset = offset + length; while (offset < endOffset) { @@ -272,36 +450,35 @@ Error LinkMetrics::AppendReport(Message &aMessage, const Message &aRequestMessag break; case SubTlv::kQueryOptions: - SuccessOrExit(error = ReadTypeIdFlagsFromMessage(aRequestMessage, offset + sizeof(tlv), - static_cast(offset + tlv.GetSize()), - values.GetMetrics())); + SuccessOrExit(error = ReadTypeIdsFromMessage(aRequestMessage, offset + sizeof(tlv), + static_cast(offset + tlv.GetSize()), + values.GetMetrics())); break; default: break; } - offset += tlv.GetSize(); + offset += static_cast(tlv.GetSize()); } VerifyOrExit(hasQueryId, error = kErrorParse); - // Link Metrics Report TLV + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Append MLE Link Metrics Report TLV and its sub-TLVs to + // `aMessage`. + + offset = aMessage.GetLength(); tlv.SetType(Mle::Tlv::kLinkMetricsReport); SuccessOrExit(error = aMessage.Append(tlv)); if (queryId == kQueryIdSingleProbe) { - values.mPduCountValue = HostSwap32(aRequestMessage.GetPsduCount()); - values.mLqiValue = aRequestMessage.GetAverageLqi(); - // Linearly scale Link Margin from [0, 130] to [0, 255] - values.mLinkMarginValue = - LinkQualityInfo::ConvertRssToLinkMargin(Get().GetNoiseFloor(), aRequestMessage.GetAverageRss()) * - 255 / 130; - // Linearly scale rss from [-130, 0] to [0, 255] - values.mRssiValue = (aRequestMessage.GetAverageRss() + 130) * 255 / 130; - - SuccessOrExit(error = AppendReportSubTlvToMessage(aMessage, length, values)); + values.mPduCountValue = aRequestMessage.GetPsduCount(); + values.mLqiValue = aRequestMessage.GetAverageLqi(); + values.mLinkMarginValue = Get().ComputeLinkMargin(aRequestMessage.GetAverageRss()); + values.mRssiValue = aRequestMessage.GetAverageRss(); + SuccessOrExit(error = AppendReportSubTlvToMessage(aMessage, values)); } else { @@ -309,380 +486,224 @@ Error LinkMetrics::AppendReport(Message &aMessage, const Message &aRequestMessag if (seriesInfo == nullptr) { - SuccessOrExit(error = AppendStatusSubTlvToMessage(aMessage, length, kStatusSeriesIdNotRecognized)); + SuccessOrExit(error = Tlv::Append(aMessage, kStatusSeriesIdNotRecognized)); } else if (seriesInfo->GetPduCount() == 0) { - SuccessOrExit(error = AppendStatusSubTlvToMessage(aMessage, length, kStatusNoMatchingFramesReceived)); + SuccessOrExit(error = Tlv::Append(aMessage, kStatusNoMatchingFramesReceived)); } else { values.SetMetrics(seriesInfo->GetLinkMetrics()); - values.mPduCountValue = HostSwap32(seriesInfo->GetPduCount()); - values.mLqiValue = seriesInfo->GetAverageLqi(); - // Linearly scale Link Margin from [0, 130] to [0, 255] - values.mLinkMarginValue = - LinkQualityInfo::ConvertRssToLinkMargin(Get().GetNoiseFloor(), seriesInfo->GetAverageRss()) * - 255 / 130; - // Linearly scale RSSI from [-130, 0] to [0, 255] - values.mRssiValue = (seriesInfo->GetAverageRss() + 130) * 255 / 130; - SuccessOrExit(error = AppendReportSubTlvToMessage(aMessage, length, values)); + values.mPduCountValue = seriesInfo->GetPduCount(); + values.mLqiValue = seriesInfo->GetAverageLqi(); + values.mLinkMarginValue = Get().ComputeLinkMargin(seriesInfo->GetAverageRss()); + values.mRssiValue = seriesInfo->GetAverageRss(); + SuccessOrExit(error = AppendReportSubTlvToMessage(aMessage, values)); } } - tlv.SetLength(length); - aMessage.Write(startOffset, tlv); + // Update the TLV length in message. + length = aMessage.GetLength() - offset - sizeof(Tlv); + tlv.SetLength(static_cast(length)); + aMessage.Write(offset, tlv); exit: LogDebg("AppendReport, error:%s", ErrorToString(error)); return error; } -Error LinkMetrics::HandleManagementRequest(const Message &aMessage, Neighbor &aNeighbor, Status &aStatus) +Error Subject::HandleManagementRequest(const Message &aMessage, Neighbor &aNeighbor, Status &aStatus) { - Error error = kErrorNone; - Tlv tlv; - uint8_t seriesId; - SeriesFlags seriesFlags; - EnhAckFlags enhAckFlags; - Metrics metrics; - bool hasForwardProbingRegistrationTlv = false; - bool hasEnhAckProbingTlv = false; - uint16_t offset; - uint16_t length; - uint16_t index = 0; + Error error = kErrorNone; + uint16_t offset; + uint16_t endOffset; + uint16_t tlvEndOffset; + uint16_t length; + FwdProbingRegSubTlv fwdProbingSubTlv; + EnhAckConfigSubTlv enhAckConfigSubTlv; + Metrics metrics; SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Mle::Tlv::Type::kLinkMetricsManagement, offset, length)); + endOffset = offset + length; + + // Set sub-TLV lengths to zero to indicate that we have + // not yet seen them in the message. + fwdProbingSubTlv.SetLength(0); + enhAckConfigSubTlv.SetLength(0); - while (index < length) + for (; offset < endOffset; offset = tlvEndOffset) { - uint16_t pos = offset + index; + Tlv tlv; + uint16_t minTlvSize; + Tlv *subTlv; - SuccessOrExit(aMessage.Read(pos, tlv)); + SuccessOrExit(error = aMessage.Read(offset, tlv)); - pos += sizeof(tlv); + VerifyOrExit(offset + tlv.GetSize() <= endOffset, error = kErrorParse); + tlvEndOffset = static_cast(offset + tlv.GetSize()); switch (tlv.GetType()) { case SubTlv::kFwdProbingReg: - VerifyOrExit(!hasForwardProbingRegistrationTlv && !hasEnhAckProbingTlv, error = kErrorParse); - VerifyOrExit(tlv.GetLength() >= sizeof(seriesId) + sizeof(seriesFlags), error = kErrorParse); - SuccessOrExit(aMessage.Read(pos, seriesId)); - pos += sizeof(seriesId); - SuccessOrExit(aMessage.Read(pos, seriesFlags)); - pos += sizeof(seriesFlags); - SuccessOrExit(error = ReadTypeIdFlagsFromMessage( - aMessage, pos, static_cast(offset + index + tlv.GetSize()), metrics)); - hasForwardProbingRegistrationTlv = true; + subTlv = &fwdProbingSubTlv; + minTlvSize = sizeof(Tlv) + FwdProbingRegSubTlv::kMinLength; break; case SubTlv::kEnhAckConfig: - VerifyOrExit(!hasForwardProbingRegistrationTlv && !hasEnhAckProbingTlv, error = kErrorParse); - VerifyOrExit(tlv.GetLength() >= sizeof(EnhAckFlags), error = kErrorParse); - SuccessOrExit(aMessage.Read(pos, enhAckFlags)); - pos += sizeof(enhAckFlags); - SuccessOrExit(error = ReadTypeIdFlagsFromMessage( - aMessage, pos, static_cast(offset + index + tlv.GetSize()), metrics)); - hasEnhAckProbingTlv = true; + subTlv = &enhAckConfigSubTlv; + minTlvSize = sizeof(Tlv) + EnhAckConfigSubTlv::kMinLength; break; default: - break; + continue; } - index += tlv.GetSize(); + // Ensure message contains only one sub-TLV. + VerifyOrExit(fwdProbingSubTlv.GetLength() == 0, error = kErrorParse); + VerifyOrExit(enhAckConfigSubTlv.GetLength() == 0, error = kErrorParse); + + VerifyOrExit(tlv.GetSize() >= minTlvSize, error = kErrorParse); + + // Read `subTlv` with its `minTlvSize`, followed by the Type IDs. + SuccessOrExit(error = aMessage.Read(offset, subTlv, minTlvSize)); + SuccessOrExit(error = ReadTypeIdsFromMessage(aMessage, offset + minTlvSize, tlvEndOffset, metrics)); } - if (hasForwardProbingRegistrationTlv) + if (fwdProbingSubTlv.GetLength() != 0) { - aStatus = ConfigureForwardTrackingSeries(seriesId, seriesFlags, metrics, aNeighbor); + aStatus = ConfigureForwardTrackingSeries(fwdProbingSubTlv.GetSeriesId(), fwdProbingSubTlv.GetSeriesFlagsMask(), + metrics, aNeighbor); } - else if (hasEnhAckProbingTlv) + + if (enhAckConfigSubTlv.GetLength() != 0) { - aStatus = ConfigureEnhAckProbing(enhAckFlags, metrics, aNeighbor); + aStatus = ConfigureEnhAckProbing(enhAckConfigSubTlv.GetEnhAckFlags(), metrics, aNeighbor); } exit: return error; } -#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE -Error LinkMetrics::HandleManagementResponse(const Message &aMessage, const Ip6::Address &aAddress) +Error Subject::HandleLinkProbe(const Message &aMessage, uint8_t &aSeriesId) { Error error = kErrorNone; - Tlv tlv; uint16_t offset; uint16_t length; - uint16_t index = 0; - Status status; - bool hasStatus = false; - - VerifyOrExit(mMgmtResponseCallback != nullptr); - - SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Mle::Tlv::Type::kLinkMetricsManagement, offset, length)); - - while (index < length) - { - SuccessOrExit(aMessage.Read(offset + index, tlv)); - - switch (tlv.GetType()) - { - case SubTlv::kStatus: - VerifyOrExit(!hasStatus, error = kErrorParse); - VerifyOrExit(tlv.GetLength() == sizeof(status), error = kErrorParse); - SuccessOrExit(aMessage.Read(offset + index + sizeof(tlv), status)); - hasStatus = true; - break; - - default: - break; - } - - index += tlv.GetSize(); - } - - VerifyOrExit(hasStatus, error = kErrorParse); - mMgmtResponseCallback(&aAddress, status, mMgmtResponseCallbackContext); + SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Mle::Tlv::Type::kLinkProbe, offset, length)); + VerifyOrExit(length >= sizeof(aSeriesId), error = kErrorParse); + error = aMessage.Read(offset, aSeriesId); exit: return error; } -void LinkMetrics::HandleReport(const Message & aMessage, - uint16_t aOffset, - uint16_t aLength, - const Ip6::Address &aAddress) +Error Subject::AppendReportSubTlvToMessage(Message &aMessage, const MetricsValues &aValues) { - Error error = kErrorNone; - MetricsValues values; - uint8_t rawValue; - uint16_t pos = aOffset; - uint16_t endPos = aOffset + aLength; - Tlv tlv; - TypeIdFlags typeIdFlags; - bool hasStatus = false; - bool hasReport = false; - Status status; - - OT_UNUSED_VARIABLE(error); + Error error = kErrorNone; + ReportSubTlv reportTlv; - VerifyOrExit(mReportCallback != nullptr); + reportTlv.Init(); - values.Clear(); - - while (pos < endPos) + if (aValues.mMetrics.mPduCount) { - SuccessOrExit(aMessage.Read(pos, tlv)); - VerifyOrExit(tlv.GetType() == SubTlv::kReport); - pos += sizeof(Tlv); - - VerifyOrExit(pos + tlv.GetLength() <= endPos, error = kErrorParse); - - switch (tlv.GetType()) - { - case SubTlv::kStatus: - // There should be either: one Status TLV or some Report-Sub TLVs - VerifyOrExit(!hasStatus && !hasReport, error = kErrorDrop); - VerifyOrExit(tlv.GetLength() == sizeof(status), error = kErrorParse); - SuccessOrExit(aMessage.Read(pos, status)); - hasStatus = true; - pos += sizeof(status); - break; - - case SubTlv::kReport: - // There shouldn't be any Report-Sub TLV when there's a Status TLV - VerifyOrExit(!hasStatus, error = kErrorDrop); - VerifyOrExit(tlv.GetLength() > sizeof(typeIdFlags), error = kErrorParse); - SuccessOrExit(aMessage.Read(pos, typeIdFlags)); - - if (typeIdFlags.IsExtendedFlagSet()) - { - pos += tlv.GetLength(); // Skip the whole sub-TLV if `E` flag is set - continue; - } - - hasReport = true; - pos += sizeof(TypeIdFlags); - - switch (typeIdFlags.GetRawValue()) - { - case TypeIdFlags::kPdu: - values.GetMetrics().mPduCount = true; - SuccessOrExit(aMessage.Read(pos, values.mPduCountValue)); - values.mPduCountValue = HostSwap32(values.mPduCountValue); - pos += sizeof(uint32_t); - LogDebg(" - PDU Counter: %d (Count/Summation)", values.mPduCountValue); - break; - - case TypeIdFlags::kLqi: - values.GetMetrics().mLqi = true; - SuccessOrExit(aMessage.Read(pos, values.mLqiValue)); - pos += sizeof(uint8_t); - LogDebg(" - LQI: %d (Exponential Moving Average)", values.mLqiValue); - break; - - case TypeIdFlags::kLinkMargin: - values.GetMetrics().mLinkMargin = true; - SuccessOrExit(aMessage.Read(pos, rawValue)); - // Reverse operation for linear scale, map from [0, 255] to [0, 130] - values.mLinkMarginValue = rawValue * 130 / 255; - pos += sizeof(uint8_t); - LogDebg(" - Margin: %d (dB) (Exponential Moving Average)", values.mLinkMarginValue); - break; - - case TypeIdFlags::kRssi: - values.GetMetrics().mRssi = true; - SuccessOrExit(aMessage.Read(pos, rawValue)); - // Reverse operation for linear scale, map from [0, 255] to [-130, 0] - values.mRssiValue = rawValue * 130 / 255 - 130; - pos += sizeof(uint8_t); - LogDebg(" - RSSI: %d (dBm) (Exponential Moving Average)", values.mRssiValue); - break; - - default: - break; - } - break; - } + reportTlv.SetMetricsTypeId(TypeId::kPdu); + reportTlv.SetMetricsValue32(aValues.mPduCountValue); + SuccessOrExit(error = reportTlv.AppendTo(aMessage)); } - if (hasStatus) + if (aValues.mMetrics.mLqi) { - mReportCallback(&aAddress, nullptr, status, mReportCallbackContext); + reportTlv.SetMetricsTypeId(TypeId::kLqi); + reportTlv.SetMetricsValue8(aValues.mLqiValue); + SuccessOrExit(error = reportTlv.AppendTo(aMessage)); } - else if (hasReport) + + if (aValues.mMetrics.mLinkMargin) { - mReportCallback(&aAddress, &values, OT_LINK_METRICS_STATUS_SUCCESS, mReportCallbackContext); + reportTlv.SetMetricsTypeId(TypeId::kLinkMargin); + reportTlv.SetMetricsValue8(ScaleLinkMarginToRawValue(aValues.mLinkMarginValue)); + SuccessOrExit(error = reportTlv.AppendTo(aMessage)); } -exit: - LogDebg("HandleReport, error:%s", ErrorToString(error)); - return; -} - -Error LinkMetrics::HandleLinkProbe(const Message &aMessage, uint8_t &aSeriesId) -{ - Error error = kErrorNone; - uint16_t offset; - uint16_t length; - - SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Mle::Tlv::Type::kLinkProbe, offset, length)); - VerifyOrExit(length >= sizeof(aSeriesId), error = kErrorParse); - error = aMessage.Read(offset, aSeriesId); + if (aValues.mMetrics.mRssi) + { + reportTlv.SetMetricsTypeId(TypeId::kRssi); + reportTlv.SetMetricsValue8(ScaleRssiToRawValue(aValues.mRssiValue)); + SuccessOrExit(error = reportTlv.AppendTo(aMessage)); + } exit: return error; } -void LinkMetrics::SetReportCallback(ReportCallback aCallback, void *aContext) -{ - mReportCallback = aCallback; - mReportCallbackContext = aContext; -} +void Subject::Free(SeriesInfo &aSeriesInfo) { mSeriesInfoPool.Free(aSeriesInfo); } -void LinkMetrics::SetMgmtResponseCallback(MgmtResponseCallback aCallback, void *aContext) +Error Subject::ReadTypeIdsFromMessage(const Message &aMessage, + uint16_t aStartOffset, + uint16_t aEndOffset, + Metrics &aMetrics) { - mMgmtResponseCallback = aCallback; - mMgmtResponseCallbackContext = aContext; -} - -void LinkMetrics::SetEnhAckProbingCallback(EnhAckProbingIeReportCallback aCallback, void *aContext) -{ - mEnhAckProbingIeReportCallback = aCallback; - mEnhAckProbingIeReportCallbackContext = aContext; -} - -void LinkMetrics::ProcessEnhAckIeData(const uint8_t *aData, uint8_t aLength, const Neighbor &aNeighbor) -{ - MetricsValues values; - uint8_t idx = 0; - - VerifyOrExit(mEnhAckProbingIeReportCallback != nullptr); + Error error = kErrorNone; - values.SetMetrics(aNeighbor.GetEnhAckProbingMetrics()); + aMetrics.Clear(); - if (values.GetMetrics().mLqi && idx < aLength) - { - values.mLqiValue = aData[idx++]; - } - if (values.GetMetrics().mLinkMargin && idx < aLength) + for (uint16_t offset = aStartOffset; offset < aEndOffset; offset++) { - // Reverse operation for linear scale, map from [0, 255] to [0, 130] - values.mLinkMarginValue = aData[idx++] * 130 / 255; - } - if (values.GetMetrics().mRssi && idx < aLength) - { - // Reverse operation for linear scale, map from [0, 255] to [-130, 0] - values.mRssiValue = aData[idx++] * 130 / 255 - 130; - } + uint8_t typeId; - mEnhAckProbingIeReportCallback(aNeighbor.GetRloc16(), &aNeighbor.GetExtAddress(), &values, - mEnhAckProbingIeReportCallbackContext); + SuccessOrExit(aMessage.Read(offset, typeId)); -exit: - return; -} + switch (typeId) + { + case TypeId::kPdu: + VerifyOrExit(!aMetrics.mPduCount, error = kErrorParse); + aMetrics.mPduCount = true; + break; -Error LinkMetrics::SendLinkMetricsQuery(const Ip6::Address &aDestination, - uint8_t aSeriesId, - const TypeIdFlags * aTypeIdFlags, - uint8_t aTypeIdFlagsCount) -{ - // LinkMetricsQuery Tlv + LinkMetricsQueryId sub-TLV (value-length: 1 byte) + - // LinkMetricsQueryOptions sub-TLV (value-length: `kMaxTypeIdFlags` bytes) - constexpr uint16_t kBufferSize = sizeof(Tlv) * 3 + sizeof(uint8_t) + sizeof(TypeIdFlags) * kMaxTypeIdFlags; - - Error error = kErrorNone; - QueryOptionsSubTlv queryOptionsTlv; - uint8_t length = 0; - static const uint8_t tlvs[] = {Mle::Tlv::kLinkMetricsReport}; - uint8_t buf[kBufferSize]; - Tlv * tlv = reinterpret_cast(buf); - Tlv subTlv; - - // Link Metrics Query TLV - tlv->SetType(Mle::Tlv::kLinkMetricsQuery); - length += sizeof(Tlv); - - // Link Metrics Query ID sub-TLV - subTlv.SetType(SubTlv::kQueryId); - subTlv.SetLength(sizeof(uint8_t)); - memcpy(buf + length, &subTlv, sizeof(subTlv)); - length += sizeof(subTlv); - memcpy(buf + length, &aSeriesId, sizeof(aSeriesId)); - length += sizeof(aSeriesId); - - // Link Metrics Query Options sub-TLV - if (aTypeIdFlagsCount > 0) - { - queryOptionsTlv.Init(); - queryOptionsTlv.SetLength(aTypeIdFlagsCount * sizeof(TypeIdFlags)); + case TypeId::kLqi: + VerifyOrExit(!aMetrics.mLqi, error = kErrorParse); + aMetrics.mLqi = true; + break; - memcpy(buf + length, &queryOptionsTlv, sizeof(queryOptionsTlv)); - length += sizeof(queryOptionsTlv); - memcpy(buf + length, aTypeIdFlags, queryOptionsTlv.GetLength()); - length += queryOptionsTlv.GetLength(); - } + case TypeId::kLinkMargin: + VerifyOrExit(!aMetrics.mLinkMargin, error = kErrorParse); + aMetrics.mLinkMargin = true; + break; - // Set Length for Link Metrics Report TLV - tlv->SetLength(length - sizeof(Tlv)); + case TypeId::kRssi: + VerifyOrExit(!aMetrics.mRssi, error = kErrorParse); + aMetrics.mRssi = true; + break; - SuccessOrExit(error = Get().SendDataRequest(aDestination, tlvs, sizeof(tlvs), 0, buf, length)); + default: + if (TypeId::IsExtended(typeId)) + { + offset += sizeof(uint8_t); // Skip the additional second byte. + } + else + { + aMetrics.mReserved = true; + } + break; + } + } exit: return error; } -Status LinkMetrics::ConfigureForwardTrackingSeries(uint8_t aSeriesId, - const SeriesFlags &aSeriesFlags, - const Metrics & aMetrics, - Neighbor & aNeighbor) +Status Subject::ConfigureForwardTrackingSeries(uint8_t aSeriesId, + uint8_t aSeriesFlagsMask, + const Metrics &aMetrics, + Neighbor &aNeighbor) { Status status = kStatusSuccess; VerifyOrExit(0 < aSeriesId, status = kStatusOtherError); - if (aSeriesFlags.GetRawValue() == 0) // Remove the series + + if (aSeriesFlagsMask == 0) // Remove the series { if (aSeriesId == kSeriesIdAllSeries) // Remove all { @@ -702,7 +723,7 @@ Status LinkMetrics::ConfigureForwardTrackingSeries(uint8_t aSeriesId, seriesInfo = mSeriesInfoPool.Allocate(); VerifyOrExit(seriesInfo != nullptr, status = kStatusCannotSupportNewSeries); - seriesInfo->Init(aSeriesId, aSeriesFlags, aMetrics); + seriesInfo->Init(aSeriesId, aSeriesFlagsMask, aMetrics); aNeighbor.AddForwardTrackingSeriesInfo(*seriesInfo); } @@ -711,8 +732,7 @@ Status LinkMetrics::ConfigureForwardTrackingSeries(uint8_t aSeriesId, return status; } -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE -Status LinkMetrics::ConfigureEnhAckProbing(EnhAckFlags aEnhAckFlags, const Metrics &aMetrics, Neighbor &aNeighbor) +Status Subject::ConfigureEnhAckProbing(uint8_t aEnhAckFlags, const Metrics &aMetrics, Neighbor &aNeighbor) { Status status = kStatusSuccess; Error error = kErrorNone; @@ -742,136 +762,57 @@ Status LinkMetrics::ConfigureEnhAckProbing(EnhAckFlags aEnhAckFlags, const Metri exit: return status; } + #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE -Neighbor *LinkMetrics::GetNeighborFromLinkLocalAddr(const Ip6::Address &aDestination) +uint8_t ScaleLinkMarginToRawValue(uint8_t aLinkMargin) { - Neighbor * neighbor = nullptr; - Mac::Address macAddress; + // Linearly scale Link Margin from [0, 130] to [0, 255]. + // `kMaxLinkMargin = 130`. - VerifyOrExit(aDestination.IsLinkLocal()); - aDestination.GetIid().ConvertToMacAddress(macAddress); - neighbor = Get().FindNeighbor(macAddress); + uint16_t value; -exit: - return neighbor; + value = Min(aLinkMargin, kMaxLinkMargin); + value = value * NumericLimits::kMax; + value = DivideAndRoundToClosest(value, kMaxLinkMargin); + + return static_cast(value); } -Error LinkMetrics::ReadTypeIdFlagsFromMessage(const Message &aMessage, - uint8_t aStartPos, - uint8_t aEndPos, - Metrics & aMetrics) +uint8_t ScaleRawValueToLinkMargin(uint8_t aRawValue) { - Error error = kErrorNone; - - memset(&aMetrics, 0, sizeof(aMetrics)); + // Scale back raw value of [0, 255] to Link Margin from [0, 130]. - for (uint16_t pos = aStartPos; pos < aEndPos; pos += sizeof(TypeIdFlags)) - { - TypeIdFlags typeIdFlags; - - SuccessOrExit(aMessage.Read(pos, typeIdFlags)); + uint16_t value = aRawValue; - switch (typeIdFlags.GetRawValue()) - { - case TypeIdFlags::kPdu: - VerifyOrExit(!aMetrics.mPduCount, error = kErrorParse); - aMetrics.mPduCount = true; - break; - - case TypeIdFlags::kLqi: - VerifyOrExit(!aMetrics.mLqi, error = kErrorParse); - aMetrics.mLqi = true; - break; - - case TypeIdFlags::kLinkMargin: - VerifyOrExit(!aMetrics.mLinkMargin, error = kErrorParse); - aMetrics.mLinkMargin = true; - break; - - case TypeIdFlags::kRssi: - VerifyOrExit(!aMetrics.mRssi, error = kErrorParse); - aMetrics.mRssi = true; - break; - - default: - if (typeIdFlags.IsExtendedFlagSet()) - { - pos += sizeof(uint8_t); // Skip the additional second flags byte. - } - else - { - aMetrics.mReserved = true; - } - break; - } - } - -exit: - return error; + value = value * kMaxLinkMargin; + value = DivideAndRoundToClosest(value, NumericLimits::kMax); + return static_cast(value); } -Error LinkMetrics::AppendReportSubTlvToMessage(Message &aMessage, uint8_t &aLength, const MetricsValues &aValues) +uint8_t ScaleRssiToRawValue(int8_t aRssi) { - Error error = kErrorNone; - ReportSubTlv metric; - - aLength = 0; - - // Link Metrics Report sub-TLVs - if (aValues.mMetrics.mPduCount) - { - metric.Init(); - metric.SetMetricsTypeId(TypeIdFlags(TypeIdFlags::kPdu)); - metric.SetMetricsValue32(aValues.mPduCountValue); - SuccessOrExit(error = aMessage.AppendBytes(&metric, metric.GetSize())); - aLength += metric.GetSize(); - } - - if (aValues.mMetrics.mLqi) - { - metric.Init(); - metric.SetMetricsTypeId(TypeIdFlags(TypeIdFlags::kLqi)); - metric.SetMetricsValue8(aValues.mLqiValue); - SuccessOrExit(error = aMessage.AppendBytes(&metric, metric.GetSize())); - aLength += metric.GetSize(); - } + // Linearly scale RSSI from [-130, 0] to [0, 255]. + // `kMinRssi = -130`, `kMaxRssi = 0`. - if (aValues.mMetrics.mLinkMargin) - { - metric.Init(); - metric.SetMetricsTypeId(TypeIdFlags(TypeIdFlags::kLinkMargin)); - metric.SetMetricsValue8(aValues.mLinkMarginValue); - SuccessOrExit(error = aMessage.AppendBytes(&metric, metric.GetSize())); - aLength += metric.GetSize(); - } + int32_t value = aRssi; - if (aValues.mMetrics.mRssi) - { - metric.Init(); - metric.SetMetricsTypeId(TypeIdFlags(TypeIdFlags::kRssi)); - metric.SetMetricsValue8(aValues.mRssiValue); - SuccessOrExit(error = aMessage.AppendBytes(&metric, metric.GetSize())); - aLength += metric.GetSize(); - } + value = Clamp(value, kMinRssi, kMaxRssi) - kMinRssi; + value = value * NumericLimits::kMax; + value = DivideAndRoundToClosest(value, kMaxRssi - kMinRssi); -exit: - return error; + return static_cast(value); } -Error LinkMetrics::AppendStatusSubTlvToMessage(Message &aMessage, uint8_t &aLength, Status aStatus) +int8_t ScaleRawValueToRssi(uint8_t aRawValue) { - Error error = kErrorNone; - Tlv statusTlv; + int32_t value = aRawValue; - statusTlv.SetType(SubTlv::kStatus); - statusTlv.SetLength(sizeof(uint8_t)); - SuccessOrExit(error = aMessage.AppendBytes(&statusTlv, sizeof(statusTlv))); - SuccessOrExit(error = aMessage.AppendBytes(&aStatus, sizeof(aStatus))); - aLength += statusTlv.GetSize(); + value = value * (kMaxRssi - kMinRssi); + value = DivideAndRoundToClosest(value, NumericLimits::kMax); + value += kMinRssi; -exit: - return error; + return static_cast(value); } } // namespace LinkMetrics diff --git a/src/core/thread/link_metrics.hpp b/src/core/thread/link_metrics.hpp index 318aea0e6a3..0dcdd991ffe 100644 --- a/src/core/thread/link_metrics.hpp +++ b/src/core/thread/link_metrics.hpp @@ -46,6 +46,7 @@ #include #include "common/as_core_type.hpp" +#include "common/callback.hpp" #include "common/clearable.hpp" #include "common/locator.hpp" #include "common/message.hpp" @@ -57,6 +58,7 @@ namespace ot { class Neighbor; +class UnitTester; namespace LinkMetrics { @@ -69,189 +71,89 @@ namespace LinkMetrics { * @{ */ -/** - * This type represents the results (values) for a set of metrics. - * - * @sa otLinkMetricsValues. - * - */ -class MetricsValues : public otLinkMetricsValues, public Clearable -{ -public: - /** - * This method gets the metrics flags. - * - * @returns The metrics flags. - * - */ - Metrics &GetMetrics(void) { return static_cast(mMetrics); } - - /** - * This method gets the metrics flags. - * - * @returns The metrics flags. - * - */ - const Metrics &GetMetrics(void) const { return static_cast(mMetrics); } - - /** - * This method set the metrics flags. - * - * @param[in] aMetrics The metrics flags to set from. - * - */ - void SetMetrics(const Metrics &aMetrics) { mMetrics = aMetrics; } -}; +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE /** - * This class represents one Series that is being tracked by the Subject. - * - * When an Initiator successfully configured a Forward Tracking Series, the Subject would use an instance of this class - * to track the information of the Series. The Subject has a `Pool` of `SeriesInfo`. It would allocate one when a new - * Series comes, and free it when a Series finishes. + * This class implements the Thread Link Metrics Initiator. * - * This class inherits `LinkedListEntry` and each `Neighbor` has a list of `SeriesInfo` so that the Subject could track - * per Series initiated by neighbors as long as it has available resources. + * The Initiator makes queries, configures Link Metrics probing at the Subject and generates reports of the results. * */ -class SeriesInfo : public LinkedListEntry +class Initiator : public InstanceLocator, private NonCopyable { - friend class LinkedList; - friend class LinkedListEntry; - public: - /** - * This constant represents Link Probe when filtering frames to be accounted using Series Flag. There's - * already `kFcfFrameData`, `kFcfFrameAck` and `kFcfFrameMacCmd`. This item is added so that we can - * filter a Link Probe for series in the same way as other frames. - * - */ - static constexpr uint8_t kSeriesTypeLinkProbe = 0; + // Initiator callbacks + typedef otLinkMetricsReportCallback ReportCallback; + typedef otLinkMetricsMgmtResponseCallback MgmtResponseCallback; + typedef otLinkMetricsEnhAckProbingIeReportCallback EnhAckProbingIeReportCallback; /** - * This method initializes the SeriesInfo object. - * - * @param[in] aSeriesId The Series ID. - * @param[in] aSeriesFlags The Series Flags which specify what types of frames are to be accounted. - * @param[in] aMetrics Metrics to query. + * This structure provides the info used for appending MLE Link Metric Query TLV. * */ - void Init(uint8_t aSeriesId, const SeriesFlags &aSeriesFlags, const Metrics &aMetrics); + struct QueryInfo : public Clearable + { + uint8_t mSeriesId; ///< Series ID. + uint8_t mTypeIds[kMaxTypeIds]; ///< Type IDs. + uint8_t mTypeIdCount; ///< Number of entries in `mTypeIds[]`. + }; /** - * This method gets the Series ID. + * This constructor initializes an instance of the Initiator class. * - * @returns The Series ID. + * @param[in] aInstance A reference to the OpenThread interface. * */ - uint8_t GetSeriesId(void) const { return mSeriesId; } + explicit Initiator(Instance &aInstance); /** - * This method gets the PDU count. + * This method sends an MLE Data Request containing Link Metrics Query TLV to query Link Metrics data. * - * @returns The PDU count. + * It could be either a Single Probe or a Forward Tracking Series. * - */ - uint32_t GetPduCount(void) const { return mPduCount; } - - /** - * This method gets the average LQI. + * @param[in] aDestination A reference to the IPv6 address of the destination. + * @param[in] aSeriesId The Series ID to query, 0 for single probe. + * @param[in] aMetrics A pointer to metrics to query. * - * @returns The average LQI. + * @retval kErrorNone Successfully sent a Link Metrics query message. + * @retval kErrorNoBufs Insufficient buffers to generate the MLE Data Request message. + * @retval kErrorInvalidArgs Type IDs are not valid or exceed the count limit. + * @retval kErrorUnknownNeighbor @p aDestination is not link-local or the neighbor is not found. * */ - uint8_t GetAverageLqi(void) const { return mLqiAverager.GetAverage(); } + Error Query(const Ip6::Address &aDestination, uint8_t aSeriesId, const Metrics *aMetrics); /** - * This method gets the average RSS. + * This method appends MLE Link Metrics Query TLV to a given message. * - * @returns The average RSS. + * @param[in] aMessage The message to append to. + * @param[in] aInfo The link metrics query info to use to prepare the message. * - */ - int8_t GetAverageRss(void) const { return mRssAverager.GetAverage(); } - - /** - * This method aggregates the Link Metrics data of a frame into this series. - * - * @param[in] aFrameType The type of the frame. - * @param[in] aLqi The LQI value. - * @param[in] aRss The RSS value. + * @retval kErrorNone Successfully appended the TLV to the message. + * @retval kErrorNoBufs Insufficient buffers available to append the TLV. * */ - void AggregateLinkMetrics(uint8_t aFrameType, uint8_t aLqi, int8_t aRss); + Error AppendLinkMetricsQueryTlv(Message &aMessage, const QueryInfo &aInfo); /** - * This methods gets the metrics. - * - * @returns The metrics associated with `SeriesInfo`. - * - */ - const Metrics &GetLinkMetrics(void) const { return mMetrics; } - -private: - bool Matches(const uint8_t &aSeriesId) const { return mSeriesId == aSeriesId; } - bool IsFrameTypeMatch(uint8_t aFrameType) const; - - SeriesInfo *mNext; - uint8_t mSeriesId; - SeriesFlags mSeriesFlags; - Metrics mMetrics; - RssAverager mRssAverager; - LqiAverager mLqiAverager; - uint32_t mPduCount; -}; - -/** - * This enumeration type represent Link Metrics Status. - * - */ -enum Status : uint8_t -{ - kStatusSuccess = OT_LINK_METRICS_STATUS_SUCCESS, - kStatusCannotSupportNewSeries = OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES, - kStatusSeriesIdAlreadyRegistered = OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED, - kStatusSeriesIdNotRecognized = OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED, - kStatusNoMatchingFramesReceived = OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED, - kStatusOtherError = OT_LINK_METRICS_STATUS_OTHER_ERROR, -}; - -/** - * This class implements Thread Link Metrics query and management. - * - */ -class LinkMetrics : public InstanceLocator, private NonCopyable -{ - friend class ot::Neighbor; - -public: - typedef otLinkMetricsReportCallback ReportCallback; - typedef otLinkMetricsMgmtResponseCallback MgmtResponseCallback; - typedef otLinkMetricsEnhAckProbingIeReportCallback EnhAckProbingIeReportCallback; - - /** - * This constructor initializes an instance of the LinkMetrics class. + * This method registers a callback to handle Link Metrics report received. * - * @param[in] aInstance A reference to the OpenThread interface. + * @param[in] aCallback A pointer to a function that is called when a Link Metrics report is received. + * @param[in] aContext A pointer to application-specific context. * */ - explicit LinkMetrics(Instance &aInstance); + void SetReportCallback(ReportCallback aCallback, void *aContext) { mReportCallback.Set(aCallback, aContext); } /** - * This method sends an MLE Data Request containing Link Metrics Query TLV to query Link Metrics data. - * - * It could be either a Single Probe or a Forward Tracking Series. - * - * @param[in] aDestination A reference to the IPv6 address of the destination. - * @param[in] aSeriesId The Series ID to query, 0 for single probe. - * @param[in] aMetrics A pointer to metrics to query. + * This method handles the received Link Metrics report contained in @p aMessage. * - * @retval kErrorNone Successfully sent a Link Metrics query message. - * @retval kErrorNoBufs Insufficient buffers to generate the MLE Data Request message. - * @retval kErrorInvalidArgs TypeIdFlags are not valid or exceed the count limit. - * @retval kErrorUnknownNeighbor @p aDestination is not link-local or the neighbor is not found. + * @param[in] aMessage A reference to the message. + * @param[in] aOffset The offset in bytes where the metrics report sub-TLVs start. + * @param[in] aLength The length of the metrics report sub-TLVs in bytes. + * @param[in] aAddress A reference to the source address of the message. * */ - Error Query(const Ip6::Address &aDestination, uint8_t aSeriesId, const Metrics *aMetrics); + void HandleReport(const Message &aMessage, uint16_t aOffset, uint16_t aLength, const Ip6::Address &aAddress); /** * This method sends an MLE Link Metrics Management Request to configure/clear a Forward Tracking Series. @@ -267,12 +169,23 @@ class LinkMetrics : public InstanceLocator, private NonCopyable * @retval kErrorUnknownNeighbor @p aDestination is not link-local or the neighbor is not found. * */ - Error SendMgmtRequestForwardTrackingSeries(const Ip6::Address & aDestination, - uint8_t aSeriesId, - const SeriesFlags::Info &aSeriesFlags, - const Metrics * aMetrics); + Error SendMgmtRequestForwardTrackingSeries(const Ip6::Address &aDestination, + uint8_t aSeriesId, + const SeriesFlags &aSeriesFlags, + const Metrics *aMetrics); + + /** + * This method registers a callback to handle Link Metrics Management Response received. + * + * @param[in] aCallback A pointer to a function that is called when a Link Metrics Management Response is received. + * @param[in] aContext A pointer to application-specific context. + * + */ + void SetMgmtResponseCallback(MgmtResponseCallback aCallback, void *aContext) + { + mMgmtResponseCallback.Set(aCallback, aContext); + } -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE /** * This method sends an MLE Link Metrics Management Request to configure/clear a Enhanced-ACK Based Probing. * @@ -290,7 +203,31 @@ class LinkMetrics : public InstanceLocator, private NonCopyable */ Error SendMgmtRequestEnhAckProbing(const Ip6::Address &aDestination, EnhAckFlags aEnhAckFlags, - const Metrics * aMetrics); + const Metrics *aMetrics); + + /** + * This method registers a callback to handle Link Metrics when Enh-ACK Probing IE is received. + * + * @param[in] aCallback A pointer to a function that is called when Enh-ACK Probing IE is received is received. + * @param[in] aContext A pointer to application-specific context. + * + */ + void SetEnhAckProbingCallback(EnhAckProbingIeReportCallback aCallback, void *aContext) + { + mEnhAckProbingIeReportCallback.Set(aCallback, aContext); + } + + /** + * This method handles the received Link Metrics Management Response contained in @p aMessage. + * + * @param[in] aMessage A reference to the message that contains the Link Metrics Management Response. + * @param[in] aAddress A reference to the source address of the message. + * + * @retval kErrorNone Successfully handled the Link Metrics Management Response. + * @retval kErrorParse Cannot parse sub-TLVs from @p aMessage successfully. + * + */ + Error HandleManagementResponse(const Message &aMessage, const Ip6::Address &aAddress); /** * This method sends an MLE Link Probe message. @@ -306,9 +243,50 @@ class LinkMetrics : public InstanceLocator, private NonCopyable * */ Error SendLinkProbe(const Ip6::Address &aDestination, uint8_t aSeriesId, uint8_t aLength); -#endif + + /** + * This method processes received Enh-ACK Probing IE data. + * + * @param[in] aData A pointer to buffer containing the Enh-ACK Probing IE data. + * @param[in] aLength The length of @p aData. + * @param[in] aNeighbor The neighbor from which the Enh-ACK Probing IE was received. + * + */ + void ProcessEnhAckIeData(const uint8_t *aData, uint8_t aLength, const Neighbor &aNeighbor); + +private: + static constexpr uint8_t kLinkProbeMaxLen = 64; // Max length of data payload in Link Probe TLV. + + Error FindNeighbor(const Ip6::Address &aDestination, Neighbor *&aNeighbor); + + Callback mReportCallback; + Callback mMgmtResponseCallback; + Callback mEnhAckProbingIeReportCallback; +}; + +#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE + +/** + * This class implements the Thread Link Metrics Subject. + * + * The Subject reponds queries with reports, handles Link Metrics Management Requests and Link Probe Messages. + * + */ +class Subject : public InstanceLocator, private NonCopyable +{ +public: + typedef otLinkMetricsEnhAckProbingIeReportCallback EnhAckProbingIeReportCallback; + + /** + * This constructor initializes an instance of the Subject class. + * + * @param[in] aInstance A reference to the OpenThread interface. + * + */ + explicit Subject(Instance &aInstance); + /** * This method appends a Link Metrics Report to a message according to the Link Metrics query. * @@ -322,7 +300,7 @@ class LinkMetrics : public InstanceLocator, private NonCopyable * */ Error AppendReport(Message &aMessage, const Message &aRequestMessage, Neighbor &aNeighbor); -#endif + /** * This method handles the received Link Metrics Management Request contained in @p aMessage and return a status. * @@ -336,29 +314,6 @@ class LinkMetrics : public InstanceLocator, private NonCopyable */ Error HandleManagementRequest(const Message &aMessage, Neighbor &aNeighbor, Status &aStatus); - /** - * This method handles the received Link Metrics Management Response contained in @p aMessage. - * - * @param[in] aMessage A reference to the message that contains the Link Metrics Management Response. - * @param[in] aAddress A reference to the source address of the message. - * - * @retval kErrorNone Successfully handled the Link Metrics Management Response. - * @retval kErrorParse Cannot parse sub-TLVs from @p aMessage successfully. - * - */ - Error HandleManagementResponse(const Message &aMessage, const Ip6::Address &aAddress); - - /** - * This method handles the received Link Metrics report contained in @p aMessage. - * - * @param[in] aMessage A reference to the message. - * @param[in] aOffset The offset in bytes where the metrics report sub-TLVs start. - * @param[in] aLength The length of the metrics report sub-TLVs in bytes. - * @param[in] aAddress A reference to the source address of the message. - * - */ - void HandleReport(const Message &aMessage, uint16_t aOffset, uint16_t aLength, const Ip6::Address &aAddress); - /** * This method handles the Link Probe contained in @p aMessage. * @@ -372,93 +327,44 @@ class LinkMetrics : public InstanceLocator, private NonCopyable Error HandleLinkProbe(const Message &aMessage, uint8_t &aSeriesId); /** - * This method registers a callback to handle Link Metrics report received. - * - * @param[in] aCallback A pointer to a function that is called when a Link Metrics report is received. - * @param[in] aContext A pointer to application-specific context. - * - */ - void SetReportCallback(ReportCallback aCallback, void *aContext); - - /** - * This method registers a callback to handle Link Metrics Management Response received. - * - * @param[in] aCallback A pointer to a function that is called when a Link Metrics Management Response is received. - * @param[in] aContext A pointer to application-specific context. - * - */ - void SetMgmtResponseCallback(MgmtResponseCallback aCallback, void *aContext); - - /** - * This method registers a callback to handle Link Metrics when Enh-ACK Probing IE is received. + * This method frees a SeriesInfo entry that was allocated from the Subject object. * - * @param[in] aCallback A pointer to a function that is called when Enh-ACK Probing IE is received is received. - * @param[in] aContext A pointer to application-specific context. + * @param[in] aSeries A reference to the SeriesInfo to free. * */ - void SetEnhAckProbingCallback(EnhAckProbingIeReportCallback aCallback, void *aContext); - - /** - * This method processes received Enh-ACK Probing IE data. - * - * @param[in] aData A pointer to buffer containing the Enh-ACK Probing IE data. - * @param[in] aLength The length of @p aData. - * @param[in] aNeighbor The neighbor from which the Enh-ACK Probing IE was received. - * - */ - void ProcessEnhAckIeData(const uint8_t *aData, uint8_t aLength, const Neighbor &aNeighbor); + void Free(SeriesInfo &aSeriesInfo); private: - static constexpr uint8_t kMaxTypeIdFlags = 4; - // Max number of SeriesInfo that could be allocated by the pool. static constexpr uint16_t kMaxSeriesSupported = OPENTHREAD_CONFIG_MLE_LINK_METRICS_MAX_SERIES_SUPPORTED; - static constexpr uint8_t kQueryIdSingleProbe = 0; // This query ID represents Single Probe. - static constexpr uint8_t kSeriesIdAllSeries = 255; // This series ID represents all series. - static constexpr uint8_t kLinkProbeMaxLen = 64; // Max length of data payload in Link Probe TLV. - - Error SendLinkMetricsQuery(const Ip6::Address &aDestination, - uint8_t aSeriesId, - const TypeIdFlags * aTypeIdFlags, - uint8_t aTypeIdFlagsCount); - - Status ConfigureForwardTrackingSeries(uint8_t aSeriesId, - const SeriesFlags &aSeriesFlags, - const Metrics & aMetrics, - Neighbor & aNeighbor); - - Status ConfigureEnhAckProbing(EnhAckFlags aEnhAckFlags, const Metrics &aMetrics, Neighbor &aNeighbor); + static Error ReadTypeIdsFromMessage(const Message &aMessage, + uint16_t aStartOffset, + uint16_t aEndOffset, + Metrics &aMetrics); + static Error AppendReportSubTlvToMessage(Message &aMessage, const MetricsValues &aValues); - Neighbor *GetNeighborFromLinkLocalAddr(const Ip6::Address &aDestination); - - static Error ReadTypeIdFlagsFromMessage(const Message &aMessage, - uint8_t aStartPos, - uint8_t aEndPos, - Metrics & aMetrics); - static Error AppendReportSubTlvToMessage(Message &aMessage, uint8_t &aLength, const MetricsValues &aValues); - static Error AppendStatusSubTlvToMessage(Message &aMessage, uint8_t &aLength, Status aStatus); - - ReportCallback mReportCallback; - void * mReportCallbackContext; - MgmtResponseCallback mMgmtResponseCallback; - void * mMgmtResponseCallbackContext; - EnhAckProbingIeReportCallback mEnhAckProbingIeReportCallback; - void * mEnhAckProbingIeReportCallbackContext; + Status ConfigureForwardTrackingSeries(uint8_t aSeriesId, + uint8_t aSeriesFlags, + const Metrics &aMetrics, + Neighbor &aNeighbor); + Status ConfigureEnhAckProbing(uint8_t aEnhAckFlags, const Metrics &aMetrics, Neighbor &aNeighbor); Pool mSeriesInfoPool; }; +#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE + +uint8_t ScaleLinkMarginToRawValue(uint8_t aLinkMargin); +uint8_t ScaleRawValueToLinkMargin(uint8_t aRawValue); +uint8_t ScaleRssiToRawValue(int8_t aRssi); +int8_t ScaleRawValueToRssi(uint8_t aRawValue); + /** * @} */ } // namespace LinkMetrics - -DefineCoreType(otLinkMetrics, LinkMetrics::Metrics); -DefineCoreType(otLinkMetricsValues, LinkMetrics::MetricsValues); -DefineMapEnum(otLinkMetricsEnhAckFlags, LinkMetrics::EnhAckFlags); - } // namespace ot #endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE diff --git a/src/core/thread/link_metrics_tlvs.hpp b/src/core/thread/link_metrics_tlvs.hpp index 118f730be71..ccf4af1ac97 100644 --- a/src/core/thread/link_metrics_tlvs.hpp +++ b/src/core/thread/link_metrics_tlvs.hpp @@ -45,19 +45,12 @@ #include "common/encoding.hpp" #include "common/message.hpp" #include "common/tlvs.hpp" +#include "thread/link_metrics_types.hpp" namespace ot { namespace LinkMetrics { -/** - * This type represents Link Metric Flags indicating a set of metrics. - * - * @sa otLinkMetrics - * - */ -class Metrics : public otLinkMetrics, public Clearable -{ -}; +using ot::Encoding::BigEndian::HostSwap32; /** * This class defines constants related to Link Metrics Sub-TLVs. @@ -88,208 +81,10 @@ class SubTlv typedef UintTlvInfo QueryIdSubTlv; /** - * This class implements Link Metrics Type ID Flags generation and parsing. + * This type defines a Link Metrics Status Sub-Tlv. * */ -OT_TOOL_PACKED_BEGIN class TypeIdFlags -{ - static constexpr uint8_t kExtendedFlag = 1 << 7; - static constexpr uint8_t kLengthOffset = 6; - static constexpr uint8_t kLengthFlag = 1 << kLengthOffset; - static constexpr uint8_t kTypeEnumOffset = 3; - static constexpr uint8_t kTypeEnumMask = 7 << kTypeEnumOffset; - static constexpr uint8_t kMetricEnumOffset = 0; - static constexpr uint8_t kMetricEnumMask = 7 << kMetricEnumOffset; - -public: - /** - * This enumeration specifies the Length field in Type ID Flags. - * - */ - enum Length - { - kShortLength = 0, ///< Short value length (1 byte value) - kExtendedLength = 1, ///< Extended value length (4 bytes value) - }; - - /** - * This enumeration specifies the Type values in Type ID Flags. - * - */ - enum TypeEnum : uint8_t - { - kTypeCountSummation = 0, ///< Count or summation - kTypeExpMovingAverage = 1, ///< Exponential moving average. - kTypeReserved = 2, ///< Reserved for future use. - }; - - /** - * This enumeration specifies the Metric values in Type ID Flag. - * - */ - enum MetricEnum : uint8_t - { - kMetricPdusReceived = 0, ///< Number of PDUs received. - kMetricLqi = 1, ///< Link Quality Indicator. - kMetricLinkMargin = 2, ///< Link Margin. - kMetricRssi = 3, ///< RSSI in dbm. - }; - - /** - * This constant defines the raw value for Type ID Flag for PDU. - * - */ - static constexpr uint8_t kPdu = (kExtendedLength << kLengthOffset) | (kTypeCountSummation << kTypeEnumOffset) | - (kMetricPdusReceived << kMetricEnumOffset); - - /** - * This constant defines the raw value for Type ID Flag for LQI. - * - */ - static constexpr uint8_t kLqi = (kShortLength << kLengthOffset) | (kTypeExpMovingAverage << kTypeEnumOffset) | - (kMetricLqi << kMetricEnumOffset); - - /** - * This constant defines the raw value for Type ID Flag for Link Margin. - * - */ - static constexpr uint8_t kLinkMargin = (kShortLength << kLengthOffset) | - (kTypeExpMovingAverage << kTypeEnumOffset) | - (kMetricLinkMargin << kMetricEnumOffset); - - /** - * This constant defines the raw value for Type ID Flag for RSSI - * - */ - static constexpr uint8_t kRssi = (kShortLength << kLengthOffset) | (kTypeExpMovingAverage << kTypeEnumOffset) | - (kMetricRssi << kMetricEnumOffset); - - /** - * Default constructor. - * - */ - TypeIdFlags(void) = default; - - /** - * Constructor to initialize from raw value. - * - * @param[in] aFlags The raw flags value. - * - */ - explicit TypeIdFlags(uint8_t aFlags) - : mFlags(aFlags) - { - } - - /** - * This method initializes the Type ID value - * - */ - void Init(void) { mFlags = 0; } - - /** - * This method clears the Extended flag. - * - */ - void ClearExtendedFlag(void) { mFlags &= ~kExtendedFlag; } - - /** - * This method sets the Extended flag, indicating an additional second flags byte after the current 1-byte flags. - * MUST NOT set in Thread 1.2.1. - * - */ - void SetExtendedFlag(void) { mFlags |= kExtendedFlag; } - - /** - * This method indicates whether or not the Extended flag is set. - * - * @retval true The Extended flag is set. - * @retval false The Extended flag is not set. - * - */ - bool IsExtendedFlagSet(void) const { return (mFlags & kExtendedFlag) != 0; } - - /** - * This method clears value length flag. - * - */ - void ClearLengthFlag(void) { mFlags &= ~kLengthFlag; } - - /** - * This method sets the value length flag. - * - */ - void SetLengthFlag(void) { mFlags |= kLengthFlag; } - - /** - * This method indicates whether or not the value length flag is set. - * - * @retval true The value length flag is set, extended value length (4 bytes) - * @retval false The value length flag is not set, short value length (1 byte) - * - */ - bool IsLengthFlagSet(void) const { return (mFlags & kLengthFlag) != 0; } - - /** - * This method sets the Type/Average Enum. - * - * @param[in] aTypeEnum Type/Average Enum. - * - */ - void SetTypeEnum(TypeEnum aTypeEnum) - { - mFlags = (mFlags & ~kTypeEnumMask) | ((aTypeEnum << kTypeEnumOffset) & kTypeEnumMask); - } - - /** - * This method returns the Type/Average Enum. - * - * @returns The Type/Average Enum. - * - */ - TypeEnum GetTypeEnum(void) const { return static_cast((mFlags & kTypeEnumMask) >> kTypeEnumOffset); } - - /** - * This method sets the Metric Enum. - * - * @param[in] aMetricEnum Metric Enum. - * - */ - void SetMetricEnum(MetricEnum aMetricEnum) - { - mFlags = (mFlags & ~kMetricEnumMask) | ((aMetricEnum << kMetricEnumOffset) & kMetricEnumMask); - } - - /** - * This method returns the Metric Enum. - * - * @returns The Metric Enum. - * - */ - MetricEnum GetMetricEnum(void) const - { - return static_cast((mFlags & kMetricEnumMask) >> kMetricEnumOffset); - } - - /** - * This method returns the raw value of the entire TypeIdFlags. - * - * @returns The raw value of TypeIdFlags. - * - */ - uint8_t GetRawValue(void) const { return mFlags; } - - /** - * This method sets the raw value of the entire TypeIdFlags. - * - * @param[in] aFlags The raw flags value. - * - */ - void SetRawValue(uint8_t aFlags) { mFlags = aFlags; } - -private: - uint8_t mFlags; -} OT_TOOL_PACKED_END; +typedef UintTlvInfo StatusSubTlv; /** * This class implements Link Metrics Report Sub-TLV generation and parsing. @@ -299,15 +94,13 @@ OT_TOOL_PACKED_BEGIN class ReportSubTlv : public Tlv, public TlvInfo { public: + static constexpr uint8_t kMinLength = 2; ///< Minimum expected TLV length (type ID and u8 value). + /** * This method initializes the TLV. * */ - void Init(void) - { - SetType(SubTlv::kReport); - SetLength(sizeof(*this) - sizeof(Tlv)); - } + void Init(void) { SetType(SubTlv::kReport); } /** * This method indicates whether or not the TLV appears to be well-formed. @@ -316,7 +109,7 @@ class ReportSubTlv : public Tlv, public TlvInfo * @retval false The TLV does not appear to be well-formed. * */ - bool IsValid(void) const { return GetLength() >= sizeof(TypeIdFlags) + sizeof(uint8_t); } + bool IsValid(void) const { return GetLength() >= kMinLength; } /** * This method returns the Link Metrics Type ID. @@ -324,7 +117,7 @@ class ReportSubTlv : public Tlv, public TlvInfo * @returns The Link Metrics Type ID. * */ - TypeIdFlags GetMetricsTypeId(void) const { return mMetricsTypeId; } + uint8_t GetMetricsTypeId(void) const { return mMetricsTypeId; } /** * This method sets the Link Metrics Type ID. @@ -332,15 +125,7 @@ class ReportSubTlv : public Tlv, public TlvInfo * @param[in] aMetricsTypeId The Link Metrics Type ID to set. * */ - void SetMetricsTypeId(TypeIdFlags aMetricsTypeId) - { - mMetricsTypeId = aMetricsTypeId; - - if (!aMetricsTypeId.IsLengthFlagSet()) - { - SetLength(sizeof(*this) - sizeof(Tlv) - sizeof(uint32_t) + sizeof(uint8_t)); // The value is 1 byte long - } - } + void SetMetricsTypeId(uint8_t aMetricsTypeId) { mMetricsTypeId = aMetricsTypeId; } /** * This method returns the metric value in 8 bits. @@ -356,7 +141,7 @@ class ReportSubTlv : public Tlv, public TlvInfo * @returns The metric value. * */ - uint32_t GetMetricsValue32(void) const { return mMetricsValue.m32; } + uint32_t GetMetricsValue32(void) const { return HostSwap32(mMetricsValue.m32); } /** * This method sets the metric value (8 bits). @@ -364,7 +149,11 @@ class ReportSubTlv : public Tlv, public TlvInfo * @param[in] aMetricsValue Metrics value. * */ - void SetMetricsValue8(uint8_t aMetricsValue) { mMetricsValue.m8 = aMetricsValue; } + void SetMetricsValue8(uint8_t aMetricsValue) + { + mMetricsValue.m8 = aMetricsValue; + SetLength(kMinLength); + } /** * This method sets the metric value (32 bits). @@ -372,10 +161,14 @@ class ReportSubTlv : public Tlv, public TlvInfo * @param[in] aMetricsValue Metrics value. * */ - void SetMetricsValue32(uint32_t aMetricsValue) { mMetricsValue.m32 = aMetricsValue; } + void SetMetricsValue32(uint32_t aMetricsValue) + { + mMetricsValue.m32 = HostSwap32(aMetricsValue); + SetLength(sizeof(*this) - sizeof(Tlv)); + } private: - TypeIdFlags mMetricsTypeId; + uint8_t mMetricsTypeId; union { uint8_t m8; @@ -408,227 +201,90 @@ class QueryOptionsSubTlv : public Tlv, public TlvInfo * @retval FALSE If the TLV does not appear to be well-formed. * */ - bool IsValid(void) const { return GetLength() >= sizeof(TypeIdFlags); } + bool IsValid(void) const { return GetLength() >= sizeof(uint8_t); } } OT_TOOL_PACKED_END; /** - * This class implements Series Flags for Forward Tracking Series. + * This class defines Link Metrics Forward Probing Registration Sub-TLV. * */ OT_TOOL_PACKED_BEGIN -class SeriesFlags +class FwdProbingRegSubTlv : public Tlv, public TlvInfo { public: - /** - * This type represents which frames to be accounted in a Forward Tracking Series. - * - * @sa otLinkMetricsSeriesFlags - * - */ - typedef otLinkMetricsSeriesFlags Info; - - /** - * Default constructor. - * - */ - SeriesFlags(void) - : mFlags(0) - { - } + static constexpr uint8_t kMinLength = sizeof(uint8_t) + sizeof(uint8_t); ///< Minimum expected TLV length /** - * This method sets the values from an `Info` object. - * - * @param[in] aSeriesFlags The `Info` object. + * This method initializes the TLV. * */ - void SetFrom(const Info &aSeriesFlags) + void Init(void) { - Clear(); - - if (aSeriesFlags.mLinkProbe) - { - SetLinkProbeFlag(); - } - - if (aSeriesFlags.mMacData) - { - SetMacDataFlag(); - } - - if (aSeriesFlags.mMacDataRequest) - { - SetMacDataRequestFlag(); - } - - if (aSeriesFlags.mMacAck) - { - SetMacAckFlag(); - } + SetType(SubTlv::kFwdProbingReg); + SetLength(kMinLength); } /** - * This method clears the Link Probe flag. - * - */ - void ClearLinkProbeFlag(void) { mFlags &= ~kLinkProbeFlag; } - - /** - * This method sets the Link Probe flag. - * - */ - void SetLinkProbeFlag(void) { mFlags |= kLinkProbeFlag; } - - /** - * This method indicates whether or not the Link Probe flag is set. - * - * @retval true The Link Probe flag is set. - * @retval false The Link Probe flag is not set. - * - */ - bool IsLinkProbeFlagSet(void) const { return (mFlags & kLinkProbeFlag) != 0; } - - /** - * This method clears the MAC Data flag. + * This method indicates whether or not the TLV appears to be well-formed. * - */ - void ClearMacDataFlag(void) { mFlags &= ~kMacDataFlag; } - - /** - * This method sets the MAC Data flag. + * @retval true The TLV appears to be well-formed. + * @retval false The TLV does not appear to be well-formed. * */ - void SetMacDataFlag(void) { mFlags |= kMacDataFlag; } + bool IsValid(void) const { return GetLength() >= kMinLength; } /** - * This method indicates whether or not the MAC Data flag is set. + * This method gets the Forward Series ID value. * - * @retval true The MAC Data flag is set. - * @retval false The MAC Data flag is not set. + * @returns The Forward Series ID. * */ - bool IsMacDataFlagSet(void) const { return (mFlags & kMacDataFlag) != 0; } + uint8_t GetSeriesId(void) const { return mSeriesId; } /** - * This method clears the MAC Data Request flag. + * This method sets the Forward Series ID value. * - */ - void ClearMacDataRequestFlag(void) { mFlags &= ~kMacDataRequestFlag; } - - /** - * This method sets the MAC Data Request flag. + * @param[in] aSeriesId The Forward Series ID. * */ - void SetMacDataRequestFlag(void) { mFlags |= kMacDataRequestFlag; } + void SetSeriesId(uint8_t aSeriesId) { mSeriesId = aSeriesId; } /** - * This method indicates whether or not the MAC Data Request flag is set. + * This method gets the Forward Series Flags bit-mask. * - * @retval true The MAC Data Request flag is set. - * @retval false The MAC Data Request flag is not set. + * @returns The Forward Series Flags mask. * */ - bool IsMacDataRequestFlagSet(void) const { return (mFlags & kMacDataRequestFlag) != 0; } + uint8_t GetSeriesFlagsMask(void) const { return mSeriesFlagsMask; } /** - * This method clears the Mac Ack flag. + * This method sets the Forward Series Flags bit-mask * - */ - void ClearMacAckFlag(void) { mFlags &= ~kMacAckFlag; } - - /** - * This method sets the Mac Ack flag. + * @param[in] aSeriesFlagsMask The Forward Series Flags. * */ - void SetMacAckFlag(void) { mFlags |= kMacAckFlag; } + void SetSeriesFlagsMask(uint8_t aSeriesFlagsMask) { mSeriesFlagsMask = aSeriesFlagsMask; } /** - * This method indicates whether or not the Mac Ack flag is set. + * This method gets the start of Type ID array. * - * @retval true The Mac Ack flag is set. - * @retval false The Mac Ack flag is not set. + * @returns The start of Type ID array. Array has `kMaxTypeIds` max length. * */ - bool IsMacAckFlagSet(void) const { return (mFlags & kMacAckFlag) != 0; } - - /** - * This method returns the raw value of flags. - * - */ - uint8_t GetRawValue(void) const { return mFlags; } - - /** - * This method clears the all the flags. - * - */ - void Clear(void) { mFlags = 0; } + uint8_t *GetTypeIds(void) { return mTypeIds; } private: - static constexpr uint8_t kLinkProbeFlag = 1 << 0; - static constexpr uint8_t kMacDataFlag = 1 << 1; - static constexpr uint8_t kMacDataRequestFlag = 1 << 2; - static constexpr uint8_t kMacAckFlag = 1 << 3; - - uint8_t mFlags; + uint8_t mSeriesId; + uint8_t mSeriesFlagsMask; + uint8_t mTypeIds[kMaxTypeIds]; } OT_TOOL_PACKED_END; -/** - * This enumeration type represent Enhanced-ACK Flags. - * - */ -enum EnhAckFlags : uint8_t -{ - kEnhAckClear = OT_LINK_METRICS_ENH_ACK_CLEAR, ///< Clear. - kEnhAckRegister = OT_LINK_METRICS_ENH_ACK_REGISTER, ///< Register. -}; - -static uint8_t TypeIdFlagsFromMetrics(TypeIdFlags aTypeIdFlags[], const Metrics &aMetrics) -{ - uint8_t count = 0; - - if (aMetrics.mPduCount) - { - aTypeIdFlags[count++].SetRawValue(TypeIdFlags::kPdu); - } - - if (aMetrics.mLqi) - { - aTypeIdFlags[count++].SetRawValue(TypeIdFlags::kLqi); - } - - if (aMetrics.mLinkMargin) - { - aTypeIdFlags[count++].SetRawValue(TypeIdFlags::kLinkMargin); - } - - if (aMetrics.mRssi) - { - aTypeIdFlags[count++].SetRawValue(TypeIdFlags::kRssi); - } - -#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE - if (aMetrics.mReserved) - { - for (uint8_t i = 0; i < count; i++) - { - aTypeIdFlags[i].SetTypeEnum(TypeIdFlags::kTypeReserved); - } - } -#endif - - return count; -} - OT_TOOL_PACKED_BEGIN class EnhAckConfigSubTlv : public Tlv, public TlvInfo { public: - /** - * Default constructor - * - */ - EnhAckConfigSubTlv(void) { Init(); } + static constexpr uint8_t kMinLength = sizeof(uint8_t); ///< Minimum TLV length (only `EnhAckFlags`). /** * This method initializes the TLV. @@ -637,43 +293,45 @@ class EnhAckConfigSubTlv : public Tlv, public TlvInfo void Init(void) { SetType(SubTlv::kEnhAckConfig); - SetLength(sizeof(EnhAckFlags)); + SetLength(kMinLength); } /** - * This method sets Enhanced ACK Flags. + * This method indicates whether or not the TLV appears to be well-formed. * - * @param[in] aEnhAckFlags The value of Enhanced ACK Flags. + * @retval true The TLV appears to be well-formed. + * @retval false The TLV does not appear to be well-formed. * */ - void SetEnhAckFlags(EnhAckFlags aEnhAckFlags) - { - memcpy(mSubTlvs + kEnhAckFlagsOffset, &aEnhAckFlags, sizeof(aEnhAckFlags)); - } + bool IsValid(void) const { return GetLength() >= kMinLength; } /** - * This method sets Type ID Flags. + * This method gets the Enhanced ACK Flags. * - * @param[in] aMetrics A metrics flags to indicate the Type ID Flags. + * @returns The Enhanced ACK Flags. * */ - void SetTypeIdFlags(const Metrics &aMetrics) - { - uint8_t count; - - count = TypeIdFlagsFromMetrics(reinterpret_cast(mSubTlvs + kTypeIdFlagsOffset), aMetrics); + uint8_t GetEnhAckFlags(void) const { return mEnhAckFlags; } - OT_ASSERT(count <= kMaxTypeIdFlagsEnhAck); + /** + * This method sets Enhanced ACK Flags. + * + * @param[in] aEnhAckFlags The value of Enhanced ACK Flags. + * + */ + void SetEnhAckFlags(EnhAckFlags aEnhAckFlags) { mEnhAckFlags = aEnhAckFlags; } - SetLength(sizeof(EnhAckFlags) + sizeof(TypeIdFlags) * count); - } + /** + * This method gets the start of Type ID array. + * + * @returns The start of Type ID array. Array has `kMaxTypeIds` max length. + * + */ + uint8_t *GetTypeIds(void) { return mTypeIds; } private: - static constexpr uint8_t kMaxTypeIdFlagsEnhAck = 3; - static constexpr uint8_t kEnhAckFlagsOffset = 0; - static constexpr uint16_t kTypeIdFlagsOffset = sizeof(TypeIdFlags); - - uint8_t mSubTlvs[sizeof(EnhAckFlags) + sizeof(TypeIdFlags) * kMaxTypeIdFlagsEnhAck]; + uint8_t mEnhAckFlags; + uint8_t mTypeIds[kMaxTypeIds]; } OT_TOOL_PACKED_END; } // namespace LinkMetrics diff --git a/src/core/thread/link_metrics_types.cpp b/src/core/thread/link_metrics_types.cpp new file mode 100644 index 00000000000..8a5fece54bf --- /dev/null +++ b/src/core/thread/link_metrics_types.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2020-22, 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 Thread Link Metrics. + */ + +#include "link_metrics_types.hpp" + +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE + +#include "common/code_utils.hpp" +#include "mac/mac_frame.hpp" + +namespace ot { +namespace LinkMetrics { + +//---------------------------------------------------------------------------------------------------------------------- +// Metrics + +uint8_t Metrics::ConvertToTypeIds(uint8_t aTypeIds[]) const +{ + uint8_t count = 0; + + if (mPduCount) + { + aTypeIds[count++] = TypeId::kPdu; + } + + if (mLqi) + { + aTypeIds[count++] = TypeId::kLqi; + } + + if (mLinkMargin) + { + aTypeIds[count++] = TypeId::kLinkMargin; + } + + if (mRssi) + { + aTypeIds[count++] = TypeId::kRssi; + } + +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + if (mReserved) + { + for (uint8_t i = 0; i < count; i++) + { + TypeId::MarkAsReserved(aTypeIds[i]); + } + } +#endif + + return count; +} + +//---------------------------------------------------------------------------------------------------------------------- +// SeriesFlags + +uint8_t SeriesFlags::ConvertToMask(void) const +{ + uint8_t mask = 0; + + mask |= (mLinkProbe ? kLinkProbeFlag : 0); + mask |= (mMacData ? kMacDataFlag : 0); + mask |= (mMacDataRequest ? kMacDataRequestFlag : 0); + mask |= (mMacAck ? kMacAckFlag : 0); + + return mask; +} + +void SeriesFlags::SetFrom(uint8_t aFlagsMask) +{ + mLinkProbe = (aFlagsMask & kLinkProbeFlag); + mMacData = (aFlagsMask & kMacDataFlag); + mMacDataRequest = (aFlagsMask & kMacDataRequestFlag); + mMacAck = (aFlagsMask & kMacAckFlag); +} + +//---------------------------------------------------------------------------------------------------------------------- +// SeriesInfo + +void SeriesInfo::Init(uint8_t aSeriesId, uint8_t aSeriesFlagsMask, const Metrics &aMetrics) +{ + mSeriesId = aSeriesId; + mSeriesFlags.SetFrom(aSeriesFlagsMask); + mMetrics = aMetrics; + mRssAverager.Clear(); + mLqiAverager.Clear(); + mPduCount = 0; +} + +void SeriesInfo::AggregateLinkMetrics(uint8_t aFrameType, uint8_t aLqi, int8_t aRss) +{ + if (IsFrameTypeMatch(aFrameType)) + { + mPduCount++; + mLqiAverager.Add(aLqi); + IgnoreError(mRssAverager.Add(aRss)); + } +} + +bool SeriesInfo::IsFrameTypeMatch(uint8_t aFrameType) const +{ + bool match = false; + + switch (aFrameType) + { + case kSeriesTypeLinkProbe: + VerifyOrExit(!mSeriesFlags.IsMacDataFlagSet()); // Ignore this when Mac Data is accounted + match = mSeriesFlags.IsLinkProbeFlagSet(); + break; + case Mac::Frame::kTypeData: + match = mSeriesFlags.IsMacDataFlagSet(); + break; + case Mac::Frame::kTypeMacCmd: + match = mSeriesFlags.IsMacDataRequestFlagSet(); + break; + case Mac::Frame::kTypeAck: + match = mSeriesFlags.IsMacAckFlagSet(); + break; + default: + break; + } + +exit: + return match; +} + +} // namespace LinkMetrics +} // namespace ot + +#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE diff --git a/src/core/thread/link_metrics_types.hpp b/src/core/thread/link_metrics_types.hpp new file mode 100644 index 00000000000..eb10b2c0bd5 --- /dev/null +++ b/src/core/thread/link_metrics_types.hpp @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2020-22, 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 generating and processing Link Metrics TLVs. + * + */ + +#ifndef LINK_METRICS_TYPES_HPP_ +#define LINK_METRICS_TYPES_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE + +#include + +#include "common/as_core_type.hpp" +#include "common/clearable.hpp" +#include "common/encoding.hpp" +#include "common/message.hpp" + +namespace ot { +namespace LinkMetrics { + +constexpr uint8_t kMaxTypeIds = 4; ///< Maximum number of Type IDs in a `Metrics`. + +/** + * This type represents Link Metric Flags indicating a set of metrics. + * + * @sa otLinkMetrics + * + */ +class Metrics : public otLinkMetrics, public Clearable +{ +public: + /** + * This method converts the `Metrics` into an array of Type IDs. + * + * @param[out] aTypeIds The array of Type IDs to populate. MUST have at least `kMaxTypeIds` elements. + * + * @returns Number of entries added in the array @p aTypeIds. + * + */ + uint8_t ConvertToTypeIds(uint8_t aTypeIds[]) const; +}; + +/** + * This type represents the results (values) for a set of metrics. + * + * @sa otLinkMetricsValues + * + */ +class MetricsValues : public otLinkMetricsValues, public Clearable +{ +public: + /** + * This method gets the metrics flags. + * + * @returns The metrics flags. + * + */ + Metrics &GetMetrics(void) { return static_cast(mMetrics); } + + /** + * This method gets the metrics flags. + * + * @returns The metrics flags. + * + */ + const Metrics &GetMetrics(void) const { return static_cast(mMetrics); } + + /** + * This method set the metrics flags. + * + * @param[in] aMetrics The metrics flags to set from. + * + */ + void SetMetrics(const Metrics &aMetrics) { mMetrics = aMetrics; } +}; + +class TypeId +{ + // Type ID Flags + // + // 7 6 5 4 3 2 1 0 + // +---+---+---+---+---+---+---+---+ + // | E | L | Type | Metric | + // +---+---+---+---+---+---+---+---+ + // + + static constexpr uint8_t kExtendedFlag = 1 << 7; + static constexpr uint8_t kLengthFlag = 1 << 6; + static constexpr uint8_t kTypeOffset = 3; + static constexpr uint8_t kMetricOffset = 0; + static constexpr uint8_t kTypeMask = (7 << kTypeOffset); + + static constexpr uint8_t kTypeCount = (0 << kTypeOffset); // Count/summation + static constexpr uint8_t kTypeAve = (1 << kTypeOffset); // Exponential Moving average + static constexpr uint8_t kTypeReserved = (2 << kTypeOffset); // Reserved + + static constexpr uint8_t kMetricPdu = (0 << kMetricOffset); // Number of PDUs received. + static constexpr uint8_t kMetricLqi = (1 << kMetricOffset); + static constexpr uint8_t kMetricLinkMargin = (2 << kMetricOffset); + static constexpr uint8_t kMetricRssi = (3 << kMetricOffset); + +public: + static constexpr uint8_t kPdu = (kMetricPdu | kTypeCount | kLengthFlag); ///< Type ID for num PDU received. + static constexpr uint8_t kLqi = (kMetricLqi | kTypeAve); ///< Type ID for LQI. + static constexpr uint8_t kLinkMargin = (kMetricLinkMargin | kTypeAve); ///< Type ID for Link Margin. + static constexpr uint8_t kRssi = (kMetricRssi | kTypeAve); ///< Type ID for RSSI. + + /** + * This static method indicates whether or not a given Type ID is extended. + * + * Extended Type IDs are reserved for future use. When set an additional second byte follows the current ID flags. + * + * @param[in] aTypeId The Type ID to check. + * + * @retval TRUE The @p aTypeId is extended. + * @retval FALSE The @p aTypeId is not extended. + * + */ + static bool IsExtended(uint8_t aTypeId) { return (aTypeId & kExtendedFlag); } + + /** + * This static method determines the value length (number of bytes) associated with a given Type ID. + * + * Type IDs can either have a short value as a `uint8_t` (e.g., `kLqi`, `kLinkMargin` or `kRssi`) or a long value as + * a `uint32_t` (`kPdu`). + * + * @param[in] aTypeId The Type ID. + * + * @returns the associated value length of @p aTypeId. + * + */ + static uint8_t GetValueLength(uint8_t aTypeId) + { + return (aTypeId & kLengthFlag) ? sizeof(uint32_t) : sizeof(uint8_t); + } + + /** + * This static method updates a Type ID to mark it as reversed. + * + * This is used for testing only. + * + * @param[in, out] aTypeId A reference to a Type ID variable to update. + * + */ + static void MarkAsReserved(uint8_t &aTypeId) { aTypeId = (aTypeId & ~kTypeMask) | kTypeReserved; } + + TypeId(void) = delete; +}; + +/** + * This class represents the Series Flags for Forward Tracking Series. + * + */ +class SeriesFlags : public otLinkMetricsSeriesFlags +{ +public: + /** + * This method converts the `SeriesFlags` to `uint8_t` bit-mask (for inclusion in TLVs). + * + * @returns The bit-mask representation. + * + */ + uint8_t ConvertToMask(void) const; + + /** + * This method sets the `SeriesFlags` from a given bit-mask value. + * + * @param[in] aFlagsMask The bit-mask flags. + * + */ + void SetFrom(uint8_t aFlagsMask); + + /** + * This method indicates whether or not the Link Probe flag is set. + * + * @retval true The Link Probe flag is set. + * @retval false The Link Probe flag is not set. + * + */ + bool IsLinkProbeFlagSet(void) const { return mLinkProbe; } + + /** + * This method indicates whether or not the MAC Data flag is set. + * + * @retval true The MAC Data flag is set. + * @retval false The MAC Data flag is not set. + * + */ + bool IsMacDataFlagSet(void) const { return mMacData; } + + /** + * This method indicates whether or not the MAC Data Request flag is set. + * + * @retval true The MAC Data Request flag is set. + * @retval false The MAC Data Request flag is not set. + * + */ + bool IsMacDataRequestFlagSet(void) const { return mMacDataRequest; } + + /** + * This method indicates whether or not the Mac Ack flag is set. + * + * @retval true The Mac Ack flag is set. + * @retval false The Mac Ack flag is not set. + * + */ + bool IsMacAckFlagSet(void) const { return mMacAck; } + +private: + static constexpr uint8_t kLinkProbeFlag = 1 << 0; + static constexpr uint8_t kMacDataFlag = 1 << 1; + static constexpr uint8_t kMacDataRequestFlag = 1 << 2; + static constexpr uint8_t kMacAckFlag = 1 << 3; +}; + +/** + * This enumeration type represent Enhanced-ACK Flags. + * + */ +enum EnhAckFlags : uint8_t +{ + kEnhAckClear = OT_LINK_METRICS_ENH_ACK_CLEAR, ///< Clear. + kEnhAckRegister = OT_LINK_METRICS_ENH_ACK_REGISTER, ///< Register. +}; + +/** + * This class represents one Series that is being tracked by the Subject. + * + * When an Initiator successfully configured a Forward Tracking Series, the Subject would use an instance of this class + * to track the information of the Series. The Subject has a `Pool` of `SeriesInfo`. It would allocate one when a new + * Series comes, and free it when a Series finishes. + * + * This class inherits `LinkedListEntry` and each `Neighbor` has a list of `SeriesInfo` so that the Subject could track + * per Series initiated by neighbors as long as it has available resources. + * + */ +class SeriesInfo : public LinkedListEntry +{ + friend class LinkedList; + friend class LinkedListEntry; + +public: + /** + * This constant represents Link Probe when filtering frames to be accounted using Series Flag. There's + * already `Mac::Frame::kTypeData`, `Mac::Frame::kTypeAck` and `Mac::Frame::kTypeMacCmd`. This item is + * added so that we can filter a Link Probe for series in the same way as other frames. + * + */ + static constexpr uint8_t kSeriesTypeLinkProbe = 0; + + /** + * This method initializes the SeriesInfo object. + * + * @param[in] aSeriesId The Series ID. + * @param[in] aSeriesFlagsMask The Series Flags bitmask which specify what types of frames are to be accounted. + * @param[in] aMetrics Metrics to query. + * + */ + void Init(uint8_t aSeriesId, uint8_t aSeriesFlagsMask, const Metrics &aMetrics); + + /** + * This method gets the Series ID. + * + * @returns The Series ID. + * + */ + uint8_t GetSeriesId(void) const { return mSeriesId; } + + /** + * This method gets the PDU count. + * + * @returns The PDU count. + * + */ + uint32_t GetPduCount(void) const { return mPduCount; } + + /** + * This method gets the average LQI. + * + * @returns The average LQI. + * + */ + uint8_t GetAverageLqi(void) const { return mLqiAverager.GetAverage(); } + + /** + * This method gets the average RSS. + * + * @returns The average RSS. + * + */ + int8_t GetAverageRss(void) const { return mRssAverager.GetAverage(); } + + /** + * This method aggregates the Link Metrics data of a frame into this series. + * + * @param[in] aFrameType The type of the frame. + * @param[in] aLqi The LQI value. + * @param[in] aRss The RSS value. + * + */ + void AggregateLinkMetrics(uint8_t aFrameType, uint8_t aLqi, int8_t aRss); + + /** + * This methods gets the metrics. + * + * @returns The metrics associated with `SeriesInfo`. + * + */ + const Metrics &GetLinkMetrics(void) const { return mMetrics; } + +private: + bool Matches(const uint8_t &aSeriesId) const { return mSeriesId == aSeriesId; } + bool IsFrameTypeMatch(uint8_t aFrameType) const; + + SeriesInfo *mNext; + uint8_t mSeriesId; + SeriesFlags mSeriesFlags; + Metrics mMetrics; + RssAverager mRssAverager; + LqiAverager mLqiAverager; + uint32_t mPduCount; +}; + +/** + * This enumeration type represents Link Metrics Status. + * + */ +enum Status : uint8_t +{ + kStatusSuccess = OT_LINK_METRICS_STATUS_SUCCESS, + kStatusCannotSupportNewSeries = OT_LINK_METRICS_STATUS_CANNOT_SUPPORT_NEW_SERIES, + kStatusSeriesIdAlreadyRegistered = OT_LINK_METRICS_STATUS_SERIESID_ALREADY_REGISTERED, + kStatusSeriesIdNotRecognized = OT_LINK_METRICS_STATUS_SERIESID_NOT_RECOGNIZED, + kStatusNoMatchingFramesReceived = OT_LINK_METRICS_STATUS_NO_MATCHING_FRAMES_RECEIVED, + kStatusOtherError = OT_LINK_METRICS_STATUS_OTHER_ERROR, +}; + +} // namespace LinkMetrics + +DefineCoreType(otLinkMetrics, LinkMetrics::Metrics); +DefineCoreType(otLinkMetricsValues, LinkMetrics::MetricsValues); +DefineCoreType(otLinkMetricsSeriesFlags, LinkMetrics::SeriesFlags); +DefineMapEnum(otLinkMetricsEnhAckFlags, LinkMetrics::EnhAckFlags); +DefineMapEnum(otLinkMetricsStatus, LinkMetrics::Status); + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE + +#endif // LINK_METRICS_TYPES_HPP_ diff --git a/src/core/thread/link_quality.cpp b/src/core/thread/link_quality.cpp index f3e1cb2919e..23a68c5f446 100644 --- a/src/core/thread/link_quality.cpp +++ b/src/core/thread/link_quality.cpp @@ -38,6 +38,7 @@ #include "common/code_utils.hpp" #include "common/instance.hpp" #include "common/locator_getters.hpp" +#include "common/num_utils.hpp" namespace ot { @@ -63,7 +64,7 @@ Error RssAverager::Add(int8_t aRss) Error error = kErrorNone; uint16_t newValue; - VerifyOrExit(aRss != OT_RADIO_RSSI_INVALID, error = kErrorInvalidArgs); + VerifyOrExit(aRss != Radio::kInvalidRssi, error = kErrorInvalidArgs); // Restrict the RSS value to the closed range [0, -128] so the RSS times precision multiple can fit in 11 bits. if (aRss > 0) @@ -89,7 +90,7 @@ int8_t RssAverager::GetAverage(void) const { int8_t average; - VerifyOrExit(mCount != 0, average = OT_RADIO_RSSI_INVALID); + VerifyOrExit(mCount != 0, average = Radio::kInvalidRssi); average = -static_cast(mAverage >> kPrecisionBitShift); @@ -123,7 +124,8 @@ void LqiAverager::Add(uint8_t aLqi) { mCount++; } - count = OT_MIN((1 << kCoeffBitShift), mCount); + + count = Min(static_cast(1 << kCoeffBitShift), mCount); mAverage = static_cast(((mAverage * (count - 1)) + aLqi) / count); } @@ -132,7 +134,7 @@ void LinkQualityInfo::Clear(void) { mRssAverager.Clear(); SetLinkQuality(kLinkQuality0); - mLastRss = OT_RADIO_RSSI_INVALID; + mLastRss = Radio::kInvalidRssi; mFrameErrorRate.Clear(); mMessageErrorRate.Clear(); @@ -142,7 +144,7 @@ void LinkQualityInfo::AddRss(int8_t aRss) { uint8_t oldLinkQuality = kNoLinkQuality; - VerifyOrExit(aRss != OT_RADIO_RSSI_INVALID); + VerifyOrExit(aRss != Radio::kInvalidRssi); mLastRss = aRss; @@ -161,7 +163,7 @@ void LinkQualityInfo::AddRss(int8_t aRss) uint8_t LinkQualityInfo::GetLinkMargin(void) const { - return ConvertRssToLinkMargin(Get().GetNoiseFloor(), GetAverageRss()); + return ComputeLinkMargin(Get().GetNoiseFloor(), GetAverageRss()); } LinkQualityInfo::InfoString LinkQualityInfo::ToInfoString(void) const @@ -174,11 +176,11 @@ LinkQualityInfo::InfoString LinkQualityInfo::ToInfoString(void) const return string; } -uint8_t LinkQualityInfo::ConvertRssToLinkMargin(int8_t aNoiseFloor, int8_t aRss) +uint8_t ComputeLinkMargin(int8_t aNoiseFloor, int8_t aRss) { int8_t linkMargin = aRss - aNoiseFloor; - if (linkMargin < 0 || aRss == OT_RADIO_RSSI_INVALID) + if (linkMargin < 0 || aRss == Radio::kInvalidRssi) { linkMargin = 0; } @@ -186,40 +188,58 @@ uint8_t LinkQualityInfo::ConvertRssToLinkMargin(int8_t aNoiseFloor, int8_t aRss) return static_cast(linkMargin); } -LinkQuality LinkQualityInfo::ConvertLinkMarginToLinkQuality(uint8_t aLinkMargin) -{ - return CalculateLinkQuality(aLinkMargin, kNoLinkQuality); -} - -LinkQuality LinkQualityInfo::ConvertRssToLinkQuality(int8_t aNoiseFloor, int8_t aRss) +LinkQuality LinkQualityForLinkMargin(uint8_t aLinkMargin) { - return ConvertLinkMarginToLinkQuality(ConvertRssToLinkMargin(aNoiseFloor, aRss)); + return LinkQualityInfo::CalculateLinkQuality(aLinkMargin, LinkQualityInfo::kNoLinkQuality); } -int8_t LinkQualityInfo::ConvertLinkQualityToRss(int8_t aNoiseFloor, LinkQuality aLinkQuality) +int8_t GetTypicalRssForLinkQuality(int8_t aNoiseFloor, LinkQuality aLinkQuality) { - int8_t linkmargin = 0; + int8_t linkMargin = 0; switch (aLinkQuality) { case kLinkQuality3: - linkmargin = kLinkQuality3LinkMargin; + linkMargin = LinkQualityInfo::kLinkQuality3LinkMargin; break; case kLinkQuality2: - linkmargin = kLinkQuality2LinkMargin; + linkMargin = LinkQualityInfo::kLinkQuality2LinkMargin; break; case kLinkQuality1: - linkmargin = kLinkQuality1LinkMargin; + linkMargin = LinkQualityInfo::kLinkQuality1LinkMargin; break; default: - linkmargin = kLinkQuality0LinkMargin; + linkMargin = LinkQualityInfo::kLinkQuality0LinkMargin; break; } - return linkmargin + aNoiseFloor; + return linkMargin + aNoiseFloor; +} + +uint8_t CostForLinkQuality(LinkQuality aLinkQuality) +{ + static const uint8_t kCostsForLinkQuality[] = { + kCostForLinkQuality0, // Link cost for `kLinkQuality0` (0). + kCostForLinkQuality1, // Link cost for `kLinkQuality1` (1). + kCostForLinkQuality2, // Link cost for `kLinkQuality2` (2). + kCostForLinkQuality3, // Link cost for `kLinkQuality3` (3). + }; + + static_assert(kLinkQuality0 == 0, "kLinkQuality0 is invalid"); + static_assert(kLinkQuality1 == 1, "kLinkQuality1 is invalid"); + static_assert(kLinkQuality2 == 2, "kLinkQuality2 is invalid"); + static_assert(kLinkQuality3 == 3, "kLinkQuality3 is invalid"); + + uint8_t cost = Mle::kMaxRouteCost; + + VerifyOrExit(aLinkQuality <= kLinkQuality3); + cost = kCostsForLinkQuality[aLinkQuality]; + +exit: + return cost; } LinkQuality LinkQualityInfo::CalculateLinkQuality(uint8_t aLinkMargin, uint8_t aLastLinkQuality) diff --git a/src/core/thread/link_quality.hpp b/src/core/thread/link_quality.hpp index bc10957c6c7..086d74774a8 100644 --- a/src/core/thread/link_quality.hpp +++ b/src/core/thread/link_quality.hpp @@ -41,6 +41,7 @@ #include "common/clearable.hpp" #include "common/locator.hpp" #include "common/string.hpp" +#include "thread/mle_types.hpp" namespace ot { @@ -125,13 +126,13 @@ class RssAverager : public Clearable /** * This method adds a received signal strength (RSS) value to the average. * - * If @p aRss is OT_RADIO_RSSI_INVALID, it is ignored and error status kErrorInvalidArgs is returned. + * If @p aRss is `Radio::kInvalidRssi`, it is ignored and error status kErrorInvalidArgs is returned. * The value of RSS is capped at 0dBm (i.e., for any given RSS value higher than 0dBm, 0dBm is used instead). * * @param[in] aRss Received signal strength value (in dBm) to be added to the average. * * @retval kErrorNone New RSS value added to average successfully. - * @retval kErrorInvalidArgs Value of @p aRss is OT_RADIO_RSSI_INVALID. + * @retval kErrorInvalidArgs Value of @p aRss is `Radio::kInvalidRssi`. * */ Error Add(int8_t aRss); @@ -139,7 +140,7 @@ class RssAverager : public Clearable /** * This method returns the current average signal strength value maintained by the averager. * - * @returns The current average value (in dBm) or OT_RADIO_RSSI_INVALID if no average is available. + * @returns The current average value (in dBm) or `Radio::kInvalidRssi` if no average is available. * */ int8_t GetAverage(void) const; @@ -241,6 +242,53 @@ enum LinkQuality : uint8_t kLinkQuality3 = 3, ///< Link quality 3 }; +constexpr uint8_t kCostForLinkQuality0 = Mle::kMaxRouteCost; ///< Link Cost for Link Quality 0. +constexpr uint8_t kCostForLinkQuality1 = 4; ///< Link Cost for Link Quality 1. +constexpr uint8_t kCostForLinkQuality2 = 2; ///< Link Cost for Link Quality 2. +constexpr uint8_t kCostForLinkQuality3 = 1; ///< Link Cost for Link Quality 3. + +/** + * This function converts link quality to route cost. + * + * @param[in] aLinkQuality The link quality to covert. + * + * @returns The route cost corresponding to @p aLinkQuality. + * + */ +uint8_t CostForLinkQuality(LinkQuality aLinkQuality); + +/** + * This function computes the link margin from a given noise floor and received signal strength. + * + * @param[in] aNoiseFloor The noise floor value (in dBm). + * @param[in] aRss The received signal strength value (in dBm). + * + * @returns The link margin value in dB. + * + */ +uint8_t ComputeLinkMargin(int8_t aNoiseFloor, int8_t aRss); + +/** + * This function converts a link margin value to a link quality value. + * + * @param[in] aLinkMargin The Link Margin in dB. + * + * @returns The link quality value (0-3). + * + */ +LinkQuality LinkQualityForLinkMargin(uint8_t aLinkMargin); + +/** + * This function gets the typical received signal strength value for a given link quality. + * + * @param[in] aNoiseFloor The noise floor value (in dBm). + * @param[in] aLinkQuality The link quality value in [0, 3]. + * + * @returns The typical platform RSSI in dBm. + * + */ +int8_t GetTypicalRssForLinkQuality(int8_t aNoiseFloor, LinkQuality aLinkQuality); + /** * This class encapsulates/stores all relevant information about quality of a link, including average received signal * strength (RSS), last RSS, link margin, and link quality. @@ -248,6 +296,9 @@ enum LinkQuality : uint8_t */ class LinkQualityInfo : public InstanceLocatorInit { + friend LinkQuality LinkQualityForLinkMargin(uint8_t aLinkMargin); + friend int8_t GetTypicalRssForLinkQuality(int8_t aNoiseFloor, LinkQuality aLinkQuality); + public: static constexpr uint16_t kInfoStringSize = 50; ///< `InfoString` size (@sa ToInfoString()). @@ -271,6 +322,12 @@ class LinkQualityInfo : public InstanceLocatorInit */ void Clear(void); + /** + * This method clears the average RSS value. + * + */ + void ClearAverageRss(void) { mRssAverager.Clear(); } + /** * This method adds a new received signal strength (RSS) value to the average. * @@ -282,7 +339,7 @@ class LinkQualityInfo : public InstanceLocatorInit /** * This method returns the current average received signal strength value. * - * @returns The current average value or @c OT_RADIO_RSSI_INVALID if no average is available. + * @returns The current average value or `Radio::kInvalidRssi` if no average is available. * */ int8_t GetAverageRss(void) const { return mRssAverager.GetAverage(); } @@ -387,51 +444,6 @@ class LinkQualityInfo : public InstanceLocatorInit */ uint16_t GetMessageErrorRate(void) const { return mMessageErrorRate.GetFailureRate(); } - /** - * This method converts a received signal strength value to a link margin value. - * - * @param[in] aNoiseFloor The noise floor value (in dBm). - * @param[in] aRss The received signal strength value (in dBm). - * - * @returns The link margin value. - * - */ - static uint8_t ConvertRssToLinkMargin(int8_t aNoiseFloor, int8_t aRss); - - /** - * This method converts a link margin value to a link quality value. - * - * @param[in] aLinkMargin The Link Margin in dB. - * - * @returns The link quality value (0-3). - * - */ - static LinkQuality ConvertLinkMarginToLinkQuality(uint8_t aLinkMargin); - - /** - * This method converts a received signal strength value to a link quality value. - * - * @param[in] aNoiseFloor The noise floor value (in dBm). - * @param[in] aRss The received signal strength value (in dBm). - * - * @returns The link quality value (0-3). - * - */ - static LinkQuality ConvertRssToLinkQuality(int8_t aNoiseFloor, int8_t aRss); - - /** - * This method converts a link quality value to a typical received signal strength value. - * - * @note only for test. - * - * @param[in] aNoiseFloor The noise floor value (in dBm). - * @param[in] aLinkQuality The link quality value in [0, 3]. - * - * @returns The typical platform RSSI. - * - */ - static int8_t ConvertLinkQualityToRss(int8_t aNoiseFloor, LinkQuality aLinkQuality); - private: // Constants for obtaining link quality from link margin: diff --git a/src/core/thread/lowpan.cpp b/src/core/thread/lowpan.cpp index d373fbe1ca0..a31519b3a51 100644 --- a/src/core/thread/lowpan.cpp +++ b/src/core/thread/lowpan.cpp @@ -45,7 +45,6 @@ using ot::Encoding::BigEndian::HostSwap16; using ot::Encoding::BigEndian::ReadUint16; -using ot::Encoding::BigEndian::WriteUint16; namespace ot { namespace Lowpan { @@ -55,37 +54,43 @@ Lowpan::Lowpan(Instance &aInstance) { } -void Lowpan::CopyContext(const Context &aContext, Ip6::Address &aAddress) +void Lowpan::FindContextForId(uint8_t aContextId, Context &aContext) const { - aAddress.SetPrefix(aContext.mPrefix); + if (Get().GetContext(aContextId, aContext) != kErrorNone) + { + aContext.Clear(); + } +} + +void Lowpan::FindContextToCompressAddress(const Ip6::Address &aIp6Address, Context &aContext) const +{ + Error error = Get().GetContext(aIp6Address, aContext); + + if ((error != kErrorNone) || !aContext.mCompressFlag) + { + aContext.Clear(); + } } -Error Lowpan::ComputeIid(const Mac::Address &aMacAddr, const Context &aContext, Ip6::Address &aIpAddress) +Error Lowpan::ComputeIid(const Mac::Address &aMacAddr, const Context &aContext, Ip6::InterfaceIdentifier &aIid) { Error error = kErrorNone; switch (aMacAddr.GetType()) { case Mac::Address::kTypeShort: - aIpAddress.GetIid().SetToLocator(aMacAddr.GetShort()); + aIid.SetToLocator(aMacAddr.GetShort()); break; case Mac::Address::kTypeExtended: - aIpAddress.GetIid().SetFromExtAddress(aMacAddr.GetExtended()); + aIid.SetFromExtAddress(aMacAddr.GetExtended()); break; default: ExitNow(error = kErrorParse); } - if (aContext.mPrefix.GetLength() > 64) - { - for (int i = (aContext.mPrefix.GetLength() & ~7); i < aContext.mPrefix.GetLength(); i++) - { - aIpAddress.mFields.m8[i / CHAR_BIT] &= ~(0x80 >> (i % CHAR_BIT)); - aIpAddress.mFields.m8[i / CHAR_BIT] |= aContext.mPrefix.GetBytes()[i / CHAR_BIT] & (0x80 >> (i % CHAR_BIT)); - } - } + aIid.ApplyPrefix(aContext.mPrefix); exit: return error; @@ -93,75 +98,59 @@ Error Lowpan::ComputeIid(const Mac::Address &aMacAddr, const Context &aContext, Error Lowpan::CompressSourceIid(const Mac::Address &aMacAddr, const Ip6::Address &aIpAddr, - const Context & aContext, - uint16_t & aHcCtl, - FrameBuilder & aFrameBuilder) + const Context &aContext, + uint16_t &aHcCtl, + FrameBuilder &aFrameBuilder) { - Error error = kErrorNone; - Ip6::Address ipaddr; - Mac::Address tmp; + Error error = kErrorNone; + Ip6::InterfaceIdentifier iid; - IgnoreError(ComputeIid(aMacAddr, aContext, ipaddr)); + IgnoreError(ComputeIid(aMacAddr, aContext, iid)); - if (ipaddr.GetIid() == aIpAddr.GetIid()) + if (iid == aIpAddr.GetIid()) { aHcCtl |= kHcSrcAddrMode3; } + else if (aIpAddr.GetIid().IsLocator()) + { + aHcCtl |= kHcSrcAddrMode2; + error = aFrameBuilder.AppendBigEndianUint16(aIpAddr.GetIid().GetLocator()); + } else { - tmp.SetShort(aIpAddr.GetIid().GetLocator()); - IgnoreError(ComputeIid(tmp, aContext, ipaddr)); - - if (ipaddr.GetIid() == aIpAddr.GetIid()) - { - aHcCtl |= kHcSrcAddrMode2; - SuccessOrExit(error = aFrameBuilder.AppendBytes(aIpAddr.mFields.m8 + 14, 2)); - } - else - { - aHcCtl |= kHcSrcAddrMode1; - SuccessOrExit(error = aFrameBuilder.Append(aIpAddr.GetIid())); - } + aHcCtl |= kHcSrcAddrMode1; + error = aFrameBuilder.Append(aIpAddr.GetIid()); } -exit: return error; } Error Lowpan::CompressDestinationIid(const Mac::Address &aMacAddr, const Ip6::Address &aIpAddr, - const Context & aContext, - uint16_t & aHcCtl, - FrameBuilder & aFrameBuilder) + const Context &aContext, + uint16_t &aHcCtl, + FrameBuilder &aFrameBuilder) { - Error error = kErrorNone; - Ip6::Address ipaddr; - Mac::Address tmp; + Error error = kErrorNone; + Ip6::InterfaceIdentifier iid; - IgnoreError(ComputeIid(aMacAddr, aContext, ipaddr)); + IgnoreError(ComputeIid(aMacAddr, aContext, iid)); - if (ipaddr.GetIid() == aIpAddr.GetIid()) + if (iid == aIpAddr.GetIid()) { aHcCtl |= kHcDstAddrMode3; } + else if (aIpAddr.GetIid().IsLocator()) + { + aHcCtl |= kHcDstAddrMode2; + error = aFrameBuilder.AppendBigEndianUint16(aIpAddr.GetIid().GetLocator()); + } else { - tmp.SetShort(aIpAddr.GetIid().GetLocator()); - IgnoreError(ComputeIid(tmp, aContext, ipaddr)); - - if (ipaddr.GetIid() == aIpAddr.GetIid()) - { - aHcCtl |= kHcDstAddrMode2; - SuccessOrExit(error = aFrameBuilder.AppendBytes(aIpAddr.mFields.m8 + 14, 2)); - } - else - { - aHcCtl |= kHcDstAddrMode1; - SuccessOrExit(error = aFrameBuilder.Append(aIpAddr.GetIid())); - } + aHcCtl |= kHcDstAddrMode1; + error = aFrameBuilder.Append(aIpAddr.GetIid()); } -exit: return error; } @@ -198,9 +187,10 @@ Error Lowpan::CompressMulticast(const Ip6::Address &aIpAddr, uint16_t &aHcCtl, F } else { - // Check if multicast address can be compressed using Context ID 0. - if (Get().GetContext(0, multicastContext) == kErrorNone && - multicastContext.mPrefix.GetLength() == aIpAddr.mFields.m8[3] && + // Check if multicast address can be compressed using Context ID 0 (mesh local prefix) + FindContextForId(0, multicastContext); + + if (multicastContext.mPrefix.GetLength() == aIpAddr.mFields.m8[3] && memcmp(multicastContext.mPrefix.GetBytes(), aIpAddr.mFields.m8 + 4, 8) == 0) { aHcCtl |= kHcDstAddrContext | kHcDstAddrMode0; @@ -221,10 +211,7 @@ Error Lowpan::CompressMulticast(const Ip6::Address &aIpAddr, uint16_t &aHcCtl, F return error; } -Error Lowpan::Compress(Message & aMessage, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - FrameBuilder & aFrameBuilder) +Error Lowpan::Compress(Message &aMessage, const Mac::Addresses &aMacAddrs, FrameBuilder &aFrameBuilder) { Error error = kErrorNone; uint8_t headerDepth = 0xff; @@ -233,7 +220,7 @@ Error Lowpan::Compress(Message & aMessage, { FrameBuilder frameBuilder = aFrameBuilder; - error = Compress(aMessage, aMacSource, aMacDest, aFrameBuilder, headerDepth); + error = Compress(aMessage, aMacAddrs, aFrameBuilder, headerDepth); // We exit if `Compress()` is successful. Otherwise we reset // the `aFrameBuidler` to its earlier state (remove all @@ -248,44 +235,28 @@ Error Lowpan::Compress(Message & aMessage, return error; } -Error Lowpan::Compress(Message & aMessage, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - FrameBuilder & aFrameBuilder, - uint8_t & aHeaderDepth) +Error Lowpan::Compress(Message &aMessage, + const Mac::Addresses &aMacAddrs, + FrameBuilder &aFrameBuilder, + uint8_t &aHeaderDepth) { - Error error = kErrorNone; - NetworkData::Leader &networkData = Get(); - uint16_t startOffset = aMessage.GetOffset(); - uint16_t hcCtl = kHcDispatch; - uint16_t hcCtlOffset = 0; - Ip6::Header ip6Header; - uint8_t * ip6HeaderBytes = reinterpret_cast(&ip6Header); - Context srcContext, dstContext; - bool srcContextValid, dstContextValid; - uint8_t nextHeader; - uint8_t ecn; - uint8_t dscp; - uint8_t headerDepth = 0; - uint8_t headerMaxDepth = aHeaderDepth; + Error error = kErrorNone; + uint16_t startOffset = aMessage.GetOffset(); + uint16_t hcCtl = kHcDispatch; + uint16_t hcCtlOffset = 0; + Ip6::Header ip6Header; + uint8_t *ip6HeaderBytes = reinterpret_cast(&ip6Header); + Context srcContext, dstContext; + uint8_t nextHeader; + uint8_t ecn; + uint8_t dscp; + uint8_t headerDepth = 0; + uint8_t headerMaxDepth = aHeaderDepth; SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), ip6Header)); - srcContextValid = - (networkData.GetContext(ip6Header.GetSource(), srcContext) == kErrorNone && srcContext.mCompressFlag); - - if (!srcContextValid) - { - IgnoreError(networkData.GetContext(0, srcContext)); - } - - dstContextValid = - (networkData.GetContext(ip6Header.GetDestination(), dstContext) == kErrorNone && dstContext.mCompressFlag); - - if (!dstContextValid) - { - IgnoreError(networkData.GetContext(0, dstContext)); - } + FindContextToCompressAddress(ip6Header.GetSource(), srcContext); + FindContextToCompressAddress(ip6Header.GetDestination(), dstContext); // Lowpan HC Control Bits hcCtlOffset = aFrameBuilder.GetLength(); @@ -378,12 +349,14 @@ Error Lowpan::Compress(Message & aMessage, } else if (ip6Header.GetSource().IsLinkLocal()) { - SuccessOrExit(error = CompressSourceIid(aMacSource, ip6Header.GetSource(), srcContext, hcCtl, aFrameBuilder)); + SuccessOrExit( + error = CompressSourceIid(aMacAddrs.mSource, ip6Header.GetSource(), srcContext, hcCtl, aFrameBuilder)); } - else if (srcContextValid) + else if (srcContext.mIsValid) { hcCtl |= kHcSrcAddrContext; - SuccessOrExit(error = CompressSourceIid(aMacSource, ip6Header.GetSource(), srcContext, hcCtl, aFrameBuilder)); + SuccessOrExit( + error = CompressSourceIid(aMacAddrs.mSource, ip6Header.GetSource(), srcContext, hcCtl, aFrameBuilder)); } else { @@ -397,14 +370,14 @@ Error Lowpan::Compress(Message & aMessage, } else if (ip6Header.GetDestination().IsLinkLocal()) { - SuccessOrExit( - error = CompressDestinationIid(aMacDest, ip6Header.GetDestination(), dstContext, hcCtl, aFrameBuilder)); + SuccessOrExit(error = CompressDestinationIid(aMacAddrs.mDestination, ip6Header.GetDestination(), dstContext, + hcCtl, aFrameBuilder)); } - else if (dstContextValid) + else if (dstContext.mIsValid) { hcCtl |= kHcDstAddrContext; - SuccessOrExit( - error = CompressDestinationIid(aMacDest, ip6Header.GetDestination(), dstContext, hcCtl, aFrameBuilder)); + SuccessOrExit(error = CompressDestinationIid(aMacAddrs.mDestination, ip6Header.GetDestination(), dstContext, + hcCtl, aFrameBuilder)); } else { @@ -433,7 +406,7 @@ Error Lowpan::Compress(Message & aMessage, // For IP-in-IP the NH bit of the LOWPAN_NHC encoding MUST be set to zero. SuccessOrExit(error = aFrameBuilder.AppendUint8(kExtHdrDispatch | kExtHdrEidIp6)); - error = Compress(aMessage, aMacSource, aMacDest, aFrameBuilder); + error = Compress(aMessage, aMacAddrs, aFrameBuilder); OT_FALL_THROUGH; @@ -465,7 +438,7 @@ Error Lowpan::CompressExtensionHeader(Message &aMessage, FrameBuilder &aFrameBui uint16_t startOffset = aMessage.GetOffset(); Ip6::ExtensionHeader extHeader; uint16_t len; - uint8_t padLength = 0; + uint16_t padLength = 0; uint8_t tmpByte; SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), extHeader)); @@ -488,7 +461,7 @@ Error Lowpan::CompressExtensionHeader(Message &aMessage, FrameBuilder &aFrameBui SuccessOrExit(error = aFrameBuilder.AppendUint8(tmpByte)); - len = (extHeader.GetLength() + 1) * 8 - sizeof(extHeader); + len = extHeader.GetSize() - sizeof(extHeader); // RFC 6282 does not support compressing large extension headers VerifyOrExit(len <= kExtHdrMaxLength, error = kErrorFailed); @@ -499,34 +472,23 @@ Error Lowpan::CompressExtensionHeader(Message &aMessage, FrameBuilder &aFrameBui // Pad1 or PadN option MAY be elided by the compressor." if (aNextHeader == Ip6::kProtoHopOpts || aNextHeader == Ip6::kProtoDstOpts) { - uint16_t offset = aMessage.GetOffset(); - Ip6::OptionHeader optionHeader; + uint16_t offset = aMessage.GetOffset(); + uint16_t endOffset = offset + len; + bool hasOption = false; + Ip6::Option option; - while ((offset - aMessage.GetOffset()) < len) + for (; offset < endOffset; offset += option.GetSize()) { - SuccessOrExit(error = aMessage.Read(offset, optionHeader)); - - if (optionHeader.GetType() == Ip6::OptionPad1::kType) - { - offset += sizeof(Ip6::OptionPad1); - } - else - { - offset += sizeof(optionHeader) + optionHeader.GetLength(); - } + SuccessOrExit(error = option.ParseFrom(aMessage, offset, endOffset)); + hasOption = true; } // Check if the last option can be compressed. - if (optionHeader.GetType() == Ip6::OptionPad1::kType) - { - padLength = sizeof(Ip6::OptionPad1); - } - else if (optionHeader.GetType() == Ip6::OptionPadN::kType) + if (hasOption && option.IsPadding()) { - padLength = sizeof(optionHeader) + optionHeader.GetLength(); + padLength = option.GetSize(); + len -= padLength; } - - len -= padLength; } VerifyOrExit(aMessage.GetOffset() + len + padLength <= aMessage.GetLength(), error = kErrorParse); @@ -636,19 +598,19 @@ Error Lowpan::DispatchToNextHeader(uint8_t aDispatch, uint8_t &aNextHeader) return error; } -Error Lowpan::DecompressBaseHeader(Ip6::Header & aIp6Header, - bool & aCompressedNextHeader, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - FrameData & aFrameData) +Error Lowpan::DecompressBaseHeader(Ip6::Header &aIp6Header, + bool &aCompressedNextHeader, + const Mac::Addresses &aMacAddrs, + FrameData &aFrameData) { - NetworkData::Leader &networkData = Get(); - Error error = kErrorParse; - uint16_t hcCtl; - uint8_t byte; - Context srcContext, dstContext; - bool srcContextValid = true, dstContextValid = true; - uint8_t nextHeader; + Error error = kErrorParse; + uint16_t hcCtl; + uint8_t byte; + uint8_t srcContextId = 0; + uint8_t dstContextId = 0; + Context srcContext; + Context dstContext; + uint8_t nextHeader; SuccessOrExit(aFrameData.ReadBigEndianUint16(hcCtl)); @@ -656,29 +618,17 @@ Error Lowpan::DecompressBaseHeader(Ip6::Header & aIp6Header, VerifyOrExit((hcCtl & kHcDispatchMask) == kHcDispatch); // Context Identifier - srcContext.mPrefix.SetLength(0); - dstContext.mPrefix.SetLength(0); - if ((hcCtl & kHcContextId) != 0) { SuccessOrExit(aFrameData.ReadUint8(byte)); - if (networkData.GetContext(byte >> 4, srcContext) != kErrorNone) - { - srcContextValid = false; - } - - if (networkData.GetContext(byte & 0xf, dstContext) != kErrorNone) - { - dstContextValid = false; - } - } - else - { - IgnoreError(networkData.GetContext(0, srcContext)); - IgnoreError(networkData.GetContext(0, dstContext)); + srcContextId = (byte >> 4); + dstContextId = (byte & 0xf); } + FindContextForId(srcContextId, srcContext); + FindContextForId(dstContextId, dstContext); + aIp6Header.Clear(); aIp6Header.InitVersionTrafficClassFlow(); @@ -764,7 +714,7 @@ Error Lowpan::DecompressBaseHeader(Ip6::Header & aIp6Header, break; case kHcSrcAddrMode3: - IgnoreError(ComputeIid(aMacSource, srcContext, aIp6Header.GetSource())); + IgnoreError(ComputeIid(aMacAddrs.mSource, srcContext, aIp6Header.GetSource().GetIid())); break; } @@ -776,8 +726,8 @@ Error Lowpan::DecompressBaseHeader(Ip6::Header & aIp6Header, } else { - VerifyOrExit(srcContextValid); - CopyContext(srcContext, aIp6Header.GetSource()); + VerifyOrExit(srcContext.mIsValid); + aIp6Header.GetSource().SetPrefix(srcContext.mPrefix); } } @@ -803,7 +753,7 @@ Error Lowpan::DecompressBaseHeader(Ip6::Header & aIp6Header, break; case kHcDstAddrMode3: - SuccessOrExit(ComputeIid(aMacDest, dstContext, aIp6Header.GetDestination())); + SuccessOrExit(ComputeIid(aMacAddrs.mDestination, dstContext, aIp6Header.GetDestination().GetIid())); break; } @@ -816,8 +766,8 @@ Error Lowpan::DecompressBaseHeader(Ip6::Header & aIp6Header, } else { - VerifyOrExit(dstContextValid); - CopyContext(dstContext, aIp6Header.GetDestination()); + VerifyOrExit(dstContext.mIsValid); + aIp6Header.GetDestination().SetPrefix(dstContext.mPrefix); } } else @@ -855,7 +805,7 @@ Error Lowpan::DecompressBaseHeader(Ip6::Header & aIp6Header, switch (hcCtl & kHcDstAddrModeMask) { case 0: - VerifyOrExit(dstContextValid); + VerifyOrExit(dstContext.mIsValid); SuccessOrExit(aFrameData.ReadBytes(aIp6Header.GetDestination().mFields.m8 + 1, 2)); aIp6Header.GetDestination().mFields.m8[3] = dstContext.mPrefix.GetLength(); memcpy(aIp6Header.GetDestination().mFields.m8 + 4, dstContext.mPrefix.GetBytes(), 8); @@ -883,11 +833,11 @@ Error Lowpan::DecompressBaseHeader(Ip6::Header & aIp6Header, Error Lowpan::DecompressExtensionHeader(Message &aMessage, FrameData &aFrameData) { - Error error = kErrorParse; - uint8_t hdr[2]; - uint8_t len; - uint8_t ctl; - uint8_t padLength; + Error error = kErrorParse; + uint8_t hdr[2]; + uint8_t len; + uint8_t ctl; + Ip6::PadOption padOption; SuccessOrExit(aFrameData.ReadUint8(ctl)); @@ -921,26 +871,11 @@ Error Lowpan::DecompressExtensionHeader(Message &aMessage, FrameData &aFrameData // The RFC6282 says: "The trailing Pad1 or PadN option MAY be elided by the compressor. // A decompressor MUST ensure that the containing header is padded out to a multiple of 8 octets // in length, using a Pad1 or PadN option if necessary." - padLength = 8 - ((len + sizeof(hdr)) & 0x07); - if (padLength != 8) + if (padOption.InitToPadHeaderWithSize(len + sizeof(hdr)) == kErrorNone) { - if (padLength == 1) - { - Ip6::OptionPad1 optionPad1; - - optionPad1.Init(); - SuccessOrExit(aMessage.AppendBytes(&optionPad1, padLength)); - } - else - { - Ip6::OptionPadN optionPadN; - - optionPadN.Init(padLength); - SuccessOrExit(aMessage.AppendBytes(&optionPadN, padLength)); - } - - aMessage.MoveOffset(padLength); + SuccessOrExit(aMessage.AppendBytes(&padOption, padOption.GetSize())); + aMessage.MoveOffset(padOption.GetSize()); } error = kErrorNone; @@ -1034,11 +969,10 @@ Error Lowpan::DecompressUdpHeader(Message &aMessage, FrameData &aFrameData, uint return error; } -Error Lowpan::Decompress(Message & aMessage, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - FrameData & aFrameData, - uint16_t aDatagramLength) +Error Lowpan::Decompress(Message &aMessage, + const Mac::Addresses &aMacAddrs, + FrameData &aFrameData, + uint16_t aDatagramLength) { Error error = kErrorParse; Ip6::Header ip6Header; @@ -1046,7 +980,7 @@ Error Lowpan::Decompress(Message & aMessage, uint16_t ip6PayloadLength; uint16_t currentOffset = aMessage.GetOffset(); - SuccessOrExit(DecompressBaseHeader(ip6Header, compressed, aMacSource, aMacDest, aFrameData)); + SuccessOrExit(DecompressBaseHeader(ip6Header, compressed, aMacAddrs, aFrameData)); SuccessOrExit(aMessage.Append(ip6Header)); aMessage.MoveOffset(sizeof(ip6Header)); @@ -1066,7 +1000,7 @@ Error Lowpan::Decompress(Message & aMessage, aFrameData.SkipOver(sizeof(uint8_t)); - SuccessOrExit(Decompress(aMessage, aMacSource, aMacDest, aFrameData, aDatagramLength)); + SuccessOrExit(Decompress(aMessage, aMacAddrs, aFrameData, aDatagramLength)); } else { @@ -1232,50 +1166,43 @@ void MeshHeader::DecrementHopsLeft(void) } } -uint16_t MeshHeader::WriteTo(uint8_t *aFrame) const +Error MeshHeader::AppendTo(FrameBuilder &aFrameBuilder) const { - uint8_t *cur = aFrame; - uint8_t dispatch = (kDispatch | kSourceShort | kDestShort); + Error error; + uint8_t dispatch = (kDispatch | kSourceShort | kDestShort); if (mHopsLeft < kDeepHopsLeft) { - *cur++ = (dispatch | mHopsLeft); + SuccessOrExit(error = aFrameBuilder.AppendUint8(dispatch | mHopsLeft)); } else { - *cur++ = (dispatch | kDeepHopsLeft); - *cur++ = mHopsLeft; + SuccessOrExit(error = aFrameBuilder.AppendUint8(dispatch | kDeepHopsLeft)); + SuccessOrExit(error = aFrameBuilder.AppendUint8(mHopsLeft)); } - WriteUint16(mSource, cur); - cur += sizeof(uint16_t); - - WriteUint16(mDestination, cur); - cur += sizeof(uint16_t); + SuccessOrExit(error = aFrameBuilder.AppendBigEndianUint16(mSource)); + SuccessOrExit(error = aFrameBuilder.AppendBigEndianUint16(mDestination)); - return static_cast(cur - aFrame); +exit: + return error; } Error MeshHeader::AppendTo(Message &aMessage) const { - uint8_t frame[kDeepHopsHeaderLength]; - uint16_t headerLength; + uint8_t frame[kDeepHopsHeaderLength]; + FrameBuilder frameBuilder; + + frameBuilder.Init(frame, sizeof(frame)); - headerLength = WriteTo(frame); + IgnoreError(AppendTo(frameBuilder)); - return aMessage.AppendBytes(frame, headerLength); + return aMessage.AppendBytes(frameBuilder.GetBytes(), frameBuilder.GetLength()); } //--------------------------------------------------------------------------------------------------------------------- // FragmentHeader -void FragmentHeader::Init(uint16_t aSize, uint16_t aTag, uint16_t aOffset) -{ - mSize = (aSize & kSizeMask); - mTag = aTag; - mOffset = (aOffset & kOffsetMask); -} - bool FragmentHeader::IsFragmentHeader(const FrameData &aFrameData) { return IsFragmentHeader(aFrameData.GetBytes(), aFrameData.GetLength()); @@ -1283,7 +1210,7 @@ bool FragmentHeader::IsFragmentHeader(const FrameData &aFrameData) bool FragmentHeader::IsFragmentHeader(const uint8_t *aFrame, uint16_t aFrameLength) { - return (aFrameLength >= kFirstFragmentHeaderSize) && ((*aFrame & kDispatchMask) == kDispatch); + return (aFrameLength >= sizeof(FirstFrag)) && ((*aFrame & kDispatchMask) == kDispatch); } Error FragmentHeader::ParseFrom(FrameData &aFrameData) @@ -1309,14 +1236,14 @@ Error FragmentHeader::ParseFrom(const uint8_t *aFrame, uint16_t aFrameLength, ui if ((*aFrame & kOffsetFlag) == kOffsetFlag) { - VerifyOrExit(aFrameLength >= kSubsequentFragmentHeaderSize); + VerifyOrExit(aFrameLength >= sizeof(NextFrag)); mOffset = aFrame[kOffsetIndex] * 8; - aHeaderLength = kSubsequentFragmentHeaderSize; + aHeaderLength = sizeof(NextFrag); } else { mOffset = 0; - aHeaderLength = kFirstFragmentHeaderSize; + aHeaderLength = sizeof(FirstFrag); } error = kErrorNone; @@ -1327,7 +1254,7 @@ Error FragmentHeader::ParseFrom(const uint8_t *aFrame, uint16_t aFrameLength, ui Error FragmentHeader::ParseFrom(const Message &aMessage, uint16_t aOffset, uint16_t &aHeaderLength) { - uint8_t frame[kSubsequentFragmentHeaderSize]; + uint8_t frame[sizeof(NextFrag)]; uint16_t frameLength; frameLength = aMessage.ReadBytes(aOffset, frame, sizeof(frame)); @@ -1335,24 +1262,5 @@ Error FragmentHeader::ParseFrom(const Message &aMessage, uint16_t aOffset, uint1 return ParseFrom(frame, frameLength, aHeaderLength); } -uint16_t FragmentHeader::WriteTo(uint8_t *aFrame) const -{ - uint8_t *cur = aFrame; - - WriteUint16((static_cast(kDispatch) << 8) + mSize, cur); - cur += sizeof(uint16_t); - - WriteUint16(mTag, cur); - cur += sizeof(uint16_t); - - if (mOffset != 0) - { - *aFrame |= kOffsetFlag; - *cur++ = static_cast(mOffset >> 3); - } - - return static_cast(cur - aFrame); -} - } // namespace Lowpan } // namespace ot diff --git a/src/core/thread/lowpan.hpp b/src/core/thread/lowpan.hpp index 719360790a0..77139a63134 100644 --- a/src/core/thread/lowpan.hpp +++ b/src/core/thread/lowpan.hpp @@ -36,6 +36,7 @@ #include "openthread-core-config.h" +#include "common/clearable.hpp" #include "common/debug.hpp" #include "common/frame_builder.hpp" #include "common/frame_data.hpp" @@ -73,11 +74,12 @@ using ot::Encoding::BigEndian::HostSwap16; * This structure represents a LOWPAN_IPHC Context. * */ -struct Context +struct Context : public Clearable { Ip6::Prefix mPrefix; ///< The Prefix uint8_t mContextId; ///< The Context ID. bool mCompressFlag; ///< The Context compression flag. + bool mIsValid; ///< Indicates whether the context is valid. }; /** @@ -125,17 +127,13 @@ class Lowpan : public InstanceLocator, private NonCopyable * This method compresses an IPv6 header. * * @param[in] aMessage A reference to the IPv6 message. - * @param[in] aMacSource The MAC source address. - * @param[in] aMacDest The MAC destination address. - * @param[in] aFrameBuilder The `FrameBuilder` to use to append the compressed headers. + * @param[in] aMacAddrs The MAC source and destination addresses. + * @param[in] aFrameBuilder The `FrameBuilder` to use to append the compressed headers. * * @returns The size of the compressed header in bytes. * */ - Error Compress(Message & aMessage, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - FrameBuilder & aFrameBuilder); + Error Compress(Message &aMessage, const Mac::Addresses &aMacAddrs, FrameBuilder &aFrameBuilder); /** * This method decompresses a LOWPAN_IPHC header. @@ -143,8 +141,7 @@ class Lowpan : public InstanceLocator, private NonCopyable * If the header is parsed successfully the @p aFrameData is updated to skip over the parsed header bytes. * * @param[out] aMessage A reference where the IPv6 header will be placed. - * @param[in] aMacSource The MAC source address. - * @param[in] aMacDest The MAC destination address. + * @param[in] aMacAddrs The MAC source and destination addresses. * @param[in,out] aFrameData A frame data containing the LOWPAN_IPHC header. * @param[in] aDatagramLength The IPv6 datagram length. * @@ -153,11 +150,10 @@ class Lowpan : public InstanceLocator, private NonCopyable * @retval kErrorNoBufs Could not grow @p aMessage to write the parsed IPv6 header. * */ - Error Decompress(Message & aMessage, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - FrameData & aFrameData, - uint16_t aDatagramLength); + Error Decompress(Message &aMessage, + const Mac::Addresses &aMacAddrs, + FrameData &aFrameData, + uint16_t aDatagramLength); /** * This method decompresses a LOWPAN_IPHC header. @@ -166,19 +162,17 @@ class Lowpan : public InstanceLocator, private NonCopyable * * @param[out] aIp6Header A reference where the IPv6 header will be placed. * @param[out] aCompressedNextHeader A boolean reference to output whether next header is compressed or not. - * @param[in] aMacSource The MAC source address. - * @param[in] aMacDest The MAC destination address. + * @param[in] aMacAddrs The MAC source and destination addresses * @param[in,out] aFrameData A frame data containing the LOWPAN_IPHC header. * - * @retval kErrorNone The header was decompressed successfully. @p aIp6Headre and @p aFrameData are updated. + * @retval kErrorNone The header was decompressed successfully. @p aIp6Header and @p aFrameData are updated. * @retval kErrorParse Failed to parse the lowpan header. * */ - Error DecompressBaseHeader(Ip6::Header & aIp6Header, - bool & aCompressedNextHeader, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - FrameData & aFrameData); + Error DecompressBaseHeader(Ip6::Header &aIp6Header, + bool &aCompressedNextHeader, + const Mac::Addresses &aMacAddrs, + FrameData &aFrameData); /** * This method decompresses a LOWPAN_NHC UDP header. @@ -268,23 +262,24 @@ class Lowpan : public InstanceLocator, private NonCopyable static constexpr uint8_t kUdpChecksum = 1 << 2; static constexpr uint8_t kUdpPortMask = 3 << 0; - Error Compress(Message & aMessage, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - FrameBuilder & aFrameBuilder, - uint8_t & aHeaderDepth); + void FindContextForId(uint8_t aContextId, Context &aContext) const; + void FindContextToCompressAddress(const Ip6::Address &aIp6Address, Context &aContext) const; + Error Compress(Message &aMessage, + const Mac::Addresses &aMacAddrs, + FrameBuilder &aFrameBuilder, + uint8_t &aHeaderDepth); Error CompressExtensionHeader(Message &aMessage, FrameBuilder &aFrameBuilder, uint8_t &aNextHeader); Error CompressSourceIid(const Mac::Address &aMacAddr, const Ip6::Address &aIpAddr, - const Context & aContext, - uint16_t & aHcCtl, - FrameBuilder & aFrameBuilder); + const Context &aContext, + uint16_t &aHcCtl, + FrameBuilder &aFrameBuilder); Error CompressDestinationIid(const Mac::Address &aMacAddr, const Ip6::Address &aIpAddr, - const Context & aContext, - uint16_t & aHcCtl, - FrameBuilder & aFrameBuilder); + const Context &aContext, + uint16_t &aHcCtl, + FrameBuilder &aFrameBuilder); Error CompressMulticast(const Ip6::Address &aIpAddr, uint16_t &aHcCtl, FrameBuilder &aFrameBuilder); Error CompressUdp(Message &aMessage, FrameBuilder &aFrameBuilder); @@ -292,8 +287,7 @@ class Lowpan : public InstanceLocator, private NonCopyable Error DecompressUdpHeader(Message &aMessage, FrameData &aFrameData, uint16_t aDatagramLength); Error DispatchToNextHeader(uint8_t aDispatch, uint8_t &aNextHeader); - static void CopyContext(const Context &aContext, Ip6::Address &aAddress); - static Error ComputeIid(const Mac::Address &aMacAddr, const Context &aContext, Ip6::Address &aIpAddress); + static Error ComputeIid(const Mac::Address &aMacAddr, const Context &aContext, Ip6::InterfaceIdentifier &aIid); }; /** @@ -303,12 +297,6 @@ class Lowpan : public InstanceLocator, private NonCopyable class MeshHeader { public: - /** - * The additional value that is added to predicted value of the route cost. - * - */ - static constexpr uint8_t kAdditionalHopsLeft = 1; - /** * This method initializes the Mesh Header with a given Mesh Source, Mesh Destination and Hops Left value. * @@ -428,16 +416,15 @@ class MeshHeader uint16_t GetDestination(void) const { return mDestination; } /** - * This method writes the Mesh Header into a given frame. - * - * @note This method expects the frame buffer to have enough space for the entire Mesh Header. + * This method appends the Mesh Header into a given frame. * - * @param[out] aFrame The pointer to the frame buffer to write to. + * @param[out] aFrameBuilder The `FrameBuilder` to append to. * - * @returns The header length (number of bytes written). + * @retval kErrorNone Successfully appended the MeshHeader to @p aFrameBuilder. + * @retval kErrorNoBufs Insufficient available buffers. * */ - uint16_t WriteTo(uint8_t *aFrame) const; + Error AppendTo(FrameBuilder &aFrameBuilder) const; /** * This method appends the Mesh Header to a given message. @@ -475,31 +462,70 @@ class MeshHeader class FragmentHeader { public: - static constexpr uint16_t kFirstFragmentHeaderSize = 4; ///< First fragment header size in octets. - static constexpr uint16_t kSubsequentFragmentHeaderSize = 5; ///< Subsequent fragment header size in octets. - - /** - * This method initializes the Fragment Header as a first fragment. - * - * A first fragment header starts at offset zero. - * - * @param[in] aSize The Datagram Size value. - * @param[in] aTag The Datagram Tag value. - * - */ - void InitFirstFragment(uint16_t aSize, uint16_t aTag) { Init(aSize, aTag, 0); } - - /** - * This method initializes the Fragment Header. - * - * The @p aOffset value will be truncated to become a multiple of 8. - * - * @param[in] aSize The Datagram Size value. - * @param[in] aTag The Datagram Tag value. - * @param[in] aOffset The Datagram Offset value. - * - */ - void Init(uint16_t aSize, uint16_t aTag, uint16_t aOffset); + OT_TOOL_PACKED_BEGIN + class FirstFrag + { + public: + /** + * This method initializes the `FirstFrag`. + * + * @param[in] aSize The Datagram Size value. + * @param[in] aTag The Datagram Tag value. + * + */ + void Init(uint16_t aSize, uint16_t aTag) + { + mDispatchSize = HostSwap16(kFirstDispatch | (aSize & kSizeMask)); + mTag = HostSwap16(aTag); + } + + private: + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |1 1 0 0 0| datagram_size | datagram_tag | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + static constexpr uint16_t kFirstDispatch = 0xc000; // 0b11000_0000_0000_0000 + + uint16_t mDispatchSize; + uint16_t mTag; + } OT_TOOL_PACKED_END; + + OT_TOOL_PACKED_BEGIN + class NextFrag + { + public: + /** + * This method initializes the `NextFrag`. + * + * @param[in] aSize The Datagram Size value. + * @param[in] aTag The Datagram Tag value. + * @param[in] aOffset The Datagram Offset value. + * + */ + void Init(uint16_t aSize, uint16_t aTag, uint16_t aOffset) + { + mDispatchSize = HostSwap16(kNextDispatch | (aSize & kSizeMask)); + mTag = HostSwap16(aTag); + mOffset = static_cast(aOffset >> 3); + } + + private: + // 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |1 1 1 0 0| datagram_size | datagram_tag | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // |datagram_offset| + // +-+-+-+-+-+-+-+-+ + + static constexpr uint16_t kNextDispatch = 0xe000; // 0b11100_0000_0000_0000 + + uint16_t mDispatchSize; + uint16_t mTag; + uint8_t mOffset; + } OT_TOOL_PACKED_END; /** * This static method indicates whether or not the header (in a given frame) is a Fragment Header. @@ -516,19 +542,6 @@ class FragmentHeader */ static bool IsFragmentHeader(const FrameData &aFrameData); - /** - * This method parses the Fragment Header from a frame @p aFrame. - * - * @param[in] aFrame The pointer to the frame. - * @param[in] aFrameLength The length of the frame. - * @param[out] aHeaderLength A reference to a variable to output the parsed header length (on success). - * - * @retval kErrorNone Fragment Header parsed successfully. - * @retval kErrorParse Fragment header could not be parsed from @p aFrame. - * - */ - Error ParseFrom(const uint8_t *aFrame, uint16_t aFrameLength, uint16_t &aHeaderLength); //~~~ REMOVE OR MAKE PRIVATE - /** * This method parses the Fragment Header from a given frame data. * @@ -581,18 +594,6 @@ class FragmentHeader */ uint16_t GetDatagramOffset(void) const { return mOffset; } - /** - * This method writes the Fragment Header into a given frame. - * - * @note This method expects the frame buffer to have enough space for the entire Fragment Header - * - * @param[out] aFrame The pointer to the frame buffer to write to. - * - * @returns The header length (number of bytes written). - * - */ - uint16_t WriteTo(uint8_t *aFrame) const; - private: static constexpr uint8_t kDispatch = 0xc0; // 0b1100_0000 static constexpr uint8_t kDispatchMask = 0xd8; // 0b1101_1000 accepts first (0b1100_0xxx) and next (0b1110_0xxx). @@ -607,6 +608,8 @@ class FragmentHeader static bool IsFragmentHeader(const uint8_t *aFrame, uint16_t aFrameLength); + Error ParseFrom(const uint8_t *aFrame, uint16_t aFrameLength, uint16_t &aHeaderLength); + uint16_t mSize; uint16_t mTag; uint16_t mOffset; diff --git a/src/core/thread/mesh_forwarder.cpp b/src/core/thread/mesh_forwarder.cpp index bd55d74d6e7..a88830c4b25 100644 --- a/src/core/thread/mesh_forwarder.cpp +++ b/src/core/thread/mesh_forwarder.cpp @@ -103,9 +103,9 @@ MeshForwarder::MeshForwarder(Instance &aInstance) , mSendBusy(false) #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE , mDelayNextTx(false) - , mTxDelayTimer(aInstance, HandleTxDelayTimer) + , mTxDelayTimer(aInstance) #endif - , mScheduleTransmissionTask(aInstance, MeshForwarder::ScheduleTransmissionTask) + , mScheduleTransmissionTask(aInstance) #if OPENTHREAD_FTD , mIndirectSender(aInstance) #endif @@ -164,51 +164,24 @@ void MeshForwarder::Stop(void) void MeshForwarder::PrepareEmptyFrame(Mac::TxFrame &aFrame, const Mac::Address &aMacDest, bool aAckRequest) { - uint16_t fcf = 0; - bool iePresent = CalcIePresent(nullptr); + Mac::Addresses addresses; + Mac::PanIds panIds; - Mac::Address macSource; - macSource.SetShort(Get().GetShortAddress()); + addresses.mSource.SetShort(Get().GetShortAddress()); - if (macSource.IsShortAddrInvalid() || aMacDest.IsExtended()) + if (addresses.mSource.IsShortAddrInvalid() || aMacDest.IsExtended()) { - macSource.SetExtended(Get().GetExtAddress()); + addresses.mSource.SetExtended(Get().GetExtAddress()); } - fcf = Mac::Frame::kFcfFrameData | Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfSecurityEnabled; + addresses.mDestination = aMacDest; + panIds.mSource = Get().GetPanId(); + panIds.mDestination = Get().GetPanId(); - if (iePresent) - { - fcf |= Mac::Frame::kFcfIePresent; - } - - fcf |= CalcFrameVersion(Get().FindNeighbor(aMacDest), iePresent); - - if (aAckRequest) - { - fcf |= Mac::Frame::kFcfAckRequest; - } - - fcf |= (aMacDest.IsShort()) ? Mac::Frame::kFcfDstAddrShort : Mac::Frame::kFcfDstAddrExt; - fcf |= (macSource.IsShort()) ? Mac::Frame::kFcfSrcAddrShort : Mac::Frame::kFcfSrcAddrExt; + PrepareMacHeaders(aFrame, Mac::Frame::kTypeData, addresses, panIds, Mac::Frame::kSecurityEncMic32, + Mac::Frame::kKeyIdMode1, nullptr); - aFrame.InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32); - - if (aFrame.IsDstPanIdPresent()) - { - aFrame.SetDstPanId(Get().GetPanId()); - } - IgnoreError(aFrame.SetSrcPanId(Get().GetPanId())); - - aFrame.SetDstAddr(aMacDest); - aFrame.SetSrcAddr(macSource); - aFrame.SetFramePending(false); -#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT - if (iePresent) - { - AppendHeaderIe(nullptr, aFrame); - } -#endif + aFrame.SetAckRequest(aAckRequest); aFrame.SetPayloadLength(0); } @@ -247,11 +220,6 @@ void MeshForwarder::ResumeMessageTransmissions(void) } #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE -void MeshForwarder::HandleTxDelayTimer(Timer &aTimer) -{ - aTimer.Get().HandleTxDelayTimer(); -} - void MeshForwarder::HandleTxDelayTimer(void) { mDelayNextTx = false; @@ -521,11 +489,6 @@ void MeshForwarder::ApplyDirectTxQueueLimit(Message &aMessage) #endif // (OPENTHREAD_CONFIG_MAX_FRAMES_IN_DIRECT_TX_QUEUE > 0) -void MeshForwarder::ScheduleTransmissionTask(Tasklet &aTasklet) -{ - aTasklet.Get().ScheduleTransmissionTask(); -} - void MeshForwarder::ScheduleTransmissionTask(void) { VerifyOrExit(!mSendBusy && !mTxPaused); @@ -639,13 +602,13 @@ Error MeshForwarder::UpdateIp6Route(Message &aMessage) VerifyOrExit(!ip6Header.GetSource().IsMulticast(), error = kErrorDrop); - GetMacSourceAddress(ip6Header.GetSource(), mMacSource); + GetMacSourceAddress(ip6Header.GetSource(), mMacAddrs.mSource); if (mle.IsDisabled() || mle.IsDetached()) { if (ip6Header.GetDestination().IsLinkLocal() || ip6Header.GetDestination().IsLinkLocalMulticast()) { - GetMacDestinationAddress(ip6Header.GetDestination(), mMacDest); + GetMacDestinationAddress(ip6Header.GetDestination(), mMacAddrs.mDestination); } else { @@ -663,20 +626,20 @@ Error MeshForwarder::UpdateIp6Route(Message &aMessage) if (mle.IsChild() && aMessage.IsLinkSecurityEnabled() && !aMessage.IsSubTypeMle()) { - mMacDest.SetShort(mle.GetNextHop(Mac::kShortAddrBroadcast)); + mMacAddrs.mDestination.SetShort(mle.GetNextHop(Mac::kShortAddrBroadcast)); } else { - mMacDest.SetShort(Mac::kShortAddrBroadcast); + mMacAddrs.mDestination.SetShort(Mac::kShortAddrBroadcast); } } else if (ip6Header.GetDestination().IsLinkLocal()) { - GetMacDestinationAddress(ip6Header.GetDestination(), mMacDest); + GetMacDestinationAddress(ip6Header.GetDestination(), mMacAddrs.mDestination); } else if (mle.IsMinimalEndDevice()) { - mMacDest.SetShort(mle.GetNextHop(Mac::kShortAddrBroadcast)); + mMacAddrs.mDestination.SetShort(mle.GetNextHop(Mac::kShortAddrBroadcast)); } else { @@ -691,10 +654,7 @@ Error MeshForwarder::UpdateIp6Route(Message &aMessage) return error; } -bool MeshForwarder::GetRxOnWhenIdle(void) const -{ - return Get().GetRxOnWhenIdle(); -} +bool MeshForwarder::GetRxOnWhenIdle(void) const { return Get().GetRxOnWhenIdle(); } void MeshForwarder::SetRxOnWhenIdle(bool aRxOnWhenIdle) { @@ -703,16 +663,12 @@ void MeshForwarder::SetRxOnWhenIdle(bool aRxOnWhenIdle) if (aRxOnWhenIdle) { mDataPollSender.StopPolling(); -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - Get().Stop(); -#endif + Get().Stop(); } else { mDataPollSender.StartPolling(); -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - Get().Start(); -#endif + Get().Start(); } } @@ -750,7 +706,7 @@ Mac::TxFrame *MeshForwarder::HandleFrameRequest(Mac::TxFrames &aTxFrames) VerifyOrExit(mEnabled && (mSendMessage != nullptr)); #if OPENTHREAD_CONFIG_MULTI_RADIO - frame = &Get().SelectRadio(*mSendMessage, mMacDest, aTxFrames); + frame = &Get().SelectRadio(*mSendMessage, mMacAddrs.mDestination, aTxFrames); // If multi-radio link is supported, when sending frame with link // security enabled, Fragment Header is always included (even if @@ -777,13 +733,13 @@ Mac::TxFrame *MeshForwarder::HandleFrameRequest(Mac::TxFrames &aTxFrames) VerifyOrExit(frame != nullptr); } #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - if (Get().IsCslEnabled() && mSendMessage->IsSubTypeMle()) + else if (Get().IsCslEnabled() && mSendMessage->IsSubTypeMle()) { mSendMessage->SetLinkSecurityEnabled(true); } #endif - mMessageNextOffset = PrepareDataFrame(*frame, *mSendMessage, mMacSource, mMacDest, mAddMeshHeader, mMeshSource, - mMeshDest, addFragHeader); + mMessageNextOffset = + PrepareDataFrame(*frame, *mSendMessage, mMacAddrs, mAddMeshHeader, mMeshSource, mMeshDest, addFragHeader); if ((mSendMessage->GetSubType() == Message::kSubTypeMleChildIdRequest) && mSendMessage->IsLinkSecurityEnabled()) { @@ -832,6 +788,30 @@ Mac::TxFrame *MeshForwarder::HandleFrameRequest(Mac::TxFrames &aTxFrames) return frame; } +void MeshForwarder::PrepareMacHeaders(Mac::TxFrame &aFrame, + Mac::Frame::Type aFrameType, + const Mac::Addresses &aMacAddrs, + const Mac::PanIds &aPanIds, + Mac::Frame::SecurityLevel aSecurityLevel, + Mac::Frame::KeyIdMode aKeyIdMode, + const Message *aMessage) +{ + bool iePresent; + Mac::Frame::Version version; + + iePresent = CalcIePresent(aMessage); + version = CalcFrameVersion(Get().FindNeighbor(aMacAddrs.mDestination), iePresent); + + aFrame.InitMacHeader(aFrameType, version, aMacAddrs, aPanIds, aSecurityLevel, aKeyIdMode); + +#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT + if (iePresent) + { + AppendHeaderIe(aMessage, aFrame); + } +#endif +} + // This method constructs a MAC data from from a given IPv6 message. // // This method handles generation of MAC header, mesh header (if @@ -843,151 +823,78 @@ Mac::TxFrame *MeshForwarder::HandleFrameRequest(Mac::TxFrames &aTxFrames) // It returns the next offset into the message after the prepared // frame. // -uint16_t MeshForwarder::PrepareDataFrame(Mac::TxFrame & aFrame, - Message & aMessage, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - bool aAddMeshHeader, - uint16_t aMeshSource, - uint16_t aMeshDest, - bool aAddFragHeader) +uint16_t MeshForwarder::PrepareDataFrame(Mac::TxFrame &aFrame, + Message &aMessage, + const Mac::Addresses &aMacAddrs, + bool aAddMeshHeader, + uint16_t aMeshSource, + uint16_t aMeshDest, + bool aAddFragHeader) { - uint16_t fcf; - uint8_t *payload; - uint8_t headerLength; - uint16_t maxPayloadLength; - uint16_t payloadLength; - uint16_t fragmentLength; - uint16_t dstpan; - uint8_t secCtl; - uint16_t nextOffset; - bool iePresent = CalcIePresent(&aMessage); + Mac::Frame::SecurityLevel securityLevel; + Mac::Frame::KeyIdMode keyIdMode; + Mac::PanIds panIds; + uint16_t payloadLength; + uint16_t origMsgOffset; + uint16_t nextOffset; + FrameBuilder frameBuilder; start: - // Initialize MAC header - fcf = Mac::Frame::kFcfFrameData; - - fcf |= (aMacDest.IsShort()) ? Mac::Frame::kFcfDstAddrShort : Mac::Frame::kFcfDstAddrExt; - fcf |= (aMacSource.IsShort()) ? Mac::Frame::kFcfSrcAddrShort : Mac::Frame::kFcfSrcAddrExt; - - if (iePresent) - { - fcf |= Mac::Frame::kFcfIePresent; - } - - fcf |= CalcFrameVersion(Get().FindNeighbor(aMacDest), iePresent); - - // All unicast frames request ACK - if (aMacDest.IsExtended() || !aMacDest.IsBroadcast()) - { - fcf |= Mac::Frame::kFcfAckRequest; - } + securityLevel = Mac::Frame::kSecurityNone; + keyIdMode = Mac::Frame::kKeyIdMode1; if (aMessage.IsLinkSecurityEnabled()) { - fcf |= Mac::Frame::kFcfSecurityEnabled; + securityLevel = Mac::Frame::kSecurityEncMic32; switch (aMessage.GetSubType()) { case Message::kSubTypeJoinerEntrust: - secCtl = static_cast(Mac::Frame::kKeyIdMode0); + keyIdMode = Mac::Frame::kKeyIdMode0; break; case Message::kSubTypeMleAnnounce: - secCtl = static_cast(Mac::Frame::kKeyIdMode2); + keyIdMode = Mac::Frame::kKeyIdMode2; break; default: - secCtl = static_cast(Mac::Frame::kKeyIdMode1); + // Use the `kKeyIdMode1` break; } - - secCtl |= Mac::Frame::kSecEncMic32; - } - else - { - secCtl = Mac::Frame::kSecNone; } - dstpan = Get().GetPanId(); + panIds.mSource = Get().GetPanId(); + panIds.mDestination = Get().GetPanId(); switch (aMessage.GetSubType()) { case Message::kSubTypeMleAnnounce: aFrame.SetChannel(aMessage.GetChannel()); - dstpan = Mac::kPanIdBroadcast; + aFrame.SetRxChannelAfterTxDone(Get().GetPanChannel()); + panIds.mDestination = Mac::kPanIdBroadcast; break; case Message::kSubTypeMleDiscoverRequest: case Message::kSubTypeMleDiscoverResponse: - dstpan = aMessage.GetPanId(); + panIds.mDestination = aMessage.GetPanId(); break; default: break; } - // Handle special case in 15.4-2015: - // Dest Address: Extended - // Source Address: Extended - // Dest PanId: Present - // Src Panid: Not Present - // Pan ID Compression: 0 - if (dstpan == Get().GetPanId() && - ((fcf & Mac::Frame::kFcfFrameVersionMask) == Mac::Frame::kFcfFrameVersion2006 || - (fcf & Mac::Frame::kFcfDstAddrMask) != Mac::Frame::kFcfDstAddrExt || - (fcf & Mac::Frame::kFcfSrcAddrMask) != Mac::Frame::kFcfSrcAddrExt)) - { -#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT - // Handle a special case in IEEE 802.15.4-2015, when Pan ID Compression is 0, but Src Pan ID is not present: - // Dest Address: Extended - // Src Address: Extended - // Dest Pan ID: Present - // Src Pan ID: Not Present - // Pan ID Compression: 0 - - if ((fcf & Mac::Frame::kFcfFrameVersionMask) != Mac::Frame::kFcfFrameVersion2015 || - (fcf & Mac::Frame::kFcfDstAddrMask) != Mac::Frame::kFcfDstAddrExt || - (fcf & Mac::Frame::kFcfSrcAddrMask) != Mac::Frame::kFcfSrcAddrExt) -#endif - { - fcf |= Mac::Frame::kFcfPanidCompression; - } - } - - aFrame.InitMacHeader(fcf, secCtl); - - if (aFrame.IsDstPanIdPresent()) - { - aFrame.SetDstPanId(dstpan); - } - - IgnoreError(aFrame.SetSrcPanId(Get().GetPanId())); - aFrame.SetDstAddr(aMacDest); - aFrame.SetSrcAddr(aMacSource); - -#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT - if (iePresent) - { - AppendHeaderIe(&aMessage, aFrame); - } -#endif - - payload = aFrame.GetPayload(); - maxPayloadLength = aFrame.GetMaxPayloadLength(); + PrepareMacHeaders(aFrame, Mac::Frame::kTypeData, aMacAddrs, panIds, securityLevel, keyIdMode, &aMessage); - headerLength = 0; + frameBuilder.Init(aFrame.GetPayload(), aFrame.GetMaxPayloadLength()); #if OPENTHREAD_FTD // Initialize Mesh header if (aAddMeshHeader) { - Mle::MleRouter & mle = Get(); Lowpan::MeshHeader meshHeader; - uint16_t meshHeaderLength; - uint8_t hopsLeft; + uint16_t maxPayloadLength; // Mesh Header frames are forwarded by routers over multiple // hops to reach a final destination. The forwarding path can @@ -1007,82 +914,72 @@ uint16_t MeshForwarder::PrepareDataFrame(Mac::TxFrame & aFrame, maxPayloadLength = kMeshHeaderFrameMtu - aFrame.GetHeaderLength() - (aFrame.GetFooterLength() - aFrame.GetFcsSize() + kMeshHeaderFrameFcsSize); - if (mle.IsChild()) - { - // REED sets hopsLeft to max (16) + 1. It does not know the route cost. - hopsLeft = Mle::kMaxRouteCost + 1; - } - else - { - // Calculate the number of predicted hops. - hopsLeft = mle.GetRouteCost(aMeshDest); - - if (hopsLeft != Mle::kMaxRouteCost) - { - hopsLeft += mle.GetLinkCost(Mle::Mle::RouterIdFromRloc16(mle.GetNextHop(aMeshDest))); - } - else - { - // In case there is no route to the destination router (only link). - hopsLeft = mle.GetLinkCost(Mle::Mle::RouterIdFromRloc16(aMeshDest)); - } - } + frameBuilder.Init(aFrame.GetPayload(), maxPayloadLength); - // The hopsLft field MUST be incremented by one if the - // destination RLOC16 is not that of an active Router. - if (!Mle::Mle::IsActiveRouter(aMeshDest)) - { - hopsLeft += 1; - } + meshHeader.Init(aMeshSource, aMeshDest, kMeshHeaderHopsLeft); - meshHeader.Init(aMeshSource, aMeshDest, hopsLeft + Lowpan::MeshHeader::kAdditionalHopsLeft); - meshHeaderLength = meshHeader.WriteTo(payload); - payload += meshHeaderLength; - headerLength += meshHeaderLength; + IgnoreError(meshHeader.AppendTo(frameBuilder)); } -#endif +#endif // OPENTHREAD_FTD + + // While performing lowpan compression, the message offset may be + // changed to skip over the compressed IPv6 headers, we save the + // original offset and set it back on `aMessage` at the end + // before returning. + + origMsgOffset = aMessage.GetOffset(); // Compress IPv6 Header if (aMessage.GetOffset() == 0) { - FrameBuilder frameBuilder; - uint8_t hcLength; - Mac::Address meshSource, meshDest; + uint16_t fragHeaderOffset; + uint16_t maxFrameLength; + Mac::Addresses macAddrs; - frameBuilder.Init(payload, maxPayloadLength - headerLength - Lowpan::FragmentHeader::kFirstFragmentHeaderSize); + // Before performing lowpan header compression, we reduce the + // max length on `frameBuilder` to reserve bytes for first + // fragment header. This ensures that lowpan compression will + // leave room for a first fragment header. After the lowpan + // header compression is done, we reclaim the reserved bytes + // by setting the max length back to its original value. + + fragHeaderOffset = frameBuilder.GetLength(); + maxFrameLength = frameBuilder.GetMaxLength(); + frameBuilder.SetMaxLength(maxFrameLength - sizeof(Lowpan::FragmentHeader::FirstFrag)); if (aAddMeshHeader) { - meshSource.SetShort(aMeshSource); - meshDest.SetShort(aMeshDest); + macAddrs.mSource.SetShort(aMeshSource); + macAddrs.mDestination.SetShort(aMeshDest); } else { - meshSource = aMacSource; - meshDest = aMacDest; + macAddrs = aMacAddrs; } - SuccessOrAssert(Get().Compress(aMessage, meshSource, meshDest, frameBuilder)); + SuccessOrAssert(Get().Compress(aMessage, macAddrs, frameBuilder)); + + frameBuilder.SetMaxLength(maxFrameLength); - hcLength = static_cast(frameBuilder.GetLength()); - headerLength += hcLength; - payloadLength = aMessage.GetLength() - aMessage.GetOffset(); - fragmentLength = maxPayloadLength - headerLength; + payloadLength = aMessage.GetLength() - aMessage.GetOffset(); - if ((payloadLength > fragmentLength) || aAddFragHeader) + if (aAddFragHeader || (payloadLength > frameBuilder.GetRemainingLength())) { - Lowpan::FragmentHeader fragmentHeader; + Lowpan::FragmentHeader::FirstFrag firstFragHeader; if ((!aMessage.IsLinkSecurityEnabled()) && aMessage.IsSubTypeMle()) { - // Enable security and try again. + // MLE messages that require fragmentation MUST use + // link-layer security. We enable security and try + // constructing the frame again. + aMessage.SetOffset(0); aMessage.SetLinkSecurityEnabled(true); goto start; } - // Write Fragment header + // Insert Fragment header if (aMessage.GetDatagramTag() == 0) { // Avoid using datagram tag value 0, which indicates the tag has not been set @@ -1094,59 +991,31 @@ uint16_t MeshForwarder::PrepareDataFrame(Mac::TxFrame & aFrame, aMessage.SetDatagramTag(mFragTag++); } - memmove(payload + Lowpan::FragmentHeader::kFirstFragmentHeaderSize, payload, hcLength); - - fragmentHeader.InitFirstFragment(aMessage.GetLength(), static_cast(aMessage.GetDatagramTag())); - fragmentHeader.WriteTo(payload); - - payload += Lowpan::FragmentHeader::kFirstFragmentHeaderSize; - headerLength += Lowpan::FragmentHeader::kFirstFragmentHeaderSize; - - fragmentLength = maxPayloadLength - headerLength; - - if (payloadLength > fragmentLength) - { - payloadLength = fragmentLength & ~0x7; - } + firstFragHeader.Init(aMessage.GetLength(), static_cast(aMessage.GetDatagramTag())); + SuccessOrAssert(frameBuilder.Insert(fragHeaderOffset, firstFragHeader)); } - - payload += hcLength; - - // copy IPv6 Payload - aMessage.ReadBytes(aMessage.GetOffset(), payload, payloadLength); - aFrame.SetPayloadLength(headerLength + payloadLength); - - nextOffset = aMessage.GetOffset() + payloadLength; - aMessage.SetOffset(0); } else { - Lowpan::FragmentHeader fragmentHeader; - uint16_t fragmentHeaderLength; + Lowpan::FragmentHeader::NextFrag nextFragHeader; - payloadLength = aMessage.GetLength() - aMessage.GetOffset(); - - // Write Fragment header - fragmentHeader.Init(aMessage.GetLength(), static_cast(aMessage.GetDatagramTag()), + nextFragHeader.Init(aMessage.GetLength(), static_cast(aMessage.GetDatagramTag()), aMessage.GetOffset()); - fragmentHeaderLength = fragmentHeader.WriteTo(payload); + SuccessOrAssert(frameBuilder.Append(nextFragHeader)); - payload += fragmentHeaderLength; - headerLength += fragmentHeaderLength; - - fragmentLength = maxPayloadLength - headerLength; + payloadLength = aMessage.GetLength() - aMessage.GetOffset(); + } - if (payloadLength > fragmentLength) - { - payloadLength = (fragmentLength & ~0x7); - } + if (payloadLength > frameBuilder.GetRemainingLength()) + { + payloadLength = (frameBuilder.GetRemainingLength() & ~0x7); + } - // Copy IPv6 Payload - aMessage.ReadBytes(aMessage.GetOffset(), payload, payloadLength); - aFrame.SetPayloadLength(headerLength + payloadLength); + // Copy IPv6 Payload + SuccessOrAssert(frameBuilder.AppendBytesFromMessage(aMessage, aMessage.GetOffset(), payloadLength)); + aFrame.SetPayloadLength(frameBuilder.GetLength()); - nextOffset = aMessage.GetOffset() + payloadLength; - } + nextOffset = aMessage.GetOffset() + payloadLength; if (nextOffset < aMessage.GetLength()) { @@ -1156,10 +1025,12 @@ uint16_t MeshForwarder::PrepareDataFrame(Mac::TxFrame & aFrame, #endif } + aMessage.SetOffset(origMsgOffset); + return nextOffset; } -Neighbor *MeshForwarder::UpdateNeighborOnSentFrame(Mac::TxFrame & aFrame, +Neighbor *MeshForwarder::UpdateNeighborOnSentFrame(Mac::TxFrame &aFrame, Error aError, const Mac::Address &aMacDest, bool aIsDataPoll) @@ -1218,7 +1089,7 @@ void MeshForwarder::UpdateNeighborLinkFailures(Neighbor &aNeighbor, { aNeighbor.IncrementLinkFailures(); - if (aAllowNeighborRemove && (Mle::Mle::IsActiveRouter(aNeighbor.GetRloc16())) && + if (aAllowNeighborRemove && (Mle::IsActiveRouter(aNeighbor.GetRloc16())) && (aNeighbor.GetLinkFailures() >= aFailLimit)) { Get().RemoveRouterLink(static_cast(aNeighbor)); @@ -1254,7 +1125,7 @@ void MeshForwarder::HandleDeferredAck(Neighbor &aNeighbor, Error aError) void MeshForwarder::HandleSentFrame(Mac::TxFrame &aFrame, Error aError) { - Neighbor * neighbor = nullptr; + Neighbor *neighbor = nullptr; Mac::Address macDest; OT_ASSERT((aError == kErrorNone) || (aError == kErrorChannelAccessFailure) || (aError == kErrorAbort) || @@ -1268,7 +1139,7 @@ void MeshForwarder::HandleSentFrame(Mac::TxFrame &aFrame, Error aError) if (mDelayNextTx && (aError == kErrorNone)) { mTxDelayTimer.Start(kTxDelayInterval); - LogDebg("Start tx delay timer for %u msec", kTxDelayInterval); + LogDebg("Start tx delay timer for %lu msec", ToUlong(kTxDelayInterval)); } else { @@ -1414,40 +1285,37 @@ void MeshForwarder::RemoveMessageIfNoPendingTx(Message &aMessage) void MeshForwarder::HandleReceivedFrame(Mac::RxFrame &aFrame) { ThreadLinkInfo linkInfo; - Mac::Address macDest; - Mac::Address macSource; + Mac::Addresses macAddrs; FrameData frameData; Error error = kErrorNone; VerifyOrExit(mEnabled, error = kErrorInvalidState); - SuccessOrExit(error = aFrame.GetSrcAddr(macSource)); - SuccessOrExit(error = aFrame.GetDstAddr(macDest)); + SuccessOrExit(error = aFrame.GetSrcAddr(macAddrs.mSource)); + SuccessOrExit(error = aFrame.GetDstAddr(macAddrs.mDestination)); linkInfo.SetFrom(aFrame); frameData.Init(aFrame.GetPayload(), aFrame.GetPayloadLength()); -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - Get().UpdateOnReceive(macSource, linkInfo.IsLinkSecurityEnabled()); -#endif + Get().UpdateOnReceive(macAddrs.mSource, linkInfo.IsLinkSecurityEnabled()); switch (aFrame.GetType()) { - case Mac::Frame::kFcfFrameData: + case Mac::Frame::kTypeData: if (Lowpan::MeshHeader::IsMeshHeader(frameData)) { #if OPENTHREAD_FTD - HandleMesh(frameData, macSource, linkInfo); + HandleMesh(frameData, macAddrs.mSource, linkInfo); #endif } else if (Lowpan::FragmentHeader::IsFragmentHeader(frameData)) { - HandleFragment(frameData, macSource, macDest, linkInfo); + HandleFragment(frameData, macAddrs, linkInfo); } else if (Lowpan::Lowpan::IsLowpanHc(frameData)) { - HandleLowpanHC(frameData, macSource, macDest, linkInfo); + HandleLowpanHC(frameData, macAddrs, linkInfo); } else { @@ -1458,7 +1326,7 @@ void MeshForwarder::HandleReceivedFrame(Mac::RxFrame &aFrame) break; - case Mac::Frame::kFcfFrameBeacon: + case Mac::Frame::kTypeBeacon: break; default: @@ -1474,14 +1342,13 @@ void MeshForwarder::HandleReceivedFrame(Mac::RxFrame &aFrame) } } -void MeshForwarder::HandleFragment(FrameData & aFrameData, - const Mac::Address & aMacSource, - const Mac::Address & aMacDest, +void MeshForwarder::HandleFragment(FrameData &aFrameData, + const Mac::Addresses &aMacAddrs, const ThreadLinkInfo &aLinkInfo) { Error error = kErrorNone; Lowpan::FragmentHeader fragmentHeader; - Message * message = nullptr; + Message *message = nullptr; SuccessOrExit(error = fragmentHeader.ParseFrom(aFrameData)); @@ -1489,7 +1356,7 @@ void MeshForwarder::HandleFragment(FrameData & aFrameData, if (aLinkInfo.mLinkSecurity) { - Neighbor *neighbor = Get().FindNeighbor(aMacSource, Neighbor::kInStateAnyExceptInvalid); + Neighbor *neighbor = Get().FindNeighbor(aMacAddrs.mSource, Neighbor::kInStateAnyExceptInvalid); if (neighbor != nullptr) { @@ -1525,10 +1392,10 @@ void MeshForwarder::HandleFragment(FrameData & aFrameData, uint16_t datagramSize = fragmentHeader.GetDatagramSize(); #if OPENTHREAD_FTD - UpdateRoutes(aFrameData, aMacSource, aMacDest); + UpdateRoutes(aFrameData, aMacAddrs); #endif - SuccessOrExit(error = FrameToMessage(aFrameData, datagramSize, aMacSource, aMacDest, message)); + SuccessOrExit(error = FrameToMessage(aFrameData, datagramSize, aMacAddrs, message)); VerifyOrExit(datagramSize >= message->GetLength(), error = kErrorParse); SuccessOrExit(error = message->SetLength(datagramSize)); @@ -1540,7 +1407,7 @@ void MeshForwarder::HandleFragment(FrameData & aFrameData, VerifyOrExit(Get().Accept(*message), error = kErrorDrop); #if OPENTHREAD_FTD - SendIcmpErrorIfDstUnreach(*message, aMacSource, aMacDest); + SendIcmpErrorIfDstUnreach(*message, aMacAddrs); #endif // Allow re-assembly of only one message at a time on a SED by clearing @@ -1601,12 +1468,12 @@ void MeshForwarder::HandleFragment(FrameData & aFrameData, if (message->GetOffset() >= message->GetLength()) { mReassemblyList.Dequeue(*message); - IgnoreError(HandleDatagram(*message, aLinkInfo, aMacSource)); + IgnoreError(HandleDatagram(*message, aLinkInfo, aMacAddrs.mSource)); } } else { - LogFragmentFrameDrop(error, aFrameData.GetLength(), aMacSource, aMacDest, fragmentHeader, + LogFragmentFrameDrop(error, aFrameData.GetLength(), aMacAddrs, fragmentHeader, aLinkInfo.IsLinkSecurityEnabled()); FreeMessage(message); } @@ -1629,15 +1496,15 @@ void MeshForwarder::ClearReassemblyList(void) void MeshForwarder::HandleTimeTick(void) { - bool contineRxingTicks = false; + bool continueRxingTicks = false; #if OPENTHREAD_FTD - contineRxingTicks = mFragmentPriorityList.UpdateOnTimeTick(); + continueRxingTicks = mFragmentPriorityList.UpdateOnTimeTick(); #endif - contineRxingTicks = UpdateReassemblyList() || contineRxingTicks; + continueRxingTicks = UpdateReassemblyList() || continueRxingTicks; - if (!contineRxingTicks) + if (!continueRxingTicks) { Get().UnregisterReceiver(TimeTicker::kMeshForwarder); } @@ -1665,22 +1532,21 @@ bool MeshForwarder::UpdateReassemblyList(void) return mReassemblyList.GetHead() != nullptr; } -Error MeshForwarder::FrameToMessage(const FrameData & aFrameData, - uint16_t aDatagramSize, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - Message *& aMessage) +Error MeshForwarder::FrameToMessage(const FrameData &aFrameData, + uint16_t aDatagramSize, + const Mac::Addresses &aMacAddrs, + Message *&aMessage) { Error error = kErrorNone; FrameData frameData = aFrameData; Message::Priority priority; - SuccessOrExit(error = GetFramePriority(frameData, aMacSource, aMacDest, priority)); + SuccessOrExit(error = GetFramePriority(frameData, aMacAddrs, priority)); aMessage = Get().Allocate(Message::kTypeIp6, /* aReserveHeader */ 0, Message::Settings(priority)); VerifyOrExit(aMessage, error = kErrorNoBufs); - SuccessOrExit(error = Get().Decompress(*aMessage, aMacSource, aMacDest, frameData, aDatagramSize)); + SuccessOrExit(error = Get().Decompress(*aMessage, aMacAddrs, frameData, aDatagramSize)); SuccessOrExit(error = aMessage->AppendData(frameData)); aMessage->MoveOffset(frameData.GetLength()); @@ -1689,45 +1555,42 @@ Error MeshForwarder::FrameToMessage(const FrameData & aFrameData, return error; } -void MeshForwarder::HandleLowpanHC(const FrameData & aFrameData, - const Mac::Address & aMacSource, - const Mac::Address & aMacDest, +void MeshForwarder::HandleLowpanHC(const FrameData &aFrameData, + const Mac::Addresses &aMacAddrs, const ThreadLinkInfo &aLinkInfo) { Error error = kErrorNone; Message *message = nullptr; #if OPENTHREAD_FTD - UpdateRoutes(aFrameData, aMacSource, aMacDest); + UpdateRoutes(aFrameData, aMacAddrs); #endif - SuccessOrExit(error = FrameToMessage(aFrameData, 0, aMacSource, aMacDest, message)); + SuccessOrExit(error = FrameToMessage(aFrameData, 0, aMacAddrs, message)); message->SetLinkInfo(aLinkInfo); VerifyOrExit(Get().Accept(*message), error = kErrorDrop); #if OPENTHREAD_FTD - SendIcmpErrorIfDstUnreach(*message, aMacSource, aMacDest); + SendIcmpErrorIfDstUnreach(*message, aMacAddrs); #endif exit: if (error == kErrorNone) { - IgnoreError(HandleDatagram(*message, aLinkInfo, aMacSource)); + IgnoreError(HandleDatagram(*message, aLinkInfo, aMacAddrs.mSource)); } else { - LogLowpanHcFrameDrop(error, aFrameData.GetLength(), aMacSource, aMacDest, aLinkInfo.IsLinkSecurityEnabled()); + LogLowpanHcFrameDrop(error, aFrameData.GetLength(), aMacAddrs, aLinkInfo.IsLinkSecurityEnabled()); FreeMessage(message); } } Error MeshForwarder::HandleDatagram(Message &aMessage, const ThreadLinkInfo &aLinkInfo, const Mac::Address &aMacSource) { - ThreadNetif &netif = Get(); - #if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE Get().RecordRxMessage(aMessage, aMacSource); #endif @@ -1739,18 +1602,17 @@ Error MeshForwarder::HandleDatagram(Message &aMessage, const ThreadLinkInfo &aLi mIpCounters.mRxSuccess++; } - return Get().HandleDatagram(aMessage, &netif, &aLinkInfo, false); + return Get().HandleDatagram(aMessage, Ip6::Ip6::kFromThreadNetif, &aLinkInfo); } -Error MeshForwarder::GetFramePriority(const FrameData & aFrameData, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - Message::Priority & aPriority) +Error MeshForwarder::GetFramePriority(const FrameData &aFrameData, + const Mac::Addresses &aMacAddrs, + Message::Priority &aPriority) { Error error = kErrorNone; Ip6::Headers headers; - SuccessOrExit(error = headers.DecompressFrom(aFrameData, aMacSource, aMacDest, GetInstance())); + SuccessOrExit(error = headers.DecompressFrom(aFrameData, aMacAddrs, GetInstance())); aPriority = Ip6::Ip6::DscpToPriority(headers.GetIp6Header().GetDscp()); @@ -1764,10 +1626,14 @@ Error MeshForwarder::GetFramePriority(const FrameData & aFrameData, { uint16_t destPort = headers.GetUdpHeader().GetDestinationPort(); - if ((destPort == Mle::kUdpPort) || (destPort == Tmf::kUdpPort)) + if (destPort == Mle::kUdpPort) { aPriority = Message::kPriorityNet; } + else if (Get().IsTmfMessage(headers.GetSourceAddress(), headers.GetDestinationAddress(), destPort)) + { + aPriority = Tmf::Agent::DscpToPriority(headers.GetIp6Header().GetDscp()); + } } exit: @@ -1807,7 +1673,10 @@ bool MeshForwarder::CalcIePresent(const Message *aMessage) iePresent |= (aMessage != nullptr && aMessage->IsTimeSync()); #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - iePresent |= Get().IsCslEnabled(); + if (!(aMessage != nullptr && aMessage->GetSubType() == Message::kSubTypeMleDiscoverRequest)) + { + iePresent |= Get().IsCslEnabled(); + } #endif #endif @@ -1820,7 +1689,7 @@ void MeshForwarder::AppendHeaderIe(const Message *aMessage, Mac::TxFrame &aFrame uint8_t index = 0; bool iePresent = false; bool payloadPresent = - (aFrame.GetType() == Mac::Frame::kFcfFrameMacCmd) || (aMessage != nullptr && aMessage->GetLength() != 0); + (aFrame.GetType() == Mac::Frame::kTypeMacCmd) || (aMessage != nullptr && aMessage->GetLength() != 0); #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE if (aMessage != nullptr && aMessage->IsTimeSync()) @@ -1850,26 +1719,26 @@ void MeshForwarder::AppendHeaderIe(const Message *aMessage, Mac::TxFrame &aFrame } #endif -uint16_t MeshForwarder::CalcFrameVersion(const Neighbor *aNeighbor, bool aIePresent) +Mac::Frame::Version MeshForwarder::CalcFrameVersion(const Neighbor *aNeighbor, bool aIePresent) const { - uint16_t version = Mac::Frame::kFcfFrameVersion2006; + Mac::Frame::Version version = Mac::Frame::kVersion2006; OT_UNUSED_VARIABLE(aNeighbor); if (aIePresent) { - version = Mac::Frame::kFcfFrameVersion2015; + version = Mac::Frame::kVersion2015; } #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE - else if (aNeighbor != nullptr && !Mle::MleRouter::IsActiveRouter(aNeighbor->GetRloc16()) && + else if ((aNeighbor != nullptr) && Get().Contains(*aNeighbor) && static_cast(aNeighbor)->IsCslSynchronized()) { - version = Mac::Frame::kFcfFrameVersion2015; + version = Mac::Frame::kVersion2015; } #endif #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE else if (aNeighbor != nullptr && aNeighbor->IsEnhAckProbingActive()) { - version = Mac::Frame::kFcfFrameVersion2015; ///< Set version to 2015 to fetch Link Metrics data in Enh-ACK. + version = Mac::Frame::kVersion2015; ///< Set version to 2015 to fetch Link Metrics data in Enh-ACK. } #endif @@ -1956,13 +1825,11 @@ void MeshForwarder::LogIp6SourceDestAddresses(const Ip6::Headers &aHeaders, LogL } } #else -void MeshForwarder::LogIp6SourceDestAddresses(const Ip6::Headers &, LogLevel) -{ -} +void MeshForwarder::LogIp6SourceDestAddresses(const Ip6::Headers &, LogLevel) {} #endif void MeshForwarder::LogIp6Message(MessageAction aAction, - const Message & aMessage, + const Message &aMessage, const Mac::Address *aMacAddress, Error aError, LogLevel aLogLevel) @@ -1970,7 +1837,7 @@ void MeshForwarder::LogIp6Message(MessageAction aAction, Ip6::Headers headers; bool shouldLogRss; bool shouldLogRadio = false; - const char * radioString = ""; + const char *radioString = ""; SuccessOrExit(headers.ParseFrom(aMessage)); @@ -2002,7 +1869,7 @@ void MeshForwarder::LogIp6Message(MessageAction aAction, } void MeshForwarder::LogMessage(MessageAction aAction, - const Message & aMessage, + const Message &aMessage, Error aError, const Mac::Address *aMacAddress) @@ -2069,49 +1936,37 @@ void MeshForwarder::LogFrame(const char *aActionText, const Mac::Frame &aFrame, void MeshForwarder::LogFragmentFrameDrop(Error aError, uint16_t aFrameLength, - const Mac::Address & aMacSource, - const Mac::Address & aMacDest, + const Mac::Addresses &aMacAddrs, const Lowpan::FragmentHeader &aFragmentHeader, bool aIsSecure) { LogNote("Dropping rx frag frame, error:%s, len:%d, src:%s, dst:%s, tag:%d, offset:%d, dglen:%d, sec:%s", - ErrorToString(aError), aFrameLength, aMacSource.ToString().AsCString(), aMacDest.ToString().AsCString(), - aFragmentHeader.GetDatagramTag(), aFragmentHeader.GetDatagramOffset(), aFragmentHeader.GetDatagramSize(), - ToYesNo(aIsSecure)); + ErrorToString(aError), aFrameLength, aMacAddrs.mSource.ToString().AsCString(), + aMacAddrs.mDestination.ToString().AsCString(), aFragmentHeader.GetDatagramTag(), + aFragmentHeader.GetDatagramOffset(), aFragmentHeader.GetDatagramSize(), ToYesNo(aIsSecure)); } -void MeshForwarder::LogLowpanHcFrameDrop(Error aError, - uint16_t aFrameLength, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - bool aIsSecure) +void MeshForwarder::LogLowpanHcFrameDrop(Error aError, + uint16_t aFrameLength, + const Mac::Addresses &aMacAddrs, + bool aIsSecure) { LogNote("Dropping rx lowpan HC frame, error:%s, len:%d, src:%s, dst:%s, sec:%s", ErrorToString(aError), - aFrameLength, aMacSource.ToString().AsCString(), aMacDest.ToString().AsCString(), ToYesNo(aIsSecure)); + aFrameLength, aMacAddrs.mSource.ToString().AsCString(), aMacAddrs.mDestination.ToString().AsCString(), + ToYesNo(aIsSecure)); } #else // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_NOTE) -void MeshForwarder::LogMessage(MessageAction, const Message &, Error, const Mac::Address *) -{ -} +void MeshForwarder::LogMessage(MessageAction, const Message &, Error, const Mac::Address *) {} -void MeshForwarder::LogFrame(const char *, const Mac::Frame &, Error) -{ -} +void MeshForwarder::LogFrame(const char *, const Mac::Frame &, Error) {} -void MeshForwarder::LogFragmentFrameDrop(Error, - uint16_t, - const Mac::Address &, - const Mac::Address &, - const Lowpan::FragmentHeader &, - bool) +void MeshForwarder::LogFragmentFrameDrop(Error, uint16_t, const Mac::Addresses &, const Lowpan::FragmentHeader &, bool) { } -void MeshForwarder::LogLowpanHcFrameDrop(Error, uint16_t, const Mac::Address &, const Mac::Address &, bool) -{ -} +void MeshForwarder::LogLowpanHcFrameDrop(Error, uint16_t, const Mac::Addresses &, bool) {} #endif // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_NOTE) diff --git a/src/core/thread/mesh_forwarder.hpp b/src/core/thread/mesh_forwarder.hpp index e063c9dfb75..9ee160b5c39 100644 --- a/src/core/thread/mesh_forwarder.hpp +++ b/src/core/thread/mesh_forwarder.hpp @@ -334,6 +334,12 @@ class MeshForwarder : public InstanceLocator, private NonCopyable static constexpr uint8_t kMeshHeaderFrameMtu = OT_RADIO_FRAME_MAX_SIZE; // Max MTU with a Mesh Header frame. static constexpr uint8_t kMeshHeaderFrameFcsSize = sizeof(uint16_t); // Frame FCS size for Mesh Header frame. + // Hops left to use in lowpan mesh header: We use `kMaxRouteCost` as + // max hops between routers within Thread mesh. We then add two + // for possibility of source or destination being a child + // (requiring one hop) and one as additional guard increment. + static constexpr uint8_t kMeshHeaderHopsLeft = Mle::kMaxRouteCost + 3; + static constexpr uint32_t kTxDelayInterval = OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_INTERVAL; // In msec #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE @@ -426,38 +432,35 @@ class MeshForwarder : public InstanceLocator, private NonCopyable }; #endif // OPENTHREAD_FTD - void SendIcmpErrorIfDstUnreach(const Message & aMessage, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest); - Error CheckReachability(const FrameData & aFrameData, - const Mac::Address &aMeshSource, - const Mac::Address &aMeshDest); - void UpdateRoutes(const FrameData &aFrameData, const Mac::Address &aMeshSource, const Mac::Address &aMeshDest); - Error FrameToMessage(const FrameData & aFrameData, - uint16_t aDatagramSize, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - Message *& aMessage); + void SendIcmpErrorIfDstUnreach(const Message &aMessage, const Mac::Addresses &aMacAddrs); + Error CheckReachability(const FrameData &aFrameData, const Mac::Addresses &aMeshAddrs); + void UpdateRoutes(const FrameData &aFrameData, const Mac::Addresses &aMeshAddrs); + Error FrameToMessage(const FrameData &aFrameData, + uint16_t aDatagramSize, + const Mac::Addresses &aMacAddrs, + Message *&aMessage); void GetMacDestinationAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr); void GetMacSourceAddress(const Ip6::Address &aIp6Addr, Mac::Address &aMacAddr); Message *PrepareNextDirectTransmission(void); void HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSource, const ThreadLinkInfo &aLinkInfo); - void HandleFragment(FrameData & aFrameData, - const Mac::Address & aMacSource, - const Mac::Address & aMacDest, - const ThreadLinkInfo &aLinkInfo); - void HandleLowpanHC(const FrameData & aFrameData, - const Mac::Address & aMacSource, - const Mac::Address & aMacDest, - const ThreadLinkInfo &aLinkInfo); - uint16_t PrepareDataFrame(Mac::TxFrame & aFrame, - Message & aMessage, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - bool aAddMeshHeader = false, - uint16_t aMeshSource = 0xffff, - uint16_t aMeshDest = 0xffff, - bool aAddFragHeader = false); + void HandleFragment(FrameData &aFrameData, const Mac::Addresses &aMacAddrs, const ThreadLinkInfo &aLinkInfo); + void HandleLowpanHC(const FrameData &aFrameData, const Mac::Addresses &aMacAddrs, const ThreadLinkInfo &aLinkInfo); + + void PrepareMacHeaders(Mac::TxFrame &aFrame, + Mac::Frame::Type aFrameType, + const Mac::Addresses &aMacAddr, + const Mac::PanIds &aPanIds, + Mac::Frame::SecurityLevel aSecurityLevel, + Mac::Frame::KeyIdMode aKeyIdMode, + const Message *aMessage); + + uint16_t PrepareDataFrame(Mac::TxFrame &aFrame, + Message &aMessage, + const Mac::Addresses &aMacAddrs, + bool aAddMeshHeader = false, + uint16_t aMeshSource = 0xffff, + uint16_t aMeshDest = 0xffff, + bool aAddFragHeader = false); void PrepareEmptyFrame(Mac::TxFrame &aFrame, const Mac::Address &aMacDest, bool aAckRequest); #if OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE @@ -487,7 +490,7 @@ class MeshForwarder : public InstanceLocator, private NonCopyable void HandleReceivedFrame(Mac::RxFrame &aFrame); Mac::TxFrame *HandleFrameRequest(Mac::TxFrames &aTxFrames); - Neighbor * UpdateNeighborOnSentFrame(Mac::TxFrame & aFrame, + Neighbor *UpdateNeighborOnSentFrame(Mac::TxFrame &aFrame, Error aError, const Mac::Address &aMacDest, bool aIsDataPoll = false); @@ -499,24 +502,19 @@ class MeshForwarder : public InstanceLocator, private NonCopyable void UpdateSendMessage(Error aFrameTxError, Mac::Address &aMacDest, Neighbor *aNeighbor); void RemoveMessageIfNoPendingTx(Message &aMessage); - void HandleTimeTick(void); - static void ScheduleTransmissionTask(Tasklet &aTasklet); - void ScheduleTransmissionTask(void); + void HandleTimeTick(void); + void ScheduleTransmissionTask(void); - Error GetFramePriority(const FrameData & aFrameData, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - Message::Priority & aPriority); + Error GetFramePriority(const FrameData &aFrameData, const Mac::Addresses &aMacAddrs, Message::Priority &aPriority); Error GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader, uint16_t aSrcRloc16, - Message::Priority & aPriority); - void GetForwardFramePriority(const FrameData & aFrameData, - const Mac::Address &aMeshSource, - const Mac::Address &aMeshDest, - Message::Priority & aPriority); - - bool CalcIePresent(const Message *aMessage); - uint16_t CalcFrameVersion(const Neighbor *aNeighbor, bool aIePresent); + Message::Priority &aPriority); + void GetForwardFramePriority(const FrameData &aFrameData, + const Mac::Addresses &aMeshAddrs, + Message::Priority &aPriority); + + bool CalcIePresent(const Message *aMessage); + Mac::Frame::Version CalcFrameVersion(const Neighbor *aNeighbor, bool aIePresent) const; #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT void AppendHeaderIe(const Message *aMessage, Mac::TxFrame &aFrame); #endif @@ -525,26 +523,20 @@ class MeshForwarder : public InstanceLocator, private NonCopyable void ResumeMessageTransmissions(void); #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE - static void HandleTxDelayTimer(Timer &aTimer); - void HandleTxDelayTimer(void); + void HandleTxDelayTimer(void); #endif void LogMessage(MessageAction aAction, - const Message & aMessage, + const Message &aMessage, Error aError = kErrorNone, const Mac::Address *aAddress = nullptr); void LogFrame(const char *aActionText, const Mac::Frame &aFrame, Error aError); void LogFragmentFrameDrop(Error aError, uint16_t aFrameLength, - const Mac::Address & aMacSource, - const Mac::Address & aMacDest, + const Mac::Addresses &aMacAddrs, const Lowpan::FragmentHeader &aFragmentHeader, bool aIsSecure); - void LogLowpanHcFrameDrop(Error aError, - uint16_t aFrameLength, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest, - bool aIsSecure); + void LogLowpanHcFrameDrop(Error aError, uint16_t aFrameLength, const Mac::Addresses &aMacAddrs, bool aIsSecure); #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE) const char *MessageActionToString(MessageAction aAction, Error aError); @@ -552,32 +544,36 @@ class MeshForwarder : public InstanceLocator, private NonCopyable #if OPENTHREAD_FTD Error LogMeshFragmentHeader(MessageAction aAction, - const Message & aMessage, + const Message &aMessage, const Mac::Address *aMacAddress, Error aError, - uint16_t & aOffset, - Mac::Address & aMeshSource, - Mac::Address & aMeshDest, + uint16_t &aOffset, + Mac::Addresses &aMeshAddrs, LogLevel aLogLevel); - void LogMeshIpHeader(const Message & aMessage, - uint16_t aOffset, - const Mac::Address &aMeshSource, - const Mac::Address &aMeshDest, - LogLevel aLogLevel); + void LogMeshIpHeader(const Message &aMessage, + uint16_t aOffset, + const Mac::Addresses &aMeshAddrs, + LogLevel aLogLevel); void LogMeshMessage(MessageAction aAction, - const Message & aMessage, + const Message &aMessage, const Mac::Address *aAddress, Error aError, LogLevel aLogLevel); #endif void LogIp6SourceDestAddresses(const Ip6::Headers &aHeaders, LogLevel aLogLevel); void LogIp6Message(MessageAction aAction, - const Message & aMessage, + const Message &aMessage, const Mac::Address *aAddress, Error aError, LogLevel aLogLevel); #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE) + using TxTask = TaskletIn; + +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE + using TxDelayTimer = TimerMilliIn; +#endif + PriorityQueue mSendQueue; MessageQueue mReassemblyList; uint16_t mFragTag; @@ -585,20 +581,19 @@ class MeshForwarder : public InstanceLocator, private NonCopyable Message *mSendMessage; - Mac::Address mMacSource; - Mac::Address mMacDest; - uint16_t mMeshSource; - uint16_t mMeshDest; - bool mAddMeshHeader : 1; - bool mEnabled : 1; - bool mTxPaused : 1; - bool mSendBusy : 1; + Mac::Addresses mMacAddrs; + uint16_t mMeshSource; + uint16_t mMeshDest; + bool mAddMeshHeader : 1; + bool mEnabled : 1; + bool mTxPaused : 1; + bool mSendBusy : 1; #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE - bool mDelayNextTx : 1; - TimerMilli mTxDelayTimer; + bool mDelayNextTx : 1; + TxDelayTimer mTxDelayTimer; #endif - Tasklet mScheduleTransmissionTask; + TxTask mScheduleTransmissionTask; otIpCounters mIpCounters; diff --git a/src/core/thread/mesh_forwarder_ftd.cpp b/src/core/thread/mesh_forwarder_ftd.cpp index 9b5cdc33a05..ffec4c71694 100644 --- a/src/core/thread/mesh_forwarder_ftd.cpp +++ b/src/core/thread/mesh_forwarder_ftd.cpp @@ -36,6 +36,7 @@ #if OPENTHREAD_FTD #include "common/locator_getters.hpp" +#include "common/num_utils.hpp" #include "meshcop/meshcop.hpp" #include "net/ip6.hpp" #include "net/tcp6.hpp" @@ -49,7 +50,6 @@ Error MeshForwarder::SendMessage(Message &aMessage) { Mle::MleRouter &mle = Get(); Error error = kErrorNone; - Neighbor * neighbor; aMessage.SetOffset(0); aMessage.SetDatagramTag(0); @@ -103,31 +103,32 @@ Error MeshForwarder::SendMessage(Message &aMessage) } } } - else if ((neighbor = Get().FindNeighbor(ip6Header.GetDestination())) != nullptr && - !neighbor->IsRxOnWhenIdle() && !aMessage.IsDirectTransmission()) + else // Destination is unicast { - // destined for a sleepy child - Child &child = *static_cast(neighbor); - mIndirectSender.AddMessageForSleepyChild(aMessage, child); - } - else - { - // schedule direct transmission - aMessage.SetDirectTransmission(); + Neighbor *neighbor = Get().FindNeighbor(ip6Header.GetDestination()); + + if ((neighbor != nullptr) && !neighbor->IsRxOnWhenIdle() && !aMessage.IsDirectTransmission() && + Get().Contains(*neighbor)) + { + // Destined for a sleepy child + mIndirectSender.AddMessageForSleepyChild(aMessage, *static_cast(neighbor)); + } + else + { + aMessage.SetDirectTransmission(); + } } break; } -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE case Message::kTypeSupervision: { - Child *child = Get().GetDestination(aMessage); + Child *child = Get().GetDestination(aMessage); OT_ASSERT((child != nullptr) && !child->IsRxOnWhenIdle()); mIndirectSender.AddMessageForSleepyChild(aMessage, *child); break; } -#endif default: aMessage.SetDirectTransmission(); @@ -184,7 +185,7 @@ void MeshForwarder::HandleResolved(const Ip6::Address &aEid, Error aError) hopLimit++; message.Write(Ip6::Header::kHopLimitFieldOffset, hopLimit); - IgnoreError(Get().HandleDatagram(message, nullptr, nullptr, /* aFromHost */ false)); + IgnoreError(Get().HandleDatagram(message, Ip6::Ip6::kFromHostAllowLoopBack)); continue; } #endif @@ -282,7 +283,7 @@ void MeshForwarder::RemoveMessages(Child &aChild, Message::SubType aSubType) IgnoreError(message.Read(0, ip6header)); - if (&aChild == static_cast(Get().FindNeighbor(ip6header.GetDestination()))) + if (&aChild == Get().FindNeighbor(ip6header.GetDestination())) { message.ClearDirectTransmission(); } @@ -296,7 +297,7 @@ void MeshForwarder::RemoveMessages(Child &aChild, Message::SubType aSubType) IgnoreError(meshHeader.ParseFrom(message)); - if (&aChild == static_cast(Get().FindNeighbor(meshHeader.GetDestination()))) + if (&aChild == Get().FindNeighbor(meshHeader.GetDestination())) { message.ClearDirectTransmission(); } @@ -346,31 +347,13 @@ void MeshForwarder::RemoveDataResponseMessages(void) void MeshForwarder::SendMesh(Message &aMessage, Mac::TxFrame &aFrame) { - uint16_t fcf; - bool iePresent = CalcIePresent(&aMessage); - - // initialize MAC header - fcf = Mac::Frame::kFcfFrameData | Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfDstAddrShort | - Mac::Frame::kFcfSrcAddrShort | Mac::Frame::kFcfAckRequest | Mac::Frame::kFcfSecurityEnabled; - - if (iePresent) - { - fcf |= Mac::Frame::kFcfIePresent; - } + Mac::PanIds panIds; - fcf |= CalcFrameVersion(Get().FindNeighbor(mMacDest), iePresent); + panIds.mSource = Get().GetPanId(); + panIds.mDestination = Get().GetPanId(); - aFrame.InitMacHeader(fcf, Mac::Frame::kKeyIdMode1 | Mac::Frame::kSecEncMic32); - aFrame.SetDstPanId(Get().GetPanId()); - aFrame.SetDstAddr(mMacDest.GetShort()); - aFrame.SetSrcAddr(mMacSource.GetShort()); - -#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT - if (iePresent) - { - AppendHeaderIe(&aMessage, aFrame); - } -#endif + PrepareMacHeaders(aFrame, Mac::Frame::kTypeData, mMacAddrs, panIds, Mac::Frame::kSecurityEncMic32, + Mac::Frame::kKeyIdMode1, &aMessage); // write payload OT_ASSERT(aMessage.GetLength() <= aFrame.GetMaxPayloadLength()); @@ -384,7 +367,7 @@ Error MeshForwarder::UpdateMeshRoute(Message &aMessage) { Error error = kErrorNone; Lowpan::MeshHeader meshHeader; - Neighbor * neighbor; + Neighbor *neighbor; uint16_t nextHop; IgnoreError(meshHeader.ParseFrom(aMessage)); @@ -405,15 +388,15 @@ Error MeshForwarder::UpdateMeshRoute(Message &aMessage) ExitNow(error = kErrorDrop); } - mMacDest.SetShort(neighbor->GetRloc16()); - mMacSource.SetShort(Get().GetShortAddress()); + mMacAddrs.mDestination.SetShort(neighbor->GetRloc16()); + mMacAddrs.mSource.SetShort(Get().GetShortAddress()); mAddMeshHeader = true; mMeshDest = meshHeader.GetDestination(); mMeshSource = meshHeader.GetSource(); #if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE - if (mMacDest.GetShort() != mMeshDest) + if (mMacAddrs.mDestination.GetShort() != mMeshDest) { mDelayNextTx = true; } @@ -425,44 +408,12 @@ Error MeshForwarder::UpdateMeshRoute(Message &aMessage) void MeshForwarder::EvaluateRoutingCost(uint16_t aDest, uint8_t &aBestCost, uint16_t &aBestDest) const { - const Neighbor *neighbor; - uint8_t curCost = 0x00; - - // Path cost - curCost = Get().GetCost(aDest); - - if (!Mle::MleRouter::IsActiveRouter(aDest)) - { - // Assume best link between remote child server and its parent. - curCost += 1; - } - - // Cost if the server is direct neighbor. - neighbor = Get().FindNeighbor(aDest); - - if (neighbor != nullptr && neighbor->IsStateValid()) - { - uint8_t cost; - - if (!Mle::MleRouter::IsActiveRouter(aDest)) - { - // Cost calculated only from Link Quality In as the parent only maintains - // one-direction link info. - cost = Mle::MleRouter::LinkQualityToCost(neighbor->GetLinkInfo().GetLinkQuality()); - } - else - { - cost = Get().GetLinkCost(Mle::Mle::RouterIdFromRloc16(aDest)); - } - - // Choose the minimum cost - curCost = OT_MIN(curCost, cost); - } + uint8_t cost = Get().GetPathCost(aDest); - if ((aBestDest == Mac::kShortAddrInvalid) || (curCost < aBestCost)) + if ((aBestDest == Mac::kShortAddrInvalid) || (cost < aBestCost)) { aBestDest = aDest; - aBestCost = curCost; + aBestCost = cost; } } @@ -532,14 +483,13 @@ Error MeshForwarder::AnycastRouteLookup(uint8_t aServiceId, AnycastType aType, u } } - routerId = Mle::Mle::RouterIdFromRloc16(bestDest); + routerId = Mle::RouterIdFromRloc16(bestDest); - if (!(Mle::Mle::IsActiveRouter(bestDest) || - Mle::Mle::Rloc16FromRouterId(routerId) == Get().GetRloc16())) + if (!(Mle::IsActiveRouter(bestDest) || Mle::Rloc16FromRouterId(routerId) == Get().GetRloc16())) { // if agent is neither active router nor child of this device // use the parent of the ED Agent as Dest - bestDest = Mle::Mle::Rloc16FromRouterId(routerId); + bestDest = Mle::Rloc16FromRouterId(routerId); } aMeshDest = bestDest; @@ -552,7 +502,7 @@ Error MeshForwarder::UpdateIp6RouteFtd(Ip6::Header &ip6Header, Message &aMessage { Mle::MleRouter &mle = Get(); Error error = kErrorNone; - Neighbor * neighbor; + Neighbor *neighbor; if (aMessage.GetOffset() > 0) { @@ -561,7 +511,7 @@ Error MeshForwarder::UpdateIp6RouteFtd(Ip6::Header &ip6Header, Message &aMessage else if (mle.IsRoutingLocator(ip6Header.GetDestination())) { uint16_t rloc16 = ip6Header.GetDestination().GetIid().GetLocator(); - VerifyOrExit(mle.IsRouterIdValid(Mle::Mle::RouterIdFromRloc16(rloc16)), error = kErrorDrop); + VerifyOrExit(mle.IsRouterIdValid(Mle::RouterIdFromRloc16(rloc16)), error = kErrorDrop); mMeshDest = rloc16; } else if (mle.IsAnycastLocator(ip6Header.GetDestination())) @@ -570,7 +520,7 @@ Error MeshForwarder::UpdateIp6RouteFtd(Ip6::Header &ip6Header, Message &aMessage if (aloc16 == Mle::kAloc16Leader) { - mMeshDest = Mle::Mle::Rloc16FromRouterId(mle.GetLeaderId()); + mMeshDest = Mle::Rloc16FromRouterId(mle.GetLeaderId()); } else if (aloc16 <= Mle::kAloc16DhcpAgentEnd) { @@ -615,8 +565,8 @@ Error MeshForwarder::UpdateIp6RouteFtd(Ip6::Header &ip6Header, Message &aMessage } else { - IgnoreError(Get().RouteLookup(ip6Header.GetSource(), ip6Header.GetDestination(), nullptr, - &mMeshDest)); + IgnoreError( + Get().RouteLookup(ip6Header.GetSource(), ip6Header.GetDestination(), mMeshDest)); } VerifyOrExit(mMeshDest != Mac::kShortAddrInvalid, error = kErrorDrop); @@ -625,12 +575,12 @@ Error MeshForwarder::UpdateIp6RouteFtd(Ip6::Header &ip6Header, Message &aMessage SuccessOrExit(error = mle.CheckReachability(mMeshDest, ip6Header)); aMessage.SetMeshDest(mMeshDest); - mMacDest.SetShort(mle.GetNextHop(mMeshDest)); + mMacAddrs.mDestination.SetShort(mle.GetNextHop(mMeshDest)); - if (mMacDest.GetShort() != mMeshDest) + if (mMacAddrs.mDestination.GetShort() != mMeshDest) { // destination is not neighbor - mMacSource.SetShort(mMeshSource); + mMacAddrs.mSource.SetShort(mMeshSource); mAddMeshHeader = true; #if OPENTHREAD_CONFIG_MAC_COLLISION_AVOIDANCE_DELAY_ENABLE mDelayNextTx = true; @@ -641,17 +591,15 @@ Error MeshForwarder::UpdateIp6RouteFtd(Ip6::Header &ip6Header, Message &aMessage return error; } -void MeshForwarder::SendIcmpErrorIfDstUnreach(const Message & aMessage, - const Mac::Address &aMacSource, - const Mac::Address &aMacDest) +void MeshForwarder::SendIcmpErrorIfDstUnreach(const Message &aMessage, const Mac::Addresses &aMacAddrs) { Error error; Ip6::Headers ip6Headers; - Child * child; + Child *child; - VerifyOrExit(aMacSource.IsShort() && aMacDest.IsShort()); + VerifyOrExit(aMacAddrs.mSource.IsShort() && aMacAddrs.mDestination.IsShort()); - child = Get().FindChild(aMacSource.GetShort(), Child::kInStateAnyExceptInvalid); + child = Get().FindChild(aMacAddrs.mSource.GetShort(), Child::kInStateAnyExceptInvalid); VerifyOrExit((child == nullptr) || child->IsFullThreadDevice()); SuccessOrExit(ip6Headers.ParseFrom(aMessage)); @@ -659,25 +607,23 @@ void MeshForwarder::SendIcmpErrorIfDstUnreach(const Message & aMessage, VerifyOrExit(!ip6Headers.GetDestinationAddress().IsMulticast() && Get().IsOnMesh(ip6Headers.GetDestinationAddress())); - error = Get().CheckReachability(aMacDest.GetShort(), ip6Headers.GetIp6Header()); + error = Get().CheckReachability(aMacAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header()); if (error == kErrorNoRoute) { - SendDestinationUnreachable(aMacSource.GetShort(), ip6Headers); + SendDestinationUnreachable(aMacAddrs.mSource.GetShort(), ip6Headers); } exit: return; } -Error MeshForwarder::CheckReachability(const FrameData & aFrameData, - const Mac::Address &aMeshSource, - const Mac::Address &aMeshDest) +Error MeshForwarder::CheckReachability(const FrameData &aFrameData, const Mac::Addresses &aMeshAddrs) { Error error; Ip6::Headers ip6Headers; - error = ip6Headers.DecompressFrom(aFrameData, aMeshSource, aMeshDest, GetInstance()); + error = ip6Headers.DecompressFrom(aFrameData, aMeshAddrs, GetInstance()); if (error == kErrorNotFound) { @@ -685,11 +631,11 @@ Error MeshForwarder::CheckReachability(const FrameData & aFrameData, ExitNow(error = kErrorNone); } - error = Get().CheckReachability(aMeshDest.GetShort(), ip6Headers.GetIp6Header()); + error = Get().CheckReachability(aMeshAddrs.mDestination.GetShort(), ip6Headers.GetIp6Header()); if (error == kErrorNoRoute) { - SendDestinationUnreachable(aMeshSource.GetShort(), ip6Headers); + SendDestinationUnreachable(aMeshAddrs.mSource.GetShort(), ip6Headers); } exit: @@ -710,9 +656,8 @@ void MeshForwarder::SendDestinationUnreachable(uint16_t aMeshSource, const Ip6:: void MeshForwarder::HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSource, const ThreadLinkInfo &aLinkInfo) { Error error = kErrorNone; - Message * message = nullptr; - Mac::Address meshDest; - Mac::Address meshSource; + Message *message = nullptr; + Mac::Addresses meshAddrs; Lowpan::MeshHeader meshHeader; // Security Check: only process Mesh Header frames that had security enabled. @@ -720,21 +665,21 @@ void MeshForwarder::HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSo SuccessOrExit(error = meshHeader.ParseFrom(aFrameData)); - meshSource.SetShort(meshHeader.GetSource()); - meshDest.SetShort(meshHeader.GetDestination()); + meshAddrs.mSource.SetShort(meshHeader.GetSource()); + meshAddrs.mDestination.SetShort(meshHeader.GetDestination()); - UpdateRoutes(aFrameData, meshSource, meshDest); + UpdateRoutes(aFrameData, meshAddrs); - if (meshDest.GetShort() == Get().GetShortAddress() || - Get().IsMinimalChild(meshDest.GetShort())) + if (meshAddrs.mDestination.GetShort() == Get().GetShortAddress() || + Get().IsMinimalChild(meshAddrs.mDestination.GetShort())) { if (Lowpan::FragmentHeader::IsFragmentHeader(aFrameData)) { - HandleFragment(aFrameData, meshSource, meshDest, aLinkInfo); + HandleFragment(aFrameData, meshAddrs, aLinkInfo); } else if (Lowpan::Lowpan::IsLowpanHc(aFrameData)) { - HandleLowpanHC(aFrameData, meshSource, meshDest, aLinkInfo); + HandleLowpanHC(aFrameData, meshAddrs, aLinkInfo); } else { @@ -745,13 +690,13 @@ void MeshForwarder::HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSo { Message::Priority priority = Message::kPriorityNormal; - Get().ResolveRoutingLoops(aMacSource.GetShort(), meshDest.GetShort()); + Get().ResolveRoutingLoops(aMacSource.GetShort(), meshAddrs.mDestination.GetShort()); - SuccessOrExit(error = CheckReachability(aFrameData, meshSource, meshDest)); + SuccessOrExit(error = CheckReachability(aFrameData, meshAddrs)); meshHeader.DecrementHopsLeft(); - GetForwardFramePriority(aFrameData, meshSource, meshDest, priority); + GetForwardFramePriority(aFrameData, meshAddrs, priority); message = Get().Allocate(Message::kType6lowpan, /* aReserveHeader */ 0, Message::Settings(priority)); VerifyOrExit(message != nullptr, error = kErrorNoBufs); @@ -790,16 +735,14 @@ void MeshForwarder::HandleMesh(FrameData &aFrameData, const Mac::Address &aMacSo } } -void MeshForwarder::UpdateRoutes(const FrameData & aFrameData, - const Mac::Address &aMeshSource, - const Mac::Address &aMeshDest) +void MeshForwarder::UpdateRoutes(const FrameData &aFrameData, const Mac::Addresses &aMeshAddrs) { Ip6::Headers ip6Headers; - Neighbor * neighbor; + Neighbor *neighbor; - VerifyOrExit(!aMeshDest.IsBroadcast() && aMeshSource.IsShort()); + VerifyOrExit(!aMeshAddrs.mDestination.IsBroadcast() && aMeshAddrs.mSource.IsShort()); - SuccessOrExit(ip6Headers.DecompressFrom(aFrameData, aMeshSource, aMeshDest, GetInstance())); + SuccessOrExit(ip6Headers.DecompressFrom(aFrameData, aMeshAddrs, GetInstance())); if (!ip6Headers.GetSourceAddress().GetIid().IsLocator() && Get().IsOnMesh(ip6Headers.GetSourceAddress())) @@ -808,14 +751,14 @@ void MeshForwarder::UpdateRoutes(const FrameData & aFrameData, // inspecting packets being received only for on mesh // addresses. - Get().UpdateSnoopedCacheEntry(ip6Headers.GetSourceAddress(), aMeshSource.GetShort(), - aMeshDest.GetShort()); + Get().UpdateSnoopedCacheEntry(ip6Headers.GetSourceAddress(), aMeshAddrs.mSource.GetShort(), + aMeshAddrs.mDestination.GetShort()); } neighbor = Get().FindNeighbor(ip6Headers.GetSourceAddress()); VerifyOrExit(neighbor != nullptr && !neighbor->IsFullThreadDevice()); - if (!Mle::Mle::RouterIdMatch(aMeshSource.GetShort(), Get().GetShortAddress())) + if (!Mle::RouterIdMatch(aMeshAddrs.mSource.GetShort(), Get().GetShortAddress())) { Get().RemoveNeighbor(*neighbor); } @@ -826,7 +769,7 @@ void MeshForwarder::UpdateRoutes(const FrameData & aFrameData, bool MeshForwarder::FragmentPriorityList::UpdateOnTimeTick(void) { - bool contineRxingTicks = false; + bool continueRxingTicks = false; for (Entry &entry : mEntries) { @@ -836,12 +779,12 @@ bool MeshForwarder::FragmentPriorityList::UpdateOnTimeTick(void) if (!entry.IsExpired()) { - contineRxingTicks = true; + continueRxingTicks = true; } } } - return contineRxingTicks; + return continueRxingTicks; } void MeshForwarder::UpdateFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader, @@ -925,7 +868,7 @@ MeshForwarder::FragmentPriorityList::Entry *MeshForwarder::FragmentPriorityList: Error MeshForwarder::GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader, uint16_t aSrcRloc16, - Message::Priority & aPriority) + Message::Priority &aPriority) { Error error = kErrorNone; FragmentPriorityList::Entry *entry; @@ -938,10 +881,9 @@ Error MeshForwarder::GetFragmentPriority(Lowpan::FragmentHeader &aFragmentHeader return error; } -void MeshForwarder::GetForwardFramePriority(const FrameData & aFrameData, - const Mac::Address &aMeshSource, - const Mac::Address &aMeshDest, - Message::Priority & aPriority) +void MeshForwarder::GetForwardFramePriority(const FrameData &aFrameData, + const Mac::Addresses &aMeshAddrs, + Message::Priority &aPriority) { Error error = kErrorNone; FrameData frameData = aFrameData; @@ -955,22 +897,23 @@ void MeshForwarder::GetForwardFramePriority(const FrameData & aFrameData, if (fragmentHeader.GetDatagramOffset() > 0) { // Get priority from the pre-buffered info - ExitNow(error = GetFragmentPriority(fragmentHeader, aMeshSource.GetShort(), aPriority)); + ExitNow(error = GetFragmentPriority(fragmentHeader, aMeshAddrs.mSource.GetShort(), aPriority)); } } // Get priority from IPv6 header or UDP destination port directly - error = GetFramePriority(frameData, aMeshSource, aMeshDest, aPriority); + error = GetFramePriority(frameData, aMeshAddrs, aPriority); exit: if (error != kErrorNone) { - LogNote("Failed to get forwarded frame priority, error:%s, len:%d, src:%d, dst:%s", ErrorToString(error), - frameData.GetLength(), aMeshSource.ToString().AsCString(), aMeshDest.ToString().AsCString()); + LogNote("Failed to get forwarded frame priority, error:%s, len:%d, src:%s, dst:%s", ErrorToString(error), + frameData.GetLength(), aMeshAddrs.mSource.ToString().AsCString(), + aMeshAddrs.mDestination.ToString().AsCString()); } else if (isFragment) { - UpdateFragmentPriority(fragmentHeader, frameData.GetLength(), aMeshSource.GetShort(), aPriority); + UpdateFragmentPriority(fragmentHeader, frameData.GetLength(), aMeshAddrs.mSource.GetShort(), aPriority); } } @@ -979,12 +922,11 @@ void MeshForwarder::GetForwardFramePriority(const FrameData & aFrameData, #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE) Error MeshForwarder::LogMeshFragmentHeader(MessageAction aAction, - const Message & aMessage, + const Message &aMessage, const Mac::Address *aMacAddress, Error aError, - uint16_t & aOffset, - Mac::Address & aMeshSource, - Mac::Address & aMeshDest, + uint16_t &aOffset, + Mac::Addresses &aMeshAddrs, LogLevel aLogLevel) { Error error = kErrorFailed; @@ -994,12 +936,12 @@ Error MeshForwarder::LogMeshFragmentHeader(MessageAction aAction, Lowpan::FragmentHeader fragmentHeader; uint16_t headerLength; bool shouldLogRadio = false; - const char * radioString = ""; + const char *radioString = ""; SuccessOrExit(meshHeader.ParseFrom(aMessage, headerLength)); - aMeshSource.SetShort(meshHeader.GetSource()); - aMeshDest.SetShort(meshHeader.GetDestination()); + aMeshAddrs.mSource.SetShort(meshHeader.GetSource()); + aMeshAddrs.mDestination.SetShort(meshHeader.GetDestination()); aOffset = headerLength; @@ -1019,9 +961,10 @@ Error MeshForwarder::LogMeshFragmentHeader(MessageAction aAction, LogAt(aLogLevel, "%s mesh frame, len:%d%s%s, msrc:%s, mdst:%s, hops:%d, frag:%s, sec:%s%s%s%s%s%s%s", MessageActionToString(aAction, aError), aMessage.GetLength(), (aMacAddress == nullptr) ? "" : ((aAction == kMessageReceive) ? ", from:" : ", to:"), - (aMacAddress == nullptr) ? "" : aMacAddress->ToString().AsCString(), aMeshSource.ToString().AsCString(), - aMeshDest.ToString().AsCString(), meshHeader.GetHopsLeft() + ((aAction == kMessageReceive) ? 1 : 0), - ToYesNo(hasFragmentHeader), ToYesNo(aMessage.IsLinkSecurityEnabled()), + (aMacAddress == nullptr) ? "" : aMacAddress->ToString().AsCString(), + aMeshAddrs.mSource.ToString().AsCString(), aMeshAddrs.mDestination.ToString().AsCString(), + meshHeader.GetHopsLeft() + ((aAction == kMessageReceive) ? 1 : 0), ToYesNo(hasFragmentHeader), + ToYesNo(aMessage.IsLinkSecurityEnabled()), (aError == kErrorNone) ? "" : ", error:", (aError == kErrorNone) ? "" : ErrorToString(aError), shouldLogRss ? ", rss:" : "", shouldLogRss ? aMessage.GetRssAverager().ToString().AsCString() : "", shouldLogRadio ? ", radio:" : "", radioString); @@ -1040,15 +983,14 @@ Error MeshForwarder::LogMeshFragmentHeader(MessageAction aAction, return error; } -void MeshForwarder::LogMeshIpHeader(const Message & aMessage, - uint16_t aOffset, - const Mac::Address &aMeshSource, - const Mac::Address &aMeshDest, - LogLevel aLogLevel) +void MeshForwarder::LogMeshIpHeader(const Message &aMessage, + uint16_t aOffset, + const Mac::Addresses &aMeshAddrs, + LogLevel aLogLevel) { Ip6::Headers headers; - SuccessOrExit(headers.DecompressFrom(aMessage, aOffset, aMeshSource, aMeshDest)); + SuccessOrExit(headers.DecompressFrom(aMessage, aOffset, aMeshAddrs)); LogAt(aLogLevel, " IPv6 %s msg, chksum:%04x, ecn:%s, prio:%s", Ip6::Ip6::IpProtoToString(headers.GetIpProto()), headers.GetChecksum(), Ip6::Ip6::EcnToString(headers.GetEcn()), MessagePriorityToString(aMessage)); @@ -1060,17 +1002,15 @@ void MeshForwarder::LogMeshIpHeader(const Message & aMessage, } void MeshForwarder::LogMeshMessage(MessageAction aAction, - const Message & aMessage, + const Message &aMessage, const Mac::Address *aMacAddress, Error aError, LogLevel aLogLevel) { - uint16_t offset; - Mac::Address meshSource; - Mac::Address meshDest; + uint16_t offset; + Mac::Addresses meshAddrs; - SuccessOrExit( - LogMeshFragmentHeader(aAction, aMessage, aMacAddress, aError, offset, meshSource, meshDest, aLogLevel)); + SuccessOrExit(LogMeshFragmentHeader(aAction, aMessage, aMacAddress, aError, offset, meshAddrs, aLogLevel)); // When log action is `kMessageTransmit` we do not include // the IPv6 header info in the logs, as the same info is @@ -1079,7 +1019,7 @@ void MeshForwarder::LogMeshMessage(MessageAction aAction, VerifyOrExit(aAction != kMessageTransmit); - LogMeshIpHeader(aMessage, offset, meshSource, meshDest, aLogLevel); + LogMeshIpHeader(aMessage, offset, meshAddrs, aLogLevel); exit: return; diff --git a/src/core/thread/mle.cpp b/src/core/thread/mle.cpp index 451aa9261e8..bd6510185f2 100644 --- a/src/core/thread/mle.cpp +++ b/src/core/thread/mle.cpp @@ -43,6 +43,7 @@ #include "common/encoding.hpp" #include "common/instance.hpp" #include "common/locator_getters.hpp" +#include "common/num_utils.hpp" #include "common/random.hpp" #include "common/serial_number.hpp" #include "common/settings.hpp" @@ -56,6 +57,7 @@ #include "thread/mle_router.hpp" #include "thread/thread_netif.hpp" #include "thread/time_sync_service.hpp" +#include "thread/version.hpp" using ot::Encoding::BigEndian::HostSwap16; @@ -71,6 +73,7 @@ const otMeshLocalPrefix Mle::sMeshLocalPrefixInit = { Mle::Mle(Instance &aInstance) : InstanceLocator(aInstance) , mRetrieveNewNetworkData(false) + , mRequestRouteTlv(false) , mRole(kRoleDisabled) , mNeighborTable(aInstance) , mDeviceMode(DeviceMode::kModeRxOnWhenIdle) @@ -78,54 +81,41 @@ Mle::Mle(Instance &aInstance) , mReattachState(kReattachStop) , mAttachCounter(0) , mAnnounceDelay(kAnnounceTimeout) - , mAttachTimer(aInstance, Mle::HandleAttachTimer) - , mDelayedResponseTimer(aInstance, Mle::HandleDelayedResponseTimer) - , mMessageTransmissionTimer(aInstance, Mle::HandleMessageTransmissionTimer) - , mDetachGracefullyTimer(aInstance, Mle::HandleDetachGracefullyTimer) - , mParentLeaderCost(0) - , mDetachGracefullyCallback(nullptr) - , mDetachGracefullyContext(nullptr) + , mAttachTimer(aInstance) + , mDelayedResponseTimer(aInstance) + , mMessageTransmissionTimer(aInstance) +#if OPENTHREAD_FTD + , mWasLeader(false) +#endif , mAttachMode(kAnyPartition) - , mParentPriority(0) - , mParentLinkQuality3(0) - , mParentLinkQuality2(0) - , mParentLinkQuality1(0) - , mParentSedBufferSize(0) - , mParentSedDatagramCount(0) , mChildUpdateAttempts(0) , mChildUpdateRequestState(kChildUpdateRequestNone) , mDataRequestAttempts(0) , mDataRequestState(kDataRequestNone) , mAddressRegistrationMode(kAppendAllAddresses) , mHasRestored(false) - , mParentLinkMargin(0) - , mParentIsSingleton(false) , mReceivedResponseFromParent(false) + , mInitiallyAttachedAsSleepy(false) , mSocket(aInstance) , mTimeout(kMleEndDeviceTimeout) #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - , mCslTimeout(OPENTHREAD_CONFIG_CSL_TIMEOUT) + , mCslTimeout(kDefaultCslTimeout) #endif + , mRloc16(Mac::kShortAddrInvalid) , mPreviousParentRloc(Mac::kShortAddrInvalid) #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE - , mParentSearchIsInBackoff(false) - , mParentSearchBackoffWasCanceled(false) - , mParentSearchRecentlyDetached(false) - , mParentSearchBackoffCancelTime(0) - , mParentSearchTimer(aInstance, Mle::HandleParentSearchTimer) + , mParentSearch(aInstance) #endif , mAnnounceChannel(0) , mAlternateChannel(0) , mAlternatePanId(Mac::kPanIdBroadcast) , mAlternateTimestamp(0) - , mParentResponseCb(nullptr) - , mParentResponseCbContext(nullptr) + , mDetachGracefullyTimer(aInstance) { mParent.Init(aInstance); mParentCandidate.Init(aInstance); mLeaderData.Clear(); - mParentLeaderData.Clear(); mParent.Clear(); mParentCandidate.Clear(); ResetCounters(); @@ -142,9 +132,6 @@ Mle::Mle(Instance &aInstance) mMeshLocal16.GetAddress().GetIid().SetToLocator(0); mMeshLocal16.mRloc = true; - // Store RLOC address reference in MPL module. - Get().SetMatchingAddress(mMeshLocal16.GetAddress()); - mLinkLocalAllThreadNodes.Clear(); mLinkLocalAllThreadNodes.GetAddress().mFields.m16[0] = HostSwap16(0xff32); mLinkLocalAllThreadNodes.GetAddress().mFields.m16[7] = HostSwap16(0x0001); @@ -168,7 +155,7 @@ Error Mle::Enable(void) SuccessOrExit(error = mSocket.Bind(kUdpPort)); #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE - StartParentSearchTimer(); + mParentSearch.StartTimer(); #endif exit: return error; @@ -264,17 +251,53 @@ void Mle::Stop(StopMode aMode) exit: mDetachGracefullyTimer.Stop(); - if (mDetachGracefullyCallback != nullptr) + if (mDetachGracefullyCallback.IsSet()) { - otDetachGracefullyCallback callback = mDetachGracefullyCallback; - void * context = mDetachGracefullyContext; + Callback callbackCopy = mDetachGracefullyCallback; + + mDetachGracefullyCallback.Clear(); + callbackCopy.Invoke(); + } +} + +void Mle::ResetCounters(void) +{ + memset(&mCounters, 0, sizeof(mCounters)); +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + mLastUpdatedTimestamp = Get().GetUptime(); +#endif +} - mDetachGracefullyCallback = nullptr; - mDetachGracefullyContext = nullptr; +#if OPENTHREAD_CONFIG_UPTIME_ENABLE +void Mle::UpdateRoleTimeCounters(DeviceRole aRole) +{ + uint64_t currentUptimeMsec = Get().GetUptime(); + uint64_t durationMsec = currentUptimeMsec - mLastUpdatedTimestamp; - callback(context); + mLastUpdatedTimestamp = currentUptimeMsec; + + mCounters.mTrackedTime += durationMsec; + + switch (aRole) + { + case kRoleDisabled: + mCounters.mDisabledTime += durationMsec; + break; + case kRoleDetached: + mCounters.mDetachedTime += durationMsec; + break; + case kRoleChild: + mCounters.mChildTime += durationMsec; + break; + case kRoleRouter: + mCounters.mRouterTime += durationMsec; + break; + case kRoleLeader: + mCounters.mLeaderTime += durationMsec; + break; } } +#endif void Mle::SetRole(DeviceRole aRole) { @@ -284,6 +307,10 @@ void Mle::SetRole(DeviceRole aRole) LogNote("Role %s -> %s", RoleToString(oldRole), RoleToString(mRole)); +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + UpdateRoleTimeCounters(oldRole); +#endif + switch (mRole) { case kRoleDisabled: @@ -309,6 +336,17 @@ void Mle::SetRole(DeviceRole aRole) mParent.SetState(Neighbor::kStateInvalid); } + if ((oldRole == kRoleDetached) && IsChild()) + { + // On transition from detached to child, we remember whether we + // attached as sleepy or not. This is then used to determine + // whether or not we need to re-attach on mode changes between + // rx-on and sleepy (rx-off). If we initially attach as sleepy, + // then rx-on/off mode changes are allowed without re-attach. + + mInitiallyAttachedAsSleepy = !GetDeviceMode().IsRxOnWhenIdle(); + } + exit: return; } @@ -339,7 +377,7 @@ void Mle::Restore(void) Get().SetCurrentKeySequence(networkInfo.GetKeySequence()); Get().SetMleFrameCounter(networkInfo.GetMleFrameCounter()); - Get().SetAllMacFrameCounters(networkInfo.GetMacFrameCounter()); + Get().SetAllMacFrameCounters(networkInfo.GetMacFrameCounter(), /* aSetIfLarger */ false); #if OPENTHREAD_MTD mDeviceMode.Set(networkInfo.GetDeviceMode() & ~DeviceMode::kModeFullThreadDevice); @@ -366,6 +404,7 @@ void Mle::Restore(void) #endif { Get().SetShortAddress(networkInfo.GetRloc16()); + mRloc16 = networkInfo.GetRloc16(); } Get().SetExtAddress(networkInfo.GetExtAddress()); @@ -393,7 +432,7 @@ void Mle::Restore(void) mParent.Clear(); mParent.SetExtAddress(parentInfo.GetExtAddress()); - mParent.SetVersion(static_cast(parentInfo.GetVersion())); + mParent.SetVersion(parentInfo.GetVersion()); mParent.SetDeviceMode(DeviceMode(DeviceMode::kModeFullThreadDevice | DeviceMode::kModeRxOnWhenIdle | DeviceMode::kModeFullNetworkData)); mParent.SetRloc16(Rloc16FromRouterId(RouterIdFromRloc16(networkInfo.GetRloc16()))); @@ -408,6 +447,8 @@ void Mle::Restore(void) Get().SetPreviousPartitionId(networkInfo.GetPreviousPartitionId()); Get().Restore(); } + + mWasLeader = networkInfo.GetRole() == kRoleLeader; #endif // Successfully restored the network information from non-volatile settings after boot. @@ -462,10 +503,8 @@ Error Mle::Store(void) } networkInfo.SetKeySequence(Get().GetCurrentKeySequence()); - networkInfo.SetMleFrameCounter(Get().GetMleFrameCounter() + - OPENTHREAD_CONFIG_STORE_FRAME_COUNTER_AHEAD); - networkInfo.SetMacFrameCounter(Get().GetMaximumMacFrameCounter() + - OPENTHREAD_CONFIG_STORE_FRAME_COUNTER_AHEAD); + networkInfo.SetMleFrameCounter(Get().GetMleFrameCounter() + kStoreFrameCounterAhead); + networkInfo.SetMacFrameCounter(Get().GetMaximumMacFrameCounter() + kStoreFrameCounterAhead); networkInfo.SetDeviceMode(mDeviceMode.Get()); SuccessOrExit(error = Get().Save(networkInfo)); @@ -498,7 +537,7 @@ Error Mle::BecomeDetached(void) } #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE - mParentSearchRecentlyDetached = true; + mParentSearch.SetRecentlyDetached(); #endif SetStateDetached(); @@ -523,6 +562,17 @@ Error Mle::BecomeChild(void) return error; } +Error Mle::SearchForBetterParent(void) +{ + Error error = kErrorNone; + + VerifyOrExit(IsChild(), error = kErrorInvalidState); + Attach(kBetterParent); + +exit: + return error; +} + void Mle::Attach(AttachMode aMode) { VerifyOrExit(!IsDisabled() && !IsAttaching()); @@ -622,22 +672,16 @@ uint32_t Mle::GetAttachStartDelay(void) const delay += jitter; } - LogNote("Attach attempt %d unsuccessful, will try again in %u.%03u seconds", mAttachCounter, delay / 1000, - delay % 1000); + LogNote("Attach attempt %u unsuccessful, will try again in %lu.%03u seconds", mAttachCounter, ToUlong(delay / 1000), + static_cast(delay % 1000)); exit: return delay; } -bool Mle::IsAttached(void) const -{ - return (IsChild() || IsRouter() || IsLeader()); -} +bool Mle::IsAttached(void) const { return (IsChild() || IsRouter() || IsLeader()); } -bool Mle::IsRouterOrLeader(void) const -{ - return (IsRouter() || IsLeader()); -} +bool Mle::IsRouterOrLeader(void) const { return (IsRouter() || IsLeader()); } void Mle::SetStateDetached(void) { @@ -657,18 +701,15 @@ void Mle::SetStateDetached(void) SetAttachState(kAttachStateIdle); mAttachTimer.Stop(); mMessageTransmissionTimer.Stop(); - mChildUpdateRequestState = kChildUpdateRequestNone; - mChildUpdateAttempts = 0; - mDataRequestState = kDataRequestNone; - mDataRequestAttempts = 0; + mChildUpdateRequestState = kChildUpdateRequestNone; + mChildUpdateAttempts = 0; + mDataRequestState = kDataRequestNone; + mDataRequestAttempts = 0; + mInitiallyAttachedAsSleepy = false; Get().SetRxOnWhenIdle(true); Get().SetBeaconEnabled(false); #if OPENTHREAD_FTD Get().HandleDetachStart(); -#endif - Get().SetForwardingEnabled(false); -#if OPENTHREAD_FTD - Get().SetTimerExpirations(0); #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE Get().UpdateCsl(); @@ -699,16 +740,11 @@ void Mle::SetStateChild(uint16_t aRloc16) } #endif - Get().SetForwardingEnabled(false); -#if OPENTHREAD_FTD - Get().SetTimerExpirations(kMplChildDataMessageTimerExpirations); -#endif - // send announce after attached if needed InformPreviousChannel(); #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE - UpdateParentSearchState(); + mParentSearch.UpdateState(); #endif if ((mPreviousParentRloc != Mac::kShortAddrInvalid) && (mPreviousParentRloc != mParent.GetRloc16())) @@ -790,30 +826,46 @@ Error Mle::SetDeviceMode(DeviceMode aDeviceMode) IgnoreError(Store()); - switch (mRole) + if (IsAttached()) { - case kRoleDisabled: - break; + bool shouldReattach = false; - case kRoleDetached: - mAttachCounter = 0; - SetStateDetached(); - Attach(kAnyPartition); - break; + // We need to re-attach when switching between MTD/FTD modes. - case kRoleChild: - SetStateChild(GetRloc16()); - IgnoreError(SendChildUpdateRequest()); - break; + if (oldMode.IsFullThreadDevice() != mDeviceMode.IsFullThreadDevice()) + { + shouldReattach = true; + } - case kRoleRouter: - case kRoleLeader: - if (oldMode.IsFullThreadDevice() && !mDeviceMode.IsFullThreadDevice()) + // If we initially attached as sleepy we allow mode changes + // between rx-on/off without a re-attach (we send "Child Update + // Request" to update the parent). But if we initially attached + // as rx-on, we require a re-attach on switching from rx-on to + // sleepy (rx-off) mode. + + if (!mInitiallyAttachedAsSleepy && oldMode.IsRxOnWhenIdle() && !mDeviceMode.IsRxOnWhenIdle()) { + shouldReattach = true; + } + + if (shouldReattach) + { + mAttachCounter = 0; IgnoreError(BecomeDetached()); + ExitNow(); } + } - break; + if (IsDetached()) + { + mAttachCounter = 0; + SetStateDetached(); + Attach(kAnyPartition); + } + else if (IsChild()) + { + SetStateChild(GetRloc16()); + IgnoreError(SendChildUpdateRequest()); } exit: @@ -936,11 +988,6 @@ void Mle::ApplyMeshLocalPrefix(void) Get().Signal(kEventThreadMeshLocalAddrChanged); } -uint16_t Mle::GetRloc16(void) const -{ - return Get().GetShortAddress(); -} - void Mle::SetRloc16(uint16_t aRloc16) { uint16_t oldRloc16 = GetRloc16(); @@ -958,7 +1005,7 @@ void Mle::SetRloc16(uint16_t aRloc16) } Get().SetShortAddress(aRloc16); - Get().SetSeedId(aRloc16); + mRloc16 = aRloc16; if (aRloc16 != Mac::kShortAddrInvalid) { @@ -1084,6 +1131,15 @@ void Mle::SetCslTimeout(uint32_t aTimeout) } #endif +void Mle::InitNeighbor(Neighbor &aNeighbor, const RxInfo &aRxInfo) +{ + aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(aNeighbor.GetExtAddress()); + aNeighbor.GetLinkInfo().Clear(); + aNeighbor.GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss()); + aNeighbor.ResetLinkFailures(); + aNeighbor.SetLastHeard(TimerMilli::GetNow()); +} + void Mle::HandleNotifierEvents(Events aEvents) { VerifyOrExit(!IsDisabled()); @@ -1277,11 +1333,6 @@ void Mle::UpdateServiceAlocs(void) #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE -void Mle::HandleAttachTimer(Timer &aTimer) -{ - aTimer.Get().HandleAttachTimer(); -} - Error Mle::DetermineParentRequestType(ParentRequestType &aType) const { // This method determines the Parent Request type to use during an @@ -1338,7 +1389,6 @@ Error Mle::DetermineParentRequestType(ParentRequestType &aType) const bool Mle::HasAcceptableParentCandidate(void) const { bool hasAcceptableParent = false; - LinkQuality linkQuality; ParentRequestType parentReqType; VerifyOrExit(mParentCandidate.IsStateParentResponse()); @@ -1346,7 +1396,7 @@ bool Mle::HasAcceptableParentCandidate(void) const switch (mAttachState) { case kAttachStateAnnounce: - VerifyOrExit(!HasMoreChannelsToAnnouce()); + VerifyOrExit(!HasMoreChannelsToAnnounce()); break; case kAttachStateParentRequest: @@ -1358,8 +1408,7 @@ bool Mle::HasAcceptableParentCandidate(void) const // in Parent Request was sent to routers, we will keep the // candidate and forward to REED stage to potentially find a // better parent. - linkQuality = OT_MIN(mParentCandidate.GetLinkInfo().GetLinkQuality(), mParentCandidate.GetLinkQualityOut()); - VerifyOrExit(linkQuality == kLinkQuality3); + VerifyOrExit(mParentCandidate.GetTwoWayLinkQuality() == kLinkQuality3); } break; @@ -1398,7 +1447,7 @@ void Mle::HandleAttachTimer(void) if (HasAcceptableParentCandidate() && (SendChildIdRequest() == kErrorNone)) { SetAttachState(kAttachStateChildIdRequest); - delay = kParentRequestReedTimeout; + delay = kChildIdResponseTimeout; ExitNow(); } @@ -1455,7 +1504,7 @@ void Mle::HandleAttachTimer(void) OT_FALL_THROUGH; case kAttachStateAnnounce: - if (shouldAnnounce && (GetNextAnnouceChannel(mAnnounceChannel) == kErrorNone)) + if (shouldAnnounce && (GetNextAnnounceChannel(mAnnounceChannel) == kErrorNone)) { SendAnnounce(mAnnounceChannel, kOrphanAnnounce); delay = mAnnounceDelay; @@ -1581,11 +1630,6 @@ uint32_t Mle::Reattach(void) return delay; } -void Mle::HandleDelayedResponseTimer(Timer &aTimer) -{ - aTimer.Get().HandleDelayedResponseTimer(); -} - void Mle::HandleDelayedResponseTimer(void) { TimeMilli now = TimerMilli::GetNow(); @@ -1599,10 +1643,7 @@ void Mle::HandleDelayedResponseTimer(void) if (now < metadata.mSendTime) { - if (nextSendTime > metadata.mSendTime) - { - nextSendTime = metadata.mSendTime; - } + nextSendTime = Min(nextSendTime, metadata.mSendTime); } else { @@ -1643,7 +1684,11 @@ void Mle::SendDelayedResponse(TxMessage &aMessage, const DelayedResponseMetadata } exit: - FreeMessageOnError(&aMessage, error); + if (error != kErrorNone) + { + // do not use `FreeMessageOnError()` to avoid null check on nonnull pointer + aMessage.Free(); + } } void Mle::RemoveDelayedDataResponseMessage(void) @@ -1676,7 +1721,7 @@ void Mle::RemoveDelayedMessage(Message::SubType aSubType, MessageType aMessageTy void Mle::SendParentRequest(ParentRequestType aType) { Error error = kErrorNone; - TxMessage * message; + TxMessage *message; uint8_t scanMask = 0; Ip6::Address destination; @@ -1731,10 +1776,11 @@ void Mle::RequestShorterChildIdRequest(void) Error Mle::SendChildIdRequest(void) { + static const uint8_t kTlvs[] = {Tlv::kAddress16, Tlv::kNetworkData, Tlv::kRoute}; + Error error = kErrorNone; - uint8_t tlvs[] = {Tlv::kAddress16, Tlv::kNetworkData, Tlv::kRoute}; - uint8_t tlvsLen = sizeof(tlvs); - TxMessage * message = nullptr; + uint8_t tlvsLen = sizeof(kTlvs); + TxMessage *message = nullptr; Ip6::Address destination; if (mParent.GetExtAddress() == mParentCandidate.GetExtAddress()) @@ -1756,12 +1802,13 @@ Error Mle::SendChildIdRequest(void) } VerifyOrExit((message = NewMleMessage(kCommandChildIdRequest)) != nullptr, error = kErrorNoBufs); - SuccessOrExit(error = message->AppendResponseTlv(mParentCandidateChallenge)); + SuccessOrExit(error = message->AppendResponseTlv(mParentCandidate.mChallenge)); SuccessOrExit(error = message->AppendLinkFrameCounterTlv()); SuccessOrExit(error = message->AppendMleFrameCounterTlv()); SuccessOrExit(error = message->AppendModeTlv(mDeviceMode)); SuccessOrExit(error = message->AppendTimeoutTlv(mTimeout)); SuccessOrExit(error = message->AppendVersionTlv()); + SuccessOrExit(error = message->AppendSupervisionIntervalTlv(Get().GetInterval())); if (!IsFullThreadDevice()) { @@ -1771,7 +1818,7 @@ Error Mle::SendChildIdRequest(void) tlvsLen -= 1; } - SuccessOrExit(error = message->AppendTlvRequestTlv(tlvs, tlvsLen)); + SuccessOrExit(error = message->AppendTlvRequestTlv(kTlvs, tlvsLen)); SuccessOrExit(error = message->AppendActiveTimestampTlv()); SuccessOrExit(error = message->AppendPendingTimestampTlv()); @@ -1795,12 +1842,38 @@ Error Mle::SendChildIdRequest(void) return error; } -Error Mle::SendDataRequest(const Ip6::Address &aDestination, - const uint8_t * aTlvs, - uint8_t aTlvsLength, - uint16_t aDelay, - const uint8_t * aExtraTlvs, - uint8_t aExtraTlvsLength) +Error Mle::SendDataRequest(const Ip6::Address &aDestination) +{ + return SendDataRequestAfterDelay(aDestination, /* aDelay */ 0); +} + +Error Mle::SendDataRequestAfterDelay(const Ip6::Address &aDestination, uint16_t aDelay) +{ + static const uint8_t kTlvs[] = {Tlv::kNetworkData, Tlv::kRoute}; + + // Based on `mRequestRouteTlv` include both Network Data and Route + // TLVs or only Network Data TLV. + + return SendDataRequest(aDestination, kTlvs, mRequestRouteTlv ? 2 : 1, aDelay); +} + +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE +Error Mle::SendDataRequestForLinkMetricsReport(const Ip6::Address &aDestination, + const LinkMetrics::Initiator::QueryInfo &aQueryInfo) +{ + static const uint8_t kTlvs[] = {Tlv::kLinkMetricsReport}; + + return SendDataRequest(aDestination, kTlvs, sizeof(kTlvs), /* aDelay */ 0, &aQueryInfo); +} + +Error Mle::SendDataRequest(const Ip6::Address &aDestination, + const uint8_t *aTlvs, + uint8_t aTlvsLength, + uint16_t aDelay, + const LinkMetrics::Initiator::QueryInfo *aQueryInfo) +#else +Error Mle::SendDataRequest(const Ip6::Address &aDestination, const uint8_t *aTlvs, uint8_t aTlvsLength, uint16_t aDelay) +#endif { Error error = kErrorNone; TxMessage *message; @@ -1810,10 +1883,12 @@ Error Mle::SendDataRequest(const Ip6::Address &aDestination, VerifyOrExit((message = NewMleMessage(kCommandDataRequest)) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->AppendTlvRequestTlv(aTlvs, aTlvsLength)); - if (aExtraTlvs != nullptr && aExtraTlvsLength > 0) +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE + if (aQueryInfo != nullptr) { - SuccessOrExit(error = message->AppendBytes(aExtraTlvs, aExtraTlvsLength)); + SuccessOrExit(error = Get().AppendLinkMetricsQueryTlv(*message, *aQueryInfo)); } +#endif if (aDelay) { @@ -1854,6 +1929,14 @@ void Mle::ScheduleMessageTransmissionTimer(void) { uint32_t interval = 0; +#if OPENTHREAD_FTD + if (mRole == kRoleDetached && mLinkRequestAttempts > 0) + { + ExitNow(interval = Random::NonCrypto::GetUint32InRange(kMulticastTransmissionDelayMin, + kMulticastTransmissionDelayMax)); + } +#endif + switch (mChildUpdateRequestState) { case kChildUpdateRequestNone: @@ -1866,8 +1949,7 @@ void Mle::ScheduleMessageTransmissionTimer(void) #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE if (Get().IsCslEnabled()) { - ExitNow(interval = Get().GetCslPeriod() * kUsPerTenSymbols / 1000 + - static_cast(kUnicastRetransmissionDelay)); + ExitNow(interval = Get().GetCslPeriodMs() + static_cast(kUnicastRetransmissionDelay)); } else #endif @@ -1902,11 +1984,6 @@ void Mle::ScheduleMessageTransmissionTimer(void) } } -void Mle::HandleMessageTransmissionTimer(Timer &aTimer) -{ - aTimer.Get().HandleMessageTransmissionTimer(); -} - void Mle::HandleMessageTransmissionTimer(void) { // The `mMessageTransmissionTimer` is used for: @@ -1915,20 +1992,32 @@ void Mle::HandleMessageTransmissionTimer(void) // - Retransmission of "Child Update Request", // - Retransmission of "Data Request" on a child, // - Sending periodic keep-alive "Child Update Request" messages on a non-sleepy (rx-on) child. + // - Retransmission of "Link Request" after router reset + +#if OPENTHREAD_FTD + // Retransmit multicast link request if no response has been received + // and maximum transmission limit has not been reached. + if (mRole == kRoleDetached && mLinkRequestAttempts > 0) + { + IgnoreError(Get().SendLinkRequest(nullptr)); + mLinkRequestAttempts--; + ScheduleMessageTransmissionTimer(); + ExitNow(); + } +#endif switch (mChildUpdateRequestState) { case kChildUpdateRequestNone: if (mDataRequestState == kDataRequestActive) { - static const uint8_t tlvs[] = {Tlv::kNetworkData}; - Ip6::Address destination; + Ip6::Address destination; VerifyOrExit(mDataRequestAttempts < kMaxChildKeepAliveAttempts, IgnoreError(BecomeDetached())); destination.SetToLinkLocalAddress(mParent.GetExtAddress()); - if (SendDataRequest(destination, tlvs, sizeof(tlvs), 0) == kErrorNone) + if (SendDataRequest(destination) == kErrorNone) { mDataRequestAttempts++; } @@ -1968,17 +2057,14 @@ void Mle::HandleMessageTransmissionTimer(void) return; } -Error Mle::SendChildUpdateRequest(bool aAppendChallenge) -{ - return SendChildUpdateRequest(aAppendChallenge, mTimeout); -} +Error Mle::SendChildUpdateRequest(void) { return SendChildUpdateRequest(kNormalChildUpdateRequest); } -Error Mle::SendChildUpdateRequest(bool aAppendChallenge, uint32_t aTimeout) +Error Mle::SendChildUpdateRequest(ChildUpdateRequestMode aMode) { Error error = kErrorNone; Ip6::Address destination; - TxMessage * message = nullptr; - AddressRegistrationMode mode = kAppendAllAddresses; + TxMessage *message = nullptr; + AddressRegistrationMode addrRegMode = kAppendAllAddresses; if (!mParent.IsStateValidOrRestoring()) { @@ -1993,7 +2079,7 @@ Error Mle::SendChildUpdateRequest(bool aAppendChallenge, uint32_t aTimeout) VerifyOrExit((message = NewMleMessage(kCommandChildUpdateRequest)) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->AppendModeTlv(mDeviceMode)); - if (aAppendChallenge || IsDetached()) + if ((aMode == kAppendChallengeTlv) || IsDetached()) { mParentRequestChallenge.GenerateRandom(); SuccessOrExit(error = message->AppendChallengeTlv(mParentRequestChallenge)); @@ -2002,13 +2088,14 @@ Error Mle::SendChildUpdateRequest(bool aAppendChallenge, uint32_t aTimeout) switch (mRole) { case kRoleDetached: - mode = kAppendMeshLocalOnly; + addrRegMode = kAppendMeshLocalOnly; break; case kRoleChild: SuccessOrExit(error = message->AppendSourceAddressTlv()); SuccessOrExit(error = message->AppendLeaderDataTlv()); - SuccessOrExit(error = message->AppendTimeoutTlv(aTimeout)); + SuccessOrExit(error = message->AppendTimeoutTlv((aMode == kAppendZeroTimeout) ? 0 : mTimeout)); + SuccessOrExit(error = message->AppendSupervisionIntervalTlv(Get().GetInterval())); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE if (Get().IsCslEnabled()) { @@ -2022,12 +2109,11 @@ Error Mle::SendChildUpdateRequest(bool aAppendChallenge, uint32_t aTimeout) case kRoleRouter: case kRoleLeader: OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); } if (!IsFullThreadDevice()) { - SuccessOrExit(error = message->AppendAddressRegistrationTlv(mode)); + SuccessOrExit(error = message->AppendAddressRegistrationTlv(addrRegMode)); } destination.SetToLinkLocalAddress(mParent.GetExtAddress()); @@ -2054,20 +2140,20 @@ Error Mle::SendChildUpdateRequest(bool aAppendChallenge, uint32_t aTimeout) return error; } -Error Mle::SendChildUpdateResponse(const uint8_t *aTlvs, uint8_t aNumTlvs, const Challenge &aChallenge) +Error Mle::SendChildUpdateResponse(const TlvList &aTlvList, const Challenge &aChallenge) { Error error = kErrorNone; Ip6::Address destination; - TxMessage * message; + TxMessage *message; bool checkAddress = false; VerifyOrExit((message = NewMleMessage(kCommandChildUpdateResponse)) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->AppendSourceAddressTlv()); SuccessOrExit(error = message->AppendLeaderDataTlv()); - for (int i = 0; i < aNumTlvs; i++) + for (uint8_t tlvType : aTlvList) { - switch (aTlvs[i]) + switch (tlvType) { case Tlv::kTimeout: SuccessOrExit(error = message->AppendTimeoutTlv(mTimeout)); @@ -2103,6 +2189,10 @@ Error Mle::SendChildUpdateResponse(const uint8_t *aTlvs, uint8_t aNumTlvs, const SuccessOrExit(error = message->AppendMleFrameCounterTlv()); break; + case Tlv::kSupervisionInterval: + SuccessOrExit(error = message->AppendSupervisionIntervalTlv(Get().GetInterval())); + break; + #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE case Tlv::kCslTimeout: if (Get().IsCslEnabled()) @@ -2141,19 +2231,19 @@ void Mle::SendAnnounce(uint8_t aChannel, AnnounceMode aMode) void Mle::SendAnnounce(uint8_t aChannel, const Ip6::Address &aDestination, AnnounceMode aMode) { Error error = kErrorNone; - ChannelTlv channel; + ChannelTlv channelTlv; MeshCoP::Timestamp activeTimestamp; - TxMessage * message = nullptr; + TxMessage *message = nullptr; VerifyOrExit(Get().GetSupportedChannelMask().ContainsChannel(aChannel), error = kErrorInvalidArgs); VerifyOrExit((message = NewMleMessage(kCommandAnnounce)) != nullptr, error = kErrorNoBufs); message->SetLinkSecurityEnabled(true); message->SetChannel(aChannel); - channel.Init(); - channel.SetChannelPage(0); - channel.SetChannel(Get().GetPanChannel()); - SuccessOrExit(error = channel.AppendTo(*message)); + channelTlv.Init(); + channelTlv.SetChannelPage(0); + channelTlv.SetChannel(Get().GetPanChannel()); + SuccessOrExit(error = channelTlv.AppendTo(*message)); switch (aMode) { @@ -2178,7 +2268,7 @@ void Mle::SendAnnounce(uint8_t aChannel, const Ip6::Address &aDestination, Annou FreeMessageOnError(message, error); } -Error Mle::GetNextAnnouceChannel(uint8_t &aChannel) const +Error Mle::GetNextAnnounceChannel(uint8_t &aChannel) const { // This method gets the next channel to send announce on after // `aChannel`. Returns `kErrorNotFound` if no more channel in the @@ -2194,11 +2284,11 @@ Error Mle::GetNextAnnouceChannel(uint8_t &aChannel) const return channelMask.GetNextChannel(aChannel); } -bool Mle::HasMoreChannelsToAnnouce(void) const +bool Mle::HasMoreChannelsToAnnounce(void) const { uint8_t channel = mAnnounceChannel; - return GetNextAnnouceChannel(channel) == kErrorNone; + return GetNextAnnounceChannel(channel) == kErrorNone; } #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE @@ -2253,10 +2343,10 @@ Error Mle::SendLinkProbe(const Ip6::Address &aDestination, uint8_t aSeriesId, ui #endif Error Mle::ProcessMessageSecurity(Crypto::AesCcm::Mode aMode, - Message & aMessage, + Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint16_t aCmdOffset, - const SecurityHeader & aHeader) + const SecurityHeader &aHeader) { // This method performs MLE message security. Based on `aMode` it // can be used to encrypt and append tag to `aMessage` or to @@ -2303,7 +2393,7 @@ Error Mle::ProcessMessageSecurity(Crypto::AesCcm::Mode aMode, } senderAddress->GetIid().ConvertToExtAddress(extAddress); - Crypto::AesCcm::GenerateNonce(extAddress, aHeader.GetFrameCounter(), Mac::Frame::kSecEncMic32, nonce); + Crypto::AesCcm::GenerateNonce(extAddress, aHeader.GetFrameCounter(), Mac::Frame::kSecurityEncMic32, nonce); keySequence = aHeader.GetKeyId(); @@ -2359,7 +2449,7 @@ void Mle::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageIn uint32_t frameCounter; Mac::ExtAddress extAddr; uint8_t command; - Neighbor * neighbor; + Neighbor *neighbor; LogDebg("Receive MLE message"); @@ -2647,7 +2737,7 @@ void Mle::ReestablishLinkWithNeighbor(Neighbor &aNeighbor) if (IsChild() && (&aNeighbor == &mParent)) { - IgnoreError(SendChildUpdateRequest(/* aAppendChallenge */ true)); + IgnoreError(SendChildUpdateRequest(kAppendChallengeTlv)); ExitNow(); } @@ -2676,79 +2766,57 @@ void Mle::HandleAdvertisement(RxInfo &aRxInfo) Error error = kErrorNone; uint16_t sourceAddress; LeaderData leaderData; - uint8_t tlvs[] = {Tlv::kNetworkData}; uint16_t delay; - // Source Address + VerifyOrExit(IsAttached()); + SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, sourceAddress)); Log(kMessageReceive, kTypeAdvertisement, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress); - // Leader Data SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)); - if (!IsDetached()) - { #if OPENTHREAD_FTD - if (IsFullThreadDevice()) - { - SuccessOrExit(error = Get().HandleAdvertisement(aRxInfo)); - } - else -#endif - { - if ((aRxInfo.mNeighbor == &mParent) && (mParent.GetRloc16() != sourceAddress)) - { - // Remove stale parent. - IgnoreError(BecomeDetached()); - } - } + if (IsFullThreadDevice()) + { + SuccessOrExit(error = Get().HandleAdvertisement(aRxInfo, sourceAddress, leaderData)); } +#endif - switch (mRole) + if (IsChild()) { - case kRoleDisabled: - case kRoleDetached: - ExitNow(); - - case kRoleChild: VerifyOrExit(aRxInfo.mNeighbor == &mParent); - if ((mParent.GetRloc16() == sourceAddress) && (leaderData.GetPartitionId() != mLeaderData.GetPartitionId() || - leaderData.GetLeaderRouterId() != GetLeaderId())) + if (mParent.GetRloc16() != sourceAddress) + { + // Remove stale parent. + IgnoreError(BecomeDetached()); + ExitNow(); + } + + if ((leaderData.GetPartitionId() != mLeaderData.GetPartitionId()) || + (leaderData.GetLeaderRouterId() != GetLeaderId())) { SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId()); #if OPENTHREAD_FTD - if (IsFullThreadDevice()) - { - switch (Get().ProcessRouteTlv(aRxInfo)) - { - case kErrorNone: - case kErrorNotFound: - break; - default: - ExitNow(error = kErrorParse); - } - } + SuccessOrExit(error = Get().ReadAndProcessRouteTlvOnFed(aRxInfo, mParent.GetRouterId())); #endif mRetrieveNewNetworkData = true; } mParent.SetLastHeard(TimerMilli::GetNow()); - break; - - case kRoleRouter: - case kRoleLeader: - VerifyOrExit(aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid()); - break; + } + else // Device is router or leader + { + VerifyOrExit(aRxInfo.IsNeighborStateValid()); } if (mRetrieveNewNetworkData || IsNetworkDataNewer(leaderData)) { delay = Random::NonCrypto::GetUint16InRange(0, kMleMaxResponseDelay); - IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr(), tlvs, sizeof(tlvs), delay)); + IgnoreError(SendDataRequestAfterDelay(aRxInfo.mMessageInfo.GetPeerAddr(), delay)); } aRxInfo.mClass = RxInfo::kPeerMessage; @@ -2760,24 +2828,28 @@ void Mle::HandleAdvertisement(RxInfo &aRxInfo) void Mle::HandleDataResponse(RxInfo &aRxInfo) { Error error; -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE - uint16_t metricsReportValueOffset; - uint16_t length; -#endif Log(kMessageReceive, kTypeDataResponse, aRxInfo.mMessageInfo.GetPeerAddr()); - VerifyOrExit(aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid(), error = kErrorDrop); + VerifyOrExit(aRxInfo.IsNeighborStateValid(), error = kErrorDrop); #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE - if (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kLinkMetricsReport, metricsReportValueOffset, length) == - kErrorNone) { - Get().HandleReport(aRxInfo.mMessage, metricsReportValueOffset, length, - aRxInfo.mMessageInfo.GetPeerAddr()); + uint16_t offset; + uint16_t length; + + if (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kLinkMetricsReport, offset, length) == kErrorNone) + { + Get().HandleReport(aRxInfo.mMessage, offset, length, + aRxInfo.mMessageInfo.GetPeerAddr()); + } } #endif +#if OPENTHREAD_FTD + SuccessOrExit(error = Get().ReadAndProcessRouteTlvOnFed(aRxInfo, mParent.GetRouterId())); +#endif + error = HandleLeaderData(aRxInfo); if (mDataRequestState == kDataRequestNone && !IsRxOnWhenIdle()) @@ -2810,13 +2882,15 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo) MeshCoP::Timestamp activeTimestamp; MeshCoP::Timestamp pendingTimestamp; const MeshCoP::Timestamp *timestamp; - bool hasActiveTimestamp = false; - bool hasPendingTimestamp = false; - uint16_t networkDataOffset = 0; + bool hasActiveTimestamp = false; + bool hasPendingTimestamp = false; + uint16_t networkDataOffset; + uint16_t networkDataLength; uint16_t activeDatasetOffset = 0; + uint16_t activeDatasetLength = 0; uint16_t pendingDatasetOffset = 0; + uint16_t pendingDatasetLength = 0; bool dataRequest = false; - Tlv tlv; // Leader Data SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)); @@ -2850,7 +2924,8 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo) // if received timestamp does not match the local value and message does not contain the dataset, // send MLE Data Request if (!IsLeader() && (MeshCoP::Timestamp::Compare(&activeTimestamp, timestamp) != 0) && - (Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kActiveDataset, activeDatasetOffset) != kErrorNone)) + (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kActiveDataset, activeDatasetOffset, activeDatasetLength) != + kErrorNone)) { ExitNow(dataRequest = true); } @@ -2875,7 +2950,8 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo) // if received timestamp does not match the local value and message does not contain the dataset, // send MLE Data Request if (!IsLeader() && (MeshCoP::Timestamp::Compare(&pendingTimestamp, timestamp) != 0) && - (Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kPendingDataset, pendingDatasetOffset) != kErrorNone)) + (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kPendingDataset, pendingDatasetOffset, + pendingDatasetLength) != kErrorNone)) { ExitNow(dataRequest = true); } @@ -2889,11 +2965,12 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo) ExitNow(error = kErrorParse); } - if (Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kNetworkData, networkDataOffset) == kErrorNone) + if (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kNetworkData, networkDataOffset, networkDataLength) == + kErrorNone) { - error = Get().SetNetworkData(leaderData.GetDataVersion(NetworkData::kFullSet), - leaderData.GetDataVersion(NetworkData::kStableSubset), - GetNetworkDataType(), aRxInfo.mMessage, networkDataOffset); + error = Get().SetNetworkData( + leaderData.GetDataVersion(NetworkData::kFullSet), leaderData.GetDataVersion(NetworkData::kStableSubset), + GetNetworkDataType(), aRxInfo.mMessage, networkDataOffset, networkDataLength); SuccessOrExit(error); } else @@ -2914,9 +2991,8 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo) { if (activeDatasetOffset > 0) { - IgnoreError(aRxInfo.mMessage.Read(activeDatasetOffset, tlv)); - IgnoreError(Get().Save( - activeTimestamp, aRxInfo.mMessage, activeDatasetOffset + sizeof(tlv), tlv.GetLength())); + IgnoreError(Get().Save(activeTimestamp, aRxInfo.mMessage, + activeDatasetOffset, activeDatasetLength)); } } @@ -2925,9 +3001,8 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo) { if (pendingDatasetOffset > 0) { - IgnoreError(aRxInfo.mMessage.Read(pendingDatasetOffset, tlv)); - IgnoreError(Get().Save( - pendingTimestamp, aRxInfo.mMessage, pendingDatasetOffset + sizeof(tlv), tlv.GetLength())); + IgnoreError(Get().Save(pendingTimestamp, aRxInfo.mMessage, + pendingDatasetOffset, pendingDatasetLength)); } } } @@ -2938,8 +3013,7 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo) if (dataRequest) { - static const uint8_t tlvs[] = {Tlv::kNetworkData}; - uint16_t delay; + uint16_t delay; if (aRxInfo.mMessageInfo.GetSockAddr().IsMulticast()) { @@ -2953,7 +3027,7 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo) delay = 10; } - IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr(), tlvs, sizeof(tlvs), delay)); + IgnoreError(SendDataRequestAfterDelay(aRxInfo.mMessageInfo.GetPeerAddr(), delay)); } else if (error == kErrorNone) { @@ -2971,115 +3045,87 @@ Error Mle::HandleLeaderData(RxInfo &aRxInfo) return error; } -bool Mle::IsBetterParent(uint16_t aRloc16, - LinkQuality aLinkQuality, - uint8_t aLinkMargin, - const ConnectivityTlv &aConnectivityTlv, - uint8_t aVersion, - uint8_t aCslClockAccuracy, - uint8_t aCslUncertainty) +bool Mle::IsBetterParent(uint16_t aRloc16, + LinkQuality aLinkQuality, + uint8_t aLinkMargin, + const ConnectivityTlv &aConnectivityTlv, + uint16_t aVersion, + const Mac::CslAccuracy &aCslAccuracy) { - bool rval = false; - - LinkQuality candidateLinkQualityIn = mParentCandidate.GetLinkInfo().GetLinkQuality(); - LinkQuality candidateTwoWayLinkQuality = OT_MIN(candidateLinkQualityIn, mParentCandidate.GetLinkQualityOut()); -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - uint64_t candidateCslMetric = 0; - uint64_t cslMetric = 0; -#else - OT_UNUSED_VARIABLE(aCslClockAccuracy); - OT_UNUSED_VARIABLE(aCslUncertainty); -#endif + int rval; + LinkQuality candidateTwoWayLinkQuality = mParentCandidate.GetTwoWayLinkQuality(); // Mesh Impacting Criteria - if (aLinkQuality != candidateTwoWayLinkQuality) - { - ExitNow(rval = (aLinkQuality > candidateTwoWayLinkQuality)); - } + rval = ThreeWayCompare(aLinkQuality, candidateTwoWayLinkQuality); + VerifyOrExit(rval == 0); - if (IsActiveRouter(aRloc16) != IsActiveRouter(mParentCandidate.GetRloc16())) - { - ExitNow(rval = IsActiveRouter(aRloc16)); - } + rval = ThreeWayCompare(IsActiveRouter(aRloc16), IsActiveRouter(mParentCandidate.GetRloc16())); + VerifyOrExit(rval == 0); - if (aConnectivityTlv.GetParentPriority() != mParentPriority) - { - ExitNow(rval = (aConnectivityTlv.GetParentPriority() > mParentPriority)); - } + rval = ThreeWayCompare(aConnectivityTlv.GetParentPriority(), mParentCandidate.mPriority); + VerifyOrExit(rval == 0); // Prefer the parent with highest quality links (Link Quality 3 field in Connectivity TLV) to neighbors - if (aConnectivityTlv.GetLinkQuality3() != mParentLinkQuality3) - { - ExitNow(rval = (aConnectivityTlv.GetLinkQuality3() > mParentLinkQuality3)); - } + rval = ThreeWayCompare(aConnectivityTlv.GetLinkQuality3(), mParentCandidate.mLinkQuality3); + VerifyOrExit(rval == 0); // Thread 1.2 Specification 4.5.2.1.2 Child Impacting Criteria - if (aVersion != mParentCandidate.GetVersion()) - { - ExitNow(rval = (aVersion > mParentCandidate.GetVersion())); - } - if (aConnectivityTlv.GetSedBufferSize() != mParentSedBufferSize) - { - ExitNow(rval = (aConnectivityTlv.GetSedBufferSize() > mParentSedBufferSize)); - } + rval = ThreeWayCompare(aVersion, mParentCandidate.GetVersion()); + VerifyOrExit(rval == 0); - if (aConnectivityTlv.GetSedDatagramCount() != mParentSedDatagramCount) - { - ExitNow(rval = (aConnectivityTlv.GetSedDatagramCount() > mParentSedDatagramCount)); - } + rval = ThreeWayCompare(aConnectivityTlv.GetSedBufferSize(), mParentCandidate.mSedBufferSize); + VerifyOrExit(rval == 0); + + rval = ThreeWayCompare(aConnectivityTlv.GetSedDatagramCount(), mParentCandidate.mSedDatagramCount); + VerifyOrExit(rval == 0); // Extra rules - if (aConnectivityTlv.GetLinkQuality2() != mParentLinkQuality2) - { - ExitNow(rval = (aConnectivityTlv.GetLinkQuality2() > mParentLinkQuality2)); - } + rval = ThreeWayCompare(aConnectivityTlv.GetLinkQuality2(), mParentCandidate.mLinkQuality2); + VerifyOrExit(rval == 0); - if (aConnectivityTlv.GetLinkQuality1() != mParentLinkQuality1) - { - ExitNow(rval = (aConnectivityTlv.GetLinkQuality1() > mParentLinkQuality1)); - } + rval = ThreeWayCompare(aConnectivityTlv.GetLinkQuality1(), mParentCandidate.mLinkQuality1); + VerifyOrExit(rval == 0); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE // CSL metric if (!IsRxOnWhenIdle()) { - cslMetric = CalcParentCslMetric(aCslClockAccuracy, aCslUncertainty); - candidateCslMetric = - CalcParentCslMetric(mParentCandidate.GetCslClockAccuracy(), mParentCandidate.GetCslUncertainty()); - if (candidateCslMetric != cslMetric) - { - ExitNow(rval = (cslMetric < candidateCslMetric)); - } + uint64_t cslMetric = CalcParentCslMetric(aCslAccuracy); + uint64_t candidateCslMetric = CalcParentCslMetric(mParentCandidate.GetCslAccuracy()); + + // Smaller metric is better. + rval = ThreeWayCompare(candidateCslMetric, cslMetric); + VerifyOrExit(rval == 0); } +#else + OT_UNUSED_VARIABLE(aCslAccuracy); #endif - rval = (aLinkMargin > mParentLinkMargin); + rval = ThreeWayCompare(aLinkMargin, mParentCandidate.mLinkMargin); exit: - return rval; + return (rval > 0); } void Mle::HandleParentResponse(RxInfo &aRxInfo) { - Error error = kErrorNone; - const ThreadLinkInfo *linkInfo = aRxInfo.mMessageInfo.GetThreadLinkInfo(); - Challenge response; - uint16_t version; - uint16_t sourceAddress; - LeaderData leaderData; - uint8_t linkMarginFromTlv; - uint8_t linkMargin; - LinkQuality linkQuality; - ConnectivityTlv connectivity; - uint32_t linkFrameCounter; - uint32_t mleFrameCounter; - Mac::ExtAddress extAddress; + Error error = kErrorNone; + int8_t rss = aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss(); + Challenge response; + uint16_t version; + uint16_t sourceAddress; + LeaderData leaderData; + uint8_t linkMarginFromTlv; + uint8_t linkMargin; + LinkQuality linkQuality; + ConnectivityTlv connectivityTlv; + uint32_t linkFrameCounter; + uint32_t mleFrameCounter; + Mac::ExtAddress extAddress; + Mac::CslAccuracy cslAccuracy; #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE - TimeParameterTlv timeParameter; -#endif -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - CslClockAccuracyTlv clockAccuracy; + TimeParameterTlv timeParameterTlv; #endif // Source Address @@ -3089,7 +3135,7 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo) // Version SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, version)); - VerifyOrExit(version >= OT_THREAD_VERSION_1_1, error = kErrorParse); + VerifyOrExit(version >= kThreadVersion1p1, error = kErrorParse); // Response SuccessOrExit(error = aRxInfo.mMessage.ReadResponseTlv(response)); @@ -3108,44 +3154,52 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo) // Link Margin SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, linkMarginFromTlv)); - linkMargin = LinkQualityInfo::ConvertRssToLinkMargin(Get().GetNoiseFloor(), linkInfo->GetRss()); + linkMargin = Get().ComputeLinkMargin(rss); if (linkMargin > linkMarginFromTlv) { linkMargin = linkMarginFromTlv; } - linkQuality = LinkQualityInfo::ConvertLinkMarginToLinkQuality(linkMargin); + linkQuality = LinkQualityForLinkMargin(linkMargin); // Connectivity - SuccessOrExit(error = Tlv::FindTlv(aRxInfo.mMessage, connectivity)); - VerifyOrExit(connectivity.IsValid(), error = kErrorParse); + SuccessOrExit(error = Tlv::FindTlv(aRxInfo.mMessage, connectivityTlv)); + VerifyOrExit(connectivityTlv.IsValid(), error = kErrorParse); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE // CSL Accuracy - if (Tlv::FindTlv(aRxInfo.mMessage, clockAccuracy) != kErrorNone) + switch (aRxInfo.mMessage.ReadCslClockAccuracyTlv(cslAccuracy)) { - clockAccuracy.SetCslClockAccuracy(kCslWorstCrystalPpm); - clockAccuracy.SetCslUncertainty(kCslWorstUncertainty); + case kErrorNone: + break; + case kErrorNotFound: + cslAccuracy.Init(); // Use worst-case values if TLV is not found + break; + default: + ExitNow(error = kErrorParse); } +#else + cslAccuracy.Init(); #endif - // Share data with application, if requested. - if (mParentResponseCb) +#if OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE + if (mParentResponseCallback.IsSet()) { otThreadParentResponseInfo parentinfo; parentinfo.mExtAddr = extAddress; parentinfo.mRloc16 = sourceAddress; - parentinfo.mRssi = linkInfo->GetRss(); - parentinfo.mPriority = connectivity.GetParentPriority(); - parentinfo.mLinkQuality3 = connectivity.GetLinkQuality3(); - parentinfo.mLinkQuality2 = connectivity.GetLinkQuality2(); - parentinfo.mLinkQuality1 = connectivity.GetLinkQuality1(); + parentinfo.mRssi = rss; + parentinfo.mPriority = connectivityTlv.GetParentPriority(); + parentinfo.mLinkQuality3 = connectivityTlv.GetLinkQuality3(); + parentinfo.mLinkQuality2 = connectivityTlv.GetLinkQuality2(); + parentinfo.mLinkQuality1 = connectivityTlv.GetLinkQuality1(); parentinfo.mIsAttached = IsAttached(); - mParentResponseCb(&parentinfo, mParentResponseCbContext); + mParentResponseCallback.Invoke(&parentinfo); } +#endif aRxInfo.mClass = RxInfo::kAuthoritativeMessage; @@ -3153,9 +3207,9 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo) if (IsFullThreadDevice() && !IsDetached()) { bool isPartitionIdSame = (leaderData.GetPartitionId() == mLeaderData.GetPartitionId()); - bool isIdSequenceSame = (connectivity.GetIdSequence() == Get().GetRouterIdSequence()); + bool isIdSequenceSame = (connectivityTlv.GetIdSequence() == Get().GetRouterIdSequence()); bool isIdSequenceGreater = - SerialNumber::IsGreater(connectivity.GetIdSequence(), Get().GetRouterIdSequence()); + SerialNumber::IsGreater(connectivityTlv.GetIdSequence(), Get().GetRouterIdSequence()); switch (mAttachMode) { @@ -3176,7 +3230,7 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo) case kBetterPartition: VerifyOrExit(!isPartitionIdSame); - VerifyOrExit(MleRouter::ComparePartitions(connectivity.GetActiveRouters() <= 1, leaderData, + VerifyOrExit(MleRouter::ComparePartitions(connectivityTlv.GetActiveRouters() <= 1, leaderData, Get().IsSingleton(), mLeaderData) > 0); break; } @@ -3195,8 +3249,8 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo) #if OPENTHREAD_FTD if (IsFullThreadDevice()) { - compare = MleRouter::ComparePartitions(connectivity.GetActiveRouters() <= 1, leaderData, mParentIsSingleton, - mParentLeaderData); + compare = MleRouter::ComparePartitions(connectivityTlv.GetActiveRouters() <= 1, leaderData, + mParentCandidate.mIsSingleton, mParentCandidate.mLeaderData); } // only consider partitions that are the same or better @@ -3204,14 +3258,10 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo) #endif // only consider better parents if the partitions are the same -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - VerifyOrExit(compare != 0 || - IsBetterParent(sourceAddress, linkQuality, linkMargin, connectivity, static_cast(version), - clockAccuracy.GetCslClockAccuracy(), clockAccuracy.GetCslUncertainty())); -#else - VerifyOrExit(compare != 0 || IsBetterParent(sourceAddress, linkQuality, linkMargin, connectivity, - static_cast(version), 0, 0)); -#endif + if (compare == 0) + { + VerifyOrExit(IsBetterParent(sourceAddress, linkQuality, linkMargin, connectivityTlv, version, cslAccuracy)); + } } // Link/MLE Frame Counters @@ -3220,12 +3270,12 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo) #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE // Time Parameter - if (Tlv::FindTlv(aRxInfo.mMessage, timeParameter) == kErrorNone) + if (Tlv::FindTlv(aRxInfo.mMessage, timeParameterTlv) == kErrorNone) { - VerifyOrExit(timeParameter.IsValid()); + VerifyOrExit(timeParameterTlv.IsValid()); - Get().SetTimeSyncPeriod(timeParameter.GetTimeSyncPeriod()); - Get().SetXtalThreshold(timeParameter.GetXtalThreshold()); + Get().SetTimeSyncPeriod(timeParameterTlv.GetTimeSyncPeriod()); + Get().SetXtalThreshold(timeParameterTlv.GetXtalThreshold()); } #if OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED @@ -3239,37 +3289,33 @@ void Mle::HandleParentResponse(RxInfo &aRxInfo) #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE // Challenge - SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(mParentCandidateChallenge)); + SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(mParentCandidate.mChallenge)); - mParentCandidate.SetExtAddress(extAddress); + InitNeighbor(mParentCandidate, aRxInfo); mParentCandidate.SetRloc16(sourceAddress); mParentCandidate.GetLinkFrameCounters().SetAll(linkFrameCounter); mParentCandidate.SetLinkAckFrameCounter(linkFrameCounter); mParentCandidate.SetMleFrameCounter(mleFrameCounter); - mParentCandidate.SetVersion(static_cast(version)); + mParentCandidate.SetVersion(version); mParentCandidate.SetDeviceMode(DeviceMode(DeviceMode::kModeFullThreadDevice | DeviceMode::kModeRxOnWhenIdle | DeviceMode::kModeFullNetworkData)); - mParentCandidate.GetLinkInfo().Clear(); - mParentCandidate.GetLinkInfo().AddRss(linkInfo->GetRss()); - mParentCandidate.ResetLinkFailures(); - mParentCandidate.SetLinkQualityOut(LinkQualityInfo::ConvertLinkMarginToLinkQuality(linkMarginFromTlv)); + mParentCandidate.SetLinkQualityOut(LinkQualityForLinkMargin(linkMarginFromTlv)); mParentCandidate.SetState(Neighbor::kStateParentResponse); mParentCandidate.SetKeySequence(aRxInfo.mKeySequence); + mParentCandidate.SetLeaderCost(connectivityTlv.GetLeaderCost()); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - mParentCandidate.SetCslClockAccuracy(clockAccuracy.GetCslClockAccuracy()); - mParentCandidate.SetCslUncertainty(clockAccuracy.GetCslUncertainty()); + mParentCandidate.SetCslAccuracy(cslAccuracy); #endif - mParentPriority = connectivity.GetParentPriority(); - mParentLinkQuality3 = connectivity.GetLinkQuality3(); - mParentLinkQuality2 = connectivity.GetLinkQuality2(); - mParentLinkQuality1 = connectivity.GetLinkQuality1(); - mParentLeaderCost = connectivity.GetLeaderCost(); - mParentSedBufferSize = connectivity.GetSedBufferSize(); - mParentSedDatagramCount = connectivity.GetSedDatagramCount(); - mParentLeaderData = leaderData; - mParentIsSingleton = connectivity.GetActiveRouters() <= 1; - mParentLinkMargin = linkMargin; + mParentCandidate.mPriority = connectivityTlv.GetParentPriority(); + mParentCandidate.mLinkQuality3 = connectivityTlv.GetLinkQuality3(); + mParentCandidate.mLinkQuality2 = connectivityTlv.GetLinkQuality2(); + mParentCandidate.mLinkQuality1 = connectivityTlv.GetLinkQuality1(); + mParentCandidate.mSedBufferSize = connectivityTlv.GetSedBufferSize(); + mParentCandidate.mSedDatagramCount = connectivityTlv.GetSedDatagramCount(); + mParentCandidate.mLeaderData = leaderData; + mParentCandidate.mIsSingleton = connectivityTlv.GetActiveRouters() <= 1; + mParentCandidate.mLinkMargin = linkMargin; exit: LogProcessError(kTypeParentResponse, error); @@ -3282,16 +3328,17 @@ void Mle::HandleChildIdResponse(RxInfo &aRxInfo) uint16_t sourceAddress; uint16_t shortAddress; MeshCoP::Timestamp timestamp; - Tlv tlv; uint16_t networkDataOffset; + uint16_t networkDataLength; uint16_t offset; + uint16_t length; // Source Address SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, sourceAddress)); Log(kMessageReceive, kTypeChildIdResponse, aRxInfo.mMessageInfo.GetPeerAddr(), sourceAddress); - VerifyOrExit(aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid(), error = kErrorSecurity); + VerifyOrExit(aRxInfo.IsNeighborStateValid(), error = kErrorSecurity); VerifyOrExit(mAttachState == kAttachStateChildIdRequest); @@ -3303,18 +3350,18 @@ void Mle::HandleChildIdResponse(RxInfo &aRxInfo) SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)); // Network Data - SuccessOrExit(error = Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kNetworkData, networkDataOffset)); + SuccessOrExit( + error = Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kNetworkData, networkDataOffset, networkDataLength)); // Active Timestamp switch (Tlv::Find(aRxInfo.mMessage, timestamp)) { case kErrorNone: // Active Dataset - if (Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kActiveDataset, offset) == kErrorNone) + if (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kActiveDataset, offset, length) == kErrorNone) { - IgnoreError(aRxInfo.mMessage.Read(offset, tlv)); - SuccessOrExit(error = Get().Save(timestamp, aRxInfo.mMessage, - offset + sizeof(tlv), tlv.GetLength())); + SuccessOrExit(error = + Get().Save(timestamp, aRxInfo.mMessage, offset, length)); } break; @@ -3336,11 +3383,9 @@ void Mle::HandleChildIdResponse(RxInfo &aRxInfo) { case kErrorNone: // Pending Dataset - if (Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kPendingDataset, offset) == kErrorNone) + if (Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kPendingDataset, offset, length) == kErrorNone) { - IgnoreError(aRxInfo.mMessage.Read(offset, tlv)); - IgnoreError(Get().Save(timestamp, aRxInfo.mMessage, offset + sizeof(tlv), - tlv.GetLength())); + IgnoreError(Get().Save(timestamp, aRxInfo.mMessage, offset, length)); } break; @@ -3367,32 +3412,21 @@ void Mle::HandleChildIdResponse(RxInfo &aRxInfo) SetLeaderData(leaderData.GetPartitionId(), leaderData.GetWeighting(), leaderData.GetLeaderRouterId()); #if OPENTHREAD_FTD - if (IsFullThreadDevice()) - { - switch (Get().ProcessRouteTlv(aRxInfo)) - { - case kErrorNone: - case kErrorNotFound: - break; - default: - ExitNow(error = kErrorParse); - } - } + SuccessOrExit(error = Get().ReadAndProcessRouteTlvOnFed(aRxInfo, RouterIdFromRloc16(sourceAddress))); #endif - mParent = mParentCandidate; + mParentCandidate.CopyTo(mParent); mParentCandidate.Clear(); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - Get().SetCslParentUncertainty(mParent.GetCslUncertainty()); - Get().SetCslParentClockAccuracy(mParent.GetCslClockAccuracy()); + Get().SetCslParentAccuracy(mParent.GetCslAccuracy()); #endif mParent.SetRloc16(sourceAddress); - IgnoreError(Get().SetNetworkData(leaderData.GetDataVersion(NetworkData::kFullSet), - leaderData.GetDataVersion(NetworkData::kStableSubset), - GetNetworkDataType(), aRxInfo.mMessage, networkDataOffset)); + IgnoreError(Get().SetNetworkData( + leaderData.GetDataVersion(NetworkData::kFullSet), leaderData.GetDataVersion(NetworkData::kStableSubset), + GetNetworkDataType(), aRxInfo.mMessage, networkDataOffset, networkDataLength)); SetStateChild(shortAddress); @@ -3414,14 +3448,11 @@ void Mle::HandleChildIdResponse(RxInfo &aRxInfo) void Mle::HandleChildUpdateRequest(RxInfo &aRxInfo) { - static const uint8_t kMaxResponseTlvs = 6; - - Error error = kErrorNone; - uint16_t sourceAddress; - Challenge challenge; - RequestedTlvs requestedTlvs; - uint8_t tlvs[kMaxResponseTlvs] = {}; - uint8_t numTlvs = 0; + Error error = kErrorNone; + uint16_t sourceAddress; + Challenge challenge; + TlvList requestedTlvList; + TlvList tlvList; // Source Address SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, sourceAddress)); @@ -3432,9 +3463,9 @@ void Mle::HandleChildUpdateRequest(RxInfo &aRxInfo) switch (aRxInfo.mMessage.ReadChallengeTlv(challenge)) { case kErrorNone: - tlvs[numTlvs++] = Tlv::kResponse; - tlvs[numTlvs++] = Tlv::kMleFrameCounter; - tlvs[numTlvs++] = Tlv::kLinkFrameCounter; + tlvList.Add(Tlv::kResponse); + tlvList.Add(Tlv::kMleFrameCounter); + tlvList.Add(Tlv::kLinkFrameCounter); break; case kErrorNotFound: challenge.mLength = 0; @@ -3468,34 +3499,28 @@ void Mle::HandleChildUpdateRequest(RxInfo &aRxInfo) SuccessOrExit(error = HandleLeaderData(aRxInfo)); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - CslClockAccuracyTlv cslClockAccuracyTlv; - if (Tlv::FindTlv(aRxInfo.mMessage, cslClockAccuracyTlv) == kErrorNone) { - // MUST include CSL timeout TLV when request includes CSL accuracy - tlvs[numTlvs++] = Tlv::kCslTimeout; + Mac::CslAccuracy cslAccuracy; + + if (aRxInfo.mMessage.ReadCslClockAccuracyTlv(cslAccuracy) == kErrorNone) + { + // MUST include CSL timeout TLV when request includes CSL accuracy + tlvList.Add(Tlv::kCslTimeout); + } } #endif } else { // this device is not a child of the Child Update Request source - tlvs[numTlvs++] = Tlv::kStatus; + tlvList.Add(Tlv::kStatus); } // TLV Request - switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvs)) + switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList)) { case kErrorNone: - for (uint8_t i = 0; i < requestedTlvs.mNumTlvs; i++) - { - if (numTlvs >= sizeof(tlvs)) - { - LogWarn("Failed to respond with TLVs: %d of %d", i, requestedTlvs.mNumTlvs); - break; - } - - tlvs[numTlvs++] = requestedTlvs.mTlvs[i]; - } + tlvList.AddElementsFrom(requestedTlvList); break; case kErrorNotFound: break; @@ -3512,7 +3537,7 @@ void Mle::HandleChildUpdateRequest(RxInfo &aRxInfo) } #endif - SuccessOrExit(error = SendChildUpdateResponse(tlvs, numTlvs, challenge)); + SuccessOrExit(error = SendChildUpdateResponse(tlvList, challenge)); exit: LogProcessError(kTypeChildUpdateRequestOfParent, error); @@ -3528,9 +3553,6 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo) uint32_t mleFrameCounter; uint16_t sourceAddress; uint32_t timeout; -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - CslClockAccuracyTlv clockAccuracy; -#endif Log(kMessageReceive, kTypeChildUpdateResponseOfParent, aRxInfo.mMessageInfo.GetPeerAddr()); @@ -3557,7 +3579,6 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo) default: OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); } // Status @@ -3585,6 +3606,13 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo) mRetrieveNewNetworkData = true; +#if OPENTHREAD_FTD + if (IsFullThreadDevice()) + { + mRequestRouteTlv = true; + } +#endif + OT_FALL_THROUGH; case kRoleChild: @@ -3620,11 +3648,20 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo) } #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - // CSL Accuracy - if (Tlv::FindTlv(aRxInfo.mMessage, clockAccuracy) != kErrorNone) { - Get().SetCslParentClockAccuracy(clockAccuracy.GetCslClockAccuracy()); - Get().SetCslParentUncertainty(clockAccuracy.GetCslUncertainty()); + Mac::CslAccuracy cslAccuracy; + + // CSL Accuracy + switch (aRxInfo.mMessage.ReadCslClockAccuracyTlv(cslAccuracy)) + { + case kErrorNone: + Get().SetCslParentAccuracy(cslAccuracy); + break; + case kErrorNotFound: + break; + default: + ExitNow(error = kErrorParse); + } } #endif @@ -3642,7 +3679,6 @@ void Mle::HandleChildUpdateResponse(RxInfo &aRxInfo) default: OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); } aRxInfo.mClass = (response.mLength == 0) ? RxInfo::kPeerMessage : RxInfo::kAuthoritativeMessage; @@ -3743,7 +3779,7 @@ void Mle::HandleLinkMetricsManagementRequest(RxInfo &aRxInfo) VerifyOrExit(aRxInfo.mNeighbor != nullptr, error = kErrorInvalidState); SuccessOrExit( - error = Get().HandleManagementRequest(aRxInfo.mMessage, *aRxInfo.mNeighbor, status)); + error = Get().HandleManagementRequest(aRxInfo.mMessage, *aRxInfo.mNeighbor, status)); error = SendLinkMetricsManagementResponse(aRxInfo.mMessageInfo.GetPeerAddr(), status); @@ -3765,7 +3801,7 @@ void Mle::HandleLinkMetricsManagementResponse(RxInfo &aRxInfo) VerifyOrExit(aRxInfo.mNeighbor != nullptr, error = kErrorInvalidState); error = - Get().HandleManagementResponse(aRxInfo.mMessage, aRxInfo.mMessageInfo.GetPeerAddr()); + Get().HandleManagementResponse(aRxInfo.mMessage, aRxInfo.mMessageInfo.GetPeerAddr()); aRxInfo.mClass = RxInfo::kPeerMessage; @@ -3782,7 +3818,7 @@ void Mle::HandleLinkProbe(RxInfo &aRxInfo) Log(kMessageReceive, kTypeLinkProbe, aRxInfo.mMessageInfo.GetPeerAddr()); - SuccessOrExit(error = Get().HandleLinkProbe(aRxInfo.mMessage, seriesId)); + SuccessOrExit(error = Get().HandleLinkProbe(aRxInfo.mMessage, seriesId)); aRxInfo.mNeighbor->AggregateLinkMetrics(seriesId, LinkMetrics::SeriesInfo::kSeriesTypeLinkProbe, aRxInfo.mMessage.GetAverageLqi(), aRxInfo.mMessage.GetAverageRss()); @@ -3821,6 +3857,24 @@ uint16_t Mle::GetNextHop(uint16_t aDestination) const return (mParent.IsStateValid()) ? mParent.GetRloc16() : static_cast(Mac::kShortAddrInvalid); } +Error Mle::GetParentInfo(Router::Info &aParentInfo) const +{ + Error error = kErrorNone; + + // Skip the check for reference device since it needs to get the + // original parent's info even after role change. + +#if !OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + VerifyOrExit(IsChild(), error = kErrorInvalidState); +#endif + + aParentInfo.SetFrom(mParent); + ExitNow(); + +exit: + return error; +} + bool Mle::IsRoutingLocator(const Ip6::Address &aAddress) const { return IsMeshLocalAddress(aAddress) && aAddress.GetIid().IsRoutingLocator(); @@ -3856,7 +3910,7 @@ Error Mle::CheckReachability(uint16_t aMeshDest, const Ip6::Header &aIp6Header) void Mle::InformPreviousParent(void) { Error error = kErrorNone; - Message * message = nullptr; + Message *message = nullptr; Ip6::MessageInfo messageInfo; VerifyOrExit((message = Get().NewMessage(0)) != nullptr, error = kErrorNoBufs); @@ -3882,70 +3936,65 @@ void Mle::InformPreviousParent(void) #endif // OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE -void Mle::HandleParentSearchTimer(Timer &aTimer) -{ - aTimer.Get().HandleParentSearchTimer(); -} - -void Mle::HandleParentSearchTimer(void) +void Mle::ParentSearch::HandleTimer(void) { int8_t parentRss; - LogInfo("PeriodicParentSearch: %s interval passed", mParentSearchIsInBackoff ? "Backoff" : "Check"); + LogInfo("PeriodicParentSearch: %s interval passed", mIsInBackoff ? "Backoff" : "Check"); - if (mParentSearchBackoffWasCanceled) + if (mBackoffWasCanceled) { // Backoff can be canceled if the device switches to a new parent. // from `UpdateParentSearchState()`. We want to limit this to happen // only once within a backoff interval. - if (TimerMilli::GetNow() - mParentSearchBackoffCancelTime >= kParentSearchBackoffInterval) + if (TimerMilli::GetNow() - mBackoffCancelTime >= kBackoffInterval) { - mParentSearchBackoffWasCanceled = false; + mBackoffWasCanceled = false; LogInfo("PeriodicParentSearch: Backoff cancellation is allowed on parent switch"); } } - mParentSearchIsInBackoff = false; + mIsInBackoff = false; - VerifyOrExit(IsChild()); + VerifyOrExit(Get().IsChild()); - parentRss = GetParent().GetLinkInfo().GetAverageRss(); + parentRss = Get().GetParent().GetLinkInfo().GetAverageRss(); LogInfo("PeriodicParentSearch: Parent RSS %d", parentRss); - VerifyOrExit(parentRss != OT_RADIO_RSSI_INVALID); + VerifyOrExit(parentRss != Radio::kInvalidRssi); - if (parentRss < kParentSearchRssThreadhold) + if (parentRss < kRssThreshold) { - LogInfo("PeriodicParentSearch: Parent RSS less than %d, searching for new parents", kParentSearchRssThreadhold); - mParentSearchIsInBackoff = true; - Attach(kBetterParent); + LogInfo("PeriodicParentSearch: Parent RSS less than %d, searching for new parents", kRssThreshold); + mIsInBackoff = true; + Get().Attach(kBetterParent); } exit: - StartParentSearchTimer(); + StartTimer(); } -void Mle::StartParentSearchTimer(void) +void Mle::ParentSearch::StartTimer(void) { uint32_t interval; - interval = Random::NonCrypto::GetUint32InRange(0, kParentSearchJitterInterval); + interval = Random::NonCrypto::GetUint32InRange(0, kJitterInterval); - if (mParentSearchIsInBackoff) + if (mIsInBackoff) { - interval += kParentSearchBackoffInterval; + interval += kBackoffInterval; } else { - interval += kParentSearchCheckInterval; + interval += kCheckInterval; } - mParentSearchTimer.Start(interval); + mTimer.Start(interval); - LogInfo("PeriodicParentSearch: (Re)starting timer for %s interval", mParentSearchIsInBackoff ? "backoff" : "check"); + LogInfo("PeriodicParentSearch: (Re)starting timer for %s interval", mIsInBackoff ? "backoff" : "check"); } -void Mle::UpdateParentSearchState(void) +void Mle::ParentSearch::UpdateState(void) { #if OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH @@ -3964,24 +4013,25 @@ void Mle::UpdateParentSearchState(void) // the chance to switch back to the original (and possibly // preferred) parent more quickly. - if (mParentSearchIsInBackoff && !mParentSearchBackoffWasCanceled && mParentSearchRecentlyDetached) + if (mIsInBackoff && !mBackoffWasCanceled && mRecentlyDetached) { - if ((mPreviousParentRloc != Mac::kShortAddrInvalid) && (mPreviousParentRloc != mParent.GetRloc16())) + if ((Get().mPreviousParentRloc != Mac::kShortAddrInvalid) && + (Get().mPreviousParentRloc != Get().mParent.GetRloc16())) { - mParentSearchIsInBackoff = false; - mParentSearchBackoffWasCanceled = true; - mParentSearchBackoffCancelTime = TimerMilli::GetNow(); + mIsInBackoff = false; + mBackoffWasCanceled = true; + mBackoffCancelTime = TimerMilli::GetNow(); LogInfo("PeriodicParentSearch: Canceling backoff on switching to a new parent"); } } #endif // OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH - mParentSearchRecentlyDetached = false; + mRecentlyDetached = false; - if (!mParentSearchIsInBackoff) + if (!mIsInBackoff) { - StartParentSearchTimer(); + StartTimer(); } } #endif // OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE @@ -4012,15 +4062,9 @@ void Mle::Log(MessageAction aAction, MessageType aType, const Ip6::Address &aAdd #endif #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) -void Mle::LogProcessError(MessageType aType, Error aError) -{ - LogError(kMessageReceive, aType, aError); -} +void Mle::LogProcessError(MessageType aType, Error aError) { LogError(kMessageReceive, aType, aError); } -void Mle::LogSendError(MessageType aType, Error aError) -{ - LogError(kMessageSend, aType, aError); -} +void Mle::LogSendError(MessageType aType, Error aError) { LogError(kMessageSend, aType, aError); } void Mle::LogError(MessageAction aAction, MessageType aType, Error aError) { @@ -4190,25 +4234,6 @@ const char *Mle::MessageTypeActionToSuffixString(MessageType aType, MessageActio #endif // #if OT_SHOULD_LOG_AT( OT_LOG_LEVEL_WARN) -const char *Mle::RoleToString(DeviceRole aRole) -{ - static const char *const kRoleStrings[] = { - "disabled", // (0) kRoleDisabled - "detached", // (1) kRoleDetached - "child", // (2) kRoleChild - "router", // (3) kRoleRouter - "leader", // (4) kRoleLeader - }; - - static_assert(kRoleDisabled == 0, "kRoleDisabled value is incorrect"); - static_assert(kRoleDetached == 1, "kRoleDetached value is incorrect"); - static_assert(kRoleChild == 2, "kRoleChild value is incorrect"); - static_assert(kRoleRouter == 3, "kRoleRouter value is incorrect"); - static_assert(kRoleLeader == 4, "kRoleLeader value is incorrect"); - - return (aRole < GetArrayLength(kRoleStrings)) ? kRoleStrings[aRole] : "invalid"; -} - // LCOV_EXCL_START #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_NOTE) @@ -4277,22 +4302,21 @@ const char *Mle::ReattachStateToString(ReattachState aState) // LCOV_EXCL_STOP #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE -Error Mle::SendLinkMetricsManagementRequest(const Ip6::Address &aDestination, const uint8_t *aSubTlvs, uint8_t aLength) +Error Mle::SendLinkMetricsManagementRequest(const Ip6::Address &aDestination, const ot::Tlv &aSubTlv) { - Error error = kErrorNone; - TxMessage *message; + Error error = kErrorNone; + TxMessage *message = NewMleMessage(kCommandLinkMetricsManagementRequest); Tlv tlv; - VerifyOrExit((message = NewMleMessage(kCommandLinkMetricsManagementRequest)) != nullptr, error = kErrorNoBufs); + VerifyOrExit(message != nullptr, error = kErrorNoBufs); - // Link Metrics Management TLV tlv.SetType(Tlv::kLinkMetricsManagement); - tlv.SetLength(aLength); + tlv.SetLength(static_cast(aSubTlv.GetSize())); - SuccessOrExit(error = message->AppendBytes(&tlv, sizeof(tlv))); - SuccessOrExit(error = message->AppendBytes(aSubTlvs, aLength)); + SuccessOrExit(error = message->Append(tlv)); + SuccessOrExit(error = aSubTlv.AppendTo(*message)); - SuccessOrExit(error = message->SendTo(aDestination)); + error = message->SendTo(aDestination); exit: FreeMessageOnError(message, error); @@ -4300,96 +4324,94 @@ Error Mle::SendLinkMetricsManagementRequest(const Ip6::Address &aDestination, co } #endif -void Mle::RegisterParentResponseStatsCallback(otThreadParentResponseCallback aCallback, void *aContext) -{ - mParentResponseCb = aCallback; - mParentResponseCbContext = aContext; -} - #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -uint64_t Mle::CalcParentCslMetric(uint8_t aCslClockAccuracy, uint8_t aCslUncertainty) +uint64_t Mle::CalcParentCslMetric(const Mac::CslAccuracy &aCslAccuracy) const { - /* - * This function calculates the overall time that device will operate on battery - * by summming sequence of "ON quants" over a period of time. - */ - const uint64_t usInSecond = 1000000; - uint64_t cslPeriodUs = kMinCslPeriod * kUsPerTenSymbols; - uint64_t cslTimeoutUs = GetCslTimeout() * usInSecond; - uint64_t k = cslTimeoutUs / cslPeriodUs; + // This function calculates the overall time that device will operate + // on battery by summing sequence of "ON quants" over a period of time. + + static constexpr uint64_t usInSecond = 1000000; - return k * (k + 1) * cslPeriodUs / usInSecond * aCslClockAccuracy + aCslUncertainty * k * kUsPerUncertUnit; + uint64_t cslPeriodUs = kMinCslPeriod * kUsPerTenSymbols; + uint64_t cslTimeoutUs = GetCslTimeout() * usInSecond; + uint64_t k = cslTimeoutUs / cslPeriodUs; + + return k * (k + 1) * cslPeriodUs / usInSecond * aCslAccuracy.GetClockAccuracy() + + aCslAccuracy.GetUncertaintyInMicrosec() * k; } #endif Error Mle::DetachGracefully(otDetachGracefullyCallback aCallback, void *aContext) { - Error error = kErrorNone; + Error error = kErrorNone; + uint32_t timeout = kDetachGracefullyTimeout; VerifyOrExit(!IsDetachingGracefully(), error = kErrorBusy); - OT_ASSERT(mDetachGracefullyCallback == nullptr); + OT_ASSERT(!mDetachGracefullyCallback.IsSet()); - mDetachGracefullyCallback = aCallback; - mDetachGracefullyContext = aContext; + mDetachGracefullyCallback.Set(aCallback, aContext); - if (IsChild() || IsRouter()) - { - mDetachGracefullyTimer.Start(kDetachGracefullyTimeout); - } - else - { - // If the device is a leader, or it's already detached or disabled, we start the timer with zero duration to - // stop and invoke the callback when the timer fires, so the operation finishes immediately and asynchronously. - mDetachGracefullyTimer.Start(0); - } +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + Get().RequestStop(); +#endif - if (IsChild()) + switch (mRole) { - IgnoreError(SendChildUpdateRequest(/* aAppendChallenge */ false, /* aTimeout */ 0)); - } + case kRoleLeader: + break; + + case kRoleRouter: #if OPENTHREAD_FTD - else if (IsRouter()) - { - Get().SendAddressRelease(&Mle::HandleDetachGracefullyAddressReleaseResponse, this); - } + Get().SendAddressRelease(); #endif + break; + + case kRoleChild: + IgnoreError(SendChildUpdateRequest(kAppendZeroTimeout)); + break; + + case kRoleDisabled: + case kRoleDetached: + // If device is already detached or disabled, we start the timer + // with zero duration to stop and invoke the callback when the + // timer fires, so the operation finishes immediately and + // asynchronously. + timeout = 0; + break; + } + + mDetachGracefullyTimer.Start(timeout); exit: return error; } -void Mle::HandleDetachGracefullyTimer(Timer &aTimer) -{ - aTimer.Get().HandleDetachGracefullyTimer(); -} +void Mle::HandleDetachGracefullyTimer(void) { Stop(); } -void Mle::HandleDetachGracefullyTimer(void) -{ - Stop(); -} +//--------------------------------------------------------------------------------------------------------------------- +// TlvList -#if OPENTHREAD_FTD -void Mle::HandleDetachGracefullyAddressReleaseResponse(void * aContext, - otMessage * aMessage, - const otMessageInfo *aMessageInfo, - Error aResult) +void Mle::TlvList::Add(uint8_t aTlvType) { - OT_UNUSED_VARIABLE(aMessage); - OT_UNUSED_VARIABLE(aMessageInfo); - OT_UNUSED_VARIABLE(aResult); + VerifyOrExit(!Contains(aTlvType)); - static_cast(aContext)->HandleDetachGracefullyAddressReleaseResponse(); + if (PushBack(aTlvType) != kErrorNone) + { + LogWarn("Failed to include TLV %d", aTlvType); + } + +exit: + return; } -void Mle::HandleDetachGracefullyAddressReleaseResponse(void) +void Mle::TlvList::AddElementsFrom(const TlvList &aTlvList) { - if (IsDetachingGracefully()) + for (uint8_t tlvType : aTlvList) { - Stop(); + Add(tlvType); } } -#endif // OPENTHREAD_FTD //--------------------------------------------------------------------------------------------------------------------- // Challenge @@ -4427,7 +4449,7 @@ void Mle::DelayedResponseMetadata::RemoveFrom(Message &aMessage) const Mle::TxMessage *Mle::NewMleMessage(Command aCommand) { Error error = kErrorNone; - TxMessage * message; + TxMessage *message; Message::Settings settings(Message::kNoLinkSecurity, Message::kPriorityNet); Message::SubType subType; uint8_t securitySuite; @@ -4501,20 +4523,11 @@ Error Mle::TxMessage::AppendSourceAddressTlv(void) return Tlv::Append(*this, Get().GetRloc16()); } -Error Mle::TxMessage::AppendStatusTlv(StatusTlv::Status aStatus) -{ - return Tlv::Append(*this, aStatus); -} +Error Mle::TxMessage::AppendStatusTlv(StatusTlv::Status aStatus) { return Tlv::Append(*this, aStatus); } -Error Mle::TxMessage::AppendModeTlv(DeviceMode aMode) -{ - return Tlv::Append(*this, aMode.Get()); -} +Error Mle::TxMessage::AppendModeTlv(DeviceMode aMode) { return Tlv::Append(*this, aMode.Get()); } -Error Mle::TxMessage::AppendTimeoutTlv(uint32_t aTimeout) -{ - return Tlv::Append(*this, aTimeout); -} +Error Mle::TxMessage::AppendTimeoutTlv(uint32_t aTimeout) { return Tlv::Append(*this, aTimeout); } Error Mle::TxMessage::AppendChallengeTlv(const Challenge &aChallenge) { @@ -4543,7 +4556,7 @@ Error Mle::TxMessage::AppendLinkFrameCounterTlv(void) counter = Get().GetMaximumMacFrameCounter(); #if OPENTHREAD_CONFIG_MULTI_RADIO - Get().SetAllMacFrameCounters(counter); + Get().SetAllMacFrameCounters(counter, /* aSetIfLarger */ true); #endif return Tlv::Append(*this, counter); @@ -4554,10 +4567,7 @@ Error Mle::TxMessage::AppendMleFrameCounterTlv(void) return Tlv::Append(*this, Get().GetMleFrameCounter()); } -Error Mle::TxMessage::AppendAddress16Tlv(uint16_t aRloc16) -{ - return Tlv::Append(*this, aRloc16); -} +Error Mle::TxMessage::AppendAddress16Tlv(uint16_t aRloc16) { return Tlv::Append(*this, aRloc16); } Error Mle::TxMessage::AppendLeaderDataTlv(void) { @@ -4594,63 +4604,43 @@ Error Mle::TxMessage::AppendTlvRequestTlv(const uint8_t *aTlvs, uint8_t aTlvsLen return Tlv::Append(*this, aTlvs, aTlvsLength); } -Error Mle::TxMessage::AppendScanMaskTlv(uint8_t aScanMask) -{ - return Tlv::Append(*this, aScanMask); -} +Error Mle::TxMessage::AppendScanMaskTlv(uint8_t aScanMask) { return Tlv::Append(*this, aScanMask); } Error Mle::TxMessage::AppendLinkMarginTlv(uint8_t aLinkMargin) { return Tlv::Append(*this, aLinkMargin); } -Error Mle::TxMessage::AppendVersionTlv(void) -{ - return Tlv::Append(*this, kThreadVersion); -} +Error Mle::TxMessage::AppendVersionTlv(void) { return Tlv::Append(*this, kThreadVersion); } Error Mle::TxMessage::AppendAddressRegistrationTlv(AddressRegistrationMode aMode) { - Error error = kErrorNone; - Tlv tlv; - AddressRegistrationEntry entry; - Lowpan::Context context; - uint8_t length = 0; - uint8_t counter = 0; - uint16_t startOffset = GetLength(); -#if OPENTHREAD_CONFIG_DUA_ENABLE - Ip6::Address domainUnicastAddress; -#endif + Error error = kErrorNone; + Tlv tlv; + Lowpan::Context context; + uint8_t counter = 0; + uint16_t startOffset = GetLength(); tlv.SetType(Tlv::kAddressRegistration); SuccessOrExit(error = Append(tlv)); // Prioritize ML-EID - entry.SetContextId(kMeshLocalPrefixContextId); - entry.SetIid(Get().GetMeshLocal64().GetIid()); - SuccessOrExit(error = AppendBytes(&entry, entry.GetLength())); - length += entry.GetLength(); + SuccessOrExit(error = AppendCompressedAddressEntry(kMeshLocalPrefixContextId, Get().GetMeshLocal64())); // Continue to append the other addresses if not `kAppendMeshLocalOnly` mode VerifyOrExit(aMode != kAppendMeshLocalOnly); counter++; #if OPENTHREAD_CONFIG_DUA_ENABLE - // Cache Domain Unicast Address. - domainUnicastAddress = Get().GetDomainUnicastAddress(); - - if (Get().HasUnicastAddress(domainUnicastAddress)) + if (Get().HasUnicastAddress(Get().GetDomainUnicastAddress()) && + (Get().GetContext(Get().GetDomainUnicastAddress(), context) == kErrorNone)) { - SuccessOrAssert(Get().GetContext(domainUnicastAddress, context)); - // Prioritize DUA, compressed entry - entry.SetContextId(context.mContextId); - entry.SetIid(domainUnicastAddress.GetIid()); - SuccessOrExit(error = AppendBytes(&entry, entry.GetLength())); - length += entry.GetLength(); + SuccessOrExit( + error = AppendCompressedAddressEntry(context.mContextId, Get().GetDomainUnicastAddress())); counter++; } -#endif // OPENTHREAD_CONFIG_DUA_ENABLE +#endif for (const Ip6::Netif::UnicastAddress &addr : Get().GetUnicastAddresses()) { @@ -4661,8 +4651,7 @@ Error Mle::TxMessage::AppendAddressRegistrationTlv(AddressRegistrationMode aMode } #if OPENTHREAD_CONFIG_DUA_ENABLE - // Skip DUA that was already appended above. - if (addr.GetAddress() == domainUnicastAddress) + if (addr.GetAddress() == Get().GetDomainUnicastAddress()) { continue; } @@ -4670,22 +4659,16 @@ Error Mle::TxMessage::AppendAddressRegistrationTlv(AddressRegistrationMode aMode if (Get().GetContext(addr.GetAddress(), context) == kErrorNone) { - // compressed entry - entry.SetContextId(context.mContextId); - entry.SetIid(addr.GetAddress().GetIid()); + SuccessOrExit(error = AppendCompressedAddressEntry(context.mContextId, addr.GetAddress())); } else { - // uncompressed entry - entry.SetUncompressed(); - entry.SetIp6Address(addr.GetAddress()); + SuccessOrExit(error = AppendAddressEntry(addr.GetAddress())); } - SuccessOrExit(error = AppendBytes(&entry, entry.GetLength())); - length += entry.GetLength(); counter++; // only continue to append if there is available entry. - VerifyOrExit(counter < OPENTHREAD_CONFIG_MLE_IP_ADDRS_TO_REGISTER); + VerifyOrExit(counter < kMaxIpAddressesToRegister); } // Append external multicast addresses. For sleepy end device, @@ -4710,28 +4693,58 @@ Error Mle::TxMessage::AppendAddressRegistrationTlv(AddressRegistrationMode aMode } #endif - entry.SetUncompressed(); - entry.SetIp6Address(addr.GetAddress()); - SuccessOrExit(error = AppendBytes(&entry, entry.GetLength())); - length += entry.GetLength(); - + SuccessOrExit(error = AppendAddressEntry(addr.GetAddress())); counter++; // only continue to append if there is available entry. - VerifyOrExit(counter < OPENTHREAD_CONFIG_MLE_IP_ADDRS_TO_REGISTER); + VerifyOrExit(counter < kMaxIpAddressesToRegister); } } exit: - if (error == kErrorNone && length > 0) + if (error == kErrorNone) { - tlv.SetLength(length); + tlv.SetLength(static_cast(GetLength() - startOffset - sizeof(Tlv))); Write(startOffset, tlv); } return error; } +Error Mle::TxMessage::AppendCompressedAddressEntry(uint8_t aContextId, const Ip6::Address &aAddress) +{ + // Append an IPv6 address entry in an Address Registration TLV + // using compressed format (context ID with IID). + + Error error; + + SuccessOrExit(error = Append(AddressRegistrationTlv::ControlByteFor(aContextId))); + error = Append(aAddress.GetIid()); + +exit: + return error; +} + +Error Mle::TxMessage::AppendAddressEntry(const Ip6::Address &aAddress) +{ + // Append an IPv6 address entry in an Address Registration TLV + // using uncompressed format + + Error error; + uint8_t controlByte = AddressRegistrationTlv::kControlByteUncompressed; + + SuccessOrExit(error = Append(controlByte)); + error = Append(aAddress); + +exit: + return error; +} + +Error Mle::TxMessage::AppendSupervisionIntervalTlv(uint16_t aInterval) +{ + return Tlv::Append(*this, aInterval); +} + #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE Error Mle::TxMessage::AppendTimeRequestTlv(void) { @@ -4783,48 +4796,41 @@ Error Mle::TxMessage::AppendPendingTimestampTlv(void) #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE Error Mle::TxMessage::AppendCslChannelTlv(void) { - Error error = kErrorNone; CslChannelTlv cslChannel; - // In current implementation, it's allowed to set CSL Channel unspecified. As `0` is not valid for Channel value - // in CSL Channel TLV, if CSL channel is not specified, we don't append CSL Channel TLV. - // And on transmitter side, it would also set CSL Channel for the child to `0` if it doesn't find a CSL Channel - // TLV. - VerifyOrExit(Get().GetCslChannel()); + // CSL channel value of zero indicates that the CSL channel is not + // specified. We can use this value in the TLV as well. cslChannel.Init(); cslChannel.SetChannelPage(0); cslChannel.SetChannel(Get().GetCslChannel()); - SuccessOrExit(error = Append(cslChannel)); - -exit: - return error; + return Append(cslChannel); } Error Mle::TxMessage::AppendCslTimeoutTlv(void) { - OT_ASSERT(Get().IsCslEnabled()); - return Tlv::Append(*this, - Get().mCslTimeout == 0 ? Get().mTimeout : Get().mCslTimeout); + uint32_t timeout = Get().GetCslTimeout(); + + if (timeout == 0) + { + timeout = Get().GetTimeout(); + } + + return Tlv::Append(*this, timeout); } #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE Error Mle::TxMessage::AppendCslClockAccuracyTlv(void) { - Error error = kErrorNone; - CslClockAccuracyTlv cslClockAccuracy; - - cslClockAccuracy.Init(); - - cslClockAccuracy.SetCslClockAccuracy(Get().GetCslAccuracy()); - cslClockAccuracy.SetCslUncertainty(Get().GetCslUncertainty()); + CslClockAccuracyTlv cslClockAccuracyTlv; - SuccessOrExit(error = Append(cslClockAccuracy)); + cslClockAccuracyTlv.Init(); + cslClockAccuracyTlv.SetCslClockAccuracy(Get().GetCslAccuracy()); + cslClockAccuracyTlv.SetCslUncertainty(Get().GetCslUncertainty()); -exit: - return error; + return Append(cslClockAccuracyTlv); } #endif @@ -4896,14 +4902,12 @@ Error Mle::TxMessage::AppendConnectivityTlv(void) return tlv.AppendTo(*this); } -Error Mle::TxMessage::AppendAddresseRegisterationTlv(Child &aChild) +Error Mle::TxMessage::AppendAddressRegistrationTlv(Child &aChild) { - Error error; - Tlv tlv; - AddressRegistrationEntry entry; - Lowpan::Context context; - uint8_t length = 0; - uint16_t startOffset = GetLength(); + Error error; + Tlv tlv; + Lowpan::Context context; + uint16_t startOffset = GetLength(); tlv.SetType(Tlv::kAddressRegistration); SuccessOrExit(error = Append(tlv)); @@ -4912,26 +4916,19 @@ Error Mle::TxMessage::AppendAddresseRegisterationTlv(Child &aChild) { if (address.IsMulticast() || Get().GetContext(address, context) != kErrorNone) { - // uncompressed entry - entry.SetUncompressed(); - entry.SetIp6Address(address); + SuccessOrExit(error = AppendAddressEntry(address)); } else if (context.mContextId != kMeshLocalPrefixContextId) { - // compressed entry - entry.SetContextId(context.mContextId); - entry.SetIid(address.GetIid()); + SuccessOrExit(error = AppendCompressedAddressEntry(context.mContextId, address)); } else { continue; } - - SuccessOrExit(error = AppendBytes(&entry, entry.GetLength())); - length += entry.GetLength(); } - tlv.SetLength(length); + tlv.SetLength(static_cast(GetLength() - startOffset - sizeof(Tlv))); Write(startOffset, tlv); exit: @@ -4943,7 +4940,7 @@ Error Mle::TxMessage::AppendRouteTlv(Neighbor *aNeighbor) RouteTlv tlv; tlv.Init(); - Get().FillRouteTlv(tlv, aNeighbor); + Get().FillRouteTlv(tlv, aNeighbor); return tlv.AppendTo(*this); } @@ -4972,10 +4969,7 @@ Error Mle::RxMessage::ReadChallengeOrResponse(uint8_t aTlvType, Challenge &aBuff SuccessOrExit(error = Tlv::FindTlvValueOffset(*this, aTlvType, offset, length)); VerifyOrExit(length >= kMinChallengeSize, error = kErrorParse); - if (length > kMaxChallengeSize) - { - length = kMaxChallengeSize; - } + length = Min(length, kMaxChallengeSize); ReadBytes(offset, aBuffer.mBuffer, length); aBuffer.mLength = static_cast(length); @@ -5029,7 +5023,7 @@ Error Mle::RxMessage::ReadLeaderDataTlv(LeaderData &aLeaderData) const return error; } -Error Mle::RxMessage::ReadTlvRequestTlv(RequestedTlvs &aRequestedTlvs) const +Error Mle::RxMessage::ReadTlvRequestTlv(TlvList &aTlvList) const { Error error; uint16_t offset; @@ -5037,17 +5031,68 @@ Error Mle::RxMessage::ReadTlvRequestTlv(RequestedTlvs &aRequestedTlvs) const SuccessOrExit(error = Tlv::FindTlvValueOffset(*this, Tlv::kTlvRequest, offset, length)); - if (length > sizeof(aRequestedTlvs.mTlvs)) + if (length > aTlvList.GetMaxSize()) { - length = sizeof(aRequestedTlvs.mTlvs); + length = aTlvList.GetMaxSize(); } - ReadBytes(offset, aRequestedTlvs.mTlvs, length); - aRequestedTlvs.mNumTlvs = static_cast(length); + ReadBytes(offset, aTlvList.GetArrayBuffer(), length); + aTlvList.SetLength(static_cast(length)); exit: return error; } +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE +Error Mle::RxMessage::ReadCslClockAccuracyTlv(Mac::CslAccuracy &aCslAccuracy) const +{ + Error error; + CslClockAccuracyTlv clockAccuracyTlv; + + SuccessOrExit(error = Tlv::FindTlv(*this, clockAccuracyTlv)); + VerifyOrExit(clockAccuracyTlv.IsValid(), error = kErrorParse); + aCslAccuracy.SetClockAccuracy(clockAccuracyTlv.GetCslClockAccuracy()); + aCslAccuracy.SetUncertainty(clockAccuracyTlv.GetCslUncertainty()); + +exit: + return error; +} +#endif + +#if OPENTHREAD_FTD +Error Mle::RxMessage::ReadRouteTlv(RouteTlv &aRouteTlv) const +{ + Error error; + + SuccessOrExit(error = Tlv::FindTlv(*this, aRouteTlv)); + VerifyOrExit(aRouteTlv.IsValid(), error = kErrorParse); + +exit: + return error; +} +#endif + +//--------------------------------------------------------------------------------------------------------------------- +// ParentCandidate + +void Mle::ParentCandidate::Clear(void) +{ + Instance &instance = GetInstance(); + + memset(reinterpret_cast(this), 0, sizeof(ParentCandidate)); + Init(instance); +} + +void Mle::ParentCandidate::CopyTo(Parent &aParent) const +{ + // We use an intermediate pointer to copy `ParentCandidate` + // to silence code checker's warning about object slicing + // (assigning a sub-class to base class instance). + + const Parent *candidateAsParent = this; + + aParent = *candidateAsParent; +} + } // namespace Mle } // namespace ot diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 5d07436ef25..f33b525523f 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -36,6 +36,7 @@ #include "openthread-core-config.h" +#include "common/callback.hpp" #include "common/encoding.hpp" #include "common/locator.hpp" #include "common/log.hpp" @@ -72,6 +73,8 @@ namespace ot { * @} */ +class SupervisionListener; + /** * @namespace ot::Mle * @@ -99,8 +102,9 @@ class Mle : public InstanceLocator, private NonCopyable { friend class DiscoverScanner; friend class ot::Notifier; -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE - friend class ot::LinkMetrics::LinkMetrics; + friend class ot::SupervisionListener; +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE + friend class ot::LinkMetrics::Initiator; #endif public: @@ -415,7 +419,26 @@ class Mle : public InstanceLocator, private NonCopyable * @returns A reference to the parent. * */ - Router &GetParent(void) { return mParent; } + Parent &GetParent(void) { return mParent; } + + /** + * This method gets the parent when operating in End Device mode. + * + * @returns A reference to the parent. + * + */ + const Parent &GetParent(void) const { return mParent; } + + /** + * The method retrieves information about the parent. + * + * @param[out] aParentInfo Reference to a parent information structure. + * + * @retval kErrorNone Successfully retrieved the parent info and updated @p aParentInfo. + * @retval kErrorInvalidState Device role is not child. + * + */ + Error GetParentInfo(Router::Info &aParentInfo) const; /** * This method get the parent candidate. @@ -423,7 +446,17 @@ class Mle : public InstanceLocator, private NonCopyable * The parent candidate is valid when attempting to attach to a new parent. * */ - Router &GetParentCandidate(void) { return mParentCandidate; } + Parent &GetParentCandidate(void) { return mParentCandidate; } + + /** + * This method starts the process for child to search for a better parent while staying attached to its current + * parent + * + * @retval kErrorNone Successfully started the process to search for a better parent. + * @retval kErrorInvalidState Device role is not child. + * + */ + Error SearchForBetterParent(void); /** * This method indicates whether or not an IPv6 address is an RLOC. @@ -474,7 +507,7 @@ class Mle : public InstanceLocator, private NonCopyable * @returns The RLOC16 assigned to the Thread interface. * */ - uint16_t GetRloc16(void) const; + uint16_t GetRloc16(void) const { return mRloc16; } /** * This method returns a reference to the RLOC assigned to the Thread interface. @@ -557,100 +590,6 @@ class Mle : public InstanceLocator, private NonCopyable */ const LeaderData &GetLeaderData(void); - /** - * This method derives the Child ID from a given RLOC16. - * - * @param[in] aRloc16 The RLOC16 value. - * - * @returns The Child ID portion of an RLOC16. - * - */ - static uint16_t ChildIdFromRloc16(uint16_t aRloc16) { return aRloc16 & kMaxChildId; } - - /** - * This method derives the Router ID portion from a given RLOC16. - * - * @param[in] aRloc16 The RLOC16 value. - * - * @returns The Router ID portion of an RLOC16. - * - */ - static uint8_t RouterIdFromRloc16(uint16_t aRloc16) { return aRloc16 >> kRouterIdOffset; } - - /** - * This method returns whether the two RLOC16 have the same Router ID. - * - * @param[in] aRloc16A The first RLOC16 value. - * @param[in] aRloc16B The second RLOC16 value. - * - * @returns true if the two RLOC16 have the same Router ID, false otherwise. - * - */ - static bool RouterIdMatch(uint16_t aRloc16A, uint16_t aRloc16B) - { - return RouterIdFromRloc16(aRloc16A) == RouterIdFromRloc16(aRloc16B); - } - - /** - * This method returns the Service ID corresponding to a Service ALOC16. - * - * @param[in] aAloc16 The Service ALOC16 value. - * - * @returns The Service ID corresponding to given ALOC16. - * - */ - static uint8_t ServiceIdFromAloc(uint16_t aAloc16) { return static_cast(aAloc16 - kAloc16ServiceStart); } - - /** - * This method returns the Service ALOC16 corresponding to a Service ID. - * - * @param[in] aServiceId The Service ID value. - * - * @returns The Service ALOC16 corresponding to given ID. - * - */ - static uint16_t ServiceAlocFromId(uint8_t aServiceId) - { - return static_cast(aServiceId + kAloc16ServiceStart); - } - - /** - * This method returns the Commissioner Aloc corresponding to a Commissioner Session ID. - * - * @param[in] aSessionId The Commissioner Session ID value. - * - * @returns The Commissioner ALOC16 corresponding to given ID. - * - */ - static uint16_t CommissionerAloc16FromId(uint16_t aSessionId) - { - return static_cast((aSessionId & kAloc16CommissionerMask) + kAloc16CommissionerStart); - } - - /** - * This method derives RLOC16 from a given Router ID. - * - * @param[in] aRouterId The Router ID value. - * - * @returns The RLOC16 corresponding to the given Router ID. - * - */ - static uint16_t Rloc16FromRouterId(uint8_t aRouterId) - { - return static_cast(aRouterId << kRouterIdOffset); - } - - /** - * This method indicates whether or not @p aRloc16 refers to an active router. - * - * @param[in] aRloc16 The RLOC16 value. - * - * @retval TRUE If @p aRloc16 refers to an active router. - * @retval FALSE If @p aRloc16 does not refer to an active router. - * - */ - static bool IsActiveRouter(uint16_t aRloc16) { return ChildIdFromRloc16(aRloc16) == 0; } - /** * This method returns a reference to the send queue. * @@ -665,26 +604,27 @@ class Mle : public InstanceLocator, private NonCopyable */ void RemoveDelayedDataResponseMessage(void); - /** - * This method converts a device role into a human-readable string. - * - */ - static const char *RoleToString(DeviceRole aRole); - /** * This method gets the MLE counters. * * @returns A reference to the MLE counters. * */ - const otMleCounters &GetCounters(void) const { return mCounters; } + const Counters &GetCounters(void) + { +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + UpdateRoleTimeCounters(mRole); +#endif + return mCounters; + } /** * This method resets the MLE counters. * */ - void ResetCounters(void) { memset(&mCounters, 0, sizeof(mCounters)); } + void ResetCounters(void); +#if OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE /** * This function registers the client callback that is called when processing an MLE Parent Response message. * @@ -692,7 +632,11 @@ class Mle : public InstanceLocator, private NonCopyable * @param[in] aContext A pointer to application-specific context. * */ - void RegisterParentResponseStatsCallback(otThreadParentResponseCallback aCallback, void *aContext); + void RegisterParentResponseStatsCallback(otThreadParentResponseCallback aCallback, void *aContext) + { + mParentResponseCallback.Set(aCallback, aContext); + } +#endif /** * This method requests MLE layer to prepare and send a shorter version of Child ID Request message by only @@ -752,12 +696,12 @@ class Mle : public InstanceLocator, private NonCopyable /** * This method calculates CSL metric of parent. * - * @param[in] aCslClockAccuracy The CSL Clock Accuracy. - * @param[in] aCslUncertainty The CSL Uncertainty. + * @param[in] aCslAccuracy The CSL accuracy. * * @returns CSL metric. + * */ - uint64_t CalcParentCslMetric(uint8_t aCslClockAccuracy, uint8_t aCslUncertainty); + uint64_t CalcParentCslMetric(const Mac::CslAccuracy &aCslAccuracy) const; #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE @@ -903,6 +847,40 @@ class Mle : public InstanceLocator, private NonCopyable #endif }; + static constexpr uint8_t kMaxTlvListSize = 32; ///< Maximum number of TLVs in a `TlvList`. + + /** + * This type represents a list of TLVs (array of TLV types). + * + */ + class TlvList : public Array + { + public: + /** + * This constructor initializes the `TlvList` as empty. + * + */ + TlvList(void) = default; + + /** + * This method checks if a given TLV type is not already present in the list and adds it in the list. + * + * If the list is full, this method logs it as a warning. + * + * @param[in] aTlvType The TLV type to add to the list. + * + */ + void Add(uint8_t aTlvType); + + /** + * This method adds elements from a given list to this TLV list (if not already present in the list). + * + * @param[in] aTlvList The TLV list to add elements from. + * + */ + void AddElementsFrom(const TlvList &aTlvList); + }; + /** * This type represents a Challenge (or Response) data. * @@ -942,18 +920,6 @@ class Mle : public InstanceLocator, private NonCopyable bool operator==(const Challenge &aOther) const { return Matches(aOther.mBuffer, aOther.mLength); } }; - /** - * This type represents list of requested TLVs in a TLV Request TLV. - * - */ - struct RequestedTlvs - { - static constexpr uint8_t kMaxNumTlvs = 16; ///< Maximum number of TLVs in request array. - - uint8_t mTlvs[kMaxNumTlvs]; ///< Array of requested TLVs. - uint8_t mNumTlvs; ///< Number of TLVs in the array. - }; - /** * This class represents an MLE Tx message. * @@ -1078,6 +1044,22 @@ class Mle : public InstanceLocator, private NonCopyable */ Error AppendTlvRequestTlv(const uint8_t *aTlvs, uint8_t aTlvsLength); + /** + * This method appends a TLV Request TLV to the message. + * + * @tparam kArrayLength The TLV array length. + * + * @param[in] aTlvArray A reference to an array of TLV types of @p kArrayLength length. + * + * @retval kErrorNone Successfully appended the TLV Request TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the TLV Request TLV. + * + */ + template Error AppendTlvRequestTlv(const uint8_t (&aTlvArray)[kArrayLength]) + { + return AppendTlvRequestTlv(aTlvArray, kArrayLength); + } + /** * This method appends a Leader Data TLV to the message. * @@ -1140,6 +1122,17 @@ class Mle : public InstanceLocator, private NonCopyable */ Error AppendAddressRegistrationTlv(AddressRegistrationMode aMode = kAppendAllAddresses); + /** + * This method appends a Supervision Interval TLV to the message. + * + * @param[in] aInterval The interval value. + * + * @retval kErrorNone Successfully appended the Supervision Interval TLV. + * @retval kErrorNoBufs Insufficient buffers available to append the Supervision Interval TLV. + * + */ + Error AppendSupervisionIntervalTlv(uint16_t aInterval); + #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE /** * This method appends a Time Request TLV to the message. @@ -1265,7 +1258,7 @@ class Mle : public InstanceLocator, private NonCopyable * @retval kErrorNoBufs Insufficient buffers available to append the Connectivity TLV. * */ - Error AppendAddresseRegisterationTlv(Child &aChild); + Error AppendAddressRegistrationTlv(Child &aChild); #endif // OPENTHREAD_FTD /** @@ -1290,6 +1283,10 @@ class Mle : public InstanceLocator, private NonCopyable * */ Error SendAfterDelay(const Ip6::Address &aDestination, uint16_t aDelay); + + private: + Error AppendCompressedAddressEntry(uint8_t aContextId, const Ip6::Address &aAddress); + Error AppendAddressEntry(const Ip6::Address &aAddress); }; /** @@ -1344,14 +1341,14 @@ class Mle : public InstanceLocator, private NonCopyable /** * This method reads TLV Request TLV from the message. * - * @param[out] aRequestedTlvs A reference to output the read list of requested TLVs. + * @param[out] aTlvList A reference to output the read list of requested TLVs. * * @retval kErrorNone Successfully read the TLV. * @retval kErrorNotFound TLV was not found in the message. * @retval kErrorParse TLV was found but could not be parsed. * */ - Error ReadTlvRequestTlv(RequestedTlvs &aRequestedTlvs) const; + Error ReadTlvRequestTlv(TlvList &aTlvList) const; /** * This method reads Leader Data TLV from a message. @@ -1365,6 +1362,34 @@ class Mle : public InstanceLocator, private NonCopyable */ Error ReadLeaderDataTlv(LeaderData &aLeaderData) const; +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + /** + * This method reads CSL Clock Accuracy TLV from a message. + * + * @param[out] aCslAccuracy A reference to output the CSL accuracy. + * + * @retval kErrorNone Successfully read the TLV. + * @retval kErrorNotFound TLV was not found in the message. + * @retval kErrorParse TLV was found but could not be parsed. + * + */ + Error ReadCslClockAccuracyTlv(Mac::CslAccuracy &aCslAccuracy) const; +#endif + +#if OPENTHREAD_FTD + /** + * This method reads and validates Route TLV from a message. + * + * @param[out] aRouteTlv A reference to output the read Route TLV. + * + * @retval kErrorNone Successfully read and validated the Route TLV. + * @retval kErrorNotFound TLV was not found in the message. + * @retval kErrorParse TLV was found but could not be parsed or is not valid. + * + */ + Error ReadRouteTlv(RouteTlv &aRouteTlv) const; +#endif + private: Error ReadChallengeOrResponse(uint8_t aTlvType, Challenge &aBuffer) const; }; @@ -1404,11 +1429,21 @@ class Mle : public InstanceLocator, private NonCopyable { } - RxMessage & mMessage; ///< The MLE message. + /** + * This method indicates whether the `mNeighbor` (neighbor from which message was received) is non-null and + * in valid state. + * + * @retval TRUE If `mNeighbor` is non-null and in valid state. + * @retval FALSE If `mNeighbor` is `nullptr` or not in valid state. + * + */ + bool IsNeighborStateValid(void) const { return (mNeighbor != nullptr) && mNeighbor->IsStateValid(); } + + RxMessage &mMessage; ///< The MLE message. const Ip6::MessageInfo &mMessageInfo; ///< The `MessageInfo` associated with the message. uint32_t mFrameCounter; ///< The frame counter from aux security header. uint32_t mKeySequence; ///< The key sequence from aux security header. - Neighbor * mNeighbor; ///< Neighbor from which message was received (can be `nullptr`). + Neighbor *mNeighbor; ///< Neighbor from which message was received (can be `nullptr`). Class mClass; ///< The message class (authoritative, peer, or unknown). }; @@ -1446,6 +1481,28 @@ class Mle : public InstanceLocator, private NonCopyable */ void SetAttachState(AttachState aState); + /** + * This method initializes a given @p aNeighbor with information from @p aRxInfo. + * + * This method updates the following properties on @p aNeighbor from @p aRxInfo: + * + * - Sets the Extended MAC address from `MessageInfo` peer IPv6 address IID. + * - Clears the `GetLinkInfo()` and adds RSS from the `GetThreadLinkInfo()`. + * - Resets the link failure counter (`ResetLinkFailures()`). + * - Sets the "last heard" time to now (`SetLastHeard()`). + * + * @param[in,out] aNeighbor The `Neighbor` to initialize. + * @param[in] aRxInfo The `RxtInfo` to use for initialization. + * + */ + void InitNeighbor(Neighbor &aNeighbor, const RxInfo &aRxInfo); + + /** + * This method clears the parent candidate. + * + */ + void ClearParentCandidate(void) { mParentCandidate.Clear(); } + /** * This method checks if the destination is reachable. * @@ -1471,47 +1528,49 @@ class Mle : public InstanceLocator, private NonCopyable /** * This method generates an MLE Data Request message. * + * @param[in] aDestination The IPv6 destination address. + * + * @retval kErrorNone Successfully generated an MLE Data Request message. + * @retval kErrorNoBufs Insufficient buffers to generate the MLE Data Request message. + * + */ + Error SendDataRequest(const Ip6::Address &aDestination); + +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE + /** + * This method generates an MLE Data Request message which request Link Metrics Report TLV. + * * @param[in] aDestination A reference to the IPv6 address of the destination. - * @param[in] aTlvs A pointer to requested TLV types. - * @param[in] aTlvsLength The number of TLV types in @p aTlvs. - * @param[in] aDelay Delay in milliseconds before the Data Request message is sent. - * @param[in] aExtraTlvs A pointer to extra TLVs. - * @param[in] aExtraTlvsLength Length of extra TLVs. + * @param[in] aQueryInfo A Link Metrics query info. * * @retval kErrorNone Successfully generated an MLE Data Request message. * @retval kErrorNoBufs Insufficient buffers to generate the MLE Data Request message. * */ - Error SendDataRequest(const Ip6::Address &aDestination, - const uint8_t * aTlvs, - uint8_t aTlvsLength, - uint16_t aDelay, - const uint8_t * aExtraTlvs = nullptr, - uint8_t aExtraTlvsLength = 0); + Error SendDataRequestForLinkMetricsReport(const Ip6::Address &aDestination, + const LinkMetrics::Initiator::QueryInfo &aQueryInfo); +#endif /** * This method generates an MLE Child Update Request message. * - * @param[in] aAppendChallenge Indicates whether or not to include a Challenge TLV (even when already attached). - * * @retval kErrorNone Successfully generated an MLE Child Update Request message. * @retval kErrorNoBufs Insufficient buffers to generate the MLE Child Update Request message. * */ - Error SendChildUpdateRequest(bool aAppendChallenge = false); + Error SendChildUpdateRequest(void); /** * This method generates an MLE Child Update Response message. * - * @param[in] aTlvs A pointer to requested TLV types. - * @param[in] aNumTlvs The number of TLV types in @p aTlvs. + * @param[in] aTlvList A list of requested TLV types. * @param[in] aChallenge The Challenge for the response. * * @retval kErrorNone Successfully generated an MLE Child Update Response message. * @retval kErrorNoBufs Insufficient buffers to generate the MLE Child Update Response message. * */ - Error SendChildUpdateResponse(const uint8_t *aTlvs, uint8_t aNumTlvs, const Challenge &aChallenge); + Error SendChildUpdateResponse(const TlvList &aTlvList, const Challenge &aChallenge); /** * This method sets the RLOC16 assigned to the Thread interface. @@ -1653,14 +1712,13 @@ class Mle : public InstanceLocator, private NonCopyable * This method sends a Link Metrics Management Request message. * * @param[in] aDestination A reference to the IPv6 address of the destination. - * @param[in] aSubTlvs A pointer to the buffer of the sub-TLVs in the message. - * @param[in] aLength The overall length of @p aSubTlvs. + * @param[in] aSubTlv A reference to the sub-TLV to include. * * @retval kErrorNone Successfully sent a Link Metrics Management Request. * @retval kErrorNoBufs Insufficient buffers to generate the MLE Link Metrics Management Request message. * */ - Error SendLinkMetricsManagementRequest(const Ip6::Address &aDestination, const uint8_t *aSubTlvs, uint8_t aLength); + Error SendLinkMetricsManagementRequest(const Ip6::Address &aDestination, const ot::Tlv &aSubTlv); /** * This method sends an MLE Link Probe message. @@ -1679,51 +1737,45 @@ class Mle : public InstanceLocator, private NonCopyable #endif - /** - * This method indicates whether the device is detaching gracefully. - * - * @retval TRUE Detaching is in progress. - * @retval FALSE Not detaching. - * - */ - bool IsDetachingGracefully(void) { return mDetachGracefullyTimer.IsRunning(); } + void ScheduleMessageTransmissionTimer(void); - Ip6::Netif::UnicastAddress mLeaderAloc; ///< Leader anycast locator +private: + // Declare early so we can use in as `TimerMilli` callbacks. + void HandleAttachTimer(void); + void HandleDelayedResponseTimer(void); + void HandleMessageTransmissionTimer(void); - LeaderData mLeaderData; ///< Last received Leader Data TLV. - bool mRetrieveNewNetworkData; ///< Indicating new Network Data is needed if set. - DeviceRole mRole; ///< Current Thread role. - Router mParent; ///< Parent information. - Router mParentCandidate; ///< Parent candidate information. - NeighborTable mNeighborTable; ///< The neighbor table. - DeviceMode mDeviceMode; ///< Device mode setting. - AttachState mAttachState; ///< The attach state. - uint8_t mParentRequestCounter; ///< Number of parent requests while in `kAttachStateParentRequest`. - ReattachState mReattachState; ///< Reattach state - uint16_t mAttachCounter; ///< Attach attempt counter. - uint16_t mAnnounceDelay; ///< Delay in between sending Announce messages during attach. - TimerMilli mAttachTimer; ///< The timer for driving the attach process. - TimerMilli mDelayedResponseTimer; ///< The timer to delay MLE responses. - TimerMilli mMessageTransmissionTimer; ///< The timer for (re-)sending of MLE messages (e.g. Child Update). - TimerMilli mDetachGracefullyTimer; - uint8_t mParentLeaderCost; - - otDetachGracefullyCallback mDetachGracefullyCallback; - void * mDetachGracefullyContext; +protected: + using AttachTimer = TimerMilliIn; + using DelayTimer = TimerMilliIn; + using MsgTxTimer = TimerMilliIn; - static constexpr uint32_t kDetachGracefullyTimeout = 1000; + Ip6::Netif::UnicastAddress mLeaderAloc; ///< Leader anycast locator + + LeaderData mLeaderData; ///< Last received Leader Data TLV. + bool mRetrieveNewNetworkData : 1; ///< Indicating new Network Data is needed if set. + bool mRequestRouteTlv : 1; ///< Request Route TLV when sending Data Request. + DeviceRole mRole; ///< Current Thread role. + Parent mParent; ///< Parent information. + NeighborTable mNeighborTable; ///< The neighbor table. + DeviceMode mDeviceMode; ///< Device mode setting. + AttachState mAttachState; ///< The attach state. + uint8_t mParentRequestCounter; ///< Number of parent requests while in `kAttachStateParentRequest`. + ReattachState mReattachState; ///< Reattach state + uint16_t mAttachCounter; ///< Attach attempt counter. + uint16_t mAnnounceDelay; ///< Delay in between sending Announce messages during attach. + AttachTimer mAttachTimer; ///< The timer for driving the attach process. + DelayTimer mDelayedResponseTimer; ///< The timer to delay MLE responses. + MsgTxTimer mMessageTransmissionTimer; ///< The timer for (re-)sending of MLE messages (e.g. Child Update). +#if OPENTHREAD_FTD + uint8_t mLinkRequestAttempts; ///< Number of remaining link requests to send after reset. + bool mWasLeader; ///< Indicating if device was leader before reset. +#endif private: static constexpr uint8_t kMleHopLimit = 255; static constexpr uint8_t kMleSecurityTagSize = 4; // Security tag size in bytes. - // Parameters related to "periodic parent search" feature (CONFIG_ENABLE_PERIODIC_PARENT_SEARCH). - // All timer intervals are converted to milliseconds. - static constexpr uint32_t kParentSearchCheckInterval = (OPENTHREAD_CONFIG_PARENT_SEARCH_CHECK_INTERVAL * 1000u); - static constexpr uint32_t kParentSearchBackoffInterval = (OPENTHREAD_CONFIG_PARENT_SEARCH_BACKOFF_INTERVAL * 1000u); - static constexpr uint32_t kParentSearchJitterInterval = (15 * 1000u); - static constexpr int8_t kParentSearchRssThreadhold = OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD; - // Parameters for "attach backoff" feature (CONFIG_ENABLE_ATTACH_BACKOFF) - Intervals are in milliseconds. static constexpr uint32_t kAttachBackoffMinInterval = OPENTHREAD_CONFIG_MLE_ATTACH_BACKOFF_MINIMUM_INTERVAL; static constexpr uint32_t kAttachBackoffMaxInterval = OPENTHREAD_CONFIG_MLE_ATTACH_BACKOFF_MAXIMUM_INTERVAL; @@ -1745,6 +1797,12 @@ class Mle : public InstanceLocator, private NonCopyable static constexpr uint8_t kNextAttachCycleTotalParentRequests = 2; static constexpr uint8_t kNextAttachCycleNumParentRequestToRouters = 1; + static constexpr uint32_t kDetachGracefullyTimeout = 1000; + + static constexpr uint32_t kStoreFrameCounterAhead = OPENTHREAD_CONFIG_STORE_FRAME_COUNTER_AHEAD; + static constexpr uint8_t kMaxIpAddressesToRegister = OPENTHREAD_CONFIG_MLE_IP_ADDRS_TO_REGISTER; + static constexpr uint32_t kDefaultCslTimeout = OPENTHREAD_CONFIG_CSL_TIMEOUT; + enum StartMode : uint8_t // Used in `Start()`. { kNormalAttach, @@ -1776,6 +1834,13 @@ class Mle : public InstanceLocator, private NonCopyable kChildUpdateRequestActive, // Child Update Request has been sent and Child Update Response is expected. }; + enum ChildUpdateRequestMode : uint8_t // Used in `SendChildUpdateRequest()` + { + kNormalChildUpdateRequest, // Normal Child Update Request. + kAppendChallengeTlv, // Append Challenge TLV to Child Update Request even if currently attached. + kAppendZeroTimeout, // Use zero timeout when appending Timeout TLV (used for graceful detach). + }; + enum DataRequestState : uint8_t { kDataRequestNone, // Not waiting for a Data Response. @@ -1816,7 +1881,8 @@ class Mle : public InstanceLocator, private NonCopyable } private: - static constexpr uint8_t kKeyIdMode2Mic32 = (Mac::Frame::kKeyIdMode2 | Mac::Frame::kSecEncMic32); + static constexpr uint8_t kKeyIdMode2Mic32 = + static_cast(Mac::Frame::kKeyIdMode2) | static_cast(Mac::Frame::kSecurityEncMic32); uint8_t mSecurityControl; uint32_t mFrameCounter; @@ -1824,6 +1890,25 @@ class Mle : public InstanceLocator, private NonCopyable uint8_t mKeyIndex; } OT_TOOL_PACKED_END; + class ParentCandidate : public Parent + { + public: + void Init(Instance &aInstance) { Parent::Init(aInstance); } + void Clear(void); + void CopyTo(Parent &aParent) const; + + Challenge mChallenge; + int8_t mPriority; + uint8_t mLinkQuality3; + uint8_t mLinkQuality2; + uint8_t mLinkQuality1; + uint16_t mSedBufferSize; + uint8_t mSedDatagramCount; + uint8_t mLinkMargin; + LeaderData mLeaderData; + bool mIsSingleton; + }; + #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE class ServiceAloc : public Ip6::Netif::UnicastAddress { @@ -1840,27 +1925,70 @@ class Mle : public InstanceLocator, private NonCopyable }; #endif +#if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE + void HandleParentSearchTimer(void) { mParentSearch.HandleTimer(); } + + class ParentSearch : public InstanceLocator + { + public: + explicit ParentSearch(Instance &aInstance) + : InstanceLocator(aInstance) + , mIsInBackoff(false) + , mBackoffWasCanceled(false) + , mRecentlyDetached(false) + , mBackoffCancelTime(0) + , mTimer(aInstance) + { + } + + void StartTimer(void); + void UpdateState(void); + void SetRecentlyDetached(void) { mRecentlyDetached = true; } + void HandleTimer(void); + + private: + // All timer intervals are converted to milliseconds. + static constexpr uint32_t kCheckInterval = (OPENTHREAD_CONFIG_PARENT_SEARCH_CHECK_INTERVAL * 1000u); + static constexpr uint32_t kBackoffInterval = (OPENTHREAD_CONFIG_PARENT_SEARCH_BACKOFF_INTERVAL * 1000u); + static constexpr uint32_t kJitterInterval = (15 * 1000u); + static constexpr int8_t kRssThreshold = OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD; + + using SearchTimer = TimerMilliIn; + + bool mIsInBackoff : 1; + bool mBackoffWasCanceled : 1; + bool mRecentlyDetached : 1; + TimeMilli mBackoffCancelTime; + SearchTimer mTimer; + }; +#endif // OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE + Error Start(StartMode aMode); void Stop(StopMode aMode); void HandleNotifierEvents(Events aEvents); - static void HandleAttachTimer(Timer &aTimer); - void HandleAttachTimer(void); - static void HandleDelayedResponseTimer(Timer &aTimer); - void HandleDelayedResponseTimer(void); void SendDelayedResponse(TxMessage &aMessage, const DelayedResponseMetadata &aMetadata); - static void HandleMessageTransmissionTimer(Timer &aTimer); - void HandleMessageTransmissionTimer(void); static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - void ScheduleMessageTransmissionTimer(void); void ReestablishLinkWithNeighbor(Neighbor &aNeighbor); static void HandleDetachGracefullyTimer(Timer &aTimer); void HandleDetachGracefullyTimer(void); - Error SendChildUpdateRequest(bool aAppendChallenge, uint32_t aTimeout); + bool IsDetachingGracefully(void) { return mDetachGracefullyTimer.IsRunning(); } + Error SendChildUpdateRequest(ChildUpdateRequestMode aMode); + Error SendDataRequestAfterDelay(const Ip6::Address &aDestination, uint16_t aDelay); + +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE + Error SendDataRequest(const Ip6::Address &aDestination, + const uint8_t *aTlvs, + uint8_t aTlvsLength, + uint16_t aDelay, + const LinkMetrics::Initiator::QueryInfo *aQueryInfo = nullptr); +#else + Error SendDataRequest(const Ip6::Address &aDestination, const uint8_t *aTlvs, uint8_t aTlvsLength, uint16_t aDelay); +#endif #if OPENTHREAD_FTD - static void HandleDetachGracefullyAddressReleaseResponse(void * aContext, - otMessage * aMessage, + static void HandleDetachGracefullyAddressReleaseResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); void HandleDetachGracefullyAddressReleaseResponse(void); @@ -1875,12 +2003,10 @@ class Mle : public InstanceLocator, private NonCopyable void HandleAnnounce(RxInfo &aRxInfo); #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE void HandleLinkMetricsManagementRequest(RxInfo &aRxInfo); + void HandleLinkProbe(RxInfo &aRxInfo); #endif #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE void HandleLinkMetricsManagementResponse(RxInfo &aRxInfo); -#endif -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE - void HandleLinkProbe(RxInfo &aRxInfo); #endif Error HandleLeaderData(RxInfo &aRxInfo); void ProcessAnnounce(void); @@ -1889,8 +2015,8 @@ class Mle : public InstanceLocator, private NonCopyable uint32_t GetAttachStartDelay(void) const; void SendParentRequest(ParentRequestType aType); Error SendChildIdRequest(void); - Error GetNextAnnouceChannel(uint8_t &aChannel) const; - bool HasMoreChannelsToAnnouce(void) const; + Error GetNextAnnounceChannel(uint8_t &aChannel) const; + bool HasMoreChannelsToAnnounce(void) const; bool PrepareAnnounceState(void); void SendAnnounce(uint8_t aChannel, AnnounceMode aMode); void SendAnnounce(uint8_t aChannel, const Ip6::Address &aDestination, AnnounceMode aMode = kNormalAnnounce); @@ -1903,20 +2029,19 @@ class Mle : public InstanceLocator, private NonCopyable bool HasAcceptableParentCandidate(void) const; Error DetermineParentRequestType(ParentRequestType &aType) const; - bool IsBetterParent(uint16_t aRloc16, - LinkQuality aLinkQuality, - uint8_t aLinkMargin, - const ConnectivityTlv &aConnectivityTlv, - uint8_t aVersion, - uint8_t aCslClockAccuracy, - uint8_t aCslUncertainty); + bool IsBetterParent(uint16_t aRloc16, + LinkQuality aLinkQuality, + uint8_t aLinkMargin, + const ConnectivityTlv &aConnectivityTlv, + uint16_t aVersion, + const Mac::CslAccuracy &aCslAccuracy); bool IsNetworkDataNewer(const LeaderData &aLeaderData); Error ProcessMessageSecurity(Crypto::AesCcm::Mode aMode, - Message & aMessage, + Message &aMessage, const Ip6::MessageInfo &aMessageInfo, uint16_t aCmdOffset, - const SecurityHeader & aHeader); + const SecurityHeader &aHeader); #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE ServiceAloc *FindInServiceAlocs(uint16_t aAloc16); @@ -1927,13 +2052,6 @@ class Mle : public InstanceLocator, private NonCopyable void InformPreviousParent(void); #endif -#if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE - static void HandleParentSearchTimer(Timer &aTimer); - void HandleParentSearchTimer(void); - void StartParentSearchTimer(void); - void UpdateParentSearchState(void); -#endif - #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) static void LogError(MessageAction aAction, MessageType aType, Error aError); static const char *MessageActionToString(MessageAction aAction); @@ -1941,17 +2059,18 @@ class Mle : public InstanceLocator, private NonCopyable static const char *MessageTypeActionToSuffixString(MessageType aType, MessageAction aAction); #endif +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + void UpdateRoleTimeCounters(DeviceRole aRole); +#endif + + using DetachGracefullyTimer = TimerMilliIn; + MessageQueue mDelayedResponses; Challenge mParentRequestChallenge; - AttachMode mAttachMode; - int8_t mParentPriority; - uint8_t mParentLinkQuality3; - uint8_t mParentLinkQuality2; - uint8_t mParentLinkQuality1; - uint16_t mParentSedBufferSize; - uint8_t mParentSedDatagramCount; + AttachMode mAttachMode; + ParentCandidate mParentCandidate; uint8_t mChildUpdateAttempts; ChildUpdateRequestState mChildUpdateRequestState; @@ -1960,13 +2079,9 @@ class Mle : public InstanceLocator, private NonCopyable AddressRegistrationMode mAddressRegistrationMode; - bool mHasRestored; - uint8_t mParentLinkMargin; - bool mParentIsSingleton; - bool mReceivedResponseFromParent; - LeaderData mParentLeaderData; - - Challenge mParentCandidateChallenge; + bool mHasRestored; + bool mReceivedResponseFromParent; + bool mInitiallyAttachedAsSleepy; Ip6::Udp::Socket mSocket; uint32_t mTimeout; @@ -1974,14 +2089,11 @@ class Mle : public InstanceLocator, private NonCopyable uint32_t mCslTimeout; #endif + uint16_t mRloc16; uint16_t mPreviousParentRloc; #if OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE - bool mParentSearchIsInBackoff : 1; - bool mParentSearchBackoffWasCanceled : 1; - bool mParentSearchRecentlyDetached : 1; - TimeMilli mParentSearchBackoffCancelTime; - TimerMilli mParentSearchTimer; + ParentSearch mParentSearch; #endif uint8_t mAnnounceChannel; @@ -1993,7 +2105,10 @@ class Mle : public InstanceLocator, private NonCopyable ServiceAloc mServiceAlocs[kMaxServiceAlocs]; #endif - otMleCounters mCounters; + Counters mCounters; +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + uint64_t mLastUpdatedTimestamp; +#endif static const otMeshLocalPrefix sMeshLocalPrefixInit; @@ -2003,8 +2118,12 @@ class Mle : public InstanceLocator, private NonCopyable Ip6::Netif::MulticastAddress mLinkLocalAllThreadNodes; Ip6::Netif::MulticastAddress mRealmLocalAllThreadNodes; - otThreadParentResponseCallback mParentResponseCb; - void * mParentResponseCbContext; + DetachGracefullyTimer mDetachGracefullyTimer; + Callback mDetachGracefullyCallback; + +#if OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE + Callback mParentResponseCallback; +#endif }; } // namespace Mle diff --git a/src/core/thread/mle_router.cpp b/src/core/thread/mle_router.cpp index d630347f95e..bade0cb6437 100644 --- a/src/core/thread/mle_router.cpp +++ b/src/core/thread/mle_router.cpp @@ -40,6 +40,7 @@ #include "common/encoding.hpp" #include "common/instance.hpp" #include "common/locator_getters.hpp" +#include "common/num_utils.hpp" #include "common/random.hpp" #include "common/serial_number.hpp" #include "common/settings.hpp" @@ -51,6 +52,7 @@ #include "thread/thread_tlvs.hpp" #include "thread/time_sync_service.hpp" #include "thread/uri_paths.hpp" +#include "thread/version.hpp" #include "utils/otns.hpp" namespace ot { @@ -61,8 +63,6 @@ RegisterLogModule("Mle"); MleRouter::MleRouter(Instance &aInstance) : Mle(aInstance) , mAdvertiseTrickleTimer(aInstance, MleRouter::HandleAdvertiseTrickleTimer) - , mAddressSolicit(UriPath::kAddressSolicit, &MleRouter::HandleAddressSolicit, this) - , mAddressRelease(UriPath::kAddressRelease, &MleRouter::HandleAddressRelease, this) , mChildTable(aInstance) , mRouterTable(aInstance) , mChallengeTimeout(0) @@ -70,7 +70,6 @@ MleRouter::MleRouter(Instance &aInstance) , mNetworkIdTimeout(kNetworkIdTimeout) , mRouterUpgradeThreshold(kRouterUpgradeThreshold) , mRouterDowngradeThreshold(kRouterDowngradeThreshold) - , mLeaderWeight(kLeaderWeight) #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE , mPreferredLeaderPartitionId(0) , mCcmEnabled(false) @@ -85,7 +84,7 @@ MleRouter::MleRouter(Instance &aInstance) , mPreviousPartitionIdTimeout(0) , mRouterSelectionJitter(kRouterSelectionJitter) , mRouterSelectionJitterTimeout(0) - , mLinkRequestDelay(0) + , mChildRouterLinks(kChildRouterLinks) , mParentPriority(kParentPriorityUnspecified) #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE , mBackboneRouterRegistrationDelay(0) @@ -93,10 +92,9 @@ MleRouter::MleRouter(Instance &aInstance) #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE , mMaxChildIpAddresses(0) #endif - , mDiscoveryRequestCallback(nullptr) - , mDiscoveryRequestCallbackContext(nullptr) { mDeviceMode.Set(mDeviceMode.Get() | DeviceMode::kModeFullThreadDevice | DeviceMode::kModeFullNetworkData); + mLeaderWeight = mDeviceProperties.CalculateLeaderWeight(); SetRouterId(kInvalidRouterId); @@ -185,6 +183,13 @@ Error MleRouter::SetRouterEligible(bool aEligible) return error; } +void MleRouter::SetDeviceProperties(const DeviceProperties &aDeviceProperties) +{ + mDeviceProperties = aDeviceProperties; + mDeviceProperties.ClampWeightAdjustment(); + SetLeaderWeight(mDeviceProperties.CalculateLeaderWeight()); +} + Error MleRouter::BecomeRouter(ThreadStatusTlv::Status aStatus) { Error error = kErrorNone; @@ -197,12 +202,21 @@ Error MleRouter::BecomeRouter(ThreadStatusTlv::Status aStatus) Get().SetRxOnWhenIdle(true); mRouterSelectionJitterTimeout = 0; - mLinkRequestDelay = 0; switch (mRole) { case kRoleDetached: + // If router had more than `kMinCriticalChildrenCount` children + // or was a leader prior to reset we treat the multicast Link + // Request as a critical message. + mLinkRequestAttempts = + (mWasLeader || mChildTable.GetNumChildren(Child::kInStateValidOrRestoring) >= kMinCriticalChildrenCount) + ? kMaxCriticalTransmissionCount + : kMaxTransmissionCount; + SuccessOrExit(error = SendLinkRequest(nullptr)); + mLinkRequestAttempts--; + ScheduleMessageTransmissionTimer(); Get().RegisterReceiver(TimeTicker::kMleRouter); break; @@ -212,7 +226,6 @@ Error MleRouter::BecomeRouter(ThreadStatusTlv::Status aStatus) default: OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); } exit: @@ -222,13 +235,9 @@ Error MleRouter::BecomeRouter(ThreadStatusTlv::Status aStatus) Error MleRouter::BecomeLeader(void) { Error error = kErrorNone; - Router * router; + Router *router; uint32_t partitionId; uint8_t leaderId; -#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE - uint8_t minRouterId; - uint8_t maxRouterId; -#endif VerifyOrExit(!Get().IsPartiallyComplete(), error = kErrorInvalidState); VerifyOrExit(!IsDisabled(), error = kErrorInvalidState); @@ -238,24 +247,20 @@ Error MleRouter::BecomeLeader(void) mRouterTable.Clear(); #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE - partitionId = mPreferredLeaderPartitionId ? mPreferredLeaderPartitionId : Random::NonCrypto::GetUint32(); -#else - partitionId = Random::NonCrypto::GetUint32(); -#endif - -#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE - mRouterTable.GetRouterIdRange(minRouterId, maxRouterId); - if (IsRouterIdValid(mPreviousRouterId) && minRouterId <= mPreviousRouterId && mPreviousRouterId <= maxRouterId) - { - leaderId = mPreviousRouterId; - } - else { - leaderId = Random::NonCrypto::GetUint8InRange(minRouterId, maxRouterId + 1); + uint8_t minId; + uint8_t maxId; + + mRouterTable.GetRouterIdRange(minId, maxId); + partitionId = mPreferredLeaderPartitionId ? mPreferredLeaderPartitionId : Random::NonCrypto::GetUint32(); + leaderId = (IsRouterIdValid(mPreviousRouterId) && minId <= mPreviousRouterId && mPreviousRouterId <= maxId) + ? mPreviousRouterId + : Random::NonCrypto::GetUint8InRange(minId, maxId + 1); } #else + partitionId = Random::NonCrypto::GetUint32(); leaderId = IsRouterIdValid(mPreviousRouterId) ? mPreviousRouterId - : Random::NonCrypto::GetUint8InRange(0, kMaxRouterId + 1); + : Random::NonCrypto::GetUint8InRange(0, kMaxRouterId + 1); #endif SetLeaderData(partitionId, mLeaderWeight, leaderId); @@ -277,12 +282,7 @@ Error MleRouter::BecomeLeader(void) void MleRouter::StopLeader(void) { - Get().RemoveResource(mAddressSolicit); - Get().RemoveResource(mAddressRelease); - Get().StopLeader(); - Get().StopLeader(); StopAdvertiseTrickleTimer(); - Get().Stop(); Get().UnsubscribeAllRoutersMulticast(); } @@ -295,7 +295,6 @@ void MleRouter::HandleDetachStart(void) void MleRouter::HandleChildStart(AttachMode aMode) { - mLinkRequestDelay = 0; mAddressSolicitRejected = false; mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter); @@ -317,13 +316,11 @@ void MleRouter::HandleChildStart(AttachMode aMode) case kDowngradeToReed: SendAddressRelease(); - // reset children info if any if (HasChildren()) { RemoveChildren(); } - // reset routerId info SetRouterId(kInvalidRouterId); break; @@ -374,68 +371,50 @@ void MleRouter::HandleChildStart(AttachMode aMode) void MleRouter::SetStateRouter(uint16_t aRloc16) { - SetRloc16(aRloc16); - - SetRole(kRoleRouter); - SetAttachState(kAttachStateIdle); - mAttachCounter = 0; - mAttachTimer.Stop(); - mMessageTransmissionTimer.Stop(); - StopAdvertiseTrickleTimer(); - ResetAdvertiseInterval(); + // The `aStartMode` is ignored when used with `kRoleRouter` + SetStateRouterOrLeader(kRoleRouter, aRloc16, /* aStartMode */ kStartingAsLeader); +} - Get().SubscribeAllRoutersMulticast(); - mPreviousPartitionIdRouter = mLeaderData.GetPartitionId(); - Get().Stop(); - Get().SetForwardingEnabled(true); - Get().SetTimerExpirations(kMplRouterDataMessageTimerExpirations); - Get().SetBeaconEnabled(true); +void MleRouter::SetStateLeader(uint16_t aRloc16, LeaderStartMode aStartMode) +{ + SetStateRouterOrLeader(kRoleLeader, aRloc16, aStartMode); +} - // remove children that do not have matching RLOC16 - for (Child &child : Get().Iterate(Child::kInStateValidOrRestoring)) +void MleRouter::SetStateRouterOrLeader(DeviceRole aRole, uint16_t aRloc16, LeaderStartMode aStartMode) +{ + if (aRole == kRoleLeader) { - if (RouterIdFromRloc16(child.GetRloc16()) != mRouterId) - { - RemoveNeighbor(child); - } + IgnoreError(Get().Restore()); + IgnoreError(Get().Restore()); } -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - Get().UpdateCsl(); -#endif -} - -void MleRouter::SetStateLeader(uint16_t aRloc16, LeaderStartMode aStartMode) -{ - IgnoreError(Get().Restore()); - IgnoreError(Get().Restore()); SetRloc16(aRloc16); - SetRole(kRoleLeader); + SetRole(aRole); + SetAttachState(kAttachStateIdle); mAttachCounter = 0; mAttachTimer.Stop(); mMessageTransmissionTimer.Stop(); StopAdvertiseTrickleTimer(); ResetAdvertiseInterval(); - IgnoreError(GetLeaderAloc(mLeaderAloc.GetAddress())); - Get().AddUnicastAddress(mLeaderAloc); Get().SubscribeAllRoutersMulticast(); mPreviousPartitionIdRouter = mLeaderData.GetPartitionId(); - Get().RegisterReceiver(TimeTicker::kMleRouter); - - Get().Start(aStartMode); - Get().StartLeader(); - Get().StartLeader(); - Get().AddResource(mAddressSolicit); - Get().AddResource(mAddressRelease); - Get().SetForwardingEnabled(true); - Get().SetTimerExpirations(kMplRouterDataMessageTimerExpirations); Get().SetBeaconEnabled(true); - Get().Clear(); - // remove children that do not have matching RLOC16 + if (aRole == kRoleLeader) + { + IgnoreError(GetLeaderAloc(mLeaderAloc.GetAddress())); + Get().AddUnicastAddress(mLeaderAloc); + Get().RegisterReceiver(TimeTicker::kMleRouter); + Get().Start(aStartMode); + Get().StartLeader(); + Get().StartLeader(); + Get().Clear(); + } + + // Remove children that do not have matching RLOC16 for (Child &child : Get().Iterate(Child::kInStateValidOrRestoring)) { if (RouterIdFromRloc16(child.GetRloc16()) != mRouterId) @@ -448,7 +427,7 @@ void MleRouter::SetStateLeader(uint16_t aRloc16, LeaderStartMode aStartMode) Get().UpdateCsl(); #endif - LogNote("Leader partition id 0x%x", mLeaderData.GetPartitionId()); + LogNote("Partition ID 0x%lx", ToUlong(mLeaderData.GetPartitionId())); } void MleRouter::HandleAdvertiseTrickleTimer(TrickleTimer &aTimer) @@ -466,10 +445,7 @@ void MleRouter::HandleAdvertiseTrickleTimer(void) return; } -void MleRouter::StopAdvertiseTrickleTimer(void) -{ - mAdvertiseTrickleTimer.Stop(); -} +void MleRouter::StopAdvertiseTrickleTimer(void) { mAdvertiseTrickleTimer.Stop(); } void MleRouter::ResetAdvertiseInterval(void) { @@ -491,7 +467,7 @@ void MleRouter::SendAdvertisement(void) { Error error = kErrorNone; Ip6::Address destination; - TxMessage * message = nullptr; + TxMessage *message = nullptr; // Suppress MLE Advertisements when trying to attach to a better partition. // @@ -506,22 +482,12 @@ void MleRouter::SendAdvertisement(void) // children to detach. VerifyOrExit(!mAddressSolicitPending); - // Suppress MLE Advertisements before sending multicast Link Request. - // - // Before sending the multicast Link Request message, no links have been established to neighboring routers. - VerifyOrExit(mLinkRequestDelay == 0); - VerifyOrExit((message = NewMleMessage(kCommandAdvertisement)) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->AppendSourceAddressTlv()); SuccessOrExit(error = message->AppendLeaderDataTlv()); switch (mRole) { - case kRoleDisabled: - case kRoleDetached: - OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); - case kRoleChild: break; @@ -529,6 +495,10 @@ void MleRouter::SendAdvertisement(void) case kRoleLeader: SuccessOrExit(error = message->AppendRouteTlv()); break; + + case kRoleDisabled: + case kRoleDetached: + OT_ASSERT(false); } destination.SetToLinkLocalAllNodesMulticast(); @@ -543,14 +513,15 @@ void MleRouter::SendAdvertisement(void) Error MleRouter::SendLinkRequest(Neighbor *aNeighbor) { - static const uint8_t detachedTlvs[] = {Tlv::kAddress16, Tlv::kRoute}; - static const uint8_t routerTlvs[] = {Tlv::kLinkMargin}; - static const uint8_t validNeighborTlvs[] = {Tlv::kLinkMargin, Tlv::kRoute}; - Error error = kErrorNone; - TxMessage * message = nullptr; - Ip6::Address destination; + static const uint8_t kDetachedTlvs[] = {Tlv::kAddress16, Tlv::kRoute}; + static const uint8_t kRouterTlvs[] = {Tlv::kLinkMargin}; + static const uint8_t kValidNeighborTlvs[] = {Tlv::kLinkMargin, Tlv::kRoute}; - VerifyOrExit(mLinkRequestDelay == 0 && mChallengeTimeout == 0); + Error error = kErrorNone; + TxMessage *message = nullptr; + Ip6::Address destination; + + VerifyOrExit(mChallengeTimeout == 0); destination.Clear(); @@ -559,12 +530,8 @@ Error MleRouter::SendLinkRequest(Neighbor *aNeighbor) switch (mRole) { - case kRoleDisabled: - OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); - case kRoleDetached: - SuccessOrExit(error = message->AppendTlvRequestTlv(detachedTlvs, sizeof(detachedTlvs))); + SuccessOrExit(error = message->AppendTlvRequestTlv(kDetachedTlvs)); break; case kRoleChild: @@ -576,16 +543,19 @@ Error MleRouter::SendLinkRequest(Neighbor *aNeighbor) case kRoleLeader: if (aNeighbor == nullptr || !aNeighbor->IsStateValid()) { - SuccessOrExit(error = message->AppendTlvRequestTlv(routerTlvs, sizeof(routerTlvs))); + SuccessOrExit(error = message->AppendTlvRequestTlv(kRouterTlvs)); } else { - SuccessOrExit(error = message->AppendTlvRequestTlv(validNeighborTlvs, sizeof(validNeighborTlvs))); + SuccessOrExit(error = message->AppendTlvRequestTlv(kValidNeighborTlvs)); } SuccessOrExit(error = message->AppendSourceAddressTlv()); SuccessOrExit(error = message->AppendLeaderDataTlv()); break; + + case kRoleDisabled: + OT_ASSERT(false); } #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE @@ -595,7 +565,7 @@ Error MleRouter::SendLinkRequest(Neighbor *aNeighbor) if (aNeighbor == nullptr) { mChallenge.GenerateRandom(); - mChallengeTimeout = (((2 * kMaxResponseDelay) + kStateUpdatePeriod - 1) / kStateUpdatePeriod); + mChallengeTimeout = kChallengeTimeout; SuccessOrExit(error = message->AppendChallengeTlv(mChallenge)); destination.SetToLinkLocalAllRoutersMulticast(); @@ -630,13 +600,13 @@ Error MleRouter::SendLinkRequest(Neighbor *aNeighbor) void MleRouter::HandleLinkRequest(RxInfo &aRxInfo) { - Error error = kErrorNone; - Neighbor * neighbor = nullptr; - Challenge challenge; - uint16_t version; - LeaderData leaderData; - uint16_t sourceAddress; - RequestedTlvs requestedTlvs; + Error error = kErrorNone; + Neighbor *neighbor = nullptr; + Challenge challenge; + uint16_t version; + LeaderData leaderData; + uint16_t sourceAddress; + TlvList requestedTlvList; Log(kMessageReceive, kTypeLinkRequest, aRxInfo.mMessageInfo.GetPeerAddr()); @@ -649,7 +619,7 @@ void MleRouter::HandleLinkRequest(RxInfo &aRxInfo) // Version SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, version)); - VerifyOrExit(version >= OT_THREAD_VERSION_1_1, error = kErrorParse); + VerifyOrExit(version >= kThreadVersion1p1, error = kErrorParse); // Leader Data switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)) @@ -669,25 +639,20 @@ void MleRouter::HandleLinkRequest(RxInfo &aRxInfo) case kErrorNone: if (IsActiveRouter(sourceAddress)) { - Mac::ExtAddress extAddr; - - aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr); - - neighbor = mRouterTable.GetRouter(RouterIdFromRloc16(sourceAddress)); + neighbor = mRouterTable.FindRouterByRloc16(sourceAddress); VerifyOrExit(neighbor != nullptr, error = kErrorParse); VerifyOrExit(!neighbor->IsStateLinkRequest(), error = kErrorAlready); if (!neighbor->IsStateValid()) { - neighbor->SetExtAddress(extAddr); - neighbor->GetLinkInfo().Clear(); - neighbor->GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss()); - neighbor->ResetLinkFailures(); - neighbor->SetLastHeard(TimerMilli::GetNow()); + InitNeighbor(*neighbor, aRxInfo); neighbor->SetState(Neighbor::kStateLinkRequest); } else { + Mac::ExtAddress extAddr; + + aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr); VerifyOrExit(neighbor->GetExtAddress() == extAddr); } } @@ -696,8 +661,7 @@ void MleRouter::HandleLinkRequest(RxInfo &aRxInfo) case kErrorNotFound: // lack of source address indicates router coming out of reset - VerifyOrExit(aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid() && - IsActiveRouter(aRxInfo.mNeighbor->GetRloc16()), + VerifyOrExit(aRxInfo.IsNeighborStateValid() && IsActiveRouter(aRxInfo.mNeighbor->GetRloc16()), error = kErrorDrop); neighbor = aRxInfo.mNeighbor; break; @@ -707,12 +671,10 @@ void MleRouter::HandleLinkRequest(RxInfo &aRxInfo) } // TLV Request - switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvs)) + switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList)) { case kErrorNone: - break; case kErrorNotFound: - requestedTlvs.mNumTlvs = 0; break; default: ExitNow(error = kErrorParse); @@ -734,22 +696,23 @@ void MleRouter::HandleLinkRequest(RxInfo &aRxInfo) aRxInfo.mClass = RxInfo::kPeerMessage; - SuccessOrExit(error = SendLinkAccept(aRxInfo.mMessageInfo, neighbor, requestedTlvs, challenge)); + SuccessOrExit(error = SendLinkAccept(aRxInfo.mMessageInfo, neighbor, requestedTlvList, challenge)); exit: LogProcessError(kTypeLinkRequest, error); } Error MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, - Neighbor * aNeighbor, - const RequestedTlvs & aRequestedTlvs, - const Challenge & aChallenge) + Neighbor *aNeighbor, + const TlvList &aRequestedTlvList, + const Challenge &aChallenge) { - Error error = kErrorNone; - static const uint8_t routerTlvs[] = {Tlv::kLinkMargin}; - TxMessage * message; - Command command; - uint8_t linkMargin; + static const uint8_t kRouterTlvs[] = {Tlv::kLinkMargin}; + + Error error = kErrorNone; + TxMessage *message; + Command command; + uint8_t linkMargin; command = (aNeighbor == nullptr || aNeighbor->IsStateValid()) ? kCommandLinkAccept : kCommandLinkAcceptAndRequest; @@ -761,8 +724,7 @@ Error MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, SuccessOrExit(error = message->AppendMleFrameCounterTlv()); // always append a link margin, regardless of whether or not it was requested - linkMargin = LinkQualityInfo::ConvertRssToLinkMargin(Get().GetNoiseFloor(), - aMessageInfo.GetThreadLinkInfo()->GetRss()); + linkMargin = Get().ComputeLinkMargin(aMessageInfo.GetThreadLinkInfo()->GetRss()); SuccessOrExit(error = message->AppendLinkMarginTlv(linkMargin)); @@ -771,9 +733,9 @@ Error MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, SuccessOrExit(error = message->AppendLeaderDataTlv()); } - for (uint8_t i = 0; i < aRequestedTlvs.mNumTlvs; i++) + for (uint8_t tlvType : aRequestedTlvList) { - switch (aRequestedTlvs.mTlvs[i]) + switch (tlvType) { case Tlv::kRoute: SuccessOrExit(error = message->AppendRouteTlv(aNeighbor)); @@ -797,7 +759,7 @@ Error MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, aNeighbor->GenerateChallenge(); SuccessOrExit(error = message->AppendChallengeTlv(aNeighbor->GetChallenge(), aNeighbor->GetChallengeSize())); - SuccessOrExit(error = message->AppendTlvRequestTlv(routerTlvs, sizeof(routerTlvs))); + SuccessOrExit(error = message->AppendTlvRequestTlv(kRouterTlvs)); aNeighbor->SetLastHeard(TimerMilli::GetNow()); aNeighbor->SetState(Neighbor::kStateLinkRequest); } @@ -814,13 +776,15 @@ Error MleRouter::SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, SuccessOrExit(error = message->SendAfterDelay(aMessageInfo.GetPeerAddr(), 1 + Random::NonCrypto::GetUint16InRange(0, kMaxResponseDelay))); - Log(kMessageDelay, kTypeLinkAccept, aMessageInfo.GetPeerAddr()); + Log(kMessageDelay, (command == kCommandLinkAccept) ? kTypeLinkAccept : kTypeLinkAcceptAndRequest, + aMessageInfo.GetPeerAddr()); } else { SuccessOrExit(error = message->SendTo(aMessageInfo.GetPeerAddr())); - Log(kMessageSend, kTypeLinkAccept, aMessageInfo.GetPeerAddr()); + Log(kMessageSend, (command == kCommandLinkAccept) ? kTypeLinkAccept : kTypeLinkAcceptAndRequest, + aMessageInfo.GetPeerAddr()); } exit: @@ -844,12 +808,9 @@ void MleRouter::HandleLinkAcceptAndRequest(RxInfo &aRxInfo) Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) { - static const uint8_t dataRequestTlvs[] = {Tlv::kNetworkData}; - Error error = kErrorNone; - Router * router; + Router *router; Neighbor::State neighborState; - Mac::ExtAddress extAddr; uint16_t version; Challenge response; uint16_t sourceAddress; @@ -870,7 +831,7 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) VerifyOrExit(IsActiveRouter(sourceAddress), error = kErrorParse); routerId = RouterIdFromRloc16(sourceAddress); - router = mRouterTable.GetRouter(routerId); + router = mRouterTable.FindRouterById(routerId); neighborState = (router != nullptr) ? router->GetState() : Neighbor::kStateInvalid; // Response @@ -884,7 +845,8 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) break; case Neighbor::kStateInvalid: - VerifyOrExit((mChallengeTimeout > 0) && (response == mChallenge), error = kErrorSecurity); + VerifyOrExit((mLinkRequestAttempts > 0 || mChallengeTimeout > 0) && (response == mChallenge), + error = kErrorSecurity); OT_FALL_THROUGH; @@ -903,7 +865,7 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) // Version SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, version)); - VerifyOrExit(version >= OT_THREAD_VERSION_1_1, error = kErrorParse); + VerifyOrExit(version >= kThreadVersion1p1, error = kErrorParse); // Link and MLE Frame Counters SuccessOrExit(error = aRxInfo.mMessage.ReadFrameCounterTlvs(linkFrameCounter, mleFrameCounter)); @@ -925,10 +887,6 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) switch (mRole) { - case kRoleDisabled: - OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); - case kRoleDetached: // Address16 SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, address16)); @@ -940,8 +898,9 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) // Route mRouterTable.Clear(); - SuccessOrExit(error = ProcessRouteTlv(aRxInfo)); - router = mRouterTable.GetRouter(routerId); + SuccessOrExit(error = aRxInfo.mMessage.ReadRouteTlv(routeTlv)); + SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo)); + router = mRouterTable.FindRouterById(routerId); VerifyOrExit(router != nullptr); if (mLeaderData.GetLeaderRouterId() == RouterIdFromRloc16(GetRloc16())) @@ -953,8 +912,9 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) SetStateRouter(GetRloc16()); } + mLinkRequestAttempts = 0; // completed router sync after reset, no more link request to retransmit mRetrieveNewNetworkData = true; - IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr(), dataRequestTlvs, sizeof(dataRequestTlvs), 0)); + IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr())); #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE Get().HandleTimeSyncMessage(aRxInfo.mMessage); @@ -977,52 +937,53 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) SerialNumber::IsGreater(leaderData.GetDataVersion(NetworkData::kFullSet), Get().GetVersion(NetworkData::kFullSet))) { - IgnoreError( - SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr(), dataRequestTlvs, sizeof(dataRequestTlvs), 0)); + IgnoreError(SendDataRequest(aRxInfo.mMessageInfo.GetPeerAddr())); } // Route (optional) - switch (error = ProcessRouteTlv(aRxInfo, routeTlv)) + switch (aRxInfo.mMessage.ReadRouteTlv(routeTlv)) { case kErrorNone: - UpdateRoutes(routeTlv, routerId); - // Need to update router after ProcessRouteTlv - router = mRouterTable.GetRouter(routerId); - OT_ASSERT(router != nullptr); + VerifyOrExit(routeTlv.IsRouterIdSet(routerId), error = kErrorParse); + + if (mRouterTable.IsRouteTlvIdSequenceMoreRecent(routeTlv)) + { + SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo)); + router = mRouterTable.FindRouterById(routerId); + OT_ASSERT(router != nullptr); + } + + mRouterTable.UpdateRoutes(routeTlv, routerId); break; case kErrorNotFound: - error = kErrorNone; break; default: - ExitNow(); + ExitNow(error = kErrorParse); } - // update routing table if (routerId != mRouterId && !IsRouterIdValid(router->GetNextHop())) { ResetAdvertiseInterval(); } break; + + case kRoleDisabled: + OT_ASSERT(false); } // finish link synchronization - aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr); - router->SetExtAddress(extAddr); + InitNeighbor(*router, aRxInfo); router->SetRloc16(sourceAddress); router->GetLinkFrameCounters().SetAll(linkFrameCounter); router->SetLinkAckFrameCounter(linkFrameCounter); router->SetMleFrameCounter(mleFrameCounter); - router->SetLastHeard(TimerMilli::GetNow()); - router->SetVersion(static_cast(version)); + router->SetVersion(version); router->SetDeviceMode(DeviceMode(DeviceMode::kModeFullThreadDevice | DeviceMode::kModeRxOnWhenIdle | DeviceMode::kModeFullNetworkData)); - router->GetLinkInfo().Clear(); - router->GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss()); - router->SetLinkQualityOut(LinkQualityInfo::ConvertLinkMarginToLinkQuality(linkMargin)); - router->ResetLinkFailures(); + router->SetLinkQualityOut(LinkQualityForLinkMargin(linkMargin)); router->SetState(Neighbor::kStateValid); router->SetKeySequence(aRxInfo.mKeySequence); @@ -1032,73 +993,29 @@ Error MleRouter::HandleLinkAccept(RxInfo &aRxInfo, bool aRequest) if (aRequest) { - Challenge challenge; - RequestedTlvs requestedTlvs; + Challenge challenge; + TlvList requestedTlvList; // Challenge SuccessOrExit(error = aRxInfo.mMessage.ReadChallengeTlv(challenge)); // TLV Request - switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvs)) + switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList)) { case kErrorNone: - break; case kErrorNotFound: - requestedTlvs.mNumTlvs = 0; break; default: ExitNow(error = kErrorParse); } - SuccessOrExit(error = SendLinkAccept(aRxInfo.mMessageInfo, router, requestedTlvs, challenge)); + SuccessOrExit(error = SendLinkAccept(aRxInfo.mMessageInfo, router, requestedTlvList, challenge)); } exit: return error; } -uint8_t MleRouter::LinkQualityToCost(uint8_t aLinkQuality) -{ - uint8_t rval; - - switch (aLinkQuality) - { - case 1: - rval = kLinkQuality1LinkCost; - break; - - case 2: - rval = kLinkQuality2LinkCost; - break; - - case 3: - rval = kLinkQuality3LinkCost; - break; - - default: - rval = kLinkQuality0LinkCost; - break; - } - - return rval; -} - -uint8_t MleRouter::GetLinkCost(uint8_t aRouterId) -{ - uint8_t rval = kMaxRouteCost; - Router *router; - - router = mRouterTable.GetRouter(aRouterId); - - // `nullptr` aRouterId indicates non-existing next hop, hence return kMaxRouteCost for it. - VerifyOrExit(router != nullptr); - - rval = mRouterTable.GetLinkCost(*router); - -exit: - return rval; -} - Error MleRouter::SetRouterSelectionJitter(uint8_t aRouterJitter) { Error error = kErrorNone; @@ -1111,20 +1028,9 @@ Error MleRouter::SetRouterSelectionJitter(uint8_t aRouterJitter) return error; } -Error MleRouter::ProcessRouteTlv(RxInfo &aRxInfo) +Error MleRouter::ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo) { - RouteTlv routeTlv; - - return ProcessRouteTlv(aRxInfo, routeTlv); -} - -Error MleRouter::ProcessRouteTlv(RxInfo &aRxInfo, RouteTlv &aRouteTlv) -{ - // This method processes Route TLV in a received MLE message - // (from `RxInfo`). In case of success, `aRouteTlv` is updated - // to return the read/processed route TLV from the message. - // If the message contains no Route TLV, `kErrorNotFound` is - // returned. + // This method processes `aRouteTlv` read from an MLE message. // // During processing of Route TLV, the entries in the router table // may shuffle. This method ensures that the `aRxInfo.mNeighbor` @@ -1133,7 +1039,7 @@ Error MleRouter::ProcessRouteTlv(RxInfo &aRxInfo, RouteTlv &aRouteTlv) // (in case `mNeighbor` was pointing to a router entry from the // `RouterTable`). - Error error; + Error error = kErrorNone; uint16_t neighborRloc16 = Mac::kShortAddrInvalid; if ((aRxInfo.mNeighbor != nullptr) && Get().Contains(*aRxInfo.mNeighbor)) @@ -1141,13 +1047,9 @@ Error MleRouter::ProcessRouteTlv(RxInfo &aRxInfo, RouteTlv &aRouteTlv) neighborRloc16 = aRxInfo.mNeighbor->GetRloc16(); } - SuccessOrExit(error = Tlv::FindTlv(aRxInfo.mMessage, aRouteTlv)); - - VerifyOrExit(aRouteTlv.IsValid(), error = kErrorParse); + mRouterTable.UpdateRouterIdSet(aRouteTlv.GetRouterIdSequence(), aRouteTlv.GetRouterIdMask()); - Get().UpdateRouterIdSet(aRouteTlv.GetRouterIdSequence(), aRouteTlv.GetRouterIdMask()); - - if (IsRouter() && !Get().IsAllocated(mRouterId)) + if (IsRouter() && !mRouterTable.IsAllocated(mRouterId)) { IgnoreError(BecomeDetached()); error = kErrorNoRoute; @@ -1155,134 +1057,125 @@ Error MleRouter::ProcessRouteTlv(RxInfo &aRxInfo, RouteTlv &aRouteTlv) if (neighborRloc16 != Mac::kShortAddrInvalid) { - aRxInfo.mNeighbor = Get().GetNeighbor(neighborRloc16); + aRxInfo.mNeighbor = Get().FindNeighbor(neighborRloc16); } -exit: return error; } -bool MleRouter::IsSingleton(void) +Error MleRouter::ReadAndProcessRouteTlvOnFed(RxInfo &aRxInfo, uint8_t aParentId) { - bool rval = true; + // This method reads and processes Route TLV from message on an + // FED if message contains one. It returns `kErrorNone` when + // successfully processed or if there is no Route TLV in the + // message. + // + // It MUST be used only when device is acting as a child and + // for a message received from device's current parent. - if (IsAttached() && IsRouterEligible()) + Error error = kErrorNone; + RouteTlv routeTlv; + + VerifyOrExit(IsFullThreadDevice()); + + switch (aRxInfo.mMessage.ReadRouteTlv(routeTlv)) { - // not a singleton if any other routers exist - if (mRouterTable.GetActiveRouterCount() > 1) - { - ExitNow(rval = false); - } + case kErrorNone: + SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo)); + mRouterTable.UpdateRoutesOnFed(routeTlv, aParentId); + mRequestRouteTlv = false; + break; + case kErrorNotFound: + break; + default: + ExitNow(error = kErrorParse); } exit: - return rval; + return error; } -int MleRouter::ComparePartitions(bool aSingletonA, - const LeaderData &aLeaderDataA, - bool aSingletonB, - const LeaderData &aLeaderDataB) +bool MleRouter::IsSingleton(void) const { - int rval = 0; + bool isSingleton = true; - if (aLeaderDataA.GetWeighting() != aLeaderDataB.GetWeighting()) - { - ExitNow(rval = aLeaderDataA.GetWeighting() > aLeaderDataB.GetWeighting() ? 1 : -1); - } - - if (aSingletonA != aSingletonB) - { - ExitNow(rval = aSingletonB ? 1 : -1); - } - - if (aLeaderDataA.GetPartitionId() != aLeaderDataB.GetPartitionId()) - { - ExitNow(rval = aLeaderDataA.GetPartitionId() > aLeaderDataB.GetPartitionId() ? 1 : -1); - } + VerifyOrExit(IsAttached() && IsRouterEligible()); + isSingleton = (mRouterTable.GetActiveRouterCount() <= 1); exit: - return rval; + return isSingleton; } -bool MleRouter::IsSingleton(const RouteTlv &aRouteTlv) +int MleRouter::ComparePartitions(bool aSingletonA, + const LeaderData &aLeaderDataA, + bool aSingletonB, + const LeaderData &aLeaderDataB) { - bool rval = true; - uint8_t count = 0; + int rval = 0; - // REEDs do not include a Route TLV and indicate not a singleton - if (!aRouteTlv.IsValid()) - { - ExitNow(rval = false); - } + rval = ThreeWayCompare(aLeaderDataA.GetWeighting(), aLeaderDataB.GetWeighting()); + VerifyOrExit(rval == 0); - // Check if 2 or more active routers - for (uint8_t routerId = 0; routerId <= kMaxRouterId; routerId++) - { - if (aRouteTlv.IsRouterIdSet(routerId) && (++count >= 2)) - { - ExitNow(rval = false); - } - } + // Not being a singleton is better. + rval = ThreeWayCompare(!aSingletonA, !aSingletonB); + VerifyOrExit(rval == 0); + + rval = ThreeWayCompare(aLeaderDataA.GetPartitionId(), aLeaderDataB.GetPartitionId()); exit: return rval; } -Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo) +Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo, uint16_t aSourceAddress, const LeaderData &aLeaderData) { - Error error = kErrorNone; - const ThreadLinkInfo *linkInfo = aRxInfo.mMessageInfo.GetThreadLinkInfo(); - uint8_t linkMargin = LinkQualityInfo::ConvertRssToLinkMargin(Get().GetNoiseFloor(), linkInfo->GetRss()); - Mac::ExtAddress extAddr; - uint16_t sourceAddress = Mac::kShortAddrInvalid; - LeaderData leaderData; - RouteTlv route; - uint32_t partitionId; - Router * router; - uint8_t routerId; - uint8_t routerCount; - - aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr); - - // Source Address - SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, sourceAddress)); + // This method processes a received MLE Advertisement message on + // an FTD device. It is called from `Mle::HandleAdvertisement()` + // only when device is attached (in child, router, or leader roles) + // and `IsFullThreadDevice()`. + // + // - `aSourceAddress` is the read value from `SourceAddressTlv`. + // - `aLeaderData` is the read value from `LeaderDataTlv`. - // Leader Data - SuccessOrExit(error = aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)); + Error error = kErrorNone; + uint8_t linkMargin = Get().ComputeLinkMargin(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss()); + RouteTlv routeTlv; + Router *router; + uint8_t routerId; - // Route Data (optional) - if (Tlv::FindTlv(aRxInfo.mMessage, route) == kErrorNone) - { - VerifyOrExit(route.IsValid(), error = kErrorParse); - } - else + switch (aRxInfo.mMessage.ReadRouteTlv(routeTlv)) { - // mark that a Route TLV was not included - route.SetLength(0); + case kErrorNone: + break; + case kErrorNotFound: + routeTlv.SetLength(0); // Mark that a Route TLV was not included. + break; + default: + ExitNow(error = kErrorParse); } - partitionId = leaderData.GetPartitionId(); + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Handle Partition ID mismatch - if (partitionId != mLeaderData.GetPartitionId()) + if (aLeaderData.GetPartitionId() != mLeaderData.GetPartitionId()) { - LogNote("Different partition (peer:%u, local:%u)", partitionId, mLeaderData.GetPartitionId()); + LogNote("Different partition (peer:%lu, local:%lu)", ToUlong(aLeaderData.GetPartitionId()), + ToUlong(mLeaderData.GetPartitionId())); - VerifyOrExit(linkMargin >= OPENTHREAD_CONFIG_MLE_PARTITION_MERGE_MARGIN_MIN, error = kErrorLinkMarginLow); + VerifyOrExit(linkMargin >= kPartitionMergeMinMargin, error = kErrorLinkMarginLow); - if (route.IsValid() && IsFullThreadDevice() && (mPreviousPartitionIdTimeout > 0) && - (partitionId == mPreviousPartitionId)) + if (routeTlv.IsValid() && (mPreviousPartitionIdTimeout > 0) && + (aLeaderData.GetPartitionId() == mPreviousPartitionId)) { - VerifyOrExit(SerialNumber::IsGreater(route.GetRouterIdSequence(), mPreviousPartitionRouterIdSequence), + VerifyOrExit(SerialNumber::IsGreater(routeTlv.GetRouterIdSequence(), mPreviousPartitionRouterIdSequence), error = kErrorDrop); } - if (IsChild() && (aRxInfo.mNeighbor == &mParent || !IsFullThreadDevice())) + if (IsChild() && (aRxInfo.mNeighbor == &mParent)) { ExitNow(); } - if (ComparePartitions(IsSingleton(route), leaderData, IsSingleton(), mLeaderData) > 0 + if (ComparePartitions(routeTlv.IsSingleton(), aLeaderData, IsSingleton(), mLeaderData) > 0 #if OPENTHREAD_CONFIG_TIME_SYNC_REQUIRED // if time sync is required, it will only migrate to a better network which also enables time sync. && aRxInfo.mMessage.GetTimeSyncSeq() != OT_TIME_SYNC_INVALID_SEQ @@ -1294,9 +1187,13 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo) ExitNow(error = kErrorDrop); } - else if (leaderData.GetLeaderRouterId() != GetLeaderId()) + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Handle Leader Router ID mismatch + + if (aLeaderData.GetLeaderRouterId() != GetLeaderId()) { - VerifyOrExit(aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid()); + VerifyOrExit(aRxInfo.IsNeighborStateValid()); if (!IsChild()) { @@ -1308,130 +1205,80 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo) ExitNow(); } - VerifyOrExit(IsActiveRouter(sourceAddress) && route.IsValid()); - routerId = RouterIdFromRloc16(sourceAddress); + VerifyOrExit(IsActiveRouter(aSourceAddress) && routeTlv.IsValid()); + routerId = RouterIdFromRloc16(aSourceAddress); #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE Get().HandleTimeSyncMessage(aRxInfo.mMessage); #endif - if (IsFullThreadDevice() && (aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid()) && - ((mRouterTable.GetActiveRouterCount() == 0) || - SerialNumber::IsGreater(route.GetRouterIdSequence(), mRouterTable.GetRouterIdSequence()))) + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Process `RouteTlv` + + if (aRxInfo.IsNeighborStateValid() && mRouterTable.IsRouteTlvIdSequenceMoreRecent(routeTlv)) { bool processRouteTlv = false; - switch (mRole) + if (IsChild()) { - case kRoleDisabled: - case kRoleDetached: - break; - - case kRoleChild: - if (sourceAddress == mParent.GetRloc16()) + if (aSourceAddress == mParent.GetRloc16()) { processRouteTlv = true; } else { - router = mRouterTable.GetRouter(routerId); + router = mRouterTable.FindRouterById(routerId); if (router != nullptr && router->IsStateValid()) { processRouteTlv = true; } } - - break; - - case kRoleRouter: - case kRoleLeader: + } + else // Device is router or leader + { processRouteTlv = true; - break; } if (processRouteTlv) { - SuccessOrExit(error = ProcessRouteTlv(aRxInfo)); + SuccessOrExit(error = ProcessRouteTlv(routeTlv, aRxInfo)); } } - switch (mRole) - { - case kRoleDisabled: - case kRoleDetached: - ExitNow(); + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Update routers as a child - case kRoleChild: + if (IsChild()) + { if (aRxInfo.mNeighbor == &mParent) { // MLE Advertisement from parent router = &mParent; - if (mParent.GetRloc16() != sourceAddress) + if (mParent.GetRloc16() != aSourceAddress) { IgnoreError(BecomeDetached()); ExitNow(error = kErrorDetached); } - if (IsFullThreadDevice()) + if ((mRouterSelectionJitterTimeout == 0) && (mRouterTable.GetActiveRouterCount() < mRouterUpgradeThreshold)) { - Router *leader; - - if ((mRouterSelectionJitterTimeout == 0) && - (mRouterTable.GetActiveRouterCount() < mRouterUpgradeThreshold)) - { - mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter); - ExitNow(); - } - - leader = mRouterTable.GetLeader(); - - if (leader != nullptr) - { - for (uint8_t id = 0, routeCount = 0; id <= kMaxRouterId; id++) - { - if (!route.IsRouterIdSet(id)) - { - continue; - } - - if (id != GetLeaderId()) - { - routeCount++; - continue; - } - - if (route.GetRouteCost(routeCount) > 0) - { - leader->SetNextHop(id); - leader->SetCost(route.GetRouteCost(routeCount)); - } - else - { - leader->SetNextHop(kInvalidRouterId); - leader->SetCost(0); - } - - break; - } - } + mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter); } + + mRouterTable.UpdateRoutesOnFed(routeTlv, routerId); } else { // MLE Advertisement not from parent, but from some other neighboring router - router = mRouterTable.GetRouter(routerId); + router = mRouterTable.FindRouterById(routerId); VerifyOrExit(router != nullptr); - if (IsFullThreadDevice() && !router->IsStateValid() && !router->IsStateLinkRequest() && - (mRouterTable.GetActiveLinkCount() < OPENTHREAD_CONFIG_MLE_CHILD_ROUTER_LINKS)) + if (!router->IsStateValid() && !router->IsStateLinkRequest() && + (mRouterTable.GetNeighborCount() < mChildRouterLinks)) { - router->SetExtAddress(extAddr); - router->GetLinkInfo().Clear(); - router->GetLinkInfo().AddRss(linkInfo->GetRss()); - router->ResetLinkFailures(); - router->SetLastHeard(TimerMilli::GetNow()); + InitNeighbor(*router, aRxInfo); router->SetState(Neighbor::kStateLinkRequest); IgnoreError(SendLinkRequest(router)); ExitNow(error = kErrorNoRoute); @@ -1441,232 +1288,61 @@ Error MleRouter::HandleAdvertisement(RxInfo &aRxInfo) router->SetLastHeard(TimerMilli::GetNow()); ExitNow(); + } - case kRoleRouter: - if (mLinkRequestDelay > 0 && route.IsRouterIdSet(mRouterId)) - { - mLinkRequestDelay = 0; - IgnoreError(SendLinkRequest(nullptr)); - } - - router = mRouterTable.GetRouter(routerId); - VerifyOrExit(router != nullptr); - - // check current active router number - routerCount = 0; + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Update routers as a router or leader. - for (uint8_t id = 0; id <= kMaxRouterId; id++) - { - if (route.IsRouterIdSet(id)) - { - routerCount++; - } - } - - if (routerCount > mRouterDowngradeThreshold && mRouterSelectionJitterTimeout == 0 && - HasMinDowngradeNeighborRouters() && HasSmallNumberOfChildren() && - HasOneNeighborWithComparableConnectivity(route, routerId) -#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE - && !Get().IsEligibleForRouterRoleUpgradeAsBorderRouter() -#endif - ) + if (IsRouter()) + { + if (ShouldDowngrade(routerId, routeTlv)) { mRouterSelectionJitterTimeout = 1 + Random::NonCrypto::GetUint8InRange(0, mRouterSelectionJitter); } - - OT_FALL_THROUGH; - - case kRoleLeader: - router = mRouterTable.GetRouter(routerId); - VerifyOrExit(router != nullptr); - - // Send unicast link request if no link to router and no unicast/multicast link request in progress - if (!router->IsStateValid() && !router->IsStateLinkRequest() && (mChallengeTimeout == 0) && - (linkMargin >= OPENTHREAD_CONFIG_MLE_LINK_REQUEST_MARGIN_MIN)) - { - router->SetExtAddress(extAddr); - router->GetLinkInfo().Clear(); - router->GetLinkInfo().AddRss(linkInfo->GetRss()); - router->ResetLinkFailures(); - router->SetLastHeard(TimerMilli::GetNow()); - router->SetState(Neighbor::kStateLinkRequest); - IgnoreError(SendLinkRequest(router)); - ExitNow(error = kErrorNoRoute); - } - - router->SetLastHeard(TimerMilli::GetNow()); - break; } - UpdateRoutes(route, routerId); - -exit: - if (aRxInfo.mNeighbor && aRxInfo.mNeighbor->GetRloc16() != sourceAddress) - { - // Remove stale neighbors - RemoveNeighbor(*aRxInfo.mNeighbor); - } - - return error; -} - -void MleRouter::UpdateRoutes(const RouteTlv &aRoute, uint8_t aRouterId) -{ - Router *neighbor; - bool resetAdvInterval = false; - bool changed = false; - - neighbor = mRouterTable.GetRouter(aRouterId); - VerifyOrExit(neighbor != nullptr); - - // update link quality out to neighbor - changed = UpdateLinkQualityOut(aRoute, *neighbor, resetAdvInterval); + router = mRouterTable.FindRouterById(routerId); + VerifyOrExit(router != nullptr); - // update routes - for (uint8_t routerId = 0, routeCount = 0; routerId <= kMaxRouterId; routerId++) + if (!router->IsStateValid() && aRxInfo.IsNeighborStateValid() && Get().Contains(*aRxInfo.mNeighbor)) { - Router *router; - Router *nextHop; - uint8_t oldNextHop; - uint8_t cost; - - if (!aRoute.IsRouterIdSet(routerId)) - { - continue; - } + // The Adv is from a former child that is now acting as a router, + // we copy the info from child entry and update the RLOC16. - router = mRouterTable.GetRouter(routerId); + *static_cast(router) = *aRxInfo.mNeighbor; + router->SetRloc16(Rloc16FromRouterId(routerId)); + router->SetDeviceMode(DeviceMode(DeviceMode::kModeFullThreadDevice | DeviceMode::kModeRxOnWhenIdle | + DeviceMode::kModeFullNetworkData)); - if (router == nullptr || router->GetRloc16() == GetRloc16() || router == neighbor) - { - routeCount++; - continue; - } - - oldNextHop = router->GetNextHop(); - nextHop = mRouterTable.GetRouter(oldNextHop); - - cost = aRoute.GetRouteCost(routeCount); - - if (cost == 0) - { - cost = kMaxRouteCost; - } - - if (nextHop == nullptr || nextHop == neighbor) - { - // router has no next hop or next hop is neighbor (sender) - - if (cost + mRouterTable.GetLinkCost(*neighbor) < kMaxRouteCost) - { - if (nextHop == nullptr && mRouterTable.GetLinkCost(*router) >= kMaxRouteCost) - { - resetAdvInterval = true; - } - - if (router->GetNextHop() != aRouterId) - { - router->SetNextHop(aRouterId); - changed = true; - } - - if (router->GetCost() != cost) - { - router->SetCost(cost); - changed = true; - } - } - else if (nextHop == neighbor) - { - if (mRouterTable.GetLinkCost(*router) >= kMaxRouteCost) - { - resetAdvInterval = true; - } - - router->SetNextHop(kInvalidRouterId); - router->SetCost(0); - router->SetLastHeard(TimerMilli::GetNow()); - changed = true; - } - } - else - { - uint8_t curCost = router->GetCost() + mRouterTable.GetLinkCost(*nextHop); - uint8_t newCost = cost + mRouterTable.GetLinkCost(*neighbor); + mNeighborTable.Signal(NeighborTable::kRouterAdded, *router); - if (newCost < curCost) - { - router->SetNextHop(aRouterId); - router->SetCost(cost); - changed = true; - } - } - - routeCount++; + // Change the cache entries associated with the former child + // from using the old RLOC16 to its new RLOC16. + Get().ReplaceEntriesForRloc16(aRxInfo.mNeighbor->GetRloc16(), router->GetRloc16()); } - if (resetAdvInterval) + // Send unicast link request if no link to router and no unicast/multicast link request in progress + if (!router->IsStateValid() && !router->IsStateLinkRequest() && (mChallengeTimeout == 0) && + (linkMargin >= kLinkRequestMinMargin)) { - ResetAdvertiseInterval(); + InitNeighbor(*router, aRxInfo); + router->SetState(Neighbor::kStateLinkRequest); + IgnoreError(SendLinkRequest(router)); + ExitNow(error = kErrorNoRoute); } -#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) - - VerifyOrExit(changed); - LogInfo("Route table updated"); + router->SetLastHeard(TimerMilli::GetNow()); - for (Router &router : Get().Iterate()) - { - LogInfo(" %04x -> %04x, cost:%d %d, lqin:%d, lqout:%d, link:%s", router.GetRloc16(), - (router.GetNextHop() == kInvalidRouterId) ? 0xffff : Rloc16FromRouterId(router.GetNextHop()), - router.GetCost(), mRouterTable.GetLinkCost(router), router.GetLinkInfo().GetLinkQuality(), - router.GetLinkQualityOut(), - router.GetRloc16() == GetRloc16() ? "device" : ToYesNo(router.IsStateValid())); - } - -#else - OT_UNUSED_VARIABLE(changed); -#endif + mRouterTable.UpdateRoutes(routeTlv, routerId); exit: - return; -} - -bool MleRouter::UpdateLinkQualityOut(const RouteTlv &aRoute, Router &aNeighbor, bool &aResetAdvInterval) -{ - bool changed = false; - LinkQuality linkQuality; - uint8_t myRouterId; - uint8_t myRouteCount; - uint8_t oldLinkCost; - Router * nextHop; - - myRouterId = RouterIdFromRloc16(GetRloc16()); - VerifyOrExit(aRoute.IsRouterIdSet(myRouterId)); - - myRouteCount = 0; - for (uint8_t routerId = 0; routerId < myRouterId; routerId++) + if (aRxInfo.mNeighbor && aRxInfo.mNeighbor->GetRloc16() != aSourceAddress) { - myRouteCount += aRoute.IsRouterIdSet(routerId); - } - - linkQuality = aRoute.GetLinkQualityIn(myRouteCount); - VerifyOrExit(aNeighbor.GetLinkQualityOut() != linkQuality); - - oldLinkCost = mRouterTable.GetLinkCost(aNeighbor); - - aNeighbor.SetLinkQualityOut(linkQuality); - nextHop = mRouterTable.GetRouter(aNeighbor.GetNextHop()); - - // reset MLE advertisement timer if neighbor route cost changed to or from infinite - if (nextHop == nullptr && (oldLinkCost >= kMaxRouteCost) != (mRouterTable.GetLinkCost(aNeighbor) >= kMaxRouteCost)) - { - aResetAdvInterval = true; + // Remove stale neighbors + RemoveNeighbor(*aRxInfo.mNeighbor); } - changed = true; -exit: - return changed; + return error; } void MleRouter::HandleParentRequest(RxInfo &aRxInfo) @@ -1676,8 +1352,7 @@ void MleRouter::HandleParentRequest(RxInfo &aRxInfo) uint16_t version; uint8_t scanMask; Challenge challenge; - Router * leader; - Child * child; + Child *child; uint8_t modeBitmask; DeviceMode mode; @@ -1700,13 +1375,7 @@ void MleRouter::HandleParentRequest(RxInfo &aRxInfo) VerifyOrExit(mRouterTable.GetLeaderAge() < mNetworkIdTimeout, error = kErrorDrop); // 3. Its current routing path cost to the Leader is infinite. - leader = mRouterTable.GetLeader(); - OT_ASSERT(leader != nullptr); - - VerifyOrExit(IsLeader() || GetLinkCost(GetLeaderId()) < kMaxRouteCost || - (IsChild() && leader->GetCost() + 1 < kMaxRouteCost) || - (leader->GetCost() + GetLinkCost(leader->GetNextHop()) < kMaxRouteCost), - error = kErrorDrop); + VerifyOrExit(mRouterTable.GetPathCostToLeader() < kMaxRouteCost, error = kErrorDrop); // 4. It is a REED and there are already `kMaxRouters` active routers in // the network (because Leader would reject any further address solicit). @@ -1716,7 +1385,7 @@ void MleRouter::HandleParentRequest(RxInfo &aRxInfo) // Version SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, version)); - VerifyOrExit(version >= OT_THREAD_VERSION_1_1, error = kErrorParse); + VerifyOrExit(version >= kThreadVersion1p1, error = kErrorParse); // Scan Mask SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, scanMask)); @@ -1748,10 +1417,7 @@ void MleRouter::HandleParentRequest(RxInfo &aRxInfo) VerifyOrExit((child = mChildTable.GetNewChild()) != nullptr, error = kErrorNoBufs); // MAC Address - child->SetExtAddress(extAddr); - child->GetLinkInfo().Clear(); - child->GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss()); - child->ResetLinkFailures(); + InitNeighbor(*child, aRxInfo); child->SetState(Neighbor::kStateParentRequest); #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE child->SetTimeSyncEnabled(Tlv::Find(aRxInfo.mMessage, nullptr, 0) == kErrorNone); @@ -1760,7 +1426,7 @@ void MleRouter::HandleParentRequest(RxInfo &aRxInfo) { mode.Set(modeBitmask); child->SetDeviceMode(mode); - child->SetVersion(static_cast(version)); + child->SetVersion(version); } } else if (TimerMilli::GetNow() - child->GetLastHeard() < kParentRequestRouterTimeout - kParentRequestDuplicateMargin) @@ -1771,7 +1437,7 @@ void MleRouter::HandleParentRequest(RxInfo &aRxInfo) if (!child->IsStateValidOrRestoring()) { child->SetLastHeard(TimerMilli::GetNow()); - child->SetTimeout(Time::MsecToSec(kMaxChildIdRequestTimeout)); + child->SetTimeout(Time::MsecToSec(kChildIdRequestTimeout)); } aRxInfo.mClass = RxInfo::kPeerMessage; @@ -1787,25 +1453,23 @@ bool MleRouter::HasNeighborWithGoodLinkQuality(void) const bool haveNeighbor = true; uint8_t linkMargin; - linkMargin = - LinkQualityInfo::ConvertRssToLinkMargin(Get().GetNoiseFloor(), mParent.GetLinkInfo().GetLastRss()); + linkMargin = Get().ComputeLinkMargin(mParent.GetLinkInfo().GetLastRss()); - if (linkMargin >= OPENTHREAD_CONFIG_MLE_LINK_REQUEST_MARGIN_MIN) + if (linkMargin >= kLinkRequestMinMargin) { ExitNow(); } - for (Router &router : Get().Iterate()) + for (const Router &router : Get()) { if (!router.IsStateValid()) { continue; } - linkMargin = - LinkQualityInfo::ConvertRssToLinkMargin(Get().GetNoiseFloor(), router.GetLinkInfo().GetLastRss()); + linkMargin = Get().ComputeLinkMargin(router.GetLinkInfo().GetLastRss()); - if (linkMargin >= OPENTHREAD_CONFIG_MLE_LINK_REQUEST_MARGIN_MIN) + if (linkMargin >= kLinkRequestMinMargin) { ExitNow(); } @@ -1823,11 +1487,6 @@ void MleRouter::HandleTimeTick(void) VerifyOrExit(IsFullThreadDevice(), Get().UnregisterReceiver(TimeTicker::kMleRouter)); - if (mLinkRequestDelay > 0 && --mLinkRequestDelay == 0) - { - IgnoreError(SendLinkRequest(nullptr)); - } - if (mChallengeTimeout > 0) { mChallengeTimeout--; @@ -1870,12 +1529,8 @@ void MleRouter::HandleTimeTick(void) switch (mRole) { - case kRoleDisabled: - OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); - case kRoleDetached: - if (mChallengeTimeout == 0) + if (mChallengeTimeout == 0 && mLinkRequestAttempts == 0) { IgnoreError(BecomeDetached()); ExitNow(); @@ -1912,7 +1567,7 @@ void MleRouter::HandleTimeTick(void) case kRoleRouter: // verify path to leader - LogDebg("network id timeout = %d", mRouterTable.GetLeaderAge()); + LogDebg("network id timeout = %lu", ToUlong(mRouterTable.GetLeaderAge())); if ((mRouterTable.GetActiveRouterCount() > 0) && (mRouterTable.GetLeaderAge() >= mNetworkIdTimeout)) { @@ -1930,6 +1585,9 @@ void MleRouter::HandleTimeTick(void) case kRoleLeader: break; + + case kRoleDisabled: + OT_ASSERT(false); } // update children state @@ -1953,7 +1611,6 @@ void MleRouter::HandleTimeTick(void) case Neighbor::kStateParentResponse: case Neighbor::kStateLinkRequest: OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); } #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE @@ -1978,7 +1635,7 @@ void MleRouter::HandleTimeTick(void) } // update router state - for (Router &router : Get().Iterate()) + for (Router &router : Get()) { uint32_t age; @@ -2021,7 +1678,7 @@ void MleRouter::HandleTimeTick(void) } else if (router.IsStateLinkRequest()) { - if (age >= kMaxLinkRequestTimeout) + if (age >= kLinkRequestTimeout) { LogInfo("Link Request timeout expired"); RemoveNeighbor(router); @@ -2031,8 +1688,8 @@ void MleRouter::HandleTimeTick(void) if (IsLeader()) { - if (mRouterTable.GetRouter(router.GetNextHop()) == nullptr && - mRouterTable.GetLinkCost(router) >= kMaxRouteCost && age >= Time::SecToMsec(kMaxLeaderToRouterTimeout)) + if (mRouterTable.FindNextHopOf(router) == nullptr && mRouterTable.GetLinkCost(router) >= kMaxRouteCost && + age >= Time::SecToMsec(kMaxLeaderToRouterTimeout)) { LogInfo("Router ID timeout expired (no route)"); IgnoreError(mRouterTable.Release(router.GetRouterId())); @@ -2059,7 +1716,7 @@ void MleRouter::SendParentResponse(Child *aChild, const Challenge &aChallenge, b { Error error = kErrorNone; Ip6::Address destination; - TxMessage * message; + TxMessage *message; uint16_t delay; VerifyOrExit((message = NewMleMessage(kCommandParentResponse)) != nullptr, error = kErrorNoBufs); @@ -2084,24 +1741,15 @@ void MleRouter::SendParentResponse(Child *aChild, const Challenge &aChallenge, b #endif aChild->GenerateChallenge(); - SuccessOrExit(error = message->AppendChallengeTlv(aChild->GetChallenge(), aChild->GetChallengeSize())); - error = message->AppendLinkMarginTlv(aChild->GetLinkInfo().GetLinkMargin()); - SuccessOrExit(error); - + SuccessOrExit(error = message->AppendLinkMarginTlv(aChild->GetLinkInfo().GetLinkMargin())); SuccessOrExit(error = message->AppendConnectivityTlv()); SuccessOrExit(error = message->AppendVersionTlv()); destination.SetToLinkLocalAddress(aChild->GetExtAddress()); - if (aRoutersOnlyRequest) - { - delay = 1 + Random::NonCrypto::GetUint16InRange(0, kParentResponseMaxDelayRouters); - } - else - { - delay = 1 + Random::NonCrypto::GetUint16InRange(0, kParentResponseMaxDelayAll); - } + delay = 1 + Random::NonCrypto::GetUint16InRange(0, aRoutersOnlyRequest ? kParentResponseMaxDelayRouters + : kParentResponseMaxDelayAll); SuccessOrExit(error = message->SendAfterDelay(destination, delay)); @@ -2114,7 +1762,7 @@ void MleRouter::SendParentResponse(Child *aChild, const Challenge &aChallenge, b uint8_t MleRouter::GetMaxChildIpAddresses(void) const { - uint8_t num = OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD; + uint8_t num = kMaxChildIpAddresses; #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE if (mMaxChildIpAddresses != 0) @@ -2131,7 +1779,7 @@ Error MleRouter::SetMaxChildIpAddresses(uint8_t aMaxIpAddresses) { Error error = kErrorNone; - VerifyOrExit(aMaxIpAddresses <= OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD, error = kErrorInvalidArgs); + VerifyOrExit(aMaxIpAddresses <= kMaxChildIpAddresses, error = kErrorInvalidArgs); mMaxChildIpAddresses = aMaxIpAddresses; @@ -2140,42 +1788,36 @@ Error MleRouter::SetMaxChildIpAddresses(uint8_t aMaxIpAddresses) } #endif -Error MleRouter::UpdateChildAddresses(const Message &aMessage, uint16_t aOffset, Child &aChild) +Error MleRouter::ProcessAddressRegistrationTlv(RxInfo &aRxInfo, Child &aChild) { - Error error = kErrorNone; - AddressRegistrationEntry entry; - Ip6::Address address; - Lowpan::Context context; - Tlv tlv; - uint8_t registeredCount = 0; - uint8_t storedCount = 0; - uint16_t offset = 0; - uint16_t end = 0; -#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE + Error error; + uint16_t offset; + uint16_t length; + uint16_t endOffset; + uint8_t count = 0; + uint8_t storedCount = 0; +#if OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE Ip6::Address oldDua; const Ip6::Address *oldDuaPtr = nullptr; bool hasDua = false; #endif - -#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE - Ip6::Address oldMlrRegisteredAddresses[OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD - 1]; +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE + Ip6::Address oldMlrRegisteredAddresses[kMaxChildIpAddresses - 1]; uint16_t oldMlrRegisteredAddressNum = 0; #endif - SuccessOrExit(error = aMessage.Read(aOffset, tlv)); - VerifyOrExit(tlv.GetLength() <= (aMessage.GetLength() - aOffset - sizeof(tlv)), error = kErrorParse); + SuccessOrExit(error = Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kAddressRegistration, offset, length)); - offset = aOffset + sizeof(tlv); - end = offset + tlv.GetLength(); + endOffset = offset + length; -#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE +#if OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE if ((oldDuaPtr = aChild.GetDomainUnicastAddress()) != nullptr) { oldDua = *oldDuaPtr; } #endif -#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE // Retrieve registered multicast addresses of the Child if (aChild.HasAnyMlrRegisteredAddress()) { @@ -2194,36 +1836,47 @@ Error MleRouter::UpdateChildAddresses(const Message &aMessage, uint16_t aOffset, aChild.ClearIp6Addresses(); - while (offset < end) + while (offset < endOffset) { - uint8_t len; + uint8_t controlByte; + Ip6::Address address; - // read out the control field - SuccessOrExit(error = aMessage.Read(offset, &entry, sizeof(uint8_t))); + // Read out the control byte (first byte in entry) + SuccessOrExit(error = aRxInfo.mMessage.Read(offset, controlByte)); + offset++; + count++; - len = entry.GetLength(); + address.Clear(); - SuccessOrExit(error = aMessage.Read(offset, &entry, len)); + if (AddressRegistrationTlv::IsEntryCompressed(controlByte)) + { + // Compressed entry contains IID with the 64-bit prefix + // determined from 6LoWPAN context identifier (from + // the control byte). - offset += len; - registeredCount++; + uint8_t contextId = AddressRegistrationTlv::GetContextId(controlByte); + Lowpan::Context context; - if (entry.IsCompressed()) - { - if (Get().GetContext(entry.GetContextId(), context) != kErrorNone) + VerifyOrExit(offset + sizeof(Ip6::InterfaceIdentifier) <= endOffset, error = kErrorParse); + IgnoreError(aRxInfo.mMessage.Read(offset, address.GetIid())); + offset += sizeof(Ip6::InterfaceIdentifier); + + if (Get().GetContext(contextId, context) != kErrorNone) { - LogWarn("Failed to get context %d for compressed address from child 0x%04x", entry.GetContextId(), + LogWarn("Failed to get context %u for compressed address from child 0x%04x", contextId, aChild.GetRloc16()); continue; } - address.Clear(); address.SetPrefix(context.mPrefix); - address.SetIid(entry.GetIid()); } else { - address = entry.GetIp6Address(); + // Uncompressed entry contains the full IPv6 address. + + VerifyOrExit(offset + sizeof(Ip6::Address) <= endOffset, error = kErrorParse); + IgnoreError(aRxInfo.mMessage.Read(offset, address)); + offset += sizeof(Ip6::Address); } #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE @@ -2245,7 +1898,7 @@ Error MleRouter::UpdateChildAddresses(const Message &aMessage, uint16_t aOffset, { storedCount++; -#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE +#if OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE if (Get().IsDomainUnicast(address)) { hasDua = true; @@ -2262,12 +1915,12 @@ Error MleRouter::UpdateChildAddresses(const Message &aMessage, uint16_t aOffset, } #endif - LogInfo("Child 0x%04x IPv6 address[%d]=%s", aChild.GetRloc16(), storedCount, + LogInfo("Child 0x%04x IPv6 address[%u]=%s", aChild.GetRloc16(), storedCount, address.ToString().AsCString()); } else { -#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE +#if OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE if (Get().IsDomainUnicast(address)) { // if not able to store DUA, then assume child does not have one @@ -2305,9 +1958,9 @@ Error MleRouter::UpdateChildAddresses(const Message &aMessage, uint16_t aOffset, } // Clear EID-to-RLOC cache for the unicast address registered by the child. - Get().Remove(address); + Get().RemoveEntryForAddress(address); } -#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE +#if OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE // Dua is removed if (oldDuaPtr != nullptr && !hasDua) { @@ -2315,18 +1968,18 @@ Error MleRouter::UpdateChildAddresses(const Message &aMessage, uint16_t aOffset, } #endif -#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE Get().UpdateProxiedSubscriptions(aChild, oldMlrRegisteredAddresses, oldMlrRegisteredAddressNum); #endif - if (registeredCount == 0) + if (count == 0) { LogInfo("Child 0x%04x has no registered IPv6 address", aChild.GetRloc16()); } else { - LogInfo("Child 0x%04x has %d registered IPv6 address%s, %d address%s stored", aChild.GetRloc16(), - registeredCount, (registeredCount == 1) ? "" : "es", storedCount, (storedCount == 1) ? "" : "es"); + LogInfo("Child 0x%04x has %u registered IPv6 address%s, %u address%s stored", aChild.GetRloc16(), count, + (count == 1) ? "" : "es", storedCount, (storedCount == 1) ? "" : "es"); } error = kErrorNone; @@ -2346,14 +1999,14 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) uint8_t modeBitmask; DeviceMode mode; uint32_t timeout; - RequestedTlvs requestedTlvs; + TlvList requestedTlvList; MeshCoP::Timestamp timestamp; bool needsActiveDatasetTlv; bool needsPendingDatasetTlv; - Child * child; - Router * router; + Child *child; + Router *router; uint8_t numTlvs; - uint16_t addressRegistrationOffset = 0; + uint16_t supervisionInterval; Log(kMessageReceive, kTypeChildIdRequest, aRxInfo.mMessageInfo.GetPeerAddr()); @@ -2370,7 +2023,7 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) // Version SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, version)); - VerifyOrExit(version >= OT_THREAD_VERSION_1_1, error = kErrorParse); + VerifyOrExit(version >= kThreadVersion1p1, error = kErrorParse); // Response SuccessOrExit(error = aRxInfo.mMessage.ReadResponseTlv(response)); @@ -2392,9 +2045,20 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) // Timeout SuccessOrExit(error = Tlv::Find(aRxInfo.mMessage, timeout)); + // Supervision interval + switch (Tlv::Find(aRxInfo.mMessage, supervisionInterval)) + { + case kErrorNone: + break; + case kErrorNotFound: + supervisionInterval = (version <= kThreadVersion1p3) ? kChildSupervisionDefaultIntervalForOlderVersion : 0; + break; + default: + ExitNow(error = kErrorParse); + } + // TLV Request - SuccessOrExit(error = aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvs)); - VerifyOrExit(requestedTlvs.mNumTlvs <= Child::kMaxRequestTlvs, error = kErrorParse); + SuccessOrExit(error = aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList)); // Active Timestamp needsActiveDatasetTlv = true; @@ -2424,15 +2088,28 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) ExitNow(error = kErrorParse); } + numTlvs = requestedTlvList.GetLength(); + + if (needsActiveDatasetTlv) + { + numTlvs++; + } + + if (needsPendingDatasetTlv) + { + numTlvs++; + } + + VerifyOrExit(numTlvs <= Child::kMaxRequestTlvs, error = kErrorParse); + if (!mode.IsFullThreadDevice()) { - SuccessOrExit(error = - Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kAddressRegistration, addressRegistrationOffset)); - SuccessOrExit(error = UpdateChildAddresses(aRxInfo.mMessage, addressRegistrationOffset, *child)); + SuccessOrExit(error = ProcessAddressRegistrationTlv(aRxInfo, *child)); } // Remove from router table - router = mRouterTable.GetRouter(extAddr); + router = mRouterTable.FindRouter(extAddr); + if (router != nullptr) { // The `router` here can be invalid @@ -2454,9 +2131,10 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) child->SetMleFrameCounter(mleFrameCounter); child->SetKeySequence(aRxInfo.mKeySequence); child->SetDeviceMode(mode); - child->SetVersion(static_cast(version)); + child->SetVersion(version); child->GetLinkInfo().AddRss(aRxInfo.mMessageInfo.GetThreadLinkInfo()->GetRss()); child->SetTimeout(timeout); + child->SetSupervisionInterval(supervisionInterval); #if OPENTHREAD_CONFIG_MULTI_RADIO child->ClearLastRxFragmentTag(); #endif @@ -2464,9 +2142,9 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) child->SetNetworkDataVersion(mLeaderData.GetDataVersion(mode.GetNetworkDataType())); child->ClearRequestTlvs(); - for (numTlvs = 0; numTlvs < requestedTlvs.mNumTlvs; numTlvs++) + for (numTlvs = 0; numTlvs < requestedTlvList.GetLength(); numTlvs++) { - child->SetRequestTlv(numTlvs, requestedTlvs.mTlvs[numTlvs]); + child->SetRequestTlv(numTlvs, requestedTlvList[numTlvs]); } if (needsActiveDatasetTlv) @@ -2483,11 +2161,6 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) switch (mRole) { - case kRoleDisabled: - case kRoleDetached: - OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); - case kRoleChild: child->SetState(Neighbor::kStateChildIdRequest); IgnoreError(BecomeRouter(ThreadStatusTlv::kHaveChildIdRequest)); @@ -2497,6 +2170,10 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) case kRoleLeader: SuccessOrExit(error = SendChildIdResponse(*child)); break; + + case kRoleDisabled: + case kRoleDetached: + OT_ASSERT(false); } exit: @@ -2505,8 +2182,6 @@ void MleRouter::HandleChildIdRequest(RxInfo &aRxInfo) void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) { - static const uint8_t kMaxResponseTlvs = 10; - Error error = kErrorNone; Mac::ExtAddress extAddr; uint8_t modeBitmask; @@ -2514,13 +2189,12 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) Challenge challenge; LeaderData leaderData; uint32_t timeout; - Child * child; + uint16_t supervisionInterval; + Child *child; DeviceMode oldMode; - RequestedTlvs requestedTlvs; - uint8_t tlvs[kMaxResponseTlvs]; - uint8_t tlvslength = 0; - uint16_t addressRegistrationOffset = 0; - bool childDidChange = false; + TlvList requestedTlvList; + TlvList tlvList; + bool childDidChange = false; Log(kMessageReceive, kTypeChildUpdateRequestOfChild, aRxInfo.mMessageInfo.GetPeerAddr()); @@ -2532,7 +2206,7 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) switch (aRxInfo.mMessage.ReadChallengeTlv(challenge)) { case kErrorNone: - tlvs[tlvslength++] = Tlv::kResponse; + tlvList.Add(Tlv::kResponse); break; case kErrorNotFound: challenge.mLength = 0; @@ -2541,7 +2215,7 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) ExitNow(error = kErrorParse); } - tlvs[tlvslength++] = Tlv::kSourceAddress; + tlvList.Add(Tlv::kSourceAddress); aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(extAddr); child = mChildTable.FindChild(extAddr, Child::kInStateAnyExceptInvalid); @@ -2552,8 +2226,8 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) // Status TLV (error). if (mode.IsRxOnWhenIdle()) { - tlvs[tlvslength++] = Tlv::kStatus; - SendChildUpdateResponse(nullptr, aRxInfo.mMessageInfo, tlvs, tlvslength, challenge); + tlvList.Add(Tlv::kStatus); + SendChildUpdateResponse(nullptr, aRxInfo.mMessageInfo, tlvList, challenge); } ExitNow(); @@ -2570,22 +2244,27 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) oldMode = child->GetDeviceMode(); child->SetDeviceMode(mode); - tlvs[tlvslength++] = Tlv::kMode; + tlvList.Add(Tlv::kMode); // Parent MUST include Leader Data TLV in Child Update Response - tlvs[tlvslength++] = Tlv::kLeaderData; + tlvList.Add(Tlv::kLeaderData); if (challenge.mLength != 0) { - tlvs[tlvslength++] = Tlv::kMleFrameCounter; - tlvs[tlvslength++] = Tlv::kLinkFrameCounter; + tlvList.Add(Tlv::kMleFrameCounter); + tlvList.Add(Tlv::kLinkFrameCounter); } // IPv6 Address TLV - if (Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kAddressRegistration, addressRegistrationOffset) == kErrorNone) + switch (ProcessAddressRegistrationTlv(aRxInfo, *child)) { - SuccessOrExit(error = UpdateChildAddresses(aRxInfo.mMessage, addressRegistrationOffset, *child)); - tlvs[tlvslength++] = Tlv::kAddressRegistration; + case kErrorNone: + tlvList.Add(Tlv::kAddressRegistration); + break; + case kErrorNotFound: + break; + default: + ExitNow(error = kErrorParse); } // Leader Data @@ -2610,7 +2289,7 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) childDidChange = true; } - tlvs[tlvslength++] = Tlv::kTimeout; + tlvList.Add(Tlv::kTimeout); break; case kErrorNotFound: @@ -2620,19 +2299,29 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) ExitNow(error = kErrorParse); } + // Supervision interval + switch (Tlv::Find(aRxInfo.mMessage, supervisionInterval)) + { + case kErrorNone: + tlvList.Add(Tlv::kSupervisionInterval); + break; + + case kErrorNotFound: + supervisionInterval = + (child->GetVersion() <= kThreadVersion1p3) ? kChildSupervisionDefaultIntervalForOlderVersion : 0; + break; + + default: + ExitNow(error = kErrorParse); + } + + child->SetSupervisionInterval(supervisionInterval); + // TLV Request - switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvs)) + switch (aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvList)) { case kErrorNone: - VerifyOrExit(requestedTlvs.mNumTlvs <= (kMaxResponseTlvs - tlvslength), error = kErrorParse); - for (uint8_t i = 0; i < requestedTlvs.mNumTlvs; i++) - { - // Skip LeaderDataTlv since it is already included by default. - if (requestedTlvs.mTlvs[i] != Tlv::kLeaderData) - { - tlvs[tlvslength++] = requestedTlvs.mTlvs[i]; - } - } + tlvList.AddElementsFrom(requestedTlvList); break; case kErrorNotFound: break; @@ -2646,22 +2335,27 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) CslChannelTlv cslChannel; uint32_t cslTimeout; - if (Tlv::Find(aRxInfo.mMessage, cslTimeout) == kErrorNone) + switch (Tlv::Find(aRxInfo.mMessage, cslTimeout)) { + case kErrorNone: child->SetCslTimeout(cslTimeout); // MUST include CSL accuracy TLV when request includes CSL timeout - tlvs[tlvslength++] = Tlv::kCslClockAccuracy; + tlvList.Add(Tlv::kCslClockAccuracy); + break; + case kErrorNotFound: + break; + default: + ExitNow(error = kErrorNone); } if (Tlv::FindTlv(aRxInfo.mMessage, cslChannel) == kErrorNone) { + VerifyOrExit(cslChannel.IsValid(), error = kErrorParse); + + // Special value of zero is used to indicate that + // CSL channel is not specified. child->SetCslChannel(static_cast(cslChannel.GetChannel())); } - else - { - // Set CSL Channel unspecified. - child->SetCslChannel(0); - } } #endif // OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE @@ -2706,7 +2400,7 @@ void MleRouter::HandleChildUpdateRequest(RxInfo &aRxInfo) } #endif - SendChildUpdateResponse(child, aRxInfo.mMessageInfo, tlvs, tlvslength, challenge); + SendChildUpdateResponse(child, aRxInfo.mMessageInfo, tlvList, challenge); aRxInfo.mClass = RxInfo::kPeerMessage; @@ -2724,10 +2418,10 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo) uint32_t linkFrameCounter; uint32_t mleFrameCounter; LeaderData leaderData; - Child * child; - uint16_t addressRegistrationOffset = 0; + Child *child; - if ((aRxInfo.mNeighbor == nullptr) || IsActiveRouter(aRxInfo.mNeighbor->GetRloc16())) + if ((aRxInfo.mNeighbor == nullptr) || IsActiveRouter(aRxInfo.mNeighbor->GetRloc16()) || + !Get().Contains(*aRxInfo.mNeighbor)) { Log(kMessageReceive, kTypeChildUpdateResponseOfUnknownChild, aRxInfo.mMessageInfo.GetPeerAddr()); ExitNow(error = kErrorNotFound); @@ -2771,7 +2465,7 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo) } // Status - switch (Tlv::Find(aRxInfo.mMessage, status)) + switch (Tlv::Find(aRxInfo.mMessage, status)) { case kErrorNone: VerifyOrExit(status != StatusTlv::kError, RemoveNeighbor(*child)); @@ -2820,12 +2514,31 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo) ExitNow(error = kErrorParse); } - // IPv6 Address - if (Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kAddressRegistration, addressRegistrationOffset) == kErrorNone) - { - SuccessOrExit(error = UpdateChildAddresses(aRxInfo.mMessage, addressRegistrationOffset, *child)); - } - + { + uint16_t supervisionInterval; + + switch (Tlv::Find(aRxInfo.mMessage, supervisionInterval)) + { + case kErrorNone: + child->SetSupervisionInterval(supervisionInterval); + break; + case kErrorNotFound: + break; + default: + ExitNow(error = kErrorParse); + } + } + + // IPv6 Address + switch (ProcessAddressRegistrationTlv(aRxInfo, *child)) + { + case kErrorNone: + case kErrorNotFound: + break; + default: + ExitNow(error = kErrorParse); + } + // Leader Data switch (aRxInfo.mMessage.ReadLeaderDataTlv(leaderData)) { @@ -2852,22 +2565,15 @@ void MleRouter::HandleChildUpdateResponse(RxInfo &aRxInfo) void MleRouter::HandleDataRequest(RxInfo &aRxInfo) { Error error = kErrorNone; - RequestedTlvs requestedTlvs; + TlvList tlvList; MeshCoP::Timestamp timestamp; - uint8_t tlvs[4]; - uint8_t numTlvs; Log(kMessageReceive, kTypeDataRequest, aRxInfo.mMessageInfo.GetPeerAddr()); - VerifyOrExit(aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid(), error = kErrorSecurity); + VerifyOrExit(aRxInfo.IsNeighborStateValid(), error = kErrorSecurity); // TLV Request - SuccessOrExit(error = aRxInfo.mMessage.ReadTlvRequestTlv(requestedTlvs)); - VerifyOrExit(requestedTlvs.mNumTlvs <= sizeof(tlvs), error = kErrorParse); - - memset(tlvs, Tlv::kInvalid, sizeof(tlvs)); - memcpy(tlvs, requestedTlvs.mTlvs, requestedTlvs.mNumTlvs); - numTlvs = requestedTlvs.mNumTlvs; + SuccessOrExit(error = aRxInfo.mMessage.ReadTlvRequestTlv(tlvList)); // Active Timestamp switch (Tlv::Find(aRxInfo.mMessage, timestamp)) @@ -2881,7 +2587,7 @@ void MleRouter::HandleDataRequest(RxInfo &aRxInfo) OT_FALL_THROUGH; case kErrorNotFound: - tlvs[numTlvs++] = Tlv::kActiveDataset; + tlvList.Add(Tlv::kActiveDataset); break; default: @@ -2900,7 +2606,7 @@ void MleRouter::HandleDataRequest(RxInfo &aRxInfo) OT_FALL_THROUGH; case kErrorNotFound: - tlvs[numTlvs++] = Tlv::kPendingDataset; + tlvList.Add(Tlv::kPendingDataset); break; default: @@ -2909,7 +2615,7 @@ void MleRouter::HandleDataRequest(RxInfo &aRxInfo) aRxInfo.mClass = RxInfo::kPeerMessage; - SendDataResponse(aRxInfo.mMessageInfo.GetPeerAddr(), tlvs, numTlvs, 0, &aRxInfo.mMessage); + SendDataResponse(aRxInfo.mMessageInfo.GetPeerAddr(), tlvList, /* aDelay */ 0, &aRxInfo.mMessage); exit: LogProcessError(kTypeDataRequest, error); @@ -2917,16 +2623,17 @@ void MleRouter::HandleDataRequest(RxInfo &aRxInfo) void MleRouter::HandleNetworkDataUpdateRouter(void) { - static const uint8_t tlvs[] = {Tlv::kNetworkData}; - Ip6::Address destination; - uint16_t delay; + Ip6::Address destination; + uint16_t delay; + TlvList tlvList; VerifyOrExit(IsRouterOrLeader()); destination.SetToLinkLocalAllNodesMulticast(); + tlvList.Add(Tlv::kNetworkData); delay = IsLeader() ? 0 : Random::NonCrypto::GetUint16InRange(0, kUnsolicitedDataResponseJitter); - SendDataResponse(destination, tlvs, sizeof(tlvs), delay); + SendDataResponse(destination, tlvList, delay); SynchronizeChildNetworkData(); @@ -2988,26 +2695,22 @@ void MleRouter::SetSteeringData(const Mac::ExtAddress *aExtAddress) void MleRouter::HandleDiscoveryRequest(RxInfo &aRxInfo) { Error error = kErrorNone; - Tlv tlv; MeshCoP::Tlv meshcopTlv; - MeshCoP::DiscoveryRequestTlv discoveryRequest; + MeshCoP::DiscoveryRequestTlv discoveryRequestTlv; MeshCoP::ExtendedPanId extPanId; uint16_t offset; + uint16_t length; uint16_t end; Log(kMessageReceive, kTypeDiscoveryRequest, aRxInfo.mMessageInfo.GetPeerAddr()); - discoveryRequest.SetLength(0); + discoveryRequestTlv.SetLength(0); // only Routers and REEDs respond VerifyOrExit(IsRouterEligible(), error = kErrorInvalidState); - // find MLE Discovery TLV - VerifyOrExit(Tlv::FindTlvOffset(aRxInfo.mMessage, Tlv::kDiscovery, offset) == kErrorNone, error = kErrorParse); - IgnoreError(aRxInfo.mMessage.Read(offset, tlv)); - - offset += sizeof(tlv); - end = offset + sizeof(tlv) + tlv.GetLength(); + SuccessOrExit(error = Tlv::FindTlvValueOffset(aRxInfo.mMessage, Tlv::kDiscovery, offset, length)); + end = offset + length; while (offset < end) { @@ -3016,8 +2719,8 @@ void MleRouter::HandleDiscoveryRequest(RxInfo &aRxInfo) switch (meshcopTlv.GetType()) { case MeshCoP::Tlv::kDiscoveryRequest: - IgnoreError(aRxInfo.mMessage.Read(offset, discoveryRequest)); - VerifyOrExit(discoveryRequest.IsValid(), error = kErrorParse); + IgnoreError(aRxInfo.mMessage.Read(offset, discoveryRequestTlv)); + VerifyOrExit(discoveryRequestTlv.IsValid(), error = kErrorParse); break; @@ -3034,20 +2737,20 @@ void MleRouter::HandleDiscoveryRequest(RxInfo &aRxInfo) offset += sizeof(meshcopTlv) + meshcopTlv.GetLength(); } - if (discoveryRequest.IsValid()) + if (discoveryRequestTlv.IsValid()) { - if (mDiscoveryRequestCallback != nullptr) + if (mDiscoveryRequestCallback.IsSet()) { otThreadDiscoveryRequestInfo info; aRxInfo.mMessageInfo.GetPeerAddr().GetIid().ConvertToExtAddress(AsCoreType(&info.mExtAddress)); - info.mVersion = discoveryRequest.GetVersion(); - info.mIsJoiner = discoveryRequest.IsJoiner(); + info.mVersion = discoveryRequestTlv.GetVersion(); + info.mIsJoiner = discoveryRequestTlv.IsJoiner(); - mDiscoveryRequestCallback(&info, mDiscoveryRequestCallbackContext); + mDiscoveryRequestCallback.Invoke(&info); } - if (discoveryRequest.IsJoiner()) + if (discoveryRequestTlv.IsJoiner()) { #if OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE if (!mSteeringData.IsEmpty()) @@ -3070,14 +2773,15 @@ void MleRouter::HandleDiscoveryRequest(RxInfo &aRxInfo) Error MleRouter::SendDiscoveryResponse(const Ip6::Address &aDestination, const Message &aDiscoverRequestMessage) { Error error = kErrorNone; - TxMessage * message; + TxMessage *message; uint16_t startOffset; Tlv tlv; - MeshCoP::DiscoveryResponseTlv discoveryResponse; - MeshCoP::NetworkNameTlv networkName; + MeshCoP::DiscoveryResponseTlv discoveryResponseTlv; + MeshCoP::NetworkNameTlv networkNameTlv; uint16_t delay; VerifyOrExit((message = NewMleMessage(kCommandDiscoveryResponse)) != nullptr, error = kErrorNoBufs); + message->SetDirectTransmission(); message->SetPanId(aDiscoverRequestMessage.GetPanId()); #if OPENTHREAD_CONFIG_MULTI_RADIO // Send the MLE Discovery Response message on same radio link @@ -3092,8 +2796,8 @@ Error MleRouter::SendDiscoveryResponse(const Ip6::Address &aDestination, const M startOffset = message->GetLength(); // Discovery Response TLV - discoveryResponse.Init(); - discoveryResponse.SetVersion(kThreadVersion); + discoveryResponseTlv.Init(); + discoveryResponseTlv.SetVersion(kThreadVersion); #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE if (Get().GetSecurityPolicy().mNativeCommissioningEnabled) @@ -3101,29 +2805,29 @@ Error MleRouter::SendDiscoveryResponse(const Ip6::Address &aDestination, const M SuccessOrExit( error = Tlv::Append(*message, Get().GetUdpPort())); - discoveryResponse.SetNativeCommissioner(true); + discoveryResponseTlv.SetNativeCommissioner(true); } else #endif { - discoveryResponse.SetNativeCommissioner(false); + discoveryResponseTlv.SetNativeCommissioner(false); } if (Get().GetSecurityPolicy().mCommercialCommissioningEnabled) { - discoveryResponse.SetCommercialCommissioningMode(true); + discoveryResponseTlv.SetCommercialCommissioningMode(true); } - SuccessOrExit(error = discoveryResponse.AppendTo(*message)); + SuccessOrExit(error = discoveryResponseTlv.AppendTo(*message)); // Extended PAN ID TLV SuccessOrExit( error = Tlv::Append(*message, Get().GetExtPanId())); // Network Name TLV - networkName.Init(); - networkName.SetNetworkName(Get().GetNetworkName().GetAsData()); - SuccessOrExit(error = networkName.AppendTo(*message)); + networkNameTlv.Init(); + networkNameTlv.SetNetworkName(Get().GetNetworkName().GetAsData()); + SuccessOrExit(error = networkNameTlv.AppendTo(*message)); #if OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE // If steering data is set out of band, use that value. @@ -3168,7 +2872,7 @@ Error MleRouter::SendChildIdResponse(Child &aChild) { Error error = kErrorNone; Ip6::Address destination; - TxMessage * message; + TxMessage *message; VerifyOrExit((message = NewMleMessage(kCommandChildIdResponse)) != nullptr, error = kErrorNoBufs); SuccessOrExit(error = message->AppendSourceAddressTlv()); @@ -3227,7 +2931,7 @@ Error MleRouter::SendChildIdResponse(Child &aChild) if (!aChild.IsFullThreadDevice()) { - SuccessOrExit(error = message->AppendAddresseRegisterationTlv(aChild)); + SuccessOrExit(error = message->AppendAddressRegistrationTlv(aChild)); } SetChildStateToValid(aChild); @@ -3256,10 +2960,11 @@ Error MleRouter::SendChildIdResponse(Child &aChild) Error MleRouter::SendChildUpdateRequest(Child &aChild) { - static const uint8_t tlvs[] = {Tlv::kTimeout, Tlv::kAddressRegistration}; - Error error = kErrorNone; - Ip6::Address destination; - TxMessage * message = nullptr; + static const uint8_t kTlvs[] = {Tlv::kTimeout, Tlv::kAddressRegistration}; + + Error error = kErrorNone; + Ip6::Address destination; + TxMessage *message = nullptr; if (!aChild.IsRxOnWhenIdle()) { @@ -3292,7 +2997,7 @@ Error MleRouter::SendChildUpdateRequest(Child &aChild) if (!aChild.IsStateValid()) { - SuccessOrExit(error = message->AppendTlvRequestTlv(tlvs, sizeof(tlvs))); + SuccessOrExit(error = message->AppendTlvRequestTlv(kTlvs)); aChild.GenerateChallenge(); SuccessOrExit(error = message->AppendChallengeTlv(aChild.GetChallenge(), aChild.GetChallengeSize())); } @@ -3313,43 +3018,30 @@ Error MleRouter::SendChildUpdateRequest(Child &aChild) return error; } -void MleRouter::SendChildUpdateResponse(Child * aChild, +void MleRouter::SendChildUpdateResponse(Child *aChild, const Ip6::MessageInfo &aMessageInfo, - const uint8_t * aTlvs, - uint8_t aTlvsLength, - const Challenge & aChallenge) + const TlvList &aTlvList, + const Challenge &aChallenge) { Error error = kErrorNone; TxMessage *message; VerifyOrExit((message = NewMleMessage(kCommandChildUpdateResponse)) != nullptr, error = kErrorNoBufs); - for (int i = 0; i < aTlvsLength; i++) + for (uint8_t tlvType : aTlvList) { - switch (aTlvs[i]) + // Add all TLV types that do not depend on `child` + + switch (tlvType) { case Tlv::kStatus: SuccessOrExit(error = message->AppendStatusTlv(StatusTlv::kError)); break; - case Tlv::kAddressRegistration: - SuccessOrExit(error = message->AppendAddresseRegisterationTlv(*aChild)); - break; - case Tlv::kLeaderData: SuccessOrExit(error = message->AppendLeaderDataTlv()); break; - case Tlv::kMode: - SuccessOrExit(error = message->AppendModeTlv(aChild->GetDeviceMode())); - break; - - case Tlv::kNetworkData: - SuccessOrExit(error = message->AppendNetworkDataTlv(aChild->GetNetworkDataType())); - SuccessOrExit(error = message->AppendActiveTimestampTlv()); - SuccessOrExit(error = message->AppendPendingTimestampTlv()); - break; - case Tlv::kResponse: SuccessOrExit(error = message->AppendResponseTlv(aChallenge)); break; @@ -3358,10 +3050,6 @@ void MleRouter::SendChildUpdateResponse(Child * aChild, SuccessOrExit(error = message->AppendSourceAddressTlv()); break; - case Tlv::kTimeout: - SuccessOrExit(error = message->AppendTimeoutTlv(aChild->GetTimeout())); - break; - case Tlv::kMleFrameCounter: SuccessOrExit(error = message->AppendMleFrameCounterTlv()); break; @@ -3369,6 +3057,39 @@ void MleRouter::SendChildUpdateResponse(Child * aChild, case Tlv::kLinkFrameCounter: SuccessOrExit(error = message->AppendLinkFrameCounterTlv()); break; + } + + // Make sure `child` is not null before adding TLV types + // that can depend on it. + + if (aChild == nullptr) + { + continue; + } + + switch (tlvType) + { + case Tlv::kAddressRegistration: + SuccessOrExit(error = message->AppendAddressRegistrationTlv(*aChild)); + break; + + case Tlv::kMode: + SuccessOrExit(error = message->AppendModeTlv(aChild->GetDeviceMode())); + break; + + case Tlv::kNetworkData: + SuccessOrExit(error = message->AppendNetworkDataTlv(aChild->GetNetworkDataType())); + SuccessOrExit(error = message->AppendActiveTimestampTlv()); + SuccessOrExit(error = message->AppendPendingTimestampTlv()); + break; + + case Tlv::kTimeout: + SuccessOrExit(error = message->AppendTimeoutTlv(aChild->GetTimeout())); + break; + + case Tlv::kSupervisionInterval: + SuccessOrExit(error = message->AppendSupervisionIntervalTlv(aChild->GetSupervisionInterval())); + break; #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE case Tlv::kCslClockAccuracy: @@ -3397,16 +3118,15 @@ void MleRouter::SendChildUpdateResponse(Child * aChild, } void MleRouter::SendDataResponse(const Ip6::Address &aDestination, - const uint8_t * aTlvs, - uint8_t aTlvsLength, + const TlvList &aTlvList, uint16_t aDelay, - const Message * aRequestMessage) + const Message *aRequestMessage) { OT_UNUSED_VARIABLE(aRequestMessage); Error error = kErrorNone; TxMessage *message = nullptr; - Neighbor * neighbor; + Neighbor *neighbor; if (mRetrieveNewNetworkData) { @@ -3420,9 +3140,9 @@ void MleRouter::SendDataResponse(const Ip6::Address &aDestination, SuccessOrExit(error = message->AppendActiveTimestampTlv()); SuccessOrExit(error = message->AppendPendingTimestampTlv()); - for (int i = 0; i < aTlvsLength; i++) + for (uint8_t tlvType : aTlvList) { - switch (aTlvs[i]) + switch (tlvType) { case Tlv::kNetworkData: neighbor = mNeighborTable.FindNeighbor(aDestination); @@ -3438,12 +3158,16 @@ void MleRouter::SendDataResponse(const Ip6::Address &aDestination, SuccessOrExit(error = message->AppendPendingDatasetTlv()); break; + case Tlv::kRoute: + SuccessOrExit(error = message->AppendRouteTlv()); + break; + #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE case Tlv::kLinkMetricsReport: OT_ASSERT(aRequestMessage != nullptr); neighbor = mNeighborTable.FindNeighbor(aDestination); VerifyOrExit(neighbor != nullptr, error = kErrorInvalidState); - SuccessOrExit(error = Get().AppendReport(*message, *aRequestMessage, *neighbor)); + SuccessOrExit(error = Get().AppendReport(*message, *aRequestMessage, *neighbor)); break; #endif } @@ -3498,12 +3222,10 @@ void MleRouter::RemoveRouterLink(Router &aRouter) } break; -#if OPENTHREAD_FTD case kRoleRouter: case kRoleLeader: mRouterTable.RemoveRouterLink(aRouter); break; -#endif default: break; @@ -3521,13 +3243,13 @@ void MleRouter::RemoveNeighbor(Neighbor &aNeighbor) IgnoreError(BecomeDetached()); } } - else if (&aNeighbor == &mParentCandidate) + else if (&aNeighbor == &GetParentCandidate()) { - mParentCandidate.Clear(); + ClearParentCandidate(); } else if (!IsActiveRouter(aNeighbor.GetRloc16())) { - OT_ASSERT(mChildTable.GetChildIndex(static_cast(aNeighbor)) < kMaxChildren); + OT_ASSERT(mChildTable.Contains(aNeighbor)); if (aNeighbor.IsStateValidOrRestoring()) { @@ -3539,7 +3261,7 @@ void MleRouter::RemoveNeighbor(Neighbor &aNeighbor) if (aNeighbor.IsFullThreadDevice()) { // Clear all EID-to-RLOC entries associated with the child. - Get().Remove(aNeighbor.GetRloc16()); + Get().RemoveEntriesForRloc16(aNeighbor.GetRloc16()); } mChildTable.RemoveStoredChild(static_cast(aNeighbor)); @@ -3562,82 +3284,6 @@ void MleRouter::RemoveNeighbor(Neighbor &aNeighbor) return; } -uint16_t MleRouter::GetNextHop(uint16_t aDestination) -{ - uint8_t destinationId = RouterIdFromRloc16(aDestination); - uint8_t routeCost; - uint8_t linkCost; - uint16_t rval = Mac::kShortAddrInvalid; - const Router *router; - const Router *nextHop; - - if (IsChild()) - { - ExitNow(rval = Mle::GetNextHop(aDestination)); - } - - // The frame is destined to a child - if (destinationId == mRouterId) - { - ExitNow(rval = aDestination); - } - - router = mRouterTable.GetRouter(destinationId); - VerifyOrExit(router != nullptr); - - linkCost = GetLinkCost(destinationId); - routeCost = GetRouteCost(aDestination); - - if ((routeCost + GetLinkCost(router->GetNextHop())) < linkCost) - { - nextHop = mRouterTable.GetRouter(router->GetNextHop()); - VerifyOrExit(nextHop != nullptr && !nextHop->IsStateInvalid()); - - rval = Rloc16FromRouterId(router->GetNextHop()); - } - else if (linkCost < kMaxRouteCost) - { - rval = Rloc16FromRouterId(destinationId); - } - -exit: - return rval; -} - -uint8_t MleRouter::GetCost(uint16_t aRloc16) -{ - uint8_t routerId = RouterIdFromRloc16(aRloc16); - uint8_t cost = GetLinkCost(routerId); - Router *router = mRouterTable.GetRouter(routerId); - uint8_t routeCost; - - VerifyOrExit(router != nullptr && mRouterTable.GetRouter(router->GetNextHop()) != nullptr); - - routeCost = GetRouteCost(aRloc16) + GetLinkCost(router->GetNextHop()); - - if (cost > routeCost) - { - cost = routeCost; - } - -exit: - return cost; -} - -uint8_t MleRouter::GetRouteCost(uint16_t aRloc16) const -{ - uint8_t rval = kMaxRouteCost; - const Router *router; - - router = mRouterTable.GetRouter(RouterIdFromRloc16(aRloc16)); - VerifyOrExit(router != nullptr && mRouterTable.GetRouter(router->GetNextHop()) != nullptr); - - rval = router->GetCost(); - -exit: - return rval; -} - Error MleRouter::SetPreferredRouterId(uint8_t aRouterId) { Error error = kErrorNone; @@ -3666,11 +3312,11 @@ void MleRouter::ResolveRoutingLoops(uint16_t aSourceMac, uint16_t aDestRloc16) } // loop exists - router = mRouterTable.GetRouter(RouterIdFromRloc16(aDestRloc16)); + router = mRouterTable.FindRouterByRloc16(aDestRloc16); VerifyOrExit(router != nullptr); // invalidate next hop - router->SetNextHop(kInvalidRouterId); + router->SetNextHopToInvalid(); ResetAdvertiseInterval(); exit: @@ -3725,11 +3371,11 @@ Error MleRouter::SendAddressSolicit(ThreadStatusTlv::Status aStatus) { Error error = kErrorNone; Tmf::MessageInfo messageInfo(GetInstance()); - Coap::Message * message = nullptr; + Coap::Message *message = nullptr; VerifyOrExit(!mAddressSolicitPending); - message = Get().NewPriorityConfirmablePostMessage(UriPath::kAddressSolicit); + message = Get().NewPriorityConfirmablePostMessage(kUriAddressSolicit); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = Tlv::Append(*message, Get().GetExtAddress())); @@ -3757,13 +3403,13 @@ Error MleRouter::SendAddressSolicit(ThreadStatusTlv::Status aStatus) return error; } -void MleRouter::SendAddressRelease(Coap::ResponseHandler aResponseHandler, void *aResponseHandlerContext) +void MleRouter::SendAddressRelease(void) { Error error = kErrorNone; Tmf::MessageInfo messageInfo(GetInstance()); - Coap::Message * message; + Coap::Message *message; - message = Get().NewPriorityConfirmablePostMessage(UriPath::kAddressRelease); + message = Get().NewPriorityConfirmablePostMessage(kUriAddressRelease); VerifyOrExit(message != nullptr, error = kErrorNoBufs); SuccessOrExit(error = Tlv::Append(*message, Rloc16FromRouterId(mRouterId))); @@ -3771,8 +3417,7 @@ void MleRouter::SendAddressRelease(Coap::ResponseHandler aResponseHandler, void SuccessOrExit(error = messageInfo.SetSockAddrToRlocPeerAddrToLeaderRloc()); - SuccessOrExit(error = - Get().SendMessage(*message, messageInfo, aResponseHandler, aResponseHandlerContext)); + SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); Log(kMessageSend, kTypeAddressRelease, messageInfo.GetPeerAddr()); @@ -3781,8 +3426,8 @@ void MleRouter::SendAddressRelease(Coap::ResponseHandler aResponseHandler, void LogSendError(kTypeAddressRelease, error); } -void MleRouter::HandleAddressSolicitResponse(void * aContext, - otMessage * aMessage, +void MleRouter::HandleAddressSolicitResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult) { @@ -3790,7 +3435,7 @@ void MleRouter::HandleAddressSolicitResponse(void * aContext, AsCoreTypePtr(aMessageInfo), aResult); } -void MleRouter::HandleAddressSolicitResponse(Coap::Message * aMessage, +void MleRouter::HandleAddressSolicitResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult) { @@ -3798,8 +3443,7 @@ void MleRouter::HandleAddressSolicitResponse(Coap::Message * aMessage, uint16_t rloc16; ThreadRouterMaskTlv routerMaskTlv; uint8_t routerId; - Router * router; - Router * leader; + Router *router; mAddressSolicitPending = false; @@ -3838,46 +3482,69 @@ void MleRouter::HandleAddressSolicitResponse(Coap::Message * aMessage, SetRouterId(routerId); SetStateRouter(Rloc16FromRouterId(mRouterId)); - mRouterTable.Clear(); + + // We keep the router table next hop and cost as what we had as a + // REED, i.e., our parent was the next hop towards all other + // routers and we tracked its cost towards them. As FED, we may + // have established links with a subset of neighboring routers. + // We ensure to clear these links to avoid using them (since will + // be rejected by the neighbor). + + mRouterTable.ClearNeighbors(); + mRouterTable.UpdateRouterIdSet(routerMaskTlv.GetIdSequence(), routerMaskTlv.GetAssignedRouterIdMask()); - router = mRouterTable.GetRouter(routerId); + router = mRouterTable.FindRouterById(routerId); VerifyOrExit(router != nullptr); - router->SetExtAddress(Get().GetExtAddress()); - router->SetCost(0); + router->SetNextHopToInvalid(); - router = mRouterTable.GetRouter(mParent.GetRouterId()); - VerifyOrExit(router != nullptr); + // Ensure we have our parent as a neighboring router, copying the + // `mParent` entry. - // Keep link to the parent in order to respond to Parent Requests before new link is established. - *router = mParent; + router = mRouterTable.FindRouterById(mParent.GetRouterId()); + VerifyOrExit(router != nullptr); + router->SetFrom(mParent); router->SetState(Neighbor::kStateValid); - router->SetNextHop(kInvalidRouterId); - router->SetCost(0); - - leader = mRouterTable.GetLeader(); - OT_ASSERT(leader != nullptr); + router->SetNextHopToInvalid(); - if (leader != router) + // Ensure we have a next hop and cost towards leader. + if (mRouterTable.GetPathCostToLeader() >= kMaxRouteCost) { - // Keep route path to the Leader reported by the parent before it is updated. - leader->SetCost(mParentLeaderCost); - leader->SetNextHop(RouterIdFromRloc16(mParent.GetRloc16())); + Router *leader = mRouterTable.GetLeader(); + + OT_ASSERT(leader != nullptr); + leader->SetNextHopAndCost(RouterIdFromRloc16(mParent.GetRloc16()), mParent.GetLeaderCost()); } + // We send an Advertisement to inform our former parent of our + // newly allocated Router ID. This will cause the parent to + // reset its advertisement trickle timer which can help speed + // up the dissemination of the new Router ID to other routers. + // This can also help with quicker link establishment with our + // former parent and other routers. + SendAdvertisement(); + for (Child &child : Get().Iterate(Child::kInStateChildIdRequest)) { IgnoreError(SendChildIdResponse(child)); } - mLinkRequestDelay = kMulticastLinkRequestDelay; - exit: // Send announce after received address solicit reply if needed InformPreviousChannel(); } +Error MleRouter::SetChildRouterLinks(uint8_t aChildRouterLinks) +{ + Error error = kErrorNone; + + VerifyOrExit(IsDisabled(), error = kErrorInvalidState); + mChildRouterLinks = aChildRouterLinks; +exit: + return error; +} + bool MleRouter::IsExpectedToBecomeRouterSoon(void) const { static constexpr uint8_t kMaxDelay = 10; @@ -3887,20 +3554,17 @@ bool MleRouter::IsExpectedToBecomeRouterSoon(void) const mAddressSolicitPending); } -void MleRouter::HandleAddressSolicit(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleAddressSolicit(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void MleRouter::HandleAddressSolicit(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> void MleRouter::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { Error error = kErrorNone; ThreadStatusTlv::Status responseStatus = ThreadStatusTlv::kNoAddressAvailable; - Router * router = nullptr; + Router *router = nullptr; Mac::ExtAddress extAddress; uint16_t rloc16; uint8_t status; + VerifyOrExit(mRole == kRoleLeader, error = kErrorInvalidState); + VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorParse); Log(kMessageReceive, kTypeAddressSolicit, aMessageInfo.GetPeerAddr()); @@ -3929,7 +3593,7 @@ void MleRouter::HandleAddressSolicit(Coap::Message &aMessage, const Ip6::Message #endif // Check if allocation already exists - router = mRouterTable.GetRouter(extAddress); + router = mRouterTable.FindRouter(extAddress); if (router != nullptr) { @@ -3952,7 +3616,7 @@ void MleRouter::HandleAddressSolicit(Coap::Message &aMessage, const Ip6::Message (Get().CountBorderRouters(NetworkData::kRouterRoleOnly) >= kRouterUpgradeBorderRouterRequestThreshold)) { - LogInfo("Rejecting BR %s router role req - have %d BR routers", extAddress.ToString().AsCString(), + LogInfo("Rejecting BR %s router role req - have %u BR routers", extAddress.ToString().AsCString(), kRouterUpgradeBorderRouterRequestThreshold); ExitNow(); } @@ -3969,7 +3633,7 @@ void MleRouter::HandleAddressSolicit(Coap::Message &aMessage, const Ip6::Message if (router != nullptr) { - LogInfo("Router id %d requested and provided!", RouterIdFromRloc16(rloc16)); + LogInfo("Router id %u requested and provided!", RouterIdFromRloc16(rloc16)); } } @@ -3989,9 +3653,9 @@ void MleRouter::HandleAddressSolicit(Coap::Message &aMessage, const Ip6::Message } } -void MleRouter::SendAddressSolicitResponse(const Coap::Message & aRequest, +void MleRouter::SendAddressSolicitResponse(const Coap::Message &aRequest, ThreadStatusTlv::Status aResponseStatus, - const Router * aRouter, + const Router *aRouter, const Ip6::MessageInfo &aMessageInfo) { Coap::Message *message = Get().NewPriorityResponseMessage(aRequest); @@ -4008,7 +3672,7 @@ void MleRouter::SendAddressSolicitResponse(const Coap::Message & aRequest, routerMaskTlv.Init(); routerMaskTlv.SetIdSequence(mRouterTable.GetRouterIdSequence()); - routerMaskTlv.SetAssignedRouterIdMask(mRouterTable.GetRouterIdSet()); + mRouterTable.GetRouterIdSet(routerMaskTlv.GetAssignedRouterIdMask()); SuccessOrExit(routerMaskTlv.AppendTo(*message)); } @@ -4018,21 +3682,33 @@ void MleRouter::SendAddressSolicitResponse(const Coap::Message & aRequest, Log(kMessageSend, kTypeAddressReply, aMessageInfo.GetPeerAddr()); + // If assigning a new RLOC16 (e.g., on promotion of a child to + // router role) we clear any address cache entries associated + // with the old RLOC16. + + if ((aResponseStatus == ThreadStatusTlv::kSuccess) && (aRouter != nullptr)) + { + uint16_t oldRloc16; + + VerifyOrExit(IsRoutingLocator(aMessageInfo.GetPeerAddr())); + oldRloc16 = aMessageInfo.GetPeerAddr().GetIid().GetLocator(); + + VerifyOrExit(oldRloc16 != aRouter->GetRloc16()); + Get().RemoveEntriesForRloc16(oldRloc16); + } + exit: FreeMessage(message); } -void MleRouter::HandleAddressRelease(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleAddressRelease(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void MleRouter::HandleAddressRelease(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> void MleRouter::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { uint16_t rloc16; Mac::ExtAddress extAddress; uint8_t routerId; - Router * router; + Router *router; + + VerifyOrExit(mRole == kRoleLeader); VerifyOrExit(aMessage.IsConfirmablePostRequest()); @@ -4042,7 +3718,7 @@ void MleRouter::HandleAddressRelease(Coap::Message &aMessage, const Ip6::Message SuccessOrExit(Tlv::Find(aMessage, extAddress)); routerId = RouterIdFromRloc16(rloc16); - router = mRouterTable.GetRouter(routerId); + router = mRouterTable.FindRouterById(routerId); VerifyOrExit((router != nullptr) && (router->GetExtAddress() == extAddress)); @@ -4058,9 +3734,7 @@ void MleRouter::HandleAddressRelease(Coap::Message &aMessage, const Ip6::Message void MleRouter::FillConnectivityTlv(ConnectivityTlv &aTlv) { - Router *leader; - uint8_t cost; - int8_t parentPriority = kParentPriorityMedium; + int8_t parentPriority = kParentPriorityMedium; if (mParentPriority != kParentPriorityUnspecified) { @@ -4083,67 +3757,17 @@ void MleRouter::FillConnectivityTlv(ConnectivityTlv &aTlv) aTlv.SetParentPriority(parentPriority); - // compute leader cost and link qualities aTlv.SetLinkQuality1(0); aTlv.SetLinkQuality2(0); aTlv.SetLinkQuality3(0); - leader = mRouterTable.GetLeader(); - cost = (leader != nullptr) ? leader->GetCost() : static_cast(kMaxRouteCost); - - switch (mRole) + if (IsChild()) { - case kRoleDisabled: - case kRoleDetached: - cost = static_cast(kMaxRouteCost); - break; - - case kRoleChild: - switch (mParent.GetLinkInfo().GetLinkQuality()) - { - case kLinkQuality0: - break; - - case kLinkQuality1: - aTlv.SetLinkQuality1(aTlv.GetLinkQuality1() + 1); - break; - - case kLinkQuality2: - aTlv.SetLinkQuality2(aTlv.GetLinkQuality2() + 1); - break; - - case kLinkQuality3: - aTlv.SetLinkQuality3(aTlv.GetLinkQuality3() + 1); - break; - } - - cost += LinkQualityToCost(mParent.GetLinkInfo().GetLinkQuality()); - break; - - case kRoleRouter: - if (leader != nullptr) - { - cost += GetLinkCost(leader->GetNextHop()); - - if (!IsRouterIdValid(leader->GetNextHop()) || GetLinkCost(GetLeaderId()) < cost) - { - cost = GetLinkCost(GetLeaderId()); - } - } - - break; - - case kRoleLeader: - cost = 0; - break; + aTlv.IncrementLinkQuality(mParent.GetLinkQualityIn()); } - aTlv.SetActiveRouters(mRouterTable.GetActiveRouterCount()); - - for (Router &router : Get().Iterate()) + for (const Router &router : Get()) { - LinkQuality linkQuality; - if (router.GetRloc16() == GetRloc16()) { // skip self @@ -4156,223 +3780,135 @@ void MleRouter::FillConnectivityTlv(ConnectivityTlv &aTlv) continue; } - linkQuality = router.GetLinkInfo().GetLinkQuality(); - - if (linkQuality > router.GetLinkQualityOut()) - { - linkQuality = router.GetLinkQualityOut(); - } - - switch (linkQuality) - { - case kLinkQuality0: - break; - - case kLinkQuality1: - aTlv.SetLinkQuality1(aTlv.GetLinkQuality1() + 1); - break; - - case kLinkQuality2: - aTlv.SetLinkQuality2(aTlv.GetLinkQuality2() + 1); - break; - - case kLinkQuality3: - aTlv.SetLinkQuality3(aTlv.GetLinkQuality3() + 1); - break; - } + aTlv.IncrementLinkQuality(router.GetTwoWayLinkQuality()); } - aTlv.SetLeaderCost((cost < kMaxRouteCost) ? cost : static_cast(kMaxRouteCost)); + aTlv.SetActiveRouters(mRouterTable.GetActiveRouterCount()); + aTlv.SetLeaderCost(Min(mRouterTable.GetPathCostToLeader(), kMaxRouteCost)); aTlv.SetIdSequence(mRouterTable.GetRouterIdSequence()); aTlv.SetSedBufferSize(OPENTHREAD_CONFIG_DEFAULT_SED_BUFFER_SIZE); aTlv.SetSedDatagramCount(OPENTHREAD_CONFIG_DEFAULT_SED_DATAGRAM_COUNT); } -void MleRouter::FillRouteTlv(RouteTlv &aTlv, Neighbor *aNeighbor) +bool MleRouter::ShouldDowngrade(uint8_t aNeighborId, const RouteTlv &aRouteTlv) const { - uint8_t routerIdSequence = mRouterTable.GetRouterIdSequence(); - RouterIdSet routerIdSet = mRouterTable.GetRouterIdSet(); - uint8_t routerCount; - - if (aNeighbor && IsActiveRouter(aNeighbor->GetRloc16())) - { - // Sending a Link Accept message that may require truncation - // of Route64 TLV + // Determine whether all conditions are satisfied for the router + // to downgrade after receiving info for a neighboring router + // with Router ID `aNeighborId` along with its `aRouteTlv`. - routerCount = mRouterTable.GetActiveRouterCount(); + bool shouldDowngrade = false; + uint8_t activeRouterCount = mRouterTable.GetActiveRouterCount(); + uint8_t count; - if (routerCount > kLinkAcceptMaxRouters) - { - for (uint8_t routerId = 0; routerId <= kMaxRouterId; routerId++) - { - if (routerCount <= kLinkAcceptMaxRouters) - { - break; - } + VerifyOrExit(IsRouter()); + VerifyOrExit(mRouterTable.IsAllocated(aNeighborId)); - if ((routerId == RouterIdFromRloc16(GetRloc16())) || (routerId == aNeighbor->GetRouterId()) || - (routerId == GetLeaderId())) - { - // Route64 TLV must contain this device and the - // neighboring router to ensure that at least this - // link can be established. - continue; - } + // `mRouterSelectionJitterTimeout` is non-zero if we are already + // waiting to downgrade. - if (routerIdSet.Contains(routerId)) - { - routerIdSet.Remove(routerId); - routerCount--; - } - } + VerifyOrExit(mRouterSelectionJitterTimeout == 0); - // Ensure that the neighbor will process the current - // Route64 TLV in a subsequent message exchange - routerIdSequence -= kLinkAcceptSequenceRollback; - } - } + VerifyOrExit(activeRouterCount > mRouterDowngradeThreshold); - aTlv.SetRouterIdSequence(routerIdSequence); - aTlv.SetRouterIdMask(routerIdSet); + // Check that we have at least `kMinDowngradeNeighbors` + // neighboring routers with two-way link quality of 2 or better. - routerCount = 0; + count = 0; - for (Router &router : Get().Iterate()) + for (const Router &router : mRouterTable) { - if (!routerIdSet.Contains(router.GetRouterId())) + if (!router.IsStateValid() || (router.GetTwoWayLinkQuality() < kLinkQuality2)) { continue; } - if (router.GetRloc16() == GetRloc16()) - { - aTlv.SetLinkQualityIn(routerCount, kLinkQuality0); - aTlv.SetLinkQualityOut(routerCount, kLinkQuality0); - aTlv.SetRouteCost(routerCount, 1); - } - else - { - Router *nextHop; - uint8_t linkCost; - uint8_t routeCost; - - linkCost = mRouterTable.GetLinkCost(router); - nextHop = mRouterTable.GetRouter(router.GetNextHop()); - - if (nextHop == nullptr) - { - routeCost = linkCost; - } - else - { - routeCost = router.GetCost() + mRouterTable.GetLinkCost(*nextHop); - - if (linkCost < routeCost) - { - routeCost = linkCost; - } - } - - if (routeCost >= kMaxRouteCost) - { - routeCost = 0; - } + count++; - aTlv.SetRouteCost(routerCount, routeCost); - aTlv.SetLinkQualityOut(routerCount, router.GetLinkQualityOut()); - aTlv.SetLinkQualityIn(routerCount, router.GetLinkInfo().GetLinkQuality()); + if (count >= kMinDowngradeNeighbors) + { + break; } - - routerCount++; } - aTlv.SetRouteDataLength(routerCount); -} + VerifyOrExit(count >= kMinDowngradeNeighbors); -bool MleRouter::HasMinDowngradeNeighborRouters(void) -{ - uint8_t linkQuality; - uint8_t routerCount = 0; + // Check that we have fewer children than three times the number + // of excess routers (defined as the difference between number of + // active routers and `mRouterDowngradeThreshold`). - for (Router &router : Get().Iterate()) - { - if (!router.IsStateValid()) - { - continue; - } + count = activeRouterCount - mRouterDowngradeThreshold; + VerifyOrExit(mChildTable.GetNumChildren(Child::kInStateValid) < count * 3); - linkQuality = router.GetLinkInfo().GetLinkQuality(); + // Check that the neighbor has as good or better-quality links to + // same routers. - if (linkQuality > router.GetLinkQualityOut()) - { - linkQuality = router.GetLinkQualityOut(); - } + VerifyOrExit(NeighborHasComparableConnectivity(aRouteTlv, aNeighborId)); - if (linkQuality >= 2) - { - routerCount++; - } - } +#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE + // Check if we are eligible to be router due to being a BR. + VerifyOrExit(!Get().IsEligibleForRouterRoleUpgradeAsBorderRouter()); +#endif - return routerCount >= kMinDowngradeNeighbors; + shouldDowngrade = true; + +exit: + return shouldDowngrade; } -bool MleRouter::HasOneNeighborWithComparableConnectivity(const RouteTlv &aRoute, uint8_t aRouterId) +bool MleRouter::NeighborHasComparableConnectivity(const RouteTlv &aRouteTlv, uint8_t aNeighborId) const { - uint8_t routerCount = 0; - bool rval = true; + // Check whether the neighboring router with Router ID `aNeighborId` + // (along with its `aRouteTlv`) has as good or better-quality links + // to all our neighboring routers which have a two-way link quality + // of two or better. - // process local neighbor routers - for (Router &router : Get().Iterate()) + bool isComparable = true; + + for (uint8_t routerId = 0, index = 0; routerId <= kMaxRouterId; + index += aRouteTlv.IsRouterIdSet(routerId) ? 1 : 0, routerId++) { - uint8_t localLinkQuality; - uint8_t peerLinkQuality; + const Router *router; + LinkQuality localLinkQuality; + LinkQuality peerLinkQuality; - if (!router.IsStateValid() || router.GetRouterId() == mRouterId || router.GetRouterId() == aRouterId) + if ((routerId == mRouterId) || (routerId == aNeighborId)) { - routerCount++; continue; } - localLinkQuality = router.GetLinkInfo().GetLinkQuality(); - - if (localLinkQuality > router.GetLinkQualityOut()) - { - localLinkQuality = router.GetLinkQualityOut(); - } + router = mRouterTable.FindRouterById(routerId); - if (localLinkQuality < 2) + if ((router == nullptr) || !router->IsStateValid()) { - routerCount++; continue; } - // check if this neighbor router is in peer Route64 TLV - if (!aRoute.IsRouterIdSet(router.GetRouterId())) + localLinkQuality = router->GetTwoWayLinkQuality(); + + if (localLinkQuality < kLinkQuality2) { - ExitNow(rval = false); + continue; } - // get the peer's two-way link quality to this router - peerLinkQuality = aRoute.GetLinkQualityIn(routerCount); + // `router` is our neighbor with two-way link quality of + // at least two. Check that `aRouteTlv` has as good or + // better-quality link to it as well. - if (peerLinkQuality > aRoute.GetLinkQualityOut(routerCount)) + if (!aRouteTlv.IsRouterIdSet(routerId)) { - peerLinkQuality = aRoute.GetLinkQualityOut(routerCount); + ExitNow(isComparable = false); } - // compare local link quality to this router with peer's - if (peerLinkQuality >= localLinkQuality) + peerLinkQuality = Min(aRouteTlv.GetLinkQualityIn(index), aRouteTlv.GetLinkQualityOut(index)); + + if (peerLinkQuality < localLinkQuality) { - routerCount++; - continue; + ExitNow(isComparable = false); } - - ExitNow(rval = false); } exit: - return rval; + return isComparable; } void MleRouter::SetChildStateToValid(Child &aChild) @@ -4382,7 +3918,7 @@ void MleRouter::SetChildStateToValid(Child &aChild) aChild.SetState(Neighbor::kStateValid); IgnoreError(mChildTable.StoreChild(aChild)); -#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE +#if OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE Get().UpdateProxiedSubscriptions(aChild, nullptr, 0); #endif @@ -4392,10 +3928,7 @@ void MleRouter::SetChildStateToValid(Child &aChild) return; } -bool MleRouter::HasChildren(void) -{ - return mChildTable.HasChildren(Child::kInStateValidOrAttaching); -} +bool MleRouter::HasChildren(void) { return mChildTable.HasChildren(Child::kInStateValidOrAttaching); } void MleRouter::RemoveChildren(void) { @@ -4405,21 +3938,6 @@ void MleRouter::RemoveChildren(void) } } -bool MleRouter::HasSmallNumberOfChildren(void) -{ - uint16_t numChildren = 0; - uint8_t routerCount = mRouterTable.GetActiveRouterCount(); - - VerifyOrExit(routerCount > mRouterDowngradeThreshold); - - numChildren = mChildTable.GetNumChildren(Child::kInStateValid); - - return numChildren < (routerCount - mRouterDowngradeThreshold) * 3; - -exit: - return false; -} - Error MleRouter::SetAssignParentPriority(int8_t aParentPriority) { Error error = kErrorNone; @@ -4465,7 +3983,7 @@ void MleRouter::HandleTimeSync(RxInfo &aRxInfo) { Log(kMessageReceive, kTypeTimeSync, aRxInfo.mMessageInfo.GetPeerAddr()); - VerifyOrExit(aRxInfo.mNeighbor && aRxInfo.mNeighbor->IsStateValid()); + VerifyOrExit(aRxInfo.IsNeighborStateValid()); aRxInfo.mClass = RxInfo::kPeerMessage; @@ -4479,7 +3997,7 @@ Error MleRouter::SendTimeSync(void) { Error error = kErrorNone; Ip6::Address destination; - TxMessage * message = nullptr; + TxMessage *message = nullptr; VerifyOrExit((message = NewMleMessage(kCommandTimeSync)) != nullptr, error = kErrorNoBufs); diff --git a/src/core/thread/mle_router.hpp b/src/core/thread/mle_router.hpp index e0c7778c462..ac84f757f73 100644 --- a/src/core/thread/mle_router.hpp +++ b/src/core/thread/mle_router.hpp @@ -38,8 +38,8 @@ #include -#include "coap/coap.hpp" #include "coap/coap_message.hpp" +#include "common/callback.hpp" #include "common/time_ticker.hpp" #include "common/timer.hpp" #include "common/trickle_timer.hpp" @@ -52,6 +52,7 @@ #include "thread/mle_tlvs.hpp" #include "thread/router_table.hpp" #include "thread/thread_tlvs.hpp" +#include "thread/tmf.hpp" #include "thread/topology.hpp" namespace ot { @@ -77,6 +78,7 @@ class MleRouter : public Mle friend class Mle; friend class ot::Instance; friend class ot::TimeTicker; + friend class Tmf::Agent; public: /** @@ -117,7 +119,7 @@ class MleRouter : public Mle * @retval FALSE It is a child or is not a single router in the network. * */ - bool IsSingleton(void); + bool IsSingleton(void) const; /** * This method generates an Address Solicit request for a Router ID. @@ -141,6 +143,22 @@ class MleRouter : public Mle */ Error BecomeLeader(void); + /** + * This method gets the device properties which are used to determine the Leader Weight. + * + * @returns The current device properties. + * + */ + const DeviceProperties &GetDeviceProperties(void) const { return mDeviceProperties; } + + /** + * This method sets the device properties which are then used to determine and set the Leader Weight. + * + * @param[in] aDeviceProperties The device properties. + * + */ + void SetDeviceProperties(const DeviceProperties &aDeviceProperties); + /** * This method returns the Leader Weighting value for this Thread interface. * @@ -152,6 +170,9 @@ class MleRouter : public Mle /** * This method sets the Leader Weighting value for this Thread interface. * + * This method directly sets the Leader Weight to the new value replacing its previous value (which may have been + * determined from a previous call to `SetDeviceProperties()`). + * * @param[in] aWeight The Leader Weighting value. * */ @@ -220,7 +241,7 @@ class MleRouter : public Mle * @returns A RLOC16 of the next hop if a route is known, kInvalidRloc16 otherwise. * */ - uint16_t GetNextHop(uint16_t aDestination); + uint16_t GetNextHop(uint16_t aDestination) { return mRouterTable.GetNextHop(aDestination); } /** * This method returns the NETWORK_ID_TIMEOUT value. @@ -238,36 +259,6 @@ class MleRouter : public Mle */ void SetNetworkIdTimeout(uint8_t aTimeout) { mNetworkIdTimeout = aTimeout; } - /** - * This method returns the route cost to a RLOC16. - * - * @param[in] aRloc16 The RLOC16 of the destination. - * - * @returns The route cost to a RLOC16. - * - */ - uint8_t GetRouteCost(uint16_t aRloc16) const; - - /** - * This method returns the link cost to the given Router. - * - * @param[in] aRouterId The Router ID. - * - * @returns The link cost to the Router. - * - */ - uint8_t GetLinkCost(uint8_t aRouterId); - - /** - * This method returns the minimum cost to the given router. - * - * @param[in] aRloc16 The short address of the given router. - * - * @returns The minimum cost to the given router (via direct link or forwarding). - * - */ - uint8_t GetCost(uint16_t aRloc16); - /** * This method returns the ROUTER_SELECTION_JITTER value. * @@ -324,6 +315,24 @@ class MleRouter : public Mle */ void SetRouterDowngradeThreshold(uint8_t aThreshold) { mRouterDowngradeThreshold = aThreshold; } + /** + * This method returns the MLE_CHILD_ROUTER_LINKS value. + * + * @returns The MLE_CHILD_ROUTER_LINKS value. + * + */ + uint8_t GetChildRouterLinks(void) const { return mChildRouterLinks; } + + /** + * This method sets the MLE_CHILD_ROUTER_LINKS value. + * + * @param[in] aChildRouterLinks The MLE_CHILD_ROUTER_LINKS value. + * + * @retval kErrorNone Successfully set the value. + * @retval kErrorInvalidState Thread protocols are enabled. + */ + Error SetChildRouterLinks(uint8_t aChildRouterLinks); + /** * This method returns if the REED is expected to become Router soon. * @@ -418,14 +427,6 @@ class MleRouter : public Mle */ void FillConnectivityTlv(ConnectivityTlv &aTlv); - /** - * This method fills an RouteTlv. - * - * @param[out] aTlv A reference to the tlv to be filled. - * - */ - void FillRouteTlv(RouteTlv &aTlv, Neighbor *aNeighbor = nullptr); - /** * This method generates an MLE Child Update Request message to be sent to the parent. * @@ -490,8 +491,7 @@ class MleRouter : public Mle */ void SetDiscoveryRequestCallback(otThreadDiscoveryRequestCallback aCallback, void *aContext) { - mDiscoveryRequestCallback = aCallback; - mDiscoveryRequestCallbackContext = aContext; + mDiscoveryRequestCallback.Set(aCallback, aContext); } /** @@ -500,16 +500,6 @@ class MleRouter : public Mle */ void ResetAdvertiseInterval(void); - /** - * This static method converts link quality to route cost. - * - * @param[in] aLinkQuality The link quality. - * - * @returns The link cost corresponding to @p aLinkQuality. - * - */ - static uint8_t LinkQualityToCost(uint8_t aLinkQuality); - #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE /** * This method generates an MLE Time Synchronization message. @@ -570,32 +560,33 @@ class MleRouter : public Mle void SetThreadVersionCheckEnabled(bool aEnabled) { mThreadVersionCheckEnabled = aEnabled; } #endif - /** - * This function sends an Address Release. - * - * @param[in] aResponseHandler A pointer to a function that is called upon response reception or time-out. - * @param[in] aResponseHandlerContext A pointer to callback application-specific context. - * - */ - void SendAddressRelease(Coap::ResponseHandler aResponseHandler = nullptr, void *aResponseHandlerContext = nullptr); - private: - static constexpr uint16_t kDiscoveryMaxJitter = 250; // Max jitter delay Discovery Responses (in msec). - static constexpr uint32_t kStateUpdatePeriod = 1000; // State update period (in msec). - static constexpr uint16_t kUnsolicitedDataResponseJitter = 500; // Max delay for unsol Data Response (in msec). + static constexpr uint16_t kDiscoveryMaxJitter = 250; // Max jitter delay Discovery Responses (in msec). + static constexpr uint16_t kChallengeTimeout = 2; // Challenge timeout (in sec). + static constexpr uint16_t kUnsolicitedDataResponseJitter = 500; // Max delay for unsol Data Response (in msec). // Threshold to accept a router upgrade request with reason // `kBorderRouterRequest` (number of BRs acting as router in // Network Data). static constexpr uint8_t kRouterUpgradeBorderRouterRequestThreshold = 2; + static constexpr uint8_t kLinkRequestMinMargin = OPENTHREAD_CONFIG_MLE_LINK_REQUEST_MARGIN_MIN; + static constexpr uint8_t kPartitionMergeMinMargin = OPENTHREAD_CONFIG_MLE_PARTITION_MERGE_MARGIN_MIN; + static constexpr uint8_t kChildRouterLinks = OPENTHREAD_CONFIG_MLE_CHILD_ROUTER_LINKS; + static constexpr uint8_t kMaxChildIpAddresses = OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD; + + static constexpr uint8_t kMinCriticalChildrenCount = 6; + + static constexpr uint16_t kChildSupervisionDefaultIntervalForOlderVersion = + OPENTHREAD_CONFIG_CHILD_SUPERVISION_OLDER_VERSION_CHILD_DEFAULT_INTERVAL; + void HandleDetachStart(void); void HandleChildStart(AttachMode aMode); void HandleLinkRequest(RxInfo &aRxInfo); void HandleLinkAccept(RxInfo &aRxInfo); Error HandleLinkAccept(RxInfo &aRxInfo, bool aRequest); void HandleLinkAcceptAndRequest(RxInfo &aRxInfo); - Error HandleAdvertisement(RxInfo &aRxInfo); + Error HandleAdvertisement(RxInfo &aRxInfo, uint16_t aSourceAddress, const LeaderData &aLeaderData); void HandleParentRequest(RxInfo &aRxInfo); void HandleChildIdRequest(RxInfo &aRxInfo); void HandleChildUpdateRequest(RxInfo &aRxInfo); @@ -607,62 +598,57 @@ class MleRouter : public Mle void HandleTimeSync(RxInfo &aRxInfo); #endif - Error ProcessRouteTlv(RxInfo &aRxInfo); - Error ProcessRouteTlv(RxInfo &aRxInfo, RouteTlv &aRouteTlv); + Error ProcessRouteTlv(const RouteTlv &aRouteTlv, RxInfo &aRxInfo); + Error ReadAndProcessRouteTlvOnFed(RxInfo &aRxInfo, uint8_t aParentId); + void StopAdvertiseTrickleTimer(void); Error SendAddressSolicit(ThreadStatusTlv::Status aStatus); - void SendAddressSolicitResponse(const Coap::Message & aRequest, + void SendAddressSolicitResponse(const Coap::Message &aRequest, ThreadStatusTlv::Status aResponseStatus, - const Router * aRouter, + const Router *aRouter, const Ip6::MessageInfo &aMessageInfo); + void SendAddressRelease(void); void SendAdvertisement(void); Error SendLinkAccept(const Ip6::MessageInfo &aMessageInfo, - Neighbor * aNeighbor, - const RequestedTlvs & aRequestedTlvs, - const Challenge & aChallenge); + Neighbor *aNeighbor, + const TlvList &aRequestedTlvList, + const Challenge &aChallenge); void SendParentResponse(Child *aChild, const Challenge &aChallenge, bool aRoutersOnlyRequest); Error SendChildIdResponse(Child &aChild); Error SendChildUpdateRequest(Child &aChild); - void SendChildUpdateResponse(Child * aChild, + void SendChildUpdateResponse(Child *aChild, const Ip6::MessageInfo &aMessageInfo, - const uint8_t * aTlvs, - uint8_t aTlvsLength, - const Challenge & aChallenge); + const TlvList &aTlvList, + const Challenge &aChallenge); void SendDataResponse(const Ip6::Address &aDestination, - const uint8_t * aTlvs, - uint8_t aTlvsLength, + const TlvList &aTlvList, uint16_t aDelay, - const Message * aRequestMessage = nullptr); + const Message *aRequestMessage = nullptr); Error SendDiscoveryResponse(const Ip6::Address &aDestination, const Message &aDiscoverRequestMessage); void SetStateRouter(uint16_t aRloc16); void SetStateLeader(uint16_t aRloc16, LeaderStartMode aStartMode); + void SetStateRouterOrLeader(DeviceRole aRole, uint16_t aRloc16, LeaderStartMode aStartMode); void StopLeader(void); void SynchronizeChildNetworkData(void); - Error UpdateChildAddresses(const Message &aMessage, uint16_t aOffset, Child &aChild); - void UpdateRoutes(const RouteTlv &aRoute, uint8_t aRouterId); - bool UpdateLinkQualityOut(const RouteTlv &aRoute, Router &aNeighbor, bool &aResetAdvInterval); + Error ProcessAddressRegistrationTlv(RxInfo &aRxInfo, Child &aChild); + Error UpdateChildAddresses(const Message &aMessage, uint16_t aOffset, uint16_t aLength, Child &aChild); bool HasNeighborWithGoodLinkQuality(void) const; - static void HandleAddressSolicitResponse(void * aContext, - otMessage * aMessage, + static void HandleAddressSolicitResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); void HandleAddressSolicitResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); - static void HandleAddressRelease(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleAddressRelease(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleAddressSolicit(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleAddressSolicit(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static bool IsSingleton(const RouteTlv &aRouteTlv); + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); void HandlePartitionChange(void); void SetChildStateToValid(Child &aChild); bool HasChildren(void); void RemoveChildren(void); - bool HasMinDowngradeNeighborRouters(void); - bool HasOneNeighborWithComparableConnectivity(const RouteTlv &aRoute, uint8_t aRouterId); - bool HasSmallNumberOfChildren(void); + bool ShouldDowngrade(uint8_t aNeighborId, const RouteTlv &aRouteTlv) const; + bool NeighborHasComparableConnectivity(const RouteTlv &aRouteTlv, uint8_t aNeighborId) const; static void HandleAdvertiseTrickleTimer(TrickleTimer &aTimer); void HandleAdvertiseTrickleTimer(void); @@ -670,8 +656,7 @@ class MleRouter : public Mle TrickleTimer mAdvertiseTrickleTimer; - Coap::Resource mAddressSolicit; - Coap::Resource mAddressRelease; + DeviceProperties mDeviceProperties; ChildTable mChildTable; RouterTable mRouterTable; @@ -704,7 +689,7 @@ class MleRouter : public Mle uint8_t mRouterSelectionJitter; ///< The variable to save the assigned jitter value. uint8_t mRouterSelectionJitterTimeout; ///< The Timeout prior to request/release Router ID. - uint8_t mLinkRequestDelay; + uint8_t mChildRouterLinks; int8_t mParentPriority; ///< The assigned parent priority value, -2 means not assigned. #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE @@ -718,10 +703,12 @@ class MleRouter : public Mle MeshCoP::SteeringData mSteeringData; #endif - otThreadDiscoveryRequestCallback mDiscoveryRequestCallback; - void * mDiscoveryRequestCallbackContext; + Callback mDiscoveryRequestCallback; }; +DeclareTmfHandler(MleRouter, kUriAddressSolicit); +DeclareTmfHandler(MleRouter, kUriAddressRelease); + #endif // OPENTHREAD_FTD #if OPENTHREAD_MTD @@ -741,8 +728,6 @@ class MleRouter : public Mle uint16_t GetNextHop(uint16_t aDestination) const { return Mle::GetNextHop(aDestination); } - uint8_t GetCost(uint16_t) { return 0; } - Error RemoveNeighbor(Neighbor &) { return BecomeDetached(); } void RemoveRouterLink(Router &) { IgnoreError(BecomeDetached()); } diff --git a/src/core/thread/mle_tlvs.cpp b/src/core/thread/mle_tlvs.cpp new file mode 100644 index 00000000000..1908d61d48f --- /dev/null +++ b/src/core/thread/mle_tlvs.cpp @@ -0,0 +1,98 @@ +/* + * 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 function for generating and processing MLE TLVs. + */ + +#include "mle_tlvs.hpp" + +#include "common/code_utils.hpp" + +namespace ot { +namespace Mle { + +#if !OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE + +void RouteTlv::Init(void) +{ + SetType(kRoute); + SetLength(sizeof(*this) - sizeof(Tlv)); + mRouterIdMask.Clear(); + memset(mRouteData, 0, sizeof(mRouteData)); +} + +bool RouteTlv::IsValid(void) const +{ + bool isValid = false; + uint8_t numAllocatedIds; + + VerifyOrExit(GetLength() >= sizeof(mRouterIdSequence) + sizeof(mRouterIdMask)); + + numAllocatedIds = mRouterIdMask.GetNumberOfAllocatedIds(); + VerifyOrExit(numAllocatedIds <= Mle::kMaxRouters); + + isValid = (GetRouteDataLength() >= numAllocatedIds); + +exit: + return isValid; +} + +#endif // #if !OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE + +void ConnectivityTlv::IncrementLinkQuality(LinkQuality aLinkQuality) +{ + switch (aLinkQuality) + { + case kLinkQuality0: + break; + case kLinkQuality1: + mLinkQuality1++; + break; + case kLinkQuality2: + mLinkQuality2++; + break; + case kLinkQuality3: + mLinkQuality3++; + break; + } +} + +int8_t ConnectivityTlv::GetParentPriority(void) const +{ + return Preference::From2BitUint(mFlags >> kFlagsParentPriorityOffset); +} + +void ConnectivityTlv::SetParentPriority(int8_t aParentPriority) +{ + mFlags = static_cast(Preference::To2BitUint(aParentPriority) << kFlagsParentPriorityOffset); +} + +} // namespace Mle +} // namespace ot diff --git a/src/core/thread/mle_tlvs.hpp b/src/core/thread/mle_tlvs.hpp index 4b1dd6725e7..7449965b9ac 100644 --- a/src/core/thread/mle_tlvs.hpp +++ b/src/core/thread/mle_tlvs.hpp @@ -38,6 +38,7 @@ #include "common/encoding.hpp" #include "common/message.hpp" +#include "common/preference.hpp" #include "common/tlvs.hpp" #include "meshcop/timestamp.hpp" #include "net/ip6_address.hpp" @@ -102,6 +103,7 @@ class Tlv : public ot::Tlv kActiveDataset = 24, ///< Active Operational Dataset TLV kPendingDataset = 25, ///< Pending Operational Dataset TLV kDiscovery = 26, ///< Thread Discovery TLV + kSupervisionInterval = 27, ///< Supervision Interval TLV kCslChannel = 80, ///< CSL Channel TLV kCslTimeout = 85, ///< CSL Timeout TLV kCslClockAccuracy = 86, ///< CSL Clock Accuracy TLV @@ -230,6 +232,12 @@ typedef SimpleTlvInfo ActiveTimestamp */ typedef SimpleTlvInfo PendingTimestampTlv; +/** + * This class defines Timeout TLV constants and types. + * + */ +typedef UintTlvInfo SupervisionIntervalTlv; + /** * This class defines CSL Timeout TLV constants and types. * @@ -256,13 +264,7 @@ class RouteTlv : public Tlv, public TlvInfo * This method initializes the TLV. * */ - void Init(void) - { - SetType(kRoute); - SetLength(sizeof(*this) - sizeof(Tlv)); - mRouterIdMask.Clear(); - memset(mRouteData, 0, sizeof(mRouteData)); - } + void Init(void); /** * This method indicates whether or not the TLV appears to be well-formed. @@ -271,7 +273,7 @@ class RouteTlv : public Tlv, public TlvInfo * @retval FALSE If the TLV does not appear to be well-formed. * */ - bool IsValid(void) const { return GetLength() >= sizeof(mRouterIdSequence) + sizeof(mRouterIdMask); } + bool IsValid(void) const; /** * This method returns the Router ID Sequence value. @@ -314,6 +316,15 @@ class RouteTlv : public Tlv, public TlvInfo */ bool IsRouterIdSet(uint8_t aRouterId) const { return mRouterIdMask.Contains(aRouterId); } + /** + * This method indicates whether the `RouteTlv` is a singleton, i.e., only one router is allocated. + * + * @retval TRUE It is a singleton. + * @retval FALSE It is not a singleton. + * + */ + bool IsSingleton(void) const { return IsValid() && (mRouterIdMask.GetNumberOfAllocatedIds() <= 1); } + /** * This method returns the Route Data Length value. * @@ -340,18 +351,6 @@ class RouteTlv : public Tlv, public TlvInfo */ uint8_t GetRouteCost(uint8_t aRouterIndex) const { return mRouteData[aRouterIndex] & kRouteCostMask; } - /** - * This method sets the Route Cost value for a given Router index. - * - * @param[in] aRouterIndex The Router index. - * @param[in] aRouteCost The Route Cost value. - * - */ - void SetRouteCost(uint8_t aRouterIndex, uint8_t aRouteCost) - { - mRouteData[aRouterIndex] = (mRouteData[aRouterIndex] & ~kRouteCostMask) | aRouteCost; - } - /** * This method returns the Link Quality In value for a given Router index. * @@ -365,19 +364,6 @@ class RouteTlv : public Tlv, public TlvInfo return static_cast((mRouteData[aRouterIndex] & kLinkQualityInMask) >> kLinkQualityInOffset); } - /** - * This method sets the Link Quality In value for a given Router index. - * - * @param[in] aRouterIndex The Router index. - * @param[in] aLinkQuality The Link Quality In value for a given Router index. - * - */ - void SetLinkQualityIn(uint8_t aRouterIndex, LinkQuality aLinkQuality) - { - mRouteData[aRouterIndex] = (mRouteData[aRouterIndex] & ~kLinkQualityInMask) | - ((aLinkQuality << kLinkQualityInOffset) & kLinkQualityInMask); - } - /** * This method returns the Link Quality Out value for a given Router index. * @@ -392,16 +378,19 @@ class RouteTlv : public Tlv, public TlvInfo } /** - * This method sets the Link Quality Out value for a given Router index. + * This method sets the Route Data (Link Quality In/Out and Route Cost) for a given Router index. * - * @param[in] aRouterIndex The Router index. - * @param[in] aLinkQuality The Link Quality Out value for a given Router index. + * @param[in] aRouterIndex The Router index. + * @param[in] aLinkQualityIn The Link Quality In value. + * @param[in] aLinkQualityOut The Link Quality Out value. + * @param[in] aRouteCost The Route Cost value. * */ - void SetLinkQualityOut(uint8_t aRouterIndex, LinkQuality aLinkQuality) + void SetRouteData(uint8_t aRouterIndex, LinkQuality aLinkQualityIn, LinkQuality aLinkQualityOut, uint8_t aRouteCost) { - mRouteData[aRouterIndex] = (mRouteData[aRouterIndex] & ~kLinkQualityOutMask) | - ((aLinkQuality << kLinkQualityOutOffset) & kLinkQualityOutMask); + mRouteData[aRouterIndex] = (((aLinkQualityIn << kLinkQualityInOffset) & kLinkQualityInMask) | + ((aLinkQualityOut << kLinkQualityOutOffset) & kLinkQualityOutMask) | + ((aRouteCost << kRouteCostOffset) & kRouteCostMask)); } private: @@ -487,6 +476,15 @@ class RouteTlv : public Tlv, public TlvInfo */ bool IsRouterIdSet(uint8_t aRouterId) const { return mRouterIdMask.Contains(aRouterId); } + /** + * This method indicates whether the `RouteTlv` is a singleton, i.e., only one router is allocated. + * + * @retval TRUE It is a singleton. + * @retval FALSE It is not a singleton. + * + */ + bool IsSingleton(void) const { return IsValid() && (mRouterIdMask.GetNumberOfAllocatedIds() <= 1); } + /** * This method sets the Router ID bit. * @@ -538,30 +536,6 @@ class RouteTlv : public Tlv, public TlvInfo } } - /** - * This method sets the Route Cost value for a given Router index. - * - * @param[in] aRouterIndex The Router index. - * @param[in] aRouteCost The Route Cost value. - * - */ - void SetRouteCost(uint8_t aRouterIndex, uint8_t aRouteCost) - { - if (aRouterIndex & 1) - { - mRouteData[aRouterIndex + aRouterIndex / 2 + 1] = aRouteCost; - } - else - { - mRouteData[aRouterIndex + aRouterIndex / 2] = - (mRouteData[aRouterIndex + aRouterIndex / 2] & ~kRouteCostMask) | - ((aRouteCost >> kOddEntryOffset) & kRouteCostMask); - mRouteData[aRouterIndex + aRouterIndex / 2 + 1] = static_cast( - (mRouteData[aRouterIndex + aRouterIndex / 2 + 1] & ~(kRouteCostMask << kOddEntryOffset)) | - ((aRouteCost & kRouteCostMask) << kOddEntryOffset)); - } - } - /** * This method returns the Link Quality In value for a given Router index. * @@ -570,26 +544,12 @@ class RouteTlv : public Tlv, public TlvInfo * @returns The Link Quality In value for a given Router index. * */ - uint8_t GetLinkQualityIn(uint8_t aRouterIndex) const - { - int offset = ((aRouterIndex & 1) ? kOddEntryOffset : 0); - return (mRouteData[aRouterIndex + aRouterIndex / 2] & (kLinkQualityInMask >> offset)) >> - (kLinkQualityInOffset - offset); - } - - /** - * This method sets the Link Quality In value for a given Router index. - * - * @param[in] aRouterIndex The Router index. - * @param[in] aLinkQuality The Link Quality In value for a given Router index. - * - */ - void SetLinkQualityIn(uint8_t aRouterIndex, uint8_t aLinkQuality) + LinkQuality GetLinkQualityIn(uint8_t aRouterIndex) const { int offset = ((aRouterIndex & 1) ? kOddEntryOffset : 0); - mRouteData[aRouterIndex + aRouterIndex / 2] = - (mRouteData[aRouterIndex + aRouterIndex / 2] & ~(kLinkQualityInMask >> offset)) | - ((aLinkQuality << (kLinkQualityInOffset - offset)) & (kLinkQualityInMask >> offset)); + return static_cast( + (mRouteData[aRouterIndex + aRouterIndex / 2] & (kLinkQualityInMask >> offset)) >> + (kLinkQualityInOffset - offset)); } /** @@ -609,18 +569,19 @@ class RouteTlv : public Tlv, public TlvInfo } /** - * This method sets the Link Quality Out value for a given Router index. + * This method sets the Route Data (Link Quality In/Out and Route Cost) for a given Router index. * - * @param[in] aRouterIndex The Router index. - * @param[in] aLinkQuality The Link Quality Out value for a given Router index. + * @param[in] aRouterIndex The Router index. + * @param[in] aLinkQualityIn The Link Quality In value. + * @param[in] aLinkQualityOut The Link Quality Out value. + * @param[in] aRouteCost The Route Cost value. * */ - void SetLinkQualityOut(uint8_t aRouterIndex, LinkQuality aLinkQuality) + void SetRouteData(uint8_t aRouterIndex, LinkQuality aLinkQualityIn, LinkQuality aLinkQualityOut, uint8_t aRouteCost) { - int offset = ((aRouterIndex & 1) ? kOddEntryOffset : 0); - mRouteData[aRouterIndex + aRouterIndex / 2] = - (mRouteData[aRouterIndex + aRouterIndex / 2] & ~(kLinkQualityOutMask >> offset)) | - ((aLinkQuality << (kLinkQualityOutOffset - offset)) & (kLinkQualityOutMask >> offset)); + SetLinkQualityIn(aRouterIndex, aLinkQualityIn); + SetLinkQualityOut(aRouterIndex, aLinkQualityOut); + SetRouteCost(aRouterIndex, aRouteCost); } private: @@ -632,6 +593,39 @@ class RouteTlv : public Tlv, public TlvInfo static constexpr uint8_t kRouteCostMask = 0xf << kRouteCostOffset; static constexpr uint8_t kOddEntryOffset = 4; + void SetRouteCost(uint8_t aRouterIndex, uint8_t aRouteCost) + { + if (aRouterIndex & 1) + { + mRouteData[aRouterIndex + aRouterIndex / 2 + 1] = aRouteCost; + } + else + { + mRouteData[aRouterIndex + aRouterIndex / 2] = + (mRouteData[aRouterIndex + aRouterIndex / 2] & ~kRouteCostMask) | + ((aRouteCost >> kOddEntryOffset) & kRouteCostMask); + mRouteData[aRouterIndex + aRouterIndex / 2 + 1] = static_cast( + (mRouteData[aRouterIndex + aRouterIndex / 2 + 1] & ~(kRouteCostMask << kOddEntryOffset)) | + ((aRouteCost & kRouteCostMask) << kOddEntryOffset)); + } + } + + void SetLinkQualityIn(uint8_t aRouterIndex, uint8_t aLinkQuality) + { + int offset = ((aRouterIndex & 1) ? kOddEntryOffset : 0); + mRouteData[aRouterIndex + aRouterIndex / 2] = + (mRouteData[aRouterIndex + aRouterIndex / 2] & ~(kLinkQualityInMask >> offset)) | + ((aLinkQuality << (kLinkQualityInOffset - offset)) & (kLinkQualityInMask >> offset)); + } + + void SetLinkQualityOut(uint8_t aRouterIndex, LinkQuality aLinkQuality) + { + int offset = ((aRouterIndex & 1) ? kOddEntryOffset : 0); + mRouteData[aRouterIndex + aRouterIndex / 2] = + (mRouteData[aRouterIndex + aRouterIndex / 2] & ~(kLinkQualityOutMask >> offset)) | + ((aLinkQuality << (kLinkQualityOutOffset - offset)) & (kLinkQualityOutMask >> offset)); + } + uint8_t mRouterIdSequence; RouterIdSet mRouterIdMask; // Since we do hold 12 (compressible to 11) bits of data per router, each entry occupies 1.5 bytes, @@ -783,10 +777,7 @@ class ConnectivityTlv : public Tlv, public TlvInfo * @returns The Parent Priority value. * */ - int8_t GetParentPriority(void) const - { - return (static_cast(mParentPriority & kParentPriorityMask)) >> kParentPriorityOffset; - } + int8_t GetParentPriority(void) const; /** * This method sets the Parent Priority value. @@ -794,10 +785,7 @@ class ConnectivityTlv : public Tlv, public TlvInfo * @param[in] aParentPriority The Parent Priority value. * */ - void SetParentPriority(int8_t aParentPriority) - { - mParentPriority = (static_cast(aParentPriority) << kParentPriorityOffset) & kParentPriorityMask; - } + void SetParentPriority(int8_t aParentPriority); /** * This method returns the Link Quality 3 value. @@ -847,6 +835,17 @@ class ConnectivityTlv : public Tlv, public TlvInfo */ void SetLinkQuality1(uint8_t aLinkQuality) { mLinkQuality1 = aLinkQuality; } + /** + * This method increments the Link Quality N field in TLV for a given Link Quality N (1,2,3). + * + * The Link Quality N field specifies the number of neighboring router devices with which the sender shares a link + * of quality N. + * + * @param[in] aLinkQuality The Link Quality N (1,2,3) field to update. + * + */ + void IncrementLinkQuality(LinkQuality aLinkQuality); + /** * This method sets the Active Routers value. * @@ -946,10 +945,10 @@ class ConnectivityTlv : public Tlv, public TlvInfo void SetSedDatagramCount(uint8_t aSedDatagramCount) { mSedDatagramCount = aSedDatagramCount; } private: - static constexpr uint8_t kParentPriorityOffset = 6; - static constexpr uint8_t kParentPriorityMask = 3 << kParentPriorityOffset; + static constexpr uint8_t kFlagsParentPriorityOffset = 6; + static constexpr uint8_t kFlagsParentPriorityMask = (3 << kFlagsParentPriorityOffset); - uint8_t mParentPriority; + uint8_t mFlags; uint8_t mLinkQuality3; uint8_t mLinkQuality2; uint8_t mLinkQuality1; @@ -976,96 +975,57 @@ struct StatusTlv : public UintTlvInfo }; /** - * This class implements Source Address TLV generation and parsing. + * This class provides constants and methods for generation and parsing of Address Registration TLV. * */ -OT_TOOL_PACKED_BEGIN -class AddressRegistrationEntry +class AddressRegistrationTlv : public TlvInfo { public: /** - * This method returns the IPv6 address or IID length. - * - * @returns The IPv6 address length if the Compressed bit is clear, or the IID length if the Compressed bit is - * set. - * - */ - uint8_t GetLength(void) const { return sizeof(mControl) + (IsCompressed() ? sizeof(mIid) : sizeof(mIp6Address)); } - - /** - * This method indicates whether or not the Compressed flag is set. - * - * @retval TRUE If the Compressed flag is set. - * @retval FALSE If the Compressed flag is not set. + * This constant defines the control byte to use in an uncompressed entry where the full IPv6 address is included in + * the TLV. * */ - bool IsCompressed(void) const { return (mControl & kCompressed) != 0; } + static constexpr uint8_t kControlByteUncompressed = 0; /** - * This method sets the Uncompressed flag. + * This static method returns the control byte to use in a compressed entry where the 64-prefix is replaced with a + * 6LoWPAN context identifier. * - */ - void SetUncompressed(void) { mControl = 0; } - - /** - * This method returns the Context ID for the compressed form. + * @param[in] aContextId The 6LoWPAN context ID. * - * @returns The Context ID value. + * @returns The control byte associated with compressed entry with @p aContextId. * */ - uint8_t GetContextId(void) const { return mControl & kCidMask; } + static uint8_t ControlByteFor(uint8_t aContextId) { return kCompressed | (aContextId & kContextIdMask); } /** - * This method sets the Context ID value. - * - * @param[in] aContextId The Context ID value. + * This static method indicates whether or not an address entry is using compressed format. * - */ - void SetContextId(uint8_t aContextId) { mControl = kCompressed | aContextId; } - - /** - * This method returns the IID value. + * @param[in] aControlByte The control byte (the first byte in the entry). * - * @returns The IID value. + * @retval TRUE If the entry uses compressed format. + * @retval FALSE If the entry uses uncompressed format. * */ - const Ip6::InterfaceIdentifier &GetIid(void) const { return mIid; } + static bool IsEntryCompressed(uint8_t aControlByte) { return (aControlByte & kCompressed); } /** - * This method sets the IID value. + * This static method gets the context ID in a compressed entry. * - * @param[in] aIid The IID value. - * - */ - void SetIid(const Ip6::InterfaceIdentifier &aIid) { mIid = aIid; } - - /** - * This method returns the IPv6 Address value. + * @param[in] aControlByte The control byte (the first byte in the entry). * - * @returns The IPv6 Address value. + * @returns The 6LoWPAN context ID. * */ - const Ip6::Address &GetIp6Address(void) const { return mIp6Address; } + static uint8_t GetContextId(uint8_t aControlByte) { return (aControlByte & kContextIdMask); } - /** - * This method sets the IPv6 Address value. - * - * @param[in] aAddress A reference to the IPv6 Address value. - * - */ - void SetIp6Address(const Ip6::Address &aAddress) { mIp6Address = aAddress; } + AddressRegistrationTlv(void) = delete; private: - static constexpr uint8_t kCompressed = 1 << 7; - static constexpr uint8_t kCidMask = 0xf; - - uint8_t mControl; - union - { - Ip6::InterfaceIdentifier mIid; - Ip6::Address mIp6Address; - } OT_TOOL_PACKED_FIELD; -} OT_TOOL_PACKED_END; + static constexpr uint8_t kCompressed = 1 << 7; + static constexpr uint8_t kContextIdMask = 0xf; +}; /** * This class implements Channel TLV generation and parsing. @@ -1230,7 +1190,7 @@ class CslChannelTlv : public Tlv, public TlvInfo * @retval FALSE If the TLV does not appear to be well-formed. * */ - bool IsValid(void) const { return GetLength() == sizeof(*this) - sizeof(Tlv); } + bool IsValid(void) const { return GetLength() >= sizeof(*this) - sizeof(Tlv); } /** * This method returns the Channel Page value. @@ -1290,13 +1250,22 @@ class CslClockAccuracyTlv : public Tlv, public TlvInfo SetLength(sizeof(*this) - sizeof(Tlv)); } + /** + * This method indicates whether or not the TLV appears to be well-formed. + * + * @retval TRUE If the TLV appears to be well-formed. + * @retval FALSE If the TLV does not appear to be well-formed. + * + */ + bool IsValid(void) const { return GetLength() >= sizeof(*this) - sizeof(Tlv); } + /** * This method returns the CSL Clock Accuracy value. * * @returns The CSL Clock Accuracy value. * */ - uint8_t GetCslClockAccuracy(void) { return mCslClockAccuracy; } + uint8_t GetCslClockAccuracy(void) const { return mCslClockAccuracy; } /** * This method sets the CSL Clock Accuracy value. @@ -1307,12 +1276,12 @@ class CslClockAccuracyTlv : public Tlv, public TlvInfo void SetCslClockAccuracy(uint8_t aCslClockAccuracy) { mCslClockAccuracy = aCslClockAccuracy; } /** - * This method returns the Clock Accuracy value. + * This method returns the Clock Uncertainty value. * - * @returns The Clock Accuracy value. + * @returns The Clock Uncertainty value. * */ - uint8_t GetCslUncertainty(void) { return mCslUncertainty; } + uint8_t GetCslUncertainty(void) const { return mCslUncertainty; } /** * This method sets the CSL Uncertainty value. diff --git a/src/core/thread/mle_types.cpp b/src/core/thread/mle_types.cpp index 28233bab596..ca76165cff5 100644 --- a/src/core/thread/mle_types.cpp +++ b/src/core/thread/mle_types.cpp @@ -33,11 +33,15 @@ #include "mle_types.hpp" +#include "common/array.hpp" #include "common/code_utils.hpp" namespace ot { namespace Mle { +//--------------------------------------------------------------------------------------------------------------------- +// DeviceMode + void DeviceMode::Get(ModeConfig &aModeConfig) const { aModeConfig.mRxOnWhenIdle = IsRxOnWhenIdle(); @@ -63,5 +67,109 @@ DeviceMode::InfoString DeviceMode::ToString(void) const return string; } +//--------------------------------------------------------------------------------------------------------------------- +// DeviceProperties + +#if OPENTHREAD_FTD + +DeviceProperties::DeviceProperties(void) +{ + Clear(); + + mPowerSupply = OPENTHREAD_CONFIG_DEVICE_POWER_SUPPLY; + mLeaderWeightAdjustment = kDefaultAdjustment; +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + mIsBorderRouter = true; +#endif +} + +void DeviceProperties::ClampWeightAdjustment(void) +{ + mLeaderWeightAdjustment = Clamp(mLeaderWeightAdjustment, kMinAdjustment, kMaxAdjustment); +} + +uint8_t DeviceProperties::CalculateLeaderWeight(void) const +{ + static const int8_t kPowerSupplyIncs[] = { + kPowerBatteryInc, // (0) kPowerSupplyBattery + kPowerExternalInc, // (1) kPowerSupplyExternal + kPowerExternalStableInc, // (2) kPowerSupplyExternalStable + kPowerExternalUnstableInc, // (3) kPowerSupplyExternalUnstable + }; + + static_assert(0 == kPowerSupplyBattery, "kPowerSupplyBattery value is incorrect"); + static_assert(1 == kPowerSupplyExternal, "kPowerSupplyExternal value is incorrect"); + static_assert(2 == kPowerSupplyExternalStable, "kPowerSupplyExternalStable value is incorrect"); + static_assert(3 == kPowerSupplyExternalUnstable, "kPowerSupplyExternalUnstable value is incorrect"); + + uint8_t weight = kBaseWeight; + PowerSupply powerSupply = MapEnum(mPowerSupply); + + if (mIsBorderRouter) + { + weight += (mSupportsCcm ? kCcmBorderRouterInc : kBorderRouterInc); + } + + if (powerSupply < GetArrayLength(kPowerSupplyIncs)) + { + weight += kPowerSupplyIncs[powerSupply]; + } + + if (mIsUnstable) + { + switch (powerSupply) + { + case kPowerSupplyBattery: + case kPowerSupplyExternalUnstable: + break; + + default: + weight += kIsUnstableInc; + } + } + + weight += mLeaderWeightAdjustment; + + return weight; +} + +#endif // OPENTHREAD_FTD + +//--------------------------------------------------------------------------------------------------------------------- +// RouterIdSet + +uint8_t RouterIdSet::GetNumberOfAllocatedIds(void) const +{ + uint8_t count = 0; + + for (uint8_t byte : mRouterIdSet) + { + count += CountBitsInMask(byte); + } + + return count; +} + +//--------------------------------------------------------------------------------------------------------------------- + +const char *RoleToString(DeviceRole aRole) +{ + static const char *const kRoleStrings[] = { + "disabled", // (0) kRoleDisabled + "detached", // (1) kRoleDetached + "child", // (2) kRoleChild + "router", // (3) kRoleRouter + "leader", // (4) kRoleLeader + }; + + static_assert(kRoleDisabled == 0, "kRoleDisabled value is incorrect"); + static_assert(kRoleDetached == 1, "kRoleDetached value is incorrect"); + static_assert(kRoleChild == 2, "kRoleChild value is incorrect"); + static_assert(kRoleRouter == 3, "kRoleRouter value is incorrect"); + static_assert(kRoleLeader == 4, "kRoleLeader value is incorrect"); + + return (aRole < GetArrayLength(kRoleStrings)) ? kRoleStrings[aRole] : "invalid"; +} + } // namespace Mle } // namespace ot diff --git a/src/core/thread/mle_types.hpp b/src/core/thread/mle_types.hpp index fbc92a63efa..286e63ae667 100644 --- a/src/core/thread/mle_types.hpp +++ b/src/core/thread/mle_types.hpp @@ -41,6 +41,9 @@ #include #include +#if OPENTHREAD_FTD +#include +#endif #include "common/as_core_type.hpp" #include "common/clearable.hpp" @@ -77,8 +80,7 @@ constexpr uint8_t kMaxServiceAlocs = OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_MAX_A constexpr uint8_t kMaxServiceAlocs = OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_MAX_ALOCS; #endif -constexpr uint8_t kThreadVersion = OPENTHREAD_CONFIG_THREAD_VERSION; ///< Thread Version -constexpr uint16_t kUdpPort = 19788; ///< MLE UDP Port +constexpr uint16_t kUdpPort = 19788; ///< MLE UDP Port /* * MLE Protocol delays and timeouts. @@ -87,6 +89,7 @@ constexpr uint16_t kUdpPort = 19788; ///< MLE U constexpr uint32_t kParentRequestRouterTimeout = 750; ///< Router Parent Request timeout (in msec) constexpr uint32_t kParentRequestDuplicateMargin = 50; ///< Margin for duplicate parent request constexpr uint32_t kParentRequestReedTimeout = 1250; ///< Router and REEDs Parent Request timeout (in msec) +constexpr uint32_t kChildIdResponseTimeout = 1250; ///< Wait time to receive Child ID Response (in msec) constexpr uint32_t kAttachStartJitter = 50; ///< Max jitter time added to start of attach (in msec) constexpr uint32_t kAnnounceProcessTimeout = 250; ///< Delay after Announce rx before channel/pan-id change constexpr uint32_t kAnnounceTimeout = 1400; ///< Total timeout for sending Announce messages (in msec) @@ -97,10 +100,16 @@ constexpr uint32_t kUnicastRetransmissionDelay = 1000; ///< Base delay befor constexpr uint32_t kChildUpdateRequestPendingDelay = 100; ///< Delay for aggregating Child Update Req (in msec) constexpr uint8_t kMaxTransmissionCount = 3; ///< Max number of times an MLE message may be transmitted constexpr uint32_t kMaxResponseDelay = 1000; ///< Max response delay for a multicast request (in msec) -constexpr uint32_t kMaxChildIdRequestTimeout = 5000; ///< Max delay to rx a Child ID Request (in msec) -constexpr uint32_t kMaxChildUpdateResponseTimeout = 2000; ///< Max delay to rx a Child Update Response (in msec) -constexpr uint32_t kMaxLinkRequestTimeout = 2000; ///< Max delay to rx a Link Accept +constexpr uint32_t kChildIdRequestTimeout = 5000; ///< Max delay to rx a Child ID Request (in msec) +constexpr uint32_t kLinkRequestTimeout = 2000; ///< Max delay to rx a Link Accept constexpr uint8_t kMulticastLinkRequestDelay = 5; ///< Max delay for sending a mcast Link Request (in sec) +constexpr uint8_t kMaxCriticalTransmissionCount = 6; ///< Max number of times an critical MLE message may be transmitted + +constexpr uint32_t kMulticastTransmissionDelay = 5000; ///< Delay for retransmitting a multicast packet (in msec) +constexpr uint32_t kMulticastTransmissionDelayMin = + kMulticastTransmissionDelay * 9 / 10; ///< Min delay for retransmitting a multicast packet (in msec) +constexpr uint32_t kMulticastTransmissionDelayMax = + kMulticastTransmissionDelay * 11 / 10; ///< Max delay for retransmitting a multicast packet (in msec) constexpr uint32_t kMinTimeoutKeepAlive = (((kMaxChildKeepAliveAttempts + 1) * kUnicastRetransmissionDelay) / 1000); constexpr uint32_t kMinPollPeriod = OPENTHREAD_CONFIG_MAC_MINIMUM_POLL_PERIOD; @@ -121,8 +130,8 @@ constexpr uint16_t kMaxChildId = 511; ///< Maximum Child ID constexpr uint8_t kRouterIdOffset = 10; ///< Bit offset of Router ID in RLOC16 constexpr uint8_t kRlocPrefixLength = 14; ///< Prefix length of RLOC in bytes -constexpr uint8_t kMinChallengeSize = 4; ///< Minimum Challenge size in bytes. -constexpr uint8_t kMaxChallengeSize = 8; ///< Maximum Challenge size in bytes. +constexpr uint16_t kMinChallengeSize = 4; ///< Minimum Challenge size in bytes. +constexpr uint16_t kMaxChallengeSize = 8; ///< Maximum Challenge size in bytes. /* * Routing Protocol Constants @@ -162,6 +171,8 @@ constexpr uint8_t kRouterSelectionJitter = 120; ///< (in sec) constexpr uint8_t kRouterDowngradeThreshold = 23; constexpr uint8_t kRouterUpgradeThreshold = 16; +constexpr uint16_t kInvalidRloc16 = Mac::kShortAddrInvalid; ///< Invalid RLOC16. + /** * Threshold to accept a router upgrade request with reason `kBorderRouterRequest` (number of BRs acting as router in * Network Data). @@ -173,7 +184,6 @@ constexpr uint32_t kMaxLeaderToRouterTimeout = 90; ///< (in sec) constexpr uint32_t kReedAdvertiseInterval = 570; ///< (in sec) constexpr uint32_t kReedAdvertiseJitter = 60; ///< (in sec) -constexpr uint8_t kLeaderWeight = 64; ///< Default leader weight constexpr uint32_t kMleEndDeviceTimeout = OPENTHREAD_CONFIG_MLE_CHILD_TIMEOUT_DEFAULT; ///< (in sec) constexpr uint8_t kMeshLocalPrefixContextId = 0; ///< 0 is reserved for Mesh Local Prefix @@ -182,14 +192,6 @@ constexpr int8_t kParentPriorityMedium = 0; ///< Parent Priority Medium (d constexpr int8_t kParentPriorityLow = -1; ///< Parent Priority Low constexpr int8_t kParentPriorityUnspecified = -2; ///< Parent Priority Unspecified -constexpr uint8_t kLinkQuality3LinkCost = 1; ///< Link Cost for Link Quality 3 -constexpr uint8_t kLinkQuality2LinkCost = 2; ///< Link Cost for Link Quality 2 -constexpr uint8_t kLinkQuality1LinkCost = 4; ///< Link Cost for Link Quality 1 -constexpr uint8_t kLinkQuality0LinkCost = kMaxRouteCost; ///< Link Cost for Link Quality 0 - -constexpr uint8_t kMplChildDataMessageTimerExpirations = 0; ///< Number of MPL retransmissions for Children. -constexpr uint8_t kMplRouterDataMessageTimerExpirations = 2; ///< Number of MPL retransmissions for Routers. - /** * This type represents a Thread device role. * @@ -236,7 +238,7 @@ enum LeaderStartMode : uint8_t * Backbone Router / DUA / MLR constants * */ -constexpr uint16_t kRegistrationDelayDefault = 1200; ///< In seconds. +constexpr uint16_t kRegistrationDelayDefault = 5; ///< In seconds. constexpr uint32_t kMlrTimeoutDefault = 3600; ///< In seconds. constexpr uint32_t kMlrTimeoutMin = 300; ///< In seconds. constexpr uint32_t kMlrTimeoutMax = 0x7fffffff / 1000; ///< In seconds (about 24 days). @@ -435,6 +437,67 @@ class DeviceMode : public Equatable uint8_t mMode; }; +#if OPENTHREAD_FTD +/** + * This class represents device properties. + * + * The device properties are used for calculating the local leader weight on the device. + * + */ +class DeviceProperties : public otDeviceProperties, public Clearable +{ +public: + /** + * This enumeration represents the device's power supply property. + * + */ + enum PowerSupply : uint8_t + { + kPowerSupplyBattery = OT_POWER_SUPPLY_BATTERY, ///< Battery powered. + kPowerSupplyExternal = OT_POWER_SUPPLY_EXTERNAL, ///< External powered. + kPowerSupplyExternalStable = OT_POWER_SUPPLY_EXTERNAL_STABLE, ///< Stable external power with backup. + kPowerSupplyExternalUnstable = OT_POWER_SUPPLY_EXTERNAL_UNSTABLE, ///< Unstable external power. + }; + + /** + * This constructor initializes `DeviceProperties` with default values. + * + */ + DeviceProperties(void); + + /** + * This method clamps the `mLeaderWeightAdjustment` value to the valid range. + * + */ + void ClampWeightAdjustment(void); + + /** + * This method calculates the leader weight based on the device properties. + * + * @returns The calculated leader weight. + * + */ + uint8_t CalculateLeaderWeight(void) const; + +private: + static constexpr int8_t kDefaultAdjustment = OPENTHREAD_CONFIG_MLE_DEFAULT_LEADER_WEIGHT_ADJUSTMENT; + static constexpr uint8_t kBaseWeight = 64; + static constexpr int8_t kBorderRouterInc = +1; + static constexpr int8_t kCcmBorderRouterInc = +8; + static constexpr int8_t kIsUnstableInc = -4; + static constexpr int8_t kPowerBatteryInc = -8; + static constexpr int8_t kPowerExternalInc = 0; + static constexpr int8_t kPowerExternalStableInc = +4; + static constexpr int8_t kPowerExternalUnstableInc = -4; + static constexpr int8_t kMinAdjustment = -16; + static constexpr int8_t kMaxAdjustment = +16; + + static_assert(kDefaultAdjustment >= kMinAdjustment, "Invalid default weight adjustment"); + static_assert(kDefaultAdjustment <= kMaxAdjustment, "Invalid default weight adjustment"); +}; + +#endif // OPENTHREAD_FTD + /** * This class represents the Thread Leader Data. * @@ -539,7 +602,7 @@ class RouterIdSet : public Equatable * @retval FALSE If the Router ID bit is not set. * */ - bool Contains(uint8_t aRouterId) const { return (mRouterIdSet[aRouterId / 8] & (0x80 >> (aRouterId % 8))) != 0; } + bool Contains(uint8_t aRouterId) const { return (mRouterIdSet[aRouterId / 8] & MaskFor(aRouterId)) != 0; } /** * This method sets a given Router ID. @@ -547,7 +610,7 @@ class RouterIdSet : public Equatable * @param[in] aRouterId The Router ID to set. * */ - void Add(uint8_t aRouterId) { mRouterIdSet[aRouterId / 8] |= 0x80 >> (aRouterId % 8); } + void Add(uint8_t aRouterId) { mRouterIdSet[aRouterId / 8] |= MaskFor(aRouterId); } /** * This method removes a given Router ID. @@ -555,9 +618,19 @@ class RouterIdSet : public Equatable * @param[in] aRouterId The Router ID to remove. * */ - void Remove(uint8_t aRouterId) { mRouterIdSet[aRouterId / 8] &= ~(0x80 >> (aRouterId % 8)); } + void Remove(uint8_t aRouterId) { mRouterIdSet[aRouterId / 8] &= ~MaskFor(aRouterId); } + + /** + * This method calculates the number of allocated Router IDs in the set. + * + * @returns The number of allocated Router IDs in the set. + * + */ + uint8_t GetNumberOfAllocatedIds(void) const; private: + static uint8_t MaskFor(uint8_t aRouterId) { return (0x80 >> (aRouterId % 8)); } + uint8_t mRouterIdSet[BitVectorBytes(Mle::kMaxRouterId + 1)]; } OT_TOOL_PACKED_END; @@ -573,6 +646,113 @@ typedef Mac::KeyMaterial KeyMaterial; */ typedef Mac::Key Key; +/** + * This structure represents the Thread MLE counters. + * + */ +typedef otMleCounters Counters; + +/** + * This function derives the Child ID from a given RLOC16. + * + * @param[in] aRloc16 The RLOC16 value. + * + * @returns The Child ID portion of an RLOC16. + * + */ +inline uint16_t ChildIdFromRloc16(uint16_t aRloc16) { return aRloc16 & kMaxChildId; } + +/** + * This function derives the Router ID portion from a given RLOC16. + * + * @param[in] aRloc16 The RLOC16 value. + * + * @returns The Router ID portion of an RLOC16. + * + */ +inline uint8_t RouterIdFromRloc16(uint16_t aRloc16) { return aRloc16 >> kRouterIdOffset; } + +/** + * This function returns whether the two RLOC16 have the same Router ID. + * + * @param[in] aRloc16A The first RLOC16 value. + * @param[in] aRloc16B The second RLOC16 value. + * + * @returns true if the two RLOC16 have the same Router ID, false otherwise. + * + */ +inline bool RouterIdMatch(uint16_t aRloc16A, uint16_t aRloc16B) +{ + return RouterIdFromRloc16(aRloc16A) == RouterIdFromRloc16(aRloc16B); +} + +/** + * This function returns the Service ID corresponding to a Service ALOC16. + * + * @param[in] aAloc16 The Service ALOC16 value. + * + * @returns The Service ID corresponding to given ALOC16. + * + */ +inline uint8_t ServiceIdFromAloc(uint16_t aAloc16) { return static_cast(aAloc16 - kAloc16ServiceStart); } + +/** + * This function returns the Service ALOC16 corresponding to a Service ID. + * + * @param[in] aServiceId The Service ID value. + * + * @returns The Service ALOC16 corresponding to given ID. + * + */ +inline uint16_t ServiceAlocFromId(uint8_t aServiceId) +{ + return static_cast(aServiceId + kAloc16ServiceStart); +} + +/** + * This function returns the Commissioner Aloc corresponding to a Commissioner Session ID. + * + * @param[in] aSessionId The Commissioner Session ID value. + * + * @returns The Commissioner ALOC16 corresponding to given ID. + * + */ +inline uint16_t CommissionerAloc16FromId(uint16_t aSessionId) +{ + return static_cast((aSessionId & kAloc16CommissionerMask) + kAloc16CommissionerStart); +} + +/** + * This function derives RLOC16 from a given Router ID. + * + * @param[in] aRouterId The Router ID value. + * + * @returns The RLOC16 corresponding to the given Router ID. + * + */ +inline uint16_t Rloc16FromRouterId(uint8_t aRouterId) { return static_cast(aRouterId << kRouterIdOffset); } + +/** + * This function indicates whether or not @p aRloc16 refers to an active router. + * + * @param[in] aRloc16 The RLOC16 value. + * + * @retval TRUE If @p aRloc16 refers to an active router. + * @retval FALSE If @p aRloc16 does not refer to an active router. + * + */ +inline bool IsActiveRouter(uint16_t aRloc16) { return ChildIdFromRloc16(aRloc16) == 0; } + +/** + * This function converts a device role into a human-readable string. + * + * @param[in] aRole The device role to convert. + * + * @returns The string representation of @p aRole. + * + */ +const char *RoleToString(DeviceRole aRole); + /** * @} * @@ -582,6 +762,10 @@ typedef Mac::Key Key; DefineCoreType(otLeaderData, Mle::LeaderData); DefineMapEnum(otDeviceRole, Mle::DeviceRole); +#if OPENTHREAD_FTD +DefineCoreType(otDeviceProperties, Mle::DeviceProperties); +DefineMapEnum(otPowerSupply, Mle::DeviceProperties::PowerSupply); +#endif } // namespace ot diff --git a/src/core/thread/mlr_manager.cpp b/src/core/thread/mlr_manager.cpp index 98272febe63..d8e8a037837 100644 --- a/src/core/thread/mlr_manager.cpp +++ b/src/core/thread/mlr_manager.cpp @@ -51,10 +51,6 @@ RegisterLogModule("MlrManager"); MlrManager::MlrManager(Instance &aInstance) : InstanceLocator(aInstance) -#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE - , mRegisterMulticastListenersCallback(nullptr) - , mRegisterMulticastListenersContext(nullptr) -#endif , mReregistrationDelay(0) , mSendDelay(0) , mMlrPending(false) @@ -80,8 +76,8 @@ void MlrManager::HandleNotifierEvents(Events aEvents) } } -void MlrManager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState, - const BackboneRouter::BackboneRouterConfig &aConfig) +void MlrManager::HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState, + const BackboneRouter::Config &aConfig) { OT_UNUSED_VARIABLE(aConfig); @@ -150,7 +146,7 @@ bool MlrManager::IsAddressMlrRegisteredByAnyChildExcept(const Ip6::Address &aAdd return ret; } -void MlrManager::UpdateProxiedSubscriptions(Child & aChild, +void MlrManager::UpdateProxiedSubscriptions(Child &aChild, const Ip6::Address *aOldMlrRegisteredAddresses, uint16_t aOldMlrRegisteredAddressNum) { @@ -293,7 +289,7 @@ void MlrManager::SendMulticastListenerRegistration(void) mMlrPending = true; - // Generally Thread 1.2 Router would send MLR.req on bebelf for MA (scope >=4) subscribed by its MTD child. + // Generally Thread 1.2 Router would send MLR.req on behalf for MA (scope >=4) subscribed by its MTD child. // When Thread 1.2 MTD attaches to Thread 1.1 parent, 1.2 MTD should send MLR.req to PBBR itself. // In this case, Thread 1.2 sleepy end device relies on fast data poll to fetch the response timely. if (!Get().IsRxOnWhenIdle()) @@ -317,11 +313,11 @@ void MlrManager::SendMulticastListenerRegistration(void) } #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE -Error MlrManager::RegisterMulticastListeners(const otIp6Address * aAddresses, +Error MlrManager::RegisterMulticastListeners(const otIp6Address *aAddresses, uint8_t aAddressNum, - const uint32_t * aTimeout, + const uint32_t *aTimeout, otIp6RegisterMulticastListenersCallback aCallback, - void * aContext) + void *aContext) { Error error; @@ -343,16 +339,15 @@ Error MlrManager::RegisterMulticastListeners(const otIp6Address * SuccessOrExit(error = SendMulticastListenerRegistrationMessage( aAddresses, aAddressNum, aTimeout, &MlrManager::HandleRegisterMulticastListenersResponse, this)); - mRegisterMulticastListenersPending = true; - mRegisterMulticastListenersCallback = aCallback; - mRegisterMulticastListenersContext = aContext; + mRegisterMulticastListenersPending = true; + mRegisterMulticastListenersCallback.Set(aCallback, aContext); exit: return error; } -void MlrManager::HandleRegisterMulticastListenersResponse(void * aContext, - otMessage * aMessage, +void MlrManager::HandleRegisterMulticastListenersResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult) { @@ -360,51 +355,46 @@ void MlrManager::HandleRegisterMulticastListenersResponse(void * a AsCoreTypePtr(aMessageInfo), aResult); } -void MlrManager::HandleRegisterMulticastListenersResponse(otMessage * aMessage, +void MlrManager::HandleRegisterMulticastListenersResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult) { OT_UNUSED_VARIABLE(aMessageInfo); - uint8_t status; - Error error; - Ip6::Address failedAddresses[Ip6AddressesTlv::kMaxAddresses]; - uint8_t failedAddressNum = 0; - otIp6RegisterMulticastListenersCallback callback = mRegisterMulticastListenersCallback; - void * context = mRegisterMulticastListenersContext; + uint8_t status; + Error error; + Ip6::Address failedAddresses[Ip6AddressesTlv::kMaxAddresses]; + uint8_t failedAddressNum = 0; + Callback callbackCopy = mRegisterMulticastListenersCallback; - mRegisterMulticastListenersPending = false; - mRegisterMulticastListenersCallback = nullptr; - mRegisterMulticastListenersContext = nullptr; + mRegisterMulticastListenersPending = false; + mRegisterMulticastListenersCallback.Clear(); error = ParseMulticastListenerRegistrationResponse(aResult, AsCoapMessagePtr(aMessage), status, failedAddresses, failedAddressNum); - if (callback != nullptr) - { - callback(context, error, status, failedAddresses, failedAddressNum); - } + callbackCopy.InvokeIfSet(error, status, failedAddresses, failedAddressNum); } #endif // (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE -Error MlrManager::SendMulticastListenerRegistrationMessage(const otIp6Address * aAddresses, +Error MlrManager::SendMulticastListenerRegistrationMessage(const otIp6Address *aAddresses, uint8_t aAddressNum, - const uint32_t * aTimeout, + const uint32_t *aTimeout, Coap::ResponseHandler aResponseHandler, - void * aResponseContext) + void *aResponseContext) { OT_UNUSED_VARIABLE(aTimeout); Error error = kErrorNone; - Mle::MleRouter & mle = Get(); - Coap::Message * message = nullptr; + Mle::MleRouter &mle = Get(); + Coap::Message *message = nullptr; Tmf::MessageInfo messageInfo(GetInstance()); Ip6AddressesTlv addressesTlv; VerifyOrExit(Get().HasPrimary(), error = kErrorInvalidState); - message = Get().NewConfirmablePostMessage(UriPath::kMlr); + message = Get().NewConfirmablePostMessage(kUriMlr); VerifyOrExit(message != nullptr, error = kErrorNoBufs); addressesTlv.Init(); @@ -452,8 +442,8 @@ Error MlrManager::SendMulticastListenerRegistrationMessage(const otIp6Address * return error; } -void MlrManager::HandleMulticastListenerRegistrationResponse(void * aContext, - otMessage * aMessage, +void MlrManager::HandleMulticastListenerRegistrationResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult) { @@ -461,7 +451,7 @@ void MlrManager::HandleMulticastListenerRegistrationResponse(void * AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo), aResult); } -void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message * aMessage, +void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult) { @@ -484,7 +474,7 @@ void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message * } else { - otBackboneRouterConfig config; + BackboneRouter::Config config; uint16_t reregDelay; // The Device has just attempted a Multicast Listener Registration which failed, and it retries the same @@ -502,9 +492,9 @@ void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message * Error MlrManager::ParseMulticastListenerRegistrationResponse(Error aResult, Coap::Message *aMessage, - uint8_t & aStatus, - Ip6::Address * aFailedAddresses, - uint8_t & aFailedAddressNum) + uint8_t &aStatus, + Ip6::Address *aFailedAddresses, + uint8_t &aFailedAddressNum) { Error error; uint16_t addressesOffset, addressesLength; @@ -642,9 +632,9 @@ void MlrManager::UpdateReregistrationDelay(bool aRereg) } else { - BackboneRouter::BackboneRouterConfig config; - uint32_t reregDelay; - uint32_t effectiveMlrTimeout; + BackboneRouter::Config config; + uint32_t reregDelay; + uint32_t effectiveMlrTimeout; IgnoreError(Get().GetConfig(config)); @@ -658,8 +648,7 @@ void MlrManager::UpdateReregistrationDelay(bool aRereg) { // Calculate renewing period according to Thread Spec. 5.24.2.3.2 // The random time t SHOULD be chosen such that (0.5* MLR-Timeout) < t < (MLR-Timeout – 9 seconds). - effectiveMlrTimeout = config.mMlrTimeout > Mle::kMlrTimeoutMin ? config.mMlrTimeout - : static_cast(Mle::kMlrTimeoutMin); + effectiveMlrTimeout = Max(config.mMlrTimeout, Mle::kMlrTimeoutMin); reregDelay = Random::NonCrypto::GetUint32InRange((effectiveMlrTimeout >> 1u) + 1, effectiveMlrTimeout - 9); } @@ -672,7 +661,7 @@ void MlrManager::UpdateReregistrationDelay(bool aRereg) UpdateTimeTickerRegistration(); LogDebg("MlrManager::UpdateReregistrationDelay: rereg=%d, needSendMlr=%d, ReregDelay=%lu", aRereg, needSendMlr, - mReregistrationDelay); + ToUlong(mReregistrationDelay)); } void MlrManager::LogMulticastAddresses(void) @@ -702,7 +691,7 @@ void MlrManager::LogMulticastAddresses(void) } void MlrManager::AppendToUniqueAddressList(Ip6::Address (&aAddresses)[Ip6AddressesTlv::kMaxAddresses], - uint8_t & aAddressNum, + uint8_t &aAddressNum, const Ip6::Address &aAddress) { #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE diff --git a/src/core/thread/mlr_manager.hpp b/src/core/thread/mlr_manager.hpp index 6eda998ed47..b78e0fba031 100644 --- a/src/core/thread/mlr_manager.hpp +++ b/src/core/thread/mlr_manager.hpp @@ -44,6 +44,7 @@ #include "backbone_router/bbr_leader.hpp" #include "coap/coap_message.hpp" +#include "common/callback.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" #include "common/notifier.hpp" @@ -94,8 +95,7 @@ class MlrManager : public InstanceLocator, private NonCopyable * @param[in] aConfig The Primary Backbone Router service. * */ - void HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState, - const BackboneRouter::BackboneRouterConfig &aConfig); + void HandleBackboneRouterPrimaryUpdate(BackboneRouter::Leader::State aState, const BackboneRouter::Config &aConfig); #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE /** @@ -106,7 +106,7 @@ class MlrManager : public InstanceLocator, private NonCopyable * @param[in] aOldMlrRegisteredAddressNum The number of previously registered IPv6 addresses. * */ - void UpdateProxiedSubscriptions(Child & aChild, + void UpdateProxiedSubscriptions(Child &aChild, const Ip6::Address *aOldMlrRegisteredAddresses, uint16_t aOldMlrRegisteredAddressNum); #endif @@ -134,42 +134,42 @@ class MlrManager : public InstanceLocator, private NonCopyable * @retval kErrorNoBufs If insufficient message buffers available. * */ - Error RegisterMulticastListeners(const otIp6Address * aAddresses, + Error RegisterMulticastListeners(const otIp6Address *aAddresses, uint8_t aAddressNum, - const uint32_t * aTimeout, + const uint32_t *aTimeout, otIp6RegisterMulticastListenersCallback aCallback, - void * aContext); + void *aContext); #endif private: void HandleNotifierEvents(Events aEvents); void SendMulticastListenerRegistration(void); - Error SendMulticastListenerRegistrationMessage(const otIp6Address * aAddresses, + Error SendMulticastListenerRegistrationMessage(const otIp6Address *aAddresses, uint8_t aAddressNum, - const uint32_t * aTimeout, + const uint32_t *aTimeout, Coap::ResponseHandler aResponseHandler, - void * aResponseContext); + void *aResponseContext); - static void HandleMulticastListenerRegistrationResponse(void * aContext, - otMessage * aMessage, + static void HandleMulticastListenerRegistrationResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); - void HandleMulticastListenerRegistrationResponse(Coap::Message * aMessage, + void HandleMulticastListenerRegistrationResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); static Error ParseMulticastListenerRegistrationResponse(Error aResult, Coap::Message *aMessage, - uint8_t & aStatus, - Ip6::Address * aFailedAddresses, - uint8_t & aFailedAddressNum); + uint8_t &aStatus, + Ip6::Address *aFailedAddresses, + uint8_t &aFailedAddressNum); #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE - static void HandleRegisterMulticastListenersResponse(void * aContext, - otMessage * aMessage, + static void HandleRegisterMulticastListenersResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); - void HandleRegisterMulticastListenersResponse(otMessage * aMessage, + void HandleRegisterMulticastListenersResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); #endif @@ -193,7 +193,7 @@ class MlrManager : public InstanceLocator, private NonCopyable uint8_t aFailedAddressNum); void AppendToUniqueAddressList(Ip6::Address (&aAddresses)[Ip6AddressesTlv::kMaxAddresses], - uint8_t & aAddressNum, + uint8_t &aAddressNum, const Ip6::Address &aAddress); static bool AddressListContains(const Ip6::Address *aAddressList, uint8_t aAddressListSize, @@ -214,8 +214,7 @@ class MlrManager : public InstanceLocator, private NonCopyable uint8_t aFailedAddressNum); #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE - otIp6RegisterMulticastListenersCallback mRegisterMulticastListenersCallback; - void * mRegisterMulticastListenersContext; + Callback mRegisterMulticastListenersCallback; #endif uint32_t mReregistrationDelay; diff --git a/src/core/thread/neighbor_table.cpp b/src/core/thread/neighbor_table.cpp index f7856a11dc8..9355473bb79 100644 --- a/src/core/thread/neighbor_table.cpp +++ b/src/core/thread/neighbor_table.cpp @@ -136,7 +136,7 @@ Neighbor *NeighborTable::FindChildOrRouter(const Neighbor::AddressMatcher &aMatc Neighbor *NeighborTable::FindNeighbor(const Ip6::Address &aIp6Address, Neighbor::StateFilter aFilter) { - Neighbor * neighbor = nullptr; + Neighbor *neighbor = nullptr; Mac::Address macAddress; if (aIp6Address.IsLinkLocal()) @@ -172,7 +172,7 @@ Neighbor *NeighborTable::FindRxOnlyNeighborRouter(const Mac::Address &aMacAddres Neighbor *neighbor = nullptr; VerifyOrExit(Get().IsChild()); - neighbor = Get().GetNeighbor(aMacAddress); + neighbor = Get().FindNeighbor(aMacAddress); exit: return neighbor; @@ -213,7 +213,7 @@ Error NeighborTable::GetNextNeighborInfo(otNeighborInfoIterator &aIterator, Neig for (index = -aIterator; index <= Mle::kMaxRouterId; index++) { - Router *router = Get().GetRouter(static_cast(index)); + Router *router = Get().FindRouterById(static_cast(index)); if (router != nullptr && router->IsStateValid()) { @@ -271,6 +271,7 @@ void NeighborTable::Signal(Event aEvent, const Neighbor &aNeighbor) case kChildRemoved: case kChildModeChanged: #if OPENTHREAD_FTD + OT_ASSERT(Get().Contains(aNeighbor)); static_cast(info.mInfo.mChild).SetFrom(static_cast(aNeighbor)); #endif break; diff --git a/src/core/thread/neighbor_table.hpp b/src/core/thread/neighbor_table.hpp index abe2df67f9d..d1e31d55790 100644 --- a/src/core/thread/neighbor_table.hpp +++ b/src/core/thread/neighbor_table.hpp @@ -124,7 +124,7 @@ class NeighborTable : public InstanceLocator, private NonCopyable * @returns A pointer to the `Neighbor` corresponding to @p aMacAddress, `nullptr` otherwise. * */ - Neighbor *FindParent(const Mac::Address & aMacAddress, + Neighbor *FindParent(const Mac::Address &aMacAddress, Neighbor::StateFilter aFilter = Neighbor::kInStateValidOrRestoring); /** @@ -160,7 +160,7 @@ class NeighborTable : public InstanceLocator, private NonCopyable * @returns A pointer to the `Neighbor` corresponding to @p aMacAddress, `nullptr` otherwise. * */ - Neighbor *FindNeighbor(const Mac::Address & aMacAddress, + Neighbor *FindNeighbor(const Mac::Address &aMacAddress, Neighbor::StateFilter aFilter = Neighbor::kInStateValidOrRestoring); #if OPENTHREAD_FTD @@ -174,7 +174,7 @@ class NeighborTable : public InstanceLocator, private NonCopyable * @returns A pointer to the `Neighbor` corresponding to @p aIp6Address, `nullptr` otherwise. * */ - Neighbor *FindNeighbor(const Ip6::Address & aIp6Address, + Neighbor *FindNeighbor(const Ip6::Address &aIp6Address, Neighbor::StateFilter aFilter = Neighbor::kInStateValidOrRestoring); /** diff --git a/src/core/thread/network_data.cpp b/src/core/thread/network_data.cpp index 3c96a9284ce..009e67515ec 100644 --- a/src/core/thread/network_data.cpp +++ b/src/core/thread/network_data.cpp @@ -92,6 +92,7 @@ Error NetworkData::GetNextOnMeshPrefix(Iterator &aIterator, uint16_t aRloc16, On config.mOnMeshPrefix = &aConfig; config.mExternalRoute = nullptr; config.mService = nullptr; + config.mLowpanContext = nullptr; return Iterate(aIterator, aRloc16, config); } @@ -108,6 +109,7 @@ Error NetworkData::GetNextExternalRoute(Iterator &aIterator, uint16_t aRloc16, E config.mOnMeshPrefix = nullptr; config.mExternalRoute = &aConfig; config.mService = nullptr; + config.mLowpanContext = nullptr; return Iterate(aIterator, aRloc16, config); } @@ -124,10 +126,23 @@ Error NetworkData::GetNextService(Iterator &aIterator, uint16_t aRloc16, Service config.mOnMeshPrefix = nullptr; config.mExternalRoute = nullptr; config.mService = &aConfig; + config.mLowpanContext = nullptr; return Iterate(aIterator, aRloc16, config); } +Error NetworkData::GetNextLowpanContextInfo(Iterator &aIterator, LowpanContextInfo &aContextInfo) const +{ + Config config; + + config.mOnMeshPrefix = nullptr; + config.mExternalRoute = nullptr; + config.mService = nullptr; + config.mLowpanContext = &aContextInfo; + + return Iterate(aIterator, Mac::kShortAddrBroadcast, config); +} + Error NetworkData::Iterate(Iterator &aIterator, uint16_t aRloc16, Config &aConfig) const { // Iterate to the next entry in Network Data matching `aRloc16` @@ -152,7 +167,8 @@ Error NetworkData::Iterate(Iterator &aIterator, uint16_t aRloc16, Config &aConfi switch (cur->GetType()) { case NetworkDataTlv::kTypePrefix: - if ((aConfig.mOnMeshPrefix != nullptr) || (aConfig.mExternalRoute != nullptr)) + if ((aConfig.mOnMeshPrefix != nullptr) || (aConfig.mExternalRoute != nullptr) || + (aConfig.mLowpanContext != nullptr)) { subTlvs = As(cur)->GetSubTlvs(); } @@ -174,7 +190,7 @@ Error NetworkData::Iterate(Iterator &aIterator, uint16_t aRloc16, Config &aConfi for (const NetworkDataTlv *subCur; subCur = iterator.GetSubTlv(subTlvs), (subCur + 1 <= cur->GetNext()) && (subCur->GetNext() <= cur->GetNext()); - iterator.AdvaceSubTlv(subTlvs)) + iterator.AdvanceSubTlv(subTlvs)) { if (cur->GetType() == NetworkDataTlv::kTypePrefix) { @@ -199,6 +215,7 @@ Error NetworkData::Iterate(Iterator &aIterator, uint16_t aRloc16, Config &aConfi aConfig.mExternalRoute = nullptr; aConfig.mService = nullptr; + aConfig.mLowpanContext = nullptr; aConfig.mOnMeshPrefix->SetFrom(*prefixTlv, *borderRouter, *borderRouterEntry); ExitNow(error = kErrorNone); @@ -223,8 +240,9 @@ Error NetworkData::Iterate(Iterator &aIterator, uint16_t aRloc16, Config &aConfi { const HasRouteEntry *hasRouteEntry = hasRoute->GetEntry(index); - aConfig.mOnMeshPrefix = nullptr; - aConfig.mService = nullptr; + aConfig.mOnMeshPrefix = nullptr; + aConfig.mService = nullptr; + aConfig.mLowpanContext = nullptr; aConfig.mExternalRoute->SetFrom(GetInstance(), *prefixTlv, *hasRoute, *hasRouteEntry); ExitNow(error = kErrorNone); @@ -234,6 +252,29 @@ Error NetworkData::Iterate(Iterator &aIterator, uint16_t aRloc16, Config &aConfi break; } + case NetworkDataTlv::kTypeContext: + { + const ContextTlv *contextTlv = As(subCur); + + if (aConfig.mLowpanContext == nullptr) + { + continue; + } + + if (iterator.IsNewEntry()) + { + aConfig.mOnMeshPrefix = nullptr; + aConfig.mExternalRoute = nullptr; + aConfig.mService = nullptr; + aConfig.mLowpanContext->SetFrom(*prefixTlv, *contextTlv); + + iterator.MarkEntryAsNotNew(); + ExitNow(error = kErrorNone); + } + + break; + } + default: break; } @@ -260,6 +301,7 @@ Error NetworkData::Iterate(Iterator &aIterator, uint16_t aRloc16, Config &aConfi { aConfig.mOnMeshPrefix = nullptr; aConfig.mExternalRoute = nullptr; + aConfig.mLowpanContext = nullptr; aConfig.mService->SetFrom(*service, *server); iterator.MarkEntryAsNotNew(); @@ -344,6 +386,7 @@ bool NetworkData::ContainsEntriesFrom(const NetworkData &aCompare, uint16_t aRlo config.mOnMeshPrefix = &prefix; config.mExternalRoute = &route; config.mService = &service; + config.mLowpanContext = nullptr; SuccessOrExit(aCompare.Iterate(iterator, aRloc16, config)); @@ -425,7 +468,7 @@ void MutableNetworkData::RemoveTemporaryDataIn(PrefixTlv &aPrefix) case NetworkDataTlv::kTypeBorderRouter: { BorderRouterTlv *borderRouter = As(cur); - ContextTlv * context = aPrefix.FindSubTlv(); + ContextTlv *context = aPrefix.FindSubTlv(); // Replace p_border_router_16 for (BorderRouterEntry *entry = borderRouter->GetFirstEntry(); entry <= borderRouter->GetLastEntry(); @@ -486,7 +529,7 @@ void MutableNetworkData::RemoveTemporaryDataIn(ServiceTlv &aService) switch (cur->GetType()) { case NetworkDataTlv::kTypeServer: - As(cur)->SetServer16(Mle::Mle::ServiceAlocFromId(aService.GetServiceId())); + As(cur)->SetServer16(Mle::ServiceAlocFromId(aService.GetServiceId())); break; default: @@ -540,7 +583,7 @@ const ServiceTlv *NetworkData::FindService(uint32_t aEnterpriseNumber, return serviceTlv; } -const ServiceTlv *NetworkData::FindNextService(const ServiceTlv * aPrevServiceTlv, +const ServiceTlv *NetworkData::FindNextService(const ServiceTlv *aPrevServiceTlv, uint32_t aEnterpriseNumber, const ServiceData &aServiceData, ServiceMatchMode aServiceMatchMode) const @@ -562,14 +605,14 @@ const ServiceTlv *NetworkData::FindNextService(const ServiceTlv * aPrevServiceTl return NetworkData(GetInstance(), tlvs, length).FindService(aEnterpriseNumber, aServiceData, aServiceMatchMode); } -const ServiceTlv *NetworkData::FindNextThreadService(const ServiceTlv * aPrevServiceTlv, +const ServiceTlv *NetworkData::FindNextThreadService(const ServiceTlv *aPrevServiceTlv, const ServiceData &aServiceData, ServiceMatchMode aServiceMatchMode) const { return FindNextService(aPrevServiceTlv, ServiceTlv::kThreadEnterpriseNumber, aServiceData, aServiceMatchMode); } -bool NetworkData::MatchService(const ServiceTlv & aServiceTlv, +bool NetworkData::MatchService(const ServiceTlv &aServiceTlv, uint32_t aEnterpriseNumber, const ServiceData &aServiceData, ServiceMatchMode aServiceMatchMode) @@ -630,46 +673,7 @@ void MutableNetworkData::Remove(void *aRemoveStart, uint8_t aRemoveLength) mLength -= aRemoveLength; } -void MutableNetworkData::RemoveTlv(NetworkDataTlv *aTlv) -{ - Remove(aTlv, aTlv->GetSize()); -} - -Error NetworkData::SendServerDataNotification(uint16_t aRloc16, - bool aAppendNetDataTlv, - Coap::ResponseHandler aHandler, - void * aContext) const -{ - Error error = kErrorNone; - Coap::Message * message; - Tmf::MessageInfo messageInfo(GetInstance()); - - message = Get().NewPriorityConfirmablePostMessage(UriPath::kServerData); - VerifyOrExit(message != nullptr, error = kErrorNoBufs); - - if (aAppendNetDataTlv) - { - ThreadTlv tlv; - tlv.SetType(ThreadTlv::kThreadNetworkData); - tlv.SetLength(mLength); - SuccessOrExit(error = message->Append(tlv)); - SuccessOrExit(error = message->AppendBytes(mTlvs, mLength)); - } - - if (aRloc16 != Mac::kShortAddrInvalid) - { - SuccessOrExit(error = Tlv::Append(*message, aRloc16)); - } - - IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); - SuccessOrExit(error = Get().SendMessage(*message, messageInfo, aHandler, aContext)); - - LogInfo("Sent server data notification"); - -exit: - FreeMessageOnError(message, error); - return error; -} +void MutableNetworkData::RemoveTlv(NetworkDataTlv *aTlv) { Remove(aTlv, aTlv->GetSize()); } Error NetworkData::GetNextServer(Iterator &aIterator, uint16_t &aRloc16) const { @@ -682,6 +686,7 @@ Error NetworkData::GetNextServer(Iterator &aIterator, uint16_t &aRloc16) const config.mOnMeshPrefix = &prefixConfig; config.mExternalRoute = &routeConfig; config.mService = &serviceConfig; + config.mLowpanContext = nullptr; SuccessOrExit(error = Iterate(aIterator, Mac::kShortAddrBroadcast, config)); @@ -736,11 +741,11 @@ Error NetworkData::FindBorderRouters(RoleFilter aRoleFilter, uint16_t aRlocs[], break; case kRouterRoleOnly: - VerifyOrExit(Mle::Mle::IsActiveRouter(aRloc16)); + VerifyOrExit(Mle::IsActiveRouter(aRloc16)); break; case kChildRoleOnly: - VerifyOrExit(!Mle::Mle::IsActiveRouter(aRloc16)); + VerifyOrExit(!Mle::IsActiveRouter(aRloc16)); break; } @@ -764,7 +769,7 @@ Error NetworkData::FindBorderRouters(RoleFilter aRoleFilter, uint16_t aRlocs[], private: RoleFilter mRoleFilter; - uint16_t * mRlocs; + uint16_t *mRlocs; uint8_t mLength; uint8_t mMaxLength; }; diff --git a/src/core/thread/network_data.hpp b/src/core/thread/network_data.hpp index cbe51330563..3c1db30e432 100644 --- a/src/core/thread/network_data.hpp +++ b/src/core/thread/network_data.hpp @@ -266,6 +266,18 @@ class NetworkData : public InstanceLocator */ Error GetNextService(Iterator &aIterator, uint16_t aRloc16, ServiceConfig &aConfig) const; + /** + * This method gets the next 6LoWPAN Context ID info in the Thread Network Data. + * + * @param[in,out] aIterator A reference to the Network Data iterator. + * @param[out] aContextInfo A reference to where the retrieved 6LoWPAN Context ID information will be placed. + * + * @retval kErrorNone Successfully found the next 6LoWPAN Context ID info. + * @retval kErrorNotFound No subsequent 6LoWPAN Context info exists in the partition's Network Data. + * + */ + Error GetNextLowpanContextInfo(Iterator &aIterator, LowpanContextInfo &aContextInfo) const; + /** * This method indicates whether or not the Thread Network Data contains a given on mesh prefix entry. * @@ -465,7 +477,7 @@ class NetworkData : public InstanceLocator * @returns A pointer to the next matching Service TLV if one is found or `nullptr` if it cannot be found. * */ - const ServiceTlv *FindNextService(const ServiceTlv * aPrevServiceTlv, + const ServiceTlv *FindNextService(const ServiceTlv *aPrevServiceTlv, uint32_t aEnterpriseNumber, const ServiceData &aServiceData, ServiceMatchMode aServiceMatchMode) const; @@ -484,27 +496,10 @@ class NetworkData : public InstanceLocator * @returns A pointer to the next matching Thread Service TLV if one is found or `nullptr` if it cannot be found. * */ - const ServiceTlv *FindNextThreadService(const ServiceTlv * aPrevServiceTlv, + const ServiceTlv *FindNextThreadService(const ServiceTlv *aPrevServiceTlv, const ServiceData &aServiceData, ServiceMatchMode aServiceMatchMode) const; - /** - * This method sends a Server Data Notification message to the Leader. - * - * @param[in] aRloc16 The old RLOC16 value that was previously registered. - * @param[in] aAppendNetDataTlv Indicates whether or not to append Thread Network Data TLV to the message. - * @param[in] aHandler A function pointer that is called when the transaction ends. - * @param[in] aContext A pointer to arbitrary context information. - * - * @retval kErrorNone Successfully enqueued the notification message. - * @retval kErrorNoBufs Insufficient message buffers to generate the notification message. - * - */ - Error SendServerDataNotification(uint16_t aRloc16, - bool aAppendNetDataTlv, - Coap::ResponseHandler aHandler, - void * aContext) const; - private: class NetworkDataIterator { @@ -532,7 +527,7 @@ class NetworkData : public InstanceLocator GetSubTlvOffset()); } - void AdvaceSubTlv(const NetworkDataTlv *aSubTlvs) + void AdvanceSubTlv(const NetworkDataTlv *aSubTlvs) { SaveSubTlvOffset(GetSubTlv(aSubTlvs)->GetNext(), aSubTlvs); SetEntryIndex(0); @@ -571,14 +566,15 @@ class NetworkData : public InstanceLocator struct Config { - OnMeshPrefixConfig * mOnMeshPrefix; + OnMeshPrefixConfig *mOnMeshPrefix; ExternalRouteConfig *mExternalRoute; - ServiceConfig * mService; + ServiceConfig *mService; + LowpanContextInfo *mLowpanContext; }; Error Iterate(Iterator &aIterator, uint16_t aRloc16, Config &aConfig) const; - static bool MatchService(const ServiceTlv & aServiceTlv, + static bool MatchService(const ServiceTlv &aServiceTlv, uint32_t aEnterpriseNumber, const ServiceData &aServiceData, ServiceMatchMode aServiceMatchMode); diff --git a/src/core/thread/network_data_leader.cpp b/src/core/thread/network_data_leader.cpp index 0207c390f88..351ea69a79d 100644 --- a/src/core/thread/network_data_leader.cpp +++ b/src/core/thread/network_data_leader.cpp @@ -60,13 +60,13 @@ void LeaderBase::Reset(void) mVersion = Random::NonCrypto::GetUint8(); mStableVersion = Random::NonCrypto::GetUint8(); SetLength(0); - Get().Signal(kEventThreadNetdataChanged); + SignalNetDataChanged(); } Error LeaderBase::GetServiceId(uint32_t aEnterpriseNumber, const ServiceData &aServiceData, bool aServerStable, - uint8_t & aServiceId) const + uint8_t &aServiceId) const { Error error = kErrorNotFound; Iterator iterator = kIteratorInit; @@ -102,7 +102,8 @@ Error LeaderBase::GetPreferredNat64Prefix(ExternalRouteConfig &aConfig) const continue; } - if ((error == kErrorNotFound) || (config.mPreference > aConfig.mPreference)) + if ((error == kErrorNotFound) || (config.mPreference > aConfig.mPreference) || + (config.mPreference == aConfig.mPreference && config.GetPrefix() < aConfig.GetPrefix())) { aConfig = config; error = kErrorNone; @@ -112,8 +113,14 @@ Error LeaderBase::GetPreferredNat64Prefix(ExternalRouteConfig &aConfig) const return error; } -const PrefixTlv *LeaderBase::FindNextMatchingPrefix(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const +const PrefixTlv *LeaderBase::FindNextMatchingPrefixTlv(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const { + // This method iterates over Prefix TLVs which match a given IPv6 + // `aAddress`. If `aPrevTlv` is `nullptr` we start from the + // beginning. Otherwise, we search for a match after `aPrevTlv`. + // This method returns a pointer to the next matching Prefix TLV + // when found, or `nullptr` if no match is found. + const PrefixTlv *prefixTlv; TlvIterator tlvIterator((aPrevTlv == nullptr) ? GetTlvsStart() : aPrevTlv->GetNext(), GetTlvsEnd()); @@ -130,32 +137,31 @@ const PrefixTlv *LeaderBase::FindNextMatchingPrefix(const Ip6::Address &aAddress Error LeaderBase::GetContext(const Ip6::Address &aAddress, Lowpan::Context &aContext) const { - const PrefixTlv * prefix = nullptr; + const PrefixTlv *prefixTlv = nullptr; const ContextTlv *contextTlv; aContext.mPrefix.SetLength(0); if (Get().IsMeshLocalAddress(aAddress)) { - aContext.mPrefix.Set(Get().GetMeshLocalPrefix()); - aContext.mContextId = Mle::kMeshLocalPrefixContextId; - aContext.mCompressFlag = true; + GetContextForMeshLocalPrefix(aContext); } - while ((prefix = FindNextMatchingPrefix(aAddress, prefix)) != nullptr) + while ((prefixTlv = FindNextMatchingPrefixTlv(aAddress, prefixTlv)) != nullptr) { - contextTlv = prefix->FindSubTlv(); + contextTlv = prefixTlv->FindSubTlv(); if (contextTlv == nullptr) { continue; } - if (prefix->GetPrefixLength() > aContext.mPrefix.GetLength()) + if (prefixTlv->GetPrefixLength() > aContext.mPrefix.GetLength()) { - aContext.mPrefix.Set(prefix->GetPrefix(), prefix->GetPrefixLength()); + prefixTlv->CopyPrefixTo(aContext.mPrefix); aContext.mContextId = contextTlv->GetContextId(); aContext.mCompressFlag = contextTlv->IsCompress(); + aContext.mIsValid = true; } } @@ -166,28 +172,27 @@ Error LeaderBase::GetContext(uint8_t aContextId, Lowpan::Context &aContext) cons { Error error = kErrorNotFound; TlvIterator tlvIterator(GetTlvsStart(), GetTlvsEnd()); - const PrefixTlv *prefix; + const PrefixTlv *prefixTlv; if (aContextId == Mle::kMeshLocalPrefixContextId) { - aContext.mPrefix.Set(Get().GetMeshLocalPrefix()); - aContext.mContextId = Mle::kMeshLocalPrefixContextId; - aContext.mCompressFlag = true; + GetContextForMeshLocalPrefix(aContext); ExitNow(error = kErrorNone); } - while ((prefix = tlvIterator.Iterate()) != nullptr) + while ((prefixTlv = tlvIterator.Iterate()) != nullptr) { - const ContextTlv *contextTlv = prefix->FindSubTlv(); + const ContextTlv *contextTlv = prefixTlv->FindSubTlv(); if ((contextTlv == nullptr) || (contextTlv->GetContextId() != aContextId)) { continue; } - aContext.mPrefix.Set(prefix->GetPrefix(), prefix->GetPrefixLength()); + prefixTlv->CopyPrefixTo(aContext.mPrefix); aContext.mContextId = contextTlv->GetContextId(); aContext.mCompressFlag = contextTlv->IsCompress(); + aContext.mIsValid = true; ExitNow(error = kErrorNone); } @@ -195,62 +200,57 @@ Error LeaderBase::GetContext(uint8_t aContextId, Lowpan::Context &aContext) cons return error; } +void LeaderBase::GetContextForMeshLocalPrefix(Lowpan::Context &aContext) const +{ + aContext.mPrefix.Set(Get().GetMeshLocalPrefix()); + aContext.mContextId = Mle::kMeshLocalPrefixContextId; + aContext.mCompressFlag = true; + aContext.mIsValid = true; +} + bool LeaderBase::IsOnMesh(const Ip6::Address &aAddress) const { - const PrefixTlv *prefix = nullptr; - bool rval = false; + const PrefixTlv *prefixTlv = nullptr; + bool isOnMesh = false; - VerifyOrExit(!Get().IsMeshLocalAddress(aAddress), rval = true); + VerifyOrExit(!Get().IsMeshLocalAddress(aAddress), isOnMesh = true); - while ((prefix = FindNextMatchingPrefix(aAddress, prefix)) != nullptr) + while ((prefixTlv = FindNextMatchingPrefixTlv(aAddress, prefixTlv)) != nullptr) { - // check both stable and temporary Border Router TLVs - for (int i = 0; i < 2; i++) - { - const BorderRouterTlv *borderRouter = prefix->FindSubTlv(/* aStable */ (i == 0)); + TlvIterator subTlvIterator(*prefixTlv); + const BorderRouterTlv *brTlv; - if (borderRouter == nullptr) - { - continue; - } - - for (const BorderRouterEntry *entry = borderRouter->GetFirstEntry(); entry <= borderRouter->GetLastEntry(); + while ((brTlv = subTlvIterator.Iterate()) != nullptr) + { + for (const BorderRouterEntry *entry = brTlv->GetFirstEntry(); entry <= brTlv->GetLastEntry(); entry = entry->GetNext()) { if (entry->IsOnMesh()) { - ExitNow(rval = true); + ExitNow(isOnMesh = true); } } } } exit: - return rval; + return isOnMesh; } -Error LeaderBase::RouteLookup(const Ip6::Address &aSource, - const Ip6::Address &aDestination, - uint8_t * aPrefixMatchLength, - uint16_t * aRloc16) const +Error LeaderBase::RouteLookup(const Ip6::Address &aSource, const Ip6::Address &aDestination, uint16_t &aRloc16) const { - Error error = kErrorNoRoute; - const PrefixTlv *prefix = nullptr; + Error error = kErrorNoRoute; + const PrefixTlv *prefixTlv = nullptr; - while ((prefix = FindNextMatchingPrefix(aSource, prefix)) != nullptr) + while ((prefixTlv = FindNextMatchingPrefixTlv(aSource, prefixTlv)) != nullptr) { - if (ExternalRouteLookup(prefix->GetDomainId(), aDestination, aPrefixMatchLength, aRloc16) == kErrorNone) + if (ExternalRouteLookup(prefixTlv->GetDomainId(), aDestination, aRloc16) == kErrorNone) { ExitNow(error = kErrorNone); } - if (DefaultRouteLookup(*prefix, aRloc16) == kErrorNone) + if (DefaultRouteLookup(*prefixTlv, aRloc16) == kErrorNone) { - if (aPrefixMatchLength) - { - *aPrefixMatchLength = 0; - } - ExitNow(error = kErrorNone); } } @@ -259,18 +259,62 @@ Error LeaderBase::RouteLookup(const Ip6::Address &aSource, return error; } -Error LeaderBase::ExternalRouteLookup(uint8_t aDomainId, - const Ip6::Address &aDestination, - uint8_t * aPrefixMatchLength, - uint16_t * aRloc16) const +template +int LeaderBase::CompareRouteEntries(const EntryType &aFirst, const EntryType &aSecond) const +{ + // `EntryType` can be `HasRouteEntry` or `BorderRouterEntry`. + + return CompareRouteEntries(aFirst.GetPreference(), aFirst.GetRloc(), aSecond.GetPreference(), aSecond.GetRloc()); +} + +int LeaderBase::CompareRouteEntries(int8_t aFirstPreference, + uint16_t aFirstRloc, + int8_t aSecondPreference, + uint16_t aSecondRloc) const +{ + // Performs three-way comparison between two BR entries. + + int result; + + // Prefer the entry with higher preference. + + result = ThreeWayCompare(aFirstPreference, aSecondPreference); + VerifyOrExit(result == 0); + +#if OPENTHREAD_MTD + // On MTD, prefer the BR that is this device itself. This handles + // the uncommon case where an MTD itself may be acting as BR. + + result = ThreeWayCompare((aFirstRloc == Get().GetRloc16()), (aSecondRloc == Get().GetRloc16())); +#endif + +#if OPENTHREAD_FTD + // If all the same, prefer the one with lower mesh path cost. + // Lower cost is preferred so we pass the second entry's cost as + // the first argument in the call to `ThreeWayCompare()`, i.e., + // if the second entry's cost is larger, we return 1 indicating + // that the first entry is preferred over the second one. + + result = ThreeWayCompare(Get().GetPathCost(aSecondRloc), Get().GetPathCost(aFirstRloc)); + VerifyOrExit(result == 0); + + // If all the same, prefer the BR acting as a router over an + // end device. + result = ThreeWayCompare(Mle::IsActiveRouter(aFirstRloc), Mle::IsActiveRouter(aSecondRloc)); +#endif + +exit: + return result; +} + +Error LeaderBase::ExternalRouteLookup(uint8_t aDomainId, const Ip6::Address &aDestination, uint16_t &aRloc16) const { - Error error = kErrorNoRoute; - TlvIterator tlvIterator(GetTlvsStart(), GetTlvsEnd()); - const PrefixTlv * prefixTlv; + Error error = kErrorNoRoute; + const PrefixTlv *prefixTlv = nullptr; const HasRouteEntry *bestRouteEntry = nullptr; uint8_t bestMatchLength = 0; - while ((prefixTlv = tlvIterator.Iterate()) != nullptr) + while ((prefixTlv = FindNextMatchingPrefixTlv(aDestination, prefixTlv)) != nullptr) { const HasRouteTlv *hasRoute; uint8_t prefixLength = prefixTlv->GetPrefixLength(); @@ -281,11 +325,6 @@ Error LeaderBase::ExternalRouteLookup(uint8_t aDomainId, continue; } - if (!aDestination.MatchesPrefix(prefixTlv->GetPrefix(), prefixLength)) - { - continue; - } - if ((bestRouteEntry != nullptr) && (prefixLength <= bestMatchLength)) { continue; @@ -296,12 +335,8 @@ Error LeaderBase::ExternalRouteLookup(uint8_t aDomainId, for (const HasRouteEntry *entry = hasRoute->GetFirstEntry(); entry <= hasRoute->GetLastEntry(); entry = entry->GetNext()) { - if (bestRouteEntry == nullptr || entry->GetPreference() > bestRouteEntry->GetPreference() || - (entry->GetPreference() == bestRouteEntry->GetPreference() && - (entry->GetRloc() == Get().GetRloc16() || - (bestRouteEntry->GetRloc() != Get().GetRloc16() && - Get().GetCost(entry->GetRloc()) < - Get().GetCost(bestRouteEntry->GetRloc()))))) + if ((bestRouteEntry == nullptr) || (prefixLength > bestMatchLength) || + CompareRouteEntries(*entry, *bestRouteEntry) > 0) { bestRouteEntry = entry; bestMatchLength = prefixLength; @@ -312,32 +347,23 @@ Error LeaderBase::ExternalRouteLookup(uint8_t aDomainId, if (bestRouteEntry != nullptr) { - if (aRloc16 != nullptr) - { - *aRloc16 = bestRouteEntry->GetRloc(); - } - - if (aPrefixMatchLength != nullptr) - { - *aPrefixMatchLength = bestMatchLength; - } - - error = kErrorNone; + aRloc16 = bestRouteEntry->GetRloc(); + error = kErrorNone; } return error; } -Error LeaderBase::DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t *aRloc16) const +Error LeaderBase::DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t &aRloc16) const { Error error = kErrorNoRoute; TlvIterator subTlvIterator(aPrefix); - const BorderRouterTlv * borderRouter; + const BorderRouterTlv *brTlv; const BorderRouterEntry *route = nullptr; - while ((borderRouter = subTlvIterator.Iterate()) != nullptr) + while ((brTlv = subTlvIterator.Iterate()) != nullptr) { - for (const BorderRouterEntry *entry = borderRouter->GetFirstEntry(); entry <= borderRouter->GetLastEntry(); + for (const BorderRouterEntry *entry = brTlv->GetFirstEntry(); entry <= brTlv->GetLastEntry(); entry = entry->GetNext()) { if (!entry->IsDefaultRoute()) @@ -345,11 +371,7 @@ Error LeaderBase::DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t *aRloc16 continue; } - if (route == nullptr || entry->GetPreference() > route->GetPreference() || - (entry->GetPreference() == route->GetPreference() && - (entry->GetRloc() == Get().GetRloc16() || - (route->GetRloc() != Get().GetRloc16() && - Get().GetCost(entry->GetRloc()) < Get().GetCost(route->GetRloc()))))) + if (route == nullptr || CompareRouteEntries(*entry, *route) > 0) { route = entry; } @@ -358,12 +380,8 @@ Error LeaderBase::DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t *aRloc16 if (route != nullptr) { - if (aRloc16 != nullptr) - { - *aRloc16 = route->GetRloc(); - } - - error = kErrorNone; + aRloc16 = route->GetRloc(); + error = kErrorNone; } return error; @@ -373,18 +391,15 @@ Error LeaderBase::SetNetworkData(uint8_t aVersion, uint8_t aStableVersion, Type aType, const Message &aMessage, - uint16_t aMessageOffset) + uint16_t aOffset, + uint16_t aLength) { - Error error = kErrorNone; - Mle::Tlv tlv; - uint16_t length; + Error error = kErrorNone; - SuccessOrExit(error = aMessage.Read(aMessageOffset, tlv)); + VerifyOrExit(aLength <= kMaxSize, error = kErrorParse); + SuccessOrExit(error = aMessage.Read(aOffset, GetBytes(), aLength)); - length = aMessage.ReadBytes(aMessageOffset + sizeof(tlv), GetBytes(), tlv.GetLength()); - VerifyOrExit(length == tlv.GetLength(), error = kErrorParse); - - SetLength(tlv.GetLength()); + SetLength(static_cast(aLength)); mVersion = aVersion; mStableVersion = aStableVersion; @@ -402,7 +417,7 @@ Error LeaderBase::SetNetworkData(uint8_t aVersion, DumpDebg("SetNetworkData", GetBytes(), GetLength()); - Get().Signal(kEventThreadNetdataChanged); + SignalNetDataChanged(); exit: return error; @@ -427,7 +442,7 @@ Error LeaderBase::SetCommissioningData(const uint8_t *aValue, uint8_t aValueLeng } mVersion++; - Get().Signal(kEventThreadNetdataChanged); + SignalNetDataChanged(); exit: return error; @@ -440,7 +455,7 @@ const CommissioningDataTlv *LeaderBase::GetCommissioningData(void) const const MeshCoP::Tlv *LeaderBase::GetCommissioningDataSubTlv(MeshCoP::Tlv::Type aType) const { - const MeshCoP::Tlv * rval = nullptr; + const MeshCoP::Tlv *rval = nullptr; const NetworkDataTlv *commissioningDataTlv; commissioningDataTlv = GetCommissioningData(); @@ -488,7 +503,7 @@ void LeaderBase::RemoveCommissioningData(void) Error LeaderBase::SteeringDataCheck(const FilterIndexes &aFilterIndexes) const { Error error = kErrorNone; - const MeshCoP::Tlv * steeringDataTlv; + const MeshCoP::Tlv *steeringDataTlv; MeshCoP::SteeringData steeringData; steeringDataTlv = GetCommissioningDataSubTlv(MeshCoP::Tlv::kSteeringData); @@ -522,5 +537,11 @@ Error LeaderBase::SteeringDataCheckJoiner(const MeshCoP::JoinerDiscerner &aDisce return SteeringDataCheck(filterIndexes); } +void LeaderBase::SignalNetDataChanged(void) +{ + mMaxLength = Max(mMaxLength, GetLength()); + Get().Signal(kEventThreadNetdataChanged); +} + } // namespace NetworkData } // namespace ot diff --git a/src/core/thread/network_data_leader.hpp b/src/core/thread/network_data_leader.hpp index 54765ef7340..92dae938220 100644 --- a/src/core/thread/network_data_leader.hpp +++ b/src/core/thread/network_data_leader.hpp @@ -74,6 +74,7 @@ class LeaderBase : public MutableNetworkData */ explicit LeaderBase(Instance &aInstance) : MutableNetworkData(aInstance, mTlvBuffer, 0, sizeof(mTlvBuffer)) + , mMaxLength(0) { Reset(); } @@ -84,6 +85,23 @@ class LeaderBase : public MutableNetworkData */ void Reset(void); + /** + * This method returns the maximum observed Network Data length since OT stack initialization or since the last + * call to `ResetMaxLength()`. + * + * @returns The maximum observed Network Data length (high water mark for Network Data length). + * + */ + uint8_t GetMaxLength(void) const { return mMaxLength; } + + /** + * This method resets the tracked maximum Network Data Length. + * + * @sa GetMaxLength + * + */ + void ResetMaxLength(void) { mMaxLength = GetLength(); } + /** * This method returns the Data Version value for a type (full set or stable subset). * @@ -134,36 +152,34 @@ class LeaderBase : public MutableNetworkData * * @param[in] aSource A reference to the IPv6 source address. * @param[in] aDestination A reference to the IPv6 destination address. - * @param[out] aPrefixMatchLength A pointer to output the longest prefix match length in bits. - * @param[out] aRloc16 A pointer to the RLOC16 for the selected route. + * @param[out] aRloc16 A reference to return the RLOC16 for the selected route. * - * @retval kErrorNone Successfully found a route. + * @retval kErrorNone Successfully found a route. @p aRloc16 is updated. * @retval kErrorNoRoute No valid route was found. * */ - Error RouteLookup(const Ip6::Address &aSource, - const Ip6::Address &aDestination, - uint8_t * aPrefixMatchLength, - uint16_t * aRloc16) const; + Error RouteLookup(const Ip6::Address &aSource, const Ip6::Address &aDestination, uint16_t &aRloc16) const; /** - * This method is used by non-Leader devices to set newly received Network Data from the Leader. + * This method is used by non-Leader devices to set Network Data by reading it from a message from Leader. * * @param[in] aVersion The Version value. * @param[in] aStableVersion The Stable Version value. * @param[in] aType The Network Data type to set, the full set or stable subset. - * @param[in] aMessage A reference to the MLE message. - * @param[in] aMessageOffset The offset in @p aMessage for the Network Data TLV. + * @param[in] aMessage A reference to the message. + * @param[in] aOffset The offset in @p aMessage pointing to start of Network Data. + * @param[in] aLength The length of Network Data. * * @retval kErrorNone Successfully set the network data. - * @retval kErrorParse Network Data TLV in @p aMessage is not valid. + * @retval kErrorParse Network Data in @p aMessage is not valid. * */ Error SetNetworkData(uint8_t aVersion, uint8_t aStableVersion, Type aType, const Message &aMessage, - uint16_t aMessageOffset); + uint16_t aOffset, + uint16_t aLength); /** * This method returns a pointer to the Commissioning Data. @@ -265,7 +281,7 @@ class LeaderBase : public MutableNetworkData Error GetServiceId(uint32_t aEnterpriseNumber, const ServiceData &aServiceData, bool aServerStable, - uint8_t & aServiceId) const; + uint8_t &aServiceId) const; /** * This methods gets the preferred NAT64 prefix from network data. @@ -282,24 +298,31 @@ class LeaderBase : public MutableNetworkData Error GetPreferredNat64Prefix(ExternalRouteConfig &aConfig) const; protected: + void SignalNetDataChanged(void); + uint8_t mStableVersion; uint8_t mVersion; private: using FilterIndexes = MeshCoP::SteeringData::HashBitIndexes; - const PrefixTlv *FindNextMatchingPrefix(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const; + const PrefixTlv *FindNextMatchingPrefixTlv(const Ip6::Address &aAddress, const PrefixTlv *aPrevTlv) const; void RemoveCommissioningData(void); - Error ExternalRouteLookup(uint8_t aDomainId, - const Ip6::Address &aDestination, - uint8_t * aPrefixMatchLength, - uint16_t * aRloc16) const; - Error DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t *aRloc16) const; + template int CompareRouteEntries(const EntryType &aFirst, const EntryType &aSecond) const; + int CompareRouteEntries(int8_t aFirstPreference, + uint16_t aFirstRloc, + int8_t aSecondPreference, + uint16_t aSecondRloc) const; + + Error ExternalRouteLookup(uint8_t aDomainId, const Ip6::Address &aDestination, uint16_t &aRloc16) const; + Error DefaultRouteLookup(const PrefixTlv &aPrefix, uint16_t &aRloc16) const; Error SteeringDataCheck(const FilterIndexes &aFilterIndexes) const; + void GetContextForMeshLocalPrefix(Lowpan::Context &aContext) const; uint8_t mTlvBuffer[kMaxSize]; + uint8_t mMaxLength; }; /** diff --git a/src/core/thread/network_data_leader_ftd.cpp b/src/core/thread/network_data_leader_ftd.cpp index dac504d7610..39a033ded60 100644 --- a/src/core/thread/network_data_leader_ftd.cpp +++ b/src/core/thread/network_data_leader_ftd.cpp @@ -61,10 +61,8 @@ RegisterLogModule("NetworkData"); Leader::Leader(Instance &aInstance) : LeaderBase(aInstance) , mWaitingForNetDataSync(false) - , mTimer(aInstance, Leader::HandleTimer) - , mServerData(UriPath::kServerData, &Leader::HandleServerData, this) - , mCommissioningDataGet(UriPath::kCommissionerGet, &Leader::HandleCommissioningGet, this) - , mCommissioningDataSet(UriPath::kCommissionerSet, &Leader::HandleCommissioningSet, this) + , mContextIds(aInstance) + , mTimer(aInstance) { Reset(); } @@ -73,9 +71,7 @@ void Leader::Reset(void) { LeaderBase::Reset(); - memset(reinterpret_cast(mContextLastUsed), 0, sizeof(mContextLastUsed)); - mContextUsed = 0; - mContextIdReuseDelay = kContextIdReuseDelay; + mContextIds.Clear(); } void Leader::Start(Mle::LeaderStartMode aStartMode) @@ -86,17 +82,6 @@ void Leader::Start(Mle::LeaderStartMode aStartMode) { mTimer.Start(kMaxNetDataSyncWait); } - - Get().AddResource(mServerData); - Get().AddResource(mCommissioningDataGet); - Get().AddResource(mCommissioningDataSet); -} - -void Leader::Stop(void) -{ - Get().RemoveResource(mServerData); - Get().RemoveResource(mCommissioningDataGet); - Get().RemoveResource(mCommissioningDataSet); } void Leader::IncrementVersion(void) @@ -131,7 +116,7 @@ void Leader::IncrementVersions(bool aIncludeStable) } mVersion++; - Get().Signal(kEventThreadNetdataChanged); + SignalNetDataChanged(); } void Leader::RemoveBorderRouter(uint16_t aRloc16, MatchMode aMatchMode) @@ -142,19 +127,14 @@ void Leader::RemoveBorderRouter(uint16_t aRloc16, MatchMode aMatchMode) IncrementVersions(flags); } -void Leader::HandleServerData(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleServerData(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void Leader::HandleServerData(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> void Leader::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { ThreadNetworkDataTlv networkDataTlv; uint16_t rloc16; - LogInfo("Received network data registration"); + VerifyOrExit(Get().IsLeader() && !mWaitingForNetDataSync); - VerifyOrExit(!mWaitingForNetDataSync); + LogInfo("Received %s", UriToString()); VerifyOrExit(aMessageInfo.GetPeerAddr().GetIid().IsRoutingLocator()); @@ -182,18 +162,13 @@ void Leader::HandleServerData(Coap::Message &aMessage, const Ip6::MessageInfo &a SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); - LogInfo("Sent network data registration acknowledgment"); + LogInfo("Sent %s ack", UriToString()); exit: return; } -void Leader::HandleCommissioningSet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleCommissioningSet(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void Leader::HandleCommissioningSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> void Leader::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { uint16_t offset = aMessage.GetOffset(); uint16_t length = aMessage.GetLength() - aMessage.GetOffset(); @@ -202,13 +177,14 @@ void Leader::HandleCommissioningSet(Coap::Message &aMessage, const Ip6::MessageI bool hasSessionId = false; bool hasValidTlv = false; uint16_t sessionId = 0; - CommissioningDataTlv * commDataTlv; + CommissioningDataTlv *commDataTlv; MeshCoP::Tlv *cur; MeshCoP::Tlv *end; + VerifyOrExit(Get().IsLeader() && !mWaitingForNetDataSync); + VerifyOrExit(length <= sizeof(tlvs)); - VerifyOrExit(Get().IsLeader()); aMessage.ReadBytes(offset, tlvs, length); @@ -290,31 +266,31 @@ void Leader::HandleCommissioningSet(Coap::Message &aMessage, const Ip6::MessageI } } -void Leader::HandleCommissioningGet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleCommissioningGet(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void Leader::HandleCommissioningGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +template <> void Leader::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { uint16_t length = 0; uint16_t offset; + VerifyOrExit(Get().IsLeader() && !mWaitingForNetDataSync); + SuccessOrExit(Tlv::FindTlvValueOffset(aMessage, MeshCoP::Tlv::kGet, offset, length)); aMessage.SetOffset(offset); exit: - SendCommissioningGetResponse(aMessage, length, aMessageInfo); + if (Get().IsLeader()) + { + SendCommissioningGetResponse(aMessage, length, aMessageInfo); + } } -void Leader::SendCommissioningGetResponse(const Coap::Message & aRequest, +void Leader::SendCommissioningGetResponse(const Coap::Message &aRequest, uint16_t aLength, const Ip6::MessageInfo &aMessageInfo) { Error error = kErrorNone; - Coap::Message * message; + Coap::Message *message; CommissioningDataTlv *commDataTlv; - uint8_t * data = nullptr; + uint8_t *data = nullptr; uint8_t length = 0; message = Get().NewPriorityResponseMessage(aRequest); @@ -356,14 +332,14 @@ void Leader::SendCommissioningGetResponse(const Coap::Message & aRequest, SuccessOrExit(error = Get().SendMessage(*message, aMessageInfo)); - LogInfo("sent commissioning dataset get response"); + LogInfo("Sent %s response", UriToString()); exit: FreeMessageOnError(message, error); } -void Leader::SendCommissioningSetResponse(const Coap::Message & aRequest, - const Ip6::MessageInfo & aMessageInfo, +void Leader::SendCommissioningSetResponse(const Coap::Message &aRequest, + const Ip6::MessageInfo &aMessageInfo, MeshCoP::StateTlv::State aState) { Error error = kErrorNone; @@ -376,7 +352,7 @@ void Leader::SendCommissioningSetResponse(const Coap::Message & aRequest, SuccessOrExit(error = Get().SendMessage(*message, aMessageInfo)); - LogInfo("sent commissioning dataset set response"); + LogInfo("sent %s response", UriToString()); exit: FreeMessageOnError(message, error); @@ -393,7 +369,7 @@ bool Leader::RlocMatch(uint16_t aFirstRloc16, uint16_t aSecondRloc16, MatchMode break; case kMatchModeRouterId: - matched = Mle::Mle::RouterIdMatch(aFirstRloc16, aSecondRloc16); + matched = Mle::RouterIdMatch(aFirstRloc16, aSecondRloc16); break; } @@ -664,15 +640,9 @@ bool Leader::ContainsMatchingServer(const ServiceTlv *aService, const ServerTlv return contains; } -Leader::UpdateStatus Leader::UpdatePrefix(PrefixTlv &aPrefix) -{ - return UpdateTlv(aPrefix, aPrefix.GetSubTlvs()); -} +Leader::UpdateStatus Leader::UpdatePrefix(PrefixTlv &aPrefix) { return UpdateTlv(aPrefix, aPrefix.GetSubTlvs()); } -Leader::UpdateStatus Leader::UpdateService(ServiceTlv &aService) -{ - return UpdateTlv(aService, aService.GetSubTlvs()); -} +Leader::UpdateStatus Leader::UpdateService(ServiceTlv &aService) { return UpdateTlv(aService, aService.GetSubTlvs()); } Leader::UpdateStatus Leader::UpdateTlv(NetworkDataTlv &aTlv, const NetworkDataTlv *aSubTlvs) { @@ -707,7 +677,7 @@ void Leader::RegisterNetworkData(uint16_t aRloc16, const NetworkData &aNetworkDa Error error = kErrorNone; ChangedFlags flags; - VerifyOrExit(Get().IsAllocated(Mle::Mle::RouterIdFromRloc16(aRloc16)), error = kErrorNoRoute); + VerifyOrExit(Get().IsAllocated(Mle::RouterIdFromRloc16(aRloc16)), error = kErrorNoRoute); // Validate that the `aNetworkData` contains well-formed TLVs, sub-TLVs, // and entries all matching `aRloc16` (no other RLOCs). @@ -735,11 +705,10 @@ void Leader::RegisterNetworkData(uint16_t aRloc16, const NetworkData &aNetworkDa } } - IncrementVersions(flags); - DumpDebg("Register", GetBytes(), GetLength()); exit: + IncrementVersions(flags); if (error != kErrorNone) { @@ -795,7 +764,7 @@ Error Leader::AddPrefix(const PrefixTlv &aPrefix, ChangedFlags &aChangedFlags) Error Leader::AddService(const ServiceTlv &aService, ChangedFlags &aChangedFlags) { Error error = kErrorNone; - ServiceTlv * dstService; + ServiceTlv *dstService; ServiceData serviceData; const ServerTlv *server; @@ -838,7 +807,7 @@ Error Leader::AddService(const ServiceTlv &aService, ChangedFlags &aChangedFlags Error Leader::AddHasRoute(const HasRouteTlv &aHasRoute, PrefixTlv &aDstPrefix, ChangedFlags &aChangedFlags) { Error error = kErrorNone; - HasRouteTlv * dstHasRoute = aDstPrefix.FindSubTlv(aHasRoute.IsStable()); + HasRouteTlv *dstHasRoute = aDstPrefix.FindSubTlv(aHasRoute.IsStable()); const HasRouteEntry *entry = aHasRoute.GetFirstEntry(); if (dstHasRoute == nullptr) @@ -875,17 +844,17 @@ Error Leader::AddHasRoute(const HasRouteTlv &aHasRoute, PrefixTlv &aDstPrefix, C Error Leader::AddBorderRouter(const BorderRouterTlv &aBorderRouter, PrefixTlv &aDstPrefix, ChangedFlags &aChangedFlags) { Error error = kErrorNone; - BorderRouterTlv * dstBorderRouter = aDstPrefix.FindSubTlv(aBorderRouter.IsStable()); - ContextTlv * dstContext = aDstPrefix.FindSubTlv(); + BorderRouterTlv *dstBorderRouter = aDstPrefix.FindSubTlv(aBorderRouter.IsStable()); + ContextTlv *dstContext = aDstPrefix.FindSubTlv(); uint8_t contextId = 0; const BorderRouterEntry *entry = aBorderRouter.GetFirstEntry(); if (dstContext == nullptr) { - // Allocate a Context ID first. This ensure that if we cannot - // allocate, we fail and exit before potentially inserting a - // Border Router sub-TLV. - SuccessOrExit(error = AllocateContextId(contextId)); + // Get a new Context ID first. This ensure that if we cannot + // get new Context ID, we fail and exit before potentially + // inserting a Border Router sub-TLV. + SuccessOrExit(error = mContextIds.GetUnallocatedId(contextId)); } if (dstBorderRouter == nullptr) @@ -924,7 +893,7 @@ Error Leader::AddBorderRouter(const BorderRouterTlv &aBorderRouter, PrefixTlv &a } dstContext->SetCompress(); - StopContextReuseTimer(dstContext->GetContextId()); + mContextIds.MarkAsInUse(dstContext->GetContextId()); VerifyOrExit(!ContainsMatchingEntry(dstBorderRouter, *entry)); @@ -1004,50 +973,6 @@ const ServiceTlv *Leader::FindServiceById(uint8_t aServiceId) const return service; } -Error Leader::AllocateContextId(uint8_t &aContextId) -{ - Error error = kErrorNotFound; - - for (uint8_t contextId = kMinContextId; contextId < kMinContextId + kNumContextIds; contextId++) - { - if ((mContextUsed & (1 << contextId)) == 0) - { - mContextUsed |= (1 << contextId); - aContextId = contextId; - error = kErrorNone; - LogInfo("Allocated Context ID = %d", contextId); - break; - } - } - - return error; -} - -void Leader::FreeContextId(uint8_t aContextId) -{ - LogInfo("Free Context Id = %d", aContextId); - RemoveContext(aContextId); - mContextUsed &= ~(1 << aContextId); - IncrementVersions(/* aIncludeStable */ true); -} - -void Leader::StartContextReuseTimer(uint8_t aContextId) -{ - mContextLastUsed[aContextId - kMinContextId] = TimerMilli::GetNow(); - - if (mContextLastUsed[aContextId - kMinContextId].GetValue() == 0) - { - mContextLastUsed[aContextId - kMinContextId].SetValue(1); - } - - mTimer.Start(kStateUpdatePeriod); -} - -void Leader::StopContextReuseTimer(uint8_t aContextId) -{ - mContextLastUsed[aContextId - kMinContextId].SetValue(0); -} - void Leader::RemoveRloc(uint16_t aRloc16, MatchMode aMatchMode, ChangedFlags &aChangedFlags) { NetworkData excludeNetworkData(GetInstance()); // Empty network data. @@ -1058,7 +983,7 @@ void Leader::RemoveRloc(uint16_t aRloc16, MatchMode aMatchMode, ChangedFlags &aC void Leader::RemoveRloc(uint16_t aRloc16, MatchMode aMatchMode, const NetworkData &aExcludeNetworkData, - ChangedFlags & aChangedFlags) + ChangedFlags &aChangedFlags) { // Remove entries from Network Data matching `aRloc16` (using // `aMatchMode` to determine the match) but exclude any entries @@ -1074,7 +999,7 @@ void Leader::RemoveRloc(uint16_t aRloc16, { case NetworkDataTlv::kTypePrefix: { - PrefixTlv * prefix = As(cur); + PrefixTlv *prefix = As(cur); const PrefixTlv *excludePrefix = aExcludeNetworkData.FindPrefix(prefix->GetPrefix(), prefix->GetPrefixLength()); @@ -1091,7 +1016,7 @@ void Leader::RemoveRloc(uint16_t aRloc16, case NetworkDataTlv::kTypeService: { - ServiceTlv * service = As(cur); + ServiceTlv *service = As(cur); ServiceData serviceData; const ServiceTlv *excludeService; @@ -1119,17 +1044,17 @@ void Leader::RemoveRloc(uint16_t aRloc16, } } -void Leader::RemoveRlocInPrefix(PrefixTlv & aPrefix, +void Leader::RemoveRlocInPrefix(PrefixTlv &aPrefix, uint16_t aRloc16, MatchMode aMatchMode, const PrefixTlv *aExcludePrefix, - ChangedFlags & aChangedFlags) + ChangedFlags &aChangedFlags) { // Remove entries in `aPrefix` TLV matching the given `aRloc16` // excluding any entries that are present in `aExcludePrefix`. NetworkDataTlv *cur = aPrefix.GetSubTlvs(); - ContextTlv * context; + ContextTlv *context; while (cur < aPrefix.GetNext()) { @@ -1169,30 +1094,30 @@ void Leader::RemoveRlocInPrefix(PrefixTlv & aPrefix, if ((context = aPrefix.FindSubTlv()) != nullptr) { - if (aPrefix.GetSubTlvsLength() == sizeof(ContextTlv)) + if (aPrefix.FindSubTlv() == nullptr) { context->ClearCompress(); - StartContextReuseTimer(context->GetContextId()); + mContextIds.ScheduleToRemove(context->GetContextId()); } else { context->SetCompress(); - StopContextReuseTimer(context->GetContextId()); + mContextIds.MarkAsInUse(context->GetContextId()); } } } -void Leader::RemoveRlocInService(ServiceTlv & aService, +void Leader::RemoveRlocInService(ServiceTlv &aService, uint16_t aRloc16, MatchMode aMatchMode, const ServiceTlv *aExcludeService, - ChangedFlags & aChangedFlags) + ChangedFlags &aChangedFlags) { // Remove entries in `aService` TLV matching the given `aRloc16` // excluding any entries that are present in `aExcludeService`. NetworkDataTlv *start = aService.GetSubTlvs(); - ServerTlv * server; + ServerTlv *server; while ((server = NetworkDataTlv::Find(start, aService.GetNext())) != nullptr) { @@ -1210,12 +1135,12 @@ void Leader::RemoveRlocInService(ServiceTlv & aService, } } -void Leader::RemoveRlocInHasRoute(PrefixTlv & aPrefix, - HasRouteTlv & aHasRoute, +void Leader::RemoveRlocInHasRoute(PrefixTlv &aPrefix, + HasRouteTlv &aHasRoute, uint16_t aRloc16, MatchMode aMatchMode, const PrefixTlv *aExcludePrefix, - ChangedFlags & aChangedFlags) + ChangedFlags &aChangedFlags) { // Remove entries in `aHasRoute` (a sub-TLV of `aPrefix` TLV) // matching the given `aRloc16` excluding entries that are present @@ -1239,12 +1164,12 @@ void Leader::RemoveRlocInHasRoute(PrefixTlv & aPrefix, } } -void Leader::RemoveRlocInBorderRouter(PrefixTlv & aPrefix, +void Leader::RemoveRlocInBorderRouter(PrefixTlv &aPrefix, BorderRouterTlv &aBorderRouter, uint16_t aRloc16, MatchMode aMatchMode, const PrefixTlv *aExcludePrefix, - ChangedFlags & aChangedFlags) + ChangedFlags &aChangedFlags) { // Remove entries in `aBorderRouter` (a sub-TLV of `aPrefix` TLV) // matching the given `aRloc16` excluding entries that are present @@ -1271,7 +1196,7 @@ void Leader::RemoveRlocInBorderRouter(PrefixTlv & aPrefix, void Leader::RemoveContext(uint8_t aContextId) { NetworkDataTlv *start = GetTlvsStart(); - PrefixTlv * prefix; + PrefixTlv *prefix; while ((prefix = NetworkDataTlv::Find(start, GetTlvsEnd())) != nullptr) { @@ -1285,12 +1210,14 @@ void Leader::RemoveContext(uint8_t aContextId) start = prefix->GetNext(); } + + IncrementVersions(/* aIncludeStable */ true); } void Leader::RemoveContext(PrefixTlv &aPrefix, uint8_t aContextId) { NetworkDataTlv *start = aPrefix.GetSubTlvs(); - ContextTlv * context; + ContextTlv *context; while ((context = NetworkDataTlv::Find(start, aPrefix.GetNext())) != nullptr) { @@ -1325,82 +1252,26 @@ void Leader::HandleNetworkDataRestoredAfterReset(void) continue; } - mContextUsed |= 1 << context->GetContextId(); + mContextIds.MarkAsInUse(context->GetContextId()); - if (context->IsCompress()) + if (!context->IsCompress()) { - StopContextReuseTimer(context->GetContextId()); - } - else - { - StartContextReuseTimer(context->GetContextId()); + mContextIds.ScheduleToRemove(context->GetContextId()); } } } -void Leader::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void Leader::HandleTimer(void) { - bool contextsWaiting = false; - if (mWaitingForNetDataSync) { LogInfo("Timed out waiting for netdata on restoring leader role after reset"); IgnoreError(Get().BecomeDetached()); - ExitNow(); - } - - for (uint8_t i = 0; i < kNumContextIds; i++) - { - if (mContextLastUsed[i].GetValue() == 0) - { - continue; - } - - if (TimerMilli::GetNow() - mContextLastUsed[i] >= Time::SecToMsec(mContextIdReuseDelay)) - { - FreeContextId(kMinContextId + i); - } - else - { - contextsWaiting = true; - } } - - if (contextsWaiting) - { - mTimer.Start(kStateUpdatePeriod); - } - -exit: - return; -} - -Error Leader::RemoveStaleChildEntries(Coap::ResponseHandler aHandler, void *aContext) -{ - Error error = kErrorNotFound; - Iterator iterator = kIteratorInit; - uint16_t rloc16; - - VerifyOrExit(Get().IsRouterOrLeader()); - - while (GetNextServer(iterator, rloc16) == kErrorNone) + else { - if (!Mle::Mle::IsActiveRouter(rloc16) && Mle::Mle::RouterIdMatch(Get().GetRloc16(), rloc16) && - Get().FindChild(rloc16, Child::kInStateValid) == nullptr) - { - // In Thread 1.1 Specification 5.15.6.1, only one RLOC16 TLV entry may appear in SRV_DATA.ntf. - error = SendServerDataNotification(rloc16, /* aAppendNetDataTlv */ false, aHandler, aContext); - ExitNow(); - } + mContextIds.HandleTimer(); } - -exit: - return error; } #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE @@ -1442,6 +1313,86 @@ bool Leader::ContainsOmrPrefix(const Ip6::Prefix &aPrefix) } #endif +//--------------------------------------------------------------------------------------------------------------------- +// Leader::ContextIds + +void Leader::ContextIds::Clear(void) +{ + for (uint8_t id = kMinId; id <= kMaxId; id++) + { + MarkAsUnallocated(id); + } +} + +Error Leader::ContextIds::GetUnallocatedId(uint8_t &aId) +{ + Error error = kErrorNotFound; + + for (uint8_t id = kMinId; id <= kMaxId; id++) + { + if (IsUnallocated(id)) + { + aId = id; + error = kErrorNone; + break; + } + } + + return error; +} + +void Leader::ContextIds::ScheduleToRemove(uint8_t aId) +{ + VerifyOrExit(IsInUse(aId)); + + SetRemoveTime(aId, TimerMilli::GetNow() + Time::SecToMsec(mReuseDelay)); + Get().mTimer.FireAtIfEarlier(GetRemoveTime(aId)); + +exit: + return; +} + +void Leader::ContextIds::SetRemoveTime(uint8_t aId, TimeMilli aTime) +{ + uint32_t time = aTime.GetValue(); + + while ((time == kUnallocated) || (time == kInUse)) + { + time++; + } + + mRemoveTimes[aId - kMinId].SetValue(time); +} + +void Leader::ContextIds::HandleTimer(void) +{ + TimeMilli now = TimerMilli::GetNow(); + TimeMilli nextTime = now.GetDistantFuture(); + + for (uint8_t id = kMinId; id <= kMaxId; id++) + { + if (IsUnallocated(id) || IsInUse(id)) + { + continue; + } + + if (now >= GetRemoveTime(id)) + { + MarkAsUnallocated(id); + Get().RemoveContext(id); + } + else + { + nextTime = Min(nextTime, GetRemoveTime(id)); + } + } + + if (nextTime != now.GetDistantFuture()) + { + Get().mTimer.FireAt(nextTime); + } +} + } // namespace NetworkData } // namespace ot diff --git a/src/core/thread/network_data_leader_ftd.hpp b/src/core/thread/network_data_leader_ftd.hpp index 1ca969f6776..85096c33c18 100644 --- a/src/core/thread/network_data_leader_ftd.hpp +++ b/src/core/thread/network_data_leader_ftd.hpp @@ -40,12 +40,13 @@ #include -#include "coap/coap.hpp" #include "common/non_copyable.hpp" +#include "common/numeric_limits.hpp" #include "common/timer.hpp" #include "net/ip6_address.hpp" #include "thread/mle_router.hpp" #include "thread/network_data.hpp" +#include "thread/tmf.hpp" namespace ot { @@ -67,6 +68,8 @@ namespace NetworkData { */ class Leader : public LeaderBase, private NonCopyable { + friend class Tmf::Agent; + public: /** * This enumeration defines the match mode constants to compare two RLOC16 values. @@ -105,12 +108,6 @@ class Leader : public LeaderBase, private NonCopyable */ void Start(Mle::LeaderStartMode aStartMode); - /** - * This method stops the Leader services. - * - */ - void Stop(void); - /** * This method increments the Thread Network Data version. * @@ -126,20 +123,20 @@ class Leader : public LeaderBase, private NonCopyable /** * This method returns CONTEXT_ID_RESUSE_DELAY value. * - * @returns The CONTEXT_ID_REUSE_DELAY value. + * @returns The CONTEXT_ID_REUSE_DELAY value (in seconds). * */ - uint32_t GetContextIdReuseDelay(void) const { return mContextIdReuseDelay; } + uint32_t GetContextIdReuseDelay(void) const { return mContextIds.GetReuseDelay(); } /** * This method sets CONTEXT_ID_RESUSE_DELAY value. * * @warning This method should only be used for testing. * - * @param[in] aDelay The CONTEXT_ID_REUSE_DELAY value. + * @param[in] aDelay The CONTEXT_ID_REUSE_DELAY value (in seconds). * */ - void SetContextIdReuseDelay(uint32_t aDelay) { mContextIdReuseDelay = aDelay; } + void SetContextIdReuseDelay(uint32_t aDelay) { mContextIds.SetReuseDelay(aDelay); } /** * This method removes Network Data entries matching with a given RLOC16. @@ -167,19 +164,6 @@ class Leader : public LeaderBase, private NonCopyable */ const ServiceTlv *FindServiceById(uint8_t aServiceId) const; - /** - * This method sends SVR_DATA.ntf message for any stale child entries that exist in the network data. - * - * @param[in] aHandler A function pointer that is called when the transaction ends. - * @param[in] aContext A pointer to arbitrary context information. - * - * @retval kErrorNone A stale child entry was found and successfully enqueued a SVR_DATA.ntf message. - * @retval kErrorNoBufs A stale child entry was found, but insufficient message buffers were available. - * @retval kErrorNotFound No stale child entries were found. - * - */ - Error RemoveStaleChildEntries(Coap::ResponseHandler aHandler, void *aContext); - #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE /** * This method indicates whether a given Prefix can act as a valid OMR prefix and exists in the network data. @@ -194,6 +178,8 @@ class Leader : public LeaderBase, private NonCopyable #endif private: + static constexpr uint32_t kMaxNetDataSyncWait = 60 * 1000; // Maximum time to wait for netdata sync in msec. + class ChangedFlags { public: @@ -223,11 +209,58 @@ class Leader : public LeaderBase, private NonCopyable kTlvUpdated, // TLV stable flag is updated based on its sub TLVs. }; - static void HandleServerData(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleServerData(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + class ContextIds : public InstanceLocator + { + public: + // This class tracks Context IDs. A Context ID can be in one + // of the 3 states: It is unallocated, or it is allocated + // and in-use, or it scheduled to be removed (after reuse delay + // interval is passed). + + static constexpr uint8_t kInvalidId = NumericLimits::kMax; + + explicit ContextIds(Instance &aInstance) + : InstanceLocator(aInstance) + , mReuseDelay(kReuseDelay) + { + } + + void Clear(void); + Error GetUnallocatedId(uint8_t &aId); + void MarkAsInUse(uint8_t aId) { mRemoveTimes[aId - kMinId].SetValue(kInUse); } + void ScheduleToRemove(uint8_t aId); + uint32_t GetReuseDelay(void) const { return mReuseDelay; } + void SetReuseDelay(uint32_t aDelay) { mReuseDelay = aDelay; } + void HandleTimer(void); + + private: + static constexpr uint32_t kReuseDelay = 5 * 60; // 5 minutes (in seconds). + + static constexpr uint8_t kMinId = 1; + static constexpr uint8_t kMaxId = 15; + + // The `mRemoveTimes[id]` is used to track the state of a + // Context ID and its remove time. Two specific values + // `kUnallocated` and `kInUse` are used to indicate ID is in + // unallocated or in-use states. Other values indicate we + // are in remove state waiting to remove it at `mRemoveTime`. + + static constexpr uint32_t kUnallocated = 0; + static constexpr uint32_t kInUse = 1; + + bool IsUnallocated(uint8_t aId) const { return mRemoveTimes[aId - kMinId].GetValue() == kUnallocated; } + bool IsInUse(uint8_t aId) const { return mRemoveTimes[aId - kMinId].GetValue() == kInUse; } + TimeMilli GetRemoveTime(uint8_t aId) const { return mRemoveTimes[aId - kMinId]; } + void SetRemoveTime(uint8_t aId, TimeMilli aTime); + void MarkAsUnallocated(uint8_t aId) { mRemoveTimes[aId - kMinId].SetValue(kUnallocated); } + + TimeMilli mRemoveTimes[kMaxId - kMinId + 1]; + uint32_t mReuseDelay; + }; + + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); + void HandleTimer(void); void RegisterNetworkData(uint16_t aRloc16, const NetworkData &aNetworkData); @@ -239,11 +272,6 @@ class Leader : public LeaderBase, private NonCopyable Error AllocateServiceId(uint8_t &aServiceId) const; - Error AllocateContextId(uint8_t &aContextId); - void FreeContextId(uint8_t aContextId); - void StartContextReuseTimer(uint8_t aContextId); - void StopContextReuseTimer(uint8_t aContextId); - void RemoveContext(uint8_t aContextId); void RemoveContext(PrefixTlv &aPrefix, uint8_t aContextId); @@ -253,29 +281,29 @@ class Leader : public LeaderBase, private NonCopyable void RemoveRloc(uint16_t aRloc16, MatchMode aMatchMode, const NetworkData &aExcludeNetworkData, - ChangedFlags & aChangedFlags); - void RemoveRlocInPrefix(PrefixTlv & aPrefix, + ChangedFlags &aChangedFlags); + void RemoveRlocInPrefix(PrefixTlv &aPrefix, uint16_t aRloc16, MatchMode aMatchMode, const PrefixTlv *aExcludePrefix, - ChangedFlags & aChangedFlags); - void RemoveRlocInService(ServiceTlv & aService, + ChangedFlags &aChangedFlags); + void RemoveRlocInService(ServiceTlv &aService, uint16_t aRloc16, MatchMode aMatchMode, const ServiceTlv *aExcludeService, - ChangedFlags & aChangedFlags); - void RemoveRlocInHasRoute(PrefixTlv & aPrefix, - HasRouteTlv & aHasRoute, + ChangedFlags &aChangedFlags); + void RemoveRlocInHasRoute(PrefixTlv &aPrefix, + HasRouteTlv &aHasRoute, uint16_t aRloc16, MatchMode aMatchMode, const PrefixTlv *aExcludePrefix, - ChangedFlags & aChangedFlags); - void RemoveRlocInBorderRouter(PrefixTlv & aPrefix, + ChangedFlags &aChangedFlags); + void RemoveRlocInBorderRouter(PrefixTlv &aPrefix, BorderRouterTlv &aBorderRouter, uint16_t aRloc16, MatchMode aMatchMode, const PrefixTlv *aExcludePrefix, - ChangedFlags & aChangedFlags); + ChangedFlags &aChangedFlags); static bool RlocMatch(uint16_t aFirstRloc16, uint16_t aSecondRloc16, MatchMode aMatchMode); @@ -293,39 +321,26 @@ class Leader : public LeaderBase, private NonCopyable UpdateStatus UpdateService(ServiceTlv &aService); UpdateStatus UpdateTlv(NetworkDataTlv &aTlv, const NetworkDataTlv *aSubTlvs); - static void HandleCommissioningSet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleCommissioningSet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - - static void HandleCommissioningGet(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleCommissioningGet(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - - void SendCommissioningGetResponse(const Coap::Message & aRequest, + void SendCommissioningGetResponse(const Coap::Message &aRequest, uint16_t aLength, const Ip6::MessageInfo &aMessageInfo); - void SendCommissioningSetResponse(const Coap::Message & aRequest, - const Ip6::MessageInfo & aMessageInfo, + void SendCommissioningSetResponse(const Coap::Message &aRequest, + const Ip6::MessageInfo &aMessageInfo, MeshCoP::StateTlv::State aState); void IncrementVersions(bool aIncludeStable); void IncrementVersions(const ChangedFlags &aFlags); - static constexpr uint8_t kMinContextId = 1; // Minimum Context ID (0 is used for Mesh Local) - static constexpr uint8_t kNumContextIds = 15; // Maximum Context ID - static constexpr uint32_t kContextIdReuseDelay = 48 * 60 * 60; // in seconds - static constexpr uint32_t kStateUpdatePeriod = 60 * 1000; // State update period in milliseconds - static constexpr uint32_t kMaxNetDataSyncWait = 60 * 1000; // Maximum time to wait for netdata sync. - - bool mWaitingForNetDataSync; - uint16_t mContextUsed; - TimeMilli mContextLastUsed[kNumContextIds]; - uint32_t mContextIdReuseDelay; - TimerMilli mTimer; + using UpdateTimer = TimerMilliIn; - Coap::Resource mServerData; - - Coap::Resource mCommissioningDataGet; - Coap::Resource mCommissioningDataSet; + bool mWaitingForNetDataSync; + ContextIds mContextIds; + UpdateTimer mTimer; }; +DeclareTmfHandler(Leader, kUriServerData); +DeclareTmfHandler(Leader, kUriCommissionerGet); +DeclareTmfHandler(Leader, kUriCommissionerSet); + /** * @} */ diff --git a/src/core/thread/network_data_local.cpp b/src/core/thread/network_data_local.cpp index 7299befc155..a7eb9d9fd03 100644 --- a/src/core/thread/network_data_local.cpp +++ b/src/core/thread/network_data_local.cpp @@ -168,7 +168,6 @@ void Local::UpdateRloc(PrefixTlv &aPrefixTlv) default: OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); } } } @@ -179,11 +178,11 @@ void Local::UpdateRloc(PrefixTlv &aPrefixTlv) Error Local::AddService(uint32_t aEnterpriseNumber, const ServiceData &aServiceData, bool aServerStable, - const ServerData & aServerData) + const ServerData &aServerData) { Error error = kErrorNone; ServiceTlv *serviceTlv; - ServerTlv * serverTlv; + ServerTlv *serverTlv; uint16_t serviceTlvSize = ServiceTlv::CalculateSize(aEnterpriseNumber, aServiceData.GetLength()) + sizeof(ServerTlv) + aServerData.GetLength(); @@ -243,7 +242,6 @@ void Local::UpdateRloc(ServiceTlv &aService) default: OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); } } } @@ -271,46 +269,10 @@ void Local::UpdateRloc(void) default: OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); } } } -bool Local::IsConsistent(void) const -{ - return Get().ContainsEntriesFrom(*this, Get().GetRloc16()) && - ContainsEntriesFrom(Get(), Get().GetRloc16()); -} - -Error Local::UpdateInconsistentServerData(Coap::ResponseHandler aHandler, void *aContext) -{ - Error error = kErrorNone; - uint16_t rloc = Get().GetRloc16(); - -#if OPENTHREAD_FTD - // Don't send this Server Data Notification if the device is going to upgrade to Router - if (Get().IsExpectedToBecomeRouterSoon()) - { - ExitNow(error = kErrorInvalidState); - } -#endif - - UpdateRloc(); - - VerifyOrExit(!IsConsistent(), error = kErrorNotFound); - - if (mOldRloc == rloc) - { - mOldRloc = Mac::kShortAddrInvalid; - } - - SuccessOrExit(error = SendServerDataNotification(mOldRloc, /* aAppendNetDataTlv */ true, aHandler, aContext)); - mOldRloc = rloc; - -exit: - return error; -} - } // namespace NetworkData } // namespace ot diff --git a/src/core/thread/network_data_local.hpp b/src/core/thread/network_data_local.hpp index ec274d1dd87..27090a5ba79 100644 --- a/src/core/thread/network_data_local.hpp +++ b/src/core/thread/network_data_local.hpp @@ -54,12 +54,16 @@ namespace ot { namespace NetworkData { +class Notifier; + /** * This class implements the Thread Network Data contributed by the local device. * */ class Local : public MutableNetworkData, private NonCopyable { + friend class Notifier; + public: /** * This constructor initializes the local Network Data. @@ -69,7 +73,6 @@ class Local : public MutableNetworkData, private NonCopyable */ explicit Local(Instance &aInstance) : MutableNetworkData(aInstance, mTlvBuffer, 0, sizeof(mTlvBuffer)) - , mOldRloc(Mac::kShortAddrInvalid) { } @@ -148,7 +151,7 @@ class Local : public MutableNetworkData, private NonCopyable Error AddService(uint32_t aEnterpriseNumber, const ServiceData &aServiceData, bool aServerStable, - const ServerData & aServerData); + const ServerData &aServerData); /** * This method removes a Service entry from the Thread Network local data. @@ -163,23 +166,8 @@ class Local : public MutableNetworkData, private NonCopyable Error RemoveService(uint32_t aEnterpriseNumber, const ServiceData &aServiceData); #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE - /** - * This method sends a Server Data Notification message to the Leader. - * - * @param[in] aHandler A function pointer that is called when the transaction ends. - * @param[in] aContext A pointer to arbitrary context information. - * - * @retval kErrorNone Successfully enqueued the notification message. - * @retval kErrorNoBufs Insufficient message buffers to generate the notification message. - * @retval kErrorInvalidState Device is a REED and is in the process of becoming a Router. - * @retval kErrorNotFound Server Data is already consistent with network data. - * - */ - Error UpdateInconsistentServerData(Coap::ResponseHandler aHandler, void *aContext); - private: void UpdateRloc(void); - bool IsConsistent(void) const; #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE Error AddPrefix(const Ip6::Prefix &aPrefix, NetworkDataTlv::Type aSubTlvType, uint16_t aFlags, bool aStable); @@ -191,8 +179,7 @@ class Local : public MutableNetworkData, private NonCopyable void UpdateRloc(ServiceTlv &aService); #endif - uint8_t mTlvBuffer[kMaxSize]; - uint16_t mOldRloc; + uint8_t mTlvBuffer[kMaxSize]; }; } // namespace NetworkData diff --git a/src/core/thread/network_data_notifier.cpp b/src/core/thread/network_data_notifier.cpp index 00b15ce4ce0..65b76e06a56 100644 --- a/src/core/thread/network_data_notifier.cpp +++ b/src/core/thread/network_data_notifier.cpp @@ -41,6 +41,8 @@ #include "common/log.hpp" #include "thread/network_data_leader.hpp" #include "thread/network_data_local.hpp" +#include "thread/tmf.hpp" +#include "thread/uri_paths.hpp" namespace ot { namespace NetworkData { @@ -49,9 +51,10 @@ RegisterLogModule("NetworkData"); Notifier::Notifier(Instance &aInstance) : InstanceLocator(aInstance) - , mTimer(aInstance, HandleTimer) - , mSynchronizeDataTask(aInstance, HandleSynchronizeDataTask) + , mTimer(aInstance) + , mSynchronizeDataTask(aInstance) , mNextDelay(0) + , mOldRloc(Mac::kShortAddrInvalid) , mWaitingForResponse(false) #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE , mDidRequestRouterRoleUpgrade(false) @@ -71,11 +74,6 @@ void Notifier::HandleServerDataUpdated(void) mSynchronizeDataTask.Post(); } -void Notifier::HandleSynchronizeDataTask(Tasklet &aTasklet) -{ - aTasklet.Get().SynchronizeServerData(); -} - void Notifier::SynchronizeServerData(void) { Error error = kErrorNotFound; @@ -86,13 +84,13 @@ void Notifier::SynchronizeServerData(void) #if OPENTHREAD_FTD mNextDelay = kDelayRemoveStaleChildren; - error = Get().RemoveStaleChildEntries(&Notifier::HandleCoapResponse, this); + error = RemoveStaleChildEntries(); VerifyOrExit(error == kErrorNotFound); #endif #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE mNextDelay = kDelaySynchronizeServerData; - error = Get().UpdateInconsistentServerData(&Notifier::HandleCoapResponse, this); + error = UpdateInconsistentData(); VerifyOrExit(error == kErrorNotFound); #endif @@ -114,10 +112,112 @@ void Notifier::SynchronizeServerData(void) break; default: OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); } } +#if OPENTHREAD_FTD +Error Notifier::RemoveStaleChildEntries(void) +{ + // Check if there is any stale child entry in network data and send + // a "Server Data" notification to leader to remove it. + // + // - `kErrorNone` when a stale child entry was found and successfully + // sent a "Server Data" notification to leader. + // - `kErrorNoBufs` if could not allocate message to send message. + // - `kErrorNotFound` if no stale child entries were found. + + Error error = kErrorNotFound; + Iterator iterator = kIteratorInit; + uint16_t rloc16; + + VerifyOrExit(Get().IsRouterOrLeader()); + + while (Get().GetNextServer(iterator, rloc16) == kErrorNone) + { + if (!Mle::IsActiveRouter(rloc16) && Mle::RouterIdMatch(Get().GetRloc16(), rloc16) && + Get().FindChild(rloc16, Child::kInStateValid) == nullptr) + { + error = SendServerDataNotification(rloc16); + ExitNow(); + } + } + +exit: + return error; +} +#endif // OPENTHREAD_FTD + +#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE +Error Notifier::UpdateInconsistentData(void) +{ + Error error = kErrorNone; + uint16_t deviceRloc = Get().GetRloc16(); + +#if OPENTHREAD_FTD + // Don't send this Server Data Notification if the device is going + // to upgrade to Router. + + if (Get().IsExpectedToBecomeRouterSoon()) + { + ExitNow(error = kErrorInvalidState); + } +#endif + + Get().UpdateRloc(); + + if (Get().ContainsEntriesFrom(Get(), deviceRloc) && + Get().ContainsEntriesFrom(Get(), deviceRloc)) + { + ExitNow(error = kErrorNotFound); + } + + if (mOldRloc == deviceRloc) + { + mOldRloc = Mac::kShortAddrInvalid; + } + + SuccessOrExit(error = SendServerDataNotification(mOldRloc, &Get())); + mOldRloc = deviceRloc; + +exit: + return error; +} +#endif // #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE + +Error Notifier::SendServerDataNotification(uint16_t aOldRloc16, const NetworkData *aNetworkData) +{ + Error error = kErrorNone; + Coap::Message *message; + Tmf::MessageInfo messageInfo(GetInstance()); + + message = Get().NewPriorityConfirmablePostMessage(kUriServerData); + VerifyOrExit(message != nullptr, error = kErrorNoBufs); + + if (aNetworkData != nullptr) + { + ThreadTlv tlv; + + tlv.SetType(ThreadTlv::kThreadNetworkData); + tlv.SetLength(aNetworkData->GetLength()); + SuccessOrExit(error = message->Append(tlv)); + SuccessOrExit(error = message->AppendBytes(aNetworkData->GetBytes(), aNetworkData->GetLength())); + } + + if (aOldRloc16 != Mac::kShortAddrInvalid) + { + SuccessOrExit(error = Tlv::Append(*message, aOldRloc16)); + } + + IgnoreError(messageInfo.SetSockAddrToRlocPeerAddrToLeaderAloc()); + SuccessOrExit(error = Get().SendMessage(*message, messageInfo, HandleCoapResponse, this)); + + LogInfo("Sent %s", UriToString()); + +exit: + FreeMessageOnError(message, error); + return error; +} + void Notifier::HandleNotifierEvents(Events aEvents) { if (aEvents.ContainsAny(kEventThreadRoleChanged | kEventThreadChildRemoved)) @@ -143,15 +243,7 @@ void Notifier::HandleNotifierEvents(Events aEvents) } } -void Notifier::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - -void Notifier::HandleTimer(void) -{ - SynchronizeServerData(); -} +void Notifier::HandleTimer(void) { SynchronizeServerData(); } void Notifier::HandleCoapResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult) { @@ -178,7 +270,6 @@ void Notifier::HandleCoapResponse(Error aResult) default: OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); } } diff --git a/src/core/thread/network_data_notifier.hpp b/src/core/thread/network_data_notifier.hpp index 2388da739ee..65426cda093 100644 --- a/src/core/thread/network_data_notifier.hpp +++ b/src/core/thread/network_data_notifier.hpp @@ -48,6 +48,8 @@ namespace ot { namespace NetworkData { +class NetworkData; + /** * This class implements the SVR_DATA.ntf transmission logic. * @@ -104,29 +106,36 @@ class Notifier : public InstanceLocator, private NonCopyable static constexpr uint32_t kDelaySynchronizeServerData = 300000; // in msec static constexpr uint8_t kRouterRoleUpgradeMaxTimeout = 10; // in sec - void HandleNotifierEvents(Events aEvents); + void SynchronizeServerData(void); + Error SendServerDataNotification(uint16_t aOldRloc16, const NetworkData *aNetworkData = nullptr); +#if OPENTHREAD_FTD + Error RemoveStaleChildEntries(void); +#endif +#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE || OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE + Error UpdateInconsistentData(void); +#endif - static void HandleTimer(Timer &aTimer); + void HandleNotifierEvents(Events aEvents); void HandleTimer(void); - - static void HandleCoapResponse(void * aContext, - otMessage * aMessage, + static void HandleCoapResponse(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); void HandleCoapResponse(Error aResult); - static void HandleSynchronizeDataTask(Tasklet &aTasklet); - - void SynchronizeServerData(void); #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE void ScheduleRouterRoleUpgradeIfEligible(void); void HandleTimeTick(void); #endif - TimerMilli mTimer; - Tasklet mSynchronizeDataTask; - uint32_t mNextDelay; - bool mWaitingForResponse : 1; + using SynchronizeDataTask = TaskletIn; + using DelayTimer = TimerMilliIn; + + DelayTimer mTimer; + SynchronizeDataTask mSynchronizeDataTask; + uint32_t mNextDelay; + uint16_t mOldRloc; + bool mWaitingForResponse : 1; #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE && OPENTHREAD_CONFIG_BORDER_ROUTER_REQUEST_ROUTER_ROLE bool mDidRequestRouterRoleUpgrade : 1; diff --git a/src/core/thread/network_data_publisher.cpp b/src/core/thread/network_data_publisher.cpp index 8141c760a17..056765cb9c4 100644 --- a/src/core/thread/network_data_publisher.cpp +++ b/src/core/thread/network_data_publisher.cpp @@ -59,17 +59,13 @@ Publisher::Publisher(Instance &aInstance) #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE , mDnsSrpServiceEntry(aInstance) #endif -#if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE - , mPrefixCallback(nullptr) - , mPrefixCallbackContext(nullptr) -#endif - , mTimer(aInstance, Publisher::HandleTimer) + , mTimer(aInstance) { #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE // Since the `PrefixEntry` type is used in an array, // we cannot use a constructor with an argument (e.g., - // we cannot use `InstacneLocator`) so we use - // `IntanceLocatorInit` and `Init()` the entries one + // we cannot use `InstanceLocator`) so we use + // `InstanceLocatorInit` and `Init()` the entries one // by one. for (PrefixEntry &entry : mPrefixEntries) @@ -81,13 +77,7 @@ Publisher::Publisher(Instance &aInstance) #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE -void Publisher::SetPrefixCallback(PrefixCallback aCallback, void *aContext) -{ - mPrefixCallback = aCallback; - mPrefixCallbackContext = aContext; -} - -Error Publisher::PublishOnMeshPrefix(const OnMeshPrefixConfig &aConfig) +Error Publisher::PublishOnMeshPrefix(const OnMeshPrefixConfig &aConfig, Requester aRequester) { Error error = kErrorNone; PrefixEntry *entry; @@ -95,16 +85,23 @@ Error Publisher::PublishOnMeshPrefix(const OnMeshPrefixConfig &aConfig) VerifyOrExit(aConfig.IsValid(GetInstance()), error = kErrorInvalidArgs); VerifyOrExit(aConfig.mStable, error = kErrorInvalidArgs); - entry = FindOrAllocatePrefixEntry(aConfig.GetPrefix()); + entry = FindOrAllocatePrefixEntry(aConfig.GetPrefix(), aRequester); VerifyOrExit(entry != nullptr, error = kErrorNoBufs); - entry->Publish(aConfig); + entry->Publish(aConfig, aRequester); exit: return error; } -Error Publisher::PublishExternalRoute(const ExternalRouteConfig &aConfig) +Error Publisher::PublishExternalRoute(const ExternalRouteConfig &aConfig, Requester aRequester) +{ + return ReplacePublishedExternalRoute(aConfig.GetPrefix(), aConfig, aRequester); +} + +Error Publisher::ReplacePublishedExternalRoute(const Ip6::Prefix &aPrefix, + const ExternalRouteConfig &aConfig, + Requester aRequester) { Error error = kErrorNone; PrefixEntry *entry; @@ -112,10 +109,10 @@ Error Publisher::PublishExternalRoute(const ExternalRouteConfig &aConfig) VerifyOrExit(aConfig.IsValid(GetInstance()), error = kErrorInvalidArgs); VerifyOrExit(aConfig.mStable, error = kErrorInvalidArgs); - entry = FindOrAllocatePrefixEntry(aConfig.GetPrefix()); + entry = FindOrAllocatePrefixEntry(aPrefix, aRequester); VerifyOrExit(entry != nullptr, error = kErrorNoBufs); - entry->Publish(aConfig); + entry->Publish(aConfig, aRequester); exit: return error; @@ -149,24 +146,48 @@ Error Publisher::UnpublishPrefix(const Ip6::Prefix &aPrefix) return error; } -Publisher::PrefixEntry *Publisher::FindOrAllocatePrefixEntry(const Ip6::Prefix &aPrefix) +Publisher::PrefixEntry *Publisher::FindOrAllocatePrefixEntry(const Ip6::Prefix &aPrefix, Requester aRequester) { // Returns a matching prefix entry if found, otherwise tries // to allocate a new entry. - PrefixEntry *prefixEntry = FindMatchingPrefixEntry(aPrefix); - - VerifyOrExit(prefixEntry == nullptr); + PrefixEntry *prefixEntry = nullptr; + uint16_t numEntries = 0; + uint8_t maxEntries = 0; for (PrefixEntry &entry : mPrefixEntries) { - if (!entry.IsInUse()) + if (entry.IsInUse()) + { + if (entry.GetRequester() == aRequester) + { + numEntries++; + } + + if (entry.Matches(aPrefix)) + { + prefixEntry = &entry; + ExitNow(); + } + } + else if (prefixEntry == nullptr) { prefixEntry = &entry; - ExitNow(); } } + switch (aRequester) + { + case kFromUser: + maxEntries = kMaxUserPrefixEntries; + break; + case kFromRoutingManager: + maxEntries = kMaxRoutingManagerPrefixEntries; + break; + } + + VerifyOrExit(numEntries < maxEntries, prefixEntry = nullptr); + exit: return prefixEntry; } @@ -199,10 +220,7 @@ bool Publisher::IsAPrefixEntry(const Entry &aEntry) const void Publisher::NotifyPrefixEntryChange(Event aEvent, const Ip6::Prefix &aPrefix) const { - if (mPrefixCallback != nullptr) - { - mPrefixCallback(static_cast(aEvent), &aPrefix, mPrefixCallbackContext); - } + mPrefixCallback.InvokeIfSet(static_cast(aEvent), &aPrefix); } #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE @@ -221,11 +239,6 @@ void Publisher::HandleNotifierEvents(Events aEvents) #endif } -void Publisher::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void Publisher::HandleTimer(void) { #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE @@ -262,7 +275,7 @@ bool Publisher::Entry::IsPreferred(uint16_t aRloc16) const // router over an entry from an end-device (e.g., a REED). If both // are the same type, then the one with smaller RLOC16 is preferred. - bool isOtherRouter = Mle::Mle::IsActiveRouter(aRloc16); + bool isOtherRouter = Mle::IsActiveRouter(aRloc16); return (Get().IsRouterOrLeader() == isOtherRouter) ? (aRloc16 < Get().GetRloc16()) : isOtherRouter; @@ -324,7 +337,7 @@ void Publisher::Entry::UpdateState(uint8_t aNumEntries, uint8_t aNumPreferredEnt if (aNumPreferredEntries < aDesiredNumEntries) { - mUpdateTime += kExtraDelayToRemovePeferred; + mUpdateTime += kExtraDelayToRemovePreferred; } SetState(kRemoving); @@ -437,7 +450,7 @@ Publisher::Entry::InfoString Publisher::Entry::ToString(bool aIncludeState) cons break; } - string.Append(prefixEntry.mPrefix.ToString().AsCString()); + string.Append("%s", prefixEntry.mPrefix.ToString().AsCString()); ExitNow(); } #endif @@ -453,7 +466,7 @@ Publisher::Entry::InfoString Publisher::Entry::ToString(bool aIncludeState) cons void Publisher::Entry::LogUpdateTime(void) const { - LogInfo("%s - update in %u msec", ToString().AsCString(), mUpdateTime - TimerMilli::GetNow()); + LogInfo("%s - update in %lu msec", ToString().AsCString(), ToUlong(mUpdateTime - TimerMilli::GetNow())); } const char *Publisher::Entry::StateToString(State aState) @@ -480,18 +493,7 @@ const char *Publisher::Entry::StateToString(State aState) //--------------------------------------------------------------------------------------------------------------------- // Publisher::DnsSrpServiceEntry -Publisher::DnsSrpServiceEntry::DnsSrpServiceEntry(Instance &aInstance) - : mCallback(nullptr) - , mCallbackContext(nullptr) -{ - Init(aInstance); -} - -void Publisher::DnsSrpServiceEntry::SetCallback(DnsSrpServiceCallback aCallback, void *aContext) -{ - mCallback = aCallback; - mCallbackContext = aContext; -} +Publisher::DnsSrpServiceEntry::DnsSrpServiceEntry(Instance &aInstance) { Init(aInstance); } void Publisher::DnsSrpServiceEntry::PublishAnycast(uint8_t aSequenceNumber) { @@ -630,10 +632,7 @@ void Publisher::DnsSrpServiceEntry::Notify(Event aEvent) const Get().HandleNetDataPublisherEvent(aEvent); #endif - if (mCallback != nullptr) - { - mCallback(static_cast(aEvent), mCallbackContext); - } + mCallback.InvokeIfSet(static_cast(aEvent)); } void Publisher::DnsSrpServiceEntry::Process(void) @@ -680,7 +679,7 @@ void Publisher::DnsSrpServiceEntry::CountAnycastEntries(uint8_t &aNumEntries, ui // smaller RLCO16. Service::DnsSrpAnycast::ServiceData serviceData(mInfo.GetSequenceNumber()); - const ServiceTlv * serviceTlv = nullptr; + const ServiceTlv *serviceTlv = nullptr; ServiceData data; data.Init(&serviceData, serviceData.GetLength()); @@ -791,41 +790,47 @@ Publisher::DnsSrpServiceEntry::Info::Info(Type aType, uint16_t aPortOrSeqNumber, //--------------------------------------------------------------------------------------------------------------------- // Publisher::PrefixEntry -void Publisher::PrefixEntry::Publish(const OnMeshPrefixConfig &aConfig) +void Publisher::PrefixEntry::Publish(const OnMeshPrefixConfig &aConfig, Requester aRequester) { LogInfo("Publishing OnMeshPrefix %s", aConfig.GetPrefix().ToString().AsCString()); - Publish(aConfig.GetPrefix(), aConfig.ConvertToTlvFlags(), kTypeOnMeshPrefix); + Publish(aConfig.GetPrefix(), aConfig.ConvertToTlvFlags(), kTypeOnMeshPrefix, aRequester); } -void Publisher::PrefixEntry::Publish(const ExternalRouteConfig &aConfig) +void Publisher::PrefixEntry::Publish(const ExternalRouteConfig &aConfig, Requester aRequester) { LogInfo("Publishing ExternalRoute %s", aConfig.GetPrefix().ToString().AsCString()); - Publish(aConfig.GetPrefix(), aConfig.ConvertToTlvFlags(), kTypeExternalRoute); + Publish(aConfig.GetPrefix(), aConfig.ConvertToTlvFlags(), kTypeExternalRoute, aRequester); } -void Publisher::PrefixEntry::Publish(const Ip6::Prefix &aPrefix, uint16_t aNewFlags, Type aNewType) +void Publisher::PrefixEntry::Publish(const Ip6::Prefix &aPrefix, + uint16_t aNewFlags, + Type aNewType, + Requester aRequester) { + mRequester = aRequester; + if (GetState() != kNoEntry) { - // If this is an existing entry, first we check that there is - // a change in either type or flags. We remove the old entry - // from Network Data if it was added. If the only change is - // to flags (e.g., change to the preference level) and the - // entry was previously added in Network Data, we re-add it - // with the new flags. This ensures that changes to flags are - // immediately reflected in the Network Data. + // If this is an existing entry, check if there is a change in + // type, flags, or the prefix itself. If not, everything is + // as before. If something is different, first, remove the + // old entry from Network Data if it was added. Then, re-add + // the new prefix/flags (replacing the old entry). This + // ensures the changes are immediately reflected in the + // Network Data. State oldState = GetState(); - VerifyOrExit((mType != aNewType) || (mFlags != aNewFlags)); + VerifyOrExit((mType != aNewType) || (mFlags != aNewFlags) || (mPrefix != aPrefix)); Remove(/* aNextState */ kNoEntry); if ((mType == aNewType) && ((oldState == kAdded) || (oldState == kRemoving))) { - mFlags = aNewFlags; + mPrefix = aPrefix; + mFlags = aNewFlags; Add(); } } @@ -962,7 +967,7 @@ void Publisher::PrefixEntry::Process(void) void Publisher::PrefixEntry::CountOnMeshPrefixEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const { - const PrefixTlv * prefixTlv; + const PrefixTlv *prefixTlv; const BorderRouterTlv *brSubTlv; int8_t preference = BorderRouterEntry::PreferenceFromFlags(mFlags); uint16_t flagsWithoutPreference = BorderRouterEntry::FlagsWithoutPreference(mFlags); @@ -1009,7 +1014,7 @@ void Publisher::PrefixEntry::CountOnMeshPrefixEntries(uint8_t &aNumEntries, uint void Publisher::PrefixEntry::CountExternalRouteEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const { - const PrefixTlv * prefixTlv; + const PrefixTlv *prefixTlv; const HasRouteTlv *hrSubTlv; int8_t preference = HasRouteEntry::PreferenceFromFlags(static_cast(mFlags)); uint8_t flagsWithoutPreference = HasRouteEntry::FlagsWithoutPreference(static_cast(mFlags)); diff --git a/src/core/thread/network_data_publisher.hpp b/src/core/thread/network_data_publisher.hpp index 7d11790951a..3d3a37f27a3 100644 --- a/src/core/thread/network_data_publisher.hpp +++ b/src/core/thread/network_data_publisher.hpp @@ -43,14 +43,10 @@ "or OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE" #endif -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && (OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES < \ - (OPENTHREAD_CONFIG_BORDER_ROUTING_MAX_DISCOVERED_PREFIXES + 4)) -#error "OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES needs to support more entries when "\ - "OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE is enabled to accommodate for max on-link prefixes" -#endif - #include +#include "border_router/routing_manager.hpp" +#include "common/callback.hpp" #include "common/clearable.hpp" #include "common/equatable.hpp" #include "common/error.hpp" @@ -87,6 +83,16 @@ class Publisher : public InstanceLocator, private NonCopyable kEventEntryRemoved = OT_NETDATA_PUBLISHER_EVENT_ENTRY_REMOVED, ///< Entry is removed from Network Data. }; + /** + * This enumeration represents the requester associated with a published prefix. + * + */ + enum Requester : uint8_t + { + kFromUser, ///< Requested by user (public OT API). + kFromRoutingManager, ///< Requested by `RoutingManager` module. + }; + /** * This constructor initializes `Publisher` object. * @@ -207,7 +213,7 @@ class Publisher : public InstanceLocator, private NonCopyable * @param[in] aContext A pointer to application-specific context (used when @p aCallback is invoked). * */ - void SetPrefixCallback(PrefixCallback aCallback, void *aContext); + void SetPrefixCallback(PrefixCallback aCallback, void *aContext) { mPrefixCallback.Set(aCallback, aContext); } /** * This method requests an on-mesh prefix to be published in the Thread Network Data. @@ -222,6 +228,7 @@ class Publisher : public InstanceLocator, private NonCopyable * same prefix with the same or higher preference. * * @param[in] aConfig The on-mesh prefix config to publish. + * @param[in] aRequester The requester (`kFromUser` or `kFromRoutingManager` module). * * @retval kErrorNone The on-mesh prefix is published successfully. * @retval kErrorInvalidArgs The @p aConfig is not valid (bad prefix, invalid flag combinations, or not stable). @@ -232,7 +239,7 @@ class Publisher : public InstanceLocator, private NonCopyable * * */ - Error PublishOnMeshPrefix(const OnMeshPrefixConfig &aConfig); + Error PublishOnMeshPrefix(const OnMeshPrefixConfig &aConfig, Requester aRequester); /** * This method requests an external route prefix to be published in the Thread Network Data. @@ -247,6 +254,42 @@ class Publisher : public InstanceLocator, private NonCopyable * same prefix with the same or higher preference. * * @param[in] aConfig The external route config to publish. + * @param[in] aRequester The requester (`kFromUser` or `kFromRoutingManager` module). + * + * @retval kErrorNone The external route is published successfully. + * @retval kErrorInvalidArgs The @p aConfig is not valid (bad prefix, invalid flag combinations, or not stable). + * @retval kErrorNoBufs 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`. + * + * + */ + Error PublishExternalRoute(const ExternalRouteConfig &aConfig, Requester aRequester); + + /** + * This method replaces a previously published external route. + * + * Only stable entries can be published (i.e.,`aConfig.mStable` MUST be `true`). + * + * If there is no previously published external route matching @p aPrefix, this method behaves similarly to + * `PublishExternalRoute()`, i.e., it will start the process of publishing @a aConfig as an external route in the + * Thread Network Data. + * + * If there is a previously published route entry matching @p aPrefix, it will be replaced with the new prefix from + * @p aConfig. + * + * - If the @p aPrefix was already added in the Network Data, the change to the new prefix in @p aConfig is + * immediately reflected in the Network Data. This ensures that route entries in the Network Data are not + * abruptly removed and the transition from aPrefix to the new prefix is smooth. + * + * - If the old published @p aPrefix was not added in the Network Data, it will be replaced with the new @p aConfig + * prefix but it will not be immediately added. Instead, it will start the process of publishing it in the + * Network Data (monitoring the Network Data to determine when/if to add the prefix, depending on the number of + * similar prefixes present in the Network Data). + * + * @param[in] aPrefix The previously published external route prefix to replace. + * @param[in] aConfig The external route config to publish. + * @param[in] aRequester The requester (`kFromUser` or `kFromRoutingManager` module). * * @retval kErrorNone The external route is published successfully. * @retval kErrorInvalidArgs The @p aConfig is not valid (bad prefix, invalid flag combinations, or not stable). @@ -256,7 +299,9 @@ class Publisher : public InstanceLocator, private NonCopyable * * */ - Error PublishExternalRoute(const ExternalRouteConfig &aConfig); + Error ReplacePublishedExternalRoute(const Ip6::Prefix &aPrefix, + const ExternalRouteConfig &aConfig, + Requester aRequester); /** * This method indicates whether or not currently a published prefix entry (on-mesh or external route) is added to @@ -298,10 +343,10 @@ class Publisher : public InstanceLocator, private NonCopyable // All intervals are in milliseconds. static constexpr uint32_t kMaxDelayToAdd = OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_DELAY_TO_ADD; static constexpr uint32_t kMaxDelayToRemove = OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_DELAY_TO_REMOVE; - static constexpr uint32_t kExtraDelayToRemovePeferred = + static constexpr uint32_t kExtraDelayToRemovePreferred = OPENTHREAD_CONFIG_NETDATA_PUBLISHER_EXTRA_DELAY_TIME_TO_REMOVE_PREFERRED; - static constexpr uint16_t kInfoStringSize = 50; + static constexpr uint16_t kInfoStringSize = 60; typedef String InfoString; @@ -339,7 +384,7 @@ class Publisher : public InstanceLocator, private NonCopyable public: explicit DnsSrpServiceEntry(Instance &aInstance); - void SetCallback(DnsSrpServiceCallback aCallback, void *aContext); + void SetCallback(DnsSrpServiceCallback aCallback, void *aContext) { mCallback.Set(aCallback, aContext); } void PublishAnycast(uint8_t aSequenceNumber); void PublishUnicast(const Ip6::Address &aAddress, uint16_t aPort); void PublishUnicast(uint16_t aPort); @@ -394,29 +439,36 @@ class Publisher : public InstanceLocator, private NonCopyable void CountAnycastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const; void CountUnicastEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const; - Info mInfo; - DnsSrpServiceCallback mCallback; - void * mCallbackContext; + Info mInfo; + Callback mCallback; }; #endif // OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE + // Max number of prefix (on-mesh or external route) entries. - static constexpr uint16_t kMaxPrefixEntries = OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES; + static constexpr uint16_t kMaxUserPrefixEntries = OPENTHREAD_CONFIG_NETDATA_PUBLISHER_MAX_PREFIX_ENTRIES; + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + static constexpr uint16_t kMaxRoutingManagerPrefixEntries = BorderRouter::RoutingManager::kMaxPublishedPrefixes; +#else + static constexpr uint16_t kMaxRoutingManagerPrefixEntries = 0; +#endif class PrefixEntry : public Entry, private NonCopyable { friend class Entry; public: - void Init(Instance &aInstance) { Entry::Init(aInstance); } - bool IsInUse(void) const { return GetState() != kNoEntry; } - bool Matches(const Ip6::Prefix &aPrefix) const { return mPrefix == aPrefix; } - void Publish(const OnMeshPrefixConfig &aConfig); - void Publish(const ExternalRouteConfig &aConfig); - void Unpublish(void); - void HandleTimer(void) { Entry::HandleTimer(); } - void HandleNotifierEvents(Events aEvents); + void Init(Instance &aInstance) { Entry::Init(aInstance); } + bool IsInUse(void) const { return GetState() != kNoEntry; } + bool Matches(const Ip6::Prefix &aPrefix) const { return mPrefix == aPrefix; } + void Publish(const OnMeshPrefixConfig &aConfig, Requester aRequester); + void Publish(const ExternalRouteConfig &aConfig, Requester aRequester); + Requester GetRequester(void) const { return mRequester; } + void Unpublish(void); + void HandleTimer(void) { Entry::HandleTimer(); } + void HandleNotifierEvents(Events aEvents); private: static constexpr uint8_t kDesiredNumOnMeshPrefix = @@ -431,7 +483,7 @@ class Publisher : public InstanceLocator, private NonCopyable kTypeExternalRoute, }; - void Publish(const Ip6::Prefix &aPrefix, uint16_t aNewFlags, Type aNewType); + void Publish(const Ip6::Prefix &aPrefix, uint16_t aNewFlags, Type aNewType, Requester aRequester); void Add(void); Error AddOnMeshPrefix(void); Error AddExternalRoute(void); @@ -441,6 +493,7 @@ class Publisher : public InstanceLocator, private NonCopyable void CountExternalRouteEntries(uint8_t &aNumEntries, uint8_t &aNumPreferredEntries) const; Type mType; + Requester mRequester; Ip6::Prefix mPrefix; uint16_t mFlags; }; @@ -451,8 +504,8 @@ class Publisher : public InstanceLocator, private NonCopyable #endif #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE - PrefixEntry * FindOrAllocatePrefixEntry(const Ip6::Prefix &aPrefix); - PrefixEntry * FindMatchingPrefixEntry(const Ip6::Prefix &aPrefix); + PrefixEntry *FindOrAllocatePrefixEntry(const Ip6::Prefix &aPrefix, Requester aRequester); + PrefixEntry *FindMatchingPrefixEntry(const Ip6::Prefix &aPrefix); const PrefixEntry *FindMatchingPrefixEntry(const Ip6::Prefix &aPrefix) const; bool IsAPrefixEntry(const Entry &aEntry) const; void NotifyPrefixEntryChange(Event aEvent, const Ip6::Prefix &aPrefix) const; @@ -460,20 +513,20 @@ class Publisher : public InstanceLocator, private NonCopyable TimerMilli &GetTimer(void) { return mTimer; } void HandleNotifierEvents(Events aEvents); - static void HandleTimer(Timer &aTimer); void HandleTimer(void); + using PublisherTimer = TimerMilliIn; + #if OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE DnsSrpServiceEntry mDnsSrpServiceEntry; #endif #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE - PrefixEntry mPrefixEntries[kMaxPrefixEntries]; - PrefixCallback mPrefixCallback; - void * mPrefixCallbackContext; + PrefixEntry mPrefixEntries[kMaxUserPrefixEntries + kMaxRoutingManagerPrefixEntries]; + Callback mPrefixCallback; #endif - TimerMilli mTimer; + PublisherTimer mTimer; }; } // namespace NetworkData diff --git a/src/core/thread/network_data_service.cpp b/src/core/thread/network_data_service.cpp index 618812bb56e..1d2e90dbae7 100644 --- a/src/core/thread/network_data_service.cpp +++ b/src/core/thread/network_data_service.cpp @@ -83,7 +83,7 @@ Error Manager::RemoveService(const void *aServiceData, uint8_t aServiceDataLengt Error Manager::GetServiceId(const void *aServiceData, uint8_t aServiceDataLength, bool aServerStable, - uint8_t & aServiceId) const + uint8_t &aServiceId) const { ServiceData serviceData; @@ -94,11 +94,11 @@ Error Manager::GetServiceId(const void *aServiceData, #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) -void Manager::GetBackboneRouterPrimary(ot::BackboneRouter::BackboneRouterConfig &aConfig) const +void Manager::GetBackboneRouterPrimary(ot::BackboneRouter::Config &aConfig) const { - const ServerTlv * rvalServerTlv = nullptr; + const ServerTlv *rvalServerTlv = nullptr; const BackboneRouter::ServerData *rvalServerData = nullptr; - const ServiceTlv * serviceTlv = nullptr; + const ServiceTlv *serviceTlv = nullptr; ServiceData serviceData; serviceData.Init(&BackboneRouter::kServiceData, BackboneRouter::kServiceDataMinSize); @@ -146,13 +146,13 @@ void Manager::GetBackboneRouterPrimary(ot::BackboneRouter::BackboneRouterConfig return; } -bool Manager::IsBackboneRouterPreferredTo(const ServerTlv & aServerTlv, +bool Manager::IsBackboneRouterPreferredTo(const ServerTlv &aServerTlv, const BackboneRouter::ServerData &aServerData, - const ServerTlv & aOtherServerTlv, + const ServerTlv &aOtherServerTlv, const BackboneRouter::ServerData &aOtherServerData) const { bool isPreferred; - uint16_t leaderRloc16 = Mle::Mle::Rloc16FromRouterId(Get().GetLeaderId()); + uint16_t leaderRloc16 = Mle::Rloc16FromRouterId(Get().GetLeaderId()); VerifyOrExit(aServerTlv.GetServer16() != leaderRloc16, isPreferred = true); VerifyOrExit(aOtherServerTlv.GetServer16() != leaderRloc16, isPreferred = false); @@ -183,7 +183,7 @@ Error Manager::GetNextDnsSrpAnycastInfo(Iterator &aIterator, DnsSrpAnycast::Info tlv->GetServiceData(serviceData); aInfo.mAnycastAddress.SetToAnycastLocator(Get().GetMeshLocalPrefix(), - Mle::Mle::ServiceAlocFromId(tlv->GetServiceId())); + Mle::ServiceAlocFromId(tlv->GetServiceId())); aInfo.mSequenceNumber = reinterpret_cast(serviceData.GetBytes())->GetSequenceNumber(); @@ -288,6 +288,7 @@ Error Manager::GetNextDnsSrpUnicastInfo(Iterator &aIterator, DnsSrpUnicast::Info aInfo.mSockAddr.SetAddress(serverData->GetAddress()); aInfo.mSockAddr.SetPort(serverData->GetPort()); aInfo.mOrigin = DnsSrpUnicast::kFromServerData; + aInfo.mRloc16 = aIterator.mServerSubTlv->GetServer16(); ExitNow(); } @@ -300,6 +301,7 @@ Error Manager::GetNextDnsSrpUnicastInfo(Iterator &aIterator, DnsSrpUnicast::Info aIterator.mServerSubTlv->GetServer16()); aInfo.mSockAddr.SetPort(Encoding::BigEndian::ReadUint16(data.GetBytes())); aInfo.mOrigin = DnsSrpUnicast::kFromServerData; + aInfo.mRloc16 = aIterator.mServerSubTlv->GetServer16(); ExitNow(); } } @@ -322,6 +324,7 @@ Error Manager::GetNextDnsSrpUnicastInfo(Iterator &aIterator, DnsSrpUnicast::Info aInfo.mSockAddr.SetAddress(dnsServiceData->GetAddress()); aInfo.mSockAddr.SetPort(dnsServiceData->GetPort()); aInfo.mOrigin = DnsSrpUnicast::kFromServiceData; + aInfo.mRloc16 = Mle::kInvalidRloc16; ExitNow(); } diff --git a/src/core/thread/network_data_service.hpp b/src/core/thread/network_data_service.hpp index bd41dc775c1..5dec83a69b9 100644 --- a/src/core/thread/network_data_service.hpp +++ b/src/core/thread/network_data_service.hpp @@ -258,6 +258,7 @@ class DnsSrpUnicast { Ip6::SockAddr mSockAddr; ///< The socket address (IPv6 address and port) of the DNS/SRP server. Origin mOrigin; ///< The origin of the socket address (whether from service or server data). + uint16_t mRloc16; ///< The BR RLOC16 adding the entry (only used when `mOrigin == kFromServerData`). }; /** @@ -404,7 +405,7 @@ class Manager : public InstanceLocator, private NonCopyable private: const ServiceTlv *mServiceTlv; - const ServerTlv * mServerSubTlv; + const ServerTlv *mServerSubTlv; }; /** @@ -422,7 +423,7 @@ class Manager : public InstanceLocator, private NonCopyable /** * This method adds a Thread Service entry to the local Thread Network Data. * - * This version of `Add()` is intended for use with a `ServiceType` that has a constant service data + * This version of `Add()` is intended for use with a `ServiceType` that has a constant service data * format with a non-empty and potentially non-const server data format (provided as input parameter). * * The template type `ServiceType` has the following requirements: @@ -449,7 +450,7 @@ class Manager : public InstanceLocator, private NonCopyable /** * This method adds a Thread Service entry to the local Thread Network Data. * - * This version of `Add()` is intended for use with a `ServiceType` that has a non-const service data + * This version of `Add()` is intended for use with a `ServiceType` that has a non-const service data * format (provided as input parameter) with an empty server data. * * The template type `ServiceType` has the following requirements: @@ -494,8 +495,8 @@ class Manager : public InstanceLocator, private NonCopyable /** * This method removes a Thread Service entry from the local Thread Network Data. * - * This version of `Remove()` is intended for use with a `ServiceType` that has a non-const service data - * format (provided as input parameter). + * This version of `Remove()` is intended for use with a `ServiceType` that has a non-const service + * data format (provided as input parameter). * * The template type `ServiceType` has the following requirements: * - It MUST define nested type `ServiceType::ServiceData` representing the service data (and its format). @@ -543,7 +544,7 @@ class Manager : public InstanceLocator, private NonCopyable * @param[out] aConfig The Primary Backbone Router configuration. * */ - void GetBackboneRouterPrimary(ot::BackboneRouter::BackboneRouterConfig &aConfig) const; + void GetBackboneRouterPrimary(ot::BackboneRouter::Config &aConfig) const; #endif /** @@ -604,13 +605,13 @@ class Manager : public InstanceLocator, private NonCopyable Error GetServiceId(const void *aServiceData, uint8_t aServiceDataLength, bool aServerStable, - uint8_t & aServiceId) const; + uint8_t &aServiceId) const; Error IterateToNextServer(Iterator &aIterator) const; #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) - bool IsBackboneRouterPreferredTo(const ServerTlv & aServerTlv, + bool IsBackboneRouterPreferredTo(const ServerTlv &aServerTlv, const BackboneRouter::ServerData &aServerData, - const ServerTlv & aOtherServerTlv, + const ServerTlv &aOtherServerTlv, const BackboneRouter::ServerData &aOtherServerData) const; #endif }; diff --git a/src/core/thread/network_data_tlvs.cpp b/src/core/thread/network_data_tlvs.cpp index 6d2d4002be1..e81ec2d93c9 100644 --- a/src/core/thread/network_data_tlvs.cpp +++ b/src/core/thread/network_data_tlvs.cpp @@ -81,10 +81,7 @@ const NetworkDataTlv *NetworkDataTlv::Find(const NetworkDataTlv *aStart, //--------------------------------------------------------------------------------------------------------------------- // PrefixTlv -const NetworkDataTlv *PrefixTlv::FindSubTlv(Type aType) const -{ - return Find(GetSubTlvs(), GetNext(), aType); -} +const NetworkDataTlv *PrefixTlv::FindSubTlv(Type aType) const { return Find(GetSubTlvs(), GetNext(), aType); } const NetworkDataTlv *PrefixTlv::FindSubTlv(Type aType, bool aStable) const { diff --git a/src/core/thread/network_data_tlvs.hpp b/src/core/thread/network_data_tlvs.hpp index 48568c7d70d..443d1e1ba2d 100644 --- a/src/core/thread/network_data_tlvs.hpp +++ b/src/core/thread/network_data_tlvs.hpp @@ -73,10 +73,7 @@ class NetworkDataTlv; * @returns A `TlvType` pointer to `aTlv`. * */ -template TlvType *As(NetworkDataTlv *aTlv) -{ - return static_cast(aTlv); -} +template TlvType *As(NetworkDataTlv *aTlv) { return static_cast(aTlv); } /** * This template method casts a `NetworkDataTlv` pointer to a given subclass `TlvType` pointer. @@ -88,10 +85,7 @@ template TlvType *As(NetworkDataTlv *aTlv) * @returns A `TlvType` pointer to `aTlv`. * */ -template const TlvType *As(const NetworkDataTlv *aTlv) -{ - return static_cast(aTlv); -} +template const TlvType *As(const NetworkDataTlv *aTlv) { return static_cast(aTlv); } /** * This template method casts a `NetworkDataTlv` reference to a given subclass `TlvType` reference. @@ -103,10 +97,7 @@ template const TlvType *As(const NetworkDataTlv *aTlv) * @returns A `TlvType` reference to `aTlv`. * */ -template TlvType &As(NetworkDataTlv &aTlv) -{ - return static_cast(aTlv); -} +template TlvType &As(NetworkDataTlv &aTlv) { return static_cast(aTlv); } /** * This template method casts a `NetworkDataTlv` reference to a given subclass `TlvType` reference. @@ -118,10 +109,7 @@ template TlvType &As(NetworkDataTlv &aTlv) * @returns A `TlvType` reference to `aTlv`. * */ -template const TlvType &As(const NetworkDataTlv &aTlv) -{ - return static_cast(aTlv); -} +template const TlvType &As(const NetworkDataTlv &aTlv) { return static_cast(aTlv); } /** * This class implements Thread Network Data TLV generation and parsing. @@ -1561,7 +1549,7 @@ class ServerTlv : public NetworkDataTlv private: const uint8_t *GetServerData(void) const { return reinterpret_cast(this) + sizeof(*this); } - uint8_t * GetServerData(void) { return AsNonConst(AsConst(this)->GetServerData()); } + uint8_t *GetServerData(void) { return AsNonConst(AsConst(this)->GetServerData()); } uint16_t mServer16; } OT_TOOL_PACKED_END; diff --git a/src/core/thread/network_data_types.cpp b/src/core/thread/network_data_types.cpp index 628f780a744..24440cf53b5 100644 --- a/src/core/thread/network_data_types.cpp +++ b/src/core/thread/network_data_types.cpp @@ -131,8 +131,8 @@ uint16_t OnMeshPrefixConfig::ConvertToTlvFlags(void) const #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE -void OnMeshPrefixConfig::SetFrom(const PrefixTlv & aPrefixTlv, - const BorderRouterTlv & aBorderRouterTlv, +void OnMeshPrefixConfig::SetFrom(const PrefixTlv &aPrefixTlv, + const BorderRouterTlv &aBorderRouterTlv, const BorderRouterEntry &aBorderRouterEntry) { Clear(); @@ -191,9 +191,9 @@ uint8_t ExternalRouteConfig::ConvertToTlvFlags(void) const #endif // OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE -void ExternalRouteConfig::SetFrom(Instance & aInstance, - const PrefixTlv & aPrefixTlv, - const HasRouteTlv & aHasRouteTlv, +void ExternalRouteConfig::SetFrom(Instance &aInstance, + const PrefixTlv &aPrefixTlv, + const HasRouteTlv &aHasRouteTlv, const HasRouteEntry &aHasRouteEntry) { Clear(); @@ -249,5 +249,13 @@ void ServiceConfig::SetFrom(const ServiceTlv &aServiceTlv, const ServerTlv &aSer GetServerConfig().SetFrom(aServerTlv); } +void LowpanContextInfo::SetFrom(const PrefixTlv &aPrefixTlv, const ContextTlv &aContextTlv) +{ + mContextId = aContextTlv.GetContextId(); + mCompressFlag = aContextTlv.IsCompress(); + aPrefixTlv.CopyPrefixTo(GetPrefix()); + GetPrefix().SetLength(aContextTlv.GetContextLength()); +} + } // namespace NetworkData } // namespace ot diff --git a/src/core/thread/network_data_types.hpp b/src/core/thread/network_data_types.hpp index 82153439453..642b398e6a5 100644 --- a/src/core/thread/network_data_types.hpp +++ b/src/core/thread/network_data_types.hpp @@ -43,6 +43,7 @@ #include "common/data.hpp" #include "common/debug.hpp" #include "common/equatable.hpp" +#include "common/preference.hpp" #include "net/ip6_address.hpp" namespace ot { @@ -67,6 +68,7 @@ class HasRouteTlv; class HasRouteEntry; class ServiceTlv; class ServerTlv; +class ContextTlv; /** * This enumeration represents the Network Data type. @@ -89,6 +91,10 @@ enum RoutePreference : int8_t kRoutePreferenceHigh = OT_ROUTE_PREFERENCE_HIGH, ///< High route preference. }; +static_assert(kRoutePreferenceHigh == Preference::kHigh, "kRoutePreferenceHigh is not valid"); +static_assert(kRoutePreferenceMedium == Preference::kMedium, "kRoutePreferenceMedium is not valid"); +static_assert(kRoutePreferenceLow == Preference::kLow, "kRoutePreferenceLow is not valid"); + /** * This enumeration represents the border router RLOC role filter used when searching for border routers in the Network * Data. @@ -111,10 +117,7 @@ enum RoleFilter : uint8_t * @retval FALSE if @p aPref is not valid * */ -inline bool IsRoutePreferenceValid(int8_t aPref) -{ - return (aPref == kRoutePreferenceLow) || (aPref == kRoutePreferenceMedium) || (aPref == kRoutePreferenceHigh); -} +inline bool IsRoutePreferenceValid(int8_t aPref) { return Preference::IsValid(aPref); } /** * This function coverts a route preference to a 2-bit unsigned value. @@ -126,16 +129,7 @@ inline bool IsRoutePreferenceValid(int8_t aPref) * @returns The 2-bit unsigned value representing @p aPref. * */ -inline uint8_t RoutePreferenceToValue(int8_t aPref) -{ - constexpr uint8_t kHigh = 1; // 01 - constexpr uint8_t kMedium = 0; // 00 - constexpr uint8_t kLow = 3; // 11 - - OT_ASSERT(IsRoutePreferenceValid(aPref)); - - return (aPref == 0) ? kMedium : ((aPref > 0) ? kHigh : kLow); -} +inline uint8_t RoutePreferenceToValue(int8_t aPref) { return Preference::To2BitUint(aPref); } /** * This function coverts a 2-bit unsigned value to a route preference. @@ -148,18 +142,19 @@ inline uint8_t RoutePreferenceToValue(int8_t aPref) */ inline RoutePreference RoutePreferenceFromValue(uint8_t aValue) { - constexpr uint8_t kMask = 3; // First two bits. - - static const RoutePreference kRoutePreferences[] = { - /* 0 (00) -> */ kRoutePreferenceMedium, - /* 1 (01) -> */ kRoutePreferenceHigh, - /* 2 (10) -> */ kRoutePreferenceMedium, // Per RFC-4191, the reserved value (10) MUST be treated as (00) - /* 3 (11) -> */ kRoutePreferenceLow, - }; - - return kRoutePreferences[aValue & kMask]; + return static_cast(Preference::From2BitUint(aValue)); } +/** + * This function converts a router preference to a human-readable string. + * + * @param[in] aPreference The preference to convert + * + * @returns The string representation of @p aPreference. + * + */ +inline const char *RoutePreferenceToString(RoutePreference aPreference) { return Preference::ToString(aPreference); } + /** * This class represents an On-mesh Prefix (Border Router) configuration. * @@ -215,8 +210,8 @@ class OnMeshPrefixConfig : public otBorderRouterConfig, #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE uint16_t ConvertToTlvFlags(void) const; #endif - void SetFrom(const PrefixTlv & aPrefixTlv, - const BorderRouterTlv & aBorderRouterTlv, + void SetFrom(const PrefixTlv &aPrefixTlv, + const BorderRouterTlv &aBorderRouterTlv, const BorderRouterEntry &aBorderRouterEntry); void SetFromTlvFlags(uint16_t aFlags); }; @@ -275,13 +270,35 @@ class ExternalRouteConfig : public otExternalRouteConfig, #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE uint8_t ConvertToTlvFlags(void) const; #endif - void SetFrom(Instance & aInstance, - const PrefixTlv & aPrefixTlv, - const HasRouteTlv & aHasRouteTlv, + void SetFrom(Instance &aInstance, + const PrefixTlv &aPrefixTlv, + const HasRouteTlv &aHasRouteTlv, const HasRouteEntry &aHasRouteEntry); void SetFromTlvFlags(uint8_t aFlags); }; +/** + * This class represents 6LoWPAN Context ID information associated with a prefix in Network Data. + * + */ +class LowpanContextInfo : public otLowpanContextInfo, public Clearable +{ + friend class NetworkData; + +public: + /** + * This method gets the prefix. + * + * @return The prefix. + * + */ + const Ip6::Prefix &GetPrefix(void) const { return AsCoreType(&mPrefix); } + +private: + Ip6::Prefix &GetPrefix(void) { return AsCoreType(&mPrefix); } + void SetFrom(const PrefixTlv &aPrefixTlv, const ContextTlv &aContextTlv); +}; + /** * This class represents a Service Data. * @@ -382,6 +399,7 @@ class ServiceConfig : public otServiceConfig, public Clearable, p DefineCoreType(otBorderRouterConfig, NetworkData::OnMeshPrefixConfig); DefineCoreType(otExternalRouteConfig, NetworkData::ExternalRouteConfig); +DefineCoreType(otLowpanContextInfo, NetworkData::LowpanContextInfo); DefineCoreType(otServiceConfig, NetworkData::ServiceConfig); DefineCoreType(otServerConfig, NetworkData::ServiceConfig::ServerConfig); diff --git a/src/core/thread/network_diagnostic.cpp b/src/core/thread/network_diagnostic.cpp index 738e904abb8..dfa8570e799 100644 --- a/src/core/thread/network_diagnostic.cpp +++ b/src/core/thread/network_diagnostic.cpp @@ -33,8 +33,6 @@ #include "network_diagnostic.hpp" -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE - #include "coap/coap_message.hpp" #include "common/array.hpp" #include "common/as_core_type.hpp" @@ -50,7 +48,7 @@ #include "thread/mle_router.hpp" #include "thread/thread_netif.hpp" #include "thread/thread_tlvs.hpp" -#include "thread/uri_paths.hpp" +#include "thread/version.hpp" namespace ot { @@ -58,135 +56,88 @@ RegisterLogModule("NetDiag"); namespace NetworkDiagnostic { -NetworkDiagnostic::NetworkDiagnostic(Instance &aInstance) +const char Server::kVendorName[] = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME; +const char Server::kVendorModel[] = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL; +const char Server::kVendorSwVersion[] = OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION; + +//--------------------------------------------------------------------------------------------------------------------- +// Server + +Server::Server(Instance &aInstance) : InstanceLocator(aInstance) - , mDiagnosticGetRequest(UriPath::kDiagnosticGetRequest, &NetworkDiagnostic::HandleDiagnosticGetRequest, this) - , mDiagnosticGetQuery(UriPath::kDiagnosticGetQuery, &NetworkDiagnostic::HandleDiagnosticGetQuery, this) - , mDiagnosticGetAnswer(UriPath::kDiagnosticGetAnswer, &NetworkDiagnostic::HandleDiagnosticGetAnswer, this) - , mDiagnosticReset(UriPath::kDiagnosticReset, &NetworkDiagnostic::HandleDiagnosticReset, this) - , mReceiveDiagnosticGetCallback(nullptr) - , mReceiveDiagnosticGetCallbackContext(nullptr) { - Get().AddResource(mDiagnosticGetRequest); - Get().AddResource(mDiagnosticGetQuery); - Get().AddResource(mDiagnosticGetAnswer); - Get().AddResource(mDiagnosticReset); + static_assert(sizeof(kVendorName) <= sizeof(VendorNameTlv::StringType), "VENDOR_NAME is too long"); + static_assert(sizeof(kVendorModel) <= sizeof(VendorModelTlv::StringType), "VENDOR_MODEL is too long"); + static_assert(sizeof(kVendorSwVersion) <= sizeof(VendorSwVersionTlv::StringType), "VENDOR_SW_VERSION is too long"); + +#if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE + memcpy(mVendorName, kVendorName, sizeof(kVendorName)); + memcpy(mVendorModel, kVendorModel, sizeof(kVendorModel)); + memcpy(mVendorSwVersion, kVendorSwVersion, sizeof(kVendorSwVersion)); +#endif } -Error NetworkDiagnostic::SendDiagnosticGet(const Ip6::Address & aDestination, - const uint8_t aTlvTypes[], - uint8_t aCount, - otReceiveDiagnosticGetCallback aCallback, - void * aCallbackContext) -{ - Error error; - Coap::Message * message = nullptr; - Tmf::MessageInfo messageInfo(GetInstance()); - otCoapResponseHandler handler = nullptr; +#if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE - if (aDestination.IsMulticast()) - { - message = Get().NewNonConfirmablePostMessage(UriPath::kDiagnosticGetQuery); - messageInfo.SetMulticastLoop(true); - } - else - { - handler = &NetworkDiagnostic::HandleDiagnosticGetResponse; - message = Get().NewConfirmablePostMessage(UriPath::kDiagnosticGetRequest); - } +Error Server::SetVendorName(const char *aVendorName) +{ + return SetVendorString(mVendorName, sizeof(mVendorName), aVendorName); +} - VerifyOrExit(message != nullptr, error = kErrorNoBufs); +Error Server::SetVendorModel(const char *aVendorModel) +{ + return SetVendorString(mVendorModel, sizeof(mVendorModel), aVendorModel); +} - if (aCount > 0) - { - SuccessOrExit(error = Tlv::Append(*message, aTlvTypes, aCount)); - } +Error Server::SetVendorSwVersion(const char *aVendorSwVersion) +{ + return SetVendorString(mVendorSwVersion, sizeof(mVendorSwVersion), aVendorSwVersion); +} - if (aDestination.IsLinkLocal() || aDestination.IsLinkLocalMulticast()) - { - messageInfo.SetSockAddr(Get().GetLinkLocalAddress()); - } - else - { - messageInfo.SetSockAddrToRloc(); - } +Error Server::SetVendorString(char *aDestString, uint16_t kMaxSize, const char *aSrcString) +{ + Error error = kErrorInvalidArgs; + uint16_t length; - messageInfo.SetPeerAddr(aDestination); + VerifyOrExit(aSrcString != nullptr); - SuccessOrExit(error = Get().SendMessage(*message, messageInfo, handler, this)); + length = StringLength(aSrcString, kMaxSize); + VerifyOrExit(length < kMaxSize); - mReceiveDiagnosticGetCallback = aCallback; - mReceiveDiagnosticGetCallbackContext = aCallbackContext; + VerifyOrExit(IsValidUtf8String(aSrcString)); - LogInfo("Sent diagnostic get"); + memcpy(aDestString, aSrcString, length + 1); + error = kErrorNone; exit: - FreeMessageOnError(message, error); return error; } -void NetworkDiagnostic::HandleDiagnosticGetResponse(void * aContext, - otMessage * aMessage, - const otMessageInfo *aMessageInfo, - Error aResult) -{ - static_cast(aContext)->HandleDiagnosticGetResponse(AsCoapMessagePtr(aMessage), - AsCoreTypePtr(aMessageInfo), aResult); -} +#endif // OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE -void NetworkDiagnostic::HandleDiagnosticGetResponse(Coap::Message * aMessage, - const Ip6::MessageInfo *aMessageInfo, - Error aResult) +void Server::PrepareMessageInfoForDest(const Ip6::Address &aDestination, Tmf::MessageInfo &aMessageInfo) const { - SuccessOrExit(aResult); - VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, aResult = kErrorFailed); - -exit: - if (mReceiveDiagnosticGetCallback) + if (aDestination.IsMulticast()) { - mReceiveDiagnosticGetCallback(aResult, aMessage, aMessageInfo, mReceiveDiagnosticGetCallbackContext); + aMessageInfo.SetMulticastLoop(true); } - else + + if (aDestination.IsLinkLocal() || aDestination.IsLinkLocalMulticast()) { - LogDebg("Received diagnostic get response, error = %s", ErrorToString(aResult)); + aMessageInfo.SetSockAddr(Get().GetLinkLocalAddress()); } - return; -} - -void NetworkDiagnostic::HandleDiagnosticGetAnswer(void * aContext, - otMessage * aMessage, - const otMessageInfo *aMessageInfo) -{ - static_cast(aContext)->HandleDiagnosticGetAnswer(AsCoapMessage(aMessage), - AsCoreType(aMessageInfo)); -} - -void NetworkDiagnostic::HandleDiagnosticGetAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) -{ - VerifyOrExit(aMessage.IsConfirmablePostRequest()); - - LogInfo("Diagnostic get answer received"); - - if (mReceiveDiagnosticGetCallback) + else { - mReceiveDiagnosticGetCallback(kErrorNone, &aMessage, &aMessageInfo, mReceiveDiagnosticGetCallbackContext); + aMessageInfo.SetSockAddrToRloc(); } - SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); - - LogInfo("Sent diagnostic answer acknowledgment"); - -exit: - return; + aMessageInfo.SetPeerAddr(aDestination); } -Error NetworkDiagnostic::AppendIp6AddressList(Message &aMessage) +Error Server::AppendIp6AddressList(Message &aMessage) { - Error error = kErrorNone; - Ip6AddressListTlv tlv; - uint8_t count = 0; - - tlv.Init(); + Error error = kErrorNone; + uint16_t count = 0; for (const Ip6::Netif::UnicastAddress &addr : Get().GetUnicastAddresses()) { @@ -194,8 +145,22 @@ Error NetworkDiagnostic::AppendIp6AddressList(Message &aMessage) count++; } - tlv.SetLength(count * sizeof(Ip6::Address)); - SuccessOrExit(error = aMessage.Append(tlv)); + if (count * Ip6::Address::kSize <= Tlv::kBaseTlvMaxLength) + { + Tlv tlv; + + tlv.SetType(Tlv::kIp6AddressList); + tlv.SetLength(static_cast(count * Ip6::Address::kSize)); + SuccessOrExit(error = aMessage.Append(tlv)); + } + else + { + ExtendedTlv extTlv; + + extTlv.SetType(Tlv::kIp6AddressList); + extTlv.SetLength(count * Ip6::Address::kSize); + SuccessOrExit(error = aMessage.Append(extTlv)); + } for (const Ip6::Netif::UnicastAddress &addr : Get().GetUnicastAddresses()) { @@ -203,445 +168,440 @@ Error NetworkDiagnostic::AppendIp6AddressList(Message &aMessage) } exit: - return error; } #if OPENTHREAD_FTD -Error NetworkDiagnostic::AppendChildTable(Message &aMessage) +Error Server::AppendChildTable(Message &aMessage) { - Error error = kErrorNone; - uint16_t count = 0; - uint8_t timeout = 0; - ChildTableTlv tlv; - ChildTableEntry entry; + Error error = kErrorNone; + uint16_t count; - tlv.Init(); + VerifyOrExit(Get().IsRouterOrLeader()); - count = Get().GetNumChildren(Child::kInStateValid); + count = Min(Get().GetNumChildren(Child::kInStateValid), kMaxChildEntries); - // The length of the Child Table TLV may exceed the outgoing link's MTU (1280B). - // As a workaround we limit the number of entries in the Child Table TLV, - // also to avoid using extended TLV format. The issue is processed by the - // Thread Group (SPEC-894). - if (count > (Tlv::kBaseTlvMaxLength / sizeof(ChildTableEntry))) + if (count * sizeof(ChildTableEntry) <= Tlv::kBaseTlvMaxLength) { - count = Tlv::kBaseTlvMaxLength / sizeof(ChildTableEntry); - } + Tlv tlv; - tlv.SetLength(static_cast(count * sizeof(ChildTableEntry))); + tlv.SetType(Tlv::kChildTable); + tlv.SetLength(static_cast(count * sizeof(ChildTableEntry))); + SuccessOrExit(error = aMessage.Append(tlv)); + } + else + { + ExtendedTlv extTlv; - SuccessOrExit(error = aMessage.Append(tlv)); + extTlv.SetType(Tlv::kChildTable); + extTlv.SetLength(count * sizeof(ChildTableEntry)); + SuccessOrExit(error = aMessage.Append(extTlv)); + } for (Child &child : Get().Iterate(Child::kInStateValid)) { - VerifyOrExit(count--); + uint8_t timeout = 0; + ChildTableEntry entry; - timeout = 0; + VerifyOrExit(count--); while (static_cast(1 << timeout) < child.GetTimeout()) { timeout++; } - entry.SetReserved(0); + entry.Clear(); entry.SetTimeout(timeout + 4); - - entry.SetChildId(Mle::Mle::ChildIdFromRloc16(child.GetRloc16())); + entry.SetLinkQuality(child.GetLinkQualityIn()); + entry.SetChildId(Mle::ChildIdFromRloc16(child.GetRloc16())); entry.SetMode(child.GetDeviceMode()); SuccessOrExit(error = aMessage.Append(entry)); } exit: - return error; } #endif // OPENTHREAD_FTD -void NetworkDiagnostic::FillMacCountersTlv(MacCountersTlv &aMacCountersTlv) +Error Server::AppendMacCounters(Message &aMessage) { - const otMacCounters &macCounters = Get().GetCounters(); - - aMacCountersTlv.SetIfInUnknownProtos(macCounters.mRxOther); - aMacCountersTlv.SetIfInErrors(macCounters.mRxErrNoFrame + macCounters.mRxErrUnknownNeighbor + - macCounters.mRxErrInvalidSrcAddr + macCounters.mRxErrSec + macCounters.mRxErrFcs + - macCounters.mRxErrOther); - aMacCountersTlv.SetIfOutErrors(macCounters.mTxErrCca); - aMacCountersTlv.SetIfInUcastPkts(macCounters.mRxUnicast); - aMacCountersTlv.SetIfInBroadcastPkts(macCounters.mRxBroadcast); - aMacCountersTlv.SetIfInDiscards(macCounters.mRxAddressFiltered + macCounters.mRxDestAddrFiltered + - macCounters.mRxDuplicated); - aMacCountersTlv.SetIfOutUcastPkts(macCounters.mTxUnicast); - aMacCountersTlv.SetIfOutBroadcastPkts(macCounters.mTxBroadcast); - aMacCountersTlv.SetIfOutDiscards(macCounters.mTxErrBusyChannel); + MacCountersTlv tlv; + const otMacCounters &counters = Get().GetCounters(); + + memset(&tlv, 0, sizeof(tlv)); + + tlv.Init(); + tlv.SetIfInUnknownProtos(counters.mRxOther); + tlv.SetIfInErrors(counters.mRxErrNoFrame + counters.mRxErrUnknownNeighbor + counters.mRxErrInvalidSrcAddr + + counters.mRxErrSec + counters.mRxErrFcs + counters.mRxErrOther); + tlv.SetIfOutErrors(counters.mTxErrCca); + tlv.SetIfInUcastPkts(counters.mRxUnicast); + tlv.SetIfInBroadcastPkts(counters.mRxBroadcast); + tlv.SetIfInDiscards(counters.mRxAddressFiltered + counters.mRxDestAddrFiltered + counters.mRxDuplicated); + tlv.SetIfOutUcastPkts(counters.mTxUnicast); + tlv.SetIfOutBroadcastPkts(counters.mTxBroadcast); + tlv.SetIfOutDiscards(counters.mTxErrBusyChannel); + + return tlv.AppendTo(aMessage); } -Error NetworkDiagnostic::FillRequestedTlvs(const Message & aRequest, - Message & aResponse, - NetworkDiagnosticTlv &aNetworkDiagnosticTlv) +Error Server::AppendRequestedTlvs(const Message &aRequest, Message &aResponse) { - Error error = kErrorNone; - uint16_t offset = 0; - uint8_t type; + Error error; + uint16_t offset; + uint16_t length; + uint16_t endOffset; - offset = aRequest.GetOffset() + sizeof(NetworkDiagnosticTlv); + SuccessOrExit(error = Tlv::FindTlvValueOffset(aRequest, Tlv::kTypeList, offset, length)); + endOffset = offset + length; - for (uint32_t i = 0; i < aNetworkDiagnosticTlv.GetLength(); i++) + for (; offset < endOffset; offset++) { - SuccessOrExit(error = aRequest.Read(offset, type)); + uint8_t tlvType; - LogInfo("Type %d", type); + SuccessOrExit(error = aRequest.Read(offset, tlvType)); + SuccessOrExit(error = AppendDiagTlv(tlvType, aResponse)); + } - switch (type) - { - case NetworkDiagnosticTlv::kExtMacAddress: - SuccessOrExit(error = Tlv::Append(aResponse, Get().GetExtAddress())); - break; +exit: + return error; +} - case NetworkDiagnosticTlv::kAddress16: - SuccessOrExit(error = Tlv::Append(aResponse, Get().GetRloc16())); - break; +Error Server::AppendDiagTlv(uint8_t aTlvType, Message &aMessage) +{ + Error error = kErrorNone; - case NetworkDiagnosticTlv::kMode: - SuccessOrExit(error = Tlv::Append(aResponse, Get().GetDeviceMode().Get())); - break; + switch (aTlvType) + { + case Tlv::kExtMacAddress: + error = Tlv::Append(aMessage, Get().GetExtAddress()); + break; - case NetworkDiagnosticTlv::kTimeout: - if (!Get().IsRxOnWhenIdle()) - { - SuccessOrExit(error = Tlv::Append(aResponse, Get().GetTimeout())); - } + case Tlv::kAddress16: + error = Tlv::Append(aMessage, Get().GetRloc16()); + break; - break; + case Tlv::kMode: + error = Tlv::Append(aMessage, Get().GetDeviceMode().Get()); + break; -#if OPENTHREAD_FTD - case NetworkDiagnosticTlv::kConnectivity: - { - ConnectivityTlv tlv; - tlv.Init(); - Get().FillConnectivityTlv(reinterpret_cast(tlv)); - SuccessOrExit(error = tlv.AppendTo(aResponse)); - break; - } + case Tlv::kVersion: + error = Tlv::Append(aMessage, kThreadVersion); + break; - case NetworkDiagnosticTlv::kRoute: - { - RouteTlv tlv; - tlv.Init(); - Get().FillRouteTlv(reinterpret_cast(tlv)); - SuccessOrExit(error = tlv.AppendTo(aResponse)); - break; - } -#endif + case Tlv::kTimeout: + VerifyOrExit(!Get().IsRxOnWhenIdle()); + error = Tlv::Append(aMessage, Get().GetTimeout()); + break; - case NetworkDiagnosticTlv::kLeaderData: - { - LeaderDataTlv tlv; - const Mle::LeaderData &leaderData = Get().GetLeaderData(); + case Tlv::kLeaderData: + { + LeaderDataTlv tlv; - tlv.Init(); - tlv.SetPartitionId(leaderData.GetPartitionId()); - tlv.SetWeighting(leaderData.GetWeighting()); - tlv.SetDataVersion(leaderData.GetDataVersion(NetworkData::kFullSet)); - tlv.SetStableDataVersion(leaderData.GetDataVersion(NetworkData::kStableSubset)); - tlv.SetLeaderRouterId(leaderData.GetLeaderRouterId()); + tlv.Init(); + tlv.Set(Get().GetLeaderData()); + error = tlv.AppendTo(aMessage); + break; + } - SuccessOrExit(error = tlv.AppendTo(aResponse)); - break; - } + case Tlv::kNetworkData: + error = Tlv::Append(aMessage, Get().GetBytes(), + Get().GetLength()); + break; - case NetworkDiagnosticTlv::kNetworkData: - { - NetworkData::NetworkData &netData = Get(); + case Tlv::kIp6AddressList: + error = AppendIp6AddressList(aMessage); + break; - SuccessOrExit(error = Tlv::Append(aResponse, netData.GetBytes(), netData.GetLength())); - break; - } + case Tlv::kMacCounters: + error = AppendMacCounters(aMessage); + break; - case NetworkDiagnosticTlv::kIp6AddressList: - { - SuccessOrExit(error = AppendIp6AddressList(aResponse)); - break; - } + case Tlv::kVendorName: + error = Tlv::Append(aMessage, GetVendorName()); + break; - case NetworkDiagnosticTlv::kMacCounters: - { - MacCountersTlv tlv; - memset(&tlv, 0, sizeof(tlv)); - tlv.Init(); - FillMacCountersTlv(tlv); - SuccessOrExit(error = tlv.AppendTo(aResponse)); - break; - } + case Tlv::kVendorModel: + error = Tlv::Append(aMessage, GetVendorModel()); + break; - case NetworkDiagnosticTlv::kBatteryLevel: - { - // Thread 1.1.1 Specification Section 10.11.4.2: - // Omitted if the battery level is not measured, is unknown or the device does not - // operate on battery power. - break; - } + case Tlv::kVendorSwVersion: + error = Tlv::Append(aMessage, GetVendorSwVersion()); + break; - case NetworkDiagnosticTlv::kSupplyVoltage: - { - // Thread 1.1.1 Specification Section 10.11.4.3: - // Omitted if the supply voltage is not measured, is unknown. - break; - } + case Tlv::kThreadStackVersion: + error = Tlv::Append(aMessage, otGetVersionString()); + break; -#if OPENTHREAD_FTD - case NetworkDiagnosticTlv::kChildTable: + case Tlv::kChannelPages: + { + ChannelPagesTlv tlv; + uint8_t length = 0; + + tlv.Init(); + + for (uint8_t page = 0; page < sizeof(Radio::kSupportedChannelPages) * CHAR_BIT; page++) { - // Thread 1.1.1 Specification Section 10.11.2.2: - // If a Thread device is unable to supply a specific Diagnostic TLV, that TLV is omitted. - // Here only Leader or Router may have children. - if (Get().IsRouterOrLeader()) + if (Radio::kSupportedChannelPages & (1 << page)) { - SuccessOrExit(error = AppendChildTable(aResponse)); + tlv.GetChannelPages()[length++] = page; } - break; } -#endif - - case NetworkDiagnosticTlv::kChannelPages: - { - uint8_t length = 0; - uint32_t pageMask = Radio::kSupportedChannelPages; - ChannelPagesTlv tlv; - tlv.Init(); - for (uint8_t page = 0; page < sizeof(pageMask) * 8; page++) - { - if (pageMask & (1 << page)) - { - tlv.GetChannelPages()[length++] = page; - } - } + tlv.SetLength(length); + error = tlv.AppendTo(aMessage); - tlv.SetLength(length); - SuccessOrExit(error = tlv.AppendTo(aResponse)); - break; - } + break; + } #if OPENTHREAD_FTD - case NetworkDiagnosticTlv::kMaxChildTimeout: - { - uint32_t maxTimeout; - if (Get().GetMaxChildTimeout(maxTimeout) == kErrorNone) - { - SuccessOrExit(error = Tlv::Append(aResponse, maxTimeout)); - } + case Tlv::kConnectivity: + { + ConnectivityTlv tlv; - break; - } -#endif + tlv.Init(); + Get().FillConnectivityTlv(tlv); + error = tlv.AppendTo(aMessage); + break; + } - default: - // Skip unrecognized TLV type. - break; - } + case Tlv::kRoute: + { + RouteTlv tlv; + + tlv.Init(); + Get().FillRouteTlv(tlv); + SuccessOrExit(error = tlv.AppendTo(aMessage)); + break; + } + + case Tlv::kChildTable: + error = AppendChildTable(aMessage); + break; - offset += sizeof(type); + case Tlv::kMaxChildTimeout: + { + uint32_t maxTimeout; + + SuccessOrExit(Get().GetMaxChildTimeout(maxTimeout)); + error = Tlv::Append(aMessage, maxTimeout); + break; + } + +#endif // OPENTHREAD_FTD + + default: + break; } exit: return error; } -void NetworkDiagnostic::HandleDiagnosticGetQuery(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +template <> +void Server::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - static_cast(aContext)->HandleDiagnosticGetQuery(AsCoapMessage(aMessage), - AsCoreType(aMessageInfo)); -} - -void NetworkDiagnostic::HandleDiagnosticGetQuery(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) -{ - Error error = kErrorNone; - Coap::Message * message = nullptr; - NetworkDiagnosticTlv networkDiagnosticTlv; - Tmf::MessageInfo messageInfo(GetInstance()); + Error error = kErrorNone; + Coap::Message *response = nullptr; + Tmf::MessageInfo responseInfo(GetInstance()); VerifyOrExit(aMessage.IsPostRequest(), error = kErrorDrop); - LogInfo("Received diagnostic get query"); - - SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), networkDiagnosticTlv)); + LogInfo("Received %s from %s", UriToString(), + aMessageInfo.GetPeerAddr().ToString().AsCString()); - VerifyOrExit(networkDiagnosticTlv.GetType() == NetworkDiagnosticTlv::kTypeList, error = kErrorParse); - - // DIAG_GET.qry may be sent as a confirmable message. + // DIAG_GET.qry may be sent as a confirmable request. if (aMessage.IsConfirmable()) { - if (Get().SendEmptyAck(aMessage, aMessageInfo) == kErrorNone) - { - LogInfo("Sent diagnostic get query acknowledgment"); - } + IgnoreError(Get().SendEmptyAck(aMessage, aMessageInfo)); } - message = Get().NewConfirmablePostMessage(UriPath::kDiagnosticGetAnswer); - VerifyOrExit(message != nullptr, error = kErrorNoBufs); + response = Get().NewConfirmablePostMessage(kUriDiagnosticGetAnswer); + VerifyOrExit(response != nullptr, error = kErrorNoBufs); - if (aMessageInfo.GetPeerAddr().IsLinkLocal()) - { - messageInfo.SetSockAddr(Get().GetLinkLocalAddress()); - } - else - { - messageInfo.SetSockAddrToRloc(); - } + SuccessOrExit(error = AppendRequestedTlvs(aMessage, *response)); + + PrepareMessageInfoForDest(aMessageInfo.GetPeerAddr(), responseInfo); + SuccessOrExit(error = Get().SendMessage(*response, responseInfo)); - messageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr()); +exit: + FreeMessageOnError(response, error); +} + +template <> +void Server::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + Error error = kErrorNone; + Coap::Message *response = nullptr; + + VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorDrop); - SuccessOrExit(error = FillRequestedTlvs(aMessage, *message, networkDiagnosticTlv)); + LogInfo("Received %s from %s", UriToString(), + aMessageInfo.GetPeerAddr().ToString().AsCString()); - SuccessOrExit(error = Get().SendMessage(*message, messageInfo, nullptr, this)); + response = Get().NewResponseMessage(aMessage); + VerifyOrExit(response != nullptr, error = kErrorNoBufs); - LogInfo("Sent diagnostic get answer"); + SuccessOrExit(error = AppendRequestedTlvs(aMessage, *response)); + SuccessOrExit(error = Get().SendMessage(*response, aMessageInfo)); exit: - FreeMessageOnError(message, error); + FreeMessageOnError(response, error); } -void NetworkDiagnostic::HandleDiagnosticGetRequest(void * aContext, - otMessage * aMessage, - const otMessageInfo *aMessageInfo) +template <> void Server::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - static_cast(aContext)->HandleDiagnosticGetRequest(AsCoapMessage(aMessage), - AsCoreType(aMessageInfo)); -} + uint16_t offset = 0; + uint8_t type; + Tlv tlv; -void NetworkDiagnostic::HandleDiagnosticGetRequest(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) -{ - Error error = kErrorNone; - Coap::Message * message = nullptr; - NetworkDiagnosticTlv networkDiagnosticTlv; - Ip6::MessageInfo messageInfo(aMessageInfo); + VerifyOrExit(aMessage.IsConfirmablePostRequest()); - VerifyOrExit(aMessage.IsConfirmablePostRequest(), error = kErrorDrop); + LogInfo("Received %s from %s", UriToString(), + aMessageInfo.GetPeerAddr().ToString().AsCString()); - LogInfo("Received diagnostic get request"); + SuccessOrExit(aMessage.Read(aMessage.GetOffset(), tlv)); - SuccessOrExit(error = aMessage.Read(aMessage.GetOffset(), networkDiagnosticTlv)); + VerifyOrExit(tlv.GetType() == Tlv::kTypeList); - VerifyOrExit(networkDiagnosticTlv.GetType() == NetworkDiagnosticTlv::kTypeList, error = kErrorParse); + offset = aMessage.GetOffset() + sizeof(Tlv); - message = Get().NewResponseMessage(aMessage); - VerifyOrExit(message != nullptr, error = kErrorNoBufs); + for (uint8_t i = 0; i < tlv.GetLength(); i++) + { + SuccessOrExit(aMessage.Read(offset + i, type)); - SuccessOrExit(error = FillRequestedTlvs(aMessage, *message, networkDiagnosticTlv)); + switch (type) + { + case Tlv::kMacCounters: + Get().ResetCounters(); + break; - SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); + default: + break; + } + } - LogInfo("Sent diagnostic get response"); + IgnoreError(Get().SendEmptyAck(aMessage, aMessageInfo)); exit: - FreeMessageOnError(message, error); + return; } -Error NetworkDiagnostic::SendDiagnosticReset(const Ip6::Address &aDestination, - const uint8_t aTlvTypes[], - uint8_t aCount) -{ - Error error; - Coap::Message * message = nullptr; - Tmf::MessageInfo messageInfo(GetInstance()); +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE - message = Get().NewConfirmablePostMessage(UriPath::kDiagnosticReset); - VerifyOrExit(message != nullptr, error = kErrorNoBufs); +//--------------------------------------------------------------------------------------------------------------------- +// Client - if (aCount > 0) - { - SuccessOrExit(error = Tlv::Append(*message, aTlvTypes, aCount)); - } +Client::Client(Instance &aInstance) + : InstanceLocator(aInstance) +{ +} - if (aDestination.IsLinkLocal() || aDestination.IsLinkLocalMulticast()) +Error Client::SendDiagnosticGet(const Ip6::Address &aDestination, + const uint8_t aTlvTypes[], + uint8_t aCount, + GetCallback aCallback, + void *aContext) +{ + Error error; + + if (aDestination.IsMulticast()) { - messageInfo.SetSockAddr(Get().GetLinkLocalAddress()); + error = SendCommand(kUriDiagnosticGetQuery, aDestination, aTlvTypes, aCount); } else { - messageInfo.SetSockAddrToRloc(); + error = SendCommand(kUriDiagnosticGetRequest, aDestination, aTlvTypes, aCount, &HandleGetResponse, this); } - messageInfo.SetPeerAddr(aDestination); + SuccessOrExit(error); - SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - - LogInfo("Sent network diagnostic reset"); + mGetCallback.Set(aCallback, aContext); exit: - FreeMessageOnError(message, error); return error; } -void NetworkDiagnostic::HandleDiagnosticReset(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +Error Client::SendCommand(Uri aUri, + const Ip6::Address &aDestination, + const uint8_t aTlvTypes[], + uint8_t aCount, + Coap::ResponseHandler aHandler, + void *aContext) { - static_cast(aContext)->HandleDiagnosticReset(AsCoapMessage(aMessage), - AsCoreType(aMessageInfo)); -} + Error error; + Coap::Message *message = nullptr; + Tmf::MessageInfo messageInfo(GetInstance()); -void NetworkDiagnostic::HandleDiagnosticReset(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) -{ - uint16_t offset = 0; - uint8_t type; - NetworkDiagnosticTlv tlv; + switch (aUri) + { + case kUriDiagnosticGetQuery: + message = Get().NewNonConfirmablePostMessage(aUri); + break; - LogInfo("Received diagnostic reset request"); + case kUriDiagnosticGetRequest: + case kUriDiagnosticReset: + message = Get().NewConfirmablePostMessage(aUri); + break; - VerifyOrExit(aMessage.IsConfirmablePostRequest()); + default: + OT_ASSERT(false); + } - SuccessOrExit(aMessage.Read(aMessage.GetOffset(), tlv)); + VerifyOrExit(message != nullptr, error = kErrorNoBufs); - VerifyOrExit(tlv.GetType() == NetworkDiagnosticTlv::kTypeList); + if (aCount > 0) + { + SuccessOrExit(error = Tlv::Append(*message, aTlvTypes, aCount)); + } - offset = aMessage.GetOffset() + sizeof(NetworkDiagnosticTlv); + Get().PrepareMessageInfoForDest(aDestination, messageInfo); - for (uint8_t i = 0; i < tlv.GetLength(); i++) - { - SuccessOrExit(aMessage.Read(offset + i, type)); + SuccessOrExit(error = Get().SendMessage(*message, messageInfo, aHandler, aContext)); - switch (type) - { - case NetworkDiagnosticTlv::kMacCounters: - Get().ResetCounters(); - LogInfo("Received diagnostic reset type kMacCounters(9)"); - break; + LogInfo("Sent %s to %s", UriToString(aUri), aDestination.ToString().AsCString()); - default: - LogInfo("Received diagnostic reset other type %d not resetable", type); - break; - } - } +exit: + FreeMessageOnError(message, error); + return error; +} - SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); +void Client::HandleGetResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult) +{ + static_cast(aContext)->HandleGetResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo), + aResult); +} - LogInfo("Sent diagnostic reset acknowledgment"); +void Client::HandleGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult) +{ + SuccessOrExit(aResult); + VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, aResult = kErrorFailed); exit: - return; + mGetCallback.InvokeIfSet(aResult, aMessage, aMessageInfo); } -static inline void ParseMode(const Mle::DeviceMode &aMode, otLinkModeConfig &aLinkModeConfig) +template <> +void Client::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - aLinkModeConfig.mRxOnWhenIdle = aMode.IsRxOnWhenIdle(); - aLinkModeConfig.mDeviceType = aMode.IsFullThreadDevice(); - aLinkModeConfig.mNetworkData = (aMode.GetNetworkDataType() == NetworkData::kFullSet); + VerifyOrExit(aMessage.IsConfirmablePostRequest()); + + LogInfo("Received %s from %s", ot::UriToString(), + aMessageInfo.GetPeerAddr().ToString().AsCString()); + + mGetCallback.InvokeIfSet(kErrorNone, &aMessage, &aMessageInfo); + + IgnoreError(Get().SendEmptyAck(aMessage, aMessageInfo)); + +exit: + return; } -static inline void ParseConnectivity(const ConnectivityTlv & aConnectivityTlv, - otNetworkDiagConnectivity &aNetworkDiagConnectivity) +Error Client::SendDiagnosticReset(const Ip6::Address &aDestination, const uint8_t aTlvTypes[], uint8_t aCount) { - aNetworkDiagConnectivity.mParentPriority = aConnectivityTlv.GetParentPriority(); - aNetworkDiagConnectivity.mLinkQuality3 = aConnectivityTlv.GetLinkQuality3(); - aNetworkDiagConnectivity.mLinkQuality2 = aConnectivityTlv.GetLinkQuality2(); - aNetworkDiagConnectivity.mLinkQuality1 = aConnectivityTlv.GetLinkQuality1(); - aNetworkDiagConnectivity.mLeaderCost = aConnectivityTlv.GetLeaderCost(); - aNetworkDiagConnectivity.mIdSequence = aConnectivityTlv.GetIdSequence(); - aNetworkDiagConnectivity.mActiveRouters = aConnectivityTlv.GetActiveRouters(); - aNetworkDiagConnectivity.mSedBufferSize = aConnectivityTlv.GetSedBufferSize(); - aNetworkDiagConnectivity.mSedDatagramCount = aConnectivityTlv.GetSedDatagramCount(); + return SendCommand(kUriDiagnosticReset, aDestination, aTlvTypes, aCount); } static void ParseRoute(const RouteTlv &aRouteTlv, otNetworkDiagRoute &aNetworkDiagRoute) @@ -664,15 +624,6 @@ static void ParseRoute(const RouteTlv &aRouteTlv, otNetworkDiagRoute &aNetworkDi aNetworkDiagRoute.mIdSequence = aRouteTlv.GetRouterIdSequence(); } -static inline void ParseLeaderData(const LeaderDataTlv &aLeaderDataTlv, otLeaderData &aLeaderData) -{ - aLeaderData.mPartitionId = aLeaderDataTlv.GetPartitionId(); - aLeaderData.mWeighting = aLeaderDataTlv.GetWeighting(); - aLeaderData.mDataVersion = aLeaderDataTlv.GetDataVersion(); - aLeaderData.mStableDataVersion = aLeaderDataTlv.GetStableDataVersion(); - aLeaderData.mLeaderRouterId = aLeaderDataTlv.GetLeaderRouterId(); -} - static inline void ParseMacCounters(const MacCountersTlv &aMacCountersTlv, otNetworkDiagMacCounters &aMacCounters) { aMacCounters.mIfInUnknownProtos = aMacCountersTlv.GetIfInUnknownProtos(); @@ -686,184 +637,266 @@ static inline void ParseMacCounters(const MacCountersTlv &aMacCountersTlv, otNet aMacCounters.mIfOutDiscards = aMacCountersTlv.GetIfOutDiscards(); } -static inline void ParseChildEntry(const ChildTableEntry &aChildTableTlvEntry, otNetworkDiagChildEntry &aChildEntry) -{ - aChildEntry.mTimeout = aChildTableTlvEntry.GetTimeout(); - aChildEntry.mChildId = aChildTableTlvEntry.GetChildId(); - ParseMode(aChildTableTlvEntry.GetMode(), aChildEntry.mMode); -} - -Error NetworkDiagnostic::GetNextDiagTlv(const Coap::Message &aMessage, - Iterator & aIterator, - otNetworkDiagTlv & aNetworkDiagTlv) +Error Client::GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, TlvInfo &aTlvInfo) { - Error error = kErrorNone; - uint16_t offset = aMessage.GetOffset() + aIterator; - NetworkDiagnosticTlv tlv; + Error error; + uint16_t offset = (aIterator == 0) ? aMessage.GetOffset() : aIterator; - while (true) + while (offset < aMessage.GetLength()) { - uint16_t tlvTotalLength; + bool skipTlv = false; + uint16_t valueOffset; + uint16_t tlvLength; + union + { + Tlv tlv; + ExtendedTlv extTlv; + }; + + SuccessOrExit(error = aMessage.Read(offset, tlv)); + + if (tlv.IsExtended()) + { + SuccessOrExit(error = aMessage.Read(offset, extTlv)); + valueOffset = offset + sizeof(ExtendedTlv); + tlvLength = extTlv.GetLength(); + } + else + { + valueOffset = offset + sizeof(Tlv); + tlvLength = tlv.GetLength(); + } - VerifyOrExit(aMessage.Read(offset, tlv) == kErrorNone, error = kErrorNotFound); + VerifyOrExit(offset + tlv.GetSize() <= aMessage.GetLength(), error = kErrorParse); switch (tlv.GetType()) { - case NetworkDiagnosticTlv::kExtMacAddress: - SuccessOrExit( - error = Tlv::Read(aMessage, offset, AsCoreType(&aNetworkDiagTlv.mData.mExtAddress))); + case Tlv::kExtMacAddress: + SuccessOrExit(error = + Tlv::Read(aMessage, offset, AsCoreType(&aTlvInfo.mData.mExtAddress))); break; - case NetworkDiagnosticTlv::kAddress16: - SuccessOrExit(error = Tlv::Read(aMessage, offset, aNetworkDiagTlv.mData.mAddr16)); + case Tlv::kAddress16: + SuccessOrExit(error = Tlv::Read(aMessage, offset, aTlvInfo.mData.mAddr16)); break; - case NetworkDiagnosticTlv::kMode: + case Tlv::kMode: { uint8_t mode; SuccessOrExit(error = Tlv::Read(aMessage, offset, mode)); - ParseMode(Mle::DeviceMode(mode), aNetworkDiagTlv.mData.mMode); + Mle::DeviceMode(mode).Get(aTlvInfo.mData.mMode); break; } - case NetworkDiagnosticTlv::kTimeout: - SuccessOrExit(error = Tlv::Read(aMessage, offset, aNetworkDiagTlv.mData.mTimeout)); + case Tlv::kTimeout: + SuccessOrExit(error = Tlv::Read(aMessage, offset, aTlvInfo.mData.mTimeout)); break; - case NetworkDiagnosticTlv::kConnectivity: + case Tlv::kConnectivity: { - ConnectivityTlv connectivity; + ConnectivityTlv connectivityTlv; - SuccessOrExit(error = aMessage.Read(offset, connectivity)); - VerifyOrExit(connectivity.IsValid(), error = kErrorParse); - - ParseConnectivity(connectivity, aNetworkDiagTlv.mData.mConnectivity); + VerifyOrExit(!tlv.IsExtended(), error = kErrorParse); + SuccessOrExit(error = aMessage.Read(offset, connectivityTlv)); + VerifyOrExit(connectivityTlv.IsValid(), error = kErrorParse); + connectivityTlv.GetConnectivity(aTlvInfo.mData.mConnectivity); break; } - case NetworkDiagnosticTlv::kRoute: + case Tlv::kRoute: { - RouteTlv route; - - tlvTotalLength = sizeof(tlv) + tlv.GetLength(); - VerifyOrExit(tlvTotalLength <= sizeof(route), error = kErrorParse); - SuccessOrExit(error = aMessage.Read(offset, &route, tlvTotalLength)); - VerifyOrExit(route.IsValid(), error = kErrorParse); + RouteTlv routeTlv; + uint16_t bytesToRead = static_cast(Min(tlv.GetSize(), static_cast(sizeof(routeTlv)))); - ParseRoute(route, aNetworkDiagTlv.mData.mRoute); + VerifyOrExit(!tlv.IsExtended(), error = kErrorParse); + SuccessOrExit(error = aMessage.Read(offset, &routeTlv, bytesToRead)); + VerifyOrExit(routeTlv.IsValid(), error = kErrorParse); + ParseRoute(routeTlv, aTlvInfo.mData.mRoute); break; } - case NetworkDiagnosticTlv::kLeaderData: + case Tlv::kLeaderData: { - LeaderDataTlv leaderData; - - SuccessOrExit(error = aMessage.Read(offset, leaderData)); - VerifyOrExit(leaderData.IsValid(), error = kErrorParse); + LeaderDataTlv leaderDataTlv; - ParseLeaderData(leaderData, aNetworkDiagTlv.mData.mLeaderData); + VerifyOrExit(!tlv.IsExtended(), error = kErrorParse); + SuccessOrExit(error = aMessage.Read(offset, leaderDataTlv)); + VerifyOrExit(leaderDataTlv.IsValid(), error = kErrorParse); + leaderDataTlv.Get(AsCoreType(&aTlvInfo.mData.mLeaderData)); break; } - case NetworkDiagnosticTlv::kNetworkData: - { - NetworkDataTlv networkData; - - tlvTotalLength = sizeof(tlv) + tlv.GetLength(); - VerifyOrExit(tlvTotalLength <= sizeof(networkData), error = kErrorParse); - SuccessOrExit(error = aMessage.Read(offset, &networkData, tlvTotalLength)); - VerifyOrExit(networkData.IsValid(), error = kErrorParse); - VerifyOrExit(sizeof(aNetworkDiagTlv.mData.mNetworkData.m8) >= networkData.GetLength(), error = kErrorParse); + case Tlv::kNetworkData: + static_assert(sizeof(aTlvInfo.mData.mNetworkData.m8) >= NetworkData::NetworkData::kMaxSize, + "NetworkData array in `otNetworkDiagTlv` is too small"); - memcpy(aNetworkDiagTlv.mData.mNetworkData.m8, networkData.GetNetworkData(), networkData.GetLength()); - aNetworkDiagTlv.mData.mNetworkData.mCount = networkData.GetLength(); + VerifyOrExit(tlvLength <= NetworkData::NetworkData::kMaxSize, error = kErrorParse); + aTlvInfo.mData.mNetworkData.mCount = static_cast(tlvLength); + aMessage.ReadBytes(valueOffset, aTlvInfo.mData.mNetworkData.m8, tlvLength); break; - } - case NetworkDiagnosticTlv::kIp6AddressList: + case Tlv::kIp6AddressList: { - Ip6AddressListTlv &ip6AddrList = As(tlv); - - VerifyOrExit(ip6AddrList.IsValid(), error = kErrorParse); - VerifyOrExit(sizeof(aNetworkDiagTlv.mData.mIp6AddrList.mList) >= ip6AddrList.GetLength(), - error = kErrorParse); - SuccessOrExit(error = aMessage.Read(offset + sizeof(ip6AddrList), aNetworkDiagTlv.mData.mIp6AddrList.mList, - ip6AddrList.GetLength())); - aNetworkDiagTlv.mData.mIp6AddrList.mCount = ip6AddrList.GetLength() / OT_IP6_ADDRESS_SIZE; + uint16_t addrListLength = GetArrayLength(aTlvInfo.mData.mIp6AddrList.mList); + Ip6::Address *addrEntry = AsCoreTypePtr(&aTlvInfo.mData.mIp6AddrList.mList[0]); + uint8_t &addrCount = aTlvInfo.mData.mIp6AddrList.mCount; + + VerifyOrExit((tlvLength % Ip6::Address::kSize) == 0, error = kErrorParse); + + // `TlvInfo` has a fixed array for IPv6 addresses. If there + // are more addresses in the message, we read and return as + // many as can fit in array and ignore the rest. + + addrCount = 0; + + while ((tlvLength > 0) && (addrCount < addrListLength)) + { + SuccessOrExit(error = aMessage.Read(valueOffset, *addrEntry)); + addrCount++; + addrEntry++; + valueOffset += Ip6::Address::kSize; + tlvLength -= Ip6::Address::kSize; + } + break; } - case NetworkDiagnosticTlv::kMacCounters: + case Tlv::kMacCounters: { - MacCountersTlv macCounters; + MacCountersTlv macCountersTlv; - SuccessOrExit(error = aMessage.Read(offset, macCounters)); - VerifyOrExit(macCounters.IsValid(), error = kErrorParse); - - ParseMacCounters(macCounters, aNetworkDiagTlv.mData.mMacCounters); + SuccessOrExit(error = aMessage.Read(offset, macCountersTlv)); + VerifyOrExit(macCountersTlv.IsValid(), error = kErrorParse); + ParseMacCounters(macCountersTlv, aTlvInfo.mData.mMacCounters); break; } - case NetworkDiagnosticTlv::kBatteryLevel: - SuccessOrExit(error = Tlv::Read(aMessage, offset, aNetworkDiagTlv.mData.mBatteryLevel)); + case Tlv::kBatteryLevel: + SuccessOrExit(error = Tlv::Read(aMessage, offset, aTlvInfo.mData.mBatteryLevel)); break; - case NetworkDiagnosticTlv::kSupplyVoltage: - SuccessOrExit(error = Tlv::Read(aMessage, offset, aNetworkDiagTlv.mData.mSupplyVoltage)); + case Tlv::kSupplyVoltage: + SuccessOrExit(error = Tlv::Read(aMessage, offset, aTlvInfo.mData.mSupplyVoltage)); break; - case NetworkDiagnosticTlv::kChildTable: + case Tlv::kChildTable: { - ChildTableTlv &childTable = As(tlv); + uint16_t childInfoLength = GetArrayLength(aTlvInfo.mData.mChildTable.mTable); + ChildInfo *childInfo = &aTlvInfo.mData.mChildTable.mTable[0]; + uint8_t &childCount = aTlvInfo.mData.mChildTable.mCount; + + VerifyOrExit((tlvLength % sizeof(ChildTableEntry)) == 0, error = kErrorParse); - VerifyOrExit(childTable.IsValid(), error = kErrorParse); - VerifyOrExit(childTable.GetNumEntries() <= GetArrayLength(aNetworkDiagTlv.mData.mChildTable.mTable), - error = kErrorParse); + // `TlvInfo` has a fixed array Child Table entries. If there + // are more entries in the message, we read and return as + // many as can fit in array and ignore the rest. - for (uint8_t i = 0; i < childTable.GetNumEntries(); ++i) + childCount = 0; + + while ((tlvLength > 0) && (childCount < childInfoLength)) { - ChildTableEntry childEntry; - VerifyOrExit(childTable.ReadEntry(childEntry, aMessage, offset, i) == kErrorNone, error = kErrorParse); - ParseChildEntry(childEntry, aNetworkDiagTlv.mData.mChildTable.mTable[i]); + ChildTableEntry entry; + + SuccessOrExit(error = aMessage.Read(valueOffset, entry)); + + childInfo->mTimeout = entry.GetTimeout(); + childInfo->mLinkQuality = entry.GetLinkQuality(); + childInfo->mChildId = entry.GetChildId(); + entry.GetMode().Get(childInfo->mMode); + + childCount++; + childInfo++; + tlvLength -= sizeof(ChildTableEntry); + valueOffset += sizeof(ChildTableEntry); } - aNetworkDiagTlv.mData.mChildTable.mCount = childTable.GetNumEntries(); + break; } - case NetworkDiagnosticTlv::kChannelPages: - { - VerifyOrExit(sizeof(aNetworkDiagTlv.mData.mChannelPages.m8) >= tlv.GetLength(), error = kErrorParse); - SuccessOrExit( - error = aMessage.Read(offset + sizeof(tlv), aNetworkDiagTlv.mData.mChannelPages.m8, tlv.GetLength())); - aNetworkDiagTlv.mData.mChannelPages.mCount = tlv.GetLength(); + case Tlv::kChannelPages: + aTlvInfo.mData.mChannelPages.mCount = + static_cast(Min(tlvLength, GetArrayLength(aTlvInfo.mData.mChannelPages.m8))); + aMessage.ReadBytes(valueOffset, aTlvInfo.mData.mChannelPages.m8, aTlvInfo.mData.mChannelPages.mCount); + break; + + case Tlv::kMaxChildTimeout: + SuccessOrExit(error = Tlv::Read(aMessage, offset, aTlvInfo.mData.mMaxChildTimeout)); + break; + + case Tlv::kVersion: + SuccessOrExit(error = Tlv::Read(aMessage, offset, aTlvInfo.mData.mVersion)); + break; + + case Tlv::kVendorName: + SuccessOrExit(error = Tlv::Read(aMessage, offset, aTlvInfo.mData.mVendorName)); + break; + + case Tlv::kVendorModel: + SuccessOrExit(error = Tlv::Read(aMessage, offset, aTlvInfo.mData.mVendorModel)); break; - } - case NetworkDiagnosticTlv::kMaxChildTimeout: + case Tlv::kVendorSwVersion: + SuccessOrExit(error = Tlv::Read(aMessage, offset, aTlvInfo.mData.mVendorSwVersion)); + break; + + case Tlv::kThreadStackVersion: SuccessOrExit(error = - Tlv::Read(aMessage, offset, aNetworkDiagTlv.mData.mMaxChildTimeout)); + Tlv::Read(aMessage, offset, aTlvInfo.mData.mThreadStackVersion)); break; default: - // Ignore unrecognized Network Diagnostic TLV silently and - // continue to top of the `while(true)` loop. - offset += tlv.GetSize(); - continue; + // Skip unrecognized TLVs. + skipTlv = true; + break; } - // Exit if a TLV is recognized and parsed successfully. - aNetworkDiagTlv.mType = tlv.GetType(); - aIterator = static_cast(offset - aMessage.GetOffset() + tlv.GetSize()); - ExitNow(); + offset += tlv.GetSize(); + + if (!skipTlv) + { + // Exit if a TLV is recognized and parsed successfully. + aTlvInfo.mType = tlv.GetType(); + aIterator = offset; + error = kErrorNone; + ExitNow(); + } } + error = kErrorNotFound; + exit: return error; } +#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) + +const char *Client::UriToString(Uri aUri) +{ + const char *str = ""; + + switch (aUri) + { + case kUriDiagnosticGetQuery: + str = ot::UriToString(); + break; + case kUriDiagnosticGetRequest: + str = ot::UriToString(); + break; + case kUriDiagnosticReset: + str = ot::UriToString(); + break; + default: + break; + } + + return str; +} + +#endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) + +#endif // OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE + } // namespace NetworkDiagnostic } // namespace ot - -#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE diff --git a/src/core/thread/network_diagnostic.hpp b/src/core/thread/network_diagnostic.hpp index ade893fd9b4..4026aa32c18 100644 --- a/src/core/thread/network_diagnostic.hpp +++ b/src/core/thread/network_diagnostic.hpp @@ -36,15 +36,15 @@ #include "openthread-core-config.h" -#if OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE - #include -#include "coap/coap.hpp" +#include "common/callback.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" #include "net/udp6.hpp" #include "thread/network_diagnostic_tlvs.hpp" +#include "thread/tmf.hpp" +#include "thread/uri_paths.hpp" namespace ot { @@ -59,49 +59,166 @@ namespace NetworkDiagnostic { * @{ */ +class Client; + /** - * This class implements the Network Diagnostic processing. + * This class implements the Network Diagnostic server responding to requests. * */ -class NetworkDiagnostic : public InstanceLocator, private NonCopyable +class Server : public InstanceLocator, private NonCopyable { + friend class Tmf::Agent; + friend class Client; + public: /** - * This type represents an iterator used to iterate through Network Diagnostic TLVs from `GetNextDiagTlv()`. + * This constructor initializes the Server. + * + * @param[in] aInstance The OpenThread instance. + * + */ + explicit Server(Instance &aInstance); + +#if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE + /** + * This method returns the vendor name string. + * + * @returns The vendor name string. + * + */ + const char *GetVendorName(void) const { return mVendorName; } + + /** + * This method sets the vendor name string. + * + * @param[in] aVendorName The vendor name string. + * + * @retval kErrorNone Successfully set the vendor name. + * @retval kErrorInvalidArgs @p aVendorName is not valid (too long or not UTF8). + * + */ + Error SetVendorName(const char *aVendorName); + + /** + * This method returns the vendor model string. + * + * @returns The vendor model string. + * + */ + const char *GetVendorModel(void) const { return mVendorModel; } + + /** + * This method sets the vendor model string. + * + * @param[in] aVendorModel The vendor model string. + * + * @retval kErrorNone Successfully set the vendor model. + * @retval kErrorInvalidArgs @p aVendorModel is not valid (too long or not UTF8). + * + */ + Error SetVendorModel(const char *aVendorModel); + + /** + * This method returns the vendor software version string. + * + * @returns The vendor software version string. + * + */ + const char *GetVendorSwVersion(void) const { return mVendorSwVersion; } + + /** + * This method sets the vendor sw version string + * + * @param[in] aVendorSwVersion The vendor sw version string. + * + * @retval kErrorNone Successfully set the vendor sw version. + * @retval kErrorInvalidArgs @p aVendorSwVersion is not valid (too long or not UTF8). * */ - typedef otNetworkDiagIterator Iterator; + Error SetVendorSwVersion(const char *aVendorSwVersion); + +#else + const char *GetVendorName(void) const { return kVendorName; } + const char *GetVendorModel(void) const { return kVendorModel; } + const char *GetVendorSwVersion(void) const { return kVendorSwVersion; } +#endif // OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE + +private: + static constexpr uint16_t kMaxChildEntries = 398; + + static const char kVendorName[]; + static const char kVendorModel[]; + static const char kVendorSwVersion[]; + + Error AppendDiagTlv(uint8_t aTlvType, Message &aMessage); + Error AppendIp6AddressList(Message &aMessage); + Error AppendMacCounters(Message &aMessage); + Error AppendChildTable(Message &aMessage); + Error AppendRequestedTlvs(const Message &aRequest, Message &aResponse); + void PrepareMessageInfoForDest(const Ip6::Address &aDestination, Tmf::MessageInfo &aMessageInfo) const; + + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + +#if OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE + Error SetVendorString(char *aDestString, uint16_t kMaxSize, const char *aSrcString); + + VendorNameTlv::StringType mVendorName; + VendorModelTlv::StringType mVendorModel; + VendorSwVersionTlv::StringType mVendorSwVersion; +#endif +}; + +DeclareTmfHandler(Server, kUriDiagnosticGetRequest); +DeclareTmfHandler(Server, kUriDiagnosticGetQuery); +DeclareTmfHandler(Server, kUriDiagnosticGetAnswer); + +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE + +/** + * This class implements the Network Diagnostic client sending requests and queries. + * + */ +class Client : public InstanceLocator, private NonCopyable +{ + friend class Tmf::Agent; + +public: + typedef otNetworkDiagIterator Iterator; ///< Iterator to go through TLVs in `GetNextDiagTlv()`. + typedef otNetworkDiagTlv TlvInfo; ///< Parse info from a Network Diagnostic TLV. + typedef otNetworkDiagChildEntry ChildInfo; ///< Parsed info for child table entry. + typedef otReceiveDiagnosticGetCallback GetCallback; ///< Diagnostic Get callback function pointer type. static constexpr Iterator kIteratorInit = OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT; ///< Initializer for Iterator. /** - * This constructor initializes the object. + * This constructor initializes the Client. + * + * @param[in] aInstance The OpenThread instance. * */ - explicit NetworkDiagnostic(Instance &aInstance); + explicit Client(Instance &aInstance); /** * This method sends Diagnostic Get request. If the @p aDestination is of multicast type, the DIAG_GET.qry * message is sent or the DIAG_GET.req otherwise. * - * @param[in] aDestination A reference to the destination address. + * @param[in] aDestination The destination address. * @param[in] aTlvTypes An array of Network Diagnostic TLV types. - * @param[in] aCount Number of types in aTlvTypes. - * @param[in] aCallback A pointer to a function that is called when Network Diagnostic Get response - * is received or NULL to disable the callback. - * @param[in] aCallbackContext A pointer to application-specific context. + * @param[in] aCount Number of types in @p aTlvTypes. + * @param[in] aCallback Callback when Network Diagnostic Get response is received (can be NULL). + * @param[in] Context Application-specific context used with @p aCallback. * */ - Error SendDiagnosticGet(const Ip6::Address & aDestination, - const uint8_t aTlvTypes[], - uint8_t aCount, - otReceiveDiagnosticGetCallback aCallback, - void * aCallbackContext); + Error SendDiagnosticGet(const Ip6::Address &aDestination, + const uint8_t aTlvTypes[], + uint8_t aCount, + GetCallback aCallback, + void *Context); /** * This method sends Diagnostic Reset request. * - * @param[in] aDestination A reference to the destination address. + * @param[in] aDestination The destination address. * @param[in] aTlvTypes An array of Network Diagnostic TLV types. * @param[in] aCount Number of types in aTlvTypes * @@ -111,50 +228,43 @@ class NetworkDiagnostic : public InstanceLocator, private NonCopyable /** * This static method gets the next Network Diagnostic TLV in a given message. * - * @param[in] aMessage A message. - * @param[in,out] aIterator The Network Diagnostic iterator. To get the first TLV set it to - * `kIteratorInit`. - * @param[out] aNetworkDiagTlv A reference to a Network Diagnostic TLV to output the next TLV. + * @param[in] aMessage Message to read TLVs from. + * @param[in,out] aIterator The Network Diagnostic iterator. To get the first TLV set it to `kIteratorInit`. + * @param[out] aTlvInfo A reference to a `TlvInfo` to output the next TLV data. * * @retval kErrorNone Successfully found the next Network Diagnostic TLV. * @retval kErrorNotFound No subsequent Network Diagnostic TLV exists in the message. * @retval kErrorParse Parsing the next Network Diagnostic failed. * */ - static Error GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, otNetworkDiagTlv &aNetworkDiagTlv); + static Error GetNextDiagTlv(const Coap::Message &aMessage, Iterator &aIterator, TlvInfo &aTlvInfo); private: - Error AppendIp6AddressList(Message &aMessage); - Error AppendChildTable(Message &aMessage); - void FillMacCountersTlv(MacCountersTlv &aMacCountersTlv); - Error FillRequestedTlvs(const Message &aRequest, Message &aResponse, NetworkDiagnosticTlv &aNetworkDiagnosticTlv); + Error SendCommand(Uri aUri, + const Ip6::Address &aDestination, + const uint8_t aTlvTypes[], + uint8_t aCount, + Coap::ResponseHandler aHandler = nullptr, + void *aContext = nullptr); - static void HandleDiagnosticGetRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleDiagnosticGetRequest(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + static void HandleGetResponse(void *aContext, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, + Error aResult); + void HandleGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); - static void HandleDiagnosticGetQuery(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleDiagnosticGetQuery(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - static void HandleDiagnosticGetResponse(void * aContext, - otMessage * aMessage, - const otMessageInfo *aMessageInfo, - Error aResult); - void HandleDiagnosticGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); +#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) + static const char *UriToString(Uri aUri); +#endif - static void HandleDiagnosticGetAnswer(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleDiagnosticGetAnswer(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); - - static void HandleDiagnosticReset(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleDiagnosticReset(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + Callback mGetCallback; +}; - Coap::Resource mDiagnosticGetRequest; - Coap::Resource mDiagnosticGetQuery; - Coap::Resource mDiagnosticGetAnswer; - Coap::Resource mDiagnosticReset; +DeclareTmfHandler(Client, kUriDiagnosticReset); - otReceiveDiagnosticGetCallback mReceiveDiagnosticGetCallback; - void * mReceiveDiagnosticGetCallbackContext; -}; +#endif // OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE /** * @} @@ -163,6 +273,4 @@ class NetworkDiagnostic : public InstanceLocator, private NonCopyable } // namespace ot -#endif // OPENTHREAD_FTD || OPENTHREAD_CONFIG_TMF_NETWORK_DIAG_MTD_ENABLE - #endif // NETWORK_DIAGNOSTIC_HPP_ diff --git a/src/core/thread/network_diagnostic_tlvs.hpp b/src/core/thread/network_diagnostic_tlvs.hpp index 83526342eff..c46298cff9d 100644 --- a/src/core/thread/network_diagnostic_tlvs.hpp +++ b/src/core/thread/network_diagnostic_tlvs.hpp @@ -36,64 +36,86 @@ #include "openthread-core-config.h" +#include #include +#include "common/clearable.hpp" #include "common/encoding.hpp" #include "common/message.hpp" #include "common/tlvs.hpp" #include "net/ip6_address.hpp" #include "radio/radio.hpp" +#include "thread/link_quality.hpp" +#include "thread/mle_tlvs.hpp" #include "thread/mle_types.hpp" namespace ot { - namespace NetworkDiagnostic { using ot::Encoding::BigEndian::HostSwap16; using ot::Encoding::BigEndian::HostSwap32; /** - * @addtogroup core-mle-tlvs - * - * @brief - * This module includes definitions for generating and processing MLE TLVs. - * - * @{ - * - */ - -/** - * This class implements MLE TLV generation and parsing. + * This class implements Network Diagnostic TLV generation and parsing. * */ OT_TOOL_PACKED_BEGIN -class NetworkDiagnosticTlv : public ot::Tlv +class Tlv : public ot::Tlv { public: /** - * MLE TLV Types. + * Network Diagnostic TLV Types. * */ enum Type : uint8_t { - kExtMacAddress = OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS, - kAddress16 = OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS, - kMode = OT_NETWORK_DIAGNOSTIC_TLV_MODE, - kTimeout = OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT, - kConnectivity = OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY, - kRoute = OT_NETWORK_DIAGNOSTIC_TLV_ROUTE, - kLeaderData = OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA, - kNetworkData = OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA, - kIp6AddressList = OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST, - kMacCounters = OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS, - kBatteryLevel = OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL, - kSupplyVoltage = OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE, - kChildTable = OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE, - kChannelPages = OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES, - kTypeList = OT_NETWORK_DIAGNOSTIC_TLV_TYPE_LIST, - kMaxChildTimeout = OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT, + kExtMacAddress = OT_NETWORK_DIAGNOSTIC_TLV_EXT_ADDRESS, + kAddress16 = OT_NETWORK_DIAGNOSTIC_TLV_SHORT_ADDRESS, + kMode = OT_NETWORK_DIAGNOSTIC_TLV_MODE, + kTimeout = OT_NETWORK_DIAGNOSTIC_TLV_TIMEOUT, + kConnectivity = OT_NETWORK_DIAGNOSTIC_TLV_CONNECTIVITY, + kRoute = OT_NETWORK_DIAGNOSTIC_TLV_ROUTE, + kLeaderData = OT_NETWORK_DIAGNOSTIC_TLV_LEADER_DATA, + kNetworkData = OT_NETWORK_DIAGNOSTIC_TLV_NETWORK_DATA, + kIp6AddressList = OT_NETWORK_DIAGNOSTIC_TLV_IP6_ADDR_LIST, + kMacCounters = OT_NETWORK_DIAGNOSTIC_TLV_MAC_COUNTERS, + kBatteryLevel = OT_NETWORK_DIAGNOSTIC_TLV_BATTERY_LEVEL, + kSupplyVoltage = OT_NETWORK_DIAGNOSTIC_TLV_SUPPLY_VOLTAGE, + kChildTable = OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE, + kChannelPages = OT_NETWORK_DIAGNOSTIC_TLV_CHANNEL_PAGES, + kTypeList = OT_NETWORK_DIAGNOSTIC_TLV_TYPE_LIST, + kMaxChildTimeout = OT_NETWORK_DIAGNOSTIC_TLV_MAX_CHILD_TIMEOUT, + kVersion = OT_NETWORK_DIAGNOSTIC_TLV_VERSION, + kVendorName = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_NAME, + kVendorModel = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_MODEL, + kVendorSwVersion = OT_NETWORK_DIAGNOSTIC_TLV_VENDOR_SW_VERSION, + kThreadStackVersion = OT_NETWORK_DIAGNOSTIC_TLV_THREAD_STACK_VERSION, }; + /** + * Maximum length of Vendor Name TLV. + * + */ + static constexpr uint8_t kMaxVendorNameLength = OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_NAME_TLV_LENGTH; + + /** + * Maximum length of Vendor Model TLV. + * + */ + static constexpr uint8_t kMaxVendorModelLength = OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_MODEL_TLV_LENGTH; + + /** + * Maximum length of Vendor SW Version TLV. + * + */ + static constexpr uint8_t kMaxVendorSwVersionLength = OT_NETWORK_DIAGNOSTIC_MAX_VENDOR_SW_VERSION_TLV_LENGTH; + + /** + * Maximum length of Vendor SW Version TLV. + * + */ + static constexpr uint8_t kMaxThreadStackVersionLength = OT_NETWORK_DIAGNOSTIC_MAX_THREAD_STACK_VERSION_TLV_LENGTH; + /** * This method returns the Type value. * @@ -116,632 +138,175 @@ class NetworkDiagnosticTlv : public ot::Tlv * This class defines Extended MAC Address TLV constants and types. * */ -typedef SimpleTlvInfo ExtMacAddressTlv; +typedef SimpleTlvInfo ExtMacAddressTlv; /** * This class defines Address16 TLV constants and types. * */ -typedef UintTlvInfo Address16Tlv; +typedef UintTlvInfo Address16Tlv; /** * This class defines Mode TLV constants and types. * */ -typedef UintTlvInfo ModeTlv; +typedef UintTlvInfo ModeTlv; /** * This class defines Timeout TLV constants and types. * */ -typedef UintTlvInfo TimeoutTlv; +typedef UintTlvInfo TimeoutTlv; /** - * This class defines Battery Level TLV constants and types. + * This class defines Network Data TLV constants and types. * */ -typedef UintTlvInfo BatteryLevelTlv; +typedef TlvInfo NetworkDataTlv; /** - * This class defines Supply Voltage TLV constants and types. + * This class defines IPv6 Address List TLV constants and types. * */ -typedef UintTlvInfo SupplyVoltageTlv; +typedef TlvInfo Ip6AddressListTlv; /** - * This class defines Max Child Timeout TLV constants and types. + * This class defines Battery Level TLV constants and types. * */ -typedef UintTlvInfo MaxChildTimeoutTlv; +typedef UintTlvInfo BatteryLevelTlv; /** - * This class implements Connectivity TLV generation and parsing. + * This class defines Supply Voltage TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class ConnectivityTlv : public NetworkDiagnosticTlv, public TlvInfo -{ -public: - /** - * This method initializes the TLV. - * - */ - void Init(void) - { - SetType(kConnectivity); - SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); - } - - /** - * This method indicates whether or not the TLV appears to be well-formed. - * - * @retval TRUE If the TLV appears to be well-formed. - * @retval FALSE If the TLV does not appear to be well-formed. - * - */ - bool IsValid(void) const - { - return IsSedBufferingIncluded() || (GetLength() == sizeof(*this) - sizeof(NetworkDiagnosticTlv) - - sizeof(mSedBufferSize) - sizeof(mSedDatagramCount)); - } - - /** - * This method indicates whether or not the sed buffer size and datagram count are included. - * - * @retval TRUE If the sed buffer size and datagram count are included. - * @retval FALSE If the sed buffer size and datagram count are not included. - * - */ - bool IsSedBufferingIncluded(void) const { return GetLength() >= sizeof(*this) - sizeof(Tlv); } - - /** - * This method returns the Parent Priority value. - * - * @returns The Parent Priority value. - * - */ - int8_t GetParentPriority(void) const { return (mParentPriority & kParentPriorityMask) >> kParentPriorityOffset; } - - /** - * This method sets the Parent Priority value. - * - * @param[in] aParentPriority The Parent Priority value. - * - */ - void SetParentPriority(int8_t aParentPriority) - { - mParentPriority = (aParentPriority << kParentPriorityOffset) & kParentPriorityMask; - } - - /** - * This method returns the Link Quality 3 value. - * - * @returns The Link Quality 3 value. - * - */ - uint8_t GetLinkQuality3(void) const { return mLinkQuality3; } - - /** - * This method sets the Link Quality 3 value. - * - * @param[in] aLinkQuality The Link Quality 3 value. - * - */ - void SetLinkQuality3(uint8_t aLinkQuality) { mLinkQuality3 = aLinkQuality; } - - /** - * This method returns the Link Quality 2 value. - * - * @returns The Link Quality 2 value. - * - */ - uint8_t GetLinkQuality2(void) const { return mLinkQuality2; } - - /** - * This method sets the Link Quality 2 value. - * - * @param[in] aLinkQuality The Link Quality 2 value. - * - */ - void SetLinkQuality2(uint8_t aLinkQuality) { mLinkQuality2 = aLinkQuality; } - - /** - * This method sets the Link Quality 1 value. - * - * @returns The Link Quality 1 value. - * - */ - uint8_t GetLinkQuality1(void) const { return mLinkQuality1; } - - /** - * This method sets the Link Quality 1 value. - * - * @param[in] aLinkQuality The Link Quality 1 value. - * - */ - void SetLinkQuality1(uint8_t aLinkQuality) { mLinkQuality1 = aLinkQuality; } - - /** - * This method sets the Active Routers value. - * - * @returns The Active Routers value. - * - */ - uint8_t GetActiveRouters(void) const { return mActiveRouters; } - - /** - * This method sets the Active Routers value. - * - * @param[in] aActiveRouters The Active Routers value. - * - */ - void SetActiveRouters(uint8_t aActiveRouters) { mActiveRouters = aActiveRouters; } - - /** - * This method returns the Leader Cost value. - * - * @returns The Leader Cost value. - * - */ - uint8_t GetLeaderCost(void) const { return mLeaderCost; } - - /** - * This method sets the Leader Cost value. - * - * @param[in] aCost The Leader Cost value. - * - */ - void SetLeaderCost(uint8_t aCost) { mLeaderCost = aCost; } - - /** - * This method returns the ID Sequence value. - * - * @returns The ID Sequence value. - * - */ - uint8_t GetIdSequence(void) const { return mIdSequence; } - - /** - * This method sets the ID Sequence value. - * - * @param[in] aSequence The ID Sequence value. - * - */ - void SetIdSequence(uint8_t aSequence) { mIdSequence = aSequence; } - - /** - * This method returns the SED Buffer Size value. - * - * @returns The SED Buffer Size value. - * - */ - uint16_t GetSedBufferSize(void) const - { - uint16_t buffersize = OPENTHREAD_CONFIG_DEFAULT_SED_BUFFER_SIZE; - - if (IsSedBufferingIncluded()) - { - buffersize = HostSwap16(mSedBufferSize); - } - return buffersize; - } - - /** - * This method sets the SED Buffer Size value. - * - * @param[in] aSedBufferSize The SED Buffer Size value. - * - */ - void SetSedBufferSize(uint16_t aSedBufferSize) { mSedBufferSize = HostSwap16(aSedBufferSize); } - - /** - * This method returns the SED Datagram Count value. - * - * @returns The SED Datagram Count value. - * - */ - uint8_t GetSedDatagramCount(void) const - { - uint8_t count = OPENTHREAD_CONFIG_DEFAULT_SED_DATAGRAM_COUNT; - - if (IsSedBufferingIncluded()) - { - count = mSedDatagramCount; - } - return count; - } - - /** - * This method sets the SED Datagram Count value. - * - * @param[in] aSedDatagramCount The SED Datagram Count value. - * - */ - void SetSedDatagramCount(uint8_t aSedDatagramCount) { mSedDatagramCount = aSedDatagramCount; } - -private: - static constexpr uint8_t kParentPriorityOffset = 6; - static constexpr uint8_t kParentPriorityMask = 3 << kParentPriorityOffset; - - uint8_t mParentPriority; - uint8_t mLinkQuality3; - uint8_t mLinkQuality2; - uint8_t mLinkQuality1; - uint8_t mLeaderCost; - uint8_t mIdSequence; - uint8_t mActiveRouters; - uint16_t mSedBufferSize; - uint8_t mSedDatagramCount; -} OT_TOOL_PACKED_END; +typedef UintTlvInfo SupplyVoltageTlv; /** - * This class implements Route TLV generation and parsing. + * This class defines Child Table TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class RouteTlv : public NetworkDiagnosticTlv, public TlvInfo -{ -public: - /** - * This method initializes the TLV. - * - */ - void Init(void) - { - SetType(kRoute); - SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); - mRouterIdMask.Clear(); - } +typedef TlvInfo ChildTableTlv; - /** - * This method indicates whether or not the TLV appears to be well-formed. - * - * @retval TRUE If the TLV appears to be well-formed. - * @retval FALSE If the TLV does not appear to be well-formed. - * - */ - bool IsValid(void) const { return GetLength() >= sizeof(mRouterIdSequence) + sizeof(mRouterIdMask); } - - /** - * This method returns the Router ID Sequence value. - * - * @returns The Router ID Sequence value. - * - */ - uint8_t GetRouterIdSequence(void) const { return mRouterIdSequence; } - - /** - * This method sets the Router ID Sequence value. - * - * @param[in] aSequence The Router ID Sequence value. - * - */ - void SetRouterIdSequence(uint8_t aSequence) { mRouterIdSequence = aSequence; } - - /** - * This method indicates whether or not a Router ID bit is set. - * - * @param[in] aRouterId The Router ID. - * - * @retval TRUE If the Router ID bit is set. - * @retval FALSE If the Router ID bit is not set. - * - */ - bool IsRouterIdSet(uint8_t aRouterId) const { return mRouterIdMask.Contains(aRouterId); } - - /** - * This method sets the Router ID bit. - * - * @param[in] aRouterId The Router ID bit to set. - * - */ - void SetRouterId(uint8_t aRouterId) { mRouterIdMask.Add(aRouterId); } - - /** - * This method returns the Route Data Length value. - * - * @returns The Route Data Length value. - * - */ - uint8_t GetRouteDataLength(void) const { return GetLength() - sizeof(mRouterIdSequence) - sizeof(mRouterIdMask); } - - /** - * This method sets the Route Data Length value. - * - * @param[in] aLength The Route Data Length value. - * - */ - void SetRouteDataLength(uint8_t aLength) { SetLength(sizeof(mRouterIdSequence) + sizeof(mRouterIdMask) + aLength); } - - /** - * This method returns the Route Cost value for a given Router index. - * - * @param[in] aRouterIndex The Router index. - * - * @returns The Route Cost value for a given Router index. - * - */ - uint8_t GetRouteCost(uint8_t aRouterIndex) const { return mRouteData[aRouterIndex] & kRouteCostMask; } +/** + * This class defines Max Child Timeout TLV constants and types. + * + */ +typedef UintTlvInfo MaxChildTimeoutTlv; - /** - * This method sets the Route Cost value for a given Router index. - * - * @param[in] aRouterIndex The Router index. - * @param[in] aRouteCost The Route Cost value. - * - */ - void SetRouteCost(uint8_t aRouterIndex, uint8_t aRouteCost) - { - mRouteData[aRouterIndex] = (mRouteData[aRouterIndex] & ~kRouteCostMask) | aRouteCost; - } +/** + * This class defines Version TLV constants and types. + * + */ +typedef UintTlvInfo VersionTlv; - /** - * This method returns the Link Quality In value for a given Router index. - * - * @param[in] aRouterIndex The Router index. - * - * @returns The Link Quality In value for a given Router index. - * - */ - uint8_t GetLinkQualityIn(uint8_t aRouterIndex) const - { - return (mRouteData[aRouterIndex] & kLinkQualityInMask) >> kLinkQualityInOffset; - } +/** + * This class defines Vendor Name TLV constants and types. + * + */ +typedef StringTlvInfo VendorNameTlv; - /** - * This method sets the Link Quality In value for a given Router index. - * - * @param[in] aRouterIndex The Router index. - * @param[in] aLinkQuality The Link Quality In value for a given Router index. - * - */ - void SetLinkQualityIn(uint8_t aRouterIndex, uint8_t aLinkQuality) - { - mRouteData[aRouterIndex] = (mRouteData[aRouterIndex] & ~kLinkQualityInMask) | - ((aLinkQuality << kLinkQualityInOffset) & kLinkQualityInMask); - } +/** + * This class defines Vendor Model TLV constants and types. + * + */ +typedef StringTlvInfo VendorModelTlv; - /** - * This method returns the Link Quality Out value for a given Router index. - * - * @param[in] aRouterIndex The Router index. - * - * @returns The Link Quality Out value for a given Router index. - * - */ - uint8_t GetLinkQualityOut(uint8_t aRouterIndex) const - { - return (mRouteData[aRouterIndex] & kLinkQualityOutMask) >> kLinkQualityOutOffset; - } +/** + * This class defines Vendor SW Version TLV constants and types. + * + */ +typedef StringTlvInfo VendorSwVersionTlv; - /** - * This method sets the Link Quality Out value for a given Router index. - * - * @param[in] aRouterIndex The Router index. - * @param[in] aLinkQuality The Link Quality Out value for a given Router index. - * - */ - void SetLinkQualityOut(uint8_t aRouterIndex, uint8_t aLinkQuality) - { - mRouteData[aRouterIndex] = (mRouteData[aRouterIndex] & ~kLinkQualityOutMask) | - ((aLinkQuality << kLinkQualityOutOffset) & kLinkQualityOutMask); - } +/** + * This class defines Thread Stack Version TLV constants and types. + * + */ +typedef StringTlvInfo ThreadStackVersionTlv; -private: - static constexpr uint8_t kLinkQualityOutOffset = 6; - static constexpr uint8_t kLinkQualityOutMask = 3 << kLinkQualityOutOffset; - static constexpr uint8_t kLinkQualityInOffset = 4; - static constexpr uint8_t kLinkQualityInMask = 3 << kLinkQualityInOffset; - static constexpr uint8_t kRouteCostOffset = 0; - static constexpr uint8_t kRouteCostMask = 0xf << kRouteCostOffset; - - uint8_t mRouterIdSequence; - Mle::RouterIdSet mRouterIdMask; - uint8_t mRouteData[Mle::kMaxRouterId + 1]; -} OT_TOOL_PACKED_END; +typedef otNetworkDiagConnectivity Connectivity; ///< Network Diagnostic Connectivity value. /** - * This class implements Leader Data TLV generation and parsing. + * This class implements Connectivity TLV generation and parsing. * */ OT_TOOL_PACKED_BEGIN -class LeaderDataTlv : public NetworkDiagnosticTlv, public TlvInfo +class ConnectivityTlv : public Mle::ConnectivityTlv { public: + static constexpr uint8_t kType = ot::NetworkDiagnostic::Tlv::kConnectivity; ///< The TLV Type value. + /** * This method initializes the TLV. * */ void Init(void) { - SetType(kLeaderData); - SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); + Mle::ConnectivityTlv::Init(); + ot::Tlv::SetType(kType); } /** - * This method indicates whether or not the TLV appears to be well-formed. - * - * @retval TRUE If the TLV appears to be well-formed. - * @retval FALSE If the TLV does not appear to be well-formed. - * - */ - bool IsValid(void) const { return GetLength() >= sizeof(*this) - sizeof(NetworkDiagnosticTlv); } - - /** - * This method returns the Partition ID value. - * - * @returns The Partition ID value. - * - */ - uint32_t GetPartitionId(void) const { return HostSwap32(mPartitionId); } - - /** - * This method sets the Partition ID value. - * - * @param[in] aPartitionId The Partition ID value. - * - */ - void SetPartitionId(uint32_t aPartitionId) { mPartitionId = HostSwap32(aPartitionId); } - - /** - * This method returns the Weighting value. - * - * @returns The Weighting value. - * - */ - uint8_t GetWeighting(void) const { return mWeighting; } - - /** - * This method sets the Weighting value. - * - * @param[in] aWeighting The Weighting value. - * - */ - void SetWeighting(uint8_t aWeighting) { mWeighting = aWeighting; } - - /** - * This method returns the Data Version value. - * - * @returns The Data Version value. - * - */ - uint8_t GetDataVersion(void) const { return mDataVersion; } - - /** - * This method sets the Data Version value. - * - * @param[in] aVersion The Data Version value. - * - */ - void SetDataVersion(uint8_t aVersion) { mDataVersion = aVersion; } - - /** - * This method returns the Stable Data Version value. - * - * @returns The Stable Data Version value. - * - */ - uint8_t GetStableDataVersion(void) const { return mStableDataVersion; } - - /** - * This method sets the Stable Data Version value. - * - * @param[in] aVersion The Stable Data Version value. - * - */ - void SetStableDataVersion(uint8_t aVersion) { mStableDataVersion = aVersion; } - - /** - * This method returns the Leader Router ID value. + * This method retrieves the `Connectivity` value. * - * @returns The Leader Router ID value. + * @param[out] aConnectivity A reference to `Connectivity` to populate. * */ - uint8_t GetLeaderRouterId(void) const { return mLeaderRouterId; } - - /** - * This method sets the Leader Router ID value. - * - * @param[in] aRouterId The Leader Router ID value. - * - */ - void SetLeaderRouterId(uint8_t aRouterId) { mLeaderRouterId = aRouterId; } + void GetConnectivity(Connectivity &aConnectivity) const + { + aConnectivity.mParentPriority = GetParentPriority(); + aConnectivity.mLinkQuality3 = GetLinkQuality3(); + aConnectivity.mLinkQuality2 = GetLinkQuality2(); + aConnectivity.mLinkQuality1 = GetLinkQuality1(); + aConnectivity.mLeaderCost = GetLeaderCost(); + aConnectivity.mIdSequence = GetIdSequence(); + aConnectivity.mActiveRouters = GetActiveRouters(); + aConnectivity.mSedBufferSize = GetSedBufferSize(); + aConnectivity.mSedDatagramCount = GetSedDatagramCount(); + } -private: - uint32_t mPartitionId; - uint8_t mWeighting; - uint8_t mDataVersion; - uint8_t mStableDataVersion; - uint8_t mLeaderRouterId; } OT_TOOL_PACKED_END; /** - * This class implements Network Data TLV generation and parsing. + * This class implements Route TLV generation and parsing. * */ OT_TOOL_PACKED_BEGIN -class NetworkDataTlv : public NetworkDiagnosticTlv, public TlvInfo +class RouteTlv : public Mle::RouteTlv { public: + static constexpr uint8_t kType = ot::NetworkDiagnostic::Tlv::kRoute; ///< The TLV Type value. + /** * This method initializes the TLV. * */ void Init(void) { - SetType(kNetworkData); - SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); + Mle::RouteTlv::Init(); + ot::Tlv::SetType(kType); } - - /** - * This method indicates whether or not the TLV appears to be well-formed. - * - * @retval TRUE If the TLV appears to be well-formed. - * @retval FALSE If the TLV does not appear to be well-formed. - * - */ - bool IsValid(void) const { return GetLength() < sizeof(*this) - sizeof(NetworkDiagnosticTlv); } - - /** - * This method returns a pointer to the Network Data. - * - * @returns A pointer to the Network Data. - * - */ - uint8_t *GetNetworkData(void) { return mNetworkData; } - - /** - * This method sets the Network Data. - * - * @param[in] aNetworkData A pointer to the Network Data. - * - */ - void SetNetworkData(const uint8_t *aNetworkData) { memcpy(mNetworkData, aNetworkData, GetLength()); } - -private: - uint8_t mNetworkData[255]; } OT_TOOL_PACKED_END; /** - * This class implements IPv6 Address List TLV generation and parsing. + * This class implements Leader Data TLV generation and parsing. * */ OT_TOOL_PACKED_BEGIN -class Ip6AddressListTlv : public NetworkDiagnosticTlv, public TlvInfo +class LeaderDataTlv : public Mle::LeaderDataTlv { public: + static constexpr uint8_t kType = ot::NetworkDiagnostic::Tlv::kLeaderData; ///< The TLV Type value. + /** * This method initializes the TLV. * */ void Init(void) { - SetType(kIp6AddressList); - SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); - } - - /** - * This method indicates whether or not the TLV appears to be well-formed. - * - * @retval TRUE If the TLV appears to be well-formed. - * @retval FALSE If the TLV does not appear to be well-formed. - * - */ - bool IsValid(void) const { return !IsExtended() && (GetLength() % sizeof(Ip6::Address) == 0); } - - /** - * This method returns a pointer to the IPv6 address entry. - * - * @param[in] aIndex The index into the IPv6 address list. - * - * @returns A reference to the IPv6 address. - * - */ - const Ip6::Address &GetIp6Address(uint8_t aIndex) const - { - return *reinterpret_cast(GetValue() + (aIndex * sizeof(Ip6::Address))); + Mle::LeaderDataTlv::Init(); + ot::Tlv::SetType(kType); } - } OT_TOOL_PACKED_END; /** @@ -749,7 +314,7 @@ class Ip6AddressListTlv : public NetworkDiagnosticTlv, public TlvInfo +class MacCountersTlv : public Tlv, public TlvInfo { public: /** @@ -759,7 +324,7 @@ class MacCountersTlv : public NetworkDiagnosticTlv, public TlvInfo= sizeof(*this) - sizeof(NetworkDiagnosticTlv); } + bool IsValid(void) const { return GetLength() >= sizeof(*this) - sizeof(Tlv); } /** * This method returns the IfInUnknownProtos counter. @@ -940,37 +505,48 @@ class MacCountersTlv : public NetworkDiagnosticTlv, public TlvInfo { public: /** - * Default constructor. + * This method returns the Timeout value. + * + * @returns The Timeout value. + * + */ + uint8_t GetTimeout(void) const { return (GetTimeoutChildId() & kTimeoutMask) >> kTimeoutOffset; } + + /** + * This method sets the Timeout value. + * + * @param[in] aTimeout The Timeout value. * */ - ChildTableEntry(void) - : mTimeoutRsvChildId(0) - , mMode(0) + void SetTimeout(uint8_t aTimeout) { + SetTimeoutChildId((GetTimeoutChildId() & ~kTimeoutMask) | ((aTimeout << kTimeoutOffset) & kTimeoutMask)); } /** - * This method returns the Timeout value. + * This method the Link Quality value. * - * @returns The Timeout value. + * @returns The Link Quality value. * */ - uint8_t GetTimeout(void) const { return (HostSwap16(mTimeoutRsvChildId) & kTimeoutMask) >> kTimeoutOffset; } + LinkQuality GetLinkQuality(void) const + { + return static_cast((GetTimeoutChildId() & kLqiMask) >> kLqiOffset); + } /** - * This method sets the Timeout value. + * This method set the Link Quality value. * - * @param[in] aTimeout The Timeout value. + * @param[in] aLinkQuality The Link Quality value. * */ - void SetTimeout(uint8_t aTimeout) + void SetLinkQuality(LinkQuality aLinkQuality) { - mTimeoutRsvChildId = HostSwap16((HostSwap16(mTimeoutRsvChildId) & ~kTimeoutMask) | - ((aTimeout << kTimeoutOffset) & kTimeoutMask)); + SetTimeoutChildId((GetTimeoutChildId() & ~kLqiMask) | ((aLinkQuality << kLqiOffset) & kLqiMask)); } /** @@ -979,7 +555,7 @@ class ChildTableEntry * @returns The Child ID value. * */ - uint16_t GetChildId(void) const { return HostSwap16(mTimeoutRsvChildId) & kChildIdMask; } + uint16_t GetChildId(void) const { return (GetTimeoutChildId() & kChildIdMask) >> kChildIdOffset; } /** * This method sets the Child ID value. @@ -989,7 +565,7 @@ class ChildTableEntry */ void SetChildId(uint16_t aChildId) { - mTimeoutRsvChildId = HostSwap16((HostSwap16(mTimeoutRsvChildId) & ~kChildIdMask) | (aChildId & kChildIdMask)); + SetTimeoutChildId((GetTimeoutChildId() & ~kChildIdMask) | ((aChildId << kChildIdOffset) & kChildIdMask)); } /** @@ -1008,114 +584,33 @@ class ChildTableEntry */ void SetMode(Mle::DeviceMode aMode) { mMode = aMode.Get(); } - /** - * This method returns the Reserved value. - * - * @returns The Reserved value. - * - */ - uint8_t GetReserved(void) const { return (HostSwap16(mTimeoutRsvChildId) & kReservedMask) >> kReservedOffset; } - - /** - * This method sets the Reserved value. - * - * @param[in] aReserved The Reserved value. - * - */ - void SetReserved(uint8_t aReserved) - { - mTimeoutRsvChildId = HostSwap16((HostSwap16(mTimeoutRsvChildId) & ~kReservedMask) | - ((aReserved << kReservedOffset) & kReservedMask)); - } - private: - static constexpr uint8_t kTimeoutOffset = 11; - static constexpr uint8_t kReservedOffset = 9; - static constexpr uint16_t kTimeoutMask = 0xf800; - static constexpr uint16_t kReservedMask = 0x0600; - static constexpr uint16_t kChildIdMask = 0x1ff; - - uint16_t mTimeoutRsvChildId; + // 1 0 + // 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Timeout |LQI| Child ID | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + static constexpr uint8_t kTimeoutOffset = 11; + static constexpr uint8_t kLqiOffset = 9; + static constexpr uint8_t kChildIdOffset = 0; + static constexpr uint16_t kTimeoutMask = 0x1f << kTimeoutOffset; + static constexpr uint16_t kLqiMask = 0x3 << kLqiOffset; + static constexpr uint16_t kChildIdMask = 0x1ff << kChildIdOffset; + + uint16_t GetTimeoutChildId(void) const { return HostSwap16(mTimeoutChildId); } + void SetTimeoutChildId(uint16_t aTimeoutChildIf) { mTimeoutChildId = HostSwap16(aTimeoutChildIf); } + + uint16_t mTimeoutChildId; uint8_t mMode; } OT_TOOL_PACKED_END; -/** - * This class implements Child Table TLV generation and parsing. - * - */ -OT_TOOL_PACKED_BEGIN -class ChildTableTlv : public NetworkDiagnosticTlv, public TlvInfo -{ -public: - /** - * This method initializes the TLV. - * - */ - void Init(void) - { - SetType(kChildTable); - SetLength(sizeof(*this) - sizeof(NetworkDiagnosticTlv)); - } - - /** - * This method indicates whether or not the TLV appears to be well-formed. - * - * @retval TRUE If the TLV appears to be well-formed. - * @retval FALSE If the TLV does not appear to be well-formed. - * - */ - bool IsValid(void) const { return (GetLength() % sizeof(ChildTableEntry)) == 0; } - - /** - * This method returns the number of Child Table entries. - * - * @returns The number of Child Table entries. - * - */ - uint8_t GetNumEntries(void) const { return GetLength() / sizeof(ChildTableEntry); } - - /** - * This method returns the Child Table entry at @p aIndex. - * - * @param[in] aIndex The index into the Child Table list. - * - * @returns A reference to the Child Table entry. - * - */ - ChildTableEntry &GetEntry(uint16_t aIndex) - { - return *reinterpret_cast(GetValue() + (aIndex * sizeof(ChildTableEntry))); - } - - /** - * This method reads the Child Table entry at @p aIndex. - * - * @param[out] aEntry A reference to a ChildTableEntry. - * @param[in] aMessage A reference to the message. - * @param[in] aOffset The offset of the ChildTableTLV in aMessage. - * @param[in] aIndex The index into the Child Table list. - * - * @retval kErrorNotFound No such entry is found. - * @retval kErrorNone Successfully read the entry. - * - */ - Error ReadEntry(ChildTableEntry &aEntry, const Message &aMessage, uint16_t aOffset, uint8_t aIndex) const - { - return ((aIndex < GetNumEntries()) && - (aMessage.Read(aOffset + sizeof(ChildTableTlv) + (aIndex * sizeof(ChildTableEntry)), aEntry) == - kErrorNone)) - ? kErrorNone - : kErrorInvalidArgs; - } - -} OT_TOOL_PACKED_END; - /** * This class implements Channel Pages TLV generation and parsing. * */ OT_TOOL_PACKED_BEGIN -class ChannelPagesTlv : public NetworkDiagnosticTlv, public TlvInfo +class ChannelPagesTlv : public Tlv, public TlvInfo { public: /** @@ -1125,7 +620,7 @@ class ChannelPagesTlv : public NetworkDiagnosticTlv, public TlvInfo +class TypeListTlv : public Tlv, public TlvInfo { public: /** @@ -1168,17 +663,11 @@ class TypeListTlv : public NetworkDiagnosticTlv, public TlvInfo().AddResource(mPanIdQuery); } -void PanIdQueryServer::HandleQuery(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +template <> +void PanIdQueryServer::HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { - static_cast(aContext)->HandleQuery(AsCoapMessage(aMessage), AsCoreType(aMessageInfo)); -} - -void PanIdQueryServer::HandleQuery(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) -{ - uint16_t panId; - Ip6::MessageInfo responseInfo(aMessageInfo); - uint32_t mask; + uint16_t panId; + uint32_t mask; VerifyOrExit(aMessage.IsPostRequest()); VerifyOrExit((mask = MeshCoP::ChannelMaskTlv::GetChannelMask(aMessage)) != 0); @@ -82,8 +75,8 @@ void PanIdQueryServer::HandleQuery(Coap::Message &aMessage, const Ip6::MessageIn if (aMessage.IsConfirmable() && !aMessageInfo.GetSockAddr().IsMulticast()) { - SuccessOrExit(Get().SendEmptyAck(aMessage, responseInfo)); - LogInfo("sent panid query response"); + SuccessOrExit(Get().SendEmptyAck(aMessage, aMessageInfo)); + LogInfo("Sent %s ack", UriToString()); } exit: @@ -115,9 +108,9 @@ void PanIdQueryServer::SendConflict(void) Error error = kErrorNone; MeshCoP::ChannelMaskTlv channelMask; Tmf::MessageInfo messageInfo(GetInstance()); - Coap::Message * message; + Coap::Message *message; - message = Get().NewPriorityConfirmablePostMessage(UriPath::kPanIdConflict); + message = Get().NewPriorityConfirmablePostMessage(kUriPanIdConflict); VerifyOrExit(message != nullptr, error = kErrorNoBufs); channelMask.Init(); @@ -130,18 +123,13 @@ void PanIdQueryServer::SendConflict(void) SuccessOrExit(error = Get().SendMessage(*message, messageInfo)); - LogInfo("sent panid conflict"); + LogInfo("Sent %s", UriToString()); exit: FreeMessageOnError(message, error); MeshCoP::LogError("send panid conflict", error); } -void PanIdQueryServer::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void PanIdQueryServer::HandleTimer(void) { IgnoreError(Get().ActiveScan(mChannelMask, 0, HandleScanResult, this)); diff --git a/src/core/thread/panid_query_server.hpp b/src/core/thread/panid_query_server.hpp index 20f3843a5cf..70deaf9e98c 100644 --- a/src/core/thread/panid_query_server.hpp +++ b/src/core/thread/panid_query_server.hpp @@ -36,13 +36,13 @@ #include "openthread-core-config.h" -#include "coap/coap.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" #include "common/timer.hpp" #include "mac/mac.hpp" #include "net/ip6_address.hpp" #include "net/udp6.hpp" +#include "thread/tmf.hpp" namespace ot { @@ -52,6 +52,8 @@ namespace ot { */ class PanIdQueryServer : public InstanceLocator, private NonCopyable { + friend class Tmf::Agent; + public: /** * This constructor initializes the object. @@ -62,28 +64,28 @@ class PanIdQueryServer : public InstanceLocator, private NonCopyable private: static constexpr uint32_t kScanDelay = 1000; ///< SCAN_DELAY (in msec) - static void HandleQuery(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleQuery(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); static void HandleScanResult(Mac::ActiveScanResult *aScanResult, void *aContext); void HandleScanResult(Mac::ActiveScanResult *aScanResult); - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); + void HandleTimer(void); static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); void SendConflict(void); + using DelayTimer = TimerMilliIn; + Ip6::Address mCommissioner; uint32_t mChannelMask; uint16_t mPanId; - TimerMilli mTimer; - - Coap::Resource mPanIdQuery; + DelayTimer mTimer; }; +DeclareTmfHandler(PanIdQueryServer, kUriPanIdQuery); + /** * @} */ diff --git a/src/core/thread/radio_selector.cpp b/src/core/thread/radio_selector.cpp index cb619a3f1f4..965734a0ca1 100644 --- a/src/core/thread/radio_selector.cpp +++ b/src/core/thread/radio_selector.cpp @@ -85,28 +85,28 @@ void RadioSelector::NeighborInfo::PopulateMultiRadioInfo(MultiRadioInfo &aInfo) LogLevel RadioSelector::UpdatePreference(Neighbor &aNeighbor, Mac::RadioType aRadioType, int16_t aDifference) { uint8_t old = aNeighbor.GetRadioPreference(aRadioType); - int16_t preferecne = static_cast(old); + int16_t preference = static_cast(old); - preferecne += aDifference; + preference += aDifference; - if (preferecne > kMaxPreference) + if (preference > kMaxPreference) { - preferecne = kMaxPreference; + preference = kMaxPreference; } - if (preferecne < kMinPreference) + if (preference < kMinPreference) { - preferecne = kMinPreference; + preference = kMinPreference; } - aNeighbor.SetRadioPreference(aRadioType, static_cast(preferecne)); + aNeighbor.SetRadioPreference(aRadioType, static_cast(preference)); // We check whether the update to the preference value caused it // to cross the threshold `kHighPreference`. Based on this we // return a suggested log level. If there is cross, suggest info // log level, otherwise debug log level. - return ((old >= kHighPreference) != (preferecne >= kHighPreference)) ? kLogLevelInfo : kLogLevelDebg; + return ((old >= kHighPreference) != (preference >= kHighPreference)) ? kLogLevelInfo : kLogLevelDebg; } void RadioSelector::UpdateOnReceive(Neighbor &aNeighbor, Mac::RadioType aRadioType, bool aIsDuplicate) @@ -134,7 +134,7 @@ void RadioSelector::UpdateOnSendDone(Mac::TxFrame &aFrame, Error aTxError) LogLevel logLevel = kLogLevelInfo; Mac::RadioType radioType = aFrame.GetRadioType(); Mac::Address macDest; - Neighbor * neighbor; + Neighbor *neighbor; #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE if (radioType == Mac::kRadioTypeTrel) @@ -257,7 +257,7 @@ Mac::RadioType RadioSelector::Select(Mac::RadioTypes aRadioOptions, const Neighb Mac::TxFrame &RadioSelector::SelectRadio(Message &aMessage, const Mac::Address &aMacDest, Mac::TxFrames &aTxFrames) { - Neighbor * neighbor; + Neighbor *neighbor; Mac::RadioType selectedRadio; Mac::RadioTypes selections; @@ -358,7 +358,7 @@ Mac::RadioType RadioSelector::SelectPollFrameRadio(const Neighbor &aParent) #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) void RadioSelector::Log(LogLevel aLogLevel, - const char * aActionText, + const char *aActionText, Mac::RadioType aRadioType, const Neighbor &aNeighbor) { @@ -387,9 +387,7 @@ void RadioSelector::Log(LogLevel aLogLevel, #else // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) -void RadioSelector::Log(LogLevel, const char *, Mac::RadioType, const Neighbor &) -{ -} +void RadioSelector::Log(LogLevel, const char *, Mac::RadioType, const Neighbor &) {} #endif // #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) diff --git a/src/core/thread/router_table.cpp b/src/core/thread/router_table.cpp index 722faddb992..407f51117a1 100644 --- a/src/core/thread/router_table.cpp +++ b/src/core/thread/router_table.cpp @@ -44,61 +44,32 @@ namespace ot { RegisterLogModule("RouterTable"); -RouterTable::Iterator::Iterator(Instance &aInstance) - : InstanceLocator(aInstance) - , ItemPtrIterator(Get().GetFirstEntry()) -{ -} - -void RouterTable::Iterator::Advance(void) -{ - mItem = Get().GetNextEntry(mItem); -} - RouterTable::RouterTable(Instance &aInstance) : InstanceLocator(aInstance) + , mRouters(aInstance) + , mChangedTask(aInstance) , mRouterIdSequenceLastUpdated(0) , mRouterIdSequence(Random::NonCrypto::GetUint8()) - , mActiveRouterCount(0) #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE , mMinRouterId(0) , mMaxRouterId(Mle::kMaxRouterId) #endif { - for (Router &router : mRouters) - { - router.Init(aInstance); - } - Clear(); } -const Router *RouterTable::GetFirstEntry(void) const -{ - const Router *router = &mRouters[0]; - VerifyOrExit(router->GetRloc16() != 0xffff, router = nullptr); - -exit: - return router; -} - -const Router *RouterTable::GetNextEntry(const Router *aRouter) const +void RouterTable::Clear(void) { - VerifyOrExit(aRouter != nullptr); - aRouter++; - VerifyOrExit(aRouter < &mRouters[Mle::kMaxRouters], aRouter = nullptr); - VerifyOrExit(aRouter->GetRloc16() != 0xffff, aRouter = nullptr); - -exit: - return aRouter; + ClearNeighbors(); + mRouterIdMap.Clear(); + mRouters.Clear(); + SignalTableChanged(); } -void RouterTable::Clear(void) +bool RouterTable::IsRouteTlvIdSequenceMoreRecent(const Mle::RouteTlv &aRouteTlv) const { - ClearNeighbors(); - mAllocatedRouterIds.Clear(); - memset(mRouterIdReuseDelay, 0, sizeof(mRouterIdReuseDelay)); - UpdateAllocation(); + return (GetActiveRouterCount() == 0) || + SerialNumber::IsGreater(aRouteTlv.GetRouterIdSequence(), GetRouterIdSequence()); } void RouterTable::ClearNeighbors(void) @@ -108,163 +79,107 @@ void RouterTable::ClearNeighbors(void) if (router.IsStateValid()) { Get().Signal(NeighborTable::kRouterRemoved, router); + SignalTableChanged(); } router.SetState(Neighbor::kStateInvalid); } } -bool RouterTable::IsAllocated(uint8_t aRouterId) const -{ - return mAllocatedRouterIds.Contains(aRouterId); -} - -void RouterTable::UpdateAllocation(void) +Router *RouterTable::AddRouter(uint8_t aRouterId) { - uint8_t indexMap[Mle::kMaxRouterId + 1]; + // Add a new `Router` entry to `mRouters` array with given + // `aRouterId` and update the `mRouterIdMap`. - mActiveRouterCount = 0; + Router *router = mRouters.PushBack(); - // build index map - for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) - { - if (IsAllocated(routerId) && mActiveRouterCount < Mle::kMaxRouters) - { - indexMap[routerId] = mActiveRouterCount++; - } - else - { - indexMap[routerId] = Mle::kInvalidRouterId; - } - } + VerifyOrExit(router != nullptr); - // shift entries forward - for (int index = Mle::kMaxRouters - 2; index >= 0; index--) - { - uint8_t routerId = mRouters[index].GetRouterId(); - uint8_t newIndex; + router->Clear(); + router->SetRloc16(Mle::Rloc16FromRouterId(aRouterId)); + router->SetNextHopToInvalid(); - if (routerId > Mle::kMaxRouterId || indexMap[routerId] == Mle::kInvalidRouterId) - { - continue; - } + mRouterIdMap.SetIndex(aRouterId, mRouters.IndexOf(*router)); + SignalTableChanged(); - newIndex = indexMap[routerId]; +exit: + return router; +} - if (newIndex > index) - { - mRouters[newIndex] = mRouters[index]; - } - } +void RouterTable::RemoveRouter(Router &aRouter) +{ + // Remove an existing `aRouter` entry from `mRouters` and update the + // `mRouterIdMap`. - // shift entries backward - for (uint8_t index = 1; index < Mle::kMaxRouters; index++) + if (aRouter.IsStateValid()) { - uint8_t routerId = mRouters[index].GetRouterId(); - uint8_t newIndex; - - if (routerId > Mle::kMaxRouterId || indexMap[routerId] == Mle::kInvalidRouterId) - { - continue; - } - - newIndex = indexMap[routerId]; - - if (newIndex < index) - { - mRouters[newIndex] = mRouters[index]; - } + Get().Signal(NeighborTable::kRouterRemoved, aRouter); } - // fix replaced entries - for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) - { - uint8_t index = indexMap[routerId]; - - if (index != Mle::kInvalidRouterId) - { - Router &router = mRouters[index]; + mRouterIdMap.Release(aRouter.GetRouterId()); + mRouters.Remove(aRouter); - if (router.GetRouterId() != routerId) - { - router.Clear(); - router.SetRloc16(Mle::Mle::Rloc16FromRouterId(routerId)); - router.SetNextHop(Mle::kInvalidRouterId); - } - } - } + // Removing `aRouter` from `mRouters` array will replace it with + // the last entry in the array (if not already the last entry) so + // we update the index in `mRouteIdMap` for the moved entry. - // clear unused entries - for (uint8_t index = mActiveRouterCount; index < Mle::kMaxRouters; index++) + if (IsAllocated(aRouter.GetRouterId())) { - Router &router = mRouters[index]; - router.Clear(); - router.SetRloc16(0xffff); + mRouterIdMap.SetIndex(aRouter.GetRouterId(), mRouters.IndexOf((aRouter))); } + + SignalTableChanged(); } Router *RouterTable::Allocate(void) { - Router *rval = nullptr; - uint8_t numAvailable = 0; - uint8_t freeBit; + Router *router = nullptr; + uint8_t numAvailable = 0; + uint8_t selectedRouterId = Mle::kInvalidRouterId; + + VerifyOrExit(!mRouters.IsFull()); - // count available router ids #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE for (uint8_t routerId = mMinRouterId; routerId <= mMaxRouterId; routerId++) #else for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) #endif { - if (!IsAllocated(routerId) && mRouterIdReuseDelay[routerId] == 0) + if (mRouterIdMap.CanAllocate(routerId)) { numAvailable++; - } - } - - VerifyOrExit(mActiveRouterCount < Mle::kMaxRouters && numAvailable > 0); - // choose available router id at random - freeBit = Random::NonCrypto::GetUint8InRange(0, numAvailable); + // Randomly select a router ID as we iterate through the + // list using Reservoir algorithm: We replace the + // selected ID with current entry in the list with + // probably `1/numAvailable`. - // allocate router -#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE - for (uint8_t routerId = mMinRouterId; routerId <= mMaxRouterId; routerId++) -#else - for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) -#endif - { - if (IsAllocated(routerId) || mRouterIdReuseDelay[routerId] > 0) - { - continue; + if (Random::NonCrypto::GetUint8InRange(0, numAvailable) == 0) + { + selectedRouterId = routerId; + } } + } - if (freeBit == 0) - { - rval = Allocate(routerId); - OT_ASSERT(rval != nullptr); - ExitNow(); - } + VerifyOrExit(selectedRouterId != Mle::kInvalidRouterId); - freeBit--; - } + router = Allocate(selectedRouterId); + OT_ASSERT(router != nullptr); exit: - return rval; + return router; } Router *RouterTable::Allocate(uint8_t aRouterId) { - Router *rval = nullptr; + Router *router = nullptr; - VerifyOrExit(aRouterId <= Mle::kMaxRouterId && mActiveRouterCount < Mle::kMaxRouters && !IsAllocated(aRouterId) && - mRouterIdReuseDelay[aRouterId] == 0); + VerifyOrExit(aRouterId <= Mle::kMaxRouterId && mRouterIdMap.CanAllocate(aRouterId)); - mAllocatedRouterIds.Add(aRouterId); - UpdateAllocation(); + router = AddRouter(aRouterId); + VerifyOrExit(router != nullptr); - rval = GetRouter(aRouterId); - rval->SetLastHeard(TimerMilli::GetNow()); + router->SetLastHeard(TimerMilli::GetNow()); mRouterIdSequence++; mRouterIdSequenceLastUpdated = TimerMilli::GetNow(); @@ -273,46 +188,37 @@ Router *RouterTable::Allocate(uint8_t aRouterId) LogNote("Allocate router id %d", aRouterId); exit: - return rval; + return router; } Error RouterTable::Release(uint8_t aRouterId) { - Error error = kErrorNone; - uint16_t rloc16 = Mle::Mle::Rloc16FromRouterId(aRouterId); - Router * router; + Error error = kErrorNone; + Router *router; OT_ASSERT(aRouterId <= Mle::kMaxRouterId); VerifyOrExit(Get().IsLeader(), error = kErrorInvalidState); - VerifyOrExit(IsAllocated(aRouterId), error = kErrorNotFound); - - router = GetNeighbor(rloc16); - if (router != nullptr) - { - Get().Signal(NeighborTable::kRouterRemoved, *router); - } - - mAllocatedRouterIds.Remove(aRouterId); - UpdateAllocation(); + router = FindRouterById(aRouterId); + VerifyOrExit(router != nullptr, error = kErrorNotFound); - mRouterIdReuseDelay[aRouterId] = Mle::kRouterIdReuseDelay; + RemoveRouter(*router); - for (router = GetFirstEntry(); router != nullptr; router = GetNextEntry(router)) + for (Router &otherRouter : mRouters) { - if (router->GetNextHop() == rloc16) + if (otherRouter.GetNextHop() == aRouterId) { - router->SetNextHop(Mle::kInvalidRouterId); - router->SetCost(0); + otherRouter.SetNextHopToInvalid(); } } mRouterIdSequence++; mRouterIdSequenceLastUpdated = TimerMilli::GetNow(); - Get().Remove(aRouterId); - Get().RemoveBorderRouter(rloc16, NetworkData::Leader::kMatchModeRouterId); + Get().RemoveEntriesForRouterId(aRouterId); + Get().RemoveBorderRouter(Mle::Rloc16FromRouterId(aRouterId), + NetworkData::Leader::kMatchModeRouterId); Get().ResetAdvertiseInterval(); LogNote("Release router id %d", aRouterId); @@ -323,20 +229,21 @@ Error RouterTable::Release(uint8_t aRouterId) void RouterTable::RemoveRouterLink(Router &aRouter) { - if (aRouter.GetLinkQualityOut() != 0) + if (aRouter.GetLinkQualityOut() != kLinkQuality0) { aRouter.SetLinkQualityOut(kLinkQuality0); aRouter.SetLastHeard(TimerMilli::GetNow()); + SignalTableChanged(); } - for (Router *cur = GetFirstEntry(); cur != nullptr; cur = GetNextEntry(cur)) + for (Router &router : mRouters) { - if (cur->GetNextHop() == aRouter.GetRouterId()) + if (router.GetNextHop() == aRouter.GetRouterId()) { - cur->SetNextHop(Mle::kInvalidRouterId); - cur->SetCost(0); + router.SetNextHopToInvalid(); + SignalTableChanged(); - if (GetLinkCost(*cur) >= Mle::kMaxRouteCost) + if (GetLinkCost(router) >= Mle::kMaxRouteCost) { Get().ResetAdvertiseInterval(); } @@ -348,41 +255,16 @@ void RouterTable::RemoveRouterLink(Router &aRouter) Get().ResetAdvertiseInterval(); // Clear all EID-to-RLOC entries associated with the router. - Get().Remove(aRouter.GetRouterId()); - } -} - -uint8_t RouterTable::GetActiveLinkCount(void) const -{ - uint8_t activeLinks = 0; - - for (const Router *router = GetFirstEntry(); router != nullptr; router = GetNextEntry(router)) - { - if (router->IsStateValid()) - { - activeLinks++; - } + Get().RemoveEntriesForRouterId(aRouter.GetRouterId()); } - - return activeLinks; } const Router *RouterTable::FindRouter(const Router::AddressMatcher &aMatcher) const { - const Router *router; - - for (router = GetFirstEntry(); router != nullptr; router = GetNextEntry(router)) - { - if (router->Matches(aMatcher)) - { - break; - } - } - - return router; + return mRouters.FindMatching(aMatcher); } -Router *RouterTable::GetNeighbor(uint16_t aRloc16) +Router *RouterTable::FindNeighbor(uint16_t aRloc16) { Router *router = nullptr; @@ -393,32 +275,37 @@ Router *RouterTable::GetNeighbor(uint16_t aRloc16) return router; } -Router *RouterTable::GetNeighbor(const Mac::ExtAddress &aExtAddress) +Router *RouterTable::FindNeighbor(const Mac::ExtAddress &aExtAddress) { return FindRouter(Router::AddressMatcher(aExtAddress, Router::kInStateValid)); } -Router *RouterTable::GetNeighbor(const Mac::Address &aMacAddress) +Router *RouterTable::FindNeighbor(const Mac::Address &aMacAddress) { return FindRouter(Router::AddressMatcher(aMacAddress, Router::kInStateValid)); } -const Router *RouterTable::GetRouter(uint8_t aRouterId) const +const Router *RouterTable::FindRouterById(uint8_t aRouterId) const { const Router *router = nullptr; - uint16_t rloc16; - // Skip if invalid router id is passed. - VerifyOrExit(aRouterId < Mle::kInvalidRouterId); + VerifyOrExit(aRouterId <= Mle::kMaxRouterId); - rloc16 = Mle::Mle::Rloc16FromRouterId(aRouterId); - router = FindRouter(Router::AddressMatcher(rloc16, Router::kInStateAny)); + VerifyOrExit(IsAllocated(aRouterId)); + router = &mRouters[mRouterIdMap.GetIndex(aRouterId)]; exit: return router; } -Router *RouterTable::GetRouter(const Mac::ExtAddress &aExtAddress) +const Router *RouterTable::FindRouterByRloc16(uint16_t aRloc16) const +{ + return FindRouterById(Mle::RouterIdFromRloc16(aRloc16)); +} + +const Router *RouterTable::FindNextHopOf(const Router &aRouter) const { return FindRouterById(aRouter.GetNextHop()); } + +Router *RouterTable::FindRouter(const Mac::ExtAddress &aExtAddress) { return FindRouter(Router::AddressMatcher(aExtAddress, Router::kInStateAny)); } @@ -435,12 +322,12 @@ Error RouterTable::GetRouterInfo(uint16_t aRouterId, Router::Info &aRouterInfo) } else { - VerifyOrExit(Mle::Mle::IsActiveRouter(aRouterId), error = kErrorInvalidArgs); - routerId = Mle::Mle::RouterIdFromRloc16(aRouterId); + VerifyOrExit(Mle::IsActiveRouter(aRouterId), error = kErrorInvalidArgs); + routerId = Mle::RouterIdFromRloc16(aRouterId); VerifyOrExit(routerId <= Mle::kMaxRouterId, error = kErrorInvalidArgs); } - router = GetRouter(routerId); + router = FindRouterById(routerId); VerifyOrExit(router != nullptr, error = kErrorNotFound); aRouterInfo.SetFrom(*router); @@ -449,23 +336,20 @@ Error RouterTable::GetRouterInfo(uint16_t aRouterId, Router::Info &aRouterInfo) return error; } -Router *RouterTable::GetLeader(void) -{ - return GetRouter(Get().GetLeaderId()); -} +const Router *RouterTable::GetLeader(void) const { return FindRouterById(Get().GetLeaderId()); } uint32_t RouterTable::GetLeaderAge(void) const { - return (mActiveRouterCount > 0) ? Time::MsecToSec(TimerMilli::GetNow() - mRouterIdSequenceLastUpdated) : 0xffffffff; + return (!mRouters.IsEmpty()) ? Time::MsecToSec(TimerMilli::GetNow() - mRouterIdSequenceLastUpdated) : 0xffffffff; } uint8_t RouterTable::GetNeighborCount(void) const { uint8_t count = 0; - for (const Router *router = GetFirstEntry(); router != nullptr; router = GetNextEntry(router)) + for (const Router &router : mRouters) { - if (router->IsStateValid()) + if (router.IsStateValid()) { count++; } @@ -474,76 +358,468 @@ uint8_t RouterTable::GetNeighborCount(void) const return count; } -uint8_t RouterTable::GetLinkCost(Router &aRouter) +uint8_t RouterTable::GetLinkCost(const Router &aRouter) const { uint8_t rval = Mle::kMaxRouteCost; VerifyOrExit(aRouter.GetRloc16() != Get().GetRloc16() && aRouter.IsStateValid()); - rval = aRouter.GetLinkInfo().GetLinkQuality(); + rval = CostForLinkQuality(aRouter.GetTwoWayLinkQuality()); - if (rval > aRouter.GetLinkQualityOut()) +exit: + return rval; +} + +uint8_t RouterTable::GetLinkCost(uint8_t aRouterId) const +{ + uint8_t rval = Mle::kMaxRouteCost; + const Router *router; + + router = FindRouterById(aRouterId); + + // `nullptr` aRouterId indicates non-existing next hop, hence return kMaxRouteCost for it. + VerifyOrExit(router != nullptr); + + rval = GetLinkCost(*router); + +exit: + return rval; +} + +uint8_t RouterTable::GetPathCost(uint16_t aDestRloc16) const +{ + uint8_t pathCost; + uint16_t nextHopRloc16; + + GetNextHopAndPathCost(aDestRloc16, nextHopRloc16, pathCost); + + return pathCost; +} + +uint8_t RouterTable::GetPathCostToLeader(void) const +{ + return GetPathCost(Mle::Rloc16FromRouterId(Get().GetLeaderId())); +} + +void RouterTable::GetNextHopAndPathCost(uint16_t aDestRloc16, uint16_t &aNextHopRloc16, uint8_t &aPathCost) const +{ + uint8_t destRouterId; + const Router *router; + const Router *nextHop; + + aPathCost = Mle::kMaxRouteCost; + aNextHopRloc16 = Mle::kInvalidRloc16; + + VerifyOrExit(Get().IsAttached()); + + if (aDestRloc16 == Get().GetRloc16()) + { + // Destination is this device, return cost as zero. + aPathCost = 0; + aNextHopRloc16 = aDestRloc16; + ExitNow(); + } + + destRouterId = Mle::RouterIdFromRloc16(aDestRloc16); + + router = FindRouterById(destRouterId); + nextHop = (router != nullptr) ? FindNextHopOf(*router) : nullptr; + + if (Get().IsChild()) { - rval = aRouter.GetLinkQualityOut(); + const Router &parent = Get().GetParent(); + + if (parent.IsStateValid()) + { + aNextHopRloc16 = parent.GetRloc16(); + } + + // If destination is our parent or another child of our + // parent, we use the link cost to our parent. Otherwise we + // check if we have a next hop towards the destination and + // add its cost to the link cost to parent. + + VerifyOrExit((destRouterId == parent.GetRouterId()) || (nextHop != nullptr)); + + aPathCost = CostForLinkQuality(parent.GetLinkQualityIn()); + + if (destRouterId != parent.GetRouterId()) + { + aPathCost += router->GetCost(); + } + + // The case where destination itself is a child is handled at + // the end (after `else` block). } + else // Role is router or leader + { + if (destRouterId == Mle::RouterIdFromRloc16(Get().GetRloc16())) + { + // Destination is a one of our children. + + const Child *child = Get().FindChild(aDestRloc16, Child::kInStateAnyExceptInvalid); + + VerifyOrExit(child != nullptr); + aNextHopRloc16 = aDestRloc16; + aPathCost = CostForLinkQuality(child->GetLinkQualityIn()); + ExitNow(); + } - rval = Mle::MleRouter::LinkQualityToCost(rval); + VerifyOrExit(router != nullptr); + + aPathCost = GetLinkCost(*router); + + if (aPathCost < Mle::kMaxRouteCost) + { + aNextHopRloc16 = router->GetRloc16(); + } + + if (nextHop != nullptr) + { + // Determine whether direct link or forwarding hop link + // through `nextHop` has a lower path cost. + + uint8_t nextHopPathCost = router->GetCost() + GetLinkCost(*nextHop); + + if (nextHopPathCost < aPathCost) + { + aPathCost = nextHopPathCost; + aNextHopRloc16 = nextHop->GetRloc16(); + } + } + } + + if (!Mle::IsActiveRouter(aDestRloc16)) + { + // Destination is a child. we assume best link quality + // between destination and its parent router. + + aPathCost += kCostForLinkQuality3; + } exit: - return rval; + return; +} + +uint16_t RouterTable::GetNextHop(uint16_t aDestRloc16) const +{ + uint8_t pathCost; + uint16_t nextHopRloc16; + + GetNextHopAndPathCost(aDestRloc16, nextHopRloc16, pathCost); + + return nextHopRloc16; } void RouterTable::UpdateRouterIdSet(uint8_t aRouterIdSequence, const Mle::RouterIdSet &aRouterIdSet) { + bool shouldAdd = false; + mRouterIdSequence = aRouterIdSequence; mRouterIdSequenceLastUpdated = TimerMilli::GetNow(); - VerifyOrExit(mAllocatedRouterIds != aRouterIdSet); + // Remove all previously allocated routers that are now removed in + // new `aRouterIdSet`. for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) { - // If was allocated but removed in new Router Id Set - if (IsAllocated(routerId) && !aRouterIdSet.Contains(routerId)) + if (IsAllocated(routerId) == aRouterIdSet.Contains(routerId)) + { + continue; + } + + if (IsAllocated(routerId)) { - Router *router = GetRouter(routerId); + Router *router = FindRouterById(routerId); OT_ASSERT(router != nullptr); - router->SetNextHop(Mle::kInvalidRouterId); + router->SetNextHopToInvalid(); RemoveRouterLink(*router); + RemoveRouter(*router); + } + else + { + shouldAdd = true; + } + } + + VerifyOrExit(shouldAdd); + + // Now add all new routers in `aRouterIdSet`. - mAllocatedRouterIds.Remove(routerId); + for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) + { + if (!IsAllocated(routerId) && aRouterIdSet.Contains(routerId)) + { + AddRouter(routerId); } } - mAllocatedRouterIds = aRouterIdSet; - UpdateAllocation(); Get().ResetAdvertiseInterval(); exit: return; } -void RouterTable::HandleTimeTick(void) +void RouterTable::UpdateRoutes(const Mle::RouteTlv &aRouteTlv, uint8_t aNeighborId) { - Mle::MleRouter &mle = Get(); + Router *neighbor; + Mle::RouterIdSet finitePathCostIdSet; + uint8_t linkCostToNeighbor; + + neighbor = FindRouterById(aNeighborId); + VerifyOrExit(neighbor != nullptr); + + // Before updating the routes, we track which routers have finite + // path cost. After the update we check again to see if any path + // cost changed from finite to infinite or vice versa to decide + // whether to reset the MLE Advertisement interval. + + finitePathCostIdSet.Clear(); + + for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) + { + if (GetPathCost(Mle::Rloc16FromRouterId(routerId)) < Mle::kMaxRouteCost) + { + finitePathCostIdSet.Add(routerId); + } + } + + // Find the entry corresponding to our Router ID in the received + // `aRouteTlv` to get the `LinkQualityIn` from the perspective of + // neighbor. We use this to update our `LinkQualityOut` to the + // neighbor. - if (mle.IsLeader()) + for (uint8_t routerId = 0, index = 0; routerId <= Mle::kMaxRouterId; + index += aRouteTlv.IsRouterIdSet(routerId) ? 1 : 0, routerId++) { - // update router id sequence - if (GetLeaderAge() >= Mle::kRouterIdSequencePeriod) + if (routerId != Mle::RouterIdFromRloc16(Get().GetRloc16())) { - mRouterIdSequence++; - mRouterIdSequenceLastUpdated = TimerMilli::GetNow(); + continue; } - for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) + if (aRouteTlv.IsRouterIdSet(routerId)) { - if (mRouterIdReuseDelay[routerId] > 0) + LinkQuality linkQuality = aRouteTlv.GetLinkQualityIn(index); + + if (neighbor->GetLinkQualityOut() != linkQuality) { - mRouterIdReuseDelay[routerId]--; + neighbor->SetLinkQualityOut(linkQuality); + SignalTableChanged(); } } + + break; } + + linkCostToNeighbor = GetLinkCost(*neighbor); + + for (uint8_t routerId = 0, index = 0; routerId <= Mle::kMaxRouterId; + index += aRouteTlv.IsRouterIdSet(routerId) ? 1 : 0, routerId++) + { + Router *router; + Router *nextHop; + uint8_t cost; + + if (!aRouteTlv.IsRouterIdSet(routerId)) + { + continue; + } + + router = FindRouterById(routerId); + + if (router == nullptr || router->GetRloc16() == Get().GetRloc16() || router == neighbor) + { + continue; + } + + nextHop = FindNextHopOf(*router); + + cost = aRouteTlv.GetRouteCost(index); + cost = (cost == 0) ? Mle::kMaxRouteCost : cost; + + if ((nextHop == nullptr) || (nextHop == neighbor)) + { + // `router` has no next hop or next hop is neighbor (sender) + + if (cost + linkCostToNeighbor < Mle::kMaxRouteCost) + { + if (router->SetNextHopAndCost(aNeighborId, cost)) + { + SignalTableChanged(); + } + } + else if (nextHop == neighbor) + { + router->SetNextHopToInvalid(); + router->SetLastHeard(TimerMilli::GetNow()); + SignalTableChanged(); + } + } + else + { + uint8_t curCost = router->GetCost() + GetLinkCost(*nextHop); + uint8_t newCost = cost + linkCostToNeighbor; + + if (newCost < curCost) + { + router->SetNextHopAndCost(aNeighborId, cost); + SignalTableChanged(); + } + } + } + + for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) + { + bool oldCostFinite = finitePathCostIdSet.Contains(routerId); + bool newCostFinite = (GetPathCost(Mle::Rloc16FromRouterId(routerId)) < Mle::kMaxRouteCost); + + if (newCostFinite != oldCostFinite) + { + Get().ResetAdvertiseInterval(); + break; + } + } + +exit: + return; +} + +void RouterTable::UpdateRoutesOnFed(const Mle::RouteTlv &aRouteTlv, uint8_t aParentId) +{ + for (uint8_t routerId = 0, index = 0; routerId <= Mle::kMaxRouterId; + index += aRouteTlv.IsRouterIdSet(routerId) ? 1 : 0, routerId++) + { + Router *router; + uint8_t cost; + uint8_t nextHopId; + + if (!aRouteTlv.IsRouterIdSet(routerId) || (routerId == aParentId)) + { + continue; + } + + router = FindRouterById(routerId); + + if (router == nullptr) + { + continue; + } + + cost = aRouteTlv.GetRouteCost(index); + nextHopId = (cost == 0) ? Mle::kInvalidRouterId : aParentId; + + if (router->SetNextHopAndCost(nextHopId, cost)) + { + SignalTableChanged(); + } + } +} + +void RouterTable::FillRouteTlv(Mle::RouteTlv &aRouteTlv, const Neighbor *aNeighbor) const +{ + uint8_t routerIdSequence = mRouterIdSequence; + Mle::RouterIdSet routerIdSet; + uint8_t routerIndex; + + mRouterIdMap.GetAsRouterIdSet(routerIdSet); + + if ((aNeighbor != nullptr) && Mle::IsActiveRouter(aNeighbor->GetRloc16())) + { + // Sending a Link Accept message that may require truncation + // of Route64 TLV. + + uint8_t routerCount = mRouters.GetLength(); + + if (routerCount > Mle::kLinkAcceptMaxRouters) + { + for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) + { + if (routerCount <= Mle::kLinkAcceptMaxRouters) + { + break; + } + + if ((routerId == Mle::RouterIdFromRloc16(Get().GetRloc16())) || + (routerId == aNeighbor->GetRouterId()) || (routerId == Get().GetLeaderId())) + { + // Route64 TLV must contain this device and the + // neighboring router to ensure that at least this + // link can be established. + continue; + } + + if (routerIdSet.Contains(routerId)) + { + routerIdSet.Remove(routerId); + routerCount--; + } + } + + // Ensure that the neighbor will process the current + // Route64 TLV in a subsequent message exchange + routerIdSequence -= Mle::kLinkAcceptSequenceRollback; + } + } + + aRouteTlv.SetRouterIdSequence(routerIdSequence); + aRouteTlv.SetRouterIdMask(routerIdSet); + + routerIndex = 0; + + for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) + { + uint16_t routerRloc16; + + if (!routerIdSet.Contains(routerId)) + { + continue; + } + + routerRloc16 = Mle::Rloc16FromRouterId(routerId); + + if (routerRloc16 == Get().GetRloc16()) + { + aRouteTlv.SetRouteData(routerIndex, kLinkQuality0, kLinkQuality0, 1); + } + else + { + const Router *router = FindRouterById(routerId); + uint8_t pathCost; + + OT_ASSERT(router != nullptr); + + pathCost = GetPathCost(routerRloc16); + + if (pathCost >= Mle::kMaxRouteCost) + { + pathCost = 0; + } + + aRouteTlv.SetRouteData(routerIndex, router->GetLinkQualityIn(), router->GetLinkQualityOut(), pathCost); + } + + routerIndex++; + } + + aRouteTlv.SetRouteDataLength(routerIndex); +} + +void RouterTable::HandleTimeTick(void) +{ + mRouterIdMap.HandleTimeTick(); + + VerifyOrExit(Get().IsLeader()); + + // Update router id sequence + if (GetLeaderAge() >= Mle::kRouterIdSequencePeriod) + { + mRouterIdSequence++; + mRouterIdSequenceLastUpdated = TimerMilli::GetNow(); + } + +exit: + return; } #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE @@ -567,6 +843,91 @@ Error RouterTable::SetRouterIdRange(uint8_t aMinRouterId, uint8_t aMaxRouterId) } #endif +void RouterTable::RouterIdMap::GetAsRouterIdSet(Mle::RouterIdSet &aRouterIdSet) const +{ + aRouterIdSet.Clear(); + + for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) + { + if (IsAllocated(routerId)) + { + aRouterIdSet.Add(routerId); + } + } +} + +void RouterTable::RouterIdMap::HandleTimeTick(void) +{ + for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) + { + // If Router ID is not allocated the `mIndexes` tracks the + // remaining reuse delay time in seconds. + + if (!IsAllocated(routerId) && (mIndexes[routerId] > 0)) + { + mIndexes[routerId]--; + } + } +} + +void RouterTable::SignalTableChanged(void) { mChangedTask.Post(); } + +void RouterTable::HandleTableChanged(void) +{ +#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) + LogRouteTable(); +#endif + +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ENABLE + Get().RecordRouterTableChange(); +#endif +} + +#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) +void RouterTable::LogRouteTable(void) const +{ + static constexpr uint16_t kStringSize = 128; + + LogInfo("Route table"); + + for (const Router &router : mRouters) + { + String string; + + string.Append(" %2d 0x%04x", router.GetRouterId(), router.GetRloc16()); + + if (router.GetRloc16() == Get().GetRloc16()) + { + string.Append(" - me"); + } + else if (Get().IsChild() && (router.GetRloc16() == Get().GetParent().GetRloc16())) + { + string.Append(" - parent"); + } + else + { + if (router.IsStateValid()) + { + string.Append(" - nbr{lq[i/o]:%d/%d cost:%d}", router.GetLinkQualityIn(), router.GetLinkQualityOut(), + GetLinkCost(router)); + } + + if (router.GetNextHop() != Mle::kInvalidRouterId) + { + string.Append(" - nexthop{%d cost:%d}", router.GetNextHop(), router.GetCost()); + } + } + + if (router.GetRouterId() == Get().GetLeaderId()) + { + string.Append(" - leader"); + } + + LogInfo("%s", string.AsCString()); + } +} +#endif + } // namespace ot #endif // OPENTHREAD_FTD diff --git a/src/core/thread/router_table.hpp b/src/core/thread/router_table.hpp index 7fa65495afa..e87ed0dbcb3 100644 --- a/src/core/thread/router_table.hpp +++ b/src/core/thread/router_table.hpp @@ -33,12 +33,16 @@ #if OPENTHREAD_FTD +#include "common/array.hpp" #include "common/const_cast.hpp" #include "common/encoding.hpp" #include "common/iterator_utils.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" +#include "common/serial_number.hpp" +#include "common/tasklet.hpp" #include "mac/mac_types.hpp" +#include "thread/mle_tlvs.hpp" #include "thread/mle_types.hpp" #include "thread/thread_tlvs.hpp" #include "thread/topology.hpp" @@ -48,41 +52,8 @@ namespace ot { class RouterTable : public InstanceLocator, private NonCopyable { friend class NeighborTable; - class IteratorBuilder; public: - /** - * This class represents an iterator for iterating through entries in the router table. - * - */ - class Iterator : public InstanceLocator, public ItemPtrIterator - { - friend class ItemPtrIterator; - friend class IteratorBuilder; - - public: - /** - * This constructor initializes an `Iterator` instance to start from beginning of the router table. - * - * @param[in] aInstance A reference to the OpenThread instance. - * - */ - explicit Iterator(Instance &aInstance); - - private: - enum IteratorType : uint8_t - { - kEndIterator, - }; - - Iterator(Instance &aInstance, IteratorType) - : InstanceLocator(aInstance) - { - } - - void Advance(void); - }; - /** * Constructor. * @@ -104,29 +75,31 @@ class RouterTable : public InstanceLocator, private NonCopyable void ClearNeighbors(void); /** - * This method allocates a router with a random router id. + * This method allocates a router with a random Router ID. * - * @returns A pointer to the allocated router or `nullptr` if a router ID is not available. + * @returns A pointer to the allocated router or `nullptr` if a Router ID is not available. * */ Router *Allocate(void); /** - * This method allocates a router with a specified router id. + * This method allocates a router with a specified Router ID. + * + * @param[in] aRouterId The Router ID to try to allocate. * - * @returns A pointer to the allocated router or `nullptr` if the router id could not be allocated. + * @returns A pointer to the allocated router or `nullptr` if the ID @p aRouterId could not be allocated. * */ Router *Allocate(uint8_t aRouterId); /** - * This method releases a router id. + * This method releases a Router ID. * - * @param[in] aRouterId The router id. + * @param[in] aRouterId The Router ID. * - * @retval kErrorNone Successfully released the router id. + * @retval kErrorNone Successfully released the Router ID @p aRouterId. * @retval kErrorInvalidState The device is not currently operating as a leader. - * @retval kErrorNotFound The router id is not currently allocated. + * @retval kErrorNotFound The Router ID @p aRouterId is not currently allocated. * */ Error Release(uint8_t aRouterId); @@ -145,15 +118,15 @@ class RouterTable : public InstanceLocator, private NonCopyable * @returns The number of active routers in the Thread network. * */ - uint8_t GetActiveRouterCount(void) const { return mActiveRouterCount; } + uint8_t GetActiveRouterCount(void) const { return mRouters.GetLength(); } /** - * This method returns the number of active links with neighboring routers. + * This method returns the leader in the Thread network. * - * @returns The number of active links with neighboring routers. + * @returns A pointer to the Leader in the Thread network. * */ - uint8_t GetActiveLinkCount(void) const; + Router *GetLeader(void) { return AsNonConst(AsConst(this)->GetLeader()); } /** * This method returns the leader in the Thread network. @@ -161,12 +134,12 @@ class RouterTable : public InstanceLocator, private NonCopyable * @returns A pointer to the Leader in the Thread network. * */ - Router *GetLeader(void); + const Router *GetLeader(void) const; /** - * This method returns the time in seconds since the last Router ID Sequence update. + * This method returns the leader's age in seconds, i.e., seconds since the last Router ID Sequence update. * - * @returns The time in seconds since the last Router ID Sequence update. + * @returns The leader's age. * */ uint32_t GetLeaderAge(void) const; @@ -174,87 +147,144 @@ class RouterTable : public InstanceLocator, private NonCopyable /** * This method returns the link cost for a neighboring router. * - * @param[in] aRouter A reference to the router. + * @param[in] aRouter A router. + * + * @returns The link cost to @p aRouter. * - * @returns The link cost. + */ + uint8_t GetLinkCost(const Router &aRouter) const; + + /** + * This method returns the link cost to the given Router. + * + * @param[in] aRouterId The Router ID. + * + * @returns The link cost to the Router. + * + */ + uint8_t GetLinkCost(uint8_t aRouterId) const; + + /** + * This method returns the minimum mesh path cost to the given RLOC16 + * + * @param[in] aDestRloc16 The RLOC16 of destination + * + * @returns The minimum mesh path cost to @p aDestRloc16 (via direct link or forwarding). + * + */ + uint8_t GetPathCost(uint16_t aDestRloc16) const; + + /** + * This method returns the mesh path cost to leader. + * + * @returns The path cost to leader. + * + */ + uint8_t GetPathCostToLeader(void) const; + + /** + * This method determines the next hop towards an RLOC16 destination. + * + * @param[in] aDestRloc16 The RLOC16 of the destination. + * + * @returns A RLOC16 of the next hop if a route is known, `Mle::kInvalidRloc16` otherwise. + * + */ + uint16_t GetNextHop(uint16_t aDestRloc16) const; + + /** + * This method determines the next hop and the path cost towards an RLOC16 destination. + * + * @param[in] aDestRloc16 The RLOC16 of the destination. + * @param[out] aNextHopRloc16 A reference to return the RLOC16 of next hop if known, or `Mle::kInvalidRloc16`. + * @param[out] aPathCost A reference to return the path cost. + * + */ + void GetNextHopAndPathCost(uint16_t aDestRloc16, uint16_t &aNextHopRloc16, uint8_t &aPathCost) const; + + /** + * This method finds the router for a given Router ID. + * + * @param[in] aRouterId The Router ID to search for. + * + * @returns A pointer to the router or `nullptr` if the router could not be found. * */ - uint8_t GetLinkCost(Router &aRouter); + Router *FindRouterById(uint8_t aRouterId) { return AsNonConst(AsConst(this)->FindRouterById(aRouterId)); } /** - * This method returns the neighbor for a given RLOC16. + * This method finds the router for a given Router ID. * - * @param[in] aRloc16 The RLOC16 value. + * @param[in] aRouterId The Router ID to search for. * * @returns A pointer to the router or `nullptr` if the router could not be found. * */ - Router *GetNeighbor(uint16_t aRloc16); + const Router *FindRouterById(uint8_t aRouterId) const; /** - * This method returns the neighbor for a given IEEE Extended Address. + * This method finds the router for a given RLOC16. * - * @param[in] aExtAddress A reference to the IEEE Extended Address. + * @param[in] aRloc16 The RLOC16 to search for. * * @returns A pointer to the router or `nullptr` if the router could not be found. * */ - Router *GetNeighbor(const Mac::ExtAddress &aExtAddress); + Router *FindRouterByRloc16(uint16_t aRloc16) { return AsNonConst(AsConst(this)->FindRouterByRloc16(aRloc16)); } /** - * This method returns the neighbor for a given MAC address. + * This method finds the router for a given RLOC16. * - * @param[in] aMacAddress A MAC address + * @param[in] aRloc16 The RLOC16 to search for. * * @returns A pointer to the router or `nullptr` if the router could not be found. * */ - Router *GetNeighbor(const Mac::Address &aMacAddress); + const Router *FindRouterByRloc16(uint16_t aRloc16) const; /** - * This method returns the router for a given router id. + * This method finds the router that is the next hop of a given router. * - * @param[in] aRouterId The router id. + * @param[in] aRouter The router to find next hop of. * * @returns A pointer to the router or `nullptr` if the router could not be found. * */ - Router *GetRouter(uint8_t aRouterId) { return AsNonConst(AsConst(this)->GetRouter(aRouterId)); } + Router *FindNextHopOf(const Router &aRouter) { return AsNonConst(AsConst(this)->FindNextHopOf(aRouter)); } /** - * This method returns the router for a given router id. + * This method finds the router that is the next hop of a given router. * - * @param[in] aRouterId The router id. + * @param[in] aRouter The router to find next hop of. * * @returns A pointer to the router or `nullptr` if the router could not be found. * */ - const Router *GetRouter(uint8_t aRouterId) const; + const Router *FindNextHopOf(const Router &aRouter) const; /** - * This method returns the router for a given IEEE Extended Address. + * This method find the router for a given MAC Extended Address. * - * @param[in] aExtAddress A reference to the IEEE Extended Address. + * @param[in] aExtAddress A reference to the MAC Extended Address. * * @returns A pointer to the router or `nullptr` if the router could not be found. * */ - Router *GetRouter(const Mac::ExtAddress &aExtAddress); + Router *FindRouter(const Mac::ExtAddress &aExtAddress); /** - * This method returns if the router table contains a given `Neighbor` instance. + * This method indicates whether the router table contains a given `Neighbor` instance. * * @param[in] aNeighbor A reference to a `Neighbor`. * * @retval TRUE if @p aNeighbor is a `Router` in the router table. * @retval FALSE if @p aNeighbor is not a `Router` in the router table - * (i.e. mParent, mParentCandidate, a `Child` of the child table). + * (i.e. it can be the parent or parent candidate, or a `Child` of the child table). * */ bool Contains(const Neighbor &aNeighbor) const { - return mRouters <= &static_cast(aNeighbor) && - &static_cast(aNeighbor) < mRouters + Mle::kMaxRouters; + return mRouters.IsInArrayBuffer(&static_cast(aNeighbor)); } /** @@ -286,6 +316,18 @@ class RouterTable : public InstanceLocator, private NonCopyable */ TimeMilli GetRouterIdSequenceLastUpdated(void) const { return mRouterIdSequenceLastUpdated; } + /** + * This method determines whether the Router ID Sequence in a received Route TLV is more recent than the current + * Router ID Sequence being used by `RouterTable`. + * + * @param[in] aRouteTlv The Route TLV to compare. + * + * @retval TRUE The Router ID Sequence in @p aRouteTlv is more recent. + * @retval FALSE The Router ID Sequence in @p aRouteTlv is not more recent. + * + */ + bool IsRouteTlvIdSequenceMoreRecent(const Mle::RouteTlv &aRouteTlv) const; + /** * This method returns the number of neighbor links. * @@ -295,30 +337,65 @@ class RouterTable : public InstanceLocator, private NonCopyable uint8_t GetNeighborCount(void) const; /** - * This method indicates whether or not @p aRouterId is allocated. + * This method indicates whether or not a Router ID is allocated. + * + * @param[in] aRouterId The Router ID. * - * @retval TRUE if @p aRouterId is allocated. + * @retval TRUE if @p aRouterId is allocated. * @retval FALSE if @p aRouterId is not allocated. * */ - bool IsAllocated(uint8_t aRouterId) const; + bool IsAllocated(uint8_t aRouterId) const { return mRouterIdMap.IsAllocated(aRouterId); } /** - * This method updates the Router ID allocation. + * This method updates the Router ID allocation set. * - * @param[in] aRouterIdSequence The Router Id Sequence. - * @param[in] aRouterIdSet A reference to the Router Id Set. + * @param[in] aRouterIdSequence The Router ID Sequence. + * @param[in] aRouterIdSet The Router ID Set. * */ void UpdateRouterIdSet(uint8_t aRouterIdSequence, const Mle::RouterIdSet &aRouterIdSet); + /** + * This method updates the routes based on a received `RouteTlv` from a neighboring router. + * + * @param[in] aRouteTlv The received `RouteTlv` + * @param[in] aNeighborId The router ID of neighboring router from which @p aRouteTlv is received. + * + */ + void UpdateRoutes(const Mle::RouteTlv &aRouteTlv, uint8_t aNeighborId); + + /** + * This method updates the routes on an FED based on a received `RouteTlv` from the parent. + * + * This method MUST be called when device is an FED child and @p aRouteTlv is received from its current parent. + * + * @param[in] aRouteTlv The received `RouteTlv` from parent. + * @param[in] aParentId The Router ID of parent. + * + */ + void UpdateRoutesOnFed(const Mle::RouteTlv &aRouteTlv, uint8_t aParentId); + /** * This method gets the allocated Router ID set. * * @returns The allocated Router ID set. * */ - const Mle::RouterIdSet &GetRouterIdSet(void) const { return mAllocatedRouterIds; } + void GetRouterIdSet(Mle::RouterIdSet &aRouterIdSet) const { return mRouterIdMap.GetAsRouterIdSet(aRouterIdSet); } + + /** + * This method fills a Route TLV. + * + * When @p aNeighbor is not `nullptr`, we limit the number of router entries to `Mle::kLinkAcceptMaxRouters` when + * populating `aRouteTlv`, so that the TLV can be appended in a Link Accept message. In this case, we ensure to + * include router entries associated with @p aNeighbor, leader, and this device itself. + * + * @param[out] aRouteTlv A Route TLV to be filled. + * @param[in] aNeighbor A pointer to the receiver (in case TLV is for a Link Accept message). + * + */ + void FillRouteTlv(Mle::RouteTlv &aRouteTlv, const Neighbor *aNeighbor = nullptr) const; /** * This method updates the router table and must be called with a one second period. @@ -326,55 +403,96 @@ class RouterTable : public InstanceLocator, private NonCopyable */ void HandleTimeTick(void); +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE /** - * This method enables range-based `for` loop iteration over all Router entries in the Router table. - * - * This method should be used as follows: + * This method gets the range of Router IDs. * - * for (Router &router : Get().Iterate()) { ... } + * All the Router IDs in the range `[aMinRouterId, aMaxRouterId]` are allowed. This is intended for testing only. * - * @returns An `IteratorBuilder` instance. + * @param[out] aMinRouterId Reference to return the minimum Router ID. + * @param[out] aMaxRouterId Reference to return the maximum Router ID. * */ - IteratorBuilder Iterate(void) { return IteratorBuilder(GetInstance()); } - -#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE void GetRouterIdRange(uint8_t &aMinRouterId, uint8_t &aMaxRouterId) const; + /** + * This method sets the range of Router IDs. + * + * All the Router IDs in the range `[aMinRouterId, aMaxRouterId]` are allowed. This is intended for testing only. + * + * @param[in] aMinRouterId The minimum Router ID. + * @param[in] aMaxRouterId The maximum Router ID. + * + * @retval kErrorNone Successfully set the Router ID range. + * @retval kErrorInvalidArgs The given range is not valid. + * + */ Error SetRouterIdRange(uint8_t aMinRouterId, uint8_t aMaxRouterId); #endif -private: - class IteratorBuilder : public InstanceLocator - { - public: - explicit IteratorBuilder(Instance &aInstance) - : InstanceLocator(aInstance) - { - } - - Iterator begin(void) { return Iterator(GetInstance()); } - Iterator end(void) { return Iterator(GetInstance(), Iterator::kEndIterator); } - }; + // The following methods are intended to support range-based `for` + // loop iteration over the router and should not be used + // directly. - void UpdateAllocation(void); - const Router *GetFirstEntry(void) const; - const Router *GetNextEntry(const Router *aRouter) const; - Router * GetFirstEntry(void) { return AsNonConst(AsConst(this)->GetFirstEntry()); } - Router * GetNextEntry(Router *aRouter) { return AsNonConst(AsConst(this)->GetNextEntry(aRouter)); } + Router *begin(void) { return mRouters.begin(); } + Router *end(void) { return mRouters.end(); } + const Router *begin(void) const { return mRouters.begin(); } + const Router *end(void) const { return mRouters.end(); } +private: + Router *AddRouter(uint8_t aRouterId); + void RemoveRouter(Router &aRouter); + Router *FindNeighbor(uint16_t aRloc16); + Router *FindNeighbor(const Mac::ExtAddress &aExtAddress); + Router *FindNeighbor(const Mac::Address &aMacAddress); const Router *FindRouter(const Router::AddressMatcher &aMatcher) const; - Router * FindRouter(const Router::AddressMatcher &aMatcher) + Router *FindRouter(const Router::AddressMatcher &aMatcher) { return AsNonConst(AsConst(this)->FindRouter(aMatcher)); } - Router mRouters[Mle::kMaxRouters]; - Mle::RouterIdSet mAllocatedRouterIds; - uint8_t mRouterIdReuseDelay[Mle::kMaxRouterId + 1]; - TimeMilli mRouterIdSequenceLastUpdated; - uint8_t mRouterIdSequence; - uint8_t mActiveRouterCount; + void SignalTableChanged(void); + void HandleTableChanged(void); + void LogRouteTable(void) const; + + class RouterIdMap + { + public: + // The `RouterIdMap` tracks which Router IDs are allocated. + // For allocated IDs, tracks the index of the `Router` entry + // in `mRouters` array. For unallocated IDs, tracks the + // remaining reuse delay time (in seconds). + + RouterIdMap(void) { Clear(); } + void Clear(void) { memset(mIndexes, 0, sizeof(mIndexes)); } + bool IsAllocated(uint8_t aRouterId) const { return (mIndexes[aRouterId] & kAllocatedFlag); } + uint8_t GetIndex(uint8_t aRouterId) const { return (mIndexes[aRouterId] & kIndexMask); } + void SetIndex(uint8_t aRouterId, uint8_t aIndex) { mIndexes[aRouterId] = kAllocatedFlag | aIndex; } + bool CanAllocate(uint8_t aRouterId) const { return (mIndexes[aRouterId] == 0); } + void Release(uint8_t aRouterId) { mIndexes[aRouterId] = Mle::kRouterIdReuseDelay; } + void GetAsRouterIdSet(Mle::RouterIdSet &aRouterIdSet) const; + void HandleTimeTick(void); + + private: + // The high bit in `mIndexes[aRouterId]` indicates whether or + // not the router ID is allocated. The lower 7 bits give either + // the index in `mRouter` array or remaining reuse delay time. + + static constexpr uint8_t kAllocatedFlag = 1 << 7; + static constexpr uint8_t kIndexMask = 0x7f; + + static_assert(Mle::kRouterIdReuseDelay <= kIndexMask, "Mle::kRouterIdReuseDelay does not fit in 7 bits"); + + uint8_t mIndexes[Mle::kMaxRouterId + 1]; + }; + + using ChangedTask = TaskletIn; + + Array mRouters; + ChangedTask mChangedTask; + RouterIdMap mRouterIdMap; + TimeMilli mRouterIdSequenceLastUpdated; + uint8_t mRouterIdSequence; #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE uint8_t mMinRouterId; uint8_t mMaxRouterId; diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp index 3866d0d3ce3..e01d1921844 100644 --- a/src/core/thread/thread_netif.cpp +++ b/src/core/thread/thread_netif.cpp @@ -98,7 +98,7 @@ void ThreadNetif::Down(void) Get().Stop(); #endif #if OPENTHREAD_CONFIG_DTLS_ENABLE - Get().Stop(); + Get().Stop(); #endif IgnoreError(Get().Stop()); IgnoreError(Get().Disable()); @@ -118,30 +118,4 @@ void ThreadNetif::Down(void) return; } -Error ThreadNetif::SendMessage(Message &aMessage) -{ - return Get().SendMessage(aMessage); -} - -Error ThreadNetif::RouteLookup(const Ip6::Address &aSource, const Ip6::Address &aDestination, uint8_t *aPrefixMatch) -{ - Error error; - uint16_t rloc; - - SuccessOrExit(error = Get().RouteLookup(aSource, aDestination, aPrefixMatch, &rloc)); - - if (rloc == Get().GetRloc16()) - { - error = kErrorNoRoute; - } - -exit: - return error; -} - -bool ThreadNetif::IsOnMesh(const Ip6::Address &aAddress) const -{ - return Get().IsOnMesh(aAddress); -} - } // namespace ot diff --git a/src/core/thread/thread_netif.hpp b/src/core/thread/thread_netif.hpp index e06e6b678f1..af776bee299 100644 --- a/src/core/thread/thread_netif.hpp +++ b/src/core/thread/thread_netif.hpp @@ -82,40 +82,6 @@ class ThreadNetif : public Ip6::Netif */ bool IsUp(void) const { return mIsUp; } - /** - * This method submits a message to the network interface. - * - * @param[in] aMessage A reference to the message. - * - * @retval kErrorNone Successfully submitted the message to the interface. - * - */ - Error SendMessage(Message &aMessage); - - /** - * This method performs a route lookup. - * - * @param[in] aSource A reference to the IPv6 source address. - * @param[in] aDestination A reference to the IPv6 destination address. - * @param[out] aPrefixMatch A pointer where the number of prefix match bits for the chosen route is stored. - * - * @retval kErrorNone Successfully found a route. - * @retval kErrorNoRoute Could not find a valid route. - * - */ - Error RouteLookup(const Ip6::Address &aSource, const Ip6::Address &aDestination, uint8_t *aPrefixMatch); - - /** - * This method indicates whether @p aAddress matches an on-mesh prefix. - * - * @param[in] aAddress The IPv6 address. - * - * @retval TRUE If @p aAddress matches an on-mesh prefix. - * @retval FALSE If @p aAddress does not match an on-mesh prefix. - * - */ - bool IsOnMesh(const Ip6::Address &aAddress) const; - private: bool mIsUp; }; diff --git a/src/core/thread/thread_tlvs.hpp b/src/core/thread/thread_tlvs.hpp index f092c5f3d13..4527aac39e7 100644 --- a/src/core/thread/thread_tlvs.hpp +++ b/src/core/thread/thread_tlvs.hpp @@ -39,6 +39,7 @@ #include "common/encoding.hpp" #include "common/message.hpp" #include "common/tlvs.hpp" +#include "meshcop/network_name.hpp" #include "net/ip6_address.hpp" #include "thread/mle.hpp" #include "thread/mle_types.hpp" @@ -136,7 +137,7 @@ typedef UintTlvInfo ThreadTimeoutTlv; * This class defines Network Name TLV constants and types. * */ -typedef TlvInfo ThreadNetworkNameTlv; +typedef StringTlvInfo ThreadNetworkNameTlv; /** * This class defines Commissioner Session ID TLV constants and types. @@ -248,6 +249,14 @@ class ThreadRouterMaskTlv : public ThreadTlv, public TlvInfo(static_cast(otPlatTimeGet()) + mNetworkTimeOffset); @@ -140,13 +138,7 @@ void TimeSync::IncrementTimeSyncSeq(void) } } -void TimeSync::NotifyTimeSyncCallback(void) -{ - if (mTimeSyncCallback != nullptr) - { - mTimeSyncCallback(mTimeSyncCallbackContext); - } -} +void TimeSync::NotifyTimeSyncCallback(void) { mTimeSyncCallback.InvokeIfSet(); } #if OPENTHREAD_FTD void TimeSync::ProcessTimeSync(void) @@ -189,7 +181,7 @@ void TimeSync::HandleNotifierEvents(Events aEvents) mTimeSyncSeq = OT_TIME_SYNC_INVALID_SEQ; mTimeSyncRequired = false; - // Network time status will become OT_NETWORK_TIME_UNSYNCHRONIZED because no network time has yet been received + // Network time status will become kUnsychronized because no network time has yet been received // on the new partition. mLastTimeSyncReceived.SetValue(0); @@ -204,21 +196,13 @@ void TimeSync::HandleNotifierEvents(Events aEvents) } } -void TimeSync::HandleTimeout(void) -{ - CheckAndHandleChanges(false); -} - -void TimeSync::HandleTimeout(Timer &aTimer) -{ - aTimer.Get().HandleTimeout(); -} +void TimeSync::HandleTimeout(void) { CheckAndHandleChanges(false); } void TimeSync::CheckAndHandleChanges(bool aTimeUpdated) { - otNetworkTimeStatus networkTimeStatus = OT_NETWORK_TIME_SYNCHRONIZED; - const uint32_t resyncNeededThresholdMs = 2 * Time::SecToMsec(mTimeSyncPeriod); - const uint32_t timeSyncLastSyncMs = TimerMilli::GetNow() - mLastTimeSyncReceived; + Status networkTimeStatus = kSynchronized; + const uint32_t resyncNeededThresholdMs = 2 * Time::SecToMsec(mTimeSyncPeriod); + const uint32_t timeSyncLastSyncMs = TimerMilli::GetNow() - mLastTimeSyncReceived; mTimer.Stop(); @@ -226,7 +210,7 @@ void TimeSync::CheckAndHandleChanges(bool aTimeUpdated) { case Mle::kRoleDisabled: case Mle::kRoleDetached: - networkTimeStatus = OT_NETWORK_TIME_UNSYNCHRONIZED; + networkTimeStatus = kUnsynchronized; LogInfo("Time sync status UNSYNCHRONIZED as role:DISABLED/DETACHED"); break; @@ -235,15 +219,15 @@ void TimeSync::CheckAndHandleChanges(bool aTimeUpdated) if (mLastTimeSyncReceived.GetValue() == 0) { // Haven't yet received any time sync - networkTimeStatus = OT_NETWORK_TIME_UNSYNCHRONIZED; + networkTimeStatus = kUnsynchronized; LogInfo("Time sync status UNSYNCHRONIZED as mLastTimeSyncReceived:0"); } else if (timeSyncLastSyncMs > resyncNeededThresholdMs) { // The device hasn’t received time sync for more than two periods time. - networkTimeStatus = OT_NETWORK_TIME_RESYNC_NEEDED; - LogInfo("Time sync status RESYNC_NEEDED as timeSyncLastSyncMs:%u > resyncNeededThresholdMs:%u", - timeSyncLastSyncMs, resyncNeededThresholdMs); + networkTimeStatus = kResyncNeeded; + LogInfo("Time sync status RESYNC_NEEDED as timeSyncLastSyncMs:%lu > resyncNeededThresholdMs:%lu", + ToUlong(timeSyncLastSyncMs), ToUlong(resyncNeededThresholdMs)); } else { diff --git a/src/core/thread/time_sync_service.hpp b/src/core/thread/time_sync_service.hpp index 4f2e1444941..d0ad1acf852 100644 --- a/src/core/thread/time_sync_service.hpp +++ b/src/core/thread/time_sync_service.hpp @@ -40,6 +40,8 @@ #include +#include "common/as_core_type.hpp" +#include "common/callback.hpp" #include "common/locator.hpp" #include "common/message.hpp" #include "common/non_copyable.hpp" @@ -57,6 +59,17 @@ class TimeSync : public InstanceLocator, private NonCopyable friend class ot::Notifier; public: + /** + * This enumeration represents Network Time Status + * + */ + enum Status : int8_t + { + kUnsynchronized = OT_NETWORK_TIME_UNSYNCHRONIZED, ///< The device hasn't attached to a network. + kResyncNeeded = OT_NETWORK_TIME_RESYNC_NEEDED, ///< The device hasn’t received time sync for 2 periods. + kSynchronized = OT_NETWORK_TIME_SYNCHRONIZED, ///< The device network time is synchronized. + }; + /** * This constructor initializes the object. * @@ -71,7 +84,7 @@ class TimeSync : public InstanceLocator, private NonCopyable * @returns The time synchronization status. * */ - otNetworkTimeStatus GetTime(uint64_t &aNetworkTime) const; + Status GetTime(uint64_t &aNetworkTime) const; /** * Handle the message which includes time synchronization information. @@ -151,8 +164,7 @@ class TimeSync : public InstanceLocator, private NonCopyable */ void SetTimeSyncCallback(otNetworkTimeSyncCallbackFn aCallback, void *aCallbackContext) { - mTimeSyncCallback = aCallback; - mTimeSyncCallbackContext = aCallbackContext; + mTimeSyncCallback.Set(aCallback, aCallbackContext); } /** @@ -170,14 +182,6 @@ class TimeSync : public InstanceLocator, private NonCopyable */ void HandleNotifierEvents(Events aEvents); - /** - * Callback to be called when timer expires. - * - * @param[in] aTimer The corresponding timer. - * - */ - static void HandleTimeout(Timer &aTimer); - /** * Check and handle any status change, and notify observers if applicable. * @@ -199,6 +203,8 @@ class TimeSync : public InstanceLocator, private NonCopyable */ void NotifyTimeSyncCallback(void); + using SyncTimer = TimerMilliIn; + bool mTimeSyncRequired; ///< Indicate whether or not a time synchronization message is required. uint8_t mTimeSyncSeq; ///< The time synchronization sequence. uint16_t mTimeSyncPeriod; ///< The time synchronization period. @@ -208,13 +214,14 @@ class TimeSync : public InstanceLocator, private NonCopyable #endif TimeMilli mLastTimeSyncReceived; ///< The time when the last time synchronization message was received. int64_t mNetworkTimeOffset; ///< The time offset to the Thread Network time - otNetworkTimeSyncCallbackFn - mTimeSyncCallback; ///< The callback to be called when time sync is handled or status updated. - void * mTimeSyncCallbackContext; ///< The context to be passed to callback. - TimerMilli mTimer; ///< Timer for checking if a resync is required. - otNetworkTimeStatus mCurrentStatus; ///< Current network time status. + + Callback mTimeSyncCallback; ///< Callback when time sync is handled or status updated. + SyncTimer mTimer; ///< Timer for checking if a resync is required. + Status mCurrentStatus; ///< Current network time status. }; +DefineMapEnum(otNetworkTimeStatus, TimeSync::Status); + /** * @} */ diff --git a/src/core/thread/tmf.cpp b/src/core/thread/tmf.cpp index 24a4ae9a82e..583f6e29d7e 100644 --- a/src/core/thread/tmf.cpp +++ b/src/core/thread/tmf.cpp @@ -34,6 +34,7 @@ #include "thread/tmf.hpp" #include "common/locator_getters.hpp" +#include "net/ip6_types.hpp" namespace ot { namespace Tmf { @@ -41,10 +42,7 @@ namespace Tmf { //---------------------------------------------------------------------------------------------------------------------- // MessageInfo -void MessageInfo::SetSockAddrToRloc(void) -{ - SetSockAddr(Get().GetMeshLocal16()); -} +void MessageInfo::SetSockAddrToRloc(void) { SetSockAddr(Get().GetMeshLocal16()); } Error MessageInfo::SetSockAddrToRlocPeerAddrToLeaderAloc(void) { @@ -80,9 +78,118 @@ void MessageInfo::SetSockAddrToRlocPeerAddrTo(const Ip6::Address &aPeerAddress) //---------------------------------------------------------------------------------------------------------------------- // Agent -Error Agent::Start(void) +Agent::Agent(Instance &aInstance) + : Coap::Coap(aInstance) { - return Coap::Start(kUdpPort, OT_NETIF_THREAD); + SetInterceptor(&Filter, this); + SetResourceHandler(&HandleResource); +} + +Error Agent::Start(void) { return Coap::Start(kUdpPort, Ip6::kNetifThread); } + +template <> void Agent::HandleTmf(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + OT_UNUSED_VARIABLE(aMessage); + OT_UNUSED_VARIABLE(aMessageInfo); + +#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE) + Get().HandleTmf(aMessage, aMessageInfo); +#endif +#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE + Get().HandleTmf(aMessage, aMessageInfo); +#endif +} + +bool Agent::HandleResource(CoapBase &aCoapBase, + const char *aUriPath, + Message &aMessage, + const Ip6::MessageInfo &aMessageInfo) +{ + return static_cast(aCoapBase).HandleResource(aUriPath, aMessage, aMessageInfo); +} + +bool Agent::HandleResource(const char *aUriPath, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + bool didHandle = true; + Uri uri = UriFromPath(aUriPath); + +#define Case(kUri, Type) \ + case kUri: \ + Get().HandleTmf(aMessage, aMessageInfo); \ + break + + switch (uri) + { + Case(kUriAddressError, AddressResolver); + Case(kUriEnergyScan, EnergyScanServer); + Case(kUriActiveGet, MeshCoP::ActiveDatasetManager); + Case(kUriPendingGet, MeshCoP::PendingDatasetManager); + Case(kUriPanIdQuery, PanIdQueryServer); + +#if OPENTHREAD_FTD + Case(kUriAddressQuery, AddressResolver); + Case(kUriAddressNotify, AddressResolver); + Case(kUriAddressSolicit, Mle::MleRouter); + Case(kUriAddressRelease, Mle::MleRouter); + Case(kUriActiveSet, MeshCoP::ActiveDatasetManager); + Case(kUriPendingSet, MeshCoP::PendingDatasetManager); + Case(kUriLeaderPetition, MeshCoP::Leader); + Case(kUriLeaderKeepAlive, MeshCoP::Leader); + Case(kUriServerData, NetworkData::Leader); + Case(kUriCommissionerGet, NetworkData::Leader); + Case(kUriCommissionerSet, NetworkData::Leader); + Case(kUriAnnounceBegin, AnnounceBeginServer); + Case(kUriRelayTx, MeshCoP::JoinerRouter); +#endif + +#if OPENTHREAD_CONFIG_JOINER_ENABLE + Case(kUriJoinerEntrust, MeshCoP::Joiner); +#endif + +#if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD + Case(kUriPanIdConflict, PanIdQueryClient); + Case(kUriEnergyReport, EnergyScanClient); + Case(kUriDatasetChanged, MeshCoP::Commissioner); + // kUriRelayRx is handled below +#endif + +#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE) + Case(kUriRelayRx, Agent); +#endif + +#if OPENTHREAD_CONFIG_DUA_ENABLE || (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_DUA_ENABLE) + Case(kUriDuaRegistrationNotify, DuaManager); +#endif + +#if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE + Case(kUriAnycastLocate, AnycastLocator); +#endif + + Case(kUriDiagnosticGetRequest, NetworkDiagnostic::Server); + Case(kUriDiagnosticGetQuery, NetworkDiagnostic::Server); + Case(kUriDiagnosticReset, NetworkDiagnostic::Server); + +#if OPENTHREAD_CONFIG_TMF_NETDIAG_CLIENT_ENABLE + Case(kUriDiagnosticGetAnswer, NetworkDiagnostic::Client); +#endif + +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE +#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE + Case(kUriMlr, BackboneRouter::Manager); +#endif +#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE + Case(kUriDuaRegistrationRequest, BackboneRouter::Manager); +#endif +#endif + + default: + didHandle = false; + break; + } + +#undef Case + + return didHandle; } Error Agent::Filter(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, void *aContext) @@ -117,5 +224,112 @@ bool Agent::IsTmfMessage(const Ip6::Address &aSourceAddress, const Ip6::Address return isTmf; } +uint8_t Agent::PriorityToDscp(Message::Priority aPriority) +{ + uint8_t dscp = Ip6::kDscpTmfNormalPriority; + + switch (aPriority) + { + case Message::kPriorityNet: + dscp = Ip6::kDscpTmfNetPriority; + break; + + case Message::kPriorityHigh: + case Message::kPriorityNormal: + break; + + case Message::kPriorityLow: + dscp = Ip6::kDscpTmfLowPriority; + break; + } + + return dscp; +} + +Message::Priority Agent::DscpToPriority(uint8_t aDscp) +{ + Message::Priority priority = Message::kPriorityNet; + + // If the sender does not use TMF specific DSCP value, we use + // `kPriorityNet`. This ensures that senders that do not use the + // new value (older firmware) experience the same behavior as + // before where all TMF message were treated as `kPriorityNet`. + + switch (aDscp) + { + case Ip6::kDscpTmfNetPriority: + default: + break; + case Ip6::kDscpTmfNormalPriority: + priority = Message::kPriorityNormal; + break; + case Ip6::kDscpTmfLowPriority: + priority = Message::kPriorityLow; + break; + } + + return priority; +} + +#if OPENTHREAD_CONFIG_DTLS_ENABLE + +SecureAgent::SecureAgent(Instance &aInstance) + : Coap::CoapSecure(aInstance) +{ + SetResourceHandler(&HandleResource); +} + +bool SecureAgent::HandleResource(CoapBase &aCoapBase, + const char *aUriPath, + Message &aMessage, + const Ip6::MessageInfo &aMessageInfo) +{ + return static_cast(aCoapBase).HandleResource(aUriPath, aMessage, aMessageInfo); +} + +bool SecureAgent::HandleResource(const char *aUriPath, Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + OT_UNUSED_VARIABLE(aMessage); + OT_UNUSED_VARIABLE(aMessageInfo); + + bool didHandle = true; + Uri uri = UriFromPath(aUriPath); + +#define Case(kUri, Type) \ + case kUri: \ + Get().HandleTmf(aMessage, aMessageInfo); \ + break + + switch (uri) + { +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE + Case(kUriJoinerFinalize, MeshCoP::Commissioner); +#endif + +#if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE + Case(kUriCommissionerPetition, MeshCoP::BorderAgent); + Case(kUriCommissionerKeepAlive, MeshCoP::BorderAgent); + Case(kUriRelayTx, MeshCoP::BorderAgent); + Case(kUriCommissionerGet, MeshCoP::BorderAgent); + Case(kUriCommissionerSet, MeshCoP::BorderAgent); + Case(kUriActiveGet, MeshCoP::BorderAgent); + Case(kUriActiveSet, MeshCoP::BorderAgent); + Case(kUriPendingGet, MeshCoP::BorderAgent); + Case(kUriPendingSet, MeshCoP::BorderAgent); + Case(kUriProxyTx, MeshCoP::BorderAgent); +#endif + + default: + didHandle = false; + break; + } + +#undef Case + + return didHandle; +} + +#endif // OPENTHREAD_CONFIG_DTLS_ENABLE + } // namespace Tmf } // namespace ot diff --git a/src/core/thread/tmf.hpp b/src/core/thread/tmf.hpp index 39e0185cdcd..02a1aa34a51 100644 --- a/src/core/thread/tmf.hpp +++ b/src/core/thread/tmf.hpp @@ -37,11 +37,26 @@ #include "openthread-core-config.h" #include "coap/coap.hpp" +#include "coap/coap_secure.hpp" #include "common/locator.hpp" namespace ot { namespace Tmf { +/** + * This macro declares a TMF handler (a full template specialization of `HandleTmf` method) in a given `Type`. + * + * The class `Type` MUST declare a template method of the following format: + * + * template void HandleTmf(Coap::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + * + * @param[in] Type The `Type` in which the TMF handler is declared. + * @param[in] kUri The `Uri` which is handled. + * + */ +#define DeclareTmfHandler(Type, kUri) \ + template <> void Type::HandleTmf(Coap::Message & aMessage, const Ip6::MessageInfo &aMessageInfo) + constexpr uint16_t kUdpPort = 61631; ///< TMF UDP Port typedef Coap::Message Message; ///< A TMF message. @@ -137,11 +152,7 @@ class Agent : public Coap::Coap * @param[in] aInstance A reference to the OpenThread instance. * */ - explicit Agent(Instance &aInstance) - : Coap::Coap(aInstance) - { - SetInterceptor(&Filter, this); - } + explicit Agent(Instance &aInstance); /** * This method starts the TMF agent. @@ -171,10 +182,65 @@ class Agent : public Coap::Coap */ bool IsTmfMessage(const Ip6::Address &aSourceAddress, const Ip6::Address &aDestAddress, uint16_t aDestPort) const; + /** + * This static method converts a TMF message priority to IPv6 header DSCP value. + * + * @param[in] aPriority The message priority to convert. + * + * @returns The DSCP value corresponding to @p aPriority. + * + */ + static uint8_t PriorityToDscp(Message::Priority aPriority); + + /** + * This static method converts a IPv6 header DSCP value to message priority for TMF message. + * + * @param[in] aDscp The IPv6 header DSCP value in a TMF message. + * + * @returns The message priority corresponding to the @p aDscp. + * + */ + static Message::Priority DscpToPriority(uint8_t aDscp); + private: + template void HandleTmf(Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + + static bool HandleResource(CoapBase &aCoapBase, + const char *aUriPath, + Message &aMessage, + const Ip6::MessageInfo &aMessageInfo); + bool HandleResource(const char *aUriPath, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + static Error Filter(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, void *aContext); }; +#if OPENTHREAD_CONFIG_DTLS_ENABLE + +/** + * This class implements functionality of the secure TMF agent. + * + */ +class SecureAgent : public Coap::CoapSecure +{ +public: + /** + * This constructor initializes the object. + * + * @param[in] aInstance A reference to the OpenThread instance. + * + */ + explicit SecureAgent(Instance &aInstance); + +private: + static bool HandleResource(CoapBase &aCoapBase, + const char *aUriPath, + Message &aMessage, + const Ip6::MessageInfo &aMessageInfo); + bool HandleResource(const char *aUriPath, Message &aMessage, const Ip6::MessageInfo &aMessageInfo); +}; + +#endif + } // namespace Tmf } // namespace ot diff --git a/src/core/thread/topology.cpp b/src/core/thread/topology.cpp index 0eef0a0bc50..ebb6c94dd6b 100644 --- a/src/core/thread/topology.cpp +++ b/src/core/thread/topology.cpp @@ -38,9 +38,33 @@ #include "common/debug.hpp" #include "common/instance.hpp" #include "common/locator_getters.hpp" +#include "common/num_utils.hpp" namespace ot { +void Neighbor::SetState(State aState) +{ + VerifyOrExit(mState != aState); + mState = static_cast(aState); + +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + if (mState == kStateValid) + { + mConnectionStart = Uptime::MsecToSec(Get().GetUptime()); + } +#endif + +exit: + return; +} + +#if OPENTHREAD_CONFIG_UPTIME_ENABLE +uint32_t Neighbor::GetConnectionTime(void) const +{ + return IsStateValid() ? Uptime::MsecToSec(Get().GetUptime()) - mConnectionStart : 0; +} +#endif + bool Neighbor::AddressMatcher::Matches(const Neighbor &aNeighbor) const { bool matches = false; @@ -72,14 +96,19 @@ void Neighbor::Info::SetFrom(const Neighbor &aNeighbor) mRloc16 = aNeighbor.GetRloc16(); mLinkFrameCounter = aNeighbor.GetLinkFrameCounters().GetMaximum(); mMleFrameCounter = aNeighbor.GetMleFrameCounter(); - mLinkQualityIn = aNeighbor.GetLinkInfo().GetLinkQuality(); + mLinkQualityIn = aNeighbor.GetLinkQualityIn(); mAverageRssi = aNeighbor.GetLinkInfo().GetAverageRss(); mLastRssi = aNeighbor.GetLinkInfo().GetLastRss(); + mLinkMargin = aNeighbor.GetLinkInfo().GetLinkMargin(); mFrameErrorRate = aNeighbor.GetLinkInfo().GetFrameErrorRate(); mMessageErrorRate = aNeighbor.GetLinkInfo().GetMessageErrorRate(); mRxOnWhenIdle = aNeighbor.IsRxOnWhenIdle(); mFullThreadDevice = aNeighbor.IsFullThreadDevice(); mFullNetworkData = (aNeighbor.GetNetworkDataType() == NetworkData::kFullSet); + mVersion = aNeighbor.GetVersion(); +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + mConnectionTime = aNeighbor.GetConnectionTime(); +#endif } void Neighbor::Init(Instance &aInstance) @@ -173,7 +202,7 @@ void Neighbor::GenerateChallenge(void) Random::Crypto::FillBuffer(mValidPending.mPending.mChallenge, sizeof(mValidPending.mPending.mChallenge))); } -#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE void Neighbor::AggregateLinkMetrics(uint8_t aSeriesId, uint8_t aFrameType, uint8_t aLqi, int8_t aRss) { for (LinkMetrics::SeriesInfo &entry : mLinkMetricsSeriesInfoList) @@ -205,10 +234,10 @@ void Neighbor::RemoveAllForwardTrackingSeriesInfo(void) while (!mLinkMetricsSeriesInfoList.IsEmpty()) { LinkMetrics::SeriesInfo *seriesInfo = mLinkMetricsSeriesInfoList.Pop(); - Get().mSeriesInfoPool.Free(*seriesInfo); + Get().Free(*seriesInfo); } } -#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE +#endif // OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE const char *Neighbor::StateToString(State aState) { @@ -240,28 +269,32 @@ const char *Neighbor::StateToString(State aState) void Child::Info::SetFrom(const Child &aChild) { Clear(); - mExtAddress = aChild.GetExtAddress(); - mTimeout = aChild.GetTimeout(); - mRloc16 = aChild.GetRloc16(); - mChildId = Mle::Mle::ChildIdFromRloc16(aChild.GetRloc16()); - mNetworkDataVersion = aChild.GetNetworkDataVersion(); - mAge = Time::MsecToSec(TimerMilli::GetNow() - aChild.GetLastHeard()); - mLinkQualityIn = aChild.GetLinkInfo().GetLinkQuality(); - mAverageRssi = aChild.GetLinkInfo().GetAverageRss(); - mLastRssi = aChild.GetLinkInfo().GetLastRss(); - mFrameErrorRate = aChild.GetLinkInfo().GetFrameErrorRate(); - mMessageErrorRate = aChild.GetLinkInfo().GetMessageErrorRate(); - mQueuedMessageCnt = aChild.GetIndirectMessageCount(); - mVersion = aChild.GetVersion(); - mRxOnWhenIdle = aChild.IsRxOnWhenIdle(); - mFullThreadDevice = aChild.IsFullThreadDevice(); - mFullNetworkData = (aChild.GetNetworkDataType() == NetworkData::kFullSet); - mIsStateRestoring = aChild.IsStateRestoring(); + mExtAddress = aChild.GetExtAddress(); + mTimeout = aChild.GetTimeout(); + mRloc16 = aChild.GetRloc16(); + mChildId = Mle::ChildIdFromRloc16(aChild.GetRloc16()); + mNetworkDataVersion = aChild.GetNetworkDataVersion(); + mAge = Time::MsecToSec(TimerMilli::GetNow() - aChild.GetLastHeard()); + mLinkQualityIn = aChild.GetLinkQualityIn(); + mAverageRssi = aChild.GetLinkInfo().GetAverageRss(); + mLastRssi = aChild.GetLinkInfo().GetLastRss(); + mFrameErrorRate = aChild.GetLinkInfo().GetFrameErrorRate(); + mMessageErrorRate = aChild.GetLinkInfo().GetMessageErrorRate(); + mQueuedMessageCnt = aChild.GetIndirectMessageCount(); + mVersion = ClampToUint8(aChild.GetVersion()); + mRxOnWhenIdle = aChild.IsRxOnWhenIdle(); + mFullThreadDevice = aChild.IsFullThreadDevice(); + mFullNetworkData = (aChild.GetNetworkDataType() == NetworkData::kFullSet); + mIsStateRestoring = aChild.IsStateRestoring(); + mSupervisionInterval = aChild.GetSupervisionInterval(); #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE mIsCslSynced = aChild.IsCslSynchronized(); #else mIsCslSynced = false; #endif +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + mConnectionTime = aChild.GetConnectionTime(); +#endif } const Ip6::Address *Child::AddressIterator::GetAddress(void) const @@ -515,15 +548,25 @@ void Router::Info::SetFrom(const Router &aRouter) { Clear(); mRloc16 = aRouter.GetRloc16(); - mRouterId = Mle::Mle::RouterIdFromRloc16(mRloc16); + mRouterId = Mle::RouterIdFromRloc16(mRloc16); mExtAddress = aRouter.GetExtAddress(); mAllocated = true; mNextHop = aRouter.GetNextHop(); mLinkEstablished = aRouter.IsStateValid(); mPathCost = aRouter.GetCost(); - mLinkQualityIn = aRouter.GetLinkInfo().GetLinkQuality(); + mLinkQualityIn = aRouter.GetLinkQualityIn(); mLinkQualityOut = aRouter.GetLinkQualityOut(); mAge = static_cast(Time::MsecToSec(TimerMilli::GetNow() - aRouter.GetLastHeard())); + mVersion = ClampToUint8(aRouter.GetVersion()); +} + +void Router::Info::SetFrom(const Parent &aParent) +{ + SetFrom(static_cast(aParent)); +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + mCslClockAccuracy = aParent.GetCslAccuracy().GetClockAccuracy(); + mCslUncertainty = aParent.GetCslAccuracy().GetUncertainty(); +#endif } void Router::Clear(void) @@ -534,4 +577,46 @@ void Router::Clear(void) Init(instance); } +LinkQuality Router::GetTwoWayLinkQuality(void) const { return Min(GetLinkQualityIn(), GetLinkQualityOut()); } + +void Router::SetFrom(const Parent &aParent) +{ + // We use an intermediate pointer to copy `aParent` to silence + // code checkers warning about object slicing (assigning a + // sub-class to base class instance). + + const Router *parentAsRouter = &aParent; + + *this = *parentAsRouter; +} + +void Parent::Clear(void) +{ + Instance &instance = GetInstance(); + + memset(reinterpret_cast(this), 0, sizeof(Parent)); + Init(instance); +} + +bool Router::SetNextHopAndCost(uint8_t aNextHop, uint8_t aCost) +{ + bool changed = false; + + if (mNextHop != aNextHop) + { + mNextHop = aNextHop; + changed = true; + } + + if (mCost != aCost) + { + mCost = aCost; + changed = true; + } + + return changed; +} + +bool Router::SetNextHopToInvalid(void) { return SetNextHopAndCost(Mle::kInvalidRouterId, 0); } + } // namespace ot diff --git a/src/core/thread/topology.hpp b/src/core/thread/topology.hpp index 778356564c9..62824f24918 100644 --- a/src/core/thread/topology.hpp +++ b/src/core/thread/topology.hpp @@ -47,6 +47,7 @@ #include "common/random.hpp" #include "common/serial_number.hpp" #include "common/timer.hpp" +#include "common/uptime.hpp" #include "mac/mac_types.hpp" #include "net/ip6.hpp" #include "radio/radio.hpp" @@ -59,6 +60,7 @@ #include "thread/mle_types.hpp" #include "thread/network_data_types.hpp" #include "thread/radio_selector.hpp" +#include "thread/version.hpp" namespace ot { @@ -223,7 +225,7 @@ class Neighbor : public InstanceLocatorInit * @param[in] aState The state value. * */ - void SetState(State aState) { mState = static_cast(aState); } + void SetState(State aState); /** * This method indicates whether the neighbor is in the Invalid state. @@ -363,10 +365,12 @@ class Neighbor : public InstanceLocatorInit NetworkData::Type GetNetworkDataType(void) const { return GetDeviceMode().GetNetworkDataType(); } /** - * This method sets all bytes of the Extended Address to zero. + * This method returns the Extended Address. + * + * @returns A const reference to the Extended Address. * */ - void ClearExtAddress(void) { memset(&mMacAddr, 0, sizeof(mMacAddr)); } + const Mac::ExtAddress &GetExtAddress(void) const { return mMacAddr; } /** * This method returns the Extended Address. @@ -374,7 +378,7 @@ class Neighbor : public InstanceLocatorInit * @returns A reference to the Extended Address. * */ - const Mac::ExtAddress &GetExtAddress(void) const { return mMacAddr; } + Mac::ExtAddress &GetExtAddress(void) { return mMacAddr; } /** * This method sets the Extended Address. @@ -539,7 +543,7 @@ class Neighbor : public InstanceLocatorInit * This method MUST be used only when the tag is set (and not cleared). Otherwise its behavior is undefined. * * The tag value compassion follows the Serial Number Arithmetic logic from RFC-1982. It is semantically equivalent - * to `LastRxFragementTag > aTag`. + * to `LastRxFragmentTag > aTag`. * * @param[in] aTag A tag value to compare against. * @@ -557,7 +561,7 @@ class Neighbor : public InstanceLocatorInit * @returns TRUE if neighbors is Thread 1.1, FALSE otherwise. * */ - bool IsThreadVersion1p1(void) const { return mState != kStateInvalid && mVersion == OT_THREAD_VERSION_1_1; } + bool IsThreadVersion1p1(void) const { return mState != kStateInvalid && mVersion == kThreadVersion1p1; } /** * This method indicates whether or not neighbor is Thread 1.2 or higher.. @@ -565,7 +569,7 @@ class Neighbor : public InstanceLocatorInit * @returns TRUE if neighbor is Thread 1.2 or higher, FALSE otherwise. * */ - bool IsThreadVersion1p2OrHigher(void) const { return mState != kStateInvalid && mVersion >= OT_THREAD_VERSION_1_2; } + bool IsThreadVersion1p2OrHigher(void) const { return mState != kStateInvalid && mVersion >= kThreadVersion1p2; } /** * This method indicates whether Thread version supports CSL. @@ -583,14 +587,14 @@ class Neighbor : public InstanceLocatorInit */ bool IsEnhancedKeepAliveSupported(void) const { - return mState != kStateInvalid && mVersion >= OT_THREAD_VERSION_1_2; + return (mState != kStateInvalid) && (mVersion >= kThreadVersion1p2); } /** * This method gets the device MLE version. * */ - uint8_t GetVersion(void) const { return mVersion; } + uint16_t GetVersion(void) const { return mVersion; } /** * This method sets the device MLE version. @@ -598,7 +602,7 @@ class Neighbor : public InstanceLocatorInit * @param[in] aVersion The device MLE version. * */ - void SetVersion(uint8_t aVersion) { mVersion = aVersion; } + void SetVersion(uint16_t aVersion) { mVersion = aVersion; } /** * This method gets the number of consecutive link failures. @@ -636,6 +640,14 @@ class Neighbor : public InstanceLocatorInit */ const LinkQualityInfo &GetLinkInfo(void) const { return mLinkInfo; } + /** + * This method gets the link quality in value. + * + * @returns The link quality in value. + * + */ + LinkQuality GetLinkQualityIn(void) const { return GetLinkInfo().GetLinkQuality(); } + /** * This method generates a new challenge value for MLE Link Request/Response exchanges. * @@ -658,6 +670,16 @@ class Neighbor : public InstanceLocatorInit */ uint8_t GetChallengeSize(void) const { return sizeof(mValidPending.mPending.mChallenge); } +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + /** + * This method returns the connection time (in seconds) of the neighbor (seconds since entering `kStateValid`). + * + * @returns The connection time (in seconds), zero if device is not currently in `kStateValid`. + * + */ + uint32_t GetConnectionTime(void) const; +#endif + #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE /** * This method indicates whether or not time sync feature is enabled. @@ -816,7 +838,7 @@ class Neighbor : public InstanceLocatorInit #else uint8_t mLinkFailures; ///< Consecutive link failure count #endif - uint8_t mVersion; ///< The MLE version + uint16_t mVersion; ///< The MLE version LinkQualityInfo mLinkInfo; ///< Link quality info (contains average RSS, link margin and link quality) #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE // A list of Link Metrics Forward Tracking Series that is being @@ -829,6 +851,9 @@ class Neighbor : public InstanceLocatorInit // and this neighbor is the Subject. LinkMetrics::Metrics mEnhAckProbingMetrics; #endif +#if OPENTHREAD_CONFIG_UPTIME_ENABLE + uint32_t mConnectionStart; +#endif }; #if OPENTHREAD_FTD @@ -970,7 +995,7 @@ class Child : public Neighbor, * @returns A reference to the `Ip6::Address` entry currently pointed by the iterator. * */ - const Ip6::Address &operator*(void)const { return *GetAddress(); } + const Ip6::Address &operator*(void) const { return *GetAddress(); } /** * This method overloads operator `==` to evaluate whether or not two `Iterator` instances are equal. @@ -1001,7 +1026,7 @@ class Child : public Neighbor, void Update(void); - const Child & mChild; + const Child &mChild; Ip6::Address::TypeFilter mFilter; Index mIndex; Ip6::Address mMeshLocalAddress; @@ -1201,7 +1226,21 @@ class Child : public Neighbor, */ void SetRequestTlv(uint8_t aIndex, uint8_t aType) { mRequestTlvs[aIndex] = aType; } -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE + /** + * This method returns the supervision interval (in seconds). + * + * @returns The supervision interval (in seconds). + * + */ + uint16_t GetSupervisionInterval(void) const { return mSupervisionInterval; } + + /** + * This method sets the supervision interval. + * + * @param[in] aInterval The supervision interval (in seconds). + * + */ + void SetSupervisionInterval(uint16_t aInterval) { mSupervisionInterval = aInterval; } /** * This method increments the number of seconds since last supervision of the child. @@ -1223,13 +1262,11 @@ class Child : public Neighbor, */ void ResetSecondsSinceLastSupervision(void) { mSecondsSinceSupervision = 0; } -#endif // #if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE /** * This method returns MLR state of an IPv6 multicast address. * - * @note The @p aAdddress reference MUST be from `IterateIp6Addresses()` or `AddressIterator`. + * @note The @p aAddress reference MUST be from `IterateIp6Addresses()` or `AddressIterator`. * * @param[in] aAddress The IPv6 multicast address. * @@ -1241,7 +1278,7 @@ class Child : public Neighbor, /** * This method sets MLR state of an IPv6 multicast address. * - * @note The @p aAdddress reference MUST be from `IterateIp6Addresses()` or `AddressIterator`. + * @note The @p aAddress reference MUST be from `IterateIp6Addresses()` or `AddressIterator`. * * @param[in] aAddress The IPv6 multicast address. * @param[in] aState The target MLR state. @@ -1301,7 +1338,7 @@ class Child : public Neighbor, AddressIterator end(void) { return AddressIterator(mChild, AddressIterator::kEndIterator); } private: - const Child & mChild; + const Child &mChild; Ip6::Address::TypeFilter mFilter; }; @@ -1322,15 +1359,16 @@ class Child : public Neighbor, uint8_t mAttachChallenge[Mle::kMaxChallengeSize]; ///< The challenge value }; -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - uint16_t mSecondsSinceSupervision; ///< Number of seconds since last supervision of the child. -#endif + uint16_t mSupervisionInterval; // Supervision interval for the child (in sec). + uint16_t mSecondsSinceSupervision; // Number of seconds since last supervision of the child. static_assert(OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS < 8192, "mQueuedMessageCount cannot fit max required!"); }; #endif // OPENTHREAD_FTD +class Parent; + /** * This class represents a Thread Router * @@ -1352,6 +1390,14 @@ class Router : public Neighbor * */ void SetFrom(const Router &aRouter); + + /** + * This method sets the `Info` instance from a given `Parent`. + * + * @param[in] aParent A parent. + * + */ + void SetFrom(const Parent &aParent); }; /** @@ -1360,14 +1406,7 @@ class Router : public Neighbor * @param[in] aInstance A reference to OpenThread instance. * */ - void Init(Instance &aInstance) - { - Neighbor::Init(aInstance); -#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - SetCslClockAccuracy(kCslWorstCrystalPpm); - SetCslUncertainty(kCslWorstUncertainty); -#endif - } + void Init(Instance &aInstance) { Neighbor::Init(aInstance); } /** * This method clears the router entry. @@ -1376,20 +1415,18 @@ class Router : public Neighbor void Clear(void); /** - * This method gets the router ID of the next hop to this router. - * - * @returns The router ID of the next hop to this router. + * This method sets the `Router` entry from a `Parent` * */ - uint8_t GetNextHop(void) const { return mNextHop; } + void SetFrom(const Parent &aParent); /** - * This method sets the router ID of the next hop to this router. + * This method gets the router ID of the next hop to this router. * - * @param[in] aRouterId The router ID of the next hop to this router. + * @returns The router ID of the next hop to this router. * */ - void SetNextHop(uint8_t aRouterId) { mNextHop = aRouterId; } + uint8_t GetNextHop(void) const { return mNextHop; } /** * This method gets the link quality out value for this router. @@ -1407,6 +1444,14 @@ class Router : public Neighbor */ void SetLinkQualityOut(LinkQuality aLinkQuality) { mLinkQualityOut = aLinkQuality; } + /** + * This method gets the two-way link quality value (minimum of link quality in and out). + * + * @returns The two-way link quality value. + * + */ + LinkQuality GetTwoWayLinkQuality(void) const; + /** * This method get the route cost to this router. * @@ -1416,59 +1461,102 @@ class Router : public Neighbor uint8_t GetCost(void) const { return mCost; } /** - * This method sets the router cost to this router. + * This method sets the next hop and cost to this router. * - * @param[in] aCost The router cost to this router. + * @param[in] aNextHop The Router ID of the next hop to this router. + * @param[in] aCost The cost to this router. + * + * @retval TRUE If there was a change, i.e., @p aNextHop or @p aCost were different from their previous values. + * @retval FALSE If no change to next hop and cost values (new values are the same as before). * */ - void SetCost(uint8_t aCost) { mCost = aCost; } + bool SetNextHopAndCost(uint8_t aNextHop, uint8_t aCost); + /** + * This method sets the next hop to this router as invalid and clears the cost. + * + * @retval TRUE If there was a change (next hop was valid before). + * @retval FALSE No change to next hop (next hop was invalid before). + * + */ + bool SetNextHopToInvalid(void); + +private: + uint8_t mNextHop; ///< The next hop towards this router + uint8_t mLinkQualityOut : 2; ///< The link quality out for this router + +#if OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE + uint8_t mCost; ///< The cost to this router via neighbor router +#else + uint8_t mCost : 4; ///< The cost to this router via neighbor router +#endif +}; + +/** + * This class represent parent of a child node. + * + */ +class Parent : public Router +{ +public: + /** + * This method initializes the `Parent`. + * + * @param[in] aInstance A reference to OpenThread instance. + * + */ + void Init(Instance &aInstance) + { + Neighbor::Init(aInstance); #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + mCslAccuracy.Init(); +#endif + } + + /** + * This method clears the parent entry. + * + */ + void Clear(void); + /** - * This method get the CSL clock accuracy of this router. + * This method gets route cost from parent to leader. * - * @returns The CSL clock accuracy of this router. + * @returns The route cost from parent to leader * */ - uint8_t GetCslClockAccuracy(void) const { return mCslClockAccuracy; } + uint8_t GetLeaderCost(void) const { return mLeaderCost; } /** - * This method sets the CSL clock accuracy of this router. + * This method sets route cost from parent to leader. * - * @param[in] aCslClockAccuracy The CSL clock accuracy of this router. + * @param[in] aLaderConst The route cost. * */ - void SetCslClockAccuracy(uint8_t aCslClockAccuracy) { mCslClockAccuracy = aCslClockAccuracy; } + void SetLeaderCost(uint8_t aLeaderCost) { mLeaderCost = aLeaderCost; } +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE /** - * This method get the CSL clock uncertainty of this router. + * This method gets the CSL accuracy (clock accuracy and uncertainty). * - * @returns The CSL clock uncertainty of this router. + * @returns The CSL accuracy. * */ - uint8_t GetCslUncertainty(void) const { return mCslUncertainty; } + const Mac::CslAccuracy &GetCslAccuracy(void) const { return mCslAccuracy; } /** - * This method sets the CSL clock uncertainty of this router. + * This method sets CSL accuracy. * - * @param[in] aCslUncertainty The CSL clock uncertainty of this router. + * @param[in] aCslAccuracy The CSL accuracy. * */ - void SetCslUncertainty(uint8_t aCslUncertainty) { mCslUncertainty = aCslUncertainty; } + void SetCslAccuracy(const Mac::CslAccuracy &aCslAccuracy) { mCslAccuracy = aCslAccuracy; } #endif private: - uint8_t mNextHop; ///< The next hop towards this router - uint8_t mLinkQualityOut : 2; ///< The link quality out for this router - -#if OPENTHREAD_CONFIG_MLE_LONG_ROUTES_ENABLE - uint8_t mCost; ///< The cost to this router via neighbor router -#else - uint8_t mCost : 4; ///< The cost to this router via neighbor router -#endif + uint8_t mLeaderCost; #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE - uint8_t mCslClockAccuracy; ///< Crystal accuracy, in units of ± ppm. - uint8_t mCslUncertainty; ///< Scheduling uncertainty, in units of 10 us. + Mac::CslAccuracy mCslAccuracy; // CSL accuracy (clock accuracy in ppm and uncertainty). #endif }; diff --git a/src/core/thread/uri_paths.cpp b/src/core/thread/uri_paths.cpp index 9f72a6aef0b..ff26d2e76ab 100644 --- a/src/core/thread/uri_paths.cpp +++ b/src/core/thread/uri_paths.cpp @@ -33,46 +33,172 @@ #include "uri_paths.hpp" +#include "common/binary_search.hpp" +#include "common/debug.hpp" +#include "common/string.hpp" + +#include + namespace ot { -const char UriPath::kAddressQuery[] = "a/aq"; -const char UriPath::kAddressNotify[] = "a/an"; -const char UriPath::kAddressError[] = "a/ae"; -const char UriPath::kAddressRelease[] = "a/ar"; -const char UriPath::kAddressSolicit[] = "a/as"; -const char UriPath::kAnycastLocate[] = "a/yl"; -const char UriPath::kActiveGet[] = "c/ag"; -const char UriPath::kActiveSet[] = "c/as"; -const char UriPath::kDatasetChanged[] = "c/dc"; -const char UriPath::kEnergyScan[] = "c/es"; -const char UriPath::kEnergyReport[] = "c/er"; -const char UriPath::kPendingGet[] = "c/pg"; -const char UriPath::kPendingSet[] = "c/ps"; -const char UriPath::kServerData[] = "a/sd"; -const char UriPath::kAnnounceBegin[] = "c/ab"; -const char UriPath::kProxyRx[] = "c/ur"; -const char UriPath::kProxyTx[] = "c/ut"; -const char UriPath::kRelayRx[] = "c/rx"; -const char UriPath::kRelayTx[] = "c/tx"; -const char UriPath::kJoinerFinalize[] = "c/jf"; -const char UriPath::kJoinerEntrust[] = "c/je"; -const char UriPath::kLeaderPetition[] = "c/lp"; -const char UriPath::kLeaderKeepAlive[] = "c/la"; -const char UriPath::kPanIdConflict[] = "c/pc"; -const char UriPath::kPanIdQuery[] = "c/pq"; -const char UriPath::kCommissionerGet[] = "c/cg"; -const char UriPath::kCommissionerKeepAlive[] = "c/ca"; -const char UriPath::kCommissionerPetition[] = "c/cp"; -const char UriPath::kCommissionerSet[] = "c/cs"; -const char UriPath::kDiagnosticGetRequest[] = "d/dg"; -const char UriPath::kDiagnosticGetQuery[] = "d/dq"; -const char UriPath::kDiagnosticGetAnswer[] = "d/da"; -const char UriPath::kDiagnosticReset[] = "d/dr"; -const char UriPath::kMlr[] = "n/mr"; -const char UriPath::kDuaRegistrationRequest[] = "n/dr"; -const char UriPath::kDuaRegistrationNotify[] = "n/dn"; -const char UriPath::kBackboneQuery[] = "b/bq"; -const char UriPath::kBackboneAnswer[] = "b/ba"; -const char UriPath::kBackboneMlr[] = "b/bmr"; +namespace UriList { + +struct Entry +{ + const char *mPath; + + constexpr static bool AreInOrder(const Entry &aFirst, const Entry &aSecond) + { + return AreStringsInOrder(aFirst.mPath, aSecond.mPath); + } + + int Compare(const char *aPath) const { return strcmp(aPath, mPath); } +}; + +// The list of URI paths (MUST be sorted alphabetically) +static constexpr Entry kEntries[] = { + {"a/ae"}, // (0) kUriAddressError + {"a/an"}, // (1) kUriAddressNotify + {"a/aq"}, // (2) kUriAddressQuery + {"a/ar"}, // (3) kUriAddressRelease + {"a/as"}, // (4) kUriAddressSolicit + {"a/sd"}, // (5) kUriServerData + {"a/yl"}, // (6) kUriAnycastLocate + {"b/ba"}, // (7) kUriBackboneAnswer + {"b/bmr"}, // (8) kUriBackboneMlr + {"b/bq"}, // (9) kUriBackboneQuery + {"c/ab"}, // (10) kUriAnnounceBegin + {"c/ag"}, // (11) kUriActiveGet + {"c/as"}, // (12) kUriActiveSet + {"c/ca"}, // (13) kUriCommissionerKeepAlive + {"c/cg"}, // (14) kUriCommissionerGet + {"c/cp"}, // (15) kUriCommissionerPetition + {"c/cs"}, // (16) kUriCommissionerSet + {"c/dc"}, // (17) kUriDatasetChanged + {"c/er"}, // (18) kUriEnergyReport + {"c/es"}, // (19) kUriEnergyScan + {"c/je"}, // (20) kUriJoinerEntrust + {"c/jf"}, // (21) kUriJoinerFinalize + {"c/la"}, // (22) kUriLeaderKeepAlive + {"c/lp"}, // (23) kUriLeaderPetition + {"c/pc"}, // (24) kUriPanIdConflict + {"c/pg"}, // (25) kUriPendingGet + {"c/pq"}, // (26) kUriPanIdQuery + {"c/ps"}, // (27) kUriPendingSet + {"c/rx"}, // (28) kUriRelayRx + {"c/tx"}, // (29) kUriRelayTx + {"c/ur"}, // (30) kUriProxyRx + {"c/ut"}, // (31) kUriProxyTx + {"d/da"}, // (32) kUriDiagnosticGetAnswer + {"d/dg"}, // (33) kUriDiagnosticGetRequest + {"d/dq"}, // (34) kUriDiagnosticGetQuery + {"d/dr"}, // (35) kUriDiagnosticReset + {"n/dn"}, // (36) kUriDuaRegistrationNotify + {"n/dr"}, // (37) kUriDuaRegistrationRequest + {"n/mr"}, // (38) kUriMlr +}; + +static_assert(BinarySearch::IsSorted(kEntries), "kEntries is not sorted"); + +static_assert(0 == kUriAddressError, "kUriAddressError (`a/ae`) is invalid"); +static_assert(1 == kUriAddressNotify, "kUriAddressNotify (`a/an`) is invalid"); +static_assert(2 == kUriAddressQuery, "kUriAddressQuery (`a/aq`) is invalid"); +static_assert(3 == kUriAddressRelease, "kUriAddressRelease (`a/ar`) is invalid"); +static_assert(4 == kUriAddressSolicit, "kUriAddressSolicit (`a/as`) is invalid"); +static_assert(5 == kUriServerData, "kUriServerData (`a/sd`) is invalid"); +static_assert(6 == kUriAnycastLocate, "kUriAnycastLocate (`a/yl`) is invalid"); +static_assert(7 == kUriBackboneAnswer, "kUriBackboneAnswer (`b/ba`) is invalid"); +static_assert(8 == kUriBackboneMlr, "kUriBackboneMlr (`b/bmr`) is invalid"); +static_assert(9 == kUriBackboneQuery, "kUriBackboneQuery (`b/bq`) is invalid"); +static_assert(10 == kUriAnnounceBegin, "kUriAnnounceBegin (`c/ab`) is invalid"); +static_assert(11 == kUriActiveGet, "kUriActiveGet (`c/ag`) is invalid"); +static_assert(12 == kUriActiveSet, "kUriActiveSet (`c/as`) is invalid"); +static_assert(13 == kUriCommissionerKeepAlive, "kUriCommissionerKeepAlive (`c/ca`) is invalid"); +static_assert(14 == kUriCommissionerGet, "kUriCommissionerGet (`c/cg`) is invalid"); +static_assert(15 == kUriCommissionerPetition, "kUriCommissionerPetition (`c/cp`) is invalid"); +static_assert(16 == kUriCommissionerSet, "kUriCommissionerSet (`c/cs`) is invalid"); +static_assert(17 == kUriDatasetChanged, "kUriDatasetChanged (`c/dc`) is invalid"); +static_assert(18 == kUriEnergyReport, "kUriEnergyReport (`c/er`) is invalid"); +static_assert(19 == kUriEnergyScan, "kUriEnergyScan (`c/es`) is invalid"); +static_assert(20 == kUriJoinerEntrust, "kUriJoinerEntrust (`c/je`) is invalid"); +static_assert(21 == kUriJoinerFinalize, "kUriJoinerFinalize (`c/jf`) is invalid"); +static_assert(22 == kUriLeaderKeepAlive, "kUriLeaderKeepAlive (`c/la`) is invalid"); +static_assert(23 == kUriLeaderPetition, "kUriLeaderPetition (`c/lp`) is invalid"); +static_assert(24 == kUriPanIdConflict, "kUriPanIdConflict (`c/pc`) is invalid"); +static_assert(25 == kUriPendingGet, "kUriPendingGet (`c/pg`) is invalid"); +static_assert(26 == kUriPanIdQuery, "kUriPanIdQuery (`c/pq`) is invalid"); +static_assert(27 == kUriPendingSet, "kUriPendingSet (`c/ps`) is invalid"); +static_assert(28 == kUriRelayRx, "kUriRelayRx (`c/rx`) is invalid"); +static_assert(29 == kUriRelayTx, "kUriRelayTx (`c/tx`) is invalid"); +static_assert(30 == kUriProxyRx, "kUriProxyRx (`c/ur`) is invalid"); +static_assert(31 == kUriProxyTx, "kUriProxyTx (`c/ut`) is invalid"); +static_assert(32 == kUriDiagnosticGetAnswer, "kUriDiagnosticGetAnswer (`d/da`) is invalid"); +static_assert(33 == kUriDiagnosticGetRequest, "kUriDiagnosticGetRequest (`d/dg`) is invalid"); +static_assert(34 == kUriDiagnosticGetQuery, "kUriDiagnosticGetQuery (`d/dq`) is invalid"); +static_assert(35 == kUriDiagnosticReset, "kUriDiagnosticReset (`d/dr`) is invalid"); +static_assert(36 == kUriDuaRegistrationNotify, "kUriDuaRegistrationNotify (`n/dn`) is invalid"); +static_assert(37 == kUriDuaRegistrationRequest, "kUriDuaRegistrationRequest (`n/dr`) is invalid"); +static_assert(38 == kUriMlr, "kUriMlr (`n/mr`) is invalid"); + +} // namespace UriList + +const char *PathForUri(Uri aUri) +{ + OT_ASSERT(aUri != kUriUnknown); + + return UriList::kEntries[aUri].mPath; +} + +Uri UriFromPath(const char *aPath) +{ + Uri uri = kUriUnknown; + const UriList::Entry *entry = BinarySearch::Find(aPath, UriList::kEntries); + + VerifyOrExit(entry != nullptr); + uri = static_cast(entry - UriList::kEntries); + +exit: + return uri; +} + +template <> const char *UriToString(void) { return "AddressError"; } +template <> const char *UriToString(void) { return "AddressNotify"; } +template <> const char *UriToString(void) { return "AddressQuery"; } +template <> const char *UriToString(void) { return "AddressRelease"; } +template <> const char *UriToString(void) { return "AddressSolicit"; } +template <> const char *UriToString(void) { return "ServerData"; } +template <> const char *UriToString(void) { return "AnycastLocate"; } +template <> const char *UriToString(void) { return "BackboneAnswer"; } +template <> const char *UriToString(void) { return "BackboneMlr"; } +template <> const char *UriToString(void) { return "BackboneQuery"; } +template <> const char *UriToString(void) { return "AnnounceBegin"; } +template <> const char *UriToString(void) { return "ActiveGet"; } +template <> const char *UriToString(void) { return "ActiveSet"; } +template <> const char *UriToString(void) { return "CommissionerKeepAlive"; } +template <> const char *UriToString(void) { return "CommissionerGet"; } +template <> const char *UriToString(void) { return "CommissionerPetition"; } +template <> const char *UriToString(void) { return "CommissionerSet"; } +template <> const char *UriToString(void) { return "DatasetChanged"; } +template <> const char *UriToString(void) { return "EnergyReport"; } +template <> const char *UriToString(void) { return "EnergyScan"; } +template <> const char *UriToString(void) { return "JoinerEntrust"; } +template <> const char *UriToString(void) { return "JoinerFinalize"; } +template <> const char *UriToString(void) { return "LeaderKeepAlive"; } +template <> const char *UriToString(void) { return "LeaderPetition"; } +template <> const char *UriToString(void) { return "PanIdConflict"; } +template <> const char *UriToString(void) { return "PendingGet"; } +template <> const char *UriToString(void) { return "PanIdQuery"; } +template <> const char *UriToString(void) { return "PendingSet"; } +template <> const char *UriToString(void) { return "RelayRx"; } +template <> const char *UriToString(void) { return "RelayTx"; } +template <> const char *UriToString(void) { return "ProxyRx"; } +template <> const char *UriToString(void) { return "ProxyTx"; } +template <> const char *UriToString(void) { return "DiagGetAnswer"; } +template <> const char *UriToString(void) { return "DiagGetRequest"; } +template <> const char *UriToString(void) { return "DiagGetQuery"; } +template <> const char *UriToString(void) { return "DiagReset"; } +template <> const char *UriToString(void) { return "DuaRegNotify"; } +template <> const char *UriToString(void) { return "DuaRegRequest"; } +template <> const char *UriToString(void) { return "Mlr"; } } // namespace ot diff --git a/src/core/thread/uri_paths.hpp b/src/core/thread/uri_paths.hpp index 30c7999e2ae..01ef7b08c38 100644 --- a/src/core/thread/uri_paths.hpp +++ b/src/core/thread/uri_paths.hpp @@ -36,56 +36,129 @@ #include "openthread-core-config.h" +#include "common/error.hpp" + namespace ot { /** - * - * This structure contains Thread URI Path string definitions. + * This enumeration represents Thread URIs. * */ -struct UriPath +enum Uri : uint8_t { - static const char kAddressQuery[]; ///< The URI Path for Address Query ("a/aq"). - static const char kAddressNotify[]; ///< The URI Path for Address Notify ("a/an"). - static const char kAddressError[]; ///< The URI Path for Address Error ("a/ae"). - static const char kAddressRelease[]; ///< The URI Path for Address Release ("a/ar"). - static const char kAddressSolicit[]; ///< The URI Path for Address Solicit ("a/as"). - static const char kAnycastLocate[]; ///< The URI Path for Anycast Locate ("a/yl") - static const char kActiveGet[]; ///< The URI Path for MGMT_ACTIVE_GE ("c/ag")T - static const char kActiveSet[]; ///< The URI Path for MGMT_ACTIVE_SET ("c/as"). - static const char kDatasetChanged[]; ///< The URI Path for MGMT_DATASET_CHANGED ("c/dc"). - static const char kEnergyScan[]; ///< The URI Path for Energy Scan ("c/es"). - static const char kEnergyReport[]; ///< The URI Path for Energy Report ("c/er"). - static const char kPendingGet[]; ///< The URI Path for MGMT_PENDING_GET ("c/pg"). - static const char kPendingSet[]; ///< The URI Path for MGMT_PENDING_SET ("c/ps"). - static const char kServerData[]; ///< The URI Path for Server Data Registration ("a/sd"). - static const char kAnnounceBegin[]; ///< The URI Path for Announce Begin ("c/ab"). - static const char kProxyRx[]; ///< The URI Path for Proxy RX ("c/ur"). - static const char kProxyTx[]; ///< The URI Path for Proxy TX ("c/ut"). - static const char kRelayRx[]; ///< The URI Path for Relay RX ("c/rx"). - static const char kRelayTx[]; ///< The URI Path for Relay TX ("c/tx"). - static const char kJoinerFinalize[]; ///< The URI Path for Joiner Finalize ("c/jf"). - static const char kJoinerEntrust[]; ///< The URI Path for Joiner Entrust ("c/je"). - static const char kLeaderPetition[]; ///< The URI Path for Leader Petition ("c/lp"). - static const char kLeaderKeepAlive[]; ///< The URI Path for Leader Keep Alive ("c/la"). - static const char kPanIdConflict[]; ///< The URI Path for PAN ID Conflict ("c/pc"). - static const char kPanIdQuery[]; ///< The URI Path for PAN ID Query ("c/pq"). - static const char kCommissionerGet[]; ///< The URI Path for MGMT_COMMISSIONER_GET ("c/cg"). - static const char kCommissionerKeepAlive[]; ///< The URI Path for Commissioner Keep Alive ("c/ca"). - static const char kCommissionerPetition[]; ///< The URI Path for Commissioner Petition ("c/cp"). - static const char kCommissionerSet[]; ///< The URI Path for MGMT_COMMISSIONER_SET ("c/cs"). - static const char kDiagnosticGetRequest[]; ///< The URI Path for Network Diagnostic Get Request ("d/dg"). - static const char kDiagnosticGetQuery[]; ///< The URI Path for Network Diagnostic Get Query ("d/dq"). - static const char kDiagnosticGetAnswer[]; ///< The URI Path for Network Diagnostic Get Answer ("d/da"). - static const char kDiagnosticReset[]; ///< The URI Path for Network Diagnostic Reset ("d/dr"). - static const char kMlr[]; ///< The URI Path for Multicast Listener Registration ("n/mr"). - static const char kDuaRegistrationRequest[]; ///< The URI Path for DUA Registration Request ("n/dr"). - static const char kDuaRegistrationNotify[]; ///< The URI Path for DUA Registration Notification ("n/dn"). - static const char kBackboneQuery[]; ///< The URI Path for Backbone Query ("b/bq"). - static const char kBackboneAnswer[]; ///< The URI Path for Backbone Answer / Backbone Notification ("b/ba"). - static const char kBackboneMlr[]; ///< The URI Path for Backbone Multicast Listener Report ("b/bmr"). + kUriAddressError, ///< Address Error ("a/ae") + kUriAddressNotify, ///< Address Notify ("a/an") + kUriAddressQuery, ///< Address Query ("a/aq") + kUriAddressRelease, ///< Address Release ("a/ar") + kUriAddressSolicit, ///< Address Solicit ("a/as") + kUriServerData, ///< Server Data Registration ("a/sd") + kUriAnycastLocate, ///< Anycast Locate ("a/yl") + kUriBackboneAnswer, ///< Backbone Answer / Backbone Notification ("b/ba") + kUriBackboneMlr, ///< Backbone Multicast Listener Report ("b/bmr") + kUriBackboneQuery, ///< Backbone Query ("b/bq") + kUriAnnounceBegin, ///< Announce Begin ("c/ab") + kUriActiveGet, ///< MGMT_ACTIVE_GET "c/ag" + kUriActiveSet, ///< MGMT_ACTIVE_SET ("c/as") + kUriCommissionerKeepAlive, ///< Commissioner Keep Alive ("c/ca") + kUriCommissionerGet, ///< MGMT_COMMISSIONER_GET ("c/cg") + kUriCommissionerPetition, ///< Commissioner Petition ("c/cp") + kUriCommissionerSet, ///< MGMT_COMMISSIONER_SET ("c/cs") + kUriDatasetChanged, ///< MGMT_DATASET_CHANGED ("c/dc") + kUriEnergyReport, ///< Energy Report ("c/er") + kUriEnergyScan, ///< Energy Scan ("c/es") + kUriJoinerEntrust, ///< Joiner Entrust ("c/je") + kUriJoinerFinalize, ///< Joiner Finalize ("c/jf") + kUriLeaderKeepAlive, ///< Leader Keep Alive ("c/la") + kUriLeaderPetition, ///< Leader Petition ("c/lp") + kUriPanIdConflict, ///< PAN ID Conflict ("c/pc") + kUriPendingGet, ///< MGMT_PENDING_GET ("c/pg") + kUriPanIdQuery, ///< PAN ID Query ("c/pq") + kUriPendingSet, ///< MGMT_PENDING_SET ("c/ps") + kUriRelayRx, ///< Relay RX ("c/rx") + kUriRelayTx, ///< Relay TX ("c/tx") + kUriProxyRx, ///< Proxy RX ("c/ur") + kUriProxyTx, ///< Proxy TX ("c/ut") + kUriDiagnosticGetAnswer, ///< Network Diagnostic Get Answer ("d/da") + kUriDiagnosticGetRequest, ///< Network Diagnostic Get Request ("d/dg") + kUriDiagnosticGetQuery, ///< Network Diagnostic Get Query ("d/dq") + kUriDiagnosticReset, ///< Network Diagnostic Reset ("d/dr") + kUriDuaRegistrationNotify, ///< DUA Registration Notification ("n/dn") + kUriDuaRegistrationRequest, ///< DUA Registration Request ("n/dr") + kUriMlr, ///< Multicast Listener Registration ("n/mr") + kUriUnknown, ///< Unknown URI }; +/** + * This function returns URI path string for a given URI. + * + * @param[in] aUri A URI. + * + * @returns The path string for @p aUri. + * + */ +const char *PathForUri(Uri aUri); + +/** + * This function looks up the URI from a given path string. + * + * @param[in] aPath A path string. + * + * @returns The URI associated with @p aPath or `kUriUnknown` if no match is found. + * + */ +Uri UriFromPath(const char *aPath); + +/** + * This template function converts a given URI to a human-readable string. + * + * @tparam kUri The URI to convert to string. + * + * @returns The string representation of @p kUri. + * + */ +template const char *UriToString(void); + +// Declaring specializations of `UriToString` for every `Uri` +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); +template <> const char *UriToString(void); + } // namespace ot #endif // URI_PATHS_HPP_ diff --git a/examples/platforms/cc2538/logging.c b/src/core/thread/version.hpp similarity index 67% rename from examples/platforms/cc2538/logging.c rename to src/core/thread/version.hpp index 82158aeed3a..c3584aad324 100644 --- a/examples/platforms/cc2538/logging.c +++ b/src/core/thread/version.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The OpenThread Authors. + * Copyright (c) 2022, The OpenThread Authors. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,21 +27,26 @@ */ /** - * @file logging.c - * Platform abstraction for the logging - * + * @file + * This file includes definitions for Thread Version. */ -#include -#include -#include -#include - -#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED) -OT_TOOL_WEAK void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) -{ - OT_UNUSED_VARIABLE(aLogLevel); - OT_UNUSED_VARIABLE(aLogRegion); - OT_UNUSED_VARIABLE(aFormat); -} -#endif +#ifndef VERSION_HPP_ +#define VERSION_HPP_ + +#include "openthread-core-config.h" + +#include + +namespace ot { + +constexpr uint16_t kThreadVersion = OPENTHREAD_CONFIG_THREAD_VERSION; ///< Thread Version of this device. + +constexpr uint16_t kThreadVersion1p1 = OT_THREAD_VERSION_1_1; ///< Thread Version 1.1 +constexpr uint16_t kThreadVersion1p2 = OT_THREAD_VERSION_1_2; ///< Thread Version 1.2 +constexpr uint16_t kThreadVersion1p3 = OT_THREAD_VERSION_1_3; ///< Thread Version 1.3 +constexpr uint16_t kThreadVersion1p3p1 = OT_THREAD_VERSION_1_3_1; ///< Thread Version 1.3.1 + +} // namespace ot + +#endif // VERSION_HPP_ diff --git a/src/core/utils/channel_manager.cpp b/src/core/utils/channel_manager.cpp index 93c24f598a4..1303509f72d 100644 --- a/src/core/utils/channel_manager.cpp +++ b/src/core/utils/channel_manager.cpp @@ -57,7 +57,7 @@ ChannelManager::ChannelManager(Instance &aInstance) , mDelay(kMinimumDelay) , mChannel(0) , mState(kStateIdle) - , mTimer(aInstance, ChannelManager::HandleTimer) + , mTimer(aInstance) , mAutoSelectInterval(kDefaultAutoSelectInterval) , mAutoSelectEnabled(false) , mCcaFailureRateThreshold(kCcaFailureRateThreshold) @@ -154,11 +154,6 @@ void ChannelManager::HandleDatasetUpdateDone(Error aError) StartAutoSelectTimer(); } -void ChannelManager::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void ChannelManager::HandleTimer(void) { switch (mState) @@ -191,8 +186,8 @@ Error ChannelManager::FindBetterChannel(uint8_t &aNewChannel, uint16_t &aOccupan if (Get().GetSampleCount() <= kMinChannelMonitorSampleCount) { - LogInfo("Too few samples (%d <= %d) to select channel", Get().GetSampleCount(), - kMinChannelMonitorSampleCount); + LogInfo("Too few samples (%lu <= %lu) to select channel", ToUlong(Get().GetSampleCount()), + ToUlong(kMinChannelMonitorSampleCount)); ExitNow(error = kErrorInvalidState); } diff --git a/src/core/utils/channel_manager.hpp b/src/core/utils/channel_manager.hpp index a6d102054cb..f375be02244 100644 --- a/src/core/utils/channel_manager.hpp +++ b/src/core/utils/channel_manager.hpp @@ -276,7 +276,6 @@ class ChannelManager : public InstanceLocator, private NonCopyable void StartDatasetUpdate(void); static void HandleDatasetUpdateDone(Error aError, void *aContext); void HandleDatasetUpdateDone(Error aError); - static void HandleTimer(Timer &aTimer); void HandleTimer(void); void StartAutoSelectTimer(void); @@ -285,12 +284,14 @@ class ChannelManager : public InstanceLocator, private NonCopyable bool ShouldAttemptChannelChange(void); #endif + using ManagerTimer = TimerMilliIn; + Mac::ChannelMask mSupportedChannelMask; Mac::ChannelMask mFavoredChannelMask; uint16_t mDelay; uint8_t mChannel; State mState; - TimerMilli mTimer; + ManagerTimer mTimer; uint32_t mAutoSelectInterval; bool mAutoSelectEnabled; uint16_t mCcaFailureRateThreshold; diff --git a/src/core/utils/channel_monitor.cpp b/src/core/utils/channel_monitor.cpp index 8925c646fa6..b707a79b636 100644 --- a/src/core/utils/channel_monitor.cpp +++ b/src/core/utils/channel_monitor.cpp @@ -64,7 +64,7 @@ ChannelMonitor::ChannelMonitor(Instance &aInstance) : InstanceLocator(aInstance) , mChannelMaskIndex(0) , mSampleCount(0) - , mTimer(aInstance, ChannelMonitor::HandleTimer) + , mTimer(aInstance) { memset(mChannelOccupancy, 0, sizeof(mChannelOccupancy)); } @@ -114,11 +114,6 @@ uint16_t ChannelMonitor::GetChannelOccupancy(uint8_t aChannel) const return occupancy; } -void ChannelMonitor::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void ChannelMonitor::HandleTimer(void) { IgnoreError(Get().EnergyScan(mScanChannelMasks[mChannelMaskIndex], 0, @@ -158,7 +153,7 @@ void ChannelMonitor::HandleEnergyScanResult(Mac::EnergyScanResult *aResult) LogDebg("channel: %d, rssi:%d", aResult->mChannel, aResult->mMaxRssi); - if (aResult->mMaxRssi != OT_RADIO_RSSI_INVALID) + if (aResult->mMaxRssi != Radio::kInvalidRssi) { newValue = (aResult->mMaxRssi >= kRssiThreshold) ? kMaxOccupancy : 0; } @@ -200,7 +195,7 @@ void ChannelMonitor::LogResults(void) logString.Append("%02x ", channel >> 8); } - LogInfo("%u [%s]", mSampleCount, logString.AsCString()); + LogInfo("%lu [%s]", ToUlong(mSampleCount), logString.AsCString()); #endif } diff --git a/src/core/utils/channel_monitor.hpp b/src/core/utils/channel_monitor.hpp index 8a51f4386e1..b0221af5223 100644 --- a/src/core/utils/channel_monitor.hpp +++ b/src/core/utils/channel_monitor.hpp @@ -193,18 +193,19 @@ class ChannelMonitor : public InstanceLocator, private NonCopyable static constexpr uint16_t kMaxJitterInterval = 4096; static constexpr uint32_t kMaxOccupancy = 0xffff; - static void HandleTimer(Timer &aTimer); void HandleTimer(void); static void HandleEnergyScanResult(Mac::EnergyScanResult *aResult, void *aContext); void HandleEnergyScanResult(Mac::EnergyScanResult *aResult); void LogResults(void); + using ScanTimer = TimerMilliIn; + static const uint32_t mScanChannelMasks[kNumChannelMasks]; - uint8_t mChannelMaskIndex : 3; - uint32_t mSampleCount : 29; - uint16_t mChannelOccupancy[kNumChannels]; - TimerMilli mTimer; + uint8_t mChannelMaskIndex : 3; + uint32_t mSampleCount : 29; + uint16_t mChannelOccupancy[kNumChannels]; + ScanTimer mTimer; }; /** diff --git a/src/core/utils/heap.cpp b/src/core/utils/heap.cpp index 0f8432d1bb4..02309f372f2 100644 --- a/src/core/utils/heap.cpp +++ b/src/core/utils/heap.cpp @@ -63,9 +63,9 @@ Heap::Heap(void) void *Heap::CAlloc(size_t aCount, size_t aSize) { - void * ret = nullptr; - Block * prev = nullptr; - Block * curr = nullptr; + void *ret = nullptr; + Block *prev = nullptr; + Block *curr = nullptr; uint16_t size = static_cast(aCount * aSize); VerifyOrExit(size); diff --git a/src/core/utils/heap.hpp b/src/core/utils/heap.hpp index 02b9a5e83a9..bd391a97db9 100644 --- a/src/core/utils/heap.hpp +++ b/src/core/utils/heap.hpp @@ -209,7 +209,7 @@ class Heap : private NonCopyable */ bool IsClean(void) const { - Heap & self = *AsNonConst(this); + Heap &self = *AsNonConst(this); const Block &super = self.BlockSuper(); const Block &first = self.BlockRight(super); return super.GetNext() == self.BlockOffset(first) && first.GetSize() == kFirstBlockSize; @@ -227,7 +227,7 @@ class Heap : private NonCopyable size_t GetFreeSize(void) const { return mMemory.mFreeSize; } private: -#if OPENTHREAD_CONFIG_DTLS_ENABLE +#if OPENTHREAD_CONFIG_TLS_ENABLE || OPENTHREAD_CONFIG_DTLS_ENABLE static constexpr uint16_t kMemorySize = OPENTHREAD_CONFIG_HEAP_INTERNAL_SIZE; #else static constexpr uint16_t kMemorySize = OPENTHREAD_CONFIG_HEAP_INTERNAL_SIZE_NO_DTLS; diff --git a/src/core/utils/history_tracker.cpp b/src/core/utils/history_tracker.cpp index 199146569bd..2bcda12fd90 100644 --- a/src/core/utils/history_tracker.cpp +++ b/src/core/utils/history_tracker.cpp @@ -40,6 +40,7 @@ #include "common/debug.hpp" #include "common/instance.hpp" #include "common/locator_getters.hpp" +#include "common/num_utils.hpp" #include "common/string.hpp" #include "common/timer.hpp" #include "net/ip6_headers.hpp" @@ -52,17 +53,21 @@ namespace Utils { HistoryTracker::HistoryTracker(Instance &aInstance) : InstanceLocator(aInstance) - , mTimer(aInstance, HandleTimer) + , mTimer(aInstance) #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA , mPreviousNetworkData(aInstance, mNetworkDataTlvBuffer, 0, sizeof(mNetworkDataTlvBuffer)) #endif { mTimer.Start(kAgeCheckPeriod); + +#if OPENTHREAD_FTD && (OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE > 0) + memset(mRouterEntries, 0, sizeof(mRouterEntries)); +#endif } void HistoryTracker::RecordNetworkInfo(void) { - NetworkInfo * entry = mNetInfoHistory.AddNewEntry(); + NetworkInfo *entry = mNetInfoHistory.AddNewEntry(); Mle::DeviceMode mode; VerifyOrExit(entry != nullptr); @@ -77,7 +82,7 @@ void HistoryTracker::RecordNetworkInfo(void) return; } -void HistoryTracker::RecordMessage(const Message &aMessage, const Mac::Address &aMacAddresss, MessageType aType) +void HistoryTracker::RecordMessage(const Message &aMessage, const Mac::Address &aMacAddress, MessageType aType) { MessageInfo *entry = nullptr; Ip6::Headers headers; @@ -120,7 +125,7 @@ void HistoryTracker::RecordMessage(const Message &aMessage, const Mac::Address & VerifyOrExit(entry != nullptr); entry->mPayloadLength = headers.GetIp6Header().GetPayloadLength(); - entry->mNeighborRloc16 = aMacAddresss.IsShort() ? aMacAddresss.GetShort() : kInvalidRloc16; + entry->mNeighborRloc16 = aMacAddress.IsShort() ? aMacAddress.GetShort() : kInvalidRloc16; entry->mSource.mAddress = headers.GetSourceAddress(); entry->mSource.mPort = headers.GetSourcePort(); entry->mDestination.mAddress = headers.GetDestinationAddress(); @@ -128,14 +133,14 @@ void HistoryTracker::RecordMessage(const Message &aMessage, const Mac::Address & entry->mChecksum = headers.GetChecksum(); entry->mIpProto = headers.GetIpProto(); entry->mIcmp6Type = headers.IsIcmp6() ? headers.GetIcmpHeader().GetType() : 0; - entry->mAveRxRss = (aType == kRxMessage) ? aMessage.GetRssAverager().GetAverage() : kInvalidRss; + entry->mAveRxRss = (aType == kRxMessage) ? aMessage.GetRssAverager().GetAverage() : Radio::kInvalidRssi; entry->mLinkSecurity = aMessage.IsLinkSecurityEnabled(); entry->mTxSuccess = (aType == kTxMessage) ? aMessage.GetTxSuccess() : true; entry->mPriority = aMessage.GetPriority(); - if (aMacAddresss.IsExtended()) + if (aMacAddress.IsExtended()) { - Neighbor *neighbor = Get().FindNeighbor(aMacAddresss, Neighbor::kInStateAnyExceptInvalid); + Neighbor *neighbor = Get().FindNeighbor(aMacAddress, Neighbor::kInStateAnyExceptInvalid); if (neighbor != nullptr) { @@ -278,6 +283,76 @@ void HistoryTracker::RecordAddressEvent(Ip6::Netif::AddressEvent aEve return; } +#if OPENTHREAD_FTD +void HistoryTracker::RecordRouterTableChange(void) +{ +#if OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE > 0 + + for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) + { + RouterInfo entry; + RouterEntry &oldEntry = mRouterEntries[routerId]; + + entry.mRouterId = routerId; + + if (Get().IsAllocated(routerId)) + { + uint16_t nextHopRloc; + uint8_t pathCost; + + Get().GetNextHopAndPathCost(Mle::Rloc16FromRouterId(routerId), nextHopRloc, pathCost); + + entry.mNextHop = (nextHopRloc == Mle::kInvalidRloc16) ? kNoNextHop : Mle::RouterIdFromRloc16(nextHopRloc); + entry.mPathCost = (pathCost < Mle::kMaxRouteCost) ? pathCost : 0; + + if (!oldEntry.mIsAllocated) + { + entry.mEvent = kRouterAdded; + entry.mOldPathCost = 0; + } + else if (oldEntry.mNextHop != entry.mNextHop) + { + entry.mEvent = kRouterNextHopChanged; + entry.mOldPathCost = oldEntry.mPathCost; + } + else if ((entry.mNextHop != kNoNextHop) && (oldEntry.mPathCost != entry.mPathCost)) + { + entry.mEvent = kRouterCostChanged; + entry.mOldPathCost = oldEntry.mPathCost; + } + else + { + continue; + } + + mRouterHistory.AddNewEntry(entry); + + oldEntry.mIsAllocated = true; + oldEntry.mNextHop = entry.mNextHop; + oldEntry.mPathCost = entry.mPathCost; + } + else + { + // `routerId` is not allocated. + + if (oldEntry.mIsAllocated) + { + entry.mEvent = kRouterRemoved; + entry.mNextHop = Mle::kInvalidRouterId; + entry.mOldPathCost = 0; + entry.mPathCost = 0; + + mRouterHistory.AddNewEntry(entry); + + oldEntry.mIsAllocated = false; + } + } + } + +#endif // (OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE > 0) +} +#endif // OPENTHREAD_FTD + #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA void HistoryTracker::RecordNetworkDataChange(void) { @@ -374,11 +449,6 @@ void HistoryTracker::HandleNotifierEvents(Events aEvents) #endif } -void HistoryTracker::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void HistoryTracker::HandleTimer(void) { mNetInfoHistory.UpdateAgedEntries(); @@ -399,7 +469,7 @@ void HistoryTracker::EntryAgeToString(uint32_t aEntryAge, char *aBuffer, uint16_ if (aEntryAge >= kMaxAge) { - writer.Append("more than %u days", kMaxAge / Time::kOneDayInMsec); + writer.Append("more than %u days", static_cast(kMaxAge / Time::kOneDayInMsec)); } else { @@ -407,14 +477,14 @@ void HistoryTracker::EntryAgeToString(uint32_t aEntryAge, char *aBuffer, uint16_ if (days > 0) { - writer.Append("%u day%s ", days, (days == 1) ? "" : "s"); + writer.Append("%lu day%s ", ToUlong(days), (days == 1) ? "" : "s"); aEntryAge -= days * Time::kOneDayInMsec; } - writer.Append("%02u:%02u:%02u.%03u", (aEntryAge / Time::kOneHourInMsec), - (aEntryAge % Time::kOneHourInMsec) / Time::kOneMinuteInMsec, - (aEntryAge % Time::kOneMinuteInMsec) / Time::kOneSecondInMsec, - (aEntryAge % Time::kOneSecondInMsec)); + writer.Append("%02u:%02u:%02u.%03u", static_cast(aEntryAge / Time::kOneHourInMsec), + static_cast((aEntryAge % Time::kOneHourInMsec) / Time::kOneMinuteInMsec), + static_cast((aEntryAge % Time::kOneMinuteInMsec) / Time::kOneSecondInMsec), + static_cast(aEntryAge % Time::kOneSecondInMsec)); } } @@ -436,7 +506,7 @@ void HistoryTracker::Timestamp::SetToNow(void) uint32_t HistoryTracker::Timestamp::GetDurationTill(TimeMilli aTime) const { - return IsDistantPast() ? kMaxAge : OT_MIN(aTime - mTime, kMaxAge); + return IsDistantPast() ? kMaxAge : Min(aTime - mTime, kMaxAge); } //--------------------------------------------------------------------------------------------------------------------- @@ -473,9 +543,9 @@ uint16_t HistoryTracker::List::Add(uint16_t aMaxSize, Timestamp aTimestamps[]) Error HistoryTracker::List::Iterate(uint16_t aMaxSize, const Timestamp aTimestamps[], - Iterator & aIterator, - uint16_t & aListIndex, - uint32_t & aEntryAge) const + Iterator &aIterator, + uint16_t &aListIndex, + uint32_t &aEntryAge) const { Error error = kErrorNone; diff --git a/src/core/utils/history_tracker.hpp b/src/core/utils/history_tracker.hpp index 3a0a2ee2dae..f1cabbf8918 100644 --- a/src/core/utils/history_tracker.hpp +++ b/src/core/utils/history_tracker.hpp @@ -54,6 +54,7 @@ #include "thread/mle_types.hpp" #include "thread/neighbor_table.hpp" #include "thread/network_data.hpp" +#include "thread/router_table.hpp" namespace ot { namespace Utils { @@ -78,6 +79,9 @@ class HistoryTracker : public InstanceLocator, private NonCopyable friend class ot::Mle::Mle; friend class ot::NeighborTable; friend class ot::Ip6::Netif; +#if OPENTHREAD_FTD + friend class ot::RouterTable; +#endif public: /** @@ -94,6 +98,14 @@ class HistoryTracker : public InstanceLocator, private NonCopyable */ static constexpr uint16_t kEntryAgeStringSize = OT_HISTORY_TRACKER_ENTRY_AGE_STRING_SIZE; + /** + * This constants specified no next hop. + * + * Used for `mNextHop` in `RouteInfo` struture. + * + */ + static constexpr uint8_t kNoNextHop = OT_HISTORY_TRACKER_NO_NEXT_HOP; + /** * This type represents an iterator to iterate through a history list. * @@ -125,6 +137,7 @@ class HistoryTracker : public InstanceLocator, private NonCopyable typedef otHistoryTrackerMulticastAddressInfo MulticastAddressInfo; ///< Multicast IPv6 address info. typedef otHistoryTrackerMessageInfo MessageInfo; ///< RX/TX IPv6 message info. typedef otHistoryTrackerNeighborInfo NeighborInfo; ///< Neighbor info. + typedef otHistoryTrackerRouterInfo RouterInfo; ///< Router info. typedef otHistoryTrackerOnMeshPrefixInfo OnMeshPrefixInfo; ///< Network Data on mesh prefix info. typedef otHistoryTrackerExternalRouteInfo ExternalRouteInfo; ///< Network Data external route info @@ -226,6 +239,11 @@ class HistoryTracker : public InstanceLocator, private NonCopyable return mNeighborHistory.Iterate(aIterator, aEntryAge); } + const RouterInfo *IterateRouterHistory(Iterator &aIterator, uint32_t &aEntryAge) const + { + return mRouterHistory.Iterate(aIterator, aEntryAge); + } + const OnMeshPrefixInfo *IterateOnMeshPrefixHistory(Iterator &aIterator, uint32_t &aEntryAge) const { return mOnMeshPrefixHistory.Iterate(aIterator, aEntryAge); @@ -265,6 +283,7 @@ class HistoryTracker : public InstanceLocator, private NonCopyable static constexpr uint16_t kRxListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_RX_LIST_SIZE; static constexpr uint16_t kTxListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_TX_LIST_SIZE; static constexpr uint16_t kNeighborListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_NEIGHBOR_LIST_SIZE; + static constexpr uint16_t kRouterListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE; static constexpr uint16_t kOnMeshPrefixListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_ON_MESH_PREFIX_LIST_SIZE; static constexpr uint16_t kExternalRouteListSize = OPENTHREAD_CONFIG_HISTORY_TRACKER_EXTERNAL_ROUTE_LIST_SIZE; @@ -273,7 +292,6 @@ class HistoryTracker : public InstanceLocator, private NonCopyable static constexpr AddressEvent kAddressAdded = OT_HISTORY_TRACKER_ADDRESS_EVENT_ADDED; static constexpr AddressEvent kAddressRemoved = OT_HISTORY_TRACKER_ADDRESS_EVENT_REMOVED; - static constexpr int8_t kInvalidRss = OT_RADIO_RSSI_INVALID; static constexpr uint16_t kInvalidRloc16 = Mac::kShortAddrInvalid; typedef otHistoryTrackerNeighborEvent NeighborEvent; @@ -283,6 +301,13 @@ class HistoryTracker : public InstanceLocator, private NonCopyable static constexpr NeighborEvent kNeighborChanged = OT_HISTORY_TRACKER_NEIGHBOR_EVENT_CHANGED; static constexpr NeighborEvent kNeighborRestoring = OT_HISTORY_TRACKER_NEIGHBOR_EVENT_RESTORING; + typedef otHistoryTrackerRouterEvent RouterEvent; + + static constexpr RouterEvent kRouterAdded = OT_HISTORY_TRACKER_ROUTER_EVENT_ADDED; + static constexpr RouterEvent kRouterRemoved = OT_HISTORY_TRACKER_ROUTER_EVENT_REMOVED; + static constexpr RouterEvent kRouterNextHopChanged = OT_HISTORY_TRACKER_ROUTER_EVENT_NEXT_HOP_CHANGED; + static constexpr RouterEvent kRouterCostChanged = OT_HISTORY_TRACKER_ROUTER_EVENT_COST_CHANGED; + typedef otHistoryTrackerNetDataEvent NetDataEvent; static constexpr NetDataEvent kNetDataEntryAdded = OT_HISTORY_TRACKER_NET_DATA_ENTRY_ADDED; @@ -316,9 +341,9 @@ class HistoryTracker : public InstanceLocator, private NonCopyable uint16_t MapEntryNumberToListIndex(uint16_t aEntryNumber, uint16_t aMaxSize) const; Error Iterate(uint16_t aMaxSize, const Timestamp aTimestamps[], - Iterator & aIterator, - uint16_t & aListIndex, - uint32_t & aEntryAge) const; + Iterator &aIterator, + uint16_t &aListIndex, + uint32_t &aEntryAge) const; private: uint16_t mStartIndex; @@ -357,7 +382,7 @@ class HistoryTracker : public InstanceLocator, private NonCopyable public: void Clear(void) {} uint16_t GetSize(void) const { return 0; } - Entry * AddNewEntry(void) { return nullptr; } + Entry *AddNewEntry(void) { return nullptr; } void AddNewEntry(const Entry &) {} const Entry *Iterate(Iterator &, uint32_t &) const { return nullptr; } void RemoveAgedEntries(void) {} @@ -379,32 +404,48 @@ class HistoryTracker : public InstanceLocator, private NonCopyable RecordMessage(aMessage, aMacDest, kTxMessage); } - void RecordNetworkInfo(void); - void RecordMessage(const Message &aMessage, const Mac::Address &aMacAddress, MessageType aType); - void RecordNeighborEvent(NeighborTable::Event aEvent, const NeighborTable::EntryInfo &aInfo); - void RecordAddressEvent(Ip6::Netif::AddressEvent aEvent, const Ip6::Netif::UnicastAddress &aUnicastAddress); - void RecordAddressEvent(Ip6::Netif::AddressEvent aEvent, - const Ip6::Netif::MulticastAddress &aMulticastAddress, - Ip6::Netif::AddressOrigin aAddressOrigin); - void HandleNotifierEvents(Events aEvents); - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); + void RecordNetworkInfo(void); + void RecordMessage(const Message &aMessage, const Mac::Address &aMacAddress, MessageType aType); + void RecordNeighborEvent(NeighborTable::Event aEvent, const NeighborTable::EntryInfo &aInfo); + void RecordAddressEvent(Ip6::Netif::AddressEvent aEvent, const Ip6::Netif::UnicastAddress &aUnicastAddress); + void RecordAddressEvent(Ip6::Netif::AddressEvent aEvent, + const Ip6::Netif::MulticastAddress &aMulticastAddress, + Ip6::Netif::AddressOrigin aAddressOrigin); + void HandleNotifierEvents(Events aEvents); + void HandleTimer(void); +#if OPENTHREAD_FTD + void RecordRouterTableChange(void); +#endif #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA void RecordNetworkDataChange(void); void RecordOnMeshPrefixEvent(NetDataEvent aEvent, const NetworkData::OnMeshPrefixConfig &aPrefix); void RecordExternalRouteEvent(NetDataEvent aEvent, const NetworkData::ExternalRouteConfig &aRoute); #endif + using TrackerTimer = TimerMilliIn; + EntryList mNetInfoHistory; EntryList mUnicastAddressHistory; EntryList mMulticastAddressHistory; EntryList mRxHistory; EntryList mTxHistory; EntryList mNeighborHistory; + EntryList mRouterHistory; EntryList mOnMeshPrefixHistory; EntryList mExternalRouteHistory; - TimerMilli mTimer; + TrackerTimer mTimer; + +#if OPENTHREAD_FTD && (OPENTHREAD_CONFIG_HISTORY_TRACKER_ROUTER_LIST_SIZE > 0) + struct RouterEntry + { + bool mIsAllocated : 1; + uint8_t mNextHop : 6; + uint8_t mPathCost : 4; + }; + + RouterEntry mRouterEntries[Mle::kMaxRouterId + 1]; +#endif #if OPENTHREAD_CONFIG_HISTORY_TRACKER_NET_DATA NetworkData::MutableNetworkData mPreviousNetworkData; @@ -419,6 +460,7 @@ DefineCoreType(otHistoryTrackerIterator, Utils::HistoryTracker::Iterator); DefineCoreType(otHistoryTrackerNetworkInfo, Utils::HistoryTracker::NetworkInfo); DefineCoreType(otHistoryTrackerMessageInfo, Utils::HistoryTracker::MessageInfo); DefineCoreType(otHistoryTrackerNeighborInfo, Utils::HistoryTracker::NeighborInfo); +DefineCoreType(otHistoryTrackerRouterInfo, Utils::HistoryTracker::RouterInfo); DefineCoreType(otHistoryTrackerOnMeshPrefixInfo, Utils::HistoryTracker::OnMeshPrefixInfo); DefineCoreType(otHistoryTrackerExternalRouteInfo, Utils::HistoryTracker::ExternalRouteInfo); diff --git a/src/core/utils/jam_detector.cpp b/src/core/utils/jam_detector.cpp index ce4a4bcf72f..a628fed6ceb 100644 --- a/src/core/utils/jam_detector.cpp +++ b/src/core/utils/jam_detector.cpp @@ -49,9 +49,7 @@ RegisterLogModule("JamDetector"); JamDetector::JamDetector(Instance &aInstance) : InstanceLocator(aInstance) - , mHandler(nullptr) - , mContext(nullptr) - , mTimer(aInstance, JamDetector::HandleTimer) + , mTimer(aInstance) , mHistoryBitmap(0) , mCurSecondStartTime(0) , mSampleInterval(0) @@ -71,8 +69,7 @@ Error JamDetector::Start(Handler aHandler, void *aContext) VerifyOrExit(!mEnabled, error = kErrorAlready); VerifyOrExit(aHandler != nullptr, error = kErrorInvalidArgs); - mHandler = aHandler; - mContext = aContext; + mCallback.Set(aHandler, aContext); mEnabled = true; LogInfo("Started"); @@ -161,11 +158,6 @@ Error JamDetector::SetBusyPeriod(uint8_t aBusyPeriod) return error; } -void JamDetector::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void JamDetector::HandleTimer(void) { int8_t rssi; @@ -177,7 +169,7 @@ void JamDetector::HandleTimer(void) // If the RSSI is valid, check if it exceeds the threshold // and try to update the history bit map - if (rssi != OT_RADIO_RSSI_INVALID) + if (rssi != Radio::kInvalidRssi) { didExceedThreshold = (rssi >= mRssiThreshold); UpdateHistory(didExceedThreshold); @@ -268,7 +260,7 @@ void JamDetector::SetJamState(bool aNewState) if (shouldInvokeHandler) { - mHandler(mJamState, mContext); + mCallback.Invoke(aNewState); } } diff --git a/src/core/utils/jam_detector.hpp b/src/core/utils/jam_detector.hpp index 98dc5e2a620..084ab777375 100644 --- a/src/core/utils/jam_detector.hpp +++ b/src/core/utils/jam_detector.hpp @@ -40,6 +40,7 @@ #include +#include "common/callback.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" #include "common/notifier.hpp" @@ -185,26 +186,26 @@ class JamDetector : public InstanceLocator, private NonCopyable static constexpr uint16_t kMinSampleInterval = 2; // in ms static constexpr uint32_t kMaxRandomDelay = 4; // in ms - void CheckState(void); - void SetJamState(bool aNewState); - static void HandleTimer(Timer &aTimer); - void HandleTimer(void); - void UpdateHistory(bool aDidExceedThreshold); - void UpdateJamState(void); - void HandleNotifierEvents(Events aEvents); - - Handler mHandler; // Handler/callback to inform about jamming state - void * mContext; // Context for handler/callback - TimerMilli mTimer; // RSSI sample timer - uint64_t mHistoryBitmap; // History bitmap, each bit correspond to 1 sec interval - TimeMilli mCurSecondStartTime; // Start time for current 1 sec interval - uint16_t mSampleInterval; // Current sample interval - uint8_t mWindow : 6; // Window (in sec) to monitor jamming - uint8_t mBusyPeriod : 6; // BusyPeriod (in sec) with mWindow to alert jamming - bool mEnabled : 1; // If jam detection is enabled - bool mAlwaysAboveThreshold : 1; // State for current 1 sec interval - bool mJamState : 1; // Current jam state - int8_t mRssiThreshold; // RSSI threshold for jam detection + void CheckState(void); + void SetJamState(bool aNewState); + void HandleTimer(void); + void UpdateHistory(bool aDidExceedThreshold); + void UpdateJamState(void); + void HandleNotifierEvents(Events aEvents); + + using SampleTimer = TimerMilliIn; + + Callback mCallback; // Callback to inform about jamming state + SampleTimer mTimer; // RSSI sample timer + uint64_t mHistoryBitmap; // History bitmap, each bit correspond to 1 sec interval + TimeMilli mCurSecondStartTime; // Start time for current 1 sec interval + uint16_t mSampleInterval; // Current sample interval + uint8_t mWindow : 6; // Window (in sec) to monitor jamming + uint8_t mBusyPeriod : 6; // BusyPeriod (in sec) with mWindow to alert jamming + bool mEnabled : 1; // If jam detection is enabled + bool mAlwaysAboveThreshold : 1; // State for current 1 sec interval + bool mJamState : 1; // Current jam state + int8_t mRssiThreshold; // RSSI threshold for jam detection }; /** diff --git a/src/core/utils/mesh_diag.cpp b/src/core/utils/mesh_diag.cpp new file mode 100644 index 00000000000..f49932990bb --- /dev/null +++ b/src/core/utils/mesh_diag.cpp @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2023, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @file + * This file implements the Mesh Diag module. + */ + +#include "mesh_diag.hpp" + +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD + +#include "common/as_core_type.hpp" +#include "common/code_utils.hpp" +#include "common/debug.hpp" +#include "common/instance.hpp" +#include "common/locator_getters.hpp" +#include "common/log.hpp" + +namespace ot { +namespace Utils { + +using namespace ot::NetworkDiagnostic; + +RegisterLogModule("MeshDiag"); + +//--------------------------------------------------------------------------------------------------------------------- +// MeshDiag + +MeshDiag::MeshDiag(Instance &aInstance) + : InstanceLocator(aInstance) + , mTimer(aInstance) +{ +} + +Error MeshDiag::DiscoverTopology(const DiscoverConfig &aConfig, DiscoverCallback aCallback, void *aContext) +{ + Error error = kErrorNone; + + VerifyOrExit(Get().IsAttached(), error = kErrorInvalidState); + VerifyOrExit(!mTimer.IsRunning(), error = kErrorBusy); + + Get().GetRouterIdSet(mExpectedRouterIdSet); + + for (uint8_t routerId = 0; routerId <= Mle::kMaxRouterId; routerId++) + { + if (mExpectedRouterIdSet.Contains(routerId)) + { + SuccessOrExit(error = SendDiagGetTo(Mle::Rloc16FromRouterId(routerId), aConfig)); + } + } + + mDiscoverCallback.Set(aCallback, aContext); + mTimer.Start(kResponseTimeout); + +exit: + return error; +} + +void MeshDiag::Cancel(void) +{ + mTimer.Stop(); + IgnoreError(Get().AbortTransaction(HandleDiagGetResponse, this)); +} + +Error MeshDiag::SendDiagGetTo(uint16_t aRloc16, const DiscoverConfig &aConfig) +{ + static constexpr uint8_t kMaxTlvsToRequest = 6; + + Error error = kErrorNone; + Coap::Message *message = nullptr; + Tmf::MessageInfo messageInfo(GetInstance()); + uint8_t tlvs[kMaxTlvsToRequest]; + uint8_t tlvsLength = 0; + + message = Get().NewConfirmablePostMessage(kUriDiagnosticGetRequest); + VerifyOrExit(message != nullptr, error = kErrorNoBufs); + + IgnoreError(message->SetPriority(Message::kPriorityLow)); + + tlvs[tlvsLength++] = Address16Tlv::kType; + tlvs[tlvsLength++] = ExtMacAddressTlv::kType; + tlvs[tlvsLength++] = RouteTlv::kType; + tlvs[tlvsLength++] = VersionTlv::kType; + + if (aConfig.mDiscoverIp6Addresses) + { + tlvs[tlvsLength++] = Ip6AddressListTlv::kType; + } + + if (aConfig.mDiscoverChildTable) + { + tlvs[tlvsLength++] = ChildTableTlv::kType; + } + + SuccessOrExit(error = Tlv::Append(*message, tlvs, tlvsLength)); + + messageInfo.SetSockAddrToRlocPeerAddrTo(aRloc16); + error = Get().SendMessage(*message, messageInfo, HandleDiagGetResponse, this); + +exit: + FreeMessageOnError(message, error); + return error; +} + +void MeshDiag::HandleDiagGetResponse(void *aContext, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, + Error aResult) +{ + static_cast(aContext)->HandleDiagGetResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo), + aResult); +} + +void MeshDiag::HandleDiagGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult) +{ + OT_UNUSED_VARIABLE(aMessageInfo); + + Error error; + RouterInfo routerInfo; + Ip6AddrIterator ip6AddrIterator; + ChildIterator childIterator; + + SuccessOrExit(aResult); + VerifyOrExit((aMessage != nullptr) && mTimer.IsRunning()); + + SuccessOrExit(routerInfo.ParseFrom(*aMessage)); + + if (ip6AddrIterator.InitFrom(*aMessage) == kErrorNone) + { + routerInfo.mIp6AddrIterator = &ip6AddrIterator; + } + + if (childIterator.InitFrom(*aMessage, routerInfo.mRloc16) == kErrorNone) + { + routerInfo.mChildIterator = &childIterator; + } + + mExpectedRouterIdSet.Remove(routerInfo.mRouterId); + + if (mExpectedRouterIdSet.GetNumberOfAllocatedIds() == 0) + { + error = kErrorNone; + mTimer.Stop(); + } + else + { + error = kErrorPending; + } + + mDiscoverCallback.InvokeIfSet(error, &routerInfo); + +exit: + return; +} + +void MeshDiag::HandleTimer(void) +{ + // Timed out waiting for response from one or more routers. + + IgnoreError(Get().AbortTransaction(HandleDiagGetResponse, this)); + + mDiscoverCallback.InvokeIfSet(kErrorResponseTimeout, nullptr); +} + +//--------------------------------------------------------------------------------------------------------------------- +// MeshDiag::RouterInfo + +Error MeshDiag::RouterInfo::ParseFrom(const Message &aMessage) +{ + Error error = kErrorNone; + Mle::Mle &mle = aMessage.Get(); + RouteTlv routeTlv; + + Clear(); + + SuccessOrExit(error = Tlv::Find(aMessage, mRloc16)); + SuccessOrExit(error = Tlv::Find(aMessage, AsCoreType(&mExtAddress))); + SuccessOrExit(error = Tlv::FindTlv(aMessage, routeTlv)); + + switch (error = Tlv::Find(aMessage, mVersion)) + { + case kErrorNone: + break; + case kErrorNotFound: + mVersion = kVersionUnknown; + error = kErrorNone; + break; + default: + ExitNow(); + } + + mRouterId = Mle::RouterIdFromRloc16(mRloc16); + mIsThisDevice = (mRloc16 == mle.GetRloc16()); + mIsThisDeviceParent = mle.IsChild() && (mRloc16 == mle.GetParent().GetRloc16()); + mIsLeader = (mRouterId == mle.GetLeaderId()); + mIsBorderRouter = aMessage.Get().ContainsBorderRouterWithRloc(mRloc16); + + for (uint8_t id = 0, index = 0; id <= Mle::kMaxRouterId; id++) + { + if (routeTlv.IsRouterIdSet(id)) + { + mLinkQualities[id] = routeTlv.GetLinkQualityIn(index); + index++; + } + } + +exit: + return error; +} + +//--------------------------------------------------------------------------------------------------------------------- +// MeshDiag::Ip6AddrIterator + +Error MeshDiag::Ip6AddrIterator::InitFrom(const Message &aMessage) +{ + Error error; + uint16_t tlvLength; + + SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, Ip6AddressListTlv::kType, mCurOffset, tlvLength)); + mEndOffset = mCurOffset + tlvLength; + mMessage = &aMessage; + +exit: + return error; +} + +Error MeshDiag::Ip6AddrIterator::GetNextAddress(Ip6::Address &aAddress) +{ + Error error = kErrorNone; + + VerifyOrExit(mMessage != nullptr, error = kErrorNotFound); + VerifyOrExit(mCurOffset + sizeof(Ip6::Address) <= mEndOffset, error = kErrorNotFound); + + IgnoreError(mMessage->Read(mCurOffset, aAddress)); + mCurOffset += sizeof(Ip6::Address); + +exit: + return error; +} + +//--------------------------------------------------------------------------------------------------------------------- +// MeshDiag::ChildIterator + +Error MeshDiag::ChildIterator::InitFrom(const Message &aMessage, uint16_t aParentRloc16) +{ + Error error; + uint16_t tlvLength; + + SuccessOrExit(error = Tlv::FindTlvValueOffset(aMessage, ChildTableTlv::kType, mCurOffset, tlvLength)); + mEndOffset = mCurOffset + tlvLength; + mMessage = &aMessage; + mParentRloc16 = aParentRloc16; + +exit: + return error; +} + +Error MeshDiag::ChildIterator::GetNextChildInfo(ChildInfo &aChildInfo) +{ + Error error = kErrorNone; + ChildTableEntry entry; + + VerifyOrExit(mMessage != nullptr, error = kErrorNotFound); + VerifyOrExit(mCurOffset + sizeof(ChildTableEntry) <= mEndOffset, error = kErrorNotFound); + + IgnoreError(mMessage->Read(mCurOffset, entry)); + mCurOffset += sizeof(ChildTableEntry); + + aChildInfo.mRloc16 = mParentRloc16 + entry.GetChildId(); + entry.GetMode().Get(aChildInfo.mMode); + aChildInfo.mLinkQuality = entry.GetLinkQuality(); + + aChildInfo.mIsThisDevice = (aChildInfo.mRloc16 == mMessage->Get().GetRloc16()); + aChildInfo.mIsBorderRouter = mMessage->Get().ContainsBorderRouterWithRloc(aChildInfo.mRloc16); + +exit: + return error; +} + +} // namespace Utils +} // namespace ot + +#endif // #if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD diff --git a/src/core/utils/mesh_diag.hpp b/src/core/utils/mesh_diag.hpp new file mode 100644 index 00000000000..931a080a762 --- /dev/null +++ b/src/core/utils/mesh_diag.hpp @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2023, 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 Mesh Diagnostic module. + */ + +#ifndef MESH_DIAG_HPP_ +#define MESH_DIAG_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD + +#include + +#include "coap/coap.hpp" +#include "common/callback.hpp" +#include "common/locator.hpp" +#include "common/message.hpp" +#include "common/timer.hpp" +#include "net/ip6_address.hpp" +#include "thread/network_diagnostic_tlvs.hpp" + +struct otMeshDiagIp6AddrIterator +{ +}; + +struct otMeshDiagChildIterator +{ +}; + +namespace ot { +namespace Utils { + +/** + * This class implements the Mesh Diagnostics. + * + */ +class MeshDiag : public InstanceLocator +{ +public: + static constexpr uint16_t kVersionUnknown = OT_MESH_DIAG_VERSION_UNKNOWN; ///< Unknown version. + + typedef otMeshDiagDiscoverConfig DiscoverConfig; ///< The discovery configuration. + typedef otMeshDiagDiscoverCallback DiscoverCallback; ///< The discovery callback function pointer type. + + /** + * This type represents an iterator to go over list of IPv6 addresses of a router. + * + */ + class Ip6AddrIterator : public otMeshDiagIp6AddrIterator + { + friend class MeshDiag; + + public: + /** + * This method iterates through the discovered IPv6 address of a router. + * + * @param[out] aIp6Address A reference to return the next IPv6 address (if any). + * + * @retval kErrorNone Successfully retrieved the next address. @p aIp6Address is updated. + * @retval kErrorNotFound No more address. Reached the end of the list. + * + */ + Error GetNextAddress(Ip6::Address &aAddress); + + private: + Error InitFrom(const Message &aMessage); + + const Message *mMessage; + uint16_t mCurOffset; + uint16_t mEndOffset; + }; + + /** + * This type represents information about a router in Thread mesh. + * + */ + class RouterInfo : public otMeshDiagRouterInfo, public Clearable + { + friend class MeshDiag; + + private: + Error ParseFrom(const Message &aMessage); + }; + + /** + * This type represents information about a child in Thread mesh. + * + */ + class ChildInfo : public otMeshDiagChildInfo, public Clearable + { + }; + + /** + * This type represents an iterator to go over list of IPv6 addresses of a router. + * + */ + class ChildIterator : public otMeshDiagChildIterator + { + friend class MeshDiag; + + public: + /** + * This method iterates through the discovered children of a router. + * + * @param[out] aChildInfo A reference to return the info for the next child (if any). + * + * @retval kErrorNone Successfully retrieved the next child info. @p aChildInfo is updated. + * @retval kErrorNotFound No more child entry. Reached the end of the list. + * + */ + Error GetNextChildInfo(ChildInfo &aChildInfo); + + private: + Error InitFrom(const Message &aMessage, uint16_t aParentRloc16); + + const Message *mMessage; + uint16_t mCurOffset; + uint16_t mEndOffset; + uint16_t mParentRloc16; + }; + + /** + * This constructor initializes the `MeshDiag` instance. + * + * @param[in] aInstance The OpenThread instance. + * + */ + explicit MeshDiag(Instance &aInstance); + + /** + * This method starts network topology discovery. + * + * @param[in] aConfig The configuration to use for discovery (e.g., which items to discover). + * @param[in] aCallback The callback to report the discovered routers. + * @param[in] aContext A context to pass in @p aCallback. + * + * @retval kErrorNone The network topology discovery started successfully. + * @retval kErrorBusy A previous discovery request is still ongoing. + * @retval kErrorInvalidState Device is not attached. + * @retval kErrorNoBufs Could not allocate buffer to send discovery messages. + * + */ + Error DiscoverTopology(const DiscoverConfig &aConfig, DiscoverCallback aCallback, void *aContext); + + /** + * This method cancels an ongoing topology discovery if there one, otherwise no action. + * + * When ongoing discovery is cancelled, the callback from `DiscoverTopology()` will not be called anymore. + * + */ + void Cancel(void); + +private: + typedef ot::NetworkDiagnostic::Tlv Tlv; + + static constexpr uint32_t kResponseTimeout = OPENTHREAD_CONFIG_MESH_DIAG_RESPONSE_TIMEOUT; + + Error SendDiagGetTo(uint16_t aRloc16, const DiscoverConfig &aConfig); + void HandleTimer(void); + void HandleDiagGetResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); + + static void HandleDiagGetResponse(void *aContext, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, + Error aResult); + + using TimeoutTimer = TimerMilliIn; + + Callback mDiscoverCallback; + Mle::RouterIdSet mExpectedRouterIdSet; + TimeoutTimer mTimer; +}; + +} // namespace Utils + +DefineCoreType(otMeshDiagIp6AddrIterator, Utils::MeshDiag::Ip6AddrIterator); +DefineCoreType(otMeshDiagRouterInfo, Utils::MeshDiag::RouterInfo); +DefineCoreType(otMeshDiagChildInfo, Utils::MeshDiag::ChildInfo); +DefineCoreType(otMeshDiagChildIterator, Utils::MeshDiag::ChildIterator); + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_MESH_DIAG_ENABLE && OPENTHREAD_FTD + +#endif // MESH_DIAG_HPP_ diff --git a/src/core/utils/otns.cpp b/src/core/utils/otns.cpp index 0030fba0c4f..f19185fb6d4 100644 --- a/src/core/utils/otns.cpp +++ b/src/core/utils/otns.cpp @@ -47,10 +47,7 @@ RegisterLogModule("Otns"); const int kMaxStatusStringLength = 128; -void Otns::EmitShortAddress(uint16_t aShortAddress) -{ - EmitStatus("rloc16=%d", aShortAddress); -} +void Otns::EmitShortAddress(uint16_t aShortAddress) { EmitStatus("rloc16=%d", aShortAddress); } void Otns::EmitExtendedAddress(const Mac::ExtAddress &aExtAddress) { diff --git a/src/core/utils/parse_cmdline.cpp b/src/core/utils/parse_cmdline.cpp index 52f77ddedd2..3f30c8337ac 100644 --- a/src/core/utils/parse_cmdline.cpp +++ b/src/core/utils/parse_cmdline.cpp @@ -44,15 +44,9 @@ namespace ot { namespace Utils { namespace CmdLineParser { -static bool IsSeparator(char aChar) -{ - return (aChar == ' ') || (aChar == '\t') || (aChar == '\r') || (aChar == '\n'); -} +static bool IsSeparator(char aChar) { return (aChar == ' ') || (aChar == '\t') || (aChar == '\r') || (aChar == '\n'); } -static bool IsEscapable(char aChar) -{ - return IsSeparator(aChar) || (aChar == '\\'); -} +static bool IsEscapable(char aChar) { return IsSeparator(aChar) || (aChar == '\\'); } static Error ParseDigit(char aDigitChar, uint8_t &aValue) { @@ -89,7 +83,7 @@ Error ParseCmd(char *aCommandString, Arg aArgs[], uint8_t aArgsMaxLength) { Error error = kErrorNone; uint8_t index = 0; - char * cmd; + char *cmd; for (cmd = aCommandString; *cmd; cmd++) { @@ -137,20 +131,11 @@ template Error ParseUint(const char *aString, UintType &aUin return error; } -Error ParseAsUint8(const char *aString, uint8_t &aUint8) -{ - return ParseUint(aString, aUint8); -} +Error ParseAsUint8(const char *aString, uint8_t &aUint8) { return ParseUint(aString, aUint8); } -Error ParseAsUint16(const char *aString, uint16_t &aUint16) -{ - return ParseUint(aString, aUint16); -} +Error ParseAsUint16(const char *aString, uint16_t &aUint16) { return ParseUint(aString, aUint16); } -Error ParseAsUint32(const char *aString, uint32_t &aUint32) -{ - return ParseUint(aString, aUint32); -} +Error ParseAsUint32(const char *aString, uint32_t &aUint32) { return ParseUint(aString, aUint32); } Error ParseAsUint64(const char *aString, uint64_t &aUint64) { @@ -161,8 +146,8 @@ Error ParseAsUint64(const char *aString, uint64_t &aUint64) enum : uint64_t { - kMaxHexBeforeOveflow = (0xffffffffffffffffULL / 16), - kMaxDecBeforeOverlow = (0xffffffffffffffffULL / 10), + kMaxHexBeforeOverflow = (0xffffffffffffffffULL / 16), + kMaxDecBeforeOverflow = (0xffffffffffffffffULL / 10), }; VerifyOrExit(aString != nullptr, error = kErrorInvalidArgs); @@ -179,7 +164,7 @@ Error ParseAsUint64(const char *aString, uint64_t &aUint64) uint64_t newValue; SuccessOrExit(error = isHex ? ParseHexDigit(*cur, digit) : ParseDigit(*cur, digit)); - VerifyOrExit(value <= (isHex ? kMaxHexBeforeOveflow : kMaxDecBeforeOverlow), error = kErrorInvalidArgs); + VerifyOrExit(value <= (isHex ? kMaxHexBeforeOverflow : kMaxDecBeforeOverflow), error = kErrorInvalidArgs); value = isHex ? (value << 4) : (value * 10); newValue = value + digit; VerifyOrExit(newValue >= value, error = kErrorInvalidArgs); @@ -208,28 +193,22 @@ template Error ParseInt(const char *aString, IntType &aInt) return error; } -Error ParseAsInt8(const char *aString, int8_t &aInt8) -{ - return ParseInt(aString, aInt8); -} +Error ParseAsInt8(const char *aString, int8_t &aInt8) { return ParseInt(aString, aInt8); } -Error ParseAsInt16(const char *aString, int16_t &aInt16) -{ - return ParseInt(aString, aInt16); -} +Error ParseAsInt16(const char *aString, int16_t &aInt16) { return ParseInt(aString, aInt16); } Error ParseAsInt32(const char *aString, int32_t &aInt32) { Error error; uint64_t value; - bool isNegavtive = false; + bool isNegative = false; VerifyOrExit(aString != nullptr, error = kErrorInvalidArgs); if (*aString == '-') { aString++; - isNegavtive = true; + isNegative = true; } else if (*aString == '+') { @@ -237,10 +216,10 @@ Error ParseAsInt32(const char *aString, int32_t &aInt32) } SuccessOrExit(error = ParseAsUint64(aString, value)); - VerifyOrExit(value <= (isNegavtive ? static_cast(-static_cast(NumericLimits::kMin)) - : static_cast(NumericLimits::kMax)), + VerifyOrExit(value <= (isNegative ? static_cast(-static_cast(NumericLimits::kMin)) + : static_cast(NumericLimits::kMax)), error = kErrorInvalidArgs); - aInt32 = static_cast(isNegavtive ? -static_cast(value) : static_cast(value)); + aInt32 = static_cast(isNegative ? -static_cast(value) : static_cast(value)); exit: return error; @@ -264,38 +243,20 @@ Error ParseAsIp6Address(const char *aString, otIp6Address &aAddress) return (aString != nullptr) ? otIp6AddressFromString(aString, &aAddress) : kErrorInvalidArgs; } -Error ParseAsIp6Prefix(const char *aString, otIp6Prefix &aPrefix) +Error ParseAsIp4Address(const char *aString, otIp4Address &aAddress) { - enum : uint8_t - { - kMaxIp6AddressStringSize = 45, - }; - - Error error = kErrorInvalidArgs; - char string[kMaxIp6AddressStringSize]; - const char *prefixLengthStr; - - VerifyOrExit(aString != nullptr); - - prefixLengthStr = StringFind(aString, '/'); - VerifyOrExit(prefixLengthStr != nullptr); - - VerifyOrExit(prefixLengthStr - aString < static_cast(sizeof(string))); - - memcpy(string, aString, static_cast(prefixLengthStr - aString)); - string[prefixLengthStr - aString] = '\0'; - - SuccessOrExit(static_cast(aPrefix.mPrefix).FromString(string)); - error = ParseAsUint8(prefixLengthStr + 1, aPrefix.mLength); + return (aString != nullptr) ? otIp4AddressFromString(aString, &aAddress) : kErrorInvalidArgs; +} -exit: - return error; +Error ParseAsIp6Prefix(const char *aString, otIp6Prefix &aPrefix) +{ + return (aString != nullptr) ? otIp6PrefixFromString(aString, &aPrefix) : kErrorInvalidArgs; } #endif // #if OPENTHREAD_FTD || OPENTHREAD_MTD enum HexStringParseMode { - kModeExtactSize, // Parse hex string expecting an exact size (number of bytes when parsed). + kModeExactSize, // Parse hex string expecting an exact size (number of bytes when parsed). kModeUpToSize, // Parse hex string expecting less than or equal a given size. kModeAllowPartial, // Allow parsing of partial segments. }; @@ -315,7 +276,7 @@ static Error ParseHexString(const char *&aString, uint16_t &aSize, uint8_t *aBuf switch (aMode) { - case kModeExtactSize: + case kModeExactSize: VerifyOrExit(expectedSize == aSize, error = kErrorInvalidArgs); break; case kModeUpToSize: @@ -369,7 +330,7 @@ static Error ParseHexString(const char *&aString, uint16_t &aSize, uint8_t *aBuf Error ParseAsHexString(const char *aString, uint8_t *aBuffer, uint16_t aSize) { - return ParseHexString(aString, aSize, aBuffer, kModeExtactSize); + return ParseHexString(aString, aSize, aBuffer, kModeExactSize); } Error ParseAsHexString(const char *aString, uint16_t &aSize, uint8_t *aBuffer) @@ -385,15 +346,9 @@ Error ParseAsHexStringSegment(const char *&aString, uint16_t &aSize, uint8_t *aB //--------------------------------------------------------------------------------------------------------------------- // Arg class -uint16_t Arg::GetLength(void) const -{ - return IsEmpty() ? 0 : static_cast(strlen(mString)); -} +uint16_t Arg::GetLength(void) const { return IsEmpty() ? 0 : static_cast(strlen(mString)); } -bool Arg::operator==(const char *aString) const -{ - return !IsEmpty() && (strcmp(mString, aString) == 0); -} +bool Arg::operator==(const char *aString) const { return !IsEmpty() && (strcmp(mString, aString) == 0); } void Arg::CopyArgsToStringArray(Arg aArgs[], char *aStrings[]) { diff --git a/src/core/utils/parse_cmdline.hpp b/src/core/utils/parse_cmdline.hpp index 6b9025f6f7c..777a50aaeb7 100644 --- a/src/core/utils/parse_cmdline.hpp +++ b/src/core/utils/parse_cmdline.hpp @@ -38,7 +38,9 @@ #include #include +#include #include +#include namespace ot { namespace Utils { @@ -182,6 +184,18 @@ otError ParseAsBool(const char *aString, bool &aBool); */ otError ParseAsIp6Address(const char *aString, otIp6Address &aAddress); +/** + * This function parses a string as an IPv4 address. + * + * @param[in] aString The string to parse. + * @param[out] aAddress A reference to an `otIp6Address` to output the parsed IPv6 address. + * + * @retval kErrorNone The string was parsed successfully. + * @retval kErrorInvalidArgs The string does not contain valid IPv4 address. + * + */ +otError ParseAsIp4Address(const char *aString, otIp4Address &aAddress); + /** * This function parses a string as an IPv6 prefix. * @@ -277,7 +291,7 @@ otError ParseAsHexString(const char *aString, uint16_t &aSize, uint8_t *aBuffer) * @param[out] aBuffer A pointer to a buffer to output the parsed byte sequence. * * @retval kErrorNone The string was parsed successfully to the end of string. - * @retval kErrorPedning The string segment was parsed successfully, but there are additional bytes remaining + * @retval kErrorPending The string segment was parsed successfully, but there are additional bytes remaining * to be parsed. * @retval kErrorInvalidArgs The string does not contain valid format hex digits. * @@ -485,6 +499,20 @@ class Arg return CmdLineParser::ParseAsIp6Address(mString, aAddress); } + /** + * This method parses the argument as an IPv4 address. + * + * @param[out] aAddress A reference to an `otIp4Address` to output the parsed IPv4 address. + * + * @retval kErrorNone The argument was parsed successfully. + * @retval kErrorInvalidArgs The argument is empty or does not contain valid IPv4 address. + * + */ + otError ParseAsIp4Address(otIp4Address &aAddress) const + { + return CmdLineParser::ParseAsIp4Address(mString, aAddress); + } + /** * This method parses the argument as an IPv6 prefix. * @@ -629,57 +657,32 @@ template inline otError ParseCmd(char *aCommandString, Arg (&a //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Specializations of `Arg::ParseAs()` method. -template <> inline otError Arg::ParseAs(uint8_t &aValue) const -{ - return ParseAsUint8(aValue); -} +template <> inline otError Arg::ParseAs(uint8_t &aValue) const { return ParseAsUint8(aValue); } -template <> inline otError Arg::ParseAs(uint16_t &aValue) const -{ - return ParseAsUint16(aValue); -} +template <> inline otError Arg::ParseAs(uint16_t &aValue) const { return ParseAsUint16(aValue); } -template <> inline otError Arg::ParseAs(uint32_t &aValue) const -{ - return ParseAsUint32(aValue); -} +template <> inline otError Arg::ParseAs(uint32_t &aValue) const { return ParseAsUint32(aValue); } -template <> inline otError Arg::ParseAs(uint64_t &aValue) const -{ - return ParseAsUint64(aValue); -} +template <> inline otError Arg::ParseAs(uint64_t &aValue) const { return ParseAsUint64(aValue); } -template <> inline otError Arg::ParseAs(bool &aValue) const -{ - return ParseAsBool(aValue); -} +template <> inline otError Arg::ParseAs(bool &aValue) const { return ParseAsBool(aValue); } -template <> inline otError Arg::ParseAs(int8_t &aValue) const -{ - return ParseAsInt8(aValue); -} +template <> inline otError Arg::ParseAs(int8_t &aValue) const { return ParseAsInt8(aValue); } -template <> inline otError Arg::ParseAs(int16_t &aValue) const -{ - return ParseAsInt16(aValue); -} +template <> inline otError Arg::ParseAs(int16_t &aValue) const { return ParseAsInt16(aValue); } + +template <> inline otError Arg::ParseAs(int32_t &aValue) const { return ParseAsInt32(aValue); } -template <> inline otError Arg::ParseAs(int32_t &aValue) const +template <> inline otError Arg::ParseAs(const char *&aValue) const { - return ParseAsInt32(aValue); + return IsEmpty() ? OT_ERROR_INVALID_ARGS : (aValue = GetCString(), OT_ERROR_NONE); } #if OPENTHREAD_FTD || OPENTHREAD_MTD -template <> inline otError Arg::ParseAs(otIp6Address &aValue) const -{ - return ParseAsIp6Address(aValue); -} +template <> inline otError Arg::ParseAs(otIp6Address &aValue) const { return ParseAsIp6Address(aValue); } -template <> inline otError Arg::ParseAs(otIp6Prefix &aValue) const -{ - return ParseAsIp6Prefix(aValue); -} +template <> inline otError Arg::ParseAs(otIp6Prefix &aValue) const { return ParseAsIp6Prefix(aValue); } #endif diff --git a/src/core/utils/ping_sender.cpp b/src/core/utils/ping_sender.cpp index a0fe9c6e1ec..d1565ef7856 100644 --- a/src/core/utils/ping_sender.cpp +++ b/src/core/utils/ping_sender.cpp @@ -38,6 +38,7 @@ #include "common/as_core_type.hpp" #include "common/encoding.hpp" #include "common/locator_getters.hpp" +#include "common/num_utils.hpp" #include "common/random.hpp" namespace ot { @@ -90,7 +91,7 @@ PingSender::PingSender(Instance &aInstance) : InstanceLocator(aInstance) , mIdentifier(0) , mTargetEchoSequence(0) - , mTimer(aInstance, PingSender::HandleTimer) + , mTimer(aInstance) , mIcmpHandler(PingSender::HandleIcmpReceive, this) { IgnoreError(Get().RegisterHandler(mIcmpHandler)); @@ -126,7 +127,7 @@ void PingSender::Stop(void) void PingSender::SendPing(void) { TimeMilli now = TimerMilli::GetNow(); - Message * message = nullptr; + Message *message = nullptr; Ip6::MessageInfo messageInfo; messageInfo.SetSockAddr(mConfig.GetSource()); @@ -134,7 +135,7 @@ void PingSender::SendPing(void) messageInfo.mHopLimit = mConfig.mHopLimit; messageInfo.mAllowZeroHopLimit = mConfig.mAllowZeroHopLimit; - message = Get().NewMessage(0); + message = Get().NewMessage(); VerifyOrExit(message != nullptr); SuccessOrExit(message->Append(HostSwap32(now.GetValue()))); @@ -168,11 +169,6 @@ void PingSender::SendPing(void) } } -void PingSender::HandleTimer(Timer &aTimer) -{ - aTimer.Get().HandleTimer(); -} - void PingSender::HandleTimer(void) { if (mConfig.mCount > 0) @@ -185,8 +181,8 @@ void PingSender::HandleTimer(void) } } -void PingSender::HandleIcmpReceive(void * aContext, - otMessage * aMessage, +void PingSender::HandleIcmpReceive(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, const otIcmp6Header *aIcmpHeader) { @@ -194,8 +190,8 @@ void PingSender::HandleIcmpReceive(void * aContext, AsCoreType(aIcmpHeader)); } -void PingSender::HandleIcmpReceive(const Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, +void PingSender::HandleIcmpReceive(const Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, const Ip6::Icmp::Header &aIcmpHeader) { Reply reply; @@ -208,17 +204,16 @@ void PingSender::HandleIcmpReceive(const Message & aMessage, SuccessOrExit(aMessage.Read(aMessage.GetOffset(), timestamp)); timestamp = HostSwap32(timestamp); - reply.mSenderAddress = aMessageInfo.GetPeerAddr(); - reply.mRoundTripTime = - static_cast(OT_MIN(TimerMilli::GetNow() - TimeMilli(timestamp), NumericLimits::kMax)); + reply.mSenderAddress = aMessageInfo.GetPeerAddr(); + reply.mRoundTripTime = ClampToUint16(TimerMilli::GetNow() - TimeMilli(timestamp)); reply.mSize = aMessage.GetLength() - aMessage.GetOffset(); reply.mSequenceNumber = aIcmpHeader.GetSequence(); reply.mHopLimit = aMessageInfo.GetHopLimit(); mStatistics.mReceivedCount++; mStatistics.mTotalRoundTripTime += reply.mRoundTripTime; - mStatistics.mMaxRoundTripTime = OT_MAX(mStatistics.mMaxRoundTripTime, reply.mRoundTripTime); - mStatistics.mMinRoundTripTime = OT_MIN(mStatistics.mMinRoundTripTime, reply.mRoundTripTime); + mStatistics.mMaxRoundTripTime = Max(mStatistics.mMaxRoundTripTime, reply.mRoundTripTime); + mStatistics.mMinRoundTripTime = Min(mStatistics.mMinRoundTripTime, reply.mRoundTripTime); #if OPENTHREAD_CONFIG_OTNS_ENABLE Get().EmitPingReply(aMessageInfo.GetPeerAddr(), reply.mSize, timestamp, reply.mHopLimit); diff --git a/src/core/utils/ping_sender.hpp b/src/core/utils/ping_sender.hpp index aae63c75e76..9eb0a9129f2 100644 --- a/src/core/utils/ping_sender.hpp +++ b/src/core/utils/ping_sender.hpp @@ -130,7 +130,7 @@ class PingSender : public InstanceLocator, private NonCopyable private: static constexpr uint16_t kDefaultSize = OPENTHREAD_CONFIG_PING_SENDER_DEFAULT_SIZE; static constexpr uint16_t kDefaultCount = OPENTHREAD_CONFIG_PING_SENDER_DEFAULT_COUNT; - static constexpr uint32_t kDefaultInterval = OPENTHREAD_CONFIG_PING_SENDER_DEFAULT_INTEVRAL; + static constexpr uint32_t kDefaultInterval = OPENTHREAD_CONFIG_PING_SENDER_DEFAULT_INTERVAL; static constexpr uint32_t kDefaultTimeout = OPENTHREAD_CONFIG_PING_SENDER_DEFAULT_TIMEOUT; void SetUnspecifiedToDefault(void); @@ -166,21 +166,22 @@ class PingSender : public InstanceLocator, private NonCopyable private: void SendPing(void); - static void HandleTimer(Timer &aTimer); void HandleTimer(void); - static void HandleIcmpReceive(void * aContext, - otMessage * aMessage, + static void HandleIcmpReceive(void *aContext, + otMessage *aMessage, const otMessageInfo *aMessageInfo, const otIcmp6Header *aIcmpHeader); - void HandleIcmpReceive(const Message & aMessage, - const Ip6::MessageInfo & aMessageInfo, + void HandleIcmpReceive(const Message &aMessage, + const Ip6::MessageInfo &aMessageInfo, const Ip6::Icmp::Header &aIcmpHeader); + using PingTimer = TimerMilliIn; + Config mConfig; Statistics mStatistics; uint16_t mIdentifier; uint16_t mTargetEchoSequence; - TimerMilli mTimer; + PingTimer mTimer; Ip6::Icmp::Handler mIcmpHandler; }; diff --git a/src/core/utils/power_calibration.cpp b/src/core/utils/power_calibration.cpp new file mode 100644 index 00000000000..0818d044086 --- /dev/null +++ b/src/core/utils/power_calibration.cpp @@ -0,0 +1,245 @@ +/* + * 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. + */ + +#include "power_calibration.hpp" + +#if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +#include + +#include "common/as_core_type.hpp" +#include "common/code_utils.hpp" +#include "common/locator_getters.hpp" + +namespace ot { +namespace Utils { + +PowerCalibration::PowerCalibration(Instance &aInstance) + : InstanceLocator(aInstance) + , mLastChannel(0) + , mCalibratedPowerIndex(kInvalidIndex) +{ + for (int16_t &targetPower : mTargetPowerTable) + { + targetPower = kInvalidPower; + } +} + +void PowerCalibration::CalibratedPowerEntry::Init(int16_t aActualPower, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength) +{ + AssertPointerIsNotNull(aRawPowerSetting); + OT_ASSERT(aRawPowerSettingLength <= kMaxRawPowerSettingSize); + + mActualPower = aActualPower; + mLength = aRawPowerSettingLength; + memcpy(mSettings, aRawPowerSetting, aRawPowerSettingLength); +} + +Error PowerCalibration::CalibratedPowerEntry::GetRawPowerSetting(uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) +{ + Error error = kErrorNone; + + AssertPointerIsNotNull(aRawPowerSetting); + AssertPointerIsNotNull(aRawPowerSettingLength); + VerifyOrExit(*aRawPowerSettingLength >= mLength, error = kErrorInvalidArgs); + + memcpy(aRawPowerSetting, mSettings, mLength); + *aRawPowerSettingLength = mLength; + +exit: + return error; +} + +Error PowerCalibration::AddCalibratedPower(uint8_t aChannel, + int16_t aActualPower, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength) +{ + Error error = kErrorNone; + CalibratedPowerEntry entry; + uint8_t chIndex; + + AssertPointerIsNotNull(aRawPowerSetting); + VerifyOrExit(IsChannelValid(aChannel) && aRawPowerSettingLength <= CalibratedPowerEntry::kMaxRawPowerSettingSize, + error = kErrorInvalidArgs); + + chIndex = aChannel - Radio::kChannelMin; + VerifyOrExit(!mCalibratedPowerTables[chIndex].ContainsMatching(aActualPower), error = kErrorInvalidArgs); + VerifyOrExit(!mCalibratedPowerTables[chIndex].IsFull(), error = kErrorNoBufs); + + entry.Init(aActualPower, aRawPowerSetting, aRawPowerSettingLength); + SuccessOrExit(error = mCalibratedPowerTables[chIndex].PushBack(entry)); + + if (aChannel == mLastChannel) + { + mCalibratedPowerIndex = kInvalidIndex; + } + +exit: + return error; +} + +void PowerCalibration::ClearCalibratedPowers(void) +{ + for (CalibratedPowerTable &table : mCalibratedPowerTables) + { + table.Clear(); + } + + mCalibratedPowerIndex = kInvalidIndex; +} + +Error PowerCalibration::SetChannelTargetPower(uint8_t aChannel, int16_t aTargetPower) +{ + Error error = kErrorNone; + + VerifyOrExit(IsChannelValid(aChannel), error = kErrorInvalidArgs); + mTargetPowerTable[aChannel - Radio::kChannelMin] = aTargetPower; + + if (aChannel == mLastChannel) + { + mCalibratedPowerIndex = kInvalidIndex; + } + +exit: + return error; +} + +Error PowerCalibration::GetPowerSettings(uint8_t aChannel, + int16_t *aTargetPower, + int16_t *aActualPower, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) +{ + Error error = kErrorNone; + uint8_t chIndex; + uint8_t powerIndex = kInvalidIndex; + int16_t foundPower = kInvalidPower; + int16_t targetPower; + int16_t actualPower; + + VerifyOrExit(IsChannelValid(aChannel), error = kErrorInvalidArgs); + VerifyOrExit((mLastChannel != aChannel) || IsPowerUpdated()); + + chIndex = aChannel - Radio::kChannelMin; + targetPower = mTargetPowerTable[chIndex]; + VerifyOrExit(targetPower != kInvalidPower, error = kErrorNotFound); + + for (uint8_t i = 0; i < mCalibratedPowerTables[chIndex].GetLength(); i++) + { + actualPower = mCalibratedPowerTables[chIndex][i].GetActualPower(); + + if ((actualPower <= targetPower) && ((foundPower == kInvalidPower) || (foundPower <= actualPower))) + { + foundPower = actualPower; + powerIndex = i; + } + } + + VerifyOrExit(powerIndex != kInvalidIndex, error = kErrorNotFound); + + mCalibratedPowerIndex = powerIndex; + mLastChannel = aChannel; + +exit: + if (error == kErrorNone) + { + chIndex = mLastChannel - Radio::kChannelMin; + + if (aTargetPower != nullptr) + { + *aTargetPower = mTargetPowerTable[chIndex]; + } + + if (aActualPower != nullptr) + { + *aActualPower = mCalibratedPowerTables[chIndex][mCalibratedPowerIndex].GetActualPower(); + } + + error = mCalibratedPowerTables[chIndex][mCalibratedPowerIndex].GetRawPowerSetting(aRawPowerSetting, + aRawPowerSettingLength); + } + + return error; +} +} // namespace Utils +} // namespace ot + +using namespace ot; + +otError otPlatRadioAddCalibratedPower(otInstance *aInstance, + uint8_t aChannel, + int16_t aActualPower, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength) +{ + return AsCoreType(aInstance).Get().AddCalibratedPower( + aChannel, aActualPower, aRawPowerSetting, aRawPowerSettingLength); +} + +otError otPlatRadioClearCalibratedPowers(otInstance *aInstance) +{ + AsCoreType(aInstance).Get().ClearCalibratedPowers(); + return OT_ERROR_NONE; +} + +otError otPlatRadioSetChannelTargetPower(otInstance *aInstance, uint8_t aChannel, int16_t aTargetPower) +{ + return AsCoreType(aInstance).Get().SetChannelTargetPower(aChannel, aTargetPower); +} + +otError otPlatRadioGetRawPowerSetting(otInstance *aInstance, + uint8_t aChannel, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) +{ + AssertPointerIsNotNull(aRawPowerSetting); + AssertPointerIsNotNull(aRawPowerSettingLength); + + return AsCoreType(aInstance).Get().GetPowerSettings( + aChannel, nullptr, nullptr, aRawPowerSetting, aRawPowerSettingLength); +} + +otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance, + uint8_t aChannel, + int16_t *aTargetPower, + int16_t *aActualPower, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) +{ + OT_UNUSED_VARIABLE(aInstance); + + AssertPointerIsNotNull(aRawPowerSetting); + AssertPointerIsNotNull(aRawPowerSettingLength); + + return AsCoreType(aInstance).Get().GetPowerSettings( + aChannel, aTargetPower, aActualPower, aRawPowerSetting, aRawPowerSettingLength); +} +#endif // OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE diff --git a/src/core/utils/power_calibration.hpp b/src/core/utils/power_calibration.hpp new file mode 100644 index 00000000000..e16775b3a3f --- /dev/null +++ b/src/core/utils/power_calibration.hpp @@ -0,0 +1,177 @@ +/* + * 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 + * @brief + * This file includes definitions for the platform power calibration module. + * + */ +#ifndef POWER_CALIBRATION_HPP_ +#define POWER_CALIBRATION_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + +#include + +#include "common/array.hpp" +#include "common/numeric_limits.hpp" +#include "radio/radio.hpp" + +namespace ot { +namespace Utils { + +/** + * This class implements power calibration module. + * + * The power calibration module implements the radio platform power calibration APIs. It mainly stores the calibrated + * power table and the target power table, provides an API for the platform to get the raw power setting of the + * specified channel. + * + */ +class PowerCalibration : public InstanceLocator, private NonCopyable +{ +public: + explicit PowerCalibration(Instance &aInstance); + + /** + * Add a calibrated power of the specified channel to the power calibration table. + * + * @param[in] aChannel The radio channel. + * @param[in] aActualPower The actual power in 0.01dBm. + * @param[in] aRawPowerSetting A pointer to the raw power setting byte array. + * @param[in] aRawPowerSettingLength The length of the @p aRawPowerSetting. + * + * @retval kErrorNone Successfully added the calibrated power to the power calibration table. + * @retval kErrorNoBufs No available entry in the power calibration table. + * @retval kErrorInvalidArgs The @p aChannel, @p aActualPower or @p aRawPowerSetting is invalid or the + * @ aActualPower already exists in the power calibration table. + * + */ + Error AddCalibratedPower(uint8_t aChannel, + int16_t aActualPower, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength); + + /** + * Clear all calibrated powers from the power calibration table. + * + */ + void ClearCalibratedPowers(void); + + /** + * Set the target power for the given channel. + * + * @param[in] aChannel The radio channel. + * @param[in] aTargetPower The target power in 0.01dBm. Passing `INT16_MAX` will disable this channel. + * + * @retval kErrorNone Successfully set the target power. + * @retval kErrorInvalidArgs The @p aChannel or @p aTargetPower is invalid. + * + */ + Error SetChannelTargetPower(uint8_t aChannel, int16_t aTargetPower); + + /** + * Get the power settings for the given channel. + * + * Platform radio layer should parse the raw power setting based on the radio layer defined format and set the + * parameters of each radio hardware module. + * + * @param[in] aChannel The radio channel. + * @param[out] aTargetPower A pointer to the target power in 0.01 dBm. May be set to nullptr if + * the caller doesn't want to get the target power. + * @param[out] aActualPower A pointer to the actual power in 0.01 dBm. May be set to nullptr if + * the caller doesn't want to get the actual power. + * @param[out] aRawPowerSetting A pointer to the raw power setting byte array. + * @param[in,out] aRawPowerSettingLength On input, a pointer to the size of @p aRawPowerSetting. + * On output, a pointer to the length of the raw power setting data. + * + * @retval kErrorNone Successfully got the target power. + * @retval kErrorInvalidArgs The @p aChannel is invalid, @p aRawPowerSetting or @p aRawPowerSettingLength is + * nullptr or @aRawPowerSettingLength is too short. + * @retval kErrorNotFound The power settings for the @p aChannel was not found. + * + */ + Error GetPowerSettings(uint8_t aChannel, + int16_t *aTargetPower, + int16_t *aActualPower, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength); + +private: + class CalibratedPowerEntry + { + public: + static constexpr uint16_t kMaxRawPowerSettingSize = OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE; + + CalibratedPowerEntry(void) + : mActualPower(kInvalidPower) + , mLength(0) + { + } + + void Init(int16_t aActualPower, const uint8_t *aRawPowerSetting, uint16_t aRawPowerSettingLength); + Error GetRawPowerSetting(uint8_t *aRawPowerSetting, uint16_t *aRawPowerSettingLength); + int16_t GetActualPower(void) const { return mActualPower; } + bool Matches(int16_t aActualPower) const { return aActualPower == mActualPower; } + + private: + int16_t mActualPower; + uint8_t mSettings[kMaxRawPowerSettingSize]; + uint16_t mLength; + }; + + bool IsPowerUpdated(void) const { return mCalibratedPowerIndex == kInvalidIndex; } + bool IsChannelValid(uint8_t aChannel) const + { + return ((aChannel >= Radio::kChannelMin) && (aChannel <= Radio::kChannelMax)); + } + + static constexpr uint8_t kInvalidIndex = NumericLimits::kMax; + static constexpr uint16_t kInvalidPower = NumericLimits::kMax; + static constexpr uint16_t kMaxNumCalibratedPowers = + OPENTHREAD_CONFIG_POWER_CALIBRATION_NUM_CALIBRATED_POWER_ENTRIES; + static constexpr uint16_t kNumChannels = Radio::kChannelMax - Radio::kChannelMin + 1; + + static_assert(kMaxNumCalibratedPowers < NumericLimits::kMax, + "kMaxNumCalibratedPowers is larger than or equal to max"); + + typedef Array CalibratedPowerTable; + + uint8_t mLastChannel; + int16_t mTargetPowerTable[kNumChannels]; + uint8_t mCalibratedPowerIndex; + CalibratedPowerTable mCalibratedPowerTables[kNumChannels]; +}; +} // namespace Utils +} // namespace ot + +#endif // OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +#endif // POWER_CALIBRATION_HPP_ diff --git a/src/core/utils/slaac_address.cpp b/src/core/utils/slaac_address.cpp index 82cadfcba45..825130a178c 100644 --- a/src/core/utils/slaac_address.cpp +++ b/src/core/utils/slaac_address.cpp @@ -136,7 +136,7 @@ void Slaac::HandleNotifierEvents(Events aEvents) } bool Slaac::DoesConfigMatchNetifAddr(const NetworkData::OnMeshPrefixConfig &aConfig, - const Ip6::Netif::UnicastAddress & aAddr) + const Ip6::Netif::UnicastAddress &aAddr) { return (((aConfig.mOnMesh && (aAddr.mPrefixLength == aConfig.mPrefix.mLength)) || (!aConfig.mOnMesh && (aAddr.mPrefixLength == 128))) && @@ -204,7 +204,8 @@ void Slaac::Update(UpdateMode aMode) { Ip6::Prefix &prefix = config.GetPrefix(); - if (config.mDp || !config.mSlaac || ShouldFilter(prefix)) + if (config.mDp || !config.mSlaac || (prefix.GetLength() != Ip6::NetworkPrefix::kLength) || + ShouldFilter(prefix)) { continue; } @@ -255,9 +256,9 @@ void Slaac::Update(UpdateMode aMode) } Error Slaac::GenerateIid(Ip6::Netif::UnicastAddress &aAddress, - uint8_t * aNetworkId, + uint8_t *aNetworkId, uint8_t aNetworkIdLength, - uint8_t * aDadCounter) const + uint8_t *aDadCounter) const { /* * This method generates a semantically opaque IID per RFC 7217. diff --git a/src/core/utils/slaac_address.hpp b/src/core/utils/slaac_address.hpp index db7913b3690..1784d33ac16 100644 --- a/src/core/utils/slaac_address.hpp +++ b/src/core/utils/slaac_address.hpp @@ -132,16 +132,16 @@ class Slaac : public InstanceLocator, private NonCopyable * @param[in] aNetworkId A pointer to a byte array of Network_ID to generate IID. * @param[in] aNetworkIdLength The size of array @p aNetworkId. * @param[in,out] aDadCounter A pointer to the DAD_Counter that is employed to resolve Duplicate - * Address Detection connflicts. + * Address Detection conflicts. * * @retval kErrorNone If successfully generated the IID. * @retval kErrorFailed If no valid IID was generated. * */ Error GenerateIid(Ip6::Netif::UnicastAddress &aAddress, - uint8_t * aNetworkId = nullptr, + uint8_t *aNetworkId = nullptr, uint8_t aNetworkIdLength = 0, - uint8_t * aDadCounter = nullptr) const; + uint8_t *aDadCounter = nullptr) const; private: static constexpr uint16_t kMaxIidCreationAttempts = 256; // Maximum number of attempts when generating IID. @@ -163,7 +163,7 @@ class Slaac : public InstanceLocator, private NonCopyable void GetIidSecretKey(IidSecretKey &aKey) const; void HandleNotifierEvents(Events aEvents); static bool DoesConfigMatchNetifAddr(const NetworkData::OnMeshPrefixConfig &aConfig, - const Ip6::Netif::UnicastAddress & aAddr); + const Ip6::Netif::UnicastAddress &aAddr); bool mEnabled; otIp6SlaacPrefixFilter mFilter; diff --git a/src/core/utils/srp_client_buffers.hpp b/src/core/utils/srp_client_buffers.hpp index 7746de88df5..a60be7b73bd 100644 --- a/src/core/utils/srp_client_buffers.hpp +++ b/src/core/utils/srp_client_buffers.hpp @@ -171,7 +171,7 @@ class SrpClientBuffers : public InstanceLocator, private NonCopyable } private: - ServiceEntry * GetNext(void) { return reinterpret_cast(mService.mNext); } + ServiceEntry *GetNext(void) { return reinterpret_cast(mService.mNext); } const ServiceEntry *GetNext(void) const { return reinterpret_cast(mService.mNext); } void SetNext(ServiceEntry *aEntry) { mService.mNext = reinterpret_cast(aEntry); } diff --git a/src/lib/hdlc/hdlc.hpp b/src/lib/hdlc/hdlc.hpp index 84581d1ee6b..eb80f6f2c30 100644 --- a/src/lib/hdlc/hdlc.hpp +++ b/src/lib/hdlc/hdlc.hpp @@ -584,7 +584,7 @@ class Decoder State mState; FrameWritePointer &mWritePointer; FrameHandler mFrameHandler; - void * mContext; + void *mContext; uint16_t mFcs; uint16_t mDecodedLength; }; diff --git a/src/lib/spinel/CMakeLists.txt b/src/lib/spinel/CMakeLists.txt index 85114f78ffa..0e41c879845 100644 --- a/src/lib/spinel/CMakeLists.txt +++ b/src/lib/spinel/CMakeLists.txt @@ -31,13 +31,19 @@ add_library(openthread-spinel-rcp) target_compile_definitions(openthread-spinel-ncp PRIVATE OPENTHREAD_FTD=1 - OPENTHREAD_CONFIG_NCP_HDLC_ENABLE=1 PUBLIC OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE=1 ) +if (OT_NCP_SPI) + target_compile_definitions(openthread-spinel-ncp PRIVATE OPENTHREAD_CONFIG_NCP_HDLC_ENABLE=0) + target_compile_definitions(openthread-spinel-rcp PRIVATE OPENTHREAD_CONFIG_NCP_HDLC_ENABLE=0) +else() + target_compile_definitions(openthread-spinel-ncp PRIVATE OPENTHREAD_CONFIG_NCP_HDLC_ENABLE=1) + target_compile_definitions(openthread-spinel-rcp PRIVATE OPENTHREAD_CONFIG_NCP_HDLC_ENABLE=1) +endif() + target_compile_definitions(openthread-spinel-rcp PRIVATE OPENTHREAD_RADIO=1 - OPENTHREAD_CONFIG_NCP_HDLC_ENABLE=1 PUBLIC OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE=0 ) @@ -67,5 +73,24 @@ target_include_directories(openthread-spinel-rcp PUBLIC ${OT_PUBLIC_INCLUDES} PR target_sources(openthread-spinel-ncp PRIVATE ${COMMON_SOURCES}) target_sources(openthread-spinel-rcp PRIVATE ${COMMON_SOURCES}) -target_link_libraries(openthread-spinel-ncp PRIVATE ot-config) -target_link_libraries(openthread-spinel-rcp PRIVATE ot-config) +target_link_libraries(openthread-spinel-ncp + PRIVATE + ot-config-ftd + ot-config +) + +target_link_libraries(openthread-spinel-rcp + PRIVATE + ot-config-radio + ot-config +) + +if(BUILD_TESTING) + add_executable(ot-test-spinel + spinel.c + ) + target_compile_definitions(ot-test-spinel + PRIVATE -DSPINEL_SELF_TEST=1 -D_GNU_SOURCE + ) + add_test(NAME ot-test-spinel COMMAND ot-test-spinel) +endif() diff --git a/src/lib/spinel/Makefile.am b/src/lib/spinel/Makefile.am index 04061f6037f..1540424fb2a 100644 --- a/src/lib/spinel/Makefile.am +++ b/src/lib/spinel/Makefile.am @@ -99,18 +99,6 @@ libopenthread_spinel_rcp_a_SOURCES = \ if OPENTHREAD_BUILD_TESTS -check_PROGRAMS = spinel-test -spinel_test_SOURCES = spinel.c -spinel_test_CFLAGS = \ - $(COMMON_CPPFLAGS) \ - -DSPINEL_SELF_TEST=1 \ - -D_GNU_SOURCE \ - -I$(top_srcdir)/src/core \ - -I$(top_srcdir)/include \ - $(NULL) - -TESTS = spinel-test - install-headers: install-includeHEADERS if OPENTHREAD_BUILD_COVERAGE diff --git a/src/lib/spinel/openthread-spinel-config.h b/src/lib/spinel/openthread-spinel-config.h index 3cd91f24376..d8dec50ba92 100644 --- a/src/lib/spinel/openthread-spinel-config.h +++ b/src/lib/spinel/openthread-spinel-config.h @@ -34,6 +34,8 @@ #ifndef OPENTHREAD_SPINEL_CONFIG_H_ #define OPENTHREAD_SPINEL_CONFIG_H_ +#include "openthread-core-config.h" + /** * @def OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE * @@ -55,4 +57,13 @@ #define OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT 0 #endif +/** + * @def OPENTHREAD_SPINEL_CONFIG_ABORT_ON_UNEXPECTED_RCP_RESET_ENABLE + * + * Define 1 to abort the host when receiving unexpected reset from RCP. + * + */ +#ifndef OPENTHREAD_SPINEL_CONFIG_ABORT_ON_UNEXPECTED_RCP_RESET_ENABLE +#define OPENTHREAD_SPINEL_CONFIG_ABORT_ON_UNEXPECTED_RCP_RESET_ENABLE 0 +#endif #endif // OPENTHREAD_SPINEL_CONFIG_H_ diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index b0d3b463fe7..d14f7c0aaa2 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -648,10 +648,12 @@ template class RadioSpinel /** * This method sets the current MAC Frame Counter value. * - * @param[in] aMacFrameCounter The MAC Frame Counter value. + * @param[in] aMacFrameCounter The MAC Frame Counter value. + * @param[in] aSetIfLarger If `true`, set only if the new value is larger than the current value. + * If `false`, set the new value independent of the current value. * */ - otError SetMacFrameCounter(uint32_t aMacFrameCounter); + otError SetMacFrameCounter(uint32_t aMacFrameCounter, bool aSetIfLarger); /** * This method sets the radio region code. @@ -699,7 +701,7 @@ template class RadioSpinel */ otError ConfigureEnhAckProbing(otLinkMetrics aLinkMetrics, const otShortAddress aShortAddress, - const otExtAddress & aExtAddress); + const otExtAddress &aExtAddress); #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE @@ -727,14 +729,16 @@ template class RadioSpinel /** * This method checks whether the spinel interface is radio-only. * - * @param[out] aSupportsRcpApiVersion A reference to a boolean variable to update whether the list of spinel - * capabilities include `SPINEL_CAP_RCP_API_VERSION`. + * @param[out] aSupportsRcpApiVersion A reference to a boolean variable to update whether the list of + * spinel capabilities includes `SPINEL_CAP_RCP_API_VERSION`. + * @param[out] aSupportsRcpMinHostApiVersion A reference to a boolean variable to update whether the list of + * spinel capabilities includes `SPINEL_CAP_RCP_MIN_HOST_API_VERSION`. * * @retval TRUE The radio chip is in radio-only mode. * @retval FALSE Otherwise. * */ - bool IsRcp(bool &aSupportsRcpApiVersion); + bool IsRcp(bool &aSupportsRcpApiVersion, bool &aSupportsRcpMinHostApiVersion); /** * This method checks whether there is pending frame in the buffer. @@ -820,9 +824,9 @@ template class RadioSpinel * */ otError GetWithParam(spinel_prop_key_t aKey, - const uint8_t * aParam, + const uint8_t *aParam, spinel_size_t aParamSize, - const char * aFormat, + const char *aFormat, ...); /** @@ -886,6 +890,55 @@ template class RadioSpinel */ const otRadioSpinelMetrics *GetRadioSpinelMetrics(void) const { return &mRadioSpinelMetrics; } +#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + /** + * Add a calibrated power of the specified channel to the power calibration table. + * + * @param[in] aChannel The radio channel. + * @param[in] aActualPower The actual power in 0.01dBm. + * @param[in] aRawPowerSetting A pointer to the raw power setting byte array. + * @param[in] aRawPowerSettingLength The length of the @p aRawPowerSetting. + * + * @retval OT_ERROR_NONE Successfully added the calibrated power to the power calibration table. + * @retval OT_ERROR_NO_BUFS No available entry in the power calibration table. + * @retval OT_ERROR_INVALID_ARGS The @p aChannel, @p aActualPower or @p aRawPowerSetting is invalid. + * @retval OT_ERROR_NOT_IMPLEMENTED This feature is not implemented. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError AddCalibratedPower(uint8_t aChannel, + int16_t aActualPower, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength); + + /** + * Clear all calibrated powers from the power calibration table. + * + * @retval OT_ERROR_NONE Successfully cleared all calibrated powers from the power calibration table. + * @retval OT_ERROR_NOT_IMPLEMENTED This feature is not implemented. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError ClearCalibratedPowers(void); + + /** + * Set the target power for the given channel. + * + * @param[in] aChannel The radio channel. + * @param[in] aTargetPower The target power in 0.01dBm. Passing `INT16_MAX` will disable this channel. + * + * @retval OT_ERROR_NONE Successfully set the target power. + * @retval OT_ERROR_INVALID_ARGS The @p aChannel or @p aTargetPower is invalid.. + * @retval OT_ERROR_NOT_IMPLEMENTED The feature is not implemented. + * @retval OT_ERROR_BUSY Failed due to another operation is on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * + */ + otError SetChannelTargetPower(uint8_t aChannel, int16_t aTargetPower); +#endif + private: enum { @@ -909,9 +962,10 @@ template class RadioSpinel static void HandleReceivedFrame(void *aContext); + void ResetRcp(bool aResetRadio); otError CheckSpinelVersion(void); otError CheckRadioCapabilities(void); - otError CheckRcpApiVersion(bool aSupportsRcpApiVersion); + otError CheckRcpApiVersion(bool aSupportsRcpApiVersion, bool aSupportsMinHostRcpApiVersion); /** * This method triggers a state transfer of the state machine. @@ -930,26 +984,26 @@ template class RadioSpinel otError RequestV(uint32_t aCommand, spinel_prop_key_t aKey, const char *aFormat, va_list aArgs); otError Request(uint32_t aCommand, spinel_prop_key_t aKey, const char *aFormat, ...); - otError RequestWithPropertyFormat(const char * aPropertyFormat, + otError RequestWithPropertyFormat(const char *aPropertyFormat, uint32_t aCommand, spinel_prop_key_t aKey, - const char * aFormat, + const char *aFormat, ...); - otError RequestWithPropertyFormatV(const char * aPropertyFormat, + otError RequestWithPropertyFormatV(const char *aPropertyFormat, uint32_t aCommand, spinel_prop_key_t aKey, - const char * aFormat, + const char *aFormat, va_list aArgs); otError RequestWithExpectedCommandV(uint32_t aExpectedCommand, uint32_t aCommand, spinel_prop_key_t aKey, - const char * aFormat, + const char *aFormat, va_list aArgs); - otError WaitResponse(void); + otError WaitResponse(bool aHandleRcpTimeout = true); otError SendCommand(uint32_t aCommand, spinel_prop_key_t aKey, spinel_tid_t aTid, - const char * aFormat, + const char *aFormat, va_list aArgs); otError ParseRadioFrame(otRadioFrame &aFrame, const uint8_t *aBuffer, uint16_t aLength, spinel_ssize_t &aUnpacked); otError ThreadDatasetHandler(const uint8_t *aBuffer, uint16_t aLength); @@ -996,6 +1050,9 @@ template class RadioSpinel mRadioSpinelMetrics.mSpinelParseErrorCount += (aError == OT_ERROR_PARSE) ? 1 : 0; } + uint32_t Snprintf(char *aDest, uint32_t aSize, const char *aFormat, ...); + void LogSpinelFrame(const uint8_t *aFrame, uint16_t aLength, bool aTx); + otInstance *mInstance; SpinelInterface::RxFrameBuffer mRxFrameBuffer; @@ -1007,7 +1064,7 @@ template class RadioSpinel spinel_tid_t mTxRadioTid; ///< The transaction id used to send a radio frame. spinel_tid_t mWaitingTid; ///< The transaction id of current transaction. spinel_prop_key_t mWaitingKey; ///< The property key of current transaction. - const char * mPropertyFormat; ///< The spinel property format of current transaction. + const char *mPropertyFormat; ///< The spinel property format of current transaction. va_list mPropertyArgs; ///< The arguments pack or unpack spinel property of current transaction. uint32_t mExpectedCommand; ///< Expected response command of current transaction. otError mError; ///< The result of current transaction. @@ -1070,13 +1127,13 @@ template class RadioSpinel #if OPENTHREAD_CONFIG_DIAG_ENABLE bool mDiagMode; - char * mDiagOutput; + char *mDiagOutput; size_t mDiagOutputMaxLen; #endif uint64_t mTxRadioEndUs; uint64_t mRadioTimeRecalcStart; ///< When to recalculate RCP time offset. - int64_t mRadioTimeOffset; ///< Time difference with estimated RCP time minus host time. + uint64_t mRadioTimeOffset; ///< Time difference with estimated RCP time minus host time. MaxPowerTable mMaxPowerTable; diff --git a/src/lib/spinel/radio_spinel_impl.hpp b/src/lib/spinel/radio_spinel_impl.hpp index c9eda5c483c..c9c676576c7 100644 --- a/src/lib/spinel/radio_spinel_impl.hpp +++ b/src/lib/spinel/radio_spinel_impl.hpp @@ -64,18 +64,11 @@ #ifndef US_PER_S #define US_PER_S (MS_PER_S * US_PER_MS) #endif -#ifndef NS_PER_US -#define NS_PER_US 1000 -#endif #ifndef TX_WAIT_US #define TX_WAIT_US (5 * US_PER_S) #endif -#ifndef RCP_TIME_OFFSET_CHECK_INTERVAL -#define RCP_TIME_OFFSET_CHECK_INTERVAL (60 * US_PER_S) -#endif - using ot::Spinel::Decoder; namespace ot { @@ -136,6 +129,9 @@ static inline otError SpinelStatusToOtError(spinel_status_t aError) break; case SPINEL_STATUS_PROP_NOT_FOUND: + ret = OT_ERROR_NOT_IMPLEMENTED; + break; + case SPINEL_STATUS_ITEM_NOT_FOUND: ret = OT_ERROR_NOT_FOUND; break; @@ -215,7 +211,7 @@ RadioSpinel::RadioSpinel(void) #endif , mTxRadioEndUs(UINT64_MAX) , mRadioTimeRecalcStart(UINT64_MAX) - , mRadioTimeOffset(0) + , mRadioTimeOffset(UINT64_MAX) { mVersion[0] = '\0'; memset(&mRadioSpinelMetrics, 0, sizeof(mRadioSpinelMetrics)); @@ -228,33 +224,18 @@ void RadioSpinel::Init(bool aResetRadio, { otError error = OT_ERROR_NONE; bool supportsRcpApiVersion; + bool supportsRcpMinHostApiVersion; #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 mResetRadioOnStartup = aResetRadio; #endif - if (aResetRadio) - { - SuccessOrExit(error = SendReset(SPINEL_RESET_STACK)); - SuccessOrDie(mSpinelInterface.ResetConnection()); - } - - SuccessOrExit(error = WaitResponse()); - -#if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 - while (mRcpFailed) - { - RecoverFromRcpFailure(); - } -#endif - - VerifyOrExit(mIsReady, error = OT_ERROR_FAILED); - + ResetRcp(aResetRadio); SuccessOrExit(error = CheckSpinelVersion()); SuccessOrExit(error = Get(SPINEL_PROP_NCP_VERSION, SPINEL_DATATYPE_UTF8_S, mVersion, sizeof(mVersion))); SuccessOrExit(error = Get(SPINEL_PROP_HWADDR, SPINEL_DATATYPE_EUI64_S, mIeeeEui64.m8)); - if (!IsRcp(supportsRcpApiVersion)) + if (!IsRcp(supportsRcpApiVersion, supportsRcpMinHostApiVersion)) { uint8_t exitCode = OT_EXIT_RADIO_SPINEL_INCOMPATIBLE; @@ -270,7 +251,7 @@ void RadioSpinel::Init(bool aResetRadio, if (!aSkipRcpCompatibilityCheck) { - SuccessOrDie(CheckRcpApiVersion(supportsRcpApiVersion)); + SuccessOrDie(CheckRcpApiVersion(supportsRcpApiVersion, supportsRcpMinHostApiVersion)); SuccessOrDie(CheckRadioCapabilities()); } @@ -282,6 +263,43 @@ void RadioSpinel::Init(bool aResetRadio, SuccessOrDie(error); } +template +void RadioSpinel::ResetRcp(bool aResetRadio) +{ + bool hardwareReset; + bool resetDone = false; + + mIsReady = false; + mWaitingKey = SPINEL_PROP_LAST_STATUS; + + if (aResetRadio && (SendReset(SPINEL_RESET_STACK) == OT_ERROR_NONE) && (WaitResponse(false) == OT_ERROR_NONE)) + { + otLogInfoPlat("Software reset RCP successfully"); + ExitNow(resetDone = true); + } + + hardwareReset = (mSpinelInterface.HardwareReset() == OT_ERROR_NONE); + SuccessOrExit(WaitResponse(false)); + + resetDone = true; + + if (hardwareReset) + { + otLogInfoPlat("Hardware reset RCP successfully"); + } + else + { + otLogInfoPlat("RCP self reset successfully"); + } + +exit: + if (!resetDone) + { + otLogCritPlat("Failed to reset RCP!"); + DieNow(OT_EXIT_FAILURE); + } +} + template otError RadioSpinel::CheckSpinelVersion(void) { @@ -306,7 +324,8 @@ otError RadioSpinel::CheckSpinelVersion(void) } template -bool RadioSpinel::IsRcp(bool &aSupportsRcpApiVersion) +bool RadioSpinel::IsRcp(bool &aSupportsRcpApiVersion, + bool &aSupportsRcpMinHostApiVersion) { uint8_t capsBuffer[kCapsBufferSize]; const uint8_t *capsData = capsBuffer; @@ -314,7 +333,8 @@ bool RadioSpinel::IsRcp(bool &aSupportsRcpApi bool supportsRawRadio = false; bool isRcp = false; - aSupportsRcpApiVersion = false; + aSupportsRcpApiVersion = false; + aSupportsRcpMinHostApiVersion = false; SuccessOrDie(Get(SPINEL_PROP_CAPS, SPINEL_DATATYPE_DATA_S, capsBuffer, &capsLength)); @@ -346,6 +366,11 @@ bool RadioSpinel::IsRcp(bool &aSupportsRcpApi aSupportsRcpApiVersion = true; } + if (capability == SPINEL_PROP_RCP_MIN_HOST_API_VERSION) + { + aSupportsRcpMinHostApiVersion = true; + } + capsData += unpacked; capsLength -= static_cast(unpacked); } @@ -397,29 +422,50 @@ otError RadioSpinel::CheckRadioCapabilities(v } template -otError RadioSpinel::CheckRcpApiVersion(bool aSupportsRcpApiVersion) +otError RadioSpinel::CheckRcpApiVersion(bool aSupportsRcpApiVersion, + bool aSupportsRcpMinHostApiVersion) { - otError error = OT_ERROR_NONE; - unsigned int rcpApiVersion = 1; + otError error = OT_ERROR_NONE; - // Use RCP API Version value 1, when the RCP capability - // list does not contain `SPINEL_CAP_RCP_API_VERSION`. + static_assert(SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION <= SPINEL_RCP_API_VERSION, + "MIN_HOST_SUPPORTED_RCP_API_VERSION must be smaller than or equal to RCP_API_VERSION"); if (aSupportsRcpApiVersion) { + // Make sure RCP is not too old and its version is within the + // range host supports. + + unsigned int rcpApiVersion; + SuccessOrExit(error = Get(SPINEL_PROP_RCP_API_VERSION, SPINEL_DATATYPE_UINT_PACKED_S, &rcpApiVersion)); + + if (rcpApiVersion < SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION) + { + otLogCritPlat("RCP and host are using incompatible API versions"); + otLogCritPlat("RCP API Version %u is older than min required by host %u", rcpApiVersion, + SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION); + DieNow(OT_EXIT_RADIO_SPINEL_INCOMPATIBLE); + } } - otLogNotePlat("RCP API Version: %u", rcpApiVersion); + if (aSupportsRcpMinHostApiVersion) + { + // Check with RCP about min host API version it can work with, + // and make sure on host side our version is within the supported + // range. - static_assert(SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION <= SPINEL_RCP_API_VERSION, - "MIN_HOST_SUPPORTED_RCP_API_VERSION must be smaller than or equal to RCP_API_VERSION"); + unsigned int minHostRcpApiVersion; - if ((rcpApiVersion < SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION) || (rcpApiVersion > SPINEL_RCP_API_VERSION)) - { - otLogCritPlat("RCP API Version %u is not in the supported range [%u-%u]", rcpApiVersion, - SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION, SPINEL_RCP_API_VERSION); - DieNow(OT_EXIT_RADIO_SPINEL_INCOMPATIBLE); + SuccessOrExit( + error = Get(SPINEL_PROP_RCP_MIN_HOST_API_VERSION, SPINEL_DATATYPE_UINT_PACKED_S, &minHostRcpApiVersion)); + + if (SPINEL_RCP_API_VERSION < minHostRcpApiVersion) + { + otLogCritPlat("RCP and host are using incompatible API versions"); + otLogCritPlat("RCP requires min host API version %u but host is older and at version %u", + minHostRcpApiVersion, SPINEL_RCP_API_VERSION); + DieNow(OT_EXIT_RADIO_SPINEL_INCOMPATIBLE); + } } exit: @@ -461,6 +507,7 @@ void RadioSpinel::HandleReceivedFrame(void) uint8_t header; spinel_ssize_t unpacked; + LogSpinelFrame(mRxFrameBuffer.GetFrame(), mRxFrameBuffer.GetLength(), false); unpacked = spinel_datatype_unpack(mRxFrameBuffer.GetFrame(), mRxFrameBuffer.GetLength(), "C", &header); VerifyOrExit(unpacked > 0 && (header & SPINEL_HEADER_FLAG) == SPINEL_HEADER_FLAG && @@ -493,7 +540,7 @@ void RadioSpinel::HandleNotification(SpinelIn spinel_prop_key_t key; spinel_size_t len = 0; spinel_ssize_t unpacked; - uint8_t * data = nullptr; + uint8_t *data = nullptr; uint32_t cmd; uint8_t header; otError error = OT_ERROR_NONE; @@ -521,7 +568,7 @@ void RadioSpinel::HandleNotification(SpinelIn case SPINEL_CMD_PROP_VALUE_INSERTED: case SPINEL_CMD_PROP_VALUE_REMOVED: - otLogInfoPlat("Ignored command %d", cmd); + otLogInfoPlat("Ignored command %lu", ToUlong(cmd)); break; default: @@ -548,7 +595,7 @@ void RadioSpinel::HandleNotification(const ui spinel_prop_key_t key; spinel_size_t len = 0; spinel_ssize_t unpacked; - uint8_t * data = nullptr; + uint8_t *data = nullptr; uint32_t cmd; uint8_t header; otError error = OT_ERROR_NONE; @@ -568,7 +615,7 @@ template void RadioSpinel::HandleResponse(const uint8_t *aBuffer, uint16_t aLength) { spinel_prop_key_t key; - uint8_t * data = nullptr; + uint8_t *data = nullptr; spinel_size_t len = 0; uint8_t header = 0; uint32_t cmd = 0; @@ -774,7 +821,7 @@ otError RadioSpinel::ThreadDatasetHandler(con template void RadioSpinel::HandleWaitingResponse(uint32_t aCommand, spinel_prop_key_t aKey, - const uint8_t * aBuffer, + const uint8_t *aBuffer, uint16_t aLength) { if (aKey == SPINEL_PROP_LAST_STATUS) @@ -790,6 +837,7 @@ void RadioSpinel::HandleWaitingResponse(uint3 { spinel_ssize_t unpacked; + mError = OT_ERROR_NONE; VerifyOrExit(mDiagOutput != nullptr); unpacked = spinel_datatype_unpack_in_place(aBuffer, aLength, SPINEL_DATATYPE_UTF8_S, mDiagOutput, &mDiagOutputMaxLen); @@ -841,7 +889,7 @@ void RadioSpinel::HandleWaitingResponse(uint3 template void RadioSpinel::HandleValueIs(spinel_prop_key_t aKey, - const uint8_t * aBuffer, + const uint8_t *aBuffer, uint16_t aLength) { otError error = OT_ERROR_NONE; @@ -948,8 +996,8 @@ void RadioSpinel::HandleValueIs(spinel_prop_k } template -otError RadioSpinel::ParseRadioFrame(otRadioFrame & aFrame, - const uint8_t * aBuffer, +otError RadioSpinel::ParseRadioFrame(otRadioFrame &aFrame, + const uint8_t *aBuffer, uint16_t aLength, spinel_ssize_t &aUnpacked) { @@ -1195,11 +1243,12 @@ otError RadioSpinel::SetMacKey(uint8_t } template -otError RadioSpinel::SetMacFrameCounter(uint32_t aMacFrameCounter) +otError RadioSpinel::SetMacFrameCounter(uint32_t aMacFrameCounter, bool aSetIfLarger) { otError error; - SuccessOrExit(error = Set(SPINEL_PROP_RCP_MAC_FRAME_COUNTER, SPINEL_DATATYPE_UINT32_S, aMacFrameCounter)); + SuccessOrExit(error = Set(SPINEL_PROP_RCP_MAC_FRAME_COUNTER, SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_BOOL_S, + aMacFrameCounter, aSetIfLarger)); exit: return error; @@ -1576,9 +1625,9 @@ otError RadioSpinel::Get(spinel_prop_key_t aK // This is not a normal use case for VALUE_GET command and should be only used to get RCP timestamp with dummy payload template otError RadioSpinel::GetWithParam(spinel_prop_key_t aKey, - const uint8_t * aParam, + const uint8_t *aParam, spinel_size_t aParamSize, - const char * aFormat, + const char *aFormat, ...) { otError error; @@ -1671,11 +1720,11 @@ otError RadioSpinel::Remove(spinel_prop_key_t } template -otError RadioSpinel::WaitResponse(void) +otError RadioSpinel::WaitResponse(bool aHandleRcpTimeout) { uint64_t end = otPlatTimeGet() + kMaxWaitTime * US_PER_MS; - otLogDebgPlat("Wait response: tid=%u key=%u", mWaitingTid, mWaitingKey); + otLogDebgPlat("Wait response: tid=%u key=%lu", mWaitingTid, ToUlong(mWaitingKey)); do { @@ -1685,7 +1734,10 @@ otError RadioSpinel::WaitResponse(void) if ((end <= now) || (mSpinelInterface.WaitForFrame(end - now) != OT_ERROR_NONE)) { otLogWarnPlat("Wait for response timeout"); - HandleRcpTimeout(); + if (aHandleRcpTimeout) + { + HandleRcpTimeout(); + } ExitNow(mError = OT_ERROR_NONE); } } while (mWaitingTid || !mIsReady); @@ -1737,6 +1789,7 @@ otError RadioSpinel::SendReset(uint8_t aReset VerifyOrExit(packed > 0 && static_cast(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS); SuccessOrExit(error = mSpinelInterface.SendFrame(buffer, static_cast(packed))); + LogSpinelFrame(buffer, static_cast(packed), true); exit: return error; @@ -1746,7 +1799,7 @@ template otError RadioSpinel::SendCommand(uint32_t aCommand, spinel_prop_key_t aKey, spinel_tid_t tid, - const char * aFormat, + const char *aFormat, va_list args) { otError error = OT_ERROR_NONE; @@ -1771,7 +1824,8 @@ otError RadioSpinel::SendCommand(uint32_t offset += static_cast(packed); } - error = mSpinelInterface.SendFrame(buffer, offset); + SuccessOrExit(error = mSpinelInterface.SendFrame(buffer, offset)); + LogSpinelFrame(buffer, offset, true); exit: return error; @@ -1780,7 +1834,7 @@ otError RadioSpinel::SendCommand(uint32_t template otError RadioSpinel::RequestV(uint32_t command, spinel_prop_key_t aKey, - const char * aFormat, + const char *aFormat, va_list aArgs) { otError error = OT_ERROR_NONE; @@ -1812,7 +1866,7 @@ otError RadioSpinel::RequestV(uint32_t template otError RadioSpinel::Request(uint32_t aCommand, spinel_prop_key_t aKey, - const char * aFormat, + const char *aFormat, ...) { va_list args; @@ -1823,10 +1877,10 @@ otError RadioSpinel::Request(uint32_t } template -otError RadioSpinel::RequestWithPropertyFormat(const char * aPropertyFormat, +otError RadioSpinel::RequestWithPropertyFormat(const char *aPropertyFormat, uint32_t aCommand, spinel_prop_key_t aKey, - const char * aFormat, + const char *aFormat, ...) { otError error; @@ -1840,10 +1894,10 @@ otError RadioSpinel::RequestWithPropertyForma } template -otError RadioSpinel::RequestWithPropertyFormatV(const char * aPropertyFormat, +otError RadioSpinel::RequestWithPropertyFormatV(const char *aPropertyFormat, uint32_t aCommand, spinel_prop_key_t aKey, - const char * aFormat, + const char *aFormat, va_list aArgs) { otError error; @@ -1859,7 +1913,7 @@ template otError RadioSpinel::RequestWithExpectedCommandV(uint32_t aExpectedCommand, uint32_t aCommand, spinel_prop_key_t aKey, - const char * aFormat, + const char *aFormat, va_list aArgs) { otError error; @@ -1874,7 +1928,7 @@ otError RadioSpinel::RequestWithExpectedComma template void RadioSpinel::HandleTransmitDone(uint32_t aCommand, spinel_prop_key_t aKey, - const uint8_t * aBuffer, + const uint8_t *aBuffer, uint16_t aLength) { otError error = OT_ERROR_NONE; @@ -1950,21 +2004,23 @@ otError RadioSpinel::Transmit(otRadioFrame &a otPlatRadioTxStarted(mInstance, mTransmitFrame); error = Request(SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_STREAM_RAW, - SPINEL_DATATYPE_DATA_WLEN_S // Frame data - SPINEL_DATATYPE_UINT8_S // Channel - SPINEL_DATATYPE_UINT8_S // MaxCsmaBackoffs - SPINEL_DATATYPE_UINT8_S // MaxFrameRetries - SPINEL_DATATYPE_BOOL_S // CsmaCaEnabled - SPINEL_DATATYPE_BOOL_S // IsHeaderUpdated - SPINEL_DATATYPE_BOOL_S // IsARetx - SPINEL_DATATYPE_BOOL_S // SkipAes - SPINEL_DATATYPE_UINT32_S // TxDelay - SPINEL_DATATYPE_UINT32_S, // TxDelayBaseTime + SPINEL_DATATYPE_DATA_WLEN_S // Frame data + SPINEL_DATATYPE_UINT8_S // Channel + SPINEL_DATATYPE_UINT8_S // MaxCsmaBackoffs + SPINEL_DATATYPE_UINT8_S // MaxFrameRetries + SPINEL_DATATYPE_BOOL_S // CsmaCaEnabled + SPINEL_DATATYPE_BOOL_S // IsHeaderUpdated + SPINEL_DATATYPE_BOOL_S // IsARetx + SPINEL_DATATYPE_BOOL_S // IsSecurityProcessed + SPINEL_DATATYPE_UINT32_S // TxDelay + SPINEL_DATATYPE_UINT32_S // TxDelayBaseTime + SPINEL_DATATYPE_UINT8_S, // RxChannelAfterTxDone mTransmitFrame->mPsdu, mTransmitFrame->mLength, mTransmitFrame->mChannel, mTransmitFrame->mInfo.mTxInfo.mMaxCsmaBackoffs, mTransmitFrame->mInfo.mTxInfo.mMaxFrameRetries, mTransmitFrame->mInfo.mTxInfo.mCsmaCaEnabled, mTransmitFrame->mInfo.mTxInfo.mIsHeaderUpdated, mTransmitFrame->mInfo.mTxInfo.mIsARetx, mTransmitFrame->mInfo.mTxInfo.mIsSecurityProcessed, - mTransmitFrame->mInfo.mTxInfo.mTxDelay, mTransmitFrame->mInfo.mTxInfo.mTxDelayBaseTime); + mTransmitFrame->mInfo.mTxInfo.mTxDelay, mTransmitFrame->mInfo.mTxInfo.mTxDelayBaseTime, + mTransmitFrame->mInfo.mTxInfo.mRxChannelAfterTxDone); if (error == OT_ERROR_NONE) { @@ -2081,7 +2137,7 @@ otError RadioSpinel::Disable(void) #if OPENTHREAD_CONFIG_DIAG_ENABLE template otError RadioSpinel::PlatDiagProcess(const char *aString, - char * aOutput, + char *aOutput, size_t aOutputMaxLen) { otError error; @@ -2196,9 +2252,9 @@ void RadioSpinel::CalcRcpTimeOffset(void) VerifyOrExit(error == OT_ERROR_NONE, mRadioTimeRecalcStart = localRxTimestamp); - mRadioTimeOffset = static_cast(remoteTimestamp - ((localRxTimestamp / 2) + (localTxTimestamp / 2))); + mRadioTimeOffset = (remoteTimestamp - ((localRxTimestamp / 2) + (localTxTimestamp / 2))); mIsTimeSynced = true; - mRadioTimeRecalcStart = localRxTimestamp + RCP_TIME_OFFSET_CHECK_INTERVAL; + mRadioTimeRecalcStart = localRxTimestamp + OPENTHREAD_POSIX_CONFIG_RCP_TIME_SYNC_INTERVAL; exit: LogIfFail("Error calculating RCP time offset: %s", error); @@ -2208,7 +2264,7 @@ void RadioSpinel::CalcRcpTimeOffset(void) template uint64_t RadioSpinel::GetNow(void) { - return mIsTimeSynced ? (otPlatTimeGet() + static_cast(mRadioTimeOffset)) : UINT64_MAX; + return (mIsTimeSynced) ? (otPlatTimeGet() + mRadioTimeOffset) : UINT64_MAX; } template @@ -2227,6 +2283,8 @@ void RadioSpinel::HandleRcpUnexpectedReset(sp #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 mRcpFailed = true; +#elif OPENTHREAD_SPINEL_CONFIG_ABORT_ON_UNEXPECTED_RCP_RESET_ENABLE + abort(); #else DieNow(OT_EXIT_RADIO_SPINEL_RESET); #endif @@ -2271,24 +2329,14 @@ void RadioSpinel::RecoverFromRcpFailure(void) mState = kStateDisabled; mRxFrameBuffer.Clear(); - mSpinelInterface.OnRcpReset(); mCmdTidsInUse = 0; mCmdNextTid = 1; mTxRadioTid = 0; mWaitingTid = 0; - mWaitingKey = SPINEL_PROP_LAST_STATUS; mError = OT_ERROR_NONE; - mIsReady = false; mIsTimeSynced = false; - if (mResetRadioOnStartup) - { - SuccessOrDie(SendReset(SPINEL_RESET_STACK)); - SuccessOrDie(mSpinelInterface.ResetConnection()); - } - - SuccessOrDie(WaitResponse()); - + ResetRcp(mResetRadioOnStartup); SuccessOrDie(Set(SPINEL_PROP_PHY_ENABLED, SPINEL_DATATYPE_BOOL_S, true)); mState = kStateSleep; @@ -2383,6 +2431,7 @@ void RadioSpinel::RestoreProperties(void) SuccessOrDie(Set(SPINEL_PROP_PHY_FEM_LNA_GAIN, SPINEL_DATATYPE_INT8_S, mFemLnaGain)); } +#if OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE for (uint8_t channel = Radio::kChannelMin; channel <= Radio::kChannelMax; channel++) { int8_t power = mMaxPowerTable.GetTransmitPower(channel); @@ -2398,6 +2447,7 @@ void RadioSpinel::RestoreProperties(void) } } } +#endif // OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE CalcRcpTimeOffset(); } @@ -2452,7 +2502,7 @@ otError RadioSpinel::GetRadioRegion(uint16_t template otError RadioSpinel::ConfigureEnhAckProbing(otLinkMetrics aLinkMetrics, const otShortAddress aShortAddress, - const otExtAddress & aExtAddress) + const otExtAddress &aExtAddress) { otError error = OT_ERROR_NONE; uint8_t flags = 0; @@ -2509,5 +2559,683 @@ uint8_t RadioSpinel::GetCslUncertainty(void) } #endif +#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +template +otError RadioSpinel::AddCalibratedPower(uint8_t aChannel, + int16_t aActualPower, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength) +{ + otError error; + + assert(aRawPowerSetting != nullptr); + SuccessOrExit(error = Insert(SPINEL_PROP_PHY_CALIBRATED_POWER, + SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_DATA_WLEN_S, aChannel, + aActualPower, aRawPowerSetting, aRawPowerSettingLength)); + +exit: + return error; +} + +template +otError RadioSpinel::ClearCalibratedPowers(void) +{ + return Set(SPINEL_PROP_PHY_CALIBRATED_POWER, nullptr); +} + +template +otError RadioSpinel::SetChannelTargetPower(uint8_t aChannel, int16_t aTargetPower) +{ + otError error = OT_ERROR_NONE; + VerifyOrExit(aChannel >= Radio::kChannelMin && aChannel <= Radio::kChannelMax, error = OT_ERROR_INVALID_ARGS); + error = + Set(SPINEL_PROP_PHY_CHAN_TARGET_POWER, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S, aChannel, aTargetPower); + +exit: + return error; +} +#endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + +template +uint32_t RadioSpinel::Snprintf(char *aDest, uint32_t aSize, const char *aFormat, ...) +{ + int len; + va_list args; + + va_start(args, aFormat); + len = vsnprintf(aDest, static_cast(aSize), aFormat, args); + va_end(args); + + return (len < 0) ? 0 : Min(static_cast(len), aSize - 1); +} + +template +void RadioSpinel::LogSpinelFrame(const uint8_t *aFrame, uint16_t aLength, bool aTx) +{ + otError error = OT_ERROR_NONE; + char buf[OPENTHREAD_CONFIG_LOG_MAX_SIZE] = {0}; + spinel_ssize_t unpacked; + uint8_t header; + uint32_t cmd; + spinel_prop_key_t key; + uint8_t *data; + spinel_size_t len; + const char *prefix = nullptr; + char *start = buf; + char *end = buf + sizeof(buf); + + VerifyOrExit(otLoggingGetLevel() >= OT_LOG_LEVEL_DEBG); + + prefix = aTx ? "Sent spinel frame" : "Received spinel frame"; + unpacked = spinel_datatype_unpack(aFrame, aLength, "CiiD", &header, &cmd, &key, &data, &len); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + + start += Snprintf(start, static_cast(end - start), "%s, flg:0x%x, tid:%u, cmd:%s", prefix, + SPINEL_HEADER_GET_FLAG(header), SPINEL_HEADER_GET_TID(header), spinel_command_to_cstr(cmd)); + VerifyOrExit(cmd != SPINEL_CMD_RESET); + + start += Snprintf(start, static_cast(end - start), ", key:%s", spinel_prop_key_to_cstr(key)); + VerifyOrExit(cmd != SPINEL_CMD_PROP_VALUE_GET); + + switch (key) + { + case SPINEL_PROP_LAST_STATUS: + { + spinel_status_t status; + + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &status); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + start += Snprintf(start, static_cast(end - start), ", status:%s", spinel_status_to_cstr(status)); + } + break; + + case SPINEL_PROP_MAC_RAW_STREAM_ENABLED: + case SPINEL_PROP_MAC_SRC_MATCH_ENABLED: + case SPINEL_PROP_PHY_ENABLED: + case SPINEL_PROP_RADIO_COEX_ENABLE: + { + bool enabled; + + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_BOOL_S, &enabled); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + start += Snprintf(start, static_cast(end - start), ", enabled:%u", enabled); + } + break; + + case SPINEL_PROP_PHY_CCA_THRESHOLD: + case SPINEL_PROP_PHY_FEM_LNA_GAIN: + case SPINEL_PROP_PHY_RX_SENSITIVITY: + case SPINEL_PROP_PHY_RSSI: + case SPINEL_PROP_PHY_TX_POWER: + { + const char *name = nullptr; + int8_t value; + + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_INT8_S, &value); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + + switch (key) + { + case SPINEL_PROP_PHY_TX_POWER: + name = "power"; + break; + case SPINEL_PROP_PHY_CCA_THRESHOLD: + name = "threshold"; + break; + case SPINEL_PROP_PHY_FEM_LNA_GAIN: + name = "gain"; + break; + case SPINEL_PROP_PHY_RX_SENSITIVITY: + name = "sensitivity"; + break; + case SPINEL_PROP_PHY_RSSI: + name = "rssi"; + break; + } + + start += Snprintf(start, static_cast(end - start), ", %s:%d", name, value); + } + break; + + case SPINEL_PROP_MAC_PROMISCUOUS_MODE: + case SPINEL_PROP_MAC_SCAN_STATE: + case SPINEL_PROP_PHY_CHAN: + case SPINEL_PROP_RCP_CSL_ACCURACY: + case SPINEL_PROP_RCP_CSL_UNCERTAINTY: + { + const char *name = nullptr; + uint8_t value; + + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &value); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + + switch (key) + { + case SPINEL_PROP_MAC_SCAN_STATE: + name = "state"; + break; + case SPINEL_PROP_RCP_CSL_ACCURACY: + name = "accuracy"; + break; + case SPINEL_PROP_RCP_CSL_UNCERTAINTY: + name = "uncertainty"; + break; + case SPINEL_PROP_MAC_PROMISCUOUS_MODE: + name = "mode"; + break; + case SPINEL_PROP_PHY_CHAN: + name = "channel"; + break; + } + + start += Snprintf(start, static_cast(end - start), ", %s:%u", name, value); + } + break; + + case SPINEL_PROP_MAC_15_4_PANID: + case SPINEL_PROP_MAC_15_4_SADDR: + case SPINEL_PROP_MAC_SCAN_PERIOD: + case SPINEL_PROP_PHY_REGION_CODE: + { + const char *name = nullptr; + uint16_t value; + + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &value); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + + switch (key) + { + case SPINEL_PROP_MAC_SCAN_PERIOD: + name = "period"; + break; + case SPINEL_PROP_PHY_REGION_CODE: + name = "region"; + break; + case SPINEL_PROP_MAC_15_4_SADDR: + name = "saddr"; + break; + case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES: + name = "saddr"; + break; + case SPINEL_PROP_MAC_15_4_PANID: + name = "panid"; + break; + } + + start += Snprintf(start, static_cast(end - start), ", %s:0x%04x", name, value); + } + break; + + case SPINEL_PROP_MAC_SRC_MATCH_SHORT_ADDRESSES: + { + uint16_t saddr; + + start += Snprintf(start, static_cast(end - start), ", saddr:"); + + if (len < sizeof(saddr)) + { + start += Snprintf(start, static_cast(end - start), "none"); + } + else + { + while (len >= sizeof(saddr)) + { + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT16_S, &saddr); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + data += unpacked; + len -= static_cast(unpacked); + start += Snprintf(start, static_cast(end - start), "0x%04x ", saddr); + } + } + } + break; + + case SPINEL_PROP_RCP_MAC_FRAME_COUNTER: + case SPINEL_PROP_RCP_TIMESTAMP: + { + const char *name; + uint32_t value; + + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT32_S, &value); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + + name = (key == SPINEL_PROP_RCP_TIMESTAMP) ? "timestamp" : "counter"; + start += Snprintf(start, static_cast(end - start), ", %s:%u", name, value); + } + break; + + case SPINEL_PROP_RADIO_CAPS: + case SPINEL_PROP_RCP_API_VERSION: + case SPINEL_PROP_RCP_MIN_HOST_API_VERSION: + { + const char *name; + unsigned int value; + + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &value); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + + switch (key) + { + case SPINEL_PROP_RADIO_CAPS: + name = "caps"; + break; + case SPINEL_PROP_RCP_API_VERSION: + name = "version"; + break; + case SPINEL_PROP_RCP_MIN_HOST_API_VERSION: + name = "min-host-version"; + break; + default: + name = ""; + break; + } + + start += Snprintf(start, static_cast(end - start), ", %s:%u", name, value); + } + break; + + case SPINEL_PROP_MAC_ENERGY_SCAN_RESULT: + case SPINEL_PROP_PHY_CHAN_MAX_POWER: + { + const char *name; + uint8_t channel; + int8_t value; + + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S, &channel, &value); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + + name = (key == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT) ? "rssi" : "power"; + start += Snprintf(start, static_cast(end - start), ", channel:%u, %s:%d", channel, name, value); + } + break; + + case SPINEL_PROP_CAPS: + { + unsigned int capability; + + start += Snprintf(start, static_cast(end - start), ", caps:"); + + while (len > 0) + { + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S, &capability); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + data += unpacked; + len -= static_cast(unpacked); + start += Snprintf(start, static_cast(end - start), "%s ", spinel_capability_to_cstr(capability)); + } + } + break; + + case SPINEL_PROP_PROTOCOL_VERSION: + { + unsigned int major; + unsigned int minor; + + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT_PACKED_S SPINEL_DATATYPE_UINT_PACKED_S, + &major, &minor); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + start += Snprintf(start, static_cast(end - start), ", major:%u, minor:%u", major, minor); + } + break; + + case SPINEL_PROP_PHY_CHAN_PREFERRED: + case SPINEL_PROP_PHY_CHAN_SUPPORTED: + { + uint8_t maskBuffer[kChannelMaskBufferSize]; + uint32_t channelMask = 0; + const uint8_t *maskData = maskBuffer; + spinel_size_t maskLength = sizeof(maskBuffer); + + unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, maskBuffer, &maskLength); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + + while (maskLength > 0) + { + uint8_t channel; + + unpacked = spinel_datatype_unpack(maskData, maskLength, SPINEL_DATATYPE_UINT8_S, &channel); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + VerifyOrExit(channel < kChannelMaskBufferSize, error = OT_ERROR_PARSE); + channelMask |= (1UL << channel); + + maskData += unpacked; + maskLength -= static_cast(unpacked); + } + + start += Snprintf(start, static_cast(end - start), ", channelMask:0x%08x", channelMask); + } + break; + + case SPINEL_PROP_NCP_VERSION: + { + const char *version; + + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &version); + VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE); + start += Snprintf(start, static_cast(end - start), ", version:%s", version); + } + break; + + case SPINEL_PROP_STREAM_RAW: + { + otRadioFrame frame; + + if (cmd == SPINEL_CMD_PROP_VALUE_IS) + { + uint16_t flags; + int8_t noiseFloor; + unsigned int receiveError; + + unpacked = spinel_datatype_unpack(data, len, + SPINEL_DATATYPE_DATA_WLEN_S // Frame + SPINEL_DATATYPE_INT8_S // RSSI + SPINEL_DATATYPE_INT8_S // Noise Floor + SPINEL_DATATYPE_UINT16_S // Flags + SPINEL_DATATYPE_STRUCT_S( // PHY-data + SPINEL_DATATYPE_UINT8_S // 802.15.4 channel + SPINEL_DATATYPE_UINT8_S // 802.15.4 LQI + SPINEL_DATATYPE_UINT64_S // Timestamp (us). + ) SPINEL_DATATYPE_STRUCT_S( // Vendor-data + SPINEL_DATATYPE_UINT_PACKED_S // Receive error + ), + &frame.mPsdu, &frame.mLength, &frame.mInfo.mRxInfo.mRssi, &noiseFloor, + &flags, &frame.mChannel, &frame.mInfo.mRxInfo.mLqi, + &frame.mInfo.mRxInfo.mTimestamp, &receiveError); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + start += Snprintf(start, static_cast(end - start), ", len:%u, rssi:%d ...", frame.mLength, + frame.mInfo.mRxInfo.mRssi); + otLogDebgPlat("%s", buf); + + start = buf; + start += Snprintf(start, static_cast(end - start), + "... noise:%d, flags:0x%04x, channel:%u, lqi:%u, timestamp:%lu, rxerr:%u", noiseFloor, + flags, frame.mChannel, frame.mInfo.mRxInfo.mLqi, + static_cast(frame.mInfo.mRxInfo.mTimestamp), receiveError); + } + else if (cmd == SPINEL_CMD_PROP_VALUE_SET) + { + bool csmaCaEnabled; + bool isHeaderUpdated; + bool isARetx; + bool skipAes; + + unpacked = spinel_datatype_unpack( + data, len, + SPINEL_DATATYPE_DATA_WLEN_S // Frame data + SPINEL_DATATYPE_UINT8_S // Channel + SPINEL_DATATYPE_UINT8_S // MaxCsmaBackoffs + SPINEL_DATATYPE_UINT8_S // MaxFrameRetries + SPINEL_DATATYPE_BOOL_S // CsmaCaEnabled + SPINEL_DATATYPE_BOOL_S // IsHeaderUpdated + SPINEL_DATATYPE_BOOL_S // IsARetx + SPINEL_DATATYPE_BOOL_S // SkipAes + SPINEL_DATATYPE_UINT32_S // TxDelay + SPINEL_DATATYPE_UINT32_S, // TxDelayBaseTime + &frame.mPsdu, &frame.mLength, &frame.mChannel, &frame.mInfo.mTxInfo.mMaxCsmaBackoffs, + &frame.mInfo.mTxInfo.mMaxFrameRetries, &csmaCaEnabled, &isHeaderUpdated, &isARetx, &skipAes, + &frame.mInfo.mTxInfo.mTxDelay, &frame.mInfo.mTxInfo.mTxDelayBaseTime); + + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + start += Snprintf(start, static_cast(end - start), + ", len:%u, channel:%u, maxbackoffs:%u, maxretries:%u ...", frame.mLength, frame.mChannel, + frame.mInfo.mTxInfo.mMaxCsmaBackoffs, frame.mInfo.mTxInfo.mMaxFrameRetries); + otLogDebgPlat("%s", buf); + + start = buf; + start += Snprintf(start, static_cast(end - start), + "... csmaCaEnabled:%u, isHeaderUpdated:%u, isARetx:%u, skipAes:%u" + ", txDelay:%u, txDelayBase:%u", + csmaCaEnabled, isHeaderUpdated, isARetx, skipAes, frame.mInfo.mTxInfo.mTxDelay, + frame.mInfo.mTxInfo.mTxDelayBaseTime); + } + } + break; + + case SPINEL_PROP_STREAM_DEBUG: + { + char debugString[OPENTHREAD_CONFIG_NCP_SPINEL_LOG_MAX_SIZE + 1]; + spinel_size_t stringLength = sizeof(debugString); + + unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_DATA_S, debugString, &stringLength); + assert(stringLength < sizeof(debugString)); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + debugString[stringLength] = '\0'; + start += Snprintf(start, static_cast(end - start), ", debug:%s", debugString); + } + break; + + case SPINEL_PROP_STREAM_LOG: + { + const char *logString; + uint8_t logLevel; + + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &logString); + VerifyOrExit(unpacked >= 0, error = OT_ERROR_PARSE); + data += unpacked; + len -= static_cast(unpacked); + + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S, &logLevel); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + start += Snprintf(start, static_cast(end - start), ", level:%u, log:%s", logLevel, logString); + } + break; + + case SPINEL_PROP_NEST_STREAM_MFG: + { + const char *output; + size_t outputLen; + + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UTF8_S, &output, &outputLen); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + start += Snprintf(start, static_cast(end - start), ", diag:%s", output); + } + break; + + case SPINEL_PROP_RCP_MAC_KEY: + { + uint8_t keyIdMode; + uint8_t keyId; + otMacKey prevKey; + unsigned int prevKeyLen = sizeof(otMacKey); + otMacKey currKey; + unsigned int currKeyLen = sizeof(otMacKey); + otMacKey nextKey; + unsigned int nextKeyLen = sizeof(otMacKey); + + unpacked = spinel_datatype_unpack(data, len, + SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_DATA_WLEN_S + SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_DATA_WLEN_S, + &keyIdMode, &keyId, prevKey.m8, &prevKeyLen, currKey.m8, &currKeyLen, + nextKey.m8, &nextKeyLen); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + start += Snprintf(start, static_cast(end - start), + ", keyIdMode:%u, keyId:%u, prevKey:***, currKey:***, nextKey:***", keyIdMode, keyId); + } + break; + + case SPINEL_PROP_HWADDR: + case SPINEL_PROP_MAC_15_4_LADDR: + { + const char *name = nullptr; + uint8_t m8[OT_EXT_ADDRESS_SIZE] = {0}; + + unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, &m8[0]); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + + name = (key == SPINEL_PROP_HWADDR) ? "eui64" : "laddr"; + start += Snprintf(start, static_cast(end - start), ", %s:%02x%02x%02x%02x%02x%02x%02x%02x", name, + m8[0], m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]); + } + break; + + case SPINEL_PROP_MAC_SRC_MATCH_EXTENDED_ADDRESSES: + { + uint8_t m8[OT_EXT_ADDRESS_SIZE]; + + start += Snprintf(start, static_cast(end - start), ", extaddr:"); + + if (len < sizeof(m8)) + { + start += Snprintf(start, static_cast(end - start), "none"); + } + else + { + while (len >= sizeof(m8)) + { + unpacked = spinel_datatype_unpack_in_place(data, len, SPINEL_DATATYPE_EUI64_S, m8); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + data += unpacked; + len -= static_cast(unpacked); + start += Snprintf(start, static_cast(end - start), "%02x%02x%02x%02x%02x%02x%02x%02x ", m8[0], + m8[1], m8[2], m8[3], m8[4], m8[5], m8[6], m8[7]); + } + } + } + break; + + case SPINEL_PROP_RADIO_COEX_METRICS: + { + otRadioCoexMetrics metrics; + unpacked = spinel_datatype_unpack( + data, len, + SPINEL_DATATYPE_STRUCT_S( // Tx Coex Metrics Structure + SPINEL_DATATYPE_UINT32_S // NumTxRequest + SPINEL_DATATYPE_UINT32_S // NumTxGrantImmediate + SPINEL_DATATYPE_UINT32_S // NumTxGrantWait + SPINEL_DATATYPE_UINT32_S // NumTxGrantWaitActivated + SPINEL_DATATYPE_UINT32_S // NumTxGrantWaitTimeout + SPINEL_DATATYPE_UINT32_S // NumTxGrantDeactivatedDuringRequest + SPINEL_DATATYPE_UINT32_S // NumTxDelayedGrant + SPINEL_DATATYPE_UINT32_S // AvgTxRequestToGrantTime + ) SPINEL_DATATYPE_STRUCT_S( // Rx Coex Metrics Structure + SPINEL_DATATYPE_UINT32_S // NumRxRequest + SPINEL_DATATYPE_UINT32_S // NumRxGrantImmediate + SPINEL_DATATYPE_UINT32_S // NumRxGrantWait + SPINEL_DATATYPE_UINT32_S // NumRxGrantWaitActivated + SPINEL_DATATYPE_UINT32_S // NumRxGrantWaitTimeout + SPINEL_DATATYPE_UINT32_S // NumRxGrantDeactivatedDuringRequest + SPINEL_DATATYPE_UINT32_S // NumRxDelayedGrant + SPINEL_DATATYPE_UINT32_S // AvgRxRequestToGrantTime + SPINEL_DATATYPE_UINT32_S // NumRxGrantNone + ) SPINEL_DATATYPE_BOOL_S // Stopped + SPINEL_DATATYPE_UINT32_S, // NumGrantGlitch + &metrics.mNumTxRequest, &metrics.mNumTxGrantImmediate, &metrics.mNumTxGrantWait, + &metrics.mNumTxGrantWaitActivated, &metrics.mNumTxGrantWaitTimeout, + &metrics.mNumTxGrantDeactivatedDuringRequest, &metrics.mNumTxDelayedGrant, + &metrics.mAvgTxRequestToGrantTime, &metrics.mNumRxRequest, &metrics.mNumRxGrantImmediate, + &metrics.mNumRxGrantWait, &metrics.mNumRxGrantWaitActivated, &metrics.mNumRxGrantWaitTimeout, + &metrics.mNumRxGrantDeactivatedDuringRequest, &metrics.mNumRxDelayedGrant, + &metrics.mAvgRxRequestToGrantTime, &metrics.mNumRxGrantNone, &metrics.mStopped, &metrics.mNumGrantGlitch); + + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + + otLogDebgPlat("%s ...", buf); + otLogDebgPlat(" txRequest:%lu", ToUlong(metrics.mNumTxRequest)); + otLogDebgPlat(" txGrantImmediate:%lu", ToUlong(metrics.mNumTxGrantImmediate)); + otLogDebgPlat(" txGrantWait:%lu", ToUlong(metrics.mNumTxGrantWait)); + otLogDebgPlat(" txGrantWaitActivated:%lu", ToUlong(metrics.mNumTxGrantWaitActivated)); + otLogDebgPlat(" txGrantWaitTimeout:%lu", ToUlong(metrics.mNumTxGrantWaitTimeout)); + otLogDebgPlat(" txGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumTxGrantDeactivatedDuringRequest)); + otLogDebgPlat(" txDelayedGrant:%lu", ToUlong(metrics.mNumTxDelayedGrant)); + otLogDebgPlat(" avgTxRequestToGrantTime:%lu", ToUlong(metrics.mAvgTxRequestToGrantTime)); + otLogDebgPlat(" rxRequest:%lu", ToUlong(metrics.mNumRxRequest)); + otLogDebgPlat(" rxGrantImmediate:%lu", ToUlong(metrics.mNumRxGrantImmediate)); + otLogDebgPlat(" rxGrantWait:%lu", ToUlong(metrics.mNumRxGrantWait)); + otLogDebgPlat(" rxGrantWaitActivated:%lu", ToUlong(metrics.mNumRxGrantWaitActivated)); + otLogDebgPlat(" rxGrantWaitTimeout:%lu", ToUlong(metrics.mNumRxGrantWaitTimeout)); + otLogDebgPlat(" rxGrantDeactivatedDuringRequest:%lu", ToUlong(metrics.mNumRxGrantDeactivatedDuringRequest)); + otLogDebgPlat(" rxDelayedGrant:%lu", ToUlong(metrics.mNumRxDelayedGrant)); + otLogDebgPlat(" avgRxRequestToGrantTime:%lu", ToUlong(metrics.mAvgRxRequestToGrantTime)); + otLogDebgPlat(" rxGrantNone:%lu", ToUlong(metrics.mNumRxGrantNone)); + otLogDebgPlat(" stopped:%u", metrics.mStopped); + + start = buf; + start += Snprintf(start, static_cast(end - start), " grantGlitch:%u", metrics.mNumGrantGlitch); + } + break; + + case SPINEL_PROP_MAC_SCAN_MASK: + { + constexpr uint8_t kNumChannels = 16; + uint8_t channels[kNumChannels]; + spinel_size_t size; + + unpacked = spinel_datatype_unpack(data, len, SPINEL_DATATYPE_DATA_S, channels, &size); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + start += Snprintf(start, static_cast(end - start), ", channels:"); + + for (uint8_t i = 0; i < size; i++) + { + start += Snprintf(start, static_cast(end - start), "%u ", channels[i]); + } + } + break; + + case SPINEL_PROP_RCP_ENH_ACK_PROBING: + { + uint16_t saddr; + uint8_t m8[OT_EXT_ADDRESS_SIZE]; + uint8_t flags; + + unpacked = spinel_datatype_unpack( + data, len, SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_EUI64_S SPINEL_DATATYPE_UINT8_S, &saddr, m8, &flags); + + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + start += Snprintf(start, static_cast(end - start), + ", saddr:%04x, extaddr:%02x%02x%02x%02x%02x%02x%02x%02x, flags:0x%02x", saddr, m8[0], m8[1], + m8[2], m8[3], m8[4], m8[5], m8[6], m8[7], flags); + } + break; + + case SPINEL_PROP_PHY_CALIBRATED_POWER: + { + if (cmd == SPINEL_CMD_PROP_VALUE_INSERT) + { + uint8_t channel; + int16_t actualPower; + uint8_t *rawPowerSetting; + unsigned int rawPowerSettingLength; + + unpacked = spinel_datatype_unpack( + data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_DATA_WLEN_S, &channel, + &actualPower, &rawPowerSetting, &rawPowerSettingLength); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + + start += Snprintf(start, static_cast(end - start), + ", ch:%u, actualPower:%d, rawPowerSetting:", channel, actualPower); + for (uint16_t i = 0; i < rawPowerSettingLength; i++) + { + start += Snprintf(start, static_cast(end - start), "%02x", rawPowerSetting[i]); + } + } + } + break; + + case SPINEL_PROP_PHY_CHAN_TARGET_POWER: + { + uint8_t channel; + int16_t targetPower; + + unpacked = + spinel_datatype_unpack(data, len, SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT16_S, &channel, &targetPower); + VerifyOrExit(unpacked > 0, error = OT_ERROR_PARSE); + start += Snprintf(start, static_cast(end - start), ", ch:%u, targetPower:%d", channel, targetPower); + } + break; + } + +exit: + if (error == OT_ERROR_NONE) + { + otLogDebgPlat("%s", buf); + } + else + { + otLogDebgPlat("%s, failed to parse spinel frame !", prefix); + } + + return; +} + } // namespace Spinel } // namespace ot diff --git a/src/lib/spinel/radio_spinel_metrics.h b/src/lib/spinel/radio_spinel_metrics.h index 438e14c8afe..91d4d76da07 100644 --- a/src/lib/spinel/radio_spinel_metrics.h +++ b/src/lib/spinel/radio_spinel_metrics.h @@ -46,7 +46,7 @@ extern "C" { typedef struct otRadioSpinelMetrics { uint32_t mRcpTimeoutCount; ///< The number of RCP timeouts. - uint32_t mRcpUnexpectedResetCount; ///< The number of RCP unexcepted resets. + uint32_t mRcpUnexpectedResetCount; ///< The number of RCP unexpected resets. uint32_t mRcpRestorationCount; ///< The number of RCP restorations. uint32_t mSpinelParseErrorCount; ///< The number of spinel frame parse errors. } otRadioSpinelMetrics; diff --git a/src/lib/spinel/spinel.c b/src/lib/spinel/spinel.c index 190381db5c2..5cf3836b7a6 100644 --- a/src/lib/spinel/spinel.c +++ b/src/lib/spinel/spinel.c @@ -340,8 +340,8 @@ const char *spinel_next_packed_datatype(const char *pack_format) static spinel_ssize_t spinel_datatype_vunpack_(bool in_place, const uint8_t *data_in, spinel_size_t data_len, - const char * pack_format, - va_list_obj * args) + const char *pack_format, + va_list_obj *args) { spinel_ssize_t ret = 0; @@ -528,7 +528,7 @@ static spinel_ssize_t spinel_datatype_vunpack_(bool in_place, case SPINEL_DATATYPE_UINT_PACKED_C: { - unsigned int * arg_ptr = va_arg(args->obj, unsigned int *); + unsigned int *arg_ptr = va_arg(args->obj, unsigned int *); spinel_ssize_t pui_len = spinel_packed_uint_decode(data_in, data_len, arg_ptr); // Range check @@ -564,7 +564,7 @@ static spinel_ssize_t spinel_datatype_vunpack_(bool in_place, if (in_place) { - char * arg = va_arg(args->obj, char *); + char *arg = va_arg(args->obj, char *); size_t len_arg = va_arg(args->obj, size_t); if (arg) { @@ -593,8 +593,8 @@ static spinel_ssize_t spinel_datatype_vunpack_(bool in_place, spinel_ssize_t pui_len = 0; uint16_t block_len = 0; const uint8_t *block_ptr = data_in; - void * arg_ptr = va_arg(args->obj, void *); - unsigned int * block_len_ptr = va_arg(args->obj, unsigned int *); + void *arg_ptr = va_arg(args->obj, void *); + unsigned int *block_len_ptr = va_arg(args->obj, unsigned int *); char nextformat = *spinel_next_packed_datatype(pack_format); if ((pack_format[0] == SPINEL_DATATYPE_DATA_WLEN_C) || ((nextformat != 0) && (nextformat != ')'))) @@ -704,7 +704,7 @@ static spinel_ssize_t spinel_datatype_vunpack_(bool in_place, spinel_ssize_t spinel_datatype_unpack_in_place(const uint8_t *data_in, spinel_size_t data_len, - const char * pack_format, + const char *pack_format, ...) { spinel_ssize_t ret; @@ -731,7 +731,7 @@ spinel_ssize_t spinel_datatype_unpack(const uint8_t *data_in, spinel_size_t data spinel_ssize_t spinel_datatype_vunpack_in_place(const uint8_t *data_in, spinel_size_t data_len, - const char * pack_format, + const char *pack_format, va_list args) { spinel_ssize_t ret; @@ -746,7 +746,7 @@ spinel_ssize_t spinel_datatype_vunpack_in_place(const uint8_t *data_in, spinel_ssize_t spinel_datatype_vunpack(const uint8_t *data_in, spinel_size_t data_len, - const char * pack_format, + const char *pack_format, va_list args) { spinel_ssize_t ret; @@ -759,10 +759,10 @@ spinel_ssize_t spinel_datatype_vunpack(const uint8_t *data_in, return ret; } -static spinel_ssize_t spinel_datatype_vpack_(uint8_t * data_out, +static spinel_ssize_t spinel_datatype_vpack_(uint8_t *data_out, spinel_size_t data_len_max, - const char * pack_format, - va_list_obj * args) + const char *pack_format, + va_list_obj *args) { spinel_ssize_t ret = 0; @@ -1128,9 +1128,9 @@ spinel_ssize_t spinel_datatype_pack(uint8_t *data_out, spinel_size_t data_len_ma return ret; } -spinel_ssize_t spinel_datatype_vpack(uint8_t * data_out, +spinel_ssize_t spinel_datatype_vpack(uint8_t *data_out, spinel_size_t data_len_max, - const char * pack_format, + const char *pack_format, va_list args) { int ret; @@ -1235,6 +1235,9 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) {SPINEL_PROP_PHY_PCAP_ENABLED, "PHY_PCAP_ENABLED"}, {SPINEL_PROP_PHY_CHAN_PREFERRED, "PHY_CHAN_PREFERRED"}, {SPINEL_PROP_PHY_CHAN_MAX_POWER, "PHY_CHAN_MAX_POWER"}, + {SPINEL_PROP_PHY_REGION_CODE, "PHY_REGION_CODE"}, + {SPINEL_PROP_PHY_CALIBRATED_POWER, "PHY_CALIBRATED_POWER"}, + {SPINEL_PROP_PHY_CHAN_TARGET_POWER, "PHY_CHAN_TARGET_POWER"}, {SPINEL_PROP_JAM_DETECT_ENABLE, "JAM_DETECT_ENABLE"}, {SPINEL_PROP_JAM_DETECTED, "JAM_DETECTED"}, {SPINEL_PROP_JAM_DETECT_RSSI_THRESHOLD, "JAM_DETECT_RSSI_THRESHOLD"}, @@ -1400,6 +1403,7 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) {SPINEL_PROP_CHILD_SUPERVISION_INTERVAL, "CHILD_SUPERVISION_INTERVAL"}, {SPINEL_PROP_CHILD_SUPERVISION_CHECK_TIMEOUT, "CHILD_SUPERVISION_CHECK_TIMEOUT"}, {SPINEL_PROP_RCP_VERSION, "RCP_VERSION"}, + {SPINEL_PROP_RCP_TIMESTAMP, "TIMESTAMP"}, {SPINEL_PROP_RCP_ENH_ACK_PROBING, "ENH_ACK_PROBING"}, {SPINEL_PROP_RCP_CSL_ACCURACY, "CSL_ACCURACY"}, {SPINEL_PROP_RCP_CSL_UNCERTAINTY, "CSL_UNCERTAINTY"}, @@ -1422,6 +1426,7 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) {SPINEL_PROP_SERVER_SERVICES, "SERVER_SERVICES"}, {SPINEL_PROP_SERVER_LEADER_SERVICES, "SERVER_LEADER_SERVICES"}, {SPINEL_PROP_RCP_API_VERSION, "RCP_API_VERSION"}, + {SPINEL_PROP_RCP_MIN_HOST_API_VERSION, "RCP_MIN_HOST_API_VERSION"}, {SPINEL_PROP_UART_BITRATE, "UART_BITRATE"}, {SPINEL_PROP_UART_XON_XOFF, "UART_XON_XOFF"}, {SPINEL_PROP_15_4_PIB_PHY_CHANNELS_SUPPORTED, "15_4_PIB_PHY_CHANNELS_SUPPORTED"}, @@ -1479,8 +1484,6 @@ const char *spinel_prop_key_to_cstr(spinel_prop_key_t prop_key) {SPINEL_PROP_CNTR_ALL_IP_COUNTERS, "CNTR_ALL_IP_COUNTERS"}, {SPINEL_PROP_CNTR_MAC_RETRY_HISTOGRAM, "CNTR_MAC_RETRY_HISTOGRAM"}, {SPINEL_PROP_NEST_STREAM_MFG, "NEST_STREAM_MFG"}, - {SPINEL_PROP_NEST_LEGACY_ULA_PREFIX, "NEST_LEGACY_ULA_PREFIX"}, - {SPINEL_PROP_NEST_LEGACY_LAST_NODE_JOINED, "NEST_LEGACY_LAST_NODE_JOINED"}, {SPINEL_PROP_DEBUG_TEST_ASSERT, "DEBUG_TEST_ASSERT"}, {SPINEL_PROP_DEBUG_NCP_LOG_LEVEL, "DEBUG_NCP_LOG_LEVEL"}, {SPINEL_PROP_DEBUG_TEST_WATCHDOG, "DEBUG_TEST_WATCHDOG"}, @@ -1628,8 +1631,6 @@ const char *spinel_capability_to_cstr(spinel_capability_t capability) {SPINEL_CAP_THREAD_SERVICE, "THREAD_SERVICE"}, {SPINEL_CAP_THREAD_CSL_RECEIVER, "THREAD_CSL_RECEIVER"}, {SPINEL_CAP_THREAD_BACKBONE_ROUTER, "THREAD_BACKBONE_ROUTER"}, - {SPINEL_CAP_NEST_LEGACY_INTERFACE, "NEST_LEGACY_INTERFACE"}, - {SPINEL_CAP_NEST_LEGACY_NET_WAKE, "NEST_LEGACY_NET_WAKE"}, {SPINEL_CAP_NEST_TRANSMIT_HOOK, "NEST_TRANSMIT_HOOK"}, {0, NULL}, }; @@ -1686,7 +1687,7 @@ int main(void) unsigned int i1 = 0; unsigned int i2 = 0; uint32_t l = 0; - const char * str = NULL; + const char *str = NULL; const spinel_eui64_t *eui64 = NULL; len = spinel_datatype_unpack(buffer, (spinel_size_t)len, "CiiLUE", &c, &i1, &i2, &l, &str, &eui64); @@ -1806,7 +1807,7 @@ int main(void) unsigned int i1 = 0; unsigned int i2 = 0; uint32_t l = 0; - const char * str = NULL; + const char *str = NULL; spinel_eui64_t *eui64 = NULL; len = spinel_datatype_unpack(buffer, (spinel_size_t)len, "Cit(iL)UE", &c, &i1, &i2, &l, &str, &eui64); @@ -1930,8 +1931,8 @@ int main(void) const uint8_t str6[] = {0xE5, 0xA2, 0x82, 0xE0, 0xA0, 0x80, 0xC2, 0x83, 0xC2, 0x80, 0xF4, 0x8F, 0xBF, 0xBF, 0xF4, 0x8F, 0xBF, 0xBF, 0xDF, 0xBF, 0x21, 0x00}; - const uint8_t * good_strings[] = {single1, single2, single3, single4, single5, single6, single7, single8, - str1, str2, str3, str4, str5, str6, NULL}; + const uint8_t *good_strings[] = {single1, single2, single3, single4, single5, single6, single7, single8, + str1, str2, str3, str4, str5, str6, NULL}; const uint8_t **str_ptr; for (str_ptr = &good_strings[0]; *str_ptr != NULL; str_ptr++) @@ -1961,7 +1962,7 @@ int main(void) const uint8_t bad5[] = {0x21, 0xA0, 0x00}; const uint8_t bad6[] = {0xCE, 0xBA, 0xE1, 0xBD, 0xB9, 0xCF, 0x83, 0xCE, 0xBC, 0xCE, 0x00}; - const uint8_t * bad_strings[] = {single1, single2, single3, single4, bad1, bad2, bad3, bad4, bad5, bad6, NULL}; + const uint8_t *bad_strings[] = {single1, single2, single3, single4, bad1, bad2, bad3, bad4, bad5, bad6, NULL}; const uint8_t **str_ptr; for (str_ptr = &bad_strings[0]; *str_ptr != NULL; str_ptr++) diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index bc1b8d56429..1f4476a9f7e 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -321,11 +321,54 @@ * * - SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION specifies the minimum spinel * RCP API Version which is supported by the host-side implementation. + * To reduce the backward compatibility issues, this number should be kept + * as constant as possible. * * - On start, host implementation queries the RCP API version and accepts - * any version number from SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION up to - * and including SPINEL_RCP_API_VERSION. + * any version starting from SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION. * + * - Host implementation also queries the RCP about the minimum host RCP + * API version it can work with, and then checks that its own version is + * within the range. + * + * Host and RCP compatibility guideline: + * + * - New host spinel layer should work with an older RCP firmware, i.e., host + * implementation should remain backward compatible. + * + * - Existing fields in the format of an already implemented spinel + * property or command must not change. + * + * - New fields must be appended to the end of the existing spinel format. + * * New fields for new features: + * Adding a new capability flag to the otRadioCaps to indicate the new + * fields. The host parses the spinel format based on the pre-fetched + * otRadioCaps. The host should be able to enable/disable the feature + * in runtime based on the otRadioCaps. Refer to PR4919 and PR5139. + * * New fields for changing existing implementations: + * This case should be avoided as much as possible. It will cause the + * compatibility issue. + * + * - Deprecated fields must not be removed from the spinel format and they + * must be set to a suitable default value. + * + * - Adding new spinel properties. + * * If the old version RCP doesn't support the new spinel property, it + * must return the spinel error SPINEL_STATUS_PROP_NOT_FOUND. + * + * * If the host can handle the new spinel property by processing the error + * SPINEL_STATUS_PROP_NOT_FOUND, the API of the new spinel property must + * return OT_ERROR_NOT_IMPLEMENTED or default value. + * + * * If the host can't handle the new spinel property by processing the + * error SPINEL_STATUS_PROP_NOT_FOUND, a new capability flag must be + * added to the otRadioCaps to indicate whether RCP supports the new + * spinel property. The host must handle the new spinel property by + * processing the new capability flag. + * + * - If none of the above methods make the new functions work, increasing the + * SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION. This case should be avoided + * as much as possible. * --------------------------------------------------------------------------- */ @@ -377,7 +420,7 @@ * Please see section "Spinel definition compatibility guideline" for more details. * */ -#define SPINEL_RCP_API_VERSION 6 +#define SPINEL_RCP_API_VERSION 9 /** * @def SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION @@ -1238,9 +1281,10 @@ enum SPINEL_CAP_NET_THREAD_1_2 = (SPINEL_CAP_NET__BEGIN + 2), SPINEL_CAP_NET__END = 64, - SPINEL_CAP_RCP__BEGIN = 64, - SPINEL_CAP_RCP_API_VERSION = (SPINEL_CAP_RCP__BEGIN + 0), - SPINEL_CAP_RCP__END = 80, + SPINEL_CAP_RCP__BEGIN = 64, + SPINEL_CAP_RCP_API_VERSION = (SPINEL_CAP_RCP__BEGIN + 0), + SPINEL_CAP_RCP_MIN_HOST_API_VERSION = (SPINEL_CAP_RCP__BEGIN + 1), + SPINEL_CAP_RCP__END = 80, SPINEL_CAP_OPENTHREAD__BEGIN = 512, SPINEL_CAP_MAC_ALLOWLIST = (SPINEL_CAP_OPENTHREAD__BEGIN + 0), @@ -1275,8 +1319,8 @@ enum SPINEL_CAP_THREAD__END = 1152, SPINEL_CAP_NEST__BEGIN = 15296, - SPINEL_CAP_NEST_LEGACY_INTERFACE = (SPINEL_CAP_NEST__BEGIN + 0), - SPINEL_CAP_NEST_LEGACY_NET_WAKE = (SPINEL_CAP_NEST__BEGIN + 1), + SPINEL_CAP_NEST_LEGACY_INTERFACE = (SPINEL_CAP_NEST__BEGIN + 0), ///< deprecated + SPINEL_CAP_NEST_LEGACY_NET_WAKE = (SPINEL_CAP_NEST__BEGIN + 1), ///< deprecated SPINEL_CAP_NEST_TRANSMIT_HOOK = (SPINEL_CAP_NEST__BEGIN + 2), SPINEL_CAP_NEST__END = 15360, @@ -1581,14 +1625,14 @@ enum * * For GPIOs configured as inputs: * - * * `CMD_PROP_VAUE_GET`: The value of the associated bit describes the + * * `CMD_PROP_VALUE_GET`: The value of the associated bit describes the * logic level read from the pin. * * `CMD_PROP_VALUE_SET`: The value of the associated bit is ignored * for these pins. * * For GPIOs configured as outputs: * - * * `CMD_PROP_VAUE_GET`: The value of the associated bit is + * * `CMD_PROP_VALUE_GET`: The value of the associated bit is * implementation specific. * * `CMD_PROP_VALUE_SET`: The value of the associated bit determines * the new logic level of the output. If this pin is configured as an @@ -1597,7 +1641,7 @@ enum * * For GPIOs which are not specified in `PROP_GPIO_CONFIG`: * - * * `CMD_PROP_VAUE_GET`: The value of the associated bit is + * * `CMD_PROP_VALUE_GET`: The value of the associated bit is * implementation specific. * * `CMD_PROP_VALUE_SET`: The value of the associated bit MUST be * ignored by the NCP. @@ -1703,6 +1747,28 @@ enum */ SPINEL_PROP_PHY_REGION_CODE = SPINEL_PROP_PHY__BEGIN + 12, + /// Calibrated Power Table + /** Format: `A(Csd)` - Insert/Set + * + * The `Insert` command on the property inserts a calibration power entry to the calibrated power table. + * The `Set` command on the property with empty payload clears the calibrated power table. + * + * Structure Parameters: + * `C`: Channel. + * `s`: Actual power in 0.01 dBm. + * `d`: Raw power setting. + */ + SPINEL_PROP_PHY_CALIBRATED_POWER = SPINEL_PROP_PHY__BEGIN + 13, + + /// Target power for a channel + /** Format: `t(Cs)` - Write only + * + * Structure Parameters: + * `C`: Channel. + * `s`: Target power in 0.01 dBm. + */ + SPINEL_PROP_PHY_CHAN_TARGET_POWER = SPINEL_PROP_PHY__BEGIN + 14, + SPINEL_PROP_PHY__END = 0x30, SPINEL_PROP_PHY_EXT__BEGIN = 0x1200, @@ -1925,7 +1991,7 @@ enum * Channel energy result will be reported by emissions * of `PROP_MAC_ENERGY_SCAN_RESULT` (per channel). * - * Set to `SCAN_STATE_DISOVER` to start a Thread MLE discovery + * Set to `SCAN_STATE_DISCOVER` to start a Thread MLE discovery * scan operation. Discovery scan result will be emitted from * `PROP_MAC_SCAN_BEACON`. * @@ -2295,7 +2361,7 @@ enum SPINEL_PROP_THREAD_LEADER_ADDR = SPINEL_PROP_THREAD__BEGIN + 0, /// Thread Parent Info - /** Format: `ESLccCC` - Read only + /** Format: `ESLccCCCCC` - Read only * * `E`: Extended address * `S`: RLOC16 @@ -2304,6 +2370,9 @@ enum * `c`: Last RSSI (in dBm) * `C`: Link Quality In * `C`: Link Quality Out + * `C`: Version + * `C`: CSL clock accuracy + * `C`: CSL uncertainty * */ SPINEL_PROP_THREAD_PARENT = SPINEL_PROP_THREAD__BEGIN + 1, @@ -2403,7 +2472,7 @@ enum * `6`: Route Prefix * `C`: Prefix length in bits * `b`: Stable flag - * `C`: Route flags (SPINEL_ROUTE_FLAG_* and SPINEL_ROUTE_PREFERNCE_* definitions) + * `C`: Route flags (SPINEL_ROUTE_FLAG_* and SPINEL_ROUTE_PREFERENCE_* definitions) * `b`: "Is defined locally" flag. Set if this route info was locally * defined as part of local network data. Assumed to be true for set, * insert and replace. Clear if the route is part of partition's network @@ -2915,7 +2984,7 @@ enum /** Format: `A(t(iD))` - Write only * * The formatting of this property follows the same rules as in SPINEL_PROP_THREAD_MGMT_SET_ACTIVE_DATASET. This - * property further allows the sender to not include a value associated with properties in formating of `t(iD)`, + * property further allows the sender to not include a value associated with properties in formatting of `t(iD)`, * i.e., it should accept either a `t(iD)` or a `t(i)` encoding (in both cases indicating that the associated * Dataset property should be requested as part of MGMT_GET command). * @@ -3197,7 +3266,7 @@ enum * Write to this property initiates update of Multicast Listeners Table on the primary BBR. * If the write succeeded, the result of network operation will be notified later by the * SPINEL_PROP_THREAD_MLR_RESPONSE property. If the write fails, no MLR.req is issued and - * notifiaction through the SPINEL_PROP_THREAD_MLR_RESPONSE property will not occur. + * notification through the SPINEL_PROP_THREAD_MLR_RESPONSE property will not occur. * */ SPINEL_PROP_THREAD_MLR_REQUEST = SPINEL_PROP_THREAD_EXT__BEGIN + 52, @@ -3250,7 +3319,7 @@ enum * * The valid values are specified by SPINEL_THREAD_BACKBONE_ROUTER_STATE_ enumeration. * Backbone functionality will be disabled if SPINEL_THREAD_BACKBONE_ROUTER_STATE_DISABLED - * is writted to this property, enabled otherwise. + * is written to this property, enabled otherwise. * */ SPINEL_PROP_THREAD_BACKBONE_ROUTER_LOCAL_STATE = SPINEL_PROP_THREAD_EXT__BEGIN + 56, @@ -3442,19 +3511,27 @@ enum * over the radio. This allows the caller to use the radio directly. * * The frame meta data for the `CMD_PROP_VALUE_SET` contains the following - * optional fields. Default values are used for all unspecified fields. + * fields. Default values are used for all unspecified fields. * - * `C` : Channel (for frame tx) + * `C` : Channel (for frame tx) - MUST be included. * `C` : Maximum number of backoffs attempts before declaring CCA failure * (use Thread stack default if not specified) * `C` : Maximum number of retries allowed after a transmission failure * (use Thread stack default if not specified) * `b` : Set to true to enable CSMA-CA for this packet, false otherwise. * (default true). - * `b` : Set to true to indicate it is a retransmission packet, false otherwise. - * (default false). - * `b` : Set to true to indicate that SubMac should skip AES processing, false otherwise. - * (default false). + * `b` : Set to true to indicate if header is updated - related to + * `mIsHeaderUpdated` in `otRadioFrame` (default false). + * `b` : Set to true to indicate it is a retransmission - related to + * `mIsARetx` in `otRadioFrame` (default false). + * `b` : Set to true to indicate security was processed on tx frame + * `mIsSecurityProcessed` in `otRadioFrame` (default false). + * `L` : TX delay interval used for CSL - related to `mTxDelay` in + * `otRadioFrame` (default zero). + * `L` : TX delay based time used for CSL - related to `mTxDelayBaseTime` + * in `otRadioFrame` (default zero). + * `C` : RX channel after TX done (default assumed to be same as + * channel in metadata) * */ SPINEL_PROP_STREAM_RAW = SPINEL_PROP_STREAM__BEGIN + 1, @@ -4284,6 +4361,18 @@ enum */ SPINEL_PROP_RCP_API_VERSION = SPINEL_PROP_RCP__BEGIN + 0, + /// Min host RCP API Version number + /** Format: `i` (read-only) + * + * Required capability: SPINEL_CAP_RADIO and SPINEL_CAP_RCP_MIN_HOST_API_VERSION. + * + * This property gives the minimum host RCP API Version number. + * + * Please see "Spinel definition compatibility guideline" section. + * + */ + SPINEL_PROP_RCP_MIN_HOST_API_VERSION = SPINEL_PROP_RCP__BEGIN + 1, + SPINEL_PROP_RCP__END = 0xFF, SPINEL_PROP_INTERFACE__BEGIN = 0x100, @@ -4691,9 +4780,12 @@ enum SPINEL_PROP_RCP_MAC_KEY = SPINEL_PROP_RCP_EXT__BEGIN + 0, /// MAC Frame Counter - /** Format: `L`. + /** Format: `L` for read and `Lb` or `L` for write * * `L`: MAC frame counter + * 'b': Optional boolean used only during write. If not provided, `false` is assumed. + * If `true` counter is set only if the new value is larger than current value. + * If `false` the new value is set as frame counter independent of the current value. * * The Spinel property is used to set MAC frame counter to RCP. * @@ -4755,12 +4847,20 @@ enum SPINEL_PROP_NEST_STREAM_MFG = SPINEL_PROP_NEST__BEGIN + 0, - /// The legacy network ULA prefix (8 bytes) - /** Format: 'D' */ + /// The legacy network ULA prefix (8 bytes). + /** Format: 'D' + * + * This property is deprecated. + * + */ SPINEL_PROP_NEST_LEGACY_ULA_PREFIX = SPINEL_PROP_NEST__BEGIN + 1, /// The EUI64 of last node joined using legacy protocol (if none, all zero EUI64 is returned). - /** Format: 'E' */ + /** Format: 'E' + * + * This property is deprecated. + * + */ SPINEL_PROP_NEST_LEGACY_LAST_NODE_JOINED = SPINEL_PROP_NEST__BEGIN + 2, SPINEL_PROP_NEST__END = 0x3C00, @@ -4828,6 +4928,9 @@ typedef uint32_t spinel_prop_key_t; // ---------------------------------------------------------------------------- #define SPINEL_HEADER_FLAG 0x80 +#define SPINEL_HEADER_FLAGS_SHIFT 6 +#define SPINEL_HEADER_FLAGS_MASK (3 << SPINEL_HEADER_FLAGS_SHIFT) +#define SPINEL_HEADER_GET_FLAG(x) (((x)&SPINEL_HEADER_FLAGS_MASK) >> SPINEL_HEADER_FLAGS_SHIFT) #define SPINEL_HEADER_TID_SHIFT 0 #define SPINEL_HEADER_TID_MASK (15 << SPINEL_HEADER_TID_SHIFT) @@ -4915,17 +5018,17 @@ typedef char spinel_datatype_t; #define SPINEL_MAX_UINT_PACKED 2097151 -SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_pack(uint8_t * data_out, +SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_pack(uint8_t *data_out, spinel_size_t data_len_max, - const char * pack_format, + const char *pack_format, ...); -SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_vpack(uint8_t * data_out, +SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_vpack(uint8_t *data_out, spinel_size_t data_len_max, - const char * pack_format, + const char *pack_format, va_list args); SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_unpack(const uint8_t *data_in, spinel_size_t data_len, - const char * pack_format, + const char *pack_format, ...); /** * This function parses spinel data similar to sscanf(). @@ -4953,11 +5056,11 @@ SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_unpack(const uint8_t *data_in, */ SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_unpack_in_place(const uint8_t *data_in, spinel_size_t data_len, - const char * pack_format, + const char *pack_format, ...); SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_vunpack(const uint8_t *data_in, spinel_size_t data_len, - const char * pack_format, + const char *pack_format, va_list args); /** * This function parses spinel data similar to vsscanf(). @@ -4983,12 +5086,12 @@ SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_vunpack(const uint8_t *data_in, */ SPINEL_API_EXTERN spinel_ssize_t spinel_datatype_vunpack_in_place(const uint8_t *data_in, spinel_size_t data_len, - const char * pack_format, + const char *pack_format, va_list args); SPINEL_API_EXTERN spinel_ssize_t spinel_packed_uint_decode(const uint8_t *bytes, spinel_size_t len, - unsigned int * value_ptr); + unsigned int *value_ptr); SPINEL_API_EXTERN spinel_ssize_t spinel_packed_uint_encode(uint8_t *bytes, spinel_size_t len, unsigned int value); SPINEL_API_EXTERN spinel_ssize_t spinel_packed_uint_size(unsigned int value); diff --git a/src/lib/spinel/spinel_buffer.cpp b/src/lib/spinel/spinel_buffer.cpp index abaa2af9378..d4c92756292 100644 --- a/src/lib/spinel/spinel_buffer.cpp +++ b/src/lib/spinel/spinel_buffer.cpp @@ -536,20 +536,11 @@ otError Buffer::InFrameEnd(void) return error; } -Buffer::FrameTag Buffer::InFrameGetLastTag(void) const -{ - return mWriteFrameTag; -} +Buffer::FrameTag Buffer::InFrameGetLastTag(void) const { return mWriteFrameTag; } -bool Buffer::HasFrame(Priority aPriority) const -{ - return mReadFrameStart[aPriority] != mWriteFrameStart[aPriority]; -} +bool Buffer::HasFrame(Priority aPriority) const { return mReadFrameStart[aPriority] != mWriteFrameStart[aPriority]; } -bool Buffer::IsEmpty(void) const -{ - return !HasFrame(kPriorityHigh) && !HasFrame(kPriorityLow); -} +bool Buffer::IsEmpty(void) const { return !HasFrame(kPriorityHigh) && !HasFrame(kPriorityLow); } void Buffer::OutFrameSelectReadDirection(void) { @@ -704,10 +695,7 @@ otError Buffer::OutFrameBegin(void) return error; } -bool Buffer::OutFrameHasEnded(void) -{ - return (mReadState == kReadStateDone) || (mReadState == kReadStateNotActive); -} +bool Buffer::OutFrameHasEnded(void) { return (mReadState == kReadStateDone) || (mReadState == kReadStateNotActive); } uint8_t Buffer::OutFrameReadByte(void) { diff --git a/src/lib/spinel/spinel_buffer.hpp b/src/lib/spinel/spinel_buffer.hpp index 6377a1f8289..fecdb645c55 100644 --- a/src/lib/spinel/spinel_buffer.hpp +++ b/src/lib/spinel/spinel_buffer.hpp @@ -625,14 +625,14 @@ class Buffer const uint16_t mBufferLength; // Length of the buffer. BufferCallback mFrameAddedCallback; // Callback to signal when a new frame is added - void * mFrameAddedContext; // Context passed to `mFrameAddedCallback`. + void *mFrameAddedContext; // Context passed to `mFrameAddedCallback`. BufferCallback mFrameRemovedCallback; // Callback to signal when a frame is removed. - void * mFrameRemovedContext; // Context passed to `mFrameRemovedCallback`. + void *mFrameRemovedContext; // Context passed to `mFrameRemovedCallback`. Direction mWriteDirection; // Direction (priority) for current frame being read. - uint8_t * mWriteFrameStart[kNumPrios]; // Pointer to start of current frame being written. - uint8_t * mWriteSegmentHead; // Pointer to start of current segment in the frame being written. - uint8_t * mWriteSegmentTail; // Pointer to end of current segment in the frame being written. + uint8_t *mWriteFrameStart[kNumPrios]; // Pointer to start of current frame being written. + uint8_t *mWriteSegmentHead; // Pointer to start of current segment in the frame being written. + uint8_t *mWriteSegmentTail; // Pointer to end of current segment in the frame being written. FrameTag mWriteFrameTag; // Tag associated with last successfully written frame. Direction mReadDirection; // Direction (priority) for current frame being read. @@ -647,10 +647,10 @@ class Buffer #if OPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE otMessageQueue mWriteFrameMessageQueue; // Message queue for the current frame being written. otMessageQueue mMessageQueue[kNumPrios]; // Main message queues. - otMessage * mReadMessage; // Current Message in the frame being read. + otMessage *mReadMessage; // Current Message in the frame being read. uint16_t mReadMessageOffset; // Offset within current message being read. uint8_t mMessageBuffer[kMessageReadBufferSize]; // Buffer to hold part of current message being read. - uint8_t * mReadMessageTail; // Pointer to end of current part in mMessageBuffer. + uint8_t *mReadMessageTail; // Pointer to end of current part in mMessageBuffer. #endif }; diff --git a/src/lib/spinel/spinel_encoder.hpp b/src/lib/spinel/spinel_encoder.hpp index 079005433e1..214e8136d2d 100644 --- a/src/lib/spinel/spinel_encoder.hpp +++ b/src/lib/spinel/spinel_encoder.hpp @@ -123,7 +123,7 @@ class Encoder /** * This method overwrites the property key with `LAST_STATUS` in a property update command frame. * - * This method should be only used after a successful `BeginFrame(aHeader, aCommand, aPropertKey)`, otherwise, its + * This method should be only used after a successful `BeginFrame(aHeader, aCommand, aPropertyKey)`, otherwise, its * behavior is undefined. * * This method moves the write position back to saved position by `BeginFrame()` and replaces the property key @@ -683,7 +683,7 @@ class Encoder kMaxNestedStructs = 4, ///< Maximum number of nested structs. }; - Spinel::Buffer & mNcpBuffer; + Spinel::Buffer &mNcpBuffer; Spinel::Buffer::WritePosition mStructPosition[kMaxNestedStructs]; uint8_t mNumOpenStructs; diff --git a/src/lib/spinel/spinel_interface.hpp b/src/lib/spinel/spinel_interface.hpp index 585f257aa6d..1feafddd4a6 100644 --- a/src/lib/spinel/spinel_interface.hpp +++ b/src/lib/spinel/spinel_interface.hpp @@ -36,6 +36,7 @@ #define POSIX_APP_SPINEL_INTERFACE_HPP_ #include "lib/hdlc/hdlc.hpp" +#include "lib/spinel/spinel.h" namespace ot { namespace Spinel { @@ -58,6 +59,23 @@ class SpinelInterface typedef Hdlc::MultiFrameBuffer RxFrameBuffer; typedef void (*ReceiveFrameCallback)(void *aContext); + + /** + * This method indicates whether or not the frame is the Spinel SPINEL_CMD_RESET frame. + * + * @param[in] aFrame A pointer to buffer containing the spinel frame. + * @param[in] aLength The length (number of bytes) in the frame. + * + * @retval true If the frame is a Spinel SPINEL_CMD_RESET frame. + * @retval false If the frame is not a Spinel SPINEL_CMD_RESET frame. + * + */ + static bool IsSpinelResetCommand(const uint8_t *aFrame, uint16_t aLength) + { + static constexpr uint8_t kSpinelResetCommand[] = {SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_RESET}; + return (aLength >= sizeof(kSpinelResetCommand)) && + (memcmp(aFrame, kSpinelResetCommand, sizeof(kSpinelResetCommand)) == 0); + } }; } // namespace Spinel } // namespace ot diff --git a/src/lib/url/CMakeLists.txt b/src/lib/url/CMakeLists.txt index ed74f5c8fa7..0ed677bc16e 100644 --- a/src/lib/url/CMakeLists.txt +++ b/src/lib/url/CMakeLists.txt @@ -31,3 +31,16 @@ add_library(openthread-url EXCLUDE_FROM_ALL ) target_link_libraries(openthread-url PRIVATE ot-config) + +if(BUILD_TESTING) + add_executable(ot-test-url + url.cpp + ) + target_compile_definitions(ot-test-url + PRIVATE -DSELF_TEST=1 + ) + target_link_libraries(ot-test-url + PRIVATE ot-config + ) + add_test(NAME ot-test-url COMMAND ot-test-url) +endif() diff --git a/src/lib/url/Makefile.am b/src/lib/url/Makefile.am index 11037e2a938..d90e96e75f3 100644 --- a/src/lib/url/Makefile.am +++ b/src/lib/url/Makefile.am @@ -41,21 +41,4 @@ libopenthread_url_a_SOURCES = \ url.hpp \ $(NULL) -check_PROGRAMS = test-url - -test_url_CPPFLAGS = \ - -I$(top_srcdir)/include \ - -I$(top_srcdir)/src \ - -I$(top_srcdir)/src/core \ - -DSELF_TEST \ - $(NULL) - -test_url_SOURCES = \ - url.cpp \ - $(NULL) - -TESTS = \ - test-url \ - $(NULL) - include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/lib/url/url.cpp b/src/lib/url/url.cpp index 7d4312cd00d..ed576d25755 100644 --- a/src/lib/url/url.cpp +++ b/src/lib/url/url.cpp @@ -40,7 +40,7 @@ namespace Url { otError Url::Init(char *aUrl) { otError error = OT_ERROR_NONE; - char * url = aUrl; + char *url = aUrl; mEnd = aUrl + strlen(aUrl); mProtocol = aUrl; @@ -73,9 +73,9 @@ otError Url::Init(char *aUrl) const char *Url::GetValue(const char *aName, const char *aLastValue) const { - const char * rval = nullptr; + const char *rval = nullptr; const size_t len = strlen(aName); - const char * start; + const char *start; if (aLastValue == nullptr) { @@ -158,7 +158,7 @@ void TestEmptyValue(void) { char url[] = "spinel:///dev/ttyUSB0?rtscts&baudrate=115200&verbose&verbose&verbose"; ot::Url::Url args; - const char * arg = nullptr; + const char *arg = nullptr; assert(!args.Init(url)); assert(!strcmp(args.GetPath(), "/dev/ttyUSB0")); @@ -188,7 +188,7 @@ void TestMultipleProtocolsAndDuplicateParameters(void) { char url[] = "spinel+exec:///path/to/ot-rcp?arg=1&arg=arg2&arg=3"; ot::Url::Url args; - const char * arg = nullptr; + const char *arg = nullptr; assert(!args.Init(url)); assert(!strcmp(args.GetPath(), "/path/to/ot-rcp")); diff --git a/src/ncp/changed_props_set.cpp b/src/ncp/changed_props_set.cpp index 3299c32ebf1..ceac025d994 100644 --- a/src/ncp/changed_props_set.cpp +++ b/src/ncp/changed_props_set.cpp @@ -67,10 +67,6 @@ const ChangedPropsSet::Entry ChangedPropsSet::mSupportedProps[] = { {SPINEL_PROP_LAST_STATUS, SPINEL_STATUS_DROPPED, true}, #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE {SPINEL_PROP_JAM_DETECTED, SPINEL_STATUS_OK, true}, -#endif -#if OPENTHREAD_CONFIG_LEGACY_ENABLE - {SPINEL_PROP_NEST_LEGACY_ULA_PREFIX, SPINEL_STATUS_OK, true}, - {SPINEL_PROP_NEST_LEGACY_LAST_NODE_JOINED, SPINEL_STATUS_OK, true}, #endif {SPINEL_PROP_LAST_STATUS, SPINEL_STATUS_JOIN_FAILURE, false}, {SPINEL_PROP_MAC_SCAN_STATE, SPINEL_STATUS_OK, false}, diff --git a/src/ncp/example_vendor_hook.cpp b/src/ncp/example_vendor_hook.cpp index 4c0b72db163..62c0b6aaa24 100644 --- a/src/ncp/example_vendor_hook.cpp +++ b/src/ncp/example_vendor_hook.cpp @@ -150,7 +150,7 @@ static OT_DEFINE_ALIGNED_VAR(sNcpVendorRaw, sizeof(NcpVendorUart), uint64_t); extern "C" void otAppNcpInit(otInstance *aInstance) { NcpVendorUart *ncpVendor = nullptr; - ot::Instance * instance = static_cast(aInstance); + ot::Instance *instance = static_cast(aInstance); ncpVendor = new (&sNcpVendorRaw) NcpVendorUart(instance); diff --git a/src/ncp/ftd.cmake b/src/ncp/ftd.cmake index 4ba31307dd7..43b5f4f8065 100644 --- a/src/ncp/ftd.cmake +++ b/src/ncp/ftd.cmake @@ -49,5 +49,6 @@ target_link_libraries(openthread-ncp-ftd ${OT_MBEDTLS} openthread-hdlc openthread-spinel-ncp + ot-config-ftd ot-config ) diff --git a/src/ncp/mtd.cmake b/src/ncp/mtd.cmake index e1ceb6f8ad5..6d8de8e14b4 100644 --- a/src/ncp/mtd.cmake +++ b/src/ncp/mtd.cmake @@ -49,5 +49,6 @@ target_link_libraries(openthread-ncp-mtd ${OT_MBEDTLS} openthread-hdlc openthread-spinel-ncp + ot-config-mtd ot-config ) diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index 0f922a31bf8..73541aed75e 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -56,10 +56,7 @@ namespace Ncp { // ---------------------------------------------------------------------------- #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE -static bool HasOnly1BitSet(uint32_t aValue) -{ - return aValue != 0 && ((aValue & (aValue - 1)) == 0); -} +static bool HasOnly1BitSet(uint32_t aValue) { return aValue != 0 && ((aValue & (aValue - 1)) == 0); } static uint8_t IndexOfMSB(uint32_t aValue) { @@ -288,17 +285,13 @@ NcpBase::NcpBase(Instance *aInstance) #if OPENTHREAD_CONFIG_MLE_STEERING_DATA_SET_OOB_ENABLE memset(&mSteeringDataAddress, 0, sizeof(mSteeringDataAddress)); #endif +#if OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE otThreadRegisterParentResponseCallback(mInstance, &NcpBase::HandleParentResponseInfo, static_cast(this)); +#endif #endif // OPENTHREAD_FTD #if OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE otSrpClientSetCallback(mInstance, HandleSrpClientCallback, this); #endif -#if OPENTHREAD_CONFIG_LEGACY_ENABLE - mLegacyNodeDidJoin = false; - mLegacyHandlers = nullptr; - memset(mLegacyUlaPrefix, 0, sizeof(mLegacyUlaPrefix)); - memset(&mLegacyLastJoinedNode, 0, sizeof(mLegacyLastJoinedNode)); -#endif #endif // OPENTHREAD_MTD || OPENTHREAD_FTD mChangedPropsSet.AddLastStatus(SPINEL_STATUS_RESET_UNKNOWN); mUpdateChangedPropsTask.Post(); @@ -308,10 +301,7 @@ NcpBase::NcpBase(Instance *aInstance) #endif } -NcpBase *NcpBase::GetNcpInstance(void) -{ - return sNcpInstance; -} +NcpBase *NcpBase::GetNcpInstance(void) { return sNcpInstance; } void NcpBase::ResetCounters(void) { @@ -334,10 +324,7 @@ void NcpBase::ResetCounters(void) // MARK: Serial Traffic Glue // ---------------------------------------------------------------------------- -Spinel::Buffer::FrameTag NcpBase::GetLastOutboundFrameTag(void) -{ - return mTxFrameBuffer.InFrameGetLastTag(); -} +Spinel::Buffer::FrameTag NcpBase::GetLastOutboundFrameTag(void) { return mTxFrameBuffer.InFrameGetLastTag(); } void NcpBase::HandleReceive(const uint8_t *aBuf, uint16_t aBufLength) { @@ -402,10 +389,10 @@ void NcpBase::HandleReceive(const uint8_t *aBuf, uint16_t aBufLength) mDisableStreamWrite = false; } -void NcpBase::HandleFrameRemovedFromNcpBuffer(void * aContext, +void NcpBase::HandleFrameRemovedFromNcpBuffer(void *aContext, Spinel::Buffer::FrameTag aFrameTag, Spinel::Buffer::Priority aPriority, - Spinel::Buffer * aNcpBuffer) + Spinel::Buffer *aNcpBuffer) { OT_UNUSED_VARIABLE(aNcpBuffer); OT_UNUSED_VARIABLE(aPriority); @@ -477,10 +464,7 @@ bool NcpBase::ShouldDeferHostSend(void) return (mHostPowerState == SPINEL_HOST_POWER_STATE_DEEP_SLEEP && !mHostPowerStateInProgress); } -void NcpBase::IncrementFrameErrorCounter(void) -{ - mFramingErrorCounter++; -} +void NcpBase::IncrementFrameErrorCounter(void) { mFramingErrorCounter++; } otError NcpBase::StreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen) { @@ -689,7 +673,7 @@ void NcpBase::Log(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLog #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE -void NcpBase::RegisterPeekPokeDelagates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate, +void NcpBase::RegisterPeekPokeDelegates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate, otNcpDelegateAllowPeekPoke aAllowPokeDelegate) { mAllowPeekDelegate = aAllowPeekDelegate; @@ -1058,7 +1042,7 @@ otError NcpBase::HandleCommandPropertyInsertRemove(uint8_t aHeader, spinel_prop_ otError error = OT_ERROR_NONE; PropertyHandler handler = nullptr; unsigned int responseCommand = 0; - const uint8_t * valuePtr; + const uint8_t *valuePtr; uint16_t valueLen; switch (aCommand) @@ -1075,7 +1059,6 @@ otError NcpBase::HandleCommandPropertyInsertRemove(uint8_t aHeader, spinel_prop_ default: OT_ASSERT(false); - OT_UNREACHABLE_CODE(break); } VerifyOrExit(handler != nullptr, error = PrepareLastStatusResponse(aHeader, SPINEL_STATUS_PROP_NOT_FOUND)); @@ -1182,7 +1165,7 @@ otError NcpBase::WritePropertyValueIsFrame(uint8_t aHeader, spinel_prop_key_t aP otError NcpBase::WritePropertyValueInsertedRemovedFrame(uint8_t aHeader, unsigned int aResponseCommand, spinel_prop_key_t aPropKey, - const uint8_t * aValuePtr, + const uint8_t *aValuePtr, uint16_t aValueLen) { otError error = OT_ERROR_NONE; @@ -1199,10 +1182,7 @@ otError NcpBase::WritePropertyValueInsertedRemovedFrame(uint8_t aHeade // MARK: Individual Command Handlers // ---------------------------------------------------------------------------- -otError NcpBase::CommandHandler_NOOP(uint8_t aHeader) -{ - return PrepareLastStatusResponse(aHeader, SPINEL_STATUS_OK); -} +otError NcpBase::CommandHandler_NOOP(uint8_t aHeader) { return PrepareLastStatusResponse(aHeader, SPINEL_STATUS_OK); } otError NcpBase::CommandHandler_RESET(uint8_t aHeader) { @@ -1371,7 +1351,7 @@ otError NcpBase::HandlePropertySet_SPINEL_PROP_NEST_STREAM_MFG(uint8_t aHeader) } #endif - otDiagProcessCmdLine(mInstance, string, output, sizeof(output)); + SuccessOrExit(error = otDiagProcessCmdLine(mInstance, string, output, sizeof(output))); // Prepare the response SuccessOrExit(error = mEncoder.BeginFrame(aHeader, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_NEST_STREAM_MFG)); @@ -1860,6 +1840,7 @@ template <> otError NcpBase::HandlePropertyGet(void) #if OPENTHREAD_RADIO SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_RCP_API_VERSION)); + SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_RCP_MIN_HOST_API_VERSION)); #endif #if OPENTHREAD_PLATFORM_POSIX @@ -1888,9 +1869,7 @@ template <> otError NcpBase::HandlePropertyGet(void) SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_JAM_DETECT)); #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_CHILD_SUPERVISION)); -#endif #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_CHANNEL_MONITOR)); @@ -1932,10 +1911,6 @@ template <> otError NcpBase::HandlePropertyGet(void) SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_ROLE_SLEEPY)); -#if OPENTHREAD_CONFIG_LEGACY_ENABLE - SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_NEST_LEGACY_INTERFACE)); -#endif - #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE SuccessOrExit(error = mEncoder.WriteUintPacked(SPINEL_CAP_THREAD_COMMISSIONER)); #endif @@ -2063,7 +2038,6 @@ template <> otError NcpBase::HandlePropertySet(void if (otThreadGetDeviceRole(mInstance) != OT_DEVICE_ROLE_DISABLED) { IgnoreError(otThreadSetEnabled(mInstance, false)); - StopLegacy(); } if (otIp6IsEnabled(mInstance)) @@ -2091,10 +2065,7 @@ template <> otError NcpBase::HandlePropertyGet(void) return mEncoder.WriteUint8(SPINEL_POWER_STATE_ONLINE); } -template <> otError NcpBase::HandlePropertySet(void) -{ - return OT_ERROR_NOT_IMPLEMENTED; -} +template <> otError NcpBase::HandlePropertySet(void) { return OT_ERROR_NOT_IMPLEMENTED; } template <> otError NcpBase::HandlePropertyGet(void) { @@ -2445,7 +2416,6 @@ template <> otError NcpBase::HandlePropertySet( default: ExitNow(error = OT_ERROR_INVALID_ARGS); - OT_UNREACHABLE_CODE(break); } IgnoreError(otLoggingSetLevel(logLevel)); @@ -2586,6 +2556,44 @@ otError NcpBase::DecodeLinkMetrics(otLinkMetrics *aMetrics, bool aAllowPduCount) } #endif +#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +template <> otError NcpBase::HandlePropertySet(void) +{ + otError error; + uint8_t channel; + int16_t targetPower; + + SuccessOrExit(error = mDecoder.ReadUint8(channel)); + SuccessOrExit(error = mDecoder.ReadInt16(targetPower)); + error = otPlatRadioSetChannelTargetPower(mInstance, channel, targetPower); + +exit: + return error; +} + +template <> otError NcpBase::HandlePropertyInsert(void) +{ + otError error; + uint8_t channel; + int16_t actualPower; + const uint8_t *dataPtr; + uint16_t dataLen; + + SuccessOrExit(error = mDecoder.ReadUint8(channel)); + SuccessOrExit(error = mDecoder.ReadInt16(actualPower)); + SuccessOrExit(error = mDecoder.ReadDataWithLen(dataPtr, dataLen)); + error = otPlatRadioAddCalibratedPower(mInstance, channel, actualPower, dataPtr, dataLen); + +exit: + return error; +} + +template <> otError NcpBase::HandlePropertySet(void) +{ + return otPlatRadioClearCalibratedPowers(mInstance); +} +#endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + } // namespace Ncp } // namespace ot @@ -2594,14 +2602,13 @@ otError NcpBase::DecodeLinkMetrics(otLinkMetrics *aMetrics, bool aAllowPduCount) // ---------------------------------------------------------------------------- #if OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE -void otNcpRegisterPeekPokeDelagates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate, - otNcpDelegateAllowPeekPoke aAllowPokeDelegate) +void otNcpRegisterPeekPoke(otNcpDelegateAllowPeekPoke aAllowPeekDelegate, otNcpDelegateAllowPeekPoke aAllowPokeDelegate) { ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance(); if (ncp != nullptr) { - ncp->RegisterPeekPokeDelagates(aAllowPeekDelegate, aAllowPokeDelegate); + ncp->RegisterPeekPoke(aAllowPeekDelegate, aAllowPokeDelegate); } } #endif // OPENTHREAD_CONFIG_NCP_ENABLE_PEEK_POKE diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index f4e9f644aa2..995dbc4d132 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -126,40 +126,10 @@ class NcpBase * @param[in] aAllowPokeDelegate Delegate function pointer for poke operation. * */ - void RegisterPeekPokeDelagates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate, + void RegisterPeekPokeDelegates(otNcpDelegateAllowPeekPoke aAllowPeekDelegate, otNcpDelegateAllowPeekPoke aAllowPokeDelegate); #endif -#if OPENTHREAD_MTD || OPENTHREAD_FTD -#if OPENTHREAD_CONFIG_LEGACY_ENABLE - /** - * This callback is invoked by the legacy stack to notify that a new - * legacy node did join the network. - * - * @param[in] aExtAddr The extended address of the joined node. - * - */ - void HandleLegacyNodeDidJoin(const otExtAddress *aExtAddr); - - /** - * This callback is invoked by the legacy stack to notify that the - * legacy ULA prefix has changed. - * - * param[in] aUlaPrefix The changed ULA prefix. - * - */ - void HandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix); - - /** - * This method registers a set of legacy handlers with NCP. - * - * @param[in] aHandlers A pointer to a handler struct. - * - */ - void RegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers); -#endif -#endif // OPENTHREAD_MTD || OPENTHREAD_FTD - /** * This method is called by the framer whenever a framing error is detected. */ @@ -237,7 +207,7 @@ class NcpBase otError WritePropertyValueInsertedRemovedFrame(uint8_t aHeader, unsigned int aResponseCommand, spinel_prop_key_t aPropKey, - const uint8_t * aValuePtr, + const uint8_t *aValuePtr, uint16_t aValueLen); otError SendQueuedResponses(void); @@ -262,10 +232,10 @@ class NcpBase static void UpdateChangedProps(Tasklet &aTasklet); void UpdateChangedProps(void); - static void HandleFrameRemovedFromNcpBuffer(void * aContext, + static void HandleFrameRemovedFromNcpBuffer(void *aContext, Spinel::Buffer::FrameTag aFrameTag, Spinel::Buffer::Priority aPriority, - Spinel::Buffer * aNcpBuffer); + Spinel::Buffer *aNcpBuffer); void HandleFrameRemovedFromNcpBuffer(Spinel::Buffer::FrameTag aFrameTag); otError EncodeChannelMask(uint32_t aChannelMask); @@ -277,7 +247,7 @@ class NcpBase static void LinkRawReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError); void LinkRawReceiveDone(otRadioFrame *aFrame, otError aError); - static void LinkRawTransmitDone(otInstance * aInstance, + static void LinkRawTransmitDone(otInstance *aInstance, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError); @@ -302,8 +272,10 @@ class NcpBase static void HandleNeighborTableChanged(otNeighborTableEvent aEvent, const otNeighborTableEntryInfo *aEntry); void HandleNeighborTableChanged(otNeighborTableEvent aEvent, const otNeighborTableEntryInfo &aEntry); +#if OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE static void HandleParentResponseInfo(otThreadParentResponseInfo *aInfo, void *aContext); void HandleParentResponseInfo(const otThreadParentResponseInfo &aInfo); +#endif #endif static void HandleDatagramFromStack(otMessage *aMessage, void *aContext); @@ -325,7 +297,7 @@ class NcpBase static void HandleCommissionerEnergyReport_Jump(uint32_t aChannelMask, const uint8_t *aEnergyData, uint8_t aLength, - void * aContext); + void *aContext); void HandleCommissionerEnergyReport(uint32_t aChannelMask, const uint8_t *aEnergyData, uint8_t aLength); static void HandleCommissionerPanIdConflict_Jump(uint16_t aPanId, uint32_t aChannelMask, void *aContext); @@ -338,12 +310,12 @@ class NcpBase #endif #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE - static void HandleLinkMetricsReport_Jump(const otIp6Address * aSource, + static void HandleLinkMetricsReport_Jump(const otIp6Address *aSource, const otLinkMetricsValues *aMetricsValues, uint8_t aStatus, - void * aContext); + void *aContext); - void HandleLinkMetricsReport(const otIp6Address * aSource, + void HandleLinkMetricsReport(const otIp6Address *aSource, const otLinkMetricsValues *aMetricsValues, uint8_t aStatus); @@ -352,16 +324,16 @@ class NcpBase void HandleLinkMetricsMgmtResponse(const otIp6Address *aSource, uint8_t aStatus); static void HandleLinkMetricsEnhAckProbingIeReport_Jump(otShortAddress aShortAddress, - const otExtAddress * aExtAddress, + const otExtAddress *aExtAddress, const otLinkMetricsValues *aMetricsValues, - void * aContext); + void *aContext); void HandleLinkMetricsEnhAckProbingIeReport(otShortAddress aShortAddress, - const otExtAddress * aExtAddress, + const otExtAddress *aExtAddress, const otLinkMetricsValues *aMetricsValues); #endif - static void HandleMlrRegResult_Jump(void * aContext, + static void HandleMlrRegResult_Jump(void *aContext, otError aError, uint8_t aMlrStatus, const otIp6Address *aFailedAddresses, @@ -374,9 +346,9 @@ class NcpBase otError EncodeOperationalDataset(const otOperationalDataset &aDataset); otError DecodeOperationalDataset(otOperationalDataset &aDataset, - const uint8_t ** aTlvs = nullptr, - uint8_t * aTlvsLength = nullptr, - const otIp6Address ** aDestIpAddress = nullptr, + const uint8_t **aTlvs = nullptr, + uint8_t *aTlvsLength = nullptr, + const otIp6Address **aDestIpAddress = nullptr, bool aAllowEmptyValues = false); otError EncodeNeighborInfo(const otNeighborInfo &aNeighborInfo); @@ -393,11 +365,11 @@ class NcpBase #endif #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE - static void HandleUdpForwardStream(otMessage * aMessage, + static void HandleUdpForwardStream(otMessage *aMessage, uint16_t aPeerPort, otIp6Address *aPeerAddr, uint16_t aSockPort, - void * aContext); + void *aContext); void HandleUdpForwardStream(otMessage *aMessage, uint16_t aPeerPort, otIp6Address &aPeerAddr, uint16_t aPort); #endif // OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE #endif // OPENTHREAD_MTD || OPENTHREAD_FTD @@ -485,14 +457,6 @@ class NcpBase void ResetCounters(void); -#if OPENTHREAD_CONFIG_LEGACY_ENABLE - void StartLegacy(void); - void StopLegacy(void); -#else - void StartLegacy(void) {} - void StopLegacy(void) {} -#endif - static uint8_t ConvertLogLevel(otLogLevel aLogLevel); static unsigned int ConvertLogRegion(otLogRegion aLogRegion); @@ -563,10 +527,10 @@ class NcpBase #endif // OPENTHREAD_ENABLE_NCP_VENDOR_HOOK protected: - static NcpBase * sNcpInstance; + static NcpBase *sNcpInstance; static spinel_status_t ThreadErrorToSpinelStatus(otError aError); static uint8_t LinkFlagsToFlagByte(bool aRxOnWhenIdle, bool aDeviceType, bool aNetworkData); - Instance * mInstance; + Instance *mInstance; Spinel::Buffer mTxFrameBuffer; Spinel::Encoder mEncoder; Spinel::Decoder mDecoder; @@ -651,23 +615,17 @@ class NcpBase static void HandleSrpClientCallback(otError aError, const otSrpClientHostInfo *aHostInfo, - const otSrpClientService * aServices, - const otSrpClientService * aRemovedServices, - void * aContext); + const otSrpClientService *aServices, + const otSrpClientService *aRemovedServices, + void *aContext); void HandleSrpClientCallback(otError aError, const otSrpClientHostInfo *aHostInfo, - const otSrpClientService * aServices, - const otSrpClientService * aRemovedServices); + const otSrpClientService *aServices, + const otSrpClientService *aRemovedServices); bool mSrpClientCallbackEnabled; #endif // OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE -#if OPENTHREAD_CONFIG_LEGACY_ENABLE - const otNcpLegacyHandlers *mLegacyHandlers; - uint8_t mLegacyUlaPrefix[OT_NCP_LEGACY_ULA_PREFIX_LENGTH]; - otExtAddress mLegacyLastJoinedNode; - bool mLegacyNodeDidJoin; -#endif #endif // OPENTHREAD_MTD || OPENTHREAD_FTD uint32_t mFramingErrorCounter; // Number of improperly formed received spinel frames. diff --git a/src/ncp/ncp_base_dispatcher.cpp b/src/ncp/ncp_base_dispatcher.cpp index 89b1f61fece..6fc42678531 100644 --- a/src/ncp/ncp_base_dispatcher.cpp +++ b/src/ncp/ncp_base_dispatcher.cpp @@ -153,6 +153,7 @@ NcpBase::PropertyHandler NcpBase::FindGetPropertyHandler(spinel_prop_key_t aKey) #endif // OPENTHREAD_MTD || OPENTHREAD_FTD #if OPENTHREAD_RADIO OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_RCP_API_VERSION), + OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_RCP_MIN_HOST_API_VERSION), #endif #if OPENTHREAD_MTD || OPENTHREAD_FTD OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_CNTR_TX_PKT_TOTAL), @@ -343,10 +344,8 @@ NcpBase::PropertyHandler NcpBase::FindGetPropertyHandler(spinel_prop_key_t aKey) OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_TIME_SYNC_PERIOD), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_TIME_SYNC_XTAL_THRESHOLD), #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_CHILD_SUPERVISION_INTERVAL), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_CHILD_SUPERVISION_CHECK_TIMEOUT), -#endif #endif // OPENTHREAD_FTD #if OPENTHREAD_PLATFORM_POSIX OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_RCP_VERSION), @@ -370,10 +369,6 @@ NcpBase::PropertyHandler NcpBase::FindGetPropertyHandler(spinel_prop_key_t aKey) #endif #endif -#if OPENTHREAD_CONFIG_LEGACY_ENABLE - OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_NEST_LEGACY_ULA_PREFIX), - OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_NEST_LEGACY_LAST_NODE_JOINED), -#endif #endif // OPENTHREAD_MTD || OPENTHREAD_FTD OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_DEBUG_TEST_ASSERT), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_DEBUG_NCP_LOG_LEVEL), @@ -419,6 +414,10 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_PHY_FEM_LNA_GAIN), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_PHY_CHAN_MAX_POWER), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_PHY_REGION_CODE), +#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_PHY_CALIBRATED_POWER), + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_PHY_CHAN_TARGET_POWER), +#endif OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_SCAN_STATE), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_SCAN_MASK), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_SCAN_PERIOD), @@ -502,7 +501,6 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) #if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_RADIO_COEX_ENABLE), #endif - #if OPENTHREAD_MTD || OPENTHREAD_FTD #if OPENTHREAD_CONFIG_MAC_FILTER_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MAC_ALLOWLIST), @@ -606,10 +604,8 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_TIME_SYNC_PERIOD), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_TIME_SYNC_XTAL_THRESHOLD), #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_CHILD_SUPERVISION_INTERVAL), OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_CHILD_SUPERVISION_CHECK_TIMEOUT), -#endif #endif // OPENTHREAD_FTD #if OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_SLAAC_ENABLED), @@ -626,9 +622,6 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_SRP_CLIENT_SERVICE_KEY_ENABLED), #endif #endif -#if OPENTHREAD_CONFIG_LEGACY_ENABLE - OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_NEST_LEGACY_ULA_PREFIX), -#endif #endif // OPENTHREAD_MTD || OPENTHREAD_FTD #if OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_DEBUG_NCP_LOG_LEVEL), @@ -655,6 +648,9 @@ NcpBase::PropertyHandler NcpBase::FindInsertPropertyHandler(spinel_prop_key_t aK } constexpr static HandlerEntry sHandlerEntries[] = { +#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + OT_NCP_INSERT_HANDLER_ENTRY(SPINEL_PROP_PHY_CALIBRATED_POWER), +#endif #if OPENTHREAD_MTD || OPENTHREAD_FTD #if OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE OT_NCP_INSERT_HANDLER_ENTRY(SPINEL_PROP_THREAD_ON_MESH_NETS), diff --git a/src/ncp/ncp_base_ftd.cpp b/src/ncp/ncp_base_ftd.cpp index a7fbdb0baa7..fb763c449fd 100644 --- a/src/ncp/ncp_base_ftd.cpp +++ b/src/ncp/ncp_base_ftd.cpp @@ -39,9 +39,7 @@ #if OPENTHREAD_CONFIG_CHANNEL_MANAGER_ENABLE #include #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE #include -#endif #include #include #include @@ -87,6 +85,7 @@ otError NcpBase::EncodeChildInfo(const otChildInfo &aChildInfo) // MARK: Property/Status Changed // ---------------------------------------------------------------------------- +#if OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE void NcpBase::HandleParentResponseInfo(otThreadParentResponseInfo *aInfo, void *aContext) { VerifyOrExit(aInfo && aContext); @@ -118,6 +117,7 @@ void NcpBase::HandleParentResponseInfo(const otThreadParentResponseInfo &aInfo) exit: return; } +#endif void NcpBase::HandleNeighborTableChanged(otNeighborTableEvent aEvent, const otNeighborTableEntryInfo *aEntry) { @@ -366,9 +366,9 @@ template <> otError NcpBase::HandlePropertyGet(void) } else { - for (size_t i = 0; i < sizeof(otIp6InterfaceIdentifier); i++) + for (uint8_t i : iid->mFields.m8) { - SuccessOrExit(error = mEncoder.WriteUint8(iid->mFields.m8[i])); + SuccessOrExit(error = mEncoder.WriteUint8(i)); } } @@ -388,9 +388,9 @@ template <> otError NcpBase::HandlePropertySet(void) { otIp6InterfaceIdentifier iid; - for (size_t i = 0; i < sizeof(otIp6InterfaceIdentifier); i++) + for (uint8_t &i : iid.mFields.m8) { - SuccessOrExit(error = mDecoder.ReadUint8(iid.mFields.m8[i])); + SuccessOrExit(error = mDecoder.ReadUint8(i)); } SuccessOrExit(error = otThreadSetFixedDuaInterfaceIdentifier(mInstance, &iid)); @@ -763,7 +763,7 @@ template <> otError NcpBase::HandlePropertyInsert otError NcpBase::HandlePropertySet(aContext)->HandleCommissionerEnergyReport(aChannelMask, aEnergyData, aLength); } @@ -997,7 +997,7 @@ template <> otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertySet(void) { otError error = OT_ERROR_NONE; - const uint8_t * tlvs; + const uint8_t *tlvs; uint16_t length; otCommissioningDataset dataset; @@ -1014,8 +1014,8 @@ template <> otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertyInsert(vo { otError error = OT_ERROR_NONE; const otExtAddress *eui64 = nullptr; - const char * pskd = nullptr; + const char *pskd = nullptr; uint32_t joinerTimeout = 0; SuccessOrExit(error = mDecoder.ReadUtf8(pskd)); @@ -1223,8 +1223,6 @@ template <> otError NcpBase::HandlePropertyGet otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint16(otChildSupervisionGetInterval(mInstance)); @@ -1242,8 +1240,6 @@ template <> otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertyGet(void) diff --git a/src/ncp/ncp_base_mtd.cpp b/src/ncp/ncp_base_mtd.cpp index e2dbb532336..48a92705502 100644 --- a/src/ncp/ncp_base_mtd.cpp +++ b/src/ncp/ncp_base_mtd.cpp @@ -40,9 +40,7 @@ #if OPENTHREAD_CONFIG_CHANNEL_MONITOR_ENABLE #include #endif -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE #include -#endif #include #include #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE @@ -365,7 +363,7 @@ template <> otError NcpBase::HandlePropertySet(v return error; } -void NcpBase::HandleMlrRegResult_Jump(void * aContext, +void NcpBase::HandleMlrRegResult_Jump(void *aContext, otError aError, uint8_t aMlrStatus, const otIp6Address *aFailedAddresses, @@ -520,12 +518,10 @@ template <> otError NcpBase::HandlePropertySet(void) if (enabled) { error = otThreadSetEnabled(mInstance, true); - StartLegacy(); } else { error = otThreadSetEnabled(mInstance, false); - StopLegacy(); } } @@ -806,6 +802,11 @@ template <> otError NcpBase::HandlePropertyGet(void) SuccessOrExit(error = mEncoder.WriteInt8(lastRssi)); SuccessOrExit(error = mEncoder.WriteUint8(parentInfo.mLinkQualityIn)); SuccessOrExit(error = mEncoder.WriteUint8(parentInfo.mLinkQualityOut)); + SuccessOrExit(error = mEncoder.WriteUint8(parentInfo.mVersion)); +#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE + SuccessOrExit(error = mEncoder.WriteUint8(parentInfo.mCslClockAccuracy)); + SuccessOrExit(error = mEncoder.WriteUint8(parentInfo.mCslUncertainty)); +#endif } else { @@ -1094,7 +1095,7 @@ template <> otError NcpBase::HandlePropertyInsert(v otError error = OT_ERROR_NONE; otServiceConfig cfg; bool stable; - const uint8_t * data; + const uint8_t *data; uint16_t dataLen; VerifyOrExit(mAllowLocalServerDataChange, error = OT_ERROR_INVALID_STATE); @@ -1367,9 +1368,9 @@ template <> otError NcpBase::HandlePropertyGet otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertySet(void) uint16_t frameLen = 0; const uint8_t *metaPtr = nullptr; uint16_t metaLen = 0; - otMessage * message = nullptr; + otMessage *message = nullptr; otError error = OT_ERROR_NONE; SuccessOrExit(error = mDecoder.ReadDataWithLen(framePtr, frameLen)); @@ -2388,8 +2389,6 @@ void NcpBase::HandleJamStateChange(bool aJamState) #endif // OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE -#if OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - template <> otError NcpBase::HandlePropertyGet(void) { return mEncoder.WriteUint16(otChildSupervisionGetCheckTimeout(mInstance)); @@ -2407,8 +2406,6 @@ template <> otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertyGet(void) @@ -3286,11 +3283,11 @@ template <> otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertySet(void) { - const uint8_t * framePtr = nullptr; + const uint8_t *framePtr = nullptr; uint16_t frameLen = 0; - const uint8_t * metaPtr = nullptr; + const uint8_t *metaPtr = nullptr; uint16_t metaLen = 0; - otMessage * message = nullptr; + otMessage *message = nullptr; otError error = OT_ERROR_NONE; otMessageSettings msgSettings = {false, OT_MESSAGE_PRIORITY_NORMAL}; @@ -3662,7 +3659,7 @@ template <> otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertySet otError error; const char *name; uint16_t size; - char * hostNameBuffer; + char *hostNameBuffer; SuccessOrExit(error = mDecoder.ReadUtf8(name)); @@ -3835,9 +3832,9 @@ template <> otError NcpBase::HandlePropertyInsert otError NcpBase::HandlePropertyInsert otError NcpBase::HandlePropertyRemove(void) { otError error = OT_ERROR_NONE; - const char * serviceName; - const char * instanceName; + const char *serviceName; + const char *instanceName; bool toClear = false; const otSrpClientService *service; @@ -3976,17 +3973,17 @@ static spinel_srp_client_error_t SrpClientErrorToSpinelError(otError aError) void NcpBase::HandleSrpClientCallback(otError aError, const otSrpClientHostInfo *aHostInfo, - const otSrpClientService * aServices, - const otSrpClientService * aRemovedServices, - void * aContext) + const otSrpClientService *aServices, + const otSrpClientService *aRemovedServices, + void *aContext) { static_cast(aContext)->HandleSrpClientCallback(aError, aHostInfo, aServices, aRemovedServices); } void NcpBase::HandleSrpClientCallback(otError aError, const otSrpClientHostInfo *aHostInfo, - const otSrpClientService * aServices, - const otSrpClientService * aRemovedServices) + const otSrpClientService *aServices, + const otSrpClientService *aRemovedServices) { otError error = OT_ERROR_NONE; const otSrpClientService *service; @@ -4076,115 +4073,6 @@ template <> otError NcpBase::HandlePropertySetmStartLegacy) - { - mLegacyHandlers->mStartLegacy(); - } - } - else - { - if (mLegacyHandlers->mStopLegacy) - { - mLegacyHandlers->mStopLegacy(); - } - } - - if (mLegacyHandlers->mSetLegacyUlaPrefix) - { - mLegacyHandlers->mSetLegacyUlaPrefix(mLegacyUlaPrefix); - } - -exit: - return; -} - -void NcpBase::HandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix) -{ - memcpy(mLegacyUlaPrefix, aUlaPrefix, OT_NCP_LEGACY_ULA_PREFIX_LENGTH); - mChangedPropsSet.AddProperty(SPINEL_PROP_NEST_LEGACY_ULA_PREFIX); - mUpdateChangedPropsTask.Post(); -} - -void NcpBase::HandleLegacyNodeDidJoin(const otExtAddress *aExtAddr) -{ - mLegacyNodeDidJoin = true; - mLegacyLastJoinedNode = *aExtAddr; - mChangedPropsSet.AddProperty(SPINEL_PROP_NEST_LEGACY_LAST_NODE_JOINED); - mUpdateChangedPropsTask.Post(); -} - -template <> otError NcpBase::HandlePropertyGet(void) -{ - return mEncoder.WriteData(mLegacyUlaPrefix, sizeof(mLegacyUlaPrefix)); -} - -template <> otError NcpBase::HandlePropertySet(void) -{ - const uint8_t *ptr = nullptr; - uint16_t len; - otError error = OT_ERROR_NONE; - - SuccessOrExit(error = mDecoder.ReadData(ptr, len)); - - VerifyOrExit(len <= sizeof(mLegacyUlaPrefix), error = OT_ERROR_PARSE); - - memset(mLegacyUlaPrefix, 0, sizeof(mLegacyUlaPrefix)); - memcpy(mLegacyUlaPrefix, ptr, len); - - if ((mLegacyHandlers != nullptr) && (mLegacyHandlers->mSetLegacyUlaPrefix != nullptr)) - { - mLegacyHandlers->mSetLegacyUlaPrefix(mLegacyUlaPrefix); - } - -exit: - return error; -} - -template <> otError NcpBase::HandlePropertyGet(void) -{ - if (!mLegacyNodeDidJoin) - { - memset(&mLegacyLastJoinedNode, 0, sizeof(mLegacyLastJoinedNode)); - } - - return mEncoder.WriteEui64(mLegacyLastJoinedNode); -} - -void NcpBase::StartLegacy(void) -{ - mLegacyNodeDidJoin = false; - - if ((mLegacyHandlers != nullptr) && (mLegacyHandlers->mStartLegacy != nullptr)) - { - mLegacyHandlers->mStartLegacy(); - } -} - -void NcpBase::StopLegacy(void) -{ - mLegacyNodeDidJoin = false; - - if ((mLegacyHandlers != nullptr) && (mLegacyHandlers->mStopLegacy != nullptr)) - { - mLegacyHandlers->mStopLegacy(); - } -} - -#endif // OPENTHREAD_CONFIG_LEGACY_ENABLE - #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE template <> otError NcpBase::HandlePropertyGet(void) { @@ -4201,10 +4089,7 @@ template <> otError NcpBase::HandlePropertyGet( return error; } -void NcpBase::HandleTimeSyncUpdate(void *aContext) -{ - static_cast(aContext)->HandleTimeSyncUpdate(); -} +void NcpBase::HandleTimeSyncUpdate(void *aContext) { static_cast(aContext)->HandleTimeSyncUpdate(); } void NcpBase::HandleTimeSyncUpdate(void) { @@ -4343,15 +4228,15 @@ void NcpBase::HandleJoinerCallback(otError aError) #endif #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE -void NcpBase::HandleLinkMetricsReport_Jump(const otIp6Address * aSource, +void NcpBase::HandleLinkMetricsReport_Jump(const otIp6Address *aSource, const otLinkMetricsValues *aMetricsValues, uint8_t aStatus, - void * aContext) + void *aContext) { static_cast(aContext)->HandleLinkMetricsReport(aSource, aMetricsValues, aStatus); } -void NcpBase::HandleLinkMetricsReport(const otIp6Address * aSource, +void NcpBase::HandleLinkMetricsReport(const otIp6Address *aSource, const otLinkMetricsValues *aMetricsValues, uint8_t aStatus) { @@ -4388,16 +4273,16 @@ void NcpBase::HandleLinkMetricsMgmtResponse(const otIp6Address *aSource, uint8_t } void NcpBase::HandleLinkMetricsEnhAckProbingIeReport_Jump(otShortAddress aShortAddress, - const otExtAddress * aExtAddress, + const otExtAddress *aExtAddress, const otLinkMetricsValues *aMetricsValues, - void * aContext) + void *aContext) { static_cast(aContext)->HandleLinkMetricsEnhAckProbingIeReport(aShortAddress, aExtAddress, aMetricsValues); } void NcpBase::HandleLinkMetricsEnhAckProbingIeReport(otShortAddress aShortAddress, - const otExtAddress * aExtAddress, + const otExtAddress *aExtAddress, const otLinkMetricsValues *aMetricsValues) { SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS, @@ -4508,12 +4393,12 @@ otError NcpBase::SendQueuedDatagramMessages(void) #if OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE template <> otError NcpBase::HandlePropertySet(void) { - const uint8_t * framePtr = nullptr; + const uint8_t *framePtr = nullptr; uint16_t frameLen = 0; const otIp6Address *peerAddr; uint16_t peerPort; uint16_t sockPort; - otMessage * message; + otMessage *message; otError error = OT_ERROR_NONE; otMessageSettings msgSettings = {false, OT_MESSAGE_PRIORITY_NORMAL}; @@ -4543,11 +4428,11 @@ template <> otError NcpBase::HandlePropertySet(aContext)->HandleUdpForwardStream(aMessage, aPeerPort, *aPeerAddr, aSockPort); } @@ -4717,11 +4602,7 @@ void NcpBase::ProcessThreadChangedFlags(void) break; } - if ((otThreadGetDeviceRole(mInstance) == OT_DEVICE_ROLE_LEADER) && otThreadIsSingleton(mInstance) -#if OPENTHREAD_CONFIG_LEGACY_ENABLE - && !mLegacyNodeDidJoin -#endif - ) + if ((otThreadGetDeviceRole(mInstance) == OT_DEVICE_ROLE_LEADER) && otThreadIsSingleton(mInstance)) { mThreadChangedFlags &= ~static_cast(OT_CHANGED_THREAD_PARTITION_ID); IgnoreError(otThreadSetEnabled(mInstance, false)); @@ -4784,53 +4665,4 @@ void NcpBase::ProcessThreadChangedFlags(void) } // namespace Ncp } // namespace ot -// ---------------------------------------------------------------------------- -// MARK: Legacy network APIs -// ---------------------------------------------------------------------------- - -void otNcpRegisterLegacyHandlers(const otNcpLegacyHandlers *aHandlers) -{ -#if OPENTHREAD_CONFIG_LEGACY_ENABLE - ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance(); - - if (ncp != nullptr) - { - ncp->RegisterLegacyHandlers(aHandlers); - } - -#else - OT_UNUSED_VARIABLE(aHandlers); -#endif -} - -void otNcpHandleDidReceiveNewLegacyUlaPrefix(const uint8_t *aUlaPrefix) -{ -#if OPENTHREAD_CONFIG_LEGACY_ENABLE - ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance(); - - if (ncp != nullptr) - { - ncp->HandleDidReceiveNewLegacyUlaPrefix(aUlaPrefix); - } - -#else - OT_UNUSED_VARIABLE(aUlaPrefix); -#endif -} - -void otNcpHandleLegacyNodeDidJoin(const otExtAddress *aExtAddr) -{ -#if OPENTHREAD_CONFIG_LEGACY_ENABLE - ot::Ncp::NcpBase *ncp = ot::Ncp::NcpBase::GetNcpInstance(); - - if (ncp != nullptr) - { - ncp->HandleLegacyNodeDidJoin(aExtAddr); - } - -#else - OT_UNUSED_VARIABLE(aExtAddr); -#endif -} - #endif // OPENTHREAD_MTD || OPENTHREAD_FTD diff --git a/src/ncp/ncp_base_radio.cpp b/src/ncp/ncp_base_radio.cpp index 975cc3fcb94..11ec6aa0d78 100644 --- a/src/ncp/ncp_base_radio.cpp +++ b/src/ncp/ncp_base_radio.cpp @@ -53,6 +53,11 @@ template <> otError NcpBase::HandlePropertyGet(void { return mEncoder.WriteUintPacked(SPINEL_RCP_API_VERSION); } + +template <> otError NcpBase::HandlePropertyGet(void) +{ + return mEncoder.WriteUintPacked(SPINEL_MIN_HOST_SUPPORTED_RCP_API_VERSION); +} #endif // ---------------------------------------------------------------------------- @@ -403,14 +408,15 @@ otError NcpBase::DecodeStreamRawTxRequest(otRadioFrame &aFrame) SuccessOrExit(error = mDecoder.ReadUint8(aFrame.mChannel)); // Set the default value for all optional parameters. - aFrame.mInfo.mTxInfo.mMaxCsmaBackoffs = OPENTHREAD_CONFIG_MAC_MAX_CSMA_BACKOFFS_DIRECT; - aFrame.mInfo.mTxInfo.mMaxFrameRetries = OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_DIRECT; - aFrame.mInfo.mTxInfo.mCsmaCaEnabled = true; - aFrame.mInfo.mTxInfo.mIsHeaderUpdated = false; - aFrame.mInfo.mTxInfo.mIsARetx = false; - aFrame.mInfo.mTxInfo.mIsSecurityProcessed = false; - aFrame.mInfo.mTxInfo.mTxDelay = 0; - aFrame.mInfo.mTxInfo.mTxDelayBaseTime = 0; + aFrame.mInfo.mTxInfo.mRxChannelAfterTxDone = aFrame.mChannel; + aFrame.mInfo.mTxInfo.mMaxCsmaBackoffs = OPENTHREAD_CONFIG_MAC_MAX_CSMA_BACKOFFS_DIRECT; + aFrame.mInfo.mTxInfo.mMaxFrameRetries = OPENTHREAD_CONFIG_MAC_DEFAULT_MAX_FRAME_RETRIES_DIRECT; + aFrame.mInfo.mTxInfo.mCsmaCaEnabled = true; + aFrame.mInfo.mTxInfo.mIsHeaderUpdated = false; + aFrame.mInfo.mTxInfo.mIsARetx = false; + aFrame.mInfo.mTxInfo.mIsSecurityProcessed = false; + aFrame.mInfo.mTxInfo.mTxDelay = 0; + aFrame.mInfo.mTxInfo.mTxDelayBaseTime = 0; // All the next parameters are optional. Note that even if the // decoder fails to parse any of optional parameters we still want to @@ -419,16 +425,22 @@ otError NcpBase::DecodeStreamRawTxRequest(otRadioFrame &aFrame) SuccessOrExit(mDecoder.ReadUint8(aFrame.mInfo.mTxInfo.mMaxCsmaBackoffs)); SuccessOrExit(mDecoder.ReadUint8(aFrame.mInfo.mTxInfo.mMaxFrameRetries)); + SuccessOrExit(mDecoder.ReadBool(csmaEnable)); + aFrame.mInfo.mTxInfo.mCsmaCaEnabled = csmaEnable; + SuccessOrExit(mDecoder.ReadBool(isHeaderUpdated)); + aFrame.mInfo.mTxInfo.mIsHeaderUpdated = isHeaderUpdated; + SuccessOrExit(mDecoder.ReadBool(isARetx)); + aFrame.mInfo.mTxInfo.mIsARetx = isARetx; + SuccessOrExit(mDecoder.ReadBool(isSecurityProcessed)); + aFrame.mInfo.mTxInfo.mIsSecurityProcessed = isSecurityProcessed; + SuccessOrExit(mDecoder.ReadUint32(aFrame.mInfo.mTxInfo.mTxDelay)); SuccessOrExit(mDecoder.ReadUint32(aFrame.mInfo.mTxInfo.mTxDelayBaseTime)); - aFrame.mInfo.mTxInfo.mCsmaCaEnabled = csmaEnable; - aFrame.mInfo.mTxInfo.mIsHeaderUpdated = isHeaderUpdated; - aFrame.mInfo.mTxInfo.mIsARetx = isARetx; - aFrame.mInfo.mTxInfo.mIsSecurityProcessed = isSecurityProcessed; + SuccessOrExit(mDecoder.ReadUint8(aFrame.mInfo.mTxInfo.mRxChannelAfterTxDone)); exit: return error; @@ -446,12 +458,12 @@ otError NcpBase::HandlePropertySet_SPINEL_PROP_STREAM_RAW(uint8_t aHeader) SuccessOrExit(error = DecodeStreamRawTxRequest(*frame)); - // Cache the transaction ID for async response - mCurTransmitTID = SPINEL_HEADER_GET_TID(aHeader); - // Pass frame to the radio layer. Note, this fails if we // haven't enabled raw stream or are already transmitting. - error = otLinkRawTransmit(mInstance, &NcpBase::LinkRawTransmitDone); + SuccessOrExit(error = otLinkRawTransmit(mInstance, &NcpBase::LinkRawTransmitDone)); + + // Cache the transaction ID for async response + mCurTransmitTID = SPINEL_HEADER_GET_TID(aHeader); exit: @@ -503,10 +515,23 @@ template <> otError NcpBase::HandlePropertySet(aInstance); ncpHdlc = new (&sNcpRaw) NcpHdlc(instance, aSendCallback); @@ -95,10 +95,10 @@ NcpHdlc::NcpHdlc(Instance *aInstance, otNcpHdlcSendCallback aSendCallback) mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToNcpBuffer, this); } -void NcpHdlc::HandleFrameAddedToNcpBuffer(void * aContext, +void NcpHdlc::HandleFrameAddedToNcpBuffer(void *aContext, Spinel::Buffer::FrameTag aTag, Spinel::Buffer::Priority aPriority, - Spinel::Buffer * aBuffer) + Spinel::Buffer *aBuffer) { OT_UNUSED_VARIABLE(aBuffer); OT_UNUSED_VARIABLE(aTag); @@ -240,10 +240,7 @@ void NcpHdlc::HandleHdlcReceiveDone(const uint8_t *aBuf, uint16_t aBufLength) mFrameDecoder.Decode(aBuf, aBufLength); } -void NcpHdlc::HandleFrame(void *aContext, otError aError) -{ - static_cast(aContext)->HandleFrame(aError); -} +void NcpHdlc::HandleFrame(void *aContext, otError aError) { static_cast(aContext)->HandleFrame(aError); } void NcpHdlc::HandleFrame(otError aError) { @@ -277,8 +274,6 @@ void NcpHdlc::HandleError(otError aError, uint8_t *aBuf, uint16_t aBufLength) super_t::IncrementFrameErrorCounter(); - // We can get away with sprintf because we know - // `hexbuf` is large enough. snprintf(hexbuf, sizeof(hexbuf), "Framing error %d: [", aError); // Write out the first part of our log message. @@ -288,9 +283,6 @@ void NcpHdlc::HandleError(otError aError, uint8_t *aBuf, uint16_t aBufLength) // The second '3' comes from the length of two hex digits and a space. for (i = 0; (i < aBufLength) && (i < (sizeof(hexbuf) - 3) / 3); i++) { - // We can get away with sprintf because we know - // `hexbuf` is large enough, based on our calculations - // above. snprintf(&hexbuf[i * 3], sizeof(hexbuf) - i * 3, " %02X", static_cast(aBuf[i])); } @@ -312,10 +304,7 @@ NcpHdlc::BufferEncrypterReader::BufferEncrypterReader(Spinel::Buffer &aTxFrameBu { } -bool NcpHdlc::BufferEncrypterReader::IsEmpty(void) const -{ - return mTxFrameBuffer.IsEmpty() && !mOutputDataLength; -} +bool NcpHdlc::BufferEncrypterReader::IsEmpty(void) const { return mTxFrameBuffer.IsEmpty() && !mOutputDataLength; } otError NcpHdlc::BufferEncrypterReader::OutFrameBegin(void) { @@ -347,20 +336,11 @@ otError NcpHdlc::BufferEncrypterReader::OutFrameBegin(void) return status; } -bool NcpHdlc::BufferEncrypterReader::OutFrameHasEnded(void) -{ - return (mDataBufferReadIndex >= mOutputDataLength); -} +bool NcpHdlc::BufferEncrypterReader::OutFrameHasEnded(void) { return (mDataBufferReadIndex >= mOutputDataLength); } -uint8_t NcpHdlc::BufferEncrypterReader::OutFrameReadByte(void) -{ - return mDataBuffer[mDataBufferReadIndex++]; -} +uint8_t NcpHdlc::BufferEncrypterReader::OutFrameReadByte(void) { return mDataBuffer[mDataBufferReadIndex++]; } -otError NcpHdlc::BufferEncrypterReader::OutFrameRemove(void) -{ - return mTxFrameBuffer.OutFrameRemove(); -} +otError NcpHdlc::BufferEncrypterReader::OutFrameRemove(void) { return mTxFrameBuffer.OutFrameRemove(); } void NcpHdlc::BufferEncrypterReader::Reset(void) { diff --git a/src/ncp/ncp_hdlc.hpp b/src/ncp/ncp_hdlc.hpp index e3c489f76f5..d0516c0012a 100644 --- a/src/ncp/ncp_hdlc.hpp +++ b/src/ncp/ncp_hdlc.hpp @@ -122,15 +122,15 @@ class NcpHdlc : public NcpBase static void EncodeAndSend(Tasklet &aTasklet); static void HandleFrame(void *aContext, otError aError); - static void HandleFrameAddedToNcpBuffer(void * aContext, + static void HandleFrameAddedToNcpBuffer(void *aContext, Spinel::Buffer::FrameTag aTag, Spinel::Buffer::Priority aPriority, - Spinel::Buffer * aBuffer); + Spinel::Buffer *aBuffer); otNcpHdlcSendCallback mSendCallback; + Hdlc::FrameBuffer mHdlcBuffer; Hdlc::Encoder mFrameEncoder; Hdlc::Decoder mFrameDecoder; - Hdlc::FrameBuffer mHdlcBuffer; HdlcTxState mState; uint8_t mByte; Hdlc::FrameBuffer mRxBuffer; diff --git a/src/ncp/ncp_spi.cpp b/src/ncp/ncp_spi.cpp index 618769bc308..9b2a4729cda 100644 --- a/src/ncp/ncp_spi.cpp +++ b/src/ncp/ncp_spi.cpp @@ -64,7 +64,7 @@ static OT_DEFINE_ALIGNED_VAR(sNcpRaw, sizeof(NcpSpi), uint64_t); extern "C" void otNcpSpiInit(otInstance *aInstance) { - NcpSpi * ncpSpi = nullptr; + NcpSpi *ncpSpi = nullptr; Instance *instance = static_cast(aInstance); ncpSpi = new (&sNcpRaw) NcpSpi(instance); @@ -114,7 +114,7 @@ NcpSpi::NcpSpi(Instance *aInstance) /* aRequestTransactionFlag */ true)); } -bool NcpSpi::SpiTransactionComplete(void * aContext, +bool NcpSpi::SpiTransactionComplete(void *aContext, uint8_t *aOutputBuf, uint16_t aOutputLen, uint8_t *aInputBuf, @@ -224,10 +224,7 @@ bool NcpSpi::SpiTransactionComplete(uint8_t *aOutputBuf, return shouldProcess; } -void NcpSpi::SpiTransactionProcess(void *aContext) -{ - reinterpret_cast(aContext)->SpiTransactionProcess(); -} +void NcpSpi::SpiTransactionProcess(void *aContext) { reinterpret_cast(aContext)->SpiTransactionProcess(); } void NcpSpi::SpiTransactionProcess(void) { @@ -242,10 +239,10 @@ void NcpSpi::SpiTransactionProcess(void) } } -void NcpSpi::HandleFrameAddedToTxBuffer(void * aContext, +void NcpSpi::HandleFrameAddedToTxBuffer(void *aContext, Spinel::Buffer::FrameTag aTag, Spinel::Buffer::Priority aPriority, - Spinel::Buffer * aBuffer) + Spinel::Buffer *aBuffer) { OT_UNUSED_VARIABLE(aBuffer); OT_UNUSED_VARIABLE(aTag); diff --git a/src/ncp/ncp_spi.hpp b/src/ncp/ncp_spi.hpp index d4a87dd1738..43c80dbcb7e 100644 --- a/src/ncp/ncp_spi.hpp +++ b/src/ncp/ncp_spi.hpp @@ -278,7 +278,7 @@ class NcpSpi : public NcpBase typedef uint8_t LargeFrameBuffer[kSpiBufferSize]; typedef uint8_t EmptyFrameBuffer[kSpiHeaderSize]; - static bool SpiTransactionComplete(void * aContext, + static bool SpiTransactionComplete(void *aContext, uint8_t *aOutputBuf, uint16_t aOutputLen, uint8_t *aInputBuf, @@ -293,10 +293,10 @@ class NcpSpi : public NcpBase static void SpiTransactionProcess(void *aContext); void SpiTransactionProcess(void); - static void HandleFrameAddedToTxBuffer(void * aContext, + static void HandleFrameAddedToTxBuffer(void *aContext, Spinel::Buffer::FrameTag aFrameTag, Spinel::Buffer::Priority aPriority, - Spinel::Buffer * aBuffer); + Spinel::Buffer *aBuffer); static void PrepareTxFrame(Tasklet &aTasklet); void PrepareTxFrame(void); diff --git a/src/ncp/radio.cmake b/src/ncp/radio.cmake index 427e7b5da86..1350a28e857 100644 --- a/src/ncp/radio.cmake +++ b/src/ncp/radio.cmake @@ -31,9 +31,14 @@ add_library(openthread-rcp) target_compile_definitions(openthread-rcp PRIVATE OPENTHREAD_RADIO=1 OPENTHREAD_RADIO_CLI=0 - OPENTHREAD_CONFIG_NCP_HDLC_ENABLE=1 ) +if (OT_NCP_SPI) + target_compile_definitions(openthread-rcp PRIVATE OPENTHREAD_CONFIG_NCP_HDLC_ENABLE=0) +else() + target_compile_definitions(openthread-rcp PRIVATE OPENTHREAD_CONFIG_NCP_HDLC_ENABLE=1) +endif() + target_compile_options(openthread-rcp PRIVATE ${OT_CFLAGS} ) @@ -47,7 +52,11 @@ target_link_libraries(openthread-rcp PUBLIC openthread-radio PRIVATE - openthread-hdlc openthread-spinel-rcp + ot-config-radio ot-config ) + +if(NOT OT_NCP_SPI) + target_link_libraries(openthread-rcp PRIVATE openthread-hdlc) +endif() diff --git a/src/posix/Makefile-posix b/src/posix/Makefile-posix index 24c7a83fd9b..7fe9722de56 100644 --- a/src/posix/Makefile-posix +++ b/src/posix/Makefile-posix @@ -44,7 +44,6 @@ COAPS ?= 1 COMMISSIONER ?= 1 CHANNEL_MANAGER ?= 1 CHANNEL_MONITOR ?= 1 -CHILD_SUPERVISION ?= 1 DAEMON ?= 0 DATASET_UPDATER ?= 1 DHCP6_CLIENT ?= 1 @@ -58,14 +57,13 @@ HISTORY_TRACKER ?= 1 IP6_FRAGM ?= 1 JAM_DETECTION ?= 1 JOINER ?= 1 -LEGACY ?= 1 LINK_RAW ?= 0 LOG_OUTPUT ?= PLATFORM_DEFINED MAC_FILTER ?= 1 MAX_POWER_TABLE ?= 1 -MTD_NETDIAG ?= 1 NEIGHBOR_DISCOVERY_AGENT ?= 1 NETDATA_PUBLISHER ?= 1 +NETDIAG_CLIENT ?= 1 PING_SENDER ?= 1 READLINE ?= readline REFERENCE_DEVICE ?= 1 @@ -145,6 +143,7 @@ CXXFLAGS += \ LDFLAGS += \ $(COMMONCFLAGS) \ + -rdynamic \ $(NULL) TopSourceDir := $(dir $(shell readlink $(firstword $(MAKEFILE_LIST))))../.. diff --git a/src/posix/Makefile.am b/src/posix/Makefile.am index 3844ea1f32d..c0a07666066 100644 --- a/src/posix/Makefile.am +++ b/src/posix/Makefile.am @@ -62,6 +62,7 @@ LDADD_COMMON = \ if OPENTHREAD_TARGET_LINUX LDADD_COMMON += \ + -lanl \ -lrt \ $(NULL) endif diff --git a/src/posix/README.md b/src/posix/README.md index ced9af58bac..ccffb3612f6 100644 --- a/src/posix/README.md +++ b/src/posix/README.md @@ -28,7 +28,7 @@ If built successfully, the binary should be found at: `build/posix/src/posix/ot- ### Simulation -OpenThread provides an implemenation on the simulation platform which enables running a simulated transceiver on the host. +OpenThread provides an implementation on the simulation platform which enables running a simulated transceiver on the host. #### Build @@ -77,19 +77,25 @@ To build and program the device with RCP application, complete the following ste ```sh rm -rf build - script/build nrf52840 USB_trans -DOT_BOOTLOADER=USB -DOT_THREAD_VERSION=1.2 + script/build nrf52840 USB_trans -DOT_BOOTLOADER=USB ``` b. For nRF52840 Development Kit ```sh rm -rf build - script/build nrf52840 UART_trans -DOT_THREAD_VERSION=1.2 + script/build nrf52840 UART_trans ``` This creates an RCP image at `build/bin/ot-rcp`. -5. Depending on the hardware platform, complete the following steps to program the device: +5. Generate the HEX image: + + ```sh + arm-none-eabi-objcopy -O ihex build/bin/ot-rcp build/bin/ot-rcp.hex + ``` + +6. Depending on the hardware platform, complete the following steps to program the device: a. nRF52840 Dongle (USB transport) @@ -133,28 +139,6 @@ To build and program the device with RCP application, complete the following ste ./build/posix/src/posix/ot-cli 'spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=115200' ``` -### CC2538 - -#### Build - -``` -./script/cmake-build cc2538 -DOT_APP_CLI=OFF -DOT_APP_NCP=OFF -DOT_FTD=OFF -DOT_MTD=OFF -``` - -#### Flash - -```sh -arm-none-eabi-objcopy -O ihex build/cc2538/examples/apps/ncp/ot-rcp ot-rcp.bin -# see https://github.com/JelmerT/cc2538-bsl -python cc2538-bsl/cc2538-bsl.py -b 460800 -e -w -v -p /dev/ttyUSB0 ot-rcp.bin -``` - -#### Run - -```sh -./build/posix/src/posix/ot-cli 'spinel+hdlc+uart:///dev/ttyUSB0?uart-baudrate=115200' -``` - ## Daemon Mode OpenThread Posix Daemon mode uses a unix socket as input and output, so that OpenThread core can run as a service. And a client can communicate with it by connecting to the socket. The protocol is OpenThread CLI. diff --git a/src/posix/cli.cmake b/src/posix/cli.cmake index e9b2e10338d..4b7347b4039 100644 --- a/src/posix/cli.cmake +++ b/src/posix/cli.cmake @@ -43,7 +43,7 @@ target_compile_options(ot-cli PRIVATE ${OT_CFLAGS} ) -target_link_libraries(ot-cli +target_link_libraries(ot-cli PRIVATE openthread-cli-ftd openthread-posix openthread-ftd @@ -53,9 +53,17 @@ target_link_libraries(ot-cli openthread-spinel-rcp ${OT_MBEDTLS} ${READLINE_LINK_LIBRARIES} + ot-config-ftd ot-config ) +if(OT_LINKER_MAP) + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") + target_link_libraries(ot-cli PRIVATE -Wl,-map,ot-cli.map) + else() + target_link_libraries(ot-cli PRIVATE -Wl,-Map=ot-cli.map) + endif() +endif() install(TARGETS ot-cli DESTINATION bin) diff --git a/src/posix/cli_readline.cpp b/src/posix/cli_readline.cpp index 7fd1b0b23d0..3a402cbef56 100644 --- a/src/posix/cli_readline.cpp +++ b/src/posix/cli_readline.cpp @@ -94,10 +94,7 @@ extern "C" void otAppCliInit(otInstance *aInstance) otCliInit(aInstance, OutputCallback, nullptr); } -extern "C" void otAppCliDeinit(void) -{ - rl_callback_handler_remove(); -} +extern "C" void otAppCliDeinit(void) { rl_callback_handler_remove(); } extern "C" void otAppCliUpdate(otSysMainloopContext *aMainloop) { diff --git a/src/posix/cli_stdio.cpp b/src/posix/cli_stdio.cpp index e6c62d7ab98..54a194c4b88 100644 --- a/src/posix/cli_stdio.cpp +++ b/src/posix/cli_stdio.cpp @@ -58,14 +58,9 @@ int OutputCallback(void *aContext, const char *aFormat, va_list aArguments) } } // namespace -extern "C" void otAppCliInit(otInstance *aInstance) -{ - otCliInit(aInstance, OutputCallback, nullptr); -} +extern "C" void otAppCliInit(otInstance *aInstance) { otCliInit(aInstance, OutputCallback, nullptr); } -extern "C" void otAppCliDeinit(void) -{ -} +extern "C" void otAppCliDeinit(void) {} extern "C" void otAppCliUpdate(otSysMainloopContext *aMainloop) { diff --git a/src/posix/client.cpp b/src/posix/client.cpp index e9128b67fc4..e57516e6617 100644 --- a/src/posix/client.cpp +++ b/src/posix/client.cpp @@ -391,7 +391,7 @@ int main(int argc, char *argv[]) lineBuffer[lineBufferWritePos++] = c; if (c == '\n' || lineBufferWritePos >= sizeof(lineBuffer) - 1) { - char * line = lineBuffer; + char *line = lineBuffer; size_t len = lineBufferWritePos; // read one line successfully or line buffer is full diff --git a/src/posix/daemon.cmake b/src/posix/daemon.cmake index 7116a4a2b5a..dd726df27b7 100644 --- a/src/posix/daemon.cmake +++ b/src/posix/daemon.cmake @@ -45,9 +45,18 @@ target_link_libraries(ot-daemon PRIVATE openthread-spinel-rcp ${OT_MBEDTLS} ot-posix-config + ot-config-ftd ot-config ) +if(OT_LINKER_MAP) + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") + target_link_libraries(ot-daemon PRIVATE -Wl,-map,ot-daemon.map) + else() + target_link_libraries(ot-daemon PRIVATE -Wl,-Map=ot-daemon.map) + endif() +endif() + add_executable(ot-ctl client.cpp ) @@ -67,6 +76,14 @@ target_link_libraries(ot-ctl PRIVATE ot-config ) +if(OT_LINKER_MAP) + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") + target_link_libraries(ot-ctl PRIVATE -Wl,-map,ot-ctl.map) + else() + target_link_libraries(ot-ctl PRIVATE -Wl,-Map=ot-ctl.map) + endif() +endif() + target_include_directories(ot-ctl PRIVATE ${COMMON_INCLUDES}) install(TARGETS ot-daemon diff --git a/src/posix/main.c b/src/posix/main.c index df014ea5fd0..8800ac0c1b5 100644 --- a/src/posix/main.c +++ b/src/posix/main.c @@ -141,6 +141,7 @@ enum OT_POSIX_OPT_DRY_RUN = 'n', OT_POSIX_OPT_HELP = 'h', OT_POSIX_OPT_INTERFACE_NAME = 'I', + OT_POSIX_OPT_PERSISTENT_INTERFACE = 'p', OT_POSIX_OPT_TIME_SPEED = 's', OT_POSIX_OPT_VERBOSE = 'v', @@ -156,6 +157,7 @@ static const struct option kOptions[] = { {"dry-run", no_argument, NULL, OT_POSIX_OPT_DRY_RUN}, {"help", no_argument, NULL, OT_POSIX_OPT_HELP}, {"interface-name", required_argument, NULL, OT_POSIX_OPT_INTERFACE_NAME}, + {"persistent-interface", no_argument, NULL, OT_POSIX_OPT_PERSISTENT_INTERFACE}, {"radio-version", no_argument, NULL, OT_POSIX_OPT_RADIO_VERSION}, {"real-time-signal", required_argument, NULL, OT_POSIX_OPT_REAL_TIME_SIGNAL}, {"time-speed", required_argument, NULL, OT_POSIX_OPT_TIME_SPEED}, @@ -174,6 +176,7 @@ static void PrintUsage(const char *aProgramName, FILE *aStream, int aExitCode) " -I --interface-name name Thread network interface name.\n" " -n --dry-run Just verify if arguments is valid and radio spinel is compatible.\n" " --radio-version Print radio firmware version.\n" + " -p --persistent-interface Persistent the created thread network interface\n" " -s --time-speed factor Time speed up factor.\n" " -v --verbose Also log to stderr.\n", aProgramName); @@ -191,8 +194,9 @@ static void ParseArg(int aArgCount, char *aArgVector[], PosixConfig *aConfig) { memset(aConfig, 0, sizeof(*aConfig)); - aConfig->mPlatformConfig.mSpeedUpFactor = 1; - aConfig->mLogLevel = OT_LOG_LEVEL_CRIT; + aConfig->mPlatformConfig.mPersistentInterface = false; + aConfig->mPlatformConfig.mSpeedUpFactor = 1; + aConfig->mLogLevel = OT_LOG_LEVEL_CRIT; #ifdef __linux__ aConfig->mPlatformConfig.mRealTimeSignal = SIGRTMIN; #endif @@ -202,7 +206,7 @@ static void ParseArg(int aArgCount, char *aArgVector[], PosixConfig *aConfig) while (true) { int index = 0; - int option = getopt_long(aArgCount, aArgVector, "B:d:hI:ns:v", kOptions, &index); + int option = getopt_long(aArgCount, aArgVector, "B:d:hI:nps:v", kOptions, &index); if (option == -1) { @@ -220,6 +224,9 @@ static void ParseArg(int aArgCount, char *aArgVector[], PosixConfig *aConfig) case OT_POSIX_OPT_INTERFACE_NAME: aConfig->mPlatformConfig.mInterfaceName = optarg; break; + case OT_POSIX_OPT_PERSISTENT_INTERFACE: + aConfig->mPlatformConfig.mPersistentInterface = true; + break; case OT_POSIX_OPT_BACKBONE_INTERFACE_NAME: aConfig->mPlatformConfig.mBackboneInterfaceName = optarg; break; @@ -308,10 +315,7 @@ static otInstance *InitInstance(PosixConfig *aConfig) return instance; } -void otTaskletsSignalPending(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} +void otTaskletsSignalPending(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); } void otPlatReset(otInstance *aInstance) { @@ -325,17 +329,19 @@ void otPlatReset(otInstance *aInstance) assert(false); } -static void ProcessNetif(void *aContext, uint8_t aArgsLength, char *aArgs[]) +static otError ProcessNetif(void *aContext, uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aContext); OT_UNUSED_VARIABLE(aArgsLength); OT_UNUSED_VARIABLE(aArgs); otCliOutputFormat("%s:%u\r\n", otSysGetThreadNetifName(), otSysGetThreadNetifIndex()); + + return OT_ERROR_NONE; } #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE -static void ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[]) +static otError ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[]) { OT_UNUSED_VARIABLE(aContext); OT_UNUSED_VARIABLE(aArgsLength); @@ -374,7 +380,7 @@ int main(int argc, char *argv[]) #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE otAppCliInit(instance); #endif - otCliSetUserCommands(kCommands, OT_ARRAY_LENGTH(kCommands), instance); + IgnoreError(otCliSetUserCommands(kCommands, OT_ARRAY_LENGTH(kCommands), instance)); while (true) { diff --git a/src/posix/platform/CMakeLists.txt b/src/posix/platform/CMakeLists.txt index 701eb21839b..02a30dc0821 100644 --- a/src/posix/platform/CMakeLists.txt +++ b/src/posix/platform/CMakeLists.txt @@ -35,6 +35,14 @@ if(OT_DAEMON) target_compile_definitions(ot-posix-config INTERFACE "OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE=1" ) + + # We have to add this definition to `ot-config` because openthread core + # libraries will set config file to "openthrad-core-posix-config.h" which + # depends on `OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE` to correctly enable + # PLATFORM_NETIF and PLATFORM_UDP features. Otherwise, openthread core and + # posix libraries will use different feature definitions. + list(APPEND OT_PLATFORM_DEFINES "OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE=1") + set(OT_PLATFORM_DEFINES ${OT_PLATFORM_DEFINES} PARENT_SCOPE) endif() option(OT_POSIX_INSTALL_EXTERNAL_ROUTES "Install External Routes as IPv6 routes" ON) @@ -76,16 +84,28 @@ if(OT_POSIX_CONFIG_RCP_BUS) ) endif() +set(OT_POSIX_NAT64_CIDR "192.168.255.0/24" CACHE STRING "NAT64 CIDR for OpenThread NAT64") +if(OT_POSIX_NAT64_CIDR) + target_compile_definitions(ot-posix-config + INTERFACE "OPENTHREAD_POSIX_CONFIG_NAT64_CIDR=\"${OT_POSIX_NAT64_CIDR}\"" + ) +endif() + if(NOT OT_CONFIG) set(OT_CONFIG "openthread-core-posix-config.h" PARENT_SCOPE) endif() -set(OT_POSIX_CONFIG_RCP_VENDOR_INTERFACE "vendor_interface_example.cpp" - CACHE STRING "vendor interface implementation") +set(CMAKE_EXE_LINKER_FLAGS "-rdynamic ${CMAKE_EXE_LINKER_FLAGS}" PARENT_SCOPE) + +if(OT_ANDROID_NDK) + target_compile_options(ot-posix-config INTERFACE -Wno-sign-compare) +endif() add_library(openthread-posix alarm.cpp backbone.cpp + backtrace.cpp + config_file.cpp daemon.cpp entropy.cpp firewall.cpp @@ -97,8 +117,11 @@ add_library(openthread-posix misc.cpp multicast_routing.cpp netif.cpp + power.cpp + power_updater.cpp radio.cpp radio_url.cpp + resolver.cpp settings.cpp spi_interface.cpp system.cpp @@ -106,20 +129,34 @@ add_library(openthread-posix udp.cpp utils.cpp virtual_time.cpp - ${OT_POSIX_CONFIG_RCP_VENDOR_INTERFACE} ) +include(vendor.cmake) + target_link_libraries(openthread-posix PUBLIC openthread-platform PRIVATE + openthread-cli-ftd + openthread-ftd + openthread-hdlc + openthread-spinel-rcp openthread-url + ot-config-ftd ot-config ot-posix-config - util + $<$>:util> $<$:rt> ) +option(OT_TARGET_OPENWRT "enable openthread posix for OpenWRT" OFF) +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT OT_TARGET_OPENWRT) + target_compile_definitions(ot-posix-config + INTERFACE "OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE=1" + ) + target_link_libraries(openthread-posix PRIVATE anl) +endif() + target_compile_definitions(openthread-posix PUBLIC ${OT_PUBLIC_DEFINES} @@ -140,3 +177,18 @@ target_include_directories(openthread-posix PRIVATE PUBLIC ${PROJECT_SOURCE_DIR}/src/posix/platform/include ) + +add_executable(ot-posix-test-settings + settings.cpp +) +target_compile_definitions(ot-posix-test-settings + PRIVATE -DSELF_TEST=1 -DOPENTHREAD_CONFIG_LOG_PLATFORM=0 +) +target_include_directories(ot-posix-test-settings + PRIVATE + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_SOURCE_DIR}/src/core + ${PROJECT_SOURCE_DIR}/src/posix/platform/include +) +add_test(NAME ot-posix-test-settings COMMAND ot-posix-test-settings) diff --git a/src/posix/platform/FindExampleVendorDeps.cmake b/src/posix/platform/FindExampleVendorDeps.cmake new file mode 100644 index 00000000000..fe45a136c1d --- /dev/null +++ b/src/posix/platform/FindExampleVendorDeps.cmake @@ -0,0 +1,127 @@ +# +# Copyright (c) 2023, 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. +# + +#[=======================================================================[.rst: +FindExampleRcpVendorDeps +------------------------ + +This file provides a reference for how to implement an RCP vendor +dependency CMake module to resolve external libraries and header +files used by a vendor implementation in the posix library. + +The name of this file and the name of the targets it defines are +conventionally related. For the purpose of this reference, targets +will be based off of the identifier "ExampleRcpVendorDeps". Derived +references should be based off of the value of the cache variable, +"OT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE". + +For more information about package resolution using CMake find modules, +reference the cmake-developer documentation. + +Imported Targets +^^^^^^^^^^^^^^^^ + +This module provides the following imported targets, if found: + +``ExampleRcpVendorDeps::ExampleRcpVendorDeps`` + RCP vendor interface library dependencies + +Result Variables +^^^^^^^^^^^^^^^^ + +This will define the following variables: + +``ExampleRcpVendorDeps_FOUND`` + True if the system has all of the required external dependencies +``ExampleRcpVendorDeps_INCLUDE_DIRS`` + Include directories needed by vendor interface +``ExampleRcpVendorDeps_LIBRARIES`` + Libraries needed by vendor interface + +Cache Variables +^^^^^^^^^^^^^^^ + +Vendors modules may configure various cache variables +while resolving dependencies: + +``Dependency0_INCLUDE_DIR`` + The directory containing include files for dependency 0 +``Dependency0_LIBRARY`` + The path to the library containing symbols for dependency 0 +``Dependency1_INCLUDE_DIR`` + The directory containing include files for dependency 1 +``Dependency1_LIBRARY`` + The path to the library containing symbols for dependency 1 + +#]=======================================================================] + +include(FindPackageHandleStandardArgs) + +find_path(Dependency0_INCLUDE_DIR + NAMES example0/example.h + PATH ${EXAMPLES_ROOT}/include +) + +find_library(Dependency0_LIBRARY + NAMES example0 + PATH ${EXAMPLES_ROOT}/lib +) + +find_path(Dependency1_INCLUDE_DIR + NAMES example1/example.h + PATH ${EXAMPLES_ROOT}/include +) + +find_library(Dependency1_LIBRARY + NAMES example1 + PATH ${EXAMPLES_ROOT}/lib +) + +find_package_handle_standard_args(ExampleRcpVendorDeps + FOUND_VAR ExampleRcpVendorDeps_FOUND + REQUIRED_VARS Dependency0_INCLUDE_DIR Dependency0_LIBRARY Dependency1_INCLUDE_DIR Dependency1_LIBRARY +) + +if(ExampleRcpVendorDeps_FOUND AND NOT ExampleRcpVendorDeps::ExampleRcpVendorDeps) + set(ExampleRcpVendorDeps_INCLUDE_DIRS ${Dependency0_INCLUDE_DIR} ${Dependency1_INCLUDE_DIR}) + set(ExampleRcpVendorDeps_LIBRARIES ${Dependency0_LIBRARY} ${Dependency1_LIBRARY}) + + add_library(ExampleRcpVendorDeps::ExampleRcpVendorDeps UNKNOWN IMPORTED) + set_target_properties(ExampleRcpVendorDeps::ExampleRcpVendorDeps PROPERTIES + IMPORTED_LOCATION "${ExampleRcpVendorDeps_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${ExampleRcpVendorDeps_INCLUDE_DIRS}" + ) + + mark_as_advanced( + Dependency0_INCLUDE_DIR + Dependency0_LIBRARY + Dependency1_INCLUDE_DIR + Dependency1_LIBRARY + ) +endif() + diff --git a/src/posix/platform/Makefile.am b/src/posix/platform/Makefile.am index ab7ae85f45c..ae51023060d 100644 --- a/src/posix/platform/Makefile.am +++ b/src/posix/platform/Makefile.am @@ -46,6 +46,8 @@ libopenthread_posix_a_CPPFLAGS = \ libopenthread_posix_a_SOURCES = \ alarm.cpp \ backbone.cpp \ + backtrace.cpp \ + config_file.cpp \ daemon.cpp \ entropy.cpp \ firewall.cpp \ @@ -57,7 +59,10 @@ libopenthread_posix_a_SOURCES = \ misc.cpp \ multicast_routing.cpp \ netif.cpp \ + power.cpp \ + power_updater.cpp \ radio.cpp \ + resolver.cpp \ radio_url.cpp \ settings.cpp \ spi_interface.cpp \ @@ -93,23 +98,4 @@ if OPENTHREAD_BUILD_COVERAGE CLEANFILES = $(wildcard *.gcda *.gcno) endif # OPENTHREAD_BUILD_COVERAGE -check_PROGRAMS = test-settings - -test_settings_CPPFLAGS = \ - -I$(top_srcdir)/include \ - -I$(top_srcdir)/src \ - -I$(top_srcdir)/src/core \ - -I$(top_srcdir)/src/posix/platform/include \ - -DOPENTHREAD_CONFIG_LOG_PLATFORM=0 \ - -DSELF_TEST \ - $(NULL) - -test_settings_SOURCES = \ - settings.cpp \ - $(NULL) - -TESTS = \ - test-settings \ - $(NULL) - include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/posix/platform/alarm.cpp b/src/posix/platform/alarm.cpp index 776734ffbcf..a739a56b98f 100644 --- a/src/posix/platform/alarm.cpp +++ b/src/posix/platform/alarm.cpp @@ -87,10 +87,7 @@ uint64_t otPlatTimeGet(void) } #endif // !OPENTHREAD_POSIX_VIRTUAL_TIME -static uint64_t platformAlarmGetNow(void) -{ - return otPlatTimeGet() * sSpeedUpFactor; -} +static uint64_t platformAlarmGetNow(void) { return otPlatTimeGet() * sSpeedUpFactor; } void platformAlarmInit(uint32_t aSpeedUpFactor, int aRealTimeSignal) { @@ -131,10 +128,7 @@ void platformAlarmInit(uint32_t aSpeedUpFactor, int aRealTimeSignal) } } -uint32_t otPlatAlarmMilliGetNow(void) -{ - return (uint32_t)(platformAlarmGetNow() / US_PER_MS); -} +uint32_t otPlatAlarmMilliGetNow(void) { return (uint32_t)(platformAlarmGetNow() / US_PER_MS); } void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) { @@ -152,10 +146,7 @@ void otPlatAlarmMilliStop(otInstance *aInstance) } #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE -uint32_t otPlatAlarmMicroGetNow(void) -{ - return static_cast(platformAlarmGetNow()); -} +uint32_t otPlatAlarmMicroGetNow(void) { return static_cast(platformAlarmGetNow()); } void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) { diff --git a/src/posix/platform/backtrace.cpp b/src/posix/platform/backtrace.cpp new file mode 100644 index 00000000000..1f697ca57b5 --- /dev/null +++ b/src/posix/platform/backtrace.cpp @@ -0,0 +1,178 @@ +/* + * 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 backtrace for posix. + */ + +#include "openthread-posix-config.h" +#include "platform-posix.h" + +#include +#include +#include +#include +#include + +#include "common/code_utils.hpp" +#include "common/logging.hpp" + +#if OPENTHREAD_POSIX_CONFIG_BACKTRACE_ENABLE +#if OPENTHREAD_POSIX_CONFIG_ANDROID_ENABLE || defined(__GLIBC__) +#if OPENTHREAD_POSIX_CONFIG_ANDROID_ENABLE +#include +#include +#include + +class LogPrinter : public android::Printer +{ +public: + virtual void printLine(const char *string) { otLogCritPlat("%s", string); } +}; + +static void dumpStack(void) +{ + LogPrinter printer; + android::CallStack callstack; + + callstack.update(); + callstack.print(printer); +} +#else // OPENTHREAD_POSIX_CONFIG_ANDROID_ENABLE +#include +#include + +static void demangleSymbol(int aIndex, const char *aSymbol) +{ + constexpr uint16_t kMaxNameSize = 256; + int status; + char program[kMaxNameSize + 1]; + char mangledName[kMaxNameSize + 1]; + char *demangledName = nullptr; + char *functionName = nullptr; + unsigned int offset = 0; + unsigned int address = 0; + + // Try to demangle a C++ name + if (sscanf(aSymbol, "%256[^(]%*[^_]%256[^)+]%*[^0x]%x%*[^0x]%x", program, mangledName, &offset, &address) == 4) + { + demangledName = abi::__cxa_demangle(mangledName, nullptr, nullptr, &status); + functionName = demangledName; + } + + VerifyOrExit(demangledName == nullptr); + + // If failed to demangle a C++ name, try to get a regular C symbol + if (sscanf(aSymbol, "%256[^(](%256[^)+]%*[^0x]%x%*[^0x]%x", program, mangledName, &offset, &address) == 4) + { + functionName = mangledName; + } + +exit: + if (functionName != nullptr) + { + otLogCritPlat("#%2d: %s %s+0x%x [0x%x]\n", aIndex, program, functionName, offset, address); + } + else + { + otLogCritPlat("#%2d: %s\n", aIndex, aSymbol); + } + + if (demangledName != nullptr) + { + free(demangledName); + } +} + +static void dumpStack(void) +{ + constexpr uint8_t kMaxDepth = 50; + void *stack[kMaxDepth]; + char **symbols; + int depth; + + depth = backtrace(stack, kMaxDepth); + symbols = backtrace_symbols(stack, depth); + VerifyOrExit(symbols != nullptr); + + for (int i = 0; i < depth; i++) + { + demangleSymbol(i, symbols[i]); + } + + free(symbols); + +exit: + return; +} +#endif // OPENTHREAD_POSIX_CONFIG_ANDROID_ENABLE +static constexpr uint8_t kNumSignals = 6; +static constexpr int kSignals[kNumSignals] = {SIGABRT, SIGILL, SIGSEGV, SIGBUS, SIGTRAP, SIGFPE}; +static struct sigaction sSigActions[kNumSignals]; + +static void resetSignalActions(void) +{ + for (uint8_t i = 0; i < kNumSignals; i++) + { + sigaction(kSignals[i], &sSigActions[i], (struct sigaction *)nullptr); + } +} + +static void signalCritical(int sig, siginfo_t *info, void *ucontext) +{ + OT_UNUSED_VARIABLE(ucontext); + OT_UNUSED_VARIABLE(info); + + otLogCritPlat("------------------ BEGINNING OF CRASH -------------"); + otLogCritPlat("*** FATAL ERROR: Caught signal: %d (%s)", sig, strsignal(sig)); + + dumpStack(); + + otLogCritPlat("------------------ END OF CRASH ------------------"); + + resetSignalActions(); + raise(sig); +} + +void platformBacktraceInit(void) +{ + struct sigaction sigact; + + sigact.sa_sigaction = &signalCritical; + sigact.sa_flags = SA_RESTART | SA_SIGINFO | SA_NOCLDWAIT; + + for (uint8_t i = 0; i < kNumSignals; i++) + { + sigaction(kSignals[i], &sigact, &sSigActions[i]); + } +} +#else // OPENTHREAD_POSIX_CONFIG_ANDROID_ENABLE || defined(__GLIBC__) +void platformBacktraceInit(void) {} +#endif // OPENTHREAD_POSIX_CONFIG_ANDROID_ENABLE || defined(__GLIBC__) +#endif // OPENTHREAD_POSIX_CONFIG_BACKTRACE_ENABLE diff --git a/src/posix/platform/config_file.cpp b/src/posix/platform/config_file.cpp new file mode 100644 index 00000000000..dd78c9716d9 --- /dev/null +++ b/src/posix/platform/config_file.cpp @@ -0,0 +1,215 @@ +/* + * 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. + */ + +#include "config_file.hpp" + +#include +#include +#include +#include +#include +#include + +#include "utils.hpp" +#include +#include "common/code_utils.hpp" +#include "lib/platform/exit_code.h" + +namespace ot { +namespace Posix { + +ConfigFile::ConfigFile(const char *aFilePath) + : mFilePath(aFilePath) +{ + assert(mFilePath != nullptr); + VerifyOrDie(strlen(mFilePath) + strlen(kSwapSuffix) < kFileNameMaxSize, OT_EXIT_FAILURE); +} + +otError ConfigFile::Get(const char *aKey, int &aIterator, char *aValue, int aValueLength) +{ + otError error = OT_ERROR_NONE; + char line[kLineMaxSize + 1]; + FILE *fp = nullptr; + char *ret; + long int pos; + + VerifyOrExit((aKey != nullptr) && (aValue != nullptr), error = OT_ERROR_INVALID_ARGS); + VerifyOrExit((fp = fopen(mFilePath, "r")) != nullptr, error = OT_ERROR_NOT_FOUND); + VerifyOrDie(fseek(fp, aIterator, SEEK_SET) == 0, OT_EXIT_ERROR_ERRNO); + + while ((ret = fgets(line, sizeof(line), fp)) != nullptr) + { + char *str; + char *key; + char *value; + + // If the string exceeds the `sizeof(line) - 1`, the string will be truncated to `sizeof(line) - 1` bytes string + // by the function `fgets()`. + if (strlen(line) + 1 == sizeof(line)) + { + // The line is too long. + continue; + } + + // Remove comments + strtok(line, kCommentDelimiter); + + if ((str = strstr(line, "=")) == nullptr) + { + continue; + } + + *str = '\0'; + key = line; + + Strip(key); + + if (strcmp(aKey, key) == 0) + { + value = str + 1; + Strip(value); + aValueLength = OT_MIN(static_cast(strlen(value)), (aValueLength - 1)); + memcpy(aValue, value, static_cast(aValueLength)); + aValue[aValueLength] = '\0'; + break; + } + } + + VerifyOrExit(ret != nullptr, error = OT_ERROR_NOT_FOUND); + VerifyOrDie((pos = ftell(fp)) >= 0, OT_EXIT_ERROR_ERRNO); + aIterator = static_cast(pos); + +exit: + if (fp != nullptr) + { + fclose(fp); + } + + return error; +} + +otError ConfigFile::Add(const char *aKey, const char *aValue) +{ + otError error = OT_ERROR_NONE; + FILE *fp = nullptr; + char *path = nullptr; + char *dir; + struct stat st; + + VerifyOrExit((aKey != nullptr) && (aValue != nullptr), error = OT_ERROR_INVALID_ARGS); + VerifyOrDie((path = strdup(mFilePath)) != nullptr, OT_EXIT_ERROR_ERRNO); + dir = dirname(path); + + if (stat(dir, &st) == -1) + { + VerifyOrDie(mkdir(dir, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == 0, OT_EXIT_ERROR_ERRNO); + } + + VerifyOrDie((fp = fopen(mFilePath, "at")) != NULL, OT_EXIT_ERROR_ERRNO); + VerifyOrDie(fprintf(fp, "%s=%s\n", aKey, aValue) > 0, OT_EXIT_ERROR_ERRNO); + +exit: + if (fp != nullptr) + { + fclose(fp); + } + + if (path != nullptr) + { + free(path); + } + + return error; +} + +otError ConfigFile::Clear(const char *aKey) +{ + otError error = OT_ERROR_NONE; + char swapFile[kFileNameMaxSize]; + char line[kLineMaxSize]; + FILE *fp = nullptr; + FILE *fpSwap = nullptr; + + VerifyOrExit(aKey != nullptr, error = OT_ERROR_INVALID_ARGS); + VerifyOrDie((fp = fopen(mFilePath, "r")) != NULL, OT_EXIT_ERROR_ERRNO); + snprintf(swapFile, sizeof(swapFile), "%s%s", mFilePath, kSwapSuffix); + VerifyOrDie((fpSwap = fopen(swapFile, "w+")) != NULL, OT_EXIT_ERROR_ERRNO); + + while (fgets(line, sizeof(line), fp) != nullptr) + { + bool containsKey; + char *str1; + char *str2; + + str1 = strstr(line, kCommentDelimiter); + str2 = strstr(line, aKey); + + // If only the comment contains the key string, ignore it. + containsKey = (str2 != nullptr) && (str1 == nullptr || str2 < str1); + + if (!containsKey) + { + fputs(line, fpSwap); + } + } + +exit: + if (fp != nullptr) + { + fclose(fp); + } + + if (fpSwap != nullptr) + { + fclose(fpSwap); + } + + if (error == OT_ERROR_NONE) + { + VerifyOrDie(rename(swapFile, mFilePath) == 0, OT_EXIT_ERROR_ERRNO); + } + + return error; +} + +void ConfigFile::Strip(char *aString) +{ + int count = 0; + + for (int i = 0; aString[i]; i++) + { + if (aString[i] != ' ' && aString[i] != '\r' && aString[i] != '\n') + { + aString[count++] = aString[i]; + } + } + + aString[count] = '\0'; +} +} // namespace Posix +} // namespace ot diff --git a/src/posix/platform/config_file.hpp b/src/posix/platform/config_file.hpp new file mode 100644 index 00000000000..c0a74ca448a --- /dev/null +++ b/src/posix/platform/config_file.hpp @@ -0,0 +1,107 @@ +/* + * 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. + */ + +#ifndef POSIX_PLATFORM_CONFIG_FILE_HPP_ +#define POSIX_PLATFORM_CONFIG_FILE_HPP_ + +#include +#include +#include + +namespace ot { +namespace Posix { + +/** + * This class provides read/write/clear methods for key/value configuration files. + * + */ +class ConfigFile +{ +public: + /** + * This method initializes the configuration file path. + * + * @param[in] aFilePath A pointer to the null-terminated file path. + * + */ + explicit ConfigFile(const char *aFilePath); + + /** + * This method gets a configuration from the configuration file. + * + * @param[in] aKey The key string associated with the requested configuration. + * @param[in,out] aIterator A reference to an iterator. MUST be initialized to 0 or the behavior is undefined. + * @param[out] aValue A pointer to where the new value string of the configuration should be read from. + * The @p aValue string will be terminated with `\0` if this method returns success. + * @param[in] aValueLength The max length of the data pointed to by @p aValue. + * + * @retval OT_ERROR_NONE The given configuration was found and fetched successfully. + * @retval OT_ERROR_NOT_FOUND The given key or iterator was not found in the configuration file. + * @retval OT_ERROR_INVALID_ARGS If @p aKey or @p aValue was NULL. + * + */ + otError Get(const char *aKey, int &aIterator, char *aValue, int aValueLength); + + /** + * This method adds a configuration to the configuration file. + * + * @param[in] aKey The key string associated with the requested configuration. + * @param[in] aValue A pointer to where the new value string of the configuration should be written. + * + * @retval OT_ERROR_NONE The given key was found and removed successfully. + * @retval OT_ERROR_INVALID_ARGS If @p aKey or @p aValue was NULL. + * + */ + otError Add(const char *aKey, const char *aValue); + + /** + * This function removes all configurations with the same key string from the configuration file. + * + * @param[in] aKey The key string associated with the requested configuration. + * + * @retval OT_ERROR_NONE The given key was found and removed successfully. + * @retval OT_ERROR_INVALID_ARGS If @p aKey was NULL. + * + */ + otError Clear(const char *aKey); + +private: + const char *kCommentDelimiter = "#"; + const char *kSwapSuffix = ".swap"; + static constexpr uint16_t kLineMaxSize = 512; + static constexpr uint16_t kFileNameMaxSize = 255; + + void Strip(char *aString); + + const char *mFilePath; +}; + +} // namespace Posix +} // namespace ot + +#endif // POSIX_PLATFORM_CONFIG_FILE_HPP_ diff --git a/src/posix/platform/daemon.cpp b/src/posix/platform/daemon.cpp index 275b5a880d2..731b4f2b780 100644 --- a/src/posix/platform/daemon.cpp +++ b/src/posix/platform/daemon.cpp @@ -145,7 +145,7 @@ void Daemon::InitializeSessionSocket(void) } else { - otLogInfoPlat("Session socket is ready", strerror(errno)); + otLogInfoPlat("Session socket is ready"); } } @@ -341,7 +341,6 @@ void Daemon::Process(const otSysMainloopContext &aContext) if (rval > 0) { buffer[rval] = '\0'; - otLogInfoPlat("> %s", reinterpret_cast(buffer)); otCliInputLine(reinterpret_cast(buffer)); } else diff --git a/src/posix/platform/entropy.cpp b/src/posix/platform/entropy.cpp index 561f27971e5..79f49388674 100644 --- a/src/posix/platform/entropy.cpp +++ b/src/posix/platform/entropy.cpp @@ -95,7 +95,7 @@ otError otPlatEntropyGet(uint8_t *aOutput, uint16_t aOutputLength) #if __SANITIZE_ADDRESS__ == 0 - FILE * file = nullptr; + FILE *file = nullptr; size_t readLength; VerifyOrExit(aOutput && aOutputLength, error = OT_ERROR_INVALID_ARGS); diff --git a/src/posix/platform/hdlc_interface.cpp b/src/posix/platform/hdlc_interface.cpp index ffbc206506a..a37427e5785 100644 --- a/src/posix/platform/hdlc_interface.cpp +++ b/src/posix/platform/hdlc_interface.cpp @@ -60,6 +60,7 @@ #include #include "common/code_utils.hpp" +#include "lib/spinel/spinel.h" #ifdef __APPLE__ @@ -125,8 +126,8 @@ namespace ot { namespace Posix { HdlcInterface::HdlcInterface(SpinelInterface::ReceiveFrameCallback aCallback, - void * aCallbackContext, - SpinelInterface::RxFrameBuffer & aFrameBuffer) + void *aCallbackContext, + SpinelInterface::RxFrameBuffer &aFrameBuffer) : mReceiveFrameCallback(aCallback) , mReceiveFrameContext(aCallbackContext) , mReceiveFrameBuffer(aFrameBuffer) @@ -139,11 +140,6 @@ HdlcInterface::HdlcInterface(SpinelInterface::ReceiveFrameCallback aCallback, mInterfaceMetrics.mRcpInterfaceType = OT_POSIX_RCP_BUS_UART; } -void HdlcInterface::OnRcpReset(void) -{ - mHdlcDecoder.Reset(); -} - otError HdlcInterface::Init(const Url::Url &aRadioUrl) { otError error = OT_ERROR_NONE; @@ -177,15 +173,9 @@ otError HdlcInterface::Init(const Url::Url &aRadioUrl) return error; } -HdlcInterface::~HdlcInterface(void) -{ - Deinit(); -} +HdlcInterface::~HdlcInterface(void) { Deinit(); } -void HdlcInterface::Deinit(void) -{ - CloseFile(); -} +void HdlcInterface::Deinit(void) { CloseFile(); } void HdlcInterface::Read(void) { @@ -204,10 +194,7 @@ void HdlcInterface::Read(void) } } -void HdlcInterface::Decode(const uint8_t *aBuffer, uint16_t aLength) -{ - mHdlcDecoder.Decode(aBuffer, aLength); -} +void HdlcInterface::Decode(const uint8_t *aBuffer, uint16_t aLength) { mHdlcDecoder.Decode(aBuffer, aLength); } otError HdlcInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength) { @@ -222,6 +209,12 @@ otError HdlcInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength) error = Write(encoderBuffer.GetFrame(), encoderBuffer.GetLength()); exit: + if ((error == OT_ERROR_NONE) && ot::Spinel::SpinelInterface::IsSpinelResetCommand(aFrame, aLength)) + { + mHdlcDecoder.Reset(); + error = ResetConnection(); + } + return error; } @@ -437,7 +430,7 @@ int HdlcInterface::OpenFile(const Url::Url &aRadioUrl) if (isatty(fd)) { struct termios tios; - const char * value; + const char *value; speed_t speed; int stopBit = 1; @@ -630,7 +623,7 @@ int HdlcInterface::ForkPty(const Url::Url &aRadioUrl) if (0 == pid) { constexpr int kMaxArguments = 32; - char * argv[kMaxArguments + 1]; + char *argv[kMaxArguments + 1]; size_t index = 0; argv[index++] = const_cast(aRadioUrl.GetPath()); diff --git a/src/posix/platform/hdlc_interface.hpp b/src/posix/platform/hdlc_interface.hpp index 4fa2ba00f52..dc9fbf89a14 100644 --- a/src/posix/platform/hdlc_interface.hpp +++ b/src/posix/platform/hdlc_interface.hpp @@ -61,8 +61,8 @@ class HdlcInterface * */ HdlcInterface(Spinel::SpinelInterface::ReceiveFrameCallback aCallback, - void * aCallbackContext, - Spinel::SpinelInterface::RxFrameBuffer & aFrameBuffer); + void *aCallbackContext, + Spinel::SpinelInterface::RxFrameBuffer &aFrameBuffer); /** * This destructor deinitializes the object. @@ -158,16 +158,13 @@ class HdlcInterface uint32_t GetBusSpeed(void) const { return mBaudRate; } /** - * This method is called when RCP failure detected and resets internal states of the interface. + * This method hardware resets the RCP. * - */ - void OnRcpReset(void); - - /** - * This method is called when RCP is reset to recreate the connection with it. + * @retval OT_ERROR_NONE Successfully reset the RCP. + * @retval OT_ERROR_NOT_IMPLEMENT The hardware reset is not implemented. * */ - otError ResetConnection(void); + otError HardwareReset(void) { return OT_ERROR_NOT_IMPLEMENTED; } /** * This method returns the RCP interface metrics. @@ -178,6 +175,12 @@ class HdlcInterface const otRcpInterfaceMetrics *GetRcpInterfaceMetrics(void) const { return &mInterfaceMetrics; } private: + /** + * This method is called when RCP is reset to recreate the connection with it. + * + */ + otError ResetConnection(void); + /** * This method instructs `HdlcInterface` to read and decode data from radio over the socket. * @@ -258,8 +261,8 @@ class HdlcInterface }; Spinel::SpinelInterface::ReceiveFrameCallback mReceiveFrameCallback; - void * mReceiveFrameContext; - Spinel::SpinelInterface::RxFrameBuffer & mReceiveFrameBuffer; + void *mReceiveFrameContext; + Spinel::SpinelInterface::RxFrameBuffer &mReceiveFrameBuffer; int mSockFd; uint32_t mBaudRate; diff --git a/src/posix/platform/include/openthread/openthread-system.h b/src/posix/platform/include/openthread/openthread-system.h index 9a1a666bdad..35ae715011a 100644 --- a/src/posix/platform/include/openthread/openthread-system.h +++ b/src/posix/platform/include/openthread/openthread-system.h @@ -80,6 +80,7 @@ typedef struct otPlatformConfig uint8_t mRadioUrlNum; ///< Number of Radio URLs. int mRealTimeSignal; ///< The real-time signal for microsecond timer. uint32_t mSpeedUpFactor; ///< Speed up factor. + bool mPersistentInterface; ///< Whether persistent the interface bool mDryRun; ///< If 'DryRun' is set, the posix daemon will exit ///< directly after initialization. } otPlatformConfig; @@ -218,6 +219,29 @@ const otRadioSpinelMetrics *otSysGetRadioSpinelMetrics(void); */ const otRcpInterfaceMetrics *otSysGetRcpInterfaceMetrics(void); +/** + * This function returns the ifr_flags of the infrastructure network interface. + * + * @returns The ifr_flags of infrastructure network interface. + * + */ +uint32_t otSysGetInfraNetifFlags(void); + +typedef struct otSysInfraNetIfAddressCounters +{ + uint32_t mLinkLocalAddresses; + uint32_t mUniqueLocalAddresses; + uint32_t mGlobalUnicastAddresses; +} otSysInfraNetIfAddressCounters; + +/** + * This functions counts the number of addresses on the infrastructure network interface. + * + * @param[out] aAddressCounters The counters of addresses on infrastructure network interface. + * + */ +void otSysCountInfraNetifAddresses(otSysInfraNetIfAddressCounters *aAddressCounters); + #ifdef __cplusplus } // end of extern "C" #endif diff --git a/src/posix/platform/include/openthread/platform/secure_settings.h b/src/posix/platform/include/openthread/platform/secure_settings.h index d206c269a85..288ad4ff205 100644 --- a/src/posix/platform/include/openthread/platform/secure_settings.h +++ b/src/posix/platform/include/openthread/platform/secure_settings.h @@ -97,8 +97,8 @@ void otPosixSecureSettingsDeinit(otInstance *aInstance); otError otPosixSecureSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, - uint8_t * aValue, - uint16_t * aValueLength); + uint8_t *aValue, + uint16_t *aValueLength); /** * This function sets or replaces the value of a setting identified by aKey. If there was more than one value diff --git a/src/posix/platform/infra_if.cpp b/src/posix/platform/infra_if.cpp index 11f1562c638..2b37ceff31b 100644 --- a/src/posix/platform/infra_if.cpp +++ b/src/posix/platform/infra_if.cpp @@ -33,7 +33,7 @@ #include "platform-posix.h" -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE #ifdef __APPLE__ #define __APPLE_USE_RFC_3542 @@ -41,10 +41,12 @@ #include #include +#include // clang-format off #include #include // clang-format on +#include #include #include #include @@ -90,20 +92,32 @@ bool otPlatInfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddres otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, const otIp6Address *aDestAddress, - const uint8_t * aBuffer, + const uint8_t *aBuffer, uint16_t aBufferLength) { return ot::Posix::InfraNetif::Get().SendIcmp6Nd(aInfraIfIndex, *aDestAddress, aBuffer, aBufferLength); } -bool platformInfraIfIsRunning(void) +otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex) { - return ot::Posix::InfraNetif::Get().IsRunning(); + OT_UNUSED_VARIABLE(aInfraIfIndex); + +#if OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE + return ot::Posix::InfraNetif::Get().DiscoverNat64Prefix(aInfraIfIndex); +#else + return OT_ERROR_DROP; +#endif } -const char *otSysGetInfraNetifName(void) +bool platformInfraIfIsRunning(void) { return ot::Posix::InfraNetif::Get().IsRunning(); } + +const char *otSysGetInfraNetifName(void) { return ot::Posix::InfraNetif::Get().GetNetifName(); } + +uint32_t otSysGetInfraNetifFlags(void) { return ot::Posix::InfraNetif::Get().GetFlags(); } + +void otSysCountInfraNetifAddresses(otSysInfraNetIfAddressCounters *aAddressCounters) { - return ot::Posix::InfraNetif::Get().GetNetifName(); + ot::Posix::InfraNetif::Get().CountAddresses(*aAddressCounters); } namespace ot { @@ -123,10 +137,11 @@ int CreateIcmp6Socket(void) sock = SocketWithCloseExec(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, kSocketBlock); VerifyOrDie(sock != -1, OT_EXIT_ERROR_ERRNO); - // Only accept router advertisements and solicitations. + // Only accept Router Advertisements, Router Solicitations and Neighbor Advertisements. ICMP6_FILTER_SETBLOCKALL(&filter); ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter); ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter); + ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filter); rval = setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)); VerifyOrDie(rval == 0, OT_EXIT_ERROR_ERRNO); @@ -155,6 +170,15 @@ int CreateIcmp6Socket(void) return sock; } +bool IsAddressLinkLocal(const in6_addr &aAddress) +{ + return ((aAddress.s6_addr[0] & 0xff) == 0xfe) && ((aAddress.s6_addr[1] & 0xc0) == 0x80); +} + +bool IsAddressUniqueLocal(const in6_addr &aAddress) { return (aAddress.s6_addr[0] & 0xfe) == 0xfc; } + +bool IsAddressGlobalUnicast(const in6_addr &aAddress) { return (aAddress.s6_addr[0] & 0xe0) == 0x20; } + // Create a net-link socket that subscribes to link & addresses events. int CreateNetLinkSocket(void) { @@ -179,7 +203,7 @@ int CreateNetLinkSocket(void) otError InfraNetif::SendIcmp6Nd(uint32_t aInfraIfIndex, const otIp6Address &aDestAddress, - const uint8_t * aBuffer, + const uint8_t *aBuffer, uint16_t aBufferLength) { otError error = OT_ERROR_NONE; @@ -190,7 +214,7 @@ otError InfraNetif::SendIcmp6Nd(uint32_t aInfraIfIndex, int hopLimit = 255; uint8_t cmsgBuffer[CMSG_SPACE(sizeof(*packetInfo)) + CMSG_SPACE(sizeof(hopLimit))]; struct msghdr msgHeader; - struct cmsghdr * cmsgPointer; + struct cmsghdr *cmsgPointer; ssize_t rval; struct sockaddr_in6 dest; @@ -251,7 +275,9 @@ otError InfraNetif::SendIcmp6Nd(uint32_t aInfraIfIndex, return error; } -bool InfraNetif::IsRunning(void) const +bool InfraNetif::IsRunning(void) const { return (GetFlags() & IFF_RUNNING) && HasLinkLocalAddress(); } + +uint32_t InfraNetif::GetFlags(void) const { int sock; struct ifreq ifReq; @@ -269,7 +295,42 @@ bool InfraNetif::IsRunning(void) const close(sock); - return (ifReq.ifr_flags & IFF_RUNNING) && HasLinkLocalAddress(); + return static_cast(ifReq.ifr_flags); +} + +void InfraNetif::CountAddresses(otSysInfraNetIfAddressCounters &aAddressCounters) const +{ + struct ifaddrs *ifAddrs = nullptr; + + aAddressCounters.mLinkLocalAddresses = 0; + aAddressCounters.mUniqueLocalAddresses = 0; + aAddressCounters.mGlobalUnicastAddresses = 0; + + if (getifaddrs(&ifAddrs) < 0) + { + otLogWarnPlat("failed to get netif addresses: %s", strerror(errno)); + ExitNow(); + } + + for (struct ifaddrs *addr = ifAddrs; addr != nullptr; addr = addr->ifa_next) + { + in6_addr *in6Addr; + + if (strncmp(addr->ifa_name, mInfraIfName, sizeof(mInfraIfName)) != 0 || addr->ifa_addr->sa_family != AF_INET6) + { + continue; + } + + in6Addr = &(reinterpret_cast(addr->ifa_addr)->sin6_addr); + aAddressCounters.mLinkLocalAddresses += IsAddressLinkLocal(*in6Addr); + aAddressCounters.mUniqueLocalAddresses += IsAddressUniqueLocal(*in6Addr); + aAddressCounters.mGlobalUnicastAddresses += IsAddressGlobalUnicast(*in6Addr); + } + + freeifaddrs(ifAddrs); + +exit: + return; } bool InfraNetif::HasLinkLocalAddress(void) const @@ -515,6 +576,135 @@ void InfraNetif::ReceiveIcmp6Message(void) } } +#if OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE +const char InfraNetif::kWellKnownIpv4OnlyName[] = "ipv4only.arpa"; +const otIp4Address InfraNetif::kWellKnownIpv4OnlyAddress1 = {{{192, 0, 0, 170}}}; +const otIp4Address InfraNetif::kWellKnownIpv4OnlyAddress2 = {{{192, 0, 0, 171}}}; +const uint8_t InfraNetif::kValidNat64PrefixLength[] = {96, 64, 56, 48, 40, 32}; + +void InfraNetif::DiscoverNat64PrefixDone(union sigval sv) +{ + struct gaicb *req = (struct gaicb *)sv.sival_ptr; + struct addrinfo *res = (struct addrinfo *)req->ar_result; + + otIp6Prefix prefix = {}; + + VerifyOrExit((char *)req->ar_name == kWellKnownIpv4OnlyName); + + otLogInfoPlat("Handling host address response for %s", kWellKnownIpv4OnlyName); + + // We extract the first valid NAT64 prefix from the address look-up response. + for (struct addrinfo *rp = res; rp != NULL && prefix.mLength == 0; rp = rp->ai_next) + { + struct sockaddr_in6 *ip6Addr; + otIp6Address ip6Address; + + if (rp->ai_family != AF_INET6) + { + continue; + } + + ip6Addr = reinterpret_cast(rp->ai_addr); + memcpy(&ip6Address.mFields.m8, &ip6Addr->sin6_addr.s6_addr, OT_IP6_ADDRESS_SIZE); + for (uint8_t length : kValidNat64PrefixLength) + { + otIp4Address ip4Address; + + otIp4ExtractFromIp6Address(length, &ip6Address, &ip4Address); + if (otIp4IsAddressEqual(&ip4Address, &kWellKnownIpv4OnlyAddress1) || + otIp4IsAddressEqual(&ip4Address, &kWellKnownIpv4OnlyAddress2)) + { + // We check that the well-known IPv4 address is present only once in the IPv6 address. + // In case another instance of the value is found for another prefix length, we ignore this address + // and search for the other well-known IPv4 address (per RFC 7050 section 3). + bool foundDuplicate = false; + + for (uint8_t dupLength : kValidNat64PrefixLength) + { + otIp4Address dupIp4Address; + + if (dupLength == length) + { + continue; + } + + otIp4ExtractFromIp6Address(dupLength, &ip6Address, &dupIp4Address); + if (otIp4IsAddressEqual(&dupIp4Address, &ip4Address)) + { + foundDuplicate = true; + break; + } + } + + if (!foundDuplicate) + { + otIp6GetPrefix(&ip6Address, length, &prefix); + break; + } + } + + if (prefix.mLength != 0) + { + break; + } + } + } + + otPlatInfraIfDiscoverNat64PrefixDone(gInstance, Get().mInfraIfIndex, &prefix); + +exit: + freeaddrinfo(res); + freeaddrinfo((struct addrinfo *)req->ar_request); + free(req); +} + +otError InfraNetif::DiscoverNat64Prefix(uint32_t aInfraIfIndex) +{ + otError error = OT_ERROR_NONE; + struct addrinfo *hints = nullptr; + struct gaicb *reqs[1]; + struct sigevent sig; + int status; + + VerifyOrExit(aInfraIfIndex == mInfraIfIndex, error = OT_ERROR_DROP); + hints = (struct addrinfo *)malloc(sizeof(struct addrinfo)); + VerifyOrExit(hints != nullptr, error = OT_ERROR_NO_BUFS); + memset(hints, 0, sizeof(struct addrinfo)); + hints->ai_family = AF_INET6; + hints->ai_socktype = SOCK_STREAM; + + reqs[0] = (struct gaicb *)malloc(sizeof(struct gaicb)); + VerifyOrExit(reqs[0] != nullptr, error = OT_ERROR_NO_BUFS); + memset(reqs[0], 0, sizeof(struct gaicb)); + reqs[0]->ar_name = kWellKnownIpv4OnlyName; + reqs[0]->ar_request = hints; + + memset(&sig, 0, sizeof(struct sigevent)); + sig.sigev_notify = SIGEV_THREAD; + sig.sigev_value.sival_ptr = reqs[0]; + sig.sigev_notify_function = &InfraNetif::DiscoverNat64PrefixDone; + + status = getaddrinfo_a(GAI_NOWAIT, reqs, 1, &sig); + + if (status != 0) + { + otLogNotePlat("getaddrinfo_a failed: %s", gai_strerror(status)); + ExitNow(error = OT_ERROR_FAILED); + } + otLogInfoPlat("getaddrinfo_a requested for %s", kWellKnownIpv4OnlyName); +exit: + if (error != OT_ERROR_NONE) + { + if (hints) + { + freeaddrinfo(hints); + } + free(reqs[0]); + } + return error; +} +#endif // OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE + void InfraNetif::Process(const otSysMainloopContext &aContext) { VerifyOrExit(mInfraIfIcmp6Socket != -1); @@ -543,4 +733,4 @@ InfraNetif &InfraNetif::Get(void) } // namespace Posix } // namespace ot -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#endif // OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE diff --git a/src/posix/platform/infra_if.hpp b/src/posix/platform/infra_if.hpp index ccd2b1206e7..03146fa2747 100644 --- a/src/posix/platform/infra_if.hpp +++ b/src/posix/platform/infra_if.hpp @@ -34,11 +34,13 @@ #include "openthread-posix-config.h" #include +#include +#include #include "core/common/non_copyable.hpp" #include "posix/platform/mainloop.hpp" -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE namespace ot { namespace Posix { @@ -106,6 +108,22 @@ class InfraNetif : public Mainloop::Source, private NonCopyable */ bool IsRunning(void) const; + /** + * This method returns the ifr_flags of the infrastructure network interface. + * + * @returns The ifr_flags of the infrastructure network interface. + * + */ + uint32_t GetFlags(void) const; + + /** + * This functions counts the number of addresses on the infrastructure network interface. + * + * @param[out] aAddressCounters The counters of addresses on infrastructure network interface. + * + */ + void CountAddresses(otSysInfraNetIfAddressCounters &aAddressCounters) const; + /** * This method sends an ICMPv6 Neighbor Discovery message on given infrastructure interface. * @@ -126,9 +144,21 @@ class InfraNetif : public Mainloop::Source, private NonCopyable */ otError SendIcmp6Nd(uint32_t aInfraIfIndex, const otIp6Address &aDestAddress, - const uint8_t * aBuffer, + const uint8_t *aBuffer, uint16_t aBufferLength); + /** + * This method sends an asynchronous address lookup for the well-known host name "ipv4only.arpa" + * to discover the NAT64 prefix. + * + * @param[in] aInfraIfIndex The index of the infrastructure interface the address look-up is sent to. + * + * @retval OT_ERROR_NONE Successfully request address look-up. + * @retval OT_ERROR_FAILED Failed to request address look-up. + * + */ + otError DiscoverNat64Prefix(uint32_t aInfraIfIndex); + /** * This method gets the infrastructure network interface name. * @@ -146,16 +176,22 @@ class InfraNetif : public Mainloop::Source, private NonCopyable static InfraNetif &Get(void); private: + static const char kWellKnownIpv4OnlyName[]; // "ipv4only.arpa" + static const otIp4Address kWellKnownIpv4OnlyAddress1; // 192.0.0.170 + static const otIp4Address kWellKnownIpv4OnlyAddress2; // 192.0.0.171 + static const uint8_t kValidNat64PrefixLength[]; + char mInfraIfName[IFNAMSIZ]; uint32_t mInfraIfIndex = 0; int mInfraIfIcmp6Socket = -1; int mNetLinkSocket = -1; - void ReceiveNetLinkMessage(void); - void ReceiveIcmp6Message(void); - bool HasLinkLocalAddress(void) const; + void ReceiveNetLinkMessage(void); + void ReceiveIcmp6Message(void); + bool HasLinkLocalAddress(void) const; + static void DiscoverNat64PrefixDone(union sigval sv); }; } // namespace Posix } // namespace ot -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#endif // OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE diff --git a/src/posix/platform/mainloop.hpp b/src/posix/platform/mainloop.hpp index 4f0fa7cc93f..fd2989b66e5 100644 --- a/src/posix/platform/mainloop.hpp +++ b/src/posix/platform/mainloop.hpp @@ -66,7 +66,7 @@ class Source virtual void Process(const otSysMainloopContext &aContext) = 0; /** - * This method marks desturctor virtual method. + * This method marks destructor virtual method. * */ virtual ~Source() = default; @@ -117,7 +117,7 @@ class Manager /** * This function returns the Mainloop singleton. * - * @returns A refernce to the Mainloop singleton. + * @returns A reference to the Mainloop singleton. * */ static Manager &Get(void); diff --git a/src/posix/platform/memory.cpp b/src/posix/platform/memory.cpp index 5132c3fe5dd..15d4a799cc0 100644 --- a/src/posix/platform/memory.cpp +++ b/src/posix/platform/memory.cpp @@ -34,13 +34,9 @@ #include #if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE -void *otPlatCAlloc(size_t aNum, size_t aSize) -{ - return calloc(aNum, aSize); -} +extern "C" { +void *otPlatCAlloc(size_t aNum, size_t aSize) { return calloc(aNum, aSize); } -void otPlatFree(void *aPtr) -{ - free(aPtr); -} +void otPlatFree(void *aPtr) { free(aPtr); } +} // extern "C" #endif diff --git a/src/posix/platform/misc.cpp b/src/posix/platform/misc.cpp index 77ef07a909a..7e2a82e4d26 100644 --- a/src/posix/platform/misc.cpp +++ b/src/posix/platform/misc.cpp @@ -84,7 +84,7 @@ void otPlatAssertFail(const char *aFilename, int aLineNumber) #else otLogCritPlat("assert failed at %s:%d", aFilename, aLineNumber); #endif - // For debug build, use assert to genreate a core dump + // For debug build, use assert to generate a core dump assert(false); exit(1); } diff --git a/src/posix/platform/multicast_routing.cpp b/src/posix/platform/multicast_routing.cpp index 6580782eaa7..9d71481e9f2 100644 --- a/src/posix/platform/multicast_routing.cpp +++ b/src/posix/platform/multicast_routing.cpp @@ -86,16 +86,16 @@ void MulticastRoutingManager::TearDown(void) Mainloop::Manager::Get().Remove(*this); } -void MulticastRoutingManager::HandleBackboneMulticastListenerEvent(void * aContext, +void MulticastRoutingManager::HandleBackboneMulticastListenerEvent(void *aContext, otBackboneRouterMulticastListenerEvent aEvent, - const otIp6Address * aAddress) + const otIp6Address *aAddress) { static_cast(aContext)->HandleBackboneMulticastListenerEvent( aEvent, static_cast(*aAddress)); } void MulticastRoutingManager::HandleBackboneMulticastListenerEvent(otBackboneRouterMulticastListenerEvent aEvent, - const Ip6::Address & aAddress) + const Ip6::Address &aAddress) { switch (aEvent) { @@ -309,6 +309,8 @@ otError MulticastRoutingManager::AddMulticastForwardingCache(const Ip6::Address } else { + VerifyOrExit(!aSrcAddr.IsLinkLocal(), error = OT_ERROR_NONE); + VerifyOrExit(aSrcAddr.GetPrefix() != AsCoreType(otThreadGetMeshLocalPrefix(gInstance)), error = OT_ERROR_NONE); // Forward multicast traffic from Thread to Backbone if multicast scope > kRealmLocalScope // TODO: (MLR) allow scope configuration of outbound multicast routing if (aGroupAddr.GetScope() > Ip6::Address::kRealmLocalScope) @@ -451,7 +453,7 @@ bool MulticastRoutingManager::UpdateMulticastRouteInfo(MulticastForwardingCache } else { - otLogWarnPlat("MulticastRoutingManager: %s: SIOCGETSGCNT_IN6 %s => %s failed: %s", __FUNCTION__, + otLogDebgPlat("MulticastRoutingManager: %s: SIOCGETSGCNT_IN6 %s => %s failed: %s", __FUNCTION__, aMfc.mSrcAddr.ToString().AsCString(), aMfc.mGroupAddr.ToString().AsCString(), strerror(errno)); } @@ -541,8 +543,8 @@ void MulticastRoutingManager::MulticastForwardingCache::SetValidPktCnt(unsigned mLastUseTime = otPlatTimeGet(); } -void MulticastRoutingManager::SaveMulticastForwardingCache(const Ip6::Address & aSrcAddr, - const Ip6::Address & aGroupAddr, +void MulticastRoutingManager::SaveMulticastForwardingCache(const Ip6::Address &aSrcAddr, + const Ip6::Address &aGroupAddr, MulticastRoutingManager::MifIndex aIif, MulticastRoutingManager::MifIndex aOif) { diff --git a/src/posix/platform/multicast_routing.hpp b/src/posix/platform/multicast_routing.hpp index 72177d80568..dc8b72a03a1 100644 --- a/src/posix/platform/multicast_routing.hpp +++ b/src/posix/platform/multicast_routing.hpp @@ -69,7 +69,7 @@ class MulticastRoutingManager : public Mainloop::Source, private NonCopyable { kMulticastForwardingCacheExpireTimeout = 300, //< Expire timeout of Multicast Forwarding Cache (in seconds) kMulticastForwardingCacheExpiringInterval = 60, //< Expire interval of Multicast Forwarding Cache (in seconds) - kMulitcastForwardingCacheTableSize = + kMulticastForwardingCacheTableSize = OPENTHREAD_POSIX_CONFIG_MAX_MULTICAST_FORWARDING_CACHE_TABLE, //< The max size of MFC table. }; @@ -126,13 +126,13 @@ class MulticastRoutingManager : public Mainloop::Source, private NonCopyable void RemoveMulticastForwardingCache(MulticastForwardingCache &aMfc) const; static const char *MifIndexToString(MifIndex aMif); void DumpMulticastForwardingCache(void) const; - static void HandleBackboneMulticastListenerEvent(void * aContext, + static void HandleBackboneMulticastListenerEvent(void *aContext, otBackboneRouterMulticastListenerEvent aEvent, - const otIp6Address * aAddress); + const otIp6Address *aAddress); void HandleBackboneMulticastListenerEvent(otBackboneRouterMulticastListenerEvent aEvent, - const Ip6::Address & aAddress); + const Ip6::Address &aAddress); - MulticastForwardingCache mMulticastForwardingCacheTable[kMulitcastForwardingCacheTableSize]; + MulticastForwardingCache mMulticastForwardingCacheTable[kMulticastForwardingCacheTableSize]; uint64_t mLastExpireTime; int mMulticastRouterSock; }; diff --git a/src/posix/platform/netif.cpp b/src/posix/platform/netif.cpp index 2d761ce6525..3b2e90830da 100644 --- a/src/posix/platform/netif.cpp +++ b/src/posix/platform/netif.cpp @@ -72,6 +72,7 @@ #include #include #ifdef __linux__ +#include #include #include #include @@ -143,6 +144,7 @@ extern int #include #include #include +#include #include #include @@ -150,18 +152,15 @@ extern int #include "common/debug.hpp" #include "net/ip6_address.hpp" +#include "resolver.hpp" + unsigned int gNetifIndex = 0; char gNetifName[IFNAMSIZ]; +otIp4Cidr gNat64Cidr; -const char *otSysGetThreadNetifName(void) -{ - return gNetifName; -} +const char *otSysGetThreadNetifName(void) { return gNetifName; } -unsigned int otSysGetThreadNetifIndex(void) -{ - return gNetifIndex; -} +unsigned int otSysGetThreadNetifIndex(void) { return gNetifIndex; } #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE #if OPENTHREAD_POSIX_CONFIG_FIREWALL_ENABLE @@ -208,6 +207,14 @@ static uint8_t sAddedExternalRoutesNum = 0; static otIp6Prefix sAddedExternalRoutes[kMaxExternalRoutesNum]; #endif +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE +static constexpr uint32_t kNat64RoutePriority = 100; ///< Priority for route to NAT64 CIDR, 100 means a high priority. +#endif + +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE +ot::Posix::Resolver gResolver; +#endif + #if defined(RTM_NEWMADDR) || defined(__NetBSD__) // on some BSDs (mac OS, FreeBSD), we get RTM_NEWMADDR/RTM_DELMADDR messages, so we don't need to monitor using MLD // on NetBSD, MLD monitoring simply doesn't work @@ -330,7 +337,11 @@ static uint8_t NetmaskToPrefixLength(const struct sockaddr_in6 *netmask) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-align" -void AddRtAttr(struct nlmsghdr *aHeader, uint32_t aMaxLen, uint8_t aType, const void *aData, uint8_t aLen) +static struct rtattr *AddRtAttr(struct nlmsghdr *aHeader, + uint32_t aMaxLen, + uint8_t aType, + const void *aData, + uint8_t aLen) { uint8_t len = RTA_LENGTH(aLen); struct rtattr *rta; @@ -346,6 +357,8 @@ void AddRtAttr(struct nlmsghdr *aHeader, uint32_t aMaxLen, uint8_t aType, const memcpy(RTA_DATA(rta), aData, aLen); } aHeader->nlmsg_len = NLMSG_ALIGN(aHeader->nlmsg_len) + RTA_ALIGN(len); + + return rta; } void AddRtAttrUint32(struct nlmsghdr *aHeader, uint32_t aMaxLen, uint8_t aType, uint32_t aData) @@ -527,12 +540,13 @@ static void UpdateMulticast(otInstance *aInstance, const otIp6Address &aAddress, SuccessOrDie(error); } -static void UpdateLink(otInstance *aInstance) +static void SetLinkState(otInstance *aInstance, bool aState) { + OT_UNUSED_VARIABLE(aInstance); + otError error = OT_ERROR_NONE; struct ifreq ifr; bool ifState = false; - bool otState = false; assert(gInstance == aInstance); @@ -542,14 +556,13 @@ static void UpdateLink(otInstance *aInstance) VerifyOrExit(ioctl(sIpFd, SIOCGIFFLAGS, &ifr) == 0, perror("ioctl"); error = OT_ERROR_FAILED); ifState = ((ifr.ifr_flags & IFF_UP) == IFF_UP) ? true : false; - otState = otIp6IsEnabled(aInstance); - otLogNotePlat("[netif] Changing interface state to %s%s.", otState ? "up" : "down", - (ifState == otState) ? " (already done, ignoring)" : ""); + otLogNotePlat("[netif] Changing interface state to %s%s.", aState ? "up" : "down", + (ifState == aState) ? " (already done, ignoring)" : ""); - if (ifState != otState) + if (ifState != aState) { - ifr.ifr_flags = otState ? (ifr.ifr_flags | IFF_UP) : (ifr.ifr_flags & ~IFF_UP); + ifr.ifr_flags = aState ? (ifr.ifr_flags | IFF_UP) : (ifr.ifr_flags & ~IFF_UP); VerifyOrExit(ioctl(sIpFd, SIOCSIFFLAGS, &ifr) == 0, perror("ioctl"); error = OT_ERROR_FAILED); #if defined(RTM_NEWLINK) && defined(RTM_DELLINK) // wait for RTM_NEWLINK event before processing notification from kernel to avoid infinite loop @@ -564,10 +577,14 @@ static void UpdateLink(otInstance *aInstance) } } -#if __linux__ && \ - (OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE || OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE) +static void UpdateLink(otInstance *aInstance) +{ + assert(gInstance == aInstance); + SetLinkState(aInstance, otIp6IsEnabled(aInstance)); +} -static otError AddRoute(const otIp6Prefix &aPrefix, uint32_t aPriority) +#if defined(__linux__) +template otError AddRoute(const uint8_t (&aAddress)[N], uint8_t aPrefixLen, uint32_t aPriority) { constexpr unsigned int kBufSize = 128; struct @@ -576,10 +593,10 @@ static otError AddRoute(const otIp6Prefix &aPrefix, uint32_t aPriority) struct rtmsg msg; char buf[kBufSize]; } req{}; - unsigned char data[sizeof(in6_addr)]; - char addrBuf[OT_IP6_ADDRESS_STRING_SIZE]; - unsigned int netifIdx = otSysGetThreadNetifIndex(); - otError error = OT_ERROR_NONE; + unsigned int netifIdx = otSysGetThreadNetifIndex(); + otError error = OT_ERROR_NONE; + + static_assert(N == sizeof(in6_addr) || N == sizeof(in_addr), "aAddress should be 4 octets or 16 octets"); VerifyOrExit(netifIdx > 0, error = OT_ERROR_INVALID_STATE); VerifyOrExit(sNetlinkFd >= 0, error = OT_ERROR_INVALID_STATE); @@ -591,9 +608,9 @@ static otError AddRoute(const otIp6Prefix &aPrefix, uint32_t aPriority) req.header.nlmsg_pid = 0; req.header.nlmsg_seq = ++sNetlinkSequence; - req.msg.rtm_family = AF_INET6; + req.msg.rtm_family = (N == sizeof(in6_addr) ? AF_INET6 : AF_INET); req.msg.rtm_src_len = 0; - req.msg.rtm_dst_len = aPrefix.mLength; + req.msg.rtm_dst_len = aPrefixLen; req.msg.rtm_tos = 0; req.msg.rtm_scope = RT_SCOPE_UNIVERSE; req.msg.rtm_type = RTN_UNICAST; @@ -601,9 +618,7 @@ static otError AddRoute(const otIp6Prefix &aPrefix, uint32_t aPriority) req.msg.rtm_protocol = RTPROT_BOOT; req.msg.rtm_flags = 0; - otIp6AddressToString(&aPrefix.mPrefix, addrBuf, OT_IP6_ADDRESS_STRING_SIZE); - inet_pton(AF_INET6, addrBuf, data); - AddRtAttr(reinterpret_cast(&req), sizeof(req), RTA_DST, data, sizeof(data)); + AddRtAttr(reinterpret_cast(&req), sizeof(req), RTA_DST, aAddress, sizeof(aAddress)); AddRtAttrUint32(&req.header, sizeof(req), RTA_PRIORITY, aPriority); AddRtAttrUint32(&req.header, sizeof(req), RTA_OIF, netifIdx); @@ -616,7 +631,7 @@ static otError AddRoute(const otIp6Prefix &aPrefix, uint32_t aPriority) return error; } -static otError DeleteRoute(const otIp6Prefix &aPrefix) +template otError DeleteRoute(const uint8_t (&aAddress)[N], uint8_t aPrefixLen) { constexpr unsigned int kBufSize = 512; struct @@ -625,10 +640,10 @@ static otError DeleteRoute(const otIp6Prefix &aPrefix) struct rtmsg msg; char buf[kBufSize]; } req{}; - unsigned char data[sizeof(in6_addr)]; - char addrBuf[OT_IP6_ADDRESS_STRING_SIZE]; - unsigned int netifIdx = otSysGetThreadNetifIndex(); - otError error = OT_ERROR_NONE; + unsigned int netifIdx = otSysGetThreadNetifIndex(); + otError error = OT_ERROR_NONE; + + static_assert(N == sizeof(in6_addr) || N == sizeof(in_addr), "aAddress should be 4 octets or 16 octets"); VerifyOrExit(netifIdx > 0, error = OT_ERROR_INVALID_STATE); VerifyOrExit(sNetlinkFd >= 0, error = OT_ERROR_INVALID_STATE); @@ -640,9 +655,9 @@ static otError DeleteRoute(const otIp6Prefix &aPrefix) req.header.nlmsg_pid = 0; req.header.nlmsg_seq = ++sNetlinkSequence; - req.msg.rtm_family = AF_INET6; + req.msg.rtm_family = (N == sizeof(in6_addr) ? AF_INET6 : AF_INET); req.msg.rtm_src_len = 0; - req.msg.rtm_dst_len = aPrefix.mLength; + req.msg.rtm_dst_len = aPrefixLen; req.msg.rtm_tos = 0; req.msg.rtm_scope = RT_SCOPE_UNIVERSE; req.msg.rtm_type = RTN_UNICAST; @@ -650,9 +665,7 @@ static otError DeleteRoute(const otIp6Prefix &aPrefix) req.msg.rtm_protocol = RTPROT_BOOT; req.msg.rtm_flags = 0; - otIp6AddressToString(&aPrefix.mPrefix, addrBuf, OT_IP6_ADDRESS_STRING_SIZE); - inet_pton(AF_INET6, addrBuf, data); - AddRtAttr(reinterpret_cast(&req), sizeof(req), RTA_DST, data, sizeof(data)); + AddRtAttr(reinterpret_cast(&req), sizeof(req), RTA_DST, &aAddress, sizeof(aAddress)); AddRtAttrUint32(&req.header, sizeof(req), RTA_OIF, netifIdx); if (send(sNetlinkFd, &req, sizeof(req), 0) < 0) @@ -665,11 +678,19 @@ static otError DeleteRoute(const otIp6Prefix &aPrefix) return error; } -#endif // __linux__ && (OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE || - // OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE) +#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE || OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE +static otError AddRoute(const otIp6Prefix &aPrefix, uint32_t aPriority) +{ + return AddRoute(aPrefix.mPrefix.mFields.m8, aPrefix.mLength, aPriority); +} -#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __linux__ +static otError DeleteRoute(const otIp6Prefix &aPrefix) +{ + return DeleteRoute(aPrefix.mPrefix.mFields.m8, aPrefix.mLength); +} +#endif // OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE || OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE +#if OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE static bool HasAddedOmrRoute(const otIp6Prefix &aOmrPrefix) { bool found = false; @@ -744,15 +765,13 @@ static void UpdateOmrRoutes(otInstance *aInstance) else { sAddedOmrRoutes[sAddedOmrRoutesNum++] = config.mPrefix; - otLogInfoPlat("[netif] Successfully added an OMR route %s in kernel: %s", prefixString); + otLogInfoPlat("[netif] Successfully added an OMR route %s in kernel", prefixString); } } } +#endif // OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE -#endif // OPENTHREAD_POSIX_CONFIG_INSTALL_OMR_ROUTES_ENABLE && __linux__ - -#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__ - +#if OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE static otError AddExternalRoute(const otIp6Prefix &aPrefix) { otError error; @@ -843,13 +862,26 @@ static void UpdateExternalRoutes(otInstance *aInstance) else { sAddedExternalRoutes[sAddedExternalRoutesNum++] = config.mPrefix; - otLogWarnPlat("[netif] Successfully added an external route %s in kernel: %s", prefixString); + otLogWarnPlat("[netif] Successfully added an external route %s in kernel", prefixString); } } exit: return; } -#endif // OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE && __linux__ +#endif // OPENTHREAD_POSIX_CONFIG_INSTALL_EXTERNAL_ROUTES_ENABLE + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE +static otError AddIp4Route(const otIp4Cidr &aIp4Cidr, uint32_t aPriority) +{ + return AddRoute(aIp4Cidr.mAddress.mFields.m8, aIp4Cidr.mLength, aPriority); +} + +static otError DeleteIp4Route(const otIp4Cidr &aIp4Cidr) +{ + return DeleteRoute(aIp4Cidr.mAddress.mFields.m8, aIp4Cidr.mLength); +} +#endif +#endif // defined(__linux__) static void processAddressChange(const otIp6AddressInfo *aAddressInfo, bool aIsAdded, void *aContext) { @@ -863,6 +895,28 @@ static void processAddressChange(const otIp6AddressInfo *aAddressInfo, bool aIsA } } +#if defined(__linux__) && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE +void processNat64StateChange(otNat64State aNewState) +{ + // If the interface is not up and we are enabling NAT64, the route will be added when we bring up the route. + // Also, the route will be deleted by the kernel when we shutting down the interface. + // We should try to add route first, since otNat64SetEnabled never fails. + if (otIp6IsEnabled(gInstance)) + { + if (aNewState == OT_NAT64_STATE_ACTIVE) + { + AddIp4Route(gNat64Cidr, kNat64RoutePriority); + otLogInfoPlat("[netif] Adding route for NAT64"); + } + else + { + DeleteIp4Route(gNat64Cidr); + otLogInfoPlat("[netif] Deleting route for NAT64"); + } + } +} +#endif // defined(__linux__) && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + void platformNetifStateChange(otInstance *aInstance, otChangedFlags aFlags) { if (OT_CHANGED_THREAD_NETIF_STATE & aFlags) @@ -881,6 +935,12 @@ void platformNetifStateChange(otInstance *aInstance, otChangedFlags aFlags) ot::Posix::UpdateIpSets(aInstance); #endif } +#if defined(__linux__) && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + if (OT_CHANGED_NAT64_TRANSLATOR_STATE & aFlags) + { + processNat64StateChange(otNat64GetTranslatorState(aInstance)); + } +#endif } static void processReceive(otMessage *aMessage, void *aContext) @@ -935,21 +995,15 @@ static void processTransmit(otInstance *aInstance) char packet[kMaxIp6Size]; otError error = OT_ERROR_NONE; size_t offset = 0; +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + bool isIp4 = false; +#endif assert(gInstance == aInstance); rval = read(sTunFd, packet, sizeof(packet)); VerifyOrExit(rval > 0, error = OT_ERROR_FAILED); - { - otMessageSettings settings; - - settings.mLinkSecurityEnabled = (otThreadGetDeviceRole(aInstance) != OT_DEVICE_ROLE_DISABLED); - settings.mPriority = OT_MESSAGE_PRIORITY_LOW; - message = otIp6NewMessage(aInstance, &settings); - VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS); - } - #if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) // BSD tunnel drivers have (for legacy reasons), may have a 4-byte header on them if ((rval >= 4) && (packet[0] == 0) && (packet[1] == 0)) @@ -959,6 +1013,20 @@ static void processTransmit(otInstance *aInstance) } #endif + { + otMessageSettings settings; + + settings.mLinkSecurityEnabled = (otThreadGetDeviceRole(aInstance) != OT_DEVICE_ROLE_DISABLED); + settings.mPriority = OT_MESSAGE_PRIORITY_LOW; +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + isIp4 = (packet[offset] & 0xf0) == 0x40; + message = isIp4 ? otIp4NewMessage(aInstance, &settings) : otIp6NewMessage(aInstance, &settings); +#else + message = otIp6NewMessage(aInstance, &settings); +#endif + VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS); + } + #if OPENTHREAD_POSIX_LOG_TUN_PACKETS otLogInfoPlat("[netif] Packet to NCP (%hu bytes)", static_cast(rval)); otDumpInfoPlat("", &packet[offset], static_cast(rval)); @@ -966,7 +1034,11 @@ static void processTransmit(otInstance *aInstance) SuccessOrExit(error = otMessageAppend(message, &packet[offset], static_cast(rval))); - error = otIp6Send(aInstance, message); +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + error = isIp4 ? otNat64Send(aInstance, message) : otIp6Send(aInstance, message); +#else + error = otIp6Send(aInstance, message); +#endif message = nullptr; exit: @@ -979,7 +1051,7 @@ static void processTransmit(otInstance *aInstance) { if (error == OT_ERROR_DROP) { - otLogInfoPlat("[netif] Message dropped by Thread", otThreadErrorToString(error)); + otLogInfoPlat("[netif] Message dropped by Thread"); } else { @@ -997,10 +1069,10 @@ static void logAddrEvent(bool isAdd, const ot::Ip6::Address &aAddress, otError e { otLogInfoPlat("[netif] %s [%s] %s%s", isAdd ? "ADD" : "DEL", aAddress.IsMulticast() ? "M" : "U", aAddress.ToString().AsCString(), - error == OT_ERROR_ALREADY - ? " (already subscribed, ignored)" - : error == OT_ERROR_REJECTED ? " (rejected)" - : error == OT_ERROR_NOT_FOUND ? " (not found, ignored)" : ""); + error == OT_ERROR_ALREADY ? " (already subscribed, ignored)" + : error == OT_ERROR_REJECTED ? " (rejected)" + : error == OT_ERROR_NOT_FOUND ? " (not found, ignored)" + : ""); } else { @@ -1013,7 +1085,7 @@ static void logAddrEvent(bool isAdd, const ot::Ip6::Address &aAddress, otError e static void processNetifAddrEvent(otInstance *aInstance, struct nlmsghdr *aNetlinkMessage) { - struct ifaddrmsg * ifaddr = reinterpret_cast(NLMSG_DATA(aNetlinkMessage)); + struct ifaddrmsg *ifaddr = reinterpret_cast(NLMSG_DATA(aNetlinkMessage)); size_t rtaLength; otError error = OT_ERROR_NONE; struct sockaddr_in6 addr6; @@ -1134,13 +1206,21 @@ static void processNetifLinkEvent(otInstance *aInstance, struct nlmsghdr *aNetli otLogInfoPlat("[netif] Succeeded to sync netif state with host"); } +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE && OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + if (isUp && gNat64Cidr.mLength > 0) + { + SuccessOrExit(error = otNat64SetIp4Cidr(gInstance, &gNat64Cidr)); + otLogInfoPlat("[netif] Succeeded to enable NAT64"); + } +#endif + exit: if (error != OT_ERROR_NONE) { otLogWarnPlat("[netif] Failed to sync netif state with host: %s", otThreadErrorToString(error)); } } -#endif +#endif // defined(__linux__) #if defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) @@ -1169,10 +1249,10 @@ static void processNetifAddrEvent(otInstance *aInstance, struct rt_msghdr *rtm) #endif struct sockaddr_in6 addr6; struct sockaddr_in6 netmask; - uint8_t * addrbuf; + uint8_t *addrbuf; unsigned int addrmask = 0; unsigned int i; - struct sockaddr * sa; + struct sockaddr *sa; bool is_link_local; addr6.sin6_family = 0; @@ -1493,10 +1573,10 @@ static void processMLDEvent(otInstance *aInstance) struct sockaddr_in6 srcAddr; socklen_t addrLen = sizeof(srcAddr); bool fromSelf = false; - MLDv2Header * hdr = reinterpret_cast(buffer); + MLDv2Header *hdr = reinterpret_cast(buffer); size_t offset; uint8_t type; - struct ifaddrs * ifAddrs = nullptr; + struct ifaddrs *ifAddrs = nullptr; char addressString[INET6_ADDRSTRLEN + 1]; bufferLen = recvfrom(sMLDMonitorFd, buffer, sizeof(buffer), 0, reinterpret_cast(&srcAddr), &addrLen); @@ -1564,22 +1644,71 @@ static void processMLDEvent(otInstance *aInstance) #endif #if defined(__linux__) +static void SetAddrGenModeToNone(void) +{ + struct + { + struct nlmsghdr nh; + struct ifinfomsg ifi; + char buf[512]; + } req; + + const enum in6_addr_gen_mode mode = IN6_ADDR_GEN_MODE_NONE; + + memset(&req, 0, sizeof(req)); + + req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + req.nh.nlmsg_type = RTM_NEWLINK; + req.nh.nlmsg_pid = 0; + req.nh.nlmsg_seq = ++sNetlinkSequence; + + req.ifi.ifi_index = static_cast(gNetifIndex); + req.ifi.ifi_change = 0xffffffff; + req.ifi.ifi_flags = IFF_MULTICAST | IFF_NOARP; + + { + struct rtattr *afSpec = AddRtAttr(&req.nh, sizeof(req), IFLA_AF_SPEC, 0, 0); + struct rtattr *afInet6 = AddRtAttr(&req.nh, sizeof(req), AF_INET6, 0, 0); + struct rtattr *inet6AddrGenMode = + AddRtAttr(&req.nh, sizeof(req), IFLA_INET6_ADDR_GEN_MODE, &mode, sizeof(mode)); + + afInet6->rta_len += inet6AddrGenMode->rta_len; + afSpec->rta_len += afInet6->rta_len; + } + + if (send(sNetlinkFd, &req, req.nh.nlmsg_len, 0) != -1) + { + otLogInfoPlat("[netif] Sent request#%u to set addr_gen_mode to %d", sNetlinkSequence, mode); + } + else + { + otLogWarnPlat("[netif] Failed to send request#%u to set addr_gen_mode to %d", sNetlinkSequence, mode); + } +} + // set up the tun device -static void platformConfigureTunDevice(const char *aInterfaceName, char *deviceName, size_t deviceNameLen) +static void platformConfigureTunDevice(otPlatformConfig *aPlatformConfig) { struct ifreq ifr; + const char *interfaceName; sTunFd = open(OPENTHREAD_POSIX_TUN_DEVICE, O_RDWR | O_CLOEXEC | O_NONBLOCK); VerifyOrDie(sTunFd >= 0, OT_EXIT_ERROR_ERRNO); memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_flags = IFF_TUN | IFF_NO_PI | static_cast(IFF_TUN_EXCL); + ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + if (!aPlatformConfig->mPersistentInterface) + { + ifr.ifr_flags |= static_cast(IFF_TUN_EXCL); + } - if (aInterfaceName) + interfaceName = aPlatformConfig->mInterfaceName; + if (interfaceName) { - VerifyOrDie(strlen(aInterfaceName) < IFNAMSIZ, OT_EXIT_INVALID_ARGUMENTS); + VerifyOrDie(strlen(interfaceName) < IFNAMSIZ, OT_EXIT_INVALID_ARGUMENTS); - strncpy(ifr.ifr_name, aInterfaceName, IFNAMSIZ); + strncpy(ifr.ifr_name, interfaceName, IFNAMSIZ); } else { @@ -1587,9 +1716,18 @@ static void platformConfigureTunDevice(const char *aInterfaceName, char *deviceN } VerifyOrDie(ioctl(sTunFd, TUNSETIFF, static_cast(&ifr)) == 0, OT_EXIT_ERROR_ERRNO); - VerifyOrDie(ioctl(sTunFd, TUNSETLINK, ARPHRD_VOID) == 0, OT_EXIT_ERROR_ERRNO); - strncpy(deviceName, ifr.ifr_name, deviceNameLen); + strncpy(gNetifName, ifr.ifr_name, sizeof(gNetifName)); + + if (aPlatformConfig->mPersistentInterface) + { + VerifyOrDie(ioctl(sTunFd, TUNSETPERSIST, 1) == 0, OT_EXIT_ERROR_ERRNO); + // Set link down to reset the tun configuration. + // This will drop all existing IP addresses on the interface. + SetLinkState(gInstance, false); + } + + VerifyOrDie(ioctl(sTunFd, TUNSETLINK, ARPHRD_NONE) == 0, OT_EXIT_ERROR_ERRNO); ifr.ifr_mtu = static_cast(kMaxIp6Size); VerifyOrDie(ioctl(sIpFd, SIOCSIFMTU, static_cast(&ifr)) == 0, OT_EXIT_ERROR_ERRNO); @@ -1597,9 +1735,9 @@ static void platformConfigureTunDevice(const char *aInterfaceName, char *deviceN #endif #if defined(__APPLE__) && (OPENTHREAD_POSIX_CONFIG_MACOS_TUN_OPTION == OT_POSIX_CONFIG_MACOS_UTUN) -static void platformConfigureTunDevice(const char *aInterfaceName, char *deviceName, size_t deviceNameLen) +static void platformConfigureTunDevice(otPlatformConfig *aPlatformConfig) { - (void)aInterfaceName; + (void)aPlatformConfig; int err = 0; struct sockaddr_ctl addr; struct ctl_info info; @@ -1622,11 +1760,11 @@ static void platformConfigureTunDevice(const char *aInterfaceName, char *deviceN VerifyOrDie(err == 0, OT_EXIT_ERROR_ERRNO); socklen_t devNameLen; - devNameLen = (socklen_t)deviceNameLen; - err = getsockopt(sTunFd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, deviceName, &devNameLen); + devNameLen = (socklen_t)sizeof(gNetifName); + err = getsockopt(sTunFd, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, gNetifName, &devNameLen); VerifyOrDie(err == 0, OT_EXIT_ERROR_ERRNO); - otLogInfoPlat("[netif] Tunnel device name = '%s'", deviceName); + otLogInfoPlat("[netif] Tunnel device name = '%s'", gNetifName); } #endif @@ -1649,14 +1787,14 @@ static otError destroyTunnel(void) #if defined(__NetBSD__) || \ (defined(__APPLE__) && (OPENTHREAD_POSIX_CONFIG_MACOS_TUN_OPTION == OT_POSIX_CONFIG_MACOS_TUN)) || \ defined(__FreeBSD__) -static void platformConfigureTunDevice(const char *aInterfaceName, char *deviceName, size_t deviceNameLen) +static void platformConfigureTunDevice(otPlatformConfig *aPlatformConfig) { int flags = IFF_BROADCAST | IFF_MULTICAST; int err; const char *last_slash; const char *path; - (void)aInterfaceName; + (void)aPlatformConfig; path = OPENTHREAD_POSIX_TUN_DEVICE; @@ -1676,7 +1814,7 @@ static void platformConfigureTunDevice(const char *aInterfaceName, char *deviceN VerifyOrDie(last_slash != nullptr, OT_EXIT_ERROR_ERRNO); last_slash++; - strncpy(deviceName, last_slash, deviceNameLen); + strncpy(gNetifName, last_slash, sizeof(gNetifName)); } #endif @@ -1728,13 +1866,13 @@ static void platformConfigureNetLink(void) #endif // defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__) } -void platformNetifInit(const char *aInterfaceName) +void platformNetifInit(otPlatformConfig *aPlatformConfig) { sIpFd = SocketWithCloseExec(AF_INET6, SOCK_DGRAM, IPPROTO_IP, kSocketNonBlock); VerifyOrDie(sIpFd >= 0, OT_EXIT_ERROR_ERRNO); platformConfigureNetLink(); - platformConfigureTunDevice(aInterfaceName, gNetifName, sizeof(gNetifName)); + platformConfigureTunDevice(aPlatformConfig); gNetifIndex = if_nametoindex(gNetifName); VerifyOrDie(gNetifIndex > 0, OT_EXIT_FAILURE); @@ -1742,6 +1880,10 @@ void platformNetifInit(const char *aInterfaceName) #if OPENTHREAD_POSIX_USE_MLD_MONITOR mldListenerInit(); #endif + +#if __linux__ + SetAddrGenModeToNone(); +#endif } void platformNetifSetUp(void) @@ -1755,15 +1897,20 @@ void platformNetifSetUp(void) otIcmp6SetEchoMode(gInstance, OT_ICMP6_ECHO_HANDLER_DISABLED); #endif otIp6SetReceiveCallback(gInstance, processReceive, gInstance); +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + // We can use the same function for IPv6 and translated IPv4 messages. + otNat64SetReceiveIp4Callback(gInstance, processReceive, gInstance); +#endif otIp6SetAddressCallback(gInstance, processAddressChange, gInstance); #if OPENTHREAD_POSIX_MULTICAST_PROMISCUOUS_REQUIRED otIp6SetMulticastPromiscuousEnabled(aInstance, true); #endif +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + gResolver.Init(); +#endif } -void platformNetifTearDown(void) -{ -} +void platformNetifTearDown(void) {} void platformNetifDeinit(void) { @@ -1819,6 +1966,10 @@ void platformNetifUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *a FD_SET(sMLDMonitorFd, aErrorFdSet); #endif +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + gResolver.UpdateFdSet(aReadFdSet, aErrorFdSet, aMaxFd); +#endif + if (sTunFd > *aMaxFd) { *aMaxFd = sTunFd; @@ -1881,6 +2032,10 @@ void platformNetifProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, c } #endif +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + gResolver.Process(aReadFdSet, aErrorFdSet); +#endif + exit: return; } diff --git a/src/posix/platform/openthread-core-posix-config.h b/src/posix/platform/openthread-core-posix-config.h index 6708751f2f6..0a3cd204813 100644 --- a/src/posix/platform/openthread-core-posix-config.h +++ b/src/posix/platform/openthread-core-posix-config.h @@ -44,6 +44,16 @@ #define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS 256 #endif +/** + * @def OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE + * + * Define OpenThread diagnostic mode output buffer size in bytes + * + */ +#ifndef OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE +#define OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE 500 +#endif + /** * @def OPENTHREAD_CONFIG_LOG_PLATFORM * @@ -299,4 +309,24 @@ #define OPENTHREAD_CONFIG_SRP_CLIENT_BUFFERS_MAX_SERVICES 20 #endif +/** + * @def OPENTHREAD_CONFIG_ASSERT_CHECK_API_POINTER_PARAM_FOR_NULL + * + * Define as 1 to enable assert check of pointer-type API input parameters against null. + * + */ +#ifndef OPENTHREAD_CONFIG_ASSERT_CHECK_API_POINTER_PARAM_FOR_NULL +#define OPENTHREAD_CONFIG_ASSERT_CHECK_API_POINTER_PARAM_FOR_NULL 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + * + * Define as 1 to enable platform power calibration support. + * + */ +#ifndef OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +#define OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE 1 +#endif + #endif // OPENTHREAD_CORE_POSIX_CONFIG_H_ diff --git a/src/posix/platform/openthread-posix-config.h b/src/posix/platform/openthread-posix-config.h index 191f5a00739..170f24f85e8 100644 --- a/src/posix/platform/openthread-posix-config.h +++ b/src/posix/platform/openthread-posix-config.h @@ -31,6 +31,10 @@ #include "openthread-core-config.h" +#ifdef OPENTHREAD_POSIX_CONFIG_FILE +#include OPENTHREAD_POSIX_CONFIG_FILE +#endif + /** * @file * @brief @@ -214,6 +218,16 @@ #define OPENTHREAD_POSIX_CONFIG_MAX_EXTERNAL_ROUTE_NUM 8 #endif +/** + * @def OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE + * + * Define as 1 to enable discovering NAT64 posix on adjacent infrastructure link. + * + */ +#ifndef OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE +#define OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE 0 +#endif + /** * @def OPENTHREAD_POSIX_CONFIG_FIREWALL_ENABLE * @@ -284,4 +298,79 @@ #define OPENTHREAD_POSIX_CONFIG_TREL_UDP_PORT 0 #endif +/** + * @def OPENTHREAD_POSIX_CONFIG_NAT64_CIDR + * + * This setting configures the NAT64 CIDR, used by NAT64 translator. + * + */ +#ifndef OPENTHREAD_POSIX_CONFIG_NAT64_CIDR +#define OPENTHREAD_POSIX_CONFIG_NAT64_CIDR "192.168.255.0/24" +#endif + +/** + * @def OPENTHREAD_POSIX_CONFIG_BACKTRACE_ENABLE + * + * Define as 1 to enable backtrace support. + * + */ +#ifndef OPENTHREAD_POSIX_CONFIG_BACKTRACE_ENABLE +#define OPENTHREAD_POSIX_CONFIG_BACKTRACE_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_POSIX_CONFIG_ANDROID_ENABLE + * + * Define as 1 to enable android support. + * + */ +#ifndef OPENTHREAD_POSIX_CONFIG_ANDROID_ENABLE +#define OPENTHREAD_POSIX_CONFIG_ANDROID_ENABLE 0 +#endif + +/** + * @def OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE + * + * Defines `1` to enable the posix implementation of platform/infra_if.h APIs. + * The default value is set to `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` if it's + * not explicit defined. + */ +#ifndef OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE +#define OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#endif + +/** + * @def OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE + * + * Define the path of the factory config file. + * + * Note: The factory config file contains the persist data that configured by the factory. And it won't be changed + * after a device firmware update OTA is done. + * + */ +#ifndef OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE +#define OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE "src/posix/platform/openthread.conf.example" +#endif + +/** + * @def OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE + * + * Define the path of the product config file. + * + */ +#ifndef OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE +#define OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE "src/posix/platform/openthread.conf.example" +#endif + +/** + * @def OPENTHREAD_POSIX_CONFIG_RCP_TIME_SYNC_INTERVAL + * + * This setting configures the interval (in units of microseconds) for host-rcp + * time sync. The host will recalculate the time offset between host and RCP + * every interval. + * + */ +#ifndef OPENTHREAD_POSIX_CONFIG_RCP_TIME_SYNC_INTERVAL +#define OPENTHREAD_POSIX_CONFIG_RCP_TIME_SYNC_INTERVAL (60 * 1000 * 1000) +#endif #endif // OPENTHREAD_PLATFORM_CONFIG_H_ diff --git a/src/posix/platform/openthread.conf.example b/src/posix/platform/openthread.conf.example new file mode 100644 index 00000000000..83a2908cc70 --- /dev/null +++ b/src/posix/platform/openthread.conf.example @@ -0,0 +1,24 @@ +# +# Sample configuration file +# +# Modify this to use your own configurations! +# + +# Target power table entries. +# target_power=,,, +target_power=ETSI,11,26,1000 +target_power=FCC,11,14,1700 +target_power=FCC,15,24,2000 +target_power=FCC,25,26,1600 + +# Region domain mapping table entries. +# region_domain_mapping=,,,... +region_domain_mapping=FCC,AU,CA,CL,CO,IN,MX,PE,TW,US +region_domain_mapping=ETSI,WW + +# Power calibration table entries. +# calibrated_power=,,, +calibrated_power=11,25,1900,112233 +calibrated_power=11,25,1000,223344 +calibrated_power=26,26,1500,334455 +calibrated_power=26,26,700,445566 diff --git a/src/posix/platform/platform-posix.h b/src/posix/platform/platform-posix.h index 919a319e2ad..8c36122efca 100644 --- a/src/posix/platform/platform-posix.h +++ b/src/posix/platform/platform-posix.h @@ -49,6 +49,7 @@ #include #include #include +#include #include #include @@ -243,7 +244,7 @@ void platformUartProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, co * @param[in] aInterfaceName A pointer to Thread network interface name. * */ -void platformNetifInit(const char *aInterfaceName); +void platformNetifInit(otPlatformConfig *aPlatformConfig); /** * This function sets up platform netif. @@ -323,7 +324,7 @@ void virtualTimeDeinit(void); * @param[in] aWriteFdSet A pointer to the write file descriptors. * */ -void virtualTimeProcess(otInstance * aInstance, +void virtualTimeProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet, const fd_set *aErrorFdSet); @@ -339,10 +340,10 @@ void virtualTimeProcess(otInstance * aInstance, * @param[in,out] aTimeout A pointer to the timeout. * */ -void virtualTimeUpdateFdSet(fd_set * aReadFdSet, - fd_set * aWriteFdSet, - fd_set * aErrorFdSet, - int * aMaxFd, +void virtualTimeUpdateFdSet(fd_set *aReadFdSet, + fd_set *aWriteFdSet, + fd_set *aErrorFdSet, + int *aMaxFd, struct timeval *aTimeout); /** @@ -447,6 +448,11 @@ extern char gNetifName[IFNAMSIZ]; */ extern unsigned int gNetifIndex; +/** + * The CIDR for NAT64 + */ +extern otIp4Cidr gNat64Cidr; + /** * This function initializes platform Backbone network. * @@ -518,6 +524,12 @@ extern unsigned int gBackboneNetifIndex; */ bool platformInfraIfIsRunning(void); +/** + * This function initializes backtrace module. + * + */ +void platformBacktraceInit(void); + #ifdef __cplusplus } #endif diff --git a/src/posix/platform/power.cpp b/src/posix/platform/power.cpp new file mode 100644 index 00000000000..ea832a38a88 --- /dev/null +++ b/src/posix/platform/power.cpp @@ -0,0 +1,126 @@ +/* + * 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 strain 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. + */ + +#include "power.hpp" +#include "common/code_utils.hpp" +#include "utils/parse_cmdline.hpp" + +namespace ot { +namespace Power { + +otError Domain::Set(const char *aDomain) +{ + otError error = OT_ERROR_NONE; + uint16_t length = static_cast(strlen(aDomain)); + + VerifyOrExit(length <= kDomainSize, error = OT_ERROR_INVALID_ARGS); + memcpy(m8, aDomain, length); + m8[length] = '\0'; + +exit: + return error; +} + +otError TargetPower::FromString(char *aString) +{ + otError error = OT_ERROR_NONE; + char *str; + + VerifyOrExit((str = strtok(aString, ",")) != nullptr, error = OT_ERROR_PARSE); + SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(str, mChannelStart)); + + VerifyOrExit((str = strtok(nullptr, ",")) != nullptr, error = OT_ERROR_PARSE); + SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(str, mChannelEnd)); + + VerifyOrExit((str = strtok(nullptr, ",")) != nullptr, error = OT_ERROR_PARSE); + SuccessOrExit(error = Utils::CmdLineParser::ParseAsInt16(str, mTargetPower)); + +exit: + return error; +} + +TargetPower::InfoString TargetPower::ToString(void) const +{ + InfoString string; + + string.Append("%u,%u,%d", mChannelStart, mChannelEnd, mTargetPower); + + return string; +} + +otError RawPowerSetting::Set(const char *aRawPowerSetting) +{ + otError error; + uint16_t length = sizeof(mData); + + SuccessOrExit(error = ot::Utils::CmdLineParser::ParseAsHexString(aRawPowerSetting, length, mData)); + mLength = static_cast(length); + +exit: + return error; +} + +RawPowerSetting::InfoString RawPowerSetting::ToString(void) const +{ + InfoString string; + + string.AppendHexBytes(mData, mLength); + + return string; +} + +otError CalibratedPower::FromString(char *aString) +{ + otError error = OT_ERROR_NONE; + char *str; + char *pSave; + + VerifyOrExit((str = strtok_r(aString, ",", &pSave)) != nullptr, error = OT_ERROR_PARSE); + SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(str, mChannelStart)); + + VerifyOrExit((str = strtok_r(nullptr, ",", &pSave)) != nullptr, error = OT_ERROR_PARSE); + SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(str, mChannelEnd)); + + VerifyOrExit((str = strtok_r(nullptr, ",", &pSave)) != nullptr, error = OT_ERROR_PARSE); + SuccessOrExit(error = Utils::CmdLineParser::ParseAsInt16(str, mActualPower)); + SuccessOrExit(error = mRawPowerSetting.Set(pSave)); + +exit: + return error; +} + +CalibratedPower::InfoString CalibratedPower::ToString(void) const +{ + InfoString string; + + string.Append("%u,%u,%d,%s", mChannelStart, mChannelEnd, mActualPower, mRawPowerSetting.ToString().AsCString()); + + return string; +} +} // namespace Power +} // namespace ot diff --git a/src/posix/platform/power.hpp b/src/posix/platform/power.hpp new file mode 100644 index 00000000000..dbc92e05e40 --- /dev/null +++ b/src/posix/platform/power.hpp @@ -0,0 +1,289 @@ +/* + * 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 strain 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. + */ + +#ifndef POSIX_PLATFORM_POWER_H +#define POSIX_PLATFORM_POWER_H + +#include +#include +#include + +#include +#include +#include "common/string.hpp" + +namespace ot { +namespace Power { + +class Domain +{ +public: + Domain(void) { m8[0] = '\0'; } + + /** + * This method sets the regulatory domain from a given null terminated C string. + * + * @param[in] aDomain A regulatory domain name C string. + * + * @retval OT_ERROR_NONE Successfully set the regulatory domain. + * @retval OT_ERROR_INVALID_ARGS Given regulatory domain is too long. + * + */ + otError Set(const char *aDomain); + + /** + * This method overloads operator `==` to evaluate whether or not two `Domain` instances are equal. + * + * @param[in] aOther The other `Domain` instance to compare with. + * + * @retval TRUE If the two `Domain` instances are equal. + * @retval FALSE If the two `Domain` instances not equal. + * + */ + bool operator==(const Domain &aOther) const { return strcmp(m8, aOther.m8) == 0; } + + /** + * This method overloads operator `!=` to evaluate whether or not the `Domain` is unequal to a given C string. + * + * @param[in] aCString A C string to compare with. Can be `nullptr` which then returns 'TRUE'. + * + * @retval TRUE If the two regulatory domains are not equal. + * @retval FALSE If the two regulatory domains are equal. + * + */ + bool operator!=(const char *aCString) const { return (aCString == nullptr) ? true : strcmp(m8, aCString) != 0; } + + /** + * This method gets the regulatory domain as a null terminated C string. + * + * @returns The regulatory domain as a null terminated C string array. + * + */ + const char *AsCString(void) const { return m8; } + +private: + static constexpr uint8_t kDomainSize = 8; + char m8[kDomainSize + 1]; +}; + +class TargetPower +{ +public: + static constexpr uint16_t kInfoStringSize = 12; ///< Recommended buffer size to use with `ToString()`. + typedef String InfoString; + + /** + * This method parses an target power string. + * + * The string MUST follow the format: ",,". + * For example, "11,26,2000" + * + * @param[in] aString A pointer to the null-terminated string. + * + * @retval OT_ERROR_NONE Successfully parsed the target power string. + * @retval OT_ERROR_PARSE Failed to parse the target power string. + * + */ + otError FromString(char *aString); + + /** + * This method returns the start channel. + * + * @returns The channel. + * + */ + uint8_t GetChannelStart(void) const { return mChannelStart; } + + /** + * This method returns the end channel. + * + * @returns The channel. + * + */ + uint8_t GetChannelEnd(void) const { return mChannelEnd; } + + /** + * This method returns the target power. + * + * @returns The target power, in 0.01 dBm. + * + */ + int16_t GetTargetPower(void) const { return mTargetPower; } + + /** + * This method converts the target power into a human-readable string. + * + * @returns An `InfoString` object representing the target power. + * + */ + InfoString ToString(void) const; + +private: + uint8_t mChannelStart; + uint8_t mChannelEnd; + int16_t mTargetPower; +}; + +class RawPowerSetting +{ +public: + // Recommended buffer size to use with `ToString()`. + static constexpr uint16_t kInfoStringSize = OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE * 2 + 1; + typedef String InfoString; + + /** + * This method sets the raw power setting from a given null terminated hex C string. + * + * @param[in] aRawPowerSetting A raw power setting hex C string. + * + * @retval OT_ERROR_NONE Successfully set the raw power setting. + * @retval OT_ERROR_INVALID_ARGS The given raw power setting is too long. + * + */ + otError Set(const char *aRawPowerSetting); + + /** + * This method converts the raw power setting into a human-readable string. + * + * @returns An `InfoString` object representing the calibrated power. + * + */ + InfoString ToString(void) const; + + const uint8_t *GetData(void) const { return mData; } + uint16_t GetLength(void) const { return mLength; } + +private: + static constexpr uint16_t kMaxRawPowerSettingSize = OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE; + + uint8_t mData[kMaxRawPowerSettingSize]; + uint16_t mLength; +}; + +class CalibratedPower +{ +public: + // Recommended buffer size to use with `ToString()`. + static constexpr uint16_t kInfoStringSize = 20 + RawPowerSetting::kInfoStringSize; + typedef String InfoString; + + /** + * This method parses an calibrated power string. + * + * The string MUST follow the format: ",,,". + * For example, "11,26,2000,1122aabb" + * + * @param[in] aString A pointer to the null-terminated string. + * + * @retval OT_ERROR_NONE Successfully parsed the calibrated power string. + * @retval OT_ERROR_PARSE Failed to parse the calibrated power string. + * + */ + otError FromString(char *aString); + + /** + * This method returns the start channel. + * + * @returns The channel. + * + */ + uint8_t GetChannelStart(void) const { return mChannelStart; } + + /** + * This method sets the start channel. + * + * @param[in] aChannelStart The start channel. + * + */ + void SetChannelStart(uint8_t aChannelStart) { mChannelStart = aChannelStart; } + + /** + * This method returns the end channel. + * + * @returns The channel. + * + */ + uint8_t GetChannelEnd(void) const { return mChannelEnd; } + + /** + * This method sets the end channel. + * + * @param[in] aChannelEnd The end channel. + * + */ + void SetChannelEnd(uint8_t aChannelEnd) { mChannelEnd = aChannelEnd; } + + /** + * This method returns the actual power. + * + * @returns The actual measured power, in 0.01 dBm. + * + */ + int16_t GetActualPower(void) const { return mActualPower; } + + /** + * This method sets the actual channel. + * + * @param[in] aActualPower The actual power in 0.01 dBm. + * + */ + void SetActualPower(int16_t aActualPower) { mActualPower = aActualPower; } + + /** + * This method returns the raw power setting. + * + * @returns A reference to the raw power setting. + * + */ + const RawPowerSetting &GetRawPowerSetting(void) const { return mRawPowerSetting; } + + /** + * This method sets the raw power setting. + * + * @param[in] aRawPowerSetting The raw power setting. + * + */ + void SetRawPowerSetting(const RawPowerSetting &aRawPowerSetting) { mRawPowerSetting = aRawPowerSetting; } + + /** + * This method converts the calibrated power into a human-readable string. + * + * @returns An `InfoString` object representing the calibrated power. + * + */ + InfoString ToString(void) const; + +private: + uint8_t mChannelStart; + uint8_t mChannelEnd; + int16_t mActualPower; + RawPowerSetting mRawPowerSetting; +}; +} // namespace Power +} // namespace ot +#endif // POSIX_PLATFORM_POWER_H diff --git a/src/posix/platform/power_updater.cpp b/src/posix/platform/power_updater.cpp new file mode 100644 index 00000000000..d3b45bb9241 --- /dev/null +++ b/src/posix/platform/power_updater.cpp @@ -0,0 +1,180 @@ +/* + * 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 strain 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. + */ + +#include "power_updater.hpp" + +#include "platform-posix.h" +#include +#include "lib/platform/exit_code.h" + +#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + +namespace ot { +namespace Posix { + +otError PowerUpdater::SetRegion(uint16_t aRegionCode) +{ + otError error = OT_ERROR_NONE; + int iterator = 0; + Power::Domain domain; + Power::TargetPower targetPower; + + if (GetDomain(aRegionCode, domain) != OT_ERROR_NONE) + { + // If failed to find the domain for the region, use the world wide region as the default region. + VerifyOrExit(GetDomain(kRegionCodeWorldWide, domain) == OT_ERROR_NONE, error = OT_ERROR_FAILED); + } + + while (GetNextTargetPower(domain, iterator, targetPower) == OT_ERROR_NONE) + { + otLogInfoPlat("Update target power: %s\r\n", targetPower.ToString().AsCString()); + + for (uint8_t ch = targetPower.GetChannelStart(); ch <= targetPower.GetChannelEnd(); ch++) + { + SuccessOrExit(error = otPlatRadioSetChannelTargetPower(gInstance, ch, targetPower.GetTargetPower())); + } + } + + SuccessOrExit(error = UpdateCalibratedPower()); + + mRegionCode = aRegionCode; + +exit: + if (error == OT_ERROR_NONE) + { + otLogInfoPlat("Set region \"%c%c\" successfully", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff)); + } + else + { + otLogCritPlat("Set region \"%c%c\" failed, Error: %s", (aRegionCode >> 8) & 0xff, (aRegionCode & 0xff), + otThreadErrorToString(error)); + } + + return error; +} + +otError PowerUpdater::UpdateCalibratedPower(void) +{ + otError error = OT_ERROR_NONE; + int iterator = 0; + char value[kMaxValueSize]; + Power::CalibratedPower calibratedPower; + ConfigFile *calibrationFile = &mFactoryConfigFile; + + SuccessOrExit(error = otPlatRadioClearCalibratedPowers(gInstance)); + + // If the distribution of output power is large, the factory needs to measure the power calibration data + // for each device individually, and the power calibration data will be written to the factory config file. + // Otherwise, the power calibration data can be pre-configured in the product config file. + if (calibrationFile->Get(kKeyCalibratedPower, iterator, value, sizeof(value)) != OT_ERROR_NONE) + { + calibrationFile = &mProductConfigFile; + } + + iterator = 0; + while (calibrationFile->Get(kKeyCalibratedPower, iterator, value, sizeof(value)) == OT_ERROR_NONE) + { + SuccessOrExit(error = calibratedPower.FromString(value)); + otLogInfoPlat("Update calibrated power: %s\r\n", calibratedPower.ToString().AsCString()); + + for (uint8_t ch = calibratedPower.GetChannelStart(); ch <= calibratedPower.GetChannelEnd(); ch++) + { + SuccessOrExit(error = otPlatRadioAddCalibratedPower(gInstance, ch, calibratedPower.GetActualPower(), + calibratedPower.GetRawPowerSetting().GetData(), + calibratedPower.GetRawPowerSetting().GetLength())); + } + } + +exit: + if (error != OT_ERROR_NONE) + { + otLogCritPlat("Update calibrated power table failed, Error: %s", otThreadErrorToString(error)); + } + + return error; +} + +otError PowerUpdater::GetDomain(uint16_t aRegionCode, Power::Domain &aDomain) +{ + otError error = OT_ERROR_NOT_FOUND; + int iterator = 0; + char value[kMaxValueSize]; + char *str; + + while (mProductConfigFile.Get(kKeyRegionDomainMapping, iterator, value, sizeof(value)) == OT_ERROR_NONE) + { + if ((str = strtok(value, kCommaDelimiter)) == nullptr) + { + continue; + } + + while ((str = strtok(nullptr, kCommaDelimiter)) != nullptr) + { + if ((strlen(str) == 2) && (StringToRegionCode(str) == aRegionCode)) + { + ExitNow(error = aDomain.Set(value)); + } + } + } + +exit: + if (error != OT_ERROR_NONE) + { + otLogCritPlat("Get domain failed, Error: %s", otThreadErrorToString(error)); + } + + return error; +} + +otError PowerUpdater::GetNextTargetPower(const Power::Domain &aDomain, int &aIterator, Power::TargetPower &aTargetPower) +{ + otError error = OT_ERROR_NOT_FOUND; + char value[kMaxValueSize]; + char *domain; + char *psave; + + while (mProductConfigFile.Get(kKeyTargetPower, aIterator, value, sizeof(value)) == OT_ERROR_NONE) + { + if (((domain = strtok_r(value, kCommaDelimiter, &psave)) == nullptr) || (aDomain != domain)) + { + continue; + } + + if ((error = aTargetPower.FromString(psave)) != OT_ERROR_NONE) + { + otLogCritPlat("Read target power failed, Error: %s", otThreadErrorToString(error)); + } + break; + } + + return error; +} + +} // namespace Posix +} // namespace ot +#endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE diff --git a/src/posix/platform/power_updater.hpp b/src/posix/platform/power_updater.hpp new file mode 100644 index 00000000000..fb12c23855a --- /dev/null +++ b/src/posix/platform/power_updater.hpp @@ -0,0 +1,116 @@ +/* + * 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 strain 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. + */ + +#ifndef POSIX_PLATFORM_POWER_UPDATER_HPP_ +#define POSIX_PLATFORM_POWER_UPDATER_HPP_ + +#include "openthread-posix-config.h" + +#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + +#include +#include + +#include +#include +#include + +#include "config_file.hpp" +#include "power.hpp" +#include "common/code_utils.hpp" + +namespace ot { +namespace Posix { + +/** + * This class updates the target power table and calibrated power table to the RCP. + * + */ +class PowerUpdater +{ +public: + PowerUpdater(void) + : mFactoryConfigFile(kFactoryConfigFile) + , mProductConfigFile(kProductConfigFile) + , mRegionCode(0) + { + } + + /** + * Set the region code. + * + * The radio region format is the 2-bytes ascii representation of the + * ISO 3166 alpha-2 code. + * + * @param[in] aRegionCode The radio region. + * + * @retval OT_ERROR_NONE Successfully set region code. + * @retval OT_ERROR_FAILED Failed to set the region code. + * + */ + otError SetRegion(uint16_t aRegionCode); + + /** + * Get the region code. + * + * The radio region format is the 2-bytes ascii representation of the + * ISO 3166 alpha-2 code. + * + * @returns The region code. + * + */ + uint16_t GetRegion(void) const { return mRegionCode; } + +private: + const char *kFactoryConfigFile = OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE; + const char *kProductConfigFile = OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE; + const char *kKeyCalibratedPower = "calibrated_power"; + const char *kKeyTargetPower = "target_power"; + const char *kKeyRegionDomainMapping = "region_domain_mapping"; + const char *kCommaDelimiter = ","; + static constexpr uint16_t kMaxValueSize = 512; + static constexpr uint16_t kRegionCodeWorldWide = 0x5757; // Region Code: "WW" + + uint16_t StringToRegionCode(const char *aString) const + { + return static_cast(((aString[0] & 0xFF) << 8) | ((aString[1] & 0xFF) << 0)); + } + otError GetDomain(uint16_t aRegionCode, Power::Domain &aDomain); + otError GetNextTargetPower(const Power::Domain &aDomain, int &aIterator, Power::TargetPower &aTargetPower); + otError UpdateCalibratedPower(void); + + ConfigFile mFactoryConfigFile; + ConfigFile mProductConfigFile; + uint16_t mRegionCode; +}; + +} // namespace Posix +} // namespace ot + +#endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +#endif // POSIX_PLATFORM_POWER_UPDATER_HPP_ diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index 8fb63bfb2b4..00e2c1c9f52 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -35,10 +35,13 @@ #include +#include + #include "common/code_utils.hpp" #include "common/new.hpp" #include "lib/spinel/radio_spinel.hpp" #include "posix/platform/radio.hpp" +#include "utils/parse_cmdline.hpp" #if OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_UART #include "hdlc_interface.hpp" @@ -61,6 +64,11 @@ static ot::Spinel::RadioSpinel "OT_POSIX_RCP_BUS_VENDOR!" #endif +#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +#include "power_updater.hpp" +static ot::Posix::PowerUpdater sPowerUpdater; +#endif + namespace ot { namespace Posix { @@ -133,7 +141,7 @@ void Radio::Init(void) VerifyOrDie(strnlen(region, 3) == 2, OT_EXIT_INVALID_ARGUMENTS); regionCode = static_cast(static_cast(region[0]) << 8) + static_cast(region[1]); - SuccessOrDie(sRadioSpinel.SetRadioRegion(regionCode)); + SuccessOrDie(otPlatRadioSetRegion(gInstance, regionCode)); } #if OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE @@ -141,7 +149,7 @@ void Radio::Init(void) if (maxPowerTable != nullptr) { constexpr int8_t kPowerDefault = 30; // Default power 1 watt (30 dBm). - const char * str = nullptr; + const char *str = nullptr; uint8_t channel = ot::Radio::kChannelMin; int8_t power = kPowerDefault; otError error; @@ -151,10 +159,15 @@ void Radio::Init(void) { power = static_cast(strtol(str, nullptr, 0)); error = sRadioSpinel.SetChannelMaxTransmitPower(channel, power); - if (error != OT_ERROR_NONE && error != OT_ERROR_NOT_FOUND) + if (error != OT_ERROR_NONE && error != OT_ERROR_NOT_IMPLEMENTED) { DieNow(OT_ERROR_FAILED); } + else if (error == OT_ERROR_NOT_IMPLEMENTED) + { + otLogWarnPlat("The RCP doesn't support setting the max transmit power"); + } + ++channel; } @@ -162,10 +175,15 @@ void Radio::Init(void) while (channel <= ot::Radio::kChannelMax) { error = sRadioSpinel.SetChannelMaxTransmitPower(channel, power); - if (error != OT_ERROR_NONE && error != OT_ERROR_NOT_FOUND) + if (error != OT_ERROR_NONE && error != OT_ERROR_NOT_IMPLEMENTED) { DieNow(OT_ERROR_FAILED); } + else if (error == OT_ERROR_NOT_IMPLEMENTED) + { + otLogWarnPlat("The RCP doesn't support setting the max transmit power"); + } + ++channel; } @@ -183,13 +201,12 @@ void Radio::Init(void) #endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE } +void *Radio::GetSpinelInstance(void) { return &sRadioSpinel; } + } // namespace Posix } // namespace ot -void platformRadioDeinit(void) -{ - sRadioSpinel.Deinit(); -} +void platformRadioDeinit(void) { sRadioSpinel.Deinit(); } void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) { @@ -234,10 +251,7 @@ bool otPlatRadioIsEnabled(otInstance *aInstance) return sRadioSpinel.IsEnabled(); } -otError otPlatRadioEnable(otInstance *aInstance) -{ - return sRadioSpinel.Enable(aInstance); -} +otError otPlatRadioEnable(otInstance *aInstance) { return sRadioSpinel.Enable(aInstance); } otError otPlatRadioDisable(otInstance *aInstance) { @@ -493,8 +507,8 @@ otError otPlatRadioGetCoexMetrics(otInstance *aInstance, otRadioCoexMetrics *aCo #if OPENTHREAD_CONFIG_DIAG_ENABLE otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, - char * aArgs[], - char * aOutput, + char *aArgs[], + char *aOutput, size_t aOutputMaxLen) { // deliver the platform specific diags commands to radio only ncp. @@ -520,10 +534,7 @@ void otPlatDiagModeSet(bool aMode) return; } -bool otPlatDiagModeGet(void) -{ - return sRadioSpinel.IsDiagEnabled(); -} +bool otPlatDiagModeGet(void) { return sRadioSpinel.IsDiagEnabled(); } void otPlatDiagTxPowerSet(int8_t aTxPower) { @@ -547,17 +558,203 @@ void otPlatDiagChannelSet(uint8_t aChannel) return; } -void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError) +otError otPlatDiagGpioSet(uint32_t aGpio, bool aValue) +{ + otError error; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + + snprintf(cmd, sizeof(cmd), "gpio set %d %d", aGpio, aValue); + SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0)); + +exit: + return error; +} + +otError otPlatDiagGpioGet(uint32_t aGpio, bool *aValue) +{ + otError error; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; + char *str; + + snprintf(cmd, sizeof(cmd), "gpio get %d", aGpio); + SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, output, sizeof(output))); + VerifyOrExit((str = strtok(output, "\r")) != nullptr, error = OT_ERROR_FAILED); + *aValue = static_cast(atoi(str)); + +exit: + return error; +} + +otError otPlatDiagGpioSetMode(uint32_t aGpio, otGpioMode aMode) +{ + otError error; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + + snprintf(cmd, sizeof(cmd), "gpio mode %d %s", aGpio, aMode == OT_GPIO_MODE_INPUT ? "in" : "out"); + SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0)); + +exit: + return error; +} + +otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode) +{ + otError error; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; + char *str; + + snprintf(cmd, sizeof(cmd), "gpio mode %d", aGpio); + SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, output, sizeof(output))); + VerifyOrExit((str = strtok(output, "\r")) != nullptr, error = OT_ERROR_FAILED); + + if (strcmp(str, "in") == 0) + { + *aMode = OT_GPIO_MODE_INPUT; + } + else if (strcmp(str, "out") == 0) + { + *aMode = OT_GPIO_MODE_OUTPUT; + } + else + { + error = OT_ERROR_FAILED; + } + +exit: + return error; +} + +otError otPlatDiagRadioGetPowerSettings(otInstance *aInstance, + uint8_t aChannel, + int16_t *aTargetPower, + int16_t *aActualPower, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) { OT_UNUSED_VARIABLE(aInstance); - OT_UNUSED_VARIABLE(aFrame); - OT_UNUSED_VARIABLE(aError); + static constexpr uint16_t kRawPowerStringSize = OPENTHREAD_CONFIG_POWER_CALIBRATION_RAW_POWER_SETTING_SIZE * 2 + 1; + static constexpr uint16_t kFmtStringSize = 100; + + otError error; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; + int targetPower; + int actualPower; + char rawPowerSetting[kRawPowerStringSize]; + char fmt[kFmtStringSize]; + + assert((aTargetPower != nullptr) && (aActualPower != nullptr) && (aRawPowerSetting != nullptr) && + (aRawPowerSettingLength != nullptr)); + + snprintf(cmd, sizeof(cmd), "powersettings %d", aChannel); + SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, output, sizeof(output))); + snprintf(fmt, sizeof(fmt), "TargetPower(0.01dBm): %%d\r\nActualPower(0.01dBm): %%d\r\nRawPowerSetting: %%%us\r\n", + kRawPowerStringSize); + VerifyOrExit(sscanf(output, fmt, &targetPower, &actualPower, rawPowerSetting) == 3, error = OT_ERROR_FAILED); + SuccessOrExit( + error = ot::Utils::CmdLineParser::ParseAsHexString(rawPowerSetting, *aRawPowerSettingLength, aRawPowerSetting)); + *aTargetPower = static_cast(targetPower); + *aActualPower = static_cast(actualPower); + +exit: + return error; } -void otPlatDiagAlarmCallback(otInstance *aInstance) +otError otPlatDiagRadioSetRawPowerSetting(otInstance *aInstance, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength) { OT_UNUSED_VARIABLE(aInstance); + + otError error; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + int nbytes; + + assert(aRawPowerSetting != nullptr); + + nbytes = snprintf(cmd, sizeof(cmd), "rawpowersetting "); + + for (uint16_t i = 0; i < aRawPowerSettingLength; i++) + { + nbytes += snprintf(cmd + nbytes, sizeof(cmd) - static_cast(nbytes), "%02x", aRawPowerSetting[i]); + VerifyOrExit(nbytes < static_cast(sizeof(cmd)), error = OT_ERROR_INVALID_ARGS); + } + + SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0)); + +exit: + return error; +} + +otError otPlatDiagRadioGetRawPowerSetting(otInstance *aInstance, + uint8_t *aRawPowerSetting, + uint16_t *aRawPowerSettingLength) +{ + OT_UNUSED_VARIABLE(aInstance); + otError error; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + char output[OPENTHREAD_CONFIG_DIAG_OUTPUT_BUFFER_SIZE]; + char *str; + + assert((aRawPowerSetting != nullptr) && (aRawPowerSettingLength != nullptr)); + + snprintf(cmd, sizeof(cmd), "rawpowersetting"); + SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, output, sizeof(output))); + VerifyOrExit((str = strtok(output, "\r")) != nullptr, error = OT_ERROR_FAILED); + SuccessOrExit(error = ot::Utils::CmdLineParser::ParseAsHexString(str, *aRawPowerSettingLength, aRawPowerSetting)); + +exit: + return error; +} + +otError otPlatDiagRadioRawPowerSettingEnable(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + + otError error; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + + snprintf(cmd, sizeof(cmd), "rawpowersetting %s", aEnable ? "enable" : "disable"); + SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0)); + +exit: + return error; +} + +otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + + otError error; + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + + snprintf(cmd, sizeof(cmd), "cw %s", aEnable ? "start" : "stop"); + SuccessOrExit(error = sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0)); + +exit: + return error; } + +otError otPlatDiagRadioTransmitStream(otInstance *aInstance, bool aEnable) +{ + OT_UNUSED_VARIABLE(aInstance); + + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + + snprintf(cmd, sizeof(cmd), "stream %s", aEnable ? "start" : "stop"); + return sRadioSpinel.PlatDiagProcess(cmd, nullptr, 0); +} + +void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aFrame); + OT_UNUSED_VARIABLE(aError); +} + +void otPlatDiagAlarmCallback(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); } #endif // OPENTHREAD_CONFIG_DIAG_ENABLE uint32_t otPlatRadioGetSupportedChannelMask(otInstance *aInstance) @@ -578,7 +775,7 @@ otRadioState otPlatRadioGetState(otInstance *aInstance) return sRadioSpinel.GetState(); } -void otPlatRadioSetMacKey(otInstance * aInstance, +void otPlatRadioSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKeyId, const otMacKeyMaterial *aPrevKey, @@ -593,7 +790,13 @@ void otPlatRadioSetMacKey(otInstance * aInstance, void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter) { - SuccessOrDie(sRadioSpinel.SetMacFrameCounter(aMacFrameCounter)); + SuccessOrDie(sRadioSpinel.SetMacFrameCounter(aMacFrameCounter, /* aSetIfLarger */ false)); + OT_UNUSED_VARIABLE(aInstance); +} + +void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacFrameCounter) +{ + SuccessOrDie(sRadioSpinel.SetMacFrameCounter(aMacFrameCounter, /* aSetIfLarger */ true)); OT_UNUSED_VARIABLE(aInstance); } @@ -633,23 +836,56 @@ otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aCh return sRadioSpinel.SetChannelMaxTransmitPower(aChannel, aMaxPower); } +#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +otError otPlatRadioAddCalibratedPower(otInstance *aInstance, + uint8_t aChannel, + int16_t aActualPower, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength) +{ + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.AddCalibratedPower(aChannel, aActualPower, aRawPowerSetting, aRawPowerSettingLength); +} + +otError otPlatRadioClearCalibratedPowers(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.ClearCalibratedPowers(); +} + +otError otPlatRadioSetChannelTargetPower(otInstance *aInstance, uint8_t aChannel, int16_t aTargetPower) +{ + OT_UNUSED_VARIABLE(aInstance); + return sRadioSpinel.SetChannelTargetPower(aChannel, aTargetPower); +} +#endif + otError otPlatRadioSetRegion(otInstance *aInstance, uint16_t aRegionCode) { OT_UNUSED_VARIABLE(aInstance); +#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + return sPowerUpdater.SetRegion(aRegionCode); +#else return sRadioSpinel.SetRadioRegion(aRegionCode); +#endif } otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode) { OT_UNUSED_VARIABLE(aInstance); +#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + *aRegionCode = sPowerUpdater.GetRegion(); + return OT_ERROR_NONE; +#else return sRadioSpinel.GetRadioRegion(aRegionCode); +#endif } #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE -otError otPlatRadioConfigureEnhAckProbing(otInstance * aInstance, +otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance, otLinkMetrics aLinkMetrics, const otShortAddress aShortAddress, - const otExtAddress * aExtAddress) + const otExtAddress *aExtAddress) { OT_UNUSED_VARIABLE(aInstance); @@ -666,10 +902,7 @@ otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t a return OT_ERROR_NOT_IMPLEMENTED; } -const otRadioSpinelMetrics *otSysGetRadioSpinelMetrics(void) -{ - return sRadioSpinel.GetRadioSpinelMetrics(); -} +const otRadioSpinelMetrics *otSysGetRadioSpinelMetrics(void) { return sRadioSpinel.GetRadioSpinelMetrics(); } const otRcpInterfaceMetrics *otSysGetRcpInterfaceMetrics(void) { diff --git a/src/posix/platform/radio.hpp b/src/posix/platform/radio.hpp index 1312cb450e4..6a3d549567d 100644 --- a/src/posix/platform/radio.hpp +++ b/src/posix/platform/radio.hpp @@ -55,6 +55,14 @@ class Radio */ void Init(void); + /** + * This method acts as an accessor to the spinel instance used by the radio. + * + * @returns A pointer to the radio's spinel interface instance. + * + */ + static void *GetSpinelInstance(void); + private: RadioUrl mRadioUrl; }; diff --git a/src/posix/platform/radio_url.cpp b/src/posix/platform/radio_url.cpp index c126663b92a..e062ec90018 100644 --- a/src/posix/platform/radio_url.cpp +++ b/src/posix/platform/radio_url.cpp @@ -63,8 +63,7 @@ const char *otSysGetRadioUrlHelpString(void) " spi-small-packet=[n] Specify the smallest packet we can receive in a single transaction.\n" \ " (larger packets will require two transactions). Default value is 32.\n" -#else - +#elif OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_UART #define OT_RADIO_URL_HELP_BUS \ " forkpty-arg[=argument string] Command line arguments for subprocess, can be repeated.\n" \ " spinel+hdlc+uart://${PATH_TO_UART_DEVICE}?${Parameters} for real uart device\n" \ @@ -76,6 +75,14 @@ const char *otSysGetRadioUrlHelpString(void) " uart-flow-control Enable flow control, disabled by default.\n" \ " uart-reset Reset connection after hard resetting RCP(USB CDC ACM).\n" +#elif OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_VENDOR + +#ifndef OT_VENDOR_RADIO_URL_HELP_BUS +#define OT_VENDOR_RADIO_URL_HELP_BUS "\n" +#endif // OT_VENDOR_RADIO_URL_HELP_BUS + +#define OT_RADIO_URL_HELP_BUS OT_VENDOR_RADIO_URL_HELP_BUS + #endif // OPENTHREAD_POSIX_CONFIG_RCP_BUS == OT_POSIX_RCP_BUS_SPI #if OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE diff --git a/src/posix/platform/resolver.cpp b/src/posix/platform/resolver.cpp new file mode 100644 index 00000000000..85eda40c91e --- /dev/null +++ b/src/posix/platform/resolver.cpp @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2023, 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. + */ + +#include "resolver.hpp" + +#include "platform-posix.h" + +#include +#include +#include +#include +#include + +#include "common/code_utils.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + +namespace { +constexpr char kResolvConfFullPath[] = "/etc/resolv.conf"; +constexpr char kNameserverItem[] = "nameserver"; +} // namespace + +extern ot::Posix::Resolver gResolver; + +namespace ot { +namespace Posix { + +void Resolver::Init(void) +{ + memset(mUpstreamTransaction, 0, sizeof(mUpstreamTransaction)); + LoadDnsServerListFromConf(); +} + +void Resolver::TryRefreshDnsServerList(void) +{ + uint64_t now = otPlatTimeGet(); + + if (now > mUpstreamDnsServerListFreshness + kDnsServerListCacheTimeoutMs || + (mUpstreamDnsServerCount == 0 && now > mUpstreamDnsServerListFreshness + kDnsServerListNullCacheTimeoutMs)) + { + LoadDnsServerListFromConf(); + } +} + +void Resolver::LoadDnsServerListFromConf(void) +{ + std::string line; + std::ifstream fp; + + mUpstreamDnsServerCount = 0; + + fp.open(kResolvConfFullPath); + + while (fp.good() && std::getline(fp, line) && mUpstreamDnsServerCount < kMaxUpstreamServerCount) + { + if (line.find(kNameserverItem, 0) == 0) + { + in_addr_t addr; + + if (inet_pton(AF_INET, &line.c_str()[sizeof(kNameserverItem)], &addr) == 1) + { + otLogInfoPlat("Got nameserver #%d: %s", mUpstreamDnsServerCount, + &line.c_str()[sizeof(kNameserverItem)]); + mUpstreamDnsServerList[mUpstreamDnsServerCount] = addr; + mUpstreamDnsServerCount++; + } + } + } + + if (mUpstreamDnsServerCount == 0) + { + otLogCritPlat("No domain name servers found in %s, default to 127.0.0.1", kResolvConfFullPath); + } + + mUpstreamDnsServerListFreshness = otPlatTimeGet(); +} + +void Resolver::Query(otPlatDnsUpstreamQuery *aTxn, const otMessage *aQuery) +{ + char packet[kMaxDnsMessageSize]; + otError error = OT_ERROR_NONE; + uint16_t length = otMessageGetLength(aQuery); + sockaddr_in serverAddr; + + Transaction *txn = nullptr; + + VerifyOrExit(length <= kMaxDnsMessageSize, error = OT_ERROR_NO_BUFS); + VerifyOrExit(otMessageRead(aQuery, 0, &packet, sizeof(packet)) == length, error = OT_ERROR_NO_BUFS); + + txn = AllocateTransaction(aTxn); + VerifyOrExit(txn != nullptr, error = OT_ERROR_NO_BUFS); + + TryRefreshDnsServerList(); + + serverAddr.sin_family = AF_INET; + serverAddr.sin_port = htons(53); + for (int i = 0; i < mUpstreamDnsServerCount; i++) + { + serverAddr.sin_addr.s_addr = mUpstreamDnsServerList[i]; + VerifyOrExit( + sendto(txn->mUdpFd, packet, length, MSG_DONTWAIT, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) > 0, + error = OT_ERROR_NO_ROUTE); + } + otLogInfoPlat("Forwarded DNS query %p to %d server(s).", static_cast(aTxn), mUpstreamDnsServerCount); + +exit: + if (error != OT_ERROR_NONE) + { + otLogCritPlat("Failed to forward DNS query %p to server: %d", static_cast(aTxn), error); + } + return; +} + +void Resolver::Cancel(otPlatDnsUpstreamQuery *aTxn) +{ + Transaction *txn = GetTransaction(aTxn); + + if (txn != nullptr) + { + CloseTransaction(txn); + } + + otPlatDnsUpstreamQueryDone(gInstance, aTxn, nullptr); +} + +Resolver::Transaction *Resolver::AllocateTransaction(otPlatDnsUpstreamQuery *aThreadTxn) +{ + int fdOrError = 0; + Transaction *ret = nullptr; + + for (Transaction &txn : mUpstreamTransaction) + { + if (txn.mThreadTxn == nullptr) + { + fdOrError = socket(AF_INET, SOCK_DGRAM, 0); + if (fdOrError < 0) + { + otLogInfoPlat("Failed to create socket for upstream resolver: %d", fdOrError); + break; + } + ret = &txn; + ret->mUdpFd = fdOrError; + ret->mThreadTxn = aThreadTxn; + break; + } + } + + return ret; +} + +void Resolver::ForwardResponse(Transaction *aTxn) +{ + char response[kMaxDnsMessageSize]; + ssize_t readSize; + otError error = OT_ERROR_NONE; + otMessage *message = nullptr; + + VerifyOrExit((readSize = read(aTxn->mUdpFd, response, sizeof(response))) > 0); + + message = otUdpNewMessage(gInstance, nullptr); + VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS); + SuccessOrExit(error = otMessageAppend(message, response, readSize)); + + otPlatDnsUpstreamQueryDone(gInstance, aTxn->mThreadTxn, message); + message = nullptr; + +exit: + if (readSize < 0) + { + otLogInfoPlat("Failed to read response from upstream resolver socket: %d", errno); + } + if (error != OT_ERROR_NONE) + { + otLogInfoPlat("Failed to forward upstream DNS response: %s", otThreadErrorToString(error)); + } + if (message != nullptr) + { + otMessageFree(message); + } +} + +Resolver::Transaction *Resolver::GetTransaction(int aFd) +{ + Transaction *ret = nullptr; + + for (Transaction &txn : mUpstreamTransaction) + { + if (txn.mThreadTxn != nullptr && txn.mUdpFd == aFd) + { + ret = &txn; + break; + } + } + + return ret; +} + +Resolver::Transaction *Resolver::GetTransaction(otPlatDnsUpstreamQuery *aThreadTxn) +{ + Transaction *ret = nullptr; + + for (Transaction &txn : mUpstreamTransaction) + { + if (txn.mThreadTxn == aThreadTxn) + { + ret = &txn; + break; + } + } + + return ret; +} + +void Resolver::CloseTransaction(Transaction *aTxn) +{ + if (aTxn->mUdpFd >= 0) + { + close(aTxn->mUdpFd); + aTxn->mUdpFd = -1; + } + aTxn->mThreadTxn = nullptr; +} + +void Resolver::UpdateFdSet(fd_set *aReadFdSet, fd_set *aErrorFdSet, int *aMaxFd) +{ + for (Transaction &txn : mUpstreamTransaction) + { + if (txn.mThreadTxn != nullptr) + { + FD_SET(txn.mUdpFd, aReadFdSet); + FD_SET(txn.mUdpFd, aErrorFdSet); + if (txn.mUdpFd > *aMaxFd) + { + *aMaxFd = txn.mUdpFd; + } + } + } +} + +void Resolver::Process(const fd_set *aReadFdSet, const fd_set *aErrorFdSet) +{ + for (Transaction &txn : mUpstreamTransaction) + { + if (txn.mThreadTxn != nullptr) + { + // Note: On Linux, we can only get the error via read, so they should share the same logic. + if (FD_ISSET(txn.mUdpFd, aErrorFdSet) || FD_ISSET(txn.mUdpFd, aReadFdSet)) + { + ForwardResponse(&txn); + CloseTransaction(&txn); + } + } + } +} + +} // namespace Posix +} // namespace ot + +void otPlatDnsStartUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn, const otMessage *aQuery) +{ + OT_UNUSED_VARIABLE(aInstance); + + gResolver.Query(aTxn, aQuery); +} + +void otPlatDnsCancelUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn) +{ + OT_UNUSED_VARIABLE(aInstance); + + gResolver.Cancel(aTxn); +} + +#endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE diff --git a/src/posix/platform/resolver.hpp b/src/posix/platform/resolver.hpp new file mode 100644 index 00000000000..ea7da3bb456 --- /dev/null +++ b/src/posix/platform/resolver.hpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2023, 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. + */ + +#ifndef POSIX_PLATFORM_RESOLVER_HPP_ +#define POSIX_PLATFORM_RESOLVER_HPP_ + +#include + +#include +#include + +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + +namespace ot { +namespace Posix { + +class Resolver +{ +public: + constexpr static ssize_t kMaxDnsMessageSize = 512; + constexpr static ssize_t kMaxUpstreamTransactionCount = 16; + constexpr static ssize_t kMaxUpstreamServerCount = 3; + + /** + * This method initialize the upstream DNS resolver. + * + */ + void Init(void); + + /** + * Sends the query to the upstream. + * + * @param[in] aTxn A pointer to the OpenThread upstream DNS query transaction. + * @param[in] aQuery A pointer to a message for the payload of the DNS query. + * + */ + void Query(otPlatDnsUpstreamQuery *aTxn, const otMessage *aQuery); + + /** + * Cancels a upstream DNS query transaction. + * + * @param[in] aTxn A pointer to the OpenThread upstream DNS query transaction. + * + */ + void Cancel(otPlatDnsUpstreamQuery *aTxn); + + /** + * Updates the file descriptor sets with file descriptors used by the radio driver. + * + * @param[in,out] aReadFdSet A reference to the read file descriptors. + * @param[in,out] aErrorFdSet A reference to the error file descriptors. + * @param[in,out] aMaxFd A reference to the max file descriptor. + * @param[in,out] aTimeout A reference to the timeout. + * + */ + void UpdateFdSet(fd_set *aReadFdSet, fd_set *aErrorFdSet, int *aMaxFd); + + /** + * Handles the result of select. + * + * @param[in] aReadFdSet A reference to the read file descriptors. + * @param[in] aErrorFdSet A reference to the error file descriptors. + * + */ + void Process(const fd_set *aReadFdSet, const fd_set *aErrorFdSet); + +private: + static constexpr uint64_t kDnsServerListNullCacheTimeoutMs = 1 * 60 * 1000; // 1 minute + static constexpr uint64_t kDnsServerListCacheTimeoutMs = 10 * 60 * 1000; // 10 minutes + + struct Transaction + { + otPlatDnsUpstreamQuery *mThreadTxn; + int mUdpFd; + }; + + Transaction *GetTransaction(int aFd); + Transaction *GetTransaction(otPlatDnsUpstreamQuery *aThreadTxn); + Transaction *AllocateTransaction(otPlatDnsUpstreamQuery *aThreadTxn); + + void ForwardResponse(Transaction *aTxn); + void CloseTransaction(Transaction *aTxn); + void FinishTransaction(int aFd); + void TryRefreshDnsServerList(void); + void LoadDnsServerListFromConf(void); + + int mUpstreamDnsServerCount = 0; + in_addr_t mUpstreamDnsServerList[kMaxUpstreamServerCount]; + uint64_t mUpstreamDnsServerListFreshness = 0; + + Transaction mUpstreamTransaction[kMaxUpstreamTransactionCount]; +}; + +} // namespace Posix +} // namespace ot + +#endif // OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE + +#endif // POSIX_PLATFORM_RESOLVER_HPP_ diff --git a/src/posix/platform/settings.cpp b/src/posix/platform/settings.cpp index 7ea03abf3d9..f807ebc0864 100644 --- a/src/posix/platform/settings.cpp +++ b/src/posix/platform/settings.cpp @@ -525,10 +525,7 @@ void PlatformSettingsGetSensitiveKeys(otInstance *aInstance, const uint16_t **aK #if SELF_TEST -void otLogCritPlat(const char *aFormat, ...) -{ - OT_UNUSED_VARIABLE(aFormat); -} +void otLogCritPlat(const char *aFormat, ...) { OT_UNUSED_VARIABLE(aFormat); } const char *otExitCodeToString(uint8_t aExitCode) { @@ -544,10 +541,7 @@ void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) } // Stub implementation for testing -bool IsSystemDryRun(void) -{ - return false; -} +bool IsSystemDryRun(void) { return false; } int main() { diff --git a/src/posix/platform/spi_interface.cpp b/src/posix/platform/spi_interface.cpp index 33f6d0cacce..d3bf52e3628 100644 --- a/src/posix/platform/spi_interface.cpp +++ b/src/posix/platform/spi_interface.cpp @@ -65,8 +65,8 @@ namespace ot { namespace Posix { SpiInterface::SpiInterface(SpinelInterface::ReceiveFrameCallback aCallback, - void * aCallbackContext, - SpinelInterface::RxFrameBuffer & aFrameBuffer) + void *aCallbackContext, + SpinelInterface::RxFrameBuffer &aFrameBuffer) : mReceiveFrameCallback(aCallback) , mReceiveFrameContext(aCallbackContext) , mRxFrameBuffer(aFrameBuffer) @@ -85,7 +85,7 @@ SpiInterface::SpiInterface(SpinelInterface::ReceiveFrameCallback aCallback, { } -void SpiInterface::OnRcpReset(void) +void SpiInterface::ResetStates(void) { mSpiTxIsReady = false; mSpiTxRefusedCount = 0; @@ -95,9 +95,20 @@ void SpiInterface::OnRcpReset(void) memset(mSpiTxFrameBuffer, 0, sizeof(mSpiTxFrameBuffer)); memset(&mInterfaceMetrics, 0, sizeof(mInterfaceMetrics)); mInterfaceMetrics.mRcpInterfaceType = OT_POSIX_RCP_BUS_SPI; +} +otError SpiInterface::HardwareReset(void) +{ + ResetStates(); TriggerReset(); + + // If the `INT` pin is set to low during the restart of the RCP chip, which triggers continuous invalid SPI + // transactions by the host, it will cause the function `PushPullSpi()` to output lots of invalid warn log + // messages. Adding the delay here is used to wait for the RCP chip starts up to avoid outputing invalid + // log messages. usleep(static_cast(mSpiResetDelay) * kUsecPerMsec); + + return OT_ERROR_NONE; } otError SpiInterface::Init(const Url::Url &aRadioUrl) @@ -182,19 +193,10 @@ otError SpiInterface::Init(const Url::Url &aRadioUrl) InitResetPin(spiGpioResetDevice, spiGpioResetLine); InitSpiDev(aRadioUrl.GetPath(), spiMode, spiSpeed); - // Reset RCP chip. - TriggerReset(); - - // Waiting for the RCP chip starts up. - usleep(static_cast(spiResetDelay) * kUsecPerMsec); - return OT_ERROR_NONE; } -SpiInterface::~SpiInterface(void) -{ - Deinit(); -} +SpiInterface::~SpiInterface(void) { Deinit(); } void SpiInterface::Deinit(void) { @@ -343,7 +345,7 @@ void SpiInterface::TriggerReset(void) uint8_t *SpiInterface::GetRealRxFrameStart(uint8_t *aSpiRxFrameBuffer, uint8_t aAlignAllowance, uint16_t &aSkipLength) { - uint8_t * start = aSpiRxFrameBuffer; + uint8_t *start = aSpiRxFrameBuffer; const uint8_t *end = aSpiRxFrameBuffer + aAlignAllowance; for (; start != end && start[0] == 0xff; start++) @@ -408,8 +410,8 @@ otError SpiInterface::PushPullSpi(void) uint16_t spiTransferBytes = 0; uint8_t successfulExchanges = 0; bool discardRxFrame = true; - uint8_t * spiRxFrameBuffer; - uint8_t * spiRxFrame; + uint8_t *spiRxFrameBuffer; + uint8_t *spiRxFrame; uint8_t slaveHeader; uint16_t slaveAcceptLen; Ncp::SpiFrame txFrame(mSpiTxFrameBuffer); @@ -725,10 +727,7 @@ void SpiInterface::UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMa } } -void SpiInterface::Process(const RadioProcessContext &aContext) -{ - Process(aContext.mReadFdSet, aContext.mWriteFdSet); -} +void SpiInterface::Process(const RadioProcessContext &aContext) { Process(aContext.mReadFdSet, aContext.mWriteFdSet); } void SpiInterface::Process(const fd_set *aReadFdSet, const fd_set *aWriteFdSet) { @@ -806,6 +805,12 @@ otError SpiInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength) otError error = OT_ERROR_NONE; VerifyOrExit(aLength < (kMaxFrameSize - kSpiFrameHeaderSize), error = OT_ERROR_NO_BUFS); + + if (ot::Spinel::SpinelInterface::IsSpinelResetCommand(aFrame, aLength)) + { + ResetStates(); + } + VerifyOrExit(!mSpiTxIsReady, error = OT_ERROR_BUSY); memcpy(&mSpiTxFrameBuffer[kSpiFrameHeaderSize], aFrame, aLength); diff --git a/src/posix/platform/spi_interface.hpp b/src/posix/platform/spi_interface.hpp index b2e8a026868..0f3242e35e6 100644 --- a/src/posix/platform/spi_interface.hpp +++ b/src/posix/platform/spi_interface.hpp @@ -65,8 +65,8 @@ class SpiInterface * */ SpiInterface(Spinel::SpinelInterface::ReceiveFrameCallback aCallback, - void * aCallbackContext, - Spinel::SpinelInterface::RxFrameBuffer & aFrameBuffer); + void *aCallbackContext, + Spinel::SpinelInterface::RxFrameBuffer &aFrameBuffer); /** * This destructor deinitializes the object. @@ -147,17 +147,13 @@ class SpiInterface uint32_t GetBusSpeed(void) const { return ((mSpiDevFd >= 0) ? mSpiSpeedHz : 0); } /** - * This method is called when RCP failure detected and resets internal states of the interface. + * This method hardware resets the RCP. * - */ - void OnRcpReset(void); - - /** - * This method is called when RCP is reset to recreate the connection with it. - * Intentionally empty. + * @retval OT_ERROR_NONE Successfully reset the RCP. + * @retval OT_ERROR_NOT_IMPLEMENT The hardware reset is not implemented. * */ - otError ResetConnection(void) { return OT_ERROR_NONE; } + otError HardwareReset(void); /** * This method returns the RCP interface metrics. @@ -168,6 +164,7 @@ class SpiInterface const otRcpInterfaceMetrics *GetRcpInterfaceMetrics(void) const { return &mInterfaceMetrics; } private: + void ResetStates(void); int SetupGpioHandle(int aFd, uint8_t aLine, uint32_t aHandleFlags, const char *aLabel); int SetupGpioEvent(int aFd, uint8_t aLine, uint32_t aHandleFlags, uint32_t aEventFlags, const char *aLabel); void SetGpioValue(int aFd, uint8_t aValue); @@ -221,8 +218,8 @@ class SpiInterface }; Spinel::SpinelInterface::ReceiveFrameCallback mReceiveFrameCallback; - void * mReceiveFrameContext; - Spinel::SpinelInterface::RxFrameBuffer & mRxFrameBuffer; + void *mReceiveFrameContext; + Spinel::SpinelInterface::RxFrameBuffer &mRxFrameBuffer; int mSpiDevFd; int mResetGpioValueFd; diff --git a/src/posix/platform/system.cpp b/src/posix/platform/system.cpp index c3e176abe7d..6f11db7277f 100644 --- a/src/posix/platform/system.cpp +++ b/src/posix/platform/system.cpp @@ -36,6 +36,7 @@ #include "platform-posix.h" #include +#include #include #include @@ -122,6 +123,10 @@ static const char *getTrelRadioUrl(otPlatformConfig *aPlatformConfig) void platformInit(otPlatformConfig *aPlatformConfig) { +#if OPENTHREAD_POSIX_CONFIG_BACKTRACE_ENABLE + platformBacktraceInit(); +#endif + platformAlarmInit(aPlatformConfig->mSpeedUpFactor, aPlatformConfig->mRealTimeSignal); platformRadioInit(get802154RadioUrl(aPlatformConfig)); @@ -137,14 +142,21 @@ void platformInit(otPlatformConfig *aPlatformConfig) platformBackboneInit(aPlatformConfig->mBackboneInterfaceName); #endif -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE ot::Posix::InfraNetif::Get().Init(aPlatformConfig->mBackboneInterfaceName); #endif gNetifName[0] = '\0'; +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + if (otIp4CidrFromString(OPENTHREAD_POSIX_CONFIG_NAT64_CIDR, &gNat64Cidr) != OT_ERROR_NONE) + { + gNat64Cidr.mLength = 0; + } +#endif + #if OPENTHREAD_CONFIG_PLATFORM_NETIF_ENABLE - platformNetifInit(aPlatformConfig->mInterfaceName); + platformNetifInit(aPlatformConfig); #endif #if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE @@ -167,7 +179,7 @@ void platformSetUp(void) platformBackboneSetUp(); #endif -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE ot::Posix::InfraNetif::Get().SetUp(); #endif @@ -222,7 +234,7 @@ void platformTearDown(void) platformNetifTearDown(); #endif -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE ot::Posix::InfraNetif::Get().TearDown(); #endif @@ -254,7 +266,7 @@ void platformDeinit(void) platformTrelDeinit(); #endif -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE ot::Posix::InfraNetif::Get().Deinit(); #endif @@ -396,7 +408,4 @@ void otSysMainloopProcess(otInstance *aInstance, const otSysMainloopContext *aMa #endif } -bool IsSystemDryRun(void) -{ - return gDryRun; -} +bool IsSystemDryRun(void) { return gDryRun; } diff --git a/src/posix/platform/trel.cpp b/src/posix/platform/trel.cpp index f3ec6cf59a8..4b7cd50e302 100644 --- a/src/posix/platform/trel.cpp +++ b/src/posix/platform/trel.cpp @@ -85,8 +85,8 @@ static const char *BufferToString(const uint8_t *aBuffer, uint16_t aLength) static char string[1600]; uint16_t num = 0; - char * cur = &string[0]; - char * end = &string[sizeof(string) - 1]; + char *cur = &string[0]; + char *end = &string[sizeof(string) - 1]; cur += snprintf(cur, (uint16_t)(end - cur), "[(len:%d) ", aLength); VerifyOrExit(cur < end); @@ -449,8 +449,8 @@ void otPlatTrelDisable(otInstance *aInstance) return; } -void otPlatTrelSend(otInstance * aInstance, - const uint8_t * aUdpPayload, +void otPlatTrelSend(otInstance *aInstance, + const uint8_t *aUdpPayload, uint16_t aUdpPayloadLen, const otSockAddr *aDestSockAddr) { diff --git a/src/posix/platform/udp.cpp b/src/posix/platform/udp.cpp index b7aacc5fadc..7f78acccbba 100644 --- a/src/posix/platform/udp.cpp +++ b/src/posix/platform/udp.cpp @@ -65,25 +65,13 @@ namespace { constexpr size_t kMaxUdpSize = 1280; -void *FdToHandle(int aFd) -{ - return reinterpret_cast(aFd); -} +void *FdToHandle(int aFd) { return reinterpret_cast(aFd); } -int FdFromHandle(void *aHandle) -{ - return static_cast(reinterpret_cast(aHandle)); -} +int FdFromHandle(void *aHandle) { return static_cast(reinterpret_cast(aHandle)); } -bool IsLinkLocal(const struct in6_addr &aAddress) -{ - return aAddress.s6_addr[0] == 0xfe && aAddress.s6_addr[1] == 0x80; -} +bool IsLinkLocal(const struct in6_addr &aAddress) { return aAddress.s6_addr[0] == 0xfe && aAddress.s6_addr[1] == 0x80; } -bool IsMulticast(const otIp6Address &aAddress) -{ - return aAddress.mFields.m8[0] == 0xff; -} +bool IsMulticast(const otIp6Address &aAddress) { return aAddress.mFields.m8[0] == 0xff; } otError transmitPacket(int aFd, uint8_t *aPayload, uint16_t aLength, const otMessageInfo &aMessageInfo) { @@ -98,7 +86,7 @@ otError transmitPacket(int aFd, uint8_t *aPayload, uint16_t aLength, const otMes size_t controlLength = 0; struct iovec iov; struct msghdr msg; - struct cmsghdr * cmsg; + struct cmsghdr *cmsg; ssize_t rval; otError error = OT_ERROR_NONE; @@ -317,7 +305,7 @@ otError otPlatUdpBindToNetif(otUdpSocket *aUdpSocket, otNetifIdentifier aNetifId #else // __NetBSD__ || __FreeBSD__ || __APPLE__ unsigned int netifIndex = 0; VerifyOrExit(setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF, &netifIndex, sizeof(netifIndex)) == 0, - error = OT_ERROR_FAILED); + error = OT_ERROR_FAILED); #endif // __linux__ break; } @@ -328,7 +316,7 @@ otError otPlatUdpBindToNetif(otUdpSocket *aUdpSocket, otNetifIdentifier aNetifId error = OT_ERROR_FAILED); #else // __NetBSD__ || __FreeBSD__ || __APPLE__ VerifyOrExit(setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF, &gNetifIndex, sizeof(gNetifIndex)) == 0, - error = OT_ERROR_FAILED); + error = OT_ERROR_FAILED); #endif // __linux__ break; } @@ -388,8 +376,8 @@ otError otPlatUdpConnect(otUdpSocket *aUdpSocket) if (getsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &netifName, &len) != 0) { - otLogWarnPlat("Failed to read socket bound device: %s", strerror(errno)); - len = 0; + otLogWarnPlat("Failed to read socket bound device: %s", strerror(errno)); + len = 0; } // There is a bug in linux that connecting to AF_UNSPEC does not disconnect. @@ -400,11 +388,11 @@ otError otPlatUdpConnect(otUdpSocket *aUdpSocket) if (len > 0 && netifName[0] != '\0') { - fd = FdFromHandle(aUdpSocket->mHandle); - VerifyOrExit(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &netifName, len) == 0, { - otLogWarnPlat("Failed to bind to device: %s", strerror(errno)); - error = OT_ERROR_FAILED; - }); + fd = FdFromHandle(aUdpSocket->mHandle); + VerifyOrExit(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &netifName, len) == 0, { + otLogWarnPlat("Failed to bind to device: %s", strerror(errno)); + error = OT_ERROR_FAILED; + }); } ExitNow(); @@ -463,7 +451,7 @@ otError otPlatUdpSend(otUdpSocket *aUdpSocket, otMessage *aMessage, const otMess return error; } -otError otPlatUdpJoinMulticastGroup(otUdpSocket * aUdpSocket, +otError otPlatUdpJoinMulticastGroup(otUdpSocket *aUdpSocket, otNetifIdentifier aNetifIdentifier, const otIp6Address *aAddress) { @@ -503,7 +491,7 @@ otError otPlatUdpJoinMulticastGroup(otUdpSocket * aUdpSocket, return error; } -otError otPlatUdpLeaveMulticastGroup(otUdpSocket * aUdpSocket, +otError otPlatUdpLeaveMulticastGroup(otUdpSocket *aUdpSocket, otNetifIdentifier aNetifIdentifier, const otIp6Address *aAddress) { @@ -591,15 +579,9 @@ void Udp::Init(const char *aIfName) assert(gNetifIndex != 0); } -void Udp::SetUp(void) -{ - Mainloop::Manager::Get().Add(*this); -} +void Udp::SetUp(void) { Mainloop::Manager::Get().Add(*this); } -void Udp::TearDown(void) -{ - Mainloop::Manager::Get().Remove(*this); -} +void Udp::TearDown(void) { Mainloop::Manager::Get().Remove(*this); } void Udp::Deinit(void) { @@ -624,7 +606,7 @@ void Udp::Process(const otSysMainloopContext &aContext) if (fd > 0 && FD_ISSET(fd, &aContext.mReadFdSet)) { otMessageInfo messageInfo; - otMessage * message = nullptr; + otMessage *message = nullptr; uint8_t payload[kMaxUdpSize]; uint16_t length = sizeof(payload); diff --git a/src/posix/platform/utils.cpp b/src/posix/platform/utils.cpp index e4974042929..8183ac3916c 100644 --- a/src/posix/platform/utils.cpp +++ b/src/posix/platform/utils.cpp @@ -55,7 +55,7 @@ otError ExecuteCommand(const char *aFormat, ...) char cmd[kSystemCommandMaxLength]; char buf[kOutputBufferSize]; va_list args; - FILE * file; + FILE *file; int exitCode; otError error = OT_ERROR_NONE; diff --git a/examples/platforms/cc2538/CMakeLists.txt b/src/posix/platform/vendor.cmake similarity index 54% rename from examples/platforms/cc2538/CMakeLists.txt rename to src/posix/platform/vendor.cmake index f8ed5f212c5..b8f46d585fb 100644 --- a/examples/platforms/cc2538/CMakeLists.txt +++ b/src/posix/platform/vendor.cmake @@ -1,5 +1,5 @@ # -# Copyright (c) 2019, The OpenThread Authors. +# Copyright (c) 2023, The OpenThread Authors. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -26,54 +26,35 @@ # POSSIBILITY OF SUCH DAMAGE. # -set(OT_PLATFORM_LIB "openthread-cc2538" PARENT_SCOPE) +set(OT_POSIX_CONFIG_RCP_VENDOR_INTERFACE "vendor_interface_example.cpp" + CACHE STRING "vendor interface implementation") -if(NOT OT_CONFIG) - set(OT_CONFIG "openthread-core-cc2538-config.h") - set(OT_CONFIG ${OT_CONFIG} PARENT_SCOPE) -endif() - -list(APPEND OT_PLATFORM_DEFINES - "OPENTHREAD_CORE_CONFIG_PLATFORM_CHECK_FILE=\"openthread-core-cc2538-config-check.h\"" - "OPENTHREAD_CONFIG_NCP_HDLC_ENABLE=1" -) -set(OT_PLATFORM_DEFINES ${OT_PLATFORM_DEFINES} PARENT_SCOPE) +set(OT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE "" CACHE STRING + "name of optional external package to link to rcp vendor implementation") -list(APPEND OT_PLATFORM_DEFINES "OPENTHREAD_PROJECT_CORE_CONFIG_FILE=\"${OT_CONFIG}\"") +if(OT_POSIX_CONFIG_RCP_BUS STREQUAL "VENDOR") + add_library(rcp-vendor-intf ${OT_POSIX_CONFIG_RCP_VENDOR_INTERFACE}) -add_library(openthread-cc2538 - alarm.c - diag.c - entropy.c - flash.c - misc.c - radio.c - startup-gcc.c - system.c - logging.c - uart.c - $ -) + target_link_libraries(rcp-vendor-intf PUBLIC ot-posix-config) -target_link_libraries(openthread-cc2538 - PRIVATE - ot-config - PUBLIC - -T${PROJECT_SOURCE_DIR}/examples/platforms/cc2538/cc2538.ld - -Wl,--gc-sections -Wl,-Map=$.map -) + target_include_directories(rcp-vendor-intf + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + PRIVATE + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/src + ${PROJECT_SOURCE_DIR}/src/core + ${PROJECT_SOURCE_DIR}/src/posix/platform/include + ) -target_compile_definitions(openthread-cc2538 - PUBLIC - ${OT_PLATFORM_DEFINES} -) + target_link_libraries(openthread-posix PUBLIC rcp-vendor-intf) -target_compile_options(openthread-cc2538 PRIVATE - ${OT_CFLAGS} -) + if (OT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE) + set(DEPS_TARGET ${OT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE}::${OT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE}) + find_package(${OT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE}) -target_include_directories(openthread-cc2538 PRIVATE - ${OT_PUBLIC_INCLUDES} - ${PROJECT_SOURCE_DIR}/examples/platforms - ${PROJECT_SOURCE_DIR}/src/core -) + if(${OT_POSIX_CONFIG_RCP_VENDOR_DEPS_PACKAGE}_FOUND) + target_link_libraries(rcp-vendor-intf PUBLIC ${DEPS_TARGET}) + endif() + endif() +endif() diff --git a/src/posix/platform/vendor_interface.hpp b/src/posix/platform/vendor_interface.hpp index 7ece23487a8..8c82c204d29 100644 --- a/src/posix/platform/vendor_interface.hpp +++ b/src/posix/platform/vendor_interface.hpp @@ -62,8 +62,8 @@ class VendorInterface * */ VendorInterface(Spinel::SpinelInterface::ReceiveFrameCallback aCallback, - void * aCallbackContext, - Spinel::SpinelInterface::RxFrameBuffer & aFrameBuffer); + void *aCallbackContext, + Spinel::SpinelInterface::RxFrameBuffer &aFrameBuffer); /** * This destructor deinitializes the object. @@ -119,10 +119,10 @@ class VendorInterface /** * This method updates the file descriptor sets with file descriptors used by the radio driver. * - * @param[inout] aReadFdSet A reference to the read file descriptors. - * @param[inout] aWriteFdSet A reference to the write file descriptors. - * @param[inout] aMaxFd A reference to the max file descriptor. - * @param[inout] aTimeout A reference to the timeout. + * @param[in,out] aReadFdSet A reference to the read file descriptors. + * @param[in,out] aWriteFdSet A reference to the write file descriptors. + * @param[in,out] aMaxFd A reference to the max file descriptor. + * @param[in,out] aTimeout A reference to the timeout. * */ void UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout); @@ -144,19 +144,13 @@ class VendorInterface uint32_t GetBusSpeed(void) const; /** - * This method is called when RCP failure detected and resets internal states of the interface. + * This method hardware resets the RCP. * - */ - void OnRcpReset(void); - - /** - * This method is called when RCP is reset to recreate the connection with it. - * - * @retval OT_ERROR_NONE Reset the connection successfully. - * @retval OT_ERROR_FAILED Failed to reset the connection. + * @retval OT_ERROR_NONE Successfully reset the RCP. + * @retval OT_ERROR_NOT_IMPLEMENT The hardware reset is not implemented. * */ - otError ResetConnection(void); + otError HardwareReset(void); /** * This method returns the RCP interface metrics. diff --git a/src/posix/platform/vendor_interface_example.cpp b/src/posix/platform/vendor_interface_example.cpp index 12121d398d4..ebf6b786903 100644 --- a/src/posix/platform/vendor_interface_example.cpp +++ b/src/posix/platform/vendor_interface_example.cpp @@ -50,8 +50,8 @@ class VendorInterfaceImpl { public: explicit VendorInterfaceImpl(SpinelInterface::ReceiveFrameCallback aCallback, - void * aCallbackContext, - SpinelInterface::RxFrameBuffer & aFrameBuffer) + void *aCallbackContext, + SpinelInterface::RxFrameBuffer &aFrameBuffer) : mReceiveFrameCallback(aCallback) , mReceiveFrameContext(aCallbackContext) , mRxFrameBuffer(aFrameBuffer) @@ -65,8 +65,8 @@ class VendorInterfaceImpl private: SpinelInterface::ReceiveFrameCallback mReceiveFrameCallback; - void * mReceiveFrameContext; - SpinelInterface::RxFrameBuffer & mRxFrameBuffer; + void *mReceiveFrameContext; + SpinelInterface::RxFrameBuffer &mRxFrameBuffer; }; // ---------------------------------------------------------------------------- @@ -76,17 +76,14 @@ class VendorInterfaceImpl static OT_DEFINE_ALIGNED_VAR(sVendorInterfaceImplRaw, sizeof(VendorInterfaceImpl), uint64_t); VendorInterface::VendorInterface(SpinelInterface::ReceiveFrameCallback aCallback, - void * aCallbackContext, - SpinelInterface::RxFrameBuffer & aFrameBuffer) + void *aCallbackContext, + SpinelInterface::RxFrameBuffer &aFrameBuffer) { new (&sVendorInterfaceImplRaw) VendorInterfaceImpl(aCallback, aCallbackContext, aFrameBuffer); OT_UNUSED_VARIABLE(sVendorInterfaceImplRaw); } -VendorInterface::~VendorInterface(void) -{ - Deinit(); -} +VendorInterface::~VendorInterface(void) { Deinit(); } otError VendorInterface::Init(const Url::Url &aRadioUrl) { @@ -102,14 +99,13 @@ void VendorInterface::Deinit(void) // TODO: Implement vendor code here. } -uint32_t VendorInterface::GetBusSpeed(void) const -{ - return 1000000; -} +uint32_t VendorInterface::GetBusSpeed(void) const { return 1000000; } -void VendorInterface::OnRcpReset(void) +otError VendorInterface::HardwareReset(void) { // TODO: Implement vendor code here. + + return OT_ERROR_NOT_IMPLEMENTED; } void VendorInterface::UpdateFdSet(fd_set &aReadFdSet, fd_set &aWriteFdSet, int &aMaxFd, struct timeval &aTimeout) @@ -148,13 +144,6 @@ otError VendorInterface::SendFrame(const uint8_t *aFrame, uint16_t aLength) return OT_ERROR_NONE; } -otError VendorInterface::ResetConnection(void) -{ - // TODO: Implement vendor code here. - - return OT_ERROR_NONE; -} - const otRcpInterfaceMetrics *VendorInterface::GetRcpInterfaceMetrics(void) { // TODO: Implement vendor code here. diff --git a/src/posix/platform/virtual_time.cpp b/src/posix/platform/virtual_time.cpp index b6faf38a1e6..963c1d9f112 100644 --- a/src/posix/platform/virtual_time.cpp +++ b/src/posix/platform/virtual_time.cpp @@ -55,7 +55,7 @@ static uint16_t sPortOffset = 0; ///< Port offset for simulation. void virtualTimeInit(uint16_t aNodeId) { struct sockaddr_in sockaddr; - char * offset; + char *offset; memset(&sockaddr, 0, sizeof(sockaddr)); sockaddr.sin_family = AF_INET; @@ -159,10 +159,10 @@ void virtualTimeSendRadioSpinelWriteEvent(const uint8_t *aData, uint16_t aLength virtualTimeSendEvent(&event, offsetof(struct VirtualTimeEvent, mData) + event.mDataLength); } -void virtualTimeUpdateFdSet(fd_set * aReadFdSet, - fd_set * aWriteFdSet, - fd_set * aErrorFdSet, - int * aMaxFd, +void virtualTimeUpdateFdSet(fd_set *aReadFdSet, + fd_set *aWriteFdSet, + fd_set *aErrorFdSet, + int *aMaxFd, struct timeval *aTimeout) { OT_UNUSED_VARIABLE(aWriteFdSet); @@ -176,7 +176,7 @@ void virtualTimeUpdateFdSet(fd_set * aReadFdSet, } } -void virtualTimeProcess(otInstance * aInstance, +void virtualTimeProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set *aWriteFdSet, const fd_set *aErrorFdSet) @@ -197,9 +197,6 @@ void virtualTimeProcess(otInstance * aInstance, virtualTimeRadioSpinelProcess(aInstance, &event); } -uint64_t otPlatTimeGet(void) -{ - return sNow; -} +uint64_t otPlatTimeGet(void) { return sNow; } #endif // OPENTHREAD_POSIX_VIRTUAL_TIME diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2b00f1c86ef..84053764755 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -26,10 +26,8 @@ # POSSIBILITY OF SUCH DAMAGE. # -if(OT_PLATFORM STREQUAL "simulation") - if(OT_FTD) - add_subdirectory(unit) - endif() +if(OT_FTD AND BUILD_TESTING) + add_subdirectory(unit) endif() option(OT_FUZZ_TARGETS "enable fuzz targets" OFF) diff --git a/tests/Makefile.am b/tests/Makefile.am index 7d0459ca720..462f908b166 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -31,8 +31,6 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am # Always package (e.g. for 'make dist') these subdirectories. DIST_SUBDIRS = \ - unit \ - scripts \ fuzz \ $(NULL) @@ -41,49 +39,8 @@ DIST_SUBDIRS = \ SUBDIRS = \ $(NULL) -SUBDIRS += \ - unit \ - $(NULL) - -if OPENTHREAD_POSIX -if OPENTHREAD_ENABLE_CLI -SUBDIRS += \ - scripts \ - $(NULL) -endif -endif - if OPENTHREAD_ENABLE_FUZZ_TARGETS SUBDIRS += fuzz endif -if OPENTHREAD_BUILD_TESTS -if OPENTHREAD_BUILD_COVERAGE -CLEANFILES = $(wildcard *.gcda *.gcno) - -if OPENTHREAD_BUILD_COVERAGE_REPORTS -# The bundle should positively be qualified with the absolute build -# path. Otherwise, VPATH will get auto-prefixed to it if there is -# already such a directory in the non-colocated source tree. - -OPENTHREAD_COVERAGE_BUNDLE = ${abs_builddir}/${PACKAGE}${NL_COVERAGE_BUNDLE_SUFFIX} -OPENTHREAD_COVERAGE_INFO = ${OPENTHREAD_COVERAGE_BUNDLE}/${PACKAGE}${NL_COVERAGE_INFO_SUFFIX} - -$(OPENTHREAD_COVERAGE_BUNDLE): - $(call create-directory) - -$(OPENTHREAD_COVERAGE_INFO): check | $(dir $(OPENTHREAD_COVERAGE_INFO)) - $(call generate-coverage-report,${top_builddir}) - -coverage-local: $(OPENTHREAD_COVERAGE_INFO) - -clean-local: clean-local-coverage - -.PHONY: clean-local-coverage -clean-local-coverage: - -$(AM_V_at)rm -rf $(OPENTHREAD_COVERAGE_BUNDLE) -endif # OPENTHREAD_BUILD_COVERAGE_REPORTS -endif # OPENTHREAD_BUILD_COVERAGE -endif # OPENTHREAD_BUILD_TESTS - include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/tests/fuzz/cli_received.cpp b/tests/fuzz/cli_received.cpp index 2dcbbd8ee0a..a8a2a10e3f6 100644 --- a/tests/fuzz/cli_received.cpp +++ b/tests/fuzz/cli_received.cpp @@ -26,8 +26,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#define MAX_ITERATIONS 100 - #include #include #include @@ -40,9 +38,11 @@ #include #include #include +#include #include "fuzzer_platform.h" #include "common/code_utils.hpp" +#include "common/time.hpp" static int CliOutput(void *aContext, const char *aFormat, va_list aArguments) { @@ -53,12 +53,27 @@ static int CliOutput(void *aContext, const char *aFormat, va_list aArguments) return vsnprintf(nullptr, 0, aFormat, aArguments); } +void AdvanceTime(otInstance *aInstance, uint32_t aDuration) +{ + uint32_t time = otPlatAlarmMilliGetNow() + aDuration; + + while (ot::TimeMilli(otPlatAlarmMilliGetNow()) <= ot::TimeMilli(time)) + { + while (otTaskletsArePending(aInstance)) + { + otTaskletsProcess(aInstance); + } + + FuzzerPlatformProcess(aInstance); + } +} + extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { const otPanId panId = 0xdead; otInstance *instance = nullptr; - uint8_t * buf = nullptr; + uint8_t *buf = nullptr; VerifyOrExit(size <= 65536); @@ -71,8 +86,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) IgnoreError(otThreadSetEnabled(instance, true)); IgnoreError(otThreadBecomeLeader(instance)); - buf = static_cast(malloc(size + 1)); + AdvanceTime(instance, 10000); + buf = static_cast(malloc(size + 1)); memcpy(buf, data, size); buf[size] = '\0'; @@ -80,15 +96,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) VerifyOrExit(!FuzzerPlatformResetWasRequested()); - for (int i = 0; i < MAX_ITERATIONS; i++) - { - while (otTaskletsArePending(instance)) - { - otTaskletsProcess(instance); - } - - FuzzerPlatformProcess(instance); - } + AdvanceTime(instance, 10000); exit: diff --git a/tests/fuzz/fuzzer_platform.cpp b/tests/fuzz/fuzzer_platform.cpp index 74499377e47..012cee49b8b 100644 --- a/tests/fuzz/fuzzer_platform.cpp +++ b/tests/fuzz/fuzzer_platform.cpp @@ -144,15 +144,9 @@ void FuzzerPlatformProcess(otInstance *aInstance) } } -bool FuzzerPlatformResetWasRequested(void) -{ - return sResetWasRequested; -} +bool FuzzerPlatformResetWasRequested(void) { return sResetWasRequested; } -uint32_t otPlatAlarmMilliGetNow(void) -{ - return sAlarmNow / 1000; -} +uint32_t otPlatAlarmMilliGetNow(void) { return sAlarmNow / 1000; } void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) { @@ -169,10 +163,7 @@ void otPlatAlarmMilliStop(otInstance *aInstance) sAlarmMilli.isRunning = false; } -uint32_t otPlatAlarmMicroGetNow(void) -{ - return sAlarmNow; -} +uint32_t otPlatAlarmMicroGetNow(void) { return sAlarmNow; } void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) { @@ -207,12 +198,14 @@ otError otDiagProcessCmd(otInstance *aInstance, uint8_t aArgsLength, char *aArgs return OT_ERROR_NOT_IMPLEMENTED; } -void otDiagProcessCmdLine(otInstance *aInstance, const char *aString, char *aOutput, size_t aOutputMaxLen) +otError otDiagProcessCmdLine(otInstance *aInstance, const char *aString, char *aOutput, size_t aOutputMaxLen) { OT_UNUSED_VARIABLE(aInstance); OT_UNUSED_VARIABLE(aString); OT_UNUSED_VARIABLE(aOutput); OT_UNUSED_VARIABLE(aOutputMaxLen); + + return OT_ERROR_NOT_IMPLEMENTED; } void otPlatReset(otInstance *aInstance) @@ -228,16 +221,14 @@ otPlatResetReason otPlatGetResetReason(otInstance *aInstance) return OT_PLAT_RESET_REASON_POWER_ON; } -void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) +OT_TOOL_WEAK void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) { OT_UNUSED_VARIABLE(aLogLevel); OT_UNUSED_VARIABLE(aLogRegion); OT_UNUSED_VARIABLE(aFormat); } -void otPlatWakeHost(void) -{ -} +void otPlatWakeHost(void) {} void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) { @@ -386,15 +377,9 @@ otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddre return OT_ERROR_NONE; } -void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} +void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); } -void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} +void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); } otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration) { @@ -448,10 +433,7 @@ void otPlatSettingsInit(otInstance *aInstance, const uint16_t *aSensitiveKeys, u OT_UNUSED_VARIABLE(aSensitiveKeysLength); } -void otPlatSettingsDeinit(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} +void otPlatSettingsDeinit(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); } otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) { @@ -489,15 +471,12 @@ otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex) return OT_ERROR_NONE; } -void otPlatSettingsWipe(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} +void otPlatSettingsWipe(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); } otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, - char * aArgs[], - char * aOutput, + char *aArgs[], + char *aOutput, size_t aOutputMaxLen) { OT_UNUSED_VARIABLE(aInstance); @@ -509,25 +488,13 @@ otError otPlatDiagProcess(otInstance *aInstance, return OT_ERROR_INVALID_COMMAND; } -void otPlatDiagModeSet(bool aMode) -{ - OT_UNUSED_VARIABLE(aMode); -} +void otPlatDiagModeSet(bool aMode) { OT_UNUSED_VARIABLE(aMode); } -bool otPlatDiagModeGet(void) -{ - return false; -} +bool otPlatDiagModeGet(void) { return false; } -void otPlatDiagChannelSet(uint8_t aChannel) -{ - OT_UNUSED_VARIABLE(aChannel); -} +void otPlatDiagChannelSet(uint8_t aChannel) { OT_UNUSED_VARIABLE(aChannel); } -void otPlatDiagTxPowerSet(int8_t aTxPower) -{ - OT_UNUSED_VARIABLE(aTxPower); -} +void otPlatDiagTxPowerSet(int8_t aTxPower) { OT_UNUSED_VARIABLE(aTxPower); } void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otError aError) { @@ -536,7 +503,4 @@ void otPlatDiagRadioReceived(otInstance *aInstance, otRadioFrame *aFrame, otErro OT_UNUSED_VARIABLE(aError); } -void otPlatDiagAlarmCallback(otInstance *aInstance) -{ - OT_UNUSED_VARIABLE(aInstance); -} +void otPlatDiagAlarmCallback(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); } diff --git a/tests/fuzz/ip6_send.cpp b/tests/fuzz/ip6_send.cpp index da2cfb38cec..ef366a0a5ed 100644 --- a/tests/fuzz/ip6_send.cpp +++ b/tests/fuzz/ip6_send.cpp @@ -26,27 +26,43 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#define MAX_ITERATIONS 100 - #include #include #include #include #include +#include #include #include #include +#include #include "fuzzer_platform.h" #include "common/code_utils.hpp" +#include "common/time.hpp" + +void AdvanceTime(otInstance *aInstance, uint32_t aDuration) +{ + uint32_t time = otPlatAlarmMilliGetNow() + aDuration; + + while (ot::TimeMilli(otPlatAlarmMilliGetNow()) <= ot::TimeMilli(time)) + { + while (otTaskletsArePending(aInstance)) + { + otTaskletsProcess(aInstance); + } + + FuzzerPlatformProcess(aInstance); + } +} extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { const otPanId panId = 0xdead; - otInstance * instance = nullptr; - otMessage * message = nullptr; + otInstance *instance = nullptr; + otMessage *message = nullptr; otError error = OT_ERROR_NONE; otMessageSettings settings; @@ -58,8 +74,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) IgnoreError(otLinkSetPanId(instance, panId)); IgnoreError(otIp6SetEnabled(instance, true)); IgnoreError(otThreadSetEnabled(instance, true)); + otSrpServerSetEnabled(instance, true); IgnoreError(otThreadBecomeLeader(instance)); + AdvanceTime(instance, 10000); + settings.mLinkSecurityEnabled = (data[0] & 0x1) != 0; settings.mPriority = OT_MESSAGE_PRIORITY_NORMAL; @@ -75,15 +94,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) VerifyOrExit(!FuzzerPlatformResetWasRequested()); - for (int i = 0; i < MAX_ITERATIONS; i++) - { - while (otTaskletsArePending(instance)) - { - otTaskletsProcess(instance); - } - - FuzzerPlatformProcess(instance); - } + AdvanceTime(instance, 10000); exit: diff --git a/tests/fuzz/ncp_hdlc_received.cpp b/tests/fuzz/ncp_hdlc_received.cpp index a61b0faa06b..8afb417eaaf 100644 --- a/tests/fuzz/ncp_hdlc_received.cpp +++ b/tests/fuzz/ncp_hdlc_received.cpp @@ -26,8 +26,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#define MAX_ITERATIONS 100 - #include #include @@ -35,12 +33,15 @@ #include #include #include +#include #include #include #include +#include #include "fuzzer_platform.h" #include "common/code_utils.hpp" +#include "common/time.hpp" static int HdlcSend(const uint8_t *aBuf, uint16_t aBufLength) { @@ -50,12 +51,27 @@ static int HdlcSend(const uint8_t *aBuf, uint16_t aBufLength) return aBufLength; } +void AdvanceTime(otInstance *aInstance, uint32_t aDuration) +{ + uint32_t time = otPlatAlarmMilliGetNow() + aDuration; + + while (ot::TimeMilli(otPlatAlarmMilliGetNow()) <= ot::TimeMilli(time)) + { + while (otTaskletsArePending(aInstance)) + { + otTaskletsProcess(aInstance); + } + + FuzzerPlatformProcess(aInstance); + } +} + extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { const otPanId panId = 0xdead; otInstance *instance = nullptr; - uint8_t * buf = nullptr; + uint8_t *buf = nullptr; VerifyOrExit(size <= 65536); @@ -66,25 +82,18 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) IgnoreError(otLinkSetPanId(instance, panId)); IgnoreError(otIp6SetEnabled(instance, true)); IgnoreError(otThreadSetEnabled(instance, true)); + otSrpServerSetEnabled(instance, true); IgnoreError(otThreadBecomeLeader(instance)); - buf = static_cast(malloc(size)); + AdvanceTime(instance, 10000); + buf = static_cast(malloc(size)); memcpy(buf, data, size); - otNcpHdlcReceive(buf, static_cast(size)); VerifyOrExit(!FuzzerPlatformResetWasRequested()); - for (int i = 0; i < MAX_ITERATIONS; i++) - { - while (otTaskletsArePending(instance)) - { - otTaskletsProcess(instance); - } - - FuzzerPlatformProcess(instance); - } + AdvanceTime(instance, 10000); exit: diff --git a/tests/fuzz/oss-fuzz-build b/tests/fuzz/oss-fuzz-build index 31373c4155a..c8bb40789b7 100755 --- a/tests/fuzz/oss-fuzz-build +++ b/tests/fuzz/oss-fuzz-build @@ -27,6 +27,8 @@ # POSSIBILITY OF SUCH DAMAGE. # +set -euxo pipefail + ( mkdir build cd build || exit @@ -43,7 +45,6 @@ -DOT_BORDER_ROUTER=ON \ -DOT_CHANNEL_MANAGER=ON \ -DOT_CHANNEL_MONITOR=ON \ - -DOT_CHILD_SUPERVISION=ON \ -DOT_COAP=ON \ -DOT_COAPS=ON \ -DOT_COAP_BLOCK=ON \ @@ -61,15 +62,15 @@ -DOT_LINK_RAW=ON \ -DOT_LOG_OUTPUT=APP \ -DOT_MAC_FILTER=ON \ - -DOT_MTD_NETDIAG=ON \ -DOT_NETDATA_PUBLISHER=ON \ + -DOT_NETDIAG_CLIENT=ON \ -DOT_PING_SENDER=ON \ -DOT_SERVICE=ON \ -DOT_SLAAC=ON \ -DOT_SNTP_CLIENT=ON \ -DOT_SRP_CLIENT=ON \ -DOT_SRP_SERVER=ON \ - -DOT_THREAD_VERSION=1.2 \ + -DOT_THREAD_VERSION=1.3 \ -DOT_UPTIME=ON \ .. ninja diff --git a/tests/fuzz/ot_fuzz_framework.cpp b/tests/fuzz/ot_fuzz_framework.cpp new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/fuzz/ot_fuzz_framework.h b/tests/fuzz/ot_fuzz_framework.h new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/fuzz/radio_receive_done.cpp b/tests/fuzz/radio_receive_done.cpp index d30b4aef70f..04399cd6f9f 100644 --- a/tests/fuzz/radio_receive_done.cpp +++ b/tests/fuzz/radio_receive_done.cpp @@ -26,8 +26,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#define MAX_ITERATIONS 100 - #include #include @@ -37,18 +35,35 @@ #include #include #include +#include #include #include "fuzzer_platform.h" #include "common/code_utils.hpp" +#include "common/time.hpp" + +void AdvanceTime(otInstance *aInstance, uint32_t aDuration) +{ + uint32_t time = otPlatAlarmMilliGetNow() + aDuration; + + while (ot::TimeMilli(otPlatAlarmMilliGetNow()) <= ot::TimeMilli(time)) + { + while (otTaskletsArePending(aInstance)) + { + otTaskletsProcess(aInstance); + } + + FuzzerPlatformProcess(aInstance); + } +} extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { const otPanId panId = 0xdead; - otInstance * instance = nullptr; + otInstance *instance = nullptr; otRadioFrame frame; - uint8_t * buf = nullptr; + uint8_t *buf = nullptr; VerifyOrExit(size <= OT_RADIO_FRAME_MAX_SIZE); @@ -60,28 +75,20 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) IgnoreError(otThreadSetEnabled(instance, true)); IgnoreError(otThreadBecomeLeader(instance)); - buf = static_cast(malloc(size)); + AdvanceTime(instance, 10000); + buf = static_cast(malloc(size)); memset(&frame, 0, sizeof(frame)); frame.mPsdu = buf; frame.mChannel = 11; frame.mLength = static_cast(size); - memcpy(buf, data, frame.mLength); otPlatRadioReceiveDone(instance, &frame, OT_ERROR_NONE); VerifyOrExit(!FuzzerPlatformResetWasRequested()); - for (int i = 0; i < MAX_ITERATIONS; i++) - { - while (otTaskletsArePending(instance)) - { - otTaskletsProcess(instance); - } - - FuzzerPlatformProcess(instance); - } + AdvanceTime(instance, 10000); exit: diff --git a/tests/scripts/expect/_common.exp b/tests/scripts/expect/_common.exp index 3cdb89b46b8..820a0c674e3 100644 --- a/tests/scripts/expect/_common.exp +++ b/tests/scripts/expect/_common.exp @@ -37,7 +37,7 @@ proc skip_on_macos {} { proc wait_for {command success {failure {[\r\n]FAILURE_NOT_EXPECTED[\r\n]}}} { set timeout 1 - for {set i 0} {$i < 20} {incr i} { + for {set i 0} {$i < 40} {incr i} { if {$command != ""} { send "$command\n" } @@ -126,6 +126,15 @@ proc switch_node {id} { set spawn_id $spawn_ids($id) } +proc attach {{role "leader"}} { + send "ifconfig up\n" + expect_line "Done" + send "thread start\n" + expect_line "Done" + wait_for "state" $role + expect_line "Done" +} + proc setup_leader {} { send "dataset init new\n" expect_line "Done" @@ -133,12 +142,7 @@ proc setup_leader {} { expect_line "Done" send "dataset commit active\n" expect_line "Done" - send "ifconfig up\n" - expect_line "Done" - send "thread start\n" - expect_line "Done" - wait_for "state" "leader" - expect_line "Done" + attach } proc dispose_node {id} { @@ -173,6 +177,23 @@ proc get_extaddr {} { return $rval } +proc get_extpanid {} { + send "extpanid\n" + set rval [expect_line {[0-9a-fA-F]{16}}] + expect_line "Done" + + return $rval +} + +proc get_panid {} { + send "panid\n" + expect -re {0x([0-9a-fA-F]{4})} + set rval $expect_out(1,string) + expect_line "Done" + + return $rval +} + proc get_meshlocal_prefix {} { send "prefix meshlocal\n" expect -re {[\r\n ](([0-9a-fA-F]{1,4}:){3}[0-9a-fA-f]{1,4})::/64(?=[\r\n>])} diff --git a/tests/scripts/expect/cli-child.exp b/tests/scripts/expect/cli-child.exp index 1e730996633..af13b2ef9ec 100755 --- a/tests/scripts/expect/cli-child.exp +++ b/tests/scripts/expect/cli-child.exp @@ -38,9 +38,9 @@ set rloc [get_rloc16] switch_node 1 send "child table\n" -expect "| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt| Extended MAC |" -expect "+-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+------------------+" -expect -re "\\| +(\\d+) \\| 0x$rloc \\| +\\d+ \\| +\\d+ \\| +\\d+ \\| +\\d+ \\|\\d\\|\\d\\|\\d\\| *\\d+\\| \\d \\| +\\d+ \\| $extaddr \\|" +expect "| ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt|Suprvsn| Extended MAC |" +expect "+-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+-------+------------------+" +expect -re "\\| +(\\d+) \\| 0x$rloc \\| +\\d+ \\| +\\d+ \\| +\\d+ \\| +\\d+ \\|\\d\\|\\d\\|\\d\\| *\\d+\\| \\d \\| +\\d+ \\| +\\d+ \\| $extaddr \\|" set child_id $expect_out(1,string) expect_line "Done" send "child list\n" diff --git a/tests/scripts/expect/cli-diags.exp b/tests/scripts/expect/cli-diags.exp new file mode 100755 index 00000000000..e5d2fbfab53 --- /dev/null +++ b/tests/scripts/expect/cli-diags.exp @@ -0,0 +1,214 @@ +#!/usr/bin/expect -f +# +# 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. +# + +source "tests/scripts/expect/_common.exp" +source "tests/scripts/expect/_multinode.exp" + +spawn_node 1 +spawn_node 2 + +switch_node 1 +send "diag start\n" +expect "start diagnostics mode" +expect "status 0x00" +expect_line "Done" + +send "diag channel 11\n" +expect "set channel to 11" +expect "status 0x00" +expect_line "Done" + +send "diag stats clear\n" +expect "stats cleared" +expect_line "Done" + +switch_node 2 + +send "diag start\n" +expect "start diagnostics mode" +expect "status 0x00" +expect_line "Done" + +send "diag channel 11\n" +expect "set channel to 11" +expect "status 0x00" +expect_line "Done" + +send "diag stats clear\n" +expect "stats cleared" +expect_line "Done" + +send "diag send 10 100\n" +expect "sending 0xa packet(s), length 0x64" +expect "status 0x00" +expect_line "Done" + +sleep 2 + +send "diag stats\n" +expect "received packets: 0" +expect "sent packets: 10" +expect "first received packet: rssi=0, lqi=0" +expect "last received packet: rssi=0, lqi=0" +expect_line "Done" + +switch_node 1 + +send "diag stats\n" +expect "received packets: 10" +expect "sent packets: 0" +expect "first received packet: rssi=-20, lqi=0" +expect "last received packet: rssi=-20, lqi=0" +expect_line "Done" + +send "diag stats clear\n" +expect "stats cleared" +expect_line "Done" + +switch_node 2 + +send "diag repeat 20 100\n" +expect "sending packets of length 0x64 at the delay of 0x14 ms" +expect "status 0x00" +expect_line "Done" +sleep 1 +send "diag repeat stop\n" +expect "repeated packet transmission is stopped" +expect "status 0x00" +expect_line "Done" + +switch_node 1 + +send "diag stats\n" +expect -r {received packets: \d+} +expect "sent packets: 0" +expect "first received packet: rssi=-20, lqi=0" +expect "last received packet: rssi=-20, lqi=0" +expect_line "Done" + +send "diag stats clear\n" +expect "stats cleared" +expect_line "Done" + +dispose_all + + +spawn_node 1 + +send "diag start\n" +expect "start diagnostics mode" +expect "status 0x00" +expect_line "Done" + +send "diag channel 11\n" +expect "set channel to 11" +expect "status 0x00" +expect_line "Done" + +send "diag power 10\n" +expect "set tx power to 10 dBm" +expect "status 0x00" +expect_line "Done" + +send "diag radio sleep\n" +expect "set radio from receive to sleep" +expect "status 0x00" +expect_line "Done" + +send "diag radio state\n" +expect "sleep" +expect_line "Done" + +send "diag radio receive\n" +expect "set radio from sleep to receive on channel 11" +expect "status 0x00" +expect_line "Done" + +send "diag radio state\n" +expect "receive" +expect_line "Done" + +send "diag gpio set 0 1\n" +expect_line "Done" + +send "diag gpio get 0\n" +expect "1" +expect_line "Done" + +send "diag gpio mode 0 in\n" +expect_line "Done" + +send "diag gpio mode 0\n" +expect "in" +expect_line "Done" + +send "diag gpio mode 0 out\n" +expect_line "Done" + +send "diag gpio mode 0\n" +expect "out" +expect_line "Done" + +send "diag cw start\n" +expect_line "Done" + +send "diag cw stop\n" +expect_line "Done" + +send "diag stream start\n" +expect_line "Done" + +send "diag stream stop\n" +expect_line "Done" + +send "diag rawpowersetting 112233\n" +expect_line "Done" + +send "diag rawpowersetting\n" +expect "112233" +expect_line "Done" + +send "diag rawpowersetting enable\n" +expect_line "Done" + +send "diag rawpowersetting disable\n" +expect_line "Done" + +send "diag invalid_commad\n" +expect "Error 35: InvalidCommand" + +send "diag stop\n" +expect_line "Done" + +send "diag channel\n" +expect "failed" +expect "status 0xd" +expect "Error 13: InvalidState" + +dispose_all diff --git a/tests/scripts/expect/cli-discover.exp b/tests/scripts/expect/cli-discover.exp new file mode 100755 index 00000000000..6c769f73545 --- /dev/null +++ b/tests/scripts/expect/cli-discover.exp @@ -0,0 +1,146 @@ +#!/usr/bin/expect -f +# +# Copyright (c) 2023, 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. +# + +source "tests/scripts/expect/_common.exp" +source "tests/scripts/expect/_multinode.exp" + +for {set i 1} {$i <= 5} {incr i} { + spawn_node $i +} + +switch_node 1 + +send "dataset init new\n" +expect_line "Done" +send "dataset networkkey 00112233445566778899aabbccddeeff\n" +expect_line "Done" +send "dataset channel 12\n" +expect_line "Done" +send "dataset networkname OpenThread-ch12\n" +expect_line "Done" +send "dataset commit active\n" +expect_line "Done" +send "dataset active -x\n" +expect -re {([0-9a-f]+)[\r\n]+Done} +set dataset $expect_out(1,string) +attach + +set extaddr_1 [get_extaddr] +set extpan_1 [get_extpanid] +set pan_1 [get_panid] + +switch_node 2 + +send "dataset init new\n" +expect_line "Done" +send "dataset channel 23\n" +expect_line "Done" +send "dataset networkname OpenThread-ch23\n" +expect_line "Done" +send "dataset commit active\n" +expect_line "Done" +attach + +set extaddr_2 [get_extaddr] +set extpan_2 [get_extpanid] +set pan_2 [get_panid] + +switch_node 3 + +send "dataset set active $dataset\n" +expect_line "Done" +send "mode r\n" +expect_line "Done" +attach "child" + +switch_node 4 + +send "dataset set active $dataset\n" +expect_line "Done" +send "mode -\n" +expect_line "Done" +attach "child" + +for {set i 3} {$i <= 4} {incr i} { + switch_node $i + + send "discover\n" + expect "| Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI |" + expect "+------------------+------------------+------+------------------+----+-----+-----+" + wait_for "" "\\| OpenThread-ch12 +\\| $extpan_1 \\| $pan_1 \\| $extaddr_1 \\| 12 \\| +-?\\d+ \\| +\\d \\|" + wait_for "" "\\| OpenThread-ch23 +\\| $extpan_2 \\| $pan_2 \\| $extaddr_2 \\| 23 \\| +-?\\d+ \\| +\\d \\|" + wait_for "" "Done" +} + +if {$::env(THREAD_VERSION) != "1.1" && $::env(OT_NODE_TYPE) == "cli"} { + send "csl channel 12\n" + expect_line "Done" + send "csl period 3125\n" + expect_line "Done" + + sleep 1 + + send "discover\n" + expect "| Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI |" + expect "+------------------+------------------+------+------------------+----+-----+-----+" + wait_for "" "\\| OpenThread-ch12 +\\| $extpan_1 \\| $pan_1 \\| $extaddr_1 \\| 12 \\| +-?\\d+ \\| +\\d \\|" + wait_for "" "\\| OpenThread-ch23 +\\| $extpan_2 \\| $pan_2 \\| $extaddr_2 \\| 23 \\| +-?\\d+ \\| +\\d \\|" + wait_for "" "Done" +} + +switch_node 1 +send "discover reqcallback enable\n" +expect_line "Done" + +switch_node 5 +send "discover\n" +expect "Error 13: InvalidState" +send "ifconfig up\n" +expect_line "Done" +send "discover 12\n" + +expect "| Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI |" +expect "+------------------+------------------+------+------------------+----+-----+-----+" +wait_for "" "\\| OpenThread-ch12 +\\| $extpan_1 \\| $pan_1 \\| $extaddr_1 \\| 12 \\| +-?\\d+ \\| +\\d \\|" +wait_for "" "Done" + +switch_node 1 +expect -re {version=\d,joiner=0} + +switch_node 5 +send "ifconfig up\n" +expect_line "Done" +send "joiner start 123456\n" +set timeout 10 +expect "NotFound" + +switch_node 1 +expect -re {version=\d,joiner=1} + +dispose_all diff --git a/tests/scripts/expect/cli-multicast-loop.exp b/tests/scripts/expect/cli-multicast-loop.exp index ef6c8b68b95..14f07bd6059 100755 --- a/tests/scripts/expect/cli-multicast-loop.exp +++ b/tests/scripts/expect/cli-multicast-loop.exp @@ -44,14 +44,7 @@ set timeout 1 send "panid 0xface\n" expect_line "Done" -send "ifconfig up\n" -expect_line "Done" - -send "thread start\n" -expect_line "Done" - -wait_for "state" "leader" -expect_line "Done" +attach set extaddr1 [get_extaddr] @@ -63,14 +56,8 @@ setup_default_network send "panid 0xface\n" expect_line "Done" -send "ifconfig up\n" -expect_line "Done" - -send "thread start\n" -expect_line "Done" +attach "router" -wait_for "state" "router" -expect_line "Done" sleep 3 get_rloc16 @@ -87,14 +74,8 @@ expect_line "Done" send "macfilter addr denylist\n" expect_line "Done" -send "ifconfig up\n" -expect_line "Done" - -send "thread start\n" -expect_line "Done" +attach "router" -wait_for "state" "router" -expect_line "Done" sleep 4 set extaddr3 [get_extaddr] @@ -116,14 +97,7 @@ expect_line "Done" send "macfilter addr allowlist\n" expect_line "Done" -send "ifconfig up\n" -expect_line "Done" - -send "thread start\n" -expect_line "Done" - -wait_for "state" "child" -expect_line "Done" +attach "child" get_rloc16 diff --git a/tests/scripts/expect/cli-neighbor.exp b/tests/scripts/expect/cli-neighbor.exp index 9f5dd9e5f7a..1b5a9de85dc 100755 --- a/tests/scripts/expect/cli-neighbor.exp +++ b/tests/scripts/expect/cli-neighbor.exp @@ -38,9 +38,9 @@ set extaddr [get_extaddr] switch_node 1 send "neighbor table\n" -expect "| Role | RLOC16 | Age | Avg RSSI | Last RSSI |R|D|N| Extended MAC |" -expect "+------+--------+-----+----------+-----------+-+-+-+------------------+" -expect -re "\\| +C +\\| 0x$rloc \\| +\\d+ \\| +-?\\d+ \\| +-?\\d+ \\|1\\|0\\|0\\| $extaddr \\|" +expect "| Role | RLOC16 | Age | Avg RSSI | Last RSSI |R|D|N| Extended MAC | Version |" +expect "+------+--------+-----+----------+-----------+-+-+-+------------------+---------+" +expect -re "\\| +C +\\| 0x$rloc \\| +\\d+ \\| +-?\\d+ \\| +-?\\d+ \\|1\\|0\\|0\\| $extaddr \\| +\\d+ \\|" expect_line "Done" send "neighbor list\n" expect "0x$rloc" diff --git a/tests/scripts/expect/cli-router.exp b/tests/scripts/expect/cli-router.exp index b7f1a56dd5a..11456af196a 100755 --- a/tests/scripts/expect/cli-router.exp +++ b/tests/scripts/expect/cli-router.exp @@ -52,6 +52,7 @@ expect_line "Done" switch_node 2 send "mode rdn\n" expect_line "Done" +sleep 5 send "state router\n" expect_line "Done" wait_for "router list" $router_id diff --git a/tests/scripts/expect/cli-scan-discover.exp b/tests/scripts/expect/cli-scan.exp similarity index 73% rename from tests/scripts/expect/cli-scan-discover.exp rename to tests/scripts/expect/cli-scan.exp index 78226c77d39..1bb20ebecd7 100755 --- a/tests/scripts/expect/cli-scan-discover.exp +++ b/tests/scripts/expect/cli-scan.exp @@ -41,17 +41,7 @@ expect "panid" expect -re {([0-9a-f]{4})} set pan $expect_out(1,string) expect_line "Done" -send "extpanid\n" -expect "extpanid" -expect -re {([0-9a-f]{16})} -set extpan $expect_out(1,string) -expect_line "Done" expect "> " -send "networkname\n" -expect "networkname" -expect -re {[\r\n]([^\r\n]+?)[\r\n]} -set network $expect_out(1,string) -expect_line "Done" send "channel\n" expect "channel" expect -re {(\d+)} @@ -77,28 +67,4 @@ expect "+----+------+" expect -re "\\| +$channel \\| +-?\\d+ \\|" expect_line "Done" -switch_node 3 -send "discover\n" -expect "Error 13: InvalidState" -send "ifconfig up\n" -expect_line "Done" -send "discover $channel\n" -expect "| Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI |" -expect "+------------------+------------------+------+------------------+----+-----+-----+" -wait_for "" "\\| $network +\\| $extpan \\| $pan \\| $extaddr \\| +$channel \\| +-?\\d+ \\| +\\d \\|" -wait_for "" "Done" -send "discover something_invalid\n" -expect "Error 7: InvalidArgs" - -switch_node 1 -expect -re {version=\d,joiner=0} - -switch_node 3 -send "joiner start 123456\n" -set timeout 10 -expect "NotFound" - -switch_node 1 -expect -re {version=\d,joiner=1} - dispose_all diff --git a/tests/scripts/expect/cli-tcp-tls.exp b/tests/scripts/expect/cli-tcp-tls.exp new file mode 100644 index 00000000000..ccc44244f5c --- /dev/null +++ b/tests/scripts/expect/cli-tcp-tls.exp @@ -0,0 +1,168 @@ +#!/usr/bin/expect -f +# +# 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. +# + +source "tests/scripts/expect/_common.exp" +source "tests/scripts/expect/_multinode.exp" + +spawn_node 2 "cli" +spawn_node 1 "cli" +setup_leader +setup_node 2 "rnd" "router" + +switch_node 1 +send "tcp init tls\n" +expect_line "Done" + +switch_node 2 +send "tcp init tls\n" +expect_line "Done" +set addr_2 [get_ipaddr mleid] +send "tcp listen :: 30000\n" +expect_line "Done" +send "tcp stoplistening\n" +expect_line "Done" + +switch_node 1 +send "tcp connect $addr_2 30000\n" +expect_line "Done" +expect "TCP: Connection refused" + +switch_node 2 +send "tcp listen :: 30000\n" +expect_line "Done" + +switch_node 1 +set addr_1 [get_ipaddr mleid] +send "tcp bind $addr_1 25000\n" +expect_line "Done" +send "tcp connect $addr_2 30000\n" +expect_line "Done" +expect "TCP: Connection established" +expect "TLS Handshake Complete" + +switch_node 2 +expect "Accepted connection from \\\[$addr_1\\\]:25000" +expect "TCP: Connection established" +expect "TLS Handshake Complete" + +switch_node 1 +send "tcp send hello\n" +expect_line "Done" + +switch_node 2 +expect "TLS: Received 5 bytes: hello" +expect "(TCP: Received 26 bytes)" +send "tcp send world\n" +expect_line "Done" + +switch_node 1 +expect "TLS: Received 5 bytes: world" +expect "(TCP: Received 26 bytes)" +send "tcp sendend\n" +expect_line "Done" +send "tcp send more\n" +expect_line "Error 1: Failed" + +switch_node 2 +expect "TCP: Reached end of stream" +send "tcp send goodbye\n" +expect_line "Done" + +switch_node 1 +expect "TLS: Received 7 bytes: goodbye" +expect "(TCP: Received 28 bytes)" + +switch_node 2 +send "tcp sendend\n" +expect_line "Done" +expect "TCP: Disconnected" + +switch_node 1 +expect "TCP: Reached end of stream" +expect "TCP: Entered TIME-WAIT state" +set timeout 130 +expect "TCP: Disconnected" + +# Test second connection with roles reversed (to be sure we're managing TLS state correctly) + +send "tcp listen :: 30000\n" +expect_line "Done" + +switch_node 2 +send "tcp bind $addr_2 25000\n" +expect_line "Done" +send "tcp connect $addr_1 30000\n" +expect_line "Done" +expect "TCP: Connection established" +expect "TLS Handshake Complete" + +switch_node 1 +expect "Accepted connection from \\\[$addr_2\\\]:25000" +expect "TCP: Connection established" +expect "TLS Handshake Complete" + +switch_node 2 +send "tcp send hello\n" +expect_line "Done" + +switch_node 1 +expect "TLS: Received 5 bytes: hello" +expect "(TCP: Received 26 bytes)" +send "tcp send world\n" +expect_line "Done" + +switch_node 2 +expect "TLS: Received 5 bytes: world" +expect "(TCP: Received 26 bytes)" +send "tcp sendend\n" +expect_line "Done" +send "tcp send more\n" +expect_line "Error 1: Failed" + +switch_node 1 +expect "TCP: Reached end of stream" +send "tcp send goodbye\n" +expect_line "Done" + +switch_node 2 +expect "TLS: Received 7 bytes: goodbye" +expect "(TCP: Received 28 bytes)" + +switch_node 1 +send "tcp sendend\n" +expect_line "Done" +expect "TCP: Disconnected" + +switch_node 2 +expect "TCP: Reached end of stream" +expect "TCP: Entered TIME-WAIT state" +set timeout 130 +expect "TCP: Disconnected" + +dispose_all diff --git a/tests/scripts/expect/cli-tcp.exp b/tests/scripts/expect/cli-tcp.exp index 838730d1623..6ae03855aaf 100755 --- a/tests/scripts/expect/cli-tcp.exp +++ b/tests/scripts/expect/cli-tcp.exp @@ -30,18 +30,32 @@ source "tests/scripts/expect/_common.exp" source "tests/scripts/expect/_multinode.exp" -setup_two_nodes +spawn_node 2 "cli" +spawn_node 1 "cli" +setup_leader +setup_node 2 "rnd" "router" switch_node 1 -send "tcp init\n" +send "tcp init circular\n" expect_line "Done" switch_node 2 -send "tcp init\n" +send "tcp init linked\n" expect_line "Done" set addr_2 [get_ipaddr mleid] send "tcp listen :: 30000\n" expect_line "Done" +send "tcp stoplistening\n" +expect_line "Done" + +switch_node 1 +send "tcp connect $addr_2 30000\n" +expect_line "Done" +expect "TCP: Connection refused" + +switch_node 2 +send "tcp listen :: 30000\n" +expect_line "Done" switch_node 1 set addr_1 [get_ipaddr mleid] diff --git a/tests/scripts/expect/cli-udp.exp b/tests/scripts/expect/cli-udp.exp index 071117252c3..09e76d137c7 100755 --- a/tests/scripts/expect/cli-udp.exp +++ b/tests/scripts/expect/cli-udp.exp @@ -68,7 +68,7 @@ expect "Error 6: Parse" send "udp send -x something_invalid\n" expect "Error 7: InvalidArgs" send "udp\n" -expect "Error 7: InvalidArgs" +expect "Error 35: InvalidCommand" send "udp linksecurity\n" expect "Enabled" diff --git a/tests/scripts/expect/ot-fct.exp b/tests/scripts/expect/ot-fct.exp new file mode 100755 index 00000000000..5fff5d9d5be --- /dev/null +++ b/tests/scripts/expect/ot-fct.exp @@ -0,0 +1,48 @@ +#!/usr/bin/expect -f +# +# 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. +# + +source "tests/scripts/expect/_common.exp" + +spawn build/posix/tools/ot-fct/ot-fct + +send "targetpowertable\n" +expect_line "Done" +send "powercalibrationtable\n" +expect_line "Done" +send "powercalibrationtable add -b 11,25 -c 1900,112233/1000,223344 -b 26,26 -c 1500,334455/700,445566\n" +expect_line "Done" +send "powercalibrationtable clear\n" +expect_line "Done" +send "regiondomaintable\n" +expect "Done" +send "invalidcommand\n" +expect "failed" +expect "status 0x17" +send "\x04" +expect eof diff --git a/tests/scripts/expect/posix-diag-rcp.exp b/tests/scripts/expect/posix-diag-rcp.exp index 08b9553e3b9..e585d60b43f 100755 --- a/tests/scripts/expect/posix-diag-rcp.exp +++ b/tests/scripts/expect/posix-diag-rcp.exp @@ -42,21 +42,15 @@ send "diag rcp\n" expect "diagnostics mode is enabled" expect_line "Done" send "diag rcp channel\n" -expect "failed" -expect "status 0x7" -expect_line "Done" +expect "Error 7: InvalidArgs" send "diag rcp channel 11\n" expect_line "Done" send "diag rcp power\n" -expect "failed" -expect "status 0x7" -expect_line "Done" +expect "Error 7: InvalidArgs" send "diag rcp power 10\n" expect_line "Done" send "diag rcp echo\n" -expect "failed" -expect "status 0x7" -expect_line "Done" +expect "Error 7: InvalidArgs" send "diag rcp echo 0123456789\n" expect_line "0123456789" expect_line "Done" diff --git a/script/check-arm-build-cmake b/tests/scripts/expect/posix-power-calibration.exp similarity index 56% rename from script/check-arm-build-cmake rename to tests/scripts/expect/posix-power-calibration.exp index 3ee62b06e6d..a8f15fb29cc 100755 --- a/script/check-arm-build-cmake +++ b/tests/scripts/expect/posix-power-calibration.exp @@ -1,6 +1,6 @@ -#!/bin/bash +#!/usr/bin/expect -f # -# Copyright (c) 2020, The OpenThread Authors. +# Copyright (c) 2022, The OpenThread Authors. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -27,51 +27,48 @@ # POSSIBILITY OF SUCH DAMAGE. # -set -euxo pipefail +source "tests/scripts/expect/_common.exp" -readonly OT_BUILDDIR="$(pwd)/build" +spawn_node 1 -readonly OT_COMMON_OPTIONS=( - "-DOT_COMPILE_WARNING_AS_ERROR=ON" -) +send "diag start\n" +expect "start diagnostics mode" +expect "status 0x00" +expect_line "Done" -readonly OT_BASIC_CHECK_OPTIONS=( - "-DOT_COMMISSIONER=ON" - "-DOT_DHCP6_CLIENT=ON" - "-DOT_DHCP6_SERVER=ON" - "-DOT_DNS_CLIENT=ON" - "-DOT_JOINER=ON" -) +send "diag powersettings 11\n" +expect "failed" +expect "status 0x17" +expect_line "Error 23: NotFound" -reset_source() -{ - rm -rf "$OT_BUILDDIR" -} +send "diag powersettings\n" +expect "| StartCh | EndCh | TargetPower | ActualPower | RawPowerSetting |" +expect "+---------+-------+-------------+-------------+-----------------+" +expect_line "Done" -build_cc2538() -{ - local options=( - # cc2538 does not have enough resources to support Thread 1.3 - "-DOT_THREAD_VERSION=1.1" - ) +send "diag stop\n" +expect_line "Done" - reset_source - "$(dirname "$0")"/cmake-build cc2538 "${OT_COMMON_OPTIONS[@]}" "${OT_BASIC_CHECK_OPTIONS[@]}" "${options[@]}" -} +send "region US\n" +expect_line "Done" -main() -{ - export CPPFLAGS="${CPPFLAGS:-} -DNDEBUG" +send "diag start\n" +expect "start diagnostics mode" +expect "status 0x00" +expect_line "Done" - if [[ $# == 0 ]]; then - build_cc2538 - return 0 - fi +send "diag powersettings 11\n" +expect -re {TargetPower\(0\.01dBm\): -?\d+} +expect -re {ActualPower\(0\.01dBm\): -?\d+} +expect -re {RawPowerSetting: [0-9]{1,16}} +expect_line "Done" - while [[ $# != 0 ]]; do - "build_$1" - shift - done +send "diag powersettings\n" +expect "| StartCh | EndCh | TargetPower | ActualPower | RawPowerSetting |" +expect "+---------+-------+-------------+-------------+-----------------+" +for {set i 1} {$i <= 4} {incr i} { + expect -re "\\| +\\d+ | +\\d+ | +\\d+ | +\\d+ | +\[0-9\]\{1,16\} |" } +expect_line "Done" -main "$@" +dispose_all diff --git a/tests/scripts/expect/posix-rcp-restoration.exp b/tests/scripts/expect/posix-rcp-restoration.exp index c0dac4c2481..1d3fac22759 100755 --- a/tests/scripts/expect/posix-rcp-restoration.exp +++ b/tests/scripts/expect/posix-rcp-restoration.exp @@ -251,12 +251,7 @@ try { puts "While energy scanning" spawn_node 1 "rcp" "spinel+hdlc_uart://$host_pty" - send "ifconfig up\n" - expect_line "Done" - send "thread start\n" - expect_line "Done" - wait_for "state" "leader" - expect_line "Done" + attach send "scan energy 100\n" expect "| Ch | RSSI |" diff --git a/tests/scripts/expect/posix-scan-tx-to-sleep.exp b/tests/scripts/expect/posix-scan-tx-to-sleep.exp index 2ac44a81fd0..eea15b67944 100755 --- a/tests/scripts/expect/posix-scan-tx-to-sleep.exp +++ b/tests/scripts/expect/posix-scan-tx-to-sleep.exp @@ -36,12 +36,7 @@ send "dataset panid 0xface\n" expect_line "Done" send "dataset commit active\n" expect_line "Done" -send "ifconfig up\n" -expect_line "Done" -send "thread start\n" -expect_line "Done" -wait_for "state" "leader" -expect_line "Done" +attach set extaddr [get_extaddr] send "panid\n" diff --git a/tests/scripts/Makefile.am b/tests/scripts/expect/tun-dns-over-tcp-client.exp old mode 100644 new mode 100755 similarity index 71% rename from tests/scripts/Makefile.am rename to tests/scripts/expect/tun-dns-over-tcp-client.exp index d1cd8f17fc6..3da428a583c --- a/tests/scripts/Makefile.am +++ b/tests/scripts/expect/tun-dns-over-tcp-client.exp @@ -1,5 +1,6 @@ +#!/usr/bin/expect -f # -# Copyright (c) 2016-2017, The OpenThread Authors. +# Copyright (c) 2020, The OpenThread Authors. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -26,26 +27,20 @@ # POSSIBILITY OF SUCH DAMAGE. # -include $(abs_top_nlbuild_autotools_dir)/automake/pre.am +source "tests/scripts/expect/_common.exp" +source "tests/scripts/expect/_multinode.exp" -# Always package (e.g. for 'make dist') these subdirectories. +spawn_node 2 "cli" +spawn_node 1 +setup_leader +setup_node 2 "rnd" "router" -DIST_SUBDIRS = \ - thread-cert \ - $(NULL) +switch_node 1 +set addr_1 [get_ipaddr mleid] -# Always build (e.g. for 'make all') these subdirectories. - -SUBDIRS = \ - $(NULL) - -if OPENTHREAD_POSIX -if OPENTHREAD_ENABLE_CLI -SUBDIRS += \ - thread-cert \ - $(NULL) -endif -endif - -include $(abs_top_nlbuild_autotools_dir)/automake/post.am +switch_node 2 +send "dns resolve ipv6.google.com $addr_1 2000 6000 4 1 def tcp\n" +expect "DNS response for ipv6.google.com" +expect_line "Done" +dispose_all diff --git a/tests/scripts/expect/tun-realm-local-multicast.exp b/tests/scripts/expect/tun-realm-local-multicast.exp index b18ffba2208..7be7c5e43eb 100755 --- a/tests/scripts/expect/tun-realm-local-multicast.exp +++ b/tests/scripts/expect/tun-realm-local-multicast.exp @@ -39,27 +39,15 @@ source "tests/scripts/expect/_common.exp" spawn_node 1 setup_default_network -send "ifconfig up\n" -expect_line "Done" - -send "thread start\n" -expect_line "Done" - -wait_for "state" "leader" -expect_line "Done" +attach set extaddr1 [get_extaddr] spawn_node 2 cli setup_default_network -send "ifconfig up\n" -expect_line "Done" - -send "thread start\n" -expect_line "Done" +attach "router" -wait_for "state" "router" sleep 3 spawn_node 3 cli @@ -71,13 +59,8 @@ expect_line "Done" send "macfilter addr denylist\n" expect_line "Done" -send "ifconfig up\n" -expect_line "Done" - -send "thread start\n" -expect_line "Done" +attach "router" -wait_for "state" "router" sleep 4 set extaddr3 [get_extaddr] @@ -94,13 +77,7 @@ expect_line "Done" send "macfilter addr allowlist\n" expect_line "Done" -send "ifconfig up\n" -expect_line "Done" - -send "thread start\n" -expect_line "Done" - -wait_for "state" "child" +attach "child" set mleid4 [get_ipaddr "mleid"] diff --git a/tests/scripts/expect/tun-sntp.exp b/tests/scripts/expect/tun-sntp.exp index a72439004ea..fe91233d0dc 100755 --- a/tests/scripts/expect/tun-sntp.exp +++ b/tests/scripts/expect/tun-sntp.exp @@ -35,12 +35,7 @@ spawn_node 1 send "panid 0xface\n" expect_line "Done" -send "ifconfig up\n" -expect_line "Done" -send "thread start\n" -expect_line "Done" -wait_for "state" "leader" -expect_line "Done" +attach send "sntp query ::1 123\n" expect -re {SNTP response - Unix time: \d+ \(era: \d+\)} diff --git a/tests/scripts/expect/tun-udp.exp b/tests/scripts/expect/tun-udp.exp index 03ba12f1562..53cff14e7eb 100755 --- a/tests/scripts/expect/tun-udp.exp +++ b/tests/scripts/expect/tun-udp.exp @@ -38,14 +38,7 @@ set timeout 1 send "panid 0xface\n" expect_line "Done" -send "ifconfig up\n" -expect_line "Done" - -send "thread start\n" -expect_line "Done" - -wait_for "state" "leader" -expect_line "Done" +attach send "udp open\n" expect_line "Done" diff --git a/tests/scripts/thread-cert/Cert_5_1_01_RouterAttach.py b/tests/scripts/thread-cert/Cert_5_1_01_RouterAttach.py index 1e4c3269e63..0c87399489f 100755 --- a/tests/scripts/thread-cert/Cert_5_1_01_RouterAttach.py +++ b/tests/scripts/thread-cert/Cert_5_1_01_RouterAttach.py @@ -271,53 +271,8 @@ def verify(self, pv): ).\ must_next() - # Step 8: Router Sends a Link Request Message. - # The Link Request Message MUST be multicast and contain - # the following TLVs: - # - Challenge TLV - # - Leader Data TLV - # - Source Address TLV - # - Version TLV - # - TLV Request TLV: Link Margin - - pkts.filter_wpan_src64(ROUTER).\ - filter_LLARMA().\ - filter_mle_cmd(MLE_LINK_REQUEST).\ - filter(lambda p: { - CHALLENGE_TLV, - LEADER_DATA_TLV, - SOURCE_ADDRESS_TLV, - VERSION_TLV, - TLV_REQUEST_TLV, - LINK_MARGIN_TLV - } <= set(p.mle.tlv.type)\ - ).\ - must_next() - - # Step 9: Leader sends a Unicast Link Accept and Request Message. - # The Message MUST be unicast to Router - # The Message MUST contain the following TLVs: - # - Leader Data TLV - # - Link-layer Frame Counter TLV - # - Link Margin TLV - # - Response TLV - # - Source Address TLV - # - Version TLV - # - Challenge TLV (optional) - # - MLE Frame Counter TLV (optional) - - pkts.filter_wpan_src64(LEADER).\ - filter_wpan_dst64(ROUTER).\ - filter_mle_cmd(MLE_LINK_ACCEPT_AND_REQUEST).\ - filter(lambda p: { - LEADER_DATA_TLV, - LINK_LAYER_FRAME_COUNTER_TLV, - LINK_MARGIN_TLV, - RESPONSE_TLV, - SOURCE_ADDRESS_TLV, - VERSION_TLV - } <= set(p.mle.tlv.type)).\ - must_next() + # Steps 8 and 9 are skipped due to change the Link establishment + # process (no multicast MLE Link Request by new router). # Step 10: Router is sending properly formatted MLE Advertisements. # MLE Advertisements MUST be sent with an IP Hop Limit of diff --git a/tests/scripts/thread-cert/Cert_5_2_04_REEDUpgrade.py b/tests/scripts/thread-cert/Cert_5_2_04_REEDUpgrade.py index 241c9bb36f2..5a05068377f 100755 --- a/tests/scripts/thread-cert/Cert_5_2_04_REEDUpgrade.py +++ b/tests/scripts/thread-cert/Cert_5_2_04_REEDUpgrade.py @@ -344,28 +344,8 @@ def verify(self, pv): must_next() # Step 10: REED Sends a Link Request Message. - # The Link Request Message MUST be multicast and contain - # the following TLVs: - # - Challenge TLV - # - Leader Data TLV - # - Source Address TLV - # - TLV Request TLV: Link Margin - # - Version TLV - - with pkts.save_index(): - pkts.filter_wpan_src64(REED).\ - filter_LLARMA().\ - filter_mle_cmd(MLE_LINK_REQUEST).\ - filter(lambda p: { - CHALLENGE_TLV, - LEADER_DATA_TLV, - SOURCE_ADDRESS_TLV, - VERSION_TLV, - TLV_REQUEST_TLV, - LINK_MARGIN_TLV - } <= set(p.mle.tlv.type) - ).\ - must_next() + # This step is skipped due to change that new router no + # longer send multicast Link Request. # Step 11: The REED MLE Child ID Response MUST be properly # formatted with MED_1’s new 16-bit address. diff --git a/tests/scripts/thread-cert/Cert_5_3_06_RouterIdMask.py b/tests/scripts/thread-cert/Cert_5_3_06_RouterIdMask.py index 1f57e9f4612..97455af05d6 100755 --- a/tests/scripts/thread-cert/Cert_5_3_06_RouterIdMask.py +++ b/tests/scripts/thread-cert/Cert_5_3_06_RouterIdMask.py @@ -114,7 +114,7 @@ def test(self): # 5 self.nodes[ROUTER2].start() - self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.simulator.go(config.ROUTER_RESET_DELAY) self.assertEqual(self.nodes[ROUTER2].get_state(), 'router') self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL) @@ -190,12 +190,8 @@ def verify(self, pv): filter_mle_cmd(MLE_ADVERTISEMENT).\ filter(lambda p: p.sniff_timestamp - _pkt.sniff_timestamp <= 4).\ must_next() - # check router cost before and after the re-attach - pkts.filter_wpan_src64(LEADER).\ - filter_LLANMA().\ - filter_mle_cmd(MLE_ADVERTISEMENT).\ - filter(lambda p: {1,0,1} == set(p.mle.tlv.route64.cost)).\ - must_next() + + # check router cost after the re-attach pkts.filter_wpan_src64(LEADER).\ filter_LLANMA().\ filter_mle_cmd(MLE_ADVERTISEMENT).\ diff --git a/tests/scripts/thread-cert/Cert_5_5_02_LeaderReboot.py b/tests/scripts/thread-cert/Cert_5_5_02_LeaderReboot.py index 6aff3cfcedf..80aba877441 100755 --- a/tests/scripts/thread-cert/Cert_5_5_02_LeaderReboot.py +++ b/tests/scripts/thread-cert/Cert_5_5_02_LeaderReboot.py @@ -84,7 +84,7 @@ def test(self): self.assertEqual(self.nodes[ROUTER].get_state(), 'leader') self.nodes[LEADER].start() - self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.simulator.go(config.LEADER_RESET_DELAY) self.assertEqual(self.nodes[LEADER].get_state(), 'router') addrs = self.nodes[ED].get_addrs() @@ -176,14 +176,9 @@ def verify(self, pv): thread_address.tlv.status == 0) #Step 15: Leader Send a Multicast Link Request - _lpkts.filter_mle_cmd(MLE_LINK_REQUEST).must_next().must_verify( - lambda p: {VERSION_TLV, TLV_REQUEST_TLV, SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, CHALLENGE_TLV} < set( - p.mle.tlv.type)) - #Step 16: Router_1 send a Unicast Link Accept - _rpkts.filter_mle_cmd(MLE_LINK_ACCEPT_AND_REQUEST).must_next().must_verify(lambda p: { - VERSION_TLV, SOURCE_ADDRESS_TLV, RESPONSE_TLV, MLE_FRAME_COUNTER_TLV, LINK_MARGIN_TLV, LEADER_DATA_TLV - } < set(p.mle.tlv.type)) + # Steps 15 and 16 are skipped since new router no longer + # send multicast Link Request. #Step 17: Router_1 MUST respond with an ICMPv6 Echo Reply _rpkts.filter_ping_request().filter_wpan_dst64(MED).must_next() diff --git a/tests/scripts/thread-cert/Cert_5_5_03_SplitMergeChildren.py b/tests/scripts/thread-cert/Cert_5_5_03_SplitMergeChildren.py index 92040d01393..06f77f65d61 100755 --- a/tests/scripts/thread-cert/Cert_5_5_03_SplitMergeChildren.py +++ b/tests/scripts/thread-cert/Cert_5_5_03_SplitMergeChildren.py @@ -114,7 +114,7 @@ def test(self): self.assertEqual(self.nodes[ROUTER2].get_state(), 'leader') self.nodes[LEADER].start() - self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.simulator.go(config.LEADER_RESET_DELAY) self.assertEqual(self.nodes[LEADER].get_state(), 'router') self.simulator.go(30) diff --git a/tests/scripts/thread-cert/Cert_5_5_04_SplitMergeRouters.py b/tests/scripts/thread-cert/Cert_5_5_04_SplitMergeRouters.py index 8c08d1ecf40..2bcc876524a 100755 --- a/tests/scripts/thread-cert/Cert_5_5_04_SplitMergeRouters.py +++ b/tests/scripts/thread-cert/Cert_5_5_04_SplitMergeRouters.py @@ -102,7 +102,7 @@ def test(self): self.simulator.go(150) self.nodes[LEADER].start() - self.simulator.go(50) + self.simulator.go(50 + config.LEADER_RESET_DELAY) self.assertEqual(self.nodes[LEADER].get_state(), 'router') diff --git a/tests/scripts/thread-cert/Cert_5_5_05_SplitMergeREED.py b/tests/scripts/thread-cert/Cert_5_5_05_SplitMergeREED.py index abfab51496b..0cd99f5cdb2 100755 --- a/tests/scripts/thread-cert/Cert_5_5_05_SplitMergeREED.py +++ b/tests/scripts/thread-cert/Cert_5_5_05_SplitMergeREED.py @@ -197,9 +197,8 @@ def verify(self, pv): lambda p: {NL_MAC_EXTENDED_ADDRESS_TLV, NL_STATUS_TLV} <= set(p.coap.tlv.type)) # Step 7: DUT send a Multicast Link Request - pkts.filter_wpan_src64(REED).filter_mle_cmd(MLE_LINK_REQUEST).must_next().must_verify(lambda p: { - VERSION_TLV, TLV_REQUEST_TLV, SOURCE_ADDRESS_TLV, LEADER_DATA_TLV, CHALLENGE_TLV, LINK_MARGIN_TLV - } <= set(p.mle.tlv.type)) + # Step 7 is skipped since new router no longer sends + # multicast Link Request. # Step 8: DUT send Child ID Response to Router_1 reed_pkts.filter_mle_cmd(MLE_CHILD_ID_RESPONSE).must_next().must_verify(lambda p: p.wpan.dst64 == ROUTER_1) diff --git a/tests/scripts/thread-cert/Cert_5_5_07_SplitMergeThreeWay.py b/tests/scripts/thread-cert/Cert_5_5_07_SplitMergeThreeWay.py index 3b6e201ff9c..fbb9dee487d 100755 --- a/tests/scripts/thread-cert/Cert_5_5_07_SplitMergeThreeWay.py +++ b/tests/scripts/thread-cert/Cert_5_5_07_SplitMergeThreeWay.py @@ -93,7 +93,7 @@ def test(self): self.simulator.go(140) self.nodes[LEADER1].start() - self.simulator.go(30) + self.simulator.go(30 + config.LEADER_RESET_DELAY) addrs = self.nodes[LEADER1].get_addrs() for addr in addrs: diff --git a/tests/scripts/thread-cert/Cert_7_1_06_BorderRouterAsLeader.py b/tests/scripts/thread-cert/Cert_7_1_06_BorderRouterAsLeader.py index f6053cb6b16..36e0f9c9784 100755 --- a/tests/scripts/thread-cert/Cert_7_1_06_BorderRouterAsLeader.py +++ b/tests/scripts/thread-cert/Cert_7_1_06_BorderRouterAsLeader.py @@ -143,7 +143,7 @@ def test(self): self.simulator.go(720) self.nodes[ROUTER_1].start() - self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.simulator.go(config.ROUTER_RESET_DELAY) self.assertEqual(self.nodes[ROUTER_1].get_state(), 'router') self.collect_rloc16s() diff --git a/tests/scripts/thread-cert/Cert_7_1_07_BorderRouterAsLeader.py b/tests/scripts/thread-cert/Cert_7_1_07_BorderRouterAsLeader.py index 617fb58b7ab..49e000bc753 100755 --- a/tests/scripts/thread-cert/Cert_7_1_07_BorderRouterAsLeader.py +++ b/tests/scripts/thread-cert/Cert_7_1_07_BorderRouterAsLeader.py @@ -158,7 +158,7 @@ def test(self): # Wait for Router_2 reattachment and network data propagation # ADVERTISEMENT_I_MAX + DEFAULT_CHILD_TIMEOUT + ATTACH_DELAY + Extra - self.simulator.go(60) + self.simulator.go(120) self.assertEqual(self.nodes[ROUTER_2].get_state(), 'router') self.collect_ipaddrs() self.collect_rloc16s() diff --git a/tests/scripts/thread-cert/Cert_9_2_15_PendingPartition.py b/tests/scripts/thread-cert/Cert_9_2_15_PendingPartition.py index 13d3e2bec99..a0e2bccda71 100755 --- a/tests/scripts/thread-cert/Cert_9_2_15_PendingPartition.py +++ b/tests/scripts/thread-cert/Cert_9_2_15_PendingPartition.py @@ -138,7 +138,7 @@ def test(self): self.simulator.go(100) self.nodes[ROUTER2].start() - self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.simulator.go(config.ROUTER_RESET_DELAY) self.assertEqual(self.nodes[ROUTER2].get_state(), 'router') self.simulator.go(100) diff --git a/tests/scripts/thread-cert/Cert_9_2_16_ActivePendingPartition.py b/tests/scripts/thread-cert/Cert_9_2_16_ActivePendingPartition.py index e257f28d3d4..8515659d50c 100755 --- a/tests/scripts/thread-cert/Cert_9_2_16_ActivePendingPartition.py +++ b/tests/scripts/thread-cert/Cert_9_2_16_ActivePendingPartition.py @@ -142,7 +142,7 @@ def test(self): self.simulator.go(100) self.nodes[ROUTER2].start() - self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.simulator.go(config.ROUTER_RESET_DELAY) self.assertEqual(self.nodes[ROUTER2].get_state(), 'router') self.assertEqual(self.nodes[COMMISSIONER].get_network_name(), NETWORK_NAME_FINAL) diff --git a/tests/scripts/thread-cert/Makefile.am b/tests/scripts/thread-cert/Makefile.am deleted file mode 100644 index d6e4210c60d..00000000000 --- a/tests/scripts/thread-cert/Makefile.am +++ /dev/null @@ -1,399 +0,0 @@ -# -# Copyright (c) 2016-2017, 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. -# - -include $(abs_top_nlbuild_autotools_dir)/automake/pre.am - -LOG_DRIVER=$(abs_top_srcdir)/third_party/openthread-test-driver/test-driver - -EXTRA_DIST = \ - Cert_5_1_01_RouterAttach.py \ - Cert_5_1_02_ChildAddressTimeout.py \ - Cert_5_1_03_RouterAddressReallocation.py \ - Cert_5_1_04_RouterAddressReallocation.py \ - Cert_5_1_05_RouterAddressTimeout.py \ - Cert_5_1_06_RemoveRouterId.py \ - Cert_5_1_07_MaxChildCount.py \ - Cert_5_1_08_RouterAttachConnectivity.py \ - Cert_5_1_09_REEDAttachConnectivity.py \ - Cert_5_1_10_RouterAttachLinkQuality.py \ - Cert_5_1_11_REEDAttachLinkQuality.py \ - Cert_5_1_12_NewRouterNeighborSync.py \ - Cert_5_1_13_RouterReset.py \ - Cert_5_2_01_REEDAttach.py \ - Cert_5_2_03_LeaderReject2Hops.py \ - Cert_5_2_04_REEDUpgrade.py \ - Cert_5_2_05_AddressQuery.py \ - Cert_5_2_06_RouterDowngrade.py \ - Cert_5_2_07_REEDSynchronization.py \ - Cert_5_3_01_LinkLocal.py \ - Cert_5_3_02_RealmLocal.py \ - Cert_5_3_03_AddressQuery.py \ - Cert_5_3_04_AddressMapCache.py \ - Cert_5_3_05_RoutingLinkQuality.py \ - Cert_5_3_06_RouterIdMask.py \ - Cert_5_3_07_DuplicateAddress.py \ - Cert_5_3_08_ChildAddressSet.py \ - Cert_5_3_09_AddressQuery.py \ - Cert_5_3_10_AddressQuery.py \ - Cert_5_3_11_AddressQueryTimeoutIntervals.py \ - Cert_5_5_01_LeaderReboot.py \ - Cert_5_5_02_LeaderReboot.py \ - Cert_5_5_03_SplitMergeChildren.py \ - Cert_5_5_04_SplitMergeRouters.py \ - Cert_5_5_05_SplitMergeREED.py \ - Cert_5_5_07_SplitMergeThreeWay.py \ - Cert_5_6_01_NetworkDataRegisterBeforeAttachLeader.py \ - Cert_5_6_02_NetworkDataRegisterBeforeAttachRouter.py \ - Cert_5_6_03_NetworkDataRegisterAfterAttachLeader.py \ - Cert_5_6_04_NetworkDataRegisterAfterAttachRouter.py \ - Cert_5_6_05_NetworkDataRegisterAfterAttachRouter.py \ - Cert_5_6_06_NetworkDataExpiration.py \ - Cert_5_6_07_NetworkDataRequestREED.py \ - Cert_5_6_09_NetworkDataForwarding.py \ - Cert_5_7_01_CoapDiagCommands.py \ - Cert_5_7_02_CoapDiagCommands.py \ - Cert_5_7_03_CoapDiagCommands.py \ - Cert_5_8_02_KeyIncrement.py \ - Cert_5_8_03_KeyIncrementRollOver.py \ - Cert_5_8_04_SecurityPolicyTLV.py \ - Cert_6_1_01_RouterAttach.py \ - Cert_6_1_02_REEDAttach.py \ - Cert_6_1_03_RouterAttachConnectivity.py \ - Cert_6_1_04_REEDAttachConnectivity.py \ - Cert_6_1_05_REEDAttachConnectivity.py \ - Cert_6_1_07_RouterAttachLinkQuality.py \ - Cert_6_1_06_REEDAttachLinkQuality.py \ - Cert_6_2_01_NewPartition.py \ - Cert_6_2_02_NewPartition.py \ - Cert_6_3_01_OrphanReattach.py \ - Cert_6_3_02_NetworkDataUpdate.py \ - Cert_6_4_01_LinkLocal.py \ - Cert_6_4_02_RealmLocal.py \ - Cert_6_5_01_ChildResetReattach.py \ - Cert_6_5_02_ChildResetReattach.py \ - Cert_6_5_03_ChildResetSynchronize.py \ - Cert_6_6_01_KeyIncrement.py \ - Cert_6_6_02_KeyIncrementRollOver.py \ - Cert_7_1_01_BorderRouterAsLeader.py \ - Cert_7_1_02_BorderRouterAsRouter.py \ - Cert_7_1_03_BorderRouterAsLeader.py \ - Cert_7_1_04_BorderRouterAsRouter.py \ - Cert_7_1_05_BorderRouterAsRouter.py \ - Cert_7_1_06_BorderRouterAsLeader.py \ - Cert_7_1_07_BorderRouterAsLeader.py \ - Cert_7_1_08_BorderRouterAsFED.py \ - Cert_8_1_01_Commissioning.py \ - Cert_8_1_02_Commissioning.py \ - Cert_8_2_01_JoinerRouter.py \ - Cert_8_2_02_JoinerRouter.py \ - Cert_9_2_01_MGMTCommissionerGet.py \ - Cert_9_2_02_MGMTCommissionerSet.py \ - Cert_9_2_03_ActiveDatasetGet.py \ - Cert_9_2_04_ActiveDataset.py \ - Cert_9_2_05_ActiveDataset.py \ - Cert_9_2_06_DatasetDissemination.py \ - Cert_9_2_07_DelayTimer.py \ - Cert_9_2_08_PersistentDatasets.py \ - Cert_9_2_09_PendingPartition.py \ - Cert_9_2_10_PendingPartition.py \ - Cert_9_2_11_NetworkKey.py \ - Cert_9_2_12_Announce.py \ - Cert_9_2_13_EnergyScan.py \ - Cert_9_2_14_PanIdQuery.py \ - Cert_9_2_15_PendingPartition.py \ - Cert_9_2_16_ActivePendingPartition.py \ - Cert_9_2_17_Orphan.py \ - Cert_9_2_18_RollBackActiveTimestamp.py \ - Cert_9_2_19_PendingDatasetGet.py \ - coap.py \ - command.py \ - common.py \ - config.py \ - debug.py \ - dtls.py \ - ipv6.py \ - lowpan.py \ - mac802154.py \ - mesh_cop.py \ - message.py \ - mle.py \ - net_crypto.py \ - network_data.py \ - network_diag.py \ - network_layer.py \ - node.py \ - pcap.py \ - simulator.py \ - sniffer.py \ - sniffer_transport.py \ - test_anycast.py \ - test_anycast_locator.py \ - test_br_upgrade_router_role.py \ - test_coap.py \ - test_coap_block.py \ - test_coap_observe.py \ - test_coaps.py \ - test_common.py \ - test_crypto.py \ - test_dataset_updater.py \ - test_detach.py \ - test_diag.py \ - test_dns_client_config_auto_start.py \ - test_dnssd.py \ - test_dnssd_name_with_special_chars.py \ - test_history_tracker.py \ - test_inform_previous_parent_on_reattach.py \ - test_ipv6.py \ - test_ipv6_fragmentation.py \ - test_ipv6_source_selection.py \ - test_lowpan.py \ - test_mac802154.py \ - test_mac_scan.py \ - test_mle.py \ - test_mle_msg_key_seq_jump.py \ - test_netdata_publisher.py \ - test_network_data.py \ - test_network_layer.py \ - test_on_mesh_prefix.py \ - test_pbbr_aloc.py \ - test_ping.py \ - test_radio_filter.py \ - test_reed_address_solicit_rejected.py \ - test_reset.py \ - test_route_table.py \ - test_router_reattach.py \ - test_router_upgrade.py \ - test_service.py \ - test_set_mliid.py \ - test_srp_auto_host_address.py \ - test_srp_auto_start_mode.py \ - test_srp_client_remove_host.py \ - test_srp_client_save_server_info.py \ - test_srp_lease.py \ - test_srp_many_services_mtu_check.py \ - test_srp_name_conflicts.py \ - test_srp_register_single_service.py \ - test_srp_server_anycast_mode.py \ - test_srp_server_reboot_port.py \ - test_srp_sub_type.py \ - test_srp_ttl.py \ - test_zero_len_external_route.py \ - thread_cert.py \ - tlvs_parsing.py \ - thread_cert.py \ - pktverify/__init__.py \ - pktverify/addrs.py \ - pktverify/bytes.py \ - pktverify/coap.py \ - pktverify/consts.py \ - pktverify/decorators.py \ - pktverify/errors.py \ - pktverify/layer_fields.py \ - pktverify/layer_fields_container.py \ - pktverify/layers.py \ - pktverify/null_field.py \ - pktverify/packet.py \ - pktverify/packet_filter.py \ - pktverify/packet_verifier.py \ - pktverify/pcap_reader.py \ - pktverify/summary.py \ - pktverify/test_info.py \ - pktverify/utils.py \ - pktverify/verify_result.py \ - wpan.py \ - $(NULL) - -check_PROGRAMS = \ - $(NULL) - -check_SCRIPTS = \ - test_anycast.py \ - test_anycast_locator.py \ - test_br_upgrade_router_role.py \ - test_coap.py \ - test_coap_block.py \ - test_coap_observe.py \ - test_coaps.py \ - test_common.py \ - test_crypto.py \ - test_dataset_updater.py \ - test_detach.py \ - test_diag.py \ - test_dns_client_config_auto_start.py \ - test_dnssd.py \ - test_dnssd_name_with_special_chars.py \ - test_history_tracker.py \ - test_inform_previous_parent_on_reattach.py \ - test_ipv6.py \ - test_ipv6_fragmentation.py \ - test_ipv6_source_selection.py \ - test_lowpan.py \ - test_mac802154.py \ - test_mac_scan.py \ - test_mle.py \ - test_mle_msg_key_seq_jump.py \ - test_netdata_publisher.py \ - test_network_data.py \ - test_network_layer.py \ - test_on_mesh_prefix.py \ - test_pbbr_aloc.py \ - test_ping.py \ - test_radio_filter.py \ - test_reed_address_solicit_rejected.py \ - test_reset.py \ - test_route_table.py \ - test_router_reattach.py \ - test_router_upgrade.py \ - test_service.py \ - test_srp_auto_host_address.py \ - test_srp_auto_start_mode.py \ - test_srp_client_remove_host.py \ - test_srp_client_save_server_info.py \ - test_srp_lease.py \ - test_srp_many_services_mtu_check.py \ - test_srp_name_conflicts.py \ - test_srp_register_single_service.py \ - test_srp_server_anycast_mode.py \ - test_srp_server_reboot_port.py \ - test_srp_sub_type.py \ - test_srp_ttl.py \ - test_zero_len_external_route.py \ - Cert_5_1_01_RouterAttach.py \ - Cert_5_1_02_ChildAddressTimeout.py \ - Cert_5_1_03_RouterAddressReallocation.py \ - Cert_5_1_04_RouterAddressReallocation.py \ - Cert_5_1_05_RouterAddressTimeout.py \ - Cert_5_1_06_RemoveRouterId.py \ - Cert_5_1_07_MaxChildCount.py \ - Cert_5_1_08_RouterAttachConnectivity.py \ - Cert_5_1_09_REEDAttachConnectivity.py \ - Cert_5_1_10_RouterAttachLinkQuality.py \ - Cert_5_1_11_REEDAttachLinkQuality.py \ - Cert_5_1_12_NewRouterNeighborSync.py \ - Cert_5_1_13_RouterReset.py \ - Cert_5_2_01_REEDAttach.py \ - Cert_5_2_05_AddressQuery.py \ - Cert_5_2_06_RouterDowngrade.py \ - Cert_5_2_07_REEDSynchronization.py \ - Cert_5_2_04_REEDUpgrade.py \ - Cert_5_3_01_LinkLocal.py \ - Cert_5_3_02_RealmLocal.py \ - Cert_5_3_03_AddressQuery.py \ - Cert_5_3_04_AddressMapCache.py \ - Cert_5_3_05_RoutingLinkQuality.py \ - Cert_5_3_06_RouterIdMask.py \ - Cert_5_3_07_DuplicateAddress.py \ - Cert_5_3_08_ChildAddressSet.py \ - Cert_5_3_09_AddressQuery.py \ - Cert_5_3_10_AddressQuery.py \ - Cert_5_3_11_AddressQueryTimeoutIntervals.py \ - Cert_5_5_01_LeaderReboot.py \ - Cert_5_5_02_LeaderReboot.py \ - Cert_5_5_03_SplitMergeChildren.py \ - Cert_5_5_04_SplitMergeRouters.py \ - Cert_5_5_05_SplitMergeREED.py \ - Cert_5_5_07_SplitMergeThreeWay.py \ - Cert_5_6_01_NetworkDataRegisterBeforeAttachLeader.py \ - Cert_5_6_02_NetworkDataRegisterBeforeAttachRouter.py \ - Cert_5_6_03_NetworkDataRegisterAfterAttachLeader.py \ - Cert_5_6_04_NetworkDataRegisterAfterAttachRouter.py \ - Cert_5_6_05_NetworkDataRegisterAfterAttachRouter.py \ - Cert_5_6_06_NetworkDataExpiration.py \ - Cert_5_6_07_NetworkDataRequestREED.py \ - Cert_5_6_09_NetworkDataForwarding.py \ - Cert_5_7_01_CoapDiagCommands.py \ - Cert_5_7_02_CoapDiagCommands.py \ - Cert_5_7_03_CoapDiagCommands.py \ - Cert_5_8_02_KeyIncrement.py \ - Cert_5_8_03_KeyIncrementRollOver.py \ - Cert_5_8_04_SecurityPolicyTLV.py \ - Cert_6_1_01_RouterAttach.py \ - Cert_6_1_02_REEDAttach.py \ - Cert_6_1_03_RouterAttachConnectivity.py \ - Cert_6_1_04_REEDAttachConnectivity.py \ - Cert_6_1_05_REEDAttachConnectivity.py \ - Cert_6_1_06_REEDAttachLinkQuality.py \ - Cert_6_1_07_RouterAttachLinkQuality.py \ - Cert_6_2_01_NewPartition.py \ - Cert_6_2_02_NewPartition.py \ - Cert_6_3_01_OrphanReattach.py \ - Cert_6_3_02_NetworkDataUpdate.py \ - Cert_6_4_01_LinkLocal.py \ - Cert_6_4_02_RealmLocal.py \ - Cert_6_5_01_ChildResetReattach.py \ - Cert_6_5_02_ChildResetReattach.py \ - Cert_6_5_03_ChildResetSynchronize.py \ - Cert_6_6_01_KeyIncrement.py \ - Cert_6_6_02_KeyIncrementRollOver.py \ - Cert_5_2_03_LeaderReject2Hops.py \ - Cert_7_1_01_BorderRouterAsLeader.py \ - Cert_7_1_02_BorderRouterAsRouter.py \ - Cert_7_1_03_BorderRouterAsLeader.py \ - Cert_7_1_04_BorderRouterAsRouter.py \ - Cert_7_1_05_BorderRouterAsRouter.py \ - Cert_7_1_06_BorderRouterAsLeader.py \ - Cert_7_1_07_BorderRouterAsLeader.py \ - Cert_7_1_08_BorderRouterAsFED.py \ - Cert_8_1_01_Commissioning.py \ - Cert_8_1_02_Commissioning.py \ - Cert_8_2_01_JoinerRouter.py \ - Cert_8_2_02_JoinerRouter.py \ - Cert_9_2_01_MGMTCommissionerGet.py \ - Cert_9_2_02_MGMTCommissionerSet.py \ - Cert_9_2_03_ActiveDatasetGet.py \ - Cert_9_2_04_ActiveDataset.py \ - Cert_9_2_05_ActiveDataset.py \ - Cert_9_2_06_DatasetDissemination.py \ - Cert_9_2_07_DelayTimer.py \ - Cert_9_2_08_PersistentDatasets.py \ - Cert_9_2_09_PendingPartition.py \ - Cert_9_2_10_PendingPartition.py \ - Cert_9_2_11_NetworkKey.py \ - Cert_9_2_12_Announce.py \ - Cert_9_2_13_EnergyScan.py \ - Cert_9_2_14_PanIdQuery.py \ - Cert_9_2_15_PendingPartition.py \ - Cert_9_2_16_ActivePendingPartition.py \ - Cert_9_2_17_Orphan.py \ - Cert_9_2_18_RollBackActiveTimestamp.py \ - Cert_9_2_19_PendingDatasetGet.py \ - $(NULL) - -TESTS_ENVIRONMENT = \ - export \ - top_builddir='$(top_builddir)' \ - top_srcdir='$(top_srcdir)' \ - VERBOSE=1; \ - $(NULL) - -TESTS = \ - $(check_PROGRAMS) \ - $(check_SCRIPTS) \ - $(NULL) - -include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/tests/scripts/thread-cert/__init__.py b/tests/scripts/thread-cert/__init__.py old mode 100755 new mode 100644 diff --git a/tests/scripts/thread-cert/backbone/test_bmlr.py b/tests/scripts/thread-cert/backbone/test_bmlr.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/backbone/test_dua_dad.py b/tests/scripts/thread-cert/backbone/test_dua_dad.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/backbone/test_dua_routing.py b/tests/scripts/thread-cert/backbone/test_dua_routing.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/backbone/test_dua_routing_med.py b/tests/scripts/thread-cert/backbone/test_dua_routing_med.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/backbone/test_mle_must_not_send_icmpv6_destination_unreachable.py b/tests/scripts/thread-cert/backbone/test_mle_must_not_send_icmpv6_destination_unreachable.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/backbone/test_mlr_multicast_routing.py b/tests/scripts/thread-cert/backbone/test_mlr_multicast_routing.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/backbone/test_mlr_multicast_routing_across_thread_pans.py b/tests/scripts/thread-cert/backbone/test_mlr_multicast_routing_across_thread_pans.py old mode 100644 new mode 100755 index 8da0c1c18eb..cf7405edc41 --- a/tests/scripts/thread-cert/backbone/test_mlr_multicast_routing_across_thread_pans.py +++ b/tests/scripts/thread-cert/backbone/test_mlr_multicast_routing_across_thread_pans.py @@ -143,13 +143,25 @@ def test(self): self.collect_ipaddrs() self.collect_rloc16s() - # ping MA1 from Host could get replied from R1 and R2 + # ping MA1 from Host could generate a reply from R1 and R2 self.assertTrue(self.nodes[HOST].ping(MA1, backbone=True, ttl=5)) self.simulator.go(WAIT_REDUNDANCE) + self.verify_border_routing_counters(self.nodes[PBBR1], {'inbound_multicast': 1, 'outbound_unicast': 1}) + self.verify_border_routing_counters(self.nodes[PBBR2], {'inbound_multicast': 1, 'outbound_unicast': 1}) - # ping MA2 from R1 could get replied from Host and R2 + # ping MA2 from R1 could generate a reply from Host and R2 self.assertTrue(self.nodes[ROUTER1].ping(MA2)) self.simulator.go(WAIT_REDUNDANCE) + self.verify_border_routing_counters(self.nodes[PBBR1], {'inbound_unicast': 2, 'outbound_multicast': 1}) + self.verify_border_routing_counters(self.nodes[PBBR2], {'inbound_multicast': 1, 'outbound_unicast': 1}) + + # ping MA2 from R1's MLE-ID shouldn't generate a reply from Host or R2 + self.assertFalse(self.nodes[ROUTER1].ping(MA2, interface=self.nodes[ROUTER1].get_mleid())) + self.simulator.go(WAIT_REDUNDANCE) + + # ping MA2 from R1's LLA shouldn't generate a reply from Host or R2 + self.assertFalse(self.nodes[ROUTER1].ping(MA2, interface=self.nodes[ROUTER1].get_linklocal())) + self.simulator.go(WAIT_REDUNDANCE) def verify(self, pv: PacketVerifier): pkts = pv.pkts @@ -211,6 +223,35 @@ def verify(self, pv: PacketVerifier): pkts.filter_wpan_src64(ROUTER2).filter_ipv6_src_dst( ROUTER2_DUA, ROUTER1_DUA).filter_ping_reply(identifier=ping_ma2.icmpv6.echo.identifier).must_next() + # + # Verify pinging MA2 from R1's MLE-ID will not be forwarded to the Backbone link + # + + # ROUTER1 should send the multicast ping request + ping_ma2_2 = pkts.filter_wpan_src64(ROUTER1).filter_AMPLFMA(mpl_seed_id=ROUTER1_RLOC16).filter_ping_request( + identifier=ping_ma2.icmpv6.echo.identifier + 1).must_next() + + # PBBR1 shouldn't forward the multicast ping request to the Backbone link + pkts.filter_eth_src(PBBR1_ETH).filter_ping_request(ping_ma2_2.icmpv6.echo.identifier).must_not_next() + + # + # Verify pinging MA2 from R1's Link-Local address will not be forwarded to the Backbone link + # + + # ROUTER1 should send the multicast ping request + ping_ma2_3 = pkts.filter_wpan_src64(ROUTER1).filter_AMPLFMA(mpl_seed_id=ROUTER1_RLOC16).filter_ping_request( + identifier=ping_ma2.icmpv6.echo.identifier + 1).must_next() + + # PBBR1 shouldn't forward the multicast ping request to the Backbone link + pkts.filter_eth_src(PBBR1_ETH).filter_ping_request(ping_ma2_3.icmpv6.echo.identifier).must_not_next() + + def verify_border_routing_counters(self, br, expect_delta): + delta_counters = br.read_border_routing_counters_delta() + self.assertEqual(set(delta_counters.keys()), set(expect_delta.keys())) + for key in delta_counters: + self.assertGreaterEqual(delta_counters[key][0], expect_delta[key]) + self.assertGreater(delta_counters[key][1], 0) + if __name__ == '__main__': unittest.main() diff --git a/tests/scripts/thread-cert/backbone/test_mlr_multicast_routing_commissioner_timeout.py b/tests/scripts/thread-cert/backbone/test_mlr_multicast_routing_commissioner_timeout.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/backbone/test_mlr_multicast_routing_timeout.py b/tests/scripts/thread-cert/backbone/test_mlr_multicast_routing_timeout.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/backbone/test_ndproxy.py b/tests/scripts/thread-cert/backbone/test_ndproxy.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/LowPower/v1_2_LowPower_5_3_01_SSEDAttachment_BR.py b/tests/scripts/thread-cert/border_router/LowPower/v1_2_LowPower_5_3_01_SSEDAttachment_BR.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/MATN/MATN_02_MLRFirstUse.py b/tests/scripts/thread-cert/border_router/MATN/MATN_02_MLRFirstUse.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/MATN/MATN_03_InvalidCommissionerDeregistration.py b/tests/scripts/thread-cert/border_router/MATN/MATN_03_InvalidCommissionerDeregistration.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/MATN/MATN_04_MulticastListenerTimeout.py b/tests/scripts/thread-cert/border_router/MATN/MATN_04_MulticastListenerTimeout.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/MATN/MATN_05_ReregistrationToSameMulticastGroup.py b/tests/scripts/thread-cert/border_router/MATN/MATN_05_ReregistrationToSameMulticastGroup.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/MATN/MATN_09_DefaultBRMulticastForwarding.py b/tests/scripts/thread-cert/border_router/MATN/MATN_09_DefaultBRMulticastForwarding.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/MATN/MATN_12_HopLimitProcessing.py b/tests/scripts/thread-cert/border_router/MATN/MATN_12_HopLimitProcessing.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/MATN/MATN_15_ChangeOfPrimaryBBRTriggersRegistration.py b/tests/scripts/thread-cert/border_router/MATN/MATN_15_ChangeOfPrimaryBBRTriggersRegistration.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/MATN/MATN_16_LargeNumberOfMulticastGroupSubscriptionsToBBR.py b/tests/scripts/thread-cert/border_router/MATN/MATN_16_LargeNumberOfMulticastGroupSubscriptionsToBBR.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/nat64/test_multi_border_routers.py b/tests/scripts/thread-cert/border_router/nat64/test_multi_border_routers.py old mode 100644 new mode 100755 index 8ce9e0de2cb..7a4791d44e7 --- a/tests/scripts/thread-cert/border_router/nat64/test_multi_border_routers.py +++ b/tests/scripts/thread-cert/border_router/nat64/test_multi_border_routers.py @@ -30,14 +30,12 @@ import config import thread_cert +import enum # Test description: -# This test verifies that a single NAT64 prefix is advertised when there are +# This test verifies that a single NAT64 prefix is published when there are # multiple Border Routers in the same Thread and infrastructure network. # -# TODO: add checks for outbound connectivity from Thread device to IPv4 host -# after OTBR change is ready. -# # Topology: # ----------------(eth)-------------------------- # | | | @@ -51,6 +49,13 @@ BR2 = 3 HOST = 4 +NAT64_PREFIX_REFRESH_DELAY = 305 + +NAT64_STATE_DISABLED = 'disabled' +NAT64_STATE_NOT_RUNNING = 'not_running' +NAT64_STATE_IDLE = 'idle' +NAT64_STATE_ACTIVE = 'active' + class Nat64MultiBorderRouter(thread_cert.TestCase): USE_MESSAGE_FACTORY = False @@ -89,7 +94,12 @@ def test(self): self.simulator.go(5) br1.start() + # When feature flag is enabled, NAT64 might be disabled by default. So + # ensure NAT64 is enabled here. + br1.nat64_set_enabled(True) self.simulator.go(config.LEADER_STARTUP_DELAY) + br1.bash("service bind9 stop") + self.simulator.go(NAT64_PREFIX_REFRESH_DELAY) self.assertEqual('leader', br1.get_state()) router.start() @@ -97,44 +107,156 @@ def test(self): self.assertEqual('router', router.get_state()) # - # Case 1. BR2 joins the network later and it will not add - # its local nat64 prefix to Network Data. + # Case 1. BR2 with an infrastructure prefix joins the network later and + # it will add the infrastructure nat64 prefix to Network Data. + # Note: NAT64 translator will be bypassed. # br2.start() + # When feature flag is enabled, NAT64 might be disabled by default. So + # ensure NAT64 is enabled here. + br2.nat64_set_enabled(True) self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) self.assertEqual('router', br2.get_state()) - # Only 1 NAT64 prefix in Network Data. - self.simulator.go(30) + self.simulator.go(10) + self.assertNotEqual(br1.get_br_favored_nat64_prefix(), br2.get_br_favored_nat64_prefix()) + br1_local_nat64_prefix = br1.get_br_nat64_prefix() + br2_infra_nat64_prefix = br2.get_br_favored_nat64_prefix() + self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1) - self.assertEqual(len(br2.get_netdata_nat64_prefix()), 1) - self.assertEqual(br1.get_netdata_nat64_prefix()[0], br2.get_netdata_nat64_prefix()[0]) nat64_prefix = br1.get_netdata_nat64_prefix()[0] + self.assertEqual(nat64_prefix, br2_infra_nat64_prefix) + self.assertNotEqual(nat64_prefix, br1_local_nat64_prefix) + self.assertDictIncludes(br1.nat64_state, { + 'PrefixManager': NAT64_STATE_IDLE, + 'Translator': NAT64_STATE_NOT_RUNNING + }) + self.assertDictIncludes(br2.nat64_state, { + 'PrefixManager': NAT64_STATE_ACTIVE, + 'Translator': NAT64_STATE_NOT_RUNNING + }) - # The NAT64 prefix in Network Data is same as BR1's local NAT64 prefix. - br1_nat64_prefix = br1.get_br_nat64_prefix() - br2_nat64_prefix = br2.get_br_nat64_prefix() - self.assertEqual(nat64_prefix, br1_nat64_prefix) - self.assertNotEqual(nat64_prefix, br2_nat64_prefix) + # + # Case 2. Disable NAT64 on BR2. + # BR1 will add its local nat64 prefix. + # + br2.nat64_set_enabled(False) + self.simulator.go(10) + + self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1) + nat64_prefix = br1.get_netdata_nat64_prefix()[0] + self.assertEqual(nat64_prefix, br1_local_nat64_prefix) + self.assertDictIncludes(br1.nat64_state, { + 'PrefixManager': NAT64_STATE_ACTIVE, + 'Translator': NAT64_STATE_ACTIVE + }) + self.assertDictIncludes(br2.nat64_state, { + 'PrefixManager': NAT64_STATE_DISABLED, + 'Translator': NAT64_STATE_DISABLED + }) # - # Case 2. Disable and re-enable border routing on BR1. + # Case 3. Re-enables BR2 with a local prefix and it will not add + # its local nat64 prefix to Network Data. # - br1.disable_br() - self.simulator.go(30) + br2.bash("service bind9 stop") + self.simulator.go(5) + br2.nat64_set_enabled(True) + + self.simulator.go(10) + self.assertNotEqual(br2_infra_nat64_prefix, br2.get_br_favored_nat64_prefix()) + br2_local_nat64_prefix = br2.get_br_nat64_prefix() - # BR1 withdraws its prefix and BR2 advertises its prefix. self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1) - self.assertEqual(br2_nat64_prefix, br1.get_netdata_nat64_prefix()[0]) - self.assertNotEqual(br1_nat64_prefix, br1.get_netdata_nat64_prefix()[0]) + nat64_prefix = br1.get_netdata_nat64_prefix()[0] + self.assertEqual(nat64_prefix, br1_local_nat64_prefix) + self.assertNotEqual(nat64_prefix, br2_local_nat64_prefix) + self.assertDictIncludes(br1.nat64_state, { + 'PrefixManager': NAT64_STATE_ACTIVE, + 'Translator': NAT64_STATE_ACTIVE + }) + self.assertDictIncludes(br2.nat64_state, { + 'PrefixManager': NAT64_STATE_IDLE, + 'Translator': NAT64_STATE_NOT_RUNNING + }) - br1.enable_br() - self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) + # + # Case 4. Disable NAT64 on BR1. + # BR1 withdraws its local prefix and BR2 advertises its local prefix. + # + br1.nat64_set_enabled(False) + + self.simulator.go(10) + self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1) + nat64_prefix = br1.get_netdata_nat64_prefix()[0] + self.assertEqual(br2_local_nat64_prefix, nat64_prefix) + self.assertNotEqual(br1_local_nat64_prefix, nat64_prefix) + self.assertDictIncludes(br1.nat64_state, { + 'PrefixManager': NAT64_STATE_DISABLED, + 'Translator': NAT64_STATE_DISABLED + }) + self.assertDictIncludes(br2.nat64_state, { + 'PrefixManager': NAT64_STATE_ACTIVE, + 'Translator': NAT64_STATE_ACTIVE + }) + + # + # Case 5. Re-enable NAT64 on BR1. + # NAT64 prefix in Network Data is still BR2's local prefix. + # + br1.nat64_set_enabled(True) + + self.simulator.go(10) + self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1) + nat64_prefix = br1.get_netdata_nat64_prefix()[0] + self.assertEqual(br2_local_nat64_prefix, nat64_prefix) + self.assertNotEqual(br1_local_nat64_prefix, nat64_prefix) + self.assertDictIncludes(br1.nat64_state, { + 'PrefixManager': NAT64_STATE_IDLE, + 'Translator': NAT64_STATE_NOT_RUNNING + }) + self.assertDictIncludes(br2.nat64_state, { + 'PrefixManager': NAT64_STATE_ACTIVE, + 'Translator': NAT64_STATE_ACTIVE + }) + + # + # Case 6. Disable the routing manager should stop NAT64 prefix manager. + # + # + br2.disable_br() + self.simulator.go(10) + self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1) + nat64_prefix = br1.get_netdata_nat64_prefix()[0] + self.assertEqual(br1_local_nat64_prefix, nat64_prefix) + self.assertNotEqual(br2_local_nat64_prefix, nat64_prefix) + self.assertDictIncludes(br1.nat64_state, { + 'PrefixManager': NAT64_STATE_ACTIVE, + 'Translator': NAT64_STATE_ACTIVE + }) + self.assertDictIncludes(br2.nat64_state, { + 'PrefixManager': NAT64_STATE_NOT_RUNNING, + 'Translator': NAT64_STATE_NOT_RUNNING + }) - # NAT64 prefix in Network Data is still advertised by BR2. + # + # Case 7. Enable the routing manager the BR should start NAT64 prefix manager if the prefix manager is enabled. + # + # + br2.enable_br() + self.simulator.go(10) self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1) - self.assertEqual(br2_nat64_prefix, br1.get_netdata_nat64_prefix()[0]) - self.assertNotEqual(br1_nat64_prefix, br1.get_netdata_nat64_prefix()[0]) + nat64_prefix = br1.get_netdata_nat64_prefix()[0] + self.assertEqual(br1_local_nat64_prefix, nat64_prefix) + self.assertNotEqual(br2_local_nat64_prefix, nat64_prefix) + self.assertDictIncludes(br1.nat64_state, { + 'PrefixManager': NAT64_STATE_ACTIVE, + 'Translator': NAT64_STATE_ACTIVE + }) + self.assertDictIncludes(br2.nat64_state, { + 'PrefixManager': NAT64_STATE_IDLE, + 'Translator': NAT64_STATE_NOT_RUNNING + }) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/border_router/nat64/test_single_border_router.py b/tests/scripts/thread-cert/border_router/nat64/test_single_border_router.py old mode 100644 new mode 100755 index a301cdaea34..4a69b2a9370 --- a/tests/scripts/thread-cert/border_router/nat64/test_single_border_router.py +++ b/tests/scripts/thread-cert/border_router/nat64/test_single_border_router.py @@ -30,12 +30,19 @@ import config import thread_cert +import ipv6 + +import ipaddress + +# For NAT64 connectivity tests +import socket +import select # Test description: -# This test verifies the advertisement of NAT64 prefix in Thread network. -# -# TODO: add checks for outbound connectivity from Thread device to IPv4 host -# after OTBR change is ready. +# This test verifies publishing the local NAT64 prefix in Thread network +# when no NAT64 prefix found on infrastructure interface. +# It also verifies the outbound connectivity from a Thread device to +# an IPv4 host address. # # Topology: # ----------------(eth)-------------------- @@ -49,9 +56,12 @@ ROUTER = 2 HOST = 3 -# The prefix is set small enough that a random-generated NAT64 prefix is very -# likely greater than it. So that the BR will remove the random-generated one. +# The prefix is set small enough that a random-generated ULA NAT64 prefix is very +# likely greater than it. So the BR will remove the random-generated one. SMALL_NAT64_PREFIX = "fd00:00:00:01:00:00::/96" +# The prefix is set larger than a random-generated ULA NAT64 prefix. +# So the BR will remove the random-generated one. +LARGE_NAT64_PREFIX = "ff00:00:00:01:00:00::/96" class Nat64SingleBorderRouter(thread_cert.TestCase): @@ -75,6 +85,28 @@ class Nat64SingleBorderRouter(thread_cert.TestCase): }, } + def get_host_ip(self): + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.setblocking(False) + # Note: The address is not important, we use this function to get the host ip address. + sock.connect(('8.8.8.8', 54321)) + host_ip, _ = sock.getsockname() + sock.close() + return host_ip + + def receive_from(self, sock, timeout_seconds): + ready = select.select([sock], [], [], timeout_seconds) + if ready[0]: + return sock.recv(1024) + else: + raise AssertionError("No data recevied") + + def listen_udp(self, addr, port): + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.setblocking(False) + sock.bind((addr, port)) + return sock + def test(self): br = self.nodes[BR] router = self.nodes[ROUTER] @@ -84,7 +116,12 @@ def test(self): self.simulator.go(5) br.start() + # When feature flag is enabled, NAT64 might be disabled by default. So + # ensure NAT64 is enabled here. + br.nat64_set_enabled(True) self.simulator.go(config.LEADER_STARTUP_DELAY) + br.bash("service bind9 stop") + self.simulator.go(330) self.assertEqual('leader', br.get_state()) router.start() @@ -103,10 +140,10 @@ def test(self): # # Case 2. - # User adds a smaller NAT64 prefix and the local prefix is withdrawn. + # User adds a smaller NAT64 prefix (same preference) and the local prefix is withdrawn. # User removes the smaller NAT64 prefix and the local prefix is re-added. # - br.add_route(SMALL_NAT64_PREFIX, stable=False, nat64=True) + br.add_route(SMALL_NAT64_PREFIX, stable=False, nat64=True, prf='low') br.register_netdata() self.simulator.go(5) @@ -115,13 +152,32 @@ def test(self): br.remove_route(SMALL_NAT64_PREFIX) br.register_netdata() + self.simulator.go(10) + + self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) + self.assertEqual(local_nat64_prefix, br.get_netdata_nat64_prefix()[0]) + + # + # Case 3. + # User adds a larger NAT64 prefix (higher preference) and the local prefix is withdrawn. + # User removes the larger NAT64 prefix and the local prefix is re-added. + # + br.add_route(LARGE_NAT64_PREFIX, stable=False, nat64=True, prf='med') + br.register_netdata() self.simulator.go(5) + self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) + self.assertNotEqual(local_nat64_prefix, br.get_netdata_nat64_prefix()[0]) + + br.remove_route(LARGE_NAT64_PREFIX) + br.register_netdata() + self.simulator.go(10) + self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) self.assertEqual(local_nat64_prefix, br.get_netdata_nat64_prefix()[0]) # - # Case 3. Disable and re-enable border routing on the border router. + # Case 4. Disable and re-enable border routing on the border router. # br.disable_br() self.simulator.go(5) @@ -132,12 +188,57 @@ def test(self): br.enable_br() self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) - # Same NAT64 prefix is advertised to Network Data. - self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) - self.assertEqual(nat64_prefix, br.get_netdata_nat64_prefix()[0]) + # + # Case 5. NAT64 connectivity and counters. + # + host_ip = self.get_host_ip() + self.assertTrue(router.ping(ipaddr=host_ip)) + + mappings = br.nat64_mappings + self.assertEqual(mappings[0]['counters']['ICMP']['4to6']['packets'], 1) + self.assertEqual(mappings[0]['counters']['ICMP']['6to4']['packets'], 1) + self.assertEqual(mappings[0]['counters']['total']['4to6']['packets'], 1) + self.assertEqual(mappings[0]['counters']['total']['6to4']['packets'], 1) + + counters = br.nat64_counters + self.assertEqual(counters['protocol']['ICMP']['4to6']['packets'], 1) + self.assertEqual(counters['protocol']['ICMP']['6to4']['packets'], 1) + + sock = self.listen_udp('0.0.0.0', 54321) + router.udp_start('::', 54321) + # We can use IPv4 addresses for commands like UDP send. + # The address will be converted to an IPv6 address by CLI. + router.udp_send(10, host_ip, 54321) + self.assertTrue(len(self.receive_from(sock, timeout_seconds=1)) == 10) + + sock.close() + + counters = br.nat64_counters + self.assertEqual(counters['protocol']['UDP']['6to4']['packets'], 1) + mappings = br.nat64_mappings + self.assertEqual(mappings[0]['counters']['UDP']['6to4']['packets'], 1) + + # We should be able to get a IPv4 mapped IPv6 address. + # 203.0.113.1, RFC5737 TEST-NET-3, should be unreachable. + mapped_ip6_address = str( + ipv6.synthesize_ip6_address(ipaddress.IPv6Network(nat64_prefix), ipaddress.IPv4Address('203.0.113.1'))) + self.assertFalse(router.ping(ipaddr=mapped_ip6_address)) + + mappings = br.nat64_mappings + self.assertEqual(mappings[0]['counters']['ICMP']['4to6']['packets'], 1) + self.assertEqual(mappings[0]['counters']['ICMP']['6to4']['packets'], 2) + self.assertEqual(mappings[0]['counters']['total']['4to6']['packets'], 1) + self.assertEqual(mappings[0]['counters']['total']['6to4']['packets'], 3) + + counters = br.nat64_counters + self.assertEqual(counters['protocol']['ICMP']['4to6']['packets'], 1) + self.assertEqual(counters['protocol']['ICMP']['6to4']['packets'], 2) # - # Case 4. Disable and re-enable ethernet on the border router. + # Case 6. Disable and re-enable ethernet on the border router. + # Note: disable_ether will remove default route but enable_ether won't add it back, + # NAT64 connectivity tests will fail after this. + # TODO: Add a default IPv4 route after enable_ether. # br.disable_ether() self.simulator.go(5) @@ -151,6 +252,9 @@ def test(self): # Same NAT64 prefix is advertised to Network Data. self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) self.assertEqual(nat64_prefix, br.get_netdata_nat64_prefix()[0]) + # Same NAT64 prefix is advertised to Network Data. + self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) + self.assertEqual(nat64_prefix, br.get_netdata_nat64_prefix()[0]) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/border_router/nat64/test_upstream_dns.py b/tests/scripts/thread-cert/border_router/nat64/test_upstream_dns.py new file mode 100755 index 00000000000..617ea0f1901 --- /dev/null +++ b/tests/scripts/thread-cert/border_router/nat64/test_upstream_dns.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2023, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +import unittest + +import config +import thread_cert + +import ipaddress +import shlex + +# Test description: +# This test verifies forwarding DNS queries sent by 'Router' by using +# a record resolved by BIND9 server. +# +# Topology: +# ----------------(eth)-------------------- +# | | +# BR (Leader) HOST +# | +# ROUTER +# + +BR = 1 +ROUTER = 2 +HOST = 3 + +TEST_DOMAIN = 'test.domain' +TEST_DOMAIN_IP6_ADDRESSES = {'2001:db8::1'} + +TEST_DOMAIN_BIND_CONF = f''' +zone "{TEST_DOMAIN}" {{ type master; file "/etc/bind/db.test.domain"; }}; +''' + +TEST_DOMAIN_BIND_ZONE = f''' +$TTL 24h +@ IN SOA {TEST_DOMAIN} test.{TEST_DOMAIN}. ( 20230330 86400 300 604800 3600 ) +@ IN NS {TEST_DOMAIN}. +''' + '\n'.join(f'@ IN AAAA {addr}' for addr in TEST_DOMAIN_IP6_ADDRESSES) + + +class UpstreamDns(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + + TOPOLOGY = { + BR: { + 'name': 'BR', + 'allowlist': [ROUTER], + 'is_otbr': True, + 'version': '1.3', + }, + ROUTER: { + 'name': 'Router', + 'allowlist': [BR], + 'version': '1.3', + }, + HOST: { + 'name': 'Host', + 'is_host': True + }, + } + + def test(self): + br = self.nodes[BR] + router = self.nodes[ROUTER] + host = self.nodes[HOST] + + host.start(start_radvd=False) + self.simulator.go(5) + + br.start() + # When feature flag is enabled, NAT64 might be disabled by default. So + # ensure NAT64 is enabled here. + self.simulator.go(config.LEADER_STARTUP_DELAY) + self.assertEqual('leader', br.get_state()) + + br.nat64_set_enabled(True) + br.srp_server_set_enabled(True) + + br.bash('service bind9 stop') + + br.bash(shlex.join(['echo', TEST_DOMAIN_BIND_CONF]) + ' >> /etc/bind/named.conf.local') + br.bash(shlex.join(['echo', TEST_DOMAIN_BIND_ZONE]) + ' >> /etc/bind/db.test.domain') + + br.bash('service bind9 start') + + router.start() + self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.assertEqual('router', router.get_state()) + + self.simulator.go(10) + router.srp_client_enable_auto_start_mode() + + # verify the server can forward the DNS query to upstream server. + self._verify_upstream_dns(br, router) + + def _verify_upstream_dns(self, br, ed): + upstream_dns_enabled = br.dns_upstream_query_state + if not upstream_dns_enabled: + br.dns_upstream_query_state = True + self.assertTrue(br.dns_upstream_query_state) + + resolved_names = ed.dns_resolve(TEST_DOMAIN) + self.assertEqual(len(resolved_names), len(TEST_DOMAIN_IP6_ADDRESSES)) + for record in resolved_names: + self.assertIn(ipaddress.IPv6Address(record[0]).compressed, TEST_DOMAIN_IP6_ADDRESSES) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/scripts/thread-cert/border_router/nat64/test_with_infrastructure_prefix.py b/tests/scripts/thread-cert/border_router/nat64/test_with_infrastructure_prefix.py new file mode 100755 index 00000000000..465b5a7a177 --- /dev/null +++ b/tests/scripts/thread-cert/border_router/nat64/test_with_infrastructure_prefix.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +# +# 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. +# +import unittest + +import config +import thread_cert + +# Test description: +# This test verifies publishing infrastructure NAT64 prefix in Thread network. +# +# +# Topology: +# +# ----------------(eth)-------------------- +# | +# BR (with DNS64 on infrastructure interface) +# | +# ROUTER +# + +BR = 1 +ROUTER = 2 + +# The prefix is set smaller than the default infrastructure NAT64 prefix. +SMALL_NAT64_PREFIX = "2000:0:0:1:0:0::/96" + +NAT64_PREFIX_REFRESH_DELAY = 305 + +NAT64_STATE_DISABLED = 'disabled' +NAT64_STATE_NOT_RUNNING = 'not_running' +NAT64_STATE_IDLE = 'idle' +NAT64_STATE_ACTIVE = 'active' + + +class Nat64SingleBorderRouter(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + + TOPOLOGY = { + BR: { + 'name': 'BR', + 'allowlist': [ROUTER], + 'is_otbr': True, + 'version': '1.2', + }, + ROUTER: { + 'name': 'Router', + 'allowlist': [BR], + 'version': '1.2', + }, + } + + def test(self): + br = self.nodes[BR] + router = self.nodes[ROUTER] + + br.start() + # When feature flag is enabled, NAT64 might be disabled by default. So + # ensure NAT64 is enabled here. + br.nat64_set_enabled(True) + self.simulator.go(config.LEADER_STARTUP_DELAY) + self.assertEqual('leader', br.get_state()) + + router.start() + self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.assertEqual('router', router.get_state()) + + # Case 1 BR advertise the infrastructure prefix + infra_nat64_prefix = br.get_br_favored_nat64_prefix() + + self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) + nat64_prefix = br.get_netdata_nat64_prefix()[0] + self.assertEqual(nat64_prefix, infra_nat64_prefix) + self.assertDictIncludes(br.nat64_state, { + 'PrefixManager': NAT64_STATE_ACTIVE, + 'Translator': NAT64_STATE_NOT_RUNNING + }) + + # Case 2 Withdraw infrastructure prefix when a smaller prefix in medium + # preference is present + br.add_route(SMALL_NAT64_PREFIX, stable=False, nat64=True, prf='med') + br.register_netdata() + self.simulator.go(5) + + self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) + self.assertNotEqual(infra_nat64_prefix, br.get_netdata_nat64_prefix()[0]) + self.assertDictIncludes(br.nat64_state, { + 'PrefixManager': NAT64_STATE_IDLE, + 'Translator': NAT64_STATE_NOT_RUNNING + }) + + br.remove_route(SMALL_NAT64_PREFIX) + br.register_netdata() + self.simulator.go(10) + + self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) + self.assertEqual(nat64_prefix, infra_nat64_prefix) + + # Case 3 No change when a smaller prefix in low preference is present + br.add_route(SMALL_NAT64_PREFIX, stable=False, nat64=True, prf='low') + br.register_netdata() + self.simulator.go(5) + + self.assertEqual(len(br.get_netdata_nat64_prefix()), 2) + self.assertEqual(br.get_netdata_nat64_prefix(), [infra_nat64_prefix, SMALL_NAT64_PREFIX]) + self.assertDictIncludes(br.nat64_state, { + 'PrefixManager': NAT64_STATE_ACTIVE, + 'Translator': NAT64_STATE_NOT_RUNNING + }) + + br.remove_route(SMALL_NAT64_PREFIX) + br.register_netdata() + self.simulator.go(5) + + # Case 4 Infrastructure nat64 prefix no longer presents + br.bash("service bind9 stop") + self.simulator.go(NAT64_PREFIX_REFRESH_DELAY) + + local_nat64_prefix = br.get_br_nat64_prefix() + self.assertNotEqual(local_nat64_prefix, infra_nat64_prefix) + self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) + self.assertEqual(br.get_netdata_nat64_prefix()[0], local_nat64_prefix) + self.assertDictIncludes(br.nat64_state, { + 'PrefixManager': NAT64_STATE_ACTIVE, + 'Translator': NAT64_STATE_ACTIVE + }) + + # Case 5 Infrastructure nat64 prefix is recovered + br.bash("service bind9 start") + self.simulator.go(NAT64_PREFIX_REFRESH_DELAY) + + self.assertEqual(br.get_br_favored_nat64_prefix(), infra_nat64_prefix) + self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) + self.assertEqual(br.get_netdata_nat64_prefix()[0], infra_nat64_prefix) + self.assertDictIncludes(br.nat64_state, { + 'PrefixManager': NAT64_STATE_ACTIVE, + 'Translator': NAT64_STATE_NOT_RUNNING + }) + + # Case 6 Change infrastructure nat64 prefix + br.bash("sed -i 's/dns64 /\/\/dns64 /' /etc/bind/named.conf.options") + br.bash("sed -i '/\/\/dns64 /a dns64 " + SMALL_NAT64_PREFIX + " {};' /etc/bind/named.conf.options") + br.bash("service bind9 restart") + self.simulator.go(NAT64_PREFIX_REFRESH_DELAY) + + self.assertEqual(br.get_br_favored_nat64_prefix(), SMALL_NAT64_PREFIX) + self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) + self.assertEqual(br.get_netdata_nat64_prefix()[0], SMALL_NAT64_PREFIX) + self.assertDictIncludes(br.nat64_state, { + 'PrefixManager': NAT64_STATE_ACTIVE, + 'Translator': NAT64_STATE_NOT_RUNNING + }) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/scripts/thread-cert/border_router/test_advertising_proxy.py b/tests/scripts/thread-cert/border_router/test_advertising_proxy.py index 0c088dce9ab..5c66bf9730d 100755 --- a/tests/scripts/thread-cert/border_router/test_advertising_proxy.py +++ b/tests/scripts/thread-cert/border_router/test_advertising_proxy.py @@ -212,16 +212,54 @@ def test(self): self.host_check_mdns_service(host, host_address, 'my-service', service_port) # - # 9. Check if the expired service is removed by the Advertising Proxy. + # 9. Check if Advertising Proxy filters out Mesh Local and Link Local host addresses # + client.srp_client_remove_host() + self.simulator.go(2) + client.srp_client_set_host_name('my-host') + client.srp_client_set_host_address('2001::1', '2002::2', + client.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], + client.get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL), + client.get_ip6_address(config.ADDRESS_TYPE.ML_EID)) + client.srp_client_add_service('my-service', '_ipps._tcp', 12345) + client.srp_client_enable_auto_start_mode() + self.simulator.go(10) + self.check_host_and_service(server, client, [ + '2001::1', '2002::2', + client.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], + client.get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL), + client.get_ip6_address(config.ADDRESS_TYPE.ML_EID) + ], 'my-service', 12345) + self.host_check_mdns_service( + host, ['2001::1', '2002::2', client.get_ip6_address(config.ADDRESS_TYPE.OMR)[0]], 'my-service', 12345) + + client.srp_client_set_host_address(client.get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL), + client.get_ip6_address(config.ADDRESS_TYPE.ML_EID), client.get_rloc()) + self.simulator.go(10) + self.check_host_and_service(server, client, [ + client.get_ip6_address(config.ADDRESS_TYPE.LINK_LOCAL), + client.get_ip6_address(config.ADDRESS_TYPE.ML_EID), + client.get_rloc() + ], 'my-service', 12345) + self.host_check_mdns_service(host, [], 'my-service', 12345) + + client.srp_client_set_host_address('2005::3') + self.simulator.go(10) + self.check_host_and_service(server, client, '2005::3', 'my-service', 12345) + self.host_check_mdns_service(host, '2005::3', 'my-service', 12345) + # + # 10. Check if the expired service is removed by the Advertising Proxy. + # client.srp_client_stop() self.simulator.go(LEASE + 2) self.assertIsNone(host.discover_mdns_service('my-service', '_ipps._tcp', 'my-host')) self.assertIsNone(host.discover_mdns_service('my-service-1', '_ipps._tcp', 'my-host')) - def host_check_mdns_service(self, host, host_addr, service_instance, service_port=12345): + def host_check_mdns_service(self, host, host_addrs, service_instance, service_port=12345): + if isinstance(host_addrs, str): + host_addrs = [host_addrs] service = host.discover_mdns_service(service_instance, '_ipps._tcp', 'my-host') self.assertIsNotNone(service) self.assertEqual(service['instance'], service_instance) @@ -230,13 +268,16 @@ def host_check_mdns_service(self, host, host_addr, service_instance, service_por self.assertEqual(service['priority'], 0) self.assertEqual(service['weight'], 0) self.assertEqual(service['host'], 'my-host') - self.assertEqual(ipaddress.ip_address(service['addresses'][0]), ipaddress.ip_address(host_addr)) - self.assertEqual(len(service['addresses']), 1) + self.assertEqual(len(service['addresses']), len(host_addrs)) + self.assertEqual(sorted(map(ipaddress.ip_address, service['addresses'])), + sorted(map(ipaddress.ip_address, host_addrs))) - def check_host_and_service(self, server, client, host_addr, service_instance, service_port=12345): + def check_host_and_service(self, server, client, host_addrs, service_instance, service_port=12345): """Check that we have properly registered host and service instance. """ + if isinstance(host_addrs, str): + host_addrs = [host_addrs] client_services = client.srp_client_get_services() print(client_services) client_services = [service for service in client_services if service['instance'] == service_instance] @@ -276,8 +317,8 @@ def check_host_and_service(self, server, client, host_addr, service_instance, se self.assertEqual(server_host['deleted'], 'false') self.assertEqual(server_host['fullname'], server_service['host_fullname']) - self.assertEqual(len(server_host['addresses']), 1) - self.assertEqual(ipaddress.ip_address(server_host['addresses'][0]), ipaddress.ip_address(host_addr)) + self.assertEqual(sorted(map(ipaddress.ip_address, server_host['addresses'])), + sorted(map(ipaddress.ip_address, host_addrs))) class SrpClientRemoveNonExistingHost(thread_cert.TestCase): diff --git a/tests/scripts/thread-cert/border_router/test_border_router_as_fed.py b/tests/scripts/thread-cert/border_router/test_border_router_as_fed.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/test_dnssd_instance_name_with_space.py b/tests/scripts/thread-cert/border_router/test_dnssd_instance_name_with_space.py old mode 100644 new mode 100755 index 7c2cc998509..720b7bc36c9 --- a/tests/scripts/thread-cert/border_router/test_dnssd_instance_name_with_space.py +++ b/tests/scripts/thread-cert/border_router/test_dnssd_instance_name_with_space.py @@ -93,6 +93,7 @@ def test(self): self.simulator.go(config.LEADER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) server.srp_server_set_enabled(True) + br1.dns_upstream_query_state = False br2.start() self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) diff --git a/tests/scripts/thread-cert/border_router/test_dnssd_server.py b/tests/scripts/thread-cert/border_router/test_dnssd_server.py old mode 100644 new mode 100755 index 471c83ee290..d31bf072506 --- a/tests/scripts/thread-cert/border_router/test_dnssd_server.py +++ b/tests/scripts/thread-cert/border_router/test_dnssd_server.py @@ -97,6 +97,7 @@ def test(self): self.simulator.go(config.LEADER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) server.srp_server_set_enabled(True) + server.dns_upstream_query_state = False client1.start() diff --git a/tests/scripts/thread-cert/border_router/test_dnssd_server_multi_border_routers.py b/tests/scripts/thread-cert/border_router/test_dnssd_server_multi_border_routers.py old mode 100644 new mode 100755 index 3a09b726daf..46178480b81 --- a/tests/scripts/thread-cert/border_router/test_dnssd_server_multi_border_routers.py +++ b/tests/scripts/thread-cert/border_router/test_dnssd_server_multi_border_routers.py @@ -114,6 +114,7 @@ def test(self): self.simulator.go(config.LEADER_STARTUP_DELAY) self.assertEqual('leader', br1.get_state()) br1.srp_server_set_enabled(True) + br1.dns_upstream_query_state = False br2.stop_mdns_service() br2.stop_otbr_service() @@ -141,6 +142,7 @@ def test(self): br2.start() self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) + br2.dns_upstream_query_state = False br2_addr = br2.get_ip6_address(config.ADDRESS_TYPE.OMR)[0] diff --git a/tests/scripts/thread-cert/border_router/test_end_device_udp_reachability.py b/tests/scripts/thread-cert/border_router/test_end_device_udp_reachability.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/test_external_route.py b/tests/scripts/thread-cert/border_router/test_external_route.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/test_firewall.py b/tests/scripts/thread-cert/border_router/test_firewall.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/test_manual_address.py b/tests/scripts/thread-cert/border_router/test_manual_address.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/test_manual_maddress.py b/tests/scripts/thread-cert/border_router/test_manual_maddress.py old mode 100644 new mode 100755 index 1bbe1900758..536d3adca2c --- a/tests/scripts/thread-cert/border_router/test_manual_maddress.py +++ b/tests/scripts/thread-cert/border_router/test_manual_maddress.py @@ -108,8 +108,7 @@ def verify(self, pv: pktverify.packet_verifier.PacketVerifier): # packet back to Host. # TD receives the MPL packet containing an encapsulated ping packet to # MA1, sent by Host, and unicasts a ping response packet back to Host. - pkts.filter_eth_src(vars['TD_ETH']) \ - .filter_ipv6_dst(_pkt.ipv6.src) \ + pkts.filter_ipv6_dst(_pkt.ipv6.src) \ .filter_ping_reply(identifier=_pkt.icmpv6.echo.identifier) \ .must_next() diff --git a/tests/scripts/thread-cert/border_router/test_manual_omr_prefix.py b/tests/scripts/thread-cert/border_router/test_manual_omr_prefix.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/test_multi_border_routers.py b/tests/scripts/thread-cert/border_router/test_multi_border_routers.py index 2e5855b598e..ab354d17184 100755 --- a/tests/scripts/thread-cert/border_router/test_multi_border_routers.py +++ b/tests/scripts/thread-cert/border_router/test_multi_border_routers.py @@ -147,8 +147,6 @@ def test(self): self.assertEqual(len(router2.get_netdata_non_nat64_prefixes()), 2) br1_on_link_prefix = br1.get_br_on_link_prefix() - self.assertEqual(br1_on_link_prefix, br1.get_netdata_non_nat64_prefixes()[0]) - self.assertEqual(br1_on_link_prefix, br1.get_netdata_non_nat64_prefixes()[0]) self.assertEqual(len(br1.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) self.assertEqual(len(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) @@ -200,8 +198,6 @@ def test(self): self.assertEqual(len(router2.get_netdata_non_nat64_prefixes()), 1) br2_on_link_prefix = br2.get_br_on_link_prefix() - self.assertEqual(set(map(IPv6Network, br2.get_netdata_non_nat64_prefixes())), - set(map(IPv6Network, [br1_on_link_prefix, br2_on_link_prefix]))) self.assertEqual(len(br1.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) self.assertEqual(len(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) diff --git a/tests/scripts/thread-cert/border_router/test_multi_thread_networks.py b/tests/scripts/thread-cert/border_router/test_multi_thread_networks.py index 34688af4fdd..bc2bb38a80d 100755 --- a/tests/scripts/thread-cert/border_router/test_multi_thread_networks.py +++ b/tests/scripts/thread-cert/border_router/test_multi_thread_networks.py @@ -130,10 +130,10 @@ def test(self): # Each BR should independently register an external route for the on-link prefix # and OMR prefix in another Thread Network. - self.assertTrue(len(br1.get_netdata_non_nat64_prefixes()) == 2) - self.assertTrue(len(router1.get_netdata_non_nat64_prefixes()) == 2) - self.assertTrue(len(br2.get_netdata_non_nat64_prefixes()) == 2) - self.assertTrue(len(router2.get_netdata_non_nat64_prefixes()) == 2) + self.assertTrue(len(br1.get_netdata_non_nat64_prefixes()) == 1) + self.assertTrue(len(router1.get_netdata_non_nat64_prefixes()) == 1) + self.assertTrue(len(br2.get_netdata_non_nat64_prefixes()) == 1) + self.assertTrue(len(router2.get_netdata_non_nat64_prefixes()) == 1) br1_external_routes = br1.get_routes() br2_external_routes = br2.get_routes() @@ -146,7 +146,21 @@ def test(self): self.assertTrue(len(router2.get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1) self.assertTrue(router1.ping(router2.get_ip6_address(config.ADDRESS_TYPE.OMR)[0])) + self.verify_border_routing_counters(br1, {'inbound_unicast': 1, 'outbound_unicast': 1}) + self.verify_border_routing_counters(br2, {'inbound_unicast': 1, 'outbound_unicast': 1}) self.assertTrue(router2.ping(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0])) + self.verify_border_routing_counters(br1, {'inbound_unicast': 1, 'outbound_unicast': 1}) + self.verify_border_routing_counters(br2, {'inbound_unicast': 1, 'outbound_unicast': 1}) + self.assertGreater(br1.get_border_routing_counters()['ra_rx'], 0) + self.assertGreater(br1.get_border_routing_counters()['ra_tx_success'], 0) + self.assertGreater(br1.get_border_routing_counters()['rs_tx_success'], 0) + + def verify_border_routing_counters(self, br, expect_delta): + delta_counters = br.read_border_routing_counters_delta() + self.assertEqual(set(delta_counters.keys()), set(expect_delta.keys())) + for key in delta_counters: + self.assertEqual(delta_counters[key][0], expect_delta[key]) + self.assertGreater(delta_counters[key][1], 0) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/border_router/test_on_link_prefix.py b/tests/scripts/thread-cert/border_router/test_on_link_prefix.py index 4ff831ac4f1..4067236232c 100755 --- a/tests/scripts/thread-cert/border_router/test_on_link_prefix.py +++ b/tests/scripts/thread-cert/border_router/test_on_link_prefix.py @@ -127,10 +127,8 @@ def test(self): logging.info("HOST addrs: %r", host.get_addrs()) self.assertEqual(len(br1.get_netdata_non_nat64_prefixes()), 1) - on_link_prefix = br1.get_netdata_non_nat64_prefixes()[0] - self.assertEqual(IPv6Network(on_link_prefix), IPv6Network(ON_LINK_PREFIX)) - host_on_link_addr = host.get_matched_ula_addresses(on_link_prefix)[0] + host_on_link_addr = host.get_matched_ula_addresses(ON_LINK_PREFIX)[0] self.assertTrue(router1.ping(host_on_link_addr)) self.assertTrue( host.ping(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True, interface=host_on_link_addr)) @@ -164,18 +162,16 @@ def test(self): br2_omr_prefix = br2.get_br_omr_prefix() self.assertNotEqual(br1_omr_prefix, br2_omr_prefix) - # Verify that the Border Routers starts advertsing new on-link prefix + # Verify that the Border Routers starts advertising new on-link prefix # but don't remove the external routes for the radvd on-link prefix # immediately, because the SLAAC addresses are still valid. - self.assertEqual(len(br1.get_netdata_non_nat64_prefixes()), 3) - self.assertEqual(len(router1.get_netdata_non_nat64_prefixes()), 3) - self.assertEqual(len(br2.get_netdata_non_nat64_prefixes()), 2) - self.assertEqual(len(router2.get_netdata_non_nat64_prefixes()), 2) - on_link_prefixes = list( - set(br1.get_netdata_non_nat64_prefixes()).intersection(br2.get_netdata_non_nat64_prefixes())) - self.assertEqual(len(on_link_prefixes), 1) - self.assertEqual(IPv6Network(on_link_prefixes[0]), IPv6Network(br2.get_br_on_link_prefix())) + self.assertEqual(len(br1.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(router1.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(br2.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(router2.get_netdata_non_nat64_prefixes()), 1) + + br2_on_link_prefix = br2.get_br_on_link_prefix() router1_omr_addr = router1.get_ip6_address(config.ADDRESS_TYPE.OMR)[0] router2_omr_addr = router2.get_ip6_address(config.ADDRESS_TYPE.OMR)[0] @@ -184,7 +180,7 @@ def test(self): # and preferred Border Router on-link prefix can be reached by Thread # devices in network of Border Router 1. for host_on_link_addr in [ - host.get_matched_ula_addresses(on_link_prefixes[0])[0], + host.get_matched_ula_addresses(br2_on_link_prefix)[0], host.get_matched_ula_addresses(ON_LINK_PREFIX)[0] ]: self.assertTrue(router1.ping(host_on_link_addr)) @@ -192,11 +188,6 @@ def test(self): host_on_link_addr = host.get_matched_ula_addresses(ON_LINK_PREFIX)[0] - # Make sure that addresses of the deprecated radvd `ON_LINK_PREFIX` - # can't be reached by Thread devices in network of Border Router 2. - self.assertFalse(router2.ping(host_on_link_addr)) - self.assertFalse(host.ping(router2_omr_addr, backbone=True, interface=host_on_link_addr)) - # Wait 30 seconds for the radvd `ON_LINK_PREFIX` to be invalidated # and make sure that Thread devices in both networks can't reach # the on-link address. diff --git a/tests/scripts/thread-cert/border_router/test_plat_udp_accessiblity.py b/tests/scripts/thread-cert/border_router/test_plat_udp_accessiblity.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py index 25e5cd4f3a8..afc8d33499b 100755 --- a/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py +++ b/tests/scripts/thread-cert/border_router/test_publish_meshcop_service.py @@ -157,8 +157,11 @@ def check_meshcop_service_by_data(self, br, service_data): self.assertEqual((state_bitmap >> 3 & 3), 2) # Thread is attached self.assertEqual((state_bitmap >> 5 & 3), 1) # high availability self.assertEqual((state_bitmap >> 7 & 1), + br.get_state() not in ['disabled', 'detached'] and br.get_backbone_router_state() != 'Disabled') # BBR is enabled or not - self.assertEqual((state_bitmap >> 8 & 1), br.get_backbone_router_state() == 'Primary') # BBR is primary or not + self.assertEqual((state_bitmap >> 8 & 1), + br.get_state() not in ['disabled', 'detached'] and + br.get_backbone_router_state() == 'Primary') # BBR is primary or not self.assertEqual(service_data['txt']['nn'], br.get_network_name()) self.assertEqual(service_data['txt']['rv'], '1') self.assertIn(service_data['txt']['tv'], ['1.1.0', '1.1.1', '1.2.0', '1.3.0']) diff --git a/tests/scripts/thread-cert/border_router/test_single_border_router.py b/tests/scripts/thread-cert/border_router/test_single_border_router.py index 577b73ede42..a660345fcec 100755 --- a/tests/scripts/thread-cert/border_router/test_single_border_router.py +++ b/tests/scripts/thread-cert/border_router/test_single_border_router.py @@ -177,8 +177,6 @@ def test(self): # The same local OMR and on-link prefix should be re-register. self.assertEqual(br.get_netdata_omr_prefixes(), [omr_prefix]) self.assertEqual(router.get_netdata_omr_prefixes(), [omr_prefix]) - self.assertEqual(br.get_netdata_non_nat64_prefixes(), [on_link_prefix]) - self.assertEqual(router.get_netdata_non_nat64_prefixes(), [on_link_prefix]) self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) @@ -231,8 +229,6 @@ def test(self): # The same local OMR and on-link prefix should be re-registered. self.assertEqual(br.get_netdata_omr_prefixes(), [omr_prefix]) self.assertEqual(router.get_netdata_omr_prefixes(), [omr_prefix]) - self.assertEqual(br.get_netdata_non_nat64_prefixes(), [on_link_prefix]) - self.assertEqual(router.get_netdata_non_nat64_prefixes(), [on_link_prefix]) self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) @@ -270,7 +266,7 @@ def test(self): # The routing manager may fail to send RS and will wait for 4 seconds # before retrying. - self.simulator.go(20) + self.simulator.go(40) self.collect_ipaddrs() logging.info("BR addrs: %r", br.get_addrs()) @@ -285,8 +281,6 @@ def test(self): # The same local OMR and on-link prefix should be re-registered. self.assertEqual(br.get_netdata_omr_prefixes(), [omr_prefix]) self.assertEqual(router.get_netdata_omr_prefixes(), [omr_prefix]) - self.assertEqual(br.get_netdata_non_nat64_prefixes(), [on_link_prefix]) - self.assertEqual(router.get_netdata_non_nat64_prefixes(), [on_link_prefix]) self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) @@ -297,8 +291,8 @@ def test(self): self.assertEqual(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA), [host_ula_address]) # Router1 can ping to/from the Host on infra link. - self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) self.assertTrue(host.ping(router.get_ip6_address(config.ADDRESS_TYPE.OMR)[0], backbone=True)) + self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) # # Case 5. Test if the linux host is still reachable if rejoin the network. @@ -320,8 +314,8 @@ def test(self): br.start_radvd_service(prefix=config.ONLINK_GUA_PREFIX, slaac=True) self.simulator.go(5) - self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 2) - self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 2) + self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1) self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_GUA)[0])) self.assertTrue(router.ping(host.get_ip6_address(config.ADDRESS_TYPE.ONLINK_ULA)[0])) diff --git a/tests/scripts/thread-cert/border_router/test_srp_register_500_services_br.py b/tests/scripts/thread-cert/border_router/test_srp_register_500_services_br.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/border_router/test_trel_connectivity.py b/tests/scripts/thread-cert/border_router/test_trel_connectivity.py old mode 100644 new mode 100755 index 7181138192e..33487d83614 --- a/tests/scripts/thread-cert/border_router/test_trel_connectivity.py +++ b/tests/scripts/thread-cert/border_router/test_trel_connectivity.py @@ -104,8 +104,14 @@ def test(self): br2 = self.nodes[BR2] router2 = self.nodes[ROUTER2] - if br1.get_trel_state() is None: - self.skipTest("TREL is not enabled") + if br1.is_trel_enabled() is None: + self.skipTest("TREL is not supported") + + if br1.is_trel_enabled() == False: + br1.enable_trel() + + if br2.is_trel_enabled() == False: + br2.enable_trel() br1.start() self.wait_node_state(br1, 'leader', 10) diff --git a/script/check-arm-build-autotools b/tests/scripts/thread-cert/call_dbus_method.py old mode 100755 new mode 100644 similarity index 68% rename from script/check-arm-build-autotools rename to tests/scripts/thread-cert/call_dbus_method.py index e994c202525..913c6a51fad --- a/script/check-arm-build-autotools +++ b/tests/scripts/thread-cert/call_dbus_method.py @@ -1,6 +1,6 @@ -#!/bin/bash +#!/usr/bin/env python3 # -# Copyright (c) 2020, The OpenThread Authors. +# Copyright (c) 2022, The OpenThread Authors. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -26,46 +26,21 @@ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # +import dbus +import json +import sys -set -euxo pipefail -reset_source() -{ - rm -rf build output tmp -} +def main(): + args = sys.argv[1:] + bus = dbus.SystemBus() + interface, method_name, arguments = args[0], args[1], json.loads(args[2]) + obj = bus.get_object('io.openthread.BorderRouter.wpan0', '/io/openthread/BorderRouter/wpan0') + iface = dbus.Interface(obj, interface) + method = getattr(iface, method_name) + res = method(*arguments) + print(json.dumps(res)) -build_cc2538() -{ - local options=( - "COMMISSIONER=1" - "DHCP6_CLIENT=1" - "DHCP6_SERVER=1" - "DNS_CLIENT=1" - "JOINER=1" - "SLAAC=1" - # cc2538 does not have enough resources to support Thread 1.3 - "THREAD_VERSION=1.1" - ) - reset_source - make -f examples/Makefile-cc2538 "${options[@]}" -} - -main() -{ - ./bootstrap - - export CPPFLAGS="${CPPFLAGS:-} -DNDEBUG" - - if [[ $# == 0 ]]; then - build_cc2538 - return 0 - fi - - while [[ $# != 0 ]]; do - "build_$1" - shift - done -} - -main "$@" +if __name__ == '__main__': + main() diff --git a/tests/scripts/thread-cert/coap.py b/tests/scripts/thread-cert/coap.py old mode 100755 new mode 100644 diff --git a/tests/scripts/thread-cert/command.py b/tests/scripts/thread-cert/command.py old mode 100755 new mode 100644 diff --git a/tests/scripts/thread-cert/common.py b/tests/scripts/thread-cert/common.py old mode 100755 new mode 100644 diff --git a/tests/scripts/thread-cert/config.py b/tests/scripts/thread-cert/config.py old mode 100755 new mode 100644 index 3878d4e2f1a..cf04cbe7458 --- a/tests/scripts/thread-cert/config.py +++ b/tests/scripts/thread-cert/config.py @@ -127,6 +127,7 @@ class ADDRESS_TYPE(Enum): LEADER_STARTUP_DELAY = 12 ROUTER_STARTUP_DELAY = 10 +ED_STARTUP_DELAY = 5 BORDER_ROUTER_STARTUP_DELAY = 20 MAX_NEIGHBOR_AGE = 100 INFINITE_COST_TIMEOUT = 90 @@ -153,6 +154,13 @@ class ADDRESS_TYPE(Enum): PACKET_VERIFICATION_DEFAULT = 1 PACKET_VERIFICATION_TREL = 2 +# After leader reset it may retransmit link request 6 times with max 5.5s interval +LEADER_RESET_DELAY = 41 +# After router reset it may retransmit link request 3 times with max 5.5s interval +ROUTER_RESET_DELAY = 23 +MLE_MAX_CRITICAL_TRANSMISSION_COUNT = 6 +MLE_MAX_TRANSMISSION_COUNT = 3 + def create_default_network_data_prefix_sub_tlvs_factories(): return { diff --git a/tests/scripts/thread-cert/debug.py b/tests/scripts/thread-cert/debug.py old mode 100755 new mode 100644 diff --git a/tests/scripts/thread-cert/dtls.py b/tests/scripts/thread-cert/dtls.py old mode 100755 new mode 100644 diff --git a/tests/scripts/thread-cert/ipv6.py b/tests/scripts/thread-cert/ipv6.py old mode 100755 new mode 100644 index 5daa6ffdd2b..ab00a26e91d --- a/tests/scripts/thread-cert/ipv6.py +++ b/tests/scripts/thread-cert/ipv6.py @@ -29,10 +29,10 @@ import abc import io +import ipaddress import struct from binascii import hexlify -from ipaddress import ip_address import common @@ -93,6 +93,25 @@ def calculate_checksum(data): return checksum +def synthesize_ip6_address(ip6_network: ipaddress.IPv6Network, + ip4_address: ipaddress.IPv4Address) -> ipaddress.IPv6Address: + """ Synthesize an IPv6 address from a prefix for NAT64 and an IPv4 address. + + Only supports /96 network for now. + + Args: + ip6_network: The network for NAT64. + ip4_address: The IPv4 address. + + Returns: + ipaddress.IPv6Address: The synthesized IPv6 address. + """ + if ip6_network.prefixlen != 96: + # We are only using /96 networks in openthread + raise NotImplementedError("synthesize_ip6_address only supports /96 networks") + return ipaddress.IPv6Address(int(ip6_network.network_address) | int(ip4_address)) + + class PacketFactory(object): """ Interface for classes that produce objects from data. """ @@ -209,7 +228,7 @@ def _convert_to_ipaddress(self, value): if isinstance(value, bytearray): value = bytes(value) - return ip_address(value) + return ipaddress.ip_address(value) @property def source_address(self): @@ -267,7 +286,7 @@ def _convert_to_ipaddress(self, value): if isinstance(value, bytearray): value = bytes(value) - return ip_address(value) + return ipaddress.ip_address(value) @property def source_address(self): diff --git a/tests/scripts/thread-cert/lowpan.py b/tests/scripts/thread-cert/lowpan.py old mode 100755 new mode 100644 diff --git a/tests/scripts/thread-cert/mac802154.py b/tests/scripts/thread-cert/mac802154.py old mode 100755 new mode 100644 diff --git a/tests/scripts/thread-cert/mesh_cop.py b/tests/scripts/thread-cert/mesh_cop.py old mode 100755 new mode 100644 diff --git a/tests/scripts/thread-cert/message.py b/tests/scripts/thread-cert/message.py old mode 100755 new mode 100644 diff --git a/tests/scripts/thread-cert/mle.py b/tests/scripts/thread-cert/mle.py old mode 100755 new mode 100644 index 3c70b7f4dfc..0c826ad98fb --- a/tests/scripts/thread-cert/mle.py +++ b/tests/scripts/thread-cert/mle.py @@ -90,6 +90,7 @@ class TlvType(IntEnum): ACTIVE_OPERATIONAL_DATASET = 24 PENDING_OPERATIONAL_DATASET = 25 THREAD_DISCOVERY = 26 + SUPERVISION_INTERVAL = 27 CSL_CHANNEL = 80 CSL_SYNCHRONIZED_TIMEOUT = 85 CSL_CLOCK_ACCURACY = 86 diff --git a/tests/scripts/thread-cert/net_crypto.py b/tests/scripts/thread-cert/net_crypto.py old mode 100755 new mode 100644 index 66533b9f7c6..d9d007f1cbd --- a/tests/scripts/thread-cert/net_crypto.py +++ b/tests/scripts/thread-cert/net_crypto.py @@ -33,8 +33,6 @@ from binascii import hexlify -from Crypto.Cipher import AES - class CryptoEngine: """ Class responsible for encryption and decryption of data. """ @@ -64,6 +62,8 @@ def encrypt(self, data, message_info): """ key, nonce, auth_data = self._crypto_material_creator.create_key_and_nonce_and_authenticated_data(message_info) + from Crypto.Cipher import AES + cipher = AES.new(key, AES.MODE_CCM, nonce, mac_len=self.mic_length) cipher.update(auth_data) @@ -83,6 +83,8 @@ def decrypt(self, enc_data, mic, message_info): """ key, nonce, auth_data = self._crypto_material_creator.create_key_and_nonce_and_authenticated_data(message_info) + from Crypto.Cipher import AES + cipher = AES.new(key, AES.MODE_CCM, nonce, mac_len=self.mic_length) cipher.update(auth_data) diff --git a/tests/scripts/thread-cert/network_data.py b/tests/scripts/thread-cert/network_data.py old mode 100755 new mode 100644 diff --git a/tests/scripts/thread-cert/network_diag.py b/tests/scripts/thread-cert/network_diag.py old mode 100755 new mode 100644 diff --git a/tests/scripts/thread-cert/network_layer.py b/tests/scripts/thread-cert/network_layer.py old mode 100755 new mode 100644 diff --git a/tests/scripts/thread-cert/node.py b/tests/scripts/thread-cert/node.py index d8b254c5fd1..57a41b2d80e 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -27,11 +27,13 @@ # POSSIBILITY OF SUCH DAMAGE. # +import json import binascii import ipaddress import logging import os import re +import shlex import socket import subprocess import sys @@ -51,6 +53,8 @@ PORT_OFFSET = int(os.getenv('PORT_OFFSET', "0")) +INFRA_DNS64 = int(os.getenv('NAT64', 0)) + class OtbrDocker: RESET_DELAY = 3 @@ -58,6 +62,7 @@ class OtbrDocker: _socat_proc = None _ot_rcp_proc = None _docker_proc = None + _border_routing_counters = None def __init__(self, nodeid: int, **kwargs): self.verbose = int(float(os.getenv('VERBOSE', 0))) @@ -107,13 +112,17 @@ def _launch_docker(self): logging.info(f'Docker image: {config.OTBR_DOCKER_IMAGE}') subprocess.check_call(f"docker rm -f {self._docker_name} || true", shell=True) CI_ENV = os.getenv('CI_ENV', '').split() + dns = ['--dns=127.0.0.1'] if INFRA_DNS64 == 1 else ['--dns=8.8.8.8'] + nat64_prefix = ['--nat64-prefix', '2001:db8:1:ffff::/96'] if INFRA_DNS64 == 1 else [] os.makedirs('/tmp/coverage/', exist_ok=True) - self._docker_proc = subprocess.Popen(['docker', 'run'] + CI_ENV + [ + + cmd = ['docker', 'run'] + CI_ENV + [ '--rm', '--name', self._docker_name, '--network', config.BACKBONE_DOCKER_NETWORK_NAME, + ] + dns + [ '-i', '--sysctl', 'net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1', @@ -128,10 +137,9 @@ def _launch_docker(self): config.BACKBONE_IFNAME, '--trel-url', f'trel://{config.BACKBONE_IFNAME}', - ], - stdin=subprocess.DEVNULL, - stdout=sys.stdout, - stderr=sys.stderr) + ] + nat64_prefix + logging.info(' '.join(cmd)) + self._docker_proc = subprocess.Popen(cmd, stdin=subprocess.DEVNULL, stdout=sys.stdout, stderr=sys.stderr) launch_docker_deadline = time.time() + 300 launch_ok = False @@ -163,12 +171,10 @@ def stop_otbr_service(self): self.bash('service otbr-agent stop') def stop_mdns_service(self): - self.bash('service avahi-daemon stop') - self.bash('service mdns stop') + self.bash('service avahi-daemon stop; service mdns stop; !(cat /proc/net/udp | grep -i :14E9)') def start_mdns_service(self): - self.bash('service avahi-daemon start') - self.bash('service mdns start') + self.bash('service avahi-daemon start; service mdns start; cat /proc/net/udp | grep -i :14E9') def start_ot_ctl(self): cmd = f'docker exec -i {self._docker_name} ot-ctl' @@ -358,6 +364,135 @@ def dns_dig(self, server: str, name: str, qtype: str): return dig_result + def call_dbus_method(self, *args): + args = shlex.join([args[0], args[1], json.dumps(args[2:])]) + return json.loads( + self.bash(f'python3 /app/third_party/openthread/repo/tests/scripts/thread-cert/call_dbus_method.py {args}') + [0]) + + def get_dbus_property(self, property_name): + return self.call_dbus_method('org.freedesktop.DBus.Properties', 'Get', 'io.openthread.BorderRouter', + property_name) + + def set_dbus_property(self, property_name, property_value): + return self.call_dbus_method('org.freedesktop.DBus.Properties', 'Set', 'io.openthread.BorderRouter', + property_name, property_value) + + def get_border_routing_counters(self): + counters = self.get_dbus_property('BorderRoutingCounters') + counters = { + 'inbound_unicast': counters[0], + 'inbound_multicast': counters[1], + 'outbound_unicast': counters[2], + 'outbound_multicast': counters[3], + 'ra_rx': counters[4], + 'ra_tx_success': counters[5], + 'ra_tx_failure': counters[6], + 'rs_rx': counters[7], + 'rs_tx_success': counters[8], + 'rs_tx_failure': counters[9], + } + logging.info(f'border routing counters: {counters}') + return counters + + def _process_traffic_counters(self, counter): + return { + '4to6': { + 'packets': counter[0], + 'bytes': counter[1], + }, + '6to4': { + 'packets': counter[2], + 'bytes': counter[3], + } + } + + def _process_packet_counters(self, counter): + return {'4to6': {'packets': counter[0]}, '6to4': {'packets': counter[1]}} + + def nat64_set_enabled(self, enable): + return self.call_dbus_method('io.openthread.BorderRouter', 'SetNat64Enabled', enable) + + @property + def nat64_state(self): + state = self.get_dbus_property('Nat64State') + return {'PrefixManager': state[0], 'Translator': state[1]} + + @property + def nat64_mappings(self): + return [{ + 'id': row[0], + 'ip4': row[1], + 'ip6': row[2], + 'expiry': row[3], + 'counters': { + 'total': self._process_traffic_counters(row[4][0]), + 'ICMP': self._process_traffic_counters(row[4][1]), + 'UDP': self._process_traffic_counters(row[4][2]), + 'TCP': self._process_traffic_counters(row[4][3]), + } + } for row in self.get_dbus_property('Nat64Mappings')] + + @property + def nat64_counters(self): + res_error = self.get_dbus_property('Nat64ErrorCounters') + res_proto = self.get_dbus_property('Nat64ProtocolCounters') + return { + 'protocol': { + 'Total': self._process_traffic_counters(res_proto[0]), + 'ICMP': self._process_traffic_counters(res_proto[1]), + 'UDP': self._process_traffic_counters(res_proto[2]), + 'TCP': self._process_traffic_counters(res_proto[3]), + }, + 'errors': { + 'Unknown': self._process_packet_counters(res_error[0]), + 'Illegal Pkt': self._process_packet_counters(res_error[1]), + 'Unsup Proto': self._process_packet_counters(res_error[2]), + 'No Mapping': self._process_packet_counters(res_error[3]), + } + } + + @property + def nat64_traffic_counters(self): + res = self.get_dbus_property('Nat64TrafficCounters') + return { + 'Total': self._process_traffic_counters(res[0]), + 'ICMP': self._process_traffic_counters(res[1]), + 'UDP': self._process_traffic_counters(res[2]), + 'TCP': self._process_traffic_counters(res[3]), + } + + @property + def dns_upstream_query_state(self): + return bool(self.get_dbus_property('DnsUpstreamQueryState')) + + @dns_upstream_query_state.setter + def dns_upstream_query_state(self, value): + if type(value) is not bool: + raise ValueError("dns_upstream_query_state must be a bool") + return self.set_dbus_property('DnsUpstreamQueryState', value) + + def read_border_routing_counters_delta(self): + old_counters = self._border_routing_counters + new_counters = self.get_border_routing_counters() + self._border_routing_counters = new_counters + delta_counters = {} + if old_counters is None: + delta_counters = new_counters + else: + for i in ('inbound', 'outbound'): + for j in ('unicast', 'multicast'): + key = f'{i}_{j}' + assert (key in old_counters) + assert (key in new_counters) + value = [new_counters[key][0] - old_counters[key][0], new_counters[key][1] - old_counters[key][1]] + delta_counters[key] = value + delta_counters = { + key: value for key, value in delta_counters.items() if not isinstance(value, int) and value[0] and value[1] + } + + return delta_counters + @staticmethod def __unescape_dns_instance_name(name: str) -> str: new_name = [] @@ -987,7 +1122,7 @@ def srp_server_get_hosts(self): addresses = lines.pop(0).strip().split('[')[1].strip(' ]').split(',') map(str.strip, addresses) - host['addresses'] = [addr for addr in addresses if addr] + host['addresses'] = [addr.strip() for addr in addresses if addr] host_list.append(host) @@ -1018,6 +1153,8 @@ def srp_server_get_services(self): 'priority': '0', 'weight': '0', 'ttl': '7200', + 'lease': '7200', + 'key-lease': '7200', 'TXT': ['abc=010203'], 'host_fullname': 'my-host.default.service.arpa.', 'host': 'my-host', @@ -1030,6 +1167,7 @@ def srp_server_get_services(self): cmd = 'srp server service' self.send_command(cmd) lines = self._expect_command_output() + service_list = [] while lines: service = {} @@ -1044,8 +1182,8 @@ def srp_server_get_services(self): service_list.append(service) continue - # 'subtypes', port', 'priority', 'weight', 'ttl' - for i in range(0, 5): + # 'subtypes', port', 'priority', 'weight', 'ttl', 'lease', and 'key-lease' + for i in range(0, 7): key_value = lines.pop(0).strip().split(':') service[key_value[0].strip()] = key_value[1].strip() @@ -1158,11 +1296,22 @@ def srp_client_get_host_address(self): self.send_command(f'srp client host address') self._expect_done() - def srp_client_add_service(self, instance_name, service_name, port, priority=0, weight=0, txt_entries=[]): + def srp_client_add_service(self, + instance_name, + service_name, + port, + priority=0, + weight=0, + txt_entries=[], + lease=0, + key_lease=0): txt_record = "".join(self._encode_txt_entry(entry) for entry in txt_entries) + if txt_record == '': + txt_record = '-' instance_name = self._escape_escapable(instance_name) self.send_command( - f'srp client service add {instance_name} {service_name} {port} {priority} {weight} {txt_record}') + f'srp client service add {instance_name} {service_name} {port} {priority} {weight} {txt_record} {lease} {key_lease}' + ) self._expect_done() def srp_client_remove_service(self, instance_name, service_name): @@ -1189,6 +1338,16 @@ def srp_client_get_lease_interval(self) -> int: self.send_command(cmd) return int(self._expect_result('\d+')) + def srp_client_set_key_lease_interval(self, leaseinterval: int): + cmd = f'srp client keyleaseinterval {leaseinterval}' + self.send_command(cmd) + self._expect_done() + + def srp_client_get_key_lease_interval(self) -> int: + cmd = 'srp client keyleaseinterval' + self.send_command(cmd) + return int(self._expect_result('\d+')) + def srp_client_set_ttl(self, ttl: int): cmd = f'srp client ttl {ttl}' self.send_command(cmd) @@ -1203,7 +1362,12 @@ def srp_client_get_ttl(self) -> int: # TREL utilities # - def get_trel_state(self) -> Union[None, bool]: + def enable_trel(self): + cmd = 'trel enable' + self.send_command(cmd) + self._expect_done() + + def is_trel_enabled(self) -> Union[None, bool]: states = [r'Disabled', r'Enabled'] self.send_command('trel') try: @@ -1569,6 +1733,30 @@ def set_pollperiod(self, pollperiod): self.send_command('pollperiod %d' % pollperiod) self._expect_done() + def get_child_supervision_interval(self): + self.send_command('childsupervision interval') + return self._expect_result(r'\d+') + + def set_child_supervision_interval(self, interval): + self.send_command('childsupervision interval %d' % interval) + self._expect_done() + + def get_child_supervision_check_timeout(self): + self.send_command('childsupervision checktimeout') + return self._expect_result(r'\d+') + + def set_child_supervision_check_timeout(self, timeout): + self.send_command('childsupervision checktimeout %d' % timeout) + self._expect_done() + + def get_child_supervision_check_failure_counter(self): + self.send_command('childsupervision failcounter') + return self._expect_result(r'\d+') + + def reset_child_supervision_check_failure_counter(self): + self.send_command('childsupervision failcounter reset') + self._expect_done() + def get_csl_info(self): self.send_command('csl') self._expect_done() @@ -1776,10 +1964,10 @@ def get_child_table(self) -> Dict[int, Dict[str, Any]]: # # Example output: - # | ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt| Extended MAC | - # +-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+------------------+ - # | 1 | 0xc801 | 240 | 24 | 3 | 131 |1|0|0| 3| 0 | 0 | 4ecede68435358ac | - # | 2 | 0xc802 | 240 | 2 | 3 | 131 |0|0|0| 3| 1 | 0 | a672a601d2ce37d8 | + # | ID | RLOC16 | Timeout | Age | LQ In | C_VN |R|D|N|Ver|CSL|QMsgCnt|Suprvsn| Extended MAC | + # +-----+--------+------------+------------+-------+------+-+-+-+---+---+-------+-------+------------------+ + # | 1 | 0xc801 | 240 | 24 | 3 | 131 |1|0|0| 3| 0 | 0 | 129 | 4ecede68435358ac | + # | 2 | 0xc802 | 240 | 2 | 3 | 131 |0|0|0| 3| 1 | 0 | 0 | a672a601d2ce37d8 | # Done # @@ -1810,6 +1998,7 @@ def get_child_table(self) -> Dict[int, Dict[str, Any]]: 'ver': int(col('Ver')), 'csl': bool(int(col('CSL'))), 'qmsgcnt': int(col('QMsgCnt')), + 'suprvsn': int(col('Suprvsn')) } return table @@ -1958,7 +2147,7 @@ def disable_br(self): self._expect_done() def get_br_omr_prefix(self): - cmd = 'br omrprefix' + cmd = 'br omrprefix local' self.send_command(cmd) return self._expect_command_output()[0] @@ -1972,7 +2161,7 @@ def get_netdata_omr_prefixes(self): return omr_prefixes def get_br_on_link_prefix(self): - cmd = 'br onlinkprefix' + cmd = 'br onlinkprefix local' self.send_command(cmd) return self._expect_command_output()[0] @@ -1985,10 +2174,122 @@ def get_netdata_non_nat64_prefixes(self): return prefixes def get_br_nat64_prefix(self): - cmd = 'br nat64prefix' + cmd = 'br nat64prefix local' self.send_command(cmd) return self._expect_command_output()[0] + def get_br_favored_nat64_prefix(self): + cmd = 'br nat64prefix favored' + self.send_command(cmd) + return self._expect_command_output()[0].split(' ')[0] + + def enable_nat64(self): + self.send_command(f'nat64 enable') + self._expect_done() + + def disable_nat64(self): + self.send_command(f'nat64 disable') + self._expect_done() + + def get_nat64_state(self): + self.send_command('nat64 state') + res = {} + for line in self._expect_command_output(): + state = line.split(':') + res[state[0].strip()] = state[1].strip() + return res + + def get_nat64_mappings(self): + cmd = 'nat64 mappings' + self.send_command(cmd) + result = self._expect_command_output() + session = None + session_counters = None + sessions = [] + + for line in result: + m = re.match( + r'\|\s+([a-f0-9]+)\s+\|\s+(.+)\s+\|\s+(.+)\s+\|\s+(\d+)s\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|', + line) + if m: + groups = m.groups() + if session: + session['counters'] = session_counters + sessions.append(session) + session = { + 'id': groups[0], + 'ip6': groups[1], + 'ip4': groups[2], + 'expiry': int(groups[3]), + } + session_counters = {} + session_counters['total'] = { + '4to6': { + 'packets': int(groups[4]), + 'bytes': int(groups[5]), + }, + '6to4': { + 'packets': int(groups[6]), + 'bytes': int(groups[7]), + }, + } + continue + if not session: + continue + m = re.match(r'\|\s+\|\s+(.+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|', line) + if m: + groups = m.groups() + session_counters[groups[0]] = { + '4to6': { + 'packets': int(groups[1]), + 'bytes': int(groups[2]), + }, + '6to4': { + 'packets': int(groups[3]), + 'bytes': int(groups[4]), + }, + } + if session: + session['counters'] = session_counters + sessions.append(session) + return sessions + + def get_nat64_counters(self): + cmd = 'nat64 counters' + self.send_command(cmd) + result = self._expect_command_output() + + protocol_counters = {} + error_counters = {} + for line in result: + m = re.match(r'\|\s+(.+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|', line) + if m: + groups = m.groups() + protocol_counters[groups[0]] = { + '4to6': { + 'packets': int(groups[1]), + 'bytes': int(groups[2]), + }, + '6to4': { + 'packets': int(groups[3]), + 'bytes': int(groups[4]), + }, + } + continue + m = re.match(r'\|\s+(.+)\s+\|\s+(\d+)\s+\|\s+(\d+)\s+\|', line) + if m: + groups = m.groups() + error_counters[groups[0]] = { + '4to6': { + 'packets': int(groups[1]), + }, + '6to4': { + 'packets': int(groups[2]), + }, + } + continue + return {'protocol': protocol_counters, 'errors': error_counters} + def get_netdata_nat64_prefix(self): prefixes = [] routes = self.get_routes() @@ -2011,6 +2312,8 @@ def get_services(self): for line in netdata: if line.startswith('Services:'): services_section = True + elif line.startswith('Contexts'): + services_section = False elif services_section: services.append(line.strip().split(' ')) return services @@ -2021,8 +2324,8 @@ def netdata_show(self): def get_netdata(self): raw_netdata = self.netdata_show() - netdata = {'Prefixes': [], 'Routes': [], 'Services': []} - key_list = ['Prefixes', 'Routes', 'Services'] + netdata = {'Prefixes': [], 'Routes': [], 'Services': [], 'Contexts': []} + key_list = ['Prefixes', 'Routes', 'Services', 'Contexts'] key = None for i in range(0, len(raw_netdata)): @@ -2077,6 +2380,10 @@ def netdata_publish_route(self, prefix, flags='s', prf='med'): self.send_command(f'netdata publish route {prefix} {flags} {prf}') self._expect_done() + def netdata_publish_replace(self, old_prefix, prefix, flags='s', prf='med'): + self.send_command(f'netdata publish replace {old_prefix} {prefix} {flags} {prf}') + self._expect_done() + def netdata_unpublish_prefix(self, prefix): self.send_command(f'netdata unpublish {prefix}') self._expect_done() @@ -2617,7 +2924,7 @@ def coap_wait_ack(self): else: timeout = 5 - self._expect(r'Received ACK in reply to notification ' r'from ([\da-f:]+)\b', timeout=timeout) + self._expect(r'Received ACK in reply to notification from ([\da-f:]+)\b', timeout=timeout) (source,) = self.pexpect.match.groups() source = source.decode('UTF-8') @@ -2851,15 +3158,38 @@ def router_table(self): return router_table - def link_metrics_query_single_probe(self, dst_addr: str, linkmetrics_flags: str): - cmd = 'linkmetrics query %s single %s' % (dst_addr, linkmetrics_flags) + def link_metrics_query_single_probe(self, dst_addr: str, linkmetrics_flags: str, block: str = ""): + cmd = 'linkmetrics query %s single %s %s' % (dst_addr, linkmetrics_flags, block) self.send_command(cmd) - self._expect_done() + self.simulator.go(5) + return self._parse_linkmetrics_query_result(self._expect_command_output()) - def link_metrics_query_forward_tracking_series(self, dst_addr: str, series_id: int): - cmd = 'linkmetrics query %s forward %d' % (dst_addr, series_id) + def link_metrics_query_forward_tracking_series(self, dst_addr: str, series_id: int, block: str = ""): + cmd = 'linkmetrics query %s forward %d %s' % (dst_addr, series_id, block) self.send_command(cmd) - self._expect_done() + self.simulator.go(5) + return self._parse_linkmetrics_query_result(self._expect_command_output()) + + def _parse_linkmetrics_query_result(self, lines): + """Parse link metrics query result""" + + # Exmaple of command output: + # ['Received Link Metrics Report from: fe80:0:0:0:146e:a00:0:1', + # '- PDU Counter: 1 (Count/Summation)', + # '- LQI: 0 (Exponential Moving Average)', + # '- Margin: 80 (dB) (Exponential Moving Average)', + # '- RSSI: -20 (dBm) (Exponential Moving Average)'] + # + # Or 'Link Metrics Report, status: {status}' + + result = {} + for line in lines: + if line.startswith('- '): + k, v = line[2:].split(': ') + result[k] = v.split(' ')[0] + elif line.startswith('Link Metrics Report, status: '): + result['Status'] = line[29:] + return result def link_metrics_mgmt_req_enhanced_ack_based_probing(self, dst_addr: str, @@ -3381,6 +3711,8 @@ def discover_mdns_service(self, instance, name, host_name, timeout=2): fullname = f'{host_name}.local.' if fullname not in elements: continue + if 'Add' not in elements: + continue addresses.append(elements[elements.index(fullname) + 1].split('%')[0]) logging.debug(f'addresses of {host_name}: {addresses}') @@ -3417,7 +3749,7 @@ def discover_mdns_service(self, instance, name, host_name, timeout=2): assert (service['host_fullname'] == f'{host_name}.local.') service['host'] = host_name service['addresses'] = addresses - return service if 'addresses' in service and service['addresses'] else None + return service or None def start_radvd_service(self, prefix, slaac): self.bash("""cat >/etc/radvd.conf <&2') @@ -107,10 +114,12 @@ def parse_args(): import argparse parser = argparse.ArgumentParser(description='Process some integers.') parser.add_argument('--multiply', type=int, default=1, help='run each test for multiple times') + parser.add_argument('--run-directory', type=str, default=None, help='run each test in the specified directory') parser.add_argument("scripts", nargs='+', type=str, help='specify Backbone test scripts') args = parser.parse_args() logging.info("Max jobs: %d", MAX_JOBS) + logging.info("Run directory: %s", args.run_directory or '.') logging.info("Multiply: %d", args.multiply) logging.info("Test scripts: %d", len(args.scripts)) return args @@ -141,7 +150,7 @@ def release(self, port_offset: int): self._pool.put_nowait(port_offset) -def run_tests(scripts: List[str], multiply: int = 1): +def run_tests(scripts: List[str], multiply: int = 1, run_directory: str = None): script_fail_count = Counter() script_succ_count = Counter() @@ -168,7 +177,7 @@ def pass_callback(port_offset, script): for script, i in script_ids: port_offset = port_offset_pool.allocate() pool.apply_async( - run_cert, [i, port_offset, script], + run_cert, [i, port_offset, script, run_directory], callback=lambda ret, port_offset=port_offset, script=script: pass_callback(port_offset, script), error_callback=lambda err, port_offset=port_offset, script=script: error_callback( port_offset, script, err)) @@ -189,7 +198,7 @@ def main(): setup_backbone_env() try: - fail_count = run_tests(args.scripts, args.multiply) + fail_count = run_tests(args.scripts, args.multiply, args.run_directory) exit(fail_count) finally: if has_backbone_tests: diff --git a/tests/scripts/thread-cert/sniffer_transport.py b/tests/scripts/thread-cert/sniffer_transport.py old mode 100755 new mode 100644 diff --git a/tests/scripts/thread-cert/test_child_supervision.py b/tests/scripts/thread-cert/test_child_supervision.py new file mode 100755 index 00000000000..0418f80de3d --- /dev/null +++ b/tests/scripts/thread-cert/test_child_supervision.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +# +# 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. +# + +import unittest + +import command +import config +import thread_cert + +# Test description: +# +# This test verifies behavior child supervision. +# +# +# Topology: +# +# Parent (leader) +# | +# | +# Child (sleepy). + +PARENT = 1 +CHILD = 2 + + +class ChildSupervision(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + SUPPORT_NCP = False + + TOPOLOGY = { + PARENT: { + 'name': 'PARENT', + 'mode': 'rdn', + }, + CHILD: { + 'name': 'CHILD', + 'is_mtd': True, + 'mode': 'n', + }, + } + + def test(self): + parent = self.nodes[PARENT] + child = self.nodes[CHILD] + + # Form the network. + + parent.start() + self.simulator.go(config.LEADER_STARTUP_DELAY) + self.assertEqual(parent.get_state(), 'leader') + + child.start() + self.simulator.go(5) + self.assertEqual(child.get_state(), 'child') + child.set_pollperiod(500) + + self.assertEqual(int(child.get_child_supervision_check_failure_counter()), 0) + + # Check the parent's child table. + + table = parent.get_child_table() + self.assertEqual(len(table), 1) + self.assertEqual(table[1]['suprvsn'], int(child.get_child_supervision_interval())) + + # Change the supervision interval on child. This should trigger an + # MLE Child Update exchange from child to parent so to inform parent + # about the change. Verify that parent is notified by checking the + # parent's child table. + + child.set_child_supervision_interval(20) + + self.simulator.go(2) + + self.assertEqual(int(child.get_child_supervision_interval()), 20) + table = parent.get_child_table() + self.assertEqual(len(table), 1) + self.assertEqual(table[1]['suprvsn'], int(child.get_child_supervision_interval())) + + # Change supervision check timeout on the child. + + child.set_child_supervision_check_timeout(25) + self.assertEqual(int(child.get_child_supervision_check_timeout()), 25) + + # Wait for multiple supervision intervals and ensure that child + # stays attached (child supervision working as expected). + + self.simulator.go(110) + + self.assertEqual(child.get_state(), 'child') + table = parent.get_child_table() + self.assertEqual(len(table), 1) + self.assertEqual(int(child.get_child_supervision_check_failure_counter()), 0) + + # Disable supervision check on child. + + child.set_child_supervision_check_timeout(0) + + # Enable allowlist on parent without adding the child. After child + # timeout expires, the parent should remove the child from its child + # table. + + parent.clear_allowlist() + parent.enable_allowlist() + + table = parent.get_child_table() + child_timeout = table[1]['timeout'] + + self.simulator.go(child_timeout + 1) + table = parent.get_child_table() + self.assertEqual(len(table), 0) + + # Since supervision check is disabled on the child, it should + # continue to stay attached to parent (since data polls are acked by + # radio driver). + + self.assertEqual(child.get_state(), 'child') + self.assertEqual(int(child.get_child_supervision_check_failure_counter()), 0) + + # Re-enable supervision check on child. After the check timeout the + # child must try to exchange "Child Update" messages with parent and + # then detect that parent is not responding and detach. + + child.set_child_supervision_check_timeout(25) + + self.simulator.go(35) + self.assertEqual(child.get_state(), 'detached') + self.assertTrue(int(child.get_child_supervision_check_failure_counter()) > 0) + + # Disable allowlist on parent. Child should be able to attach again. + + parent.disable_allowlist() + self.simulator.go(30) + self.assertEqual(child.get_state(), 'child') + child.reset_child_supervision_check_failure_counter() + self.assertEqual(int(child.get_child_supervision_check_failure_counter()), 0) + + # Set the supervision interval to zero on child (child is asking + # parent not to supervise it anymore). This practically behaves + # the same as if parent does not support child supervision + # feature. + + child.set_child_supervision_interval(0) + child.set_child_supervision_check_timeout(25) + self.simulator.go(2) + + self.assertEqual(int(child.get_child_supervision_interval()), 0) + self.assertEqual(int(child.get_child_supervision_check_timeout()), 25) + + table = parent.get_child_table() + self.assertEqual(len(table), 1) + self.assertEqual(table[2]['suprvsn'], int(child.get_child_supervision_interval())) + + # Wait for multiple check timeouts. The child should still stay + # attached to parent. + + self.simulator.go(100) + self.assertEqual(child.get_state(), 'child') + self.assertEqual(len(parent.get_child_table()), 1) + self.assertTrue(int(child.get_child_supervision_check_failure_counter()) > 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/scripts/thread-cert/test_detach.py b/tests/scripts/thread-cert/test_detach.py index 2c1693d4cf0..954df98447e 100755 --- a/tests/scripts/thread-cert/test_detach.py +++ b/tests/scripts/thread-cert/test_detach.py @@ -145,10 +145,13 @@ def test(self): self.assertEqual(leader.get_state(), 'disabled') leader.start() - self.simulator.go(config.LEADER_STARTUP_DELAY) + # leader didn't become leader after the last start(), so it re-syncs in a non-critical manner thus taking ROUTER_RESET_DELAY to recover + self.simulator.go(config.ROUTER_RESET_DELAY / 2) + self.assertEqual(leader.get_state(), 'detached') + self.simulator.go(config.ROUTER_RESET_DELAY / 2) self.assertEqual(leader.get_state(), 'leader') router1.start() - self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.simulator.go(config.ROUTER_RESET_DELAY) self.assertEqual(router1.get_state(), 'router') leader.thread_stop() diff --git a/tests/scripts/thread-cert/test_dnssd_name_with_special_chars.py b/tests/scripts/thread-cert/test_dnssd_name_with_special_chars.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/test_history_tracker.py b/tests/scripts/thread-cert/test_history_tracker.py index 74bbc1b0457..2424f2a0fce 100755 --- a/tests/scripts/thread-cert/test_history_tracker.py +++ b/tests/scripts/thread-cert/test_history_tracker.py @@ -128,7 +128,7 @@ def test(self): # Start leader and child leader.start() - self.simulator.go(SHORT_WAIT * 2) + self.simulator.go(config.LEADER_RESET_DELAY) self.assertEqual(leader.get_state(), 'leader') child.start() diff --git a/tests/scripts/thread-cert/test_leader_reboot_multiple_link_request.py b/tests/scripts/thread-cert/test_leader_reboot_multiple_link_request.py new file mode 100755 index 00000000000..ad9f00e26ed --- /dev/null +++ b/tests/scripts/thread-cert/test_leader_reboot_multiple_link_request.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2023, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +import copy +import unittest + +import command +import config +import mle +import thread_cert +from pktverify.consts import MLE_PARENT_REQUEST, MLE_LINK_REQUEST, MLE_LINK_ACCEPT, MLE_LINK_ACCEPT_AND_REQUEST, SOURCE_ADDRESS_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, TLV_REQUEST_TLV, VERSION_TLV +from pktverify.packet_verifier import PacketVerifier +from pktverify.null_field import nullField + +DUT_LEADER = 1 +DUT_ROUTER1 = 2 + +# Test Purpose and Description: +# ----------------------------- +# The purpose of this test case is to show that when the Leader is rebooted, it sends MLE_MAX_CRITICAL_TRANSMISSION_COUNT MLE link request packets if no response is received. +# +# Test Topology: +# ------------- +# Leader +# | +# Router +# +# DUT Types: +# ---------- +# Leader +# Router + + +class Test_LeaderRebootMultipleLinkRequest(thread_cert.TestCase): + #USE_MESSAGE_FACTORY = False + + TOPOLOGY = { + DUT_LEADER: { + 'name': 'LEADER', + 'mode': 'rdn', + 'allowlist': [DUT_ROUTER1] + }, + DUT_ROUTER1: { + 'name': 'ROUTER', + 'mode': 'rdn', + 'allowlist': [DUT_LEADER] + }, + } + + def _setUpLeader(self): + self.nodes[DUT_LEADER].add_allowlist(self.nodes[DUT_ROUTER1].get_addr64()) + self.nodes[DUT_LEADER].enable_allowlist() + + def test(self): + self.nodes[DUT_LEADER].start() + self.simulator.go(config.LEADER_STARTUP_DELAY) + self.assertEqual(self.nodes[DUT_LEADER].get_state(), 'leader') + + self.nodes[DUT_ROUTER1].start() + self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.assertEqual(self.nodes[DUT_ROUTER1].get_state(), 'router') + + leader_rloc = self.nodes[DUT_LEADER].get_ip6_address(config.ADDRESS_TYPE.RLOC) + + leader_rloc16 = self.nodes[DUT_LEADER].get_addr16() + self.nodes[DUT_LEADER].reset() + self.assertFalse(self.nodes[DUT_ROUTER1].ping(leader_rloc)) + self._setUpLeader() + + # Router1 will not reply to leader's link request + self.nodes[DUT_ROUTER1].clear_allowlist() + + self.nodes[DUT_LEADER].start() + + self.simulator.go(config.LEADER_RESET_DELAY) + + def verify(self, pv): + pkts = pv.pkts + pv.summary.show() + + LEADER = pv.vars['LEADER'] + ROUTER = pv.vars['ROUTER'] + + # Verify topology is formed correctly. + pv.verify_attached('ROUTER', 'LEADER') + + # The DUT MUST send properly formatted MLE Advertisements with + # an IP Hop Limit of 255 to the Link-Local All Nodes multicast + # address (FF02::1). + # The following TLVs MUST be present in the MLE Advertisements: + # - Leader Data TLV + # - Route64 TLV + # - Source Address TLV + with pkts.save_index(): + pkts.filter_wpan_src64(LEADER).\ + filter_mle_advertisement('Leader').\ + must_next() + pkts.filter_wpan_src64(ROUTER).\ + filter_mle_advertisement('Router').\ + must_next() + + pkts.filter_ping_request().\ + filter_wpan_src64(ROUTER).\ + must_next() + + # The Leader MUST send MLE_MAX_CRITICAL_TRANSMISSION_COUNT multicast Link Request + # The following TLVs MUST be present in the Link Request: + # - Challenge TLV + # - Version TLV + # - TLV Request TLV: Address16 TLV, Route64 TLV + for i in range(0, config.MLE_MAX_CRITICAL_TRANSMISSION_COUNT): + pkts.filter_wpan_src64(LEADER).\ + filter_LLARMA().\ + filter_mle_cmd(MLE_LINK_REQUEST).\ + filter(lambda p: { + CHALLENGE_TLV, + VERSION_TLV, + TLV_REQUEST_TLV, + ADDRESS16_TLV, + ROUTE64_TLV + } <= set(p.mle.tlv.type) and\ + p.mle.tlv.addr16 is nullField and\ + p.mle.tlv.route64.id_mask is nullField + ).\ + must_next() + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/scripts/thread-cert/test_netdata_publisher.py b/tests/scripts/thread-cert/test_netdata_publisher.py index 54854408137..f1a1c5d359b 100755 --- a/tests/scripts/thread-cert/test_netdata_publisher.py +++ b/tests/scripts/thread-cert/test_netdata_publisher.py @@ -461,6 +461,19 @@ def test(self): routes = leader.get_routes() self.check_num_of_routes(routes, num - 1, 0, 1) + # Replace the published route on leader with '::/0'. + leader.netdata_publish_replace(EXTERNAL_ROUTE, '::/0', EXTERNAL_FLAGS, 'med') + self.simulator.go(0.2) + routes = leader.get_routes() + self.assertEqual([route.split(' ')[0] == '::/0' for route in routes].count(True), 1) + self.check_num_of_routes(routes, num - 1, 1, 0) + + # Replace it back to the original route. + leader.netdata_publish_replace('::/0', EXTERNAL_ROUTE, EXTERNAL_FLAGS, 'high') + self.simulator.go(WAIT_TIME) + routes = leader.get_routes() + self.check_num_of_routes(routes, num - 1, 0, 1) + # Publish the same prefix on leader as an on-mesh prefix. Make # sure it is removed from external routes and now seen in the # prefix list. diff --git a/tests/scripts/thread-cert/test_ping_lla_src.py b/tests/scripts/thread-cert/test_ping_lla_src.py old mode 100644 new mode 100755 diff --git a/tests/scripts/thread-cert/test_router_multicast_link_request.py b/tests/scripts/thread-cert/test_router_multicast_link_request.py index 38e76813172..fb366053e1f 100755 --- a/tests/scripts/thread-cert/test_router_multicast_link_request.py +++ b/tests/scripts/thread-cert/test_router_multicast_link_request.py @@ -31,7 +31,7 @@ import config import thread_cert -from pktverify.consts import MLE_LINK_REQUEST, MLE_LINK_ACCEPT +from pktverify.consts import MLE_LINK_REQUEST from pktverify.packet_verifier import PacketVerifier LEADER = 1 @@ -110,30 +110,12 @@ def test(self): self.assertEqual(self.nodes[REED].get_state(), 'router') self.simulator.go(LINK_ESTABLISH_DELAY_THRESHOLD + 3) - def verify(self, pv: PacketVerifier): - pkts = pv.pkts - print(pv.vars) - pv.summary.show() - - REED = pv.vars['REED'] - as_pkt = pkts.filter_wpan_src64(REED).filter_coap_request('/a/as', confirmable=True).must_next() - parent_rloc16 = as_pkt.wpan.dst16 - as_ack_pkt = pkts.filter_wpan_src16(parent_rloc16).filter_coap_ack('/a/as').must_next() - become_router_timestamp = as_ack_pkt.sniff_timestamp - - # REED has just received `/a/as` and become a Router - # REED should send Multicast Link Request after becoming Router - link_request_pkt = pkts.filter_wpan_src64(REED).filter_mle_cmd(MLE_LINK_REQUEST).must_next() - link_request_pkt.must_verify('ipv6.dst == "ff02::2"') - - # REED should send Link Accept to the three Routers - for router in ('ROUTER1', 'ROUTER2', 'ROUTER3'): - with pkts.save_index(): - pkt = pkts.filter_wpan_src64(REED).filter_wpan_dst64( - pv.vars[router]).filter_mle_cmd(MLE_LINK_ACCEPT).must_next() - link_establish_delay = pkt.sniff_timestamp - become_router_timestamp - logging.info("Link to %s established in %.3f seconds", router, link_establish_delay) - self.assertLess(link_establish_delay, LINK_ESTABLISH_DELAY_THRESHOLD) + # Verify that REED has established link with all routers + reed_table = self.nodes[REED].router_table() + reed_id = self.nodes[REED].get_router_id() + for router in [self.nodes[ROUTER1], self.nodes[ROUTER2], self.nodes[ROUTER3]]: + self.assertEqual(reed_table[router.get_router_id()]['link'], 1) + self.assertEqual(router.router_table()[reed_id]['link'], 1) if __name__ == '__main__': diff --git a/tests/scripts/thread-cert/test_router_reboot_multiple_link_request.py b/tests/scripts/thread-cert/test_router_reboot_multiple_link_request.py new file mode 100755 index 00000000000..6773b65a7c9 --- /dev/null +++ b/tests/scripts/thread-cert/test_router_reboot_multiple_link_request.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2023, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +import copy +import unittest + +import command +import config +import mle +import thread_cert +from pktverify.consts import MLE_PARENT_REQUEST, MLE_LINK_REQUEST, MLE_LINK_ACCEPT, MLE_LINK_ACCEPT_AND_REQUEST, SOURCE_ADDRESS_TLV, CHALLENGE_TLV, RESPONSE_TLV, LINK_LAYER_FRAME_COUNTER_TLV, ROUTE64_TLV, ADDRESS16_TLV, LEADER_DATA_TLV, TLV_REQUEST_TLV, VERSION_TLV +from pktverify.packet_verifier import PacketVerifier +from pktverify.null_field import nullField + +LEADER = 1 +DUT_ROUTER = 2 +MED1 = 3 +MED2 = 4 +MED3 = 5 +MED4 = 6 +MED5 = 7 +MED6 = 8 + +# Test Purpose and Description: +# ----------------------------- +# The purpose of this test case is to show that when a router with > 5 children is rebooted, it sends MLE_MAX_CRITICAL_TRANSMISSION_COUNT MLE link request packets if no response is received. +# +# Test Topology: +# ------------- +# Leader +# | +# Router ------------------------+ +# | | | | | | +# MED1 MED2 MED3 MED4 MED5 MED6 +# +# DUT Types: +# ---------- +# Router + + +class Test_LeaderRebootMultipleLinkRequest(thread_cert.TestCase): + #USE_MESSAGE_FACTORY = False + + TOPOLOGY = { + LEADER: { + 'name': 'LEADER', + 'mode': 'rdn', + 'allowlist': [DUT_ROUTER] + }, + DUT_ROUTER: { + 'name': 'ROUTER', + 'mode': 'rdn', + 'allowlist': [LEADER, MED1, MED2, MED3, MED4, MED5, MED6] + }, + MED1: { + 'name': 'MED1', + 'mode': 'rn', + 'allowlist': [DUT_ROUTER] + }, + MED2: { + 'name': 'MED2', + 'mode': 'rn', + 'allowlist': [DUT_ROUTER] + }, + MED3: { + 'name': 'MED3', + 'mode': 'rn', + 'allowlist': [DUT_ROUTER] + }, + MED4: { + 'name': 'MED4', + 'mode': 'rn', + 'allowlist': [DUT_ROUTER] + }, + MED5: { + 'name': 'MED5', + 'mode': 'rn', + 'allowlist': [DUT_ROUTER] + }, + MED6: { + 'name': 'MED6', + 'mode': 'rn', + 'allowlist': [DUT_ROUTER] + }, + } + + def test(self): + self.nodes[LEADER].start() + self.simulator.go(config.LEADER_STARTUP_DELAY) + self.assertEqual(self.nodes[LEADER].get_state(), 'leader') + + self.nodes[DUT_ROUTER].start() + self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.assertEqual(self.nodes[DUT_ROUTER].get_state(), 'router') + + for medid in range(MED1, MED6 + 1): + self.nodes[medid].start() + self.simulator.go(config.ED_STARTUP_DELAY) + self.assertEqual(self.nodes[medid].get_state(), 'child') + + self.simulator.go(config.MAX_ADVERTISEMENT_INTERVAL) + + self.nodes[DUT_ROUTER].reset() + # Leader will not reply to router's link request + self.nodes[LEADER].clear_allowlist() + + self.nodes[DUT_ROUTER].start() + + self.simulator.go(config.LEADER_RESET_DELAY) + + def verify(self, pv): + pkts = pv.pkts + pv.summary.show() + + LEADER = pv.vars['LEADER'] + ROUTER = pv.vars['ROUTER'] + + # Verify topology is formed correctly. + pv.verify_attached('ROUTER', 'LEADER') + for i in range(1, 7): + pv.verify_attached('MED%d' % i, 'ROUTER', 'MTD') + + pkts.filter_wpan_src64(ROUTER).\ + filter_mle_advertisement('Router').\ + must_next() + + # The router MUST send MLE_MAX_CRITICAL_TRANSMISSION_COUNT multicast Link Request + # The following TLVs MUST be present in the Link Request: + # - Challenge TLV + # - Version TLV + # - TLV Request TLV: Address16 TLV, Route64 TLV + for i in range(0, config.MLE_MAX_CRITICAL_TRANSMISSION_COUNT): + pkts.filter_wpan_src64(ROUTER).\ + filter_LLARMA().\ + filter_mle_cmd(MLE_LINK_REQUEST).\ + filter(lambda p: { + CHALLENGE_TLV, + VERSION_TLV, + TLV_REQUEST_TLV, + ADDRESS16_TLV, + ROUTE64_TLV + } <= set(p.mle.tlv.type) and\ + p.mle.tlv.addr16 is nullField and\ + p.mle.tlv.route64.id_mask is nullField + ).\ + must_next() + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/scripts/thread-cert/test_set_mliid.py b/tests/scripts/thread-cert/test_set_mliid.py index 760f0da6c36..292b6aea822 100755 --- a/tests/scripts/thread-cert/test_set_mliid.py +++ b/tests/scripts/thread-cert/test_set_mliid.py @@ -63,7 +63,7 @@ def test(self): self.nodes[LEADER].reset() self.nodes[LEADER].start() - self.simulator.go(config.LEADER_STARTUP_DELAY) + self.simulator.go(config.LEADER_RESET_DELAY) self.assertEqual(self.nodes[LEADER].get_state(), 'leader') # Ensure ML-IID is persistent after reset. diff --git a/tests/scripts/thread-cert/test_srp_lease.py b/tests/scripts/thread-cert/test_srp_lease.py index 946104c5321..6d147117c86 100755 --- a/tests/scripts/thread-cert/test_srp_lease.py +++ b/tests/scripts/thread-cert/test_srp_lease.py @@ -137,7 +137,7 @@ def test(self): self.check_host_and_service(server, client) # - # 4. Clear the first service, shorten the lease time and register a second service. + # 4. Clear the first service, lengthen the lease time and register a second service. # Verify that the lease time of the first service is not affected by new SRP # registrations. # @@ -156,7 +156,7 @@ def test(self): self.assertEqual(len(server.srp_server_get_hosts()), 1) # - # 5. Clear the second service, lengthen the lease time and register a third service. + # 5. Clear the second service, shorten the lease time and register a third service. # Verify that the lease time of the second service is not affected by new SRP # registrations. # @@ -174,6 +174,19 @@ def test(self): self.assertEqual(len(server.srp_server_get_services()), 2) self.assertEqual(len(server.srp_server_get_hosts()), 1) + # + # 6. Clear the third service. The host and services should expire in lease time. + # Verify that the second service and the third service are removed when their host + # expires. + # + client.srp_client_clear_service('my-service3', '_ipps._tcp') + self.simulator.go(LEASE + 2) + self.assertEqual(len(server.srp_server_get_services()), 2) + self.assertEqual(server.srp_server_get_service('my-service2', '_ipps._tcp')['deleted'], 'true') + self.assertEqual(server.srp_server_get_service('my-service3', '_ipps._tcp')['deleted'], 'true') + self.assertEqual(len(server.srp_server_get_hosts()), 1) + self.assertEqual(server.srp_server_get_host('my-host')['deleted'], 'true') + def check_host_and_service(self, server, client): """Check that we have properly registered host and service instance. """ diff --git a/tests/scripts/thread-cert/test_srp_many_services_mtu_check.py b/tests/scripts/thread-cert/test_srp_many_services_mtu_check.py index 89a173fa146..65dfd2e9977 100755 --- a/tests/scripts/thread-cert/test_srp_many_services_mtu_check.py +++ b/tests/scripts/thread-cert/test_srp_many_services_mtu_check.py @@ -101,7 +101,7 @@ def test(self): name = chr(ord('a') + num) * 63 client.srp_client_add_service( name, - '_longsrvname._udp,_subtype1,_subtype2,_subtype3,_subtype4,_subtype5,_subtype6', + '_longlongsrvname._udp,_subtype1,_subtype2,_subtype3,_subtype4,_subtype5,_subtype6', 1977, txt_entries=txt_info) @@ -119,6 +119,23 @@ def test(self): server_services = server.srp_server_get_services() self.assertEqual(len(server_services), num_services) + # Remove all 8 services. + + for num in range(num_services): + name = chr(ord('a') + num) * 63 + client.srp_client_remove_service(name, '_longlongsrvname._udp') + + self.simulator.go(10) + + client_services = client.srp_client_get_services() + self.assertEqual(len(client_services), 0) + + server_services = server.srp_server_get_services() + self.assertEqual(len(server_services), num_services) + + for service in server_services: + self.assertEqual(service['deleted'], 'true') + if __name__ == '__main__': unittest.main() diff --git a/tests/scripts/thread-cert/test_srp_register_services_diff_lease.py b/tests/scripts/thread-cert/test_srp_register_services_diff_lease.py new file mode 100755 index 00000000000..6247527d856 --- /dev/null +++ b/tests/scripts/thread-cert/test_srp_register_services_diff_lease.py @@ -0,0 +1,481 @@ +#!/usr/bin/env python3 +# +# 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. +# + +import ipaddress +import unittest + +import command +import config +import thread_cert + +# Test description: +# +# This test verifies the SRP client and server behavior when services +# with different lease (and/or key lease) intervals are registered. +# +# Topology: +# +# LEADER (SRP server) +# | +# | +# ROUTER (SRP client) +# + +SERVER = 1 +CLIENT = 2 + + +class SrpRegisterServicesDiffLease(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + SUPPORT_NCP = False + + TOPOLOGY = { + SERVER: { + 'name': 'SRP_SERVER', + 'mode': 'rdn', + }, + CLIENT: { + 'name': 'SRP_CLIENT', + 'mode': 'rdn', + }, + } + + def test(self): + server = self.nodes[SERVER] + client = self.nodes[CLIENT] + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Start the server and client. + + server.start() + self.simulator.go(config.LEADER_STARTUP_DELAY) + self.assertEqual(server.get_state(), 'leader') + + client.start() + self.simulator.go(config.ROUTER_STARTUP_DELAY) + self.assertEqual(client.get_state(), 'router') + + server.srp_server_set_enabled(True) + client.srp_client_enable_auto_start_mode() + + self.simulator.go(5) + + client.srp_client_set_host_name('host') + client.srp_client_enable_auto_host_address() + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Add a service with specific lease and key lease and verify that + # it is successfully registered and seen with same lease/key-lease + # on server. + + client.srp_client_add_service('ins1', '_test._udp', 1111, lease=60, key_lease=800) + + self.simulator.go(5) + + self.check_services_on_client(client, 1) + services = server.srp_server_get_services() + self.assertEqual(len(services), 1) + service = services[0] + self.assertEqual(service['fullname'], 'ins1._test._udp.default.service.arpa.') + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 60) + self.assertEqual(int(service['lease']), 60) + self.assertEqual(int(service['key-lease']), 800) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Register two more services with different lease intervals. + + client.srp_client_add_service('ins2', '_test._udp', 2222, lease=30, key_lease=200) + client.srp_client_add_service('ins3', '_test._udp', 3333, lease=100, key_lease=1000) + + self.simulator.go(10) + + self.check_services_on_client(client, 3) + server_services = server.srp_server_get_services() + self.assertEqual(len(server_services), 3) + for service in server_services: + if service['fullname'] == 'ins1._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 60) + self.assertEqual(int(service['lease']), 60) + self.assertEqual(int(service['key-lease']), 800) + elif service['fullname'] == 'ins2._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 30) + self.assertEqual(int(service['lease']), 30) + self.assertEqual(int(service['key-lease']), 200) + elif service['fullname'] == 'ins3._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 100) + self.assertEqual(int(service['lease']), 100) + self.assertEqual(int(service['key-lease']), 1000) + else: + self.assertTrue(False) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Wait for longest lease time to validate that all services renew their + # lease successfully. + + self.simulator.go(105) + + self.check_services_on_client(client, 3) + server_services = server.srp_server_get_services() + self.assertEqual(len(server_services), 3) + for service in server_services: + self.assertEqual(service['deleted'], 'false') + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Remove two services. + + client.srp_client_remove_service('ins2', '_test._udp') + client.srp_client_remove_service('ins3', '_test._udp') + + self.simulator.go(10) + + self.check_services_on_client(client, 1) + server_services = server.srp_server_get_services() + self.assertEqual(len(server_services), 3) + for service in server_services: + if service['fullname'] == 'ins1._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 60) + self.assertEqual(int(service['lease']), 60) + self.assertEqual(int(service['key-lease']), 800) + elif service['fullname'] == 'ins2._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'true') + elif service['fullname'] == 'ins3._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'true') + else: + self.assertTrue(False) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Wait for longer than key-lease of `ins2` service and check that it is + # removed on server. + + self.simulator.go(201) + + self.check_services_on_client(client, 1) + server_services = server.srp_server_get_services() + self.assertEqual(len(server_services), 2) + for service in server_services: + if service['fullname'] == 'ins1._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 60) + self.assertEqual(int(service['lease']), 60) + self.assertEqual(int(service['key-lease']), 800) + elif service['fullname'] == 'ins3._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'true') + else: + self.assertTrue(False) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Add both services again now with same lease intervals. + + client.srp_client_add_service('ins2', '_test._udp', 2222, lease=30, key_lease=100) + client.srp_client_add_service('ins3', '_test._udp', 3333, lease=30, key_lease=100) + + self.simulator.go(10) + + self.check_services_on_client(client, 3) + server_services = server.srp_server_get_services() + self.assertEqual(len(server_services), 3) + for service in server_services: + if service['fullname'] == 'ins1._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 60) + self.assertEqual(int(service['lease']), 60) + self.assertEqual(int(service['key-lease']), 800) + elif service['fullname'] == 'ins2._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 30) + self.assertEqual(int(service['lease']), 30) + self.assertEqual(int(service['key-lease']), 100) + elif service['fullname'] == 'ins3._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 30) + self.assertEqual(int(service['lease']), 30) + self.assertEqual(int(service['key-lease']), 100) + else: + self.assertTrue(False) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Remove `ins1` while adding a new service with same key-lease as + # `ins1` but different lease interval. + + client.srp_client_remove_service('ins1', '_test._udp') + client.srp_client_add_service('ins4', '_test._udp', 4444, lease=90, key_lease=800) + + self.simulator.go(5) + + self.check_services_on_client(client, 3) + server_services = server.srp_server_get_services() + self.assertEqual(len(server_services), 4) + for service in server_services: + if service['fullname'] == 'ins1._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'true') + elif service['fullname'] == 'ins2._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 30) + self.assertEqual(int(service['lease']), 30) + self.assertEqual(int(service['key-lease']), 100) + elif service['fullname'] == 'ins3._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 30) + self.assertEqual(int(service['lease']), 30) + self.assertEqual(int(service['key-lease']), 100) + elif service['fullname'] == 'ins4._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 90) + self.assertEqual(int(service['lease']), 90) + self.assertEqual(int(service['key-lease']), 800) + else: + self.assertTrue(False) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Remove two services `ins2` and `ins3` (they now have same key lease). + + client.srp_client_remove_service('ins2', '_test._udp') + client.srp_client_remove_service('ins3', '_test._udp') + + self.simulator.go(10) + + self.check_services_on_client(client, 1) + server_services = server.srp_server_get_services() + self.assertEqual(len(server_services), 4) + for service in server_services: + if service['fullname'] == 'ins1._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'true') + elif service['fullname'] == 'ins2._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'true') + elif service['fullname'] == 'ins3._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'true') + elif service['fullname'] == 'ins4._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 90) + self.assertEqual(int(service['lease']), 90) + self.assertEqual(int(service['key-lease']), 800) + else: + self.assertTrue(False) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Add `ins1` with key-lease smaller than lease and check that + # client handles this properly (uses the lease value for + # key-lease). + + client.srp_client_add_service('ins1', '_test._udp', 1111, lease=100, key_lease=90) + + self.simulator.go(10) + + self.check_services_on_client(client, 2) + server_services = server.srp_server_get_services() + self.assertEqual(len(server_services), 4) + for service in server_services: + if service['fullname'] == 'ins1._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 100) + self.assertEqual(int(service['lease']), 100) + self.assertEqual(int(service['key-lease']), 100) + elif service['fullname'] == 'ins2._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'true') + elif service['fullname'] == 'ins3._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'true') + elif service['fullname'] == 'ins4._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 90) + self.assertEqual(int(service['lease']), 90) + self.assertEqual(int(service['key-lease']), 800) + else: + self.assertTrue(False) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Change default lease and key-lease intervals on client. + + client.srp_client_set_lease_interval(40) + self.assertEqual(client.srp_client_get_lease_interval(), 40) + + client.srp_client_set_key_lease_interval(330) + self.assertEqual(client.srp_client_get_key_lease_interval(), 330) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Add `ins2` and `ins3`. `ins2` specifies the key-lease explicitly but + # leaves lease as default. `ins3` does the opposite. + + client.srp_client_add_service('ins2', '_test._udp', 2222, key_lease=330) + client.srp_client_add_service('ins3', '_test._udp', 3333, lease=40) + + self.simulator.go(10) + + self.check_services_on_client(client, 4) + server_services = server.srp_server_get_services() + self.assertEqual(len(server_services), 4) + for service in server_services: + if service['fullname'] == 'ins1._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 100) + self.assertEqual(int(service['lease']), 100) + self.assertEqual(int(service['key-lease']), 100) + elif service['fullname'] == 'ins2._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 40) + self.assertEqual(int(service['lease']), 40) + self.assertEqual(int(service['key-lease']), 330) + elif service['fullname'] == 'ins3._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 40) + self.assertEqual(int(service['lease']), 40) + self.assertEqual(int(service['key-lease']), 330) + elif service['fullname'] == 'ins4._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 90) + self.assertEqual(int(service['lease']), 90) + self.assertEqual(int(service['key-lease']), 800) + else: + self.assertTrue(False) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Change the default lease to 50 and wait for long enough for `ins2` + # and `ins3` to do lease refresh. Validate that `ins2` now requests + # new default lease of 50 while `ins3` should stay as before. + + client.srp_client_set_lease_interval(50) + self.assertEqual(client.srp_client_get_lease_interval(), 50) + + self.simulator.go(45) + + self.check_services_on_client(client, 4) + server_services = server.srp_server_get_services() + self.assertEqual(len(server_services), 4) + for service in server_services: + if service['fullname'] == 'ins1._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 100) + self.assertEqual(int(service['lease']), 100) + self.assertEqual(int(service['key-lease']), 100) + elif service['fullname'] == 'ins2._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 50) + self.assertEqual(int(service['lease']), 50) + self.assertEqual(int(service['key-lease']), 330) + elif service['fullname'] == 'ins3._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 40) + self.assertEqual(int(service['lease']), 40) + self.assertEqual(int(service['key-lease']), 330) + elif service['fullname'] == 'ins4._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 90) + self.assertEqual(int(service['lease']), 90) + self.assertEqual(int(service['key-lease']), 800) + else: + self.assertTrue(False) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Change the default key lease to 30. `ins3` should adopt this but + # since it is shorter than its explicitly specified lease the + # client should use same value for both lease and key-lease. + + client.srp_client_set_key_lease_interval(35) + self.assertEqual(client.srp_client_get_key_lease_interval(), 35) + + self.simulator.go(45) + + self.check_services_on_client(client, 4) + server_services = server.srp_server_get_services() + self.assertEqual(len(server_services), 4) + for service in server_services: + if service['fullname'] == 'ins1._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 100) + self.assertEqual(int(service['lease']), 100) + self.assertEqual(int(service['key-lease']), 100) + elif service['fullname'] == 'ins2._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 50) + self.assertEqual(int(service['lease']), 50) + self.assertEqual(int(service['key-lease']), 330) + elif service['fullname'] == 'ins3._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 40) + self.assertEqual(int(service['lease']), 40) + self.assertEqual(int(service['key-lease']), 40) + elif service['fullname'] == 'ins4._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 90) + self.assertEqual(int(service['lease']), 90) + self.assertEqual(int(service['key-lease']), 800) + else: + self.assertTrue(False) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # Change the requested TTL. Wait for long enough for all + # services to refresh and check that the new TTL is correctly + # requested by the client (when it is not larger than + # service lease). + + client.srp_client_set_ttl(65) + self.assertEqual(client.srp_client_get_ttl(), 65) + + self.simulator.go(110) + + self.check_services_on_client(client, 4) + server_services = server.srp_server_get_services() + self.assertEqual(len(server_services), 4) + for service in server_services: + if service['fullname'] == 'ins1._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 65) + self.assertEqual(int(service['lease']), 100) + self.assertEqual(int(service['key-lease']), 100) + elif service['fullname'] == 'ins2._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 50) + self.assertEqual(int(service['lease']), 50) + self.assertEqual(int(service['key-lease']), 330) + elif service['fullname'] == 'ins3._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 40) + self.assertEqual(int(service['lease']), 40) + self.assertEqual(int(service['key-lease']), 40) + elif service['fullname'] == 'ins4._test._udp.default.service.arpa.': + self.assertEqual(service['deleted'], 'false') + self.assertEqual(int(service['ttl']), 65) + self.assertEqual(int(service['lease']), 90) + self.assertEqual(int(service['key-lease']), 800) + else: + self.assertTrue(False) + + def check_services_on_client(self, client, expected_num_services): + services = client.srp_client_get_services() + self.assertEqual(len(services), expected_num_services) + for service in client.srp_client_get_services(): + self.assertIn(service['state'], ['Registered', 'ToRefresh', 'Refreshing']) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/scripts/thread-cert/thread_cert.py b/tests/scripts/thread-cert/thread_cert.py old mode 100755 new mode 100644 index 369ed5075f3..7277d3bb916 --- a/tests/scripts/thread-cert/thread_cert.py +++ b/tests/scripts/thread-cert/thread_cert.py @@ -38,7 +38,7 @@ import time import traceback import unittest -from typing import Optional, Callable, Union, Any +from typing import Optional, Callable, Union, Mapping, Any import config import debug @@ -592,3 +592,16 @@ def wait_route_established(self, node1: int, node2: int, timeout=10): else: raise Exception("Route between node %d and %d is not established" % (node1, node2)) + + def assertDictIncludes(self, actual: Mapping[str, str], expected: Mapping[str, str]): + """ Asserts the `actual` dict includes the `expected` dict. + + Args: + actual: A dict for checking. + expected: The expected items that the actual dict should contains. + """ + for k, v in expected.items(): + if k not in actual: + raise AssertionError(f"key {k} is not found in first dict") + if v != actual[k]: + raise AssertionError(f"{repr(actual[k])} != {repr(v)} for key {k}") diff --git a/tests/scripts/thread-cert/tlvs_parsing.py b/tests/scripts/thread-cert/tlvs_parsing.py old mode 100755 new mode 100644 diff --git a/tests/scripts/thread-cert/v1_2_LowPower_7_1_01_SingleProbeLinkMetricsWithEnhancedAcks.py b/tests/scripts/thread-cert/v1_2_LowPower_7_1_01_SingleProbeLinkMetricsWithEnhancedAcks.py index e14e49a7b5b..dea41987574 100755 --- a/tests/scripts/thread-cert/v1_2_LowPower_7_1_01_SingleProbeLinkMetricsWithEnhancedAcks.py +++ b/tests/scripts/thread-cert/v1_2_LowPower_7_1_01_SingleProbeLinkMetricsWithEnhancedAcks.py @@ -202,7 +202,7 @@ def verify(self, pv): pkts.filter_wpan_src64(SED_1) \ .filter_wpan_dst64(LEADER) \ .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \ - .filter(lambda p: p.mle.tlv.link_sub_tlv == consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV) \ + .filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \ .filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_REGISTER) \ .filter(lambda p: p.mle.tlv.link_requested_type_id_flags == '0a') \ .must_next() @@ -231,7 +231,7 @@ def verify(self, pv): # ---- Metrics Enum = 3 (RSSI) pkts.filter_wpan_src64(SSED_1) \ .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \ - .filter(lambda p: p.mle.tlv.link_sub_tlv == consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV) \ + .filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \ .filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_REGISTER) \ .filter(lambda p: p.mle.tlv.link_requested_type_id_flags == '0a0b') \ .must_next() @@ -295,7 +295,7 @@ def verify(self, pv): pkts.filter_wpan_src64(SSED_1) \ .filter_wpan_dst64(LEADER) \ .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \ - .filter(lambda p: p.mle.tlv.link_sub_tlv == consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV) \ + .filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \ .filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_CLEAR) \ .must_next() @@ -340,7 +340,7 @@ def verify(self, pv): pkts.filter_wpan_src64(SSED_1) \ .filter_wpan_dst64(LEADER) \ .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \ - .filter(lambda p: p.mle.tlv.link_sub_tlv == consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV) \ + .filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \ .filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_REGISTER) \ .filter(lambda p: p.mle.tlv.link_requested_type_id_flags == '090a0b') \ .must_next() @@ -369,7 +369,7 @@ def verify(self, pv): pkts.filter_wpan_src64(SSED_1) \ .filter_wpan_dst64(LEADER) \ .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \ - .filter(lambda p: p.mle.tlv.link_sub_tlv == consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV) \ + .filter(lambda p: consts.LM_ENHANCED_ACK_CONFIGURATION_SUB_TLV in p.mle.tlv.link_sub_tlv) \ .filter(lambda p: p.mle.tlv.link_enh_ack_flags == consts.LINK_METRICS_ENH_ACK_PROBING_REGISTER) \ .filter(lambda p: p.mle.tlv.link_requested_type_id_flags == '12') \ .must_next() diff --git a/tests/scripts/thread-cert/v1_2_LowPower_7_1_02_SingleProbeLinkMetricsWithoutEnhancedAck.py b/tests/scripts/thread-cert/v1_2_LowPower_7_1_02_SingleProbeLinkMetricsWithoutEnhancedAck.py new file mode 100755 index 00000000000..92ab70cd1b7 --- /dev/null +++ b/tests/scripts/thread-cert/v1_2_LowPower_7_1_02_SingleProbeLinkMetricsWithoutEnhancedAck.py @@ -0,0 +1,388 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2023, The OpenThread Authors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the +# names of its contributors may be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +import unittest + +from config import ADDRESS_TYPE +from mle import LinkMetricsSubTlvType +from pktverify import consts +from pktverify.null_field import nullField +from pktverify.packet_verifier import PacketVerifier + +import config +import thread_cert + +LEADER = 1 +SED_1 = 2 +SSED_1 = 3 + +SERIES_ID = 1 +SERIES_ID_2 = 2 +POLL_PERIOD = 2000 # 2s + + +class LowPower_7_1_02_SingleProbeLinkMetricsWithoutEnhancedAck(thread_cert.TestCase): + USE_MESSAGE_FACTORY = False + TOPOLOGY = { + LEADER: { + 'version': '1.2', + 'name': 'LEADER', + 'mode': 'rdn', + 'allowlist': [SED_1, SSED_1], + }, + SED_1: { + 'version': '1.2', + 'name': 'SED_1', + 'mode': '-', + 'allowlist': [LEADER], + }, + SSED_1: { + 'version': '1.2', + 'name': 'SSED_1', + 'mode': '-', + 'allowlist': [LEADER], + } + } + """All nodes are created with default configurations""" + + def test(self): + self.nodes[LEADER].start() + self.simulator.go(config.LEADER_STARTUP_DELAY) + self.assertEqual(self.nodes[LEADER].get_state(), 'leader') + + self.nodes[SED_1].set_pollperiod(POLL_PERIOD) + self.nodes[SED_1].start() + self.simulator.go(5) + self.assertEqual(self.nodes[SED_1].get_state(), 'child') + + self.nodes[SSED_1].set_csl_period(consts.CSL_DEFAULT_PERIOD) + self.nodes[SSED_1].start() + self.simulator.go(5) + self.assertEqual(self.nodes[SSED_1].get_state(), 'child') + + leader_addr = self.nodes[LEADER].get_ip6_address(ADDRESS_TYPE.LINK_LOCAL) + sed_extaddr = self.nodes[SED_1].get_addr64() + + # Step 3 - Verify connectivity by instructing each device to send an ICMPv6 Echo Request to the DUT + self.assertTrue(self.nodes[SED_1].ping(leader_addr, timeout=POLL_PERIOD / 1000)) + self.assertTrue(self.nodes[SSED_1].ping(leader_addr, timeout=(2 * consts.CSL_DEFAULT_PERIOD_IN_SECOND))) + self.simulator.go(5) + + # Step 4 - SED_1 sends a Single Probe Link Metric for RSSI using MLE Data Request + # MLE Data Request Payload: + # - TLV Request TLV (Link Metrics Report TLV specified) + # - Link Metrics Query TLV + # -- Link Metrics Query ID Sub-TLV + # --- Query ID = 0 (Single Probe Query) + # -- Link Metrics Query Options Sub-TLV + # --- Metric Type ID Flags + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 3 (RSSI) + # + # In this step, SED_1 should set its TxPower to 'High'. In simulation, this will be implemented by + # setting Macfilter on the Rx side (Leader). + self.nodes[LEADER].add_allowlist(sed_extaddr, -30) + res = self.nodes[SED_1].link_metrics_query_single_probe(leader_addr, 'r', 'block') + rss_1 = int(res['RSSI']) + self.simulator.go(5) + + # Step 6 - SED_1 sends a Single Probe Link Metric for RSSI using MLE Data Request + # MLE Data Request Payload: + # - TLV Request TLV (Link Metrics Report TLV specified) + # - Link Metrics Query TLV + # -- Link Metrics Query ID Sub-TLV + # --- Query ID = 0 (Single Probe Query) + # -- Link Metrics Query Options Sub-TLV + # --- Metric Type ID Flags + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 3 (RSSI) + # + # In this step, SED_1 should set its TxPower to 'Low'. + self.nodes[LEADER].add_allowlist(sed_extaddr, -95) + res = self.nodes[SED_1].link_metrics_query_single_probe(leader_addr, 'r', 'block') + rss_2 = int(res['RSSI']) + self.simulator.go(5) + + # Step 8 - Compare the rssi value in step 5 & 7, RSSI in 5 should be larger than RSSI in 7 + self.assertTrue(rss_1 > rss_2) + + # Step 9 - SSED_1 sends a Single Probe Link Metric for Layer 2 LQI using MLE Data Request + # MLE Data Request Payload: + # - TLV Request TLV (Link Metrics Report TLV specified) + # - Link Metrics Query TLV + # -- Link Metrics Query ID Sub-TLV + # --- Query ID = 0 (Single Probe Query) + # -- Link Metrics Query Options Sub-TLV + # --- Metric Type ID Flags + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 1 (Layer 2 LQI) + self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'q', 'block') + self.simulator.go(5) + + # Step 11 - SSED_1 sends a Single Probe Link Metric for Link Margin using MLE Data Request + # MLE Data Request Payload: + # - TLV Request TLV (Link Metrics Report TLV specified) + # - Link Metrics Query TLV + # -- Link Metrics Query ID Sub-TLV + # --- Query ID = 0 (Single Probe Query) + # -- Link Metrics Query Options Sub-TLV + # --- Metric Type ID Flags + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 2 (Link Margin) + self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'm', 'block') + self.simulator.go(5) + + # Step 13 - SSED_1 sends a Single Probe Link Metric using MLE Data Request + # MLE Data Request Payload: + # - TLV Request TLV (Link Metrics Report TLV specified) + # - Link Metrics Query TLV + # -- Link Metrics Query ID Sub-TLV + # --- Query ID = 0 (Single Probe Query) + # -- Link Metrics Query Options Sub-TLV + # --- Metric Type ID Flags + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 1 (Layer 2 LQI) + # ---- Metric Enum = 2 (Link Margin) + # ---- Metric Enum = 3 (RSSI) + self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'qmr', 'block') + self.simulator.go(5) + + def verify(self, pv): + pkts = pv.pkts + pv.summary.show() + LEADER = pv.vars['LEADER'] + SED_1 = pv.vars['SED_1'] + SSED_1 = pv.vars['SSED_1'] + + # Step 3 - The DUT MUST send ICMPv6 Echo Responses to both SED1 & SSED1 + pkts.filter_wpan_src64(LEADER) \ + .filter_wpan_dst64(SED_1) \ + .filter_ping_reply() \ + .must_next() + pkts.filter_wpan_src64(LEADER) \ + .filter_wpan_dst64(SSED_1) \ + .filter_ping_reply() \ + .must_next() + + # Step 4 - SED_1 sends a Single Probe Link Metric for RSSI using MLE Data Request + # MLE Data Request Payload: + # - TLV Request TLV (Link Metrics Report TLV specified) + # - Link Metrics Query TLV + # -- Link Metrics Query ID Sub-TLV + # --- Query ID = 0 (Single Probe Query) + # -- Link Metrics Query Options Sub-TLV + # --- Metric Type ID Flags + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 3 (RSSI) + # TODO: Currently the ot-pktverify version of wireshark cannot parse Link Metrics Query Options Sub-TLV correctly. Will add check for the fields after fixing this. + pkts.filter_wpan_src64(SED_1) \ + .filter_wpan_dst64(LEADER) \ + .filter_mle_cmd(consts.MLE_DATA_REQUEST) \ + .filter_mle_has_tlv(consts.TLV_REQUEST_TLV) \ + .filter_mle_has_tlv(consts.LINK_METRICS_QUERY_TLV) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_ID in p.mle.tlv.link_sub_tlv) \ + .filter(lambda p: p.mle.tlv.query_id == 0) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_OPTIONS in p.mle.tlv.link_sub_tlv) \ + .must_next() + + # Step 5 The DUT MUST reply to SED_1 with MLE Data Response with the following: + # - Link Metrics Report TLV + # -- Link Metrics Report Sub-TLV + # --- Metric Type ID Flags + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 3 (RSSI) + # --- RSSI Value (1-byte) + pkts.filter_wpan_src64(LEADER) \ + .filter_wpan_dst64(SED_1) \ + .filter_mle_cmd(consts.MLE_DATA_RESPONSE) \ + .filter_mle_has_tlv(consts.LINK_METRICS_REPORT_TLV) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_REPORT in p.mle.tlv.link_sub_tlv) \ + .filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_EXPONENTIAL in p.mle.tlv.metric_type_id_flags.type) \ + .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_RSSI in p.mle.tlv.metric_type_id_flags.metric) \ + .must_next() + + # Step 6 - SED_1 sends a Single Probe Link Metric for RSSI using MLE Data Request + # MLE Data Request Payload: + # - TLV Request TLV (Link Metrics Report TLV specified) + # - Link Metrics Query TLV + # -- Link Metrics Query ID Sub-TLV + # --- Query ID = 0 (Single Probe Query) + # -- Link Metrics Query Options Sub-TLV + # --- Metric Type ID Flags + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 3 (RSSI) + pkts.filter_wpan_src64(SED_1) \ + .filter_wpan_dst64(LEADER) \ + .filter_mle_cmd(consts.MLE_DATA_REQUEST) \ + .filter_mle_has_tlv(consts.TLV_REQUEST_TLV) \ + .filter_mle_has_tlv(consts.LINK_METRICS_QUERY_TLV) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_ID in p.mle.tlv.link_sub_tlv) \ + .filter(lambda p: p.mle.tlv.query_id == 0) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_OPTIONS in p.mle.tlv.link_sub_tlv) \ + .must_next() + + # Step 7 The DUT MUST reply to SED_1 with MLE Data Response with the following: + # - Link Metrics Report TLV + # -- Link Metrics Report Sub-TLV + # --- Metric Type ID Flags + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 3 (RSSI) + # --- RSSI Value (1-byte) + pkts.filter_wpan_src64(LEADER) \ + .filter_wpan_dst64(SED_1) \ + .filter_mle_cmd(consts.MLE_DATA_RESPONSE) \ + .filter_mle_has_tlv(consts.LINK_METRICS_REPORT_TLV) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_REPORT in p.mle.tlv.link_sub_tlv) \ + .filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_EXPONENTIAL in p.mle.tlv.metric_type_id_flags.type) \ + .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_RSSI in p.mle.tlv.metric_type_id_flags.metric) \ + .must_next() + + # Step 9 - SSED_1 sends a Single Probe Link Metric for Layer 2 LQI using MLE Data Request + # MLE Data Request Payload: + # - TLV Request TLV (Link Metrics Report TLV specified) + # - Link Metrics Query TLV + # -- Link Metrics Query ID Sub-TLV + # --- Query ID = 0 (Single Probe Query) + # -- Link Metrics Query Options Sub-TLV + # --- Metric Type ID Flags + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 1 (Layer 2 LQI) + pkts.filter_wpan_src64(SSED_1) \ + .filter_wpan_dst64(LEADER) \ + .filter_mle_cmd(consts.MLE_DATA_REQUEST) \ + .filter_mle_has_tlv(consts.TLV_REQUEST_TLV) \ + .filter_mle_has_tlv(consts.LINK_METRICS_QUERY_TLV) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_ID in p.mle.tlv.link_sub_tlv) \ + .filter(lambda p: p.mle.tlv.query_id == 0) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_OPTIONS in p.mle.tlv.link_sub_tlv) \ + .must_next() + + # Step 10 The DUT MUST reply to SSED_1 with MLE Data Response with the following: + # - Link Metrics Report TLV + # -- Link Metrics Report Sub-TLV + # --- Metric Type ID Flags + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 1 (Layer 2 LQI) + # --- Layer 2 LQI value (1-byte) + pkts.filter_wpan_src64(LEADER) \ + .filter_wpan_dst64(SSED_1) \ + .filter_mle_cmd(consts.MLE_DATA_RESPONSE) \ + .filter_mle_has_tlv(consts.LINK_METRICS_REPORT_TLV) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_REPORT in p.mle.tlv.link_sub_tlv) \ + .filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_EXPONENTIAL in p.mle.tlv.metric_type_id_flags.type) \ + .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_LQI in p.mle.tlv.metric_type_id_flags.metric) \ + .must_next() + + # Step 11 - SSED_1 sends a Single Probe Link Metric for Link Margin using MLE Data Request + # MLE Data Request Payload: + # - TLV Request TLV (Link Metrics Report TLV specified) + # - Link Metrics Query TLV + # -- Link Metrics Query ID Sub-TLV + # --- Query ID = 0 (Single Probe Query) + # -- Link Metrics Query Options Sub-TLV + # --- Metric Type ID Flags + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 2 (Link Margin) + pkts.filter_wpan_src64(SSED_1) \ + .filter_wpan_dst64(LEADER) \ + .filter_mle_cmd(consts.MLE_DATA_REQUEST) \ + .filter_mle_has_tlv(consts.TLV_REQUEST_TLV) \ + .filter_mle_has_tlv(consts.LINK_METRICS_QUERY_TLV) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_ID in p.mle.tlv.link_sub_tlv) \ + .filter(lambda p: p.mle.tlv.query_id == 0) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_OPTIONS in p.mle.tlv.link_sub_tlv) \ + .must_next() + + # Step 12 The DUT MUST reply to SSED_1 with MLE Data Response with the following: + # - Link Metrics Report TLV + # -- Link Metrics Report Sub-TLV + # --- Metric Type ID Flags + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 2 (Link Margin) + # --- Link Margin value (1-byte) + pkts.filter_wpan_src64(LEADER) \ + .filter_wpan_dst64(SSED_1) \ + .filter_mle_cmd(consts.MLE_DATA_RESPONSE) \ + .filter_mle_has_tlv(consts.LINK_METRICS_REPORT_TLV) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_REPORT in p.mle.tlv.link_sub_tlv) \ + .filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_EXPONENTIAL in p.mle.tlv.metric_type_id_flags.type) \ + .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_LINK_MARGIN in p.mle.tlv.metric_type_id_flags.metric) \ + .must_next() + + # Step 13 - SSED_1 sends a Single Probe Link Metric using MLE Data Request + # MLE Data Request Payload: + # - TLV Request TLV (Link Metrics Report TLV specified) + # - Link Metrics Query TLV + # -- Link Metrics Query ID Sub-TLV + # --- Query ID = 0 (Single Probe Query) + # -- Link Metrics Query Options Sub-TLV + # --- Metric Type ID Flags + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 1 (Layer 2 LQI) + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 2 (Link Margin) + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 3 (RSSI) + pkts.filter_wpan_src64(SSED_1) \ + .filter_wpan_dst64(LEADER) \ + .filter_mle_cmd(consts.MLE_DATA_REQUEST) \ + .filter_mle_has_tlv(consts.TLV_REQUEST_TLV) \ + .filter_mle_has_tlv(consts.LINK_METRICS_QUERY_TLV) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_ID in p.mle.tlv.link_sub_tlv) \ + .filter(lambda p: p.mle.tlv.query_id == 0) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_QUERY_OPTIONS in p.mle.tlv.link_sub_tlv) \ + .must_next() + + # Step 14 The DUT MUST reply to SSED_1 with MLE Data Response with the following: + # - Link Metrics Report TLV + # -- Link Metrics Report Sub-TLV + # --- Metric Type ID Flags + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 1 (Layer 2 LQI) + # ---- Layer 2 LQI value (1-byte) + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 2 (Link Margin) + # ---- Link Margin value (1-byte) + # ---- Type / Average Enum = 1 (Exponential Moving Avg) + # ---- Metric Enum = 3 (RSSI) + # ---- RSSI value (1-byte) + pkts.filter_wpan_src64(LEADER) \ + .filter_wpan_dst64(SSED_1) \ + .filter_mle_cmd(consts.MLE_DATA_RESPONSE) \ + .filter_mle_has_tlv(consts.LINK_METRICS_REPORT_TLV) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_REPORT in p.mle.tlv.link_sub_tlv) \ + .filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_EXPONENTIAL in p.mle.tlv.metric_type_id_flags.type) \ + .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_LQI in p.mle.tlv.metric_type_id_flags.metric) \ + .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_LINK_MARGIN in p.mle.tlv.metric_type_id_flags.metric) \ + .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_RSSI in p.mle.tlv.metric_type_id_flags.metric) \ + .must_next() + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/scripts/thread-cert/v1_2_LowPower_7_2_01_ForwardTrackingSeries.py b/tests/scripts/thread-cert/v1_2_LowPower_7_2_01_ForwardTrackingSeries.py index b9543cf103b..7cb02415665 100755 --- a/tests/scripts/thread-cert/v1_2_LowPower_7_2_01_ForwardTrackingSeries.py +++ b/tests/scripts/thread-cert/v1_2_LowPower_7_2_01_ForwardTrackingSeries.py @@ -111,9 +111,10 @@ def test(self): self.simulator.go(30) # Step 7 - SED_1 sends an MLE Data Request to retrieve aggregated Forward Series Results - self.nodes[SED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID) + result = self.nodes[SED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID, 'block') + self.assertIn("PDU Counter", result) - self.simulator.go(5) + #self.simulator.go(5) # Step 9 - SED_1 clears the Forward Tracking Series # Forward Series Flags = 0x00: @@ -144,9 +145,8 @@ def test(self): self.simulator.go(1) # Step 19 - SSED_1 sends an MLE Data Request to retrieve aggregated Forward Series Results - self.nodes[SSED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2) - - self.simulator.go(5) + result = self.nodes[SSED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2, 'block') + self.assertIn("Margin", result) # Step 21 - SSED_1 clears the Forward Series Link Metrics # Forward Series Flags = 0x00: @@ -157,9 +157,9 @@ def test(self): self.simulator.go(5) # Step 23 - SSED_1 sends an MLE Data Request to retrieve aggregated Forward Series Results - self.nodes[SSED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2) - - self.simulator.go(5) + result = self.nodes[SSED_1].link_metrics_query_forward_tracking_series(leader_addr, SERIES_ID_2, 'block') + self.assertIn('Status', result) + self.assertEqual(result['Status'], 'Series ID not recognized') def verify(self, pv): pkts = pv.pkts @@ -191,7 +191,7 @@ def verify(self, pv): pkts.filter_wpan_src64(SED_1) \ .filter_wpan_dst64(LEADER) \ .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \ - .filter(lambda p: p.mle.tlv.link_sub_tlv == LinkMetricsSubTlvType.FORWARD_PROBING_REGISTRATION) \ + .filter(lambda p: LinkMetricsSubTlvType.FORWARD_PROBING_REGISTRATION in p.mle.tlv.link_sub_tlv) \ .filter(lambda p: 4 in p.mle.tlv.link_forward_series) \ .filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_COUNT in p.mle.tlv.metric_type_id_flags.type) \ .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_PDU_COUNT in p.mle.tlv.metric_type_id_flags.metric) \ @@ -221,7 +221,7 @@ def verify(self, pv): pkts.filter_wpan_src64(SED_1) \ .filter_wpan_dst64(LEADER) \ .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \ - .filter(lambda p: p.mle.tlv.link_sub_tlv == LinkMetricsSubTlvType.FORWARD_PROBING_REGISTRATION) \ + .filter(lambda p: LinkMetricsSubTlvType.FORWARD_PROBING_REGISTRATION in p.mle.tlv.link_sub_tlv) \ .filter(lambda p: 0 in p.mle.tlv.link_forward_series) \ .filter(lambda p: p.mle.tlv.metric_type_id_flags.type is nullField) \ .filter(lambda p: p.mle.tlv.metric_type_id_flags.metric is nullField) \ @@ -247,7 +247,7 @@ def verify(self, pv): pkts.filter_wpan_src64(SSED_1) \ .filter_wpan_dst64(LEADER) \ .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \ - .filter(lambda p: p.mle.tlv.link_sub_tlv == LinkMetricsSubTlvType.FORWARD_PROBING_REGISTRATION) \ + .filter(lambda p: LinkMetricsSubTlvType.FORWARD_PROBING_REGISTRATION in p.mle.tlv.link_sub_tlv) \ .filter(lambda p: 2 in p.mle.tlv.link_forward_series) \ .filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_EXPONENTIAL in p.mle.tlv.metric_type_id_flags.type) \ .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_LINK_MARGIN in p.mle.tlv.metric_type_id_flags.metric) \ @@ -284,7 +284,7 @@ def verify(self, pv): .filter_wpan_dst64(SSED_1) \ .filter_mle_cmd(consts.MLE_DATA_RESPONSE) \ .filter_mle_has_tlv(TlvType.LINK_METRICS_REPORT) \ - .filter(lambda p: p.mle.tlv.link_sub_tlv == LinkMetricsSubTlvType.LINK_METRICS_REPORT) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_REPORT in p.mle.tlv.link_sub_tlv) \ .filter(lambda p: consts.LINK_METRICS_TYPE_AVERAGE_ENUM_EXPONENTIAL in p.mle.tlv.metric_type_id_flags.type) \ .filter(lambda p: consts.LINK_METRICS_METRIC_TYPE_ENUM_LINK_MARGIN in p.mle.tlv.metric_type_id_flags.metric) \ .must_next() @@ -296,7 +296,7 @@ def verify(self, pv): pkts.filter_wpan_src64(SSED_1) \ .filter_wpan_dst64(LEADER) \ .filter_mle_cmd(consts.MLE_LINK_METRICS_MANAGEMENT_REQUEST) \ - .filter(lambda p: p.mle.tlv.link_sub_tlv == LinkMetricsSubTlvType.FORWARD_PROBING_REGISTRATION) \ + .filter(lambda p: LinkMetricsSubTlvType.FORWARD_PROBING_REGISTRATION in p.mle.tlv.link_sub_tlv) \ .filter(lambda p: 0 in p.mle.tlv.link_forward_series) \ .filter(lambda p: p.mle.tlv.metric_type_id_flags.type is nullField) \ .filter(lambda p: p.mle.tlv.metric_type_id_flags.metric is nullField) \ @@ -330,7 +330,7 @@ def verify(self, pv): .filter_wpan_dst64(SSED_1) \ .filter_mle_cmd(consts.MLE_DATA_RESPONSE) \ .filter_mle_has_tlv(TlvType.LINK_METRICS_REPORT) \ - .filter(lambda p: p.mle.tlv.link_sub_tlv == LinkMetricsSubTlvType.LINK_METRICS_STATUS) \ + .filter(lambda p: LinkMetricsSubTlvType.LINK_METRICS_STATUS in p.mle.tlv.link_sub_tlv) \ .filter(lambda p: p.mle.tlv.link_status_sub_tlv == consts.LINK_METRICS_STATUS_SERIES_ID_NOT_RECOGNIZED) \ .must_next() diff --git a/tests/scripts/thread-cert/v1_2_router_5_1_1.py b/tests/scripts/thread-cert/v1_2_router_5_1_1.py index 20173f5ba53..6f8b01c0255 100755 --- a/tests/scripts/thread-cert/v1_2_router_5_1_1.py +++ b/tests/scripts/thread-cert/v1_2_router_5_1_1.py @@ -136,31 +136,7 @@ def test(self): status_tlv = msg.get_coap_message_tlv(network_layer.Status) self.assertEqual(network_layer.StatusValues.SUCCESS, status_tlv.status) - # 8 - Router_1 sends a multicast Link Request Message - msg = router_messages.next_mle_message(mle.CommandType.LINK_REQUEST) - msg.assertMleMessageContainsTlv(mle.SourceAddress) - msg.assertMleMessageContainsTlv(mle.LeaderData) - msg.assertMleMessageContainsTlv(mle.Challenge) - msg.assertMleMessageContainsTlv(mle.Version) - msg.assertMleMessageContainsTlv(mle.TlvRequest) - assert msg.get_mle_message_tlv(mle.Version).version >= 3 - - tlv_request = msg.get_mle_message_tlv(mle.TlvRequest) - self.assertIn(mle.TlvType.LINK_MARGIN, tlv_request.tlvs) - - # 9 - Leader sends a Unicast Link Accept - msg = leader_messages.next_mle_message(mle.CommandType.LINK_ACCEPT_AND_REQUEST) - msg.assertMleMessageContainsTlv(mle.SourceAddress) - msg.assertMleMessageContainsTlv(mle.LeaderData) - msg.assertMleMessageContainsTlv(mle.Response) - msg.assertMleMessageContainsTlv(mle.LinkLayerFrameCounter) - msg.assertMleMessageContainsTlv(mle.Version) - msg.assertMleMessageContainsTlv(mle.LinkMargin) - msg.assertMleMessageContainsOptionalTlv(mle.MleFrameCounter) - msg.assertMleMessageContainsOptionalTlv(mle.Challenge) - assert msg.get_mle_message_tlv(mle.Version).version >= 3 - - # 10 - Router_1 Transmit MLE advertisements + # 8 - Router_1 Transmit MLE advertisements msg = router_messages.next_mle_message(mle.CommandType.ADVERTISEMENT) msg.assertSentWithHopLimit(255) msg.assertSentToDestinationAddress('ff02::1') @@ -168,7 +144,7 @@ def test(self): msg.assertMleMessageContainsTlv(mle.LeaderData) msg.assertMleMessageContainsTlv(mle.Route64) - # 11 - Verify connectivity by sending an ICMPv6 Echo Request to the DUT link local address + # 9 - Verify connectivity by sending an ICMPv6 Echo Request to the DUT link local address self.assertTrue(self.nodes[LEADER].ping(self.nodes[ROUTER_1].get_linklocal())) self.assertTrue(self.nodes[ROUTER_1].ping(self.nodes[LEADER].get_linklocal())) diff --git a/tests/scripts/thread-cert/v1_2_test_backbone_router_service.py b/tests/scripts/thread-cert/v1_2_test_backbone_router_service.py index 1a9d54df825..36d52640ef8 100755 --- a/tests/scripts/thread-cert/v1_2_test_backbone_router_service.py +++ b/tests/scripts/thread-cert/v1_2_test_backbone_router_service.py @@ -144,7 +144,7 @@ def test(self): self.nodes[BBR_1].set_domain_prefix(config.DOMAIN_PREFIX) self.nodes[BBR_1].enable_backbone_router() self.nodes[BBR_1].start() - WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER + WAIT_TIME = config.ROUTER_RESET_DELAY self.simulator.go(WAIT_TIME) self.assertEqual(self.nodes[BBR_1].get_state(), 'router') WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE @@ -229,7 +229,7 @@ def test(self): self.nodes[BBR_2].enable_backbone_router() self.nodes[BBR_2].interface_up() self.nodes[BBR_2].thread_start() - WAIT_TIME = WAIT_ATTACH + ROUTER_SELECTION_JITTER + WAIT_TIME = config.ROUTER_RESET_DELAY self.simulator.go(WAIT_TIME) self.assertEqual(self.nodes[BBR_2].get_state(), 'router') WAIT_TIME = BBR_REGISTRATION_JITTER + WAIT_REDUNDANCE diff --git a/tests/scripts/thread-cert/v1_2_test_csl_transmission.py b/tests/scripts/thread-cert/v1_2_test_csl_transmission.py index fa358f4a9ab..a799922ddc7 100755 --- a/tests/scripts/thread-cert/v1_2_test_csl_transmission.py +++ b/tests/scripts/thread-cert/v1_2_test_csl_transmission.py @@ -72,7 +72,6 @@ def test(self): ssed_messages = self.simulator.get_messages_sent_by(SSED_1) msg = ssed_messages.next_mle_message(mle.CommandType.CHILD_UPDATE_REQUEST) - msg.assertMleMessageDoesNotContainTlv(mle.CslChannel) self.nodes[SSED_1].set_csl_channel(consts.CSL_DEFAULT_CHANNEL) self.simulator.go(1) @@ -89,7 +88,6 @@ def test(self): ssed_messages = self.simulator.get_messages_sent_by(SSED_1) msg = ssed_messages.next_mle_message(mle.CommandType.CHILD_UPDATE_REQUEST) - msg.assertMleMessageDoesNotContainTlv(mle.CslChannel) self.nodes[SSED_1].set_csl_period(0) self.assertFalse(self.nodes[LEADER].ping(self.nodes[SSED_1].get_rloc())) @@ -127,7 +125,7 @@ def test(self): # Check if SSED is able to resynchronize with the parent after it is gone longer than the timeout self.nodes[LEADER].start() - self.simulator.go(config.LEADER_STARTUP_DELAY) + self.simulator.go(config.LEADER_RESET_DELAY) self.nodes[SSED_1].set_csl_timeout(8) self.nodes[SSED_1].set_timeout(10) self.simulator.go(2) @@ -135,7 +133,7 @@ def test(self): self.simulator.go(25) self.flush_all() self.nodes[LEADER].start() - self.simulator.go(config.LEADER_STARTUP_DELAY) + self.simulator.go(config.LEADER_RESET_DELAY) self.assertEqual(self.nodes[LEADER].get_state(), 'leader') self.simulator.go(5) self.assertEqual(self.nodes[SSED_1].get_state(), 'child') diff --git a/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py b/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py index a424e99f4f5..c6d369e45dc 100755 --- a/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py +++ b/tests/scripts/thread-cert/v1_2_test_multicast_listener_registration.py @@ -838,6 +838,7 @@ def __switch_to_1_2_parent(self): # Turn off Router 1.1 and turn on Router 1.2 self.nodes[ROUTER_1_1].stop() self.nodes[ROUTER_1_2].start() + self.simulator.go(config.ROUTER_RESET_DELAY) for id in [FED_1, MED_1, SED_1]: self.simulator.go(config.DEFAULT_CHILD_TIMEOUT + WAIT_REDUNDANCE) diff --git a/tests/scripts/thread-cert/v1_2_test_single_probe.py b/tests/scripts/thread-cert/v1_2_test_single_probe.py index b929479c985..93a8f67fe7d 100755 --- a/tests/scripts/thread-cert/v1_2_test_single_probe.py +++ b/tests/scripts/thread-cert/v1_2_test_single_probe.py @@ -70,40 +70,48 @@ def test(self): leader_messages = self.simulator.get_messages_sent_by(LEADER) # SSED_1 sends a Single Probe Link Metrics for L2 PDU count using MLE Data Request - self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'p') - self.simulator.go(5) + result = self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'p', 'block') + self.assertIn('PDU Counter', result) + self.assertEqual(len(result), 1) leader_messages = self.simulator.get_messages_sent_by(LEADER) msg = leader_messages.next_mle_message(mle.CommandType.DATA_RESPONSE) msg.assertMleMessageContainsTlv(mle.LinkMetricsReport) # SSED_1 sends a Single Probe Link Metrics for L2 LQI using MLE Data Request - self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'q') - self.simulator.go(5) + result = self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'q', 'block') + self.assertIn('LQI', result) + self.assertEqual(len(result), 1) leader_messages = self.simulator.get_messages_sent_by(LEADER) msg = leader_messages.next_mle_message(mle.CommandType.DATA_RESPONSE) msg.assertMleMessageContainsTlv(mle.LinkMetricsReport) # SSED_1 sends a Single Probe Link Metrics for Link Margin using MLE Data Request - self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'm') - self.simulator.go(5) + result = self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'm', 'block') + self.assertIn('Margin', result) + self.assertEqual(len(result), 1) leader_messages = self.simulator.get_messages_sent_by(LEADER) msg = leader_messages.next_mle_message(mle.CommandType.DATA_RESPONSE) msg.assertMleMessageContainsTlv(mle.LinkMetricsReport) # SSED_1 sends a Single Probe Link Metrics for Link Margin using MLE Data Request - self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'r') - self.simulator.go(5) + result = self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'r', 'block') + self.assertIn('RSSI', result) + self.assertEqual(len(result), 1) leader_messages = self.simulator.get_messages_sent_by(LEADER) msg = leader_messages.next_mle_message(mle.CommandType.DATA_RESPONSE) msg.assertMleMessageContainsTlv(mle.LinkMetricsReport) # SSED_1 sends a Single Probe Link Metrics for all metrics using MLE Data Request - self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'pqmr') - self.simulator.go(5) + result = self.nodes[SSED_1].link_metrics_query_single_probe(leader_addr, 'pqmr', 'block') + self.assertIn('PDU Counter', result) + self.assertIn('LQI', result) + self.assertIn('Margin', result) + self.assertIn('RSSI', result) + self.assertEqual(len(result), 4) leader_messages = self.simulator.get_messages_sent_by(LEADER) msg = leader_messages.next_mle_message(mle.CommandType.DATA_RESPONSE) diff --git a/tests/scripts/thread-cert/wpan.py b/tests/scripts/thread-cert/wpan.py old mode 100755 new mode 100644 diff --git a/tests/toranj/build.sh b/tests/toranj/build.sh index 4c25b1a2a70..09428132cfd 100755 --- a/tests/toranj/build.sh +++ b/tests/toranj/build.sh @@ -68,10 +68,13 @@ cd ../.. || die "cd failed" coverage=no tests=no +ot_coverage=OFF + while [ $# -ge 2 ]; do case $1 in -c | --enable-coverage) coverage=yes + ot_coverage=ON shift ;; -t | --enable-tests) @@ -104,14 +107,6 @@ ncp_configure_options=( "--enable-ncp" ) -cli_configure_options=( - "--disable-docs" - "--enable-tests=$tests" - "--enable-coverage=$coverage" - "--enable-ftd" - "--enable-cli" -) - posix_configure_options=( "--disable-docs" "--enable-tests=$tests" @@ -198,30 +193,25 @@ case ${build_config} in echo "===================================================================================================" echo "Building OpenThread CLI FTD mode with simulation platform (radios determined by config)" echo "===================================================================================================" - ./bootstrap || die "bootstrap failed" cd "${top_builddir}" || die "cd failed" - cppflags_config='-DOPENTHREAD_PROJECT_CORE_CONFIG_FILE=\"../tests/toranj/openthread-core-toranj-config-simulation.h\"' - ${top_srcdir}/configure \ - CPPFLAGS="$cppflags_config" \ - --with-examples=simulation \ - "${cli_configure_options[@]}" || die - make -j 8 || die + cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \ + -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \ + -DOT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \ + "${top_srcdir}" || die + ninja || die ;; cli-15.4) echo "===================================================================================================" echo "Building OpenThread CLI FTD mode with simulation platform - 15.4 radio" echo "===================================================================================================" - cppflags_config='-DOPENTHREAD_PROJECT_CORE_CONFIG_FILE=\"../tests/toranj/openthread-core-toranj-config-simulation.h\"' - cppflags_config="${cppflags_config} -DOPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE=1" - cppflags_config="${cppflags_config} -DOPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE=0" - ./bootstrap || die "bootstrap failed" cd "${top_builddir}" || die "cd failed" - ${top_srcdir}/configure \ - CPPFLAGS="$cppflags_config" \ - --with-examples=simulation \ - "${cli_configure_options[@]}" || die - make -j 8 || die + cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \ + -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \ + -DOT_15_4=ON -DOT_TREL=OFF \ + -DOT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \ + "${top_srcdir}" || die + ninja || die cp -p ${top_builddir}/examples/apps/cli/ot-cli-ftd ${top_builddir}/examples/apps/cli/ot-cli-ftd-15.4 ;; @@ -229,16 +219,13 @@ case ${build_config} in echo "===================================================================================================" echo "Building OpenThread CLI FTD mode with simulation platform - TREL radio" echo "===================================================================================================" - cppflags_config='-DOPENTHREAD_PROJECT_CORE_CONFIG_FILE=\"../tests/toranj/openthread-core-toranj-config-simulation.h\"' - cppflags_config="${cppflags_config} -DOPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE=0" - cppflags_config="${cppflags_config} -DOPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE=1" - ./bootstrap || die "bootstrap failed" cd "${top_builddir}" || die "cd failed" - ${top_srcdir}/configure \ - CPPFLAGS="$cppflags_config" \ - --with-examples=simulation \ - "${cli_configure_options[@]}" || die - make -j 8 || die + cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \ + -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \ + -DOT_15_4=OFF -DOT_TREL=ON \ + -DOT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \ + "${top_srcdir}" || die + ninja || die cp -p ${top_builddir}/examples/apps/cli/ot-cli-ftd ${top_builddir}/examples/apps/cli/ot-cli-ftd-trel ;; @@ -246,16 +233,13 @@ case ${build_config} in echo "===================================================================================================" echo "Building OpenThread NCP FTD mode with simulation platform - multi radio (15.4 + TREL)" echo "===================================================================================================" - cppflags_config='-DOPENTHREAD_PROJECT_CORE_CONFIG_FILE=\"../tests/toranj/openthread-core-toranj-config-simulation.h\"' - cppflags_config="${cppflags_config} -DOPENTHREAD_CONFIG_RADIO_LINK_IEEE_802_15_4_ENABLE=1" - cppflags_config="${cppflags_config} -DOPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE=1" - ./bootstrap || die "bootstrap failed" cd "${top_builddir}" || die "cd failed" - ${top_srcdir}/configure \ - CPPFLAGS="$cppflags_config" \ - --with-examples=simulation \ - "${cli_configure_options[@]}" || die - make -j 8 || die + cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \ + -DOT_APP_CLI=ON -DOT_APP_NCP=OFF -DOT_APP_RCP=OFF \ + -DOT_15_4=ON -DOT_TREL=ON \ + -DOT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \ + "${top_srcdir}" || die + ninja || die cp -p ${top_builddir}/examples/apps/cli/ot-cli-ftd ${top_builddir}/examples/apps/cli/ot-cli-ftd-15.4-trel ;; @@ -344,7 +328,8 @@ case ${build_config} in echo "Building OpenThread (NCP/CLI for FTD/MTD/RCP mode) with simulation platform using cmake" echo "====================================================================================================" cd "${top_builddir}" || die "cd failed" - cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=on -DOT_APP_CLI=on \ + cmake -GNinja -DOT_PLATFORM=simulation -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_COVERAGE=${ot_coverage} \ + -DOT_APP_CLI=ON -DOT_APP_NCP=ON -DOT_APP_RCP=ON \ -DOT_CONFIG=../tests/toranj/openthread-core-toranj-config-simulation.h \ "${top_srcdir}" || die ninja || die @@ -355,7 +340,7 @@ case ${build_config} in echo "Building OpenThread POSIX using cmake" echo "====================================================================================================" cd "${top_builddir}" || die "cd failed" - cmake -GNinja -DOT_PLATFORM=posix -DOT_COMPILE_WARNING_AS_ERROR=on -DOT_APP_CLI=off \ + cmake -GNinja -DOT_PLATFORM=posix -DOT_COMPILE_WARNING_AS_ERROR=ON -DOT_APP_CLI=OFF \ -DOT_CONFIG=../tests/toranj/openthread-core-toranj-config-posix.h \ "${top_srcdir}" || die ninja || die diff --git a/tests/toranj/cli/cli.py b/tests/toranj/cli/cli.py index 3bfb7a830cc..fd24205b86b 100644 --- a/tests/toranj/cli/cli.py +++ b/tests/toranj/cli/cli.py @@ -46,6 +46,12 @@ JOIN_TYPE_ROUTER = 'router' JOIN_TYPE_END_DEVICE = 'ed' JOIN_TYPE_SLEEPY_END_DEVICE = 'sed' +JOIN_TYPE_REED = 'reed' + +# for use as `radios` parameter in `Node.__init__()` +RADIO_15_4 = "-15.4" +RADIO_TREL = "-trel" +RADIO_15_4_TREL = "-15.4-trel" # ---------------------------------------------------------------------------------------------------------------------- @@ -106,26 +112,28 @@ class Node(object): _all_nodes = weakref.WeakSet() - def __init__(self, verbose=_VERBOSE): + def __init__(self, radios='', index=None, verbose=_VERBOSE): """Creates a new `Node` instance""" - index = Node._cur_index - Node._cur_index += 1 + if index is None: + index = Node._cur_index + Node._cur_index += 1 self._index = index self._verbose = verbose + cmd = f'{self._OT_CLI_FTD}{radios} --time-speed={self._SPEED_UP_FACTOR} ' + if Node._SAVE_LOGS: - self._log_file = open(self._LOG_FNAME + str(index) + '.log', 'wb') - else: - self._log_file = None + log_file_name = self._LOG_FNAME + str(index) + '.log' + cmd = cmd + f'--log-file={log_file_name} ' - cmd = f'{self._OT_CLI_FTD} --time-speed={self._SPEED_UP_FACTOR} {self._index}' + cmd = cmd + f'{self._index}' if self._verbose: _log(f'$ Node{index}.__init__() cmd: `{cmd}`') - self._cli_process = pexpect.popen_spawn.PopenSpawn(cmd, logfile=self._log_file) + self._cli_process = pexpect.popen_spawn.PopenSpawn(cmd) Node._all_nodes.add(self) def __del__(self): @@ -187,8 +195,8 @@ def _cli_no_output(self, cmd, *args): outputs = self.cli(cmd, *args) verify(len(outputs) == 0) - def _cli_single_output(self, cmd, expected_outputs=None): - outputs = self.cli(cmd) + def _cli_single_output(self, cmd, *args, expected_outputs=None): + outputs = self.cli(cmd, *args) verify(len(outputs) == 1) verify((expected_outputs is None) or (outputs[0] in expected_outputs)) return outputs[0] @@ -204,7 +212,10 @@ def _finalize(self): # cli commands def get_state(self): - return self._cli_single_output('state', ['detached', 'child', 'router', 'leader', 'disabled']) + return self._cli_single_output('state', expected_outputs=['detached', 'child', 'router', 'leader', 'disabled']) + + def get_version(self): + return self._cli_single_output('version') def get_channel(self): return self._cli_single_output('channel') @@ -260,6 +271,18 @@ def get_router_selection_jitter(self): def set_router_selection_jitter(self, jitter): self._cli_no_output('routerselectionjitter', jitter) + def get_router_eligible(self): + return self._cli_single_output('routereligible') + + def set_router_eligible(self, enable): + self._cli_no_output('routereligible', enable) + + def get_context_reuse_delay(self): + return self._cli_single_output('contextreusedelay') + + def set_context_reuse_delay(self, delay): + self._cli_no_output('contextreusedelay', delay) + def interface_up(self): self._cli_no_output('ifconfig up') @@ -275,9 +298,18 @@ def thread_start(self): def thread_stop(self): self._cli_no_output('thread stop') + def get_rloc16(self): + return self._cli_single_output('rloc16') + def get_ip_addrs(self): return self.cli('ipaddr') + def add_ip_addr(self, address): + self._cli_no_output('ipaddr add', address) + + def remove_ip_addr(self, address): + self._cli_no_output('ipaddr del', address) + def get_mleid_ip_addr(self): return self._cli_single_output('ipaddr mleid') @@ -287,6 +319,159 @@ def get_linklocal_ip_addr(self): def get_rloc_ip_addr(self): return self._cli_single_output('ipaddr rloc') + def get_mesh_local_prefix(self): + return self._cli_single_output('prefix meshlocal') + + def get_ip_maddrs(self): + return self.cli('ipmaddr') + + def add_ip_maddr(self, maddr): + return self._cli_no_output('ipmaddr add', maddr) + + def get_pollperiod(self): + return self._cli_single_output('pollperiod') + + def set_pollperiod(self, period): + self._cli_no_output('pollperiod', period) + + def get_partition_id(self): + return self._cli_single_output('partitionid') + + def get_nexthop(self, rloc16): + return self._cli_single_output('nexthop', rloc16) + + def get_parent_info(self): + outputs = self.cli('parent') + result = {} + for line in outputs: + fields = line.split(':') + result[fields[0].strip()] = fields[1].strip() + return result + + def get_child_table(self): + return Node.parse_table(self.cli('child table')) + + def get_neighbor_table(self): + return Node.parse_table(self.cli('neighbor table')) + + def get_router_table(self): + return Node.parse_table(self.cli('router table')) + + def get_eidcache(self): + return self.cli('eidcache') + + def get_vendor_name(self): + return self._cli_single_output('vendor name') + + def set_vendor_name(self, name): + self._cli_no_output('vendor name', name) + + def get_vendor_model(self): + return self._cli_single_output('vendor model') + + def set_vendor_model(self, model): + self._cli_no_output('vendor model', model) + + def get_vendor_sw_version(self): + return self._cli_single_output('vendor swversion') + + def set_vendor_sw_version(self, version): + return self._cli_no_output('vendor swversion', version) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # netdata + + def get_netdata(self): + outputs = self.cli('netdata show') + outputs = [line.strip() for line in outputs] + routes_index = outputs.index('Routes:') + services_index = outputs.index('Services:') + contexts_index = outputs.index('Contexts:') + result = {} + result['prefixes'] = outputs[1:routes_index] + result['routes'] = outputs[routes_index + 1:services_index] + result['services'] = outputs[services_index + 1:contexts_index] + result['contexts'] = outputs[contexts_index + 1:] + + return result + + def get_netdata_prefixes(self): + return self.get_netdata()['prefixes'] + + def get_netdata_routes(self): + return self.get_netdata()['routes'] + + def get_netdata_services(self): + return self.get_netdata()['services'] + + def get_netdata_contexts(self): + return self.get_netdata()['contexts'] + + def get_netdata_versions(self): + leaderdata = Node.parse_list(self.cli('leaderdata')) + return (int(leaderdata['Data Version']), int(leaderdata['Stable Data Version'])) + + def add_prefix(self, prefix, flags=None, prf=None): + return self._cli_no_output('prefix add', prefix, flags, prf) + + def add_route(self, prefix, flags=None, prf=None): + return self._cli_no_output('route add', prefix, flags, prf) + + def remove_prefix(self, prefix): + return self._cli_no_output('prefix remove', prefix) + + def register_netdata(self): + self._cli_no_output('netdata register') + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # ping and counters + + def ping(self, address, size=0, count=1, verify_success=True): + outputs = self.cli('ping', address, size, count) + m = re.match(r'(\d+) packets transmitted, (\d+) packets received.', outputs[-1].strip()) + verify(m is not None) + verify(int(m.group(1)) == count) + if verify_success: + verify(int(m.group(2)) == count) + + def get_mle_counter(self): + return self.cli('counters mle') + + def get_br_counter_unicast_outbound_packets(self): + outputs = self.cli('counters br') + for line in outputs: + m = re.match(r'Outbound Unicast: Packets (\d+) Bytes (\d+)', line.strip()) + if m is not None: + counter = int(m.group(1)) + break + else: + verify(False) + return counter + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # UDP + + def udp_open(self): + self._cli_no_output('udp open') + + def udp_close(self): + self._cli_no_output('udp close') + + def udp_bind(self, address, port): + self._cli_no_output('udp bind', address, port) + + def udp_send(self, address, port, text): + self._cli_no_output('udp send', address, port, '-t', text) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # multiradio + + def multiradio_get_radios(self): + return self._cli_single_output('multiradio') + + def multiradio_get_neighbor_list(self): + return self.cli('multiradio neighbor list') + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # SRP client @@ -297,10 +482,10 @@ def srp_client_stop(self): self._cli_no_output('srp client stop') def srp_client_get_state(self): - return self._cli_single_output('srp client state', ['Enabled', 'Disabled']) + return self._cli_single_output('srp client state', expected_outputs=['Enabled', 'Disabled']) def srp_client_get_auto_start_mode(self): - return self._cli_single_output('srp client autostart', ['Enabled', 'Disabled']) + return self._cli_single_output('srp client autostart', expected_outputs=['Enabled', 'Disabled']) def srp_client_enable_auto_start_mode(self): self._cli_no_output('srp client autostart enable') @@ -338,9 +523,18 @@ def srp_client_set_host_address(self, *addrs): def srp_client_get_host_address(self): return self.cli('srp client host address') - def srp_client_add_service(self, instance_name, service_name, port, priority=0, weight=0, txt_entries=[]): + def srp_client_add_service(self, + instance_name, + service_name, + port, + priority=0, + weight=0, + txt_entries=[], + lease=0, + key_lease=0): txt_record = "".join(self._encode_txt_entry(entry) for entry in txt_entries) - self._cli_no_output('srp client service add', instance_name, service_name, port, priority, weight, txt_record) + self._cli_no_output('srp client service add', instance_name, service_name, port, priority, weight, txt_record, + lease, key_lease) def srp_client_remove_service(self, instance_name, service_name): self._cli_no_output('srp client service remove', instance_name, service_name) @@ -387,10 +581,10 @@ def _parse_srp_client_service(self, line): # SRP server def srp_server_get_state(self): - return self._cli_single_output('srp server state', ['disabled', 'running', 'stopped']) + return self._cli_single_output('srp server state', expected_outputs=['disabled', 'running', 'stopped']) def srp_server_get_addr_mode(self): - return self._cli_single_output('srp server addrmode', ['unicast', 'anycast']) + return self._cli_single_output('srp server addrmode', expected_outputs=['unicast', 'anycast']) def srp_server_set_addr_mode(self, mode): self._cli_no_output('srp server addrmode', mode) @@ -462,6 +656,8 @@ def srp_server_get_services(self): 'priority': '0', 'weight': '0', 'ttl': '7200', + 'lease': '7200', + 'key-lease', '1209600', 'TXT': ['abc=010203'], 'host_fullname': 'my-host.default.service.arpa.', 'host': 'my-host', @@ -482,8 +678,8 @@ def srp_server_get_services(self): if service['deleted'] == 'true': service_list.append(service) continue - # 'subtypes', port', 'priority', 'weight', 'ttl' - for i in range(0, 5): + # 'subtypes', port', 'priority', 'weight', 'ttl', 'lease', 'key-lease' + for i in range(0, 7): key_value = outputs.pop(0).strip().split(':') service[key_value[0].strip()] = key_value[1].strip() txt_entries = outputs.pop(0).strip().split('[')[1].strip(' ]').split(',') @@ -520,6 +716,7 @@ def form(self, network_name=None, network_key=None, channel=None, panid=0x1234, self.set_channel(channel) if xpanid is not None: self.set_ext_panid(xpanid) + self.set_mode('rdn') self.set_panid(panid) self.interface_up() self.thread_start() @@ -534,6 +731,9 @@ def join(self, node, type=JOIN_TYPE_ROUTER): self.set_mode('rn') elif type == JOIN_TYPE_SLEEPY_END_DEVICE: self.set_mode('-') + elif type == JOIN_TYPE_REED: + self.set_mode('rdn') + self.set_router_eligible('disable') else: self.set_mode('rdn') self.set_router_selection_jitter(1) @@ -553,6 +753,64 @@ def un_allowlist_node(self, node): """Removes a given node (of node `Node) from the allowlist""" self._cli_no_output('macfilter addr remove', node.get_ext_addr()) + def set_macfilter_lqi_to_node(self, node, lqi): + self._cli_no_output('macfilter rss add-lqi', node.get_ext_addr(), lqi) + + # ------------------------------------------------------------------------------------------------------------------ + # Radio nodeidfilter + + def nodeidfilter_clear(self, node): + self._cli_no_output('nodeidfilter clear') + + def nodeidfilter_allow(self, node): + self._cli_no_output('nodeidfilter allow', node.index) + + def nodeidfilter_deny(self, node): + self._cli_no_output('nodeidfilter deny', node.index) + + # ------------------------------------------------------------------------------------------------------------------ + # Parsing helpers + + @classmethod + def parse_table(cls, table_lines): + verify(len(table_lines) >= 2) + headers = cls.split_table_row(table_lines[0]) + info = [] + for row in table_lines[2:]: + if row.strip() == '': + continue + fields = cls.split_table_row(row) + verify(len(fields) == len(headers)) + info.append({headers[i]: fields[i] for i in range(len(fields))}) + return info + + @classmethod + def split_table_row(cls, row): + return [field.strip() for field in row.strip().split('|')[1:-1]] + + @classmethod + def parse_list(cls, list_lines): + result = {} + for line in list_lines: + fields = line.split(':', 1) + result[fields[0].strip()] = fields[1].strip() + return result + + @classmethod + def parse_multiradio_neighbor_entry(cls, line): + # Example: "ExtAddr:42aa94ad67229f14, RLOC16:0x9400, Radios:[15.4(245), TREL(255)]" + result = {} + for field in line.split(', ', 2): + key_value = field.split(':') + result[key_value[0]] = key_value[1] + radios = {} + for item in result['Radios'][1:-1].split(','): + name, prf = item.strip().split('(') + verify(prf.endswith(')')) + radios[name] = int(prf[:-1]) + result['Radios'] = radios + return result + # ------------------------------------------------------------------------------------------------------------------ # class methods diff --git a/tests/toranj/cli/test-002-form.py b/tests/toranj/cli/test-002-form.py index eea06d5f057..3bd3c160001 100755 --- a/tests/toranj/cli/test-002-form.py +++ b/tests/toranj/cli/test-002-form.py @@ -40,7 +40,7 @@ # ----------------------------------------------------------------------------------------------------------------------- # Creating `Nodes` instances -speedup = 4 +speedup = 10 cli.Node.set_time_speedup_factor(speedup) node = cli.Node() @@ -48,8 +48,6 @@ # ----------------------------------------------------------------------------------------------------------------------- # Test implementation -WAIT_TIME = 5 - verify(node.get_state() == 'disabled') node.form('test') diff --git a/tests/toranj/cli/test-003-join.py b/tests/toranj/cli/test-003-join.py index f1cc05a4c17..d24602a9409 100755 --- a/tests/toranj/cli/test-003-join.py +++ b/tests/toranj/cli/test-003-join.py @@ -40,14 +40,12 @@ # ----------------------------------------------------------------------------------------------------------------------- # Creating `cli.Nodes` instances -speedup = 4 +speedup = 10 cli.Node.set_time_speedup_factor(speedup) node1 = cli.Node() node2 = cli.Node() -WAIT_TIME = 5 - # ----------------------------------------------------------------------------------------------------------------------- # Test implementation @@ -72,6 +70,20 @@ verify(node2.get_state() == 'child') verify(node2.get_mode() == '-') +node2.interface_down() + +# Create a poor link between child and parent using MAC fixed RSSI +# filter and make sure child can still attach. + +node1.cli('macfilter rss add * -99') +node2.cli('macfilter rss add * -99') + +node2.join(node1, cli.JOIN_TYPE_END_DEVICE) +verify(node2.get_state() == 'child') +verify(node2.get_mode() == 'rn') + +verify(len(node1.get_child_table()) == 1) + # ----------------------------------------------------------------------------------------------------------------------- # Test finished diff --git a/tests/toranj/cli/test-004-scan.py b/tests/toranj/cli/test-004-scan.py new file mode 100755 index 00000000000..35590db7fa4 --- /dev/null +++ b/tests/toranj/cli/test-004-scan.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: MAC scan operation + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + + +# ----------------------------------------------------------------------------------------------------------------------- +def verify_scan_result_conatins_nodes(scan_result, nodes): + table = cli.Node.parse_table(scan_result) + verify(len(table) >= len(nodes)) + for node in nodes: + ext_addr = node.get_ext_addr() + for entry in table: + if entry['MAC Address'] == ext_addr: + verify(int(entry['PAN'], 16) == int(node.get_panid(), 16)) + verify(int(entry['Ch']) == int(node.get_channel())) + break + else: + verify(False) + + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Nodes` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +node1 = cli.Node() +node2 = cli.Node() +node3 = cli.Node() +node4 = cli.Node() +node5 = cli.Node() +scanner = cli.Node() + +nodes = [node1, node2, node3, node4, node5] + +# ----------------------------------------------------------------------------------------------------------------------- +# Test implementation + +node1.form('net1', panid=0x1111, channel=12) +node2.form('net2', panid=0x2222, channel=13) +node3.form('net3', panid=0x3333, channel=14) +node4.form('net4', panid=0x4444, channel=15) +node5.form('net5', panid=0x5555, channel=16) + +# MAC scan + +# Scan on all channels, should see all nodes +verify_scan_result_conatins_nodes(scanner.cli('scan'), nodes) + +# Scan on channel 12 only, should only see node1 +verify_scan_result_conatins_nodes(scanner.cli('scan 12'), [node1]) + +# Scan on channel 20 only, should see no result +verify_scan_result_conatins_nodes(scanner.cli('scan 20'), []) + +# MLE Discover scan + +scanner.interface_up() + +verify_scan_result_conatins_nodes(scanner.cli('discover'), nodes) +verify_scan_result_conatins_nodes(scanner.cli('scan 12'), [node1]) +verify_scan_result_conatins_nodes(scanner.cli('scan 20'), []) + +scanner.form('scanner') +verify_scan_result_conatins_nodes(scanner.cli('discover'), nodes) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-005-traffic-router-to-child.py b/tests/toranj/cli/test-005-traffic-router-to-child.py new file mode 100755 index 00000000000..ac004447960 --- /dev/null +++ b/tests/toranj/cli/test-005-traffic-router-to-child.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: +# +# Traffic between router to end-device (link-local and mesh-local IPv6 addresses). + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Nodes` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +node1 = cli.Node() +node2 = cli.Node() +node3 = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Test implementation + +node1.form('net') +node2.join(node1, cli.JOIN_TYPE_END_DEVICE) +node3.join(node1, cli.JOIN_TYPE_SLEEPY_END_DEVICE) + +verify(node1.get_state() == 'leader') +verify(node2.get_state() == 'child') +verify(node3.get_state() == 'child') + +ll1 = node1.get_linklocal_ip_addr() +ll2 = node2.get_linklocal_ip_addr() +ll3 = node2.get_linklocal_ip_addr() + +ml1 = node1.get_mleid_ip_addr() +ml2 = node2.get_mleid_ip_addr() +ml3 = node2.get_mleid_ip_addr() + +sizes = [0, 80, 500, 1000] +count = 2 + +for size in sizes: + node1.ping(ll2, size=size, count=count) + node2.ping(ll1, size=size, count=count) + node1.ping(ml2, size=size, count=count) + node2.ping(ml1, size=size, count=count) + +poll_periods = [10, 100, 300] + +for period in poll_periods: + node3.set_pollperiod(period) + verify(int(node3.get_pollperiod()) == period) + + for size in sizes: + node1.ping(ll3, size=size, count=count) + node3.ping(ll1, size=size, count=count) + node1.ping(ml3, size=size, count=count) + node3.ping(ml1, size=size, count=count) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-006-traffic-multi-hop.py b/tests/toranj/cli/test-006-traffic-multi-hop.py new file mode 100755 index 00000000000..0f6f48c71ab --- /dev/null +++ b/tests/toranj/cli/test-006-traffic-multi-hop.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: +# +# Traffic over multi-hop in a network with chain topology +# +# r1 ----- r2 ---- r3 ----- r4 +# /\ /\ +# / \ / \ +# fed1 sed1 sed4 fed4 +# +# +# Traffic flow: +# - From first router to last router +# - From SED child of last router to SED child of first router +# - From FED child of first router to FED child of last router +# +# The test verifies the following: +# - Verifies Address Query process to routers and FEDs. +# - Verifies Mesh Header frame forwarding over multiple routers. +# - Verifies forwarding of large IPv6 messages (1000 bytes) requiring lowpan fragmentation. + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Nodes` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +r3 = cli.Node() +r4 = cli.Node() +fed1 = cli.Node() +sed1 = cli.Node() +fed4 = cli.Node() +sed4 = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) +r1.allowlist_node(fed1) +r1.allowlist_node(sed1) + +r2.allowlist_node(r1) +r2.allowlist_node(r3) + +r3.allowlist_node(r2) +r3.allowlist_node(r4) + +r4.allowlist_node(r3) +r4.allowlist_node(sed4) +r4.allowlist_node(fed4) + +fed1.allowlist_node(r1) +sed1.allowlist_node(r1) + +fed4.allowlist_node(r4) +sed4.allowlist_node(r4) + +r1.form("pan") +r2.join(r1) +r3.join(r2) +r4.join(r3) +fed1.join(r1, cli.JOIN_TYPE_REED) +sed1.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE) +fed4.join(r4, cli.JOIN_TYPE_REED) +sed4.join(r4, cli.JOIN_TYPE_SLEEPY_END_DEVICE) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') +verify(r3.get_state() == 'router') +verify(r4.get_state() == 'router') +verify(fed1.get_state() == 'child') +verify(fed4.get_state() == 'child') +verify(sed1.get_state() == 'child') +verify(sed4.get_state() == 'child') + +sed1.set_pollperiod(200) +sed4.set_pollperiod(200) +verify(int(sed1.get_pollperiod()) == 200) +verify(int(sed4.get_pollperiod()) == 200) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +# Wait till first router has either established a link or +# has a valid "next hop" towards all other routers. + +r1_rloc16 = int(r1.get_rloc16(), 16) + + +def check_r1_router_table(): + table = r1.get_router_table() + verify(len(table) == 4) + for entry in table: + verify(int(entry['RLOC16'], 0) == r1_rloc16 or int(entry['Link']) == 1 or int(entry['Next Hop']) != 63) + + +verify_within(check_r1_router_table, 160) + +sizes = [0, 80, 500, 1000] + +# Traffic from the first router in the chain to the last one. + +r4_mleid = r4.get_mleid_ip_addr() + +for size in sizes: + r1.ping(r4_mleid, size=size) + +# Send from the SED child of the last router to the SED child of the first +# router. + +sed1_mleid = sed1.get_mleid_ip_addr() + +for size in sizes: + sed4.ping(sed1_mleid, size=size) + +# Send from the FED child of the first router to the FED child of the last +# router. + +fed4_mleid = fed4.get_mleid_ip_addr() + +for size in sizes: + fed1.ping(fed4_mleid, size=size) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-007-off-mesh-route-traffic.py b/tests/toranj/cli/test-007-off-mesh-route-traffic.py new file mode 100755 index 00000000000..bd7238785a0 --- /dev/null +++ b/tests/toranj/cli/test-007-off-mesh-route-traffic.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: Adding off-mesh routes (on routers and FEDs) and traffic flow to off-mesh addresses. +# +# Test topology: +# +# r1---- r2 ---- r3 ---- r4 +# | | +# | | +# fed1 med3 +# +# The off-mesh-routes are added as follows: +# - `r1` adds fd00:1:1::/48 high +# - `r2` adds fd00:1:1:2::64 med +# - `r3` adds fd00:3::/64 med & fed00:4::/32 med +# - 'r4' adds fd00:1:1::/48 low +# - `fed1` adds fd00:4::/32 med +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Nodes` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +r3 = cli.Node() +r4 = cli.Node() +fed1 = cli.Node() +med3 = cli.Node() + +nodes = [r1, r2, r3, r4, fed1, med3] + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) +r1.allowlist_node(fed1) + +r2.allowlist_node(r1) +r2.allowlist_node(r3) + +r3.allowlist_node(r2) +r3.allowlist_node(r4) +r3.allowlist_node(med3) + +r4.allowlist_node(r3) + +fed1.allowlist_node(r1) +med3.allowlist_node(r3) + +r1.form("off-mesh") +r2.join(r1) +r3.join(r2) +r4.join(r3) +fed1.join(r1, cli.JOIN_TYPE_REED) +med3.join(r3, cli.JOIN_TYPE_END_DEVICE) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') +verify(r3.get_state() == 'router') +verify(r4.get_state() == 'router') +verify(fed1.get_state() == 'child') +verify(med3.get_state() == 'child') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +# Wait till first router has either established a link or +# has a valid "next hop" towards all other routers. + +r1_rloc16 = int(r1.get_rloc16(), 16) + + +def check_r1_router_table(): + table = r1.get_router_table() + verify(len(table) == 4) + for entry in table: + verify(int(entry['RLOC16'], 0) == r1_rloc16 or int(entry['Link']) == 1 or int(entry['Next Hop']) != 63) + + +verify_within(check_r1_router_table, 120) + +# Add an on-mesh prefix on r1 and different off-mesh routes +# on different nodes and make sure they are seen on all nodes + +r1.add_prefix('fd00:abba::/64', 'paros', 'med') +r1.add_route('fd00:1:1::/48', 's', 'high') +r1.register_netdata() + +r2.add_route('fd00:1:1:2::/64', 's', 'med') +r2.register_netdata() + +r3.add_route('fd00:3::/64', 's', 'med') +r3.add_route('fd00:4::/32', 'med') +r3.register_netdata() + +r4.add_route('fd00:1:1::/48', 's', 'med') + +fed1.add_route('fd00:4::/32', 'med') +fed1.register_netdata() + + +def check_netdata_on_all_nodes(): + for node in nodes: + netdata = node.get_netdata() + verify(len(netdata['prefixes']) == 1) + verify(len(netdata['routes']) == 6) + + +verify_within(check_netdata_on_all_nodes, 5) + +# Send from `med3` to an address matching `fd00:1:1:2::/64` added +# by`r2` and verify that it is received on `r2` and not `r1`, since +# it is better prefix match with `fd00:1:1:2/64` on r2. + +r1_counter = r1.get_br_counter_unicast_outbound_packets() +r2_counter = r2.get_br_counter_unicast_outbound_packets() + +med3.ping('fd00:1:1:2::1', verify_success=False) + +verify(r1_counter == r1.get_br_counter_unicast_outbound_packets()) +verify(r2_counter + 1 == r2.get_br_counter_unicast_outbound_packets()) + +# Send from`r3` to an address matching `fd00:1:1::/48` which is +# added by both `r1` and `r4` and verify that it is received on +# `r1` since it adds it with higher preference. + +r1_counter = r1.get_br_counter_unicast_outbound_packets() +r4_counter = r4.get_br_counter_unicast_outbound_packets() + +r3.ping('fd00:1:1::2', count=3, verify_success=False) + +verify(r1_counter + 3 == r1.get_br_counter_unicast_outbound_packets()) +verify(r4_counter == r4.get_br_counter_unicast_outbound_packets()) + +# TRy the same address from `r4` itself, it should again end up +# going to `r1` due to it adding it at high preference. + +r1_counter = r1.get_br_counter_unicast_outbound_packets() +r4_counter = r4.get_br_counter_unicast_outbound_packets() + +r4.ping('fd00:1:1::2', count=2, verify_success=False) + +verify(r1_counter + 2 == r1.get_br_counter_unicast_outbound_packets()) +verify(r4_counter == r4.get_br_counter_unicast_outbound_packets()) + +# Send from `fed1` to an address matching `fd00::3::/64` (from `r3`) +# and verify that it is received on `r3`. + +r3_counter = r3.get_br_counter_unicast_outbound_packets() + +fed1.ping('fd00:3::3', count=2, verify_success=False) + +verify(r3_counter + 2 == r3.get_br_counter_unicast_outbound_packets()) + +# Send from `r1` to an address matching `fd00::4::/32` which is added +# by both `fed1` and `r3` with same preference. Verify that it is +# received on `fed1` since it is closer to `r1` and we would have a +# smaller path cost from `r1` to `fed1`. + +fed1_counter = fed1.get_br_counter_unicast_outbound_packets() +r3_counter = r3.get_br_counter_unicast_outbound_packets() + +r1.ping('fd00:4::4', count=1, verify_success=False) + +verify(fed1_counter + 1 == fed1.get_br_counter_unicast_outbound_packets()) +verify(r3_counter == r3.get_br_counter_unicast_outbound_packets()) + +# Try the same, but now send from `fed1` and make sure it selects +# itself as destination. + +fed1_counter = fed1.get_br_counter_unicast_outbound_packets() +r3_counter = r3.get_br_counter_unicast_outbound_packets() + +fed1.ping('fd00:4::5', count=2, verify_success=False) + +verify(fed1_counter + 2 == fed1.get_br_counter_unicast_outbound_packets()) +verify(r3_counter == r3.get_br_counter_unicast_outbound_packets()) + +# Try the same but now send from `r2`. Now the `r3` would be closer +# and should be selected as destination. + +fed1_counter = fed1.get_br_counter_unicast_outbound_packets() +r3_counter = r3.get_br_counter_unicast_outbound_packets() + +r2.ping('fd00:4::6', count=3, verify_success=False) + +verify(fed1_counter == fed1.get_br_counter_unicast_outbound_packets()) +verify(r3_counter + 3 == r3.get_br_counter_unicast_outbound_packets()) + +# Again try same but send from `med1` and make sure its parent +# `r3` receives. + +fed1_counter = fed1.get_br_counter_unicast_outbound_packets() +r3_counter = r3.get_br_counter_unicast_outbound_packets() + +med3.ping('fd00:4::7', count=1, verify_success=False) + +verify(fed1_counter == fed1.get_br_counter_unicast_outbound_packets()) +verify(r3_counter + 1 == r3.get_br_counter_unicast_outbound_packets()) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-008-multicast-traffic.py b/tests/toranj/cli/test-008-multicast-traffic.py new file mode 100755 index 00000000000..7623a9c5a75 --- /dev/null +++ b/tests/toranj/cli/test-008-multicast-traffic.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: Multicast traffic +# +# Network topology +# +# r1 ---- r2 ---- r3 ---- r4 +# | | +# | | +# fed sed +# +# Test covers the following multicast traffic: +# +# - r2 =>> link-local all-nodes. Expected response from [r1, r3, fed]. +# - r3 =>> mesh-local all-nodes. Expected response from [r1, r2, r4, fed]. +# - r3 =>> link-local all-routers. Expected response from [r2, r4]. +# - r3 =>> mesh-local all-routers. Expected response from all routers. +# - r1 =>> link-local all-thread. Expected response from [r1, r2]. +# - fed =>> mesh-local all-thread. Expected response from all nodes. +# - r1 =>> mesh-local all-thread (one hop). Expected response from [r2]. +# - r1 =>> mesh-local all-thread (two hops). Expected response from [r2, r3, fed]. +# - r1 =>> mesh-local all-thread (three hops). Expected response from [r2, r3, r4, fed]. +# - r1 =>> mesh-local all-thread (four hops). Expected response from [r2, r3, r4, fed, sed]. +# - r1 =>> specific address (on r2 and sed). Expected to receive on [r2, sed]. +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Nodes` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +r3 = cli.Node() +r4 = cli.Node() +fed = cli.Node() +sed = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) + +r2.allowlist_node(r1) +r2.allowlist_node(fed) +r2.allowlist_node(r3) + +fed.allowlist_node(r2) + +r3.allowlist_node(r2) +r3.allowlist_node(r4) + +r4.allowlist_node(r3) +r4.allowlist_node(sed) + +sed.allowlist_node(r4) + +r1.form("multicast") +r2.join(r1) +r3.join(r2) +r4.join(r3) +fed.join(r1, cli.JOIN_TYPE_REED) + +sed.join(r3, cli.JOIN_TYPE_SLEEPY_END_DEVICE) + +sed.set_pollperiod(600) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') +verify(r3.get_state() == 'router') +verify(r4.get_state() == 'router') +verify(fed.get_state() == 'child') +verify(sed.get_state() == 'child') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +# Wait till first router has either established a link or +# has a valid "next hop" towards all other routers. + +r1_rloc16 = int(r1.get_rloc16(), 16) + + +def check_r1_router_table(): + table = r1.get_router_table() + verify(len(table) == 4) + for entry in table: + verify(int(entry['RLOC16'], 0) == r1_rloc16 or int(entry['Link']) == 1 or int(entry['Next Hop']) != 63) + + +verify_within(check_r1_router_table, 160) + +# r2 =>> link-local all-nodes. Expected response from [r1, r3, fed]. + +outputs = r2.cli('ping ff02::1') +verify(len(outputs) == 4) +for node in [r1, r3, fed]: + ll_addr = node.get_linklocal_ip_addr() + verify(any(ll_addr in line for line in outputs)) + +# r3 =>> mesh-local all-nodes. Expected response from [r1, r2, r4, fed]. + +outputs = r3.cli('ping ff03::1') +verify(len(outputs) == 5) +for node in [r1, r2, r4, fed]: + ml_addr = node.get_mleid_ip_addr() + verify(any(ml_addr in line for line in outputs)) + +# r3 =>> link-local all-routers. Expected response from [r2, r4]. + +outputs = r3.cli('ping ff02::2') +verify(len(outputs) == 3) +for node in [r2, r4]: + ll_addr = node.get_linklocal_ip_addr() + verify(any(ll_addr in line for line in outputs)) + +# r3 =>> mesh-local all-routers. Expected response from all routers. + +outputs = r3.cli('ping ff03::2') +verify(len(outputs) == 5) +for node in [r1, r2, r4, fed]: + ml_addr = node.get_mleid_ip_addr() + verify(any(ml_addr in line for line in outputs)) + +# r1 =>> link-local all-thread. Expected response from [r2]. + +ml_prefix = r1.get_mesh_local_prefix().strip().split('/')[0] +ll_all_thread_nodes_addr = 'ff32:40:' + ml_prefix + '1' +outputs = r1.cli('ping', ll_all_thread_nodes_addr) +verify(len(outputs) == 2) +for node in [r2]: + ll_addr = node.get_linklocal_ip_addr() + verify(any(ll_addr in line for line in outputs)) + +# fed =>> mesh-local all-thread. Expected response from all nodes. + +ml_all_thread_nodes_addr = 'ff33:40:' + ml_prefix + '1' +outputs = fed.cli('ping', ml_all_thread_nodes_addr) +verify(len(outputs) == 6) +for node in [r1, r2, r3, r4, sed]: + ml_addr = node.get_mleid_ip_addr() + verify(any(ml_addr in line for line in outputs)) + +# Repeat the same with larger ping msg requiring fragmentation + +outputs = fed.cli('ping', ml_all_thread_nodes_addr, 200) +verify(len(outputs) == 6) +for node in [r1, r2, r3, r4, sed]: + ml_addr = node.get_mleid_ip_addr() + verify(any(ml_addr in line for line in outputs)) + +# r1 =>> mesh-local all-thread (one hop). Expected response from [r2]. + +hop_limit = 1 +outputs = r1.cli('ping', ml_all_thread_nodes_addr, 10, 1, 0, hop_limit) +verify(len(outputs) == 2) +for node in [r2]: + ml_addr = node.get_mleid_ip_addr() + verify(any(ml_addr in line for line in outputs)) + +# r1 =>> mesh-local all-thread (two hops). Expected response from [r2, r3, fed]. + +hop_limit = 2 +outputs = r1.cli('ping', ml_all_thread_nodes_addr, 10, 1, 0, hop_limit) +verify(len(outputs) == 4) +for node in [r2, r3, fed]: + ml_addr = node.get_mleid_ip_addr() + verify(any(ml_addr in line for line in outputs)) + +# r1 =>> mesh-local all-thread (three hops). Expected response from [r2, r3, r4, fed]. + +hop_limit = 3 +outputs = r1.cli('ping', ml_all_thread_nodes_addr, 10, 1, 0, hop_limit) +verify(len(outputs) == 5) +for node in [r2, r3, r4, fed]: + ml_addr = node.get_mleid_ip_addr() + verify(any(ml_addr in line for line in outputs)) + +# r1 =>> mesh-local all-thread (four hops). Expected response from [r2, r3, r4, fed, sed]. + +hop_limit = 4 +outputs = r1.cli('ping', ml_all_thread_nodes_addr, 10, 1, 0, hop_limit) +verify(len(outputs) == 6) +for node in [r2, r3, r4, fed, sed]: + ml_addr = node.get_mleid_ip_addr() + verify(any(ml_addr in line for line in outputs)) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Subscribe to a specific multicast address on r2 and sed + +mcast_addr = 'ff03:0:0:0:0:0:0:114' +r2.add_ip_maddr(mcast_addr) +sed.add_ip_maddr(mcast_addr) +time.sleep(1) +maddrs = r2.get_ip_maddrs() +verify(any(mcast_addr in maddr for maddr in maddrs)) +maddrs = sed.get_ip_maddrs() +verify(any(mcast_addr in maddr for maddr in maddrs)) + +outputs = r1.cli('ping', mcast_addr) +verify(len(outputs) == 3) +for node in [r2, sed]: + ml_addr = node.get_mleid_ip_addr() + verify(any(ml_addr in line for line in outputs)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-009-router-table.py b/tests/toranj/cli/test-009-router-table.py new file mode 100755 index 00000000000..67159f87ee3 --- /dev/null +++ b/tests/toranj/cli/test-009-router-table.py @@ -0,0 +1,251 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: +# +# Verify router table entries. +# +# r1 ------ r2 ---- r6 +# \ / +# \ / +# \ / +# r3 ------ r4 ----- r5 +# +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Nodes` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +r3 = cli.Node() +r4 = cli.Node() +r5 = cli.Node() +r6 = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) +r1.allowlist_node(r3) + +r2.allowlist_node(r1) +r2.allowlist_node(r3) +r2.allowlist_node(r6) + +r3.allowlist_node(r1) +r3.allowlist_node(r2) +r3.allowlist_node(r4) + +r4.allowlist_node(r3) +r4.allowlist_node(r5) + +r5.allowlist_node(r4) + +r6.allowlist_node(r2) + +r1.form("topo") +for node in [r2, r3, r4, r5, r6]: + node.join(r1) + +verify(r1.get_state() == 'leader') +for node in [r2, r3, r4, r5, r6]: + verify(node.get_state() == 'router') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +r1_rloc16 = int(r1.get_rloc16(), 16) +r2_rloc16 = int(r2.get_rloc16(), 16) +r3_rloc16 = int(r3.get_rloc16(), 16) +r4_rloc16 = int(r4.get_rloc16(), 16) +r5_rloc16 = int(r5.get_rloc16(), 16) +r6_rloc16 = int(r6.get_rloc16(), 16) + +r1_rid = r1_rloc16 / 1024 +r2_rid = r2_rloc16 / 1024 +r3_rid = r3_rloc16 / 1024 +r4_rid = r4_rloc16 / 1024 +r5_rid = r5_rloc16 / 1024 +r6_rid = r6_rloc16 / 1024 + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +def check_r1_router_table(): + table = r1.get_router_table() + verify(len(table) == 6) + for entry in table: + rloc16 = int(entry['RLOC16'], 0) + link = int(entry['Link']) + nexthop = int(entry['Next Hop']) + cost = int(entry['Path Cost']) + if rloc16 == r1_rloc16: + verify(link == 0) + elif rloc16 == r2_rloc16: + verify(link == 1) + verify(nexthop == r3_rid) + elif rloc16 == r3_rloc16: + verify(link == 1) + verify(nexthop == r2_rid) + elif rloc16 == r4_rloc16: + verify(link == 0) + verify(nexthop == r3_rid) + verify(cost == 1) + elif rloc16 == r5_rloc16: + verify(link == 0) + verify(nexthop == r3_rid) + verify(cost == 2) + elif rloc16 == r6_rloc16: + verify(link == 0) + verify(nexthop == r2_rid) + verify(cost == 1) + + +verify_within(check_r1_router_table, 160) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +def check_r2_router_table(): + table = r2.get_router_table() + verify(len(table) == 6) + for entry in table: + rloc16 = int(entry['RLOC16'], 0) + link = int(entry['Link']) + nexthop = int(entry['Next Hop']) + cost = int(entry['Path Cost']) + if rloc16 == r1_rloc16: + verify(link == 1) + verify(nexthop == r3_rid) + elif rloc16 == r2_rloc16: + verify(link == 0) + elif rloc16 == r3_rloc16: + verify(link == 1) + verify(nexthop == r1_rid) + elif rloc16 == r4_rloc16: + verify(link == 0) + verify(nexthop == r3_rid) + verify(cost == 1) + elif rloc16 == r5_rloc16: + verify(link == 0) + verify(nexthop == r3_rid) + verify(cost == 2) + elif rloc16 == r6_rloc16: + verify(link == 1) + + +verify_within(check_r2_router_table, 160) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +def check_r4_router_table(): + table = r4.get_router_table() + verify(len(table) == 6) + for entry in table: + rloc16 = int(entry['RLOC16'], 0) + link = int(entry['Link']) + nexthop = int(entry['Next Hop']) + cost = int(entry['Path Cost']) + if rloc16 == r1_rloc16: + verify(link == 0) + verify(nexthop == r3_rid) + verify(cost == 1) + elif rloc16 == r2_rloc16: + verify(link == 0) + verify(nexthop == r3_rid) + verify(cost == 1) + elif rloc16 == r3_rloc16: + verify(link == 1) + elif rloc16 == r4_rloc16: + verify(link == 0) + elif rloc16 == r5_rloc16: + verify(link == 1) + elif rloc16 == r6_rloc16: + verify(link == 0) + verify(nexthop == r3_rid) + verify(cost == 2) + + +verify_within(check_r4_router_table, 160) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +def check_r5_router_table(): + table = r5.get_router_table() + verify(len(table) == 6) + for entry in table: + rloc16 = int(entry['RLOC16'], 0) + link = int(entry['Link']) + nexthop = int(entry['Next Hop']) + cost = int(entry['Path Cost']) + if rloc16 == r1_rloc16: + verify(link == 0) + verify(nexthop == r4_rid) + verify(cost == 2) + elif rloc16 == r2_rloc16: + verify(link == 0) + verify(nexthop == r4_rid) + verify(cost == 2) + elif rloc16 == r3_rloc16: + verify(link == 0) + verify(nexthop == r4_rid) + verify(cost == 1) + elif rloc16 == r4_rloc16: + verify(link == 1) + elif rloc16 == r5_rloc16: + verify(link == 0) + elif rloc16 == r6_rloc16: + verify(link == 0) + verify(nexthop == r4_rid) + verify(cost == 3) + + +verify_within(check_r5_router_table, 160) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-010-partition-merge.py b/tests/toranj/cli/test-010-partition-merge.py new file mode 100755 index 00000000000..41537370174 --- /dev/null +++ b/tests/toranj/cli/test-010-partition-merge.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: Partition formation and merge +# +# Network Topology: +# +# r1 ---- / ---- r2 +# | \ | +# | / | +# c1 \ c2 +# +# +# Test covers the following situations: +# +# - r2 forming its own partition when it can no longer hear r1 +# - Partitions merging into one once r1 and r2 can talk again +# - Adding on-mesh prefixes on each partition and ensuring after +# merge the info in combined. +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Nodes` instances + +speedup = 25 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +c1 = cli.Node() +c2 = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) +r1.allowlist_node(c1) + +r2.allowlist_node(r1) +r2.allowlist_node(c2) + +r1.form("partmrge") +r2.join(r1) +c1.join(r1, cli.JOIN_TYPE_END_DEVICE) +c2.join(r2, cli.JOIN_TYPE_END_DEVICE) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') +verify(c1.get_state() == 'child') +verify(c2.get_state() == 'child') + +nodes = [r1, r2, c1, c2] + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +# Force the two routers to form their own partition +# by removing them from each other's allowlist table + +r1.un_allowlist_node(r2) +r2.un_allowlist_node(r1) + +# Add a prefix before r2 realizes it can no longer talk +# to leader (r1). + +r2.add_prefix('fd00:abba::/64', 'paros', 'med') +r2.register_netdata() + +# Check that r2 forms its own partition + + +def check_r2_become_leader(): + verify(r2.get_state() == 'leader') + + +verify_within(check_r2_become_leader, 20) + +# While we have two partition, add a prefix on r1 +r1.add_prefix('fd00:1234::/64', 'paros', 'med') +r1.register_netdata() + +# Update allowlist and wait for partitions to merge. +r1.allowlist_node(r2) +r2.allowlist_node(r1) + + +def check_partition_id_match(): + verify(r1.get_partition_id() == r2.get_partition_id()) + + +verify_within(check_partition_id_match, 20) + +# Check that partitions merged successfully + + +def check_r1_r2_roles(): + verify(r1.get_state() in ['leader', 'router']) + verify(r2.get_state() in ['leader', 'router']) + + +verify_within(check_r1_r2_roles, 10) + +# Verify all nodes see both prefixes + + +def check_netdata_on_all_nodes(): + for node in nodes: + netdata = node.get_netdata() + verify(len(netdata['prefixes']) == 2) + + +verify_within(check_netdata_on_all_nodes, 10) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-011-network-data-timeout.py b/tests/toranj/cli/test-011-network-data-timeout.py new file mode 100755 index 00000000000..a3a4086a771 --- /dev/null +++ b/tests/toranj/cli/test-011-network-data-timeout.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: Network Data (on-mesh prefix) timeout and entry removal +# +# Network topology +# +# r1 ----- r2 +# | +# | +# c2 (sleepy) +# +# +# Test covers the following steps: +# +# - Every node adds a unique on-mesh prefix. +# - Every node also adds a common on-mesh prefix (with different flags). +# - Verify that all the unique and common prefixes are present on all nodes are associated with correct RLOC16. +# - Remove `r2` from network (which removes `c2` as well) from Thread partition created by `r1`. +# - Verify that all on-mesh prefixes added by `r2` or `c2` (unique and common) are removed on `r1`. +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Nodes` instances + +speedup = 25 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +c2 = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) + +r2.allowlist_node(r1) +r2.allowlist_node(c2) + +c2.allowlist_node(r2) + +r1.form("netdatatmout") +r2.join(r1) +c2.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE) +c2.set_pollperiod(500) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') +verify(c2.get_state() == 'child') + +nodes = [r1, r2, c2] + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +common_prefix = 'fd00:cafe::' +prefix1 = 'fd00:1::' +prefix2 = 'fd00:2::' +prefix3 = 'fd00:3::' + +# Each node adds its own prefix. +r1.add_prefix('fd00:1::/64', 'paros', 'med') +r2.add_prefix('fd00:2::/64', 'paros', 'med') +c2.add_prefix('fd00:3::/64', 'paros', 'med') + +# a common prefix is added by all three nodes (with different preference) +r1.add_prefix('fd00:abba::/64', 'paros', 'high') +r2.add_prefix('fd00:abba::/64', 'paros', 'med') +c2.add_prefix('fd00:abba::/64', 'paros', 'low') + +r1.register_netdata() +r2.register_netdata() +c2.register_netdata() + + +def check_netdata_on_all_nodes(): + for node in nodes: + netdata = node.get_netdata() + prefixes = netdata['prefixes'] + verify(len(prefixes) == 6) + + +verify_within(check_netdata_on_all_nodes, 10) + +# Remove `r2`. This should trigger all the prefixes added by it or its +# child to timeout and be removed. + +r2.thread_stop() +r2.interface_down() + + +def check_netdata_on_r1(): + netdata = r1.get_netdata() + prefixes = netdata['prefixes'] + verify(len(prefixes) == 2) + + +verify_within(check_netdata_on_r1, 120) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-012-reset-recovery.py b/tests/toranj/cli/test-012-reset-recovery.py new file mode 100755 index 00000000000..ea92a434c01 --- /dev/null +++ b/tests/toranj/cli/test-012-reset-recovery.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: +# This test covers reset of parent, router, leader and restoring children after reset +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Nodes` instances + +speedup = 25 +cli.Node.set_time_speedup_factor(speedup) + +leader = cli.Node() +router = cli.Node() +child1 = cli.Node() +child2 = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +leader.form('reset') +child1.join(leader, cli.JOIN_TYPE_REED) +child2.join(leader, cli.JOIN_TYPE_END_DEVICE) + +verify(leader.get_state() == 'leader') +verify(child1.get_state() == 'child') +verify(child2.get_state() == 'child') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Reset the parent and verify that both children are restored on +# parent through "Child Update" exchange process and none of them got +# detached and needed to attach back. + +del leader +leader = cli.Node(index=1) +leader.interface_up() +leader.thread_start() + + +def check_leader_state(): + verify(leader.get_state() == 'leader') + + +verify_within(check_leader_state, 10) + +# Check that `child1` and `child2` did not detach + +verify(child1.get_state() == 'child') +verify(child2.get_state() == 'child') + +verify(int(cli.Node.parse_list(child1.get_mle_counter())['Role Detached']) == 1) +verify(int(cli.Node.parse_list(child2.get_mle_counter())['Role Detached']) == 1) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Reset `router` and make sure it recovers as router with same router ID. + +router.join(leader) + +verify(router.get_state() == 'router') +router_rloc16 = int(router.get_rloc16(), 16) + +time.sleep(0.75) + +del router +router = cli.Node(index=2) +router.interface_up() +router.thread_start() + + +def check_router_state(): + verify(router.get_state() == 'router') + + +verify_within(check_router_state, 10) +verify(router_rloc16 == int(router.get_rloc16(), 16)) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Reset `leader` and make sure `router` is its neighbor again + +del leader +leader = cli.Node(index=1) +leader.interface_up() +leader.thread_start() + + +def check_leader_state(): + verify(leader.get_state() == 'leader') + + +verify_within(check_leader_state, 10) + + +def check_leader_neighbor_table(): + verify(len(leader.get_neighbor_table()) == 3) + + +verify_within(check_leader_neighbor_table, 10) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Reset `child` and make sure it re-attaches successfully. + +del child1 +child1 = cli.Node(index=3) +child1.set_router_eligible('disable') +child1.interface_up() +child1.thread_start() + + +def check_child1_state(): + verify(child1.get_state() == 'child') + table = child1.get_router_table() + verify(len(table) == 2) + + +verify_within(check_child1_state, 10) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-013-address-cache-parent-switch.py b/tests/toranj/cli/test-013-address-cache-parent-switch.py new file mode 100755 index 00000000000..eee1befd9d9 --- /dev/null +++ b/tests/toranj/cli/test-013-address-cache-parent-switch.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: Address Cache Table +# +# This test verifies the behavior of `AddressResolver` module and entries in +# address cache table. It also tests the behavior of nodes when there are +# topology changes in the network (e.g., a child switches parent). In +# particular, the test covers the address cache update through snooping, i.e., +# the logic which inspects forwarded frames to update address cache table if +# source RLOC16 on a received frame differs from an existing entry in the +# address cache table. +# +# Network topology: +# +# r1 ---- r2 ---- r3 +# | | | +# | | | +# c1 c2(s) c3 +# +# c1 and c3 are FED children, c2 is an SED which is first attached to r2 and +# then forced to switch to r3. + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Nodes` instances + +speedup = 25 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +r3 = cli.Node() +c1 = cli.Node() +c2 = cli.Node() +c3 = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) +r1.allowlist_node(c1) + +r2.allowlist_node(r1) +r2.allowlist_node(r3) +r2.allowlist_node(c2) + +r3.allowlist_node(r2) +r3.allowlist_node(c3) + +c1.allowlist_node(r1) +c2.allowlist_node(r2) +c3.allowlist_node(r3) + +r1.form('addrrslvr') +r2.join(r1) +r3.join(r1) +c1.join(r1, cli.JOIN_TYPE_REED) +c2.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE) +c3.join(r1, cli.JOIN_TYPE_REED) +c2.set_pollperiod(400) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') +verify(r3.get_state() == 'router') +verify(c1.get_state() == 'child') +verify(c2.get_state() == 'child') +verify(c3.get_state() == 'child') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +# Wait till first router has either established a link or +# has a valid "next hop" towards all other routers. + +r1_rloc16 = int(r1.get_rloc16(), 16) + + +def check_r1_router_table(): + table = r1.get_router_table() + verify(len(table) == 3) + for entry in table: + verify(int(entry['RLOC16'], 0) == r1_rloc16 or int(entry['Link']) == 1 or int(entry['Next Hop']) != 63) + + +verify_within(check_r1_router_table, 120) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +r1_address = r1.get_mleid_ip_addr() +c1_address = c1.get_mleid_ip_addr() +c2_address = c2.get_mleid_ip_addr() +c3_address = c3.get_mleid_ip_addr() + +r1_rloc16 = int(r1.get_rloc16(), 16) +r2_rloc16 = int(r2.get_rloc16(), 16) +r3_rloc16 = int(r3.get_rloc16(), 16) +c1_rloc16 = int(c1.get_rloc16(), 16) +c3_rloc16 = int(c3.get_rloc16(), 16) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# From r1 ping c2 and c3 + +r1.ping(c2_address) +r1.ping(c3_address) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Verify that address cache table contains both c2 and c3 addresses c2 +# address should match its parent r2 (since c2 is sleepy), and c1 +# address should match to itself (since c3 is fed). + +cache_table = r1.get_eidcache() +verify(len(cache_table) >= 2) + +for entry in cache_table: + fields = entry.strip().split(' ') + verify(fields[2] == 'cache') + if fields[0] == c2_address: + verify(int(fields[1], 16) == r2_rloc16) + elif fields[0] == c3_address: + verify(int(fields[1], 16) == c3_rloc16) + else: + verify(False) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Force c2 to switch its parent from r2 to r3 +# +# New network topology +# +# r1 ---- r2 ---- r3 +# | /\ +# | / \ +# c1 c2(s) c3 + +r2.un_allowlist_node(c2) +r3.allowlist_node(c2) +c2.allowlist_node(r3) + +c2.thread_stop() +c2.thread_start() + + +def check_c2_attaches_to_r3(): + verify(c2.get_state() == 'child') + verify(int(c2.get_parent_info()['Rloc'], 16) == r3_rloc16) + + +verify_within(check_c2_attaches_to_r3, 10) + + +def check_r2_child_table_is_empty(): + verify(len(r2.get_child_table()) == 0) + + +verify_within(check_r2_child_table_is_empty, 10) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Note that r1 still has r2 as the destination for c2's address in its +# address cache table. But since r2 is aware that c2 is no longer its +# child, when it receives the IPv6 message with c2's address, r2 +# itself would do an address query for the address and forward the +# IPv6 message. + +cache_table = r1.get_eidcache() +for entry in cache_table: + fields = entry.strip().split(' ') + if fields[0] == c2_address: + verify(int(fields[1], 16) == r2_rloc16) + break +else: + verify(False) + +r1.ping(c2_address) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Ping c1 from c2. This will go through c1's parent r1. r1 upon +# receiving and forwarding the message should update its address +# cache table for c2 (address cache update through snooping). + +c2.ping(c1_address) + +cache_table = r1.get_eidcache() + +for entry in cache_table: + fields = entry.strip().split(' ') + if fields[0] == c2_address: + verify(int(fields[1], 16) == r3_rloc16) + verify(fields[2] == 'snoop') + break +else: + verify(False) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-014-address-resolver.py b/tests/toranj/cli/test-014-address-resolver.py new file mode 100755 index 00000000000..4203129fa59 --- /dev/null +++ b/tests/toranj/cli/test-014-address-resolver.py @@ -0,0 +1,415 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: Address Cache Table +# +# This test verifies the behavior of `AddressResolver` and how the cache +# table is managed. In particular it verifies behavior query timeout and +# query retry and snoop optimization. +# +# Build network topology +# +# r3 ---- r1 ---- r2 +# | | +# | | +# c3 c2 +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +r3 = cli.Node() +c2 = cli.Node() +c3 = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) +r1.allowlist_node(r3) + +r2.allowlist_node(r1) +r2.allowlist_node(c2) + +r3.allowlist_node(r1) +r3.allowlist_node(c3) + +c2.allowlist_node(r2) +c3.allowlist_node(r3) + +r1.form('addrrslvr') + +prefix = 'fd00:abba::' +r1.add_prefix(prefix + '/64', 'pos', 'med') +r1.register_netdata() + +r2.join(r1) +r3.join(r1) +c2.join(r1, cli.JOIN_TYPE_END_DEVICE) +c3.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE) +c3.set_pollperiod(400) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') +verify(r3.get_state() == 'router') +verify(c2.get_state() == 'child') +verify(c3.get_state() == 'child') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +# Wait till first router has either established a link or +# has a valid "next hop" towards all other routers. + +r1_rloc16 = int(r1.get_rloc16(), 16) + + +def check_r1_router_table(): + table = r1.get_router_table() + verify(len(table) == 3) + for entry in table: + verify(int(entry['RLOC16'], 0) == r1_rloc16 or int(entry['Link']) == 1 or int(entry['Next Hop']) != 63) + + +verify_within(check_r1_router_table, 120) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +r1_rloc = int(r1.get_rloc16(), 16) +r2_rloc = int(r2.get_rloc16(), 16) +r3_rloc = int(r3.get_rloc16(), 16) +c2_rloc = int(c2.get_rloc16(), 16) +c3_rloc = int(c3.get_rloc16(), 16) + +# AddressResolver constants: + +max_cache_entries = 16 +max_snooped_non_evictable = 2 + +# Add IPv6 addresses matching the on-mesh prefix on all nodes + +r1.add_ip_addr(prefix + '1') + +num_addresses = 4 # Number of addresses to add on r2, r3, c2, and c3 + +for num in range(num_addresses): + r2.add_ip_addr(prefix + "2:" + str(num)) + r3.add_ip_addr(prefix + "3:" + str(num)) + c2.add_ip_addr(prefix + "c2:" + str(num)) + c3.add_ip_addr(prefix + "c3:" + str(num)) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# From r1 send msg to a group of addresses that are not provided by +# any nodes in network. + +num_queries = 5 +stagger_interval = 1.2 +port = 1234 +initial_retry_delay = 8 + +r1.udp_open() + +for num in range(num_queries): + r1.udp_send(prefix + '800:' + str(num), port, 'hi_nobody') + # Wait before next tx to stagger the address queries + # request ensuring different timeouts + time.sleep(stagger_interval / (num_queries * speedup)) + +# Verify that we do see entries in cache table for all the addresses +# and all are in "query" state + +cache_table = r1.get_eidcache() +verify(len(cache_table) == num_queries) +for entry in cache_table: + fields = entry.strip().split(' ') + verify(fields[2] == 'query') + verify(fields[3] == 'canEvict=0') + verify(fields[4].startswith('timeout=')) + verify(int(fields[4].split('=')[1]) > 0) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Check the retry-query behavior +# +# Wait till all the address queries time out and verify they +# enter "retry-query" state. + + +def check_cache_entry_switch_to_retry_state(): + cache_table = r1.get_eidcache() + for entry in cache_table: + fields = entry.strip().split(' ') + verify(fields[2] == 'retry') + verify(fields[3] == 'canEvict=1') + verify(fields[4].startswith('timeout=')) + verify(int(fields[4].split('=')[1]) >= 0) + verify(fields[5].startswith('retryDelay=')) + verify(int(fields[5].split('=')[1]) == initial_retry_delay) + + +verify_within(check_cache_entry_switch_to_retry_state, 20) + +# Try sending again to same addresses which are all in "retry" state. + +for num in range(num_queries): + r1.udp_send(prefix + '800:' + str(num), port, 'hi_nobody') + +# Make sure the entries stayed in retry-query state as before. + +verify_within(check_cache_entry_switch_to_retry_state, 20) + +# Now wait for all entries to reach zero timeout. + + +def check_cache_entry_in_retry_state_to_get_to_zero_timeout(): + cache_table = r1.get_eidcache() + for entry in cache_table: + fields = entry.strip().split(' ') + verify(fields[2] == 'retry') + verify(fields[3] == 'canEvict=1') + verify(fields[4].startswith('timeout=')) + verify(int(fields[4].split('=')[1]) == 0) + + +verify_within(check_cache_entry_in_retry_state_to_get_to_zero_timeout, 20) + +# Now send again to the same addresses. + +for num in range(num_queries): + r1.udp_send(prefix + '800:' + str(num), port, 'hi_nobody') + +# We expect now after the delay to see retries for same addresses. + + +def check_cache_entry_switch_to_query_state(): + cache_table = r1.get_eidcache() + for entry in cache_table: + fields = entry.strip().split(' ') + verify(fields[2] == 'query') + verify(fields[3] == 'canEvict=1') + + +verify_within(check_cache_entry_switch_to_query_state, 20) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Verify snoop optimization behavior. + +# Send to r1 from all addresses on r2. + +r2.udp_open() +for num in range(num_addresses): + r2.udp_bind(prefix + '2:' + str(num), port) + r2.udp_send(prefix + '1', port, 'hi_r1_from_r2_snoop_me') + +# Verify that we see all addresses from r2 as snooped in cache table. +# At most two of them should be marked as non-evictable. + + +def check_cache_entry_contains_snooped_entries(): + cache_table = r1.get_eidcache() + verify(len(cache_table) >= num_addresses) + snooped_count = 0 + snooped_non_evictable = 0 + for entry in cache_table: + fields = entry.strip().split(' ') + if fields[2] == 'snoop': + verify(fields[0].startswith('fd00:abba:0:0:0:0:2:')) + verify(int(fields[1], 16) == r2_rloc) + snooped_count = snooped_count + 1 + if fields[3] == 'canEvict=0': + snooped_non_evictable = snooped_non_evictable + 1 + verify(snooped_count == num_addresses) + verify(snooped_non_evictable == max_snooped_non_evictable) + + +verify_within(check_cache_entry_contains_snooped_entries, 20) + +# Now we use the snooped entries by sending from r1 to r2 using +# all its addresses. + +for num in range(num_addresses): + r1.udp_send(prefix + '2:' + str(num), port, 'hi_back_r2_from_r1') + +time.sleep(0.1) + +# We expect to see the entries to be in "cached" state now. + +cache_table = r1.get_eidcache() +verify(len(cache_table) >= num_addresses) +match_count = 0 +for entry in cache_table: + fields = entry.strip().split(' ') + if fields[0].startswith('fd00:abba:0:0:0:0:2:'): + verify(fields[2] == 'cache') + verify(fields[3] == 'canEvict=1') + match_count = match_count + 1 +verify(match_count == num_addresses) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Check query requests and last transaction time + +# Send from r1 to all addresses on r3. Check entries +# for r3 are at the top of cache table list. + +for num in range(num_addresses): + r1.udp_send(prefix + '3:' + str(num), port, 'hi_r3_from_r1') + + +def check_cache_entry_contains_r3_entries(): + cache_table = r1.get_eidcache() + for num in range(num_addresses): + entry = cache_table[num] + fields = entry.strip().split(' ') + verify(fields[0].startswith('fd00:abba:0:0:0:0:3:')) + verify(int(fields[1], 16) == r3_rloc) + verify(fields[2] == 'cache') + verify(fields[3] == 'canEvict=1') + verify(fields[4] == 'transTime=0') + + +verify_within(check_cache_entry_contains_r3_entries, 20) + +# Send from r1 to all addresses of c3 (sleepy child of r3) + +for num in range(num_addresses): + r1.udp_send(prefix + 'c3:' + str(num), port, 'hi_c3_from_r1') + + +def check_cache_entry_contains_c3_entries(): + cache_table = r1.get_eidcache() + for num in range(num_addresses): + entry = cache_table[num] + fields = entry.strip().split(' ') + verify(fields[0].startswith('fd00:abba:0:0:0:0:c3:')) + verify(int(fields[1], 16) == r3_rloc) + verify(fields[2] == 'cache') + verify(fields[3] == 'canEvict=1') + verify(fields[4] == 'transTime=0') + + +verify_within(check_cache_entry_contains_c3_entries, 20) + +# Send again to r2. This should cause the related cache entries to +# be moved to top of the list. + +for num in range(num_addresses): + r1.udp_send(prefix + '2:' + str(num), port, 'hi_again_r2_from_r1') + + +def check_cache_entry_contains_r2_entries(): + cache_table = r1.get_eidcache() + for num in range(num_addresses): + entry = cache_table[num] + fields = entry.strip().split(' ') + verify(fields[0].startswith('fd00:abba:0:0:0:0:2:')) + verify(int(fields[1], 16) == r2_rloc) + verify(fields[2] == 'cache') + verify(fields[3] == 'canEvict=1') + + +verify_within(check_cache_entry_contains_r2_entries, 20) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Check behavior when address cache table is full. + +cache_table = r1.get_eidcache() +verify(len(cache_table) == max_cache_entries) + +# From r1 send to non-existing addresses. + +for num in range(num_queries): + r1.udp_send(prefix + '900:' + str(num), port, 'hi_nobody!') + +cache_table = r1.get_eidcache() +verify(len(cache_table) == max_cache_entries) + +# Send from c2 to r1 and verify that snoop optimization uses at most +# `max_snooped_non_evictable` entries + +c2.udp_open() + +for num in range(num_addresses): + c2.udp_bind(prefix + 'c2:' + str(num), port) + c2.udp_send(prefix + '1', port, 'hi_r1_from_c2_snoop_me') + + +def check_cache_entry_contains_max_allowed_snopped(): + cache_table = r1.get_eidcache() + snooped_non_evictable = 0 + for entry in cache_table: + fields = entry.strip().split(' ') + if fields[2] == 'snoop': + verify(fields[0].startswith('fd00:abba:0:0:0:0:c2:')) + verify(fields[3] == 'canEvict=0') + snooped_non_evictable = snooped_non_evictable + 1 + verify(snooped_non_evictable == max_snooped_non_evictable) + + +verify_within(check_cache_entry_contains_max_allowed_snopped, 20) + +# Now send from r1 to c2, the snooped entries would be used +# some other addresses will go through full address query. + +for num in range(num_addresses): + r1.udp_send(prefix + 'c2:' + str(num), port, 'hi_c2_from_r1') + + +def check_cache_entry_contains_c2_entries(): + cache_table = r1.get_eidcache() + for num in range(num_addresses): + entry = cache_table[num] + fields = entry.strip().split(' ') + verify(fields[0].startswith('fd00:abba:0:0:0:0:c2:')) + verify(int(fields[1], 16) == r2_rloc) + verify(fields[2] == 'cache') + verify(fields[3] == 'canEvict=1') + + +verify_within(check_cache_entry_contains_c2_entries, 20) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-015-clear-addresss-cache-for-sed.py b/tests/toranj/cli/test-015-clear-addresss-cache-for-sed.py new file mode 100755 index 00000000000..31f75cc1490 --- /dev/null +++ b/tests/toranj/cli/test-015-clear-addresss-cache-for-sed.py @@ -0,0 +1,232 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: Address Cache Table +# +# This test verifies that address cache entries associated with SED child +# addresses are removed from new parent node ensuring we would not have a +# routing loop. +# +# +# r1 ---- r2 ---- r3 +# | +# | +# c +# +# c is initially attached to r3 but it switches parent during test to r2 and then r1. + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 40 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +r3 = cli.Node() +c = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) + +r2.allowlist_node(r1) +r2.allowlist_node(r3) + +r3.allowlist_node(r2) +r3.allowlist_node(c) + +c.allowlist_node(r3) + +r1.form('sed-cache') + +prefix = 'fd00:abba::' +r1.add_prefix(prefix + '/64', 'paos', 'med') +r1.register_netdata() + +r2.join(r1) +r3.join(r1) +c.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE) +c.set_pollperiod(400) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') +verify(r3.get_state() == 'router') +verify(c.get_state() == 'child') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +# Wait till first router has either established a link or +# has a valid "next hop" towards all other routers. + +r1_rloc16 = int(r1.get_rloc16(), 16) + + +def check_r1_router_table(): + table = r1.get_router_table() + verify(len(table) == 3) + for entry in table: + verify(int(entry['RLOC16'], 0) == r1_rloc16 or int(entry['Link']) == 1 or int(entry['Next Hop']) != 63) + + +verify_within(check_r1_router_table, 120) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +r1_rloc = int(r1.get_rloc16(), 16) +r2_rloc = int(r2.get_rloc16(), 16) +r3_rloc = int(r3.get_rloc16(), 16) +c_rloc = int(c.get_rloc16(), 16) + +r1_address = next(addr for addr in r1.get_ip_addrs() if addr.startswith('fd00:abba:')) +r2_address = next(addr for addr in r2.get_ip_addrs() if addr.startswith('fd00:abba:')) +r3_address = next(addr for addr in r3.get_ip_addrs() if addr.startswith('fd00:abba:')) +c_address = next(addr for addr in c.get_ip_addrs() if addr.startswith('fd00:abba:')) + +port = 4321 + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Send a single UDP message from r2 to c. This adds an address cache +# entry on r2 for c pointing to r3 (the current parent of c). + +r2.udp_open() +r2.udp_send(c_address, port, 'hi_from_r2_to_c') + + +def check_r2_cache_table(): + cache_table = r2.get_eidcache() + for entry in cache_table: + fields = entry.strip().split(' ') + if (fields[0] == c_address): + verify(int(fields[1], 16) == r3_rloc) + verify(fields[2] == 'cache') + break + else: + verify(False) + + +verify_within(check_r2_cache_table, 20) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Force c to switch its parent from r3 to r2 +# +# r1 ---- r2 ---- r3 +# | +# | +# c + +r3.un_allowlist_node(c) +r2.allowlist_node(c) +c.allowlist_node(r2) +c.un_allowlist_node(r3) + +c.thread_stop() +c.thread_start() + + +def check_c_attaches_to_r2(): + verify(c.get_state() == 'child') + verify(int(c.get_parent_info()['Rloc'], 16) == r2_rloc) + + +verify_within(check_c_attaches_to_r2, 10) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Send a single UDP message from r3 to c. This adds an address cache +# entry on r3 for c pointing to r2 (the current parent of c). + +r3.udp_open() +r3.udp_send(c_address, port, 'hi_from_r3_to_c') + + +def check_r3_cache_table(): + cache_table = r3.get_eidcache() + for entry in cache_table: + fields = entry.strip().split(' ') + if (fields[0] == c_address): + verify(int(fields[1], 16) == r2_rloc) + verify(fields[2] == 'cache') + break + else: + verify(False) + + +verify_within(check_r3_cache_table, 20) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Force c to switch its parent again now to r1 +# +# r1 ---- r2 ---- r3 +# | +# | +# c + +r2.un_allowlist_node(c) +r1.allowlist_node(c) +c.allowlist_node(r1) +c.un_allowlist_node(r2) + +c.thread_stop() +c.thread_start() + + +def check_c_attaches_to_r1(): + verify(c.get_state() == 'child') + verify(int(c.get_parent_info()['Rloc'], 16) == r1_rloc) + + +verify_within(check_c_attaches_to_r1, 10) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Now ping c from r2. +# +# If r2 address cache entry is not cleared when c attached to r1, r2 +# will still have an entry pointing to r3, and r3 will have an entry +# pointing to r2, thus creating a loop (the msg will not be delivered +# to r3) + +r2.ping(c_address) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-016-child-mode-change.py b/tests/toranj/cli/test-016-child-mode-change.py new file mode 100755 index 00000000000..b8cfa2ec470 --- /dev/null +++ b/tests/toranj/cli/test-016-child-mode-change.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: +# +# Verify device mode change on children. +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +parent = cli.Node() +child1 = cli.Node() +child2 = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +parent.form('modechange') +child1.join(parent, cli.JOIN_TYPE_END_DEVICE) +child2.join(parent, cli.JOIN_TYPE_SLEEPY_END_DEVICE) + +verify(parent.get_state() == 'leader') +verify(child1.get_state() == 'child') +verify(child2.get_state() == 'child') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +child1_rloc = int(child1.get_rloc16(), 16) +child2_rloc = int(child2.get_rloc16(), 16) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Check the mode on children and also on parent child table + +verify(parent.get_mode() == 'rdn') +verify(child1.get_mode() == 'rn') +verify(child2.get_mode() == '-') + +child_table = parent.get_child_table() +verify(len(child_table) == 2) +for entry in child_table: + if int(entry['RLOC16'], 16) == child1_rloc: + verify(entry['R'] == '1') + verify(entry['D'] == '0') + verify(entry['N'] == '1') + elif int(entry['RLOC16'], 16) == child2_rloc: + verify(entry['R'] == '0') + verify(entry['D'] == '0') + verify(entry['N'] == '0') + else: + verify(False) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Change the network data flag on child 2 (sleepy) and verify that it +# gets changed on parent's child table. + +child2.set_mode('n') + + +def check_child2_n_flag_change(): + verify(child2.get_mode() == 'n') + child_table = parent.get_child_table() + verify(len(child_table) == 2) + for entry in child_table: + if int(entry['RLOC16'], 16) == child1_rloc: + verify(entry['R'] == '1') + verify(entry['D'] == '0') + verify(entry['N'] == '1') + elif int(entry['RLOC16'], 16) == child2_rloc: + verify(entry['R'] == '0') + verify(entry['D'] == '0') + verify(entry['N'] == '1') + else: + verify(False) + + +verify_within(check_child2_n_flag_change, 5) + +# Verify that mode change did not cause child2 to detach and re-attach + +verify(int(cli.Node.parse_list(child2.get_mle_counter())['Role Detached']) == 1) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Change child1 from rx-on to sleepy and verify the change on +# parent's child table. This mode change should require child +# to detach and attach again. + +child1.set_mode('n') + + +def check_child1_become_sleepy(): + verify(child1.get_mode() == 'n') + child_table = parent.get_child_table() + verify(len(child_table) == 2) + for entry in child_table: + if int(entry['RLOC16'], 16) == child1_rloc: + verify(entry['R'] == '0') + verify(entry['D'] == '0') + verify(entry['N'] == '1') + elif int(entry['RLOC16'], 16) == child2_rloc: + verify(entry['R'] == '0') + verify(entry['D'] == '0') + verify(entry['N'] == '1') + else: + verify(False) + + +verify_within(check_child1_become_sleepy, 5) + +verify(int(cli.Node.parse_list(child1.get_mle_counter())['Role Detached']) == 2) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Change child2 from sleepy to rx-on and verify the change on +# parent's child table. Verify that child2 did not detach and +# used MLE "Child Update" exchange. + +child2.set_mode('rn') + + +def check_child2_become_rx_on(): + verify(child2.get_mode() == 'rn') + child_table = parent.get_child_table() + verify(len(child_table) == 2) + for entry in child_table: + if int(entry['RLOC16'], 16) == child1_rloc: + verify(entry['R'] == '0') + verify(entry['D'] == '0') + verify(entry['N'] == '1') + elif int(entry['RLOC16'], 16) == child2_rloc: + verify(entry['R'] == '1') + verify(entry['D'] == '0') + verify(entry['N'] == '1') + else: + verify(False) + + +verify_within(check_child2_become_rx_on, 5) + +verify(int(cli.Node.parse_list(child2.get_mle_counter())['Role Detached']) == 1) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Now change child2 to become sleepy again. Since it was originally +# attached as sleepy it should not detach and attach again and +# can do this using MlE "Child Update" exchange with parent. + +child2.set_mode('n') + + +def check_child2_become_sleepy_again(): + verify(child2.get_mode() == 'n') + child_table = parent.get_child_table() + verify(len(child_table) == 2) + for entry in child_table: + if int(entry['RLOC16'], 16) == child1_rloc: + verify(entry['R'] == '0') + verify(entry['D'] == '0') + verify(entry['N'] == '1') + elif int(entry['RLOC16'], 16) == child2_rloc: + verify(entry['R'] == '0') + verify(entry['D'] == '0') + verify(entry['N'] == '1') + else: + verify(False) + + +verify_within(check_child2_become_sleepy_again, 5) + +verify(int(cli.Node.parse_list(child2.get_mle_counter())['Role Detached']) == 1) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-017-network-data-versions.py b/tests/toranj/cli/test-017-network-data-versions.py new file mode 100755 index 00000000000..d90bb13f0aa --- /dev/null +++ b/tests/toranj/cli/test-017-network-data-versions.py @@ -0,0 +1,346 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: Network Data update and version changes (stable only vs. full version). +# +# Network topology +# +# r1 ---- r2 ---- r3 +# | +# | +# sed +# +# +# sed is sleepy-end node and also configured to request stable Network Data only +# +# Test covers the following steps: +# - Adding/removing prefixes (stable or temporary) on r1 +# - Verifying that Network Data is updated on all nodes +# - Ensuring correct update to version and stable version +# +# The above steps are repeated over many different situations: +# - Where the same prefixes are also added by other nodes +# - Or the same prefixes are added as off-mesh routes by other nodes + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +r3 = cli.Node() +sed = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) + +r2.allowlist_node(r1) +r2.allowlist_node(r3) +r2.allowlist_node(sed) + +r2.allowlist_node(r2) + +sed.allowlist_node(r2) + +r1.form('netdata') +r2.join(r1) +r3.join(r1) +sed.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE) + +sed.set_mode('-') +sed.set_pollperiod(400) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +r1_rloc = r1.get_rloc16() +r2_rloc = r2.get_rloc16() +r3_rloc = r3.get_rloc16() + +versions = r1.get_netdata_versions() + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +def verify_versions_incremented(): + global versions + new_versions = r1.get_netdata_versions() + verify(new_versions[0] == ((versions[0] + 1) % 256)) + verify(new_versions[1] == ((versions[1] + 1) % 256)) + versions = new_versions + + +def verify_stabe_version_incremented(): + global versions + new_versions = r1.get_netdata_versions() + verify(new_versions[0] == ((versions[0] + 1) % 256)) + verify(new_versions[1] == versions[1]) + versions = new_versions + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Add prefix `fd00:1::/64` on r1 as stable and validate +# that entries are updated on all nodes and versions are changed. + +r1.add_prefix('fd00:1::/64', 'os') +r1.register_netdata() + + +def check_r1_prefix_added_on_all_nodes(): + for node in [r1, r2, r3]: + verify('fd00:1:0:0::/64 os med ' + r1_rloc in node.get_netdata_prefixes()) + verify('fd00:1:0:0::/64 os med fffe' in sed.get_netdata_prefixes()) + + +verify_within(check_r1_prefix_added_on_all_nodes, 2) +verify_versions_incremented() + +# Add prefix `fd00:2::/64` on r2 as temporary and ensure it is seen on +# all nodes and not seen on sed. + +r2.add_prefix('fd00:2::/64', 'po', 'high') +r2.register_netdata() + + +def check_r2_prefix_added_on_all_nodes(): + for node in [r1, r2, r3]: + verify('fd00:2:0:0::/64 po high ' + r2_rloc in node.get_netdata_prefixes()) + verify(not 'fd00:2:0:0::/64 po high fffe' in sed.get_netdata_prefixes()) + + +verify_within(check_r2_prefix_added_on_all_nodes, 2) +verify_stabe_version_incremented() + +# Remove prefix `fd00:1::/64` from r1. + +r1.remove_prefix('fd00:1::/64') +r1.register_netdata() + + +def check_r1_prefix_removed_on_all_nodes(): + for node in [r1, r2, r3]: + verify(not 'fd00:1:0:0::/64 os med ' + r1_rloc in node.get_netdata_prefixes()) + verify(not 'fd00:1:0:0::/64 os med fffe' in sed.get_netdata_prefixes()) + + +verify_within(check_r1_prefix_removed_on_all_nodes, 2) +verify_versions_incremented() + +# Remove prefix `fd00:2::/64` from r2. + +r2.remove_prefix('fd00:2::/64') +r2.register_netdata() + + +def check_r2_prefix_removed_on_all_nodes(): + for node in [r1, r2, r3]: + verify(not 'fd00:2:0:0::/64 po high ' + r2_rloc in node.get_netdata_prefixes()) + verify(not 'fd00:2:0:0::/64 po high fffe' in sed.get_netdata_prefixes()) + + +verify_within(check_r2_prefix_removed_on_all_nodes, 2) +verify_stabe_version_incremented() + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Repeat the same checks but now r3 also adds ``fd00:1::/64` with different +# flags. + +r3.add_prefix('fd00:1::/64', 'paos') +r3.register_netdata() + + +def check_r3_prefix_added_on_all_nodes(): + for node in [r1, r2, r3]: + verify('fd00:1:0:0::/64 paos med ' + r3_rloc in node.get_netdata_prefixes()) + verify('fd00:1:0:0::/64 paos med fffe' in sed.get_netdata_prefixes()) + + +verify_within(check_r3_prefix_added_on_all_nodes, 2) +verify_versions_incremented() + +r1.add_prefix('fd00:1::/64', 'os') +r1.register_netdata() +verify_within(check_r1_prefix_added_on_all_nodes, 2) +verify_versions_incremented() + +r2.add_prefix('fd00:2::/64', 'po', 'high') +r2.register_netdata() +verify_within(check_r2_prefix_added_on_all_nodes, 2) +verify_stabe_version_incremented() + +r1.remove_prefix('fd00:1::/64') +r1.register_netdata() +verify_within(check_r1_prefix_removed_on_all_nodes, 2) +verify_versions_incremented() + +r2.remove_prefix('fd00:2::/64') +r2.register_netdata() +verify_within(check_r2_prefix_removed_on_all_nodes, 2) +verify_stabe_version_incremented() + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Repeat the same checks with r3 also adding ``fd00:2::/64` + +r3.add_prefix('fd00:2::/64', 'paos') +r3.register_netdata() + + +def check_new_r3_prefix_added_on_all_nodes(): + for node in [r1, r2, r3]: + verify('fd00:2:0:0::/64 paos med ' + r3_rloc in node.get_netdata_prefixes()) + verify('fd00:2:0:0::/64 paos med fffe' in sed.get_netdata_prefixes()) + + +verify_within(check_new_r3_prefix_added_on_all_nodes, 2) +verify_versions_incremented() + +r1.add_prefix('fd00:1::/64', 'os') +r1.register_netdata() +verify_within(check_r1_prefix_added_on_all_nodes, 2) +verify_versions_incremented() + +r2.add_prefix('fd00:2::/64', 'po', 'high') +r2.register_netdata() +verify_within(check_r2_prefix_added_on_all_nodes, 2) +verify_stabe_version_incremented() + +r1.remove_prefix('fd00:1::/64') +r1.register_netdata() +verify_within(check_r1_prefix_removed_on_all_nodes, 2) +verify_versions_incremented() + +r2.remove_prefix('fd00:2::/64') +r2.register_netdata() +verify_within(check_r2_prefix_removed_on_all_nodes, 2) +verify_stabe_version_incremented() + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Repeat the same checks with r3 adding the two prefixes as temporary. + +r3.remove_prefix('fd00:1::/64') +r3.remove_prefix('fd00:2::/64') +r3.add_prefix('fd00:1::/64', 'pao') +r3.add_prefix('fd00:2::/64', 'pao') +r3.register_netdata() + + +def check_r3_prefixes_as_temp_added_on_all_nodes(): + for node in [r1, r2, r3]: + prefixes = node.get_netdata_prefixes() + verify('fd00:1:0:0::/64 pao med ' + r3_rloc in prefixes) + verify('fd00:1:0:0::/64 pao med ' + r3_rloc in prefixes) + verify(len(sed.get_netdata_prefixes()) == 0) + + +verify_within(check_r3_prefixes_as_temp_added_on_all_nodes, 2) +verify_versions_incremented() + +r1.add_prefix('fd00:1::/64', 'os') +r1.register_netdata() +verify_within(check_r1_prefix_added_on_all_nodes, 2) +verify_versions_incremented() + +r2.add_prefix('fd00:2::/64', 'po', 'high') +r2.register_netdata() +verify_within(check_r2_prefix_added_on_all_nodes, 2) +verify_stabe_version_incremented() + +r1.remove_prefix('fd00:1::/64') +r1.register_netdata() +verify_within(check_r1_prefix_removed_on_all_nodes, 2) +verify_versions_incremented() + +r2.remove_prefix('fd00:2::/64') +r2.register_netdata() +verify_within(check_r2_prefix_removed_on_all_nodes, 2) +verify_stabe_version_incremented() + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Finally repeat the same checks with r3 adding the two prefixes +# as off-mesh route prefix. + +r3.remove_prefix('fd00:1::/64') +r3.remove_prefix('fd00:2::/64') +r3.add_route('fd00:1::/64', 's') +r3.add_route('fd00:2::/64', '-') +r3.register_netdata() + + +def check_r3_routes_added_on_all_nodes(): + for node in [r1, r2, r3]: + routes = node.get_netdata_routes() + verify('fd00:1:0:0::/64 s med ' + r3_rloc in routes) + verify('fd00:2:0:0::/64 med ' + r3_rloc in routes) + verify('fd00:1:0:0::/64 s med fffe' in sed.get_netdata_routes()) + verify(not 'fd00:1:0:0::/64 med fffe' in sed.get_netdata_routes()) + + +verify_within(check_r3_routes_added_on_all_nodes, 2) +verify_versions_incremented() + +r1.add_prefix('fd00:1::/64', 'os') +r1.register_netdata() +verify_within(check_r1_prefix_added_on_all_nodes, 2) +verify_versions_incremented() + +r2.add_prefix('fd00:2::/64', 'po', 'high') +r2.register_netdata() +verify_within(check_r2_prefix_added_on_all_nodes, 2) +verify_stabe_version_incremented() + +r1.remove_prefix('fd00:1::/64') +r1.register_netdata() +verify_within(check_r1_prefix_removed_on_all_nodes, 2) +verify_versions_incremented() + +r2.remove_prefix('fd00:2::/64') +r2.register_netdata() +verify_within(check_r2_prefix_removed_on_all_nodes, 2) +verify_stabe_version_incremented() + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-018-next-hop-and-path-cost.py b/tests/toranj/cli/test-018-next-hop-and-path-cost.py new file mode 100755 index 00000000000..b31b2f87816 --- /dev/null +++ b/tests/toranj/cli/test-018-next-hop-and-path-cost.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2023, 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: Next hop and path cost calculation. +# +# Network topology +# +# r1 ---- r2...2...r3 +# / | \ / \ +# / | \ / \ +# fed1 fed2 r4...1...r5 ---- fed3 +# +# Link r2 --> r3 is configured to be at link quality of 2. +# Link r5 --> r4 is configured to be at link quality of 1. +# Link r1 --> fed2 is configured to be at link quality 1. +# Other links are at link quality 3 (best possible link quality). +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 40 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +r3 = cli.Node() +r4 = cli.Node() +r5 = cli.Node() +fed1 = cli.Node() +fed2 = cli.Node() +fed3 = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) +r1.allowlist_node(fed1) +r1.allowlist_node(fed2) + +r2.allowlist_node(r1) +r2.allowlist_node(r3) +r2.allowlist_node(r4) + +r3.allowlist_node(r2) +r3.allowlist_node(r4) +r3.allowlist_node(r5) +r3.set_macfilter_lqi_to_node(r2, 2) + +r4.allowlist_node(r2) +r4.allowlist_node(r3) +r4.allowlist_node(r5) +r4.set_macfilter_lqi_to_node(r5, 1) + +r5.allowlist_node(r4) +r5.allowlist_node(r3) +r5.allowlist_node(fed3) + +fed1.allowlist_node(r1) +fed2.allowlist_node(r1) +fed3.allowlist_node(r5) +fed2.set_macfilter_lqi_to_node(r1, 1) + +r1.form('hop-cost') +r2.join(r1) +r3.join(r1) +r4.join(r1) +r5.join(r1) +fed1.join(r1, cli.JOIN_TYPE_REED) +fed2.join(r1, cli.JOIN_TYPE_REED) +fed3.join(r1, cli.JOIN_TYPE_REED) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +r1_rloc = int(r1.get_rloc16(), 16) +r2_rloc = int(r2.get_rloc16(), 16) +r3_rloc = int(r3.get_rloc16(), 16) +r4_rloc = int(r4.get_rloc16(), 16) +r5_rloc = int(r5.get_rloc16(), 16) + +fed1_rloc = int(fed1.get_rloc16(), 16) +fed2_rloc = int(fed2.get_rloc16(), 16) +fed3_rloc = int(fed3.get_rloc16(), 16) + + +def parse_nexthop(line): + # Exmaple: "0x5000 cost:3" -> (0x5000, 3). + items = line.strip().split(' ', 2) + return (int(items[0], 16), int(items[1].split(':')[1])) + + +def check_nexthops_and_costs(): + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # `r1` next hops and costs + verify(parse_nexthop(r1.get_nexthop(r1_rloc)) == (r1_rloc, 0)) + verify(parse_nexthop(r1.get_nexthop(r2_rloc)) == (r2_rloc, 1)) + verify(parse_nexthop(r1.get_nexthop(r3_rloc)) == (r2_rloc, 3)) + verify(parse_nexthop(r1.get_nexthop(r4_rloc)) == (r2_rloc, 2)) + verify(parse_nexthop(r1.get_nexthop(r5_rloc)) == (r2_rloc, 4)) + verify(parse_nexthop(r1.get_nexthop(fed3_rloc)) == (r2_rloc, 5)) + # On `r1` its children can be reached directly. + verify(parse_nexthop(r1.get_nexthop(fed1_rloc)) == (fed1_rloc, 1)) + verify(parse_nexthop(r1.get_nexthop(fed2_rloc)) == (fed2_rloc, 1)) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # `r2` next hops and costs + verify(parse_nexthop(r2.get_nexthop(r1_rloc)) == (r1_rloc, 1)) + verify(parse_nexthop(r2.get_nexthop(r2_rloc)) == (r2_rloc, 0)) + # On `r2` the direct link to `r3` and the path through `r4` both + # have the same cost, but the direct link should be preferred. + verify(parse_nexthop(r2.get_nexthop(r3_rloc)) == (r3_rloc, 2)) + verify(parse_nexthop(r2.get_nexthop(r4_rloc)) == (r4_rloc, 1)) + # On 'r2' the path to `r5` can go through `r3` or `r4` + # as both have the same cost. + (nexthop, cost) = parse_nexthop(r2.get_nexthop(r5_rloc)) + verify(cost == 3) + verify(nexthop in [r3_rloc, r4_rloc]) + verify(parse_nexthop(r2.get_nexthop(fed1_rloc)) == (r1_rloc, 2)) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # `r3` next hops and costs + verify(parse_nexthop(r3.get_nexthop(r3_rloc)) == (r3_rloc, 0)) + verify(parse_nexthop(r3.get_nexthop(r5_rloc)) == (r5_rloc, 1)) + verify(parse_nexthop(r3.get_nexthop(r4_rloc)) == (r4_rloc, 1)) + verify(parse_nexthop(r3.get_nexthop(r2_rloc)) == (r2_rloc, 2)) + # On `r3` the path to `r1` can go through `r2` or `r4` + # as both have the same cost. + (nexthop, cost) = parse_nexthop(r3.get_nexthop(r1_rloc)) + verify(cost == 3) + verify(nexthop in [r2_rloc, r4_rloc]) + # On `r3` the path to fed1 should use the same next hop as `r1` + verify(parse_nexthop(r3.get_nexthop(fed2_rloc)) == (nexthop, 4)) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # `r4` next hops and costs + verify(parse_nexthop(r4.get_nexthop(fed1_rloc)) == (r2_rloc, 3)) + verify(parse_nexthop(r4.get_nexthop(r1_rloc)) == (r2_rloc, 2)) + verify(parse_nexthop(r4.get_nexthop(r2_rloc)) == (r2_rloc, 1)) + verify(parse_nexthop(r4.get_nexthop(r3_rloc)) == (r3_rloc, 1)) + verify(parse_nexthop(r4.get_nexthop(r4_rloc)) == (r4_rloc, 0)) + # On `r4` even though we have a direct link to `r5` + # the path cost through `r3` has a smaller cost over + # the direct link cost. + verify(parse_nexthop(r4.get_nexthop(r5_rloc)) == (r3_rloc, 2)) + verify(parse_nexthop(r4.get_nexthop(fed3_rloc)) == (r3_rloc, 3)) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # `r5` next hops and costs + verify(parse_nexthop(r5.get_nexthop(fed3_rloc)) == (fed3_rloc, 1)) + verify(parse_nexthop(r5.get_nexthop(r5_rloc)) == (r5_rloc, 0)) + verify(parse_nexthop(r5.get_nexthop(r3_rloc)) == (r3_rloc, 1)) + verify(parse_nexthop(r5.get_nexthop(r4_rloc)) == (r3_rloc, 2)) + verify(parse_nexthop(r5.get_nexthop(r2_rloc)) == (r3_rloc, 3)) + verify(parse_nexthop(r5.get_nexthop(r1_rloc)) == (r3_rloc, 4)) + verify(parse_nexthop(r5.get_nexthop(fed1_rloc)) == (r3_rloc, 5)) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # `fed1` next hops and costs + verify(parse_nexthop(fed1.get_nexthop(fed1_rloc)) == (fed1_rloc, 0)) + verify(parse_nexthop(fed1.get_nexthop(r1_rloc)) == (r1_rloc, 1)) + verify(parse_nexthop(fed1.get_nexthop(r2_rloc)) == (r1_rloc, 2)) + verify(parse_nexthop(fed1.get_nexthop(r3_rloc)) == (r1_rloc, 4)) + verify(parse_nexthop(fed1.get_nexthop(r4_rloc)) == (r1_rloc, 3)) + verify(parse_nexthop(fed1.get_nexthop(r5_rloc)) == (r1_rloc, 5)) + verify(parse_nexthop(fed1.get_nexthop(fed3_rloc)) == (r1_rloc, 6)) + # On `fed1`, path to `fed2` should go through our parent. + verify(parse_nexthop(fed1.get_nexthop(fed2_rloc)) == (r1_rloc, 2)) + + #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + # `fed2` next hops and costs + verify(parse_nexthop(fed2.get_nexthop(fed2_rloc)) == (fed2_rloc, 0)) + verify(parse_nexthop(fed2.get_nexthop(r1_rloc)) == (r1_rloc, 4)) + verify(parse_nexthop(fed2.get_nexthop(r2_rloc)) == (r1_rloc, 5)) + verify(parse_nexthop(fed2.get_nexthop(r3_rloc)) == (r1_rloc, 7)) + verify(parse_nexthop(fed2.get_nexthop(r4_rloc)) == (r1_rloc, 6)) + verify(parse_nexthop(fed2.get_nexthop(r5_rloc)) == (r1_rloc, 8)) + verify(parse_nexthop(fed2.get_nexthop(fed3_rloc)) == (r1_rloc, 9)) + verify(parse_nexthop(fed2.get_nexthop(fed1_rloc)) == (r1_rloc, 5)) + + +verify_within(check_nexthops_and_costs, 5) + +#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Disable `r4` and check nexthop and cost on it and on other +# nodes. +# +# +# r1 ---- r2...2...r3 +# / | \ +# / | \ +# fed1 fed2 r4 r5 ---- fed3 + +r4.thread_stop() +r4.interface_down() + +verify(parse_nexthop(r4.get_nexthop(r2_rloc)) == (0xfffe, 16)) +verify(parse_nexthop(r4.get_nexthop(r4_rloc)) == (0xfffe, 16)) + + +def check_nexthops_and_costs_after_r4_detach(): + # Make sure we have no next hop towards `r4`. + verify(parse_nexthop(r1.get_nexthop(r4_rloc)) == (0xfffe, 16)) + verify(parse_nexthop(r2.get_nexthop(r4_rloc)) == (0xfffe, 16)) + verify(parse_nexthop(r3.get_nexthop(r4_rloc)) == (0xfffe, 16)) + verify(parse_nexthop(r5.get_nexthop(r4_rloc)) == (0xfffe, 16)) + verify(parse_nexthop(fed3.get_nexthop(r4_rloc)) == (r5_rloc, 16)) + # Check cost and next hop on other nodes + verify(parse_nexthop(r1.get_nexthop(r5_rloc)) == (r2_rloc, 4)) + verify(parse_nexthop(r2.get_nexthop(r3_rloc)) == (r3_rloc, 2)) + verify(parse_nexthop(r3.get_nexthop(r2_rloc)) == (r2_rloc, 2)) + verify(parse_nexthop(fed3.get_nexthop(fed1_rloc)) == (r5_rloc, 6)) + + +verify_within(check_nexthops_and_costs_after_r4_detach, 45) +verify(r1.get_state() == 'leader') + +#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Disable `r1` (which was previous leader) and check +# routes on other nodes +# +# +# r1 r2...2...r3 +# / | \ +# / | \ +# fed1 fed2 r4 r5 ---- fed3 + +r1.thread_stop() +r1.interface_down() +fed1.thread_stop() +fed1.interface_down() +fed2.thread_stop() +fed2.interface_down() + +verify(parse_nexthop(r1.get_nexthop(r2_rloc)) == (0xfffe, 16)) +verify(parse_nexthop(r1.get_nexthop(r1_rloc)) == (0xfffe, 16)) +verify(parse_nexthop(r1.get_nexthop(fed1_rloc)) == (0xfffe, 16)) + + +def check_nexthops_and_costs_after_r1_detach(): + verify(parse_nexthop(r2.get_nexthop(r1_rloc)) == (0xfffe, 16)) + verify(parse_nexthop(r3.get_nexthop(r1_rloc)) == (0xfffe, 16)) + verify(parse_nexthop(r5.get_nexthop(r1_rloc)) == (0xfffe, 16)) + verify(parse_nexthop(fed3.get_nexthop(r1_rloc)) == (r5_rloc, 16)) + verify(parse_nexthop(r2.get_nexthop(r5_rloc)) == (r3_rloc, 3)) + verify(parse_nexthop(fed3.get_nexthop(r2_rloc)) == (r5_rloc, 4)) + + +verify_within(check_nexthops_and_costs_after_r1_detach, 30) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-019-netdata-context-id.py b/tests/toranj/cli/test-019-netdata-context-id.py new file mode 100755 index 00000000000..68029da21e9 --- /dev/null +++ b/tests/toranj/cli/test-019-netdata-context-id.py @@ -0,0 +1,373 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2023, 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: Network Data Context ID assignment and reuse delay. +# +# Network topology +# +# r1 ---- r2 ---- r3 +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 40 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +r3 = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) + +r2.allowlist_node(r1) +r2.allowlist_node(r3) + +r3.allowlist_node(r2) + +r1.form('netdata') +r2.join(r1) +r3.join(r1) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') +verify(r3.get_state() == 'router') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +# Check the default reuse delay on `r1` to be 5 minutes. + +verify(int(r1.get_context_reuse_delay()) == 5 * 60) + +# Change the reuse delay on `r1` (leader) to 5 seconds. + +r1.set_context_reuse_delay(5) +verify(int(r1.get_context_reuse_delay()) == 5) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Add an on-mesh prefix from each router `r1`, r2`, and `r3`. +# Validate that netdata is updated and Context IDs are assigned. + +r1.add_prefix('fd00:1::/64', 'poas') +r1.register_netdata() + +r2.add_prefix('fd00:2::/64', 'poas') +r2.register_netdata() + +r3.add_prefix('fd00:3::/64', 'poas') +r3.add_route('fd00:beef::/64', 's') +r3.register_netdata() + + +def check_netdata_1(): + global netdata + netdata = r1.get_netdata() + verify(len(netdata['prefixes']) == 3) + verify(len(netdata['routes']) == 1) + + +verify_within(check_netdata_1, 5) + +contexts = netdata['contexts'] +verify(len(contexts) == 3) +verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) +for context in contexts: + verify(context.endswith('c')) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Remove prefix on `r3`. Validate that Context compress flag +# is cleared for the removed prefix. + +r3.remove_prefix('fd00:3::/64') +r3.register_netdata() + + +def check_netdata_2(): + global netdata, versions + versions = r1.get_netdata_versions() + netdata = r1.get_netdata() + verify(len(netdata['prefixes']) == 2) + verify(len(netdata['routes']) == 1) + + +verify_within(check_netdata_2, 5) + +contexts = netdata['contexts'] +verify(len(contexts) == 3) +verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) +for context in contexts: + if context.startswith('fd00:1:0:0::/64') or context.startswith('fd00:2:0:0::/64'): + verify(context.endswith('c')) + else: + verify(context.endswith('-')) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Validate that the prefix context is removed within reuse delay +# time (which is set to 5 seconds on leader). + + +def check_netdata_3(): + global netdata + netdata = r1.get_netdata() + verify(len(netdata['prefixes']) == 2) + verify(len(netdata['routes']) == 1) + verify(len(netdata['contexts']) == 2) + + +verify_within(check_netdata_3, 5) +old_versions = versions +versions = r1.get_netdata_versions() +verify(versions != old_versions) + +# Make sure netdata does not change afterwards + +time.sleep(5 * 3 / speedup) + +verify(versions == r1.get_netdata_versions()) +netdata = r1.get_netdata() +verify(len(netdata['prefixes']) == 2) +verify(len(netdata['routes']) == 1) +verify(len(netdata['contexts']) == 2) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Add two new on-mesh prefixes from `r3`. Validate that they get +# assigned Context IDs. + +r3.add_prefix('fd00:4::/64', 'poas') +r3.register_netdata() + +r3.add_prefix('fd00:3::/64', 'poas') +r3.register_netdata() + + +def check_netdata_4(): + global netdata + netdata = r1.get_netdata() + verify(len(netdata['prefixes']) == 4) + verify(len(netdata['routes']) == 1) + verify(len(netdata['contexts']) == 4) + + +verify_within(check_netdata_4, 5) + +contexts = netdata['contexts'] +verify(len(contexts) == 4) +verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts])) +for context in contexts: + verify(context.endswith('c')) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Remove prefixes on `r1` and `r2` and re-add them back quickly both +# from `r2`. Validate that they get the same context IDs as before. + +for context in contexts: + if context.startswith('fd00:1:0:0::/64'): + cid1 = int(context.split()[1]) + elif context.startswith('fd00:2:0:0::/64'): + cid2 = int(context.split()[1]) + +r1.remove_prefix('fd00:1:0:0::/64') +r1.register_netdata() + +r2.remove_prefix('fd00:2:0:0::/64') +r2.register_netdata() + + +def check_netdata_5(): + global netdata + netdata = r1.get_netdata() + verify(len(netdata['prefixes']) == 2) + verify(len(netdata['routes']) == 1) + + +verify_within(check_netdata_5, 5) + +contexts = netdata['contexts'] +verify(len(contexts) == 4) +verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts])) +for context in contexts: + if context.startswith('fd00:1:0:0::/64') or context.startswith('fd00:2:0:0::/64'): + verify(context.endswith('-')) + else: + verify(context.endswith('c')) + +# Re-add both prefixes (now from `r2`) before CID remove delay time +# is expired. + +r2.add_prefix('fd00:1::/64', 'poas') +r2.add_prefix('fd00:2::/64', 'poas') +r2.register_netdata() + + +def check_netdata_6(): + global netdata + netdata = r1.get_netdata() + verify(len(netdata['prefixes']) == 4) + verify(len(netdata['routes']) == 1) + + +verify_within(check_netdata_6, 5) + +contexts = netdata['contexts'] +verify(len(contexts) == 4) +verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts])) +for context in contexts: + verify(context.endswith('c')) + if context.startswith('fd00:1:0:0::/64'): + verify(int(context.split()[1]) == cid1) + elif context.startswith('fd00:2:0:0::/64'): + verify(int(context.split()[1]) == cid2) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Remove two prefixes on `r3`. Add one back as an external route from +# `r1`. Also add a new prefix from `r1`. Validate that the context IDs +# for the removed on-mesh prefixes are removed after reuse delay time +# and that the new prefix gets a different Context ID. + +# Remember the CID used. +for context in contexts: + if context.startswith('fd00:3:0:0::/64'): + cid3 = int(context.split()[1]) + elif context.startswith('fd00:4:0:0::/64'): + cid4 = int(context.split()[1]) + +r3.remove_prefix('fd00:3:0:0::/64') +r3.remove_prefix('fd00:4:0:0::/64') +r3.register_netdata() + + +def check_netdata_7(): + global netdata + netdata = r1.get_netdata() + verify(len(netdata['prefixes']) == 2) + verify(len(netdata['routes']) == 1) + + +verify_within(check_netdata_7, 5) + +contexts = netdata['contexts'] +verify(len(contexts) == 4) +verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:3:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:4:0:0::/64') for context in contexts])) +for context in contexts: + if context.startswith('fd00:3:0:0::/64') or context.startswith('fd00:4:0:0::/64'): + verify(context.endswith('-')) + else: + verify(context.endswith('c')) + +# Add first one removed as route and add a new prefix. + +r1.add_route('fd00:3:0:0::/64', 's') +r1.add_prefix('fd00:5:0:0::/64', 'poas') +r1.register_netdata() + + +def check_netdata_8(): + global netdata + netdata = r1.get_netdata() + verify(len(netdata['prefixes']) == 3) + verify(len(netdata['routes']) == 2) + verify(len(netdata['contexts']) == 3) + + +verify_within(check_netdata_8, 5) + +contexts = netdata['contexts'] +verify(len(contexts) == 3) +verify(any([context.startswith('fd00:1:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:2:0:0::/64') for context in contexts])) +verify(any([context.startswith('fd00:5:0:0::/64') for context in contexts])) + +for context in contexts: + verify(context.endswith('c')) + if context.startswith('fd00:5:0:0::/64'): + verify(not int(context.split()[1]) in [cid3, cid4]) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Remove a prefix on `r2`, wait one second and remove a second +# prefix. Validate the Context IDs for both are removed. + +r2.remove_prefix('fd00:1::/64') +r2.register_netdata() + +time.sleep(1 / speedup) + +r2.remove_prefix('fd00:2::/64') +r2.register_netdata() + + +def check_netdata_9(): + global netdata + netdata = r1.get_netdata() + verify(len(netdata['prefixes']) == 1) + verify(len(netdata['routes']) == 2) + verify(len(netdata['contexts']) == 1) + + +verify_within(check_netdata_9, 5) + +contexts = netdata['contexts'] +verify(len(contexts) == 1) +verify(contexts[0].startswith('fd00:5:0:0::/64')) +verify(contexts[0].endswith('c')) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-020-net-diag-vendor-info.py b/tests/toranj/cli/test-020-net-diag-vendor-info.py new file mode 100755 index 00000000000..6f205cd2585 --- /dev/null +++ b/tests/toranj/cli/test-020-net-diag-vendor-info.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2023, 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: Network Diagnostics Vendor Name, Vendor Model, Vendor SW Version TLVs. +# +# Network topology +# +# r1 ---- r2 +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 40 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.form('netdiag-vendor') +r2.join(r1) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +VENDOR_NAME_TLV = 25 +VENDOR_MODEL_TLV = 26 +VENDOR_SW_VERSION_TLV = 27 +THREAD_STACK_VERSION_TLV = 28 + +#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Check setting vendor name, model, ans sw version + +r1.set_vendor_name('nest') +r1.set_vendor_model('marble') +r1.set_vendor_sw_version('ot-1.3.1') + +verify(r1.get_vendor_name() == 'nest') +verify(r1.get_vendor_model() == 'marble') +verify(r1.get_vendor_sw_version() == 'ot-1.3.1') + +#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Check invalid names (too long) + +# Vendor name should accept up to 32 chars + +r2.set_vendor_name('01234567890123456789012345678901') # 32 chars + +errored = False + +try: + r2.set_vendor_name('012345678901234567890123456789012') # 33 chars +except cli.CliError as e: + verify(e.message == 'InvalidArgs') + errored = True + +verify(errored) + +# Vendor model should accept up to 32 chars + +r2.set_vendor_model('01234567890123456789012345678901') # 32 chars + +errored = False + +try: + r2.set_vendor_model('012345678901234567890123456789012') # 33 chars +except cli.CliError as e: + verify(e.message == 'InvalidArgs') + errored = True + +verify(errored) + +# Vendor SW version should accept up to 16 chars + +r2.set_vendor_sw_version('0123456789012345') # 16 chars + +errored = False + +try: + r2.set_vendor_sw_version('01234567890123456') # 17 chars +except cli.CliError as e: + verify(e.message == 'InvalidArgs') + errored = True + +verify(errored) + +#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Perform net diag query + +r1_rloc = r1.get_rloc_ip_addr() +r2_rloc = r2.get_rloc_ip_addr() + +# Get vendor name (TLV 27) + +result = r2.cli('networkdiagnostic get', r1_rloc, VENDOR_NAME_TLV) +verify(len(result) == 2) +verify(result[1].startswith("Vendor Name:")) +verify(result[1].split(':')[1].strip() == r1.get_vendor_name()) + +# Get vendor model (TLV 28) + +result = r2.cli('networkdiagnostic get', r1_rloc, VENDOR_MODEL_TLV) +verify(len(result) == 2) +verify(result[1].startswith("Vendor Model:")) +verify(result[1].split(':')[1].strip() == r1.get_vendor_model()) + +# Get vendor sw version (TLV 29) + +result = r2.cli('networkdiagnostic get', r1_rloc, VENDOR_SW_VERSION_TLV) +verify(len(result) == 2) +verify(result[1].startswith("Vendor SW Version:")) +verify(result[1].split(':')[1].strip() == r1.get_vendor_sw_version()) + +# Get thread stack version (TLV 30) + +result = r2.cli('networkdiagnostic get', r1_rloc, THREAD_STACK_VERSION_TLV) +verify(len(result) == 2) +verify(result[1].startswith("Thread Stack Version:")) +verify(r1.get_version().startswith(result[1].split(':', 1)[1].strip())) + +# Get all three TLVs (now from `r1`) + +result = r1.cli('networkdiagnostic get', r2_rloc, VENDOR_NAME_TLV, VENDOR_MODEL_TLV, VENDOR_SW_VERSION_TLV, + THREAD_STACK_VERSION_TLV) +verify(len(result) == 5) +for line in result[1:]: + if line.startswith("Vendor Name:"): + verify(line.split(':')[1].strip() == r2.get_vendor_name()) + elif line.startswith("Vendor Model:"): + verify(line.split(':')[1].strip() == r2.get_vendor_model()) + elif line.startswith("Vendor SW Version:"): + verify(line.split(':')[1].strip() == r2.get_vendor_sw_version()) + elif line.startswith("Thread Stack Version:"): + verify(r2.get_version().startswith(line.split(':', 1)[1].strip())) + else: + verify(False) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-601-channel-manager-channel-change.py b/tests/toranj/cli/test-601-channel-manager-channel-change.py new file mode 100755 index 00000000000..cb67d64f675 --- /dev/null +++ b/tests/toranj/cli/test-601-channel-manager-channel-change.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: +# +# Verifies `ChannelManager` channel change process. +# +# Network Topology: +# +# r1 --- r2 --- r3 +# /\ | | +# / \ | | +# sc1 ec1 sc2 sc3 +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 20 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node() +r2 = cli.Node() +r3 = cli.Node() +sc1 = cli.Node() +ec1 = cli.Node() +sc2 = cli.Node() +sc3 = cli.Node() + +nodes = [r1, r2, r3, sc1, ec1, sc2, sc3] + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +r1.allowlist_node(r2) +r1.allowlist_node(sc1) +r1.allowlist_node(ec1) + +r2.allowlist_node(r1) +r2.allowlist_node(r3) +r2.allowlist_node(sc2) + +r3.allowlist_node(r2) +r3.allowlist_node(sc3) + +sc1.allowlist_node(r1) +ec1.allowlist_node(r1) +sc2.allowlist_node(r2) +sc3.allowlist_node(r3) + +r1.form('chan-man', channel=11) +r2.join(r1) +r3.join(r1) +sc1.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE) +ec1.join(r1, cli.JOIN_TYPE_END_DEVICE) +sc2.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE) +sc3.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE) + +sc1.set_pollperiod(500) +sc2.set_pollperiod(500) +sc3.set_pollperiod(500) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') +verify(r3.get_state() == 'router') +verify(ec1.get_state() == 'child') +verify(sc1.get_state() == 'child') +verify(sc2.get_state() == 'child') +verify(sc3.get_state() == 'child') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +result = cli.Node.parse_list(r1.cli('channel manager')) +verify(result['channel'] == '0') +verify(result['auto'] == '0') + +r1.cli('channel manager delay 4') +verify(int(r1.cli('channel manager delay')[0]) == 4) + +channel = 11 + + +def check_channel_on_all_nodes(): + verify(all(int(node.get_channel()) == channel for node in nodes)) + + +verify_within(check_channel_on_all_nodes, 10) + +# Request a channel change to channel 13 from router r1. Verify +# that all nodes switch to the new channel. + +channel = 13 +r1.cli('channel manager change', channel) +verify_within(check_channel_on_all_nodes, 10) + +# Request same channel change on multiple routers at the same time. + +channel = 14 +r1.cli('channel manager change', channel) +r2.cli('channel manager change', channel) +r3.cli('channel manager change', channel) +verify_within(check_channel_on_all_nodes, 10) + +# Request different channel changes from same router back-to-back. + +channel = 15 +r1.cli('channel manager change', channel) +time.sleep(1 / speedup) +channel = 16 +r1.cli('channel manager change', channel) +verify_within(check_channel_on_all_nodes, 10) + +# Request different channels from two routers (r1 and r2). +# We increase delay on r1 to make sure r1 is in middle of +# channel when new change is requested from r2. + +r1.cli('channel manager delay 20') +r1.cli('channel manager change 17') +time.sleep(5 / speedup) +verify_within(check_channel_on_all_nodes, 10) +channael = 18 +r2.cli('channel manager change', channel) +verify_within(check_channel_on_all_nodes, 10) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-602-channel-manager-channel-select.py b/tests/toranj/cli/test-602-channel-manager-channel-select.py new file mode 100755 index 00000000000..0ddf3584bff --- /dev/null +++ b/tests/toranj/cli/test-602-channel-manager-channel-select.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: +# +# Verifies `ChannelManager` channel selection procedure + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +# Run the test with 10,000 time speedup factor +speedup = 10000 +cli.Node.set_time_speedup_factor(speedup) + +node = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +node.form('chan-sel', channel=24) + +verify(node.get_state() == 'leader') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +channel = 24 + + +def check_channel(): + verify(int(node.get_channel()) == channel) + + +check_channel() + +all_channels_mask = int('0x7fff800', 0) +chan_12_to_15_mask = int('0x000f000', 0) +chan_15_to_17_mask = int('0x0038000', 0) + +# Set supported channel mask to be all channels +node.cli('channel manager supported', all_channels_mask) + +# Sleep for 4.5 second with speedup factor of 10,000 this is more than 12 +# hours. We sleep instead of immediately checking the sample counter in +# order to not add more actions/events into simulation (specially since +# we are running at very high speedup). +time.sleep(4.5) + +result = cli.Node.parse_list(node.cli('channel monitor')[:5]) +verify(result['enabled'] == '1') +verify(int(result['count']) > 970) + +# Issue a channel-select with quality check enabled, and verify that no +# action is taken. + +node.cli('channel manager select 0') +result = cli.Node.parse_list(node.cli('channel manager')) +verify(result['channel'] == '0') + +# Issue a channel-select with quality check disabled, verify that channel +# is switched to channel 11. + +node.cli('channel manager select 1') +result = cli.Node.parse_list(node.cli('channel manager')) +verify(result['channel'] == '11') +channel = 11 +verify_within(check_channel, 2) + +# Set channels 12-15 as favorable and request a channel select, verify +# that channel is switched to 12. +# +# Even though 11 would be best, quality difference between 11 and 12 +# is not high enough for selection algorithm to pick an unfavored +# channel. + +node.cli('channel manager favored', chan_12_to_15_mask) + +channel = 25 +node.cli('channel manager change', channel) +verify_within(check_channel, 2) + +node.cli('channel manager select 1') +result = cli.Node.parse_list(node.cli('channel manager')) +verify(result['channel'] == '12') +channel = 12 +verify_within(check_channel, 2) + +# Set channels 15-17 as favorables and request a channel select, +# verify that channel is switched to 11. +# +# This time the quality difference between 11 and 15 should be high +# enough for selection algorithm to pick the best though unfavored +# channel (i.e., channel 11). + +channel = 25 +node.cli('channel manager change', channel) +verify_within(check_channel, 2) + +node.cli('channel manager favored', chan_15_to_17_mask) + +node.cli('channel manager select 1') +result = cli.Node.parse_list(node.cli('channel manager')) +verify(result['channel'] == '11') +channel = 11 +verify_within(check_channel, 2) + +# Set channels 12-15 as favorable and request a channel select, verify +# that channel is not switched. + +node.cli('channel manager favored', chan_12_to_15_mask) + +node.cli('channel manager select 1') +result = cli.Node.parse_list(node.cli('channel manager')) +verify(result['channel'] == '11') +channel = 11 +verify_within(check_channel, 2) + +# Starting from channel 12 and issuing a channel select (which would +# pick 11 as best channel). However, since quality difference between +# current channel 12 and new best channel 11 is not large enough, no +# action should be taken. + +channel = 12 +node.cli('channel manager change', channel) +verify_within(check_channel, 2) + +node.cli('channel manager favored', all_channels_mask) + +node.cli('channel manager select 1') +result = cli.Node.parse_list(node.cli('channel manager')) +verify(result['channel'] == '12') +verify_within(check_channel, 2) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-603-channel-announce-recovery.py b/tests/toranj/cli/test-603-channel-announce-recovery.py new file mode 100755 index 00000000000..6482aa989e4 --- /dev/null +++ b/tests/toranj/cli/test-603-channel-announce-recovery.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: +# +# Orphaned node attach through MLE Announcement after channel change. + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 40 +cli.Node.set_time_speedup_factor(speedup) + +router = cli.Node() +c1 = cli.Node() +c2 = cli.Node() + +# ----------------------------------------------------------------------------------------------------------------------- +# Form topology + +router.form('announce-tst', channel=11) + +c1.join(router, cli.JOIN_TYPE_SLEEPY_END_DEVICE) +c2.join(router, cli.JOIN_TYPE_SLEEPY_END_DEVICE) +c1.set_pollperiod(500) +c2.set_pollperiod(500) + +verify(router.get_state() == 'leader') +verify(c1.get_state() == 'child') +verify(c2.get_state() == 'child') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +# Stop c2 + +c2.thread_stop() + +# Switch the rest of network to channel 26 +router.cli('channel manager change 26') + + +def check_channel_changed_to_26_on_r1_c1(): + for node in [router, c1]: + verify(int(node.get_channel()) == 26) + + +verify_within(check_channel_changed_to_26_on_r1_c1, 10) + +# Now re-enable c2 and verify that it does attach to router and is on +# channel 26. c2 would go through the ML Announce recovery. + +c2.thread_start() +verify(int(c2.get_channel()) == 11) + +# wait for 20s for c2 to be attached/associated + + +def check_c2_is_attched(): + verify(c2.get_state() == 'child') + + +verify_within(check_c2_is_attched, 20) + +# Check that c2 is now on channel 26. +verify(int(c2.get_channel()) == 26) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-700-multi-radio-join.py b/tests/toranj/cli/test-700-multi-radio-join.py new file mode 100755 index 00000000000..236a0f09d55 --- /dev/null +++ b/tests/toranj/cli/test-700-multi-radio-join.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: This test covers joining of nodes with different radio links +# +# Parent node with trel and 15.4 with 3 children. +# +# parent +# (trel + 15.4) +# / | \ +# / | \ +# / | \ +# c1 c2 c3 +# (15.4) (trel) (trel+15.4) + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +parent = cli.Node(cli.RADIO_15_4_TREL) +c1 = cli.Node(cli.RADIO_15_4) +c2 = cli.Node(cli.RADIO_TREL) +c3 = cli.Node(cli.RADIO_15_4_TREL) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +# Verify that each node supports the correct radio links. + +verify(parent.multiradio_get_radios() == '[15.4, TREL]') +verify(c1.multiradio_get_radios() == '[15.4]') +verify(c2.multiradio_get_radios() == '[TREL]') +verify(c3.multiradio_get_radios() == '[15.4, TREL]') + +parent.form("multi-radio") + +c1.join(parent, cli.JOIN_TYPE_END_DEVICE) +c2.join(parent, cli.JOIN_TYPE_END_DEVICE) +c3.join(parent, cli.JOIN_TYPE_END_DEVICE) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Verify that parent correctly learns about all the children and their +# supported radio links. + +c1_ext_addr = c1.get_ext_addr() +c2_ext_addr = c2.get_ext_addr() +c3_ext_addr = c3.get_ext_addr() + +neighbor_radios = parent.multiradio_get_neighbor_list() +verify(len(neighbor_radios) == 3) +for entry in neighbor_radios: + info = cli.Node.parse_multiradio_neighbor_entry(entry) + radios = info['Radios'] + if info['ExtAddr'] == c1_ext_addr: + verify(int(info['RLOC16'], 16) == int(c1.get_rloc16(), 16)) + verify(len(radios) == 1) + verify('15.4' in radios) + elif info['ExtAddr'] == c2_ext_addr: + verify(int(info['RLOC16'], 16) == int(c2.get_rloc16(), 16)) + verify(len(radios) == 1) + verify('TREL' in radios) + elif info['ExtAddr'] == c3_ext_addr: + verify(int(info['RLOC16'], 16) == int(c3.get_rloc16(), 16)) + verify(len(radios) == 2) + verify('15.4' in radios) + verify('TREL' in radios) + else: + verify(False) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Reset the parent and check that all children are attached back successfully + +del parent +parent = cli.Node(cli.RADIO_15_4_TREL, index=1) +parent.interface_up() +parent.thread_start() + + +def check_all_children_are_attached(): + verify(len(parent.get_child_table()) == 3) + + +verify_within(check_all_children_are_attached, 10) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-701-multi-radio-probe.py b/tests/toranj/cli/test-701-multi-radio-probe.py new file mode 100755 index 00000000000..bc0f128c91a --- /dev/null +++ b/tests/toranj/cli/test-701-multi-radio-probe.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: This test covers behavior of device after trel network is temporarily disabled +# and rediscovery of trel radio using probe mechanism. +# +# r1 ---------- r2 +# (15.4+trel) (15.4+trel) +# +# On r2 we disable trel temporarily. +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node(cli.RADIO_15_4_TREL) +r2 = cli.Node(cli.RADIO_15_4_TREL) + +# ----------------------------------------------------------------------------------------------------------------------- +# Build network topology + +r1.form("prove-discover") +r2.join(r1) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +high_preference_threshold = 220 +min_preference_threshold = 0 + +verify(r1.multiradio_get_radios() == '[15.4, TREL]') +verify(r2.multiradio_get_radios() == '[15.4, TREL]') + +r1_rloc = int(r1.get_rloc16(), 16) +r2_rloc = int(r2.get_rloc16(), 16) + +r1_ml_addr = r1.get_mleid_ip_addr() +r2_ml_addr = r2.get_mleid_ip_addr() + +# Wait till routes are discovered. + + +def check_r1_router_table(): + table = r1.get_router_table() + verify(len(table) == 2) + for entry in table: + verify(int(entry['RLOC16'], 0) == r1_rloc or int(entry['Link']) == 1) + + +verify_within(check_r1_router_table, 120) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Verify that r1 detected both TREL & 15.4 as supported radios by r2 + + +def check_r1_sees_r2_has_two_radio_links(): + neighbor_radios = r1.multiradio_get_neighbor_list() + verify(len(neighbor_radios) == 1) + info = cli.Node.parse_multiradio_neighbor_entry(neighbor_radios[0]) + verify(int(info['RLOC16'], 16) == r2_rloc) + radios = info['Radios'] + verify(len(radios) == 2) + verify('15.4' in radios) + verify('TREL' in radios) + + +cli.verify_within(check_r1_sees_r2_has_two_radio_links, 10) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Ping r2 from r1 and verify that r1 prefers trel radio link for +# sending to r2. + +r1.ping(r2_ml_addr, count=5) + +neighbor_radios = r1.multiradio_get_neighbor_list() +verify(len(neighbor_radios) == 1) +info = cli.Node.parse_multiradio_neighbor_entry(neighbor_radios[0]) +radios = info['Radios'] +verify(radios['TREL'] >= high_preference_threshold) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Now temporary filter trel link on r2 and ping again. We expect that +# r1 to quickly detect that trel is no longer supported by r2 and +# prefer 15.4 for tx to r2. + +r2.cli('trel filter enable') +verify(r2.cli('trel filter')[0] == 'Enabled') + +r1.udp_open() +for count in range(5): + r1.udp_send(r2_ml_addr, 12345, 'hi_r2_from_r1') + + +def check_r1_does_not_prefer_trel_for_r2(): + neighbor_radios = r1.multiradio_get_neighbor_list() + verify(len(neighbor_radios) == 1) + info = cli.Node.parse_multiradio_neighbor_entry(neighbor_radios[0]) + radios = info['Radios'] + verify(radios['TREL'] <= min_preference_threshold) + + +verify_within(check_r1_does_not_prefer_trel_for_r2, 10) + +# Check that we can send between r1 and r2 (now all tx should use 15.4) + +r1.ping(r2_ml_addr, count=5, verify_success=False) +r1.ping(r2_ml_addr, count=5) + +neighbor_radios = r1.multiradio_get_neighbor_list() +verify(len(neighbor_radios) == 1) +info = cli.Node.parse_multiradio_neighbor_entry(neighbor_radios[0]) +radios = info['Radios'] +verify(radios['TREL'] <= min_preference_threshold) +verify(radios['15.4'] >= high_preference_threshold) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Enable trel back on r2, start sending traffic from r1 to r2 +# The probe mechanism should kick and detect that r2 has trel again. + +r2.cli('trel filter disable') +verify(r2.cli('trel filter')[0] == 'Disabled') + +r2.udp_open() +for count in range(80): + r2.udp_send(r1_ml_addr, 12345, 'hi_r1_from_r2') + + +def check_r1_again_prefers_trel_for_r2(): + neighbor_radios = r1.multiradio_get_neighbor_list() + verify(len(neighbor_radios) == 1) + info = cli.Node.parse_multiradio_neighbor_entry(neighbor_radios[0]) + radios = info['Radios'] + verify(radios['TREL'] >= high_preference_threshold) + + +verify_within(check_r1_again_prefers_trel_for_r2, 10) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-702-multi-radio-discover-by-rx.py b/tests/toranj/cli/test-702-multi-radio-discover-by-rx.py new file mode 100755 index 00000000000..8e4a396c51f --- /dev/null +++ b/tests/toranj/cli/test-702-multi-radio-discover-by-rx.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: This test covers behavior of device after TREL network is temporarily disabled +# and rediscovery of TREL by receiving message over that radio from the neighbor. +# +# r1 ---------- r2 +# (15.4+trel) (15.4+trel) +# +# On r2 we disable trel temporarily. +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node(cli.RADIO_15_4_TREL) +r2 = cli.Node(cli.RADIO_15_4_TREL) + +# ----------------------------------------------------------------------------------------------------------------------- +# Build network topology + +r1.form("discover-by-rx") +r2.join(r1) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +high_preference_threshold = 220 +min_preference_threshold = 0 + +verify(r1.multiradio_get_radios() == '[15.4, TREL]') +verify(r2.multiradio_get_radios() == '[15.4, TREL]') + +r1_rloc = int(r1.get_rloc16(), 16) +r2_rloc = int(r2.get_rloc16(), 16) + +r1_ml_addr = r1.get_mleid_ip_addr() +r2_ml_addr = r2.get_mleid_ip_addr() + +# Wait till routes are discovered. + + +def check_r1_router_table(): + table = r1.get_router_table() + verify(len(table) == 2) + for entry in table: + verify(int(entry['RLOC16'], 0) == r1_rloc or int(entry['Link']) == 1) + + +verify_within(check_r1_router_table, 120) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Verify that r1 detected both TREL & 15.4 as supported radios by r2 + + +def check_r1_sees_r2_has_two_radio_links(): + neighbor_radios = r1.multiradio_get_neighbor_list() + verify(len(neighbor_radios) == 1) + info = cli.Node.parse_multiradio_neighbor_entry(neighbor_radios[0]) + verify(int(info['RLOC16'], 16) == r2_rloc) + radios = info['Radios'] + verify(len(radios) == 2) + verify('15.4' in radios) + verify('TREL' in radios) + + +cli.verify_within(check_r1_sees_r2_has_two_radio_links, 10) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Ping r2 from r1 and verify that r1 prefers trel radio link for +# sending to r2. + +r1.ping(r2_ml_addr, count=5) + +neighbor_radios = r1.multiradio_get_neighbor_list() +verify(len(neighbor_radios) == 1) +info = cli.Node.parse_multiradio_neighbor_entry(neighbor_radios[0]) +radios = info['Radios'] +verify(radios['TREL'] >= high_preference_threshold) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Now temporary filter trel link on r2 and ping again. We expect that +# r1 to quickly detect that trel is no longer supported by r2 and +# prefer 15.4 for tx to r2. + +r2.cli('trel filter enable') +verify(r2.cli('trel filter')[0] == 'Enabled') + +r1.udp_open() +for count in range(5): + r1.udp_send(r2.get_mleid_ip_addr(), 12345, 'hi_r2_from_r1') + + +def check_r1_does_not_prefer_trel_for_r2(): + neighbor_radios = r1.multiradio_get_neighbor_list() + verify(len(neighbor_radios) == 1) + info = cli.Node.parse_multiradio_neighbor_entry(neighbor_radios[0]) + radios = info['Radios'] + verify(radios['TREL'] <= min_preference_threshold) + + +verify_within(check_r1_does_not_prefer_trel_for_r2, 10) + +# Check that we can send between r1 and r2 (now all tx should use 15.4) + +r1.ping(r2.get_mleid_ip_addr(), count=5, verify_success=False) +r1.ping(r2.get_mleid_ip_addr(), count=5) + +neighbor_radios = r1.multiradio_get_neighbor_list() +verify(len(neighbor_radios) == 1) +info = cli.Node.parse_multiradio_neighbor_entry(neighbor_radios[0]) +radios = info['Radios'] +verify(radios['TREL'] <= min_preference_threshold) +verify(radios['15.4'] >= high_preference_threshold) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Enable trel back on r2, start sending traffic from r2 to r1. +# r2 would use probe mechanism to discover that trel is enabled again +# r1 should notice new rx on trel and update trel preference for r1 + +r2.cli('trel filter disable') +verify(r2.cli('trel filter')[0] == 'Disabled') + +for count in range(80): + r1.udp_send(r2.get_mleid_ip_addr(), 12345, 'hi_r2_from_r1_probe') + + +def check_r1_again_prefers_trel_for_r2(): + neighbor_radios = r1.multiradio_get_neighbor_list() + verify(len(neighbor_radios) == 1) + info = cli.Node.parse_multiradio_neighbor_entry(neighbor_radios[0]) + radios = info['Radios'] + verify(radios['TREL'] >= high_preference_threshold) + + +verify_within(check_r1_again_prefers_trel_for_r2, 10) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-703-multi-radio-mesh-header-msg.py b/tests/toranj/cli/test-703-multi-radio-mesh-header-msg.py new file mode 100755 index 00000000000..d20286734e5 --- /dev/null +++ b/tests/toranj/cli/test-703-multi-radio-mesh-header-msg.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: This test covers behavior of MeshHeader messages (message sent over multi-hop) when the +# underlying devices (in the path) support different radio types with different frame MTU length. +# +# r1 --------- r2 ---------- r3 +# (trel) (15.4+trel) (15.4) +# | +# | +# c2 (15.4) +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +r1 = cli.Node(cli.RADIO_TREL) +r2 = cli.Node(cli.RADIO_15_4_TREL) +r3 = cli.Node(cli.RADIO_15_4) +c2 = cli.Node(cli.RADIO_15_4) + +# ----------------------------------------------------------------------------------------------------------------------- +# Build network topology + +r1.allowlist_node(r2) + +r2.allowlist_node(r1) +r2.allowlist_node(r3) +r2.allowlist_node(c2) + +r3.allowlist_node(r2) + +c2.allowlist_node(r2) + +r1.form("mesh-header") + +r2.join(r1) +r3.join(r2) +c2.join(r2, cli.JOIN_TYPE_END_DEVICE) + +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') +verify(r3.get_state() == 'router') +verify(c2.get_state() == 'child') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +verify(r1.multiradio_get_radios() == '[TREL]') +verify(r2.multiradio_get_radios() == '[15.4, TREL]') +verify(r3.multiradio_get_radios() == '[15.4]') + +r1_rloc = int(r1.get_rloc16(), 16) + +r1_ml_addr = r1.get_mleid_ip_addr() +r3_ml_addr = r3.get_mleid_ip_addr() + +# Wait till routes are discovered. + + +def check_r1_router_table(): + table = r1.get_router_table() + verify(len(table) == 3) + for entry in table: + verify(int(entry['RLOC16'], 0) == r1_rloc or int(entry['Link']) == 1 or int(entry['Next Hop']) != 63) + + +verify_within(check_r1_router_table, 120) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Ping r3 from r2 using large message size. Such a message would be sent as a +# Mesh Header message. Note that the origin r1 is a trel-only device +# whereas the final destination r3 is a 15.4-only device. The originator +# should use smaller MTU size (which then fragment the message into +# multiple frames) otherwise the 15.4 link won't be able to handle it. + +r1.ping(r3_ml_addr, size=1000, count=2) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Ping r1 from c2 using large message size. + +c2.ping(r1_ml_addr, size=1000, count=2) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Ping r1 from r3 + +r3.ping(r1_ml_addr, size=1000, count=2) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Finally ping r1 and r3 from r2. + +r2.ping(r1_ml_addr, size=1000, count=2) +r2.ping(r3_ml_addr, size=1000, count=2) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-704-multi-radio-scan.py b/tests/toranj/cli/test-704-multi-radio-scan.py new file mode 100755 index 00000000000..697f08dd81c --- /dev/null +++ b/tests/toranj/cli/test-704-multi-radio-scan.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: Test active scan with nodes supporting different radios +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +n1 = cli.Node(cli.RADIO_15_4) +n2 = cli.Node(cli.RADIO_TREL) +n3 = cli.Node(cli.RADIO_15_4_TREL) +s1 = cli.Node(cli.RADIO_15_4) +s2 = cli.Node(cli.RADIO_TREL) +s3 = cli.Node(cli.RADIO_15_4_TREL) + +# ----------------------------------------------------------------------------------------------------------------------- +# Build network topology + +n1.form("n1", channel='20') +n2.form("n2", channel='21') +n3.form("n3", channel='22') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +verify(n1.multiradio_get_radios() == '[15.4]') +verify(n2.multiradio_get_radios() == '[TREL]') +verify(n3.multiradio_get_radios() == '[15.4, TREL]') +verify(s1.multiradio_get_radios() == '[15.4]') +verify(s2.multiradio_get_radios() == '[TREL]') +verify(s3.multiradio_get_radios() == '[15.4, TREL]') + + +def verify_scan_result_conatins_nodes(scan_result, nodes): + table = cli.Node.parse_table(scan_result) + verify(len(table) >= len(nodes)) + for node in nodes: + ext_addr = node.get_ext_addr() + for entry in table: + if entry['MAC Address'] == ext_addr: + verify(int(entry['PAN'], 16) == int(node.get_panid(), 16)) + verify(int(entry['Ch']) == int(node.get_channel())) + break + else: + verify(False) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Scan by scanner nodes (no network) + +# Scan by s1 (15.4 only), expect to see n1(15.4) and n3(15.4+trel) + +verify_scan_result_conatins_nodes(s1.cli('scan'), [n1, n3]) + +# Scan by s2 (trel only), expect to see n2(trel) and n3(15.4+trel) +verify_scan_result_conatins_nodes(s2.cli('scan'), [n2, n3]) + +# Scan by s3 (trel+15.4), expect to see all nodes +verify_scan_result_conatins_nodes(s3.cli('scan'), [n1, n2, n3]) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/cli/test-705-multi-radio-discover-scan.py b/tests/toranj/cli/test-705-multi-radio-discover-scan.py new file mode 100755 index 00000000000..4206280c866 --- /dev/null +++ b/tests/toranj/cli/test-705-multi-radio-discover-scan.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 +# +# 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. + +from cli import verify +from cli import verify_within +import cli +import time + +# ----------------------------------------------------------------------------------------------------------------------- +# Test description: Test MLE discover scan with nodes supporting different radios +# + +test_name = __file__[:-3] if __file__.endswith('.py') else __file__ +print('-' * 120) +print('Starting \'{}\''.format(test_name)) + +# ----------------------------------------------------------------------------------------------------------------------- +# Creating `cli.Node` instances + +speedup = 10 +cli.Node.set_time_speedup_factor(speedup) + +n1 = cli.Node(cli.RADIO_15_4) +n2 = cli.Node(cli.RADIO_TREL) +n3 = cli.Node(cli.RADIO_15_4_TREL) +s1 = cli.Node(cli.RADIO_15_4) +s2 = cli.Node(cli.RADIO_TREL) +s3 = cli.Node(cli.RADIO_15_4_TREL) + +# ----------------------------------------------------------------------------------------------------------------------- +# Build network topology + +n1.form("n1", channel='20') +n2.form("n2", channel='21') +n3.form("n3", channel='22') + +# ----------------------------------------------------------------------------------------------------------------------- +# Test Implementation + +verify(n1.multiradio_get_radios() == '[15.4]') +verify(n2.multiradio_get_radios() == '[TREL]') +verify(n3.multiradio_get_radios() == '[15.4, TREL]') +verify(s1.multiradio_get_radios() == '[15.4]') +verify(s2.multiradio_get_radios() == '[TREL]') +verify(s3.multiradio_get_radios() == '[15.4, TREL]') + + +def verify_scan_result_conatins_nodes(scan_result, nodes): + table = cli.Node.parse_table(scan_result) + verify(len(table) >= len(nodes)) + for node in nodes: + ext_addr = node.get_ext_addr() + for entry in table: + if entry['MAC Address'] == ext_addr: + verify(int(entry['PAN'], 16) == int(node.get_panid(), 16)) + verify(int(entry['Ch']) == int(node.get_channel())) + break + else: + verify(False) + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Scan by scanner nodes (no network) + +s1.interface_up() +s2.interface_up() +s3.interface_up() + +# Scan by s1 (15.4 only), expect to see n1(15.4) and n3(15.4+trel) + +verify_scan_result_conatins_nodes(s1.cli('discover'), [n1, n3]) + +# Scan by s2 (trel only), expect to see n2(trel) and n3(15.4+trel) +verify_scan_result_conatins_nodes(s2.cli('discover'), [n2, n3]) + +# Scan by s3 (trel+15.4), expect to see all nodes +verify_scan_result_conatins_nodes(s3.cli('discover'), [n1, n2, n3]) + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +# Scan by the nodes + +# Scan by n1 (15.4 only), expect to see only n3(15.4+trel) +verify_scan_result_conatins_nodes(n1.cli('discover'), [n3]) + +# Scan by n2 (trel only), expect to see only n3(15.4+trel) +verify_scan_result_conatins_nodes(n2.cli('discover'), [n3]) + +# Scan by n3 (15.4+trel), expect to see n1(15.4) and n2(trel) +verify_scan_result_conatins_nodes(n3.cli('discover'), [n1, n2]) + +# ----------------------------------------------------------------------------------------------------------------------- +# Test finished + +cli.Node.finalize_all_nodes() + +print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/ncp/test-017-parent-reset-child-recovery.py b/tests/toranj/ncp/test-017-parent-reset-child-recovery.py index ede43dccf86..e6ff9342abf 100644 --- a/tests/toranj/ncp/test-017-parent-reset-child-recovery.py +++ b/tests/toranj/ncp/test-017-parent-reset-child-recovery.py @@ -148,7 +148,7 @@ def check_parent_is_associated(): verify(parent.is_associated()) -wpan.verify_within(check_parent_is_associated, 10) +wpan.verify_within(check_parent_is_associated, 40) # Verify that all the children are recovered and present in the parent's # child table again. diff --git a/tests/toranj/ncp/test-018-child-supervision.py b/tests/toranj/ncp/test-018-child-supervision.py deleted file mode 100644 index 2fe27673974..00000000000 --- a/tests/toranj/ncp/test-018-child-supervision.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (c) 2018, The OpenThread Authors. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. Neither the name of the copyright holder nor the -# names of its contributors may be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. - -import time -import wpan -from wpan import verify - -# ----------------------------------------------------------------------------------------------------------------------- -# Test description: Child Supervision feature -# -# This test covers the behavior of Child Supervision feature. -# -# This test uses MAC allowlisting to emulate the situation where a child is -# removed from parent's child table while the child continues to stay attached -# to the parent (since data polls from child are acked at radio platform layer). -# Specifically the test verifies that once supervision check is enabled on the -# child, the child detects that it is no longer present in the parent's table -# and tries to re-attach. - -# The test verifies the behavior of both parent and child, when supervision is -# enabled. It verifies that parent is periodically sending supervision messages -# to the child and that the child is monitoring the messages. -# -# This test also indirectly verifies the child timeout on parent. -# - -test_name = __file__[:-3] if __file__.endswith('.py') else __file__ -print('-' * 120) -print('Starting \'{}\''.format(test_name)) - -# ----------------------------------------------------------------------------------------------------------------------- -# Creating `wpan.Nodes` instances - -speedup = 2 -wpan.Node.set_time_speedup_factor(speedup) - -parent = wpan.Node() -child = wpan.Node() - -# ----------------------------------------------------------------------------------------------------------------------- -# Init all nodes - -wpan.Node.init_all_nodes() - -# ----------------------------------------------------------------------------------------------------------------------- -# Build network topology - -CHILD_TIMEOUT = 6 -CHILD_SUPERVISION_CHECK_TIMEOUT = 2 -PARENT_SUPERVISION_INTERVAL = 1 - -child.set(wpan.WPAN_POLL_INTERVAL, '500') -child.set(wpan.WPAN_THREAD_CHILD_TIMEOUT, str(CHILD_TIMEOUT)) - -parent.form("child-sup") -child.join_node(parent, wpan.JOIN_TYPE_SLEEPY_END_DEVICE) - -# ----------------------------------------------------------------------------------------------------------------------- -# Test implementation - -# Disable child supervision on child and parent -parent.set(wpan.WPAN_CHILD_SUPERVISION_INTERVAL, '0') -child.set(wpan.WPAN_CHILD_SUPERVISION_CHECK_TIMEOUT, '0') -verify(int(parent.get(wpan.WPAN_CHILD_SUPERVISION_INTERVAL), 0) == 0) -verify(int(child.get(wpan.WPAN_CHILD_SUPERVISION_CHECK_TIMEOUT), 0) == 0) - -# Check that the child is associated and has correct timeout -verify(child.is_associated()) -verify(int(child.get(wpan.WPAN_THREAD_CHILD_TIMEOUT), 0) == CHILD_TIMEOUT) - -# Verify the child table on parent contains the child with correct timeout -child_table = wpan.parse_child_table_result(parent.get(wpan.WPAN_THREAD_CHILD_TABLE)) -verify(len(child_table) == 1) -verify(int(child_table[0].timeout, 0) == CHILD_TIMEOUT) - -time.sleep(1) - -# Enabling allowlisting on parent -# -# Since child is not in parent's allowlist, the data polls from child -# should be rejected and the child should be removed from parent's -# child table after timeout. The child however should continue to -# stay attached (since data polls are acked by radio driver) and -# supervision check is disabled on the child. - -parent.set(wpan.WPAN_MAC_ALLOWLIST_ENABLED, '1') - - -def check_child_is_removed_from_parent_child_table(): - child_table = wpan.parse_child_table_result(parent.get(wpan.WPAN_THREAD_CHILD_TABLE)) - verify(len(child_table) == 0) - - -# wait till child is removed from parent's child table -# after this child should still be associated -wpan.verify_within(check_child_is_removed_from_parent_child_table, CHILD_TIMEOUT / speedup + 2) -verify(child.is_associated()) - -# Enable supervision check on child and expect the child to -# become detached after the check timeout - -child.set( - wpan.WPAN_CHILD_SUPERVISION_CHECK_TIMEOUT, - str(CHILD_SUPERVISION_CHECK_TIMEOUT), -) - - -def check_child_is_detached(): - verify(not child.is_associated()) - - -wpan.verify_within(check_child_is_detached, CHILD_SUPERVISION_CHECK_TIMEOUT / speedup + 8) - -# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -# Enable child supervision on parent and disable allowlisting - -parent.set(wpan.WPAN_CHILD_SUPERVISION_INTERVAL, str(PARENT_SUPERVISION_INTERVAL)) -parent.set(wpan.WPAN_MAC_ALLOWLIST_ENABLED, '0') - -# Wait for the child to attach back - - -def check_child_is_attached(): - verify(child.is_associated()) - - -wpan.verify_within(check_child_is_attached, 5) - -# MAC counters are used to verify the child supervision behavior. - -parent_unicast_tx_count = int(parent.get("NCP:Counter:TX_PKT_UNICAST"), 0) - -time.sleep(PARENT_SUPERVISION_INTERVAL * 1.2 / speedup) - -# To verify that the parent is indeed sending empty "supervision" -# messages to its child, MAC counter for number of unicast tx is -# used. Note that supervision interval on parent is set to 1 sec. - -verify(int(parent.get("NCP:Counter:TX_PKT_UNICAST"), 0) >= parent_unicast_tx_count + 1) - -verify(child.is_associated()) - -# Disable child supervision on parent -parent.set(wpan.WPAN_CHILD_SUPERVISION_INTERVAL, '0') - -time.sleep(CHILD_SUPERVISION_CHECK_TIMEOUT * 3 / speedup) -verify(child.is_associated()) - -# ----------------------------------------------------------------------------------------------------------------------- -# Test finished - -wpan.Node.finalize_all_nodes() - -print('\'{}\' passed.'.format(test_name)) diff --git a/tests/toranj/ncp/test-027-child-mode-change.py b/tests/toranj/ncp/test-027-child-mode-change.py index b3c92c6e8f1..8301cd8aa0a 100644 --- a/tests/toranj/ncp/test-027-child-mode-change.py +++ b/tests/toranj/ncp/test-027-child-mode-change.py @@ -97,6 +97,7 @@ def verify_child_table(parent, children): # Test implementation WAIT_INTERVAL = 6 +LEADER_RESET_DELAY = 38 # Thread Mode for end-device and sleepy end-device DEVICE_MODE_SLEEPY_END_DEVICE = (wpan.THREAD_MODE_FLAG_FULL_NETWORK_DATA) @@ -127,8 +128,6 @@ def check_child_table(): sender = parent.prepare_tx(parent_ml_address, child2_ml_address, 800, NUM_MSGS) -child2_rx_ip_counter = int(child2.get(wpan.WPAN_NCP_COUNTER_RX_IP_SEC_TOTAL), 0) - wpan.Node.perform_async_tx_rx() verify(sender.was_successful) @@ -144,16 +143,9 @@ def check_child_table(): # Verify that the child table on parent is also updated wpan.verify_within(check_child_table, WAIT_INTERVAL) - -def check_child2_received_msg(): - verify(int(child2.get(wpan.WPAN_NCP_COUNTER_RX_IP_SEC_TOTAL), 0) >= child2_rx_ip_counter + NUM_MSGS) - - -wpan.verify_within(check_child2_received_msg, WAIT_INTERVAL) - # Reset parent and verify all children are recovered parent.reset() -wpan.verify_within(check_child_table, WAIT_INTERVAL) +wpan.verify_within(check_child_table, WAIT_INTERVAL + LEADER_RESET_DELAY) # ----------------------------------------------------------------------------------------------------------------------- # Test finished diff --git a/tests/toranj/ncp/test-600-channel-manager-properties.py b/tests/toranj/ncp/test-600-channel-manager-properties.py index a222d09b181..c99ec38633f 100644 --- a/tests/toranj/ncp/test-600-channel-manager-properties.py +++ b/tests/toranj/ncp/test-600-channel-manager-properties.py @@ -101,7 +101,7 @@ node.reset() start_time = time.time() -wait_time = 20 +wait_time = 50 while node.get(wpan.WPAN_STATE) != wpan.STATE_ASSOCIATED: if time.time() - start_time > wait_time: diff --git a/tests/toranj/openthread-core-toranj-config-simulation.h b/tests/toranj/openthread-core-toranj-config-simulation.h index 09d925d08b3..04d6d0e566c 100644 --- a/tests/toranj/openthread-core-toranj-config-simulation.h +++ b/tests/toranj/openthread-core-toranj-config-simulation.h @@ -65,7 +65,23 @@ * Selects if, and where the LOG output goes to. * */ -#define OPENTHREAD_CONFIG_LOG_OUTPUT OPENTHREAD_CONFIG_LOG_OUTPUT_APP +#define OPENTHREAD_CONFIG_LOG_OUTPUT OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED + +/** + * @def OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE + * + * Define as 1 for CLI to emit its command input string and the resulting output to the logs. + * + */ +#define OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_ENABLE 1 + +/** + * @def OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL + * + * Defines the log level to use when CLI emits its command input/output to the logs. + * + */ +#define OPENTHREAD_CONFIG_CLI_LOG_INPUT_OUTPUT_LEVEL OT_LOG_LEVEL_INFO /** * @def OPENTHREAD_CONFIG_DNS_DSO_ENABLE @@ -75,4 +91,12 @@ */ #define OPENTHREAD_CONFIG_DNS_DSO_ENABLE 1 +/** + * @def OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE + * + * Enable the external heap. + * + */ +#define OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE 1 + #endif /* OPENTHREAD_CORE_TORANJ_CONFIG_SIMULATION_H_ */ diff --git a/tests/toranj/openthread-core-toranj-config.h b/tests/toranj/openthread-core-toranj-config.h index 508f605533d..1812c81a4f4 100644 --- a/tests/toranj/openthread-core-toranj-config.h +++ b/tests/toranj/openthread-core-toranj-config.h @@ -75,6 +75,22 @@ */ #define OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE 1 +/** + * @def OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE + * + * Define as 1 to enable IPv6 Border Routing counters. + * + */ +#define OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE 1 + +/** + * @def OPENTHREAD_CONFIG_MESH_DIAG_ENABLE + * + * Define as 1 to enable Mesh Diagnostics module. + * + */ +#define OPENTHREAD_CONFIG_MESH_DIAG_ENABLE 1 + /** * @def OPENTHREAD_CONFIG_COMMISSIONER_ENABLE * @@ -139,14 +155,6 @@ */ #define OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE 1 -/** - * @def OPENTHREAD_CONFIG_LEGACY_ENABLE - * - * Define to 1 to enable legacy network support. - * - */ -#define OPENTHREAD_CONFIG_LEGACY_ENABLE 1 - /** * @def OPENTHREAD_CONFIG_ECDSA_ENABLE * @@ -435,14 +443,6 @@ */ #define OPENTHREAD_CONFIG_CHANNEL_MANAGER_THRESHOLD_TO_CHANGE_CHANNEL (0xffff * 10 / 100) -/** - * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE - * - * Define to 1 to enable Child Supervision support. - * - */ -#define OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE 1 - /** * @def OPENTHREAD_CONFIG_TMF_PENDING_DATASET_MINIMUM_DELAY * @@ -538,6 +538,71 @@ */ #define OPENTHREAD_CONFIG_DELAY_AWARE_QUEUE_MANAGEMENT_ENABLE 1 +/** + * @def OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE + * + * Define to 1 to enable Backbone Router support. + * + */ +#define OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE 1 + +/** + * @def OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK + * + * Define as 1 to have CLI register an IPv6 receive callback using `otIp6SetReceiveCallback()`. + * + * This is intended for testing only. Receive callback should be registered for the `otIp6GetBorderRoutingCounters()` + * to count the messages being passed to the callback. + * + */ +#define OPENTHREAD_CONFIG_CLI_REGISTER_IP6_RECV_CALLBACK 1 + +/** + * @def OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE + * + * Define as 1 to support `otThreadRegisterParentResponseCallback()` API which registers a callback to notify user + * of received Parent Response message(s) during attach. + * + */ +#define OPENTHREAD_CONFIG_MLE_PARENT_RESPONSE_CALLBACK_API_ENABLE 1 + +/** + * @def OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME + * + * Specifies the default Vendor Name string. + * + */ +#ifndef OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME +#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_NAME "OpenThread by Google Nest" +#endif + +/** + * @def OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL + * + * Specifies the default Vendor Model string. + * + */ +#ifndef OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL +#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_MODEL "Toranj Simulation" +#endif + +/** + * @def OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION + * + * Specifies the default Vendor SW Version string. + * + */ +#ifndef OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION +#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_SW_VERSION "OT-simul-toranj" +#endif + +/** + * @def OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE + * + * Define as 1 to add APIs to allow Vendor Name, Model, SW Version to change at run-time. + */ +#define OPENTHREAD_CONFIG_NET_DIAG_VENDOR_INFO_SET_API_ENABLE OPENTHREAD_FTD + #if OPENTHREAD_RADIO /** * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE diff --git a/tests/toranj/start.sh b/tests/toranj/start.sh index 3831afc2baa..2bc8fa996ec 100755 --- a/tests/toranj/start.sh +++ b/tests/toranj/start.sh @@ -39,32 +39,18 @@ cleanup() sudo rm tmp/*.flash tmp/*.data tmp/*.swap >/dev/null 2>&1 sudo rm ./*.log >/dev/null 2>&1 - # Clear any wpantund instances - sudo killall wpantund >/dev/null 2>&1 + if [ "$TORANJ_CLI" != 1 ]; then + # Clear any wpantund instances + sudo killall wpantund >/dev/null 2>&1 - while read -r interface; do - sudo ip link delete "$interface" >/dev/null 2>&1 - done < <(ifconfig 2>/dev/null | grep -o "wpan[0-9]*") - - sudo ip address flush trel + while read -r interface; do + sudo ip link delete "$interface" >/dev/null 2>&1 + done < <(ifconfig 2>/dev/null | grep -o "wpan[0-9]*") + fi sleep 0.3 } -prepare_trel_link() -{ - # Prepares a netif "trel" as virtual eth to use for - # testing "TREL" radio link in POSIX platform. - - echo "Preparing trel netif" - sudo ip link delete trel - sudo ip link add trel type veth peer name trel-peer || die - sudo ip link set trel multicast on || die - sudo ip link set trel up || die - sudo ip link set trel-peer multicast on || die - sudo ip link set trel-peer up || die -} - run() { counter=0 @@ -89,8 +75,52 @@ run() done } +install_wpantund() +{ + echo "Installing wpantund" + sudo apt-get --no-install-recommends install -y dbus libdbus-1-dev + sudo apt-get --no-install-recommends install -y autoconf-archive + sudo apt-get --no-install-recommends install -y libarchive-tools + sudo apt-get --no-install-recommends install -y libtool + sudo apt-get --no-install-recommends install -y libglib2.0-dev + sudo apt-get --no-install-recommends install -y lcov + + sudo add-apt-repository universe + sudo apt-get update + sudo apt-get --no-install-recommends install -y libboost-all-dev python2 + + git clone --depth=1 --branch=master https://github.com/openthread/wpantund.git || die "wpandtund clone" + cd wpantund || die "cd wpantund failed" + ./bootstrap.sh + ./configure + sudo make -j2 || die "wpantund make failed" + sudo make install || die "wpantund make install failed" + cd .. || die "cd .. failed" +} + cd "$(dirname "$0")" || die "cd failed" +if [ "$TORANJ_NCP" = 1 ]; then + echo "========================================================================" + echo "Running toranj-ncp triggered by event ${TORANJ_EVENT_NAME}" + + if [ "$TORANJ_EVENT_NAME" = "pull_request" ]; then + cd ../.. + OT_SHA_OLD="$(git cat-file -p HEAD | grep 'parent ' | head -n1 | cut -d' ' -f2)" + git fetch --depth 1 --no-recurse-submodules origin "${OT_SHA_OLD}" + if git diff --name-only --exit-code "${OT_SHA_OLD}" -- src/ncp; then + echo "No changes to any of src/ncp files - skip running tests." + echo "========================================================================" + exit 0 + fi + cd tests/toranj || die "cd tests/toranj failed" + echo "There are change in src/ncp files, running toranj-ncp tests" + fi + + echo "========================================================================" + install_wpantund +fi + if [ -z "${top_builddir}" ]; then top_builddir=. fi @@ -107,18 +137,15 @@ if [ "$TORANJ_CLI" = 1 ]; then log_file_name="ot-logs" else app_name="ncp" - python_app="python" + python_app="python2" log_file_name="wpantund-logs" fi if [ "$TORANJ_RADIO" = "multi" ]; then # Build all combinations ./build.sh "${coverage_option}" "${app_name}"-15.4 || die "${app_name}-15.4 build failed" - (cd ${top_builddir} && make clean) || die "cd and clean failed" ./build.sh "${coverage_option}" "${app_name}"-trel || die "${app_name}-trel build failed" - (cd ${top_builddir} && make clean) || die "cd and clean failed" ./build.sh "${coverage_option}" "${app_name}"-15.4+trel || die "${app_name}-15.4+trel build failed" - (cd ${top_builddir} && make clean) || die "cd and clean failed" else ./build.sh "${coverage_option}" "${app_name}"-"${TORANJ_RADIO}" || die "build failed" fi @@ -126,10 +153,46 @@ fi cleanup if [ "$TORANJ_CLI" = 1 ]; then + + if [ "$TORANJ_RADIO" = "multi" ]; then + run cli/test-700-multi-radio-join.py + run cli/test-701-multi-radio-probe.py + run cli/test-702-multi-radio-discover-by-rx.py + run cli/test-703-multi-radio-mesh-header-msg.py + run cli/test-704-multi-radio-scan.py + run cli/test-705-multi-radio-discover-scan.py + + exit 0 + fi + run cli/test-001-get-set.py run cli/test-002-form.py run cli/test-003-join.py + run cli/test-004-scan.py + run cli/test-005-traffic-router-to-child.py + run cli/test-006-traffic-multi-hop.py + run cli/test-007-off-mesh-route-traffic.py + run cli/test-008-multicast-traffic.py + run cli/test-009-router-table.py + run cli/test-010-partition-merge.py + run cli/test-011-network-data-timeout.py + run cli/test-012-reset-recovery.py + run cli/test-013-address-cache-parent-switch.py + run cli/test-014-address-resolver.py + run cli/test-015-clear-addresss-cache-for-sed.py + run cli/test-016-child-mode-change.py + run cli/test-017-network-data-versions.py + run cli/test-018-next-hop-and-path-cost.py + run cli/test-019-netdata-context-id.py + run cli/test-020-net-diag-vendor-info.py run cli/test-400-srp-client-server.py + run cli/test-601-channel-manager-channel-change.py + # Skip the "channel-select" test on a TREL only radio link, since it + # requires energy scan which is not supported in this case. + if [ "$TORANJ_RADIO" != "trel" ]; then + run cli/test-602-channel-manager-channel-select.py + fi + run cli/test-603-channel-announce-recovery.py exit 0 fi @@ -161,7 +224,6 @@ run ncp/test-014-ip6-address-add.py run ncp/test-015-same-prefix-on-multiple-nodes.py run ncp/test-016-neighbor-table.py run ncp/test-017-parent-reset-child-recovery.py -run ncp/test-018-child-supervision.py run ncp/test-019-inform-previous-parent.py run ncp/test-020-router-table.py run ncp/test-021-address-cache-table.py diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 9a68ecfcf57..ca963d24738 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -30,7 +30,6 @@ set(COMMON_INCLUDES ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/core - ${PROJECT_SOURCE_DIR}/examples/platforms/simulation ) set(COMMON_COMPILE_OPTIONS @@ -60,11 +59,14 @@ target_link_libraries(ot-test-platform ) set(COMMON_LIBS + openthread-spinel-ncp + openthread-hdlc ot-test-platform openthread-ftd ot-test-platform ${OT_MBEDTLS} ot-config + openthread-ftd ) add_executable(ot-test-aes @@ -255,6 +257,27 @@ target_link_libraries(ot-test-dns add_test(NAME ot-test-dns COMMAND ot-test-dns) +add_executable(ot-test-dns-client + test_dns_client.cpp +) + +target_include_directories(ot-test-dns-client + PRIVATE + ${COMMON_INCLUDES} +) + +target_compile_options(ot-test-dns-client + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) + +target_link_libraries(ot-test-dns-client + PRIVATE + ${COMMON_LIBS} +) + +add_test(NAME ot-test-dns-client COMMAND ot-test-dns-client) + add_executable(ot-test-dso test_dso.cpp ) @@ -662,6 +685,27 @@ target_link_libraries(ot-test-message-queue add_test(NAME ot-test-message-queue COMMAND ot-test-message-queue) +add_executable(ot-test-mle + test_mle.cpp +) + +target_include_directories(ot-test-mle + PRIVATE + ${COMMON_INCLUDES} +) + +target_compile_options(ot-test-mle + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) + +target_link_libraries(ot-test-mle + PRIVATE + ${COMMON_LIBS} +) + +add_test(NAME ot-test-mle COMMAND ot-test-mle) + add_executable(ot-test-multicast-listeners-table test_multicast_listeners_table.cpp ) @@ -683,6 +727,27 @@ target_link_libraries(ot-test-multicast-listeners-table add_test(NAME ot-test-multicast-listeners-table COMMAND ot-test-multicast-listeners-table) +add_test(NAME ot-test-nat64 COMMAND ot-test-nat64) + +add_executable(ot-test-nat64 + test_nat64.cpp +) + +target_include_directories(ot-test-nat64 + PRIVATE + ${COMMON_INCLUDES} +) + +target_compile_options(ot-test-nat64 + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) + +target_link_libraries(ot-test-nat64 + PRIVATE + ${COMMON_LIBS} +) + add_executable(ot-test-ndproxy-table test_ndproxy_table.cpp ) @@ -790,6 +855,27 @@ target_link_libraries(ot-test-pool add_test(NAME ot-test-pool COMMAND ot-test-pool) +add_executable(ot-test-power-calibration + test_power_calibration.cpp +) + +target_include_directories(ot-test-power-calibration + PRIVATE + ${COMMON_INCLUDES} +) + +target_compile_options(ot-test-power-calibration + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) + +target_link_libraries(ot-test-power-calibration + PRIVATE + ${COMMON_LIBS} +) + +add_test(NAME ot-test-power-calibration COMMAND ot-test-power-calibration) + add_executable(ot-test-priority-queue test_priority_queue.cpp ) @@ -916,6 +1002,28 @@ target_link_libraries(ot-test-serial-number add_test(NAME ot-test-serial-number COMMAND ot-test-serial-number) +add_executable(ot-test-srp-server + test_srp_server.cpp +) + +target_include_directories(ot-test-srp-server + PRIVATE + ${COMMON_INCLUDES} +) + +target_compile_options(ot-test-srp-server + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) + +target_link_libraries(ot-test-srp-server + PRIVATE + ${COMMON_LIBS} +) + +add_test(NAME ot-test-srp-server COMMAND ot-test-srp-server) + + add_executable(ot-test-string test_string.cpp ) @@ -941,6 +1049,22 @@ add_executable(ot-test-timer test_timer.cpp ) +add_executable(ot-test-toolchain + test_toolchain.cpp test_toolchain_c.c +) + +target_include_directories(ot-test-toolchain + PRIVATE + ${COMMON_INCLUDES} +) + +target_link_libraries(ot-test-toolchain + PRIVATE + ${COMMON_LIBS} +) + +add_test(NAME ot-test-toolchain COMMAND ot-test-toolchain) + target_include_directories(ot-test-timer PRIVATE ${COMMON_INCLUDES} @@ -957,3 +1081,109 @@ target_link_libraries(ot-test-timer ) add_test(NAME ot-test-timer COMMAND ot-test-timer) + +add_executable(ot-test-tlv + test_tlv.cpp +) + +target_include_directories(ot-test-tlv + PRIVATE + ${COMMON_INCLUDES} +) + +target_compile_options(ot-test-tlv + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) + +target_link_libraries(ot-test-tlv + PRIVATE + ${COMMON_LIBS} +) + +add_test(NAME ot-test-tlv COMMAND ot-test-tlv) + +add_executable(ot-test-hdlc + test_hdlc.cpp +) +target_include_directories(ot-test-hdlc + PRIVATE + ${COMMON_INCLUDES} +) +target_compile_options(ot-test-hdlc + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) +target_link_libraries(ot-test-hdlc + PRIVATE + ${COMMON_LIBS} +) +add_test(NAME ot-test-hdlc COMMAND ot-test-hdlc) + +add_executable(ot-test-spinel-buffer + test_spinel_buffer.cpp +) +target_include_directories(ot-test-spinel-buffer + PRIVATE + ${COMMON_INCLUDES} +) +target_compile_options(ot-test-spinel-buffer + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) +target_link_libraries(ot-test-spinel-buffer + PRIVATE + ${COMMON_LIBS} +) +add_test(NAME ot-test-spinel-buffer COMMAND ot-test-spinel-buffer) + +add_executable(ot-test-spinel-decoder + test_spinel_decoder.cpp +) +target_include_directories(ot-test-spinel-decoder + PRIVATE + ${COMMON_INCLUDES} +) +target_compile_options(ot-test-spinel-decoder + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) +target_link_libraries(ot-test-spinel-decoder + PRIVATE + ${COMMON_LIBS} +) +add_test(NAME ot-test-spinel-decoder COMMAND ot-test-spinel-decoder) + +add_executable(ot-test-spinel-encoder + test_spinel_encoder.cpp +) +target_include_directories(ot-test-spinel-encoder + PRIVATE + ${COMMON_INCLUDES} +) +target_compile_options(ot-test-spinel-encoder + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) +target_link_libraries(ot-test-spinel-encoder + PRIVATE + ${COMMON_LIBS} +) +add_test(NAME ot-test-spinel-encoder COMMAND ot-test-spinel-encoder) + +add_executable(ot-test-address-sanitizer + test_address_sanitizer.cpp +) +target_include_directories(ot-test-address-sanitizer + PRIVATE + ${COMMON_INCLUDES} +) +target_compile_options(ot-test-address-sanitizer + PRIVATE + ${COMMON_COMPILE_OPTIONS} +) +target_link_libraries(ot-test-address-sanitizer + PRIVATE + ${COMMON_LIBS} +) +add_test(NAME ot-test-address-sanitizer COMMAND ot-test-address-sanitizer) diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am deleted file mode 100644 index 21316aab29a..00000000000 --- a/tests/unit/Makefile.am +++ /dev/null @@ -1,389 +0,0 @@ -# -# Copyright (c) 2016, 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. -# - -include $(abs_top_nlbuild_autotools_dir)/automake/pre.am - -COMMON_LIBTOOLFLAGS = --preserve-dup-deps - -# -# Local headers to build against and distribute but not to install -# since they are not part of the package. -# -noinst_HEADERS = \ - test_lowpan.hpp \ - test_platform.h \ - test_util.h \ - test_util.hpp \ - $(NULL) - -# -# Other files we do want to distribute with the package. -# -EXTRA_DIST = \ - $(NULL) - -if OPENTHREAD_BUILD_TESTS -# C preprocessor option flags that will apply to all compiled objects in this -# makefile. - -AM_CPPFLAGS = \ - -DOPENTHREAD_FTD=1 \ - -DOPENTHREAD_MTD=0 \ - -DOPENTHREAD_RADIO=0 \ - -DOPENTHREAD_RADIO_CLI=0 \ - -DOPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE=1 \ - -I$(top_srcdir)/include \ - -I$(top_srcdir)/src \ - -I$(top_srcdir)/src/core \ - $(NULL) - -if OPENTHREAD_EXAMPLES_SIMULATION -AM_CPPFLAGS += \ - -I$(top_srcdir)/examples/platforms \ - $(NULL) -endif - -if OPENTHREAD_PLATFORM_POSIX -AM_CPPFLAGS += \ - -DOPENTHREAD_PLATFORM_POSIX=1 \ - -I$(top_srcdir)/src/posix/platform \ - $(NULL) -endif - -COMMON_LDADD = \ - $(NULL) - -if OPENTHREAD_ENABLE_NCP -COMMON_LDADD += \ - $(top_builddir)/src/ncp/libopenthread-ncp-ftd.a \ - $(NULL) -endif - -COMMON_LDADD += \ - $(top_builddir)/src/core/libopenthread-ftd.a \ - $(top_builddir)/third_party/tcplp/libtcplp.a \ - $(top_builddir)/src/core/libopenthread-ftd.a \ - -lpthread \ - $(NULL) - -if OPENTHREAD_ENABLE_BUILTIN_MBEDTLS -COMMON_LDADD += \ - $(top_builddir)/third_party/mbedtls/libmbedcrypto.a \ - $(NULL) -endif - -if OPENTHREAD_PLATFORM_POSIX -COMMON_LDADD += \ - -lutil - $(NULL) -endif - -# Test applications that should be run when the 'check' target is run. - -check_PROGRAMS = \ - ot-test-toolchain \ - $(NULL) - -if OPENTHREAD_ENABLE_FTD -check_PROGRAMS += \ - ot-test-aes \ - ot-test-array \ - ot-test-binary-search \ - ot-test-checksum \ - ot-test-child \ - ot-test-child-table \ - ot-test-cmd-line-parser \ - ot-test-data \ - ot-test-dns \ - ot-test-dso \ - ot-test-ecdsa \ - ot-test-flash \ - ot-test-frame-builder \ - ot-test-heap \ - ot-test-heap-array \ - ot-test-heap-string \ - ot-test-hkdf-sha256 \ - ot-test-hmac-sha256 \ - ot-test-ip4-header \ - ot-test-ip6-header \ - ot-test-ip-address \ - ot-test-link-quality \ - ot-test-linked-list \ - ot-test-lowpan \ - ot-test-mac-frame \ - ot-test-macros \ - ot-test-meshcop \ - ot-test-message \ - ot-test-message-queue \ - ot-test-multicast-listeners-table \ - ot-test-ndproxy-table \ - ot-test-netif \ - ot-test-network-data \ - ot-test-network-name \ - ot-test-pool \ - ot-test-priority-queue \ - ot-test-pskc \ - ot-test-serial-number \ - ot-test-routing-manager \ - ot-test-smart-ptrs \ - ot-test-string \ - ot-test-timer \ - $(NULL) - -if OPENTHREAD_ENABLE_NCP -check_PROGRAMS += \ - ot-test-hdlc \ - ot-test-spinel-buffer \ - ot-test-spinel-decoder \ - ot-test-spinel-encoder \ - $(NULL) -endif -endif # OPENTHREAD_ENABLE_FTD - -XFAIL_TESTS = \ - $(NULL) - -if OPENTHREAD_WITH_ADDRESS_SANITIZER -check_PROGRAMS += ot-test-address-sanitizer -XFAIL_TESTS += ot-test-address-sanitizer -ot_test_address_sanitizer_SOURCES = test_address_sanitizer.cpp -endif # OPENTHREAD_WITH_ADDRESS_SANITIZER - - -# Test applications and scripts that should be built and run when the -# 'check' target is run. - -TESTS = \ - $(check_PROGRAMS) \ - $(NULL) - -# The additional environment variables and their values that will be -# made available to all programs and scripts in TESTS. - -TESTS_ENVIRONMENT = \ - top_srcdir='$(top_srcdir)' \ - $(NULL) - -COMMON_SOURCES = test_platform.cpp test_util.cpp - -# Source, compiler, and linker options for test programs. - -ot_test_aes_LDADD = $(COMMON_LDADD) -ot_test_aes_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_aes_SOURCES = $(COMMON_SOURCES) test_aes.cpp - -ot_test_array_LDADD = $(COMMON_LDADD) -ot_test_array_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_array_SOURCES = $(COMMON_SOURCES) test_array.cpp - -ot_test_binary_search_LDADD = $(COMMON_LDADD) -ot_test_binary_search_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_binary_search_SOURCES = $(COMMON_SOURCES) test_binary_search.cpp - -ot_test_checksum_LDADD = $(COMMON_LDADD) -ot_test_checksum_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_checksum_SOURCES = $(COMMON_SOURCES) test_checksum.cpp - -ot_test_child_LDADD = $(COMMON_LDADD) -ot_test_child_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_child_SOURCES = $(COMMON_SOURCES) test_child.cpp - -ot_test_child_table_LDADD = $(COMMON_LDADD) -ot_test_child_table_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_child_table_SOURCES = $(COMMON_SOURCES) test_child_table.cpp - -ot_test_cmd_line_parser_LDADD = $(COMMON_LDADD) -ot_test_cmd_line_parser_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_cmd_line_parser_SOURCES = $(COMMON_SOURCES) test_cmd_line_parser.cpp - -ot_test_data_LDADD = $(COMMON_LDADD) -ot_test_data_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_data_SOURCES = $(COMMON_SOURCES) test_data.cpp - -ot_test_dns_LDADD = $(COMMON_LDADD) -ot_test_dns_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_dns_SOURCES = $(COMMON_SOURCES) test_dns.cpp - -ot_test_dso_LDADD = $(COMMON_LDADD) -ot_test_dso_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_dso_SOURCES = $(COMMON_SOURCES) test_dso.cpp - -ot_test_ecdsa_LDADD = $(COMMON_LDADD) -ot_test_ecdsa_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_ecdsa_SOURCES = $(COMMON_SOURCES) test_ecdsa.cpp - -ot_test_flash_LDADD = $(COMMON_LDADD) -ot_test_flash_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_flash_SOURCES = $(COMMON_SOURCES) test_flash.cpp - -ot_test_frame_builder_LDADD = $(COMMON_LDADD) -ot_test_frame_builder_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_frame_builder_SOURCES = $(COMMON_SOURCES) test_frame_builder.cpp - -ot_test_hdlc_LDADD = $(COMMON_LDADD) -ot_test_hdlc_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_hdlc_SOURCES = $(COMMON_SOURCES) test_hdlc.cpp - -ot_test_heap_LDADD = $(COMMON_LDADD) -ot_test_heap_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_heap_SOURCES = $(COMMON_SOURCES) test_heap.cpp - -ot_test_heap_array_LDADD = $(COMMON_LDADD) -ot_test_heap_array_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_heap_array_SOURCES = $(COMMON_SOURCES) test_heap_array.cpp - -ot_test_heap_string_LDADD = $(COMMON_LDADD) -ot_test_heap_string_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_heap_string_SOURCES = $(COMMON_SOURCES) test_heap_string.cpp - -ot_test_hkdf_sha256_LDADD = $(COMMON_LDADD) -ot_test_hkdf_sha256_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_hkdf_sha256_SOURCES = $(COMMON_SOURCES) test_hkdf_sha256.cpp - -ot_test_hmac_sha256_LDADD = $(COMMON_LDADD) -ot_test_hmac_sha256_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_hmac_sha256_SOURCES = $(COMMON_SOURCES) test_hmac_sha256.cpp - -ot_test_ip4_header_LDADD = $(COMMON_LDADD) -ot_test_ip4_header_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_ip4_header_SOURCES = $(COMMON_SOURCES) test_ip4_header.cpp - -ot_test_ip6_header_LDADD = $(COMMON_LDADD) -ot_test_ip6_header_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_ip6_header_SOURCES = $(COMMON_SOURCES) test_ip6_header.cpp - -ot_test_ip_address_LDADD = $(COMMON_LDADD) -ot_test_ip_address_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_ip_address_SOURCES = $(COMMON_SOURCES) test_ip_address.cpp - -ot_test_link_quality_LDADD = $(COMMON_LDADD) -ot_test_link_quality_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_link_quality_SOURCES = $(COMMON_SOURCES) test_link_quality.cpp - -ot_test_linked_list_LDADD = $(COMMON_LDADD) -ot_test_linked_list_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_linked_list_SOURCES = $(COMMON_SOURCES) test_linked_list.cpp - -ot_test_lowpan_LDADD = $(COMMON_LDADD) -ot_test_lowpan_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_lowpan_SOURCES = $(COMMON_SOURCES) test_lowpan.cpp - -ot_test_mac_frame_LDADD = $(COMMON_LDADD) -ot_test_mac_frame_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_mac_frame_SOURCES = $(COMMON_SOURCES) test_mac_frame.cpp - -ot_test_macros_LDADD = $(COMMON_LDADD) -ot_test_macros_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_macros_SOURCES = $(COMMON_SOURCES) test_macros.cpp - -ot_test_message_LDADD = $(COMMON_LDADD) -ot_test_message_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_message_SOURCES = $(COMMON_SOURCES) test_message.cpp - -ot_test_message_queue_LDADD = $(COMMON_LDADD) -ot_test_message_queue_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_message_queue_SOURCES = $(COMMON_SOURCES) test_message_queue.cpp - -ot_test_multicast_listeners_table_LDADD = $(COMMON_LDADD) -ot_test_multicast_listeners_table_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_multicast_listeners_table_SOURCES = $(COMMON_SOURCES) test_multicast_listeners_table.cpp - -ot_test_network_name_LDADD = $(COMMON_LDADD) -ot_test_network_name_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_network_name_SOURCES = $(COMMON_SOURCES) test_network_name.cpp - -ot_test_spinel_buffer_LDADD = $(COMMON_LDADD) -ot_test_spinel_buffer_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_spinel_buffer_SOURCES = $(COMMON_SOURCES) test_spinel_buffer.cpp - -ot_test_ndproxy_table_LDADD = $(COMMON_LDADD) -ot_test_ndproxy_table_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_ndproxy_table_SOURCES = $(COMMON_SOURCES) test_ndproxy_table.cpp - -ot_test_netif_LDADD = $(COMMON_LDADD) -ot_test_netif_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_netif_SOURCES = $(COMMON_SOURCES) test_netif.cpp - -ot_test_network_data_LDADD = $(COMMON_LDADD) -ot_test_network_data_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_network_data_SOURCES = $(COMMON_SOURCES) test_network_data.cpp - -ot_test_pool_LDADD = $(COMMON_LDADD) -ot_test_pool_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_pool_SOURCES = $(COMMON_SOURCES) test_pool.cpp - -ot_test_priority_queue_LDADD = $(COMMON_LDADD) -ot_test_priority_queue_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_priority_queue_SOURCES = $(COMMON_SOURCES) test_priority_queue.cpp - -ot_test_pskc_LDADD = $(COMMON_LDADD) -ot_test_pskc_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_pskc_SOURCES = $(COMMON_SOURCES) test_pskc.cpp - -ot_test_smart_ptrs_LDADD = $(COMMON_LDADD) -ot_test_smart_ptrs_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_smart_ptrs_SOURCES = $(COMMON_SOURCES) test_smart_ptrs.cpp - -ot_test_meshcop_LDADD = $(COMMON_LDADD) -ot_test_meshcop_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_meshcop_SOURCES = $(COMMON_SOURCES) test_meshcop.cpp - -ot_test_routing_manager_LDADD = $(COMMON_LDADD) -ot_test_routing_manager_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_routing_manager_SOURCES = $(COMMON_SOURCES) test_routing_manager.cpp - -ot_test_serial_number_LDADD = $(COMMON_LDADD) -ot_test_serial_number_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_serial_number_SOURCES = $(COMMON_SOURCES) test_serial_number.cpp - -ot_test_string_LDADD = $(COMMON_LDADD) -ot_test_string_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_string_SOURCES = $(COMMON_SOURCES) test_string.cpp - -ot_test_spinel_decoder_LDADD = $(COMMON_LDADD) -ot_test_spinel_decoder_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_spinel_decoder_SOURCES = $(COMMON_SOURCES) test_spinel_decoder.cpp - -ot_test_spinel_encoder_LDADD = $(COMMON_LDADD) -ot_test_spinel_encoder_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_spinel_encoder_SOURCES = $(COMMON_SOURCES) test_spinel_encoder.cpp - -ot_test_timer_LDADD = $(COMMON_LDADD) -ot_test_timer_LIBTOOLFLAGS = $(COMMON_LIBTOOLFLAGS) -ot_test_timer_SOURCES = $(COMMON_SOURCES) test_timer.cpp - -ot_test_toolchain_LDADD = $(NULL) -ot_test_toolchain_SOURCES = test_toolchain.cpp test_toolchain_c.c - -if OPENTHREAD_BUILD_COVERAGE -CLEANFILES = $(wildcard *.gcda *.gcno) -endif # OPENTHREAD_BUILD_COVERAGE - -endif # OPENTHREAD_BUILD_TESTS - -include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/tests/unit/README.md b/tests/unit/README.md new file mode 100644 index 00000000000..132062c1ab8 --- /dev/null +++ b/tests/unit/README.md @@ -0,0 +1,57 @@ +# OpenThread Unit Tests + +This page describes how to build and run OpenThread unit tests. It will be helpful for developers to debug failed unit test cases if they got one in CI or to add some new test cases. + +## Build Simulation + +The unit tests cannot be built solely without building the whole project. So first build OpenThread on the simulation platform, which will also build all unit tests: + +``` +# Go to the root directory of OpenThread +$ script/cmake-build simulation +``` + +## List all tests + +To see what tests are available in OpenThread: + +``` +# Make sure you are at the simulation build directory (build/simulation) +$ ctest -N +``` + +## Run the Unit Tests + +To run all the unit tests: + +``` +# Make sure you are at the simulation build directory (build/simulation) +$ ctest +``` + +To run a specific unit test, for example, `ot-test-spinel`: + +``` +# Make sure you are at the simulation build directory (build/simulation) +$ ctest -R ot-test-spinel +``` + +## Update a Test Case + +If you are developing a unit test case and have made some changes in the test source file, you will need rebuild the test before running it: + +``` +# Make sure you are at the simulation build directory (build/simulation) +$ ninja +``` + +This will only build the test and take a short time. + +If any changes or fixes were made to the OpenThread code, then you'll need to rebuild the entire project: + +``` +# Make sure you are at the simulation build directory (build/simulation) +$ ninja +``` + +This will build the updated OpenThread code as well as the test cases. diff --git a/tests/unit/test_aes.cpp b/tests/unit/test_aes.cpp index ce75b28529e..e88b1e20e33 100644 --- a/tests/unit/test_aes.cpp +++ b/tests/unit/test_aes.cpp @@ -55,7 +55,7 @@ void TestMacBeaconFrame(void) 0xAC, 0x02, 0x05, 0x00, 0x00, 0x00, 0x55, 0xCF, 0x00, 0x00, 0x51, 0x52, 0x53, 0x54, 0x22, 0x3B, 0xC1, 0xEC, 0x84, 0x1A, 0xB5, 0x53}; - otInstance * instance = testInitInstance(); + otInstance *instance = testInitInstance(); ot::Crypto::AesCcm aesCcm; uint32_t headerLength = sizeof(test) - 8; uint32_t payloadLength = 0; @@ -122,8 +122,8 @@ void TestMacCommandFrame(void) uint8_t tag[kTagLength]; - ot::Instance * instance = testInitInstance(); - ot::Message * message; + ot::Instance *instance = testInitInstance(); + ot::Message *message; ot::Crypto::AesCcm aesCcm; VerifyOrQuit(instance != nullptr); @@ -195,9 +195,9 @@ void TestInPlaceAesCcmProcessing(void) uint8_t header[kHeaderLength]; ot::Crypto::AesCcm aesCcm; - ot::Instance * instance = testInitInstance(); - ot::Message * message; - ot::Message * messageClone; + ot::Instance *instance = testInitInstance(); + ot::Message *message; + ot::Message *messageClone; VerifyOrQuit(instance != nullptr); diff --git a/tests/unit/test_binary_search.cpp b/tests/unit/test_binary_search.cpp index bf05d9f6395..d2d0224956a 100644 --- a/tests/unit/test_binary_search.cpp +++ b/tests/unit/test_binary_search.cpp @@ -64,9 +64,12 @@ void TestBinarySearch(void) constexpr Entry kUnsortedTable[] = {{"z", 0}, {"a", 0}, {"b", 0}}; constexpr Entry kDuplicateEntryTable[] = {{"duplicate", 1}, {"duplicate", 2}}; +// gcc-4 does not support constexpr function +#if __GNUC__ > 4 static_assert(BinarySearch::IsSorted(kTable), "IsSorted() failed"); static_assert(!BinarySearch::IsSorted(kUnsortedTable), "failed for unsorted table"); static_assert(!BinarySearch::IsSorted(kDuplicateEntryTable), "failed for table with duplicate entries"); +#endif for (const Entry &tableEntry : kTable) { diff --git a/tests/unit/test_checksum.cpp b/tests/unit/test_checksum.cpp index 101b88582dc..b0b17b5f67e 100644 --- a/tests/unit/test_checksum.cpp +++ b/tests/unit/test_checksum.cpp @@ -73,7 +73,7 @@ uint16_t CalculateChecksum(const void *aBuffer, uint16_t aLength) uint16_t CalculateChecksum(const Ip6::Address &aSource, const Ip6::Address &aDestination, uint8_t aIpProto, - const Message & aMessage) + const Message &aMessage) { // This method calculates the checksum over an IPv6 message. constexpr uint16_t kMaxPayload = 1024; @@ -112,7 +112,7 @@ uint16_t CalculateChecksum(const Ip6::Address &aSource, uint16_t CalculateChecksum(const Ip4::Address &aSource, const Ip4::Address &aDestination, uint8_t aIpProto, - const Message & aMessage) + const Message &aMessage) { // This method calculates the checksum over an IPv4 message. constexpr uint16_t kMaxPayload = 1024; @@ -181,7 +181,7 @@ void TestUdpMessageChecksum(void) for (uint16_t size = kMinSize; size <= kMaxSize; size++) { - Message * message = instance->Get().NewMessage(sizeof(Ip6::Udp::Header)); + Message *message = instance->Get().NewMessage(sizeof(Ip6::Udp::Header)); Ip6::Udp::Header udpHeader; Ip6::MessageInfo messageInfo; @@ -249,7 +249,7 @@ void TestIcmp6MessageChecksum(void) for (uint16_t size = kMinSize; size <= kMaxSize; size++) { - Message * message = instance->Get().NewMessage(sizeof(Ip6::Icmp::Header)); + Message *message = instance->Get().NewMessage(sizeof(Ip6::Icmp::Header)); Ip6::Icmp::Header icmp6Header; Ip6::MessageInfo messageInfo; @@ -324,7 +324,7 @@ void TestTcp4MessageChecksum(void) for (uint16_t size = kMinSize; size <= kMaxSize; size++) { - Message * message = instance->Get().NewMessage(sizeof(Ip4::Tcp::Header)); + Message *message = instance->Get().NewMessage(sizeof(Ip4::Tcp::Header)); Ip4::Tcp::Header tcpHeader; VerifyOrQuit(message != nullptr, "Ip6::NewMesssage() failed"); @@ -379,7 +379,7 @@ void TestUdp4MessageChecksum(void) for (uint16_t size = kMinSize; size <= kMaxSize; size++) { - Message * message = instance->Get().NewMessage(sizeof(Ip4::Udp::Header)); + Message *message = instance->Get().NewMessage(sizeof(Ip4::Udp::Header)); Ip4::Udp::Header udpHeader; VerifyOrQuit(message != nullptr, "Ip6::NewMesssage() failed"); @@ -418,13 +418,13 @@ void TestUdp4MessageChecksum(void) void TestIcmp4MessageChecksum(void) { // A captured ICMP echo request (ping) message. Checksum field is set to zero. - const uint8_t kExampleIcmpMessage[] = "\x08\x00\x00\x00\x67\x2e\x00\x00\x62\xaf\xf1\x61\x00\x04\xfc\x24" - "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17" - "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27" - "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37"; - uint16_t kChecksumForExampleMessage = 0x5594; - Instance *instance = static_cast(testInitInstance()); - Message * message = instance->Get().NewMessage(sizeof(kExampleIcmpMessage)); + const uint8_t kExampleIcmpMessage[] = "\x08\x00\x00\x00\x67\x2e\x00\x00\x62\xaf\xf1\x61\x00\x04\xfc\x24" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27" + "\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37"; + uint16_t kChecksumForExampleMessage = 0x5594; + Instance *instance = static_cast(testInitInstance()); + Message *message = instance->Get().NewMessage(sizeof(kExampleIcmpMessage)); Ip4::Address source; Ip4::Address dest; diff --git a/tests/unit/test_child.cpp b/tests/unit/test_child.cpp index fb30ace794b..2a796a33377 100644 --- a/tests/unit/test_child.cpp +++ b/tests/unit/test_child.cpp @@ -191,10 +191,10 @@ void TestChildIp6Address(void) Child child; Ip6::Address addresses[kMaxChildIp6Addresses]; uint8_t numAddresses; - const char * ip6Addresses[] = { - "fd00:1234::1234", - "ff6b:e251:52fb:0:12e6:b94c:1c28:c56a", - "fd00:1234::204c:3d7c:98f6:9a1b", + const char *ip6Addresses[] = { + "fd00:1234::1234", + "ff6b:e251:52fb:0:12e6:b94c:1c28:c56a", + "fd00:1234::204c:3d7c:98f6:9a1b", }; const uint8_t meshLocalIidArray[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; diff --git a/tests/unit/test_child_table.cpp b/tests/unit/test_child_table.cpp index 3281baa1a90..2fb4583f45c 100644 --- a/tests/unit/test_child_table.cpp +++ b/tests/unit/test_child_table.cpp @@ -124,7 +124,7 @@ void VerifyChildTableContent(ChildTable &aTable, uint16_t aChildListLength, cons for (uint16_t listIndex = 0; listIndex < aChildListLength; listIndex++) { - Child * child; + Child *child; Mac::Address address; if (!StateMatchesFilter(aChildList[listIndex].mState, filter)) @@ -164,8 +164,8 @@ void VerifyChildTableContent(ChildTable &aTable, uint16_t aChildListLength, cons for (; !iter.IsDone(); iter++) { - Child * child = iter.GetChild(); - Child & childRef = *iter; + Child *child = iter.GetChild(); + Child &childRef = *iter; bool didFind = false; uint16_t childIndex; diff --git a/tests/unit/test_cmd_line_parser.cpp b/tests/unit/test_cmd_line_parser.cpp index 46d5915c15a..7f832cf1156 100644 --- a/tests/unit/test_cmd_line_parser.cpp +++ b/tests/unit/test_cmd_line_parser.cpp @@ -252,7 +252,7 @@ void TestParsingHexStrings(void) uint8_t buffer[sizeof(kEvenParsedArray)]; uint8_t buf3[3]; uint16_t len; - const char * string; + const char *string; const uint8_t *bufPtr; // Verify `ParseAsHexString(const char *aString, uint8_t *aBuffer, uint16_t aSize)` diff --git a/tests/unit/test_dns.cpp b/tests/unit/test_dns.cpp index 074cb9ca3b5..6df6b3bd15f 100644 --- a/tests/unit/test_dns.cpp +++ b/tests/unit/test_dns.cpp @@ -49,24 +49,25 @@ void TestDnsName(void) struct TestName { - const char * mName; + const char *mName; uint16_t mEncodedLength; const uint8_t *mEncodedData; - const char ** mLabels; - const char * mExpectedReadName; + const char **mLabels; + const char *mExpectedReadName; }; - Instance * instance; + Instance *instance; MessagePool *messagePool; - Message * message; + Message *message; uint8_t buffer[kMaxSize]; uint16_t len; uint16_t offset; char label[Dns::Name::kMaxLabelSize]; uint8_t labelLength; char name[Dns::Name::kMaxNameSize]; - const char * subDomain; - const char * domain; + const char *subDomain; + const char *domain; + const char *domain2; static const uint8_t kEncodedName1[] = {7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0}; static const uint8_t kEncodedName2[] = {3, 'f', 'o', 'o', 1, 'a', 2, 'b', 'b', 3, 'e', 'd', 'u', 0}; @@ -210,6 +211,42 @@ void TestDnsName(void) domain = "Vice.arpa."; VerifyOrQuit(!Dns::Name::IsSubDomainOf(subDomain, domain)); + domain = "example.com."; + domain2 = "example.com."; + VerifyOrQuit(Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example.com."; + domain2 = "example.com"; + VerifyOrQuit(Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example.com."; + domain2 = "ExAmPlE.cOm"; + VerifyOrQuit(Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example.com"; + domain2 = "ExAmPlE.cOm"; + VerifyOrQuit(Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example.com."; + domain2 = "ExAmPlE.cOm."; + VerifyOrQuit(Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example.com."; + domain2 = "aExAmPlE.cOm."; + VerifyOrQuit(!Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example.com."; + domain2 = "cOm."; + VerifyOrQuit(!Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example."; + domain2 = "example.com."; + VerifyOrQuit(!Dns::Name::IsSameDomain(domain, domain2)); + + domain = "example.com."; + domain2 = ".example.com."; + VerifyOrQuit(!Dns::Name::IsSameDomain(domain, domain2)); + printf("----------------------------------------------------------------\n"); printf("Append names, check encoded bytes, parse name and read labels:\n"); @@ -444,10 +481,10 @@ void TestDnsCompressedName(void) static const char kBadName[] = "bad.name"; - Instance * instance; + Instance *instance; MessagePool *messagePool; - Message * message; - Message * message2; + Message *message; + Message *message2; uint16_t offset; uint16_t name1Offset; uint16_t name2Offset; @@ -799,9 +836,9 @@ void TestHeaderAndResourceRecords(void) const char *kInstanceLabels[] = {kInstance1Label, kInstance2Label}; const char *kInstanceNames[] = {kInstance1Name, kInstance2Name}; - Instance * instance; - MessagePool * messagePool; - Message * message; + Instance *instance; + MessagePool *messagePool; + Message *message; Dns::Header header; uint16_t messageId; uint16_t headerOffset; @@ -1021,6 +1058,8 @@ void TestHeaderAndResourceRecords(void) for (const char *instanceName : kInstanceNames) { + uint16_t savedOffset; + // SRV record SuccessOrQuit(Dns::Name::CompareName(*message, offset, instanceName)); SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, srvRecord)); @@ -1037,12 +1076,21 @@ void TestHeaderAndResourceRecords(void) SuccessOrQuit(Dns::Name::CompareName(*message, offset, instanceName)); SuccessOrQuit(Dns::ResourceRecord::ReadRecord(*message, offset, txtRecord)); VerifyOrQuit(txtRecord.GetTtl() == kTxtTtl); - len = sizeof(buffer); + savedOffset = offset; + len = sizeof(buffer); SuccessOrQuit(txtRecord.ReadTxtData(*message, offset, buffer, len)); VerifyOrQuit(len == sizeof(kTxtData)); VerifyOrQuit(memcmp(buffer, kTxtData, len) == 0); printf(" \"%s\" TXT %u %d \"%s\"\n", instanceName, txtRecord.GetTtl(), txtRecord.GetLength(), reinterpret_cast(buffer)); + + // Partial read of TXT data + len = sizeof(kTxtData) - 1; + memset(buffer, 0, sizeof(buffer)); + VerifyOrQuit(txtRecord.ReadTxtData(*message, savedOffset, buffer, len) == kErrorNoBufs); + VerifyOrQuit(len == sizeof(kTxtData) - 1); + VerifyOrQuit(memcmp(buffer, kTxtData, len) == 0); + VerifyOrQuit(savedOffset == offset); } SuccessOrQuit(Dns::Name::CompareName(*message, offset, kHostName)); @@ -1229,9 +1277,9 @@ void TestDnsTxtEntry(void) {kEncodedTxt5, sizeof(kEncodedTxt5)}, {kEncodedTxt6, sizeof(kEncodedTxt6)}, {kEncodedTxt7, sizeof(kEncodedTxt7)}}; - Instance * instance; - MessagePool * messagePool; - Message * message; + Instance *instance; + MessagePool *messagePool; + Message *message; uint8_t txtData[kMaxTxtDataSize]; uint16_t txtDataLength; uint8_t index; diff --git a/tests/unit/test_dns_client.cpp b/tests/unit/test_dns_client.cpp new file mode 100644 index 00000000000..1b27a5904e2 --- /dev/null +++ b/tests/unit/test_dns_client.cpp @@ -0,0 +1,682 @@ +/* + * Copyright (c) 2023, 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. + */ + +#include + +#include "test_platform.h" +#include "test_util.hpp" + +#include +#include +#include +#include + +#include "common/arg_macros.hpp" +#include "common/array.hpp" +#include "common/instance.hpp" +#include "common/string.hpp" +#include "common/time.hpp" + +#if OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE && OPENTHREAD_CONFIG_DNS_CLIENT_SERVICE_DISCOVERY_ENABLE && \ + OPENTHREAD_CONFIG_DNS_CLIENT_DEFAULT_SERVER_ADDRESS_AUTO_SET_ENABLE && OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE && \ + OPENTHREAD_CONFIG_SRP_SERVER_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE && \ + !OPENTHREAD_CONFIG_TIME_SYNC_ENABLE && !OPENTHREAD_PLATFORM_POSIX +#define ENABLE_DNS_TEST 1 +#else +#define ENABLE_DNS_TEST 0 +#endif + +#if ENABLE_DNS_TEST + +using namespace ot; + +// Logs a message and adds current time (sNow) as "::." +#define Log(...) \ + printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 36000000), (sNow / 60000) % 60, \ + (sNow / 1000) % 60, sNow % 1000 OT_REST_ARGS(__VA_ARGS__)) + +static constexpr uint16_t kMaxRaSize = 800; + +static ot::Instance *sInstance; + +static uint32_t sNow = 0; +static uint32_t sAlarmTime; +static bool sAlarmOn = false; + +static otRadioFrame sRadioTxFrame; +static uint8_t sRadioTxFramePsdu[OT_RADIO_FRAME_MAX_SIZE]; +static bool sRadioTxOngoing = false; + +//---------------------------------------------------------------------------------------------------------------------- +// Function prototypes + +void ProcessRadioTxAndTasklets(void); +void AdvanceTime(uint32_t aDuration); + +//---------------------------------------------------------------------------------------------------------------------- +// `otPlatRadio` + +extern "C" { + +otError otPlatRadioTransmit(otInstance *, otRadioFrame *) +{ + sRadioTxOngoing = true; + + return OT_ERROR_NONE; +} + +otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *) { return &sRadioTxFrame; } + +//---------------------------------------------------------------------------------------------------------------------- +// `otPlatAlaram` + +void otPlatAlarmMilliStop(otInstance *) { sAlarmOn = false; } + +void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt) +{ + sAlarmOn = true; + sAlarmTime = aT0 + aDt; +} + +uint32_t otPlatAlarmMilliGetNow(void) { return sNow; } + +//---------------------------------------------------------------------------------------------------------------------- + +Array sHeapAllocatedPtrs; + +#if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE +void *otPlatCAlloc(size_t aNum, size_t aSize) +{ + void *ptr = calloc(aNum, aSize); + + SuccessOrQuit(sHeapAllocatedPtrs.PushBack(ptr)); + + return ptr; +} + +void otPlatFree(void *aPtr) +{ + if (aPtr != nullptr) + { + void **entry = sHeapAllocatedPtrs.Find(aPtr); + + VerifyOrQuit(entry != nullptr, "A heap allocated item is freed twice"); + sHeapAllocatedPtrs.Remove(*entry); + } + + free(aPtr); +} +#endif + +#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED +void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) +{ + OT_UNUSED_VARIABLE(aLogLevel); + OT_UNUSED_VARIABLE(aLogRegion); + + va_list args; + + printf(" "); + va_start(args, aFormat); + vprintf(aFormat, args); + va_end(args); + printf("\n"); +} +#endif + +} // extern "C" + +//--------------------------------------------------------------------------------------------------------------------- + +void ProcessRadioTxAndTasklets(void) +{ + do + { + if (sRadioTxOngoing) + { + sRadioTxOngoing = false; + otPlatRadioTxStarted(sInstance, &sRadioTxFrame); + otPlatRadioTxDone(sInstance, &sRadioTxFrame, nullptr, OT_ERROR_NONE); + } + + otTaskletsProcess(sInstance); + } while (otTaskletsArePending(sInstance)); +} + +void AdvanceTime(uint32_t aDuration) +{ + uint32_t time = sNow + aDuration; + + Log("AdvanceTime for %u.%03u", aDuration / 1000, aDuration % 1000); + + while (TimeMilli(sAlarmTime) <= TimeMilli(time)) + { + ProcessRadioTxAndTasklets(); + sNow = sAlarmTime; + otPlatAlarmMilliFired(sInstance); + } + + ProcessRadioTxAndTasklets(); + sNow = time; +} + +void InitTest(void) +{ + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Initialize OT instance. + + sNow = 0; + sInstance = static_cast(testInitInstance()); + + memset(&sRadioTxFrame, 0, sizeof(sRadioTxFrame)); + sRadioTxFrame.mPsdu = sRadioTxFramePsdu; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Initialize Border Router and start Thread operation. + + SuccessOrQuit(otLinkSetPanId(sInstance, 0x1234)); + SuccessOrQuit(otIp6SetEnabled(sInstance, true)); + SuccessOrQuit(otThreadSetEnabled(sInstance, true)); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Ensure device starts as leader. + + AdvanceTime(10000); + + VerifyOrQuit(otThreadGetDeviceRole(sInstance) == OT_DEVICE_ROLE_LEADER); +} + +void FinalizeTest(void) +{ + SuccessOrQuit(otIp6SetEnabled(sInstance, false)); + SuccessOrQuit(otThreadSetEnabled(sInstance, false)); + // Make sure there is no message/buffer leak + VerifyOrQuit(sInstance->Get().GetFreeBufferCount() == + sInstance->Get().GetTotalBufferCount()); + SuccessOrQuit(otInstanceErasePersistentInfo(sInstance)); + testFreeInstance(sInstance); +} + +//--------------------------------------------------------------------------------------------------------------------- + +static const char kHostName[] = "elden"; +static const char kHostFullName[] = "elden.default.service.arpa."; + +static const char kService1Name[] = "_srv._udp"; +static const char kService1FullName[] = "_srv._udp.default.service.arpa."; +static const char kInstance1Label[] = "srv-instance"; + +static const char kService2Name[] = "_game._udp"; +static const char kService2FullName[] = "_game._udp.default.service.arpa."; +static const char kInstance2Label[] = "last-ninja"; + +void PrepareService1(Srp::Client::Service &aService) +{ + static const char kSub1[] = "_sub1"; + static const char kSub2[] = "_V1234567"; + static const char kSub3[] = "_XYZWS"; + static const char *kSubLabels[] = {kSub1, kSub2, kSub3, nullptr}; + static const char kTxtKey1[] = "ABCD"; + static const uint8_t kTxtValue1[] = {'a', '0'}; + static const char kTxtKey2[] = "Z0"; + static const uint8_t kTxtValue2[] = {'1', '2', '3'}; + static const char kTxtKey3[] = "D"; + static const uint8_t kTxtValue3[] = {0}; + static const otDnsTxtEntry kTxtEntries[] = { + {kTxtKey1, kTxtValue1, sizeof(kTxtValue1)}, + {kTxtKey2, kTxtValue2, sizeof(kTxtValue2)}, + {kTxtKey3, kTxtValue3, sizeof(kTxtValue3)}, + }; + + memset(&aService, 0, sizeof(aService)); + aService.mName = kService1Name; + aService.mInstanceName = kInstance1Label; + aService.mSubTypeLabels = kSubLabels; + aService.mTxtEntries = kTxtEntries; + aService.mNumTxtEntries = 3; + aService.mPort = 777; + aService.mWeight = 1; + aService.mPriority = 2; +} + +void PrepareService2(Srp::Client::Service &aService) +{ + static const char kSub4[] = "_44444444"; + static const char *kSubLabels2[] = {kSub4, nullptr}; + + memset(&aService, 0, sizeof(aService)); + aService.mName = kService2Name; + aService.mInstanceName = kInstance2Label; + aService.mSubTypeLabels = kSubLabels2; + aService.mTxtEntries = nullptr; + aService.mNumTxtEntries = 0; + aService.mPort = 555; + aService.mWeight = 0; + aService.mPriority = 3; +} + +void ValidateHost(Srp::Server &aServer, const char *aHostName) +{ + // Validate that only a host with `aHostName` is + // registered on SRP server. + + const Srp::Server::Host *host; + const char *name; + + Log("ValidateHost()"); + + host = aServer.GetNextHost(nullptr); + VerifyOrQuit(host != nullptr); + + name = host->GetFullName(); + Log("Hostname: %s", name); + + VerifyOrQuit(StringStartsWith(name, aHostName, kStringCaseInsensitiveMatch)); + VerifyOrQuit(name[strlen(aHostName)] == '.'); + + // Only one host on server + VerifyOrQuit(aServer.GetNextHost(host) == nullptr); +} + +//--------------------------------------------------------------------------------------------------------------------- + +void LogServiceInfo(const Dns::Client::ServiceInfo &aInfo) +{ + Log(" TTL: %u", aInfo.mTtl); + Log(" Port: %u", aInfo.mPort); + Log(" Weight: %u", aInfo.mWeight); + Log(" HostName: %s", aInfo.mHostNameBuffer); + Log(" HostAddr: %s", AsCoreType(&aInfo.mHostAddress).ToString().AsCString()); + Log(" TxtDataLength: %u", aInfo.mTxtDataSize); + Log(" TxtDataTTL: %u", aInfo.mTxtDataTtl); +} + +const char *ServiceModeToString(Dns::Client::QueryConfig::ServiceMode aMode) +{ + static const char *const kServiceModeStrings[] = { + "unspec", // kServiceModeUnspecified (0) + "srv", // kServiceModeSrv (1) + "txt", // kServiceModeTxt (2) + "srv_txt", // kServiceModeSrvTxt (3) + "srv_txt_sep", // kServiceModeSrvTxtSeparate (4) + "srv_txt_opt", // kServiceModeSrvTxtOptimize (5) + }; + + static_assert(Dns::Client::QueryConfig::kServiceModeUnspecified == 0, "Unspecified value is incorrect"); + static_assert(Dns::Client::QueryConfig::kServiceModeSrv == 1, "Srv value is incorrect"); + static_assert(Dns::Client::QueryConfig::kServiceModeTxt == 2, "Txt value is incorrect"); + static_assert(Dns::Client::QueryConfig::kServiceModeSrvTxt == 3, "SrvTxt value is incorrect"); + static_assert(Dns::Client::QueryConfig::kServiceModeSrvTxtSeparate == 4, "SrvTxtSeparate value is incorrect"); + static_assert(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize == 5, "SrvTxtOptimize value is incorrect"); + + return kServiceModeStrings[aMode]; +} + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +struct BrowseInfo +{ + void Reset(void) { mCallbackCount = 0; } + + uint16_t mCallbackCount; + Error mError; + char mServiceName[Dns::Name::kMaxNameSize]; + uint16_t mNumInstances; +}; + +static BrowseInfo sBrowseInfo; + +void BrowseCallback(otError aError, const otDnsBrowseResponse *aResponse, void *aContext) +{ + const Dns::Client::BrowseResponse &response = AsCoreType(aResponse); + + Log("BrowseCallback"); + Log(" Error: %s", ErrorToString(aError)); + + VerifyOrQuit(aContext == sInstance); + + sBrowseInfo.mCallbackCount++; + sBrowseInfo.mError = aError; + + SuccessOrExit(aError); + + SuccessOrQuit(response.GetServiceName(sBrowseInfo.mServiceName, sizeof(sBrowseInfo.mServiceName))); + Log(" ServiceName: %s", sBrowseInfo.mServiceName); + + for (uint16_t index = 0;; index++) + { + char instLabel[Dns::Name::kMaxLabelSize]; + Error error; + + error = response.GetServiceInstance(index, instLabel, sizeof(instLabel)); + + if (error == kErrorNotFound) + { + sBrowseInfo.mNumInstances = index; + break; + } + + SuccessOrQuit(error); + + Log(" %2u) %s", index + 1, instLabel); + } + +exit: + return; +} + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +struct ResolveServiceInfo +{ + void Reset(void) + { + memset(this, 0, sizeof(*this)); + mInfo.mHostNameBuffer = mNameBuffer; + mInfo.mHostNameBufferSize = sizeof(mNameBuffer); + mInfo.mTxtData = mTxtBuffer; + mInfo.mTxtDataSize = sizeof(mTxtBuffer); + }; + + uint16_t mCallbackCount; + Error mError; + Dns::Client::ServiceInfo mInfo; + char mNameBuffer[Dns::Name::kMaxNameSize]; + uint8_t mTxtBuffer[256]; +}; + +static ResolveServiceInfo sResolveServiceInfo; + +void ServiceCallback(otError aError, const otDnsServiceResponse *aResponse, void *aContext) +{ + const Dns::Client::ServiceResponse &response = AsCoreType(aResponse); + char instLabel[Dns::Name::kMaxLabelSize]; + char serviceName[Dns::Name::kMaxNameSize]; + + Log("ServiceCallback"); + Log(" Error: %s", ErrorToString(aError)); + + VerifyOrQuit(aContext == sInstance); + + SuccessOrQuit(response.GetServiceName(instLabel, sizeof(instLabel), serviceName, sizeof(serviceName))); + Log(" InstLabel: %s", instLabel); + Log(" ServiceName: %s", serviceName); + + sResolveServiceInfo.mCallbackCount++; + sResolveServiceInfo.mError = aError; + + SuccessOrExit(aError); + SuccessOrQuit(response.GetServiceInfo(sResolveServiceInfo.mInfo)); + LogServiceInfo(sResolveServiceInfo.mInfo); + +exit: + return; +} + +//---------------------------------------------------------------------------------------------------------------------- + +void TestDnsClient(void) +{ + const Dns::Client::QueryConfig::ServiceMode kServiceModes[] = { + Dns::Client::QueryConfig::kServiceModeSrv, + Dns::Client::QueryConfig::kServiceModeTxt, + Dns::Client::QueryConfig::kServiceModeSrvTxt, + Dns::Client::QueryConfig::kServiceModeSrvTxtSeparate, + Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize, + }; + + Srp::Server *srpServer; + Srp::Client *srpClient; + Srp::Client::Service service1; + Srp::Client::Service service2; + Dns::Client *dnsClient; + Dns::Client::QueryConfig queryConfig; + Dns::ServiceDiscovery::Server *dnsServer; + uint16_t heapAllocations; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestDnsClient"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + dnsClient = &sInstance->Get(); + dnsServer = &sInstance->Get(); + + heapAllocations = sHeapAllocatedPtrs.GetLength(); + + PrepareService1(service1); + PrepareService2(service2); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + SuccessOrQuit(srpClient->SetHostName(kHostName)); + SuccessOrQuit(srpClient->EnableAutoHostAddress()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register two services on SRP. + + SuccessOrQuit(srpClient->AddService(service1)); + SuccessOrQuit(srpClient->AddService(service2)); + + AdvanceTime(2 * 1000); + + VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered); + VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered); + ValidateHost(*srpServer, kHostName); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check DNS Client's default config + + VerifyOrQuit(dnsClient->GetDefaultConfig().GetServiceMode() == + Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate DNS Client `Browse()` + + sBrowseInfo.Reset(); + Log("Browse(%s)", kService1FullName); + SuccessOrQuit(dnsClient->Browse(kService1FullName, BrowseCallback, sInstance)); + AdvanceTime(100); + VerifyOrQuit(sBrowseInfo.mCallbackCount == 1); + SuccessOrQuit(sBrowseInfo.mError); + VerifyOrQuit(sBrowseInfo.mNumInstances == 1); + + sBrowseInfo.Reset(); + + Log("Browse(%s)", kService2FullName); + SuccessOrQuit(dnsClient->Browse(kService2FullName, BrowseCallback, sInstance)); + AdvanceTime(100); + VerifyOrQuit(sBrowseInfo.mCallbackCount == 1); + SuccessOrQuit(sBrowseInfo.mError); + VerifyOrQuit(sBrowseInfo.mNumInstances == 1); + + sBrowseInfo.Reset(); + Log("Browse() for unknwon service"); + SuccessOrQuit(dnsClient->Browse("_unknown._udp.default.service.arpa.", BrowseCallback, sInstance)); + AdvanceTime(100); + VerifyOrQuit(sBrowseInfo.mCallbackCount == 1); + VerifyOrQuit(sBrowseInfo.mError == kErrorNotFound); + + Log("Issue four parallel `Browse()` at the same time"); + sBrowseInfo.Reset(); + SuccessOrQuit(dnsClient->Browse(kService1FullName, BrowseCallback, sInstance)); + SuccessOrQuit(dnsClient->Browse(kService2FullName, BrowseCallback, sInstance)); + SuccessOrQuit(dnsClient->Browse("_unknown._udp.default.service.arpa.", BrowseCallback, sInstance)); + SuccessOrQuit(dnsClient->Browse("_unknown2._udp.default.service.arpa.", BrowseCallback, sInstance)); + AdvanceTime(100); + VerifyOrQuit(sBrowseInfo.mCallbackCount == 4); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate DNS Client `ResolveService()` using all service modes + + for (Dns::Client::QueryConfig::ServiceMode mode : kServiceModes) + { + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + Log("ResolveService(%s,%s) with ServiceMode: %s", kInstance1Label, kService1FullName, + ServiceModeToString(mode)); + + queryConfig.Clear(); + queryConfig.mServiceMode = static_cast(mode); + + sResolveServiceInfo.Reset(); + SuccessOrQuit( + dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig)); + AdvanceTime(100); + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + SuccessOrQuit(sResolveServiceInfo.mError); + + if (mode != Dns::Client::QueryConfig::kServiceModeTxt) + { + VerifyOrQuit(sResolveServiceInfo.mInfo.mTtl != 0); + VerifyOrQuit(sResolveServiceInfo.mInfo.mPort == service1.mPort); + VerifyOrQuit(sResolveServiceInfo.mInfo.mWeight == service1.mWeight); + VerifyOrQuit(strcmp(sResolveServiceInfo.mInfo.mHostNameBuffer, kHostFullName) == 0); + } + + if (mode != Dns::Client::QueryConfig::kServiceModeSrv) + { + VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataTtl != 0); + VerifyOrQuit(sResolveServiceInfo.mInfo.mTxtDataSize != 0); + } + } + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + Log("Set TestMode on server to only accept single question"); + dnsServer->SetTestMode(Dns::ServiceDiscovery::Server::kTestModeSingleQuestionOnly); + + Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName, + ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize)); + + queryConfig.Clear(); + queryConfig.mServiceMode = static_cast(Dns::Client::QueryConfig::kServiceModeSrvTxtOptimize); + + sResolveServiceInfo.Reset(); + SuccessOrQuit( + dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig)); + AdvanceTime(200); + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + SuccessOrQuit(sResolveServiceInfo.mError); + + // Use `kServiceModeSrvTxt` and check that server does reject two questions. + + Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName, + ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrvTxt)); + + queryConfig.Clear(); + queryConfig.mServiceMode = static_cast(Dns::Client::QueryConfig::kServiceModeSrvTxt); + + sResolveServiceInfo.Reset(); + SuccessOrQuit( + dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig)); + AdvanceTime(200); + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + VerifyOrQuit(sResolveServiceInfo.mError != kErrorNone); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + Log("Stop DNS-SD server"); + dnsServer->Stop(); + + Log("ResolveService(%s,%s) with ServiceMode %s", kInstance1Label, kService1FullName, + ServiceModeToString(Dns::Client::QueryConfig::kServiceModeSrv)); + + queryConfig.Clear(); + queryConfig.mServiceMode = static_cast(Dns::Client::QueryConfig::kServiceModeSrv); + + sResolveServiceInfo.Reset(); + SuccessOrQuit( + dnsClient->ResolveService(kInstance1Label, kService1FullName, ServiceCallback, sInstance, &queryConfig)); + AdvanceTime(25 * 1000); + + VerifyOrQuit(sResolveServiceInfo.mCallbackCount == 1); + VerifyOrQuit(sResolveServiceInfo.mError == kErrorResponseTimeout); + + Log("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - "); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable SRP server, verify that all heap allocations by SRP server + // and/or by DNS Client are freed. + + Log("Disabling SRP server"); + + srpServer->SetEnabled(false); + AdvanceTime(100); + + VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + FinalizeTest(); + + VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty()); + + Log("End of TestDnsClient"); +} + +#endif // ENABLE_DNS_TEST + +int main(void) +{ +#if ENABLE_DNS_TEST + TestDnsClient(); + printf("All tests passed\n"); +#else + printf("DNS_CLIENT or DSNSSD_SERVER feature is not enabled\n"); +#endif + + return 0; +} diff --git a/tests/unit/test_dso.cpp b/tests/unit/test_dso.cpp index 7b878e8c94a..2ee18286a9e 100644 --- a/tests/unit/test_dso.cpp +++ b/tests/unit/test_dso.cpp @@ -35,6 +35,7 @@ #include "common/array.hpp" #include "common/as_core_type.hpp" #include "common/instance.hpp" +#include "common/time.hpp" #include "net/dns_dso.hpp" #if OPENTHREAD_CONFIG_DNS_DSO_ENABLE @@ -51,10 +52,7 @@ static otInstance *sInstance; printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 36000000), (sNow / 60000) % 60, \ (sNow / 1000) % 60, sNow % 1000 OT_REST_ARGS(__VA_ARGS__)) -void otPlatAlarmMilliStop(otInstance *) -{ - sAlarmOn = false; -} +void otPlatAlarmMilliStop(otInstance *) { sAlarmOn = false; } void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt) { @@ -65,10 +63,7 @@ void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt) (sAlarmTime - sNow) / 1000, (sAlarmTime - sNow) % 1000); } -uint32_t otPlatAlarmMilliGetNow(void) -{ - return sNow; -} +uint32_t otPlatAlarmMilliGetNow(void) { return sNow; } } // extern "C" @@ -78,7 +73,7 @@ void AdvanceTime(uint32_t aDuration) Log(" AdvanceTime for %u.%03u", aDuration / 1000, aDuration % 1000); - while (sAlarmTime <= time) + while (ot::TimeMilli(sAlarmTime) <= ot::TimeMilli(time)) { sNow = sAlarmTime; otPlatAlarmMilliFired(sInstance); @@ -117,8 +112,8 @@ class Connection : public Dso::Connection friend void otPlatDsoSend(otPlatDsoConnection *aConnection, otMessage *aMessage); public: - explicit Connection(Instance & aInstance, - const char * aName, + explicit Connection(Instance &aInstance, + const char *aName, const Ip6::SockAddr &aLocalSockAddr, const Ip6::SockAddr &aPeerSockAddr) : Dso::Connection(aInstance, aPeerSockAddr, sCallbacks) @@ -128,7 +123,7 @@ class Connection : public Dso::Connection ClearTestFlags(); } - const char * GetName(void) const { return mName; } + const char *GetName(void) const { return mName; } const Ip6::SockAddr &GetLocalSockAddr(void) const { return mLocalSockAddr; } void ClearTestFlags(void) @@ -246,7 +241,7 @@ class Connection : public Dso::Connection } Error ProcessResponseMessage(const Dns::Header &aHeader, - const Message & aMessage, + const Message &aMessage, Dso::Tlv::Type aResponseTlvType, Dso::Tlv::Type aRequestTlvType) { @@ -288,22 +283,22 @@ class Connection : public Dso::Connection static Error ProcessRequestMessage(Dso::Connection &aConnection, MessageId aMessageId, - const Message & aMessage, + const Message &aMessage, Dso::Tlv::Type aPrimaryTlvType) { return static_cast(aConnection).ProcessRequestMessage(aMessageId, aMessage, aPrimaryTlvType); } static Error ProcessUnidirectionalMessage(Dso::Connection &aConnection, - const Message & aMessage, + const Message &aMessage, Dso::Tlv::Type aPrimaryTlvType) { return static_cast(aConnection).ProcessUnidirectionalMessage(aMessage, aPrimaryTlvType); } - static Error ProcessResponseMessage(Dso::Connection & aConnection, + static Error ProcessResponseMessage(Dso::Connection &aConnection, const Dns::Header &aHeader, - const Message & aMessage, + const Message &aMessage, Dso::Tlv::Type aResponseTlvType, Dso::Tlv::Type aRequestTlvType) { @@ -311,7 +306,7 @@ class Connection : public Dso::Connection .ProcessResponseMessage(aHeader, aMessage, aResponseTlvType, aRequestTlvType); } - const char * mName; + const char *mName; Ip6::SockAddr mLocalSockAddr; bool mDidGetConnectedSignal; bool mDidGetSessionEstablishedSignal; @@ -379,8 +374,8 @@ void otPlatDsoEnableListening(otInstance *, bool aEnable) void otPlatDsoConnect(otPlatDsoConnection *aConnection, const otSockAddr *aPeerSockAddr) { - Connection & conn = *static_cast(aConnection); - Connection * peerConn = nullptr; + Connection &conn = *static_cast(aConnection); + Connection *peerConn = nullptr; const Ip6::SockAddr &peerSockAddr = AsCoreType(aPeerSockAddr); Log(" otPlatDsoConnect(%s, aPeer:0x%04x)", conn.GetName(), peerSockAddr.GetPort()); @@ -544,12 +539,12 @@ void TestDso(void) static constexpr uint32_t kRetryDelayInterval = TimeMilli::SecToMsec(3600); static constexpr uint32_t kLongResponseTimeout = Dso::kResponseTimeout + TimeMilli::SecToMsec(17); - Instance & instance = *static_cast(testInitInstance()); + Instance &instance = *static_cast(testInitInstance()); Ip6::SockAddr serverSockAddr(kPortA); Ip6::SockAddr clientSockAddr(kPortB); Connection serverConn(instance, "serverConn", serverSockAddr, clientSockAddr); Connection clientConn(instance, "clinetConn", clientSockAddr, serverSockAddr); - Message * message; + Message *message; Dso::Tlv tlv; Connection::MessageId messageId; diff --git a/tests/unit/test_ecdsa.cpp b/tests/unit/test_ecdsa.cpp index 533ebcce470..aad13a74882 100644 --- a/tests/unit/test_ecdsa.cpp +++ b/tests/unit/test_ecdsa.cpp @@ -66,12 +66,14 @@ void TestEcdsaVector(void) const uint8_t kMessage[] = {'s', 'a', 'm', 'p', 'l', 'e'}; +#if OPENTHREAD_CONFIG_DETERMINISTIC_ECDSA_ENABLE const uint8_t kExpectedSignature[] = { 0xEF, 0xD4, 0x8B, 0x2A, 0xAC, 0xB6, 0xA8, 0xFD, 0x11, 0x40, 0xDD, 0x9C, 0xD4, 0x5E, 0x81, 0xD6, 0x9D, 0x2C, 0x87, 0x7B, 0x56, 0xAA, 0xF9, 0x91, 0xC3, 0x4D, 0x0E, 0xA8, 0x4E, 0xAF, 0x37, 0x16, 0xF7, 0xCB, 0x1C, 0x94, 0x2D, 0x65, 0x7C, 0x41, 0xD4, 0x36, 0xC7, 0xA1, 0xB6, 0xE2, 0x9F, 0x65, 0xF3, 0xE9, 0x00, 0xDB, 0xB9, 0xAF, 0xF4, 0x06, 0x4D, 0xC4, 0xAB, 0x2F, 0x84, 0x3A, 0xCD, 0xA8, }; +#endif Instance *instance = testInitInstance(); @@ -110,6 +112,7 @@ void TestEcdsaVector(void) SuccessOrQuit(keyPair.Sign(hash, signature)); DumpBuffer("Signature", signature.GetBytes(), sizeof(signature)); +#if OPENTHREAD_CONFIG_DETERMINISTIC_ECDSA_ENABLE printf("\nCheck signature against expected sequence----------------------------------\n"); DumpBuffer("Expected signature", kExpectedSignature, sizeof(kExpectedSignature)); @@ -117,6 +120,7 @@ void TestEcdsaVector(void) VerifyOrQuit(memcmp(signature.GetBytes(), kExpectedSignature, sizeof(kExpectedSignature)) == 0); printf("Signature matches expected sequence.\n"); +#endif printf("\nVerify the signature ------------------------------------------------------\n"); SuccessOrQuit(publicKey.Verify(hash, signature)); @@ -125,7 +129,7 @@ void TestEcdsaVector(void) testFreeInstance(instance); } -void TestEdsaKeyGenerationSignAndVerify(void) +void TestEcdsaKeyGenerationSignAndVerify(void) { Instance *instance = testInitInstance(); @@ -183,7 +187,7 @@ int main(void) { #if OPENTHREAD_CONFIG_ECDSA_ENABLE ot::Crypto::TestEcdsaVector(); - ot::Crypto::TestEdsaKeyGenerationSignAndVerify(); + ot::Crypto::TestEcdsaKeyGenerationSignAndVerify(); printf("All tests passed\n"); #else printf("ECDSA feature is not enabled\n"); diff --git a/tests/unit/test_frame_builder.cpp b/tests/unit/test_frame_builder.cpp index cbc09c01b53..8c648e32e83 100644 --- a/tests/unit/test_frame_builder.cpp +++ b/tests/unit/test_frame_builder.cpp @@ -39,11 +39,12 @@ void TestFrameBuilder(void) { const uint8_t kData1[] = {0x01, 0x02, 0x03, 0x04, 0x05}; const uint8_t kData2[] = {0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa}; + const uint8_t kData3[] = {0xca, 0xfe, 0xbe, 0xef}; static constexpr uint16_t kMaxBufferSize = sizeof(kData1) * 2 + sizeof(kData2); - Instance * instance; - Message * message; + Instance *instance; + Message *message; uint16_t offset; uint8_t buffer[kMaxBufferSize]; uint8_t zeroBuffer[kMaxBufferSize]; @@ -72,12 +73,25 @@ void TestFrameBuilder(void) VerifyOrQuit(frameBuilder.CanAppend(sizeof(buffer))); VerifyOrQuit(!frameBuilder.CanAppend(sizeof(buffer) + 1)); + frameBuilder.SetMaxLength(sizeof(kData1)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(frameBuilder.GetLength() == 0); + VerifyOrQuit(frameBuilder.GetMaxLength() == sizeof(kData1)); + VerifyOrQuit(memcmp(buffer, zeroBuffer, sizeof(buffer)) == 0); + VerifyOrQuit(frameBuilder.CanAppend(sizeof(kData1))); + VerifyOrQuit(!frameBuilder.CanAppend(sizeof(kData1) + 1)); + SuccessOrQuit(frameBuilder.Append(kData1)); VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1)); VerifyOrQuit(frameBuilder.GetBytes() == buffer); VerifyOrQuit(memcmp(buffer, kData1, sizeof(kData1)) == 0); VerifyOrQuit(memcmp(buffer + sizeof(kData1), zeroBuffer, sizeof(buffer) - sizeof(kData1)) == 0); + frameBuilder.SetMaxLength(sizeof(buffer)); + VerifyOrQuit(frameBuilder.GetMaxLength() == sizeof(buffer)); + VerifyOrQuit(frameBuilder.CanAppend(sizeof(buffer) - sizeof(kData1))); + VerifyOrQuit(!frameBuilder.CanAppend(sizeof(buffer) - sizeof(kData1) + 1)); + SuccessOrQuit(frameBuilder.AppendUint8(0x01)); SuccessOrQuit(frameBuilder.AppendBigEndianUint16(0x0203)); SuccessOrQuit(frameBuilder.AppendLittleEndianUint16(0x0504)); @@ -135,6 +149,66 @@ void TestFrameBuilder(void) VerifyOrQuit(memcmp(buffer + sizeof(kData1), kData2, sizeof(kData2)) == 0); VerifyOrQuit(memcmp(buffer + sizeof(kData1) + sizeof(kData2), kData1, sizeof(kData1)) == 0); + frameBuilder.Init(buffer, sizeof(buffer)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(frameBuilder.GetLength() == 0); + VerifyOrQuit(frameBuilder.GetMaxLength() == sizeof(buffer)); + + offset = 0; + SuccessOrQuit(frameBuilder.Insert(offset, kData1)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData1, sizeof(kData1)) == 0); + + offset = 0; + SuccessOrQuit(frameBuilder.Insert(offset, kData2)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1) + sizeof(kData2)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData2, sizeof(kData2)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2), kData1, sizeof(kData1)) == 0); + + offset = sizeof(kData2); + SuccessOrQuit(frameBuilder.InsertBytes(offset, kData3, sizeof(kData3))); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1) + sizeof(kData2) + sizeof(kData3)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData2, sizeof(kData2)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2), kData3, sizeof(kData3)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2) + sizeof(kData3), kData1, sizeof(kData1)) == 0); + + offset = frameBuilder.GetLength(); + SuccessOrQuit(frameBuilder.Insert(offset, 0x77)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1) + sizeof(kData2) + sizeof(kData3) + sizeof(uint8_t)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData2, sizeof(kData2)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2), kData3, sizeof(kData3)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2) + sizeof(kData3), kData1, sizeof(kData1)) == 0); + VerifyOrQuit(buffer[sizeof(kData2) + sizeof(kData3) + sizeof(kData1)] == 0x77); + + offset = frameBuilder.GetLength() - 1; + frameBuilder.RemoveBytes(offset, sizeof(uint8_t)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1) + sizeof(kData2) + sizeof(kData3)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData2, sizeof(kData2)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2), kData3, sizeof(kData3)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2) + sizeof(kData3), kData1, sizeof(kData1)) == 0); + + offset = sizeof(kData2); + frameBuilder.RemoveBytes(offset, sizeof(kData3)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1) + sizeof(kData2)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData2, sizeof(kData2)) == 0); + VerifyOrQuit(memcmp(buffer + sizeof(kData2), kData1, sizeof(kData1)) == 0); + + offset = 0; + frameBuilder.RemoveBytes(offset, sizeof(kData2)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kData1)); + VerifyOrQuit(frameBuilder.GetBytes() == buffer); + VerifyOrQuit(memcmp(buffer, kData1, sizeof(kData1)) == 0); + + offset = 0; + frameBuilder.RemoveBytes(offset, sizeof(kData1)); + VerifyOrQuit(frameBuilder.GetLength() == 0); + message->Free(); testFreeInstance(instance); } diff --git a/tests/unit/test_hdlc.cpp b/tests/unit/test_hdlc.cpp index e820d5c46c1..f23ae99996d 100644 --- a/tests/unit/test_hdlc.cpp +++ b/tests/unit/test_hdlc.cpp @@ -58,7 +58,7 @@ static const uint8_t sMottoText[] = "Think good thoughts, say good words, d static const uint8_t sHexText[] = "0123456789abcdef"; static const uint8_t sSkipText[] = "Skip text"; static const uint8_t sHdlcSpecials[] = {kFlagSequence, kFlagXOn, kFlagXOff, - kFlagSequence, kEscapeSequence, kFlagSpecial}; + kFlagSequence, kEscapeSequence, kFlagSpecial}; otError WriteToBuffer(const uint8_t *aText, Hdlc::FrameWritePointer &aWritePointer) { @@ -126,8 +126,8 @@ void TestHdlcFrameBuffer(void) void TestHdlcMultiFrameBuffer(void) { Hdlc::MultiFrameBuffer frameBuffer; - uint8_t * frame = nullptr; - uint8_t * newFrame = nullptr; + uint8_t *frame = nullptr; + uint8_t *newFrame = nullptr; uint16_t length; uint16_t newLength; @@ -454,7 +454,7 @@ void TestEncoderDecoder(void) DecoderContext decoderContext; Hdlc::Encoder encoder(encoderBuffer); Hdlc::Decoder decoder(decoderBuffer, ProcessDecodedFrame, &decoderContext); - uint8_t * frame; + uint8_t *frame; uint16_t length; uint8_t badShortFrame[3] = {kFlagSequence, 0xaa, kFlagSequence}; @@ -591,10 +591,7 @@ void TestEncoderDecoder(void) printf(" -- PASS\n"); } -uint32_t GetRandom(uint32_t max) -{ - return static_cast(rand()) % max; -} +uint32_t GetRandom(uint32_t max) { return static_cast(rand()) % max; } void TestFuzzEncoderDecoder(void) { diff --git a/tests/unit/test_heap.cpp b/tests/unit/test_heap.cpp index 813d200bbff..b6cc88425c6 100644 --- a/tests/unit/test_heap.cpp +++ b/tests/unit/test_heap.cpp @@ -82,7 +82,7 @@ void TestAllocateRandomly(size_t aSizeLimit, unsigned int aSeed) { struct Node { - Node * mNext; + Node *mNext; size_t mSize; }; @@ -93,7 +93,7 @@ void TestAllocateRandomly(size_t aSizeLimit, unsigned int aSeed) srand(aSeed); const size_t totalSize = heap.GetFreeSize(); - Node * last = &head; + Node *last = &head; do { diff --git a/tests/unit/test_heap_array.cpp b/tests/unit/test_heap_array.cpp index 2ed95357041..bb95185cad3 100644 --- a/tests/unit/test_heap_array.cpp +++ b/tests/unit/test_heap_array.cpp @@ -182,7 +182,7 @@ void TestHeapArrayOfUint16(void) { Heap::Array array; Heap::Array array2; - uint16_t * entry; + uint16_t *entry; printf("\n\n====================================================================================\n"); printf("TestHeapArrayOfUint16\n\n"); @@ -348,7 +348,7 @@ void TestHeapArray(void) { Heap::Array array; Heap::Array array2; - Entry * entry; + Entry *entry; printf("------------------------------------------------------------------------------------\n"); printf("After constructor\n"); diff --git a/tests/unit/test_heap_string.cpp b/tests/unit/test_heap_string.cpp index b7973c2c9d3..cc72d5af24c 100644 --- a/tests/unit/test_heap_string.cpp +++ b/tests/unit/test_heap_string.cpp @@ -86,7 +86,7 @@ void TestHeapString(void) { Heap::String str1; Heap::String str2; - const char * oldBuffer; + const char *oldBuffer; printf("====================================================================================\n"); printf("TestHeapString\n\n"); @@ -150,10 +150,7 @@ void TestHeapString(void) printf("\n -- PASS\n"); } -void PrintData(const Heap::Data &aData) -{ - DumpBuffer("data", aData.GetBytes(), aData.GetLength()); -} +void PrintData(const Heap::Data &aData) { DumpBuffer("data", aData.GetBytes(), aData.GetLength()); } static const uint8_t kTestValue = 0x77; @@ -199,9 +196,9 @@ template void VerifyData(const Heap::Data &aData, const uint8 void TestHeapData(void) { - Instance * instance; - MessagePool * messagePool; - Message * message; + Instance *instance; + MessagePool *messagePool; + Message *message; Heap::Data data; uint16_t offset; const uint8_t *oldBuffer; diff --git a/tests/unit/test_hkdf_sha256.cpp b/tests/unit/test_hkdf_sha256.cpp index ca12be62096..a50ac84885b 100644 --- a/tests/unit/test_hkdf_sha256.cpp +++ b/tests/unit/test_hkdf_sha256.cpp @@ -59,7 +59,7 @@ void TestHkdfSha256(void) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Test Case #1: RFC-5869 Appendix A.1 const uint8_t kInKey1[] = {0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}; + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b}; const uint8_t kSalt1[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c}; const uint8_t kInfo1[] = {0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9}; const uint8_t kOutKey1[] = {0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, diff --git a/tests/unit/test_hmac_sha256.cpp b/tests/unit/test_hmac_sha256.cpp index 5c9a978314d..7b9e20ad1ae 100644 --- a/tests/unit/test_hmac_sha256.cpp +++ b/tests/unit/test_hmac_sha256.cpp @@ -72,7 +72,7 @@ void TestSha256(void) struct TestCase { - const char * mData; // (null-terminated string). + const char *mData; // (null-terminated string). otCryptoSha256Hash mHash; }; @@ -85,9 +85,9 @@ void TestSha256(void) printf("TestSha256\n"); - Instance * instance = testInitInstance(); + Instance *instance = testInitInstance(); MessagePool *messagePool; - Message * message; + Message *message; uint16_t offsets[GetArrayLength(kTestCases)]; uint8_t index; @@ -142,7 +142,7 @@ void TestHmacSha256(void) struct TestCase { otCryptoKey mKey; - const void * mData; + const void *mData; uint16_t mDataLength; otCryptoSha256Hash mHash; }; @@ -226,9 +226,9 @@ void TestHmacSha256(void) {{&kKey5[0], sizeof(kKey5), 0}, kData5, sizeof(kData5) - 1, kHash5}, }; - Instance * instance = testInitInstance(); + Instance *instance = testInitInstance(); MessagePool *messagePool; - Message * message; + Message *message; uint16_t offsets[GetArrayLength(kTestCases)]; uint8_t index; diff --git a/tests/unit/test_ip_address.cpp b/tests/unit/test_ip_address.cpp index b25cef9b6e9..06cef10d866 100644 --- a/tests/unit/test_ip_address.cpp +++ b/tests/unit/test_ip_address.cpp @@ -29,6 +29,7 @@ #include #include "common/encoding.hpp" +#include "common/string.hpp" #include "net/ip4_types.hpp" #include "net/ip6_address.hpp" @@ -36,7 +37,7 @@ template struct TestVector { - const char * mString; + const char *mString; const uint8_t mAddr[sizeof(AddressType)]; ot::Error mError; }; @@ -166,6 +167,56 @@ void TestIp6AddressFromString(void) { checkAddressFromString(&testVector); } + + // Validate parsing all test vectors now as an IPv6 prefix. + + for (Ip6AddressTestVector &testVector : testVectors) + { + constexpr uint16_t kMaxString = 80; + + ot::Ip6::Prefix prefix; + char string[kMaxString]; + uint16_t length; + + length = ot::StringLength(testVector.mString, kMaxString); + memcpy(string, testVector.mString, length); + VerifyOrQuit(length + sizeof("/128") <= kMaxString); + strcpy(&string[length], "/128"); + + printf("%s\n", string); + + VerifyOrQuit(prefix.FromString(string) == testVector.mError); + + if (testVector.mError == ot::kErrorNone) + { + VerifyOrQuit(memcmp(prefix.GetBytes(), testVector.mAddr, sizeof(ot::Ip6::Address)) == 0); + VerifyOrQuit(prefix.GetLength() == 128); + } + } +} + +void TestIp6PrefixFromString(void) +{ + ot::Ip6::Prefix prefix; + + SuccessOrQuit(prefix.FromString("::/128")); + VerifyOrQuit(prefix.GetLength() == 128); + + SuccessOrQuit(prefix.FromString("::/0128")); + VerifyOrQuit(prefix.GetLength() == 128); + + SuccessOrQuit(prefix.FromString("::/5")); + VerifyOrQuit(prefix.GetLength() == 5); + + SuccessOrQuit(prefix.FromString("::/0")); + VerifyOrQuit(prefix.GetLength() == 0); + + VerifyOrQuit(prefix.FromString("::") == ot::kErrorParse); + VerifyOrQuit(prefix.FromString("::/") == ot::kErrorParse); + VerifyOrQuit(prefix.FromString("::/129") == ot::kErrorParse); + VerifyOrQuit(prefix.FromString(":: /12") == ot::kErrorParse); + VerifyOrQuit(prefix.FromString("::/a1") == ot::kErrorParse); + VerifyOrQuit(prefix.FromString("::/12 ") == ot::kErrorParse); } void TestIp4AddressFromString(void) @@ -194,6 +245,78 @@ void TestIp4AddressFromString(void) } } +struct CidrTestVector +{ + const char *mString; + const uint8_t mAddr[sizeof(otIp4Address)]; + const uint8_t mLength; + ot::Error mError; +}; + +static void checkCidrFromString(CidrTestVector *aTestVector) +{ + ot::Error error; + ot::Ip4::Cidr cidr; + + cidr.Clear(); + + error = cidr.FromString(aTestVector->mString); + + printf("%-42s -> %-42s\n", aTestVector->mString, + (error == ot::kErrorNone) ? cidr.ToString().AsCString() : "(parse error)"); + + VerifyOrQuit(error == aTestVector->mError, "Address::FromString returned unexpected error code"); + + if (error == ot::kErrorNone) + { + VerifyOrQuit(0 == memcmp(cidr.GetBytes(), aTestVector->mAddr, sizeof(aTestVector->mAddr)), + "Cidr::FromString parsing failed"); + VerifyOrQuit(cidr.mLength == aTestVector->mLength, "Cidr::FromString parsing failed"); + } +} + +void TestIp4CidrFromString(void) +{ + CidrTestVector testVectors[] = { + {"0.0.0.0/0", {0, 0, 0, 0}, 0, ot::kErrorNone}, + {"255.255.255.255/32", {255, 255, 255, 255}, 32, ot::kErrorNone}, + {"127.0.0.1/8", {127, 0, 0, 1}, 8, ot::kErrorNone}, + {"1.2.3.4/24", {1, 2, 3, 4}, 24, ot::kErrorNone}, + {"001.002.003.004/20", {1, 2, 3, 4}, 20, ot::kErrorNone}, + {"00000127.000.000.000001/8", {127, 0, 0, 1}, 8, ot::kErrorNone}, + // Valid suffix, invalid address + {"123.231.0.256/4", {0}, 0, ot::kErrorParse}, // Invalid byte value. + {"100123.231.0.256/4", {0}, 0, ot::kErrorParse}, // Invalid byte value. + {"1.22.33/4", {0}, 0, ot::kErrorParse}, // Too few bytes. + {"1.22.33.44.5/4", {0}, 0, ot::kErrorParse}, // Too many bytes. + {"a.b.c.d/4", {0}, 0, ot::kErrorParse}, // Wrong digit char. + {"123.23.45 .12/4", {0}, 0, ot::kErrorParse}, // Extra space. + {"./4", {0}, 0, ot::kErrorParse}, // Invalid. + // valid address, invalid suffix + {"1.2.3.4/33", {0}, 0, ot::kErrorParse}, // Prefix length too large + {"1.2.3.4/12345678", {0}, 0, ot::kErrorParse}, // Prefix length too large? + {"1.2.3.4/12a", {0}, 0, ot::kErrorParse}, // Extra char after prefix length. + {"1.2.3.4/-1", {0}, 0, ot::kErrorParse}, // Not even a non-negative integer. + {"1.2.3.4/3.14", {0}, 0, ot::kErrorParse}, // Not even a integer. + {"1.2.3.4/abcd", {0}, 0, ot::kErrorParse}, // Not even a number. + {"1.2.3.4/", {0}, 0, ot::kErrorParse}, // Where is the suffix? + {"1.2.3.4", {0}, 0, ot::kErrorParse}, // Where is the suffix? + // invalid address and invalid suffix + {"123.231.0.256/41", {0}, 0, ot::kErrorParse}, // Invalid byte value. + {"100123.231.0.256/abc", {0}, 0, ot::kErrorParse}, // Invalid byte value. + {"1.22.33", {0}, 0, ot::kErrorParse}, // Too few bytes. + {"1.22.33.44.5/36", {0}, 0, ot::kErrorParse}, // Too many bytes. + {"a.b.c.d/99", {0}, 0, ot::kErrorParse}, // Wrong digit char. + {"123.23.45 .12", {0}, 0, ot::kErrorParse}, // Extra space. + {".", {0}, 0, ot::kErrorParse}, // Invalid. + }; + + for (CidrTestVector &testVector : testVectors) + { + checkCidrFromString(&testVector); + } +} + bool CheckPrefix(const ot::Ip6::Address &aAddress, const uint8_t *aPrefix, uint8_t aPrefixLength) { // Check the first aPrefixLength bits of aAddress to match the given aPrefix. @@ -215,6 +338,27 @@ bool CheckPrefix(const ot::Ip6::Address &aAddress, const uint8_t *aPrefix, uint8 return matches; } +bool CheckPrefixInIid(const ot::Ip6::InterfaceIdentifier &aIid, const uint8_t *aPrefix, uint8_t aPrefixLength) +{ + // Check the IID to contain the prefix bits (applicable when prefix length is longer than 64). + + bool matches = true; + + for (uint8_t bit = 64; bit < aPrefixLength; bit++) + { + uint8_t index = bit / CHAR_BIT; + uint8_t mask = (0x80 >> (bit % CHAR_BIT)); + + if ((aIid.mFields.m8[index - 8] & mask) != (aPrefix[index] & mask)) + { + matches = false; + break; + } + } + + return matches; +} + bool CheckInterfaceId(const ot::Ip6::Address &aAddress1, const ot::Ip6::Address &aAddress2, uint8_t aPrefixLength) { // Check whether all the bits after aPrefixLength of the two given IPv6 Address match or not. @@ -248,6 +392,7 @@ void TestIp6AddressSetPrefix(void) ot::Ip6::Address address; ot::Ip6::Address allZeroAddress; ot::Ip6::Address allOneAddress; + ot::Ip6::Prefix ip6Prefix; allZeroAddress.Clear(); memset(&allOneAddress, 0xff, sizeof(allOneAddress)); @@ -259,18 +404,33 @@ void TestIp6AddressSetPrefix(void) for (uint8_t prefixLength = 0; prefixLength <= sizeof(ot::Ip6::Address) * CHAR_BIT; prefixLength++) { + ip6Prefix.Clear(); + ip6Prefix.Set(prefix, prefixLength); + address = allZeroAddress; - address.SetPrefix(prefix, prefixLength); + address.SetPrefix(ip6Prefix); printf(" prefix-len:%-3d --> %s\n", prefixLength, address.ToString().AsCString()); VerifyOrQuit(CheckPrefix(address, prefix, prefixLength), "Prefix does not match after SetPrefix()"); VerifyOrQuit(CheckInterfaceId(address, allZeroAddress, prefixLength), "SetPrefix changed bits beyond the prefix length"); address = allOneAddress; - address.SetPrefix(prefix, prefixLength); + address.SetPrefix(ip6Prefix); VerifyOrQuit(CheckPrefix(address, prefix, prefixLength), "Prefix does not match after SetPrefix()"); VerifyOrQuit(CheckInterfaceId(address, allOneAddress, prefixLength), "SetPrefix changed bits beyond the prefix length"); + + address = allZeroAddress; + address.GetIid().ApplyPrefix(ip6Prefix); + VerifyOrQuit(CheckPrefixInIid(address.GetIid(), prefix, prefixLength), "IID is not correct"); + VerifyOrQuit(CheckInterfaceId(address, allZeroAddress, prefixLength), + "Iid:ApplyPrefrix() changed bits beyond the prefix length"); + + address = allOneAddress; + address.GetIid().ApplyPrefix(ip6Prefix); + VerifyOrQuit(CheckPrefixInIid(address.GetIid(), prefix, prefixLength), "IID is not correct"); + VerifyOrQuit(CheckInterfaceId(address, allOneAddress, prefixLength), + "Iid:ApplyPrefrix() changed bits beyond the prefix length"); } } } @@ -395,6 +555,30 @@ void TestIp6Prefix(void) VerifyOrQuit(!(testCase.mPrefixB < testCase.mPrefixA)); } } + + // `IsLinkLocal()` - should contain `fe80::/10`. + VerifyOrQuit(PrefixFrom("fe80::", 10).IsLinkLocal()); + VerifyOrQuit(PrefixFrom("fe80::", 11).IsLinkLocal()); + VerifyOrQuit(PrefixFrom("fea0::", 16).IsLinkLocal()); + VerifyOrQuit(!PrefixFrom("fe80::", 9).IsLinkLocal()); + VerifyOrQuit(!PrefixFrom("ff80::", 10).IsLinkLocal()); + VerifyOrQuit(!PrefixFrom("fe00::", 10).IsLinkLocal()); + VerifyOrQuit(!PrefixFrom("fec0::", 10).IsLinkLocal()); + + // `IsMulticast()` - should contain `ff00::/8`. + VerifyOrQuit(PrefixFrom("ff00::", 8).IsMulticast()); + VerifyOrQuit(PrefixFrom("ff80::", 9).IsMulticast()); + VerifyOrQuit(PrefixFrom("ffff::", 16).IsMulticast()); + VerifyOrQuit(!PrefixFrom("ff00::", 7).IsMulticast()); + VerifyOrQuit(!PrefixFrom("fe00::", 8).IsMulticast()); + + // `IsUniqueLocal()` - should contain `fc00::/7`. + VerifyOrQuit(PrefixFrom("fc00::", 7).IsUniqueLocal()); + VerifyOrQuit(PrefixFrom("fd00::", 8).IsUniqueLocal()); + VerifyOrQuit(PrefixFrom("fc10::", 16).IsUniqueLocal()); + VerifyOrQuit(!PrefixFrom("fc00::", 6).IsUniqueLocal()); + VerifyOrQuit(!PrefixFrom("f800::", 7).IsUniqueLocal()); + VerifyOrQuit(!PrefixFrom("fe00::", 7).IsUniqueLocal()); } void TestIp4Ip6Translation(void) @@ -406,7 +590,7 @@ void TestIp4Ip6Translation(void) const char *mIp6Address; // Expected IPv6 address (with embedded IPv4 "192.0.2.33"). }; - // The test cases are from RFC 6502 - section 2.4 + // The test cases are from RFC 6052 - section 2.4 const TestCase kTestCases[] = { {"2001:db8::", 32, "2001:db8:c000:221::"}, @@ -468,10 +652,10 @@ void TestIp4Cidr(void) using ot::Encoding::BigEndian::HostSwap32; struct TestCase { - const char * mNetwork; + const char *mNetwork; const uint8_t mLength; const uint32_t mHost; - const char * mOutcome; + const char *mOutcome; }; const TestCase kTestCases[] = { @@ -519,9 +703,11 @@ int main(void) TestIp6AddressSetPrefix(); TestIp4AddressFromString(); TestIp6AddressFromString(); + TestIp6PrefixFromString(); TestIp6Prefix(); TestIp4Ip6Translation(); TestIp4Cidr(); + TestIp4CidrFromString(); printf("All tests passed\n"); return 0; } diff --git a/tests/unit/test_link_quality.cpp b/tests/unit/test_link_quality.cpp index f9162e3667a..0f2e9eb7639 100644 --- a/tests/unit/test_link_quality.cpp +++ b/tests/unit/test_link_quality.cpp @@ -31,6 +31,7 @@ #include "common/array.hpp" #include "common/code_utils.hpp" +#include "thread/link_metrics.hpp" #include "thread/link_quality.hpp" namespace ot { @@ -69,7 +70,7 @@ int8_t sNoiseFloor = -100; // dBm // Check and verify the raw average RSS value to match the value from GetAverage(). void VerifyRawRssValue(int8_t aAverage, uint16_t aRawValue) { - if (aAverage != OT_RADIO_RSSI_INVALID) + if (aAverage != Radio::kInvalidRssi) { VerifyOrQuit(aAverage == -static_cast((aRawValue + (kRawAverageMultiple / 2)) >> kRawAverageBitShift), "Raw value does not match the average."); @@ -81,10 +82,7 @@ void VerifyRawRssValue(int8_t aAverage, uint16_t aRawValue) } // This function prints the values in the passed in link info instance. It is invoked as the final step in test-case. -void PrintOutcome(LinkQualityInfo &aLinkInfo) -{ - printf("%s -> PASS \n", aLinkInfo.ToInfoString().AsCString()); -} +void PrintOutcome(LinkQualityInfo &aLinkInfo) { printf("%s -> PASS \n", aLinkInfo.ToInfoString().AsCString()); } void TestLinkQualityData(RssTestData aRssData) { @@ -125,7 +123,7 @@ void VerifyRawRssValue(RssAverager &aRssAverager) int8_t average = aRssAverager.GetAverage(); uint16_t rawValue = aRssAverager.GetRaw(); - if (average != OT_RADIO_RSSI_INVALID) + if (average != Radio::kInvalidRssi) { VerifyOrQuit(average == -static_cast((rawValue + (kRawAverageMultiple / 2)) >> kRawAverageBitShift), "Raw value does not match the average."); @@ -137,10 +135,7 @@ void VerifyRawRssValue(RssAverager &aRssAverager) } // This function prints the values in the passed link info instance. It is invoked as the final step in test-case. -void PrintOutcome(RssAverager &aRssAverager) -{ - printf("%s -> PASS\n", aRssAverager.ToString().AsCString()); -} +void PrintOutcome(RssAverager &aRssAverager) { printf("%s -> PASS\n", aRssAverager.ToString().AsCString()); } int8_t GetRandomRss(void) { @@ -165,7 +160,7 @@ void TestRssAveraging(void) rssAverager.Clear(); printf("\nAfter Clear: "); - VerifyOrQuit(rssAverager.GetAverage() == OT_RADIO_RSSI_INVALID, "Initial value from GetAverage() is incorrect."); + VerifyOrQuit(rssAverager.GetAverage() == Radio::kInvalidRssi, "Initial value from GetAverage() is incorrect."); VerifyRawRssValue(rssAverager); PrintOutcome(rssAverager); @@ -183,7 +178,7 @@ void TestRssAveraging(void) printf("Clear(): "); rssAverager.Clear(); - VerifyOrQuit(rssAverager.GetAverage() == OT_RADIO_RSSI_INVALID, "GetAverage() after Clear() is incorrect."); + VerifyOrQuit(rssAverager.GetAverage() == Radio::kInvalidRssi, "GetAverage() after Clear() is incorrect."); VerifyRawRssValue(rssAverager); PrintOutcome(rssAverager); @@ -338,30 +333,30 @@ void TestLinkQualityCalculations(void) { const int8_t rssList1[] = {-81, -80, -79, -78, -76, -80, -77, -75, -77, -76, -77, -74}; const RssTestData rssData1 = { - rssList1, // mRssList - sizeof(rssList1), // mRssListSize - 3 // mExpectedLinkQuality + rssList1, // mRssList + sizeof(rssList1), // mRssListSize + 3 // mExpectedLinkQuality }; const int8_t rssList2[] = {-90, -80, -85}; const RssTestData rssData2 = { - rssList2, // mRssList - sizeof(rssList2), // mRssListSize - 2 // mExpectedLinkQuality + rssList2, // mRssList + sizeof(rssList2), // mRssListSize + 2 // mExpectedLinkQuality }; const int8_t rssList3[] = {-95, -96, -98, -99, -100, -100, -98, -99, -100, -100, -100, -100, -100}; const RssTestData rssData3 = { - rssList3, // mRssList - sizeof(rssList3), // mRssListSize - 0 // mExpectedLinkQuality + rssList3, // mRssList + sizeof(rssList3), // mRssListSize + 0 // mExpectedLinkQuality }; const int8_t rssList4[] = {-75, -100, -100, -100, -100, -100, -95, -92, -93, -94, -93, -93}; const RssTestData rssData4 = { - rssList4, // mRssList - sizeof(rssList4), // mRssListSize - 1 // mExpectedLinkQuality + rssList4, // mRssList + sizeof(rssList4), // mRssListSize + 1 // mExpectedLinkQuality }; TestLinkQualityData(rssData1); @@ -481,6 +476,53 @@ void TestSuccessRateTracker(void) } } +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE + +class UnitTester +{ +public: + static void TestLinkMetricsScaling(void) + { + printf("\nTestLinkMetricsScaling\n"); + + // Test Link Margin scaling from [0,130] -> [0, 255] + + for (uint8_t linkMargin = 0; linkMargin <= 130; linkMargin++) + { + double scaled = 255.0 / 130.0 * linkMargin; + uint8_t scaledAsU8 = static_cast(scaled + 0.5); + + printf("\nLinkMargin : %-3u -> Scaled : %.1f (rounded:%u)", linkMargin, scaled, scaledAsU8); + + VerifyOrQuit(LinkMetrics::ScaleLinkMarginToRawValue(linkMargin) == scaledAsU8); + VerifyOrQuit(LinkMetrics::ScaleRawValueToLinkMargin(scaledAsU8) == linkMargin); + } + + VerifyOrQuit(LinkMetrics::ScaleLinkMarginToRawValue(131) == 255); + VerifyOrQuit(LinkMetrics::ScaleLinkMarginToRawValue(150) == 255); + VerifyOrQuit(LinkMetrics::ScaleLinkMarginToRawValue(255) == 255); + + // Test RSSI scaling from [-130, 0] -> [0, 255] + + for (int8_t rssi = -128; rssi <= 0; rssi++) + { + double scaled = 255.0 / 130.0 * (rssi + 130.0); + uint8_t scaledAsU8 = static_cast(scaled + 0.5); + + printf("\nRSSI : %-3d -> Scaled :%.1f (rounded:%u)", rssi, scaled, scaledAsU8); + + VerifyOrQuit(LinkMetrics::ScaleRssiToRawValue(rssi) == scaledAsU8); + VerifyOrQuit(LinkMetrics::ScaleRawValueToRssi(scaledAsU8) == rssi); + } + + VerifyOrQuit(LinkMetrics::ScaleRssiToRawValue(1) == 255); + VerifyOrQuit(LinkMetrics::ScaleRssiToRawValue(10) == 255); + VerifyOrQuit(LinkMetrics::ScaleRssiToRawValue(127) == 255); + } +}; + +#endif + } // namespace ot int main(void) @@ -488,6 +530,10 @@ int main(void) ot::TestRssAveraging(); ot::TestLinkQualityCalculations(); ot::TestSuccessRateTracker(); +#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE + ot::UnitTester::TestLinkMetricsScaling(); +#endif + printf("\nAll tests passed\n"); return 0; } diff --git a/tests/unit/test_linked_list.cpp b/tests/unit/test_linked_list.cpp index f84f63dcea3..40a4f7f0517 100644 --- a/tests/unit/test_linked_list.cpp +++ b/tests/unit/test_linked_list.cpp @@ -88,8 +88,8 @@ constexpr Entry::Type kBetaType = Entry::Type::kBeta; void VerifyLinkedListContent(const LinkedList *aList, ...) { va_list args; - Entry * argEntry; - Entry * argPrev = nullptr; + Entry *argEntry; + Entry *argPrev = nullptr; const Entry *prev; uint16_t unusedId = 100; @@ -134,7 +134,7 @@ void TestLinkedList(void) { Entry a("a", 1, kAlphaType), b("b", 2, kAlphaType), c("c", 3, kBetaType); Entry d("d", 4, kBetaType), e("e", 5, kAlphaType), f("f", 6, kBetaType); - Entry * prev; + Entry *prev; LinkedList list; LinkedList removedList; diff --git a/tests/unit/test_lowpan.cpp b/tests/unit/test_lowpan.cpp index beb6981fc1e..c28f8fda84d 100644 --- a/tests/unit/test_lowpan.cpp +++ b/tests/unit/test_lowpan.cpp @@ -35,8 +35,8 @@ using namespace ot; namespace ot { -ot::Instance * sInstance; -Ip6::Ip6 * sIp6; +ot::Instance *sInstance; +Ip6::Ip6 *sIp6; Lowpan::Lowpan *sLowpan; void TestIphcVector::GetCompressedStream(uint8_t *aIphc, uint16_t &aIphcLength) @@ -129,7 +129,8 @@ static void Init(void) SuccessOrQuit(message->AppendBytes(mockNetworkData, sizeof(mockNetworkData))); - IgnoreError(sInstance->Get().SetNetworkData(0, 0, NetworkData::kStableSubset, *message, 0)); + IgnoreError( + sInstance->Get().SetNetworkData(0, 0, NetworkData::kStableSubset, *message, 2, 0x20)); } /** @@ -145,7 +146,7 @@ static void Init(void) */ static void Test(TestIphcVector &aVector, bool aCompress, bool aDecompress) { - Message * message = nullptr; + Message *message = nullptr; uint8_t result[512]; uint8_t iphc[512]; uint8_t ip6[512]; @@ -172,7 +173,7 @@ static void Test(TestIphcVector &aVector, bool aCompress, bool aDecompress) if (aCompress) { FrameBuilder frameBuilder; - Message * compressedMsg; + Message *compressedMsg; Ip6::Ecn ecn; frameBuilder.Init(result, 127); @@ -181,8 +182,7 @@ static void Test(TestIphcVector &aVector, bool aCompress, bool aDecompress) aVector.GetUncompressedStream(*message); - VerifyOrQuit(sLowpan->Compress(*message, aVector.mMacSource, aVector.mMacDestination, frameBuilder) == - aVector.mError); + VerifyOrQuit(sLowpan->Compress(*message, aVector.mMacAddrs, frameBuilder) == aVector.mError); if (aVector.mError == kErrorNone) { @@ -229,7 +229,7 @@ static void Test(TestIphcVector &aVector, bool aCompress, bool aDecompress) frameData.Init(iphc, iphcLength); - error = sLowpan->Decompress(*message, aVector.mMacSource, aVector.mMacDestination, frameData, 0); + error = sLowpan->Decompress(*message, aVector.mMacAddrs, frameData, 0); message->ReadBytes(0, result, message->GetLength()); @@ -1870,6 +1870,7 @@ void TestLowpanMeshHeader(void) uint8_t frame[kMaxFrameSize]; uint16_t length; FrameData frameData; + FrameBuilder frameBuilder; Lowpan::MeshHeader meshHeader; meshHeader.Init(kSourceAddr, kDestAddr, 1); @@ -1877,10 +1878,12 @@ void TestLowpanMeshHeader(void) VerifyOrQuit(meshHeader.GetDestination() == kDestAddr, "failed after Init()"); VerifyOrQuit(meshHeader.GetHopsLeft() == 1, "failed after Init()"); - length = meshHeader.WriteTo(frame); + frameBuilder.Init(frame, sizeof(frame)); + SuccessOrQuit(meshHeader.AppendTo(frameBuilder)); + length = frameBuilder.GetLength(); VerifyOrQuit(length == meshHeader.GetHeaderLength()); - VerifyOrQuit(length == sizeof(kMeshHeader1), "MeshHeader::WriteTo() returned length is incorrect"); - VerifyOrQuit(memcmp(frame, kMeshHeader1, length) == 0, "MeshHeader::WriteTo() failed"); + VerifyOrQuit(length == sizeof(kMeshHeader1), "MeshHeader::AppendTo() returned length is incorrect"); + VerifyOrQuit(memcmp(frame, kMeshHeader1, length) == 0, "MeshHeader::AppendTo() failed"); memset(&meshHeader, 0, sizeof(meshHeader)); frameData.Init(frame, length); @@ -1903,10 +1906,12 @@ void TestLowpanMeshHeader(void) VerifyOrQuit(meshHeader.GetDestination() == kDestAddr, "failed after Init()"); VerifyOrQuit(meshHeader.GetHopsLeft() == 0x20, "failed after Init()"); - length = meshHeader.WriteTo(frame); - VerifyOrQuit(length == sizeof(kMeshHeader2), "MeshHeader::WriteTo() returned length is incorrect"); + frameBuilder.Init(frame, sizeof(frame)); + SuccessOrQuit(meshHeader.AppendTo(frameBuilder)); + length = frameBuilder.GetLength(); + VerifyOrQuit(length == sizeof(kMeshHeader2), "MeshHeader::AppendTo() returned length is incorrect"); VerifyOrQuit(length == meshHeader.GetHeaderLength()); - VerifyOrQuit(memcmp(frame, kMeshHeader2, length) == 0, "MeshHeader::WriteTo() failed"); + VerifyOrQuit(memcmp(frame, kMeshHeader2, length) == 0, "MeshHeader::AppendTo() failed"); memset(&meshHeader, 0, sizeof(meshHeader)); frameData.Init(frame, length); @@ -1933,7 +1938,9 @@ void TestLowpanMeshHeader(void) VerifyOrQuit(meshHeader.GetDestination() == kDestAddr, "failed after ParseFrom()"); VerifyOrQuit(meshHeader.GetHopsLeft() == 1, "failed after ParseFrom()"); - VerifyOrQuit(meshHeader.WriteTo(frame) == sizeof(kMeshHeader1)); + frameBuilder.Init(frame, sizeof(frame)); + SuccessOrQuit(meshHeader.AppendTo(frameBuilder)); + VerifyOrQuit(frameBuilder.GetLength() == sizeof(kMeshHeader1)); frameData.Init(kMeshHeader3, sizeof(kMeshHeader3) - 1); VerifyOrQuit(meshHeader.ParseFrom(frameData) == kErrorParse, @@ -1942,13 +1949,10 @@ void TestLowpanMeshHeader(void) void TestLowpanFragmentHeader(void) { - enum - { - kMaxFrameSize = 127, - kSize = 0x7ef, - kTag = 0x1234, - kOffset = (100 * 8), - }; + static constexpr uint16_t kMaxFrameSize = 127; + static constexpr uint16_t kSize = 0x7ef; + static constexpr uint16_t kTag = 0x1234; + static constexpr uint16_t kOffset = (100 * 8); const uint8_t kFragHeader1[] = {0xc7, 0xef, 0x12, 0x34}; // size:0x7ef, tag:0x1234, offset:0 (first frag) const uint8_t kFragHeader2[] = {0xe7, 0xef, 0x12, 0x34, 0x64}; // size:0x7ef, tag:0x1234, offset:100 (next frag) @@ -1958,21 +1962,22 @@ void TestLowpanFragmentHeader(void) const uint8_t kInvalidFragHeader2[] = {0xd0, 0xef, 0x12, 0x34, 0x64}; const uint8_t kInvalidFragHeader3[] = {0x90, 0xef, 0x12, 0x34, 0x64}; - uint8_t frame[kMaxFrameSize]; - uint16_t length; - FrameData frameData; - Lowpan::FragmentHeader fragHeader; + uint8_t frame[kMaxFrameSize]; + uint16_t length; + FrameData frameData; + FrameBuilder frameBuilder; + Lowpan::FragmentHeader fragHeader; + Lowpan::FragmentHeader::FirstFrag firstFragHeader; + Lowpan::FragmentHeader::NextFrag nextFragHeader; + + frameBuilder.Init(frame, sizeof(frame)); - fragHeader.InitFirstFragment(kSize, kTag); - VerifyOrQuit(fragHeader.GetDatagramSize() == kSize, "failed after Init"); - VerifyOrQuit(fragHeader.GetDatagramTag() == kTag, "failed after Init()"); - VerifyOrQuit(fragHeader.GetDatagramOffset() == 0, "failed after Init()"); + firstFragHeader.Init(kSize, kTag); + SuccessOrQuit(frameBuilder.Append(firstFragHeader)); - length = fragHeader.WriteTo(frame); - VerifyOrQuit(length == Lowpan::FragmentHeader::kFirstFragmentHeaderSize, - "FragmentHeader::WriteTo() returned length is incorrect"); - VerifyOrQuit(length == sizeof(kFragHeader1), "FragmentHeader::WriteTo() returned length is incorrect"); - VerifyOrQuit(memcmp(frame, kFragHeader1, length) == 0, "FragmentHeader::WriteTo() failed"); + length = frameBuilder.GetLength(); + VerifyOrQuit(length == sizeof(Lowpan::FragmentHeader::FirstFrag)); + VerifyOrQuit(memcmp(frame, kFragHeader1, length) == 0); memset(&fragHeader, 0, sizeof(fragHeader)); @@ -1993,22 +1998,27 @@ void TestLowpanFragmentHeader(void) //- - - - - - - - - - - - - - - - - - - - - - - - - - - fragHeader.Init(kSize, kTag, kOffset); - VerifyOrQuit(fragHeader.GetDatagramSize() == kSize, "failed after Init"); - VerifyOrQuit(fragHeader.GetDatagramTag() == kTag, "failed after Init()"); - VerifyOrQuit(fragHeader.GetDatagramOffset() == kOffset, "failed after Init()"); + frameBuilder.Init(frame, sizeof(frame)); + nextFragHeader.Init(kSize, kTag, kOffset); + SuccessOrQuit(frameBuilder.Append(nextFragHeader)); + length = frameBuilder.GetLength(); + VerifyOrQuit(length == sizeof(kFragHeader2)); + VerifyOrQuit(memcmp(frame, kFragHeader2, length) == 0); // Check the truncation of offset (to be multiple of 8). - fragHeader.Init(kSize, kTag, kOffset + 1); - VerifyOrQuit(fragHeader.GetDatagramOffset() == kOffset, "FragmentHeader::GetDatagramOffset() did not truncate"); - fragHeader.Init(kSize, kTag, kOffset + 7); - VerifyOrQuit(fragHeader.GetDatagramOffset() == kOffset, "FragmentHeader::GetDatagramOffset() did not truncate"); - - length = fragHeader.WriteTo(frame); - VerifyOrQuit(length == Lowpan::FragmentHeader::kSubsequentFragmentHeaderSize, - "FragmentHeader::WriteTo() returned length is incorrect"); - VerifyOrQuit(length == sizeof(kFragHeader2), "FragmentHeader::WriteTo() returned length is incorrect"); - VerifyOrQuit(memcmp(frame, kFragHeader2, length) == 0, "FragmentHeader::WriteTo() failed"); + frameBuilder.Init(frame, sizeof(frame)); + nextFragHeader.Init(kSize, kTag, kOffset + 1); + SuccessOrQuit(frameBuilder.Append(nextFragHeader)); + length = frameBuilder.GetLength(); + VerifyOrQuit(length == sizeof(kFragHeader2)); + VerifyOrQuit(memcmp(frame, kFragHeader2, length) == 0); + + frameBuilder.Init(frame, sizeof(frame)); + nextFragHeader.Init(kSize, kTag, kOffset + 7); + SuccessOrQuit(frameBuilder.Append(nextFragHeader)); + length = frameBuilder.GetLength(); + VerifyOrQuit(length == sizeof(kFragHeader2)); + VerifyOrQuit(memcmp(frame, kFragHeader2, length) == 0); memset(&fragHeader, 0, sizeof(fragHeader)); frameData.Init(frame, length); diff --git a/tests/unit/test_lowpan.hpp b/tests/unit/test_lowpan.hpp index b06fb07cb09..1625dbc6514 100644 --- a/tests/unit/test_lowpan.hpp +++ b/tests/unit/test_lowpan.hpp @@ -73,7 +73,7 @@ class TestIphcVector * @param aAddress Pointer to the long MAC address. * */ - void SetMacSource(const uint8_t *aAddress) { mMacSource.SetExtended(aAddress); } + void SetMacSource(const uint8_t *aAddress) { mMacAddrs.mSource.SetExtended(aAddress); } /** * This method sets short MAC source address. @@ -81,7 +81,7 @@ class TestIphcVector * @param aAddress Short MAC address. * */ - void SetMacSource(uint16_t aAddress) { mMacSource.SetShort(aAddress); } + void SetMacSource(uint16_t aAddress) { mMacAddrs.mSource.SetShort(aAddress); } /** * This method sets long MAC destination address. @@ -89,7 +89,7 @@ class TestIphcVector * @param aAddress Pointer to the long MAC address. * */ - void SetMacDestination(const uint8_t *aAddress) { mMacDestination.SetExtended(aAddress); } + void SetMacDestination(const uint8_t *aAddress) { mMacAddrs.mDestination.SetExtended(aAddress); } /** * This method sets short MAC destination address. @@ -97,7 +97,7 @@ class TestIphcVector * @param aAddress Short MAC address. * */ - void SetMacDestination(uint16_t aAddress) { mMacDestination.SetShort(aAddress); } + void SetMacDestination(uint16_t aAddress) { mMacAddrs.mDestination.SetShort(aAddress); } /** * This method gets the IPv6 header @@ -261,8 +261,7 @@ class TestIphcVector * This fields represent uncompressed IPv6 packet. * */ - Mac::Address mMacSource; - Mac::Address mMacDestination; + Mac::Addresses mMacAddrs; Ip6::Header mIpHeader; Payload mExtHeader; Ip6::Header mIpTunneledHeader; diff --git a/tests/unit/test_mac_frame.cpp b/tests/unit/test_mac_frame.cpp index 5aac167cc1a..7966b70e975 100644 --- a/tests/unit/test_mac_frame.cpp +++ b/tests/unit/test_mac_frame.cpp @@ -53,12 +53,36 @@ bool CompareReversed(const uint8_t *aFirst, const uint8_t *aSecond, uint16_t aLe return matches; } +bool CompareAddresses(const Mac::Address &aFirst, const Mac::Address &aSecond) +{ + bool matches = false; + + VerifyOrExit(aFirst.GetType() == aSecond.GetType()); + + switch (aFirst.GetType()) + { + case Mac::Address::kTypeNone: + break; + case Mac::Address::kTypeShort: + VerifyOrExit(aFirst.GetShort() == aSecond.GetShort()); + break; + case Mac::Address::kTypeExtended: + VerifyOrExit(aFirst.GetExtended() == aSecond.GetExtended()); + break; + } + + matches = true; + +exit: + return matches; +} + void TestMacAddress(void) { const uint8_t kExtAddr[OT_EXT_ADDRESS_SIZE] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}; const Mac::ShortAddress kShortAddr = 0x1234; - ot::Instance * instance; + ot::Instance *instance; Mac::Address addr; Mac::ExtAddress extAddr; uint8_t buffer[OT_EXT_ADDRESS_SIZE]; @@ -156,57 +180,186 @@ void TestMacAddress(void) void TestMacHeader(void) { - static const struct + enum AddrType : uint8_t + { + kNoneAddr, + kShrtAddr, + kExtdAddr, + }; + + enum PanIdMode { - uint16_t fcf; - uint8_t secCtl; - uint8_t headerLength; - uint8_t footerLength; - } tests[] = { - {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrNone | Mac::Frame::kFcfSrcAddrNone, 0, 3, 2}, - {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrNone | Mac::Frame::kFcfSrcAddrShort, 0, 7, 2}, - {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrNone | Mac::Frame::kFcfSrcAddrExt, 0, 13, 2}, - {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrNone, 0, 7, 2}, - {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrNone, 0, 13, 2}, - {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort, 0, 11, 2}, - {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrExt, 0, 17, 2}, - {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrShort, 0, 17, 2}, - {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrExt, 0, 23, 2}, - - {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort | - Mac::Frame::kFcfPanidCompression, - 0, 9, 2}, - {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrExt | - Mac::Frame::kFcfPanidCompression, - 0, 15, 2}, - {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrShort | - Mac::Frame::kFcfPanidCompression, - 0, 15, 2}, - {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrExt | Mac::Frame::kFcfSrcAddrExt | - Mac::Frame::kFcfPanidCompression, - 0, 21, 2}, - - {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort | - Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfSecurityEnabled, - Mac::Frame::kSecMic32 | Mac::Frame::kKeyIdMode1, 15, 6}, - {Mac::Frame::kFcfFrameVersion2006 | Mac::Frame::kFcfDstAddrShort | Mac::Frame::kFcfSrcAddrShort | - Mac::Frame::kFcfPanidCompression | Mac::Frame::kFcfSecurityEnabled, - Mac::Frame::kSecMic32 | Mac::Frame::kKeyIdMode2, 19, 6}, + kSamePanIds, + kDiffPanIds, }; - for (const auto &test : tests) + struct TestCase { - uint8_t psdu[OT_RADIO_FRAME_MAX_SIZE]; - Mac::TxFrame frame; + Mac::Frame::Version mVersion; + AddrType mSrcAddrType; + AddrType mDstAddrType; + PanIdMode mPanIdMode; + Mac::Frame::SecurityLevel mSecurity; + Mac::Frame::KeyIdMode mKeyIdMode; + uint8_t mHeaderLength; + uint8_t mFooterLength; + }; + + static constexpr Mac::Frame::Version kVer2006 = Mac::Frame::kVersion2006; + static constexpr Mac::Frame::Version kVer2015 = Mac::Frame::kVersion2015; + + static constexpr Mac::Frame::SecurityLevel kNoSec = Mac::Frame::kSecurityNone; + static constexpr Mac::Frame::SecurityLevel kMic32 = Mac::Frame::kSecurityMic32; + + static constexpr Mac::Frame::KeyIdMode kModeId1 = Mac::Frame::kKeyIdMode1; + static constexpr Mac::Frame::KeyIdMode kModeId2 = Mac::Frame::kKeyIdMode2; + + static constexpr TestCase kTestCases[] = { + {kVer2006, kNoneAddr, kNoneAddr, kSamePanIds, kNoSec, kModeId1, 3, 2}, + {kVer2006, kShrtAddr, kNoneAddr, kSamePanIds, kNoSec, kModeId1, 7, 2}, + {kVer2006, kExtdAddr, kNoneAddr, kSamePanIds, kNoSec, kModeId1, 13, 2}, + {kVer2006, kNoneAddr, kShrtAddr, kSamePanIds, kNoSec, kModeId1, 7, 2}, + {kVer2006, kNoneAddr, kExtdAddr, kSamePanIds, kNoSec, kModeId1, 13, 2}, + {kVer2006, kShrtAddr, kShrtAddr, kDiffPanIds, kNoSec, kModeId1, 11, 2}, + {kVer2006, kShrtAddr, kExtdAddr, kDiffPanIds, kNoSec, kModeId1, 17, 2}, + {kVer2006, kExtdAddr, kShrtAddr, kDiffPanIds, kNoSec, kModeId1, 17, 2}, + {kVer2006, kExtdAddr, kExtdAddr, kDiffPanIds, kNoSec, kModeId1, 23, 2}, + {kVer2006, kShrtAddr, kShrtAddr, kSamePanIds, kNoSec, kModeId1, 9, 2}, + {kVer2006, kShrtAddr, kExtdAddr, kSamePanIds, kNoSec, kModeId1, 15, 2}, + {kVer2006, kExtdAddr, kShrtAddr, kSamePanIds, kNoSec, kModeId1, 15, 2}, + {kVer2006, kExtdAddr, kExtdAddr, kSamePanIds, kNoSec, kModeId1, 21, 2}, + {kVer2006, kShrtAddr, kShrtAddr, kSamePanIds, kMic32, kModeId1, 15, 6}, + {kVer2006, kShrtAddr, kShrtAddr, kSamePanIds, kMic32, kModeId2, 19, 6}, + + {kVer2015, kNoneAddr, kNoneAddr, kSamePanIds, kNoSec, kModeId1, 3, 2}, + {kVer2015, kShrtAddr, kNoneAddr, kSamePanIds, kNoSec, kModeId1, 7, 2}, + {kVer2015, kExtdAddr, kNoneAddr, kSamePanIds, kNoSec, kModeId1, 13, 2}, + {kVer2015, kNoneAddr, kShrtAddr, kSamePanIds, kNoSec, kModeId1, 7, 2}, + {kVer2015, kNoneAddr, kExtdAddr, kSamePanIds, kNoSec, kModeId1, 13, 2}, + {kVer2015, kShrtAddr, kShrtAddr, kDiffPanIds, kNoSec, kModeId1, 11, 2}, + {kVer2015, kShrtAddr, kExtdAddr, kDiffPanIds, kNoSec, kModeId1, 17, 2}, + {kVer2015, kExtdAddr, kShrtAddr, kDiffPanIds, kNoSec, kModeId1, 17, 2}, + {kVer2015, kShrtAddr, kShrtAddr, kSamePanIds, kNoSec, kModeId1, 9, 2}, + {kVer2015, kShrtAddr, kExtdAddr, kSamePanIds, kNoSec, kModeId1, 15, 2}, + {kVer2015, kExtdAddr, kShrtAddr, kSamePanIds, kNoSec, kModeId1, 15, 2}, + {kVer2015, kExtdAddr, kExtdAddr, kSamePanIds, kNoSec, kModeId1, 21, 2}, + {kVer2015, kShrtAddr, kShrtAddr, kSamePanIds, kMic32, kModeId1, 15, 6}, + {kVer2015, kShrtAddr, kShrtAddr, kSamePanIds, kMic32, kModeId2, 19, 6}, + }; + + const uint16_t kPanId1 = 0xbaba; + const uint16_t kPanId2 = 0xdede; + const uint16_t kShortAddr1 = 0x1234; + const uint16_t kShortAddr2 = 0x5678; + const uint8_t kExtAddr1[] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0}; + const uint8_t kExtAddr2[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + + Mac::ExtAddress extAddr1; + Mac::ExtAddress extAddr2; + + extAddr1.Set(kExtAddr1); + extAddr2.Set(kExtAddr2); + + printf("TestMacHeader\n"); + + for (const TestCase &testCase : kTestCases) + { + uint8_t psdu[OT_RADIO_FRAME_MAX_SIZE]; + Mac::TxFrame frame; + Mac::Addresses addresses; + Mac::Address address; + Mac::PanIds panIds; + Mac::PanId panId; frame.mPsdu = psdu; frame.mLength = 0; frame.mRadioType = 0; - frame.InitMacHeader(test.fcf, test.secCtl); - VerifyOrQuit(frame.GetHeaderLength() == test.headerLength); - VerifyOrQuit(frame.GetFooterLength() == test.footerLength); - VerifyOrQuit(frame.GetLength() == test.headerLength + test.footerLength); + switch (testCase.mSrcAddrType) + { + case kNoneAddr: + addresses.mSource.SetNone(); + break; + case kShrtAddr: + addresses.mSource.SetShort(kShortAddr1); + break; + case kExtdAddr: + addresses.mSource.SetExtended(extAddr1); + break; + } + + switch (testCase.mDstAddrType) + { + case kNoneAddr: + addresses.mDestination.SetNone(); + break; + case kShrtAddr: + addresses.mDestination.SetShort(kShortAddr2); + break; + case kExtdAddr: + addresses.mDestination.SetExtended(extAddr2); + break; + } + + switch (testCase.mPanIdMode) + { + case kSamePanIds: + panIds.mSource = panIds.mDestination = kPanId1; + break; + case kDiffPanIds: + panIds.mSource = kPanId1; + panIds.mDestination = kPanId2; + break; + } + + frame.InitMacHeader(Mac::Frame::kTypeData, testCase.mVersion, addresses, panIds, testCase.mSecurity, + testCase.mKeyIdMode); + + VerifyOrQuit(frame.GetHeaderLength() == testCase.mHeaderLength); + VerifyOrQuit(frame.GetFooterLength() == testCase.mFooterLength); + VerifyOrQuit(frame.GetLength() == testCase.mHeaderLength + testCase.mFooterLength); + + VerifyOrQuit(frame.GetType() == Mac::Frame::kTypeData); + VerifyOrQuit(!frame.IsAck()); + VerifyOrQuit(frame.GetVersion() == testCase.mVersion); + VerifyOrQuit(frame.GetSecurityEnabled() == (testCase.mSecurity != kNoSec)); + VerifyOrQuit(!frame.GetFramePending()); + VerifyOrQuit(!frame.IsIePresent()); + VerifyOrQuit(frame.GetAckRequest() == (testCase.mDstAddrType != kNoneAddr)); + + VerifyOrQuit(frame.IsSrcAddrPresent() == (testCase.mSrcAddrType != kNoneAddr)); + SuccessOrQuit(frame.GetSrcAddr(address)); + VerifyOrQuit(CompareAddresses(address, addresses.mSource)); + VerifyOrQuit(frame.IsDstAddrPresent() == (testCase.mDstAddrType != kNoneAddr)); + SuccessOrQuit(frame.GetDstAddr(address)); + VerifyOrQuit(CompareAddresses(address, addresses.mDestination)); + + if (testCase.mDstAddrType != kNoneAddr) + { + VerifyOrQuit(frame.IsDstPanIdPresent()); + SuccessOrQuit(frame.GetDstPanId(panId)); + VerifyOrQuit(panId == panIds.mDestination); + } + + if (frame.IsSrcPanIdPresent()) + { + SuccessOrQuit(frame.GetSrcPanId(panId)); + VerifyOrQuit(panId == panIds.mSource); + } + + if (frame.GetSecurityEnabled()) + { + uint8_t security; + uint8_t keyIdMode; + + SuccessOrQuit(frame.GetSecurityLevel(security)); + VerifyOrQuit(security == testCase.mSecurity); + + SuccessOrQuit(frame.GetKeyIdMode(keyIdMode)); + VerifyOrQuit(keyIdMode == testCase.mKeyIdMode); + } + + printf(" %d, %d\n", testCase.mHeaderLength, testCase.mFooterLength); } } @@ -358,7 +511,7 @@ void TestMacFrameApi(void) #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) uint8_t data_psdu1[] = {0x29, 0xee, 0x53, 0xce, 0xfa, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6e, 0x16, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6e, 0x16, 0x0d, 0x01, 0x00, 0x00, 0x00, 0x01}; + 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6e, 0x16, 0x0d, 0x01, 0x00, 0x00, 0x00, 0x01}; uint8_t mac_cmd_psdu2[] = {0x6b, 0xaa, 0x8d, 0xce, 0xfa, 0x00, 0x68, 0x01, 0x68, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x01, 0x04, 0x0d, 0xed, 0x0b, 0x35, 0x0c, 0x80, 0x3f, 0x04, 0x4b, 0x88, 0x89, 0xd6, 0x59, 0xe1}; @@ -381,14 +534,14 @@ void TestMacFrameApi(void) // FCS: 0x9bd2 (Correct) frame.mPsdu = ack_psdu1; frame.mLength = sizeof(ack_psdu1); - VerifyOrQuit(frame.GetType() == Mac::Frame::kFcfFrameAck); + VerifyOrQuit(frame.GetType() == Mac::Frame::kTypeAck); VerifyOrQuit(!frame.GetSecurityEnabled()); VerifyOrQuit(!frame.GetFramePending()); VerifyOrQuit(!frame.GetAckRequest()); VerifyOrQuit(!frame.IsIePresent()); VerifyOrQuit(!frame.IsDstPanIdPresent()); VerifyOrQuit(!frame.IsDstAddrPresent()); - VerifyOrQuit(frame.GetVersion() == Mac::Frame::kFcfFrameVersion2006); + VerifyOrQuit(frame.GetVersion() == Mac::Frame::kVersion2006); VerifyOrQuit(!frame.IsSrcAddrPresent()); VerifyOrQuit(frame.GetSequence() == 94); @@ -420,8 +573,8 @@ void TestMacFrameApi(void) frame.mPsdu = mac_cmd_psdu1; frame.mLength = sizeof(mac_cmd_psdu1); VerifyOrQuit(frame.GetSequence() == 133); - VerifyOrQuit(frame.GetVersion() == Mac::Frame::kFcfFrameVersion2006); - VerifyOrQuit(frame.GetType() == Mac::Frame::kFcfFrameMacCmd); + VerifyOrQuit(frame.GetVersion() == Mac::Frame::kVersion2006); + VerifyOrQuit(frame.GetType() == Mac::Frame::kTypeMacCmd); SuccessOrQuit(frame.GetCommandId(commandId)); VerifyOrQuit(commandId == Mac::Frame::kMacCmdDataRequest); SuccessOrQuit(frame.SetCommandId(Mac::Frame::kMacCmdBeaconRequest)); @@ -439,7 +592,7 @@ void TestMacFrameApi(void) frame.mLength = sizeof(mac_cmd_psdu2); VerifyOrQuit(frame.GetSequence() == 141); VerifyOrQuit(frame.IsVersion2015()); - VerifyOrQuit(frame.GetType() == Mac::Frame::kFcfFrameMacCmd); + VerifyOrQuit(frame.GetType() == Mac::Frame::kTypeMacCmd); SuccessOrQuit(frame.GetCommandId(commandId)); VerifyOrQuit(commandId == Mac::Frame::kMacCmdDataRequest); printf("commandId:%d\n", commandId); @@ -452,6 +605,8 @@ void TestMacFrameApi(void) void TestMacFrameAckGeneration(void) { + constexpr uint8_t kImmAckLength = 5; + Mac::RxFrame receivedFrame; Mac::TxFrame ackFrame; uint8_t ackFrameBuffer[100]; @@ -477,18 +632,18 @@ void TestMacFrameAckGeneration(void) // Destination: 16:6e:0a:00:00:00:00:01 (16:6e:0a:00:00:00:00:01) // Extended Source: 16:6e:0a:00:00:00:00:02 (16:6e:0a:00:00:00:00:02) uint8_t data_psdu1[] = {0x61, 0xdc, 0xbd, 0xce, 0xfa, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6e, 0x16, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6e, 0x16, 0x7f, 0x33, 0xf0, 0x4d, 0x4c, 0x4d, 0x4c, - 0x8b, 0xf0, 0x00, 0x15, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc2, - 0x57, 0x9c, 0x31, 0xb3, 0x2a, 0xa1, 0x86, 0xba, 0x9a, 0xed, 0x5a, 0xb9, 0xa3, 0x59, - 0x88, 0xeb, 0xbb, 0x0d, 0xc3, 0xed, 0xeb, 0x8a, 0x53, 0xa6, 0xed, 0xf7, 0xdd, 0x45, - 0x6e, 0xf7, 0x9a, 0x17, 0xb4, 0xab, 0xc6, 0x75, 0x71, 0x46, 0x37, 0x93, 0x4a, 0x32, - 0xb1, 0x21, 0x9f, 0x9d, 0xb3, 0x65, 0x27, 0xd5, 0xfc, 0x50, 0x16, 0x90, 0xd2, 0xd4}; + 0x00, 0x00, 0x00, 0x00, 0x0a, 0x6e, 0x16, 0x7f, 0x33, 0xf0, 0x4d, 0x4c, 0x4d, 0x4c, + 0x8b, 0xf0, 0x00, 0x15, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc2, + 0x57, 0x9c, 0x31, 0xb3, 0x2a, 0xa1, 0x86, 0xba, 0x9a, 0xed, 0x5a, 0xb9, 0xa3, 0x59, + 0x88, 0xeb, 0xbb, 0x0d, 0xc3, 0xed, 0xeb, 0x8a, 0x53, 0xa6, 0xed, 0xf7, 0xdd, 0x45, + 0x6e, 0xf7, 0x9a, 0x17, 0xb4, 0xab, 0xc6, 0x75, 0x71, 0x46, 0x37, 0x93, 0x4a, 0x32, + 0xb1, 0x21, 0x9f, 0x9d, 0xb3, 0x65, 0x27, 0xd5, 0xfc, 0x50, 0x16, 0x90, 0xd2, 0xd4}; receivedFrame.mPsdu = data_psdu1; receivedFrame.mLength = sizeof(data_psdu1); ackFrame.GenerateImmAck(receivedFrame, false); - VerifyOrQuit(ackFrame.mLength == Mac::Frame::kImmAckLength); - VerifyOrQuit(ackFrame.GetType() == Mac::Frame::kFcfFrameAck); + VerifyOrQuit(ackFrame.mLength == kImmAckLength); + VerifyOrQuit(ackFrame.GetType() == Mac::Frame::kTypeAck); VerifyOrQuit(!ackFrame.GetSecurityEnabled()); VerifyOrQuit(!ackFrame.GetFramePending()); @@ -497,7 +652,7 @@ void TestMacFrameAckGeneration(void) VerifyOrQuit(!ackFrame.IsDstPanIdPresent()); VerifyOrQuit(!ackFrame.IsDstAddrPresent()); VerifyOrQuit(!ackFrame.IsSrcAddrPresent()); - VerifyOrQuit(ackFrame.GetVersion() == Mac::Frame::kFcfFrameVersion2006); + VerifyOrQuit(ackFrame.GetVersion() == Mac::Frame::kVersion2006); VerifyOrQuit(ackFrame.GetSequence() == 189); #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) @@ -537,8 +692,8 @@ void TestMacFrameAckGeneration(void) // [Key Number: 0] // FCS: 0x8c40 (Correct) uint8_t data_psdu2[] = {0x69, 0xa8, 0x8e, 0xce, 0xfa, 0x02, 0x24, 0x00, 0x24, 0x0d, 0x02, - 0x00, 0x00, 0x00, 0x01, 0x6b, 0x64, 0x60, 0x08, 0x55, 0xb8, 0x10, - 0x18, 0xc7, 0x40, 0x2e, 0xfb, 0xf3, 0xda, 0xf9, 0x4e, 0x58, 0x70}; + 0x00, 0x00, 0x00, 0x01, 0x6b, 0x64, 0x60, 0x08, 0x55, 0xb8, 0x10, + 0x18, 0xc7, 0x40, 0x2e, 0xfb, 0xf3, 0xda, 0xf9, 0x4e, 0x58, 0x70}; receivedFrame.mPsdu = data_psdu2; receivedFrame.mLength = sizeof(data_psdu2); @@ -548,13 +703,13 @@ void TestMacFrameAckGeneration(void) IgnoreError(ackFrame.GenerateEnhAck(receivedFrame, false, ie_data, sizeof(ie_data))); csl = reinterpret_cast(ackFrame.GetHeaderIe(Mac::CslIe::kHeaderIeId) + sizeof(Mac::HeaderIe)); VerifyOrQuit(ackFrame.mLength == 23); - VerifyOrQuit(ackFrame.GetType() == Mac::Frame::kFcfFrameAck); + VerifyOrQuit(ackFrame.GetType() == Mac::Frame::kTypeAck); VerifyOrQuit(ackFrame.GetSecurityEnabled()); VerifyOrQuit(ackFrame.IsIePresent()); VerifyOrQuit(!ackFrame.IsDstPanIdPresent()); VerifyOrQuit(ackFrame.IsDstAddrPresent()); VerifyOrQuit(!ackFrame.IsSrcAddrPresent()); - VerifyOrQuit(ackFrame.GetVersion() == Mac::Frame::kFcfFrameVersion2015); + VerifyOrQuit(ackFrame.GetVersion() == Mac::Frame::kVersion2015); VerifyOrQuit(ackFrame.GetSequence() == 142); VerifyOrQuit(csl->GetPeriod() == 3125 && csl->GetPhase() == 3105); diff --git a/tests/unit/test_macros.cpp b/tests/unit/test_macros.cpp index 0db16ca2c75..a8ebdc289b4 100644 --- a/tests/unit/test_macros.cpp +++ b/tests/unit/test_macros.cpp @@ -30,55 +30,25 @@ #include "common/arg_macros.hpp" -static constexpr uint8_t NumberOfArgs(void) -{ - return 0; -} +static constexpr uint8_t NumberOfArgs(void) { return 0; } -static constexpr uint8_t NumberOfArgs(uint8_t) -{ - return 1; -} +static constexpr uint8_t NumberOfArgs(uint8_t) { return 1; } -static constexpr uint8_t NumberOfArgs(uint8_t, uint8_t) -{ - return 2; -} +static constexpr uint8_t NumberOfArgs(uint8_t, uint8_t) { return 2; } -static constexpr uint8_t NumberOfArgs(uint8_t, uint8_t, uint8_t) -{ - return 3; -} +static constexpr uint8_t NumberOfArgs(uint8_t, uint8_t, uint8_t) { return 3; } -static constexpr uint8_t NumberOfArgs(uint8_t, uint8_t, uint8_t, uint8_t) -{ - return 4; -} +static constexpr uint8_t NumberOfArgs(uint8_t, uint8_t, uint8_t, uint8_t) { return 4; } -static constexpr uint8_t NumberOfArgs(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t) -{ - return 5; -} +static constexpr uint8_t NumberOfArgs(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t) { return 5; } -static constexpr uint8_t NumberOfArgs(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t) -{ - return 6; -} +static constexpr uint8_t NumberOfArgs(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t) { return 6; } -static constexpr uint8_t NumberOfArgs(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t) -{ - return 7; -} +static constexpr uint8_t NumberOfArgs(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t) { return 7; } -int Sum(int aFirst) -{ - return aFirst; -} +int Sum(int aFirst) { return aFirst; } -template int Sum(int aFirst, Args... aArgs) -{ - return aFirst + Sum(aArgs...); -} +template int Sum(int aFirst, Args... aArgs) { return aFirst + Sum(aArgs...); } void TestMacros(void) { diff --git a/tests/unit/test_message.cpp b/tests/unit/test_message.cpp index fe2ce3f13bb..d6aa9d330ec 100644 --- a/tests/unit/test_message.cpp +++ b/tests/unit/test_message.cpp @@ -46,10 +46,10 @@ void TestMessage(void) kLengthStep = 21, }; - Instance * instance; + Instance *instance; MessagePool *messagePool; - Message * message; - Message * message2; + Message *message; + Message *message2; uint8_t writeBuffer[kMaxSize]; uint8_t readBuffer[kMaxSize]; uint8_t zeroBuffer[kMaxSize]; @@ -134,63 +134,90 @@ void TestMessage(void) VerifyOrQuit(message->GetLength() == kMaxSize); - // Test `Message::CopyTo()` behavior. + // Test `WriteBytesFromMessage()` behavior copying between different + // messages. VerifyOrQuit((message2 = messagePool->Allocate(Message::kTypeIp6)) != nullptr); SuccessOrQuit(message2->SetLength(kMaxSize)); - for (uint16_t srcOffset = 0; srcOffset < kMaxSize; srcOffset += kOffsetStep) + for (uint16_t readOffset = 0; readOffset < kMaxSize; readOffset += kOffsetStep) { - for (uint16_t dstOffset = 0; dstOffset < kMaxSize; dstOffset += kOffsetStep) + for (uint16_t writeOffset = 0; writeOffset < kMaxSize; writeOffset += kOffsetStep) { - for (uint16_t length = 0; length <= kMaxSize - dstOffset; length += kLengthStep) + for (uint16_t length = 0; length <= kMaxSize - Max(writeOffset, readOffset); length += kLengthStep) { - uint16_t bytesCopied; - message2->WriteBytes(0, zeroBuffer, kMaxSize); - bytesCopied = message->CopyTo(srcOffset, dstOffset, length, *message2); - - if (srcOffset + length <= kMaxSize) - { - VerifyOrQuit(bytesCopied == length, "CopyTo() failed"); - } - else - { - VerifyOrQuit(bytesCopied == kMaxSize - srcOffset, "CopyTo() failed"); - } + message2->WriteBytesFromMessage(writeOffset, *message, readOffset, length); SuccessOrQuit(message2->Read(0, readBuffer, kMaxSize)); - VerifyOrQuit(memcmp(&readBuffer[0], zeroBuffer, dstOffset) == 0, "read before length"); - VerifyOrQuit(memcmp(&readBuffer[dstOffset], &writeBuffer[srcOffset], bytesCopied) == 0); - VerifyOrQuit( - memcmp(&readBuffer[dstOffset + bytesCopied], zeroBuffer, kMaxSize - bytesCopied - dstOffset) == 0, - "read after length"); + VerifyOrQuit(memcmp(&readBuffer[0], zeroBuffer, writeOffset) == 0); + VerifyOrQuit(memcmp(&readBuffer[writeOffset], &writeBuffer[readOffset], length) == 0); + VerifyOrQuit(memcmp(&readBuffer[writeOffset + length], zeroBuffer, kMaxSize - length - writeOffset) == + 0); - VerifyOrQuit(message->CompareBytes(srcOffset, *message2, dstOffset, bytesCopied)); - VerifyOrQuit(message2->CompareBytes(dstOffset, *message, srcOffset, bytesCopied)); + VerifyOrQuit(message->CompareBytes(readOffset, *message2, writeOffset, length)); + VerifyOrQuit(message2->CompareBytes(writeOffset, *message, readOffset, length)); } } } - // Verify `CopyTo()` with same source and destination message and a backward copy. + // Verify `WriteBytesFromMessage()` behavior copying backwards within + // same message. - for (uint16_t srcOffset = 0; srcOffset < kMaxSize; srcOffset++) + for (uint16_t readOffset = 0; readOffset < kMaxSize; readOffset++) { - uint16_t bytesCopied; + uint16_t length = kMaxSize - readOffset; message->WriteBytes(0, writeBuffer, kMaxSize); - bytesCopied = message->CopyTo(srcOffset, 0, kMaxSize, *message); - VerifyOrQuit(bytesCopied == kMaxSize - srcOffset, "CopyTo() failed"); + message->WriteBytesFromMessage(0, *message, readOffset, length); SuccessOrQuit(message->Read(0, readBuffer, kMaxSize)); - VerifyOrQuit(memcmp(&readBuffer[0], &writeBuffer[srcOffset], bytesCopied) == 0, - "CopyTo() changed before srcOffset"); - VerifyOrQuit(memcmp(&readBuffer[bytesCopied], &writeBuffer[bytesCopied], kMaxSize - bytesCopied) == 0, - "CopyTo() write error"); + VerifyOrQuit(memcmp(&readBuffer[0], &writeBuffer[readOffset], length) == 0); + VerifyOrQuit(memcmp(&readBuffer[length], &writeBuffer[length], kMaxSize - length) == 0); + } + + // Verify `WriteBytesFromMessage()` behavior copying forward within + // same message. + + for (uint16_t writeOffset = 0; writeOffset < kMaxSize; writeOffset++) + { + uint16_t length = kMaxSize - writeOffset; + + message->WriteBytes(0, writeBuffer, kMaxSize); + + message->WriteBytesFromMessage(writeOffset, *message, 0, length); + + SuccessOrQuit(message->Read(0, readBuffer, kMaxSize)); + + VerifyOrQuit(memcmp(&readBuffer[0], &writeBuffer[0], writeOffset) == 0); + VerifyOrQuit(memcmp(&readBuffer[writeOffset], &writeBuffer[0], length) == 0); + } + + // Test `WriteBytesFromMessage()` behavior copying within same + // message at different read and write offsets and lengths. + + for (uint16_t readOffset = 0; readOffset < kMaxSize; readOffset += kOffsetStep) + { + for (uint16_t writeOffset = 0; writeOffset < kMaxSize; writeOffset += kOffsetStep) + { + for (uint16_t length = 0; length <= kMaxSize - Max(writeOffset, readOffset); length += kLengthStep) + { + message->WriteBytes(0, writeBuffer, kMaxSize); + + message->WriteBytesFromMessage(writeOffset, *message, readOffset, length); + + SuccessOrQuit(message->Read(0, readBuffer, kMaxSize)); + + VerifyOrQuit(memcmp(&readBuffer[0], writeBuffer, writeOffset) == 0); + VerifyOrQuit(memcmp(&readBuffer[writeOffset], &writeBuffer[readOffset], length) == 0); + VerifyOrQuit(memcmp(&readBuffer[writeOffset + length], &writeBuffer[writeOffset + length], + kMaxSize - length - writeOffset) == 0); + } + } } // Verify `AppendBytesFromMessage()` with two different messages as source and destination. @@ -236,6 +263,49 @@ void TestMessage(void) message->Free(); message2->Free(); + // Verify `RemoveHeader()` + + for (uint16_t offset = 0; offset < kMaxSize; offset += kOffsetStep) + { + for (uint16_t length = 0; length <= kMaxSize - offset; length += kLengthStep) + { + VerifyOrQuit((message = messagePool->Allocate(Message::kTypeIp6)) != nullptr); + SuccessOrQuit(message->AppendBytes(writeBuffer, kMaxSize)); + + message->RemoveHeader(offset, length); + + VerifyOrQuit(message->GetLength() == kMaxSize - length); + + SuccessOrQuit(message->Read(0, readBuffer, kMaxSize - length)); + + VerifyOrQuit(memcmp(&readBuffer[0], &writeBuffer[0], offset) == 0); + VerifyOrQuit(memcmp(&readBuffer[offset], &writeBuffer[offset + length], kMaxSize - length - offset) == 0); + message->Free(); + } + } + + // Verify `InsertHeader()` + + for (uint16_t offset = 0; offset < kMaxSize; offset += kOffsetStep) + { + for (uint16_t length = 0; length <= kMaxSize; length += kLengthStep) + { + VerifyOrQuit((message = messagePool->Allocate(Message::kTypeIp6)) != nullptr); + SuccessOrQuit(message->AppendBytes(writeBuffer, kMaxSize)); + + SuccessOrQuit(message->InsertHeader(offset, length)); + + VerifyOrQuit(message->GetLength() == kMaxSize + length); + + SuccessOrQuit(message->Read(0, readBuffer, offset)); + VerifyOrQuit(memcmp(&readBuffer[0], &writeBuffer[0], offset) == 0); + + SuccessOrQuit(message->Read(offset + length, readBuffer, kMaxSize - offset)); + VerifyOrQuit(memcmp(&readBuffer[0], &writeBuffer[offset], kMaxSize - offset) == 0); + message->Free(); + } + } + testFreeInstance(instance); } @@ -246,8 +316,8 @@ void TestAppender(void) static constexpr uint16_t kMaxBufferSize = sizeof(kData1) * 2 + sizeof(kData2); - Instance * instance; - Message * message; + Instance *instance; + Message *message; uint8_t buffer[kMaxBufferSize]; uint8_t zeroBuffer[kMaxBufferSize]; Appender bufAppender(buffer, sizeof(buffer)); diff --git a/tests/unit/test_message_queue.cpp b/tests/unit/test_message_queue.cpp index b8cedc6bf03..9eb3c77219c 100644 --- a/tests/unit/test_message_queue.cpp +++ b/tests/unit/test_message_queue.cpp @@ -40,7 +40,7 @@ #define kNumTestMessages 5 -static ot::Instance * sInstance; +static ot::Instance *sInstance; static ot::MessagePool *sMessagePool; // This function verifies the content of the message queue to match the passed in messages @@ -48,8 +48,8 @@ void VerifyMessageQueueContent(ot::MessageQueue &aMessageQueue, int aExpectedLen { const ot::MessageQueue &constQueue = aMessageQueue; va_list args; - ot::Message * message; - ot::Message * msgArg; + ot::Message *message; + ot::Message *msgArg; va_start(args, aExpectedLength); @@ -103,7 +103,7 @@ void VerifyMessageQueueContent(ot::MessageQueue &aMessageQueue, int aExpectedLen void TestMessageQueue(void) { ot::MessageQueue messageQueue; - ot::Message * messages[kNumTestMessages]; + ot::Message *messages[kNumTestMessages]; ot::MessageQueue::Info info; sInstance = testInitInstance(); @@ -280,8 +280,8 @@ void VerifyMessageQueueContentUsingOtApi(otMessageQueue *aQueue, int aExpectedLe // This test checks all the OpenThread C APIs for `otMessageQueue` void TestMessageQueueOtApis(void) { - otMessage * messages[kNumTestMessages]; - otMessage * message; + otMessage *messages[kNumTestMessages]; + otMessage *message; otMessageQueue queue, queue2; sInstance = testInitInstance(); diff --git a/tests/unit/test_mle.cpp b/tests/unit/test_mle.cpp new file mode 100644 index 00000000000..e7e1748e819 --- /dev/null +++ b/tests/unit/test_mle.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2023, 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. + */ + +#include + +#include "test_platform.h" +#include "test_util.hpp" + +#include "common/num_utils.hpp" +#include "thread/mle_types.hpp" + +namespace ot { + +#if OPENTHREAD_FTD +void TestDefaultDeviceProperties(void) +{ + Instance *instance; + const otDeviceProperties *props; + uint8_t weight; + + instance = static_cast(testInitInstance()); + VerifyOrQuit(instance != nullptr); + + props = otThreadGetDeviceProperties(instance); + + VerifyOrQuit(props->mPowerSupply == OPENTHREAD_CONFIG_DEVICE_POWER_SUPPLY); + VerifyOrQuit(!props->mSupportsCcm); + VerifyOrQuit(!props->mIsUnstable); + VerifyOrQuit(props->mLeaderWeightAdjustment == OPENTHREAD_CONFIG_MLE_DEFAULT_LEADER_WEIGHT_ADJUSTMENT); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + VerifyOrQuit(props->mIsBorderRouter); +#else + VerifyOrQuit(!props->mIsBorderRouter); +#endif + + weight = 64; + + switch (props->mPowerSupply) + { + case OT_POWER_SUPPLY_BATTERY: + weight -= 8; + break; + case OT_POWER_SUPPLY_EXTERNAL: + break; + case OT_POWER_SUPPLY_EXTERNAL_STABLE: + weight += 4; + break; + case OT_POWER_SUPPLY_EXTERNAL_UNSTABLE: + weight -= 4; + break; + } + + weight += props->mIsBorderRouter ? 1 : 0; + + VerifyOrQuit(otThreadGetLocalLeaderWeight(instance) == weight); + + printf("TestDefaultDeviceProperties passed\n"); +} + +void CompareDevicePropertiess(const otDeviceProperties &aFirst, const otDeviceProperties &aSecond) +{ + static constexpr int8_t kMinAdjustment = -16; + static constexpr int8_t kMaxAdjustment = +16; + + VerifyOrQuit(aFirst.mPowerSupply == aSecond.mPowerSupply); + VerifyOrQuit(aFirst.mIsBorderRouter == aSecond.mIsBorderRouter); + VerifyOrQuit(aFirst.mSupportsCcm == aSecond.mSupportsCcm); + VerifyOrQuit(aFirst.mIsUnstable == aSecond.mIsUnstable); + VerifyOrQuit(Clamp(aFirst.mLeaderWeightAdjustment, kMinAdjustment, kMaxAdjustment) == + Clamp(aSecond.mLeaderWeightAdjustment, kMinAdjustment, kMaxAdjustment)); +} + +void TestLeaderWeightCalculation(void) +{ + struct TestCase + { + otDeviceProperties mDeviceProperties; + uint8_t mExpectedLeaderWeight; + }; + + static const TestCase kTestCases[] = { + {{OT_POWER_SUPPLY_BATTERY, false, false, false, 0}, 56}, + {{OT_POWER_SUPPLY_EXTERNAL, false, false, false, 0}, 64}, + {{OT_POWER_SUPPLY_EXTERNAL_STABLE, false, false, false, 0}, 68}, + {{OT_POWER_SUPPLY_EXTERNAL_UNSTABLE, false, false, false, 0}, 60}, + + {{OT_POWER_SUPPLY_BATTERY, true, false, false, 0}, 57}, + {{OT_POWER_SUPPLY_EXTERNAL, true, false, false, 0}, 65}, + {{OT_POWER_SUPPLY_EXTERNAL_STABLE, true, false, false, 0}, 69}, + {{OT_POWER_SUPPLY_EXTERNAL_UNSTABLE, true, false, false, 0}, 61}, + + {{OT_POWER_SUPPLY_BATTERY, true, true, false, 0}, 64}, + {{OT_POWER_SUPPLY_EXTERNAL, true, true, false, 0}, 72}, + {{OT_POWER_SUPPLY_EXTERNAL_STABLE, true, true, false, 0}, 76}, + {{OT_POWER_SUPPLY_EXTERNAL_UNSTABLE, true, true, false, 0}, 68}, + + // Check when `mIsUnstable` is set. + {{OT_POWER_SUPPLY_BATTERY, false, false, true, 0}, 56}, + {{OT_POWER_SUPPLY_EXTERNAL, false, false, true, 0}, 60}, + {{OT_POWER_SUPPLY_EXTERNAL_STABLE, false, false, true, 0}, 64}, + {{OT_POWER_SUPPLY_EXTERNAL_UNSTABLE, false, false, true, 0}, 60}, + + {{OT_POWER_SUPPLY_BATTERY, true, false, true, 0}, 57}, + {{OT_POWER_SUPPLY_EXTERNAL, true, false, true, 0}, 61}, + {{OT_POWER_SUPPLY_EXTERNAL_STABLE, true, false, true, 0}, 65}, + {{OT_POWER_SUPPLY_EXTERNAL_UNSTABLE, true, false, true, 0}, 61}, + + // Include non-zero `mLeaderWeightAdjustment`. + {{OT_POWER_SUPPLY_BATTERY, true, false, false, 10}, 67}, + {{OT_POWER_SUPPLY_EXTERNAL, true, false, false, 10}, 75}, + {{OT_POWER_SUPPLY_EXTERNAL_STABLE, true, false, false, 10}, 79}, + {{OT_POWER_SUPPLY_EXTERNAL_UNSTABLE, true, false, false, 10}, 71}, + + {{OT_POWER_SUPPLY_BATTERY, false, false, false, -10}, 46}, + {{OT_POWER_SUPPLY_EXTERNAL, false, false, false, -10}, 54}, + {{OT_POWER_SUPPLY_EXTERNAL_STABLE, false, false, false, -10}, 58}, + {{OT_POWER_SUPPLY_EXTERNAL_UNSTABLE, false, false, false, -10}, 50}, + + // Use `mLeaderWeightAdjustment` larger than valid range + // Make sure it clamps to -16 and +16. + {{OT_POWER_SUPPLY_BATTERY, false, false, false, 20}, 72}, + {{OT_POWER_SUPPLY_EXTERNAL, false, false, false, 20}, 80}, + {{OT_POWER_SUPPLY_EXTERNAL_STABLE, false, false, false, 20}, 84}, + {{OT_POWER_SUPPLY_EXTERNAL_UNSTABLE, false, false, false, 20}, 76}, + + {{OT_POWER_SUPPLY_BATTERY, true, false, false, -20}, 41}, + {{OT_POWER_SUPPLY_EXTERNAL, true, false, false, -20}, 49}, + {{OT_POWER_SUPPLY_EXTERNAL_STABLE, true, false, false, -20}, 53}, + {{OT_POWER_SUPPLY_EXTERNAL_UNSTABLE, true, false, false, -20}, 45}, + }; + + Instance *instance; + + instance = static_cast(testInitInstance()); + VerifyOrQuit(instance != nullptr); + + for (const TestCase &testCase : kTestCases) + { + otThreadSetDeviceProperties(instance, &testCase.mDeviceProperties); + CompareDevicePropertiess(testCase.mDeviceProperties, *otThreadGetDeviceProperties(instance)); + VerifyOrQuit(otThreadGetLocalLeaderWeight(instance) == testCase.mExpectedLeaderWeight); + } + + printf("TestLeaderWeightCalculation passed\n"); +} + +#endif // OPENTHREAD_FTD + +} // namespace ot + +int main(void) +{ +#if OPENTHREAD_FTD + ot::TestDefaultDeviceProperties(); + ot::TestLeaderWeightCalculation(); +#endif + + printf("All tests passed\n"); + return 0; +} diff --git a/tests/unit/test_multicast_listeners_table.cpp b/tests/unit/test_multicast_listeners_table.cpp index a1e341ce804..20c810e7f30 100644 --- a/tests/unit/test_multicast_listeners_table.cpp +++ b/tests/unit/test_multicast_listeners_table.cpp @@ -57,10 +57,7 @@ static const otIp6Address MA501 = { uint32_t sNow; -extern "C" uint32_t otPlatAlarmMilliGetNow(void) -{ - return sNow; -} +extern "C" uint32_t otPlatAlarmMilliGetNow(void) { return sNow; } void testMulticastListenersTableAPIs(Instance *aInstance); @@ -199,8 +196,5 @@ int main(void) } #else -int main(void) -{ - return 0; -} +int main(void) { return 0; } #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE diff --git a/tests/unit/test_nat64.cpp b/tests/unit/test_nat64.cpp new file mode 100644 index 00000000000..66e5fbeb191 --- /dev/null +++ b/tests/unit/test_nat64.cpp @@ -0,0 +1,314 @@ +/* + * 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. + */ + +#include "net/nat64_translator.hpp" + +#include "test_platform.h" +#include "test_util.hpp" + +#include "string.h" + +#include "common/code_utils.hpp" +#include "common/debug.hpp" +#include "common/instance.hpp" +#include "common/message.hpp" +#include "net/ip6.hpp" + +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + +namespace ot { +namespace BorderRouter { + +static ot::Instance *sInstance; + +void DumpMessageInHex(const char *prefix, const uint8_t *aBuf, size_t aBufLen) +{ + // This function dumps all packets the output of this function can be imported to packet analyser for debugging. + printf("%s", prefix); + for (uint16_t i = 0; i < aBufLen; i++) + { + printf("%02x", aBuf[i]); + } + printf("\n"); +} + +bool CheckMessage(const Message &aMessage, const uint8_t *aExpectedMessage, size_t aExpectedMessageLen) +{ + uint8_t readMessage[OPENTHREAD_CONFIG_IP6_MAX_DATAGRAM_LENGTH]; + uint16_t messageLength; + bool success = true; + + success = success && (aMessage.GetLength() == aExpectedMessageLen); + messageLength = aMessage.ReadBytes(0, readMessage, aMessage.GetLength()); + success = success && (aExpectedMessageLen == messageLength); + success = success && (memcmp(readMessage, aExpectedMessage, aExpectedMessageLen) == 0); + + if (!success) + { + printf("Expected Message\n"); + for (uint16_t i = 0; i < aExpectedMessageLen; i++) + { + printf("%02x%c", aExpectedMessage[i], " \n"[(i & 0xf) == 0xf]); + } + printf("\n"); + printf("Actual Message\n"); + for (uint16_t i = 0; i < messageLength; i++) + { + printf("%02x%c", readMessage[i], " \n"[(i & 0xf) == 0xf]); + } + printf("\n"); + } + + return success; +} + +template +void TestCase6To4(const char *aTestName, + const uint8_t (&aIp6Message)[N], + Nat64::Translator::Result aResult, + const uint8_t *aOutMessage, + size_t aOutMessageLen) +{ + Message *msg = sInstance->Get().NewMessage(0); + + printf("Testing NAT64 6 to 4: %s\n", aTestName); + + VerifyOrQuit(msg != nullptr); + SuccessOrQuit(msg->AppendBytes(aIp6Message, N)); + + DumpMessageInHex("I ", aIp6Message, N); + + VerifyOrQuit(sInstance->Get().TranslateFromIp6(*msg) == aResult); + + if (aOutMessage != nullptr) + { + DumpMessageInHex("O ", aOutMessage, aOutMessageLen); + VerifyOrQuit(CheckMessage(*msg, aOutMessage, aOutMessageLen)); + } + + printf(" ... PASS\n"); +} + +template +void TestCase4To6(const char *aTestName, + const uint8_t (&aIp4Message)[N], + Nat64::Translator::Result aResult, + const uint8_t *aOutMessage, + size_t aOutMessageLen) +{ + Message *msg = sInstance->Get().NewMessage(0); + + printf("Testing NAT64 4 to 6: %s\n", aTestName); + + VerifyOrQuit(msg != nullptr); + SuccessOrQuit(msg->AppendBytes(aIp4Message, N)); + + DumpMessageInHex("I ", aIp4Message, N); + + VerifyOrQuit(sInstance->Get().TranslateToIp6(*msg) == aResult); + + if (aOutMessage != nullptr) + { + DumpMessageInHex("O ", aOutMessage, aOutMessageLen); + VerifyOrQuit(CheckMessage(*msg, aOutMessage, aOutMessageLen)); + } + + printf(" ... PASS\n"); +} + +void TestNat64(void) +{ + Ip6::Prefix nat64prefix; + Ip4::Cidr nat64cidr; + Ip6::Address ip6Source; + Ip6::Address ip6Dest; + + sInstance = testInitInstance(); + + { + const uint8_t ip6Address[] = {0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + const uint8_t ip4Address[] = {192, 168, 123, 1}; + + nat64cidr.Set(ip4Address, 32); + nat64prefix.Set(ip6Address, 96); + SuccessOrQuit(sInstance->Get().SetIp4Cidr(nat64cidr)); + sInstance->Get().SetNat64Prefix(nat64prefix); + } + + { + // fd02::1 fd01::ac10:f3c5 UDP 52 43981 → 4660 Len=4 + const uint8_t kIp6Packet[] = { + 0x60, 0x08, 0x6e, 0x38, 0x00, 0x0c, 0x11, 0x40, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 172, 16, 243, 197, 0xab, 0xcd, 0x12, 0x34, 0x00, 0x0c, 0xe3, 0x31, 0x61, 0x62, 0x63, 0x64, + }; + // 192.168.123.1 172.16.243.197 UDP 32 43981 → 4660 Len=4 + const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x11, 0x9f, + 0x4d, 192, 168, 123, 1, 172, 16, 243, 197, 0xab, 0xcd, + 0x12, 0x34, 0x00, 0x0c, 0xa1, 0x8d, 0x61, 0x62, 0x63, 0x64}; + + TestCase6To4("good v6 udp datagram", kIp6Packet, Nat64::Translator::kForward, kIp4Packet, sizeof(kIp4Packet)); + } + + { + // 172.16.243.197 192.168.123.1 UDP 32 43981 → 4660 Len=4 + const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x11, 0xa0, + 0x4d, 172, 16, 243, 197, 192, 168, 123, 1, 0xab, 0xcd, + 0x12, 0x34, 0x00, 0x0c, 0xa1, 0x8d, 0x61, 0x62, 0x63, 0x64}; + // fd01::ac10:f3c5 fd02::1 UDP 52 43981 → 4660 Len=4 + const uint8_t kIp6Packet[] = { + 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x3f, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 172, 16, 243, 197, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0xab, 0xcd, 0x12, 0x34, 0x00, 0x0c, 0xe3, 0x31, 0x61, 0x62, 0x63, 0x64, + }; + + TestCase4To6("good v4 udp datagram", kIp4Packet, Nat64::Translator::kForward, kIp6Packet, sizeof(kIp6Packet)); + } + + { + // fd02::1 fd01::ac10:f3c5 TCP 64 43981 → 4660 [ACK] Seq=1 Ack=1 Win=1 Len=4 + const uint8_t kIp6Packet[] = { + 0x60, 0x08, 0x6e, 0x38, 0x00, 0x18, 0x06, 0x40, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 172, 16, 243, 197, 0xab, 0xcd, 0x12, 0x34, 0x87, 0x65, 0x43, 0x21, + 0x12, 0x34, 0x56, 0x78, 0x50, 0x10, 0x00, 0x01, 0x5f, 0xf8, 0x00, 0x00, 0x61, 0x62, 0x63, 0x64, + }; + // 192.168.123.1 172.16.243.197 TCP 44 43981 → 4660 [ACK] Seq=1 Ack=1 Win=1 Len=4 + const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x9f, + 0x4c, 192, 168, 123, 1, 172, 16, 243, 197, 0xab, 0xcd, + 0x12, 0x34, 0x87, 0x65, 0x43, 0x21, 0x12, 0x34, 0x56, 0x78, 0x50, + 0x10, 0x00, 0x01, 0x1e, 0x54, 0x00, 0x00, 0x61, 0x62, 0x63, 0x64}; + + TestCase6To4("good v6 tcp datagram", kIp6Packet, Nat64::Translator::kForward, kIp4Packet, sizeof(kIp4Packet)); + } + + { + // 172.16.243.197 192.168.123.1 TCP 44 43981 → 4660 [ACK] Seq=1 Ack=1 Win=1 Len=4 + const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x40, 0x06, 0x9f, + 0x4c, 172, 16, 243, 197, 192, 168, 123, 1, 0xab, 0xcd, + 0x12, 0x34, 0x87, 0x65, 0x43, 0x21, 0x12, 0x34, 0x56, 0x78, 0x50, + 0x10, 0x00, 0x01, 0x1e, 0x54, 0x00, 0x00, 0x61, 0x62, 0x63, 0x64}; + // fd01::ac10:f3c5 fd02::1 TCP 64 43981 → 4660 [ACK] Seq=1 Ack=1 Win=1 Len=4 + const uint8_t kIp6Packet[] = { + 0x60, 0x00, 0x00, 0x00, 0x00, 0x18, 0x06, 0x40, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 172, 16, 243, 197, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xab, 0xcd, 0x12, 0x34, 0x87, 0x65, 0x43, 0x21, + 0x12, 0x34, 0x56, 0x78, 0x50, 0x10, 0x00, 0x01, 0x5f, 0xf8, 0x00, 0x00, 0x61, 0x62, 0x63, 0x64, + }; + + TestCase4To6("good v4 tcp datagram", kIp4Packet, Nat64::Translator::kForward, kIp6Packet, sizeof(kIp6Packet)); + } + + { + // fd02::1 fd01::ac10:f3c5 ICMPv6 52 Echo (ping) request id=0xaabb, seq=1, hop limit=64 + const uint8_t kIp6Packet[] = { + 0x60, 0x08, 0x6e, 0x38, 0x00, 0x0c, 0x3a, 0x40, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 172, 16, 243, 197, 0x80, 0x00, 0x76, 0x59, 0xaa, 0xbb, 0x00, 0x01, 0x61, 0x62, 0x63, 0x64, + }; + // 192.168.123.1 172.16.243.197 ICMP 32 Echo (ping) request id=0xaabb, seq=1/256, ttl=63 + const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x9f, + 0x5d, 192, 168, 123, 1, 172, 16, 243, 197, 0x08, 0x00, + 0x88, 0x7c, 0xaa, 0xbb, 0x00, 0x01, 0x61, 0x62, 0x63, 0x64}; + + TestCase6To4("good v6 icmp ping request datagram", kIp6Packet, Nat64::Translator::kForward, kIp4Packet, + sizeof(kIp4Packet)); + } + + { + // 172.16.243.197 192.168.123.1 ICMP 32 Echo (ping) reply id=0xaabb, seq=1/256, ttl=63 + const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x01, 0xa0, + 0x5d, 172, 16, 243, 197, 192, 168, 123, 1, 0x00, 0x00, + 0x90, 0x7c, 0xaa, 0xbb, 0x00, 0x01, 0x61, 0x62, 0x63, 0x64}; + // fd01::ac10:f3c5 fd02::1 ICMPv6 52 Echo (ping) reply id=0xaabb, seq=1, hop limit=62 + const uint8_t kIp6Packet[] = { + 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x3a, 0x3f, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 172, 16, 243, 197, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x81, 0x00, 0x75, 0x59, 0xaa, 0xbb, 0x00, 0x01, 0x61, 0x62, 0x63, 0x64, + }; + + TestCase4To6("good v4 icmp ping response datagram", kIp4Packet, Nat64::Translator::kForward, kIp6Packet, + sizeof(kIp6Packet)); + } + + { + // fd02::1 N/A IPv6 39 Invalid IPv6 header + const uint8_t kIp6Packet[] = {0x60, 0x08, 0x6e, 0x38, 0x00, 0x0c, 0x11, 0x40, 0xfd, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfd, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 172, 16, 243}; + + TestCase6To4("bad v6 datagram", kIp6Packet, Nat64::Translator::kDrop, nullptr, 0); + } + + { + // 172.16.243.197 N/A IPv4 19 [Malformed Packet] + const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x11, + 0xa0, 0x4c, 172, 16, 243, 197, 192, 168, 123}; + + TestCase4To6("bad v4 datagram", kIp4Packet, Nat64::Translator::kDrop, nullptr, 0); + } + + { + // 172.16.243.197 192.168.123.2 UDP 32 43981 → 4660 Len=4 + const uint8_t kIp4Packet[] = {0x45, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x11, 0xa0, + 0x4c, 172, 16, 243, 197, 192, 168, 123, 2, 0xab, 0xcd, + 0x12, 0x34, 0x00, 0x0c, 0xa1, 0x8c, 0x61, 0x62, 0x63, 0x64}; + + TestCase4To6("no v4 mapping", kIp4Packet, Nat64::Translator::kDrop, nullptr, 0); + } + + { + // fd02::2 fd01::ac10:f3c5 UDP 52 43981 → 4660 Len=4 + const uint8_t kIp6Packet[] = { + 0x60, 0x08, 0x6e, 0x38, 0x00, 0x0c, 0x11, 0x40, 0xfd, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xfd, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 172, 16, 243, 197, 0xab, 0xcd, 0x12, 0x34, 0x00, 0x0c, 0xe3, 0x30, 0x61, 0x62, 0x63, 0x64, + }; + + TestCase6To4("mapping pool exhausted", kIp6Packet, Nat64::Translator::kDrop, nullptr, 0); + } + + testFreeInstance(sInstance); +} + +} // namespace BorderRouter +} // namespace ot + +#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + +int main(void) +{ +#if OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + ot::BorderRouter::TestNat64(); + printf("All tests passed\n"); +#else // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + printf("NAT64 is not enabled\n"); +#endif // OPENTHREAD_CONFIG_NAT64_TRANSLATOR_ENABLE + return 0; +} diff --git a/tests/unit/test_ndproxy_table.cpp b/tests/unit/test_ndproxy_table.cpp index cee7aec331b..4589db93aa9 100644 --- a/tests/unit/test_ndproxy_table.cpp +++ b/tests/unit/test_ndproxy_table.cpp @@ -111,8 +111,5 @@ int main(void) } #else -int main(void) -{ - return 0; -} +int main(void) { return 0; } #endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_BACKBONE_ROUTER_DUA_NDPROXYING_ENABLE diff --git a/tests/unit/test_netif.cpp b/tests/unit/test_netif.cpp index d82b05197a1..1a5c0e12017 100644 --- a/tests/unit/test_netif.cpp +++ b/tests/unit/test_netif.cpp @@ -89,7 +89,7 @@ void TestNetifMulticastAddresses(void) { const uint8_t kMaxAddresses = 8; - Instance * instance = testInitInstance(); + Instance *instance = testInitInstance(); TestNetif netif(*instance); Ip6::Address addresses[kMaxAddresses]; diff --git a/tests/unit/test_network_data.cpp b/tests/unit/test_network_data.cpp index 44df23646f7..51c0b67c47c 100644 --- a/tests/unit/test_network_data.cpp +++ b/tests/unit/test_network_data.cpp @@ -93,7 +93,7 @@ void VerifyRlocsArray(const uint16_t *aRlocs, uint16_t aRlocsLength, const uint1 printf("\nRLOCs: { "); - for (uint8_t index = 0; index < aRlocsLength; index++) + for (uint16_t index = 0; index < aRlocsLength; index++) { VerifyOrQuit(aRlocs[index] == aExpectedRlocs[index]); printf("0x%04x ", aRlocs[index]); @@ -106,7 +106,7 @@ void TestNetworkDataIterator(void) { static constexpr uint8_t kMaxRlocsArray = 10; - ot::Instance * instance; + ot::Instance *instance; Iterator iter = kIteratorInit; ExternalRouteConfig rconfig; OnMeshPrefixConfig pconfig; @@ -639,9 +639,10 @@ void TestNetworkDataDsnSrpServices(void) struct UnicastEntry { - const char * mAddress; + const char *mAddress; uint16_t mPort; Service::DnsSrpUnicast::Origin mOrigin; + uint16_t mRloc16; bool Matches(Service::DnsSrpUnicast::Info aInfo) const { @@ -650,7 +651,7 @@ void TestNetworkDataDsnSrpServices(void) SuccessOrQuit(sockAddr.GetAddress().FromString(mAddress)); sockAddr.SetPort(mPort); - return (aInfo.mSockAddr == sockAddr) && (aInfo.mOrigin == mOrigin); + return (aInfo.mSockAddr == sockAddr) && (aInfo.mOrigin == mOrigin) && (aInfo.mRloc16 == mRloc16); } }; @@ -671,16 +672,16 @@ void TestNetworkDataDsnSrpServices(void) }; const UnicastEntry kUnicastEntries[] = { - {"fdde:ad00:beef:0:2d0e:c627:5556:18d9", 0x1234, Service::DnsSrpUnicast::kFromServiceData}, - {"fd00:aabb:ccdd:eeff:11:2233:4455:6677", 0xabcd, Service::DnsSrpUnicast::kFromServerData}, - {"fdde:ad00:beef:0:0:ff:fe00:2800", 0x5678, Service::DnsSrpUnicast::kFromServerData}, - {"fd00:1234:5678:9abc:def0:123:4567:89ab", 0x0e, Service::DnsSrpUnicast::kFromServerData}, - {"fdde:ad00:beef:0:0:ff:fe00:6c00", 0xcd12, Service::DnsSrpUnicast::kFromServerData}, + {"fdde:ad00:beef:0:2d0e:c627:5556:18d9", 0x1234, Service::DnsSrpUnicast::kFromServiceData, 0xfffe}, + {"fd00:aabb:ccdd:eeff:11:2233:4455:6677", 0xabcd, Service::DnsSrpUnicast::kFromServerData, 0x6c00}, + {"fdde:ad00:beef:0:0:ff:fe00:2800", 0x5678, Service::DnsSrpUnicast::kFromServerData, 0x2800}, + {"fd00:1234:5678:9abc:def0:123:4567:89ab", 0x0e, Service::DnsSrpUnicast::kFromServerData, 0x4c00}, + {"fdde:ad00:beef:0:0:ff:fe00:6c00", 0xcd12, Service::DnsSrpUnicast::kFromServerData, 0x6c00}, }; const uint8_t kPreferredAnycastEntryIndex = 2; - Service::Manager & manager = instance->Get(); + Service::Manager &manager = instance->Get(); Service::Manager::Iterator iterator; Service::DnsSrpAnycast::Info anycastInfo; Service::DnsSrpUnicast::Info unicastInfo; @@ -725,8 +726,8 @@ void TestNetworkDataDsnSrpServices(void) for (const UnicastEntry &entry : kUnicastEntries) { SuccessOrQuit(manager.GetNextDnsSrpUnicastInfo(iterator, unicastInfo)); - printf("\nunicastInfo { %s, origin:%s }", unicastInfo.mSockAddr.ToString().AsCString(), - kOriginStrings[unicastInfo.mOrigin]); + printf("\nunicastInfo { %s, origin:%s, rloc16:%04x }", unicastInfo.mSockAddr.ToString().AsCString(), + kOriginStrings[unicastInfo.mOrigin], unicastInfo.mRloc16); VerifyOrQuit(entry.Matches(unicastInfo), "GetNextDnsSrpUnicastInfo() returned incorrect info"); } diff --git a/tests/unit/test_network_name.cpp b/tests/unit/test_network_name.cpp index c1f448fe487..874efe18174 100644 --- a/tests/unit/test_network_name.cpp +++ b/tests/unit/test_network_name.cpp @@ -72,7 +72,8 @@ void TestNetworkName(void) SuccessOrQuit(networkName.Set(MeshCoP::NameData(kName2, sizeof(kName2)))); CompareNetworkName(networkName, kName2); - VerifyOrQuit(networkName.Set(MeshCoP::NameData(kEmptyName, 0)) == kErrorInvalidArgs); + SuccessOrQuit(networkName.Set(MeshCoP::NameData(kEmptyName, 0))); + CompareNetworkName(networkName, kEmptyName); SuccessOrQuit(networkName.Set(MeshCoP::NameData(kLongName, sizeof(kLongName)))); CompareNetworkName(networkName, kLongName); @@ -80,8 +81,6 @@ void TestNetworkName(void) VerifyOrQuit(networkName.Set(MeshCoP::NameData(kLongName, sizeof(kLongName) - 1)) == kErrorAlready, "failed to detect duplicate"); - VerifyOrQuit(networkName.Set(kEmptyName) == kErrorInvalidArgs); - SuccessOrQuit(networkName.Set(MeshCoP::NameData(kName1, sizeof(kName1)))); VerifyOrQuit(networkName.Set(MeshCoP::NameData(kTooLongName, sizeof(kTooLongName))) == kErrorInvalidArgs, diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index 9d01cd3cf1a..5487643140e 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -26,8 +26,13 @@ * POSSIBILITY OF SUCH DAMAGE. */ +// Disable OpenThread's own new implementation to avoid duplicate definition +#define OT_CORE_COMMON_NEW_HPP_ #include "test_platform.h" +#include +#include + #include #include @@ -37,7 +42,7 @@ enum FLASH_SWAP_NUM = 2, }; -static uint8_t sFlash[FLASH_SWAP_SIZE * FLASH_SWAP_NUM]; +std::map>> settings; ot::Instance *testInitInstance(void) { @@ -78,28 +83,16 @@ bool sDiagMode = false; extern "C" { #if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE -OT_TOOL_WEAK void *otPlatCAlloc(size_t aNum, size_t aSize) -{ - return calloc(aNum, aSize); -} +OT_TOOL_WEAK void *otPlatCAlloc(size_t aNum, size_t aSize) { return calloc(aNum, aSize); } -OT_TOOL_WEAK void otPlatFree(void *aPtr) -{ - free(aPtr); -} +OT_TOOL_WEAK void otPlatFree(void *aPtr) { free(aPtr); } #endif -OT_TOOL_WEAK void otTaskletsSignalPending(otInstance *) -{ -} +OT_TOOL_WEAK void otTaskletsSignalPending(otInstance *) {} -OT_TOOL_WEAK void otPlatAlarmMilliStop(otInstance *) -{ -} +OT_TOOL_WEAK void otPlatAlarmMilliStop(otInstance *) {} -OT_TOOL_WEAK void otPlatAlarmMilliStartAt(otInstance *, uint32_t, uint32_t) -{ -} +OT_TOOL_WEAK void otPlatAlarmMilliStartAt(otInstance *, uint32_t, uint32_t) {} OT_TOOL_WEAK uint32_t otPlatAlarmMilliGetNow(void) { @@ -110,13 +103,9 @@ OT_TOOL_WEAK uint32_t otPlatAlarmMilliGetNow(void) return (uint32_t)((tv.tv_sec * 1000) + (tv.tv_usec / 1000) + 123456); } -OT_TOOL_WEAK void otPlatAlarmMicroStop(otInstance *) -{ -} +OT_TOOL_WEAK void otPlatAlarmMicroStop(otInstance *) {} -OT_TOOL_WEAK void otPlatAlarmMicroStartAt(otInstance *, uint32_t, uint32_t) -{ -} +OT_TOOL_WEAK void otPlatAlarmMicroStartAt(otInstance *, uint32_t, uint32_t) {} OT_TOOL_WEAK uint32_t otPlatAlarmMicroGetNow(void) { @@ -127,122 +116,55 @@ OT_TOOL_WEAK uint32_t otPlatAlarmMicroGetNow(void) return (uint32_t)((tv.tv_sec * 1000000) + tv.tv_usec + 123456); } -OT_TOOL_WEAK void otPlatRadioGetIeeeEui64(otInstance *, uint8_t *) -{ -} +OT_TOOL_WEAK void otPlatRadioGetIeeeEui64(otInstance *, uint8_t *) {} -OT_TOOL_WEAK void otPlatRadioSetPanId(otInstance *, uint16_t) -{ -} +OT_TOOL_WEAK void otPlatRadioSetPanId(otInstance *, uint16_t) {} -OT_TOOL_WEAK void otPlatRadioSetExtendedAddress(otInstance *, const otExtAddress *) -{ -} +OT_TOOL_WEAK void otPlatRadioSetExtendedAddress(otInstance *, const otExtAddress *) {} -OT_TOOL_WEAK void otPlatRadioSetShortAddress(otInstance *, uint16_t) -{ -} +OT_TOOL_WEAK void otPlatRadioSetShortAddress(otInstance *, uint16_t) {} -OT_TOOL_WEAK void otPlatRadioSetPromiscuous(otInstance *, bool) -{ -} +OT_TOOL_WEAK void otPlatRadioSetPromiscuous(otInstance *, bool) {} -OT_TOOL_WEAK bool otPlatRadioIsEnabled(otInstance *) -{ - return true; -} +OT_TOOL_WEAK bool otPlatRadioIsEnabled(otInstance *) { return true; } -OT_TOOL_WEAK otError otPlatRadioEnable(otInstance *) -{ - return OT_ERROR_NONE; -} +OT_TOOL_WEAK otError otPlatRadioEnable(otInstance *) { return OT_ERROR_NONE; } -OT_TOOL_WEAK otError otPlatRadioDisable(otInstance *) -{ - return OT_ERROR_NONE; -} +OT_TOOL_WEAK otError otPlatRadioDisable(otInstance *) { return OT_ERROR_NONE; } -OT_TOOL_WEAK otError otPlatRadioSleep(otInstance *) -{ - return OT_ERROR_NONE; -} +OT_TOOL_WEAK otError otPlatRadioSleep(otInstance *) { return OT_ERROR_NONE; } -OT_TOOL_WEAK otError otPlatRadioReceive(otInstance *, uint8_t) -{ - return OT_ERROR_NONE; -} +OT_TOOL_WEAK otError otPlatRadioReceive(otInstance *, uint8_t) { return OT_ERROR_NONE; } -OT_TOOL_WEAK otError otPlatRadioTransmit(otInstance *, otRadioFrame *) -{ - return OT_ERROR_NONE; -} +OT_TOOL_WEAK otError otPlatRadioTransmit(otInstance *, otRadioFrame *) { return OT_ERROR_NONE; } -OT_TOOL_WEAK otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *) -{ - return nullptr; -} +OT_TOOL_WEAK otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *) { return nullptr; } -OT_TOOL_WEAK int8_t otPlatRadioGetRssi(otInstance *) -{ - return 0; -} +OT_TOOL_WEAK int8_t otPlatRadioGetRssi(otInstance *) { return 0; } -OT_TOOL_WEAK otRadioCaps otPlatRadioGetCaps(otInstance *) -{ - return OT_RADIO_CAPS_NONE; -} +OT_TOOL_WEAK otRadioCaps otPlatRadioGetCaps(otInstance *) { return OT_RADIO_CAPS_NONE; } -OT_TOOL_WEAK bool otPlatRadioGetPromiscuous(otInstance *) -{ - return false; -} +OT_TOOL_WEAK bool otPlatRadioGetPromiscuous(otInstance *) { return false; } -OT_TOOL_WEAK void otPlatRadioEnableSrcMatch(otInstance *, bool) -{ -} +OT_TOOL_WEAK void otPlatRadioEnableSrcMatch(otInstance *, bool) {} -OT_TOOL_WEAK otError otPlatRadioAddSrcMatchShortEntry(otInstance *, uint16_t) -{ - return OT_ERROR_NONE; -} +OT_TOOL_WEAK otError otPlatRadioAddSrcMatchShortEntry(otInstance *, uint16_t) { return OT_ERROR_NONE; } -OT_TOOL_WEAK otError otPlatRadioAddSrcMatchExtEntry(otInstance *, const otExtAddress *) -{ - return OT_ERROR_NONE; -} +OT_TOOL_WEAK otError otPlatRadioAddSrcMatchExtEntry(otInstance *, const otExtAddress *) { return OT_ERROR_NONE; } -OT_TOOL_WEAK otError otPlatRadioClearSrcMatchShortEntry(otInstance *, uint16_t) -{ - return OT_ERROR_NONE; -} +OT_TOOL_WEAK otError otPlatRadioClearSrcMatchShortEntry(otInstance *, uint16_t) { return OT_ERROR_NONE; } -OT_TOOL_WEAK otError otPlatRadioClearSrcMatchExtEntry(otInstance *, const otExtAddress *) -{ - return OT_ERROR_NONE; -} +OT_TOOL_WEAK otError otPlatRadioClearSrcMatchExtEntry(otInstance *, const otExtAddress *) { return OT_ERROR_NONE; } -OT_TOOL_WEAK void otPlatRadioClearSrcMatchShortEntries(otInstance *) -{ -} +OT_TOOL_WEAK void otPlatRadioClearSrcMatchShortEntries(otInstance *) {} -OT_TOOL_WEAK void otPlatRadioClearSrcMatchExtEntries(otInstance *) -{ -} +OT_TOOL_WEAK void otPlatRadioClearSrcMatchExtEntries(otInstance *) {} -OT_TOOL_WEAK otError otPlatRadioEnergyScan(otInstance *, uint8_t, uint16_t) -{ - return OT_ERROR_NOT_IMPLEMENTED; -} +OT_TOOL_WEAK otError otPlatRadioEnergyScan(otInstance *, uint8_t, uint16_t) { return OT_ERROR_NOT_IMPLEMENTED; } -OT_TOOL_WEAK otError otPlatRadioSetTransmitPower(otInstance *, int8_t) -{ - return OT_ERROR_NOT_IMPLEMENTED; -} +OT_TOOL_WEAK otError otPlatRadioSetTransmitPower(otInstance *, int8_t) { return OT_ERROR_NOT_IMPLEMENTED; } -OT_TOOL_WEAK int8_t otPlatRadioGetReceiveSensitivity(otInstance *) -{ - return -100; -} +OT_TOOL_WEAK int8_t otPlatRadioGetReceiveSensitivity(otInstance *) { return -100; } OT_TOOL_WEAK otError otPlatEntropyGet(uint8_t *aOutput, uint16_t aOutputLength) { @@ -252,7 +174,7 @@ OT_TOOL_WEAK otError otPlatEntropyGet(uint8_t *aOutput, uint16_t aOutputLength) #if __SANITIZE_ADDRESS__ == 0 { - FILE * file = nullptr; + FILE *file = nullptr; size_t readLength; file = fopen("/dev/urandom", "rb"); @@ -278,100 +200,128 @@ OT_TOOL_WEAK otError otPlatEntropyGet(uint8_t *aOutput, uint16_t aOutputLength) return error; } -OT_TOOL_WEAK void otPlatDiagProcess(otInstance *, uint8_t, char *aArgs[], char *aOutput, size_t) +OT_TOOL_WEAK void otPlatDiagProcess(otInstance *, uint8_t, char *aArgs[], char *aOutput, size_t aOutputMaxLen) { - sprintf(aOutput, "diag feature '%s' is not supported\r\n", aArgs[0]); + snprintf(aOutput, aOutputMaxLen, "diag feature '%s' is not supported\r\n", aArgs[0]); } -OT_TOOL_WEAK void otPlatDiagModeSet(bool aMode) -{ - sDiagMode = aMode; -} +OT_TOOL_WEAK void otPlatDiagModeSet(bool aMode) { sDiagMode = aMode; } -OT_TOOL_WEAK bool otPlatDiagModeGet() -{ - return sDiagMode; -} +OT_TOOL_WEAK bool otPlatDiagModeGet() { return sDiagMode; } -OT_TOOL_WEAK void otPlatDiagChannelSet(uint8_t) -{ -} +OT_TOOL_WEAK void otPlatDiagChannelSet(uint8_t) {} -OT_TOOL_WEAK void otPlatDiagTxPowerSet(int8_t) -{ -} +OT_TOOL_WEAK void otPlatDiagTxPowerSet(int8_t) {} -OT_TOOL_WEAK void otPlatDiagRadioReceived(otInstance *, otRadioFrame *, otError) -{ -} +OT_TOOL_WEAK void otPlatDiagRadioReceived(otInstance *, otRadioFrame *, otError) {} -OT_TOOL_WEAK void otPlatDiagAlarmCallback(otInstance *) -{ -} +OT_TOOL_WEAK void otPlatDiagAlarmCallback(otInstance *) {} -OT_TOOL_WEAK void otPlatUartSendDone(void) -{ -} +OT_TOOL_WEAK void otPlatUartSendDone(void) {} -OT_TOOL_WEAK void otPlatUartReceived(const uint8_t *, uint16_t) -{ -} +OT_TOOL_WEAK void otPlatUartReceived(const uint8_t *, uint16_t) {} -OT_TOOL_WEAK void otPlatReset(otInstance *) -{ -} +OT_TOOL_WEAK void otPlatReset(otInstance *) {} -OT_TOOL_WEAK otPlatResetReason otPlatGetResetReason(otInstance *) -{ - return OT_PLAT_RESET_REASON_POWER_ON; -} +OT_TOOL_WEAK otPlatResetReason otPlatGetResetReason(otInstance *) { return OT_PLAT_RESET_REASON_POWER_ON; } -OT_TOOL_WEAK void otPlatLog(otLogLevel, otLogRegion, const char *, ...) -{ -} +OT_TOOL_WEAK void otPlatLog(otLogLevel, otLogRegion, const char *, ...) {} -OT_TOOL_WEAK void otPlatSettingsInit(otInstance *, const uint16_t *, uint16_t) -{ -} +OT_TOOL_WEAK void otPlatSettingsInit(otInstance *, const uint16_t *, uint16_t) {} -OT_TOOL_WEAK void otPlatSettingsDeinit(otInstance *) -{ -} +OT_TOOL_WEAK void otPlatSettingsDeinit(otInstance *) {} -OT_TOOL_WEAK otError otPlatSettingsGet(otInstance *, uint16_t, int, uint8_t *, uint16_t *) +OT_TOOL_WEAK otError otPlatSettingsGet(otInstance *, uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) { - return OT_ERROR_NOT_FOUND; -} + auto setting = settings.find(aKey); + + if (setting == settings.end()) + { + return OT_ERROR_NOT_FOUND; + } + + if (aIndex > setting->second.size()) + { + return OT_ERROR_NOT_FOUND; + } + + if (aValueLength == nullptr) + { + return OT_ERROR_NONE; + } + + const auto &data = setting->second[aIndex]; + + if (aValue == nullptr) + { + *aValueLength = data.size(); + return OT_ERROR_NONE; + } + + if (*aValueLength >= data.size()) + { + *aValueLength = data.size(); + } + + memcpy(aValue, &data[0], *aValueLength); -OT_TOOL_WEAK otError otPlatSettingsSet(otInstance *, uint16_t, const uint8_t *, uint16_t) -{ return OT_ERROR_NONE; } -OT_TOOL_WEAK otError otPlatSettingsAdd(otInstance *, uint16_t, const uint8_t *, uint16_t) +OT_TOOL_WEAK otError otPlatSettingsSet(otInstance *, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) { + auto setting = std::vector(aValue, aValue + aValueLength); + + settings[aKey].clear(); + settings[aKey].push_back(setting); + return OT_ERROR_NONE; } -OT_TOOL_WEAK otError otPlatSettingsDelete(otInstance *, uint16_t, int) +OT_TOOL_WEAK otError otPlatSettingsAdd(otInstance *, uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength) { + auto setting = std::vector(aValue, aValue + aValueLength); + settings[aKey].push_back(setting); + return OT_ERROR_NONE; } -OT_TOOL_WEAK void otPlatSettingsWipe(otInstance *) +OT_TOOL_WEAK otError otPlatSettingsDelete(otInstance *, uint16_t aKey, int aIndex) { -} + auto setting = settings.find(aKey); + if (setting == settings.end()) + { + return OT_ERROR_NOT_FOUND; + } -OT_TOOL_WEAK void otPlatFlashInit(otInstance *) -{ - memset(sFlash, 0xff, sizeof(sFlash)); + if (aIndex >= setting->second.size()) + { + return OT_ERROR_NOT_FOUND; + } + setting->second.erase(setting->second.begin() + aIndex); + return OT_ERROR_NONE; } -OT_TOOL_WEAK uint32_t otPlatFlashGetSwapSize(otInstance *) +OT_TOOL_WEAK void otPlatSettingsWipe(otInstance *) { settings.clear(); } + +uint8_t *GetFlash(void) { - return FLASH_SWAP_SIZE; + static uint8_t sFlash[FLASH_SWAP_SIZE * FLASH_SWAP_NUM]; + static bool sInitialized; + + if (!sInitialized) + { + memset(sFlash, 0xff, sizeof(sFlash)); + sInitialized = true; + } + + return sFlash; } +OT_TOOL_WEAK void otPlatFlashInit(otInstance *) {} + +OT_TOOL_WEAK uint32_t otPlatFlashGetSwapSize(otInstance *) { return FLASH_SWAP_SIZE; } + OT_TOOL_WEAK void otPlatFlashErase(otInstance *, uint8_t aSwapIndex) { uint32_t address; @@ -380,7 +330,7 @@ OT_TOOL_WEAK void otPlatFlashErase(otInstance *, uint8_t aSwapIndex) address = aSwapIndex ? FLASH_SWAP_SIZE : 0; - memset(sFlash + address, 0xff, FLASH_SWAP_SIZE); + memset(GetFlash() + address, 0xff, FLASH_SWAP_SIZE); } OT_TOOL_WEAK void otPlatFlashRead(otInstance *, uint8_t aSwapIndex, uint32_t aOffset, void *aData, uint32_t aSize) @@ -393,7 +343,7 @@ OT_TOOL_WEAK void otPlatFlashRead(otInstance *, uint8_t aSwapIndex, uint32_t aOf address = aSwapIndex ? FLASH_SWAP_SIZE : 0; - memcpy(aData, sFlash + address + aOffset, aSize); + memcpy(aData, GetFlash() + address + aOffset, aSize); } OT_TOOL_WEAK void otPlatFlashWrite(otInstance *, @@ -412,15 +362,12 @@ OT_TOOL_WEAK void otPlatFlashWrite(otInstance *, for (uint32_t index = 0; index < aSize; index++) { - sFlash[address + aOffset + index] &= ((uint8_t *)aData)[index]; + GetFlash()[address + aOffset + index] &= ((uint8_t *)aData)[index]; } } #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE || OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE -OT_TOOL_WEAK uint16_t otPlatTimeGetXtalAccuracy(void) -{ - return 0; -} +OT_TOOL_WEAK uint16_t otPlatTimeGetXtalAccuracy(void) { return 0; } #endif #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE @@ -429,9 +376,7 @@ OT_TOOL_WEAK otError otPlatRadioEnableCsl(otInstance *, uint32_t, otShortAddress return OT_ERROR_NONE; } -OT_TOOL_WEAK void otPlatRadioUpdateCslSampleTime(otInstance *, uint32_t) -{ -} +OT_TOOL_WEAK void otPlatRadioUpdateCslSampleTime(otInstance *, uint32_t) {} OT_TOOL_WEAK uint8_t otPlatRadioGetCslAccuracy(otInstance *) { @@ -440,27 +385,17 @@ OT_TOOL_WEAK uint8_t otPlatRadioGetCslAccuracy(otInstance *) #endif #if OPENTHREAD_CONFIG_OTNS_ENABLE -OT_TOOL_WEAK void otPlatOtnsStatus(const char *) -{ -} +OT_TOOL_WEAK void otPlatOtnsStatus(const char *) {} #endif #if OPENTHREAD_CONFIG_RADIO_LINK_TREL_ENABLE -OT_TOOL_WEAK void otPlatTrelEnable(otInstance *, uint16_t *) -{ -} +OT_TOOL_WEAK void otPlatTrelEnable(otInstance *, uint16_t *) {} -OT_TOOL_WEAK void otPlatTrelDisable(otInstance *) -{ -} +OT_TOOL_WEAK void otPlatTrelDisable(otInstance *) {} -OT_TOOL_WEAK void otPlatTrelSend(otInstance *, const uint8_t *, uint16_t, const otSockAddr *) -{ -} +OT_TOOL_WEAK void otPlatTrelSend(otInstance *, const uint8_t *, uint16_t, const otSockAddr *) {} -OT_TOOL_WEAK void otPlatTrelRegisterService(otInstance *, uint16_t, const uint8_t *, uint8_t) -{ -} +OT_TOOL_WEAK void otPlatTrelRegisterService(otInstance *, uint16_t, const uint8_t *, uint8_t) {} #endif #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE @@ -483,25 +418,24 @@ OT_TOOL_WEAK otLinkMetrics otPlatRadioGetEnhAckProbingMetrics(otInstance *, cons #endif #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE -OT_TOOL_WEAK bool otPlatInfraIfHasAddress(uint32_t, const otIp6Address *) -{ - return false; -} +OT_TOOL_WEAK bool otPlatInfraIfHasAddress(uint32_t, const otIp6Address *) { return false; } OT_TOOL_WEAK otError otPlatInfraIfSendIcmp6Nd(uint32_t, const otIp6Address *, const uint8_t *, uint16_t) { return OT_ERROR_FAILED; } + +OT_TOOL_WEAK otError otPlatInfraIfDiscoverNat64Prefix(uint32_t) { return OT_ERROR_FAILED; } #endif #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE -otError otPlatCryptoImportKey(otCryptoKeyRef * aKeyRef, +otError otPlatCryptoImportKey(otCryptoKeyRef *aKeyRef, otCryptoKeyType aKeyType, otCryptoKeyAlgorithm aKeyAlgorithm, int aKeyUsage, otCryptoKeyStorage aKeyPersistence, - const uint8_t * aKey, + const uint8_t *aKey, size_t aKeyLen) { OT_UNUSED_VARIABLE(aKeyRef); @@ -542,6 +476,43 @@ bool otPlatCryptoHasKey(otCryptoKeyRef aKeyRef) #endif // OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE +otError otPlatCryptoEcdsaGenerateAndImportKey(otCryptoKeyRef aKeyRef) +{ + OT_UNUSED_VARIABLE(aKeyRef); + + return OT_ERROR_NONE; +} + +otError otPlatCryptoEcdsaExportPublicKey(otCryptoKeyRef aKeyRef, otPlatCryptoEcdsaPublicKey *aPublicKey) +{ + OT_UNUSED_VARIABLE(aKeyRef); + OT_UNUSED_VARIABLE(aPublicKey); + + return OT_ERROR_NONE; +} + +otError otPlatCryptoEcdsaSignUsingKeyRef(otCryptoKeyRef aKeyRef, + const otPlatCryptoSha256Hash *aHash, + otPlatCryptoEcdsaSignature *aSignature) +{ + OT_UNUSED_VARIABLE(aKeyRef); + OT_UNUSED_VARIABLE(aHash); + OT_UNUSED_VARIABLE(aSignature); + + return OT_ERROR_NONE; +} + +otError otPlatCryptoEcdsaVerifyUsingKeyRef(otCryptoKeyRef aKeyRef, + const otPlatCryptoSha256Hash *aHash, + const otPlatCryptoEcdsaSignature *aSignature) +{ + OT_UNUSED_VARIABLE(aKeyRef); + OT_UNUSED_VARIABLE(aHash); + OT_UNUSED_VARIABLE(aSignature); + + return OT_ERROR_NONE; +} + otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aThreshold) { OT_UNUSED_VARIABLE(aInstance); @@ -578,4 +549,78 @@ OT_TOOL_WEAK void otPlatDsoDisconnect(otPlatDsoConnection *aConnection, otPlatDs #endif // #if OPENTHREAD_CONFIG_DNS_DSO_ENABLE +#if OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE +otError otPlatUdpSocket(otUdpSocket *aUdpSocket) +{ + OT_UNUSED_VARIABLE(aUdpSocket); + return OT_ERROR_NONE; +} + +otError otPlatUdpClose(otUdpSocket *aUdpSocket) +{ + OT_UNUSED_VARIABLE(aUdpSocket); + return OT_ERROR_NONE; +} + +otError otPlatUdpBind(otUdpSocket *aUdpSocket) +{ + OT_UNUSED_VARIABLE(aUdpSocket); + return OT_ERROR_NONE; +} + +otError otPlatUdpBindToNetif(otUdpSocket *aUdpSocket, otNetifIdentifier aNetifIdentifier) +{ + OT_UNUSED_VARIABLE(aUdpSocket); + OT_UNUSED_VARIABLE(aNetifIdentifier); + return OT_ERROR_NONE; +} + +otError otPlatUdpConnect(otUdpSocket *aUdpSocket) +{ + OT_UNUSED_VARIABLE(aUdpSocket); + return OT_ERROR_NONE; +} + +otError otPlatUdpSend(otUdpSocket *aUdpSocket, otMessage *aMessage, const otMessageInfo *aMessageInfo) +{ + OT_UNUSED_VARIABLE(aUdpSocket); + OT_UNUSED_VARIABLE(aMessageInfo); + return OT_ERROR_NONE; +} + +otError otPlatUdpJoinMulticastGroup(otUdpSocket *aUdpSocket, + otNetifIdentifier aNetifIdentifier, + const otIp6Address *aAddress) +{ + OT_UNUSED_VARIABLE(aUdpSocket); + OT_UNUSED_VARIABLE(aNetifIdentifier); + OT_UNUSED_VARIABLE(aAddress); + return OT_ERROR_NONE; +} + +otError otPlatUdpLeaveMulticastGroup(otUdpSocket *aUdpSocket, + otNetifIdentifier aNetifIdentifier, + const otIp6Address *aAddress) +{ + OT_UNUSED_VARIABLE(aUdpSocket); + OT_UNUSED_VARIABLE(aNetifIdentifier); + OT_UNUSED_VARIABLE(aAddress); + return OT_ERROR_NONE; +} +#endif // OPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE + +#if OPENTHREAD_CONFIG_DNS_UPSTREAM_QUERY_ENABLE +void otPlatDnsStartUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn, const otMessage *aQuery) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aTxn); + OT_UNUSED_VARIABLE(aQuery); +} + +void otPlatDnsCancelUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery *aTxn) +{ + otPlatDnsUpstreamQueryDone(aInstance, aTxn, nullptr); +} +#endif + } // extern "C" diff --git a/tests/unit/test_platform.h b/tests/unit/test_platform.h index 2944e5225dd..ec5f8870947 100644 --- a/tests/unit/test_platform.h +++ b/tests/unit/test_platform.h @@ -33,6 +33,7 @@ #include #include +#include #include #include #include diff --git a/tests/unit/test_power_calibration.cpp b/tests/unit/test_power_calibration.cpp new file mode 100644 index 00000000000..91c1d692055 --- /dev/null +++ b/tests/unit/test_power_calibration.cpp @@ -0,0 +1,151 @@ +/* + * 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. + */ + +#include + +#include "test_platform.h" +#include "test_util.h" + +#if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + +namespace ot { + +void TestPowerCalibration(void) +{ + otInstance *instance; + uint8_t rawPowerSetting[2]; + uint16_t rawPowerSettingLength; + + struct CalibratedPowerEntry + { + uint8_t mChannel; + int16_t mActualPower; + uint8_t mRawPowerSetting[1]; + uint16_t mRawPowerSettingLength; + }; + + constexpr CalibratedPowerEntry kCalibratedPowerTable[] = { + {11, 15000, {0x02}, 1}, + {11, 5000, {0x00}, 1}, + {11, 10000, {0x01}, 1}, + }; + + instance = static_cast(testInitInstance()); + VerifyOrQuit(instance != nullptr, "Null OpenThread instance"); + + for (const CalibratedPowerEntry &calibratedPower : kCalibratedPowerTable) + { + SuccessOrQuit(otPlatRadioAddCalibratedPower(instance, calibratedPower.mChannel, calibratedPower.mActualPower, + calibratedPower.mRawPowerSetting, + calibratedPower.mRawPowerSettingLength)); + } + + SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 4999)); + rawPowerSettingLength = sizeof(rawPowerSetting); + VerifyOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength) == + OT_ERROR_NOT_FOUND); + + SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 5000)); + rawPowerSettingLength = sizeof(rawPowerSetting); + SuccessOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength)); + VerifyOrQuit(rawPowerSettingLength == 1); + VerifyOrQuit(rawPowerSetting[0] == 0x00); + + SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 9999)); + rawPowerSettingLength = sizeof(rawPowerSetting); + SuccessOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength)); + VerifyOrQuit(rawPowerSettingLength == 1); + VerifyOrQuit(rawPowerSetting[0] == 0x00); + + SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 10000)); + rawPowerSettingLength = sizeof(rawPowerSetting); + SuccessOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength)); + VerifyOrQuit(rawPowerSettingLength == 1); + VerifyOrQuit(rawPowerSetting[0] == 0x01); + + SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 14999)); + rawPowerSettingLength = sizeof(rawPowerSetting); + SuccessOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength)); + VerifyOrQuit(rawPowerSettingLength == 1); + VerifyOrQuit(rawPowerSetting[0] == 0x01); + + SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 15000)); + rawPowerSettingLength = sizeof(rawPowerSetting); + SuccessOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength)); + VerifyOrQuit(rawPowerSettingLength == 1); + VerifyOrQuit(rawPowerSetting[0] == 0x02); + + SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 15001)); + rawPowerSettingLength = sizeof(rawPowerSetting); + SuccessOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength)); + VerifyOrQuit(rawPowerSettingLength == 1); + VerifyOrQuit(rawPowerSetting[0] == 0x02); + + rawPowerSettingLength = sizeof(rawPowerSetting); + VerifyOrQuit(otPlatRadioGetRawPowerSetting(instance, 12, rawPowerSetting, &rawPowerSettingLength) == + OT_ERROR_NOT_FOUND); + + SuccessOrQuit(otPlatRadioClearCalibratedPowers(instance)); + rawPowerSettingLength = sizeof(rawPowerSetting); + VerifyOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength) == + OT_ERROR_NOT_FOUND); + + for (const CalibratedPowerEntry &calibratedPower : kCalibratedPowerTable) + { + SuccessOrQuit(otPlatRadioAddCalibratedPower(instance, calibratedPower.mChannel, calibratedPower.mActualPower, + calibratedPower.mRawPowerSetting, + calibratedPower.mRawPowerSettingLength)); + } + + SuccessOrQuit(otPlatRadioSetChannelTargetPower(instance, 11, 15000)); + rawPowerSettingLength = sizeof(rawPowerSetting); + SuccessOrQuit(otPlatRadioGetRawPowerSetting(instance, 11, rawPowerSetting, &rawPowerSettingLength)); + VerifyOrQuit(rawPowerSettingLength == 1); + VerifyOrQuit(rawPowerSetting[0] == 0x02); + + VerifyOrQuit( + otPlatRadioAddCalibratedPower(instance, kCalibratedPowerTable[0].mChannel, + kCalibratedPowerTable[0].mActualPower, kCalibratedPowerTable[0].mRawPowerSetting, + kCalibratedPowerTable[0].mRawPowerSettingLength) == OT_ERROR_INVALID_ARGS); + + testFreeInstance(instance); +} +} // namespace ot + +#endif // OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + +int main(void) +{ +#if OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + ot::TestPowerCalibration(); + printf("All tests passed\n"); +#else // OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + printf("Power calibration is not enabled\n"); +#endif // OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE && OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + return 0; +} diff --git a/tests/unit/test_priority_queue.cpp b/tests/unit/test_priority_queue.cpp index 51f6b720bee..6348f55c5cb 100644 --- a/tests/unit/test_priority_queue.cpp +++ b/tests/unit/test_priority_queue.cpp @@ -44,8 +44,8 @@ void VerifyPriorityQueueContent(ot::PriorityQueue &aPriorityQueue, int aExpected { const ot::PriorityQueue &constQueue = aPriorityQueue; va_list args; - ot::Message * message; - ot::Message * msgArg; + ot::Message *message; + ot::Message *msgArg; int8_t curPriority = ot::Message::kNumPriorities; ot::PriorityQueue::Info info; @@ -168,14 +168,14 @@ void VerifyMsgQueueContent(ot::MessageQueue &aMessageQueue, int aExpectedLength, void TestPriorityQueue(void) { - ot::Instance * instance; - ot::MessagePool * messagePool; + ot::Instance *instance; + ot::MessagePool *messagePool; ot::PriorityQueue queue; ot::MessageQueue messageQueue; - ot::Message * msgNet[kNumTestMessages]; - ot::Message * msgHigh[kNumTestMessages]; - ot::Message * msgNor[kNumTestMessages]; - ot::Message * msgLow[kNumTestMessages]; + ot::Message *msgNet[kNumTestMessages]; + ot::Message *msgHigh[kNumTestMessages]; + ot::Message *msgNor[kNumTestMessages]; + ot::Message *msgLow[kNumTestMessages]; instance = testInitInstance(); VerifyOrQuit(instance != nullptr, "Null OpenThread instance"); diff --git a/tests/unit/test_pskc.cpp b/tests/unit/test_pskc.cpp index 3eedad98f22..0ae4e973e49 100644 --- a/tests/unit/test_pskc.cpp +++ b/tests/unit/test_pskc.cpp @@ -39,10 +39,10 @@ void TestMinimumPassphrase(void) { ot::Pskc pskc; const uint8_t expectedPskc[] = {0x44, 0x98, 0x8e, 0x22, 0xcf, 0x65, 0x2e, 0xee, - 0xcc, 0xd1, 0xe4, 0xc0, 0x1d, 0x01, 0x54, 0xf8}; + 0xcc, 0xd1, 0xe4, 0xc0, 0x1d, 0x01, 0x54, 0xf8}; const otExtendedPanId xpanid = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; const char passphrase[] = "123456"; - otInstance * instance = testInitInstance(); + otInstance *instance = testInitInstance(); SuccessOrQuit(ot::MeshCoP::GeneratePskc(passphrase, *reinterpret_cast("OpenThread"), static_cast(xpanid), pskc)); @@ -54,24 +54,24 @@ void TestMaximumPassphrase(void) { ot::Pskc pskc; const uint8_t expectedPskc[] = {0x9e, 0x81, 0xbd, 0x35, 0xa2, 0x53, 0x76, 0x2f, - 0x80, 0xee, 0x04, 0xff, 0x2f, 0xa2, 0x85, 0xe9}; + 0x80, 0xee, 0x04, 0xff, 0x2f, 0xa2, 0x85, 0xe9}; const otExtendedPanId xpanid = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}}; const char passphrase[] = "1234567812345678" - "1234567812345678" - "1234567812345678" - "1234567812345678" - "1234567812345678" - "1234567812345678" - "1234567812345678" - "1234567812345678" - "1234567812345678" - "1234567812345678" - "1234567812345678" - "1234567812345678" - "1234567812345678" - "1234567812345678" - "1234567812345678" - "123456781234567"; + "1234567812345678" + "1234567812345678" + "1234567812345678" + "1234567812345678" + "1234567812345678" + "1234567812345678" + "1234567812345678" + "1234567812345678" + "1234567812345678" + "1234567812345678" + "1234567812345678" + "1234567812345678" + "1234567812345678" + "1234567812345678" + "123456781234567"; otInstance *instance = testInitInstance(); SuccessOrQuit(ot::MeshCoP::GeneratePskc(passphrase, @@ -85,7 +85,7 @@ void TestExampleInSpec(void) { ot::Pskc pskc; const uint8_t expectedPskc[] = {0xc3, 0xf5, 0x93, 0x68, 0x44, 0x5a, 0x1b, 0x61, - 0x06, 0xbe, 0x42, 0x0a, 0x70, 0x6d, 0x4c, 0xc9}; + 0x06, 0xbe, 0x42, 0x0a, 0x70, 0x6d, 0x4c, 0xc9}; const otExtendedPanId xpanid = {{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}}; const char passphrase[] = "12SECRETPASSWORD34"; diff --git a/tests/unit/test_routing_manager.cpp b/tests/unit/test_routing_manager.cpp index 56085d0d3a8..bd1174b13bd 100644 --- a/tests/unit/test_routing_manager.cpp +++ b/tests/unit/test_routing_manager.cpp @@ -35,7 +35,9 @@ #include "border_router/routing_manager.hpp" #include "common/arg_macros.hpp" +#include "common/array.hpp" #include "common/instance.hpp" +#include "common/time.hpp" #include "net/icmp6.hpp" #include "net/nd6.hpp" @@ -44,8 +46,8 @@ using namespace ot; // Logs a message and adds current time (sNow) as "::." -#define Log(...) \ - printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 36000000), (sNow / 60000) % 60, \ +#define Log(...) \ + printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 3600000), (sNow / 60000) % 60, \ (sNow / 1000) % 60, sNow % 1000 OT_REST_ARGS(__VA_ARGS__)) static constexpr uint32_t kInfraIfIndex = 1; @@ -54,7 +56,10 @@ static const char kInfraIfAddress[] = "fe80::1"; static constexpr uint32_t kValidLitime = 2000; static constexpr uint32_t kPreferredLifetime = 1800; -static otInstance *sInstance; +static constexpr uint16_t kMaxRaSize = 800; +static constexpr uint16_t kMaxDeprecatingPrefixes = 16; + +static ot::Instance *sInstance; static uint32_t sNow = 0; static uint32_t sAlarmTime; @@ -73,13 +78,75 @@ enum ExpectedPio kPioDeprecatingLocalOnLink, // Expect to see local on-link prefix deprecated (zero preferred lifetime). }; +struct DeprecatingPrefix +{ + DeprecatingPrefix(void) = default; + + DeprecatingPrefix(const Ip6::Prefix &aPrefix, uint32_t aLifetime) + : mPrefix(aPrefix) + , mLifetime(aLifetime) + { + } + + bool Matches(const Ip6::Prefix &aPrefix) const { return mPrefix == aPrefix; } + + Ip6::Prefix mPrefix; // Old on-link prefix being deprecated. + uint32_t mLifetime; // Valid lifetime of prefix from PIO. +}; + static Ip6::Address sInfraIfAddress; -bool sRsEmitted; // Indicates if an RS message was emitted by BR. -bool sRaValidated; // Indicates if an RA was emitted by BR and successfully validated. -bool sSawExpectedRio; // Indicates if the emitted RA by BR contained an RIO with `sExpectedRioPrefix` -ExpectedPio sExpectedPio; // Expected PIO in the emitted RA by BR (MUST be seen in RA to set `sRaValidated`). -Ip6::Prefix sExpectedRioPrefix; // Expected RIO prefix to see in RA (MUST be seen to set `sSawExpectedRio`). +bool sRsEmitted; // Indicates if an RS message was emitted by BR. +bool sRaValidated; // Indicates if an RA was emitted by BR and successfully validated. +bool sNsEmitted; // Indicates if an NS message was emitted by BR. +bool sRespondToNs; // Indicates whether or not to respond to NS. +ExpectedPio sExpectedPio; // Expected PIO in the emitted RA by BR (MUST be seen in RA to set `sRaValidated`). +uint32_t sOnLinkLifetime; // Valid lifetime for local on-link prefix from the last processed RA. + +// Array containing deprecating prefixes from PIOs in the last processed RA. +Array sDeprecatingPrefixes; + +static constexpr uint16_t kMaxRioPrefixes = 10; + +struct RioPrefix +{ + RioPrefix(void) = default; + + explicit RioPrefix(const Ip6::Prefix &aPrefix) + : mSawInRa(false) + , mPrefix(aPrefix) + , mLifetime(0) + { + } + + bool mSawInRa; // Indicate whether or not this prefix was seen in the emitted RA (as RIO). + Ip6::Prefix mPrefix; // The RIO prefix. + uint32_t mLifetime; // The RIO prefix lifetime - only valid when `mSawInRa` +}; + +class ExpectedRios : public Array +{ +public: + void Add(const Ip6::Prefix &aPrefix) { SuccessOrQuit(PushBack(RioPrefix(aPrefix))); } + + bool SawAll(void) const + { + bool sawAll = true; + + for (const RioPrefix &rioPrefix : *this) + { + if (!rioPrefix.mSawInRa) + { + sawAll = false; + break; + } + } + + return sawAll; + } +}; + +ExpectedRios sExpectedRios; // Expected RIO prefixes in emitted RAs. //---------------------------------------------------------------------------------------------------------------------- // Function prototypes @@ -88,8 +155,25 @@ void ProcessRadioTxAndTasklets(void); void AdvanceTime(uint32_t aDuration); void LogRouterAdvert(const Icmp6Packet &aPacket); void ValidateRouterAdvert(const Icmp6Packet &aPacket); -const char *PreferenceToString(uint8_t aPreference); +const char *PreferenceToString(int8_t aPreference); void SendRouterAdvert(const Ip6::Address &aAddress, const Icmp6Packet &aPacket); +void SendNeighborAdvert(const Ip6::Address &aAddress, const Ip6::Nd::NeighborAdvertMessage &aNaMessage); + +#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED +void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) +{ + OT_UNUSED_VARIABLE(aLogLevel); + OT_UNUSED_VARIABLE(aLogRegion); + + va_list args; + + printf(" "); + va_start(args, aFormat); + vprintf(aFormat, args); + va_end(args); + printf("\n"); +} +#endif //---------------------------------------------------------------------------------------------------------------------- // `otPlatRadio @@ -103,18 +187,12 @@ otError otPlatRadioTransmit(otInstance *, otRadioFrame *) return OT_ERROR_NONE; } -otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *) -{ - return &sRadioTxFrame; -} +otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *) { return &sRadioTxFrame; } //---------------------------------------------------------------------------------------------------------------------- // `otPlatAlaram -void otPlatAlarmMilliStop(otInstance *) -{ - sAlarmOn = false; -} +void otPlatAlarmMilliStop(otInstance *) { sAlarmOn = false; } void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt) { @@ -122,10 +200,7 @@ void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt) sAlarmTime = aT0 + aDt; } -uint32_t otPlatAlarmMilliGetNow(void) -{ - return sNow; -} +uint32_t otPlatAlarmMilliGetNow(void) { return sNow; } //--------------------------------------------------------------------------------------------------------------------- // otPlatInfraIf @@ -139,7 +214,7 @@ bool otPlatInfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddres otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, const otIp6Address *aDestAddress, - const uint8_t * aBuffer, + const uint8_t *aBuffer, uint16_t aBufferLength) { Icmp6Packet packet; @@ -164,9 +239,32 @@ otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, Log(" Router Advertisement message"); LogRouterAdvert(packet); ValidateRouterAdvert(packet); - sRaValidated = true; break; + case Ip6::Icmp::Header::kTypeNeighborSolicit: + { + const Ip6::Nd::NeighborSolicitMessage *nsMsg = + reinterpret_cast(packet.GetBytes()); + + Log(" Neighbor Solicit message"); + + VerifyOrQuit(packet.GetLength() >= sizeof(Ip6::Nd::NeighborSolicitMessage)); + VerifyOrQuit(nsMsg->IsValid()); + sNsEmitted = true; + + if (sRespondToNs) + { + Ip6::Nd::NeighborAdvertMessage naMsg; + + naMsg.SetTargetAddress(nsMsg->GetTargetAddress()); + naMsg.SetRouterFlag(); + naMsg.SetSolicitedFlag(); + SendNeighborAdvert(AsCoreType(aDestAddress), naMsg); + } + + break; + } + default: VerifyOrQuit(false, "Bad ICMP6 type"); } @@ -199,7 +297,7 @@ void AdvanceTime(uint32_t aDuration) Log("AdvanceTime for %u.%03u", aDuration / 1000, aDuration % 1000); - while (sAlarmTime <= time) + while (TimeMilli(sAlarmTime) <= TimeMilli(time)) { ProcessRadioTxAndTasklets(); sNow = sAlarmTime; @@ -212,10 +310,17 @@ void AdvanceTime(uint32_t aDuration) void ValidateRouterAdvert(const Icmp6Packet &aPacket) { - Ip6::Nd::RouterAdvertMessage raMsg(aPacket); + constexpr uint8_t kMaxPrefixes = 16; + + Ip6::Nd::RouterAdvertMessage raMsg(aPacket); + bool sawExpectedPio = false; + Array pioPrefixes; + Array rioPrefixes; VerifyOrQuit(raMsg.IsValid()); + sDeprecatingPrefixes.Clear(); + for (const Ip6::Nd::Option &option : raMsg) { switch (option.GetType()) @@ -229,20 +334,40 @@ void ValidateRouterAdvert(const Icmp6Packet &aPacket) VerifyOrQuit(pio.IsValid()); pio.GetPrefix(prefix); - VerifyOrQuit(sExpectedPio != kNoPio, "Received RA contain an unexpected PIO"); + VerifyOrQuit(!pioPrefixes.Contains(prefix), "Duplicate PIO prefix in RA"); + SuccessOrQuit(pioPrefixes.PushBack(prefix)); SuccessOrQuit(otBorderRoutingGetOnLinkPrefix(sInstance, &localOnLink)); - VerifyOrQuit(prefix == localOnLink); - if (sExpectedPio == kPioAdvertisingLocalOnLink) + if (prefix == localOnLink) { - VerifyOrQuit(pio.GetPreferredLifetime() > 0, "On link prefix is deprecated unexpectedly"); + switch (sExpectedPio) + { + case kNoPio: + break; + + case kPioAdvertisingLocalOnLink: + if (pio.GetPreferredLifetime() > 0) + { + sOnLinkLifetime = pio.GetValidLifetime(); + sawExpectedPio = true; + } + break; + + case kPioDeprecatingLocalOnLink: + if (pio.GetPreferredLifetime() == 0) + { + sOnLinkLifetime = pio.GetValidLifetime(); + sawExpectedPio = true; + } + break; + } } else { - VerifyOrQuit(pio.GetPreferredLifetime() == 0, "On link prefix is not deprecated"); + VerifyOrQuit(pio.GetPreferredLifetime() == 0, "Old on link prefix is not deprecated"); + SuccessOrQuit(sDeprecatingPrefixes.PushBack(DeprecatingPrefix(prefix, pio.GetValidLifetime()))); } - break; } @@ -254,9 +379,16 @@ void ValidateRouterAdvert(const Icmp6Packet &aPacket) VerifyOrQuit(rio.IsValid()); rio.GetPrefix(prefix); - if (prefix == sExpectedRioPrefix) + VerifyOrQuit(!rioPrefixes.Contains(prefix), "Duplicate RIO prefix in RA"); + SuccessOrQuit(rioPrefixes.PushBack(prefix)); + + for (RioPrefix &rioPrefix : sExpectedRios) { - sSawExpectedRio = true; + if (prefix == rioPrefix.mPrefix) + { + rioPrefix.mSawInRa = true; + rioPrefix.mLifetime = rio.GetRouteLifetime(); + } } break; @@ -266,6 +398,27 @@ void ValidateRouterAdvert(const Icmp6Packet &aPacket) VerifyOrQuit(false, "Unexpected option type in RA msg"); } } + + if (!sRaValidated) + { + switch (sExpectedPio) + { + case kNoPio: + break; + case kPioAdvertisingLocalOnLink: + case kPioDeprecatingLocalOnLink: + // First emitted RAs may not yet have the expected PIO + // so we exit and not set `sRaValidated` to allow it + // to be checked for next received RA. + VerifyOrExit(sawExpectedPio); + break; + } + + sRaValidated = true; + } + +exit: + return; } void LogRouterAdvert(const Icmp6Packet &aPacket) @@ -312,7 +465,7 @@ void LogRouterAdvert(const Icmp6Packet &aPacket) } } -const char *PreferenceToString(uint8_t aPreference) +const char *PreferenceToString(int8_t aPreference) { const char *str = ""; @@ -342,6 +495,13 @@ void SendRouterAdvert(const Ip6::Address &aAddress, const Icmp6Packet &aPacket) otPlatInfraIfRecvIcmp6Nd(sInstance, kInfraIfIndex, &aAddress, aPacket.GetBytes(), aPacket.GetLength()); } +void SendNeighborAdvert(const Ip6::Address &aAddress, const Ip6::Nd::NeighborAdvertMessage &aNaMessage) +{ + Log("Sending NA from %s", aAddress.ToString().AsCString()); + otPlatInfraIfRecvIcmp6Nd(sInstance, kInfraIfIndex, &aAddress, reinterpret_cast(&aNaMessage), + sizeof(aNaMessage)); +} + Ip6::Prefix PrefixFromString(const char *aString, uint8_t aPrefixLength) { Ip6::Prefix prefix; @@ -361,349 +521,461 @@ Ip6::Address AddressFromString(const char *aString) return address; } -void TestRoutingManager(void) +void VerifyOmrPrefixInNetData(const Ip6::Prefix &aOmrPrefix, bool aDefaultRoute) { - Instance & instance = *static_cast(testInitInstance()); - BorderRouter::RoutingManager & rm = instance.Get(); - Ip6::Prefix localOnLink; - Ip6::Prefix localOmr; - Ip6::Prefix onLinkPrefix = PrefixFromString("2000:abba:baba::", 64); - Ip6::Prefix routePrefix = PrefixFromString("2000:1234:5678::", 64); - Ip6::Prefix omrPrefix = PrefixFromString("2000:0000:1111:4444::", 64); - Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); - Ip6::Address routerAddressB = AddressFromString("fd00::bbbb"); - BorderRouter::RoutingManager::PrefixTableIterator iter; - BorderRouter::RoutingManager::PrefixTableEntry entry; - NetworkData::Iterator iterator; - NetworkData::OnMeshPrefixConfig prefixConfig; - NetworkData::ExternalRouteConfig routeConfig; - uint8_t counter; - uint8_t buffer[800]; + otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; + NetworkData::OnMeshPrefixConfig prefixConfig; - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Initialize OT instance. + Log("VerifyOmrPrefixInNetData(%s, def-route:%s)", aOmrPrefix.ToString().AsCString(), aDefaultRoute ? "yes" : "no"); - sNow = 0; - sInstance = &instance; + SuccessOrQuit(otNetDataGetNextOnMeshPrefix(sInstance, &iterator, &prefixConfig)); + VerifyOrQuit(prefixConfig.GetPrefix() == aOmrPrefix); + VerifyOrQuit(prefixConfig.mStable == true); + VerifyOrQuit(prefixConfig.mSlaac == true); + VerifyOrQuit(prefixConfig.mPreferred == true); + VerifyOrQuit(prefixConfig.mOnMesh == true); + VerifyOrQuit(prefixConfig.mDefaultRoute == aDefaultRoute); - memset(&sRadioTxFrame, 0, sizeof(sRadioTxFrame)); - sRadioTxFrame.mPsdu = sRadioTxFramePsdu; + VerifyOrQuit(otNetDataGetNextOnMeshPrefix(sInstance, &iterator, &prefixConfig) == kErrorNotFound); +} - SuccessOrQuit(sInfraIfAddress.FromString(kInfraIfAddress)); +void VerifyNoOmrPrefixInNetData(void) +{ + otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; + NetworkData::OnMeshPrefixConfig prefixConfig; - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Initialize Border Router and start Thread operation. + Log("VerifyNoOmrPrefixInNetData()"); + VerifyOrQuit(otNetDataGetNextOnMeshPrefix(sInstance, &iterator, &prefixConfig) != kErrorNone); +} - SuccessOrQuit(otBorderRoutingInit(sInstance, kInfraIfIndex, /* aInfraIfIsRunning */ true)); +using NetworkData::RoutePreference; - SuccessOrQuit(otLinkSetPanId(sInstance, 0x1234)); - SuccessOrQuit(otIp6SetEnabled(sInstance, true)); - SuccessOrQuit(otThreadSetEnabled(sInstance, true)); +enum ExternalRouteMode : uint8_t +{ + kNoRoute, + kDefaultRoute, + kUlaRoute, +}; - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Ensure device starts as leader. +void VerifyExternalRouteInNetData(ExternalRouteMode aMode) +{ + Error error; + otNetworkDataIterator iterator = OT_NETWORK_DATA_ITERATOR_INIT; + otExternalRouteConfig routeConfig; - AdvanceTime(10000); + error = otNetDataGetNextRoute(sInstance, &iterator, &routeConfig); - VerifyOrQuit(otThreadGetDeviceRole(sInstance) == OT_DEVICE_ROLE_LEADER); + switch (aMode) + { + case kNoRoute: + Log("VerifyExternalRouteInNetData(kNoRoute)"); + VerifyOrQuit(error != kErrorNone); + break; - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Start Routing Manager. Check emitted RS and RA messages. + case kDefaultRoute: + Log("VerifyExternalRouteInNetData(kDefaultRoute)"); + VerifyOrQuit(error == kErrorNone); + VerifyOrQuit(routeConfig.mPrefix.mLength == 0); + VerifyOrQuit(otNetDataGetNextRoute(sInstance, &iterator, &routeConfig) != kErrorNone); + break; - sRsEmitted = false; - sRaValidated = false; - sSawExpectedRio = false; - sExpectedPio = kPioAdvertisingLocalOnLink; + case kUlaRoute: + Log("VerifyExternalRouteInNetData(kUlaRoute)"); + VerifyOrQuit(error == kErrorNone); + VerifyOrQuit(routeConfig.mPrefix.mLength == 7); + VerifyOrQuit(routeConfig.mPrefix.mPrefix.mFields.m8[0] == 0xfc); + VerifyOrQuit(otNetDataGetNextRoute(sInstance, &iterator, &routeConfig) != kErrorNone); + break; + } +} - Log("Starting RoutingManager"); +struct Pio +{ + Pio(const Ip6::Prefix &aPrefix, uint32_t aValidLifetime, uint32_t aPreferredLifetime) + : mPrefix(aPrefix) + , mValidLifetime(aValidLifetime) + , mPreferredLifetime(aPreferredLifetime) + { + } - SuccessOrQuit(rm.SetEnabled(true)); + const Ip6::Prefix &mPrefix; + uint32_t mValidLifetime; + uint32_t mPreferredLifetime; +}; - SuccessOrQuit(rm.GetOnLinkPrefix(localOnLink)); - SuccessOrQuit(rm.GetOmrPrefix(localOmr)); +struct Rio +{ + Rio(const Ip6::Prefix &aPrefix, uint32_t aValidLifetime, RoutePreference aPreference) + : mPrefix(aPrefix) + , mValidLifetime(aValidLifetime) + , mPreference(aPreference) + { + } - Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); - Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + const Ip6::Prefix &mPrefix; + uint32_t mValidLifetime; + RoutePreference mPreference; +}; - sExpectedRioPrefix = localOmr; +struct DefaultRoute +{ + DefaultRoute(uint32_t aLifetime, RoutePreference aPreference) + : mLifetime(aLifetime) + , mPreference(aPreference) + { + } - AdvanceTime(30000); + uint32_t mLifetime; + RoutePreference mPreference; +}; - VerifyOrQuit(sRsEmitted); - VerifyOrQuit(sRaValidated); - VerifyOrQuit(sSawExpectedRio); - Log("Received RA was validated"); +void SendRouterAdvert(const Ip6::Address &aRouterAddress, + const Pio *aPios, + uint16_t aNumPios, + const Rio *aRios, + uint16_t aNumRios, + const DefaultRoute &aDefaultRoute) +{ + Ip6::Nd::RouterAdvertMessage::Header header; + uint8_t buffer[kMaxRaSize]; - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Check Network Data to include the local OMR and on-link prefix. + header.SetRouterLifetime(aDefaultRoute.mLifetime); + header.SetDefaultRouterPreference(aDefaultRoute.mPreference); - iterator = NetworkData::kIteratorInit; + { + Ip6::Nd::RouterAdvertMessage raMsg(header, buffer); - // We expect to see OMR prefix in net data as on-mesh prefix. - SuccessOrQuit(instance.Get().GetNextOnMeshPrefix(iterator, prefixConfig)); - VerifyOrQuit(prefixConfig.GetPrefix() == localOmr); - VerifyOrQuit(prefixConfig.mStable == true); - VerifyOrQuit(prefixConfig.mSlaac == true); - VerifyOrQuit(prefixConfig.mPreferred == true); - VerifyOrQuit(prefixConfig.mOnMesh == true); - VerifyOrQuit(prefixConfig.mDefaultRoute == false); + for (; aNumPios > 0; aPios++, aNumPios--) + { + SuccessOrQuit( + raMsg.AppendPrefixInfoOption(aPios->mPrefix, aPios->mValidLifetime, aPios->mPreferredLifetime)); + } - VerifyOrQuit(instance.Get().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNotFound); + for (; aNumRios > 0; aRios++, aNumRios--) + { + SuccessOrQuit(raMsg.AppendRouteInfoOption(aRios->mPrefix, aRios->mValidLifetime, aRios->mPreference)); + } + + SendRouterAdvert(aRouterAddress, raMsg.GetAsPacket()); + Log("Sending RA from router %s", aRouterAddress.ToString().AsCString()); + LogRouterAdvert(raMsg.GetAsPacket()); + } +} - iterator = NetworkData::kIteratorInit; +template +void SendRouterAdvert(const Ip6::Address &aRouterAddress, + const Pio (&aPios)[kNumPios], + const Rio (&aRios)[kNumRios], + const DefaultRoute &aDefaultRoute = DefaultRoute(0, NetworkData::kRoutePreferenceMedium)) +{ + SendRouterAdvert(aRouterAddress, aPios, kNumPios, aRios, kNumRios, aDefaultRoute); +} - // We expect to see local on-link prefix in net data as external route. - SuccessOrQuit(instance.Get().GetNextExternalRoute(iterator, routeConfig)); - VerifyOrQuit(routeConfig.GetPrefix() == localOnLink); - VerifyOrQuit(routeConfig.mStable == true); - VerifyOrQuit(routeConfig.mPreference == NetworkData::kRoutePreferenceMedium); +template +void SendRouterAdvert(const Ip6::Address &aRouterAddress, + const Pio (&aPios)[kNumPios], + const DefaultRoute &aDefaultRoute = DefaultRoute(0, NetworkData::kRoutePreferenceMedium)) +{ + SendRouterAdvert(aRouterAddress, aPios, kNumPios, nullptr, 0, aDefaultRoute); +} - VerifyOrQuit(instance.Get().GetNextExternalRoute(iterator, routeConfig) == kErrorNotFound); +template +void SendRouterAdvert(const Ip6::Address &aRouterAddress, + const Rio (&aRios)[kNumRios], + const DefaultRoute &aDefaultRoute = DefaultRoute(0, NetworkData::kRoutePreferenceMedium)) +{ + SendRouterAdvert(aRouterAddress, nullptr, 0, aRios, kNumRios, aDefaultRoute); +} - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Send an RA from router A with a new on-link (PIO) and route prefix (RIO). +void SendRouterAdvert(const Ip6::Address &aRouterAddress, const DefaultRoute &aDefaultRoute) +{ + SendRouterAdvert(aRouterAddress, nullptr, 0, nullptr, 0, aDefaultRoute); +} +struct OnLinkPrefix : public Pio +{ + OnLinkPrefix(const Ip6::Prefix &aPrefix, + uint32_t aValidLifetime, + uint32_t aPreferredLifetime, + const Ip6::Address &aRouterAddress) + : Pio(aPrefix, aValidLifetime, aPreferredLifetime) + , mRouterAddress(aRouterAddress) { - Ip6::Nd::RouterAdvertMessage raMsg(Ip6::Nd::RouterAdvertMessage::Header(), buffer); - - SuccessOrQuit(raMsg.AppendPrefixInfoOption(onLinkPrefix, kValidLitime, kPreferredLifetime)); - SuccessOrQuit(raMsg.AppendRouteInfoOption(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium)); + } - SendRouterAdvert(routerAddressA, raMsg.GetAsPacket()); + const Ip6::Address &mRouterAddress; +}; - Log("Send RA from router A"); - LogRouterAdvert(raMsg.GetAsPacket()); +struct RoutePrefix : public Rio +{ + RoutePrefix(const Ip6::Prefix &aPrefix, + uint32_t aValidLifetime, + RoutePreference aPreference, + const Ip6::Address &aRouterAddress) + : Rio(aPrefix, aValidLifetime, aPreference) + , mRouterAddress(aRouterAddress) + { } - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Check that the local on-link prefix is now deprecating in the new RA. + const Ip6::Address &mRouterAddress; +}; - sRaValidated = false; - sSawExpectedRio = false; - sExpectedPio = kPioDeprecatingLocalOnLink; +template +void VerifyPrefixTable(const OnLinkPrefix (&aOnLinkPrefixes)[kNumOnLinkPrefixes], + const RoutePrefix (&aRoutePrefixes)[kNumRoutePrefixes]) +{ + VerifyPrefixTable(aOnLinkPrefixes, kNumOnLinkPrefixes, aRoutePrefixes, kNumRoutePrefixes); +} - AdvanceTime(10000); - VerifyOrQuit(sRaValidated); - VerifyOrQuit(sSawExpectedRio); +template void VerifyPrefixTable(const OnLinkPrefix (&aOnLinkPrefixes)[kNumOnLinkPrefixes]) +{ + VerifyPrefixTable(aOnLinkPrefixes, kNumOnLinkPrefixes, nullptr, 0); +} - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Check the discovered prefix table and ensure info from router A - // is present in the table. +template void VerifyPrefixTable(const RoutePrefix (&aRoutePrefixes)[kNumRoutePrefixes]) +{ + VerifyPrefixTable(nullptr, 0, aRoutePrefixes, kNumRoutePrefixes); +} + +void VerifyPrefixTable(const OnLinkPrefix *aOnLinkPrefixes, + uint16_t aNumOnLinkPrefixes, + const RoutePrefix *aRoutePrefixes, + uint16_t aNumRoutePrefixes) +{ + BorderRouter::RoutingManager::PrefixTableIterator iter; + BorderRouter::RoutingManager::PrefixTableEntry entry; + uint16_t onLinkPrefixCount = 0; + uint16_t routePrefixCount = 0; - counter = 0; + Log("VerifyPrefixTable()"); - rm.InitPrefixTableIterator(iter); + sInstance->Get().InitPrefixTableIterator(iter); - while (rm.GetNextPrefixTableEntry(iter, entry) == kErrorNone) + while (sInstance->Get().GetNextPrefixTableEntry(iter, entry) == kErrorNone) { - counter++; - VerifyOrQuit(AsCoreType(&entry.mRouterAddress) == routerAddressA); + bool didFind = false; if (entry.mIsOnLink) { - VerifyOrQuit(AsCoreType(&entry.mPrefix) == onLinkPrefix); - VerifyOrQuit(entry.mValidLifetime = kValidLitime); - VerifyOrQuit(entry.mPreferredLifetime = kPreferredLifetime); + Log(" on-link prefix:%s, valid:%u, preferred:%u, router:%s, age:%u", + AsCoreType(&entry.mPrefix).ToString().AsCString(), entry.mValidLifetime, entry.mPreferredLifetime, + AsCoreType(&entry.mRouterAddress).ToString().AsCString(), entry.mMsecSinceLastUpdate / 1000); + + onLinkPrefixCount++; + + for (uint16_t index = 0; index < aNumOnLinkPrefixes; index++) + { + const OnLinkPrefix &onLinkPrefix = aOnLinkPrefixes[index]; + + if ((onLinkPrefix.mPrefix == AsCoreType(&entry.mPrefix)) && + (AsCoreType(&entry.mRouterAddress) == onLinkPrefix.mRouterAddress)) + { + VerifyOrQuit(entry.mValidLifetime == onLinkPrefix.mValidLifetime); + VerifyOrQuit(entry.mPreferredLifetime == onLinkPrefix.mPreferredLifetime); + didFind = true; + break; + } + } } else { - VerifyOrQuit(AsCoreType(&entry.mPrefix) == routePrefix); - VerifyOrQuit(entry.mValidLifetime = kValidLitime); - VerifyOrQuit(static_cast(entry.mRoutePreference) == NetworkData::kRoutePreferenceMedium); - } - } + Log(" route prefix:%s, valid:%u, prf:%s, router:%s, age:%u", + AsCoreType(&entry.mPrefix).ToString().AsCString(), entry.mValidLifetime, + PreferenceToString(entry.mRoutePreference), AsCoreType(&entry.mRouterAddress).ToString().AsCString(), + entry.mMsecSinceLastUpdate / 1000); - VerifyOrQuit(counter == 2); + routePrefixCount++; - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Check Network Data to include new prefixes from router A. + for (uint16_t index = 0; index < aNumRoutePrefixes; index++) + { + const RoutePrefix &routePrefix = aRoutePrefixes[index]; + + if ((routePrefix.mPrefix == AsCoreType(&entry.mPrefix)) && + (AsCoreType(&entry.mRouterAddress) == routePrefix.mRouterAddress)) + { + VerifyOrQuit(entry.mValidLifetime == routePrefix.mValidLifetime); + VerifyOrQuit(static_cast(entry.mRoutePreference) == routePrefix.mPreference); + didFind = true; + break; + } + } + } - iterator = NetworkData::kIteratorInit; + VerifyOrQuit(didFind); + } - // We expect to see OMR prefix in net data as on-mesh prefix. - SuccessOrQuit(instance.Get().GetNextOnMeshPrefix(iterator, prefixConfig)); - VerifyOrQuit(prefixConfig.GetPrefix() == localOmr); - VerifyOrQuit(prefixConfig.mStable == true); - VerifyOrQuit(prefixConfig.mSlaac == true); - VerifyOrQuit(prefixConfig.mPreferred == true); - VerifyOrQuit(prefixConfig.mOnMesh == true); - VerifyOrQuit(prefixConfig.mDefaultRoute == false); + VerifyOrQuit(onLinkPrefixCount == aNumOnLinkPrefixes); + VerifyOrQuit(routePrefixCount == aNumRoutePrefixes); +} - VerifyOrQuit(instance.Get().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNotFound); +void VerifyPrefixTableIsEmpty(void) { VerifyPrefixTable(nullptr, 0, nullptr, 0); } - iterator = NetworkData::kIteratorInit; +void InitTest(bool aEnablBorderRouting = false, bool aAfterReset = false) +{ + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Initialize OT instance. - counter = 0; + sNow = 0; + sInstance = static_cast(testInitInstance()); - // We expect to see 3 entries, our local on link and new prefixes from router A. - while (instance.Get().GetNextExternalRoute(iterator, routeConfig) == kErrorNone) + uint32_t delay = 10000; + if (aAfterReset) { - VerifyOrQuit((routeConfig.GetPrefix() == localOnLink) || (routeConfig.GetPrefix() == onLinkPrefix) || - (routeConfig.GetPrefix() == routePrefix)); - VerifyOrQuit(static_cast(routeConfig.mPreference) == NetworkData::kRoutePreferenceMedium); - counter++; + delay += 26000; // leader reset sync delay } - VerifyOrQuit(counter == 3); + memset(&sRadioTxFrame, 0, sizeof(sRadioTxFrame)); + sRadioTxFrame.mPsdu = sRadioTxFramePsdu; + + SuccessOrQuit(sInfraIfAddress.FromString(kInfraIfAddress)); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Send the same RA again from router A with the on-link (PIO) and route prefix (RIO). + // Initialize and start Border Router and Thread operation. - { - Ip6::Nd::RouterAdvertMessage raMsg(Ip6::Nd::RouterAdvertMessage::Header(), buffer); + SuccessOrQuit(otBorderRoutingInit(sInstance, kInfraIfIndex, /* aInfraIfIsRunning */ true)); - SuccessOrQuit(raMsg.AppendPrefixInfoOption(onLinkPrefix, kValidLitime, kPreferredLifetime)); - SuccessOrQuit(raMsg.AppendRouteInfoOption(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium)); + SuccessOrQuit(otLinkSetPanId(sInstance, 0x1234)); + SuccessOrQuit(otIp6SetEnabled(sInstance, true)); + SuccessOrQuit(otThreadSetEnabled(sInstance, true)); + SuccessOrQuit(otBorderRoutingSetEnabled(sInstance, aEnablBorderRouting)); - SendRouterAdvert(routerAddressA, raMsg.GetAsPacket()); + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Ensure device starts as leader. - Log("Send RA from router A"); - LogRouterAdvert(raMsg.GetAsPacket()); - } + AdvanceTime(delay); - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Check the discovered prefix table and ensure info from router A - // remains unchanged. + VerifyOrQuit(otThreadGetDeviceRole(sInstance) == OT_DEVICE_ROLE_LEADER); - counter = 0; + // Reset all test flags + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kNoPio; + sExpectedRios.Clear(); + sRespondToNs = true; +} - rm.InitPrefixTableIterator(iter); +void FinalizeTest(void) +{ + SuccessOrQuit(otIp6SetEnabled(sInstance, false)); + SuccessOrQuit(otThreadSetEnabled(sInstance, false)); + SuccessOrQuit(otInstanceErasePersistentInfo(sInstance)); + testFreeInstance(sInstance); +} - while (rm.GetNextPrefixTableEntry(iter, entry) == kErrorNone) - { - counter++; - VerifyOrQuit(AsCoreType(&entry.mRouterAddress) == routerAddressA); +//--------------------------------------------------------------------------------------------------------------------- - if (entry.mIsOnLink) - { - VerifyOrQuit(AsCoreType(&entry.mPrefix) == onLinkPrefix); - VerifyOrQuit(entry.mValidLifetime = kValidLitime); - VerifyOrQuit(entry.mPreferredLifetime = kPreferredLifetime); - } - else - { - VerifyOrQuit(AsCoreType(&entry.mPrefix) == routePrefix); - VerifyOrQuit(entry.mValidLifetime = kValidLitime); - VerifyOrQuit(static_cast(entry.mRoutePreference) == NetworkData::kRoutePreferenceMedium); - } - } +void TestSamePrefixesFromMultipleRouters(void) +{ + Ip6::Prefix localOnLink; + Ip6::Prefix localOmr; + Ip6::Prefix onLinkPrefix = PrefixFromString("2000:abba:baba::", 64); + Ip6::Prefix routePrefix = PrefixFromString("2000:1234:5678::", 64); + Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); + Ip6::Address routerAddressB = AddressFromString("fd00::bbbb"); + + Log("--------------------------------------------------------------------------------------------"); + Log("TestSamePrefixesFromMultipleRouters"); - VerifyOrQuit(counter == 2); + InitTest(); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Send an RA from router B with same route prefix (RIO) but with - // high route preference. + // Start Routing Manager. Check emitted RS and RA messages. - { - Ip6::Nd::RouterAdvertMessage raMsg(Ip6::Nd::RouterAdvertMessage::Header(), buffer); + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); - SuccessOrQuit(raMsg.AppendRouteInfoOption(routePrefix, kValidLitime, NetworkData::kRoutePreferenceHigh)); + SuccessOrQuit(sInstance->Get().SetEnabled(true)); - SendRouterAdvert(routerAddressB, raMsg.GetAsPacket()); + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); - Log("Send RA from router B"); - LogRouterAdvert(raMsg.GetAsPacket()); - } + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); - AdvanceTime(10000); + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Received RA was validated"); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Check the discovered prefix table and ensure info from router B - // is also included in the table. + // Check Network Data to include the local OMR and on-link prefix. - counter = 0; + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); - rm.InitPrefixTableIterator(iter); + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A with a new on-link (PIO) and route prefix (RIO). - while (rm.GetNextPrefixTableEntry(iter, entry) == kErrorNone) - { - const Ip6::Address &routerAddr = AsCoreType(&entry.mRouterAddress); - counter++; + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}, + {Rio(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium)}); - if (routerAddr == routerAddressA) - { - if (entry.mIsOnLink) - { - VerifyOrQuit(AsCoreType(&entry.mPrefix) == onLinkPrefix); - VerifyOrQuit(entry.mValidLifetime = kValidLitime); - VerifyOrQuit(entry.mPreferredLifetime = kPreferredLifetime); - } - else - { - VerifyOrQuit(AsCoreType(&entry.mPrefix) == routePrefix); - VerifyOrQuit(entry.mValidLifetime = kValidLitime); - VerifyOrQuit(static_cast(entry.mRoutePreference) == NetworkData::kRoutePreferenceMedium); - } - } - else if (routerAddr == routerAddressB) - { - VerifyOrQuit(!entry.mIsOnLink); - VerifyOrQuit(AsCoreType(&entry.mPrefix) == routePrefix); - VerifyOrQuit(entry.mValidLifetime = kValidLitime); - VerifyOrQuit(static_cast(entry.mRoutePreference) == NetworkData::kRoutePreferenceHigh); - } - else - { - VerifyOrQuit(false, "Unexpected entry in prefix table with unknown router address"); - } - } + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that the local on-link prefix is now deprecating in the new RA. - VerifyOrQuit(counter == 3); + sRaValidated = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + + AdvanceTime(10000); + VerifyOrQuit(sRaValidated); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Check Network Data. + // Check the discovered prefix table and ensure info from router A + // is present in the table. - iterator = NetworkData::kIteratorInit; + VerifyPrefixTable({OnLinkPrefix(onLinkPrefix, kValidLitime, kPreferredLifetime, routerAddressA)}, + {RoutePrefix(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium, routerAddressA)}); - // We expect to see OMR prefix in net data as on-mesh prefix - SuccessOrQuit(instance.Get().GetNextOnMeshPrefix(iterator, prefixConfig)); - VerifyOrQuit(prefixConfig.GetPrefix() == localOmr); - VerifyOrQuit(prefixConfig.mStable == true); - VerifyOrQuit(prefixConfig.mSlaac == true); - VerifyOrQuit(prefixConfig.mPreferred == true); - VerifyOrQuit(prefixConfig.mOnMesh == true); - VerifyOrQuit(prefixConfig.mDefaultRoute == false); + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to include new prefixes from router A. - VerifyOrQuit(instance.Get().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNotFound); + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ true); + VerifyExternalRouteInNetData(kDefaultRoute); - iterator = NetworkData::kIteratorInit; + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send the same RA again from router A with the on-link (PIO) and route prefix (RIO). - counter = 0; + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}, + {Rio(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium)}); - // We expect to see 3 entries, our local on link and new prefixes - // from router A and B. The `routePrefix` now should have high - // preference. + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check the discovered prefix table and ensure info from router A + // remains unchanged. - while (instance.Get().GetNextExternalRoute(iterator, routeConfig) == kErrorNone) - { - counter++; + VerifyPrefixTable({OnLinkPrefix(onLinkPrefix, kValidLitime, kPreferredLifetime, routerAddressA)}, + {RoutePrefix(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium, routerAddressA)}); - if (routeConfig.GetPrefix() == routePrefix) - { - VerifyOrQuit(static_cast(routeConfig.mPreference) == NetworkData::kRoutePreferenceHigh); - } - else - { - VerifyOrQuit((routeConfig.GetPrefix() == localOnLink) || (routeConfig.GetPrefix() == onLinkPrefix)); - VerifyOrQuit(static_cast(routeConfig.mPreference) == NetworkData::kRoutePreferenceMedium); - } - } + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router B with same route prefix (RIO) but with + // high route preference. + + SendRouterAdvert(routerAddressB, {Rio(routePrefix, kValidLitime, NetworkData::kRoutePreferenceHigh)}); - VerifyOrQuit(counter == 3); + AdvanceTime(10000); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Send an RA from router B removing the route prefix. + // Check the discovered prefix table and ensure info from router B + // is also included in the table. - { - Ip6::Nd::RouterAdvertMessage raMsg(Ip6::Nd::RouterAdvertMessage::Header(), buffer); + VerifyPrefixTable({OnLinkPrefix(onLinkPrefix, kValidLitime, kPreferredLifetime, routerAddressA)}, + {RoutePrefix(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium, routerAddressA), + RoutePrefix(routePrefix, kValidLitime, NetworkData::kRoutePreferenceHigh, routerAddressB)}); - SuccessOrQuit(raMsg.AppendRouteInfoOption(routePrefix, 0, NetworkData::kRoutePreferenceHigh)); + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. - SendRouterAdvert(routerAddressB, raMsg.GetAsPacket()); + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ true); + VerifyExternalRouteInNetData(kDefaultRoute); - Log("Send RA from router B"); - LogRouterAdvert(raMsg.GetAsPacket()); - } + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router B removing the route prefix. + + SendRouterAdvert(routerAddressB, {Rio(routePrefix, 0, NetworkData::kRoutePreferenceHigh)}); AdvanceTime(10000); @@ -711,48 +983,63 @@ void TestRoutingManager(void) // Check the discovered prefix table and ensure info from router B // is now removed from the table. - counter = 0; + VerifyPrefixTable({OnLinkPrefix(onLinkPrefix, kValidLitime, kPreferredLifetime, routerAddressA)}, + {RoutePrefix(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium, routerAddressA)}); - rm.InitPrefixTableIterator(iter); + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. - while (rm.GetNextPrefixTableEntry(iter, entry) == kErrorNone) - { - counter++; + VerifyExternalRouteInNetData(kDefaultRoute); - VerifyOrQuit(AsCoreType(&entry.mRouterAddress) == routerAddressA); + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if (entry.mIsOnLink) - { - VerifyOrQuit(AsCoreType(&entry.mPrefix) == onLinkPrefix); - VerifyOrQuit(entry.mValidLifetime = kValidLitime); - VerifyOrQuit(entry.mPreferredLifetime = kPreferredLifetime); - } - else - { - VerifyOrQuit(AsCoreType(&entry.mPrefix) == routePrefix); - VerifyOrQuit(entry.mValidLifetime = kValidLitime); - VerifyOrQuit(static_cast(entry.mRoutePreference) == NetworkData::kRoutePreferenceMedium); - } - } + Log("End of TestSamePrefixesFromMultipleRouters"); + + FinalizeTest(); +} + +void TestOmrSelection(void) +{ + Ip6::Prefix localOnLink; + Ip6::Prefix localOmr; + Ip6::Prefix omrPrefix = PrefixFromString("2000:0000:1111:4444::", 64); + NetworkData::OnMeshPrefixConfig prefixConfig; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestOmrSelection"); - VerifyOrQuit(counter == 2); + InitTest(); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Check Network Data, all prefixes should be again at medium preference. + // Start Routing Manager. Check emitted RS and RA messages. - counter = 0; - iterator = NetworkData::kIteratorInit; + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); - while (instance.Get().GetNextExternalRoute(iterator, routeConfig) == kErrorNone) - { - counter++; + SuccessOrQuit(sInstance->Get().SetEnabled(true)); - VerifyOrQuit((routeConfig.GetPrefix() == localOnLink) || (routeConfig.GetPrefix() == onLinkPrefix) || - (routeConfig.GetPrefix() == routePrefix)); - VerifyOrQuit(static_cast(routeConfig.mPreference) == NetworkData::kRoutePreferenceMedium); - } + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Received RA was validated"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to include the local OMR and on-link prefix. - VerifyOrQuit(counter == 3); + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Add a new OMR prefix directly into net data. The new prefix should @@ -775,45 +1062,22 @@ void TestRoutingManager(void) //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Make sure BR emits RA with new OMR prefix now. - sRaValidated = false; - sSawExpectedRio = false; - sExpectedRioPrefix = omrPrefix; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + sExpectedRios.Add(omrPrefix); AdvanceTime(20000); VerifyOrQuit(sRaValidated); - VerifyOrQuit(sSawExpectedRio); + VerifyOrQuit(sExpectedRios.SawAll()); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Check Network Data. We should now see that the local OMR prefix // is removed. - iterator = NetworkData::kIteratorInit; - - // We expect to see new OMR prefix in net data. - SuccessOrQuit(instance.Get().GetNextOnMeshPrefix(iterator, prefixConfig)); - VerifyOrQuit(prefixConfig.GetPrefix() == omrPrefix); - VerifyOrQuit(prefixConfig.mStable == true); - VerifyOrQuit(prefixConfig.mSlaac == true); - VerifyOrQuit(prefixConfig.mPreferred == true); - VerifyOrQuit(prefixConfig.mOnMesh == true); - VerifyOrQuit(prefixConfig.mDefaultRoute == false); - - VerifyOrQuit(instance.Get().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNotFound); - - counter = 0; - iterator = NetworkData::kIteratorInit; - - while (instance.Get().GetNextExternalRoute(iterator, routeConfig) == kErrorNone) - { - counter++; - - VerifyOrQuit((routeConfig.GetPrefix() == localOnLink) || (routeConfig.GetPrefix() == onLinkPrefix) || - (routeConfig.GetPrefix() == routePrefix)); - VerifyOrQuit(static_cast(routeConfig.mPreference) == NetworkData::kRoutePreferenceMedium); - } - - VerifyOrQuit(counter == 3); + VerifyOmrPrefixInNetData(omrPrefix, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Remove the OMR prefix previously added in net data. @@ -826,59 +1090,1942 @@ void TestRoutingManager(void) //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Make sure BR emits RA with local OMR prefix again. - sRaValidated = false; - sSawExpectedRio = false; - sExpectedRioPrefix = localOmr; + sRaValidated = false; + sExpectedRios.Clear(); + sExpectedRios.Add(localOmr); AdvanceTime(20000); VerifyOrQuit(sRaValidated); - VerifyOrQuit(sSawExpectedRio); + VerifyOrQuit(sExpectedRios.SawAll()); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Check Network Data. We should see that the local OMR prefix is // added again. - iterator = NetworkData::kIteratorInit; + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); - // We expect to see new OMR prefix in net data as on-mesh prefix. - SuccessOrQuit(instance.Get().GetNextOnMeshPrefix(iterator, prefixConfig)); - VerifyOrQuit(prefixConfig.GetPrefix() == localOmr); - VerifyOrQuit(prefixConfig.mStable == true); - VerifyOrQuit(prefixConfig.mSlaac == true); - VerifyOrQuit(prefixConfig.mPreferred == true); - VerifyOrQuit(prefixConfig.mOnMesh == true); - VerifyOrQuit(prefixConfig.mDefaultRoute == false); + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("End of TestOmrSelection"); + FinalizeTest(); +} - VerifyOrQuit(instance.Get().GetNextOnMeshPrefix(iterator, prefixConfig) == kErrorNotFound); +void TestDefaultRoute(void) +{ + Ip6::Prefix localOnLink; + Ip6::Prefix localOmr; + Ip6::Prefix omrPrefix = PrefixFromString("2000:0000:1111:4444::", 64); + Ip6::Prefix defaultRoute = PrefixFromString("::", 0); + Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); + NetworkData::OnMeshPrefixConfig prefixConfig; - counter = 0; - iterator = NetworkData::kIteratorInit; + Log("--------------------------------------------------------------------------------------------"); + Log("TestDefaultRoute"); - while (instance.Get().GetNextExternalRoute(iterator, routeConfig) == kErrorNone) - { - counter++; + InitTest(); - VerifyOrQuit((routeConfig.GetPrefix() == localOnLink) || (routeConfig.GetPrefix() == onLinkPrefix) || - (routeConfig.GetPrefix() == routePrefix)); - VerifyOrQuit(static_cast(routeConfig.mPreference) == NetworkData::kRoutePreferenceMedium); - } + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. Check emitted RS and RA messages. + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); - VerifyOrQuit(counter == 3); + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Received RA was validated"); //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to include the local OMR and ULA prefix. - Log("End of test"); + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); - testFreeInstance(&instance); -} + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send RA from router A advertising a default route. -#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + SendRouterAdvert(routerAddressA, DefaultRoute(kValidLitime, NetworkData::kRoutePreferenceLow)); -int main(void) + AdvanceTime(10000); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check the discovered prefix table and ensure default route + // from router A is in the table. + + VerifyPrefixTable({RoutePrefix(defaultRoute, kValidLitime, NetworkData::kRoutePreferenceLow, routerAddressA)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. We should not see default route in + // Network Data yet since there is no infrastructure-derived + // OMR prefix (with preference medium or higher). + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Add an OMR prefix directly into Network Data with + // preference medium (infrastructure-derived). + + prefixConfig.Clear(); + prefixConfig.mPrefix = omrPrefix; + prefixConfig.mStable = true; + prefixConfig.mSlaac = true; + prefixConfig.mPreferred = true; + prefixConfig.mOnMesh = true; + prefixConfig.mDefaultRoute = true; + prefixConfig.mPreference = NetworkData::kRoutePreferenceMedium; + + SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig)); + SuccessOrQuit(otBorderRouterRegister(sInstance)); + + AdvanceTime(10000); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. Now that we have an infrastructure-derived + // OMR prefix, the default route should be published. + + VerifyOmrPrefixInNetData(omrPrefix, /* aDefaultRoute */ true); + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Remove the OMR prefix from Network Data. + + SuccessOrQuit(otBorderRouterRemoveOnMeshPrefix(sInstance, &omrPrefix)); + SuccessOrQuit(otBorderRouterRegister(sInstance)); + + AdvanceTime(10000); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. We should again go back to ULA prefix. The + // default route advertised by router A should be still present in + // the discovered prefix table. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + VerifyPrefixTable({RoutePrefix(defaultRoute, kValidLitime, NetworkData::kRoutePreferenceLow, routerAddressA)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Add the OMR prefix again. + + SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig)); + SuccessOrQuit(otBorderRouterRegister(sInstance)); + + AdvanceTime(10000); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. Again the default route should be published. + + VerifyOmrPrefixInNetData(omrPrefix, /* aDefaultRoute */ true); + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send RA from router A removing the default route. + + SendRouterAdvert(routerAddressA, DefaultRoute(0, NetworkData::kRoutePreferenceLow)); + + AdvanceTime(10000); + + VerifyPrefixTableIsEmpty(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. Now that router A no longer advertised + // a default-route, we should go back to publishing ULA route. + + VerifyOmrPrefixInNetData(omrPrefix, /* aDefaultRoute */ true); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send RA from router A again advertising a default route. + + SendRouterAdvert(routerAddressA, DefaultRoute(kValidLitime, NetworkData::kRoutePreferenceLow)); + + AdvanceTime(10000); + + VerifyPrefixTable({RoutePrefix(defaultRoute, kValidLitime, NetworkData::kRoutePreferenceLow, routerAddressA)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. We should see default route published. + + VerifyOmrPrefixInNetData(omrPrefix, /* aDefaultRoute */ true); + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("End of TestDefaultRoute"); + + FinalizeTest(); +} + +void TestAdvNonUlaRoute(void) { -#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE - TestRoutingManager(); + Ip6::Prefix localOnLink; + Ip6::Prefix localOmr; + Ip6::Prefix omrPrefix = PrefixFromString("2000:0000:1111:4444::", 64); + Ip6::Prefix routePrefix = PrefixFromString("2000:1234:5678::", 64); + Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); + NetworkData::OnMeshPrefixConfig prefixConfig; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestAdvNonUlaRoute"); + + InitTest(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. Check emitted RS and RA messages. + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Received RA was validated"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to include the local OMR and ULA prefix. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send RA from router A advertising a non-ULA. + + SendRouterAdvert(routerAddressA, {Rio(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium)}); + + AdvanceTime(10000); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check the discovered prefix table and ensure the non-ULA + // from router A is in the table. + + VerifyPrefixTable({RoutePrefix(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium, routerAddressA)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. We should not see default route in + // Network Data yet since there is no infrastructure-derived + // OMR prefix (with preference medium or higher). + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Add an OMR prefix directly into Network Data with + // preference medium (infrastructure-derived). + + prefixConfig.Clear(); + prefixConfig.mPrefix = omrPrefix; + prefixConfig.mStable = true; + prefixConfig.mSlaac = true; + prefixConfig.mPreferred = true; + prefixConfig.mOnMesh = true; + prefixConfig.mDefaultRoute = true; + prefixConfig.mPreference = NetworkData::kRoutePreferenceMedium; + + SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig)); + SuccessOrQuit(otBorderRouterRegister(sInstance)); + + AdvanceTime(10000); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. Now that we have an infrastructure-derived + // OMR prefix, the default route should be published. + + VerifyOmrPrefixInNetData(omrPrefix, /* aDefaultRoute */ true); + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Remove the OMR prefix from Network Data. + + SuccessOrQuit(otBorderRouterRemoveOnMeshPrefix(sInstance, &omrPrefix)); + SuccessOrQuit(otBorderRouterRegister(sInstance)); + + AdvanceTime(10000); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. We should again go back to ULA prefix. The + // non-ULA route advertised by router A should be still present in + // the discovered prefix table. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + VerifyPrefixTable({RoutePrefix(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium, routerAddressA)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Add the OMR prefix again. + + SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig)); + SuccessOrQuit(otBorderRouterRegister(sInstance)); + + AdvanceTime(10000); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. Again the default route should be published. + + VerifyOmrPrefixInNetData(omrPrefix, /* aDefaultRoute */ true); + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send RA from router A removing the route. + + SendRouterAdvert(routerAddressA, {Rio(routePrefix, 0, NetworkData::kRoutePreferenceMedium)}); + + AdvanceTime(10000); + + VerifyPrefixTableIsEmpty(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. Now that router A no longer advertised + // the route, we should go back to publishing the ULA route. + + VerifyOmrPrefixInNetData(omrPrefix, /* aDefaultRoute */ true); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send RA from router A again advertising the route again. + + SendRouterAdvert(routerAddressA, {Rio(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium)}); + + AdvanceTime(10000); + + VerifyPrefixTable({RoutePrefix(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium, routerAddressA)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. We should see default route published. + + VerifyOmrPrefixInNetData(omrPrefix, /* aDefaultRoute */ true); + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("End of TestAdvNonUlaRoute"); + + FinalizeTest(); +} + +void TestLocalOnLinkPrefixDeprecation(void) +{ + static constexpr uint32_t kMaxRaTxInterval = 601; // In seconds + + Ip6::Prefix localOnLink; + Ip6::Prefix localOmr; + Ip6::Prefix onLinkPrefix = PrefixFromString("fd00:abba:baba::", 64); + Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); + uint32_t localOnLinkLifetime; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestLocalOnLinkPrefixDeprecation"); + + InitTest(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. Check emitted RS and RA messages. + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Local on-link prefix is being advertised, lifetime: %d", sOnLinkLifetime); + localOnLinkLifetime = sOnLinkLifetime; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to include the local OMR and on-link prefix. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A with a new on-link (PIO) which is preferred over + // the local on-link prefix. + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that the local on-link prefix is now deprecating in the new RA. + + sRaValidated = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + sExpectedRios.Clear(); + sExpectedRios.Add(localOmr); + + AdvanceTime(10000); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("On-link prefix is deprecating, remaining lifetime:%d", sOnLinkLifetime); + VerifyOrQuit(sOnLinkLifetime < localOnLinkLifetime); + localOnLinkLifetime = sOnLinkLifetime; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. We must see the new on-link prefix from router A + // along with the deprecating local on-link prefix. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Wait for local on-link prefix to expire + + while (localOnLinkLifetime > kMaxRaTxInterval) + { + // Send same RA from router A to keep the on-link prefix alive. + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + // Ensure Network Data entries remain as before. Mainly we still + // see the deprecating local on-link prefix. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + // Keep checking the emitted RAs and make sure on-link prefix + // is included with smaller lifetime every time. + + sRaValidated = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + sExpectedRios.Clear(); + sExpectedRios.Add(localOmr); + + AdvanceTime(kMaxRaTxInterval * 1000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("On-link prefix is deprecating, remaining lifetime:%d", sOnLinkLifetime); + VerifyOrQuit(sOnLinkLifetime < localOnLinkLifetime); + localOnLinkLifetime = sOnLinkLifetime; + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // The local on-link prefix must be expired and should no + // longer be seen in the emitted RA message. + + sRaValidated = false; + sExpectedPio = kNoPio; + sExpectedRios.Clear(); + sExpectedRios.Add(localOmr); + + AdvanceTime(kMaxRaTxInterval * 1000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("On-link prefix is now expired"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("End of TestLocalOnLinkPrefixDeprecation"); + + FinalizeTest(); +} + +#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE +void TestDomainPrefixAsOmr(void) +{ + Ip6::Prefix localOnLink; + Ip6::Prefix localOmr; + Ip6::Prefix domainPrefix = PrefixFromString("2000:0000:1111:4444::", 64); + NetworkData::OnMeshPrefixConfig prefixConfig; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestDomainPrefixAsOmr"); + + InitTest(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. Check emitted RS and RA messages. + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Received RA was validated"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to include the local OMR and on-link prefix. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Add a domain prefix directly into net data. The new prefix should + // be favored over the local OMR prefix. + + otBackboneRouterSetEnabled(sInstance, true); + + prefixConfig.Clear(); + prefixConfig.mPrefix = domainPrefix; + prefixConfig.mStable = true; + prefixConfig.mSlaac = true; + prefixConfig.mPreferred = true; + prefixConfig.mOnMesh = true; + prefixConfig.mDefaultRoute = false; + prefixConfig.mDp = true; + prefixConfig.mPreference = NetworkData::kRoutePreferenceMedium; + + SuccessOrQuit(otBorderRouterAddOnMeshPrefix(sInstance, &prefixConfig)); + SuccessOrQuit(otBorderRouterRegister(sInstance)); + + AdvanceTime(100); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Make sure BR emits RA without domain prefix or previous local OMR. + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + sExpectedRios.Add(domainPrefix); + sExpectedRios.Add(localOmr); + + AdvanceTime(20000); + + VerifyOrQuit(sRaValidated); + + // We should see RIO removing the local OMR prefix with lifetime zero + // and should not see the domain prefix as RIO. + + VerifyOrQuit(sExpectedRios[0].mPrefix == domainPrefix); + VerifyOrQuit(!sExpectedRios[0].mSawInRa); + + VerifyOrQuit(sExpectedRios[1].mPrefix == localOmr); + VerifyOrQuit(sExpectedRios[1].mSawInRa); + VerifyOrQuit(sExpectedRios[1].mLifetime == 0); + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + sExpectedRios.Add(domainPrefix); + sExpectedRios.Add(localOmr); + + // Wait for next RA (650 seconds). + + AdvanceTime(650000); + + VerifyOrQuit(sRaValidated); + + // We should not see either domain prefix or local OMR + // as RIO. + + VerifyOrQuit(!sExpectedRios[0].mSawInRa); + VerifyOrQuit(!sExpectedRios[1].mSawInRa); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. We should now see that the local OMR prefix + // is removed. + + VerifyOmrPrefixInNetData(domainPrefix, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Remove the domain prefix from net data. + + SuccessOrQuit(otBorderRouterRemoveOnMeshPrefix(sInstance, &domainPrefix)); + SuccessOrQuit(otBorderRouterRegister(sInstance)); + + AdvanceTime(100); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Make sure BR emits RA with local OMR prefix again. + + sRaValidated = false; + sExpectedRios.Clear(); + sExpectedRios.Add(localOmr); + + AdvanceTime(20000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data. We should see that the local OMR prefix is + // added again. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("End of TestDomainPrefixAsOmr"); + FinalizeTest(); +} +#endif // OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE + +void TestExtPanIdChange(void) +{ + static constexpr uint32_t kMaxRaTxInterval = 601; // In seconds + + static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x08}}; + static const otExtendedPanId kExtPanId2 = {{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x99, 0x88}}; + static const otExtendedPanId kExtPanId3 = {{0x12, 0x34, 0x56, 0x78, 0x9a, 0xab, 0xcd, 0xef}}; + static const otExtendedPanId kExtPanId4 = {{0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00}}; + static const otExtendedPanId kExtPanId5 = {{0x77, 0x88, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55}}; + + Ip6::Prefix localOnLink; + Ip6::Prefix oldLocalOnLink; + Ip6::Prefix localOmr; + Ip6::Prefix onLinkPrefix = PrefixFromString("2000:abba:baba::", 64); + Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); + uint32_t oldPrefixLifetime; + Ip6::Prefix oldPrefixes[4]; + otOperationalDataset dataset; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestExtPanIdChange"); + + InitTest(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. Check emitted RS and RA messages. + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Local on-link prefix is being advertised, lifetime: %d", sOnLinkLifetime); + + //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + // Check behavior when ext PAN ID changes while the local on-link is + // being advertised. + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Change the extended PAN ID. + + Log("Changing ext PAN ID"); + + oldLocalOnLink = localOnLink; + oldPrefixLifetime = sOnLinkLifetime; + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + SuccessOrQuit(otDatasetGetActive(sInstance, &dataset)); + + VerifyOrQuit(dataset.mComponents.mIsExtendedPanIdPresent); + + dataset.mExtendedPanId = kExtPanId1; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + + AdvanceTime(500); + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + Log("Local on-link prefix changed to %s from %s", localOnLink.ToString().AsCString(), + oldLocalOnLink.ToString().AsCString()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate the received RA message and that it contains the + // old on-link prefix being deprecated. + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1); + VerifyOrQuit(sDeprecatingPrefixes[0].mPrefix == oldLocalOnLink); + oldPrefixLifetime = sDeprecatingPrefixes[0].mLifetime; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate Network Data. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Stop BR and validate that a final RA is emitted deprecating + // both current local on-link prefix and old prefix. + + sRaValidated = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + + SuccessOrQuit(sInstance->Get().SetEnabled(false)); + AdvanceTime(100); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1); + VerifyOrQuit(sDeprecatingPrefixes[0].mPrefix == oldLocalOnLink); + oldPrefixLifetime = sDeprecatingPrefixes[0].mLifetime; + + sRaValidated = false; + AdvanceTime(350000); + VerifyOrQuit(!sRaValidated); + + VerifyNoOmrPrefixInNetData(); + VerifyExternalRouteInNetData(kNoRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start BR again and validate old prefix will continue to + // be deprecated. + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + AdvanceTime(300000); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1); + VerifyOrQuit(sDeprecatingPrefixes[0].mPrefix == oldLocalOnLink); + VerifyOrQuit(oldPrefixLifetime > sDeprecatingPrefixes[0].mLifetime); + oldPrefixLifetime = sDeprecatingPrefixes[0].mLifetime; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Wait for old local on-link prefix to expire. + + while (oldPrefixLifetime > 2 * kMaxRaTxInterval) + { + // Ensure Network Data entries remain as before. + + VerifyExternalRouteInNetData(kUlaRoute); + + // Keep checking the emitted RAs and make sure the prefix + // is included with smaller lifetime every time. + + sRaValidated = false; + + AdvanceTime(kMaxRaTxInterval * 1000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1); + Log("Old on-link prefix is deprecating, remaining lifetime:%d", sDeprecatingPrefixes[0].mLifetime); + VerifyOrQuit(sDeprecatingPrefixes[0].mLifetime < oldPrefixLifetime); + oldPrefixLifetime = sDeprecatingPrefixes[0].mLifetime; + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // The local on-link prefix must be expired now and should no + // longer be seen in the emitted RA message. + + sRaValidated = false; + + AdvanceTime(3 * kMaxRaTxInterval * 1000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.IsEmpty()); + Log("Old on-link prefix is now expired"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate the Network Data. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + // Check behavior when ext PAN ID changes while the local on-link is being + // deprecated. + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A with a new on-link (PIO) which is preferred over + // the local on-link prefix. + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate that the local on-link prefix is deprecated. + + sRaValidated = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Change the extended PAN ID. + + oldLocalOnLink = localOnLink; + oldPrefixLifetime = sOnLinkLifetime; + + dataset.mExtendedPanId = kExtPanId2; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + + AdvanceTime(500); + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + Log("Local on-link prefix changed to %s from %s", localOnLink.ToString().AsCString(), + oldLocalOnLink.ToString().AsCString()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate that the old local on-link prefix is still being included + // as PIO in the emitted RA. + + sRaValidated = false; + sExpectedPio = kNoPio; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1); + VerifyOrQuit(sDeprecatingPrefixes[0].mPrefix == oldLocalOnLink); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate that Network Data. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ true); + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Wait for old local on-link prefix to expire. + + while (oldPrefixLifetime > 2 * kMaxRaTxInterval) + { + // Send same RA from router A to keep its on-link prefix alive. + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + // Ensure Network Data entries remain as before. + + VerifyExternalRouteInNetData(kDefaultRoute); + + // Keep checking the emitted RAs and make sure the prefix + // is included with smaller lifetime every time. + + sRaValidated = false; + + AdvanceTime(kMaxRaTxInterval * 1000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1); + Log("Old on-link prefix is deprecating, remaining lifetime:%d", sDeprecatingPrefixes[0].mLifetime); + VerifyOrQuit(sDeprecatingPrefixes[0].mLifetime < oldPrefixLifetime); + oldPrefixLifetime = sDeprecatingPrefixes[0].mLifetime; + } + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // The old on-link prefix must be expired now and should no + // longer be seen in the emitted RA message. + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + sRaValidated = false; + + AdvanceTime(kMaxRaTxInterval * 1000); + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + AdvanceTime(kMaxRaTxInterval * 1000); + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + AdvanceTime(kMaxRaTxInterval * 1000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.IsEmpty()); + Log("Old on-link prefix is now expired"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate the Network Data. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ true); + VerifyExternalRouteInNetData(kDefaultRoute); + + //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + // Check behavior when ext PAN ID changes while the local on-link is not + // advertised. + + Log("Changing ext PAN ID again"); + + oldLocalOnLink = localOnLink; + + sRaValidated = false; + sExpectedPio = kNoPio; + + dataset.mExtendedPanId = kExtPanId3; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + + AdvanceTime(500); + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + Log("Local on-link prefix changed to %s from %s", localOnLink.ToString().AsCString(), + oldLocalOnLink.ToString().AsCString()); + + AdvanceTime(35000); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.IsEmpty()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate the Network Data. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ true); + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Remove the on-link prefix PIO being advertised by router A + // and ensure local on-link prefix is advertised again. + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, 0)}); + + AdvanceTime(300000); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.IsEmpty()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Wait for longer than valid lifetime of PIO entry from router A. + // Validate that default route is unpublished from network data. + + AdvanceTime(2000 * 1000); + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + // Multiple PAN ID changes and multiple deprecating old prefixes. + + oldPrefixes[0] = localOnLink; + + dataset.mExtendedPanId = kExtPanId2; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + AdvanceTime(30000); + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[0])); + + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Change the prefix again. We should see two deprecating prefixes. + + oldPrefixes[1] = localOnLink; + + dataset.mExtendedPanId = kExtPanId1; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + AdvanceTime(30000); + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 2); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[0])); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[1])); + + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Wait for 15 minutes and then change ext PAN ID again. + // Now we should see three deprecating prefixes. + + AdvanceTime(15 * 60 * 1000); + + oldPrefixes[2] = localOnLink; + + dataset.mExtendedPanId = kExtPanId4; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + AdvanceTime(30000); + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 3); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[0])); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[1])); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[2])); + + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Change ext PAN ID back to previous value of `kExtPanId1`. + // We should still see three deprecating prefixes and the last prefix + // at `oldPrefixes[2]` should again be treated as local on-link prefix. + + oldPrefixes[3] = localOnLink; + + dataset.mExtendedPanId = kExtPanId1; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + AdvanceTime(30000); + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 3); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[0])); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[1])); + VerifyOrQuit(oldPrefixes[2] == localOnLink); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[3])); + + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Stop BR and validate the final emitted RA to contain + // all deprecating prefixes. + + sRaValidated = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + + SuccessOrQuit(sInstance->Get().SetEnabled(false)); + AdvanceTime(100); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 3); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[0])); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[1])); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[3])); + + VerifyNoOmrPrefixInNetData(); + VerifyExternalRouteInNetData(kNoRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Wait for 15 minutes while BR stays disabled and validate + // there are no emitted RAs. We want to check that deprecating + // prefixes continue to expire while BR is stopped. + + sRaValidated = false; + AdvanceTime(15 * 60 * 1000); + + VerifyOrQuit(!sRaValidated); + + VerifyNoOmrPrefixInNetData(); + VerifyExternalRouteInNetData(kNoRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start BR again, and check that we only see the last deprecating prefix + // at `oldPrefixes[3]` in emitted RA and the other two are expired and + // no longer included as PIO and/or in network data. + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[3])); + + VerifyExternalRouteInNetData(kUlaRoute); + + //= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = + // Validate the oldest prefix is removed when we have too many + // back-to-back PAN ID changes. + + // Remember the oldest deprecating prefix (associated with `kExtPanId4`). + oldLocalOnLink = oldPrefixes[3]; + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(oldPrefixes[0])); + dataset.mExtendedPanId = kExtPanId2; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + AdvanceTime(30000); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(oldPrefixes[1])); + dataset.mExtendedPanId = kExtPanId3; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + AdvanceTime(30000); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(oldPrefixes[2])); + dataset.mExtendedPanId = kExtPanId5; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + + sRaValidated = false; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 3); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[0])); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[1])); + VerifyOrQuit(sDeprecatingPrefixes.ContainsMatching(oldPrefixes[2])); + VerifyOrQuit(!sDeprecatingPrefixes.ContainsMatching(oldLocalOnLink)); + + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("End of TestExtPanIdChange"); + FinalizeTest(); +} + +void TestRouterNsProbe(void) +{ + Ip6::Prefix localOnLink; + Ip6::Prefix localOmr; + Ip6::Prefix onLinkPrefix = PrefixFromString("2000:abba:baba::", 64); + Ip6::Prefix routePrefix = PrefixFromString("2000:1234:5678::", 64); + Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); + Ip6::Address routerAddressB = AddressFromString("fd00::bbbb"); + + Log("--------------------------------------------------------------------------------------------"); + Log("TestRouterNsProbe"); + + InitTest(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. Check emitted RS and RA messages. + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Received RA was validated"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A with a new on-link (PIO) and route prefix (RIO). + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}, + {Rio(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium)}); + + sExpectedPio = kPioDeprecatingLocalOnLink; + + AdvanceTime(10); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check the discovered prefix table and ensure info from router A + // is present in the table. + + VerifyPrefixTable({OnLinkPrefix(onLinkPrefix, kValidLitime, kPreferredLifetime, routerAddressA)}, + {RoutePrefix(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium, routerAddressA)}); + + AdvanceTime(30000); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router B with same route prefix (RIO) but with + // high route preference. + + SendRouterAdvert(routerAddressB, {Rio(routePrefix, kValidLitime, NetworkData::kRoutePreferenceHigh)}); + + AdvanceTime(200); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check the discovered prefix table and ensure entries from + // both router A and B are seen. + + VerifyPrefixTable({OnLinkPrefix(onLinkPrefix, kValidLitime, kPreferredLifetime, routerAddressA)}, + {RoutePrefix(routePrefix, kValidLitime, NetworkData::kRoutePreferenceMedium, routerAddressA), + RoutePrefix(routePrefix, kValidLitime, NetworkData::kRoutePreferenceHigh, routerAddressB)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that BR emitted an NS to ensure routers are active. + + sNsEmitted = false; + sRsEmitted = false; + + AdvanceTime(160 * 1000); + + VerifyOrQuit(sNsEmitted); + VerifyOrQuit(!sRsEmitted); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disallow responding to NS message. + // + // This should trigger `RoutingManager` to send RS (which will get + // no response as well) and then remove all router entries. + + sRespondToNs = false; + + sExpectedPio = kPioAdvertisingLocalOnLink; + sRaValidated = false; + sNsEmitted = false; + + AdvanceTime(240 * 1000); + + VerifyOrQuit(sNsEmitted); + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check the discovered prefix table. We should see the on-link entry from + // router A as deprecated and no route prefix. + + VerifyPrefixTable({OnLinkPrefix(onLinkPrefix, kValidLitime, 0, routerAddressA)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Verify that no more NS is being sent (since there is no more valid + // router entry in the table). + + sExpectedPio = kPioAdvertisingLocalOnLink; + sRaValidated = false; + sNsEmitted = false; + + AdvanceTime(6 * 60 * 1000); + + VerifyOrQuit(!sNsEmitted); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router B and verify that we see router B + // entry in prefix table. + + SendRouterAdvert(routerAddressB, {Rio(routePrefix, kValidLitime, NetworkData::kRoutePreferenceHigh)}); + + VerifyPrefixTable({OnLinkPrefix(onLinkPrefix, kValidLitime, 0, routerAddressA)}, + {RoutePrefix(routePrefix, kValidLitime, NetworkData::kRoutePreferenceHigh, routerAddressB)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Wait for longer than router active time before NS probe. + // Check again that NS are sent again. + + sRespondToNs = true; + sNsEmitted = false; + sRsEmitted = false; + + AdvanceTime(3 * 60 * 1000); + + VerifyOrQuit(sNsEmitted); + VerifyOrQuit(!sRsEmitted); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("End of TestRouterNsProbe"); + FinalizeTest(); +} + +void TestConflictingPrefix(void) +{ + static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x08}}; + + Ip6::Prefix localOnLink; + Ip6::Prefix oldLocalOnLink; + Ip6::Prefix localOmr; + Ip6::Prefix onLinkPrefix = PrefixFromString("2000:abba:baba::", 64); + Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); + Ip6::Address routerAddressB = AddressFromString("fd00::bbbb"); + otOperationalDataset dataset; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestConflictingPrefix"); + + InitTest(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. Check emitted RS and RA messages. + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Received RA was validated"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to include the local OMR and on-link prefix. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A with our local on-link prefix as RIO. + + Log("Send RA from router A with local on-link as RIO"); + SendRouterAdvert(routerAddressA, {Rio(localOnLink, kValidLitime, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that the local on-link prefix is still being advertised. + + sRaValidated = false; + AdvanceTime(610000); + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to still include the local OMR and ULA prefix. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A removing local on-link prefix as RIO. + + SendRouterAdvert(routerAddressA, {Rio(localOnLink, 0, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Verify that ULA prefix is still included in Network Data and + // the change by router A did not cause it to be unpublished. + + AdvanceTime(10000); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that the local on-link prefix is still being advertised. + + sRaValidated = false; + AdvanceTime(610000); + VerifyOrQuit(sRaValidated); + + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send RA from router B advertising an on-link prefix. This + // should cause local on-link prefix to be deprecated. + + SendRouterAdvert(routerAddressB, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that the local on-link prefix is now deprecating. + + sRaValidated = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + + AdvanceTime(10000); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("On-link prefix is deprecating, remaining lifetime:%d", sOnLinkLifetime); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to include the default route now due + // the new on-link prefix from router B. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ true); + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A again adding local on-link prefix as RIO. + + Log("Send RA from router A with local on-link as RIO"); + SendRouterAdvert(routerAddressA, {Rio(localOnLink, kValidLitime, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that the local on-link prefix is still being deprecated. + + sRaValidated = false; + AdvanceTime(610000); + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data remains unchanged. + + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A removing the previous RIO. + + SendRouterAdvert(routerAddressA, {Rio(localOnLink, 0, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data remains unchanged. + + AdvanceTime(60000); + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send RA from router B removing its on-link prefix. + + SendRouterAdvert(routerAddressB, {Pio(onLinkPrefix, kValidLitime, 0)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that the local on-link prefix is once again being advertised. + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + AdvanceTime(10000); + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to remain unchanged. + + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Change the extended PAN ID. + + Log("Changing ext PAN ID"); + + SuccessOrQuit(otDatasetGetActive(sInstance, &dataset)); + + VerifyOrQuit(dataset.mComponents.mIsExtendedPanIdPresent); + + dataset.mExtendedPanId = kExtPanId1; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + AdvanceTime(10000); + + oldLocalOnLink = localOnLink; + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + + Log("Local on-link prefix is changed to %s from %s", localOnLink.ToString().AsCString(), + oldLocalOnLink.ToString().AsCString()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data contains default route due to the + // deprecating on-link prefix from router B. + + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A again adding the old local on-link prefix + // as RIO. + + SendRouterAdvert(routerAddressA, {Rio(oldLocalOnLink, kValidLitime, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data remains unchanged. + + AdvanceTime(10000); + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send an RA from router A removing the previous RIO. + + SendRouterAdvert(routerAddressA, {Rio(localOnLink, 0, NetworkData::kRoutePreferenceMedium)}); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data remains unchanged. + + AdvanceTime(10000); + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("End of TestConflictingPrefix"); + + FinalizeTest(); +} + +#if OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE +void TestSavedOnLinkPrefixes(void) +{ + static const otExtendedPanId kExtPanId1 = {{0x01, 0x02, 0x03, 0x04, 0x05, 0x6, 0x7, 0x08}}; + + Ip6::Prefix localOnLink; + Ip6::Prefix oldLocalOnLink; + Ip6::Prefix localOmr; + Ip6::Prefix onLinkPrefix = PrefixFromString("2000:abba:baba::", 64); + Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); + otOperationalDataset dataset; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestSavedOnLinkPrefixes"); + + InitTest(/* aEnablBorderRouting */ true); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check emitted RS and RA messages. + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to include the local OMR and ULA prefix. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable the instance and re-enable it. + + Log("Disabling and re-enabling OT Instance"); + + testFreeInstance(sInstance); + + InitTest(/* aEnablBorderRouting */ true, /* aAfterReset */ true); + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + sExpectedPio = kPioAdvertisingLocalOnLink; + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to include the local OMR and ULA prefix. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send RA from router A advertising an on-link prefix. + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + sRaValidated = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 0); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable the instance and re-enable it. + + Log("Disabling and re-enabling OT Instance"); + + testFreeInstance(sInstance); + + InitTest(/* aEnablBorderRouting */ true, /* aAfterReset */ true); + + sExpectedPio = kPioAdvertisingLocalOnLink; + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to include the local OMR and ULA prefix. + + VerifyOmrPrefixInNetData(localOmr, /* aDefaultRoute */ false); + VerifyExternalRouteInNetData(kUlaRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("Changing ext PAN ID"); + + oldLocalOnLink = localOnLink; + + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + + SuccessOrQuit(otDatasetGetActive(sInstance, &dataset)); + + VerifyOrQuit(dataset.mComponents.mIsExtendedPanIdPresent); + + dataset.mExtendedPanId = kExtPanId1; + dataset.mActiveTimestamp.mSeconds++; + SuccessOrQuit(otDatasetSetActive(sInstance, &dataset)); + + AdvanceTime(30000); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + Log("Local on-link prefix changed to %s from %s", localOnLink.ToString().AsCString(), + oldLocalOnLink.ToString().AsCString()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable the instance and re-enable it. + + Log("Disabling and re-enabling OT Instance"); + + testFreeInstance(sInstance); + + InitTest(/* aEnablBorderRouting */ false, /* aAfterReset */ true); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + AdvanceTime(100); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send RA from router A advertising an on-link prefix. + // This ensures the local on-link prefix is not advertised, but + // it must be deprecated since it was advertised last time and + // saved in `Settings`. + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + sRaValidated = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 1); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data to now use default route due to the + // on-link prefix from router A. + + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Wait for more than 1800 seconds to let the deprecating + // prefixes expire (keep sending RA from router A). + + for (uint16_t index = 0; index < 185; index++) + { + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + AdvanceTime(10 * 1000); + } + + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable the instance and re-enable it and restart Routing Manager. + + Log("Disabling and re-enabling OT Instance again"); + + testFreeInstance(sInstance); + InitTest(/* aEnablBorderRouting */ false, /* aAfterReset */ true); + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + AdvanceTime(100); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Send RA from router A advertising an on-link prefix. + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + sRaValidated = false; + sExpectedPio = kNoPio; + + AdvanceTime(30000); + + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sDeprecatingPrefixes.GetLength() == 0); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check Network Data still contains the default route. + + VerifyExternalRouteInNetData(kDefaultRoute); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("End of TestSavedOnLinkPrefixes"); + FinalizeTest(); +} +#endif // OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE + +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE +void TestAutoEnableOfSrpServer(void) +{ + Ip6::Prefix localOnLink; + Ip6::Prefix localOmr; + Ip6::Address routerAddressA = AddressFromString("fd00::aaaa"); + Ip6::Prefix onLinkPrefix = PrefixFromString("2000:abba:baba::", 64); + + Log("--------------------------------------------------------------------------------------------"); + Log("TestAutoEnableOfSrpServer"); + + InitTest(); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check SRP Server state and enable auto-enable mode + + otSrpServerSetAutoEnableMode(sInstance, true); + VerifyOrQuit(otSrpServerIsAutoEnableMode(sInstance)); + VerifyOrQuit(otSrpServerGetState(sInstance) == OT_SRP_SERVER_STATE_DISABLED); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start Routing Manager. Check emitted RS and RA messages. + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Clear(); + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + + sExpectedRios.Add(localOmr); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Received RA was validated"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate that SRP server was auto-enabled + + VerifyOrQuit(otSrpServerGetState(sInstance) != OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is enabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Signal that infra if state changed and is no longer running. + // This should stop Routing Manager and in turn the SRP server. + + sRaValidated = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + + Log("Signal infra if is not running"); + SuccessOrQuit(otPlatInfraIfStateChanged(sInstance, kInfraIfIndex, false)); + AdvanceTime(1); + + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that SRP server is disabled. + + VerifyOrQuit(otSrpServerGetState(sInstance) == OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is disabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Signal that infra if state changed and is running again. + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kPioAdvertisingLocalOnLink; + sExpectedRios.Add(localOmr); + + Log("Signal infra if is running"); + SuccessOrQuit(otPlatInfraIfStateChanged(sInstance, kInfraIfIndex, true)); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Received RA was validated"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that SRP server is enabled again. + + VerifyOrQuit(otSrpServerGetState(sInstance) != OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is enabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable `RoutingManager` explicitly. + + sRaValidated = false; + sExpectedPio = kPioDeprecatingLocalOnLink; + + Log("Disabling RoutingManager"); + SuccessOrQuit(sInstance->Get().SetEnabled(false)); + AdvanceTime(1); + + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that SRP server is also disabled. + + VerifyOrQuit(otSrpServerGetState(sInstance) == OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is disabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable auto-enable mode on SRP server. + + otSrpServerSetAutoEnableMode(sInstance, false); + VerifyOrQuit(!otSrpServerIsAutoEnableMode(sInstance)); + VerifyOrQuit(otSrpServerGetState(sInstance) == OT_SRP_SERVER_STATE_DISABLED); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Re-start Routing Manager. Check emitted RS and RA messages. + // This cycle, router A will send a RA including a PIO. + + sRsEmitted = false; + sRaValidated = false; + sExpectedPio = kNoPio; + sExpectedRios.Clear(); + + SuccessOrQuit(sInstance->Get().SetEnabled(true)); + + SuccessOrQuit(sInstance->Get().GetOnLinkPrefix(localOnLink)); + SuccessOrQuit(sInstance->Get().GetOmrPrefix(localOmr)); + + Log("Local on-link prefix is %s", localOnLink.ToString().AsCString()); + Log("Local OMR prefix is %s", localOmr.ToString().AsCString()); + + sExpectedRios.Add(localOmr); + + AdvanceTime(2000); + + SendRouterAdvert(routerAddressA, {Pio(onLinkPrefix, kValidLitime, kPreferredLifetime)}); + + AdvanceTime(30000); + + VerifyOrQuit(sRsEmitted); + VerifyOrQuit(sRaValidated); + VerifyOrQuit(sExpectedRios.SawAll()); + Log("Received RA was validated"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check that SRP server is still disabled. + + VerifyOrQuit(otSrpServerGetState(sInstance) == OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is disabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Enable auto-enable mode on SRP server. Since `RoutingManager` + // is already done with initial policy evaluation, the SRP server + // must be started immediately. + + otSrpServerSetAutoEnableMode(sInstance, true); + VerifyOrQuit(otSrpServerIsAutoEnableMode(sInstance)); + + VerifyOrQuit(otSrpServerGetState(sInstance) != OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is enabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable auto-enable mode on SRP server. It must not impact + // its current state and it should remain enabled. + + otSrpServerSetAutoEnableMode(sInstance, false); + VerifyOrQuit(!otSrpServerIsAutoEnableMode(sInstance)); + + AdvanceTime(2000); + VerifyOrQuit(otSrpServerGetState(sInstance) != OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is enabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Signal that infra if state changed and is no longer running. + // This should stop Routing Manager. + + sRaValidated = false; + + Log("Signal infra if is not running"); + SuccessOrQuit(otPlatInfraIfStateChanged(sInstance, kInfraIfIndex, false)); + AdvanceTime(1); + + VerifyOrQuit(sRaValidated); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Re-enable auto-enable mode on SRP server. Since `RoutingManager` + // is stopped (infra if is down), the SRP serer must be stopped + // immediately. + + otSrpServerSetAutoEnableMode(sInstance, true); + VerifyOrQuit(otSrpServerIsAutoEnableMode(sInstance)); + + VerifyOrQuit(otSrpServerGetState(sInstance) == OT_SRP_SERVER_STATE_DISABLED); + Log("Srp::Server is disabled"); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Log("End of TestAutoEnableOfSrpServer"); + FinalizeTest(); +} +#endif // OPENTHREAD_CONFIG_SRP_SERVER_ENABLE + +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + +int main(void) +{ +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + TestSamePrefixesFromMultipleRouters(); + TestOmrSelection(); + TestDefaultRoute(); + TestAdvNonUlaRoute(); + TestLocalOnLinkPrefixDeprecation(); +#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE + TestDomainPrefixAsOmr(); +#endif + TestExtPanIdChange(); + TestConflictingPrefix(); + TestRouterNsProbe(); +#if OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE + TestSavedOnLinkPrefixes(); +#endif +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE + TestAutoEnableOfSrpServer(); +#endif + printf("All tests passed\n"); #else printf("BORDER_ROUTING feature is not enabled\n"); diff --git a/tests/unit/test_serial_number.cpp b/tests/unit/test_serial_number.cpp index dc8d7a1ebb5..df5f1185114 100644 --- a/tests/unit/test_serial_number.cpp +++ b/tests/unit/test_serial_number.cpp @@ -32,7 +32,9 @@ #include "test_util.h" #include "common/code_utils.hpp" +#include "common/num_utils.hpp" #include "common/numeric_limits.hpp" +#include "common/preference.hpp" #include "common/serial_number.hpp" namespace ot { @@ -65,6 +67,152 @@ template void TestSerialNumber(const char *aName) printf("TestSerialNumber<%s>() passed\n", aName); } +void TestNumUtils(void) +{ + uint16_t u16; + uint32_t u32; + + VerifyOrQuit(Min(1, 2) == 1); + VerifyOrQuit(Min(2, 1) == 1); + VerifyOrQuit(Min(1, 1) == 1); + + VerifyOrQuit(Max(1, 2) == 2); + VerifyOrQuit(Max(2, 1) == 2); + VerifyOrQuit(Max(1, 1) == 1); + + VerifyOrQuit(Clamp(1, 5, 10) == 5); + VerifyOrQuit(Clamp(5, 5, 10) == 5); + VerifyOrQuit(Clamp(7, 5, 10) == 7); + VerifyOrQuit(Clamp(10, 5, 10) == 10); + VerifyOrQuit(Clamp(12, 5, 10) == 10); + + VerifyOrQuit(Clamp(10, 10, 10) == 10); + VerifyOrQuit(Clamp(9, 10, 10) == 10); + VerifyOrQuit(Clamp(11, 10, 10) == 10); + + u16 = 100; + VerifyOrQuit(ClampToUint8(u16) == 100); + u16 = 255; + VerifyOrQuit(ClampToUint8(u16) == 255); + u16 = 256; + VerifyOrQuit(ClampToUint8(u16) == 255); + u16 = 400; + VerifyOrQuit(ClampToUint8(u16) == 255); + + u32 = 100; + VerifyOrQuit(ClampToUint16(u32) == 100); + u32 = 256; + VerifyOrQuit(ClampToUint16(u32) == 256); + u32 = 0xffff; + VerifyOrQuit(ClampToUint16(u32) == 0xffff); + u32 = 0x10000; + VerifyOrQuit(ClampToUint16(u32) == 0xffff); + u32 = 0xfff0000; + VerifyOrQuit(ClampToUint16(u32) == 0xffff); + + VerifyOrQuit(ThreeWayCompare(2, 2) == 0); + VerifyOrQuit(ThreeWayCompare(2, 1) > 0); + VerifyOrQuit(ThreeWayCompare(1, 2) < 0); + + VerifyOrQuit(ThreeWayCompare(false, false) == 0); + VerifyOrQuit(ThreeWayCompare(true, true) == 0); + VerifyOrQuit(ThreeWayCompare(true, false) > 0); + VerifyOrQuit(ThreeWayCompare(false, true) < 0); + + VerifyOrQuit(DivideAndRoundToClosest(2, 1) == 2); + VerifyOrQuit(DivideAndRoundToClosest(1, 3) == 0); + VerifyOrQuit(DivideAndRoundToClosest(1, 2) == 1); + VerifyOrQuit(DivideAndRoundToClosest(2, 3) == 1); + VerifyOrQuit(DivideAndRoundToClosest(3, 2) == 2); + VerifyOrQuit(DivideAndRoundToClosest(4, 2) == 2); + + VerifyOrQuit(DivideAndRoundToClosest(0, 10) == 0); + VerifyOrQuit(DivideAndRoundToClosest(4, 10) == 0); + VerifyOrQuit(DivideAndRoundToClosest(5, 10) == 1); + VerifyOrQuit(DivideAndRoundToClosest(9, 10) == 1); + VerifyOrQuit(DivideAndRoundToClosest(10, 10) == 1); + + VerifyOrQuit(CountBitsInMask(0) == 0); + VerifyOrQuit(CountBitsInMask(1) == 1); + VerifyOrQuit(CountBitsInMask(2) == 1); + VerifyOrQuit(CountBitsInMask(3) == 2); + VerifyOrQuit(CountBitsInMask(4) == 1); + VerifyOrQuit(CountBitsInMask(7) == 3); + VerifyOrQuit(CountBitsInMask(11) == 3); + VerifyOrQuit(CountBitsInMask(15) == 4); + VerifyOrQuit(CountBitsInMask(0x11) == 2); + VerifyOrQuit(CountBitsInMask(0xef) == 7); + VerifyOrQuit(CountBitsInMask(0xff) == 8); + + VerifyOrQuit(CountBitsInMask(0) == 0); + VerifyOrQuit(CountBitsInMask(0xff00) == 8); + VerifyOrQuit(CountBitsInMask(0xff) == 8); + VerifyOrQuit(CountBitsInMask(0xaa55) == 8); + VerifyOrQuit(CountBitsInMask(0xffff) == 16); + + printf("TestNumUtils() passed\n"); +} + +void TestPreference(void) +{ + VerifyOrQuit(Preference::kHigh == 1); + VerifyOrQuit(Preference::kMedium == 0); + VerifyOrQuit(Preference::kLow == -1); + + // To2BitUint() + VerifyOrQuit(Preference::To2BitUint(Preference::kHigh) == 0x1); + VerifyOrQuit(Preference::To2BitUint(Preference::kMedium) == 0x0); + VerifyOrQuit(Preference::To2BitUint(Preference::kLow) == 0x3); + VerifyOrQuit(Preference::To2BitUint(2) == 0x1); + VerifyOrQuit(Preference::To2BitUint(-2) == 0x3); + VerifyOrQuit(Preference::To2BitUint(127) == 0x1); + VerifyOrQuit(Preference::To2BitUint(-128) == 0x3); + + // From2BitUint() + VerifyOrQuit(Preference::From2BitUint(0x1) == Preference::kHigh); + VerifyOrQuit(Preference::From2BitUint(0x0) == Preference::kMedium); + VerifyOrQuit(Preference::From2BitUint(0x3) == Preference::kLow); + VerifyOrQuit(Preference::From2BitUint(0x2) == Preference::kMedium); + + VerifyOrQuit(Preference::From2BitUint(0x1 | 4) == Preference::kHigh); + VerifyOrQuit(Preference::From2BitUint(0x0 | 4) == Preference::kMedium); + VerifyOrQuit(Preference::From2BitUint(0x3 | 4) == Preference::kLow); + VerifyOrQuit(Preference::From2BitUint(0x2 | 4) == Preference::kMedium); + + VerifyOrQuit(Preference::From2BitUint(0x1 | 0xfc) == Preference::kHigh); + VerifyOrQuit(Preference::From2BitUint(0x0 | 0xfc) == Preference::kMedium); + VerifyOrQuit(Preference::From2BitUint(0x3 | 0xfc) == Preference::kLow); + VerifyOrQuit(Preference::From2BitUint(0x2 | 0xfc) == Preference::kMedium); + + // IsValid() + VerifyOrQuit(Preference::IsValid(Preference::kHigh)); + VerifyOrQuit(Preference::IsValid(Preference::kMedium)); + VerifyOrQuit(Preference::IsValid(Preference::kLow)); + + VerifyOrQuit(!Preference::IsValid(2)); + VerifyOrQuit(!Preference::IsValid(-2)); + VerifyOrQuit(!Preference::IsValid(127)); + VerifyOrQuit(!Preference::IsValid(-128)); + + // Is2BitUintValid + VerifyOrQuit(Preference::Is2BitUintValid(0x1)); + VerifyOrQuit(Preference::Is2BitUintValid(0x0)); + VerifyOrQuit(Preference::Is2BitUintValid(0x3)); + VerifyOrQuit(!Preference::Is2BitUintValid(0x2)); + + VerifyOrQuit(Preference::Is2BitUintValid(0x1 | 4)); + VerifyOrQuit(Preference::Is2BitUintValid(0x0 | 4)); + VerifyOrQuit(Preference::Is2BitUintValid(0x3 | 4)); + VerifyOrQuit(!Preference::Is2BitUintValid(0x2 | 4)); + + VerifyOrQuit(Preference::Is2BitUintValid(0x1 | 0xfc)); + VerifyOrQuit(Preference::Is2BitUintValid(0x0 | 0xfc)); + VerifyOrQuit(Preference::Is2BitUintValid(0x3 | 0xfc)); + VerifyOrQuit(!Preference::Is2BitUintValid(0x2 | 0xfc)); + + printf("TestPreference() passed\n"); +} + } // namespace ot int main(void) @@ -73,6 +221,8 @@ int main(void) ot::TestSerialNumber("uint16_t"); ot::TestSerialNumber("uint32_t"); ot::TestSerialNumber("uint64_t"); + ot::TestNumUtils(); + ot::TestPreference(); printf("\nAll tests passed.\n"); return 0; } diff --git a/tests/unit/test_smart_ptrs.cpp b/tests/unit/test_smart_ptrs.cpp index 273dc9d26a2..70ec98ca452 100644 --- a/tests/unit/test_smart_ptrs.cpp +++ b/tests/unit/test_smart_ptrs.cpp @@ -60,7 +60,7 @@ static constexpr uint16_t kSkipRetainCountCheck = 0xffff; template void VerifyPointer(const PointerType &aPointer, - const TestObject * aObject, + const TestObject *aObject, uint16_t aRetainCount = kSkipRetainCountCheck) { if (aObject == nullptr) diff --git a/tests/unit/test_spinel_buffer.cpp b/tests/unit/test_spinel_buffer.cpp index 4df9764c6d4..0fbdb07a421 100644 --- a/tests/unit/test_spinel_buffer.cpp +++ b/tests/unit/test_spinel_buffer.cpp @@ -58,7 +58,7 @@ static const uint8_t sMysteryText[] = "4871(\\):|(3$}{4|/4/2%14(\\)"; static const uint8_t sHexText[] = "0123456789abcdef"; static ot::Instance *sInstance; -static MessagePool * sMessagePool; +static MessagePool *sMessagePool; struct CallbackContext { @@ -126,10 +126,10 @@ void VerifyAndRemoveTagFromHistory(Spinel::Buffer::FrameTag aTag, Spinel::Buffer } } -void FrameAddedCallback(void * aContext, +void FrameAddedCallback(void *aContext, Spinel::Buffer::FrameTag aTag, Spinel::Buffer::Priority aPriority, - Spinel::Buffer * aNcpBuffer) + Spinel::Buffer *aNcpBuffer) { CallbackContext *callbackContext = reinterpret_cast(aContext); @@ -143,10 +143,10 @@ void FrameAddedCallback(void * aContext, callbackContext->mFrameAddedCount++; } -void FrameRemovedCallback(void * aContext, +void FrameRemovedCallback(void *aContext, Spinel::Buffer::FrameTag aTag, Spinel::Buffer::Priority aPriority, - Spinel::Buffer * aNcpBuffer) + Spinel::Buffer *aNcpBuffer) { CallbackContext *callbackContext = reinterpret_cast(aContext); @@ -173,7 +173,7 @@ void ReadAndVerifyContent(Spinel::Buffer &aNcpBuffer, const uint8_t *aContentBuf void WriteTestFrame1(Spinel::Buffer &aNcpBuffer, Spinel::Buffer::Priority aPriority) { - Message * message; + Message *message; CallbackContext oldContext; message = sMessagePool->Allocate(Message::kTypeIp6); @@ -216,8 +216,8 @@ void VerifyAndRemoveFrame1(Spinel::Buffer &aNcpBuffer) void WriteTestFrame2(Spinel::Buffer &aNcpBuffer, Spinel::Buffer::Priority aPriority) { - Message * message1; - Message * message2; + Message *message1; + Message *message2; CallbackContext oldContext = sContext; message1 = sMessagePool->Allocate(Message::kTypeIp6); @@ -262,7 +262,7 @@ void VerifyAndRemoveFrame2(Spinel::Buffer &aNcpBuffer) void WriteTestFrame3(Spinel::Buffer &aNcpBuffer, Spinel::Buffer::Priority aPriority) { - Message * message1; + Message *message1; CallbackContext oldContext = sContext; message1 = sMessagePool->Allocate(Message::kTypeIp6); @@ -335,7 +335,7 @@ void TestBuffer(void) uint8_t buffer[kTestBufferSize]; Spinel::Buffer ncpBuffer(buffer, kTestBufferSize); - Message * message; + Message *message; uint8_t readBuffer[16]; uint16_t readLen, readOffset; Spinel::Buffer::WritePosition pos1, pos2; diff --git a/tests/unit/test_spinel_decoder.cpp b/tests/unit/test_spinel_decoder.cpp index 13bd82c4321..1a2249ab71e 100644 --- a/tests/unit/test_spinel_decoder.cpp +++ b/tests/unit/test_spinel_decoder.cpp @@ -90,12 +90,12 @@ void TestDecoder(void) int64_t i64; unsigned int u_1, u_2, u_3, u_4; const spinel_ipv6addr_t *ip6Addr; - const spinel_eui48_t * eui48; - const spinel_eui64_t * eui64; - const char * utf_1; - const char * utf_2; - const uint8_t * dataPtr_1; - const uint8_t * dataPtr_2; + const spinel_eui48_t *eui48; + const spinel_eui64_t *eui64; + const char *utf_1; + const char *utf_2; + const uint8_t *dataPtr_1; + const uint8_t *dataPtr_2; uint16_t dataLen_1; uint16_t dataLen_2; diff --git a/tests/unit/test_spinel_encoder.cpp b/tests/unit/test_spinel_encoder.cpp index b640af37922..7acb0d01539 100644 --- a/tests/unit/test_spinel_encoder.cpp +++ b/tests/unit/test_spinel_encoder.cpp @@ -105,11 +105,11 @@ void TestEncoder(void) int64_t i64; unsigned int u_1, u_2, u_3, u_4; spinel_ipv6addr_t *ip6Addr; - spinel_eui48_t * eui48; - spinel_eui64_t * eui64; - const char * utf_1; - const char * utf_2; - const uint8_t * dataPtr; + spinel_eui48_t *eui48; + spinel_eui64_t *eui64; + const char *utf_1; + const char *utf_2; + const uint8_t *dataPtr; spinel_size_t dataLen; memset(buffer, 0, sizeof(buffer)); @@ -145,7 +145,7 @@ void TestEncoder(void) DumpBuffer("Frame", frame, frameLen); parsedLen = spinel_datatype_unpack( - frame, (spinel_size_t)frameLen, + frame, static_cast(frameLen), (SPINEL_DATATYPE_BOOL_S SPINEL_DATATYPE_BOOL_S SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_INT8_S SPINEL_DATATYPE_UINT16_S SPINEL_DATATYPE_INT16_S SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_INT32_S SPINEL_DATATYPE_UINT64_S SPINEL_DATATYPE_INT64_S SPINEL_DATATYPE_UINT_PACKED_S @@ -200,7 +200,7 @@ void TestEncoder(void) DumpBuffer("Frame", frame, frameLen); parsedLen = spinel_datatype_unpack( - frame, (spinel_size_t)frameLen, + frame, static_cast(frameLen), (SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_STRUCT_S( SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_EUI48_S SPINEL_DATATYPE_UINT_PACKED_S) SPINEL_DATATYPE_INT16_S @@ -215,7 +215,7 @@ void TestEncoder(void) VerifyOrQuit(memcmp(eui48, &kEui48, sizeof(spinel_eui48_t)) == 0); // Parse the struct as a "data with len". - parsedLen = spinel_datatype_unpack(frame, (spinel_size_t)frameLen, + parsedLen = spinel_datatype_unpack(frame, static_cast(frameLen), (SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_DATA_WLEN_S SPINEL_DATATYPE_INT16_S ), @@ -258,7 +258,7 @@ void TestEncoder(void) SuccessOrQuit(ReadFrame(ncpBuffer, frame, frameLen)); parsedLen = spinel_datatype_unpack( - frame, (spinel_size_t)frameLen, + frame, static_cast(frameLen), (SPINEL_DATATYPE_STRUCT_S(SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_UTF8_S SPINEL_DATATYPE_STRUCT_S( SPINEL_DATATYPE_BOOL_S SPINEL_DATATYPE_IPv6ADDR_S) SPINEL_DATATYPE_UINT16_S) SPINEL_DATATYPE_EUI48_S SPINEL_DATATYPE_STRUCT_S(SPINEL_DATATYPE_UINT32_S) SPINEL_DATATYPE_INT32_S), @@ -296,7 +296,7 @@ void TestEncoder(void) SuccessOrQuit(ReadFrame(ncpBuffer, frame, frameLen)); parsedLen = spinel_datatype_unpack( - frame, (spinel_size_t)frameLen, + frame, static_cast(frameLen), (SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_STRUCT_S( SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_STRUCT_S(SPINEL_DATATYPE_EUI48_S SPINEL_DATATYPE_UINT_PACKED_S))), &u8, &u32, &eui48, &u_3); @@ -340,7 +340,7 @@ void TestEncoder(void) SuccessOrQuit(ReadFrame(ncpBuffer, frame, frameLen)); parsedLen = spinel_datatype_unpack( - frame, (spinel_size_t)frameLen, + frame, static_cast(frameLen), (SPINEL_DATATYPE_UINT8_S SPINEL_DATATYPE_STRUCT_S( SPINEL_DATATYPE_UINT32_S SPINEL_DATATYPE_IPv6ADDR_S SPINEL_DATATYPE_EUI64_S) SPINEL_DATATYPE_UTF8_S), &u8, &u32, &ip6Addr, &eui64, &utf_1); diff --git a/tests/unit/test_srp_server.cpp b/tests/unit/test_srp_server.cpp new file mode 100644 index 00000000000..34ab9052a01 --- /dev/null +++ b/tests/unit/test_srp_server.cpp @@ -0,0 +1,919 @@ +/* + * 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. + */ + +#include + +#include "test_platform.h" +#include "test_util.hpp" + +#include +#include +#include + +#include "common/arg_macros.hpp" +#include "common/array.hpp" +#include "common/instance.hpp" +#include "common/string.hpp" +#include "common/time.hpp" + +#if OPENTHREAD_CONFIG_SRP_SERVER_ENABLE && OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE && \ + !OPENTHREAD_CONFIG_TIME_SYNC_ENABLE && !OPENTHREAD_PLATFORM_POSIX +#define ENABLE_SRP_TEST 1 +#else +#define ENABLE_SRP_TEST 0 +#endif + +#if ENABLE_SRP_TEST + +using namespace ot; + +// Logs a message and adds current time (sNow) as "::." +#define Log(...) \ + printf("%02u:%02u:%02u.%03u " OT_FIRST_ARG(__VA_ARGS__) "\n", (sNow / 36000000), (sNow / 60000) % 60, \ + (sNow / 1000) % 60, sNow % 1000 OT_REST_ARGS(__VA_ARGS__)) + +static constexpr uint16_t kMaxRaSize = 800; + +static ot::Instance *sInstance; + +static uint32_t sNow = 0; +static uint32_t sAlarmTime; +static bool sAlarmOn = false; + +static otRadioFrame sRadioTxFrame; +static uint8_t sRadioTxFramePsdu[OT_RADIO_FRAME_MAX_SIZE]; +static bool sRadioTxOngoing = false; + +//---------------------------------------------------------------------------------------------------------------------- +// Function prototypes + +void ProcessRadioTxAndTasklets(void); +void AdvanceTime(uint32_t aDuration); + +//---------------------------------------------------------------------------------------------------------------------- +// `otPlatRadio` + +extern "C" { + +otError otPlatRadioTransmit(otInstance *, otRadioFrame *) +{ + sRadioTxOngoing = true; + + return OT_ERROR_NONE; +} + +otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *) { return &sRadioTxFrame; } + +//---------------------------------------------------------------------------------------------------------------------- +// `otPlatAlaram` + +void otPlatAlarmMilliStop(otInstance *) { sAlarmOn = false; } + +void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt) +{ + sAlarmOn = true; + sAlarmTime = aT0 + aDt; +} + +uint32_t otPlatAlarmMilliGetNow(void) { return sNow; } + +//---------------------------------------------------------------------------------------------------------------------- + +Array sHeapAllocatedPtrs; + +#if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE +void *otPlatCAlloc(size_t aNum, size_t aSize) +{ + void *ptr = calloc(aNum, aSize); + + SuccessOrQuit(sHeapAllocatedPtrs.PushBack(ptr)); + + return ptr; +} + +void otPlatFree(void *aPtr) +{ + if (aPtr != nullptr) + { + void **entry = sHeapAllocatedPtrs.Find(aPtr); + + VerifyOrQuit(entry != nullptr, "A heap allocated item is freed twice"); + sHeapAllocatedPtrs.Remove(*entry); + } + + free(aPtr); +} +#endif + +#if OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED +void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) +{ + OT_UNUSED_VARIABLE(aLogLevel); + OT_UNUSED_VARIABLE(aLogRegion); + + va_list args; + + printf(" "); + va_start(args, aFormat); + vprintf(aFormat, args); + va_end(args); + printf("\n"); +} +#endif + +} // extern "C" + +//--------------------------------------------------------------------------------------------------------------------- + +void ProcessRadioTxAndTasklets(void) +{ + do + { + if (sRadioTxOngoing) + { + sRadioTxOngoing = false; + otPlatRadioTxStarted(sInstance, &sRadioTxFrame); + otPlatRadioTxDone(sInstance, &sRadioTxFrame, nullptr, OT_ERROR_NONE); + } + + otTaskletsProcess(sInstance); + } while (otTaskletsArePending(sInstance)); +} + +void AdvanceTime(uint32_t aDuration) +{ + uint32_t time = sNow + aDuration; + + Log("AdvanceTime for %u.%03u", aDuration / 1000, aDuration % 1000); + + while (TimeMilli(sAlarmTime) <= TimeMilli(time)) + { + ProcessRadioTxAndTasklets(); + sNow = sAlarmTime; + otPlatAlarmMilliFired(sInstance); + } + + ProcessRadioTxAndTasklets(); + sNow = time; +} + +void InitTest(void) +{ + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Initialize OT instance. + + sNow = 0; + sInstance = static_cast(testInitInstance()); + + memset(&sRadioTxFrame, 0, sizeof(sRadioTxFrame)); + sRadioTxFrame.mPsdu = sRadioTxFramePsdu; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Initialize Border Router and start Thread operation. + + SuccessOrQuit(otLinkSetPanId(sInstance, 0x1234)); + SuccessOrQuit(otIp6SetEnabled(sInstance, true)); + SuccessOrQuit(otThreadSetEnabled(sInstance, true)); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Ensure device starts as leader. + + AdvanceTime(10000); + + VerifyOrQuit(otThreadGetDeviceRole(sInstance) == OT_DEVICE_ROLE_LEADER); +} + +void FinalizeTest(void) +{ + SuccessOrQuit(otIp6SetEnabled(sInstance, false)); + SuccessOrQuit(otThreadSetEnabled(sInstance, false)); + SuccessOrQuit(otInstanceErasePersistentInfo(sInstance)); + testFreeInstance(sInstance); +} + +//--------------------------------------------------------------------------------------------------------------------- + +enum UpdateHandlerMode +{ + kAccept, // Accept all updates. + kReject, // Reject all updates. + kIgnore // Ignore all updates (do not call `otSrpServerHandleServiceUpdateResult()`). +}; + +static UpdateHandlerMode sUpdateHandlerMode = kAccept; +static bool sProcessedUpdateCallback = false; +static otSrpServerLeaseInfo sUpdateHostLeaseInfo; +static uint32_t sUpdateHostKeyLease; + +void HandleSrpServerUpdate(otSrpServerServiceUpdateId aId, + const otSrpServerHost *aHost, + uint32_t aTimeout, + void *aContext) +{ + Log("HandleSrpServerUpdate() called with %u, timeout:%u", aId, aTimeout); + + VerifyOrQuit(aHost != nullptr); + VerifyOrQuit(aContext == sInstance); + + sProcessedUpdateCallback = true; + + otSrpServerHostGetLeaseInfo(aHost, &sUpdateHostLeaseInfo); + + switch (sUpdateHandlerMode) + { + case kAccept: + otSrpServerHandleServiceUpdateResult(sInstance, aId, kErrorNone); + break; + case kReject: + otSrpServerHandleServiceUpdateResult(sInstance, aId, kErrorFailed); + break; + case kIgnore: + break; + } +} + +static bool sProcessedClientCallback = false; +static Error sLastClientCallbackError = kErrorNone; + +void HandleSrpClientCallback(otError aError, + const otSrpClientHostInfo *aHostInfo, + const otSrpClientService *aServices, + const otSrpClientService *aRemovedServices, + void *aContext) +{ + Log("HandleSrpClientCallback() called with error %s", ErrorToString(aError)); + + VerifyOrQuit(aContext == sInstance); + + sProcessedClientCallback = true; + sLastClientCallbackError = aError; + + OT_UNUSED_VARIABLE(aHostInfo); + OT_UNUSED_VARIABLE(aServices); + OT_UNUSED_VARIABLE(aRemovedServices); +} + +static const char kHostName[] = "myhost"; + +void PrepareService1(Srp::Client::Service &aService) +{ + static const char kServiceName[] = "_srv._udp"; + static const char kInstanceLabel[] = "srv-instance"; + static const char kSub1[] = "_sub1"; + static const char kSub2[] = "_V1234567"; + static const char kSub3[] = "_XYZWS"; + static const char *kSubLabels[] = {kSub1, kSub2, kSub3, nullptr}; + static const char kTxtKey1[] = "ABCD"; + static const uint8_t kTxtValue1[] = {'a', '0'}; + static const char kTxtKey2[] = "Z0"; + static const uint8_t kTxtValue2[] = {'1', '2', '3'}; + static const char kTxtKey3[] = "D"; + static const uint8_t kTxtValue3[] = {0}; + static const otDnsTxtEntry kTxtEntries[] = { + {kTxtKey1, kTxtValue1, sizeof(kTxtValue1)}, + {kTxtKey2, kTxtValue2, sizeof(kTxtValue2)}, + {kTxtKey3, kTxtValue3, sizeof(kTxtValue3)}, + }; + + memset(&aService, 0, sizeof(aService)); + aService.mName = kServiceName; + aService.mInstanceName = kInstanceLabel; + aService.mSubTypeLabels = kSubLabels; + aService.mTxtEntries = kTxtEntries; + aService.mNumTxtEntries = 3; + aService.mPort = 777; + aService.mWeight = 1; + aService.mPriority = 2; +} + +void PrepareService2(Srp::Client::Service &aService) +{ + static const char kService2Name[] = "_00112233667882554._matter._udp"; + static const char kInstance2Label[] = "ABCDEFGHI"; + static const char kSub4[] = "_44444444"; + static const char *kSubLabels2[] = {kSub4, nullptr}; + + memset(&aService, 0, sizeof(aService)); + aService.mName = kService2Name; + aService.mInstanceName = kInstance2Label; + aService.mSubTypeLabels = kSubLabels2; + aService.mTxtEntries = nullptr; + aService.mNumTxtEntries = 0; + aService.mPort = 555; + aService.mWeight = 0; + aService.mPriority = 3; +} + +void ValidateHost(Srp::Server &aServer, const char *aHostName) +{ + // Validate that only a host with `aHostName` is + // registered on SRP server. + + const Srp::Server::Host *host; + const char *name; + + Log("ValidateHost()"); + + host = aServer.GetNextHost(nullptr); + VerifyOrQuit(host != nullptr); + + name = host->GetFullName(); + Log("Hostname: %s", name); + + VerifyOrQuit(StringStartsWith(name, aHostName, kStringCaseInsensitiveMatch)); + VerifyOrQuit(name[strlen(aHostName)] == '.'); + + // Only one host on server + VerifyOrQuit(aServer.GetNextHost(host) == nullptr); +} + +//---------------------------------------------------------------------------------------------------------------------- + +void TestSrpServerBase(void) +{ + Srp::Server *srpServer; + Srp::Client *srpClient; + Srp::Client::Service service1; + Srp::Client::Service service2; + uint16_t heapAllocations; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestSrpServerBase"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + + heapAllocations = sHeapAllocatedPtrs.GetLength(); + + PrepareService1(service1); + PrepareService2(service2); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetAddressMode() == Srp::Server::kAddressModeUnicast); + + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetServiceHandler(HandleSrpServerUpdate, sInstance); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->SetCallback(HandleSrpClientCallback, sInstance); + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + SuccessOrQuit(srpClient->SetHostName(kHostName)); + SuccessOrQuit(srpClient->EnableAutoHostAddress()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register a service, validate that update handler is called. + + SuccessOrQuit(srpClient->AddService(service1)); + + sUpdateHandlerMode = kAccept; + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError == kErrorNone); + + VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered); + ValidateHost(*srpServer, kHostName); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register a second service, validate that update handler is called. + + SuccessOrQuit(srpClient->AddService(service2)); + + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError == kErrorNone); + + VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered); + VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Unregister first service, validate that update handler is called. + + SuccessOrQuit(srpClient->RemoveService(service1)); + + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError == kErrorNone); + + VerifyOrQuit(service1.GetState() == Srp::Client::kRemoved); + VerifyOrQuit(service2.GetState() == Srp::Client::kRegistered); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable SRP server, verify that all heap allocations by SRP server + // are freed. + + Log("Disabling SRP server"); + + srpServer->SetEnabled(false); + AdvanceTime(100); + + VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + FinalizeTest(); + + VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty()); + + Log("End of TestSrpServerBase"); +} + +void TestSrpServerReject(void) +{ + Srp::Server *srpServer; + Srp::Client *srpClient; + Srp::Client::Service service1; + Srp::Client::Service service2; + uint16_t heapAllocations; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestSrpServerReject"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + + heapAllocations = sHeapAllocatedPtrs.GetLength(); + + PrepareService1(service1); + PrepareService2(service2); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetServiceHandler(HandleSrpServerUpdate, sInstance); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->SetCallback(HandleSrpClientCallback, sInstance); + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + SuccessOrQuit(srpClient->SetHostName(kHostName)); + SuccessOrQuit(srpClient->EnableAutoHostAddress()); + + sUpdateHandlerMode = kReject; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register a service, validate that update handler is called + // and rejected and no service is registered. + + SuccessOrQuit(srpClient->AddService(service1)); + + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError != kErrorNone); + + VerifyOrQuit(service1.GetState() != Srp::Client::kRegistered); + + VerifyOrQuit(srpServer->GetNextHost(nullptr) == nullptr); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register a second service, validate that update handler is + // again called and update is rejected. + + SuccessOrQuit(srpClient->AddService(service2)); + + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError != kErrorNone); + + VerifyOrQuit(service1.GetState() != Srp::Client::kRegistered); + VerifyOrQuit(service2.GetState() != Srp::Client::kRegistered); + + VerifyOrQuit(srpServer->GetNextHost(nullptr) == nullptr); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable SRP server, verify that all heap allocations by SRP server + // are freed. + + Log("Disabling SRP server"); + + srpServer->SetEnabled(false); + AdvanceTime(100); + + VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + FinalizeTest(); + + VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty()); + + Log("End of TestSrpServerReject"); +} + +void TestSrpServerIgnore(void) +{ + Srp::Server *srpServer; + Srp::Client *srpClient; + Srp::Client::Service service1; + Srp::Client::Service service2; + uint16_t heapAllocations; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestSrpServerIgnore"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + + heapAllocations = sHeapAllocatedPtrs.GetLength(); + + PrepareService1(service1); + PrepareService2(service2); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetServiceHandler(HandleSrpServerUpdate, sInstance); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->SetCallback(HandleSrpClientCallback, sInstance); + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + SuccessOrQuit(srpClient->SetHostName(kHostName)); + SuccessOrQuit(srpClient->EnableAutoHostAddress()); + + sUpdateHandlerMode = kIgnore; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register a service, validate that update handler is called + // and ignored the update and no service is registered. + + SuccessOrQuit(srpClient->AddService(service1)); + + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError != kErrorNone); + + VerifyOrQuit(service1.GetState() != Srp::Client::kRegistered); + + VerifyOrQuit(srpServer->GetNextHost(nullptr) == nullptr); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register a second service, validate that update handler is + // again called and update is still ignored. + + SuccessOrQuit(srpClient->AddService(service2)); + + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError != kErrorNone); + + VerifyOrQuit(service1.GetState() != Srp::Client::kRegistered); + VerifyOrQuit(service2.GetState() != Srp::Client::kRegistered); + + VerifyOrQuit(srpServer->GetNextHost(nullptr) == nullptr); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable SRP server, verify that all heap allocations by SRP server + // are freed. + + Log("Disabling SRP server"); + + srpServer->SetEnabled(false); + AdvanceTime(100); + + VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + FinalizeTest(); + + VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty()); + + Log("End of TestSrpServerIgnore"); +} + +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE +void TestUpdateLeaseShortVariant(void) +{ + // Test behavior of SRP client and server when short variant of + // Update Lease Option is used (which only include lease interval). + // This test uses `SetUseShortLeaseOption()` method of `Srp::Client` + // which changes the default behavior and is available under the + // `REFERENCE_DEVICE` config. + + Srp::Server *srpServer; + Srp::Server::LeaseConfig leaseConfig; + const Srp::Server::Service *service; + Srp::Client *srpClient; + Srp::Client::Service service1; + uint16_t heapAllocations; + + Log("--------------------------------------------------------------------------------------------"); + Log("TestUpdateLeaseShortVariant"); + + InitTest(); + + srpServer = &sInstance->Get(); + srpClient = &sInstance->Get(); + + heapAllocations = sHeapAllocatedPtrs.GetLength(); + + PrepareService1(service1); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP server. + + SuccessOrQuit(srpServer->SetAddressMode(Srp::Server::kAddressModeUnicast)); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateDisabled); + + srpServer->SetServiceHandler(HandleSrpServerUpdate, sInstance); + + srpServer->SetEnabled(true); + VerifyOrQuit(srpServer->GetState() != Srp::Server::kStateDisabled); + + AdvanceTime(10000); + VerifyOrQuit(srpServer->GetState() == Srp::Server::kStateRunning); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Check the default Lease Config on SRP server. + // Server to accept lease in [30 sec, 27 hours] and + // key-lease in [30 sec, 189 hours]. + + srpServer->GetLeaseConfig(leaseConfig); + + VerifyOrQuit(leaseConfig.mMinLease == 30); // 30 seconds + VerifyOrQuit(leaseConfig.mMaxLease == 27u * 3600); // 27 hours + VerifyOrQuit(leaseConfig.mMinKeyLease == 30); // 30 seconds + VerifyOrQuit(leaseConfig.mMaxKeyLease == 189u * 3600); // 189 hours + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Start SRP client. + + srpClient->SetCallback(HandleSrpClientCallback, sInstance); + + srpClient->EnableAutoStartMode(nullptr, nullptr); + VerifyOrQuit(srpClient->IsAutoStartModeEnabled()); + + AdvanceTime(2000); + VerifyOrQuit(srpClient->IsRunning()); + + SuccessOrQuit(srpClient->SetHostName(kHostName)); + SuccessOrQuit(srpClient->EnableAutoHostAddress()); + + sUpdateHandlerMode = kAccept; + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Change default lease intervals on SRP client and enable + // "use short Update Lease Option" mode. + + srpClient->SetLeaseInterval(15u * 3600); + srpClient->SetKeyLeaseInterval(40u * 3600); + + srpClient->SetUseShortLeaseOption(true); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register a service, validate that update handler is called + // and service is successfully registered. + + SuccessOrQuit(srpClient->AddService(service1)); + + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError == kErrorNone); + + VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered); + + ValidateHost(*srpServer, kHostName); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate the lease info for service on SRP server. The client + // is set up to use "short Update Lease Option format, so it only + // include the lease interval as 15 hours in its request + // message. Server should then see 15 hours for both lease and + // key lease + + VerifyOrQuit(sUpdateHostLeaseInfo.mLease == 15u * 3600 * 1000); + VerifyOrQuit(sUpdateHostLeaseInfo.mKeyLease == 15u * 3600 * 1000); + + // Check that SRP server granted 15 hours for both lease and + // key lease. + + service = srpServer->GetNextHost(nullptr)->GetServices().GetHead(); + VerifyOrQuit(service != nullptr); + VerifyOrQuit(service->GetLease() == 15u * 3600); + VerifyOrQuit(service->GetKeyLease() == 15u * 3600); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Remove the service. + + SuccessOrQuit(srpClient->RemoveService(service1)); + + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError == kErrorNone); + + VerifyOrQuit(service1.GetState() == Srp::Client::kRemoved); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Register the service again, but this time change it to request + // a lease time that is larger than the `LeaseConfig.mMinLease` of + // 27 hours. This ensures that server needs to include the Lease + // Option in its response (since it need to grant a different + // lease interval). + + service1.mLease = 100u * 3600; // 100 hours >= 27 hours. + service1.mKeyLease = 110u * 3600; + + SuccessOrQuit(srpClient->AddService(service1)); + + sProcessedUpdateCallback = false; + sProcessedClientCallback = false; + + AdvanceTime(2 * 1000); + + VerifyOrQuit(sProcessedUpdateCallback); + VerifyOrQuit(sProcessedClientCallback); + VerifyOrQuit(sLastClientCallbackError == kErrorNone); + + VerifyOrQuit(service1.GetState() == Srp::Client::kRegistered); + + ValidateHost(*srpServer, kHostName); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Validate the lease info for service on SRP server. + + // We should see the 100 hours in request from client + VerifyOrQuit(sUpdateHostLeaseInfo.mLease == 100u * 3600 * 1000); + VerifyOrQuit(sUpdateHostLeaseInfo.mKeyLease == 100u * 3600 * 1000); + + // Check that SRP server granted 27 hours for both lease and + // key lease. + + service = srpServer->GetNextHost(nullptr)->GetServices().GetHead(); + VerifyOrQuit(service != nullptr); + VerifyOrQuit(service->GetLease() == 27u * 3600); + VerifyOrQuit(service->GetKeyLease() == 27u * 3600); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Disable SRP server, verify that all heap allocations by SRP server + // are freed. + + Log("Disabling SRP server"); + + srpServer->SetEnabled(false); + AdvanceTime(100); + + VerifyOrQuit(heapAllocations == sHeapAllocatedPtrs.GetLength()); + + //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // Finalize OT instance and validate all heap allocations are freed. + + Log("Finalizing OT instance"); + FinalizeTest(); + + VerifyOrQuit(sHeapAllocatedPtrs.IsEmpty()); + + Log("End of TestUpdateLeaseShortVariant"); +} + +#endif // OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + +#endif // ENABLE_SRP_TEST + +int main(void) +{ +#if ENABLE_SRP_TEST + TestSrpServerBase(); + TestSrpServerReject(); + TestSrpServerIgnore(); +#if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + TestUpdateLeaseShortVariant(); +#endif + printf("All tests passed\n"); +#else + printf("SRP_SERVER or SRP_CLIENT feature is not enabled\n"); +#endif + + return 0; +} diff --git a/tests/unit/test_string.cpp b/tests/unit/test_string.cpp index b32a11126a5..80ff3b099d5 100644 --- a/tests/unit/test_string.cpp +++ b/tests/unit/test_string.cpp @@ -309,12 +309,72 @@ void TestStringToLowercase(void) printf(" -- PASS\n"); } +void TestStringParseUint8(void) +{ + struct TestCase + { + const char *mString; + Error mError; + uint8_t mExpectedValue; + uint16_t mParsedLength; + }; + + static const TestCase kTestCases[] = { + {"0", kErrorNone, 0, 1}, + {"1", kErrorNone, 1, 1}, + {"12", kErrorNone, 12, 2}, + {"91", kErrorNone, 91, 2}, + {"200", kErrorNone, 200, 3}, + {"00000", kErrorNone, 0, 5}, + {"00000255", kErrorNone, 255, 8}, + {"2 00", kErrorNone, 2, 1}, + {"77a12", kErrorNone, 77, 2}, + {"", kErrorParse}, // Does not start with digit char ['0'-'9'] + {"a12", kErrorParse}, // Does not start with digit char ['0'-'9'] + {" 12", kErrorParse}, // Does not start with digit char ['0'-'9'] + {"256", kErrorParse}, // Larger than max `uint8_t` + {"1000", kErrorParse}, // Larger than max `uint8_t` + {"0256", kErrorParse}, // Larger than max `uint8_t` + }; + + printf("\nTest 11: TestStringParseUint8() function\n"); + + for (const TestCase &testCase : kTestCases) + { + const char *string = testCase.mString; + Error error; + uint8_t u8; + + error = StringParseUint8(string, u8); + + VerifyOrQuit(error == testCase.mError); + + if (testCase.mError == kErrorNone) + { + printf("\n%-10s -> %-3u (expect: %-3u), len:%u (expect:%u)", testCase.mString, u8, testCase.mExpectedValue, + static_cast(string - testCase.mString), testCase.mParsedLength); + + VerifyOrQuit(u8 == testCase.mExpectedValue); + VerifyOrQuit(string - testCase.mString == testCase.mParsedLength); + } + else + { + printf("\n%-10s -> kErrorParse", testCase.mString); + } + } + + printf("\n\n -- PASS\n"); +} + +// gcc-4 does not support constexpr function +#if __GNUC__ > 4 static_assert(ot::AreStringsInOrder("a", "b"), "AreStringsInOrder() failed"); static_assert(ot::AreStringsInOrder("aa", "aaa"), "AreStringsInOrder() failed"); static_assert(ot::AreStringsInOrder("", "a"), "AreStringsInOrder() failed"); static_assert(!ot::AreStringsInOrder("cd", "cd"), "AreStringsInOrder() failed"); static_assert(!ot::AreStringsInOrder("z", "abcd"), "AreStringsInOrder() failed"); static_assert(!ot::AreStringsInOrder("0", ""), "AreStringsInOrder() failed"); +#endif } // namespace ot @@ -328,6 +388,7 @@ int main(void) ot::TestStringEndsWith(); ot::TestStringMatch(); ot::TestStringToLowercase(); + ot::TestStringParseUint8(); printf("\nAll tests passed.\n"); return 0; } diff --git a/tests/unit/test_timer.cpp b/tests/unit/test_timer.cpp index 00cc02ed80f..b6ecd665193 100644 --- a/tests/unit/test_timer.cpp +++ b/tests/unit/test_timer.cpp @@ -65,10 +65,7 @@ void otPlatAlarmMilliStartAt(otInstance *, uint32_t aT0, uint32_t aDt) sPlatDt = aDt; } -uint32_t otPlatAlarmMilliGetNow(void) -{ - return sNow; -} +uint32_t otPlatAlarmMilliGetNow(void) { return sNow; } #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE void otPlatAlarmMicroStop(otInstance *) @@ -85,18 +82,12 @@ void otPlatAlarmMicroStartAt(otInstance *, uint32_t aT0, uint32_t aDt) sPlatDt = aDt; } -uint32_t otPlatAlarmMicroGetNow(void) -{ - return sNow; -} +uint32_t otPlatAlarmMicroGetNow(void) { return sNow; } #endif } // extern "C" -void InitCounters(void) -{ - memset(sCallCount, 0, sizeof(sCallCount)); -} +void InitCounters(void) { memset(sCallCount, 0, sizeof(sCallCount)); } /** * `TestTimer` sub-classes `ot::TimerMilli` and provides a handler and a counter to keep track of number of times timer @@ -131,16 +122,10 @@ template class TestTimer : public TimerType template void AlarmFired(otInstance *aInstance); -template <> void AlarmFired(otInstance *aInstance) -{ - otPlatAlarmMilliFired(aInstance); -} +template <> void AlarmFired(otInstance *aInstance) { otPlatAlarmMilliFired(aInstance); } #if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE -template <> void AlarmFired(otInstance *aInstance) -{ - otPlatAlarmMicroFired(aInstance); -} +template <> void AlarmFired(otInstance *aInstance) { otPlatAlarmMicroFired(aInstance); } #endif /** @@ -150,7 +135,7 @@ template int TestOneTimer(void) { const uint32_t kTimeT0 = 1000; const uint32_t kTimerInterval = 10; - ot::Instance * instance = testInitInstance(); + ot::Instance *instance = testInitInstance(); TestTimer timer(*instance); // Test one Timer basic operation. @@ -276,7 +261,7 @@ template int TestTwoTimers(void) { const uint32_t kTimeT0 = 1000; const uint32_t kTimerInterval = 10; - ot::Instance * instance = testInitInstance(); + ot::Instance *instance = testInitInstance(); TestTimer timer1(*instance); TestTimer timer2(*instance); diff --git a/tests/unit/test_tlv.cpp b/tests/unit/test_tlv.cpp new file mode 100644 index 00000000000..1af0121be3e --- /dev/null +++ b/tests/unit/test_tlv.cpp @@ -0,0 +1,194 @@ +/* + * 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. + */ + +#include "test_platform.h" + +#include + +#include "common/instance.hpp" +#include "common/message.hpp" +#include "common/tlvs.hpp" + +#include "test_util.h" + +namespace ot { + +void TestTlv(void) +{ + Instance *instance = testInitInstance(); + Message *message; + Tlv tlv; + ExtendedTlv extTlv; + uint16_t offset; + uint16_t valueOffset; + uint16_t length; + uint8_t buffer[4]; + + VerifyOrQuit(instance != nullptr); + + VerifyOrQuit((message = instance->Get().Allocate(Message::kTypeIp6)) != nullptr); + VerifyOrQuit(message != nullptr); + + VerifyOrQuit(message->GetOffset() == 0); + VerifyOrQuit(message->GetLength() == 0); + + VerifyOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 1, valueOffset, length) == kErrorNotFound); + VerifyOrQuit(Tlv::ReadTlvValue(*message, 0, buffer, 1) == kErrorParse); + + // Add an empty TLV with type 1 and check that we can find it + + offset = message->GetLength(); + + tlv.SetType(1); + tlv.SetLength(0); + SuccessOrQuit(message->Append(tlv)); + + SuccessOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 1, valueOffset, length)); + VerifyOrQuit(valueOffset == sizeof(Tlv)); + VerifyOrQuit(length == 0); + SuccessOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 0)); + VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 1) == kErrorParse); + + // Add an empty extended TLV (type 2), and check that we can find it. + + offset = message->GetLength(); + + extTlv.SetType(2); + extTlv.SetLength(0); + SuccessOrQuit(message->Append(extTlv)); + + SuccessOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 2, valueOffset, length)); + VerifyOrQuit(valueOffset == offset + sizeof(ExtendedTlv)); + VerifyOrQuit(length == 0); + SuccessOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 0)); + VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 1) == kErrorParse); + + // Add a TLV with type 3 with one byte value and check if we can find it. + + offset = message->GetLength(); + + tlv.SetType(3); + tlv.SetLength(1); + SuccessOrQuit(message->Append(tlv)); + SuccessOrQuit(message->Append(0xff)); + + SuccessOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 3, valueOffset, length)); + VerifyOrQuit(valueOffset == offset + sizeof(Tlv)); + VerifyOrQuit(length == 1); + SuccessOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 1)); + VerifyOrQuit(buffer[0] == 0x0ff); + VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 2) == kErrorParse); + + // Add an extended TLV with type 4 with two byte value and check if we can find it. + + offset = message->GetLength(); + + extTlv.SetType(4); + extTlv.SetLength(2); + SuccessOrQuit(message->Append(extTlv)); + SuccessOrQuit(message->Append(0x12)); + SuccessOrQuit(message->Append(0x34)); + + SuccessOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 4, valueOffset, length)); + VerifyOrQuit(valueOffset == offset + sizeof(ExtendedTlv)); + VerifyOrQuit(length == 2); + SuccessOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 1)); + VerifyOrQuit(buffer[0] == 0x12); + SuccessOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 2)); + VerifyOrQuit(buffer[0] == 0x12); + VerifyOrQuit(buffer[1] == 0x34); + VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 3) == kErrorParse); + + // Add a TLV with missing value. + + offset = message->GetLength(); + + tlv.SetType(5); + tlv.SetLength(1); + SuccessOrQuit(message->Append(tlv)); + + VerifyOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 5, valueOffset, length) != kErrorNone); + VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 0) == kErrorParse); + + // Add the missing value. + SuccessOrQuit(message->Append(0xaa)); + + SuccessOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 5, valueOffset, length)); + VerifyOrQuit(valueOffset == offset + sizeof(Tlv)); + VerifyOrQuit(length == 1); + SuccessOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 1)); + VerifyOrQuit(buffer[0] == 0xaa); + VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 2) == kErrorParse); + + // Add an extended TLV with missing value. + + offset = message->GetLength(); + + extTlv.SetType(6); + extTlv.SetLength(2); + SuccessOrQuit(message->Append(extTlv)); + SuccessOrQuit(message->Append(0xbb)); + + VerifyOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 6, valueOffset, length) != kErrorNone); + VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 1) == kErrorParse); + + SuccessOrQuit(message->Append(0xcc)); + + SuccessOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 6, valueOffset, length) != kErrorNone); + VerifyOrQuit(valueOffset == offset + sizeof(ExtendedTlv)); + VerifyOrQuit(length == 2); + SuccessOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 2)); + VerifyOrQuit(buffer[0] == 0xbb); + VerifyOrQuit(buffer[1] == 0xcc); + VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 3) == kErrorParse); + + // Add an extended TLV with overflow length. + + offset = message->GetLength(); + + extTlv.SetType(7); + extTlv.SetLength(0xffff); + SuccessOrQuit(message->Append(extTlv)); + SuccessOrQuit(message->Append(0x11)); + + VerifyOrQuit(Tlv::FindTlvValueOffset(*message, /* aType */ 7, valueOffset, length) != kErrorNone); + VerifyOrQuit(Tlv::ReadTlvValue(*message, offset, buffer, 1) == kErrorParse); + + message->Free(); + + testFreeInstance(instance); +} + +} // namespace ot + +int main(void) +{ + ot::TestTlv(); + printf("All tests passed\n"); + return 0; +} diff --git a/tests/unit/test_toolchain.cpp b/tests/unit/test_toolchain.cpp index 22993e83586..643b502feb0 100644 --- a/tests/unit/test_toolchain.cpp +++ b/tests/unit/test_toolchain.cpp @@ -109,10 +109,7 @@ void test_addr_sizes(void) VerifyOrQuit(sizeof(otNetifAddress) == otNetifAddress_Size_c(), "otNetifAddress should the same in C & C++"); } -void test_addr_bitfield(void) -{ - VerifyOrQuit(CreateNetif_c().mScopeOverrideValid == true, "test_addr_size_cpp"); -} +void test_addr_bitfield(void) { VerifyOrQuit(CreateNetif_c().mScopeOverrideValid == true, "test_addr_size_cpp"); } void test_packed_alignment(void) { diff --git a/tests/unit/test_toolchain_c.c b/tests/unit/test_toolchain_c.c index 0159e4296c3..cf34464ad9b 100644 --- a/tests/unit/test_toolchain_c.c +++ b/tests/unit/test_toolchain_c.c @@ -36,15 +36,9 @@ #include "test_util.h" -uint32_t otNetifAddress_Size_c() -{ - return sizeof(otNetifAddress); -} +uint32_t otNetifAddress_Size_c() { return sizeof(otNetifAddress); } -uint32_t otNetifAddress_offset_mNext_c() -{ - return offsetof(otNetifAddress, mNext); -} +uint32_t otNetifAddress_offset_mNext_c() { return offsetof(otNetifAddress, mNext); } otNetifAddress CreateNetif_c() { diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt index 7191432b896..d57660fe6a3 100644 --- a/third_party/CMakeLists.txt +++ b/third_party/CMakeLists.txt @@ -30,6 +30,4 @@ if(NOT OT_EXTERNAL_MBEDTLS) add_subdirectory(mbedtls) endif() -if(NOT OT_EXCLUDE_TCPLP_LIB) - add_subdirectory(tcplp) -endif() +add_subdirectory(tcplp) diff --git a/third_party/Makefile.am b/third_party/Makefile.am index 5e374316d99..825f6e8465a 100644 --- a/third_party/Makefile.am +++ b/third_party/Makefile.am @@ -30,7 +30,6 @@ include $(abs_top_nlbuild_autotools_dir)/automake/pre.am EXTRA_DIST = \ nlbuild-autotools \ - openthread-test-driver \ $(NULL) # Always package (e.g. for 'make dist') these subdirectories. diff --git a/third_party/mbedtls/CMakeLists.txt b/third_party/mbedtls/CMakeLists.txt index e58ef81d080..f32df163cba 100644 --- a/third_party/mbedtls/CMakeLists.txt +++ b/third_party/mbedtls/CMakeLists.txt @@ -45,6 +45,8 @@ find_program(SED_EXE sed) string(REPLACE "-Wconversion" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") string(REPLACE "-Wconversion" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +set(MBEDTLS_FATAL_WARNINGS OFF CACHE BOOL "Compiler warnings treated as errors" FORCE) + add_subdirectory(repo) if(UNIFDEFALL_EXE AND SED_EXE AND UNIFDEF_VERSION VERSION_GREATER_EQUAL 2.10) diff --git a/third_party/mbedtls/mbedtls-config.h b/third_party/mbedtls/mbedtls-config.h index 997870c2103..ffb50df1424 100644 --- a/third_party/mbedtls/mbedtls-config.h +++ b/third_party/mbedtls/mbedtls-config.h @@ -86,6 +86,9 @@ #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE #define MBEDTLS_KEY_EXCHANGE_PSK_ENABLED +#endif + +#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE || OPENTHREAD_CONFIG_TLS_ENABLE #define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED #endif @@ -103,7 +106,9 @@ #define MBEDTLS_BASE64_C #define MBEDTLS_ECDH_C #define MBEDTLS_ECDSA_C +#if OPENTHREAD_CONFIG_DETERMINISTIC_ECDSA_ENABLE #define MBEDTLS_ECDSA_DETERMINISTIC +#endif #define MBEDTLS_OID_C #define MBEDTLS_PEM_PARSE_C #define MBEDTLS_PK_WRITE_C diff --git a/third_party/openthread-test-driver/LICENSE b/third_party/openthread-test-driver/LICENSE deleted file mode 100644 index d511905c164..00000000000 --- a/third_party/openthread-test-driver/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/third_party/openthread-test-driver/README.md b/third_party/openthread-test-driver/README.md deleted file mode 100644 index acffbab6782..00000000000 --- a/third_party/openthread-test-driver/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# Automake test-driver - -## URL - -http://git.savannah.gnu.org/cgit/automake.git/tree/lib/test-driver - -## Version - -2016-01-11.22 - -## License - -GPLv2 - -## License File - -[LICENSE](https://www.gnu.org/licenses/) - -## Description - -Automake testsuite driver script. - -## Modifications - -Change the test-driver automake script to support -parallel test scripts execution, each one initiated -with a unique number. diff --git a/third_party/openthread-test-driver/test-driver b/third_party/openthread-test-driver/test-driver deleted file mode 100755 index 96f6db86fb8..00000000000 --- a/third_party/openthread-test-driver/test-driver +++ /dev/null @@ -1,165 +0,0 @@ -#! /bin/sh -# test-driver - basic testsuite driver script. - -scriptversion=2016-01-11.22; # UTC - -# Copyright (C) 2011-2015 Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - -# This file is maintained in Automake, please report -# bugs to or send patches to -# . - -# Make unconditional expansion of undefined variables an error. This -# helps a lot in preventing typo-related bugs. -set -u - -usage_error () -{ - echo "$0: $*" >&2 - print_usage >&2 - exit 2 -} - -print_usage () -{ - cat < /dev/null 2>&1 - if [ $? -eq 0 ]; then - break - fi - OFFSET=$(expr $OFFSET + 1) -done - -# Run a test -TEST_NAME="${test_name}" PORT_OFFSET=$OFFSET python3 "$@" >$log_file 2>&1 -estatus=$? - -# Return the offset -rm -rf "${lock_path}.${OFFSET}.lock.d" -# Remove the flash files -rm -f tmp/${OFFSET}_*.flash tmp/${OFFSET}_*.data tmp/${OFFSET}_*.swap - -if test $enable_hard_errors = no && test $estatus -eq 99; then - tweaked_estatus=1 -else - tweaked_estatus=$estatus -fi - -case $tweaked_estatus:$expect_failure in - 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; - 0:*) col=$grn res=PASS recheck=no gcopy=no;; - 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; - 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; - *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; - *:*) col=$red res=FAIL recheck=yes gcopy=yes;; -esac - -# Report the test outcome and exit status in the logs, so that one can -# know whether the test passed or failed simply by looking at the '.log' -# file, without the need of also peaking into the corresponding '.trs' -# file (automake bug#11814). -echo "$res $test_name (exit status: $estatus)" >>$log_file - -# Report outcome to console. -echo "${col}${res}${std}: $test_name" - -# Register the test result, and other relevant metadata. -echo ":test-result: $res" > $trs_file -echo ":global-test-result: $res" >> $trs_file -echo ":recheck: $recheck" >> $trs_file -echo ":copy-in-global-log: $gcopy" >> $trs_file - -# Local Variables: -# mode: shell-script -# sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "scriptversion=" -# time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC0" -# time-stamp-end: "; # UTC" -# End: diff --git a/third_party/tcplp/CMakeLists.txt b/third_party/tcplp/CMakeLists.txt index 3ea41b6ca52..9ec23b8d78b 100644 --- a/third_party/tcplp/CMakeLists.txt +++ b/third_party/tcplp/CMakeLists.txt @@ -43,7 +43,6 @@ set(src_tcplp lib/lbuf.c ) -set(tcplp_static_target "tcplp") string(REPLACE "-Wsign-compare" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") @@ -51,40 +50,58 @@ string(REPLACE "-Wconversion" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") string(REPLACE "-Wunused-parameter" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") -add_library(${tcplp_static_target} STATIC ${src_tcplp}) -target_compile_options(${tcplp_static_target} - PRIVATE - "-Wno-sign-compare" - "-Wno-unused-parameter" -) -set_target_properties(${tcplp_static_target} PROPERTIES OUTPUT_NAME tcplp) -target_include_directories(${tcplp_static_target} - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/bsdtcp - ${CMAKE_CURRENT_SOURCE_DIR}/lib - PRIVATE - ${OT_PUBLIC_INCLUDES} -) - -target_link_libraries(${tcplp_static_target} - PRIVATE - ot-config -) +if(OT_FTD) + add_library(tcplp-ftd STATIC ${src_tcplp}) + target_compile_options(tcplp-ftd + PRIVATE + "-Wno-sign-compare" + "-Wno-unused-parameter" + ) + set_target_properties(tcplp-ftd PROPERTIES OUTPUT_NAME tcplp-ftd) + target_include_directories(tcplp-ftd + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/bsdtcp + ${CMAKE_CURRENT_SOURCE_DIR}/lib + PRIVATE + ${OT_PUBLIC_INCLUDES} + ) -# TCPlp calls functions that are defined by the core OpenThread (like -# "otMessageWrite()"), so we need to add the core library (FTD or MTD, as -# appropriate) as a link dependency. + target_link_libraries(tcplp-ftd + PRIVATE + ot-config + ) -if(OT_FTD) - target_link_libraries(${tcplp_static_target} + target_link_libraries(tcplp-ftd PRIVATE openthread-ftd ) + endif() if(OT_MTD) - target_link_libraries(${tcplp_static_target} + add_library(tcplp-mtd STATIC ${src_tcplp}) + target_compile_options(tcplp-mtd + PRIVATE + "-Wno-sign-compare" + "-Wno-unused-parameter" + ) + set_target_properties(tcplp-mtd PROPERTIES OUTPUT_NAME tcplp-mtd) + target_include_directories(tcplp-mtd + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/bsdtcp + ${CMAKE_CURRENT_SOURCE_DIR}/lib + PRIVATE + ${OT_PUBLIC_INCLUDES} + ) + + target_link_libraries(tcplp-mtd + PRIVATE + ot-config + ) + + target_link_libraries(tcplp-mtd PRIVATE openthread-mtd ) + endif() diff --git a/third_party/tcplp/bsdtcp/tcp_input.c b/third_party/tcplp/bsdtcp/tcp_input.c index 8211f2f3064..d79f2d2f357 100644 --- a/third_party/tcplp/bsdtcp/tcp_input.c +++ b/third_party/tcplp/bsdtcp/tcp_input.c @@ -241,8 +241,12 @@ cc_cong_signal(struct tcpcb *tp, struct tcphdr *th, uint32_t type) tp->t_dupacks = 0; tp->t_bytes_acked = 0; EXIT_RECOVERY(tp->t_flags); + /* + * samkumar: I added the cast to uint64_t below to fix an OpenThread + * code scanning alert relating to integer overflow in multiplication. + */ tp->snd_ssthresh = max(2, min(tp->snd_wnd, tp->snd_cwnd) / 2 / - tp->t_maxseg) * tp->t_maxseg; + tp->t_maxseg) * ((uint64_t) tp->t_maxseg); tp->snd_cwnd = tp->t_maxseg; /* @@ -766,13 +770,14 @@ tcp_input(struct ip6_hdr* ip6, struct tcphdr* th, otMessage* msg, struct tcpcb* */ tcp_dooptions(&to, optp, optlen, TO_SYN); - tp = tcplp_sys_accept_ready(tpl, &ip6->ip6_dst, th->th_sport); // Try to allocate an active socket to accept into + tp = tcplp_sys_accept_ready(tpl, &ip6->ip6_src, th->th_sport); // Try to allocate an active socket to accept into if (tp == NULL) { /* If we couldn't allocate, just ignore the SYN. */ return IPPROTO_DONE; } if (tp == (struct tcpcb *) -1) { rstreason = ECONNREFUSED; + tp = NULL; goto dropwithreset; } tcp_state_change(tp, TCPS_SYN_RECEIVED); @@ -2012,9 +2017,14 @@ tcp_do_segment(struct ip6_hdr* ip6, struct tcphdr *th, otMessage* msg, tp->snd_nxt = th->th_ack; tp->snd_cwnd = tp->t_maxseg; (void) tcp_output(tp); + /* + * samkumar: I added casts to uint64_t below to + * fix an OpenThread code scanning alert relating + * to integer overflow in multiplication. + */ tp->snd_cwnd = tp->snd_ssthresh + - tp->t_maxseg * - (tp->t_dupacks - tp->snd_limited); + ((uint64_t) tp->t_maxseg) * + ((uint64_t) (tp->t_dupacks - tp->snd_limited)); #ifdef INSTRUMENT_TCP tcplp_sys_log("TCP SET_cwnd %d", (int) tp->snd_cwnd); #endif diff --git a/third_party/tcplp/lib/bitmap.c b/third_party/tcplp/lib/bitmap.c index 70d03c147bc..a0dca482119 100644 --- a/third_party/tcplp/lib/bitmap.c +++ b/third_party/tcplp/lib/bitmap.c @@ -67,7 +67,9 @@ void bmp_setrange(uint8_t* buf, size_t start, size_t len) { } else { *first_byte_set |= first_byte_mask; memset(first_byte_set + 1, 0xFF, (size_t) (last_byte_set - first_byte_set - 1)); - *last_byte_set |= last_byte_mask; + if (last_byte_mask != 0x00) { + *last_byte_set |= last_byte_mask; + } } } @@ -89,7 +91,9 @@ void bmp_clrrange(uint8_t* buf, size_t start, size_t len) { } else { *first_byte_clear &= first_byte_mask; memset(first_byte_clear + 1, 0x00, (size_t) (last_byte_clear - first_byte_clear - 1)); - *last_byte_clear &= last_byte_mask; + if (last_byte_mask != 0xFF) { + *last_byte_clear &= last_byte_mask; + } } } @@ -131,6 +135,67 @@ size_t bmp_countset(uint8_t* buf, size_t buflen, size_t start, size_t limit) { return numset; } +static inline uint8_t bmp_read_bit(uint8_t* buf, size_t i) { + size_t byte_index = i >> 3; + size_t bit_index = i & 0x7; // Amount to left shift to get bit in MSB + return ((uint8_t) (buf[byte_index] << bit_index)) >> 7; +} + +static inline void bmp_write_bit(uint8_t* buf, size_t i, uint8_t bit) { + size_t byte_index = i >> 3; + size_t bit_index = i & 0x7; // Amount to left shift to get bit in MSB + size_t bit_shift = 7 - bit_index; // Amount to right shift to get bit in LSB + buf[byte_index] = (buf[byte_index] & ~(1 << bit_shift)) | (bit << bit_shift); +} + +static inline uint8_t bmp_read_byte(uint8_t* buf, size_t i) { + size_t byte_index = i >> 3; + size_t bit_index = i & 0x7; // Amount to left shift to get bit in MSB + if (bit_index == 0) { + return buf[byte_index]; + } + return (buf[byte_index] << bit_index) | (buf[byte_index + 1] >> (8 - bit_index)); +} + +static inline void bmp_write_byte(uint8_t* buf, size_t i, uint8_t byte) { + size_t byte_index = i >> 3; + size_t bit_index = i & 0x7; // Amount to left shift to get bit in MSB + if (bit_index == 0) { + buf[byte_index] = byte; + return; + } + buf[byte_index] = (buf[byte_index] & (0xFF << (8 - bit_index))) | (byte >> bit_index); + buf[byte_index + 1] = (buf[byte_index + 1] & (0xFF >> bit_index)) | (byte << (8 - bit_index)); +} + +void bmp_swap(uint8_t* buf, size_t start_1, size_t start_2, size_t len) { + while ((len & 0x7) != 0) { + uint8_t bit_1 = bmp_read_bit(buf, start_1); + uint8_t bit_2 = bmp_read_bit(buf, start_2); + if (bit_1 != bit_2) { + bmp_write_bit(buf, start_1, bit_2); + bmp_write_bit(buf, start_2, bit_1); + } + + start_1++; + start_2++; + len--; + } + + while (len != 0) { + uint8_t byte_1 = bmp_read_byte(buf, start_1); + uint8_t byte_2 = bmp_read_byte(buf, start_2); + if (byte_1 != byte_2) { + bmp_write_byte(buf, start_1, byte_2); + bmp_write_byte(buf, start_2, byte_1); + } + + start_1 += 8; + start_2 += 8; + len -= 8; + } +} + int bmp_isempty(uint8_t* buf, size_t buflen) { uint8_t* bufend = buf + buflen; while (buf < bufend) { diff --git a/third_party/tcplp/lib/bitmap.h b/third_party/tcplp/lib/bitmap.h index a4ef41a9958..9193ce8534f 100644 --- a/third_party/tcplp/lib/bitmap.h +++ b/third_party/tcplp/lib/bitmap.h @@ -57,6 +57,11 @@ void bmp_clrrange(uint8_t* buf, size_t start, size_t len); which case it returns exactly the number of set bits it found. */ size_t bmp_countset(uint8_t* buf, size_t buflen, size_t start, size_t limit); +/* Swaps two non-overlapping regions of the bitmap. START_1 is the index of + the first region, START_2 is the index of the secoind region, and LEN is + the length of each region, in bits. */ +void bmp_swap(uint8_t* buf, size_t start_1, size_t start_2, size_t len); + /* Returns 1 if the bitmap is all zeros, and 0 otherwise. */ int bmp_isempty(uint8_t* buf, size_t buflen); diff --git a/third_party/tcplp/lib/cbuf.c b/third_party/tcplp/lib/cbuf.c index b7793ac65e9..5662ef209bb 100644 --- a/third_party/tcplp/lib/cbuf.c +++ b/third_party/tcplp/lib/cbuf.c @@ -73,67 +73,73 @@ void cbuf_copy_from_message(void* arr, size_t arr_offset, const void* buffer, si void cbuf_init(struct cbufhead* chdr, uint8_t* buf, size_t len) { chdr->r_index = 0; - chdr->w_index = 0; + chdr->used = 0; chdr->size = len; chdr->buf = buf; } size_t cbuf_used_space(struct cbufhead* chdr) { - if (chdr->w_index >= chdr->r_index) { - return chdr->w_index - chdr->r_index; - } else { - return chdr->size + chdr->w_index - chdr->r_index; - } + return chdr->used; } -/* There's always one byte of lost space so I can distinguish between a full - buffer and an empty buffer. */ size_t cbuf_free_space(struct cbufhead* chdr) { - return chdr->size - 1 - cbuf_used_space(chdr); + return chdr->size - chdr->used; } size_t cbuf_size(struct cbufhead* chdr) { - return chdr->size - 1; + return chdr->size; } bool cbuf_empty(struct cbufhead* chdr) { - return (chdr->w_index == chdr->r_index); + return chdr->used == 0; +} + +static inline size_t cbuf_get_w_index(const struct cbufhead* chdr) { + size_t until_end = chdr->size - chdr->r_index; + if (chdr->used < until_end) { + return chdr->r_index + chdr->used; + } else { + return chdr->used - until_end; + } } size_t cbuf_write(struct cbufhead* chdr, const void* data, size_t data_offset, size_t data_len, cbuf_copier_t copy_from) { size_t free_space = cbuf_free_space(chdr); uint8_t* buf_data; - size_t fw_index; + size_t w_index; size_t bytes_to_end; if (free_space < data_len) { data_len = free_space; } buf_data = chdr->buf; - fw_index = (chdr->w_index + data_len) % chdr->size; - if (fw_index >= chdr->w_index) { - copy_from(buf_data, chdr->w_index, data, data_offset, data_len); + w_index = cbuf_get_w_index(chdr); + bytes_to_end = chdr->size - w_index; + if (data_len <= bytes_to_end) { + copy_from(buf_data, w_index, data, data_offset, data_len); } else { - bytes_to_end = chdr->size - chdr->w_index; - copy_from(buf_data, chdr->w_index, data, data_offset, bytes_to_end); + copy_from(buf_data, w_index, data, data_offset, bytes_to_end); copy_from(buf_data, 0, data, data_offset + bytes_to_end, data_len - bytes_to_end); } - chdr->w_index = fw_index; + chdr->used += data_len; return data_len; } void cbuf_read_unsafe(struct cbufhead* chdr, void* data, size_t data_offset, size_t numbytes, int pop, cbuf_copier_t copy_into) { uint8_t* buf_data = chdr->buf; - size_t fr_index = (chdr->r_index + numbytes) % chdr->size; - size_t bytes_to_end; - if (fr_index >= chdr->r_index) { + size_t bytes_to_end = chdr->size - chdr->r_index; + if (numbytes < bytes_to_end) { copy_into(data, data_offset, buf_data, chdr->r_index, numbytes); + if (pop) { + chdr->r_index += numbytes; + chdr->used -= numbytes; + } } else { - bytes_to_end = chdr->size - chdr->r_index; copy_into(data, data_offset, buf_data, chdr->r_index, bytes_to_end); copy_into(data, data_offset + bytes_to_end, buf_data, 0, numbytes - bytes_to_end); - } - if (pop) { - chdr->r_index = fr_index; + if (pop) { + chdr->r_index = numbytes - bytes_to_end; + chdr->used -= numbytes; + } } } @@ -167,22 +173,113 @@ size_t cbuf_pop(struct cbufhead* chdr, size_t numbytes) { numbytes = used_space; } chdr->r_index = (chdr->r_index + numbytes) % chdr->size; + chdr->used -= numbytes; return numbytes; } +static void cbuf_swap(struct cbufhead* chdr, uint8_t* bitmap, size_t start_1, size_t start_2, size_t length) { + size_t i; + + /* Swap the data regions. */ + for (i = 0; i != length; i++) { + uint8_t temp = chdr->buf[start_1 + i]; + chdr->buf[start_1 + i] = chdr->buf[start_2 + i]; + chdr->buf[start_2 + i] = temp; + } + + /* Swap the bitmaps. */ + if (bitmap) { + bmp_swap(bitmap, start_1, start_2, length); + } +} + +void cbuf_contiguify(struct cbufhead* chdr, uint8_t* bitmap) { + /* + * We treat contiguify as a special case of rotation. In principle, we + * could make this more efficient by inspecting R_INDEX, W_INDEX, and the + * bitmap to only move around in-sequence data and buffered out-of-sequence + * data, while ignoring the other bytes in the circular buffer. We leave + * this as an optimization to implement if/when it becomes necessary. + * + * The rotation algorithm is recursive. It is parameterized by three + * arguments. START_IDX is the index of the first element of the subarray + * that is being rotated. END_IDX is one plus the index of the last element + * of the subarray that is being rotated. MOVE_TO_START_IDX is the index of + * the element that should be located at START_IDX after the rotation. + * + * The algorithm is as follows. First, identify the largest block of data + * starting at MOVE_TO_START_IDX that can be swapped with data starting at + * START_IDX. If MOVE_TO_START_IDX is right at the midpoint of the array, + * then we're done. If it isn't, then we can treat the block of data that + * was just swapped to the beginning of the array as "done", and then + * complete the rotation by recursively rotating the rest of the array. + * + * Here's an example. Suppose that the array is "1 2 3 4 5 6 7 8 9" and + * MOVE_TO_START_IDX is the index of the element "3". First, we swap "1 2" + * AND "3 4" to get "3 4 1 2 5 6 7 8 9". Then, we recursively rotate the + * subarray "1 2 5 6 7 8 9", with MOVE_TO_START_IDX being the index of the + * element "5". The final array is "3 4 5 6 7 8 9 1 2". + * + * Here's another example. Suppose that the array is "1 2 3 4 5 6 7 8 9" + * and MOVE_TO_START_IDX is the index of the element "6". First, we swap + * "1 2 3 4" and "6 7 8 9" to get "6 7 8 9 5 1 2 3 4". Then, we recursively + * rotate the subarray "5 1 2 3 4", with MOVE_TO_START_IDX being the index + * of the element "1". The final array is "6 7 8 9 1 2 3 4 5". + * + * In order for this to work, it's important that the blocks that we + * choose are maximally large. If, in the first example, we swap only the + * elements "1" and "3", then the algorithm won't work. Note that "1 2" and + * "3 4" corresponds to maximally large blocks because if we make the + * blocks any bigger, they would overlap (e.g., "1 2 3" and "3 4 5"). In + * the second example, the block "6 7 8 9" is maximally large because we + * reach the end of the subarray. + * + * The algorithm above is tail-recursive (i.e., there's no more work to do + * after recursively rotating the subarray), so we write it as a while + * loop below. Each iteration of the while loop identifies the blocks to + * swap, swaps the blocks, and then sets up the indices such that the + * next iteration of the loop rotates the appropriate subarray. + * + * The performance of the algorithm is linear in the length of the array, + * with constant space overhead. + */ + size_t start_idx = 0; + const size_t end_idx = chdr->size; + size_t move_to_start_idx = chdr->r_index; + + /* Invariant: start_idx <= move_to_start_idx <= end_idx */ + while (start_idx < move_to_start_idx && move_to_start_idx < end_idx) { + size_t distance_from_start = move_to_start_idx - start_idx; + size_t distance_to_end = end_idx - move_to_start_idx; + if (distance_from_start <= distance_to_end) { + cbuf_swap(chdr, bitmap, start_idx, move_to_start_idx, distance_from_start); + start_idx = move_to_start_idx; + move_to_start_idx = move_to_start_idx + distance_from_start; + } else { + cbuf_swap(chdr, bitmap, start_idx, move_to_start_idx, distance_to_end); + start_idx = start_idx + distance_to_end; + // move_to_start_idx does not change + } + } + + /* Finally, fix up the indices. */ + chdr->r_index = 0; +} + void cbuf_reference(const struct cbufhead* chdr, otLinkedBuffer* first, otLinkedBuffer* second) { - if (chdr->w_index >= chdr->r_index) { + size_t until_end = chdr->size - chdr->r_index; + if (chdr->used <= until_end) { first->mNext = NULL; first->mData = &chdr->buf[chdr->r_index]; - first->mLength = (uint16_t) (chdr->w_index - chdr->r_index); + first->mLength = (uint16_t) chdr->used; } else { first->mNext = second; first->mData = &chdr->buf[chdr->r_index]; - first->mLength = (uint16_t) (chdr->size - chdr->r_index); + first->mLength = (uint16_t) until_end; second->mNext = NULL; second->mData = &chdr->buf[0]; - second->mLength = (uint16_t) chdr->w_index; + second->mLength = (uint16_t) (chdr->used - until_end); } } @@ -197,7 +294,7 @@ size_t cbuf_reass_write(struct cbufhead* chdr, size_t offset, const void* data, } else if (offset + numbytes > free_space) { numbytes = free_space - offset; } - start_index = (chdr->w_index + offset) % chdr->size; + start_index = (cbuf_get_w_index(chdr) + offset) % chdr->size; end_index = (start_index + numbytes) % chdr->size; if (end_index >= start_index) { copy_from(buf_data, start_index, data, data_offset, numbytes); @@ -220,29 +317,29 @@ size_t cbuf_reass_write(struct cbufhead* chdr, size_t offset, const void* data, } size_t cbuf_reass_merge(struct cbufhead* chdr, size_t numbytes, uint8_t* bitmap) { - size_t old_w = chdr->w_index; + size_t old_w = cbuf_get_w_index(chdr); size_t free_space = cbuf_free_space(chdr); size_t bytes_to_end; if (numbytes > free_space) { numbytes = free_space; } - chdr->w_index = (chdr->w_index + numbytes) % chdr->size; if (bitmap) { - if (chdr->w_index >= old_w) { + bytes_to_end = chdr->size - old_w; + if (numbytes <= bytes_to_end) { bmp_clrrange(bitmap, old_w, numbytes); } else { - bytes_to_end = chdr->size - old_w; bmp_clrrange(bitmap, old_w, bytes_to_end); bmp_clrrange(bitmap, 0, numbytes - bytes_to_end); } } + chdr->used += numbytes; return numbytes; } size_t cbuf_reass_count_set(struct cbufhead* chdr, size_t offset, uint8_t* bitmap, size_t limit) { size_t bitmap_size = BITS_TO_BYTES(chdr->size); size_t until_end; - offset = (chdr->w_index + offset) % chdr->size; + offset = (cbuf_get_w_index(chdr) + offset) % chdr->size; until_end = bmp_countset(bitmap, bitmap_size, offset, limit); if (until_end >= limit || until_end < (chdr->size - offset)) { // If we already hit the limit, or if the streak ended before wrapping, then stop here @@ -254,7 +351,7 @@ size_t cbuf_reass_count_set(struct cbufhead* chdr, size_t offset, uint8_t* bitma } int cbuf_reass_within_offset(struct cbufhead* chdr, size_t offset, size_t index) { - size_t range_start = chdr->w_index; + size_t range_start = cbuf_get_w_index(chdr); size_t range_end = (range_start + offset) % chdr->size; if (range_end >= range_start) { return index >= range_start && index < range_end; diff --git a/third_party/tcplp/lib/cbuf.h b/third_party/tcplp/lib/cbuf.h index 7ba6b6d0f44..1668723075b 100644 --- a/third_party/tcplp/lib/cbuf.h +++ b/third_party/tcplp/lib/cbuf.h @@ -42,7 +42,7 @@ struct otLinkedBuffer; /* Represents a circular buffer. */ struct cbufhead { size_t r_index; - size_t w_index; + size_t used; size_t size; uint8_t* buf; }; @@ -64,7 +64,7 @@ void cbuf_copy_from_message(void* arr, size_t arr_offset, const void* buffer, si /* Writes data to the back of the circular buffer using the specified copier. */ size_t cbuf_write(struct cbufhead* chdr, const void* data, size_t data_offset, size_t data_len, cbuf_copier_t copy_from); -/* Reads data from the front ofthe circular buffer using the specified copier. */ +/* Reads data from the front of the circular buffer using the specified copier. */ size_t cbuf_read(struct cbufhead* chdr, void* data, size_t data_offset, size_t numbytes, int pop, cbuf_copier_t copy_into); /* Reads data at the specified offset, in bytes, from the front of the circular buffer using the specified copier. */ @@ -85,12 +85,15 @@ size_t cbuf_size(struct cbufhead* chdr); /* Returns true if the circular buffer is empty, and false if it is not empty. */ bool cbuf_empty(struct cbufhead* chdr); +/* Rotates the circular buffer's data so that the "used" portion begins at the beginning of the buffer. */ +void cbuf_contiguify(struct cbufhead* chdr, uint8_t* bitmap); + /* Populates the provided otLinkedBuffers to reference the data currently in the circular buffer. */ void cbuf_reference(const struct cbufhead* chdr, struct otLinkedBuffer* first, struct otLinkedBuffer* second); /* Writes DATA at the end of the circular buffer without making it available for reading. This data is said to be "out-of-sequence". OFFSET is position at - which to write these bytes, relative to the positoin where cbuf_write would + which to write these bytes, relative to the position where cbuf_write would write them. Each bit in the BITMAP corresponds to a byte in the circular buffer; the bits corresponding to the bytes containing the newly written data are set. The index of the first byte written is stored into FIRSTINDEX, @@ -105,7 +108,15 @@ size_t cbuf_reass_write(struct cbufhead* chdr, size_t offset, const void* data, size_t cbuf_reass_merge(struct cbufhead* chdr, size_t numbytes, uint8_t* bitmap); /* Counts the number of contiguous out-of-sequence bytes at the specified - OFFSET, until the count reaches the specified LIMIT. */ + OFFSET, until the count reaches the specified LIMIT. Note that, for a given, + limit, this function might overcount the length of the continuous + out-of-sequence bytes and return a greater number; the caller is assumed to + handle this appropriately (i.e., treating the limit not as a hard upper + bound on the return value, but rather as, "I don't care if more bits than + this are set"). Just because the function returns something more than + LIMIT, it doesn't necessarily mean that more than LIMIT bits are actually + set. Note that LIMIT should never be set to a value greater than the number + of bytes in the circular buffer. */ size_t cbuf_reass_count_set(struct cbufhead* chdr, size_t offset, uint8_t* bitmap, size_t limit); /* Returns a true value iff INDEX is the index of a byte within OFFSET bytes diff --git a/third_party/tcplp/lib/lbuf.c b/third_party/tcplp/lib/lbuf.c index 91dd4561bbe..ba39c5d1c7e 100644 --- a/third_party/tcplp/lib/lbuf.c +++ b/third_party/tcplp/lib/lbuf.c @@ -55,6 +55,7 @@ void lbuf_append(struct lbufhead* buffer, otLinkedBuffer* newentry) { void lbuf_extend(struct lbufhead* buffer, size_t numbytes) { buffer->tail->mLength += numbytes; + buffer->length += numbytes; } size_t lbuf_pop(struct lbufhead* buffer, size_t numbytes, uint32_t* ntraversed) { diff --git a/third_party/tcplp/lib/test/Makefile b/third_party/tcplp/lib/test/Makefile new file mode 100644 index 00000000000..5613cbd18d4 --- /dev/null +++ b/third_party/tcplp/lib/test/Makefile @@ -0,0 +1,16 @@ +CC=clang +CFLAGS=-I ../../../../include -O2 -Wall + +all: test_all + +%.o: ../%.c + clang -c $(CFLAGS) $< -o $@ + +test_all.o: test_all.c + clang -c $(CFLAGS) test_all.c -o $@ + +test_all: test_all.o cbuf.o lbuf.o bitmap.o + clang test_all.o cbuf.o lbuf.o bitmap.o -o test_all + +clean: + rm -f *.o test_all diff --git a/third_party/tcplp/lib/test/test_all.c b/third_party/tcplp/lib/test/test_all.c new file mode 100644 index 00000000000..9884259f40d --- /dev/null +++ b/third_party/tcplp/lib/test/test_all.c @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "../bitmap.h" +#include "../cbuf.h" + +uint32_t num_tests_passed = 0; +uint32_t num_tests_failed = 0; + +uint16_t otMessageRead(const otMessage *aMessage, uint16_t aOffset, void *aBuf, uint16_t aLength) { + return aLength; +} + +int otMessageWrite(otMessage *aMessage, uint16_t aOffset, const void *aBuf, uint16_t aLength) { + return aLength; +} + +void bmp_print(uint8_t* buf, size_t buflen) { + size_t i; + for (i = 0; i < buflen; i++) { + printf("%02X", buf[i]); + } + printf("\n"); +} + +void bmp_test(const char* test_name, uint8_t* buf, size_t buflen, const char* contents) { + char buf_string[(buflen << 1) + 1]; + buf_string[0] = '\0'; + for (size_t i = 0; i < buflen; i++) { + snprintf(&buf_string[i << 1], 3, "%02X", buf[i]); + } + if (strcmp(contents, buf_string) == 0) { + printf("%s: PASS\n", test_name); + num_tests_passed++; + } else { + printf("%s: FAIL: %s vs. %s\n", test_name, contents, buf_string); + num_tests_failed++; + } +} + +void test_bmp() { + size_t test_bmp_size = 8; + uint8_t buffer[test_bmp_size]; + + bmp_init(buffer, test_bmp_size); + bmp_test("bmp_init", buffer, test_bmp_size, "0000000000000000"); + + bmp_setrange(buffer, 11, 7); + bmp_test("bmp_setrange 1", buffer, test_bmp_size, "001FC00000000000"); + + bmp_setrange(buffer, 35, 3); + bmp_test("bmp_setrange 2", buffer, test_bmp_size, "001FC0001C000000"); + + bmp_setrange(buffer, 47, 4); + bmp_test("bmp_setrange 3", buffer, test_bmp_size, "001FC0001C01E000"); + + bmp_swap(buffer, 3, 36, 1); + bmp_test("bmp_swap 1", buffer, test_bmp_size, "101FC0001401E000"); + + bmp_swap(buffer, 0, 40, 24); + bmp_test("bmp_swap 2", buffer, test_bmp_size, "01E0000014101FC0"); + + bmp_swap(buffer, 2, 42, 15); + bmp_test("bmp_swap 3", buffer, test_bmp_size, "101F80001401E040"); + + bmp_swap(buffer, 13, 23, 2); + bmp_test("bmp_swap 4", buffer, test_bmp_size, "101981801401E040"); + + bmp_swap(buffer, 0, 35, 24); + bmp_test("bmp_swap 5", buffer, test_bmp_size, "A00F028002033020"); +} + +void cbuf_test(const char* test_name, struct cbufhead* chdr, const char* contents) { + char buf_string[chdr->size + 1]; + struct otLinkedBuffer first; + struct otLinkedBuffer second; + cbuf_reference(chdr, &first, &second); + + + memcpy(&buf_string[0], &first.mData[0], first.mLength); + if (first.mNext != NULL) { + assert(first.mNext == &second); + memcpy(&buf_string[first.mLength], &second.mData[0], second.mLength); + assert(second.mNext == NULL); + buf_string[first.mLength + second.mLength] = '\0'; + } else { + buf_string[first.mLength] = '\0'; + } + + if (strcmp(contents, buf_string) == 0) { + printf("%s: PASS\n", test_name); + num_tests_passed++; + } else { + printf("%s: FAIL: %s (%zu) vs. %s (%zu)\n", test_name, contents, strlen(contents), buf_string, strlen(buf_string)); + num_tests_failed++; + } +} + +void cbuf_write_string(struct cbufhead* chdr, const char* string) { + cbuf_write(chdr, string, 0, strlen(string), cbuf_copy_into_buffer); +} + +void test_cbuf() { + uint8_t buffer[65]; + uint8_t bitmap[8]; + struct cbufhead chdr; + + cbuf_init(&chdr, buffer, 64); // capacity is actually 64 + cbuf_test("cbuf_init", &chdr, ""); + + cbuf_write_string(&chdr, "abcdefghijklmnopqrstuvwxyz0123456789"); + cbuf_test("cbuf_write", &chdr, "abcdefghijklmnopqrstuvwxyz0123456789"); + + cbuf_pop(&chdr, 1); + cbuf_test("cbuf_pop", &chdr, "bcdefghijklmnopqrstuvwxyz0123456789"); + + cbuf_pop(&chdr, 5); + cbuf_test("cbuf_pop", &chdr, "ghijklmnopqrstuvwxyz0123456789"); + + cbuf_write_string(&chdr, "abcdefghijklmnopqrstuvwxyz01234567"); + cbuf_test("cbuf_write", &chdr, "ghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01234567"); + + cbuf_contiguify(&chdr, NULL); + cbuf_test("cbuf_contiguify", &chdr, "ghijklmnopqrstuvwxyz0123456789abcdefghijklmnopqrstuvwxyz01234567"); + + cbuf_pop(&chdr, 50); + cbuf_test("cbuf_pop", &chdr, "uvwxyz01234567"); + + cbuf_write_string(&chdr, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); // yz overflows and isn't written + cbuf_test("cbuf_write", &chdr, "uvwxyz01234567ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx"); + + cbuf_contiguify(&chdr, NULL); + cbuf_test("cbuf_contiguify", &chdr, "uvwxyz01234567ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx"); + + cbuf_contiguify(&chdr, NULL); // check that a second "contiguify" operation doesn't mess things up + cbuf_test("cbuf_contiguify", &chdr, "uvwxyz01234567ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx"); + + cbuf_pop(&chdr, 20); + cbuf_test("cbuf_pop", &chdr, "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx"); + + cbuf_write_string(&chdr, "yz"); + cbuf_test("cbuf_write", &chdr, "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); + + bmp_init(bitmap, 8); + bmp_test("bmp_init", bitmap, 8, "0000000000000000"); + + cbuf_reass_write(&chdr, 4, "@@@@@@@@@@@", 0, 11, bitmap, NULL, cbuf_copy_from_buffer); + cbuf_test("cbuf_reass_write (cbuf)", &chdr, "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); + bmp_test("cbuf_reass_write (bitmap)", bitmap, 8, "03FF800000000000"); + + cbuf_contiguify(&chdr, bitmap); + cbuf_test("cbuf_contiguify (cbuf)", &chdr, "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); + bmp_test("cbuf_reass_write (bitmap)", bitmap, 8, "0000000000003FF8"); + + cbuf_write_string(&chdr, "1234"); + cbuf_test("cbuf_write", &chdr, "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234"); + + cbuf_reass_merge(&chdr, 9, bitmap); + cbuf_test("cbuf_reass_merge (cbuf)", &chdr, "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234@@@@@@@@@"); + bmp_test("cbuf_reass_merge (bitmap)", bitmap, 8, "0000000000000018"); + + cbuf_reass_merge(&chdr, 2, bitmap); + cbuf_test("cbuf_reass_merge (cbuf)", &chdr, "GHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234@@@@@@@@@@@"); + bmp_test("cbuf_reass_merge (bitmap)", bitmap, 8, "0000000000000000"); + + cbuf_pop(&chdr, 61); + cbuf_test("cbuf_pop", &chdr, ""); +} + +void test_cbuf_2() { + uint8_t buffer[32]; + uint8_t bitmap[4]; + struct cbufhead chdr; + + cbuf_init(&chdr, buffer, 32); + cbuf_test("cbuf_init", &chdr, ""); + + bmp_init(bitmap, 4); + bmp_test("bmp_init", bitmap, 4, "00000000"); + + cbuf_reass_write(&chdr, 6, "abcdefghijklmnopqrstuvwxyz", 0, 26, bitmap, NULL, cbuf_copy_from_buffer); + cbuf_test("cbuf_reass_write (cbuf)", &chdr, ""); + bmp_test("cbuf_reass_write (bitmap)", bitmap, 4, "03FFFFFF"); + + cbuf_write_string(&chdr, "ASDFGH"); + cbuf_test("cbuf_write (cbuf)", &chdr, "ASDFGH"); + bmp_test("cbuf_write (bitmap)", bitmap, 4, "03FFFFFF"); + + cbuf_pop(&chdr, 6); + cbuf_test("cbuf_pop (cbuf)", &chdr, ""); + bmp_test("cbuf_pop (bitmap)", bitmap, 4, "03FFFFFF"); + + cbuf_reass_write(&chdr, 26, "!@#$^&", 0, 6, bitmap, NULL, cbuf_copy_from_buffer); + cbuf_test("cbuf_reass_write (cbuf)", &chdr, ""); + bmp_test("cbuf_reass_write (bitmap)", bitmap, 4, "FFFFFFFF"); + + printf("Count Set: %d (should be at least 32)\n", (int) cbuf_reass_count_set(&chdr, 0, bitmap, 32)); + + cbuf_reass_merge(&chdr, 32, bitmap); + cbuf_test("cbuf_reass_merge (cbuf)", &chdr, "abcdefghijklmnopqrstuvwxyz!@#$^&"); + bmp_test("cbuf_reass_merge (bitmap)", bitmap, 4, "00000000"); +} + +int main(int argc, char** argv) { + test_bmp(); + test_cbuf(); + test_cbuf_2(); + + printf("%" PRIu32 " tests passed (out of %" PRIu32 ")\n", num_tests_passed, num_tests_passed + num_tests_failed); + if (num_tests_failed != 0) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 00000000000..5822c05f6b2 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,31 @@ +# +# 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. +# + +if(OT_PLATFORM STREQUAL "posix") + add_subdirectory(ot-fct) +endif() diff --git a/tools/harness-simulation/README.md b/tools/harness-simulation/README.md index 1412e9c4379..602fca53e60 100644 --- a/tools/harness-simulation/README.md +++ b/tools/harness-simulation/README.md @@ -4,46 +4,47 @@ THCI (Thread Host Controller Interface) is an implementation of the Python abstr SI (Sniffer Interface) is an implementation of the sniffer abstract class template `ISniffer`, which is used by the Thread Test Harness Software to sniff all packets sent by devices. -Both OpenThread simulation and sniffer simulation are required to run on a POSIX environment. However, Harness has to be run on Windows, which is a non-POSIX environment. So both two systems are needed, and their setup procedures in detail are listed in the following sections. Either two machines or one machine running two (sub)systems (for example, VM, WSL) is feasible. +Both OpenThread simulation and sniffer simulation are required to run on a POSIX environment. However, Harness has to be run on Windows, which is a non-POSIX environment. So two systems are needed, and their setup procedures in detail are listed in the following sections. Either two machines or one machine running two (sub)systems (for example, VM, WSL) is feasible. Platform developers should modify the THCI implementation and/or the SI implementation directly to match their platform (for example, the path of the OpenThread repository). ## POSIX Environment Setup -1. Build OpenThread to generate standalone OpenThread simulation `ot-cli-ftd`. For example, run the following command in the top directory of OpenThread. +1. Open the JSON format configuration file `tools/harness-simulation/posix/config.yml`: + + - Edit the value of `ot_path` to the absolute path where the top directory of the OpenThread repository is located. For example, change the value of `ot_path` to `/home//repo/openthread`. + - For each entry in `ot_build.ot`, update the value of `number` to be the number of OT FTD simulations needed with the corresponding version. + - For each entry in `ot_build.otbr`, update the value of `number` to be the number of OTBR simulations needed with the corresponding version. + - The numbers above can be adjusted according to the requirement of test cases. + - Edit the value of `ssh.username` to the username to be used for connecting to the remote POSIX environment. + - Edit the value of `ssh.password` to the password corresponding to the username above. + - Edit the value of `discovery_ifname` to the network interface that the Harness will connect to. + + Note that it may be time-consuming to build all versions of `ot-cli-ftd`s and OTBR Docker images especially on devices such as Raspberry Pis. + +2. Run the installation script. + ```bash - $ script/cmake-build simulation + $ tools/harness-simulation/posix/install.sh ``` - Then `ot-cli-ftd` is built in the directory `build/simulation/examples/apps/cli/`. ## Test Harness Environment Setup -1. Double click the file `harness\install.bat` on the machine which installed Harness. +1. Copy the directory `tools/harness-simulation` from the POSIX machine to the Windows machine, and then switch to that directory. -2. Check the configuration file `C:\GRL\Thread1.2\Thread_Harness\simulation\config.py` - - - Edit the value of `REMOTE_USERNAME` to the username expected to connect to on the remote POSIX environment. - - Edit the value of `REMOTE_PASSWORD` to the password corresponding to the username above. - - Edit the value of `REMOTE_OT_PATH` to the absolute path where the top directory of the OpenThread repository is located. - -3. Add the additional simulation device information in `harness\Web\data\deviceInputFields.xml` to `C:\GRL\Thread1.2\Web\data\deviceInputFields.xml`. +2. Double click the file `harness\install.bat` on Windows. ## Run Test Harness on Simulation -1. On POSIX machine, change directory to the top of OpenThread repository, and run the following commands. +1. On the POSIX machine, change directory to the top of the OpenThread repository, and run the following commands. ```bash $ cd tools/harness-simulation/posix - $ python harness_dev_discovery.py \ - --interface=eth0 \ - --ot1.1=24 \ - --sniffer=2 + $ ./launch_testbed.py -c config.yml ``` - It starts 24 OT FTD simulations and 2 sniffer simulations and can be discovered on eth0. - - The arguments can be adjusted according to the requirement of test cases. + This example starts several OT FTD simulations, OTBR simulations, and sniffer simulations and can be discovered on `eth0`. The number of each type of simulation is specified in the configuration file `config.yml`. -2. Run Test Harness. The information field of the device is encoded as `@`. Choose the proper device as the DUT accordingly. +2. Run the Test Harness. The information field of the device is encoded as `_@`. Choose the desired device as the DUT. 3. Select one or more test cases to start the test. diff --git a/tools/harness-simulation/harness/Thread_Harness/Sniffer/SimSniffer.py b/tools/harness-simulation/harness/Thread_Harness/Sniffer/SimSniffer.py index fffdc831b9a..c5c55dbed6c 100644 --- a/tools/harness-simulation/harness/Thread_Harness/Sniffer/SimSniffer.py +++ b/tools/harness-simulation/harness/Thread_Harness/Sniffer/SimSniffer.py @@ -27,31 +27,29 @@ # POSSIBILITY OF SUCH DAMAGE. # +import grpc import ipaddress +import itertools +import json import netifaces -import os -import paramiko import select import socket import struct -import subprocess +import threading import time +import win32api import winreg as wr -from ISniffer import ISniffer +from GRLLibs.UtilityModules.ModuleHelper import ModuleHelper +from GRLLibs.UtilityModules.SnifferManager import SnifferManager +from GRLLibs.UtilityModules.TopologyManager import TopologyManager +from Sniffer.ISniffer import ISniffer from THCI.OpenThread import watched -from simulation.config import ( - REMOTE_PORT, - REMOTE_USERNAME, - REMOTE_PASSWORD, - REMOTE_OT_PATH, - REMOTE_SNIFFER_OUTPUT_PREFIX, - EDITCAP_PATH, -) +from simulation.Sniffer.proto import sniffer_pb2 +from simulation.Sniffer.proto import sniffer_pb2_grpc DISCOVERY_ADDR = ('ff02::114', 12345) - -IFNAME = 'WLAN' +IFNAME = ISniffer.ethernet_interface_name SCAN_TIME = 3 @@ -63,17 +61,128 @@ WINREG_KEY = r'SYSTEM\CurrentControlSet\Control\Network\{4d36e972-e325-11ce-bfc1-08002be10318}' +# When Harness requires an RF shield box, it will pop up a message box via `ModuleHelper.UIMsgBox` +# Replace the function to add and remove an RF enclosure simulation automatically without popping up a window +def UIMsgBoxDecorator(UIMsgBox, replaceFuncs): + + @staticmethod + def UIMsgBoxWrapper(msg='Confirm ??', + title='User Input Required', + inputRequired=False, + default='', + choices=None, + timeout=None, + isPrompt=False): + func = replaceFuncs.get((msg, title)) + if func is None: + return UIMsgBox(msg, title, inputRequired, default, choices, timeout, isPrompt) + else: + return func() + + return UIMsgBoxWrapper + + +class DeviceManager: + + def __init__(self): + deviceManager = TopologyManager.m_DeviceManager + self._devices = {'DUT': deviceManager.AutoDUTDeviceObject.THCI_Object} + for device_obj in deviceManager.DeviceList.values(): + if device_obj.IsDeviceUsed: + self._devices[device_obj.DeviceTopologyInfo] = device_obj.THCI_Object + + def __getitem__(self, deviceName): + device = self._devices.get(deviceName) + if device is None: + raise KeyError('Device Name "%s" not found' % deviceName) + return device + + +class RFEnclosureManager: + + def __init__(self, deviceNames1, deviceNames2, snifferPartitionId): + self.deviceNames1 = deviceNames1 + self.deviceNames2 = deviceNames2 + self.snifferPartitionId = snifferPartitionId + + def placeRFEnclosure(self): + devices = DeviceManager() + self._partition1 = [devices[role] for role in self.deviceNames1] + self._partition2 = [devices[role] for role in self.deviceNames2] + sniffer_denied_partition = self._partition1 if self.snifferPartitionId == 2 else self._partition2 + + for device1 in self._partition1: + for device2 in self._partition2: + device1.addBlockedNodeId(device2.node_id) + device2.addBlockedNodeId(device1.node_id) + + for sniffer in SnifferManager.SnifferObjects.values(): + if sniffer.isSnifferCapturing(): + sniffer.filterNodes(device.node_id for device in sniffer_denied_partition) + + def removeRFEnclosure(self): + for device in itertools.chain(self._partition1, self._partition2): + device.clearBlockedNodeIds() + + for sniffer in SnifferManager.SnifferObjects.values(): + if sniffer.isSnifferCapturing(): + sniffer.filterNodes(()) + + self._partition1 = None + self._partition2 = None + + class SimSniffer(ISniffer): + replaced_msgbox = False @watched def __init__(self, **kwargs): self.channel = kwargs.get('channel') - self.ipaddr = kwargs.get('addressofDevice') + self.addr_port = kwargs.get('addressofDevice') self.is_active = False self._local_pcapng_location = None - self._ssh = None - self._remote_pcap_location = None - self._remote_pid = None + + if self.addr_port is not None: + # Replace `ModuleHelper.UIMsgBox` only when simulation devices exist + self._replaceMsgBox() + + self._sniffer = grpc.insecure_channel(self.addr_port) + self._stub = sniffer_pb2_grpc.SnifferStub(self._sniffer) + + # Close the sniffer only when Harness exits + win32api.SetConsoleCtrlHandler(self.__disconnect, True) + + @watched + def _replaceMsgBox(self): + # Replace the function only once + if SimSniffer.replaced_msgbox: + return + SimSniffer.replaced_msgbox = True + + test_9_2_9_leader = RFEnclosureManager(['Router_1', 'Router_2'], ['DUT', 'Commissioner'], 1) + test_9_2_9_router = RFEnclosureManager(['DUT', 'Router_2'], ['Leader', 'Commissioner'], 1) + test_9_2_10_router = RFEnclosureManager(['Leader', 'Commissioner'], ['DUT', 'MED_1', 'SED_1'], 2) + + # Alter the behavior of `ModuleHelper.UIMsgBox` only when it comes to the following test cases: + # - Leader 9.2.9 + # - Router 9.2.9 + # - Router 9.2.10 + ModuleHelper.UIMsgBox = UIMsgBoxDecorator( + ModuleHelper.UIMsgBox, + replaceFuncs={ + ("Place [Router1, Router2 and Sniffer]
or
[DUT and Commissioner]
in an RF enclosure ", "Shield Devices"): + test_9_2_9_leader.placeRFEnclosure, + ("Remove [Router1, Router2 and Sniffer]
or
[DUT and Commissioner]
from RF enclosure ", "Unshield Devices"): + test_9_2_9_leader.removeRFEnclosure, + ("Place [DUT,Router2 and sniffer]
or
[Leader and Commissioner]
in an RF enclosure ", "Shield Devices"): + test_9_2_9_router.placeRFEnclosure, + ("Remove [DUT, Router2 and sniffer]
or
[Leader and Commissioner]
from RF enclosure ", "Unshield Devices"): + test_9_2_9_router.removeRFEnclosure, + ("Place the
[Leader and Commissioner] devices
in an RF enclosure ", "Shield Devices"): + test_9_2_10_router.placeRFEnclosure, + ("Remove
[Leader and Commissioner]
from RF enclosure ", "Unshield Devices"): + test_9_2_10_router.removeRFEnclosure, + }) def __repr__(self): return '%r' % self.__dict__ @@ -130,13 +239,14 @@ def discoverSniffer(self): start = time.time() while time.time() - start < SCAN_TIME: if select.select([sock], [], [], 1)[0]: - addr, _ = sock.recvfrom(1024) - devs.add(addr) + data, _ = sock.recvfrom(1024) + data = json.loads(data) + devs.add((data['add'], data['por'])) else: # Re-send the request, due to unreliability of UDP especially on WLAN sock.sendto(('Sniffer').encode(), DISCOVERY_ADDR) - devs = [SimSniffer(addressofDevice=addr, channel=None) for addr in devs] + devs = [SimSniffer(addressofDevice=self._encode_address_port(addr, port), channel=None) for addr, port in devs] self.log('List of SimSniffers: %r', devs) return devs @@ -145,53 +255,52 @@ def discoverSniffer(self): def startSniffer(self, channelToCapture, captureFileLocation, includeEthernet=False): self.channel = channelToCapture self._local_pcapng_location = captureFileLocation - self._remote_pcap_location = os.path.join(REMOTE_SNIFFER_OUTPUT_PREFIX, self.ipaddr.split('@')[0] + '.pcap') - self._ssh = paramiko.SSHClient() - self._ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - remote_ip = self.ipaddr.split('@')[1] - self._ssh.connect(remote_ip, port=REMOTE_PORT, username=REMOTE_USERNAME, password=REMOTE_PASSWORD) + response = self._stub.Start(sniffer_pb2.StartRequest(channel=self.channel, includeEthernet=includeEthernet)) + if response.status != sniffer_pb2.OK: + raise RuntimeError('startSniffer error: %s' % sniffer_pb2.Status.Name(response.status)) - _, stdout, _ = self._ssh.exec_command( - 'echo $$ && exec python3 %s -o %s -c %d' % - (os.path.join(REMOTE_OT_PATH, 'tools/harness-simulation/posix/sniffer_sim/sniffer.py'), - self._remote_pcap_location, self.channel)) - self._remote_pid = int(stdout.readline()) - - self.log('local pcapng location = %s', self._local_pcapng_location) - self.log('remote pcap location = %s', self._remote_pcap_location) - self.log('remote pid = %d', self._remote_pid) + self._thread = threading.Thread(target=self._file_sync_main_loop) + self._thread.setDaemon(True) + self._thread.start() self.is_active = True + @watched + def _file_sync_main_loop(self): + with open(self._local_pcapng_location, 'wb') as f: + for response in self._stub.TransferPcapng(sniffer_pb2.TransferPcapngRequest()): + f.write(response.content) + f.flush() + @watched def stopSniffer(self): if not self.is_active: return - self.is_active = False - assert self._ssh is not None - self._ssh.exec_command('kill -s TERM %d' % self._remote_pid) - # Wait to make sure the file is closed - time.sleep(3) + response = self._stub.Stop(sniffer_pb2.StopRequest()) + if response.status != sniffer_pb2.OK: + raise RuntimeError('stopSniffer error: %s' % sniffer_pb2.Status.Name(response.status)) - # Truncate suffix from .pcapng to .pcap - local_pcap_location = self._local_pcapng_location[:-2] + self._thread.join() + + self.is_active = False - with self._ssh.open_sftp() as sftp: - sftp.get(self._remote_pcap_location, local_pcap_location) + @watched + def filterNodes(self, nodeids): + if not self.is_active: + return - self._ssh.close() + request = sniffer_pb2.FilterNodesRequest() + request.nodeids.extend(nodeids) - cmd = [EDITCAP_PATH, '-F', 'pcapng', local_pcap_location, self._local_pcapng_location] - self.log('running editcap: %r', cmd) - subprocess.Popen(cmd).wait() - self.log('editcap done') + response = self._stub.FilterNodes(request) + if response.status != sniffer_pb2.OK: + raise RuntimeError('filterNodes error: %s' % sniffer_pb2.Status.Name(response.status)) - self._local_pcapng_location = None - self._ssh = None - self._remote_pcap_location = None - self._remote_pid = None + def __disconnect(self, dwCtrlType): + if self._sniffer is not None: + self._sniffer.close() @watched def setChannel(self, channelToCapture): @@ -211,7 +320,7 @@ def isSnifferCapturing(self): @watched def getSnifferAddress(self): - return self.ipaddr + return self.addr_port @watched def globalReset(self): diff --git a/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_BR_Sim.py b/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_BR_Sim.py new file mode 100644 index 00000000000..17c8698b7a3 --- /dev/null +++ b/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_BR_Sim.py @@ -0,0 +1,147 @@ +#!/usr/bin/env python +# +# 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. +# +""" +>> Thread Host Controller Interface +>> Device : OpenThread_BR_Sim THCI +>> Class : OpenThread_BR_Sim +""" + +import ipaddress +import logging +import paramiko +import pipes +import sys +import time + +from THCI.IThci import IThci +from THCI.OpenThread import watched +from THCI.OpenThread_BR import OpenThread_BR +from simulation.config import load_config + +logging.getLogger('paramiko').setLevel(logging.WARNING) + +config = load_config() + + +class SSHHandle(object): + # Unit: second + KEEPALIVE_INTERVAL = 30 + + def __init__(self, ip, port, username, password, docker_name): + self.ip = ip + self.port = int(port) + self.username = username + self.password = password + self.docker_name = docker_name + self.__handle = None + + self.__connect() + + def __connect(self): + self.close() + + self.__handle = paramiko.SSHClient() + self.__handle.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + try: + self.__handle.connect(self.ip, port=self.port, username=self.username, password=self.password) + except paramiko.AuthenticationException: + if not self.password: + self.__handle.get_transport().auth_none(self.username) + else: + raise Exception('Password error') + + # Avoid SSH disconnection after idle for a long time + self.__handle.get_transport().set_keepalive(self.KEEPALIVE_INTERVAL) + + def close(self): + if self.__handle is not None: + self.__handle.close() + self.__handle = None + + def bash(self, cmd, timeout): + # It is necessary to quote the command when there is stdin/stdout redirection + cmd = pipes.quote(cmd) + + retry = 3 + for i in range(retry): + try: + stdin, stdout, stderr = self.__handle.exec_command('docker exec %s bash -c %s' % + (self.docker_name, cmd), + timeout=timeout) + stdout._set_mode('rb') + + sys.stderr.write(stderr.read()) + output = [r.rstrip() for r in stdout.readlines()] + return output + + except paramiko.SSHException: + if i < retry - 1: + print('SSH connection is lost, try reconnect after 1 second.') + time.sleep(1) + self.__connect() + else: + raise ConnectionError('SSH connection is lost') + + +class OpenThread_BR_Sim(OpenThread_BR): + + def _getHandle(self): + self.log('SSH connecting ...') + return SSHHandle(self.ssh_ip, self.telnetPort, self.telnetUsername, self.telnetPassword, self.docker_name) + + @watched + def _parseConnectionParams(self, params): + discovery_add = params.get('SerialPort') + if '@' not in discovery_add: + raise ValueError('%r in the field `add` is invalid' % discovery_add) + + self.docker_name, self.ssh_ip = discovery_add.split('@') + self.tag, self.node_id = self.docker_name.split('_') + self.node_id = int(self.node_id) + # Let it crash if it is an invalid IP address + ipaddress.ip_address(self.ssh_ip) + + self.connectType = 'ip' + self.telnetIp = self.port = discovery_add + + global config + ssh = config['ssh'] + self.telnetPort = ssh['port'] + self.telnetUsername = ssh['username'] + self.telnetPassword = ssh['password'] + + self.extraParams = { + 'cmd-start-otbr-agent': 'service otbr-agent start', + 'cmd-stop-otbr-agent': 'service otbr-agent stop', + 'cmd-restart-otbr-agent': 'service otbr-agent restart', + 'cmd-restart-radvd': 'service radvd stop; service radvd start', + } + + +assert issubclass(OpenThread_BR_Sim, IThci) diff --git a/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_Sim.py b/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_Sim.py index e0db9bd8490..18c622ec067 100644 --- a/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_Sim.py +++ b/tools/harness-simulation/harness/Thread_Harness/THCI/OpenThread_Sim.py @@ -33,32 +33,41 @@ """ import ipaddress -import os import paramiko import socket import time +import win32api -from IThci import IThci -from OpenThread import OpenThreadTHCI, watched -from simulation.config import REMOTE_OT_PATH +from simulation.config import load_config +from THCI.IThci import IThci +from THCI.OpenThread import OpenThreadTHCI, watched + +config = load_config() +ot_subpath = {item['tag']: item['subpath'] for item in config['ot_build']['ot']} class SSHHandle(object): + KEEPALIVE_INTERVAL = 30 def __init__(self, ip, port, username, password, device, node_id): - ipaddress.ip_address(ip) self.ip = ip self.port = int(port) self.username = username self.password = password + self.node_id = node_id self.__handle = None self.__stdin = None self.__stdout = None - self.__connect(device, node_id) + + self.__connect(device) + + # Close the SSH connection only when Harness exits + win32api.SetConsoleCtrlHandler(self.__disconnect, True) @watched - def __connect(self, device, node_id): - self.close() + def __connect(self, device): + if self.__handle is not None: + return self.__handle = paramiko.SSHClient() self.__handle.set_missing_host_key_policy(paramiko.AutoAddPolicy()) @@ -71,58 +80,66 @@ def __connect(self, device, node_id): else: raise Exception('Password error') - self.__stdin, self.__stdout, _ = self.__handle.exec_command(device + ' ' + str(node_id), get_pty=True) + # Avoid SSH connection lost after inactivity for a while + self.__handle.get_transport().set_keepalive(self.KEEPALIVE_INTERVAL) + + self.__stdin, self.__stdout, _ = self.__handle.exec_command(device + ' ' + str(self.node_id)) + + # Receive the output in non-blocking mode self.__stdout.channel.setblocking(0) - # Wait some time for initiation - time.sleep(0.1) + # Some commands such as `udp send -x ` send binary data + # The UDP packet recevier will output the data in binary to stdout + self.__stdout._set_mode('rb') - @watched - def close(self): + def __disconnect(self, dwCtrlType): if self.__handle is None: return - self.__stdin.write('exit\n') - # Wait some time for termination - time.sleep(0.1) + + # Exit ot-cli-ftd and close the SSH connection + self.send('exit\n') + self.__stdin.close() + self.__stdout.close() self.__handle.close() - self.__stdin = None - self.__stdout = None - self.__handle = None + + def close(self): + # Do nothing, because disconnecting and then connecting will automatically factory reset all states + # compared to real devices, which is not the intended behavior + pass def send(self, cmd): self.__stdin.write(cmd) + self.__stdin.flush() def recv(self): - try: - return self.__stdout.readline().rstrip() - except socket.timeout: - return '' + outputs = [] + while True: + try: + outputs.append(self.__stdout.read(1)) + except socket.timeout: + break + return ''.join(outputs) def log(self, fmt, *args): try: msg = fmt % args - print('%s - %s - %s' % (self.port, time.strftime('%b %d %H:%M:%S'), msg)) + print('%d@%s - %s - %s' % (self.node_id, self.ip, time.strftime('%b %d %H:%M:%S'), msg)) except Exception: pass class OpenThread_Sim(OpenThreadTHCI, IThci): - DEFAULT_COMMAND_TIMEOUT = 20 - __handle = None - device = os.path.join(REMOTE_OT_PATH, 'build/simulation/examples/apps/cli/ot-cli-ftd') - @watched def _connect(self): + self.__lines = [] + # Only actually connect once. if self.__handle is None: - assert self.connectType == 'ip' - assert '@' in self.telnetIp self.log('SSH connecting ...') - node_id, ssh_ip = self.telnetIp.split('@') - self.__handle = SSHHandle(ssh_ip, self.telnetPort, self.telnetUsername, self.telnetPassword, self.device, - node_id) + self.__handle = SSHHandle(self.ssh_ip, self.telnetPort, self.telnetUsername, self.telnetPassword, + self.device, self.node_id) self.log('connected to %s successfully', self.telnetIp) @@ -130,9 +147,45 @@ def _connect(self): def _disconnect(self): pass + @watched + def _parseConnectionParams(self, params): + discovery_add = params.get('SerialPort') + if '@' not in discovery_add: + raise ValueError('%r in the field `add` is invalid' % discovery_add) + + prefix, self.ssh_ip = discovery_add.split('@') + self.tag, self.node_id = prefix.split('_') + self.node_id = int(self.node_id) + # Let it crash if it is an invalid IP address + ipaddress.ip_address(self.ssh_ip) + + # Do not use `os.path.join` as it uses backslash as the separator on Windows + global config + self.device = '/'.join([config['ot_path'], ot_subpath[self.tag], 'examples/apps/cli/ot-cli-ftd']) + + self.connectType = 'ip' + self.telnetIp = self.port = discovery_add + + ssh = config['ssh'] + self.telnetPort = ssh['port'] + self.telnetUsername = ssh['username'] + self.telnetPassword = ssh['password'] + def _cliReadLine(self): - tail = self.__handle.recv() - return tail if tail else None + if len(self.__lines) > 1: + return self.__lines.pop(0) + + tail = '' + if len(self.__lines) != 0: + tail = self.__lines.pop() + + tail += self.__handle.recv() + + self.__lines += self._lineSepX.split(tail) + if len(self.__lines) > 1: + return self.__lines.pop(0) + + return None def _cliWriteLine(self, line): self.__handle.send(line + '\n') diff --git a/tools/harness-simulation/harness/Thread_Harness/simulation/Sniffer/__init__.py b/tools/harness-simulation/harness/Thread_Harness/simulation/Sniffer/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tools/harness-simulation/harness/Thread_Harness/simulation/Sniffer/proto/__init__.py b/tools/harness-simulation/harness/Thread_Harness/simulation/Sniffer/proto/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tools/harness-simulation/harness/Thread_Harness/simulation/config.py b/tools/harness-simulation/harness/Thread_Harness/simulation/config.py index 44e866e5553..61bc379c7eb 100644 --- a/tools/harness-simulation/harness/Thread_Harness/simulation/config.py +++ b/tools/harness-simulation/harness/Thread_Harness/simulation/config.py @@ -27,12 +27,13 @@ # POSSIBILITY OF SUCH DAMAGE. # -REMOTE_PORT = 22 -REMOTE_USERNAME = 'pi' -REMOTE_PASSWORD = 'raspberry' +import os +import yaml -REMOTE_SNIFFER_OUTPUT_PREFIX = '/tmp/' +CONFIG_PATH = r'%s\GRL\Thread1.2\Thread_Harness\simulation\config.yml' % os.environ['systemdrive'] -REMOTE_OT_PATH = '/home/pi/work/src/openthread-pr/' -EDITCAP_PATH = r'C:\Program Files (x86)\Wireshark_Thread\editcap.exe' +def load_config(): + with open(CONFIG_PATH, 'rt') as f: + config = yaml.safe_load(f) + return config diff --git a/tools/harness-simulation/harness/Web/data/deviceInputFields.xml b/tools/harness-simulation/harness/Web/data/deviceInputFields.xml index a9e09d424a5..3191ac645fb 100644 --- a/tools/harness-simulation/harness/Web/data/deviceInputFields.xml +++ b/tools/harness-simulation/harness/Web/data/deviceInputFields.xml @@ -1,10 +1,8 @@ - COM1 - + UNSPECIFIED + + + UNSPECIFIED diff --git a/tools/harness-simulation/harness/Web/data/updateDeviceFields.py b/tools/harness-simulation/harness/Web/data/updateDeviceFields.py new file mode 100644 index 00000000000..a6830022dad --- /dev/null +++ b/tools/harness-simulation/harness/Web/data/updateDeviceFields.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# +# 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. +# + +# This script merges the device fields in the argument file to `deviceInputFields.xml` in Harness +# If a device field appears in both files, it will only keep that in the argument file + +import os +import sys +import xml.etree.ElementTree as ET + +HARNESS_XML_PATH = r'%s\GRL\Thread1.2\Web\data\deviceInputFields.xml' % os.environ['systemdrive'] + + +def main(): + tree = ET.parse(HARNESS_XML_PATH) + root = tree.getroot() + added = ET.parse(sys.argv[1]).getroot() + + added_names = set(device.attrib['name'] for device in added.iter('DEVICE')) + # If some devices already exist, remove them first, and then add them back in case of update + removed_devices = filter(lambda x: x.attrib['name'] in added_names, root.iter('DEVICE')) + + for device in removed_devices: + root.remove(device) + for device in added.iter('DEVICE'): + root.append(device) + + tree.write(HARNESS_XML_PATH) + + +if __name__ == '__main__': + main() diff --git a/tools/harness-simulation/harness/install.bat b/tools/harness-simulation/harness/install.bat index 0b4a898b18d..99be41c388c 100644 --- a/tools/harness-simulation/harness/install.bat +++ b/tools/harness-simulation/harness/install.bat @@ -1,2 +1,45 @@ -xcopy /E /Y Thread_Harness %systemdrive%\GRL\Thread1.2\Thread_Harness -copy /Y ..\..\harness-thci\OpenThread.py %systemdrive%\GRL\Thread1.2\Thread_Harness\THCI +:: 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. +:: + +set THREADDIR=%systemdrive%\GRL\Thread1.2 +xcopy /E /Y Thread_Harness %THREADDIR%\Thread_Harness +copy /Y ..\..\harness-thci\OpenThread.py %THREADDIR%\Thread_Harness\THCI +copy /Y ..\..\harness-thci\OpenThread_BR.py %THREADDIR%\Thread_Harness\THCI +copy /Y ..\..\harness-thci\OpenThread.png %THREADDIR%\Web\images +copy /Y ..\..\harness-thci\OpenThread_BR.png %THREADDIR%\Web\images +copy /Y ..\posix\config.yml %THREADDIR%\Thread_Harness\simulation +xcopy /E /Y ..\posix\sniffer_sim\proto %THREADDIR%\Thread_Harness\simulation\Sniffer\proto + +%THREADDIR%\Python27\python.exe -m pip install --upgrade pip +%THREADDIR%\Python27\python.exe -m pip install -r requirements.txt + +%THREADDIR%\Python27\python.exe Web\data\updateDeviceFields.py Web\data\deviceInputFields.xml + +set BASEDIR=%THREADDIR%\Thread_Harness +%systemdrive%\GRL\Thread1.2\Python27\python.exe -m grpc_tools.protoc -I%BASEDIR% --python_out=%BASEDIR% --grpc_python_out=%BASEDIR% simulation/Sniffer/proto/sniffer.proto + +pause diff --git a/tools/harness-simulation/harness/requirements.txt b/tools/harness-simulation/harness/requirements.txt new file mode 100644 index 00000000000..179c044ca44 --- /dev/null +++ b/tools/harness-simulation/harness/requirements.txt @@ -0,0 +1,3 @@ +grpcio==1.20.1 +grpcio-tools==1.20.1 +PyYAML==5.4.1 diff --git a/tools/harness-simulation/posix/config.yml b/tools/harness-simulation/posix/config.yml new file mode 100644 index 00000000000..11b29bca545 --- /dev/null +++ b/tools/harness-simulation/posix/config.yml @@ -0,0 +1,91 @@ +ot_path: "/home/pi/repo/openthread" +ot_build: + max_number: 64 + ot: + - tag: OT11 + version: '1.1' + number: 33 + subpath: build/ot11/simulation + cflags: + - "-DOPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS=8" + options: + - "-DOT_REFERENCE_DEVICE=ON" + - "-DOT_COMMISSIONER=ON" + - "-DOT_JOINER=ON" + - tag: OT12 + version: '1.2' + number: 10 + subpath: build/ot12/simulation + cflags: + - "-DOPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS=8" + options: + - "-DOT_REFERENCE_DEVICE=ON" + - "-DOT_DUA=ON" + - "-DOT_MLR=ON" + - "-DOT_COMMISSIONER=ON" + - "-DOT_JOINER=ON" + - "-DOT_CSL_RECEIVER=ON" + - "-DOT_LINK_METRICS_SUBJECT=ON" + - "-DOT_LINK_METRICS_INITIATOR=ON" + - tag: OT13 + version: '1.3' + number: 10 + subpath: build/ot13/simulation + cflags: + - "-DOPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS=8" + options: + - "-DOT_REFERENCE_DEVICE=ON" + - "-DOT_DUA=ON" + - "-DOT_MLR=ON" + - "-DOT_COMMISSIONER=ON" + - "-DOT_JOINER=ON" + - "-DOT_CSL_RECEIVER=ON" + - "-DOT_LINK_METRICS_SUBJECT=ON" + - "-DOT_LINK_METRICS_INITIATOR=ON" + otbr: + - tag: OTBR12 + version: '1.2' + number: 4 + docker_image: otbr-reference-device-1.2 + build_args: + - REFERENCE_DEVICE=1 + - BORDER_ROUTING=0 + - BACKBONE_ROUTER=1 + - NAT64=0 + - WEB_GUI=0 + - REST_API=0 + - OT_COMMISSIONER=1 + options: + - "-DOTBR_DUA_ROUTING=ON" + - "-DOT_DUA=ON" + - "-DOT_MLR=ON" + rcp_subpath: build/ot12/simulation + rcp_options: + - "-DOT_LINK_METRICS_SUBJECT=ON" + - tag: OTBR13 + version: '1.3' + number: 4 + docker_image: otbr-reference-device-1.3 + build_args: + - REFERENCE_DEVICE=1 + - BORDER_ROUTING=1 + - BACKBONE_ROUTER=1 + - NAT64=0 + - WEB_GUI=0 + - REST_API=0 + - EXTERNAL_COMMISSIONER=1 + options: + - "-DOTBR_DUA_ROUTING=ON" + - "-DOT_DUA=ON" + - "-DOT_MLR=ON" + rcp_subpath: build/ot13/simulation + rcp_options: + - "-DOT_LINK_METRICS_SUBJECT=ON" +ssh: + username: pi + password: raspberry + port: 22 +sniffer: + number: 2 + server_port_base: 50051 +discovery_ifname: eth0 diff --git a/tools/harness-simulation/posix/etc/Dockerfile b/tools/harness-simulation/posix/etc/Dockerfile new file mode 100644 index 00000000000..736349389a2 --- /dev/null +++ b/tools/harness-simulation/posix/etc/Dockerfile @@ -0,0 +1,119 @@ +# 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. +# + +ARG BASE_IMAGE=ubuntu:focal +FROM ${BASE_IMAGE} + +ARG INFRA_IF_NAME +ARG BORDER_ROUTING +ARG BACKBONE_ROUTER +ARG OTBR_OPTIONS +ARG EXTERNAL_COMMISSIONER +ARG DNS64 +ARG NAT64 +ARG NAT64_SERVICE +ARG REFERENCE_DEVICE +ARG REST_API +ARG WEB_GUI +ARG MDNS + +ENV INFRA_IF_NAME=${INFRA_IF_NAME:-eth0} +ENV BORDER_ROUTING=${BORDER_ROUTING:-1} +ENV BACKBONE_ROUTER=${BACKBONE_ROUTER:-1} +ENV OTBR_MDNS=${MDNS:-mDNSResponder} +ENV OTBR_OPTIONS=${OTBR_OPTIONS} +ENV EXTERNAL_COMMISSIONER=${EXTERNAL_COMMISSIONER:-1} +ENV DEBIAN_FRONTEND noninteractive +ENV PLATFORM ubuntu +ENV REFERENCE_DEVICE=${REFERENCE_DEVICE:-0} +ENV NAT64=${NAT64:-1} +ENV NAT64_SERVICE=${NAT64_SERVICE:-tayga} +ENV DNS64=${DNS64:-0} +ENV WEB_GUI=${WEB_GUI:-1} +ENV REST_API=${REST_API:-1} +ENV DOCKER 1 + +RUN env + +COPY . /app +WORKDIR /app + +# Required during build or run +ENV OTBR_DOCKER_REQS sudo python2 python3 python-is-python2 + +# Required during build, could be removed +ENV OTBR_DOCKER_DEPS git ca-certificates python3-pip wget + +# Required during run python scripts +ENV OTBR_PYTHON_REQS zeroconf + +# Required and installed during build (script/bootstrap), could be removed +ENV OTBR_BUILD_DEPS apt-utils build-essential psmisc ninja-build cmake ca-certificates \ + libreadline-dev libncurses-dev libcpputest-dev libdbus-1-dev libavahi-common-dev \ + libavahi-client-dev libboost-dev libboost-filesystem-dev libboost-system-dev \ + libnetfilter-queue-dev + +RUN apt-get update \ + && cp -r ./root/. / \ + && rm -rf ./root \ + && apt-get install --no-install-recommends -y $OTBR_DOCKER_REQS $OTBR_DOCKER_DEPS \ + && wget -P /tmp "https://bootstrap.pypa.io/pip/2.7/get-pip.py" \ + && python2 /tmp/get-pip.py \ + && pip2 install -r /tmp/requirements.txt \ + && pip3 install $OTBR_PYTHON_REQS \ + && ln -fs /usr/share/zoneinfo/UTC /etc/localtime \ + && ([ "${EXTERNAL_COMMISSIONER}" != "1" ] || ( \ + git clone https://github.com/openthread/ot-commissioner.git --recurse-submodules --shallow-submodules --depth=1 \ + && cd ot-commissioner \ + && ./script/bootstrap.sh \ + && mkdir -p build \ + && cd build \ + && cmake -GNinja -DCMAKE_INSTALL_PREFIX="/usr/local" -DOT_COMM_REFERENCE_DEVICE=ON .. \ + && ninja \ + && ninja install \ + && cd /app \ + && rm -rf ot-commissioner \ + )) \ + && ./script/bootstrap \ + && ./script/setup \ + && ([ "${DNS64}" = "0" ] || chmod 644 /etc/bind/named.conf.options) \ + && mv ./script /tmp \ + && mv ./etc /tmp \ + && find . -delete \ + && rm -rf /usr/include \ + && mv /tmp/script . \ + && mv /tmp/etc . \ + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $OTBR_DOCKER_DEPS \ + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $OTBR_BUILD_DEPS \ + && rm -rf /var/lib/apt/lists/* \ + && rm -rf /tmp/* \ + && sed -i "s/\/root/\/home\/pi/g" /etc/passwd + # The command above changes root home directory to /home/pi + +ENTRYPOINT ["/app/etc/docker/docker_entrypoint.sh"] + +EXPOSE 80 diff --git a/tools/harness-simulation/posix/etc/commissionerd b/tools/harness-simulation/posix/etc/commissionerd new file mode 100755 index 00000000000..6a264e163b8 --- /dev/null +++ b/tools/harness-simulation/posix/etc/commissionerd @@ -0,0 +1,100 @@ +#!/bin/sh +# +# 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. +# +### BEGIN INIT INFO +# Provides: commissionerd +# Required-Start: +# Required-Stop: +# Should-Start: +# Should-Stop: +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: OT-commissioner daemon +# Description: OT-commissioner daemon +### END INIT INFO + +set -e + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +DESC="OT-commissioner daemon" +NAME=commissionerd +DAEMON=/usr/bin/python2 +PIDFILE=/var/run/commissionerd.pid + +# shellcheck source=/dev/null +. /lib/lsb/init-functions +# shellcheck source=/dev/null +. /lib/init/vars.sh + +start_commissionerd() +{ + if [ -e $PIDFILE ]; then + if $0 status >/dev/null; then + log_success_msg "$DESC already started; not starting." + return + else + log_success_msg "Removing stale PID file $PIDFILE." + rm -f $PIDFILE + fi + fi + + log_daemon_msg "Starting $DESC" "$NAME" + start-stop-daemon --start --quiet \ + --pidfile $PIDFILE --make-pidfile \ + -b --exec $DAEMON -- \ + -u /usr/local/bin/commissionerd.py -c /usr/local/bin/commissioner-cli + log_end_msg $? +} + +stop_commissionerd() +{ + log_daemon_msg "Stopping $DESC" "$NAME" + start-stop-daemon --stop --retry 5 --quiet --oknodo \ + --pidfile $PIDFILE --remove-pidfile + log_end_msg $? +} + +case "$1" in + start) + start_commissionerd + ;; + restart | reload | force-reload) + stop_commissionerd + start_commissionerd + ;; + stop | force-stop) + stop_commissionerd + ;; + status) + status_of_proc -p $PIDFILE $DAEMON $NAME && exit 0 || exit $? + ;; + *) + log_action_msg "Usage: /etc/init.d/$NAME {start | stop | status | restart | reload | force-reload}" + exit 2 + ;; +esac diff --git a/tools/harness-simulation/posix/etc/requirements.txt b/tools/harness-simulation/posix/etc/requirements.txt new file mode 100644 index 00000000000..5804683d2ac --- /dev/null +++ b/tools/harness-simulation/posix/etc/requirements.txt @@ -0,0 +1,3 @@ +pexpect==4.7.0 +ptyprocess==0.6.0 +pyserial==3.4 diff --git a/tools/harness-simulation/posix/etc/server.patch b/tools/harness-simulation/posix/etc/server.patch new file mode 100644 index 00000000000..0f29c95736a --- /dev/null +++ b/tools/harness-simulation/posix/etc/server.patch @@ -0,0 +1,18 @@ +--- script/server 2022-08-15 11:41:53.915673348 +0800 ++++ script/server2 2022-08-15 11:43:12.387100651 +0800 +@@ -50,6 +50,7 @@ + systemctl is-active avahi-daemon || sudo systemctl start avahi-daemon || die 'Failed to start avahi!' + without WEB_GUI || systemctl is-active otbr-web || sudo systemctl start otbr-web || die 'Failed to start otbr-web!' + systemctl is-active otbr-agent || sudo systemctl start otbr-agent || die 'Failed to start otbr-agent!' ++ systemctl is-active commissionerd || sudo systemctl start commissionerd || die 'Failed to start commissionerd!' + elif have service; then + sudo service rsyslog status || sudo service rsyslog start || die 'Failed to start rsyslog!' + sudo service dbus status || sudo service dbus start || die 'Failed to start dbus!' +@@ -58,6 +59,7 @@ + sudo service avahi-daemon status || sudo service avahi-daemon start || die 'Failed to start avahi!' + sudo service otbr-agent status || sudo service otbr-agent start || die 'Failed to start otbr-agent!' + without WEB_GUI || sudo service otbr-web status || sudo service otbr-web start || die 'Failed to start otbr-web!' ++ sudo service commissionerd status || sudo service commissionerd start || die 'Failed to start commissionerd!' + else + die 'Unable to find service manager. Try script/console to start in console mode!' + fi diff --git a/tools/harness-simulation/posix/harness_dev_discovery.py b/tools/harness-simulation/posix/harness_dev_discovery.py deleted file mode 100644 index 1587c998e67..00000000000 --- a/tools/harness-simulation/posix/harness_dev_discovery.py +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/env python3 -# -# 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. -# - -import argparse -import ctypes -import ctypes.util -import json -import logging -import os -import socket -import struct - -GROUP = 'ff02::114' -PORT = 12345 -MAX_OT11_NUM = 33 -MAX_SNIFFER_NUM = 4 - - -def if_nametoindex(ifname: str) -> int: - libc = ctypes.CDLL(ctypes.util.find_library('c')) - ret = libc.if_nametoindex(ifname.encode('ascii')) - if not ret: - raise RuntimeError('Invalid interface name') - return ret - - -def get_ipaddr(ifname: str) -> str: - for line in os.popen(f'ip addr list dev {ifname} | grep inet | grep global'): - addr = line.strip().split()[1] - return addr.split('/')[0] - raise RuntimeError(f'No IP address on dev {ifname}') - - -def init_socket(ifname: str, group: str, port: int) -> socket.socket: - # Look up multicast group address in name server and find out IP version - addrinfo = socket.getaddrinfo(group, None)[0] - assert addrinfo[0] == socket.AF_INET6 - - # Create a socket - s = socket.socket(addrinfo[0], socket.SOCK_DGRAM) - s.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, (ifname + '\0').encode('ascii')) - - # Bind it to the port - s.bind((group, port)) - - group_bin = socket.inet_pton(addrinfo[0], addrinfo[4][0]) - # Join group - interface_index = if_nametoindex(ifname) - mreq = group_bin + struct.pack('@I', interface_index) - s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) - - return s - - -def advertise_ftd(s: socket.socket, dst, ven: str, ver: str, add: str, por: int, number: int): - # Node ID of ot-cli-ftd is 1-indexed - for i in range(1, number + 1): - info = { - 'ven': ven, - 'mod': f'{ven}_{i}', - 'ver': ver, - 'add': f'{i}@{add}', - 'por': por, - } - logging.info('Advertise: %r', info) - s.sendto(json.dumps(info).encode('utf-8'), dst) - - -def advertise_sniffer(s: socket.socket, dst, add: str, number: int): - for i in range(number): - info = 'Sniffer_%d@%s' % (i, add) - logging.info('Advertise: %r', info) - s.sendto(info.encode('utf-8'), dst) - - -def main(): - logging.basicConfig(level=logging.INFO) - - # Parse arguments - parser = argparse.ArgumentParser() - - # Determine the interface - parser.add_argument('-i', - '--interface', - dest='ifname', - type=str, - required=True, - help='the interface used for discovery') - - # Determine the number of OpenThread 1.1 FTD simulations to be "detected" and then initiated - parser.add_argument('--ot1.1', - dest='ot11_num', - type=int, - required=False, - default=0, - help=f'the number of OpenThread FTD simulations, no more than {MAX_OT11_NUM}') - - # Determine the number of sniffer simulations to be initiated and then detected - parser.add_argument('-s', - '--sniffer', - dest='sniffer_num', - type=int, - required=False, - default=0, - help=f'the number of sniffer simulations, no more than {MAX_SNIFFER_NUM}') - - args = parser.parse_args() - - # Check validation of arguments - if not 0 <= args.ot11_num <= MAX_OT11_NUM: - raise ValueError(f'The number of FTDs should be between 0 and {MAX_OT11_NUM}') - - if not 0 <= args.sniffer_num <= MAX_SNIFFER_NUM: - raise ValueError(f'The number of FTDs should be between 0 and {MAX_SNIFFER_NUM}') - - if args.ot11_num == args.sniffer_num == 0: - raise ValueError('At least one device is required') - - # Get the local IP address on the specified interface - addr = get_ipaddr(args.ifname) - - s = init_socket(args.ifname, GROUP, PORT) - - logging.info('Advertising on interface %s group %s ...', args.ifname, GROUP) - - # Loop, printing any data we receive - while True: - data, src = s.recvfrom(64) - - if data == b'BBR': - logging.info('Received OpenThread simulation query, advertising') - advertise_ftd(s, src, ven='OpenThread_Sim', ver='4', add=addr, por=22, number=args.ot11_num) - - elif data == b'Sniffer': - logging.info('Received sniffer simulation query, advertising') - advertise_sniffer(s, src, add=addr, number=args.sniffer_num) - - else: - logging.warning('Received %r, but ignored', data) - - -if __name__ == '__main__': - main() diff --git a/tools/harness-simulation/posix/install.sh b/tools/harness-simulation/posix/install.sh new file mode 100755 index 00000000000..aedba39166d --- /dev/null +++ b/tools/harness-simulation/posix/install.sh @@ -0,0 +1,149 @@ +#!/bin/bash +# +# 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. +# + +set -euxo pipefail + +POSIX_DIR="$(cd "$(dirname "$0")" && pwd)" +OT_DIR="${POSIX_DIR}/../../.." +ETC_DIR="${POSIX_DIR}/etc" +SNIFFER_DIR="${POSIX_DIR}/sniffer_sim" + +PACKAGES=( + "docker.io" + "git" + "jq" + "socat" + "tshark" +) + +sudo apt install -y "${PACKAGES[@]}" + +pip3 install -r "${POSIX_DIR}/requirements.txt" +python3 -m grpc_tools.protoc -I"${SNIFFER_DIR}" --python_out="${SNIFFER_DIR}" --grpc_python_out="${SNIFFER_DIR}" proto/sniffer.proto + +CONFIG_NAME=${1:-"${POSIX_DIR}/config.yml"} +# convert YAML to JSON +CONFIG=$(python3 -c 'import json, sys, yaml; print(json.dumps(yaml.safe_load(open(sys.argv[1]))))' "$CONFIG_NAME") + +MAX_NETWORK_SIZE=$(jq -r '.ot_build.max_number' <<<"$CONFIG") + +build_ot() +{ + # SC2155: Declare and assign separately to avoid masking return values + local target build_dir cflags version options + target="ot-cli-ftd" + build_dir=$(jq -r '.subpath' <<<"$1") + cflags=$(jq -r '.cflags | join(" ")' <<<"$1") + version=$(jq -r '.version' <<<"$1") + options=$(jq -r '.options | join(" ")' <<<"$1") + # Intended splitting of options + read -ra options <<<"$options" + + ( + cd "$OT_DIR" + + OT_CMAKE_NINJA_TARGET="$target" \ + OT_CMAKE_BUILD_DIR="$build_dir" \ + CFLAGS="$cflags" \ + CXXFLAGS="$cflags" \ + script/cmake-build \ + simulation \ + "${options[@]}" \ + -DOT_THREAD_VERSION="$version" \ + -DOT_SIMULATION_MAX_NETWORK_SIZE="$MAX_NETWORK_SIZE" + ) +} + +build_otbr() +{ + # SC2155: Declare and assign separately to avoid masking return values + local target build_dir version rcp_options + target="ot-rcp" + build_dir=$(jq -r '.rcp_subpath' <<<"$1") + version=$(jq -r '.version' <<<"$1") + rcp_options=$(jq -r '.rcp_options | join(" ")' <<<"$1") + # Intended splitting of rcp_options + read -ra rcp_options <<<"$rcp_options" + + ( + cd "$OT_DIR" + + OT_CMAKE_NINJA_TARGET="$target" \ + OT_CMAKE_BUILD_DIR="$build_dir" \ + script/cmake-build \ + simulation \ + "${rcp_options[@]}" \ + -DOT_THREAD_VERSION="$version" \ + -DOT_SIMULATION_MAX_NETWORK_SIZE="$MAX_NETWORK_SIZE" + ) + + # SC2155: Declare and assign separately to avoid masking return values + local otbr_docker_image build_args options + otbr_docker_image=$(jq -r '.docker_image' <<<"$1") + build_args=$(jq -r '.build_args | map("--build-arg " + .) | join(" ")' <<<"$1") + # Intended splitting of build_args + read -ra build_args <<<"$build_args" + options=$(jq -r '.options | join(" ")' <<<"$1") + + local otbr_options=( + "$options" + "-DOT_THREAD_VERSION=$version" + "-DOT_SIMULATION_MAX_NETWORK_SIZE=$MAX_NETWORK_SIZE" + ) + + docker build . \ + -t "${otbr_docker_image}" \ + -f "${ETC_DIR}/Dockerfile" \ + "${build_args[@]}" \ + --build-arg OTBR_OPTIONS="${otbr_options[*]}" +} + +for item in $(jq -c '.ot_build.ot | .[]' <<<"$CONFIG"); do + build_ot "$item" +done + +git clone https://github.com/openthread/ot-br-posix.git --recurse-submodules --shallow-submodules --depth=1 +( + cd ot-br-posix + # Use system V `service` command instead + mkdir -p root/etc/init.d + cp "${ETC_DIR}/commissionerd" root/etc/init.d/commissionerd + sudo chown root:root root/etc/init.d/commissionerd + sudo chmod +x root/etc/init.d/commissionerd + + cp "${ETC_DIR}/server.patch" script/server.patch + patch script/server script/server.patch + mkdir -p root/tmp + cp "${ETC_DIR}/requirements.txt" root/tmp/requirements.txt + + for item in $(jq -c '.ot_build.otbr | .[]' <<<"$CONFIG"); do + build_otbr "$item" + done +) +rm -rf ot-br-posix diff --git a/tools/harness-simulation/posix/launch_testbed.py b/tools/harness-simulation/posix/launch_testbed.py new file mode 100755 index 00000000000..5ca2537eaf5 --- /dev/null +++ b/tools/harness-simulation/posix/launch_testbed.py @@ -0,0 +1,246 @@ +#!/usr/bin/env python3 +# +# 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. +# + +import argparse +import ctypes +import ctypes.util +import ipaddress +import json +import logging +import os +import signal +import socket +import struct +import subprocess +import sys +from typing import Iterable +import yaml + +from otbr_sim import otbr_docker + +GROUP = 'ff02::114' +PORT = 12345 + + +def if_nametoindex(ifname: str) -> int: + libc = ctypes.CDLL(ctypes.util.find_library('c')) + ret = libc.if_nametoindex(ifname.encode('ascii')) + if not ret: + raise RuntimeError('Invalid interface name') + return ret + + +def get_ipaddr(ifname: str) -> str: + for line in os.popen(f'ip addr list dev {ifname} | grep inet | grep global'): + addr = line.strip().split()[1] + return addr.split('/')[0] + raise RuntimeError(f'No IP address on dev {ifname}') + + +def init_socket(ifname: str, group: str, port: int) -> socket.socket: + # Look up multicast group address in name server and find out IP version + addrinfo = socket.getaddrinfo(group, None)[0] + assert addrinfo[0] == socket.AF_INET6 + + # Create a socket + s = socket.socket(addrinfo[0], socket.SOCK_DGRAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_BINDTODEVICE, (ifname + '\0').encode('ascii')) + + # Bind it to the port + s.bind((group, port)) + + group_bin = socket.inet_pton(addrinfo[0], addrinfo[4][0]) + # Join group + interface_index = if_nametoindex(ifname) + mreq = group_bin + struct.pack('@I', interface_index) + s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) + + return s + + +def _advertise(s: socket.socket, dst, info): + logging.info('Advertise: %r', info) + s.sendto(json.dumps(info).encode('utf-8'), dst) + + +def advertise_devices(s: socket.socket, dst, ven: str, add: str, nodeids: Iterable[int], tag: str): + for nodeid in nodeids: + info = { + 'ven': ven, + 'mod': 'OpenThread', + 'ver': '4', + 'add': f'{tag}_{nodeid}@{add}', + 'por': 22, + } + _advertise(s, dst, info) + + +def advertise_sniffers(s: socket.socket, dst, add: str, ports: Iterable[int]): + for port in ports: + info = { + 'add': add, + 'por': port, + } + _advertise(s, dst, info) + + +def start_sniffer(addr: str, port: int, ot_path: str, max_nodes_num: int) -> subprocess.Popen: + if isinstance(ipaddress.ip_address(addr), ipaddress.IPv6Address): + server = f'[{addr}]:{port}' + else: + server = f'{addr}:{port}' + + cmd = [ + 'python3', + os.path.join(ot_path, 'tools/harness-simulation/posix/sniffer_sim/sniffer.py'), + '--grpc-server', + server, + '--max-nodes-num', + str(max_nodes_num), + ] + logging.info('Executing command: %s', ' '.join(cmd)) + return subprocess.Popen(cmd) + + +def main(): + logging.basicConfig(level=logging.INFO) + + # Parse arguments + parser = argparse.ArgumentParser() + parser.add_argument('-c', + '--config', + dest='config', + type=str, + required=True, + help='the path of the configuration JSON file') + args = parser.parse_args() + with open(args.config, 'rt') as f: + config = yaml.safe_load(f) + + ot_path = config['ot_path'] + ot_build = config['ot_build'] + max_nodes_num = ot_build['max_number'] + # No test case requires more than 2 sniffers + MAX_SNIFFER_NUM = 2 + + ot_devices = [(item['tag'], item['number']) for item in ot_build['ot']] + otbr_devices = [(item['tag'], item['number']) for item in ot_build['otbr']] + ot_nodes_num = sum(x[1] for x in ot_devices) + otbr_nodes_num = sum(x[1] for x in otbr_devices) + nodes_num = ot_nodes_num + otbr_nodes_num + sniffer_num = config['sniffer']['number'] + + # Check validation of numbers + if not all(0 <= x[1] <= max_nodes_num for x in ot_devices): + raise ValueError(f'The number of devices of each OT version should be between 0 and {max_nodes_num}') + + if not all(0 <= x[1] <= max_nodes_num for x in otbr_devices): + raise ValueError(f'The number of devices of each OTBR version should be between 0 and {max_nodes_num}') + + if not 1 <= nodes_num <= max_nodes_num: + raise ValueError(f'The number of devices should be between 1 and {max_nodes_num}') + + if not 1 <= sniffer_num <= MAX_SNIFFER_NUM: + raise ValueError(f'The number of sniffers should be between 1 and {MAX_SNIFFER_NUM}') + + # Get the local IP address on the specified interface + ifname = config['discovery_ifname'] + addr = get_ipaddr(ifname) + + # Start the sniffer + sniffer_server_port_base = config['sniffer']['server_port_base'] + sniffer_procs = [] + for i in range(sniffer_num): + sniffer_procs.append(start_sniffer(addr, i + sniffer_server_port_base, ot_path, max_nodes_num)) + + # OTBR firewall scripts create rules inside the Docker container + # Run modprobe to load the kernel modules for iptables + subprocess.run(['sudo', 'modprobe', 'ip6table_filter']) + # Start the BRs + otbr_dockers = [] + nodeid = ot_nodes_num + for item in ot_build['otbr']: + tag = item['tag'] + ot_rcp_path = os.path.join(ot_path, item['rcp_subpath'], 'examples/apps/ncp/ot-rcp') + docker_image = item['docker_image'] + for _ in range(item['number']): + nodeid += 1 + otbr_dockers.append( + otbr_docker.OtbrDocker(nodeid=nodeid, + ot_path=ot_path, + ot_rcp_path=ot_rcp_path, + docker_image=docker_image, + docker_name=f'{tag}_{nodeid}')) + + s = init_socket(ifname, GROUP, PORT) + + logging.info('Advertising on interface %s group %s ...', ifname, GROUP) + + # Terminate all sniffer simulation server processes and then exit + def exit_handler(signum, context): + # Return code is non-zero if any return code of the processes is non-zero + ret = 0 + for sniffer_proc in sniffer_procs: + sniffer_proc.terminate() + ret = max(ret, sniffer_proc.wait()) + + for otbr in otbr_dockers: + otbr.close() + + sys.exit(ret) + + signal.signal(signal.SIGINT, exit_handler) + signal.signal(signal.SIGTERM, exit_handler) + + # Loop, printing any data we receive + while True: + data, src = s.recvfrom(64) + + if data == b'BBR': + logging.info('Received OpenThread simulation query, advertising') + + nodeid = 1 + for ven, devices in [('OpenThread_Sim', ot_devices), ('OpenThread_BR_Sim', otbr_devices)]: + for tag, number in devices: + advertise_devices(s, src, ven=ven, add=addr, nodeids=range(nodeid, nodeid + number), tag=tag) + nodeid += number + + elif data == b'Sniffer': + logging.info('Received sniffer simulation query, advertising') + advertise_sniffers(s, + src, + add=addr, + ports=range(sniffer_server_port_base, sniffer_server_port_base + sniffer_num)) + + else: + logging.warning('Received %r, but ignored', data) + + +if __name__ == '__main__': + main() diff --git a/tools/harness-simulation/posix/otbr_sim/otbr_docker.py b/tools/harness-simulation/posix/otbr_sim/otbr_docker.py new file mode 100644 index 00000000000..cc7f73b47fc --- /dev/null +++ b/tools/harness-simulation/posix/otbr_sim/otbr_docker.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +# +# 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. +# + +import logging +import os +import re +import subprocess +import time + + +class OtbrDocker: + device_pattern = re.compile('(?<=PTY is )/dev/.+$') + + def __init__(self, nodeid: int, ot_path: str, ot_rcp_path: str, docker_image: str, docker_name: str): + self.nodeid = nodeid + self.ot_path = ot_path + self.ot_rcp_path = ot_rcp_path + self.docker_image = docker_image + self.docker_name = docker_name + + self.logger = logging.getLogger('otbr_docker.OtbrDocker') + self.logger.setLevel(logging.INFO) + + self._socat_proc = None + self._ot_rcp_proc = None + + self._rcp_device_pty = None + self._rcp_device = None + + self._launch() + + def __repr__(self) -> str: + return f'OTBR<{self.nodeid}>' + + def _launch(self): + self.logger.info('Launching %r ...', self) + self._launch_socat() + self._launch_ot_rcp() + self._launch_docker() + self.logger.info('Launched %r successfully', self) + + def close(self): + self.logger.info('Shutting down %r ...', self) + self._shutdown_docker() + self._shutdown_ot_rcp() + self._shutdown_socat() + self.logger.info('Shut down %r successfully', self) + + def _launch_socat(self): + self._socat_proc = subprocess.Popen(['socat', '-d', '-d', 'pty,raw,echo=0', 'pty,raw,echo=0'], + stderr=subprocess.PIPE, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL) + + line = self._socat_proc.stderr.readline().decode('ascii').strip() + self._rcp_device_pty = self.device_pattern.findall(line)[0] + line = self._socat_proc.stderr.readline().decode('ascii').strip() + self._rcp_device = self.device_pattern.findall(line)[0] + self.logger.info(f"socat running: device PTY: {self._rcp_device_pty}, device: {self._rcp_device}") + + def _shutdown_socat(self): + if self._socat_proc is None: + return + + self._socat_proc.stderr.close() + self._socat_proc.terminate() + self._socat_proc.wait() + self._socat_proc = None + + self._rcp_device_pty = None + self._rcp_device = None + + def _launch_ot_rcp(self): + self._ot_rcp_proc = subprocess.Popen( + f'{self.ot_rcp_path} {self.nodeid} > {self._rcp_device_pty} < {self._rcp_device_pty}', + shell=True, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + try: + self._ot_rcp_proc.wait(1) + except subprocess.TimeoutExpired: + # We expect ot-rcp not to quit in 1 second. + pass + else: + raise Exception(f"ot-rcp {self.nodeid} exited unexpectedly!") + + def _shutdown_ot_rcp(self): + if self._ot_rcp_proc is None: + return + + self._ot_rcp_proc.terminate() + self._ot_rcp_proc.wait() + self._ot_rcp_proc = None + + def _launch_docker(self): + local_cmd_path = f'/tmp/{self.docker_name}' + os.makedirs(local_cmd_path, exist_ok=True) + + cmd = [ + 'docker', + 'run', + '--rm', + '--name', + self.docker_name, + '-d', + '--sysctl', + 'net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1', + '--privileged', + '-v', + f'{self._rcp_device}:/dev/ttyUSB0', + '-v', + f'{self.ot_path.rstrip("/")}:/home/pi/repo/openthread', + self.docker_image, + ] + self.logger.info('Launching docker: %s', ' '.join(cmd)) + launch_proc = subprocess.Popen(cmd, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + + launch_docker_deadline = time.time() + 60 + launch_ok = False + time.sleep(5) + + while time.time() < launch_docker_deadline: + try: + subprocess.check_call(['docker', 'exec', self.docker_name, 'ot-ctl', 'state'], + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL) + launch_ok = True + logging.info("OTBR Docker %s is ready!", self.docker_name) + break + except subprocess.CalledProcessError: + time.sleep(5) + continue + + if not launch_ok: + raise RuntimeError('Cannot start OTBR Docker %s!' % self.docker_name) + launch_proc.wait() + + def _shutdown_docker(self): + subprocess.run(['docker', 'stop', self.docker_name]) diff --git a/tools/harness-simulation/posix/requirements.txt b/tools/harness-simulation/posix/requirements.txt new file mode 100644 index 00000000000..21ed00b551f --- /dev/null +++ b/tools/harness-simulation/posix/requirements.txt @@ -0,0 +1,3 @@ +grpcio +grpcio-tools +PyYAML diff --git a/tools/harness-simulation/posix/sniffer_sim/pcap_codec.py b/tools/harness-simulation/posix/sniffer_sim/pcap_codec.py index ff25356bb55..3817c70010c 100644 --- a/tools/harness-simulation/posix/sniffer_sim/pcap_codec.py +++ b/tools/harness-simulation/posix/sniffer_sim/pcap_codec.py @@ -42,16 +42,20 @@ class PcapCodec(object): """ Utility class for .pcap formatters. """ - def __init__(self, filename, channel): + def __init__(self, channel, filename): self._dlt = DLT_IEEE802_15_4_WITHFCS - if not filename.endswith('.pcap'): - raise ValueError('Filename should end with .pcap') - self._pcap_file = open(filename, 'wb') - self._pcap_file.write(self.encode_header()) self._channel = channel - def encode_header(self): + self._pcap_writer = open(filename, 'wb') + self._write(self._encode_header()) + + def _write(self, content): + self._pcap_writer.write(content) + self._pcap_writer.flush() + + def _encode_header(self): """ Return a pcap file header. """ + return struct.pack( ' powercalibrationtable +| ChStart | ChEnd | ActualPower(0.01dBm) | RawPowerSetting | ++---------+---------+----------------------+-----------------+ +| 11 | 25 | 1900 | 112233 | +| 11 | 25 | 1000 | 223344 | +| 26 | 26 | 1500 | 334455 | +| 26 | 26 | 700 | 445566 | +Done +``` + +#### powercalibrationtable add -b \,\ -c \,\/... ... + +Add power calibration table entry. + +- channelstart: Sub-band start channel. +- channelend: Sub-band end channel. +- actualpower: The actual power in 0.01 dBm. +- rawpowersetting: The raw power setting hex string. + +```bash +> powercalibrationtable add -b 11,25 -c 1900,112233/1000,223344 -b 26,26 -c 1500,334455/700,445566 +Done +``` + +#### powercalibrationtable clear + +Clear the power calibration table. + +```bash +> powercalibrationtable clear +Done +``` + +#### regiondomaintable + +Show the region and regulatory domain mapping table. + +```bash +> regiondomaintable +FCC,AU,CA,CL,CO,IN,MX,PE,TW,US +ETSI,WW +Done +``` + +#### targetpowertable + +Show the target power table. + +```bash +> targetpowertable +| Domain | ChStart | ChEnd | TargetPower(0.01dBm) | ++----------+---------+---------+----------------------+ +| FCC | 11 | 14 | 1700 | +| FCC | 15 | 24 | 2000 | +| FCC | 25 | 26 | 1600 | +| ETSI | 11 | 26 | 1000 | +Done +``` diff --git a/tools/ot-fct/cli.cpp b/tools/ot-fct/cli.cpp new file mode 100644 index 00000000000..893e595b4a4 --- /dev/null +++ b/tools/ot-fct/cli.cpp @@ -0,0 +1,329 @@ +/* + * 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 strain 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. + */ + +#include "cli.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "power.hpp" +#include "common/code_utils.hpp" + +namespace ot { +namespace Fct { + +const struct Cli::Command Cli::sCommands[] = { + {"powercalibrationtable", &Cli::ProcessCalibrationTable}, + {"targetpowertable", &Cli::ProcessTargetPowerTable}, + {"regiondomaintable", &Cli::ProcessRegionDomainTable}, +}; + +otError Cli::GetNextTargetPower(const Power::Domain &aDomain, int &aIterator, Power::TargetPower &aTargetPower) +{ + otError error = OT_ERROR_NOT_FOUND; + char value[kMaxValueSize]; + char *domain; + char *psave; + + while (mProductConfigFile.Get(kKeyTargetPower, aIterator, value, sizeof(value)) == OT_ERROR_NONE) + { + if (((domain = strtok_r(value, kCommaDelimiter, &psave)) == nullptr) || (aDomain != domain)) + { + continue; + } + + error = aTargetPower.FromString(psave); + break; + } + + return error; +} + +otError Cli::GetNextDomain(int &aIterator, Power::Domain &aDomain) +{ + otError error = OT_ERROR_NOT_FOUND; + char value[kMaxValueSize]; + char *str; + + while (mProductConfigFile.Get(kKeyRegionDomainMapping, aIterator, value, sizeof(value)) == OT_ERROR_NONE) + { + if ((str = strtok(value, kCommaDelimiter)) == nullptr) + { + continue; + } + + error = aDomain.Set(str); + break; + } + +exit: + return error; +} + +otError Cli::ProcessTargetPowerTable(Utils::CmdLineParser::Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + int iterator = 0; + Power::Domain domain; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + printf("| Domain | ChStart | ChEnd | TargetPower(0.01dBm) |\r\n"); + printf("+----------+---------+---------+----------------------+\r\n"); + while (GetNextDomain(iterator, domain) == OT_ERROR_NONE) + { + int iter = 0; + Power::TargetPower targetPower; + + while (GetNextTargetPower(domain, iter, targetPower) == OT_ERROR_NONE) + { + printf("| %-8s | %-7d | %-7d | %-20d |\r\n", domain.AsCString(), targetPower.GetChannelStart(), + targetPower.GetChannelEnd(), targetPower.GetTargetPower()); + } + } + +exit: + return error; +} + +otError Cli::ProcessRegionDomainTable(Utils::CmdLineParser::Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + int iterator = 0; + char value[kMaxValueSize]; + char *domain; + char *psave; + + VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + while (mProductConfigFile.Get(kKeyRegionDomainMapping, iterator, value, sizeof(value)) == OT_ERROR_NONE) + { + printf("%s\r\n", value); + } + +exit: + return error; +} + +otError Cli::ParseNextCalibratedPower(char *aCalibratedPowerString, + uint16_t aLength, + uint16_t &aIterator, + Power::CalibratedPower &aCalibratedPower) +{ + otError error = OT_ERROR_NONE; + char *start = aCalibratedPowerString + aIterator; + char *end; + char *subString; + int16_t actualPower; + ot::Power::RawPowerSetting rawPowerSetting; + + VerifyOrExit(aIterator < aLength, error = OT_ERROR_PARSE); + + end = strstr(start, "/"); + if (end != nullptr) + { + aIterator = end - aCalibratedPowerString + 1; // +1 to skip '/' + *end = '\0'; + } + else + { + aIterator = aLength; + end = aCalibratedPowerString + aLength; + } + + subString = strstr(start, kCommaDelimiter); + VerifyOrExit(subString != nullptr, error = OT_ERROR_PARSE); + *subString = '\0'; + subString++; + + SuccessOrExit(error = Utils::CmdLineParser::ParseAsInt16(start, actualPower)); + aCalibratedPower.SetActualPower(actualPower); + + VerifyOrExit(subString < end, error = OT_ERROR_PARSE); + SuccessOrExit(error = rawPowerSetting.Set(subString)); + aCalibratedPower.SetRawPowerSetting(rawPowerSetting); + +exit: + return error; +} + +otError Cli::ProcessCalibrationTable(Utils::CmdLineParser::Arg aArgs[]) +{ + otError error = OT_ERROR_NONE; + + if (aArgs[0].IsEmpty()) + { + int iterator = 0; + char value[kMaxValueSize]; + + ot::Power::CalibratedPower calibratedPower; + + printf("| ChStart | ChEnd | ActualPower(0.01dBm) | RawPowerSetting |\r\n"); + printf("+---------+---------+----------------------+-----------------+\r\n"); + + while (mFactoryConfigFile.Get(kKeyCalibratedPower, iterator, value, sizeof(value)) == OT_ERROR_NONE) + { + SuccessOrExit(error = calibratedPower.FromString(value)); + printf("| %-7d | %-7d | %-20d | %-15s |\r\n", calibratedPower.GetChannelStart(), + calibratedPower.GetChannelEnd(), calibratedPower.GetActualPower(), + calibratedPower.GetRawPowerSetting().ToString().AsCString()); + } + } + else if (aArgs[0] == "add") + { + constexpr uint16_t kStateSearchDomain = 0; + constexpr uint16_t kStateSearchPower = 1; + + uint8_t state = kStateSearchDomain; + char *subString; + uint8_t channel; + Power::CalibratedPower calibratedPower; + + for (Utils::CmdLineParser::Arg *arg = &aArgs[1]; !arg->IsEmpty(); arg++) + { + if ((state == kStateSearchDomain) && (*arg == "-b")) + { + arg++; + VerifyOrExit(!arg->IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + subString = strtok(arg->GetCString(), kCommaDelimiter); + VerifyOrExit(subString != nullptr, error = OT_ERROR_PARSE); + SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(subString, channel)); + calibratedPower.SetChannelStart(channel); + + subString = strtok(NULL, kCommaDelimiter); + VerifyOrExit(subString != nullptr, error = OT_ERROR_PARSE); + SuccessOrExit(error = Utils::CmdLineParser::ParseAsUint8(subString, channel)); + calibratedPower.SetChannelEnd(channel); + VerifyOrExit(calibratedPower.GetChannelStart() <= calibratedPower.GetChannelEnd(), + error = OT_ERROR_INVALID_ARGS); + + state = kStateSearchPower; + } + else if ((state == kStateSearchPower) && (*arg == "-c")) + { + uint16_t length; + uint16_t iterator = 0; + + arg++; + VerifyOrExit(!arg->IsEmpty(), error = OT_ERROR_INVALID_ARGS); + + length = strlen(arg->GetCString()); + while (ParseNextCalibratedPower(arg->GetCString(), length, iterator, calibratedPower) == OT_ERROR_NONE) + { + SuccessOrExit( + error = mFactoryConfigFile.Add(kKeyCalibratedPower, calibratedPower.ToString().AsCString())); + } + + state = kStateSearchDomain; + } + else + { + error = OT_ERROR_INVALID_ARGS; + break; + } + } + + if (state == kStateSearchPower) + { + error = OT_ERROR_INVALID_ARGS; + } + } + else if (aArgs[0] == "clear") + { + error = mFactoryConfigFile.Clear(kKeyCalibratedPower); + } + else + { + error = OT_ERROR_INVALID_ARGS; + } + +exit: + return error; +} + +void Cli::ProcessCommand(Utils::CmdLineParser::Arg aArgs[]) +{ + otError error = OT_ERROR_NOT_FOUND; + int i; + + for (i = 0; i < (sizeof(sCommands) / sizeof(sCommands[0])); i++) + { + if (strcmp(aArgs[0].GetCString(), sCommands[i].mName) == 0) + { + error = (this->*sCommands[i].mCommand)(aArgs + 1); + break; + } + } + +exit: + AppendErrorResult(error); +} + +void Cli::ProcessLine(char *aLine) +{ + const int kMaxArgs = 20; + Utils::CmdLineParser::Arg args[kMaxArgs + 1]; + + SuccessOrExit(ot::Utils::CmdLineParser::ParseCmd(aLine, args, kMaxArgs)); + VerifyOrExit(!args[0].IsEmpty()); + + ProcessCommand(args); + +exit: + OutputPrompt(); +} + +void Cli::OutputPrompt(void) +{ + printf("> "); + fflush(stdout); +} + +void Cli::AppendErrorResult(otError aError) +{ + if (aError != OT_ERROR_NONE) + { + printf("failed\r\nstatus %#x\r\n", aError); + } + else + { + printf("Done\r\n"); + } + + fflush(stdout); +} +} // namespace Fct +} // namespace ot diff --git a/tools/ot-fct/cli.hpp b/tools/ot-fct/cli.hpp new file mode 100644 index 00000000000..e679ef5931d --- /dev/null +++ b/tools/ot-fct/cli.hpp @@ -0,0 +1,117 @@ +/* + * 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 strain 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. + */ + +#ifndef CLI_H +#define CLI_H + +#include "openthread-posix-config.h" + +#include +#include + +#include "config_file.hpp" +#include "power.hpp" +#include "utils/parse_cmdline.hpp" + +#include +#include + +namespace ot { +namespace Fct { + +class Cli; + +/** + * This class implements the factory CLI. + * + */ +class Cli +{ +public: + Cli(void) + : mFactoryConfigFile(OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE) + , mProductConfigFile(OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE) + { + } + + /** + * This method processes a factory command. + * + * @param[in] aArgs The arguments of command line. + * @param[in] aArgsLength The number of args in @p aArgs. + * + */ + void ProcessCommand(Utils::CmdLineParser::Arg aArgs[]); + + /** + * This method processes the command line. + * + * @param[in] aLine A pointer to a command line string. + * + */ + void ProcessLine(char *aLine); + + /** + * This method outputs the prompt. + * + */ + void OutputPrompt(void); + +private: + static constexpr uint16_t kMaxValueSize = 512; + const char *kKeyCalibratedPower = "calibrated_power"; + const char *kKeyTargetPower = "target_power"; + const char *kKeyRegionDomainMapping = "region_domain_mapping"; + const char *kCommaDelimiter = ","; + + struct Command + { + const char *mName; + otError (Cli::*mCommand)(Utils::CmdLineParser::Arg aArgs[]); + }; + + otError ParseNextCalibratedPower(char *aCalibratedPowerString, + uint16_t aLength, + uint16_t &aIterator, + Power::CalibratedPower &aCalibratedPower); + otError ProcessCalibrationTable(Utils::CmdLineParser::Arg aArgs[]); + otError ProcessTargetPowerTable(Utils::CmdLineParser::Arg aArgs[]); + otError ProcessRegionDomainTable(Utils::CmdLineParser::Arg aArgs[]); + otError GetNextDomain(int &aIterator, Power::Domain &aDomain); + otError GetNextTargetPower(const Power::Domain &aDomain, int &aIterator, Power::TargetPower &aTargetPower); + + void AppendErrorResult(otError aError); + + static const struct Command sCommands[]; + + ot::Posix::ConfigFile mFactoryConfigFile; + ot::Posix::ConfigFile mProductConfigFile; +}; +} // namespace Fct +} // namespace ot +#endif diff --git a/examples/platforms/cc2538/openthread-core-cc2538-config-check.h b/tools/ot-fct/logging.cpp similarity index 78% rename from examples/platforms/cc2538/openthread-core-cc2538-config-check.h rename to tools/ot-fct/logging.cpp index 93788b12165..344b162644d 100644 --- a/examples/platforms/cc2538/openthread-core-cc2538-config-check.h +++ b/tools/ot-fct/logging.cpp @@ -1,10 +1,10 @@ /* - * Copyright (c) 2019, The OpenThread Authors. + * 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 + * 1. Redistributions of source code must strain 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 @@ -26,11 +26,16 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef OPENTHREAD_CORE_CC2538_CONFIG_CHECK_H_ -#define OPENTHREAD_CORE_CC2538_CONFIG_CHECK_H_ +#include +#include -#if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT -#error "Platform cc2538 doesn't support configuration option: OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT" -#endif +#include -#endif /* OPENTHREAD_CORE_CC2538_CONFIG_CHECK_H_ */ +void otLogCritPlat(const char *aFormat, ...) +{ + va_list args; + + va_start(args, aFormat); + vprintf(aFormat, args); + va_end(args); +} diff --git a/tools/ot-fct/main.cpp b/tools/ot-fct/main.cpp new file mode 100644 index 00000000000..ffa98f315d8 --- /dev/null +++ b/tools/ot-fct/main.cpp @@ -0,0 +1,102 @@ +/* + * 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 strain 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. + */ + +#include +#include +#include +#include +#include + +#include "cli.hpp" + +static ot::Fct::Cli sCli; + +int main(int argc, char *argv[]) +{ + if (argc >= 2) + { + const int kMaxArgs = 20; + ot::Utils::CmdLineParser::Arg args[kMaxArgs + 1]; + + if (argc - 1 > kMaxArgs) + { + fprintf(stderr, "Too many arguments!\r\n"); + exit(EXIT_FAILURE); + } + + for (int i = 0; i < argc - 1; i++) + { + args[i].SetCString(argv[i + 1]); + } + args[argc - 1].Clear(); + + sCli.ProcessCommand(args); + } + else + { + fd_set rset; + int maxFd; + int ret; + + sCli.OutputPrompt(); + + while (true) + { + FD_ZERO(&rset); + FD_SET(STDIN_FILENO, &rset); + maxFd = STDIN_FILENO + 1; + + ret = select(maxFd, &rset, nullptr, nullptr, nullptr); + + if ((ret == -1) && (errno != EINTR)) + { + fprintf(stderr, "select: %s\n", strerror(errno)); + break; + } + else if (ret > 0) + { + if (FD_ISSET(STDIN_FILENO, &rset)) + { + const int kBufferSize = 512; + char buffer[kBufferSize]; + + if (fgets(buffer, sizeof(buffer), stdin) != nullptr) + { + sCli.ProcessLine(buffer); + } + else + { + break; + } + } + } + } + } + + return EXIT_SUCCESS; +} diff --git a/tools/otci/otci/__init__.py b/tools/otci/otci/__init__.py index 3e97e89598b..7cb0aa45b83 100644 --- a/tools/otci/otci/__init__.py +++ b/tools/otci/otci/__init__.py @@ -29,13 +29,16 @@ from . import errors from .constants import THREAD_VERSION_1_1, THREAD_VERSION_1_2 +from .command_handlers import OTCommandHandler from .otci import OTCI from .otci import \ connect_cli_sim, \ connect_cli_serial, \ connect_ncp_sim, \ connect_cmd_handler, \ - connect_otbr_ssh + connect_otbr_ssh, \ + connect_otbr_adb + from .types import Rloc16, ChildId, NetifIdentifier _connectors = [ @@ -43,8 +46,17 @@ 'connect_cli_serial', 'connect_ncp_sim', 'connect_otbr_ssh', + 'connect_otbr_adb', 'connect_cmd_handler', ] -__all__ = ['OTCI', 'errors', 'Rloc16', 'ChildId', 'NetifIdentifer', 'THREAD_VERSION_1_1', 'THREAD_VERSION_1_2' - ] + _connectors +__all__ = [ + 'OTCI', + 'OTCommandHandler', + 'errors', + 'Rloc16', + 'ChildId', + 'NetifIdentifier', + 'THREAD_VERSION_1_1', + 'THREAD_VERSION_1_2', +] + _connectors diff --git a/tools/otci/otci/command_handlers.py b/tools/otci/otci/command_handlers.py index 0055b1a4644..7c790e6d839 100644 --- a/tools/otci/otci/command_handlers.py +++ b/tools/otci/otci/command_handlers.py @@ -31,7 +31,7 @@ import re import threading import time -from abc import abstractmethod +from abc import abstractmethod, ABC from typing import Any, Callable, Optional, Union, List, Pattern from .connectors import OtCliHandler @@ -39,7 +39,7 @@ from .utils import match_line -class OTCommandHandler: +class OTCommandHandler(ABC): """This abstract class defines interfaces of a OT Command Handler.""" @abstractmethod @@ -50,12 +50,10 @@ def execute_command(self, cmd: str, timeout: float) -> List[str]: Note: each line SHOULD NOT contain '\r\n' at the end. The last line of output should be 'Done' or 'Error : ' following OT CLI conventions. """ - pass @abstractmethod def close(self): """Method close should close the OT Command Handler.""" - pass @abstractmethod def wait(self, duration: float) -> List[str]: @@ -64,7 +62,6 @@ def wait(self, duration: float) -> List[str]: Normally, OT CLI does not output when it's not executing any command. But OT CLI can also output asynchronously in some cases (e.g. `Join Success` when Joiner joins successfully). """ - pass @abstractmethod def set_line_read_callback(self, callback: Optional[Callable[[str], Any]]): @@ -85,7 +82,7 @@ class OtCliCommandRunner(OTCommandHandler): r'(Done|Error|Error \d+:.*|.*: command not found)$') # "Error" for spinel-cli.py __PATTERN_LOG_LINE = re.compile(r'((\[(NONE|CRIT|WARN|NOTE|INFO|DEBG)\])' - r'|(-.*-+: )' # e.g. -CLI-----: + r'|(-.*-+: )' # e.g. -CLI-----: r'|(\[[DINWC\-]\] (?=[\w\-]{14}:)\w+-*:)' # e.g. [I] Mac-----------: r')') """regex used to filter logs""" @@ -240,7 +237,9 @@ def __init__(self, host, port, username, password, sudo): look_for_keys=False) except paramiko.ssh_exception.AuthenticationException: if not password: - self.__ssh.get_transport().auth_none(username) + transport = self.__ssh.get_transport() + assert transport is not None + transport.auth_none(username) else: raise @@ -282,3 +281,46 @@ def wait(self, duration: float) -> List[str]: def set_line_read_callback(self, callback: Optional[Callable[[str], Any]]): self.__line_read_callback = callback + + +class OtbrAdbCommandRunner(OTCommandHandler): + + def __init__(self, host, port): + from adb_shell.adb_device import AdbDeviceTcp + + self.__host = host + self.__port = port + self.__adb = AdbDeviceTcp(host, port, default_transport_timeout_s=9.0) + + self.__line_read_callback = None + self.__adb.connect(rsa_keys=None, auth_timeout_s=0.1) + + def __repr__(self): + return f'{self.__host}:{self.__port}' + + def execute_command(self, cmd: str, timeout: float) -> List[str]: + sh_cmd = f'ot-ctl {cmd}' + + output = self.shell(sh_cmd, timeout=timeout) + + if self.__line_read_callback is not None: + for line in output: + self.__line_read_callback(line) + + if cmd in ('reset', 'factoryreset'): + self.wait(3) + + return output + + def shell(self, cmd: str, timeout: float) -> List[str]: + return self.__adb.shell(cmd, timeout_s=timeout).splitlines() + + def close(self): + self.__adb.close() + + def wait(self, duration: float) -> List[str]: + time.sleep(duration) + return [] + + def set_line_read_callback(self, callback: Optional[Callable[[str], Any]]): + self.__line_read_callback = callback diff --git a/tools/otci/otci/connectors.py b/tools/otci/otci/connectors.py index 713fbc311f9..55477e1be4f 100644 --- a/tools/otci/otci/connectors.py +++ b/tools/otci/otci/connectors.py @@ -29,17 +29,16 @@ import logging import subprocess import time -from abc import abstractmethod +from abc import abstractmethod, ABC from typing import Optional -class OtCliHandler: +class OtCliHandler(ABC): """This abstract class defines interfaces for a OT CLI Handler.""" @abstractmethod - def readline(self) -> str: + def readline(self) -> Optional[str]: """Method readline should return the next line read from OT CLI.""" - pass @abstractmethod def writeline(self, s: str) -> None: @@ -47,7 +46,6 @@ def writeline(self, s: str) -> None: It should block until all characters are written to OT CLI. """ - pass @abstractmethod def wait(self, duration: float) -> None: @@ -56,15 +54,13 @@ def wait(self, duration: float) -> None: A normal implementation should just call `time.sleep(duration)`. This is intended for proceeding Virtual Time Simulation instances. """ - pass @abstractmethod def close(self) -> None: """Method close should close the OT CLI Handler.""" - pass -class Simulator: +class Simulator(ABC): """This abstract class defines interfaces for a Virtual Time Simulator.""" @abstractmethod @@ -84,10 +80,12 @@ def __init__(self, proc: subprocess.Popen, nodeid: int, simulator: Simulator): def __repr__(self): return 'OTCli<%d>' % self.__nodeid - def readline(self) -> str: + def readline(self) -> Optional[str]: + assert self.__otcli_proc.stdout is not None return self.__otcli_proc.stdout.readline().rstrip('\r\n') def writeline(self, s: str): + assert self.__otcli_proc.stdin is not None self.__otcli_proc.stdin.write(s + '\n') self.__otcli_proc.stdin.flush() @@ -100,6 +98,8 @@ def wait(self, duration: float): time.sleep(duration) def close(self): + assert self.__otcli_proc.stdin is not None + assert self.__otcli_proc.stdout is not None self.__otcli_proc.stdin.close() self.__otcli_proc.stdout.close() self.__otcli_proc.wait() @@ -120,7 +120,7 @@ def __init__(self, executable: str, nodeid: int, simulator: Simulator): super().__init__(proc, nodeid, simulator) -class OtNcpSim(OtCliHandler): +class OtNcpSim(OtCliPopen): """Connector for OT NCP Simulation instances.""" def __init__(self, executable: str, nodeid: int, simulator: Simulator): diff --git a/tools/otci/otci/otci.py b/tools/otci/otci/otci.py index da6ef6135a5..9a8c2cb22bc 100644 --- a/tools/otci/otci/otci.py +++ b/tools/otci/otci/otci.py @@ -33,7 +33,7 @@ from typing import Callable, List, Collection, Union, Tuple, Optional, Dict, Pattern, Any from . import connectors -from .command_handlers import OTCommandHandler, OtCliCommandRunner, OtbrSshCommandRunner +from .command_handlers import OTCommandHandler, OtCliCommandRunner, OtbrSshCommandRunner, OtbrAdbCommandRunner from .connectors import Simulator from .errors import UnexpectedCommandOutput, ExpectLineTimeoutError, CommandError, InvalidArgumentsError from .types import ChildId, Rloc16, Ip6Addr, ThreadState, PartitionId, DeviceMode, RouterId, SecurityPolicy, Ip6Prefix, \ @@ -64,7 +64,7 @@ def __repr__(self): """Gets the string representation of the OTCI instance.""" return repr(self.__otcmd) - def wait(self, duration: float, expect_line: Union[str, Pattern, Collection[Any]] = None): + def wait(self, duration: float, expect_line: Optional[Union[str, Pattern, Collection[Any]]] = None): """Wait for a given duration. :param duration: The duration (in seconds) wait for. @@ -101,8 +101,10 @@ def execute_command(self, try: return self.__execute_command(cmd, timeout, silent, already_is_ok=already_is_ok) except Exception: + self.wait(2) if i == self.__exec_command_retry: raise + assert False def __execute_command(self, cmd: str, @@ -218,7 +220,7 @@ def factory_reset(self): ) def ping(self, - ip: str, + ip: Union[str, Ip6Addr], size: int = 8, count: int = 1, interval: float = 1, @@ -260,15 +262,15 @@ def ping_stop(self): """Stop sending ICMPv6 Echo Requests.""" self.execute_command('ping stop') - def discover(self, channel: int = None) -> List[Dict[str, Any]]: + def discover(self, channel: Optional[int] = None) -> List[Dict[str, Any]]: """Perform an MLE Discovery operation.""" return self.__scan_networks('discover', channel) - def scan(self, channel: int = None) -> List[Dict[str, Any]]: + def scan(self, channel: Optional[int] = None) -> List[Dict[str, Any]]: """Perform an IEEE 802.15.4 Active Scan.""" return self.__scan_networks('scan', channel) - def __scan_networks(self, cmd: str, channel: int = None) -> List[Dict[str, Any]]: + def __scan_networks(self, cmd: str, channel: Optional[int] = None) -> List[Dict[str, Any]]: if channel is not None: cmd += f' {channel}' @@ -299,7 +301,7 @@ def __scan_networks(self, cmd: str, channel: int = None) -> List[Dict[str, Any]] return networks - def scan_energy(self, duration: float = None, channel: int = None) -> Dict[int, int]: + def scan_energy(self, duration: Optional[float] = None, channel: Optional[int] = None) -> Dict[int, int]: """Perform an IEEE 802.15.4 Energy Scan.""" cmd = 'scan energy' if duration is not None: @@ -495,9 +497,9 @@ def set_state(self, state: str): """Try to switch to state detached, child, router or leader.""" self.execute_command(f'state {state}') - def get_rloc16(self) -> int: + def get_rloc16(self) -> Rloc16: """Get the Thread RLOC16 value.""" - return self.__parse_int(self.execute_command('rloc16'), 16) + return Rloc16(self.__parse_int(self.execute_command('rloc16'), 16)) def get_router_id(self) -> int: """Get the Thread Router ID value.""" @@ -783,7 +785,9 @@ def dns_get_config(self): for line in output: k, v = line.split(': ') if k == 'Server': - ip, port = re.match(OTCI._IPV6_SERVER_PORT_PATTERN, v).groups() + matched = re.match(OTCI._IPV6_SERVER_PORT_PATTERN, v) + assert matched is not None + ip, port = matched.groups() config['server'] = (Ip6Addr(ip), int(port)) elif k == 'ResponseTimeout': config['response_timeout'] = int(v[:-3]) @@ -798,9 +802,9 @@ def dns_get_config(self): def dns_set_config(self, server: Tuple[Union[str, ipaddress.IPv6Address], int], - response_timeout: int = None, - max_tx_attempts: int = None, - recursion_desired: bool = None): + response_timeout: Optional[int] = None, + max_tx_attempts: Optional[int] = None, + recursion_desired: Optional[bool] = None): """Set DNS client query config.""" cmd = f'dns config {str(server[0])} {server[1]}' if response_timeout is not None: @@ -935,6 +939,7 @@ def __parse_srp_server_hosts(self, output: List[str]) -> List[Dict]: info = {'host': line} result.append(info) else: + assert info is not None k, v = line.strip().split(': ') if k == 'deleted': if v not in ('true', 'false'): @@ -961,6 +966,7 @@ def __parse_srp_server_services(self, output: List[str]) -> List[Dict]: info = {'instance': line} result.append(info) else: + assert info is not None k, v = line.strip().split(': ') if k == 'deleted': if v not in ('true', 'false'): @@ -976,7 +982,7 @@ def __parse_srp_server_services(self, output: List[str]) -> List[Dict]: info['addresses'] = list(map(Ip6Addr, v.split(', '))) elif k == 'subtypes': info[k] = list() if v == '(null)' else list(v.split(',')) - elif k in ('port', 'weight', 'priority', 'ttl'): + elif k in ('port', 'weight', 'priority', 'ttl', 'lease', 'key-lease'): info[k] = int(v) elif k in ('host',): info[k] = v @@ -1129,7 +1135,7 @@ def srp_client_add_service(self, port: int, priority: int = 0, weight: int = 0, - txt: Dict[str, Union[str, bytes, bool]] = None): + txt: Optional[Dict[str, Union[str, bytes, bool]]] = None): instance = self.__escape_escapable(instance) cmd = f'srp client service add {instance} {service} {port} {priority} {weight}' if txt: @@ -1163,7 +1169,9 @@ def srp_client_set_lease_interval(self, interval: int): def srp_client_get_server(self) -> Tuple[Ip6Addr, int]: """Get the SRP server (IP, port).""" result = self.__parse_str(self.execute_command('srp client server')) - ip, port = re.match(OTCI._IPV6_SERVER_PORT_PATTERN, result).groups() + matched = re.match(OTCI._IPV6_SERVER_PORT_PATTERN, result) + assert matched + ip, port = matched.groups() return Ip6Addr(ip), int(port) def srp_client_get_service_key(self) -> bool: @@ -1374,15 +1382,19 @@ def get_csl_config(self) -> Dict[str, int]: if k == 'Channel': cfg['channel'] = int(v) elif k == 'Timeout': - cfg['timeout'] = int(OTCI._CSL_TIMEOUT_PATTERN.match(v).group(1)) + matched = OTCI._CSL_TIMEOUT_PATTERN.match(v) + assert matched is not None + cfg['timeout'] = int(matched.group(1)) elif k == 'Period': - cfg['period'] = int(OTCI._CSL_PERIOD_PATTERN.match(v).group(1)) + matched = OTCI._CSL_PERIOD_PATTERN.match(v) + assert matched is not None + cfg['period'] = int(matched.group(1)) else: logging.warning("Ignore unknown CSL parameter: %s: %s", k, v) return cfg - def config_csl(self, channel: int = None, period: int = None, timeout: int = None): + def config_csl(self, channel: Optional[int] = None, period: Optional[int] = None, timeout: Optional[int] = None): """Configure CSL parameters. :param channel: Set CSL channel. @@ -1486,7 +1498,7 @@ def set_commissioner_provisioning_url(self, url: str): # # Joiner operations # - def joiner_start(self, psk: str, provisioning_url: str = None): + def joiner_start(self, psk: str, provisioning_url: Optional[str] = None): """Start the Joiner.""" cmd = f'joiner start {psk}' if provisioning_url is not None: @@ -1569,7 +1581,16 @@ def get_network_data(self) -> Dict[str, List]: routes_output.append(line) netdata['routes'] = self.__parse_routes(routes_output) - netdata['services'] = self.__parse_services(output) + + services_output = [] + while True: + line = output.pop(0) + if line == 'Contexts:': + break + else: + services_output.append(line) + + netdata['services'] = self.__parse_services(services_output) return netdata @@ -1621,9 +1642,9 @@ def __parse_routes(self, output: List[str]) -> List[Tuple[str, bool, str, Rloc16 routes = [] for line in output: line = line.split() - if line[1] == 's': - prefix, _, prf, rloc16 = line - stable = True + if len(line) == 4: + prefix, flags, prf, rloc16 = line + stable = 's' in flags else: prefix, prf, rloc16 = line stable = False @@ -1770,17 +1791,17 @@ def set_dataset_bytes(self, dataset: str, data: bytes) -> None: self.execute_command(cmd) def dataset_set_buffer(self, - active_timestamp: int = None, - channel: int = None, - channel_mask: int = None, - extpanid: str = None, - mesh_local_prefix: str = None, - network_key: str = None, - network_name: str = None, - panid: int = None, - pskc: str = None, - security_policy: tuple = None, - pending_timestamp: int = None): + active_timestamp: Optional[int] = None, + channel: Optional[int] = None, + channel_mask: Optional[int] = None, + extpanid: Optional[str] = None, + mesh_local_prefix: Optional[str] = None, + network_key: Optional[str] = None, + network_name: Optional[str] = None, + panid: Optional[int] = None, + pskc: Optional[str] = None, + security_policy: Optional[tuple] = None, + pending_timestamp: Optional[int] = None): if active_timestamp is not None: self.execute_command(f'dataset activetimestamp {active_timestamp}') @@ -1830,7 +1851,7 @@ def enable_allowlist(self): def disable_allowlist(self): self.execute_command('macfilter addr disable') - def add_allowlist(self, addr: str, rssi: int = None): + def add_allowlist(self, addr: str, rssi: Optional[int] = None): cmd = f'macfilter addr add {addr}' if rssi is not None: @@ -2043,7 +2064,10 @@ def get_backbone_router_config(self) -> dict: return config - def set_backbone_router_config(self, seqno: int = None, delay: int = None, timeout: int = None): + def set_backbone_router_config(self, + seqno: Optional[int] = None, + delay: Optional[int] = None, + timeout: Optional[int] = None): """Configure local Backbone Router configuration for Thread 1.2 FTD. Call register_backbone_router_dataset() to explicitly register Backbone Router service to Leader for Secondary Backbone Router. @@ -2207,7 +2231,12 @@ def udp_connect(self, ip: str, port: int): """ self.execute_command(f'udp connect {ip} {port}') - def udp_send(self, ip: str = None, port: int = None, text: str = None, random_bytes: int = None, hex: str = None): + def udp_send(self, + ip: Optional[Union[str, Ip6Addr]] = None, + port: Optional[int] = None, + text: Optional[str] = None, + random_bytes: Optional[int] = None, + hex: Optional[str] = None): """Send a few bytes over UDP. ip: the IPv6 destination address. @@ -2282,11 +2311,11 @@ def coap_stop(self): """Stops the application coap service.""" self.execute_command('coap stop') - def coap_get(self, addr: str, uri_path: str, type: str = "con"): + def coap_get(self, addr: Union[str, Ip6Addr], uri_path: str, type: str = "con"): cmd = f'coap get {addr} {uri_path} {type}' self.execute_command(cmd) - def coap_put(self, addr: str, uri_path: str, type: str = "con", payload: str = None): + def coap_put(self, addr: Union[str, Ip6Addr], uri_path: str, type: str = "con", payload: Optional[str] = None): cmd = f'coap put {addr} {uri_path} {type}' if payload is not None: @@ -2294,7 +2323,7 @@ def coap_put(self, addr: str, uri_path: str, type: str = "con", payload: str = N self.execute_command(cmd) - def coap_post(self, addr: str, uri_path: str, type: str = "con", payload: str = None): + def coap_post(self, addr: Union[str, Ip6Addr], uri_path: str, type: str = "con", payload: Optional[str] = None): cmd = f'coap post {addr} {uri_path} {type}' if payload is not None: @@ -2302,7 +2331,7 @@ def coap_post(self, addr: str, uri_path: str, type: str = "con", payload: str = self.execute_command(cmd) - def coap_delete(self, addr: str, uri_path: str, type: str = "con", payload: str = None): + def coap_delete(self, addr: Union[str, Ip6Addr], uri_path: str, type: str = "con", payload: Optional[str] = None): cmd = f'coap delete {addr} {uri_path} {type}' if payload is not None: @@ -2486,5 +2515,10 @@ def connect_otbr_ssh(host: str, port: int = 22, username='pi', password='raspber return OTCI(cmd_handler) +def connect_otbr_adb(host: str, port: int = 5555): + cmd_handler = OtbrAdbCommandRunner(host, port) + return OTCI(cmd_handler) + + def connect_cmd_handler(cmd_handler: OTCommandHandler) -> OTCI: return OTCI(cmd_handler) diff --git a/tools/otci/otci/types.py b/tools/otci/otci/types.py index 22295523341..d1e9d11f5df 100644 --- a/tools/otci/otci/types.py +++ b/tools/otci/otci/types.py @@ -28,6 +28,7 @@ # import ipaddress from collections import namedtuple +from enum import IntEnum class ChildId(int): @@ -52,7 +53,7 @@ class PartitionId(int): pass -class NetifIdentifier(int): +class NetifIdentifier(IntEnum): """Represents a network interface identifier.""" UNSPECIFIED = 0 THERAD = 1 diff --git a/tools/otci/otci/utils.py b/tools/otci/otci/utils.py index 3a52a827181..b5952b58c22 100644 --- a/tools/otci/otci/utils.py +++ b/tools/otci/otci/utils.py @@ -33,7 +33,7 @@ def match_line(line: str, expect_line: Union[str, Pattern, Collection[Any]]) -> bool: """Checks if a line is expected (matched by one of the given patterns).""" if isinstance(expect_line, Pattern): - match = expect_line.match(line) + match = expect_line.match(line) is not None elif isinstance(expect_line, str): match = (line == expect_line) else: diff --git a/tools/otci/setup.py b/tools/otci/setup.py index f3d68685aca..6284ace6502 100644 --- a/tools/otci/setup.py +++ b/tools/otci/setup.py @@ -46,5 +46,5 @@ "Operating System :: OS Independent", ], python_requires='>=3.6', - install_requires=['pySerial', 'paramiko', 'pyspinel'], + install_requires=['pySerial', 'paramiko', 'pyspinel', 'adb-shell'], ) diff --git a/tools/otci/tests/test_otci.py b/tools/otci/tests/test_otci.py index 38c59810fb2..ad4b25f52c6 100644 --- a/tools/otci/tests/test_otci.py +++ b/tools/otci/tests/test_otci.py @@ -243,7 +243,7 @@ def _test_otci_single_node(self, leader): for counter_name in leader.counter_names: logging.info('counter %s: %r', counter_name, leader.get_counter(counter_name)) leader.reset_counter(counter_name) - self.assertTrue(all(x == 0 for x in leader.get_counter(counter_name).values())) + self.assertTrue(all(x == 0 for name, x in leader.get_counter(counter_name).items() if "Time" not in name)) logging.info("CSL config: %r", leader.get_csl_config()) leader.config_csl(channel=13, period=100, timeout=200) @@ -378,7 +378,7 @@ def _test_otci_srp(self, client: OTCI, server: OTCI): self.assertEqual('default.service.arpa.', server.srp_server_get_domain()) default_leases = server.srp_server_get_lease() - self.assertEqual(default_leases, (1800, 7200, 86400, 1209600)) + self.assertEqual(default_leases, (30, 97200, 30, 680400)) server.srp_server_set_lease(1801, 7201, 86401, 1209601) leases = server.srp_server_get_lease() self.assertEqual(leases, (1801, 7201, 86401, 1209601)) diff --git a/tools/spi-hdlc-adapter/spi-hdlc-adapter.c b/tools/spi-hdlc-adapter/spi-hdlc-adapter.c index 76650fcaba5..0adff6b734d 100644 --- a/tools/spi-hdlc-adapter/spi-hdlc-adapter.c +++ b/tools/spi-hdlc-adapter/spi-hdlc-adapter.c @@ -314,9 +314,9 @@ static void signal_critical(int sig, siginfo_t *info, void *ucontext) // This is the last hurah for this process. // We dump the stack, because that's all we can do. - void * stack_mem[AUTO_PRINT_BACKTRACE_STACK_DEPTH]; - void ** stack = stack_mem; - char ** stack_symbols; + void *stack_mem[AUTO_PRINT_BACKTRACE_STACK_DEPTH]; + void **stack = stack_mem; + char **stack_symbols; int stack_depth, i; ucontext_t *uc = (ucontext_t *)ucontext; @@ -395,10 +395,7 @@ static void log_debug_buffer(const char *desc, const uint8_t *buffer_ptr, int bu /* ------------------------------------------------------------------------- */ /* MARK: SPI Transfer Functions */ -static void spi_header_set_flag_byte(uint8_t *header, uint8_t value) -{ - header[0] = value; -} +static void spi_header_set_flag_byte(uint8_t *header, uint8_t value) { header[0] = value; } static void spi_header_set_accept_len(uint8_t *header, uint16_t len) { @@ -412,20 +409,11 @@ static void spi_header_set_data_len(uint8_t *header, uint16_t len) header[4] = ((len >> 8) & 0xFF); } -static uint8_t spi_header_get_flag_byte(const uint8_t *header) -{ - return header[0]; -} +static uint8_t spi_header_get_flag_byte(const uint8_t *header) { return header[0]; } -static uint16_t spi_header_get_accept_len(const uint8_t *header) -{ - return (header[1] + (uint16_t)(header[2] << 8)); -} +static uint16_t spi_header_get_accept_len(const uint8_t *header) { return (header[1] + (uint16_t)(header[2] << 8)); } -static uint16_t spi_header_get_data_len(const uint8_t *header) -{ - return (header[3] + (uint16_t)(header[4] << 8)); -} +static uint16_t spi_header_get_data_len(const uint8_t *header) { return (header[3] + (uint16_t)(header[4] << 8)); } static uint8_t *get_real_rx_frame_start(void) { @@ -813,7 +801,7 @@ static bool hdlc_byte_needs_escape(uint8_t byte) static int push_hdlc(void) { int ret = 0; - const uint8_t * spiRxFrameBuffer = get_real_rx_frame_start(); + const uint8_t *spiRxFrameBuffer = get_real_rx_frame_start(); static uint8_t escaped_frame_buffer[MAX_FRAME_SIZE * 2]; static uint16_t unescaped_frame_len; static uint16_t escaped_frame_len; @@ -1019,7 +1007,7 @@ static int pull_hdlc(void) static int push_raw(void) { int ret = 0; - const uint8_t * spiRxFrameBuffer = get_real_rx_frame_start(); + const uint8_t *spiRxFrameBuffer = get_real_rx_frame_start(); static uint8_t raw_frame_buffer[MAX_FRAME_SIZE]; static uint16_t raw_frame_len; static uint16_t raw_frame_sent; @@ -1291,9 +1279,9 @@ static void trigger_reset(void) static bool setup_int_gpio(const char *path) { - char * edge_path = NULL; - char * dir_path = NULL; - char * value_path = NULL; + char *edge_path = NULL; + char *dir_path = NULL; + char *value_path = NULL; ssize_t len; int setup_fd = -1; diff --git a/zephyr/module.yml b/zephyr/module.yml new file mode 100644 index 00000000000..75f79ec19f0 --- /dev/null +++ b/zephyr/module.yml @@ -0,0 +1,31 @@ +# +# 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. +# + +build: + cmake-ext: True + kconfig-ext: True