diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ed878d6fb..5f3d85ab2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,7 +49,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -71,7 +71,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -85,7 +85,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -104,7 +104,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -144,7 +144,7 @@ jobs: CXX: ${{ matrix.compiler_cpp }} steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -163,7 +163,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -182,7 +182,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -235,7 +235,7 @@ jobs: gcc_extract_dir: arm-gnu-toolchain-12.2.rel1-x86_64-arm-none-eabi steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -276,7 +276,7 @@ jobs: CXX: g++-${{ matrix.gcc_ver }} steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -309,7 +309,7 @@ jobs: CXX: clang++-${{ matrix.clang_ver }} steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -347,7 +347,7 @@ jobs: LDFLAGS: -m32 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -375,7 +375,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -411,7 +411,7 @@ jobs: CXX: ${{ matrix.CXX }} steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -435,7 +435,7 @@ jobs: image: openthread/environment steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 0b8a3e761..17044aa71 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -54,7 +54,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1f7328d2e..8cce3c7f4 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -55,7 +55,7 @@ jobs: - docker_name: environment steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml index 32b064159..932742765 100644 --- a/.github/workflows/fuzz.yml +++ b/.github/workflows/fuzz.yml @@ -45,7 +45,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/makefile-check.yml b/.github/workflows/makefile-check.yml index 6daf99dca..2f5044757 100644 --- a/.github/workflows/makefile-check.yml +++ b/.github/workflows/makefile-check.yml @@ -48,7 +48,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/otci.yml b/.github/workflows/otci.yml index 72d8b4f9a..ece75a0dd 100644 --- a/.github/workflows/otci.yml +++ b/.github/workflows/otci.yml @@ -57,7 +57,7 @@ jobs: REAL_DEVICE: 0 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/otns.yml b/.github/workflows/otns.yml index 00bd7d6e8..05ca7e32a 100644 --- a/.github/workflows/otns.yml +++ b/.github/workflows/otns.yml @@ -58,7 +58,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -67,7 +67,7 @@ jobs: with: go-version: "1.20" - name: Set up Python - uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0 + uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 with: python-version: "3.9" - name: Bootstrap @@ -106,7 +106,7 @@ jobs: with: go-version: "1.20" - name: Set up Python - uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0 + uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 with: python-version: "3.9" - name: Bootstrap @@ -159,7 +159,7 @@ jobs: STRESS_LEVEL: ${{ matrix.stress_level }} steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -168,7 +168,7 @@ jobs: with: go-version: "1.20" - name: Set up Python - uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0 + uses: actions/setup-python@65d7f2d534ac1bc67fcd62888c5f4f3d2cb2b236 # v4.7.1 with: python-version: "3.9" - name: Bootstrap @@ -206,7 +206,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/posix.yml b/.github/workflows/posix.yml index e425d933d..b019a255a 100644 --- a/.github/workflows/posix.yml +++ b/.github/workflows/posix.yml @@ -52,7 +52,7 @@ jobs: CXXFLAGS: -DCLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER=1 -DOPENTHREAD_CONFIG_MLE_MAX_CHILDREN=15 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -135,7 +135,7 @@ jobs: VIRTUAL_TIME: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -179,7 +179,7 @@ jobs: OT_READLINE: 'readline' steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -228,7 +228,7 @@ jobs: OT_READLINE: 'off' steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -258,7 +258,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -291,7 +291,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -318,7 +318,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/simulation-1.1.yml b/.github/workflows/simulation-1.1.yml index a31635848..3705ceb4d 100644 --- a/.github/workflows/simulation-1.1.yml +++ b/.github/workflows/simulation-1.1.yml @@ -55,7 +55,7 @@ jobs: MULTIPLY: 3 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -103,7 +103,7 @@ jobs: VIRTUAL_TIME: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -153,7 +153,7 @@ jobs: MESSAGE_USE_HEAP: ${{ matrix.message_use_heap }} steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -196,7 +196,7 @@ jobs: VIRTUAL_TIME: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -235,7 +235,7 @@ jobs: THREAD_VERSION: 1.1 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -274,7 +274,7 @@ jobs: THREAD_VERSION: 1.1 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -326,7 +326,7 @@ jobs: CXXFLAGS: "-DOPENTHREAD_CONFIG_LOG_PREPEND_UPTIME=0" steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -368,7 +368,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -395,7 +395,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/simulation-1.2.yml b/.github/workflows/simulation-1.2.yml index 4080e945f..d88be6c5c 100644 --- a/.github/workflows/simulation-1.2.yml +++ b/.github/workflows/simulation-1.2.yml @@ -66,7 +66,7 @@ jobs: arch: ["m32", "m64"] steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -127,7 +127,7 @@ jobs: INTER_OP_BBR: 0 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -142,6 +142,9 @@ jobs: - name: Build run: | ./script/test build + - name: Build with OT_CSL_RECEIVER_LOCAL_TIME_SYNC + run: | + OT_BUILDDIR="${PWD}/build_csl_receiver_local_time_sync" OT_OPTIONS="-DOT_CSL_RECEIVER_LOCAL_TIME_SYNC=ON" ./script/test build - name: Get Thread-Wireshark run: | ./script/test get_thread_wireshark @@ -153,6 +156,9 @@ jobs: do ./script/test cert_suite ./tests/scripts/thread-cert/v1_2_LowPower*.py done + - name: Run with OT_CSL_RECEIVER_LOCAL_TIME_SYNC + run: | + OT_BUILDDIR="${PWD}/build_csl_receiver_local_time_sync" ./script/test cert_suite ./tests/scripts/thread-cert/v1_2_LowPower*.py - name: Check Crash if: ${{ failure() }} run: | @@ -191,7 +197,7 @@ jobs: MULTIPLY: 3 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -235,7 +241,7 @@ jobs: VIRTUAL_TIME: 0 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -283,7 +289,7 @@ jobs: INTER_OP: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -343,7 +349,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -370,7 +376,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/size.yml b/.github/workflows/size.yml index 6eeb3ab22..f0a364df3 100644 --- a/.github/workflows/size.yml +++ b/.github/workflows/size.yml @@ -49,7 +49,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/toranj.yml b/.github/workflows/toranj.yml index e6a64d6e4..16e9e92e8 100644 --- a/.github/workflows/toranj.yml +++ b/.github/workflows/toranj.yml @@ -59,7 +59,7 @@ jobs: TORANJ_EVENT_NAME: ${{ github.event_name }} steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -90,7 +90,7 @@ jobs: TORANJ_CLI: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -122,7 +122,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -144,6 +144,12 @@ jobs: ninja test # Validate posix builds with different radio configs git clean -dfx + ./tests/toranj/build.sh ncp-15.4 + git clean -dfx + ./tests/toranj/build.sh ncp-trel + git clean -dfx + ./tests/toranj/build.sh ncp-15.4+trel + git clean -dfx ./tests/toranj/build.sh posix-15.4 git clean -dfx ./tests/toranj/build.sh posix-15.4+trel @@ -158,7 +164,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -185,7 +191,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 91011286c..1df2df537 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -49,7 +49,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -67,7 +67,7 @@ jobs: COVERAGE: 1 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -82,6 +82,10 @@ jobs: run: ./script/cmake-build simulation - name: Test Simulation run: cd build/simulation && ninja test + - name: Build Multipan Simulation + run: ./script/cmake-build simulation -DOT_MULTIPAN_TEST=ON + - name: Test Multipan Simulation + run: cd build/simulation && ninja test - name: Build POSIX run: ./script/cmake-build posix - name: Test POSIX @@ -100,7 +104,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -127,7 +131,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index e7024abbf..41a4e8db9 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -45,7 +45,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Harden Runner - uses: step-security/harden-runner@1b05615854632b887b69ae1be8cbefe72d3ae423 # v2.6.0 + uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs diff --git a/doc/ot_api_doc.h b/doc/ot_api_doc.h index d6ce51e47..84e48c800 100644 --- a/doc/ot_api_doc.h +++ b/doc/ot_api_doc.h @@ -176,6 +176,7 @@ * @defgroup plat-memory Memory * @defgroup plat-messagepool Message Pool * @defgroup plat-misc Miscellaneous + * @defgroup plat-multipan Multipan * @defgroup plat-otns Network Simulator * @defgroup plat-radio Radio * @defgroup plat-settings Settings diff --git a/etc/cmake/options.cmake b/etc/cmake/options.cmake index 5f5d96863..45e079dc5 100644 --- a/etc/cmake/options.cmake +++ b/etc/cmake/options.cmake @@ -172,6 +172,7 @@ 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_BLE_TCAT OPENTHREAD_CONFIG_BLE_TCAT_ENABLE "Ble based thread commissioning") 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") @@ -246,7 +247,7 @@ ot_option(OT_UDP_FORWARD OPENTHREAD_CONFIG_UDP_FORWARD_ENABLE "UDP forward") ot_option(OT_UPTIME OPENTHREAD_CONFIG_UPTIME_ENABLE "uptime") option(OT_DOC "build OpenThread documentation") - +option(OT_MULTIPAN_RCP "Multi-PAN RCP" OFF) message(STATUS "- - - - - - - - - - - - - - - - ") # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/etc/gn/openthread.gni b/etc/gn/openthread.gni index 9bbb543d2..277ee830d 100644 --- a/etc/gn/openthread.gni +++ b/etc/gn/openthread.gni @@ -81,6 +81,9 @@ if (openthread_enable_core_config_args) { # Enable backbone router functionality openthread_config_backbone_router_enable = false + # Enable BLE based commissioning functionality + openthread_config_ble_tcat_enable = false + # Enable border agent support openthread_config_border_agent_enable = false diff --git a/examples/apps/ncp/main.c b/examples/apps/ncp/main.c index ec3f8dc96..5f59261e8 100644 --- a/examples/apps/ncp/main.c +++ b/examples/apps/ncp/main.c @@ -42,6 +42,16 @@ #include "openthread-system.h" #include "lib/platform/reset_util.h" + +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE +#if OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE == 0 +#error "Support for multiple OpenThread static instance is disabled." +#endif +#define ENDPOINT_CT OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM +#else +#define ENDPOINT_CT 1 +#endif /* OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE */ + /** * Initializes the NCP app. * @@ -49,6 +59,7 @@ * */ extern void otAppNcpInit(otInstance *aInstance); +extern void otAppNcpInitMulti(otInstance **aInstances, uint8_t count); #if OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE OT_TOOL_WEAK void *otPlatCAlloc(size_t aNum, size_t aSize) { return calloc(aNum, aSize); } @@ -70,7 +81,9 @@ int main(int argc, char *argv[]) prctl(PR_SET_PDEATHSIG, SIGHUP); #endif -#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + otInstance *instances[ENDPOINT_CT] = {NULL}; +#elif OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE size_t otInstanceBufferLength = 0; uint8_t *otInstanceBuffer = NULL; #endif @@ -79,7 +92,16 @@ int main(int argc, char *argv[]) otSysInit(argc, argv); -#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + for (int i = 0; i < ENDPOINT_CT; i++) + { + instances[i] = otInstanceInitMultiple(i); + + assert(instances[i]); + } + instance = instances[0]; +#elif OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE + // Call to query the buffer size (void)otInstanceInit(NULL, &otInstanceBufferLength); @@ -94,7 +116,11 @@ int main(int argc, char *argv[]) #endif assert(instance); +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + otAppNcpInitMulti(instances, ENDPOINT_CT); +#else otAppNcpInit(instance); +#endif while (!otSysPseudoResetWasRequested()) { @@ -103,7 +129,7 @@ int main(int argc, char *argv[]) } otInstanceFinalize(instance); -#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE +#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && !OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE free(otInstanceBuffer); #endif diff --git a/examples/apps/ncp/ncp.c b/examples/apps/ncp/ncp.c index df6529a3a..38c95b42d 100644 --- a/examples/apps/ncp/ncp.c +++ b/examples/apps/ncp/ncp.c @@ -61,4 +61,17 @@ void otAppNcpInit(otInstance *aInstance) otNcpHdlcInit(aInstance, NcpSend); #endif } + +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE +void otAppNcpInitMulti(otInstance **aInstances, uint8_t aCount) +{ +#if OPENTHREAD_CONFIG_NCP_SPI_ENABLE +#error Multipan support not implemented for SPI +#else + IgnoreError(otPlatUartEnable()); + + otNcpHdlcInitMulti(aInstances, aCount, NcpSend); +#endif +} +#endif #endif // !OPENTHREAD_ENABLE_NCP_VENDOR_HOOK diff --git a/examples/platforms/simulation/CMakeLists.txt b/examples/platforms/simulation/CMakeLists.txt index c99597d4a..945583d70 100644 --- a/examples/platforms/simulation/CMakeLists.txt +++ b/examples/platforms/simulation/CMakeLists.txt @@ -59,6 +59,7 @@ set(OT_PLATFORM_DEFINES ${OT_PLATFORM_DEFINES} PARENT_SCOPE) add_library(openthread-simulation alarm.c + ble.c crypto.c diag.c dns.c @@ -68,6 +69,7 @@ add_library(openthread-simulation infra_if.c logging.c misc.c + multipan.c radio.c spi-stubs.c system.c diff --git a/examples/platforms/simulation/ble.c b/examples/platforms/simulation/ble.c new file mode 100644 index 000000000..2fe3c6453 --- /dev/null +++ b/examples/platforms/simulation/ble.c @@ -0,0 +1,75 @@ +/* + * 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 + +otError otPlatBleEnable(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleDisable(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aInterval); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapAdvStop(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapDisconnect(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aMtu); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aHandle); + OT_UNUSED_VARIABLE(aPacket); + return OT_ERROR_NOT_IMPLEMENTED; +} diff --git a/examples/platforms/simulation/multipan.c b/examples/platforms/simulation/multipan.c new file mode 100644 index 000000000..1c173dfe0 --- /dev/null +++ b/examples/platforms/simulation/multipan.c @@ -0,0 +1,66 @@ +/* + * 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 "platform-simulation.h" + +#include +#include + +#include + +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE +static otInstance *sActiveInstance; +#endif + +otError otPlatMultipanGetActiveInstance(otInstance **aInstance) +{ + otError error = OT_ERROR_NOT_IMPLEMENTED; + OT_UNUSED_VARIABLE(aInstance); + +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + *aInstance = sActiveInstance; + error = OT_ERROR_NONE; +#endif + + return error; +} + +otError otPlatMultipanSetActiveInstance(otInstance *aInstance, bool aCompletePending) +{ + otError error = OT_ERROR_NOT_IMPLEMENTED; + + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aCompletePending); + +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + sActiveInstance = aInstance; + error = OT_ERROR_NONE; +#endif + + return error; +} diff --git a/include/openthread/BUILD.gn b/include/openthread/BUILD.gn index 5bc1c0967..f779443aa 100644 --- a/include/openthread/BUILD.gn +++ b/include/openthread/BUILD.gn @@ -41,6 +41,7 @@ source_set("openthread") { public = [ "backbone_router.h", "backbone_router_ftd.h", + "ble_secure.h", "border_agent.h", "border_router.h", "border_routing.h", @@ -84,6 +85,7 @@ source_set("openthread") { "ping_sender.h", "platform/alarm-micro.h", "platform/alarm-milli.h", + "platform/ble.h", "platform/border_routing.h", "platform/crypto.h", "platform/debug_uart.h", @@ -97,6 +99,7 @@ source_set("openthread") { "platform/memory.h", "platform/messagepool.h", "platform/misc.h", + "platform/multipan.h", "platform/otns.h", "platform/radio.h", "platform/settings.h", @@ -114,6 +117,7 @@ source_set("openthread") { "srp_client_buffers.h", "srp_server.h", "tasklet.h", + "tcat.h", "tcp.h", "tcp_ext.h", "thread.h", diff --git a/include/openthread/ble_secure.h b/include/openthread/ble_secure.h new file mode 100644 index 000000000..98d015640 --- /dev/null +++ b/include/openthread/ble_secure.h @@ -0,0 +1,434 @@ +/* + * 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 top-level functions for the OpenThread BLE Secure implementation. + * + * @note + * The functions in this module require the build-time feature `OPENTHREAD_CONFIG_BLE_TCAT_ENABLE=1`. + * + * @note + * To enable cipher suite DTLS_PSK_WITH_AES_128_CCM_8, MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + * must be enabled in mbedtls-config.h + * To enable cipher suite DTLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + * MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED must be enabled in mbedtls-config.h. + */ + +#ifndef OPENTHREAD_BLE_SECURE_H_ +#define OPENTHREAD_BLE_SECURE_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup api-ble-secure + * + * @brief + * This module includes functions that control BLE Secure (TLS over BLE) communication. + * + * The functions in this module are available when BLE Secure API feature + * (`OPENTHREAD_CONFIG_BLE_TCAT_ENABLE`) is enabled. + * + * @{ + * + */ + +/** + * Pointer to call when ble secure connection state changes. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aConnected TRUE, if a secure connection was established, FALSE otherwise. + * @param[in] aBleConnectionOpen TRUE if a BLE connection was established to carry a TLS data stream, FALSE + * otherwise. + * @param[in] aContext A pointer to arbitrary context information. + * + */ +typedef void (*otHandleBleSecureConnect)(otInstance *aInstance, + bool aConnected, + bool aBleConnectionOpen, + void *aContext); + +/** + * Pointer to call when data was received over a BLE Secure TLS connection. + * + */ +typedef otHandleTcatApplicationDataReceive otHandleBleSecureReceive; + +/** + * Starts the BLE Secure service. + * When TLV mode is active, the function @p aReceiveHandler will be called once a complete TLV was received and the + * message offset points to the TLV value. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aConnectHandler A pointer to a function that will be called when the connection + * state changes. + * @param[in] aReceiveHandler A pointer to a function that will be called once data has been received + * over the TLS connection. + * @param[in] aTlvMode A boolean value indicating if line mode shall be activated. + * @param[in] aContext A pointer to arbitrary context information. May be NULL if not used. + * + * @retval OT_ERROR_NONE Successfully started the BLE Secure server. + * @retval OT_ERROR_ALREADY The service was stated already. + * + */ +otError otBleSecureStart(otInstance *aInstance, + otHandleBleSecureConnect aConnectHandler, + otHandleBleSecureReceive aReceiveHandler, + bool aTlvMode, + void *aContext); + +/** + * Enables the TCAT protocol over BLE Secure. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aVendorInfo A pointer to the Vendor Information (must remain valid after the method call, may be + * NULL). + * @param[in] aHandler A pointer to a function that is called when the join operation completes. + * + * @retval OT_ERROR_NONE Successfully started the BLE Secure Joiner role. + * @retval OT_ERROR_INVALID_ARGS @p aElevationPsk or @p aVendorInfo is invalid. + * @retval OT_ERROR_INVALID_STATE The BLE function has not been started or line mode is not selected. + * + */ +otError otBleSecureTcatStart(otInstance *aInstance, const otTcatVendorInfo *aVendorInfo, otHandleTcatJoin aHandler); + +/** + * Stops the BLE Secure server. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + */ +void otBleSecureStop(otInstance *aInstance); + +/** + * Sets the Pre-Shared Key (PSK) and cipher suite + * TLS_PSK_WITH_AES_128_CCM_8. + * + * @note Requires the build-time feature `MBEDTLS_KEY_EXCHANGE_PSK_ENABLED` to be enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aPsk A pointer to the PSK. + * @param[in] aPskLength The PSK length. + * @param[in] aPskIdentity The Identity Name for the PSK. + * @param[in] aPskIdLength The PSK Identity Length. + * + */ +void otBleSecureSetPsk(otInstance *aInstance, + const uint8_t *aPsk, + uint16_t aPskLength, + const uint8_t *aPskIdentity, + uint16_t aPskIdLength); + +/** + * Returns the peer x509 certificate base64 encoded. + * + * @note Requires the build-time features `MBEDTLS_BASE64_C` and + * `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` to be enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[out] aPeerCert A pointer to the base64 encoded certificate buffer. + * @param[in,out] aCertLength On input, the size the max size of @p aPeerCert. + * On output, the length of the base64 encoded peer certificate. + * + * @retval OT_ERROR_NONE Successfully get the peer certificate. + * @retval OT_ERROR_INVALID_ARGS @p aInstance or @p aCertLength is invalid. + * @retval OT_ERROR_INVALID_STATE Not connected yet. + * @retval OT_ERROR_NO_BUFS Can't allocate memory for certificate. + * + */ +otError otBleSecureGetPeerCertificateBase64(otInstance *aInstance, unsigned char *aPeerCert, size_t *aCertLength); + +/** + * Returns an attribute value identified by its OID from the subject + * of the peer x509 certificate. The peer OID is provided in binary format. + * The attribute length is set if the attribute was successfully read or zero + * if unsuccessful. The ASN.1 type as is set as defineded in the ITU-T X.690 standard + * if the attribute was successfully read. + * + * @note Requires the build-time feature + * `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` to be enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aOid A pointer to the OID to be found. + * @param[in] aOidLength The length of the OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[in,out] aAttributeLength On input, the size the max size of @p aAttributeBuffer. + * On output, the length of the attribute written to the buffer. + * @param[out] aAsn1Type A pointer to the ASN.1 type of the attribute written to the buffer. + * + * @retval OT_ERROR_INVALID_STATE Not connected yet. + * @retval OT_ERROR_INVALID_ARGS Invalid attribute length. + * @retval OT_ERROR_NONE Successfully read attribute. + * @retval OT_ERROR_NO_BUFS Insufficient memory for storing the attribute value. + * + */ +otError otBleSecureGetPeerSubjectAttributeByOid(otInstance *aInstance, + const char *aOid, + size_t aOidLength, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength, + int *aAsn1Type); + +/** + * Returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of + * the peer x509 certificate, where the last digit x is set to aThreadOidDescriptor. + * The attribute length is set if the attribute was successfully read or zero if unsuccessful. + * Requires a connection to be active. + * + * @note Requires the build-time feature + * `MBEDTLS_SSL_KEEP_PEER_CERTIFICATE` to be enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aThreadOidDescriptor The last digit of the Thread attribute OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[in,out] aAttributeLength On input, the size the max size of @p aAttributeBuffer. + * On output, the length of the attribute written to the buffer. + * + * @retval OT_ERROR_NONE Successfully read attribute. + * @retval OT_ERROR_INVALID_ARGS Invalid attribute length. + * @retval OT_NOT_FOUND The requested attribute was not found. + * @retval OT_ERROR_NO_BUFS Insufficient memory for storing the attribute value. + * @retval OT_ERROR_INVALID_STATE Not connected yet. + * @retval OT_ERROR_NOT_IMPLEMENTED The value of aThreadOidDescriptor is >127. + * @retval OT_ERROR_PARSE The certificate extensions could not be parsed. + * + */ +otError otBleSecureGetThreadAttributeFromPeerCertificate(otInstance *aInstance, + int aThreadOidDescriptor, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength); + +/** + * Returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of + * the own x509 certificate, where the last digit x is set to aThreadOidDescriptor. + * The attribute length is set if the attribute was successfully read or zero if unsuccessful. + * Requires a connection to be active. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aThreadOidDescriptor The last digit of the Thread attribute OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[in,out] aAttributeLength On input, the size the max size of @p aAttributeBuffer. + * On output, the length of the attribute written to the buffer. + * + * @retval OT_ERROR_NONE Successfully read attribute. + * @retval OT_ERROR_INVALID_ARGS Invalid attribute length. + * @retval OT_NOT_FOUND The requested attribute was not found. + * @retval OT_ERROR_NO_BUFS Insufficient memory for storing the attribute value. + * @retval OT_ERROR_INVALID_STATE Not connected yet. + * @retval OT_ERROR_NOT_IMPLEMENTED The value of aThreadOidDescriptor is >127. + * @retval OT_ERROR_PARSE The certificate extensions could not be parsed. + * + */ +otError otBleSecureGetThreadAttributeFromOwnCertificate(otInstance *aInstance, + int aThreadOidDescriptor, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength); + +/** + * Sets the authentication mode for the BLE secure connection. + * + * Disable or enable the verification of peer certificate. + * Must be called before start. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aVerifyPeerCertificate true, to verify the peer certificate. + * + */ +void otBleSecureSetSslAuthMode(otInstance *aInstance, bool aVerifyPeerCertificate); + +/** + * Sets the local device's X509 certificate with corresponding private key for + * TLS session with TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8. + * + * @note Requires `MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=1`. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aX509Cert A pointer to the PEM formatted X509 certificate. + * @param[in] aX509Length The length of certificate. + * @param[in] aPrivateKey A pointer to the PEM formatted private key. + * @param[in] aPrivateKeyLength The length of the private key. + * + */ +void otBleSecureSetCertificate(otInstance *aInstance, + const uint8_t *aX509Cert, + uint32_t aX509Length, + const uint8_t *aPrivateKey, + uint32_t aPrivateKeyLength); + +/** + * Sets the trusted top level CAs. It is needed for validating the + * certificate of the peer. + * + * TLS mode "ECDHE ECDSA with AES 128 CCM 8" for secure BLE. + * + * @note Requires `MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=1`. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aX509CaCertificateChain A pointer to the PEM formatted X509 CA chain. + * @param[in] aX509CaCertChainLength The length of chain. + * + */ +void otBleSecureSetCaCertificateChain(otInstance *aInstance, + const uint8_t *aX509CaCertificateChain, + uint32_t aX509CaCertChainLength); + +/** + * Initializes TLS session with a peer using an already open BLE connection. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @retval OT_ERROR_NONE Successfully started TLS connection. + * + */ +otError otBleSecureConnect(otInstance *aInstance); + +/** + * Stops the BLE and TLS connection. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + */ +void otBleSecureDisconnect(otInstance *aInstance); + +/** + * Indicates whether or not the TLS session is active (connected or conneting). + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @retval TRUE If TLS session is active. + * @retval FALSE If TLS session is not active. + * + */ +bool otBleSecureIsConnectionActive(otInstance *aInstance); + +/** + * Indicates whether or not the TLS session is connected. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @retval TRUE The TLS session is connected. + * @retval FALSE The TLS session is not connected. + * + */ +bool otBleSecureIsConnected(otInstance *aInstance); + +/** + * Indicates whether or not the TCAT agent is enabled. + * + * @retval TRUE The TCAT agent is enabled. + * @retval FALSE The TCAT agent is not enabled. + * + */ +bool otBleSecureIsTcatEnabled(otInstance *aInstance); + +/** + * Indicates whether or not a TCAT command class is authorized. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aCommandClass A command class to check. + * + * @retval TRUE The command class is authorized. + * @retval FALSE The command class is not authorized. + * + */ +bool otBleSecureIsCommandClassAuthorized(otInstance *aInstance, otTcatCommandClass aCommandClass); + +/** + * Sends a secure BLE message. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aMessage A pointer to the message to send. + * + * If the return value is OT_ERROR_NONE, OpenThread takes ownership of @p aMessage, and the caller should no longer + * reference @p aMessage. If the return value is not OT_ERROR_NONE, the caller retains ownership of @p aMessage, + * including freeing @p aMessage if the message buffer is no longer needed. + * + * @retval OT_ERROR_NONE Successfully sent message. + * @retval OT_ERROR_NO_BUFS Failed to allocate buffer memory. + * @retval OT_ERROR_INVALID_STATE TLS connection was not initialized. + * + */ +otError otBleSecureSendMessage(otInstance *aInstance, otMessage *aMessage); + +/** + * Sends a secure BLE data packet. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aBuf A pointer to the data to send as the Value of the TCAT Send Application Data TLV. + * @param[in] aLength A number indicating the length of the data buffer. + * + * @retval OT_ERROR_NONE Successfully sent data. + * @retval OT_ERROR_NO_BUFS Failed to allocate buffer memory. + * @retval OT_ERROR_INVALID_STATE TLS connection was not initialized. + * + */ +otError otBleSecureSend(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength); + +/** + * Sends a secure BLE data packet containing a TCAT Send Application Data TLV. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aBuf A pointer to the data to send as the Value of the TCAT Send Application Data TLV. + * @param[in] aLength A number indicating the length of the data buffer. + * + * @retval OT_ERROR_NONE Successfully sent data. + * @retval OT_ERROR_NO_BUFS Failed to allocate buffer memory. + * @retval OT_ERROR_INVALID_STATE TLS connection was not initialized. + * + */ +otError otBleSecureSendApplicationTlv(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength); + +/** + * Flushes the send buffer. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @retval OT_ERROR_NONE Successfully flushed output buffer. + * @retval OT_ERROR_NO_BUFS Failed to allocate buffer memory. + * @retval OT_ERROR_INVALID_STATE TLS connection was not initialized. + * + */ +otError otBleSecureFlush(otInstance *aInstance); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* OPENTHREAD_BLE_SECURE_H_ */ diff --git a/include/openthread/border_routing.h b/include/openthread/border_routing.h index 3569a9e44..5236f0d9d 100644 --- a/include/openthread/border_routing.h +++ b/include/openthread/border_routing.h @@ -118,6 +118,17 @@ typedef struct otBorderRoutingPrefixTableEntry uint32_t mPreferredLifetime; ///< Preferred lifetime of the on-link prefix when `mIsOnLink`. } otBorderRoutingPrefixTableEntry; +/** + * Represents a group of data of platform-generated RA messages processed. + * + */ +typedef struct otPdProcessedRaInfo +{ + uint32_t mNumPlatformRaReceived; ///< The number of platform generated RA handled by ProcessPlatfromGeneratedRa. + uint32_t mNumPlatformPioProcessed; ///< The number of PIO processed for adding OMR prefixes. + uint32_t mLastPlatformRaMsec; ///< The timestamp of last processed RA message. +} otPdProcessedRaInfo; + /** * Represents the state of Border Routing Manager. * @@ -307,6 +318,21 @@ otError otBorderRoutingGetOmrPrefix(otInstance *aInstance, otIp6Prefix *aPrefix) */ otError otBorderRoutingGetPdOmrPrefix(otInstance *aInstance, otBorderRoutingPrefixTableEntry *aPrefixInfo); +/** + * Gets the data of platform generated RA message processed.. + * + * `OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE` must be enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[out] aPrefixInfo A pointer to where the prefix info will be output to. + * + * @retval OT_ERROR_NONE Successfully retrieved the Info. + * @retval OT_ERROR_INVALID_STATE The Border Routing Manager is not initialized yet. + * @retval OT_ERROR_NOT_FOUND There are no valid Info on this BR. + * + */ +otError otBorderRoutingGetPdProcessedRaInfo(otInstance *aInstance, otPdProcessedRaInfo *aPdProcessedRaInfo); + /** * Gets the currently favored Off-Mesh-Routable (OMR) Prefix. * @@ -444,6 +470,18 @@ otError otBorderRoutingGetNextRouterEntry(otInstance *aI */ void otBorderRoutingDhcp6PdSetEnabled(otInstance *aInstance, bool aEnabled); +/** + * Gets the current state of DHCPv6 Prefix Delegation. + * + * Requires `OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE` to be enabled. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * + * @returns The current state of DHCPv6 Prefix Delegation. + * + */ +otBorderRoutingDhcp6PdState otBorderRoutingDhcp6PdGetState(otInstance *aInstance); + /** * @} * diff --git a/include/openthread/instance.h b/include/openthread/instance.h index 18e23d2e6..3a668cffe 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 (376) +#define OPENTHREAD_API_VERSION (383) /** * @addtogroup api-instance @@ -102,6 +102,23 @@ otInstance *otInstanceInit(void *aInstanceBuffer, size_t *aInstanceBufferSize); */ otInstance *otInstanceInitSingle(void); +/** + * Initializes the OpenThread instance. + * + * This function initializes OpenThread and prepares it for subsequent OpenThread API calls. This function must be + * called before any other calls to OpenThread. This method utilizes static buffer to initialize the OpenThread + * instance. + * + * This function is available and can only be used when support for multiple OpenThread static instances is + * enabled (`OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE`) + * + * @param[in] aIdx The index of the OpenThread instance to initialize. + * + * @returns A pointer to the new OpenThread instance. + * + */ +otInstance *otInstanceInitMultiple(uint8_t aIdx); + /** * Gets the instance identifier. * diff --git a/include/openthread/ip6.h b/include/openthread/ip6.h index 850c64c6b..8301ff93d 100644 --- a/include/openthread/ip6.h +++ b/include/openthread/ip6.h @@ -189,6 +189,7 @@ typedef struct otNetifAddress unsigned int mScopeOverride : 4; ///< The IPv6 scope of this address. bool mRloc : 1; ///< TRUE if the address is an RLOC, FALSE otherwise. bool mMeshLocal : 1; ///< TRUE if the address is mesh-local, FALSE otherwise. + bool mSrpRegistered : 1; ///< Used by OT core only (indicates whether registered by SRP Client). const struct otNetifAddress *mNext; ///< A pointer to the next network interface address. } otNetifAddress; @@ -906,6 +907,8 @@ typedef struct otBorderRoutingCounters otPacketsAndBytes mInboundMulticast; ///< The counters for inbound multicast. otPacketsAndBytes mOutboundUnicast; ///< The counters for outbound unicast. otPacketsAndBytes mOutboundMulticast; ///< The counters for outbound multicast. + otPacketsAndBytes mInboundInternet; ///< The counters for inbound Internet when DHCPv6 PD enabled. + otPacketsAndBytes mOutboundInternet; ///< The counters for outbound Internet when DHCPv6 PD enabled. 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. diff --git a/include/openthread/ncp.h b/include/openthread/ncp.h index 7e07fa290..8c810fc84 100644 --- a/include/openthread/ncp.h +++ b/include/openthread/ncp.h @@ -89,6 +89,16 @@ void otNcpHdlcReceive(const uint8_t *aBuf, uint16_t aBufLength); */ void otNcpHdlcInit(otInstance *aInstance, otNcpHdlcSendCallback aSendCallback); +/** + * Initialize the NCP based on HDLC framing. + * + * @param[in] aInstances The OpenThread instance pointers array. + * @param[in] aCount Number of elements in the array. + * @param[in] aSendCallback The function pointer used to send NCP data. + * + */ +void otNcpHdlcInitMulti(otInstance **aInstance, uint8_t aCount, otNcpHdlcSendCallback aSendCallback); + /** * Initialize the NCP based on SPI framing. * diff --git a/include/openthread/platform/ble.h b/include/openthread/platform/ble.h new file mode 100644 index 000000000..bf7e48bc4 --- /dev/null +++ b/include/openthread/platform/ble.h @@ -0,0 +1,293 @@ +/* + * 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 a OpenThread BLE GATT peripheral interface driver. + * + */ + +#ifndef OPENTHREAD_PLATFORM_BLE_H_ +#define OPENTHREAD_PLATFORM_BLE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +/** + * @addtogroup plat-ble + * + * @brief + * This module includes the platform abstraction for BLE Host communication. + * The platform needs to implement Bluetooth LE 4.2 or higher. + * + * @{ + * + */ + +/** + * Time slot duration on PHY layer in microseconds (0.625ms). + * + */ + +#define OT_BLE_TIMESLOT_UNIT 625 + +/** + * Minimum allowed interval for advertising packet in OT_BLE_ADV_INTERVAL_UNIT units (20ms). + * + */ + +#define OT_BLE_ADV_INTERVAL_MIN 0x0020 + +/** + * Maximum allowed interval for advertising packet in OT_BLE_ADV_INTERVAL_UNIT units (10.24s). + * + */ + +#define OT_BLE_ADV_INTERVAL_MAX 0x4000 + +/** + * Default interval for advertising packet (ms). + * + */ + +#define OT_BLE_ADV_INTERVAL_DEFAULT 100 + +/** + * Unit used to calculate interval duration (0.625ms). + * + */ + +#define OT_BLE_ADV_INTERVAL_UNIT OT_BLE_TIMESLOT_UNIT + +/** + * Maximum allowed ATT MTU size (must be >= 23). + * + */ + +#define OT_BLE_ATT_MTU_MAX 67 + +/** + * Default power value for BLE. + */ + +#define OT_BLE_DEFAULT_POWER 0 + +/** + * Represents a BLE packet. + * + */ +typedef struct otBleRadioPacket +{ + uint8_t *mValue; ///< The value of an attribute + uint16_t mLength; ///< Length of the @p mValue. + int8_t mPower; ///< Transmit/receive power in dBm. +} otBleRadioPacket; + +/******************************************************************************* + * @section Bluetooth Low Energy management. + ******************************************************************************/ + +/** + * Enable the Bluetooth Low Energy radio. + * + * @note BLE Device should use the highest ATT_MTU supported that does not + * exceed OT_BLE_ATT_MTU_MAX octets. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @retval OT_ERROR_NONE Successfully enabled. + * @retval OT_ERROR_FAILED The BLE radio could not be enabled. + */ +otError otPlatBleEnable(otInstance *aInstance); + +/** + * Disable the Bluetooth Low Energy radio. + * + * When disabled, the BLE stack will flush event queues and not generate new + * events. The BLE peripheral is turned off or put into a low power sleep + * state. Any dynamic memory used by the stack should be released, + * but static memory may remain reserved. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @retval OT_ERROR_NONE Successfully transitioned to disabled. + * @retval OT_ERROR_FAILED The BLE radio could not be disabled. + * + */ +otError otPlatBleDisable(otInstance *aInstance); + +/**************************************************************************** + * @section Bluetooth Low Energy GAP. + ***************************************************************************/ + +/** + * Starts BLE Advertising procedure. + * + * The BLE device shall use undirected advertising with no filter applied. + * A single BLE Advertising packet must be sent on all advertising + * channels (37, 38 and 39). + * + * @note This function shall be used only for BLE Peripheral role. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aInterval The interval between subsequent advertising packets + * in OT_BLE_ADV_INTERVAL_UNIT units. + * Shall be within OT_BLE_ADV_INTERVAL_MIN and + * OT_BLE_ADV_INTERVAL_MAX range or OT_BLE_ADV_INTERVAL_DEFAULT + * for a default value set at compile time. + * + * @retval OT_ERROR_NONE Advertising procedure has been started. + * @retval OT_ERROR_INVALID_STATE BLE Device is in invalid state. + * @retval OT_ERROR_INVALID_ARGS Invalid interval value has been supplied. + * + */ +otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval); + +/** + * Stops BLE Advertising procedure. + * + * @note This function shall be used only for BLE Peripheral role. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @retval OT_ERROR_NONE Advertising procedure has been stopped. + * @retval OT_ERROR_INVALID_STATE BLE Device is in invalid state. + * + */ +otError otPlatBleGapAdvStop(otInstance *aInstance); + +/** + * The BLE driver calls this method to notify OpenThread that a BLE Central Device has + * been connected. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aConnectionId The identifier of the open connection. + * + */ +extern void otPlatBleGapOnConnected(otInstance *aInstance, uint16_t aConnectionId); + +/** + * The BLE driver calls this method to notify OpenThread that the BLE Central Device + * has been disconnected. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aConnectionId The identifier of the closed connection. + * + */ +extern void otPlatBleGapOnDisconnected(otInstance *aInstance, uint16_t aConnectionId); + +/** + * Disconnects BLE connection. + * + * The BLE device shall use the Remote User Terminated Connection (0x13) reason + * code when disconnecting from the peer BLE device.. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @retval OT_ERROR_NONE Disconnection procedure has been started. + * @retval OT_ERROR_INVALID_STATE BLE Device is in invalid state. + * + */ +otError otPlatBleGapDisconnect(otInstance *aInstance); + +/******************************************************************************* + * @section Bluetooth Low Energy GATT Common. + *******************************************************************************/ + +/** + * Reads currently use value of ATT_MTU. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[out] aMtu A pointer to output the current ATT_MTU value. + * + * @retval OT_ERROR_NONE ATT_MTU value has been placed in @p aMtu. + * @retval OT_ERROR_FAILED BLE Device cannot determine its ATT_MTU. + * + */ +otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu); + +/** + * The BLE driver calls this method to notify OpenThread that ATT_MTU has been updated. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aMtu The updated ATT_MTU value. + * + */ +extern void otPlatBleGattOnMtuUpdate(otInstance *aInstance, uint16_t aMtu); + +/******************************************************************************* + * @section Bluetooth Low Energy GATT Server. + ******************************************************************************/ + +/** + * Sends ATT Handle Value Indication. + * + * @note This function shall be used only for GATT Server. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aHandle The handle of the attribute to be indicated. + * @param[in] aPacket A pointer to the packet contains value to be indicated. + * + * @retval OT_ERROR_NONE ATT Handle Value Indication has been sent. + * @retval OT_ERROR_INVALID_STATE BLE Device is in invalid state. + * @retval OT_ERROR_INVALID_ARGS Invalid handle value, data or data length has been supplied. + * @retval OT_ERROR_NO_BUFS No available internal buffer found. + * + */ +otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket); + +/** + * The BLE driver calls this method to notify OpenThread that an ATT Write Request + * packet has been received. + * + * @note This function shall be used only for GATT Server. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aHandle The handle of the attribute to be written. + * @param[in] aPacket A pointer to the packet contains value to be written to the attribute. + * + */ +extern void otPlatBleGattServerOnWriteRequest(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // end of extern "C" +#endif + +#endif // OPENTHREAD_PLATFORM_BLE_H_ diff --git a/include/openthread/platform/crypto.h b/include/openthread/platform/crypto.h index 8762fc2ce..fe3c0ead5 100644 --- a/include/openthread/platform/crypto.h +++ b/include/openthread/platform/crypto.h @@ -743,14 +743,18 @@ otError otPlatCryptoEcdsaVerifyUsingKeyRef(otCryptoKeyRef aKe * @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); + * @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. + */ +otError 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/multipan.h b/include/openthread/platform/multipan.h new file mode 100644 index 000000000..69cd9839c --- /dev/null +++ b/include/openthread/platform/multipan.h @@ -0,0 +1,145 @@ +/* + * 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 multipan interface for OpenThread. + * + * Multipan RCP is a feature that allows single RCP operate in multiple networks. + * + * Currently we support two types of multipan RCP: + * - Full multipan: RCP operates in parallel on both networks (for example using more than one transceiver) + * - Switching RCP: RCP can communicate only with one network at a time and requires network switching mechanism. + * Switching can be automatic (for example time based, radio sleep based) or manually contolled by + * the host. + * + * Full multipan RCP and Automatic Switching RCP do not require any special care from the host side. + * Manual Switching RCP requires host to switch currently active network. + * + */ + +#ifndef OPENTHREAD_PLATFORM_MULTIPAN_H_ +#define OPENTHREAD_PLATFORM_MULTIPAN_H_ + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup plat-multipan + * + * @brief + * This module includes the platform abstraction for multipan support. + * + * @{ + * + */ + +/** + * Get instance currently in control of the radio. + * + * If radio does not operate in parallel on all interfaces, this function returns an instance object with granted + * radio access. + * + * @param[out] aInstance Pointer to the variable for storing the active instance pointer. + * + * @retval OT_ERROR_NONE Successfully retrieved the property. + * @retval OT_ERROR_NOT_IMPLEMENTED Failed due to lack of the support in radio. + * @retval OT_ERROR_INVALID_COMMAND Platform supports all interfaces simultaneously. + * + */ +otError otPlatMultipanGetActiveInstance(otInstance **aInstance); + +/** + * Set `aInstance` as the current active instance controlling radio. + * + * This function allows selecting the currently active instance on platforms that do not support parallel + * communication on multiple interfaces. In other words, if more than one instance is in a receive state, calling + * #otPlatMultipanSetActiveInstance guarantees that specified instance will be the one receiving. This function returns + * if the request was received properly. After interface switching is complete, the platform should call + * #otPlatMultipanSwitchoverDone. Switching interfaces may take longer if `aCompletePending` is set true. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aCompletePending True if ongoing radio operation should complete before interface switch (Soft switch), + * false for force switch. + * + * @retval OT_ERROR_NONE Successfully set the property. + * @retval OT_ERROR_BUSY Failed due to another operation ongoing. + * @retval OT_ERROR_NOT_IMPLEMENTED Failed due to unknown instance or more instances than interfaces available. + * @retval OT_ERROR_INVALID_COMMAND Platform supports all interfaces simultaneously. + * @retval OT_ERROR_ALREADY Given interface is already active. + * + */ +otError otPlatMultipanSetActiveInstance(otInstance *aInstance, bool aCompletePending); + +/** + * The platform completed the interface switching procedure. + * + * Should be invoked immediately after processing #otPlatMultipanSetActiveInstance if no delay is needed, or if + * some longer radio operations need to complete first, after the switch in interfaces is fully complete. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aSuccess True if successfully switched the interfaces, false if switching failed. + * + */ +extern void otPlatMultipanSwitchoverDone(otInstance *aInstance, bool aSuccess); + +/** + * Get the instance pointer corresponding to the given IID. + * + * @param[in] aIid The IID of the interface. + * + * @retval Instance pointer if aIid is has an instance assigned, nullptr otherwise. + */ +otInstance *otPlatMultipanIidToInstance(uint8_t aIid); + +/** + * Get the IID corresponding to the given OpenThread instance pointer. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @retval IID of the given instance, broadcast IID otherwise. + */ +uint8_t otPlatMultipanInstanceToIid(otInstance *aInstance); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // end of extern "C" +#endif + +#endif // OPENTHREAD_PLATFORM_MULTIPAN_H_ diff --git a/include/openthread/srp_client.h b/include/openthread/srp_client.h index 6eac2b1a8..71a217c37 100644 --- a/include/openthread/srp_client.h +++ b/include/openthread/srp_client.h @@ -460,10 +460,10 @@ otError otSrpClientSetHostName(otInstance *aInstance, const char *aName); /** * Enables auto host address mode. * - * When enabled host IPv6 addresses are automatically set by SRP client using all the unicast addresses on Thread netif - * excluding all link-local and mesh-local addresses. If there is no valid address, then Mesh Local EID address is - * added. The SRP client will automatically re-register when/if addresses on Thread netif are updated (new addresses - * are added or existing addresses are removed). + * When enabled host IPv6 addresses are automatically set by SRP client using all the preferred unicast addresses on + * Thread netif excluding all link-local and mesh-local addresses. If there is no preferred address, then Mesh Local + * EID address is added. The SRP client will automatically re-register when/if addresses on Thread netif are updated + * (new addresses are added or existing addresses are removed or marked as non-preferred). * * The auto host address mode can be enabled before start or during operation of SRP client except when the host info * is being removed (client is busy handling a remove request from an call to `otSrpClientRemoveHostAndServices()` and diff --git a/include/openthread/tcat.h b/include/openthread/tcat.h new file mode 100644 index 000000000..291ce4817 --- /dev/null +++ b/include/openthread/tcat.h @@ -0,0 +1,170 @@ +/* + * 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 top-level functions for the OpenThread TCAT. + * + * @note + * The functions in this module require the build-time feature `OPENTHREAD_CONFIG_BLE_TCAT_ENABLE=1`. + * + * @note + * To enable cipher suite DTLS_PSK_WITH_AES_128_CCM_8, MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + * must be enabled in mbedtls-config.h + * To enable cipher suite DTLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + * MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED must be enabled in mbedtls-config.h. + */ + +#ifndef OPENTHREAD_TCAT_H_ +#define OPENTHREAD_TCAT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @addtogroup api-ble-secure + * + * @brief + * This module includes functions that implement TCAT communication. + * + * The functions in this module are available when TCAT feature + * (`OPENTHREAD_CONFIG_BLE_TCAT_ENABLE`) is enabled. + * + * @{ + * + */ + +#define OT_TCAT_MAX_SERVICE_NAME_LENGTH \ + 15 ///< Maximum string length of a UDP or TCP service name (does not include null char). + +/** + * Represents TCAT status code. + * + */ +typedef enum otTcatStatusCode +{ + OT_TCAT_STATUS_SUCCESS = 0, ///< Command or request was successfully processed + OT_TCAT_STATUS_UNSUPPORTED = 1, ///< Requested command or received TLV is not supported + OT_TCAT_STATUS_PARSE_ERROR = 2, ///< Request / command could not be parsed correctly + OT_TCAT_STATUS_VALUE_ERROR = 3, ///< The value of the transmitted TLV has an error + OT_TCAT_STATUS_GENERAL_ERROR = 4, ///< An error not matching any other category occurred + OT_TCAT_STATUS_BUSY = 5, ///< Command cannot be executed because the resource is busy + OT_TCAT_STATUS_UNDEFINED = 6, ///< The requested value, data or service is not defined (currently) or not present + OT_TCAT_STATUS_HASH_ERROR = 7, ///< The hash value presented by the commissioner was incorrect + OT_TCAT_STATUS_UNAUTHORIZED = 16, ///< Sender does not have sufficient authorization for the given command + +} otTcatStatusCode; + +/** + * Represents TCAT application protocol. + * + */ +typedef enum otTcatApplicationProtocol +{ + OT_TCAT_APPLICATION_PROTOCOL_NONE = 0, ///< Message which has been sent without activating the TCAT agent + OT_TCAT_APPLICATION_PROTOCOL_STATUS = 1, ///< Message directed to a UDP service + OT_TCAT_APPLICATION_PROTOCOL_TCP = 2, ///< Message directed to a TCP service + +} otTcatApplicationProtocol; + +/** + * Represents a TCAT command class. + * + */ +typedef enum otTcatCommandClass +{ + OT_TCAT_COMMAND_CLASS_GENERAL = 0, ///< TCAT commands related to general operations + OT_TCAT_COMMAND_CLASS_COMMISSIONING = 1, ///< TCAT commands related to commissioning + OT_TCAT_COMMAND_CLASS_EXTRACTION = 2, ///< TCAT commands related to key extraction + OT_TCAT_COMMAND_CLASS_DECOMMISSIONING = 3, ///< TCAT commands related to de-commissioning + OT_TCAT_COMMAND_CLASS_APPLICATION = 4, ///< TCAT commands related to application layer + +} otTcatCommandClass; + +/** + * This structure represents a TCAT vendor information. + * + * The content of this structure MUST persist and remain unchanged while a TCAT session is running. + * + */ +typedef struct otTcatVendorInfo +{ + const char *mProvisioningUrl; ///< Provisioning URL path string + const char *mVendorName; ///< Vendor name string + const char *mVendorModel; ///< Vendor model string + const char *mVendorSwVersion; ///< Vendor software version string + const char *mVendorData; ///< Vendor specific data string + const char *mPskdString; ///< Vendor managed pre-shared key for device + const char *mInstallCode; ///< Vendor managed install code string + const char *mDeviceId; ///< Vendor managed device ID string (if NULL: device ID is set to EUI-64 in binary format) + +} otTcatVendorInfo; + +/** + * Pointer to call when application data was received over a TCAT TLS connection. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aMessage A pointer to the message. + * @param[in] aOffset The offset where the application data begins. + * @param[in] aTcatApplicationProtocol The protocol type of the message received. + * @param[in] aServiceName The name of the service the message is direced to. + * @param[in] aContext A pointer to arbitrary context information. + * + */ +typedef void (*otHandleTcatApplicationDataReceive)(otInstance *aInstance, + const otMessage *aMessage, + int32_t aOffset, + otTcatApplicationProtocol aTcatApplicationProtocol, + const char *aServiceName, + void *aContext); + +/** + * Pointer to call to notify the completion of a join operation. + * + * @param[in] aError OT_ERROR_NONE if the join process succeeded. + * OT_ERROR_SECURITY if the join process failed due to security credentials. + * @param[in] aContext A pointer to arbitrary context information. + * + */ +typedef void (*otHandleTcatJoin)(otError aError, void *aContext); + +/** + * @} + * + */ + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* OPENTHREAD_TCAT_H_ */ diff --git a/script/check-simulation-build-cmake b/script/check-simulation-build-cmake index ee1e2ffa5..eaee3716b 100755 --- a/script/check-simulation-build-cmake +++ b/script/check-simulation-build-cmake @@ -201,6 +201,9 @@ build_all_features() # Build with RAM settings reset_source "$(dirname "$0")"/cmake-build simulation -DOT_SETTINGS_RAM=ON + + reset_source + "$(dirname "$0")"/cmake-build simulation -DOT_BLE_TCAT=ON } build_nest_common() diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index 6be47773c..022de520b 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -48,6 +48,7 @@ set(COMMON_SOURCES cli_output.cpp cli_srp_client.cpp cli_srp_server.cpp + cli_tcat.cpp cli_tcp.cpp cli_udp.cpp ) diff --git a/src/cli/README.md b/src/cli/README.md index c0b21e493..20df53e0d 100644 --- a/src/cli/README.md +++ b/src/cli/README.md @@ -117,6 +117,7 @@ Done - [sntp](#sntp-query-sntp-server-ip-sntp-server-port) - [state](#state) - [srp](README_SRP.md) +- [tcat](README_TCAT.md) - [tcp](README_TCP.md) - [thread](#thread-start) - [timeinqueue](#timeinqueue) diff --git a/src/cli/README_SRP_CLIENT.md b/src/cli/README_SRP_CLIENT.md index ae48fbf1e..ca486f310 100644 --- a/src/cli/README_SRP_CLIENT.md +++ b/src/cli/README_SRP_CLIENT.md @@ -187,7 +187,7 @@ fd00:0:0:0:0:0:0:beef Done ``` -Enable auto host address mode. When enabled client will automatically use all Thread netif unicast addresses excluding all link-local and mesh-local addresses. If there is no valid address, then Mesh Local EID address is added. SRP client will automatically re-register if/when addresses on Thread netif get changed (e.g., new address is added or existing address is removed). +Enable auto host address mode. When enabled client will automatically use all preferred Thread netif unicast addresses excluding all link-local and mesh-local addresses. If there is no preferred address, then Mesh Local EID address is added. SRP client will automatically re-register if/when addresses on Thread netif get changed (e.g., new address is added or existing address is removed or marked as non-preferred). ```bash > srp client host address auto diff --git a/src/cli/README_SRP_SERVER.md b/src/cli/README_SRP_SERVER.md index ad8718259..f86e52fbe 100644 --- a/src/cli/README_SRP_SERVER.md +++ b/src/cli/README_SRP_SERVER.md @@ -8,6 +8,7 @@ See [README_SRP.md](README_SRP.md). - [help](#help) - [addrmode](#addrmode) +- [auto](#auto) - [disable](#disable) - [domain](#domain) - [enable](#enable) @@ -28,6 +29,7 @@ Print SRP server help menu. ```bash > srp server help addrmode +auto disable domain enable @@ -67,6 +69,25 @@ anycast Done ``` +### auto + +Usage: `srp server auto [enable|disable]` + +Enables or disables the auto-enable mode on the SRP server. + +When this mode is enabled, the Border Routing Manager controls if and when to enable or disable the SRP server. + +This command requires that `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` be enabled. + +```bash +> srp server auto enable +Done + +> srp server auto +Enabled +Done +``` + ### disable Usage: `srp server disable` diff --git a/src/cli/README_TCAT.md b/src/cli/README_TCAT.md new file mode 100644 index 000000000..066a57109 --- /dev/null +++ b/src/cli/README_TCAT.md @@ -0,0 +1,37 @@ +# OpenThread CLI - TCAT Example + +## Command List + +- help [#help] +- start [#start] +- stop [#stop] + +### help + +print help + +```bash +tcat help +help +start +stop +Done +``` + +### start + +Start tcat server and ble advertisement. + +```bash +tcat start +Done +``` + +### stop + +Stop tcat server and ble advertisement. + +```bash +tcat stop +Done +``` diff --git a/src/cli/cli.cpp b/src/cli/cli.cpp index cf9fea6ee..c1443aec7 100644 --- a/src/cli/cli.cpp +++ b/src/cli/cli.cpp @@ -148,6 +148,9 @@ Interpreter::Interpreter(Instance *aInstance, otCliOutputCallback aCallback, voi #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE , mLinkMetrics(aInstance, *this) #endif +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE + , mTcat(aInstance, *this) +#endif #if OPENTHREAD_CONFIG_TMF_ANYCAST_LOCATOR_ENABLE , mLocateInProgress(false) #endif @@ -7240,6 +7243,10 @@ template <> otError Interpreter::Process(Arg aArgs[]) return error; } +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE +template <> otError Interpreter::Process(Arg aArgs[]) { return mTcat.Process(aArgs); } +#endif + #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE template <> otError Interpreter::Process(Arg aArgs[]) { return mTcp.Process(aArgs); } #endif @@ -8509,6 +8516,9 @@ otError Interpreter::ProcessCommand(Arg aArgs[]) CmdEntry("srp"), #endif CmdEntry("state"), +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE + CmdEntry("tcat"), +#endif #if OPENTHREAD_CONFIG_TCP_ENABLE && OPENTHREAD_CONFIG_CLI_TCP_ENABLE CmdEntry("tcp"), #endif diff --git a/src/cli/cli.hpp b/src/cli/cli.hpp index 81c5fe7d0..f95759e71 100644 --- a/src/cli/cli.hpp +++ b/src/cli/cli.hpp @@ -71,6 +71,7 @@ #include "cli/cli_output.hpp" #include "cli/cli_srp_client.hpp" #include "cli/cli_srp_server.hpp" +#include "cli/cli_tcat.hpp" #include "cli/cli_tcp.hpp" #include "cli/cli_udp.hpp" #if OPENTHREAD_CONFIG_COAP_API_ENABLE @@ -594,6 +595,9 @@ class Interpreter : public OutputImplementer, public Output #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE LinkMetrics mLinkMetrics; #endif +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE + Tcat mTcat; +#endif #endif // OPENTHREAD_FTD || OPENTHREAD_MTD #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE diff --git a/src/cli/cli_config.h b/src/cli/cli_config.h index c3762c4a1..ac9b5e1cc 100644 --- a/src/cli/cli_config.h +++ b/src/cli/cli_config.h @@ -58,6 +58,16 @@ #define OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH 384 #endif +/** + * @def OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE + * + * Indicates whether TCAT should be enabled in the CLI tool. + * + */ +#ifndef OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE +#define OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE 1 +#endif + /** * @def OPENTHREAD_CONFIG_CLI_TCP_ENABLE * diff --git a/src/cli/cli_srp_client.cpp b/src/cli/cli_srp_client.cpp index a153059ca..58c9ef457 100644 --- a/src/cli/cli_srp_client.cpp +++ b/src/cli/cli_srp_client.cpp @@ -146,7 +146,7 @@ template <> otError SrpClient::Process(Arg aArgs[]) #endif // OPENTHREAD_CONFIG_SRP_CLIENT_AUTO_START_API_ENABLE /** - * @cli srp client callback (enable/disable, get) + * @cli srp client callback (get,enable,disable) * @code * srp client callback enable * Done @@ -196,7 +196,7 @@ template <> otError SrpClient::Process(Arg aArgs[]) OutputHostInfo(0, *otSrpClientGetHostInfo(GetInstancePtr())); } /** - * @cli srp client host name (set, get) + * @cli srp client host name (get,set) * @code * srp client host name dev4312 * Done @@ -210,7 +210,7 @@ template <> otError SrpClient::Process(Arg aArgs[]) * To set the client host name when the host has either been removed or not yet * registered with the server, use the `name` parameter. * @par - * Sets or returns the host name of the SRP client. + * Gets or sets the host name of the SRP client. * @sa otSrpClientSetHostName */ else if (aArgs[0] == "name") @@ -315,12 +315,12 @@ template <> otError SrpClient::Process(Arg aArgs[]) * @endcode * @cparam srp client host address [auto|@ca{address...}] * * Use the `auto` parameter to enable auto host address mode. - * When enabled, the client automatically uses all Thread `netif` - * unicast addresses except for link-local and mesh-local + * When enabled, the client automatically uses all preferred Thread + * `netif` unicast addresses except for link-local and mesh-local * addresses. If there is no valid address, the mesh local * EID address gets added. The SRP client automatically * re-registers if addresses on the Thread `netif` are - * added or removed. + * added or removed or marked as non-preferred. * * Explicitly specify the list of host addresses, separating * each address by a space. You can set this list while the client is * running. This will also disable auto host address mode. @@ -412,8 +412,8 @@ template <> otError SrpClient::Process(Arg aArgs[]) * @endcode * @par * Clears all host information and all services. - * @sa otSrpClientClearHostAndServices * @sa otSrpClientBuffersFreeAllServices + * @sa otSrpClientClearHostAndServices */ else if (aArgs[0] == "clear") { @@ -431,7 +431,7 @@ template <> otError SrpClient::Process(Arg aArgs[]) } /** - * @cli srp client leaseinterval (set, get) + * @cli srp client leaseinterval (get,set) * @code * srp client leaseinterval 3600 * Done @@ -443,9 +443,9 @@ template <> otError SrpClient::Process(Arg aArgs[]) * @endcode * @cparam srp client leaseinterval [@ca{interval}] * @par - * Sets or gets the lease interval in seconds. - * @sa otSrpClientSetLeaseInterval + * Gets or sets the lease interval in seconds. * @sa otSrpClientGetLeaseInterval + * @sa otSrpClientSetLeaseInterval */ template <> otError SrpClient::Process(Arg aArgs[]) { @@ -453,7 +453,7 @@ template <> otError SrpClient::Process(Arg aArgs[]) } /** - * @cli srp client keyleaseinterval (set, get) + * @cli srp client keyleaseinterval (get,set) * @code * srp client keyleaseinterval 864000 * Done @@ -465,9 +465,9 @@ template <> otError SrpClient::Process(Arg aArgs[]) * @endcode * @cparam srp client keyleaseinterval [@ca{interval}] * @par - * Sets or gets the key lease interval in seconds. - * @sa otSrpClientSetKeyLeaseInterval + * Gets or sets the key lease interval in seconds. * @sa otSrpClientGetKeyLeaseInterval + * @sa otSrpClientSetKeyLeaseInterval */ template <> otError SrpClient::Process(Arg aArgs[]) { @@ -639,7 +639,7 @@ template <> otError SrpClient::Process(Arg aArgs[]) } #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE /** - * @cli srp client service key (set, get) + * @cli srp client service key (get,set) * @code * srp client service key enable * Done @@ -905,7 +905,7 @@ template <> otError SrpClient::Process(Arg aArgs[]) } /** - * @cli srp client ttl (set, get) + * @cli srp client ttl (get,set) * @code * srp client ttl 3600 * Done @@ -917,9 +917,9 @@ template <> otError SrpClient::Process(Arg aArgs[]) * @endcode * @cparam srp client ttl [@ca{value}] * @par - * Sets or gets the `ttl`(time to live) value in seconds. - * @sa otSrpClientSetTtl + * Gets or sets the `ttl`(time to live) value in seconds. * @sa otSrpClientGetTtl + * @sa otSrpClientSetTtl */ template <> otError SrpClient::Process(Arg aArgs[]) { diff --git a/src/cli/cli_srp_server.cpp b/src/cli/cli_srp_server.cpp index 5a96d5fe6..98213d524 100644 --- a/src/cli/cli_srp_server.cpp +++ b/src/cli/cli_srp_server.cpp @@ -43,6 +43,26 @@ namespace ot { namespace Cli { +/** + * @cli srp server addrmode (get,set) + * @code + * srp server addrmode anycast + * Done + * @endcode + * @code + * srp server addrmode + * anycast + * Done + * @endcode + * @cparam srp server addrmode [@ca{anycast}|@ca{unicast}] + * @par + * Gets or sets the address mode used by the SRP server. + * @par + * The address mode tells the SRP server how to determine its address and port number, + * which then get published in the Thread network data. + * @sa otSrpServerGetAddressMode + * @sa otSrpServerSetAddressMode + */ template <> otError SrpServer::Process(Arg aArgs[]) { otError error = OT_ERROR_INVALID_ARGS; @@ -75,32 +95,53 @@ template <> otError SrpServer::Process(Arg aArgs[]) } #if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + /** - * @cli srp server auto + * @cli srp server auto (enable,disable) + * @code + * srp server auto enable + * Done + * @endcode * @code * srp server auto - * Disabled + * Enabled * Done * @endcode - * @par api_copy - * #otSrpServerIsAutoEnableMode + * @cparam srp server auto [@ca{enable}|@ca{disable}] + * @par + * Enables or disables the auto-enable mode on the SRP server. + * @par + * When this mode is enabled, the Border Routing Manager controls if and when + * to enable or disable the SRP server. + * @par + * This command requires that `OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE` be enabled. + * @sa otSrpServerIsAutoEnableMode + * @sa otSrpServerSetAutoEnableMode */ template <> otError SrpServer::Process(Arg aArgs[]) { - /** - * @cli srp server auto enable - * @code - * srp server auto enable - * Done - * @endcode - * @par api_copy - * #otSrpServerSetAutoEnableMode - */ return Interpreter::GetInterpreter().ProcessEnableDisable(aArgs, otSrpServerIsAutoEnableMode, otSrpServerSetAutoEnableMode); } #endif +/** + * @cli srp server domain (get,set) + * @code + * srp server domain thread.service.arpa. + * Done + * @endcode + * @code + * srp server domain + * thread.service.arpa. + * Done + * @endcode + * @cparam srp server domain [@ca{domain-name}] + * @par + * Gets or sets the domain name of the SRP server. + * @sa otSrpServerGetDomain + * @sa otSrpServerSetDomain + */ template <> otError SrpServer::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -117,6 +158,23 @@ template <> otError SrpServer::Process(Arg aArgs[]) return error; } +/** + * @cli srp server state + * @code + * srp server state + * running + * Done + * @endcode + * @par + * Returns one of the following possible states of the SRP server: + * * `disabled`: The SRP server is not enabled. + * * `stopped`: The SRP server is enabled but not active due to existing + * SRP servers that are already active in the Thread network. + * The SRP server may become active when the existing + * SRP servers are no longer active within the Thread network. + * * `running`: The SRP server is active and can handle service registrations. + * @sa otSrpServerGetState + */ template <> otError SrpServer::Process(Arg aArgs[]) { static const char *const kStateStrings[] = { @@ -145,6 +203,17 @@ template <> otError SrpServer::Process(Arg aArgs[]) return OT_ERROR_NONE; } +/** + * @cli srp server (enable,disable) + * @code + * srp server disable + * Done + * @endcode + * @cparam srp server [@ca{enable}|@ca{disable}] + * @par + * Enables or disables the SRP server. + * @sa otSrpServerSetEnabled + */ template <> otError SrpServer::Process(Arg aArgs[]) { OT_UNUSED_VARIABLE(aArgs); @@ -178,6 +247,26 @@ template <> otError SrpServer::Process(Arg aArgs[]) return error; } +/** + * @cli srp server lease (get,set) + * @code + * srp server lease 1800 7200 86400 1209600 + * Done + * @endcode + * @code + * srp server lease + * min lease: 1800 + * max lease: 7200 + * min key-lease: 86400 + * max key-lease: 1209600 + * Done + * @endcode + * @cparam srp server lease [@ca{min-lease} @ca{max-lease} @ca{min-key-lease} @ca{max-key-lease}] + * @par + * Gets or sets the SRP server lease values in number of seconds. + * @sa otSrpServerGetLeaseConfig + * @sa otSrpServerSetLeaseConfig + */ template <> otError SrpServer::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -206,6 +295,24 @@ template <> otError SrpServer::Process(Arg aArgs[]) return error; } +/** + * @cli srp server host + * @code + * srp server host + * srp-api-test-1.default.service.arpa. + * deleted: false + * addresses: [fdde:ad00:beef:0:0:ff:fe00:fc10] + * srp-api-test-0.default.service.arpa. + * deleted: false + * addresses: [fdde:ad00:beef:0:0:ff:fe00:fc10] + * Done + * @endcode + * @par + * Returns information about all registered hosts. + * @sa otSrpServerGetNextHost + * @sa otSrpServerHostGetAddresses + * @sa otSrpServerHostGetFullName + */ template <> otError SrpServer::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -268,6 +375,46 @@ void SrpServer::OutputHostAddresses(const otSrpServerHost *aHost) OutputFormat("]"); } +/** + * @cli srp server service + * @code + * srp server service + * srp-api-test-1._ipps._tcp.default.service.arpa. + * deleted: false + * subtypes: (null) + * port: 49152 + * priority: 0 + * weight: 0 + * 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. + * deleted: false + * subtypes: _sub1,_sub2 + * port: 49152 + * priority: 0 + * weight: 0 + * 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 + * @endcode + * @par + * Returns information about registered services. + * @par + * The `TXT` record is displayed + * as an array of entries. If an entry contains a key, the key is printed in + * ASCII format. The value portion is printed in hexadecimal bytes. + * @sa otSrpServerServiceGetInstanceName + * @sa otSrpServerServiceGetServiceName + * @sa otSrpServerServiceGetSubTypeServiceNameAt + */ template <> otError SrpServer::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; @@ -340,6 +487,25 @@ template <> otError SrpServer::Process(Arg aArgs[]) return error; } +/** + * @cli srp server seqnum (get,set) + * @code + * srp server seqnum 20 + * Done + * @endcode + * @code + * srp server seqnum + * 20 + * Done + * @endcode + * @cparam srp server seqnum [@ca{seqnum}] + * @par + * Gets or sets the sequence number used with the anycast address mode. + * The sequence number is included in the "DNS/SRP Service Anycast Address" + * entry that is published in the Network Data. + * @sa otSrpServerGetAnycastModeSequenceNumber + * @sa otSrpServerSetAnycastModeSequenceNumber + */ template <> otError SrpServer::Process(Arg aArgs[]) { otError error = OT_ERROR_NONE; diff --git a/src/cli/cli_tcat.cpp b/src/cli/cli_tcat.cpp new file mode 100644 index 000000000..e754bca93 --- /dev/null +++ b/src/cli/cli_tcat.cpp @@ -0,0 +1,178 @@ +/* + * 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 "openthread-core-config.h" + +#include "cli/cli_output.hpp" + +#include "cli/cli_tcat.hpp" + +#include + +#include +#include +#include + +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE + +#define OT_CLI_TCAT_X509_CERT \ + "-----BEGIN CERTIFICATE-----\r\n" \ + "MIIBmDCCAT+gAwIBAgIEAQIDBDAKBggqhkjOPQQDAjBvMQswCQYDVQQGEwJYWDEQ\r\n" \ + "MA4GA1UECBMHTXlTdGF0ZTEPMA0GA1UEBxMGTXlDaXR5MQ8wDQYDVQQLEwZNeVVu\r\n" \ + "aXQxETAPBgNVBAoTCE15VmVuZG9yMRkwFwYDVQQDExB3d3cubXl2ZW5kb3IuY29t\r\n" \ + "MB4XDTIzMTAxNjEwMzk1NFoXDTI0MTAxNjEwMzk1NFowIjEgMB4GA1UEAxMXbXl2\r\n" \ + "ZW5kb3IuY29tL3RjYXQvbXlkZXYwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQB\r\n" \ + "aWwFDNj1bpQIdN+Kp2cHWw55U/+fa+OmZnoy1B4BOT+822jdwPBuyXWAQoBdYdQJ\r\n" \ + "ff4RgmhczyV4PhArPIuAoxYwFDASBgkrBgEEAYLfKgMEBQABAQEBMAoGCCqGSM49\r\n" \ + "BAMCA0cAMEQCIBEHxiEDij26y6V77Q311Gj4CZAuZuPGXZpnzL2BLk7bAiAlFk6G\r\n" \ + "mYGzkcrYyssFI9HlPgrisWoMmgummaTtCuvrEw==\r\n" \ + "-----END CERTIFICATE-----\r\n" + +#define OT_CLI_TCAT_PRIV_KEY \ + "-----BEGIN EC PRIVATE KEY-----\r\n" \ + "MHcCAQEEIDeJ6lVQKiOIBxKwTZp6TkU5QVHt9pvXOR9CGpPBI3DhoAoGCCqGSM49\r\n" \ + "AwEHoUQDQgAEAWlsBQzY9W6UCHTfiqdnB1sOeVP/n2vjpmZ6MtQeATk/vNto3cDw\r\n" \ + "bsl1gEKAXWHUCX3+EYJoXM8leD4QKzyLgA==\r\n" \ + "-----END EC PRIVATE KEY-----\r\n" + +#define OT_CLI_TCAT_TRUSTED_ROOT_CERTIFICATE \ + "-----BEGIN CERTIFICATE-----\r\n" \ + "MIICCDCCAa2gAwIBAgIJAIKxygBXoH+5MAoGCCqGSM49BAMCMG8xCzAJBgNVBAYT\r\n" \ + "AlhYMRAwDgYDVQQIEwdNeVN0YXRlMQ8wDQYDVQQHEwZNeUNpdHkxDzANBgNVBAsT\r\n" \ + "Bk15VW5pdDERMA8GA1UEChMITXlWZW5kb3IxGTAXBgNVBAMTEHd3dy5teXZlbmRv\r\n" \ + "ci5jb20wHhcNMjMxMDE2MTAzMzE1WhcNMjYxMDE2MTAzMzE1WjBvMQswCQYDVQQG\r\n" \ + "EwJYWDEQMA4GA1UECBMHTXlTdGF0ZTEPMA0GA1UEBxMGTXlDaXR5MQ8wDQYDVQQL\r\n" \ + "EwZNeVVuaXQxETAPBgNVBAoTCE15VmVuZG9yMRkwFwYDVQQDExB3d3cubXl2ZW5k\r\n" \ + "b3IuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWdyzPAXGKeZY94OhHAWX\r\n" \ + "HzJfQIjGSyaOzlgL9OEFw2SoUDncLKPGwfPAUSfuMyEkzszNDM0HHkBsDLqu4n25\r\n" \ + "/6MyMDAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU4EynoSw9eDKZEVPkums2\r\n" \ + "IWLAJCowCgYIKoZIzj0EAwIDSQAwRgIhAMYGGL9xShyE6P9wEU+MAYF6W3CzdrwV\r\n" \ + "kuerX1encIH2AiEA5rq490NUobM1Au43roxJq1T6Z43LscPVbGZfULD1Jq0=\r\n" \ + "-----END CERTIFICATE-----\r\n" + +namespace ot { + +namespace Cli { + +otTcatVendorInfo sVendorInfo; +const char kPskdVendor[] = "J01NM3"; +const char kUrl[] = "dummy_url"; + +static void HandleBleSecureReceive(otInstance *aInstance, + const otMessage *aMessage, + int32_t aOffset, + otTcatApplicationProtocol aTcatApplicationProtocol, + const char *aServiceName, + void *aContext) +{ + OT_UNUSED_VARIABLE(aContext); + OT_UNUSED_VARIABLE(aTcatApplicationProtocol); + OT_UNUSED_VARIABLE(aServiceName); + static constexpr int kTextMaxLen = 100; + static constexpr uint8_t kBufPrefixLen = 5; + uint16_t nLen; + uint8_t buf[kTextMaxLen]; + + nLen = otMessageRead(aMessage, (uint16_t)aOffset, buf + kBufPrefixLen, sizeof(buf) - kBufPrefixLen - 1); + + memcpy(buf, "RECV:", kBufPrefixLen); + + buf[nLen + kBufPrefixLen] = 0; + + IgnoreReturnValue(otBleSecureSendApplicationTlv(aInstance, buf, (uint16_t)strlen((char *)buf))); + IgnoreReturnValue(otBleSecureFlush(aInstance)); +} + +template <> otError Tcat::Process(Arg aArgs[]) +{ + OT_UNUSED_VARIABLE(aArgs); + + otError error = OT_ERROR_NONE; + + sVendorInfo.mPskdString = kPskdVendor; + sVendorInfo.mProvisioningUrl = kUrl; + + otBleSecureSetCertificate(GetInstancePtr(), reinterpret_cast(OT_CLI_TCAT_X509_CERT), + sizeof(OT_CLI_TCAT_X509_CERT), reinterpret_cast(OT_CLI_TCAT_PRIV_KEY), + sizeof(OT_CLI_TCAT_PRIV_KEY)); + + otBleSecureSetCaCertificateChain(GetInstancePtr(), + reinterpret_cast(OT_CLI_TCAT_TRUSTED_ROOT_CERTIFICATE), + sizeof(OT_CLI_TCAT_TRUSTED_ROOT_CERTIFICATE)); + + otBleSecureSetSslAuthMode(GetInstancePtr(), true); + + SuccessOrExit(error = otBleSecureStart(GetInstancePtr(), nullptr, HandleBleSecureReceive, true, nullptr)); + SuccessOrExit(error = otBleSecureTcatStart(GetInstancePtr(), &sVendorInfo, nullptr)); + +exit: + return error; +} + +template <> otError Tcat::Process(Arg aArgs[]) +{ + OT_UNUSED_VARIABLE(aArgs); + otError error = OT_ERROR_NONE; + + otBleSecureStop(GetInstancePtr()); + + return error; +} + +otError Tcat::Process(Arg aArgs[]) +{ +#define CmdEntry(aCommandString) \ + { \ + aCommandString, &Tcat::Process \ + } + + static constexpr Command kCommands[] = {CmdEntry("start"), CmdEntry("stop")}; + + static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted"); + + otError error = OT_ERROR_NONE; + 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_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE diff --git a/src/cli/cli_tcat.hpp b/src/cli/cli_tcat.hpp new file mode 100644 index 000000000..3f1d0be6e --- /dev/null +++ b/src/cli/cli_tcat.hpp @@ -0,0 +1,89 @@ +/* + * 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 CLI_TCAT_HPP_ +#define CLI_TCAT_HPP_ + +#include "openthread-core-config.h" + +#include "cli/cli_output.hpp" + +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE + +namespace ot { + +namespace Cli { + +/** + * Implements the Tcat CLI interpreter. + * + */ +class Tcat : private Output +{ +public: + typedef Utils::CmdLineParser::Arg Arg; + + /** + * Constructor + * + * @param[in] aInstance The OpenThread Instance. + * @param[in] aOutputImplementer An `OutputImplementer`. + * + */ + Tcat(otInstance *aInstance, OutputImplementer &aOutputImplementer) + : Output(aInstance, aOutputImplementer) + { + } + + /** + * Processes a CLI sub-command. + * + * @param[in] aArgs An array of command line arguments. + * + * @retval OT_ERROR_NONE Successfully executed the CLI command. + * @retval OT_ERROR_PENDING The CLI command was successfully started but final result is pending. + * @retval OT_ERROR_INVALID_COMMAND Invalid or unknown CLI command. + * @retval OT_ERROR_INVALID_ARGS Invalid arguments. + * @retval ... Error during execution of the CLI command. + * + */ + otError Process(Arg aArgs[]); + +private: + using Command = CommandEntry; + + template otError Process(Arg aArgs[]); +}; + +} // namespace Cli + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE + +#endif // CLI_TCAT_HPP_ diff --git a/src/core/BUILD.gn b/src/core/BUILD.gn index 37843dd2e..5263637bd 100644 --- a/src/core/BUILD.gn +++ b/src/core/BUILD.gn @@ -74,6 +74,10 @@ if (openthread_enable_core_config_args) { defines += [ "OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE=1" ] } + if (openthread_config_ble_tcat_enable) { + defines += [ "OPENTHREAD_CONFIG_BLE_TCAT_ENABLE=1" ] + } + if (openthread_config_border_agent_enable) { defines += [ "OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE=1" ] } @@ -301,6 +305,7 @@ config("core_config") { openthread_core_files = [ "api/backbone_router_api.cpp", "api/backbone_router_ftd_api.cpp", + "api/ble_secure_api.cpp", "api/border_agent_api.cpp", "api/border_router_api.cpp", "api/border_routing_api.cpp", @@ -510,8 +515,6 @@ openthread_core_files = [ "meshcop/dataset_manager_ftd.cpp", "meshcop/dataset_updater.cpp", "meshcop/dataset_updater.hpp", - "meshcop/dtls.cpp", - "meshcop/dtls.hpp", "meshcop/energy_scan_client.cpp", "meshcop/energy_scan_client.hpp", "meshcop/extended_panid.cpp", @@ -530,6 +533,10 @@ openthread_core_files = [ "meshcop/network_name.hpp", "meshcop/panid_query_client.cpp", "meshcop/panid_query_client.hpp", + "meshcop/secure_transport.cpp", + "meshcop/secure_transport.hpp", + "meshcop/tcat_agent.cpp", + "meshcop/tcat_agent.hpp", "meshcop/timestamp.cpp", "meshcop/timestamp.hpp", "net/checksum.cpp", @@ -585,6 +592,8 @@ openthread_core_files = [ "net/tcp6_ext.hpp", "net/udp6.cpp", "net/udp6.hpp", + "radio/ble_secure.cpp", + "radio/ble_secure.hpp", "radio/max_power_table.hpp", "radio/radio.cpp", "radio/radio.hpp", @@ -789,7 +798,6 @@ source_set("libopenthread_core_config") { "config/dns_client.h", "config/dns_dso.h", "config/dnssd_server.h", - "config/dtls.h", "config/history_tracker.h", "config/ip6.h", "config/joiner.h", @@ -811,6 +819,7 @@ source_set("libopenthread_core_config") { "config/platform.h", "config/power_calibration.h", "config/radio_link.h", + "config/secure_transport.h", "config/sntp_client.h", "config/srp_client.h", "config/srp_server.h", diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 813979ad7..24ceb2271 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -33,6 +33,7 @@ set(COMMON_INCLUDES set(COMMON_SOURCES api/backbone_router_api.cpp api/backbone_router_ftd_api.cpp + api/ble_secure_api.cpp api/border_agent_api.cpp api/border_router_api.cpp api/border_routing_api.cpp @@ -148,7 +149,6 @@ set(COMMON_SOURCES meshcop/dataset_manager.cpp meshcop/dataset_manager_ftd.cpp meshcop/dataset_updater.cpp - meshcop/dtls.cpp meshcop/energy_scan_client.cpp meshcop/extended_panid.cpp meshcop/joiner.cpp @@ -158,6 +158,8 @@ set(COMMON_SOURCES meshcop/meshcop_tlvs.cpp meshcop/network_name.cpp meshcop/panid_query_client.cpp + meshcop/secure_transport.cpp + meshcop/tcat_agent.cpp meshcop/timestamp.cpp net/checksum.cpp net/dhcp6_client.cpp @@ -185,6 +187,7 @@ set(COMMON_SOURCES net/tcp6.cpp net/tcp6_ext.cpp net/udp6.cpp + radio/ble_secure.cpp radio/radio.cpp radio/radio_callbacks.cpp radio/radio_platform.cpp diff --git a/src/core/api/ble_secure_api.cpp b/src/core/api/ble_secure_api.cpp new file mode 100644 index 000000000..d30a7f0aa --- /dev/null +++ b/src/core/api/ble_secure_api.cpp @@ -0,0 +1,184 @@ +/* + * 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 OpenThread BLE Secure API. + */ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE + +#include +#include + +#include "common/as_core_type.hpp" +#include "common/code_utils.hpp" +#include "common/locator_getters.hpp" +#include "meshcop/tcat_agent.hpp" +#include "radio/ble_secure.hpp" + +using namespace ot; + +otError otBleSecureStart(otInstance *aInstance, + otHandleBleSecureConnect aConnectHandler, + otHandleBleSecureReceive aReceiveHandler, + bool aTlvMode, + void *aContext) +{ + return AsCoreType(aInstance).Get().Start(aConnectHandler, aReceiveHandler, aTlvMode, aContext); +} + +otError otBleSecureTcatStart(otInstance *aInstance, const otTcatVendorInfo *aVendorInfo, otHandleTcatJoin aHandler) +{ + return AsCoreType(aInstance).Get().TcatStart(AsCoreType(aVendorInfo), aHandler); +} + +void otBleSecureStop(otInstance *aInstance) { AsCoreType(aInstance).Get().Stop(); } + +#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED +void otBleSecureSetPsk(otInstance *aInstance, + const uint8_t *aPsk, + uint16_t aPskLength, + const uint8_t *aPskIdentity, + uint16_t aPskIdLength) +{ + AssertPointerIsNotNull(aPsk); + AssertPointerIsNotNull(aPskIdentity); + OT_ASSERT(aPskLength != 0 && aPskIdLength != 0); + + AsCoreType(aInstance).Get().SetPreSharedKey(aPsk, aPskLength, aPskIdentity, aPskIdLength); +} +#endif // MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + +#if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +otError otBleSecureGetPeerCertificateBase64(otInstance *aInstance, unsigned char *aPeerCert, size_t *aCertLength) +{ + return AsCoreType(aInstance).Get().GetPeerCertificateBase64(aPeerCert, aCertLength); +} +#endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +otError otBleSecureGetPeerSubjectAttributeByOid(otInstance *aInstance, + const char *aOid, + size_t aOidLength, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength, + int *aAsn1Type) +{ + return AsCoreType(aInstance).Get().GetPeerSubjectAttributeByOid(aOid, aOidLength, aAttributeBuffer, + aAttributeLength, aAsn1Type); +} + +otError otBleSecureGetThreadAttributeFromPeerCertificate(otInstance *aInstance, + int aThreadOidDescriptor, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength) +{ + return AsCoreType(aInstance).Get().GetThreadAttributeFromPeerCertificate( + aThreadOidDescriptor, aAttributeBuffer, aAttributeLength); +} +#endif // defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + +otError otBleSecureGetThreadAttributeFromOwnCertificate(otInstance *aInstance, + int aThreadOidDescriptor, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength) +{ + return AsCoreType(aInstance).Get().GetThreadAttributeFromOwnCertificate( + aThreadOidDescriptor, aAttributeBuffer, aAttributeLength); +} + +void otBleSecureSetSslAuthMode(otInstance *aInstance, bool aVerifyPeerCertificate) +{ + AsCoreType(aInstance).Get().SetSslAuthMode(aVerifyPeerCertificate); +} + +#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED +void otBleSecureSetCertificate(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).Get().SetCertificate(aX509Cert, aX509Length, aPrivateKey, aPrivateKeyLength); +} + +void otBleSecureSetCaCertificateChain(otInstance *aInstance, + const uint8_t *aX509CaCertificateChain, + uint32_t aX509CaCertChainLength) +{ + OT_ASSERT(aX509CaCertificateChain != nullptr && aX509CaCertChainLength != 0); + + AsCoreType(aInstance).Get().SetCaCertificateChain(aX509CaCertificateChain, aX509CaCertChainLength); +} +#endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + +otError otBleSecureConnect(otInstance *aInstance) { return AsCoreType(aInstance).Get().Connect(); } + +void otBleSecureDisconnect(otInstance *aInstance) { AsCoreType(aInstance).Get().Disconnect(); } + +bool otBleSecureIsConnectionActive(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().IsConnectionActive(); +} + +bool otBleSecureIsConnected(otInstance *aInstance) { return AsCoreType(aInstance).Get().IsConnected(); } + +bool otBleSecureIsTcatEnabled(otInstance *aInstance) +{ + return AsCoreType(aInstance).Get().IsTcatEnabled(); +} + +bool otBleSecureIsCommandClassAuthorized(otInstance *aInstance, otTcatCommandClass aCommandClass) +{ + return AsCoreType(aInstance).Get().IsCommandClassAuthorized( + static_cast(aCommandClass)); +} + +otError otBleSecureSendMessage(otInstance *aInstance, otMessage *aMessage) +{ + return AsCoreType(aInstance).Get().SendMessage(AsCoreType(aMessage)); +} + +otError otBleSecureSend(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength) +{ + return AsCoreType(aInstance).Get().Send(aBuf, aLength); +} + +otError otBleSecureSendApplicationTlv(otInstance *aInstance, uint8_t *aBuf, uint16_t aLength) +{ + return AsCoreType(aInstance).Get().SendApplicationTlv(aBuf, aLength); +} + +otError otBleSecureFlush(otInstance *aInstance) { return AsCoreType(aInstance).Get().Flush(); } + +#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE diff --git a/src/core/api/border_routing_api.cpp b/src/core/api/border_routing_api.cpp index 9fbfbcb4b..b62ee7041 100644 --- a/src/core/api/border_routing_api.cpp +++ b/src/core/api/border_routing_api.cpp @@ -104,6 +104,13 @@ otError otBorderRoutingGetPdOmrPrefix(otInstance *aInstance, otBorderRoutingPref return AsCoreType(aInstance).Get().GetPdOmrPrefix(*aPrefixInfo); } + +otError otBorderRoutingGetPdProcessedRaInfo(otInstance *aInstance, otPdProcessedRaInfo *aPdProcessedRaInfo) +{ + AssertPointerIsNotNull(aPdProcessedRaInfo); + + return AsCoreType(aInstance).Get().GetPdProcessedRaInfo(*aPdProcessedRaInfo); +} #endif otError otBorderRoutingGetFavoredOmrPrefix(otInstance *aInstance, otIp6Prefix *aPrefix, otRoutePreference *aPreference) diff --git a/src/core/api/instance_api.cpp b/src/core/api/instance_api.cpp index 6d51d3c39..5d6d5971e 100644 --- a/src/core/api/instance_api.cpp +++ b/src/core/api/instance_api.cpp @@ -58,6 +58,16 @@ using namespace ot; #if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE +#if OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE +otInstance *otInstanceInitMultiple(uint8_t aIdx) +{ + Instance *instance; + + instance = Instance::InitMultiple(aIdx); + + return instance; +} +#endif // OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE otInstance *otInstanceInit(void *aInstanceBuffer, size_t *aInstanceBufferSize) { Instance *instance; diff --git a/src/core/api/ip6_api.cpp b/src/core/api/ip6_api.cpp index d70557069..5ca4f50d9 100644 --- a/src/core/api/ip6_api.cpp +++ b/src/core/api/ip6_api.cpp @@ -249,8 +249,8 @@ otError otIp6RegisterMulticastListeners(otInstance * otIp6RegisterMulticastListenersCallback aCallback, void *aContext) { - return AsCoreType(aInstance).Get().RegisterMulticastListeners(aAddresses, aAddressNum, aTimeout, - aCallback, aContext); + return AsCoreType(aInstance).Get().RegisterMulticastListeners(AsCoreTypePtr(aAddresses), aAddressNum, + aTimeout, aCallback, aContext); } #endif diff --git a/src/core/backbone_router/bbr_leader.hpp b/src/core/backbone_router/bbr_leader.hpp index 465765d45..4102651f2 100644 --- a/src/core/backbone_router/bbr_leader.hpp +++ b/src/core/backbone_router/bbr_leader.hpp @@ -39,6 +39,7 @@ #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) #include +#include #include #include "coap/coap.hpp" @@ -72,10 +73,10 @@ static_assert(kParentAggregateDelay > 1, "kParentAggregateDelay should be larger */ enum DomainPrefixEvent : uint8_t { - kDomainPrefixAdded, ///< Domain Prefix Added. - kDomainPrefixRemoved, ///< Domain Prefix Removed. - kDomainPrefixRefreshed, ///< Domain Prefix Changed. - kDomainPrefixUnchanged, ///< Domain Prefix did not change. + kDomainPrefixAdded = OT_BACKBONE_ROUTER_DOMAIN_PREFIX_ADDED, ///< Domain Prefix Added. + kDomainPrefixRemoved = OT_BACKBONE_ROUTER_DOMAIN_PREFIX_REMOVED, ///< Domain Prefix Removed. + kDomainPrefixRefreshed = OT_BACKBONE_ROUTER_DOMAIN_PREFIX_CHANGED, ///< Domain Prefix Changed. + kDomainPrefixUnchanged, ///< Domain Prefix did not change. }; /** diff --git a/src/core/backbone_router/bbr_local.cpp b/src/core/backbone_router/bbr_local.cpp index ffdc4757b..66cb8f519 100644 --- a/src/core/backbone_router/bbr_local.cpp +++ b/src/core/backbone_router/bbr_local.cpp @@ -51,19 +51,19 @@ RegisterLogModule("BbrLocal"); Local::Local(Instance &aInstance) : InstanceLocator(aInstance) + , mIsServiceAdded(false) , mState(kStateDisabled) - , mMlrTimeout(kDefaultMlrTimeout) - , mReregistrationDelay(kDefaultRegistrationDelay) - , mRegistrationTimeout(0) , mSequenceNumber(Random::NonCrypto::GetUint8() % 127) , mRegistrationJitter(kDefaultRegistrationJitter) - , mIsServiceAdded(false) + , mReregistrationDelay(kDefaultRegistrationDelay) + , mRegistrationTimeout(0) + , mMlrTimeout(kDefaultMlrTimeout) { mDomainPrefixConfig.GetPrefix().SetLength(0); // Primary Backbone Router Aloc - mBackboneRouterPrimaryAloc.InitAsThreadOriginMeshLocal(); - mBackboneRouterPrimaryAloc.GetAddress().GetIid().SetToLocator(Mle::kAloc16BackboneRouterPrimary); + mBbrPrimaryAloc.InitAsThreadOriginMeshLocal(); + mBbrPrimaryAloc.GetAddress().GetIid().SetToLocator(Mle::kAloc16BackboneRouterPrimary); // All Network Backbone Routers Multicast Address. mAllNetworkBackboneRouters.Clear(); @@ -110,7 +110,7 @@ void Local::Reset(void) if (mState == kStatePrimary) { // Increase sequence number when changing from Primary to Secondary. - SequenceNumberIncrease(); + IncrementSequenceNumber(); Get().Signal(kEventThreadBackboneRouterLocalChanged); SetState(kStateSecondary); } @@ -168,7 +168,7 @@ Error Local::SetConfig(const Config &aConfig) } exit: - LogBackboneRouterService("Set", error); + LogService(kActionSet, error); return error; } @@ -199,7 +199,7 @@ Error Local::AddService(RegisterMode aMode) mIsServiceAdded = true; exit: - LogBackboneRouterService("Add", error); + LogService(kActionAdd, error); return error; } @@ -212,28 +212,31 @@ void Local::RemoveService(void) mIsServiceAdded = false; exit: - LogBackboneRouterService("Remove", error); + LogService(kActionRemove, error); } void Local::SetState(State aState) { VerifyOrExit(mState != aState); - if (mState == kStateDisabled) + switch (mState) { + case kStateDisabled: // Update All Network Backbone Routers Multicast Address for both Secondary and Primary state. mAllNetworkBackboneRouters.SetMulticastNetworkPrefix(Get().GetMeshLocalPrefix()); + break; + case kStateSecondary: + break; + case kStatePrimary: + Get().RemoveUnicastAddress(mBbrPrimaryAloc); + break; } - if (mState == kStatePrimary) + if (aState == kStatePrimary) { - Get().RemoveUnicastAddress(mBackboneRouterPrimaryAloc); - } - else if (aState == kStatePrimary) - { - // Add Primary Backbone Router Aloc for Primary Backbone Router. - mBackboneRouterPrimaryAloc.GetAddress().SetPrefix(Get().GetMeshLocalPrefix()); - Get().AddUnicastAddress(mBackboneRouterPrimaryAloc); + // Add Primary Backbone Router ALOC for Primary Backbone Router. + mBbrPrimaryAloc.GetAddress().SetPrefix(Get().GetMeshLocalPrefix()); + Get().AddUnicastAddress(mBbrPrimaryAloc); } mState = aState; @@ -274,7 +277,7 @@ void Local::HandleBackboneRouterPrimaryUpdate(Leader::State aState, const Config mSequenceNumber = aConfig.mSequenceNumber; mReregistrationDelay = aConfig.mReregistrationDelay; mMlrTimeout = aConfig.mMlrTimeout; - SequenceNumberIncrease(); + IncrementSequenceNumber(); Get().Signal(kEventThreadBackboneRouterLocalChanged); IgnoreError(AddService(kForceRegistration)); } @@ -353,7 +356,7 @@ Error Local::SetDomainPrefix(const NetworkData::OnMeshPrefixConfig &aConfig) } mDomainPrefixConfig = aConfig; - LogDomainPrefix("Set", kErrorNone); + LogDomainPrefix(kActionSet, kErrorNone); if (IsEnabled()) { @@ -394,22 +397,10 @@ void Local::HandleDomainPrefixUpdate(DomainPrefixEvent aEvent) Get().SubscribeMulticast(mAllDomainBackboneRouters); } - if (mDomainPrefixCallback.IsSet()) + if (aEvent != kDomainPrefixUnchanged) { - switch (aEvent) - { - case kDomainPrefixAdded: - mDomainPrefixCallback.Invoke(OT_BACKBONE_ROUTER_DOMAIN_PREFIX_ADDED, Get().GetDomainPrefix()); - break; - case kDomainPrefixRemoved: - mDomainPrefixCallback.Invoke(OT_BACKBONE_ROUTER_DOMAIN_PREFIX_REMOVED, Get().GetDomainPrefix()); - break; - case kDomainPrefixRefreshed: - mDomainPrefixCallback.Invoke(OT_BACKBONE_ROUTER_DOMAIN_PREFIX_CHANGED, Get().GetDomainPrefix()); - break; - default: - break; - } + mDomainPrefixCallback.InvokeIfSet(static_cast(aEvent), + Get().GetDomainPrefix()); } exit: @@ -425,10 +416,10 @@ void Local::RemoveDomainPrefixFromNetworkData(void) error = Get().RemoveOnMeshPrefix(mDomainPrefixConfig.GetPrefix()); } - LogDomainPrefix("Remove", error); + LogDomainPrefix(kActionRemove, error); } -void Local::SequenceNumberIncrease(void) +void Local::IncrementSequenceNumber(void) { switch (mSequenceNumber) { @@ -455,21 +446,38 @@ void Local::AddDomainPrefixToNetworkData(void) error = Get().AddOnMeshPrefix(mDomainPrefixConfig); } - LogDomainPrefix("Add", error); + LogDomainPrefix(kActionAdd, error); } #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) -void Local::LogDomainPrefix(const char *aAction, Error aError) + +const char *Local::ActionToString(Action aAction) { - LogInfo("%s Domain Prefix: %s, %s", aAction, mDomainPrefixConfig.GetPrefix().ToString().AsCString(), + static const char *const kActionStrings[] = { + "Set", // (0) kActionSet + "Add", // (1) kActionAdd + "Remove", // (2) kActionRemove + }; + + static_assert(0 == kActionSet, "kActionSet value is incorrect"); + static_assert(1 == kActionAdd, "kActionAdd value is incorrect"); + static_assert(2 == kActionRemove, "kActionRemove value is incorrect"); + + return kActionStrings[aAction]; +} + +void Local::LogDomainPrefix(Action aAction, Error aError) +{ + LogInfo("%s Domain Prefix: %s, %s", ActionToString(aAction), mDomainPrefixConfig.GetPrefix().ToString().AsCString(), ErrorToString(aError)); } -void Local::LogBackboneRouterService(const char *aAction, Error aError) +void Local::LogService(Action aAction, Error aError) { - LogInfo("%s BBR Service: seqno (%u), delay (%us), timeout (%lus), %s", aAction, mSequenceNumber, + LogInfo("%s BBR Service: seqno (%u), delay (%us), timeout (%lus), %s", ActionToString(aAction), mSequenceNumber, mReregistrationDelay, ToUlong(mMlrTimeout), ErrorToString(aError)); } + #endif } // namespace BackboneRouter diff --git a/src/core/backbone_router/bbr_local.hpp b/src/core/backbone_router/bbr_local.hpp index 90b2d894d..754f5a2a1 100644 --- a/src/core/backbone_router/bbr_local.hpp +++ b/src/core/backbone_router/bbr_local.hpp @@ -76,6 +76,8 @@ class Local : public InstanceLocator, private NonCopyable friend class ot::TimeTicker; public: + typedef otBackboneRouterDomainPrefixCallback DomainPrefixCallback; ///< Domain Prefix callback. + /** * Represents Backbone Router state. * @@ -273,44 +275,49 @@ class Local : public InstanceLocator, private NonCopyable * @param[in] aContext A user context pointer. * */ - void SetDomainPrefixCallback(otBackboneRouterDomainPrefixCallback aCallback, void *aContext) + void SetDomainPrefixCallback(DomainPrefixCallback aCallback, void *aContext) { mDomainPrefixCallback.Set(aCallback, aContext); } private: + enum Action : uint8_t + { + kActionSet, + kActionAdd, + kActionRemove, + }; + void SetState(State aState); void RemoveService(void); void HandleTimeTick(void); void AddDomainPrefixToNetworkData(void); void RemoveDomainPrefixFromNetworkData(void); - void SequenceNumberIncrease(void); + void IncrementSequenceNumber(void); #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_INFO) - void LogBackboneRouterService(const char *aAction, Error aError); - void LogDomainPrefix(const char *aAction, Error aError); + static const char *ActionToString(Action aAction); + void LogService(Action aAction, Error aError); + void LogDomainPrefix(Action aAction, Error aError); #else - void LogBackboneRouterService(const char *, Error) {} - void LogDomainPrefix(const char *, Error) {} + void LogService(Action, Error) {} + void LogDomainPrefix(Action, Error) {} #endif - State mState; - uint32_t mMlrTimeout; - uint16_t mReregistrationDelay; - uint16_t mRegistrationTimeout; - 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 // Backbone Router service for Secondary Backbone Router if it was added by force. - bool mIsServiceAdded; - + bool mIsServiceAdded; + State mState; + uint8_t mSequenceNumber; + uint8_t mRegistrationJitter; + uint16_t mReregistrationDelay; + uint16_t mRegistrationTimeout; + uint32_t mMlrTimeout; NetworkData::OnMeshPrefixConfig mDomainPrefixConfig; - - Ip6::Netif::UnicastAddress mBackboneRouterPrimaryAloc; - Ip6::Address mAllNetworkBackboneRouters; - Ip6::Address mAllDomainBackboneRouters; - Callback mDomainPrefixCallback; + Ip6::Netif::UnicastAddress mBbrPrimaryAloc; + Ip6::Address mAllNetworkBackboneRouters; + Ip6::Address mAllDomainBackboneRouters; + Callback mDomainPrefixCallback; }; } // namespace BackboneRouter diff --git a/src/core/border_router/routing_manager.cpp b/src/core/border_router/routing_manager.cpp index 7e8a08947..e981af508 100644 --- a/src/core/border_router/routing_manager.cpp +++ b/src/core/border_router/routing_manager.cpp @@ -211,6 +211,17 @@ Error RoutingManager::GetPdOmrPrefix(PrefixTableEntry &aPrefixInfo) const VerifyOrExit(IsInitialized(), error = kErrorInvalidState); error = mPdPrefixManager.GetPrefixInfo(aPrefixInfo); +exit: + return error; +} + +Error RoutingManager::GetPdProcessedRaInfo(PdProcessedRaInfo &aPdProcessedRaInfo) +{ + Error error = kErrorNone; + + VerifyOrExit(IsInitialized(), error = kErrorInvalidState); + error = mPdPrefixManager.GetProcessedRaInfo(aPdProcessedRaInfo); + exit: return error; } @@ -342,6 +353,9 @@ void RoutingManager::Start(void) mOmrPrefixManager.Start(); mRoutePublisher.Start(); mRsSender.Start(); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE + mPdPrefixManager.Start(); +#endif #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE mNat64PrefixManager.Start(); #endif @@ -354,7 +368,9 @@ void RoutingManager::Stop(void) mOmrPrefixManager.Stop(); mOnLinkPrefixManager.Stop(); - +#if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE + mPdPrefixManager.Stop(); +#endif #if OPENTHREAD_CONFIG_NAT64_BORDER_ROUTING_ENABLE mNat64PrefixManager.Stop(); #endif @@ -3429,14 +3445,79 @@ void RoutingManager::RsSender::HandleTimer(void) } #if OPENTHREAD_CONFIG_BORDER_ROUTING_DHCP6_PD_ENABLE +const char *RoutingManager::PdPrefixManager::StateToString(Dhcp6PdState aState) +{ + static const char *const kStateStrings[] = { + "Disabled", // (0) kDisabled + "Stopped", // (1) kStopped + "Running", // (2) kRunning + }; + + static_assert(0 == kDhcp6PdStateDisabled, "kDhcp6PdStateDisabled value is incorrect"); + static_assert(1 == kDhcp6PdStateStopped, "kDhcp6PdStateStopped value is incorrect"); + static_assert(2 == kDhcp6PdStateRunning, "kDhcp6PdStateRunning value is incorrect"); + + return kStateStrings[aState]; +} + RoutingManager::PdPrefixManager::PdPrefixManager(Instance &aInstance) : InstanceLocator(aInstance) , mEnabled(false) + , mIsRunning(false) + , mNumPlatformPioProcessed(0) + , mNumPlatformRaReceived(0) + , mLastPlatformRaTime(0) , mTimer(aInstance) { mPrefix.Clear(); } +void RoutingManager::PdPrefixManager::StartStop(bool aStart) +{ + Dhcp6PdState oldState = GetState(); + + VerifyOrExit(aStart != mIsRunning); + mIsRunning = aStart; + EvaluateStateChange(oldState); + +exit: + return; +} + +RoutingManager::Dhcp6PdState RoutingManager::PdPrefixManager::GetState(void) const +{ + Dhcp6PdState state = kDhcp6PdStateDisabled; + + if (mEnabled) + { + state = mIsRunning ? kDhcp6PdStateRunning : kDhcp6PdStateStopped; + } + + return state; +} + +void RoutingManager::PdPrefixManager::EvaluateStateChange(Dhcp6PdState aOldState) +{ + Dhcp6PdState newState = GetState(); + + VerifyOrExit(aOldState != newState); + LogInfo("PdPrefixManager: %s -> %s", StateToString(aOldState), StateToString(newState)); + + // TODO: We may also want to inform the platform that PD is stopped. + switch (newState) + { + case kDhcp6PdStateDisabled: + case kDhcp6PdStateStopped: + WithdrawPrefix(); + break; + case kDhcp6PdStateRunning: + break; + } + +exit: + return; +} + Error RoutingManager::PdPrefixManager::GetPrefixInfo(PrefixTableEntry &aInfo) const { Error error = kErrorNone; @@ -3452,6 +3533,20 @@ Error RoutingManager::PdPrefixManager::GetPrefixInfo(PrefixTableEntry &aInfo) co return error; } +Error RoutingManager::PdPrefixManager::GetProcessedRaInfo(PdProcessedRaInfo &aPdProcessedRaInfo) const +{ + Error error = kErrorNone; + + VerifyOrExit(IsRunning() && HasPrefix(), error = kErrorNotFound); + + aPdProcessedRaInfo.mNumPlatformRaReceived = mNumPlatformRaReceived; + aPdProcessedRaInfo.mNumPlatformPioProcessed = mNumPlatformPioProcessed; + aPdProcessedRaInfo.mLastPlatformRaMsec = TimerMilli::GetNow() - mLastPlatformRaTime; + +exit: + return error; +} + void RoutingManager::PdPrefixManager::WithdrawPrefix(void) { VerifyOrExit(HasPrefix()); @@ -3472,9 +3567,11 @@ void RoutingManager::PdPrefixManager::ProcessPlatformGeneratedRa(const uint8_t * Error error = kErrorNone; Ip6::Nd::RouterAdvertMessage::Icmp6Packet packet; - VerifyOrExit(mEnabled, LogWarn("Ignore platform generated RA since PD is disabled.")); + VerifyOrExit(IsRunning(), LogWarn("Ignore platform generated RA since PD is disabled or not running.")); packet.Init(aRouterAdvert, aLength); error = Process(Ip6::Nd::RouterAdvertMessage(packet)); + mNumPlatformRaReceived++; + mLastPlatformRaTime = TimerMilli::GetNow(); exit: if (error != kErrorNone) @@ -3501,7 +3598,7 @@ Error RoutingManager::PdPrefixManager::Process(const Ip6::Nd::RouterAdvertMessag { continue; } - + mNumPlatformPioProcessed++; entry.SetFrom(static_cast(option)); if (!IsValidPdPrefix(entry.GetPrefix())) @@ -3563,15 +3660,11 @@ Error RoutingManager::PdPrefixManager::Process(const Ip6::Nd::RouterAdvertMessag void RoutingManager::PdPrefixManager::SetEnabled(bool aEnabled) { - VerifyOrExit(mEnabled != aEnabled); + Dhcp6PdState oldState = GetState(); + VerifyOrExit(mEnabled != aEnabled); mEnabled = aEnabled; - if (!aEnabled) - { - WithdrawPrefix(); - } - - LogInfo("PdPrefixManager is %s", aEnabled ? "enabled" : "disabled"); + EvaluateStateChange(oldState); exit: return; diff --git a/src/core/border_router/routing_manager.hpp b/src/core/border_router/routing_manager.hpp index f0d824e82..95a86f0e0 100644 --- a/src/core/border_router/routing_manager.hpp +++ b/src/core/border_router/routing_manager.hpp @@ -88,6 +88,7 @@ class RoutingManager : public InstanceLocator typedef otBorderRoutingPrefixTableIterator PrefixTableIterator; ///< Prefix Table Iterator. typedef otBorderRoutingPrefixTableEntry PrefixTableEntry; ///< Prefix Table Entry. typedef otBorderRoutingRouterEntry RouterEntry; ///< Router Entry. + typedef otPdProcessedRaInfo PdProcessedRaInfo; ///< Data of PdProcessedRaInfo. /** * This constant specifies the maximum number of route prefixes that may be published by `RoutingManager` @@ -121,9 +122,9 @@ class RoutingManager : public InstanceLocator */ enum Dhcp6PdState : uint8_t { - kDhcp6PdStateDisabled = OT_BORDER_ROUTING_STATE_DISABLED, ///< Disabled. - kDhcp6PdStateStopped = OT_BORDER_ROUTING_STATE_STOPPED, ///< Enabled, but currently stopped. - kDhcp6PdStateRunning = OT_BORDER_ROUTING_STATE_RUNNING, ///< Enabled, and running. + kDhcp6PdStateDisabled = OT_BORDER_ROUTING_DHCP6_PD_STATE_DISABLED, ///< Disabled. + kDhcp6PdStateStopped = OT_BORDER_ROUTING_DHCP6_PD_STATE_STOPPED, ///< Enabled, but currently stopped. + kDhcp6PdStateRunning = OT_BORDER_ROUTING_DHCP6_PD_STATE_RUNNING, ///< Enabled, and running. }; /** @@ -292,6 +293,18 @@ class RoutingManager : public InstanceLocator * */ Error GetPdOmrPrefix(PrefixTableEntry &aPrefixInfo) const; + + /** + * Returns platform generated RA message processed information. + * + * @param[out] aPdProcessedRaInfo A reference to where the PD processed RA info will be output to. + * + * @retval kErrorNone Successfully retrieved the Info. + * @retval kErrorNotFound There are no valid RA process info on this BR. + * @retval kErrorInvalidState The Border Routing Manager is not initialized yet. + * + */ + Error GetPdProcessedRaInfo(PdProcessedRaInfo &aPdProcessedRaInfo); #endif /** @@ -1169,20 +1182,20 @@ class RoutingManager : public InstanceLocator explicit PdPrefixManager(Instance &aInstance); void SetEnabled(bool aEnabled); + void Start(void) { StartStop(/* aStart= */ true); } + void Stop(void) { StartStop(/* aStart= */ false); } bool IsRunning(void) const { return GetState() == Dhcp6PdState::kDhcp6PdStateRunning; } bool HasPrefix(void) const { return IsValidOmrPrefix(mPrefix.GetPrefix()); } const Ip6::Prefix &GetPrefix(void) const { return mPrefix.GetPrefix(); } - Dhcp6PdState GetState(void) const - { - // TODO: We need to stop and inform the platform when there is already a GUA prefix advertised in the - // network. - return mEnabled ? kDhcp6PdStateRunning : kDhcp6PdStateDisabled; - } + Dhcp6PdState GetState(void) const; void ProcessPlatformGeneratedRa(const uint8_t *aRouterAdvert, uint16_t aLength); Error GetPrefixInfo(PrefixTableEntry &aInfo) const; + Error GetProcessedRaInfo(PdProcessedRaInfo &aPdProcessedRaInfo) const; void HandleTimer(void) { WithdrawPrefix(); } + static const char *StateToString(Dhcp6PdState aState); + static bool IsValidPdPrefix(const Ip6::Prefix &aPrefix) { // We should accept ULA prefix since it could be used by the internet infrastructure like NAT64. @@ -1192,11 +1205,17 @@ class RoutingManager : public InstanceLocator private: Error Process(const Ip6::Nd::RouterAdvertMessage &aMessage); + void EvaluateStateChange(Dhcp6PdState aOldState); void WithdrawPrefix(void); + void StartStop(bool aStart); using PlatformOmrPrefixTimer = TimerMilliIn; bool mEnabled; + bool mIsRunning; + uint32_t mNumPlatformPioProcessed; + uint32_t mNumPlatformRaReceived; + TimeMilli mLastPlatformRaTime; PlatformOmrPrefixTimer mTimer; DiscoveredPrefixTable::Entry mPrefix; }; diff --git a/src/core/coap/coap_secure.cpp b/src/core/coap/coap_secure.cpp index 7d0fbe1b3..1077cecc0 100644 --- a/src/core/coap/coap_secure.cpp +++ b/src/core/coap/coap_secure.cpp @@ -28,13 +28,14 @@ #include "coap_secure.hpp" -#if OPENTHREAD_CONFIG_DTLS_ENABLE +#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE #include "common/locator_getters.hpp" #include "common/log.hpp" #include "common/new.hpp" #include "instance/instance.hpp" -#include "meshcop/dtls.hpp" +#include "meshcop/secure_transport.hpp" + #include "thread/thread_netif.hpp" /** @@ -67,7 +68,7 @@ Error CoapSecure::Start(uint16_t aPort) return error; } -Error CoapSecure::Start(MeshCoP::Dtls::TransportCallback aCallback, void *aContext) +Error CoapSecure::Start(MeshCoP::SecureTransport::TransportCallback aCallback, void *aContext) { Error error = kErrorNone; @@ -98,7 +99,7 @@ Error CoapSecure::Connect(const Ip6::SockAddr &aSockAddr, ConnectedCallback aCal void CoapSecure::SetPsk(const MeshCoP::JoinerPskd &aPskd) { static_assert(static_cast(MeshCoP::JoinerPskd::kMaxLength) <= - static_cast(MeshCoP::Dtls::kPskMaxLength), + static_cast(MeshCoP::SecureTransport::kPskMaxLength), "The maximum length of DTLS PSK is smaller than joiner PSKd"); SuccessOrAssert(mDtls.SetPsk(reinterpret_cast(aPskd.GetAsCString()), aPskd.GetLength())); @@ -224,4 +225,4 @@ void CoapSecure::HandleTransmit(void) } // namespace Coap } // namespace ot -#endif // OPENTHREAD_CONFIG_DTLS_ENABLE +#endif // OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE diff --git a/src/core/coap/coap_secure.hpp b/src/core/coap/coap_secure.hpp index b24e7d0fb..50a1d4521 100644 --- a/src/core/coap/coap_secure.hpp +++ b/src/core/coap/coap_secure.hpp @@ -31,12 +31,12 @@ #include "openthread-core-config.h" -#if OPENTHREAD_CONFIG_DTLS_ENABLE +#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE #include "coap/coap.hpp" #include "common/callback.hpp" -#include "meshcop/dtls.hpp" #include "meshcop/meshcop.hpp" +#include "meshcop/secure_transport.hpp" #include @@ -91,7 +91,7 @@ class CoapSecure : public CoapBase * @retval kErrorAlready Already started. * */ - Error Start(MeshCoP::Dtls::TransportCallback aCallback, void *aContext); + Error Start(MeshCoP::SecureTransport::TransportCallback aCallback, void *aContext); /** * Sets connected callback of this secure CoAP agent. @@ -153,7 +153,7 @@ class CoapSecure : public CoapBase * @returns A reference to the DTLS object. * */ - MeshCoP::Dtls &GetDtls(void) { return mDtls; } + MeshCoP::SecureTransport &GetDtls(void) { return mDtls; } /** * Gets the UDP port of this agent. @@ -409,7 +409,7 @@ class CoapSecure : public CoapBase static void HandleTransmit(Tasklet &aTasklet); void HandleTransmit(void); - MeshCoP::Dtls mDtls; + MeshCoP::SecureTransport mDtls; Callback mConnectedCallback; ot::MessageQueue mTransmitQueue; TaskletContext mTransmitTask; @@ -418,6 +418,6 @@ class CoapSecure : public CoapBase } // namespace Coap } // namespace ot -#endif // OPENTHREAD_CONFIG_DTLS_ENABLE +#endif // OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE #endif // COAP_SECURE_HPP_ diff --git a/src/core/common/encoding.hpp b/src/core/common/encoding.hpp index 981338513..d56c5aafd 100644 --- a/src/core/common/encoding.hpp +++ b/src/core/common/encoding.hpp @@ -167,6 +167,23 @@ inline uint64_t ReadUint64(const uint8_t *aBuffer) (static_cast(aBuffer[6]) << 8) | (static_cast(aBuffer[7]) << 0)); } +/** + * Reads a `UintType` integer value from a given buffer assuming big-endian encoding. + * + * @tparam UintType The unsigned int type. + * + * @param[in] aBuffer Pointer to the buffer to read from. + * + * @returns The `UintType` value read from the buffer. + * + */ +template UintType Read(const uint8_t *aBuffer); + +template <> inline uint8_t Read(const uint8_t *aBuffer) { return *aBuffer; } +template <> inline uint16_t Read(const uint8_t *aBuffer) { return ReadUint16(aBuffer); } +template <> inline uint32_t Read(const uint8_t *aBuffer) { return ReadUint32(aBuffer); } +template <> inline uint64_t Read(const uint8_t *aBuffer) { return ReadUint64(aBuffer); } + /** * Writes a `uint16_t` value to a given buffer using big-endian encoding. * @@ -228,6 +245,22 @@ inline void WriteUint64(uint64_t aValue, uint8_t *aBuffer) aBuffer[7] = (aValue >> 0) & 0xff; } +/** + * Writes a `UintType` integer value to a given buffer assuming big-endian encoding. + * + * @tparam UintType The unsigned int type. + * + * @param[in] aValue The value to write to buffer. + * @param[in] aBuffer Pointer to the buffer to write to. + * + */ +template void Write(UintType aValue, uint8_t *aBuffer); + +template <> inline void Write(uint8_t aValue, uint8_t *aBuffer) { *aBuffer = aValue; } +template <> inline void Write(uint16_t aValue, uint8_t *aBuffer) { WriteUint16(aValue, aBuffer); } +template <> inline void Write(uint32_t aValue, uint8_t *aBuffer) { WriteUint32(aValue, aBuffer); } +template <> inline void Write(uint64_t aValue, uint8_t *aBuffer) { WriteUint64(aValue, aBuffer); } + } // namespace BigEndian namespace LittleEndian { @@ -317,6 +350,23 @@ inline uint64_t ReadUint64(const uint8_t *aBuffer) (static_cast(aBuffer[6]) << 48) | (static_cast(aBuffer[7]) << 56)); } +/** + * Reads a `UintType` integer value from a given buffer assuming little-endian encoding. + * + * @tparam UintType The unsigned int type. + * + * @param[in] aBuffer Pointer to the buffer to read from. + * + * @returns The `UintType` value read from the buffer. + * + */ +template UintType Read(const uint8_t *aBuffer); + +template <> inline uint8_t Read(const uint8_t *aBuffer) { return *aBuffer; } +template <> inline uint16_t Read(const uint8_t *aBuffer) { return ReadUint16(aBuffer); } +template <> inline uint32_t Read(const uint8_t *aBuffer) { return ReadUint32(aBuffer); } +template <> inline uint64_t Read(const uint8_t *aBuffer) { return ReadUint64(aBuffer); } + /** * Writes a `uint16_t` value to a given buffer using little-endian encoding. * @@ -378,6 +428,22 @@ inline void WriteUint64(uint64_t aValue, uint8_t *aBuffer) aBuffer[7] = (aValue >> 56) & 0xff; } +/** + * Writes a `UintType` integer value to a given buffer assuming little-endian encoding. + * + * @tparam UintType The unsigned int type. + * + * @param[in] aValue The value to write to buffer. + * @param[in] aBuffer Pointer to the buffer to write to. + * + */ +template void Write(UintType aValue, uint8_t *aBuffer); + +template <> inline void Write(uint8_t aValue, uint8_t *aBuffer) { *aBuffer = aValue; } +template <> inline void Write(uint16_t aValue, uint8_t *aBuffer) { WriteUint16(aValue, aBuffer); } +template <> inline void Write(uint32_t aValue, uint8_t *aBuffer) { WriteUint32(aValue, aBuffer); } +template <> inline void Write(uint64_t aValue, uint8_t *aBuffer) { WriteUint64(aValue, aBuffer); } + } // namespace LittleEndian } // namespace ot diff --git a/src/core/common/message.hpp b/src/core/common/message.hpp index 66153e240..d48f719c2 100644 --- a/src/core/common/message.hpp +++ b/src/core/common/message.hpp @@ -286,7 +286,8 @@ class Message : public otMessage, public Buffer, public GetProvider kTypeSupervision = 2, ///< A child supervision frame. kTypeMacEmptyData = 3, ///< An empty MAC data frame. kTypeIp4 = 4, ///< A full uncompressed IPv4 packet, for NAT64. - kTypeOther = 5, ///< Other (data) message. + kTypeBle = 5, ///< A BLE payload message. + kTypeOther = 6, ///< Other (data) message. }; /** diff --git a/src/core/common/tlvs.hpp b/src/core/common/tlvs.hpp index e72bf46ab..fd2d6791f 100644 --- a/src/core/common/tlvs.hpp +++ b/src/core/common/tlvs.hpp @@ -173,6 +173,74 @@ class Tlv */ Error AppendTo(Message &aMessage) const; + /** + * Reads the value of TLV treating it as a given simple TLV type. + * + * This method requires the TLV to be already validated, in particular, its length MUST NOT be less than the + * required size of the value type. The TLV MUST NOT be extended. If these conditions are not met, the behavior of + * this method is undefined. + * + * @tparam SimpleTlvType The simple TLV type to read (must be a sub-class of `SimpleTlvInfo`). + * + * @returns The TLV value as `SimpleTlvType::ValueType`. + * + */ + template const typename SimpleTlvType::ValueType &ReadValueAs(void) const + { + return *reinterpret_cast(this + 1); + } + + /** + * Reads the value of TLV treating it as a given integer-value TLV type. + * + * This method requires the TLV to be already validated, in particular, its length MUST NOT be less than the + * required size of the value type. The TLV MUST NOT be extended. If these conditions are not met, the behavior of + * this method is undefined. + * + * @tparam UintTlvType The integer simple TLV type to read (must be a sub-class of `UintTlvInfo`). + * + * @returns The TLV value as `UintTlvInfo::UintValueType`. + * + */ + template typename UintTlvType::UintValueType ReadValueAs(void) const + { + return BigEndian::Read(reinterpret_cast(this + 1)); + } + + /** + * Writes the value of TLV treating it as a given simple TLV type. + * + * This method requires the TLV to be already validated, in particular, its length MUST NOT be less than the + * required size of the value type. The TLV MUST NOT be extended. If these conditions are not met, the behavior of + * this method is undefined. + * + * @tparam SimpleTlvType The simple TLV type to read (must be a sub-class of `SimpleTlvInfo`). + * + * @param[in] aValue The new TLV value. + * + */ + template void WriteValueAs(const typename SimpleTlvType::ValueType &aValue) + { + memcpy(this + 1, &aValue, sizeof(aValue)); + } + + /** + * Writes the value of TLV treating it as a given integer-value TLV type. + * + * This method requires the TLV to be already validated, in particular, its length MUST NOT be less than the + * required size of the value type. The TLV MUST NOT be extended. If these conditions are not met, the behavior of + * this method is undefined. + * + * @tparam UintTlvType The integer simple TLV type to read (must be a sub-class of `UintTlvInfo`). + * + * @param[in] aValue The new TLV value. + * + */ + template void WriteValueAs(typename UintTlvType::UintValueType aValue) + { + return BigEndian::Write(aValue, reinterpret_cast(this + 1)); + } + //------------------------------------------------------------------------------------------------------------------ // Static methods for reading/finding/appending TLVs in a `Message`. diff --git a/src/core/config/ip6.h b/src/core/config/ip6.h index 75976e1a1..82dc45c95 100644 --- a/src/core/config/ip6.h +++ b/src/core/config/ip6.h @@ -178,7 +178,7 @@ * Define as 1 to enable support for TLS over TCP. * */ -#if OPENTHREAD_CONFIG_TCP_ENABLE && !defined(OPENTHREAD_CONFIG_TLS_ENABLE) +#if (OPENTHREAD_CONFIG_TCP_ENABLE || OPENTHREAD_CONFIG_BLE_TCAT_ENABLE) && !defined(OPENTHREAD_CONFIG_TLS_ENABLE) #define OPENTHREAD_CONFIG_TLS_ENABLE 1 #endif diff --git a/src/core/config/misc.h b/src/core/config/misc.h index df9fd28fa..c9be7ec5e 100644 --- a/src/core/config/misc.h +++ b/src/core/config/misc.h @@ -141,6 +141,16 @@ #define OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + * + * Define to 1 to enable multipan RCP support. + * + */ +#ifndef OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE +#define OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE 0 +#endif + /** * @def OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE * @@ -520,6 +530,26 @@ #define OPENTHREAD_CONFIG_NEIGHBOR_DISCOVERY_AGENT_ENABLE 0 #endif +/** + * @def OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE + * + * Define to 1 to enable multiple static instance support. + * + */ +#ifndef OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE +#define OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM + * + * Define number of OpenThread instance for static allocation buffer. + * + */ +#ifndef OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM +#define OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM 3 +#endif + /** * @def OPENTHREAD_CONFIG_ALLOW_EMPTY_NETWORK_NAME * diff --git a/src/core/config/openthread-core-config-check.h b/src/core/config/openthread-core-config-check.h index 5d26a6e0f..de4fe1559 100644 --- a/src/core/config/openthread-core-config-check.h +++ b/src/core/config/openthread-core-config-check.h @@ -122,7 +122,7 @@ #endif #ifdef OPENTHREAD_ENABLE_DTLS -#error "OPENTHREAD_ENABLE_DTLS was replaced by OPENTHREAD_CONFIG_DTLS_ENABLE." +#error "OPENTHREAD_ENABLE_DTLS was replaced by OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE." #endif #ifdef OPENTHREAD_ENABLE_JAM_DETECTION diff --git a/src/core/config/dtls.h b/src/core/config/secure_transport.h similarity index 79% rename from src/core/config/dtls.h rename to src/core/config/secure_transport.h index 09e833788..ea6799ff9 100644 --- a/src/core/config/dtls.h +++ b/src/core/config/secure_transport.h @@ -28,12 +28,12 @@ /** * @file - * This file includes compile-time configurations for DTLS. + * This file includes compile-time configurations for TLS/DTLS. * */ -#ifndef CONFIG_DTLS_H_ -#define CONFIG_DTLS_H_ +#ifndef CONFIG_SECURE_TRANSPORT_H_ +#define CONFIG_SECURE_TRANSPORT_H_ #include "config/border_agent.h" #include "config/coap.h" @@ -51,15 +51,19 @@ #endif /** - * @def OPENTHREAD_CONFIG_DTLS_ENABLE + * @def OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE * - * Define to 1 to enable DTLS. + * Define to 1 to enable DTLS/TLS. * */ -#ifndef OPENTHREAD_CONFIG_DTLS_ENABLE -#define OPENTHREAD_CONFIG_DTLS_ENABLE \ +#ifndef OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE +#define OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE \ (OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE || OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE || \ - OPENTHREAD_CONFIG_COMMISSIONER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE) + OPENTHREAD_CONFIG_COMMISSIONER_ENABLE || OPENTHREAD_CONFIG_JOINER_ENABLE || OPENTHREAD_CONFIG_BLE_TCAT_ENABLE) #endif -#endif // CONFIG_DTLS_H_ +#if OPENTHREAD_CONFIG_DTLS_ENABLE +#error "OPENTHREAD_CONFIG_DTLS_ENABLE is deprecated please use OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE instead" +#endif + +#endif // CONFIG_SECURE_TRANSPORT_H_ diff --git a/src/core/crypto/crypto_platform.cpp b/src/core/crypto/crypto_platform.cpp index 6c7e54cee..4865b08f1 100644 --- a/src/core/crypto/crypto_platform.cpp +++ b/src/core/crypto/crypto_platform.cpp @@ -662,67 +662,15 @@ OT_TOOL_WEAK otError otPlatCryptoEcdsaVerify(const otPlatCryptoEcdsaPublicKey *a #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) +OT_TOOL_WEAK otError otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword, + uint16_t aPasswordLen, + const uint8_t *aSalt, + uint16_t aSaltLen, + uint32_t aIterationCounter, + uint16_t aKeyLen, + uint8_t *aKey) { #if (MBEDTLS_VERSION_NUMBER >= 0x03050000) const size_t kBlockSize = MBEDTLS_CMAC_MAX_BLOCK_SIZE; @@ -737,6 +685,8 @@ OT_TOOL_WEAK void otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword, uint8_t *key = aKey; uint16_t keyLen = aKeyLen; uint16_t useLen = 0; + Error error = kErrorNone; + int ret; OT_ASSERT(aSaltLen <= sizeof(prfInput)); memcpy(prfInput, aSalt, aSaltLen); @@ -757,12 +707,14 @@ OT_TOOL_WEAK void otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword, prfInput[aSaltLen + 3] = static_cast(blockCounter); // Calculate U_1 - mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, prfInput, aSaltLen + 4, - reinterpret_cast(keyBlock)); + ret = mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, prfInput, aSaltLen + 4, + reinterpret_cast(keyBlock)); + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); // Calculate U_2 - mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast(keyBlock), kBlockSize, - reinterpret_cast(prfOne)); + ret = mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast(keyBlock), kBlockSize, + reinterpret_cast(prfOne)); + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); for (uint32_t j = 0; j < kBlockSize / sizeof(long); ++j) { @@ -772,11 +724,13 @@ OT_TOOL_WEAK void otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword, 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)); + ret = mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast(prfOne), + kBlockSize, reinterpret_cast(prfTwo)); + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); // Calculate U_{2 * i} - mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast(prfTwo), kBlockSize, - reinterpret_cast(prfOne)); + ret = mbedtls_aes_cmac_prf_128(aPassword, aPasswordLen, reinterpret_cast(prfTwo), + kBlockSize, reinterpret_cast(prfOne)); + VerifyOrExit(ret == 0, error = MbedTls::MapError(ret)); for (uint32_t j = 0; j < kBlockSize / sizeof(long); ++j) { @@ -789,6 +743,81 @@ OT_TOOL_WEAK void otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword, key += useLen; keyLen -= useLen; } + +exit: + return error; } #endif // #if OPENTHREAD_FTD + +#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 + +#if OPENTHREAD_FTD + +OT_TOOL_WEAK otError otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword, + uint16_t aPasswordLen, + const uint8_t *aSalt, + uint16_t aSaltLen, + uint32_t aIterationCounter, + uint16_t aKeyLen, + uint8_t *aKey) +{ + OT_UNUSED_VARIABLE(aPassword); + OT_UNUSED_VARIABLE(aPasswordLen); + OT_UNUSED_VARIABLE(aSalt); + OT_UNUSED_VARIABLE(aSaltLen); + OT_UNUSED_VARIABLE(aIterationCounter); + OT_UNUSED_VARIABLE(aKeyLen); + OT_UNUSED_VARIABLE(aKey); + + return OT_ERROR_NOT_CAPABLE; +} + +#endif // #if OPENTHREAD_FTD + +#endif // #if OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_MBEDTLS diff --git a/src/core/instance/instance.cpp b/src/core/instance/instance.cpp index 872718faf..81e3324a1 100644 --- a/src/core/instance/instance.cpp +++ b/src/core/instance/instance.cpp @@ -48,6 +48,16 @@ OT_DEFINE_ALIGNED_VAR(gInstanceRaw, sizeof(Instance), uint64_t); #endif +#if OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE + +#define INSTANCE_SIZE_ALIGNED OT_ALIGNED_VAR_SIZE(sizeof(ot::Instance), uint64_t) +#define MULTI_INSTANCE_SIZE (OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM * INSTANCE_SIZE_ALIGNED) + +// Define the raw storage used for OpenThread instance (in multi-instance case). +static uint64_t gMultiInstanceRaw[MULTI_INSTANCE_SIZE]; + +#endif + #if OPENTHREAD_MTD || OPENTHREAD_FTD #if !OPENTHREAD_CONFIG_HEAP_EXTERNAL_ENABLE OT_DEFINE_ALIGNED_VAR(sHeapRaw, sizeof(Utils::Heap), uint64_t); @@ -146,7 +156,7 @@ Instance::Instance(void) #if OPENTHREAD_CONFIG_COMMISSIONER_ENABLE && OPENTHREAD_FTD , mCommissioner(*this) #endif -#if OPENTHREAD_CONFIG_DTLS_ENABLE +#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE , mTmfSecureAgent(*this) #endif #if OPENTHREAD_CONFIG_JOINER_ENABLE @@ -201,6 +211,9 @@ Instance::Instance(void) #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE , mApplicationCoapSecure(*this, /* aLayerTwoSecurity */ true) #endif +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE + , mApplicationBleSecure(*this) +#endif #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE , mPingSender(*this) #endif @@ -288,6 +301,25 @@ Instance &Instance::Get(void) } #else // #if !OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE +#if OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE + +Instance *Instance::InitMultiple(uint8_t aIdx) +{ + size_t bufferSize; + uint64_t *instanceBuffer = gMultiInstanceRaw + aIdx * INSTANCE_SIZE_ALIGNED; + Instance *instance = reinterpret_cast(instanceBuffer); + + VerifyOrExit(aIdx < OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); + VerifyOrExit(!instance->mIsInitialized); + + bufferSize = (&gMultiInstanceRaw[MULTI_INSTANCE_SIZE] - instanceBuffer) * sizeof(uint64_t); + instance = Instance::Init(instanceBuffer, &bufferSize); + +exit: + return instance; +} + +#endif // #if OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE Instance *Instance::Init(void *aBuffer, size_t *aBufferSize) { @@ -427,7 +459,7 @@ void Instance::GetBufferInfo(BufferInfo &aInfo) Get().GetRequestMessages().GetInfo(aInfo.mCoapQueue); Get().GetCachedResponses().GetInfo(aInfo.mCoapQueue); -#if OPENTHREAD_CONFIG_DTLS_ENABLE +#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE Get().GetRequestMessages().GetInfo(aInfo.mCoapSecureQueue); Get().GetCachedResponses().GetInfo(aInfo.mCoapSecureQueue); #endif diff --git a/src/core/instance/instance.hpp b/src/core/instance/instance.hpp index 4d1a3255b..efde7fffb 100644 --- a/src/core/instance/instance.hpp +++ b/src/core/instance/instance.hpp @@ -97,6 +97,7 @@ #include "net/sntp_client.hpp" #include "net/srp_client.hpp" #include "net/srp_server.hpp" +#include "radio/ble_secure.hpp" #include "thread/address_resolver.hpp" #include "thread/announce_begin_server.hpp" #include "thread/announce_sender.hpp" @@ -186,6 +187,21 @@ class Instance : public otInstance, private NonCopyable */ static Instance *Init(void *aBuffer, size_t *aBufferSize); +#if OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE + /** + * This static method initializes the OpenThread instance. + * + * This method utilizes static buffer to initialize the OpenThread instance. + * This function must be called before any other calls on OpenThread instance. + * + * @param[in] aIdx The index of the OpenThread instance to initialize. + * + * @returns A pointer to the new OpenThread instance. + * + */ + static Instance *InitMultiple(uint8_t aIdx); +#endif + #else // OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE /** @@ -532,7 +548,7 @@ class Instance : public otInstance, private NonCopyable MeshCoP::Commissioner mCommissioner; #endif -#if OPENTHREAD_CONFIG_DTLS_ENABLE +#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE Tmf::SecureAgent mTmfSecureAgent; #endif @@ -603,6 +619,10 @@ class Instance : public otInstance, private NonCopyable Coap::CoapSecure mApplicationCoapSecure; #endif +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE + Ble::BleSecure mApplicationBleSecure; +#endif + #if OPENTHREAD_CONFIG_PING_SENDER_ENABLE Utils::PingSender mPingSender; #endif @@ -815,7 +835,7 @@ template <> inline Ip6::Mpl &Instance::Get(void) { return mIp6.mMpl; } template <> inline Tmf::Agent &Instance::Get(void) { return mTmfAgent; } -#if OPENTHREAD_CONFIG_DTLS_ENABLE +#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE template <> inline Tmf::SecureAgent &Instance::Get(void) { return mTmfSecureAgent; } #endif @@ -1002,6 +1022,10 @@ template <> inline Nat64::Translator &Instance::Get(void) { return mNat64Transla template <> inline Srp::Server &Instance::Get(void) { return mSrpServer; } #endif +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE +template <> inline Ble::BleSecure &Instance::Get(void) { return mApplicationBleSecure; } +#endif + #endif // OPENTHREAD_MTD || OPENTHREAD_FTD #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE diff --git a/src/core/mac/sub_mac.cpp b/src/core/mac/sub_mac.cpp index 6077483c8..2884d8673 100644 --- a/src/core/mac/sub_mac.cpp +++ b/src/core/mac/sub_mac.cpp @@ -1268,7 +1268,7 @@ void SubMac::GetCslWindowEdges(uint32_t &aAhead, uint32_t &aAfter) uint32_t curTime, elapsed, semiWindow; #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_LOCAL_TIME_SYNC - curTime = TimerMicro::GetNow(); + curTime = TimerMicro::GetNow().GetValue(); #else curTime = static_cast(otPlatRadioGetNow(&GetInstance())); #endif diff --git a/src/core/meshcop/commissioner.hpp b/src/core/meshcop/commissioner.hpp index b62a8d269..fda668c44 100644 --- a/src/core/meshcop/commissioner.hpp +++ b/src/core/meshcop/commissioner.hpp @@ -50,9 +50,9 @@ #include "common/timer.hpp" #include "mac/mac_types.hpp" #include "meshcop/announce_begin_client.hpp" -#include "meshcop/dtls.hpp" #include "meshcop/energy_scan_client.hpp" #include "meshcop/panid_query_client.hpp" +#include "meshcop/secure_transport.hpp" #include "net/ip6_address.hpp" #include "net/udp6.hpp" #include "thread/key_manager.hpp" diff --git a/src/core/meshcop/dataset.cpp b/src/core/meshcop/dataset.cpp index cc958313c..1692696fc 100644 --- a/src/core/meshcop/dataset.cpp +++ b/src/core/meshcop/dataset.cpp @@ -179,7 +179,7 @@ bool Dataset::IsValid(void) const return rval; } -const Tlv *Dataset::GetTlv(Tlv::Type aType) const { return As(Tlv::FindTlv(mTlvs, mLength, aType)); } +const Tlv *Dataset::FindTlv(Tlv::Type aType) const { return As(Tlv::FindTlv(mTlvs, mLength, aType)); } void Dataset::ConvertTo(Info &aDatasetInfo) const { @@ -190,11 +190,11 @@ void Dataset::ConvertTo(Info &aDatasetInfo) const switch (cur->GetType()) { case Tlv::kActiveTimestamp: - aDatasetInfo.SetActiveTimestamp(As(cur)->GetTimestamp()); + aDatasetInfo.SetActiveTimestamp(cur->ReadValueAs()); break; case Tlv::kChannel: - aDatasetInfo.SetChannel(As(cur)->GetChannel()); + aDatasetInfo.SetChannel(cur->ReadValueAs().GetChannel()); break; case Tlv::kChannelMask: @@ -210,19 +210,19 @@ void Dataset::ConvertTo(Info &aDatasetInfo) const } case Tlv::kDelayTimer: - aDatasetInfo.SetDelay(As(cur)->GetDelayTimer()); + aDatasetInfo.SetDelay(cur->ReadValueAs()); break; case Tlv::kExtendedPanId: - aDatasetInfo.SetExtendedPanId(As(cur)->GetExtendedPanId()); + aDatasetInfo.SetExtendedPanId(cur->ReadValueAs()); break; case Tlv::kMeshLocalPrefix: - aDatasetInfo.SetMeshLocalPrefix(As(cur)->GetMeshLocalPrefix()); + aDatasetInfo.SetMeshLocalPrefix(cur->ReadValueAs()); break; case Tlv::kNetworkKey: - aDatasetInfo.SetNetworkKey(As(cur)->GetNetworkKey()); + aDatasetInfo.SetNetworkKey(cur->ReadValueAs()); break; case Tlv::kNetworkName: @@ -230,15 +230,15 @@ void Dataset::ConvertTo(Info &aDatasetInfo) const break; case Tlv::kPanId: - aDatasetInfo.SetPanId(As(cur)->GetPanId()); + aDatasetInfo.SetPanId(cur->ReadValueAs()); break; case Tlv::kPendingTimestamp: - aDatasetInfo.SetPendingTimestamp(As(cur)->GetTimestamp()); + aDatasetInfo.SetPendingTimestamp(cur->ReadValueAs()); break; case Tlv::kPskc: - aDatasetInfo.SetPskc(As(cur)->GetPskc()); + aDatasetInfo.SetPskc(cur->ReadValueAs()); break; case Tlv::kSecurityPolicy: @@ -286,7 +286,7 @@ Error Dataset::SetFrom(const Info &aDatasetInfo) Timestamp activeTimestamp; aDatasetInfo.GetActiveTimestamp(activeTimestamp); - IgnoreError(SetTlv(Tlv::kActiveTimestamp, activeTimestamp)); + IgnoreError(Write(activeTimestamp)); } if (aDatasetInfo.IsPendingTimestampPresent()) @@ -294,20 +294,20 @@ Error Dataset::SetFrom(const Info &aDatasetInfo) Timestamp pendingTimestamp; aDatasetInfo.GetPendingTimestamp(pendingTimestamp); - IgnoreError(SetTlv(Tlv::kPendingTimestamp, pendingTimestamp)); + IgnoreError(Write(pendingTimestamp)); } if (aDatasetInfo.IsDelayPresent()) { - IgnoreError(SetTlv(Tlv::kDelayTimer, aDatasetInfo.GetDelay())); + IgnoreError(Write(aDatasetInfo.GetDelay())); } if (aDatasetInfo.IsChannelPresent()) { - ChannelTlv tlv; - tlv.Init(); - tlv.SetChannel(aDatasetInfo.GetChannel()); - IgnoreError(SetTlv(tlv)); + ChannelTlvValue channelValue; + + channelValue.SetChannelAndPage(aDatasetInfo.GetChannel()); + IgnoreError(Write(channelValue)); } if (aDatasetInfo.IsChannelMaskPresent()) @@ -315,39 +315,39 @@ Error Dataset::SetFrom(const Info &aDatasetInfo) ChannelMaskTlv tlv; tlv.Init(); tlv.SetChannelMask(aDatasetInfo.GetChannelMask()); - IgnoreError(SetTlv(tlv)); + IgnoreError(WriteTlv(tlv)); } if (aDatasetInfo.IsExtendedPanIdPresent()) { - IgnoreError(SetTlv(Tlv::kExtendedPanId, aDatasetInfo.GetExtendedPanId())); + IgnoreError(Write(aDatasetInfo.GetExtendedPanId())); } if (aDatasetInfo.IsMeshLocalPrefixPresent()) { - IgnoreError(SetTlv(Tlv::kMeshLocalPrefix, aDatasetInfo.GetMeshLocalPrefix())); + IgnoreError(Write(aDatasetInfo.GetMeshLocalPrefix())); } if (aDatasetInfo.IsNetworkKeyPresent()) { - IgnoreError(SetTlv(Tlv::kNetworkKey, aDatasetInfo.GetNetworkKey())); + IgnoreError(Write(aDatasetInfo.GetNetworkKey())); } if (aDatasetInfo.IsNetworkNamePresent()) { NameData nameData = aDatasetInfo.GetNetworkName().GetAsData(); - IgnoreError(SetTlv(Tlv::kNetworkName, nameData.GetBuffer(), nameData.GetLength())); + IgnoreError(WriteTlv(Tlv::kNetworkName, nameData.GetBuffer(), nameData.GetLength())); } if (aDatasetInfo.IsPanIdPresent()) { - IgnoreError(SetTlv(Tlv::kPanId, aDatasetInfo.GetPanId())); + IgnoreError(Write(aDatasetInfo.GetPanId())); } if (aDatasetInfo.IsPskcPresent()) { - IgnoreError(SetTlv(Tlv::kPskc, aDatasetInfo.GetPskc())); + IgnoreError(Write(aDatasetInfo.GetPskc())); } if (aDatasetInfo.IsSecurityPolicyPresent()) @@ -356,7 +356,7 @@ Error Dataset::SetFrom(const Info &aDatasetInfo) tlv.Init(); tlv.SetSecurityPolicy(aDatasetInfo.GetSecurityPolicy()); - IgnoreError(SetTlv(tlv)); + IgnoreError(WriteTlv(tlv)); } mUpdateTime = TimerMilli::GetNow(); @@ -366,21 +366,20 @@ Error Dataset::SetFrom(const Info &aDatasetInfo) Error Dataset::GetTimestamp(Type aType, Timestamp &aTimestamp) const { - Error error = kErrorNone; + Error error = kErrorNone; + const Tlv *tlv; if (aType == kActive) { - const ActiveTimestampTlv *tlv = GetTlv(); - + tlv = FindTlv(Tlv::kActiveTimestamp); VerifyOrExit(tlv != nullptr, error = kErrorNotFound); - aTimestamp = tlv->GetTimestamp(); + aTimestamp = tlv->ReadValueAs(); } else { - const PendingTimestampTlv *tlv = GetTlv(); - + tlv = FindTlv(Tlv::kPendingTimestamp); VerifyOrExit(tlv != nullptr, error = kErrorNotFound); - aTimestamp = tlv->GetTimestamp(); + aTimestamp = tlv->ReadValueAs(); } exit: @@ -389,35 +388,38 @@ Error Dataset::GetTimestamp(Type aType, Timestamp &aTimestamp) const void Dataset::SetTimestamp(Type aType, const Timestamp &aTimestamp) { - IgnoreError(SetTlv((aType == kActive) ? Tlv::kActiveTimestamp : Tlv::kPendingTimestamp, aTimestamp)); + if (aType == kActive) + { + IgnoreError(Write(aTimestamp)); + } + else + { + IgnoreError(Write(aTimestamp)); + } } -Error Dataset::SetTlv(Tlv::Type aType, const void *aValue, uint8_t aLength) +Error Dataset::WriteTlv(Tlv::Type aType, const void *aValue, uint8_t aLength) { Error error = kErrorNone; uint16_t bytesAvailable = sizeof(mTlvs) - mLength; - Tlv *old = GetTlv(aType); - Tlv tlv; + Tlv *oldTlv = FindTlv(aType); + Tlv *newTlv; - if (old != nullptr) + if (oldTlv != nullptr) { - bytesAvailable += sizeof(Tlv) + old->GetLength(); + bytesAvailable += sizeof(Tlv) + oldTlv->GetLength(); } VerifyOrExit(sizeof(Tlv) + aLength <= bytesAvailable, error = kErrorNoBufs); - if (old != nullptr) - { - RemoveTlv(old); - } + RemoveTlv(oldTlv); - tlv.SetType(aType); - tlv.SetLength(aLength); - memcpy(mTlvs + mLength, &tlv, sizeof(Tlv)); - mLength += sizeof(Tlv); + newTlv = GetTlvsEnd(); + mLength += sizeof(Tlv) + aLength; - memcpy(mTlvs + mLength, aValue, aLength); - mLength += aLength; + newTlv->SetType(aType); + newTlv->SetLength(aLength); + memcpy(newTlv->GetValue(), aValue, aLength); mUpdateTime = TimerMilli::GetNow(); @@ -425,7 +427,7 @@ 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::WriteTlv(const Tlv &aTlv) { return WriteTlv(aTlv.GetType(), aTlv.GetValue(), aTlv.GetLength()); } Error Dataset::ReadFromMessage(const Message &aMessage, uint16_t aOffset, uint16_t aLength) { @@ -445,16 +447,7 @@ Error Dataset::ReadFromMessage(const Message &aMessage, uint16_t aOffset, uint16 return error; } -void Dataset::RemoveTlv(Tlv::Type aType) -{ - Tlv *tlv; - - VerifyOrExit((tlv = GetTlv(aType)) != nullptr); - RemoveTlv(tlv); - -exit: - return; -} +void Dataset::RemoveTlv(Tlv::Type aType) { RemoveTlv(FindTlv(aType)); } Error Dataset::AppendMleDatasetTlv(Type aType, Message &aMessage) const { @@ -479,19 +472,19 @@ Error Dataset::AppendMleDatasetTlv(Type aType, Message &aMessage) const } else if (cur->GetType() == Tlv::kDelayTimer) { - uint32_t elapsed = TimerMilli::GetNow() - mUpdateTime; - DelayTimerTlv delayTimer = *As(cur); + uint32_t elapsed = TimerMilli::GetNow() - mUpdateTime; + uint32_t delayTimer = cur->ReadValueAs(); - if (delayTimer.GetDelayTimer() > elapsed) + if (delayTimer > elapsed) { - delayTimer.SetDelayTimer(delayTimer.GetDelayTimer() - elapsed); + delayTimer -= elapsed; } else { - delayTimer.SetDelayTimer(0); + delayTimer = 0; } - SuccessOrExit(error = delayTimer.AppendTo(aMessage)); + SuccessOrExit(error = Tlv::Append(aMessage, delayTimer)); } else { @@ -505,11 +498,14 @@ Error Dataset::AppendMleDatasetTlv(Type aType, Message &aMessage) const void Dataset::RemoveTlv(Tlv *aTlv) { - uint8_t *start = reinterpret_cast(aTlv); - uint16_t length = sizeof(Tlv) + aTlv->GetLength(); + if (aTlv != nullptr) + { + uint8_t *start = reinterpret_cast(aTlv); + uint16_t length = sizeof(Tlv) + aTlv->GetLength(); - memmove(start, start + length, mLength - (static_cast(start - mTlvs) + length)); - mLength -= length; + memmove(start, start + length, mLength - (static_cast(start - mTlvs) + length)); + mLength -= length; + } } Error Dataset::ApplyConfiguration(Instance &aInstance, bool *aIsNetworkKeyUpdated) const @@ -531,7 +527,7 @@ Error Dataset::ApplyConfiguration(Instance &aInstance, bool *aIsNetworkKeyUpdate { case Tlv::kChannel: { - uint8_t channel = static_cast(As(cur)->GetChannel()); + uint8_t channel = static_cast(cur->ReadValueAs().GetChannel()); error = mac.SetPanChannel(channel); @@ -545,11 +541,11 @@ Error Dataset::ApplyConfiguration(Instance &aInstance, bool *aIsNetworkKeyUpdate } case Tlv::kPanId: - mac.SetPanId(As(cur)->GetPanId()); + mac.SetPanId(cur->ReadValueAs()); break; case Tlv::kExtendedPanId: - aInstance.Get().SetExtPanId(As(cur)->GetExtendedPanId()); + aInstance.Get().SetExtPanId(cur->ReadValueAs()); break; case Tlv::kNetworkName: @@ -558,30 +554,29 @@ Error Dataset::ApplyConfiguration(Instance &aInstance, bool *aIsNetworkKeyUpdate case Tlv::kNetworkKey: { - const NetworkKeyTlv *key = As(cur); - NetworkKey networkKey; + NetworkKey networkKey; keyManager.GetNetworkKey(networkKey); - if (aIsNetworkKeyUpdated && (key->GetNetworkKey() != networkKey)) + if (aIsNetworkKeyUpdated && (cur->ReadValueAs() != networkKey)) { *aIsNetworkKeyUpdated = true; } - keyManager.SetNetworkKey(key->GetNetworkKey()); + keyManager.SetNetworkKey(cur->ReadValueAs()); break; } #if OPENTHREAD_FTD case Tlv::kPskc: - keyManager.SetPskc(As(cur)->GetPskc()); + keyManager.SetPskc(cur->ReadValueAs()); break; #endif case Tlv::kMeshLocalPrefix: - aInstance.Get().SetMeshLocalPrefix(As(cur)->GetMeshLocalPrefix()); + aInstance.Get().SetMeshLocalPrefix(cur->ReadValueAs()); break; case Tlv::kSecurityPolicy: @@ -605,5 +600,45 @@ void Dataset::ConvertToActive(void) const char *Dataset::TypeToString(Type aType) { return (aType == kActive) ? "Active" : "Pending"; } +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + +void Dataset::SaveTlvInSecureStorageAndClearValue(Tlv::Type aTlvType, Crypto::Storage::KeyRef aKeyRef) +{ + using namespace ot::Crypto::Storage; + + Tlv *tlv = FindTlv(aTlvType); + + VerifyOrExit(tlv != nullptr); + VerifyOrExit(tlv->GetLength() > 0); + + SuccessOrAssert(ImportKey(aKeyRef, kKeyTypeRaw, kKeyAlgorithmVendor, kUsageExport, kTypePersistent, tlv->GetValue(), + tlv->GetLength())); + + memset(tlv->GetValue(), 0, tlv->GetLength()); + +exit: + return; +} + +Error Dataset::ReadTlvFromSecureStorage(Tlv::Type aTlvType, Crypto::Storage::KeyRef aKeyRef) +{ + using namespace ot::Crypto::Storage; + + Error error = kErrorNone; + Tlv *tlv = FindTlv(aTlvType); + size_t readLength; + + VerifyOrExit(tlv != nullptr); + VerifyOrExit(tlv->GetLength() > 0); + + SuccessOrExit(error = ExportKey(aKeyRef, tlv->GetValue(), tlv->GetLength(), readLength)); + VerifyOrExit(readLength == tlv->GetLength(), error = OT_ERROR_FAILED); + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + } // namespace MeshCoP } // namespace ot diff --git a/src/core/meshcop/dataset.hpp b/src/core/meshcop/dataset.hpp index 0f9a69e0c..131a271a2 100644 --- a/src/core/meshcop/dataset.hpp +++ b/src/core/meshcop/dataset.hpp @@ -623,47 +623,126 @@ class Dataset bool IsValid(void) const; /** - * Returns a pointer to the TLV with a given type. + * Indicates whether or not a given TLV type is present in the Dataset. * - * @param[in] aType A TLV type. + * @param[in] aType The TLV type to check. * - * @returns A pointer to the TLV or `nullptr` if none is found. + * @retval TRUE TLV with @p aType is present in the Dataset. + * @retval FALSE TLV with @p aType is not present in the Dataset. * */ - Tlv *GetTlv(Tlv::Type aType) { return AsNonConst(AsConst(this)->GetTlv(aType)); } + bool ContainsTlv(Tlv::Type aType) const { return (FindTlv(aType) != nullptr); } /** - * Returns a pointer to the TLV with a given type. + * Indicates whether or not a given TLV type is present in the Dataset. * - * @param[in] aType The TLV type. + * @tparam aTlvType The TLV type to check. * - * @returns A pointer to the TLV or `nullptr` if none is found. + * @retval TRUE TLV of @p aTlvType is present in the Dataset. + * @retval FALSE TLV of @p aTlvType is not present in the Dataset. * */ - const Tlv *GetTlv(Tlv::Type aType) const; + template bool Contains(void) const + { + return ContainsTlv(static_cast(TlvType::kType)); + } + + /** + * Searches for a given TLV type in the Dataset. + * + * @param[in] aType The TLV type to find. + * + * @returns A pointer to the TLV or `nullptr` if not found. + * + */ + Tlv *FindTlv(Tlv::Type aType) { return AsNonConst(AsConst(this)->FindTlv(aType)); } + + /** + * Searches for a given TLV type in the Dataset. + * + * @param[in] aType The TLV type to find. + * + * @returns A pointer to the TLV or `nullptr` if not found. + * + */ + const Tlv *FindTlv(Tlv::Type aType) const; + + /** + * Writes a TLV to the Dataset. + * + * If the specified TLV type already exists, it will be replaced. Otherwise, the TLV will be appended. + * + * @param[in] aTlv A reference to the TLV. + * + * @retval kErrorNone Successfully updated the TLV. + * @retval kErrorNoBufs Could not add the TLV due to insufficient buffer space. + * + */ + Error WriteTlv(const Tlv &aTlv); + + /** + * Writes a TLV in the Dataset. + * + * If the specified TLV type already exists, it will be replaced. Otherwise, the TLV will be appended. + * + * @param[in] aType The TLV type. + * @param[in] aValue A pointer to a buffer containing the TLV value. + * @param[in] aLength The TLV length. + * + * @retval kErrorNone Successfully updated the TLV. + * @retval kErrorNoBufs Could not add the TLV due to insufficient buffer space. + * + */ + Error WriteTlv(Tlv::Type aType, const void *aValue, uint8_t aLength); /** - * Returns a pointer to the TLV with a given template type `TlvType` + * Writes a simple TLV in the Dataset. + * + * If the specified TLV type already exists, it will be replaced. Otherwise, the TLV will be appended. + * + * @tparam SimpleTlvType The simple TLV type (must be a sub-class of `SimpleTlvInfo`). * - * @returns A pointer to the TLV or `nullptr` if none is found. + * @param[in] aValue The TLV value. + * + * @retval kErrorNone Successfully updated the TLV. + * @retval kErrorNoBufs Could not add the TLV due to insufficient buffer space. * */ - template TlvType *GetTlv(void) + template Error Write(const typename SimpleTlvType::ValueType &aValue) { - return As(GetTlv(static_cast(TlvType::kType))); + return WriteTlv(static_cast(SimpleTlvType::kType), &aValue, sizeof(aValue)); } /** - * Returns a pointer to the TLV with a given template type `TlvType` + * Writes a `uint` TLV in the Dataset. + * + * If the specified TLV type already exists, it will be replaced. Otherwise, the TLV will be appended. + * + * @tparam UintTlvType The integer simple TLV type (must be a sub-class of `UintTlvInfo`). * - * @returns A pointer to the TLV or `nullptr` if none is found. + * @param[in] aValue The TLV value. + * + * @retval kErrorNone Successfully updated the TLV. + * @retval kErrorNoBufs Could not add the TLV due to insufficient buffer space. * */ - template const TlvType *GetTlv(void) const + template Error Write(typename UintTlvType::UintValueType aValue) { - return As(GetTlv(static_cast(TlvType::kType))); + typename UintTlvType::UintValueType value = BigEndian::HostSwap(aValue); + + return WriteTlv(static_cast(UintTlvType::kType), &value, sizeof(value)); } + /** + * Removes a TLV from the Dataset. + * + * If the Dataset does not contain the given TLV type, no action is performed. + * + * @param[in] aType The TLV type to remove. + * + */ + void RemoveTlv(Tlv::Type aType); + /** * Returns a pointer to the byte representation of the Dataset. * @@ -741,55 +820,6 @@ class Dataset */ void SetTimestamp(Type aType, const Timestamp &aTimestamp); - /** - * Sets a TLV in the Dataset. - * - * @param[in] aTlv A reference to the TLV. - * - * @retval kErrorNone Successfully set the TLV. - * @retval kErrorNoBufs Could not set the TLV due to insufficient buffer space. - * - */ - Error SetTlv(const Tlv &aTlv); - - /** - * Sets a TLV with a given TLV Type and Value. - * - * @param[in] aType The TLV Type. - * @param[in] aValue A pointer to TLV Value. - * @param[in] aLength The TLV Length in bytes (length of @p aValue). - * - * @retval kErrorNone Successfully set the TLV. - * @retval kErrorNoBufs Could not set the TLV due to insufficient buffer space. - * - */ - Error SetTlv(Tlv::Type aType, const void *aValue, uint8_t aLength); - - /** - * Sets a TLV with a given TLV Type and Value. - * - * @tparam ValueType The type of TLV's Value. - * - * Specializations of this template method are provided for `uint16_t` and `uint32_t` types which ensure big-endian - * encoding is used. - * - * @param[in] aType The TLV Type. - * @param[in] aValue The TLV Value (of type `ValueType`). - * - * @retval kErrorNone Successfully set the TLV. - * @retval kErrorNoBufs Could not set the TLV due to insufficient buffer space. - * - */ - template Error SetTlv(Tlv::Type aType, const ValueType &aValue) - { - static_assert(!TypeTraits::IsPointer::kValue, "ValueType must not be a pointer"); - static_assert(!TypeTraits::IsSame::kValue, "Specialization must be used for uint16_t"); - static_assert(!TypeTraits::IsSame::kValue, "Specialization must be used for uint32_t"); - static_assert(!TypeTraits::IsSame::kValue, "Specialization must be used for uint64_t"); - - return SetTlv(aType, &aValue, sizeof(ValueType)); - } - /** * Reads the Dataset from a given message and checks that it is well-formed and valid. * @@ -834,14 +864,6 @@ class Dataset */ void SetFrom(const otOperationalDatasetTlvs &aDataset); - /** - * Removes a TLV from the Dataset. - * - * @param[in] aType The type of a specific TLV. - * - */ - void RemoveTlv(Tlv::Type aType); - /** * Appends the MLE Dataset TLV but excluding MeshCoP Sub Timestamp TLV. * @@ -918,6 +940,35 @@ class Dataset */ static const char *TypeToString(Type aType); +#if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + + /** + * Saves a given TLV value in secure storage and clears the TLV value by setting all value bytes to zero. + * + * If the Dataset does not contain the @p aTlvType, no action is performed. + * + * @param[in] aTlvType The TLV type. + * @param[in] aKeyRef The `KeyRef` to use with secure storage. + * + */ + void SaveTlvInSecureStorageAndClearValue(Tlv::Type aTlvType, Crypto::Storage::KeyRef aKeyRef); + + /** + * Reads and updates a given TLV value in Dataset from secure storage. + * + * If the Dataset does not contain the @p aTlvType, no action is performed and `kErrorNone` is returned. + * + * @param[in] aTlvType The TLV type. + * @param[in] aKeyRef The `KeyRef` to use with secure storage. + * + * @retval kErrorNone Successfully read the TLV value from secure storage and updated the Dataset. + * @retval KErrorFailed Could not read the @aKeyRef from secure storage. + * + */ + Error ReadTlvFromSecureStorage(Tlv::Type aTlvType, Crypto::Storage::KeyRef aKeyRef); + +#endif // OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + private: void RemoveTlv(Tlv *aTlv); @@ -926,40 +977,6 @@ class Dataset uint16_t mLength; ///< The number of valid bytes in @var mTlvs }; -/** - * This is a template specialization of `SetTlv` with a `uint16_t` value type. - * - * @param[in] aType The TLV Type. - * @param[in] aValue The TLV value (as `uint16_t`). - * - * @retval kErrorNone Successfully set the TLV. - * @retval kErrorNoBufs Could not set the TLV due to insufficient buffer space. - * - */ -template <> inline Error Dataset::SetTlv(Tlv::Type aType, const uint16_t &aValue) -{ - uint16_t value = BigEndian::HostSwap16(aValue); - - return SetTlv(aType, &value, sizeof(uint16_t)); -} - -/** - * This is a template specialization of `SetTlv` with a `uint32_t` value type - * - * @param[in] aType The TLV Type. - * @param[in] aValue The TLV value (as `uint32_t`). - * - * @retval kErrorNone Successfully set the TLV. - * @retval kErrorNoBufs Could not set the TLV due to insufficient buffer space. - * - */ -template <> inline Error Dataset::SetTlv(Tlv::Type aType, const uint32_t &aValue) -{ - uint32_t value = BigEndian::HostSwap32(aValue); - - return SetTlv(aType, &value, sizeof(uint32_t)); -} - } // namespace MeshCoP DefineCoreType(otOperationalDatasetComponents, MeshCoP::Dataset::Components); diff --git a/src/core/meshcop/dataset_local.cpp b/src/core/meshcop/dataset_local.cpp index ac1095732..4c971c7bb 100644 --- a/src/core/meshcop/dataset_local.cpp +++ b/src/core/meshcop/dataset_local.cpp @@ -90,9 +90,7 @@ Error DatasetLocal::Restore(Dataset &aDataset) Error DatasetLocal::Read(Dataset &aDataset) const { - DelayTimerTlv *delayTimer; - uint32_t elapsed; - Error error; + Error error; error = Get().ReadOperationalDataset(mType, aDataset); VerifyOrExit(error == kErrorNone, aDataset.mLength = 0); @@ -108,19 +106,25 @@ Error DatasetLocal::Read(Dataset &aDataset) const } else { - delayTimer = aDataset.GetTlv(); - VerifyOrExit(delayTimer); + uint32_t elapsed; + uint32_t delayTimer; + Tlv *tlv = aDataset.FindTlv(Tlv::kDelayTimer); + + VerifyOrExit(tlv != nullptr); - elapsed = TimerMilli::GetNow() - mUpdateTime; + elapsed = TimerMilli::GetNow() - mUpdateTime; + delayTimer = tlv->ReadValueAs(); - if (delayTimer->GetDelayTimer() > elapsed) + if (delayTimer > elapsed) { - delayTimer->SetDelayTimer(delayTimer->GetDelayTimer() - elapsed); + delayTimer -= elapsed; } else { - delayTimer->SetDelayTimer(0); + delayTimer = 0; } + + tlv->WriteValueAs(delayTimer); } aDataset.mUpdateTime = TimerMilli::GetNow(); @@ -218,112 +222,64 @@ Error DatasetLocal::Save(const Dataset &aDataset) } #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE -void DatasetLocal::DestroySecurelyStoredKeys(void) const -{ - using namespace Crypto::Storage; - KeyRef networkKeyRef = IsActive() ? kActiveDatasetNetworkKeyRef : kPendingDatasetNetworkKeyRef; - KeyRef pskcRef = IsActive() ? kActiveDatasetPskcRef : kPendingDatasetPskcRef; - - // Destroy securely stored keys associated with the given operational dataset type. - DestroyKey(networkKeyRef); - DestroyKey(pskcRef); -} +const DatasetLocal::SecurelyStoredTlv DatasetLocal::kSecurelyStoredTlvs[] = { + { + Tlv::kNetworkKey, + Crypto::Storage::kActiveDatasetNetworkKeyRef, + Crypto::Storage::kPendingDatasetNetworkKeyRef, + }, + { + Tlv::kPskc, + Crypto::Storage::kActiveDatasetPskcRef, + Crypto::Storage::kPendingDatasetPskcRef, + }, +}; -void DatasetLocal::MoveKeysToSecureStorage(Dataset &aDataset) const +void DatasetLocal::DestroySecurelyStoredKeys(void) const { - using namespace Crypto::Storage; - - KeyRef networkKeyRef = IsActive() ? kActiveDatasetNetworkKeyRef : kPendingDatasetNetworkKeyRef; - KeyRef pskcRef = IsActive() ? kActiveDatasetPskcRef : kPendingDatasetPskcRef; - NetworkKeyTlv *networkKeyTlv = aDataset.GetTlv(); - PskcTlv *pskcTlv = aDataset.GetTlv(); - - if (networkKeyTlv != nullptr) + for (const SecurelyStoredTlv &entry : kSecurelyStoredTlvs) { - // If the dataset contains a network key, put it in the secure storage - // and zero the corresponding TLV element. - NetworkKey networkKey; - SuccessOrAssert(ImportKey(networkKeyRef, kKeyTypeRaw, kKeyAlgorithmVendor, kUsageExport, kTypePersistent, - networkKeyTlv->GetNetworkKey().m8, NetworkKey::kSize)); - networkKey.Clear(); - networkKeyTlv->SetNetworkKey(networkKey); + Crypto::Storage::DestroyKey(entry.GetKeyRef(mType)); } +} - if (pskcTlv != nullptr) +void DatasetLocal::MoveKeysToSecureStorage(Dataset &aDataset) const +{ + for (const SecurelyStoredTlv &entry : kSecurelyStoredTlvs) { - // If the dataset contains a PSKC, put it in the secure storage and zero - // the corresponding TLV element. - Pskc pskc; - SuccessOrAssert(ImportKey(pskcRef, kKeyTypeRaw, kKeyAlgorithmVendor, kUsageExport, kTypePersistent, - pskcTlv->GetPskc().m8, Pskc::kSize)); - pskc.Clear(); - pskcTlv->SetPskc(pskc); + aDataset.SaveTlvInSecureStorageAndClearValue(entry.mTlvType, entry.GetKeyRef(mType)); } } void DatasetLocal::EmplaceSecurelyStoredKeys(Dataset &aDataset) const { - using namespace Crypto::Storage; + bool moveKeys = false; - KeyRef networkKeyRef = IsActive() ? kActiveDatasetNetworkKeyRef : kPendingDatasetNetworkKeyRef; - KeyRef pskcRef = IsActive() ? kActiveDatasetPskcRef : kPendingDatasetPskcRef; - NetworkKeyTlv *networkKeyTlv = aDataset.GetTlv(); - PskcTlv *pskcTlv = aDataset.GetTlv(); - bool moveKeys = false; - size_t keyLen; - Error error; + // If reading any of the TLVs fails, it indicates they are not yet + // stored in secure storage and are still contained in the `Dataset` + // read from `Settings`. In this case, we move the keys to secure + // storage and then clear them from 'Settings'. - if (networkKeyTlv != nullptr) + for (const SecurelyStoredTlv &entry : kSecurelyStoredTlvs) { - // If the dataset contains a network key, its real value must have been moved to - // the secure storage upon saving the dataset, so restore it back now. - NetworkKey networkKey; - error = ExportKey(networkKeyRef, networkKey.m8, NetworkKey::kSize, keyLen); - - if (error != kErrorNone) + if (aDataset.ReadTlvFromSecureStorage(entry.mTlvType, entry.GetKeyRef(mType)) != kErrorNone) { - // If ExportKey fails, key is not in secure storage and is stored in settings moveKeys = true; } - else - { - OT_ASSERT(keyLen == NetworkKey::kSize); - networkKeyTlv->SetNetworkKey(networkKey); - } - } - - if (pskcTlv != nullptr) - { - // If the dataset contains a PSKC, its real value must have been moved to - // the secure storage upon saving the dataset, so restore it back now. - Pskc pskc; - error = ExportKey(pskcRef, pskc.m8, Pskc::kSize, keyLen); - - if (error != kErrorNone) - { - // If ExportKey fails, key is not in secure storage and is stored in settings - moveKeys = true; - } - else - { - OT_ASSERT(keyLen == Pskc::kSize); - pskcTlv->SetPskc(pskc); - } } if (moveKeys) { - // Clear the networkkey and Pskc stored in the settings and move them to secure storage. - // Store the network key and PSKC in the secure storage instead of settings. Dataset dataset; dataset.Set(GetType(), aDataset); MoveKeysToSecureStorage(dataset); - SuccessOrAssert(error = Get().SaveOperationalDataset(mType, dataset)); + SuccessOrAssert(Get().SaveOperationalDataset(mType, dataset)); } } -#endif + +#endif // OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE } // namespace MeshCoP } // namespace ot diff --git a/src/core/meshcop/dataset_local.hpp b/src/core/meshcop/dataset_local.hpp index 2981b7c02..249f069d5 100644 --- a/src/core/meshcop/dataset_local.hpp +++ b/src/core/meshcop/dataset_local.hpp @@ -184,14 +184,29 @@ class DatasetLocal : public InstanceLocator Error Save(const Dataset &aDataset); private: - bool IsActive(void) const { return (mType == Dataset::kActive); } - void SetTimestamp(const Dataset &aDataset); #if OPENTHREAD_CONFIG_PLATFORM_KEY_REFERENCES_ENABLE + struct SecurelyStoredTlv + { + Crypto::Storage::KeyRef GetKeyRef(Dataset::Type aType) const + { + return (aType == Dataset::kActive) ? mActiveKeyRef : mPendingKeyRef; + } + + Tlv::Type mTlvType; + Crypto::Storage::KeyRef mActiveKeyRef; + Crypto::Storage::KeyRef mPendingKeyRef; + }; + + static const SecurelyStoredTlv kSecurelyStoredTlvs[]; + void MoveKeysToSecureStorage(Dataset &aDataset) const; void DestroySecurelyStoredKeys(void) const; void EmplaceSecurelyStoredKeys(Dataset &aDataset) const; #endif + bool IsActive(void) const { return (mType == Dataset::kActive); } + void SetTimestamp(const Dataset &aDataset); + Timestamp mTimestamp; ///< Active or Pending Timestamp TimeMilli mUpdateTime; ///< Local time last updated Dataset::Type mType; ///< Active or Pending diff --git a/src/core/meshcop/dataset_manager.cpp b/src/core/meshcop/dataset_manager.cpp index 3e08889ea..bc316980e 100644 --- a/src/core/meshcop/dataset_manager.cpp +++ b/src/core/meshcop/dataset_manager.cpp @@ -226,7 +226,7 @@ Error DatasetManager::GetChannelMask(Mac::ChannelMask &aChannelMask) const SuccessOrExit(error = Read(dataset)); - channelMaskTlv = dataset.GetTlv(); + channelMaskTlv = As(dataset.FindTlv(Tlv::kChannelMask)); VerifyOrExit(channelMaskTlv != nullptr, error = kErrorNotFound); VerifyOrExit((mask = channelMaskTlv->GetChannelMask()) != 0); @@ -418,7 +418,7 @@ void DatasetManager::SendGetResponse(const Coap::Message &aRequest, continue; } - if ((tlv = dataset.GetTlv(static_cast(aTlvs[index]))) != nullptr) + if ((tlv = dataset.FindTlv(static_cast(aTlvs[index]))) != nullptr) { SuccessOrExit(error = tlv->AppendTo(*message)); } @@ -738,41 +738,42 @@ Error PendingDatasetManager::Save(const Timestamp &aTimestamp, void PendingDatasetManager::StartDelayTimer(void) { - DelayTimerTlv *delayTimer; - Dataset dataset; + Tlv *tlv; + uint32_t delay; + Dataset dataset; IgnoreError(Read(dataset)); mDelayTimer.Stop(); - if ((delayTimer = dataset.GetTlv()) != nullptr) - { - uint32_t delay = delayTimer->GetDelayTimer(); + tlv = dataset.FindTlv(Tlv::kDelayTimer); + VerifyOrExit(tlv != nullptr); - // the Timer implementation does not support the full 32 bit range - if (delay > Timer::kMaxDelay) - { - delay = Timer::kMaxDelay; - } + delay = tlv->ReadValueAs(); - mDelayTimer.StartAt(dataset.GetUpdateTime(), delay); - LogInfo("delay timer started %lu", ToUlong(delay)); - } + // the Timer implementation does not support the full 32 bit range + delay = Min(delay, Timer::kMaxDelay); + + mDelayTimer.StartAt(dataset.GetUpdateTime(), delay); + LogInfo("delay timer started %lu", ToUlong(delay)); + +exit: + return; } void PendingDatasetManager::HandleDelayTimer(void) { - DelayTimerTlv *delayTimer; - Dataset dataset; + Tlv *tlv; + Dataset dataset; IgnoreError(Read(dataset)); // if the Delay Timer value is larger than what our Timer implementation can handle, we have to compute // the remainder and wait some more. - if ((delayTimer = dataset.GetTlv()) != nullptr) + if ((tlv = dataset.FindTlv(Tlv::kDelayTimer)) != nullptr) { uint32_t elapsed = mDelayTimer.GetFireTime() - dataset.GetUpdateTime(); - uint32_t delay = delayTimer->GetDelayTimer(); + uint32_t delay = tlv->ReadValueAs(); if (elapsed < delay) { diff --git a/src/core/meshcop/dataset_manager.hpp b/src/core/meshcop/dataset_manager.hpp index 6637f002c..36162a315 100644 --- a/src/core/meshcop/dataset_manager.hpp +++ b/src/core/meshcop/dataset_manager.hpp @@ -182,6 +182,12 @@ class DatasetManager : public InstanceLocator #endif protected: + /** + * Default Delay Timer value for a Pending Operational Dataset (ms) + * + */ + static constexpr uint32_t kDefaultDelayTimer = OPENTHREAD_CONFIG_TMF_PENDING_DATASET_DEFAULT_DELAY; + /** * Defines a generic Dataset TLV to read from a message. * diff --git a/src/core/meshcop/dataset_manager_ftd.cpp b/src/core/meshcop/dataset_manager_ftd.cpp index 5a2fd2176..e5423dc3c 100644 --- a/src/core/meshcop/dataset_manager_ftd.cpp +++ b/src/core/meshcop/dataset_manager_ftd.cpp @@ -82,7 +82,7 @@ Error DatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo StateTlv::State state = StateTlv::kReject; Dataset dataset; Timestamp activeTimestamp; - ChannelTlv channel; + ChannelTlvValue channelValue; uint16_t sessionId; Ip6::NetworkPrefix meshLocalPrefix; NetworkKey networkKey; @@ -116,12 +116,11 @@ Error DatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo VerifyOrExit(Timestamp::Compare(&activeTimestamp, mLocal.GetTimestamp()) > 0); } - // check channel - if (Tlv::FindTlv(aMessage, channel) == kErrorNone) + if (Tlv::Find(aMessage, channelValue) == kErrorNone) { - VerifyOrExit(channel.IsValid()); + VerifyOrExit(channelValue.IsValid()); - if (channel.GetChannel() != Get().GetPanChannel()) + if (channelValue.GetChannel() != Get().GetPanChannel()) { doesAffectConnectivity = true; } @@ -203,22 +202,24 @@ Error DatasetManager::HandleSet(Coap::Message &aMessage, const Ip6::MessageInfo case Tlv::kDelayTimer: { - DelayTimerTlv &delayTimerTlv = As(datasetTlv); + uint32_t delayTimer = datasetTlv.ReadValueAs(); - if (doesAffectNetworkKey && delayTimerTlv.GetDelayTimer() < DelayTimerTlv::kDelayTimerDefault) + if (doesAffectNetworkKey && delayTimer < kDefaultDelayTimer) { - delayTimerTlv.SetDelayTimer(DelayTimerTlv::kDelayTimerDefault); + delayTimer = kDefaultDelayTimer; } - else if (delayTimerTlv.GetDelayTimer() < Get().GetDelayTimerMinimal()) + else { - delayTimerTlv.SetDelayTimer(Get().GetDelayTimerMinimal()); + delayTimer = Max(delayTimer, Get().GetDelayTimerMinimal()); } + + datasetTlv.WriteValueAs(delayTimer); } OT_FALL_THROUGH; default: - SuccessOrExit(dataset.SetTlv(datasetTlv)); + SuccessOrExit(dataset.WriteTlv(datasetTlv)); break; } @@ -300,61 +301,61 @@ Error ActiveDatasetManager::GenerateLocal(void) IgnoreError(Read(dataset)); - if (dataset.GetTlv() == nullptr) + if (!dataset.Contains()) { Timestamp timestamp; timestamp.Clear(); - IgnoreError(dataset.SetTlv(Tlv::kActiveTimestamp, timestamp)); + IgnoreError(dataset.Write(timestamp)); } - if (dataset.GetTlv() == nullptr) + if (!dataset.Contains()) { - ChannelTlv tlv; - tlv.Init(); - tlv.SetChannel(Get().GetPanChannel()); - IgnoreError(dataset.SetTlv(tlv)); + ChannelTlvValue channelValue; + + channelValue.SetChannelAndPage(Get().GetPanChannel()); + IgnoreError(dataset.Write(channelValue)); } - if (dataset.GetTlv() == nullptr) + if (!dataset.Contains()) { ChannelMaskTlv tlv; tlv.Init(); tlv.SetChannelMask(Get().GetSupportedChannelMask().GetMask()); - IgnoreError(dataset.SetTlv(tlv)); + IgnoreError(dataset.WriteTlv(tlv)); } - if (dataset.GetTlv() == nullptr) + if (!dataset.Contains()) { - IgnoreError(dataset.SetTlv(Tlv::kExtendedPanId, Get().GetExtPanId())); + IgnoreError(dataset.Write(Get().GetExtPanId())); } - if (dataset.GetTlv() == nullptr) + if (!dataset.Contains()) { - IgnoreError(dataset.SetTlv(Tlv::kMeshLocalPrefix, Get().GetMeshLocalPrefix())); + IgnoreError(dataset.Write(Get().GetMeshLocalPrefix())); } - if (dataset.GetTlv() == nullptr) + if (!dataset.Contains()) { NetworkKey networkKey; Get().GetNetworkKey(networkKey); - IgnoreError(dataset.SetTlv(Tlv::kNetworkKey, networkKey)); + IgnoreError(dataset.Write(networkKey)); } - if (dataset.GetTlv() == nullptr) + if (!dataset.Contains()) { NameData nameData = Get().GetNetworkName().GetAsData(); - IgnoreError(dataset.SetTlv(Tlv::kNetworkName, nameData.GetBuffer(), nameData.GetLength())); + IgnoreError(dataset.WriteTlv(Tlv::kNetworkName, nameData.GetBuffer(), nameData.GetLength())); } - if (dataset.GetTlv() == nullptr) + if (!dataset.Contains()) { - IgnoreError(dataset.SetTlv(Tlv::kPanId, Get().GetPanId())); + IgnoreError(dataset.Write(Get().GetPanId())); } - if (dataset.GetTlv() == nullptr) + if (!dataset.Contains()) { Pskc pskc; @@ -367,16 +368,16 @@ Error ActiveDatasetManager::GenerateLocal(void) SuccessOrExit(error = pskc.GenerateRandom()); } - IgnoreError(dataset.SetTlv(Tlv::kPskc, pskc)); + IgnoreError(dataset.Write(pskc)); } - if (dataset.GetTlv() == nullptr) + if (!dataset.Contains()) { SecurityPolicyTlv tlv; tlv.Init(); tlv.SetSecurityPolicy(Get().GetSecurityPolicy()); - IgnoreError(dataset.SetTlv(tlv)); + IgnoreError(dataset.WriteTlv(tlv)); } SuccessOrExit(error = mLocal.Save(dataset)); @@ -430,17 +431,14 @@ void PendingDatasetManager::ApplyActiveDataset(const Timestamp &aTimestamp, Coap SuccessOrExit(datasetTlv.ReadFromMessage(aMessage, offset)); offset += static_cast(datasetTlv.GetSize()); - IgnoreError(dataset.SetTlv(datasetTlv)); + IgnoreError(dataset.WriteTlv(datasetTlv)); } - // add delay timer tlv - IgnoreError(dataset.SetTlv(Tlv::kDelayTimer, Get().GetDelayTimerMinimal())); + IgnoreError(dataset.Write(Get().GetDelayTimerMinimal())); - // add pending timestamp tlv dataset.SetTimestamp(Dataset::kPending, aTimestamp); IgnoreError(DatasetManager::Save(dataset)); - // reset delay timer StartDelayTimer(); exit: diff --git a/src/core/meshcop/dataset_updater.cpp b/src/core/meshcop/dataset_updater.cpp index 0ba5b1db2..c4b1145cf 100644 --- a/src/core/meshcop/dataset_updater.cpp +++ b/src/core/meshcop/dataset_updater.cpp @@ -123,7 +123,7 @@ void DatasetUpdater::PreparePendingDataset(void) { uint32_t delay = kDefaultDelay; - SuccessOrExit(error = dataset.SetTlv(Tlv::kDelayTimer, delay)); + SuccessOrExit(error = dataset.Write(delay)); } { @@ -133,15 +133,20 @@ void DatasetUpdater::PreparePendingDataset(void) { timestamp = *Get().GetTimestamp(); } + else + { + timestamp.Clear(); + } timestamp.AdvanceRandomTicks(); dataset.SetTimestamp(Dataset::kPending, timestamp); } { - ActiveTimestampTlv *tlv = dataset.GetTlv(); + Timestamp timestamp = dataset.FindTlv(Tlv::kActiveTimestamp)->ReadValueAs(); - tlv->GetTimestamp().AdvanceRandomTicks(); + timestamp.AdvanceRandomTicks(); + dataset.SetTimestamp(Dataset::kActive, timestamp); } SuccessOrExit(error = Get().Save(dataset)); diff --git a/src/core/meshcop/joiner.hpp b/src/core/meshcop/joiner.hpp index 1b42c6ad3..ce430a737 100644 --- a/src/core/meshcop/joiner.hpp +++ b/src/core/meshcop/joiner.hpp @@ -49,9 +49,9 @@ #include "common/message.hpp" #include "common/non_copyable.hpp" #include "mac/mac_types.hpp" -#include "meshcop/dtls.hpp" #include "meshcop/meshcop.hpp" #include "meshcop/meshcop_tlvs.hpp" +#include "meshcop/secure_transport.hpp" #include "thread/discover_scanner.hpp" #include "thread/tmf.hpp" diff --git a/src/core/meshcop/joiner_router.cpp b/src/core/meshcop/joiner_router.cpp index fe58e4201..acadf1a9d 100644 --- a/src/core/meshcop/joiner_router.cpp +++ b/src/core/meshcop/joiner_router.cpp @@ -308,7 +308,7 @@ Coap::Message *JoinerRouter::PrepareJoinerEntrustMessage(void) for (Tlv::Type tlvType : kTlvTypes) { - const Tlv *tlv = dataset.GetTlv(tlvType); + const Tlv *tlv = dataset.FindTlv(tlvType); VerifyOrExit(tlv != nullptr, error = kErrorInvalidState); SuccessOrExit(error = tlv->AppendTo(*message)); diff --git a/src/core/meshcop/meshcop.cpp b/src/core/meshcop/meshcop.cpp index 35ad539b5..d2d14c321 100644 --- a/src/core/meshcop/meshcop.cpp +++ b/src/core/meshcop/meshcop.cpp @@ -334,8 +334,8 @@ Error GeneratePskc(const char *aPassPhrase, memcpy(salt + saltLen, aNetworkName.GetAsCString(), networkNameLen); saltLen += networkNameLen; - otPlatCryptoPbkdf2GenerateKey(reinterpret_cast(aPassPhrase), passphraseLen, salt, saltLen, 16384, - OT_PSKC_MAX_SIZE, aPskc.m8); + error = otPlatCryptoPbkdf2GenerateKey(reinterpret_cast(aPassPhrase), passphraseLen, salt, saltLen, + 16384, OT_PSKC_MAX_SIZE, aPskc.m8); exit: return error; diff --git a/src/core/meshcop/meshcop_leader.cpp b/src/core/meshcop/meshcop_leader.cpp index f2fe3c0f6..64c028d1b 100644 --- a/src/core/meshcop/meshcop_leader.cpp +++ b/src/core/meshcop/meshcop_leader.cpp @@ -58,7 +58,7 @@ RegisterLogModule("MeshCoPLeader"); Leader::Leader(Instance &aInstance) : InstanceLocator(aInstance) , mTimer(aInstance) - , mDelayTimerMinimal(DelayTimerTlv::kDelayTimerMinimal) + , mDelayTimerMinimal(kMinDelayTimer) , mSessionId(Random::NonCrypto::GetUint16()) { } @@ -218,16 +218,13 @@ Error Leader::SetDelayTimerMinimal(uint32_t aDelayTimerMinimal) { Error error = kErrorNone; - VerifyOrExit((aDelayTimerMinimal != 0 && aDelayTimerMinimal < DelayTimerTlv::kDelayTimerDefault), - error = kErrorInvalidArgs); + VerifyOrExit((aDelayTimerMinimal != 0 && aDelayTimerMinimal < kMinDelayTimer), error = kErrorInvalidArgs); mDelayTimerMinimal = aDelayTimerMinimal; exit: return error; } -uint32_t Leader::GetDelayTimerMinimal(void) const { return mDelayTimerMinimal; } - void Leader::HandleTimer(void) { VerifyOrExit(Get().IsLeader()); diff --git a/src/core/meshcop/meshcop_leader.hpp b/src/core/meshcop/meshcop_leader.hpp index 2cf6de13d..50436b3c9 100644 --- a/src/core/meshcop/meshcop_leader.hpp +++ b/src/core/meshcop/meshcop_leader.hpp @@ -95,7 +95,7 @@ class Leader : public InstanceLocator, private NonCopyable * @retval the minimal delay timer (in ms). * */ - uint32_t GetDelayTimerMinimal(void) const; + uint32_t GetDelayTimerMinimal(void) const { return mDelayTimerMinimal; } /** * Sets empty Commissioner Data TLV in the Thread Network Data. @@ -104,6 +104,7 @@ class Leader : public InstanceLocator, private NonCopyable void SetEmptyCommissionerData(void); private: + static constexpr uint32_t kMinDelayTimer = OPENTHREAD_CONFIG_TMF_PENDING_DATASET_MINIMUM_DELAY; // (msec) static constexpr uint32_t kTimeoutLeaderPetition = 50; // TIMEOUT_LEAD_PET (seconds) OT_TOOL_PACKED_BEGIN diff --git a/src/core/meshcop/meshcop_tlvs.cpp b/src/core/meshcop/meshcop_tlvs.cpp index 04063dfa5..573e6b095 100644 --- a/src/core/meshcop/meshcop_tlvs.cpp +++ b/src/core/meshcop/meshcop_tlvs.cpp @@ -45,51 +45,53 @@ namespace MeshCoP { bool Tlv::IsValid(const Tlv &aTlv) { - bool rval = true; + bool isValid = true; + uint8_t minLength = 0; switch (aTlv.GetType()) { - case Tlv::kChannel: - rval = As(aTlv).IsValid(); - break; - case Tlv::kPanId: - rval = As(aTlv).IsValid(); + minLength = sizeof(PanIdTlv::UintValueType); break; - case Tlv::kExtendedPanId: - rval = As(aTlv).IsValid(); + minLength = sizeof(ExtendedPanIdTlv::ValueType); break; - - case Tlv::kNetworkName: - rval = As(aTlv).IsValid(); + case Tlv::kPskc: + minLength = sizeof(PskcTlv::ValueType); break; - case Tlv::kNetworkKey: - rval = As(aTlv).IsValid(); - break; - - case Tlv::kPskc: - rval = As(aTlv).IsValid(); + minLength = sizeof(NetworkKeyTlv::ValueType); break; - case Tlv::kMeshLocalPrefix: - rval = As(aTlv).IsValid(); + minLength = sizeof(MeshLocalPrefixTlv::ValueType); + break; + case Tlv::kChannel: + VerifyOrExit(aTlv.GetLength() >= sizeof(ChannelTlvValue), isValid = false); + isValid = aTlv.ReadValueAs().IsValid(); + break; + case Tlv::kNetworkName: + isValid = As(aTlv).IsValid(); break; case Tlv::kSecurityPolicy: - rval = As(aTlv).IsValid(); + isValid = As(aTlv).IsValid(); break; case Tlv::kChannelMask: - rval = As(aTlv).IsValid(); + isValid = As(aTlv).IsValid(); break; default: break; } - return rval; + if (minLength > 0) + { + isValid = (aTlv.GetLength() >= minLength); + } + +exit: + return isValid; } NameData NetworkNameTlv::GetNetworkName(void) const @@ -142,51 +144,6 @@ void SecurityPolicyTlv::SetSecurityPolicy(const SecurityPolicy &aSecurityPolicy) aSecurityPolicy.GetFlags(mFlags, sizeof(mFlags)); } -bool ChannelTlv::IsValid(void) const -{ - bool ret = false; - - VerifyOrExit(GetLength() == sizeof(*this) - sizeof(Tlv)); - VerifyOrExit(mChannelPage < BitSizeOf(uint32_t)); - VerifyOrExit((1U << mChannelPage) & Radio::kSupportedChannelPages); - VerifyOrExit(Radio::kChannelMin <= GetChannel() && GetChannel() <= Radio::kChannelMax); - ret = true; - -exit: - return ret; -} - -void ChannelTlv::SetChannel(uint16_t aChannel) -{ - uint8_t channelPage = OT_RADIO_CHANNEL_PAGE_0; - -#if OPENTHREAD_CONFIG_RADIO_2P4GHZ_OQPSK_SUPPORT - if ((OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN <= aChannel) && (aChannel <= OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX)) - { - channelPage = OT_RADIO_CHANNEL_PAGE_0; - } -#endif - -#if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT - if ((OT_RADIO_915MHZ_OQPSK_CHANNEL_MIN <= aChannel) && (aChannel <= OT_RADIO_915MHZ_OQPSK_CHANNEL_MAX)) - { - channelPage = OT_RADIO_CHANNEL_PAGE_2; - } -#endif - -#if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT - if ((OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN == aChannel) || - ((OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN < aChannel) && - (aChannel <= OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MAX))) - { - channelPage = OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE; - } -#endif - - SetChannelPage(channelPage); - mChannel = BigEndian::HostSwap16(aChannel); -} - const char *StateTlv::StateToString(State aState) { static const char *const kStateStrings[] = { diff --git a/src/core/meshcop/meshcop_tlvs.hpp b/src/core/meshcop/meshcop_tlvs.hpp index 32121bdb5..b7b2af3fb 100644 --- a/src/core/meshcop/meshcop_tlvs.hpp +++ b/src/core/meshcop/meshcop_tlvs.hpp @@ -54,6 +54,7 @@ #include "net/ip6_address.hpp" #include "radio/radio.hpp" #include "thread/key_manager.hpp" +#include "thread/mle_tlvs.hpp" #include "thread/mle_types.hpp" namespace ot { @@ -245,169 +246,34 @@ typedef UintTlvInfo PeriodTlv; typedef UintTlvInfo ScanDurationTlv; /** - * Defines Commissioner ID TLV constants and type.s + * Defines Commissioner ID TLV constants and types. * */ typedef StringTlvInfo CommissionerIdTlv; /** - * Implements Channel TLV generation and parsing. + * Implements Channel TLV value format. * */ -OT_TOOL_PACKED_BEGIN -class ChannelTlv : public Tlv, public TlvInfo -{ -public: - /** - * Initializes the TLV. - * - */ - void Init(void) - { - SetType(kChannel); - SetLength(sizeof(*this) - sizeof(Tlv)); - } - - /** - * 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; - - /** - * Returns the ChannelPage value. - * - * @returns The ChannelPage value. - * - */ - uint8_t GetChannelPage(void) const { return mChannelPage; } - - /** - * Sets the ChannelPage value. - * - * @param[in] aChannelPage The ChannelPage value. - * - */ - void SetChannelPage(uint8_t aChannelPage) { mChannelPage = aChannelPage; } - - /** - * Returns the Channel value. - * - * @returns The Channel value. - * - */ - uint16_t GetChannel(void) const { return BigEndian::HostSwap16(mChannel); } - - /** - * Sets the Channel value. - * Note: This method also sets the channel page according to the channel value. - * - * @param[in] aChannel The Channel value. - * - */ - void SetChannel(uint16_t aChannel); - -private: - uint8_t mChannelPage; - uint16_t mChannel; -} OT_TOOL_PACKED_END; +typedef Mle::ChannelTlvValue ChannelTlvValue; /** - * Implements PAN ID TLV generation and parsing. + * Defines Channel TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class PanIdTlv : public Tlv, public UintTlvInfo -{ -public: - /** - * Initializes the TLV. - * - */ - void Init(void) - { - SetType(kPanId); - SetLength(sizeof(*this) - sizeof(Tlv)); - } - - /** - * 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); } - - /** - * Returns the PAN ID value. - * - * @returns The PAN ID value. - * - */ - uint16_t GetPanId(void) const { return BigEndian::HostSwap16(mPanId); } - - /** - * Sets the PAN ID value. - * - * @param[in] aPanId The PAN ID value. - * - */ - void SetPanId(uint16_t aPanId) { mPanId = BigEndian::HostSwap16(aPanId); } - -private: - uint16_t mPanId; -} OT_TOOL_PACKED_END; +typedef SimpleTlvInfo ChannelTlv; /** - * Implements Extended PAN ID TLV generation and parsing. + * Defines PAN ID TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class ExtendedPanIdTlv : public Tlv, public SimpleTlvInfo -{ -public: - /** - * Initializes the TLV. - * - */ - void Init(void) - { - SetType(kExtendedPanId); - SetLength(sizeof(*this) - sizeof(Tlv)); - } - - /** - * 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); } +typedef UintTlvInfo PanIdTlv; - /** - * Returns the Extended PAN ID value. - * - * @returns The Extended PAN ID value. - * - */ - const ExtendedPanId &GetExtendedPanId(void) const { return mExtendedPanId; } - - /** - * Sets the Extended PAN ID value. - * - * @param[in] aExtendedPanId An Extended PAN ID value. - * - */ - void SetExtendedPanId(const ExtendedPanId &aExtendedPanId) { mExtendedPanId = aExtendedPanId; } - -private: - ExtendedPanId mExtendedPanId; -} OT_TOOL_PACKED_END; +/** + * Defines Extended PAN ID TLV constants and types. + * + */ +typedef SimpleTlvInfo ExtendedPanIdTlv; /** * Implements Network Name TLV generation and parsing. @@ -457,203 +323,28 @@ class NetworkNameTlv : public Tlv, public TlvInfo } OT_TOOL_PACKED_END; /** - * Implements PSKc TLV generation and parsing. + * Defines PSKc TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class PskcTlv : public Tlv, public SimpleTlvInfo -{ -public: - /** - * Initializes the TLV. - * - */ - void Init(void) - { - SetType(kPskc); - SetLength(sizeof(*this) - sizeof(Tlv)); - } - - /** - * 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); } - - /** - * Returns the PSKc value. - * - * @returns The PSKc value. - * - */ - const Pskc &GetPskc(void) const { return mPskc; } - - /** - * Sets the PSKc value. - * - * @param[in] aPskc A pointer to the PSKc value. - * - */ - void SetPskc(const Pskc &aPskc) { mPskc = aPskc; } - -private: - Pskc mPskc; -} OT_TOOL_PACKED_END; +typedef SimpleTlvInfo PskcTlv; /** - * Implements Network Network Key TLV generation and parsing. + * Defines Network Key TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class NetworkKeyTlv : public Tlv, public SimpleTlvInfo -{ -public: - /** - * Initializes the TLV. - * - */ - void Init(void) - { - SetType(kNetworkKey); - SetLength(sizeof(*this) - sizeof(Tlv)); - } - - /** - * 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); } - - /** - * Returns the Network Network Key value. - * - * @returns The Network Network Key value. - * - */ - const NetworkKey &GetNetworkKey(void) const { return mNetworkKey; } - - /** - * Sets the Network Network Key value. - * - * @param[in] aNetworkKey The Network Network Key. - * - */ - void SetNetworkKey(const NetworkKey &aNetworkKey) { mNetworkKey = aNetworkKey; } - -private: - NetworkKey mNetworkKey; -} OT_TOOL_PACKED_END; +typedef SimpleTlvInfo NetworkKeyTlv; /** - * Implements Network Key Sequence TLV generation and parsing. + * Defines Network Key Sequence TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class NetworkKeySequenceTlv : public Tlv, public UintTlvInfo -{ -public: - /** - * Initializes the TLV. - * - */ - void Init(void) - { - SetType(kNetworkKeySequence); - SetLength(sizeof(*this) - sizeof(Tlv)); - } - - /** - * 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); } - - /** - * Returns the Network Key Sequence value. - * - * @returns The Network Key Sequence value. - * - */ - uint32_t GetNetworkKeySequence(void) const { return BigEndian::HostSwap32(mNetworkKeySequence); } - - /** - * Sets the Network Key Sequence value. - * - * @param[in] aNetworkKeySequence The Network Key Sequence value. - * - */ - void SetNetworkKeySequence(uint32_t aNetworkKeySequence) - { - mNetworkKeySequence = BigEndian::HostSwap32(aNetworkKeySequence); - } - -private: - uint32_t mNetworkKeySequence; -} OT_TOOL_PACKED_END; +typedef UintTlvInfo NetworkKeySequenceTlv; /** - * Implements Mesh Local Prefix TLV generation and parsing. + * Defines Mesh Local Prefix TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class MeshLocalPrefixTlv : public Tlv, public SimpleTlvInfo -{ -public: - /** - * Initializes the TLV. - * - */ - void Init(void) - { - SetType(kMeshLocalPrefix); - SetLength(sizeof(*this) - sizeof(Tlv)); - } - - /** - * 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); } - - /** - * Returns the size (in bytes) of the Mesh Local Prefix field. - * - * @returns The size (in bytes) of the Mesh Local Prefix field (8 bytes). - * - */ - uint8_t GetMeshLocalPrefixLength(void) const { return sizeof(mMeshLocalPrefix); } - - /** - * Returns the Mesh Local Prefix value. - * - * @returns The Mesh Local Prefix value. - * - */ - const Ip6::NetworkPrefix &GetMeshLocalPrefix(void) const { return mMeshLocalPrefix; } - - /** - * Sets the Mesh Local Prefix value. - * - * @param[in] aMeshLocalPrefix A pointer to the Mesh Local Prefix value. - * - */ - void SetMeshLocalPrefix(const Ip6::NetworkPrefix &aMeshLocalPrefix) { mMeshLocalPrefix = aMeshLocalPrefix; } - -private: - Ip6::NetworkPrefix mMeshLocalPrefix; -} OT_TOOL_PACKED_END; +typedef SimpleTlvInfo MeshLocalPrefixTlv; class SteeringData; @@ -868,60 +559,10 @@ class SecurityPolicyTlv : public Tlv, public TlvInfo } OT_TOOL_PACKED_END; /** - * Implements Active Timestamp TLV generation and parsing. + * Defines Active Timestamp TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class ActiveTimestampTlv : public Tlv, public SimpleTlvInfo -{ -public: - /** - * Initializes the TLV. - * - */ - void Init(void) - { - SetType(kActiveTimestamp); - SetLength(sizeof(*this) - sizeof(Tlv)); - mTimestamp.Clear(); - } - - /** - * 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); } - - /** - * Gets the timestamp. - * - * @returns The timestamp. - * - */ - const Timestamp &GetTimestamp(void) const { return mTimestamp; } - - /** - * Returns a reference to the timestamp. - * - * @returns A reference to the timestamp. - * - */ - Timestamp &GetTimestamp(void) { return mTimestamp; } - - /** - * Sets the timestamp. - * - * @param[in] aTimestamp The new timestamp. - * - */ - void SetTimestamp(const Timestamp &aTimestamp) { mTimestamp = aTimestamp; } - -private: - Timestamp mTimestamp; -} OT_TOOL_PACKED_END; +typedef SimpleTlvInfo ActiveTimestampTlv; /** * Implements State TLV generation and parsing. @@ -955,168 +596,22 @@ class StateTlv : public UintTlvInfo }; /** - * Implements Joiner UDP Port TLV generation and parsing. + * Defines Joiner UDP Port TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class JoinerUdpPortTlv : public Tlv, public UintTlvInfo -{ -public: - /** - * Initializes the TLV. - * - */ - void Init(void) - { - SetType(kJoinerUdpPort); - SetLength(sizeof(*this) - sizeof(Tlv)); - } - - /** - * 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); } - - /** - * Returns the UDP Port value. - * - * @returns The UDP Port value. - * - */ - uint16_t GetUdpPort(void) const { return BigEndian::HostSwap16(mUdpPort); } - - /** - * Sets the UDP Port value. - * - * @param[in] aUdpPort The UDP Port value. - * - */ - void SetUdpPort(uint16_t aUdpPort) { mUdpPort = BigEndian::HostSwap16(aUdpPort); } - -private: - uint16_t mUdpPort; -} OT_TOOL_PACKED_END; +typedef UintTlvInfo JoinerUdpPortTlv; /** - * Implements Pending Timestamp TLV generation and parsing. + * Defines Pending Timestamp TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class PendingTimestampTlv : public Tlv, public SimpleTlvInfo -{ -public: - /** - * Initializes the TLV. - * - */ - void Init(void) - { - SetType(kPendingTimestamp); - SetLength(sizeof(*this) - sizeof(Tlv)); - mTimestamp.Clear(); - } - - /** - * 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); } - - /** - * Gets the timestamp. - * - * @returns The timestamp. - * - */ - const Timestamp &GetTimestamp(void) const { return mTimestamp; } - - /** - * Returns a reference to the timestamp. - * - * @returns A reference to the timestamp. - * - */ - Timestamp &GetTimestamp(void) { return mTimestamp; } - - /** - * Sets the timestamp. - * - * @param[in] aTimestamp The new timestamp. - * - */ - void SetTimestamp(const Timestamp &aTimestamp) { mTimestamp = aTimestamp; } - -private: - Timestamp mTimestamp; -} OT_TOOL_PACKED_END; +typedef SimpleTlvInfo PendingTimestampTlv; /** - * Implements Delay Timer TLV generation and parsing. + * Defines Delay Timer TLV constants and types. * */ -OT_TOOL_PACKED_BEGIN -class DelayTimerTlv : public Tlv, public UintTlvInfo -{ -public: - /** - * Initializes the TLV. - * - */ - void Init(void) - { - SetType(kDelayTimer); - SetLength(sizeof(*this) - sizeof(Tlv)); - } - - /** - * 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); } - - /** - * Returns the Delay Timer value. - * - * @returns The Delay Timer value. - * - */ - uint32_t GetDelayTimer(void) const { return BigEndian::HostSwap32(mDelayTimer); } - - /** - * Sets the Delay Timer value. - * - * @param[in] aDelayTimer The Delay Timer value. - * - */ - void SetDelayTimer(uint32_t aDelayTimer) { mDelayTimer = BigEndian::HostSwap32(aDelayTimer); } - - static constexpr uint32_t kMaxDelayTimer = 259200; ///< Maximum delay timer value for a Pending Dataset in seconds - - /** - * Minimum Delay Timer value for a Pending Operational Dataset (ms) - * - */ - static constexpr uint32_t kDelayTimerMinimal = OPENTHREAD_CONFIG_TMF_PENDING_DATASET_MINIMUM_DELAY; - - /** - * Default Delay Timer value for a Pending Operational Dataset (ms) - * - */ - static constexpr uint32_t kDelayTimerDefault = OPENTHREAD_CONFIG_TMF_PENDING_DATASET_DEFAULT_DELAY; - -private: - uint32_t mDelayTimer; -} OT_TOOL_PACKED_END; +typedef UintTlvInfo DelayTimerTlv; // forward declare ChannelMaskTlv class ChannelMaskTlv; @@ -1818,23 +1313,23 @@ class DiscoveryResponseTlv : public Tlv, public TlvInfo * @retval FALSE If the Commercial Commissioning Mode flag is not set. * */ - bool IsCommercialCommissioningMode(void) const { return (mFlags & kCCMMask) != 0; } + bool IsCommercialCommissioningMode(void) const { return (mFlags & kCcmMask) != 0; } /** * Sets the Commercial Commissioning Mode flag. * - * @param[in] aCCM TRUE if set, FALSE otherwise. + * @param[in] aCcm TRUE if set, FALSE otherwise. * */ - void SetCommercialCommissioningMode(bool aCCM) + void SetCommercialCommissioningMode(bool aCcm) { - if (aCCM) + if (aCcm) { - mFlags |= kCCMMask; + mFlags |= kCcmMask; } else { - mFlags &= ~kCCMMask; + mFlags &= ~kCcmMask; } } @@ -1843,8 +1338,8 @@ class DiscoveryResponseTlv : public Tlv, public TlvInfo static constexpr uint8_t kVersionMask = 0xf << kVersionOffset; static constexpr uint8_t kNativeOffset = 3; static constexpr uint8_t kNativeMask = 1 << kNativeOffset; - static constexpr uint8_t kCCMOffset = 2; - static constexpr uint8_t kCCMMask = 1 << kCCMOffset; + static constexpr uint8_t kCcmOffset = 2; + static constexpr uint8_t kCcmMask = 1 << kCcmOffset; uint8_t mFlags; uint8_t mReserved; diff --git a/src/core/meshcop/dtls.cpp b/src/core/meshcop/secure_transport.cpp similarity index 59% rename from src/core/meshcop/dtls.cpp rename to src/core/meshcop/secure_transport.cpp index e3c2275f1..945f701c6 100644 --- a/src/core/meshcop/dtls.cpp +++ b/src/core/meshcop/secure_transport.cpp @@ -31,7 +31,7 @@ * This file implements the necessary hooks for mbedTLS. */ -#include "dtls.hpp" +#include "secure_transport.hpp" #include #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED @@ -52,42 +52,43 @@ #include "instance/instance.hpp" #include "thread/thread_netif.hpp" -#if OPENTHREAD_CONFIG_DTLS_ENABLE +#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE namespace ot { namespace MeshCoP { -RegisterLogModule("Dtls"); +RegisterLogModule("SecTransport"); #if (MBEDTLS_VERSION_NUMBER >= 0x03010000) -const uint16_t Dtls::sGroups[] = {MBEDTLS_SSL_IANA_TLS_GROUP_SECP256R1, MBEDTLS_SSL_IANA_TLS_GROUP_NONE}; +const uint16_t SecureTransport::sGroups[] = {MBEDTLS_SSL_IANA_TLS_GROUP_SECP256R1, MBEDTLS_SSL_IANA_TLS_GROUP_NONE}; #else -const mbedtls_ecp_group_id Dtls::sCurves[] = {MBEDTLS_ECP_DP_SECP256R1, MBEDTLS_ECP_DP_NONE}; +const mbedtls_ecp_group_id SecureTransport::sCurves[] = {MBEDTLS_ECP_DP_SECP256R1, MBEDTLS_ECP_DP_NONE}; #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}; +const uint16_t SecureTransport::sSignatures[] = {MBEDTLS_TLS1_3_SIG_ECDSA_SECP256R1_SHA256, MBEDTLS_TLS1_3_SIG_NONE}; #else -const int Dtls::sHashes[] = {MBEDTLS_MD_SHA256, MBEDTLS_MD_NONE}; +const int SecureTransport::sHashes[] = {MBEDTLS_MD_SHA256, MBEDTLS_MD_NONE}; #endif #endif -Dtls::Dtls(Instance &aInstance, bool aLayerTwoSecurity) +SecureTransport::SecureTransport(Instance &aInstance, bool aLayerTwoSecurity, bool aDatagramTransport) : InstanceLocator(aInstance) , mState(kStateClosed) , mPskLength(0) , mVerifyPeerCertificate(true) - , mTimer(aInstance, Dtls::HandleTimer, this) + , mTimer(aInstance, SecureTransport::HandleTimer, this) , mTimerIntermediate(0) , mTimerSet(false) , mLayerTwoSecurity(aLayerTwoSecurity) + , mDatagramTransport(aDatagramTransport) , mReceiveMessage(nullptr) , mSocket(aInstance) , mMessageSubType(Message::kSubTypeNone) , mMessageDefaultSubType(Message::kSubTypeNone) { -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED mPreSharedKey = nullptr; mPreSharedKeyIdentity = nullptr; @@ -118,12 +119,15 @@ Dtls::Dtls(Instance &aInstance, bool aLayerTwoSecurity) #endif } -void Dtls::FreeMbedtls(void) +void SecureTransport::FreeMbedtls(void) { #if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_COOKIE_C) - mbedtls_ssl_cookie_free(&mCookieCtx); + if (mDatagramTransport) + { + mbedtls_ssl_cookie_free(&mCookieCtx); + } #endif -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED mbedtls_x509_crt_free(&mCaChain); mbedtls_x509_crt_free(&mOwnCert); @@ -134,13 +138,13 @@ void Dtls::FreeMbedtls(void) mbedtls_ssl_free(&mSsl); } -Error Dtls::Open(ReceiveHandler aReceiveHandler, ConnectedHandler aConnectedHandler, void *aContext) +Error SecureTransport::Open(ReceiveHandler aReceiveHandler, ConnectedHandler aConnectedHandler, void *aContext) { Error error; VerifyOrExit(mState == kStateClosed, error = kErrorAlready); - SuccessOrExit(error = mSocket.Open(&Dtls::HandleUdpReceive, this)); + SuccessOrExit(error = mSocket.Open(&SecureTransport::HandleUdpReceive, this)); mConnectedCallback.Set(aConnectedHandler, aContext); mReceiveCallback.Set(aReceiveHandler, aContext); @@ -150,7 +154,7 @@ Error Dtls::Open(ReceiveHandler aReceiveHandler, ConnectedHandler aConnectedHand return error; } -Error Dtls::Connect(const Ip6::SockAddr &aSockAddr) +Error SecureTransport::Connect(const Ip6::SockAddr &aSockAddr) { Error error; @@ -165,24 +169,30 @@ Error Dtls::Connect(const Ip6::SockAddr &aSockAddr) return error; } -void Dtls::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) +void SecureTransport::HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo) { - static_cast(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo)); + static_cast(aContext)->HandleUdpReceive(AsCoreType(aMessage), AsCoreType(aMessageInfo)); } -void Dtls::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +void SecureTransport::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo) { switch (mState) { - case Dtls::kStateClosed: + case SecureTransport::kStateClosed: ExitNow(); - case Dtls::kStateOpen: + case SecureTransport::kStateOpen: + IgnoreError(mSocket.Connect(Ip6::SockAddr(aMessageInfo.GetPeerAddr(), aMessageInfo.GetPeerPort()))); + mMessageInfo.SetPeerAddr(aMessageInfo.GetPeerAddr()); mMessageInfo.SetPeerPort(aMessageInfo.GetPeerPort()); mMessageInfo.SetIsHostInterface(aMessageInfo.IsHostInterface()); - mMessageInfo.SetSockAddr(aMessageInfo.GetSockAddr()); + if (Get().HasUnicastAddress(aMessageInfo.GetSockAddr())) + { + mMessageInfo.SetSockAddr(aMessageInfo.GetSockAddr()); + } + mMessageInfo.SetSockPort(aMessageInfo.GetSockPort()); SuccessOrExit(Setup(false)); @@ -196,7 +206,7 @@ void Dtls::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageI } #ifdef MBEDTLS_SSL_SRV_C - if (mState == Dtls::kStateConnecting) + if (mState == SecureTransport::kStateConnecting) { IgnoreError(SetClientId(mMessageInfo.GetPeerAddr().mFields.m8, sizeof(mMessageInfo.GetPeerAddr().mFields))); } @@ -208,9 +218,9 @@ void Dtls::HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageI return; } -uint16_t Dtls::GetUdpPort(void) const { return mSocket.GetSockName().GetPort(); } +uint16_t SecureTransport::GetUdpPort(void) const { return mSocket.GetSockName().GetPort(); } -Error Dtls::Bind(uint16_t aPort) +Error SecureTransport::Bind(uint16_t aPort) { Error error; @@ -223,7 +233,7 @@ Error Dtls::Bind(uint16_t aPort) return error; } -Error Dtls::Bind(TransportCallback aCallback, void *aContext) +Error SecureTransport::Bind(TransportCallback aCallback, void *aContext) { Error error = kErrorNone; @@ -237,7 +247,7 @@ Error Dtls::Bind(TransportCallback aCallback, void *aContext) return error; } -Error Dtls::Setup(bool aClient) +Error SecureTransport::Setup(bool aClient) { int rval; @@ -248,7 +258,7 @@ Error Dtls::Setup(bool aClient) mbedtls_ssl_init(&mSsl); mbedtls_ssl_config_init(&mConf); -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED mbedtls_x509_crt_init(&mCaChain); mbedtls_x509_crt_init(&mOwnCert); @@ -256,15 +266,20 @@ Error Dtls::Setup(bool aClient) #endif #endif #if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_COOKIE_C) - mbedtls_ssl_cookie_init(&mCookieCtx); + if (mDatagramTransport) + { + mbedtls_ssl_cookie_init(&mCookieCtx); + } #endif - rval = mbedtls_ssl_config_defaults(&mConf, aClient ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER, - MBEDTLS_SSL_TRANSPORT_DATAGRAM, MBEDTLS_SSL_PRESET_DEFAULT); + rval = mbedtls_ssl_config_defaults( + &mConf, aClient ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER, + mDatagramTransport ? MBEDTLS_SSL_TRANSPORT_DATAGRAM : MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); VerifyOrExit(rval == 0); -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE - if (mVerifyPeerCertificate && mCipherSuites[0] == MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8) +#if OPENTHREAD_CONFIG_TLS_API_ENABLE + if (mVerifyPeerCertificate && (mCipherSuites[0] == MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 || + mCipherSuites[0] == MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)) { mbedtls_ssl_conf_authmode(&mConf, MBEDTLS_SSL_VERIFY_REQUIRED); } @@ -313,7 +328,7 @@ Error Dtls::Setup(bool aClient) mbedtls_ssl_conf_dbg(&mConf, HandleMbedtlsDebug, this); #if defined(MBEDTLS_SSL_SRV_C) && defined(MBEDTLS_SSL_COOKIE_C) - if (!aClient) + if (!aClient && mDatagramTransport) { rval = mbedtls_ssl_cookie_setup(&mCookieCtx, Crypto::MbedTls::CryptoSecurePrng, nullptr); VerifyOrExit(rval == 0); @@ -325,17 +340,21 @@ Error Dtls::Setup(bool aClient) rval = mbedtls_ssl_setup(&mSsl, &mConf); VerifyOrExit(rval == 0); - mbedtls_ssl_set_bio(&mSsl, this, &Dtls::HandleMbedtlsTransmit, HandleMbedtlsReceive, nullptr); - mbedtls_ssl_set_timer_cb(&mSsl, this, &Dtls::HandleMbedtlsSetTimer, HandleMbedtlsGetTimer); + mbedtls_ssl_set_bio(&mSsl, this, &SecureTransport::HandleMbedtlsTransmit, HandleMbedtlsReceive, nullptr); + + if (mDatagramTransport) + { + mbedtls_ssl_set_timer_cb(&mSsl, this, &SecureTransport::HandleMbedtlsSetTimer, HandleMbedtlsGetTimer); + } if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8) { rval = mbedtls_ssl_set_hs_ecjpake_password(&mSsl, mPsk, mPskLength); } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE else { - rval = SetApplicationCoapSecureKeys(); + rval = SetApplicationSecureKeys(); } #endif VerifyOrExit(rval == 0); @@ -348,10 +367,10 @@ Error Dtls::Setup(bool aClient) { LogInfo("DTLS started"); } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE else { - LogInfo("Application Coap Secure DTLS started"); + LogInfo("Application Secure (D)TLS started"); } #endif @@ -369,14 +388,16 @@ Error Dtls::Setup(bool aClient) return Crypto::MbedTls::MapError(rval); } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE -int Dtls::SetApplicationCoapSecureKeys(void) +#if OPENTHREAD_CONFIG_TLS_API_ENABLE +int SecureTransport::SetApplicationSecureKeys(void) { int rval = 0; switch (mCipherSuites[0]) { case MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: + case MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: + #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED if (mCaChainSrc != nullptr) { @@ -426,9 +447,9 @@ int Dtls::SetApplicationCoapSecureKeys(void) return rval; } -#endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#endif // OPENTHREAD_CONFIG_TLS_API_ENABLE -void Dtls::Close(void) +void SecureTransport::Close(void) { Disconnect(); @@ -440,7 +461,7 @@ void Dtls::Close(void) mTimer.Stop(); } -void Dtls::Disconnect(void) +void SecureTransport::Disconnect(void) { VerifyOrExit(mState == kStateConnecting || mState == kStateConnected); @@ -457,7 +478,7 @@ void Dtls::Disconnect(void) return; } -Error Dtls::SetPsk(const uint8_t *aPsk, uint8_t aPskLength) +Error SecureTransport::SetPsk(const uint8_t *aPsk, uint8_t aPskLength) { Error error = kErrorNone; @@ -472,13 +493,13 @@ Error Dtls::SetPsk(const uint8_t *aPsk, uint8_t aPskLength) return error; } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED -void Dtls::SetCertificate(const uint8_t *aX509Certificate, - uint32_t aX509CertLength, - const uint8_t *aPrivateKey, - uint32_t aPrivateKeyLength) +void SecureTransport::SetCertificate(const uint8_t *aX509Certificate, + uint32_t aX509CertLength, + const uint8_t *aPrivateKey, + uint32_t aPrivateKeyLength) { OT_ASSERT(aX509CertLength > 0); OT_ASSERT(aX509Certificate != nullptr); @@ -491,11 +512,19 @@ void Dtls::SetCertificate(const uint8_t *aX509Certificate, mPrivateKeySrc = aPrivateKey; mPrivateKeyLength = aPrivateKeyLength; - mCipherSuites[0] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; + if (mDatagramTransport) + { + mCipherSuites[0] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; + } + else + { + mCipherSuites[0] = MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256; + } + mCipherSuites[1] = 0; } -void Dtls::SetCaCertificateChain(const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength) +void SecureTransport::SetCaCertificateChain(const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength) { OT_ASSERT(aX509CaCertChainLength > 0); OT_ASSERT(aX509CaCertificateChain != nullptr); @@ -507,7 +536,10 @@ void Dtls::SetCaCertificateChain(const uint8_t *aX509CaCertificateChain, uint32_ #endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED -void Dtls::SetPreSharedKey(const uint8_t *aPsk, uint16_t aPskLength, const uint8_t *aPskIdentity, uint16_t aPskIdLength) +void SecureTransport::SetPreSharedKey(const uint8_t *aPsk, + uint16_t aPskLength, + const uint8_t *aPskIdentity, + uint16_t aPskIdLength) { OT_ASSERT(aPsk != nullptr); OT_ASSERT(aPskIdentity != nullptr); @@ -525,7 +557,7 @@ void Dtls::SetPreSharedKey(const uint8_t *aPsk, uint16_t aPskLength, const uint8 #endif #if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) -Error Dtls::GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength, size_t aCertBufferSize) +Error SecureTransport::GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength, size_t aCertBufferSize) { Error error = kErrorNone; @@ -550,17 +582,153 @@ Error Dtls::GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLeng } #endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) -#endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +Error SecureTransport::GetPeerSubjectAttributeByOid(const char *aOid, + size_t aOidLength, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength, + int *aAsn1Type) +{ + Error error = kErrorNone; + const mbedtls_asn1_named_data *data; + size_t length; + size_t attributeBufferSize; + mbedtls_x509_crt *peerCert = const_cast(mbedtls_ssl_get_peer_cert(&mSsl)); + + VerifyOrExit(aAttributeLength != nullptr, error = kErrorInvalidArgs); + attributeBufferSize = *aAttributeLength; + *aAttributeLength = 0; + + VerifyOrExit(aAttributeBuffer != nullptr, error = kErrorNoBufs); + VerifyOrExit(peerCert != nullptr, error = kErrorInvalidState); + data = mbedtls_asn1_find_named_data(&peerCert->subject, aOid, aOidLength); + VerifyOrExit(data != nullptr, error = kErrorNotFound); + length = data->val.len; + VerifyOrExit(length <= attributeBufferSize, error = kErrorNoBufs); + + if (aAttributeLength != nullptr) + { + *aAttributeLength = length; + } + + if (aAsn1Type != nullptr) + { + *aAsn1Type = data->val.tag; + } + + memcpy(aAttributeBuffer, data->val.p, length); + +exit: + return error; +} + +Error SecureTransport::GetThreadAttributeFromPeerCertificate(int aThreadOidDescriptor, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength) +{ + const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(&mSsl); + + return GetThreadAttributeFromCertificate(cert, aThreadOidDescriptor, aAttributeBuffer, aAttributeLength); +} + +#endif // defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + +Error SecureTransport::GetThreadAttributeFromOwnCertificate(int aThreadOidDescriptor, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength) +{ + const mbedtls_x509_crt *cert = &mOwnCert; + + return GetThreadAttributeFromCertificate(cert, aThreadOidDescriptor, aAttributeBuffer, aAttributeLength); +} + +Error SecureTransport::GetThreadAttributeFromCertificate(const mbedtls_x509_crt *aCert, + int aThreadOidDescriptor, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength) +{ + Error error = kErrorNotFound; + char oid[9] = {0x2B, 0x06, 0x01, 0x04, 0x01, static_cast(0x82), static_cast(0xDF), + 0x2A, 0x00}; // 1.3.6.1.4.1.44970.0 + mbedtls_x509_buf v3_ext; + unsigned char *p, *end, *endExtData; + size_t len; + size_t attributeBufferSize; + mbedtls_x509_buf extnOid; + int ret, isCritical; + + VerifyOrExit(aAttributeLength != nullptr, error = kErrorInvalidArgs); + attributeBufferSize = *aAttributeLength; + *aAttributeLength = 0; + + VerifyOrExit(aCert != nullptr, error = kErrorInvalidState); + v3_ext = aCert->v3_ext; + p = v3_ext.p; + VerifyOrExit(p != nullptr, error = kErrorInvalidState); + end = p + v3_ext.len; + VerifyOrExit(mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) == 0, + error = kErrorParse); + VerifyOrExit(end == p + len, error = kErrorParse); + + VerifyOrExit(aThreadOidDescriptor < 128, error = kErrorNotImplemented); + oid[sizeof(oid) - 1] = static_cast(aThreadOidDescriptor); + + while (p < end) + { + isCritical = 0; + VerifyOrExit(mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) == 0, + error = kErrorParse); + endExtData = p + len; + + // Get extension ID + VerifyOrExit(mbedtls_asn1_get_tag(&p, endExtData, &extnOid.len, MBEDTLS_ASN1_OID) == 0, error = kErrorParse); + extnOid.tag = MBEDTLS_ASN1_OID; + extnOid.p = p; + p += extnOid.len; + + // Get optional critical + ret = mbedtls_asn1_get_bool(&p, endExtData, &isCritical); + VerifyOrExit(ret == 0 || ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = kErrorParse); + + // Data should be octet string type + VerifyOrExit(mbedtls_asn1_get_tag(&p, endExtData, &len, MBEDTLS_ASN1_OCTET_STRING) == 0, error = kErrorParse); + VerifyOrExit(endExtData == p + len, error = kErrorParse); + + if (isCritical || extnOid.len != sizeof(oid)) + { + continue; + } + + if (memcmp(extnOid.p, oid, sizeof(oid)) == 0) + { + *aAttributeLength = len; + + if (aAttributeBuffer != nullptr) + { + VerifyOrExit(len <= attributeBufferSize, error = kErrorNoBufs); + memcpy(aAttributeBuffer, p, len); + } + + error = kErrorNone; + break; + } + } + +exit: + return error; +} + +#endif // OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_SSL_SRV_C -Error Dtls::SetClientId(const uint8_t *aClientId, uint8_t aLength) +Error SecureTransport::SetClientId(const uint8_t *aClientId, uint8_t aLength) { int rval = mbedtls_ssl_set_client_transport_id(&mSsl, aClientId, aLength); return Crypto::MbedTls::MapError(rval); } #endif -Error Dtls::Send(Message &aMessage, uint16_t aLength) +Error SecureTransport::Send(Message &aMessage, uint16_t aLength) { Error error = kErrorNone; uint8_t buffer[kApplicationDataMaxLength]; @@ -583,7 +751,7 @@ Error Dtls::Send(Message &aMessage, uint16_t aLength) return error; } -void Dtls::Receive(Message &aMessage) +void SecureTransport::Receive(Message &aMessage) { mReceiveMessage = &aMessage; @@ -592,28 +760,28 @@ void Dtls::Receive(Message &aMessage) mReceiveMessage = nullptr; } -int Dtls::HandleMbedtlsTransmit(void *aContext, const unsigned char *aBuf, size_t aLength) +int SecureTransport::HandleMbedtlsTransmit(void *aContext, const unsigned char *aBuf, size_t aLength) { - return static_cast(aContext)->HandleMbedtlsTransmit(aBuf, aLength); + return static_cast(aContext)->HandleMbedtlsTransmit(aBuf, aLength); } -int Dtls::HandleMbedtlsTransmit(const unsigned char *aBuf, size_t aLength) +int SecureTransport::HandleMbedtlsTransmit(const unsigned char *aBuf, size_t aLength) { Error error; int rval = 0; if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8) { - LogDebg("HandleMbedtlsTransmit"); + LogDebg("HandleMbedtlsTransmit DTLS"); } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE else { - LogDebg("ApplicationCoapSecure HandleMbedtlsTransmit"); + LogDebg("HandleMbedtlsTransmit TLS"); } #endif - error = HandleDtlsSend(aBuf, static_cast(aLength), mMessageSubType); + error = HandleSecureTransportSend(aBuf, static_cast(aLength), mMessageSubType); // Restore default sub type. mMessageSubType = mMessageDefaultSubType; @@ -637,23 +805,23 @@ int Dtls::HandleMbedtlsTransmit(const unsigned char *aBuf, size_t aLength) return rval; } -int Dtls::HandleMbedtlsReceive(void *aContext, unsigned char *aBuf, size_t aLength) +int SecureTransport::HandleMbedtlsReceive(void *aContext, unsigned char *aBuf, size_t aLength) { - return static_cast(aContext)->HandleMbedtlsReceive(aBuf, aLength); + return static_cast(aContext)->HandleMbedtlsReceive(aBuf, aLength); } -int Dtls::HandleMbedtlsReceive(unsigned char *aBuf, size_t aLength) +int SecureTransport::HandleMbedtlsReceive(unsigned char *aBuf, size_t aLength) { int rval; if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8) { - LogDebg("HandleMbedtlsReceive"); + LogDebg("HandleMbedtlsReceive DTLS"); } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE else { - LogDebg("ApplicationCoapSecure HandleMbedtlsReceive"); + LogDebg("HandleMbedtlsReceive TLS"); } #endif @@ -672,9 +840,12 @@ int Dtls::HandleMbedtlsReceive(unsigned char *aBuf, size_t aLength) return rval; } -int Dtls::HandleMbedtlsGetTimer(void *aContext) { return static_cast(aContext)->HandleMbedtlsGetTimer(); } +int SecureTransport::HandleMbedtlsGetTimer(void *aContext) +{ + return static_cast(aContext)->HandleMbedtlsGetTimer(); +} -int Dtls::HandleMbedtlsGetTimer(void) +int SecureTransport::HandleMbedtlsGetTimer(void) { int rval; @@ -682,10 +853,10 @@ int Dtls::HandleMbedtlsGetTimer(void) { LogDebg("HandleMbedtlsGetTimer"); } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE else { - LogDebg("ApplicationCoapSecure HandleMbedtlsGetTimer"); + LogDebg("HandleMbedtlsGetTimer"); } #endif @@ -709,21 +880,21 @@ int Dtls::HandleMbedtlsGetTimer(void) return rval; } -void Dtls::HandleMbedtlsSetTimer(void *aContext, uint32_t aIntermediate, uint32_t aFinish) +void SecureTransport::HandleMbedtlsSetTimer(void *aContext, uint32_t aIntermediate, uint32_t aFinish) { - static_cast(aContext)->HandleMbedtlsSetTimer(aIntermediate, aFinish); + static_cast(aContext)->HandleMbedtlsSetTimer(aIntermediate, aFinish); } -void Dtls::HandleMbedtlsSetTimer(uint32_t aIntermediate, uint32_t aFinish) +void SecureTransport::HandleMbedtlsSetTimer(uint32_t aIntermediate, uint32_t aFinish) { if (mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8) { - LogDebg("SetTimer"); + LogDebg("SetTimer DTLS"); } -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE else { - LogDebg("ApplicationCoapSecure SetTimer"); + LogDebg("SetTimer TLS"); } #endif @@ -742,42 +913,42 @@ void Dtls::HandleMbedtlsSetTimer(uint32_t aIntermediate, uint32_t aFinish) #if (MBEDTLS_VERSION_NUMBER >= 0x03000000) -void Dtls::HandleMbedtlsExportKeys(void *aContext, - mbedtls_ssl_key_export_type aType, - const unsigned char *aMasterSecret, - size_t aMasterSecretLen, - const unsigned char aClientRandom[32], - const unsigned char aServerRandom[32], - mbedtls_tls_prf_types aTlsPrfType) +void SecureTransport::HandleMbedtlsExportKeys(void *aContext, + mbedtls_ssl_key_export_type aType, + const unsigned char *aMasterSecret, + size_t aMasterSecretLen, + const unsigned char aClientRandom[32], + const unsigned char aServerRandom[32], + mbedtls_tls_prf_types aTlsPrfType) { - static_cast(aContext)->HandleMbedtlsExportKeys(aType, aMasterSecret, aMasterSecretLen, aClientRandom, - aServerRandom, aTlsPrfType); + static_cast(aContext)->HandleMbedtlsExportKeys(aType, aMasterSecret, aMasterSecretLen, + aClientRandom, aServerRandom, aTlsPrfType); } -void Dtls::HandleMbedtlsExportKeys(mbedtls_ssl_key_export_type aType, - const unsigned char *aMasterSecret, - size_t aMasterSecretLen, - const unsigned char aClientRandom[32], - const unsigned char aServerRandom[32], - mbedtls_tls_prf_types aTlsPrfType) +void SecureTransport::HandleMbedtlsExportKeys(mbedtls_ssl_key_export_type aType, + const unsigned char *aMasterSecret, + size_t aMasterSecretLen, + const unsigned char aClientRandom[32], + const unsigned char aServerRandom[32], + mbedtls_tls_prf_types aTlsPrfType) { Crypto::Sha256::Hash kek; Crypto::Sha256 sha256; - unsigned char keyBlock[kDtlsKeyBlockSize]; - unsigned char randBytes[2 * kDtlsRandomBufferSize]; + unsigned char keyBlock[kSecureTransportKeyBlockSize]; + unsigned char randBytes[2 * kSecureTransportRandomBufferSize]; VerifyOrExit(mCipherSuites[0] == MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8); VerifyOrExit(aType == MBEDTLS_SSL_KEY_EXPORT_TLS12_MASTER_SECRET); - memcpy(randBytes, aServerRandom, kDtlsRandomBufferSize); - memcpy(randBytes + kDtlsRandomBufferSize, aClientRandom, kDtlsRandomBufferSize); + memcpy(randBytes, aServerRandom, kSecureTransportRandomBufferSize); + memcpy(randBytes + kSecureTransportRandomBufferSize, aClientRandom, kSecureTransportRandomBufferSize); // Retrieve the Key block from Master secret mbedtls_ssl_tls_prf(aTlsPrfType, aMasterSecret, aMasterSecretLen, "key expansion", randBytes, sizeof(randBytes), keyBlock, sizeof(keyBlock)); sha256.Start(); - sha256.Update(keyBlock, kDtlsKeyBlockSize); + sha256.Update(keyBlock, kSecureTransportKeyBlockSize); sha256.Finish(kek); LogDebg("Generated KEK"); @@ -789,22 +960,22 @@ void Dtls::HandleMbedtlsExportKeys(mbedtls_ssl_key_export_type aType, #else -int Dtls::HandleMbedtlsExportKeys(void *aContext, - const unsigned char *aMasterSecret, - const unsigned char *aKeyBlock, - size_t aMacLength, - size_t aKeyLength, - size_t aIvLength) +int SecureTransport::HandleMbedtlsExportKeys(void *aContext, + const unsigned char *aMasterSecret, + const unsigned char *aKeyBlock, + size_t aMacLength, + size_t aKeyLength, + size_t aIvLength) { - return static_cast(aContext)->HandleMbedtlsExportKeys(aMasterSecret, aKeyBlock, aMacLength, aKeyLength, - aIvLength); + return static_cast(aContext)->HandleMbedtlsExportKeys(aMasterSecret, aKeyBlock, aMacLength, + aKeyLength, aIvLength); } -int Dtls::HandleMbedtlsExportKeys(const unsigned char *aMasterSecret, - const unsigned char *aKeyBlock, - size_t aMacLength, - size_t aKeyLength, - size_t aIvLength) +int SecureTransport::HandleMbedtlsExportKeys(const unsigned char *aMasterSecret, + const unsigned char *aKeyBlock, + size_t aMacLength, + size_t aKeyLength, + size_t aIvLength) { OT_UNUSED_VARIABLE(aMasterSecret); @@ -826,12 +997,12 @@ int Dtls::HandleMbedtlsExportKeys(const unsigned char *aMasterSecret, #endif // (MBEDTLS_VERSION_NUMBER >= 0x03000000) -void Dtls::HandleTimer(Timer &aTimer) +void SecureTransport::HandleTimer(Timer &aTimer) { - static_cast(static_cast(aTimer).GetContext())->HandleTimer(); + static_cast(static_cast(aTimer).GetContext())->HandleTimer(); } -void Dtls::HandleTimer(void) +void SecureTransport::HandleTimer(void) { switch (mState) { @@ -852,7 +1023,7 @@ void Dtls::HandleTimer(void) } } -void Dtls::Process(void) +void SecureTransport::Process(void) { uint8_t buf[OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN]; bool shouldDisconnect = false; @@ -938,12 +1109,12 @@ void Dtls::Process(void) } } -void Dtls::HandleMbedtlsDebug(void *aContext, int aLevel, const char *aFile, int aLine, const char *aStr) +void SecureTransport::HandleMbedtlsDebug(void *aContext, int aLevel, const char *aFile, int aLine, const char *aStr) { - static_cast(aContext)->HandleMbedtlsDebug(aLevel, aFile, aLine, aStr); + static_cast(aContext)->HandleMbedtlsDebug(aLevel, aFile, aLine, aStr); } -void Dtls::HandleMbedtlsDebug(int aLevel, const char *aFile, int aLine, const char *aStr) +void SecureTransport::HandleMbedtlsDebug(int aLevel, const char *aFile, int aLine, const char *aStr) { OT_UNUSED_VARIABLE(aStr); OT_UNUSED_VARIABLE(aFile); @@ -970,7 +1141,9 @@ void Dtls::HandleMbedtlsDebug(int aLevel, const char *aFile, int aLine, const ch } } -Error Dtls::HandleDtlsSend(const uint8_t *aBuf, uint16_t aLength, Message::SubType aMessageSubType) +Error SecureTransport::HandleSecureTransportSend(const uint8_t *aBuf, + uint16_t aLength, + Message::SubType aMessageSubType) { Error error = kErrorNone; ot::Message *message = nullptr; @@ -1004,4 +1177,4 @@ Error Dtls::HandleDtlsSend(const uint8_t *aBuf, uint16_t aLength, Message::SubTy } // namespace MeshCoP } // namespace ot -#endif // OPENTHREAD_CONFIG_DTLS_ENABLE +#endif // OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE diff --git a/src/core/meshcop/dtls.hpp b/src/core/meshcop/secure_transport.hpp similarity index 65% rename from src/core/meshcop/dtls.hpp rename to src/core/meshcop/secure_transport.hpp index 2a4df728d..ef51f5e28 100644 --- a/src/core/meshcop/dtls.hpp +++ b/src/core/meshcop/secure_transport.hpp @@ -31,17 +31,33 @@ * This file includes definitions for using mbedTLS. */ -#ifndef DTLS_HPP_ -#define DTLS_HPP_ +#ifndef SECURE_TRANSPORT_HPP_ +#define SECURE_TRANSPORT_HPP_ #include "openthread-core-config.h" +#ifdef OPENTHREAD_CONFIG_TLS_API_ENABLE +#error `OPENTHREAD_CONFIG_TLS_API_ENABLE` must not be defined directly, it is determined from `COAP_SECURE_API_ENABLE` and `BLE_TCAT_ENABLE` +#endif + +#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE || OPENTHREAD_CONFIG_BLE_TCAT_ENABLE +#define OPENTHREAD_CONFIG_TLS_API_ENABLE 1 +#else +#define OPENTHREAD_CONFIG_TLS_API_ENABLE 0 +#endif + #include #include #include #include -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE +#ifndef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED +#error OPENTHREAD_CONFIG_BLE_TCAT_ENABLE requires MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED +#endif +#endif + +#if OPENTHREAD_CONFIG_TLS_API_ENABLE #if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) #include #endif @@ -67,19 +83,20 @@ namespace ot { namespace MeshCoP { -class Dtls : public InstanceLocator +class SecureTransport : public InstanceLocator { public: static constexpr uint8_t kPskMaxLength = 32; ///< Maximum PSK length. /** - * Initializes the DTLS object. + * Initializes the SecureTransport object. * * @param[in] aInstance A reference to the OpenThread instance. * @param[in] aLayerTwoSecurity Specifies whether to use layer two security or not. + * @param[in] aDatagramTransport Specifies if dtls of tls connection should be used. * */ - explicit Dtls(Instance &aInstance, bool aLayerTwoSecurity); + explicit SecureTransport(Instance &aInstance, bool aLayerTwoSecurity, bool aDatagramTransport = true); /** * Pointer is called when a connection is established or torn down. @@ -91,7 +108,7 @@ class Dtls : public InstanceLocator typedef void (*ConnectedHandler)(void *aContext, bool aConnected); /** - * Pointer is called when data is received from the DTLS session. + * Pointer is called when data is received from the session. * * @param[in] aContext A pointer to application-specific context. * @param[in] aBuf A pointer to the received data buffer. @@ -111,14 +128,14 @@ class Dtls : public InstanceLocator typedef Error (*TransportCallback)(void *aContext, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); /** - * Opens the DTLS socket. + * Opens the socket. * - * @param[in] aReceiveHandler A pointer to a function that is called to receive DTLS payload. + * @param[in] aReceiveHandler A pointer to a function that is called to receive payload. * @param[in] aConnectedHandler A pointer to a function that is called when connected or disconnected. * @param[in] aContext A pointer to arbitrary context information. * * @retval kErrorNone Successfully opened the socket. - * @retval kErrorAlready The DTLS is already open. + * @retval kErrorAlready The connection is already open. * */ Error Open(ReceiveHandler aReceiveHandler, ConnectedHandler aConnectedHandler, void *aContext); @@ -128,8 +145,8 @@ class Dtls : public InstanceLocator * * @param[in] aPort The port to bind. * - * @retval kErrorNone Successfully bound the DTLS socket. - * @retval kErrorInvalidState The DTLS socket is not open. + * @retval kErrorNone Successfully bound the socket. + * @retval kErrorInvalidState The socket is not open. * @retval kErrorAlready Already bound. * */ @@ -144,20 +161,20 @@ class Dtls : public InstanceLocator uint16_t GetUdpPort(void) const; /** - * Binds this DTLS with a transport callback. + * Binds with a transport callback. * * @param[in] aCallback A pointer to a function for sending messages. * @param[in] aContext A pointer to arbitrary context information. * - * @retval kErrorNone Successfully bound the DTLS socket. - * @retval kErrorInvalidState The DTLS socket is not open. + * @retval kErrorNone Successfully bound the socket. + * @retval kErrorInvalidState The socket is not open. * @retval kErrorAlready Already bound. * */ Error Bind(TransportCallback aCallback, void *aContext); /** - * Establishes a DTLS session. + * Establishes a secure session. * * For CoAP Secure API do first: * Set X509 Pk and Cert for use DTLS mode ECDHE ECDSA with AES 128 CCM 8 or @@ -165,38 +182,38 @@ class Dtls : public InstanceLocator * * @param[in] aSockAddr A reference to the remote sockaddr. * - * @retval kErrorNone Successfully started DTLS handshake. - * @retval kErrorInvalidState The DTLS socket is not open. + * @retval kErrorNone Successfully started handshake. + * @retval kErrorInvalidState The socket is not open. * */ Error Connect(const Ip6::SockAddr &aSockAddr); /** - * Indicates whether or not the DTLS session is active. + * Indicates whether or not the session is active. * - * @retval TRUE If DTLS session is active. - * @retval FALSE If DTLS session is not active. + * @retval TRUE If session is active. + * @retval FALSE If session is not active. * */ bool IsConnectionActive(void) const { return mState >= kStateConnecting; } /** - * Indicates whether or not the DTLS session is connected. + * Indicates whether or not the session is connected. * - * @retval TRUE The DTLS session is connected. - * @retval FALSE The DTLS session is not connected. + * @retval TRUE The session is connected. + * @retval FALSE The session is not connected. * */ bool IsConnected(void) const { return mState == kStateConnected; } /** - * Disconnects the DTLS session. + * Disconnects the session. * */ void Disconnect(void); /** - * Closes the DTLS socket. + * Closes the socket. * */ void Close(void); @@ -212,10 +229,10 @@ class Dtls : public InstanceLocator */ Error SetPsk(const uint8_t *aPsk, uint8_t aPskLength); -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED /** - * Sets the Pre-Shared Key (PSK) for DTLS sessions- + * Sets the Pre-Shared Key (PSK) for sessions- * identified by a PSK. * * DTLS mode "PSK with AES 128 CCM 8" for Application CoAPS. @@ -280,8 +297,84 @@ class Dtls : public InstanceLocator Error GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength, size_t aCertBufferSize); #endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) /** - * Set the authentication mode for a dtls connection. + * Returns an attribute value identified by its OID from the subject + * of the peer x509 certificate. The peer OID is provided in binary format. + * The attribute length is set if the attribute was successfully read or zero + * if unsuccessful. The ASN.1 type as is set as defineded in the ITU-T X.690 standard + * if the attribute was successfully read. + * + * @param[in] aOid A pointer to the OID to be found. + * @param[in] aOidLength The length of the OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[in,out] aAttributeLength On input, the size the max size of @p aAttributeBuffer. + * On output, the length of the attribute written to the buffer. + * @param[out] aAsn1Type A pointer to the ASN.1 type of the attribute written to the buffer. + * + * @retval kErrorInvalidState Not connected yet. + * @retval kErrorInvalidArgs Invalid attribute length. + * @retval kErrorNone Successfully read attribute. + * @retval kErrorNoBufs Insufficient memory for storing the attribute value. + * + */ + Error GetPeerSubjectAttributeByOid(const char *aOid, + size_t aOidLength, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength, + int *aAsn1Type); + + /** + * Returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of + * the peer x509 certificate, where the last digit x is set to aThreadOidDescriptor. + * The attribute length is set if the attribute was successfully read or zero if unsuccessful. + * Requires a connection to be active. + * + * @param[in] aThreadOidDescriptor The last digit of the Thread attribute OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[in,out] aAttributeLength On input, the size the max size of @p aAttributeBuffer. + * On output, the length of the attribute written to the buffer. + * + * @retval kErrorNone Successfully read attribute. + * @retval kErrorInvalidArgs Invalid attribute length. + * @retval kErrorNotFound The requested attribute was not found. + * @retval kErrorNoBufs Insufficient memory for storing the attribute value. + * @retval kErrorInvalidState Not connected yet. + * @retval kErrorNotImplemented The value of aThreadOidDescriptor is >127. + * @retval kErrorParse The certificate extensions could not be parsed. + * + */ + Error GetThreadAttributeFromPeerCertificate(int aThreadOidDescriptor, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength); +#endif // defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + + /** + * Returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of + * the own x509 certificate, where the last digit x is set to aThreadOidDescriptor. + * The attribute length is set if the attribute was successfully read or zero if unsuccessful. + * Requires a connection to be active. + * + * @param[in] aThreadOidDescriptor The last digit of the Thread attribute OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[in,out] aAttributeLength On input, the size the max size of @p aAttributeBuffer. + * On output, the length of the attribute written to the buffer. + * + * @retval kErrorNone Successfully read attribute. + * @retval kErrorInvalidArgs Invalid attribute length. + * @retval kErrorNotFound The requested attribute was not found. + * @retval kErrorNoBufs Insufficient memory for storing the attribute value. + * @retval kErrorInvalidState Not connected yet. + * @retval kErrorNotImplemented The value of aThreadOidDescriptor is >127. + * @retval kErrorParse The certificate extensions could not be parsed. + * + */ + Error GetThreadAttributeFromOwnCertificate(int aThreadOidDescriptor, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength); + + /** + * Set the authentication mode for a connection. * * Disable or enable the verification of peer certificate. * Must called before start. @@ -290,7 +383,7 @@ class Dtls : public InstanceLocator * */ void SetSslAuthMode(bool aVerifyPeerCertificate) { mVerifyPeerCertificate = aVerifyPeerCertificate; } -#endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#endif // OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_SSL_SRV_C /** @@ -306,19 +399,19 @@ class Dtls : public InstanceLocator #endif /** - * Sends data within the DTLS session. + * Sends data within the session. * - * @param[in] aMessage A message to send via DTLS. + * @param[in] aMessage A message to send via connection. * @param[in] aLength Number of bytes in the data buffer. * - * @retval kErrorNone Successfully sent the data via the DTLS session. + * @retval kErrorNone Successfully sent the data via the session. * @retval kErrorNoBufs A message is too long. * */ Error Send(Message &aMessage, uint16_t aLength); /** - * Provides a received DTLS message to the DTLS object. + * Provides a received message to the SecureTransport object. * * @param[in] aMessage A reference to the message. * @@ -335,9 +428,9 @@ class Dtls : public InstanceLocator void SetDefaultMessageSubType(Message::SubType aMessageSubType) { mMessageDefaultSubType = aMessageSubType; } /** - * Returns the DTLS session's peer address. + * Returns the session's peer address. * - * @return DTLS session's message info. + * @return session's message info. * */ const Ip6::MessageInfo &GetMessageInfo(void) const { return mMessageInfo; } @@ -349,34 +442,39 @@ class Dtls : public InstanceLocator { kStateClosed, // UDP socket is closed. kStateOpen, // UDP socket is open. - kStateInitializing, // The DTLS service is initializing. - kStateConnecting, // The DTLS service is establishing a connection. - kStateConnected, // The DTLS service has a connection established. - kStateCloseNotify, // The DTLS service is closing a connection. + kStateInitializing, // The service is initializing. + kStateConnecting, // The service is establishing a connection. + kStateConnected, // The service has a connection established. + kStateCloseNotify, // The service is closing a connection. }; static constexpr uint32_t kGuardTimeNewConnectionMilli = 2000; -#if !OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if !OPENTHREAD_CONFIG_TLS_API_ENABLE static constexpr uint16_t kApplicationDataMaxLength = 1152; #else static constexpr uint16_t kApplicationDataMaxLength = OPENTHREAD_CONFIG_DTLS_APPLICATION_DATA_MAX_LENGTH; #endif - static constexpr size_t kDtlsKeyBlockSize = 40; - static constexpr size_t kDtlsRandomBufferSize = 32; + static constexpr size_t kSecureTransportKeyBlockSize = 40; + static constexpr size_t kSecureTransportRandomBufferSize = 32; void FreeMbedtls(void); Error Setup(bool aClient); -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE /** * Set keys and/or certificates for dtls session dependent of used cipher suite. * * @retval mbedtls error, 0 if successfully. * */ - int SetApplicationCoapSecureKeys(void); + int SetApplicationSecureKeys(void); + + Error GetThreadAttributeFromCertificate(const mbedtls_x509_crt *aCert, + int aThreadOidDescriptor, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength); #endif static void HandleMbedtlsDebug(void *aContext, int aLevel, const char *aFile, int aLine, const char *aStr); @@ -434,8 +532,8 @@ class Dtls : public InstanceLocator static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo); - void HandleDtlsReceive(const uint8_t *aBuf, uint16_t aLength); - Error HandleDtlsSend(const uint8_t *aBuf, uint16_t aLength, Message::SubType aMessageSubType); + void HandleSecureTransportReceive(const uint8_t *aBuf, uint16_t aLength); + Error HandleSecureTransportSend(const uint8_t *aBuf, uint16_t aLength, Message::SubType aMessageSubType); void Process(void); @@ -459,7 +557,7 @@ class Dtls : public InstanceLocator #endif #endif -#if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE +#if OPENTHREAD_CONFIG_TLS_API_ENABLE #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED const uint8_t *mCaChainSrc; uint32_t mCaChainLength; @@ -494,16 +592,19 @@ class Dtls : public InstanceLocator bool mTimerSet : 1; bool mLayerTwoSecurity : 1; + bool mDatagramTransport : 1; Message *mReceiveMessage; Callback mConnectedCallback; Callback mReceiveCallback; + void *mContext; Ip6::MessageInfo mMessageInfo; Ip6::Udp::Socket mSocket; Callback mTransportCallback; + void *mTransportContext; Message::SubType mMessageSubType; Message::SubType mMessageDefaultSubType; @@ -512,4 +613,4 @@ class Dtls : public InstanceLocator } // namespace MeshCoP } // namespace ot -#endif // DTLS_HPP_ +#endif // SECURE_TRANSPORT_HPP_ diff --git a/src/core/meshcop/tcat_agent.cpp b/src/core/meshcop/tcat_agent.cpp new file mode 100644 index 000000000..f8df7737e --- /dev/null +++ b/src/core/meshcop/tcat_agent.cpp @@ -0,0 +1,516 @@ +/* + * 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 TCAT Agent service. + */ + +#include "tcat_agent.hpp" + +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE + +#include + +#include "common/array.hpp" +#include "common/code_utils.hpp" +#include "common/debug.hpp" +#include "common/encoding.hpp" +#include "common/locator_getters.hpp" +#include "common/string.hpp" +#include "instance/instance.hpp" +#include "radio/radio.hpp" +#include "thread/thread_netif.hpp" +#include "thread/uri_paths.hpp" +#include "utils/otns.hpp" + +namespace ot { +namespace MeshCoP { + +RegisterLogModule("TcatAgent"); + +bool TcatAgent::VendorInfo::IsValid(void) const +{ + return mProvisioningUrl == nullptr || IsValidUtf8String(mProvisioningUrl) || mPskdString != nullptr; +} + +TcatAgent::TcatAgent(Instance &aInstance) + : InstanceLocator(aInstance) + , mVendorInfo(nullptr) + , mCurrentApplicationProtocol(kApplicationProtocolNone) + , mState(kStateDisabled) + , mAlreadyCommissioned(false) + , mCommissionerHasNetworkName(false) + , mCommissionerHasDomainName(false) + , mCommissionerHasExtendedPanId(false) +{ + mJoinerPskd.Clear(); + mCurrentServiceName[0] = 0; +} + +Error TcatAgent::Start(const TcatAgent::VendorInfo &aVendorInfo, + AppDataReceiveCallback aAppDataReceiveCallback, + JoinCallback aHandler, + void *aContext) +{ + Error error = kErrorNone; + + LogInfo("Starting"); + + VerifyOrExit(aVendorInfo.IsValid(), error = kErrorInvalidArgs); + SuccessOrExit(error = mJoinerPskd.SetFrom(aVendorInfo.mPskdString)); + + mAppDataReceiveCallback.Set(aAppDataReceiveCallback, aContext); + mJoinCallback.Set(aHandler, aContext); + + mVendorInfo = &aVendorInfo; + mCurrentApplicationProtocol = kApplicationProtocolNone; + mState = kStateEnabled; + mAlreadyCommissioned = false; + +exit: + LogError("start TCAT agent", error); + return error; +} + +void TcatAgent::Stop(void) +{ + mCurrentApplicationProtocol = kApplicationProtocolNone; + mState = kStateDisabled; + mAlreadyCommissioned = false; + mAppDataReceiveCallback.Clear(); + mJoinCallback.Clear(); + LogInfo("TCAT agent stopped"); +} + +Error TcatAgent::Connected(MeshCoP::SecureTransport &aTlsContext) +{ + size_t len; + Error error; + + VerifyOrExit(IsEnabled(), error = kErrorInvalidState); + len = sizeof(mCommissionerAuthorizationField); + SuccessOrExit( + error = aTlsContext.GetThreadAttributeFromPeerCertificate( + kCertificateAuthorizationField, reinterpret_cast(&mCommissionerAuthorizationField), &len)); + VerifyOrExit(len == sizeof(mCommissionerAuthorizationField), error = kErrorParse); + VerifyOrExit((mCommissionerAuthorizationField.mHeader & kCommissionerFlag) == 1, error = kErrorParse); + + len = sizeof(mDeviceAuthorizationField); + SuccessOrExit(error = aTlsContext.GetThreadAttributeFromOwnCertificate( + kCertificateAuthorizationField, reinterpret_cast(&mDeviceAuthorizationField), &len)); + VerifyOrExit(len == sizeof(mDeviceAuthorizationField), error = kErrorParse); + VerifyOrExit((mDeviceAuthorizationField.mHeader & kCommissionerFlag) == 0, error = kErrorParse); + + mCommissionerHasDomainName = false; + mCommissionerHasNetworkName = false; + mCommissionerHasExtendedPanId = false; + + len = sizeof(mCommissionerDomainName) - 1; + if (aTlsContext.GetThreadAttributeFromPeerCertificate( + kCertificateDomainName, reinterpret_cast(&mCommissionerDomainName), &len) == kErrorNone) + { + mCommissionerDomainName.m8[len] = '\0'; + mCommissionerHasDomainName = true; + } + + len = sizeof(mCommissionerNetworkName) - 1; + if (aTlsContext.GetThreadAttributeFromPeerCertificate( + kCertificateNetworkName, reinterpret_cast(&mCommissionerNetworkName), &len) == kErrorNone) + { + mCommissionerNetworkName.m8[len] = '\0'; + mCommissionerHasNetworkName = true; + } + + len = sizeof(mCommissionerExtendedPanId); + if (aTlsContext.GetThreadAttributeFromPeerCertificate( + kCertificateExtendedPanId, reinterpret_cast(&mCommissionerExtendedPanId), &len) == kErrorNone) + { + if (len == sizeof(mCommissionerExtendedPanId)) + { + mCommissionerHasExtendedPanId = true; + } + } + + mCurrentApplicationProtocol = kApplicationProtocolNone; + mCurrentServiceName[0] = 0; + mState = kStateConnected; + mAlreadyCommissioned = Get().IsCommissioned(); + LogInfo("TCAT agent connected"); + +exit: + return error; +} + +void TcatAgent::Disconnected(void) +{ + mCurrentApplicationProtocol = kApplicationProtocolNone; + mAlreadyCommissioned = false; + + if (mState != kStateDisabled) + { + mState = kStateEnabled; + } + + LogInfo("TCAT agent disconnected"); +} + +bool TcatAgent::CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags, + CommandClassFlags aDeviceCommandClassFlags, + Dataset *aDataset) const +{ + bool authorized = false; + bool additionalDeviceRequirementMet = false; + bool domainNamesMatch = false; + bool networkNamesMatch = false; + bool extendedPanIdsMatch = false; + + VerifyOrExit(IsConnected()); + VerifyOrExit(aCommissionerCommandClassFlags & kAccessFlag); + + if (aDeviceCommandClassFlags & kAccessFlag) + { + additionalDeviceRequirementMet = true; + } + + if (aDeviceCommandClassFlags & kPskdFlag) + { + additionalDeviceRequirementMet = true; + } + + if (aDeviceCommandClassFlags & kPskcFlag) + { + additionalDeviceRequirementMet = true; + } + + if (mCommissionerHasNetworkName || mCommissionerHasExtendedPanId) + { + Dataset::Info datasetInfo; + Error datasetError = kErrorNone; + + if (aDataset == nullptr) + { + datasetError = Get().Read(datasetInfo); + } + else + { + aDataset->ConvertTo(datasetInfo); + } + + if (datasetError == kErrorNone) + { + if (datasetInfo.IsNetworkNamePresent() && mCommissionerHasNetworkName && + (datasetInfo.GetNetworkName() == mCommissionerNetworkName)) + { + networkNamesMatch = true; + } + + if (datasetInfo.IsExtendedPanIdPresent() && mCommissionerHasExtendedPanId && + (datasetInfo.GetExtendedPanId() == mCommissionerExtendedPanId)) + { + extendedPanIdsMatch = true; + } + } + } + + if (!networkNamesMatch) + { + VerifyOrExit((aCommissionerCommandClassFlags & kNetworkNameFlag) == 0); + } + else if (aDeviceCommandClassFlags & kNetworkNameFlag) + { + additionalDeviceRequirementMet = true; + } + + if (!extendedPanIdsMatch) + { + VerifyOrExit((aCommissionerCommandClassFlags & kExtendedPanIdFlag) == 0); + } + else if (aDeviceCommandClassFlags & kExtendedPanIdFlag) + { + additionalDeviceRequirementMet = true; + } + +#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) + VerifyOrExit((aCommissionerCommandClassFlags & kThreadDomainFlag) == 0); +#endif + + if (!domainNamesMatch) + { + VerifyOrExit((aCommissionerCommandClassFlags & kThreadDomainFlag) == 0); + } + else if (aDeviceCommandClassFlags & kThreadDomainFlag) + { + additionalDeviceRequirementMet = true; + } + + if (additionalDeviceRequirementMet) + { + authorized = true; + } + +exit: + return authorized; +} + +bool TcatAgent::IsCommandClassAuthorized(CommandClass aCommandClass) const +{ + bool authorized = false; + + switch (aCommandClass) + { + case kGeneral: + authorized = true; + break; + + case kCommissioning: + authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mCommissioningFlags, + mDeviceAuthorizationField.mCommissioningFlags, nullptr); + break; + + case kExtraction: + authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mExtractionFlags, + mDeviceAuthorizationField.mExtractionFlags, nullptr); + break; + + case kTlvDecommissioning: + authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mDecommissioningFlags, + mDeviceAuthorizationField.mDecommissioningFlags, nullptr); + break; + + case kApplication: + authorized = CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mApplicationFlags, + mDeviceAuthorizationField.mApplicationFlags, nullptr); + break; + + case kInvalid: + authorized = false; + break; + } + + return authorized; +} + +TcatAgent::CommandClass TcatAgent::GetCommandClass(uint8_t aTlvType) const +{ + static constexpr int kGeneralTlvs = 0x1F; + static constexpr int kCommissioningTlvs = 0x3F; + static constexpr int kExtractionTlvs = 0x5F; + static constexpr int kTlvDecommissioningTlvs = 0x7F; + static constexpr int kApplicationTlvs = 0x9F; + + if (aTlvType <= kGeneralTlvs) + { + return kGeneral; + } + else if (aTlvType <= kCommissioningTlvs) + { + return kCommissioning; + } + else if (aTlvType <= kExtractionTlvs) + { + return kExtraction; + } + else if (aTlvType <= kTlvDecommissioningTlvs) + { + return kTlvDecommissioning; + } + else if (aTlvType <= kApplicationTlvs) + { + return kApplication; + } + else + { + return kInvalid; + } +} + +bool TcatAgent::CanProcessTlv(uint8_t aTlvType) const +{ + CommandClass tlvCommandClass = GetCommandClass(aTlvType); + return IsCommandClassAuthorized(tlvCommandClass); +} + +Error TcatAgent::HandleSingleTlv(const Message &aIncommingMessage, Message &aOutgoingMessage) +{ + Error error = kErrorParse; + ot::Tlv tlv; + uint16_t offset = aIncommingMessage.GetOffset(); + uint16_t length; + bool response = false; + + VerifyOrExit(IsConnected(), error = kErrorInvalidState); + SuccessOrExit(error = aIncommingMessage.Read(offset, tlv)); + + if (tlv.IsExtended()) + { + ot::ExtendedTlv extTlv; + SuccessOrExit(error = aIncommingMessage.Read(offset, extTlv)); + length = extTlv.GetLength(); + offset += sizeof(ot::ExtendedTlv); + } + else + { + length = tlv.GetLength(); + offset += sizeof(ot::Tlv); + } + + if (!CanProcessTlv(tlv.GetType())) + { + error = kErrorRejected; + } + else + { + switch (tlv.GetType()) + { + case kTlvDisconnect: + error = kErrorAbort; + break; + + case kTlvSetActiveOperationalDataset: + error = HandleSetActiveOperationalDataset(aIncommingMessage, offset, length); + break; + + case kTlvStartThreadInterface: + error = HandleStartThreadInterface(); + break; + + case kTlvStopThreadInterface: + error = otThreadSetEnabled(&GetInstance(), false); + break; + + case kTlvSendApplicationData: + LogInfo("Application data len:%d, offset:%d", length, offset); + mAppDataReceiveCallback.InvokeIfSet(&GetInstance(), &aIncommingMessage, offset, + MapEnum(mCurrentApplicationProtocol), mCurrentServiceName); + response = true; + error = kErrorNone; + break; + + default: + error = kErrorInvalidCommand; + } + } + + if (!response) + { + StatusCode statusCode; + + switch (error) + { + case kErrorNone: + statusCode = kStatusSuccess; + break; + + case kErrorInvalidState: + statusCode = kStatusUndefined; + break; + + case kErrorParse: + statusCode = kStatusParseError; + break; + + case kErrorInvalidCommand: + statusCode = kStatusUnsupported; + break; + + case kErrorRejected: + statusCode = kStatusUnauthorized; + break; + + case kErrorNotImplemented: + statusCode = kStatusUnsupported; + break; + + default: + statusCode = kStatusGeneralError; + break; + } + + SuccessOrExit(error = ot::Tlv::Append(aOutgoingMessage, statusCode)); + } + +exit: + return error; +} + +Error TcatAgent::HandleSetActiveOperationalDataset(const Message &aIncommingMessage, uint16_t aOffset, uint16_t aLength) +{ + Dataset dataset; + otOperationalDatasetTlvs datasetTlvs; + Error error; + + SuccessOrExit(error = dataset.ReadFromMessage(aIncommingMessage, aOffset, aLength)); + + if (!CheckCommandClassAuthorizationFlags(mCommissionerAuthorizationField.mApplicationFlags, + mDeviceAuthorizationField.mApplicationFlags, &dataset)) + { + error = kErrorRejected; + ExitNow(); + } + + dataset.ConvertTo(datasetTlvs); + error = Get().Save(datasetTlvs); + +exit: + return error; +} + +Error TcatAgent::HandleStartThreadInterface(void) +{ + Error error; + Dataset::Info datasetInfo; + + VerifyOrExit(Get().Read(datasetInfo) == kErrorNone, error = kErrorInvalidState); + VerifyOrExit(datasetInfo.IsNetworkKeyPresent(), error = kErrorInvalidState); + +#if OPENTHREAD_CONFIG_LINK_RAW_ENABLE + VerifyOrExit(!Get().IsEnabled(), error = kErrorInvalidState); +#endif + + Get().Up(); + error = Get().Start(); + +exit: + return error; +} + +#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) +void TcatAgent::LogError(const char *aActionText, Error aError) +{ + if (aError != kErrorNone) + { + LogWarn("Failed to %s: %s", aActionText, ErrorToString(aError)); + } +} +#endif + +} // namespace MeshCoP +} // namespace ot + +#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE diff --git a/src/core/meshcop/tcat_agent.hpp b/src/core/meshcop/tcat_agent.hpp new file mode 100644 index 000000000..d2dd9f340 --- /dev/null +++ b/src/core/meshcop/tcat_agent.hpp @@ -0,0 +1,374 @@ +/* + * 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 + * Implements the TCAT Agent service. + */ + +#ifndef TCAT_AGENT_HPP_ +#define TCAT_AGENT_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE + +#include +#include + +#include "common/as_core_type.hpp" +#include "common/callback.hpp" +#include "common/locator.hpp" +#include "common/log.hpp" +#include "common/message.hpp" +#include "common/non_copyable.hpp" +#include "mac/mac_types.hpp" +#include "meshcop/dataset.hpp" +#include "meshcop/meshcop.hpp" +#include "meshcop/meshcop_tlvs.hpp" +#include "meshcop/secure_transport.hpp" + +namespace ot { + +namespace Ble { +class BleSecure; +} + +namespace MeshCoP { + +class TcatAgent : public InstanceLocator, private NonCopyable +{ +public: + /** + * Pointer to call when application data was received over the TLS connection. + * + * Please see otHandleTcatApplicationDataReceive for details. + * + */ + typedef otHandleTcatApplicationDataReceive AppDataReceiveCallback; + + /** + * Pointer to call to notify the completion of a join operation. + * + * Please see otHandleTcatJoin for details. + * + */ + typedef otHandleTcatJoin JoinCallback; + + /** + * Represents a TCAT command class. + * + */ + enum CommandClass + { + kGeneral = OT_TCAT_COMMAND_CLASS_GENERAL, ///< TCAT commands related to general operations + kCommissioning = OT_TCAT_COMMAND_CLASS_COMMISSIONING, ///< TCAT commands related to commissioning + kExtraction = OT_TCAT_COMMAND_CLASS_EXTRACTION, ///< TCAT commands related to key extraction + kTlvDecommissioning = OT_TCAT_COMMAND_CLASS_DECOMMISSIONING, ///< TCAT commands related to de-commissioning + kApplication = OT_TCAT_COMMAND_CLASS_APPLICATION, ///< TCAT commands related to application layer + kInvalid ///< TCAT command belongs to reserved pool or is invalid + }; + + /** + * The certificate authorization field header type to indicate the type and version of the certificate. + * + */ + enum CertificateAuthorizationFieldHeader : uint8_t + { + kCommissionerFlag = 1 << 0, ///< TCAT commissioner ('1') or device ('0') + kHeaderVersion = 0xD0, ///< Header version (3 bits) + }; + + /** + * The command class flag type to indicate which requirements apply for a given command class. + * + */ + enum CommandClassFlags : uint8_t + { + kAccessFlag = 1 << 0, ///< Access to the command class (device: without without additional requirements). + kPskdFlag = 1 << 1, ///< Access requires proof-of-possession of the device's PSKd + kNetworkNameFlag = 1 << 2, ///< Access requires matching network name + kExtendedPanIdFlag = 1 << 3, ///< Access requires matching XPANID + kThreadDomainFlag = 1 << 4, ///< Access requires matching XPANID + kPskcFlag = 1 << 5, ///< Access requires proof-of-possession of the device's PSKc + }; + + /** + * + * Represents a data structure for storing TCAT Commissioner authorization information in the + * certificate ASN.1 field 1.3.6.1.4.1.44970.3. + * + */ + OT_TOOL_PACKED_BEGIN + struct CertificateAuthorizationField + { + CertificateAuthorizationFieldHeader mHeader; ///< Typ and version + CommandClassFlags mCommissioningFlags; ///< Command class flags + CommandClassFlags mExtractionFlags; ///< Command class flags + CommandClassFlags mDecommissioningFlags; ///< Command class flags + CommandClassFlags mApplicationFlags; ///< Command class flags + + } OT_TOOL_PACKED_END; + + typedef CertificateAuthorizationField CertificateAuthorizationField; + + /** + * Represents the TCAT vendor information. + * + */ + class VendorInfo : public otTcatVendorInfo + { + public: + /** + * Validates whether the TCAT vendor information is valid. + * + * @returns Whether the parameters are valid. + * + */ + bool IsValid(void) const; + }; + + /** + * TCAT TLV Types. + * + */ + enum TlvType : uint8_t + { + // Command Class General + kTlvResponseWithStatus = 1, ///< TCAT response with status value TLV + kTlvResponseWithPayload = 2, ///< TCAT response with payload TLV + kTlvResponseEvent = 3, ///< TCAT response event TLV (reserved) + kTlvGetNetworkName = 8, ///< TCAT network name query TLV + kTlvDisconnect = 9, ///< TCAT disconnect request TLV + kTlvPing = 10, ///< TCAT ping request TLV + kTlvGetDeviceId = 11, ///< TCAT device ID query TLV + kTlvGetExtendedPanID = 12, ///< TCAT extended PAN ID query TLV + kTlvPresentPskdHash = 16, ///< TCAT commissioner rights elevation request TLV using PSKd hash + kTlvPresentPskcHash = 17, ///< TCAT commissioner rights elevation request TLV using PSKc hash + kTlvPresentInstallCodeHash = 18, ///< TCAT commissioner rights elevation request TLV using install code + kTlvRequestRandomNumChallenge = 19, ///< TCAT random number challenge query TLV + kTlvRequestPskdHash = 20, ///< TCAT PSKd hash request TLV + + // Command Class Commissioning + kTlvSetActiveOperationalDataset = 32, ///< TCAT active operational dataset TLV + kTlvSetActiveOperationalDatasetAlternative = 33, ///< TCAT active operational dataset alternative #1 TLV + kTlvGetProvisioningTlvs = 36, ///< TCAT provisioning TLVs query TLV + kTlvGetCommissionerCertificate = 37, ///< TCAT commissioner certificate query TLV + kTlvGetDiagnosticTlvs = 38, ///< TCAT diagnostics TLVs query TLV + kTlvStartThreadInterface = 39, ///< TCAT start thread interface request TLV + kTlvStopThreadInterface = 40, ///< TCAT stop thread interface request TLV + + // Command Class Extraction + kTlvGetActiveOperationalDataset = 64, ///< TCAT active oerational dataset query TLV + kTlvGetActiveOperationalDatasetAlternative = 65, ///< TCAT active oerational dataset alternative #1 query TLV + + // Command Class Decommissioning + kTlvDecommission = 96, ///< TCAT decommission request TLV + + // Command Class Application + kTlvSelectApplicationLayerUdp = 128, ///< TCAT select UDP protocol application layer request TLV + kTlvSelectApplicationLayerTcp = 129, ///< TCAT select TCP protocol application layer request TLV + kTlvSendApplicationData = 130, ///< TCAT send application data TLV + kTlvSendVendorSpecificData = 159, ///< TCAT send vendor specific command or data TLV + + // Command Class CCM + kTlvSetLDevIdOperationalCert = 160, ///< TCAT LDevID operational certificate TLV + kTlvSetLDevIdPrivateKey = 161, ///< TCAT LDevID operational certificate pricate key TLV + kTlvSetDomainCaCert = 162, ///< TCAT domain CA certificate TLV + }; + + /** + * TCAT Response Types. + * + */ + enum StatusCode : uint8_t + { + kStatusSuccess = OT_TCAT_STATUS_SUCCESS, ///< Command or request was successfully processed + kStatusUnsupported = OT_TCAT_STATUS_UNSUPPORTED, ///< Requested command or received TLV is not supported + kStatusParseError = OT_TCAT_STATUS_PARSE_ERROR, ///< Request / command could not be parsed correctly + kStatusValueError = OT_TCAT_STATUS_VALUE_ERROR, ///< The value of the transmitted TLV has an error + kStatusGeneralError = OT_TCAT_STATUS_GENERAL_ERROR, ///< An error not matching any other category occurred + kStatusBusy = OT_TCAT_STATUS_BUSY, ///< Command cannot be executed because the resource is busy + kStatusUndefined = OT_TCAT_STATUS_UNDEFINED, ///< The requested value, data or service is not defined + ///< (currently) or not present + kStatusHashError = OT_TCAT_STATUS_HASH_ERROR, ///< The hash value presented by the commissioner was incorrect + kStatusUnauthorized = + OT_TCAT_STATUS_UNAUTHORIZED, ///< Sender does not have sufficient authorization for the given command + }; + + /** + * Represents TCAT application protocol. + * + */ + enum TcatApplicationProtocol : uint8_t + { + kApplicationProtocolNone = + OT_TCAT_APPLICATION_PROTOCOL_NONE, ///< Message which has been sent without activating the TCAT agent + kApplicationProtocolUdp = OT_TCAT_APPLICATION_PROTOCOL_STATUS, ///< Message directed to a UDP service + kApplicationProtocolTcp = OT_TCAT_APPLICATION_PROTOCOL_TCP, ///< Message directed to a TCP service + }; + + /** + * Represents a TCAT certificate V3 extension attribute (OID 1.3.6.1.4.1.44970.x). + * + */ + enum TcatCertificateAttribute + { + kCertificateDomainName = 1, + kCertificateAuthorizationField = 3, + kCertificateNetworkName = 4, + kCertificateExtendedPanId = 5, + }; + + /** + * Represents TCAT status. + * + */ + enum State : uint8_t + { + kStateDisabled, + kStateEnabled, + kStateConnected, + }; + + /** + * Initializes the Joiner object. + * + * @param[in] aInstance A reference to the OpenThread instance. + * + */ + explicit TcatAgent(Instance &aInstance); + + /** + * Enables the TCAT protocol. + * + * @param[in] aVendorInfo A pointer to the Vendor Information (must remain valid after the method + * call, may be NULL). + * @param[in] aAppDataReceiveCallback A pointer to a function that is called when the user data is received. + * @param[in] aHandler A pointer to a function that is called when the join operation completes. + * @param[in] aContext A context pointer. + * + * @retval kErrorNone Successfully started the TCAT agent. + * @retval kErrorInvalidArgs The aVendorInfo is invalid. + * + */ + Error Start(const VendorInfo &aVendorInfo, + AppDataReceiveCallback aAppDataReceiveCallback, + JoinCallback aHandler, + void *aContext); + + /** + * Stops the TCAT protocol. + * + */ + void Stop(void); + + /** + * Indicates whether or not the TCAT agent is enabled. + * + * @retval TRUE The TCAT agent is enabled. + * @retval FALSE The TCAT agent is not enabled. + * + */ + bool IsEnabled(void) const { return mState != kStateDisabled; } + + /** + * Indicates whether or not the TCAT agent is connected. + * + * @retval TRUE The TCAT agent is connected. + * @retval FALSE The TCAT agent is not connected. + * + */ + bool IsConnected(void) const { return mState == kStateConnected; } + + /** + * Indicates whether or not a command class is authorized. + * + * @param[in] aCommandClass Command class to subject for authorization check. + * + * @retval TRUE The command class is authorized. + * @retval FALSE The command class is not authorized. + * + */ + bool IsCommandClassAuthorized(CommandClass aCommandClass) const; + +private: + Error Connected(MeshCoP::SecureTransport &aTlsContext); + void Disconnected(void); + + Error HandleSingleTlv(const Message &aIncommingMessage, Message &aOutgoingMessage); + Error HandleSetActiveOperationalDataset(const Message &aIncommingMessage, uint16_t aOffset, uint16_t aLength); + Error HandleStartThreadInterface(void); + +#if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) + void LogError(const char *aActionText, Error aError); +#else + void LogError(const char *, Error) {} +#endif + + bool CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags, + CommandClassFlags aDeviceCommandClassFlags, + Dataset *aDataset) const; + bool CanProcessTlv(uint8_t aTlvType) const; + CommandClass GetCommandClass(uint8_t aTlvType) const; + + static constexpr uint16_t kJoinerUdpPort = OPENTHREAD_CONFIG_JOINER_UDP_PORT; + + JoinerPskd mJoinerPskd; + const VendorInfo *mVendorInfo; + Callback mJoinCallback; + Callback mAppDataReceiveCallback; + CertificateAuthorizationField mCommissionerAuthorizationField; + CertificateAuthorizationField mDeviceAuthorizationField; + TcatApplicationProtocol mCurrentApplicationProtocol; + NetworkName mCommissionerNetworkName; + NetworkName mCommissionerDomainName; + ExtendedPanId mCommissionerExtendedPanId; + char mCurrentServiceName[OT_TCAT_MAX_SERVICE_NAME_LENGTH + 1]; + State mState; + bool mAlreadyCommissioned : 1; + bool mCommissionerHasNetworkName : 1; + bool mCommissionerHasDomainName : 1; + bool mCommissionerHasExtendedPanId : 1; + + friend class Ble::BleSecure; +}; + +} // namespace MeshCoP + +DefineCoreType(otTcatVendorInfo, MeshCoP::TcatAgent::VendorInfo); + +DefineMapEnum(otTcatApplicationProtocol, MeshCoP::TcatAgent::TcatApplicationProtocol); + +typedef UintTlvInfo ResponseWithStatusTlv; + +} // namespace ot + +#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE + +#endif // TCAT_AGENT_HPP_ diff --git a/src/core/net/ip6.cpp b/src/core/net/ip6.cpp index b7759ef65..4f3b077e0 100644 --- a/src/core/net/ip6.cpp +++ b/src/core/net/ip6.cpp @@ -1366,11 +1366,19 @@ const Address *Ip6::SelectSourceAddress(const Address &aDestination) const continue; } } - else if (addr.mPreferred && !bestAddr->mPreferred) + else if (addr.mPreferred != bestAddr->mPreferred) { // Rule 3: Avoid deprecated addresses - bestAddr = &addr; - bestMatchLen = matchLen; + + if (addr.mPreferred) + { + bestAddr = &addr; + bestMatchLen = matchLen; + } + else + { + continue; + } } else if (matchLen > bestMatchLen) { @@ -1448,7 +1456,9 @@ Error Ip6::RouteLookup(const Address &aSource, const Address &aDestination) cons #if OPENTHREAD_CONFIG_IP6_BR_COUNTERS_ENABLE void Ip6::UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLength, bool aIsInbound) { - otPacketsAndBytes *counter = nullptr; + static constexpr uint8_t kPrefixLength = 48; + otPacketsAndBytes *counter = nullptr; + otPacketsAndBytes *internetCounter = nullptr; VerifyOrExit(!aHeader.GetSource().IsLinkLocal()); VerifyOrExit(!aHeader.GetDestination().IsLinkLocal()); @@ -1458,7 +1468,10 @@ void Ip6::UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLe if (aIsInbound) { VerifyOrExit(!Get().HasUnicastAddress(aHeader.GetSource())); - + if (!aHeader.GetSource().MatchesPrefix(aHeader.GetDestination().GetPrefix().m8, kPrefixLength)) + { + internetCounter = &mBorderRoutingCounters.mInboundInternet; + } if (aHeader.GetDestination().IsMulticast()) { VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal()); @@ -1472,7 +1485,10 @@ void Ip6::UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLe else { VerifyOrExit(!Get().HasUnicastAddress(aHeader.GetDestination())); - + if (!aHeader.GetSource().MatchesPrefix(aHeader.GetDestination().GetPrefix().m8, kPrefixLength)) + { + internetCounter = &mBorderRoutingCounters.mOutboundInternet; + } if (aHeader.GetDestination().IsMulticast()) { VerifyOrExit(aHeader.GetDestination().IsMulticastLargerThanRealmLocal()); @@ -1491,6 +1507,11 @@ void Ip6::UpdateBorderRoutingCounters(const Header &aHeader, uint16_t aMessageLe counter->mPackets += 1; counter->mBytes += aMessageLength; } + if (internetCounter) + { + internetCounter->mPackets += 1; + internetCounter->mBytes += aMessageLength; + } } #endif diff --git a/src/core/net/netif.cpp b/src/core/net/netif.cpp index ff39891e7..1b739afca 100644 --- a/src/core/net/netif.cpp +++ b/src/core/net/netif.cpp @@ -377,15 +377,29 @@ void Netif::AddUnicastAddress(UnicastAddress &aAddress) return; } -void Netif::RemoveUnicastAddress(const UnicastAddress &aAddress) +void Netif::RemoveUnicastAddress(UnicastAddress &aAddress) { SuccessOrExit(mUnicastAddresses.Remove(aAddress)); + aAddress.mSrpRegistered = false; SignalUnicastAddressChange(kAddressRemoved, aAddress); exit: return; } +void Netif::UpdatePreferredFlagOn(UnicastAddress &aAddress, bool aPreferred) +{ + VerifyOrExit(HasUnicastAddress(aAddress)); + VerifyOrExit(aAddress.mPreferred != aPreferred); + + SignalUnicastAddressChange(kAddressRemoved, aAddress); + aAddress.mPreferred = aPreferred; + SignalUnicastAddressChange(kAddressAdded, aAddress); + +exit: + return; +} + void Netif::SignalUnicastAddressChange(AddressEvent aEvent, const UnicastAddress &aAddress) { Event event; @@ -443,9 +457,10 @@ Error Netif::AddExternalUnicastAddress(const UnicastAddress &aAddress) entry = mExtUnicastAddressPool.Allocate(); VerifyOrExit(entry != nullptr, error = kErrorNoBufs); - *entry = aAddress; - entry->mRloc = false; - entry->mMeshLocal = false; + *entry = aAddress; + entry->mRloc = false; + entry->mMeshLocal = false; + entry->mSrpRegistered = false; mUnicastAddresses.Push(*entry); SignalUnicastAddressChange(kAddressAdded, *entry); @@ -504,6 +519,7 @@ void Netif::ApplyNewMeshLocalPrefix(void) if (address.mMeshLocal) { SignalUnicastAddressChange(kAddressRemoved, address); + address.mSrpRegistered = false; address.GetAddress().SetPrefix(Get().GetMeshLocalPrefix()); SignalUnicastAddressChange(kAddressAdded, address); } diff --git a/src/core/net/netif.hpp b/src/core/net/netif.hpp index 0e5057134..663eb218e 100644 --- a/src/core/net/netif.hpp +++ b/src/core/net/netif.hpp @@ -397,6 +397,14 @@ class Netif : public InstanceLocator, private NonCopyable */ const LinkedList &GetUnicastAddresses(void) const { return mUnicastAddresses; } + /** + * Returns the linked list of unicast addresses. + * + * @returns The linked list of unicast addresses. + * + */ + LinkedList &GetUnicastAddresses(void) { return mUnicastAddresses; } + /** * Adds a unicast address to the network interface. * @@ -421,7 +429,19 @@ class Netif : public InstanceLocator, private NonCopyable * @param[in] aAddress A reference to the unicast address. * */ - void RemoveUnicastAddress(const UnicastAddress &aAddress); + void RemoveUnicastAddress(UnicastAddress &aAddress); + + /** + * Updates the preferred flag on a previously added (internal to OpenThread core) unicast address. + * + * If the address is not added to the network interface or the current preferred flag of @p aAddress is the same as + * the given @p aPreferred, no action is performed. + * + * @param[in] aAddress The unicast address + * @param[in] aPreferred The new value for preferred flag. + * + */ + void UpdatePreferredFlagOn(UnicastAddress &aAddress, bool aPreferred); /** * Indicates whether or not an address is assigned to the interface. diff --git a/src/core/net/srp_client.cpp b/src/core/net/srp_client.cpp index 9663d9299..cbc24c4ff 100644 --- a/src/core/net/srp_client.cpp +++ b/src/core/net/srp_client.cpp @@ -244,13 +244,13 @@ Client::Client(Instance &aInstance) , mState(kStateStopped) , mTxFailureRetryCount(0) , mShouldRemoveKeyLease(false) - , mAutoHostAddressAddedMeshLocal(false) , mSingleServiceMode(false) #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE , mServiceKeyRecordEnabled(false) , mUseShortLeaseOption(false) #endif , mUpdateMessageId(0) + , mAutoHostAddressCount(0) , mRetryWaitInterval(kMinRetryWaitInterval) , mTtl(0) , mLease(0) @@ -421,20 +421,11 @@ void Client::HandleNotifierEvents(Events aEvents) } #endif - if (mHostInfo.IsAutoAddressEnabled()) + if (aEvents.ContainsAny(kEventIp6AddressAdded | kEventIp6AddressRemoved | kEventThreadMeshLocalAddrChanged) && + ShouldUpdateHostAutoAddresses()) { - Events::Flags eventFlags = (kEventIp6AddressAdded | kEventIp6AddressRemoved); - - if (mAutoHostAddressAddedMeshLocal) - { - eventFlags |= kEventThreadMeshLocalAddrChanged; - } - - if (aEvents.ContainsAny(eventFlags)) - { - IgnoreError(UpdateHostInfoStateOnAddressChange()); - UpdateState(); - } + IgnoreError(UpdateHostInfoStateOnAddressChange()); + UpdateState(); } } @@ -494,6 +485,13 @@ Error Client::EnableAutoHostAddress(void) VerifyOrExit(!mHostInfo.IsAutoAddressEnabled()); SuccessOrExit(error = UpdateHostInfoStateOnAddressChange()); + for (Ip6::Netif::UnicastAddress &unicastAddress : Get().GetUnicastAddresses()) + { + unicastAddress.mSrpRegistered = false; + } + + mAutoHostAddressCount = 0; + mHostInfo.EnableAutoAddress(); UpdateState(); @@ -515,6 +513,68 @@ Error Client::SetHostAddresses(const Ip6::Address *aAddresses, uint8_t aNumAddre return error; } +bool Client::ShouldUpdateHostAutoAddresses(void) const +{ + bool shouldUpdate = false; + uint16_t registeredCount = 0; + Ip6::Netif::UnicastAddress &ml64 = Get().GetMeshLocal64UnicastAddress(); + + VerifyOrExit(mHostInfo.IsAutoAddressEnabled()); + + // Check all addresses on `ThreadNetif` excluding the mesh local + // EID (`ml64`). If any address should be registered but is not, + // or if any address was registered earlier but no longer should + // be, the host information needs to be re-registered to update + // the addresses. If there is no eligible address, then `ml64` + // should be registered, so its status is checked. Finally, the + // number of addresses that should be registered is verified + // against the previous value `mAutoHostAddressCount` to handle + // the case where an earlier registered address is now removed. + + for (const Ip6::Netif::UnicastAddress &unicastAddress : Get().GetUnicastAddresses()) + { + if (&unicastAddress == &ml64) + { + continue; + } + + if (ShouldHostAutoAddressRegister(unicastAddress) != unicastAddress.mSrpRegistered) + { + ExitNow(shouldUpdate = true); + } + + if (unicastAddress.mSrpRegistered) + { + registeredCount++; + } + } + + if (registeredCount == 0) + { + ExitNow(shouldUpdate = !ml64.mSrpRegistered); + } + + shouldUpdate = (registeredCount != mAutoHostAddressCount); + +exit: + return shouldUpdate; +} + +bool Client::ShouldHostAutoAddressRegister(const Ip6::Netif::UnicastAddress &aUnicastAddress) const +{ + bool shouldRegister = false; + + VerifyOrExit(aUnicastAddress.mValid); + VerifyOrExit(aUnicastAddress.mPreferred); + VerifyOrExit(!aUnicastAddress.GetAddress().IsLinkLocal()); + VerifyOrExit(!Get().IsMeshLocalAddress(aUnicastAddress.GetAddress())); + + shouldRegister = true; + +exit: + return shouldRegister; +} + Error Client::UpdateHostInfoStateOnAddressChange(void) { Error error = kErrorNone; @@ -1240,27 +1300,33 @@ Error Client::AppendHostDescriptionInstruction(Message &aMessage, Info &aInfo) if (mHostInfo.IsAutoAddressEnabled()) { - // Append all addresses on Thread netif excluding link-local and - // mesh-local addresses. If no address is appended, we include - // the mesh local address. + // Append all preferred addresses on Thread netif excluding link-local + // and mesh-local addresses. If no address is appended, we include + // the mesh local EID. - mAutoHostAddressAddedMeshLocal = true; + mAutoHostAddressCount = 0; - for (const Ip6::Netif::UnicastAddress &unicastAddress : Get().GetUnicastAddresses()) + for (Ip6::Netif::UnicastAddress &unicastAddress : Get().GetUnicastAddresses()) { - if (unicastAddress.GetAddress().IsLinkLocal() || - Get().IsMeshLocalAddress(unicastAddress.GetAddress())) + if (ShouldHostAutoAddressRegister(unicastAddress)) { - continue; + SuccessOrExit(error = AppendAaaaRecord(unicastAddress.GetAddress(), aMessage, aInfo)); + unicastAddress.mSrpRegistered = true; + mAutoHostAddressCount++; + } + else + { + unicastAddress.mSrpRegistered = false; } - - SuccessOrExit(error = AppendAaaaRecord(unicastAddress.GetAddress(), aMessage, aInfo)); - mAutoHostAddressAddedMeshLocal = false; } - if (mAutoHostAddressAddedMeshLocal) + if (mAutoHostAddressCount == 0) { - SuccessOrExit(error = AppendAaaaRecord(Get().GetMeshLocal64(), aMessage, aInfo)); + Ip6::Netif::UnicastAddress &ml64 = Get().GetMeshLocal64UnicastAddress(); + + SuccessOrExit(error = AppendAaaaRecord(ml64.GetAddress(), aMessage, aInfo)); + ml64.mSrpRegistered = true; + mAutoHostAddressCount++; } } else diff --git a/src/core/net/srp_client.hpp b/src/core/net/srp_client.hpp index 5260ea6ac..32b1ea0de 100644 --- a/src/core/net/srp_client.hpp +++ b/src/core/net/srp_client.hpp @@ -1001,6 +1001,8 @@ class Client : public InstanceLocator, private NonCopyable void Pause(void); void HandleNotifierEvents(Events aEvents); void HandleRoleChanged(void); + bool ShouldUpdateHostAutoAddresses(void) const; + bool ShouldHostAutoAddressRegister(const Ip6::Netif::UnicastAddress &aUnicastAddress) const; Error UpdateHostInfoStateOnAddressChange(void); void UpdateServiceStateToRemove(Service &aService); State GetState(void) const { return mState; } @@ -1065,7 +1067,6 @@ class Client : public InstanceLocator, private NonCopyable State mState; uint8_t mTxFailureRetryCount : 4; bool mShouldRemoveKeyLease : 1; - bool mAutoHostAddressAddedMeshLocal : 1; bool mSingleServiceMode : 1; #if OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE bool mServiceKeyRecordEnabled : 1; @@ -1073,6 +1074,7 @@ class Client : public InstanceLocator, private NonCopyable #endif uint16_t mUpdateMessageId; + uint16_t mAutoHostAddressCount; uint32_t mRetryWaitInterval; TimeMilli mLeaseRenewTime; diff --git a/src/core/openthread-core-config.h b/src/core/openthread-core-config.h index ea238fb1f..79640a84f 100644 --- a/src/core/openthread-core-config.h +++ b/src/core/openthread-core-config.h @@ -88,7 +88,6 @@ #include "config/dns_client.h" #include "config/dns_dso.h" #include "config/dnssd_server.h" -#include "config/dtls.h" #include "config/history_tracker.h" #include "config/ip6.h" #include "config/joiner.h" @@ -109,6 +108,7 @@ #include "config/platform.h" #include "config/power_calibration.h" #include "config/radio_link.h" +#include "config/secure_transport.h" #include "config/sntp_client.h" #include "config/srp_client.h" #include "config/srp_server.h" diff --git a/src/core/radio/ble_secure.cpp b/src/core/radio/ble_secure.cpp new file mode 100644 index 000000000..fe0a6f5c3 --- /dev/null +++ b/src/core/radio/ble_secure.cpp @@ -0,0 +1,544 @@ +/* + * 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 "ble_secure.hpp" + +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE + +#include +#include "common/locator_getters.hpp" +#include "common/log.hpp" +#include "common/tlvs.hpp" +#include "instance/instance.hpp" +#include "meshcop/secure_transport.hpp" + +using namespace ot; + +/** + * @file + * This file implements the secure Ble agent. + */ + +namespace ot { +namespace Ble { + +RegisterLogModule("BleSecure"); + +BleSecure::BleSecure(Instance &aInstance) + : InstanceLocator(aInstance) + , mTls(aInstance, false, false) + , mTcatAgent(aInstance) + , mTlvMode(false) + , mReceivedMessage(nullptr) + , mSendMessage(nullptr) + , mTransmitTask(aInstance) + , mBleState(kStopped) + , mMtuSize(kInitialMtuSize) +{ +} + +Error BleSecure::Start(ConnectCallback aConnectHandler, ReceiveCallback aReceiveHandler, bool aTlvMode, void *aContext) +{ + Error error = kErrorNone; + + VerifyOrExit(mBleState == kStopped, error = kErrorAlready); + + mConnectCallback.Set(aConnectHandler, aContext); + mReceiveCallback.Set(aReceiveHandler, aContext); + mTlvMode = aTlvMode; + mMtuSize = kInitialMtuSize; + + SuccessOrExit(error = otPlatBleEnable(&GetInstance())); + SuccessOrExit(error = otPlatBleGapAdvStart(&GetInstance(), OT_BLE_ADV_INTERVAL_DEFAULT)); + SuccessOrExit(error = mTls.Open(&BleSecure::HandleTlsReceive, &BleSecure::HandleTlsConnected, this)); + SuccessOrExit(error = mTls.Bind(HandleTransport, this)); + +exit: + if (error == kErrorNone) + { + mBleState = kAdvertising; + } + return error; +} + +Error BleSecure::TcatStart(const MeshCoP::TcatAgent::VendorInfo &aVendorInfo, + MeshCoP::TcatAgent::JoinCallback aJoinHandler) +{ + return mTcatAgent.Start(aVendorInfo, mReceiveCallback.GetHandler(), aJoinHandler, mReceiveCallback.GetContext()); +} + +void BleSecure::Stop(void) +{ + VerifyOrExit(mBleState != kStopped); + SuccessOrExit(otPlatBleGapAdvStop(&GetInstance())); + SuccessOrExit(otPlatBleDisable(&GetInstance())); + mBleState = kStopped; + mMtuSize = kInitialMtuSize; + + if (mTcatAgent.IsEnabled()) + { + mTcatAgent.Stop(); + } + + mTls.Close(); + + mTransmitQueue.DequeueAndFreeAll(); + + mConnectCallback.Clear(); + mReceiveCallback.Clear(); + + FreeMessage(mReceivedMessage); + mReceivedMessage = nullptr; + FreeMessage(mSendMessage); + mSendMessage = nullptr; + +exit: + return; +} + +Error BleSecure::Connect(void) +{ + Ip6::SockAddr sockaddr; + + return mTls.Connect(sockaddr); +} + +void BleSecure::Disconnect(void) +{ + if (mTls.IsConnected()) + { + mTls.Disconnect(); + } + + if (mBleState == kConnected) + { + IgnoreReturnValue(otPlatBleGapDisconnect(&GetInstance())); + } +} + +void BleSecure::SetPsk(const MeshCoP::JoinerPskd &aPskd) +{ + static_assert(static_cast(MeshCoP::JoinerPskd::kMaxLength) <= + static_cast(MeshCoP::SecureTransport::kPskMaxLength), + "The maximum length of TLS PSK is smaller than joiner PSKd"); + + SuccessOrAssert(mTls.SetPsk(reinterpret_cast(aPskd.GetAsCString()), aPskd.GetLength())); +} + +#if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) +Error BleSecure::GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength) +{ + Error error; + + VerifyOrExit(aCertLength != nullptr, error = kErrorInvalidArgs); + + error = mTls.GetPeerCertificateBase64(aPeerCert, aCertLength, *aCertLength); + +exit: + return error; +} +#endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + +Error BleSecure::SendMessage(ot::Message &aMessage) +{ + Error error = kErrorNone; + + VerifyOrExit(IsConnected(), error = kErrorInvalidState); + if (mSendMessage == nullptr) + { + mSendMessage = Get().Allocate(Message::kTypeBle); + VerifyOrExit(mSendMessage != nullptr, error = kErrorNoBufs); + } + SuccessOrExit(error = mSendMessage->AppendBytesFromMessage(aMessage, 0, aMessage.GetLength())); + SuccessOrExit(error = Flush()); + +exit: + aMessage.Free(); + return error; +} + +Error BleSecure::Send(uint8_t *aBuf, uint16_t aLength) +{ + Error error = kErrorNone; + + VerifyOrExit(IsConnected(), error = kErrorInvalidState); + if (mSendMessage == nullptr) + { + mSendMessage = Get().Allocate(Message::kTypeBle); + VerifyOrExit(mSendMessage != nullptr, error = kErrorNoBufs); + } + SuccessOrExit(error = mSendMessage->AppendBytes(aBuf, aLength)); + +exit: + return error; +} + +Error BleSecure::SendApplicationTlv(uint8_t *aBuf, uint16_t aLength) +{ + Error error = kErrorNone; + if (aLength > Tlv::kBaseTlvMaxLength) + { + ot::ExtendedTlv tlv; + + tlv.SetType(ot::MeshCoP::TcatAgent::kTlvSendApplicationData); + tlv.SetLength(aLength); + SuccessOrExit(error = Send(reinterpret_cast(&tlv), sizeof(tlv))); + } + else + { + ot::Tlv tlv; + + tlv.SetType(ot::MeshCoP::TcatAgent::kTlvSendApplicationData); + tlv.SetLength((uint8_t)aLength); + SuccessOrExit(error = Send(reinterpret_cast(&tlv), sizeof(tlv))); + } + + error = Send(aBuf, aLength); +exit: + return error; +} + +Error BleSecure::Flush(void) +{ + Error error = kErrorNone; + + VerifyOrExit(IsConnected(), error = kErrorInvalidState); + VerifyOrExit(mSendMessage->GetLength() != 0, error = kErrorNone); + + mTransmitQueue.Enqueue(*mSendMessage); + mTransmitTask.Post(); + + mSendMessage = nullptr; + +exit: + return error; +} + +Error BleSecure::HandleBleReceive(uint8_t *aBuf, uint16_t aLength) +{ + ot::Message *message = nullptr; + Ip6::MessageInfo messageInfo; + Error error = kErrorNone; + + if ((message = Get().Allocate(Message::kTypeBle, 0)) == nullptr) + { + error = kErrorNoBufs; + ExitNow(); + } + SuccessOrExit(error = message->AppendBytes(aBuf, aLength)); + + // Cannot call Receive(..) directly because Setup(..) and mState are private + mTls.HandleUdpReceive(*message, messageInfo); + +exit: + FreeMessage(message); + return error; +} + +void BleSecure::HandleBleConnected(uint16_t aConnectionId) +{ + OT_UNUSED_VARIABLE(aConnectionId); + + mBleState = kConnected; + + IgnoreReturnValue(otPlatBleGattMtuGet(&GetInstance(), &mMtuSize)); + + mConnectCallback.InvokeIfSet(&GetInstance(), IsConnected(), true); +} + +void BleSecure::HandleBleDisconnected(uint16_t aConnectionId) +{ + OT_UNUSED_VARIABLE(aConnectionId); + + mBleState = kAdvertising; + mMtuSize = kInitialMtuSize; + + if (IsConnected()) + { + Disconnect(); // Stop TLS connection + } + + mConnectCallback.InvokeIfSet(&GetInstance(), false, false); +} + +Error BleSecure::HandleBleMtuUpdate(uint16_t aMtu) +{ + Error error = kErrorNone; + + if (aMtu <= OT_BLE_ATT_MTU_MAX) + { + mMtuSize = aMtu; + } + else + { + mMtuSize = OT_BLE_ATT_MTU_MAX; + error = kErrorInvalidArgs; + } + + return error; +} + +void BleSecure::HandleTlsConnected(void *aContext, bool aConnected) +{ + return static_cast(aContext)->HandleTlsConnected(aConnected); +} + +void BleSecure::HandleTlsConnected(bool aConnected) +{ + if (aConnected) + { + if (mReceivedMessage == nullptr) + { + mReceivedMessage = Get().Allocate(Message::kTypeBle); + } + + if (mTcatAgent.IsEnabled()) + { + IgnoreReturnValue(mTcatAgent.Connected(mTls)); + } + } + else + { + FreeMessage(mReceivedMessage); + mReceivedMessage = nullptr; + + if (mTcatAgent.IsEnabled()) + { + mTcatAgent.Disconnected(); + } + } + + mConnectCallback.InvokeIfSet(&GetInstance(), aConnected, true); +} + +void BleSecure::HandleTlsReceive(void *aContext, uint8_t *aBuf, uint16_t aLength) +{ + return static_cast(aContext)->HandleTlsReceive(aBuf, aLength); +} + +void BleSecure::HandleTlsReceive(uint8_t *aBuf, uint16_t aLength) +{ + VerifyOrExit(mReceivedMessage != nullptr); + + if (!mTlvMode) + { + SuccessOrExit(mReceivedMessage->AppendBytes(aBuf, aLength)); + mReceiveCallback.InvokeIfSet(&GetInstance(), mReceivedMessage, 0, OT_TCAT_APPLICATION_PROTOCOL_NONE, ""); + IgnoreReturnValue(mReceivedMessage->SetLength(0)); + } + else + { + ot::Tlv tlv; + uint32_t requiredBytes = sizeof(Tlv); + uint32_t offset; + + while (aLength > 0) + { + if (mReceivedMessage->GetLength() < requiredBytes) + { + uint32_t missingBytes = requiredBytes - mReceivedMessage->GetLength(); + + if (missingBytes > aLength) + { + SuccessOrExit(mReceivedMessage->AppendBytes(aBuf, aLength)); + break; + } + else + { + SuccessOrExit(mReceivedMessage->AppendBytes(aBuf, (uint16_t)missingBytes)); + aLength -= missingBytes; + aBuf += missingBytes; + } + } + + IgnoreReturnValue(mReceivedMessage->Read(0, tlv)); + + if (tlv.IsExtended()) + { + ot::ExtendedTlv extTlv; + requiredBytes = sizeof(extTlv); + + if (mReceivedMessage->GetLength() < requiredBytes) + { + continue; + } + + IgnoreReturnValue(mReceivedMessage->Read(0, extTlv)); + requiredBytes = extTlv.GetSize(); + offset = sizeof(extTlv); + } + else + { + requiredBytes = tlv.GetSize(); + offset = sizeof(tlv); + } + + if (mReceivedMessage->GetLength() < requiredBytes) + { + continue; + } + + // TLV fully loaded + + if (mTcatAgent.IsEnabled()) + { + ot::Message *message; + Error error = kErrorNone; + + message = Get().Allocate(Message::kTypeBle); + VerifyOrExit(message != nullptr, error = kErrorNoBufs); + + error = mTcatAgent.HandleSingleTlv(*mReceivedMessage, *message); + if (message->GetLength() != 0) + { + IgnoreReturnValue(SendMessage(*message)); + } + + if (error == kErrorAbort) + { + Disconnect(); + Stop(); + ExitNow(); + } + } + else + { + mReceivedMessage->SetOffset((uint16_t)offset); + mReceiveCallback.InvokeIfSet(&GetInstance(), mReceivedMessage, (int32_t)offset, + OT_TCAT_APPLICATION_PROTOCOL_NONE, ""); + } + + SuccessOrExit(mReceivedMessage->SetLength(0)); // also sets the offset to 0 + requiredBytes = sizeof(Tlv); + } + } + +exit: + return; +} + +void BleSecure::HandleTransmit(void) +{ + Error error = kErrorNone; + ot::Message *message = mTransmitQueue.GetHead(); + + VerifyOrExit(message != nullptr); + mTransmitQueue.Dequeue(*message); + + if (mTransmitQueue.GetHead() != nullptr) + { + mTransmitTask.Post(); + } + + SuccessOrExit(error = mTls.Send(*message, message->GetLength())); + +exit: + if (error != kErrorNone) + { + LogNote("Transmit: %s", ErrorToString(error)); + message->Free(); + } + else + { + LogDebg("Transmit: %s", ErrorToString(error)); + } +} + +Error BleSecure::HandleTransport(void *aContext, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) +{ + OT_UNUSED_VARIABLE(aMessageInfo); + return static_cast(aContext)->HandleTransport(aMessage); +} + +Error BleSecure::HandleTransport(ot::Message &aMessage) +{ + otBleRadioPacket packet; + uint16_t len = aMessage.GetLength(); + uint16_t offset = 0; + Error error = kErrorNone; + + while (len > 0) + { + if (len <= mMtuSize - kGattOverhead) + { + packet.mLength = len; + } + else + { + packet.mLength = mMtuSize - kGattOverhead; + } + + if (packet.mLength > kPacketBufferSize) + { + packet.mLength = kPacketBufferSize; + } + + IgnoreReturnValue(aMessage.Read(offset, mPacketBuffer, packet.mLength)); + packet.mValue = mPacketBuffer; + packet.mPower = OT_BLE_DEFAULT_POWER; + + SuccessOrExit(error = otPlatBleGattServerIndicate(&GetInstance(), kTxBleHandle, &packet)); + + len -= packet.mLength; + offset += packet.mLength; + } + + aMessage.Free(); +exit: + return error; +} + +} // namespace Ble +} // namespace ot + +void otPlatBleGattServerOnWriteRequest(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket) +{ + OT_UNUSED_VARIABLE(aHandle); // Only a single handle is expected for RX + + VerifyOrExit(aPacket != nullptr); + IgnoreReturnValue(AsCoreType(aInstance).Get().HandleBleReceive(aPacket->mValue, aPacket->mLength)); +exit: + return; +} + +void otPlatBleGapOnConnected(otInstance *aInstance, uint16_t aConnectionId) +{ + AsCoreType(aInstance).Get().HandleBleConnected(aConnectionId); +} + +void otPlatBleGapOnDisconnected(otInstance *aInstance, uint16_t aConnectionId) +{ + AsCoreType(aInstance).Get().HandleBleDisconnected(aConnectionId); +} + +void otPlatBleGattOnMtuUpdate(otInstance *aInstance, uint16_t aMtu) +{ + IgnoreReturnValue(AsCoreType(aInstance).Get().HandleBleMtuUpdate(aMtu)); +} + +#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE diff --git a/src/core/radio/ble_secure.hpp b/src/core/radio/ble_secure.hpp new file mode 100644 index 000000000..63a86aacd --- /dev/null +++ b/src/core/radio/ble_secure.hpp @@ -0,0 +1,489 @@ +/* + * 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 BLE_SECURE_HPP_ +#define BLE_SECURE_HPP_ + +#include "openthread-core-config.h" + +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE + +#include + +#include "meshcop/meshcop.hpp" +#include "meshcop/secure_transport.hpp" +#include "meshcop/tcat_agent.hpp" + +/** + * @file + * Includes definitions for the secure BLE agent. + */ + +namespace ot { + +namespace Ble { + +class BleSecure : public InstanceLocator, private NonCopyable +{ +public: + /** + * Pointer to call when the secure BLE connection state changes. + * + * Please see otHandleBleSecureConnect for details. + * + */ + typedef otHandleBleSecureConnect ConnectCallback; + + /** + * Pointer to call when data was received over the TLS connection. + * If line mode is activated the function is called only after EOL has been received. + * + * Please see otHandleBleSecureReceive for details. + * + */ + typedef otHandleBleSecureReceive ReceiveCallback; + + /** + * Represents a TCAT command class. + * + */ + typedef MeshCoP::TcatAgent::CommandClass CommandClass; + + /** + * Constructor initializes the object. + * + * @param[in] aInstance A reference to the OpenThread instance. + * + */ + explicit BleSecure(Instance &aInstance); + + /** + * Starts the secure BLE agent. + * + * @param[in] aConnectHandler A pointer to a function that will be called when the connection + * state changes. + * @param[in] aReceiveHandler A pointer to a function that will be called once data has been received + * over the TLS connection. + * @param[in] aTlvMode A boolean value indicating if line mode shall be activated. + * @param[in] aContext A pointer to arbitrary context information. May be NULL if not used. + * + * @retval kErrorNone Successfully started the BLE agent. + * @retval kErrorAlready Already started. + * + */ + Error Start(ConnectCallback aConnectHandler, ReceiveCallback aReceiveHandler, bool aTlvMode, void *aContext); + + /** + * Enables the TCAT protocol over BLE Secure. + * + * @param[in] aVendorInfo A reference to the Vendor Information (must remain valid after the method call) + * @param[in] aHandler Callback to a function that is called when the join operation completes. + * + * @retval kErrorNone Successfully started the BLE Secure Joiner role. + * @retval kErrorInvalidArgs The aVendorInfo is invalid. + * @retval kErrorInvaidState The BLE function has not been started or line mode is not selected. + * + */ + Error TcatStart(const MeshCoP::TcatAgent::VendorInfo &aVendorInfo, MeshCoP::TcatAgent::JoinCallback aHandler); + + /** + * Stops the secure BLE agent. + * + */ + void Stop(void); + + /** + * Initializes TLS session with a peer using an already open BLE connection. + * + * @retval kErrorNone Successfully started TLS connection. + * + */ + Error Connect(void); + + /** + * Stops the BLE and TLS connection. + * + */ + void Disconnect(void); + + /** + * Indicates whether or not the TLS session is active (connected or conneting). + * + * @retval TRUE If TLS session is active. + * @retval FALSE If TLS session is not active. + * + */ + bool IsConnectionActive(void) const { return mTls.IsConnectionActive(); } + + /** + * Indicates whether or not the TLS session is connected. + * + * @retval TRUE The TLS session is connected. + * @retval FALSE The TLS session is not connected. + * + */ + bool IsConnected(void) const { return mTls.IsConnected(); } + + /** + * Indicates whether or not the TCAT agent is enabled. + * + * @retval TRUE The TCAT agent is enabled. + * @retval FALSE The TCAT agent is not enabled. + * + */ + bool IsTcatEnabled(void) const { return mTcatAgent.IsEnabled(); } + + /** + * Indicates whether or not a TCAT command class is authorized. + * + * @param[in] aInstance A pointer to an OpenThread instance. + * @param[in] aCommandClass A command class to check. + * + * @retval TRUE The command class is authorized. + * @retval FALSE The command class is not authorized. + * + */ + bool IsCommandClassAuthorized(CommandClass aCommandClass) const + { + return mTcatAgent.IsCommandClassAuthorized(aCommandClass); + } + + /** + * Sets the PSK. + * + * @param[in] aPsk A pointer to the PSK. + * @param[in] aPskLength The PSK length. + * + * @retval kErrorNone Successfully set the PSK. + * @retval kErrorInvalidArgs The PSK is invalid. + * + */ + Error SetPsk(const uint8_t *aPsk, uint8_t aPskLength) { return mTls.SetPsk(aPsk, aPskLength); } + + /** + * Sets the PSK. + * + * @param[in] aPskd A Joiner PSKd. + * + */ + void SetPsk(const MeshCoP::JoinerPskd &aPskd); + +#ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + /** + * Sets the Pre-Shared Key (PSK) for TLS sessions identified by a PSK. + * + * TLS mode "TLS with AES 128 CCM 8" for secure BLE. + * + * @param[in] aPsk A pointer to the PSK. + * @param[in] aPskLength The PSK char length. + * @param[in] aPskIdentity The Identity Name for the PSK. + * @param[in] aPskIdLength The PSK Identity Length. + * + */ + void SetPreSharedKey(const uint8_t *aPsk, uint16_t aPskLength, const uint8_t *aPskIdentity, uint16_t aPskIdLength) + { + mTls.SetPreSharedKey(aPsk, aPskLength, aPskIdentity, aPskIdLength); + } +#endif // MBEDTLS_KEY_EXCHANGE_PSK_ENABLED + +#ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + /** + * Sets a X509 certificate with corresponding private key for TLS session. + * + * TLS mode "ECDHE ECDSA with AES 128 CCM 8" for secure BLE. + * + * @param[in] aX509Cert A pointer to the PEM formatted X509 PEM certificate. + * @param[in] aX509Length The length of certificate. + * @param[in] aPrivateKey A pointer to the PEM formatted private key. + * @param[in] aPrivateKeyLength The length of the private key. + * + */ + void SetCertificate(const uint8_t *aX509Cert, + uint32_t aX509Length, + const uint8_t *aPrivateKey, + uint32_t aPrivateKeyLength) + { + mTls.SetCertificate(aX509Cert, aX509Length, aPrivateKey, aPrivateKeyLength); + } + + /** + * Sets the trusted top level CAs. It is needed for validate the certificate of the peer. + * + * TLS mode "ECDHE ECDSA with AES 128 CCM 8" for secure BLE. + * + * @param[in] aX509CaCertificateChain A pointer to the PEM formatted X509 CA chain. + * @param[in] aX509CaCertChainLength The length of chain. + * + */ + void SetCaCertificateChain(const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength) + { + mTls.SetCaCertificateChain(aX509CaCertificateChain, aX509CaCertChainLength); + } +#endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED + +#if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + /** + * Returns the peer x509 certificate base64 encoded. + * + * TLS mode "ECDHE ECDSA with AES 128 CCM 8" for secure BLE. + * + * @param[out] aPeerCert A pointer to the base64 encoded certificate buffer. + * @param[out] aCertLength On input, the size the max size of @p aPeerCert. + * On output, the length of the base64 encoded peer certificate. + * + * @retval kErrorNone Successfully get the peer certificate. + * @retval kErrorInvalidArgs @p aInstance or @p aCertLength is invalid. + * @retval kErrorInvalidState Not connected yet. + * @retval kErrorNoBufs Can't allocate memory for certificate. + * + */ + Error GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength); +#endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + +#if defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + /** + * Returns an attribute value identified by its OID from the subject + * of the peer x509 certificate. The peer OID is provided in binary format. + * The attribute length is set if the attribute was successfully read or zero + * if unsuccessful. The ASN.1 type as is set as defineded in the ITU-T X.690 standard + * if the attribute was successfully read. + * + * @param[in] aOid A pointer to the OID to be found. + * @param[in] aOidLength The length of the OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[in,out] aAttributeLength On input, the size the max size of @p aAttributeBuffer. + * On output, the length of the attribute written to the buffer. + * @param[out] aAsn1Type A pointer to the ASN.1 type of the attribute written to the buffer. + * + * @retval kErrorInvalidState Not connected yet. + * @retval kErrorNone Successfully read attribute. + * @retval kErrorNoBufs Insufficient memory for storing the attribute value. + * + */ + Error GetPeerSubjectAttributeByOid(const char *aOid, + size_t aOidLength, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength, + int *aAsn1Type) + { + return mTls.GetPeerSubjectAttributeByOid(aOid, aOidLength, aAttributeBuffer, aAttributeLength, aAsn1Type); + } + + /** + * Returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of + * the peer x509 certificate, where the last digit x is set to aThreadOidDescriptor. + * The attribute length is set if the attribute was successfully read or zero if unsuccessful. + * Requires a connection to be active. + * + * @param[in] aThreadOidDescriptor The last digit of the Thread attribute OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[in,out] aAttributeLength On input, the size the max size of @p aAttributeBuffer. + * On output, the length of the attribute written to the buffer. + * + * @retval kErrorNone Successfully read attribute. + * @retval kErrorNotFound The requested attribute was not found. + * @retval kErrorNoBufs Insufficient memory for storing the attribute value. + * @retval kErrorInvalidState Not connected yet. + * @retval kErrorNotImplemented The value of aThreadOidDescriptor is >127. + * @retval kErrorParse The certificate extensions could not be parsed. + * + */ + Error GetThreadAttributeFromPeerCertificate(int aThreadOidDescriptor, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength) + { + return mTls.GetThreadAttributeFromPeerCertificate(aThreadOidDescriptor, aAttributeBuffer, aAttributeLength); + } +#endif // defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) + + /** + * Returns an attribute value for the OID 1.3.6.1.4.1.44970.x from the v3 extensions of + * the own x509 certificate, where the last digit x is set to aThreadOidDescriptor. + * The attribute length is set if the attribute was successfully read or zero if unsuccessful. + * Requires a connection to be active. + * + * @param[in] aThreadOidDescriptor The last digit of the Thread attribute OID. + * @param[out] aAttributeBuffer A pointer to the attribute buffer. + * @param[in,out] aAttributeLength On input, the size the max size of @p aAttributeBuffer. + * On output, the length of the attribute written to the buffer. + * + * @retval kErrorNone Successfully read attribute. + * @retval kErrorNotFound The requested attribute was not found. + * @retval kErrorNoBufs Insufficient memory for storing the attribute value. + * @retval kErrorInvalidState Not connected yet. + * @retval kErrorNotImplemented The value of aThreadOidDescriptor is >127. + * @retval kErrorParse The certificate extensions could not be parsed. + * + */ + Error GetThreadAttributeFromOwnCertificate(int aThreadOidDescriptor, + uint8_t *aAttributeBuffer, + size_t *aAttributeLength) + { + return mTls.GetThreadAttributeFromOwnCertificate(aThreadOidDescriptor, aAttributeBuffer, aAttributeLength); + } + + /** + * Sets the authentication mode for the BLE secure connection. It disables or enables the verification + * of peer certificate. + * + * @param[in] aVerifyPeerCertificate true, if the peer certificate should be verified + * + */ + void SetSslAuthMode(bool aVerifyPeerCertificate) { mTls.SetSslAuthMode(aVerifyPeerCertificate); } + + /** + * Sends a secure BLE message. + * + * @param[in] aMessage A pointer to the message to send. + * + * If the return value is kErrorNone, OpenThread takes ownership of @p aMessage, and the caller should no longer + * reference @p aMessage. If the return value is not kErrorNone, the caller retains ownership of @p aMessage, + * including freeing @p aMessage if the message buffer is no longer needed. + * + * @retval kErrorNone Successfully sent message. + * @retval kErrorNoBufs Failed to allocate buffer memory. + * @retval kErrorInvalidState TLS connection was not initialized. + * + */ + Error SendMessage(Message &aMessage); + + /** + * Sends a secure BLE data packet. + * + * @param[in] aBuf A pointer to the data to send as the Value of the TCAT Send Application Data TLV. + * @param[in] aLength A number indicating the length of the data buffer. + * + * @retval kErrorNone Successfully sent data. + * @retval kErrorNoBufs Failed to allocate buffer memory. + * @retval kErrorInvalidState TLS connection was not initialized. + * + */ + Error Send(uint8_t *aBuf, uint16_t aLength); + + /** + * Sends a secure BLE data packet containing a TCAT Send Application Data TLV. + * + * @param[in] aBuf A pointer to the data to send as the Value of the TCAT Send Application Data TLV. + * @param[in] aLength A number indicating the length of the data buffer. + * + * @retval kErrorNone Successfully sent data. + * @retval kErrorNoBufs Failed to allocate buffer memory. + * @retval kErrorInvalidState TLS connection was not initialized. + * + */ + Error SendApplicationTlv(uint8_t *aBuf, uint16_t aLength); + + /** + * Sends all remaining bytes in the send buffer. + * + * @retval kErrorNone Successfully enqueued data into the output interface. + * @retval kErrorNoBufs Failed to allocate buffer memory. + * @retval kErrorInvalidState TLS connection was not initialized. + * + */ + Error Flush(void); + + /** + * Used to pass data received over a BLE link to the secure BLE server. + * + * @param[in] aBuf A pointer to the data received. + * @param[in] aLength A number indicating the length of the data buffer. + * + */ + Error HandleBleReceive(uint8_t *aBuf, uint16_t aLength); + + /** + * Used to notify the secure BLE server that a BLE Device has been connected. + * + * @param[in] aConnectionId The identifier of the open connection. + * + */ + void HandleBleConnected(uint16_t aConnectionId); + + /** + * Used to notify the secure BLE server that the BLE Device has been disconnected. + * + * @param[in] aConnectionId The identifier of the open connection. + * + */ + void HandleBleDisconnected(uint16_t aConnectionId); + + /** + * Used to notify the secure BLE server that the BLE Device has updated ATT_MTU size. + * + * @param[in] aMtu The updated ATT_MTU value. + * + */ + Error HandleBleMtuUpdate(uint16_t aMtu); + +private: + enum BleState : uint8_t + { + kStopped = 0, // Ble secure not started. + kAdvertising = 1, // Ble secure not advertising. + kConnected = 2, // Ble secure not connected. + }; + + static constexpr uint8_t kInitialMtuSize = 23; // ATT_MTU + static constexpr uint8_t kGattOverhead = 3; // BLE GATT payload fits MTU size - 3 bytes + static constexpr uint8_t kPacketBufferSize = OT_BLE_ATT_MTU_MAX - kGattOverhead; + static constexpr uint16_t kTxBleHandle = 0; // Characteristics Handle for TX (not used) + + static void HandleTlsConnected(void *aContext, bool aConnected); + void HandleTlsConnected(bool aConnected); + + static void HandleTlsReceive(void *aContext, uint8_t *aBuf, uint16_t aLength); + void HandleTlsReceive(uint8_t *aBuf, uint16_t aLength); + + void HandleTransmit(void); + + static Error HandleTransport(void *aContext, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); + Error HandleTransport(ot::Message &aMessage); + + using TxTask = TaskletIn; + + MeshCoP::SecureTransport mTls; + MeshCoP::TcatAgent mTcatAgent; + Callback mConnectCallback; + Callback mReceiveCallback; + bool mTlvMode; + ot::Message *mReceivedMessage; + ot::Message *mSendMessage; + ot::MessageQueue mTransmitQueue; + TxTask mTransmitTask; + uint8_t mPacketBuffer[kPacketBufferSize]; + BleState mBleState; + uint16_t mMtuSize; +}; + +} // namespace Ble +} // namespace ot + +#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE + +#endif // BLE_SECURE_HPP_ diff --git a/src/core/thread/address_resolver.cpp b/src/core/thread/address_resolver.cpp index a779a1158..28f6483cd 100644 --- a/src/core/thread/address_resolver.cpp +++ b/src/core/thread/address_resolver.cpp @@ -785,7 +785,7 @@ void AddressResolver::HandleTmf(Coap::Message &aMessage, const SuccessOrExit(error = Tlv::Find(aMessage, target)); SuccessOrExit(error = Tlv::Find(aMessage, meshLocalIid)); - for (const Ip6::Netif::UnicastAddress &address : Get().GetUnicastAddresses()) + for (Ip6::Netif::UnicastAddress &address : Get().GetUnicastAddresses()) { if (address.GetAddress() == target && Get().GetMeshLocal64().GetIid() != meshLocalIid) { diff --git a/src/core/thread/mle.hpp b/src/core/thread/mle.hpp index 8f3e9eaa0..33d0920cf 100644 --- a/src/core/thread/mle.hpp +++ b/src/core/thread/mle.hpp @@ -530,6 +530,14 @@ class Mle : public InstanceLocator, private NonCopyable */ const Ip6::Address &GetMeshLocal64(void) const { return mMeshLocal64.GetAddress(); } + /** + * Returns a reference to the ML-EID as a `Netif::UnicastAddress`. + * + * @returns A reference to the ML-EID. + * + */ + Ip6::Netif::UnicastAddress &GetMeshLocal64UnicastAddress(void) { return mMeshLocal64; } + /** * Returns the Router ID of the Leader. * diff --git a/src/core/thread/mle_tlvs.cpp b/src/core/thread/mle_tlvs.cpp index 1908d61d4..f3081e18f 100644 --- a/src/core/thread/mle_tlvs.cpp +++ b/src/core/thread/mle_tlvs.cpp @@ -34,6 +34,8 @@ #include "mle_tlvs.hpp" #include "common/code_utils.hpp" +#include "common/numeric_limits.hpp" +#include "radio/radio.hpp" namespace ot { namespace Mle { @@ -94,5 +96,46 @@ void ConnectivityTlv::SetParentPriority(int8_t aParentPriority) mFlags = static_cast(Preference::To2BitUint(aParentPriority) << kFlagsParentPriorityOffset); } +void ChannelTlvValue::SetChannelAndPage(uint16_t aChannel) +{ + uint8_t channelPage = OT_RADIO_CHANNEL_PAGE_0; + +#if OPENTHREAD_CONFIG_RADIO_915MHZ_OQPSK_SUPPORT + if ((OT_RADIO_915MHZ_OQPSK_CHANNEL_MIN <= aChannel) && (aChannel <= OT_RADIO_915MHZ_OQPSK_CHANNEL_MAX)) + { + channelPage = OT_RADIO_CHANNEL_PAGE_2; + } +#endif + +#if OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_SUPPORT + if ((OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN == aChannel) || + ((OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MIN < aChannel) && + (aChannel <= OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_MAX))) + { + channelPage = OPENTHREAD_CONFIG_PLATFORM_RADIO_PROPRIETARY_CHANNEL_PAGE; + } +#endif + + SetChannelPage(channelPage); + SetChannel(aChannel); +} + +bool ChannelTlvValue::IsValid(void) const +{ + bool isValid = false; + uint16_t channel; + + VerifyOrExit(mChannelPage < BitSizeOf(uint32_t)); + VerifyOrExit((1U << mChannelPage) & Radio::kSupportedChannelPages); + + channel = GetChannel(); + VerifyOrExit((Radio::kChannelMin <= channel) && (channel <= Radio::kChannelMax)); + + isValid = true; + +exit: + return isValid; +} + } // namespace Mle } // namespace ot diff --git a/src/core/thread/mle_tlvs.hpp b/src/core/thread/mle_tlvs.hpp index d4be8ff45..68f62a49f 100644 --- a/src/core/thread/mle_tlvs.hpp +++ b/src/core/thread/mle_tlvs.hpp @@ -1096,6 +1096,23 @@ class ChannelTlvValue */ void SetChannel(uint16_t aChannel) { mChannel = BigEndian::HostSwap16(aChannel); } + /** + * Sets the Channel and determines and sets the Channel Page from the given channel. + * + * @param[in] aChannel The Channel value. + * + */ + void SetChannelAndPage(uint16_t aChannel); + + /** + * Indicates whether or not the Channel and Channel Page values are valid. + * + * @retval TRUE If the Channel and Channel Page values are valid. + * @retval FALSE If the Channel and Channel Page values are not valid. + * + */ + bool IsValid(void) const; + private: uint8_t mChannelPage; uint16_t mChannel; diff --git a/src/core/thread/mlr_manager.cpp b/src/core/thread/mlr_manager.cpp index db7bc938f..dd25d7bea 100644 --- a/src/core/thread/mlr_manager.cpp +++ b/src/core/thread/mlr_manager.cpp @@ -54,8 +54,8 @@ MlrManager::MlrManager(Instance &aInstance) , mReregistrationDelay(0) , mSendDelay(0) , mMlrPending(false) -#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE - , mRegisterMulticastListenersPending(false) +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE + , mRegisterPending(false) #endif { } @@ -186,7 +186,7 @@ void MlrManager::ScheduleSend(uint16_t aDelay) if (aDelay == 0) { mSendDelay = 0; - SendMulticastListenerRegistration(); + SendMlr(); } else if (mSendDelay == 0 || mSendDelay > aDelay) { @@ -210,12 +210,11 @@ void MlrManager::UpdateTimeTickerRegistration(void) } } -void MlrManager::SendMulticastListenerRegistration(void) +void MlrManager::SendMlr(void) { Error error; Mle::MleRouter &mle = Get(); - Ip6::Address addresses[Ip6AddressesTlv::kMaxAddresses]; - uint8_t addressesNum = 0; + AddressArray addresses; VerifyOrExit(!mMlrPending, error = kErrorBusy); VerifyOrExit(mle.IsAttached(), error = kErrorInvalidState); @@ -227,14 +226,14 @@ void MlrManager::SendMulticastListenerRegistration(void) for (Ip6::Netif::ExternalMulticastAddress &addr : Get().IterateExternalMulticastAddresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) { - if (addressesNum >= Ip6AddressesTlv::kMaxAddresses) + if (addresses.IsFull()) { break; } if (addr.GetMlrState() == kMlrStateToRegister) { - AppendToUniqueAddressList(addresses, addressesNum, addr.GetAddress()); + addresses.AddUnique(addr.GetAddress()); addr.SetMlrState(kMlrStateRegistering); } } @@ -244,7 +243,7 @@ void MlrManager::SendMulticastListenerRegistration(void) // Append Child multicast addresses for (Child &child : Get().Iterate(Child::kInStateValid)) { - if (addressesNum >= Ip6AddressesTlv::kMaxAddresses) + if (addresses.IsFull()) { break; } @@ -256,24 +255,23 @@ void MlrManager::SendMulticastListenerRegistration(void) for (const Ip6::Address &address : child.IterateIp6Addresses(Ip6::Address::kTypeMulticastLargerThanRealmLocal)) { - if (addressesNum >= Ip6AddressesTlv::kMaxAddresses) + if (addresses.IsFull()) { break; } if (child.GetAddressMlrState(address) == kMlrStateToRegister) { - AppendToUniqueAddressList(addresses, addressesNum, address); + addresses.AddUnique(address); child.SetAddressMlrState(address, kMlrStateRegistering); } } } #endif - VerifyOrExit(addressesNum > 0, error = kErrorNotFound); + VerifyOrExit(!addresses.IsEmpty(), error = kErrorNotFound); SuccessOrExit( - error = SendMulticastListenerRegistrationMessage( - addresses, addressesNum, nullptr, &MlrManager::HandleMulticastListenerRegistrationResponse, this)); + error = SendMlrMessage(addresses.GetArrayBuffer(), addresses.GetLength(), nullptr, HandleMlrResponse, this)); mMlrPending = true; @@ -300,12 +298,12 @@ void MlrManager::SendMulticastListenerRegistration(void) CheckInvariants(); } -#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE -Error MlrManager::RegisterMulticastListeners(const otIp6Address *aAddresses, - uint8_t aAddressNum, - const uint32_t *aTimeout, - otIp6RegisterMulticastListenersCallback aCallback, - void *aContext) +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE +Error MlrManager::RegisterMulticastListeners(const Ip6::Address *aAddresses, + uint8_t aAddressNum, + const uint32_t *aTimeout, + MlrCallback aCallback, + void *aContext) { Error error; @@ -322,55 +320,50 @@ Error MlrManager::RegisterMulticastListeners(const otIp6Address #endif // Only allow one outstanding registration if callback is specified. - VerifyOrExit(!mRegisterMulticastListenersPending, error = kErrorBusy); + VerifyOrExit(!mRegisterPending, error = kErrorBusy); - SuccessOrExit(error = SendMulticastListenerRegistrationMessage( - aAddresses, aAddressNum, aTimeout, &MlrManager::HandleRegisterMulticastListenersResponse, this)); + SuccessOrExit(error = SendMlrMessage(aAddresses, aAddressNum, aTimeout, HandleRegisterResponse, this)); - mRegisterMulticastListenersPending = true; - mRegisterMulticastListenersCallback.Set(aCallback, aContext); + mRegisterPending = true; + mRegisterCallback.Set(aCallback, aContext); exit: return error; } -void MlrManager::HandleRegisterMulticastListenersResponse(void *aContext, - otMessage *aMessage, - const otMessageInfo *aMessageInfo, - Error aResult) +void MlrManager::HandleRegisterResponse(void *aContext, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, + Error aResult) { - static_cast(aContext)->HandleRegisterMulticastListenersResponse(AsCoapMessagePtr(aMessage), - AsCoreTypePtr(aMessageInfo), aResult); + static_cast(aContext)->HandleRegisterResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo), + aResult); } -void MlrManager::HandleRegisterMulticastListenersResponse(otMessage *aMessage, - const otMessageInfo *aMessageInfo, - Error aResult) +void MlrManager::HandleRegisterResponse(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; - Callback callbackCopy = mRegisterMulticastListenersCallback; + uint8_t status; + Error error; + AddressArray failedAddresses; + Callback callbackCopy = mRegisterCallback; - mRegisterMulticastListenersPending = false; - mRegisterMulticastListenersCallback.Clear(); + mRegisterPending = false; + mRegisterCallback.Clear(); - error = ParseMulticastListenerRegistrationResponse(aResult, AsCoapMessagePtr(aMessage), status, failedAddresses, - failedAddressNum); + error = ParseMlrResponse(aResult, AsCoapMessagePtr(aMessage), status, failedAddresses); - callbackCopy.InvokeIfSet(error, status, failedAddresses, failedAddressNum); + callbackCopy.InvokeIfSet(error, status, failedAddresses.GetArrayBuffer(), failedAddresses.GetLength()); } -#endif // (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE +#endif // OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE -Error MlrManager::SendMulticastListenerRegistrationMessage(const otIp6Address *aAddresses, - uint8_t aAddressNum, - const uint32_t *aTimeout, - Coap::ResponseHandler aResponseHandler, - void *aResponseContext) +Error MlrManager::SendMlrMessage(const Ip6::Address *aAddresses, + uint8_t aAddressNum, + const uint32_t *aTimeout, + Coap::ResponseHandler aResponseHandler, + void *aResponseContext) { OT_UNUSED_VARIABLE(aTimeout); @@ -390,7 +383,7 @@ Error MlrManager::SendMulticastListenerRegistrationMessage(const otIp6Address SuccessOrExit(error = message->Append(addressesTlv)); SuccessOrExit(error = message->AppendBytes(aAddresses, sizeof(Ip6::Address) * aAddressNum)); -#if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE +#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE if (Get().IsActive()) { SuccessOrExit( @@ -425,37 +418,33 @@ Error MlrManager::SendMulticastListenerRegistrationMessage(const otIp6Address LogInfo("Sent MLR.req: addressNum=%d", aAddressNum); exit: - LogInfo("SendMulticastListenerRegistrationMessage(): %s", ErrorToString(error)); + LogInfo("SendMlrMessage(): %s", ErrorToString(error)); FreeMessageOnError(message, error); return error; } -void MlrManager::HandleMulticastListenerRegistrationResponse(void *aContext, - otMessage *aMessage, - const otMessageInfo *aMessageInfo, - Error aResult) +void MlrManager::HandleMlrResponse(void *aContext, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, + Error aResult) { - static_cast(aContext)->HandleMulticastListenerRegistrationResponse( - AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo), aResult); + static_cast(aContext)->HandleMlrResponse(AsCoapMessagePtr(aMessage), AsCoreTypePtr(aMessageInfo), + aResult); } -void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message *aMessage, - const Ip6::MessageInfo *aMessageInfo, - Error aResult) +void MlrManager::HandleMlrResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult) { OT_UNUSED_VARIABLE(aMessageInfo); uint8_t status; Error error; - Ip6::Address failedAddresses[Ip6AddressesTlv::kMaxAddresses]{}; - uint8_t failedAddressNum = 0; + AddressArray failedAddresses; - error = ParseMulticastListenerRegistrationResponse(aResult, aMessage, status, failedAddresses, failedAddressNum); + error = ParseMlrResponse(aResult, aMessage, status, failedAddresses); - FinishMulticastListenerRegistration(error == kErrorNone && status == ThreadStatusTlv::MlrStatus::kMlrSuccess, - failedAddresses, failedAddressNum); + FinishMlr(error == kErrorNone && status == ThreadStatusTlv::kMlrSuccess, failedAddresses); - if (error == kErrorNone && status == ThreadStatusTlv::MlrStatus::kMlrSuccess) + if (error == kErrorNone && status == ThreadStatusTlv::kMlrSuccess) { // keep sending until all multicast addresses are registered. ScheduleSend(0); @@ -478,16 +467,15 @@ void MlrManager::HandleMulticastListenerRegistrationResponse(Coap::Message } } -Error MlrManager::ParseMulticastListenerRegistrationResponse(Error aResult, - Coap::Message *aMessage, - uint8_t &aStatus, - Ip6::Address *aFailedAddresses, - uint8_t &aFailedAddressNum) +Error MlrManager::ParseMlrResponse(Error aResult, + Coap::Message *aMessage, + uint8_t &aStatus, + AddressArray &aFailedAddresses) { Error error; uint16_t addressesOffset, addressesLength; - aStatus = ThreadStatusTlv::MlrStatus::kMlrGeneralFailure; + aStatus = ThreadStatusTlv::kMlrGeneralFailure; VerifyOrExit(aResult == kErrorNone && aMessage != nullptr, error = kErrorParse); VerifyOrExit(aMessage->GetCode() == Coap::kCodeChanged, error = kErrorParse); @@ -502,15 +490,14 @@ Error MlrManager::ParseMulticastListenerRegistrationResponse(Error aRes for (uint16_t offset = 0; offset < addressesLength; offset += sizeof(Ip6::Address)) { - IgnoreError(aMessage->Read(addressesOffset + offset, aFailedAddresses[aFailedAddressNum])); - aFailedAddressNum++; + IgnoreError(aMessage->Read(addressesOffset + offset, *aFailedAddresses.PushBack())); } } - VerifyOrExit(aFailedAddressNum == 0 || aStatus != ThreadStatusTlv::MlrStatus::kMlrSuccess, error = kErrorParse); + VerifyOrExit(aFailedAddresses.IsEmpty() || aStatus != ThreadStatusTlv::kMlrSuccess, error = kErrorParse); exit: - LogMlrResponse(aResult, error, aStatus, aFailedAddresses, aFailedAddressNum); + LogMlrResponse(aResult, error, aStatus, aFailedAddresses); return aResult != kErrorNone ? aResult : error; } @@ -540,9 +527,7 @@ void MlrManager::SetMulticastAddressMlrState(MlrState aFromState, MlrState aToSt #endif } -void MlrManager::FinishMulticastListenerRegistration(bool aSuccess, - const Ip6::Address *aFailedAddresses, - uint8_t aFailedAddressNum) +void MlrManager::FinishMlr(bool aSuccess, const AddressArray &aFailedAddresses) { OT_ASSERT(mMlrPending); @@ -554,7 +539,7 @@ void MlrManager::FinishMulticastListenerRegistration(bool aSucces { if (addr.GetMlrState() == kMlrStateRegistering) { - bool success = aSuccess || !AddressListContains(aFailedAddresses, aFailedAddressNum, addr.GetAddress()); + bool success = aSuccess || !aFailedAddresses.IsEmptyOrContains(addr.GetAddress()); addr.SetMlrState(success ? kMlrStateRegistered : kMlrStateToRegister); } @@ -567,7 +552,7 @@ void MlrManager::FinishMulticastListenerRegistration(bool aSucces { if (child.GetAddressMlrState(address) == kMlrStateRegistering) { - bool success = aSuccess || !AddressListContains(aFailedAddresses, aFailedAddressNum, address); + bool success = aSuccess || !aFailedAddresses.IsEmptyOrContains(address); child.SetAddressMlrState(address, success ? kMlrStateRegistered : kMlrStateToRegister); } @@ -583,7 +568,7 @@ void MlrManager::HandleTimeTick(void) { if (mSendDelay > 0 && --mSendDelay == 0) { - SendMulticastListenerRegistration(); + SendMlr(); } if (mReregistrationDelay > 0 && --mReregistrationDelay == 0) @@ -678,74 +663,34 @@ void MlrManager::LogMulticastAddresses(void) #endif // OT_SHOULD_LOG_AT(OT_LOG_LEVEL_DEBG) } -void MlrManager::AppendToUniqueAddressList(Ip6::Address (&aAddresses)[Ip6AddressesTlv::kMaxAddresses], - uint8_t &aAddressNum, - const Ip6::Address &aAddress) +void MlrManager::AddressArray::AddUnique(const Ip6::Address &aAddress) { -#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE - for (uint8_t i = 0; i < aAddressNum; i++) + if (!Contains(aAddress)) { - if (aAddresses[i] == aAddress) - { - ExitNow(); - } + IgnoreError(PushBack(aAddress)); } -#endif - - aAddresses[aAddressNum++] = aAddress; - -#if OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE -exit: -#endif - return; -} - -bool MlrManager::AddressListContains(const Ip6::Address *aAddressList, - uint8_t aAddressListSize, - const Ip6::Address &aAddress) -{ - bool contains = false; - - // An empty address list is treated as if it contains all failed addresses. - VerifyOrExit(aAddressListSize > 0, contains = true); - - for (uint8_t i = 0; i < aAddressListSize; i++) - { - if (aAddressList[i] == aAddress) - { - ExitNow(contains = true); - } - } - -exit: - return contains; } -void MlrManager::LogMlrResponse(Error aResult, - Error aError, - uint8_t aStatus, - const Ip6::Address *aFailedAddresses, - uint8_t aFailedAddressNum) +void MlrManager::LogMlrResponse(Error aResult, Error aError, uint8_t aStatus, const AddressArray &aFailedAddresses) { OT_UNUSED_VARIABLE(aResult); OT_UNUSED_VARIABLE(aError); OT_UNUSED_VARIABLE(aStatus); OT_UNUSED_VARIABLE(aFailedAddresses); - OT_UNUSED_VARIABLE(aFailedAddressNum); #if OT_SHOULD_LOG_AT(OT_LOG_LEVEL_WARN) - if (aResult == kErrorNone && aError == kErrorNone && aStatus == ThreadStatusTlv::MlrStatus::kMlrSuccess) + if (aResult == kErrorNone && aError == kErrorNone && aStatus == ThreadStatusTlv::kMlrSuccess) { LogInfo("Receive MLR.rsp OK"); } else { LogWarn("Receive MLR.rsp: result=%s, error=%s, status=%d, failedAddressNum=%d", ErrorToString(aResult), - ErrorToString(aError), aStatus, aFailedAddressNum); + ErrorToString(aError), aStatus, aFailedAddresses.GetLength()); - for (uint8_t i = 0; i < aFailedAddressNum; i++) + for (const Ip6::Address &address : aFailedAddresses) { - LogWarn("MA failed: %s", aFailedAddresses[i].ToString().AsCString()); + LogWarn("MA failed: %s", address.ToString().AsCString()); } } #endif diff --git a/src/core/thread/mlr_manager.hpp b/src/core/thread/mlr_manager.hpp index 03ea44709..b1c1f63c0 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/array.hpp" #include "common/callback.hpp" #include "common/locator.hpp" #include "common/non_copyable.hpp" @@ -80,6 +81,8 @@ class MlrManager : public InstanceLocator, private NonCopyable friend class ot::TimeTicker; public: + typedef otIp6RegisterMulticastListenersCallback MlrCallback; + /** * Initializes the object. * @@ -135,44 +138,46 @@ class MlrManager : public InstanceLocator, private NonCopyable * @retval kErrorNoBufs If insufficient message buffers available. * */ - Error RegisterMulticastListeners(const otIp6Address *aAddresses, - uint8_t aAddressNum, - const uint32_t *aTimeout, - otIp6RegisterMulticastListenersCallback aCallback, - void *aContext); + Error RegisterMulticastListeners(const Ip6::Address *aAddresses, + uint8_t aAddressNum, + const uint32_t *aTimeout, + MlrCallback aCallback, + void *aContext); #endif private: + class AddressArray : public Array + { + public: + bool IsEmptyOrContains(const Ip6::Address &aAddress) const { return IsEmpty() || Contains(aAddress); } + void AddUnique(const Ip6::Address &aAddress); + }; + void HandleNotifierEvents(Events aEvents); - void SendMulticastListenerRegistration(void); - Error SendMulticastListenerRegistrationMessage(const otIp6Address *aAddresses, - uint8_t aAddressNum, - const uint32_t *aTimeout, - Coap::ResponseHandler aResponseHandler, - void *aResponseContext); - - static void HandleMulticastListenerRegistrationResponse(void *aContext, - otMessage *aMessage, - const otMessageInfo *aMessageInfo, - Error aResult); - 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); + void SendMlr(void); + Error SendMlrMessage(const Ip6::Address *aAddresses, + uint8_t aAddressNum, + const uint32_t *aTimeout, + Coap::ResponseHandler aResponseHandler, + void *aResponseContext); + + static void HandleMlrResponse(void *aContext, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, + Error aResult); + void HandleMlrResponse(Coap::Message *aMessage, const Ip6::MessageInfo *aMessageInfo, Error aResult); + static Error ParseMlrResponse(Error aResult, + Coap::Message *aMessage, + uint8_t &aStatus, + AddressArray &aFailedAddresses); #if OPENTHREAD_FTD && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE - static void HandleRegisterMulticastListenersResponse(void *aContext, - otMessage *aMessage, - const otMessageInfo *aMessageInfo, - Error aResult); - void HandleRegisterMulticastListenersResponse(otMessage *aMessage, - const otMessageInfo *aMessageInfo, - Error aResult); + static void HandleRegisterResponse(void *aContext, + otMessage *aMessage, + const otMessageInfo *aMessageInfo, + Error aResult); + void HandleRegisterResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, Error aResult); #endif #if OPENTHREAD_CONFIG_MLR_ENABLE @@ -189,16 +194,7 @@ class MlrManager : public InstanceLocator, private NonCopyable #endif void SetMulticastAddressMlrState(MlrState aFromState, MlrState aToState); - void FinishMulticastListenerRegistration(bool aSuccess, - const Ip6::Address *aFailedAddresses, - uint8_t aFailedAddressNum); - - void AppendToUniqueAddressList(Ip6::Address (&aAddresses)[Ip6AddressesTlv::kMaxAddresses], - uint8_t &aAddressNum, - const Ip6::Address &aAddress); - static bool AddressListContains(const Ip6::Address *aAddressList, - uint8_t aAddressListSize, - const Ip6::Address &aAddress); + void FinishMlr(bool aSuccess, const AddressArray &aFailedAddresses); void ScheduleSend(uint16_t aDelay); void UpdateTimeTickerRegistration(void); @@ -208,14 +204,10 @@ class MlrManager : public InstanceLocator, private NonCopyable void LogMulticastAddresses(void); void CheckInvariants(void) const; - static void LogMlrResponse(Error aResult, - Error aError, - uint8_t aStatus, - const Ip6::Address *aFailedAddresses, - uint8_t aFailedAddressNum); + static void LogMlrResponse(Error aResult, Error aError, uint8_t aStatus, const AddressArray &aFailedAddresses); #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE - Callback mRegisterMulticastListenersCallback; + Callback mRegisterCallback; #endif uint32_t mReregistrationDelay; @@ -223,7 +215,7 @@ class MlrManager : public InstanceLocator, private NonCopyable bool mMlrPending : 1; #if (OPENTHREAD_FTD && OPENTHREAD_CONFIG_TMF_PROXY_MLR_ENABLE) && OPENTHREAD_CONFIG_COMMISSIONER_ENABLE - bool mRegisterMulticastListenersPending : 1; + bool mRegisterPending : 1; #endif }; diff --git a/src/core/thread/thread_netif.cpp b/src/core/thread/thread_netif.cpp index bba16dec6..8e6848832 100644 --- a/src/core/thread/thread_netif.cpp +++ b/src/core/thread/thread_netif.cpp @@ -97,7 +97,7 @@ void ThreadNetif::Down(void) #if OPENTHREAD_CONFIG_DNSSD_SERVER_ENABLE Get().Stop(); #endif -#if OPENTHREAD_CONFIG_DTLS_ENABLE +#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE Get().Stop(); #endif IgnoreError(Get().Stop()); diff --git a/src/core/thread/tmf.cpp b/src/core/thread/tmf.cpp index 583f6e29d..477e4d1ed 100644 --- a/src/core/thread/tmf.cpp +++ b/src/core/thread/tmf.cpp @@ -271,7 +271,7 @@ Message::Priority Agent::DscpToPriority(uint8_t aDscp) return priority; } -#if OPENTHREAD_CONFIG_DTLS_ENABLE +#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE SecureAgent::SecureAgent(Instance &aInstance) : Coap::CoapSecure(aInstance) @@ -329,7 +329,7 @@ bool SecureAgent::HandleResource(const char *aUriPath, Message &aMessage, const return didHandle; } -#endif // OPENTHREAD_CONFIG_DTLS_ENABLE +#endif // OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE } // namespace Tmf } // namespace ot diff --git a/src/core/thread/tmf.hpp b/src/core/thread/tmf.hpp index 3b3854444..c90e24257 100644 --- a/src/core/thread/tmf.hpp +++ b/src/core/thread/tmf.hpp @@ -214,7 +214,7 @@ class Agent : public Coap::Coap static Error Filter(const Message &aMessage, const Ip6::MessageInfo &aMessageInfo, void *aContext); }; -#if OPENTHREAD_CONFIG_DTLS_ENABLE +#if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE /** * Implements functionality of the secure TMF agent. diff --git a/src/core/utils/heap.hpp b/src/core/utils/heap.hpp index c4125b14b..22868449c 100644 --- a/src/core/utils/heap.hpp +++ b/src/core/utils/heap.hpp @@ -227,7 +227,7 @@ class Heap : private NonCopyable size_t GetFreeSize(void) const { return mMemory.mFreeSize; } private: -#if OPENTHREAD_CONFIG_TLS_ENABLE || OPENTHREAD_CONFIG_DTLS_ENABLE +#if OPENTHREAD_CONFIG_TLS_ENABLE || OPENTHREAD_CONFIG_SECURE_TRANSPORT_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/link_metrics_manager.cpp b/src/core/utils/link_metrics_manager.cpp index bbdac1b5d..ecb990ddc 100644 --- a/src/core/utils/link_metrics_manager.cpp +++ b/src/core/utils/link_metrics_manager.cpp @@ -85,6 +85,7 @@ Error LinkMetricsManager::GetLinkMetricsValueByExtAddr(const Mac::ExtAddress subject = mSubjectList.FindMatching(aExtAddress); VerifyOrExit(subject != nullptr, error = kErrorNotFound); + VerifyOrExit(subject->mState == kActive || subject->mState == kRenewing, error = kErrorInvalidState); aMetricsValues.mLinkMarginValue = subject->mData.mLinkMargin; aMetricsValues.mRssiValue = subject->mData.mRssi; diff --git a/src/core/utils/link_metrics_manager.hpp b/src/core/utils/link_metrics_manager.hpp index 9ab9ab331..098865e31 100644 --- a/src/core/utils/link_metrics_manager.hpp +++ b/src/core/utils/link_metrics_manager.hpp @@ -35,7 +35,7 @@ #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE == 0 #error \ - "OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE can only be used when OPENTHREAD_CONFIG_LINK_METRICS_INITIATOR_ENABLE is true" + "OPENTHREAD_CONFIG_LINK_METRICS_MANAGER_ENABLE can only be used when OPENTHREAD_CONFIG_MLE_LINK_METRICS_INITIATOR_ENABLE is true" #endif #include diff --git a/src/lib/platform/BUILD.gn b/src/lib/platform/BUILD.gn index c46831e0b..49525340f 100644 --- a/src/lib/platform/BUILD.gn +++ b/src/lib/platform/BUILD.gn @@ -38,5 +38,6 @@ config("platform_config") { static_library("libopenthread-platform") { sources = platform_sources + public_deps = [ "../../core:libopenthread_core_headers" ] public_configs = [ ":platform_config" ] } diff --git a/src/lib/platform/exit_code.h b/src/lib/platform/exit_code.h index 4aef8bdd2..ee9e7d237 100644 --- a/src/lib/platform/exit_code.h +++ b/src/lib/platform/exit_code.h @@ -38,6 +38,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { #endif diff --git a/src/lib/spinel/CMakeLists.txt b/src/lib/spinel/CMakeLists.txt index e6cf16f58..8d552cf08 100644 --- a/src/lib/spinel/CMakeLists.txt +++ b/src/lib/spinel/CMakeLists.txt @@ -62,6 +62,17 @@ set(COMMON_SOURCES spinel_encoder.cpp ) +set(OT_SPINEL_VENDOR_HOOK_SOURCE "" CACHE STRING "set vendor hook source file for Spinel") +set(OT_SPINEL_VENDOR_HOOK_HEADER "" CACHE STRING "set vendor hook header file for Spinel") +if(OT_SPINEL_VENDOR_HOOK_SOURCE) + target_compile_definitions(openthread-spinel-rcp PUBLIC "OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK=1") + target_compile_definitions(openthread-spinel-ncp PUBLIC "OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK=1") + target_compile_definitions(openthread-spinel-rcp PUBLIC "OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER=\"${OT_SPINEL_VENDOR_HOOK_HEADER}\"") + target_compile_definitions(openthread-spinel-ncp PUBLIC "OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER=\"${OT_SPINEL_VENDOR_HOOK_HEADER}\"") + list(APPEND COMMON_SOURCES ${OT_SPINEL_VENDOR_HOOK_SOURCE_DIR}${OT_SPINEL_VENDOR_HOOK_SOURCE}) + list(APPEND COMMON_INCLUDES ${OT_SPINEL_VENDOR_HOOK_SOURCE_DIR}) +endif() + target_include_directories(openthread-radio-spinel PUBLIC ${OT_PUBLIC_INCLUDES} PRIVATE ${COMMON_INCLUDES}) target_include_directories(openthread-spinel-ncp PUBLIC ${OT_PUBLIC_INCLUDES} PRIVATE ${COMMON_INCLUDES}) target_include_directories(openthread-spinel-rcp PUBLIC ${OT_PUBLIC_INCLUDES} PRIVATE ${COMMON_INCLUDES}) diff --git a/src/lib/spinel/example_vendor_hook.cpp b/src/lib/spinel/example_vendor_hook.cpp new file mode 100644 index 000000000..3c0a73a15 --- /dev/null +++ b/src/lib/spinel/example_vendor_hook.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 shows how to implement the Radio Spinel vendor hook. + */ + +#if OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK + +#include OPENTRHEAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER +#include "common/log.hpp" +#include "lib/platform/exit_code.h" + +namespace ot { +namespace Spinel { + +otError RadioSpinel::VendorHandleValueIs(spinel_prop_key_t aPropKey) +{ + otError error = OT_ERROR_NONE; + + switch (aPropKey) + { + // TODO: Implement your property get handlers here. + // + // Get handler should retrieve the property value and then encode and write the + // value into the NCP buffer. If the "get" operation itself fails, handler should + // write a `LAST_STATUS` with the error status into the NCP buffer. `OT_ERROR_NO_BUFS` + // should be returned if NCP buffer is full and response cannot be written. + default: + error = OT_ERROR_NOT_FOUND; + break; + } +exit: + return error; +} + +} // namespace Spinel +} // namespace ot + +extern ot::Spinel::RadioSpinel &GetRadioSpinel(void); + +#endif // OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK diff --git a/src/lib/spinel/example_vendor_hook.hpp b/src/lib/spinel/example_vendor_hook.hpp new file mode 100644 index 000000000..f9dedb626 --- /dev/null +++ b/src/lib/spinel/example_vendor_hook.hpp @@ -0,0 +1,59 @@ +/* + * 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 shows how to implement the Radio Spinel vendor hook. + */ + +#ifndef SPINEL_EXTENSION_H +#define SPINEL_EXTENSION_H + +#if OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK + +#include +#include "lib/spinel/radio_spinel.hpp" + +namespace ot { +namespace Spinel { + +class VendorRadioSpinel : public ot::Spinel::RadioSpinel +{ +public: + VendorRadioSpinel(void) + : ot::Spinel::RadioSpinel() + { + } + + // Add public/private methods or member variables +}; + +} // namespace Spinel +} // namespace ot + +#endif // OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK +#endif // SPINEL_EXTENSION_H diff --git a/src/lib/spinel/openthread-spinel-config.h b/src/lib/spinel/openthread-spinel-config.h index 086a75842..231a86838 100644 --- a/src/lib/spinel/openthread-spinel-config.h +++ b/src/lib/spinel/openthread-spinel-config.h @@ -78,4 +78,35 @@ #ifndef OPENTHREAD_SPINEL_CONFIG_RCP_TIME_SYNC_INTERVAL #define OPENTHREAD_SPINEL_CONFIG_RCP_TIME_SYNC_INTERVAL (60 * 1000 * 1000) #endif + +/** + * @def OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID + * + * Define broadcast IID for spinel frames dedicated to all hosts in multipan configuration. + * + */ +#ifndef OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID +#define OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID SPINEL_HEADER_IID_3 +#endif + +/** + * @def OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK + * + * Enables compilation of vendor specific code for Spinel + * + */ +#ifndef OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK +#define OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK 0 +#endif + +/** + * @def OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER + * + * Header file defining class VendorRadioSpinel + * + */ +#ifndef OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER +#define OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER "lib/spinel/example_vendor_hook.hpp" +#endif + #endif // OPENTHREAD_SPINEL_CONFIG_H_ diff --git a/src/lib/spinel/radio_spinel.cpp b/src/lib/spinel/radio_spinel.cpp index ffca5b277..7da561ae2 100644 --- a/src/lib/spinel/radio_spinel.cpp +++ b/src/lib/spinel/radio_spinel.cpp @@ -51,6 +51,35 @@ namespace ot { namespace Spinel { +char RadioSpinel::sVersion[kVersionStringSize] = ""; + +otExtAddress RadioSpinel::sIeeeEui64; + +bool RadioSpinel::sIsReady = false; ///< NCP ready. + +bool RadioSpinel::sSupportsLogStream = + false; ///< RCP supports `LOG_STREAM` property with OpenThread log meta-data format. + +bool RadioSpinel::sSupportsResetToBootloader = false; ///< RCP supports resetting into bootloader mode. + +otRadioCaps RadioSpinel::sRadioCaps = OT_RADIO_CAPS_NONE; + +inline bool RadioSpinel::IsFrameForUs(spinel_iid_t aIid) +{ + bool found = false; + + for (spinel_iid_t iid : mIidList) + { + if (aIid == iid) + { + ExitNow(found = true); + } + } + +exit: + return found; +} + RadioSpinel::RadioSpinel(void) : mInstance(nullptr) , mSpinelInterface(nullptr) @@ -62,21 +91,19 @@ RadioSpinel::RadioSpinel(void) , mPropertyFormat(nullptr) , mExpectedCommand(0) , mError(OT_ERROR_NONE) + , mIid(SPINEL_HEADER_INVALID_IID) , mTransmitFrame(nullptr) , mShortAddress(0) , mPanId(0xffff) - , mRadioCaps(0) , mChannel(0) , mRxSensitivity(0) , mState(kStateDisabled) , mIsPromiscuous(false) , mRxOnWhenIdle(true) - , mIsReady(false) - , mSupportsLogStream(false) - , mSupportsResetToBootloader(false) , mIsTimeSynced(false) #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 , mRcpFailureCount(0) + , mRcpFailure(kRcpFailureNone) , mSrcMatchShortEntryCount(0) , mSrcMatchExtEntryCount(0) , mMacKeySet(false) @@ -84,7 +111,6 @@ RadioSpinel::RadioSpinel(void) , mTransmitPowerSet(false) , mCoexEnabledSet(false) , mFemLnaGainSet(false) - , mRcpFailed(false) , mEnergyScanning(false) , mMacFrameCounterSet(false) #endif @@ -97,11 +123,16 @@ RadioSpinel::RadioSpinel(void) , mRadioTimeRecalcStart(UINT64_MAX) , mRadioTimeOffset(UINT64_MAX) { - mVersion[0] = '\0'; + memset(mIidList, SPINEL_HEADER_INVALID_IID, sizeof(mIidList)); memset(&mRadioSpinelMetrics, 0, sizeof(mRadioSpinelMetrics)); + memset(&mCallbacks, 0, sizeof(mCallbacks)); } -void RadioSpinel::Init(SpinelInterface &aSpinelInterface, bool aResetRadio, bool aSkipRcpCompatibilityCheck) +void RadioSpinel::Init(SpinelInterface &aSpinelInterface, + bool aResetRadio, + bool aSkipRcpCompatibilityCheck, + const spinel_iid_t *aIidList, + uint8_t aIidListLength) { otError error = OT_ERROR_NONE; bool supportsRcpApiVersion; @@ -114,10 +145,16 @@ void RadioSpinel::Init(SpinelInterface &aSpinelInterface, bool aResetRadio, bool mSpinelInterface = &aSpinelInterface; SuccessOrDie(mSpinelInterface->Init(HandleReceivedFrame, this, mRxFrameBuffer)); + VerifyOrDie(aIidList != nullptr, OT_EXIT_INVALID_ARGUMENTS); + VerifyOrDie(aIidListLength != 0 && aIidListLength <= OT_ARRAY_LENGTH(mIidList), OT_EXIT_INVALID_ARGUMENTS); + mIid = aIidList[0]; + memset(mIidList, SPINEL_HEADER_INVALID_IID, sizeof(mIidList)); + memcpy(mIidList, aIidList, aIidListLength * sizeof(spinel_iid_t)); + 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)); + SuccessOrExit(error = Get(SPINEL_PROP_NCP_VERSION, SPINEL_DATATYPE_UTF8_S, sVersion, sizeof(sVersion))); + SuccessOrExit(error = Get(SPINEL_PROP_HWADDR, SPINEL_DATATYPE_EUI64_S, sIeeeEui64.m8)); VerifyOrDie(IsRcp(supportsRcpApiVersion, supportsRcpMinHostApiVersion), OT_EXIT_RADIO_SPINEL_INCOMPATIBLE); @@ -135,16 +172,34 @@ void RadioSpinel::Init(SpinelInterface &aSpinelInterface, bool aResetRadio, bool SuccessOrDie(error); } +void RadioSpinel::SetCallbacks(const struct RadioSpinelCallbacks &aCallbacks) +{ +#if OPENTHREAD_CONFIG_DIAG_ENABLE + assert(aCallbacks.mDiagReceiveDone != nullptr); + assert(aCallbacks.mDiagTransmitDone != nullptr); +#endif + assert(aCallbacks.mEnergyScanDone != nullptr); + assert(aCallbacks.mReceiveDone != nullptr); + assert(aCallbacks.mTransmitDone != nullptr); + assert(aCallbacks.mTxStarted != nullptr); + + mCallbacks = aCallbacks; +} + void RadioSpinel::ResetRcp(bool aResetRadio) { bool hardwareReset; bool resetDone = false; - mIsReady = false; + // Avoid resetting the device twice in a row in Multipan RCP architecture + VerifyOrExit(!sIsReady, resetDone = true); + mWaitingKey = SPINEL_PROP_LAST_STATUS; - if (aResetRadio && (SendReset(SPINEL_RESET_STACK) == OT_ERROR_NONE) && (WaitResponse(false) == OT_ERROR_NONE)) + if (aResetRadio && (SendReset(SPINEL_RESET_STACK) == OT_ERROR_NONE) && (!sIsReady) && + (WaitResponse(false) == OT_ERROR_NONE)) { + VerifyOrExit(sIsReady, resetDone = false); LogInfo("Software reset RCP successfully"); ExitNow(resetDone = true); } @@ -230,7 +285,7 @@ bool RadioSpinel::IsRcp(bool &aSupportsRcpApiVersion, bool &aSupportsRcpMinHostA if (capability == SPINEL_CAP_OPENTHREAD_LOG_METADATA) { - mSupportsLogStream = true; + sSupportsLogStream = true; } if (capability == SPINEL_CAP_RCP_API_VERSION) @@ -240,7 +295,7 @@ bool RadioSpinel::IsRcp(bool &aSupportsRcpApiVersion, bool &aSupportsRcpMinHostA if (capability == SPINEL_CAP_RCP_RESET_TO_BOOTLOADER) { - mSupportsResetToBootloader = true; + sSupportsResetToBootloader = true; } if (capability == SPINEL_PROP_RCP_MIN_HOST_API_VERSION) @@ -273,11 +328,11 @@ otError RadioSpinel::CheckRadioCapabilities(void) unsigned int radioCaps; SuccessOrExit(error = Get(SPINEL_PROP_RADIO_CAPS, SPINEL_DATATYPE_UINT_PACKED_S, &radioCaps)); - mRadioCaps = static_cast(radioCaps); + sRadioCaps = static_cast(radioCaps); - if ((mRadioCaps & kRequiredRadioCaps) != kRequiredRadioCaps) + if ((sRadioCaps & kRequiredRadioCaps) != kRequiredRadioCaps) { - otRadioCaps missingCaps = (mRadioCaps & kRequiredRadioCaps) ^ kRequiredRadioCaps; + otRadioCaps missingCaps = (sRadioCaps & kRequiredRadioCaps) ^ kRequiredRadioCaps; // missingCaps may be an unused variable when LogCrit is blank // avoid compiler warning in that case @@ -355,6 +410,7 @@ void RadioSpinel::Deinit(void) } // This allows implementing pseudo reset. + sIsReady = false; new (this) RadioSpinel(); } @@ -369,9 +425,16 @@ void RadioSpinel::HandleReceivedFrame(void) 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 && - SPINEL_HEADER_GET_IID(header) == 0, - error = OT_ERROR_PARSE); + // Accept spinel messages with the correct IID or broadcast IID. + spinel_iid_t iid = SPINEL_HEADER_GET_IID(header); + + if (!IsFrameForUs(iid)) + { + mRxFrameBuffer.DiscardFrame(); + ExitNow(); + } + + VerifyOrExit(unpacked > 0 && (header & SPINEL_HEADER_FLAG) == SPINEL_HEADER_FLAG, error = OT_ERROR_PARSE); if (SPINEL_HEADER_GET_TID(header) == 0) { @@ -604,7 +667,14 @@ void RadioSpinel::HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, } LogInfo("RCP reset: %s", spinel_status_to_cstr(status)); - mIsReady = true; + sIsReady = true; + } + else if (status == SPINEL_STATUS_SWITCHOVER_DONE || status == SPINEL_STATUS_SWITCHOVER_FAILED) + { + if (mCallbacks.mSwitchoverDone != nullptr) + { + mCallbacks.mSwitchoverDone(mInstance, status == SPINEL_STATUS_SWITCHOVER_DONE); + } } else { @@ -624,7 +694,7 @@ void RadioSpinel::HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, mEnergyScanning = false; #endif - otPlatRadioEnergyScanDone(mInstance, maxRssi); + mCallbacks.mEnergyScanDone(mInstance, maxRssi); } else if (aKey == SPINEL_PROP_STREAM_DEBUG) { @@ -637,7 +707,7 @@ void RadioSpinel::HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, logStream[len] = '\0'; LogDebg("RCP => %s", logStream); } - else if ((aKey == SPINEL_PROP_STREAM_LOG) && mSupportsLogStream) + else if ((aKey == SPINEL_PROP_STREAM_LOG) && sSupportsLogStream) { const char *logString; uint8_t logLevel; @@ -677,6 +747,12 @@ void RadioSpinel::HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, break; } } +#if OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK + else if (aKey >= SPINEL_PROP_VENDOR__BEGIN && aKey < SPINEL_PROP_VENDOR__END) + { + error = VendorHandleValueIs(aKey); + } +#endif exit: UpdateParseErrorCount(error); @@ -719,7 +795,7 @@ otError RadioSpinel::ParseRadioFrame(otRadioFrame &aFrame, aBuffer += unpacked; aLength -= static_cast(unpacked); - if (mRadioCaps & OT_RADIO_CAPS_TRANSMIT_SEC) + if (sRadioCaps & OT_RADIO_CAPS_TRANSMIT_SEC) { unpacked = spinel_datatype_unpack_in_place(aBuffer, aLength, @@ -796,14 +872,13 @@ void RadioSpinel::RadioReceive(void) #if OPENTHREAD_CONFIG_DIAG_ENABLE if (otPlatDiagModeGet()) { - otPlatDiagRadioReceiveDone(mInstance, &mRxRadioFrame, OT_ERROR_NONE); + mCallbacks.mDiagReceiveDone(mInstance, &mRxRadioFrame, OT_ERROR_NONE); } else #endif { - otPlatRadioReceiveDone(mInstance, &mRxRadioFrame, OT_ERROR_NONE); + mCallbacks.mReceiveDone(mInstance, &mRxRadioFrame, OT_ERROR_NONE); } - exit: return; } @@ -813,12 +888,12 @@ void RadioSpinel::TransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, ot #if OPENTHREAD_CONFIG_DIAG_ENABLE if (otPlatDiagModeGet()) { - otPlatDiagRadioTransmitDone(mInstance, aFrame, aError); + mCallbacks.mDiagTransmitDone(mInstance, aFrame, aError); } else #endif { - otPlatRadioTxDone(mInstance, aFrame, aAckFrame, aError); + mCallbacks.mTransmitDone(mInstance, aFrame, aAckFrame, aError); } } @@ -991,7 +1066,7 @@ otError RadioSpinel::SetMacFrameCounter(uint32_t aMacFrameCounter, bool aSetIfLa otError RadioSpinel::GetIeeeEui64(uint8_t *aIeeeEui64) { - memcpy(aIeeeEui64, mIeeeEui64.m8, sizeof(mIeeeEui64.m8)); + memcpy(aIeeeEui64, sIeeeEui64.m8, sizeof(sIeeeEui64.m8)); return OT_ERROR_NONE; } @@ -1296,7 +1371,7 @@ otError RadioSpinel::EnergyScan(uint8_t aScanChannel, uint16_t aScanDuration) { otError error; - VerifyOrExit(mRadioCaps & OT_RADIO_CAPS_ENERGY_SCAN, error = OT_ERROR_NOT_CAPABLE); + VerifyOrExit(sRadioCaps & OT_RADIO_CAPS_ENERGY_SCAN, error = OT_ERROR_NOT_CAPABLE); #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 mScanChannel = aScanChannel; @@ -1329,7 +1404,7 @@ otError RadioSpinel::Get(spinel_prop_key_t aKey, const char *aFormat, ...) error = RequestWithPropertyFormatV(aFormat, SPINEL_CMD_PROP_VALUE_GET, aKey, nullptr, mPropertyArgs); va_end(mPropertyArgs); #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 - } while (mRcpFailed); + } while (mRcpFailure != kRcpFailureNone); #endif return error; @@ -1356,7 +1431,7 @@ otError RadioSpinel::GetWithParam(spinel_prop_key_t aKey, aParamSize); va_end(mPropertyArgs); #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 - } while (mRcpFailed); + } while (mRcpFailure != kRcpFailureNone); #endif return error; @@ -1378,7 +1453,7 @@ otError RadioSpinel::Set(spinel_prop_key_t aKey, const char *aFormat, ...) mPropertyArgs); va_end(mPropertyArgs); #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 - } while (mRcpFailed); + } while (mRcpFailure != kRcpFailureNone); #endif return error; @@ -1400,7 +1475,7 @@ otError RadioSpinel::Insert(spinel_prop_key_t aKey, const char *aFormat, ...) mPropertyArgs); va_end(mPropertyArgs); #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 - } while (mRcpFailed); + } while (mRcpFailure != kRcpFailureNone); #endif return error; @@ -1422,7 +1497,7 @@ otError RadioSpinel::Remove(spinel_prop_key_t aKey, const char *aFormat, ...) mPropertyArgs); va_end(mPropertyArgs); #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 - } while (mRcpFailed); + } while (mRcpFailure != kRcpFailureNone); #endif return error; @@ -1448,7 +1523,7 @@ otError RadioSpinel::WaitResponse(bool aHandleRcpTimeout) } ExitNow(mError = OT_ERROR_RESPONSE_TIMEOUT); } - } while (mWaitingTid || !mIsReady); + } while (mWaitingTid || !sIsReady); LogIfFail("Error waiting response", mError); // This indicates end of waiting response. @@ -1488,14 +1563,14 @@ otError RadioSpinel::SendReset(uint8_t aResetType) uint8_t buffer[kMaxSpinelFrame]; spinel_ssize_t packed; - if ((aResetType == SPINEL_RESET_BOOTLOADER) && !mSupportsResetToBootloader) + if ((aResetType == SPINEL_RESET_BOOTLOADER) && !sSupportsResetToBootloader) { ExitNow(error = OT_ERROR_NOT_CAPABLE); } // Pack the header, command and key packed = spinel_datatype_pack(buffer, sizeof(buffer), SPINEL_DATATYPE_COMMAND_S SPINEL_DATATYPE_UINT8_S, - SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_RESET, aResetType); + SPINEL_HEADER_FLAG | SPINEL_HEADER_IID(mIid), SPINEL_CMD_RESET, aResetType); VerifyOrExit(packed > 0 && static_cast(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS); @@ -1518,7 +1593,7 @@ otError RadioSpinel::SendCommand(uint32_t aCommand, uint16_t offset; // Pack the header, command and key - packed = spinel_datatype_pack(buffer, sizeof(buffer), "Cii", SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0 | tid, + packed = spinel_datatype_pack(buffer, sizeof(buffer), "Cii", SPINEL_HEADER_FLAG | SPINEL_HEADER_IID(mIid) | tid, aCommand, aKey); VerifyOrExit(packed > 0 && static_cast(packed) <= sizeof(buffer), error = OT_ERROR_NO_BUFS); @@ -1668,7 +1743,7 @@ void RadioSpinel::HandleTransmitDone(uint32_t aCommand, static_cast(mTransmitFrame)->SetIsHeaderUpdated(headerUpdated); - if ((mRadioCaps & OT_RADIO_CAPS_TRANSMIT_SEC) && headerUpdated && + if ((sRadioCaps & OT_RADIO_CAPS_TRANSMIT_SEC) && headerUpdated && static_cast(mTransmitFrame)->GetSecurityEnabled()) { uint8_t keyId; @@ -1698,12 +1773,12 @@ otError RadioSpinel::Transmit(otRadioFrame &aFrame) { otError error = OT_ERROR_INVALID_STATE; - VerifyOrExit(mState == kStateReceive || (mState == kStateSleep && (mRadioCaps & OT_RADIO_CAPS_SLEEP_TO_TX))); + VerifyOrExit(mState == kStateReceive || (mState == kStateSleep && (sRadioCaps & OT_RADIO_CAPS_SLEEP_TO_TX))); mTransmitFrame = &aFrame; // `otPlatRadioTxStarted()` is triggered immediately for now, which may be earlier than real started time. - otPlatRadioTxStarted(mInstance, mTransmitFrame); + mCallbacks.mTxStarted(mInstance, mTransmitFrame); error = Request(SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_STREAM_RAW, SPINEL_DATATYPE_DATA_WLEN_S // Frame data @@ -1965,7 +2040,7 @@ void RadioSpinel::HandleRcpUnexpectedReset(spinel_status_t aStatus) LogCrit("Unexpected RCP reset: %s", spinel_status_to_cstr(aStatus)); #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 - mRcpFailed = true; + mRcpFailure = kRcpFailureUnexpectedReset; #elif OPENTHREAD_SPINEL_CONFIG_ABORT_ON_UNEXPECTED_RCP_RESET_ENABLE abort(); #else @@ -1978,9 +2053,9 @@ void RadioSpinel::HandleRcpTimeout(void) mRadioSpinelMetrics.mRcpTimeoutCount++; #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 - mRcpFailed = true; + mRcpFailure = kRcpFailureTimeout; #else - if (!mIsReady) + if (!sIsReady) { LogCrit("Failed to communicate with RCP - no response from RCP during initialization"); LogCrit("This is not a bug and typically due a config error (wrong URL parameters) or bad RCP image:"); @@ -1997,12 +2072,18 @@ void RadioSpinel::RecoverFromRcpFailure(void) #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 constexpr int16_t kMaxFailureCount = OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT; State recoveringState = mState; + bool skipReset = false; - if (!mRcpFailed) + if (mRcpFailure == kRcpFailureNone) { ExitNow(); } - mRcpFailed = false; + +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + skipReset = (mRcpFailure == kRcpFailureUnexpectedReset); +#endif + + mRcpFailure = kRcpFailureNone; LogWarn("RCP failure detected"); @@ -2025,7 +2106,15 @@ void RadioSpinel::RecoverFromRcpFailure(void) mError = OT_ERROR_NONE; mIsTimeSynced = false; - ResetRcp(mResetRadioOnStartup); + if (skipReset) + { + sIsReady = true; + } + else + { + ResetRcp(mResetRadioOnStartup); + } + SuccessOrDie(Set(SPINEL_PROP_PHY_ENABLED, SPINEL_DATATYPE_BOOL_S, true)); mState = kStateSleep; @@ -2148,7 +2237,7 @@ void RadioSpinel::RestoreProperties(void) } #endif // OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE - if ((mRadioCaps & OT_RADIO_CAPS_RX_ON_WHEN_IDLE) != 0) + if ((sRadioCaps & OT_RADIO_CAPS_RX_ON_WHEN_IDLE) != 0) { SuccessOrDie(Set(SPINEL_PROP_MAC_RX_ON_WHEN_IDLE_MODE, SPINEL_DATATYPE_BOOL_S, mRxOnWhenIdle)); } @@ -2157,6 +2246,32 @@ void RadioSpinel::RestoreProperties(void) } #endif // OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 +otError RadioSpinel::GetMultipanActiveInterface(spinel_iid_t *aIid) +{ + otError error = Get(SPINEL_PROP_MULTIPAN_ACTIVE_INTERFACE, SPINEL_DATATYPE_UINT8_S, aIid); + LogIfFail("Get GetMultipanActiveInterface failed", error); + return error; +} + +otError RadioSpinel::SetMultipanActiveInterface(spinel_iid_t aIid, bool aCompletePending) +{ + otError error; + uint8_t value; + + VerifyOrExit(aIid == (aIid & SPINEL_MULTIPAN_INTERFACE_ID_MASK), error = OT_ERROR_INVALID_ARGS); + + value = static_cast(aIid); + if (aCompletePending) + { + value |= (1 << SPINEL_MULTIPAN_INTERFACE_SOFT_SWITCH_SHIFT); + } + + error = Set(SPINEL_PROP_MULTIPAN_ACTIVE_INTERFACE, SPINEL_DATATYPE_UINT8_S, value); + +exit: + return error; +} + otError RadioSpinel::SetChannelMaxTransmitPower(uint8_t aChannel, int8_t aMaxPower) { otError error = OT_ERROR_NONE; @@ -2320,8 +2435,9 @@ void RadioSpinel::LogSpinelFrame(const uint8_t *aFrame, uint16_t aLength, bool a 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)); + start += Snprintf(start, static_cast(end - start), "%s, flg:0x%x, iid:%d, tid:%u, cmd:%s", prefix, + SPINEL_HEADER_GET_FLAG(header), SPINEL_HEADER_GET_IID(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)); diff --git a/src/lib/spinel/radio_spinel.hpp b/src/lib/spinel/radio_spinel.hpp index 70301ec9a..bc0774d32 100644 --- a/src/lib/spinel/radio_spinel.hpp +++ b/src/lib/spinel/radio_spinel.hpp @@ -46,6 +46,103 @@ namespace ot { namespace Spinel { +/** + * Maximum number of Spinel Interface IDs. + * + */ +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE +static constexpr uint8_t kSpinelHeaderMaxNumIid = 4; +#else +static constexpr uint8_t kSpinelHeaderMaxNumIid = 1; +#endif + +struct RadioSpinelCallbacks +{ + /** + * This callback notifies user of `RadioSpinel` of a received frame. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aFrame A pointer to the received frame or nullptr if the receive operation failed. + * @param[in] aError kErrorNone when successfully received a frame, + * kErrorAbort when reception was aborted and a frame was not received, + * kErrorNoBufs when a frame could not be received due to lack of rx buffer space. + * + */ + void (*mReceiveDone)(otInstance *aInstance, otRadioFrame *aFrame, Error aError); + + /** + * The callback notifies user of `RadioSpinel` that the transmit operation has completed, providing, if + * applicable, the received ACK frame. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aFrame The transmitted frame. + * @param[in] aAckFrame A pointer to the ACK frame, nullptr if no ACK was received. + * @param[in] aError kErrorNone when the frame was transmitted, + * kErrorNoAck when the frame was transmitted but no ACK was received, + * kErrorChannelAccessFailure tx failed due to activity on the channel, + * kErrorAbort when transmission was aborted for other reasons. + * + */ + void (*mTransmitDone)(otInstance *aInstance, otRadioFrame *aFrame, otRadioFrame *aAckFrame, Error aError); + + /** + * This callback notifies user of `RadioSpinel` that energy scan is complete. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aMaxRssi Maximum RSSI seen on the channel, or `SubMac::kInvalidRssiValue` if failed. + * + */ + void (*mEnergyScanDone)(otInstance *aInstance, int8_t aMaxRssi); + + /** + * This callback notifies user of `RadioSpinel` that the transmission has started. + * + * @param[in] aInstance A pointer to the OpenThread instance structure. + * @param[in] aFrame A pointer to the frame that is being transmitted. + * + */ + void (*mTxStarted)(otInstance *aInstance, otRadioFrame *aFrame); + + /** + * This callback notifies user of `RadioSpinel` that the radio interface switchover has completed. + * + * @param[in] aInstance A pointer to the OpenThread instance structure. + * @param[in] aSuccess A value indicating if the switchover was successful or not. + * + */ + void (*mSwitchoverDone)(otInstance *aInstance, bool aSuccess); + +#if OPENTHREAD_CONFIG_DIAG_ENABLE + /** + * This callback notifies diagnostics module using `RadioSpinel` of a received frame. + * + * This callback is used when diagnostics is enabled. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aFrame A pointer to the received frame or NULL if the receive operation failed. + * @param[in] aError OT_ERROR_NONE when successfully received a frame, + * OT_ERROR_ABORT when reception was aborted and a frame was not received, + * OT_ERROR_NO_BUFS when a frame could not be received due to lack of rx buffer space. + * + */ + void (*mDiagReceiveDone)(otInstance *aInstance, otRadioFrame *aFrame, Error aError); + + /** + * This callback notifies diagnostics module using `RadioSpinel` that the transmission has completed. + * + * This callback is used when diagnostics is enabled. + * + * @param[in] aInstance The OpenThread instance structure. + * @param[in] aFrame A pointer to the frame that was transmitted. + * @param[in] aError OT_ERROR_NONE when the frame was transmitted, + * OT_ERROR_CHANNEL_ACCESS_FAILURE tx could not take place due to activity on the + * channel, OT_ERROR_ABORT when transmission was aborted for other reasons. + * + */ + void (*mDiagTransmitDone)(otInstance *aInstance, otRadioFrame *aFrame, Error aError); +#endif // OPENTHREAD_CONFIG_DIAG_ENABLE +}; + /** * The class for providing a OpenThread radio interface by talking with a radio-only * co-processor(RCP). @@ -72,9 +169,24 @@ class RadioSpinel * @param[in] aSpinelInterface A reference to the Spinel interface. * @param[in] aResetRadio TRUE to reset on init, FALSE to not reset on init. * @param[in] aSkipRcpCompatibilityCheck TRUE to skip RCP compatibility check, FALSE to perform the check. + * @param[in] aIidList A Pointer to the list of IIDs to receive spinel frame from. + * First entry must be the IID of the Host Application. + * @param[in] aIidListLength The Length of the @p aIidList. * */ - void Init(SpinelInterface &aSpinelInterface, bool aResetRadio, bool aSkipRcpCompatibilityCheck); + void Init(SpinelInterface &aSpinelInterface, + bool aResetRadio, + bool aSkipRcpCompatibilityCheck, + const spinel_iid_t *aIidList, + uint8_t aIidListLength); + + /** + * This method sets the notification callbacks. + * + * @param[in] aCallbacks A pointer to structure with notification callbacks. + * + */ + void SetCallbacks(const struct RadioSpinelCallbacks &aCallbacks); /** * Deinitialize this radio transceiver. @@ -242,7 +354,7 @@ class RadioSpinel * @returns A pointer to the radio version string. * */ - const char *GetVersion(void) const { return mVersion; } + const char *GetVersion(void) const { return sVersion; } /** * Returns the radio capabilities. @@ -250,7 +362,7 @@ class RadioSpinel * @returns The radio capability bit vector. * */ - otRadioCaps GetRadioCaps(void) const { return mRadioCaps; } + otRadioCaps GetRadioCaps(void) const { return sRadioCaps; } /** * Gets the most recent RSSI measurement. @@ -322,6 +434,44 @@ class RadioSpinel otError GetCoexMetrics(otRadioCoexMetrics &aCoexMetrics); #endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE + /** + * Get currently active interface. + * + * @param[out] aIid IID of the interface that owns the radio. + * + * @retval OT_ERROR_NONE Successfully got the property. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * @retval OT_ERROR_NOT_IMPLEMENTED Failed due to lack of the support in radio + * @retval OT_ERROR_INVALID_COMMAND Platform supports all interfaces simultaneously. + * (i.e. no active/inactive interface concept in the platform level) + * + */ + otError GetMultipanActiveInterface(spinel_iid_t *aIid); + + /** + * Sets specified radio interface active + * + * This function allows selecting currently active radio interface on platforms that do not support parallel + * communication on multiple interfaces. I.e. if more than one interface is in receive state calling + * SetMultipanActiveInterface guarantees that specified interface will not be losing frames. This function + * returns if the request was received properly. After interface switching is complete SwitchoverDone callback is + * Invoked. Switching interfaces may take longer if aCompletePending is set true. + * + * @param[in] aIid IID of the interface to set active. + * @param[in] aCompletePending Set true if pending radio operation should complete first(Soft switch) or false if + * ongoing operations should be interrupted (Force switch). + * + * @retval OT_ERROR_NONE Successfully requested interface switch. + * @retval OT_ERROR_BUSY Failed due to another operation on going. + * @retval OT_ERROR_RESPONSE_TIMEOUT Failed due to no response received from the transceiver. + * @retval OT_ERROR_NOT_IMPLEMENTED Failed due to lack of support in radio for the given interface id or + * @retval OT_ERROR_INVALID_COMMAND Platform supports all interfaces simultaneously + * (i.e. no active/inactive interface concept in the platform level) + * @retval OT_ERROR_ALREADY Given interface is already active. + * + */ + otError SetMultipanActiveInterface(spinel_iid_t aIid, bool aCompletePending); + /** * Returns a reference to the transmit buffer. * @@ -915,6 +1065,26 @@ class RadioSpinel */ void RestoreProperties(void); #endif +#if OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK + /** + * Defines a vendor "set property handler" hook to process vendor spinel properties. + * + * The vendor handler should return `OT_ERROR_NOT_FOUND` status if it does not support "set" operation for the + * given property key. Otherwise, the vendor handler should behave like other property set handlers, i.e., it + * should first decode the value from the input spinel frame and then perform the corresponding set operation. The + * handler should not prepare the spinel response and therefore should not write anything to the NCP buffer. The + * `otError` returned from handler (other than `OT_ERROR_NOT_FOUND`) indicates the error in either parsing of the + * input or the error of the set operation. In case of a successful "set", `NcpBase` set command handler will call + * the `VendorGetPropertyHandler()` for the same property key to prepare the response. + * + * @param[in] aPropKey The spinel property key. + * + * @returns OT_ERROR_NOT_FOUND if it does not support the given property key, otherwise the error in either parsing + * of the input or the "set" operation. + * + */ + otError VendorHandleValueIs(spinel_prop_key_t aPropKey); +#endif private: enum @@ -935,11 +1105,9 @@ class RadioSpinel kStateTransmitDone, ///< Radio indicated frame transmission is done. }; - enum - { - kUsPerMs = 1000, ///< Microseconds per millisecond. - kTxWaitUs = 5000000, ///< Maximum time of waiting for `TransmitDone` event, in microseconds. - }; + static constexpr uint32_t kUsPerMs = 1000; ///< Microseconds per millisecond. + static constexpr uint64_t kTxWaitUs = + 5000000; ///< Maximum time of waiting for `TransmitDone` event, in microseconds. typedef otError (RadioSpinel::*ResponseHandler)(const uint8_t *aBuffer, uint16_t aLength); @@ -1006,6 +1174,17 @@ class RadioSpinel return !(aKey == SPINEL_PROP_STREAM_RAW || aKey == SPINEL_PROP_MAC_ENERGY_SCAN_RESULT); } + /** + * Checks whether given interface ID is part of list of IIDs to be allowed. + * + * @param[in] aIid Spinel Interface ID. + * + * @retval TRUE Given IID present in allow list. + * @retval FALSE Otherwise. + * + */ + inline bool IsFrameForUs(spinel_iid_t aIid); + void HandleNotification(SpinelInterface::RxFrameBuffer &aFrameBuffer); void HandleNotification(const uint8_t *aFrame, uint16_t aLength); void HandleValueIs(spinel_prop_key_t aKey, const uint8_t *aBuffer, uint16_t aLength); @@ -1054,6 +1233,8 @@ class RadioSpinel SpinelInterface::RxFrameBuffer mRxFrameBuffer; SpinelInterface *mSpinelInterface; + RadioSpinelCallbacks mCallbacks; ///< Callbacks for notifications of higher layer. + uint16_t mCmdTidsInUse; ///< Used transaction ids. spinel_tid_t mCmdNextTid; ///< Next available transaction id. spinel_tid_t mTxRadioTid; ///< The transaction id used to send a radio frame. @@ -1063,6 +1244,8 @@ class RadioSpinel 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. + spinel_iid_t mIid; ///< The spinel interface id used by this process. + spinel_iid_t mIidList[kSpinelHeaderMaxNumIid]; ///< Array of interface ids to accept the incoming spinel frames. uint8_t mRxPsdu[OT_RADIO_FRAME_MAX_SIZE]; uint8_t mTxPsdu[OT_RADIO_FRAME_MAX_SIZE]; @@ -1072,28 +1255,37 @@ class RadioSpinel otRadioFrame mAckRadioFrame; otRadioFrame *mTransmitFrame; ///< Points to the frame to send - otExtAddress mExtendedAddress; - uint16_t mShortAddress; - uint16_t mPanId; - otRadioCaps mRadioCaps; - uint8_t mChannel; - int8_t mRxSensitivity; - otError mTxError; - char mVersion[kVersionStringSize]; - otExtAddress mIeeeEui64; + otExtAddress mExtendedAddress; + uint16_t mShortAddress; + uint16_t mPanId; + uint8_t mChannel; + int8_t mRxSensitivity; + otError mTxError; + static char sVersion[kVersionStringSize]; + static otExtAddress sIeeeEui64; + static otRadioCaps sRadioCaps; State mState; - bool mIsPromiscuous : 1; ///< Promiscuous mode. - bool mRxOnWhenIdle : 1; ///< RxOnWhenIdle mode. - bool mIsReady : 1; ///< NCP ready. - bool mSupportsLogStream : 1; ///< RCP supports `LOG_STREAM` property with OpenThread log meta-data format. - bool mSupportsResetToBootloader : 1; ///< RCP supports resetting into bootloader mode. - bool mIsTimeSynced : 1; ///< Host has calculated the time difference between host and RCP. + bool mIsPromiscuous : 1; ///< Promiscuous mode. + bool mRxOnWhenIdle : 1; ///< RxOnWhenIdle mode. + bool mIsTimeSynced : 1; ///< Host has calculated the time difference between host and RCP. + + static bool sIsReady; ///< NCP ready. + static bool sSupportsLogStream; ///< RCP supports `LOG_STREAM` property with OpenThread log meta-data format. + static bool sSupportsResetToBootloader; ///< RCP supports resetting into bootloader mode. #if OPENTHREAD_SPINEL_CONFIG_RCP_RESTORATION_MAX_COUNT > 0 + enum + { + kRcpFailureNone, + kRcpFailureTimeout, + kRcpFailureUnexpectedReset, + }; + bool mResetRadioOnStartup : 1; ///< Whether should send reset command when init. int16_t mRcpFailureCount; ///< Count of consecutive RCP failures. + uint8_t mRcpFailure : 2; ///< RCP failure reason, should recover and retry operation. // Properties set by core. uint8_t mKeyIdMode; @@ -1118,7 +1310,6 @@ class RadioSpinel bool mTransmitPowerSet : 1; ///< Whether transmit power has been set. bool mCoexEnabledSet : 1; ///< Whether coex enabled has been set. bool mFemLnaGainSet : 1; ///< Whether FEM LNA gain has been set. - bool mRcpFailed : 1; ///< RCP failure happened, should recover and retry operation. bool mEnergyScanning : 1; ///< If fails while scanning, restarts scanning. bool mMacFrameCounterSet : 1; ///< Whether the MAC frame counter has been set. diff --git a/src/lib/spinel/spinel.c b/src/lib/spinel/spinel.c index 963f27626..82ea55556 100644 --- a/src/lib/spinel/spinel.c +++ b/src/lib/spinel/spinel.c @@ -1548,6 +1548,8 @@ const char *spinel_status_to_cstr(spinel_status_t status) {SPINEL_STATUS_ITEM_NOT_FOUND, "ITEM_NOT_FOUND"}, {SPINEL_STATUS_INVALID_COMMAND_FOR_PROP, "INVALID_COMMAND_FOR_PROP"}, {SPINEL_STATUS_RESPONSE_TIMEOUT, "RESPONSE_TIMEOUT"}, + {SPINEL_STATUS_SWITCHOVER_DONE, "SWITCHOVER_DONE"}, + {SPINEL_STATUS_SWITCHOVER_FAILED, "SWITCHOVER_FAILED"}, {SPINEL_STATUS_JOIN_FAILURE, "JOIN_FAILURE"}, {SPINEL_STATUS_JOIN_SECURITY, "JOIN_SECURITY"}, {SPINEL_STATUS_JOIN_NO_PEERS, "JOIN_NO_PEERS"}, diff --git a/src/lib/spinel/spinel.h b/src/lib/spinel/spinel.h index a4802af9b..5d8094db4 100644 --- a/src/lib/spinel/spinel.h +++ b/src/lib/spinel/spinel.h @@ -78,16 +78,16 @@ * * The Interface Identifier (IID) is a number between 0 and 3, which * is associated by the OS with a specific NCP. This allows the protocol - * to support up to 4 NCPs under same connection. + * to support multiple networks under same connection. * * The least significant bits of the header represent the Transaction * Identifier (TID). The TID is used for correlating responses to the * commands which generated them. * * When a command is sent from the host, any reply to that command sent - * by the NCP will use the same value for the TID. When the host - * receives a frame that matches the TID of the command it sent, it can - * easily recognize that frame as the actual response to that command. + * by the NCP will use the same value for the IID and TID. When the host + * receives a frame that matches the IID and TID of the command it sent, it + * can easily recognize that frame as the actual response to that command. * * The TID value of zero (0) is used for commands to which a correlated * response is not expected or needed, such as for unsolicited update @@ -514,6 +514,9 @@ enum SPINEL_STATUS_UNKNOWN_NEIGHBOR = 22, ///< The neighbor is unknown. SPINEL_STATUS_NOT_CAPABLE = 23, ///< The target is not capable of handling requested operation. SPINEL_STATUS_RESPONSE_TIMEOUT = 24, ///< No response received from remote node + SPINEL_STATUS_SWITCHOVER_DONE = + 25, ///< Radio interface switch completed successfully (SPINEL_PROP_MULTIPAN_ACTIVE_INTERFACE) + SPINEL_STATUS_SWITCHOVER_FAILED = 26, ///< Radio interface switch failed (SPINEL_PROP_MULTIPAN_ACTIVE_INTERFACE) SPINEL_STATUS_JOIN__BEGIN = 104, @@ -885,6 +888,7 @@ typedef struct typedef int spinel_ssize_t; typedef unsigned int spinel_size_t; +typedef uint8_t spinel_iid_t; typedef uint8_t spinel_tid_t; enum @@ -1451,8 +1455,6 @@ enum * * Provides number of interfaces. * - * Currently always reads as 1. - * */ SPINEL_PROP_INTERFACE_COUNT = 6, @@ -4863,6 +4865,24 @@ enum SPINEL_PROP_RCP_EXT__END = 0x900, + SPINEL_PROP_MULTIPAN__BEGIN = 0x900, + + /// Multipan interface selection. + /** Format: `C` + * Type: Read-Write + * + * `C`: b[0-1] - Interface id. + * b[7] - 1: Complete pending radio operation, 0: immediate(force) switch. + * + * This feature gets or sets the radio interface to be used in multipan configuration + * + * Default value: 0 + * + */ + SPINEL_PROP_MULTIPAN_ACTIVE_INTERFACE = SPINEL_PROP_MULTIPAN__BEGIN + 0, + + SPINEL_PROP_MULTIPAN__END = 0x910, + SPINEL_PROP_NEST__BEGIN = 0x3BC0, SPINEL_PROP_NEST_STREAM_MFG = SPINEL_PROP_NEST__BEGIN + 0, @@ -4957,11 +4977,15 @@ typedef uint32_t spinel_prop_key_t; #define SPINEL_HEADER_IID_SHIFT 4 #define SPINEL_HEADER_IID_MASK (3 << SPINEL_HEADER_IID_SHIFT) +#define SPINEL_HEADER_IID(iid) (static_cast((iid) << SPINEL_HEADER_IID_SHIFT)) +#define SPINEL_HEADER_IID_MAX 3 + +#define SPINEL_HEADER_IID_0 SPINEL_HEADER_IID(0) +#define SPINEL_HEADER_IID_1 SPINEL_HEADER_IID(1) +#define SPINEL_HEADER_IID_2 SPINEL_HEADER_IID(2) +#define SPINEL_HEADER_IID_3 SPINEL_HEADER_IID(3) -#define SPINEL_HEADER_IID_0 (0 << SPINEL_HEADER_IID_SHIFT) -#define SPINEL_HEADER_IID_1 (1 << SPINEL_HEADER_IID_SHIFT) -#define SPINEL_HEADER_IID_2 (2 << SPINEL_HEADER_IID_SHIFT) -#define SPINEL_HEADER_IID_3 (3 << SPINEL_HEADER_IID_SHIFT) +#define SPINEL_HEADER_INVALID_IID 0xFF #define SPINEL_HEADER_GET_IID(x) (((x)&SPINEL_HEADER_IID_MASK) >> SPINEL_HEADER_IID_SHIFT) #define SPINEL_HEADER_GET_TID(x) (spinel_tid_t)(((x)&SPINEL_HEADER_TID_MASK) >> SPINEL_HEADER_TID_SHIFT) @@ -4976,6 +5000,10 @@ typedef uint32_t spinel_prop_key_t; #define SPINEL_BEACON_THREAD_FLAG_NATIVE (1 << 3) +#define SPINEL_MULTIPAN_INTERFACE_SOFT_SWITCH_SHIFT 7 +#define SPINEL_MULTIPAN_INTERFACE_SOFT_SWITCH_MASK (1 << SPINEL_MULTIPAN_INTERFACE_SOFT_SWITCH_SHIFT) +#define SPINEL_MULTIPAN_INTERFACE_ID_MASK 0x03 + // ---------------------------------------------------------------------------- enum diff --git a/src/lib/spinel/spinel_interface.hpp b/src/lib/spinel/spinel_interface.hpp index 432b8b7ee..10d0c9637 100644 --- a/src/lib/spinel/spinel_interface.hpp +++ b/src/lib/spinel/spinel_interface.hpp @@ -175,9 +175,27 @@ class SpinelInterface */ 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); + const uint8_t kSpinelResetCommandLength = 2; + bool resetCmd = false; + + if (aLength >= kSpinelResetCommandLength) + { +#ifndef OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + // Validate the iid. + VerifyOrExit(((aFrame[0] & SPINEL_HEADER_IID_MASK) == SPINEL_HEADER_IID_0)); +#endif + + // Validate the header flag by masking out the iid bits as it is validated above. + VerifyOrExit(((aFrame[0] & ~SPINEL_HEADER_IID_MASK) == SPINEL_HEADER_FLAG)); + + // Validate the reset command. + VerifyOrExit(aFrame[1] == SPINEL_CMD_RESET); + + ExitNow(resetCmd = true); + } + + exit: + return resetCmd; } }; } // namespace Spinel diff --git a/src/ncp/BUILD.gn b/src/ncp/BUILD.gn index 2adfdecaf..2bb99814a 100644 --- a/src/ncp/BUILD.gn +++ b/src/ncp/BUILD.gn @@ -31,6 +31,7 @@ openthread_ncp_sources = [ "changed_props_set.cpp", "changed_props_set.hpp", "example_vendor_hook.cpp", + "multipan_platform.cpp", "ncp_base.cpp", "ncp_base.hpp", "ncp_base_dispatcher.cpp", diff --git a/src/ncp/CMakeLists.txt b/src/ncp/CMakeLists.txt index 84062470b..25d7e2de1 100644 --- a/src/ncp/CMakeLists.txt +++ b/src/ncp/CMakeLists.txt @@ -33,6 +33,7 @@ set(COMMON_INCLUDES set(COMMON_SOURCES changed_props_set.cpp + multipan_platform.cpp ncp_base.cpp ncp_base_dispatcher.cpp ncp_base_radio.cpp @@ -68,6 +69,15 @@ endif() if(OT_RCP) include(radio.cmake) + if(OT_MULTIPAN_RCP) + target_compile_options(ot-config-radio + INTERFACE + "-DOPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE=1" + "-DOPENTHREAD_CONFIG_LOG_PREPEND_UPTIME=0" # Not supporting multiple instances + "-DOPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE=1" + "-DOPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE=1" + ) + endif() endif() set_property(SOURCE ncp_base_mtd.cpp diff --git a/src/ncp/example_vendor_hook.cpp b/src/ncp/example_vendor_hook.cpp index 62c0b6aaa..116b8493c 100644 --- a/src/ncp/example_vendor_hook.cpp +++ b/src/ncp/example_vendor_hook.cpp @@ -141,6 +141,12 @@ class NcpVendorUart : public ot::Ncp::NcpHdlc : ot::Ncp::NcpHdlc(aInstance, &NcpVendorUart::SendHdlc) { } +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + NcpVendorUart(ot::Instance **aInstances, uint8_t count) + : ot::Ncp::NcpHdlc(aInstances, count, &NcpVendorUart::SendHdlc) + { + } +#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO // Add public/private methods or member variables }; @@ -159,5 +165,28 @@ extern "C" void otAppNcpInit(otInstance *aInstance) // assert(false); } } +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO +extern "C" void otAppNcpInitMulti(otInstance **aInstances, uint8_t count) +{ + NcpVendorUart *ncpVendor = nullptr; + ot::Instance *instances[SPINEL_HEADER_IID_MAX]; + + OT_ASSERT(count < SPINEL_HEADER_IID_MAX); + OT_ASSERT(count > 0); + OT_ASSERT(aInstances[0] != nullptr); + + for (int i = 0; i < count; i++) + { + instances[i] = static_cast(aInstances[i]); + } + + ncpVendor = new (&sNcpVendorRaw) NcpVendorUart(instances, count); + + if (ncpVendor == nullptr || ncpVendor != ot::Ncp::NcpBase::GetNcpInstance()) + { + OT_ASSERT(false); + } +} +#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO #endif // #if OPENTHREAD_ENABLE_NCP_VENDOR_HOOK diff --git a/src/ncp/multipan_platform.cpp b/src/ncp/multipan_platform.cpp new file mode 100644 index 000000000..92374e322 --- /dev/null +++ b/src/ncp/multipan_platform.cpp @@ -0,0 +1,101 @@ +/* + * 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 multipan radio platform callbacks into OpenThread and default/weak radio platform APIs. + */ + +#include +#include + +#include "common/as_core_type.hpp" +#include "common/code_utils.hpp" +#include "instance/instance.hpp" +#include "ncp/ncp_base.hpp" + +using namespace ot; + +//--------------------------------------------------------------------------------------------------------------------- +// otPlatRadio callbacks + +otInstance *otPlatMultipanIidToInstance(uint8_t aIid) +{ + Ncp::NcpBase *ncpBase = Ncp::NcpBase::GetNcpInstance(); + OT_ASSERT(ncpBase); + + return ncpBase->IidToInstance(aIid); +} + +uint8_t otPlatMultipanInstanceToIid(otInstance *aInstance) +{ + Ncp::NcpBase *ncpBase = Ncp::NcpBase::GetNcpInstance(); + OT_ASSERT(ncpBase); + + return ncpBase->InstanceToIid(static_cast(aInstance)); +} + +#if OPENTHREAD_RADIO && OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + +void otPlatMultipanSwitchoverDone(otInstance *aInstance, bool success) +{ + Ncp::NcpBase *ncpBase = Ncp::NcpBase::GetNcpInstance(); + OT_ASSERT(ncpBase); + + ncpBase->NotifySwitchoverDone(aInstance, success); + + return; +} + +#else // OPENTHREAD_RADIO && OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + +// default implementation +OT_TOOL_WEAK void otPlatMultipanSwitchoverDone(otInstance *aInstance, bool success) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(success); +} + +#endif // OPENTHREAD_RADIO && OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + +//--------------------------------------------------------------------------------------------------------------------- +// Default/weak implementation of multipan APIs + +OT_TOOL_WEAK otError otPlatMultipanGetActiveInstance(otInstance **aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + return kErrorNotImplemented; +} + +OT_TOOL_WEAK otError otPlatMultipanSetActiveInstance(otInstance *aInstance, bool aCompletePending) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aCompletePending); + + return kErrorNotImplemented; +} diff --git a/src/ncp/ncp_base.cpp b/src/ncp/ncp_base.cpp index 288373fc7..e0b4c1881 100644 --- a/src/ncp/ncp_base.cpp +++ b/src/ncp/ncp_base.cpp @@ -55,6 +55,43 @@ namespace Ncp { // MARK: Utility Functions // ---------------------------------------------------------------------------- +uint8_t NcpBase::InstanceToIid(Instance *aInstance) +{ + uint8_t index = 0; + + OT_UNUSED_VARIABLE(aInstance); + +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + index = SPINEL_HEADER_GET_IID(SPINEL_HEADER_IID_BROADCAST); // use broadcast if no match + + for (int i = 0; i < kSpinelInterfaceCount; i++) + { + if (aInstance == mInstances[i]) + { + index = i; + break; + } + } +#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + + return index; +} + +Instance *NcpBase::IidToInstance(uint8_t aIid) +{ + Instance *instance; + OT_ASSERT(aIid < kSpinelInterfaceCount); + +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + instance = mInstances[aIid]; +#else + OT_UNUSED_VARIABLE(aIid); + instance = mInstance; +#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + + return instance; +} + #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE static bool HasOnly1BitSet(uint32_t aValue) { return aValue != 0 && ((aValue & (aValue - 1)) == 0); } @@ -202,6 +239,29 @@ static spinel_status_t ResetReasonToSpinelStatus(otPlatResetReason aReason) NcpBase *NcpBase::sNcpInstance = nullptr; +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO +NcpBase::NcpBase(Instance **aInstances, uint8_t aCount) + : NcpBase(aInstances[0]) +{ + OT_ASSERT(aCount > 0); + OT_ASSERT(aCount < SPINEL_HEADER_IID_MAX); // One IID reserved for broadcast + + uint8_t skipped = 0; + + for (int i = 0; i < aCount; i++) + { + if ((i + skipped) == SPINEL_HEADER_GET_IID(SPINEL_HEADER_IID_BROADCAST)) + { + mInstances[i + skipped] = nullptr; + skipped++; + } + + OT_ASSERT(i + skipped <= SPINEL_HEADER_IID_MAX); + mInstances[i + skipped] = aInstances[i]; + } +} +#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + NcpBase::NcpBase(Instance *aInstance) : mInstance(aInstance) , mTxFrameBuffer(mTxBuffer, sizeof(mTxBuffer)) @@ -222,12 +282,10 @@ NcpBase::NcpBase(Instance *aInstance) , mAllowPeekDelegate(nullptr) , mAllowPokeDelegate(nullptr) #endif - , mNextExpectedTid(0) , mResponseQueueHead(0) , mResponseQueueTail(0) , mAllowLocalNetworkDataChange(false) , mRequireJoinExistingNetwork(false) - , mIsRawStreamEnabled(false) , mPcapEnabled(false) , mDisableStreamWrite(false) , mShouldEmitChildTableUpdate(false) @@ -237,11 +295,7 @@ NcpBase::NcpBase(Instance *aInstance) #if OPENTHREAD_FTD , mPreferredRouteId(0) #endif -#if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE - , mCurTransmitTID(0) - , mCurScanChannel(kInvalidScanChannel) - , mSrcMatchEnabled(false) -#endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE + , mCurCommandIid(0) #if OPENTHREAD_MTD || OPENTHREAD_FTD , mInboundSecureIpFrameCounter(0) , mInboundInsecureIpFrameCounter(0) @@ -267,6 +321,13 @@ NcpBase::NcpBase(Instance *aInstance) mTxFrameBuffer.SetFrameRemovedCallback(&NcpBase::HandleFrameRemovedFromNcpBuffer, this); memset(&mResponseQueue, 0, sizeof(mResponseQueue)); +#if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE + memset(mCurTransmitTID, 0, sizeof(mCurTransmitTID)); + memset(mSrcMatchEnabled, 0, sizeof(mSrcMatchEnabled)); + memset(mCurScanChannel, kInvalidScanChannel, sizeof(mCurScanChannel)); +#endif + memset(mIsRawStreamEnabled, 0, sizeof(mIsRawStreamEnabled)); + memset(mNextExpectedTid, 0, sizeof(mNextExpectedTid)); #if OPENTHREAD_MTD || OPENTHREAD_FTD otMessageQueueInit(&mMessageQueue); @@ -303,6 +364,8 @@ NcpBase::NcpBase(Instance *aInstance) NcpBase *NcpBase::GetNcpInstance(void) { return sNcpInstance; } +spinel_iid_t NcpBase::GetCurCommandIid(void) const { return mCurCommandIid; } + void NcpBase::ResetCounters(void) { mFramingErrorCounter = 0; @@ -348,8 +411,20 @@ void NcpBase::HandleReceive(const uint8_t *aBuf, uint16_t aBufLength) mRxSpinelFrameCounter++; - // We only support IID zero for now. - if (SPINEL_HEADER_GET_IID(header) != 0) + mCurCommandIid = SPINEL_HEADER_GET_IID(header); + +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + if (mCurCommandIid > SPINEL_HEADER_IID_MAX) +#else + if (mCurCommandIid != 0) +#endif + { + IgnoreError(WriteLastStatusFrame(header, SPINEL_STATUS_INVALID_INTERFACE)); + ExitNow(); + } + + mInstance = IidToInstance(mCurCommandIid); + if (mInstance == nullptr) { IgnoreError(WriteLastStatusFrame(header, SPINEL_STATUS_INVALID_INTERFACE)); ExitNow(); @@ -378,12 +453,12 @@ void NcpBase::HandleReceive(const uint8_t *aBuf, uint16_t aBufLength) tid = SPINEL_HEADER_GET_TID(header); - if ((mNextExpectedTid != 0) && (tid != mNextExpectedTid)) + if ((mNextExpectedTid[mCurCommandIid] != 0) && (tid != mNextExpectedTid[mCurCommandIid])) { mRxSpinelOutOfOrderTidCounter++; } - mNextExpectedTid = SPINEL_GET_NEXT_TID(tid); + mNextExpectedTid[mCurCommandIid] = SPINEL_GET_NEXT_TID(tid); exit: mDisableStreamWrite = false; @@ -469,7 +544,7 @@ void NcpBase::IncrementFrameErrorCounter(void) { mFramingErrorCounter++; } otError NcpBase::StreamWrite(int aStreamId, const uint8_t *aDataPtr, int aDataLen) { otError error = OT_ERROR_NONE; - uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; + uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID; spinel_prop_key_t streamPropKey; if (aStreamId == 0) @@ -644,7 +719,7 @@ unsigned int NcpBase::ConvertLogRegion(otLogRegion aLogRegion) void NcpBase::Log(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aLogString) { otError error = OT_ERROR_NONE; - uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; + uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID; VerifyOrExit(!mDisableStreamWrite, error = OT_ERROR_INVALID_STATE); VerifyOrExit(!mChangedPropsSet.IsPropertyFiltered(SPINEL_PROP_STREAM_LOG)); @@ -699,6 +774,7 @@ uint8_t NcpBase::GetWrappedResponseQueueIndex(uint8_t aPosition) otError NcpBase::EnqueueResponse(uint8_t aHeader, ResponseType aType, unsigned int aPropKeyOrStatus) { otError error = OT_ERROR_NONE; + spinel_iid_t iid = SPINEL_HEADER_GET_IID(aHeader); spinel_tid_t tid = SPINEL_HEADER_GET_TID(aHeader); ResponseEntry *entry; @@ -731,13 +807,13 @@ otError NcpBase::EnqueueResponse(uint8_t aHeader, ResponseType aType, unsigned i // get an out of sequence TID, check if we already have a response // queued for this TID and if so mark the old entry as deleted. - if (tid != mNextExpectedTid) + if (tid != mNextExpectedTid[iid]) { for (uint8_t cur = mResponseQueueHead; cur < mResponseQueueTail; cur++) { entry = &mResponseQueue[GetWrappedResponseQueueIndex(cur)]; - if (entry->mIsInUse && (entry->mTid == tid)) + if (entry->mIsInUse && (entry->mIid == iid) && (entry->mTid == tid)) { // Entry is just marked here and will be removed // from `SendQueuedResponses()`. @@ -752,6 +828,7 @@ otError NcpBase::EnqueueResponse(uint8_t aHeader, ResponseType aType, unsigned i entry = &mResponseQueue[GetWrappedResponseQueueIndex(mResponseQueueTail)]; + entry->mIid = iid; entry->mTid = tid; entry->mIsInUse = true; entry->mType = aType; @@ -773,8 +850,8 @@ otError NcpBase::SendQueuedResponses(void) if (entry.mIsInUse) { - uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; - + uint8_t header = SPINEL_HEADER_FLAG; + header |= SPINEL_HEADER_IID(entry.mIid); header |= static_cast(entry.mTid << SPINEL_HEADER_TID_SHIFT); if (entry.mType == kResponseTypeLastStatus) @@ -857,11 +934,11 @@ void NcpBase::UpdateChangedProps(void) status = ResetReasonToSpinelStatus(otPlatGetResetReason(mInstance)); } - SuccessOrExit(WriteLastStatusFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, status)); + SuccessOrExit(WriteLastStatusFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID, status)); } else if (mDidInitialUpdates) { - SuccessOrExit(WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, propKey)); + SuccessOrExit(WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID, propKey)); } mChangedPropsSet.RemoveEntry(index); @@ -1102,7 +1179,7 @@ otError NcpBase::WriteLastStatusFrame(uint8_t aHeader, spinel_status_t aLastStat { otError error = OT_ERROR_NONE; - if (SPINEL_HEADER_GET_IID(aHeader) == 0) + if (SPINEL_HEADER_GET_IID(aHeader) == SPINEL_HEADER_GET_IID(SPINEL_HEADER_TX_NOTIFICATION_IID)) { mLastStatus = aLastStatus; } @@ -1201,15 +1278,15 @@ otError NcpBase::CommandHandler_RESET(uint8_t aHeader) { otInstanceResetRadioStack(mInstance); - mIsRawStreamEnabled = false; - mCurTransmitTID = 0; - mCurScanChannel = kInvalidScanChannel; - mSrcMatchEnabled = false; + mIsRawStreamEnabled[mCurCommandIid] = false; + mCurTransmitTID[mCurCommandIid] = 0; + mCurScanChannel[mCurCommandIid] = kInvalidScanChannel; + mSrcMatchEnabled[mCurCommandIid] = false; ResetCounters(); - SuccessOrAssert( - error = WriteLastStatusFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_STATUS_RESET_POWER_ON)); + SuccessOrAssert(error = WriteLastStatusFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID, + SPINEL_STATUS_RESET_POWER_ON)); } #if OPENTHREAD_CONFIG_PLATFORM_BOOTLOADER_MODE_ENABLE else if (reset_type == SPINEL_RESET_BOOTLOADER) @@ -1401,7 +1478,7 @@ template <> otError NcpBase::HandlePropertySet(void) // Make sure we are update the receiving channel if raw link is enabled and we have raw // stream enabled already - if (otLinkRawIsEnabled(mInstance) && mIsRawStreamEnabled) + if (otLinkRawIsEnabled(mInstance) && mIsRawStreamEnabled[mCurCommandIid]) { error = otLinkRawReceive(mInstance); } @@ -1515,7 +1592,7 @@ template <> otError NcpBase::HandlePropertyGet(void) template <> otError NcpBase::HandlePropertyGet(void) { - return mEncoder.WriteBool(mIsRawStreamEnabled); + return mEncoder.WriteBool(mIsRawStreamEnabled[mCurCommandIid]); } template <> otError NcpBase::HandlePropertySet(void) @@ -1541,7 +1618,7 @@ template <> otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertyGet(void) if (otLinkRawIsEnabled(mInstance)) { - scanState = (mCurScanChannel == kInvalidScanChannel) ? SPINEL_SCAN_STATE_IDLE : SPINEL_SCAN_STATE_ENERGY; + scanState = (mCurScanChannel[mCurCommandIid] == kInvalidScanChannel) ? SPINEL_SCAN_STATE_IDLE + : SPINEL_SCAN_STATE_ENERGY; } else @@ -1688,11 +1766,11 @@ template <> otError NcpBase::HandlePropertySet(void) // Make sure we aren't already scanning and that we have // only 1 bit set for the channel mask. - VerifyOrExit(mCurScanChannel == kInvalidScanChannel, error = OT_ERROR_INVALID_STATE); + VerifyOrExit(mCurScanChannel[mCurCommandIid] == kInvalidScanChannel, error = OT_ERROR_INVALID_STATE); VerifyOrExit(HasOnly1BitSet(mScanChannelMask), error = OT_ERROR_INVALID_ARGS); - scanChannel = IndexOfMSB(mScanChannelMask); - mCurScanChannel = static_cast(scanChannel); + scanChannel = IndexOfMSB(mScanChannelMask); + mCurScanChannel[mCurCommandIid] = static_cast(scanChannel); error = otLinkRawEnergyScan(mInstance, scanChannel, mScanPeriod, LinkRawEnergyScanDone); } @@ -1768,8 +1846,8 @@ template <> otError NcpBase::HandlePropertySet( if (error != OT_ERROR_NONE) { - IgnoreError( - WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_PROP_UNSOL_UPDATE_FILTER)); + IgnoreError(WritePropertyValueIsFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID, + SPINEL_PROP_UNSOL_UPDATE_FILTER)); } return error; @@ -1996,7 +2074,19 @@ template <> otError NcpBase::HandlePropertyGet(void) template <> otError NcpBase::HandlePropertyGet(void) { - return mEncoder.WriteUint8(1); // Only one interface for now + uint8_t instances = 1; +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + instances = 0; + for (uint8_t i = 0; i <= SPINEL_HEADER_IID_MAX; i++) + { + if (mInstances[i] != nullptr) + { + instances++; + } + } +#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + + return mEncoder.WriteUint8(instances); } #if OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL diff --git a/src/ncp/ncp_base.hpp b/src/ncp/ncp_base.hpp index b618101c3..7b8d94f87 100644 --- a/src/ncp/ncp_base.hpp +++ b/src/ncp/ncp_base.hpp @@ -62,6 +62,20 @@ #include "lib/spinel/spinel_decoder.hpp" #include "lib/spinel/spinel_encoder.hpp" +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE +#define SPINEL_HEADER_IID_BROADCAST OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID +#else +#define SPINEL_HEADER_IID_BROADCAST SPINEL_HEADER_IID_0 +#endif + +// In case of host<->ncp<->rcp configuration, notifications shall be +// received on broadcast iid on ncp, but transmitted on IID 0 to host. +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO +#define SPINEL_HEADER_TX_NOTIFICATION_IID SPINEL_HEADER_IID_BROADCAST +#else +#define SPINEL_HEADER_TX_NOTIFICATION_IID SPINEL_HEADER_IID_0 +#endif + namespace ot { namespace Ncp { @@ -72,6 +86,11 @@ class NcpBase { kSpinelCmdHeaderSize = 2, ///< Size of spinel command header (in bytes). kSpinelPropIdSize = 3, ///< Size of spinel property identifier (in bytes). +#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && OPENTHREAD_RADIO + kSpinelInterfaceCount = SPINEL_HEADER_IID_MAX + 1, // Number of supported spinel interfaces +#else + kSpinelInterfaceCount = 1, // Only one interface supported in single instance configuration +#endif }; /** @@ -82,6 +101,18 @@ class NcpBase */ explicit NcpBase(Instance *aInstance); +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + /** + * Creates and initializes an NcpBase instance. + * + * @param[in] aInstances The OpenThread instances structure pointer array. + * @param[in] aCount Number of the instances in the array. + * + */ + explicit NcpBase(Instance **aInstances, uint8_t aCount); + +#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + /** * Returns the pointer to the single NCP instance. * @@ -90,6 +121,50 @@ class NcpBase */ static NcpBase *GetNcpInstance(void); + /** + * Returns an IID for the given instance + * + * Returned IID is an integer value that must be shifted by SPINEL_HEADER_IID_SHIFT before putting into spinel + * header. If multipan interface is not enabled or build is not for RCP IID=0 is returned. If nullptr is passed it + * matches broadcast IID in current implementation. Broadcast IID is also returned in case no match was found. + * + * @param[in] aInstance Instance pointer to match with IID + * + * @returns Spinel Interface Identifier to use for communication for this instance + * + */ + uint8_t InstanceToIid(Instance *aInstance); + + /** + * Returns an OT instance for the given IID + * + * Returns an OpenThread instance object associated to the given IID. + * If multipan interface is not enabled or build is not for RCP returned value is the same instance object + * regardless of the aIid parameter In current implementation nullptr is returned for broadcast IID and values + * exceeding the instances count but lower than kSpinelInterfaceCount. + * + * @param[in] aIid IID used in the Spinel communication + * + * @returns OpenThread instance object associated with the given IID + * + */ + Instance *IidToInstance(uint8_t aIid); + +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + /** + * Called to send notification to host about switchower results. + */ + void NotifySwitchoverDone(otInstance *aInstance, bool aSuccess); +#endif + + /** + * This method returns the IID of the current spinel command. + * + * @returns IID. + * + */ + spinel_iid_t GetCurCommandIid(void) const; + /** * Sends data to host via specific stream. * @@ -172,6 +247,7 @@ class NcpBase */ struct ResponseEntry { + uint8_t mIid : 2; ///< Spinel interface id. uint8_t mTid : 4; ///< Spinel transaction id. bool mIsInUse : 1; ///< `true` if this entry is in use, `false` otherwise. ResponseType mType : 2; ///< Response type. @@ -246,17 +322,26 @@ class NcpBase #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE otError PackRadioFrame(otRadioFrame *aFrame, otError aError); +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + void NotifySwitchoverDone(bool aSuccess); +#endif + static void LinkRawReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError); - void LinkRawReceiveDone(otRadioFrame *aFrame, otError aError); + void LinkRawReceiveDone(uint8_t aIid, otRadioFrame *aFrame, otError aError); static void LinkRawTransmitDone(otInstance *aInstance, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError); - void LinkRawTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError); + void LinkRawTransmitDone(uint8_t aIid, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError); static void LinkRawEnergyScanDone(otInstance *aInstance, int8_t aEnergyScanMaxRssi); - void LinkRawEnergyScanDone(int8_t aEnergyScanMaxRssi); + void LinkRawEnergyScanDone(uint8_t aIid, int8_t aEnergyScanMaxRssi); + + static inline uint8_t GetNcpBaseIid(otInstance *aInstance) + { + return sNcpInstance->InstanceToIid(static_cast(aInstance)); + } #endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE @@ -534,11 +619,6 @@ class NcpBase static NcpBase *sNcpInstance; static spinel_status_t ThreadErrorToSpinelStatus(otError aError); static uint8_t LinkFlagsToFlagByte(bool aRxOnWhenIdle, bool aDeviceType, bool aNetworkData); - Instance *mInstance; - Spinel::Buffer mTxFrameBuffer; - Spinel::Encoder mEncoder; - Spinel::Decoder mDecoder; - bool mHostPowerStateInProgress; enum { @@ -547,6 +627,15 @@ class NcpBase kInvalidScanChannel = -1, // Invalid scan channel. }; + Instance *mInstance; +#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && OPENTHREAD_RADIO + Instance *mInstances[kSpinelInterfaceCount]; +#endif + Spinel::Buffer mTxFrameBuffer; + Spinel::Encoder mEncoder; + Spinel::Decoder mDecoder; + bool mHostPowerStateInProgress; + spinel_status_t mLastStatus; uint32_t mScanChannelMask; uint16_t mScanPeriod; @@ -569,7 +658,7 @@ class NcpBase uint8_t mTxBuffer[kTxBufferSize]; - spinel_tid_t mNextExpectedTid; + spinel_tid_t mNextExpectedTid[kSpinelInterfaceCount]; uint8_t mResponseQueueHead; uint8_t mResponseQueueTail; @@ -577,7 +666,7 @@ class NcpBase bool mAllowLocalNetworkDataChange; bool mRequireJoinExistingNetwork; - bool mIsRawStreamEnabled; + bool mIsRawStreamEnabled[kSpinelInterfaceCount]; bool mPcapEnabled; bool mDisableStreamWrite; bool mShouldEmitChildTableUpdate; @@ -591,11 +680,12 @@ class NcpBase #endif uint8_t mPreferredRouteId; #endif + uint8_t mCurCommandIid; #if OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE - uint8_t mCurTransmitTID; - int8_t mCurScanChannel; - bool mSrcMatchEnabled; + uint8_t mCurTransmitTID[kSpinelInterfaceCount]; + int8_t mCurScanChannel[kSpinelInterfaceCount]; + bool mSrcMatchEnabled[kSpinelInterfaceCount]; #endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE #if OPENTHREAD_MTD || OPENTHREAD_FTD diff --git a/src/ncp/ncp_base_dispatcher.cpp b/src/ncp/ncp_base_dispatcher.cpp index 568b95964..e653f8311 100644 --- a/src/ncp/ncp_base_dispatcher.cpp +++ b/src/ncp/ncp_base_dispatcher.cpp @@ -217,6 +217,9 @@ NcpBase::PropertyHandler NcpBase::FindGetPropertyHandler(spinel_prop_key_t aKey) #if OPENTHREAD_CONFIG_MAC_CSL_TRANSMITTER_ENABLE OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_RCP_CSL_UNCERTAINTY), #endif +#if OPENTHREAD_RADIO && OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_MULTIPAN_ACTIVE_INTERFACE), +#endif #if OPENTHREAD_MTD || OPENTHREAD_FTD OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_UNSOL_UPDATE_FILTER), OT_NCP_GET_HANDLER_ENTRY(SPINEL_PROP_UNSOL_UPDATE_LIST), @@ -489,6 +492,9 @@ NcpBase::PropertyHandler NcpBase::FindSetPropertyHandler(spinel_prop_key_t aKey) OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_RCP_ENH_ACK_PROBING), #endif #endif // OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE +#if OPENTHREAD_RADIO && OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_MULTIPAN_ACTIVE_INTERFACE), +#endif #if OPENTHREAD_MTD || OPENTHREAD_FTD OT_NCP_SET_HANDLER_ENTRY(SPINEL_PROP_UNSOL_UPDATE_FILTER), #if OPENTHREAD_CONFIG_JAM_DETECTION_ENABLE diff --git a/src/ncp/ncp_base_radio.cpp b/src/ncp/ncp_base_radio.cpp index 428037e4b..cad3ef52d 100644 --- a/src/ncp/ncp_base_radio.cpp +++ b/src/ncp/ncp_base_radio.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -123,15 +124,32 @@ otError NcpBase::PackRadioFrame(otRadioFrame *aFrame, otError aError) return error; } -void NcpBase::LinkRawReceiveDone(otInstance *, otRadioFrame *aFrame, otError aError) +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE +void NcpBase::NotifySwitchoverDone(otInstance *aInstance, bool aSuccess) { - sNcpInstance->LinkRawReceiveDone(aFrame, aError); + OT_UNUSED_VARIABLE(aInstance); + NotifySwitchoverDone(aSuccess); } -void NcpBase::LinkRawReceiveDone(otRadioFrame *aFrame, otError aError) +void NcpBase::NotifySwitchoverDone(bool aSuccess) { - uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0; + uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_TX_NOTIFICATION_IID; + spinel_status_t result = aSuccess ? SPINEL_STATUS_SWITCHOVER_DONE : SPINEL_STATUS_SWITCHOVER_FAILED; + IgnoreError(WriteLastStatusFrame(header, result)); +} +#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + +void NcpBase::LinkRawReceiveDone(otInstance *aInstance, otRadioFrame *aFrame, otError aError) +{ + sNcpInstance->LinkRawReceiveDone(GetNcpBaseIid(aInstance), aFrame, aError); +} + +void NcpBase::LinkRawReceiveDone(uint8_t aIid, otRadioFrame *aFrame, otError aError) +{ + uint8_t header = SPINEL_HEADER_FLAG; + + header |= SPINEL_HEADER_IID(aIid); // Append frame header SuccessOrExit(mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_STREAM_RAW)); @@ -142,23 +160,24 @@ void NcpBase::LinkRawReceiveDone(otRadioFrame *aFrame, otError aError) return; } -void NcpBase::LinkRawTransmitDone(otInstance *, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError) +void NcpBase::LinkRawTransmitDone(otInstance *aInstance, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError) { - sNcpInstance->LinkRawTransmitDone(aFrame, aAckFrame, aError); + sNcpInstance->LinkRawTransmitDone(GetNcpBaseIid(aInstance), aFrame, aAckFrame, aError); } -void NcpBase::LinkRawTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError) +void NcpBase::LinkRawTransmitDone(uint8_t aIid, otRadioFrame *aFrame, otRadioFrame *aAckFrame, otError aError) { OT_UNUSED_VARIABLE(aFrame); + OT_ASSERT(aIid < kSpinelInterfaceCount); - if (mCurTransmitTID) + if (mCurTransmitTID[aIid]) { - uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0 | mCurTransmitTID; + uint8_t header = SPINEL_HEADER_FLAG | SPINEL_HEADER_IID(aIid) | mCurTransmitTID[aIid]; bool framePending = (aAckFrame != nullptr && static_cast(aAckFrame)->GetFramePending()); bool headerUpdated = static_cast(aFrame)->IsHeaderUpdated(); // Clear cached transmit TID - mCurTransmitTID = 0; + mCurTransmitTID[aIid] = 0; SuccessOrExit(mEncoder.BeginFrame(header, SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_LAST_STATUS)); SuccessOrExit(mEncoder.WriteUintPacked(ThreadErrorToSpinelStatus(aError))); @@ -190,23 +209,24 @@ void NcpBase::LinkRawTransmitDone(otRadioFrame *aFrame, otRadioFrame *aAckFrame, return; } -void NcpBase::LinkRawEnergyScanDone(otInstance *, int8_t aEnergyScanMaxRssi) +void NcpBase::LinkRawEnergyScanDone(otInstance *aInstance, int8_t aEnergyScanMaxRssi) { - sNcpInstance->LinkRawEnergyScanDone(aEnergyScanMaxRssi); + sNcpInstance->LinkRawEnergyScanDone(GetNcpBaseIid(aInstance), aEnergyScanMaxRssi); } -void NcpBase::LinkRawEnergyScanDone(int8_t aEnergyScanMaxRssi) +void NcpBase::LinkRawEnergyScanDone(uint8_t aIid, int8_t aEnergyScanMaxRssi) { - int8_t scanChannel = mCurScanChannel; + OT_ASSERT(aIid < kSpinelInterfaceCount); + int8_t scanChannel = mCurScanChannel[aIid]; // Clear current scan channel - mCurScanChannel = kInvalidScanChannel; + mCurScanChannel[aIid] = kInvalidScanChannel; // Make sure we are back listening on the original receive channel, // since the energy scan could have been on a different channel. - IgnoreError(otLinkRawReceive(mInstance)); + IgnoreError(otLinkRawReceive(IidToInstance(aIid))); - SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS, + SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID(aIid), SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_MAC_ENERGY_SCAN_RESULT)); SuccessOrExit(mEncoder.WriteUint8(static_cast(scanChannel))); @@ -215,7 +235,7 @@ void NcpBase::LinkRawEnergyScanDone(int8_t aEnergyScanMaxRssi) // We are finished with the scan, so send out // a property update indicating such. - SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID_0, SPINEL_CMD_PROP_VALUE_IS, + SuccessOrExit(mEncoder.BeginFrame(SPINEL_HEADER_FLAG | SPINEL_HEADER_IID(aIid), SPINEL_CMD_PROP_VALUE_IS, SPINEL_PROP_MAC_SCAN_STATE)); SuccessOrExit(mEncoder.WriteUint8(SPINEL_SCAN_STATE_IDLE)); @@ -233,7 +253,7 @@ template <> otError NcpBase::HandlePropertyGet(void) template <> otError NcpBase::HandlePropertyGet(void) { // TODO: Would be good to add an `otLinkRaw` API to give the status of source match. - return mEncoder.WriteBool(mSrcMatchEnabled); + return mEncoder.WriteBool(mSrcMatchEnabled[mCurCommandIid]); } template <> otError NcpBase::HandlePropertyGet(void) @@ -250,9 +270,9 @@ template <> otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertySet(void) return error; } +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE +template <> otError NcpBase::HandlePropertySet(void) +{ + uint8_t interface; + Instance *instance; + bool softSwitch; + otError error = OT_ERROR_NONE; + + SuccessOrExit(error = mDecoder.ReadUint8(interface)); + softSwitch = (interface & SPINEL_MULTIPAN_INTERFACE_SOFT_SWITCH_MASK) != 0; + instance = IidToInstance(interface & SPINEL_MULTIPAN_INTERFACE_ID_MASK); + VerifyOrExit(instance != nullptr, error = OT_ERROR_NOT_IMPLEMENTED); // Instance out of range + SuccessOrExit(error = otPlatMultipanSetActiveInstance(instance, softSwitch)); + +exit: + return error; +} +#endif /* OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE */ + otError NcpBase::DecodeStreamRawTxRequest(otRadioFrame &aFrame) { otError error; @@ -449,8 +488,11 @@ otError NcpBase::DecodeStreamRawTxRequest(otRadioFrame &aFrame) otError NcpBase::HandlePropertySet_SPINEL_PROP_STREAM_RAW(uint8_t aHeader) { otError error = OT_ERROR_NONE; + uint8_t iid = SPINEL_HEADER_GET_IID(aHeader); otRadioFrame *frame; + OT_ASSERT(iid < kSpinelInterfaceCount); + VerifyOrExit(otLinkRawIsEnabled(mInstance), error = OT_ERROR_INVALID_STATE); frame = otLinkRawGetTransmitBuffer(mInstance); @@ -463,7 +505,7 @@ otError NcpBase::HandlePropertySet_SPINEL_PROP_STREAM_RAW(uint8_t aHeader) SuccessOrExit(error = otLinkRawTransmit(mInstance, &NcpBase::LinkRawTransmitDone)); // Cache the transaction ID for async response - mCurTransmitTID = SPINEL_HEADER_GET_TID(aHeader); + mCurTransmitTID[iid] = SPINEL_HEADER_GET_TID(aHeader); exit: @@ -537,6 +579,22 @@ template <> otError NcpBase::HandlePropertySet otError NcpBase::HandlePropertyGet(void) +{ + otInstance *instance; + spinel_iid_t iid; + otError error = OT_ERROR_NONE; + + SuccessOrExit(error = otPlatMultipanGetActiveInstance(&instance)); + iid = InstanceToIid(static_cast(instance)); + SuccessOrExit(error = mEncoder.WriteUint8(static_cast(iid))); + +exit: + return error; +} +#endif /* OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE */ + #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE template <> otError NcpBase::HandlePropertySet(void) { diff --git a/src/ncp/ncp_hdlc.cpp b/src/ncp/ncp_hdlc.cpp index 59da42496..9950de8a3 100644 --- a/src/ncp/ncp_hdlc.cpp +++ b/src/ncp/ncp_hdlc.cpp @@ -77,6 +77,31 @@ extern "C" void otNcpHdlcInit(otInstance *aInstance, otNcpHdlcSendCallback aSend } } +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + +extern "C" void otNcpHdlcInitMulti(otInstance **aInstances, uint8_t aCount, otNcpHdlcSendCallback aSendCallback) +{ + NcpHdlc *ncpHdlc = nullptr; + ot::Instance *instances[SPINEL_HEADER_IID_MAX]; + + OT_ASSERT(aCount < SPINEL_HEADER_IID_MAX + 1); + OT_ASSERT(aCount > 0); + OT_ASSERT(aInstances[0] != nullptr); + + for (int i = 0; i < aCount; i++) + { + instances[i] = static_cast(aInstances[i]); + } + + ncpHdlc = new (&sNcpRaw) NcpHdlc(instances, aCount, aSendCallback); + + if (ncpHdlc == nullptr || ncpHdlc != NcpBase::GetNcpInstance()) + { + OT_ASSERT(false); + } +} +#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + #endif // OPENTHREAD_ENABLE_NCP_VENDOR_HOOK == 0 NcpHdlc::NcpHdlc(Instance *aInstance, otNcpHdlcSendCallback aSendCallback) @@ -95,6 +120,26 @@ NcpHdlc::NcpHdlc(Instance *aInstance, otNcpHdlcSendCallback aSendCallback) mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToNcpBuffer, this); } +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + +NcpHdlc::NcpHdlc(Instance **aInstances, uint8_t aCount, otNcpHdlcSendCallback aSendCallback) + : NcpBase(aInstances, aCount) + , mSendCallback(aSendCallback) + , mFrameEncoder(mHdlcBuffer) + , mState(kStartingFrame) + , mByte(0) + , mHdlcSendImmediate(false) + , mHdlcSendTask(*aInstances[0], EncodeAndSend) +#if OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER + , mTxFrameBufferEncrypterReader(mTxFrameBuffer) +#endif // OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER +{ + mFrameDecoder.Init(mRxBuffer, &NcpHdlc::HandleFrame, this); + mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToNcpBuffer, this); +} + +#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + void NcpHdlc::HandleFrameAddedToNcpBuffer(void *aContext, Spinel::Buffer::FrameTag aTag, Spinel::Buffer::Priority aPriority, diff --git a/src/ncp/ncp_hdlc.hpp b/src/ncp/ncp_hdlc.hpp index d99ef96de..751611db2 100644 --- a/src/ncp/ncp_hdlc.hpp +++ b/src/ncp/ncp_hdlc.hpp @@ -59,6 +59,19 @@ class NcpHdlc : public NcpBase */ explicit NcpHdlc(Instance *aInstance, otNcpHdlcSendCallback aSendCallback); +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + /** + * Constructor + * + * @param[in] aInstancs The OpenThread instance pointers array. + * @param[in] aCount Number of instances in the array. + * @param[in] aSendCallback Callback for sending data. + * + */ + explicit NcpHdlc(Instance **aInstances, uint8_t aCount, otNcpHdlcSendCallback aSendCallback); + +#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && OPENTHREAD_RADIO + /** * Is called when uart tx is finished. It prepares and sends the next data chunk (if any) to uart. * diff --git a/src/posix/platform/CMakeLists.txt b/src/posix/platform/CMakeLists.txt index dfc62408e..e9fb7c83a 100644 --- a/src/posix/platform/CMakeLists.txt +++ b/src/posix/platform/CMakeLists.txt @@ -45,6 +45,13 @@ if(OT_DAEMON) set(OT_PLATFORM_DEFINES ${OT_PLATFORM_DEFINES} PARENT_SCOPE) endif() +if(OT_MULTIPAN_RCP) + target_compile_definitions(ot-posix-config + INTERFACE "OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE=1" + ) +endif() + + option(OT_POSIX_INSTALL_EXTERNAL_ROUTES "Install External Routes as IPv6 routes" ON) if(OT_POSIX_INSTALL_EXTERNAL_ROUTES) target_compile_definitions(ot-posix-config @@ -117,8 +124,8 @@ endif() add_library(openthread-posix alarm.cpp - backbone.cpp backtrace.cpp + ble.cpp configuration.cpp config_file.cpp daemon.cpp @@ -189,6 +196,7 @@ target_include_directories(openthread-posix PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/core ${PROJECT_SOURCE_DIR}/third_party/mbedtls/repo/include + ${OT_SPINEL_VENDOR_HOOK_SOURCE_DIR} PUBLIC ${PROJECT_SOURCE_DIR}/src/posix/platform/include ) diff --git a/src/posix/platform/backbone.cpp b/src/posix/platform/backbone.cpp deleted file mode 100644 index 517dc90f1..000000000 --- a/src/posix/platform/backbone.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2020, The OpenThread Authors. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file - * This file implements the platform Backbone interface management on Linux. - */ - -#include "openthread-posix-config.h" - -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE - -#include "multicast_routing.hpp" -#include "platform-posix.h" -#include "common/code_utils.hpp" -#include "posix/platform/mainloop.hpp" - -char gBackboneNetifName[IFNAMSIZ] = ""; -unsigned int gBackboneNetifIndex = 0; -#if OPENTHREAD_POSIX_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE -static ot::Posix::MulticastRoutingManager sMulticastRoutingManager; -#endif - -void platformBackboneInit(const char *aInterfaceName) -{ - if (aInterfaceName == nullptr || aInterfaceName[0] == '\0') - { - otLogWarnPlat("Backbone Router feature is disabled: infra/backbone interface is missing"); - ExitNow(); - } - - VerifyOrDie(strnlen(aInterfaceName, sizeof(gBackboneNetifName)) < sizeof(gBackboneNetifName), - OT_EXIT_INVALID_ARGUMENTS); - strcpy(gBackboneNetifName, aInterfaceName); - - gBackboneNetifIndex = if_nametoindex(gBackboneNetifName); - VerifyOrDie(gBackboneNetifIndex > 0, OT_EXIT_FAILURE); - - otLogInfoPlat("Backbone interface is configured to %s (%d)", gBackboneNetifName, gBackboneNetifIndex); - -exit: - return; -} - -void platformBackboneSetUp(void) -{ -#if OPENTHREAD_POSIX_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE - sMulticastRoutingManager.SetUp(); -#endif -} - -void platformBackboneTearDown(void) -{ -#if OPENTHREAD_POSIX_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE - sMulticastRoutingManager.TearDown(); -#endif -} - -void platformBackboneDeinit(void) -{ - gBackboneNetifIndex = 0; - - memset(gBackboneNetifName, 0, sizeof(gBackboneNetifName)); -} - -void platformBackboneStateChange(otInstance *aInstance, otChangedFlags aFlags) -{ - OT_UNUSED_VARIABLE(aInstance); - OT_UNUSED_VARIABLE(aFlags); - -#if OPENTHREAD_POSIX_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE - sMulticastRoutingManager.HandleStateChange(aInstance, aFlags); -#endif -} - -#endif diff --git a/src/posix/platform/ble.cpp b/src/posix/platform/ble.cpp new file mode 100644 index 000000000..2fe3c6453 --- /dev/null +++ b/src/posix/platform/ble.cpp @@ -0,0 +1,75 @@ +/* + * 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 + +otError otPlatBleEnable(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleDisable(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aInterval); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapAdvStop(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapDisconnect(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aMtu); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aHandle); + OT_UNUSED_VARIABLE(aPacket); + return OT_ERROR_NOT_IMPLEMENTED; +} diff --git a/src/posix/platform/configuration.cpp b/src/posix/platform/configuration.cpp index 6510c4cf9..07f120840 100644 --- a/src/posix/platform/configuration.cpp +++ b/src/posix/platform/configuration.cpp @@ -43,6 +43,15 @@ namespace ot { namespace Posix { +#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +const char Configuration::kKeyCalibratedPower[] = "calibrated_power"; +#endif +const char Configuration::kKeyTargetPower[] = "target_power"; +const char Configuration::kKeyRegionDomainMapping[] = "region_domain_mapping"; +const char Configuration::kKeySupportedChannelMask[] = "supported_channel_mask"; +const char Configuration::kKeyPreferredChannelMask[] = "preferred_channel_mask"; +const char Configuration::kCommaDelimiter[] = ","; + otError Configuration::SetRegion(uint16_t aRegionCode) { otError error = OT_ERROR_NONE; diff --git a/src/posix/platform/configuration.hpp b/src/posix/platform/configuration.hpp index dd6c6a209..71c479ddb 100644 --- a/src/posix/platform/configuration.hpp +++ b/src/posix/platform/configuration.hpp @@ -55,8 +55,8 @@ class Configuration { public: Configuration(void) - : mFactoryConfigFile(kFactoryConfigFile) - , mProductConfigFile(kProductConfigFile) + : mFactoryConfigFile(OPENTHREAD_POSIX_CONFIG_FACTORY_CONFIG_FILE) + , mProductConfigFile(OPENTHREAD_POSIX_CONFIG_PRODUCT_CONFIG_FILE) , mRegionCode(0) , mSupportedChannelMask(kDefaultChannelMask) , mPreferredChannelMask(kDefaultChannelMask) @@ -114,17 +114,17 @@ class Configuration bool IsValid(void) const; 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 *kKeySupportedChannelMask = "supported_channel_mask"; - const char *kKeyPreferredChannelMask = "preferred_channel_mask"; - const char *kCommaDelimiter = ","; - static constexpr uint16_t kMaxValueSize = 512; - static constexpr uint16_t kRegionCodeWorldWide = 0x5757; // Region Code: "WW" - static constexpr uint32_t kDefaultChannelMask = 0x7fff800; // Channel 11 ~ 26 +#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + static const char kKeyCalibratedPower[]; +#endif + static const char kKeyTargetPower[]; + static const char kKeyRegionDomainMapping[]; + static const char kKeySupportedChannelMask[]; + static const char kKeyPreferredChannelMask[]; + static const char kCommaDelimiter[]; + static constexpr uint16_t kMaxValueSize = 512; + static constexpr uint16_t kRegionCodeWorldWide = 0x5757; // Region Code: "WW" + static constexpr uint32_t kDefaultChannelMask = 0x7fff800; // Channel 11 ~ 26 uint16_t StringToRegionCode(const char *aString) const { diff --git a/src/posix/platform/include/openthread/openthread-system.h b/src/posix/platform/include/openthread/openthread-system.h index 820cf2b1f..61a07410f 100644 --- a/src/posix/platform/include/openthread/openthread-system.h +++ b/src/posix/platform/include/openthread/openthread-system.h @@ -187,6 +187,14 @@ unsigned int otSysGetThreadNetifIndex(void); */ const char *otSysGetInfraNetifName(void); +/** + * Returns the infrastructure network interface index. + * + * @returns The infrastructure network interface index. + * + */ +uint32_t otSysGetInfraNetifIndex(void); + /** * Returns the radio spinel metrics. * @@ -238,6 +246,14 @@ void otSysCountInfraNetifAddresses(otSysInfraNetIfAddressCounters *aAddressCount */ void otSysSetInfraNetif(const char *aInfraNetifName, int aIcmp6Socket); +/** + * Returns TRUE if the infrastructure interface is running. + * + * @returns TRUE if the infrastructure interface is running, FALSE if not. + * + */ +bool otSysInfraIfIsRunning(void); + #ifdef __cplusplus } // end of extern "C" #endif diff --git a/src/posix/platform/infra_if.cpp b/src/posix/platform/infra_if.cpp index 2435fbdbb..6e16b3f7f 100644 --- a/src/posix/platform/infra_if.cpp +++ b/src/posix/platform/infra_if.cpp @@ -91,6 +91,7 @@ bool otPlatInfraIfHasAddress(uint32_t aInfraIfIndex, const otIp6Address *aAddres return ret; } +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, const otIp6Address *aDestAddress, const uint8_t *aBuffer, @@ -98,6 +99,7 @@ otError otPlatInfraIfSendIcmp6Nd(uint32_t aInfraIfIndex, { return ot::Posix::InfraNetif::Get().SendIcmp6Nd(aInfraIfIndex, *aDestAddress, aBuffer, aBufferLength); } +#endif otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex) { @@ -110,10 +112,12 @@ otError otPlatInfraIfDiscoverNat64Prefix(uint32_t aInfraIfIndex) #endif } -bool platformInfraIfIsRunning(void) { return ot::Posix::InfraNetif::Get().IsRunning(); } +bool otSysInfraIfIsRunning(void) { return ot::Posix::InfraNetif::Get().IsRunning(); } const char *otSysGetInfraNetifName(void) { return ot::Posix::InfraNetif::Get().GetNetifName(); } +uint32_t otSysGetInfraNetifIndex(void) { return ot::Posix::InfraNetif::Get().GetNetifIndex(); } + uint32_t otSysGetInfraNetifFlags(void) { return ot::Posix::InfraNetif::Get().GetFlags(); } void otSysCountInfraNetifAddresses(otSysInfraNetIfAddressCounters *aAddressCounters) @@ -206,6 +210,7 @@ int CreateNetLinkSocket(void) return sock; } +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE otError InfraNetif::SendIcmp6Nd(uint32_t aInfraIfIndex, const otIp6Address &aDestAddress, const uint8_t *aBuffer, @@ -279,6 +284,7 @@ otError InfraNetif::SendIcmp6Nd(uint32_t aInfraIfIndex, exit: return error; } +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE bool InfraNetif::IsRunning(void) const { @@ -352,6 +358,20 @@ void InfraNetif::CountAddresses(otSysInfraNetIfAddressCounters &aAddressCounters return; } +#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE +void InfraNetif::HandleBackboneStateChange(otInstance *aInstance, otChangedFlags aFlags) +{ + OT_ASSERT(gInstance == aInstance); + + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aFlags); + +#if OPENTHREAD_POSIX_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE + mMulticastRoutingManager.HandleStateChange(aInstance, aFlags); +#endif +} +#endif + bool InfraNetif::HasLinkLocalAddress(void) const { bool hasLla = false; @@ -389,18 +409,23 @@ void InfraNetif::Init(void) { mNetLinkSocket = CreateNetLinkSocket(); } void InfraNetif::SetInfraNetif(const char *aIfName, int aIcmp6Socket) { - uint32_t ifIndex = 0; - otBorderRoutingState state = otBorderRoutingGetState(gInstance); + uint32_t ifIndex = 0; - OT_ASSERT(gInstance != nullptr); + OT_UNUSED_VARIABLE(aIcmp6Socket); + OT_ASSERT(gInstance != nullptr); VerifyOrDie(mNetLinkSocket != -1, OT_EXIT_INVALID_STATE); - VerifyOrDie(state == OT_BORDER_ROUTING_STATE_UNINITIALIZED || state == OT_BORDER_ROUTING_STATE_DISABLED, - OT_EXIT_INVALID_STATE); + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + SetInfraNetifIcmp6SocketForBorderRouting(aIcmp6Socket); +#endif +#if OPENTHREAD_POSIX_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE + VerifyOrDie(!mMulticastRoutingManager.IsEnabled(), OT_EXIT_INVALID_STATE); +#endif if (aIfName == nullptr || aIfName[0] == '\0') { - otLogWarnPlat("Border Routing feature is disabled: infra/backbone interface is missing"); + otLogWarnPlat("Border Routing/Backbone Router feature is disabled: infra interface is missing"); ExitNow(); } @@ -417,12 +442,6 @@ void InfraNetif::SetInfraNetif(const char *aIfName, int aIcmp6Socket) mInfraIfIndex = ifIndex; - if (mInfraIfIcmp6Socket != -1) - { - close(mInfraIfIcmp6Socket); - } - mInfraIfIcmp6Socket = aIcmp6Socket; - exit: return; } @@ -432,8 +451,15 @@ void InfraNetif::SetUp(void) OT_ASSERT(gInstance != nullptr); VerifyOrExit(mNetLinkSocket != -1); - SuccessOrDie(otBorderRoutingInit(gInstance, mInfraIfIndex, platformInfraIfIsRunning())); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + SuccessOrDie(otBorderRoutingInit(gInstance, mInfraIfIndex, otSysInfraIfIsRunning())); SuccessOrDie(otBorderRoutingSetEnabled(gInstance, /* aEnabled */ true)); +#endif + +#if OPENTHREAD_POSIX_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE + mMulticastRoutingManager.SetUp(); +#endif + Mainloop::Manager::Get().Add(*this); exit: return; @@ -441,17 +467,26 @@ void InfraNetif::SetUp(void) void InfraNetif::TearDown(void) { +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE IgnoreError(otBorderRoutingSetEnabled(gInstance, false)); +#endif + +#if OPENTHREAD_POSIX_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE + mMulticastRoutingManager.TearDown(); +#endif + Mainloop::Manager::Get().Remove(*this); } void InfraNetif::Deinit(void) { +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE if (mInfraIfIcmp6Socket != -1) { close(mInfraIfIcmp6Socket); mInfraIfIcmp6Socket = -1; } +#endif if (mNetLinkSocket != -1) { @@ -459,16 +494,20 @@ void InfraNetif::Deinit(void) mNetLinkSocket = -1; } - mInfraIfIndex = 0; + mInfraIfName[0] = '\0'; + mInfraIfIndex = 0; } void InfraNetif::Update(otSysMainloopContext &aContext) { - VerifyOrExit(mInfraIfIcmp6Socket != -1); VerifyOrExit(mNetLinkSocket != -1); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + VerifyOrExit(mInfraIfIcmp6Socket != -1); + FD_SET(mInfraIfIcmp6Socket, &aContext.mReadFdSet); aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mInfraIfIcmp6Socket); +#endif FD_SET(mNetLinkSocket, &aContext.mReadFdSet); aContext.mMaxFd = OT_MAX(aContext.mMaxFd, mNetLinkSocket); @@ -506,7 +545,9 @@ void InfraNetif::ReceiveNetLinkMessage(void) case RTM_DELADDR: case RTM_NEWLINK: case RTM_DELLINK: - SuccessOrDie(otPlatInfraIfStateChanged(gInstance, mInfraIfIndex, platformInfraIfIsRunning())); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + SuccessOrDie(otPlatInfraIfStateChanged(gInstance, mInfraIfIndex, otSysInfraIfIsRunning())); +#endif break; case NLMSG_ERROR: { @@ -525,6 +566,7 @@ void InfraNetif::ReceiveNetLinkMessage(void) return; } +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE void InfraNetif::ReceiveIcmp6Message(void) { otError error = OT_ERROR_NONE; @@ -596,6 +638,7 @@ void InfraNetif::ReceiveIcmp6Message(void) otLogDebgPlat("Failed to handle ICMPv6 message: %s", otThreadErrorToString(error)); } } +#endif // OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE #if OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE const char InfraNetif::kWellKnownIpv4OnlyName[] = "ipv4only.arpa"; @@ -671,7 +714,9 @@ void InfraNetif::DiscoverNat64PrefixDone(union sigval sv) } } +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE otPlatInfraIfDiscoverNat64PrefixDone(gInstance, Get().mInfraIfIndex, &prefix); +#endif exit: freeaddrinfo(res); @@ -726,15 +771,35 @@ otError InfraNetif::DiscoverNat64Prefix(uint32_t aInfraIfIndex) } #endif // OPENTHREAD_POSIX_CONFIG_NAT64_AIL_PREFIX_ENABLE +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +void InfraNetif::SetInfraNetifIcmp6SocketForBorderRouting(int aIcmp6Socket) +{ + otBorderRoutingState state = otBorderRoutingGetState(gInstance); + + VerifyOrDie(state == OT_BORDER_ROUTING_STATE_UNINITIALIZED || state == OT_BORDER_ROUTING_STATE_DISABLED, + OT_EXIT_INVALID_STATE); + + if (mInfraIfIcmp6Socket != -1) + { + close(mInfraIfIcmp6Socket); + } + mInfraIfIcmp6Socket = aIcmp6Socket; +} +#endif + void InfraNetif::Process(const otSysMainloopContext &aContext) { +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE VerifyOrExit(mInfraIfIcmp6Socket != -1); +#endif VerifyOrExit(mNetLinkSocket != -1); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE if (FD_ISSET(mInfraIfIcmp6Socket, &aContext.mReadFdSet)) { ReceiveIcmp6Message(); } +#endif if (FD_ISSET(mNetLinkSocket, &aContext.mReadFdSet)) { diff --git a/src/posix/platform/infra_if.hpp b/src/posix/platform/infra_if.hpp index b468e10a1..e8aedd2f9 100644 --- a/src/posix/platform/infra_if.hpp +++ b/src/posix/platform/infra_if.hpp @@ -37,6 +37,7 @@ #include #include +#include "multicast_routing.hpp" #include "core/common/non_copyable.hpp" #include "posix/platform/mainloop.hpp" @@ -82,7 +83,8 @@ class InfraNetif : public Mainloop::Source, private NonCopyable * Sets the infrastructure network interface. * * @param[in] aIfName A pointer to infrastructure network interface name. - * @param[in] aIcmp6Socket A SOCK_RAW socket for sending/receiving ICMPv6 messages. + * @param[in] aIcmp6Socket A SOCK_RAW socket for sending/receiving ICMPv6 messages. If you don't need border + * routing feature, you can pass in -1. * */ void SetInfraNetif(const char *aIfName, int aIcmp6Socket); @@ -133,6 +135,15 @@ class InfraNetif : public Mainloop::Source, private NonCopyable */ void CountAddresses(otSysInfraNetIfAddressCounters &aAddressCounters) const; + /** + * Handles the backbone state change events. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * @param[in] aFlags Flags that denote the state change events. + * + */ + void HandleBackboneStateChange(otInstance *aInstance, otChangedFlags aFlags); + /** * Sends an ICMPv6 Neighbor Discovery message on given infrastructure interface. * @@ -176,6 +187,14 @@ class InfraNetif : public Mainloop::Source, private NonCopyable */ const char *GetNetifName(void) const { return (mInfraIfIndex != 0) ? mInfraIfName : nullptr; } + /** + * Gets the infrastructure network interface index. + * + * @returns The infrastructure network interface index. + * + */ + uint32_t GetNetifIndex(void) const { return mInfraIfIndex; } + /** * Gets the infrastructure network interface singleton. * @@ -201,14 +220,22 @@ class InfraNetif : public Mainloop::Source, private NonCopyable static const uint8_t kValidNat64PrefixLength[]; char mInfraIfName[IFNAMSIZ]; - uint32_t mInfraIfIndex = 0; - int mInfraIfIcmp6Socket = -1; - int mNetLinkSocket = -1; + uint32_t mInfraIfIndex = 0; + int mNetLinkSocket = -1; +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + int mInfraIfIcmp6Socket = -1; +#endif +#if OPENTHREAD_POSIX_CONFIG_BACKBONE_ROUTER_MULTICAST_ROUTING_ENABLE + MulticastRoutingManager mMulticastRoutingManager; +#endif void ReceiveNetLinkMessage(void); - void ReceiveIcmp6Message(void); bool HasLinkLocalAddress(void) const; static void DiscoverNat64PrefixDone(union sigval sv); +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + void SetInfraNetifIcmp6SocketForBorderRouting(int aIcmp6Socket); + void ReceiveIcmp6Message(void); +#endif }; } // namespace Posix diff --git a/src/posix/platform/multicast_routing.cpp b/src/posix/platform/multicast_routing.cpp index abced8c4d..40af863a6 100644 --- a/src/posix/platform/multicast_routing.cpp +++ b/src/posix/platform/multicast_routing.cpp @@ -159,7 +159,7 @@ void MulticastRoutingManager::UpdateMldReport(const Ip6::Address &aAddress, bool struct ipv6_mreq ipv6mr; otError error = OT_ERROR_NONE; - ipv6mr.ipv6mr_interface = if_nametoindex(gBackboneNetifName); + ipv6mr.ipv6mr_interface = if_nametoindex(otSysGetInfraNetifName()); memcpy(&ipv6mr.ipv6mr_multiaddr, aAddress.GetBytes(), sizeof(ipv6mr.ipv6mr_multiaddr)); error = (setsockopt(mMulticastRouterSock, IPPROTO_IPV6, (isAdd ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP), (void *)&ipv6mr, sizeof(ipv6mr)) @@ -243,7 +243,7 @@ void MulticastRoutingManager::InitMulticastRouterSock(void) // Add Backbone network interface to MIF mif6ctl.mif6c_mifi = kMifIndexBackbone; - mif6ctl.mif6c_pifi = if_nametoindex(gBackboneNetifName); + mif6ctl.mif6c_pifi = otSysGetInfraNetifIndex(); VerifyOrDie(mif6ctl.mif6c_pifi > 0, OT_EXIT_ERROR_ERRNO); VerifyOrDie(0 == setsockopt(mMulticastRouterSock, IPPROTO_IPV6, MRT6_ADD_MIF, &mif6ctl, sizeof(mif6ctl)), OT_EXIT_ERROR_ERRNO); diff --git a/src/posix/platform/multicast_routing.hpp b/src/posix/platform/multicast_routing.hpp index 08d87ca58..c4d7e32fd 100644 --- a/src/posix/platform/multicast_routing.hpp +++ b/src/posix/platform/multicast_routing.hpp @@ -58,6 +58,7 @@ class MulticastRoutingManager : public Mainloop::Source, private NonCopyable { } + bool IsEnabled(void) const { return mMulticastRouterSock >= 0; } void SetUp(void); void TearDown(void); void Update(otSysMainloopContext &aContext) override; @@ -110,7 +111,6 @@ class MulticastRoutingManager : public Mainloop::Source, private NonCopyable void Remove(const Ip6::Address &aAddress); void UpdateMldReport(const Ip6::Address &aAddress, bool isAdd); bool HasMulticastListener(const Ip6::Address &aAddress) const; - bool IsEnabled(void) const { return mMulticastRouterSock >= 0; } void InitMulticastRouterSock(void); void FinalizeMulticastRouterSock(void); void ProcessMulticastRouterMessages(void); diff --git a/src/posix/platform/netif.cpp b/src/posix/platform/netif.cpp index cfc70c761..3d546eb3c 100644 --- a/src/posix/platform/netif.cpp +++ b/src/posix/platform/netif.cpp @@ -900,6 +900,7 @@ static bool isSameIp4Cidr(const otIp4Cidr &aCidr1, const otIp4Cidr &aCidr2) static void processNat64StateChange(void) { otIp4Cidr translatorCidr; + otError error = OT_ERROR_NONE; // Skip if NAT64 translator has not been configured with a CIDR. SuccessOrExit(otNat64GetCidr(gInstance, &translatorCidr)); @@ -910,7 +911,10 @@ static void processNat64StateChange(void) if (sActiveNat64Cidr.mLength != 0) { - DeleteIp4Route(sActiveNat64Cidr); + if ((error = DeleteIp4Route(sActiveNat64Cidr)) != OT_ERROR_NONE) + { + otLogWarnPlat("[netif] failed to delete route for NAT64: %s", otThreadErrorToString(error)); + } } sActiveNat64Cidr = translatorCidr; @@ -920,12 +924,18 @@ static void processNat64StateChange(void) if (otNat64GetTranslatorState(gInstance) == OT_NAT64_STATE_ACTIVE) { - AddIp4Route(sActiveNat64Cidr, kNat64RoutePriority); + if ((error = AddIp4Route(sActiveNat64Cidr, kNat64RoutePriority)) != OT_ERROR_NONE) + { + otLogWarnPlat("[netif] failed to add route for NAT64: %s", otThreadErrorToString(error)); + } otLogInfoPlat("[netif] Adding route for NAT64"); } else if (sActiveNat64Cidr.mLength > 0) // Translator is not active. { - DeleteIp4Route(sActiveNat64Cidr); + if ((error = DeleteIp4Route(sActiveNat64Cidr)) != OT_ERROR_NONE) + { + otLogWarnPlat("[netif] failed to delete route for NAT64: %s", otThreadErrorToString(error)); + } otLogInfoPlat("[netif] Deleting route for NAT64"); } @@ -1301,7 +1311,10 @@ static void processNetifLinkEvent(otInstance *aInstance, struct nlmsghdr *aNetli if (isUp && sActiveNat64Cidr.mLength > 0) { // Recover NAT64 route. - AddIp4Route(sActiveNat64Cidr, kNat64RoutePriority); + if ((error = AddIp4Route(sActiveNat64Cidr, kNat64RoutePriority)) != OT_ERROR_NONE) + { + otLogWarnPlat("[netif] failed to add route for NAT64: %s", otThreadErrorToString(error)); + } } #endif @@ -2080,10 +2093,14 @@ void platformNetifInit(otPlatformConfig *aPlatformConfig) void nat64Init(void) { otIp4Cidr cidr; + otError error = OT_ERROR_NONE; if (otIp4CidrFromString(OPENTHREAD_POSIX_CONFIG_NAT64_CIDR, &cidr) == OT_ERROR_NONE && cidr.mLength != 0) { - otNat64SetIp4Cidr(gInstance, &cidr); + if ((error = otNat64SetIp4Cidr(gInstance, &cidr)) != OT_ERROR_NONE) + { + otLogWarnPlat("[netif] failed to set CIDR for NAT64: %s", otThreadErrorToString(error)); + } } else { diff --git a/src/posix/platform/openthread-posix-config.h b/src/posix/platform/openthread-posix-config.h index 65d65d6a2..cc8afe5cb 100644 --- a/src/posix/platform/openthread-posix-config.h +++ b/src/posix/platform/openthread-posix-config.h @@ -394,7 +394,8 @@ * not explicit defined. */ #ifndef OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE -#define OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE +#define OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE \ + (OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE || OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE) #endif /** diff --git a/src/posix/platform/platform-posix.h b/src/posix/platform/platform-posix.h index 8f05602a2..912ceb590 100644 --- a/src/posix/platform/platform-posix.h +++ b/src/posix/platform/platform-posix.h @@ -409,77 +409,12 @@ extern char gNetifName[IFNAMSIZ]; */ extern unsigned int gNetifIndex; -/** - * Initializes platform Backbone network. - * - * @note This function is called before OpenThread instance is created. - * - * @param[in] aInterfaceName A pointer to Thread network interface name. - * - */ -void platformBackboneInit(const char *aInterfaceName); - -/** - * Sets up platform Backbone network. - * - * @note This function is called after OpenThread instance is created. - * - * @param[in] aInstance A pointer to the OpenThread instance. - * - */ -void platformBackboneSetUp(void); - -/** - * Tears down platform Backbone network. - * - * @note This function is called before OpenThread instance is destructed. - * - */ -void platformBackboneTearDown(void); - -/** - * Shuts down the platform Backbone network. - * - * @note This function is called after OpenThread instance is destructed. - * - */ -void platformBackboneDeinit(void); - -/** - * Performs notifies state changes to platform Backbone network. - * - * @param[in] aInstance A pointer to the OpenThread instance. - * @param[in] aFlags Flags that denote the state change events. - * - */ -void platformBackboneStateChange(otInstance *aInstance, otChangedFlags aFlags); - /** * A pointer to the OpenThread instance. * */ extern otInstance *gInstance; -/** - * The name of Backbone network interface. - * - */ -extern char gBackboneNetifName[IFNAMSIZ]; - -/** - * The index of Backbone network interface. - * - */ -extern unsigned int gBackboneNetifIndex; - -/** - * Tells if the infrastructure interface is running. - * - * @returns TRUE if the infrastructure interface is running, FALSE if not. - * - */ -bool platformInfraIfIsRunning(void); - /** * Initializes backtrace module. * diff --git a/src/posix/platform/radio.cpp b/src/posix/platform/radio.cpp index 86901dd7d..44d011606 100644 --- a/src/posix/platform/radio.cpp +++ b/src/posix/platform/radio.cpp @@ -66,12 +66,26 @@ Radio::Radio(void) void Radio::Init(const char *aUrl) { - bool resetRadio; - bool skipCompatibilityCheck; + bool resetRadio; + bool skipCompatibilityCheck; + spinel_iid_t iidList[Spinel::kSpinelHeaderMaxNumIid]; + struct ot::Spinel::RadioSpinelCallbacks callbacks; mRadioUrl = aUrl; VerifyOrDie(mRadioUrl.GetPath() != nullptr, OT_EXIT_INVALID_ARGUMENTS); + memset(&callbacks, 0, sizeof(callbacks)); +#if OPENTHREAD_CONFIG_DIAG_ENABLE + callbacks.mDiagReceiveDone = otPlatDiagRadioReceiveDone; + callbacks.mDiagTransmitDone = otPlatDiagRadioTransmitDone; +#endif // OPENTHREAD_CONFIG_DIAG_ENABLE + callbacks.mEnergyScanDone = otPlatRadioEnergyScanDone; + callbacks.mReceiveDone = otPlatRadioReceiveDone; + callbacks.mTransmitDone = otPlatRadioTxDone; + callbacks.mTxStarted = otPlatRadioTxStarted; + + GetIidListFromRadioUrl(iidList); + #if OPENTHREAD_POSIX_VIRTUAL_TIME VirtualTimeInit(); #endif @@ -81,7 +95,10 @@ void Radio::Init(const char *aUrl) resetRadio = !mRadioUrl.HasParam("no-reset"); skipCompatibilityCheck = mRadioUrl.HasParam("skip-rcp-compatibility-check"); - mRadioSpinel.Init(*mSpinelInterface, resetRadio, skipCompatibilityCheck); + + mRadioSpinel.SetCallbacks(callbacks); + mRadioSpinel.Init(*mSpinelInterface, resetRadio, skipCompatibilityCheck, iidList, OT_ARRAY_LENGTH(iidList)); + otLogDebgPlat("instance init:%p - iid = %d", (void *)&mRadioSpinel, iidList[0]); ProcessRadioUrl(mRadioUrl); } @@ -229,10 +246,57 @@ void Radio::ProcessMaxPowerTable(const RadioUrl &aRadioUrl) #endif // OPENTHREAD_POSIX_CONFIG_MAX_POWER_TABLE_ENABLE } +void Radio::GetIidListFromRadioUrl(spinel_iid_t (&aIidList)[Spinel::kSpinelHeaderMaxNumIid]) +{ + const char *iidString; + const char *iidListString; + + memset(aIidList, SPINEL_HEADER_INVALID_IID, sizeof(aIidList)); + + iidString = (mRadioUrl.GetValue("iid")); + iidListString = (mRadioUrl.GetValue("iid-list")); + +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + // First entry to the aIidList must be the IID of the host application. + VerifyOrDie(iidString != nullptr, OT_EXIT_INVALID_ARGUMENTS); + aIidList[0] = static_cast(atoi(iidString)); + + if (iidListString != nullptr) + { + // Convert string to an array of integers. + // Integer i is for traverse the iidListString. + // Integer j is for aIidList array offset location. + // First entry of aIidList is for host application iid hence j start from 1. + for (uint8_t i = 0, j = 1; iidListString[i] != '\0' && j < Spinel::kSpinelHeaderMaxNumIid; i++) + { + if (iidListString[i] == ',') + { + j++; + continue; + } + + if (iidListString[i] < '0' || iidListString[i] > '9') + { + DieNow(OT_EXIT_INVALID_ARGUMENTS); + } + else + { + aIidList[j] = iidListString[i] - '0'; + VerifyOrDie(aIidList[j] < Spinel::kSpinelHeaderMaxNumIid, OT_EXIT_INVALID_ARGUMENTS); + } + } + } +#else // !OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + VerifyOrDie(iidString == nullptr, OT_EXIT_INVALID_ARGUMENTS); + VerifyOrDie(iidListString == nullptr, OT_EXIT_INVALID_ARGUMENTS); + aIidList[0] = 0; +#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE +} + } // namespace Posix } // namespace ot -static ot::Spinel::RadioSpinel &GetRadioSpinel(void) { return sRadio.GetRadioSpinel(); } +ot::Spinel::RadioSpinel &GetRadioSpinel(void) { return sRadio.GetRadioSpinel(); } void platformRadioDeinit(void) { GetRadioSpinel().Deinit(); } diff --git a/src/posix/platform/radio.hpp b/src/posix/platform/radio.hpp index ae0626614..00c958c2f 100644 --- a/src/posix/platform/radio.hpp +++ b/src/posix/platform/radio.hpp @@ -35,6 +35,11 @@ #include "posix/platform/radio_url.hpp" #include "posix/platform/spi_interface.hpp" #include "posix/platform/vendor_interface.hpp" +#if OPENTHREAD_SPINEL_CONFIG_ENABLE_VENDOR_HOOK +#ifdef OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER +#include OPENTHREAD_SPINEL_CONFIG_VENDOR_HOOK_HEADER +#endif +#endif namespace ot { namespace Posix { @@ -88,6 +93,7 @@ class Radio void ProcessMaxPowerTable(const RadioUrl &aRadioUrl); Spinel::SpinelInterface *CreateSpinelInterface(const char *aInterfaceName); + void GetIidListFromRadioUrl(spinel_iid_t (&aIidList)[Spinel::kSpinelHeaderMaxNumIid]); #if OPENTHREAD_POSIX_CONFIG_SPINEL_HDLC_INTERFACE_ENABLE && OPENTHREAD_POSIX_CONFIG_SPINEL_SPI_INTERFACE_ENABLE static constexpr size_t kSpinelInterfaceRawSize = sizeof(ot::Posix::SpiInterface) > sizeof(ot::Posix::HdlcInterface) @@ -103,8 +109,12 @@ class Radio #error "No Spinel interface is specified!" #endif - RadioUrl mRadioUrl; - Spinel::RadioSpinel mRadioSpinel; + RadioUrl mRadioUrl; +#if OPENTHREAD_ENABLE_SPINEL_VENDOR_HOOK + Spinel::VendorRadioSpinel mRadioSpinel; +#else + Spinel::RadioSpinel mRadioSpinel; +#endif Spinel::SpinelInterface *mSpinelInterface; OT_DEFINE_ALIGNED_VAR(mSpinelInterfaceRaw, kSpinelInterfaceRawSize, uint64_t); diff --git a/src/posix/platform/radio_url.cpp b/src/posix/platform/radio_url.cpp index b57e29f81..bd34d9c65 100644 --- a/src/posix/platform/radio_url.cpp +++ b/src/posix/platform/radio_url.cpp @@ -120,7 +120,16 @@ const char *otSysGetRadioUrlHelpString(void) " Disable coex with 0, and enable it with other values.\n" " fem-lnagain[=dbm] Set the Rx LNA gain in dBm of the external FEM.\n" " no-reset Do not send Spinel reset command to RCP on initialization.\n" - " skip-rcp-compatibility-check Skip checking RCP API version and capabilities during initialization.\n"; + " skip-rcp-compatibility-check Skip checking RCP API version and capabilities during initialization.\n" +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + " iid Set the Spinel Interface ID for this process. Valid values are 0-3.\n" + " iid-list List of IIDs a host can subscribe to receive spinel frames other than \n" + " provided in 'iid' argument. If not specified, host will subscribe to \n" + " the interface ID provided in 'iid` argument. Valid values are 0-3. \n" + " Upto three IIDs can be provided with each IID separated by ',' \n" + " e.g. iid-list=1,2,3 \n" +#endif + ; } namespace ot { diff --git a/src/posix/platform/system.cpp b/src/posix/platform/system.cpp index a154ff829..db10899d2 100644 --- a/src/posix/platform/system.cpp +++ b/src/posix/platform/system.cpp @@ -72,10 +72,7 @@ static void processStateChange(otChangedFlags aFlags, void *aContext) #endif #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE - if (gBackboneNetifIndex != 0) - { - platformBackboneStateChange(instance, aFlags); - } + ot::Posix::InfraNetif::Get().HandleBackboneStateChange(instance, aFlags); #endif } #endif @@ -145,10 +142,6 @@ void platformInit(otPlatformConfig *aPlatformConfig) #endif platformRandomInit(); -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE - platformBackboneInit(aPlatformConfig->mBackboneInterfaceName); -#endif - #if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE ot::Posix::InfraNetif::Get().Init(); @@ -178,16 +171,20 @@ void platformSetUp(otPlatformConfig *aPlatformConfig) VerifyOrExit(!gDryRun); -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE - platformBackboneSetUp(); -#endif - #if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE if (aPlatformConfig->mBackboneInterfaceName != nullptr && strlen(aPlatformConfig->mBackboneInterfaceName) > 0) { - otSysSetInfraNetif(aPlatformConfig->mBackboneInterfaceName, - ot::Posix::InfraNetif::CreateIcmp6Socket(aPlatformConfig->mBackboneInterfaceName)); + int icmp6Sock = -1; + +#if OPENTHREAD_CONFIG_BORDER_ROUTING_ENABLE + icmp6Sock = ot::Posix::InfraNetif::CreateIcmp6Socket(aPlatformConfig->mBackboneInterfaceName); +#endif + + otSysSetInfraNetif(aPlatformConfig->mBackboneInterfaceName, icmp6Sock); } +#endif + +#if OPENTHREAD_POSIX_CONFIG_INFRA_IF_ENABLE ot::Posix::InfraNetif::Get().SetUp(); #endif @@ -246,10 +243,6 @@ void platformTearDown(void) ot::Posix::InfraNetif::Get().TearDown(); #endif -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE - platformBackboneTearDown(); -#endif - exit: return; } @@ -278,10 +271,6 @@ void platformDeinit(void) ot::Posix::InfraNetif::Get().Deinit(); #endif -#if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE - platformBackboneDeinit(); -#endif - exit: return; } diff --git a/src/posix/platform/udp.cpp b/src/posix/platform/udp.cpp index 7f78acccb..6d7c880a3 100644 --- a/src/posix/platform/udp.cpp +++ b/src/posix/platform/udp.cpp @@ -324,11 +324,12 @@ otError otPlatUdpBindToNetif(otUdpSocket *aUdpSocket, otNetifIdentifier aNetifId { #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE #if __linux__ - VerifyOrExit(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, gBackboneNetifName, strlen(gBackboneNetifName)) == 0, + VerifyOrExit(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, otSysGetInfraNetifName(), + strlen(otSysGetInfraNetifName())) == 0, error = OT_ERROR_FAILED); #else // __NetBSD__ || __FreeBSD__ || __APPLE__ - VerifyOrExit(setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF, &gBackboneNetifIndex, sizeof(gBackboneNetifIndex)) == - 0, + uint32_t backboneNetifIndex = otSysGetInfraNetifIndex(); + VerifyOrExit(setsockopt(fd, IPPROTO_IPV6, IPV6_BOUND_IF, &backboneNetifIndex, sizeof(backboneNetifIndex)) == 0, error = OT_ERROR_FAILED); #endif // __linux__ #else @@ -473,7 +474,7 @@ otError otPlatUdpJoinMulticastGroup(otUdpSocket *aUdpSocket, break; case OT_NETIF_BACKBONE: #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE - mreq.ipv6mr_interface = gBackboneNetifIndex; + mreq.ipv6mr_interface = otSysGetInfraNetifIndex(); #else ExitNow(error = OT_ERROR_NOT_IMPLEMENTED); #endif @@ -513,7 +514,7 @@ otError otPlatUdpLeaveMulticastGroup(otUdpSocket *aUdpSocket, break; case OT_NETIF_BACKBONE: #if OPENTHREAD_CONFIG_BACKBONE_ROUTER_ENABLE - mreq.ipv6mr_interface = gBackboneNetifIndex; + mreq.ipv6mr_interface = otSysGetInfraNetifIndex(); #else ExitNow(error = OT_ERROR_NOT_IMPLEMENTED); #endif diff --git a/tests/fuzz/fuzzer_platform.cpp b/tests/fuzz/fuzzer_platform.cpp index 8a64cc8b8..e616fc25e 100644 --- a/tests/fuzz/fuzzer_platform.cpp +++ b/tests/fuzz/fuzzer_platform.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include @@ -230,6 +231,10 @@ OT_TOOL_WEAK void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const void otPlatWakeHost(void) {} +otError otPlatMultipanGetActiveInstance(otInstance **) { return OT_ERROR_NOT_IMPLEMENTED; } + +otError otPlatMultipanSetActiveInstance(otInstance *, bool) { return OT_ERROR_NOT_IMPLEMENTED; } + void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) { OT_UNUSED_VARIABLE(aInstance); 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 index 46896bc50..32ef64ab3 100755 --- 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 @@ -131,8 +131,8 @@ def test(self): self.assertNotEqual(br2_local_nat64_prefix, br2.get_br_favored_nat64_prefix()) br2_infra_nat64_prefix = br2.get_br_favored_nat64_prefix() - self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1) - nat64_prefix = br1.get_netdata_nat64_prefix()[0] + self.assertEqual(len(br1.get_netdata_nat64_routes()), 1) + nat64_prefix = br1.get_netdata_nat64_routes()[0] self.assertEqual(nat64_prefix, br2_infra_nat64_prefix) self.assertNotEqual(nat64_prefix, br1_local_nat64_prefix) self.assertDictIncludes(br1.nat64_state, { @@ -151,8 +151,8 @@ def test(self): 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(len(br1.get_netdata_nat64_routes()), 1) + nat64_prefix = br1.get_netdata_nat64_routes()[0] self.assertEqual(nat64_prefix, br1_local_nat64_prefix) self.assertDictIncludes(br1.nat64_state, { 'PrefixManager': NAT64_STATE_ACTIVE, @@ -174,8 +174,8 @@ def test(self): self.simulator.go(10) self.assertEqual(br2_local_nat64_prefix, br2.get_br_favored_nat64_prefix()) - self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1) - nat64_prefix = br1.get_netdata_nat64_prefix()[0] + self.assertEqual(len(br1.get_netdata_nat64_routes()), 1) + nat64_prefix = br1.get_netdata_nat64_routes()[0] self.assertEqual(nat64_prefix, br1_local_nat64_prefix) self.assertNotEqual(nat64_prefix, br2_local_nat64_prefix) self.assertDictIncludes(br1.nat64_state, { @@ -194,8 +194,8 @@ def test(self): 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(len(br1.get_netdata_nat64_routes()), 1) + nat64_prefix = br1.get_netdata_nat64_routes()[0] self.assertEqual(br2_local_nat64_prefix, nat64_prefix) self.assertNotEqual(br1_local_nat64_prefix, nat64_prefix) self.assertDictIncludes(br1.nat64_state, { @@ -214,8 +214,8 @@ def test(self): 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(len(br1.get_netdata_nat64_routes()), 1) + nat64_prefix = br1.get_netdata_nat64_routes()[0] self.assertEqual(br2_local_nat64_prefix, nat64_prefix) self.assertNotEqual(br1_local_nat64_prefix, nat64_prefix) self.assertDictIncludes(br1.nat64_state, { @@ -233,8 +233,8 @@ def test(self): # 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(len(br1.get_netdata_nat64_routes()), 1) + nat64_prefix = br1.get_netdata_nat64_routes()[0] self.assertEqual(br1_local_nat64_prefix, nat64_prefix) self.assertNotEqual(br2_local_nat64_prefix, nat64_prefix) self.assertDictIncludes(br1.nat64_state, { @@ -252,8 +252,8 @@ def test(self): # br2.enable_br() self.simulator.go(10) - self.assertEqual(len(br1.get_netdata_nat64_prefix()), 1) - nat64_prefix = br1.get_netdata_nat64_prefix()[0] + self.assertEqual(len(br1.get_netdata_nat64_routes()), 1) + nat64_prefix = br1.get_netdata_nat64_routes()[0] self.assertEqual(br1_local_nat64_prefix, nat64_prefix) self.assertNotEqual(br2_local_nat64_prefix, nat64_prefix) self.assertDictIncludes(br1.nat64_state, { 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 index 82f27ebd1..33d010e5e 100755 --- 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 @@ -137,8 +137,8 @@ def test(self): self.simulator.go(5) local_nat64_prefix = br.get_br_nat64_prefix() - self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) - nat64_prefix = br.get_netdata_nat64_prefix()[0] + self.assertEqual(len(br.get_netdata_nat64_routes()), 1) + nat64_prefix = br.get_netdata_nat64_routes()[0] self.assertEqual(nat64_prefix, local_nat64_prefix) # @@ -150,15 +150,15 @@ def test(self): 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]) + self.assertEqual(len(br.get_netdata_nat64_routes()), 1) + self.assertNotEqual(local_nat64_prefix, br.get_netdata_nat64_routes()[0]) 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]) + self.assertEqual(len(br.get_netdata_nat64_routes()), 1) + self.assertEqual(local_nat64_prefix, br.get_netdata_nat64_routes()[0]) # # Case 3. @@ -169,15 +169,15 @@ def test(self): 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]) + self.assertEqual(len(br.get_netdata_nat64_routes()), 1) + self.assertNotEqual(local_nat64_prefix, br.get_netdata_nat64_routes()[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]) + self.assertEqual(len(br.get_netdata_nat64_routes()), 1) + self.assertEqual(local_nat64_prefix, br.get_netdata_nat64_routes()[0]) # # Case 4. Disable and re-enable border routing on the border router. @@ -186,7 +186,7 @@ def test(self): self.simulator.go(5) # NAT64 prefix is withdrawn from Network Data. - self.assertEqual(len(br.get_netdata_nat64_prefix()), 0) + self.assertEqual(len(br.get_netdata_nat64_routes()), 0) br.enable_br() self.simulator.go(config.BORDER_ROUTER_STARTUP_DELAY) @@ -256,17 +256,17 @@ def test(self): self.simulator.go(5) # NAT64 prefix is withdrawn from Network Data. - self.assertEqual(len(br.get_netdata_nat64_prefix()), 0) + self.assertEqual(len(br.get_netdata_nat64_routes()), 0) br.enable_ether() self.simulator.go(80) # 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]) + self.assertEqual(len(br.get_netdata_nat64_routes()), 1) + self.assertEqual(nat64_prefix, br.get_netdata_nat64_routes()[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]) + self.assertEqual(len(br.get_netdata_nat64_routes()), 1) + self.assertEqual(nat64_prefix, br.get_netdata_nat64_routes()[0]) if __name__ == '__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 index 905e6f259..21ab2ba54 100755 --- 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 @@ -94,8 +94,8 @@ def test(self): # Case 1 No infra-derived OMR prefix. BR publishes its local prefix. local_nat64_prefix = br.get_br_nat64_prefix() - self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) - nat64_prefix = br.get_netdata_nat64_prefix()[0] + self.assertEqual(len(br.get_netdata_nat64_routes()), 1) + nat64_prefix = br.get_netdata_nat64_routes()[0] self.assertEqual(nat64_prefix, local_nat64_prefix) self.assertDictIncludes(br.nat64_state, { @@ -112,8 +112,8 @@ def test(self): self.assertNotEqual(favored_nat64_prefix, local_nat64_prefix) infra_nat64_prefix = favored_nat64_prefix - self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) - nat64_prefix = br.get_netdata_nat64_prefix()[0] + self.assertEqual(len(br.get_netdata_nat64_routes()), 1) + nat64_prefix = br.get_netdata_nat64_routes()[0] self.assertEqual(nat64_prefix, infra_nat64_prefix) self.assertDictIncludes(br.nat64_state, { 'PrefixManager': NAT64_STATE_ACTIVE, @@ -126,8 +126,8 @@ def test(self): 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.assertEqual(len(br.get_netdata_nat64_routes()), 1) + self.assertNotEqual(infra_nat64_prefix, br.get_netdata_nat64_routes()[0]) self.assertDictIncludes(br.nat64_state, { 'PrefixManager': NAT64_STATE_IDLE, 'Translator': NAT64_STATE_NOT_RUNNING @@ -137,7 +137,7 @@ def test(self): br.register_netdata() self.simulator.go(10) - self.assertEqual(len(br.get_netdata_nat64_prefix()), 1) + self.assertEqual(len(br.get_netdata_nat64_routes()), 1) self.assertEqual(nat64_prefix, infra_nat64_prefix) # Case 4 No change when a smaller prefix in low preference is present @@ -145,8 +145,8 @@ def test(self): 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.assertEqual(len(br.get_netdata_nat64_routes()), 2) + self.assertEqual(br.get_netdata_nat64_routes(), [infra_nat64_prefix, SMALL_NAT64_PREFIX]) self.assertDictIncludes(br.nat64_state, { 'PrefixManager': NAT64_STATE_ACTIVE, 'Translator': NAT64_STATE_NOT_RUNNING @@ -162,8 +162,8 @@ def test(self): 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.assertEqual(len(br.get_netdata_nat64_routes()), 1) + self.assertEqual(br.get_netdata_nat64_routes()[0], local_nat64_prefix) self.assertDictIncludes(br.nat64_state, { 'PrefixManager': NAT64_STATE_ACTIVE, 'Translator': NAT64_STATE_ACTIVE @@ -174,8 +174,8 @@ def test(self): 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.assertEqual(len(br.get_netdata_nat64_routes()), 1) + self.assertEqual(br.get_netdata_nat64_routes()[0], infra_nat64_prefix) self.assertDictIncludes(br.nat64_state, { 'PrefixManager': NAT64_STATE_ACTIVE, 'Translator': NAT64_STATE_NOT_RUNNING @@ -188,8 +188,8 @@ def test(self): 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.assertEqual(len(br.get_netdata_nat64_routes()), 1) + self.assertEqual(br.get_netdata_nat64_routes()[0], SMALL_NAT64_PREFIX) self.assertDictIncludes(br.nat64_state, { 'PrefixManager': NAT64_STATE_ACTIVE, 'Translator': NAT64_STATE_NOT_RUNNING 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 ab354d171..b176b8915 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 @@ -141,10 +141,10 @@ def test(self): self.assertEqual(br1_omr_prefix, br1.get_netdata_omr_prefixes()[0]) # Each BR should independently register an external route for the on-link prefix. - self.assertEqual(len(br1.get_netdata_non_nat64_prefixes()), 2) - self.assertEqual(len(router1.get_netdata_non_nat64_prefixes()), 2) - self.assertEqual(len(br2.get_netdata_non_nat64_prefixes()), 2) - self.assertEqual(len(router2.get_netdata_non_nat64_prefixes()), 2) + self.assertEqual(len(br1.get_netdata_non_nat64_routes()), 2) + self.assertEqual(len(router1.get_netdata_non_nat64_routes()), 2) + self.assertEqual(len(br2.get_netdata_non_nat64_routes()), 2) + self.assertEqual(len(router2.get_netdata_non_nat64_routes()), 2) br1_on_link_prefix = br1.get_br_on_link_prefix() @@ -192,10 +192,10 @@ def test(self): # There should be no changes to the external route for the # on-link prefix, given that the on-link prefix is derived # from the Extended PAN ID. - 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) + self.assertEqual(len(br1.get_netdata_non_nat64_routes()), 1) + self.assertEqual(len(router1.get_netdata_non_nat64_routes()), 1) + self.assertEqual(len(br2.get_netdata_non_nat64_routes()), 1) + self.assertEqual(len(router2.get_netdata_non_nat64_routes()), 1) br2_on_link_prefix = br2.get_br_on_link_prefix() 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 4e0823b0a..7db19ded7 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,17 +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()) == 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() - - br1_external_routes.sort() - br2_external_routes.sort() - self.assertNotEqual(br1_external_routes, br2_external_routes) + self.assertEqual(br1.get_netdata_non_nat64_routes(), ['fc00::/7']) + self.assertEqual(router1.get_netdata_non_nat64_routes(), ['fc00::/7']) + self.assertEqual(br2.get_netdata_non_nat64_routes(), ['fc00::/7']) + self.assertEqual(router2.get_netdata_non_nat64_routes(), ['fc00::/7']) self.assertTrue(len(router1.get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1) self.assertTrue(len(router2.get_ip6_address(config.ADDRESS_TYPE.OMR)) == 1) 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 406723623..0f56baa77 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 @@ -126,7 +126,7 @@ def test(self): logging.info("ROUTER1 addrs: %r", router1.get_addrs()) logging.info("HOST addrs: %r", host.get_addrs()) - self.assertEqual(len(br1.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(br1.get_netdata_non_nat64_routes()), 1) host_on_link_addr = host.get_matched_ula_addresses(ON_LINK_PREFIX)[0] self.assertTrue(router1.ping(host_on_link_addr)) @@ -166,10 +166,10 @@ def test(self): # 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()), 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) + self.assertEqual(len(br1.get_netdata_non_nat64_routes()), 1) + self.assertEqual(len(router1.get_netdata_non_nat64_routes()), 1) + self.assertEqual(len(br2.get_netdata_non_nat64_routes()), 1) + self.assertEqual(len(router2.get_netdata_non_nat64_routes()), 1) br2_on_link_prefix = br2.get_br_on_link_prefix() diff --git a/tests/scripts/thread-cert/border_router/test_radvd_coexist.py b/tests/scripts/thread-cert/border_router/test_radvd_coexist.py index 2fffd6070..6dc7a135e 100755 --- a/tests/scripts/thread-cert/border_router/test_radvd_coexist.py +++ b/tests/scripts/thread-cert/border_router/test_radvd_coexist.py @@ -102,8 +102,8 @@ def test(self): self.assertEqual(len(br.get_netdata_omr_prefixes()), 1) self.assertEqual(len(router.get_netdata_omr_prefixes()), 1) - self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1) - self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1) + self.assertEqual(len(router.get_netdata_non_nat64_routes()), 1) self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) @@ -122,8 +122,8 @@ def test(self): self.assertEqual(len(br.get_netdata_omr_prefixes()), 1) self.assertEqual(len(router.get_netdata_omr_prefixes()), 1) - self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1) - self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1) + self.assertEqual(len(router.get_netdata_non_nat64_routes()), 1) self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 1) 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 a660345fc..fea02fa97 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 @@ -105,8 +105,8 @@ def test(self): self.assertEqual(len(br.get_netdata_omr_prefixes()), 1) self.assertEqual(len(router.get_netdata_omr_prefixes()), 1) - self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1) - self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1) + self.assertEqual(len(router.get_netdata_non_nat64_routes()), 1) omr_prefix = br.get_br_omr_prefix() on_link_prefix = br.get_br_on_link_prefix() @@ -145,8 +145,8 @@ def test(self): self.assertEqual(len(br.get_netdata_omr_prefixes()), 2) self.assertEqual(len(router.get_netdata_omr_prefixes()), 2) - self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1) - self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1) + self.assertEqual(len(router.get_netdata_non_nat64_routes()), 1) self.assertEqual(len(br.get_ip6_address(config.ADDRESS_TYPE.OMR)), 2) self.assertEqual(len(router.get_ip6_address(config.ADDRESS_TYPE.OMR)), 2) @@ -171,8 +171,8 @@ def test(self): self.assertEqual(len(br.get_netdata_omr_prefixes()), 1) self.assertEqual(len(router.get_netdata_omr_prefixes()), 1) - self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1) - self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1) + self.assertEqual(len(router.get_netdata_non_nat64_routes()), 1) # The same local OMR and on-link prefix should be re-register. self.assertEqual(br.get_netdata_omr_prefixes(), [omr_prefix]) @@ -223,8 +223,8 @@ def test(self): self.assertEqual(len(br.get_netdata_omr_prefixes()), 1) self.assertEqual(len(router.get_netdata_omr_prefixes()), 1) - self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1) - self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1) + self.assertEqual(len(router.get_netdata_non_nat64_routes()), 1) # The same local OMR and on-link prefix should be re-registered. self.assertEqual(br.get_netdata_omr_prefixes(), [omr_prefix]) @@ -275,8 +275,8 @@ def test(self): self.assertEqual(len(br.get_netdata_omr_prefixes()), 1) self.assertEqual(len(router.get_netdata_omr_prefixes()), 1) - self.assertEqual(len(br.get_netdata_non_nat64_prefixes()), 1) - self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1) + self.assertEqual(len(router.get_netdata_non_nat64_routes()), 1) # The same local OMR and on-link prefix should be re-registered. self.assertEqual(br.get_netdata_omr_prefixes(), [omr_prefix]) @@ -314,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()), 1) - self.assertEqual(len(router.get_netdata_non_nat64_prefixes()), 1) + self.assertEqual(len(br.get_netdata_non_nat64_routes()), 1) + self.assertEqual(len(router.get_netdata_non_nat64_routes()), 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/node.py b/tests/scripts/thread-cert/node.py index aa8416f21..607d53214 100755 --- a/tests/scripts/thread-cert/node.py +++ b/tests/scripts/thread-cert/node.py @@ -1880,8 +1880,8 @@ def del_ipmaddr(self, ipmaddr): self.send_command(cmd) self._expect_done() - def get_addrs(self): - self.send_command('ipaddr') + def get_addrs(self, verbose=False): + self.send_command('ipaddr' + (' -v' if verbose else '')) return self._expect_results(r'\S+(:\S*)+') @@ -2177,13 +2177,21 @@ def get_br_on_link_prefix(self): self.send_command(cmd) return self._expect_command_output()[0] - def get_netdata_non_nat64_prefixes(self): - prefixes = [] + def get_netdata_non_nat64_routes(self): + nat64_routes = [] routes = self.get_routes() for route in routes: if 'n' not in route.split(' ')[1]: - prefixes.append(route.split(' ')[0]) - return prefixes + nat64_routes.append(route.split(' ')[0]) + return nat64_routes + + def get_netdata_nat64_routes(self): + nat64_routes = [] + routes = self.get_routes() + for route in routes: + if 'n' in route.split(' ')[1]: + nat64_routes.append(route.split(' ')[0]) + return nat64_routes def get_br_nat64_prefix(self): cmd = 'br nat64prefix local' @@ -2302,14 +2310,6 @@ def get_nat64_counters(self): continue return {'protocol': protocol_counters, 'errors': error_counters} - def get_netdata_nat64_prefix(self): - prefixes = [] - routes = self.get_routes() - for route in routes: - if 'n' in route.split(' ')[1]: - prefixes.append(route.split(' ')[0]) - return prefixes - def get_prefixes(self): return self.get_netdata()['Prefixes'] diff --git a/tests/scripts/thread-cert/test_srp_auto_host_address.py b/tests/scripts/thread-cert/test_srp_auto_host_address.py index bb9ac6e1e..f714e6c4a 100755 --- a/tests/scripts/thread-cert/test_srp_auto_host_address.py +++ b/tests/scripts/thread-cert/test_srp_auto_host_address.py @@ -155,6 +155,19 @@ def test(self): self.assertEqual(len(slaac_addr), 1) self.check_registered_addresses(client, server) + #------------------------------------------------------------------- + # Add a non-preferred SLAAC on-mesh prefix and check that the + # set of registered addresses remains unchanged and that the + # non-preferred address is not registered by SRP client. + + client.add_prefix('fd00:a:b:c::/64', 'aos') + client.register_netdata() + self.simulator.go(5) + + slaac_addr = [addr.strip() for addr in client.get_addrs() if addr.strip().startswith('fd00:a:b:c:')] + self.assertEqual(len(slaac_addr), 1) + self.check_registered_addresses(client, server) + #------------------------------------------------------------------- # Remove the on-mesh prefix (which will trigger an address to be # removed) and check that the SRP client re-registered and updated @@ -219,19 +232,21 @@ def check_registered_addresses(self, client, server): # Check the host addresses on server to match client. host_addresses = [addr.strip() for addr in server_host['addresses']] - client_addresses = [addr.strip() for addr in client.get_addrs()] + + client_mleid = client.get_mleid() + client_addresses = [addr.split(' ')[0] for addr in client.get_addrs(verbose=True) if 'preferred:1' in addr] + client_addresses += [client_mleid] # All registered addresses must be in client list of addresses. for addr in host_addresses: self.assertIn(addr, client_addresses) - # All addresses on client excluding link-local and mesh-local - # addresses must be seen on server side. But if there was - # no address, then mesh-local address should be the only + # All preferred addresses on client excluding link-local and + # mesh-local addresses must be seen on server side. But if there + # was no address, then mesh-local address should be the only # one registered. - client_mleid = client.get_mleid() checked_address = False for addr in client_addresses: diff --git a/tests/toranj/cli/test-603-channel-announce-recovery.py b/tests/toranj/cli/test-603-channel-announce-recovery.py index 6482aa989..8fe8b68c2 100755 --- a/tests/toranj/cli/test-603-channel-announce-recovery.py +++ b/tests/toranj/cli/test-603-channel-announce-recovery.py @@ -46,43 +46,47 @@ speedup = 40 cli.Node.set_time_speedup_factor(speedup) -router = cli.Node() +r1 = cli.Node() +r2 = cli.Node() c1 = cli.Node() c2 = cli.Node() # ----------------------------------------------------------------------------------------------------------------------- # Form topology -router.form('announce-tst', channel=11) +r1.form('announce-tst', channel=11) -c1.join(router, cli.JOIN_TYPE_SLEEPY_END_DEVICE) -c2.join(router, cli.JOIN_TYPE_SLEEPY_END_DEVICE) +r2.join(r1, cli.JOIN_TYPE_ROUTER) +c1.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE) +c2.join(r1, cli.JOIN_TYPE_SLEEPY_END_DEVICE) c1.set_pollperiod(500) c2.set_pollperiod(500) -verify(router.get_state() == 'leader') +verify(r1.get_state() == 'leader') +verify(r2.get_state() == 'router') verify(c1.get_state() == 'child') verify(c2.get_state() == 'child') # ----------------------------------------------------------------------------------------------------------------------- # Test Implementation -# Stop c2 +# Stop c2 and r2 +r2.thread_stop() c2.thread_stop() # Switch the rest of network to channel 26 -router.cli('channel manager change 26') +r1.cli('channel manager change 26') def check_channel_changed_to_26_on_r1_c1(): - for node in [router, c1]: + for node in [r1, 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 +# Now re-enable c2 and verify that it does attach to r1 and is on # channel 26. c2 would go through the ML Announce recovery. c2.thread_start() @@ -100,6 +104,20 @@ def check_c2_is_attched(): # Check that c2 is now on channel 26. verify(int(c2.get_channel()) == 26) +# Now re-enable r2, and verify that it switches to channel 26 +# after processing announce from r1. + +r2.thread_start() +verify(int(r2.get_channel()) == 11) + + +def check_r2_switches_to_channel_26(): + verify(r2.get_state() != 'detached') + verify(int(r2.get_channel()) == 26) + + +verify_within(check_r2_switches_to_channel_26, 60) + # ----------------------------------------------------------------------------------------------------------------------- # Test finished diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 5ffdbd33b..e14e30ad5 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -32,27 +32,75 @@ set(COMMON_INCLUDES ${PROJECT_SOURCE_DIR}/src/core ) +set(COMMON_INCLUDES_RCP + ${COMMON_INCLUDES} + ${PROJECT_SOURCE_DIR}/src/core/radio +) + set(COMMON_COMPILE_OPTIONS -DOPENTHREAD_FTD=1 -DOPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE=1 ) -add_library(ot-test-platform +set(COMMON_COMPILE_OPTIONS_RCP + -DOPENTHREAD_RADIO=1 + -DOPENTHREAD_SPINEL_CONFIG_OPENTHREAD_MESSAGE_ENABLE=1 + -DOPENTHREAD_CONFIG_PLATFORM_UDP_ENABLE=0 +) + +set(MULTIPAN_RCP_COMPILE_OPTIONS + -DOPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE=1 + -DOPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE=1 + -DOPENTHREAD_CONFIG_LOG_PREPEND_UPTIME=0 + -DOPENTHREAD_CONFIG_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE=0 # used to skip backoff and request tx from platform directly. + -DOPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE=1 +) + +add_library(ot-test-platform-ftd + test_platform.cpp + test_util.cpp +) +add_library(ot-test-platform-rcp test_platform.cpp test_util.cpp ) -target_include_directories(ot-test-platform +target_include_directories(ot-test-platform-ftd PRIVATE ${COMMON_INCLUDES} ) -target_compile_options(ot-test-platform +target_include_directories(ot-test-platform-rcp + PRIVATE + ${COMMON_INCLUDES} +) + +target_compile_options(ot-test-platform-ftd PRIVATE ${COMMON_COMPILE_OPTIONS} ) -target_link_libraries(ot-test-platform +target_compile_options(ot-test-platform-rcp + PRIVATE + ${COMMON_COMPILE_OPTIONS_RCP} +) + +if(OT_MULTIPAN_RCP) + target_compile_options(ot-test-platform-rcp + PRIVATE + "-DOPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE=1" + "-DOPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE=1" + "-DOPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE=1" + ) +endif() + +target_link_libraries(ot-test-platform-ftd + PRIVATE + ot-config + ${OT_MBEDTLS} +) + +target_link_libraries(ot-test-platform-rcp PRIVATE ot-config ${OT_MBEDTLS} @@ -61,14 +109,21 @@ target_link_libraries(ot-test-platform set(COMMON_LIBS openthread-spinel-ncp openthread-hdlc - ot-test-platform + ot-test-platform-ftd openthread-ftd - ot-test-platform + ot-test-platform-ftd ${OT_MBEDTLS} ot-config openthread-ftd ) +set(COMMON_LIBS_RCP + ot-test-platform-rcp + openthread-rcp + ${OT_MBEDTLS} + ot-config +) + add_executable(ot-test-aes test_aes.cpp ) @@ -748,6 +803,40 @@ target_link_libraries(ot-test-multicast-listeners-table add_test(NAME ot-test-multicast-listeners-table COMMAND ot-test-multicast-listeners-table) +if(OT_MULTIPAN_RCP) + add_executable(ot-test-multipan-rcp-instances + test_multipan_rcp_instances.cpp + ) + + target_include_directories(ot-test-multipan-rcp-instances + PRIVATE + ${COMMON_INCLUDES_RCP} + ) + + target_compile_options(ot-test-multipan-rcp-instances + PRIVATE + ${COMMON_COMPILE_OPTIONS_RCP} + ${MULTIPAN_RCP_COMPILE_OPTIONS} + ) + + target_compile_definitions(ot-test-multipan-rcp-instances + PRIVATE + "OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE=1" + ) + + target_compile_options(ot-config-radio + INTERFACE + "-DOPENTHREAD_CONFIG_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE=0" # used to skip backoff and request tx from platform directly. + ) + + target_link_libraries(ot-test-multipan-rcp-instances + PRIVATE + ${COMMON_LIBS_RCP} + ) + + add_test(NAME ot-test-multipan-rcp-instances COMMAND ot-test-multipan-rcp-instances) + endif() + add_test(NAME ot-test-nat64 COMMAND ot-test-nat64) add_executable(ot-test-nat64 diff --git a/tests/unit/test_multipan_rcp_instances.cpp b/tests/unit/test_multipan_rcp_instances.cpp new file mode 100644 index 000000000..790e457cf --- /dev/null +++ b/tests/unit/test_multipan_rcp_instances.cpp @@ -0,0 +1,766 @@ +/* + * 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 "common/array.hpp" +#include "common/code_utils.hpp" +#include "instance/instance.hpp" + +#include "ncp/ncp_base.hpp" +#include "openthread/link_raw.h" + +#include "test_platform.h" +#include "test_util.hpp" + +using namespace ot; +using namespace ot::Ncp; + +enum +{ + kTestBufferSize = 800 +}; + +enum +{ + kTestMacScanChannelMask = 0x01 +}; + +OT_TOOL_PACKED_BEGIN +struct RadioMessage +{ + uint8_t mChannel; + uint8_t mPsdu[OT_RADIO_FRAME_MAX_SIZE]; +} OT_TOOL_PACKED_END; + +static struct RadioMessage sDefaultMessages[OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM]; +static otRadioFrame sTxFrame[OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM]; +static ot::Instance *sInstances[OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM]; +static ot::Instance *sLastInstance; + +otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) +{ + otRadioFrame *frame = nullptr; + + for (size_t i = 0; i < OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM; i++) + { + if (sInstances[i] == aInstance || sInstances[i] == nullptr) + { + sTxFrame[i].mPsdu = sDefaultMessages->mPsdu; + frame = &sTxFrame[i]; + + break; + } + } + + return frame; +} + +otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *) +{ + sLastInstance = static_cast(aInstance); + return OT_ERROR_NONE; +} + +otError otPlatMultipanGetActiveInstance(otInstance **aInstance) +{ + otError error = OT_ERROR_NOT_IMPLEMENTED; + OT_UNUSED_VARIABLE(aInstance); + +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + *aInstance = sLastInstance; + error = OT_ERROR_NONE; +#endif + + return error; +} + +otError otPlatMultipanSetActiveInstance(otInstance *aInstance, bool aCompletePending) +{ + otError error = OT_ERROR_NOT_IMPLEMENTED; + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aCompletePending); + +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE + VerifyOrExit(sLastInstance != static_cast(aInstance), error = OT_ERROR_ALREADY); + sLastInstance = static_cast(aInstance); + error = OT_ERROR_NONE; +exit: +#endif + + return error; +} + +class TestNcp : public NcpBase +{ +public: + explicit TestNcp(ot::Instance *aInstance) + : mLastHeader(0) + , mLastStatus(0) + , mLastProp(0) + , NcpBase(aInstance) + { + memset(mMsgBuffer, 0, kTestBufferSize); + mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToNcpBuffer, this); + mTxFrameBuffer.SetFrameRemovedCallback(nullptr, this); + }; + + explicit TestNcp(ot::Instance **aInstances, uint8_t aCount) + : mLastHeader(0) + , mLastStatus(0) + , mLastProp(0) + , NcpBase(aInstances, aCount) + { + memset(mMsgBuffer, 0, kTestBufferSize); + mTxFrameBuffer.SetFrameAddedCallback(HandleFrameAddedToNcpBuffer, this); + mTxFrameBuffer.SetFrameRemovedCallback(nullptr, this); + }; + + static void HandleFrameAddedToNcpBuffer(void *aContext, + Spinel::Buffer::FrameTag aTag, + Spinel::Buffer::Priority aPriority, + Spinel::Buffer *aBuffer) + { + OT_UNUSED_VARIABLE(aTag); + OT_UNUSED_VARIABLE(aPriority); + + static_cast(aContext)->HandleFrameAddedToNcpBuffer(aBuffer); + } + + void HandleFrameAddedToNcpBuffer(Spinel::Buffer *aBuffer) + { + static const size_t display_size = 64; + + memset(mMsgBuffer, 0, kTestBufferSize); + SuccessOrQuit(aBuffer->OutFrameBegin()); + aBuffer->OutFrameRead(kTestBufferSize, mMsgBuffer); + SuccessOrQuit(aBuffer->OutFrameRemove()); + + // DumpBuffer("Received Buffer", mMsgBuffer, display_size); + + updateSpinelStatus(); + } + + void Receive(uint8_t *aBuffer, size_t bufferSize) { HandleReceive(aBuffer, static_cast(bufferSize)); } + + void processTransmit() + { + uint8_t iid = SPINEL_HEADER_GET_IID(mLastHeader); + + LinkRawTransmitDone(iid, &sTxFrame[iid], nullptr, OT_ERROR_NONE); + }; + + void updateSpinelStatus() + { + Spinel::Decoder decoder; + + uint8_t header; + unsigned int command; + unsigned int propKey; + unsigned int status; + + decoder.Init(mMsgBuffer, kTestBufferSize); + + SuccessOrQuit(decoder.ReadUint8(mLastHeader)); + SuccessOrQuit(decoder.ReadUintPacked(command)); + SuccessOrQuit(decoder.ReadUintPacked(propKey)); + SuccessOrQuit(decoder.ReadUintPacked(status)); + + mLastStatus = static_cast(status); + mLastProp = static_cast(propKey); + } + + uint32_t getSpinelStatus() const { return mLastStatus; } + uint32_t getSpinelProp() const { return mLastProp; } + + uint8_t getLastIid() const + { + /* Return as SPINEL_HEADER_IID_N format without shift */ + return SPINEL_HEADER_IID_MASK & mLastHeader; + } + + uint8_t getLastTid() { return SPINEL_HEADER_GET_TID(mLastHeader); } + + bool gotResponse(uint8_t aIid, uint8_t aTid) { return ((aIid == getLastIid()) && (aTid == getLastTid())); } + +private: + uint8_t mLastHeader; + uint32_t mLastStatus; + uint32_t mLastProp; + uint8_t mMsgBuffer[kTestBufferSize]; +}; + +class TestHost +{ +public: + TestHost(TestNcp *aNcp, uint8_t aIid) + : mNcp(aNcp) + , mIid(aIid) + , mTid(0) + , mLastTxTid(0) + , mBuffer(mBuf, kTestBufferSize) + , mEncoder(mBuffer) + , mOffset(0) + { + memset(mBuf, 0, kTestBufferSize); + }; + + void createLinkEnableFrame(bool isEnabled) + { + startFrame(SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_PHY_ENABLED); + SuccessOrQuit(mEncoder.WriteBool(isEnabled)); + endFrame("Enable Frame"); + } + + void createTransmitFrame() + { + startFrame(SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_STREAM_RAW); + + SuccessOrQuit(mEncoder.WriteDataWithLen(sTxFrame[mIid].mPsdu, sTxFrame[mIid].mLength)); + SuccessOrQuit(mEncoder.WriteUint8(sTxFrame[mIid].mChannel)); + SuccessOrQuit(mEncoder.WriteUint8(sTxFrame[mIid].mInfo.mTxInfo.mMaxCsmaBackoffs)); + SuccessOrQuit(mEncoder.WriteUint8(sTxFrame[mIid].mInfo.mTxInfo.mMaxFrameRetries)); + SuccessOrQuit(mEncoder.WriteBool(sTxFrame[mIid].mInfo.mTxInfo.mCsmaCaEnabled)); + SuccessOrQuit(mEncoder.WriteBool(sTxFrame[mIid].mInfo.mTxInfo.mIsHeaderUpdated)); + SuccessOrQuit(mEncoder.WriteBool(sTxFrame[mIid].mInfo.mTxInfo.mIsARetx)); + SuccessOrQuit(mEncoder.WriteBool(sTxFrame[mIid].mInfo.mTxInfo.mIsSecurityProcessed)); + SuccessOrQuit(mEncoder.WriteUint32(sTxFrame[mIid].mInfo.mTxInfo.mTxDelay)); + SuccessOrQuit(mEncoder.WriteUint32(sTxFrame[mIid].mInfo.mTxInfo.mTxDelayBaseTime)); + + endFrame("Transmit Frame"); + } + + void createSwitchoverRequest(uint8_t aIid, bool aForce) + { + startFrame(SPINEL_CMD_PROP_VALUE_SET, SPINEL_PROP_MULTIPAN_ACTIVE_INTERFACE); + SuccessOrQuit(mEncoder.WriteUint8(aIid | (aForce ? 0 : (1 << SPINEL_MULTIPAN_INTERFACE_SOFT_SWITCH_SHIFT)))); + endFrame("Interface Switch Request Frame"); + } + + void createReadStatusFrame() + { + startFrame(SPINEL_CMD_PROP_VALUE_GET, SPINEL_PROP_LAST_STATUS); + endFrame("Read Status Frame"); + } + + void enableRawLink() + { + static const bool isLinkEnabled = true; + createLinkEnableFrame(isLinkEnabled); + sendToRcp(); + } + + void disableRawLink() + { + static const bool isLinkEnabled = false; + createLinkEnableFrame(isLinkEnabled); + sendToRcp(); + } + + spinel_status_t startTransmit() + { + mLastTxTid = mTid; + createTransmitFrame(); + sendToRcp(); + prepareResponse(mLastTxTid); + return static_cast(mNcp->getSpinelStatus()); + }; + + spinel_status_t requestSwitchover(uint8_t aIid, bool aForce) + { + mLastTxTid = mTid; + createSwitchoverRequest(aIid, aForce); + sendToRcp(); + prepareResponse(mLastTxTid); + return static_cast(mNcp->getSpinelStatus()); + }; + + void getCommandStatus() + { + createReadStatusFrame(); + sendToRcp(); + } + + void finishTransmit() + { + /* Reset instance submac state to sleep by resetting link + This is needed for a second transmit command to succeed + as the HandleTimer method will not be called to reset the submac */ + disableRawLink(); + enableRawLink(); + + /* Proceed with transmit done callback from ncp */ + mNcp->processTransmit(); + }; + + uint8_t getLastTransmitTid(void) { return mLastTxTid; } + +private: + void startFrame(unsigned int aCommand, spinel_prop_key_t aKey) + { + uint8_t spinelHeader = SPINEL_HEADER_FLAG | mIid | mTid; + + SuccessOrQuit(mEncoder.BeginFrame(Spinel::Buffer::kPriorityLow)); + SuccessOrQuit(mEncoder.WriteUint8(spinelHeader)); + SuccessOrQuit(mEncoder.WriteUintPacked(aCommand)); + SuccessOrQuit(mEncoder.WriteUintPacked(aKey)); + } + + void endFrame(const char *aTextMessage) + { + static const uint16_t display_length = 64; + SuccessOrQuit(mEncoder.EndFrame()); + // DumpBuffer(aTextMessage, mBuf, display_length); + } + + void sendToRcp() + { + static const uint8_t data_offset = 2; + size_t frame_len = mBuffer.OutFrameGetLength(); + + mOffset += data_offset; + + mNcp->Receive(mBuf + mOffset, frame_len); + + mTid = SPINEL_GET_NEXT_TID(mTid); + SuccessOrQuit(mBuffer.OutFrameRemove()); + + mOffset += frame_len; + mOffset %= kTestBufferSize; + } + + void prepareResponse(uint8_t aTid) + { + /* Some spinel commands immediately send queued responses when command is complete + while others require a separate command to the ncp in order to receive the response. + If a response is needed and not immediately received. Issue a command to update the status. */ + + if (!mNcp->gotResponse(mIid, aTid)) + { + getCommandStatus(); + } + } + + TestNcp *mNcp; + uint8_t mIid; + uint8_t mTid; + uint8_t mLastTxTid; + uint8_t mBuf[kTestBufferSize]; + Spinel::Buffer mBuffer; + Spinel::Encoder mEncoder; + size_t mOffset; +}; + +void InitInstances(void) +{ +#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE + for (size_t i = 0; i < OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM; i++) + { + sInstances[i] = testInitAdditionalInstance(i); + VerifyOrQuit(sInstances[i] != nullptr); + } +#endif +} + +void FreeInstances(void) +{ + for (size_t i = 0; i < OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM; i++) + { + if (sInstances[i] != nullptr) + { + testFreeInstance(sInstances[i]); + sInstances[i] = nullptr; + } + } +} + +void TestNcpBaseTransmitWithLinkRawDisabled(void) +{ + printf("\tTransmit With Link Raw Disabled"); + InitInstances(); + + TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); + TestHost host1(&ncp, SPINEL_HEADER_IID_0); + TestHost host2(&ncp, SPINEL_HEADER_IID_1); + TestHost host3(&ncp, SPINEL_HEADER_IID_2); + + host1.disableRawLink(); + host2.disableRawLink(); + host3.disableRawLink(); + + /* Test that the response status is Invalid State when transmit is skipped due to disabled link */ + VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_INVALID_STATE); + VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_INVALID_STATE); + VerifyOrQuit(host3.startTransmit() == SPINEL_STATUS_INVALID_STATE); + + FreeInstances(); + printf(" - PASS\n"); +} + +void TestNcpBaseTransmitWithLinkRawEnabled(void) +{ + printf("\tTransmit With Link Raw Enabled"); + InitInstances(); + + TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); + TestHost host(&ncp, SPINEL_HEADER_IID_0); + + host.enableRawLink(); + + /* Test that the response status is OK when transmit is started successfully */ + VerifyOrQuit(host.startTransmit() == SPINEL_STATUS_OK); + + host.finishTransmit(); + + FreeInstances(); + printf(" - PASS\n"); +} + +void TestNcpBaseTransmitWithIncorrectLinkRawEnabled(void) +{ + printf("\tTransmit With Incorrect Link Raw Enabled"); + InitInstances(); + + TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); + TestHost host1(&ncp, SPINEL_HEADER_IID_0); + TestHost host2(&ncp, SPINEL_HEADER_IID_1); + + host1.disableRawLink(); + host2.enableRawLink(); + + /* Test that Invalid State is reported when different endpoint was enabled */ + VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_INVALID_STATE); + + /* Test that status is OK when transmitting on the proper interface */ + VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK); + + host1.finishTransmit(); + + FreeInstances(); + printf(" - PASS\n"); +} + +void TestNcpBaseTransmitOnBoth(void) +{ + printf("\tTransmit on both interfaces"); + InitInstances(); + + TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); + TestHost host1(&ncp, SPINEL_HEADER_IID_0); + TestHost host2(&ncp, SPINEL_HEADER_IID_1); + + host1.enableRawLink(); + host2.enableRawLink(); + + VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_OK); + VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK); + + host1.finishTransmit(); + host2.finishTransmit(); + + FreeInstances(); + printf(" - PASS\n"); +} + +void TestNcpBaseDifferentInstanceCall(void) +{ + printf("\tTransmit on both interfaces - verify instances used"); + + InitInstances(); + + TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); + TestHost host1(&ncp, SPINEL_HEADER_IID_0); + TestHost host2(&ncp, SPINEL_HEADER_IID_1); + + sLastInstance = nullptr; + + host1.enableRawLink(); + host2.enableRawLink(); + + VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_OK); + VerifyOrQuit(sLastInstance != nullptr); + VerifyOrQuit(sLastInstance == sInstances[0]); + + VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK); + VerifyOrQuit(sLastInstance != nullptr); + VerifyOrQuit(sLastInstance == sInstances[1]); + + host1.finishTransmit(); + host2.finishTransmit(); + + /* Test reverse order of calls to make sure it is not just a fixed order */ + sLastInstance = nullptr; + VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK); + VerifyOrQuit(sLastInstance != nullptr); + VerifyOrQuit(sLastInstance == sInstances[1]); + + VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_OK); + VerifyOrQuit(sLastInstance != nullptr); + VerifyOrQuit(sLastInstance == sInstances[0]); + + host1.finishTransmit(); + host2.finishTransmit(); + + printf(" - PASS\n"); +} + +void TestNcpBaseTransmitDoneInterface(void) +{ + printf("\tTransmit on both interfaces - verify transmit done IID"); + + InitInstances(); + + TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); + TestHost host1(&ncp, SPINEL_HEADER_IID_0); + TestHost host2(&ncp, SPINEL_HEADER_IID_1); + + sLastInstance = nullptr; + + host1.enableRawLink(); + host2.enableRawLink(); + + VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_OK); + VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK); + + otPlatRadioTxDone(sInstances[0], &sTxFrame[0], nullptr, OT_ERROR_NONE); + VerifyOrQuit(ncp.gotResponse(SPINEL_HEADER_IID_0, host1.getLastTransmitTid())); + + otPlatRadioTxDone(sInstances[1], &sTxFrame[1], nullptr, OT_ERROR_NONE); + VerifyOrQuit(ncp.gotResponse(SPINEL_HEADER_IID_1, host2.getLastTransmitTid())); + + /* Test reverse order of tx processing */ + VerifyOrQuit(host1.startTransmit() == SPINEL_STATUS_OK); + VerifyOrQuit(host2.startTransmit() == SPINEL_STATUS_OK); + + otPlatRadioTxDone(sInstances[1], &sTxFrame[1], nullptr, OT_ERROR_NONE); + VerifyOrQuit(ncp.gotResponse(SPINEL_HEADER_IID_1, host2.getLastTransmitTid())); + + otPlatRadioTxDone(sInstances[0], &sTxFrame[0], nullptr, OT_ERROR_NONE); + VerifyOrQuit(ncp.gotResponse(SPINEL_HEADER_IID_0, host1.getLastTransmitTid())); + + printf(" - PASS\n"); +} + +void TestNcpBaseReceive(void) +{ + printf("\tReceive on a single interface"); + + InitInstances(); + + TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); + TestHost host1(&ncp, SPINEL_HEADER_IID_0); + + host1.enableRawLink(); + + otPlatRadioReceiveDone(sInstances[0], &sTxFrame[0], OT_ERROR_NONE); + + VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_STREAM_RAW); + VerifyOrQuit(ncp.getLastTid() == 0); + VerifyOrQuit(ncp.getLastIid() == SPINEL_HEADER_IID_0); + + printf(" - PASS\n"); +} + +void TestNcpBaseReceiveOnTwoInterfaces(void) +{ + printf("\tReceive on both interfaces"); + + InitInstances(); + + TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); + TestHost host1(&ncp, SPINEL_HEADER_IID_0); + TestHost host2(&ncp, SPINEL_HEADER_IID_1); + + sLastInstance = nullptr; + + host1.enableRawLink(); + host2.enableRawLink(); + + otPlatRadioReceiveDone(sInstances[1], &sTxFrame[1], OT_ERROR_NONE); + + VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_STREAM_RAW); + VerifyOrQuit(ncp.getLastTid() == 0); + VerifyOrQuit(ncp.getLastIid() == SPINEL_HEADER_IID_1); + + otPlatRadioReceiveDone(sInstances[0], &sTxFrame[0], OT_ERROR_NONE); + + VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_STREAM_RAW); + VerifyOrQuit(ncp.getLastTid() == 0); + VerifyOrQuit(ncp.getLastIid() == SPINEL_HEADER_IID_0); + + /* reverse order */ + otPlatRadioReceiveDone(sInstances[0], &sTxFrame[0], OT_ERROR_NONE); + + VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_STREAM_RAW); + VerifyOrQuit(ncp.getLastTid() == 0); + VerifyOrQuit(ncp.getLastIid() == SPINEL_HEADER_IID_0); + + otPlatRadioReceiveDone(sInstances[1], &sTxFrame[1], OT_ERROR_NONE); + + VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_STREAM_RAW); + VerifyOrQuit(ncp.getLastTid() == 0); + VerifyOrQuit(ncp.getLastIid() == SPINEL_HEADER_IID_1); + + printf(" - PASS\n"); +} + +void TestNcpBaseSwitchoverRequest(void) +{ + printf("\tSwitchover requests from different interfaces"); + + InitInstances(); + + TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); + TestHost host1(&ncp, SPINEL_HEADER_IID_0); + TestHost host2(&ncp, SPINEL_HEADER_IID_1); + TestHost host3(&ncp, SPINEL_HEADER_IID_2); + + sLastInstance = nullptr; + + host1.enableRawLink(); + host2.enableRawLink(); + host3.enableRawLink(); + + VerifyOrQuit(host1.requestSwitchover(0, true) == 0); + VerifyOrQuit(sLastInstance == sInstances[0]); + VerifyOrQuit(host1.requestSwitchover(1, true) == 1); + VerifyOrQuit(sLastInstance == sInstances[1]); + VerifyOrQuit(host1.requestSwitchover(2, true) == 2); + VerifyOrQuit(sLastInstance == sInstances[2]); + VerifyOrQuit(host2.requestSwitchover(0, true) == 0); + VerifyOrQuit(sLastInstance == sInstances[0]); + VerifyOrQuit(host2.requestSwitchover(1, true) == 1); + VerifyOrQuit(sLastInstance == sInstances[1]); + VerifyOrQuit(host2.requestSwitchover(2, true) == 2); + VerifyOrQuit(sLastInstance == sInstances[2]); + VerifyOrQuit(host3.requestSwitchover(0, true) == 0); + VerifyOrQuit(sLastInstance == sInstances[0]); + VerifyOrQuit(host3.requestSwitchover(1, true) == 1); + VerifyOrQuit(sLastInstance == sInstances[1]); + VerifyOrQuit(host3.requestSwitchover(2, true) == 2); + VerifyOrQuit(sLastInstance == sInstances[2]); + + printf(" - PASS\n"); +} + +void TestNcpBaseSwitchoverRequestFail(void) +{ + printf("\tSwitchover requests Fail - same interface"); + + InitInstances(); + + TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); + TestHost host1(&ncp, SPINEL_HEADER_IID_0); + TestHost host2(&ncp, SPINEL_HEADER_IID_1); + + sLastInstance = nullptr; + + host1.enableRawLink(); + host2.enableRawLink(); + + VerifyOrQuit(host1.requestSwitchover(0, true) == 0); + VerifyOrQuit(sLastInstance == sInstances[0]); + + VerifyOrQuit(host1.requestSwitchover(0, true) == SPINEL_STATUS_ALREADY); + VerifyOrQuit(sLastInstance == sInstances[0]); + + VerifyOrQuit(host2.requestSwitchover(0, true) == SPINEL_STATUS_ALREADY); + VerifyOrQuit(sLastInstance == sInstances[0]); + + printf(" - PASS\n"); +} + +void TestNcpBaseSwitchoverResponse(void) +{ + printf("\tSwitchover responses"); + + InitInstances(); + + TestNcp ncp(sInstances, OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM); + TestHost host1(&ncp, SPINEL_HEADER_IID_0); + TestHost host2(&ncp, SPINEL_HEADER_IID_1); + + sLastInstance = nullptr; + + host1.enableRawLink(); + host2.enableRawLink(); + + VerifyOrQuit(host1.requestSwitchover(0, true) == 0); + VerifyOrQuit(sLastInstance == sInstances[0]); + + otPlatMultipanSwitchoverDone(sLastInstance, true); + + VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_LAST_STATUS); + VerifyOrQuit(ncp.getLastTid() == 0); + VerifyOrQuit(ncp.getLastIid() == OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID); + VerifyOrQuit(ncp.getSpinelStatus() == SPINEL_STATUS_SWITCHOVER_DONE); + + VerifyOrQuit(host1.requestSwitchover(1, true) == 1); + VerifyOrQuit(sLastInstance == sInstances[1]); + + otPlatMultipanSwitchoverDone(sLastInstance, false); + + VerifyOrQuit(ncp.getSpinelProp() == SPINEL_PROP_LAST_STATUS); + VerifyOrQuit(ncp.getLastTid() == 0); + VerifyOrQuit(ncp.getLastIid() == OPENTHREAD_SPINEL_CONFIG_BROADCAST_IID); + VerifyOrQuit(ncp.getSpinelStatus() == SPINEL_STATUS_SWITCHOVER_FAILED); + + printf(" - PASS\n"); +} + +/// +int main(void) +{ +#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE && (OPENTHREAD_RADIO || OPENTHREAD_CONFIG_LINK_RAW_ENABLE) + printf("Executing Transmit Tests\n"); + TestNcpBaseTransmitWithLinkRawDisabled(); + TestNcpBaseTransmitWithLinkRawEnabled(); + TestNcpBaseTransmitWithIncorrectLinkRawEnabled(); + TestNcpBaseTransmitOnBoth(); + TestNcpBaseDifferentInstanceCall(); + TestNcpBaseTransmitDoneInterface(); + printf("Transmit Tests - PASS\n"); + + printf("Executing Receive Tests\n"); + TestNcpBaseReceive(); + TestNcpBaseReceiveOnTwoInterfaces(); + printf("Receive Tests - PASS\n"); + + printf("Executing Interface Switching Tests\n"); + TestNcpBaseSwitchoverRequest(); + TestNcpBaseSwitchoverRequestFail(); + TestNcpBaseSwitchoverResponse(); + printf("Executing Interface Switching Tests - PASS\n"); + + printf("\nAll tests passed\n"); + +#else + printf("MULTIPAN_RCP feature and RADIO/LINK_RAW option are not enabled\n"); +#endif + return 0; +} diff --git a/tests/unit/test_platform.cpp b/tests/unit/test_platform.cpp index 21718f1d3..361711428 100644 --- a/tests/unit/test_platform.cpp +++ b/tests/unit/test_platform.cpp @@ -35,6 +35,9 @@ #include #include +#ifdef OPENTHREAD_CONFIG_BLE_TCAT_ENABLE +#include +#endif enum { @@ -49,6 +52,9 @@ ot::Instance *testInitInstance(void) otInstance *instance = nullptr; #if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE +#if OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE + instance = otInstanceInitMultiple(0); +#else size_t instanceBufferLength = 0; uint8_t *instanceBuffer = nullptr; @@ -62,6 +68,7 @@ ot::Instance *testInitInstance(void) // Initialize OpenThread with the buffer instance = otInstanceInit(instanceBuffer, &instanceBufferLength); +#endif #else instance = otInstanceInitSingle(); #endif @@ -69,11 +76,22 @@ ot::Instance *testInitInstance(void) return static_cast(instance); } +#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE +ot::Instance *testInitAdditionalInstance(uint8_t id) +{ + otInstance *instance = nullptr; + + instance = otInstanceInitMultiple(id); + + return static_cast(instance); +} +#endif + void testFreeInstance(otInstance *aInstance) { otInstanceFinalize(aInstance); -#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE +#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && !OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE free(aInstance); #endif } @@ -116,6 +134,10 @@ OT_TOOL_WEAK uint32_t otPlatAlarmMicroGetNow(void) return (uint32_t)((tv.tv_sec * 1000000) + tv.tv_usec + 123456); } +OT_TOOL_WEAK otError otPlatMultipanGetActiveInstance(otInstance **) { return OT_ERROR_NOT_IMPLEMENTED; } + +OT_TOOL_WEAK otError otPlatMultipanSetActiveInstance(otInstance *, bool) { return OT_ERROR_NOT_IMPLEMENTED; } + OT_TOOL_WEAK void otPlatRadioGetIeeeEui64(otInstance *, uint8_t *) {} OT_TOOL_WEAK void otPlatRadioSetPanId(otInstance *, uint16_t) {} @@ -229,6 +251,8 @@ OT_TOOL_WEAK otError otPlatResetToBootloader(otInstance *) { return OT_ERROR_NOT OT_TOOL_WEAK otPlatResetReason otPlatGetResetReason(otInstance *) { return OT_PLAT_RESET_REASON_POWER_ON; } +OT_TOOL_WEAK void otPlatWakeHost(void) {} + OT_TOOL_WEAK void otPlatLog(otLogLevel, otLogRegion, const char *, ...) {} OT_TOOL_WEAK void otPlatSettingsInit(otInstance *, const uint16_t *, uint16_t) {} @@ -627,4 +651,85 @@ void otPlatDnsCancelUpstreamQuery(otInstance *aInstance, otPlatDnsUpstreamQuery } #endif +OT_TOOL_WEAK otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *, int8_t *) { return OT_ERROR_NONE; } + +OT_TOOL_WEAK otError otPlatRadioGetCoexMetrics(otInstance *, otRadioCoexMetrics *) { return OT_ERROR_NONE; } + +OT_TOOL_WEAK otError otPlatRadioGetTransmitPower(otInstance *, int8_t *) { return OT_ERROR_NONE; } + +OT_TOOL_WEAK bool otPlatRadioIsCoexEnabled(otInstance *) { return true; } + +OT_TOOL_WEAK otError otPlatRadioSetCoexEnabled(otInstance *, bool) { return OT_ERROR_NOT_IMPLEMENTED; } + +#if OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE +OT_TOOL_WEAK otError otPlatRadioSetChannelTargetPower(otInstance *aInstance, uint8_t aChannel, int16_t aTargetPower) +{ + return OT_ERROR_NONE; +} + +OT_TOOL_WEAK otError otPlatRadioAddCalibratedPower(otInstance *aInstance, + uint8_t aChannel, + int16_t aActualPower, + const uint8_t *aRawPowerSetting, + uint16_t aRawPowerSettingLength) +{ + return OT_ERROR_NONE; +} + +OT_TOOL_WEAK otError otPlatRadioClearCalibratedPowers(otInstance *aInstance) { return OT_ERROR_NONE; } +#endif // OPENTHREAD_CONFIG_PLATFORM_POWER_CALIBRATION_ENABLE + +#if OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL +OT_TOOL_WEAK otPlatMcuPowerState otPlatGetMcuPowerState(otInstance *aInstance) { return OT_PLAT_MCU_POWER_STATE_ON; } + +OT_TOOL_WEAK otError otPlatSetMcuPowerState(otInstance *aInstance, otPlatMcuPowerState aState) { return OT_ERROR_NONE; } +#endif // OPENTHREAD_CONFIG_NCP_ENABLE_MCU_POWER_STATE_CONTROL +#ifdef OPENTHREAD_CONFIG_BLE_TCAT_ENABLE +otError otPlatBleEnable(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleDisable(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aInterval); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapAdvStop(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGapDisconnect(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aMtu); + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, const otBleRadioPacket *aPacket) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aHandle); + OT_UNUSED_VARIABLE(aPacket); + return OT_ERROR_NOT_IMPLEMENTED; +} +#endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE + } // extern "C" diff --git a/tests/unit/test_platform.h b/tests/unit/test_platform.h index 76a72ba09..97a9d79d9 100644 --- a/tests/unit/test_platform.h +++ b/tests/unit/test_platform.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include "common/code_utils.hpp" @@ -46,6 +47,9 @@ #include "test_util.h" ot::Instance *testInitInstance(void); -void testFreeInstance(otInstance *aInstance); +#if OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_ENABLE && OPENTHREAD_CONFIG_MULTIPLE_STATIC_INSTANCE_ENABLE +ot::Instance *testInitAdditionalInstance(uint8_t id); +#endif +void testFreeInstance(otInstance *aInstance); #endif // TEST_PLATFORM_H diff --git a/third_party/mbedtls/mbedtls-config.h b/third_party/mbedtls/mbedtls-config.h index fcb1739e1..a3e06ac63 100644 --- a/third_party/mbedtls/mbedtls-config.h +++ b/third_party/mbedtls/mbedtls-config.h @@ -92,6 +92,10 @@ #define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED #endif +#if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE +#define MBEDTLS_SSL_KEEP_PEER_CERTIFICATE +#endif + #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED #define MBEDTLS_BASE64_C #define MBEDTLS_ECDH_C